Skip to content

Instantly share code, notes, and snippets.

@victorusachev
Created October 13, 2019 21:47
Show Gist options
  • Select an option

  • Save victorusachev/2b0d9a679fcb890c7364843f20148a26 to your computer and use it in GitHub Desktop.

Select an option

Save victorusachev/2b0d9a679fcb890c7364843f20148a26 to your computer and use it in GitHub Desktop.
import logging
import random
from dataclasses import dataclass, field
from decimal import Decimal
from typing import Callable, Iterable, List, Union
_logger = logging.getLogger(__name__)
MENU = [
{
'description': 'Томатный соус, моцарелла и орегано.',
'name': 'Пицца "Маргарита" (Margherita)'
},
{
'description': 'Томатный соус, чеснок и базилик.',
'name': 'Пицца "Маринара" (Marinara)'
},
{
'description': 'Томатный соус, моцарелла, грибы, ветчина, артишоки, оливки и орегано.',
'name': 'Пицца "Четыре сезона" (Quattro Stagioni)'
},
{
'description': 'Томатный соус, моцарелла, пармезан, яйца, бекон.',
'name': 'Пицца "Карбонара" (Carbonara)'
},
{
'description': 'Томатный соус и морепродукты.',
'name': 'Пицца с морепродуктами (Frutti di Mare)'
},
{
'description': 'Томатный соус, моцарелла, пармезан, сыр горгонзола, артишоки и орегано.',
'name': 'Пицца "Четыре сыра" (Quattro Formaggi)'
},
{
'description': 'Томатный соус, моцарелла и пармская ветчина.', 'name': 'Пицца "Крудо" (Crudo)'
},
{
'description': 'Томатный соус, моцарелла, орегано, анчоусы.',
'name': 'Пицца "Неаполетано" (Napoletana)'
},
{
'description': 'Томатный соус, моцарелла, орегано, лук.',
'name': 'Пицца по-апулийски (Pugliese)'
},
{
'description': 'Томатный соус, моцарелла, грибы, пепперони и Страккино (мягкий сыр).',
'name': 'Пицца "Монтанара" (Montanara)'
},
{
'description': 'Томатный соус, моцарелла, баклажаны, отварной картофель и колбаса.',
'name': 'Пицца "Эмилиана" (Emiliana)'
},
{
'description': 'Томатный соус, моцарелла, анчоусы, каперсы и орегано.',
'name': 'Римская пицца (Romana)'
},
{
'description': 'Томатный соус, моцарелла, перец, горошек, порчетта (итальянская свинина на вертеле).',
'name': 'Фермерская пицца (Fattoria)'
},
{
'description': 'Оливковое масло и розмарин.', 'name': 'Скьяччата (Schiacciata)'
},
{
'description': 'Томатный соус, моцарелла, ветчина, орегано.',
'name': 'Пицца с прошутто (Prosciutto)'
},
{
'description': 'Томатный соус, моцарелла, колбаса и картофель фри.',
'name': 'Пицца "Американо" (Americana)'
},
{
'description': 'Томатный соус, моцарелла, ветчина, грибы.',
'name': 'Пицца с прошутто и грибами (Prosciutto e Funghi)'
},
{
'description': 'Моцарелла, шпинат, сыр рикотта и пармезан.',
'name': 'Пицца силачей или пицца Папайя (Braccio di Ferro)'
},
{
'description': 'Томатный соус, моцарелла, сыр пекорино и острая салями.',
'name': 'Пицца "Сардиния" (Sarda)'
},
{
'description': 'Томатный соус, моцарелла, тунец, лук.',
'name': 'Пицца с тунцом (Tonno)'
},
]
class ApplicationError(Exception):
pass
class ApiError(ApplicationError):
pass
class DBError(ApplicationError):
pass
class MultipleObjectsReturned(DBError):
pass
class ObjectAlreadyExists(DBError):
pass
@dataclass
class User:
username: str
def __hash__(self):
return hash(self.username)
@dataclass
class Product:
name: str
quantity: Decimal
description: str = None
def __hash__(self):
return hash(self.name)
@dataclass
class CartItem:
product: Product
quantity: Decimal
def __hash__(self):
return hash(self.product)
@dataclass
class OrderItem:
product: Product
quantity: Decimal
def __hash__(self):
return hash(self.product)
@dataclass
class Order:
user: User
items: List[OrderItem]
def __hash__(self):
return hash(self.user)
@dataclass
class Cart:
user: User
items: List[CartItem] = field(default_factory=list)
def __hash__(self):
return hash(self.user)
@property
def empty(self):
return bool(self.items)
def append(self, product: Product, quantity: Decimal = Decimal('1')) -> None:
if quantity > 0:
self.items.append(CartItem(product, quantity))
else:
raise ApiError(f'quantity must be greater than 0')
def remove(self, item: CartItem) -> None:
self.items.remove(item)
def form_order(self) -> Order:
if not self.items:
raise ApiError('cart is empty')
items = [OrderItem(i.product, i.quantity) for i in self.items]
order = Order(self.user, items)
self.items.clear()
return order
@dataclass
class Shop:
carts: List[Cart] = field(default_factory=list)
orders: List[Order] = field(default_factory=list)
products: List[Product] = field(default_factory=list)
users: List[User] = field(default_factory=list)
@staticmethod
def _get_object(iterable: Iterable, func: Callable, unique=True):
objects = [o for o in set(iterable) if func(o)]
if objects:
if unique and len(objects) > 1:
raise MultipleObjectsReturned(str(objects))
return objects[0]
return None
def get_cart(self, user: User) -> Cart:
cart = self._get_object(
self.carts,
lambda b: b.user == user
)
if cart is None:
cart = Cart(user)
self.carts.append(cart)
return cart
def get_user(self, username) -> Union[User, None]:
user = self._get_object(
self.users,
lambda o: o.username == username
)
return user
def create_user(self, username) -> User:
user = self.get_user(username)
if user:
raise ObjectAlreadyExists(username)
user = User(username)
self.users.append(user)
return user
def get_menu(self) -> List[Product]:
return [p for p in self.products if p.quantity > 0]
def form_order(self, user: User):
cart = self.get_cart(user)
order = cart.form_order()
if cart.empty:
self.carts.remove(cart)
self.orders.append(order)
return order
# secondary logic
def chose_product(menu: Iterable[Product]) -> Product:
return random.choice(menu)
def chose_quantity(product: Product) -> Decimal:
# check available quantity
return Decimal(random.randint(0, product.quantity))
def main():
shop = Shop()
# prepare
shop.products = [
Product(**p, quantity=Decimal(random.randint(0, 10)))
for p in MENU
]
try:
user = shop.create_user('nobody')
except ObjectAlreadyExists as ex:
_logger.error(ex)
raise ex
menu = shop.get_menu()
cart = shop.get_cart(user)
for _ in range(0, random.randint(1, 5)):
product = chose_product(menu)
quantity = chose_quantity(product)
try:
cart.append(product, quantity)
except ApiError as ex:
_logger.warning(ex)
try:
order = shop.form_order(user)
print(order)
except ApiError as ex:
_logger.error(ex)
raise ex
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment