diff --git a/config/config.example.toml b/config/config.example.toml index 2b1bf40..b4be508 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -1,9 +1,6 @@ [lan] name="EZ LAN" iteration="0.5" - default_category="NORMAL" - tickets={ "LUXUS" = 40, "NORMAL" = 10 } - prices={ "LUXUS" = 3000, "NORMAL" = 2500 } # Eurocent date_from="2024-10-30 15:00:00" date_till="2024-11-01 12:00:00" organizer_mail="tech@example.com" @@ -25,4 +22,18 @@ [seating] base_svg_path="" -dev_mode_active=false # Supresses E-Mail sending +[tickets] + [tickets."NORMAL"] + total_tickets=30 + price=2500 # Eurocent + description="Normales Ticket" + is_default=true + + [tickets."LUXUS"] + total_tickets=10 + price=4000 # Eurocent + description="Luxus Ticket" + is_default=false + +[misc] + dev_mode_active=true # Supresses E-Mail sending diff --git a/src/ez_lan_manager/__init__.py b/src/ez_lan_manager/__init__.py index 88c136e..6c6712a 100644 --- a/src/ez_lan_manager/__init__.py +++ b/src/ez_lan_manager/__init__.py @@ -23,7 +23,7 @@ def init_services() -> tuple[AccountingService, CateringService, ConfigurationSe accounting_service = AccountingService(db_service) news_service = NewsService(db_service) mailing_service = MailingService(configuration_service) - ticketing_service = TicketingService(configuration_service.get_lan_info(), db_service, accounting_service) + ticketing_service = TicketingService(configuration_service.get_ticket_info(), db_service, accounting_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) diff --git a/src/ez_lan_manager/services/ConfigurationService.py b/src/ez_lan_manager/services/ConfigurationService.py index 0c731e6..f14f52a 100644 --- a/src/ez_lan_manager/services/ConfigurationService.py +++ b/src/ez_lan_manager/services/ConfigurationService.py @@ -6,7 +6,7 @@ import tomllib from from_root import from_root -from src.ez_lan_manager.types.ConfigurationTypes import DatabaseConfiguration, MailingServiceConfiguration, LanInfo, TicketInfo, SeatingConfiguration +from src.ez_lan_manager.types.ConfigurationTypes import DatabaseConfiguration, MailingServiceConfiguration, LanInfo, SeatingConfiguration, TicketInfo logger = logging.getLogger(__name__.split(".")[-1]) @@ -58,16 +58,9 @@ class ConfigurationService: def get_lan_info(self) -> LanInfo: try: lan_info = self._config["lan"] - ticket_info = TicketInfo( - default_category=lan_info["default_category"], - categories=list(lan_info["tickets"].keys()), - _prices=lan_info["prices"], - _available_tickets=lan_info["tickets"] - ) return LanInfo( name=lan_info["name"], iteration=lan_info["iteration"], - ticket_info=ticket_info, date_from=datetime.strptime(lan_info["date_from"], "%Y-%m-%d %H:%M:%S"), date_till=datetime.strptime(lan_info["date_till"], "%Y-%m-%d %H:%M:%S"), organizer_mail=lan_info["organizer_mail"] @@ -90,10 +83,24 @@ class ConfigurationService: logger.fatal("Error loading seating configuration, exiting...") sys.exit(1) + def get_ticket_info(self) -> tuple[TicketInfo, ...]: + try: + return tuple([TicketInfo( + category=value, + total_tickets=self._config["tickets"][value]["total_tickets"], + price=self._config["tickets"][value]["price"], + description=self._config["tickets"][value]["description"], + is_default=self._config["tickets"][value]["is_default"] + ) for value in self._config["tickets"]]) + except KeyError as e: + logger.debug(e) + logger.fatal("Error loading seating configuration, exiting...") + sys.exit(1) + @property def APP_VERSION(self) -> str: return self._version @property def DEV_MODE_ACTIVE(self) -> bool: - return self._config["dev_mode_active"] + return self._config["misc"]["dev_mode_active"] diff --git a/src/ez_lan_manager/services/TicketingService.py b/src/ez_lan_manager/services/TicketingService.py index 3502ff2..d597e9b 100644 --- a/src/ez_lan_manager/services/TicketingService.py +++ b/src/ez_lan_manager/services/TicketingService.py @@ -3,7 +3,7 @@ from typing import Optional 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 LanInfo +from src.ez_lan_manager.types.ConfigurationTypes import TicketInfo from src.ez_lan_manager.types.Ticket import Ticket logger = logging.getLogger(__name__.split(".")[-1]) @@ -16,37 +16,51 @@ class UserAlreadyHasTicketError(Exception): pass class TicketingService: - def __init__(self, lan_info: LanInfo, db_service: DatabaseService, accounting_service: AccountingService) -> None: - self._lan_info = lan_info + def __init__(self, ticket_infos: tuple[TicketInfo, ...], db_service: DatabaseService, accounting_service: AccountingService) -> None: + self._ticket_infos = ticket_infos self._db_service = db_service self._accounting_service = accounting_service - async def get_total_tickets(self) -> int: - return sum([self._lan_info.ticket_info.get_available_tickets(c) for c in self._lan_info.ticket_info.categories]) + def get_ticket_info_by_category(self, category: str) -> Optional[TicketInfo]: + return next(filter(lambda t: t.category == category, self._ticket_infos), None) + + def get_total_tickets(self) -> int: + return sum([t_i.total_tickets for t_i in self._ticket_infos]) + + async def get_available_tickets_for_category(self, category: str) -> int: + ticket_info = self.get_ticket_info_by_category(category) + if not ticket_info or ticket_info.total_tickets < 1: + return 0 + result = ticket_info.total_tickets - async def get_available_tickets(self) -> dict[str, int]: - result = self._lan_info.ticket_info.total_available_tickets all_tickets = await self._db_service.get_tickets() for ticket in all_tickets: - result[ticket.category] -= 1 + if ticket.category == category: + result -= 1 return result async def purchase_ticket(self, user_id: int, category: str) -> Ticket: - if category not in self._lan_info.ticket_info.categories or (await self.get_available_tickets())[category] < 1: + all_categories = [t_i.category for t_i in self._ticket_infos] + if category not in all_categories or (await self.get_available_tickets_for_category(category)) < 1: raise TicketNotAvailableError(category) user_balance = await self._accounting_service.get_balance(user_id) - if self._lan_info.ticket_info.get_price(category) > user_balance: + + ticket_info = self.get_ticket_info_by_category(category) + if not ticket_info: + raise TicketNotAvailableError(category) + + if ticket_info.price > user_balance: raise InsufficientFundsError - if self.get_user_ticket(user_id): + if await self.get_user_ticket(user_id): raise UserAlreadyHasTicketError if new_ticket := await self._db_service.generate_ticket_for_user(user_id, category): await self._accounting_service.remove_balance( user_id, - self._lan_info.ticket_info.get_price(new_ticket.category), + ticket_info.price, f"TICKET {new_ticket.ticket_id}" ) logger.debug(f"User {user_id} purchased ticket {new_ticket.ticket_id}") @@ -59,8 +73,9 @@ class TicketingService: if not user_ticket: return False - if self._db_service.delete_ticket(user_ticket.ticket_id): - await self._accounting_service.add_balance(user_id, self._lan_info.ticket_info.get_price(user_ticket.category), f"TICKET REFUND {user_ticket.ticket_id}") + ticket_info = self.get_ticket_info_by_category(user_ticket.category) + if await self._db_service.delete_ticket(user_ticket.ticket_id): + await self._accounting_service.add_balance(user_id, ticket_info.price, f"TICKET REFUND {user_ticket.ticket_id}") logger.debug(f"User {user_id} refunded ticket {user_ticket.ticket_id}") return True diff --git a/src/ez_lan_manager/types/ConfigurationTypes.py b/src/ez_lan_manager/types/ConfigurationTypes.py index e1d06d2..9fb073a 100644 --- a/src/ez_lan_manager/types/ConfigurationTypes.py +++ b/src/ez_lan_manager/types/ConfigurationTypes.py @@ -1,4 +1,3 @@ -from copy import copy from dataclasses import dataclass from datetime import datetime from pathlib import Path @@ -17,26 +16,11 @@ class DatabaseConfiguration: @dataclass(frozen=True) class TicketInfo: - default_category: str - categories: list[str] - _prices: dict[str, int] - _available_tickets: dict[str, int] - - def get_price(self, category: str) -> int: - try: - return self._prices[category] - except KeyError: - raise NoSuchCategoryError - - def get_available_tickets(self, category: str) -> int: - try: - return self._available_tickets[category] - except KeyError: - raise NoSuchCategoryError - - @property - def total_available_tickets(self): - return copy(self._available_tickets) + category: str + total_tickets: int + price: int + description: str + is_default: bool @dataclass(frozen=True) class MailingServiceConfiguration: @@ -50,7 +34,6 @@ class MailingServiceConfiguration: class LanInfo: name: str iteration: str - ticket_info: TicketInfo date_from: datetime date_till: datetime organizer_mail: str