From caacd8bbc10cf92031af145ee75e0dddc77e424a Mon Sep 17 00:00:00 2001 From: David Rodenkirchen Date: Tue, 3 Feb 2026 13:20:07 +0100 Subject: [PATCH] implement registering and unregistering from tournament --- .../pages/TournamentDetailsPage.py | 20 ++++- .../services/DatabaseService.py | 34 +++++++++ .../services/TournamentService.py | 73 ++++++------------- src/ezgg_lan_manager/types/Tournament.py | 4 + 4 files changed, 75 insertions(+), 56 deletions(-) diff --git a/src/ezgg_lan_manager/pages/TournamentDetailsPage.py b/src/ezgg_lan_manager/pages/TournamentDetailsPage.py index c4e9dbf..35161f5 100644 --- a/src/ezgg_lan_manager/pages/TournamentDetailsPage.py +++ b/src/ezgg_lan_manager/pages/TournamentDetailsPage.py @@ -43,16 +43,28 @@ class TournamentDetailsPage(Component): self.loading = True if not self.user: return - self.is_success = True - self.message = f"Erfolgreich angemeldet!" # ToDo: Hook into Tournament Service + + try: + await self.session[TournamentService].register_user_for_tournament(self.user.user_id, self.tournament.id) + self.is_success = True + self.message = f"Erfolgreich angemeldet!" + except Exception as e: + self.is_success = False + self.message = f"Fehler: {e}" self.loading = False async def unregister_pressed(self) -> None: self.loading = True if not self.user: return - self.is_success = True - self.message = f"Erfolgreich abgemeldet!" # ToDo: Hook into Tournament Service + + try: + await self.session[TournamentService].unregister_user_from_tournament(self.user.user_id, self.tournament.id) + self.is_success = True + self.message = f"Erfolgreich abgemeldet!" + except Exception as e: + self.is_success = False + self.message = f"Fehler: {e}" self.loading = False async def tree_button_clicked(self) -> None: diff --git a/src/ezgg_lan_manager/services/DatabaseService.py b/src/ezgg_lan_manager/services/DatabaseService.py index d7e5d3b..c77dc31 100644 --- a/src/ezgg_lan_manager/services/DatabaseService.py +++ b/src/ezgg_lan_manager/services/DatabaseService.py @@ -931,3 +931,37 @@ class DatabaseService: tournaments.append(current_tournament) return tournaments + + async def add_participant_to_tournament(self, participant: Participant, tournament: Tournament) -> None: + async with self._connection_pool.acquire() as conn: + async with conn.cursor(aiomysql.Cursor) as cursor: + try: + await cursor.execute( + "INSERT INTO tournament_participants (tournament_id, user_id, participant_type) VALUES (%s, %s, %s);", + (tournament.id, participant.id, participant.participant_type.name) + ) + await conn.commit() + except aiomysql.InterfaceError: + pool_init_result = await self.init_db_pool() + if not pool_init_result: + raise NoDatabaseConnectionError + return await self.add_participant_to_tournament(participant, tournament) + except Exception as e: + logger.warning(f"Error adding participant to tournament: {e}") + + async def remove_participant_from_tournament(self, participant: Participant, tournament: Tournament) -> None: + async with self._connection_pool.acquire() as conn: + async with conn.cursor(aiomysql.Cursor) as cursor: + try: + await cursor.execute( + "DELETE FROM tournament_participants WHERE (tournament_id = %s AND user_id = %s);", + (tournament.id, participant.id) + ) + await conn.commit() + except aiomysql.InterfaceError: + pool_init_result = await self.init_db_pool() + if not pool_init_result: + raise NoDatabaseConnectionError + return await self.remove_participant_from_tournament(participant, tournament) + except Exception as e: + logger.warning(f"Error removing participant from tournament: {e}") diff --git a/src/ezgg_lan_manager/services/TournamentService.py b/src/ezgg_lan_manager/services/TournamentService.py index 0ada577..1fb0b85 100644 --- a/src/ezgg_lan_manager/services/TournamentService.py +++ b/src/ezgg_lan_manager/services/TournamentService.py @@ -1,76 +1,45 @@ -from asyncio import sleep -from datetime import datetime from typing import Optional from src.ezgg_lan_manager.services.DatabaseService import DatabaseService from src.ezgg_lan_manager.services.UserService import UserService from src.ezgg_lan_manager.types.Participant import Participant from src.ezgg_lan_manager.types.Tournament import Tournament -from src.ezgg_lan_manager.types.TournamentBase import GameTitle, TournamentFormat, TournamentStatus, ParticipantType - -DEV_LOREM_IPSUM = """ -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Dieses LAN-Turnier bringt Spieler aus verschiedenen Regionen zusammen, um gemeinsam spannende Matches zu erleben. Tastaturen klappern, Monitore leuchten und die Stimmung ist von Anfang an von Vorfreude und Ehrgeiz geprägt. - -Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. In intensiven Partien zählen Strategie, Reaktion und Teamarbeit. Zwischen den Spielen wird gefachsimpelt, gelacht und neue Kontakte entstehen, während Server stabil laufen und das Netzwerk dauerhaft gefordert ist. - -Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Wenn die Finalspiele beginnen, steigt die Spannung spürbar. Am Ende bleiben faire Wettkämpfe, gemeinsame Erinnerungen und das Gefühl, Teil einer starken Community gewesen zu sein. -""" +from src.ezgg_lan_manager.types.TournamentBase import ParticipantType, TournamentError class TournamentService: def __init__(self, db_service: DatabaseService, user_service: UserService) -> None: self._db_service = db_service self._user_service = user_service + + # Crude cache mechanism. If performance suffers, maybe implement a queue with Single-Owner-Pattern or a Lock self._cache: dict[int, Tournament] = {} self._cache_dirty: bool = True # Setting this flag invokes cache update on next read - # This overrides the database access and is meant for easy development. - # Set to None before merging back into main. - self._dev_data = [ - Tournament( - 1, - "Rocket League 3vs3", - DEV_LOREM_IPSUM, - GameTitle( - "Rocket League", - "Rocket League is a high-powered hybrid of arcade-style soccer and vehicular mayhem with easy-to-understand controls and fluid, physics-driven competition.", - "https://steamcommunity.com/app/252950", - "rl.png" - ), - TournamentFormat.SINGLE_ELIMINATION_BO_3, - datetime(2026, 5, 8, 18, 0, 0), - TournamentStatus.OPEN, - [Participant(30, ParticipantType.PLAYER)], - None, - [], - 8 - ), - Tournament( - 2, - "Worms Armageddon 1vs1", - DEV_LOREM_IPSUM, - GameTitle( - "Worms Armageddon", - "2D turn-based artillery strategy game.", - "https://store.steampowered.com/app/217200/Worms_Armageddon/", - "worms.png" - ), - TournamentFormat.SINGLE_ELIMINATION_BO_1, - datetime(2026, 5, 8, 18, 30, 0), - TournamentStatus.CLOSED, - [], - None, - [], - 16 - ) - ] - async def _update_cache(self) -> None: tournaments = await self._db_service.get_all_tournaments() for tournament in tournaments: self._cache[tournament.id] = tournament self._cache_dirty = False + async def register_user_for_tournament(self, user_id: int, tournament_id: int) -> None: + tournament = await self.get_tournament_by_id(tournament_id) + if not tournament: + raise TournamentError(f"No tournament with ID {tournament_id} was found") + participant = Participant(id_=user_id, participant_type=ParticipantType.PLAYER) + tournament.add_participant(participant) + await self._db_service.add_participant_to_tournament(participant, tournament) + self._cache_dirty = True + + async def unregister_user_from_tournament(self, user_id: int, tournament_id: int) -> None: + tournament = await self.get_tournament_by_id(tournament_id) + if not tournament: + raise TournamentError(f"No tournament with ID {tournament_id} was found") + participant = next(filter(lambda p: p.id == user_id, tournament.participants), None) + if participant is not None: + tournament.remove_participant(participant) + await self._db_service.remove_participant_from_tournament(participant, tournament) + self._cache_dirty = True async def get_tournaments(self) -> list[Tournament]: if self._cache_dirty: diff --git a/src/ezgg_lan_manager/types/Tournament.py b/src/ezgg_lan_manager/types/Tournament.py index 1821230..578bcaa 100644 --- a/src/ezgg_lan_manager/types/Tournament.py +++ b/src/ezgg_lan_manager/types/Tournament.py @@ -90,6 +90,10 @@ class Tournament: raise TournamentError(f"Participant with ID {participant.id} already registered for tournament") self._participants.append(participant) + def remove_participant(self, participant: Participant) -> None: + if participant.id not in (p.id for p in self._participants): + raise TournamentError(f"Participant with ID {participant.id} not registered for tournament") + self._participants.remove(participant) def match_has_ended_callback(self, match: Match) -> None: if self._matches is None: