From cb340c8a09d26f1076f3b9e8d31ec15cc25c936c Mon Sep 17 00:00:00 2001 From: Craig Date: Sun, 3 Aug 2025 14:02:54 +0000 Subject: [PATCH] Add Label notifier * Added new label notifier --- frontend/lang/messages/en-US.json | 3 +- frontend/lib/api/types/household.ts | 9 ++++ frontend/pages/household/notifiers.vue | 18 +++++++ ...b583aac2d_add_label_notifier_crud_bools.py | 48 +++++++++++++++++++ mealie/db/models/household/events.py | 4 ++ mealie/routes/groups/controller_labels.py | 36 ++++++++++++-- mealie/schema/household/group_events.py | 4 ++ .../services/event_bus_service/event_types.py | 10 ++++ .../test_group_notifications.py | 3 ++ 9 files changed, 129 insertions(+), 6 deletions(-) create mode 100644 mealie/alembic/versions/2025-07-30-19.32.37_e6bb583aac2d_add_label_notifier_crud_bools.py diff --git a/frontend/lang/messages/en-US.json b/frontend/lang/messages/en-US.json index 59437e6d4..ba632da62 100644 --- a/frontend/lang/messages/en-US.json +++ b/frontend/lang/messages/en-US.json @@ -79,7 +79,8 @@ "tag-events": "Tag Events", "category-events": "Category Events", "when-a-new-user-joins-your-group": "When a new user joins your group", - "recipe-events": "Recipe Events" + "recipe-events": "Recipe Events", + "label-events": "Label Events" }, "general": { "add": "Add", diff --git a/frontend/lib/api/types/household.ts b/frontend/lib/api/types/household.ts index dd8d31622..7bd2e229d 100644 --- a/frontend/lib/api/types/household.ts +++ b/frontend/lib/api/types/household.ts @@ -71,6 +71,9 @@ export interface GroupEventNotifierOptions { categoryCreated?: boolean; categoryUpdated?: boolean; categoryDeleted?: boolean; + labelCreated?: boolean; + labelUpdated?: boolean; + labelDeleted?: boolean; } export interface GroupEventNotifierOptionsOut { testMessage?: boolean; @@ -95,6 +98,9 @@ export interface GroupEventNotifierOptionsOut { categoryCreated?: boolean; categoryUpdated?: boolean; categoryDeleted?: boolean; + labelCreated?: boolean; + labelUpdated?: boolean; + labelDeleted?: boolean; id: string; } export interface GroupEventNotifierOptionsSave { @@ -120,6 +126,9 @@ export interface GroupEventNotifierOptionsSave { categoryCreated?: boolean; categoryUpdated?: boolean; categoryDeleted?: boolean; + labelCreated?: boolean; + labelUpdated?: boolean; + labelDeleted?: boolean; notifierId: string; } export interface GroupEventNotifierOut { diff --git a/frontend/pages/household/notifiers.vue b/frontend/pages/household/notifiers.vue index de00aaa72..b836eb20f 100644 --- a/frontend/pages/household/notifiers.vue +++ b/frontend/pages/household/notifiers.vue @@ -368,6 +368,24 @@ export default defineNuxtComponent({ }, ], }, + { + id: 8, + text: i18n.t("events.label-events"), + options: [ + { + text: i18n.t("general.create") as string, + key: "labelCreated", + }, + { + text: i18n.t("general.update") as string, + key: "labelUpdated", + }, + { + text: i18n.t("general.delete") as string, + key: "labelDeleted", + }, + ], + }, ]; return { diff --git a/mealie/alembic/versions/2025-07-30-19.32.37_e6bb583aac2d_add_label_notifier_crud_bools.py b/mealie/alembic/versions/2025-07-30-19.32.37_e6bb583aac2d_add_label_notifier_crud_bools.py new file mode 100644 index 000000000..1d5d96878 --- /dev/null +++ b/mealie/alembic/versions/2025-07-30-19.32.37_e6bb583aac2d_add_label_notifier_crud_bools.py @@ -0,0 +1,48 @@ +"""'Add label notifier CRUD bools' + +Revision ID: e6bb583aac2d +Revises: 7cf3054cbbcc +Create Date: 2025-07-30 19:32:37.285172 + +""" + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "e6bb583aac2d" +down_revision: str | None = "7cf3054cbbcc" +branch_labels: str | tuple[str, ...] | None = None +depends_on: str | tuple[str, ...] | None = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table("group_events_notifier_options", schema=None) as batch_op: + batch_op.add_column( + sa.Column( + "label_created", sa.Boolean(), nullable=False, default=False, server_default=sa.sql.expression.false() + ) + ) + batch_op.add_column( + sa.Column( + "label_updated", sa.Boolean(), nullable=False, default=False, server_default=sa.sql.expression.false() + ) + ) + batch_op.add_column( + sa.Column( + "label_deleted", sa.Boolean(), nullable=False, default=False, server_default=sa.sql.expression.false() + ) + ) + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table("group_events_notifier_options", schema=None) as batch_op: + batch_op.drop_column("label_deleted") + batch_op.drop_column("label_updated") + batch_op.drop_column("label_created") + + # ### end Alembic commands ### diff --git a/mealie/db/models/household/events.py b/mealie/db/models/household/events.py index 6be4f9931..9c850d40e 100644 --- a/mealie/db/models/household/events.py +++ b/mealie/db/models/household/events.py @@ -46,6 +46,10 @@ class GroupEventNotifierOptionsModel(SqlAlchemyBase, BaseMixins): category_updated: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) category_deleted: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) + label_created: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) + label_updated: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) + label_deleted: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) + @auto_init() def __init__(self, **_) -> None: pass diff --git a/mealie/routes/groups/controller_labels.py b/mealie/routes/groups/controller_labels.py index fb008ef03..4a156eb94 100644 --- a/mealie/routes/groups/controller_labels.py +++ b/mealie/routes/groups/controller_labels.py @@ -3,7 +3,7 @@ from functools import cached_property from fastapi import APIRouter, Depends from pydantic import UUID4 -from mealie.routes._base.base_controllers import BaseUserController +from mealie.routes._base.base_controllers import BaseCrudController from mealie.routes._base.controller import controller from mealie.routes._base.mixins import HttpRepo from mealie.routes._base.routers import MealieCrudRoute @@ -15,13 +15,14 @@ from mealie.schema.labels import ( ) from mealie.schema.labels.multi_purpose_label import MultiPurposeLabelPagination from mealie.schema.response.pagination import PaginationQuery +from mealie.services.event_bus_service.event_types import EventLabelData, EventOperation, EventTypes from mealie.services.group_services.labels_service import MultiPurposeLabelService router = APIRouter(prefix="/groups/labels", tags=["Groups: Multi Purpose Labels"], route_class=MealieCrudRoute) @controller(router) -class MultiPurposeLabelsController(BaseUserController): +class MultiPurposeLabelsController(BaseCrudController): @cached_property def service(self): return MultiPurposeLabelService(self.repos) @@ -53,7 +54,15 @@ class MultiPurposeLabelsController(BaseUserController): @router.post("", response_model=MultiPurposeLabelOut) def create_one(self, data: MultiPurposeLabelCreate): - return self.service.create_one(data) + new_label = self.service.create_one(data) + self.publish_event( + event_type=EventTypes.label_created, + document_data=EventLabelData(operation=EventOperation.create, label_id=new_label.id), + group_id=new_label.group_id, + household_id=None, + message=self.t("notifications.generic-created", name=new_label.name), + ) + return new_label @router.get("/{item_id}", response_model=MultiPurposeLabelOut) def get_one(self, item_id: UUID4): @@ -61,8 +70,25 @@ class MultiPurposeLabelsController(BaseUserController): @router.put("/{item_id}", response_model=MultiPurposeLabelOut) def update_one(self, item_id: UUID4, data: MultiPurposeLabelUpdate): - return self.mixins.update_one(data, item_id) + label = self.mixins.update_one(data, item_id) + self.publish_event( + event_type=EventTypes.label_updated, + document_data=EventLabelData(operation=EventOperation.update, label_id=label.id), + group_id=label.group_id, + household_id=None, + message=self.t("notifications.generic-updated", name=label.name), + ) + return label @router.delete("/{item_id}", response_model=MultiPurposeLabelOut) def delete_one(self, item_id: UUID4): - return self.mixins.delete_one(item_id) # type: ignore + label = self.mixins.delete_one(item_id) + if label: + self.publish_event( + event_type=EventTypes.label_deleted, + document_data=EventLabelData(operation=EventOperation.delete, label_id=label.id), + group_id=label.group_id, + household_id=None, + message=self.t("notifications.generic-deleted", name=label.name), + ) + return label diff --git a/mealie/schema/household/group_events.py b/mealie/schema/household/group_events.py index 3554e1b9f..7ab964f76 100644 --- a/mealie/schema/household/group_events.py +++ b/mealie/schema/household/group_events.py @@ -47,6 +47,10 @@ class GroupEventNotifierOptions(MealieModel): category_updated: bool = False category_deleted: bool = False + label_created: bool = False + label_updated: bool = False + label_deleted: bool = False + class GroupEventNotifierOptionsSave(GroupEventNotifierOptions): notifier_id: UUID4 diff --git a/mealie/services/event_bus_service/event_types.py b/mealie/services/event_bus_service/event_types.py index 6e3e54b05..b14bdd9af 100644 --- a/mealie/services/event_bus_service/event_types.py +++ b/mealie/services/event_bus_service/event_types.py @@ -53,6 +53,10 @@ class EventTypes(Enum): category_updated = auto() category_deleted = auto() + label_created = auto() + label_updated = auto() + label_deleted = auto() + class EventDocumentType(Enum): generic = "generic" @@ -68,6 +72,7 @@ class EventDocumentType(Enum): recipe_bulk_report = "recipe_bulk_report" recipe_timeline_event = "recipe_timeline_event" tag = "tag" + label = "label" class EventOperation(Enum): @@ -106,6 +111,11 @@ class EventCategoryData(EventDocumentDataBase): category_id: UUID4 +class EventLabelData(EventDocumentDataBase): + document_type: EventDocumentType = EventDocumentType.label + label_id: UUID4 + + class EventCookbookData(EventDocumentDataBase): document_type: EventDocumentType = EventDocumentType.cookbook cookbook_id: UUID4 diff --git a/tests/integration_tests/user_household_tests/test_group_notifications.py b/tests/integration_tests/user_household_tests/test_group_notifications.py index fa0cba5b7..3bd2c0c1a 100644 --- a/tests/integration_tests/user_household_tests/test_group_notifications.py +++ b/tests/integration_tests/user_household_tests/test_group_notifications.py @@ -38,6 +38,9 @@ def preferences_generator(): category_created=random_bool(), category_updated=random_bool(), category_deleted=random_bool(), + label_created=random_bool(), + label_updated=random_bool(), + label_deleted=random_bool(), ).model_dump(by_alias=True)