aiomysql refactor
This commit is contained in:
parent
a9597b5c4f
commit
30b32a4c02
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
@ -1,4 +1,6 @@
|
||||
import logging
|
||||
from asyncio import get_event_loop
|
||||
|
||||
import sys
|
||||
|
||||
from pathlib import Path
|
||||
@ -27,13 +29,7 @@ if __name__ == "__main__":
|
||||
corner_radius_large=0,
|
||||
font=Font(from_root("src/ez_lan_manager/assets/fonts/joystix.otf"))
|
||||
)
|
||||
|
||||
try:
|
||||
services = init_services()
|
||||
except NoDatabaseConnectionError:
|
||||
logger.fatal("Could not connect to database, exiting...")
|
||||
sys.exit(1)
|
||||
|
||||
services = init_services()
|
||||
|
||||
lan_info = services[2].get_lan_info()
|
||||
|
||||
@ -41,6 +37,12 @@ if __name__ == "__main__":
|
||||
await session.set_title(lan_info.name)
|
||||
session.attach(SessionStorage())
|
||||
|
||||
async def on_app_start(a: App) -> None:
|
||||
init_result = await a.default_attachments[3].init_db_pool()
|
||||
if not init_result:
|
||||
logger.fatal("Could not connect to database, exiting...")
|
||||
sys.exit(1)
|
||||
|
||||
app = App(
|
||||
name="EZ LAN Manager",
|
||||
pages=[
|
||||
@ -138,6 +140,7 @@ if __name__ == "__main__":
|
||||
assets_dir=Path(__file__).parent / "assets",
|
||||
default_attachments=services,
|
||||
on_session_start=on_session_start,
|
||||
on_app_start=on_app_start,
|
||||
icon=from_root("src/ez_lan_manager/assets/img/favicon.png"),
|
||||
meta_tags={
|
||||
"robots": "INDEX,FOLLOW",
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
from datetime import datetime
|
||||
from typing import Callable
|
||||
|
||||
import rio
|
||||
from rio import Component, Row, Text, IconButton, TextStyle, Color
|
||||
from rio import Component, Row, Text, TextStyle, Color
|
||||
|
||||
from src.ez_lan_manager import AccountingService
|
||||
from src.ez_lan_manager.types.CateringOrder import CateringOrderStatus
|
||||
|
||||
MAX_LEN = 24
|
||||
|
||||
@ -6,19 +6,13 @@ from src.ez_lan_manager.components.UserInfoAndLoginBox import UserInfoAndLoginBo
|
||||
from src.ez_lan_manager.types.SessionStorage import SessionStorage
|
||||
|
||||
class DesktopNavigation(Component):
|
||||
def __post_init__(self) -> None:
|
||||
self.session[SessionStorage].subscribe_to_logged_in_or_out_event(self.__class__.__name__, self.refresh_cb)
|
||||
|
||||
async def refresh_cb(self) -> None:
|
||||
await self.force_refresh()
|
||||
|
||||
def build(self) -> Component:
|
||||
lan_info = self.session[ConfigurationService].get_lan_info()
|
||||
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=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),
|
||||
UserInfoAndLoginBox(refresh_cb=self.refresh_cb),
|
||||
UserInfoAndLoginBox(),
|
||||
DesktopNavigationButton("News", "./news"),
|
||||
Spacer(min_height=1),
|
||||
DesktopNavigationButton(f"Über {lan_info.name} {lan_info.iteration}", "./overview"),
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
from typing import Optional
|
||||
|
||||
import rio
|
||||
from rio import Component, Column, Text, TextStyle, Button, Row, ScrollContainer, Spacer
|
||||
|
||||
@ -11,9 +13,11 @@ from src.ez_lan_manager.types.SessionStorage import SessionStorage
|
||||
|
||||
class ShoppingCartAndOrders(Component):
|
||||
show_cart: bool = True
|
||||
orders: list[CateringOrder] = []
|
||||
|
||||
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)
|
||||
|
||||
async def on_remove_item(self, list_id: int) -> None:
|
||||
catering_service = self.session[CateringService]
|
||||
@ -36,7 +40,7 @@ class ShoppingCartAndOrders(Component):
|
||||
if not user_id:
|
||||
return
|
||||
cart = catering_service.get_cart(user_id)
|
||||
cart.append(catering_service.get_menu_item_by_id(article_id))
|
||||
cart.append(await catering_service.get_menu_item_by_id(article_id))
|
||||
catering_service.save_cart(user_id, cart)
|
||||
await self.force_refresh()
|
||||
|
||||
@ -99,14 +103,13 @@ class ShoppingCartAndOrders(Component):
|
||||
)
|
||||
)
|
||||
else:
|
||||
orders = catering_service.get_orders_for_user(user_id)
|
||||
orders_container = ScrollContainer(
|
||||
content=Column(
|
||||
*[CateringOrderItem(
|
||||
order_id=order_item.order_id,
|
||||
order_datetime=order_item.order_date,
|
||||
order_status=order_item.status,
|
||||
) for order_item in orders],
|
||||
) for order_item in self.orders],
|
||||
Spacer(grow_y=True)
|
||||
),
|
||||
min_height=8,
|
||||
|
||||
@ -1,15 +1,17 @@
|
||||
import logging
|
||||
from random import choice
|
||||
from typing import Callable
|
||||
from typing import Optional
|
||||
|
||||
from rio import Component, Column, Text, Row, Rectangle, Button, TextStyle, Color, Spacer, TextInput, Link
|
||||
from rio import Component, Column, Text, Row, Rectangle, Button, TextStyle, Color, Spacer, TextInput, Link, event
|
||||
|
||||
from src.ez_lan_manager import UserService
|
||||
from src.ez_lan_manager.components.UserInfoBoxButton import UserInfoBoxButton
|
||||
from src.ez_lan_manager.services.AccountingService import AccountingService
|
||||
from src.ez_lan_manager.services.DatabaseService import NoDatabaseConnectionError, DatabaseService
|
||||
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
|
||||
|
||||
logger = logging.getLogger(__name__.split(".")[-1])
|
||||
@ -39,9 +41,20 @@ class StatusButton(Component):
|
||||
|
||||
|
||||
class UserInfoAndLoginBox(Component):
|
||||
refresh_cb: Callable
|
||||
TEXT_STYLE = TextStyle(fill=Color.from_hex("02dac5"), font_size=0.9)
|
||||
show_login: bool = True
|
||||
user: Optional[User] = None
|
||||
user_balance: Optional[int] = 0
|
||||
user_ticket: Optional[Ticket] = None
|
||||
user_seat: Optional[Seat] = None
|
||||
|
||||
@event.on_populate
|
||||
async def async_init(self) -> None:
|
||||
if self.session[SessionStorage].user_id:
|
||||
self.user = await self.session[UserService].get_user(self.session[SessionStorage].user_id)
|
||||
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)
|
||||
|
||||
@staticmethod
|
||||
def get_greeting() -> str:
|
||||
@ -64,13 +77,13 @@ class UserInfoAndLoginBox(Component):
|
||||
|
||||
async def _on_login_pressed(self) -> None:
|
||||
user_name = self.user_name_input.text.lower()
|
||||
if self.session[UserService].is_login_valid(user_name, self.password_input.text):
|
||||
if await self.session[UserService].is_login_valid(user_name, self.password_input.text):
|
||||
self.user_name_input.is_valid = True
|
||||
self.password_input.is_valid = True
|
||||
self.login_button.is_loading = False
|
||||
await self.session[SessionStorage].set_user_id(self.session[UserService].get_user(user_name).user_id)
|
||||
await self.session[SessionStorage].set_user_id((await self.session[UserService].get_user(user_name)).user_id)
|
||||
await self.async_init()
|
||||
self.show_login = False
|
||||
await self.refresh_cb()
|
||||
else:
|
||||
self.user_name_input.is_valid = False
|
||||
self.password_input.is_valid = False
|
||||
@ -114,7 +127,7 @@ class UserInfoAndLoginBox(Component):
|
||||
on_press=lambda: self.session.navigate_to("./forgot-password")
|
||||
)
|
||||
|
||||
if self.show_login and self.session[SessionStorage].user_id is None:
|
||||
if self.user is None and self.session[SessionStorage].user_id is None:
|
||||
return Rectangle(
|
||||
content=Column(
|
||||
self.user_name_input,
|
||||
@ -139,25 +152,31 @@ class UserInfoAndLoginBox(Component):
|
||||
margin_top=0.3,
|
||||
margin_bottom=2
|
||||
)
|
||||
elif self.user is None and self.session[SessionStorage].user_id is not None:
|
||||
return Rectangle(
|
||||
content=Column(),
|
||||
fill=Color.TRANSPARENT,
|
||||
min_height=8,
|
||||
min_width=12,
|
||||
align_x=0.5,
|
||||
margin_top=0.3,
|
||||
margin_bottom=2
|
||||
)
|
||||
else:
|
||||
user = self.session[UserService].get_user(self.session[SessionStorage].user_id)
|
||||
if user is None:
|
||||
logger.warning("User could not be found, this should not have happend.")
|
||||
a_s = self.session[AccountingService]
|
||||
return Rectangle(
|
||||
content=Column(
|
||||
Text(f"{self.get_greeting()},", style=TextStyle(fill=Color.from_hex("02dac5"), font_size=0.9), justify="center"),
|
||||
Text(f"{user.user_name}", style=TextStyle(fill=Color.from_hex("02dac5"), font_size=1.2), justify="center"),
|
||||
Text(f"{self.user.user_name}", style=TextStyle(fill=Color.from_hex("02dac5"), font_size=1.2), justify="center"),
|
||||
Row(
|
||||
StatusButton(label="TICKET", target_url="./buy_ticket",
|
||||
enabled=self.session[TicketingService].get_user_ticket(user.user_id) is not None),
|
||||
enabled=self.user_ticket is not None),
|
||||
StatusButton(label="SITZPLATZ", target_url="./seating",
|
||||
enabled=self.session[SeatingService].get_user_seat(user.user_id) is not None),
|
||||
enabled=self.user_seat is not None),
|
||||
proportions=(50, 50),
|
||||
grow_y=False
|
||||
),
|
||||
UserInfoBoxButton("Profil bearbeiten", "./edit-profile"),
|
||||
UserInfoBoxButton(f"Guthaben: {a_s.make_euro_string_from_int(a_s.get_balance(user.user_id))}", "./account"),
|
||||
UserInfoBoxButton(f"Guthaben: {self.session[AccountingService].make_euro_string_from_int(self.user_balance)}", "./account"),
|
||||
Button(
|
||||
content=Text("Ausloggen", style=TextStyle(fill=Color.from_hex("02dac5"), font_size=0.6)),
|
||||
shape="rectangle",
|
||||
|
||||
@ -1,22 +1,47 @@
|
||||
from rio import Column, Component, event, Text, TextStyle, Button, Color, Spacer, Revealer, Row
|
||||
from asyncio import sleep
|
||||
from typing import Optional
|
||||
|
||||
from rio import Column, Component, event, Text, TextStyle, Button, Color, Spacer, Revealer, Row, ProgressCircle
|
||||
|
||||
from src.ez_lan_manager import ConfigurationService, UserService, AccountingService
|
||||
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
|
||||
from src.ez_lan_manager.pages import BasePage
|
||||
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[int] = None
|
||||
transaction_history: list[Transaction] = list()
|
||||
|
||||
@event.on_populate
|
||||
async def on_populate(self) -> None:
|
||||
await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - Guthabenkonto")
|
||||
self.user = await self.session[UserService].get_user(self.session[SessionStorage].user_id)
|
||||
self.balance = await self.session[AccountingService].get_balance(self.user.user_id)
|
||||
self.transaction_history = await self.session[AccountingService].get_transaction_history(self.user.user_id)
|
||||
|
||||
async def _on_banking_info_press(self):
|
||||
self.banking_info_revealer.is_open = not self.banking_info_revealer.is_open
|
||||
|
||||
def build(self) -> Component:
|
||||
user = self.session[UserService].get_user(self.session[SessionStorage].user_id)
|
||||
a_s = self.session[AccountingService]
|
||||
if not self.user and not self.balance:
|
||||
return BasePage(
|
||||
content=Column(
|
||||
MainViewContentBox(
|
||||
ProgressCircle(
|
||||
color="secondary",
|
||||
align_x=0.5,
|
||||
margin_top=2,
|
||||
margin_bottom=2
|
||||
)
|
||||
),
|
||||
align_y = 0,
|
||||
)
|
||||
)
|
||||
|
||||
self.banking_info_revealer = Revealer(
|
||||
header=None,
|
||||
content=Column(
|
||||
@ -45,7 +70,7 @@ class AccountPage(Component):
|
||||
align_x=0.2
|
||||
),
|
||||
Text(
|
||||
f"AUFLADUNG - {user.user_id} - {user.user_name}",
|
||||
f"AUFLADUNG - {self.user.user_id} - {self.user.user_name}",
|
||||
style=TextStyle(
|
||||
fill=self.session.theme.neutral_color
|
||||
),
|
||||
@ -73,7 +98,7 @@ class AccountPage(Component):
|
||||
)
|
||||
)
|
||||
|
||||
for transaction in sorted(self.session[AccountingService].get_transaction_history(user.user_id), key=lambda t: t.transaction_date, reverse=True):
|
||||
for transaction in sorted(self.transaction_history, key=lambda t: t.transaction_date, reverse=True):
|
||||
transaction_history.add(
|
||||
Row(
|
||||
Text(
|
||||
@ -89,7 +114,7 @@ class AccountPage(Component):
|
||||
align_x=0
|
||||
),
|
||||
Text(
|
||||
f"{'-' if transaction.is_debit else '+'}{a_s.make_euro_string_from_int(transaction.value)}",
|
||||
f"{'-' if transaction.is_debit else '+'}{AccountingService.make_euro_string_from_int(transaction.value)}",
|
||||
style=TextStyle(
|
||||
fill=self.session.theme.danger_color if transaction.is_debit else self.session.theme.success_color,
|
||||
font_size=0.8
|
||||
@ -106,7 +131,7 @@ class AccountPage(Component):
|
||||
content=Column(
|
||||
MainViewContentBox(
|
||||
content=Text(
|
||||
f"Kontostand: {a_s.make_euro_string_from_int(a_s.get_balance(user.user_id))}",
|
||||
f"Kontostand: {AccountingService.make_euro_string_from_int(self.balance)}",
|
||||
style=TextStyle(
|
||||
fill=self.session.theme.background_color,
|
||||
font_size=1.2
|
||||
|
||||
@ -4,7 +4,7 @@ from typing import * # type: ignore
|
||||
|
||||
from rio import Component, event, Spacer, Card, Container, Column, Row, TextStyle, Color, Text
|
||||
|
||||
from src.ez_lan_manager import ConfigurationService, DatabaseService
|
||||
from src.ez_lan_manager import ConfigurationService
|
||||
from src.ez_lan_manager.components.DesktopNavigation import DesktopNavigation
|
||||
|
||||
class BasePage(Component):
|
||||
@ -14,11 +14,6 @@ class BasePage(Component):
|
||||
async def on_window_size_change(self):
|
||||
await self.force_refresh()
|
||||
|
||||
@event.on_populate
|
||||
async def check_db_connection(self):
|
||||
if not self.session[DatabaseService].is_connected:
|
||||
self.session.navigate_to("./db-error")
|
||||
|
||||
def build(self) -> Component:
|
||||
if self.content is None:
|
||||
content = Spacer()
|
||||
|
||||
@ -1,16 +1,19 @@
|
||||
from rio import Column, Component, event, TextStyle, Text, Spacer, Revealer, SwitcherBar, SwitcherBarChangeEvent
|
||||
from typing import Optional
|
||||
|
||||
from rio import Column, Component, event, TextStyle, Text, Spacer, Revealer, SwitcherBar, SwitcherBarChangeEvent, ProgressCircle
|
||||
|
||||
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.pages import BasePage
|
||||
from src.ez_lan_manager.types.CateringMenuItem import CateringMenuItemCategory
|
||||
from src.ez_lan_manager.types.CateringMenuItem import CateringMenuItemCategory, CateringMenuItem
|
||||
from src.ez_lan_manager.types.SessionStorage import SessionStorage
|
||||
|
||||
|
||||
class CateringPage(Component):
|
||||
show_cart = True
|
||||
all_menu_items: Optional[list[CateringMenuItem]] = None
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
self.session[SessionStorage].subscribe_to_logged_in_or_out_event(self.__class__.__name__, self.on_user_logged_in_status_changed)
|
||||
@ -18,6 +21,8 @@ class CateringPage(Component):
|
||||
@event.on_populate
|
||||
async def on_populate(self) -> None:
|
||||
await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - Catering")
|
||||
self.all_menu_items = await self.session[CateringService].get_menu()
|
||||
|
||||
|
||||
async def on_user_logged_in_status_changed(self) -> None:
|
||||
await self.force_refresh()
|
||||
@ -25,9 +30,13 @@ class CateringPage(Component):
|
||||
async def on_switcher_bar_changed(self, _: SwitcherBarChangeEvent) -> None:
|
||||
await self.shopping_cart_and_orders.switch()
|
||||
|
||||
@staticmethod
|
||||
def get_menu_items_by_category(all_menu_items: list[CateringMenuItem], category: Optional[CateringMenuItemCategory]) -> list[CateringMenuItem]:
|
||||
return list(filter(lambda item: item.category == category, all_menu_items))
|
||||
|
||||
|
||||
def build(self) -> Component:
|
||||
user_id = self.session[SessionStorage].user_id
|
||||
catering_service = self.session[CateringService]
|
||||
self.shopping_cart_and_orders = ShoppingCartAndOrders()
|
||||
switcher_bar = SwitcherBar(
|
||||
values=["cart", "orders"],
|
||||
@ -58,12 +67,14 @@ class CateringPage(Component):
|
||||
)
|
||||
) if user_id else Spacer()
|
||||
|
||||
return BasePage(
|
||||
content=Column(
|
||||
# SHOPPING CART
|
||||
shopping_cart_and_orders_container,
|
||||
# ITEM SELECTION
|
||||
MainViewContentBox(
|
||||
menu = [MainViewContentBox(
|
||||
ProgressCircle(
|
||||
color="secondary",
|
||||
align_x=0.5,
|
||||
margin_top=2,
|
||||
margin_bottom=2
|
||||
)
|
||||
)] if not self.all_menu_items else [MainViewContentBox(
|
||||
Revealer(
|
||||
header="Snacks",
|
||||
content=Column(
|
||||
@ -75,7 +86,7 @@ class CateringPage(Component):
|
||||
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
||||
additional_info=catering_menu_item.additional_info,
|
||||
is_grey=idx % 2 == 0
|
||||
) for idx, catering_menu_item in enumerate(catering_service.get_menu(CateringMenuItemCategory.SNACK))],
|
||||
) for idx, catering_menu_item in enumerate(self.get_menu_items_by_category(self.all_menu_items, CateringMenuItemCategory.SNACK))],
|
||||
),
|
||||
header_style=TextStyle(
|
||||
fill=self.session.theme.background_color,
|
||||
@ -97,7 +108,7 @@ class CateringPage(Component):
|
||||
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
||||
additional_info=catering_menu_item.additional_info,
|
||||
is_grey=idx % 2 == 0
|
||||
) for idx, catering_menu_item in enumerate(catering_service.get_menu(CateringMenuItemCategory.BREAKFAST))],
|
||||
) for idx, catering_menu_item in enumerate(self.get_menu_items_by_category(self.all_menu_items, CateringMenuItemCategory.BREAKFAST))],
|
||||
),
|
||||
header_style=TextStyle(
|
||||
fill=self.session.theme.background_color,
|
||||
@ -119,7 +130,7 @@ class CateringPage(Component):
|
||||
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
||||
additional_info=catering_menu_item.additional_info,
|
||||
is_grey=idx % 2 == 0
|
||||
) for idx, catering_menu_item in enumerate(catering_service.get_menu(CateringMenuItemCategory.MAIN_COURSE))],
|
||||
) for idx, catering_menu_item in enumerate(self.get_menu_items_by_category(self.all_menu_items, CateringMenuItemCategory.MAIN_COURSE))],
|
||||
),
|
||||
header_style=TextStyle(
|
||||
fill=self.session.theme.background_color,
|
||||
@ -141,7 +152,7 @@ class CateringPage(Component):
|
||||
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
||||
additional_info=catering_menu_item.additional_info,
|
||||
is_grey=idx % 2 == 0
|
||||
) for idx, catering_menu_item in enumerate(catering_service.get_menu(CateringMenuItemCategory.DESSERT))],
|
||||
) for idx, catering_menu_item in enumerate(self.get_menu_items_by_category(self.all_menu_items, CateringMenuItemCategory.DESSERT))],
|
||||
),
|
||||
header_style=TextStyle(
|
||||
fill=self.session.theme.background_color,
|
||||
@ -163,7 +174,7 @@ class CateringPage(Component):
|
||||
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
||||
additional_info=catering_menu_item.additional_info,
|
||||
is_grey=idx % 2 == 0
|
||||
) for idx, catering_menu_item in enumerate(catering_service.get_menu(CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC))],
|
||||
) for idx, catering_menu_item in enumerate(self.get_menu_items_by_category(self.all_menu_items, CateringMenuItemCategory.BEVERAGE_NON_ALCOHOLIC))],
|
||||
),
|
||||
header_style=TextStyle(
|
||||
fill=self.session.theme.background_color,
|
||||
@ -185,7 +196,7 @@ class CateringPage(Component):
|
||||
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
||||
additional_info=catering_menu_item.additional_info,
|
||||
is_grey=idx % 2 == 0
|
||||
) for idx, catering_menu_item in enumerate(catering_service.get_menu(CateringMenuItemCategory.BEVERAGE_ALCOHOLIC))],
|
||||
) for idx, catering_menu_item in enumerate(self.get_menu_items_by_category(self.all_menu_items, CateringMenuItemCategory.BEVERAGE_ALCOHOLIC))],
|
||||
),
|
||||
header_style=TextStyle(
|
||||
fill=self.session.theme.background_color,
|
||||
@ -207,7 +218,7 @@ class CateringPage(Component):
|
||||
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
||||
additional_info=catering_menu_item.additional_info,
|
||||
is_grey=idx % 2 == 0
|
||||
) for idx, catering_menu_item in enumerate(catering_service.get_menu(CateringMenuItemCategory.BEVERAGE_COCKTAIL))],
|
||||
) for idx, catering_menu_item in enumerate(self.get_menu_items_by_category(self.all_menu_items, CateringMenuItemCategory.BEVERAGE_COCKTAIL))],
|
||||
),
|
||||
header_style=TextStyle(
|
||||
fill=self.session.theme.background_color,
|
||||
@ -229,7 +240,7 @@ class CateringPage(Component):
|
||||
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
||||
additional_info=catering_menu_item.additional_info,
|
||||
is_grey=idx % 2 == 0
|
||||
) for idx, catering_menu_item in enumerate(catering_service.get_menu(CateringMenuItemCategory.BEVERAGE_SHOT))],
|
||||
) for idx, catering_menu_item in enumerate(self.get_menu_items_by_category(self.all_menu_items, CateringMenuItemCategory.BEVERAGE_SHOT))],
|
||||
),
|
||||
header_style=TextStyle(
|
||||
fill=self.session.theme.background_color,
|
||||
@ -251,7 +262,7 @@ class CateringPage(Component):
|
||||
is_sensitive=(user_id is not None) and not catering_menu_item.is_disabled,
|
||||
additional_info=catering_menu_item.additional_info,
|
||||
is_grey=idx % 2 == 0
|
||||
) for idx, catering_menu_item in enumerate(catering_service.get_menu(CateringMenuItemCategory.NON_FOOD))],
|
||||
) for idx, catering_menu_item in enumerate(self.get_menu_items_by_category(self.all_menu_items, CateringMenuItemCategory.NON_FOOD))],
|
||||
),
|
||||
header_style=TextStyle(
|
||||
fill=self.session.theme.background_color,
|
||||
@ -260,7 +271,14 @@ class CateringPage(Component):
|
||||
margin=1,
|
||||
align_y=0.5
|
||||
)
|
||||
),
|
||||
)]
|
||||
|
||||
return BasePage(
|
||||
content=Column(
|
||||
# SHOPPING CART
|
||||
shopping_cart_and_orders_container,
|
||||
# ITEM SELECTION
|
||||
*menu,
|
||||
align_y=0
|
||||
)
|
||||
)
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional
|
||||
|
||||
from rio import Text, Column, TextStyle, Component, event, TextInput, MultiLineTextInput, Row, Button
|
||||
|
||||
@ -7,6 +8,7 @@ from src.ez_lan_manager.components.AnimatedText import AnimatedText
|
||||
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
|
||||
from src.ez_lan_manager.pages import BasePage
|
||||
from src.ez_lan_manager.types.SessionStorage import SessionStorage
|
||||
from src.ez_lan_manager.types.User import User
|
||||
|
||||
|
||||
class ContactPage(Component):
|
||||
@ -14,10 +16,15 @@ class ContactPage(Component):
|
||||
# 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
|
||||
|
||||
@event.on_populate
|
||||
async def on_populate(self) -> None:
|
||||
await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - Kontakt")
|
||||
if self.session[SessionStorage].user_id is not None:
|
||||
self.user = await self.session[UserService].get_user(self.session[SessionStorage].user_id)
|
||||
else:
|
||||
self.user = None
|
||||
|
||||
async def on_send_pressed(self) -> None:
|
||||
error_msg = ""
|
||||
@ -51,11 +58,6 @@ class ContactPage(Component):
|
||||
await self.animated_text.display_text(True, "Nachricht erfolgreich gesendet!")
|
||||
|
||||
def build(self) -> Component:
|
||||
if self.session[SessionStorage].user_id is not None:
|
||||
user = self.session[UserService].get_user(self.session[SessionStorage].user_id)
|
||||
else:
|
||||
user = None
|
||||
|
||||
self.animated_text = AnimatedText(
|
||||
margin_top = 2,
|
||||
margin_bottom = 1,
|
||||
@ -64,7 +66,7 @@ class ContactPage(Component):
|
||||
|
||||
self.email_input = TextInput(
|
||||
label="E-Mail Adresse",
|
||||
text="" if not user else user.user_mail,
|
||||
text="" if not self.user else self.user.user_mail,
|
||||
margin_left=1,
|
||||
margin_right=1,
|
||||
margin_bottom=1,
|
||||
|
||||
@ -15,12 +15,12 @@ class DbErrorPage(Component):
|
||||
async def on_window_size_change(self) -> None:
|
||||
await self.force_refresh()
|
||||
|
||||
@event.on_mount
|
||||
async def retry_db_connect(self) -> None:
|
||||
await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - Fehler")
|
||||
while not self.session[DatabaseService].is_connected:
|
||||
await sleep(2)
|
||||
self.session.navigate_to("./")
|
||||
# @event.on_mount
|
||||
# async def retry_db_connect(self) -> None:
|
||||
# await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - Fehler")
|
||||
# while not self.session[DatabaseService].is_connected:
|
||||
# await sleep(2)
|
||||
# self.session.navigate_to("./")
|
||||
|
||||
def build(self) -> Component:
|
||||
content = Card(
|
||||
|
||||
@ -3,7 +3,8 @@ from hashlib import sha256
|
||||
from typing import Optional
|
||||
|
||||
from from_root import from_root
|
||||
from rio import Column, Component, event, Text, TextStyle, Button, Color, Row, TextInput, Image, TextInputChangeEvent, NoFileSelectedError
|
||||
from rio import Column, Component, event, Text, TextStyle, Button, Color, Row, TextInput, Image, TextInputChangeEvent, NoFileSelectedError, \
|
||||
ProgressCircle
|
||||
from email_validator import validate_email, EmailNotValidError
|
||||
|
||||
from src.ez_lan_manager import ConfigurationService, UserService
|
||||
@ -15,6 +16,8 @@ from src.ez_lan_manager.types.User import User
|
||||
|
||||
|
||||
class EditProfilePage(Component):
|
||||
user: Optional[User] = None
|
||||
pfp: Optional[bytes] = None
|
||||
@staticmethod
|
||||
def optional_date_to_str(d: Optional[date]) -> str:
|
||||
if not d:
|
||||
@ -24,6 +27,8 @@ class EditProfilePage(Component):
|
||||
@event.on_populate
|
||||
async def on_populate(self) -> None:
|
||||
await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - Profil bearbeiten")
|
||||
self.user = await self.session[UserService].get_user(self.session[SessionStorage].user_id)
|
||||
self.pfp = await self.session[UserService].get_profile_picture(self.user.user_id)
|
||||
|
||||
def on_email_changed(self, change_event: TextInputChangeEvent) -> None:
|
||||
try:
|
||||
@ -58,7 +63,7 @@ class EditProfilePage(Component):
|
||||
return
|
||||
|
||||
image_data = await new_pfp.read_bytes()
|
||||
self.session[UserService].set_profile_picture(self.session[SessionStorage].user_id, image_data)
|
||||
await self.session[UserService].set_profile_picture(self.session[SessionStorage].user_id, image_data)
|
||||
self.pfp_image_container.image = image_data
|
||||
await self.animated_text.display_text(True, "Gespeichert!")
|
||||
|
||||
@ -72,7 +77,7 @@ class EditProfilePage(Component):
|
||||
await self.animated_text.display_text(False, "Passwörter nicht gleich!")
|
||||
return
|
||||
|
||||
user: User = self.session[UserService].get_user(self.session[SessionStorage].user_id)
|
||||
user: User = await self.session[UserService].get_user(self.session[SessionStorage].user_id)
|
||||
user.user_mail = self.email_input.text
|
||||
|
||||
if len(self.birthday_input.text) == 0:
|
||||
@ -86,12 +91,24 @@ class EditProfilePage(Component):
|
||||
if len(self.new_pw_1_input.text.strip()) > 0:
|
||||
user.user_password = sha256(self.new_pw_1_input.text.encode(encoding="utf-8")).hexdigest()
|
||||
|
||||
self.session[UserService].update_user(user)
|
||||
await self.session[UserService].update_user(user)
|
||||
await self.animated_text.display_text(True, "Gespeichert!")
|
||||
|
||||
def build(self) -> Component:
|
||||
user = self.session[UserService].get_user(self.session[SessionStorage].user_id)
|
||||
pfp = self.session[UserService].get_profile_picture(user.user_id)
|
||||
if not self.user:
|
||||
return BasePage(
|
||||
content=Column(
|
||||
MainViewContentBox(
|
||||
ProgressCircle(
|
||||
color="secondary",
|
||||
align_x=0.5,
|
||||
margin_top=2,
|
||||
margin_bottom=2
|
||||
)
|
||||
),
|
||||
align_y = 0
|
||||
)
|
||||
)
|
||||
|
||||
self.animated_text = AnimatedText(
|
||||
margin_top=2,
|
||||
@ -101,7 +118,7 @@ class EditProfilePage(Component):
|
||||
|
||||
self.email_input = TextInput(
|
||||
label="E-Mail Adresse",
|
||||
text=user.user_mail,
|
||||
text=self.user.user_mail,
|
||||
margin_left=1,
|
||||
margin_right=1,
|
||||
margin_bottom=1,
|
||||
@ -110,20 +127,20 @@ class EditProfilePage(Component):
|
||||
)
|
||||
self.first_name_input = TextInput(
|
||||
label="Vorname",
|
||||
text=user.user_first_name,
|
||||
text=self.user.user_first_name,
|
||||
margin_left=1,
|
||||
margin_right=1,
|
||||
grow_x=True
|
||||
)
|
||||
self.last_name_input = TextInput(
|
||||
label="Nachname",
|
||||
text=user.user_last_name,
|
||||
text=self.user.user_last_name,
|
||||
margin_right=1,
|
||||
grow_x=True
|
||||
)
|
||||
self.birthday_input = TextInput(
|
||||
label="Geburtstag (TT.MM.JJJJ)",
|
||||
text=self.optional_date_to_str(user.user_birth_day),
|
||||
text=self.optional_date_to_str(self.user.user_birth_day),
|
||||
margin_left=1,
|
||||
margin_right=1,
|
||||
margin_bottom=1,
|
||||
@ -150,7 +167,7 @@ class EditProfilePage(Component):
|
||||
)
|
||||
|
||||
self.pfp_image_container = Image(
|
||||
from_root("src/ez_lan_manager/assets/img/anon_pfp.png") if pfp is None else pfp,
|
||||
from_root("src/ez_lan_manager/assets/img/anon_pfp.png") if self.pfp is None else self.pfp,
|
||||
align_x=0.5,
|
||||
min_width=10,
|
||||
min_height=10,
|
||||
@ -176,8 +193,8 @@ class EditProfilePage(Component):
|
||||
on_press=self.upload_new_pfp
|
||||
),
|
||||
Row(
|
||||
TextInput(label="Deine User-ID", text=user.user_id, is_sensitive=False, margin_left=1, grow_x=False),
|
||||
TextInput(label="Dein Nickname", text=user.user_name, is_sensitive=False, margin_left=1, margin_right=1, grow_x=True),
|
||||
TextInput(label="Deine User-ID", text=self.user.user_id, is_sensitive=False, margin_left=1, grow_x=False),
|
||||
TextInput(label="Dein Nickname", text=self.user.user_name, is_sensitive=False, margin_left=1, margin_right=1, grow_x=True),
|
||||
margin_bottom=1
|
||||
),
|
||||
self.email_input,
|
||||
|
||||
@ -24,11 +24,11 @@ class ForgotPasswordPage(Component):
|
||||
lan_info = self.session[ConfigurationService].get_lan_info()
|
||||
user_service = self.session[UserService]
|
||||
mailing_service = self.session[MailingService]
|
||||
user = user_service.get_user(self.email_input.text.strip())
|
||||
user = await user_service.get_user(self.email_input.text.strip())
|
||||
if user is not None:
|
||||
new_password = "".join(choices(user_service.ALLOWED_USER_NAME_SYMBOLS, k=16))
|
||||
user.user_password = sha256(new_password.encode(encoding="utf-8")).hexdigest()
|
||||
user_service.update_user(user)
|
||||
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 EZ-LAN Manager der {lan_info.name} ein neues Passwort angefragt. "
|
||||
|
||||
@ -5,37 +5,50 @@ from rio import Column, Component, event, TextStyle, Text, Button, Row, TextInpu
|
||||
from src.ez_lan_manager import ConfigurationService, UserService, TicketingService, SeatingService
|
||||
from src.ez_lan_manager.components.MainViewContentBox import MainViewContentBox
|
||||
from src.ez_lan_manager.pages import BasePage
|
||||
from src.ez_lan_manager.types.Seat import Seat
|
||||
from src.ez_lan_manager.types.User import User
|
||||
|
||||
|
||||
class GuestsPage(Component):
|
||||
table_elements: list[Button] = []
|
||||
users_with_tickets: list[User] = []
|
||||
users_with_seats: dict[User, Seat] = {}
|
||||
user_filter: Optional[str] = None
|
||||
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
user_service = self.session[UserService]
|
||||
all_users = user_service.get_all_users()
|
||||
ticketing_service = self.session[TicketingService]
|
||||
self.users_with_tickets = list(filter(lambda user: ticketing_service.get_user_ticket(user.user_id) is not None, all_users))
|
||||
|
||||
@event.on_populate
|
||||
async def on_populate(self) -> None:
|
||||
await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - Teilnehmer")
|
||||
user_service = self.session[UserService]
|
||||
all_users = await user_service.get_all_users()
|
||||
ticketing_service = self.session[TicketingService]
|
||||
seating_service = self.session[SeatingService]
|
||||
u_w_t = []
|
||||
u_w_s = {}
|
||||
for user in all_users:
|
||||
ticket = await ticketing_service.get_user_ticket(user.user_id)
|
||||
seat = await seating_service.get_user_seat(user.user_id)
|
||||
if ticket is not None:
|
||||
u_w_t.append(user)
|
||||
if seat is not None:
|
||||
u_w_s[user] = seat
|
||||
|
||||
self.users_with_tickets = u_w_t
|
||||
self.users_with_seats = u_w_s
|
||||
|
||||
def on_searchbar_content_change(self, change_event: TextInputChangeEvent) -> None:
|
||||
self.user_filter = change_event.text
|
||||
|
||||
def build(self) -> Component:
|
||||
seating_service = self.session[SeatingService]
|
||||
if self.user_filter:
|
||||
users = [user for user in self.users_with_tickets if self.user_filter.lower() in user.user_name or self.user_filter.lower() in str(user.user_id)]
|
||||
else:
|
||||
users = self.users_with_tickets
|
||||
self.table_elements.clear()
|
||||
for idx, user in enumerate(users):
|
||||
seat = seating_service.get_user_seat(user.user_id)
|
||||
try:
|
||||
seat = self.users_with_seats[user]
|
||||
except KeyError:
|
||||
seat = None
|
||||
self.table_elements.append(
|
||||
Button(
|
||||
content=Row(Text(text=f"{user.user_id:0>4}", align_x=0, margin_right=1), Text(text=user.user_name, grow_x=True, wrap="ellipsize"), Text(text="-" if seat is None else seat.seat_id, align_x=1)),
|
||||
|
||||
@ -12,7 +12,7 @@ class NewsPage(Component):
|
||||
@event.on_populate
|
||||
async def on_populate(self) -> None:
|
||||
await self.session.set_title(f"{self.session[ConfigurationService].get_lan_info().name} - Neuigkeiten")
|
||||
self.news_posts = self.session[NewsService].get_news()[:8]
|
||||
self.news_posts = (await self.session[NewsService].get_news())[:8]
|
||||
|
||||
def build(self) -> Component:
|
||||
posts = [NewsPost(
|
||||
|
||||
@ -62,13 +62,13 @@ class RegisterPage(Component):
|
||||
mailing_service = self.session[MailingService]
|
||||
lan_info = self.session[ConfigurationService].get_lan_info()
|
||||
|
||||
if user_service.get_user(self.email_input.text) is not None or user_service.get_user(self.user_name_input.text) is not None:
|
||||
if await user_service.get_user(self.email_input.text) is not None or await user_service.get_user(self.user_name_input.text) is not None:
|
||||
await self.animated_text.display_text(False, "Benutzername oder E-Mail bereits regestriert!")
|
||||
self.submit_button.is_loading = False
|
||||
return
|
||||
|
||||
try:
|
||||
new_user = user_service.create_user(self.user_name_input.text, self.email_input.text, self.pw_1.text)
|
||||
new_user = await user_service.create_user(self.user_name_input.text, self.email_input.text, self.pw_1.text)
|
||||
if not new_user:
|
||||
raise RuntimeError("User could not be created")
|
||||
except Exception as e:
|
||||
|
||||
@ -13,8 +13,8 @@ class AccountingService:
|
||||
def __init__(self, db_service: DatabaseService) -> None:
|
||||
self._db_service = db_service
|
||||
|
||||
def add_balance(self, user_id: int, balance_to_add: int, reference: str) -> int:
|
||||
self._db_service.add_transaction(Transaction(
|
||||
async def add_balance(self, user_id: int, balance_to_add: int, reference: str) -> int:
|
||||
await self._db_service.add_transaction(Transaction(
|
||||
user_id=user_id,
|
||||
value=balance_to_add,
|
||||
is_debit=False,
|
||||
@ -22,13 +22,13 @@ class AccountingService:
|
||||
transaction_date=datetime.now()
|
||||
))
|
||||
logger.debug(f"Added balance of {self.make_euro_string_from_int(balance_to_add)} to user with ID {user_id}")
|
||||
return self.get_balance(user_id)
|
||||
return await self.get_balance(user_id)
|
||||
|
||||
def remove_balance(self, user_id: int, balance_to_remove: int, reference: str) -> int:
|
||||
current_balance = self.get_balance(user_id)
|
||||
async def remove_balance(self, user_id: int, balance_to_remove: int, reference: str) -> int:
|
||||
current_balance = await self.get_balance(user_id)
|
||||
if (current_balance - balance_to_remove) < 0:
|
||||
raise InsufficientFundsError
|
||||
self._db_service.add_transaction(Transaction(
|
||||
await self._db_service.add_transaction(Transaction(
|
||||
user_id=user_id,
|
||||
value=balance_to_remove,
|
||||
is_debit=True,
|
||||
@ -36,19 +36,19 @@ class AccountingService:
|
||||
transaction_date=datetime.now()
|
||||
))
|
||||
logger.debug(f"Removed balance of {self.make_euro_string_from_int(balance_to_remove)} to user with ID {user_id}")
|
||||
return self.get_balance(user_id)
|
||||
return await self.get_balance(user_id)
|
||||
|
||||
def get_balance(self, user_id: int) -> int:
|
||||
async def get_balance(self, user_id: int) -> int:
|
||||
balance_buffer = 0
|
||||
for transaction in self._db_service.get_all_transactions_for_user(user_id):
|
||||
for transaction in await self._db_service.get_all_transactions_for_user(user_id):
|
||||
if transaction.is_debit:
|
||||
balance_buffer -= transaction.value
|
||||
else:
|
||||
balance_buffer += transaction.value
|
||||
return balance_buffer
|
||||
|
||||
def get_transaction_history(self, user_id: int) -> list[Transaction]:
|
||||
return self._db_service.get_all_transactions_for_user(user_id)
|
||||
async def get_transaction_history(self, user_id: int) -> list[Transaction]:
|
||||
return await self._db_service.get_all_transactions_for_user(user_id)
|
||||
|
||||
@staticmethod
|
||||
def make_euro_string_from_int(cent_int: int) -> str:
|
||||
|
||||
@ -23,93 +23,93 @@ class CateringService:
|
||||
|
||||
# ORDERS
|
||||
|
||||
def place_order(self, menu_items: CateringMenuItemsWithAmount, user_id: int, is_delivery: bool = True) -> CateringOrder:
|
||||
async def place_order(self, menu_items: CateringMenuItemsWithAmount, user_id: int, is_delivery: bool = True) -> CateringOrder:
|
||||
for menu_item in menu_items:
|
||||
if menu_item.is_disabled:
|
||||
raise CateringError("Order includes disabled items")
|
||||
|
||||
user = self._user_service.get_user(user_id)
|
||||
user = await self._user_service.get_user(user_id)
|
||||
if not user:
|
||||
raise CateringError("User does not exist")
|
||||
|
||||
total_price = sum([item.price * quantity for item, quantity in menu_items.items()])
|
||||
if self._accounting_service.get_balance(user_id) < total_price:
|
||||
if await self._accounting_service.get_balance(user_id) < total_price:
|
||||
raise CateringError("Insufficient funds")
|
||||
|
||||
order = self._db_service.add_new_order(menu_items, user_id, is_delivery)
|
||||
order = await self._db_service.add_new_order(menu_items, user_id, is_delivery)
|
||||
if order:
|
||||
self._accounting_service.remove_balance(user_id, total_price, f"CATERING - {order.order_id}")
|
||||
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_int(total_price)}")
|
||||
return order
|
||||
|
||||
def update_order_status(self, order_id: int, new_status: CateringOrderStatus) -> bool:
|
||||
async def update_order_status(self, order_id: int, new_status: CateringOrderStatus) -> bool:
|
||||
if new_status == CateringOrderStatus.CANCELED:
|
||||
# Cancelled orders need to be refunded
|
||||
raise CateringError("Orders cannot be canceled this way, use CateringService.cancel_order")
|
||||
return self._db_service.change_order_status(order_id, new_status)
|
||||
return await self._db_service.change_order_status(order_id, new_status)
|
||||
|
||||
def get_orders(self) -> list[CateringOrder]:
|
||||
return self._db_service.get_orders()
|
||||
async def get_orders(self) -> list[CateringOrder]:
|
||||
return await self._db_service.get_orders()
|
||||
|
||||
def get_orders_for_user(self, user_id: int) -> list[CateringOrder]:
|
||||
return self._db_service.get_orders(user_id=user_id)
|
||||
async def get_orders_for_user(self, user_id: int) -> list[CateringOrder]:
|
||||
return await self._db_service.get_orders(user_id=user_id)
|
||||
|
||||
def get_orders_by_status(self, status: CateringOrderStatus) -> list[CateringOrder]:
|
||||
return self._db_service.get_orders(status=status)
|
||||
async def get_orders_by_status(self, status: CateringOrderStatus) -> list[CateringOrder]:
|
||||
return await self._db_service.get_orders(status=status)
|
||||
|
||||
def cancel_order(self, order: CateringOrder) -> bool:
|
||||
async def cancel_order(self, order: CateringOrder) -> bool:
|
||||
if self._db_service.change_order_status(order.order_id, CateringOrderStatus.CANCELED):
|
||||
self._accounting_service.add_balance(order.customer.user_id, order.price, f"CATERING REFUND - {order.order_id}")
|
||||
await self._accounting_service.add_balance(order.customer.user_id, order.price, f"CATERING REFUND - {order.order_id}")
|
||||
return True
|
||||
return False
|
||||
|
||||
# MENU ITEMS
|
||||
|
||||
def get_menu(self, category: Optional[CateringMenuItemCategory] = None) -> list[CateringMenuItem]:
|
||||
items = self._db_service.get_menu_items()
|
||||
async def get_menu(self, category: Optional[CateringMenuItemCategory] = None) -> list[CateringMenuItem]:
|
||||
items = await self._db_service.get_menu_items()
|
||||
if not category:
|
||||
return items
|
||||
return list(filter(lambda item: item.category == category, items))
|
||||
|
||||
def get_menu_item_by_id(self, menu_item_id: int) -> CateringMenuItem:
|
||||
item = self._db_service.get_menu_item(menu_item_id)
|
||||
async def get_menu_item_by_id(self, menu_item_id: int) -> CateringMenuItem:
|
||||
item = await self._db_service.get_menu_item(menu_item_id)
|
||||
if not item:
|
||||
raise CateringError("Menu item not found")
|
||||
return item
|
||||
|
||||
def add_menu_item(self, name: str, info: str, price: int, category: CateringMenuItemCategory, is_disabled: bool = False) -> CateringMenuItem:
|
||||
if new_item := self._db_service.add_menu_item(name, info, price, category, is_disabled):
|
||||
async def add_menu_item(self, name: str, info: str, price: int, category: CateringMenuItemCategory, is_disabled: bool = False) -> CateringMenuItem:
|
||||
if new_item := await self._db_service.add_menu_item(name, info, price, category, is_disabled):
|
||||
return new_item
|
||||
raise CateringError(f"Could not add item '{name}' to the menu.")
|
||||
|
||||
def remove_menu_item(self, menu_item_id: int) -> bool:
|
||||
return self._db_service.delete_menu_item(menu_item_id)
|
||||
async def remove_menu_item(self, menu_item_id: int) -> bool:
|
||||
return await self._db_service.delete_menu_item(menu_item_id)
|
||||
|
||||
def change_menu_item(self, updated_item: CateringMenuItem) -> bool:
|
||||
return self._db_service.update_menu_item(updated_item)
|
||||
async def change_menu_item(self, updated_item: CateringMenuItem) -> bool:
|
||||
return await self._db_service.update_menu_item(updated_item)
|
||||
|
||||
def disable_menu_item(self, menu_item_id: int) -> bool:
|
||||
async def disable_menu_item(self, menu_item_id: int) -> bool:
|
||||
try:
|
||||
item = self.get_menu_item_by_id(menu_item_id)
|
||||
item = await self.get_menu_item_by_id(menu_item_id)
|
||||
except CateringError:
|
||||
return False
|
||||
item.is_disabled = True
|
||||
return self._db_service.update_menu_item(item)
|
||||
return await self._db_service.update_menu_item(item)
|
||||
|
||||
def enable_menu_item(self, menu_item_id: int) -> bool:
|
||||
async def enable_menu_item(self, menu_item_id: int) -> bool:
|
||||
try:
|
||||
item = self.get_menu_item_by_id(menu_item_id)
|
||||
item = await self.get_menu_item_by_id(menu_item_id)
|
||||
except CateringError:
|
||||
return False
|
||||
item.is_disabled = False
|
||||
return self._db_service.update_menu_item(item)
|
||||
return await self._db_service.update_menu_item(item)
|
||||
|
||||
def disable_menu_items_by_category(self, category: CateringMenuItemCategory) -> bool:
|
||||
items = self.get_menu(category=category)
|
||||
async def disable_menu_items_by_category(self, category: CateringMenuItemCategory) -> bool:
|
||||
items = await self.get_menu(category=category)
|
||||
return all([self.disable_menu_item(item.item_id) for item in items])
|
||||
|
||||
def enable_menu_items_by_category(self, category: CateringMenuItemCategory) -> bool:
|
||||
items = self.get_menu(category=category)
|
||||
async def enable_menu_items_by_category(self, category: CateringMenuItemCategory) -> bool:
|
||||
items = await self.get_menu(category=category)
|
||||
return all([self.enable_menu_item(item.item_id) for item in items])
|
||||
|
||||
# CART
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
import logging
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
from typing import Optional
|
||||
|
||||
from src.ez_lan_manager.services.DatabaseService import DatabaseService
|
||||
@ -11,21 +11,22 @@ class NewsService:
|
||||
def __init__(self, db_service: DatabaseService) -> None:
|
||||
self._db_service = db_service
|
||||
|
||||
def add_news(self, news: News) -> None:
|
||||
async def add_news(self, news: News) -> None:
|
||||
if news.news_id is not None:
|
||||
logger.warning("Can not add news with ID, ignoring...")
|
||||
return
|
||||
self._db_service.add_news(news)
|
||||
await self._db_service.add_news(news)
|
||||
|
||||
def get_news(self, dt_start: Optional[date] = None, dt_end: Optional[date] = None) -> list[News]:
|
||||
async def get_news(self, dt_start: Optional[date] = None, dt_end: Optional[date] = None) -> list[News]:
|
||||
if not dt_end:
|
||||
dt_end = date.today()
|
||||
if not dt_start:
|
||||
dt_start = date(1900, 1, 1)
|
||||
return self._db_service.get_news(dt_start, dt_end)
|
||||
return await self._db_service.get_news(dt_start, dt_end)
|
||||
|
||||
def get_latest_news(self) -> Optional[News]:
|
||||
async def get_latest_news(self) -> Optional[News]:
|
||||
try:
|
||||
return self.get_news(None, date.today())[0]
|
||||
all_news = await self.get_news(None, date.today())
|
||||
return all_news[0]
|
||||
except IndexError:
|
||||
logger.debug("There are no news to fetch")
|
||||
|
||||
@ -36,27 +36,27 @@ class SeatingService:
|
||||
ElementTree.parse(self._seating_configuration.base_svg_path).write(self._seating_plan, encoding="unicode")
|
||||
|
||||
|
||||
def get_seating(self) -> list[Seat]:
|
||||
return self._db_service.get_seating_info()
|
||||
async def get_seating(self) -> list[Seat]:
|
||||
return await self._db_service.get_seating_info()
|
||||
|
||||
def get_seat(self, seat_id: str, cached_data: Optional[list[Seat]] = None) -> Optional[Seat]:
|
||||
all_seats = self.get_seating() if not cached_data else cached_data
|
||||
async def get_seat(self, seat_id: str, cached_data: Optional[list[Seat]] = None) -> Optional[Seat]:
|
||||
all_seats = await self.get_seating() if not cached_data else cached_data
|
||||
for seat in all_seats:
|
||||
if seat.seat_id == seat_id:
|
||||
return seat
|
||||
|
||||
def get_user_seat(self, user_id: int) -> Optional[Seat]:
|
||||
all_seats = self.get_seating()
|
||||
async def get_user_seat(self, user_id: int) -> Optional[Seat]:
|
||||
all_seats = await self.get_seating()
|
||||
for seat in all_seats:
|
||||
if seat.user and seat.user.user_id == user_id:
|
||||
return seat
|
||||
|
||||
def seat_user(self, user_id: int, seat_id: str) -> None:
|
||||
user_ticket = self._ticketing_service.get_user_ticket(user_id)
|
||||
async def seat_user(self, user_id: int, seat_id: str) -> None:
|
||||
user_ticket = await self._ticketing_service.get_user_ticket(user_id)
|
||||
if not user_ticket:
|
||||
raise NoTicketError
|
||||
|
||||
seat = self.get_seat(seat_id)
|
||||
seat = await self.get_seat(seat_id)
|
||||
if not seat:
|
||||
raise SeatNotFoundError
|
||||
|
||||
@ -66,10 +66,10 @@ class SeatingService:
|
||||
if seat.user is not None:
|
||||
raise SeatAlreadyTakenError
|
||||
|
||||
self._db_service.seat_user(seat_id, user_id)
|
||||
self.update_svg_with_seating_status()
|
||||
await self._db_service.seat_user(seat_id, user_id)
|
||||
await self.update_svg_with_seating_status()
|
||||
|
||||
def generate_new_seating_table(self, seating_plan_fp: Path, no_confirm: bool = False) -> None:
|
||||
async def generate_new_seating_table(self, seating_plan_fp: Path, no_confirm: bool = False) -> None:
|
||||
if not no_confirm:
|
||||
confirm = input("WARNING: THIS ACTION WILL DELETE ALL SEATING DATA! TYPE 'AGREE' TO CONTINUE: ")
|
||||
if confirm != "AGREE":
|
||||
@ -95,10 +95,10 @@ class SeatingService:
|
||||
except TypeError:
|
||||
continue
|
||||
|
||||
self._db_service.generate_fresh_seats_table(sorted(seat_ids, key=lambda sd: sd[0]))
|
||||
self.update_svg_with_seating_status()
|
||||
await self._db_service.generate_fresh_seats_table(sorted(seat_ids, key=lambda sd: sd[0]))
|
||||
await self.update_svg_with_seating_status()
|
||||
|
||||
def update_svg_with_seating_status(self) -> None:
|
||||
async def update_svg_with_seating_status(self) -> None:
|
||||
et = ElementTree.parse(self._seating_configuration.base_svg_path)
|
||||
root = et.getroot()
|
||||
namespace = {'svg': root.tag.split('}')[0].strip('{')} if '}' in root.tag else {}
|
||||
@ -113,13 +113,13 @@ class SeatingService:
|
||||
rect_g_pairs.append((last_rect, elem))
|
||||
last_rect = None
|
||||
|
||||
all_seats = self.get_seating()
|
||||
all_seats = await self.get_seating()
|
||||
|
||||
for rect, g in rect_g_pairs:
|
||||
seat_id = self.get_seat_id_from_element(g, namespace)
|
||||
if not seat_id:
|
||||
continue
|
||||
seat = self.get_seat(seat_id, cached_data=all_seats)
|
||||
seat = await self.get_seat(seat_id, cached_data=all_seats)
|
||||
if not seat.is_blocked and seat.user is None:
|
||||
rect.set("fill", "rgb(102, 255, 51)")
|
||||
elif not seat.is_blocked and seat.user is not None:
|
||||
|
||||
@ -21,30 +21,30 @@ class TicketingService:
|
||||
self._db_service = db_service
|
||||
self._accounting_service = accounting_service
|
||||
|
||||
def get_total_tickets(self) -> int:
|
||||
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_available_tickets(self) -> dict[str, int]:
|
||||
async def get_available_tickets(self) -> dict[str, int]:
|
||||
result = self._lan_info.ticket_info.total_available_tickets
|
||||
all_tickets = self._db_service.get_tickets()
|
||||
all_tickets = await self._db_service.get_tickets()
|
||||
for ticket in all_tickets:
|
||||
result[ticket.category] -= 1
|
||||
|
||||
return result
|
||||
|
||||
def purchase_ticket(self, user_id: int, category: str) -> Ticket:
|
||||
if category not in self._lan_info.ticket_info.categories or self.get_available_tickets()[category] < 1:
|
||||
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:
|
||||
raise TicketNotAvailableError(category)
|
||||
|
||||
user_balance = 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:
|
||||
raise InsufficientFundsError
|
||||
|
||||
if self.get_user_ticket(user_id):
|
||||
raise UserAlreadyHasTicketError
|
||||
|
||||
if new_ticket := self._db_service.generate_ticket_for_user(user_id, category):
|
||||
self._accounting_service.remove_balance(
|
||||
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),
|
||||
f"TICKET {new_ticket.ticket_id}"
|
||||
@ -54,20 +54,20 @@ class TicketingService:
|
||||
|
||||
raise RuntimeError("An unknown error occurred while purchasing ticket")
|
||||
|
||||
def refund_ticket(self, user_id: int) -> bool:
|
||||
user_ticket = self.get_user_ticket(user_id)
|
||||
async def refund_ticket(self, user_id: int) -> bool:
|
||||
user_ticket = await self.get_user_ticket(user_id)
|
||||
if not user_ticket:
|
||||
return False
|
||||
|
||||
if self._db_service.delete_ticket(user_ticket.ticket_id):
|
||||
self._accounting_service.add_balance(user_id, self._lan_info.ticket_info.get_price(user_ticket.category), f"TICKET REFUND {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}")
|
||||
logger.debug(f"User {user_id} refunded ticket {user_ticket.ticket_id}")
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def transfer_ticket(self, ticket_id: int, user_id: int) -> bool:
|
||||
return self._db_service.change_ticket_owner(ticket_id, user_id)
|
||||
async def transfer_ticket(self, ticket_id: int, user_id: int) -> bool:
|
||||
return await self._db_service.change_ticket_owner(ticket_id, user_id)
|
||||
|
||||
def get_user_ticket(self, user_id: int) -> Optional[Ticket]:
|
||||
return self._db_service.get_ticket_for_user(user_id)
|
||||
async def get_user_ticket(self, user_id: int) -> Optional[Ticket]:
|
||||
return await self._db_service.get_ticket_for_user(user_id)
|
||||
|
||||
@ -17,26 +17,26 @@ class UserService:
|
||||
def __init__(self, db_service: DatabaseService) -> None:
|
||||
self._db_service = db_service
|
||||
|
||||
def get_all_users(self) -> list[User]:
|
||||
return self._db_service.get_all_users()
|
||||
async def get_all_users(self) -> list[User]:
|
||||
return await self._db_service.get_all_users()
|
||||
|
||||
def get_user(self, accessor: Optional[Union[str, int]]) -> Optional[User]:
|
||||
async def get_user(self, accessor: Optional[Union[str, int]]) -> Optional[User]:
|
||||
if accessor is None:
|
||||
return
|
||||
if isinstance(accessor, int):
|
||||
return self._db_service.get_user_by_id(accessor)
|
||||
return await self._db_service.get_user_by_id(accessor)
|
||||
accessor = accessor.lower()
|
||||
if "@" in accessor:
|
||||
return self._db_service.get_user_by_mail(accessor)
|
||||
return self._db_service.get_user_by_name(accessor)
|
||||
return await self._db_service.get_user_by_mail(accessor)
|
||||
return await self._db_service.get_user_by_name(accessor)
|
||||
|
||||
def set_profile_picture(self, user_id: int, picture: bytes) -> None:
|
||||
self._db_service.set_user_profile_picture(user_id, picture)
|
||||
async def set_profile_picture(self, user_id: int, picture: bytes) -> None:
|
||||
await self._db_service.set_user_profile_picture(user_id, picture)
|
||||
|
||||
def get_profile_picture(self, user_id: int) -> bytes:
|
||||
return self._db_service.get_user_profile_picture(user_id)
|
||||
async def get_profile_picture(self, user_id: int) -> bytes:
|
||||
return await self._db_service.get_user_profile_picture(user_id)
|
||||
|
||||
def create_user(self, user_name: str, user_mail: str, password_clear_text: str) -> User:
|
||||
async def create_user(self, user_name: str, user_mail: str, password_clear_text: str) -> User:
|
||||
disallowed_char = self._check_for_disallowed_char(user_name)
|
||||
if disallowed_char:
|
||||
raise NameNotAllowedError(disallowed_char)
|
||||
@ -44,17 +44,17 @@ class UserService:
|
||||
user_name = user_name.lower()
|
||||
|
||||
hashed_pw = sha256(password_clear_text.encode(encoding="utf-8")).hexdigest()
|
||||
return self._db_service.create_user(user_name, user_mail, hashed_pw)
|
||||
return await self._db_service.create_user(user_name, user_mail, hashed_pw)
|
||||
|
||||
def update_user(self, user: User) -> User:
|
||||
async def update_user(self, user: User) -> User:
|
||||
disallowed_char = self._check_for_disallowed_char(user.user_name)
|
||||
if disallowed_char:
|
||||
raise NameNotAllowedError(disallowed_char)
|
||||
user.user_name = user.user_name.lower()
|
||||
return self._db_service.update_user(user)
|
||||
return await self._db_service.update_user(user)
|
||||
|
||||
def is_login_valid(self, user_name_or_mail: str, password_clear_text: str) -> bool:
|
||||
user = self.get_user(user_name_or_mail)
|
||||
async def is_login_valid(self, user_name_or_mail: str, password_clear_text: str) -> bool:
|
||||
user = await self.get_user(user_name_or_mail)
|
||||
if not user:
|
||||
return False
|
||||
return user.user_password == sha256(password_clear_text.encode(encoding="utf-8")).hexdigest()
|
||||
|
||||
@ -17,3 +17,6 @@ class User:
|
||||
is_admin: bool
|
||||
created_at: datetime
|
||||
last_updated_at: datetime
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(f"{self.user_id}{self.user_name}{self.user_mail}")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user