Coverage for opt/mealie/lib/python3.12/site-packages/mealie/services/event_bus_service/event_types.py: 100%
132 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 03:04 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 03:04 +0000
1import uuid
2from datetime import UTC, date, datetime
3from enum import Enum, auto
4from typing import Any
6from pydantic import UUID4, SerializeAsAny, field_validator
8from ...schema._mealie.mealie_model import MealieModel
10INTERNAL_INTEGRATION_ID = "mealie_generic_user"
13class EventTypes(Enum):
14 """
15 The event type defines whether or not a subscriber should receive an event.
17 Each event type is represented by a field on the subscriber repository, therefore any changes
18 made here must also be reflected in the database (and likely requires a database migration).
20 If you'd like more granular control over the metadata of the event, e.g. events for sub-records
21 (like shopping list items), modify the event document type instead (which is not tied to a database entry).
22 """
24 # used internally and cannot be subscribed to
25 test_message = auto()
26 webhook_task = auto()
28 recipe_created = auto()
29 recipe_updated = auto()
30 recipe_deleted = auto()
32 user_signup = auto()
34 data_migrations = auto()
35 data_export = auto()
36 data_import = auto()
38 mealplan_entry_created = auto()
40 shopping_list_created = auto()
41 shopping_list_updated = auto()
42 shopping_list_deleted = auto()
44 cookbook_created = auto()
45 cookbook_updated = auto()
46 cookbook_deleted = auto()
48 tag_created = auto()
49 tag_updated = auto()
50 tag_deleted = auto()
52 category_created = auto()
53 category_updated = auto()
54 category_deleted = auto()
56 label_created = auto()
57 label_updated = auto()
58 label_deleted = auto()
61class EventDocumentType(Enum):
62 generic = "generic"
64 user = "user"
66 category = "category"
67 cookbook = "cookbook"
68 mealplan = "mealplan"
69 shopping_list = "shopping_list"
70 shopping_list_item = "shopping_list_item"
71 recipe = "recipe"
72 recipe_bulk_report = "recipe_bulk_report"
73 recipe_timeline_event = "recipe_timeline_event"
74 tag = "tag"
75 label = "label"
78class EventOperation(Enum):
79 info = "info"
81 create = "create"
82 update = "update"
83 delete = "delete"
86class EventDocumentDataBase(MealieModel):
87 document_type: EventDocumentType
88 operation: EventOperation
89 ...
92class EventMealplanCreatedData(EventDocumentDataBase):
93 document_type: EventDocumentType = EventDocumentType.mealplan
94 operation: EventOperation = EventOperation.create
95 mealplan_id: int
96 date: date
97 recipe_id: UUID4 | None = None
98 recipe_name: str | None = None
99 recipe_slug: str | None = None
102class EventUserSignupData(EventDocumentDataBase):
103 document_type: EventDocumentType = EventDocumentType.user
104 operation: EventOperation = EventOperation.create
105 username: str
106 email: str
109class EventCategoryData(EventDocumentDataBase):
110 document_type: EventDocumentType = EventDocumentType.category
111 category_id: UUID4
114class EventLabelData(EventDocumentDataBase):
115 document_type: EventDocumentType = EventDocumentType.label
116 label_id: UUID4
119class EventCookbookData(EventDocumentDataBase):
120 document_type: EventDocumentType = EventDocumentType.cookbook
121 cookbook_id: UUID4
124class EventCookbookBulkData(EventDocumentDataBase):
125 document_type: EventDocumentType = EventDocumentType.cookbook
126 cookbook_ids: list[UUID4]
129class EventShoppingListData(EventDocumentDataBase):
130 document_type: EventDocumentType = EventDocumentType.shopping_list
131 shopping_list_id: UUID4
134class EventShoppingListItemData(EventDocumentDataBase):
135 document_type: EventDocumentType = EventDocumentType.shopping_list_item
136 shopping_list_id: UUID4
137 shopping_list_item_id: UUID4
140class EventShoppingListItemBulkData(EventDocumentDataBase):
141 document_type: EventDocumentType = EventDocumentType.shopping_list_item
142 shopping_list_id: UUID4
143 shopping_list_item_ids: list[UUID4]
146class EventRecipeData(EventDocumentDataBase):
147 document_type: EventDocumentType = EventDocumentType.recipe
148 recipe_slug: str
151class EventRecipeBulkData(EventDocumentDataBase):
152 document_type: EventDocumentType = EventDocumentType.recipe
153 recipe_slugs: list[str]
156class EventRecipeBulkReportData(EventDocumentDataBase):
157 document_type: EventDocumentType = EventDocumentType.recipe_bulk_report
158 report_id: UUID4
161class EventRecipeTimelineEventData(EventDocumentDataBase):
162 document_type: EventDocumentType = EventDocumentType.recipe_timeline_event
163 recipe_slug: str
164 recipe_timeline_event_id: UUID4
167class EventTagData(EventDocumentDataBase):
168 document_type: EventDocumentType = EventDocumentType.tag
169 tag_id: UUID4
172class EventWebhookData(EventDocumentDataBase):
173 webhook_start_dt: datetime
174 webhook_end_dt: datetime
175 webhook_body: Any = None
178class EventBusMessage(MealieModel):
179 title: str
180 body: str = ""
182 @classmethod
183 def from_type(cls, event_type: EventTypes, body: str = "") -> "EventBusMessage":
184 title = event_type.name.replace("_", " ").title()
185 return cls(title=title, body=body)
187 @field_validator("body")
188 def populate_body(v):
189 # if the body is empty, apprise won't send the notification
190 return v or "generic"
193class Event(MealieModel):
194 message: EventBusMessage
195 event_type: EventTypes
196 integration_id: str
197 document_data: SerializeAsAny[EventDocumentDataBase]
199 # set at instantiation
200 event_id: UUID4 | None = None
201 timestamp: datetime | None = None
203 def __init__(self, *args, **kwargs) -> None:
204 super().__init__(*args, **kwargs)
205 self.event_id = uuid.uuid4()
206 self.timestamp = datetime.now(UTC)