feat: Recipe Timeline Images (#2444)

* refactored recipe image paths/service

* added routes for updating/fetching timeline images

* make generate

* added event image upload and rendering

* switched update to patch to preserve timestamp

* added tests

* tweaked order of requests

* always reload events when opening the timeline

* re-arranged elements to make them look nicer

* delete files when timeline event is deleted
This commit is contained in:
Michael Genson
2023-08-06 12:49:30 -05:00
committed by GitHub
parent 06962cf865
commit dfe4942451
16 changed files with 355 additions and 92 deletions

View File

@@ -77,6 +77,7 @@ from .recipe_timeline_events import (
RecipeTimelineEventOut,
RecipeTimelineEventPagination,
RecipeTimelineEventUpdate,
TimelineEventImage,
TimelineEventType,
)
from .recipe_tool import RecipeToolCreate, RecipeToolOut, RecipeToolResponse, RecipeToolSave
@@ -155,6 +156,7 @@ __all__ = [
"RecipeTimelineEventOut",
"RecipeTimelineEventPagination",
"RecipeTimelineEventUpdate",
"TimelineEventImage",
"TimelineEventType",
"RecipeToolCreate",
"RecipeToolOut",

View File

@@ -129,29 +129,48 @@ class Recipe(RecipeSummary):
comments: list[RecipeCommentOut] | None = []
@staticmethod
def directory_from_id(recipe_id: UUID4 | str) -> Path:
return app_dirs.RECIPE_DATA_DIR.joinpath(str(recipe_id))
def _get_dir(dir: Path) -> Path:
"""Gets a directory and creates it if it doesn't exist"""
dir.mkdir(exist_ok=True, parents=True)
return dir
@classmethod
def directory_from_id(cls, recipe_id: UUID4 | str) -> Path:
return cls._get_dir(app_dirs.RECIPE_DATA_DIR.joinpath(str(recipe_id)))
@classmethod
def asset_dir_from_id(cls, recipe_id: UUID4 | str) -> Path:
return cls._get_dir(cls.directory_from_id(recipe_id).joinpath("assets"))
@classmethod
def image_dir_from_id(cls, recipe_id: UUID4 | str) -> Path:
return cls._get_dir(cls.directory_from_id(recipe_id).joinpath("images"))
@classmethod
def timeline_image_dir_from_id(cls, recipe_id: UUID4 | str, timeline_event_id: UUID4 | str) -> Path:
return cls._get_dir(cls.image_dir_from_id(recipe_id).joinpath("timeline").joinpath(str(timeline_event_id)))
@property
def directory(self) -> Path:
if not self.id:
raise ValueError("Recipe has no ID")
folder = app_dirs.RECIPE_DATA_DIR.joinpath(str(self.id))
folder.mkdir(exist_ok=True, parents=True)
return folder
return self.directory_from_id(self.id)
@property
def asset_dir(self) -> Path:
folder = self.directory.joinpath("assets")
folder.mkdir(exist_ok=True, parents=True)
return folder
if not self.id:
raise ValueError("Recipe has no ID")
return self.asset_dir_from_id(self.id)
@property
def image_dir(self) -> Path:
folder = self.directory.joinpath("images")
folder.mkdir(exist_ok=True, parents=True)
return folder
if not self.id:
raise ValueError("Recipe has no ID")
return self.image_dir_from_id(self.id)
class Config:
orm_mode = True

View File

@@ -1,11 +1,16 @@
from datetime import datetime
from enum import Enum
from pathlib import Path
from pydantic import UUID4, Field
from mealie.core.config import get_app_dirs
from mealie.schema._mealie.mealie_model import MealieModel
from mealie.schema.recipe.recipe import Recipe
from mealie.schema.response.pagination import PaginationBase
app_dirs = get_app_dirs()
class TimelineEventType(Enum):
system = "system"
@@ -13,6 +18,11 @@ class TimelineEventType(Enum):
comment = "comment"
class TimelineEventImage(Enum):
has_image = "has image"
does_not_have_image = "does not have image"
class RecipeTimelineEventIn(MealieModel):
recipe_id: UUID4
user_id: UUID4 | None = None
@@ -22,7 +32,7 @@ class RecipeTimelineEventIn(MealieModel):
event_type: TimelineEventType
message: str | None = Field(None, alias="eventMessage")
image: str | None = None
image: TimelineEventImage | None = TimelineEventImage.does_not_have_image
timestamp: datetime = datetime.now()
@@ -37,7 +47,10 @@ class RecipeTimelineEventCreate(RecipeTimelineEventIn):
class RecipeTimelineEventUpdate(MealieModel):
subject: str
message: str | None = Field(alias="eventMessage")
image: str | None = None
image: TimelineEventImage | None = None
class Config:
use_enum_values = True
class RecipeTimelineEventOut(RecipeTimelineEventCreate):
@@ -48,6 +61,14 @@ class RecipeTimelineEventOut(RecipeTimelineEventCreate):
class Config:
orm_mode = True
@classmethod
def image_dir_from_id(cls, recipe_id: UUID4 | str, timeline_event_id: UUID4 | str) -> Path:
return Recipe.timeline_image_dir_from_id(recipe_id, timeline_event_id)
@property
def image_dir(self) -> Path:
return self.image_dir_from_id(self.recipe_id, self.id)
class RecipeTimelineEventPagination(PaginationBase):
items: list[RecipeTimelineEventOut]