diff --git a/.nicegui/storage-user-83ffc178-0f94-4ada-8ca6-1c51b99b4b9c.json b/.nicegui/storage-user-83ffc178-0f94-4ada-8ca6-1c51b99b4b9c.json index 567972a..9963eea 100644 --- a/.nicegui/storage-user-83ffc178-0f94-4ada-8ca6-1c51b99b4b9c.json +++ b/.nicegui/storage-user-83ffc178-0f94-4ada-8ca6-1c51b99b4b9c.json @@ -1 +1 @@ -{"authenticated":true,"discord_id":"277898241750859776","discord_name":"mrteels","db_id":2,"display_name":"Zornir Grot","discord_avatar_url":"https://cdn.discordapp.com/avatars/277898241750859776/7c3446bb51fafd72b1b4c21124b4994f.png"} \ No newline at end of file +{"authenticated":true,"discord_id":"277898241750859776","discord_name":"mrteels","db_id":2,"display_name":"Daniel Nagel","discord_avatar_url":"https://cdn.discordapp.com/avatars/277898241750859776/7c3446bb51fafd72b1b4c21124b4994f.png"} \ No newline at end of file diff --git a/data/data_api.py b/data/data_api.py index 9388986..95423e6 100644 --- a/data/data_api.py +++ b/data/data_api.py @@ -12,6 +12,8 @@ def change_display_name(player_id, new_name): connection.close() + + def get_or_create_player(discord_id, discord_name, avatar_url): connection = sqlite3.connect(DB_PATH) cursor = connection.cursor() @@ -48,6 +50,8 @@ def get_or_create_player(discord_id, discord_name, avatar_url): return player + + def get_all_players(): connection = sqlite3.connect(DB_PATH) cursor = connection.cursor() @@ -64,6 +68,8 @@ def get_all_players(): return result + + def get_all_players_from_system(system_name): connection = sqlite3.connect(DB_PATH) connection.row_factory = sqlite3.Row @@ -97,6 +103,8 @@ def get_all_players_from_system(system_name): import sqlite3 from data.setup_database import DB_PATH + + def get_gamesystem_data(): connection = sqlite3.connect(DB_PATH) @@ -114,6 +122,8 @@ def get_gamesystem_data(): return result + + def get_player_statistics(player_id): connection = sqlite3.connect(DB_PATH) connection.row_factory = sqlite3.Row @@ -143,6 +153,8 @@ def get_player_statistics(player_id): return result + + def join_league(player_id, gamesystem_id): connection = sqlite3.connect(DB_PATH) cursor = connection.cursor() @@ -159,6 +171,8 @@ def join_league(player_id, gamesystem_id): connection.close() + + def get_recent_matches_for_player(player_id): connection = sqlite3.connect(DB_PATH) connection.row_factory = sqlite3.Row @@ -197,6 +211,8 @@ def get_recent_matches_for_player(player_id): return result + + def add_new_match(system_name, player1_id, player2_id, score_p1, score_p2): connection = sqlite3.connect(DB_PATH) cursor = connection.cursor() @@ -224,3 +240,69 @@ def add_new_match(system_name, player1_id, player2_id, score_p1, score_p2): connection.close() return True + + + +def get_player_system_stats(player_id, system_name): + """Holt die gespeicherten Statistiken eines Spielers für ein spezielles System direkt aus der Tabelle.""" + connection = sqlite3.connect(DB_PATH) + connection.row_factory = sqlite3.Row + cursor = connection.cursor() + + # stat.* holt einfach ALLE Spalten, die in der Tabelle player_game_statistic stehen + query = """ + SELECT stat.* + FROM player_game_statistic stat + JOIN gamesystems sys ON stat.gamesystem_id = sys.id + WHERE stat.player_id = ? AND sys.name = ? + """ + + cursor.execute(query, (player_id, system_name)) + row = cursor.fetchone() + connection.close() + + # Wenn wir was finden, machen wir ein Dictionary draus, ansonsten geben wir None zurück + if row: + return dict(row) + return None + + +def get_last_20_match_scores(player_id, system_name): + """Holt die erspielten Punkte und das Datum der letzten 20 Matches.""" + connection = sqlite3.connect(DB_PATH) + connection.row_factory = sqlite3.Row + cursor = connection.cursor() + + # NEU: Wir haben 'm.played_at' im SELECT hinzugefügt! + query = """ + SELECT m.player1_id, m.score_player1, m.score_player2, m.played_at + FROM matches m + JOIN gamesystems sys ON m.gamesystem_id = sys.id + WHERE sys.name = ? AND (m.player1_id = ? OR m.player2_id = ?) + ORDER BY m.played_at DESC + LIMIT 20 + """ + + cursor.execute(query, (system_name, player_id, player_id)) + rows = cursor.fetchall() + connection.close() + + # Wieder umdrehen für den zeitlichen Verlauf (links alt, rechts neu) + rows.reverse() + + points_list = [] + labels_list = [] + + for row in rows: + if row['player1_id'] == player_id: + points_list.append(row['score_player1']) + else: + points_list.append(row['score_player2']) + + # NEU: Wir schneiden das Datum ab (z.B. 2024-03-04) und nutzen es als Label! + date_clean = str(row['played_at'])[:10] + labels_list.append(date_clean) + + return {"points": points_list, "labels": labels_list} + + \ No newline at end of file diff --git a/gui/league_statistic.py b/gui/league_statistic.py index ee04f36..eda8beb 100644 --- a/gui/league_statistic.py +++ b/gui/league_statistic.py @@ -1,11 +1,11 @@ from nicegui import ui, app from gui import gui_style +from data import data_api def setup_routes(): # 1. Die {}-Klammern definieren eine dynamische Variable in der URL @ui.page('/statistic/{systemname}') def gamesystem_statistic_page(systemname: str): # <--- WICHTIG: Hier fangen wir das Wort aus der URL auf! - sys_name = systemname # Sicherheitscheck: Ist der User eingeloggt? if not app.storage.user.get('authenticated', False): @@ -13,19 +13,119 @@ def setup_routes(): return gui_style.apply_design() - with ui.header().classes('items-center justify-between bg-zinc-900 p-4 shadow-lg'): - ui.button('Zurück zur Übersicht', on_click=lambda: ui.navigate.to('/')).classes('mt-8 bg-gray-600 text-white') - ui.button("Spiel eintragen", on_click=lambda: ui.navigate.to(f'/add-match/{sys_name}')) + + player_id = app.storage.user.get('db_id') + stats = data_api.get_player_system_stats(player_id, systemname) + + if stats: + with ui.header().classes('items-center justify-between bg-zinc-900 p-4 shadow-lg'): + ui.button('Zurück zur Übersicht', on_click=lambda: ui.navigate.to('/')) + ui.button("Spiel eintragen", on_click=lambda: ui.navigate.to(f'/add-match/{systemname}')) + + with ui.column().classes('w-full items-center mt-10'): + ui.label(f'Deine Statistik in {systemname}').classes('text-5xl font-bold text-blue-400') + + + # --- BLOCK 1 (2 Karten) --- + with ui.card().classes("w-full"): + with ui.element('div').classes("w-full grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-4"): + + with ui.card().classes("items-center justify-center text-center"): + ui.label("MMR Punkte: ").classes('text-2xl font-bold') + ui.label(str(stats["mmr"])).classes('text-4xl font-bold text-blue-100') + + with ui.card().classes("items-center justify-center text-center"): + ui.label("Rang: ").classes('text-2xl font-bold') + ui.label("-").classes('text-4xl font-bold text-blue-100') - with ui.column().classes('w-full items-center mt-10'): - # Hier nutzen wir die Variable 'systemname', um den Titel anzupassen - ui.label(f'Deine Statistik in {sys_name}').classes('text-3xl font-bold text-blue-400') + # --- BLOCK 2 (5 Karten) --- + with ui.card().classes("w-full"): + with ui.element('div').classes("w-full grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"): + + with ui.card().classes("items-center justify-center text-center"): + ui.label("Spiele: ").classes('text-2xl font-bold') + ui.label(str(stats["games_in_system"])).classes('text-4xl font-bold text-blue-100') + + with ui.card().classes("items-center justify-center text-center"): + ui.label("Ø Punkte pro Spiel: ").classes('text-2xl font-bold') + ui.label(str(stats["avv_points"])).classes('text-4xl font-bold text-blue-100') + + with ui.card().classes("items-center justify-center text-center"): + ui.label("Win-Rate: ").classes('text-2xl font-bold') + ui.label("-").classes('text-4xl font-bold text-blue-100') + + with ui.card().classes("items-center justify-center text-center"): + ui.label("Letztes Spiel am: ").classes('text-2xl font-bold') + # Schneidet die Zeit vom Datum ab, falls eins existiert + last_played = str(stats["last_played"])[:10] if stats["last_played"] else "-" + ui.label(last_played).classes('text-4xl font-bold text-blue-100') + + with ui.card().classes("items-center justify-center text-center"): + ui.label("Win-Streak: ").classes('text-2xl font-bold') + ui.label("-").classes('text-4xl font-bold text-blue-100') + + # --- BLOCK 3 (3 Karten) --- + with ui.card().classes("w-full"): + with ui.element('div').classes("w-full grid grid-cols-1 md:grid-cols-3 lg:grid-cols-3 gap-4"): + + with ui.card().classes("items-center justify-center text-center"): + ui.label("Dein Nemesis: ").classes('text-2xl font-bold') + ui.label("-").classes('text-4xl font-bold text-blue-100') + + with ui.card().classes("items-center justify-center text-center"): + ui.label("Meisten Spiele mit: ").classes('text-2xl font-bold') + ui.label("-").classes('text-4xl font-bold text-blue-100') + + with ui.card().classes("items-center justify-center text-center"): + ui.label("Dein 'Prügelknabe': ").classes('text-2xl font-bold') + ui.label("-").classes('text-4xl font-bold text-blue-100') + + + # --- BLOCK 4 (Diagramm der letzten 20 Spiele) --- + with ui.card().classes("w-full items-center"): + ui.label("Punkte in den letzten 20 Spielen").classes('text-2xl font-bold mb-4') + + # 1. Daten aus der API holen + chart_data = data_api.get_last_20_match_scores(player_id, systemname) + + # 2. Nur zeichnen, wenn es überhaupt schon Spiele gibt! + if len(chart_data['points']) > 0: + ui.echart({ + 'xAxis': { + 'type': 'category', + 'data': chart_data['labels'], + 'axisLabel': {'color': '#e5e7eb'} + }, + 'yAxis': { + 'type': 'value', + 'axisLabel': {'color': '#e5e7eb'}, + 'splitLine': {'lineStyle': {'color': '#3f3f46'}} + }, + 'series': [ + { + 'name': 'Punkte', # Zeigt im Tooltip "Punkte: 76" statt einer unbenannten Zahl + 'type': 'line', + 'data': chart_data['points'], + 'smooth': True, + 'color': '#60a5fa' + } + ], + 'tooltip': { + 'trigger': 'axis', + 'backgroundColor': '#27272a', + 'textStyle': {'color': '#ffffff'}, + 'borderColor': '#3f3f46' # Ein feiner Rand für den Kasten + } + }).classes('w-full h-64') + + + else: + ui.label("Keine Statistiken für dieses System gefunden.").classes('text-red-500 mt-4') + + + + - with ui.card().classes('w-full max-w-2xl mt-6 p-6 items-center'): - ui.label('Hier laden wir im nächsten Schritt die Daten aus der player_game_statistic Tabelle!').classes('text-gray-400') - # Platzhalter für später: - ui.label('Winrate: -- %').classes('text-xl mt-4') - ui.label('Durchschnittspunkte: --').classes('text-xl') diff --git a/gui/main_gui.py b/gui/main_gui.py index 0617e93..dc827be 100644 --- a/gui/main_gui.py +++ b/gui/main_gui.py @@ -97,7 +97,7 @@ def setup_routes(): row_a.visible = not row_a.visible row_b.visible = not row_b.visible - with ui.grid(columns=3).classes("w-full gap-4 items-center"): + 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: sys_card = ui.card().classes("h-60 w-full items-center justify-center transition-colors") @@ -137,7 +137,6 @@ def setup_routes(): 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"Punkte: {place['points']}") # --------------------------- # Match Historie