8 Commits

Author SHA1 Message Date
David Rodenkirchen d5cd05c0e5 release 0.3.7 2026-04-16 08:51:21 +02:00
David Rodenkirchen b8c1df5ff8 Disable commercial PayPal charging 2026-04-16 08:50:11 +02:00
dusker 8877de2cef Add EPC QR code to make bank transactions easier (#61)
See https://de.wikipedia.org/wiki/EPC-QR-Code#EPC-QR-Code_f%C3%BCr_%C3%9Cberweisung_erstellen for more information about the EPC coding

Co-authored-by: dusker <dusker@gmx.de>
Reviewed-on: #61
Co-authored-by: dusker <jens.graef+ezgg@posteo.de>
Co-committed-by: dusker <jens.graef+ezgg@posteo.de>
2026-04-16 06:48:46 +00:00
David Rodenkirchen bd5c142bcf Fix logout not redirecting properly 2026-04-16 07:32:35 +02:00
David Rodenkirchen e0ed3c7059 update sanitized backup 2026-04-16 07:07:00 +02:00
dusker a53e7100da Fix mariadb health check by adding the root password 2026-04-03 22:09:39 +02:00
David Rodenkirchen 2902c6a58c add sanitized production export 2026-02-24 00:54:24 +01:00
David Rodenkirchen 4541d3763f Hotfix: Remove Session override 2026-02-24 00:33:40 +01:00
16 changed files with 1118 additions and 97 deletions
+1 -1
View File
@@ -1 +1 @@
0.3.6 0.3.7
+4 -2
View File
@@ -7,7 +7,8 @@ services:
environment: environment:
PYTHONPATH: /opt/ezgg-lan-manager PYTHONPATH: /opt/ezgg-lan-manager
ports: ports:
- "7321:7321" - "8000:8000"
- "8001:8001"
volumes: volumes:
- ./:/opt/ezgg-lan-manager - ./:/opt/ezgg-lan-manager
entrypoint: ["/bin/sh", "-c", "cd /opt/ezgg-lan-manager/src && python3 /opt/ezgg-lan-manager/src/EzggLanManager.py"] entrypoint: ["/bin/sh", "-c", "cd /opt/ezgg-lan-manager/src && python3 /opt/ezgg-lan-manager/src/EzggLanManager.py"]
@@ -20,7 +21,7 @@ services:
MARIADB_USER: ezgg_lan_manager MARIADB_USER: ezgg_lan_manager
MARIADB_PASSWORD: Alkohol1 MARIADB_PASSWORD: Alkohol1
healthcheck: healthcheck:
test: ["CMD", "mariadb-admin", "ping", "-h", "localhost"] test: ["CMD", "mariadb-admin", "ping", "-h", "localhost", "-pAlkohol1"]
interval: 5s interval: 5s
timeout: 3s timeout: 3s
retries: 5 retries: 5
@@ -29,6 +30,7 @@ services:
volumes: volumes:
- database:/var/lib/mysql - database:/var/lib/mysql
- ./sql/create_database.sql:/docker-entrypoint-initdb.d/init.sql - ./sql/create_database.sql:/docker-entrypoint-initdb.d/init.sql
- ./sql:/sql
volumes: volumes:
BIN
View File
Binary file not shown.
File diff suppressed because one or more lines are too long
+4 -4
View File
@@ -18,10 +18,10 @@ logger = logging.getLogger("EzggLanManager")
if __name__ == "__main__": if __name__ == "__main__":
theme = Theme.from_colors( theme = Theme.from_colors(
primary_color=Color.from_hex("ffffff"), primary_color=Color.from_hex("ffffff"),
secondary_color=Color.from_hex("015454"), secondary_color=Color.from_hex("018786"),
neutral_color=Color.from_hex("1e1e1e"), neutral_color=Color.from_hex("1e1e1e"),
background_color=Color.from_hex("121212"), background_color=Color.from_hex("121212"),
hud_color=Color.from_hex("02a797"), hud_color=Color.from_hex("02dac5"),
text_color=Color.from_hex("018786"), text_color=Color.from_hex("018786"),
mode="dark", mode="dark",
corner_radius_small=0, corner_radius_small=0,
@@ -36,7 +36,7 @@ if __name__ == "__main__":
async def on_session_start(session: Session) -> None: async def on_session_start(session: Session) -> None:
# Use this line to fake being any user without having to log in # Use this line to fake being any user without having to log in
session.attach(UserSession(id=uuid4(), user_id=30, is_team_member=True)) # session.attach(UserSession(id=uuid4(), user_id=30, is_team_member=True))
await session.set_title(lan_info.name) await session.set_title(lan_info.name)
if session[LocalData].stored_session_token: if session[LocalData].stored_session_token:
user_session = session[LocalDataService].verify_token(session[LocalData].stored_session_token) user_session = session[LocalDataService].verify_token(session[LocalData].stored_session_token)
@@ -218,5 +218,5 @@ if __name__ == "__main__":
sys.exit(app.run_as_web_server( sys.exit(app.run_as_web_server(
host="0.0.0.0", host="0.0.0.0",
port=7321, port=8000,
)) ))
@@ -27,7 +27,7 @@ class DesktopNavigation(Component):
navigation = [ navigation = [
DesktopNavigationButton("News", "./news"), DesktopNavigationButton("News", "./news"),
Spacer(min_height=0.7), Spacer(min_height=0.7),
DesktopNavigationButton(f"Über {lan_info.name}", "./overview"), DesktopNavigationButton(f"Über {lan_info.name} {lan_info.iteration}", "./overview"),
DesktopNavigationButton("Ticket kaufen", "./buy_ticket"), DesktopNavigationButton("Ticket kaufen", "./buy_ticket"),
DesktopNavigationButton("Sitzplan", "./seating"), DesktopNavigationButton("Sitzplan", "./seating"),
DesktopNavigationButton("Catering", "./catering"), DesktopNavigationButton("Catering", "./catering"),
@@ -48,7 +48,7 @@ class DesktopNavigation(Component):
return Card( return Card(
Column( Column(
Text(lan_info.name, align_x=0.5, margin_top=0.3, style=TextStyle(fill=self.session.theme.hud_color, font_size=1.9)), Text(lan_info.name, align_x=0.5, margin_top=0.3, style=TextStyle(fill=self.session.theme.hud_color, font_size=1.9)),
Text(f"Edition {lan_info.iteration}", align_x=0.5, style=TextStyle(fill=self.session.theme.hud_color, font_size=0.9), margin_top=0.3, margin_bottom=2), Text(f"Edition {lan_info.iteration}", align_x=0.5, style=TextStyle(fill=self.session.theme.hud_color, font_size=1.2), margin_top=0.3, margin_bottom=2),
user_info_and_login_box, user_info_and_login_box,
*navigation, *navigation,
Text("Unsere Sponsoren", align_x=0.5, style=TextStyle(fill=self.session.theme.hud_color, font_size=0.9), margin_bottom=0.5, margin_top=1), Text("Unsere Sponsoren", align_x=0.5, style=TextStyle(fill=self.session.theme.hud_color, font_size=0.9), margin_bottom=0.5, margin_top=1),
@@ -1,8 +1,8 @@
from rio import Component, TextStyle, Color, Link, Button, Text from rio import Component, TextStyle, Color, Link, Button, Text
class DesktopNavigationButton(Component): class DesktopNavigationButton(Component):
STYLE = TextStyle(fill=Color.from_hex("02a797"), font_size=0.9) STYLE = TextStyle(fill=Color.from_hex("02dac5"), font_size=0.9)
TEAM_STYLE = TextStyle(fill=Color.from_hex("02a797"), font_size=0.9) TEAM_STYLE = TextStyle(fill=Color.from_hex("F0EADE"), font_size=0.9)
label: str label: str
target_url: str target_url: str
is_team_navigation: bool = False is_team_navigation: bool = False
+3 -3
View File
@@ -61,7 +61,7 @@ class LoginBox(Component):
is_valid=self.password_input_is_valid is_valid=self.password_input_is_valid
) )
login_button = Button( login_button = Button(
Text("LOGIN", fill=Color.from_hex("02a797"), style=TextStyle(font_size=0.9), justify="center"), Text("LOGIN", fill=Color.from_hex("02dac5"), style=TextStyle(font_size=0.9), justify="center"),
shape="rectangle", shape="rectangle",
style="minor", style="minor",
color="secondary", color="secondary",
@@ -69,14 +69,14 @@ class LoginBox(Component):
on_press=self._on_login_pressed on_press=self._on_login_pressed
) )
register_button = Button( register_button = Button(
Text("REG", fill=Color.from_hex("02a797"), style=TextStyle(font_size=0.9), justify="center"), Text("REG", fill=Color.from_hex("02dac5"), style=TextStyle(font_size=0.9), justify="center"),
shape="rectangle", shape="rectangle",
style="minor", style="minor",
color="secondary", color="secondary",
on_press=lambda: self.session.navigate_to("./register") on_press=lambda: self.session.navigate_to("./register")
) )
forgot_password_button = Button( forgot_password_button = Button(
Text("LST PWD", fill=Color.from_hex("02a797"), style=TextStyle(font_size=0.9), justify="center"), Text("LST PWD", fill=Color.from_hex("02dac5"), style=TextStyle(font_size=0.9), justify="center"),
shape="rectangle", shape="rectangle",
style="minor", style="minor",
color="secondary", color="secondary",
+149 -56
View File
@@ -5,7 +5,7 @@ from rio import Component, Rectangle, Grid, Column, Row, Text, TextStyle, Color,
from src.ezgg_lan_manager.components.SeatingPlanPixels import SeatPixel, WallPixel, InvisiblePixel, TextPixel from src.ezgg_lan_manager.components.SeatingPlanPixels import SeatPixel, WallPixel, InvisiblePixel, TextPixel
from src.ezgg_lan_manager.types.Seat import Seat from src.ezgg_lan_manager.types.Seat import Seat
MAX_GRID_WIDTH_PIXELS = 34 MAX_GRID_WIDTH_PIXELS = 60
MAX_GRID_HEIGHT_PIXELS = 60 MAX_GRID_HEIGHT_PIXELS = 60
class SeatingPlanLegend(Component): class SeatingPlanLegend(Component):
@@ -14,6 +14,14 @@ class SeatingPlanLegend(Component):
Text("Legende", style=TextStyle(fill=self.session.theme.neutral_color), justify="center", margin=1), Text("Legende", style=TextStyle(fill=self.session.theme.neutral_color), justify="center", margin=1),
Row( Row(
Spacer(), Spacer(),
Rectangle(
content=Text("Normaler Platz", style=TextStyle(fill=self.session.theme.neutral_color, font_size=0.7), margin=0.2, justify="center"),
fill=Color.TRANSPARENT,
stroke_width=0.2,
stroke_color=Color.from_hex("003300"),
min_width=20,
margin_right=1
),
Rectangle( Rectangle(
content=Text("Deluxe Platz", style=TextStyle(fill=self.session.theme.neutral_color, font_size=0.7), margin=0.2, justify="center"), content=Text("Deluxe Platz", style=TextStyle(fill=self.session.theme.neutral_color, font_size=0.7), margin=0.2, justify="center"),
fill=Color.TRANSPARENT, fill=Color.TRANSPARENT,
@@ -99,76 +107,161 @@ class SeatingPlan(Component):
for x in range(0, MAX_GRID_WIDTH_PIXELS): for x in range(0, MAX_GRID_WIDTH_PIXELS):
grid.add(WallPixel(), row=MAX_GRID_HEIGHT_PIXELS, column=x) grid.add(WallPixel(), row=MAX_GRID_HEIGHT_PIXELS, column=x)
for y in range(0, 39): for x in range(0, 31):
grid.add(WallPixel(), row=y, column=MAX_GRID_WIDTH_PIXELS) grid.add(WallPixel(), row=15, column=x)
for y in range(43, MAX_GRID_HEIGHT_PIXELS + 1): for x in range(41, MAX_GRID_WIDTH_PIXELS):
grid.add(WallPixel(), row=y, column=MAX_GRID_WIDTH_PIXELS) grid.add(WallPixel(), row=32, column=x)
for x in range(19, MAX_GRID_WIDTH_PIXELS): for x in range(31, 34):
grid.add(WallPixel(), row=37, column=x) grid.add(WallPixel(), row=32, column=x)
grid.add(WallPixel(), row=19, column=x)
for x in range(42, MAX_GRID_WIDTH_PIXELS):
grid.add(WallPixel(), row=11, column=x)
grid.add(WallPixel(), row=22, column=x)
for x in range(22, 30):
grid.add(WallPixel(), row=5, column=x)
grid.add(WallPixel(), row=10, column=x)
for y in range(5, 11):
grid.add(WallPixel(), row=y, column=21)
grid.add(WallPixel(), row=y, column=30)
for x in range(19, MAX_GRID_WIDTH_PIXELS): for y in range(40, MAX_GRID_HEIGHT_PIXELS):
grid.add(WallPixel(), row=46, column=x) grid.add(WallPixel(), row=y, column=30)
for x in range(1, 15):
grid.add(WallPixel(), row=46, column=x)
# Gaderobe for y in range(32, 36):
grid.add(WallPixel(), row=y, column=30)
for y in range(19, 33):
grid.add(WallPixel(), row=y, column=34)
for y in range(16, 20):
grid.add(WallPixel(), row=y, column=30)
for y in range(0, 5):
grid.add(WallPixel(), row=y, column=41)
for y in range(9, 15):
grid.add(WallPixel(), row=y, column=41)
for y in range(19, 33):
grid.add(WallPixel(), row=y, column=41)
# Block A
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"), 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"), 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"), 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"), 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"), 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"), 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"), 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"), 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"), 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( grid.add(PointerEventListener(
TextPixel(text="Gaderobe"), TextPixel(text="Bühne"),
on_press=lambda _: self.info_clicked_cb("Gaderobe") on_press=lambda _: self.info_clicked_cb("Hier darf ab Freitag 20 Uhr ebenfalls geschlafen werden.")
), row=44, column=19, width=15, height=2) ), row=16, column=1, width=29, height=4)
for x in range(19, MAX_GRID_WIDTH_PIXELS): # Drinks
grid.add(WallPixel(), row=28, column=x) grid.add(PointerEventListener(
for x in range(1, 15): TextPixel(text="G\ne\nt\nr\nä\nn\nk\ne"),
grid.add(WallPixel(), row=28, column=x) on_press=lambda _: self.info_clicked_cb("Ich mag Bier, B - I - R")
), row=20, column=30, width=4, height=12)
# # Toilet # Main Entrance
grid.add(PointerEventListener(
TextPixel(text="H\na\nl\nl\ne\nn\n\ne\ni\nn\ng\na\nn\ng"),
on_press=lambda _: self.info_clicked_cb("Hallo, ich bin ein Haupteingang")
), row=33, column=56, width=4, height=27)
# Sleeping
grid.add(PointerEventListener(
TextPixel(icon_name="material/bed"),
on_press=lambda _: self.info_clicked_cb("In diesem Raum kann geschlafen werden.\nAchtung: Hier werden nicht alle Teilnehmer Platz finden.")
), row=1, column=1, width=20, height=14)
# Toilet
grid.add(PointerEventListener( grid.add(PointerEventListener(
TextPixel(icon_name="material/wc"), TextPixel(icon_name="material/wc"),
on_press=lambda _: self.info_clicked_cb("Toilette") on_press=lambda _: self.info_clicked_cb("Damen Toilette")
), row=29, column=1, width=14, height=17) ), row=1, column=42, width=19, height=10)
grid.add(PointerEventListener( grid.add(PointerEventListener(
TextPixel(text="Orga & Technik"), TextPixel(icon_name="material/wc"),
on_press=lambda _: self.info_clicked_cb("Hier bauen wir die Technik auf.") on_press=lambda _: self.info_clicked_cb("Herren Toilette")
), row=29, column=19, width=15, height=8) ), row=12, column=42, width=19, height=10)
# Entry/Helpdesk
grid.add(PointerEventListener( grid.add(PointerEventListener(
TextPixel(text="Bar & Schnarchbereich"), TextPixel(text="Einlass\n &Orga"),
on_press=lambda _: self.info_clicked_cb("Bier.") on_press=lambda _: self.info_clicked_cb("Für alle Anliegen findest du hier rund um die Uhr jemanden vom Team.")
), row=47, column=1, width=MAX_GRID_WIDTH_PIXELS-1, height=13) ), row=40, column=22, width=8, height=12)
# # Block A
grid.add(SeatPixel("A\n0\n1", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A01"), seat_orientation="left"), row=2, column=5, width=2, height=4)
grid.add(SeatPixel("A\n0\n2", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A02"), seat_orientation="left"), row=6, column=5, width=2, height=4)
grid.add(SeatPixel("A\n0\n3", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A03"), seat_orientation="left"), row=10, column=5, width=2, height=4)
grid.add(SeatPixel("A\n0\n4", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A04"), seat_orientation="left"), row=15, column=5, width=2, height=4)
grid.add(SeatPixel("A\n0\n5", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A05"), seat_orientation="left"), row=19, column=5, width=2, height=4)
grid.add(SeatPixel("A\n0\n6", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A06"), seat_orientation="left"), row=23, column=5, width=2, height=4)
grid.add(SeatPixel("A\n1\n0", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A10"), seat_orientation="left"), row=2, column=7, width=2, height=4)
grid.add(SeatPixel("A\n1\n1", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A11"), seat_orientation="left"), row=6, column=7, width=2, height=4)
grid.add(SeatPixel("A\n1\n2", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A12"), seat_orientation="left"), row=10, column=7, width=2, height=4)
grid.add(SeatPixel("A\n1\n3", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A13"), seat_orientation="left"), row=15, column=7, width=2, height=4)
grid.add(SeatPixel("A\n1\n4", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A14"), seat_orientation="left"), row=19, column=7, width=2, height=4)
grid.add(SeatPixel("A\n1\n5", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A15"), seat_orientation="left"), row=23, column=7, width=2, height=4)
grid.add(SeatPixel("B\n0\n1", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B01"), seat_orientation="left"), row=2, column=26, width=2, height=4)
grid.add(SeatPixel("B\n0\n2", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B02"), seat_orientation="left"), row=6, column=26, width=2, height=4)
grid.add(SeatPixel("B\n0\n3", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B03"), seat_orientation="left"), row=10, column=26, width=2, height=4)
grid.add(SeatPixel("B\n0\n4", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B04"), seat_orientation="left"), row=15, column=26, width=2, height=4)
grid.add(SeatPixel("B\n0\n5", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B05"), seat_orientation="left"), row=19, column=26, width=2, height=4)
grid.add(SeatPixel("B\n0\n6", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B06"), seat_orientation="left"), row=23, column=26, width=2, height=4)
grid.add(SeatPixel("B\n1\n0", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B10"), seat_orientation="left"), row=2, column=28, width=2, height=4)
grid.add(SeatPixel("B\n1\n1", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B11"), seat_orientation="left"), row=6, column=28, width=2, height=4)
grid.add(SeatPixel("B\n1\n2", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B12"), seat_orientation="left"), row=10, column=28, width=2, height=4)
grid.add(SeatPixel("B\n1\n3", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B13"), seat_orientation="left"), row=15, column=28, width=2, height=4)
grid.add(SeatPixel("B\n1\n4", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B14"), seat_orientation="left"), row=19, column=28, width=2, height=4)
grid.add(SeatPixel("B\n1\n5", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B15"), seat_orientation="left"), row=23, column=28, width=2, height=4)
return Rectangle( return Rectangle(
content=grid, content=grid,
grow_x=True, grow_x=True,
@@ -11,7 +11,7 @@ class SeatPixel(Component):
seat_id: str seat_id: str
on_press_cb: Callable on_press_cb: Callable
seat: Seat seat: Seat
seat_orientation: Literal["top", "bottom", "left", "right"] seat_orientation: Literal["top", "bottom"]
def determine_color(self) -> Color: def determine_color(self) -> Color:
try: try:
@@ -60,8 +60,10 @@ class UserInfoBox(Component):
self.session[LocalDataService].del_session(self.session[LocalData].stored_session_token) self.session[LocalDataService].del_session(self.session[LocalData].stored_session_token)
self.session[LocalData].stored_session_token = None self.session[LocalData].stored_session_token = None
self.session.attach(self.session[LocalData]) self.session.attach(self.session[LocalData])
if self.status_change_cb is not None:
await self.status_change_cb() await self.status_change_cb()
await self.session[RefreshService].trigger_refresh() await self.session[RefreshService].trigger_refresh()
self.session.navigate_to("")
@event.on_populate @event.on_populate
async def async_init(self) -> None: async def async_init(self) -> None:
+26 -15
View File
@@ -1,7 +1,7 @@
from decimal import Decimal from decimal import Decimal
from typing import Optional from typing import Optional
from rio import Column, Component, event, Text, TextStyle, Button, Color, Revealer, Row, ProgressCircle, Link from rio import Column, Component, event, Text, TextStyle, Button, Color, Revealer, Row, ProgressCircle, Link, Image
from src.ezgg_lan_manager import ConfigurationService, UserService, AccountingService from src.ezgg_lan_manager import ConfigurationService, UserService, AccountingService
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
@@ -14,6 +14,7 @@ class AccountPage(Component):
user: Optional[User] = None user: Optional[User] = None
balance: Optional[Decimal] = None balance: Optional[Decimal] = None
transaction_history: list[Transaction] = list() transaction_history: list[Transaction] = list()
payment_qr_image: bytes = None
banking_info_revealer_open: bool = False banking_info_revealer_open: bool = False
paypal_info_revealer_open: bool = False paypal_info_revealer_open: bool = False
@@ -28,6 +29,11 @@ class AccountPage(Component):
self.user = await self.session[UserService].get_user(user_id) self.user = await self.session[UserService].get_user(user_id)
self.balance = await self.session[AccountingService].get_balance(user_id) self.balance = await self.session[AccountingService].get_balance(user_id)
self.transaction_history = await self.session[AccountingService].get_transaction_history(user_id) self.transaction_history = await self.session[AccountingService].get_transaction_history(user_id)
self.payment_qr_image = self.session[AccountingService].make_payment_qr_image(
"Einfach Zocken Gaming Gesellschaft",
"GENODE51BIK",
"DE47517624340019856607",
f"AUFLADUNG - {self.user.user_id} - {self.user.user_name}")
async def _on_banking_info_press(self) -> None: async def _on_banking_info_press(self) -> None:
self.banking_info_revealer_open = not self.banking_info_revealer_open self.banking_info_revealer_open = not self.banking_info_revealer_open
@@ -36,7 +42,7 @@ class AccountPage(Component):
self.paypal_info_revealer_open = not self.paypal_info_revealer_open self.paypal_info_revealer_open = not self.paypal_info_revealer_open
def build(self) -> Component: def build(self) -> Component:
if not self.user and not self.balance: if not self.user or not self.payment_qr_image:
return Column( return Column(
MainViewContentBox( MainViewContentBox(
ProgressCircle( ProgressCircle(
@@ -85,6 +91,10 @@ class AccountPage(Component):
margin=0, margin=0,
margin_bottom=1, margin_bottom=1,
align_x=0.5 align_x=0.5
),
Image(self.payment_qr_image,
min_width=20,
min_height=20
) )
), ),
margin=2, margin=2,
@@ -223,19 +233,20 @@ class AccountPage(Component):
on_press=self._on_paypal_info_press on_press=self._on_paypal_info_press
), ),
paypal_info_revealer, paypal_info_revealer,
Link( # Disabled because people did not understand the fee's and kept charging 24.03 € to their accounts
content=Button( # Link(
content=Text("PAYPAL (3% Gebühr - Gewerblich)", style=TextStyle(fill=Color.from_hex("121212"), font_size=0.8), justify="center"), # content=Button(
shape="rectangle", # content=Text("PAYPAL (3% Gebühr - Gewerblich)", style=TextStyle(fill=Color.from_hex("121212"), font_size=0.8), justify="center"),
style="major", # shape="rectangle",
color="secondary", # style="major",
grow_x=True, # color="secondary",
margin=2, # grow_x=True,
margin_top=0 # margin=2,
), # margin_top=0
target_url="https://www.paypal.com/ncp/payment/89YMGVZ4S33RS", # ),
open_in_new_tab=True # target_url="https://www.paypal.com/ncp/payment/89YMGVZ4S33RS",
) # open_in_new_tab=True
# )
) )
), ),
MainViewContentBox( MainViewContentBox(
+15 -7
View File
@@ -31,13 +31,13 @@ class OverviewPage(Component):
Row( Row(
Text("Wo?", fill=self.session.theme.neutral_color, margin_left=1), Text("Wo?", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(), Spacer(),
Link(Text(f"Hasenheim Bottenhorn", fill=self.session.theme.secondary_color, margin_right=1), target_url="https://maps.app.goo.gl/9Gtykc3FEqWSWy9b6", open_in_new_tab=True), Link(Text(f"DGH Donsbach", fill=self.session.theme.secondary_color, margin_right=1), target_url="https://maps.app.goo.gl/3Zyue776A22jdoxz5", open_in_new_tab=True),
margin_bottom=0.3 margin_bottom=0.3
), ),
Row( Row(
Text("Einlass", fill=self.session.theme.neutral_color, margin_left=1), Text("Einlass", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(), Spacer(),
Text(lan_info.date_from.strftime("Samstag %H:%M Uhr"), fill=self.session.theme.neutral_color, margin_right=1), Text(lan_info.date_from.strftime("Freitag %H:%M Uhr"), fill=self.session.theme.neutral_color, margin_right=1),
margin_bottom=0.3 margin_bottom=0.3
), ),
Row( Row(
@@ -75,19 +75,19 @@ class OverviewPage(Component):
Row( Row(
Text("Internet", fill=self.session.theme.neutral_color, margin_left=1), Text("Internet", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(), Spacer(),
Text(f"Wenn's gut läuft, ja", fill=self.session.theme.neutral_color, margin_right=1), Text(f"100/50 Mbit/s (down/up)", fill=self.session.theme.neutral_color, margin_right=1),
margin_bottom=0.3 margin_bottom=0.3
), ),
Row( Row(
Text("Routing", fill=self.session.theme.neutral_color, margin_left=1), Text("Routing", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(), Spacer(),
Text(f"Hoffentlich", fill=self.session.theme.neutral_color, margin_right=1), Text(f"Flaches Netz", fill=self.session.theme.neutral_color, margin_right=1),
margin_bottom=0.3 margin_bottom=0.3
), ),
Row( Row(
Text("WLAN", fill=self.session.theme.neutral_color, margin_left=1), Text("WLAN", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(), Spacer(),
Text(f"", fill=self.session.theme.neutral_color, margin_right=1), Text(f"vorhanden", fill=self.session.theme.neutral_color, margin_right=1),
margin_bottom=0.3 margin_bottom=0.3
) )
) )
@@ -102,7 +102,7 @@ class OverviewPage(Component):
margin_bottom=0.3 margin_bottom=0.3
), ),
Row( Row(
Text("Für ein paar Luftmatratzen ist Platz. Die Bottenhorner Vereinsmitglieder überlassen sich auch die ein oder andere Couch.", font_size=0.7, Text("Es steht ein Schlafsaal zur Verfügung. Nach der Eröffnung steht auch die Bühne als Schlafbereich zur Verfügung.", font_size=0.7,
fill=self.session.theme.neutral_color, margin_left=1, overflow="wrap"), fill=self.session.theme.neutral_color, margin_left=1, overflow="wrap"),
margin_bottom=0.3 margin_bottom=0.3
), ),
@@ -111,7 +111,15 @@ class OverviewPage(Component):
margin_bottom=0.3 margin_bottom=0.3
), ),
Row( Row(
Text("Jeder bringt einfach mit was er mag.", font_size=0.7, fill=self.session.theme.neutral_color, margin_left=1, overflow="wrap"), Text("Wir sorgen für euer leibliches Wohl, ihr dürft aber auch eure eigenen Speißen und Getränke mitbringen.", font_size=0.7, fill=self.session.theme.neutral_color, margin_left=1, overflow="wrap"),
margin_bottom=0.3
),
Row(
Text("Parken", fill=self.session.theme.neutral_color, margin_left=1, justify="center"),
margin_bottom=0.3
),
Row(
Text("Vor der Halle sind ausreichend Parkplätze vorhanden.", font_size=0.7, fill=self.session.theme.neutral_color, margin_left=1, overflow="wrap"),
margin_bottom=0.3 margin_bottom=0.3
) )
) )
@@ -51,7 +51,6 @@ class SeatingPlanPage(Component):
self.is_booking_blocked = True self.is_booking_blocked = True
async def on_seat_clicked(self, seat_id: str, _: PressEvent) -> None: async def on_seat_clicked(self, seat_id: str, _: PressEvent) -> None:
seat_id = seat_id.replace("\n", "")
self.seating_info_text = "" self.seating_info_text = ""
self.show_info_box = True self.show_info_box = True
self.show_purchase_box = False self.show_purchase_box = False
@@ -1,4 +1,6 @@
import io
import logging import logging
import qrcode
from collections.abc import Callable from collections.abc import Callable
from datetime import datetime from datetime import datetime
from decimal import Decimal, ROUND_DOWN from decimal import Decimal, ROUND_DOWN
@@ -74,3 +76,29 @@ class AccountingService:
return "0.00 €" return "0.00 €"
rounded_decimal = str(euros.quantize(Decimal(".01"), rounding=ROUND_DOWN)) rounded_decimal = str(euros.quantize(Decimal(".01"), rounding=ROUND_DOWN))
return f"{rounded_decimal}" return f"{rounded_decimal}"
@staticmethod
def make_payment_qr_image(beneficiary_name, beneficiary_bic, beneficiary_iban, text, amount_euros=None) -> bytes:
text = text.replace("\n",";")
amount_formatted = "EUR{:.2f}".format(amount_euros) if amount_euros else ""
epc_text = f"""BCD
002
1
SCT
{beneficiary_bic}
{beneficiary_name}
{beneficiary_iban}
{amount_formatted}
{text}
"""
qr = qrcode.QRCode(
version=6,
error_correction=qrcode.constants.ERROR_CORRECT_M,
)
qr.add_data(epc_text)
img = qr.make_image()
img_bytes = io.BytesIO()
img.save(img_bytes)
return img_bytes.getvalue()
@@ -21,5 +21,6 @@ class LocalDataService:
self._session[key] = session self._session[key] = session
return key return key
def del_session(self, token: str) -> None: def del_session(self, token: Optional[str]) -> None:
if token is not None:
self._session.pop(token, None) self._session.pop(token, None)