mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-04-25 14:25:34 -04:00
feat: Add Households to Mealie (#3970)
This commit is contained in:
@@ -7,6 +7,7 @@ from . import (
|
||||
comments,
|
||||
explore,
|
||||
groups,
|
||||
households,
|
||||
organizers,
|
||||
parser,
|
||||
recipe,
|
||||
@@ -21,6 +22,7 @@ router = APIRouter(prefix="/api")
|
||||
router.include_router(app.router)
|
||||
router.include_router(auth.router)
|
||||
router.include_router(users.router)
|
||||
router.include_router(households.router)
|
||||
router.include_router(groups.router)
|
||||
router.include_router(recipe.router)
|
||||
router.include_router(organizers.router)
|
||||
|
||||
@@ -6,7 +6,12 @@ from pydantic import UUID4, ConfigDict
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from mealie.core.config import get_app_dirs, get_app_settings
|
||||
from mealie.core.dependencies.dependencies import get_admin_user, get_current_user, get_integration_id, get_public_group
|
||||
from mealie.core.dependencies.dependencies import (
|
||||
get_admin_user,
|
||||
get_current_user,
|
||||
get_integration_id,
|
||||
get_public_group,
|
||||
)
|
||||
from mealie.core.exceptions import mealie_registered_exceptions
|
||||
from mealie.core.root_logger import get_logger
|
||||
from mealie.core.settings.directories import AppDirectories
|
||||
@@ -14,8 +19,10 @@ from mealie.core.settings.settings import AppSettings
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.lang import local_provider
|
||||
from mealie.lang.providers import Translator
|
||||
from mealie.repos.all_repositories import AllRepositories
|
||||
from mealie.repos._utils import NOT_SET, NotSet
|
||||
from mealie.repos.all_repositories import AllRepositories, get_repositories
|
||||
from mealie.routes._base.checks import OperationChecks
|
||||
from mealie.schema.household.household import HouseholdInDB
|
||||
from mealie.schema.user.user import GroupInDB, PrivateUser
|
||||
from mealie.services.event_bus_service.event_bus_service import EventBusService
|
||||
from mealie.services.event_bus_service.event_types import EventDocumentDataBase, EventTypes
|
||||
@@ -37,7 +44,7 @@ class _BaseController(ABC): # noqa: B024
|
||||
@property
|
||||
def repos(self):
|
||||
if not self._repos:
|
||||
self._repos = AllRepositories(self.session)
|
||||
self._repos = AllRepositories(self.session, group_id=self.group_id, household_id=self.household_id)
|
||||
return self._repos
|
||||
|
||||
@property
|
||||
@@ -58,6 +65,14 @@ class _BaseController(ABC): # noqa: B024
|
||||
self._folders = get_app_dirs()
|
||||
return self._folders
|
||||
|
||||
@property
|
||||
def group_id(self) -> UUID4 | None | NotSet:
|
||||
return NOT_SET
|
||||
|
||||
@property
|
||||
def household_id(self) -> UUID4 | None | NotSet:
|
||||
return NOT_SET
|
||||
|
||||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||
|
||||
|
||||
@@ -71,15 +86,40 @@ class BasePublicController(_BaseController):
|
||||
...
|
||||
|
||||
|
||||
class BasePublicExploreController(BasePublicController):
|
||||
class BasePublicGroupExploreController(BasePublicController):
|
||||
"""
|
||||
This is a public class for all User restricted controllers in the API.
|
||||
It includes the common SharedDependencies and some common methods used
|
||||
by all Admin controllers.
|
||||
Base class for all controllers that are public and explore group data.
|
||||
"""
|
||||
|
||||
group: GroupInDB = Depends(get_public_group)
|
||||
|
||||
@property
|
||||
def group_id(self) -> UUID4 | None | NotSet:
|
||||
return self.group.id
|
||||
|
||||
def get_explore_url_path(self, endpoint: str) -> str:
|
||||
if endpoint.startswith("/"):
|
||||
endpoint = endpoint[1:]
|
||||
return f"/explore/groups/{self.group.slug}/{endpoint}"
|
||||
|
||||
|
||||
class BasePublicHouseholdExploreController(BasePublicGroupExploreController):
|
||||
"""
|
||||
Base class for all controllers that are public and explore household data.
|
||||
"""
|
||||
|
||||
@property
|
||||
def cross_household_repos(self):
|
||||
"""
|
||||
Household-level repos with no household filter. Public controllers don't have access to a household identifier;
|
||||
instead, they return all public data, filtered by the household preferences.
|
||||
|
||||
When using this repo, the caller should filter by household preferences, e.g.:
|
||||
|
||||
`household.preferences.privateHousehold = FALSE`
|
||||
"""
|
||||
return get_repositories(self.session, group_id=self.group_id, household_id=None)
|
||||
|
||||
|
||||
class BaseUserController(_BaseController):
|
||||
"""
|
||||
@@ -105,10 +145,18 @@ class BaseUserController(_BaseController):
|
||||
def group_id(self) -> UUID4:
|
||||
return self.user.group_id
|
||||
|
||||
@property
|
||||
def household_id(self) -> UUID4:
|
||||
return self.user.household_id
|
||||
|
||||
@property
|
||||
def group(self) -> GroupInDB:
|
||||
return self.repos.groups.get_one(self.group_id)
|
||||
|
||||
@property
|
||||
def household(self) -> HouseholdInDB:
|
||||
return self.repos.households.get_one(self.household_id)
|
||||
|
||||
@property
|
||||
def checks(self) -> OperationChecks:
|
||||
if not self._checks:
|
||||
@@ -125,6 +173,13 @@ class BaseAdminController(BaseUserController):
|
||||
|
||||
user: PrivateUser = Depends(get_admin_user)
|
||||
|
||||
@property
|
||||
def repos(self):
|
||||
if not self._repos:
|
||||
# Admins have access to all groups and households, so we don't want to filter by group_id or household_id
|
||||
self._repos = AllRepositories(self.session, group_id=None, household_id=None)
|
||||
return self._repos
|
||||
|
||||
|
||||
class BaseCrudController(BaseUserController):
|
||||
"""
|
||||
@@ -133,10 +188,18 @@ class BaseCrudController(BaseUserController):
|
||||
|
||||
event_bus: EventBusService = Depends(EventBusService.as_dependency)
|
||||
|
||||
def publish_event(self, event_type: EventTypes, document_data: EventDocumentDataBase, message: str = "") -> None:
|
||||
def publish_event(
|
||||
self,
|
||||
event_type: EventTypes,
|
||||
document_data: EventDocumentDataBase,
|
||||
group_id: UUID4,
|
||||
household_id: UUID4 | None,
|
||||
message: str = "",
|
||||
) -> None:
|
||||
self.event_bus.dispatch(
|
||||
integration_id=self.integration_id,
|
||||
group_id=self.group_id,
|
||||
group_id=group_id,
|
||||
household_id=household_id,
|
||||
event_type=event_type,
|
||||
document_data=document_data,
|
||||
message=message,
|
||||
|
||||
@@ -35,7 +35,7 @@ class MealieCrudRoute(APIRoute):
|
||||
response = await original_route_handler(request)
|
||||
response_body = json.loads(response.body)
|
||||
if isinstance(response_body, dict):
|
||||
if last_modified := response_body.get("updateAt"):
|
||||
if last_modified := response_body.get("updatedAt"):
|
||||
response.headers["last-modified"] = last_modified
|
||||
|
||||
# Force no-cache for all responses to prevent browser from caching API calls
|
||||
|
||||
@@ -2,11 +2,11 @@ from mealie.routes._base.routers import AdminAPIRouter
|
||||
|
||||
from . import (
|
||||
admin_about,
|
||||
admin_analytics,
|
||||
admin_backups,
|
||||
admin_email,
|
||||
admin_maintenance,
|
||||
admin_management_groups,
|
||||
admin_management_households,
|
||||
admin_management_users,
|
||||
)
|
||||
|
||||
@@ -14,8 +14,8 @@ router = AdminAPIRouter(prefix="/admin")
|
||||
|
||||
router.include_router(admin_about.router, tags=["Admin: About"])
|
||||
router.include_router(admin_management_users.router, tags=["Admin: Manage Users"])
|
||||
router.include_router(admin_management_households.router, tags=["Admin: Manage Households"])
|
||||
router.include_router(admin_management_groups.router, tags=["Admin: Manage Groups"])
|
||||
router.include_router(admin_email.router, tags=["Admin: Email"])
|
||||
router.include_router(admin_backups.router, tags=["Admin: Backups"])
|
||||
router.include_router(admin_maintenance.router, tags=["Admin: Maintenance"])
|
||||
router.include_router(admin_analytics.router, tags=["Admin: Analytics"])
|
||||
|
||||
@@ -27,6 +27,7 @@ class AdminAboutController(BaseAdminController):
|
||||
db_type=settings.DB_ENGINE,
|
||||
db_url=settings.DB_URL_PUBLIC,
|
||||
default_group=settings.DEFAULT_GROUP,
|
||||
default_household=settings.DEFAULT_HOUSEHOLD,
|
||||
allow_signup=settings.ALLOW_SIGNUP,
|
||||
build_id=settings.GIT_COMMIT_HASH,
|
||||
recipe_scraper_version=recipe_scraper_version.__version__,
|
||||
@@ -44,6 +45,7 @@ class AdminAboutController(BaseAdminController):
|
||||
uncategorized_recipes=self.repos.recipes.count_uncategorized(), # type: ignore
|
||||
untagged_recipes=self.repos.recipes.count_untagged(), # type: ignore
|
||||
total_users=self.repos.users.count_all(),
|
||||
total_households=self.repos.households.count_all(),
|
||||
total_groups=self.repos.groups.count_all(),
|
||||
)
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
from functools import cached_property
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from mealie.routes._base import BaseAdminController, controller
|
||||
from mealie.schema.analytics.analytics import MealieAnalytics
|
||||
from mealie.services.analytics.service_analytics import AnalyticsService
|
||||
|
||||
router = APIRouter(prefix="/analytics", include_in_schema=False) # deprecated - use statistics route instead
|
||||
|
||||
|
||||
@controller(router)
|
||||
class AdminAboutController(BaseAdminController):
|
||||
@cached_property
|
||||
def service(self) -> AnalyticsService:
|
||||
return AnalyticsService(self.repos)
|
||||
|
||||
@router.get("", response_model=MealieAnalytics)
|
||||
def get_analytics(self):
|
||||
return self.service.calculate_analytics()
|
||||
@@ -13,11 +13,11 @@ from mealie.services.group_services.group_service import GroupService
|
||||
from .._base import BaseAdminController, controller
|
||||
from .._base.mixins import HttpRepo
|
||||
|
||||
router = APIRouter(prefix="/groups", tags=["Admin: Groups"])
|
||||
router = APIRouter(prefix="/groups")
|
||||
|
||||
|
||||
@controller(router)
|
||||
class AdminUserManagementRoutes(BaseAdminController):
|
||||
class AdminGroupManagementRoutes(BaseAdminController):
|
||||
@cached_property
|
||||
def repo(self):
|
||||
if not self.user:
|
||||
@@ -64,6 +64,7 @@ class AdminUserManagementRoutes(BaseAdminController):
|
||||
group.preferences = self.repos.group_preferences.update(item_id, preferences)
|
||||
|
||||
if data.name not in ["", group.name]:
|
||||
# only update the group if the name changed, since the name is the only field that can be updated
|
||||
group.name = data.name
|
||||
group = self.repo.update(item_id, group)
|
||||
|
||||
|
||||
91
mealie/routes/admin/admin_management_households.py
Normal file
91
mealie/routes/admin/admin_management_households.py
Normal file
@@ -0,0 +1,91 @@
|
||||
from functools import cached_property
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from pydantic import UUID4
|
||||
from sqlalchemy import func, select
|
||||
|
||||
from mealie.db.models.users.users import User
|
||||
from mealie.schema.household.household import (
|
||||
HouseholdCreate,
|
||||
HouseholdInDB,
|
||||
HouseholdPagination,
|
||||
UpdateHouseholdAdmin,
|
||||
)
|
||||
from mealie.schema.mapper import mapper
|
||||
from mealie.schema.response.pagination import PaginationQuery
|
||||
from mealie.schema.response.responses import ErrorResponse
|
||||
from mealie.services.household_services.household_service import HouseholdService
|
||||
|
||||
from .._base import BaseAdminController, controller
|
||||
from .._base.mixins import HttpRepo
|
||||
|
||||
router = APIRouter(prefix="/households")
|
||||
|
||||
|
||||
@controller(router)
|
||||
class AdminHouseholdManagementRoutes(BaseAdminController):
|
||||
@cached_property
|
||||
def repo(self):
|
||||
if not self.user:
|
||||
raise Exception("No user is logged in.")
|
||||
|
||||
return self.repos.households
|
||||
|
||||
# =======================================================================
|
||||
# CRUD Operations
|
||||
|
||||
@property
|
||||
def mixins(self):
|
||||
return HttpRepo[HouseholdCreate, HouseholdInDB, UpdateHouseholdAdmin](
|
||||
self.repo,
|
||||
self.logger,
|
||||
self.registered_exceptions,
|
||||
)
|
||||
|
||||
@router.get("", response_model=HouseholdPagination)
|
||||
def get_all(self, q: PaginationQuery = Depends(PaginationQuery)):
|
||||
response = self.repo.page_all(
|
||||
pagination=q,
|
||||
override=HouseholdInDB,
|
||||
)
|
||||
|
||||
response.set_pagination_guides(router.url_path_for("get_all"), q.model_dump())
|
||||
return response
|
||||
|
||||
@router.post("", response_model=HouseholdInDB, status_code=status.HTTP_201_CREATED)
|
||||
def create_one(self, data: HouseholdCreate):
|
||||
return HouseholdService.create_household(self.repos, data)
|
||||
|
||||
@router.get("/{item_id}", response_model=HouseholdInDB)
|
||||
def get_one(self, item_id: UUID4):
|
||||
return self.mixins.get_one(item_id)
|
||||
|
||||
@router.put("/{item_id}", response_model=HouseholdInDB)
|
||||
def update_one(self, item_id: UUID4, data: UpdateHouseholdAdmin):
|
||||
household = self.repo.get_one(item_id)
|
||||
|
||||
if data.preferences:
|
||||
preferences = self.repos.household_preferences.get_one(value=item_id, key="household_id")
|
||||
preferences = mapper(data.preferences, preferences)
|
||||
household.preferences = self.repos.household_preferences.update(item_id, preferences)
|
||||
|
||||
if data.name not in ["", household.name]:
|
||||
# only update the household if the name changed, since the name is the only field that can be updated
|
||||
household.name = data.name
|
||||
household = self.repo.update(item_id, household)
|
||||
|
||||
return household
|
||||
|
||||
@router.delete("/{item_id}", response_model=HouseholdInDB)
|
||||
def delete_one(self, item_id: UUID4):
|
||||
item = self.repo.get_one(item_id)
|
||||
if item:
|
||||
stmt = select(func.count(User.id)).filter_by(group_id=item.group_id, household_id=item_id)
|
||||
user_count = self.session.scalar(stmt)
|
||||
if user_count:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=ErrorResponse.respond(message="Cannot delete household with users"),
|
||||
)
|
||||
|
||||
return self.mixins.delete_one(item_id)
|
||||
@@ -14,7 +14,7 @@ from mealie.schema.user.user_passwords import ForgotPassword, PasswordResetToken
|
||||
from mealie.services.user_services.password_reset_service import PasswordResetService
|
||||
from mealie.services.user_services.user_service import UserService
|
||||
|
||||
router = APIRouter(prefix="/users", tags=["Admin: Users"])
|
||||
router = APIRouter(prefix="/users")
|
||||
|
||||
|
||||
@controller(router)
|
||||
|
||||
@@ -16,12 +16,20 @@ def get_app_info(session: Session = Depends(generate_session)):
|
||||
"""Get general application information"""
|
||||
settings = get_app_settings()
|
||||
|
||||
repos = get_repositories(session)
|
||||
default_group = repos.groups.get_by_name(settings.DEFAULT_GROUP)
|
||||
public_repos = get_repositories(session, group_id=None, household_id=None)
|
||||
|
||||
default_group_slug: str | None = None
|
||||
default_household_slug: str | None = None
|
||||
|
||||
default_group = public_repos.groups.get_by_name(settings.DEFAULT_GROUP)
|
||||
if default_group and default_group.preferences and not default_group.preferences.private_group:
|
||||
default_group_slug = default_group.slug
|
||||
else:
|
||||
default_group_slug = None
|
||||
|
||||
if default_group and default_group_slug:
|
||||
group_repos = get_repositories(session, group_id=default_group.id, household_id=None)
|
||||
default_household = group_repos.households.get_by_name(settings.DEFAULT_HOUSEHOLD)
|
||||
if default_household and default_household.preferences and not default_household.preferences.private_household:
|
||||
default_household_slug = default_household.slug
|
||||
|
||||
return AppInfo(
|
||||
version=APP_VERSION,
|
||||
@@ -29,6 +37,7 @@ def get_app_info(session: Session = Depends(generate_session)):
|
||||
production=settings.PRODUCTION,
|
||||
allow_signup=settings.ALLOW_SIGNUP,
|
||||
default_group_slug=default_group_slug,
|
||||
default_household_slug=default_household_slug,
|
||||
enable_oidc=settings.OIDC_READY,
|
||||
oidc_redirect=settings.OIDC_AUTO_REDIRECT,
|
||||
oidc_provider_name=settings.OIDC_PROVIDER_NAME,
|
||||
|
||||
@@ -7,13 +7,14 @@ from . import (
|
||||
controller_public_recipes,
|
||||
)
|
||||
|
||||
prefix = "/explore"
|
||||
router = APIRouter(prefix="/explore/groups/{group_slug}")
|
||||
|
||||
router = APIRouter()
|
||||
# group
|
||||
router.include_router(controller_public_foods.router, tags=["Explore: Foods"])
|
||||
router.include_router(controller_public_organizers.categories_router, tags=["Explore: Categories"])
|
||||
router.include_router(controller_public_organizers.tags_router, tags=["Explore: Tags"])
|
||||
router.include_router(controller_public_organizers.tools_router, tags=["Explore: Tools"])
|
||||
|
||||
router.include_router(controller_public_cookbooks.router, prefix=prefix, tags=["Explore: Cookbooks"])
|
||||
router.include_router(controller_public_foods.router, prefix=prefix, tags=["Explore: Foods"])
|
||||
router.include_router(controller_public_organizers.categories_router, prefix=prefix, tags=["Explore: Categories"])
|
||||
router.include_router(controller_public_organizers.tags_router, prefix=prefix, tags=["Explore: Tags"])
|
||||
router.include_router(controller_public_organizers.tools_router, prefix=prefix, tags=["Explore: Tools"])
|
||||
router.include_router(controller_public_recipes.router, prefix=prefix, tags=["Explore: Recipes"])
|
||||
# household
|
||||
router.include_router(controller_public_cookbooks.router, tags=["Explore: Cookbooks"])
|
||||
router.include_router(controller_public_recipes.router, tags=["Explore: Recipes"])
|
||||
|
||||
@@ -3,46 +3,46 @@ from uuid import UUID
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from pydantic import UUID4
|
||||
|
||||
from mealie.repos.all_repositories import get_repositories
|
||||
from mealie.routes._base import controller
|
||||
from mealie.routes._base.base_controllers import BasePublicExploreController
|
||||
from mealie.routes._base.base_controllers import BasePublicHouseholdExploreController
|
||||
from mealie.schema.cookbook.cookbook import ReadCookBook, RecipeCookBook
|
||||
from mealie.schema.make_dependable import make_dependable
|
||||
from mealie.schema.response.pagination import PaginationBase, PaginationQuery
|
||||
|
||||
router = APIRouter(prefix="/cookbooks/{group_slug}")
|
||||
router = APIRouter(prefix="/cookbooks")
|
||||
|
||||
|
||||
@controller(router)
|
||||
class PublicCookbooksController(BasePublicExploreController):
|
||||
class PublicCookbooksController(BasePublicHouseholdExploreController):
|
||||
@property
|
||||
def cookbooks(self):
|
||||
return self.repos.cookbooks.by_group(self.group.id)
|
||||
|
||||
@property
|
||||
def recipes(self):
|
||||
return self.repos.recipes.by_group(self.group.id)
|
||||
def cross_household_cookbooks(self):
|
||||
return self.cross_household_repos.cookbooks
|
||||
|
||||
@router.get("", response_model=PaginationBase[ReadCookBook])
|
||||
def get_all(
|
||||
self, q: PaginationQuery = Depends(make_dependable(PaginationQuery)), search: str | None = None
|
||||
self,
|
||||
q: PaginationQuery = Depends(make_dependable(PaginationQuery)),
|
||||
search: str | None = None,
|
||||
) -> PaginationBase[ReadCookBook]:
|
||||
public_filter = "public = TRUE"
|
||||
public_filter = "(household.preferences.privateHousehold = FALSE AND public = TRUE)"
|
||||
if q.query_filter:
|
||||
q.query_filter = f"({q.query_filter}) AND {public_filter}"
|
||||
else:
|
||||
q.query_filter = public_filter
|
||||
|
||||
response = self.cookbooks.page_all(
|
||||
response = self.cross_household_cookbooks.page_all(
|
||||
pagination=q,
|
||||
override=ReadCookBook,
|
||||
search=search,
|
||||
)
|
||||
|
||||
response.set_pagination_guides(router.url_path_for("get_all", group_slug=self.group.slug), q.model_dump())
|
||||
response.set_pagination_guides(self.get_explore_url_path(router.url_path_for("get_all")), q.model_dump())
|
||||
return response
|
||||
|
||||
@router.get("/{item_id}", response_model=RecipeCookBook)
|
||||
def get_one(self, item_id: UUID4 | str) -> RecipeCookBook:
|
||||
NOT_FOUND_EXCEPTION = HTTPException(404, "cookbook not found")
|
||||
if isinstance(item_id, UUID):
|
||||
match_attr = "id"
|
||||
else:
|
||||
@@ -51,12 +51,24 @@ class PublicCookbooksController(BasePublicExploreController):
|
||||
match_attr = "id"
|
||||
except ValueError:
|
||||
match_attr = "slug"
|
||||
cookbook = self.cookbooks.get_one(item_id, match_attr)
|
||||
cookbook = self.cross_household_cookbooks.get_one(item_id, match_attr)
|
||||
|
||||
if not cookbook or not cookbook.public:
|
||||
raise HTTPException(404, "cookbook not found")
|
||||
raise NOT_FOUND_EXCEPTION
|
||||
household = self.repos.households.get_one(cookbook.household_id)
|
||||
if not household or household.preferences.private_household:
|
||||
raise NOT_FOUND_EXCEPTION
|
||||
|
||||
recipes = self.recipes.page_all(
|
||||
PaginationQuery(page=1, per_page=-1, query_filter="settings.public = TRUE"), cookbook=cookbook
|
||||
# limit recipes to only the household the cookbook belongs to
|
||||
recipes_repo = get_repositories(
|
||||
self.session, group_id=self.group_id, household_id=cookbook.household_id
|
||||
).recipes
|
||||
recipes = recipes_repo.page_all(
|
||||
PaginationQuery(
|
||||
page=1,
|
||||
per_page=-1,
|
||||
query_filter="settings.public = TRUE",
|
||||
),
|
||||
cookbook=cookbook,
|
||||
)
|
||||
return cookbook.cast(RecipeCookBook, recipes=recipes.items)
|
||||
|
||||
@@ -2,23 +2,25 @@ from fastapi import APIRouter, Depends, HTTPException
|
||||
from pydantic import UUID4
|
||||
|
||||
from mealie.routes._base import controller
|
||||
from mealie.routes._base.base_controllers import BasePublicExploreController
|
||||
from mealie.routes._base.base_controllers import BasePublicGroupExploreController
|
||||
from mealie.schema.make_dependable import make_dependable
|
||||
from mealie.schema.recipe.recipe_ingredient import IngredientFood
|
||||
from mealie.schema.response.pagination import PaginationBase, PaginationQuery
|
||||
|
||||
router = APIRouter(prefix="/foods/{group_slug}")
|
||||
router = APIRouter(prefix="/foods")
|
||||
|
||||
|
||||
@controller(router)
|
||||
class PublicFoodsController(BasePublicExploreController):
|
||||
class PublicFoodsController(BasePublicGroupExploreController):
|
||||
@property
|
||||
def ingredient_foods(self):
|
||||
return self.repos.ingredient_foods.by_group(self.group.id)
|
||||
return self.repos.ingredient_foods
|
||||
|
||||
@router.get("", response_model=PaginationBase[IngredientFood])
|
||||
def get_all(
|
||||
self, q: PaginationQuery = Depends(make_dependable(PaginationQuery)), search: str | None = None
|
||||
self,
|
||||
q: PaginationQuery = Depends(make_dependable(PaginationQuery)),
|
||||
search: str | None = None,
|
||||
) -> PaginationBase[IngredientFood]:
|
||||
response = self.ingredient_foods.page_all(
|
||||
pagination=q,
|
||||
@@ -26,7 +28,7 @@ class PublicFoodsController(BasePublicExploreController):
|
||||
search=search,
|
||||
)
|
||||
|
||||
response.set_pagination_guides(router.url_path_for("get_all", group_slug=self.group.slug), q.model_dump())
|
||||
response.set_pagination_guides(self.get_explore_url_path(router.url_path_for("get_all")), q.model_dump())
|
||||
return response
|
||||
|
||||
@router.get("/{item_id}", response_model=IngredientFood)
|
||||
|
||||
@@ -2,28 +2,30 @@ from fastapi import APIRouter, Depends, HTTPException
|
||||
from pydantic import UUID4
|
||||
|
||||
from mealie.routes._base import controller
|
||||
from mealie.routes._base.base_controllers import BasePublicExploreController
|
||||
from mealie.routes._base.base_controllers import BasePublicGroupExploreController
|
||||
from mealie.schema.make_dependable import make_dependable
|
||||
from mealie.schema.recipe.recipe import RecipeCategory, RecipeTag, RecipeTool
|
||||
from mealie.schema.recipe.recipe_category import CategoryOut, TagOut
|
||||
from mealie.schema.recipe.recipe_tool import RecipeToolOut
|
||||
from mealie.schema.response.pagination import PaginationBase, PaginationQuery
|
||||
|
||||
base_prefix = "/organizers/{group_slug}"
|
||||
base_prefix = "/organizers"
|
||||
categories_router = APIRouter(prefix=f"{base_prefix}/categories")
|
||||
tags_router = APIRouter(prefix=f"{base_prefix}/tags")
|
||||
tools_router = APIRouter(prefix=f"{base_prefix}/tools")
|
||||
|
||||
|
||||
@controller(categories_router)
|
||||
class PublicCategoriesController(BasePublicExploreController):
|
||||
class PublicCategoriesController(BasePublicGroupExploreController):
|
||||
@property
|
||||
def categories(self):
|
||||
return self.repos.categories.by_group(self.group.id)
|
||||
return self.repos.categories
|
||||
|
||||
@categories_router.get("", response_model=PaginationBase[RecipeCategory])
|
||||
def get_all(
|
||||
self, q: PaginationQuery = Depends(make_dependable(PaginationQuery)), search: str | None = None
|
||||
self,
|
||||
q: PaginationQuery = Depends(make_dependable(PaginationQuery)),
|
||||
search: str | None = None,
|
||||
) -> PaginationBase[RecipeCategory]:
|
||||
response = self.categories.page_all(
|
||||
pagination=q,
|
||||
@@ -31,9 +33,7 @@ class PublicCategoriesController(BasePublicExploreController):
|
||||
search=search,
|
||||
)
|
||||
|
||||
response.set_pagination_guides(
|
||||
categories_router.url_path_for("get_all", group_slug=self.group.slug), q.model_dump()
|
||||
)
|
||||
response.set_pagination_guides(self.get_explore_url_path(tags_router.url_path_for("get_all")), q.model_dump())
|
||||
return response
|
||||
|
||||
@categories_router.get("/{item_id}", response_model=CategoryOut)
|
||||
@@ -46,14 +46,16 @@ class PublicCategoriesController(BasePublicExploreController):
|
||||
|
||||
|
||||
@controller(tags_router)
|
||||
class PublicTagsController(BasePublicExploreController):
|
||||
class PublicTagsController(BasePublicGroupExploreController):
|
||||
@property
|
||||
def tags(self):
|
||||
return self.repos.tags.by_group(self.group.id)
|
||||
return self.repos.tags
|
||||
|
||||
@tags_router.get("", response_model=PaginationBase[RecipeTag])
|
||||
def get_all(
|
||||
self, q: PaginationQuery = Depends(make_dependable(PaginationQuery)), search: str | None = None
|
||||
self,
|
||||
q: PaginationQuery = Depends(make_dependable(PaginationQuery)),
|
||||
search: str | None = None,
|
||||
) -> PaginationBase[RecipeTag]:
|
||||
response = self.tags.page_all(
|
||||
pagination=q,
|
||||
@@ -61,7 +63,7 @@ class PublicTagsController(BasePublicExploreController):
|
||||
search=search,
|
||||
)
|
||||
|
||||
response.set_pagination_guides(tags_router.url_path_for("get_all", group_slug=self.group.slug), q.model_dump())
|
||||
response.set_pagination_guides(self.get_explore_url_path(tags_router.url_path_for("get_all")), q.model_dump())
|
||||
return response
|
||||
|
||||
@tags_router.get("/{item_id}", response_model=TagOut)
|
||||
@@ -74,14 +76,16 @@ class PublicTagsController(BasePublicExploreController):
|
||||
|
||||
|
||||
@controller(tools_router)
|
||||
class PublicToolsController(BasePublicExploreController):
|
||||
class PublicToolsController(BasePublicGroupExploreController):
|
||||
@property
|
||||
def tools(self):
|
||||
return self.repos.tools.by_group(self.group.id)
|
||||
return self.repos.tools
|
||||
|
||||
@tools_router.get("", response_model=PaginationBase[RecipeTool])
|
||||
def get_all(
|
||||
self, q: PaginationQuery = Depends(make_dependable(PaginationQuery)), search: str | None = None
|
||||
self,
|
||||
q: PaginationQuery = Depends(make_dependable(PaginationQuery)),
|
||||
search: str | None = None,
|
||||
) -> PaginationBase[RecipeTool]:
|
||||
response = self.tools.page_all(
|
||||
pagination=q,
|
||||
@@ -89,7 +93,7 @@ class PublicToolsController(BasePublicExploreController):
|
||||
search=search,
|
||||
)
|
||||
|
||||
response.set_pagination_guides(tools_router.url_path_for("get_all", group_slug=self.group.slug), q.model_dump())
|
||||
response.set_pagination_guides(self.get_explore_url_path(tools_router.url_path_for("get_all")), q.model_dump())
|
||||
return response
|
||||
|
||||
@tools_router.get("/{item_id}", response_model=RecipeToolOut)
|
||||
|
||||
@@ -4,8 +4,9 @@ import orjson
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Request
|
||||
from pydantic import UUID4
|
||||
|
||||
from mealie.repos.all_repositories import get_repositories
|
||||
from mealie.routes._base import controller
|
||||
from mealie.routes._base.base_controllers import BasePublicExploreController
|
||||
from mealie.routes._base.base_controllers import BasePublicHouseholdExploreController
|
||||
from mealie.routes.recipe.recipe_crud_routes import JSONBytes
|
||||
from mealie.schema.cookbook.cookbook import ReadCookBook
|
||||
from mealie.schema.make_dependable import make_dependable
|
||||
@@ -13,18 +14,18 @@ from mealie.schema.recipe import Recipe
|
||||
from mealie.schema.recipe.recipe import RecipeSummary
|
||||
from mealie.schema.response.pagination import PaginationBase, PaginationQuery, RecipeSearchQuery
|
||||
|
||||
router = APIRouter(prefix="/recipes/{group_slug}")
|
||||
router = APIRouter(prefix="/recipes")
|
||||
|
||||
|
||||
@controller(router)
|
||||
class PublicRecipesController(BasePublicExploreController):
|
||||
class PublicRecipesController(BasePublicHouseholdExploreController):
|
||||
@property
|
||||
def cookbooks(self):
|
||||
return self.repos.cookbooks.by_group(self.group.id)
|
||||
def cross_household_cookbooks(self):
|
||||
return self.cross_household_repos.cookbooks
|
||||
|
||||
@property
|
||||
def recipes(self):
|
||||
return self.repos.recipes.by_group(self.group.id)
|
||||
def cross_household_recipes(self):
|
||||
return self.cross_household_repos.recipes
|
||||
|
||||
@router.get("", response_model=PaginationBase[RecipeSummary])
|
||||
def get_all(
|
||||
@@ -38,7 +39,9 @@ class PublicRecipesController(BasePublicExploreController):
|
||||
foods: list[UUID4 | str] | None = Query(None),
|
||||
) -> PaginationBase[RecipeSummary]:
|
||||
cookbook_data: ReadCookBook | None = None
|
||||
recipes_repo = self.cross_household_recipes
|
||||
if search_query.cookbook:
|
||||
COOKBOOK_NOT_FOUND_EXCEPTION = HTTPException(404, "cookbook not found")
|
||||
if isinstance(search_query.cookbook, UUID):
|
||||
cb_match_attr = "id"
|
||||
else:
|
||||
@@ -47,18 +50,26 @@ class PublicRecipesController(BasePublicExploreController):
|
||||
cb_match_attr = "id"
|
||||
except ValueError:
|
||||
cb_match_attr = "slug"
|
||||
cookbook_data = self.cookbooks.get_one(search_query.cookbook, cb_match_attr)
|
||||
cookbook_data = self.cross_household_cookbooks.get_one(search_query.cookbook, cb_match_attr)
|
||||
|
||||
if cookbook_data is None or not cookbook_data.public:
|
||||
raise HTTPException(status_code=404, detail="cookbook not found")
|
||||
raise COOKBOOK_NOT_FOUND_EXCEPTION
|
||||
household = self.repos.households.get_one(cookbook_data.household_id)
|
||||
if not household or household.preferences.private_household:
|
||||
raise COOKBOOK_NOT_FOUND_EXCEPTION
|
||||
|
||||
public_filter = "settings.public = TRUE"
|
||||
# filter recipes by the cookbook's household
|
||||
recipes_repo = get_repositories(
|
||||
self.session, group_id=self.group_id, household_id=cookbook_data.household_id
|
||||
).recipes
|
||||
|
||||
public_filter = "(household.preferences.privateHousehold = FALSE AND settings.public = TRUE)"
|
||||
if q.query_filter:
|
||||
q.query_filter = f"({q.query_filter}) AND {public_filter}"
|
||||
else:
|
||||
q.query_filter = public_filter
|
||||
|
||||
pagination_response = self.recipes.page_all(
|
||||
pagination_response = recipes_repo.page_all(
|
||||
pagination=q,
|
||||
cookbook=cookbook_data,
|
||||
categories=categories,
|
||||
@@ -75,7 +86,7 @@ class PublicRecipesController(BasePublicExploreController):
|
||||
# merge default pagination with the request's query params
|
||||
query_params = q.model_dump() | {**request.query_params}
|
||||
pagination_response.set_pagination_guides(
|
||||
router.url_path_for("get_all", group_slug=self.group.slug),
|
||||
self.get_explore_url_path(router.url_path_for("get_all")),
|
||||
{k: v for k, v in query_params.items() if v is not None},
|
||||
)
|
||||
|
||||
@@ -86,9 +97,13 @@ class PublicRecipesController(BasePublicExploreController):
|
||||
|
||||
@router.get("/{recipe_slug}", response_model=Recipe)
|
||||
def get_recipe(self, recipe_slug: str) -> Recipe:
|
||||
recipe = self.repos.recipes.by_group(self.group.id).get_one(recipe_slug)
|
||||
RECIPE_NOT_FOUND_EXCEPTION = HTTPException(404, "recipe not found")
|
||||
recipe = self.cross_household_recipes.get_one(recipe_slug)
|
||||
|
||||
if not recipe or not recipe.settings.public:
|
||||
raise HTTPException(404, "recipe not found")
|
||||
raise RECIPE_NOT_FOUND_EXCEPTION
|
||||
household = self.repos.households.get_one(recipe.household_id)
|
||||
if not household or household.preferences.private_household:
|
||||
raise RECIPE_NOT_FOUND_EXCEPTION
|
||||
|
||||
return recipe
|
||||
|
||||
@@ -1,36 +1,17 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from . import (
|
||||
controller_cookbooks,
|
||||
controller_group_notifications,
|
||||
controller_group_recipe_actions,
|
||||
controller_group_reports,
|
||||
controller_group_self_service,
|
||||
controller_invitations,
|
||||
controller_labels,
|
||||
controller_mealplan,
|
||||
controller_mealplan_config,
|
||||
controller_mealplan_rules,
|
||||
controller_migrations,
|
||||
controller_seeder,
|
||||
controller_shopping_lists,
|
||||
controller_webhooks,
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
router.include_router(controller_group_self_service.router)
|
||||
router.include_router(controller_mealplan_rules.router)
|
||||
router.include_router(controller_mealplan_config.router)
|
||||
router.include_router(controller_mealplan.router)
|
||||
router.include_router(controller_cookbooks.router)
|
||||
router.include_router(controller_webhooks.router)
|
||||
router.include_router(controller_invitations.router)
|
||||
router.include_router(controller_migrations.router)
|
||||
router.include_router(controller_group_reports.router)
|
||||
router.include_router(controller_shopping_lists.router)
|
||||
router.include_router(controller_shopping_lists.item_router)
|
||||
router.include_router(controller_labels.router)
|
||||
router.include_router(controller_group_notifications.router)
|
||||
router.include_router(controller_group_recipe_actions.router)
|
||||
router.include_router(controller_seeder.router)
|
||||
|
||||
@@ -17,7 +17,7 @@ router = APIRouter(prefix="/groups/reports", tags=["Groups: Reports"])
|
||||
class GroupReportsController(BaseUserController):
|
||||
@cached_property
|
||||
def repo(self):
|
||||
return self.repos.group_reports.by_group(self.user.group_id)
|
||||
return self.repos.group_reports
|
||||
|
||||
def registered_exceptions(self, ex: type[Exception]) -> str:
|
||||
return {
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
from functools import cached_property
|
||||
|
||||
from fastapi import HTTPException, status
|
||||
from fastapi import Query
|
||||
from pydantic import UUID4
|
||||
|
||||
from mealie.routes._base.base_controllers import BaseUserController
|
||||
from mealie.routes._base.controller import controller
|
||||
from mealie.routes._base.routers import UserAPIRouter
|
||||
from mealie.schema.group.group_permissions import SetPermissions
|
||||
from mealie.schema.group.group_preferences import ReadGroupPreferences, UpdateGroupPreferences
|
||||
from mealie.schema.group.group_statistics import GroupStatistics, GroupStorage
|
||||
from mealie.schema.user.user import GroupSummary, UserOut
|
||||
from mealie.schema.group.group_statistics import GroupStorage
|
||||
from mealie.schema.household.household import HouseholdSummary
|
||||
from mealie.schema.response.pagination import PaginationQuery
|
||||
from mealie.schema.user.user import GroupSummary, UserSummary
|
||||
from mealie.services.group_services.group_service import GroupService
|
||||
|
||||
router = UserAPIRouter(prefix="/groups", tags=["Groups: Self Service"])
|
||||
@@ -25,10 +27,20 @@ class GroupSelfServiceController(BaseUserController):
|
||||
"""Returns the Group Data for the Current User"""
|
||||
return self.group.cast(GroupSummary)
|
||||
|
||||
@router.get("/members", response_model=list[UserOut])
|
||||
def get_group_members(self):
|
||||
"""Returns the Group of user lists"""
|
||||
return self.repos.users.multi_query(query_by={"group_id": self.group.id}, override_schema=UserOut)
|
||||
@router.get("/members", response_model=list[UserSummary])
|
||||
def get_group_members(self, household_id: UUID4 | None = Query(None, alias="householdId")):
|
||||
"""Returns all users belonging to the current group, optionally filtered by household_id"""
|
||||
|
||||
query_filter = f"household_id={household_id}" if household_id else None
|
||||
private_users = self.repos.users.page_all(PaginationQuery(page=1, per_page=-1, query_filter=query_filter)).items
|
||||
return [user.cast(UserSummary) for user in private_users]
|
||||
|
||||
@router.get("/households", response_model=list[HouseholdSummary])
|
||||
def get_group_households(self):
|
||||
"""Returns all households belonging to the current group"""
|
||||
|
||||
households = self.repos.households.page_all(PaginationQuery(page=1, per_page=-1)).items
|
||||
return [household.cast(HouseholdSummary) for household in households]
|
||||
|
||||
@router.get("/preferences", response_model=ReadGroupPreferences)
|
||||
def get_group_preferences(self):
|
||||
@@ -38,28 +50,6 @@ class GroupSelfServiceController(BaseUserController):
|
||||
def update_group_preferences(self, new_pref: UpdateGroupPreferences):
|
||||
return self.repos.group_preferences.update(self.group_id, new_pref)
|
||||
|
||||
@router.put("/permissions", response_model=UserOut)
|
||||
def set_member_permissions(self, permissions: SetPermissions):
|
||||
self.checks.can_manage()
|
||||
|
||||
target_user = self.repos.users.get_one(permissions.user_id)
|
||||
|
||||
if not target_user:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, detail="User not found")
|
||||
|
||||
if target_user.group_id != self.group_id:
|
||||
raise HTTPException(status.HTTP_403_FORBIDDEN, detail="User is not a member of this group")
|
||||
|
||||
target_user.can_invite = permissions.can_invite
|
||||
target_user.can_manage = permissions.can_manage
|
||||
target_user.can_organize = permissions.can_organize
|
||||
|
||||
return self.repos.users.update(permissions.user_id, target_user)
|
||||
|
||||
@router.get("/statistics", response_model=GroupStatistics)
|
||||
def get_statistics(self):
|
||||
return self.service.calculate_statistics()
|
||||
|
||||
@router.get("/storage", response_model=GroupStorage)
|
||||
def get_storage(self):
|
||||
return self.service.calculate_group_storage()
|
||||
|
||||
@@ -17,14 +17,14 @@ from mealie.schema.labels.multi_purpose_label import MultiPurposeLabelPagination
|
||||
from mealie.schema.response.pagination import PaginationQuery
|
||||
from mealie.services.group_services.labels_service import MultiPurposeLabelService
|
||||
|
||||
router = APIRouter(prefix="/groups/labels", tags=["Group: Multi Purpose Labels"], route_class=MealieCrudRoute)
|
||||
router = APIRouter(prefix="/groups/labels", tags=["Groups: Multi Purpose Labels"], route_class=MealieCrudRoute)
|
||||
|
||||
|
||||
@controller(router)
|
||||
class MultiPurposeLabelsController(BaseUserController):
|
||||
@cached_property
|
||||
def service(self):
|
||||
return MultiPurposeLabelService(self.repos, self.group.id)
|
||||
return MultiPurposeLabelService(self.repos)
|
||||
|
||||
@cached_property
|
||||
def repo(self):
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
from mealie.routes._base.base_controllers import BaseUserController
|
||||
from mealie.routes._base.controller import controller
|
||||
from mealie.routes._base.mixins import HttpRepo
|
||||
from mealie.routes._base.routers import UserAPIRouter
|
||||
from mealie.schema.recipe.recipe_category import CategoryBase
|
||||
from mealie.schema.user.user import GroupInDB
|
||||
|
||||
router = UserAPIRouter(prefix="/groups/categories", tags=["Groups: Mealplan Categories"])
|
||||
|
||||
|
||||
@controller(router)
|
||||
class GroupMealplanConfigController(BaseUserController):
|
||||
@property
|
||||
def mixins(self):
|
||||
return HttpRepo[GroupInDB, GroupInDB, GroupInDB](self.repos.groups, self.logger)
|
||||
|
||||
@router.get("", response_model=list[CategoryBase])
|
||||
def get_mealplan_categories(self):
|
||||
data = self.mixins.get_one(self.user.group_id)
|
||||
return data.categories
|
||||
|
||||
@router.put("", response_model=list[CategoryBase])
|
||||
def update_mealplan_categories(self, new_categories: list[CategoryBase]):
|
||||
data = self.mixins.get_one(self.user.group_id)
|
||||
data.categories = new_categories
|
||||
return self.mixins.update_one(data, data.id).categories
|
||||
@@ -21,7 +21,7 @@ from mealie.services.migrations import (
|
||||
TandoorMigrator,
|
||||
)
|
||||
|
||||
router = UserAPIRouter(prefix="/groups/migrations", tags=["Group: Migrations"])
|
||||
router = UserAPIRouter(prefix="/groups/migrations", tags=["Groups: Migrations"])
|
||||
|
||||
|
||||
@controller(router)
|
||||
@@ -43,6 +43,7 @@ class GroupMigrationController(BaseUserController):
|
||||
"db": self.repos,
|
||||
"session": self.session,
|
||||
"user_id": self.user.id,
|
||||
"household_id": self.household_id,
|
||||
"group_id": self.group_id,
|
||||
"add_migration_tag": add_migration_tag,
|
||||
"translator": self.translator,
|
||||
|
||||
@@ -15,7 +15,7 @@ router = APIRouter(prefix="/groups/seeders", tags=["Groups: Seeders"])
|
||||
class DataSeederController(BaseUserController):
|
||||
@cached_property
|
||||
def service(self) -> SeederService:
|
||||
return SeederService(self.repos, self.user, self.group)
|
||||
return SeederService(self.repos)
|
||||
|
||||
def _wrap(self, func):
|
||||
try:
|
||||
|
||||
28
mealie/routes/households/__init__.py
Normal file
28
mealie/routes/households/__init__.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from . import (
|
||||
controller_cookbooks,
|
||||
controller_group_notifications,
|
||||
controller_group_recipe_actions,
|
||||
controller_household_self_service,
|
||||
controller_invitations,
|
||||
controller_mealplan,
|
||||
controller_mealplan_rules,
|
||||
controller_shopping_lists,
|
||||
controller_webhooks,
|
||||
)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
router.include_router(controller_cookbooks.router)
|
||||
router.include_router(controller_group_notifications.router)
|
||||
router.include_router(controller_group_recipe_actions.router)
|
||||
router.include_router(controller_household_self_service.router)
|
||||
router.include_router(controller_invitations.router)
|
||||
router.include_router(controller_shopping_lists.router)
|
||||
router.include_router(controller_shopping_lists.item_router)
|
||||
router.include_router(controller_webhooks.router)
|
||||
|
||||
# mealplan_rules must be added before mealplan due to the way the routes are defined
|
||||
router.include_router(controller_mealplan_rules.router)
|
||||
router.include_router(controller_mealplan.router)
|
||||
@@ -1,3 +1,4 @@
|
||||
from collections import defaultdict
|
||||
from functools import cached_property
|
||||
from uuid import UUID
|
||||
|
||||
@@ -19,14 +20,14 @@ from mealie.services.event_bus_service.event_types import (
|
||||
EventTypes,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/groups/cookbooks", tags=["Groups: Cookbooks"], route_class=MealieCrudRoute)
|
||||
router = APIRouter(prefix="/households/cookbooks", tags=["Households: Cookbooks"], route_class=MealieCrudRoute)
|
||||
|
||||
|
||||
@controller(router)
|
||||
class GroupCookbookController(BaseCrudController):
|
||||
@cached_property
|
||||
def repo(self):
|
||||
return self.repos.cookbooks.by_group(self.group_id)
|
||||
return self.repos.cookbooks
|
||||
|
||||
def registered_exceptions(self, ex: type[Exception]) -> str:
|
||||
registered = {
|
||||
@@ -54,13 +55,15 @@ class GroupCookbookController(BaseCrudController):
|
||||
|
||||
@router.post("", response_model=ReadCookBook, status_code=201)
|
||||
def create_one(self, data: CreateCookBook):
|
||||
data = mapper.cast(data, SaveCookBook, group_id=self.group_id)
|
||||
data = mapper.cast(data, SaveCookBook, group_id=self.group_id, household_id=self.household_id)
|
||||
cookbook = self.mixins.create_one(data)
|
||||
|
||||
if cookbook:
|
||||
self.publish_event(
|
||||
event_type=EventTypes.cookbook_created,
|
||||
document_data=EventCookbookData(operation=EventOperation.create, cookbook_id=cookbook.id),
|
||||
group_id=cookbook.group_id,
|
||||
household_id=cookbook.household_id,
|
||||
message=self.t("notifications.generic-created", name=cookbook.name),
|
||||
)
|
||||
|
||||
@@ -68,19 +71,25 @@ class GroupCookbookController(BaseCrudController):
|
||||
|
||||
@router.put("", response_model=list[ReadCookBook])
|
||||
def update_many(self, data: list[UpdateCookBook]):
|
||||
updated = []
|
||||
updated_by_group_and_household: defaultdict[UUID4, defaultdict[UUID4, list[ReadCookBook]]] = defaultdict(
|
||||
lambda: defaultdict(list)
|
||||
)
|
||||
|
||||
for cookbook in data:
|
||||
cb = self.mixins.update_one(cookbook, cookbook.id)
|
||||
updated.append(cb)
|
||||
updated_by_group_and_household[cb.group_id][cb.household_id].append(cb)
|
||||
|
||||
if updated:
|
||||
self.publish_event(
|
||||
event_type=EventTypes.cookbook_updated,
|
||||
document_data=EventCookbookBulkData(
|
||||
operation=EventOperation.update, cookbook_ids=[cb.id for cb in updated]
|
||||
),
|
||||
)
|
||||
if updated_by_group_and_household:
|
||||
for group_id, household_dict in updated_by_group_and_household.items():
|
||||
for household_id, updated in household_dict.items():
|
||||
self.publish_event(
|
||||
event_type=EventTypes.cookbook_updated,
|
||||
document_data=EventCookbookBulkData(
|
||||
operation=EventOperation.update, cookbook_ids=[cb.id for cb in updated]
|
||||
),
|
||||
group_id=group_id,
|
||||
household_id=household_id,
|
||||
)
|
||||
|
||||
return updated
|
||||
|
||||
@@ -102,7 +111,7 @@ class GroupCookbookController(BaseCrudController):
|
||||
|
||||
return cookbook.cast(
|
||||
RecipeCookBook,
|
||||
recipes=self.repos.recipes.by_group(self.group_id).by_category_and_tags(
|
||||
recipes=self.repos.recipes.by_category_and_tags(
|
||||
cookbook.categories,
|
||||
cookbook.tags,
|
||||
cookbook.tools,
|
||||
@@ -119,6 +128,8 @@ class GroupCookbookController(BaseCrudController):
|
||||
self.publish_event(
|
||||
event_type=EventTypes.cookbook_updated,
|
||||
document_data=EventCookbookData(operation=EventOperation.update, cookbook_id=cookbook.id),
|
||||
group_id=cookbook.group_id,
|
||||
household_id=cookbook.household_id,
|
||||
message=self.t("notifications.generic-updated", name=cookbook.name),
|
||||
)
|
||||
|
||||
@@ -131,6 +142,8 @@ class GroupCookbookController(BaseCrudController):
|
||||
self.publish_event(
|
||||
event_type=EventTypes.cookbook_deleted,
|
||||
document_data=EventCookbookData(operation=EventOperation.delete, cookbook_id=cookbook.id),
|
||||
group_id=cookbook.group_id,
|
||||
household_id=cookbook.household_id,
|
||||
message=self.t("notifications.generic-deleted", name=cookbook.name),
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@ from mealie.routes._base.base_controllers import BaseUserController
|
||||
from mealie.routes._base.controller import controller
|
||||
from mealie.routes._base.mixins import HttpRepo
|
||||
from mealie.routes._base.routers import MealieCrudRoute
|
||||
from mealie.schema.group.group_events import (
|
||||
from mealie.schema.household.group_events import (
|
||||
GroupEventNotifierCreate,
|
||||
GroupEventNotifierOut,
|
||||
GroupEventNotifierPrivate,
|
||||
@@ -29,7 +29,7 @@ from mealie.services.event_bus_service.event_types import (
|
||||
)
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/groups/events/notifications", tags=["Group: Event Notifications"], route_class=MealieCrudRoute
|
||||
prefix="/households/events/notifications", tags=["Households: Event Notifications"], route_class=MealieCrudRoute
|
||||
)
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ class GroupEventsNotifierController(BaseUserController):
|
||||
if not self.user:
|
||||
raise Exception("No user is logged in.")
|
||||
|
||||
return self.repos.group_event_notifier.by_group(self.user.group_id)
|
||||
return self.repos.group_event_notifier
|
||||
|
||||
# =======================================================================
|
||||
# CRUD Operations
|
||||
@@ -63,7 +63,7 @@ class GroupEventsNotifierController(BaseUserController):
|
||||
|
||||
@router.post("", response_model=GroupEventNotifierOut, status_code=201)
|
||||
def create_one(self, data: GroupEventNotifierCreate):
|
||||
save_data = cast(data, GroupEventNotifierSave, group_id=self.user.group_id)
|
||||
save_data = cast(data, GroupEventNotifierSave, group_id=self.group_id, household_id=self.household_id)
|
||||
return self.mixins.create_one(save_data)
|
||||
|
||||
@router.get("/{item_id}", response_model=GroupEventNotifierOut)
|
||||
@@ -100,5 +100,5 @@ class GroupEventsNotifierController(BaseUserController):
|
||||
document_data=EventDocumentDataBase(document_type=EventDocumentType.generic, operation=EventOperation.info),
|
||||
)
|
||||
|
||||
test_listener = AppriseEventListener(self.group_id)
|
||||
test_listener = AppriseEventListener(self.group_id, self.household_id)
|
||||
test_listener.publish_to_subscribers(test_event, [item.apprise_url])
|
||||
@@ -6,7 +6,7 @@ from pydantic import UUID4
|
||||
from mealie.routes._base.base_controllers import BaseUserController
|
||||
from mealie.routes._base.controller import controller
|
||||
from mealie.routes._base.mixins import HttpRepo
|
||||
from mealie.schema.group.group_recipe_action import (
|
||||
from mealie.schema.household.group_recipe_action import (
|
||||
CreateGroupRecipeAction,
|
||||
GroupRecipeActionOut,
|
||||
GroupRecipeActionPagination,
|
||||
@@ -14,14 +14,14 @@ from mealie.schema.group.group_recipe_action import (
|
||||
)
|
||||
from mealie.schema.response.pagination import PaginationQuery
|
||||
|
||||
router = APIRouter(prefix="/groups/recipe-actions", tags=["Groups: Recipe Actions"])
|
||||
router = APIRouter(prefix="/households/recipe-actions", tags=["Households: Recipe Actions"])
|
||||
|
||||
|
||||
@controller(router)
|
||||
class GroupRecipeActionController(BaseUserController):
|
||||
@cached_property
|
||||
def repo(self):
|
||||
return self.repos.group_recipe_actions.by_group(self.group_id)
|
||||
return self.repos.group_recipe_actions
|
||||
|
||||
@property
|
||||
def mixins(self):
|
||||
@@ -39,7 +39,7 @@ class GroupRecipeActionController(BaseUserController):
|
||||
|
||||
@router.post("", response_model=GroupRecipeActionOut, status_code=201)
|
||||
def create_one(self, data: CreateGroupRecipeAction):
|
||||
save = data.cast(SaveGroupRecipeAction, group_id=self.group.id)
|
||||
save = data.cast(SaveGroupRecipeAction, group_id=self.group_id, household_id=self.household_id)
|
||||
return self.mixins.create_one(save)
|
||||
|
||||
@router.get("/{item_id}", response_model=GroupRecipeActionOut)
|
||||
@@ -0,0 +1,69 @@
|
||||
from functools import cached_property
|
||||
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
from mealie.routes._base.base_controllers import BaseUserController
|
||||
from mealie.routes._base.controller import controller
|
||||
from mealie.routes._base.routers import UserAPIRouter
|
||||
from mealie.schema.household.household import HouseholdInDB
|
||||
from mealie.schema.household.household_permissions import SetPermissions
|
||||
from mealie.schema.household.household_preferences import ReadHouseholdPreferences, UpdateHouseholdPreferences
|
||||
from mealie.schema.household.household_statistics import HouseholdStatistics
|
||||
from mealie.schema.response.pagination import PaginationQuery
|
||||
from mealie.schema.user.user import UserOut
|
||||
from mealie.services.household_services.household_service import HouseholdService
|
||||
|
||||
router = UserAPIRouter(prefix="/households", tags=["Households: Self Service"])
|
||||
|
||||
|
||||
@controller(router)
|
||||
class HouseholdSelfServiceController(BaseUserController):
|
||||
@cached_property
|
||||
def service(self) -> HouseholdService:
|
||||
return HouseholdService(self.group_id, self.household_id, self.repos)
|
||||
|
||||
@router.get("/self", response_model=HouseholdInDB)
|
||||
def get_logged_in_user_household(self):
|
||||
"""Returns the Household Data for the Current User"""
|
||||
return self.household
|
||||
|
||||
@router.get("/members", response_model=list[UserOut])
|
||||
def get_household_members(self):
|
||||
"""Returns all users belonging to the current household"""
|
||||
private_users = self.repos.users.page_all(
|
||||
PaginationQuery(page=1, per_page=-1, query_filter=f"household_id={self.household_id}")
|
||||
).items
|
||||
return [user.cast(UserOut) for user in private_users]
|
||||
|
||||
@router.get("/preferences", response_model=ReadHouseholdPreferences)
|
||||
def get_household_preferences(self):
|
||||
return self.household.preferences
|
||||
|
||||
@router.put("/preferences", response_model=ReadHouseholdPreferences)
|
||||
def update_household_preferences(self, new_pref: UpdateHouseholdPreferences):
|
||||
return self.repos.household_preferences.update(self.household_id, new_pref)
|
||||
|
||||
@router.put("/permissions", response_model=UserOut)
|
||||
def set_member_permissions(self, permissions: SetPermissions):
|
||||
self.checks.can_manage()
|
||||
|
||||
target_user = self.repos.users.get_one(permissions.user_id)
|
||||
|
||||
if not target_user:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, detail="User not found")
|
||||
|
||||
if target_user.group_id != self.group_id:
|
||||
raise HTTPException(status.HTTP_403_FORBIDDEN, detail="User is not a member of this group")
|
||||
|
||||
if target_user.household_id != self.household_id:
|
||||
raise HTTPException(status.HTTP_403_FORBIDDEN, detail="User is not a member of this household")
|
||||
|
||||
target_user.can_invite = permissions.can_invite
|
||||
target_user.can_manage = permissions.can_manage
|
||||
target_user.can_organize = permissions.can_organize
|
||||
|
||||
return self.repos.users.update(permissions.user_id, target_user)
|
||||
|
||||
@router.get("/statistics", response_model=HouseholdStatistics)
|
||||
def get_statistics(self):
|
||||
return self.service.calculate_statistics()
|
||||
@@ -4,23 +4,24 @@ from fastapi import APIRouter, Header, HTTPException, status
|
||||
|
||||
from mealie.core.security import url_safe_token
|
||||
from mealie.routes._base import BaseUserController, controller
|
||||
from mealie.schema.group.invite_token import (
|
||||
from mealie.schema.household.invite_token import (
|
||||
CreateInviteToken,
|
||||
EmailInitationResponse,
|
||||
EmailInvitation,
|
||||
ReadInviteToken,
|
||||
SaveInviteToken,
|
||||
)
|
||||
from mealie.schema.response.pagination import PaginationQuery
|
||||
from mealie.services.email.email_service import EmailService
|
||||
|
||||
router = APIRouter(prefix="/groups/invitations", tags=["Groups: Invitations"])
|
||||
router = APIRouter(prefix="/households/invitations", tags=["Households: Invitations"])
|
||||
|
||||
|
||||
@controller(router)
|
||||
class GroupInvitationsController(BaseUserController):
|
||||
@router.get("", response_model=list[ReadInviteToken])
|
||||
def get_invite_tokens(self):
|
||||
return self.repos.group_invite_tokens.multi_query({"group_id": self.group_id})
|
||||
return self.repos.group_invite_tokens.page_all(PaginationQuery(page=1, per_page=-1)).items
|
||||
|
||||
@router.post("", response_model=ReadInviteToken, status_code=status.HTTP_201_CREATED)
|
||||
def create_invite_token(self, uses: CreateInviteToken):
|
||||
@@ -30,7 +31,9 @@ class GroupInvitationsController(BaseUserController):
|
||||
detail="User is not allowed to create invite tokens",
|
||||
)
|
||||
|
||||
token = SaveInviteToken(uses_left=uses.uses, group_id=self.group_id, token=url_safe_token())
|
||||
token = SaveInviteToken(
|
||||
uses_left=uses.uses, group_id=self.group_id, household_id=self.household_id, token=url_safe_token()
|
||||
)
|
||||
return self.repos.group_invite_tokens.create(token)
|
||||
|
||||
@router.post("/email", response_model=EmailInitationResponse)
|
||||
@@ -17,14 +17,14 @@ from mealie.schema.response.pagination import PaginationQuery
|
||||
from mealie.schema.response.responses import ErrorResponse
|
||||
from mealie.services.event_bus_service.event_types import EventMealplanCreatedData, EventTypes
|
||||
|
||||
router = APIRouter(prefix="/groups/mealplans", tags=["Groups: Mealplans"])
|
||||
router = APIRouter(prefix="/households/mealplans", tags=["Households: Mealplans"])
|
||||
|
||||
|
||||
@controller(router)
|
||||
class GroupMealplanController(BaseCrudController):
|
||||
@cached_property
|
||||
def repo(self) -> RepositoryMeals:
|
||||
return self.repos.meals.by_group(self.group_id)
|
||||
return self.repos.meals
|
||||
|
||||
def registered_exceptions(self, ex: type[Exception]) -> str:
|
||||
registered = {
|
||||
@@ -42,7 +42,7 @@ class GroupMealplanController(BaseCrudController):
|
||||
|
||||
@router.get("/today")
|
||||
def get_todays_meals(self):
|
||||
return self.repo.get_today(group_id=self.group_id)
|
||||
return self.repo.get_today()
|
||||
|
||||
@router.post("/random", response_model=ReadPlanEntry)
|
||||
def create_random_meal(self, data: CreateRandomEntry):
|
||||
@@ -55,11 +55,9 @@ class GroupMealplanController(BaseCrudController):
|
||||
to the random meal selector.
|
||||
"""
|
||||
# Get relevant group rules
|
||||
rules = self.repos.group_meal_plan_rules.by_group(self.group_id).get_rules(
|
||||
PlanRulesDay.from_date(data.date), data.entry_type.value
|
||||
)
|
||||
rules = self.repos.group_meal_plan_rules.get_rules(PlanRulesDay.from_date(data.date), data.entry_type.value)
|
||||
|
||||
recipe_repo = self.repos.recipes.by_group(self.group_id)
|
||||
recipe_repo = self.repos.recipes
|
||||
random_recipes: list[Recipe] = []
|
||||
|
||||
if not rules: # If no rules are set, return any random recipe from the group
|
||||
@@ -74,9 +72,7 @@ class GroupMealplanController(BaseCrudController):
|
||||
categories.extend(rule.categories)
|
||||
|
||||
if tags or categories:
|
||||
random_recipes = self.repos.recipes.by_group(self.group_id).get_random_by_categories_and_tags(
|
||||
categories, tags
|
||||
)
|
||||
random_recipes = self.repos.recipes.get_random_by_categories_and_tags(categories, tags)
|
||||
else:
|
||||
random_recipes = recipe_repo.get_random()
|
||||
|
||||
@@ -124,7 +120,7 @@ class GroupMealplanController(BaseCrudController):
|
||||
|
||||
@router.post("", response_model=ReadPlanEntry, status_code=201)
|
||||
def create_one(self, data: CreatePlanEntry):
|
||||
data = mapper.cast(data, SavePlanEntry, group_id=self.group.id, user_id=self.user.id)
|
||||
data = mapper.cast(data, SavePlanEntry, group_id=self.group_id, user_id=self.user.id)
|
||||
result = self.mixins.create_one(data)
|
||||
|
||||
self.publish_event(
|
||||
@@ -136,6 +132,8 @@ class GroupMealplanController(BaseCrudController):
|
||||
recipe_slug=result.recipe.slug if result.recipe else None,
|
||||
date=data.date,
|
||||
),
|
||||
group_id=result.group_id,
|
||||
household_id=result.household_id,
|
||||
message=f"Mealplan entry created for {data.date} for {data.entry_type}",
|
||||
)
|
||||
|
||||
@@ -11,14 +11,14 @@ from mealie.schema import mapper
|
||||
from mealie.schema.meal_plan.plan_rules import PlanRulesCreate, PlanRulesOut, PlanRulesPagination, PlanRulesSave
|
||||
from mealie.schema.response.pagination import PaginationQuery
|
||||
|
||||
router = UserAPIRouter(prefix="/groups/mealplans/rules", tags=["Groups: Mealplan Rules"])
|
||||
router = UserAPIRouter(prefix="/households/mealplans/rules", tags=["Households: Mealplan Rules"])
|
||||
|
||||
|
||||
@controller(router)
|
||||
class GroupMealplanConfigController(BaseUserController):
|
||||
@cached_property
|
||||
def repo(self):
|
||||
return self.repos.group_meal_plan_rules.by_group(self.group_id)
|
||||
return self.repos.group_meal_plan_rules
|
||||
|
||||
@cached_property
|
||||
def mixins(self):
|
||||
@@ -36,7 +36,7 @@ class GroupMealplanConfigController(BaseUserController):
|
||||
|
||||
@router.post("", response_model=PlanRulesOut, status_code=201)
|
||||
def create_one(self, data: PlanRulesCreate):
|
||||
save = mapper.cast(data, PlanRulesSave, group_id=self.group.id)
|
||||
save = mapper.cast(data, PlanRulesSave, group_id=self.group.id, household_id=self.household.id)
|
||||
return self.mixins.create_one(save)
|
||||
|
||||
@router.get("/{item_id}", response_model=PlanRulesOut)
|
||||
@@ -7,7 +7,7 @@ from pydantic import UUID4
|
||||
from mealie.routes._base.base_controllers import BaseCrudController
|
||||
from mealie.routes._base.controller import controller
|
||||
from mealie.routes._base.mixins import HttpRepo
|
||||
from mealie.schema.group.group_shopping_list import (
|
||||
from mealie.schema.household.group_shopping_list import (
|
||||
ShoppingListAddRecipeParams,
|
||||
ShoppingListCreate,
|
||||
ShoppingListItemCreate,
|
||||
@@ -32,9 +32,9 @@ from mealie.services.event_bus_service.event_types import (
|
||||
EventShoppingListItemBulkData,
|
||||
EventTypes,
|
||||
)
|
||||
from mealie.services.group_services.shopping_lists import ShoppingListService
|
||||
from mealie.services.household_services.shopping_lists import ShoppingListService
|
||||
|
||||
item_router = APIRouter(prefix="/groups/shopping/items", tags=["Group: Shopping List Items"])
|
||||
item_router = APIRouter(prefix="/households/shopping/items", tags=["Households: Shopping List Items"])
|
||||
|
||||
|
||||
def publish_list_item_events(publisher: Callable, items_collection: ShoppingListItemsCollectionOut) -> None:
|
||||
@@ -52,6 +52,9 @@ def publish_list_item_events(publisher: Callable, items_collection: ShoppingList
|
||||
shopping_list_id=shopping_list_id,
|
||||
shopping_list_item_ids=[item.id for item in items],
|
||||
),
|
||||
# since these are all the same shopping list, they share a group_id and household_id
|
||||
group_id=items[0].group_id,
|
||||
household_id=items[0].household_id,
|
||||
)
|
||||
|
||||
if items_collection.updated_items:
|
||||
@@ -67,6 +70,9 @@ def publish_list_item_events(publisher: Callable, items_collection: ShoppingList
|
||||
shopping_list_id=shopping_list_id,
|
||||
shopping_list_item_ids=[item.id for item in items],
|
||||
),
|
||||
# since these are all the same shopping list, they share a group_id and household_id
|
||||
group_id=items[0].group_id,
|
||||
household_id=items[0].household_id,
|
||||
)
|
||||
|
||||
if items_collection.deleted_items:
|
||||
@@ -82,6 +88,9 @@ def publish_list_item_events(publisher: Callable, items_collection: ShoppingList
|
||||
shopping_list_id=shopping_list_id,
|
||||
shopping_list_item_ids=[item.id for item in items],
|
||||
),
|
||||
# since these are all the same shopping list, they share a group_id and household_id
|
||||
group_id=items[0].group_id,
|
||||
household_id=items[0].household_id,
|
||||
)
|
||||
|
||||
|
||||
@@ -89,7 +98,7 @@ def publish_list_item_events(publisher: Callable, items_collection: ShoppingList
|
||||
class ShoppingListItemController(BaseCrudController):
|
||||
@cached_property
|
||||
def service(self):
|
||||
return ShoppingListService(self.repos, self.group, self.user)
|
||||
return ShoppingListService(self.repos)
|
||||
|
||||
@cached_property
|
||||
def repo(self):
|
||||
@@ -143,18 +152,18 @@ class ShoppingListItemController(BaseCrudController):
|
||||
return self.delete_many([item_id])
|
||||
|
||||
|
||||
router = APIRouter(prefix="/groups/shopping/lists", tags=["Group: Shopping Lists"])
|
||||
router = APIRouter(prefix="/households/shopping/lists", tags=["Households: Shopping Lists"])
|
||||
|
||||
|
||||
@controller(router)
|
||||
class ShoppingListController(BaseCrudController):
|
||||
@cached_property
|
||||
def service(self):
|
||||
return ShoppingListService(self.repos, self.group, self.user)
|
||||
return ShoppingListService(self.repos)
|
||||
|
||||
@cached_property
|
||||
def repo(self):
|
||||
return self.repos.group_shopping_lists.by_group(self.user.group_id)
|
||||
return self.repos.group_shopping_lists
|
||||
|
||||
# =======================================================================
|
||||
# CRUD Operations
|
||||
@@ -175,11 +184,13 @@ class ShoppingListController(BaseCrudController):
|
||||
|
||||
@router.post("", response_model=ShoppingListOut, status_code=201)
|
||||
def create_one(self, data: ShoppingListCreate):
|
||||
shopping_list = self.service.create_one_list(data)
|
||||
shopping_list = self.service.create_one_list(data, self.user.id)
|
||||
if shopping_list:
|
||||
self.publish_event(
|
||||
event_type=EventTypes.shopping_list_created,
|
||||
document_data=EventShoppingListData(operation=EventOperation.create, shopping_list_id=shopping_list.id),
|
||||
group_id=shopping_list.group_id,
|
||||
household_id=shopping_list.household_id,
|
||||
message=self.t("notifications.generic-created", name=shopping_list.name),
|
||||
)
|
||||
|
||||
@@ -195,6 +206,8 @@ class ShoppingListController(BaseCrudController):
|
||||
self.publish_event(
|
||||
event_type=EventTypes.shopping_list_updated,
|
||||
document_data=EventShoppingListData(operation=EventOperation.update, shopping_list_id=shopping_list.id),
|
||||
group_id=shopping_list.group_id,
|
||||
household_id=shopping_list.household_id,
|
||||
message=self.t("notifications.generic-updated", name=shopping_list.name),
|
||||
)
|
||||
|
||||
@@ -207,6 +220,8 @@ class ShoppingListController(BaseCrudController):
|
||||
self.publish_event(
|
||||
event_type=EventTypes.shopping_list_deleted,
|
||||
document_data=EventShoppingListData(operation=EventOperation.delete, shopping_list_id=shopping_list.id),
|
||||
group_id=shopping_list.group_id,
|
||||
household_id=shopping_list.household_id,
|
||||
message=self.t("notifications.generic-deleted", name=shopping_list.name),
|
||||
)
|
||||
|
||||
@@ -252,6 +267,8 @@ class ShoppingListController(BaseCrudController):
|
||||
self.publish_event(
|
||||
event_type=EventTypes.shopping_list_updated,
|
||||
document_data=EventShoppingListData(operation=EventOperation.update, shopping_list_id=updated_list.id),
|
||||
group_id=updated_list.group_id,
|
||||
household_id=updated_list.household_id,
|
||||
message=self.t("notifications.generic-updated", name=updated_list.name),
|
||||
)
|
||||
|
||||
@@ -8,18 +8,18 @@ from mealie.routes._base.base_controllers import BaseUserController
|
||||
from mealie.routes._base.controller import controller
|
||||
from mealie.routes._base.mixins import HttpRepo
|
||||
from mealie.schema import mapper
|
||||
from mealie.schema.group.webhook import CreateWebhook, ReadWebhook, SaveWebhook, WebhookPagination
|
||||
from mealie.schema.household.webhook import CreateWebhook, ReadWebhook, SaveWebhook, WebhookPagination
|
||||
from mealie.schema.response.pagination import PaginationQuery
|
||||
from mealie.services.scheduler.tasks.post_webhooks import post_group_webhooks, post_single_webhook
|
||||
|
||||
router = APIRouter(prefix="/groups/webhooks", tags=["Groups: Webhooks"])
|
||||
router = APIRouter(prefix="/households/webhooks", tags=["Households: Webhooks"])
|
||||
|
||||
|
||||
@controller(router)
|
||||
class ReadWebhookController(BaseUserController):
|
||||
@cached_property
|
||||
def repo(self):
|
||||
return self.repos.webhooks.by_group(self.group_id)
|
||||
return self.repos.webhooks
|
||||
|
||||
@property
|
||||
def mixins(self) -> HttpRepo:
|
||||
@@ -37,7 +37,7 @@ class ReadWebhookController(BaseUserController):
|
||||
|
||||
@router.post("", response_model=ReadWebhook, status_code=201)
|
||||
def create_one(self, data: CreateWebhook):
|
||||
save = mapper.cast(data, SaveWebhook, group_id=self.group.id)
|
||||
save = mapper.cast(data, SaveWebhook, group_id=self.group_id, household_id=self.household_id)
|
||||
return self.mixins.create_one(save)
|
||||
|
||||
@router.post("/rerun")
|
||||
@@ -46,7 +46,7 @@ class ReadWebhookController(BaseUserController):
|
||||
|
||||
start_time = datetime.min.time()
|
||||
start_dt = datetime.combine(datetime.now(timezone.utc).date(), start_time)
|
||||
post_group_webhooks(start_dt=start_dt, group_id=self.group.id)
|
||||
post_group_webhooks(start_dt=start_dt, group_id=self.group.id, household_id=self.household.id)
|
||||
|
||||
@router.get("/{item_id}", response_model=ReadWebhook)
|
||||
def get_one(self, item_id: UUID4):
|
||||
@@ -12,7 +12,7 @@ These routes are for development only! These assets are served by Caddy when not
|
||||
in development mode. If you make changes, be sure to test the production container.
|
||||
"""
|
||||
|
||||
router = APIRouter(prefix="/recipes")
|
||||
router = APIRouter(prefix="/recipes", include_in_schema=False)
|
||||
|
||||
|
||||
class ImageType(str, Enum):
|
||||
|
||||
@@ -9,7 +9,7 @@ These routes are for development only! These assets are served by Caddy when not
|
||||
in development mode. If you make changes, be sure to test the production container.
|
||||
"""
|
||||
|
||||
router = APIRouter(prefix="/users")
|
||||
router = APIRouter(prefix="/users", include_in_schema=False)
|
||||
|
||||
|
||||
@router.get("/{user_id}/{file_name}", response_class=FileResponse)
|
||||
|
||||
@@ -8,7 +8,7 @@ from mealie.routes._base.mixins import HttpRepo
|
||||
from mealie.schema import mapper
|
||||
from mealie.schema.recipe import CategoryIn, RecipeCategoryResponse
|
||||
from mealie.schema.recipe.recipe import RecipeCategory, RecipeCategoryPagination
|
||||
from mealie.schema.recipe.recipe_category import CategoryBase, CategorySave
|
||||
from mealie.schema.recipe.recipe_category import CategoryBase, CategoryOut, CategorySave
|
||||
from mealie.schema.response.pagination import PaginationQuery
|
||||
from mealie.services import urls
|
||||
from mealie.services.event_bus_service.event_types import EventCategoryData, EventOperation, EventTypes
|
||||
@@ -29,11 +29,11 @@ class RecipeCategoryController(BaseCrudController):
|
||||
# CRUD Operations
|
||||
@cached_property
|
||||
def repo(self):
|
||||
return self.repos.categories.by_group(self.group_id)
|
||||
return self.repos.categories
|
||||
|
||||
@cached_property
|
||||
def mixins(self):
|
||||
return HttpRepo(self.repo, self.logger)
|
||||
return HttpRepo[CategorySave, CategoryOut, CategorySave](self.repo, self.logger)
|
||||
|
||||
@router.get("", response_model=RecipeCategoryPagination)
|
||||
def get_all(self, q: PaginationQuery = Depends(PaginationQuery), search: str | None = None):
|
||||
@@ -56,6 +56,8 @@ class RecipeCategoryController(BaseCrudController):
|
||||
self.publish_event(
|
||||
event_type=EventTypes.category_created,
|
||||
document_data=EventCategoryData(operation=EventOperation.create, category_id=new_category.id),
|
||||
group_id=new_category.group_id,
|
||||
household_id=None,
|
||||
message=self.t(
|
||||
"notifications.generic-created-with-url",
|
||||
name=new_category.name,
|
||||
@@ -82,6 +84,8 @@ class RecipeCategoryController(BaseCrudController):
|
||||
self.publish_event(
|
||||
event_type=EventTypes.category_updated,
|
||||
document_data=EventCategoryData(operation=EventOperation.update, category_id=category.id),
|
||||
group_id=category.group_id,
|
||||
household_id=None,
|
||||
message=self.t(
|
||||
"notifications.generic-updated-with-url",
|
||||
name=category.name,
|
||||
@@ -102,6 +106,8 @@ class RecipeCategoryController(BaseCrudController):
|
||||
self.publish_event(
|
||||
event_type=EventTypes.category_deleted,
|
||||
document_data=EventCategoryData(operation=EventOperation.delete, category_id=category.id),
|
||||
group_id=category.group_id,
|
||||
household_id=None,
|
||||
message=self.t("notifications.generic-deleted", name=category.name),
|
||||
)
|
||||
|
||||
@@ -121,5 +127,5 @@ class RecipeCategoryController(BaseCrudController):
|
||||
id=category.id,
|
||||
slug=category.slug,
|
||||
name=category.name,
|
||||
recipes=self.repos.recipes.by_group(self.group_id).get_by_categories([category]),
|
||||
recipes=self.repos.recipes.get_by_categories([category]),
|
||||
)
|
||||
|
||||
@@ -20,7 +20,7 @@ router = APIRouter(prefix="/tags", tags=["Organizer: Tags"])
|
||||
class TagController(BaseCrudController):
|
||||
@cached_property
|
||||
def repo(self):
|
||||
return self.repos.tags.by_group(self.group_id)
|
||||
return self.repos.tags
|
||||
|
||||
@cached_property
|
||||
def mixins(self):
|
||||
@@ -58,6 +58,8 @@ class TagController(BaseCrudController):
|
||||
self.publish_event(
|
||||
event_type=EventTypes.tag_created,
|
||||
document_data=EventTagData(operation=EventOperation.create, tag_id=new_tag.id),
|
||||
group_id=new_tag.group_id,
|
||||
household_id=None,
|
||||
message=self.t(
|
||||
"notifications.generic-created-with-url",
|
||||
name=new_tag.name,
|
||||
@@ -77,6 +79,8 @@ class TagController(BaseCrudController):
|
||||
self.publish_event(
|
||||
event_type=EventTypes.tag_updated,
|
||||
document_data=EventTagData(operation=EventOperation.update, tag_id=tag.id),
|
||||
group_id=tag.group_id,
|
||||
household_id=None,
|
||||
message=self.t(
|
||||
"notifications.generic-updated-with-url",
|
||||
name=tag.name,
|
||||
@@ -103,6 +107,8 @@ class TagController(BaseCrudController):
|
||||
self.publish_event(
|
||||
event_type=EventTypes.tag_deleted,
|
||||
document_data=EventTagData(operation=EventOperation.delete, tag_id=tag.id),
|
||||
group_id=tag.group_id,
|
||||
household_id=None,
|
||||
message=self.t("notifications.generic-deleted", name=tag.name),
|
||||
)
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ router = APIRouter(prefix="/tools", tags=["Organizer: Tools"])
|
||||
class RecipeToolController(BaseUserController):
|
||||
@cached_property
|
||||
def repo(self):
|
||||
return self.repos.tools.by_group(self.group_id)
|
||||
return self.repos.tools
|
||||
|
||||
@property
|
||||
def mixins(self) -> HttpRepo:
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from . import all_recipe_routes, bulk_actions, comments, recipe_crud_routes, shared_routes, timeline_events
|
||||
from . import bulk_actions, comments, recipe_crud_routes, shared_routes, timeline_events
|
||||
|
||||
prefix = "/recipes"
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
router.include_router(all_recipe_routes.router, prefix=prefix, tags=["Recipe: Query All"])
|
||||
router.include_router(recipe_crud_routes.router_exports)
|
||||
router.include_router(recipe_crud_routes.router)
|
||||
router.include_router(comments.router, prefix=prefix, tags=["Recipe: Comments"])
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from mealie.db.db_setup import generate_session
|
||||
from mealie.repos.all_repositories import get_repositories
|
||||
from mealie.schema.recipe import RecipeSummary
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/summary/untagged", response_model=list[RecipeSummary])
|
||||
async def get_untagged_recipes(count: bool = False, session: Session = Depends(generate_session)):
|
||||
db = get_repositories(session)
|
||||
return db.recipes.count_untagged(count=count, override_schema=RecipeSummary)
|
||||
|
||||
|
||||
@router.get("/summary/uncategorized", response_model=list[RecipeSummary])
|
||||
async def get_uncategorized_recipes(count: bool = False, session: Session = Depends(generate_session)):
|
||||
db = get_repositories(session)
|
||||
return db.recipes.count_uncategorized(count=count, override_schema=RecipeSummary)
|
||||
@@ -10,5 +10,5 @@ class RecipeCommentsController(BaseUserController):
|
||||
@router.get("/{slug}/comments", response_model=list[RecipeCommentOut])
|
||||
async def get_recipe_comments(self, slug: str):
|
||||
"""Get all comments for a recipe"""
|
||||
recipe = self.repos.recipes.by_group(self.group_id).get_one(slug)
|
||||
recipe = self.repos.recipes.get_one(slug)
|
||||
return self.repos.comments.multi_query({"recipe_id": recipe.id})
|
||||
|
||||
@@ -30,7 +30,7 @@ from mealie.core.dependencies import (
|
||||
validate_recipe_token,
|
||||
)
|
||||
from mealie.core.security import create_recipe_slug_token
|
||||
from mealie.db.models.group.cookbook import CookBook
|
||||
from mealie.db.models.household.cookbook import CookBook
|
||||
from mealie.pkgs import cache
|
||||
from mealie.repos.repository_generic import RepositoryGeneric
|
||||
from mealie.repos.repository_recipes import RepositoryRecipes
|
||||
@@ -95,15 +95,15 @@ class JSONBytes(JSONResponse):
|
||||
class BaseRecipeController(BaseCrudController):
|
||||
@cached_property
|
||||
def repo(self) -> RepositoryRecipes:
|
||||
return self.repos.recipes.by_group(self.group_id)
|
||||
return self.repos.recipes
|
||||
|
||||
@cached_property
|
||||
def cookbooks_repo(self) -> RepositoryGeneric[ReadCookBook, CookBook]:
|
||||
return self.repos.cookbooks.by_group(self.group_id)
|
||||
return self.repos.cookbooks
|
||||
|
||||
@cached_property
|
||||
def service(self) -> RecipeService:
|
||||
return RecipeService(self.repos, self.user, self.group, translator=self.translator)
|
||||
return RecipeService(self.repos, self.user, self.household, translator=self.translator)
|
||||
|
||||
@cached_property
|
||||
def mixins(self):
|
||||
@@ -207,7 +207,7 @@ class RecipeController(BaseRecipeController):
|
||||
) from e
|
||||
|
||||
if req.include_tags:
|
||||
ctx = ScraperContext(self.user.id, self.group_id, self.repos)
|
||||
ctx = ScraperContext(self.repos)
|
||||
|
||||
recipe.tags = extras.use_tags(ctx) # type: ignore
|
||||
|
||||
@@ -217,6 +217,8 @@ class RecipeController(BaseRecipeController):
|
||||
self.publish_event(
|
||||
event_type=EventTypes.recipe_created,
|
||||
document_data=EventRecipeData(operation=EventOperation.create, recipe_slug=new_recipe.slug),
|
||||
group_id=new_recipe.group_id,
|
||||
household_id=new_recipe.household_id,
|
||||
message=self.t(
|
||||
"notifications.generic-created-with-url",
|
||||
name=new_recipe.name,
|
||||
@@ -236,6 +238,8 @@ class RecipeController(BaseRecipeController):
|
||||
self.publish_event(
|
||||
event_type=EventTypes.recipe_created,
|
||||
document_data=EventRecipeBulkReportData(operation=EventOperation.create, report_id=report_id),
|
||||
group_id=self.group_id,
|
||||
household_id=self.household_id,
|
||||
)
|
||||
|
||||
return {"reportId": report_id}
|
||||
@@ -265,6 +269,8 @@ class RecipeController(BaseRecipeController):
|
||||
self.publish_event(
|
||||
event_type=EventTypes.recipe_created,
|
||||
document_data=EventRecipeData(operation=EventOperation.create, recipe_slug=recipe.slug),
|
||||
group_id=recipe.group_id,
|
||||
household_id=recipe.household_id,
|
||||
)
|
||||
|
||||
return recipe.slug
|
||||
@@ -290,6 +296,8 @@ class RecipeController(BaseRecipeController):
|
||||
self.publish_event(
|
||||
event_type=EventTypes.recipe_created,
|
||||
document_data=EventRecipeData(operation=EventOperation.create, recipe_slug=recipe.slug),
|
||||
group_id=recipe.group_id,
|
||||
household_id=recipe.household_id,
|
||||
)
|
||||
|
||||
return recipe.slug
|
||||
@@ -324,7 +332,7 @@ class RecipeController(BaseRecipeController):
|
||||
raise HTTPException(status_code=404, detail="cookbook not found")
|
||||
|
||||
# we use the repo by user so we can sort favorites correctly
|
||||
pagination_response = self.repo.by_user(self.user.id).page_all(
|
||||
pagination_response = self.repos.recipes.by_user(self.user.id).page_all(
|
||||
pagination=q,
|
||||
cookbook=cookbook_data,
|
||||
categories=categories,
|
||||
@@ -374,6 +382,8 @@ class RecipeController(BaseRecipeController):
|
||||
self.publish_event(
|
||||
event_type=EventTypes.recipe_created,
|
||||
document_data=EventRecipeData(operation=EventOperation.create, recipe_slug=new_recipe.slug),
|
||||
group_id=new_recipe.group_id,
|
||||
household_id=new_recipe.household_id,
|
||||
message=self.t(
|
||||
"notifications.generic-created-with-url",
|
||||
name=new_recipe.name,
|
||||
@@ -395,6 +405,8 @@ class RecipeController(BaseRecipeController):
|
||||
self.publish_event(
|
||||
event_type=EventTypes.recipe_created,
|
||||
document_data=EventRecipeData(operation=EventOperation.create, recipe_slug=new_recipe.slug),
|
||||
group_id=new_recipe.group_id,
|
||||
household_id=new_recipe.household_id,
|
||||
message=self.t(
|
||||
"notifications.generic-duplicated",
|
||||
name=new_recipe.name,
|
||||
@@ -415,6 +427,8 @@ class RecipeController(BaseRecipeController):
|
||||
self.publish_event(
|
||||
event_type=EventTypes.recipe_updated,
|
||||
document_data=EventRecipeData(operation=EventOperation.update, recipe_slug=recipe.slug),
|
||||
group_id=recipe.group_id,
|
||||
household_id=recipe.household_id,
|
||||
message=self.t(
|
||||
"notifications.generic-updated-with-url",
|
||||
name=recipe.name,
|
||||
@@ -436,6 +450,8 @@ class RecipeController(BaseRecipeController):
|
||||
self.publish_event(
|
||||
event_type=EventTypes.recipe_updated,
|
||||
document_data=EventRecipeData(operation=EventOperation.update, recipe_slug=recipe.slug),
|
||||
group_id=recipe.group_id,
|
||||
household_id=recipe.household_id,
|
||||
message=self.t(
|
||||
"notifications.generic-updated-with-url",
|
||||
name=recipe.name,
|
||||
@@ -458,6 +474,8 @@ class RecipeController(BaseRecipeController):
|
||||
self.publish_event(
|
||||
event_type=EventTypes.recipe_updated,
|
||||
document_data=EventRecipeData(operation=EventOperation.update, recipe_slug=recipe.slug),
|
||||
group_id=recipe.group_id,
|
||||
household_id=recipe.household_id,
|
||||
message=self.t(
|
||||
"notifications.generic-updated-with-url",
|
||||
name=recipe.name,
|
||||
@@ -479,6 +497,8 @@ class RecipeController(BaseRecipeController):
|
||||
self.publish_event(
|
||||
event_type=EventTypes.recipe_deleted,
|
||||
document_data=EventRecipeData(operation=EventOperation.delete, recipe_slug=recipe.slug),
|
||||
group_id=recipe.group_id,
|
||||
household_id=recipe.household_id,
|
||||
message=self.t("notifications.generic-deleted", name=recipe.name),
|
||||
)
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ router = APIRouter()
|
||||
|
||||
@router.get("/shared/{token_id}", response_model=Recipe)
|
||||
def get_shared_recipe(token_id: UUID4, session: Session = Depends(generate_session)):
|
||||
db = get_repositories(session)
|
||||
db = get_repositories(session, group_id=None, household_id=None)
|
||||
|
||||
token_summary = db.recipe_share_tokens.get_one(token_id)
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ class RecipeTimelineEventsController(BaseCrudController):
|
||||
|
||||
@cached_property
|
||||
def recipes_repo(self):
|
||||
return self.repos.recipes.by_group(self.group_id)
|
||||
return self.repos.recipes
|
||||
|
||||
@cached_property
|
||||
def mixins(self):
|
||||
@@ -69,6 +69,8 @@ class RecipeTimelineEventsController(BaseCrudController):
|
||||
document_data=EventRecipeTimelineEventData(
|
||||
operation=EventOperation.create, recipe_slug=recipe.slug, recipe_timeline_event_id=event.id
|
||||
),
|
||||
group_id=recipe.group_id,
|
||||
household_id=recipe.household_id,
|
||||
message=self.t(
|
||||
"notifications.generic-updated-with-url",
|
||||
name=recipe.name,
|
||||
@@ -92,6 +94,8 @@ class RecipeTimelineEventsController(BaseCrudController):
|
||||
document_data=EventRecipeTimelineEventData(
|
||||
operation=EventOperation.update, recipe_slug=recipe.slug, recipe_timeline_event_id=event.id
|
||||
),
|
||||
group_id=recipe.group_id,
|
||||
household_id=recipe.household_id,
|
||||
message=self.t(
|
||||
"notifications.generic-updated-with-url",
|
||||
name=recipe.name,
|
||||
@@ -117,6 +121,8 @@ class RecipeTimelineEventsController(BaseCrudController):
|
||||
document_data=EventRecipeTimelineEventData(
|
||||
operation=EventOperation.delete, recipe_slug=recipe.slug, recipe_timeline_event_id=event.id
|
||||
),
|
||||
group_id=recipe.group_id,
|
||||
household_id=recipe.household_id,
|
||||
message=self.t(
|
||||
"notifications.generic-updated-with-url",
|
||||
name=recipe.name,
|
||||
@@ -145,6 +151,8 @@ class RecipeTimelineEventsController(BaseCrudController):
|
||||
document_data=EventRecipeTimelineEventData(
|
||||
operation=EventOperation.update, recipe_slug=recipe.slug, recipe_timeline_event_id=event.id
|
||||
),
|
||||
group_id=recipe.group_id,
|
||||
household_id=recipe.household_id,
|
||||
message=self.t(
|
||||
"notifications.generic-updated-with-url",
|
||||
name=recipe.name,
|
||||
|
||||
@@ -15,7 +15,7 @@ router = UserAPIRouter(prefix="/shared/recipes", tags=["Shared: Recipes"])
|
||||
class RecipeSharedController(BaseUserController):
|
||||
@cached_property
|
||||
def repo(self):
|
||||
return self.repos.recipe_share_tokens.by_group(self.group_id)
|
||||
return self.repos.recipe_share_tokens
|
||||
|
||||
@cached_property
|
||||
def mixins(self):
|
||||
|
||||
@@ -162,13 +162,14 @@ def serve_recipe_with_meta_public(
|
||||
session: Session = Depends(generate_session),
|
||||
):
|
||||
try:
|
||||
repos = AllRepositories(session)
|
||||
group = repos.groups.get_by_slug_or_id(group_slug)
|
||||
public_repos = AllRepositories(session)
|
||||
group = public_repos.groups.get_by_slug_or_id(group_slug)
|
||||
|
||||
if not group or group.preferences.private_group: # type: ignore
|
||||
return response_404()
|
||||
|
||||
recipe = repos.recipes.by_group(group.id).get_one(recipe_slug)
|
||||
group_repos = AllRepositories(session, group_id=group.id)
|
||||
recipe = group_repos.recipes.get_one(recipe_slug)
|
||||
|
||||
if not recipe or not recipe.settings.public: # type: ignore
|
||||
return response_404()
|
||||
@@ -189,9 +190,9 @@ async def serve_recipe_with_meta(
|
||||
return serve_recipe_with_meta_public(group_slug, recipe_slug, session)
|
||||
|
||||
try:
|
||||
repos = AllRepositories(session)
|
||||
repos = AllRepositories(session, group_id=user.group_id)
|
||||
|
||||
recipe = repos.recipes.by_group(user.group_id).get_one(recipe_slug, "slug")
|
||||
recipe = repos.recipes.get_one(recipe_slug, "slug")
|
||||
if recipe is None:
|
||||
return response_404()
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ router = APIRouter(prefix="/foods", tags=["Recipes: Foods"], route_class=MealieC
|
||||
class IngredientFoodsController(BaseUserController):
|
||||
@cached_property
|
||||
def repo(self):
|
||||
return self.repos.ingredient_foods.by_group(self.group_id)
|
||||
return self.repos.ingredient_foods
|
||||
|
||||
@cached_property
|
||||
def mixins(self):
|
||||
|
||||
@@ -25,7 +25,7 @@ router = APIRouter(prefix="/units", tags=["Recipes: Units"], route_class=MealieC
|
||||
class IngredientUnitsController(BaseUserController):
|
||||
@cached_property
|
||||
def repo(self):
|
||||
return self.repos.ingredient_units.by_group(self.group_id)
|
||||
return self.repos.ingredient_units
|
||||
|
||||
@cached_property
|
||||
def mixins(self):
|
||||
|
||||
@@ -11,7 +11,7 @@ from mealie.routes.users._helpers import assert_user_change_allowed
|
||||
from mealie.schema.response import ErrorResponse, SuccessResponse
|
||||
from mealie.schema.response.pagination import PaginationQuery
|
||||
from mealie.schema.user import ChangePassword, UserBase, UserIn, UserOut
|
||||
from mealie.schema.user.user import UserPagination, UserRatings, UserRatingSummary, UserSummary, UserSummaryPagination
|
||||
from mealie.schema.user.user import UserPagination, UserRatings, UserRatingSummary
|
||||
|
||||
user_router = UserAPIRouter(prefix="/users", tags=["Users: CRUD"])
|
||||
admin_router = AdminAPIRouter(prefix="/users", tags=["Users: Admin CRUD"])
|
||||
@@ -58,18 +58,6 @@ class AdminUserController(BaseAdminController):
|
||||
|
||||
@controller(user_router)
|
||||
class UserController(BaseUserController):
|
||||
@user_router.get("/group-users", response_model=UserSummaryPagination)
|
||||
def get_all_group_users(self, q: PaginationQuery = Depends(PaginationQuery)):
|
||||
"""Returns all users from the current group"""
|
||||
|
||||
response = self.repos.users.by_group(self.group_id).page_all(
|
||||
pagination=q,
|
||||
override=UserSummary,
|
||||
)
|
||||
|
||||
response.set_pagination_guides(user_router.url_path_for("get_all_group_users"), q.model_dump())
|
||||
return response
|
||||
|
||||
@user_router.get("/self", response_model=UserOut)
|
||||
def get_logged_in_user(self):
|
||||
return self.user
|
||||
|
||||
@@ -22,7 +22,7 @@ class UserRatingsController(BaseUserController):
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
recipes_repo = self.repos.recipes.by_group(self.group_id)
|
||||
recipes_repo = self.repos.recipes
|
||||
if isinstance(slug_or_id, UUID):
|
||||
recipe = recipes_repo.get_one(slug_or_id, key="id")
|
||||
else:
|
||||
|
||||
@@ -29,7 +29,7 @@ class RegistrationController(BasePublicController):
|
||||
|
||||
registration_service = RegistrationService(
|
||||
self.logger,
|
||||
get_repositories(self.session),
|
||||
get_repositories(self.session, group_id=None, household_id=None),
|
||||
self.translator,
|
||||
)
|
||||
|
||||
@@ -38,6 +38,7 @@ class RegistrationController(BasePublicController):
|
||||
self.event_bus.dispatch(
|
||||
integration_id="registration",
|
||||
group_id=result.group_id,
|
||||
household_id=result.household_id,
|
||||
event_type=EventTypes.user_signup,
|
||||
document_data=EventUserSignupData(username=result.username, email=result.email),
|
||||
)
|
||||
|
||||
@@ -14,7 +14,7 @@ router = APIRouter()
|
||||
@router.get("/user/name", response_model=ValidationResponse)
|
||||
def validate_user(name: str, session: Session = Depends(generate_session)):
|
||||
"""Checks if a user with the given name exists"""
|
||||
db = get_repositories(session)
|
||||
db = get_repositories(session, group_id=None, household_id=None)
|
||||
existing_element = db.users.get_one(name, "username", any_case=True)
|
||||
return ValidationResponse(valid=existing_element is None)
|
||||
|
||||
@@ -22,7 +22,7 @@ def validate_user(name: str, session: Session = Depends(generate_session)):
|
||||
@router.get("/user/email", response_model=ValidationResponse)
|
||||
def validate_user_email(email: str, session: Session = Depends(generate_session)):
|
||||
"""Checks if a user with the given name exists"""
|
||||
db = get_repositories(session)
|
||||
db = get_repositories(session, group_id=None, household_id=None)
|
||||
existing_element = db.users.get_one(email, "email", any_case=True)
|
||||
return ValidationResponse(valid=existing_element is None)
|
||||
|
||||
@@ -30,15 +30,23 @@ def validate_user_email(email: str, session: Session = Depends(generate_session)
|
||||
@router.get("/group", response_model=ValidationResponse)
|
||||
def validate_group(name: str, session: Session = Depends(generate_session)):
|
||||
"""Checks if a group with the given name exists"""
|
||||
db = get_repositories(session)
|
||||
db = get_repositories(session, group_id=None, household_id=None)
|
||||
existing_element = db.groups.get_by_name(name)
|
||||
return ValidationResponse(valid=existing_element is None)
|
||||
|
||||
|
||||
@router.get("/household", response_model=ValidationResponse)
|
||||
def validate_household(name: str, session: Session = Depends(generate_session)):
|
||||
"""Checks if a household with the given name exists"""
|
||||
db = get_repositories(session, group_id=None, household_id=None)
|
||||
existing_element = db.households.get_by_name(name)
|
||||
return ValidationResponse(valid=existing_element is None)
|
||||
|
||||
|
||||
@router.get("/recipe", response_model=ValidationResponse)
|
||||
def validate_recipe(group_id: UUID, name: str, session: Session = Depends(generate_session)):
|
||||
"""Checks if a group with the given slug exists"""
|
||||
db = get_repositories(session)
|
||||
"""Checks if a recipe with the given slug exists"""
|
||||
db = get_repositories(session, group_id=None, household_id=None)
|
||||
slug = slugify(name)
|
||||
existing_element = db.recipes.get_by_slug(group_id, slug)
|
||||
return ValidationResponse(valid=existing_element is None)
|
||||
|
||||
Reference in New Issue
Block a user