Kleine Anpassung des Bereichs ab wann ein Match ein Draw ist. Einfügen dass matches von player2 bestätigt werden müssen bevor sie berechnet und der Rangliste hinzugefügt wird.

This commit is contained in:
Daniel Nagel 2026-03-06 21:20:48 +01:00
parent 380f91957d
commit 0ed86d6130
11 changed files with 150 additions and 20 deletions

View File

@ -1 +1 @@
{"authenticated":true,"discord_id":"277898241750859776","discord_name":"mrteels","db_id":1,"display_name":"Zorniger 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":"r","discord_avatar_url":"https://cdn.discordapp.com/avatars/277898241750859776/7c3446bb51fafd72b1b4c21124b4994f.png"}

View File

@ -475,3 +475,64 @@ def get_leaderboard(system_name):
result.append(dict(row)) result.append(dict(row))
return result return result
def get_unconfirmed_matches(player_id):
"""Holt alle offenen Matches, die der Spieler noch bestätigen muss."""
connection = sqlite3.connect(DB_PATH)
connection.row_factory = sqlite3.Row
cursor = connection.cursor()
# Wir joinen players (für den Namen des Gegners) und gamesystems (für den Systemnamen)
query = """
SELECT m.id AS match_id, m.score_player1, m.score_player2, m.played_at,
sys.name AS system_name,
p1.display_name AS p1_name
FROM matches m
JOIN gamesystems sys ON m.gamesystem_id = sys.id
JOIN players p1 ON m.player1_id = p1.id
WHERE m.player2_id = ? AND m.player2_check = 0
"""
cursor.execute(query, (player_id,))
rows = cursor.fetchall()
connection.close()
# Wir geben eine Liste mit Dictionaries zurück
return [dict(row) for row in rows]
def delete_match(match_id):
"""Löscht ein Match anhand seiner ID komplett aus der Datenbank."""
connection = sqlite3.connect(DB_PATH)
cursor = connection.cursor()
# DELETE FROM löscht die gesamte Zeile, bei der die ID übereinstimmt.
cursor.execute("DELETE FROM matches WHERE id = ?", (match_id,))
connection.commit()
connection.close()
def confirm_match(match_id):
connection = sqlite3.connect(DB_PATH)
cursor = connection.cursor()
# Ändert nur die Spalte player2_check auf 1 (True)
cursor.execute("UPDATE matches SET player2_check = 1 WHERE id = ?", (match_id,))
connection.commit()
connection.close()
def set_match_counted(match_id):
"""Setzt den Haken (1), dass das Match erfolgreich in die MMR eingeflossen ist."""
connection = sqlite3.connect(DB_PATH)
cursor = connection.cursor()
# Ändert nur die Spalte match_is_counted auf 1 (True)
cursor.execute("UPDATE matches SET match_is_counted = 1 WHERE id = ?", (match_id,))
connection.commit()
connection.close()

View File

