Merge branch 'mealie-next' into l10n_mealie-next

This commit is contained in:
Michael Genson 2025-08-11 13:19:09 -05:00 committed by GitHub
commit 83602cd5d5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 314 additions and 176 deletions

View file

@ -1,6 +1,6 @@
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0 rev: v6.0.0
hooks: hooks:
- id: check-yaml - id: check-yaml
exclude: "mkdocs.yml" exclude: "mkdocs.yml"
@ -12,7 +12,7 @@ repos:
exclude: ^tests/data/ exclude: ^tests/data/
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version. # Ruff version.
rev: v0.12.7 rev: v0.12.8
hooks: hooks:
- id: ruff - id: ruff
- id: ruff-format - id: ruff-format

View file

@ -33,7 +33,7 @@ export const LOCALES = [
{ {
name: "Svenska (Swedish)", name: "Svenska (Swedish)",
value: "sv-SE", value: "sv-SE",
progress: 50, progress: 52,
dir: "ltr", dir: "ltr",
}, },
{ {
@ -57,7 +57,7 @@ export const LOCALES = [
{ {
name: "Pусский (Russian)", name: "Pусский (Russian)",
value: "ru-RU", value: "ru-RU",
progress: 37, progress: 38,
dir: "ltr", dir: "ltr",
}, },
{ {
@ -75,7 +75,7 @@ export const LOCALES = [
{ {
name: "Português do Brasil (Brazilian Portuguese)", name: "Português do Brasil (Brazilian Portuguese)",
value: "pt-BR", value: "pt-BR",
progress: 36, progress: 40,
dir: "ltr", dir: "ltr",
}, },
{ {
@ -105,7 +105,7 @@ export const LOCALES = [
{ {
name: "Lietuvių (Lithuanian)", name: "Lietuvių (Lithuanian)",
value: "lt-LT", value: "lt-LT",
progress: 27, progress: 26,
dir: "ltr", dir: "ltr",
}, },
{ {
@ -153,13 +153,13 @@ export const LOCALES = [
{ {
name: "Galego (Galician)", name: "Galego (Galician)",
value: "gl-ES", value: "gl-ES",
progress: 38, progress: 39,
dir: "ltr", dir: "ltr",
}, },
{ {
name: "Français (French)", name: "Français (French)",
value: "fr-FR", value: "fr-FR",
progress: 50, progress: 52,
dir: "ltr", dir: "ltr",
}, },
{ {
@ -219,7 +219,7 @@ export const LOCALES = [
{ {
name: "Dansk (Danish)", name: "Dansk (Danish)",
value: "da-DK", value: "da-DK",
progress: 39, progress: 40,
dir: "ltr", dir: "ltr",
}, },
{ {

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

@ -18,10 +18,20 @@
{{ $t('user.it-looks-like-this-is-your-first-time-logging-in') }} {{ $t('user.it-looks-like-this-is-your-first-time-logging-in') }}
</p> </p>
<p class="mb-1"> <p class="mb-1">
<strong>{{ $t('user.username') }}:</strong> changeme@example.com <strong>{{ $t('user.username') }}: </strong>changeme@example.com
<AppButtonCopy
copy-text="changeme@example.com"
color="info"
btn-class="h-auto"
/>
</p> </p>
<p class="mb-3"> <p class="mb-3">
<strong>{{ $t('user.password') }}:</strong> MyPassword <strong>{{ $t('user.password') }}: </strong>MyPassword
<AppButtonCopy
copy-text="MyPassword"
color="info"
btn-class="h-auto"
/>
</p> </p>
<p> <p>
{{ $t('user.dont-want-to-see-this-anymore-be-sure-to-change-your-email') }} {{ $t('user.dont-want-to-see-this-anymore-be-sure-to-change-your-email') }}

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

24
poetry.lock generated
View file

@ -785,14 +785,14 @@ test = ["pytest", "pytest-cov", "pytest-mpl", "pytest-subtests"]
[[package]] [[package]]
name = "freezegun" name = "freezegun"
version = "1.5.4" version = "1.5.5"
description = "Let your Python tests travel through time" description = "Let your Python tests travel through time"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
groups = ["dev"] groups = ["dev"]
files = [ files = [
{file = "freezegun-1.5.4-py3-none-any.whl", hash = "sha256:8bdd75c9d790f53d5a173d273064ccd7900984b36635be552befeedb0cd47b20"}, {file = "freezegun-1.5.5-py3-none-any.whl", hash = "sha256:cd557f4a75cf074e84bc374249b9dd491eaeacd61376b9eb3c423282211619d2"},
{file = "freezegun-1.5.4.tar.gz", hash = "sha256:798b9372fdd4d907f33e8b6a58bc64e682d9ffa8d494ce60f780197ee81faed1"}, {file = "freezegun-1.5.5.tar.gz", hash = "sha256:ac7742a6cc6c25a2c35e9292dfd554b897b517d2dec26891a2e8debf205cb94a"},
] ]
[package.dependencies] [package.dependencies]
@ -2279,14 +2279,14 @@ testing = ["pytest", "pytest-benchmark"]
[[package]] [[package]]
name = "pre-commit" name = "pre-commit"
version = "4.2.0" version = "4.3.0"
description = "A framework for managing and maintaining multi-language pre-commit hooks." description = "A framework for managing and maintaining multi-language pre-commit hooks."
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["dev"] groups = ["dev"]
files = [ files = [
{file = "pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd"}, {file = "pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8"},
{file = "pre_commit-4.2.0.tar.gz", hash = "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146"}, {file = "pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16"},
] ]
[package.dependencies] [package.dependencies]
@ -2637,14 +2637,14 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
[[package]] [[package]]
name = "pylint" name = "pylint"
version = "3.3.7" version = "3.3.8"
description = "python code static checker" description = "python code static checker"
optional = false optional = false
python-versions = ">=3.9.0" python-versions = ">=3.9.0"
groups = ["dev"] groups = ["dev"]
files = [ files = [
{file = "pylint-3.3.7-py3-none-any.whl", hash = "sha256:43860aafefce92fca4cf6b61fe199cdc5ae54ea28f9bf4cd49de267b5195803d"}, {file = "pylint-3.3.8-py3-none-any.whl", hash = "sha256:7ef94aa692a600e82fabdd17102b73fc226758218c97473c7ad67bd4cb905d83"},
{file = "pylint-3.3.7.tar.gz", hash = "sha256:2b11de8bde49f9c5059452e0c310c079c746a0a8eeaa789e5aa966ecc23e4559"}, {file = "pylint-3.3.8.tar.gz", hash = "sha256:26698de19941363037e2937d3db9ed94fb3303fdadf7d98847875345a8bb6b05"},
] ]
[package.dependencies] [package.dependencies]
@ -3104,14 +3104,14 @@ orjson = ["orjson (>=3.9.14,<4)"]
[[package]] [[package]]
name = "recipe-scrapers" name = "recipe-scrapers"
version = "15.8.0" version = "15.9.0"
description = "Python package, scraping recipes from all over the internet" description = "Python package, scraping recipes from all over the internet"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
groups = ["main"] groups = ["main"]
files = [ files = [
{file = "recipe_scrapers-15.8.0-py3-none-any.whl", hash = "sha256:65015802f3d6e60493b9a9de4737c0aa42eb67c6d2ced43a1f32ffaf4fb9c48e"}, {file = "recipe_scrapers-15.9.0-py3-none-any.whl", hash = "sha256:8f794090b46c18b9b863c39965adee0823253d366278d1fa2623235446caa701"},
{file = "recipe_scrapers-15.8.0.tar.gz", hash = "sha256:938d0510b88809fa181e49d95498bbc40db40d8d973637b3f7b77da837bcda12"}, {file = "recipe_scrapers-15.9.0.tar.gz", hash = "sha256:0235e9d1dd290be5ad5e58ba5be3ff51941c45efb7ec58f2872216fe501cd7d1"},
] ]
[package.dependencies] [package.dependencies]

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"