mirror of
https://github.com/zaphar/kitchen.git
synced 2025-07-22 19:40:14 -04:00
parent
e40b84173e
commit
fbd4aeb59c
@ -86,7 +86,7 @@ pub fn output_ingredients_list(rs: Vec<Recipe>) {
|
||||
for r in rs {
|
||||
acc.accumulate_from(&r);
|
||||
}
|
||||
for (_, i) in acc.ingredients() {
|
||||
for (_, (i, _)) in acc.ingredients() {
|
||||
print!("{}", i.amt.normalize());
|
||||
println!(" {}", i.name);
|
||||
}
|
||||
@ -99,7 +99,7 @@ pub fn output_ingredients_csv(rs: Vec<Recipe>) {
|
||||
}
|
||||
let out = std::io::stdout();
|
||||
let mut writer = csv::Writer::from_writer(out);
|
||||
for (_, i) in acc.ingredients() {
|
||||
for (_, (i, _)) in acc.ingredients() {
|
||||
writer
|
||||
.write_record(&[format!("{}", i.amt.normalize()), i.name])
|
||||
.expect("Failed to write csv.");
|
||||
|
@ -14,7 +14,7 @@
|
||||
pub mod parse;
|
||||
pub mod unit;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use chrono::NaiveDate;
|
||||
|
||||
@ -49,7 +49,7 @@ impl Mealplan {
|
||||
}
|
||||
|
||||
/// A Recipe with a title, description, and a series of steps.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq)]
|
||||
pub struct Recipe {
|
||||
pub title: String,
|
||||
pub desc: Option<String>,
|
||||
@ -92,11 +92,14 @@ impl Recipe {
|
||||
let mut acc = IngredientAccumulator::new();
|
||||
acc.accumulate_from(&self);
|
||||
acc.ingredients()
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, v.0))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IngredientAccumulator {
|
||||
inner: BTreeMap<IngredientKey, Ingredient>,
|
||||
inner: BTreeMap<IngredientKey, (Ingredient, BTreeSet<String>)>,
|
||||
}
|
||||
|
||||
impl IngredientAccumulator {
|
||||
@ -110,26 +113,31 @@ impl IngredientAccumulator {
|
||||
for i in r.steps.iter().map(|s| s.ingredients.iter()).flatten() {
|
||||
let key = i.key();
|
||||
if !self.inner.contains_key(&key) {
|
||||
self.inner.insert(key, i.clone());
|
||||
let mut set = BTreeSet::new();
|
||||
set.insert(r.title.clone());
|
||||
self.inner.insert(key, (i.clone(), set));
|
||||
} else {
|
||||
let amt = match (self.inner[&key].amt, i.amt) {
|
||||
let amt = match (self.inner[&key].0.amt, i.amt) {
|
||||
(Volume(rvm), Volume(lvm)) => Volume(lvm + rvm),
|
||||
(Count(lqty), Count(rqty)) => Count(lqty + rqty),
|
||||
(Weight(lqty), Weight(rqty)) => Weight(lqty + rqty),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
self.inner.get_mut(&key).map(|i| i.amt = amt);
|
||||
self.inner.get_mut(&key).map(|(i, set)| {
|
||||
i.amt = amt;
|
||||
set.insert(r.title.clone());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ingredients(self) -> BTreeMap<IngredientKey, Ingredient> {
|
||||
pub fn ingredients(self) -> BTreeMap<IngredientKey, (Ingredient, BTreeSet<String>)> {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
/// A Recipe step. It has the time for the step if there is one, instructions, and an ingredients
|
||||
/// list.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq)]
|
||||
pub struct Step {
|
||||
pub prep_time: Option<std::time::Duration>,
|
||||
pub instructions: String,
|
||||
@ -172,7 +180,7 @@ pub struct IngredientKey(String, Option<String>, String);
|
||||
|
||||
/// Ingredient in a recipe. The `name` and `form` fields with the measurement type
|
||||
/// uniquely identify an ingredient.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
|
||||
pub struct Ingredient {
|
||||
pub id: Option<i64>, // TODO(jwall): use uuid instead?
|
||||
pub name: String,
|
||||
|
@ -26,7 +26,7 @@ use std::{
|
||||
|
||||
use num_rational::Ratio;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, PartialOrd, Eq, Ord)]
|
||||
/// Volume Measurements for ingredients in a recipe.
|
||||
pub enum VolumeMeasure {
|
||||
// Imperial volume measurements. US.
|
||||
@ -225,7 +225,7 @@ impl Display for VolumeMeasure {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, PartialOrd, Eq, Ord)]
|
||||
pub enum WeightMeasure {
|
||||
Gram(Quantity),
|
||||
Kilogram(Quantity),
|
||||
@ -338,7 +338,7 @@ impl Display for WeightMeasure {
|
||||
|
||||
use WeightMeasure::{Gram, Kilogram, Oz, Pound};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
|
||||
/// Measurements in a Recipe with associated units for them.
|
||||
pub enum Measure {
|
||||
/// Volume measurements as meter cubed base unit
|
||||
@ -447,7 +447,7 @@ impl Display for Measure {
|
||||
}
|
||||
|
||||
/// Represents a Quantity for an ingredient of a recipe.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Eq, Ord)]
|
||||
pub enum Quantity {
|
||||
/// Whole or non fractional quantities of an ingredient in a recipe.
|
||||
Whole(u32),
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
use crate::service::AppService;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
use std::collections::{BTreeMap, BTreeSet, HashSet};
|
||||
|
||||
use recipes::{Ingredient, IngredientKey};
|
||||
use sycamore::{context::use_context, prelude::*};
|
||||
@ -33,7 +33,7 @@ pub fn shopping_list() -> View<G> {
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
.filter(|(k, _v)| !filtered_keys.get().contains(k))
|
||||
.collect::<Vec<(IngredientKey, Ingredient)>>()
|
||||
.collect::<Vec<(IngredientKey, (Ingredient, BTreeSet<String>))>>()
|
||||
}));
|
||||
let table_view = Signal::new(View::empty());
|
||||
create_effect(
|
||||
@ -44,15 +44,17 @@ pub fn shopping_list() -> View<G> {
|
||||
tr {
|
||||
th { " Quantity " }
|
||||
th { " Ingredient " }
|
||||
th { " Recipes " }
|
||||
}
|
||||
tbody {Indexed(IndexedProps{
|
||||
iterable: ingredients.clone(),
|
||||
template: cloned!((filtered_keys, modified_amts) => move |(k, i)| {
|
||||
template: cloned!((filtered_keys, modified_amts) => move |(k, (i, rs))| {
|
||||
let mut modified_amt_set = (*modified_amts.get()).clone();
|
||||
let amt = modified_amt_set.entry(k.clone()).or_insert(Signal::new(format!("{}", i.amt.normalize()))).clone();
|
||||
modified_amts.set(modified_amt_set);
|
||||
let name = i.name;
|
||||
let form = i.form.map(|form| format!("({})", form)).unwrap_or_default();
|
||||
let names = rs.iter().fold(String::new(), |acc, s| format!("{}{},", acc, s)).trim_end_matches(",").to_owned();
|
||||
view! {
|
||||
tr {
|
||||
td { input(bind:value=amt.clone(), class="ingredient-count-sel", type="text") }
|
||||
@ -60,7 +62,8 @@ pub fn shopping_list() -> View<G> {
|
||||
let mut keyset = (*filtered_keys.get()).clone();
|
||||
keyset.insert(k.clone());
|
||||
filtered_keys.set(keyset);
|
||||
})) " " (name) " " (form) }
|
||||
})) " " (name) " " (form) "" }
|
||||
td { (names) }
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
@ -11,7 +11,7 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use crate::{console_debug, console_error, console_log};
|
||||
|
||||
@ -124,7 +124,7 @@ impl AppService {
|
||||
self.recipes.get().get(idx).map(|(_, r)| r.clone())
|
||||
}
|
||||
|
||||
pub fn get_shopping_list(&self) -> BTreeMap<IngredientKey, Ingredient> {
|
||||
pub fn get_shopping_list(&self) -> BTreeMap<IngredientKey, (Ingredient, BTreeSet<String>)> {
|
||||
let mut acc = IngredientAccumulator::new();
|
||||
let recipe_counts = self.menu_list.get();
|
||||
for (idx, count) in recipe_counts.iter() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user