Coverage for opt/mealie/lib/python3.12/site-packages/mealie/repos/repository_factory.py: 100%
184 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 03:04 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 03:04 +0000
1from collections.abc import Sequence
2from functools import cached_property
4from pydantic import UUID4
5from sqlalchemy import select
6from sqlalchemy.orm import Session
8from mealie.db.models.group import Group, ReportEntryModel, ReportModel
9from mealie.db.models.group.exports import GroupDataExportsModel
10from mealie.db.models.group.preferences import GroupPreferencesModel
11from mealie.db.models.household.cookbook import CookBook
12from mealie.db.models.household.events import GroupEventNotifierModel
13from mealie.db.models.household.household import Household
14from mealie.db.models.household.household_to_recipe import HouseholdToRecipe
15from mealie.db.models.household.invite_tokens import GroupInviteToken
16from mealie.db.models.household.mealplan import GroupMealPlan, GroupMealPlanRules
17from mealie.db.models.household.preferences import HouseholdPreferencesModel
18from mealie.db.models.household.recipe_action import GroupRecipeAction
19from mealie.db.models.household.shopping_list import (
20 ShoppingList,
21 ShoppingListItem,
22 ShoppingListItemRecipeReference,
23 ShoppingListMultiPurposeLabel,
24 ShoppingListRecipeReference,
25)
26from mealie.db.models.household.webhooks import GroupWebhooksModel
27from mealie.db.models.labels import MultiPurposeLabel
28from mealie.db.models.recipe.category import Category
29from mealie.db.models.recipe.comment import RecipeComment
30from mealie.db.models.recipe.ingredient import IngredientFoodModel, IngredientUnitModel
31from mealie.db.models.recipe.recipe import RecipeModel
32from mealie.db.models.recipe.recipe_timeline import RecipeTimelineEvent
33from mealie.db.models.recipe.shared import RecipeShareTokenModel
34from mealie.db.models.recipe.tag import Tag
35from mealie.db.models.recipe.tool import Tool
36from mealie.db.models.users import LongLiveToken, User
37from mealie.db.models.users.password_reset import PasswordResetModel
38from mealie.db.models.users.user_to_recipe import UserToRecipe
39from mealie.repos.repository_cookbooks import RepositoryCookbooks
40from mealie.repos.repository_foods import RepositoryFood
41from mealie.repos.repository_household import RepositoryHousehold, RepositoryHouseholdRecipes
42from mealie.repos.repository_meal_plan_rules import RepositoryMealPlanRules
43from mealie.repos.repository_units import RepositoryUnit
44from mealie.schema.cookbook.cookbook import ReadCookBook
45from mealie.schema.group.group_exports import GroupDataExport
46from mealie.schema.group.group_preferences import ReadGroupPreferences
47from mealie.schema.household.group_events import GroupEventNotifierOut
48from mealie.schema.household.group_recipe_action import GroupRecipeActionOut
49from mealie.schema.household.group_shopping_list import (
50 ShoppingListItemOut,
51 ShoppingListItemRecipeRefOut,
52 ShoppingListMultiPurposeLabelOut,
53 ShoppingListOut,
54 ShoppingListRecipeRefOut,
55)
56from mealie.schema.household.household import HouseholdInDB, HouseholdRecipeOut
57from mealie.schema.household.household_preferences import ReadHouseholdPreferences
58from mealie.schema.household.invite_token import ReadInviteToken
59from mealie.schema.household.webhook import ReadWebhook
60from mealie.schema.labels import MultiPurposeLabelOut
61from mealie.schema.meal_plan.new_meal import ReadPlanEntry
62from mealie.schema.meal_plan.plan_rules import PlanRulesOut
63from mealie.schema.recipe import Recipe, RecipeCommentOut, RecipeToolOut
64from mealie.schema.recipe.recipe_category import CategoryOut, TagOut
65from mealie.schema.recipe.recipe_ingredient import IngredientFood, IngredientUnit
66from mealie.schema.recipe.recipe_share_token import RecipeShareToken
67from mealie.schema.recipe.recipe_timeline_events import RecipeTimelineEventOut
68from mealie.schema.reports.reports import ReportEntryOut, ReportOut
69from mealie.schema.user import GroupInDB, LongLiveTokenInDB, PrivateUser
70from mealie.schema.user.user import UserRatingOut
71from mealie.schema.user.user_passwords import PrivatePasswordResetToken
73from ._utils import NOT_SET, NotSet
74from .repository_generic import GroupRepositoryGeneric, HouseholdRepositoryGeneric
75from .repository_group import RepositoryGroup
76from .repository_meals import RepositoryMeals
77from .repository_recipes import RepositoryRecipes
78from .repository_shopping_list import RepositoryShoppingList
79from .repository_users import RepositoryUserRatings, RepositoryUsers
81PK_ID = "id"
82PK_SLUG = "slug"
83PK_TOKEN = "token"
84PK_GROUP_ID = "group_id"
85PK_HOUSEHOLD_ID = "household_id"
88class RepositoryCategories(GroupRepositoryGeneric[CategoryOut, Category]):
89 def get_empty(self) -> Sequence[Category]:
90 stmt = select(Category).filter(~Category.recipes.any())
92 return self.session.execute(stmt).scalars().all()
95class RepositoryTags(GroupRepositoryGeneric[TagOut, Tag]):
96 def get_empty(self) -> Sequence[Tag]:
97 stmt = select(Tag).filter(~Tag.recipes.any())
98 return self.session.execute(stmt).scalars().all()
101class AllRepositories:
102 """
103 `AllRepositories` class is the data access layer for all database actions within
104 Mealie. Database uses composition from classes derived from AccessModel. These
105 can be substantiated from the AccessModel class or through inheritance when
106 additional methods are required.
107 """
109 def __init__(
110 self,
111 session: Session,
112 *,
113 group_id: UUID4 | None | NotSet = NOT_SET,
114 household_id: UUID4 | None | NotSet = NOT_SET,
115 ) -> None:
116 self.session = session
117 self.group_id = group_id
118 self.household_id = household_id
120 # ================================================================
121 # Recipe
123 @cached_property
124 def recipes(self) -> RepositoryRecipes:
125 return RepositoryRecipes(
126 self.session, PK_SLUG, RecipeModel, Recipe, group_id=self.group_id, household_id=self.household_id
127 )
129 @cached_property
130 def ingredient_foods(self) -> RepositoryFood:
131 return RepositoryFood(self.session, PK_ID, IngredientFoodModel, IngredientFood, group_id=self.group_id)
133 @cached_property
134 def ingredient_units(self) -> RepositoryUnit:
135 return RepositoryUnit(self.session, PK_ID, IngredientUnitModel, IngredientUnit, group_id=self.group_id)
137 @cached_property
138 def tools(self) -> GroupRepositoryGeneric[RecipeToolOut, Tool]:
139 return GroupRepositoryGeneric(self.session, PK_ID, Tool, RecipeToolOut, group_id=self.group_id)
141 @cached_property
142 def comments(self) -> GroupRepositoryGeneric[RecipeCommentOut, RecipeComment]:
143 # Since users can comment on recipes that belong to other households,
144 # this is a group repository, rather than a household repository.
145 return GroupRepositoryGeneric(self.session, PK_ID, RecipeComment, RecipeCommentOut, group_id=self.group_id)
147 @cached_property
148 def categories(self) -> RepositoryCategories:
149 return RepositoryCategories(self.session, PK_ID, Category, CategoryOut, group_id=self.group_id)
151 @cached_property
152 def tags(self) -> RepositoryTags:
153 return RepositoryTags(self.session, PK_ID, Tag, TagOut, group_id=self.group_id)
155 @cached_property
156 def recipe_share_tokens(self) -> GroupRepositoryGeneric[RecipeShareToken, RecipeShareTokenModel]:
157 return GroupRepositoryGeneric(
158 self.session, PK_ID, RecipeShareTokenModel, RecipeShareToken, group_id=self.group_id
159 )
161 @cached_property
162 def recipe_timeline_events(self) -> GroupRepositoryGeneric[RecipeTimelineEventOut, RecipeTimelineEvent]:
163 # Since users can post events on recipes that belong to other households,
164 # this is a group repository, rather than a household repository.
165 return GroupRepositoryGeneric(
166 self.session, PK_ID, RecipeTimelineEvent, RecipeTimelineEventOut, group_id=self.group_id
167 )
169 # ================================================================
170 # User
172 @cached_property
173 def users(self) -> RepositoryUsers:
174 return RepositoryUsers(self.session, PK_ID, User, PrivateUser, group_id=self.group_id)
176 @cached_property
177 def user_ratings(self) -> RepositoryUserRatings:
178 return RepositoryUserRatings(self.session, PK_ID, UserToRecipe, UserRatingOut, group_id=self.group_id)
180 @cached_property
181 def api_tokens(self) -> GroupRepositoryGeneric[LongLiveTokenInDB, LongLiveToken]:
182 return GroupRepositoryGeneric(self.session, PK_ID, LongLiveToken, LongLiveTokenInDB, group_id=self.group_id)
184 @cached_property
185 def tokens_pw_reset(self) -> GroupRepositoryGeneric[PrivatePasswordResetToken, PasswordResetModel]:
186 return GroupRepositoryGeneric(
187 self.session, PK_TOKEN, PasswordResetModel, PrivatePasswordResetToken, group_id=self.group_id
188 )
190 # ================================================================
191 # Group
193 @cached_property
194 def groups(self) -> RepositoryGroup:
195 return RepositoryGroup(self.session, PK_ID, Group, GroupInDB)
197 @cached_property
198 def group_preferences(self) -> GroupRepositoryGeneric[ReadGroupPreferences, GroupPreferencesModel]:
199 return GroupRepositoryGeneric(
200 self.session, PK_GROUP_ID, GroupPreferencesModel, ReadGroupPreferences, group_id=self.group_id
201 )
203 @cached_property
204 def group_exports(self) -> GroupRepositoryGeneric[GroupDataExport, GroupDataExportsModel]:
205 return GroupRepositoryGeneric(
206 self.session, PK_ID, GroupDataExportsModel, GroupDataExport, group_id=self.group_id
207 )
209 @cached_property
210 def group_reports(self) -> GroupRepositoryGeneric[ReportOut, ReportModel]:
211 return GroupRepositoryGeneric(self.session, PK_ID, ReportModel, ReportOut, group_id=self.group_id)
213 @cached_property
214 def group_report_entries(self) -> GroupRepositoryGeneric[ReportEntryOut, ReportEntryModel]:
215 return GroupRepositoryGeneric(self.session, PK_ID, ReportEntryModel, ReportEntryOut, group_id=self.group_id)
217 # ================================================================
218 # Household
220 @cached_property
221 def households(self) -> RepositoryHousehold:
222 return RepositoryHousehold(self.session, PK_ID, Household, HouseholdInDB, group_id=self.group_id)
224 @cached_property
225 def household_preferences(self) -> HouseholdRepositoryGeneric[ReadHouseholdPreferences, HouseholdPreferencesModel]:
226 return HouseholdRepositoryGeneric(
227 self.session,
228 PK_HOUSEHOLD_ID,
229 HouseholdPreferencesModel,
230 ReadHouseholdPreferences,
231 group_id=self.group_id,
232 household_id=self.household_id,
233 )
235 @cached_property
236 def household_recipes(self) -> RepositoryHouseholdRecipes:
237 return RepositoryHouseholdRecipes(
238 self.session,
239 PK_ID,
240 HouseholdToRecipe,
241 HouseholdRecipeOut,
242 group_id=self.group_id,
243 household_id=self.household_id,
244 )
246 @cached_property
247 def cookbooks(self) -> RepositoryCookbooks:
248 return RepositoryCookbooks(
249 self.session, PK_ID, CookBook, ReadCookBook, group_id=self.group_id, household_id=self.household_id
250 )
252 @cached_property
253 def group_invite_tokens(self) -> HouseholdRepositoryGeneric[ReadInviteToken, GroupInviteToken]:
254 return HouseholdRepositoryGeneric(
255 self.session,
256 PK_TOKEN,
257 GroupInviteToken,
258 ReadInviteToken,
259 group_id=self.group_id,
260 household_id=self.household_id,
261 )
263 @cached_property
264 def group_recipe_actions(self) -> HouseholdRepositoryGeneric[GroupRecipeActionOut, GroupRecipeAction]:
265 return HouseholdRepositoryGeneric(
266 self.session,
267 PK_ID,
268 GroupRecipeAction,
269 GroupRecipeActionOut,
270 group_id=self.group_id,
271 household_id=self.household_id,
272 )
274 # ================================================================
275 # Meal Plan
277 @cached_property
278 def meals(self) -> RepositoryMeals:
279 return RepositoryMeals(
280 self.session, PK_ID, GroupMealPlan, ReadPlanEntry, group_id=self.group_id, household_id=self.household_id
281 )
283 @cached_property
284 def group_meal_plan_rules(self) -> RepositoryMealPlanRules:
285 return RepositoryMealPlanRules(
286 self.session,
287 PK_ID,
288 GroupMealPlanRules,
289 PlanRulesOut,
290 group_id=self.group_id,
291 household_id=self.household_id,
292 )
294 # ================================================================
295 # Shopping List
297 @cached_property
298 def group_shopping_lists(self) -> RepositoryShoppingList:
299 return RepositoryShoppingList(
300 self.session, PK_ID, ShoppingList, ShoppingListOut, group_id=self.group_id, household_id=self.household_id
301 )
303 @cached_property
304 def group_shopping_list_item(self) -> HouseholdRepositoryGeneric[ShoppingListItemOut, ShoppingListItem]:
305 return HouseholdRepositoryGeneric(
306 self.session,
307 PK_ID,
308 ShoppingListItem,
309 ShoppingListItemOut,
310 group_id=self.group_id,
311 household_id=self.household_id,
312 )
314 @cached_property
315 def group_shopping_list_item_references(
316 self,
317 ) -> HouseholdRepositoryGeneric[ShoppingListItemRecipeRefOut, ShoppingListItemRecipeReference]:
318 return HouseholdRepositoryGeneric(
319 self.session,
320 PK_ID,
321 ShoppingListItemRecipeReference,
322 ShoppingListItemRecipeRefOut,
323 group_id=self.group_id,
324 household_id=self.household_id,
325 )
327 @cached_property
328 def group_shopping_list_recipe_refs(
329 self,
330 ) -> HouseholdRepositoryGeneric[ShoppingListRecipeRefOut, ShoppingListRecipeReference]:
331 return HouseholdRepositoryGeneric(
332 self.session,
333 PK_ID,
334 ShoppingListRecipeReference,
335 ShoppingListRecipeRefOut,
336 group_id=self.group_id,
337 household_id=self.household_id,
338 )
340 @cached_property
341 def shopping_list_multi_purpose_labels(
342 self,
343 ) -> HouseholdRepositoryGeneric[ShoppingListMultiPurposeLabelOut, ShoppingListMultiPurposeLabel]:
344 return HouseholdRepositoryGeneric(
345 self.session,
346 PK_ID,
347 ShoppingListMultiPurposeLabel,
348 ShoppingListMultiPurposeLabelOut,
349 group_id=self.group_id,
350 household_id=self.household_id,
351 )
353 @cached_property
354 def group_multi_purpose_labels(self) -> GroupRepositoryGeneric[MultiPurposeLabelOut, MultiPurposeLabel]:
355 return GroupRepositoryGeneric(
356 self.session, PK_ID, MultiPurposeLabel, MultiPurposeLabelOut, group_id=self.group_id
357 )
359 # ================================================================
360 # Events
362 @cached_property
363 def group_event_notifier(self) -> HouseholdRepositoryGeneric[GroupEventNotifierOut, GroupEventNotifierModel]:
364 return HouseholdRepositoryGeneric(
365 self.session,
366 PK_ID,
367 GroupEventNotifierModel,
368 GroupEventNotifierOut,
369 group_id=self.group_id,
370 household_id=self.household_id,
371 )
373 @cached_property
374 def webhooks(self) -> HouseholdRepositoryGeneric[ReadWebhook, GroupWebhooksModel]:
375 return HouseholdRepositoryGeneric(
376 self.session, PK_ID, GroupWebhooksModel, ReadWebhook, group_id=self.group_id, household_id=self.household_id
377 )