commit
5b20bbffd4
|
|
@ -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"}
|
||||||
|
|
@ -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"}
|
||||||
|
|
@ -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"}
|
{}
|
||||||
|
|
@ -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"}
|
||||||
|
|
@ -15,10 +15,12 @@ def validate_user_session(db_id, discord_id):
|
||||||
|
|
||||||
# 1. Fall: Die ID gibt es gar nicht mehr in der Datenbank
|
# 1. Fall: Die ID gibt es gar nicht mehr in der Datenbank
|
||||||
if result is None:
|
if result is None:
|
||||||
|
logger.log(f"Player not found in database. Discord:{discord_id}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# 2. Fall: Die ID gehört jetzt einem anderen Discord-Account (Datenbank wurde resettet)
|
# 2. Fall: Die ID gehört jetzt einem anderen Discord-Account (Datenbank wurde resettet)
|
||||||
if str(result[0]) != str(discord_id):
|
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
|
return False
|
||||||
|
|
||||||
# 3. Fall: Alles ist korrekt!
|
# 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.
|
# Random Silly Name Generator für neue Spieler. Damit sie angeregt werden ihren richtigen Namen einzutragen.
|
||||||
silly_name = generate_silly_name()
|
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))
|
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()
|
connection.commit()
|
||||||
cursor.execute("SELECT id, discord_name, display_name, discord_avatar_url FROM players WHERE discord_id = ?", (discord_id,))
|
cursor.execute("SELECT id, discord_name, display_name, discord_avatar_url FROM players WHERE discord_id = ?", (discord_id,))
|
||||||
player = cursor.fetchone()
|
player = cursor.fetchone()
|
||||||
|
|
@ -150,12 +152,18 @@ def get_player_statistics(player_id):
|
||||||
sys.*,
|
sys.*,
|
||||||
stat.mmr,
|
stat.mmr,
|
||||||
stat.games_in_system,
|
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
|
FROM gamesystems sys
|
||||||
LEFT JOIN player_game_statistic stat
|
LEFT JOIN player_game_statistic stat
|
||||||
ON sys.id = stat.gamesystem_id AND stat.player_id = ?
|
ON sys.id = stat.gamesystem_id AND stat.player_id = ?
|
||||||
"""
|
"""
|
||||||
|
|
||||||
cursor.execute(query, (player_id,))
|
cursor.execute(query, (player_id,))
|
||||||
rows = cursor.fetchall()
|
rows = cursor.fetchall()
|
||||||
connection.close()
|
connection.close()
|
||||||
|
|
@ -178,7 +186,7 @@ def join_league(player_id, gamesystem_id):
|
||||||
INSERT INTO player_game_statistic (player_id, gamesystem_id)
|
INSERT INTO player_game_statistic (player_id, gamesystem_id)
|
||||||
VALUES (?, ?)
|
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))
|
cursor.execute(query, (player_id, gamesystem_id))
|
||||||
connection.commit()
|
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))
|
cursor.execute(query, (sys_id, player1_id, player2_id, score_p1, score_p2))
|
||||||
new_match_id = cursor.lastrowid
|
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.commit()
|
||||||
connection.close()
|
connection.close()
|
||||||
|
|
@ -335,12 +343,12 @@ def save_calculated_match(calc_results: dict):
|
||||||
))
|
))
|
||||||
|
|
||||||
connection.commit()
|
connection.commit()
|
||||||
logger.log("pi.save_calculated_match", f"Match ID:{match_id} berechnet.")
|
logger.log(f"Match ID:{match_id} calculated.")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
connection.rollback()
|
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
|
return False
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
|
|
@ -439,7 +447,7 @@ def get_match_by_id(match_id: int) -> dict | None:
|
||||||
return dict(zip(columns, row))
|
return dict(zip(columns, row))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Fehler beim Laden des Matches: {e}")
|
logger.log(f"Fehler beim Laden des Matches: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
|
|
@ -546,7 +554,7 @@ def delete_match(match_id, player_id):
|
||||||
|
|
||||||
# DELETE FROM löscht die gesamte Zeile, bei der die ID übereinstimmt.
|
# DELETE FROM löscht die gesamte Zeile, bei der die ID übereinstimmt.
|
||||||
cursor.execute("DELETE FROM matches WHERE id = ?", (match_id,))
|
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.commit()
|
||||||
connection.close()
|
connection.close()
|
||||||
|
|
||||||
|
|
@ -557,7 +565,7 @@ def confirm_match(match_id):
|
||||||
|
|
||||||
# Ändert nur die Spalte player2_check auf 1 (True)
|
# Ändert nur die Spalte player2_check auf 1 (True)
|
||||||
cursor.execute("UPDATE matches SET player2_check = 1 WHERE id = ?", (match_id,))
|
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.commit()
|
||||||
connection.close()
|
connection.close()
|
||||||
|
|
||||||
|
|
@ -664,7 +672,7 @@ def create_random_dummy_match(player_id):
|
||||||
dummy_name = get_player_name(dummy_id)
|
dummy_name = get_player_name(dummy_id)
|
||||||
sys_name = get_system_name(1)
|
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
|
return True
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,16 @@ def init_db():
|
||||||
points INTEGER DEFAULT 0,
|
points INTEGER DEFAULT 0,
|
||||||
avv_points INTEGER DEFAULT 0,
|
avv_points INTEGER DEFAULT 0,
|
||||||
last_played TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
last_played TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
win_rate REAL DEFAULT 0,
|
||||||
|
win_streak INTEGER DEFAULT 0,
|
||||||
trend 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 (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)
|
FOREIGN KEY (gamesystem_id) REFERENCES gamesystems (id)
|
||||||
)
|
)
|
||||||
''')
|
''')
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,10 @@ def setup_routes():
|
||||||
ui.button("Zurück", on_click= lambda: ui.navigate.to("/") )
|
ui.button("Zurück", on_click= lambda: ui.navigate.to("/") )
|
||||||
|
|
||||||
if app.storage.user.get('authenticated', False):
|
if app.storage.user.get('authenticated', False):
|
||||||
with ui.card().classes("w-full"):
|
|
||||||
with ui.row().classes("w-full"):
|
with ui.row().classes("w-full"):
|
||||||
ui.button(text= "test", on_click=lambda: data_api.create_random_dummy_match(2))
|
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')
|
ui.label("System Audit Log").classes('text-2xl font-bold text-white mb-4')
|
||||||
|
|
||||||
# Daten abrufen
|
# Daten abrufen
|
||||||
|
|
@ -25,18 +24,16 @@ def setup_routes():
|
||||||
|
|
||||||
# Tabelle aufbauen
|
# Tabelle aufbauen
|
||||||
columns = [
|
columns = [
|
||||||
{'name': 'time', 'label': 'Zeitstempel', 'field': 'timestamp', 'align': 'left', 'sortable': True},
|
{'name': 'time', 'label': 'Time', 'field': 'timestamp', 'align': 'left', 'sortable': True},
|
||||||
{'name': 'user', 'label': 'Auslöser', 'field': 'player_name', 'align': 'left'},
|
{'name': 'file', 'label': 'Datei', 'field': 'file', 'align': 'left'},
|
||||||
{'name': 'action', 'label': 'Aktion', 'field': 'action', 'align': 'left', 'sortable': True},
|
{'name': 'source', 'label': 'Quelle', 'field': 'source', 'align': 'left'},
|
||||||
{'name': 'details', 'label': 'Details', 'field': 'details', 'align': 'left'}
|
{'name': 'details', 'label': 'Details', 'field': 'details', 'align': 'left'}
|
||||||
]
|
]
|
||||||
|
|
||||||
if len(log_data) > 0:
|
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:
|
for row in log_data:
|
||||||
row['timestamp'] = str(row['timestamp'])[:19]
|
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')
|
ui.table(columns=columns, rows=log_data, row_key='id').classes('w-full bg-zinc-900 text-gray-300')
|
||||||
else:
|
else:
|
||||||
ui.label("Das Logbuch ist leer.").classes('text-gray-500 italic')
|
ui.label("Das Logbuch ist leer.").classes('text-gray-500 italic')
|
||||||
|
|
@ -2,11 +2,11 @@ import json
|
||||||
import os
|
import os
|
||||||
from nicegui import ui
|
from nicegui import ui
|
||||||
|
|
||||||
# 1. Pfad zur JSON Datei berechnen
|
# Pfad zur JSON Datei berechnen
|
||||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
JSON_PATH = os.path.join(BASE_DIR, "info_texts.json")
|
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():
|
def load_info_texts():
|
||||||
if os.path.exists(JSON_PATH):
|
if os.path.exists(JSON_PATH):
|
||||||
# encoding="utf-8" ist wichtig für deutsche Umlaute (ä, ö, ü)!
|
# 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
|
# Wir laden das Wörterbuch genau 1x beim Serverstart
|
||||||
TEXT_DICTIONARY = load_info_texts()
|
TEXT_DICTIONARY = load_info_texts()
|
||||||
|
|
||||||
# 3. Unser neuer Baustein für die Webseite
|
|
||||||
def create_info_button(topic_key):
|
def create_info_button(topic_key):
|
||||||
# Den Text aus dem Wörterbuch holen. Falls nicht vorhanden wird eine Fehlermeldung geworfen.
|
# Den Text aus dem Wörterbuch holen. Falls nicht vorhanden wird eine Fehlermeldung geworfen.
|
||||||
texts = TEXT_DICTIONARY.get(topic_key, ["Hilfetext nicht gefunden!"])
|
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"):
|
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
|
# Loopen durch die Sätze in der JSON
|
||||||
for sentence in texts:
|
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) ---
|
# --- DER BUTTON (Sichtbar auf der Seite) ---
|
||||||
ui.button(icon="info_outline", color= "", on_click=info_dialog.open).props('round dense')
|
ui.button(icon="info_outline", color= "", on_click=info_dialog.open).props('round dense')
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,10 @@
|
||||||
"**ACHTUNG:** Ein Spieler ist nur als Gegner auswählbar wenn er sich in der Liga angemeldet hat!",
|
"**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."
|
"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": [],
|
"tyrann_info": [],
|
||||||
"prügelknabe_info": [],
|
"prügelknabe_info": [],
|
||||||
"rang_info": [
|
"rang_info": [
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ def setup_routes():
|
||||||
)
|
)
|
||||||
|
|
||||||
if system_stat:
|
if system_stat:
|
||||||
# None-Werte absichern
|
|
||||||
mmr = system_stat["mmr"] or 0
|
mmr = system_stat["mmr"] or 0
|
||||||
games = system_stat["games_in_system"] or 0
|
games = system_stat["games_in_system"] or 0
|
||||||
points = system_stat["points"] or 0
|
points = system_stat["points"] or 0
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ from gui.info_text import info_system
|
||||||
def setup_routes():
|
def setup_routes():
|
||||||
|
|
||||||
# 1. Die {}-Klammern definieren eine dynamische Variable in der URL
|
# 1. Die {}-Klammern definieren eine dynamische Variable in der URL
|
||||||
@ui.page('/add-match/{systemname}', dark=True)
|
@ui.page('/add-match/{system_name}', dark=True)
|
||||||
def match_form_page(systemname: str): # <--- WICHTIG: Hier fangen wir das Wort aus der URL auf!
|
def match_form_page(system_name: str): # <--- WICHTIG: Hier fangen wir das Wort aus der URL auf!
|
||||||
gui_style.apply_design()
|
gui_style.apply_design()
|
||||||
|
|
||||||
# --- SICHERHEITS-CHECK ---
|
# --- SICHERHEITS-CHECK ---
|
||||||
|
|
@ -17,38 +17,61 @@ def setup_routes():
|
||||||
ui.button('Back to Home', on_click=lambda: ui.navigate.to('/'))
|
ui.button('Back to Home', on_click=lambda: ui.navigate.to('/'))
|
||||||
return
|
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'):
|
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
|
# 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.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"):
|
with ui.column().classes("w-full items-center gap-6"):
|
||||||
# 1. Daten aus der DB holen
|
# 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')
|
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 = {}
|
dropdown_options = {}
|
||||||
for p in raw_players:
|
for p in raw_players:
|
||||||
if p['player_id'] == my_id:
|
if p['player_id'] == my_id:
|
||||||
continue
|
continue
|
||||||
dropdown_options[p['player_id']] = f"{p['display_name']} 'aka' {p['discord_name']}"
|
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')
|
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.
|
# Das Match in die Datenbank eintragen lassen und die MMR Berechnung triggern.
|
||||||
def input_match_to_database():
|
def input_match_to_database():
|
||||||
|
|
@ -60,14 +83,14 @@ def setup_routes():
|
||||||
score_p1 = p1_points.value
|
score_p1 = p1_points.value
|
||||||
score_p2 = p2_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
|
# 4. Erfolgsmeldung und Berechnung
|
||||||
ui.notify("Match erfolgreich eingetragen!", color="green")
|
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
|
# Buttons ganz unten in einer Reihe
|
||||||
with ui.row().classes("w-full items-center justify-between mt-8"):
|
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")
|
info_system.create_info_button("match_form_info")
|
||||||
ui.button(text="Absenden", color="positive", on_click=lambda: input_match_to_database())
|
ui.button(text="Absenden", color="positive", on_click=lambda: input_match_to_database())
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ def setup_routes():
|
||||||
|
|
||||||
player_id = app.storage.user.get('db_id')
|
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'):
|
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.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']}"
|
opponent = f"{match['p2_display']} aka {match['p2_discord']}"
|
||||||
my_score = match['score_player1']
|
my_score = match['score_player1']
|
||||||
opp_score = match['score_player2']
|
opp_score = match['score_player2']
|
||||||
my_mmr_change = match['player1_mmr_change']
|
mmr_base = match["player1_base_change"]
|
||||||
my_khorne = match['player1_khorne']
|
mmr_change = match['player1_mmr_change']
|
||||||
my_tzeentch = match['player1_tzeentch']
|
khorne = match['player1_khorne']
|
||||||
my_slaanesh = match['player1_slaanesh']
|
tzeentch = match['player1_tzeentch']
|
||||||
|
slaanesh = match['player1_slaanesh']
|
||||||
else:
|
else:
|
||||||
opponent = f"{match['p1_display']} aka {match['p1_discord']}"
|
opponent = f"{match['p1_display']} aka {match['p1_discord']}"
|
||||||
my_score = match['score_player2']
|
my_score = match['score_player2']
|
||||||
opp_score = match['score_player1']
|
opp_score = match['score_player1']
|
||||||
my_mmr_change = match['player2_mmr_change']
|
mmr_base = match["player2_base_change"]
|
||||||
my_khorne = match['player2_khorne']
|
mmr_change = match['player2_mmr_change']
|
||||||
my_tzeentch = match['player2_tzeentch']
|
khorne = match['player2_khorne']
|
||||||
my_slaanesh = match['player2_slaanesh']
|
tzeentch = match['player2_tzeentch']
|
||||||
|
slaanesh = match['player2_slaanesh']
|
||||||
|
|
||||||
if my_score > opp_score:
|
if my_score > opp_score:
|
||||||
result = "Gewonnen"
|
result = "Gewonnen"
|
||||||
|
|
@ -70,30 +72,29 @@ def setup_routes():
|
||||||
'date': str(match['played_at'])[:10],
|
'date': str(match['played_at'])[:10],
|
||||||
'system': match['gamesystem_name'],
|
'system': match['gamesystem_name'],
|
||||||
'score': str(my_score),
|
'score': str(my_score),
|
||||||
'opponent': opponent,
|
'opponent': (f"{opponent} ({opp_score})"),
|
||||||
'opp_score': str(opp_score),
|
"basis" : mmr_base,
|
||||||
'result': result,
|
|
||||||
'elo': fmt_signed(round(elo_factor, 2) if elo_factor is not None else None, pending),
|
'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),
|
'rust': fmt_signed(round(rust_factor, 2) if rust_factor is not None else None, pending),
|
||||||
'khorne': fmt_signed(my_khorne, pending),
|
'khorne': fmt_signed(khorne, pending),
|
||||||
'tzeentch': fmt_signed(my_tzeentch, pending),
|
'tzeentch': fmt_signed(tzeentch, pending),
|
||||||
'slaanesh': fmt_signed(my_slaanesh, pending),
|
'slaanesh': fmt_signed(slaanesh, pending),
|
||||||
'mmr': fmt_signed(my_mmr_change, pending),
|
'mmr': fmt_signed(mmr_change, pending),
|
||||||
})
|
})
|
||||||
|
|
||||||
columns = [
|
columns = [
|
||||||
{'name': 'date', 'label': 'Datum', 'field': 'date', 'align': 'left'},
|
{'name': 'date', 'label': 'Datum', 'field': 'date', 'align': 'left'},
|
||||||
{'name': 'system', 'label': 'System', 'field': 'system', 'align': 'left'},
|
{'name': 'system', 'label': 'System', 'field': 'system', 'align': 'left'},
|
||||||
{'name': 'score', 'label': 'Eigene Punkte', 'field': 'score', 'align': 'center'},
|
{'name': 'score', 'label': 'Punkte', 'field': 'score', 'align': 'center'},
|
||||||
{'name': 'opponent', 'label': 'Gegner', 'field': 'opponent', 'align': 'left'},
|
{'name': 'opponent', 'label': 'Gegner (Pkt.)', 'field': 'opponent', 'align': 'left'},
|
||||||
{'name': 'opp_score','label': 'Gegner Punkte', 'field': 'opp_score','align': 'center'},
|
|
||||||
{'name': 'result', 'label': 'Ergebnis', 'field': 'result', 'align': 'left'},
|
|
||||||
{'name': 'elo', 'label': 'Elo Faktor', 'field': 'elo', 'align': 'right'},
|
{'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': 'rust', 'label': 'Rost Faktor', 'field': 'rust', 'align': 'right'},
|
||||||
{'name': 'khorne', 'label': 'Khorne', 'field': 'khorne', 'align': 'right'},
|
{'name': 'khorne', 'label': 'Khorne', 'field': 'khorne', 'align': 'right'},
|
||||||
{'name': 'tzeentch', 'label': 'Tzeentch', 'field': 'tzeentch', 'align': 'right'},
|
{'name': 'tzeentch', 'label': 'Tzeentch', 'field': 'tzeentch', 'align': 'right'},
|
||||||
{'name': 'slaanesh', 'label': 'Slaanesh', 'field': 'slaanesh', '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
|
# Shared slot template for colored signed values
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import os
|
||||||
from wood import logger
|
from wood import logger
|
||||||
|
|
||||||
|
|
||||||
point_inflation = 0.8 # => entspricht %
|
point_inflation = 1 # => entspricht %
|
||||||
K_FACTOR = 40 # Die "Border" (Maximalpunkte) die ein Sieg gibt.
|
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.
|
# 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"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"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}: 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)
|
data_api.save_calculated_match(calc_results)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,40 @@
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import os
|
import os
|
||||||
|
import inspect
|
||||||
|
|
||||||
from data.setup_database import DB_PATH
|
from data.setup_database import DB_PATH
|
||||||
|
|
||||||
DB_FILE = "wood/log.db"
|
DB_FILE = "wood/log.db"
|
||||||
|
|
||||||
def log(action, details, player_id = None):
|
def log(details):
|
||||||
"""Schreibt ein Ereignis (Audit Trail) in das System-Log."""
|
# 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}"
|
||||||
|
|
||||||
|
# 5. Datenbank-Verbindung aufbauen (DB_FILE muss vorher definiert sein)
|
||||||
connection = sqlite3.connect(DB_FILE)
|
connection = sqlite3.connect(DB_FILE)
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
|
|
||||||
|
# 6. Daten in die Datenbank schreiben
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"INSERT INTO system_log (player_id, action, details) VALUES (?, ?, ?)",
|
"INSERT INTO system_log (file, source, details) VALUES (?, ?, ?)",
|
||||||
(player_id, action, details)
|
(file, source, details)
|
||||||
)
|
)
|
||||||
|
|
||||||
connection.commit()
|
connection.commit()
|
||||||
connection.close()
|
connection.close()
|
||||||
|
|
||||||
|
finally:
|
||||||
|
del this_frame
|
||||||
|
del caller_frame
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def setup_log_db():
|
def setup_log_db():
|
||||||
# --- DATENBANK CHECK ---
|
# --- DATENBANK CHECK ---
|
||||||
|
|
@ -34,8 +51,8 @@ def setup_log_db():
|
||||||
CREATE TABLE IF NOT EXISTS system_log (
|
CREATE TABLE IF NOT EXISTS system_log (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
player_id INTEGER,
|
file TEXT,
|
||||||
action TEXT,
|
source TEXT,
|
||||||
details TEXT
|
details TEXT
|
||||||
)
|
)
|
||||||
''')
|
''')
|
||||||
|
|
@ -57,19 +74,4 @@ def get_full_log():
|
||||||
logs = [dict(row) for row in log_cursor.fetchall()]
|
logs = [dict(row) for row in log_cursor.fetchall()]
|
||||||
log_conn.close()
|
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
|
return logs
|
||||||
Loading…
Reference in New Issue
Block a user