diff --git a/backend/app.py b/backend/app.py index 43f3c39..20bdede 100644 --- a/backend/app.py +++ b/backend/app.py @@ -7,7 +7,26 @@ from .settings import settings db = client["party"] -app = FastAPI() +description = """ +Party party \U0001F973 +""" + +tags_metadata = [ + { + "name": "guests", + "description": "Operations with guests (i.e., users). Intended to be called from the respective party frontend.", + }, + { + "name": "admin", + "description": "Operations for administrative purposes. Require the **admin token**. Intended to be called from the admin UI", + }, +] + +app = FastAPI( + title="PartyPage Manager", + description=description, + openapi_tags=tags_metadata, +) Coming = Literal["yes", "no", "maybe"] GrammaticalGender = Literal["m", "f", "d"] @@ -30,7 +49,7 @@ async def find_guest(party: str, token: str) -> Guest: # Guest methods -@app.get("/{party}/{token}/me", response_model=Guest) +@app.get("/{party}/{token}/me", response_model=Guest, tags=["guests"]) async def get_self(guest: Guest = Depends(find_guest)): return guest @@ -39,7 +58,7 @@ class GuestUpdate(BaseModel): coming: Coming -@app.patch("/{party}/{token}/update") +@app.patch("/{party}/{token}/update", tags=["guests"]) async def update_self( party: str, update: GuestUpdate, guest: Guest = Depends(find_guest) ): @@ -48,6 +67,22 @@ async def update_self( await db[party].replace_one({"_id": guest.id}, guest_dict) +class PartyStatus(BaseModel): + definitely_coming: int + maybe_coming: int + + +@app.get("/{party}/{token}/status", response_model=PartyStatus, tags=["guests"]) +async def get_party_status(party: str, _=Depends(find_guest)): + definitely_coming = await db[party].count_documents({"coming": "yes"}) + maybe_coming = await db[party].count_documents({"coming": "maybe"}) + + return { + "definitely_coming": definitely_coming, + "maybe_coming": maybe_coming, + } + + # Admin methods @@ -56,32 +91,47 @@ async def auth_admin(admin_token: str): raise HTTPException(status.HTTP_401_UNAUTHORIZED) -@app.get("/{party}/{admin_token}/list", response_model=list[Guest]) +@app.get("/{party}/{admin_token}/list", response_model=list[Guest], tags=["admin"]) async def list_guests(party: str, _=Depends(auth_admin)): return await db[party].find().to_list(None) -class GuestSet(BaseModel): +class GuestCreate(BaseModel): token: str name: str coming: Coming | None grammatical_gender: GrammaticalGender -@app.put("/{party}/{admin_token}/set", response_model=Guest) -async def edit_or_create_new_guest( - party: str, guest_set: GuestSet, _=Depends(auth_admin) -): - existing = await db[party].find_one({"token": guest_set.token}) +@app.put("/{party}/{admin_token}/new", response_model=Guest, tags=["admin"]) +async def create_new_guest(party: str, new_guest: GuestCreate, _=Depends(auth_admin)): + existing = await db[party].find_one({"token": new_guest.token}) if existing: - # update the existing guest - existing.update(guest_set.dict()) + raise HTTPException( + status.HTTP_400_BAD_REQUEST, f"Token {new_guest.token!r} is already in use" + ) - await db[party].replace_one({"_id": existing["_id"]}, existing) - return await db[party].find_one({"_id": existing["_id"]}) + insert_result = await db[party].insert_one(new_guest.dict()) + inserted = await db[party].find_one({"_id": insert_result.inserted_id}) + return inserted - else: - # create a new guest - insert_result = await db[party].insert_one(guest_set.dict()) - inserted = await db[party].find_one({"_id": insert_result.inserted_id}) - return inserted + +class GuestModify(MongoModel): + token: str | None + name: str | None + coming: Coming | None + grammatical_gender: GrammaticalGender | None + + +@app.patch("/{party}/{admin_token}/modify", response_model=Guest, tags=["admin"]) +async def modify_guest(party: str, modified_guest: GuestModify, _=Depends(auth_admin)): + existing = await db[party].find_one({"_id": modified_guest.id}) + if not existing: + raise HTTPException(status.HTTP_404_NOT_FOUND) + + print(modified_guest.dict()) + print(modified_guest.dict(exclude={"id"}, exclude_unset=True)) + existing.update(modified_guest.dict(exclude={"id"}, exclude_unset=True)) + + await db[party].replace_one({"_id": existing["_id"]}, existing) + return await db[party].find_one({"_id": existing["_id"]})