Coverage for opt/mealie/lib/python3.12/site-packages/mealie/repos/repository_household.py: 85%
74 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 Iterable
2from typing import cast
3from uuid import UUID
5from pydantic import UUID4
6from slugify import slugify
7from sqlalchemy import func, select
8from sqlalchemy.exc import IntegrityError
10from mealie.db.models._model_base import SqlAlchemyBase
11from mealie.db.models.household import Household, HouseholdToRecipe
12from mealie.db.models.recipe.category import Category
13from mealie.db.models.recipe.recipe import RecipeModel
14from mealie.db.models.recipe.tag import Tag
15from mealie.db.models.recipe.tool import Tool
16from mealie.db.models.users.users import User
17from mealie.repos.repository_generic import GroupRepositoryGeneric, HouseholdRepositoryGeneric
18from mealie.schema.household import (
19 HouseholdCreate,
20 HouseholdInDB,
21 HouseholdRecipeOut,
22 HouseholdStatistics,
23 UpdateHousehold,
24)
27class RepositoryHousehold(GroupRepositoryGeneric[HouseholdInDB, Household]):
28 def create(self, data: HouseholdCreate | dict) -> HouseholdInDB:
29 if isinstance(data, HouseholdCreate): 29 ↛ 32line 29 didn't jump to line 32 because the condition on line 29 was always true
30 data = data.model_dump()
32 if not data.get("group_id"):
33 data["group_id"] = self.group_id
34 max_attempts = 10
35 original_name = cast(str, data["name"])
37 attempts = 0
38 while True:
39 try:
40 data["slug"] = slugify(data["name"])
41 return super().create(data)
42 except IntegrityError:
43 self.session.rollback()
44 attempts += 1
45 if attempts >= max_attempts:
46 raise
48 data["name"] = f"{original_name} ({attempts})"
50 def create_many(self, data: Iterable[HouseholdInDB | dict]) -> list[HouseholdInDB]:
51 # since create uses special logic for resolving slugs, we don't want to use the standard create_many method
52 return [self.create(new_household) for new_household in data]
54 def update(self, match_value: str | int | UUID4, new_data: UpdateHousehold | dict) -> HouseholdInDB:
55 if isinstance(new_data, HouseholdCreate): 55 ↛ 58line 55 didn't jump to line 58 because the condition on line 55 was always true
56 new_data.slug = slugify(new_data.name)
57 else:
58 new_data["slug"] = slugify(new_data["name"])
60 return super().update(match_value, new_data)
62 def update_many(self, data: Iterable[UpdateHousehold | dict]) -> list[HouseholdInDB]:
63 # since update uses special logic for resolving slugs, we don't want to use the standard update_many method
64 return [
65 self.update(household["id"] if isinstance(household, dict) else household.id, household)
66 for household in data
67 ]
69 def get_by_name(self, name: str) -> HouseholdInDB | None:
70 if not self.group_id: 70 ↛ 71line 70 didn't jump to line 71 because the condition on line 70 was never true
71 raise Exception("group_id not set")
72 dbhousehold = (
73 self.session.execute(select(self.model).filter_by(name=name, group_id=self.group_id))
74 .scalars()
75 .one_or_none()
76 )
77 if dbhousehold is None: 77 ↛ 78line 77 didn't jump to line 78 because the condition on line 77 was never true
78 return None
79 return self.schema.model_validate(dbhousehold)
81 def get_by_slug_or_id(self, slug_or_id: str | UUID) -> HouseholdInDB | None:
82 if isinstance(slug_or_id, str): 82 ↛ 88line 82 didn't jump to line 88 because the condition on line 82 was always true
83 try:
84 slug_or_id = UUID(slug_or_id)
85 except ValueError:
86 pass
88 if isinstance(slug_or_id, UUID): 88 ↛ 89line 88 didn't jump to line 89 because the condition on line 88 was never true
89 return self.get_one(slug_or_id)
90 else:
91 return self.get_one(slug_or_id, key="slug")
93 def statistics(self, group_id: UUID4, household_id: UUID4) -> HouseholdStatistics:
94 def model_count(model: type[SqlAlchemyBase], *, filter_household: bool = True) -> int:
95 stmt = select(func.count(model.id)).filter_by(group_id=group_id)
96 if filter_household:
97 stmt = stmt.filter_by(household_id=household_id)
98 return self.session.scalar(stmt)
100 return HouseholdStatistics(
101 # household-level statistics
102 total_recipes=model_count(RecipeModel),
103 total_users=model_count(User),
104 # group-level statistics
105 total_categories=model_count(Category, filter_household=False),
106 total_tags=model_count(Tag, filter_household=False),
107 total_tools=model_count(Tool, filter_household=False),
108 )
111class RepositoryHouseholdRecipes(HouseholdRepositoryGeneric[HouseholdRecipeOut, HouseholdToRecipe]):
112 def get_by_recipe(self, recipe_id: UUID4) -> HouseholdRecipeOut | None:
113 if not self.household_id: 113 ↛ 114line 113 didn't jump to line 114 because the condition on line 113 was never true
114 raise Exception("household_id not set")
116 stmt = select(HouseholdToRecipe).filter(
117 HouseholdToRecipe.household_id == self.household_id, HouseholdToRecipe.recipe_id == recipe_id
118 )
119 result = self.session.execute(stmt).scalars().one_or_none()
120 return None if result is None else self.schema.model_validate(result)