mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-10-31 02:03:35 -04:00 
			
		
		
		
	chore: refactor base schema (#1098)
* remove dead backup code * implmenet own base model * refactor to use MealieModel instead of CamelModel * cleanup deps
This commit is contained in:
		| @@ -96,7 +96,7 @@ def generate_typescript_types() -> None: | |||||||
|  |  | ||||||
|         try: |         try: | ||||||
|             path_as_module = path_to_module(module) |             path_as_module = path_to_module(module) | ||||||
|             generate_typescript_defs(path_as_module, str(out_path), exclude=("CamelModel"))  # type: ignore |             generate_typescript_defs(path_as_module, str(out_path), exclude=("MealieModel"))  # type: ignore | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             failed_modules.append(module) |             failed_modules.append(module) | ||||||
|             print("\nModule Errors:", module, "-----------------")  # noqa |             print("\nModule Errors:", module, "-----------------")  # noqa | ||||||
|   | |||||||
| @@ -3,11 +3,12 @@ import pathlib | |||||||
| import _static | import _static | ||||||
| import dotenv | import dotenv | ||||||
| import requests | import requests | ||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from jinja2 import Template | from jinja2 import Template | ||||||
| from requests import Response | from requests import Response | ||||||
| from rich import print | from rich import print | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
| BASE = pathlib.Path(__file__).parent.parent.parent | BASE = pathlib.Path(__file__).parent.parent.parent | ||||||
|  |  | ||||||
| API_KEY = dotenv.get_key(BASE / ".env", "CROWDIN_API_KEY") | API_KEY = dotenv.get_key(BASE / ".env", "CROWDIN_API_KEY") | ||||||
| @@ -57,7 +58,7 @@ export const LOCALES = [{% for locale in locales %} | |||||||
| """ | """ | ||||||
|  |  | ||||||
|  |  | ||||||
| class TargetLanguage(CamelModel): | class TargetLanguage(MealieModel): | ||||||
|     id: str |     id: str | ||||||
|     name: str |     name: str | ||||||
|     locale: str |     locale: str | ||||||
|   | |||||||
| @@ -1,22 +1,22 @@ | |||||||
| from fastapi import APIRouter | from fastapi import APIRouter | ||||||
| from fastapi_camelcase import CamelModel |  | ||||||
|  |  | ||||||
| from mealie.routes._base import BaseAdminController, controller | from mealie.routes._base import BaseAdminController, controller | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
| from mealie.services.email import EmailService | from mealie.services.email import EmailService | ||||||
|  |  | ||||||
| router = APIRouter(prefix="/email") | router = APIRouter(prefix="/email") | ||||||
|  |  | ||||||
|  |  | ||||||
| class EmailReady(CamelModel): | class EmailReady(MealieModel): | ||||||
|     ready: bool |     ready: bool | ||||||
|  |  | ||||||
|  |  | ||||||
| class EmailSuccess(CamelModel): | class EmailSuccess(MealieModel): | ||||||
|     success: bool |     success: bool | ||||||
|     error: str = None |     error: str = None | ||||||
|  |  | ||||||
|  |  | ||||||
| class EmailTest(CamelModel): | class EmailTest(MealieModel): | ||||||
|     email: str |     email: str | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,2 @@ | |||||||
|  | from .mealie_model import * | ||||||
|  | from .types import * | ||||||
|   | |||||||
							
								
								
									
										44
									
								
								mealie/schema/_mealie/mealie_model.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								mealie/schema/_mealie/mealie_model.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | |||||||
|  | from __future__ import annotations | ||||||
|  |  | ||||||
|  | from typing import TypeVar | ||||||
|  |  | ||||||
|  | from humps.main import camelize | ||||||
|  | from pydantic import BaseModel | ||||||
|  |  | ||||||
|  | T = TypeVar("T", bound=BaseModel) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class MealieModel(BaseModel): | ||||||
|  |     class Config: | ||||||
|  |         alias_generator = camelize | ||||||
|  |         allow_population_by_field_name = True | ||||||
|  |  | ||||||
|  |     def cast(self, cls: type[T], **kwargs) -> T: | ||||||
|  |         """ | ||||||
|  |         Cast the current model to another with additional arguments. Useful for | ||||||
|  |         transforming DTOs into models that are saved to a database | ||||||
|  |         """ | ||||||
|  |         create_data = {field: getattr(self, field) for field in self.__fields__ if field in cls.__fields__} | ||||||
|  |         create_data.update(kwargs or {}) | ||||||
|  |         return cls(**create_data) | ||||||
|  |  | ||||||
|  |     def map_to(self, dest: T) -> T: | ||||||
|  |         """ | ||||||
|  |         Map matching values from the current model to another model. Model returned | ||||||
|  |         for method chaining. | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         for field in self.__fields__: | ||||||
|  |             if field in dest.__fields__: | ||||||
|  |                 setattr(dest, field, getattr(self, field)) | ||||||
|  |  | ||||||
|  |         return dest | ||||||
|  |  | ||||||
|  |     def map_from(self, src: BaseModel): | ||||||
|  |         """ | ||||||
|  |         Map matching values from another model to the current model. | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         for field in src.__fields__: | ||||||
|  |             if field in self.__fields__: | ||||||
|  |                 setattr(self, field, getattr(src, field)) | ||||||
| @@ -1,7 +1,7 @@ | |||||||
| from fastapi_camelcase import CamelModel | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
|  |  | ||||||
| class AppStatistics(CamelModel): | class AppStatistics(MealieModel): | ||||||
|     total_recipes: int |     total_recipes: int | ||||||
|     total_users: int |     total_users: int | ||||||
|     total_groups: int |     total_groups: int | ||||||
| @@ -9,7 +9,7 @@ class AppStatistics(CamelModel): | |||||||
|     untagged_recipes: int |     untagged_recipes: int | ||||||
|  |  | ||||||
|  |  | ||||||
| class AppInfo(CamelModel): | class AppInfo(MealieModel): | ||||||
|     production: bool |     production: bool | ||||||
|     version: str |     version: str | ||||||
|     demo_status: bool |     demo_status: bool | ||||||
| @@ -26,7 +26,7 @@ class AdminAboutInfo(AppInfo): | |||||||
|     build_id: str |     build_id: str | ||||||
|  |  | ||||||
|  |  | ||||||
| class CheckAppConfig(CamelModel): | class CheckAppConfig(MealieModel): | ||||||
|     email_ready: bool = False |     email_ready: bool = False | ||||||
|     ldap_ready: bool = False |     ldap_ready: bool = False | ||||||
|     base_url_set: bool = False |     base_url_set: bool = False | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| from fastapi_camelcase import CamelModel | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
|  |  | ||||||
| class MaintenanceSummary(CamelModel): | class MaintenanceSummary(MealieModel): | ||||||
|     data_dir_size: str |     data_dir_size: str | ||||||
|     log_file_size: str |     log_file_size: str | ||||||
|     cleanable_images: int |     cleanable_images: int | ||||||
|   | |||||||
| @@ -1,13 +1,14 @@ | |||||||
| from typing import Optional | from typing import Optional | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import validator | from pydantic import validator | ||||||
| from slugify import slugify | from slugify import slugify | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
| from ..recipe.recipe_category import RecipeCategoryResponse | from ..recipe.recipe_category import RecipeCategoryResponse | ||||||
|  |  | ||||||
|  |  | ||||||
| class CustomPageBase(CamelModel): | class CustomPageBase(MealieModel): | ||||||
|     name: str |     name: str | ||||||
|     slug: Optional[str] |     slug: Optional[str] | ||||||
|     position: int |     position: int | ||||||
|   | |||||||
| @@ -1,11 +1,12 @@ | |||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import UUID4, validator | from pydantic import UUID4, validator | ||||||
| from slugify import slugify | from slugify import slugify | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
| from ..recipe.recipe_category import CategoryBase, RecipeCategoryResponse | from ..recipe.recipe_category import CategoryBase, RecipeCategoryResponse | ||||||
|  |  | ||||||
|  |  | ||||||
| class CreateCookBook(CamelModel): | class CreateCookBook(MealieModel): | ||||||
|     name: str |     name: str | ||||||
|     description: str = "" |     description: str = "" | ||||||
|     slug: str = None |     slug: str = None | ||||||
|   | |||||||
| @@ -1,10 +1,11 @@ | |||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import UUID4 | from pydantic import UUID4 | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
| from .group_preferences import UpdateGroupPreferences | from .group_preferences import UpdateGroupPreferences | ||||||
|  |  | ||||||
|  |  | ||||||
| class GroupAdminUpdate(CamelModel): | class GroupAdminUpdate(MealieModel): | ||||||
|     id: UUID4 |     id: UUID4 | ||||||
|     name: str |     name: str | ||||||
|     preferences: UpdateGroupPreferences |     preferences: UpdateGroupPreferences | ||||||
|   | |||||||
| @@ -1,11 +1,12 @@ | |||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import UUID4, NoneStr | from pydantic import UUID4, NoneStr | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
| # ============================================================================= | # ============================================================================= | ||||||
| # Group Events Notifier Options | # Group Events Notifier Options | ||||||
|  |  | ||||||
|  |  | ||||||
| class GroupEventNotifierOptions(CamelModel): | class GroupEventNotifierOptions(MealieModel): | ||||||
|     """ |     """ | ||||||
|     These events are in-sync with the EventTypes found in the EventBusService. |     These events are in-sync with the EventTypes found in the EventBusService. | ||||||
|     If you modify this, make sure to update the EventBusService as well. |     If you modify this, make sure to update the EventBusService as well. | ||||||
| @@ -55,7 +56,7 @@ class GroupEventNotifierOptionsOut(GroupEventNotifierOptions): | |||||||
| # Notifiers | # Notifiers | ||||||
|  |  | ||||||
|  |  | ||||||
| class GroupEventNotifierCreate(CamelModel): | class GroupEventNotifierCreate(MealieModel): | ||||||
|     name: str |     name: str | ||||||
|     apprise_url: str |     apprise_url: str | ||||||
|  |  | ||||||
| @@ -71,7 +72,7 @@ class GroupEventNotifierUpdate(GroupEventNotifierSave): | |||||||
|     apprise_url: NoneStr = None |     apprise_url: NoneStr = None | ||||||
|  |  | ||||||
|  |  | ||||||
| class GroupEventNotifierOut(CamelModel): | class GroupEventNotifierOut(MealieModel): | ||||||
|     id: UUID4 |     id: UUID4 | ||||||
|     name: str |     name: str | ||||||
|     enabled: bool |     enabled: bool | ||||||
|   | |||||||
| @@ -1,10 +1,11 @@ | |||||||
| from datetime import datetime | from datetime import datetime | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import UUID4 | from pydantic import UUID4 | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
| class GroupDataExport(CamelModel): |  | ||||||
|  | class GroupDataExport(MealieModel): | ||||||
|     id: UUID4 |     id: UUID4 | ||||||
|     group_id: UUID4 |     group_id: UUID4 | ||||||
|     name: str |     name: str | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| import enum | import enum | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
|  |  | ||||||
| class SupportedMigrations(str, enum.Enum): | class SupportedMigrations(str, enum.Enum): | ||||||
| @@ -10,5 +10,5 @@ class SupportedMigrations(str, enum.Enum): | |||||||
|     mealie_alpha = "mealie_alpha" |     mealie_alpha = "mealie_alpha" | ||||||
|  |  | ||||||
|  |  | ||||||
| class DataMigrationCreate(CamelModel): | class DataMigrationCreate(MealieModel): | ||||||
|     source_type: SupportedMigrations |     source_type: SupportedMigrations | ||||||
|   | |||||||
| @@ -1,8 +1,9 @@ | |||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import UUID4 | from pydantic import UUID4 | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
| class SetPermissions(CamelModel): |  | ||||||
|  | class SetPermissions(MealieModel): | ||||||
|     user_id: UUID4 |     user_id: UUID4 | ||||||
|     can_manage: bool = False |     can_manage: bool = False | ||||||
|     can_invite: bool = False |     can_invite: bool = False | ||||||
|   | |||||||
| @@ -1,10 +1,11 @@ | |||||||
| from uuid import UUID | from uuid import UUID | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import UUID4 | from pydantic import UUID4 | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
| class UpdateGroupPreferences(CamelModel): |  | ||||||
|  | class UpdateGroupPreferences(MealieModel): | ||||||
|     private_group: bool = False |     private_group: bool = False | ||||||
|     first_day_of_week: int = 0 |     first_day_of_week: int = 0 | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,13 +2,13 @@ from __future__ import annotations | |||||||
|  |  | ||||||
| from typing import Optional | from typing import Optional | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import UUID4 | from pydantic import UUID4 | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
| from mealie.schema.recipe.recipe_ingredient import IngredientFood, IngredientUnit | from mealie.schema.recipe.recipe_ingredient import IngredientFood, IngredientUnit | ||||||
|  |  | ||||||
|  |  | ||||||
| class ShoppingListItemRecipeRef(CamelModel): | class ShoppingListItemRecipeRef(MealieModel): | ||||||
|     recipe_id: UUID4 |     recipe_id: UUID4 | ||||||
|     recipe_quantity: float |     recipe_quantity: float | ||||||
|  |  | ||||||
| @@ -21,7 +21,7 @@ class ShoppingListItemRecipeRefOut(ShoppingListItemRecipeRef): | |||||||
|         orm_mode = True |         orm_mode = True | ||||||
|  |  | ||||||
|  |  | ||||||
| class ShoppingListItemCreate(CamelModel): | class ShoppingListItemCreate(MealieModel): | ||||||
|     shopping_list_id: UUID4 |     shopping_list_id: UUID4 | ||||||
|     checked: bool = False |     checked: bool = False | ||||||
|     position: int = 0 |     position: int = 0 | ||||||
| @@ -51,11 +51,11 @@ class ShoppingListItemOut(ShoppingListItemUpdate): | |||||||
|         orm_mode = True |         orm_mode = True | ||||||
|  |  | ||||||
|  |  | ||||||
| class ShoppingListCreate(CamelModel): | class ShoppingListCreate(MealieModel): | ||||||
|     name: str = None |     name: str = None | ||||||
|  |  | ||||||
|  |  | ||||||
| class ShoppingListRecipeRefOut(CamelModel): | class ShoppingListRecipeRefOut(MealieModel): | ||||||
|     id: UUID4 |     id: UUID4 | ||||||
|     shopping_list_id: UUID4 |     shopping_list_id: UUID4 | ||||||
|     recipe_id: UUID4 |     recipe_id: UUID4 | ||||||
|   | |||||||
| @@ -1,20 +1,21 @@ | |||||||
| from uuid import UUID | from uuid import UUID | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import NoneStr | from pydantic import NoneStr | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
| class CreateInviteToken(CamelModel): |  | ||||||
|  | class CreateInviteToken(MealieModel): | ||||||
|     uses: int |     uses: int | ||||||
|  |  | ||||||
|  |  | ||||||
| class SaveInviteToken(CamelModel): | class SaveInviteToken(MealieModel): | ||||||
|     uses_left: int |     uses_left: int | ||||||
|     group_id: UUID |     group_id: UUID | ||||||
|     token: str |     token: str | ||||||
|  |  | ||||||
|  |  | ||||||
| class ReadInviteToken(CamelModel): | class ReadInviteToken(MealieModel): | ||||||
|     token: str |     token: str | ||||||
|     uses_left: int |     uses_left: int | ||||||
|     group_id: UUID |     group_id: UUID | ||||||
| @@ -23,11 +24,11 @@ class ReadInviteToken(CamelModel): | |||||||
|         orm_mode = True |         orm_mode = True | ||||||
|  |  | ||||||
|  |  | ||||||
| class EmailInvitation(CamelModel): | class EmailInvitation(MealieModel): | ||||||
|     email: str |     email: str | ||||||
|     token: str |     token: str | ||||||
|  |  | ||||||
|  |  | ||||||
| class EmailInitationResponse(CamelModel): | class EmailInitationResponse(MealieModel): | ||||||
|     success: bool |     success: bool | ||||||
|     error: NoneStr = None |     error: NoneStr = None | ||||||
|   | |||||||
| @@ -1,10 +1,11 @@ | |||||||
| from uuid import UUID | from uuid import UUID | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import UUID4 | from pydantic import UUID4 | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
| class CreateWebhook(CamelModel): |  | ||||||
|  | class CreateWebhook(MealieModel): | ||||||
|     enabled: bool = True |     enabled: bool = True | ||||||
|     name: str = "" |     name: str = "" | ||||||
|     url: str = "" |     url: str = "" | ||||||
|   | |||||||
| @@ -1,10 +1,11 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import UUID4 | from pydantic import UUID4 | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
| class MultiPurposeLabelCreate(CamelModel): |  | ||||||
|  | class MultiPurposeLabelCreate(MealieModel): | ||||||
|     name: str |     name: str | ||||||
|     color: str = "#E0E0E0" |     color: str = "#E0E0E0" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,11 +1,12 @@ | |||||||
| from datetime import date | from datetime import date | ||||||
| from typing import Optional | from typing import Optional | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import validator | from pydantic import validator | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
| class MealIn(CamelModel): |  | ||||||
|  | class MealIn(MealieModel): | ||||||
|     slug: Optional[str] |     slug: Optional[str] | ||||||
|     name: Optional[str] |     name: Optional[str] | ||||||
|     description: Optional[str] |     description: Optional[str] | ||||||
| @@ -14,7 +15,7 @@ class MealIn(CamelModel): | |||||||
|         orm_mode = True |         orm_mode = True | ||||||
|  |  | ||||||
|  |  | ||||||
| class MealDayIn(CamelModel): | class MealDayIn(MealieModel): | ||||||
|     date: Optional[date] |     date: Optional[date] | ||||||
|     meals: list[MealIn] |     meals: list[MealIn] | ||||||
|  |  | ||||||
| @@ -29,7 +30,7 @@ class MealDayOut(MealDayIn): | |||||||
|         orm_mode = True |         orm_mode = True | ||||||
|  |  | ||||||
|  |  | ||||||
| class MealPlanIn(CamelModel): | class MealPlanIn(MealieModel): | ||||||
|     group: str |     group: str | ||||||
|     start_date: date |     start_date: date | ||||||
|     end_date: date |     end_date: date | ||||||
|   | |||||||
| @@ -3,9 +3,9 @@ from enum import Enum | |||||||
| from typing import Optional | from typing import Optional | ||||||
| from uuid import UUID | from uuid import UUID | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import validator | from pydantic import validator | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
| from mealie.schema.recipe.recipe import RecipeSummary | from mealie.schema.recipe.recipe import RecipeSummary | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -16,12 +16,12 @@ class PlanEntryType(str, Enum): | |||||||
|     side = "side" |     side = "side" | ||||||
|  |  | ||||||
|  |  | ||||||
| class CreatRandomEntry(CamelModel): | class CreatRandomEntry(MealieModel): | ||||||
|     date: date |     date: date | ||||||
|     entry_type: PlanEntryType = PlanEntryType.dinner |     entry_type: PlanEntryType = PlanEntryType.dinner | ||||||
|  |  | ||||||
|  |  | ||||||
| class CreatePlanEntry(CamelModel): | class CreatePlanEntry(MealieModel): | ||||||
|     date: date |     date: date | ||||||
|     entry_type: PlanEntryType = PlanEntryType.breakfast |     entry_type: PlanEntryType = PlanEntryType.breakfast | ||||||
|     title: str = "" |     title: str = "" | ||||||
|   | |||||||
| @@ -1,11 +1,12 @@ | |||||||
| import datetime | import datetime | ||||||
| from enum import Enum | from enum import Enum | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import UUID4 | from pydantic import UUID4 | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
| class Category(CamelModel): |  | ||||||
|  | class Category(MealieModel): | ||||||
|     id: UUID4 |     id: UUID4 | ||||||
|     name: str |     name: str | ||||||
|     slug: str |     slug: str | ||||||
| @@ -46,7 +47,7 @@ class PlanRulesType(str, Enum): | |||||||
|     unset = "unset" |     unset = "unset" | ||||||
|  |  | ||||||
|  |  | ||||||
| class PlanRulesCreate(CamelModel): | class PlanRulesCreate(MealieModel): | ||||||
|     day: PlanRulesDay = PlanRulesDay.unset |     day: PlanRulesDay = PlanRulesDay.unset | ||||||
|     entry_type: PlanRulesType = PlanRulesType.unset |     entry_type: PlanRulesType = PlanRulesType.unset | ||||||
|     categories: list[Category] = [] |     categories: list[Category] = [] | ||||||
|   | |||||||
| @@ -1,12 +1,12 @@ | |||||||
| from typing import Optional | from typing import Optional | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic.utils import GetterDict | from pydantic.utils import GetterDict | ||||||
|  |  | ||||||
| from mealie.db.models.group.shopping_list import ShoppingList | from mealie.db.models.group.shopping_list import ShoppingList | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
|  |  | ||||||
| class ListItem(CamelModel): | class ListItem(MealieModel): | ||||||
|     title: Optional[str] |     title: Optional[str] | ||||||
|     text: str = "" |     text: str = "" | ||||||
|     quantity: int = 1 |     quantity: int = 1 | ||||||
| @@ -16,7 +16,7 @@ class ListItem(CamelModel): | |||||||
|         orm_mode = True |         orm_mode = True | ||||||
|  |  | ||||||
|  |  | ||||||
| class ShoppingListIn(CamelModel): | class ShoppingListIn(MealieModel): | ||||||
|     name: str |     name: str | ||||||
|     group: Optional[str] |     group: Optional[str] | ||||||
|     items: list[ListItem] |     items: list[ListItem] | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| from fastapi_camelcase import CamelModel | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
|  |  | ||||||
| class GetAll(CamelModel): | class GetAll(MealieModel): | ||||||
|     start: int = 0 |     start: int = 0 | ||||||
|     limit: int = 999 |     limit: int = 999 | ||||||
|   | |||||||
| @@ -5,13 +5,13 @@ from pathlib import Path | |||||||
| from typing import Any, Optional | from typing import Any, Optional | ||||||
| from uuid import uuid4 | from uuid import uuid4 | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import UUID4, BaseModel, Field, validator | from pydantic import UUID4, BaseModel, Field, validator | ||||||
| from pydantic.utils import GetterDict | from pydantic.utils import GetterDict | ||||||
| from slugify import slugify | from slugify import slugify | ||||||
|  |  | ||||||
| from mealie.core.config import get_app_dirs | from mealie.core.config import get_app_dirs | ||||||
| from mealie.db.models.recipe.recipe import RecipeModel | from mealie.db.models.recipe.recipe import RecipeModel | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
| from .recipe_asset import RecipeAsset | from .recipe_asset import RecipeAsset | ||||||
| from .recipe_comments import RecipeCommentOut | from .recipe_comments import RecipeCommentOut | ||||||
| @@ -23,7 +23,7 @@ from .recipe_step import RecipeStep | |||||||
| app_dirs = get_app_dirs() | app_dirs = get_app_dirs() | ||||||
|  |  | ||||||
|  |  | ||||||
| class RecipeTag(CamelModel): | class RecipeTag(MealieModel): | ||||||
|     id: UUID4 = None |     id: UUID4 = None | ||||||
|     name: str |     name: str | ||||||
|     slug: str |     slug: str | ||||||
| @@ -58,11 +58,11 @@ class CreateRecipeByUrlBulk(BaseModel): | |||||||
|     imports: list[CreateRecipeBulk] |     imports: list[CreateRecipeBulk] | ||||||
|  |  | ||||||
|  |  | ||||||
| class CreateRecipe(CamelModel): | class CreateRecipe(MealieModel): | ||||||
|     name: str |     name: str | ||||||
|  |  | ||||||
|  |  | ||||||
| class RecipeSummary(CamelModel): | class RecipeSummary(MealieModel): | ||||||
|     id: Optional[UUID4] |     id: Optional[UUID4] | ||||||
|  |  | ||||||
|     user_id: UUID4 = Field(default_factory=uuid4) |     user_id: UUID4 = Field(default_factory=uuid4) | ||||||
|   | |||||||
| @@ -1,9 +1,9 @@ | |||||||
| from typing import Optional | from typing import Optional | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
|  |  | ||||||
| class RecipeAsset(CamelModel): | class RecipeAsset(MealieModel): | ||||||
|     name: str |     name: str | ||||||
|     icon: str |     icon: str | ||||||
|     file_name: Optional[str] |     file_name: Optional[str] | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| import enum | import enum | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
| from mealie.schema.recipe.recipe_category import CategoryBase, TagBase | from mealie.schema.recipe.recipe_category import CategoryBase, TagBase | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -9,7 +8,7 @@ class ExportTypes(str, enum.Enum): | |||||||
|     JSON = "json" |     JSON = "json" | ||||||
|  |  | ||||||
|  |  | ||||||
| class ExportBase(CamelModel): | class ExportBase(MealieModel): | ||||||
|     recipes: list[str] |     recipes: list[str] | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -29,12 +28,12 @@ class DeleteRecipes(ExportBase): | |||||||
|     pass |     pass | ||||||
|  |  | ||||||
|  |  | ||||||
| class BulkActionError(CamelModel): | class BulkActionError(MealieModel): | ||||||
|     recipe: str |     recipe: str | ||||||
|     error: str |     error: str | ||||||
|  |  | ||||||
|  |  | ||||||
| class BulkActionsResponse(CamelModel): | class BulkActionsResponse(MealieModel): | ||||||
|     success: bool |     success: bool | ||||||
|     message: str |     message: str | ||||||
|     errors: list[BulkActionError] = [] |     errors: list[BulkActionError] = [] | ||||||
|   | |||||||
| @@ -1,9 +1,10 @@ | |||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import UUID4 | from pydantic import UUID4 | ||||||
| from pydantic.utils import GetterDict | from pydantic.utils import GetterDict | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
| class CategoryIn(CamelModel): |  | ||||||
|  | class CategoryIn(MealieModel): | ||||||
|     name: str |     name: str | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,11 +2,12 @@ from datetime import datetime | |||||||
| from typing import Optional | from typing import Optional | ||||||
| from uuid import UUID | from uuid import UUID | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import UUID4 | from pydantic import UUID4 | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
| class UserBase(CamelModel): |  | ||||||
|  | class UserBase(MealieModel): | ||||||
|     id: int |     id: int | ||||||
|     username: Optional[str] |     username: Optional[str] | ||||||
|     admin: bool |     admin: bool | ||||||
| @@ -15,7 +16,7 @@ class UserBase(CamelModel): | |||||||
|         orm_mode = True |         orm_mode = True | ||||||
|  |  | ||||||
|  |  | ||||||
| class RecipeCommentCreate(CamelModel): | class RecipeCommentCreate(MealieModel): | ||||||
|     recipe_id: UUID4 |     recipe_id: UUID4 | ||||||
|     text: str |     text: str | ||||||
|  |  | ||||||
| @@ -24,7 +25,7 @@ class RecipeCommentSave(RecipeCommentCreate): | |||||||
|     user_id: UUID4 |     user_id: UUID4 | ||||||
|  |  | ||||||
|  |  | ||||||
| class RecipeCommentUpdate(CamelModel): | class RecipeCommentUpdate(MealieModel): | ||||||
|     id: UUID |     id: UUID | ||||||
|     text: str |     text: str | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,13 +4,13 @@ import enum | |||||||
| from typing import Optional, Union | from typing import Optional, Union | ||||||
| from uuid import UUID, uuid4 | from uuid import UUID, uuid4 | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import UUID4, Field | from pydantic import UUID4, Field | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
| from mealie.schema._mealie.types import NoneFloat | from mealie.schema._mealie.types import NoneFloat | ||||||
|  |  | ||||||
|  |  | ||||||
| class UnitFoodBase(CamelModel): | class UnitFoodBase(MealieModel): | ||||||
|     name: str |     name: str | ||||||
|     description: str = "" |     description: str = "" | ||||||
|  |  | ||||||
| @@ -47,7 +47,7 @@ class IngredientUnit(CreateIngredientUnit): | |||||||
|         orm_mode = True |         orm_mode = True | ||||||
|  |  | ||||||
|  |  | ||||||
| class RecipeIngredient(CamelModel): | class RecipeIngredient(MealieModel): | ||||||
|     title: Optional[str] |     title: Optional[str] | ||||||
|     note: Optional[str] |     note: Optional[str] | ||||||
|     unit: Optional[Union[IngredientUnit, CreateIngredientUnit]] |     unit: Optional[Union[IngredientUnit, CreateIngredientUnit]] | ||||||
| @@ -64,7 +64,7 @@ class RecipeIngredient(CamelModel): | |||||||
|         orm_mode = True |         orm_mode = True | ||||||
|  |  | ||||||
|  |  | ||||||
| class IngredientConfidence(CamelModel): | class IngredientConfidence(MealieModel): | ||||||
|     average: NoneFloat = None |     average: NoneFloat = None | ||||||
|     comment: NoneFloat = None |     comment: NoneFloat = None | ||||||
|     name: NoneFloat = None |     name: NoneFloat = None | ||||||
| @@ -73,7 +73,7 @@ class IngredientConfidence(CamelModel): | |||||||
|     food: NoneFloat = None |     food: NoneFloat = None | ||||||
|  |  | ||||||
|  |  | ||||||
| class ParsedIngredient(CamelModel): | class ParsedIngredient(MealieModel): | ||||||
|     input: Optional[str] |     input: Optional[str] | ||||||
|     confidence: IngredientConfidence = IngredientConfidence() |     confidence: IngredientConfidence = IngredientConfidence() | ||||||
|     ingredient: RecipeIngredient |     ingredient: RecipeIngredient | ||||||
| @@ -84,12 +84,12 @@ class RegisteredParser(str, enum.Enum): | |||||||
|     brute = "brute" |     brute = "brute" | ||||||
|  |  | ||||||
|  |  | ||||||
| class IngredientsRequest(CamelModel): | class IngredientsRequest(MealieModel): | ||||||
|     parser: RegisteredParser = RegisteredParser.nlp |     parser: RegisteredParser = RegisteredParser.nlp | ||||||
|     ingredients: list[str] |     ingredients: list[str] | ||||||
|  |  | ||||||
|  |  | ||||||
| class IngredientRequest(CamelModel): | class IngredientRequest(MealieModel): | ||||||
|     parser: RegisteredParser = RegisteredParser.nlp |     parser: RegisteredParser = RegisteredParser.nlp | ||||||
|     ingredient: str |     ingredient: str | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,9 +1,9 @@ | |||||||
| from typing import Optional | from typing import Optional | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
|  |  | ||||||
| class Nutrition(CamelModel): | class Nutrition(MealieModel): | ||||||
|     calories: Optional[str] |     calories: Optional[str] | ||||||
|     fat_content: Optional[str] |     fat_content: Optional[str] | ||||||
|     protein_content: Optional[str] |     protein_content: Optional[str] | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| from fastapi_camelcase import CamelModel | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
|  |  | ||||||
| class RecipeSettings(CamelModel): | class RecipeSettings(MealieModel): | ||||||
|     public: bool = False |     public: bool = False | ||||||
|     show_nutrition: bool = False |     show_nutrition: bool = False | ||||||
|     show_assets: bool = False |     show_assets: bool = False | ||||||
|   | |||||||
| @@ -1,8 +1,9 @@ | |||||||
| from datetime import datetime, timedelta | from datetime import datetime, timedelta | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import UUID4, Field | from pydantic import UUID4, Field | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
| from .recipe import Recipe | from .recipe import Recipe | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -10,7 +11,7 @@ def defaut_expires_at_time() -> datetime: | |||||||
|     return datetime.utcnow() + timedelta(days=30) |     return datetime.utcnow() + timedelta(days=30) | ||||||
|  |  | ||||||
|  |  | ||||||
| class RecipeShareTokenCreate(CamelModel): | class RecipeShareTokenCreate(MealieModel): | ||||||
|     recipe_id: UUID4 |     recipe_id: UUID4 | ||||||
|     expires_at: datetime = Field(default_factory=defaut_expires_at_time) |     expires_at: datetime = Field(default_factory=defaut_expires_at_time) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,11 +1,12 @@ | |||||||
| from typing import Optional | from typing import Optional | ||||||
| from uuid import UUID, uuid4 | from uuid import UUID, uuid4 | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import UUID4, Field | from pydantic import UUID4, Field | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
| class IngredientReferences(CamelModel): |  | ||||||
|  | class IngredientReferences(MealieModel): | ||||||
|     """ |     """ | ||||||
|     A list of ingredient references. |     A list of ingredient references. | ||||||
|     """ |     """ | ||||||
| @@ -16,7 +17,7 @@ class IngredientReferences(CamelModel): | |||||||
|         orm_mode = True |         orm_mode = True | ||||||
|  |  | ||||||
|  |  | ||||||
| class RecipeStep(CamelModel): | class RecipeStep(MealieModel): | ||||||
|     id: Optional[UUID] = Field(default_factory=uuid4) |     id: Optional[UUID] = Field(default_factory=uuid4) | ||||||
|     title: Optional[str] = "" |     title: Optional[str] = "" | ||||||
|     text: str |     text: str | ||||||
|   | |||||||
| @@ -1,10 +1,11 @@ | |||||||
| import typing | import typing | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import UUID4 | from pydantic import UUID4 | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
| class RecipeToolCreate(CamelModel): |  | ||||||
|  | class RecipeToolCreate(MealieModel): | ||||||
|     name: str |     name: str | ||||||
|     on_hand: bool = False |     on_hand: bool = False | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,10 +1,11 @@ | |||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import BaseModel | from pydantic import BaseModel | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
| # TODO: Should these exist?!?!?!?!? | # TODO: Should these exist?!?!?!?!? | ||||||
|  |  | ||||||
|  |  | ||||||
| class RecipeSlug(CamelModel): | class RecipeSlug(MealieModel): | ||||||
|     slug: str |     slug: str | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,10 +1,11 @@ | |||||||
| import datetime | import datetime | ||||||
| import enum | import enum | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import Field | from pydantic import Field | ||||||
| from pydantic.types import UUID4 | from pydantic.types import UUID4 | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
|  |  | ||||||
| class ReportCategory(str, enum.Enum): | class ReportCategory(str, enum.Enum): | ||||||
|     backup = "backup" |     backup = "backup" | ||||||
| @@ -19,7 +20,7 @@ class ReportSummaryStatus(str, enum.Enum): | |||||||
|     partial = "partial" |     partial = "partial" | ||||||
|  |  | ||||||
|  |  | ||||||
| class ReportEntryCreate(CamelModel): | class ReportEntryCreate(MealieModel): | ||||||
|     report_id: UUID4 |     report_id: UUID4 | ||||||
|     timestamp: datetime.datetime = Field(default_factory=datetime.datetime.utcnow) |     timestamp: datetime.datetime = Field(default_factory=datetime.datetime.utcnow) | ||||||
|     success: bool = True |     success: bool = True | ||||||
| @@ -34,7 +35,7 @@ class ReportEntryOut(ReportEntryCreate): | |||||||
|         orm_mode = True |         orm_mode = True | ||||||
|  |  | ||||||
|  |  | ||||||
| class ReportCreate(CamelModel): | class ReportCreate(MealieModel): | ||||||
|     timestamp: datetime.datetime = Field(default_factory=datetime.datetime.utcnow) |     timestamp: datetime.datetime = Field(default_factory=datetime.datetime.utcnow) | ||||||
|     category: ReportCategory |     category: ReportCategory | ||||||
|     group_id: UUID4 |     group_id: UUID4 | ||||||
|   | |||||||
| @@ -1,8 +1,9 @@ | |||||||
| from typing import Optional | from typing import Optional | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import BaseModel | from pydantic import BaseModel | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
|  |  | ||||||
| class ErrorResponse(BaseModel): | class ErrorResponse(BaseModel): | ||||||
|     message: str |     message: str | ||||||
| @@ -31,7 +32,7 @@ class SuccessResponse(BaseModel): | |||||||
|         return cls(message=message).dict() |         return cls(message=message).dict() | ||||||
|  |  | ||||||
|  |  | ||||||
| class FileTokenResponse(CamelModel): | class FileTokenResponse(MealieModel): | ||||||
|     file_token: str |     file_token: str | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|   | |||||||
| @@ -2,9 +2,10 @@ import datetime | |||||||
| import enum | import enum | ||||||
| from uuid import UUID | from uuid import UUID | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import Field | from pydantic import Field | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
|  |  | ||||||
| class ServerTaskNames(str, enum.Enum): | class ServerTaskNames(str, enum.Enum): | ||||||
|     default = "Background Task" |     default = "Background Task" | ||||||
| @@ -18,7 +19,7 @@ class ServerTaskStatus(str, enum.Enum): | |||||||
|     failed = "failed" |     failed = "failed" | ||||||
|  |  | ||||||
|  |  | ||||||
| class ServerTaskCreate(CamelModel): | class ServerTaskCreate(MealieModel): | ||||||
|     group_id: UUID |     group_id: UUID | ||||||
|     name: ServerTaskNames = ServerTaskNames.default |     name: ServerTaskNames = ServerTaskNames.default | ||||||
|     created_at: datetime.datetime = Field(default_factory=datetime.datetime.now) |     created_at: datetime.datetime = Field(default_factory=datetime.datetime.now) | ||||||
|   | |||||||
| @@ -1,9 +1,10 @@ | |||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import validator | from pydantic import validator | ||||||
| from pydantic.types import NoneStr, constr | from pydantic.types import NoneStr, constr | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
| class CreateUserRegistration(CamelModel): |  | ||||||
|  | class CreateUserRegistration(MealieModel): | ||||||
|     group: NoneStr = None |     group: NoneStr = None | ||||||
|     group_token: NoneStr = None |     group_token: NoneStr = None | ||||||
|     email: constr(to_lower=True, strip_whitespace=True)  # type: ignore |     email: constr(to_lower=True, strip_whitespace=True)  # type: ignore | ||||||
|   | |||||||
| @@ -3,13 +3,13 @@ from pathlib import Path | |||||||
| from typing import Any, Optional | from typing import Any, Optional | ||||||
| from uuid import UUID | from uuid import UUID | ||||||
|  |  | ||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import UUID4 | from pydantic import UUID4 | ||||||
| from pydantic.types import constr | from pydantic.types import constr | ||||||
| from pydantic.utils import GetterDict | from pydantic.utils import GetterDict | ||||||
|  |  | ||||||
| from mealie.core.config import get_app_dirs, get_app_settings | from mealie.core.config import get_app_dirs, get_app_settings | ||||||
| from mealie.db.models.users import User | from mealie.db.models.users import User | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
| from mealie.schema.group.group_preferences import ReadGroupPreferences | from mealie.schema.group.group_preferences import ReadGroupPreferences | ||||||
| from mealie.schema.recipe import RecipeSummary | from mealie.schema.recipe import RecipeSummary | ||||||
|  |  | ||||||
| @@ -18,7 +18,7 @@ from ..recipe import CategoryBase | |||||||
| settings = get_app_settings() | settings = get_app_settings() | ||||||
|  |  | ||||||
|  |  | ||||||
| class LoingLiveTokenIn(CamelModel): | class LoingLiveTokenIn(MealieModel): | ||||||
|     name: str |     name: str | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -38,19 +38,19 @@ class CreateToken(LoingLiveTokenIn): | |||||||
|         orm_mode = True |         orm_mode = True | ||||||
|  |  | ||||||
|  |  | ||||||
| class ChangePassword(CamelModel): | class ChangePassword(MealieModel): | ||||||
|     current_password: str |     current_password: str | ||||||
|     new_password: str |     new_password: str | ||||||
|  |  | ||||||
|  |  | ||||||
| class GroupBase(CamelModel): | class GroupBase(MealieModel): | ||||||
|     name: str |     name: str | ||||||
|  |  | ||||||
|     class Config: |     class Config: | ||||||
|         orm_mode = True |         orm_mode = True | ||||||
|  |  | ||||||
|  |  | ||||||
| class UserBase(CamelModel): | class UserBase(MealieModel): | ||||||
|     username: Optional[str] |     username: Optional[str] | ||||||
|     full_name: Optional[str] = None |     full_name: Optional[str] = None | ||||||
|     email: constr(to_lower=True, strip_whitespace=True)  # type: ignore |     email: constr(to_lower=True, strip_whitespace=True)  # type: ignore | ||||||
|   | |||||||
| @@ -1,14 +1,15 @@ | |||||||
| from fastapi_camelcase import CamelModel |  | ||||||
| from pydantic import UUID4 | from pydantic import UUID4 | ||||||
|  |  | ||||||
|  | from mealie.schema._mealie import MealieModel | ||||||
|  |  | ||||||
| from .user import PrivateUser | from .user import PrivateUser | ||||||
|  |  | ||||||
|  |  | ||||||
| class ForgotPassword(CamelModel): | class ForgotPassword(MealieModel): | ||||||
|     email: str |     email: str | ||||||
|  |  | ||||||
|  |  | ||||||
| class ValidateResetToken(CamelModel): | class ValidateResetToken(MealieModel): | ||||||
|     token: str |     token: str | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -18,7 +19,7 @@ class ResetPassword(ValidateResetToken): | |||||||
|     passwordConfirm: str |     passwordConfirm: str | ||||||
|  |  | ||||||
|  |  | ||||||
| class SavePasswordResetToken(CamelModel): | class SavePasswordResetToken(MealieModel): | ||||||
|     user_id: UUID4 |     user_id: UUID4 | ||||||
|     token: str |     token: str | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,143 +0,0 @@ | |||||||
| import json |  | ||||||
| import shutil |  | ||||||
| from datetime import datetime |  | ||||||
| from pathlib import Path |  | ||||||
| from typing import Union |  | ||||||
|  |  | ||||||
| from jinja2 import Template |  | ||||||
| from pathvalidate import sanitize_filename |  | ||||||
| from pydantic.main import BaseModel |  | ||||||
|  |  | ||||||
| from mealie.core import root_logger |  | ||||||
| from mealie.core.config import get_app_dirs |  | ||||||
|  |  | ||||||
| app_dirs = get_app_dirs() |  | ||||||
| from mealie.repos.all_repositories import get_repositories |  | ||||||
|  |  | ||||||
| logger = root_logger.get_logger() |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class ExportDatabase: |  | ||||||
|     def __init__(self, tag=None, templates=None) -> None: |  | ||||||
|         """Export a Mealie database. Export interacts directly with class objects and can be used |  | ||||||
|         with any supported backend database platform. By default tags are timestamps, and no |  | ||||||
|         Jinja2 templates are rendered |  | ||||||
|  |  | ||||||
|         Args: |  | ||||||
|             tag ([str], optional): A str to be used as a file tag. Defaults to None. |  | ||||||
|             templates (list, optional): A list of template file names. Defaults to None. |  | ||||||
|         """ |  | ||||||
|         if tag: |  | ||||||
|             export_tag = tag + "_" + datetime.now().strftime("%Y-%b-%d") |  | ||||||
|         else: |  | ||||||
|             export_tag = datetime.now().strftime("%Y-%b-%d") |  | ||||||
|  |  | ||||||
|         self.main_dir = app_dirs.TEMP_DIR.joinpath(export_tag) |  | ||||||
|         self.recipes = self.main_dir.joinpath("recipes") |  | ||||||
|         self.templates_dir = self.main_dir.joinpath("templates") |  | ||||||
|  |  | ||||||
|         try: |  | ||||||
|             self.templates = [app_dirs.TEMPLATE_DIR.joinpath(x) for x in templates] |  | ||||||
|         except Exception: |  | ||||||
|             self.templates = [] |  | ||||||
|             logger.info("No Jinja2 Templates Registered for Export") |  | ||||||
|  |  | ||||||
|         required_dirs = [ |  | ||||||
|             self.main_dir, |  | ||||||
|             self.recipes, |  | ||||||
|             self.templates_dir, |  | ||||||
|         ] |  | ||||||
|  |  | ||||||
|         for dir in required_dirs: |  | ||||||
|             dir.mkdir(parents=True, exist_ok=True) |  | ||||||
|  |  | ||||||
|     def export_templates(self, recipe_list: list[BaseModel]): |  | ||||||
|         if self.templates: |  | ||||||
|             for template_path in self.templates: |  | ||||||
|                 out_dir = self.templates_dir.joinpath(template_path.name) |  | ||||||
|                 out_dir.mkdir(parents=True, exist_ok=True) |  | ||||||
|  |  | ||||||
|                 with open(template_path, "r") as f: |  | ||||||
|                     template = Template(f.read()) |  | ||||||
|  |  | ||||||
|                 for recipe in recipe_list: |  | ||||||
|                     filename = recipe.slug + template_path.suffix |  | ||||||
|                     out_file = out_dir.joinpath(filename) |  | ||||||
|  |  | ||||||
|                     content = template.render(recipe=recipe) |  | ||||||
|  |  | ||||||
|                     with open(out_file, "w") as f: |  | ||||||
|                         f.write(content) |  | ||||||
|  |  | ||||||
|     def export_recipe_dirs(self): |  | ||||||
|         shutil.copytree(app_dirs.RECIPE_DATA_DIR, self.recipes, dirs_exist_ok=True) |  | ||||||
|  |  | ||||||
|     def export_items(self, items: list[BaseModel], folder_name: str, export_list=True, slug_folder=False): |  | ||||||
|         items = [x.dict() for x in items] |  | ||||||
|         out_dir = self.main_dir.joinpath(folder_name) |  | ||||||
|         out_dir.mkdir(parents=True, exist_ok=True) |  | ||||||
|  |  | ||||||
|         if export_list: |  | ||||||
|             ExportDatabase._write_json_file(items, out_dir.joinpath(f"{folder_name}.json")) |  | ||||||
|         else: |  | ||||||
|             for item in items: |  | ||||||
|                 final_dest = out_dir if not slug_folder else out_dir.joinpath(item.get("slug")) |  | ||||||
|                 final_dest.mkdir(exist_ok=True) |  | ||||||
|                 filename = sanitize_filename(f"{item.get('slug')}.json") |  | ||||||
|                 ExportDatabase._write_json_file(item, final_dest.joinpath(filename)) |  | ||||||
|  |  | ||||||
|     @staticmethod |  | ||||||
|     def _write_json_file(data: Union[dict, list], out_file: Path): |  | ||||||
|         json_data = json.dumps(data, indent=4, default=str) |  | ||||||
|  |  | ||||||
|         with open(out_file, "w") as f: |  | ||||||
|             f.write(json_data) |  | ||||||
|  |  | ||||||
|     def finish_export(self): |  | ||||||
|         zip_path = app_dirs.BACKUP_DIR.joinpath(f"{self.main_dir.name}") |  | ||||||
|         shutil.make_archive(zip_path, "zip", self.main_dir) |  | ||||||
|  |  | ||||||
|         shutil.rmtree(app_dirs.TEMP_DIR, ignore_errors=True) |  | ||||||
|         return str(zip_path.absolute()) + ".zip" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def backup_all( |  | ||||||
|     session, |  | ||||||
|     tag=None, |  | ||||||
|     templates=None, |  | ||||||
|     export_recipes=True, |  | ||||||
|     export_settings=False, |  | ||||||
|     export_users=True, |  | ||||||
|     export_groups=True, |  | ||||||
|     export_notifications=True, |  | ||||||
| ): |  | ||||||
|     db_export = ExportDatabase(tag=tag, templates=templates) |  | ||||||
|  |  | ||||||
|     db = get_repositories(session) |  | ||||||
|  |  | ||||||
|     if export_users: |  | ||||||
|         all_users = db.users.get_all() |  | ||||||
|         db_export.export_items(all_users, "users") |  | ||||||
|  |  | ||||||
|     if export_groups: |  | ||||||
|         all_groups = db.groups.get_all() |  | ||||||
|         db_export.export_items(all_groups, "groups") |  | ||||||
|  |  | ||||||
|     if export_recipes: |  | ||||||
|         all_recipes = db.recipes.get_all() |  | ||||||
|         db_export.export_recipe_dirs() |  | ||||||
|         db_export.export_items(all_recipes, "recipes", export_list=False, slug_folder=True) |  | ||||||
|         db_export.export_templates(all_recipes) |  | ||||||
|  |  | ||||||
|         all_comments = db.comments.get_all() |  | ||||||
|         db_export.export_items(all_comments, "comments") |  | ||||||
|  |  | ||||||
|     if export_settings: |  | ||||||
|         all_settings = db.settings.get_all() |  | ||||||
|         db_export.export_items(all_settings, "settings") |  | ||||||
|  |  | ||||||
|     if export_notifications: |  | ||||||
|         all_notifications = db.event_notifications.get_all() |  | ||||||
|         db_export.export_items(all_notifications, "notifications") |  | ||||||
|  |  | ||||||
|     return db_export.finish_export() |  | ||||||
| @@ -1,307 +0,0 @@ | |||||||
| import json |  | ||||||
| import shutil |  | ||||||
| import zipfile |  | ||||||
| from collections.abc import Callable |  | ||||||
| from pathlib import Path |  | ||||||
|  |  | ||||||
| from pydantic.main import BaseModel |  | ||||||
| from sqlalchemy.orm.session import Session |  | ||||||
|  |  | ||||||
| from mealie.core.config import get_app_dirs |  | ||||||
| from mealie.repos.all_repositories import get_repositories |  | ||||||
| from mealie.schema.admin import CommentImport, GroupImport, RecipeImport, UserImport |  | ||||||
| from mealie.schema.recipe import Recipe, RecipeCommentOut |  | ||||||
| from mealie.schema.user import PrivateUser, UpdateGroup |  | ||||||
|  |  | ||||||
| app_dirs = get_app_dirs() |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class ImportDatabase: |  | ||||||
|     def __init__( |  | ||||||
|         self, |  | ||||||
|         user: PrivateUser, |  | ||||||
|         session: Session, |  | ||||||
|         zip_archive: str, |  | ||||||
|         force_import: bool = False, |  | ||||||
|     ) -> None: |  | ||||||
|         """Import a database.zip file exported from mealie. |  | ||||||
|  |  | ||||||
|         Args: |  | ||||||
|             session (Session): SqlAlchemy Session |  | ||||||
|             zip_archive (str): The filename contained in the backups directory |  | ||||||
|             force_import (bool, optional): Force import will update all existing recipes. If False existing recipes are skipped. Defaults to False. |  | ||||||
|  |  | ||||||
|         Raises: |  | ||||||
|             Exception: If the zip file does not exists an exception raise. |  | ||||||
|         """ |  | ||||||
|         self.user = user |  | ||||||
|         self.session = session |  | ||||||
|         self.db = get_repositories(session) |  | ||||||
|         self.archive = app_dirs.BACKUP_DIR.joinpath(zip_archive) |  | ||||||
|         self.force_imports = force_import |  | ||||||
|  |  | ||||||
|         if self.archive.is_file(): |  | ||||||
|             self.import_dir = app_dirs.TEMP_DIR.joinpath("active_import") |  | ||||||
|             self.import_dir.mkdir(parents=True, exist_ok=True) |  | ||||||
|  |  | ||||||
|             with zipfile.ZipFile(self.archive, "r") as zip_ref: |  | ||||||
|                 zip_ref.extractall(self.import_dir) |  | ||||||
|         else: |  | ||||||
|             raise Exception("Import file does not exist") |  | ||||||
|  |  | ||||||
|     def import_recipes(self): |  | ||||||
|         recipe_dir: Path = self.import_dir.joinpath("recipes") |  | ||||||
|         imports = [] |  | ||||||
|         successful_imports = {} |  | ||||||
|  |  | ||||||
|         recipes = ImportDatabase.read_models_file( |  | ||||||
|             file_path=recipe_dir, |  | ||||||
|             model=Recipe, |  | ||||||
|             single_file=False, |  | ||||||
|             migrate=ImportDatabase._recipe_migration, |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         for recipe in recipes: |  | ||||||
|             recipe: Recipe |  | ||||||
|  |  | ||||||
|             recipe.group_id = self.user.group_id |  | ||||||
|             recipe.user_id = self.user.id |  | ||||||
|  |  | ||||||
|             import_status = self.import_model( |  | ||||||
|                 db_table=self.db.recipes, |  | ||||||
|                 model=recipe, |  | ||||||
|                 return_model=RecipeImport, |  | ||||||
|                 name_attr="name", |  | ||||||
|                 search_key="slug", |  | ||||||
|                 slug=recipe.slug, |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|             if import_status.status: |  | ||||||
|                 successful_imports[recipe.slug] = recipe |  | ||||||
|  |  | ||||||
|             imports.append(import_status) |  | ||||||
|  |  | ||||||
|         self._import_images(successful_imports) |  | ||||||
|  |  | ||||||
|         return imports |  | ||||||
|  |  | ||||||
|     def import_comments(self): |  | ||||||
|         comment_dir: Path = self.import_dir.joinpath("comments", "comments.json") |  | ||||||
|  |  | ||||||
|         if not comment_dir.exists(): |  | ||||||
|             return |  | ||||||
|  |  | ||||||
|         comments = ImportDatabase.read_models_file(file_path=comment_dir, model=RecipeCommentOut) |  | ||||||
|  |  | ||||||
|         for comment in comments: |  | ||||||
|             comment: RecipeCommentOut |  | ||||||
|  |  | ||||||
|             self.import_model( |  | ||||||
|                 db_table=self.db.comments, |  | ||||||
|                 model=comment, |  | ||||||
|                 return_model=CommentImport, |  | ||||||
|                 name_attr="uuid", |  | ||||||
|                 search_key="uuid", |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|     @staticmethod |  | ||||||
|     def _recipe_migration(recipe_dict: dict) -> dict: |  | ||||||
|         if recipe_dict.get("categories", False): |  | ||||||
|             recipe_dict["recipeCategory"] = recipe_dict.get("categories") |  | ||||||
|             del recipe_dict["categories"] |  | ||||||
|         try: |  | ||||||
|             del recipe_dict["_id"] |  | ||||||
|             del recipe_dict["date_added"] |  | ||||||
|         except Exception: |  | ||||||
|             pass |  | ||||||
|         # Migration from list to Object Type Data |  | ||||||
|         try: |  | ||||||
|             if "" in recipe_dict["tags"]: |  | ||||||
|                 recipe_dict["tags"] = [tag for tag in recipe_dict["tags"] if tag != ""] |  | ||||||
|         except Exception: |  | ||||||
|             pass |  | ||||||
|  |  | ||||||
|         try: |  | ||||||
|             if "" in recipe_dict["categories"]: |  | ||||||
|                 recipe_dict["categories"] = [cat for cat in recipe_dict["categories"] if cat != ""] |  | ||||||
|  |  | ||||||
|         except Exception: |  | ||||||
|             pass |  | ||||||
|  |  | ||||||
|         if type(recipe_dict["extras"]) == list: |  | ||||||
|             recipe_dict["extras"] = {} |  | ||||||
|  |  | ||||||
|         recipe_dict["comments"] = [] |  | ||||||
|  |  | ||||||
|         return recipe_dict |  | ||||||
|  |  | ||||||
|     def _import_images(self, successful_imports: list[Recipe]): |  | ||||||
|         image_dir = self.import_dir.joinpath("images") |  | ||||||
|  |  | ||||||
|         if image_dir.exists():  # Migrate from before v0.5.0 |  | ||||||
|             for image in image_dir.iterdir(): |  | ||||||
|                 item: Recipe = successful_imports.get(image.stem)  # type: ignore |  | ||||||
|  |  | ||||||
|                 if item: |  | ||||||
|                     dest_dir = item.image_dir |  | ||||||
|  |  | ||||||
|                     if image.is_dir(): |  | ||||||
|                         shutil.copytree(image, dest_dir, dirs_exist_ok=True) |  | ||||||
|  |  | ||||||
|                     if image.is_file(): |  | ||||||
|                         shutil.copy(image, dest_dir) |  | ||||||
|  |  | ||||||
|         else: |  | ||||||
|             recipe_dir = self.import_dir.joinpath("recipes") |  | ||||||
|             shutil.copytree(recipe_dir, app_dirs.RECIPE_DATA_DIR, dirs_exist_ok=True) |  | ||||||
|  |  | ||||||
|     def import_settings(self): |  | ||||||
|         return [] |  | ||||||
|  |  | ||||||
|     def import_groups(self): |  | ||||||
|         groups_file = self.import_dir.joinpath("groups", "groups.json") |  | ||||||
|         groups = ImportDatabase.read_models_file(groups_file, UpdateGroup) |  | ||||||
|         group_imports = [] |  | ||||||
|  |  | ||||||
|         for group in groups: |  | ||||||
|             import_status = self.import_model(self.db.groups, group, GroupImport, search_key="name") |  | ||||||
|             group_imports.append(import_status) |  | ||||||
|  |  | ||||||
|         return group_imports |  | ||||||
|  |  | ||||||
|     def import_users(self): |  | ||||||
|         users_file = self.import_dir.joinpath("users", "users.json") |  | ||||||
|         users = ImportDatabase.read_models_file(users_file, PrivateUser) |  | ||||||
|         user_imports = [] |  | ||||||
|         for user in users: |  | ||||||
|             if user.id == 1:  # Update Default User |  | ||||||
|                 self.db.users.update(1, user.dict()) |  | ||||||
|                 import_status = UserImport(name=user.full_name, status=True) |  | ||||||
|                 user_imports.append(import_status) |  | ||||||
|                 continue |  | ||||||
|  |  | ||||||
|             import_status = self.import_model( |  | ||||||
|                 db_table=self.db.users, |  | ||||||
|                 model=user, |  | ||||||
|                 return_model=UserImport, |  | ||||||
|                 name_attr="full_name", |  | ||||||
|                 search_key="email", |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|             user_imports.append(import_status) |  | ||||||
|  |  | ||||||
|         return user_imports |  | ||||||
|  |  | ||||||
|     @staticmethod |  | ||||||
|     def read_models_file(file_path: Path, model: BaseModel, single_file=True, migrate: Callable = None): |  | ||||||
|         """A general purpose function that is used to process a backup `.json` file created by mealie |  | ||||||
|         note that if the file doesn't not exists the function will return any empty list |  | ||||||
|  |  | ||||||
|         Args: |  | ||||||
|             file_path (Path): The path to the .json file or directory |  | ||||||
|             model (BaseModel): The pydantic model that will be created from the .json file entries |  | ||||||
|             single_file (bool, optional): If true, the json data will be treated as list, if false it will use glob style matches and treat each file as its own entry. Defaults to True. |  | ||||||
|             migrate (Callable, optional): A migrate function that will be called on the data prior to creating a model. Defaults to None. |  | ||||||
|  |  | ||||||
|         Returns: |  | ||||||
|             [type]: [description] |  | ||||||
|         """ |  | ||||||
|         if not file_path.exists(): |  | ||||||
|             return [] |  | ||||||
|  |  | ||||||
|         if single_file: |  | ||||||
|             with open(file_path, "r") as f: |  | ||||||
|                 file_data = json.loads(f.read()) |  | ||||||
|  |  | ||||||
|             if migrate: |  | ||||||
|                 file_data = [migrate(x) for x in file_data] |  | ||||||
|  |  | ||||||
|             return [model(**g) for g in file_data] |  | ||||||
|  |  | ||||||
|         all_models = [] |  | ||||||
|         for file in file_path.glob("**/*.json"): |  | ||||||
|             with open(file, "r") as f: |  | ||||||
|                 file_data = json.loads(f.read()) |  | ||||||
|  |  | ||||||
|             if migrate: |  | ||||||
|                 file_data = migrate(file_data) |  | ||||||
|  |  | ||||||
|             all_models.append(model(**file_data)) |  | ||||||
|  |  | ||||||
|         return all_models |  | ||||||
|  |  | ||||||
|     def import_model(self, db_table, model, return_model, name_attr="name", search_key="id", **kwargs): |  | ||||||
|         """A general purpose function used to insert a list of pydantic modelsi into the database. |  | ||||||
|         The assumption at this point is that the models that are inserted. If self.force_imports is true |  | ||||||
|         any existing entries will be removed prior to creation |  | ||||||
|  |  | ||||||
|         Args: |  | ||||||
|             db_table ([type]): A database table like `db.users` |  | ||||||
|             model ([type]): The Pydantic model that matches the database |  | ||||||
|             return_model ([type]): The return model that will be used for the 'report' |  | ||||||
|             name_attr (str, optional): The name property on the return model. Defaults to "name". |  | ||||||
|             search_key (str, optional): The key used to identify if an the entry already exists. Defaults to "id" |  | ||||||
|             **kwargs (): Any kwargs passed will be used to set attributes on the `return_model` |  | ||||||
|  |  | ||||||
|         Returns: |  | ||||||
|             [type]: Returns the `return_model` specified. |  | ||||||
|         """ |  | ||||||
|         model_name = getattr(model, name_attr) |  | ||||||
|         search_value = getattr(model, search_key) |  | ||||||
|  |  | ||||||
|         item = db_table.get(search_value, search_key) |  | ||||||
|         if item: |  | ||||||
|             if not self.force_imports: |  | ||||||
|                 return return_model( |  | ||||||
|                     name=model_name, |  | ||||||
|                     status=False, |  | ||||||
|                     exception=f"Table entry with matching '{search_key}': '{search_value}' exists", |  | ||||||
|                 ) |  | ||||||
|  |  | ||||||
|             primary_key = getattr(item, db_table.primary_key) |  | ||||||
|             db_table.delete(primary_key) |  | ||||||
|         try: |  | ||||||
|             db_table.create(model.dict()) |  | ||||||
|             import_status = return_model(name=model_name, status=True) |  | ||||||
|  |  | ||||||
|         except Exception as inst: |  | ||||||
|             self.session.rollback() |  | ||||||
|             import_status = return_model(name=model_name, status=False, exception=str(inst)) |  | ||||||
|  |  | ||||||
|         for key, value in kwargs.items(): |  | ||||||
|             setattr(return_model, key, value) |  | ||||||
|  |  | ||||||
|         return import_status |  | ||||||
|  |  | ||||||
|     def clean_up(self): |  | ||||||
|         shutil.rmtree(app_dirs.TEMP_DIR) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def import_database( |  | ||||||
|     session: Session, |  | ||||||
|     user: PrivateUser, |  | ||||||
|     archive, |  | ||||||
|     import_recipes=True, |  | ||||||
|     import_settings=True, |  | ||||||
|     import_users=True, |  | ||||||
|     import_groups=True, |  | ||||||
|     force_import: bool = False, |  | ||||||
|     **_, |  | ||||||
| ): |  | ||||||
|     import_session = ImportDatabase(user, session, archive, force_import) |  | ||||||
|  |  | ||||||
|     recipe_report = import_session.import_recipes() if import_recipes else [] |  | ||||||
|     settings_report = import_session.import_settings() if import_settings else [] |  | ||||||
|     group_report = import_session.import_groups() if import_groups else [] |  | ||||||
|     user_report = import_session.import_users() if import_users else [] |  | ||||||
|     notification_report: list = [] |  | ||||||
|  |  | ||||||
|     import_session.clean_up() |  | ||||||
|  |  | ||||||
|     return { |  | ||||||
|         "recipeImports": recipe_report, |  | ||||||
|         "settingsImports": settings_report, |  | ||||||
|         "groupImports": group_report, |  | ||||||
|         "userImports": user_report, |  | ||||||
|         "notificationImports": notification_report, |  | ||||||
|     } |  | ||||||
| @@ -1,4 +1,3 @@ | |||||||
| from .auto_backup import * |  | ||||||
| from .purge_group_exports import * | from .purge_group_exports import * | ||||||
| from .purge_password_reset import * | from .purge_password_reset import * | ||||||
| from .purge_registration import * | from .purge_registration import * | ||||||
|   | |||||||
| @@ -1,20 +0,0 @@ | |||||||
| from mealie.core import root_logger |  | ||||||
| from mealie.core.config import get_app_dirs |  | ||||||
|  |  | ||||||
| app_dirs = get_app_dirs() |  | ||||||
| from mealie.db.db_setup import create_session |  | ||||||
| from mealie.services.backups.exports import backup_all |  | ||||||
|  |  | ||||||
| logger = root_logger.get_logger() |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def auto_backup(): |  | ||||||
|     for backup in app_dirs.BACKUP_DIR.glob("Auto*.zip"): |  | ||||||
|         backup.unlink() |  | ||||||
|  |  | ||||||
|     templates = [template for template in app_dirs.TEMPLATE_DIR.iterdir()] |  | ||||||
|     session = create_session() |  | ||||||
|     backup_all(session=session, tag="Auto", templates=templates) |  | ||||||
|     logger.info("generating automated backup") |  | ||||||
|     session.close() |  | ||||||
|     logger.info("automated backup generated") |  | ||||||
							
								
								
									
										38
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										38
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							| @@ -390,18 +390,6 @@ dev = ["python-jose[cryptography] (>=3.3.0,<4.0.0)", "passlib[bcrypt] (>=1.7.2,< | |||||||
| doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "typer-cli (>=0.0.12,<0.0.13)", "pyyaml (>=5.3.1,<6.0.0)"] | doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "typer-cli (>=0.0.12,<0.0.13)", "pyyaml (>=5.3.1,<6.0.0)"] | ||||||
| test = ["pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "mypy (==0.910)", "flake8 (>=3.8.3,<4.0.0)", "black (==21.9b0)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "orjson (>=3.2.1,<4.0.0)", "ujson (>=4.0.1,<5.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "flask (>=1.1.2,<3.0.0)", "anyio[trio] (>=3.2.1,<4.0.0)", "types-ujson (==0.1.1)", "types-orjson (==3.6.0)", "types-dataclasses (==0.1.7)"] | test = ["pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "mypy (==0.910)", "flake8 (>=3.8.3,<4.0.0)", "black (==21.9b0)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "orjson (>=3.2.1,<4.0.0)", "ujson (>=4.0.1,<5.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "flask (>=1.1.2,<3.0.0)", "anyio[trio] (>=3.2.1,<4.0.0)", "types-ujson (==0.1.1)", "types-orjson (==3.6.0)", "types-dataclasses (==0.1.7)"] | ||||||
|  |  | ||||||
| [[package]] |  | ||||||
| name = "fastapi-camelcase" |  | ||||||
| version = "1.0.5" |  | ||||||
| description = "Package provides an easy way to have camelcase request/response bodies for Pydantic" |  | ||||||
| category = "main" |  | ||||||
| optional = false |  | ||||||
| python-versions = ">=3.6" |  | ||||||
|  |  | ||||||
| [package.dependencies] |  | ||||||
| pydantic = "*" |  | ||||||
| pyhumps = "*" |  | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "filelock" | name = "filelock" | ||||||
| version = "3.6.0" | version = "3.6.0" | ||||||
| @@ -829,17 +817,6 @@ category = "dev" | |||||||
| optional = false | optional = false | ||||||
| python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" | ||||||
|  |  | ||||||
| [[package]] |  | ||||||
| name = "pathvalidate" |  | ||||||
| version = "2.5.0" |  | ||||||
| description = "pathvalidate is a Python library to sanitize/validate a string such as filenames/file-paths/etc." |  | ||||||
| category = "main" |  | ||||||
| optional = false |  | ||||||
| python-versions = ">=3.6" |  | ||||||
|  |  | ||||||
| [package.extras] |  | ||||||
| test = ["allpairspy", "click", "faker", "pytest (>=6.0.1)", "pytest-discord (>=0.0.6)", "pytest-md-report (>=0.0.12)"] |  | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "pillow" | name = "pillow" | ||||||
| version = "8.4.0" | version = "8.4.0" | ||||||
| @@ -1003,7 +980,7 @@ python-versions = ">=3.5" | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "pyhumps" | name = "pyhumps" | ||||||
| version = "3.5.0" | version = "3.5.3" | ||||||
| description = "🐫  Convert strings (and dictionary keys) between snake case, camel case and pascal case in Python. Inspired by Humps for Node" | description = "🐫  Convert strings (and dictionary keys) between snake case, camel case and pascal case in Python. Inspired by Humps for Node" | ||||||
| category = "main" | category = "main" | ||||||
| optional = false | optional = false | ||||||
| @@ -1622,7 +1599,7 @@ pgsql = ["psycopg2-binary"] | |||||||
| [metadata] | [metadata] | ||||||
| lock-version = "1.1" | lock-version = "1.1" | ||||||
| python-versions = "^3.10" | python-versions = "^3.10" | ||||||
| content-hash = "4fba071019a62f5d75e7c9a297a7815b2fed6486bb3616b5029a6fb08001761f" | content-hash = "84c1d9352c058da5cc0f50ca195cbe0897ce64abfbe01d08b9da317b6dd70a70" | ||||||
|  |  | ||||||
| [metadata.files] | [metadata.files] | ||||||
| aiofiles = [ | aiofiles = [ | ||||||
| @@ -1852,9 +1829,6 @@ fastapi = [ | |||||||
|     {file = "fastapi-0.74.1-py3-none-any.whl", hash = "sha256:b8ec8400623ef0b2ff558ebe06753b349f8e3a5dd38afea650800f2644ddba34"}, |     {file = "fastapi-0.74.1-py3-none-any.whl", hash = "sha256:b8ec8400623ef0b2ff558ebe06753b349f8e3a5dd38afea650800f2644ddba34"}, | ||||||
|     {file = "fastapi-0.74.1.tar.gz", hash = "sha256:b58a2c46df14f62ebe6f24a9439927539ba1959b9be55ba0e2f516a683e5b9d4"}, |     {file = "fastapi-0.74.1.tar.gz", hash = "sha256:b58a2c46df14f62ebe6f24a9439927539ba1959b9be55ba0e2f516a683e5b9d4"}, | ||||||
| ] | ] | ||||||
| fastapi-camelcase = [ |  | ||||||
|     {file = "fastapi_camelcase-1.0.5.tar.gz", hash = "sha256:2cee005fb1b75649491b9f7cfccc640b12f028eb88084565f7d8cf415192026a"}, |  | ||||||
| ] |  | ||||||
| filelock = [ | filelock = [ | ||||||
|     {file = "filelock-3.6.0-py3-none-any.whl", hash = "sha256:f8314284bfffbdcfa0ff3d7992b023d4c628ced6feb957351d4c48d059f56bc0"}, |     {file = "filelock-3.6.0-py3-none-any.whl", hash = "sha256:f8314284bfffbdcfa0ff3d7992b023d4c628ced6feb957351d4c48d059f56bc0"}, | ||||||
|     {file = "filelock-3.6.0.tar.gz", hash = "sha256:9cd540a9352e432c7246a48fe4e8712b10acb1df2ad1f30e8c070b82ae1fed85"}, |     {file = "filelock-3.6.0.tar.gz", hash = "sha256:9cd540a9352e432c7246a48fe4e8712b10acb1df2ad1f30e8c070b82ae1fed85"}, | ||||||
| @@ -2239,10 +2213,6 @@ pathspec = [ | |||||||
|     {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, |     {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, | ||||||
|     {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, |     {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, | ||||||
| ] | ] | ||||||
| pathvalidate = [ |  | ||||||
|     {file = "pathvalidate-2.5.0-py3-none-any.whl", hash = "sha256:e5b2747ad557363e8f4124f0553d68878b12ecabd77bcca7e7312d5346d20262"}, |  | ||||||
|     {file = "pathvalidate-2.5.0.tar.gz", hash = "sha256:119ba36be7e9a405d704c7b7aea4b871c757c53c9adc0ed64f40be1ed8da2781"}, |  | ||||||
| ] |  | ||||||
| pillow = [ | pillow = [ | ||||||
|     {file = "Pillow-8.4.0-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:81f8d5c81e483a9442d72d182e1fb6dcb9723f289a57e8030811bac9ea3fef8d"}, |     {file = "Pillow-8.4.0-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:81f8d5c81e483a9442d72d182e1fb6dcb9723f289a57e8030811bac9ea3fef8d"}, | ||||||
|     {file = "Pillow-8.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3f97cfb1e5a392d75dd8b9fd274d205404729923840ca94ca45a0af57e13dbe6"}, |     {file = "Pillow-8.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3f97cfb1e5a392d75dd8b9fd274d205404729923840ca94ca45a0af57e13dbe6"}, | ||||||
| @@ -2453,8 +2423,8 @@ pygments = [ | |||||||
|     {file = "Pygments-2.11.2.tar.gz", hash = "sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a"}, |     {file = "Pygments-2.11.2.tar.gz", hash = "sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a"}, | ||||||
| ] | ] | ||||||
| pyhumps = [ | pyhumps = [ | ||||||
|     {file = "pyhumps-3.5.0-py3-none-any.whl", hash = "sha256:2433eef13d1c258227a0bd5de9660ba17dd6a307e1255d2d20ec9287f8626d96"}, |     {file = "pyhumps-3.5.3-py3-none-any.whl", hash = "sha256:8d7e9865d6ddb6e64a2e97d951b78b5cc827d3d66cda1297310fc83b2ddf51dc"}, | ||||||
|     {file = "pyhumps-3.5.0.tar.gz", hash = "sha256:55e37f16846eaab26057200924cbdadd2152bf0a5d49175a42358464fa881c73"}, |     {file = "pyhumps-3.5.3.tar.gz", hash = "sha256:0ecf7fee84503b45afdd3841ec769b529d32dfaed855e07046ff8babcc0ab831"}, | ||||||
| ] | ] | ||||||
| pylint = [ | pylint = [ | ||||||
|     {file = "pylint-2.12.2-py3-none-any.whl", hash = "sha256:daabda3f7ed9d1c60f52d563b1b854632fd90035bcf01443e234d3dc794e3b74"}, |     {file = "pylint-2.12.2-py3-none-any.whl", hash = "sha256:daabda3f7ed9d1c60f52d563b1b854632fd90035bcf01443e234d3dc794e3b74"}, | ||||||
|   | |||||||
| @@ -25,13 +25,11 @@ requests = "^2.25.1" | |||||||
| PyYAML = "^5.3.1" | PyYAML = "^5.3.1" | ||||||
| extruct = "^0.13.0" | extruct = "^0.13.0" | ||||||
| python-multipart = "^0.0.5" | python-multipart = "^0.0.5" | ||||||
| fastapi-camelcase = "^1.0.5" |  | ||||||
| bcrypt = "^3.2.0" | bcrypt = "^3.2.0" | ||||||
| python-jose = "^3.3.0" | python-jose = "^3.3.0" | ||||||
| passlib = "^1.7.4" | passlib = "^1.7.4" | ||||||
| lxml = "^4.7.1" | lxml = "^4.7.1" | ||||||
| Pillow = "^8.2.0" | Pillow = "^8.2.0" | ||||||
| pathvalidate = "^2.4.1" |  | ||||||
| apprise = "^0.9.6" | apprise = "^0.9.6" | ||||||
| recipe-scrapers = "^13.18.1" | recipe-scrapers = "^13.18.1" | ||||||
| psycopg2-binary = {version = "^2.9.1", optional = true} | psycopg2-binary = {version = "^2.9.1", optional = true} | ||||||
| @@ -41,6 +39,7 @@ python-i18n = "^0.3.9" | |||||||
| python-ldap = "^3.3.1" | python-ldap = "^3.3.1" | ||||||
| pydantic = "^1.9.0" | pydantic = "^1.9.0" | ||||||
| tzdata = "^2021.5" | tzdata = "^2021.5" | ||||||
|  | pyhumps = "^3.5.3" | ||||||
|  |  | ||||||
| [tool.poetry.dev-dependencies] | [tool.poetry.dev-dependencies] | ||||||
| pylint = "^2.6.0" | pylint = "^2.6.0" | ||||||
|   | |||||||
							
								
								
									
										63
									
								
								tests/unit_tests/schema_tests/test_mealie_model.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								tests/unit_tests/schema_tests/test_mealie_model.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | |||||||
|  | from mealie.schema._mealie.mealie_model import MealieModel | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestModel(MealieModel): | ||||||
|  |     long_name: str | ||||||
|  |     long_int: int | ||||||
|  |     long_float: float | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestModel2(MealieModel): | ||||||
|  |     long_name: str | ||||||
|  |     long_int: int | ||||||
|  |     long_float: float | ||||||
|  |     another_str: str | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_camelize_variables(): | ||||||
|  |     model = TestModel(long_name="Hello", long_int=1, long_float=1.1) | ||||||
|  |  | ||||||
|  |     as_dict = model.dict(by_alias=True) | ||||||
|  |  | ||||||
|  |     assert as_dict["longName"] == "Hello" | ||||||
|  |     assert as_dict["longInt"] == 1 | ||||||
|  |     assert as_dict["longFloat"] == 1.1 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_cast_to(): | ||||||
|  |  | ||||||
|  |     model = TestModel(long_name="Hello", long_int=1, long_float=1.1) | ||||||
|  |  | ||||||
|  |     model2 = model.cast(TestModel2, another_str="World") | ||||||
|  |  | ||||||
|  |     assert model2.long_name == "Hello" | ||||||
|  |     assert model2.long_int == 1 | ||||||
|  |     assert model2.long_float == 1.1 | ||||||
|  |     assert model2.another_str == "World" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_map_to(): | ||||||
|  |  | ||||||
|  |     model = TestModel(long_name="Model1", long_int=100, long_float=1.5) | ||||||
|  |  | ||||||
|  |     model2 = TestModel2(long_name="Model2", long_int=1, long_float=1.1, another_str="World") | ||||||
|  |  | ||||||
|  |     model.map_to(model2) | ||||||
|  |  | ||||||
|  |     assert model2.long_name == "Model1" | ||||||
|  |     assert model2.long_int == 100 | ||||||
|  |     assert model2.long_float == 1.5 | ||||||
|  |     assert model2.another_str == "World" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_map_from(): | ||||||
|  |     model = TestModel(long_name="Model1", long_int=50, long_float=1.5) | ||||||
|  |  | ||||||
|  |     model2 = TestModel2(long_name="Hello", long_int=1, long_float=1.1, another_str="World") | ||||||
|  |  | ||||||
|  |     model2.map_from(model) | ||||||
|  |  | ||||||
|  |     assert model2.long_name == "Model1" | ||||||
|  |     assert model2.long_int == 50 | ||||||
|  |     assert model2.long_float == 1.5 | ||||||
|  |     assert model2.another_str == "World" | ||||||
		Reference in New Issue
	
	Block a user