Compare commits

..

3 Commits

Author SHA1 Message Date
tcprod
d98482f540 WIP 2025-02-18 12:24:12 +01:00
tcprod
1679f537de expand database schema and add blueprints for Tournament feature 2025-02-09 21:58:25 +01:00
tcprod
3eb9d6ab8c add tournaments feature 2025-02-09 19:47:39 +01:00
91 changed files with 828 additions and 1243 deletions

View File

@ -1,22 +0,0 @@
<html>
<head>
<title>EZGG LAN Manager - Wartungsmodus</title>
<style>
body { text-align: center; padding: 150px; }
h1 { font-size: 50px; }
body { font: 20px Helvetica, sans-serif; color: #333; }
article { display: block; text-align: left; width: 650px; margin: 0 auto; }
a { color: #dc8100; text-decoration: none; }
a:hover { color: #333; text-decoration: none; }
</style>
</head>
<body>
<article>
<h1>Wir sind bald wieder da!</h1>
<div>
<p>Wir f&uuml;hren zurzeit Wartungsarbeiten durch und sind in k&uuml;rze wieder f&uuml;r euch da.</p>
<p>&mdash; Euer EZGG LAN Team</p>
</div>
</article>
</body>
</html>

View File

@ -1,12 +0,0 @@
FROM python:3.12-bookworm
RUN apt-get update
RUN apt install dumb-init
COPY requirements.txt .
RUN pip install -r requirements.txt
EXPOSE 8000
EXPOSE 8001
ENTRYPOINT ["/usr/bin/dumb-init", "--"]

View File

@ -1,45 +1,13 @@
# EZGG LAN Manager
# EZ LAN Manager
## Overview
This repository contains the code for the EZGG LAN Manager.
This repository contains the code for the EZ LAN Manager.
## Development Setup
## How to install [prod]
### Prerequisites
TBD
- Working Installation of MySQL 5 or latest MariaDB Server (`mariadb-server` for Debian-based Linux, `XAMPP` for Windows)
- Python 3.9 or higher
- PyCharm or similar IDE (optional)
## How to install [dev]
### Step 1: Preparing Database
To prepare the database, apply the SQL file located in `sql/create_database.sql` to your database server. This is easily accomplished with the MYSQL Workbench, but it can be also done by pipeing the file into the mariadb-server executable.
Optionally, you can now execute the script `create_demo_database_content.py`, found in `src/ezgg_lan_manager/helpers`. Be aware that it can be buggy sometimes, especially if you overwrite existing data.
### Step 2: Preparing configuration
Use the example configuration at `config/config.example.toml` to create a `config.toml` at the base of the repository. Most of the parameters do not matter to get the development setup done, but the database credentials need to be correct.
### Step 3: Install dependecies
Use `pip install -r requirements.txt` to install the requirements. The usage of a venv is recommended.
### Step 4: Running the application
Run the application by executing the file `EzggLanManager.py` found at `src/ezgg_lan_manager`. Check the STDOUT for information regarding the port on which the application is now served.
## Docker Deployment
To get the docker compose setup running, you need to manually complete the following steps:
1. Create a valid `config.toml` in the project root, so it gets copied over into the container.
2. Create the database user:
```sql
CREATE USER 'ezgg_lan_manager'@'%' IDENTIFIED BY 'PASSWORD';
GRANT ALL PRIVILEGES ON ezgg_lan_manager.* TO 'ezgg_lan_manager'@'%';
FLUSH PRIVILEGES;
```
3. Make sure to **NOT** use the default passwords!
4. Apply the `create_database.sql` when starting the MariaDB container for the first time.
TBD

View File

@ -1 +1 @@
0.1.0
0.0.1

View File

@ -1,5 +1,5 @@
[lan]
name="EZGG LAN"
name="EZ LAN"
iteration="0.5"
date_from="2024-10-30 15:00:00"
date_till="2024-11-01 12:00:00"
@ -10,7 +10,7 @@
db_password="demo_password"
db_host="127.0.0.1"
db_port=3306
db_name="ezgg_lan_manager"
db_name="ez_lan_manager"
[mailing]
smtp_server=""
@ -19,6 +19,12 @@
username=""
password=""
[seating]
# SeatID -> Category
A01 = "NORMAL"
A02 = "NORMAL"
C01 = "LUXUS"
[tickets]
[tickets."NORMAL"]
total_tickets=30
@ -34,11 +40,5 @@
additional_info="Berechtigt zur Nutzung eines verbesserten Platzes. Dieser ist mit einer höheren Internet-Bandbreite und einem Sitzkissen ausgestattet."
is_default=false
[receipt_printing]
host="127.0.0.1"
port="5000"
order_print_endpoint="print_order"
password="Alkohol1"
[misc]
dev_mode_active=true # Supresses E-Mail sending

View File

@ -1,36 +0,0 @@
services:
lan_manager:
build: .
depends_on:
db:
condition: service_healthy
environment:
PYTHONPATH: /opt/ezgg-lan-manager
ports:
- "8000:8000"
- "8001:8001"
volumes:
- ./:/opt/ezgg-lan-manager
entrypoint: ["/bin/sh", "-c", "cd /opt/ezgg-lan-manager/src && python3 /opt/ezgg-lan-manager/src/EzggLanManager.py"]
db:
image: mariadb:latest
environment:
MARIADB_ROOT_PASSWORD: Alkohol1
MARIADB_DATABASE: ezgg_lan_manager
MARIADB_USER: ezgg_lan_manager
MARIADB_PASSWORD: Alkohol1
healthcheck:
test: ["CMD", "mariadb-admin", "ping", "-h", "localhost"]
interval: 5s
timeout: 3s
retries: 5
ports:
- "127.0.0.1:3306:3306"
volumes:
- database:/var/lib/mysql
- ./sql/create_database.sql:/docker-entrypoint-initdb.d/init.sql
volumes:
database:

Binary file not shown.

View File

@ -1,65 +0,0 @@
INSERT INTO `catering_menu_items` VALUES
(1,'Schnitzel Wiener Art','mit Pommes Frites und Salat',20.50,'MAIN_COURSE',1),
(2,'Jäger Schnitzel mit Champignonrahm Sauce','mit Spätzlen und Salat',22.50,'MAIN_COURSE',1),
(3,'Rumpsteak 250g','mit Pommes Frites und Salat',35.50,'MAIN_COURSE',1),
(4,'Rindergeschnetzeltes','mit Spätzlen und Salat',22.50,'MAIN_COURSE',1),
(5,'Galloway Burger','mit Amazing Fries',20.00,'MAIN_COURSE',1),
(6,'Käsespätzle mit Röstzwiebel','mit Salat',20.00,'MAIN_COURSE',1),
(11,'Käse Schinken Wrap','',5.00,'SNACK',1),
(12,'Puten Paprika Wrap','',7.00,'SNACK',1),
(13,'Tomate Mozzarella Wrap','',6.00,'SNACK',1),
(14,'Portion Pommes','',4.00,'SNACK',1),
(15,'Rinds-Currywurst','',4.50,'SNACK',1),
(16,'Rinds-Currywurst mit Pommes','',6.50,'SNACK',1),
(17,'Nudelsalat','',4.50,'SNACK',1),
(18,'Nudelsalat mit Bockwurst','',6.00,'SNACK',1),
(19,'Kartoffelsalat','',4.50,'SNACK',1),
(20,'Kartoffelsalat mit Bockwurst','',6.00,'SNACK',1),
(21,'Sandwichtoast - Schinken','',1.80,'SNACK',1),
(22,'Sandwichtoast - Käse','',1.80,'SNACK',1),
(23,'Sandwichtoast - Schinken/Käse','',2.10,'SNACK',1),
(24,'Sandwichtoast - Salami','',1.80,'SNACK',1),
(25,'Sandwichtoast - Salami/Käse','',2.10,'SNACK',1),
(26,'Chips - Western Style','',1.50,'SNACK',1),
(27,'Nachos - Salted','',1.50,'SNACK',1),
(28,'Panna Cotta mit Erdbeersauce','',7.00,'DESSERT',1),
(29,'Panna Cotta mit Blaubeersauce','',7.00,'DESSERT',1),
(30,'Mousse au Chocolat','',7.00,'DESSERT',1),
(31,'Fruit Loops','',1.50,'BREAKFAST',1),
(32,'Smacks','',1.50,'BREAKFAST',1),
(33,'Knuspermüsli','Schoko',2.00,'BREAKFAST',1),
(34,'Cini Minis','',1.50,'BREAKFAST',1),
(35,'Brötchen - Schinken','mit Margarine',1.20,'BREAKFAST',1),
(36,'Brötchen - Käse','mit Margarine',1.20,'BREAKFAST',1),
(37,'Brötchen - Schinken/Käse','mit Margarine',1.40,'BREAKFAST',1),
(38,'Brötchen - Salami','mit Margarine',1.20,'BREAKFAST',1),
(39,'Brötchen - Salami/Käse','mit Margarine',1.40,'BREAKFAST',1),
(40,'Brötchen - Nutella','mit Margarine',1.20,'BREAKFAST',1),
(41,'Wasser - Still','1L Flasche',2.00,'BEVERAGE_NON_ALCOHOLIC',1),
(42,'Wasser - Medium','1L Flasche',2.00,'BEVERAGE_NON_ALCOHOLIC',1),
(43,'Wasser - Spritzig','1L Flasche',2.00,'BEVERAGE_NON_ALCOHOLIC',1),
(44,'Coca-Cola','1L Flasche',2.00,'BEVERAGE_NON_ALCOHOLIC',1),
(45,'Coca-Cola Zero','1L Flasche',2.00,'BEVERAGE_NON_ALCOHOLIC',1),
(46,'Fanta','1L Flasche',2.00,'BEVERAGE_NON_ALCOHOLIC',1),
(47,'Sprite','1L Flasche',2.00,'BEVERAGE_NON_ALCOHOLIC',1),
(48,'Spezi','von Paulaner, 0,5L Flasche',1.50,'BEVERAGE_NON_ALCOHOLIC',1),
(49,'Red Bull','',2.00,'BEVERAGE_NON_ALCOHOLIC',1),
(50,'Energy','Hausmarke',1.50,'BEVERAGE_NON_ALCOHOLIC',1),
(51,'Pils','0,33L Flasche',1.90,'BEVERAGE_ALCOHOLIC',1),
(52,'Radler','0,33L Flasche',1.90,'BEVERAGE_ALCOHOLIC',1),
(53,'Diesel','0,33L Flasche',1.90,'BEVERAGE_ALCOHOLIC',1),
(54,'Apfelwein Pur','0,33L Flasche',1.90,'BEVERAGE_ALCOHOLIC',1),
(55,'Apfelwein Sauer','0,33L Flasche',1.90,'BEVERAGE_ALCOHOLIC',1),
(56,'Apfelwein Cola','0,33L Flasche',1.90,'BEVERAGE_ALCOHOLIC',1),
(57,'Vodka Energy','',4.00,'BEVERAGE_COCKTAIL',1),
(58,'Vodka O-Saft','',4.00,'BEVERAGE_COCKTAIL',1),
(59,'Whiskey Cola','mit Bourbon',4.00,'BEVERAGE_COCKTAIL',1),
(60,'Jägermeister Energy','',4.00,'BEVERAGE_COCKTAIL',1),
(61,'Sex on the Beach','',5.50,'BEVERAGE_COCKTAIL',1),
(62,'Long Island Ice Tea','',5.50,'BEVERAGE_COCKTAIL',1),
(63,'Caipirinha','',5.50,'BEVERAGE_COCKTAIL',1),
(64,'Jägermeister','',2.00,'BEVERAGE_SHOT',1),
(65,'Tequila','',2.00,'BEVERAGE_SHOT',1),
(66,'Pfeffi','Pfefferminz-Schnaps',1.50,'BEVERAGE_SHOT',1),
(67,'Zigaretten','Elixyr',8.00,'NON_FOOD',1),
(68,'Mentholfilter','passend für Elixyr',1.20,'NON_FOOD',1);

View File

@ -1,15 +1,15 @@
CREATE DATABASE IF NOT EXISTS `ezgg_lan_manager` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */;
USE `ezgg_lan_manager`;
-- MySQL dump 10.13 Distrib 5.7.24, for Linux (x86_64)
CREATE DATABASE IF NOT EXISTS `ez_lan_manager` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */;
USE `ez_lan_manager`;
-- MySQL dump 10.13 Distrib 8.0.41, for Win64 (x86_64)
--
-- Host: 127.0.0.1 Database: ezgg_lan_manager
-- Host: 127.0.0.1 Database: ez_lan_manager
-- ------------------------------------------------------
-- Server version 5.5.5-10.11.8-MariaDB-0ubuntu0.24.04.1
-- Server version 5.5.5-10.4.32-MariaDB
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!50503 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
@ -23,7 +23,7 @@ USE `ezgg_lan_manager`;
DROP TABLE IF EXISTS `catering_menu_items`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `catering_menu_items` (
`catering_menu_item_id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) NOT NULL,
@ -32,7 +32,7 @@ CREATE TABLE `catering_menu_items` (
`category` varchar(80) NOT NULL,
`is_disabled` tinyint(4) DEFAULT 0,
PRIMARY KEY (`catering_menu_item_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
) ENGINE=InnoDB AUTO_INCREMENT=69 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@ -41,7 +41,7 @@ CREATE TABLE `catering_menu_items` (
DROP TABLE IF EXISTS `news`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `news` (
`news_id` int(11) NOT NULL AUTO_INCREMENT,
`news_content` text DEFAULT NULL,
@ -52,7 +52,7 @@ CREATE TABLE `news` (
PRIMARY KEY (`news_id`),
KEY `user_is_idx` (`news_author`),
CONSTRAINT `user_is` FOREIGN KEY (`news_author`) REFERENCES `users` (`user_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@ -61,7 +61,7 @@ CREATE TABLE `news` (
DROP TABLE IF EXISTS `order_catering_menu_item`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `order_catering_menu_item` (
`order_id` int(11) NOT NULL,
`catering_menu_item_id` int(11) NOT NULL,
@ -79,7 +79,7 @@ CREATE TABLE `order_catering_menu_item` (
DROP TABLE IF EXISTS `orders`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `orders` (
`order_id` int(11) NOT NULL AUTO_INCREMENT,
`status` varchar(45) NOT NULL,
@ -87,7 +87,7 @@ CREATE TABLE `orders` (
`order_date` datetime NOT NULL DEFAULT current_timestamp(),
`is_delivery` tinyint(4) NOT NULL DEFAULT 1,
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@ -96,7 +96,7 @@ CREATE TABLE `orders` (
DROP TABLE IF EXISTS `seats`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `seats` (
`seat_id` varchar(5) NOT NULL,
`is_blocked` tinyint(4) NOT NULL DEFAULT 0,
@ -113,7 +113,7 @@ CREATE TABLE `seats` (
DROP TABLE IF EXISTS `tickets`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `tickets` (
`ticket_id` int(11) NOT NULL AUTO_INCREMENT,
`ticket_category` varchar(45) NOT NULL,
@ -122,7 +122,40 @@ CREATE TABLE `tickets` (
PRIMARY KEY (`ticket_id`),
KEY `user_id_idx` (`user`),
CONSTRAINT `user` FOREIGN KEY (`user`) REFERENCES `users` (`user_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `tournament_participants`
--
DROP TABLE IF EXISTS `tournament_participants`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `tournament_participants` (
`tournament_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`registered_at_timestamp` varchar(45) NOT NULL DEFAULT 'CURRENT_TIMESTAMP',
PRIMARY KEY (`tournament_id`,`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `tournaments`
--
DROP TABLE IF EXISTS `tournaments`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `tournaments` (
`tournament_id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) NOT NULL,
`additional_info` varchar(300) DEFAULT NULL,
`start_time` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
`participants_limit` int(4) DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT current_timestamp(),
PRIMARY KEY (`tournament_id`,`start_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@ -131,7 +164,7 @@ CREATE TABLE `tickets` (
DROP TABLE IF EXISTS `transactions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `transactions` (
`transaction_id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
@ -143,7 +176,7 @@ CREATE TABLE `transactions` (
UNIQUE KEY `transaction_id_UNIQUE` (`transaction_id`),
KEY `user_id_idx` (`user_id`),
CONSTRAINT `user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
@ -152,7 +185,7 @@ CREATE TABLE `transactions` (
DROP TABLE IF EXISTS `user_profile_picture`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `user_profile_picture` (
`user_id` int(11) NOT NULL,
`picture` mediumblob DEFAULT NULL,
@ -167,7 +200,7 @@ CREATE TABLE `user_profile_picture` (
DROP TABLE IF EXISTS `users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `users` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`user_name` varchar(50) NOT NULL,
@ -185,7 +218,7 @@ CREATE TABLE `users` (
UNIQUE KEY `user_id_UNIQUE` (`user_id`),
UNIQUE KEY `user_mail_UNIQUE` (`user_mail`),
UNIQUE KEY `user_name_UNIQUE` (`user_name`)
) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
@ -197,4 +230,4 @@ CREATE TABLE `users` (
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2024-08-25 22:37:14
-- Dump completed on 2025-02-09 21:52:04

View File

@ -1,61 +0,0 @@
INSERT INTO `seats` (`seat_id`, `is_blocked`, `seat_category`) VALUES
('A01', '0', 'NORMAL'),
('A02', '0', 'NORMAL'),
('A03', '0', 'NORMAL'),
('A04', '0', 'NORMAL'),
('A05', '0', 'NORMAL'),
('A06', '0', 'NORMAL'),
('A10', '0', 'NORMAL'),
('A11', '0', 'NORMAL'),
('A12', '0', 'NORMAL'),
('A13', '0', 'NORMAL'),
('A14', '0', 'NORMAL'),
('A15', '0', 'NORMAL'),
('B01', '0', 'NORMAL'),
('B02', '0', 'NORMAL'),
('B03', '0', 'NORMAL'),
('B04', '0', 'NORMAL'),
('B05', '0', 'NORMAL'),
('B10', '0', 'NORMAL'),
('B11', '0', 'NORMAL'),
('B12', '0', 'NORMAL'),
('B13', '0', 'NORMAL'),
('B14', '0', 'NORMAL'),
('C01', '0', 'NORMAL'),
('C02', '0', 'NORMAL'),
('C03', '0', 'NORMAL'),
('C04', '0', 'NORMAL'),
('C05', '0', 'NORMAL'),
('C10', '0', 'NORMAL'),
('C11', '0', 'NORMAL'),
('C12', '0', 'NORMAL'),
('C13', '0', 'NORMAL'),
('C14', '0', 'NORMAL'),
('D01', '0', 'NORMAL'),
('D02', '0', 'NORMAL'),
('D03', '0', 'NORMAL'),
('D04', '0', 'NORMAL'),
('D05', '0', 'NORMAL'),
('D06', '0', 'NORMAL'),
('D10', '0', 'NORMAL'),
('D11', '0', 'NORMAL'),
('D12', '0', 'NORMAL'),
('D13', '0', 'NORMAL'),
('D14', '0', 'NORMAL'),
('D15', '0', 'NORMAL'),
('E01', '0', 'NORMAL'),
('E02', '0', 'NORMAL'),
('E03', '0', 'NORMAL'),
('E04', '0', 'NORMAL'),
('E05', '0', 'NORMAL'),
('E06', '0', 'NORMAL'),
('E10', '0', 'NORMAL'),
('E11', '0', 'NORMAL'),
('E12', '0', 'NORMAL'),
('E13', '0', 'NORMAL'),
('E14', '0', 'NORMAL'),
('E15', '0', 'NORMAL');

View File

@ -8,13 +8,13 @@ from pathlib import Path
from rio import App, Theme, Color, Font, ComponentPage, Session
from from_root import from_root
from src.ezgg_lan_manager import pages, init_services
from src.ezgg_lan_manager.helpers.LoggedInGuard import logged_in_guard, not_logged_in_guard, team_guard
from src.ezgg_lan_manager.services.DatabaseService import NoDatabaseConnectionError
from src.ezgg_lan_manager.services.LocalDataService import LocalData
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ez_lan_manager import pages, init_services
from src.ez_lan_manager.helpers.LoggedInGuard import logged_in_guard, not_logged_in_guard, team_guard
from src.ez_lan_manager.services.DatabaseService import NoDatabaseConnectionError
from src.ez_lan_manager.services.LocalDataService import LocalData
from src.ez_lan_manager.types.SessionStorage import SessionStorage
logger = logging.getLogger("EzggLanManager")
logger = logging.getLogger("EzLanManager")
if __name__ == "__main__":
theme = Theme.from_colors(
@ -28,7 +28,7 @@ if __name__ == "__main__":
corner_radius_small=0,
corner_radius_medium=0,
corner_radius_large=0,
font=Font(from_root("src/ezgg_lan_manager/assets/fonts/joystix.otf"))
font=Font(from_root("src/ez_lan_manager/assets/fonts/joystix.otf"))
)
default_attachments = [LocalData()]
default_attachments.extend(init_services())
@ -46,7 +46,7 @@ if __name__ == "__main__":
sys.exit(1)
app = App(
name="EZGG LAN Manager",
name="EZ LAN Manager",
build=pages.BasePage,
pages=[
ComponentPage(
@ -62,7 +62,7 @@ if __name__ == "__main__":
ComponentPage(
name="Overview",
url_segment="overview",
build=pages.OverviewPage,
build=lambda: pages.PlaceholderPage(placeholder_name="LAN Übersicht"),
),
ComponentPage(
name="BuyTicket",
@ -168,13 +168,13 @@ if __name__ == "__main__":
default_attachments=default_attachments,
on_session_start=on_session_start,
on_app_start=on_app_start,
icon=from_root("src/ezgg_lan_manager/assets/img/favicon.png"),
icon=from_root("src/ez_lan_manager/assets/img/favicon.png"),
meta_tags={
"robots": "INDEX,FOLLOW",
"description": f"Info und Verwaltungs-Seite der LAN Party '{lan_info.name} - {lan_info.iteration}'.",
"og:description": f"Info und Verwaltungs-Seite der LAN Party '{lan_info.name} - {lan_info.iteration}'.",
"keywords": "Gaming, Clan, Guild, Verein, Club, Einfach, Zocken, Gesellschaft, Videospiele, "
"Videogames, LAN, Party, EZ, EZGG, LAN, Manager",
"Videogames, LAN, Party, EZ, LAN, Manager",
"author": "David Rodenkirchen",
"publisher": "EZ GG e.V.",
"copyright": "EZ GG e.V.",
@ -186,7 +186,4 @@ if __name__ == "__main__":
}
)
sys.exit(app.run_as_web_server(
host="0.0.0.0",
port=8000,
))
sys.exit(app.run_as_web_server())

View File

@ -0,0 +1,36 @@
import logging
from from_root import from_root
from src.ez_lan_manager.services import *
from src.ez_lan_manager.services.AccountingService import AccountingService
from src.ez_lan_manager.services.CateringService import CateringService
from src.ez_lan_manager.services.ConfigurationService import ConfigurationService
from src.ez_lan_manager.services.DatabaseService import DatabaseService
from src.ez_lan_manager.services.LocalDataService import LocalDataService
from src.ez_lan_manager.services.MailingService import MailingService
from src.ez_lan_manager.services.NewsService import NewsService
from src.ez_lan_manager.services.SeatingService import SeatingService
from src.ez_lan_manager.services.TicketingService import TicketingService
from src.ez_lan_manager.services.TournamentService import TournamentService
from src.ez_lan_manager.services.UserService import UserService
from src.ez_lan_manager.types import *
# Inits services in the correct order
def init_services() -> tuple[AccountingService, CateringService, ConfigurationService, DatabaseService, MailingService, NewsService, SeatingService, TicketingService, UserService, LocalDataService, TournamentService]:
logging.basicConfig(level=logging.DEBUG)
configuration_service = ConfigurationService(from_root("config.toml"))
db_service = DatabaseService(configuration_service.get_database_configuration())
user_service = UserService(db_service)
accounting_service = AccountingService(db_service)
news_service = NewsService(db_service)
mailing_service = MailingService(configuration_service)
ticketing_service = TicketingService(configuration_service.get_ticket_info(), db_service, accounting_service)
tournament_service = TournamentService(db_service)
seating_service = SeatingService(configuration_service.get_seating_configuration(),
configuration_service.get_lan_info(), db_service, ticketing_service)
catering_service = CateringService(db_service, accounting_service, user_service)
local_data_service = LocalDataService()
return accounting_service, catering_service, configuration_service, db_service, mailing_service, news_service, seating_service, ticketing_service, user_service, local_data_service, tournament_service

View File

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 100 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -4,7 +4,7 @@ from decimal import Decimal
import rio
from rio import Component, Row, Text, IconButton, TextStyle
from src.ezgg_lan_manager import AccountingService
from src.ez_lan_manager import AccountingService
MAX_LEN = 24

View File

@ -3,9 +3,9 @@ from typing import Optional, Callable
from rio import Component, Row, Card, Column, Text, TextStyle, Spacer, PointerEventListener, Button
from src.ezgg_lan_manager.services.CateringService import CateringService
from src.ezgg_lan_manager.types.CateringOrder import CateringOrder, CateringOrderStatus
from src.ezgg_lan_manager.types.Seat import Seat
from src.ez_lan_manager.services.CateringService import CateringService
from src.ez_lan_manager.types.CateringOrder import CateringOrder, CateringOrderStatus
from src.ez_lan_manager.types.Seat import Seat
class CateringManagementOrderDisplayStatusButton(Component):
status: CateringOrderStatus

View File

@ -3,7 +3,7 @@ from typing import Callable
from rio import Component, Row, Text, TextStyle, Color, Rectangle, CursorStyle
from rio.components.pointer_event_listener import PointerEvent, PointerEventListener
from src.ezgg_lan_manager.types.CateringOrder import CateringOrderStatus, CateringOrder
from src.ez_lan_manager.types.CateringOrder import CateringOrderStatus, CateringOrder
MAX_LEN = 24

View File

@ -4,7 +4,7 @@ from typing import Callable
import rio
from rio import Component, Row, Text, IconButton, TextStyle, Column, Spacer, Card, Color
from src.ezgg_lan_manager import AccountingService
from src.ez_lan_manager import AccountingService
MAX_LEN = 24

View File

@ -3,12 +3,12 @@ from typing import Optional, Callable
from rio import *
from src.ezgg_lan_manager import ConfigurationService, UserService, LocalDataService
from src.ezgg_lan_manager.components.DesktopNavigationButton import DesktopNavigationButton
from src.ezgg_lan_manager.components.UserInfoAndLoginBox import UserInfoAndLoginBox
from src.ezgg_lan_manager.services.LocalDataService import LocalData
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ezgg_lan_manager.types.User import User
from src.ez_lan_manager import ConfigurationService, UserService, LocalDataService
from src.ez_lan_manager.components.DesktopNavigationButton import DesktopNavigationButton
from src.ez_lan_manager.components.UserInfoAndLoginBox import UserInfoAndLoginBox
from src.ez_lan_manager.services.LocalDataService import LocalData
from src.ez_lan_manager.types.SessionStorage import SessionStorage
from src.ez_lan_manager.types.User import User
class DesktopNavigation(Component):
@ -54,7 +54,7 @@ class DesktopNavigation(Component):
DesktopNavigationButton("FAQ", "./faq"),
DesktopNavigationButton("Regeln & AGB", "./rules-gtc"),
Spacer(min_height=1),
DesktopNavigationButton("Discord", "https://discord.gg/8gTjg34yyH", open_new_tab=True),
DesktopNavigationButton("Discord", "#", open_new_tab=True), # Temporarily disabled: https://discord.gg/8gTjg34yyH
DesktopNavigationButton("Die EZ GG e.V.", "https://ezgg-ev.de/about", open_new_tab=True),
DesktopNavigationButton("Kontakt", "./contact"),
DesktopNavigationButton("Impressum & DSGVO", "./imprint"),
@ -79,7 +79,7 @@ class DesktopNavigation(Component):
return Card(
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=2.5)),
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,
*nav_to_use,

View File

@ -1,14 +1,15 @@
from rio import Component, TextStyle, Color, TextInput, Button, Text, Rectangle, Column, Row, Spacer, \
EventHandler
from src.ezgg_lan_manager.services.LocalDataService import LocalDataService, LocalData
from src.ezgg_lan_manager.services.UserService import UserService
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ezgg_lan_manager.types.User import User
from src.ez_lan_manager.services.LocalDataService import LocalDataService, LocalData
from src.ez_lan_manager.services.UserService import UserService
from src.ez_lan_manager.types.SessionStorage import SessionStorage
from src.ez_lan_manager.types.User import User
class LoginBox(Component):
status_change_cb: EventHandler = None
TEXT_STYLE = TextStyle(fill=Color.from_hex("02dac5"), font_size=0.9)
user_name_input_text: str = ""
password_input_text: str = ""
user_name_input_is_valid = True
@ -56,7 +57,7 @@ class LoginBox(Component):
is_valid=self.password_input_is_valid
)
login_button = Button(
Text("LOGIN", fill=Color.from_hex("02dac5"), style=TextStyle(font_size=0.9), justify="center"),
Text("LOGIN", style=self.TEXT_STYLE, justify="center"),
shape="rectangle",
style="minor",
color="secondary",
@ -64,14 +65,14 @@ class LoginBox(Component):
on_press=self._on_login_pressed
)
register_button = Button(
Text("REG", fill=Color.from_hex("02dac5"), style=TextStyle(font_size=0.9), justify="center"),
Text("REG", style=self.TEXT_STYLE, justify="center"),
shape="rectangle",
style="minor",
color="secondary",
on_press=lambda: self.session.navigate_to("./register")
)
forgot_password_button = Button(
Text("LST PWD", fill=Color.from_hex("02dac5"), style=TextStyle(font_size=0.9), justify="center"),
Text("LST PWD", style=self.TEXT_STYLE, justify="center"),
shape="rectangle",
style="minor",
color="secondary",
@ -94,7 +95,7 @@ class LoginBox(Component):
),
margin_bottom=0.5
),
Text(text="Dieses Konto\nist gesperrt", fill=self.session.theme.danger_color, style=TextStyle(font_size=0.9 if self.is_account_locked else 0), align_x=0.5),
Text(text="Dieses Konto\nist gesperrt", style=TextStyle(fill=self.session.theme.danger_color, font_size=0.9 if self.is_account_locked else 0), align_x=0.5),
spacing=0.4
),
fill=Color.TRANSPARENT,

View File

@ -4,8 +4,8 @@ from typing import Optional
from rio import Component, Column, NumberInput, ThemeContextSwitcher, TextInput, Row, Button, EventHandler
from src.ezgg_lan_manager.types.Transaction import Transaction
from src.ezgg_lan_manager.types.User import User
from src.ez_lan_manager.types.Transaction import Transaction
from src.ez_lan_manager.types.User import User
class NewTransactionForm(Component):
@ -46,6 +46,7 @@ class NewTransactionForm(Component):
label="Betrag",
suffix_text="",
decimals=2,
thousands_separator=".",
margin=1,
margin_bottom=0
),

View File

@ -21,8 +21,8 @@ class NewsPost(Component):
grow_x=True,
margin=2,
margin_bottom=0,
fill=self.session.theme.background_color,
style=TextStyle(
fill=self.session.theme.background_color,
font_size=1.3
),
overflow="ellipsize"
@ -31,8 +31,8 @@ class NewsPost(Component):
self.date,
margin=2,
align_x=1,
fill=self.session.theme.background_color,
style=TextStyle(
fill=self.session.theme.background_color,
font_size=0.6
),
overflow="wrap"
@ -44,8 +44,8 @@ class NewsPost(Component):
margin=2,
margin_top=0,
margin_bottom=0,
fill=self.session.theme.background_color,
style=TextStyle(
fill=self.session.theme.background_color,
font_size=0.8
),
overflow="ellipsize"
@ -53,7 +53,9 @@ class NewsPost(Component):
Text(
self.text,
margin=2,
fill=self.session.theme.background_color,
style=TextStyle(
fill=self.session.theme.background_color
),
overflow="wrap"
),
Text(
@ -63,8 +65,8 @@ class NewsPost(Component):
margin=2,
margin_top=0,
margin_bottom=1,
fill=self.session.theme.background_color,
style=TextStyle(
fill=self.session.theme.background_color,
font_size=0.5,
italic=True
),

View File

@ -0,0 +1,194 @@
from typing import Callable
from rio import Component, Rectangle, Grid, Column, Row, Text, TextStyle, Color
from src.ez_lan_manager.components.SeatingPlanPixels import SeatPixel, WallPixel, InvisiblePixel, TextPixel
from src.ez_lan_manager.types.Seat import Seat
MAX_GRID_WIDTH_PIXELS = 34
MAX_GRID_HEIGHT_PIXELS = 45
class SeatingPlanLegend(Component):
def build(self) -> Component:
return Column(
Text("Legende", style=TextStyle(fill=self.session.theme.neutral_color), justify="center", margin=1),
Row(
Text("L = Luxus Platz", justify="center", style=TextStyle(fill=self.session.theme.neutral_color)),
Text("N = Normaler Platz", justify="center", style=TextStyle(fill=self.session.theme.neutral_color)),
),
Row(
Rectangle(
content=Column(
Text(f"Freier Platz", style=TextStyle(fill=self.session.theme.primary_color, font_size=0.7), align_x=0.5, selectable=False),
Text(f"", style=TextStyle(fill=self.session.theme.primary_color, font_size=0.9), align_x=0.5,
selectable=False, overflow="wrap")
),
min_width=1,
min_height=1,
fill=self.session.theme.success_color,
grow_x=False,
grow_y=False,
hover_fill=self.session.theme.success_color,
transition_time=0.4,
ripple=True
),
Rectangle(
content=Column(
Text(f"Belegter Platz", style=TextStyle(fill=self.session.theme.primary_color, font_size=0.7), align_x=0.5, selectable=False),
Text(f"", style=TextStyle(fill=self.session.theme.primary_color, font_size=0.9), align_x=0.5,
selectable=False, overflow="wrap")
),
min_width=1,
min_height=1,
fill=self.session.theme.danger_color,
grow_x=False,
grow_y=False,
hover_fill=self.session.theme.danger_color,
transition_time=0.4,
ripple=True
),
Rectangle(
content=Column(
Text(f"Eigener Platz", style=TextStyle(fill=self.session.theme.primary_color, font_size=0.7), align_x=0.5, selectable=False),
Text(f"", style=TextStyle(fill=self.session.theme.primary_color, font_size=0.9), align_x=0.5,
selectable=False, overflow="wrap")
),
min_width=1,
min_height=1,
fill=Color.from_hex("800080"),
grow_x=False,
grow_y=False,
hover_fill=Color.from_hex("800080"),
transition_time=0.4,
ripple=True
),
margin=1,
spacing=1
)
)
class SeatingPlan(Component):
seat_clicked_cb: Callable
seating_info: list[Seat]
def get_seat(self, seat_id: str) -> Seat:
return next(filter(lambda seat: seat.seat_id == seat_id, self.seating_info))
"""
This seating plan is for the community center "Bottenhorn"
"""
def build(self) -> Component:
grid = Grid()
# Outlines
for column_id in range(0, MAX_GRID_WIDTH_PIXELS):
grid.add(InvisiblePixel(), row=0, column=column_id)
for y in range(0, 13):
grid.add(WallPixel(), row=y, column=0)
for y in range(13, 19):
grid.add(InvisiblePixel(), row=y, column=0)
for y in range(19, MAX_GRID_HEIGHT_PIXELS):
grid.add(WallPixel(), row=y, column=0)
# Block A
block_a_margin_left = 12
block_a_margin_top = 1
(grid
.add(SeatPixel("A01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A01")), row=block_a_margin_top, column=block_a_margin_left, width=2, height=3)
.add(SeatPixel("A02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A02")), row=block_a_margin_top + 4, column=block_a_margin_left, width=2, height=3)
.add(SeatPixel("A03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A03")), row=block_a_margin_top + 8, column=block_a_margin_left, width=2, height=3)
.add(SeatPixel("A10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A10")), row=block_a_margin_top, column=block_a_margin_left + 3, width=2, height=3)
.add(SeatPixel("A11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A11")), row=block_a_margin_top + 4, column=block_a_margin_left + 3, width=2, height=3)
.add(SeatPixel("A12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("A12")), row=block_a_margin_top + 8, column=block_a_margin_left + 3, width=2, height=3)
)
# Block B
block_b_margin_left = 20
block_b_margin_top = 1
(grid
.add(SeatPixel("B01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B01")), row=block_b_margin_top, column=block_b_margin_left, width=2, height=3)
.add(SeatPixel("B02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B02")), row=block_b_margin_top + 4, column=block_b_margin_left, width=2, height=3)
.add(SeatPixel("B03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B03")), row=block_b_margin_top + 8, column=block_b_margin_left, width=2, height=3)
.add(SeatPixel("B10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B10")), row=block_b_margin_top, column=block_b_margin_left + 3, width=2, height=3)
.add(SeatPixel("B11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B11")), row=block_b_margin_top + 4, column=block_b_margin_left + 3, width=2, height=3)
.add(SeatPixel("B12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("B12")), row=block_b_margin_top + 8, column=block_b_margin_left + 3, width=2, height=3)
)
# Block C
block_c_margin_left = 28
block_c_margin_top = 1
(grid
.add(SeatPixel("C01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C01")), row=block_c_margin_top, column=block_c_margin_left, width=2, height=3)
.add(SeatPixel("C02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C02")), row=block_c_margin_top + 4, column=block_c_margin_left, width=2, height=3)
.add(SeatPixel("C03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C03")), row=block_c_margin_top + 8, column=block_c_margin_left, width=2, height=3)
.add(SeatPixel("C10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C10")), row=block_c_margin_top, column=block_c_margin_left + 3, width=2, height=3)
.add(SeatPixel("C11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C11")), row=block_c_margin_top + 4, column=block_c_margin_left + 3, width=2, height=3)
.add(SeatPixel("C12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("C12")), row=block_c_margin_top + 8, column=block_c_margin_left + 3, width=2, height=3)
)
# Block D
block_d_margin_left = 20
block_d_margin_top = 20
(grid
.add(SeatPixel("D01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D01")), row=block_d_margin_top, column=block_d_margin_left, width=2, height=3)
.add(SeatPixel("D02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D02")), row=block_d_margin_top + 4, column=block_d_margin_left, width=2, height=3)
.add(SeatPixel("D03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D03")), row=block_d_margin_top + 8, column=block_d_margin_left, width=2, height=3)
.add(SeatPixel("D10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D10")), row=block_d_margin_top, column=block_d_margin_left + 3, width=2, height=3)
.add(SeatPixel("D11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D11")), row=block_d_margin_top + 4, column=block_d_margin_left + 3, width=2, height=3)
.add(SeatPixel("D12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("D12")), row=block_d_margin_top + 8, column=block_d_margin_left + 3, width=2, height=3)
)
# Block E
block_e_margin_left = 28
block_e_margin_top = 20
(grid
.add(SeatPixel("E01", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E01")), row=block_e_margin_top, column=block_e_margin_left, width=2, height=3)
.add(SeatPixel("E02", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E02")), row=block_e_margin_top + 4, column=block_e_margin_left, width=2, height=3)
.add(SeatPixel("E03", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E03")), row=block_e_margin_top + 8, column=block_e_margin_left, width=2, height=3)
.add(SeatPixel("E10", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E10")), row=block_e_margin_top, column=block_e_margin_left + 3, width=2, height=3)
.add(SeatPixel("E11", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E11")), row=block_e_margin_top + 4, column=block_e_margin_left + 3, width=2, height=3)
.add(SeatPixel("E12", on_press_cb=self.seat_clicked_cb, seat=self.get_seat("E12")), row=block_e_margin_top + 8, column=block_e_margin_left + 3, width=2, height=3)
)
# Middle Wall
for y in range(0, 13):
grid.add(WallPixel(), row=y, column=10)
for y in range(19, MAX_GRID_HEIGHT_PIXELS):
grid.add(WallPixel(), row=y, column=10)
# Stage
for x in range(11, MAX_GRID_WIDTH_PIXELS):
grid.add(WallPixel(), row=35, column=x)
grid.add(TextPixel(text="Bühne"), row=36, column=11, width=24, height=9)
# Drinks
grid.add(TextPixel(text="G\ne\nt\nr\nä\nn\nk\ne"), row=21, column=11, width=3, height=11)
# Sleeping
grid.add(TextPixel(icon_name="material/bed"), row=1, column=1, width=4, height=11)
# Toilet
grid.add(TextPixel(icon_name="material/floor", no_outline=True), row=1, column=7, width=3, height=2)
grid.add(TextPixel(icon_name="material/north", no_outline=True), row=3, column=7, width=3, height=2)
grid.add(TextPixel(icon_name="material/wc"), row=5, column=7, width=3, height=2)
# Entry/Helpdesk
grid.add(TextPixel(text="Einlass\n &Orga"), row=19, column=3, width=7, height=5)
# Wall below Entry/Helpdesk
for y in range(24, MAX_GRID_HEIGHT_PIXELS):
grid.add(WallPixel(), row=y, column=3)
# Entry Arrow
grid.add(TextPixel(icon_name="material/east", no_outline=True), row=15, column=1, width=2, height=2)
return Rectangle(
content=grid,
grow_x=True,
grow_y=True,
stroke_color=self.session.theme.neutral_color,
stroke_width=0.1,
fill=self.session.theme.primary_color,
margin=0.5
)

View File

@ -1,11 +1,7 @@
from decimal import Decimal
from functools import partial
from typing import Optional, Callable
from rio import Component, Column, Text, TextStyle, Button, Spacer, event
from src.ezgg_lan_manager import TicketingService
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from rio import Component, Column, Text, TextStyle, Button, Spacer
class SeatingPlanInfoBox(Component):
@ -16,30 +12,8 @@ class SeatingPlanInfoBox(Component):
seat_occupant: Optional[str] = None
seat_price: Decimal = Decimal("0")
is_blocked: bool = False
has_user_ticket: bool = False
booking_button_text: str = ""
override_text: str = "" # If this is set, all other functionality is disabled and the text is shown
@event.on_populate
async def check_ticket(self) -> None:
if self.session[SessionStorage].user_id:
user_ticket = await self.session[TicketingService].get_user_ticket(self.session[SessionStorage].user_id)
self.has_user_ticket = not (user_ticket is None)
self.booking_button_text = "Buchen" if self.has_user_ticket else "Ticket kaufen"
self.force_refresh()
async def purchase_clicked(self):
if self.has_user_ticket:
await self.purchase_cb()
else:
self.session.navigate_to("./buy_ticket")
def build(self) -> Component:
if self.override_text:
return Column(Text(self.override_text, margin=1,
style=TextStyle(fill=self.session.theme.neutral_color, font_size=1.4), overflow="wrap",
justify="center"), min_height=10)
if not self.show:
return Spacer()
if self.is_blocked:
@ -62,7 +36,7 @@ class SeatingPlanInfoBox(Component):
style=TextStyle(fill=self.session.theme.neutral_color), overflow="wrap", justify="center"),
Button(
Text(
text=self.booking_button_text,
f"Buchen",
margin=1,
style=TextStyle(fill=self.session.theme.neutral_color, font_size=1.1),
overflow="wrap",
@ -74,10 +48,7 @@ class SeatingPlanInfoBox(Component):
margin=1,
grow_y=False,
is_sensitive=not self.is_booking_blocked,
on_press=self.purchase_clicked
) if self.session[SessionStorage].user_id else Text(f"Du musst eingeloggt sein um einen Sitzplatz zu buchen",
margin=1,
style=TextStyle(fill=self.session.theme.neutral_color),
overflow="wrap", justify="center"),
on_press=self.purchase_cb
),
min_height=10
)

View File

@ -1,17 +1,16 @@
from functools import partial
from rio import Component, Text, Icon, TextStyle, Rectangle, Spacer, Color, PointerEventListener, Column, Row, PointerEvent, Tooltip
from typing import Optional, Callable, Literal
from rio import Component, Text, Icon, TextStyle, Rectangle, Spacer, Color, PointerEventListener, Column
from typing import Optional, Callable
from src.ezgg_lan_manager.types.Seat import Seat
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ez_lan_manager.types.Seat import Seat
from src.ez_lan_manager.types.SessionStorage import SessionStorage
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:
@ -21,37 +20,24 @@ class SeatPixel(Component):
return self.session.theme.success_color
def build(self) -> Component:
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_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
)
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,
return PointerEventListener(
content=Rectangle(
content=Column(
Text(f"{self.seat_id}", style=TextStyle(fill=self.session.theme.primary_color, font_size=0.7), align_x=0.5, selectable=False),
Text(f"{self.seat.category[0]}", style=TextStyle(fill=self.session.theme.primary_color, font_size=0.9), align_x=0.5, selectable=False, overflow="wrap")
),
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),
)
min_width=1,
min_height=1,
fill=self.determine_color(),
hover_stroke_width = 0.1,
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)
)
class TextPixel(Component):
text: Optional[str] = None
@ -72,14 +58,13 @@ 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(
@ -90,7 +75,6 @@ class WallPixel(Component):
grow_y=True,
)
class DebugPixel(Component):
def build(self) -> Component:
return Rectangle(
@ -98,15 +82,14 @@ 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(
@ -117,4 +100,4 @@ class InvisiblePixel(Component):
hover_stroke_width=0.0,
grow_x=True,
grow_y=True
)
)

View File

@ -2,14 +2,14 @@ from asyncio import sleep, create_task
from decimal import Decimal
import rio
from rio import Component, Column, Text, TextStyle, Button, Row, ScrollContainer, Spacer, Popup, Table, event
from rio import Component, Column, Text, TextStyle, Button, Row, ScrollContainer, Spacer, Popup, Table
from src.ezgg_lan_manager.components.CateringCartItem import CateringCartItem
from src.ezgg_lan_manager.components.CateringOrderItem import CateringOrderItem
from src.ezgg_lan_manager.services.AccountingService import AccountingService
from src.ezgg_lan_manager.services.CateringService import CateringService, CateringError, CateringErrorType
from src.ezgg_lan_manager.types.CateringOrder import CateringOrder, CateringMenuItemsWithAmount
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ez_lan_manager.components.CateringCartItem import CateringCartItem
from src.ez_lan_manager.components.CateringOrderItem import CateringOrderItem
from src.ez_lan_manager.services.AccountingService import AccountingService
from src.ez_lan_manager.services.CateringService import CateringService, CateringError, CateringErrorType
from src.ez_lan_manager.types.CateringOrder import CateringOrder, CateringMenuItemsWithAmount
from src.ez_lan_manager.types.SessionStorage import SessionStorage
POPUP_CLOSE_TIMEOUT_SECONDS = 3
@ -21,11 +21,6 @@ class ShoppingCartAndOrders(Component):
popup_is_shown: bool = False
popup_is_error: bool = True
@event.periodic(5)
async def periodic_refresh_of_orders(self) -> None:
if not self.show_cart and not self.popup_is_shown:
self.orders = await self.session[CateringService].get_orders_for_user(self.session[SessionStorage].user_id)
async def switch(self) -> None:
self.show_cart = not self.show_cart
self.orders = await self.session[CateringService].get_orders_for_user(self.session[SessionStorage].user_id)
@ -90,8 +85,7 @@ class ShoppingCartAndOrders(Component):
show_popup_task = create_task(self.show_popup("Guthaben nicht ausreichend", True))
else:
show_popup_task = create_task(self.show_popup("Unbekannter Fehler", True))
else:
self.session[CateringService].save_cart(self.session[SessionStorage].user_id, [])
self.session[CateringService].save_cart(self.session[SessionStorage].user_id, [])
self.order_button_loading = False
if not show_popup_task:
show_popup_task = create_task(self.show_popup("Bestellung erfolgreich aufgegeben!", False))
@ -130,7 +124,7 @@ class ShoppingCartAndOrders(Component):
dialog = await self.session.show_custom_dialog(
build=build_dialog_content,
modal=True,
user_closable=True,
user_closeable=True,
)
await dialog.wait_for_close()

View File

@ -5,9 +5,9 @@ from decimal import Decimal
import rio
from rio import Component, Card, Column, Text, Row, Button, TextStyle, ProgressBar, event, Spacer
from src.ezgg_lan_manager import TicketingService
from src.ezgg_lan_manager.services.AccountingService import AccountingService
from src.ezgg_lan_manager.types.Ticket import Ticket
from src.ez_lan_manager import TicketingService
from src.ez_lan_manager.services.AccountingService import AccountingService
from src.ez_lan_manager.types.Ticket import Ticket
class TicketBuyCard(Component):

View File

@ -7,10 +7,10 @@ from from_root import from_root
from rio import Component, Column, Button, Color, TextStyle, Text, TextInput, Row, Image, event, Spacer, DateInput, \
TextInputChangeEvent, NoFileSelectedError
from src.ezgg_lan_manager.services.UserService import UserService, NameNotAllowedError
from src.ezgg_lan_manager.services.ConfigurationService import ConfigurationService
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ezgg_lan_manager.types.User import User
from src.ez_lan_manager.services.UserService import UserService, NameNotAllowedError
from src.ez_lan_manager.services.ConfigurationService import ConfigurationService
from src.ez_lan_manager.types.SessionStorage import SessionStorage
from src.ez_lan_manager.types.User import User
class UserEditForm(Component):
@ -122,7 +122,7 @@ class UserEditForm(Component):
def build(self) -> Component:
pfp_image_container = Image(
from_root("src/ezgg_lan_manager/assets/img/anon_pfp.png") if self.profile_picture is None else self.profile_picture,
from_root("src/ez_lan_manager/assets/img/anon_pfp.png") if self.profile_picture is None else self.profile_picture,
align_x=0.5,
min_width=10,
min_height=10,

View File

@ -1,9 +1,9 @@
import logging
from rio import Component
from src.ezgg_lan_manager.components.LoginBox import LoginBox
from src.ezgg_lan_manager.components.UserInfoBox import UserInfoBox
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ez_lan_manager.components.LoginBox import LoginBox
from src.ez_lan_manager.components.UserInfoBox import UserInfoBox
from src.ez_lan_manager.types.SessionStorage import SessionStorage
logger = logging.getLogger(__name__.split(".")[-1])

View File

@ -4,16 +4,16 @@ from decimal import Decimal
from rio import Component, TextStyle, Color, Button, Text, Rectangle, Column, Row, Spacer, Link, event, EventHandler
from src.ezgg_lan_manager.components.UserInfoBoxButton import UserInfoBoxButton
from src.ezgg_lan_manager.services.LocalDataService import LocalData, LocalDataService
from src.ezgg_lan_manager.services.UserService import UserService
from src.ezgg_lan_manager.services.AccountingService import AccountingService
from src.ezgg_lan_manager.services.TicketingService import TicketingService
from src.ezgg_lan_manager.services.SeatingService import SeatingService
from src.ezgg_lan_manager.types.Seat import Seat
from src.ezgg_lan_manager.types.Ticket import Ticket
from src.ezgg_lan_manager.types.User import User
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ez_lan_manager.components.UserInfoBoxButton import UserInfoBoxButton
from src.ez_lan_manager.services.LocalDataService import LocalData, LocalDataService
from src.ez_lan_manager.services.UserService import UserService
from src.ez_lan_manager.services.AccountingService import AccountingService
from src.ez_lan_manager.services.TicketingService import TicketingService
from src.ez_lan_manager.services.SeatingService import SeatingService
from src.ez_lan_manager.types.Seat import Seat
from src.ez_lan_manager.types.Ticket import Ticket
from src.ez_lan_manager.types.User import User
from src.ez_lan_manager.types.SessionStorage import SessionStorage
class StatusButton(Component):
@ -73,8 +73,6 @@ class UserInfoBox(Component):
async def update(self) -> None:
if not self.user:
self.user = await self.session[UserService].get_user(self.session[SessionStorage].user_id)
if not self.user:
return
self.user_balance = await self.session[AccountingService].get_balance(self.user.user_id)
self.user_ticket = await self.session[TicketingService].get_user_ticket(self.user.user_id)
self.user_seat = await self.session[SeatingService].get_user_seat(self.user.user_id)

View File

@ -2,8 +2,8 @@ from typing import Optional
from rio import URL, GuardEvent
from src.ezgg_lan_manager.services.UserService import UserService
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ez_lan_manager.services.UserService import UserService
from src.ez_lan_manager.types.SessionStorage import SessionStorage
# Guards pages against access from users that are NOT logged in

View File

@ -5,14 +5,17 @@ from decimal import Decimal
import sys
from src.ezgg_lan_manager import init_services
from src.ezgg_lan_manager.types.CateringMenuItem import CateringMenuItemCategory
from src.ezgg_lan_manager.types.News import News
from src.ez_lan_manager import init_services
from src.ez_lan_manager.types.CateringMenuItem import CateringMenuItemCategory
from src.ez_lan_manager.types.Tournament import Tournament
from src.ez_lan_manager.types.News import News
DEMO_USERS = [
{"user_name": "manfred", "user_mail": "manfred@demomail.com", "password_clear_text": "manfred"}, # Gast
{"user_name": "gustav", "user_mail": "gustav@demomail.com", "password_clear_text": "gustav"}, # Gast + Ticket(NORMAL)
{"user_name": "jason", "user_mail": "juergen@demomail.com", "password_clear_text": "jason"}, # Gast + Ticket(NORMAL) + Sitzplatz
{"user_name": "gustav", "user_mail": "gustav@demomail.com", "password_clear_text": "gustav"},
# Gast + Ticket(NORMAL)
{"user_name": "jason", "user_mail": "juergen@demomail.com", "password_clear_text": "jason"},
# Gast + Ticket(NORMAL) + Sitzplatz
{"user_name": "lisa", "user_mail": "lisa@demomail.com", "password_clear_text": "lisa"}, # Teamler
{"user_name": "thomas", "user_mail": "thomas@demomail.com", "password_clear_text": "thomas"} # Teamler + Admin
]
@ -27,6 +30,7 @@ async def run() -> None:
ticket_service = services[7]
seating_service = services[6]
news_service = services[5]
tournament_service = services[10]
if input("Generate seating table? (y/N): ").lower() == "y":
sys.exit("This part of the script is currently being reworked... :(")
@ -71,37 +75,52 @@ async def run() -> None:
CateringMenuItemCategory.MAIN_COURSE)
await catering_service.add_menu_item("Tortellini in Käsesauce mit Fleischfüllung", "", Decimal("10.50"),
CateringMenuItemCategory.MAIN_COURSE)
await catering_service.add_menu_item("Tortellini in Käsesauce ohne Fleischfüllung", "Vegetarisch", Decimal("10.50"),
await catering_service.add_menu_item("Tortellini in Käsesauce ohne Fleischfüllung", "Vegetarisch",
Decimal("10.50"),
CateringMenuItemCategory.MAIN_COURSE)
# SNACK
await catering_service.add_menu_item("Käse Schinken Wrap", "", Decimal("5.00"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Puten Paprika Wrap", "", Decimal("7.00"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Tomate Mozzarella Wrap", "", Decimal("6.00"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Tomate Mozzarella Wrap", "", Decimal("6.00"),
CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Portion Pommes", "", Decimal("4.00"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Rinds-Currywurst", "", Decimal("4.50"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Rinds-Currywurst mit Pommes", "", Decimal("6.50"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Rinds-Currywurst mit Pommes", "", Decimal("6.50"),
CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Nudelsalat", "", Decimal("4.50"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Nudelsalat mit Bockwurst", "", Decimal("6.00"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Nudelsalat mit Bockwurst", "", Decimal("6.00"),
CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Kartoffelsalat", "", Decimal("4.50"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Kartoffelsalat mit Bockwurst", "", Decimal("6.00"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Sandwichtoast - Schinken", "", Decimal("1.80"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Sandwichtoast - Käse", "", Decimal("1.80"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Sandwichtoast - Schinken/Käse", "", Decimal("2.10"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Sandwichtoast - Salami", "", Decimal("1.80"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Sandwichtoast - Salami/Käse", "", Decimal("2.10"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Chips - Western Style", "", Decimal("1.30"), CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Kartoffelsalat mit Bockwurst", "", Decimal("6.00"),
CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Sandwichtoast - Schinken", "", Decimal("1.80"),
CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Sandwichtoast - Käse", "", Decimal("1.80"),
CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Sandwichtoast - Schinken/Käse", "", Decimal("2.10"),
CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Sandwichtoast - Salami", "", Decimal("1.80"),
CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Sandwichtoast - Salami/Käse", "", Decimal("2.10"),
CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Chips - Western Style", "", Decimal("1.30"),
CateringMenuItemCategory.SNACK)
await catering_service.add_menu_item("Nachos - Salted", "", Decimal("1.30"), CateringMenuItemCategory.SNACK)
# DESSERT
await catering_service.add_menu_item("Panna Cotta mit Erdbeersauce", "", Decimal("7.00"), CateringMenuItemCategory.DESSERT)
await catering_service.add_menu_item("Panna Cotta mit Blaubeersauce", "", Decimal("7.00"), CateringMenuItemCategory.DESSERT)
await catering_service.add_menu_item("Mousse au Chocolat", "", Decimal("7.00"), CateringMenuItemCategory.DESSERT)
await catering_service.add_menu_item("Panna Cotta mit Erdbeersauce", "", Decimal("7.00"),
CateringMenuItemCategory.DESSERT)
await catering_service.add_menu_item("Panna Cotta mit Blaubeersauce", "", Decimal("7.00"),
CateringMenuItemCategory.DESSERT)
await catering_service.add_menu_item("Mousse au Chocolat", "", Decimal("7.00"),
CateringMenuItemCategory.DESSERT)
# BREAKFAST
await catering_service.add_menu_item("Fruit Loops", "", Decimal("1.50"), CateringMenuItemCategory.BREAKFAST)
await catering_service.add_menu_item("Smacks", "", Decimal("1.50"), CateringMenuItemCategory.BREAKFAST)
await catering_service.add_menu_item("Knuspermüsli", "Schoko", Decimal("2.00"), CateringMenuItemCategory.BREAKFAST)
await catering_service.add_menu_item("Knuspermüsli", "Schoko", Decimal("2.00"),
CateringMenuItemCategory.BREAKFAST)
await catering_service.add_menu_item("Cini Minis", "", Decimal("2.50"), CateringMenuItemCategory.BREAKFAST)
await catering_service.add_menu_item("Brötchen - Schinken", "mit Margarine", Decimal("1.20"),
CateringMenuItemCategory.BREAKFAST)
@ -133,12 +152,14 @@ async def run() -> None:
CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC)
await catering_service.add_menu_item("Spezi", "von Paulaner, 0,5L Flasche", Decimal("1.50"),
CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC)
await catering_service.add_menu_item("Red Bull", "", Decimal("2.00"), CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC)
await catering_service.add_menu_item("Red Bull", "", Decimal("2.00"),
CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC)
await catering_service.add_menu_item("Energy", "Hausmarke", Decimal("1.50"),
CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC)
# BEVERAGE_ALCOHOLIC
await catering_service.add_menu_item("Pils", "0,33L Flasche", Decimal("1.90"), CateringMenuItemCategory.BEVERAGE_ALCOHOLIC)
await catering_service.add_menu_item("Pils", "0,33L Flasche", Decimal("1.90"),
CateringMenuItemCategory.BEVERAGE_ALCOHOLIC)
await catering_service.add_menu_item("Radler", "0,33L Flasche", Decimal("1.90"),
CateringMenuItemCategory.BEVERAGE_ALCOHOLIC)
await catering_service.add_menu_item("Diesel", "0,33L Flasche", Decimal("1.90"),
@ -151,17 +172,24 @@ async def run() -> None:
CateringMenuItemCategory.BEVERAGE_ALCOHOLIC)
# BEVERAGE_COCKTAIL
await catering_service.add_menu_item("Vodka Energy", "", Decimal("4.00"), CateringMenuItemCategory.BEVERAGE_COCKTAIL)
await catering_service.add_menu_item("Vodka O-Saft", "", Decimal("4.00"), CateringMenuItemCategory.BEVERAGE_COCKTAIL)
await catering_service.add_menu_item("Vodka Energy", "", Decimal("4.00"),
CateringMenuItemCategory.BEVERAGE_COCKTAIL)
await catering_service.add_menu_item("Vodka O-Saft", "", Decimal("4.00"),
CateringMenuItemCategory.BEVERAGE_COCKTAIL)
await catering_service.add_menu_item("Whiskey Cola", "mit Bourbon", Decimal("4.00"),
CateringMenuItemCategory.BEVERAGE_COCKTAIL)
await catering_service.add_menu_item("Jägermeister Energy", "", Decimal("4.00"), CateringMenuItemCategory.BEVERAGE_COCKTAIL)
await catering_service.add_menu_item("Sex on the Beach", "", Decimal("5.50"), CateringMenuItemCategory.BEVERAGE_COCKTAIL)
await catering_service.add_menu_item("Long Island Ice Tea", "", Decimal("5.50"), CateringMenuItemCategory.BEVERAGE_COCKTAIL)
await catering_service.add_menu_item("Caipirinha", "", Decimal("5.50"), CateringMenuItemCategory.BEVERAGE_COCKTAIL)
await catering_service.add_menu_item("Jägermeister Energy", "", Decimal("4.00"),
CateringMenuItemCategory.BEVERAGE_COCKTAIL)
await catering_service.add_menu_item("Sex on the Beach", "", Decimal("5.50"),
CateringMenuItemCategory.BEVERAGE_COCKTAIL)
await catering_service.add_menu_item("Long Island Ice Tea", "", Decimal("5.50"),
CateringMenuItemCategory.BEVERAGE_COCKTAIL)
await catering_service.add_menu_item("Caipirinha", "", Decimal("5.50"),
CateringMenuItemCategory.BEVERAGE_COCKTAIL)
# BEVERAGE_SHOT
await catering_service.add_menu_item("Jägermeister", "", Decimal("2.00"), CateringMenuItemCategory.BEVERAGE_SHOT)
await catering_service.add_menu_item("Jägermeister", "", Decimal("2.00"),
CateringMenuItemCategory.BEVERAGE_SHOT)
await catering_service.add_menu_item("Tequila", "", Decimal("2.00"), CateringMenuItemCategory.BEVERAGE_SHOT)
await catering_service.add_menu_item("PfEZzi", "Getunter Pfefferminz-Schnaps", Decimal("1.99"),
CateringMenuItemCategory.BEVERAGE_SHOT)
@ -185,15 +213,23 @@ async def run() -> None:
await news_service.add_news(News(
news_id=None,
title="Der EZGG LAN Manager",
title="Der EZ LAN Manager",
subtitle="Eine Software des EZ GG e.V.",
content="Dies ist eine WIP-Version des EZGG LAN Managers. Diese Software soll uns helfen in Zukunft die LAN "
content="Dies ist eine WIP-Version des EZ LAN Managers. Diese Software soll uns helfen in Zukunft die LAN "
"Parties des EZ GG e.V.'s zu organisieren. Wer Fehler findet darf sie behalten. (Oder er meldet "
"sie)",
author=user,
news_date=date.today()
))
if not input("Generate default tournament? (Y/n): ").lower() == "n":
await tournament_service.add_tournament(
name="League of Legends 1vs1",
additional_info="Normal 1vs1, 100 cs, first blood, first tower, aram-map",
start_time=date.today(),
participants_limit=30
)
if __name__ == "__main__":
with asyncio.Runner() as loop:

View File

@ -1,19 +1,18 @@
from decimal import Decimal
from functools import partial
from typing import Optional
from rio import Column, Component, event, Text, TextStyle, Button, Color, Revealer, Row, ProgressCircle, Link
from src.ezgg_lan_manager import ConfigurationService, UserService, AccountingService
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ezgg_lan_manager.types.Transaction import Transaction
from src.ezgg_lan_manager.types.User import User
from src.ez_lan_manager import ConfigurationService, UserService, AccountingService
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ez_lan_manager.types.SessionStorage import SessionStorage
from src.ez_lan_manager.types.Transaction import Transaction
from src.ez_lan_manager.types.User import User
class AccountPage(Component):
user: Optional[User] = None
balance: Optional[Decimal] = None
balance: Optional[int] = None
transaction_history: list[Transaction] = list()
banking_info_revealer_open: bool = False
paypal_info_revealer_open: bool = False

View File

@ -2,17 +2,14 @@ from __future__ import annotations
from typing import * # type: ignore
from rio import Component, event, Spacer, Card, Container, Column, Row, TextStyle, Color, Text, PageView, Button
from rio import Component, event, Spacer, Card, Container, Column, Row, TextStyle, Color, Text, PageView
from src.ezgg_lan_manager import ConfigurationService, DatabaseService
from src.ezgg_lan_manager.components.DesktopNavigation import DesktopNavigation
from src.ez_lan_manager import ConfigurationService, DatabaseService
from src.ez_lan_manager.components.DesktopNavigation import DesktopNavigation
class BasePage(Component):
color = "secondary"
corner_radius = (0, 0.5, 0, 0)
footer_size = 53.1
force_portrait_mode = False
@event.periodic(60)
async def check_db_conn(self) -> None:
is_healthy = await self.session[DatabaseService].is_healthy()
@ -23,20 +20,6 @@ class BasePage(Component):
async def on_window_size_change(self):
self.force_refresh()
@event.on_page_change
def check_needed_size(self):
# ToDo: Low-Prio: Change layout, so the footer is always as wide as needed.
# This is a workaround, bc the seating page needs more width
if "/seating" in self.session.active_page_url.__str__():
self.footer_size = 78.2
else:
self.footer_size = 53.1
self.force_refresh()
def enforce_portrait_mode(self) -> None:
self.force_portrait_mode = True
self.force_refresh()
def build(self) -> Component:
content = Card(
PageView(),
@ -44,7 +27,7 @@ class BasePage(Component):
min_width=38,
corner_radius=(0, 0.5, 0, 0)
)
if self.session.window_width > 28 or self.force_portrait_mode:
if self.session.window_width > 28:
return Container(
content=Column(
Column(
@ -58,13 +41,13 @@ class BasePage(Component):
Row(
Spacer(grow_x=True, grow_y=False),
Card(
content=Text(f"EZGG LAN Manager Version {self.session[ConfigurationService].APP_VERSION} © EZ GG e.V.", align_x=0.5, align_y=0.5, fill=self.session.theme.primary_color, style=TextStyle(font_size=0.5)),
content=Text(f"EZ LAN Manager Version {self.session[ConfigurationService].APP_VERSION} © EZ GG e.V.", align_x=0.5, align_y=0.5, style=TextStyle(fill=self.session.theme.primary_color, font_size=0.5)),
color=self.session.theme.neutral_color,
corner_radius=(0, 0, 0.5, 0.5),
grow_x=False,
grow_y=False,
min_height=1.2,
min_width=self.footer_size,
min_width=53.1,
margin_bottom=3
),
Spacer(grow_x=True, grow_y=False),
@ -77,21 +60,10 @@ class BasePage(Component):
grow_y=True
)
else:
return Column(
Text(
"Wir empfehlen auf\nmobilen Endgeräten im\nQuerformat zu arbeiten.\n\nBitte drehe dein Gerät.",
fill=Color.from_hex("FFFFFF"),
align_x=0.5,
align_y=0.5,
style=TextStyle(font_size=0.8)
),
Button(
content=Text("Ohne drehen fortfahren", margin=0.2),
style="minor",
shape="rounded",
align_x=0.5,
align_y=0,
on_press=self.enforce_portrait_mode
)
return Text(
"Der EZ LAN Manager wird\nauf mobilen Endgeräten nur\nim Querformat unterstützt.\nBitte drehe dein Gerät.",
align_x=0.5,
align_y=0.5,
style=TextStyle(fill=Color.from_hex("FFFFFF"), font_size=0.8)
)

View File

@ -2,14 +2,14 @@ from typing import Optional
from rio import Text, Column, TextStyle, Component, event, Button, Popup
from src.ezgg_lan_manager import ConfigurationService, UserService, TicketingService
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ezgg_lan_manager.components.TicketBuyCard import TicketBuyCard
from src.ezgg_lan_manager.services.AccountingService import InsufficientFundsError
from src.ezgg_lan_manager.services.TicketingService import TicketNotAvailableError, UserAlreadyHasTicketError
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ezgg_lan_manager.types.Ticket import Ticket
from src.ezgg_lan_manager.types.User import User
from src.ez_lan_manager import ConfigurationService, UserService, TicketingService
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ez_lan_manager.components.TicketBuyCard import TicketBuyCard
from src.ez_lan_manager.services.AccountingService import InsufficientFundsError
from src.ez_lan_manager.services.TicketingService import TicketNotAvailableError, UserAlreadyHasTicketError
from src.ez_lan_manager.types.SessionStorage import SessionStorage
from src.ez_lan_manager.types.Ticket import Ticket
from src.ez_lan_manager.types.User import User
class BuyTicketPage(Component):

View File

@ -2,12 +2,12 @@ from typing import Optional, Callable
from rio import Column, Component, event, TextStyle, Text, Spacer, Revealer, SwitcherBar, SwitcherBarChangeEvent, ProgressCircle
from src.ezgg_lan_manager import ConfigurationService, CateringService
from src.ezgg_lan_manager.components.CateringSelectionItem import CateringSelectionItem
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ezgg_lan_manager.components.ShoppingCartAndOrders import ShoppingCartAndOrders
from src.ezgg_lan_manager.types.CateringMenuItem import CateringMenuItemCategory, CateringMenuItem
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ez_lan_manager import ConfigurationService, CateringService
from src.ez_lan_manager.components.CateringSelectionItem import CateringSelectionItem
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ez_lan_manager.components.ShoppingCartAndOrders import ShoppingCartAndOrders
from src.ez_lan_manager.types.CateringMenuItem import CateringMenuItemCategory, CateringMenuItem
from src.ez_lan_manager.types.SessionStorage import SessionStorage
class CateringPage(Component):

View File

@ -3,25 +3,20 @@ from typing import Optional
from rio import Text, Column, TextStyle, Component, event, TextInput, MultiLineTextInput, Row, Button
from src.ezgg_lan_manager import ConfigurationService, UserService, MailingService
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ezgg_lan_manager.types.User import User
from src.ez_lan_manager import ConfigurationService, UserService, MailingService
from src.ez_lan_manager.components.AnimatedText import AnimatedText
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ez_lan_manager.types.SessionStorage import SessionStorage
from src.ez_lan_manager.types.User import User
class ContactPage(Component):
# Workaround: Can not reassign this value without rio triggering refresh
# Using list to bypass this behavior
last_message_sent: list[datetime] = [datetime(day=1, month=1, year=2000)]
display_printing: list[bool] = [False]
user: Optional[User] = None
e_mail: str = ""
subject: str = ""
message: str = ""
submit_button_is_loading: bool = False
response_message: str = ""
is_success: bool = True
@event.on_populate
async def on_populate(self) -> None:
await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - Kontakt")
@ -29,67 +24,73 @@ class ContactPage(Component):
self.user = await self.session[UserService].get_user(self.session[SessionStorage].user_id)
else:
self.user = None
self.e_mail = "" if not self.user else self.user.user_mail
async def on_send_pressed(self) -> None:
error_msg = ""
self.submit_button_is_loading = True
self.submit_button.is_loading = True
self.submit_button.force_refresh()
now = datetime.now()
if not self.e_mail:
if not self.email_input.text:
error_msg = "E-Mail darf nicht leer sein!"
elif not self.subject:
elif not self.subject_input.text:
error_msg = "Betreff darf nicht leer sein!"
elif not self.message:
elif not self.message_input.text:
error_msg = "Nachricht darf nicht leer sein!"
elif (now - self.last_message_sent[0]) < timedelta(minutes=1):
error_msg = "Immer mit der Ruhe!"
if error_msg:
self.submit_button_is_loading = False
self.is_success = False
self.response_message = error_msg
self.submit_button.is_loading = False
await self.animated_text.display_text(False, error_msg)
return
mail_recipient = self.session[ConfigurationService].get_lan_info().organizer_mail
msg = (f"Kontaktformular vom {now.strftime('%d.%m.%Y %H:%M')}:\n\n"
f"Betreff: {self.subject}\n"
f"Absender: {self.e_mail}\n\n"
f"Betreff: {self.subject_input.text}\n"
f"Absender: {self.email_input.text}\n\n"
f"Inhalt:\n"
f"{self.message}\n")
f"{self.message_input.text}\n")
await self.session[MailingService].send_email("Kontaktformular-Mitteilung", msg, mail_recipient)
self.last_message_sent[0] = datetime.now()
self.submit_button_is_loading = False
self.is_success = True
self.response_message = "Nachricht erfolgreich gesendet!"
self.submit_button.is_loading = False
await self.animated_text.display_text(True, "Nachricht erfolgreich gesendet!")
def build(self) -> Component:
email_input = TextInput(
self.animated_text = AnimatedText(
margin_top=2,
margin_bottom=1,
align_x=0.1
)
self.email_input = TextInput(
label="E-Mail Adresse",
text=self.bind().e_mail,
text="" if not self.user else self.user.user_mail,
margin_left=1,
margin_right=1,
margin_bottom=1,
grow_x=True
)
subject_input = TextInput(
self.subject_input = TextInput(
label="Betreff",
text=self.bind().subject,
text="",
margin_left=1,
margin_right=1,
margin_bottom=1,
grow_x=True
)
message_input = MultiLineTextInput(
self.message_input = MultiLineTextInput(
label="Deine Nachricht an uns",
text=self.bind().message,
text="",
margin_left=1,
margin_right=1,
margin_bottom=1,
min_height=5
)
submit_button = Button(
self.submit_button = Button(
content=Text(
"Absenden",
style=TextStyle(fill=self.session.theme.success_color, font_size=0.9),
@ -101,8 +102,7 @@ class ContactPage(Component):
shape="rectangle",
style="major",
color="primary",
on_press=self.on_send_pressed,
is_loading=self.bind().submit_button_is_loading
on_press=self.on_send_pressed
)
return Column(
MainViewContentBox(
@ -117,21 +117,12 @@ class ContactPage(Component):
margin_bottom=1,
align_x=0.5
),
email_input,
subject_input,
message_input,
self.email_input,
self.subject_input,
self.message_input,
Row(
Text(
text=self.bind().response_message,
style=TextStyle(
fill=self.session.theme.success_color if self.is_success else self.session.theme.danger_color,
font_size=0.9
),
margin_top=2,
margin_bottom=1,
align_x=0.1
),
submit_button,
self.animated_text,
self.submit_button,
)
)
),

View File

@ -5,9 +5,9 @@ from typing import * # type: ignore
from rio import Component, event, Spacer, Card, Container, Column, Row, TextStyle, Color, Text
from src.ezgg_lan_manager.services.DatabaseService import DatabaseService
from src.ezgg_lan_manager import ConfigurationService
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ez_lan_manager.services.DatabaseService import DatabaseService
from src.ez_lan_manager import ConfigurationService
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
class DbErrorPage(Component):
@ -62,7 +62,7 @@ class DbErrorPage(Component):
Row(
Spacer(grow_x=True, grow_y=False),
Card(
content=Text(f"EZGG LAN Manager Version {self.session[ConfigurationService].APP_VERSION} © EZ GG e.V.", align_x=0.5, align_y=0.5, style=TextStyle(fill=self.session.theme.primary_color, font_size=0.5)),
content=Text(f"EZ LAN Manager Version {self.session[ConfigurationService].APP_VERSION} © EZ GG e.V.", align_x=0.5, align_y=0.5, style=TextStyle(fill=self.session.theme.primary_color, font_size=0.5)),
color=self.session.theme.neutral_color,
corner_radius=(0, 0, 0.5, 0.5),
grow_x=False,
@ -82,7 +82,7 @@ class DbErrorPage(Component):
)
else:
return Text(
"Der EZGG LAN Manager wird\nauf mobilen Endgeräten nur\nim Querformat unterstützt.\nBitte drehe dein Gerät.",
"Der EZ LAN Manager wird\nauf mobilen Endgeräten nur\nim Querformat unterstützt.\nBitte drehe dein Gerät.",
align_x=0.5,
align_y=0.5,
style=TextStyle(fill=Color.from_hex("FFFFFF"), font_size=0.8)

View File

@ -2,11 +2,11 @@ from typing import Optional
from rio import Column, Component, event, Spacer
from src.ezgg_lan_manager import ConfigurationService, UserService
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ezgg_lan_manager.components.UserEditForm import UserEditForm
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ezgg_lan_manager.types.User import User
from src.ez_lan_manager import ConfigurationService, UserService
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ez_lan_manager.components.UserEditForm import UserEditForm
from src.ez_lan_manager.types.SessionStorage import SessionStorage
from src.ez_lan_manager.types.User import User
class EditProfilePage(Component):

View File

@ -1,7 +1,7 @@
from rio import Column, Component, event, TextStyle, Text, Revealer
from src.ezgg_lan_manager import ConfigurationService
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ez_lan_manager import ConfigurationService
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
FAQ: list[list[str]] = [
["Wie melde ich mich für die LAN an?",

View File

@ -4,8 +4,8 @@ from random import choices
from email_validator import validate_email, EmailNotValidError
from rio import Column, Component, event, Text, TextStyle, TextInput, TextInputChangeEvent, Button
from src.ezgg_lan_manager import ConfigurationService, UserService, MailingService
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ez_lan_manager import ConfigurationService, UserService, MailingService
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
class ForgotPasswordPage(Component):
@ -31,7 +31,7 @@ class ForgotPasswordPage(Component):
await user_service.update_user(user)
await mailing_service.send_email(
subject=f"Dein neues Passwort für {lan_info.name}",
body=f"Du hast für den EZGG-LAN Manager der {lan_info.name} ein neues Passwort angefragt. "
body=f"Du hast für den EZ-LAN Manager der {lan_info.name} ein neues Passwort angefragt. "
f"Und hier ist es schon:\n\n{new_password}\n\nSolltest du kein neues Passwort angefordert haben, "
f"ignoriere diese E-Mail.\n\nLiebe Grüße\nDein {lan_info.name} - Team",
receiver=self.email_input.text.strip()

View File

@ -2,10 +2,10 @@ from typing import Optional
from rio import Column, Component, event, TextStyle, Text, Button, Row, TextInput, Spacer, TextInputChangeEvent
from src.ezgg_lan_manager import ConfigurationService, UserService, TicketingService, SeatingService
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ezgg_lan_manager.types.Seat import Seat
from src.ezgg_lan_manager.types.User import User
from src.ez_lan_manager import ConfigurationService, UserService, TicketingService, SeatingService
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ez_lan_manager.types.Seat import Seat
from src.ez_lan_manager.types.User import User
class GuestsPage(Component):

View File

@ -1,7 +1,7 @@
from rio import Text, Column, TextStyle, Component, event, Link, Color
from src.ezgg_lan_manager import ConfigurationService
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ez_lan_manager import ConfigurationService
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
class ImprintPage(Component):

View File

@ -5,11 +5,11 @@ from typing import Optional, Callable
from rio import Column, Component, event, TextStyle, Text, Spacer, PointerEvent, Button, Popup, Card, Row
from src.ezgg_lan_manager import ConfigurationService, CateringService, SeatingService, AccountingService
from src.ezgg_lan_manager.components.CateringManagementOrderDisplay import CateringManagementOrderDisplay
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ezgg_lan_manager.types.CateringOrder import CateringOrder, CateringOrderStatus
from src.ezgg_lan_manager.types.Seat import Seat
from src.ez_lan_manager import ConfigurationService, CateringService, SeatingService, AccountingService
from src.ez_lan_manager.components.CateringManagementOrderDisplay import CateringManagementOrderDisplay
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ez_lan_manager.types.CateringOrder import CateringOrder, CateringOrderStatus
from src.ez_lan_manager.types.Seat import Seat
logger = logging.getLogger(__name__.split(".")[-1])

View File

@ -5,11 +5,11 @@ from time import strptime
from rio import Column, Component, event, TextStyle, Text
from src.ezgg_lan_manager import ConfigurationService, UserService
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ezgg_lan_manager.components.NewsPost import EditableNewsPost
from src.ezgg_lan_manager.services.NewsService import NewsService
from src.ezgg_lan_manager.types.News import News
from src.ez_lan_manager import ConfigurationService, UserService
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ez_lan_manager.components.NewsPost import EditableNewsPost
from src.ez_lan_manager.services.NewsService import NewsService
from src.ez_lan_manager.types.News import News
logger = logging.getLogger(__name__.split(".")[-1])
@ -78,8 +78,8 @@ class ManageNewsPage(Component):
Column(
Text(
text="News Verwaltung",
fill=self.session.theme.background_color,
style=TextStyle(
fill=self.session.theme.background_color,
font_size=1.2
),
margin_top=2,
@ -88,8 +88,8 @@ class ManageNewsPage(Component):
),
Text(
text="Neuen News Post erstellen",
fill=self.session.theme.background_color,
style=TextStyle(
fill=self.session.theme.background_color,
font_size=1.1
),
margin_top=2,
@ -106,8 +106,8 @@ class ManageNewsPage(Component):
),
Text(
text="Post erfolgreich erstellt",
fill=self.session.theme.success_color,
style=TextStyle(
fill=self.session.theme.success_color,
font_size=0.7 if self.show_success_message else 0
),
margin_top=0.1,
@ -116,8 +116,8 @@ class ManageNewsPage(Component):
),
Text(
text="Bisherige Posts",
fill=self.session.theme.background_color,
style=TextStyle(
fill=self.session.theme.background_color,
font_size=1.1
),
margin_top=2,

View File

@ -2,8 +2,8 @@ import logging
from rio import Column, Component, event, TextStyle, Text, Spacer
from src.ezgg_lan_manager import ConfigurationService
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ez_lan_manager import ConfigurationService
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
logger = logging.getLogger(__name__.split(".")[-1])

View File

@ -6,14 +6,14 @@ from rio import Column, Component, event, TextStyle, Text, TextInput, ThemeConte
PointerEventListener, PointerEvent, Rectangle, CursorStyle, Color, TextInputChangeEvent, Spacer, Row, Switch, \
SwitchChangeEvent, EventHandler
from src.ezgg_lan_manager import ConfigurationService, UserService, AccountingService, SeatingService, MailingService
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ezgg_lan_manager.components.NewTransactionForm import NewTransactionForm
from src.ezgg_lan_manager.components.UserEditForm import UserEditForm
from src.ezgg_lan_manager.services.AccountingService import InsufficientFundsError
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ezgg_lan_manager.types.Transaction import Transaction
from src.ezgg_lan_manager.types.User import User
from src.ez_lan_manager import ConfigurationService, UserService, AccountingService, SeatingService
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ez_lan_manager.components.NewTransactionForm import NewTransactionForm
from src.ez_lan_manager.components.UserEditForm import UserEditForm
from src.ez_lan_manager.services.AccountingService import InsufficientFundsError
from src.ez_lan_manager.types.SessionStorage import SessionStorage
from src.ez_lan_manager.types.Transaction import Transaction
from src.ez_lan_manager.types.User import User
logger = logging.getLogger(__name__.split(".")[-1])
@ -104,17 +104,11 @@ class ManageUsersPage(Component):
self.accounting_section_result_success = False
return
else:
new_total_balance = await self.session[AccountingService].add_balance(
await self.session[AccountingService].add_balance(
transaction.user_id,
transaction.value,
transaction.reference
)
user = await self.session[UserService].get_user(transaction.user_id)
await self.session[MailingService].send_email(
"Dein Guthaben wurde aufgeladen",
self.session[MailingService].generate_account_balance_added_mail_body(user, transaction.value, new_total_balance),
user.user_mail
)
self.accounting_section_result_text = f"Guthaben {'entfernt' if transaction.is_debit else 'hinzugefügt'}!"
self.accounting_section_result_success = True

View File

@ -1,8 +1,8 @@
from rio import Column, Component, event
from src.ezgg_lan_manager import ConfigurationService, NewsService
from src.ezgg_lan_manager.components.NewsPost import NewsPost
from src.ezgg_lan_manager.types.News import News
from src.ez_lan_manager import ConfigurationService, NewsService
from src.ez_lan_manager.components.NewsPost import NewsPost
from src.ez_lan_manager.types.News import News
class NewsPage(Component):

View File

@ -1,7 +1,7 @@
from rio import Column, Component, event
from src.ezgg_lan_manager import ConfigurationService
from src.ezgg_lan_manager.components.NewsPost import NewsPost
from src.ez_lan_manager import ConfigurationService
from src.ez_lan_manager.components.NewsPost import NewsPost
class PlaceholderPage(Component):

View File

@ -3,9 +3,9 @@ import logging
from email_validator import validate_email, EmailNotValidError
from rio import Column, Component, event, Text, TextStyle, TextInput, TextInputChangeEvent, Button
from src.ezgg_lan_manager import ConfigurationService, UserService, MailingService
from src.ezgg_lan_manager.components.AnimatedText import AnimatedText
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ez_lan_manager import ConfigurationService, UserService, MailingService
from src.ez_lan_manager.components.AnimatedText import AnimatedText
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
MINIMUM_PASSWORD_LENGTH = 6
@ -13,7 +13,7 @@ logger = logging.getLogger(__name__.split(".")[-1])
class RegisterPage(Component):
def on_pw_focus_loss(self, _: TextInputChangeEvent) -> None:
def on_pw_change(self, _: TextInputChangeEvent) -> None:
if not (self.pw_1.text == self.pw_2.text) or len(self.pw_1.text) < MINIMUM_PASSWORD_LENGTH:
self.pw_1.is_valid = False
self.pw_2.is_valid = False
@ -21,14 +21,14 @@ class RegisterPage(Component):
self.pw_1.is_valid = True
self.pw_2.is_valid = True
def on_email_focus_loss(self, change_event: TextInputChangeEvent) -> None:
def on_email_changed(self, change_event: TextInputChangeEvent) -> None:
try:
validate_email(change_event.text, check_deliverability=False)
self.email_input.is_valid = True
except EmailNotValidError:
self.email_input.is_valid = False
def on_user_name_focus_loss(self, _: TextInputChangeEvent) -> None:
def on_user_name_input_change(self, _: TextInputChangeEvent) -> None:
current_text = self.user_name_input.text
if len(current_text) > UserService.MAX_USERNAME_LENGTH:
self.user_name_input.text = current_text[:UserService.MAX_USERNAME_LENGTH]
@ -69,7 +69,7 @@ class RegisterPage(Component):
try:
new_user = await user_service.create_user(self.user_name_input.text, self.email_input.text, self.pw_1.text)
if not new_user:
logger.warning(f"UserService.create_user returned: {new_user}") # ToDo: Seems like the user is created fine, even if not returned #FixMe
raise RuntimeError("User could not be created")
except Exception as e:
logger.error(f"Unknown error during new user registration: {e}")
await self.animated_text.display_text(False, "Es ist ein unbekannter Fehler aufgetreten :(")
@ -79,7 +79,7 @@ class RegisterPage(Component):
await mailing_service.send_email(
subject="Erfolgreiche Registrierung",
body=f"Hallo {self.user_name_input.text},\n\n"
f"Du hast dich erfolgreich beim EZGG-LAN Manager für {lan_info.name} {lan_info.iteration} registriert.\n\n"
f"Du hast dich erfolgreich beim EZ-LAN Manager für {lan_info.name} {lan_info.iteration} registriert.\n\n"
f"Wenn du dich nicht registriert hast, kontaktiere bitte unser Team über unsere Homepage.\n\n"
f"Liebe Grüße\nDein {lan_info.name} - Team",
receiver=self.email_input.text
@ -100,7 +100,7 @@ class RegisterPage(Component):
margin_right=1,
margin_bottom=1,
grow_x=True,
on_lose_focus=self.on_user_name_focus_loss
on_change=self.on_user_name_input_change
)
self.email_input = TextInput(
label="E-Mail Adresse",
@ -109,7 +109,7 @@ class RegisterPage(Component):
margin_right=1,
margin_bottom=1,
grow_x=True,
on_lose_focus=self.on_email_focus_loss
on_change=self.on_email_changed
)
self.pw_1 = TextInput(
label="Passwort",
@ -119,7 +119,7 @@ class RegisterPage(Component):
margin_bottom=1,
grow_x=True,
is_secret=True,
on_lose_focus=self.on_pw_focus_loss
on_change=self.on_pw_change
)
self.pw_2 = TextInput(
label="Passwort wiederholen",
@ -129,7 +129,7 @@ class RegisterPage(Component):
margin_bottom=1,
grow_x=True,
is_secret=True,
on_lose_focus=self.on_pw_focus_loss
on_change=self.on_pw_change
)
self.submit_button = Button(
content=Text(

View File

@ -1,7 +1,7 @@
from rio import Column, Component, event, TextStyle, Text, Revealer
from src.ezgg_lan_manager import ConfigurationService
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ez_lan_manager import ConfigurationService
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
RULES: list[str] = [
"Respektvolles Verhalten: Sei höflich und respektvoll gegenüber anderen Gästen und dem Team.",

View File

@ -5,15 +5,15 @@ from typing import Optional
from rio import Text, Column, TextStyle, Component, event, PressEvent, ProgressCircle
from src.ezgg_lan_manager import ConfigurationService, SeatingService, TicketingService, UserService
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ezgg_lan_manager.components.SeatingPlan import SeatingPlan, SeatingPlanLegend
from src.ezgg_lan_manager.components.SeatingPlanInfoBox import SeatingPlanInfoBox
from src.ezgg_lan_manager.components.SeatingPurchaseBox import SeatingPurchaseBox
from src.ezgg_lan_manager.services.SeatingService import NoTicketError, SeatNotFoundError, WrongCategoryError, SeatAlreadyTakenError
from src.ezgg_lan_manager.types.Seat import Seat
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ezgg_lan_manager.types.User import User
from src.ez_lan_manager import ConfigurationService, SeatingService, TicketingService, UserService
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ez_lan_manager.components.SeatingPlan import SeatingPlan, SeatingPlanLegend
from src.ez_lan_manager.components.SeatingPlanInfoBox import SeatingPlanInfoBox
from src.ez_lan_manager.components.SeatingPurchaseBox import SeatingPurchaseBox
from src.ez_lan_manager.services.SeatingService import NoTicketError, SeatNotFoundError, WrongCategoryError, SeatAlreadyTakenError
from src.ez_lan_manager.types.Seat import Seat
from src.ez_lan_manager.types.SessionStorage import SessionStorage
from src.ez_lan_manager.types.User import User
logger = logging.getLogger(__name__.split(".")[-1])
@ -30,7 +30,6 @@ class SeatingPlanPage(Component):
purchase_box_loading: bool = False
purchase_box_success_msg: Optional[str] = None
purchase_box_error_msg: Optional[str] = None
seating_info_text = ""
is_booking_blocked: bool = False
@event.on_populate
@ -48,7 +47,6 @@ class SeatingPlanPage(Component):
self.is_booking_blocked = True
async def on_seat_clicked(self, seat_id: str, _: PressEvent) -> None:
self.seating_info_text = ""
self.show_info_box = True
self.show_purchase_box = False
seat = next(filter(lambda s: s.seat_id == seat_id, self.seating_info), None)
@ -64,12 +62,6 @@ class SeatingPlanPage(Component):
else:
self.current_seat_occupant = None
async def on_info_clicked(self, text: str) -> None:
self.show_info_box = True
self.show_purchase_box = False
self.current_seat_id = None
self.seating_info_text = text
def set_error(self, msg: str) -> None:
self.purchase_box_error_msg = msg
self.purchase_box_success_msg = None
@ -127,7 +119,7 @@ class SeatingPlanPage(Component):
Column(
SeatingPlanInfoBox(seat_id=self.current_seat_id, seat_occupant=self.current_seat_occupant, seat_price=self.current_seat_price,
is_blocked=self.current_seat_is_blocked, is_booking_blocked=self.is_booking_blocked, show=self.show_info_box,
purchase_cb=self.on_purchase_clicked, override_text=self.seating_info_text),
purchase_cb=self.on_purchase_clicked),
SeatingPurchaseBox(
show=self.show_purchase_box,
seat_id=self.current_seat_id,
@ -140,7 +132,7 @@ class SeatingPlanPage(Component):
)
),
MainViewContentBox(
SeatingPlan(seat_clicked_cb=self.on_seat_clicked, seating_info=self.seating_info, info_clicked_cb=self.on_info_clicked) if self.seating_info else
SeatingPlan(seat_clicked_cb=self.on_seat_clicked, seating_info=self.seating_info) if self.seating_info else
Column(ProgressCircle(color=self.session.theme.secondary_color, margin=3),
Text("Sitzplan wird geladen", style=TextStyle(fill=self.session.theme.neutral_color), align_x=0.5, margin=1))
),

View File

@ -1,7 +1,7 @@
from rio import Column, Component, event, TextStyle, Text
from src.ezgg_lan_manager import ConfigurationService
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ez_lan_manager import ConfigurationService
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
class PAGENAME(Component):

View File

@ -1,7 +1,7 @@
from rio import Column, Component, event, TextStyle, Text
from src.ezgg_lan_manager import ConfigurationService
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
from src.ez_lan_manager import ConfigurationService
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
class TournamentsPage(Component):

View File

@ -19,4 +19,3 @@ from .ManageNewsPage import ManageNewsPage
from .ManageUsersPage import ManageUsersPage
from .ManageCateringPage import ManageCateringPage
from .ManageTournamentsPage import ManageTournamentsPage
from .OverviewPage import OverviewPage

View File

@ -2,10 +2,9 @@ import logging
from collections.abc import Callable
from datetime import datetime
from decimal import Decimal, ROUND_DOWN
from typing import Optional
from src.ezgg_lan_manager.services.DatabaseService import DatabaseService
from src.ezgg_lan_manager.types.Transaction import Transaction
from src.ez_lan_manager.services.DatabaseService import DatabaseService
from src.ez_lan_manager.types.Transaction import Transaction
logger = logging.getLogger(__name__.split(".")[-1])
@ -66,11 +65,9 @@ class AccountingService:
return await self._db_service.get_all_transactions_for_user(user_id)
@staticmethod
def make_euro_string_from_decimal(euros: Optional[Decimal]) -> str:
def make_euro_string_from_decimal(euros: Decimal) -> str:
"""
Internally, all money values are euros as decimal. Only when showing them to the user we generate a string.
"""
if euros is None:
return "0.00 €"
rounded_decimal = str(euros.quantize(Decimal(".01"), rounding=ROUND_DOWN))
return f"{rounded_decimal}"

View File

@ -3,12 +3,11 @@ from decimal import Decimal
from enum import Enum
from typing import Optional
from src.ezgg_lan_manager.services.AccountingService import AccountingService
from src.ezgg_lan_manager.services.DatabaseService import DatabaseService
from src.ezgg_lan_manager.services.UserService import UserService
from src.ezgg_lan_manager.services.ReceiptPrintingService import ReceiptPrintingService
from src.ezgg_lan_manager.types.CateringOrder import CateringOrder, CateringOrderStatus, CateringMenuItemsWithAmount
from src.ezgg_lan_manager.types.CateringMenuItem import CateringMenuItem, CateringMenuItemCategory
from src.ez_lan_manager.services.AccountingService import AccountingService
from src.ez_lan_manager.services.DatabaseService import DatabaseService
from src.ez_lan_manager.services.UserService import UserService
from src.ez_lan_manager.types.CateringOrder import CateringOrder, CateringOrderStatus, CateringMenuItemsWithAmount
from src.ez_lan_manager.types.CateringMenuItem import CateringMenuItem, CateringMenuItemCategory
logger = logging.getLogger(__name__.split(".")[-1])
@ -26,11 +25,10 @@ class CateringError(Exception):
class CateringService:
def __init__(self, db_service: DatabaseService, accounting_service: AccountingService, user_service: UserService, receipt_printing_service: ReceiptPrintingService) -> None:
def __init__(self, db_service: DatabaseService, accounting_service: AccountingService, user_service: UserService):
self._db_service = db_service
self._accounting_service = accounting_service
self._user_service = user_service
self._receipt_printing_service = receipt_printing_service
self.cached_cart: dict[int, list[CateringMenuItem]] = {}
# ORDERS
@ -54,7 +52,6 @@ class CateringService:
await self._accounting_service.remove_balance(user_id, total_price, f"CATERING - {order.order_id}")
logger.info(
f"User '{order.customer.user_name}' (ID:{order.customer.user_id}) ordered from catering for {self._accounting_service.make_euro_string_from_decimal(total_price)}")
await self._receipt_printing_service.print_order(user, order)
# await self.cancel_order(order) # ToDo: Check if commented out before commit. Un-comment to auto-cancel every placed order
return order

View File

@ -7,8 +7,8 @@ import tomllib
from from_root import from_root
from src.ezgg_lan_manager.types.ConfigurationTypes import DatabaseConfiguration, MailingServiceConfiguration, LanInfo, \
SeatingConfiguration, TicketInfo, ReceiptPrintingConfiguration
from src.ez_lan_manager.types.ConfigurationTypes import DatabaseConfiguration, MailingServiceConfiguration, LanInfo, \
SeatingConfiguration, TicketInfo
logger = logging.getLogger(__name__.split(".")[-1])
@ -71,6 +71,15 @@ class ConfigurationService:
logger.fatal("Error loading LAN Info, exiting...")
sys.exit(1)
def get_seating_configuration(self) -> SeatingConfiguration:
try:
return SeatingConfiguration(
seats=self._config["seating"]
)
except KeyError:
logger.fatal("Error loading seating configuration, exiting...")
sys.exit(1)
def get_ticket_info(self) -> tuple[TicketInfo, ...]:
try:
return tuple([TicketInfo(
@ -86,19 +95,6 @@ class ConfigurationService:
logger.fatal("Error loading seating configuration, exiting...")
sys.exit(1)
def get_receipt_printing_configuration(self) -> ReceiptPrintingConfiguration:
try:
receipt_printing_configuration = self._config["receipt_printing"]
return ReceiptPrintingConfiguration(
host=receipt_printing_configuration["host"],
port=receipt_printing_configuration["port"],
order_print_endpoint=receipt_printing_configuration["order_print_endpoint"],
password=receipt_printing_configuration["password"]
)
except KeyError:
logger.fatal("Error loading Receipt Printing Configuration, exiting...")
sys.exit(1)
@property
def APP_VERSION(self) -> str:
return self._version

View File

@ -6,15 +6,16 @@ from decimal import Decimal
import aiomysql
from src.ezgg_lan_manager.types.CateringOrder import CateringOrder
from src.ezgg_lan_manager.types.CateringMenuItem import CateringMenuItem, CateringMenuItemCategory
from src.ezgg_lan_manager.types.CateringOrder import CateringMenuItemsWithAmount, CateringOrderStatus
from src.ezgg_lan_manager.types.ConfigurationTypes import DatabaseConfiguration
from src.ezgg_lan_manager.types.News import News
from src.ezgg_lan_manager.types.Seat import Seat
from src.ezgg_lan_manager.types.Ticket import Ticket
from src.ezgg_lan_manager.types.Transaction import Transaction
from src.ezgg_lan_manager.types.User import User
from src.ez_lan_manager.types.CateringOrder import CateringOrder
from src.ez_lan_manager.types.CateringMenuItem import CateringMenuItem, CateringMenuItemCategory
from src.ez_lan_manager.types.CateringOrder import CateringMenuItemsWithAmount, CateringOrderStatus
from src.ez_lan_manager.types.ConfigurationTypes import DatabaseConfiguration
from src.ez_lan_manager.types.Tournament import Tournament
from src.ez_lan_manager.types.News import News
from src.ez_lan_manager.types.Seat import Seat
from src.ez_lan_manager.types.Ticket import Ticket
from src.ez_lan_manager.types.Transaction import Transaction
from src.ez_lan_manager.types.User import User
logger = logging.getLogger(__name__.split(".")[-1])
@ -81,6 +82,18 @@ class DatabaseService:
last_updated_at=data[11]
)
@staticmethod
def _map_db_result_to_tournament(data: tuple) -> Tournament:
return Tournament(
tournament_id=data[0],
tournament_name=data[1],
tournament_info=data[2],
participants_limit=data[3],
start_time=data[4],
created_at=data[5],
can_register=data[6]
)
async def get_user_by_name(self, user_name: str) -> Optional[User]:
async with self._connection_pool.acquire() as conn:
async with conn.cursor(aiomysql.Cursor) as cursor:
@ -232,6 +245,7 @@ class DatabaseService:
except aiomysql.InterfaceError:
pool_init_result = await self.init_db_pool()
if not pool_init_result:
print(self._connection_pool)
raise NoDatabaseConnectionError
return await self.get_news(dt_start, dt_end)
except Exception as e:
@ -787,3 +801,73 @@ class DatabaseService:
return await self.remove_profile_picture(user_id)
except Exception as e:
logger.warning(f"Error deleting user profile picture: {e}")
async def get_all_tournaments(self):
async with self._connection_pool.acquire() as conn:
async with conn.cursor(aiomysql.Cursor) as cursor:
results = []
try:
await cursor.execute("SELECT * FROM tournaments;")
await conn.commit()
except aiomysql.InterfaceError:
pool_init_result = await self.init_db_pool()
if not pool_init_result:
raise NoDatabaseConnectionError
return await self.get_all_users()
except Exception as e:
logger.warning(f"Error getting all tournaments: {e}")
return results
for tournament_raw in await cursor.fetchall():
results.append(self._map_db_result_to_tournament(tournament_raw))
return results
async def get_tournament(self, tournament_id: int) -> Optional[Tournament]:
async with self._connection_pool.acquire() as conn:
async with conn.cursor(aiomysql.Cursor) as cursor:
await cursor.execute("SELECT * FROM tournaments WHERE tournament_id=%s", (tournament_id,))
result = await cursor.fetchone()
if not result:
return
return self._map_db_result_to_tournament(result)
async def get_all_registered_users_for_tournament(self, tournament_id: id) -> Optional[list[User]]:
async with self._connection_pool.acquire() as conn:
async with conn.cursor(aiomysql.Cursor) as cursor:
await cursor.execute("SELECT * FROM tournaments_participants WHERE tournament_id=%s", (tournament_id,))
result = await cursor.fetchone()
result = [self.get_user_by_id(user_id[1]) for user_id in result]
if not result:
return
return result
async def set_tournament_info(self, tournament_id, tournament_info):
pass
async def register_to_tournament(self, tournament_id, user_id):
pass
async def unregister_to_tournament(self, tournament_id, user_id):
pass
async def add_tournament(self, name, additional_info, start_time, participants_limit) -> None:
async with self._connection_pool.acquire() as conn:
async with conn.cursor(aiomysql.Cursor) as cursor:
try:
await cursor.execute(
"INSERT INTO tournaments (name, additional_info, start_time, participants_limit) "
"VALUES (%s, %s, %s, %s)",
(name, additional_info, start_time, participants_limit)
)
await conn.commit()
except aiomysql.InterfaceError:
pool_init_result = await self.init_db_pool()
if not pool_init_result:
raise NoDatabaseConnectionError
return await self.add_tournament(name, additional_info, start_time, participants_limit)
except Exception as e:
logger.warning(f"Error adding Tournament: {e}")
return

View File

@ -3,7 +3,7 @@ from typing import Optional
from rio import UserSettings
from src.ezgg_lan_manager.types.SessionStorage import SessionStorage
from src.ez_lan_manager.types.SessionStorage import SessionStorage
class LocalData(UserSettings):

View File

@ -1,12 +1,10 @@
import logging
from decimal import Decimal
from email.message import EmailMessage
from asyncio import sleep
import aiosmtplib
from src.ezgg_lan_manager.services.ConfigurationService import ConfigurationService
from src.ezgg_lan_manager.types.User import User
from src.ez_lan_manager.services.ConfigurationService import ConfigurationService
logger = logging.getLogger(__name__.split(".")[-1])
@ -18,9 +16,6 @@ class MailingService:
async def send_email(self, subject: str, body: str, receiver: str) -> None:
if self._configuration_service.DEV_MODE_ACTIVE:
logger.info(f"Skipped sending mail to {receiver} because demo mode is active.")
logger.info(f"Subject: {subject}")
logger.info(f"Receiver: {receiver}")
logger.info(f"Body: {body}")
await sleep(1)
return
@ -40,15 +35,3 @@ class MailingService:
)
except Exception as e:
logger.error(f"Failed to send email: {e}")
def generate_account_balance_added_mail_body(self, user: User, added_balance: Decimal, total_balance: Decimal) -> str:
return f"""
Hallo {user.user_name},
deinem Account wurden {added_balance} hinzugefügt. Dein neues Guthaben beträgt nun {total_balance} .
Wenn du zu dieser Aufladung Fragen hast, stehen wir dir in unserem Discord Server oder per Mail an {self._configuration_service.get_lan_info().organizer_mail} zur Verfügung.
Liebe Grüße
Dein {self._configuration_service.get_lan_info().name} Team
"""

View File

@ -2,8 +2,8 @@ import logging
from datetime import date
from typing import Optional
from src.ezgg_lan_manager.services.DatabaseService import DatabaseService
from src.ezgg_lan_manager.types.News import News
from src.ez_lan_manager.services.DatabaseService import DatabaseService
from src.ez_lan_manager.types.News import News
logger = logging.getLogger(__name__.split(".")[-1])

View File

@ -2,10 +2,10 @@ import logging
from typing import Optional
from src.ezgg_lan_manager.services.DatabaseService import DatabaseService
from src.ezgg_lan_manager.services.TicketingService import TicketingService
from src.ezgg_lan_manager.types.ConfigurationTypes import LanInfo, SeatingConfiguration
from src.ezgg_lan_manager.types.Seat import Seat
from src.ez_lan_manager.services.DatabaseService import DatabaseService
from src.ez_lan_manager.services.TicketingService import TicketingService
from src.ez_lan_manager.types.ConfigurationTypes import LanInfo, SeatingConfiguration
from src.ez_lan_manager.types.Seat import Seat
logger = logging.getLogger(__name__.split(".")[-1])
@ -22,7 +22,8 @@ class SeatAlreadyTakenError(Exception):
pass
class SeatingService:
def __init__(self, lan_info: LanInfo, db_service: DatabaseService, ticketing_service: TicketingService) -> None:
def __init__(self, seating_configuration: SeatingConfiguration, lan_info: LanInfo, db_service: DatabaseService, ticketing_service: TicketingService) -> None:
self._seating_configuration = seating_configuration
self._lan_info = lan_info
self._db_service = db_service
self._ticketing_service = ticketing_service

View File

@ -1,10 +1,10 @@
import logging
from typing import Optional
from src.ezgg_lan_manager.services.AccountingService import AccountingService, InsufficientFundsError
from src.ezgg_lan_manager.services.DatabaseService import DatabaseService
from src.ezgg_lan_manager.types.ConfigurationTypes import TicketInfo
from src.ezgg_lan_manager.types.Ticket import Ticket
from src.ez_lan_manager.services.AccountingService import AccountingService, InsufficientFundsError
from src.ez_lan_manager.services.DatabaseService import DatabaseService
from src.ez_lan_manager.types.ConfigurationTypes import TicketInfo
from src.ez_lan_manager.types.Ticket import Ticket
logger = logging.getLogger(__name__.split(".")[-1])

View File

@ -0,0 +1,38 @@
import logging
from typing import Optional
from src.ez_lan_manager.services.DatabaseService import DatabaseService
from src.ez_lan_manager.types.Tournament import Tournament
from src.ez_lan_manager.types.User import User
logger = logging.getLogger(__name__.split(".")[-1])
class AlreadyRegisteredToTournamentError(Exception):
pass
class TournamentService:
def __init__(self, db_service: DatabaseService):
self._db_service = db_service
async def add_tournament(self, name, additional_info, start_time, participants_limit) -> None:
await self._db_service.add_tournament(name, additional_info, start_time, participants_limit)
async def get_all_tournaments(self) -> list[Tournament]:
return await self._db_service.get_all_tournaments()
async def get_tournament(self, tournament_id: int) -> Tournament:
return await self._db_service.get_tournament()
async def get_all_registered_users_for_tournament(self, tournament_id: int) -> list[User]:
return await self._db_service.get_all_registered_users_for_tournament(tournament_id)
async def register_to_tournament(self, tournament_id: int, user_id: int) -> None:
await self._db_service.register_to_tournament(tournament_id, user_id)
async def unregister_to_tournament(self, tournament_id: int, user_id: int) -> None:
await self._db_service.unregister_to_tournament(tournament_id, user_id)
async def set_tournament_info(self, tournament_id: int, tournament_info: str) -> None:
await self._db_service.set_tournament_info(tournament_id, tournament_info)

View File

@ -2,8 +2,8 @@ from hashlib import sha256
from typing import Union, Optional
from string import ascii_letters, digits
from src.ezgg_lan_manager.services.DatabaseService import DatabaseService
from src.ezgg_lan_manager.types.User import User
from src.ez_lan_manager.services.DatabaseService import DatabaseService
from src.ez_lan_manager.types.User import User
class NameNotAllowedError(Exception):
@ -47,8 +47,7 @@ class UserService:
user_name = user_name.lower()
hashed_pw = sha256(password_clear_text.encode(encoding="utf-8")).hexdigest()
created_user = await self._db_service.create_user(user_name, user_mail, hashed_pw)
return created_user
return await self._db_service.create_user(user_name, user_mail, hashed_pw)
async def update_user(self, user: User) -> User:
disallowed_char = self._check_for_disallowed_char(user.user_name)

View File

@ -4,8 +4,8 @@ from decimal import Decimal
from enum import StrEnum
from typing import Optional, Iterable, Self
from src.ezgg_lan_manager.types.CateringMenuItem import CateringMenuItem, CateringMenuItemCategory
from src.ezgg_lan_manager.types.User import User
from src.ez_lan_manager.types.CateringMenuItem import CateringMenuItem, CateringMenuItemCategory
from src.ez_lan_manager.types.User import User
CateringMenuItemsWithAmount = dict[CateringMenuItem, int]

View File

@ -48,10 +48,3 @@ class LanInfo:
@dataclass(frozen=True)
class SeatingConfiguration:
seats: dict[str, str]
@dataclass(frozen=True)
class ReceiptPrintingConfiguration:
host: str
port: int
order_print_endpoint: str
password: str

View File

@ -2,7 +2,7 @@ from dataclasses import dataclass
from datetime import date
from typing import Optional
from src.ezgg_lan_manager.types.User import User
from src.ez_lan_manager.types.User import User
@dataclass(frozen=True)

View File

@ -1,7 +1,7 @@
from dataclasses import dataclass
from typing import Optional
from src.ezgg_lan_manager.types.User import User
from src.ez_lan_manager.types.User import User
@dataclass(frozen=True)

View File

@ -2,7 +2,7 @@ from dataclasses import dataclass
from datetime import datetime
from typing import Optional
from src.ezgg_lan_manager.types.User import User
from src.ez_lan_manager.types.User import User
@dataclass(frozen=True)

View File

@ -0,0 +1,18 @@
from dataclasses import dataclass
from datetime import date, datetime
from typing import Optional
@dataclass
class Tournament:
tournament_id: int
tournament_name: str
tournament_info: str
participants_limit: int
start_time: datetime
created_at: datetime
can_register: bool = True
def __hash__(self) -> int:
return hash(str(self.tournament_id))

View File

@ -1,35 +0,0 @@
import logging
from from_root import from_root
from src.ezgg_lan_manager.services import *
from src.ezgg_lan_manager.services.AccountingService import AccountingService
from src.ezgg_lan_manager.services.CateringService import CateringService
from src.ezgg_lan_manager.services.ConfigurationService import ConfigurationService
from src.ezgg_lan_manager.services.DatabaseService import DatabaseService
from src.ezgg_lan_manager.services.LocalDataService import LocalDataService
from src.ezgg_lan_manager.services.MailingService import MailingService
from src.ezgg_lan_manager.services.NewsService import NewsService
from src.ezgg_lan_manager.services.ReceiptPrintingService import ReceiptPrintingService
from src.ezgg_lan_manager.services.SeatingService import SeatingService
from src.ezgg_lan_manager.services.TicketingService import TicketingService
from src.ezgg_lan_manager.services.UserService import UserService
from src.ezgg_lan_manager.types import *
# Inits services in the correct order
def init_services() -> tuple[AccountingService, CateringService, ConfigurationService, DatabaseService, MailingService, NewsService, SeatingService, TicketingService, UserService, LocalDataService, ReceiptPrintingService]:
logging.basicConfig(level=logging.DEBUG)
configuration_service = ConfigurationService(from_root("config.toml"))
db_service = DatabaseService(configuration_service.get_database_configuration())
user_service = UserService(db_service)
accounting_service = AccountingService(db_service)
news_service = NewsService(db_service)
mailing_service = MailingService(configuration_service)
ticketing_service = TicketingService(configuration_service.get_ticket_info(), db_service, accounting_service)
seating_service = SeatingService(configuration_service.get_lan_info(), db_service, ticketing_service)
receipt_printing_service = ReceiptPrintingService(seating_service, configuration_service.get_receipt_printing_configuration(), configuration_service.DEV_MODE_ACTIVE)
catering_service = CateringService(db_service, accounting_service, user_service, receipt_printing_service)
local_data_service = LocalDataService()
return accounting_service, catering_service, configuration_service, db_service, mailing_service, news_service, seating_service, ticketing_service, user_service, local_data_service, receipt_printing_service

View File

@ -1,274 +0,0 @@
from typing import Callable
from rio import Component, Rectangle, Grid, Column, Row, Text, TextStyle, Color, PointerEventListener, Spacer
from src.ezgg_lan_manager.components.SeatingPlanPixels import SeatPixel, WallPixel, InvisiblePixel, TextPixel
from src.ezgg_lan_manager.types.Seat import Seat
MAX_GRID_WIDTH_PIXELS = 60
MAX_GRID_HEIGHT_PIXELS = 60
class SeatingPlanLegend(Component):
def build(self) -> Component:
return Column(
Text("Legende", style=TextStyle(fill=self.session.theme.neutral_color), justify="center", margin=1),
Row(
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(
content=Text("Deluxe 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("66ff99"),
min_width=20
),
Spacer()
),
Row(
Rectangle(
content=Column(
Text(f"Freier Platz", style=TextStyle(fill=self.session.theme.primary_color, font_size=0.7), align_x=0.5, selectable=False),
Text(f"", style=TextStyle(fill=self.session.theme.primary_color, font_size=0.9), align_x=0.5,
selectable=False, overflow="wrap")
),
min_width=1,
min_height=1,
fill=self.session.theme.success_color,
grow_x=False,
grow_y=False,
hover_fill=self.session.theme.success_color,
transition_time=0.4,
ripple=True
),
Rectangle(
content=Column(
Text(f"Belegter Platz", style=TextStyle(fill=self.session.theme.primary_color, font_size=0.7), align_x=0.5, selectable=False),
Text(f"", style=TextStyle(fill=self.session.theme.primary_color, font_size=0.9), align_x=0.5,
selectable=False, overflow="wrap")
),
min_width=1,
min_height=1,
fill=self.session.theme.danger_color,
grow_x=False,
grow_y=False,
hover_fill=self.session.theme.danger_color,
transition_time=0.4,
ripple=True
),
Rectangle(
content=Column(
Text(f"Eigener Platz", style=TextStyle(fill=self.session.theme.primary_color, font_size=0.7), align_x=0.5, selectable=False),
Text(f"", style=TextStyle(fill=self.session.theme.primary_color, font_size=0.9), align_x=0.5,
selectable=False, overflow="wrap")
),
min_width=1,
min_height=1,
fill=Color.from_hex("800080"),
grow_x=False,
grow_y=False,
hover_fill=Color.from_hex("800080"),
transition_time=0.4,
ripple=True
),
margin=1,
spacing=1
)
)
class SeatingPlan(Component):
seat_clicked_cb: Callable
seating_info: list[Seat]
info_clicked_cb: Callable
def get_seat(self, seat_id: str) -> Seat:
seat = next(filter(lambda seat_: seat_.seat_id == seat_id, self.seating_info), None)
return seat if seat else Seat(seat_id="Z99", is_blocked=True, category="LUXUS", user=None)
"""
This seating plan is for the community center "Donsbach"
"""
def build(self) -> Component:
grid = Grid()
# Outlines
for x in range(0, MAX_GRID_WIDTH_PIXELS):
grid.add(WallPixel(), row=0, column=x)
for y in range(0, MAX_GRID_HEIGHT_PIXELS):
grid.add(WallPixel(), row=y, column=0)
for x in range(0, MAX_GRID_WIDTH_PIXELS):
grid.add(WallPixel(), row=MAX_GRID_HEIGHT_PIXELS, column=x)
for x in range(0, 31):
grid.add(WallPixel(), row=15, column=x)
for x in range(41, MAX_GRID_WIDTH_PIXELS):
grid.add(WallPixel(), row=32, column=x)
for x in range(31, 34):
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 y in range(40, MAX_GRID_HEIGHT_PIXELS):
grid.add(WallPixel(), row=y, column=30)
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(
TextPixel(text="Bühne"),
on_press=lambda _: self.info_clicked_cb("Hier darf ab Freitag 20 Uhr ebenfalls geschlafen werden.")
), row=16, column=1, width=29, height=4)
# Drinks
grid.add(PointerEventListener(
TextPixel(text="G\ne\nt\nr\nä\nn\nk\ne"),
on_press=lambda _: self.info_clicked_cb("Ich mag Bier, B - I - R")
), row=20, column=30, width=4, height=12)
# 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(
TextPixel(icon_name="material/wc"),
on_press=lambda _: self.info_clicked_cb("Damen Toilette")
), row=1, column=42, width=19, height=10)
grid.add(PointerEventListener(
TextPixel(icon_name="material/wc"),
on_press=lambda _: self.info_clicked_cb("Herren Toilette")
), row=12, column=42, width=19, height=10)
# Entry/Helpdesk
grid.add(PointerEventListener(
TextPixel(text="Einlass\n &Orga"),
on_press=lambda _: self.info_clicked_cb("Für alle Anliegen findest du hier rund um die Uhr jemanden vom Team.")
), row=40, column=22, width=8, height=12)
return Rectangle(
content=grid,
grow_x=True,
grow_y=True,
stroke_color=self.session.theme.neutral_color,
stroke_width=0.1,
fill=self.session.theme.primary_color,
margin=0.5
)

View File

@ -1,141 +0,0 @@
from rio import Column, Component, event, Text, Spacer, Row, Link
from src.ezgg_lan_manager import ConfigurationService, TicketingService
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
class OverviewPage(Component):
@event.on_populate
async def on_populate(self) -> None:
await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - Übersicht")
def build(self) -> Component:
lan_info = self.session[ConfigurationService].get_lan_info()
return Column(
MainViewContentBox(
Column(
Text(lan_info.name, font_size=2, justify="center", fill=self.session.theme.neutral_color, margin_top=0.5),
Text(f"Edition {lan_info.iteration}", font_size=0.9, justify="center", fill=self.session.theme.neutral_color, margin_bottom=1.5)
)
),
MainViewContentBox(
Column(
Text("Allgemeines", font_size=2, justify="center", fill=self.session.theme.neutral_color, margin_top=0.5, margin_bottom=1),
Column(
Row(
Text("Wann?", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(),
Text(f"{lan_info.date_from.strftime("%d.%m.")} bis {lan_info.date_till.strftime("%d.%m.%Y")}", fill=self.session.theme.neutral_color, margin_right=1),
margin_bottom=0.3
),
Row(
Text("Wo?", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(),
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
),
Row(
Text("Einlass", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(),
Text(lan_info.date_from.strftime("Freitag %H:%M Uhr"), fill=self.session.theme.neutral_color, margin_right=1),
margin_bottom=0.3
),
Row(
Text("Ende", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(),
Text(lan_info.date_till.strftime("Sonntag %H:%M Uhr"), fill=self.session.theme.neutral_color, margin_right=1),
margin_bottom=0.3
),
Row(
Text("Anmeldung", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(),
Text("Geöffnet", fill=self.session.theme.success_color, margin_right=1),
margin_bottom=0.3
),
Row(
Text("Teilnehmer", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(),
Text(str(self.session[TicketingService].get_total_tickets()), fill=self.session.theme.neutral_color, margin_right=1),
margin_bottom=0.3
)
,
Row(
Text("Ticket Preise", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(),
Link(Text(f"Preisliste", fill=self.session.theme.secondary_color, margin_right=1), target_url="./buy_ticket", open_in_new_tab=False),
margin_bottom=0.3
)
)
)
),
MainViewContentBox(
Column(
Text("Technisches", font_size=2, justify="center", fill=self.session.theme.neutral_color, margin_top=0.5, margin_bottom=1),
Column(
Row(
Text("Internet", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(),
Text(f"60/20 Mbit/s (down/up)", fill=self.session.theme.neutral_color, margin_right=1),
margin_bottom=0.3
),
Row(
Text("Routing", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(),
Text(f"Flaches Netz", fill=self.session.theme.neutral_color, margin_right=1),
margin_bottom=0.3
),
Row(
Text("WLAN", fill=self.session.theme.neutral_color, margin_left=1),
Spacer(),
Text(f"vorhanden", fill=self.session.theme.neutral_color, margin_right=1),
margin_bottom=0.3
)
)
)
),
MainViewContentBox(
Column(
Text("Sonstiges", font_size=2, justify="center", fill=self.session.theme.neutral_color, margin_top=0.5, margin_bottom=1),
Column(
Row(
Text("Schlafen", fill=self.session.theme.neutral_color, margin_left=1, justify="center"),
margin_bottom=0.3
),
Row(
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"),
margin_bottom=0.3
),
Row(
Text("Essen & Trinken", fill=self.session.theme.neutral_color, margin_left=1, justify="center"),
margin_bottom=0.3
),
Row(
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
)
)
)
),
MainViewContentBox(
Column(
Text("Turniere & Ablauf", font_size=2, justify="center", fill=self.session.theme.neutral_color, margin_top=0.5, margin_bottom=1),
Column(
Row(
Text("Zum aktuellen Zeitpunkt steht noch nicht fest welche Turniere gespielt werden. Wir planen diverse Online- und Offline Turniere mit Preisen durchzuführen. Weitere Informationen gibt es, sobald sie kommen, auf der NEWS- und Turnier-Seite.", font_size=0.7,
fill=self.session.theme.neutral_color, margin_left=1, overflow="wrap"),
margin_bottom=0.3
)
)
)
),
Spacer()
)

View File

@ -1,48 +0,0 @@
import logging
import requests
from src.ezgg_lan_manager.services.SeatingService import SeatingService
from src.ezgg_lan_manager.types.CateringOrder import CateringOrder
from src.ezgg_lan_manager.types.ConfigurationTypes import ReceiptPrintingConfiguration
from src.ezgg_lan_manager.types.User import User
logger = logging.getLogger(__name__.split(".")[-1])
logging.getLogger("urllib3").setLevel(logging.FATAL) # Disable logging for urllib3
class ReceiptPrintingService:
def __init__(self, seating_service: SeatingService, config: ReceiptPrintingConfiguration, dev_mode_enabled: bool) -> None:
self._seating_service = seating_service
self._config = config
self._dev_mode_enabled = dev_mode_enabled
async def print_order(self, user: User, order: CateringOrder) -> None:
seat_id = await self._seating_service.get_user_seat(user.user_id)
if not seat_id:
seat_id = " - "
menu_items_payload = []
for item, amount in order.items.items():
menu_items_payload.append({
"menu_item_name": item.name,
"amount": amount
})
payload = {
"order_id": str(order.order_id),
"order_date": order.order_date.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z',
"customer_name": user.user_name,
"seat_id": seat_id,
"items": menu_items_payload
}
try:
requests.post(
f"http://{self._config.host}:{self._config.port}/{self._config.order_print_endpoint}",
json=payload,
headers={"x-password": self._config.password}
)
except Exception as e:
if self._dev_mode_enabled:
logger.info("An error occurred trying to print a receipt:", e)
return
logger.error("An error occurred trying to print a receipt:", e)

View File

@ -3,8 +3,8 @@ from datetime import datetime
from unittest.mock import MagicMock, AsyncMock
from decimal import Decimal
from src.ezgg_lan_manager.services.AccountingService import AccountingService, InsufficientFundsError
from src.ezgg_lan_manager.types.Transaction import Transaction
from src.ez_lan_manager.services.AccountingService import AccountingService, InsufficientFundsError
from src.ez_lan_manager.types.Transaction import Transaction
class AccountingServiceTests(unittest.IsolatedAsyncioTestCase):

View File

@ -0,0 +1,12 @@
import unittest
from unittest.mock import MagicMock, AsyncMock
from src.ez_lan_manager.services.TournamentService import TournamentService
class TournamentServiceTests(unittest.IsolatedAsyncioTestCase):
def setUp(self) -> None:
self.mock_database_service = MagicMock()
self.mock_database_service.add_transaction = AsyncMock()
self.accounting_service = TournamentService(self.mock_database_service)