diff --git a/src/models/recipe.rs b/src/models/recipe.rs index 5d49750..d0a1bee 100644 --- a/src/models/recipe.rs +++ b/src/models/recipe.rs @@ -106,16 +106,38 @@ impl Recipe { /// /// an `Option` type, specifically `Option<&Recipe01>`. pub fn search_pd(&self, product_code: String) -> Option<&Recipe01> { - self.Recipe01 - .iter() - .find(|&r| r.productCode == product_code) + self.Recipe01.iter().find(|&r| { + if r.SubMenu.is_none() { + r.productCode == product_code + } else { + r.SubMenu + .as_ref() + .unwrap() + .iter() + .filter(|x| x.productCode == product_code) + .count() + > 0 + || r.productCode == product_code + } + }) } /// Similar to `search_pd` but ignoring exacted matching. pub fn search_pd_by_no_country_code(&self, product_code: String) -> Option<&Recipe01> { - self.Recipe01 - .iter() - .find(|&r| r.productCode.contains(&product_code)) + self.Recipe01.iter().find(|&r| { + if r.SubMenu.is_none() { + r.productCode.contains(&product_code) + } else { + r.SubMenu + .as_ref() + .unwrap() + .iter() + .filter(|x| x.productCode.contains(&product_code)) + .count() + > 0 + || r.productCode.contains(&product_code) + } + }) } /// Mulitple product code searching with parallel and no exact matching. @@ -126,7 +148,20 @@ impl Recipe { .map(|pd| { self.Recipe01 .par_iter() - .find_any(|x| x.productCode.contains(pd)) + .find_any(|x| { + if x.SubMenu.is_none() { + x.productCode.contains(pd) + } else { + x.SubMenu + .clone() + .unwrap() + .iter() + .filter(|x2| x2.productCode.contains(pd)) + .count() + > 0 + || x.productCode.contains(pd) + } + }) .cloned() }) .collect_into_vec(&mut found); @@ -138,7 +173,20 @@ impl Recipe { pub fn get_pd_index(&self, product_code: String) -> usize { self.Recipe01 .iter() - .position(|r| r.productCode == product_code) + .position(|r| { + if r.SubMenu.is_none() { + r.productCode == product_code + } else { + r.SubMenu + .clone() + .unwrap() + .iter() + .filter(|x2| x2.productCode == product_code) + .count() + > 0 + || r.productCode == product_code + } + }) .unwrap() } @@ -1508,6 +1556,10 @@ impl RecipeList { diff_list } + + pub fn to_map(&self) -> serde_json::Value { + serde_json::to_value(self).unwrap() + } } /// The above code defines a Rust struct called MaterialSetting with various fields representing diff --git a/src/models/recipev2.rs b/src/models/recipev2.rs index af9727c..5fdff42 100644 --- a/src/models/recipev2.rs +++ b/src/models/recipev2.rs @@ -1,9 +1,12 @@ +use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator}; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::HashMap; +use std::path::PathBuf; +use std::sync::Arc; use crate::models::recipe::*; -use crate::recipe_functions::common::*; +use crate::recipe_functions::common::{self, *}; /// Version 2 of `models::Recipe`, WIP /// @@ -16,39 +19,39 @@ pub struct VersionRecipe { impl VersionRecipe { // import - read all file with given format from directory and mixing them together as 1 recipe - pub fn create_versioning_by_country(country: &str) -> Self { - let mut path = String::from(get_config().get("RECIPE_DIR").unwrap()); - path.push_str(country); - path.push('/'); + // pub fn create_versioning_by_country(country: &str) -> Self { + // let mut path = String::from(get_config().get("RECIPE_DIR").unwrap()); + // path.push_str(country); + // path.push('/'); - // read dir - let files = read_all_files_in_directory(&path.clone()); - let expected_file_format = "coffeethai02"; - let filtered = filter_files_by_pattern(files, expected_file_format); + // // read dir + // let files = read_all_files_in_directory(&path.clone()); + // let expected_file_format = "coffeethai02"; + // let filtered = filter_files_by_pattern(files, expected_file_format); - // create recipes vector - let mut recipes = Vec::new(); - let mut known_versions = Vec::new(); - for file in filtered { - // try create model - let mut source_path = path.clone(); - source_path.push_str(&file); - let current_recipe_path = create_recipe_model_from_file(source_path); - known_versions.push(current_recipe_path.clone().MachineSetting.configNumber); - recipes.push(current_recipe_path.clone()); - } + // // create recipes vector + // let mut recipes = Vec::new(); + // let mut known_versions = Vec::new(); + // for file in filtered { + // // try create model + // let mut source_path = path.clone(); + // source_path.push_str(&file); + // let current_recipe_path = create_recipe_model_from_file(source_path); + // known_versions.push(current_recipe_path.clone().MachineSetting.configNumber); + // recipes.push(current_recipe_path.clone()); + // } - // find current - // - let versions = grep_latest_versions(&path.clone()).unwrap(); - let expected_version = versions.get(country).unwrap_or(&0); + // // find current + // // + // let versions = grep_latest_versions(&path.clone()).unwrap(); + // let expected_version = versions.get(country).unwrap_or(&0); - VersionRecipe { - currentVersion: serde_json::json!(format!("{}", expected_version)), - knownVersions: known_versions, - recipesByCategory: break_down_recipe_into_category(recipes), - } - } + // VersionRecipe { + // currentVersion: serde_json::json!(format!("{}", expected_version)), + // knownVersions: known_versions, + // recipesByCategory: break_down_recipe_into_category(recipes), + // } + // } // export // export_full // export_full_with_history @@ -91,3 +94,188 @@ pub struct MenuToppingListWithHistory { pub menuToppingList: MenuToppingList, pub history: Vec>, } + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct RecipeListChange { + pub new_value: Value, + pub raw_old: Vec, + pub raw_new: Vec, + /// for sub menu + pub sub_changes: HashMap, +} + +impl RecipeListChange { + pub fn get_changes(&mut self) { + let old_length = self.raw_old.len(); + let new_length = self.raw_new.len(); + + let is_match_length = old_length == new_length; + let length_for_checking = if is_match_length { + old_length + } else { + if old_length < new_length { + new_length + } else { + old_length + } + }; + + let mut checked = Vec::with_capacity(length_for_checking); + + for ci in 0..length_for_checking { + let current_repl = (self.raw_old.get(ci), self.raw_new.get(ci)); + if current_repl.0.is_some() && current_repl.1.is_some() { + let old_repl = current_repl.0.unwrap(); + let new_repl = current_repl.1.unwrap(); + + let diff = old_repl.compare(new_repl); + *checked.get_mut(ci).unwrap() = diff; + } else if current_repl.0.is_none() && current_repl.1.is_some() { + // new value + *checked.get_mut(ci).unwrap() = current_repl + .1 + .unwrap() + .to_map() + .as_object() + .unwrap() + .keys() + .map(|k| k.to_string()) + .collect(); + } else if current_repl.0.is_some() && current_repl.1.is_none() { + // new value does not exist + *checked.get_mut(ci).unwrap() = current_repl + .0 + .unwrap() + .to_map() + .as_object() + .unwrap() + .keys() + .map(|k| format!("old.{}", k).to_string()) + .collect(); + } + } + + self.new_value = serde_json::to_value(checked).unwrap(); + } +} + +/// break down recipe(s) in the folder into mapping, +/// tracking diff between fields. (This does not compare other fields than Recipe01) +pub fn build_recipe_shardings(source: PathBuf) -> HashMap { + let files = common::get_all_files_in_directory(source.as_path().to_str().unwrap()); + + let mut recipes: Vec = files + .par_iter() + .map(|file| common::create_recipe_model_from_file(file.to_string())) + .collect(); + + // ascending order a.compare(b) + recipes.sort_by(|a, b| { + let version_a = a.MachineSetting.get_config_number().as_i64().unwrap(); + let version_b = b.MachineSetting.get_config_number().as_i64().unwrap(); + + version_a.cmp(&version_b) + }); + + // Structure + // { productCode: { version: [ Option ] } } + let mut shards = HashMap::new(); + + let base_version: i64 = recipes + .get(0) + .unwrap() + .MachineSetting + .get_config_number() + .as_i64() + .unwrap(); + let base_recipe = recipes.get(0).unwrap(); + + shards = base_recipe + .Recipe01 + .par_iter() + .map(|rp1| { + let mut sub_changes = HashMap::new(); + + if rp1.clone().SubMenu.is_some() { + let subs = rp1.clone().SubMenu.unwrap().clone(); + + sub_changes = subs + .par_iter() + .map(|sub1| { + let change_sub = RecipeListChange { + new_value: Value::Null, + raw_new: sub1.recipes.clone(), + raw_old: sub1.recipes.clone(), + sub_changes: HashMap::new(), + }; + + let mut change_sub_map = HashMap::new(); + change_sub_map.insert(base_version, change_sub.clone()); + recipes.iter().for_each(|rc| { + // clone change + let mut change_sub_clone = change_sub.clone(); + + match rc.search_pd_by_no_country_code(sub1.clone().productCode) { + Some(spd) => { + change_sub_clone.raw_new = spd.clone().recipes; + } + None => {} + }; + change_sub_clone.get_changes(); + change_sub_map.insert( + rc.MachineSetting + .get_config_number() + .as_i64() + .unwrap() + .clone(), + change_sub_clone.clone(), + ); + }); + + ( + sub1.clone().productCode, + serde_json::to_value(change_sub_map).unwrap(), + ) + }) + .collect(); + } + + let change = RecipeListChange { + new_value: Value::Null, + raw_old: rp1.recipes.clone(), + raw_new: rp1.recipes.clone(), + sub_changes, + }; + + let mut change_map = HashMap::new(); + change_map.insert(base_version, change.clone()); + recipes.iter().for_each(|rc| { + // clone change + let mut change_clone = change.clone(); + + match rc.search_pd_by_no_country_code(rp1.clone().productCode) { + Some(pdr) => { + change_clone.raw_new = pdr.clone().recipes; + } + None => {} + } + change_clone.get_changes(); + change_map.insert( + rc.MachineSetting + .get_config_number() + .as_i64() + .unwrap() + .clone(), + change_clone, + ); + }); + + ( + rp1.clone().productCode, + serde_json::to_value(change_map).unwrap(), + ) + }) + .collect(); + + shards +} diff --git a/src/recipe_functions/common.rs b/src/recipe_functions/common.rs index f61d932..e811575 100644 --- a/src/recipe_functions/common.rs +++ b/src/recipe_functions/common.rs @@ -66,7 +66,7 @@ pub fn create_recipe_path(country_path: &str, version: usize) -> String { path } -pub fn read_all_files_in_directory(directory_path: &str) -> Vec { +pub fn get_all_files_in_directory(directory_path: &str) -> Vec { let mut files = Vec::new(); let dir = std::fs::read_dir(directory_path).unwrap();