From b861ebf79d93bd7528bb907d7a432e714f87eae1 Mon Sep 17 00:00:00 2001 From: Daniel Nagel Date: Thu, 26 Mar 2026 08:24:33 +0000 Subject: [PATCH 1/4] Info texts. Min Max Punkte werden jetzt richitg angewandt im Match-Formular. --- ...-21d6fdd4-43d2-4625-8dc2-9282b6aa433f.json | 2 +- ...-253576f7-9827-4994-a103-e67a11c8053c.json | 1 + ...-62e9c394-87c8-4ee0-a57f-d66ec22cd26a.json | 2 +- gui/info_text/info_system.py | 2 +- gui/match_gui.py | 26 +++++++++++-------- match_calculations/calc_match.py | 2 +- 6 files changed, 20 insertions(+), 15 deletions(-) create mode 100644 .nicegui/storage-user-253576f7-9827-4994-a103-e67a11c8053c.json diff --git a/.nicegui/storage-user-21d6fdd4-43d2-4625-8dc2-9282b6aa433f.json b/.nicegui/storage-user-21d6fdd4-43d2-4625-8dc2-9282b6aa433f.json index 768c8da..6c3d053 100644 --- a/.nicegui/storage-user-21d6fdd4-43d2-4625-8dc2-9282b6aa433f.json +++ b/.nicegui/storage-user-21d6fdd4-43d2-4625-8dc2-9282b6aa433f.json @@ -1 +1 @@ -{"authenticated":true,"discord_id":"277898241750859776","discord_name":"mrteels","db_id":2,"display_name":"Verwirrter Klebschnüffler","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":"Verzweifelter Kultist","discord_avatar_url":"https://cdn.discordapp.com/avatars/277898241750859776/7c3446bb51fafd72b1b4c21124b4994f.png"} \ No newline at end of file diff --git a/.nicegui/storage-user-253576f7-9827-4994-a103-e67a11c8053c.json b/.nicegui/storage-user-253576f7-9827-4994-a103-e67a11c8053c.json new file mode 100644 index 0000000..6c3d053 --- /dev/null +++ b/.nicegui/storage-user-253576f7-9827-4994-a103-e67a11c8053c.json @@ -0,0 +1 @@ +{"authenticated":true,"discord_id":"277898241750859776","discord_name":"mrteels","db_id":2,"display_name":"Verzweifelter Kultist","discord_avatar_url":"https://cdn.discordapp.com/avatars/277898241750859776/7c3446bb51fafd72b1b4c21124b4994f.png"} \ No newline at end of file diff --git a/.nicegui/storage-user-62e9c394-87c8-4ee0-a57f-d66ec22cd26a.json b/.nicegui/storage-user-62e9c394-87c8-4ee0-a57f-d66ec22cd26a.json index 8a8a4ed..9e26dfe 100644 --- a/.nicegui/storage-user-62e9c394-87c8-4ee0-a57f-d66ec22cd26a.json +++ b/.nicegui/storage-user-62e9c394-87c8-4ee0-a57f-d66ec22cd26a.json @@ -1 +1 @@ -{"authenticated":true,"discord_id":"159672532159561728","discord_name":"buddyl33t","db_id":3,"display_name":"Marian","discord_avatar_url":"https://cdn.discordapp.com/avatars/159672532159561728/866d0dddade9d551f3c5025bb6467da0.png"} \ No newline at end of file +{} \ No newline at end of file diff --git a/gui/info_text/info_system.py b/gui/info_text/info_system.py index df52357..b584714 100644 --- a/gui/info_text/info_system.py +++ b/gui/info_text/info_system.py @@ -28,7 +28,7 @@ def create_info_button(topic_key): with ui.dialog().classes("w-full items-center") as info_dialog, ui.card().classes("items-center text-textnormal"): # Loopen durch die Sätze in der JSON for sentence in texts: - ui.markdown(sentence).classes("font-bold text-white text-lg text-center") + ui.markdown(sentence).classes("text-normaltext text-lg text-center") # --- DER BUTTON (Sichtbar auf der Seite) --- ui.button(icon="info_outline", color= "", on_click=info_dialog.open).props('round dense') diff --git a/gui/match_gui.py b/gui/match_gui.py index d4a91eb..552c84a 100644 --- a/gui/match_gui.py +++ b/gui/match_gui.py @@ -7,8 +7,8 @@ from gui.info_text import info_system def setup_routes(): # 1. Die {}-Klammern definieren eine dynamische Variable in der URL - @ui.page('/add-match/{systemname}', dark=True) - def match_form_page(systemname: str): # <--- WICHTIG: Hier fangen wir das Wort aus der URL auf! + @ui.page('/add-match/{system_name}', dark=True) + def match_form_page(system_name: str): # <--- WICHTIG: Hier fangen wir das Wort aus der URL auf! gui_style.apply_design() # --- SICHERHEITS-CHECK --- @@ -17,16 +17,21 @@ def setup_routes(): ui.button('Back to Home', on_click=lambda: ui.navigate.to('/')) return - # ÄNDERUNG: w-full (für Handy) + max-w-md (für PC) + mx-auto (Zentrieren) + p-6 (Innenabstand) + # --- Eingabeformular --- with ui.card().classes('w-full max-w-md mx-auto items-center mt-10 p-6 shadow-xl'): + system_id = data_api.get_gamesystem_id_by_name(system_name) + system_data = data_api.get_gamesystem_data(system_id) + min_score = system_data["min_score"] + max_score = system_data["max_score"] + # Text-Center hinzugefügt, falls der Systemname sehr lang ist und auf dem Handy umbricht - ui.label(f'Neues Spiel für {systemname} eintragen').classes('text-2xl font-bold text-center mb-6') + ui.label(f'Neues Spiel für {system_name} eintragen').classes('text-2xl font-bold text-center mb-6') ui.label("Meine Punkte:").classes('text-xl font-bold w-full text-left') with ui.column().classes("w-full items-center gap-6"): # 1. Daten aus der DB holen - raw_players = data_api.get_all_players_from_system(systemname) + raw_players = data_api.get_all_players_from_system(system_name) my_id = app.storage.user.get('db_id') dropdown_options = {} @@ -36,16 +41,15 @@ def setup_routes(): dropdown_options[p['player_id']] = f"{p['display_name']} 'aka' {p['discord_name']}" with ui.row().classes("w-full items-center justify-between"): - p1_points = ui.slider(min=0, max=100, value=10).classes("w-70") + p1_points = ui.slider(min=min_score, max=max_score, value=10).classes("w-70") ui.label().bind_text_from(p1_points, 'value').classes("text-lg text-normaltext") ui.separator().classes('w-full mt-4') # Ein schöner Trennstrich für die Optik - ui.label("Gegner:").classes('text-xl font-bold w-full text-left') opponent_select = ui.select(options=dropdown_options, label='Gegner auswählen').classes('w-full') with ui.row().classes("w-full items-center justify-between"): - p2_points = ui.slider(min=0, max=100, value=10).classes("w-70") + p2_points = ui.slider(min=min_score, max=max_score, value=10).classes("w-70") ui.label().bind_text_from(p2_points, 'value').classes("text-lg text-normaltext") ui.space() @@ -60,14 +64,14 @@ def setup_routes(): score_p1 = p1_points.value score_p2 = p2_points.value - match_id = data_api.add_new_match(systemname, my_id, p2_id, score_p1, score_p2) + match_id = data_api.add_new_match(system_name, my_id, p2_id, score_p1, score_p2) # 4. Erfolgsmeldung und Berechnung ui.notify("Match erfolgreich eingetragen!", color="green") - ui.navigate.to(f'/statistic/{systemname}') + ui.navigate.to(f'/statistic/{system_name}') # Buttons ganz unten in einer Reihe with ui.row().classes("w-full items-center justify-between mt-8"): - ui.button(icon="close", on_click=lambda: ui.navigate.to(f'/statistic/{systemname}')).classes("w-10 h-8 rounded-full") + ui.button(icon="close", on_click=lambda: ui.navigate.to(f'/statistic/{system_name}')).classes("w-10 h-8 rounded-full") info_system.create_info_button("match_form_info") ui.button(text="Absenden", color="positive", on_click=lambda: input_match_to_database()) diff --git a/match_calculations/calc_match.py b/match_calculations/calc_match.py index 1c92452..1a42ab4 100644 --- a/match_calculations/calc_match.py +++ b/match_calculations/calc_match.py @@ -5,7 +5,7 @@ import os from wood import logger -point_inflation = 0.8 # => entspricht % +point_inflation = 1 # => entspricht % K_FACTOR = 40 # Die "Border" (Maximalpunkte) die ein Sieg gibt. # Mach die DB abfrage für die Relevanten Daten. Von hier aus werden die "Aufgaben" und Daten dann an die kleineren Berechnungs Funktionen verteilt. -- 2.43.0 From 9cf54bd0511bf24061f3f51d3519618ae3cc059b Mon Sep 17 00:00:00 2001 From: Daniel Nagel Date: Thu, 26 Mar 2026 08:55:11 +0000 Subject: [PATCH 2/4] =?UTF-8?q?Match=20formular=20f=C3=BCr=20mobile=20ange?= =?UTF-8?q?passt.=20Punkte=20Eintrag=20hat=20jetzt=20+=20-=20Buttons=20f?= =?UTF-8?q?=C3=BCr=20Pr=C3=A4zise=20Einstellungen.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...-b1350d67-43b3-4813-9c12-9090a9f37b57.json | 1 + gui/match_gui.py | 53 +++++++++++++------ 2 files changed, 37 insertions(+), 17 deletions(-) create mode 100644 .nicegui/storage-user-b1350d67-43b3-4813-9c12-9090a9f37b57.json diff --git a/.nicegui/storage-user-b1350d67-43b3-4813-9c12-9090a9f37b57.json b/.nicegui/storage-user-b1350d67-43b3-4813-9c12-9090a9f37b57.json new file mode 100644 index 0000000..6c3d053 --- /dev/null +++ b/.nicegui/storage-user-b1350d67-43b3-4813-9c12-9090a9f37b57.json @@ -0,0 +1 @@ +{"authenticated":true,"discord_id":"277898241750859776","discord_name":"mrteels","db_id":2,"display_name":"Verzweifelter Kultist","discord_avatar_url":"https://cdn.discordapp.com/avatars/277898241750859776/7c3446bb51fafd72b1b4c21124b4994f.png"} \ No newline at end of file diff --git a/gui/match_gui.py b/gui/match_gui.py index 552c84a..de18d6e 100644 --- a/gui/match_gui.py +++ b/gui/match_gui.py @@ -19,7 +19,6 @@ def setup_routes(): # --- Eingabeformular --- with ui.card().classes('w-full max-w-md mx-auto items-center mt-10 p-6 shadow-xl'): - system_id = data_api.get_gamesystem_id_by_name(system_name) system_data = data_api.get_gamesystem_data(system_id) min_score = system_data["min_score"] @@ -27,32 +26,52 @@ def setup_routes(): # Text-Center hinzugefügt, falls der Systemname sehr lang ist und auf dem Handy umbricht ui.label(f'Neues Spiel für {system_name} eintragen').classes('text-2xl font-bold text-center mb-6') - ui.label("Meine Punkte:").classes('text-xl font-bold w-full text-left') - with ui.column().classes("w-full items-center gap-6"): - # 1. Daten aus der DB holen - raw_players = data_api.get_all_players_from_system(system_name) - my_id = app.storage.user.get('db_id') + with ui.card().classes('w-full max-w-md mx-auto items-center mt-10 p-6'): + ui.label("Meine Punkte:").classes('text-xl font-bold w-full text-left') + with ui.column().classes("w-full items-center gap-6"): + # 1. Daten aus der DB holen + raw_players = data_api.get_all_players_from_system(system_name) + my_id = app.storage.user.get('db_id') + def add_point(): + p1_points.value += 1 + + def sub_point(): + p1_points.value -= 1 + + with ui.row().classes("w-full items-center justify-between"): + p1_points = ui.slider(min=min_score, max=max_score, value=10).classes("w-35") + with ui.column().classes("items-center justify-between"): + # Punkte Up- Down- Buttons. und Textanzeige + ui.button(icon="expand_less", on_click=add_point) + ui.label().bind_text_from(p1_points, 'value').classes("text-lg text-normaltext") + ui.button(icon="expand_more", on_click=sub_point) + + with ui.card().classes('w-full max-w-md mx-auto items-center mt-10 p-6'): dropdown_options = {} for p in raw_players: if p['player_id'] == my_id: continue dropdown_options[p['player_id']] = f"{p['display_name']} 'aka' {p['discord_name']}" - with ui.row().classes("w-full items-center justify-between"): - p1_points = ui.slider(min=min_score, max=max_score, value=10).classes("w-70") - ui.label().bind_text_from(p1_points, 'value').classes("text-lg text-normaltext") - - ui.separator().classes('w-full mt-4') # Ein schöner Trennstrich für die Optik - - ui.label("Gegner:").classes('text-xl font-bold w-full text-left') opponent_select = ui.select(options=dropdown_options, label='Gegner auswählen').classes('w-full') - with ui.row().classes("w-full items-center justify-between"): - p2_points = ui.slider(min=min_score, max=max_score, value=10).classes("w-70") - ui.label().bind_text_from(p2_points, 'value').classes("text-lg text-normaltext") - ui.space() + def add_point(): + p2_points.value += 1 + + def sub_point(): + p2_points.value -= 1 + + with ui.row().classes("w-full items-center justify-between"): + p2_points = ui.slider(min=min_score, max=max_score, value=10).classes("w-35") + with ui.column().classes("items-center justify-between"): + # Punkte Up- Down- Buttons. und Textanzeige + ui.button(icon="expand_less", on_click=add_point) + ui.label().bind_text_from(p2_points, 'value').classes("text-lg text-normaltext") + ui.button(icon="expand_more", on_click=sub_point) + + # Das Match in die Datenbank eintragen lassen und die MMR Berechnung triggern. def input_match_to_database(): -- 2.43.0 From 7021bc8629041ab2427dc036c4cfa50d891c04f8 Mon Sep 17 00:00:00 2001 From: Daniel Nagel Date: Thu, 26 Mar 2026 15:20:46 +0000 Subject: [PATCH 3/4] =?UTF-8?q?Match=20History=20sch=C3=B6ner=20gestaltet.?= =?UTF-8?q?=20Basis=20MMR=20haben=20in=20der=20Historie=20gefehlt.=20Werde?= =?UTF-8?q?n=20jetzt=20abgefragt=20und=20angezeigt.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...-253576f7-9827-4994-a103-e67a11c8053c.json | 2 +- data/data_api.py | 10 ++++- data/setup_database.py | 8 ++++ gui/league_statistic.py | 1 - gui/match_history_gui.py | 43 ++++++++++--------- 5 files changed, 39 insertions(+), 25 deletions(-) diff --git a/.nicegui/storage-user-253576f7-9827-4994-a103-e67a11c8053c.json b/.nicegui/storage-user-253576f7-9827-4994-a103-e67a11c8053c.json index 6c3d053..9963eea 100644 --- a/.nicegui/storage-user-253576f7-9827-4994-a103-e67a11c8053c.json +++ b/.nicegui/storage-user-253576f7-9827-4994-a103-e67a11c8053c.json @@ -1 +1 @@ -{"authenticated":true,"discord_id":"277898241750859776","discord_name":"mrteels","db_id":2,"display_name":"Verzweifelter Kultist","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 ab2a1fc..7f341f7 100644 --- a/data/data_api.py +++ b/data/data_api.py @@ -150,12 +150,18 @@ def get_player_statistics(player_id): sys.*, stat.mmr, stat.games_in_system, - stat.points + stat.points, + stat.avv_points, + stat.last_played, + stat.win_rate, + stat.trend, + stat.tyrann_id, + stat.pushover_id, + stat.nemesis_id 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() diff --git a/data/setup_database.py b/data/setup_database.py index 75b6848..b68b828 100644 --- a/data/setup_database.py +++ b/data/setup_database.py @@ -54,8 +54,16 @@ def init_db(): points INTEGER DEFAULT 0, avv_points INTEGER DEFAULT 0, last_played TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + win_rate REAL DEFAULT 0, + win_streak INTEGER DEFAULT 0, trend INTEGER DEFAULT 0, + tyrann_id INTEGER, + pushover_id INTEGER, + nemesis_id INTEGER, FOREIGN KEY (player_id) REFERENCES players (id), + FOREIGN KEY (tyrann_id) REFERENCES players (id), + FOREIGN KEY (pushover_id) REFERENCES players (id), + FOREIGN KEY (nemesis_id) REFERENCES players (id), FOREIGN KEY (gamesystem_id) REFERENCES gamesystems (id) ) ''') diff --git a/gui/league_statistic.py b/gui/league_statistic.py index a1cddfb..fa679cf 100644 --- a/gui/league_statistic.py +++ b/gui/league_statistic.py @@ -23,7 +23,6 @@ def setup_routes(): ) if system_stat: - # None-Werte absichern mmr = system_stat["mmr"] or 0 games = system_stat["games_in_system"] or 0 points = system_stat["points"] or 0 diff --git a/gui/match_history_gui.py b/gui/match_history_gui.py index 8244ec2..3a55d64 100644 --- a/gui/match_history_gui.py +++ b/gui/match_history_gui.py @@ -15,7 +15,7 @@ def setup_routes(): player_id = app.storage.user.get('db_id') - with ui.column().classes('w-full max-w-7xl mx-auto p-4'): + with ui.column().classes('w-full mx-auto p-4'): with ui.row().classes('w-full items-center justify-between mb-6'): ui.label("Komplette Match Historie").classes("text-3xl font-bold text-white") @@ -42,18 +42,20 @@ def setup_routes(): 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'] - my_khorne = match['player1_khorne'] - my_tzeentch = match['player1_tzeentch'] - my_slaanesh = match['player1_slaanesh'] + mmr_base = match["player1_base_change"] + mmr_change = match['player1_mmr_change'] + khorne = match['player1_khorne'] + tzeentch = match['player1_tzeentch'] + slaanesh = match['player1_slaanesh'] 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'] - my_khorne = match['player2_khorne'] - my_tzeentch = match['player2_tzeentch'] - my_slaanesh = match['player2_slaanesh'] + mmr_base = match["player2_base_change"] + mmr_change = match['player2_mmr_change'] + khorne = match['player2_khorne'] + tzeentch = match['player2_tzeentch'] + slaanesh = match['player2_slaanesh'] if my_score > opp_score: result = "Gewonnen" @@ -70,30 +72,29 @@ def setup_routes(): 'date': str(match['played_at'])[:10], 'system': match['gamesystem_name'], 'score': str(my_score), - 'opponent': opponent, - 'opp_score': str(opp_score), - 'result': result, + 'opponent': (f"{opponent} ({opp_score})"), + "basis" : mmr_base, 'elo': fmt_signed(round(elo_factor, 2) if elo_factor is not None else None, pending), 'rust': fmt_signed(round(rust_factor, 2) if rust_factor is not None else None, pending), - 'khorne': fmt_signed(my_khorne, pending), - 'tzeentch': fmt_signed(my_tzeentch, pending), - 'slaanesh': fmt_signed(my_slaanesh, pending), - 'mmr': fmt_signed(my_mmr_change, pending), + 'khorne': fmt_signed(khorne, pending), + 'tzeentch': fmt_signed(tzeentch, pending), + 'slaanesh': fmt_signed(slaanesh, pending), + 'mmr': fmt_signed(mmr_change, pending), }) columns = [ {'name': 'date', 'label': 'Datum', 'field': 'date', 'align': 'left'}, {'name': 'system', 'label': 'System', 'field': 'system', 'align': 'left'}, - {'name': 'score', 'label': 'Eigene Punkte', 'field': 'score', 'align': 'center'}, - {'name': 'opponent', 'label': 'Gegner', 'field': 'opponent', 'align': 'left'}, - {'name': 'opp_score','label': 'Gegner Punkte', 'field': 'opp_score','align': 'center'}, - {'name': 'result', 'label': 'Ergebnis', 'field': 'result', 'align': 'left'}, + {'name': 'score', 'label': 'Punkte', 'field': 'score', 'align': 'center'}, + {'name': 'opponent', 'label': 'Gegner (Pkt.)', 'field': 'opponent', 'align': 'left'}, {'name': 'elo', 'label': 'Elo Faktor', 'field': 'elo', 'align': 'right'}, + {'name': 'basisMMR', 'label': 'Basis MMR', 'field': 'basis', 'align': 'right'}, {'name': 'rust', 'label': 'Rost Faktor', 'field': 'rust', 'align': 'right'}, {'name': 'khorne', 'label': 'Khorne', 'field': 'khorne', 'align': 'right'}, {'name': 'tzeentch', 'label': 'Tzeentch', 'field': 'tzeentch', 'align': 'right'}, {'name': 'slaanesh', 'label': 'Slaanesh', 'field': 'slaanesh', 'align': 'right'}, - {'name': 'mmr', 'label': 'MMR Änderung', 'field': 'mmr', 'align': 'right'}, + {'name': 'mmr', 'label': 'MMR GESAMT', 'field': 'mmr', 'align': 'right'}, + ] # Shared slot template for colored signed values -- 2.43.0 From 986e3ca4bd6c8d9f38790b173a0b3a2f88e50e02 Mon Sep 17 00:00:00 2001 From: Daniel Nagel Date: Fri, 27 Mar 2026 08:38:29 +0000 Subject: [PATCH 4/4] Logger Funktion Verbessert und Angepasst. --- ...-253576f7-9827-4994-a103-e67a11c8053c.json | 2 +- data/data_api.py | 20 ++++--- gui/admin_gui.py | 19 +++---- gui/info_text/info_system.py | 5 +- gui/info_text/info_texts.json | 4 ++ match_calculations/calc_match.py | 4 +- wood/logger.py | 56 ++++++++++--------- 7 files changed, 57 insertions(+), 53 deletions(-) diff --git a/.nicegui/storage-user-253576f7-9827-4994-a103-e67a11c8053c.json b/.nicegui/storage-user-253576f7-9827-4994-a103-e67a11c8053c.json index 9963eea..cfdc4d2 100644 --- a/.nicegui/storage-user-253576f7-9827-4994-a103-e67a11c8053c.json +++ b/.nicegui/storage-user-253576f7-9827-4994-a103-e67a11c8053c.json @@ -1 +1 @@ -{"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 +{"authenticated":true,"discord_id":"277898241750859776","discord_name":"mrteels","db_id":2,"display_name":"Daniel N.","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 7f341f7..6421c93 100644 --- a/data/data_api.py +++ b/data/data_api.py @@ -15,10 +15,12 @@ def validate_user_session(db_id, discord_id): # 1. Fall: Die ID gibt es gar nicht mehr in der Datenbank if result is None: + logger.log(f"Player not found in database. Discord:{discord_id}") return False # 2. Fall: Die ID gehört jetzt einem anderen Discord-Account (Datenbank wurde resettet) if str(result[0]) != str(discord_id): + logger.log(f"Player with false coockies logged in! {discord_id} doesnt belong to {db_id}") return False # 3. Fall: Alles ist korrekt! @@ -54,7 +56,7 @@ def get_or_create_player(discord_id, discord_name, avatar_url): # Random Silly Name Generator für neue Spieler. Damit sie angeregt werden ihren richtigen Namen einzutragen. silly_name = generate_silly_name() cursor.execute("INSERT INTO players (discord_id, discord_name, display_name, discord_avatar_url) VALUES (?, ?, ?, ?)", (discord_id, discord_name, silly_name, avatar_url)) - logger.log("data_api.get_or_create_player", str("Ein neuer Spieler wurde angelegt - " + discord_name)) + logger.log(f"new player added. Discord:{discord_name}") connection.commit() cursor.execute("SELECT id, discord_name, display_name, discord_avatar_url FROM players WHERE discord_id = ?", (discord_id,)) player = cursor.fetchone() @@ -184,7 +186,7 @@ def join_league(player_id, gamesystem_id): INSERT INTO player_game_statistic (player_id, gamesystem_id) VALUES (?, ?) """ - logger.log("data_api.join_league", f"{get_player_name(player_id)} joined {gamesystem_id}", player_id) + logger.log(f"{get_player_name(player_id)} joined {gamesystem_id}") cursor.execute(query, (player_id, gamesystem_id)) connection.commit() @@ -256,7 +258,7 @@ def add_new_match(system_name, player1_id, player2_id, score_p1, score_p2): cursor.execute(query, (sys_id, player1_id, player2_id, score_p1, score_p2)) new_match_id = cursor.lastrowid - logger.log("data_api.add_new_match", f"{get_player_name(player1_id)}:({score_p1}) posted Match. System: {system_name}, {get_player_name(player2_id)}:({score_p2})", player1_id) + logger.log(f"{get_player_name(player1_id)}:({score_p1}) posted Match. System: {system_name}, {get_player_name(player2_id)}:({score_p2})", player1_id) connection.commit() connection.close() @@ -341,12 +343,12 @@ def save_calculated_match(calc_results: dict): )) connection.commit() - logger.log("pi.save_calculated_match", f"Match ID:{match_id} berechnet.") + logger.log(f"Match ID:{match_id} calculated.") return True except Exception as e: connection.rollback() - logger.log("data_api.save_calculated_match", f"KRITISCHER FEHLER beim Speichern des Matches: {e}") + logger.log(f"KRITISCHER FEHLER beim Speichern des Matches: {e}") return False finally: @@ -445,7 +447,7 @@ def get_match_by_id(match_id: int) -> dict | None: return dict(zip(columns, row)) except Exception as e: - print(f"Fehler beim Laden des Matches: {e}") + logger.log(f"Fehler beim Laden des Matches: {e}") return None finally: @@ -552,7 +554,7 @@ def delete_match(match_id, player_id): # DELETE FROM löscht die gesamte Zeile, bei der die ID übereinstimmt. cursor.execute("DELETE FROM matches WHERE id = ?", (match_id,)) - logger.log("data_api.delete_match", f"Match mit ID{match_id} von User gelöscht.", f"{player_id}") + logger.log(f"Match ID{match_id} deleted from user {get_player_name(player_id)}(ID:{player_id})") connection.commit() connection.close() @@ -563,7 +565,7 @@ def confirm_match(match_id): # Ändert nur die Spalte player2_check auf 1 (True) cursor.execute("UPDATE matches SET player2_check = 1 WHERE id = ?", (match_id,)) - logger.log("data_api.confirm_match", f"Match mit ID{match_id} von Player2 bestätigt.") + logger.log(f"Match mit ID{match_id} von Player2 bestätigt.") connection.commit() connection.close() @@ -670,7 +672,7 @@ def create_random_dummy_match(player_id): dummy_name = get_player_name(dummy_id) sys_name = get_system_name(1) - logger.log("TEST_MATCH", f"Zufallsspiel generiert. [{sys_name}]: {p1_name} ({score_p1}) vs. {dummy_name} ({score_p2})", player_id) + logger.log(f"Zufallsspiel generiert. [{sys_name}]: {p1_name} ({score_p1}) vs. {dummy_name} ({score_p2})") return True diff --git a/gui/admin_gui.py b/gui/admin_gui.py index a9cdd25..5b609ad 100644 --- a/gui/admin_gui.py +++ b/gui/admin_gui.py @@ -12,12 +12,11 @@ def setup_routes(): with ui.header().classes('items-center justify-between bg-zinc-900 p-4 shadow-lg'): ui.button("Zurück", on_click= lambda: ui.navigate.to("/") ) - if app.storage.user.get('authenticated', False): - with ui.card().classes("w-full"): - with ui.row().classes("w-full"): + if app.storage.user.get('authenticated', False): + with ui.row().classes("w-full"): ui.button(text= "test", on_click=lambda: data_api.create_random_dummy_match(2)) - ui.button(icon="refresh", on_click=lambda: ui.navigate.reload) - + ui.button(icon="refresh", on_click= lambda: ui.navigate.to("/admin") ) + with ui.card().classes("w-full"): ui.label("System Audit Log").classes('text-2xl font-bold text-white mb-4') # Daten abrufen @@ -25,18 +24,16 @@ def setup_routes(): # Tabelle aufbauen columns = [ - {'name': 'time', 'label': 'Zeitstempel', 'field': 'timestamp', 'align': 'left', 'sortable': True}, - {'name': 'user', 'label': 'Auslöser', 'field': 'player_name', 'align': 'left'}, - {'name': 'action', 'label': 'Aktion', 'field': 'action', 'align': 'left', 'sortable': True}, + {'name': 'time', 'label': 'Time', 'field': 'timestamp', 'align': 'left', 'sortable': True}, + {'name': 'file', 'label': 'Datei', 'field': 'file', 'align': 'left'}, + {'name': 'source', 'label': 'Quelle', 'field': 'source', 'align': 'left'}, {'name': 'details', 'label': 'Details', 'field': 'details', 'align': 'left'} ] if len(log_data) > 0: - # Wir schneiden bei den Millisekunden vom Zeitstempel wieder etwas ab [:19] + # Millisekunden vom Zeitstempel abschneiden [:19] for row in log_data: row['timestamp'] = str(row['timestamp'])[:19] - - # Tabelle zeichnen ui.table(columns=columns, rows=log_data, row_key='id').classes('w-full bg-zinc-900 text-gray-300') else: ui.label("Das Logbuch ist leer.").classes('text-gray-500 italic') \ No newline at end of file diff --git a/gui/info_text/info_system.py b/gui/info_text/info_system.py index b584714..0187ef5 100644 --- a/gui/info_text/info_system.py +++ b/gui/info_text/info_system.py @@ -2,11 +2,11 @@ import json import os from nicegui import ui -# 1. Pfad zur JSON Datei berechnen +# Pfad zur JSON Datei berechnen BASE_DIR = os.path.dirname(os.path.abspath(__file__)) JSON_PATH = os.path.join(BASE_DIR, "info_texts.json") -# 2. JSON Datei in den Speicher laden +# JSON Datei in den Speicher laden def load_info_texts(): if os.path.exists(JSON_PATH): # encoding="utf-8" ist wichtig für deutsche Umlaute (ä, ö, ü)! @@ -19,7 +19,6 @@ def load_info_texts(): # Wir laden das Wörterbuch genau 1x beim Serverstart TEXT_DICTIONARY = load_info_texts() -# 3. Unser neuer Baustein für die Webseite def create_info_button(topic_key): # Den Text aus dem Wörterbuch holen. Falls nicht vorhanden wird eine Fehlermeldung geworfen. texts = TEXT_DICTIONARY.get(topic_key, ["Hilfetext nicht gefunden!"]) diff --git a/gui/info_text/info_texts.json b/gui/info_text/info_texts.json index 034cce9..182e91f 100644 --- a/gui/info_text/info_texts.json +++ b/gui/info_text/info_texts.json @@ -12,6 +12,10 @@ "**ACHTUNG:** Ein Spieler ist nur als Gegner auswählbar wenn er sich in der Liga angemeldet hat!", "Solltest du einen Fehler machen kannst du das 'falsche' Match auf der Hauptseite noch löschen bevor es bestätigt wurde." ], + "khorne": ["Khorne will Blut und Schädel!"], + "tzeentch": ["tzeentch Pläne gehen auf!"], + "basis_mmr": ["Berechnet mit dem MMR Unterschied."], + "tyrann_info": [], "prügelknabe_info": [] } \ No newline at end of file diff --git a/match_calculations/calc_match.py b/match_calculations/calc_match.py index 1a42ab4..433cd04 100644 --- a/match_calculations/calc_match.py +++ b/match_calculations/calc_match.py @@ -93,8 +93,8 @@ def calculate_match (match_id): } } - logger.log(f"calc_match ID:{match_id}", f"Winner {data_api.get_player_name(winner_id)}: Base {w_base} + Khorne({w_khorne}) + Slaanesh({slaanesh}) + Tzeentch({tzeentch}) = {calc_results[winner_id]["total"]}") - logger.log(f"calc_match ID:{match_id}", f"Looser {data_api.get_player_name(looser_id)}: -Base({l_base}) + Khorne({l_khorne}) - Slaanesh({slaanesh}) - Tzeentch({tzeentch}) = {calc_results[looser_id]["total"]}") + logger.log(f"Match{match_id}: Winner {data_api.get_player_name(winner_id)}: Base {w_base} + Khorne({w_khorne}) + Slaanesh({slaanesh}) + Tzeentch({tzeentch}) = {calc_results[winner_id]["total"]}") + logger.log(f"Match{match_id}: Looser {data_api.get_player_name(looser_id)}: -Base({l_base}) + Khorne({l_khorne}) - Slaanesh({slaanesh}) - Tzeentch({tzeentch}) = {calc_results[looser_id]["total"]}") data_api.save_calculated_match(calc_results) diff --git a/wood/logger.py b/wood/logger.py index c50652d..9c51bd5 100644 --- a/wood/logger.py +++ b/wood/logger.py @@ -1,22 +1,39 @@ import sqlite3 import os +import inspect from data.setup_database import DB_PATH DB_FILE = "wood/log.db" -def log(action, details, player_id = None): - """Schreibt ein Ereignis (Audit Trail) in das System-Log.""" - connection = sqlite3.connect(DB_FILE) - cursor = connection.cursor() +def log(details): + # Arbeitsdaten der aktuellen Funktion (log) holen + this_frame = inspect.currentframe() + try: + # Einen Schritt "nach oben" zum Aufrufer gehen + caller_frame = this_frame.f_back + # 3. Lesbare Infos aus dem Aufrufer-Frame extrahieren + info = inspect.getframeinfo(caller_frame) + file = f"{info.filename}" + source = f"{info.function}() L:{info.lineno}" - cursor.execute( - "INSERT INTO system_log (player_id, action, details) VALUES (?, ?, ?)", - (player_id, action, details) - ) + # 5. Datenbank-Verbindung aufbauen (DB_FILE muss vorher definiert sein) + connection = sqlite3.connect(DB_FILE) + cursor = connection.cursor() + + # 6. Daten in die Datenbank schreiben + cursor.execute( + "INSERT INTO system_log (file, source, details) VALUES (?, ?, ?)", + (file, source, details) + ) + + connection.commit() + connection.close() + + finally: + del this_frame + del caller_frame - connection.commit() - connection.close() def setup_log_db(): @@ -34,8 +51,8 @@ def setup_log_db(): CREATE TABLE IF NOT EXISTS system_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - player_id INTEGER, - action TEXT, + file TEXT, + source TEXT, details TEXT ) ''') @@ -57,19 +74,4 @@ def get_full_log(): logs = [dict(row) for row in log_cursor.fetchall()] log_conn.close() - # 2. Spielernamen aus der Haupt-DB holen - main_conn = sqlite3.connect(DB_PATH) - main_cursor = main_conn.cursor() - main_cursor.execute("SELECT id, display_name, discord_name FROM players") - players_dict = {row[0]: f"{row[1]} ({row[2]})" for row in main_cursor.fetchall()} - main_conn.close() - - # 3. Die IDs in den Logs durch Namen ersetzen - for log_entry in logs: - p_id = log_entry['player_id'] - if p_id is not None and p_id in players_dict: - log_entry['player_name'] = players_dict[p_id] - else: - log_entry['player_name'] = "System / Unbekannt" - return logs \ No newline at end of file -- 2.43.0