Compare commits
2 Commits
1a0573cba9
...
ff5d715a4e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff5d715a4e | ||
|
|
b505191156 |
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
@ -12,7 +12,7 @@ class SeatingPlanLegend(Component):
|
||||
def build(self) -> Component:
|
||||
return Column(
|
||||
Text("Legende", style=TextStyle(fill=self.session.theme.neutral_color), justify="center", margin=1),
|
||||
Row( # Disabled for upcoming LAN
|
||||
Row(
|
||||
Spacer(),
|
||||
Rectangle(
|
||||
content=Text("Normaler Platz", style=TextStyle(fill=self.session.theme.neutral_color, font_size=0.7), margin=0.2, justify="center"),
|
||||
@ -93,7 +93,7 @@ class SeatingPlan(Component):
|
||||
return seat if seat else Seat(seat_id="Z99", is_blocked=True, category="LUXUS", user=None)
|
||||
|
||||
"""
|
||||
This seating plan is for the community center "Bottenhorn"
|
||||
This seating plan is for the community center "Donsbach"
|
||||
"""
|
||||
def build(self) -> Component:
|
||||
grid = Grid()
|
||||
@ -153,73 +153,73 @@ class SeatingPlan(Component):
|
||||
|
||||
|
||||
# Block A
|
||||
grid.add(SeatPixel("A01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A01")), row=57, column=1, width=5, height=2)
|
||||
grid.add(SeatPixel("A02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A02")), row=57, column=6, width=5, height=2)
|
||||
grid.add(SeatPixel("A03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A03")), row=57, column=11, width=5, height=2)
|
||||
grid.add(SeatPixel("A04", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A04")), row=57, column=16, width=5, height=2)
|
||||
grid.add(SeatPixel("A05", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A05")), row=57, column=21, width=5, height=2)
|
||||
grid.add(SeatPixel("A01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A01"), seat_orientation="bottom"), row=57, column=1, width=5, height=2)
|
||||
grid.add(SeatPixel("A02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A02"), seat_orientation="bottom"), row=57, column=6, width=5, height=2)
|
||||
grid.add(SeatPixel("A03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A03"), seat_orientation="bottom"), row=57, column=11, width=5, height=2)
|
||||
grid.add(SeatPixel("A04", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A04"), seat_orientation="bottom"), row=57, column=16, width=5, height=2)
|
||||
grid.add(SeatPixel("A05", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A05"), seat_orientation="bottom"), row=57, column=21, width=5, height=2)
|
||||
|
||||
grid.add(SeatPixel("A10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A10")), row=55, column=1, width=5, height=2)
|
||||
grid.add(SeatPixel("A11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A11")), row=55, column=6, width=5, height=2)
|
||||
grid.add(SeatPixel("A12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A12")), row=55, column=11, width=5, height=2)
|
||||
grid.add(SeatPixel("A13", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A13")), row=55, column=16, width=5, height=2)
|
||||
grid.add(SeatPixel("A14", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A14")), row=55, column=21, width=5, height=2)
|
||||
grid.add(SeatPixel("A10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A10"), seat_orientation="top"), row=55, column=1, width=5, height=2)
|
||||
grid.add(SeatPixel("A11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A11"), seat_orientation="top"), row=55, column=6, width=5, height=2)
|
||||
grid.add(SeatPixel("A12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A12"), seat_orientation="top"), row=55, column=11, width=5, height=2)
|
||||
grid.add(SeatPixel("A13", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A13"), seat_orientation="top"), row=55, column=16, width=5, height=2)
|
||||
grid.add(SeatPixel("A14", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A14"), seat_orientation="top"), row=55, column=21, width=5, height=2)
|
||||
|
||||
# Block B
|
||||
grid.add(SeatPixel("B01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B01")), row=50, column=1, width=3, height=2)
|
||||
grid.add(SeatPixel("B02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B02")), row=50, column=4, width=3, height=2)
|
||||
grid.add(SeatPixel("B03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B03")), row=50, column=7, width=3, height=2)
|
||||
grid.add(SeatPixel("B04", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B04")), row=50, column=10, width=3, height=2)
|
||||
grid.add(SeatPixel("B05", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B05")), row=50, column=13, width=3, height=2)
|
||||
grid.add(SeatPixel("B06", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B06")), row=50, column=16, width=3, height=2)
|
||||
grid.add(SeatPixel("B01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B01"), seat_orientation="bottom"), row=50, column=1, width=3, height=2)
|
||||
grid.add(SeatPixel("B02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B02"), seat_orientation="bottom"), row=50, column=4, width=3, height=2)
|
||||
grid.add(SeatPixel("B03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B03"), seat_orientation="bottom"), row=50, column=7, width=3, height=2)
|
||||
grid.add(SeatPixel("B04", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B04"), seat_orientation="bottom"), row=50, column=10, width=3, height=2)
|
||||
grid.add(SeatPixel("B05", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B05"), seat_orientation="bottom"), row=50, column=13, width=3, height=2)
|
||||
grid.add(SeatPixel("B06", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B06"), seat_orientation="bottom"), row=50, column=16, width=3, height=2)
|
||||
|
||||
grid.add(SeatPixel("B10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B10")), row=48, column=1, width=3, height=2)
|
||||
grid.add(SeatPixel("B11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B11")), row=48, column=4, width=3, height=2)
|
||||
grid.add(SeatPixel("B12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B12")), row=48, column=7, width=3, height=2)
|
||||
grid.add(SeatPixel("B13", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B13")), row=48, column=10, width=3, height=2)
|
||||
grid.add(SeatPixel("B14", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B14")), row=48, column=13, width=3, height=2)
|
||||
grid.add(SeatPixel("B15", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B15")), row=48, column=16, width=3, height=2)
|
||||
grid.add(SeatPixel("B10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B10"), seat_orientation="top"), row=48, column=1, width=3, height=2)
|
||||
grid.add(SeatPixel("B11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B11"), seat_orientation="top"), row=48, column=4, width=3, height=2)
|
||||
grid.add(SeatPixel("B12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B12"), seat_orientation="top"), row=48, column=7, width=3, height=2)
|
||||
grid.add(SeatPixel("B13", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B13"), seat_orientation="top"), row=48, column=10, width=3, height=2)
|
||||
grid.add(SeatPixel("B14", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B14"), seat_orientation="top"), row=48, column=13, width=3, height=2)
|
||||
grid.add(SeatPixel("B15", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B15"), seat_orientation="top"), row=48, column=16, width=3, height=2)
|
||||
|
||||
# Block C
|
||||
grid.add(SeatPixel("C01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C01")), row=43, column=1, width=3, height=2)
|
||||
grid.add(SeatPixel("C02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C02")), row=43, column=4, width=3, height=2)
|
||||
grid.add(SeatPixel("C03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C03")), row=43, column=7, width=3, height=2)
|
||||
grid.add(SeatPixel("C04", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C04")), row=43, column=10, width=3, height=2)
|
||||
grid.add(SeatPixel("C05", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C05")), row=43, column=13, width=3, height=2)
|
||||
grid.add(SeatPixel("C06", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C06")), row=43, column=16, width=3, height=2)
|
||||
grid.add(SeatPixel("C01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C01"), seat_orientation="bottom"), row=43, column=1, width=3, height=2)
|
||||
grid.add(SeatPixel("C02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C02"), seat_orientation="bottom"), row=43, column=4, width=3, height=2)
|
||||
grid.add(SeatPixel("C03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C03"), seat_orientation="bottom"), row=43, column=7, width=3, height=2)
|
||||
grid.add(SeatPixel("C04", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C04"), seat_orientation="bottom"), row=43, column=10, width=3, height=2)
|
||||
grid.add(SeatPixel("C05", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C05"), seat_orientation="bottom"), row=43, column=13, width=3, height=2)
|
||||
grid.add(SeatPixel("C06", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C06"), seat_orientation="bottom"), row=43, column=16, width=3, height=2)
|
||||
|
||||
grid.add(SeatPixel("C10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C10")), row=41, column=1, width=3, height=2)
|
||||
grid.add(SeatPixel("C11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C11")), row=41, column=4, width=3, height=2)
|
||||
grid.add(SeatPixel("C12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C12")), row=41, column=7, width=3, height=2)
|
||||
grid.add(SeatPixel("C13", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C13")), row=41, column=10, width=3, height=2)
|
||||
grid.add(SeatPixel("C14", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C14")), row=41, column=13, width=3, height=2)
|
||||
grid.add(SeatPixel("C15", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C15")), row=41, column=16, width=3, height=2)
|
||||
grid.add(SeatPixel("C10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C10"), seat_orientation="top"), row=41, column=1, width=3, height=2)
|
||||
grid.add(SeatPixel("C11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C11"), seat_orientation="top"), row=41, column=4, width=3, height=2)
|
||||
grid.add(SeatPixel("C12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C12"), seat_orientation="top"), row=41, column=7, width=3, height=2)
|
||||
grid.add(SeatPixel("C13", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C13"), seat_orientation="top"), row=41, column=10, width=3, height=2)
|
||||
grid.add(SeatPixel("C14", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C14"), seat_orientation="top"), row=41, column=13, width=3, height=2)
|
||||
grid.add(SeatPixel("C15", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C15"), seat_orientation="top"), row=41, column=16, width=3, height=2)
|
||||
|
||||
# Block D
|
||||
grid.add(SeatPixel("D01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D01")), row=34, column=1, width=5, height=2)
|
||||
grid.add(SeatPixel("D02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D02")), row=34, column=6, width=5, height=2)
|
||||
grid.add(SeatPixel("D03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D03")), row=34, column=11, width=5, height=2)
|
||||
grid.add(SeatPixel("D04", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D04")), row=34, column=16, width=5, height=2)
|
||||
grid.add(SeatPixel("D05", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D05")), row=34, column=21, width=5, height=2)
|
||||
grid.add(SeatPixel("D01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D01"), seat_orientation="bottom"), row=34, column=1, width=5, height=2)
|
||||
grid.add(SeatPixel("D02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D02"), seat_orientation="bottom"), row=34, column=6, width=5, height=2)
|
||||
grid.add(SeatPixel("D03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D03"), seat_orientation="bottom"), row=34, column=11, width=5, height=2)
|
||||
grid.add(SeatPixel("D04", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D04"), seat_orientation="bottom"), row=34, column=16, width=5, height=2)
|
||||
grid.add(SeatPixel("D05", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D05"), seat_orientation="bottom"), row=34, column=21, width=5, height=2)
|
||||
|
||||
grid.add(SeatPixel("D10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D10")), row=32, column=1, width=5, height=2)
|
||||
grid.add(SeatPixel("D11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D11")), row=32, column=6, width=5, height=2)
|
||||
grid.add(SeatPixel("D12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D12")), row=32, column=11, width=5, height=2)
|
||||
grid.add(SeatPixel("D13", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D13")), row=32, column=16, width=5, height=2)
|
||||
grid.add(SeatPixel("D14", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D14")), row=32, column=21, width=5, height=2)
|
||||
grid.add(SeatPixel("D10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D10"), seat_orientation="top"), row=32, column=1, width=5, height=2)
|
||||
grid.add(SeatPixel("D11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D11"), seat_orientation="top"), row=32, column=6, width=5, height=2)
|
||||
grid.add(SeatPixel("D12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D12"), seat_orientation="top"), row=32, column=11, width=5, height=2)
|
||||
grid.add(SeatPixel("D13", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D13"), seat_orientation="top"), row=32, column=16, width=5, height=2)
|
||||
grid.add(SeatPixel("D14", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D14"), seat_orientation="top"), row=32, column=21, width=5, height=2)
|
||||
|
||||
# Block E
|
||||
grid.add(SeatPixel("E01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E01")), row=27, column=1, width=5, height=2)
|
||||
grid.add(SeatPixel("E02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E02")), row=27, column=6, width=5, height=2)
|
||||
grid.add(SeatPixel("E03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E03")), row=27, column=11, width=5, height=2)
|
||||
grid.add(SeatPixel("E04", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E04")), row=27, column=16, width=5, height=2)
|
||||
grid.add(SeatPixel("E05", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E05")), row=27, column=21, width=5, height=2)
|
||||
grid.add(SeatPixel("E01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E01"), seat_orientation="bottom"), row=27, column=1, width=5, height=2)
|
||||
grid.add(SeatPixel("E02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E02"), seat_orientation="bottom"), row=27, column=6, width=5, height=2)
|
||||
grid.add(SeatPixel("E03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E03"), seat_orientation="bottom"), row=27, column=11, width=5, height=2)
|
||||
grid.add(SeatPixel("E04", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E04"), seat_orientation="bottom"), row=27, column=16, width=5, height=2)
|
||||
grid.add(SeatPixel("E05", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E05"), seat_orientation="bottom"), row=27, column=21, width=5, height=2)
|
||||
|
||||
grid.add(SeatPixel("E10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E10")), row=25, column=1, width=5, height=2)
|
||||
grid.add(SeatPixel("E11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E11")), row=25, column=6, width=5, height=2)
|
||||
grid.add(SeatPixel("E12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E12")), row=25, column=11, width=5, height=2)
|
||||
grid.add(SeatPixel("E13", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E13")), row=25, column=16, width=5, height=2)
|
||||
grid.add(SeatPixel("E14", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E14")), row=25, column=21, width=5, height=2)
|
||||
grid.add(SeatPixel("E10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E10"), seat_orientation="top"), row=25, column=1, width=5, height=2)
|
||||
grid.add(SeatPixel("E11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E11"), seat_orientation="top"), row=25, column=6, width=5, height=2)
|
||||
grid.add(SeatPixel("E12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E12"), seat_orientation="top"), row=25, column=11, width=5, height=2)
|
||||
grid.add(SeatPixel("E13", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E13"), seat_orientation="top"), row=25, column=16, width=5, height=2)
|
||||
grid.add(SeatPixel("E14", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E14"), seat_orientation="top"), row=25, column=21, width=5, height=2)
|
||||
|
||||
# Stage
|
||||
grid.add(PointerEventListener(
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
from functools import partial
|
||||
|
||||
from rio import Component, Text, Icon, TextStyle, Rectangle, Spacer, Color, PointerEventListener, Column, Row
|
||||
from typing import Optional, Callable
|
||||
from rio import Component, Text, Icon, TextStyle, Rectangle, Spacer, Color, PointerEventListener, Column, Row, PointerEvent, Tooltip
|
||||
from typing import Optional, Callable, Literal
|
||||
|
||||
from src.ezgg_lan_manager.types.Seat import Seat
|
||||
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
|
||||
@ -11,6 +11,7 @@ class SeatPixel(Component):
|
||||
seat_id: str
|
||||
on_press_cb: Callable
|
||||
seat: Seat
|
||||
seat_orientation: Literal["top", "bottom"]
|
||||
|
||||
def determine_color(self) -> Color:
|
||||
if self.seat.user is not None and self.seat.user.user_id == self.session[SessionStorage].user_id:
|
||||
@ -20,26 +21,38 @@ class SeatPixel(Component):
|
||||
return self.session.theme.success_color
|
||||
|
||||
def build(self) -> Component:
|
||||
return PointerEventListener(
|
||||
content=Rectangle(
|
||||
content=Row(
|
||||
Text(f"{self.seat_id}", style=TextStyle(fill=self.session.theme.primary_color, font_size=0.9), align_x=0.5, selectable=False)
|
||||
),
|
||||
text = Text(f"{self.seat_id}", style=TextStyle(fill=self.session.theme.primary_color, font_size=0.9), align_x=0.5, selectable=False)
|
||||
rec = Rectangle(
|
||||
content=Row(text),
|
||||
min_width=1,
|
||||
min_height=1,
|
||||
fill=self.determine_color(),
|
||||
stroke_width = 0.1,
|
||||
hover_stroke_width = 0.1,
|
||||
stroke_width=0.1,
|
||||
hover_stroke_width=0.1,
|
||||
stroke_color=Color.from_hex("003300") if self.seat.category == "NORMAL" else Color.from_hex("66ff99"),
|
||||
grow_x=True,
|
||||
grow_y=True,
|
||||
hover_fill=self.session.theme.hud_color,
|
||||
transition_time=0.4,
|
||||
ripple=True
|
||||
),
|
||||
on_press=partial(self.on_press_cb, self.seat_id)
|
||||
)
|
||||
|
||||
if self.seat.user or self.seat.is_blocked:
|
||||
return PointerEventListener(
|
||||
content=Tooltip(
|
||||
anchor=rec,
|
||||
tip=self.seat.user.user_name if self.seat.user else "Gesperrt",
|
||||
position=self.seat_orientation,
|
||||
),
|
||||
on_press=partial(self.on_press_cb, self.seat_id),
|
||||
)
|
||||
else:
|
||||
return PointerEventListener(
|
||||
content=rec,
|
||||
on_press=partial(self.on_press_cb, self.seat_id),
|
||||
)
|
||||
|
||||
|
||||
class TextPixel(Component):
|
||||
text: Optional[str] = None
|
||||
icon_name: Optional[str] = None
|
||||
@ -59,13 +72,14 @@ class TextPixel(Component):
|
||||
fill=self.session.theme.primary_color,
|
||||
stroke_width=0.0 if self.no_outline else 0.1,
|
||||
stroke_color=self.session.theme.neutral_color,
|
||||
hover_stroke_width = None if self.no_outline else 0.1,
|
||||
hover_stroke_width=None if self.no_outline else 0.1,
|
||||
grow_x=True,
|
||||
grow_y=True,
|
||||
hover_fill=None,
|
||||
ripple=True
|
||||
)
|
||||
|
||||
|
||||
class WallPixel(Component):
|
||||
def build(self) -> Component:
|
||||
return Rectangle(
|
||||
@ -76,6 +90,7 @@ class WallPixel(Component):
|
||||
grow_y=True,
|
||||
)
|
||||
|
||||
|
||||
class DebugPixel(Component):
|
||||
def build(self) -> Component:
|
||||
return Rectangle(
|
||||
@ -83,14 +98,15 @@ class DebugPixel(Component):
|
||||
min_width=1,
|
||||
min_height=1,
|
||||
fill=self.session.theme.success_color,
|
||||
hover_stroke_color = self.session.theme.hud_color,
|
||||
hover_stroke_width = 0.1,
|
||||
hover_stroke_color=self.session.theme.hud_color,
|
||||
hover_stroke_width=0.1,
|
||||
grow_x=True,
|
||||
grow_y=True,
|
||||
hover_fill=self.session.theme.secondary_color,
|
||||
transition_time=0.1
|
||||
)
|
||||
|
||||
|
||||
class InvisiblePixel(Component):
|
||||
def build(self) -> Component:
|
||||
return Rectangle(
|
||||
|
||||
39
src/ezgg_lan_manager/types/Game.py
Normal file
39
src/ezgg_lan_manager/types/Game.py
Normal file
@ -0,0 +1,39 @@
|
||||
from typing import Optional
|
||||
|
||||
from src.ezgg_lan_manager.types.TournamentBase import TournamentError
|
||||
|
||||
|
||||
class Game:
|
||||
def __init__(self, id_: tuple[int, int], match_id: int, game_number: int, winner_id: Optional[int], score: Optional[tuple[int, int]], game_done: bool) -> None:
|
||||
self._id = id_
|
||||
self._match_id = match_id
|
||||
self._game_number = game_number
|
||||
self._winner_id = winner_id
|
||||
self._score = score
|
||||
self._done = game_done
|
||||
|
||||
@property
|
||||
def id(self) -> tuple[int, int]:
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def is_done(self) -> bool:
|
||||
return self._done
|
||||
|
||||
@property
|
||||
def winner(self) -> Optional[int]:
|
||||
return self._winner_id
|
||||
|
||||
@property
|
||||
def number(self) -> int:
|
||||
return self._game_number
|
||||
|
||||
|
||||
def finish(self, winner_id: int, score: tuple[int, int], force: bool = False) -> None:
|
||||
""" NEVER call this outside Match or a Testsuite """
|
||||
if self._done and not force:
|
||||
raise TournamentError("Game is already finished")
|
||||
|
||||
self._winner_id = winner_id
|
||||
self._score = score
|
||||
self._done = True
|
||||
133
src/ezgg_lan_manager/types/Match.py
Normal file
133
src/ezgg_lan_manager/types/Match.py
Normal file
@ -0,0 +1,133 @@
|
||||
from collections import Counter
|
||||
|
||||
from math import ceil
|
||||
from typing import Literal, Optional, Callable
|
||||
|
||||
from src.ezgg_lan_manager.types.Game import Game
|
||||
from src.ezgg_lan_manager.types.TournamentBase import MatchStatus, TournamentError, Bracket
|
||||
|
||||
|
||||
class MatchParticipant:
|
||||
def __init__(self, participant_id: int, slot_number: Literal[1, 2]) -> None:
|
||||
self._participant_id = participant_id
|
||||
if slot_number not in (1, 2):
|
||||
raise TournamentError("Invalid slot number")
|
||||
self.slot_number = slot_number
|
||||
|
||||
@property
|
||||
def participant_id(self) -> int:
|
||||
return self._participant_id
|
||||
|
||||
|
||||
class Match:
|
||||
def __init__(self,
|
||||
match_id: int,
|
||||
tournament_id: int,
|
||||
round_number: int,
|
||||
bracket: Bracket,
|
||||
best_of: int,
|
||||
status: MatchStatus,
|
||||
next_match_win_lose_ids: tuple[Optional[int], Optional[int]],
|
||||
match_has_ended_callback: Callable) -> None:
|
||||
self._match_id = match_id
|
||||
self._tournament_id = tournament_id
|
||||
self._round_number = round_number
|
||||
self._bracket = bracket
|
||||
self._best_of = best_of
|
||||
self._status = status
|
||||
self._next_match_win_id = next_match_win_lose_ids[0]
|
||||
self._next_match_lose_id = next_match_win_lose_ids[1]
|
||||
self._match_has_ended_callback = match_has_ended_callback
|
||||
|
||||
self._participants: list[MatchParticipant] = []
|
||||
self._games: tuple[Game] = self._prepare_games()
|
||||
|
||||
def _prepare_games(self) -> tuple[Game]:
|
||||
games = []
|
||||
for game_number in range(1, self._best_of + 1):
|
||||
game_id = (self._match_id, game_number)
|
||||
games.append(Game(game_id, self._match_id, game_number, None, None, False))
|
||||
return tuple(games)
|
||||
|
||||
@property
|
||||
def status(self) -> MatchStatus:
|
||||
if self._status == MatchStatus.COMPLETED:
|
||||
return self._status
|
||||
return self._status if self.is_fully_seeded else MatchStatus.WAITING
|
||||
|
||||
@status.setter
|
||||
def status(self, new_status: MatchStatus) -> None:
|
||||
if new_status in (MatchStatus.COMPLETED, MatchStatus.PENDING) and not self.is_fully_seeded:
|
||||
raise TournamentError("Can't complete/pend match that is not fully seeded")
|
||||
if self._status == MatchStatus.COMPLETED and new_status != MatchStatus.CANCELED:
|
||||
raise TournamentError("Can't change COMPLETED match back to another active status")
|
||||
self._status = new_status
|
||||
|
||||
@property
|
||||
def games(self) -> tuple[Game]:
|
||||
return self._games
|
||||
|
||||
@property
|
||||
def winner(self) -> Optional[int]:
|
||||
wins_needed = ceil(self._best_of / 2)
|
||||
counts = Counter(game.winner for game in self._games if game.is_done)
|
||||
|
||||
for participant_id, wins in counts.items():
|
||||
if wins >= wins_needed:
|
||||
return participant_id
|
||||
|
||||
return None
|
||||
|
||||
@property
|
||||
def is_fully_seeded(self) -> bool:
|
||||
slots = {p.slot_number for p in self._participants}
|
||||
return slots == {1, 2}
|
||||
|
||||
@property
|
||||
def match_id(self) -> int:
|
||||
return self._match_id
|
||||
|
||||
@property
|
||||
def participants(self) -> list[MatchParticipant]:
|
||||
return self._participants
|
||||
|
||||
@property
|
||||
def next_match_win_id(self) -> Optional[int]:
|
||||
return self._next_match_win_id
|
||||
|
||||
@property
|
||||
def next_match_lose_id(self) -> Optional[int]:
|
||||
return self._next_match_lose_id
|
||||
|
||||
def assign_participant(self, participant_id: int, slot: Literal[1, 2]) -> None:
|
||||
new_participant = MatchParticipant(participant_id, slot)
|
||||
if len(self._participants) < 2 and not any(p.participant_id == participant_id for p in self._participants):
|
||||
if len(self._participants) == 1 and self._participants[-1].slot_number == new_participant.slot_number:
|
||||
raise TournamentError(f"Match with ID {self._match_id} encountered slot collision")
|
||||
self._participants.append(new_participant)
|
||||
return
|
||||
raise TournamentError(f"Match with ID {self._match_id} already has the maximum number of participants")
|
||||
|
||||
def check_completion(self) -> None:
|
||||
winner = self.winner
|
||||
if winner is not None:
|
||||
self._match_has_ended_callback(self)
|
||||
self._status = MatchStatus.COMPLETED
|
||||
|
||||
def report_game_result(self, game_number: int, winner_id: int, score: tuple[int, int]) -> None:
|
||||
if winner_id not in {p.participant_id for p in self._participants}:
|
||||
raise TournamentError("Winner is not a participant of this match")
|
||||
|
||||
self._games[game_number - 1].finish(winner_id, score)
|
||||
|
||||
self.check_completion()
|
||||
|
||||
def cancel(self) -> None:
|
||||
self._status = MatchStatus.CANCELED
|
||||
|
||||
def __repr__(self) -> str:
|
||||
participants = ", ".join(
|
||||
f"{p.participant_id} (slot {p.slot_number})" for p in self._participants
|
||||
)
|
||||
return (f"<Match id={self._match_id} round={self._round_number} bracket={self._bracket.name} "
|
||||
f"status={self.status.name} participants=[{participants}] winner={self.winner}>")
|
||||
20
src/ezgg_lan_manager/types/Participant.py
Normal file
20
src/ezgg_lan_manager/types/Participant.py
Normal file
@ -0,0 +1,20 @@
|
||||
from src.ezgg_lan_manager.types.TournamentBase import ParticipantType
|
||||
|
||||
|
||||
class Participant:
|
||||
def __init__(self, id_: int, display_name: str, participant_type: ParticipantType) -> None:
|
||||
self._id = id_
|
||||
self._participant_type = participant_type
|
||||
self._display_name = display_name
|
||||
|
||||
@property
|
||||
def id(self) -> int:
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def participant_type(self) -> ParticipantType:
|
||||
return self._participant_type
|
||||
|
||||
@property
|
||||
def display_name(self) -> str:
|
||||
return self._display_name
|
||||
319
src/ezgg_lan_manager/types/Tournament.py
Normal file
319
src/ezgg_lan_manager/types/Tournament.py
Normal file
@ -0,0 +1,319 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from math import ceil, log2
|
||||
|
||||
from src.ezgg_lan_manager.types.Match import Match
|
||||
from src.ezgg_lan_manager.types.Participant import Participant
|
||||
from src.ezgg_lan_manager.types.TournamentBase import GameTitle, TournamentFormat, TournamentStatus, TournamentError, Bracket, MatchStatus
|
||||
|
||||
|
||||
class Tournament:
|
||||
def __init__(self,
|
||||
id_: int,
|
||||
name: str,
|
||||
game_title: GameTitle,
|
||||
format_: TournamentFormat,
|
||||
start_time: datetime,
|
||||
status: TournamentStatus,
|
||||
participants: list[Participant],
|
||||
matches: Optional[tuple[Match]],
|
||||
rounds: list[list[Match]]) -> None:
|
||||
self._id = id_
|
||||
self._name = name
|
||||
self._game_title = game_title
|
||||
self._format = format_
|
||||
self._start_time = start_time
|
||||
self._status = status
|
||||
self._participants = participants
|
||||
self._matches = matches
|
||||
self._rounds = rounds
|
||||
|
||||
@property
|
||||
def id(self) -> int:
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def game_title(self) -> GameTitle:
|
||||
return self._game_title
|
||||
|
||||
@property
|
||||
def format(self) -> TournamentFormat:
|
||||
return self._format
|
||||
|
||||
@property
|
||||
def start_time(self) -> datetime:
|
||||
return self._start_time
|
||||
|
||||
@property
|
||||
def status(self) -> TournamentStatus:
|
||||
return self._status
|
||||
|
||||
@status.setter
|
||||
def status(self, new_status: TournamentStatus) -> None:
|
||||
if new_status == TournamentStatus.OPEN and self._status == TournamentStatus.CLOSED and self._matches is not None:
|
||||
# Deletes all tournament preparation !
|
||||
self._matches = None
|
||||
self._status = new_status
|
||||
|
||||
|
||||
@property
|
||||
def participants(self) -> list[Participant]:
|
||||
return self._participants
|
||||
|
||||
@property
|
||||
def matches(self) -> list[Match]:
|
||||
return self._matches if self._matches else []
|
||||
|
||||
def add_participant(self, participant: Participant) -> None:
|
||||
if participant.id in (p.id for p in self._participants):
|
||||
raise TournamentError(f"Participant with ID {participant.id} already registered for tournament")
|
||||
self._participants.append(participant)
|
||||
|
||||
|
||||
def match_has_ended_callback(self, match: Match) -> None:
|
||||
if self._matches is None:
|
||||
return
|
||||
|
||||
winner = match.winner
|
||||
next_match = next((m for m in self._matches if m.match_id == match.next_match_win_id), None)
|
||||
if next_match is not None:
|
||||
try:
|
||||
next_match.assign_participant(winner, 1)
|
||||
except TournamentError:
|
||||
next_match.assign_participant(winner, 2)
|
||||
else: # No next match = final round
|
||||
pass
|
||||
|
||||
if match.next_match_lose_id is not None:
|
||||
loser = next(p for p in match.participants if p.participant_id != winner)
|
||||
next_match = next((m for m in self._matches if m.match_id == match.next_match_lose_id), None)
|
||||
if next_match is not None:
|
||||
try:
|
||||
next_match.assign_participant(loser.participant_id, 1)
|
||||
except TournamentError:
|
||||
next_match.assign_participant(loser.participant_id, 2)
|
||||
else: # No next match = final round
|
||||
pass
|
||||
|
||||
def start(self) -> None:
|
||||
""" This builds the tournament tree and sets it to ONGOING """
|
||||
|
||||
def parse_format(fmt: TournamentFormat) -> tuple[str, int]:
|
||||
if fmt.name.startswith("SINGLE_ELIMINATION"):
|
||||
bracket = "SINGLE"
|
||||
elif fmt.name.startswith("DOUBLE_ELIMINATION"):
|
||||
bracket = "DOUBLE"
|
||||
else:
|
||||
raise TournamentError(f"Unsupported tournament format: {fmt}")
|
||||
|
||||
if fmt.name.endswith("_BO_1"):
|
||||
bo = 1
|
||||
elif fmt.name.endswith("_BO_3"):
|
||||
bo = 3
|
||||
elif fmt.name.endswith("_BO_5"):
|
||||
bo = 5
|
||||
else:
|
||||
raise TournamentError(f"Unsupported best-of in format: {fmt}")
|
||||
|
||||
return bracket, bo
|
||||
|
||||
if len(self._participants) < 2:
|
||||
raise TournamentError("Cannot start tournament: not enough participants")
|
||||
|
||||
bracket_type, best_of = parse_format(self._format)
|
||||
num_participants = len(self.participants)
|
||||
match_id_counter = 1
|
||||
|
||||
if bracket_type == "SINGLE":
|
||||
# --- single-elimination as before ---
|
||||
num_rounds = ceil(log2(num_participants))
|
||||
rounds: list[list[Match]] = []
|
||||
|
||||
for round_number in range(1, num_rounds + 1):
|
||||
num_matches = 2 ** (num_rounds - round_number)
|
||||
round_matches = []
|
||||
for _ in range(num_matches):
|
||||
match = Match(
|
||||
match_id=match_id_counter,
|
||||
tournament_id=self._id,
|
||||
round_number=round_number,
|
||||
bracket=Bracket.UPPER if round_number != num_rounds else Bracket.FINAL,
|
||||
best_of=best_of,
|
||||
status=MatchStatus.WAITING,
|
||||
next_match_win_lose_ids=(None, None),
|
||||
match_has_ended_callback=self.match_has_ended_callback
|
||||
)
|
||||
round_matches.append(match)
|
||||
match_id_counter += 1
|
||||
rounds.append(round_matches)
|
||||
|
||||
# Link winner IDs
|
||||
for i in range(len(rounds) - 1):
|
||||
current_round = rounds[i]
|
||||
next_round = rounds[i + 1]
|
||||
for idx, match in enumerate(current_round):
|
||||
next_match = next_round[idx // 2]
|
||||
match._next_match_win_id = next_match.match_id
|
||||
|
||||
# Assign participants to first round
|
||||
participant_iter = iter(self.participants)
|
||||
first_round = rounds[0]
|
||||
for match in first_round:
|
||||
try:
|
||||
p1 = next(participant_iter)
|
||||
match.assign_participant(p1.id, 1)
|
||||
except StopIteration:
|
||||
continue
|
||||
try:
|
||||
p2 = next(participant_iter)
|
||||
match.assign_participant(p2.id, 2)
|
||||
except StopIteration:
|
||||
if not match.is_fully_seeded: # Auto-Bye
|
||||
for game in match.games:
|
||||
match.report_game_result(game.number, p1.id, (1, 0))
|
||||
continue
|
||||
|
||||
if match.is_fully_seeded:
|
||||
match.status = MatchStatus.PENDING
|
||||
|
||||
|
||||
# Flatten all rounds
|
||||
self._matches = [m for round_matches in rounds for m in round_matches]
|
||||
|
||||
elif bracket_type == "DOUBLE":
|
||||
# --- double-elimination bracket generation ---
|
||||
# ToDo: Rounds are not correctly persisted into self._rounds here. What data structure to use?
|
||||
# ToDo: Bye-Handling not done
|
||||
# Implementation Notice: Do not implement yet!
|
||||
num_rounds_upper = ceil(log2(num_participants))
|
||||
upper_rounds: list[list[Match]] = []
|
||||
for round_number in range(1, num_rounds_upper + 1):
|
||||
num_matches = 2 ** (num_rounds_upper - round_number)
|
||||
round_matches = []
|
||||
for _ in range(num_matches):
|
||||
match = Match(
|
||||
match_id=match_id_counter,
|
||||
tournament_id=self._id,
|
||||
round_number=round_number,
|
||||
bracket=Bracket.UPPER,
|
||||
best_of=best_of,
|
||||
status=MatchStatus.WAITING,
|
||||
next_match_win_lose_ids=(None, None), # will fill later
|
||||
match_has_ended_callback=self.match_has_ended_callback
|
||||
)
|
||||
round_matches.append(match)
|
||||
match_id_counter += 1
|
||||
upper_rounds.append(round_matches)
|
||||
|
||||
# Lower bracket (Losers)
|
||||
# Double-elim lower bracket has roughly (2*num_rounds_upper - 2) rounds
|
||||
num_rounds_lower = 2 * (num_rounds_upper - 1)
|
||||
lower_rounds: list[list[Match]] = []
|
||||
for round_number in range(1, num_rounds_lower + 1):
|
||||
num_matches = 2 ** (num_rounds_lower - round_number - 1) if round_number != 1 else 2 ** (num_rounds_upper - 1)
|
||||
round_matches = []
|
||||
for _ in range(num_matches):
|
||||
match = Match(
|
||||
match_id=match_id_counter,
|
||||
tournament_id=self._id,
|
||||
round_number=round_number,
|
||||
bracket=Bracket.LOWER,
|
||||
best_of=best_of,
|
||||
status=MatchStatus.WAITING,
|
||||
next_match_win_lose_ids=(None, None),
|
||||
match_has_ended_callback=self.match_has_ended_callback
|
||||
)
|
||||
round_matches.append(match)
|
||||
match_id_counter += 1
|
||||
lower_rounds.append(round_matches)
|
||||
|
||||
# Link upper bracket winners to next upper-round matches
|
||||
for i in range(len(upper_rounds) - 1):
|
||||
for idx, match in enumerate(upper_rounds[i]):
|
||||
next_match = upper_rounds[i + 1][idx // 2]
|
||||
match._next_match_win_id = next_match.match_id
|
||||
|
||||
# Link upper bracket losers to lower bracket first rounds
|
||||
lower_round1 = lower_rounds[0] if lower_rounds else []
|
||||
for idx, match in enumerate(upper_rounds[0]):
|
||||
if idx < len(lower_round1):
|
||||
match._next_match_lose_id = lower_round1[idx].match_id
|
||||
|
||||
# Link lower bracket winners to next lower-round matches
|
||||
for i in range(len(lower_rounds) - 1):
|
||||
for idx, match in enumerate(lower_rounds[i]):
|
||||
next_match = lower_rounds[i + 1][idx // 2]
|
||||
match._next_match_win_id = next_match.match_id
|
||||
|
||||
# Final match
|
||||
final_match = Match(
|
||||
match_id=match_id_counter,
|
||||
tournament_id=self._id,
|
||||
round_number=max(num_rounds_upper, num_rounds_lower) + 1,
|
||||
bracket=Bracket.FINAL,
|
||||
best_of=best_of,
|
||||
status=MatchStatus.WAITING,
|
||||
next_match_win_lose_ids=(None, None),
|
||||
match_has_ended_callback=self.match_has_ended_callback
|
||||
)
|
||||
match_id_counter += 1
|
||||
|
||||
# Last upper winner and last lower winner feed into final
|
||||
if upper_rounds:
|
||||
upper_last = upper_rounds[-1][0]
|
||||
upper_last._next_match_win_id = final_match.match_id
|
||||
if lower_rounds:
|
||||
lower_last = lower_rounds[-1][0]
|
||||
lower_last._next_match_win_id = final_match.match_id
|
||||
|
||||
# Flatten all matches
|
||||
self._matches = [m for round_matches in upper_rounds + lower_rounds for m in round_matches] + [final_match]
|
||||
|
||||
# Assign participants to first upper round
|
||||
participant_iter = iter(self._participants)
|
||||
first_upper = upper_rounds[0]
|
||||
for match in first_upper:
|
||||
try:
|
||||
p1 = next(participant_iter)
|
||||
match.assign_participant(p1.id, 1)
|
||||
except StopIteration:
|
||||
continue
|
||||
try:
|
||||
p2 = next(participant_iter)
|
||||
match.assign_participant(p2.id, 2)
|
||||
except StopIteration:
|
||||
if not match.is_fully_seeded: # Auto-Bye
|
||||
for game in match.games:
|
||||
match.report_game_result(game.number, p1.id, (1, 0))
|
||||
match.check_completion()
|
||||
continue
|
||||
|
||||
if match.is_fully_seeded:
|
||||
match.status = MatchStatus.PENDING
|
||||
|
||||
else:
|
||||
raise TournamentError(f"Unknown bracket type: {bracket_type}")
|
||||
|
||||
self._status = TournamentStatus.ONGOING
|
||||
for match in self._matches:
|
||||
match.check_completion()
|
||||
|
||||
|
||||
def generate_new_tournament(name: str, game_title: GameTitle, format_: TournamentFormat, start_time: datetime, initial_status: TournamentStatus = TournamentStatus.CLOSED) -> Tournament:
|
||||
id_ = uuid.uuid4().int
|
||||
return Tournament(
|
||||
id_,
|
||||
name,
|
||||
game_title,
|
||||
format_,
|
||||
start_time,
|
||||
initial_status,
|
||||
list(),
|
||||
None,
|
||||
list()
|
||||
)
|
||||
51
src/ezgg_lan_manager/types/TournamentBase.py
Normal file
51
src/ezgg_lan_manager/types/TournamentBase.py
Normal file
@ -0,0 +1,51 @@
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
|
||||
|
||||
@dataclass
|
||||
class GameTitle:
|
||||
name: str
|
||||
description: str
|
||||
web_link: str
|
||||
|
||||
|
||||
class TournamentFormat(Enum):
|
||||
SINGLE_ELIMINATION_BO_1 = 1
|
||||
SINGLE_ELIMINATION_BO_3 = 2
|
||||
SINGLE_ELIMINATION_BO_5 = 3
|
||||
DOUBLE_ELIMINATION_BO_1 = 4
|
||||
DOUBLE_ELIMINATION_BO_3 = 5
|
||||
DOUBLE_ELIMINATION_BO_5 = 6
|
||||
|
||||
|
||||
class TournamentStatus(Enum):
|
||||
CLOSED = 1
|
||||
OPEN = 2
|
||||
COMPLETED = 3
|
||||
CANCELED = 4
|
||||
INVITE_ONLY = 5 # For Show-matches
|
||||
ONGOING = 6
|
||||
|
||||
|
||||
class TournamentError(Exception):
|
||||
def __init__(self, message: str) -> None:
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class ParticipantType(Enum):
|
||||
PLAYER = 1
|
||||
TEAM = 2 # ToDo: Teams are not yet supported
|
||||
|
||||
|
||||
class Bracket(Enum):
|
||||
UPPER = 1
|
||||
LOWER = 2
|
||||
FINAL = 3
|
||||
|
||||
|
||||
class MatchStatus(Enum):
|
||||
WAITING = 1 # Participants incomplete
|
||||
PENDING = 2 # Match is ready to be played
|
||||
DELAYED = 3 # Same as PENDING, but with flag for UI
|
||||
COMPLETED = 4 # Match has been played
|
||||
CANCELED = 5 # Match got canceled, "bye" for followup
|
||||
66
testing/unittests/TournamentDomainTests.py
Normal file
66
testing/unittests/TournamentDomainTests.py
Normal file
@ -0,0 +1,66 @@
|
||||
import unittest
|
||||
from datetime import datetime
|
||||
|
||||
from src.ezgg_lan_manager.types.TournamentBase import ParticipantType
|
||||
from src.ezgg_lan_manager.types.Tournament import generate_new_tournament, GameTitle, TournamentFormat, TournamentStatus, TournamentError, Participant, MatchStatus
|
||||
|
||||
|
||||
class TournamentDomainTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
# Generic Tournament config
|
||||
self.name = "Tetris 1vs1"
|
||||
self.game_title = GameTitle("Tetris99", "Some Description", "https://de.wikipedia.org/wiki/Tetris_99")
|
||||
self.format_ = TournamentFormat.SINGLE_ELIMINATION_BO_3
|
||||
self.start_time = datetime(year=2100, month=6, day=23, hour=16, minute=30, second=0)
|
||||
self.initial_status = TournamentStatus.CLOSED
|
||||
|
||||
# Generic Participants
|
||||
self.participant_a = Participant(1, "CoolUserName", ParticipantType.PLAYER)
|
||||
self.participant_b = Participant(2, "CrazyUserName", ParticipantType.PLAYER)
|
||||
self.participant_c = Participant(3, "FunnyUserName", ParticipantType.PLAYER)
|
||||
|
||||
def test_tournament_without_participants_can_not_be_started(self) -> None:
|
||||
tournament_under_test = generate_new_tournament(self.name, self.game_title, self.format_, self.start_time, self.initial_status)
|
||||
with self.assertRaises(TournamentError):
|
||||
tournament_under_test.start()
|
||||
|
||||
def test_adding_the_same_participant_twice_leads_to_exception(self) -> None:
|
||||
tournament_under_test = generate_new_tournament(self.name, self.game_title, self.format_, self.start_time, self.initial_status)
|
||||
tournament_under_test.add_participant(self.participant_a)
|
||||
with self.assertRaises(TournamentError):
|
||||
tournament_under_test.add_participant(self.participant_a)
|
||||
|
||||
def test_single_elimination_bo3_tournament_gets_generated_correctly(self) -> None:
|
||||
tournament_under_test = generate_new_tournament(self.name, self.game_title, self.format_, self.start_time, self.initial_status)
|
||||
|
||||
tournament_under_test.add_participant(self.participant_a)
|
||||
tournament_under_test.add_participant(self.participant_b)
|
||||
tournament_under_test.add_participant(self.participant_c)
|
||||
tournament_under_test.start()
|
||||
|
||||
# Assert Tournament was switched to ONGOING
|
||||
self.assertEqual(TournamentStatus.ONGOING, tournament_under_test.status)
|
||||
|
||||
matches_in_tournament = sorted(tournament_under_test.matches, key=lambda m: m.match_id)
|
||||
|
||||
# First match
|
||||
fm = matches_in_tournament[0]
|
||||
self.assertEqual(fm.status, MatchStatus.PENDING)
|
||||
self.assertEqual(fm.participants[0].participant_id, self.participant_a.id)
|
||||
self.assertEqual(fm.participants[0].slot_number, 1)
|
||||
self.assertEqual(fm.participants[1].participant_id, self.participant_b.id)
|
||||
self.assertEqual(fm.participants[1].slot_number, 2)
|
||||
|
||||
# Second match (Bye)
|
||||
sm = matches_in_tournament[1]
|
||||
self.assertEqual(sm.status, MatchStatus.COMPLETED)
|
||||
self.assertEqual(sm.participants[0].participant_id, self.participant_c.id)
|
||||
self.assertEqual(sm.participants[0].slot_number, 1)
|
||||
self.assertEqual(sm.participants[0].participant_id, sm.winner)
|
||||
|
||||
# Third match (Final)
|
||||
sm = matches_in_tournament[2]
|
||||
self.assertEqual(sm.status, MatchStatus.WAITING)
|
||||
self.assertEqual(sm.participants[0].participant_id, self.participant_c.id)
|
||||
self.assertEqual(sm.participants[0].slot_number, 1)
|
||||
self.assertIsNone(sm.winner)
|
||||
Loading…
Reference in New Issue
Block a user