MMR Berechnung sollte jeztt funktionieren. Muss noch testen.
Ein paar Tooltips verteilen wäre noch nett...
This commit is contained in:
parent
aaaf96caed
commit
931189baca
|
|
@ -1 +1 @@
|
|||
{"authenticated":true,"discord_id":"277898241750859776","discord_name":"mrteels","db_id":2,"display_name":"Schwitzender 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":"Verwirrter Servitor","discord_avatar_url":"https://cdn.discordapp.com/avatars/277898241750859776/7c3446bb51fafd72b1b4c21124b4994f.png"}
|
||||
294
data/data_api.py
294
data/data_api.py
|
|
@ -70,8 +70,6 @@ def get_or_create_player(discord_id, discord_name, avatar_url):
|
|||
return player
|
||||
|
||||
|
||||
|
||||
|
||||
def get_all_players():
|
||||
connection = sqlite3.connect(DB_PATH)
|
||||
cursor = connection.cursor()
|
||||
|
|
@ -88,8 +86,6 @@ def get_all_players():
|
|||
return result
|
||||
|
||||
|
||||
|
||||
|
||||
def get_all_players_from_system(system_name):
|
||||
connection = sqlite3.connect(DB_PATH)
|
||||
connection.row_factory = sqlite3.Row
|
||||
|
|
@ -119,6 +115,7 @@ def get_all_players_from_system(system_name):
|
|||
|
||||
return result
|
||||
|
||||
|
||||
def get_gamesystem_id_by_name(system_name):
|
||||
"""Holt die interne ID eines Spielsystems anhand seines Namens (z.B. 'Warhammer 40k' -> 1)."""
|
||||
connection = sqlite3.connect(DB_PATH)
|
||||
|
|
@ -138,32 +135,6 @@ def get_gamesystem_id_by_name(system_name):
|
|||
return None
|
||||
|
||||
|
||||
def get_player_rank(player_id, gamesystem_id):
|
||||
"""Sortiert die Liga nach MMR und gibt den aktuellen Platz (Rang) des Spielers zurück."""
|
||||
connection = sqlite3.connect(DB_PATH)
|
||||
connection.row_factory = sqlite3.Row
|
||||
cursor = connection.cursor()
|
||||
|
||||
# Alle Spieler dieser Liga, sortiert nach MMR (höchstes zuerst)
|
||||
query = """
|
||||
SELECT player_id
|
||||
FROM player_game_statistic
|
||||
WHERE gamesystem_id = ?
|
||||
ORDER BY mmr DESC
|
||||
"""
|
||||
cursor.execute(query, (gamesystem_id,))
|
||||
rows = cursor.fetchall()
|
||||
connection.close()
|
||||
|
||||
# Wir zählen die Liste durch (enumerate fängt bei 0 an, also machen wir +1 für den Rang)
|
||||
for index, row in enumerate(rows):
|
||||
if row['player_id'] == player_id:
|
||||
return index + 1
|
||||
|
||||
# Falls der Spieler nicht in der Liga ist (sollte nicht passieren)
|
||||
return 999
|
||||
|
||||
|
||||
|
||||
def get_gamesystem_data():
|
||||
connection = sqlite3.connect(DB_PATH)
|
||||
|
|
@ -305,219 +276,98 @@ def add_new_match(system_name, player1_id, player2_id, score_p1, score_p2):
|
|||
|
||||
|
||||
|
||||
def get_player_system_stats(player_id, system_name):
|
||||
"""Holt die gespeicherten Statistiken eines Spielers für ein spezielles System direkt aus der Tabelle."""
|
||||
connection = sqlite3.connect(DB_PATH)
|
||||
connection.row_factory = sqlite3.Row
|
||||
cursor = connection.cursor()
|
||||
|
||||
# stat.* holt einfach ALLE Spalten, die in der Tabelle player_game_statistic stehen
|
||||
query = """
|
||||
SELECT stat.*
|
||||
FROM player_game_statistic stat
|
||||
JOIN gamesystems sys ON stat.gamesystem_id = sys.id
|
||||
WHERE stat.player_id = ? AND sys.name = ?
|
||||
"""
|
||||
|
||||
cursor.execute(query, (player_id, system_name))
|
||||
row = cursor.fetchone()
|
||||
connection.close()
|
||||
|
||||
# Wenn wir was finden, machen wir ein Dictionary draus, ansonsten geben wir None zurück
|
||||
if row:
|
||||
return dict(row)
|
||||
logger.log("data_api/Get Player Data", "get_player_system_stats returned None. New player?")
|
||||
return None
|
||||
|
||||
|
||||
def get_last_20_match_scores(player_id, system_name):
|
||||
"""Holt die erspielten Punkte und das Datum der letzten 20 Matches."""
|
||||
connection = sqlite3.connect(DB_PATH)
|
||||
connection.row_factory = sqlite3.Row
|
||||
cursor = connection.cursor()
|
||||
|
||||
# NEU: Wir haben 'm.played_at' im SELECT hinzugefügt!
|
||||
query = """
|
||||
SELECT m.player1_id, m.score_player1, m.score_player2, m.played_at
|
||||
FROM matches m
|
||||
JOIN gamesystems sys ON m.gamesystem_id = sys.id
|
||||
WHERE sys.name = ? AND (m.player1_id = ? OR m.player2_id = ?)
|
||||
ORDER BY m.played_at DESC
|
||||
LIMIT 20
|
||||
"""
|
||||
|
||||
cursor.execute(query, (system_name, player_id, player_id))
|
||||
rows = cursor.fetchall()
|
||||
connection.close()
|
||||
|
||||
# Wieder umdrehen für den zeitlichen Verlauf (links alt, rechts neu)
|
||||
rows.reverse()
|
||||
|
||||
points_list = []
|
||||
labels_list = []
|
||||
|
||||
for row in rows:
|
||||
if row['player1_id'] == player_id:
|
||||
points_list.append(row['score_player1'])
|
||||
else:
|
||||
points_list.append(row['score_player2'])
|
||||
|
||||
# NEU: Wir schneiden das Datum ab (z.B. 2024-03-04) und nutzen es als Label!
|
||||
date_clean = str(row['played_at'])[:10]
|
||||
labels_list.append(date_clean)
|
||||
|
||||
return {"points": points_list, "labels": labels_list}
|
||||
|
||||
|
||||
|
||||
def get_match_by_id(match_id):
|
||||
"""Holt alle Daten eines spezifischen Matches anhand der Match-ID."""
|
||||
connection = sqlite3.connect(DB_PATH)
|
||||
connection.row_factory = sqlite3.Row # Damit wir wieder ein schönes Dictionary bekommen
|
||||
cursor = connection.cursor()
|
||||
|
||||
# m.* holt alle Spalten aus dem Match (Punkte, IDs, Datum)
|
||||
# sys.name AS gamesystem_name holt uns direkt den passenden Text für deine JSON-Ladefunktion
|
||||
query = """
|
||||
SELECT m.*, sys.name AS gamesystem_name
|
||||
FROM matches m
|
||||
JOIN gamesystems sys ON m.gamesystem_id = sys.id
|
||||
WHERE m.id = ?
|
||||
"""
|
||||
|
||||
cursor.execute(query, (match_id,))
|
||||
row = cursor.fetchone()
|
||||
|
||||
connection.close()
|
||||
|
||||
# Wenn ein Match gefunden wurde, geben wir es als Dictionary zurück
|
||||
if row:
|
||||
return dict(row)
|
||||
|
||||
return None # Falls die ID nicht existiert
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def get_days_since_last_game(player_id):
|
||||
"""
|
||||
Sucht das absolut letzte Spiel eines Spielers (über alle Systeme)
|
||||
und berechnet, wie viele Tage das her ist.
|
||||
"""
|
||||
connection = sqlite3.connect(DB_PATH)
|
||||
cursor = connection.cursor()
|
||||
|
||||
# MAX(last_played) sucht den absolut neuesten Zeitstempel aus allen Einträgen dieses Spielers
|
||||
query = """
|
||||
SELECT MAX(last_played)
|
||||
FROM player_game_statistic
|
||||
WHERE player_id = ?
|
||||
"""
|
||||
|
||||
cursor.execute(query, (player_id,))
|
||||
row = cursor.fetchone()
|
||||
connection.close()
|
||||
|
||||
# row[0] enthält jetzt unseren Zeitstempel (z.B. '2026-03-05 14:30:00')
|
||||
last_played_str = row[0] if row else None
|
||||
|
||||
# Sicherheitscheck: Hat der Spieler überhaupt schon Einträge?
|
||||
if not last_played_str:
|
||||
return None
|
||||
|
||||
try:
|
||||
# [:19] schneidet Millisekunden ab, damit das Format exakt passt.
|
||||
last_played_date = datetime.strptime(last_played_str[:19], '%Y-%m-%d %H:%M:%S')
|
||||
|
||||
# 2. Die Differenz zu "Jetzt genau in diesem Moment" berechnen
|
||||
time_difference = datetime.now() - last_played_date
|
||||
|
||||
# 3. .days holt aus der Zeitdifferenz nur die reinen, vollen Tage heraus
|
||||
days_ago = time_difference.days
|
||||
|
||||
return {
|
||||
"date_string": last_played_str[:10], # Nur das Datum (YYYY-MM-DD) für die GUI
|
||||
"days_ago": days_ago # Die nackte Zahl zum Rechnen (z.B. 14)
|
||||
}
|
||||
|
||||
except ValueError:
|
||||
# Falls in der Datenbank mal ein kaputter String steht
|
||||
return None
|
||||
|
||||
|
||||
# Eintragen eines berechneten Matches.
|
||||
def save_calculated_match(results):
|
||||
"""
|
||||
Speichert alle Match-Details und aktualisiert gleichzeitig die Statistiken beider Spieler.
|
||||
'results' ist ein Dictionary (dict) mit allen berechneten Werten.
|
||||
"""
|
||||
def save_calculated_match(calc_results: dict):
|
||||
connection = sqlite3.connect(DB_PATH)
|
||||
cursor = connection.cursor()
|
||||
|
||||
try:
|
||||
# 1. Die Match-Tabelle updaten (Alle Detailwerte eintragen und als berechnet markieren)
|
||||
match_id = calc_results["match_id"]
|
||||
winner_id = calc_results["winner_id"]
|
||||
looser_id = calc_results["looser_id"]
|
||||
|
||||
# Match aus DB lesen
|
||||
cursor.execute(
|
||||
"SELECT player1_id, player2_id, gamesystem_id FROM matches WHERE id = ?",
|
||||
(match_id,)
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
if not row:
|
||||
raise ValueError(f"Match ID {match_id} nicht in der Datenbank gefunden.")
|
||||
|
||||
player1_id, player2_id, gamesystem_id = row
|
||||
|
||||
# Daten der Spieler aus calc_results holen (Key = player_id als int/str)
|
||||
p1 = calc_results[player1_id]
|
||||
p2 = calc_results[player2_id]
|
||||
|
||||
# 1. Match-Tabelle updaten
|
||||
match_query = """
|
||||
UPDATE matches
|
||||
SET
|
||||
player1_base_change = ?, player1_khorne = ?, player1_slaanesh = ?, player1_tzeentch = ?, player1_mmr_change = ?,
|
||||
player2_base_change = ?, player2_khorne = ?, player2_slaanesh = ?, player2_tzeentch = ?, player2_mmr_change = ?,
|
||||
rust_factor = ?, elo_factor = ?, point_inflation = ?,
|
||||
match_is_counted = 1
|
||||
player1_base_change = ?, player1_khorne = ?, player1_slaanesh = ?,
|
||||
player1_tzeentch = ?, player1_mmr_change = ?,
|
||||
player2_base_change = ?, player2_khorne = ?, player2_slaanesh = ?,
|
||||
player2_tzeentch = ?, player2_mmr_change = ?,
|
||||
rust_factor = ?, elo_factor = ?, point_inflation = ?,
|
||||
match_is_counted = 1
|
||||
WHERE id = ?
|
||||
"""
|
||||
cursor.execute(match_query, (
|
||||
results.get('p1_base', 0), results.get('p1_khorne', 0), results.get('p1_slaanesh', 0), results.get('p1_tzeentch', 0), results.get('p1_total', 0),
|
||||
results.get('p2_base', 0), results.get('p2_khorne', 0), results.get('p2_slaanesh', 0), results.get('p2_tzeentch', 0), results.get('p2_total', 0),
|
||||
results.get('rust_factor', 1.0), results.get('elo_factor', 0.5), results.get('point_inflation', 0.7),
|
||||
results['match_id']
|
||||
p1["base"], p1["khorne"], p1["slaanesh"], p1["tzeentch"], p1["total"],
|
||||
p2["base"], p2["khorne"], p2["slaanesh"], p2["tzeentch"], p2["total"],
|
||||
calc_results["rust_factor"], calc_results["elo_factor"], calc_results["point_inflation"],
|
||||
match_id
|
||||
))
|
||||
|
||||
# 2. Vorlage für das Update der Spieler-Statistiken
|
||||
# 2. Scores holen
|
||||
cursor.execute(
|
||||
"SELECT score_player1, score_player2 FROM matches WHERE id = ?",
|
||||
(match_id,)
|
||||
)
|
||||
score_row = cursor.fetchone()
|
||||
score_p1, score_p2 = score_row if score_row else (0, 0)
|
||||
|
||||
# 3. Statistik-Query
|
||||
stat_query = """
|
||||
UPDATE player_game_statistic
|
||||
SET
|
||||
mmr = mmr + ?,
|
||||
games_in_system = games_in_system + 1,
|
||||
points = points + ?,
|
||||
avv_points = (points + ?) / (games_in_system + 1),
|
||||
last_played = CURRENT_TIMESTAMP
|
||||
mmr = mmr + ?,
|
||||
games_in_system = games_in_system + 1,
|
||||
points = points + ?,
|
||||
avv_points = (points + ?) / (games_in_system + 1),
|
||||
last_played = CURRENT_TIMESTAMP
|
||||
WHERE player_id = ? AND gamesystem_id = ?
|
||||
"""
|
||||
|
||||
# 3. Statistik für Spieler 1 überschreiben
|
||||
# 4. Statistik Spieler 1
|
||||
cursor.execute(stat_query, (
|
||||
results.get('p1_total', 0), results.get('p1_score', 0), results.get('p1_score', 0),
|
||||
results['p1_id'], results['gamesystem_id']
|
||||
p1["total"], score_p1, score_p1,
|
||||
player1_id, gamesystem_id
|
||||
))
|
||||
|
||||
# 4. Statistik für Spieler 2 überschreiben
|
||||
# 5. Statistik Spieler 2
|
||||
cursor.execute(stat_query, (
|
||||
results.get('p2_total', 0), results.get('p2_score', 0), results.get('p2_score', 0),
|
||||
results['p2_id'], results['gamesystem_id']
|
||||
p2["total"], score_p2, score_p2,
|
||||
player2_id, gamesystem_id
|
||||
))
|
||||
|
||||
# 5. WICHTIG: Erst jetzt (wenn alle 3 Befehle fehlerfrei durchliefen) speichern wir fest!
|
||||
connection.commit()
|
||||
|
||||
# 6. Ein sauberer Log-Eintrag
|
||||
# (Achtung: Stelle sicher, dass du deinen 'logger' hier richtig importiert hast)
|
||||
logger.log("MATCH_CALC", f"Match ID {results['match_id']} wurde komplett berechnet und verbucht.")
|
||||
logger.log("MATCH_CALC", f"Match ID {match_id} wurde komplett berechnet und verbucht.")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
# FAIL-SAFE: Wenn irgendwas schiefgeht, machen wir einen ROLLBACK!
|
||||
# Die Datenbank wird auf den Stand vor dieser Funktion zurückgesetzt. Nichts wird gespeichert.
|
||||
connection.rollback()
|
||||
print(f"KRITISCHER FEHLER beim Speichern des Matches: {e}")
|
||||
return False
|
||||
|
||||
finally:
|
||||
# Die Verbindung wird am Ende immer geschlossen
|
||||
connection.close()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# -----------------------------------------------------
|
||||
# Get Data Funktionen
|
||||
# -----------------------------------------------------
|
||||
|
|
@ -550,6 +400,42 @@ def get_leaderboard(system_name):
|
|||
return result
|
||||
|
||||
|
||||
def get_match_by_id(match_id: int) -> dict | None:
|
||||
"""Gibt alle Match-Daten inkl. Gamesystem-Name als Dict zurück."""
|
||||
connection = sqlite3.connect(DB_PATH)
|
||||
cursor = connection.cursor()
|
||||
|
||||
try:
|
||||
cursor.execute("""
|
||||
SELECT
|
||||
m.id,
|
||||
m.gamesystem_id,
|
||||
g.name AS gamesystem_name,
|
||||
m.player1_id,
|
||||
m.score_player1,
|
||||
m.player2_id,
|
||||
m.score_player2,
|
||||
m.played_at
|
||||
FROM matches m
|
||||
JOIN gamesystems g ON m.gamesystem_id = g.id
|
||||
WHERE m.id = ?
|
||||
""", (match_id,))
|
||||
|
||||
row = cursor.fetchone()
|
||||
if not row:
|
||||
return None
|
||||
|
||||
columns = [desc[0] for desc in cursor.description]
|
||||
return dict(zip(columns, row))
|
||||
|
||||
except Exception as e:
|
||||
print(f"Fehler beim Laden des Matches: {e}")
|
||||
return None
|
||||
|
||||
finally:
|
||||
connection.close()
|
||||
|
||||
|
||||
def get_player_name(player_id):
|
||||
"""Gibt den Namen eines Spielers im Format 'Anzeigename (Discordname)' zurück."""
|
||||
|
||||
|
|
|
|||
|
|
@ -12,5 +12,13 @@
|
|||
"match_form_info": [
|
||||
"Um ein Spiel einzutragen gibt einfach deine Punkte ein. Wähle deinen Gegner aus. Und gibt seine Punkte ein.",
|
||||
"**ACHTUNG:** Ein Spieler ist nur als Gegner auswählbar wenn er sich in der Liga angemeldet hat!"
|
||||
],
|
||||
|
||||
"tyrann_info":[
|
||||
|
||||
],
|
||||
|
||||
"prügelknabe_info":[
|
||||
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,9 @@ from data import data_api
|
|||
from gui.info_text import info_system
|
||||
|
||||
def setup_routes():
|
||||
# 1. Die {}-Klammern definieren eine dynamische Variable in der URL
|
||||
@ui.page('/statistic/{systemname}', dark=True)
|
||||
def gamesystem_statistic_page(systemname: str): # <--- WICHTIG: Hier fangen wir das Wort aus der URL auf!
|
||||
def gamesystem_statistic_page(systemname: str):
|
||||
|
||||
# Sicherheitscheck: Ist der User eingeloggt?
|
||||
if not app.storage.user.get('authenticated', False):
|
||||
ui.navigate.to('/')
|
||||
return
|
||||
|
|
@ -16,9 +14,23 @@ def setup_routes():
|
|||
gui_style.apply_design()
|
||||
|
||||
player_id = app.storage.user.get('db_id')
|
||||
stats = data_api.get_player_system_stats(player_id, systemname)
|
||||
all_stats = data_api.get_player_statistics(player_id)
|
||||
|
||||
# Passendes System anhand des Namens (case-insensitive) herausfiltern
|
||||
system_stat = next(
|
||||
(s for s in all_stats if s["gamesystem_name"].lower() == systemname.lower()),
|
||||
None
|
||||
)
|
||||
|
||||
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
|
||||
avv_points = system_stat.get("avv_points") or "-"
|
||||
last_played_raw = system_stat.get("last_played")
|
||||
last_played = str(last_played_raw)[:10] if last_played_raw else "-"
|
||||
|
||||
if stats:
|
||||
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/{systemname}'))
|
||||
|
|
@ -26,37 +38,29 @@ def setup_routes():
|
|||
with ui.column().classes('w-full items-center justify-center mt-10'):
|
||||
ui.label(f'Deine Statistik in {systemname}').classes('text-3xl justify-center font-bold text-normaltext')
|
||||
|
||||
# --- BLOCK 1 (2 Karten) ---
|
||||
# 1. Daten für die Rangliste holen
|
||||
# --- BLOCK 1 (MMR & Rang | Rangliste) ---
|
||||
leaderboard_data = data_api.get_leaderboard(systemname)
|
||||
|
||||
# 2. Tabelle vorbereiten und den EIGENEN Rang herausfinden
|
||||
table_rows = []
|
||||
my_rank = "-"
|
||||
|
||||
for index, player in enumerate(leaderboard_data):
|
||||
current_rank = index + 1
|
||||
|
||||
# Wenn wir in der Liste über uns selbst stolpern, merken wir uns den Rang für unsere Karte!
|
||||
if player['id'] == player_id:
|
||||
my_rank = current_rank
|
||||
|
||||
table_rows.append({
|
||||
'rank': current_rank,
|
||||
'trend': '➖', # Platzhalter für später
|
||||
'trend': '➖',
|
||||
'name': f"{player['display_name']} 'aka' {player['discord_name']}",
|
||||
'mmr': player['mmr']
|
||||
})
|
||||
|
||||
table_columns = [
|
||||
{'name': 'rank', 'label': '#', 'field': 'rank', 'align': 'left'},
|
||||
{'name': 'trend', 'label': 'Trend', 'field': 'trend', 'align': 'center'},
|
||||
{'name': 'name', 'label': 'Spieler', 'field': 'name', 'align': 'left'},
|
||||
{'name': 'mmr', 'label': 'MMR', 'field': 'mmr', 'align': 'left'}
|
||||
{'name': 'rank', 'label': '#', 'field': 'rank', 'align': 'left'},
|
||||
{'name': 'trend', 'label': 'Trend', 'field': 'trend', 'align': 'center'},
|
||||
{'name': 'name', 'label': 'Spieler', 'field': 'name', 'align': 'left'},
|
||||
{'name': 'mmr', 'label': 'MMR', 'field': 'mmr', 'align': 'left'},
|
||||
]
|
||||
|
||||
# --- BLOCK 1 (Links: MMR & Rang | Rechts: Rangliste) ---
|
||||
# lg:grid-cols-3 teilt den Bildschirm auf großen Monitoren in 3 gleich große unsichtbare Spalten
|
||||
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"):
|
||||
|
|
@ -64,30 +68,27 @@ def setup_routes():
|
|||
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(stats["mmr"])).classes('text-4xl font-bold text-accent')
|
||||
ui.label(str(mmr)).classes('text-4xl font-bold text-accent')
|
||||
|
||||
with ui.card().classes("w-full items-center justify-center text-center"):
|
||||
ui.label("Rang: ").classes('text-2xl font-bold')
|
||||
# Hier tragen wir jetzt unsere gefundene Platzierung ein!
|
||||
ui.label(str(my_rank)).classes('text-4xl font-bold text-blue-100')
|
||||
|
||||
# RECHTE SEITE (Belegt 2 Spalten -> lg:col-span-2)
|
||||
with ui.card().classes("w-full lg:col-span-2"):
|
||||
ui.label("Liga Rangliste").classes("text-xl font-bold text-white mb-2")
|
||||
ui.table(columns=table_columns, rows=table_rows, row_key='rank').classes('w-full bg-zinc-900 text-white')
|
||||
|
||||
|
||||
# --- 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(stats["games_in_system"])).classes('text-4xl font-bold text-blue-100')
|
||||
ui.label(str(games)).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(stats["avv_points"])).classes('text-4xl font-bold text-blue-100')
|
||||
ui.label(str(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')
|
||||
|
|
@ -95,8 +96,6 @@ def setup_routes():
|
|||
|
||||
with ui.card().classes("items-center justify-center text-center"):
|
||||
ui.label("Letztes Spiel am: ").classes('text-2xl font-bold')
|
||||
# Schneidet die Zeit vom Datum ab, falls eins existiert
|
||||
last_played = str(stats["last_played"])[:10] if stats["last_played"] else "-"
|
||||
ui.label(last_played).classes('text-4xl font-bold text-blue-100')
|
||||
|
||||
with ui.card().classes("items-center justify-center text-center"):
|
||||
|
|
@ -119,52 +118,6 @@ def setup_routes():
|
|||
ui.label("Dein 'Prügelknabe': ").classes('text-2xl font-bold')
|
||||
ui.label("-").classes('text-4xl font-bold text-blue-100')
|
||||
|
||||
|
||||
# --- BLOCK 4 (Diagramm der letzten 20 Spiele) ---
|
||||
with ui.card().classes("w-full items-center"):
|
||||
ui.label("Punkte in den letzten 20 Spielen").classes('text-2xl font-bold mb-4')
|
||||
|
||||
# 1. Daten aus der API holen
|
||||
chart_data = data_api.get_last_20_match_scores(player_id, systemname)
|
||||
|
||||
# 2. Nur zeichnen, wenn es überhaupt schon Spiele gibt!
|
||||
if len(chart_data['points']) > 0:
|
||||
ui.echart({
|
||||
'xAxis': {
|
||||
'type': 'category',
|
||||
'data': chart_data['labels'],
|
||||
'axisLabel': {'color': '#e5e7eb'}
|
||||
},
|
||||
'yAxis': {
|
||||
'type': 'value',
|
||||
'axisLabel': {'color': '#e5e7eb'},
|
||||
'splitLine': {'lineStyle': {'color': '#3f3f46'}}
|
||||
},
|
||||
'series': [
|
||||
{
|
||||
'name': 'Punkte', # Zeigt im Tooltip "Punkte: 76" statt einer unbenannten Zahl
|
||||
'type': 'line',
|
||||
'data': chart_data['points'],
|
||||
'smooth': True,
|
||||
'color': '#60a5fa'
|
||||
}
|
||||
],
|
||||
'tooltip': {
|
||||
'trigger': 'axis',
|
||||
'backgroundColor': '#27272a',
|
||||
'textStyle': {'color': '#ffffff'},
|
||||
'borderColor': '#3f3f46' # Ein feiner Rand für den Kasten
|
||||
}
|
||||
}).classes('w-full h-64')
|
||||
|
||||
|
||||
else:
|
||||
ui.label("Keine Statistiken für dieses System gefunden.").classes('text-red-500 mt-4')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
with ui.column().classes('w-full items-center justify-center mt-10'):
|
||||
ui.label(f'Keine Statistik für "{systemname}" gefunden.').classes('text-red-500 text-xl mt-4')
|
||||
|
|
|
|||
|
|
@ -128,6 +128,7 @@ def setup_routes(admin_discord_id):
|
|||
def acccept_match(m_id):
|
||||
ui.notify("Spiel akzeptiert. Wird Berechnet.")
|
||||
ui.navigate.reload() # Lädt die Seite neu, um die Karte zu aktualisieren
|
||||
data_api.confirm_match(m_id)
|
||||
calc_match.calculate_match(m_id)
|
||||
|
||||
with ui.row().classes('w-full items-center justify-between bg-zinc-900 p-3 rounded shadow-inner'):
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from wood import logger
|
|||
|
||||
|
||||
point_inflation = 0.7 # => entspricht %
|
||||
K_FACTOR = 40 # Die "Border" (Maximalpunkte) die ein Sieg gibt.
|
||||
K_FACTOR = 30 # 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.
|
||||
def calculate_match (match_id):
|
||||
|
|
@ -16,20 +16,19 @@ def calculate_match (match_id):
|
|||
print("Fehler: Match nicht gefunden!")
|
||||
return
|
||||
|
||||
data_api.confirm_match(match_id)
|
||||
|
||||
# Laden und aufdröseln der Match Daten
|
||||
p1_id = match_data['player1_id']
|
||||
p2_id = match_data['player2_id']
|
||||
p1_score = match_data['score_player1']
|
||||
p2_score = match_data['score_player2']
|
||||
sys_name = match_data['gamesystem_name']
|
||||
sys_id = match_data['gamesystem_id']
|
||||
system_name = match_data['gamesystem_name']
|
||||
system_id = match_data['gamesystem_id']
|
||||
|
||||
match_is_draw = False
|
||||
draw_diff = determine_draw_diff(system_id)
|
||||
|
||||
# ==========================================
|
||||
# 1. IDENTIFIKATION (Bleibt gleich)
|
||||
# 1. IDENTIFIKATION
|
||||
# ==========================================
|
||||
# Draw
|
||||
if -draw_diff <= (p1_score - p2_score) <= draw_diff:
|
||||
|
|
@ -52,49 +51,51 @@ def calculate_match (match_id):
|
|||
# Wir speichern die Ergebnisse direkt in einem temporären Dictionary,
|
||||
# und nutzen die SPIELER-ID als Schlüsselwort!
|
||||
elo_factor = calculation.calc_elo_factor(winner_id, looser_id, system_name)
|
||||
base_change = calculation.calc_base_change(elo_factor, match_is_draw)
|
||||
rust_factor = calculation.calc_rust_factor(winner_id, looser_id, gamesystem_id)
|
||||
base_change = int(calculation.calc_base_change(elo_factor, match_is_draw, K_FACTOR))
|
||||
rust_factor = calculation.calc_rust_factor(winner_id, looser_id, system_id)
|
||||
|
||||
|
||||
#winner
|
||||
w_base = base_change
|
||||
w_khorne = calculation.wrath_of_khorne(winner_id)
|
||||
w_base = int(base_change)
|
||||
w_khorne = int(calculation.wrath_of_khorne(winner_id, system_id))
|
||||
|
||||
#looser
|
||||
l_base = int(base_change*point_inflation)
|
||||
l_khorne = calculation.wrath_of_khorne(looser_id)
|
||||
|
||||
slaanesh = calculation.slaanesh_delight(),
|
||||
tzeentch = calculation.tzeentch_scemes(winner_score, looser_score),
|
||||
l_khorne = int(calculation.wrath_of_khorne(looser_id, system_id))
|
||||
|
||||
slaanesh = int(calculation.slaanesh_delight())
|
||||
tzeentch = int(calculation.tzeentch_scemes(winner_score, looser_score))
|
||||
|
||||
|
||||
# ==========================================
|
||||
# 3. Daten Verpacken
|
||||
# ==========================================
|
||||
calc_results = {
|
||||
"match_id" : match_id,
|
||||
"elo_factor" : elo_factor,
|
||||
"rust_factor" : rust_factor,
|
||||
"match_id" : match_id,
|
||||
"elo_factor" : elo_factor,
|
||||
"rust_factor" : rust_factor,
|
||||
"point_inflation" : point_inflation,
|
||||
|
||||
winner_id: {
|
||||
"base": w_base,
|
||||
"khorne": w_khorne,
|
||||
"slaanesh": slaanesh,
|
||||
"tzeentch": tzeentch,
|
||||
"total": int((w_base + w_khorne + slaanesh + tzeentch)*rust_factor),
|
||||
"winner_id" : winner_id, # <-- String-Key für save_calculated_match
|
||||
"looser_id" : looser_id, # <-- String-Key für save_calculated_match
|
||||
|
||||
winner_id: { # <-- Variable als Key (z.B. 42: {...})
|
||||
"base" : w_base,
|
||||
"khorne" : w_khorne,
|
||||
"slaanesh" : slaanesh,
|
||||
"tzeentch" : tzeentch,
|
||||
"total" : int((w_base + w_khorne + slaanesh + tzeentch) * rust_factor),
|
||||
},
|
||||
looser_id: {
|
||||
"base": l_base,
|
||||
"khorne": l_khorne,
|
||||
"slaanesh": -slaanesh,
|
||||
"tzeentch": calculation.tzeentch_scemes(winner_score, looser_score),
|
||||
"total": int((calc_results[looser_id]["base"] + calc_results[looser_id]["khorne"] - calc_results[looser_id]["slaanesh"] - calc_results[looser_id]["tzeentch"])*calc_results["rust_factor"]),
|
||||
looser_id: { # <-- Variable als Key (z.B. 7: {...})
|
||||
"base" : -l_base,
|
||||
"khorne" : l_khorne,
|
||||
"slaanesh" : -slaanesh,
|
||||
"tzeentch" : -tzeentch,
|
||||
"total" : int((-l_base + l_khorne - slaanesh - tzeentch) * rust_factor),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
data_api.save_calculated_match(calc_results)
|
||||
|
||||
|
||||
def determine_draw_diff(sys_id):
|
||||
|
|
@ -104,5 +105,4 @@ def determine_draw_diff(sys_id):
|
|||
draw_diff = 3
|
||||
case 2,3:
|
||||
draw_diff = 1
|
||||
|
||||
return draw_diff
|
||||
|
|
@ -5,7 +5,7 @@ from wood import logger
|
|||
# die meisten Spieler über 1000MMR sein. Sprich: Neueinsteiger, oder leute die weniger spielen sind eher im unteren Ende als in der Mitte.
|
||||
|
||||
|
||||
def calc_base_change(elo_factor, match_is_draw):
|
||||
def calc_base_change(elo_factor, match_is_draw, K_FACTOR):
|
||||
if match_is_draw:
|
||||
# Bei einem Draw (0.5) gewinnt der Schwächere leicht Punkte, der Stärkere verliert leicht.
|
||||
base_change = (K_FACTOR * (0.5 - elo_factor)/2)
|
||||
|
|
@ -13,19 +13,21 @@ def calc_base_change(elo_factor, match_is_draw):
|
|||
# Sieg (1.0). Gewinnt der Favorit, gibt es wenig Punkte. Gewinnt der Underdog, gibt es viele!
|
||||
base_change = K_FACTOR * (1.0 - elo_factor)
|
||||
|
||||
return base_change
|
||||
|
||||
|
||||
def calc_elo_factor(winner_id, looser_id, system_name):
|
||||
w_stats = data_api.get_player_statistics(winner_id)
|
||||
l_stats = data_api.get_player_statistics(looser_id)
|
||||
|
||||
# ------------------------------------------
|
||||
# 1. Die aktuellen MMR-Punkte holen
|
||||
w_stat = data_api.get_player_system_stats(winner_id, systemname)
|
||||
l_stat = data_api.get_player_system_stats(looser_id, systemname)
|
||||
# Passendes System aus der Liste herausfiltern
|
||||
w_stat = next((s for s in w_stats if s['gamesystem_name'] == system_name), None)
|
||||
l_stat = next((s for s in l_stats if s['gamesystem_name'] == system_name), None)
|
||||
|
||||
# Wenn ein Spieler noch keine keine Stats hat wird None zurück gegeben. Fallback für diesen Fall
|
||||
w_mmr = w_stat['mmr'] if w_stat and w_stat['mmr'] is not None else 1000
|
||||
l_mmr = l_stat['mmr'] if l_stat and l_stat['mmr'] is not None else 1000
|
||||
|
||||
return (1 / (1 + 10 ** ((l_mmr - w_mmr)/400)))
|
||||
return (1 / (1 + 10 ** ((l_mmr - w_mmr) / 400)))
|
||||
|
||||
|
||||
|
||||
|
|
@ -36,8 +38,7 @@ def calc_rust_factor(winner_id, looser_id, gamesystem_id):
|
|||
l_days = data_api.get_days_since_last_system_game(looser_id, gamesystem_id)
|
||||
|
||||
# Der größeren der beiden Werte wird verwendet.
|
||||
days = max(w_days, l_days)
|
||||
rust_factor = get_rust_dampener(days)
|
||||
days_ago = max(w_days, l_days)
|
||||
|
||||
"""Berechnet den Dämpfungsfaktor basierend auf den vergangen Tagen."""
|
||||
if days_ago <= 30:
|
||||
|
|
@ -51,20 +52,20 @@ def calc_rust_factor(winner_id, looser_id, gamesystem_id):
|
|||
return round(factor, 2)
|
||||
|
||||
|
||||
def wrath_of_khorne(player_id):
|
||||
def wrath_of_khorne(player_id, system_id):
|
||||
# -----------------
|
||||
khorne_days = 16
|
||||
khorne_bonus = 8
|
||||
# -----------------
|
||||
|
||||
last_played = data_api.get_days_since_last_game(player_id)["days_ago"]
|
||||
last_played = int(data_api.get_days_since_last_system_game(player_id, system_id))
|
||||
if last_played <= khorne_days:
|
||||
return khorne_bonus
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def slaanesh_delight():
|
||||
return 0
|
||||
|
||||
def tzeentch_scemes(winner_score, looser_score):
|
||||
return 0
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user