Co-authored-by: Sebastian Beckmann <beckmann.sebastian@outlook.de>
This commit is contained in:
+47
-12
@@ -77,18 +77,36 @@ with engine.connect() as conn:
|
||||
"""))
|
||||
|
||||
# create a table for every push on the drink button
|
||||
#conn.execute(text("""ALTER TABLE drinks ADD transaction_id INTEGER"""))
|
||||
#conn.execute(text("""DROP TABLE drinks_temp"""))
|
||||
#conn.execute(text("""
|
||||
# CREATE TABLE IF NOT EXISTS drinks_temp (
|
||||
# id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
# postpaid_user_id INTEGER,
|
||||
# prepaid_user_id INTEGER,
|
||||
# timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
# drink_type INTEGER DEFAULT 1,
|
||||
# transaction_id INTEGER
|
||||
# )
|
||||
# """))
|
||||
#conn.execute(text("""INSERT INTO drinks_temp SELECT * FROM drinks"""))
|
||||
#conn.execute(text("""DROP TABLE drinks"""))
|
||||
conn.execute(text("""
|
||||
CREATE TABLE IF NOT EXISTS drinks (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
postpaid_user_id INTEGER,
|
||||
prepaid_user_id INTEGER,
|
||||
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
drink_type INTEGER DEFAULT 1,
|
||||
FOREIGN KEY (postpaid_user_id) REFERENCES users_postpaid(id),
|
||||
FOREIGN KEY (prepaid_user_id) REFERENCES users_prepaid(id),
|
||||
FOREIGN KEY (drink_type) REFERENCES drink_types(id)
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
postpaid_user_id INTEGER,
|
||||
prepaid_user_id INTEGER,
|
||||
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
drink_type INTEGER DEFAULT 1,
|
||||
transaction_id INTEGER,
|
||||
FOREIGN KEY (postpaid_user_id) REFERENCES users_postpaid(id),
|
||||
FOREIGN KEY (prepaid_user_id) REFERENCES users_prepaid(id),
|
||||
FOREIGN KEY (drink_type) REFERENCES drink_types(id),
|
||||
FOREIGN KEY (transaction_id) REFERENCES transactions(id)
|
||||
)
|
||||
"""))
|
||||
#conn.execute(text("""INSERT INTO drinks SELECT * FROM drinks_temp"""))
|
||||
#conn.execute(text("""DROP TABLE drinks_temp"""))
|
||||
|
||||
# create a table for every money transaction
|
||||
conn.execute(text("""
|
||||
@@ -136,7 +154,8 @@ def _log_transaction(
|
||||
previous_money_cent = None,
|
||||
new_money_cent = None,
|
||||
delta_money_cent = None,
|
||||
description = None
|
||||
description = None,
|
||||
transaction_type = None
|
||||
):
|
||||
"""
|
||||
Logs a transaction for a user, recording changes in their account balance.
|
||||
@@ -328,7 +347,7 @@ def drink_postpaid_user(user_id: int, drink_type: int = 1):
|
||||
raise HTTPException(status_code=403, detail="User not activated")
|
||||
|
||||
prev_money = get_postpaid_user(user_id)["money"]
|
||||
_log_transaction(
|
||||
transaction_id = _log_transaction(
|
||||
user_id=user_id,
|
||||
user_is_postpaid=True,
|
||||
previous_money_cent=prev_money,
|
||||
@@ -342,11 +361,11 @@ def drink_postpaid_user(user_id: int, drink_type: int = 1):
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
connection.commit()
|
||||
|
||||
t_without_drink_type = text("INSERT INTO drinks (postpaid_user_id, timestamp) VALUES (:postpaid_user_id, CURRENT_TIMESTAMP)")
|
||||
t_without_drink_type = text("INSERT INTO drinks (postpaid_user_id, timestamp, transaction_id) VALUES (:postpaid_user_id, CURRENT_TIMESTAMP, :transaction_id)")
|
||||
t_update_drink_types = text("UPDATE drink_types SET quantity = quantity - 1 WHERE id = 1")
|
||||
|
||||
with engine.connect() as connection:
|
||||
result = connection.execute(t_without_drink_type, {"postpaid_user_id": user_id})
|
||||
result = connection.execute(t_without_drink_type, {"postpaid_user_id": user_id, "transaction_id": transaction_id})
|
||||
if result.rowcount == 0:
|
||||
raise HTTPException(status_code=500, detail="Failed to create drink entry")
|
||||
result2 = connection.execute(t_update_drink_types)
|
||||
@@ -942,3 +961,19 @@ def get_stats_drink_hourly():
|
||||
hourly_stats.sort(key=lambda x: x["hour"])
|
||||
|
||||
return hourly_stats
|
||||
|
||||
def get_drink_history(user_id: int, limit: int = 10):
|
||||
"""
|
||||
Retrieve the drink history for the current user, sorted by timestamp in descending order. Limited to the specified number of entries. Only usable for postpaid users as the current user.
|
||||
Args:
|
||||
user_id (int): id of postpaid user
|
||||
limit (int): max number of entries for history. Default: 10
|
||||
Returns:
|
||||
list[dict]: A list containing the transaction_id, timestamp, money after the transaction, difference to the
|
||||
last entry, description of the transaction and a path to an icon containing the image of the drink,
|
||||
if this transaction was a drink.
|
||||
"""
|
||||
t2 = text("SELECT t.id, t.timestamp, t.new_money, t.delta_money, t.description, i.icon FROM transactions as t LEFT JOIN drinks ON t.id = drinks.transaction_id LEFT JOIN drink_types as i ON i.id = drinks.drink_type WHERE t.postpaid_user_id = :user_id ORDER BY t.timestamp DESC LIMIT :limit")
|
||||
with engine.connect() as connection:
|
||||
result = connection.execute(t2, {"user_id": user_id, "limit": limit}).fetchall()
|
||||
return result
|
||||
|
||||
@@ -99,6 +99,10 @@ def home(request: Request):
|
||||
|
||||
most_used_drinks = db.models.get_most_used_drinks(user_db_id, user_is_postpaid, 99)
|
||||
most_used_drinks.append({"drink_type_id": 1, "drink_type": "Sonstiges", "count": 0, "icon": "sonstiges.png"}) # ensure "Sonstiges" is in
|
||||
|
||||
drink_history = []
|
||||
if user_is_postpaid:
|
||||
drink_history = db.models.get_drink_history(user_db_id, 100)
|
||||
|
||||
return templates.TemplateResponse("index.html", {
|
||||
"request": request,
|
||||
@@ -112,6 +116,7 @@ def home(request: Request):
|
||||
"last_regular_drink": last_regular_drink,
|
||||
"avail_drink_types": most_used_drinks,
|
||||
"drink_types": drink_types,
|
||||
"drink_history": drink_history,
|
||||
})
|
||||
|
||||
@app.get("/login", response_class=HTMLResponse)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" id="main_outline" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||
y="0px" viewBox="0 0 640 640" style="enable-background:new 0 0 640 640;" xml:space="preserve">
|
||||
<g>
|
||||
<path id="teabag" style="fill:#FFFFFF" d="M395.9,484.2l-126.9-61c-12.5-6-17.9-21.2-11.8-33.8l61-126.9c6-12.5,21.2-17.9,33.8-11.8
|
||||
c17.2,8.3,27.1,13,27.1,13l-0.1-109.2l16.7-0.1l0.1,117.1c0,0,57.4,24.2,83.1,40.1c3.7,2.3,10.2,6.8,12.9,14.4
|
||||
c2.1,6.1,2,13.1-1,19.3l-61,126.9C423.6,484.9,408.4,490.3,395.9,484.2z"/>
|
||||
<g>
|
||||
<g>
|
||||
<path style="fill:#609926" d="M622.7,149.8c-4.1-4.1-9.6-4-9.6-4s-117.2,6.6-177.9,8c-13.3,0.3-26.5,0.6-39.6,0.7c0,39.1,0,78.2,0,117.2
|
||||
c-5.5-2.6-11.1-5.3-16.6-7.9c0-36.4-0.1-109.2-0.1-109.2c-29,0.4-89.2-2.2-89.2-2.2s-141.4-7.1-156.8-8.5
|
||||
c-9.8-0.6-22.5-2.1-39,1.5c-8.7,1.8-33.5,7.4-53.8,26.9C-4.9,212.4,6.6,276.2,8,285.8c1.7,11.7,6.9,44.2,31.7,72.5
|
||||
c45.8,56.1,144.4,54.8,144.4,54.8s12.1,28.9,30.6,55.5c25,33.1,50.7,58.9,75.7,62c63,0,188.9-0.1,188.9-0.1s12,0.1,28.3-10.3
|
||||
c14-8.5,26.5-23.4,26.5-23.4s12.9-13.8,30.9-45.3c5.5-9.7,10.1-19.1,14.1-28c0,0,55.2-117.1,55.2-231.1
|
||||
C633.2,157.9,624.7,151.8,622.7,149.8z M125.6,353.9c-25.9-8.5-36.9-18.7-36.9-18.7S69.6,321.8,60,295.4
|
||||
c-16.5-44.2-1.4-71.2-1.4-71.2s8.4-22.5,38.5-30c13.8-3.7,31-3.1,31-3.1s7.1,59.4,15.7,94.2c7.2,29.2,24.8,77.7,24.8,77.7
|
||||
S142.5,359.9,125.6,353.9z M425.9,461.5c0,0-6.1,14.5-19.6,15.4c-5.8,0.4-10.3-1.2-10.3-1.2s-0.3-0.1-5.3-2.1l-112.9-55
|
||||
c0,0-10.9-5.7-12.8-15.6c-2.2-8.1,2.7-18.1,2.7-18.1L322,273c0,0,4.8-9.7,12.2-13c0.6-0.3,2.3-1,4.5-1.5c8.1-2.1,18,2.8,18,2.8
|
||||
l110.7,53.7c0,0,12.6,5.7,15.3,16.2c1.9,7.4-0.5,14-1.8,17.2C474.6,363.8,425.9,461.5,425.9,461.5z"/>
|
||||
<path style="fill:#609926" d="M326.8,380.1c-8.2,0.1-15.4,5.8-17.3,13.8c-1.9,8,2,16.3,9.1,20c7.7,4,17.5,1.8,22.7-5.4
|
||||
c5.1-7.1,4.3-16.9-1.8-23.1l24-49.1c1.5,0.1,3.7,0.2,6.2-0.5c4.1-0.9,7.1-3.6,7.1-3.6c4.2,1.8,8.6,3.8,13.2,6.1
|
||||
c4.8,2.4,9.3,4.9,13.4,7.3c0.9,0.5,1.8,1.1,2.8,1.9c1.6,1.3,3.4,3.1,4.7,5.5c1.9,5.5-1.9,14.9-1.9,14.9
|
||||
c-2.3,7.6-18.4,40.6-18.4,40.6c-8.1-0.2-15.3,5-17.7,12.5c-2.6,8.1,1.1,17.3,8.9,21.3c7.8,4,17.4,1.7,22.5-5.3
|
||||
c5-6.8,4.6-16.3-1.1-22.6c1.9-3.7,3.7-7.4,5.6-11.3c5-10.4,13.5-30.4,13.5-30.4c0.9-1.7,5.7-10.3,2.7-21.3
|
||||
c-2.5-11.4-12.6-16.7-12.6-16.7c-12.2-7.9-29.2-15.2-29.2-15.2s0-4.1-1.1-7.1c-1.1-3.1-2.8-5.1-3.9-6.3c4.7-9.7,9.4-19.3,14.1-29
|
||||
c-4.1-2-8.1-4-12.2-6.1c-4.8,9.8-9.7,19.7-14.5,29.5c-6.7-0.1-12.9,3.5-16.1,9.4c-3.4,6.3-2.7,14.1,1.9,19.8
|
||||
C343.2,346.5,335,363.3,326.8,380.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
+7
-10
@@ -24,16 +24,13 @@
|
||||
fill: #0366d6;
|
||||
}
|
||||
</style>
|
||||
<a class="github-icon-link" href="https://github.com/Moritz921/GetraenkelisteWebsite" target="_blank" title="GitHub" rel="noopener">
|
||||
<svg viewBox="0 0 16 16" aria-hidden="true">
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38
|
||||
0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52
|
||||
-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2
|
||||
-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64
|
||||
-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08
|
||||
2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01
|
||||
1.93-.01 2.19 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"/>
|
||||
</svg>
|
||||
<a class="github-icon-link" href="https://git.fs.cs.uni-frankfurt.de/Fachschaft/GetraenkelisteWebsite" target="_blank" title="Gitea" rel="noopener">
|
||||
<img
|
||||
src="/static/gitea.svg"
|
||||
alt="Gitea Logo"
|
||||
height="64"
|
||||
width="64"
|
||||
/>
|
||||
</a>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
+75
-2
@@ -242,7 +242,81 @@ content %}
|
||||
<p>Es sind keine Prepaid-User vorhanden.</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if 'Getraenkeliste Verantwortliche' in user.groups %}
|
||||
|
||||
|
||||
{% if drink_history %}
|
||||
<style>
|
||||
.hidden {
|
||||
display: none
|
||||
}
|
||||
</style>
|
||||
<h3>Historie</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="padding: 0.5em 1em">Datum / Uhrzeit</th>
|
||||
<th style="padding: 0.5em 1em">Veränderung</th>
|
||||
<th style="padding: 0.5em 1em">Neuer Kontostand</th>
|
||||
<th style="padding: 0.5em 1em">Transaktionsart</th>
|
||||
<th style="padding: 0.5em 1em">Icon</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for drink in drink_history %}
|
||||
{% if loop.index <= 10 %}
|
||||
<tr>
|
||||
{% else %}
|
||||
<tr class="long-history-hidden hidden">
|
||||
{% endif %}
|
||||
<td style="padding: 0.5em 1em">{{ drink.timestamp }} UTC</td>
|
||||
<td style="padding: 0.5em 1em">
|
||||
{% if drink.delta_money > 0 %}
|
||||
+{{ drink.delta_money / 100}} €
|
||||
{% else %}
|
||||
{{ drink.delta_money / 100}} €
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="padding: 0.5em 1em">{{ drink.new_money / 100}} €</td>
|
||||
<td style="padding: 0.5em 1em">
|
||||
{% if drink.description == "Drink button pressed" %}
|
||||
Strich hinzugefügt
|
||||
{% elif drink.description == "Reverted last drink" %}
|
||||
Strich rückgängig gemacht
|
||||
{% elif drink.description == "Set money manually via Admin UI" %}
|
||||
Änderung durch Admin
|
||||
{% else %}
|
||||
{{ drink.description }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="padding: 0.5em 1em">
|
||||
{% if drink.icon %}
|
||||
<img src="/static/drinks/{{ drink.icon }}" alt="{{ drink.icon }}" style="width:48px; height:48px; object-fit:contain;">
|
||||
{% elif drink.description == "Drink button pressed" %}
|
||||
:(
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="long-history-hidden hidden">Es werden maximal 100 Ergebnisse angezeigt.</div>
|
||||
<button id="history-expand" class="long-history-hidden-inv">Mehr anzeigen</button>
|
||||
<button id="history-collapse" class="long-history-hidden hidden" type="button">Weniger anzeigen</button>
|
||||
<script>
|
||||
const hidden = () => document.querySelectorAll('.long-history-hidden');
|
||||
const hidden_inv = () => document.querySelectorAll('.long-history-hidden-inv');
|
||||
document.getElementById('history-expand').addEventListener('click', () => {
|
||||
hidden().forEach(el => el.classList.remove('hidden'));
|
||||
hidden_inv().forEach(el => el.classList.add('hidden'));
|
||||
});
|
||||
document.getElementById('history-collapse').addEventListener('click', () => {
|
||||
hidden().forEach(el => el.classList.add('hidden'));
|
||||
hidden_inv().forEach(el => el.classList.remove('hidden'));
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
{% if 'Getraenkeliste Verantwortliche' in user.groups %}
|
||||
<h2>Admin Interface</h2>
|
||||
<p>Ausgleichszahlung:</p>
|
||||
<p>Der eingegebene Betrag wird vom aktuell eingeloggten Nutzer abgezogen und dem eingetragenem Nutzer gutgeschrieben.</p>
|
||||
@@ -403,7 +477,6 @@ content %}
|
||||
<input id="drink_type_quantity" type="number" name="quantity" min="0" step="1" required style="padding: 0.5em; border: 1px solid #ccc; border-radius: 4px; width: 100px;" />
|
||||
<button type="submit" style="padding: 0.5em 1em; background: rgb(0, 97, 143); color: #fff; border: none; border-radius: 4px; cursor: pointer;">Setzen</button>
|
||||
</form>
|
||||
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user