From 29b2f076bff5ace70ae85188d39507e3020f09de Mon Sep 17 00:00:00 2001 From: artus40 Date: Thu, 7 Feb 2019 21:58:38 +0100 Subject: [PATCH] work on planner templates in progress --- cookbook/db.sqlite3 | Bin 20480 -> 20480 bytes cookbook/src/models.rs | 2 +- planner/src/bin/weekly.rs | 96 ++++++++++++++++------- planner/src/solver.rs | 35 ++++----- web/vue/package.json | 1 + web/vue/src/App.vue | 57 +++++++++----- web/vue/src/components/Heading.vue | 16 ++++ web/vue/src/components/HelloWorld.vue | 37 --------- web/vue/src/components/RecipeDetails.vue | 19 ++++- web/vue/src/components/RecipeList.vue | 54 +++++++++++++ 10 files changed, 209 insertions(+), 108 deletions(-) create mode 100644 web/vue/src/components/Heading.vue delete mode 100644 web/vue/src/components/HelloWorld.vue diff --git a/cookbook/db.sqlite3 b/cookbook/db.sqlite3 index acc34e4ab2439f396fec1d1cfedd4c3d7cd61b38..54c616fafdc30c6caebb86e7eb09e8d1ac1ede2f 100644 GIT binary patch delta 117 zcmV-*0E+*BpaFoO0gxL3Mv)vt0YL00{|CObz^d6ldn%v_E#d- delta 104 zcmV-u0GI!OpaFoO0gxL3M3Edr0YtH2q%RBu55oWt?hn}y!wn&kw{8x(}}pmk){$c@Ju{5fDrdlWI>p3IhNgQ( = Vec<&'a V>; /// We want a mapping of the week meals (matin, midi, soir) /// Breakfast => RecipeCategory::Breakfast /// Lunch => RecipeCategory::MainCourse @@ -14,52 +15,87 @@ use self::planner::solver::{Variables, Domain, Problem}; mod template { //! Exports the [`Template`] struct. + use super::{Domain, DomainValues, Variables, Recipe}; type Day = String; - const DAYS: &[&str] = &["Lundi", "Mardi", "Mercredi"]; + const DAYS: &[&str] = &[ + "Lundi", "Mardi", "Mercredi", + "Jeudi", "Vendredi", "Samedi", + "Dimanche"]; + /// An enum to discriminate every meals #[allow(dead_code)] - enum Meals { + pub enum Meals { Breakfast(Day), Lunch(Day), Dinner(Day) } + impl From for String { + fn from(item: Meals) -> String { + match item { + Meals::Breakfast(d) => format!("{}_Breakfast", d), + Meals::Lunch(d) => format!("{}_Lunch", d), + Meals::Dinner(d) => format!("{}_Dinner", d), + } + } + } + + + /// A fixed template of meals + /// + /// TODO: mask -> disabled choosen meals + /// initials -> fixed values pub struct Template; impl Template { - fn empty() { + fn keys() -> Vec { + let mut keys = Vec::new(); + for day in DAYS { + for meal in &[Meals::Breakfast, Meals::Lunch, Meals::Dinner] { + keys.push(meal(day.to_string())) + } + } + keys + } + + /// Build a [`Template`] from a variables assignment map, + /// usually a solution returned by solver + pub(super) fn from_variables<'a, V>(_vars: Variables<'a,V>) -> Template{ + Template + } + + /// Builds a vector of variables, to be used with + /// [`ProblemBuilder`]. + pub(super) fn generate_variables(domain: &Domain) + -> Vec<(String, DomainValues, Option<&Recipe>)> + { + use cookbook::recipes::RecipeCategory; + let mut vars = Vec::new(); + for key in Self::keys().into_iter() { + //TODO: Use key variants to set filters on domain + //TODO: Initial values + let _filter: fn(&&Recipe) -> bool = match key { + Meals::Breakfast(_) => |r: &&Recipe| { + r.category == RecipeCategory::Breakfast // Breakfast + }, + Meals::Lunch(_) => |r: &&Recipe| { + r.category as i16 == 2i16 // MainCourse + }, + Meals::Dinner(_) => |r: &&Recipe| { + r.category as i16 == 1i16 // Starter + } + }; + vars.push((key.into(), domain.filter(_filter), None)); + } + vars } } } - - -impl Into for Meals { - fn into(self) -> String { - match self { - Meals::Breakfast(d) => format!("{}_Breakfast", d), - Meals::Lunch(d) => format!("{}_Lunch", d), - Meals::Dinner(d) => format!("{}_Dinner", d), - } - } -} - -/// It may also contains an initial value for each variable -fn generate_variables(domain: &Domain) -> Vec<(String, &Domain, Option<&V>)> { - let mut vars = Vec::new(); - for day in DAYS { - vars.push((Meals::Lunch(day.to_string()).into(), domain, None)); - vars.push((Meals::Dinner(day.to_string()).into(), domain, None)); - } - vars -} - -fn ingredients_contains<'a>(assign: &Variables<'a,Recipe>) -> bool { - let id = 0; - assign.get("Lundi_Lunch").unwrap().unwrap().ingredients.contains(&id) - && !assign.get("Mardi_Lunch").unwrap().unwrap().ingredients.contains(&id) +fn ingredients_contains<'a>(_assign: &Variables<'a,Recipe>) -> bool { + true } @@ -80,7 +116,7 @@ fn get_planning_all_results() -> String { let possible_values = recipes::load_all(&conn); let domain = Domain::new(possible_values); let mut problem = Problem::build(); - for (var, dom, ini) in generate_variables(&domain) { + for (var, dom, ini) in template::Template::generate_variables(&domain) { problem = problem.add_variable(var, dom, ini); } let mut problem = problem diff --git a/planner/src/solver.rs b/planner/src/solver.rs index 2e1e557..695d322 100644 --- a/planner/src/solver.rs +++ b/planner/src/solver.rs @@ -14,7 +14,7 @@ enum Assignment<'a, V> { } -type Domains<'a, V> = HashMap>; +pub type DomainValues<'a, V> = Vec<&'a V>; /// The domain of values that can be assigned to variables #[derive(Clone)] pub struct Domain { @@ -26,10 +26,11 @@ impl Domain { Domain { values } } - pub fn values(&self) -> &Vec { - &self.values + /// Returns all values of a Domain instance + pub fn all(&self) -> DomainValues { + self.values.iter().collect() } - /// Returns a new domain with the given filter applied to inner values + /// Returns a Filter filter applied to inner values /// /// # Examples /// @@ -40,12 +41,10 @@ impl Domain { /// fn even(i: &i32) -> bool { i % 2 == 0 }; /// assert_eq!(&domain.filter(even).values, &vec![2]); /// ``` - pub fn filter(&self, f: fn(&V) -> bool) -> Domain + pub fn filter(&self, f: fn(&&V) -> bool) -> DomainValues where V: std::clone::Clone { - Domain { - values: self.values.iter().cloned().filter(f).collect(), - } + self.values.iter().filter(f).collect() } } @@ -62,7 +61,7 @@ pub struct Problem<'a, V> { /// The initial assignements map variables: Variables<'a, V>, /// Each variable has its associated domain - domains: Domains<'a,V>, + domains: HashMap>, /// Set of constraints to validate constraints: Vec>, } @@ -78,14 +77,14 @@ impl<'a,V> Problem<'a, V> { fn _push_updates(&self) -> Option>> { // TODO: should be able to inject a choosing strategy if let Some((key,_)) = self.variables.iter().find(|(_, val)| val.is_none()) { - let domain = self.domains.get(key).expect("No domain for variable !"); + let domain_values = self.domains.get(key).expect("No domain for variable !"); // Push a clear assignment first, just before going up the stack. let mut updates = vec![Assignment::Clear(key.clone())]; - if domain.values.is_empty() { panic!("No value in domain !"); } + if domain_values.is_empty() { panic!("No value in domain !"); } // TODO: should be able to filter domain values (inference, pertinence) - for value in domain.values.iter() { - updates.push(Assignment::Update(key.clone(), value)); + for value in domain_values.into_iter() { + updates.push(Assignment::Update(key.clone(), *value)); } Some(updates) } else { // End of assignements @@ -147,7 +146,7 @@ impl<'a, V> ProblemBuilder<'a, V> { }) } - pub fn add_variable(mut self, name: S, domain: &'a Domain, value: Option<&'a V>) -> Self + pub fn add_variable(mut self, name: S, domain: Vec<&'a V>, value: Option<&'a V>) -> Self where S: Into { let name = name.into(); @@ -174,8 +173,8 @@ mod tests { use super::*; let domain = Domain::new(vec![1,2,3]); let mut problem: Problem<_> = Problem::build() - .add_variable(String::from("Left"), &domain, None) - .add_variable(String::from("Right"), &domain, None) + .add_variable(String::from("Left"), domain.all(), None) + .add_variable(String::from("Right"), domain.all(), None) .add_constraint(|assign: &Variables| { assign.get("Left").unwrap() == assign.get("Right").unwrap() }) @@ -195,8 +194,8 @@ mod tests { use super::*; let domain = Domain::new(vec![1,2,3]); let mut problem: Problem<_> = Problem::build() - .add_variable("Left".to_string(), &domain, None) - .add_variable("Right".to_string(), &domain, Some(&2)) + .add_variable("Left".to_string(), domain.all(), None) + .add_variable("Right".to_string(), domain.all(), Some(&2)) .add_constraint( |assign: &Variables| { assign.get("Left").unwrap() == assign.get("Right").unwrap() }) diff --git a/web/vue/package.json b/web/vue/package.json index c3de63a..58fd573 100644 --- a/web/vue/package.json +++ b/web/vue/package.json @@ -8,6 +8,7 @@ "lint": "vue-cli-service lint" }, "dependencies": { + "bulma": "^0.7.2", "vue": "^2.5.22" }, "devDependencies": { diff --git a/web/vue/src/App.vue b/web/vue/src/App.vue index 6a05259..96fa163 100644 --- a/web/vue/src/App.vue +++ b/web/vue/src/App.vue @@ -1,30 +1,53 @@ + + + diff --git a/web/vue/src/components/HelloWorld.vue b/web/vue/src/components/HelloWorld.vue deleted file mode 100644 index d285cb6..0000000 --- a/web/vue/src/components/HelloWorld.vue +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - diff --git a/web/vue/src/components/RecipeDetails.vue b/web/vue/src/components/RecipeDetails.vue index 450b87d..bf186a6 100644 --- a/web/vue/src/components/RecipeDetails.vue +++ b/web/vue/src/components/RecipeDetails.vue @@ -1,5 +1,5 @@ diff --git a/web/vue/src/components/RecipeList.vue b/web/vue/src/components/RecipeList.vue index e69de29..f4abeb8 100644 --- a/web/vue/src/components/RecipeList.vue +++ b/web/vue/src/components/RecipeList.vue @@ -0,0 +1,54 @@ + + +