Compare commits

...

3 Commits

Author SHA1 Message Date
Daniel Nagel
1b1031a5e7 data api function angepoasst. Besserer Namen.
Backend Seasons System funktioniert.
2026-06-09 13:23:00 +00:00
Daniel Nagel
e281160ec7 some print() lines deleted. 2026-06-09 07:41:03 +00:00
85e0b57833 Merge pull request 'master' (#48) from master into dev_league_seasons
Reviewed-on: #48
2026-05-31 12:03:25 +02:00
6 changed files with 141 additions and 111 deletions

View File

@ -182,41 +182,75 @@ def get_player_statistics(player_id):
return rows
def get_player_statistic(player_id, system_id):
def get_player_statistic(player_id, system_id, season_id=None):
"""
- Wenn season_id angegeben: Daten für diese Season.
- Wenn season_id = None: Aggregierte Gesamtstatistik.
"""
connection = db_connection()
cursor = connection.cursor(dictionary=True)
query = """
SELECT
sys.id AS gamesystem_id,
sys.name AS gamesystem_name,
sys.description,
stat.mmr,
stat.games_in_system,
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 = %s
WHERE sys.id = %s
"""
if season_id:
query = """
SELECT
sys.name AS gamesystem_name,
stat.mmr, stat.games_in_system, 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 = %s
AND stat.season_id = %s
WHERE sys.id = %s
"""
cursor.execute(query, (player_id, season_id, system_id))
else:
query = """
SELECT
sys.name AS gamesystem_name,
SUM(stat.mmr) / COUNT(stat.season_id) AS mmr, -- Durchschnitt MMR über Seasons
SUM(stat.games_in_system) AS games_in_system,
SUM(stat.points) AS points,
SUM(stat.points) / SUM(stat.games_in_system) AS avv_points,
MAX(stat.last_played) AS last_played
FROM gamesystems sys
LEFT JOIN player_game_statistic stat
ON sys.id = stat.gamesystem_id
AND stat.player_id = %s
WHERE sys.id = %s
GROUP BY sys.id
"""
cursor.execute(query, (player_id, system_id))
cursor.execute(query, (player_id, system_id))
row = cursor.fetchone()
cursor.close()
connection.close()
return row
def get_player_rank(player_id, system_id):
"""Holt den Rang eines Spielers in einem System basierend auf MMR (absteigend)."""
def get_seasons_by_system(gamesystem_id):
"""Holt alle Seasons eines Spielsystems, sortiert nach Nummer absteigend."""
connection = db_connection()
cursor = connection.cursor(dictionary=True)
query = """
SELECT id, name, season_number
FROM seasons
WHERE gamesystem_id = %s
ORDER BY season_number DESC
"""
cursor.execute(query, (gamesystem_id,))
seasons = cursor.fetchall()
cursor.close()
connection.close()
return seasons
def get_player_rank(player_id, system_id, season_id):
"""Holt den Rang eines Spielers in einem System für eine spezifische Season."""
connection = db_connection()
cursor = connection.cursor(dictionary=True)
@ -224,23 +258,24 @@ def get_player_rank(player_id, system_id):
SELECT COUNT(*) + 1 AS rank
FROM player_game_statistic stat
WHERE stat.gamesystem_id = %s
AND stat.season_id = %s
AND stat.games_in_system > 0
AND stat.mmr > (
SELECT stat2.mmr
FROM player_game_statistic stat2
WHERE stat2.player_id = %s
AND stat2.gamesystem_id = %s
AND stat2.season_id = %s
)
"""
cursor.execute(query, (system_id, player_id, system_id))
# Parameter: gamesystem, season, player, gamesystem, season
cursor.execute(query, (system_id, season_id, player_id, system_id, season_id))
row = cursor.fetchone()
cursor.close()
connection.close()
if row and row['rank'] is not None:
return row['rank']
return None
return row['rank'] if row and row['rank'] is not None else 1
@ -548,7 +583,7 @@ def get_player_name(player_id):
return "Gelöschter Spieler"
def get_system_name(sys_id):
def get_system_name_by_id(sys_id):
if sys_id is None:
return "Unbekanntes System"

View File

@ -20,9 +20,8 @@ def db_connection():
password=os.getenv("DB_PASSWORD"),
database=os.getenv(f"{prefix}NAME")
)
return connection
if is_prod:
print("Verbindung mit Test DB")
except mariadb.Error as e:
raise RuntimeError(f"Fehler beim Verbinden mit {'Prod' if is_prod else 'Test'}-DB: {e}")

View File

@ -3,6 +3,7 @@ from data import database, data_api
from gui import gui_style
from wood import logger
from gui import main_gui
from utils.season_scheduler import check_new_seasons
def setup_routes():
@ui.page('/admin', dark=True)
@ -15,7 +16,13 @@ def setup_routes():
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.to("/admin") )
ui.button(icon="refresh", on_click= lambda: ui.navigate.to("/admin") )
def handle_manual_check():
ui.notify('Starte Season-Check...', type='info')
check_new_seasons()
ui.notify('Season-Check abgeschlossen!', type='positive')
ui.button('Admin: Season-Check erzwingen', on_click=handle_manual_check)
with ui.card().classes("w-full"):
ui.label("System Audit Log").classes('text-2xl font-bold text-white mb-4')

View File

@ -7,7 +7,6 @@ from gui.templates.leaderboard import Leaderboard
def setup_routes():
@ui.page('/statistic/{system_name}', dark=True)
def gamesystem_statistic_page(system_name: str):
if not app.storage.user.get('authenticated', False):
ui.navigate.to('/')
return
@ -16,76 +15,5 @@ def setup_routes():
player_id = app.storage.user.get('db_id')
system_id = data_api.get_gamesystem_id_by_name(system_name)
print(player_id, system_id)
player_stats = data_api.get_player_statistic(player_id, system_id)
# Header
with ui.header().classes('items-center justify-between bg-zinc-900 p-4 shadow-lg'):
ui.button(icon="arrow_back", on_click=lambda: ui.navigate.to('/')).props("round")
ui.button("Spiel eintragen", on_click=lambda: ui.navigate.to(f'/add-match/{system_name}'))
with ui.column().classes('w-full items-center justify-center mt-10'):
ui.label(f'Deine Statistik in {system_name}').classes('text-3xl justify-center font-bold text-normaltext')
# --- BLOCK 1 (MMR & Rang | Rangliste) ---
with ui.element('div').classes("w-full grid grid-cols-1 lg:grid-cols-3 gap-4 mt-4"):
with ui.column().classes("w-full gap-4"):
with ui.card().classes("w-full items-center justify-center text-center"):
with ui.row().classes("w-full items-center text-center"):
ui.label("MMR Punkte: ").classes('justify-center text-2xl font-bold text-normaltext')
ui.space()
info_system.create_info_button("mmr_info")
ui.label(str(player_stats["mmr"])).classes('text-4xl font-bold text-accent')
with ui.card().classes("w-full items-center justify-center text-center"):
with ui.row().classes("w-full items-center text-center"):
ui.label("Rang: ").classes('justify-center text-2xl font-bold text-normaltext')
ui.space()
info_system.create_info_button("rang_info")
ui.label(str(data_api.get_player_rank(player_id,system_id))).classes('text-4xl font-bold text-blue-100')
with ui.card().classes("w-full lg:col-span-2 max-h-[50vh] overflow-y-auto"):
ui.label("Liga Rangliste").classes("text-xl font-bold text-white mb-2 top-0 bg-zinc-900 z-10")
Leaderboard(system_name)
# --- BLOCK 2 (5 Karten) ---
with ui.card().classes("w-full"):
with ui.element('div').classes("w-full grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"):
with ui.card().classes("items-center justify-center text-center"):
ui.label("Spiele: ").classes('text-2xl font-bold')
ui.label(str(player_stats["games_in_system"])).classes('text-4xl font-bold text-blue-100')
with ui.card().classes("items-center justify-center text-center"):
ui.label("Ø Punkte pro Spiel: ").classes('text-2xl font-bold')
ui.label(str(player_stats["avv_points"])).classes('text-4xl font-bold text-blue-100')
with ui.card().classes("items-center justify-center text-center"):
ui.label("Win-Rate: ").classes('text-2xl font-bold')
ui.label("-").classes('text-4xl font-bold text-blue-100')
with ui.card().classes("items-center justify-center text-center"):
ui.label("Letztes Spiel am: ").classes('text-2xl font-bold')
ui.label(str(player_stats["last_played"])).classes('text-4xl font-bold text-blue-100')
with ui.card().classes("items-center justify-center text-center"):
ui.label("Win-Streak: ").classes('text-2xl font-bold')
ui.label("-").classes('text-4xl font-bold text-blue-100')
# --- BLOCK 3 (3 Karten) ---
with ui.card().classes("w-full"):
with ui.element('div').classes("w-full grid grid-cols-1 md:grid-cols-3 lg:grid-cols-3 gap-4"):
with ui.card().classes("items-center justify-center text-center"):
ui.label("Dein Nemesis: ").classes('text-2xl font-bold')
ui.label(str(player_stats["nemesis_id"])).classes('text-4xl font-bold text-blue-100')
with ui.card().classes("items-center justify-center text-center"):
ui.label("Meisten Spiele mit: ").classes('text-2xl font-bold')
ui.label(str(player_stats["nemesis_id"])).classes('text-4xl font-bold text-blue-100')
with ui.card().classes("items-center justify-center text-center"):
ui.label("Dein 'Prügelknabe': ").classes('text-2xl font-bold')
ui.label(str(player_stats["pushover_id"])).classes('text-4xl font-bold text-blue-100')

View File

@ -72,7 +72,6 @@ def setup_routes(admin_discord_id):
new_name = name_input.value
# Nur speichern, wenn ein Name drinsteht und er anders ist als vorher
if new_name and new_name != display_name:
print("save")
data_api.change_display_name(player_id, new_name) # Deine DB Funktion
app.storage.user['display_name'] = new_name
ui.notify('Name gespeichert!', color='positive')

View File

@ -1,26 +1,88 @@
import schedule
import time
from datetime import date
from data import database
from data import database, data_api
from wood import logger
def check_new_seasons():
print("Season mach brrrrr")
today = date.today()
conn = database.db_connection()
cursor = conn.cursor(dictionary=True)
# 1. Prüfen, ob für heute ein Start geplant ist
cursor.execute(
"SELECT id, gamesystem_id FROM season_schedule "
"WHERE start_month = %s AND start_day = %s",
(today.month, today.day)
)
triggers = cursor.fetchall()
print(triggers)
print(f"[Season Check] {today} – {len(triggers)} Treffer gefunden")
for trigger in triggers:
gamesystem_id = trigger['gamesystem_id']
# 2. Prüfen, ob heute schon eine Season für das System erstellt wurde
cursor.execute(
"SELECT id FROM seasons WHERE gamesystem_id = %s AND start_date = %s",
(gamesystem_id, today)
)
if cursor.fetchone():
continue
# 3. Alte Season beenden (end_date = gestern)
cursor.execute(
"UPDATE seasons SET end_date = DATE_SUB(CURDATE(), INTERVAL 1 DAY) "
"WHERE gamesystem_id = %s AND end_date IS NULL",
(gamesystem_id,)
)
# 4. Neue Season anlegen
cursor.execute(
"SELECT MAX(season_number) as last_num FROM seasons WHERE gamesystem_id = %s",
(gamesystem_id,)
)
result = cursor.fetchone()
last_num = result['last_num'] if result['last_num'] else 0
new_season_number = last_num + 1
cursor.execute(
"INSERT INTO seasons (gamesystem_id, name, start_date, season_number) "
"VALUES (%s, %s, %s, %s)",
(gamesystem_id, f"{data_api.get_system_name_by_id(gamesystem_id)} - Season{new_season_number}", today, new_season_number)
)
new_season_id = cursor.lastrowid
logger.log(f"new Season for {data_api.get_system_name_by_id(gamesystem_id)}, Season ID:{new_season_id}")
# 5. AUTO-JOIN: Spieler der alten Season in die neue Season kopieren
cursor.execute(
"SELECT id FROM seasons WHERE gamesystem_id = %s AND id != %s ORDER BY id DESC LIMIT 1",
(gamesystem_id, new_season_id)
)
old_season = cursor.fetchone()
if old_season:
old_season_id = old_season['id']
# Der Auto-Join Befehl
# Wir nehmen player_id und gamesystem_id aus der alten Season
# und setzen die neue season_id ein.
cursor.execute("""
INSERT INTO player_game_statistic (player_id, gamesystem_id, season_id, wins, loss, draws, games_in_system)
SELECT player_id, gamesystem_id, %s, 0, 0, 0, 0
FROM player_game_statistic
WHERE season_id = %s
""", (new_season_id, old_season_id))
logger.log(f"Player copied from SeasonID {old_season_id} to SeasonID {new_season_id}")
conn.commit()
cursor.close()
conn.close()
def run_scheduler():
"""Läuft in einem Hintergrund-Thread."""