diff --git a/db/models.py b/db/models.py index f479e99..2e877a4 100644 --- a/db/models.py +++ b/db/models.py @@ -17,6 +17,7 @@ Functions: Deducts 100 units from the specified postpaid user's balance and records a drink entry. Raises HTTPException if the user is not found or if the drink entry could not be created. Returns the number of rows affected by the drink entry insertion. """ import secrets +import datetime from sqlalchemy import create_engine, text from fastapi import HTTPException @@ -444,3 +445,74 @@ def del_user_prepaid(user_id: int): raise HTTPException(status_code=404, detail="User not found") connection.commit() return result.rowcount + +def get_last_drink(user_id: int, user_is_postpaid: bool, max_since_seconds: int = 60): + if user_is_postpaid: + t = text("SELECT id, timestamp, drink_type FROM drinks WHERE postpaid_user_id = :user_id ORDER BY timestamp DESC LIMIT 1") + else: + t = text("SELECT id, timestamp, drink_type FROM drinks WHERE prepaid_user_id = :user_id ORDER BY timestamp DESC LIMIT 1") + + with engine.connect() as connection: + result = connection.execute(t, {"user_id": user_id}).fetchone() + if not result: + return None + drink_id, timestamp, drink_type = result + + if timestamp: + now = datetime.datetime.now(datetime.timezone.utc) + last_drink_time = datetime.datetime.fromisoformat(timestamp.replace("Z", "+00:00")) + # Ensure both are offset-aware + if last_drink_time.tzinfo is None: + last_drink_time = last_drink_time.replace(tzinfo=datetime.timezone.utc) + if (now - last_drink_time).total_seconds() > max_since_seconds: + return None + print(f"get_last_drink: user_id={user_id}, user_is_postpaid={user_is_postpaid}, drink_id={drink_id}, timestamp={timestamp}, drink_type={drink_type}") + return {"id": drink_id, "timestamp": timestamp, "drink_type": drink_type} + +def revert_last_drink(user_id: int, user_is_postpaid: bool, drink_id: int, drink_cost: int = DRINK_COST): + if user_is_postpaid: + del_t = text("DELETE FROM drinks WHERE postpaid_user_id = :user_id AND id = :drink_id") + update_t = text("UPDATE users_postpaid SET money = money + :drink_cost WHERE id = :user_id") + money_t = text("SELECT money FROM users_postpaid WHERE id = :user_id") + else: + del_t = text("DELETE FROM drinks WHERE prepaid_user_id = :user_id AND id = :drink_id") + update_t = text("UPDATE users_prepaid SET money = money + :drink_cost WHERE id = :user_id") + money_t = text("SELECT money FROM users_prepaid WHERE id = :user_id") + + with engine.connect() as connection: + # Check if the drink exists + print(f"revert_last_drink: user_id={user_id}, user_is_postpaid={user_is_postpaid}, drink_id={drink_id}, drink_cost={drink_cost}") + drink_exists = connection.execute(del_t, {"user_id": user_id, "drink_id": drink_id}).rowcount > 0 + if not drink_exists: + raise HTTPException(status_code=404, detail="Drink not found") + + # Revert the money + prev_money = connection.execute(money_t, {"user_id": user_id}).fetchone() + if not prev_money: + raise HTTPException(status_code=404, detail="User not found") + + new_money = prev_money[0] + drink_cost + connection.execute(update_t, {"user_id": user_id, "drink_cost": drink_cost}) + connection.commit() + + _log_transaction( + user_id=user_id, + user_is_postpaid=user_is_postpaid, + previous_money_cent=prev_money[0], + new_money_cent=new_money, + delta_money_cent=drink_cost, + description="Reverted last drink" + ) + +def update_drink_type(user_id: int, user_is_postpaid: bool, drink_id, drink_type: str): + if user_is_postpaid: + t = text("UPDATE drinks SET drink_type = :drink_type WHERE postpaid_user_id = :user_id AND id = :drink_id") + else: + t = text("UPDATE drinks SET drink_type = :drink_type WHERE prepaid_user_id = :user_id AND id = :drink_id") + + with engine.connect() as connection: + result = connection.execute(t, {"user_id": user_id, "drink_id": drink_id, "drink_type": drink_type}) + if result.rowcount == 0: + raise HTTPException(status_code=404, detail="Drink not found") + connection.commit() + return result.rowcount diff --git a/main.py b/main.py index c1b59a9..0322347 100644 --- a/main.py +++ b/main.py @@ -23,6 +23,9 @@ from db.models import drink_prepaid_user from db.models import toggle_activate_prepaid_user from db.models import set_prepaid_user_money from db.models import del_user_prepaid +from db.models import get_last_drink +from db.models import revert_last_drink +from db.models import update_drink_type from auth import oidc import os @@ -95,13 +98,14 @@ def home(request: Request): prepaid_users_from_curr_user.append(prepaid_user) # load current user from database - try: - if user_authentik["prepaid"]: - db_user = get_prepaid_user(user_db_id) - else: - db_user = get_postpaid_user(user_db_id) - except KeyError: + user_is_postpaid = get_is_postpaid(user_authentik) + if user_is_postpaid: db_user = get_postpaid_user(user_db_id) + else: + db_user = get_prepaid_user(user_db_id) + + # get last drink for current user, if not less than 60 seconds ago + last_drink = get_last_drink(user_db_id, user_is_postpaid, 60) return templates.TemplateResponse("index.html", { "request": request, @@ -110,7 +114,10 @@ def home(request: Request): "user_db_id": user_db_id, "db_user": db_user, "db_users_prepaid": db_users_prepaid, - "prepaid_users_from_curr_user": prepaid_users_from_curr_user,}) + "prepaid_users_from_curr_user": prepaid_users_from_curr_user, + "last_drink": last_drink, + "avail_drink_types": ["Paulaner Spezi", "Mio Mate", "Club Mate", "Sonstiges"], + }) @app.get("/login", response_class=HTMLResponse) def login_form(request: Request): @@ -327,6 +334,57 @@ async def popup_getraenke(): alle_getraenke = ["Wasser", "Cola", "Bier", "Mate", "Saft", "Tee", "Kaffee", "Limo"] return JSONResponse(content={"getraenke": random.sample(alle_getraenke, 4)}) +@app.post("/del_last_drink") +def del_last_drink(request: Request): + user_db_id = request.session.get("user_db_id") + if not user_db_id: + raise HTTPException(status_code=404, detail="User not found") + user_authentik = request.session.get("user_authentik") + if not user_authentik: + raise HTTPException(status_code=404, detail="User not found") + + last_drink = get_last_drink(user_db_id, True, 60) + if not last_drink: + return RedirectResponse(url="/", status_code=303) + + user_is_postpaid = get_is_postpaid(user_authentik) + + revert_last_drink(user_db_id, user_is_postpaid, last_drink["id"]) + + return RedirectResponse(url="/", status_code=303) + +@app.post("/update_drink_post") +def update_drink_post(request: Request, drink_type: str = Form(...)): + user_db_id = request.session.get("user_db_id") + if not user_db_id: + raise HTTPException(status_code=404, detail="User not found") + + user_authentik = request.session.get("user_authentik") + if not user_authentik: + raise HTTPException(status_code=404, detail="User not found") + + last_drink = get_last_drink(user_db_id, True, 60) + if not last_drink: + return RedirectResponse(url="/", status_code=303) + + if not drink_type: + raise HTTPException(status_code=400, detail="Drink type is empty") + + update_drink_type(user_db_id, get_is_postpaid(user_authentik), last_drink["id"], drink_type) + + return RedirectResponse(url="/", status_code=303) + + + +def get_is_postpaid(user_authentik) -> bool: + try: + if user_authentik["prepaid"]: + user_is_postpaid = False + else: + user_is_postpaid = True + except KeyError: + user_is_postpaid = True + return user_is_postpaid if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000) diff --git a/static/drinks/Club Mate.png b/static/drinks/Club Mate.png new file mode 100644 index 0000000..82fb509 Binary files /dev/null and b/static/drinks/Club Mate.png differ diff --git a/static/drinks/Mio Mate.png b/static/drinks/Mio Mate.png new file mode 100644 index 0000000..2de5ade Binary files /dev/null and b/static/drinks/Mio Mate.png differ diff --git a/static/drinks/Paulaner Spezi.png b/static/drinks/Paulaner Spezi.png new file mode 100644 index 0000000..c333314 Binary files /dev/null and b/static/drinks/Paulaner Spezi.png differ diff --git a/templates/index.html b/templates/index.html index a828540..17b63e3 100644 --- a/templates/index.html +++ b/templates/index.html @@ -72,7 +72,42 @@ content %} -{% endif %} {% endif %} +{% endif %} +{% if last_drink %} +
+ Getränk spezialisieren: +
+ {% for drink in avail_drink_types %} +
+ + +
+ {% endfor %} +
+
+ +
+ Letztes Getränk: +
+ Typ: {{ last_drink.drink_type }}
+ Zeit: {{ last_drink.timestamp }}
+ ID: {{ last_drink.id }} +
+
+ + +
+
+{% endif %} + +{% endif %} {% if user.prepaid %}
diff --git a/test.db b/test.db deleted file mode 100644 index 22a0e70..0000000 Binary files a/test.db and /dev/null differ