diff --git a/.gitignore b/.gitignore index f460b62..fed9bdf 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ env/ # 5. IDE / Editor Einstellungen .vscode/ .idea/ + +data_trans.py \ No newline at end of file diff --git a/data/data_api.py b/data/data_api.py index c4badca..cb88fb5 100644 --- a/data/data_api.py +++ b/data/data_api.py @@ -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() diff --git a/gui/league_statistic_gui.py b/gui/league_statistic_gui.py index 3a76d03..00280e4 100644 --- a/gui/league_statistic_gui.py +++ b/gui/league_statistic_gui.py @@ -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') \ No newline at end of file diff --git a/gui/league_statistic_visitor_gui.py b/gui/league_statistic_visitor_gui.py new file mode 100644 index 0000000..5db40c8 --- /dev/null +++ b/gui/league_statistic_visitor_gui.py @@ -0,0 +1,24 @@ +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/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() + + 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) \ No newline at end of file diff --git a/gui/main_gui.py b/gui/main_gui.py index b088ee9..3f689fb 100644 --- a/gui/main_gui.py +++ b/gui/main_gui.py @@ -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') diff --git a/gui/match_gui.py b/gui/match_gui.py index 0873928..3446af5 100644 --- a/gui/match_gui.py +++ b/gui/match_gui.py @@ -33,19 +33,12 @@ def setup_routes(): raw_players = data_api.get_all_players_from_system(system_name) 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.column().classes("items-center justify-between"): + p1_points = ui.number(placeholder="Punkte eingeben", + min=system_data["min_score"], + max=system_data["max_score"], + precision=0 + ).classes("w-35").props("clearable") with ui.card().classes('w-full max-w-md mx-auto items-center mt-10 p-6'): dropdown_options = {} @@ -56,36 +49,39 @@ def setup_routes(): opponent_select = ui.select(options=dropdown_options, label='Gegner auswählen').classes('w-full') - 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) + with ui.column().classes("items-center justify-between"): + p2_points = ui.number(placeholder="Punkte eingeben", + min=system_data["min_score"], + max=system_data["max_score"], + precision=0 + ).classes("w-35").props("clearable") - # Das Match in die Datenbank eintragen lassen und die MMR Berechnung triggern. + # Das Match in die Datenbank eintragen lassen. def input_match_to_database(): p2_id = opponent_select.value + score_p1 = p1_points.value + score_p2 = p2_points.value + + + # Check der Eingegebenen Daten. + # Fehler wenn kein Spieler oder keine Punkte eingegeben werden. Das Punkte Eingabefeld fängt mit min_score und max_score ab, + # ob für das Spielsystem legale Punkte eingetragen wurden. if p2_id is None: ui.notify("Bitte wähle zuerst einen Gegner aus!", color="red", position="top") return - score_p1 = p1_points.value - score_p2 = p2_points.value + if score_p1 is None: + ui.notify("Ungültige Punkteeingabe Spieler 1!", color="red", position="top") + return + if score_p2 is None: + ui.notify("Ungültige Punkteeingabe Spieler 2!", color="red", position="top") + return + match_id = data_api.add_new_match(system_name, my_id, p2_id, score_p1, score_p2) - # 4. Erfolgsmeldung und Berechnung - ui.notify("Match erfolgreich eingetragen!", color="green") ui.navigate.to(f'/statistic/{system_name}') # Buttons ganz unten in einer Reihe diff --git a/gui/pictures/achievments/AchievementIcon_128_37.png b/gui/pictures/achievments/AchievementIcon_10_King1.png similarity index 100% rename from gui/pictures/achievments/AchievementIcon_128_37.png rename to gui/pictures/achievments/AchievementIcon_10_King1.png diff --git a/gui/pictures/achievments/AchievementIcon_128_38.png b/gui/pictures/achievments/AchievementIcon_11_King2.png similarity index 100% rename from gui/pictures/achievments/AchievementIcon_128_38.png rename to gui/pictures/achievments/AchievementIcon_11_King2.png diff --git a/gui/pictures/achievments/AchievementIcon_128_01.png b/gui/pictures/achievments/AchievementIcon_128_01.png index 4c63aa4..6486a5b 100644 Binary files a/gui/pictures/achievments/AchievementIcon_128_01.png and b/gui/pictures/achievments/AchievementIcon_128_01.png differ diff --git a/gui/pictures/achievments/AchievementIcon_128_02.png b/gui/pictures/achievments/AchievementIcon_128_02.png index bbd541c..0ddbef6 100644 Binary files a/gui/pictures/achievments/AchievementIcon_128_02.png and b/gui/pictures/achievments/AchievementIcon_128_02.png differ diff --git a/gui/pictures/achievments/AchievementIcon_128_03.png b/gui/pictures/achievments/AchievementIcon_128_03.png index b53a654..b4552eb 100644 Binary files a/gui/pictures/achievments/AchievementIcon_128_03.png and b/gui/pictures/achievments/AchievementIcon_128_03.png differ diff --git a/gui/pictures/achievments/AchievementIcon_128_43.png b/gui/pictures/achievments/AchievementIcon_128_43.png deleted file mode 100644 index 6486a5b..0000000 Binary files a/gui/pictures/achievments/AchievementIcon_128_43.png and /dev/null differ diff --git a/gui/pictures/achievments/AchievementIcon_128_44.png b/gui/pictures/achievments/AchievementIcon_128_44.png deleted file mode 100644 index 0ddbef6..0000000 Binary files a/gui/pictures/achievments/AchievementIcon_128_44.png and /dev/null differ diff --git a/gui/pictures/achievments/AchievementIcon_128_45.png b/gui/pictures/achievments/AchievementIcon_128_45.png deleted file mode 100644 index b4552eb..0000000 Binary files a/gui/pictures/achievments/AchievementIcon_128_45.png and /dev/null differ diff --git a/gui/pictures/achievments/AchievementIcon_128_39.png b/gui/pictures/achievments/AchievementIcon_12_King3.png similarity index 100% rename from gui/pictures/achievments/AchievementIcon_128_39.png rename to gui/pictures/achievments/AchievementIcon_12_King3.png diff --git a/gui/pictures/achievments/AchievementIcon_128_49.png b/gui/pictures/achievments/AchievementIcon_1_EinAnfang.png similarity index 100% rename from gui/pictures/achievments/AchievementIcon_128_49.png rename to gui/pictures/achievments/AchievementIcon_1_EinAnfang.png diff --git a/gui/pictures/achievments/AchievementIcon_128_50.png b/gui/pictures/achievments/AchievementIcon_2_Erprobt.png similarity index 100% rename from gui/pictures/achievments/AchievementIcon_128_50.png rename to gui/pictures/achievments/AchievementIcon_2_Erprobt.png diff --git a/gui/pictures/achievments/AchievementIcon_128_51.png b/gui/pictures/achievments/AchievementIcon_3_SchlachtErfahren.png similarity index 100% rename from gui/pictures/achievments/AchievementIcon_128_51.png rename to gui/pictures/achievments/AchievementIcon_3_SchlachtErfahren.png diff --git a/gui/pictures/achievments/AchievementIcon_4_1Sieg.png b/gui/pictures/achievments/AchievementIcon_4_1Sieg.png new file mode 100644 index 0000000..4c63aa4 Binary files /dev/null and b/gui/pictures/achievments/AchievementIcon_4_1Sieg.png differ diff --git a/gui/pictures/achievments/AchievementIcon_5_5Sieg.png b/gui/pictures/achievments/AchievementIcon_5_5Sieg.png new file mode 100644 index 0000000..bbd541c Binary files /dev/null and b/gui/pictures/achievments/AchievementIcon_5_5Sieg.png differ diff --git a/gui/pictures/achievments/AchievementIcon_6_10Sieg.png b/gui/pictures/achievments/AchievementIcon_6_10Sieg.png new file mode 100644 index 0000000..b53a654 Binary files /dev/null and b/gui/pictures/achievments/AchievementIcon_6_10Sieg.png differ diff --git a/gui/pictures/achievments/AchievementIcon_128_40.png b/gui/pictures/achievments/AchievementIcon_7_Kingslayer1.png similarity index 100% rename from gui/pictures/achievments/AchievementIcon_128_40.png rename to gui/pictures/achievments/AchievementIcon_7_Kingslayer1.png diff --git a/gui/pictures/achievments/AchievementIcon_128_41.png b/gui/pictures/achievments/AchievementIcon_8_Kingslayer2.png similarity index 100% rename from gui/pictures/achievments/AchievementIcon_128_41.png rename to gui/pictures/achievments/AchievementIcon_8_Kingslayer2.png diff --git a/gui/pictures/achievments/AchievementIcon_128_42.png b/gui/pictures/achievments/AchievementIcon_9_Kingslayer3.png similarity index 100% rename from gui/pictures/achievments/AchievementIcon_128_42.png rename to gui/pictures/achievments/AchievementIcon_9_Kingslayer3.png diff --git a/gui/templates/leaderboard.py b/gui/templates/leaderboard.py new file mode 100644 index 0000000..8bb0d8b --- /dev/null +++ b/gui/templates/leaderboard.py @@ -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) + ) diff --git a/gui/templates/player_card.py b/gui/templates/player_card.py new file mode 100644 index 0000000..1f6f799 --- /dev/null +++ b/gui/templates/player_card.py @@ -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') diff --git a/main.py b/main.py index 78e9274..f29f419 100644 --- a/main.py +++ b/main.py @@ -3,7 +3,7 @@ from dotenv import load_dotenv from nicegui import ui, app from data import database -from gui import main_gui, match_gui, discord_login, league_statistic_gui, admin_gui, match_history_gui, imprint_gui +from gui import main_gui, match_gui, discord_login, league_statistic_gui, admin_gui, match_history_gui, imprint_gui, league_statistic_visitor_gui from wood import logger from gui.info_text import info_system @@ -33,6 +33,7 @@ match_gui.setup_routes() admin_gui.setup_routes() match_history_gui.setup_routes() imprint_gui.setup_routes() +league_statistic_visitor_gui.setup_routes() -# 4. Wir starten die NiceGUI App +# 4. Starten der App Runtime auf dem Server. ui.run(title="Westside Diceghost Liga", port=9000, storage_secret="EIN_super-geheimes_Pa$$wort#!", favicon="gui/pictures/wsdg.png") diff --git a/setup_env.sh b/setup_env.sh index 63b40dc..53fa8b9 100644 --- a/setup_env.sh +++ b/setup_env.sh @@ -60,7 +60,8 @@ pip install \ nicegui \ requests \ python-dotenv \ - mariadb + mariadb \ + schedule echo "✅ Pakete installiert. Starte Liga Service ..."