Render edit

This commit is contained in:
Adrian Woźniak 2024-11-14 14:03:42 +01:00
parent 510376bb81
commit a3ccefb118
8 changed files with 59 additions and 8756 deletions

10
Cargo.lock generated
View File

@ -1179,6 +1179,7 @@ dependencies = [
"futures-util", "futures-util",
"humantime", "humantime",
"humantime-serde", "humantime-serde",
"itertools 0.13.0",
"migration", "migration",
"pulldown-cmark", "pulldown-cmark",
"redis 0.27.5", "redis 0.27.5",
@ -2274,6 +2275,15 @@ dependencies = [
"either", "either",
] ]
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.11" version = "1.0.11"

View File

@ -37,6 +37,7 @@ serde_qs = { version = "0.13.0", features = ["actix4", "tracing"] }
futures-core = "0.3.31" futures-core = "0.3.31"
futures-util = "0.3.31" futures-util = "0.3.31"
encoding_rs = "0.8.35" encoding_rs = "0.8.35"
itertools = "0.13.0"
[build-dependencies] [build-dependencies]

View File

@ -10,6 +10,7 @@ use actix_web::HttpMessage;
use actix_web::{get, post, HttpRequest, HttpResponse, Responder}; use actix_web::{get, post, HttpRequest, HttpResponse, Responder};
use askama::Template; use askama::Template;
use askama_actix::TemplateToResponse; use askama_actix::TemplateToResponse;
use itertools::Itertools;
use sea_orm::{ use sea_orm::{
prelude::*, DatabaseTransaction, JoinType, QueryOrder, QuerySelect, TransactionTrait, prelude::*, DatabaseTransaction, JoinType, QueryOrder, QuerySelect, TransactionTrait,
}; };
@ -568,7 +569,7 @@ async fn save_recipe(form: RecipeForm, t: &mut DatabaseTransaction) -> Result<i3
// Ingredients // Ingredients
{ {
let ingredients = crate::utils::parse_ingenedients(&form.ingeredients)?; let ingredients = crate::utils::parse_ingredients(&form.ingeredients)?;
let known = Ingredients::find() let known = Ingredients::find()
.filter( .filter(
entities::ingredients::Column::Name.is_in( entities::ingredients::Column::Name.is_in(
@ -654,40 +655,43 @@ async fn edit_recipe(
todo!() todo!()
} }
}; };
// steps
let recipe_steps = RecipeSteps::find()
.filter(entities::recipe_steps::Column::RecipeId.eq(id))
.all(&**db)
.await
.inspect_err(|e| tracing::error!("Failed to load recipe steps for edit recipe: {e}"))
.map_err(|_| Error::DatabaseError)?;
// tags
let recipe_tags = RecipeTags::find() let recipe_tags = RecipeTags::find()
.filter(entities::recipe_tags::Column::RecipeId.eq(id)) .filter(entities::recipe_tags::Column::RecipeId.eq(id))
.all(&**db) .all(&**db)
.await .await
.unwrap_or_default()
.inspect_err(|e| tracing::error!("Failed to load recipe tags for edit recipe: {e}")) .inspect_err(|e| tracing::error!("Failed to load recipe tags for edit recipe: {e}"))
.map_err(|_| Error::DatabaseError)?; .map_err(|_| Error::DatabaseError)?;
let tags = Tags::find() let tags = Tags::find()
.join(
JoinType::InnerJoin,
entities::tags::Relation::RecipeTags.def(),
)
.filter(entities::recipe_tags::Column::RecipeId.eq(id))
.all(&**db) .all(&**db)
.await .await
.unwrap_or_default() .inspect_err(|e| tracing::error!("Failed to load tags for edit recipe: {e}"))
.inspect_err(|e| tracing::error!("Failed to load ingredients for edit recipe: {e}"))
.map_err(|_| Error::DatabaseError)?; .map_err(|_| Error::DatabaseError)?;
// ingredients
let recipe_ingredients = RecipeIngredients::find() let recipe_ingredients = RecipeIngredients::find()
.filter(entities::recipe_ingredients::Column::RecipeId.eq(id)) .filter(entities::recipe_ingredients::Column::RecipeId.eq(id))
.all(&**db) .all(&**db)
.await .await
.unwrap_or_default() .inspect_err(|e| tracing::error!("Failed to load recipe ingredients for edit recipe: {e}"))
.inspect_err(|e| tracing::error!("Failed to load ingredients for edit recipe: {e}"))
.map_err(|_| Error::DatabaseError)?; .map_err(|_| Error::DatabaseError)?;
let ingredients = Ingredients::find() let ingredients = Ingredients::find()
.join_rev( .join(
JoinType::InnerJoin, JoinType::InnerJoin,
entities::ingredients::Relation::RecipeIngredients.def(), entities::ingredients::Relation::RecipeIngredients.def(),
) )
.filter(entities::recipe_ingredients::Column::RecipeId.eq(id)) .filter(entities::recipe_ingredients::Column::RecipeId.eq(id))
.all(&**db) .all(&**db)
.await .await
.unwrap_or_default()
.inspect_err(|e| tracing::error!("Failed to load ingredients for edit recipe: {e}")) .inspect_err(|e| tracing::error!("Failed to load ingredients for edit recipe: {e}"))
.map_err(|_| Error::DatabaseError)? .map_err(|_| Error::DatabaseError)?
.into_iter() .into_iter()
@ -695,39 +699,59 @@ async fn edit_recipe(
agg.insert(ing.id, ing); agg.insert(ing.id, ing);
agg agg
}); });
let tags_by_id = tags.iter().fold(HashMap::new(), |mut agg, tag| {
agg.insert(tag.id, tag);
agg
});
let steps = recipe_steps
.iter()
.map(|recipe| match recipe.hint {
Some(ref hint) => format!("* {body}\n> {hint}", body = recipe.body, hint = hint),
None => format!("* {body}", body = recipe.body),
})
.collect::<Vec<_>>()
.join("\n");
Ok(EditRecipeForm { Ok(EditRecipeForm {
id, id,
title: recipe.title, title: recipe.title,
summary: recipe.summary, summary: recipe.summary.unwrap_or_default(),
ingredients: recipe_ingredients ingredients: recipe_ingredients
.into_iter() .into_iter()
.filter_map(|ingredient| { .filter_map(|ingredient| {
format!( Some(format!(
"{} {} {}", "{} {} {}",
ingredient.qty, ingredient.qty,
ingredient.unit, ingredient.unit,
ingredients.get(&ingredient.id)?.name ingredients.get(&ingredient.id)?.name
) ))
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\n"), .join("\n"),
steps, steps,
tags, tags: recipe_tags
selected_tags, .iter()
.filter_map(|recipe_tag| Some(tags_by_id.get(&recipe_tag.tag_id)?.name.clone()))
.collect::<Vec<_>>()
.join(","),
selected_tags: recipe_tags
.iter()
.map(|rt| rt.recipe_id.to_string())
.collect(),
image_url: recipe.image_url, image_url: recipe.image_url,
time: recipe.time, time: recipe.time.map(|i| i.to_string()),
author: recipe.author, author: recipe.author,
error: None, error: None,
known_tags: recipe_tags.iter().map(|tag| tag.tag_id)).collect(), known_tags: tags.clone(),
known_ingredients: recipe_ingredients.iter().map(|ing| ing.ingredient_id).collect(), known_ingredients: ingredients.values().cloned().collect(),
session: admin.id(), session: admin.id().ok(),
page: Page::Index, page: Page::Index,
}) })
} }
#[post("/recipe/{id}/edit")] #[post("/recipe/{id}/edit")]
async fn update_recipe( async fn update_recipe(
id: actix_web::web::Path<i32>, _id: actix_web::web::Path<i32>,
admin: Identity, admin: Identity,
db: Data<DatabaseConnection>, db: Data<DatabaseConnection>,
form: QsForm<RecipeForm>, form: QsForm<RecipeForm>,

View File

@ -22,7 +22,7 @@ pub fn parse_steps(s: &str) -> Result<Vec<(String, Option<String>)>, Error> {
}) })
} }
pub fn parse_ingenedients(s: &str) -> Result<Vec<(String, Option<String>, i32)>, Error> { pub fn parse_ingredients(s: &str) -> Result<Vec<(String, Option<String>, i32)>, Error> {
s.lines() s.lines()
.map(|s| s.trim()) .map(|s| s.trim())
.filter(|s| !s.is_empty()) .filter(|s| !s.is_empty())

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff