dev_gui #34
|
|
@ -190,7 +190,7 @@ def get_player_statistic(player_id, system_id):
|
|||
SELECT
|
||||
sys.id AS gamesystem_id,
|
||||
sys.name AS gamesystem_name,
|
||||
sys.*,
|
||||
sys.description,
|
||||
stat.mmr,
|
||||
stat.games_in_system,
|
||||
stat.points,
|
||||
|
|
@ -206,6 +206,7 @@ def get_player_statistic(player_id, system_id):
|
|||
ON sys.id = stat.gamesystem_id AND stat.player_id = %s
|
||||
WHERE sys.id = %s
|
||||
"""
|
||||
|
||||
cursor.execute(query, (player_id, system_id))
|
||||
row = cursor.fetchone()
|
||||
|
||||
|
|
@ -214,6 +215,33 @@ def get_player_statistic(player_id, system_id):
|
|||
return row
|
||||
|
||||
|
||||
def get_player_rank(player_id, system_id):
|
||||
"""Holt den Rang eines Spielers in einem System basierend auf MMR (absteigend)."""
|
||||
connection = db_connection()
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
query = """
|
||||
SELECT COUNT(*) + 1 AS rank
|
||||
FROM player_game_statistic stat
|
||||
WHERE stat.gamesystem_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
|
||||
)
|
||||
"""
|
||||
cursor.execute(query, (system_id, player_id, system_id))
|
||||
row = cursor.fetchone()
|
||||
|
||||
cursor.close()
|
||||
connection.close()
|
||||
|
||||
if row and row['rank'] is not None:
|
||||
return row['rank']
|
||||
return None
|
||||
|
||||
def join_league(player_id, gamesystem_id):
|
||||
connection = db_connection()
|
||||
cursor = connection.cursor()
|
||||
|
|
@ -407,19 +435,21 @@ def get_gamesystems_data():
|
|||
return rows
|
||||
|
||||
|
||||
|
||||
def get_leaderboard(system_name):
|
||||
"""Holt alle Spieler eines Systems sortiert nach MMR für die Rangliste."""
|
||||
connection = db_connection()
|
||||
cursor = connection.cursor(dictionary=True)
|
||||
|
||||
query = """
|
||||
SELECT p.id, p.display_name, p.discord_name, stat.mmr
|
||||
FROM players p
|
||||
JOIN player_game_statistic stat ON p.id = stat.player_id
|
||||
JOIN gamesystems sys ON stat.gamesystem_id = sys.id
|
||||
WHERE sys.name = %s AND stat.games_in_system > 0
|
||||
ORDER BY stat.mmr DESC
|
||||
"""
|
||||
query = query = """
|
||||
SELECT p.id, p.display_name, p.discord_name, p.discord_id, p.discord_avatar_url,
|
||||
stat.mmr, stat.games_in_system, stat.wins, stat.loss, stat.avv_points
|
||||
FROM players p
|
||||
JOIN player_game_statistic stat ON p.id = stat.player_id
|
||||
JOIN gamesystems sys ON stat.gamesystem_id = sys.id
|
||||
WHERE sys.name = %s AND stat.games_in_system > 0
|
||||
ORDER BY stat.mmr DESC
|
||||
"""
|
||||
cursor.execute(query, (system_name,))
|
||||
rows = cursor.fetchall()
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@ from nicegui import ui, app
|
|||
from gui import gui_style
|
||||
from data import data_api
|
||||
from gui.info_text import info_system
|
||||
from gui.templates.leaderboard import Leaderboard
|
||||
|
||||
def setup_routes():
|
||||
@ui.page('/statistic/{systemname}', dark=True)
|
||||
def gamesystem_statistic_page(systemname: str):
|
||||
@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('/')
|
||||
|
|
@ -14,112 +15,77 @@ def setup_routes():
|
|||
gui_style.apply_design()
|
||||
|
||||
player_id = app.storage.user.get('db_id')
|
||||
all_stats = data_api.get_player_statistics(player_id)
|
||||
system_id = data_api.get_gamesystem_id_by_name(system_name)
|
||||
print(player_id, system_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
|
||||
)
|
||||
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}'))
|
||||
|
||||
if system_stat:
|
||||
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 "-"
|
||||
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')
|
||||
|
||||
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}'))
|
||||
# --- 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.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')
|
||||
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')
|
||||
|
||||
# --- BLOCK 1 (MMR & Rang | Rangliste) ---
|
||||
leaderboard_data = data_api.get_leaderboard(systemname)
|
||||
table_rows = []
|
||||
my_rank = "-"
|
||||
|
||||
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)
|
||||
|
||||
for index, player in enumerate(leaderboard_data):
|
||||
current_rank = index + 1
|
||||
if player['id'] == player_id:
|
||||
my_rank = current_rank
|
||||
table_rows.append({
|
||||
'rank': current_rank,
|
||||
'trend': '➖',
|
||||
'name': f"{player['display_name']} 'aka' {player['discord_name']}",
|
||||
'mmr': player['mmr']
|
||||
})
|
||||
# --- 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"):
|
||||
|
||||
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'},
|
||||
]
|
||||
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.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(mmr)).classes('text-4xl font-bold text-accent')
|
||||
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("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(my_rank)).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("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')
|
||||
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')
|
||||
|
||||
# --- 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("Win-Streak: ").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("Spiele: ").classes('text-2xl font-bold')
|
||||
ui.label(str(games)).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("Ø Punkte pro Spiel: ").classes('text-2xl font-bold')
|
||||
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("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("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("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("Letztes Spiel am: ").classes('text-2xl font-bold')
|
||||
ui.label(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("-").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("-").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("-").classes('text-4xl font-bold text-blue-100')
|
||||
|
||||
else:
|
||||
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')
|
||||
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')
|
||||
|
|
@ -3,12 +3,22 @@ from gui import gui_style
|
|||
from data import data_api
|
||||
from gui.info_text import info_system
|
||||
|
||||
from gui.templates.leaderboard import Leaderboard
|
||||
|
||||
|
||||
|
||||
def setup_routes():
|
||||
@ui.page('/statistic/visitor/{systemname}', dark=True)
|
||||
def gamesystem_statistic_visitor_page(systemname: str):
|
||||
|
||||
if not app.storage.user.get('authenticated', False):
|
||||
ui.navigate.to('/')
|
||||
return
|
||||
|
||||
gui_style.apply_design()
|
||||
gui_style.apply_design()
|
||||
|
||||
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")
|
||||
|
||||
with ui.column().classes('w-full items-center justify-center mt-10'):
|
||||
ui.label(f'Die aktuelle Statistik in {systemname}').classes('text-3xl justify-center font-bold text-normaltext')
|
||||
Leaderboard(systemname)
|
||||
|
|
@ -31,19 +31,19 @@ def setup_routes(admin_discord_id):
|
|||
# --- LINKE SEITE ---
|
||||
# Vereinslogo und den Titel in einer eigenen Reihe (Reihe 1)
|
||||
with ui.row().classes('items-center'):
|
||||
ui.image("gui/pictures/wsdg.png").classes('w-15 h-15 rounded-full')
|
||||
ui.label('Diceghost Liga').classes('text-2xl font-bold text-normaltext')
|
||||
ui.image("gui/pictures/wsdg.png").classes('w-12 h-12 md:w-25 md:h-25 rounded-full')
|
||||
ui.label('Westside Diceghost Liga').classes('text-lg md:text-3xl font-bold text-normaltext')
|
||||
|
||||
# --- MITTE ---
|
||||
if app.storage.user.get('authenticated', False):
|
||||
discord_id = app.storage.user.get("discord_id")
|
||||
if discord_id == admin_discord_id:
|
||||
ui.button(icon="hardware", on_click=lambda: ui.navigate.to('/admin')).props("round")
|
||||
ui.button(icon="hardware", on_click=lambda: ui.navigate.to('/admin')).classes('w-2 h-2')
|
||||
|
||||
# --- RECHTE SEITE ---
|
||||
if app.storage.user.get('authenticated', False):
|
||||
with ui.row().classes('items-center gap-4'):
|
||||
ui.image(app.storage.user.get('discord_avatar_url')).classes('w-15 h-15 rounded-full')
|
||||
ui.image(app.storage.user.get('discord_avatar_url')).classes('w-10 h-10 md:w-25 md:h-25 rounded-full border-1 border-red-900')
|
||||
discord_name = app.storage.user.get('discord_name')
|
||||
display_name = app.storage.user.get('display_name')
|
||||
player_id = app.storage.user.get('db_id')
|
||||
|
|
@ -57,10 +57,10 @@ def setup_routes(admin_discord_id):
|
|||
# --- ANSICHT 1: Der normale Text mit Edit-Button ---
|
||||
with ui.column().classes('items-center gap-0') as display_row:
|
||||
with ui.column():
|
||||
ui.label(display_name).classes('text-xl font-bold text-normaltext')
|
||||
ui.label(display_name).classes('md:text-3xl font-bold text-normaltext')
|
||||
with ui.row().classes("items-center justify-between"):
|
||||
ui.label("'aka'").classes('text-sm text-italic text-infotext')
|
||||
ui.label(discord_name).classes('text-m text-bold text-infotext')
|
||||
ui.label("'aka'").classes('md:text-lg text-xs text-italic text-infotext')
|
||||
ui.label(discord_name).classes('text-sm md:text-xl text-bold text-infotext')
|
||||
edit_button = ui.button(icon='edit', color='accent', on_click=toggle_edit_mode).props('round dense')
|
||||
|
||||
# --- ANSICHT 2: Das Eingabefeld (startet unsichtbar!) ---
|
||||
|
|
@ -213,6 +213,8 @@ def setup_routes(admin_discord_id):
|
|||
# Prüfen: Ist diese sys_id in den Stats des Spielers? UND hat er ein MMR?
|
||||
if sys_id not in my_stats or my_stats[sys_id]['mmr'] is None:
|
||||
ui.label(text="Du bist noch nicht in dieser Liga.").classes("text-red-500 font-bold")
|
||||
sys_card.classes("cursor-pointer hover:bg-zinc-800")
|
||||
sys_card.on('click', lambda e, name=sys_name: ui.navigate.to(f'/statistic/visitor/{name}'))
|
||||
|
||||
join_row = ui.row().classes('items-center gap-2')
|
||||
confirm_row = ui.row().classes('items-center gap-2')
|
||||
|
|
|
|||
24
gui/templates/leaderboard.py
Normal file
24
gui/templates/leaderboard.py
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
from nicegui import ui
|
||||
from data import data_api
|
||||
from gui.templates.player_card import PlayerCard
|
||||
|
||||
class Leaderboard:
|
||||
def __init__(self, system_name: str):
|
||||
# Daten holen
|
||||
leaderboard_data = data_api.get_leaderboard(system_name)
|
||||
|
||||
# Container
|
||||
with ui.column().classes('w-full gap-2') as self.container:
|
||||
for index, player in enumerate(leaderboard_data):
|
||||
PlayerCard(
|
||||
rank=index + 1,
|
||||
display_name=player['display_name'],
|
||||
discord_name=player['discord_name'],
|
||||
mmr=player['mmr'],
|
||||
discord_avatar_url=player.get('discord_avatar_url'),
|
||||
discord_id=player.get('discord_id'),
|
||||
games_in_system=player.get('games_in_system', 0),
|
||||
wins=player.get('wins', 0),
|
||||
loss=player.get('loss', 0),
|
||||
avv_points=player.get('avv_points', 0)
|
||||
)
|
||||
41
gui/templates/player_card.py
Normal file
41
gui/templates/player_card.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
from nicegui import ui
|
||||
|
||||
class PlayerCard:
|
||||
def __init__(self, rank: int, display_name: str, discord_name: str, mmr: int,
|
||||
discord_avatar_url: str = None, discord_id: str = None,
|
||||
games_in_system: int = 0, wins: int = 0, loss: int = 0, avv_points: int = 0):
|
||||
|
||||
# Rang links, Card rechts
|
||||
with ui.row().classes('w-full items-center gap-1 flex-nowrap'):
|
||||
ui.label(f'#{rank}').classes('text-2xl font-bold w-10 text-center shrink-0')
|
||||
|
||||
# Player Card
|
||||
with ui.card().classes("flex-1 no-shadow bg-zinc-900 min-w-0 overflow-hidden"):
|
||||
# Discord Block - klickbar
|
||||
if discord_id:
|
||||
with ui.link(target=f'https://discord.com/users/{discord_id}', new_tab=True).classes('no-underline w-full'):
|
||||
self._discord_content(discord_avatar_url, display_name, discord_name)
|
||||
else:
|
||||
self._discord_content(discord_avatar_url, display_name, discord_name)
|
||||
|
||||
# Stats und Achievments
|
||||
with ui.row().classes('w-full items-center flex-nowrap'):
|
||||
with ui.row().classes('flex-2 items-center gap-2 md:gap-3 md:justify-center shrink min-w-0'):
|
||||
ui.label(f'{mmr} MMR').classes('font-bold md:text-lg')
|
||||
with ui.row().classes('flex-1 justify-end items-center gap-1 md:gap-3 shrink-0'):
|
||||
ui.image('gui/pictures/achievments/AchievementIcon_4_1Sieg.png').classes('w-5 md:w-15')
|
||||
ui.image('gui/pictures/achievments/AchievementIcon_4_1Sieg.png').classes('w-5 md:w-15')
|
||||
ui.image('gui/pictures/achievments/AchievementIcon_4_1Sieg.png').classes('w-5 md:w-15')
|
||||
|
||||
|
||||
def _discord_content(self, avatar_url, display_name, discord_name):
|
||||
short_name = display_name[:15] + '...' if len(display_name) > 15 else display_name
|
||||
short_discord = discord_name[:15] + '...' if len(discord_name) > 15 else discord_name
|
||||
|
||||
with ui.card().classes("w-full md:w-90 bg-indigo-950 no-shadow"):
|
||||
with ui.row().classes('items-center gap-3 min-w-0'):
|
||||
if avatar_url:
|
||||
ui.image(avatar_url).classes('w-10 h-10 md:w-20 md:h-20 rounded-full border-red-900 border-2 shrink-0')
|
||||
with ui.column().classes('gap-0 min-w-0'):
|
||||
ui.label(short_name).classes('text-sm md:text-2xl font-bold whitespace-nowrap truncate')
|
||||
ui.label(short_discord).classes('text-xs md:text-sm opacity-60 whitespace-nowrap truncate')
|
||||
Loading…
Reference in New Issue
Block a user