Compare commits
2 Commits
e29d664f0e
...
3ee9533faf
| Author | SHA1 | Date | |
|---|---|---|---|
| 3ee9533faf | |||
| 8c89b9c059 |
@@ -38,6 +38,14 @@ pub mod recipes {
|
|||||||
.execute(conn)
|
.execute(conn)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get(conn: &SqliteConnection, recipe_id: i32) -> Option<Recipe> {
|
||||||
|
use self::schema::recipes::dsl::*;
|
||||||
|
|
||||||
|
recipes.filter(id.eq(recipe_id))
|
||||||
|
.first(conn)
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod ingredients {
|
pub mod ingredients {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ pub type DomainValues<'a, V> = Vec<&'a V>;
|
|||||||
/// The domain of values that can be assigned to variables
|
/// The domain of values that can be assigned to variables
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Domain<V> {
|
pub struct Domain<V> {
|
||||||
values: Vec<V>
|
pub values: Vec<V>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V> Domain<V> {
|
impl<V> Domain<V> {
|
||||||
@@ -51,6 +51,7 @@ impl<V> Domain<V> {
|
|||||||
.filter(|v: &&V| filter_func(*v))
|
.filter(|v: &&V| filter_func(*v))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: fmt::Debug> fmt::Debug for Domain<V> {
|
impl<V: fmt::Debug> fmt::Debug for Domain<V> {
|
||||||
@@ -118,7 +119,12 @@ impl<'a,V, K: Eq + Hash + Clone> Problem<'a, V, K> {
|
|||||||
{
|
{
|
||||||
let mut solutions: Vec<Variables<V, K>> = vec![];
|
let mut solutions: Vec<Variables<V, K>> = vec![];
|
||||||
let mut stack: Vec<Assignment<'a, V, K>> = vec![];
|
let mut stack: Vec<Assignment<'a, V, K>> = vec![];
|
||||||
stack.append(&mut self._push_updates().unwrap());
|
if let Some(mut init_updates) = self._push_updates() {
|
||||||
|
stack.append(&mut init_updates);
|
||||||
|
} else {
|
||||||
|
panic!("Could not initialize !");
|
||||||
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let node = stack.pop();
|
let node = stack.pop();
|
||||||
if node.is_none() { break; };
|
if node.is_none() { break; };
|
||||||
@@ -150,7 +156,12 @@ impl<'a,V, K: Eq + Hash + Clone> Problem<'a, V, K> {
|
|||||||
K: Clone + fmt::Debug,
|
K: Clone + fmt::Debug,
|
||||||
{
|
{
|
||||||
let mut stack: Vec<Assignment<'a, V, K>> = vec![];
|
let mut stack: Vec<Assignment<'a, V, K>> = vec![];
|
||||||
stack.append(&mut self._push_updates().unwrap());
|
if let Some(mut init_updates) = self._push_updates() {
|
||||||
|
stack.append(&mut init_updates);
|
||||||
|
} else {
|
||||||
|
panic!("Could not initialize !");
|
||||||
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let node = stack.pop();
|
let node = stack.pop();
|
||||||
if node.is_none() {
|
if node.is_none() {
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ mod api {
|
|||||||
pub struct CookbookDbConn(diesel::SqliteConnection);
|
pub struct CookbookDbConn(diesel::SqliteConnection);
|
||||||
|
|
||||||
/// A serializable wrapper for [`cookbook::recipes::Recipe`]
|
/// A serializable wrapper for [`cookbook::recipes::Recipe`]
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct RecipeObject {
|
pub struct RecipeObject {
|
||||||
id: i32,
|
id: i32,
|
||||||
title: String,
|
title: String,
|
||||||
@@ -76,13 +76,13 @@ mod api {
|
|||||||
Json( recipes::delete(&conn, id) )
|
Json( recipes::delete(&conn, id) )
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct TemplateItems {
|
pub struct TemplateItems {
|
||||||
key: (String, String),
|
key: (String, String),
|
||||||
value: Option<RecipeObject>,
|
value: Option<RecipeObject>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct TemplateObject {
|
pub struct TemplateObject {
|
||||||
items: Vec<TemplateItems>
|
items: Vec<TemplateItems>
|
||||||
}
|
}
|
||||||
@@ -118,6 +118,65 @@ mod api {
|
|||||||
panic!("No solution at all !");
|
panic!("No solution at all !");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[post("/solver/complete", data="<partial>")]
|
||||||
|
pub fn complete_problem(conn: CookbookDbConn, partial: Json<Vec<TemplateItems>>) -> Json<TemplateObject> {
|
||||||
|
use planner::{
|
||||||
|
template,
|
||||||
|
solver::{Domain, Problem}
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("{:?}", partial);
|
||||||
|
println!("Building problem");
|
||||||
|
let possible_values = recipes::load_all(&conn);
|
||||||
|
let domain = Domain::new(possible_values);
|
||||||
|
let mut problem = Problem::build();
|
||||||
|
for (var, dom, ini) in template::Template::generate_variables(&domain) {
|
||||||
|
// Let's hack for now
|
||||||
|
// BUGGY because template does not generate every variables, needs refactoring
|
||||||
|
// Find variable in partial
|
||||||
|
let initial_id = partial.iter().find_map(|slot| {
|
||||||
|
if slot.value.is_none() { return None; };
|
||||||
|
//println!("{:?} vs {:?}", slot, var);
|
||||||
|
if slot.key.0 == var.0
|
||||||
|
&& slot.key.1 == format!("{:?}",var.1)
|
||||||
|
{
|
||||||
|
let id = slot.value.as_ref().unwrap().id;
|
||||||
|
println!("found initial : recipe with id {}", id);
|
||||||
|
return Some(id);
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let ini = if let Some(id) = initial_id {
|
||||||
|
let new_ini = domain.values.iter().find(|r| r.id == id);
|
||||||
|
println!("Overrided {:?}", new_ini);
|
||||||
|
new_ini
|
||||||
|
} else {
|
||||||
|
ini
|
||||||
|
};
|
||||||
|
// If found, override initial value
|
||||||
|
problem = problem.add_variable(var, dom, ini);
|
||||||
|
}
|
||||||
|
let mut problem = problem
|
||||||
|
.add_constraint(|_| true)
|
||||||
|
.finish();
|
||||||
|
if let Some(one_result) = problem.solve_one() {
|
||||||
|
Json(TemplateObject {
|
||||||
|
items: one_result
|
||||||
|
.into_iter()
|
||||||
|
.map(|(k,v)| {
|
||||||
|
TemplateItems {
|
||||||
|
key: (format!("{}", k.0), format!("{:?}", k.1)),
|
||||||
|
value: v.map(|r| RecipeObject::from(&conn, r.clone())),
|
||||||
|
}})
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
panic!("No solution at all !");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Error> {
|
fn main() -> Result<(), Error> {
|
||||||
@@ -137,7 +196,12 @@ fn main() -> Result<(), Error> {
|
|||||||
rocket::ignite()
|
rocket::ignite()
|
||||||
.attach(api::CookbookDbConn::fairing())
|
.attach(api::CookbookDbConn::fairing())
|
||||||
.mount("/", routes![index, files])
|
.mount("/", routes![index, files])
|
||||||
.mount("/api", routes![api::recipes_list, api::delete_recipe, api::one_solution])
|
.mount("/api", routes![
|
||||||
|
api::recipes_list,
|
||||||
|
api::delete_recipe,
|
||||||
|
api::one_solution,
|
||||||
|
api::complete_problem,
|
||||||
|
])
|
||||||
.attach(cors)
|
.attach(cors)
|
||||||
.launch();
|
.launch();
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<button @click="fetchSolution"
|
<button @click="fetchCompletion"
|
||||||
class="button is-primary is-fullwidth"
|
class="button is-primary is-fullwidth"
|
||||||
v-bind:class="{'is-loading': is_loading }">
|
v-bind:class="{'is-loading': is_loading }">
|
||||||
FetchSolution</button>
|
FetchSolution</button>
|
||||||
<div v-if="template">
|
<div class="box">
|
||||||
<div class="columns is-mobile is-vcentered is-multiline">
|
<div class="columns is-mobile is-vcentered is-multiline">
|
||||||
<div v-for="[day, meals] in itemsGroupedByDay"
|
<div v-for="[day, meals] in itemsGroupedByDay"
|
||||||
class="column is-one-quarter-desktop is-half-mobile">
|
class="column is-one-quarter-desktop is-half-mobile">
|
||||||
@@ -109,7 +109,23 @@ export default {
|
|||||||
return res.json();}
|
return res.json();}
|
||||||
)
|
)
|
||||||
.then((data) => this.template.updateJson(data))
|
.then((data) => this.template.updateJson(data))
|
||||||
.catch((err) => console.log(err));
|
.catch((err) => console.error(err));
|
||||||
|
},
|
||||||
|
fetchCompletion: function() {
|
||||||
|
this.is_loading = true;
|
||||||
|
// TODO: Keep only value's id
|
||||||
|
let body = JSON.stringify(this.template.items);
|
||||||
|
fetch("http://localhost:8000/api/solver/complete", {
|
||||||
|
method: 'POST',
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
this.is_loading = false;
|
||||||
|
return res.json();}
|
||||||
|
)
|
||||||
|
.then((data) => this.template.updateJson(data))
|
||||||
|
.catch((err) => console.error(err));
|
||||||
|
|
||||||
},
|
},
|
||||||
unsetMeal: function(mealKey) {
|
unsetMeal: function(mealKey) {
|
||||||
let idx = this.template.findIndexByKey(mealKey);
|
let idx = this.template.findIndexByKey(mealKey);
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column is-narrow">
|
<div class="column is-narrow">
|
||||||
<a @click="$emit('close')"
|
<a @click="$emit('close')"
|
||||||
class="has-text-dark">
|
class="button is-large is-outlined-dark is-fullwidth">
|
||||||
|
<span>Liste</span>
|
||||||
<span class="icon is-large">
|
<span class="icon is-large">
|
||||||
<i class="mdi mdi-48px mdi-view-list"></i>
|
<i class="mdi mdi-36px mdi-view-list"></i>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
<br class="is-hidden-mobile"/>
|
<br class="is-hidden-mobile"/>
|
||||||
|
|||||||
@@ -13,9 +13,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-else class="columns">
|
<div v-else class="columns">
|
||||||
<div class="column is-narrow">
|
<div class="column is-narrow">
|
||||||
<a @click="setActiveCategory(-1)" class="has-text-dark">
|
<a @click="setActiveCategory(-1)"
|
||||||
|
class="button is-large is-fullwidth">
|
||||||
|
<span>Catégories</span>
|
||||||
<span class="icon is-large" >
|
<span class="icon is-large" >
|
||||||
<i class="mdi mdi-48px mdi-view-grid"></i>
|
<i class="mdi mdi-36px mdi-view-grid"></i>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user