-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcreate_menu.py
93 lines (68 loc) · 2.48 KB
/
create_menu.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
from dataclasses import dataclass
from typing import TypeAlias, Protocol
from itertools import combinations_with_replacement
from uuid import UUID, uuid4
from create_recipes import Recipe, RecipeRepository
from preferences import DietaryPreference, DietaryPreferenceDTO, create_preferences
MenuId: TypeAlias = UUID
@dataclass
class Menu:
id: MenuId
meals: list[Recipe]
def select_recipes_brute_force(
recipes: list[Recipe], size: int, preferences: DietaryPreference | None = None
) -> list[Recipe]:
"""Returns list of meals from preferences.
Tests all combinations (with replacement) and returns the one that best
fits preferences.
In case there's a draw, return first tested combination (which depends on
original ordering).
Args:
- recipes: List of possible recipes.
- preferences: Returned list of meals should satisfy this preferences.
- size: number of meals the menu should return.
"""
if size < 1 or len(recipes) == 0:
raise ValueError
return list(
min(
combinations_with_replacement(recipes, size),
key=preferences or (lambda recipe: 0),
)
)
class MenuRepository(Protocol):
def find(self, menu_id: MenuId) -> Menu | None: ...
def add(self, menu: Menu): ...
class DietaryPreferenceNotValid(Exception):
pass
class MenuNotFound(Exception):
pass
class CannotCreateMenu(Exception):
pass
class GetMenuUseCase:
def __init__(self, menu_repository: MenuRepository):
self.menu_repository = menu_repository
def __call__(self, menu_id: MenuId) -> Menu:
menu = self.menu_repository.find(menu_id)
if menu is None:
raise MenuNotFound()
return menu
class CreateMenuUseCase:
def __init__(
self, recipe_repository: RecipeRepository, menu_repository: MenuRepository
):
self.recipe_repository = recipe_repository
self.menu_repository = menu_repository
def __call__(self, size: int, preferences_spec: list[DietaryPreferenceDTO]) -> Menu:
recipes = self.recipe_repository.all()
try:
preferences = create_preferences(preferences_spec)
except (ValueError, TypeError):
raise DietaryPreferenceNotValid()
try:
meals = select_recipes_brute_force(recipes, size, preferences)
except ValueError:
raise CannotCreateMenu()
menu = Menu(id=uuid4(), meals=meals)
self.menu_repository.add(menu)
return menu