enable teams to register for tournaments (#53)

Co-authored-by: David Rodenkirchen <davidr.develop@gmail.com>
Reviewed-on: #53
This commit was merged in pull request #53.
This commit is contained in:
2026-02-22 00:45:11 +00:00
parent f4db57b2ff
commit d57f4baedd
7 changed files with 227 additions and 40 deletions
@@ -1,27 +1,36 @@
import logging
from asyncio import sleep
from functools import partial
from typing import Optional, Union, Literal
from from_root import from_root
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.TournamentDetailsInfoRow import TournamentDetailsInfoRow
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.Team import Team, TeamStatus
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
logger = logging.getLogger(__name__.split(".")[-1])
class TournamentDetailsPage(Component):
tournament: Optional[Union[Tournament, str]] = None
rules_accepted: bool = False
user: Optional[User] = None
user_teams: list[Team] = []
loading: 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
message: str = ""
@@ -37,11 +46,16 @@ class TournamentDetailsPage(Component):
self.tournament = await self.session[TournamentService].get_tournament_by_id(tournament_id)
if self.tournament is not None:
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:
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)
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()
@@ -51,7 +65,12 @@ class TournamentDetailsPage(Component):
async def update(self) -> None:
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:
self.participant_revealer_open = not self.participant_revealer_open
@@ -66,24 +85,73 @@ class TournamentDetailsPage(Component):
self.is_success = False
self.message = "Turnieranmeldung nur mit Ticket"
else:
try:
await self.session[TournamentService].register_user_for_tournament(self.user.user_id, self.tournament.id)
await self.artificial_delay()
self.is_success = True
self.message = f"Erfolgreich angemeldet!"
except Exception as e:
self.is_success = False
self.message = f"Fehler: {e}"
# Register single player
if self.tournament.participant_type == ParticipantType.PLAYER:
try:
await self.session[TournamentService].register_user_for_tournament(self.user.user_id, self.tournament.id)
await self.artificial_delay()
self.is_success = True
self.message = f"Erfolgreich angemeldet!"
except Exception as e:
logger.error(e)
self.is_success = False
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()
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
if not self.user:
return
try:
await self.session[TournamentService].unregister_user_from_tournament(self.user.user_id, self.tournament.id)
if self.tournament.participant_type == ParticipantType.PLAYER:
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()
self.is_success = True
self.message = f"Erfolgreich abgemeldet!"
@@ -145,22 +213,47 @@ class TournamentDetailsPage(Component):
on_press=self.tree_button_clicked
)
# ToDo: Integrate Teams logic
ids_of_participants = [p.id for p in self.tournament.participants]
color_key: Literal["hud", "danger"] = "hud"
on_press_function = self.register_pressed
if self.user and self.user.user_id in ids_of_participants: # User already registered for tournament
button_text = "Abmelden"
button_sensitive_hook = True # User has already accepted the rules previously
color_key = "danger"
on_press_function = self.unregister_pressed
elif self.user and self.user.user_id not in ids_of_participants:
button_text = "Anmelden"
button_sensitive_hook = self.rules_accepted
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
button_text = "Abmelden"
button_sensitive_hook = True # User has already accepted the rules previously
color_key = "danger"
on_press_function = self.unregister_pressed
elif self.user and self.user.user_id not in ids_of_participants:
button_text = "Anmelden"
button_sensitive_hook = self.rules_accepted
else:
# This should NEVER happen
button_text = "Anmelden"
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:
# This should NEVER happen
button_text = "Anmelden"
button_sensitive_hook = False
logger.fatal("Did someone add new values to ParticipantType ? ;)")
return Column()
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
@@ -186,8 +279,6 @@ class TournamentDetailsPage(Component):
# No UI here if user not logged in
accept_rules_row, button = Spacer(), Spacer()
content = Column(
Row(
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=TournamentDetailsInfoRow(
"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,
key_color=self.session.theme.secondary_color
),
@@ -225,7 +316,7 @@ class TournamentDetailsPage(Component):
Revealer(
header=None,
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)
),
is_open=self.participant_revealer_open,
@@ -255,6 +346,39 @@ class TournamentDetailsPage(Component):
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(
MainViewContentBox(
Column(