chore: initial import
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
|
||||
|
||||
class WarningItem(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
severity: Literal["info", "warning", "blocking"]
|
||||
code: str
|
||||
message: str
|
||||
|
||||
|
||||
class IngredientSpec(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
food_name: str = Field(min_length=1, max_length=200)
|
||||
preferred_food_id: int | None = Field(default=None, ge=1)
|
||||
create_food: bool = False
|
||||
food_plural_name: str | None = Field(default=None, max_length=200)
|
||||
|
||||
amount: float = Field(ge=0)
|
||||
amount_max: float | None = Field(default=None, ge=0)
|
||||
|
||||
unit_name: str | None = Field(default=None, max_length=80)
|
||||
preferred_unit_id: int | None = Field(default=None, ge=1)
|
||||
create_unit: bool = False
|
||||
unit_plural_name: str | None = Field(default=None, max_length=80)
|
||||
|
||||
note: str = Field(default="", max_length=500)
|
||||
no_amount: bool = False
|
||||
original_text: str = Field(default="", max_length=1000)
|
||||
|
||||
@model_validator(mode="after")
|
||||
def validate_values(self) -> "IngredientSpec":
|
||||
if self.amount_max is not None and self.amount_max < self.amount:
|
||||
raise ValueError("amount_max darf nicht kleiner als amount sein.")
|
||||
if self.no_amount and self.amount != 0:
|
||||
raise ValueError("Bei no_amount=true muss amount 0 sein.")
|
||||
if self.create_food and self.preferred_food_id is not None:
|
||||
raise ValueError(
|
||||
"create_food und preferred_food_id dürfen nicht gleichzeitig gesetzt sein."
|
||||
)
|
||||
if self.create_unit and self.preferred_unit_id is not None:
|
||||
raise ValueError(
|
||||
"create_unit und preferred_unit_id dürfen nicht gleichzeitig gesetzt sein."
|
||||
)
|
||||
if self.create_unit and not self.unit_name:
|
||||
raise ValueError("create_unit benötigt unit_name.")
|
||||
return self
|
||||
|
||||
|
||||
class StepSpec(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
name: str = Field(min_length=1, max_length=200)
|
||||
instruction: str = Field(min_length=1, max_length=12000)
|
||||
time: int = Field(default=0, ge=0, le=10080)
|
||||
ingredients: list[IngredientSpec]
|
||||
|
||||
|
||||
class RecipeSpec(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
schema_version: Literal[1] = 1
|
||||
name: str = Field(min_length=1, max_length=250)
|
||||
description: str = Field(default="", max_length=2000)
|
||||
source_url: str = Field(min_length=1, max_length=2000)
|
||||
image_url: str | None = Field(default=None, max_length=2000)
|
||||
servings: float = Field(default=1, gt=0, le=10000)
|
||||
servings_text: str = Field(default="", max_length=100)
|
||||
working_time: int = Field(default=0, ge=0, le=10080)
|
||||
waiting_time: int = Field(default=0, ge=0, le=10080)
|
||||
keywords: list[str] = Field(default_factory=list, max_length=30)
|
||||
confidence: Literal["high", "medium", "low"]
|
||||
warnings: list[WarningItem] = Field(default_factory=list, max_length=50)
|
||||
steps: list[StepSpec] = Field(min_length=1, max_length=50)
|
||||
|
||||
|
||||
class AnalyzeRequest(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
url: str = Field(min_length=3, max_length=2000)
|
||||
|
||||
|
||||
class RecipeRequest(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
recipe: RecipeSpec
|
||||
|
||||
|
||||
class ImportRequest(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
recipe: RecipeSpec
|
||||
import_image: bool = True
|
||||
force_duplicate: bool = False
|
||||
|
||||
|
||||
RECIPE_JSON_SCHEMA = {
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
"required": [
|
||||
"schema_version",
|
||||
"name",
|
||||
"description",
|
||||
"source_url",
|
||||
"image_url",
|
||||
"servings",
|
||||
"servings_text",
|
||||
"working_time",
|
||||
"waiting_time",
|
||||
"keywords",
|
||||
"confidence",
|
||||
"warnings",
|
||||
"steps",
|
||||
],
|
||||
"properties": {
|
||||
"schema_version": {"type": "integer", "enum": [1]},
|
||||
"name": {"type": "string"},
|
||||
"description": {"type": "string"},
|
||||
"source_url": {"type": "string"},
|
||||
"image_url": {"type": ["string", "null"]},
|
||||
"servings": {"type": "number"},
|
||||
"servings_text": {"type": "string"},
|
||||
"working_time": {"type": "integer"},
|
||||
"waiting_time": {"type": "integer"},
|
||||
"keywords": {"type": "array", "items": {"type": "string"}},
|
||||
"confidence": {"type": "string", "enum": ["high", "medium", "low"]},
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
"required": ["severity", "code", "message"],
|
||||
"properties": {
|
||||
"severity": {
|
||||
"type": "string",
|
||||
"enum": ["info", "warning", "blocking"],
|
||||
},
|
||||
"code": {"type": "string"},
|
||||
"message": {"type": "string"},
|
||||
},
|
||||
},
|
||||
},
|
||||
"steps": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
"required": ["name", "instruction", "time", "ingredients"],
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"instruction": {"type": "string"},
|
||||
"time": {"type": "integer"},
|
||||
"ingredients": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
"required": [
|
||||
"food_name",
|
||||
"preferred_food_id",
|
||||
"create_food",
|
||||
"food_plural_name",
|
||||
"amount",
|
||||
"amount_max",
|
||||
"unit_name",
|
||||
"preferred_unit_id",
|
||||
"create_unit",
|
||||
"unit_plural_name",
|
||||
"note",
|
||||
"no_amount",
|
||||
"original_text",
|
||||
],
|
||||
"properties": {
|
||||
"food_name": {"type": "string"},
|
||||
"preferred_food_id": {"type": ["integer", "null"]},
|
||||
"create_food": {"type": "boolean"},
|
||||
"food_plural_name": {"type": ["string", "null"]},
|
||||
"amount": {"type": "number"},
|
||||
"amount_max": {"type": ["number", "null"]},
|
||||
"unit_name": {"type": ["string", "null"]},
|
||||
"preferred_unit_id": {"type": ["integer", "null"]},
|
||||
"create_unit": {"type": "boolean"},
|
||||
"unit_plural_name": {"type": ["string", "null"]},
|
||||
"note": {"type": "string"},
|
||||
"no_amount": {"type": "boolean"},
|
||||
"original_text": {"type": "string"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user