enable teams to register for tournaments #53

Merged
Typhus merged 2 commits from feature/enable-teams-to-register-for-tournament into main 2026-02-22 00:45:11 +00:00
8 changed files with 229 additions and 42 deletions
Showing only changes of commit 6c48a798dc - Show all commits

View File

@ -1 +1 @@
0.3.3 0.3.4

View File

@ -0,0 +1,10 @@
-- =====================================================
-- Adds type of participant to tournament
-- =====================================================
ALTER TABLE `tournaments` ADD COLUMN `participant_type` ENUM('PLAYER','TEAM') NOT NULL DEFAULT 'PLAYER' AFTER `created_at`;
ALTER TABLE `tournament_participants`
CHANGE COLUMN `user_id` `user_id` INT(11) NULL AFTER `tournament_id`,
ADD COLUMN `team_id` INT(11) NULL AFTER `user_id`,
ADD CONSTRAINT `fk_tp_team` FOREIGN KEY (`team_id`) REFERENCES `teams` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT;

View File

@ -1,27 +1,36 @@
import logging
from asyncio import sleep from asyncio import sleep
from functools import partial
from typing import Optional, Union, Literal from typing import Optional, Union, Literal
from from_root import from_root from from_root import from_root
from rio import Column, Component, event, TextStyle, Text, Row, Image, Spacer, ProgressCircle, Button, Checkbox, ThemeContextSwitcher, Link, Revealer, PointerEventListener, \ from rio import Column, Component, event, TextStyle, Text, Row, Image, Spacer, ProgressCircle, Button, Checkbox, ThemeContextSwitcher, Link, Revealer, PointerEventListener, \
PointerEvent, Rectangle, Color PointerEvent, Rectangle, Color, Popup, Dropdown
from src.ezgg_lan_manager import ConfigurationService, TournamentService, UserService, TicketingService from src.ezgg_lan_manager import ConfigurationService, TournamentService, UserService, TicketingService, TeamService
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ezgg_lan_manager.components.TournamentDetailsInfoRow import TournamentDetailsInfoRow from src.ezgg_lan_manager.components.TournamentDetailsInfoRow import TournamentDetailsInfoRow
from src.ezgg_lan_manager.types.DateUtil import weekday_to_display_text from src.ezgg_lan_manager.types.DateUtil import weekday_to_display_text
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ezgg_lan_manager.types.Team import Team, TeamStatus
from src.ezgg_lan_manager.types.Tournament import Tournament from src.ezgg_lan_manager.types.Tournament import Tournament
from src.ezgg_lan_manager.types.TournamentBase import TournamentStatus, tournament_status_to_display_text, tournament_format_to_display_texts from src.ezgg_lan_manager.types.TournamentBase import TournamentStatus, tournament_status_to_display_text, tournament_format_to_display_texts, ParticipantType
from src.ezgg_lan_manager.types.User import User from src.ezgg_lan_manager.types.User import User
logger = logging.getLogger(__name__.split(".")[-1])
class TournamentDetailsPage(Component): class TournamentDetailsPage(Component):
tournament: Optional[Union[Tournament, str]] = None tournament: Optional[Union[Tournament, str]] = None
rules_accepted: bool = False rules_accepted: bool = False
user: Optional[User] = None user: Optional[User] = None
user_teams: list[Team] = []
loading: bool = False loading: bool = False
participant_revealer_open: bool = False participant_revealer_open: bool = False
current_tournament_user_list: list[User] = [] # ToDo: Integrate Teams current_tournament_user_or_team_list: Union[list[User], list[Team]] = []
team_dialog_open: bool = False
team_register_options: dict[str, Optional[Team]] = {"": None}
team_selected_for_register: Optional[Team] = None
# State for message above register button # State for message above register button
message: str = "" message: str = ""
@ -37,11 +46,16 @@ class TournamentDetailsPage(Component):
self.tournament = await self.session[TournamentService].get_tournament_by_id(tournament_id) self.tournament = await self.session[TournamentService].get_tournament_by_id(tournament_id)
if self.tournament is not None: if self.tournament is not None:
await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - {self.tournament.name}") await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - {self.tournament.name}")
self.current_tournament_user_list = await self.session[TournamentService].get_users_from_participant_list(self.tournament.participants) if self.tournament.participant_type == ParticipantType.PLAYER:
self.current_tournament_user_or_team_list = await self.session[TournamentService].get_users_from_participant_list(self.tournament.participants)
elif self.tournament.participant_type == ParticipantType.TEAM:
self.current_tournament_user_or_team_list = await self.session[TournamentService].get_teams_from_participant_list(self.tournament.participants)
else: else:
await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - Turniere") await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - Turniere")
self.user = await self.session[UserService].get_user(self.session[SessionStorage].user_id) self.user = await self.session[UserService].get_user(self.session[SessionStorage].user_id)
if self.user is not None:
self.user_teams = await self.session[TeamService].get_teams_for_user_by_id(self.user.user_id)
self.loading_done() self.loading_done()
@ -51,7 +65,12 @@ class TournamentDetailsPage(Component):
async def update(self) -> None: async def update(self) -> None:
self.tournament = await self.session[TournamentService].get_tournament_by_id(self.tournament.id) self.tournament = await self.session[TournamentService].get_tournament_by_id(self.tournament.id)
self.current_tournament_user_list = await self.session[TournamentService].get_users_from_participant_list(self.tournament.participants) if self.tournament is None:
return
if self.tournament.participant_type == ParticipantType.PLAYER:
self.current_tournament_user_or_team_list = await self.session[TournamentService].get_users_from_participant_list(self.tournament.participants)
elif self.tournament.participant_type == ParticipantType.TEAM:
self.current_tournament_user_or_team_list = await self.session[TournamentService].get_teams_from_participant_list(self.tournament.participants)
def open_close_participant_revealer(self, _: PointerEvent) -> None: def open_close_participant_revealer(self, _: PointerEvent) -> None:
self.participant_revealer_open = not self.participant_revealer_open self.participant_revealer_open = not self.participant_revealer_open
@ -66,24 +85,73 @@ class TournamentDetailsPage(Component):
self.is_success = False self.is_success = False
self.message = "Turnieranmeldung nur mit Ticket" self.message = "Turnieranmeldung nur mit Ticket"
else: else:
# Register single player
if self.tournament.participant_type == ParticipantType.PLAYER:
try: try:
await self.session[TournamentService].register_user_for_tournament(self.user.user_id, self.tournament.id) await self.session[TournamentService].register_user_for_tournament(self.user.user_id, self.tournament.id)
await self.artificial_delay() await self.artificial_delay()
self.is_success = True self.is_success = True
self.message = f"Erfolgreich angemeldet!" self.message = f"Erfolgreich angemeldet!"
except Exception as e: except Exception as e:
logger.error(e)
self.is_success = False self.is_success = False
self.message = f"Fehler: {e}" self.message = f"Fehler: {e}"
# Register team
elif self.tournament.participant_type == ParticipantType.TEAM:
try:
team_register_options = {"": None}
for team in self.user_teams:
if team.members[self.user] == TeamStatus.OFFICER or team.members[self.user] == TeamStatus.LEADER:
team_register_options[team.name] = team
if team_register_options:
self.team_register_options = team_register_options
else:
self.team_register_options = {"": None}
except StopIteration as e:
logger.error(f"Error trying to teams to register: {e}")
self.team_dialog_open = True
return # Model should handle loading state now
else:
pass
await self.update() await self.update()
self.loading = False self.loading = False
async def unregister_pressed(self) -> None: async def on_team_register_confirmed(self) -> None:
if self.team_selected_for_register is None:
await self.on_team_register_canceled()
return
try:
await self.session[TournamentService].register_team_for_tournament(self.team_selected_for_register.id, self.tournament.id)
await self.artificial_delay()
self.is_success = True
self.message = f"Erfolgreich angemeldet!"
self.team_dialog_open = False
self.team_selected_for_register = None
except Exception as e:
logger.error(e)
self.message = f"Fehler: {e}"
self.is_success = False
await self.update()
self.loading = False
async def on_team_register_canceled(self) -> None:
self.team_dialog_open = False
self.team_selected_for_register = None
self.loading = False
async def unregister_pressed(self, team: Optional[Team] = None) -> None:
self.loading = True self.loading = True
if not self.user: if not self.user:
return return
try: try:
if self.tournament.participant_type == ParticipantType.PLAYER:
await self.session[TournamentService].unregister_user_from_tournament(self.user.user_id, self.tournament.id) await self.session[TournamentService].unregister_user_from_tournament(self.user.user_id, self.tournament.id)
elif self.tournament.participant_type == ParticipantType.TEAM:
if team.members[self.user] == TeamStatus.OFFICER or team.members[self.user] == TeamStatus.LEADER:
await self.session[TournamentService].unregister_team_from_tournament(team.id, self.tournament.id)
else:
raise PermissionError("Nur Leiter und Offiziere können das Team abmelden")
await self.artificial_delay() await self.artificial_delay()
self.is_success = True self.is_success = True
self.message = f"Erfolgreich abgemeldet!" self.message = f"Erfolgreich abgemeldet!"
@ -145,10 +213,12 @@ class TournamentDetailsPage(Component):
on_press=self.tree_button_clicked on_press=self.tree_button_clicked
) )
# ToDo: Integrate Teams logic
ids_of_participants = [p.id for p in self.tournament.participants] ids_of_participants = [p.id for p in self.tournament.participants]
color_key: Literal["hud", "danger"] = "hud" color_key: Literal["hud", "danger"] = "hud"
on_press_function = self.register_pressed on_press_function = self.register_pressed
if self.tournament.participant_type == ParticipantType.PLAYER:
self.current_tournament_user_or_team_list: list[User] # IDE TypeHint
participant_names = "\n".join([u.user_name for u in self.current_tournament_user_or_team_list])
if self.user and self.user.user_id in ids_of_participants: # User already registered for tournament if self.user and self.user.user_id in ids_of_participants: # User already registered for tournament
button_text = "Abmelden" button_text = "Abmelden"
button_sensitive_hook = True # User has already accepted the rules previously button_sensitive_hook = True # User has already accepted the rules previously
@ -161,6 +231,29 @@ class TournamentDetailsPage(Component):
# This should NEVER happen # This should NEVER happen
button_text = "Anmelden" button_text = "Anmelden"
button_sensitive_hook = False button_sensitive_hook = False
elif self.tournament.participant_type == ParticipantType.TEAM:
self.current_tournament_user_or_team_list: list[Team] # IDE TypeHint
participant_names = "\n".join([t.name for t in self.current_tournament_user_or_team_list])
user_team_registered = []
for team in self.user_teams:
if team.id in ids_of_participants:
user_team_registered.append(team)
if self.user and len(user_team_registered) > 0: # Any of the users teams already registered for tournament
button_text = f"{user_team_registered[0].abbreviation} abmelden"
button_sensitive_hook = True # User has already accepted the rules previously
color_key = "danger"
on_press_function = partial(self.unregister_pressed, user_team_registered[0])
elif self.user and len(user_team_registered) == 0:
button_text = "Anmelden"
button_sensitive_hook = self.rules_accepted
else:
# This should NEVER happen
button_text = "Anmelden"
button_sensitive_hook = False
else:
logger.fatal("Did someone add new values to ParticipantType ? ;)")
return Column()
if self.tournament.status != TournamentStatus.OPEN or self.tournament.is_full: if self.tournament.status != TournamentStatus.OPEN or self.tournament.is_full:
button_sensitive_hook = False # Override button controls if tournament is not open anymore or full button_sensitive_hook = False # Override button controls if tournament is not open anymore or full
@ -186,8 +279,6 @@ class TournamentDetailsPage(Component):
# No UI here if user not logged in # No UI here if user not logged in
accept_rules_row, button = Spacer(), Spacer() accept_rules_row, button = Spacer(), Spacer()
content = Column( content = Column(
Row( Row(
Image(image=from_root(f"src/ezgg_lan_manager/assets/img/games/{self.tournament.game_title.image_name}"), margin_right=1), Image(image=from_root(f"src/ezgg_lan_manager/assets/img/games/{self.tournament.game_title.image_name}"), margin_right=1),
@ -213,7 +304,7 @@ class TournamentDetailsPage(Component):
content=Rectangle( content=Rectangle(
content=TournamentDetailsInfoRow( content=TournamentDetailsInfoRow(
"Teilnehmer ▴" if self.participant_revealer_open else "Teilnehmer ▾", "Teilnehmer ▴" if self.participant_revealer_open else "Teilnehmer ▾",
f"{len(self.current_tournament_user_list)} / {self.tournament.max_participants}", f"{len(self.current_tournament_user_or_team_list)} / {self.tournament.max_participants}",
value_color=self.session.theme.danger_color if self.tournament.is_full else self.session.theme.background_color, value_color=self.session.theme.danger_color if self.tournament.is_full else self.session.theme.background_color,
key_color=self.session.theme.secondary_color key_color=self.session.theme.secondary_color
), ),
@ -225,7 +316,7 @@ class TournamentDetailsPage(Component):
Revealer( Revealer(
header=None, header=None,
content=Text( content=Text(
"\n".join([u.user_name for u in self.current_tournament_user_list]), # ToDo: Integrate Teams participant_names,
style=TextStyle(fill=self.session.theme.background_color) style=TextStyle(fill=self.session.theme.background_color)
), ),
is_open=self.participant_revealer_open, is_open=self.participant_revealer_open,
@ -255,6 +346,39 @@ class TournamentDetailsPage(Component):
button button
) )
if self.tournament and self.tournament.participant_type == ParticipantType.TEAM:
content = Popup(
anchor=content,
content=Rectangle(
content=Column(
Text("Welches Team anmelden?", style=TextStyle(fill=self.session.theme.background_color, font_size=1.2), justify="center", margin_bottom=1),
ThemeContextSwitcher(
content=Dropdown(
options=self.team_register_options,
min_width=20,
selected_value=self.bind().team_selected_for_register
),
color=self.session.theme.hud_color,
margin_bottom=1
),
Row(
Button(content="Abbrechen", shape="rectangle", grow_x=False, on_press=self.on_team_register_canceled),
Button(content="Anmelden", shape="rectangle", grow_x=False, on_press=self.on_team_register_confirmed),
spacing=1
),
margin=0.5
),
min_width=30,
min_height=4,
fill=self.session.theme.primary_color,
margin_top=3.5,
stroke_width=0.3,
stroke_color=self.session.theme.neutral_color,
),
is_open=self.team_dialog_open,
color="none"
)
return Column( return Column(
MainViewContentBox( MainViewContentBox(
Column( Column(

View File

@ -859,6 +859,7 @@ class DatabaseService:
t.status AS tournament_status, t.status AS tournament_status,
t.max_participants, t.max_participants,
t.created_at, t.created_at,
t.participant_type AS tournament_participant_type,
/* ======================= /* =======================
Game Title Game Title
@ -874,6 +875,7 @@ class DatabaseService:
======================= */ ======================= */
tp.id AS participant_id, tp.id AS participant_id,
tp.user_id, tp.user_id,
tp.team_id,
tp.participant_type, tp.participant_type,
tp.seed, tp.seed,
tp.joined_at tp.joined_at
@ -907,6 +909,8 @@ class DatabaseService:
if current_tournament is None or current_tournament.id != row["tournament_id"]: if current_tournament is None or current_tournament.id != row["tournament_id"]:
if current_tournament is not None: if current_tournament is not None:
tournaments.append(current_tournament) tournaments.append(current_tournament)
participant_type = self._parse_participant_type(row["tournament_participant_type"])
id_accessor = "user_id" if participant_type == ParticipantType.PLAYER else "team_id"
current_tournament = Tournament( current_tournament = Tournament(
id_=row["tournament_id"], id_=row["tournament_id"],
name=row["tournament_name"], name=row["tournament_name"],
@ -920,14 +924,16 @@ class DatabaseService:
format_=self._parse_tournament_format(row["tournament_format"]), format_=self._parse_tournament_format(row["tournament_format"]),
start_time=row["start_time"], start_time=row["start_time"],
status=self._parse_tournament_status(row["tournament_status"]), status=self._parse_tournament_status(row["tournament_status"]),
participants=[Participant(id_=row["user_id"], participant_type=self._parse_participant_type(row["participant_type"]))] if row["user_id"] is not None else [], participants=[Participant(id_=row[id_accessor], participant_type=self._parse_participant_type(row["participant_type"]))] if row[id_accessor] is not None else [],
matches=None, # ToDo: Implement matches=None, # ToDo: Implement
rounds=[], # ToDo: Implement rounds=[], # ToDo: Implement
max_participants=row["max_participants"] max_participants=row["max_participants"],
participant_type=participant_type
) )
else: else:
id_accessor = "user_id" if current_tournament.participant_type == ParticipantType.PLAYER else "team_id"
current_tournament.add_participant( current_tournament.add_participant(
Participant(id_=row["user_id"], participant_type=self._parse_participant_type(row["participant_type"])) Participant(id_=row[id_accessor], participant_type=self._parse_participant_type(row["participant_type"]))
) )
else: else:
tournaments.append(current_tournament) tournaments.append(current_tournament)
@ -935,11 +941,14 @@ class DatabaseService:
return tournaments return tournaments
async def add_participant_to_tournament(self, participant: Participant, tournament: Tournament) -> None: async def add_participant_to_tournament(self, participant: Participant, tournament: Tournament) -> None:
if participant.participant_type != tournament.participant_type:
raise ValueError(f"Can not add {participant.participant_type.name} to {tournament.participant_type.name} tournament")
accessor = "user_id" if participant.participant_type == ParticipantType.PLAYER else "team_id"
async with self._connection_pool.acquire() as conn: async with self._connection_pool.acquire() as conn:
async with conn.cursor(aiomysql.Cursor) as cursor: async with conn.cursor(aiomysql.Cursor) as cursor:
try: try:
await cursor.execute( await cursor.execute(
"INSERT INTO tournament_participants (tournament_id, user_id, participant_type) VALUES (%s, %s, %s);", f"INSERT INTO tournament_participants (tournament_id, {accessor}, participant_type) VALUES (%s, %s, %s);",
(tournament.id, participant.id, participant.participant_type.name) (tournament.id, participant.id, participant.participant_type.name)
) )
await conn.commit() await conn.commit()
@ -952,11 +961,12 @@ class DatabaseService:
logger.warning(f"Error adding participant to tournament: {e}") logger.warning(f"Error adding participant to tournament: {e}")
async def remove_participant_from_tournament(self, participant: Participant, tournament: Tournament) -> None: async def remove_participant_from_tournament(self, participant: Participant, tournament: Tournament) -> None:
accessor = "user_id" if participant.participant_type == ParticipantType.PLAYER else "team_id"
async with self._connection_pool.acquire() as conn: async with self._connection_pool.acquire() as conn:
async with conn.cursor(aiomysql.Cursor) as cursor: async with conn.cursor(aiomysql.Cursor) as cursor:
try: try:
await cursor.execute( await cursor.execute(
"DELETE FROM tournament_participants WHERE (tournament_id = %s AND user_id = %s);", f"DELETE FROM tournament_participants WHERE (tournament_id = %s AND {accessor} = %s);",
(tournament.id, participant.id) (tournament.id, participant.id)
) )
await conn.commit() await conn.commit()

View File

@ -3,6 +3,7 @@ from typing import Optional
from src.ezgg_lan_manager.services.DatabaseService import DatabaseService from src.ezgg_lan_manager.services.DatabaseService import DatabaseService
from src.ezgg_lan_manager.services.UserService import UserService from src.ezgg_lan_manager.services.UserService import UserService
from src.ezgg_lan_manager.types.Participant import Participant from src.ezgg_lan_manager.types.Participant import Participant
from src.ezgg_lan_manager.types.Team import Team
from src.ezgg_lan_manager.types.Tournament import Tournament from src.ezgg_lan_manager.types.Tournament import Tournament
from src.ezgg_lan_manager.types.TournamentBase import ParticipantType, TournamentError from src.ezgg_lan_manager.types.TournamentBase import ParticipantType, TournamentError
from src.ezgg_lan_manager.types.User import User from src.ezgg_lan_manager.types.User import User
@ -27,11 +28,24 @@ class TournamentService:
tournament = await self.get_tournament_by_id(tournament_id) tournament = await self.get_tournament_by_id(tournament_id)
if not tournament: if not tournament:
raise TournamentError(f"No tournament with ID {tournament_id} was found") raise TournamentError(f"No tournament with ID {tournament_id} was found")
if tournament.participant_type != ParticipantType.PLAYER:
raise TournamentError(f"Can only add single player to team tournament, not {tournament.participant_type.name}")
participant = Participant(id_=user_id, participant_type=ParticipantType.PLAYER) participant = Participant(id_=user_id, participant_type=ParticipantType.PLAYER)
tournament.add_participant(participant) tournament.add_participant(participant)
await self._db_service.add_participant_to_tournament(participant, tournament) await self._db_service.add_participant_to_tournament(participant, tournament)
self._cache_dirty = True self._cache_dirty = True
async def register_team_for_tournament(self, team_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")
if tournament.participant_type != ParticipantType.TEAM:
raise TournamentError(f"Can only add team to team tournament, not {tournament.participant_type.name}")
participant = Participant(id_=team_id, participant_type=ParticipantType.TEAM)
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: async def unregister_user_from_tournament(self, user_id: int, tournament_id: int) -> None:
tournament = await self.get_tournament_by_id(tournament_id) tournament = await self.get_tournament_by_id(tournament_id)
if not tournament: if not tournament:
@ -42,6 +56,16 @@ class TournamentService:
await self._db_service.remove_participant_from_tournament(participant, tournament) await self._db_service.remove_participant_from_tournament(participant, tournament)
self._cache_dirty = True self._cache_dirty = True
async def unregister_team_from_tournament(self, team_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 == team_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]: async def get_tournaments(self) -> list[Tournament]:
if self._cache_dirty: if self._cache_dirty:
await self._update_cache() await self._update_cache()
@ -57,6 +81,11 @@ class TournamentService:
participant_ids = [p.id for p in participants] participant_ids = [p.id for p in participants]
return list(filter(lambda u: u.user_id in participant_ids, all_users)) return list(filter(lambda u: u.user_id in participant_ids, all_users))
async def get_teams_from_participant_list(self, participants: list[Participant]) -> list[Team]:
all_teams = await self._db_service.get_teams()
participant_ids = [p.id for p in participants]
return list(filter(lambda t: t.id in participant_ids, all_teams))
async def start_tournament(self, tournament_id: int): async def start_tournament(self, tournament_id: int):
tournament = await self.get_tournament_by_id(tournament_id) tournament = await self.get_tournament_by_id(tournament_id)
if tournament: if tournament:

View File

@ -10,8 +10,8 @@ logger = logging.getLogger(__name__.split(".")[-1])
# Note for ToDo: rio.UserSettings are saved LOCALLY, do not just read a user_id here! # Note for ToDo: rio.UserSettings are saved LOCALLY, do not just read a user_id here!
@dataclass(frozen=False) @dataclass(frozen=False)
class SessionStorage: class SessionStorage:
_user_id: Optional[int] = None # DEBUG: Put user ID here to skip login _user_id: Optional[int] = 30 # DEBUG: Put user ID here to skip login
_is_team_member: bool = False _is_team_member: bool = True
_notification_callbacks: dict[str, Callable] = field(default_factory=dict) _notification_callbacks: dict[str, Callable] = field(default_factory=dict)
async def clear(self) -> None: async def clear(self) -> None:

View File

@ -27,3 +27,11 @@ class Team:
abbreviation: str abbreviation: str
members: dict[User, TeamStatus] members: dict[User, TeamStatus]
join_password: str join_password: str
def __hash__(self) -> int:
return hash(self.id)
def __eq__(self, other):
if not isinstance(other, Team):
return NotImplemented
return self.id == other.id

View File

@ -5,7 +5,7 @@ from math import ceil, log2
from src.ezgg_lan_manager.types.Match import Match, FFAMatch from src.ezgg_lan_manager.types.Match import Match, FFAMatch
from src.ezgg_lan_manager.types.Participant import Participant from src.ezgg_lan_manager.types.Participant import Participant
from src.ezgg_lan_manager.types.TournamentBase import GameTitle, TournamentFormat, TournamentStatus, TournamentError, Bracket, MatchStatus from src.ezgg_lan_manager.types.TournamentBase import GameTitle, TournamentFormat, TournamentStatus, TournamentError, Bracket, MatchStatus, ParticipantType
class Tournament: class Tournament:
@ -20,7 +20,8 @@ class Tournament:
participants: list[Participant], participants: list[Participant],
matches: Optional[tuple[Match]], matches: Optional[tuple[Match]],
rounds: list[list[Match]], rounds: list[list[Match]],
max_participants: int) -> None: max_participants: int,
participant_type: ParticipantType) -> None:
self._id = id_ self._id = id_
self._name = name self._name = name
self._description = description self._description = description
@ -32,6 +33,7 @@ class Tournament:
self._matches = matches self._matches = matches
self._rounds = rounds self._rounds = rounds
self._max_participants = max_participants self._max_participants = max_participants
self._participant_type = participant_type
@property @property
def id(self) -> int: def id(self) -> int:
@ -85,6 +87,10 @@ class Tournament:
def is_full(self) -> bool: def is_full(self) -> bool:
return len(self._participants) >= self._max_participants return len(self._participants) >= self._max_participants
@property
def participant_type(self) -> ParticipantType:
return self._participant_type
def add_participant(self, participant: Participant) -> None: def add_participant(self, participant: Participant) -> None:
if participant.id in (p.id for p in self._participants): if participant.id in (p.id for p in self._participants):
raise TournamentError(f"Participant with ID {participant.id} already registered for tournament") raise TournamentError(f"Participant with ID {participant.id} already registered for tournament")