refactor ticket info config and service
This commit is contained in:
parent
eb7d94d46c
commit
5ed1230fde
@ -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
|
||||||
|
|||||||
@ -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)
|
||||||
|
|
||||||
|
|||||||
@ -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"]
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user