diff --git a/db/database.db b/db/database.db new file mode 100644 index 0000000..9b0b041 Binary files /dev/null and b/db/database.db differ diff --git a/src/classes/boardgame_classes.py b/src/classes/boardgame_classes.py index 95fa245..d8abdcf 100644 --- a/src/classes/boardgame_classes.py +++ b/src/classes/boardgame_classes.py @@ -1,6 +1,7 @@ -from pydantic import BaseModel, HttpUrl from datetime import date from enum import Enum +from sqlmodel import Field, SQLModel + class BoardgameType(Enum): BOARDGAME = 'boardgame' @@ -11,36 +12,55 @@ class BoardgameType(Enum): WISHLISTBOARDGAMEEXPANSION = 'wishlistboardgameexpansion' -class BoardGame(BaseModel): - id: int +class BoardGameBase(SQLModel): name: str description: str - image_url : HttpUrl - thumbnail_url : HttpUrl + weight: float + image_url : str + thumbnail_url : str year_published: int min_players: int max_players: int min_playing_time: int max_playing_time: int min_age: int - all_expansion_ids: list[int] - type: BoardgameType = BoardgameType.BOARDGAME -class BoardGameExpansion(BoardGame): - type: BoardgameType = BoardgameType.BOARDGAMEEXPANSION + model_config = { + 'validate_assignment':True + } -class OwnedBoardGame(BoardGame): +class OwnedBoardGameBase(BoardGameBase): price_paid: float acquisition_date: date acquired_from: str + +class WishlistBoardGameBase(BoardGameBase): + wishlist_priority: int + + +class BoardGame(BoardGameBase, table=True): + id: int = Field(primary_key=True) + type: BoardgameType = BoardgameType.BOARDGAME + +class BoardGameExpansion(BoardGameBase, table=True): + id: int = Field(primary_key=True) + expansion_for: int = Field(default=None, foreign_key="boardgame.id") + type: BoardgameType = BoardgameType.BOARDGAMEEXPANSION + +class OwnedBoardGame(OwnedBoardGameBase, table=True): + id: int = Field(primary_key=True) type: BoardgameType = BoardgameType.OWNEDBOARDGAME -class OwnedBoardGameExpansion(OwnedBoardGame): +class OwnedBoardGameExpansion(OwnedBoardGameBase, table=True): + id: int = Field(primary_key=True) + expansion_for: int = Field(default=None, foreign_key="boardgame.id") type: BoardgameType = BoardgameType.OWNEDBOARDGAMEEXPANSION -class WishlistBoardGame(BoardGame): - wishlist_priority: int +class WishlistBoardGame(WishlistBoardGameBase, table=True): + id: int = Field(primary_key=True) type: BoardgameType = BoardgameType.WISHLISTBOARDGAME -class WishlistBoardGameExpansion(WishlistBoardGame): +class WishlistBoardGameExpansion(WishlistBoardGameBase, table=True): + id: int = Field(primary_key=True) + expansion_for: int = Field(default=None, foreign_key="boardgame.id") type: BoardgameType = BoardgameType.WISHLISTBOARDGAMEEXPANSION \ No newline at end of file diff --git a/src/config/definitions.py b/src/config/definitions.py index 04ccd5a..3068397 100644 --- a/src/config/definitions.py +++ b/src/config/definitions.py @@ -2,6 +2,10 @@ import os ROOT_PATH = project_root = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) SECRETS_FILE_PATH = ROOT_PATH + '/secrets/auth.yaml' +DATABASE_FILE_PATH = ROOT_PATH + '/db/database.db' + +DATABASE_FILE_PROJECT_PATH = f"/db/database.db" +SQLITE_URL = f"sqlite://{DATABASE_FILE_PROJECT_PATH}" BGG_MAX_THING_BOARDGAMES = 20 BGG_PLAY_PAGE_SIZE = 100 \ No newline at end of file diff --git a/src/main.py b/src/main.py index a52b5da..5fd2785 100644 --- a/src/main.py +++ b/src/main.py @@ -1,4 +1,5 @@ from typing import Union + from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from contextlib import asynccontextmanager @@ -34,23 +35,21 @@ def read_root(): @app.get("/boardgame/{boardgame_id}", response_model=boardgame_classes.BoardGame) def get_boardgame_by_id(boardgame_id: int): - requested_boardgame: boardgame_classes.BoardGame = bgg_connection.get_boardgame(boardgame_id) + requested_boardgame: boardgame_classes.BoardGame = data_connection.get_boardgame(boardgame_id) return requested_boardgame -@app.get("/owned", response_model=list[boardgame_classes.OwnedBoardGame]) +@app.get("/owned", response_model=list[Union[boardgame_classes.OwnedBoardGame, boardgame_classes.OwnedBoardGameExpansion]]) def get_owned_collection(): - requested_collection: list[boardgame_classes.OwnedBoardGame] = bgg_connection.get_user_owned_collection() - return requested_collection + return data_connection.get_user_owned_collection() -@app.get("/wishlist", response_model=list[boardgame_classes.WishlistBoardGame]) +@app.get("/wishlist", response_model=list[Union[boardgame_classes.WishlistBoardGame, boardgame_classes.WishlistBoardGameExpansion]]) def get_wishlist_collection(): - requested_collection: list[boardgame_classes.WishlistBoardGame] = bgg_connection.get_user_wishlist_collection() - return requested_collection + return data_connection.get_user_wishlist_collection() @app.get("/plays", response_model=list[play_classes.Play]) def get_plays(): - requested_plays: list[play_classes.Play] = bgg_connection.get_plays() + requested_plays: list[play_classes.Play] = data_connection.get_plays() return requested_plays \ No newline at end of file diff --git a/src/modules/bgg_connection.py b/src/modules/bgg_connection.py index 238bf59..9154ddf 100644 --- a/src/modules/bgg_connection.py +++ b/src/modules/bgg_connection.py @@ -5,6 +5,7 @@ import requests from datetime import datetime import time import math +from typing import Union from src.classes import boardgame_classes, play_classes from src.modules import auth_manager @@ -72,6 +73,7 @@ def convert_xml_to_boardgame(boardgame_xml: ET.Element) -> boardgame_classes.Boa boardgame_dict = { "id" : int(boardgame_xml.get('id')), "name" : boardgame_xml.find('name').get('value'), + "weight": boardgame_xml.find('statistics').find('ratings').find('averageweight').get('value'), "description" : boardgame_xml.find('description').text, "image_url" : boardgame_xml.find('image').text, "thumbnail_url" : boardgame_xml.find('thumbnail').text, @@ -80,21 +82,22 @@ def convert_xml_to_boardgame(boardgame_xml: ET.Element) -> boardgame_classes.Boa "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')), - "all_expansion_ids" : expansion_ids + "min_age" : int(boardgame_xml.find('minage').get('value')) } match boardgame_type: case "boardgame": boardgame = boardgame_classes.BoardGame(**boardgame_dict) case "boardgameexpansion": + expansion_for = expansion_ids[0] + boardgame_dict['expansion_for'] = expansion_for boardgame = boardgame_classes.BoardGameExpansion(**boardgame_dict) return boardgame -def convert_collection_xml_to_owned_boardgame(boardgame_extra_info: boardgame_classes.BoardGame, collection_boardgame_xml: ET.Element) -> boardgame_classes.OwnedBoardGame: +def convert_collection_xml_to_owned_boardgame(boardgame_extra_info: boardgame_classes.BoardGame, collection_boardgame_xml: ET.Element) -> Union[boardgame_classes.OwnedBoardGame, boardgame_classes.OwnedBoardGameExpansion]: boardgame_type = collection_boardgame_xml.get('subtype') @@ -121,18 +124,7 @@ def convert_collection_xml_to_owned_boardgame(boardgame_extra_info: boardgame_cl } boardgame_dict = { - "id" : boardgame_extra_info.id, - "name" : boardgame_extra_info.name, - "description" : boardgame_extra_info.description, - "image_url" : boardgame_extra_info.image_url, - "thumbnail_url" : boardgame_extra_info.thumbnail_url, - "year_published" : boardgame_extra_info.year_published, - "min_players" : boardgame_extra_info.min_players, - "max_players" : boardgame_extra_info.max_players, - "min_playing_time" : boardgame_extra_info.min_playing_time, - "max_playing_time" : boardgame_extra_info.max_playing_time, - "min_age" : boardgame_extra_info.min_age, - "all_expansion_ids" : boardgame_extra_info.all_expansion_ids, + **boardgame_extra_info.__dict__, **owned_boardgame_dict } @@ -140,6 +132,7 @@ def convert_collection_xml_to_owned_boardgame(boardgame_extra_info: boardgame_cl case "boardgame": boardgame = boardgame_classes.OwnedBoardGame(**boardgame_dict) case "boardgameexpansion": + boardgame_dict['expansion_for'] = boardgame_extra_info.expansion_for boardgame = boardgame_classes.OwnedBoardGameExpansion(**boardgame_dict) @@ -157,18 +150,7 @@ def convert_collection_xml_to_wishlist_boardgame(boardgame_extra_info: boardgame } boardgame_dict = { - "id" : boardgame_extra_info.id, - "name" : boardgame_extra_info.name, - "description" : boardgame_extra_info.description, - "image_url" : boardgame_extra_info.image_url, - "thumbnail_url" : boardgame_extra_info.thumbnail_url, - "year_published" : boardgame_extra_info.year_published, - "min_players" : boardgame_extra_info.min_players, - "max_players" : boardgame_extra_info.max_players, - "min_playing_time" : boardgame_extra_info.min_playing_time, - "max_playing_time" : boardgame_extra_info.max_playing_time, - "min_age" : boardgame_extra_info.min_age, - "all_expansion_ids" : boardgame_extra_info.all_expansion_ids, + **boardgame_extra_info.__dict__, **wishlist_boardgame_dict } @@ -176,6 +158,7 @@ def convert_collection_xml_to_wishlist_boardgame(boardgame_extra_info: boardgame case "boardgame": boardgame = boardgame_classes.WishlistBoardGame(**boardgame_dict) case "boardgameexpansion": + boardgame_dict['expansion_for'] = boardgame_extra_info.expansion_for boardgame = boardgame_classes.WishlistBoardGameExpansion(**boardgame_dict) diff --git a/src/modules/data_connection.py b/src/modules/data_connection.py new file mode 100644 index 0000000..8649922 --- /dev/null +++ b/src/modules/data_connection.py @@ -0,0 +1,53 @@ +from typing import Union + +from src.modules import bgg_connection, db_connection +from src.classes import boardgame_classes, play_classes + + +def get_boardgame(boardgame_id: int) -> boardgame_classes.BoardGame: + #Will check if it already exists in db, then it will get it from there + + boardgame_in_db: list[boardgame_classes.BoardGame] = db_connection.get_base_boardgames(boardgame_id=boardgame_id) + + to_return_boardgame = None + + if len(boardgame_in_db) != 0: + to_return_boardgame = boardgame_in_db[0] + else: + to_return_boardgame = bgg_connection.get_boardgame(boardgame_id) + db_connection.add_boardgame(to_return_boardgame) + + return to_return_boardgame + + +def get_user_owned_collection() -> list[Union[boardgame_classes.OwnedBoardGame, boardgame_classes.OwnedBoardGameExpansion]]: + owned_boardgames_from_db = db_connection.get_all_owned_boardgames() + owned_boardgame_expanions_from_db = db_connection.get_all_owned_boardgames_expansions() + + boardgames_to_return = [] + + if len(owned_boardgames_from_db) == 0 and len(owned_boardgame_expanions_from_db) == 0: + owned_boardgames = bgg_connection.get_user_owned_collection() + for boardgame in owned_boardgames: + db_connection.add_boardgame(boardgame) + + return owned_boardgames + else: + return owned_boardgames_from_db + + + +def get_user_wishlist_collection() -> Union[list[boardgame_classes.WishlistBoardGame], list[boardgame_classes.WishlistBoardGameExpansion]]: + + owned_boardgames_from_db = db_connection.get_all_wishlisted_boardgames + + return bgg_connection.get_user_wishlist_collection() + +def get_plays() -> list[play_classes.Play]: + return bgg_connection.get_plays() + +def delete_database(): + db_connection.delete_database() + +def create_db_and_tables(): + db_connection.create_db_and_tables() \ No newline at end of file diff --git a/src/modules/db_connection.py b/src/modules/db_connection.py new file mode 100644 index 0000000..62715a0 --- /dev/null +++ b/src/modules/db_connection.py @@ -0,0 +1,64 @@ +from sqlmodel import create_engine, SQLModel, Session, select +from src.config import definitions +import os +from typing import Union + +from src.classes import boardgame_classes + +sqlite_url = definitions.SQLITE_URL + + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + +def add_boardgame(boardgame: Union[ + boardgame_classes.BoardGame, boardgame_classes.BoardGameExpansion, + boardgame_classes.OwnedBoardGame, boardgame_classes.OwnedBoardGameExpansion, + boardgame_classes.WishlistBoardGame, boardgame_classes.WishlistBoardGameExpansion]): + + with Session(engine) as session: + session.add(boardgame) + + session.commit() + session.refresh(boardgame) + +def get_base_boardgames(boardgame_id = None) -> list[boardgame_classes.BoardGame]: + with Session(engine) as session: + session.expire_on_commit = False + if boardgame_id == None: + statement = select(boardgame_classes.BoardGame) + else: + statement = select(boardgame_classes.BoardGame).where(boardgame_classes.BoardGame.id == boardgame_id) + results = session.exec(statement) + + boardgame_list = results.all() + + return boardgame_list + + +def get_all_owned_boardgames() -> list[boardgame_classes.OwnedBoardGame]: + with Session(engine) as session: + statement = select(boardgame_classes.OwnedBoardGame) + results = session.exec(statement) + + boardgame_list = results.all() + + return boardgame_list + +def get_all_owned_boardgames_expansions() -> list[boardgame_classes.OwnedBoardGameExpansion]: + with Session(engine) as session: + statement = select(boardgame_classes.OwnedBoardGameExpansion) + results = session.exec(statement) + + boardgame_list = results.all() + + return boardgame_list + +def get_all_wishlisted_boardgames() -> list[boardgame_classes.WishlistBoardGame]: + pass + +def delete_database(): + os.remove(definitions.DATABASE_FILE_PATH) + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) \ No newline at end of file diff --git a/tests/test_main.py b/tests/test_main.py index 93fd9fd..95126e9 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -11,6 +11,7 @@ client = TestClient(app) def default_boardgame_test(to_test_boardgame: boardgame_classes.BoardGame): assert type(to_test_boardgame.id) == int assert type(to_test_boardgame.name) == str + assert type(to_test_boardgame.weight) == float assert type(to_test_boardgame.description) == str assert validators.url(str(to_test_boardgame.image_url)) assert validators.url(str(to_test_boardgame.thumbnail_url)) @@ -20,8 +21,6 @@ def default_boardgame_test(to_test_boardgame: boardgame_classes.BoardGame): assert type(to_test_boardgame.min_playing_time) == int assert type(to_test_boardgame.max_playing_time) == int assert type(to_test_boardgame.min_age) == int - assert type(to_test_boardgame.all_expansion_ids) == list - assert type(to_test_boardgame.all_expansion_ids[0]) == int assert type(to_test_boardgame.type) == boardgame_classes.BoardgameType