Compare commits
2 Commits
1a3c3d8f47
...
dc473ec5f9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc473ec5f9 | ||
|
|
d5336d1800 |
|
|
@ -579,3 +579,30 @@ def get_submitted_matches(player_id):
|
|||
connection.close()
|
||||
|
||||
return [dict(row) for row in rows]
|
||||
|
||||
|
||||
|
||||
|
||||
def get_match_history_log(player_id):
|
||||
"""Holt ALLE Matches eines Spielers inklusive der MMR-Änderungen für das Log."""
|
||||
connection = sqlite3.connect(DB_PATH)
|
||||
connection.row_factory = sqlite3.Row
|
||||
cursor = connection.cursor()
|
||||
|
||||
query = """
|
||||
SELECT m.played_at, sys.name AS gamesystem_name,
|
||||
m.player1_id, p1.display_name AS p1_display, p1.discord_name AS p1_discord, m.score_player1, m.player1_mmr_change,
|
||||
m.player2_id, p2.display_name AS p2_display, p2.discord_name AS p2_discord, m.score_player2, m.player2_mmr_change,
|
||||
m.player2_check, m.match_is_counted
|
||||
FROM matches m
|
||||
JOIN gamesystems sys ON m.gamesystem_id = sys.id
|
||||
JOIN players p1 ON m.player1_id = p1.id
|
||||
JOIN players p2 ON m.player2_id = p2.id
|
||||
WHERE m.player1_id = ? OR m.player2_id = ?
|
||||
ORDER BY m.played_at DESC
|
||||
"""
|
||||
cursor.execute(query, (player_id, player_id))
|
||||
rows = cursor.fetchall()
|
||||
connection.close()
|
||||
|
||||
return [dict(row) for row in rows]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
from nicegui import ui
|
||||
|
||||
# Funktion die jede GUI Seite am Anfang aufrufen kann. Dann haben alle die gleichen Farbeinstellungen. Und, wenn man was ändert,
|
||||
# muss man es nur hier ändern!
|
||||
|
||||
def apply_design():
|
||||
ui.add_css('body { background-color: #18181b; }')
|
||||
|
||||
|
|
|
|||
|
|
@ -171,55 +171,67 @@ def setup_routes(admin_discord_id):
|
|||
placements = data_api.get_player_statistics(player_id)
|
||||
systems = data_api.get_gamesystem_data()
|
||||
|
||||
# Python-Trick: Wir wandeln die Spieler-Stats in ein "Wörterbuch" (Dictionary) um.
|
||||
# So können wir blitzschnell über die System-ID nachschauen, ob er Stats hat.
|
||||
my_stats = { p['gamesystem_id']: p for p in placements }
|
||||
|
||||
def click_join_league(p_id, sys_id):
|
||||
data_api.join_league(p_id, sys_id)
|
||||
ui.navigate.to("/")
|
||||
|
||||
# --- NEU: Wir machen die Funktion allgemein ---
|
||||
def toggle_visibility(row_a, row_b):
|
||||
row_a.visible = not row_a.visible
|
||||
row_b.visible = not row_b.visible
|
||||
|
||||
with ui.element('div').classes("w-full grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 items-center"):
|
||||
for place in placements:
|
||||
|
||||
# LOGIK UMGEKEHRT: Wir schleifen über den Katalog der verfügbaren Systeme!
|
||||
for sys in systems:
|
||||
sys_id = sys['id']
|
||||
sys_name = sys['name']
|
||||
|
||||
sys_card = ui.card().classes("h-60 w-full items-center justify-center transition-colors")
|
||||
|
||||
with sys_card:
|
||||
# Kopfzeile der Karte (Name & Beschreibung)
|
||||
ui.label(text=place['gamesystem_name']).classes('text-xl font-bold')
|
||||
# Kopfzeile
|
||||
ui.label(text=sys_name).classes('text-xl font-bold')
|
||||
|
||||
if 'description' in place and place['description']:
|
||||
ui.label(text=place['description']).classes('text-xs text-gray-400')
|
||||
# --- BILD EINFÜGEN ---
|
||||
# prüfen, ob die Spalte 'pictures' existiert und nicht leer ist
|
||||
if 'pictures' in sys and sys['pictures']:
|
||||
ui.image(f"/pictures/{sys['pictures']}").classes('w-24 h-24 object-contain m-2')
|
||||
|
||||
if 'description' in sys and sys['description']:
|
||||
ui.label(text=sys['description']).classes('text-xs text-gray-400 text-center')
|
||||
|
||||
ui.space()
|
||||
|
||||
if place['mmr'] is None:
|
||||
# Prüfen: Ist diese sys_id in den Stats des Spielers? UND hat er ein MMR?
|
||||
if sys_id not in my_stats or my_stats[sys_id]['mmr'] is None:
|
||||
ui.label(text="Du bist noch nicht in dieser Liga.").classes("text-red-500 font-bold")
|
||||
|
||||
# 1. Wir legen die Reihen an und speichern sie in lokalen Variablen
|
||||
join_row = ui.row().classes('items-center gap-2')
|
||||
confirm_row = ui.row().classes('items-center gap-2')
|
||||
confirm_row.visible = False # Standardmäßig unsichtbar
|
||||
confirm_row.visible = False
|
||||
|
||||
# 2. Erste Reihe (Der "Beitreten" Button)
|
||||
with join_row:
|
||||
ui.button("Beitreten", on_click=lambda e, r1=join_row, r2=confirm_row: toggle_visibility(r1, r2))
|
||||
|
||||
# 3. Zweite Reihe (Die Bestätigungs-Buttons)
|
||||
with confirm_row:
|
||||
ui.button("Liga Beitreten", color="green", on_click=lambda e, p=player_id, s=place['gamesystem_id']: click_join_league(p, s))
|
||||
# Der Abbrechen Button kriegt den gleichen Toggle-Befehl wie oben:
|
||||
ui.button("Liga Beitreten", color="green", on_click=lambda e, p=player_id, s=sys_id: click_join_league(p, s))
|
||||
ui.button(icon='cancel', color='red', on_click=lambda e, r1=join_row, r2=confirm_row: toggle_visibility(r1, r2)).props('round dense')
|
||||
else:
|
||||
# Spieler IST in der Liga!
|
||||
sys_card.classes("cursor-pointer hover:bg-zinc-800")
|
||||
# Auch hier machen wir es mit dem 'e' absolut sicher:
|
||||
sys_card.on('click', lambda e, name=place['gamesystem_name']: ui.navigate.to(f'/statistic/{name}'))
|
||||
stat = my_stats[sys_id] # Wir holen uns seine Stats aus dem Wörterbuch
|
||||
|
||||
sys_card.classes("cursor-pointer hover:bg-zinc-800")
|
||||
sys_card.on('click', lambda e, name=sys_name: ui.navigate.to(f'/statistic/{name}'))
|
||||
|
||||
# Wir zeigen die Stats.
|
||||
with ui.row().classes('items-center gap-4'):
|
||||
ui.label(text=f"MMR: {place['mmr']}")
|
||||
ui.label(text=f"Spiele: {place['games_in_system']}")
|
||||
ui.label(text=f"MMR: {stat['mmr']}").classes("text-lg font-bold text-blue-400")
|
||||
ui.label(text=f"Spiele: {stat['games_in_system']}").classes("text-lg")
|
||||
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# Match Historie
|
||||
|
|
|
|||
95
gui/match_history_gui.py
Normal file
95
gui/match_history_gui.py
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
from nicegui import ui, app
|
||||
from data import data_api
|
||||
# Falls du eine gui_style.py für Farben/Header hast, hier importieren!
|
||||
# from gui import gui_style
|
||||
|
||||
def setup_routes():
|
||||
@ui.page('/matchhistory', dark=True)
|
||||
def match_history_page():
|
||||
|
||||
gui_style.apply_design()
|
||||
|
||||
# Sicherheits-Check: Ist der Nutzer eingeloggt?
|
||||
if not app.storage.user.get('authenticated', False):
|
||||
ui.label('Bitte logge dich ein.').classes('text-red-500 text-2xl m-4')
|
||||
return
|
||||
|
||||
player_id = app.storage.user.get('db_id')
|
||||
|
||||
# Das Haupt-Layout der Seite
|
||||
with ui.column().classes('w-full max-w-5xl mx-auto p-4'):
|
||||
|
||||
# Kopfbereich mit Zurück-Button
|
||||
with ui.row().classes('w-full items-center justify-between mb-6'):
|
||||
ui.label("Komplette Match Historie").classes("text-3xl font-bold text-white")
|
||||
ui.button("Zurück", icon="arrow_back", on_click=lambda: ui.navigate.to('/')).classes('bg-zinc-700 text-white')
|
||||
|
||||
raw_matches = data_api.get_match_history_log(player_id)
|
||||
table_rows = []
|
||||
|
||||
# Daten für die Tabelle aufbereiten
|
||||
for i, match in enumerate(raw_matches):
|
||||
# Bin ich P1 oder P2?
|
||||
if match['player1_id'] == player_id:
|
||||
opponent = f"{match['p2_display']} aka {match['p2_discord']}"
|
||||
my_score = match['score_player1']
|
||||
opp_score = match['score_player2']
|
||||
my_mmr_change = match['player1_mmr_change']
|
||||
else:
|
||||
opponent = f"{match['p1_display']} aka {match['p1_discord']}"
|
||||
my_score = match['score_player2']
|
||||
opp_score = match['score_player1']
|
||||
my_mmr_change = match['player2_mmr_change']
|
||||
|
||||
# Ergebnis Text
|
||||
if my_score > opp_score:
|
||||
result = "Gewonnen"
|
||||
elif my_score < opp_score:
|
||||
result = "Verloren"
|
||||
else:
|
||||
result = "Unentschieden"
|
||||
|
||||
# MMR Text schön formatieren
|
||||
if match['match_is_counted'] == 0:
|
||||
mmr_text = "Ausstehend"
|
||||
elif my_mmr_change is None:
|
||||
mmr_text = "0"
|
||||
elif my_mmr_change > 0:
|
||||
mmr_text = f"+{my_mmr_change}"
|
||||
else:
|
||||
mmr_text = str(my_mmr_change)
|
||||
|
||||
table_rows.append({
|
||||
'id': i,
|
||||
'date': str(match['played_at'])[:10],
|
||||
'system': match['gamesystem_name'],
|
||||
'opponent': opponent,
|
||||
'score': f"{my_score} : {opp_score}",
|
||||
'result': result,
|
||||
'mmr': mmr_text
|
||||
})
|
||||
|
||||
# Spalten definieren
|
||||
columns = [
|
||||
{'name': 'date', 'label': 'Datum', 'field': 'date', 'align': 'left'},
|
||||
{'name': 'system', 'label': 'System', 'field': 'system', 'align': 'left'},
|
||||
{'name': 'opponent', 'label': 'Gegner', 'field': 'opponent', 'align': 'left'},
|
||||
{'name': 'score', 'label': 'Punkte', 'field': 'score', 'align': 'center'},
|
||||
{'name': 'result', 'label': 'Ergebnis', 'field': 'result', 'align': 'left'},
|
||||
{'name': 'mmr', 'label': 'MMR Änderung', 'field': 'mmr', 'align': 'right'}
|
||||
]
|
||||
|
||||
# Tabelle zeichnen
|
||||
if len(table_rows) > 0:
|
||||
history_table = ui.table(columns=columns, rows=table_rows, row_key='id').classes('w-full bg-zinc-900 text-white')
|
||||
|
||||
# KLEINER TRICK: Wir färben die MMR-Spalte grün oder rot, je nachdem ob da ein "+" oder "-" steht!
|
||||
history_table.add_slot('body-cell-mmr', '''
|
||||
<q-td :props="props">
|
||||
<span :class="{'text-green-500 font-bold': props.row.mmr.startsWith('+'), 'text-red-500 font-bold': props.row.mmr.startsWith('-'), 'text-gray-400 italic': props.row.mmr === 'Ausstehend'}">
|
||||
{{ props.row.mmr }}
|
||||
</span>
|
||||
</q-td>
|
||||
''')
|
||||
else:
|
||||
ui.label("Keine Spiele gefunden.").classes("text-gray-400 italic")
|
||||
4
main.py
4
main.py
|
|
@ -8,6 +8,8 @@ from data import database
|
|||
|
||||
# 1. Lade die geheimen Variablen aus der .env Datei in den Speicher
|
||||
load_dotenv()
|
||||
# Festschreiben des Bilder Ordnerns.
|
||||
app.add_static_files('/pictures', 'gui/pictures')
|
||||
|
||||
# 2. Variablen abrufen
|
||||
client_id = os.getenv("DISCORD_CLIENT_ID")
|
||||
|
|
@ -23,6 +25,8 @@ discord_login.setup_login_routes()
|
|||
league_statistic.setup_routes()
|
||||
match_gui.setup_routes()
|
||||
admin_gui.setup_routes()
|
||||
match_history_gui.setup_routes()
|
||||
|
||||
|
||||
# 4. Wir starten die NiceGUI App
|
||||
ui.run(title="Westside Diceghost Liga", port=9000, storage_secret="ein_sehr_geheimes_passwort_fuer_die_cookies", favicon="gui/pictures/wsdg.png")
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user