refactor ticket info config and service

This commit is contained in:
David Rodenkirchen 2024-09-04 10:40:57 +02:00
parent eb7d94d46c
commit 5ed1230fde
5 changed files with 66 additions and 50 deletions

View File

@ -1,9 +1,6 @@
[lan] [lan]
name="EZ LAN" name="EZ LAN"
iteration="0.5" 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_from="2024-10-30 15:00:00"
date_till="2024-11-01 12:00:00" date_till="2024-11-01 12:00:00"
organizer_mail="tech@example.com" organizer_mail="tech@example.com"
@ -25,4 +22,18 @@
[seating] [seating]
base_svg_path="" 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

View File

@ -23,7 +23,7 @@ def init_services() -> tuple[AccountingService, CateringService, ConfigurationSe
accounting_service = AccountingService(db_service) accounting_service = AccountingService(db_service)
news_service = NewsService(db_service) news_service = NewsService(db_service)
mailing_service = MailingService(configuration_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) 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) catering_service = CateringService(db_service, accounting_service, user_service)

View File

@ -6,7 +6,7 @@ import tomllib
from from_root import from_root 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]) logger = logging.getLogger(__name__.split(".")[-1])
@ -58,16 +58,9 @@ class ConfigurationService:
def get_lan_info(self) -> LanInfo: def get_lan_info(self) -> LanInfo:
try: try:
lan_info = self._config["lan"] 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( return LanInfo(
name=lan_info["name"], name=lan_info["name"],
iteration=lan_info["iteration"], iteration=lan_info["iteration"],
ticket_info=ticket_info,
date_from=datetime.strptime(lan_info["date_from"], "%Y-%m-%d %H:%M:%S"), 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"), date_till=datetime.strptime(lan_info["date_till"], "%Y-%m-%d %H:%M:%S"),
organizer_mail=lan_info["organizer_mail"] organizer_mail=lan_info["organizer_mail"]
@ -90,10 +83,24 @@ class ConfigurationService:
logger.fatal("Error loading seating configuration, exiting...") logger.fatal("Error loading seating configuration, exiting...")
sys.exit(1) 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 @property
def APP_VERSION(self) -> str: def APP_VERSION(self) -> str:
return self._version return self._version
@property @property
def DEV_MODE_ACTIVE(self) -> bool: def DEV_MODE_ACTIVE(self) -> bool:
return self._config["dev_mode_active"] return self._config["misc"]["dev_mode_active"]

View File

@ -3,7 +3,7 @@ from typing import Optional
from src.ez_lan_manager.services.AccountingService import AccountingService, InsufficientFundsError from src.ez_lan_manager.services.AccountingService import AccountingService, InsufficientFundsError
from src.ez_lan_manager.services.DatabaseService import DatabaseService 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 from src.ez_lan_manager.types.Ticket import Ticket
logger = logging.getLogger(__name__.split(".")[-1]) logger = logging.getLogger(__name__.split(".")[-1])
@ -16,37 +16,51 @@ class UserAlreadyHasTicketError(Exception):
pass pass
class TicketingService: class TicketingService:
def __init__(self, lan_info: LanInfo, db_service: DatabaseService, accounting_service: AccountingService) -> None: def __init__(self, ticket_infos: tuple[TicketInfo, ...], db_service: DatabaseService, accounting_service: AccountingService) -> None:
self._lan_info = lan_info self._ticket_infos = ticket_infos
self._db_service = db_service self._db_service = db_service
self._accounting_service = accounting_service self._accounting_service = accounting_service
async def get_total_tickets(self) -> int: def get_ticket_info_by_category(self, category: str) -> Optional[TicketInfo]:
return sum([self._lan_info.ticket_info.get_available_tickets(c) for c in self._lan_info.ticket_info.categories]) 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() all_tickets = await self._db_service.get_tickets()
for ticket in all_tickets: for ticket in all_tickets:
result[ticket.category] -= 1 if ticket.category == category:
result -= 1
return result return result
async def purchase_ticket(self, user_id: int, category: str) -> Ticket: 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) raise TicketNotAvailableError(category)
user_balance = await self._accounting_service.get_balance(user_id) 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 raise InsufficientFundsError
if self.get_user_ticket(user_id): if await self.get_user_ticket(user_id):
raise UserAlreadyHasTicketError raise UserAlreadyHasTicketError
if new_ticket := await self._db_service.generate_ticket_for_user(user_id, category): if new_ticket := await self._db_service.generate_ticket_for_user(user_id, category):
await self._accounting_service.remove_balance( await self._accounting_service.remove_balance(
user_id, user_id,
self._lan_info.ticket_info.get_price(new_ticket.category), ticket_info.price,
f"TICKET {new_ticket.ticket_id}" f"TICKET {new_ticket.ticket_id}"
) )
logger.debug(f"User {user_id} purchased 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: if not user_ticket:
return False return False
if self._db_service.delete_ticket(user_ticket.ticket_id): ticket_info = self.get_ticket_info_by_category(user_ticket.category)
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}") 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}") logger.debug(f"User {user_id} refunded ticket {user_ticket.ticket_id}")
return True return True

View File

@ -1,4 +1,3 @@
from copy import copy
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
@ -17,26 +16,11 @@ class DatabaseConfiguration:
@dataclass(frozen=True) @dataclass(frozen=True)
class TicketInfo: class TicketInfo:
default_category: str category: str
categories: list[str] total_tickets: int
_prices: dict[str, int] price: int
_available_tickets: dict[str, int] description: str
is_default: bool
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)
@dataclass(frozen=True) @dataclass(frozen=True)
class MailingServiceConfiguration: class MailingServiceConfiguration:
@ -50,7 +34,6 @@ class MailingServiceConfiguration:
class LanInfo: class LanInfo:
name: str name: str
iteration: str iteration: str
ticket_info: TicketInfo
date_from: datetime date_from: datetime
date_till: datetime date_till: datetime
organizer_mail: str organizer_mail: str