feat: Add label notifier (#5879)

Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
This commit is contained in:
Craig Matear 2025-08-10 02:43:23 +01:00 committed by GitHub
commit d3436a5ca8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 281 additions and 153 deletions

View file

@ -79,7 +79,8 @@
"tag-events": "Tag Events", "tag-events": "Tag Events",
"category-events": "Category Events", "category-events": "Category Events",
"when-a-new-user-joins-your-group": "When a new user joins your group", "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": { "general": {
"add": "Add", "add": "Add",

View file

@ -1,5 +1,4 @@
/* tslint:disable */ /* tslint:disable */
/** /**
/* This file was automatically generated from pydantic models by running pydantic2ts. /* This file was automatically generated from pydantic models by running pydantic2ts.
/* Do not modify it by hand - just update the pydantic models and then re-run the script /* Do not modify it by hand - just update the pydantic models and then re-run the script
@ -117,6 +116,7 @@ export interface CustomPageBase {
export interface RecipeCategoryResponse { export interface RecipeCategoryResponse {
name: string; name: string;
id: string; id: string;
groupId?: string | null;
slug: string; slug: string;
recipes?: RecipeSummary[]; recipes?: RecipeSummary[];
} }
@ -149,18 +149,21 @@ export interface RecipeSummary {
} }
export interface RecipeCategory { export interface RecipeCategory {
id?: string | null; id?: string | null;
groupId?: string | null;
name: string; name: string;
slug: string; slug: string;
[k: string]: unknown; [k: string]: unknown;
} }
export interface RecipeTag { export interface RecipeTag {
id?: string | null; id?: string | null;
groupId?: string | null;
name: string; name: string;
slug: string; slug: string;
[k: string]: unknown; [k: string]: unknown;
} }
export interface RecipeTool { export interface RecipeTool {
id: string; id: string;
groupId?: string | null;
name: string; name: string;
slug: string; slug: string;
householdsWithTool?: string[]; householdsWithTool?: string[];

View file

@ -1,5 +1,4 @@
/* tslint:disable */ /* tslint:disable */
/** /**
/* This file was automatically generated from pydantic models by running pydantic2ts. /* This file was automatically generated from pydantic models by running pydantic2ts.
/* Do not modify it by hand - just update the pydantic models and then re-run the script /* Do not modify it by hand - just update the pydantic models and then re-run the script

View file

@ -1,5 +1,4 @@
/* tslint:disable */ /* tslint:disable */
/** /**
/* This file was automatically generated from pydantic models by running pydantic2ts. /* This file was automatically generated from pydantic models by running pydantic2ts.
/* Do not modify it by hand - just update the pydantic models and then re-run the script /* Do not modify it by hand - just update the pydantic models and then re-run the script
@ -39,7 +38,6 @@ export interface QueryFilterJSONPart {
attributeName?: string | null; attributeName?: string | null;
relationalOperator?: RelationalKeyword | RelationalOperator | null; relationalOperator?: RelationalKeyword | RelationalOperator | null;
value?: string | string[] | null; value?: string | string[] | null;
[k: string]: unknown;
} }
export interface RecipeCookBook { export interface RecipeCookBook {
name: string; name: string;
@ -83,18 +81,21 @@ export interface RecipeSummary {
} }
export interface RecipeCategory { export interface RecipeCategory {
id?: string | null; id?: string | null;
groupId?: string | null;
name: string; name: string;
slug: string; slug: string;
[k: string]: unknown; [k: string]: unknown;
} }
export interface RecipeTag { export interface RecipeTag {
id?: string | null; id?: string | null;
groupId?: string | null;
name: string; name: string;
slug: string; slug: string;
[k: string]: unknown; [k: string]: unknown;
} }
export interface RecipeTool { export interface RecipeTool {
id: string; id: string;
groupId?: string | null;
name: string; name: string;
slug: string; slug: string;
householdsWithTool?: string[]; householdsWithTool?: string[];

View file

@ -1,5 +1,4 @@
/* tslint:disable */ /* tslint:disable */
/** /**
/* This file was automatically generated from pydantic models by running pydantic2ts. /* This file was automatically generated from pydantic models by running pydantic2ts.
/* Do not modify it by hand - just update the pydantic models and then re-run the script /* Do not modify it by hand - just update the pydantic models and then re-run the script

View file

@ -1,5 +1,4 @@
/* tslint:disable */ /* tslint:disable */
/** /**
/* This file was automatically generated from pydantic models by running pydantic2ts. /* This file was automatically generated from pydantic models by running pydantic2ts.
/* Do not modify it by hand - just update the pydantic models and then re-run the script /* Do not modify it by hand - just update the pydantic models and then re-run the script
@ -70,6 +69,9 @@ export interface GroupEventNotifierOptions {
categoryCreated?: boolean; categoryCreated?: boolean;
categoryUpdated?: boolean; categoryUpdated?: boolean;
categoryDeleted?: boolean; categoryDeleted?: boolean;
labelCreated?: boolean;
labelUpdated?: boolean;
labelDeleted?: boolean;
} }
export interface GroupEventNotifierOptionsOut { export interface GroupEventNotifierOptionsOut {
testMessage?: boolean; testMessage?: boolean;
@ -94,6 +96,9 @@ export interface GroupEventNotifierOptionsOut {
categoryCreated?: boolean; categoryCreated?: boolean;
categoryUpdated?: boolean; categoryUpdated?: boolean;
categoryDeleted?: boolean; categoryDeleted?: boolean;
labelCreated?: boolean;
labelUpdated?: boolean;
labelDeleted?: boolean;
id: string; id: string;
} }
export interface GroupEventNotifierOptionsSave { export interface GroupEventNotifierOptionsSave {
@ -119,6 +124,9 @@ export interface GroupEventNotifierOptionsSave {
categoryCreated?: boolean; categoryCreated?: boolean;
categoryUpdated?: boolean; categoryUpdated?: boolean;
categoryDeleted?: boolean; categoryDeleted?: boolean;
labelCreated?: boolean;
labelUpdated?: boolean;
labelDeleted?: boolean;
notifierId: string; notifierId: string;
} }
export interface GroupEventNotifierOut { export interface GroupEventNotifierOut {
@ -166,6 +174,7 @@ export interface GroupRecipeActionOut {
export interface GroupRecipeActionPayload { export interface GroupRecipeActionPayload {
action: GroupRecipeActionOut; action: GroupRecipeActionOut;
content: unknown; content: unknown;
recipeScale: number;
} }
export interface HouseholdCreate { export interface HouseholdCreate {
groupId?: string | null; groupId?: string | null;
@ -587,18 +596,21 @@ export interface RecipeSummary {
} }
export interface RecipeCategory { export interface RecipeCategory {
id?: string | null; id?: string | null;
groupId?: string | null;
name: string; name: string;
slug: string; slug: string;
[k: string]: unknown; [k: string]: unknown;
} }
export interface RecipeTag { export interface RecipeTag {
id?: string | null; id?: string | null;
groupId?: string | null;
name: string; name: string;
slug: string; slug: string;
[k: string]: unknown; [k: string]: unknown;
} }
export interface RecipeTool { export interface RecipeTool {
id: string; id: string;
groupId?: string | null;
name: string; name: string;
slug: string; slug: string;
householdsWithTool?: string[]; householdsWithTool?: string[];

View file

@ -1,5 +1,4 @@
/* tslint:disable */ /* tslint:disable */
/** /**
/* This file was automatically generated from pydantic models by running pydantic2ts. /* This file was automatically generated from pydantic models by running pydantic2ts.
/* Do not modify it by hand - just update the pydantic models and then re-run the script /* Do not modify it by hand - just update the pydantic models and then re-run the script

View file

@ -1,12 +1,9 @@
/* tslint:disable */ /* tslint:disable */
/** /**
/* This file was automatically generated from pydantic models by running pydantic2ts. /* This file was automatically generated from pydantic models by running pydantic2ts.
/* Do not modify it by hand - just update the pydantic models and then re-run the script /* Do not modify it by hand - just update the pydantic models and then re-run the script
*/ */
import type { HouseholdSummary } from "./household";
export type PlanEntryType = "breakfast" | "lunch" | "dinner" | "side"; export type PlanEntryType = "breakfast" | "lunch" | "dinner" | "side";
export type PlanRulesDay = "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday" | "unset"; export type PlanRulesDay = "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday" | "unset";
export type PlanRulesType = "breakfast" | "lunch" | "dinner" | "side" | "unset"; export type PlanRulesType = "breakfast" | "lunch" | "dinner" | "side" | "unset";
@ -44,9 +41,6 @@ export interface PlanRulesOut {
householdId: string; householdId: string;
id: string; id: string;
queryFilter?: QueryFilterJSON; queryFilter?: QueryFilterJSON;
categories?: RecipeCategory[];
tags?: RecipeTag[];
households?: HouseholdSummary[];
} }
export interface QueryFilterJSON { export interface QueryFilterJSON {
parts?: QueryFilterJSONPart[]; parts?: QueryFilterJSONPart[];
@ -108,18 +102,21 @@ export interface RecipeSummary {
} }
export interface RecipeCategory { export interface RecipeCategory {
id?: string | null; id?: string | null;
groupId?: string | null;
name: string; name: string;
slug: string; slug: string;
[k: string]: unknown; [k: string]: unknown;
} }
export interface RecipeTag { export interface RecipeTag {
id?: string | null; id?: string | null;
groupId?: string | null;
name: string; name: string;
slug: string; slug: string;
[k: string]: unknown; [k: string]: unknown;
} }
export interface RecipeTool { export interface RecipeTool {
id: string; id: string;
groupId?: string | null;
name: string; name: string;
slug: string; slug: string;
householdsWithTool?: string[]; householdsWithTool?: string[];

View file

@ -19,6 +19,7 @@ export interface AssignCategories {
export interface CategoryBase { export interface CategoryBase {
name: string; name: string;
id: string; id: string;
groupId?: string | null;
slug: string; slug: string;
} }
export interface AssignSettings { export interface AssignSettings {
@ -40,6 +41,7 @@ export interface AssignTags {
export interface TagBase { export interface TagBase {
name: string; name: string;
id: string; id: string;
groupId?: string | null;
slug: string; slug: string;
} }
export interface CategoryIn { export interface CategoryIn {
@ -48,8 +50,8 @@ export interface CategoryIn {
export interface CategoryOut { export interface CategoryOut {
name: string; name: string;
id: string; id: string;
slug: string;
groupId: string; groupId: string;
slug: string;
} }
export interface CategorySave { export interface CategorySave {
name: string; name: string;
@ -97,11 +99,13 @@ export interface CreateRecipeBulk {
} }
export interface RecipeCategory { export interface RecipeCategory {
id?: string | null; id?: string | null;
groupId?: string | null;
name: string; name: string;
slug: string; slug: string;
} }
export interface RecipeTag { export interface RecipeTag {
id?: string | null; id?: string | null;
groupId?: string | null;
name: string; name: string;
slug: string; slug: string;
} }
@ -223,7 +227,7 @@ export interface Recipe {
groupId?: string; groupId?: string;
name?: string | null; name?: string | null;
slug?: string; slug?: string;
image?: string; image?: unknown;
recipeServings?: number; recipeServings?: number;
recipeYieldQuantity?: number; recipeYieldQuantity?: number;
recipeYield?: string | null; recipeYield?: string | null;
@ -255,6 +259,7 @@ export interface Recipe {
} }
export interface RecipeTool { export interface RecipeTool {
id: string; id: string;
groupId?: string | null;
name: string; name: string;
slug: string; slug: string;
householdsWithTool?: string[]; householdsWithTool?: string[];
@ -293,6 +298,7 @@ export interface UserBase {
export interface RecipeCategoryResponse { export interface RecipeCategoryResponse {
name: string; name: string;
id: string; id: string;
groupId?: string | null;
slug: string; slug: string;
recipes?: RecipeSummary[]; recipes?: RecipeSummary[];
} }
@ -399,6 +405,7 @@ export interface RecipeSuggestionResponseItem {
export interface RecipeTagResponse { export interface RecipeTagResponse {
name: string; name: string;
id: string; id: string;
groupId?: string | null;
slug: string; slug: string;
recipes?: RecipeSummary[]; recipes?: RecipeSummary[];
} }
@ -447,12 +454,14 @@ export interface RecipeToolOut {
name: string; name: string;
householdsWithTool?: string[]; householdsWithTool?: string[];
id: string; id: string;
groupId: string;
slug: string; slug: string;
} }
export interface RecipeToolResponse { export interface RecipeToolResponse {
name: string; name: string;
householdsWithTool?: string[]; householdsWithTool?: string[];
id: string; id: string;
groupId: string;
slug: string; slug: string;
recipes?: RecipeSummary[]; recipes?: RecipeSummary[];
} }
@ -507,7 +516,7 @@ export interface ScrapeRecipeTest {
url: string; url: string;
useOpenAI?: boolean; useOpenAI?: boolean;
} }
export interface SlugResponse { } export interface SlugResponse {}
export interface TagIn { export interface TagIn {
name: string; name: string;
} }

View file

@ -1,5 +1,4 @@
/* tslint:disable */ /* tslint:disable */
/** /**
/* This file was automatically generated from pydantic models by running pydantic2ts. /* This file was automatically generated from pydantic models by running pydantic2ts.
/* Do not modify it by hand - just update the pydantic models and then re-run the script /* Do not modify it by hand - just update the pydantic models and then re-run the script

View file

@ -1,5 +1,4 @@
/* tslint:disable */ /* tslint:disable */
/** /**
/* This file was automatically generated from pydantic models by running pydantic2ts. /* This file was automatically generated from pydantic models by running pydantic2ts.
/* Do not modify it by hand - just update the pydantic models and then re-run the script /* Do not modify it by hand - just update the pydantic models and then re-run the script

View file

@ -1,5 +1,4 @@
/* tslint:disable */ /* tslint:disable */
/** /**
/* This file was automatically generated from pydantic models by running pydantic2ts. /* This file was automatically generated from pydantic models by running pydantic2ts.
/* Do not modify it by hand - just update the pydantic models and then re-run the script /* Do not modify it by hand - just update the pydantic models and then re-run the script
@ -63,6 +62,7 @@ export interface GroupInDB {
export interface CategoryBase { export interface CategoryBase {
name: string; name: string;
id: string; id: string;
groupId?: string | null;
slug: string; slug: string;
} }
export interface ReadWebhook { export interface ReadWebhook {
@ -197,7 +197,6 @@ export interface UserBase {
canManage?: boolean; canManage?: boolean;
canManageHousehold?: boolean; canManageHousehold?: boolean;
canOrganize?: boolean; canOrganize?: boolean;
advancedOptions?: boolean;
} }
export interface UserIn { export interface UserIn {
id?: string | null; id?: string | null;

View file

@ -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 { return {

View file

@ -81,4 +81,4 @@ declare module "vue" {
} }
} }
export {}; export { };

View file

@ -0,0 +1,48 @@
"""'Add label notifier CRUD bools'
Revision ID: e6bb583aac2d
Revises: d7b3ce6fa31a
Create Date: 2025-08-09 19:32:37.285172
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "e6bb583aac2d"
down_revision: str | None = "d7b3ce6fa31a"
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 ###

View file

@ -46,6 +46,10 @@ class GroupEventNotifierOptionsModel(SqlAlchemyBase, BaseMixins):
category_updated: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) category_updated: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
category_deleted: 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() @auto_init()
def __init__(self, **_) -> None: def __init__(self, **_) -> None:
pass pass

View file

@ -3,7 +3,7 @@ from functools import cached_property
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from pydantic import UUID4 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.controller import controller
from mealie.routes._base.mixins import HttpRepo from mealie.routes._base.mixins import HttpRepo
from mealie.routes._base.routers import MealieCrudRoute 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.labels.multi_purpose_label import MultiPurposeLabelPagination
from mealie.schema.response.pagination import PaginationQuery 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 from mealie.services.group_services.labels_service import MultiPurposeLabelService
router = APIRouter(prefix="/groups/labels", tags=["Groups: Multi Purpose Labels"], route_class=MealieCrudRoute) router = APIRouter(prefix="/groups/labels", tags=["Groups: Multi Purpose Labels"], route_class=MealieCrudRoute)
@controller(router) @controller(router)
class MultiPurposeLabelsController(BaseUserController): class MultiPurposeLabelsController(BaseCrudController):
@cached_property @cached_property
def service(self): def service(self):
return MultiPurposeLabelService(self.repos) return MultiPurposeLabelService(self.repos)
@ -53,7 +54,15 @@ class MultiPurposeLabelsController(BaseUserController):
@router.post("", response_model=MultiPurposeLabelOut) @router.post("", response_model=MultiPurposeLabelOut)
def create_one(self, data: MultiPurposeLabelCreate): 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) @router.get("/{item_id}", response_model=MultiPurposeLabelOut)
def get_one(self, item_id: UUID4): def get_one(self, item_id: UUID4):
@ -61,8 +70,25 @@ class MultiPurposeLabelsController(BaseUserController):
@router.put("/{item_id}", response_model=MultiPurposeLabelOut) @router.put("/{item_id}", response_model=MultiPurposeLabelOut)
def update_one(self, item_id: UUID4, data: MultiPurposeLabelUpdate): 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) @router.delete("/{item_id}", response_model=MultiPurposeLabelOut)
def delete_one(self, item_id: UUID4): 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

View file

@ -3,11 +3,11 @@ from .datetime_parse import DateError, DateTimeError, DurationError, TimeError
from .mealie_model import HasUUID, MealieModel, SearchType from .mealie_model import HasUUID, MealieModel, SearchType
__all__ = [ __all__ = [
"HasUUID",
"MealieModel",
"SearchType",
"DateError", "DateError",
"DateTimeError", "DateTimeError",
"DurationError", "DurationError",
"TimeError", "TimeError",
"HasUUID",
"MealieModel",
"SearchType",
] ]

View file

@ -18,28 +18,10 @@ from .restore import (
from .settings import CustomPageBase, CustomPageOut from .settings import CustomPageBase, CustomPageOut
__all__ = [ __all__ = [
"MaintenanceLogs",
"MaintenanceStorageDetails",
"MaintenanceSummary",
"ChowdownURL", "ChowdownURL",
"MigrationFile", "MigrationFile",
"MigrationImport", "MigrationImport",
"Migrations", "Migrations",
"CustomPageBase",
"CustomPageOut",
"CommentImport",
"CustomPageImport",
"GroupImport",
"ImportBase",
"NotificationImport",
"RecipeImport",
"SettingsImport",
"UserImport",
"AllBackups",
"BackupFile",
"BackupOptions",
"CreateBackup",
"ImportJob",
"AdminAboutInfo", "AdminAboutInfo",
"AppInfo", "AppInfo",
"AppStartupInfo", "AppStartupInfo",
@ -49,5 +31,23 @@ __all__ = [
"EmailReady", "EmailReady",
"EmailSuccess", "EmailSuccess",
"EmailTest", "EmailTest",
"CustomPageBase",
"CustomPageOut",
"AllBackups",
"BackupFile",
"BackupOptions",
"CreateBackup",
"ImportJob",
"MaintenanceLogs",
"MaintenanceStorageDetails",
"MaintenanceSummary",
"DebugResponse", "DebugResponse",
"CommentImport",
"CustomPageImport",
"GroupImport",
"ImportBase",
"NotificationImport",
"RecipeImport",
"SettingsImport",
"UserImport",
] ]

View file

@ -7,13 +7,13 @@ from .group_seeder import SeederConfig
from .group_statistics import GroupStorage from .group_statistics import GroupStorage
__all__ = [ __all__ = [
"GroupAdminUpdate",
"GroupStorage",
"GroupDataExport", "GroupDataExport",
"SeederConfig",
"CreateGroupPreferences", "CreateGroupPreferences",
"ReadGroupPreferences", "ReadGroupPreferences",
"UpdateGroupPreferences", "UpdateGroupPreferences",
"GroupStorage",
"DataMigrationCreate", "DataMigrationCreate",
"SupportedMigrations", "SupportedMigrations",
"SeederConfig",
"GroupAdminUpdate",
] ]

View file

@ -70,49 +70,6 @@ from .invite_token import CreateInviteToken, EmailInitationResponse, EmailInvita
from .webhook import CreateWebhook, ReadWebhook, SaveWebhook, WebhookPagination, WebhookType from .webhook import CreateWebhook, ReadWebhook, SaveWebhook, WebhookPagination, WebhookType
__all__ = [ __all__ = [
"GroupEventNotifierCreate",
"GroupEventNotifierOptions",
"GroupEventNotifierOptionsOut",
"GroupEventNotifierOptionsSave",
"GroupEventNotifierOut",
"GroupEventNotifierPrivate",
"GroupEventNotifierSave",
"GroupEventNotifierUpdate",
"GroupEventPagination",
"CreateGroupRecipeAction",
"GroupRecipeActionOut",
"GroupRecipeActionPagination",
"GroupRecipeActionPayload",
"GroupRecipeActionType",
"SaveGroupRecipeAction",
"CreateWebhook",
"ReadWebhook",
"SaveWebhook",
"WebhookPagination",
"WebhookType",
"CreateHouseholdPreferences",
"ReadHouseholdPreferences",
"SaveHouseholdPreferences",
"UpdateHouseholdPreferences",
"HouseholdCreate",
"HouseholdInDB",
"HouseholdPagination",
"HouseholdRecipeBase",
"HouseholdRecipeCreate",
"HouseholdRecipeOut",
"HouseholdRecipeSummary",
"HouseholdRecipeUpdate",
"HouseholdSave",
"HouseholdSummary",
"HouseholdUserSummary",
"UpdateHousehold",
"UpdateHouseholdAdmin",
"HouseholdStatistics",
"CreateInviteToken",
"EmailInitationResponse",
"EmailInvitation",
"ReadInviteToken",
"SaveInviteToken",
"ShoppingListAddRecipeParams", "ShoppingListAddRecipeParams",
"ShoppingListAddRecipeParamsBulk", "ShoppingListAddRecipeParamsBulk",
"ShoppingListCreate", "ShoppingListCreate",
@ -136,5 +93,48 @@ __all__ = [
"ShoppingListSave", "ShoppingListSave",
"ShoppingListSummary", "ShoppingListSummary",
"ShoppingListUpdate", "ShoppingListUpdate",
"GroupEventNotifierCreate",
"GroupEventNotifierOptions",
"GroupEventNotifierOptionsOut",
"GroupEventNotifierOptionsSave",
"GroupEventNotifierOut",
"GroupEventNotifierPrivate",
"GroupEventNotifierSave",
"GroupEventNotifierUpdate",
"GroupEventPagination",
"CreateGroupRecipeAction",
"GroupRecipeActionOut",
"GroupRecipeActionPagination",
"GroupRecipeActionPayload",
"GroupRecipeActionType",
"SaveGroupRecipeAction",
"CreateHouseholdPreferences",
"ReadHouseholdPreferences",
"SaveHouseholdPreferences",
"UpdateHouseholdPreferences",
"SetPermissions", "SetPermissions",
"CreateInviteToken",
"EmailInitationResponse",
"EmailInvitation",
"ReadInviteToken",
"SaveInviteToken",
"HouseholdStatistics",
"CreateWebhook",
"ReadWebhook",
"SaveWebhook",
"WebhookPagination",
"WebhookType",
"HouseholdCreate",
"HouseholdInDB",
"HouseholdPagination",
"HouseholdRecipeBase",
"HouseholdRecipeCreate",
"HouseholdRecipeOut",
"HouseholdRecipeSummary",
"HouseholdRecipeUpdate",
"HouseholdSave",
"HouseholdSummary",
"HouseholdUserSummary",
"UpdateHousehold",
"UpdateHouseholdAdmin",
] ]

View file

@ -47,6 +47,10 @@ class GroupEventNotifierOptions(MealieModel):
category_updated: bool = False category_updated: bool = False
category_deleted: bool = False category_deleted: bool = False
label_created: bool = False
label_updated: bool = False
label_deleted: bool = False
class GroupEventNotifierOptionsSave(GroupEventNotifierOptions): class GroupEventNotifierOptionsSave(GroupEventNotifierOptions):
notifier_id: UUID4 notifier_id: UUID4

View file

@ -12,9 +12,6 @@ from .plan_rules import PlanRulesCreate, PlanRulesDay, PlanRulesOut, PlanRulesPa
from .shopping_list import ListItem, ShoppingListIn, ShoppingListOut from .shopping_list import ListItem, ShoppingListIn, ShoppingListOut
__all__ = [ __all__ = [
"ListItem",
"ShoppingListIn",
"ShoppingListOut",
"CreatePlanEntry", "CreatePlanEntry",
"CreateRandomEntry", "CreateRandomEntry",
"PlanEntryPagination", "PlanEntryPagination",
@ -22,6 +19,9 @@ __all__ = [
"ReadPlanEntry", "ReadPlanEntry",
"SavePlanEntry", "SavePlanEntry",
"UpdatePlanEntry", "UpdatePlanEntry",
"ListItem",
"ShoppingListIn",
"ShoppingListOut",
"PlanRulesCreate", "PlanRulesCreate",
"PlanRulesDay", "PlanRulesDay",
"PlanRulesOut", "PlanRulesOut",

View file

@ -89,35 +89,6 @@ from .recipe_tool import RecipeToolCreate, RecipeToolOut, RecipeToolResponse, Re
from .request_helpers import RecipeDuplicate, RecipeSlug, RecipeZipTokenResponse, SlugResponse, UpdateImageResponse from .request_helpers import RecipeDuplicate, RecipeSlug, RecipeZipTokenResponse, SlugResponse, UpdateImageResponse
__all__ = [ __all__ = [
"IngredientReferences",
"RecipeStep",
"RecipeNote",
"CategoryBase",
"CategoryIn",
"CategoryOut",
"CategorySave",
"RecipeCategoryResponse",
"RecipeTagResponse",
"TagBase",
"TagIn",
"TagOut",
"TagSave",
"RecipeAsset",
"RecipeTimelineEventCreate",
"RecipeTimelineEventIn",
"RecipeTimelineEventOut",
"RecipeTimelineEventPagination",
"RecipeTimelineEventUpdate",
"TimelineEventImage",
"TimelineEventType",
"RecipeSuggestionQuery",
"RecipeSuggestionResponse",
"RecipeSuggestionResponseItem",
"Nutrition",
"RecipeShareToken",
"RecipeShareTokenCreate",
"RecipeShareTokenSave",
"RecipeShareTokenSummary",
"CreateIngredientFood", "CreateIngredientFood",
"CreateIngredientFoodAlias", "CreateIngredientFoodAlias",
"CreateIngredientUnit", "CreateIngredientUnit",
@ -140,13 +111,27 @@ __all__ = [
"SaveIngredientFood", "SaveIngredientFood",
"SaveIngredientUnit", "SaveIngredientUnit",
"UnitFoodBase", "UnitFoodBase",
"RecipeTimelineEventCreate",
"RecipeTimelineEventIn",
"RecipeTimelineEventOut",
"RecipeTimelineEventPagination",
"RecipeTimelineEventUpdate",
"TimelineEventImage",
"TimelineEventType",
"Nutrition",
"AssignCategories",
"AssignSettings",
"AssignTags",
"DeleteRecipes",
"ExportBase",
"ExportRecipes",
"ExportTypes",
"RecipeCommentCreate", "RecipeCommentCreate",
"RecipeCommentOut", "RecipeCommentOut",
"RecipeCommentPagination", "RecipeCommentPagination",
"RecipeCommentSave", "RecipeCommentSave",
"RecipeCommentUpdate", "RecipeCommentUpdate",
"UserBase", "UserBase",
"RecipeSettings",
"CreateRecipe", "CreateRecipe",
"CreateRecipeBulk", "CreateRecipeBulk",
"CreateRecipeByUrlBulk", "CreateRecipeByUrlBulk",
@ -160,25 +145,40 @@ __all__ = [
"RecipeTagPagination", "RecipeTagPagination",
"RecipeTool", "RecipeTool",
"RecipeToolPagination", "RecipeToolPagination",
"ScrapeRecipe", "IngredientReferences",
"ScrapeRecipeBase", "RecipeStep",
"ScrapeRecipeData", "RecipeNote",
"ScrapeRecipeTest", "RecipeSuggestionQuery",
"AssignCategories", "RecipeSuggestionResponse",
"AssignSettings", "RecipeSuggestionResponseItem",
"AssignTags", "RecipeSettings",
"DeleteRecipes", "RecipeShareToken",
"ExportBase", "RecipeShareTokenCreate",
"ExportRecipes", "RecipeShareTokenSave",
"ExportTypes", "RecipeShareTokenSummary",
"RecipeToolCreate", "RecipeAsset",
"RecipeToolOut",
"RecipeToolResponse",
"RecipeToolSave",
"RecipeImageTypes",
"RecipeDuplicate", "RecipeDuplicate",
"RecipeSlug", "RecipeSlug",
"RecipeZipTokenResponse", "RecipeZipTokenResponse",
"SlugResponse", "SlugResponse",
"UpdateImageResponse", "UpdateImageResponse",
"RecipeToolCreate",
"RecipeToolOut",
"RecipeToolResponse",
"RecipeToolSave",
"CategoryBase",
"CategoryIn",
"CategoryOut",
"CategorySave",
"RecipeCategoryResponse",
"RecipeTagResponse",
"TagBase",
"TagIn",
"TagOut",
"TagSave",
"ScrapeRecipe",
"ScrapeRecipeBase",
"ScrapeRecipeData",
"ScrapeRecipeTest",
"RecipeImageTypes",
] ]

View file

@ -28,14 +28,14 @@ __all__ = [
"QueryFilterJSONPart", "QueryFilterJSONPart",
"RelationalKeyword", "RelationalKeyword",
"RelationalOperator", "RelationalOperator",
"ValidationResponse", "SearchFilter",
"OrderByNullPosition", "OrderByNullPosition",
"OrderDirection", "OrderDirection",
"PaginationBase", "PaginationBase",
"PaginationQuery", "PaginationQuery",
"RecipeSearchQuery", "RecipeSearchQuery",
"RequestQuery", "RequestQuery",
"SearchFilter", "ValidationResponse",
"ErrorResponse", "ErrorResponse",
"FileTokenResponse", "FileTokenResponse",
"SuccessResponse", "SuccessResponse",

View file

@ -38,12 +38,6 @@ from .user_passwords import (
) )
__all__ = [ __all__ = [
"ForgotPassword",
"PasswordResetToken",
"PrivatePasswordResetToken",
"ResetPassword",
"SavePasswordResetToken",
"ValidateResetToken",
"CredentialsRequest", "CredentialsRequest",
"CredentialsRequestForm", "CredentialsRequestForm",
"Token", "Token",
@ -75,4 +69,10 @@ __all__ = [
"UserRatings", "UserRatings",
"UserSummary", "UserSummary",
"UserSummaryPagination", "UserSummaryPagination",
"ForgotPassword",
"PasswordResetToken",
"PrivatePasswordResetToken",
"ResetPassword",
"SavePasswordResetToken",
"ValidateResetToken",
] ]

View file

@ -53,6 +53,10 @@ class EventTypes(Enum):
category_updated = auto() category_updated = auto()
category_deleted = auto() category_deleted = auto()
label_created = auto()
label_updated = auto()
label_deleted = auto()
class EventDocumentType(Enum): class EventDocumentType(Enum):
generic = "generic" generic = "generic"
@ -68,6 +72,7 @@ class EventDocumentType(Enum):
recipe_bulk_report = "recipe_bulk_report" recipe_bulk_report = "recipe_bulk_report"
recipe_timeline_event = "recipe_timeline_event" recipe_timeline_event = "recipe_timeline_event"
tag = "tag" tag = "tag"
label = "label"
class EventOperation(Enum): class EventOperation(Enum):
@ -106,6 +111,11 @@ class EventCategoryData(EventDocumentDataBase):
category_id: UUID4 category_id: UUID4
class EventLabelData(EventDocumentDataBase):
document_type: EventDocumentType = EventDocumentType.label
label_id: UUID4
class EventCookbookData(EventDocumentDataBase): class EventCookbookData(EventDocumentDataBase):
document_type: EventDocumentType = EventDocumentType.cookbook document_type: EventDocumentType = EventDocumentType.cookbook
cookbook_id: UUID4 cookbook_id: UUID4

View file

@ -38,6 +38,9 @@ def preferences_generator():
category_created=random_bool(), category_created=random_bool(),
category_updated=random_bool(), category_updated=random_bool(),
category_deleted=random_bool(), category_deleted=random_bool(),
label_created=random_bool(),
label_updated=random_bool(),
label_deleted=random_bool(),
).model_dump(by_alias=True) ).model_dump(by_alias=True)

View file

@ -173,8 +173,6 @@ units = "/api/units"
"""`/api/units`""" """`/api/units`"""
units_merge = "/api/units/merge" units_merge = "/api/units/merge"
"""`/api/units/merge`""" """`/api/units/merge`"""
users = "/api/users"
"""`/api/users`"""
users_api_tokens = "/api/users/api-tokens" users_api_tokens = "/api/users/api-tokens"
"""`/api/users/api-tokens`""" """`/api/users/api-tokens`"""
users_forgot_password = "/api/users/forgot-password" users_forgot_password = "/api/users/forgot-password"