From 3823dac7e3e31c6cce6aa21f0ff409ee974a7bcd Mon Sep 17 00:00:00 2001 From: Daniel Nagel Date: Mon, 2 Mar 2026 15:17:50 +0000 Subject: [PATCH] Liga Buttons. Anmeldung, ...... --- ...-83ffc178-0f94-4ada-8ca6-1c51b99b4b9c.json | 2 +- data/data_api.py | 68 ++++++++++++++++++- data/setup_database.py | 10 +-- gui/league_statistic.py | 12 ++-- gui/main_gui.py | 57 ++++++++++++---- gui/match_gui.py | 16 ++--- 6 files changed, 135 insertions(+), 30 deletions(-) diff --git a/.nicegui/storage-user-83ffc178-0f94-4ada-8ca6-1c51b99b4b9c.json b/.nicegui/storage-user-83ffc178-0f94-4ada-8ca6-1c51b99b4b9c.json index b3fb00d..fc64305 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":1,"display_name":"Daniel Nagel","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":1,"display_name":"Chaos Regelvergesser","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 901752b..983773b 100644 --- a/data/data_api.py +++ b/data/data_api.py @@ -61,4 +61,70 @@ def get_all_players(): # Die Daten für das Web-GUI in eine lesbare Form umwandeln (Liste von Dictionaries) result = [] - return result \ No newline at end of file + return result + + +import sqlite3 +from data.setup_database import DB_PATH + +def get_gamesystem_data(): + connection = sqlite3.connect(DB_PATH) + + connection.row_factory = sqlite3.Row + cursor = connection.cursor() + + cursor.execute("SELECT * FROM gamesystems") + rows = cursor.fetchall() + connection.close() + + # SQLite-Rows in normale Python-Dictionaries um + result = [] + for row in rows: + result.append(dict(row)) + + return result + +def get_player_statistics(player_id): + connection = sqlite3.connect(DB_PATH) + connection.row_factory = sqlite3.Row + cursor = connection.cursor() + + query = """ + SELECT + sys.id AS gamesystem_id, + sys.name AS gamesystem_name, + sys.*, + stat.mmr, + stat.games_in_system, + stat.points + FROM gamesystems sys + LEFT JOIN player_game_statistic stat + ON sys.id = stat.gamesystem_id AND stat.player_id = ? + """ + + cursor.execute(query, (player_id,)) + rows = cursor.fetchall() + connection.close() + + result = [] + for row in rows: + result.append(dict(row)) + + return result + + +def join_league(player_id, gamesystem_id): + connection = sqlite3.connect(DB_PATH) + cursor = connection.cursor() + + # Wir fügen nur die beiden IDs ein, der Rest wird von den DEFAULT-Werten der DB erledigt + query = """ + INSERT INTO player_game_statistic (player_id, gamesystem_id) + VALUES (?, ?) + """ + + cursor.execute(query, (player_id, gamesystem_id)) + connection.commit() + + connection.close() + diff --git a/data/setup_database.py b/data/setup_database.py index e0834e4..11bb455 100644 --- a/data/setup_database.py +++ b/data/setup_database.py @@ -26,6 +26,8 @@ def init_db(): CREATE TABLE IF NOT EXISTS gamesystems ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, + picture TEXT, + description TEXT, min_score INTEGER DEFAULT 0, max_score INTEGER DEFAULT 100 ) @@ -102,13 +104,13 @@ def seed_gamesystems(): connection = sqlite3.connect(DB_PATH) cursor = connection.cursor() systems = [ - ("Warhammer 40k", 0, 100), - ("Age of Sigmar", 0, 50), - ("Spearhead", 0, 50) + ("Warhammer 40k","/gui/pictures/wsdg.png","Die Schlacht um die Galaxie in einer entfernten Zukunft." , 0, 100), + ("Age of Sigmar","/gui/pictures/wsdg.png","" , 0, 50), + ("Spearhead","/gui/pictures/wsdg.png","" , 0, 50) ] # executemany ist eine extrem schnelle SQL For-Schleife für Inserts - cursor.executemany("INSERT INTO gamesystems (name, min_score, max_score) VALUES (?, ?, ?)", systems) + cursor.executemany("INSERT INTO gamesystems (name, picture, description, min_score, max_score) VALUES (?, ?, ?, ?, ?)", systems) connection.commit() connection.close() diff --git a/gui/league_statistic.py b/gui/league_statistic.py index 6cbed41..f58496b 100644 --- a/gui/league_statistic.py +++ b/gui/league_statistic.py @@ -1,4 +1,5 @@ from nicegui import ui, app +from gui import gui_style def setup_statistic_routes(): @@ -11,9 +12,12 @@ def setup_statistic_routes(): ui.navigate.to('/') return - ui.dark_mode(True) - - # 2. Seite aufbauen + gui_style.apply_design() + print(systemname) + 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('/add-match/{systemname}')) + 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 {systemname}').classes('text-3xl font-bold text-blue-400') @@ -25,4 +29,4 @@ def setup_statistic_routes(): ui.label('Winrate: -- %').classes('text-xl mt-4') ui.label('Durchschnittspunkte: --').classes('text-xl') - ui.button('Zurück zur Übersicht', on_click=lambda: ui.navigate.to('/')).classes('mt-8 bg-gray-600 text-white') + diff --git a/gui/main_gui.py b/gui/main_gui.py index 5c27bf7..a86554e 100644 --- a/gui/main_gui.py +++ b/gui/main_gui.py @@ -16,12 +16,12 @@ def setup_routes(): ui.image("gui/pictures/wsdg.png").classes('w-20 h-20 rounded-full') ui.label('Diceghost Liga').classes('text-2xl font-bold text-white') - # --- RECHTE SEITE --- + # --- RECHTE SEITE --- if app.storage.user.get('authenticated', False): with ui.row().classes('items-center gap-4'): discord_name = app.storage.user.get('discord_name') display_name = app.storage.user.get('display_name') - db_id = app.storage.user.get('db_id') + player_id = app.storage.user.get('db_id') # 1. Wir definieren eine kleine Funktion, die zwischen Text und Eingabe hin- und herschaltet def toggle_edit_mode(): @@ -73,22 +73,55 @@ def setup_routes(): ui.button('Login with Discord', on_click=lambda: ui.navigate.to(auth_url)) # ---------------------------------- - # --- Spielsysteme --- if app.storage.user.get('authenticated', False): with ui.card().classes("w-full"): - ui.label(text="Meine Ligaplätze").classes("font-bold text-white") - with ui.row().classes("w-full h-30 gap-4 items-center"): - #Einfügen: Eine UI Card PRO Spielsystem des Spielers. - with ui.card().classes("flex-1 h-24 items-center justify-center cursor-pointer hover:bg-zinc-800 transition-colors").on('click', lambda: ui.navigate.to('/statistic/Warhammer 40k')): - ui.label(text="Warhammer 40k").classes('text-xl font-bold') + ui.label(text="Meine Ligaplätze").classes("font-bold text-white text-xl") + + placements = data_api.get_player_statistics(player_id) + systems = data_api.get_gamesystem_data() + + def click_join_league(p_id, sys_id): + data_api.join_league(p_id, sys_id) + ui.navigate.to("/") + + with ui.grid(columns=3).classes("w-full gap-4 items-center"): + for place in placements: + 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') + + if 'description' in place and place['description']: + ui.label(text=place['description']).classes('text-xs text-gray-400') + + ui.space() + + if place['mmr'] is None: + ui.label(text="Du bist noch nicht in dieser Liga.").classes("text-red-500 font-bold") + + # --- HIER IST DER MAGISCHE TRICK --- + # Das 'e' fängt das NiceGUI-Event ab und wirft es weg. + # p=player_id und s=place['gamesystem_id'] frieren die IDs für DIESEN Button bombenfest ein! + ui.button("Liga Beitreten", on_click=lambda e, p=player_id, s=place['gamesystem_id']: click_join_league(p, s)) + + 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}')) + + # 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"Punkte: {place['points']}") + + - with ui.card().classes("flex-1 h-24 items-center justify-center cursor-pointer hover:bg-zinc-800 transition-colors").on('click', lambda: ui.navigate.to('/statistic/Age of Sigmar')): - ui.label(text="Age of Sigmar").classes('text-xl font-bold') - with ui.card().classes("flex-1 h-24 items-center justify-center cursor-pointer hover:bg-zinc-800 transition-colors").on('click', lambda: ui.navigate.to('/statistic/Spearhead')): - ui.label(text="Spearhead").classes('text-xl font-bold') diff --git a/gui/match_gui.py b/gui/match_gui.py index 760c18a..a0bdadc 100644 --- a/gui/match_gui.py +++ b/gui/match_gui.py @@ -7,16 +7,16 @@ def setup_match_routes(): @ui.page('/add-match/{systemname}') def match_form_page(systemname: str): # <--- WICHTIG: Hier fangen wir das Wort aus der URL auf! gui_style.apply_design() + + # --- SICHERHEITS-CHECK --- + # Prüfen, ob der User wirklich eingeloggt ist. + if not app.storage.user.get('authenticated', False): + ui.label('Access Denied. Please log in first.').classes('text-red-500') + ui.button('Back to Home', on_click=lambda: ui.navigate.to('/')) + return with ui.card().classes('w-full items-center mt-10'): - ui.label('Enter New Match').classes('text-2xl font-bold') - - # --- SICHERHEITS-CHECK --- - # Prüfen, ob der User wirklich eingeloggt ist. - if not app.storage.user.get('authenticated', False): - ui.label('Access Denied. Please log in first.').classes('text-red-500') - ui.button('Back to Home', on_click=lambda: ui.navigate.to('/')) - return + ui.label('Neues Spiel für '+ systemname + " eintragen").classes('text-2xl font-bold') # --- PLATZHALTER --- ui.label('Hier kommt im nächsten Schritt das Eingabe-Formular hin!').classes('text-gray-500 my-4')