@ -2,8 +2,12 @@ import sqlite3
import os import os
import json import json
dummy_is_in = False
DB_PATH = "data/warhammer_league.db" # 1. Sucht den exakten, absoluten Pfad zu diesem Skript (z.B. /srv/Diceghost-Liga-System/data/)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# 2. Klebt den Datenbank-Namen an diesen Ordner
DB_PATH = os.path.join(BASE_DIR, "league_database.db")
def init_db(): def init_db():
connection = sqlite3.connect(DB_PATH) connection = sqlite3.connect(DB_PATH)
@ -64,6 +68,8 @@ def init_db():
played_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, played_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
player1_mmr_change INTEGER, player1_mmr_change INTEGER,
player2_mmr_change INTEGER, player2_mmr_change INTEGER,
player2_check INTEGER DEFAULT 0,
match_is_counted INTEGER DEFAULT 0,
FOREIGN KEY (gamesystem_id) REFERENCES gamesystems (id), FOREIGN KEY (gamesystem_id) REFERENCES gamesystems (id),
FOREIGN KEY (player1_id) REFERENCES players (id), FOREIGN KEY (player1_id) REFERENCES players (id),
FOREIGN KEY (player2_id) REFERENCES players (id) FOREIGN KEY (player2_id) REFERENCES players (id)
@ -141,7 +147,9 @@ def seed_achievements():
connection.commit() connection.commit()
connection.close() connection.close()
print("Achievements angelegt.") print("Achievements angelegt.")
#seed_dummy_player()
if dummy_is_in:
seed_dummy_player()
def seed_dummy_player(): def seed_dummy_player():
@ -215,7 +223,7 @@ def generate_default_mmr_rules():
# Das ist unsere Standard-Vorlage (Faktor 10) # Das ist unsere Standard-Vorlage (Faktor 10)
default_rules = { default_rules = {
"system_info": f"Balancing für {sys_name}", "system_info": f"Balancing für {sys_name}",
"draw_point_difference": 5, "draw_point_difference": 3,
"rank_matrix": { "rank_matrix": {
"10": {"win": 10, "draw": 30}, "10": {"win": 10, "draw": 30},
"9": {"win": 10, "draw": 30}, "9": {"win": 10, "draw": 30},

8
gui/admin_gui.py Normal file
View File

@ -0,0 +1,8 @@
from nicegui import ui, app
from data import database, data_api
from gui import gui_style
def setup_routes():
@ui.page('/admin')
def home_page():
gui_style.apply_design()

View File

@ -1,8 +1,9 @@
from nicegui import ui, app from nicegui import ui, app
from data import database, data_api from data import database, data_api
from gui import discord_login, gui_style from gui import discord_login, gui_style
from match_calculations import calc_match
def setup_routes(): def setup_routes(admin_discord_id):
@ui.page('/') @ui.page('/')
def home_page(): def home_page():
gui_style.apply_design() gui_style.apply_design()
@ -19,9 +20,15 @@ def setup_routes():
ui.image("gui/pictures/wsdg.png").classes('w-20 h-20 rounded-full') ui.image("gui/pictures/wsdg.png").classes('w-20 h-20 rounded-full')
ui.label('Diceghost Liga').classes('text-2xl font-bold text-white') ui.label('Diceghost Liga').classes('text-2xl font-bold text-white')
# --- RECHTE SEITE ---
discord_id = app.storage.user.get("discord_id")
if discord_id == admin_discord_id:
ui.button('Admin Panel', on_click=lambda: ui.navigate.to('/admin'))
# --- RECHTE SEITE --- # --- RECHTE SEITE ---
if app.storage.user.get('authenticated', False): if app.storage.user.get('authenticated', False):
with ui.row().classes('items-center gap-4'): with ui.row().classes('items-center gap-4'):
discord_name = app.storage.user.get('discord_name') discord_name = app.storage.user.get('discord_name')
display_name = app.storage.user.get('display_name') display_name = app.storage.user.get('display_name')
player_id = app.storage.user.get('db_id') player_id = app.storage.user.get('db_id')
@ -77,6 +84,49 @@ def setup_routes():
ui.button('Login with Discord', on_click=lambda: ui.navigate.to(auth_url)) ui.button('Login with Discord', on_click=lambda: ui.navigate.to(auth_url))
# ---------------------------
# --- Match Bestätigung ---
# ---------------------------
# --- Bestätigungs-Bereich für offene Spiele --- Der "Marian Balken !!!1!11!"
if app.storage.user.get('authenticated', False):
unconfirmed_matches = data_api.get_unconfirmed_matches(player_id)
if len(unconfirmed_matches) > 0:
# Eine auffällige, rote Karte über die volle Breite
with ui.card().classes('w-full bg-red-900/80 border-2 border-red-500 mb-6'):
ui.label(f"Aktion erforderlich: Du hast {len(unconfirmed_matches)} unbestätigte(s) Spiel(e)!").classes('text-2xl font-bold text-white mb-2')
for match in unconfirmed_matches:
# --- NEU: Die Funktion, die beim Klick ausgeführt wird ---
def reject_match(m_id):
data_api.delete_match(m_id)
ui.notify("Spiel abgelehnt und gelöscht!", color="warning")
ui.navigate.reload() # Lädt die Seite neu, um die Karte zu aktualisieren
def acccept_match(m_id):
ui.notify("Spiel akzeptiert. Wird Berechnet.")
ui.navigate.reload() # Lädt die Seite neu, um die Karte zu aktualisieren
calc_match.calculate_match(m_id)
# Für jedes Match machen wir eine kleine Reihe
with ui.row().classes('w-full items-center justify-between bg-zinc-900 p-3 rounded shadow-inner'):
# Info-Text: Was wurde eingetragen?
info_text = f"[{match['system_name']}] {match['p1_name']} behauptet: Er hat {match['score_player1']} : {match['score_player2']} gegen dich gespielt."
ui.label(info_text).classes('text-lg text-gray-200')
# Die Buttons (Funktion machen wir im nächsten Schritt!)
with ui.row().classes('gap-2'):
# ABLEHNEN und Spiel löschen
ui.button("Ablehnen", color="negative", icon="close", on_click=lambda e, m_id=match['match_id']: reject_match(m_id))
ui.space()
# BESTÄTIGEN und spiel berechnen lassen
ui.button("Bestätigen", color="positive", icon="check", on_click=lambda e, m_id=match['match_id']: acccept_match(m_id))
# --------------------------- # ---------------------------
# --- Spielsysteme --- # --- Spielsysteme ---
# --------------------------- # ---------------------------

View File

@ -69,8 +69,7 @@ def setup_routes():
# 4. Erfolgsmeldung und Berechnung # 4. Erfolgsmeldung und Berechnung
ui.notify("Match erfolgreich eingetragen!", color="green") ui.notify("Match erfolgreich eingetragen!", color="green")
calc_match.calculate_inserted_match(systemname, match_id) ui.navigate.to(f'/statistic/{systemname}')
ui.navigate.to('/')
# 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"):

13
main.py
View File

@ -2,7 +2,7 @@ import os
from dotenv import load_dotenv from dotenv import load_dotenv
from nicegui import ui from nicegui import ui
from gui import main_gui, match_gui, discord_login, league_statistic from gui import main_gui, match_gui, discord_login, league_statistic, admin_gui
from data import database from data import database
@ -10,18 +10,19 @@ from data import database
# 1. Lade die geheimen Variablen aus der .env Datei in den Speicher # 1. Lade die geheimen Variablen aus der .env Datei in den Speicher
load_dotenv() load_dotenv()
database.check_db()
# ----------------------
# 2. Variablen abrufen # 2. Variablen abrufen
client_id = os.getenv("DISCORD_CLIENT_ID") client_id = os.getenv("DISCORD_CLIENT_ID")
client_secret = os.getenv("DISCORD_CLIENT_SECRET") client_secret = os.getenv("DISCORD_CLIENT_SECRET")
admin_discord_id = os.getenv("ADMIN")
database.check_db()
# 3. Seitenrouten aufbauen # 3. Seitenrouten aufbauen
main_gui.setup_routes() main_gui.setup_routes(admin_discord_id)
discord_login.setup_login_routes() discord_login.setup_login_routes()
league_statistic.setup_routes() league_statistic.setup_routes()
match_gui.setup_routes() match_gui.setup_routes()
admin_gui.setup_routes()
# 4. Wir starten die NiceGUI App # 4. Wir starten die NiceGUI App
ui.run(title="Warhammer Liga", port=9000, storage_secret="ein_sehr_geheimes_passwort_fuer_die_cookies") ui.run(title="Westside Diceghost Liga", port=9000, storage_secret="ein_sehr_geheimes_passwort_fuer_die_cookies")

View File

@ -4,11 +4,15 @@ import json
import os import os
# 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.
def calculate_inserted_match (gamesystem, match_id): def calculate_match (match_id):
match_data = data_api.get_match_by_id(match_id) match_data = data_api.get_match_by_id(match_id)
if not match_data: if not match_data:
print("Fehler: Match nicht gefunden!") print("Fehler: Match nicht gefunden!")
return return
data_api.confirm_match(match_id)
# Laden und umsetzen der Match Daten # Laden und umsetzen der Match Daten
p1_id = match_data['player1_id'] p1_id = match_data['player1_id']
p2_id = match_data['player2_id'] p2_id = match_data['player2_id']
@ -60,6 +64,7 @@ def calculate_inserted_match (gamesystem, match_id):
data_api.apply_match_to_player_statistic (winner_id, sys_id, mmr_change_winner, winner_score) data_api.apply_match_to_player_statistic (winner_id, sys_id, mmr_change_winner, winner_score)
data_api.apply_match_to_player_statistic (looser_id, sys_id, mmr_change_looser, looser_score) data_api.apply_match_to_player_statistic (looser_id, sys_id, mmr_change_looser, looser_score)
data_api.set_match_counted(match_id)

View File

@ -1,6 +1,6 @@
{ {
"system_info": "Balancing für Spearhead", "system_info": "Balancing für Spearhead",
"draw_point_difference": 5, "draw_point_difference": 3,
"rank_matrix": { "rank_matrix": {
"10": { "10": {
"win": 10, "win": 10,
@ -109,5 +109,4 @@
"bonus": 0 "bonus": 0
} }
] ]
} }

View File

@ -1,6 +1,6 @@
{ {
"system_info": "Balancing für Warhammer 40k", "system_info": "Balancing für Warhammer 40k",
"draw_point_difference": 5, "draw_point_difference": 3,
"rank_matrix": { "rank_matrix": {
"10": { "10": {
"win": 10, "win": 10,

View File

@ -1,6 +1,6 @@
{ {
"system_info": "Balancing für Warhammer Age of Sigmar", "system_info": "Balancing für Warhammer Age of Sigmar",
"draw_point_difference": 5, "draw_point_difference": 3,
"rank_matrix": { "rank_matrix": {
"10": { "10": {
"win": 10, "win": 10,
@ -109,5 +109,4 @@
"bonus": 0 "bonus": 0
} }
] ]
} }