Coverage for opt/mealie/lib/python3.12/site-packages/mealie/routes/users/ratings.py: 41%
52 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-11-25 15:32 +0000
« prev ^ index » next coverage.py v7.10.6, created at 2025-11-25 15:32 +0000
1from functools import cached_property 1a
2from uuid import UUID 1a
4from fastapi import HTTPException, status 1a
5from pydantic import UUID4 1a
7from mealie.repos.all_repositories import get_repositories 1a
8from mealie.routes._base import BaseUserController, controller 1a
9from mealie.routes._base.routers import UserAPIRouter 1a
10from mealie.routes.users._helpers import assert_user_change_allowed 1a
11from mealie.schema.response.responses import ErrorResponse 1a
12from mealie.schema.user.user import UserRatingCreate, UserRatingOut, UserRatings, UserRatingUpdate 1a
14router = UserAPIRouter() 1a
17@controller(router) 1a
18class UserRatingsController(BaseUserController): 1a
19 @cached_property 1a
20 def group_recipes(self): 1a
21 return get_repositories(self.session, group_id=self.group_id, household_id=None).recipes
23 def get_recipe_or_404(self, slug_or_id: str | UUID): 1a
24 """Fetches a recipe by slug or id, or raises a 404 error if not found."""
25 if isinstance(slug_or_id, str):
26 try:
27 slug_or_id = UUID(slug_or_id)
28 except ValueError:
29 pass
31 if isinstance(slug_or_id, UUID):
32 recipe = self.group_recipes.get_one(slug_or_id, key="id")
33 else:
34 recipe = self.group_recipes.get_one(slug_or_id, key="slug")
36 if not recipe:
37 raise HTTPException(
38 status.HTTP_404_NOT_FOUND,
39 detail=ErrorResponse.respond(message="Not found."),
40 )
42 return recipe
44 @router.get("/{id}/ratings", response_model=UserRatings[UserRatingOut]) 1a
45 async def get_ratings(self, id: UUID4): 1a
46 """Get user's rated recipes"""
47 return UserRatings(ratings=self.repos.user_ratings.get_by_user(id))
49 @router.get("/{id}/favorites", response_model=UserRatings[UserRatingOut]) 1a
50 async def get_favorites(self, id: UUID4): 1a
51 """Get user's favorited recipes"""
52 return UserRatings(ratings=self.repos.user_ratings.get_by_user(id, favorites_only=True))
54 @router.post("/{id}/ratings/{slug}") 1a
55 def set_rating(self, id: UUID4, slug: str, data: UserRatingUpdate): 1a
56 """Sets the user's rating for a recipe"""
57 assert_user_change_allowed(id, self.user, self.user)
59 recipe = self.get_recipe_or_404(slug)
60 user_rating = self.repos.user_ratings.get_by_user_and_recipe(id, recipe.id)
61 if not user_rating:
62 self.repos.user_ratings.create(
63 UserRatingCreate(
64 user_id=id,
65 recipe_id=recipe.id,
66 rating=data.rating,
67 is_favorite=data.is_favorite or False,
68 )
69 )
70 else:
71 if data.rating is not None:
72 user_rating.rating = data.rating
73 if data.is_favorite is not None:
74 user_rating.is_favorite = data.is_favorite
76 self.repos.user_ratings.update(user_rating.id, user_rating)
78 @router.post("/{id}/favorites/{slug}") 1a
79 def add_favorite(self, id: UUID4, slug: str): 1a
80 """Adds a recipe to the user's favorites"""
81 self.set_rating(id, slug, data=UserRatingUpdate(is_favorite=True))
83 @router.delete("/{id}/favorites/{slug}") 1a
84 def remove_favorite(self, id: UUID4, slug: str): 1a
85 """Removes a recipe from the user's favorites"""
86 self.set_rating(id, slug, data=UserRatingUpdate(is_favorite=False))