From c1ae021daeac8e5deeda464ea163fed8fbc5a16c Mon Sep 17 00:00:00 2001 From: Yarne Coppens Date: Mon, 19 Aug 2024 13:50:27 +0200 Subject: [PATCH] Added designers to boardgames (howly mowly) --- .gitignore | 1 + src/classes/boardgame_classes.py | 12 ++++++- src/classes/many_to_many_links.py | 9 +++++ src/classes/people_classes.py | 9 +++++ src/classes/play_classes.py | 1 + src/main.py | 12 +++++-- src/modules/bgg_connection.py | 22 +++++++++--- src/modules/data_connection.py | 37 ++++++++++++++++++-- src/modules/db_connection.py | 56 ++++++++++++++++++++++++++----- 9 files changed, 141 insertions(+), 18 deletions(-) create mode 100644 src/classes/many_to_many_links.py create mode 100644 src/classes/people_classes.py diff --git a/.gitignore b/.gitignore index ffd8ad9..a1e1567 100644 --- a/.gitignore +++ b/.gitignore @@ -162,3 +162,4 @@ cython_debug/ secrets/auth.yaml db/database.db +.vscode/ diff --git a/src/classes/boardgame_classes.py b/src/classes/boardgame_classes.py index 4580e83..d8dfcbd 100644 --- a/src/classes/boardgame_classes.py +++ b/src/classes/boardgame_classes.py @@ -1,6 +1,7 @@ from datetime import date from enum import Enum from sqlmodel import Field, SQLModel, Relationship +from src.classes import many_to_many_links class BoardgameType(Enum): @@ -42,14 +43,17 @@ class BoardGame(BoardGameBase, table=True): id: int = Field(primary_key=True) type: BoardgameType = BoardgameType.BOARDGAME plays: list["Play"] = Relationship(back_populates='boardgame') + designers: list["Designer"] = Relationship(back_populates="designed_boardgames", link_model=many_to_many_links.DesignerBoardGameLink) class BoardGamePublic(BoardGameBase): id: int type: BoardgameType plays: list["Play"] + designers: list["Designer"] class BoardGameNoPlays(BoardGameBase): id: int + designers: list["Designer"] type: BoardgameType = BoardgameType.BOARDGAME class BoardGameExpansion(BoardGameBase, table=True): @@ -57,16 +61,19 @@ class BoardGameExpansion(BoardGameBase, table=True): expansion_for: int = Field(default=None, foreign_key="boardgame.id") type: BoardgameType = BoardgameType.BOARDGAMEEXPANSION plays: list["Play"] = Relationship(back_populates='boardgameexpansion') + designers: list["Designer"] = Relationship(back_populates="designed_expansions", link_model=many_to_many_links.DesignerBoardGameExpansionLink) class BoardGameExpansionPublic(BoardGameBase): id: int expansion_for: int type: BoardgameType plays: list["Play"] + designers: list["Designer"] class BoardGameExpansionNoPlays(BoardGameBase): id: int expansion_for: int + designers: list["Designer"] type: BoardgameType = BoardgameType.BOARDGAMEEXPANSION class OwnedBoardGame(OwnedBoardGameBase, table=True): @@ -91,5 +98,8 @@ class WishlistBoardGameExpansion(WishlistBoardGameBase, table=True): from src.classes.play_classes import Play, PlayPublic +from src.classes.people_classes import Designer BoardGame.model_rebuild() -BoardGameExpansion.model_rebuild() \ No newline at end of file +BoardGameExpansion.model_rebuild() +BoardGameNoPlays.model_rebuild() +BoardGameExpansionNoPlays.model_rebuild() \ No newline at end of file diff --git a/src/classes/many_to_many_links.py b/src/classes/many_to_many_links.py new file mode 100644 index 0000000..f0fb82f --- /dev/null +++ b/src/classes/many_to_many_links.py @@ -0,0 +1,9 @@ +from sqlmodel import Field,SQLModel + +class DesignerBoardGameLink(SQLModel, table=True): + boardgame_id: int | None = Field(default=None, foreign_key="boardgame.id", primary_key=True) + designer_id: int | None = Field(default=None, foreign_key="designer.id", primary_key=True) + +class DesignerBoardGameExpansionLink(SQLModel, table=True): + boardgame_id: int | None = Field(default=None, foreign_key="boardgameexpansion.id", primary_key=True) + designer_id: int | None = Field(default=None, foreign_key="designer.id", primary_key=True) \ No newline at end of file diff --git a/src/classes/people_classes.py b/src/classes/people_classes.py new file mode 100644 index 0000000..b071b90 --- /dev/null +++ b/src/classes/people_classes.py @@ -0,0 +1,9 @@ +from sqlmodel import Field, SQLModel, Relationship + +from src.classes import boardgame_classes, many_to_many_links + +class Designer(SQLModel, table=True): + id: int = Field(primary_key=True) + name: str + designed_boardgames: list[boardgame_classes.BoardGame] | None = Relationship(back_populates="designers", link_model=many_to_many_links.DesignerBoardGameLink) + designed_expansions: list[boardgame_classes.BoardGameExpansion] | None = Relationship(back_populates="designers", link_model=many_to_many_links.DesignerBoardGameExpansionLink) \ No newline at end of file diff --git a/src/classes/play_classes.py b/src/classes/play_classes.py index 7d42b00..d8e15a8 100644 --- a/src/classes/play_classes.py +++ b/src/classes/play_classes.py @@ -55,5 +55,6 @@ class PlayPublicWithPlayers(PlayPublic): from src.classes.boardgame_classes import BoardGame, BoardGameExpansion, BoardGameNoPlays, BoardGameExpansionNoPlays +from src.classes.people_classes import Designer Play.model_rebuild() PlayPublicWithPlayers.model_rebuild() \ No newline at end of file diff --git a/src/main.py b/src/main.py index e91f64b..b2e4ac3 100644 --- a/src/main.py +++ b/src/main.py @@ -1,5 +1,4 @@ from typing import Union -from datetime import date, timedelta, datetime from pydantic import BaseModel from sqlmodel import Session from threading import Thread @@ -15,7 +14,7 @@ from src.filters import boardgame_filters, play_filters is_refreshing = False -def get_session(): +async def get_session(): with Session(data_connection.get_db_engine()) as session: yield session @@ -114,6 +113,15 @@ def get_owned_collection(query: BoardgameFilterParams = Depends(), session: Sess return to_return_boardgames +@app.get('/collection', response_model=list[Union[boardgame_classes.BoardGame, boardgame_classes.BoardGameExpansion]]) +def get_collection(query: BoardgameFilterParams = Depends(), session: Session = Depends(get_session)): + to_return_boardgames = data_connection.get_user_collection(session) + + to_return_boardgames = query.do_filtering(to_return_boardgames) + + to_return_boardgames.sort(key=lambda x: x.name) + + return to_return_boardgames @app.get("/wishlist", response_model=list[Union[boardgame_classes.WishlistBoardGame, boardgame_classes.WishlistBoardGameExpansion]]) def get_wishlist_collection(priority: int = 0, query: BoardgameFilterParams = Depends(), session: Session = Depends(get_session)): diff --git a/src/modules/bgg_connection.py b/src/modules/bgg_connection.py index 04b0568..26c0fc8 100644 --- a/src/modules/bgg_connection.py +++ b/src/modules/bgg_connection.py @@ -9,10 +9,13 @@ from typing import Union import html from tqdm import tqdm -from src.classes import boardgame_classes, play_classes -from src.modules import auth_manager +from sqlmodel import Session, select + +from src.classes import boardgame_classes, play_classes, people_classes +from src.modules import auth_manager, data_connection from src.config import definitions + authenticated_session: requests.Session = requests.Session() def url_to_xml_object(url: HttpUrl) -> ET.Element: @@ -79,9 +82,17 @@ def convert_xml_to_boardgame(boardgame_xml: ET.Element) -> Union[boardgame_class all_links = boardgame_xml.findall('link') + designers = [] + for link in all_links: - if link.get('type') == 'boardgameexpansion': - expansion_ids.append(int(link.get('id'))) + match link.get('type'): + case 'boardgameexpansion': + expansion_ids.append(int(link.get('id'))) + case 'boardgamedesigner': + designer_id = int(link.get('id')) + designer_name = link.get('value') + designer = people_classes.Designer(id=designer_id, name=designer_name) + designers.append(designer) boardgame_dict = { "id" : int(boardgame_xml.get('id')), @@ -95,7 +106,8 @@ def convert_xml_to_boardgame(boardgame_xml: ET.Element) -> Union[boardgame_class "max_players" : int(boardgame_xml.find('maxplayers').get('value')), "min_playing_time" : int(boardgame_xml.find('minplaytime').get('value')), "max_playing_time" : int(boardgame_xml.find('maxplaytime').get('value')), - "min_age" : int(boardgame_xml.find('minage').get('value')) + "min_age" : int(boardgame_xml.find('minage').get('value')), + "designers" : designers } match boardgame_type: diff --git a/src/modules/data_connection.py b/src/modules/data_connection.py index 35d6af0..28c1013 100644 --- a/src/modules/data_connection.py +++ b/src/modules/data_connection.py @@ -21,8 +21,19 @@ def get_boardgame(session: Session, boardgame_id: int) -> Union[boardgame_classe to_return_boardgame = boardgame_in_db else: to_return_boardgame = bgg_connection.get_boardgame(boardgame_id) + boardgame_new_linked_designers = [] + # for designer in to_return_boardgame.designers: + # designer_in_db = db_connection.get_designer(session, designer.id) + # if designer_in_db == None: + # db_connection.add_designer(session, designer) + # designer_in_db = db_connection.get_designer(session, designer.id) + + # boardgame_new_linked_designers.append(designer_in_db) + + # to_return_boardgame.designers = boardgame_new_linked_designers db_connection.add_boardgame(session, to_return_boardgame) to_return_boardgame = db_connection.get_boardgame(session, boardgame_id) + return to_return_boardgame @@ -32,18 +43,40 @@ def get_multiple_boardgames(session: Session, boardgame_ids: list[int]) -> list[ if len(boardgame_ids_missing) != 0: missing_boardgames = bgg_connection.get_multiple_boardgames(boardgame_ids_missing) + + # for boardgame in missing_boardgames: + # db_connection.add_multiple_designers(session, boardgame.designers) + # boardgame.designers, missing_designer_ids = db_connection.get_multiple_designers(session, [designer.id for designer in boardgame.designers]) + # assert len(missing_designer_ids) == 0, "This list should be empty" + + # missing_boardgames_linked_people = [] + + # for boardgame in missing_boardgames: + # boardgame_new_linked_designers = [] + # for designer in boardgame.designers: + # designer_in_db = db_connection.get_designer(session, designer.id) + # if designer_in_db == None: + # db_connection.add_designer(session, designer) + # designer_in_db = db_connection.get_designer(session, designer.id) + + # boardgame_new_linked_designers.append(designer_in_db) + + # boardgame.designers = boardgame_new_linked_designers + # session.refresh(boardgame) db_connection.add_multiple_boardgames(session, missing_boardgames) boardgames_in_db, boardgame_ids_missing = db_connection.get_multiple_boardgames(session, boardgame_ids=boardgame_ids) return boardgames_in_db -def get_user_collection(session: Session, ) -> list[Union[boardgame_classes.BoardGame, boardgame_classes.OwnedBoardGame]]: +def get_user_collection(session: Session) -> list[Union[boardgame_classes.BoardGame, boardgame_classes.OwnedBoardGame]]: boardgames_from_db: list[boardgame_classes.BoardGame] = db_connection.get_all_boardgames(session, boardgame_classes.BoardGame) boardgame_expansions_from_db: list[boardgame_classes.BoardGameExpansion] = db_connection.get_all_boardgames(session, boardgame_classes.BoardGameExpansion) if len(boardgames_from_db) == 0 and len(boardgame_expansions_from_db) == 0: boardgames = bgg_connection.get_user_collection() + + #db_connection.add_boardgame(session, boardgame) db_connection.add_multiple_boardgames(session, boardgames) boardgames_from_db: list[boardgame_classes.BoardGame] = db_connection.get_all_boardgames(session, boardgame_classes.BoardGame) @@ -110,7 +143,7 @@ def get_plays(session: Session) -> list[play_classes.Play]: assert len(list(filter(lambda x: x == None, played_boardgame_ids))) == 0, plays_from_db - get_multiple_boardgames(session, played_boardgame_ids + played_expansion_ids) + #get_multiple_boardgames(session, played_boardgame_ids + played_expansion_ids) return plays_from_db diff --git a/src/modules/db_connection.py b/src/modules/db_connection.py index 622a829..444200c 100644 --- a/src/modules/db_connection.py +++ b/src/modules/db_connection.py @@ -5,13 +5,13 @@ from threading import Lock critical_function_lock = Lock() -from src.classes import boardgame_classes, play_classes +from src.classes import boardgame_classes, play_classes, people_classes sqlite_url = definitions.SQLITE_URL connect_args = {"check_same_thread": False} -engine = create_engine(sqlite_url, echo=False, connect_args=connect_args) +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) def get_engine(): return engine @@ -22,14 +22,20 @@ def add_boardgame(session: Session, boardgame: Union[ boardgame_classes.WishlistBoardGame, boardgame_classes.WishlistBoardGameExpansion]): with critical_function_lock: + boardgame_designers = boardgame.designers + for designer_index in range(len(boardgame_designers)): + designer_in_db = get_designer(session, boardgame_designers[designer_index].id) + if designer_in_db != None: + boardgame.designers[designer_index] = designer_in_db + is_boardgame_present = len(session.exec( select(boardgame.__class__).where(boardgame.__class__.id == boardgame.id) ).all()) != 0 if not is_boardgame_present: session.add(boardgame) - session.commit() + session.refresh(boardgame) def add_multiple_boardgames(session: Session, boardgame_list: list[Union[ boardgame_classes.BoardGame, boardgame_classes.BoardGameExpansion, @@ -37,15 +43,47 @@ def add_multiple_boardgames(session: Session, boardgame_list: list[Union[ boardgame_classes.WishlistBoardGame, boardgame_classes.WishlistBoardGameExpansion]]): with critical_function_lock: - for boardgame in boardgame_list: - is_boardgame_present = len(session.exec( - select(boardgame.__class__).where(boardgame.__class__.id == boardgame.id) - ).all()) != 0 + altered_boardgames = [] + for boardgame_index in range(len(boardgame_list)): + boardgame_designers = boardgame_list[boardgame_index].designers + for designer_index in range(len(boardgame_designers)): + designer_in_db = get_designer(session, boardgame_designers[designer_index].id) + if designer_in_db != None: + boardgame_list[boardgame_index].designers[designer_index] = designer_in_db + + statement = select(boardgame_list[boardgame_index].__class__).where(boardgame_list[boardgame_index].__class__.id == boardgame_list[boardgame_index].id) + is_boardgame_present = len(session.exec(statement).all()) != 0 if not is_boardgame_present: - session.add(boardgame) + session.add(boardgame_list[boardgame_index]) + altered_boardgames.append(boardgame_list[boardgame_index]) + session.commit() + [session.refresh(boardgame) for boardgame in altered_boardgames] + +def get_designer(session: Session, designer_id: int) -> people_classes.Designer: + + statement = select(people_classes.Designer).where(people_classes.Designer.id == designer_id) + + designer = session.exec(statement).all() + + if len(designer) == 0: + designer = None + else: + designer = designer[0] + + return designer + +def get_multiple_designers(session: Session, designer_ids: list[int]) -> list[people_classes.Designer]: + statement = select(people_classes.Designer).where(people_classes.Designer.id.in_(designer_ids)) + results = session.exec(statement) + + designers = results.all() + + missing_designer_ids = list(filter(lambda x: x not in [designer.id for designer in designers], designer_ids)) + + return designers, missing_designer_ids def get_boardgame(session: Session, boardgame_id: int) -> Union[ boardgame_classes.BoardGame, boardgame_classes.BoardGameExpansion]: @@ -104,6 +142,7 @@ def add_play(session: Session, play: play_classes.Play): session.add(play) session.commit() + session.refresh(play) def add_multiple_plays(session: Session, play_list: list[play_classes.Play]): @@ -115,6 +154,7 @@ def add_multiple_plays(session: Session, play_list: list[play_classes.Play]): session.add(play) session.commit() + [session.refresh(play) for play in play_list] def get_plays(session: Session, ) -> list[play_classes.Play]: statement = select(play_classes.Play)