diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c2820c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.env/ +**/__pycache__/ \ No newline at end of file diff --git a/data/max/wishlist.csv b/data/max/wishlist.csv index 4d353b2..34a1d46 100644 --- a/data/max/wishlist.csv +++ b/data/max/wishlist.csv @@ -1,2 +1,3 @@ -"id","name","description","seller","image","price" -"d3e203ba-9aab-4283-8c46-f4a46a5d1f62","Dune: Part 2 - 4K HDR Blu-Ray","Der zweite Film der Dune Reihe in bester Qualität auf Blu-Ray. Die Qualität ist für den Film wichtig, weshalb auf 4K HDR geachtet werden sollte. Das ist in der Regel gut auf der Verpackung gekennzeichnet.",,,29.99 \ No newline at end of file +"id","name","description","shop","image","price" +"d3e203ba-9aab-4283-8c46-f4a46a5d1f62","Dune: Part 2 - 4K HDR Blu-Ray","Der zweite Film der Dune Reihe in bester Qualität auf Blu-Ray. Die Qualität ist für den Film wichtig, weshalb auf 4K HDR geachtet werden sollte. Das ist in der Regel gut auf der Verpackung gekennzeichnet.","https://www.mediamarkt.de/de/product/_dune-part-two-blu-ray-2923536.html","https://assets.mmsrg.com/isr/166325/c1/-/ASSET_MMS_138728291?x=536&y=402&format=jpg&quality=80&sp=yes&strip=yes&trim&ex=536&ey=402&align=center&resizesource&unsharp=1.5x1+0.7+0.02&cox=0&coy=0&cdx=536&cdy=402",29.99 +"77b3c3a0-303b-482f-a1e0-67c29030ec69","Ex-Machina","","","",14.99 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..be08dc6 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +fastapi[standard] \ No newline at end of file diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..0876a45 --- /dev/null +++ b/src/main.py @@ -0,0 +1,22 @@ +import uuid +from fastapi import FastAPI, Request, Response +from fastapi.responses import HTMLResponse +from fastapi.templating import Jinja2Templates +from wishlist import read_all_wishlists + +WISHLISTS_DIR = "../data" + +app = FastAPI() + +templates = Jinja2Templates(directory="templates") + +@app.get("/{id}/view", response_class=HTMLResponse) +async def read_item(request: Request, id: uuid.UUID): + wishlists = read_all_wishlists(WISHLISTS_DIR) + + if str(id) not in wishlists.keys(): + return Response(status_code=404) + + return templates.TemplateResponse( + request=request, name="wishlist_view.html", context={"wishlist": wishlists[str(id)]} + ) \ No newline at end of file diff --git a/src/templates/wishlist_view.html b/src/templates/wishlist_view.html new file mode 100644 index 0000000..d512123 --- /dev/null +++ b/src/templates/wishlist_view.html @@ -0,0 +1,177 @@ + + + + + + {{ wishlist.config.title }} + + + +

{{ wishlist.config.title }}

+ + + + +

Reserved Items (click to expand/collapse)

+ + + + + diff --git a/src/wishlist/__init__.py b/src/wishlist/__init__.py new file mode 100644 index 0000000..8ebe8f8 --- /dev/null +++ b/src/wishlist/__init__.py @@ -0,0 +1,2 @@ +from .models import Wishlist, WishlistConfig, WishlistItem +from .storage import read_all_wishlists \ No newline at end of file diff --git a/src/wishlist/models.py b/src/wishlist/models.py new file mode 100644 index 0000000..b49ff0e --- /dev/null +++ b/src/wishlist/models.py @@ -0,0 +1,33 @@ +import uuid +from datetime import date + +class ItemReservation: + def __init__(self) -> None: + self.name: str = "" + +class WishlistItem: + def __init__(self) -> None: + self.id: uuid = uuid.uuid4() + self.name: str = "" + self.description: str = "" + self.shop: str = "" + self.image: str = "" + self.price: float = 0 + self.reservation: ItemReservation | None = None + + @property + def is_reserved(self) -> bool: + return self.reservation is not None + +class WishlistConfig: + def __init__(self) -> None: + self.id: uuid = uuid.uuid4() + self.title: str = "" + self.deadlines: dict[str, date] = {} + self.deadline_offset_days: int = 0 + +class Wishlist: + def __init__(self) -> None: + self.directory: str = "" + self.items: dict[str, WishlistItem] = {} # Mapping Item ID to item + self.config: WishlistConfig = None \ No newline at end of file diff --git a/src/wishlist/storage.py b/src/wishlist/storage.py new file mode 100644 index 0000000..4a17c54 --- /dev/null +++ b/src/wishlist/storage.py @@ -0,0 +1,60 @@ +from .models import Wishlist, WishlistConfig, WishlistItem +from datetime import date +import uuid +import json +import csv +import os + +def read_wishlist_config(wishlist_dir: str) -> WishlistConfig: + path: str = f"{wishlist_dir}/config.json" + with open(path, "r", encoding="UTF-8") as fp: + json_config = json.load(fp) + + config = WishlistConfig() + config.id = uuid.UUID(json_config["id"]) + config.title = json_config["title"] + config.deadline_offset_days = json_config["deadlineOffsetDays"] + + for label, timestamp in json_config["deadlines"].items(): + config.deadlines[label] = date.fromisoformat(timestamp) + + return config + +def read_wishlist_items(wishlist_dir: str) -> dict[str, WishlistItem]: + items: dict[str, WishlistItem] = {} + path: str = f"{wishlist_dir}/wishlist.csv" + with open(path, "r", encoding = "UTF-8") as fp: + csvreader = csv.DictReader(fp) + + for row in csvreader: + item: WishlistItem = WishlistItem() + item.id = uuid.UUID(row.get("id")) + item.name = row.get("name") + item.description = row.get("description") + item.shop = row.get("shop") + item.image = row.get("image") + item.price = float(row.get("price")) + + items[str(item.id)] = item + + return items + + +def read_wishlist(wishlist_dir: str) -> Wishlist: + wishlist = Wishlist() + wishlist.directory = wishlist_dir + + wishlist.items = read_wishlist_items(wishlist.directory) + wishlist.config = read_wishlist_config(wishlist.directory) + + return wishlist + +def read_all_wishlists(directory: str) -> dict[str, Wishlist]: + parent_dir, wishlist_dirs, _ = list(os.walk(directory))[0] + + wishlists: dict[str, Wishlist] = {} + for dir in wishlist_dirs: + wishlist: Wishlist = read_wishlist(f"{parent_dir}/{dir}") + wishlists[str(wishlist.config.id)] = wishlist + + return wishlists \ No newline at end of file