Compare commits

...

5 Commits

14 changed files with 147 additions and 104 deletions

View File

@ -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"}
{"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"}

View File

@ -0,0 +1 @@
{"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"}

View File

@ -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"}
{}

View File

@ -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"}

View File

@ -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()
@ -150,12 +152,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()
@ -178,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()
@ -250,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()
@ -335,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:
@ -439,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:
@ -546,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()
@ -557,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()
@ -664,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

View File

@ -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)
)
''')

View File

@ -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')

View File

@ -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!"])
@ -28,7 +27,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')

View File

@ -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": [],
"rang_info": [

View File

@ -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

View File

@ -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,38 +17,61 @@ 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("Meine Punkte:").classes('text-xl font-bold w-full text-left')
ui.label(f'Neues Spiel für {system_name} eintragen').classes('text-2xl font-bold text-center mb-6')
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)
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=0, max=100, 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")
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():
@ -60,14 +83,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())

View File

@ -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

View File

@ -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.
@ -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)

View File

@ -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