Fix #1
This commit is contained in:
72
db/models.py
72
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
|
||||
|
||||
72
main.py
72
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)
|
||||
|
||||
BIN
static/drinks/Club Mate.png
Normal file
BIN
static/drinks/Club Mate.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 137 KiB |
BIN
static/drinks/Mio Mate.png
Normal file
BIN
static/drinks/Mio Mate.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 164 KiB |
BIN
static/drinks/Paulaner Spezi.png
Normal file
BIN
static/drinks/Paulaner Spezi.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 625 KiB |
@@ -72,7 +72,42 @@ content %}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %} {% endif %}
|
||||
{% endif %}
|
||||
{% if last_drink %}
|
||||
<div style="margin: 1em 0; text-align: center;">
|
||||
<strong>Getränk spezialisieren:</strong>
|
||||
<div style="display: flex; flex-wrap: wrap; justify-content: center; gap: 1em; margin-top: 1em;">
|
||||
{% for drink in avail_drink_types %}
|
||||
<form method="post" action="/update_drink_post" style="display: inline-block;">
|
||||
<input type="hidden" name="drink_type" value="{{ drink }}">
|
||||
<button type="submit"
|
||||
style="display: flex; flex-direction: column; align-items: center; background-color: #00618F; color: #fff; border: none; border-radius: 8px; padding: 0.7em 1.2em; cursor: pointer; min-width: 120px;">
|
||||
<img src="/static/drinks/{{ drink|lower }}.png" alt="{{ drink }}" style="width:48px; height:48px; object-fit:contain; margin-bottom:0.5em;">
|
||||
<span>{{ drink }}</span>
|
||||
</button>
|
||||
</form>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin: 1em 0; text-align: center;">
|
||||
<strong>Letztes Getränk:</strong>
|
||||
<div style="margin: 0.5em 0;">
|
||||
Typ: {{ last_drink.drink_type }}<br>
|
||||
Zeit: {{ last_drink.timestamp }}<br>
|
||||
ID: {{ last_drink.id }}
|
||||
</div>
|
||||
<form method="post" action="/del_last_drink" style="display: inline;">
|
||||
<input type="hidden" name="drink_id" value="{{ last_drink.drink_id }}">
|
||||
<button type="submit"
|
||||
style="background-color: #c0392b; color: #fff; border: none; border-radius: 6px; padding: 0.5em 1em; cursor: pointer;">
|
||||
Getränk löschen
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% if user.prepaid %}
|
||||
<div style="display: flex; justify-content: center; text-align: center">
|
||||
|
||||
Reference in New Issue
Block a user