v0.1 init
This commit is contained in:
commit
8068810af6
18 changed files with 3859 additions and 0 deletions
199
src/recipe_functions/common.rs
Normal file
199
src/recipe_functions/common.rs
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::models::{self, recipe::Recipe01};
|
||||
use std::collections::HashMap;
|
||||
use std::{fs::File, io::Read};
|
||||
|
||||
pub const RECIPES_DIR: &str = "/Users/pkntd/Codes/mk4/cofffeemachineConfig/";
|
||||
|
||||
pub fn create_recipe_model_from_file(path: String) -> models::recipe::Recipe {
|
||||
// println!("create_recipe_model_from_file: {}", path);
|
||||
let mut file = File::open(path).unwrap();
|
||||
// println!("check file: {}", file.metadata().unwrap().is_file());
|
||||
let mut data = String::new();
|
||||
file.read_to_string(&mut data).unwrap();
|
||||
|
||||
models::recipe::Recipe::init(data)
|
||||
}
|
||||
|
||||
pub fn create_recipe_path(country_path: &str, version: usize) -> String {
|
||||
let mut path = String::from(RECIPES_DIR);
|
||||
|
||||
path.push_str(country_path);
|
||||
path.push_str("/coffeethai02_");
|
||||
|
||||
// version
|
||||
path.push_str(&version.to_string());
|
||||
|
||||
if country_path != "tha" {
|
||||
path.push('_');
|
||||
path.push_str(country_path);
|
||||
}
|
||||
|
||||
path.push_str(".json");
|
||||
|
||||
path
|
||||
}
|
||||
|
||||
pub fn read_all_files_in_directory(directory_path: &str) -> Vec<String> {
|
||||
let mut files = Vec::new();
|
||||
let dir = std::fs::read_dir(directory_path).unwrap();
|
||||
|
||||
for entry in dir {
|
||||
let entry = entry.unwrap();
|
||||
let path = entry.path();
|
||||
if path.is_file() {
|
||||
files.push(path.to_string_lossy().into_owned());
|
||||
}
|
||||
}
|
||||
|
||||
files
|
||||
}
|
||||
|
||||
pub fn filter_files_by_pattern(files: Vec<String>, pattern: &str) -> Vec<String> {
|
||||
files
|
||||
.into_iter()
|
||||
.filter(|file| file.contains(pattern))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_pd_prefix(country: &str) -> String {
|
||||
match country {
|
||||
"mys" => "12".to_string(),
|
||||
"sgp" => "52".to_string(),
|
||||
"aus" => "51".to_string(),
|
||||
"tha" => "12".to_string(),
|
||||
"hkg" => "54".to_string(),
|
||||
"dubai" => "53".to_string(),
|
||||
"uae" => "53".to_string(),
|
||||
_ => "12".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mat_prefix(country: &str) -> String {
|
||||
match country {
|
||||
"sgp" => "52".to_string(),
|
||||
"aus" => "51".to_string(),
|
||||
"hkg" => "54".to_string(),
|
||||
"dubai" => "53".to_string(),
|
||||
"uae" => "53".to_string(),
|
||||
_ => "".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_allowed_change_mat_prefix(mat_id: Value) -> bool {
|
||||
let mat_id_i = mat_id.as_i64().unwrap();
|
||||
|
||||
let exception_list = [
|
||||
2101, 1031, 1, 9100, 8101, 8102, 8111, 8112, 8113, 8114, 8115, 8116, 8117, 8118, 8119,
|
||||
8120, 8001, 8002, 8888, 8889, 200000, 201001, 201002, 100004, 0,
|
||||
];
|
||||
|
||||
!exception_list.contains(&mat_id_i)
|
||||
}
|
||||
|
||||
pub fn change_prefix_by_country(
|
||||
country: &str,
|
||||
mut recipes: Vec<Option<Recipe01>>,
|
||||
) -> Vec<Recipe01> {
|
||||
let mut result = Vec::new();
|
||||
|
||||
// get prefix first
|
||||
let prefix = get_pd_prefix(country);
|
||||
let mat_prefix = get_mat_prefix(country);
|
||||
|
||||
// run
|
||||
let updated = recipes
|
||||
.par_iter_mut()
|
||||
.update(|x| {
|
||||
if let Some(x) = x {
|
||||
// change productCode
|
||||
let mut new_product_code = x.productCode.clone();
|
||||
|
||||
new_product_code =
|
||||
format!("{}-{}", prefix, new_product_code.split_once('-').unwrap().1);
|
||||
x.productCode = new_product_code;
|
||||
// run through recipes
|
||||
let mut new_recipes_format = Vec::new();
|
||||
for ele in x.clone().recipes {
|
||||
let mut eled = ele.clone();
|
||||
if check_allowed_change_mat_prefix(eled.clone().materialPathId) {
|
||||
let new_f = format!("{}{}", mat_prefix, eled.clone().materialPathId);
|
||||
eled.materialPathId = Value::Number(new_f.parse::<i64>().unwrap().into());
|
||||
}
|
||||
new_recipes_format.push(eled);
|
||||
}
|
||||
x.recipes = new_recipes_format;
|
||||
|
||||
if let Some(s) = x.clone().SubMenu {
|
||||
//
|
||||
let cls = s.clone();
|
||||
for mut sl in cls.clone() {
|
||||
let mut new_sl_pd = sl.productCode.clone();
|
||||
new_sl_pd = format!("{}-{}", prefix, new_sl_pd.split_once('-').unwrap().1);
|
||||
sl.productCode = new_sl_pd;
|
||||
|
||||
let mut new_recipes_sub = Vec::new();
|
||||
for ele in sl.recipes {
|
||||
let mut eled = ele.clone();
|
||||
if check_allowed_change_mat_prefix(eled.clone().materialPathId) {
|
||||
let new_f =
|
||||
format!("{}{}", mat_prefix, eled.clone().materialPathId);
|
||||
eled.materialPathId =
|
||||
Value::Number(new_f.parse::<i64>().unwrap().into());
|
||||
}
|
||||
new_recipes_sub.push(eled);
|
||||
}
|
||||
sl.recipes = new_recipes_sub;
|
||||
}
|
||||
x.SubMenu = Some(cls.clone());
|
||||
}
|
||||
}
|
||||
})
|
||||
.filter(|x| x.is_some())
|
||||
.map(|x| x.clone().unwrap())
|
||||
.collect_vec_list();
|
||||
|
||||
// check by update
|
||||
for x in updated.iter() {
|
||||
result.extend_from_slice(x);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn valid_country_name() -> Vec<&'static str> {
|
||||
vec!["mys", "sgp", "aus", "tha", "hkg", "dubai", "uae"]
|
||||
}
|
||||
|
||||
pub fn grep_latest_versions(dir_path: &str) -> Result<HashMap<String, usize>, std::io::Error> {
|
||||
let mut vs = HashMap::new();
|
||||
|
||||
// open dir
|
||||
let entries = std::fs::read_dir(dir_path)?
|
||||
.map(|res| res.map(|e| e.path()))
|
||||
.collect::<Result<Vec<_>, std::io::Error>>()?;
|
||||
|
||||
for e in entries {
|
||||
let path = e.clone().to_str().unwrap().to_string();
|
||||
let mut cl_path = path.clone();
|
||||
let path_split = path.split("/").collect::<Vec<&str>>();
|
||||
let dir_name = path_split[path_split.len() - 1];
|
||||
|
||||
if valid_country_name().contains(&dir_name) {
|
||||
cl_path.push_str("/version");
|
||||
|
||||
// read filename
|
||||
let mut file = File::open(cl_path)?;
|
||||
let mut data = String::new();
|
||||
file.read_to_string(&mut data).unwrap();
|
||||
|
||||
vs.insert(dir_name.to_string(), data.parse::<usize>().unwrap());
|
||||
}
|
||||
|
||||
// expect dir with country
|
||||
}
|
||||
|
||||
Ok(vs)
|
||||
}
|
||||
592
src/recipe_functions/import.rs
Normal file
592
src/recipe_functions/import.rs
Normal file
|
|
@ -0,0 +1,592 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use log::{debug, info};
|
||||
use serde_json::{Number, Value};
|
||||
|
||||
use super::common::*;
|
||||
use crate::{
|
||||
models::recipe::{self, PartialRecipe},
|
||||
previews::{csvf::*, header::Headers},
|
||||
};
|
||||
|
||||
pub fn import_menu(main_version: usize, country_name: &str, into_version: usize) {
|
||||
let latest_recipe_thai = create_recipe_model_from_file(create_recipe_path("tha", main_version));
|
||||
|
||||
// let mut latest_recipe_mys =
|
||||
// create_recipe_model_from_file(create_recipe_path("mys", *mys_version.unwrap()));
|
||||
|
||||
let mut latest_recipe_sgp =
|
||||
create_recipe_model_from_file(create_recipe_path(country_name, into_version));
|
||||
|
||||
let diff_list = latest_recipe_thai.list_diff_pd_ignore_country_code(&latest_recipe_sgp);
|
||||
|
||||
let mut menu_no_sugar_table = Table::new();
|
||||
let insert_later_config = TableConfig {
|
||||
allow_row_length_exceeds_header: false,
|
||||
auto_fix_size: true,
|
||||
auto_fix_size_strategy: AutoFixSizeStrategy::FillRowToMatchHeader {
|
||||
default_value: "".to_string(),
|
||||
},
|
||||
};
|
||||
|
||||
let mut sg_price_table = menu_no_sugar_table.clone();
|
||||
let mut recipe_table = menu_no_sugar_table.clone();
|
||||
|
||||
menu_no_sugar_table.set_config(insert_later_config.clone());
|
||||
sg_price_table.set_config(insert_later_config.clone());
|
||||
recipe_table.set_config(insert_later_config.clone());
|
||||
|
||||
menu_no_sugar_table.set_header(["Product Code", "Name"].to_vec());
|
||||
sg_price_table.set_header(["SKU", "English Name", "Thai Name"].to_vec());
|
||||
|
||||
recipe_table.set_header(Headers::get_recipe_table_sgp_24022025());
|
||||
|
||||
let modified_recipes = change_prefix_by_country(
|
||||
country_name,
|
||||
latest_recipe_thai.search_multi_in_parallel(diff_list),
|
||||
);
|
||||
|
||||
// also clone mat setting
|
||||
// ** Will removed this
|
||||
let from_tha_mat_settings = [
|
||||
latest_recipe_thai.search_material_settings("2201".to_string()),
|
||||
latest_recipe_thai.search_material_settings("1217".to_string()),
|
||||
latest_recipe_thai.search_material_settings("1038".to_string()),
|
||||
latest_recipe_thai.search_material_settings("1037".to_string()),
|
||||
];
|
||||
|
||||
let mut formatted_mat_settings = Vec::new();
|
||||
from_tha_mat_settings.iter().for_each(|mat_smth| {
|
||||
let new_mat_smth = *mat_smth;
|
||||
if let Some(x) = new_mat_smth {
|
||||
let mut cl_x = x.clone();
|
||||
let mut new_mat_id = cl_x.id.clone().as_i64().unwrap().to_string();
|
||||
// new_product_code = format!("{}-{}", "sgp", new_product_code.split_once('-').unwrap().1);
|
||||
new_mat_id = format!("{}{}", get_mat_prefix(country_name), new_mat_id);
|
||||
cl_x.id = Value::Number(Number::from_str(&new_mat_id).unwrap()).clone();
|
||||
// debug!("new formatted mat settings [SGP] -> {:?}", cl_x);
|
||||
formatted_mat_settings.push(cl_x);
|
||||
}
|
||||
});
|
||||
|
||||
latest_recipe_sgp
|
||||
.MaterialSetting
|
||||
.extend(formatted_mat_settings.clone());
|
||||
|
||||
// debug!("modified recipes: {:#?}", modified_recipes);
|
||||
|
||||
// process: generate table
|
||||
modified_recipes.iter().enumerate().for_each(|mr| {
|
||||
menu_no_sugar_table.add_row(vec![
|
||||
mr.1.productCode.clone(),
|
||||
mr.1.name.clone().unwrap().to_string().replace("\n", "\\n"),
|
||||
]);
|
||||
sg_price_table.add_row(vec![
|
||||
mr.1.productCode.clone(),
|
||||
mr.1.otherName
|
||||
.clone()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.replace("\n", "\\n"),
|
||||
mr.1.name.clone().unwrap().to_string().replace("\n", "\\n"),
|
||||
]);
|
||||
|
||||
// Pre process for recipe table
|
||||
|
||||
// let mut missing_support_mats = Vec::new();
|
||||
let binding = mr.1.clone();
|
||||
let mat_list = binding
|
||||
.recipes
|
||||
.iter()
|
||||
.filter(|r| r.isUse)
|
||||
.map(|r| {
|
||||
// debug!(
|
||||
// "r->{:?}",
|
||||
// r.materialPathId
|
||||
// .clone()
|
||||
// .as_number()
|
||||
// .unwrap()
|
||||
// .as_i64()
|
||||
// .unwrap()
|
||||
// .to_string()
|
||||
// );
|
||||
|
||||
r.materialPathId
|
||||
.as_number()
|
||||
.unwrap()
|
||||
.as_i64()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
})
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let mut mapping_mats = Vec::new();
|
||||
// let mut missing_header = Vec::new();
|
||||
|
||||
mat_list.iter().enumerate().for_each(|(idx, mat)| {
|
||||
// if (!mat.parse::<i64>().unwrap().eq(&0i64)
|
||||
// && !recipe_table
|
||||
// .clone()
|
||||
// .get_current_header()
|
||||
// .contains(&mat.to_string()))
|
||||
// {
|
||||
// // missing_support_mats.push((idx, mat.to_string()));
|
||||
// // add to header
|
||||
|
||||
// // recipe_table.add_header(mat);
|
||||
// missing_header.push(mat);
|
||||
// }
|
||||
|
||||
// skip zero
|
||||
if !mat.eq("0") {
|
||||
mapping_mats.push((idx, mat));
|
||||
}
|
||||
});
|
||||
|
||||
// if (!missing_header.is_empty()) {
|
||||
// missing_header.iter().for_each(|mh| {
|
||||
// if (!recipe_table
|
||||
// .clone()
|
||||
// .get_current_header()
|
||||
// .contains(mh.clone()))
|
||||
// {
|
||||
// recipe_table.add_header(mh);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
let inter_name_empty = mr.1.otherName.clone().unwrap().is_empty();
|
||||
let used_name = if inter_name_empty {
|
||||
mr.1.name.clone().unwrap().to_string().replace("\n", "\\n")
|
||||
} else {
|
||||
mr.1.otherName
|
||||
.clone()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.replace("\n", "\\n")
|
||||
};
|
||||
let mut used_mats = vec![used_name, mr.1.productCode.clone()];
|
||||
|
||||
// add by using mat
|
||||
|
||||
// let mut reorder1 = Vec::new();
|
||||
// let mut add_last = Vec::new();
|
||||
|
||||
// reorder by header
|
||||
// recipe_table.clone().get_current_header().iter().for_each(|h| {
|
||||
// let mut found = false;
|
||||
// mapping_mats.iter().for_each(|(idx, mat)| {
|
||||
// if (h.eq(mat)) {
|
||||
// found = true;
|
||||
// reorder1.push((idx, mat));
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
|
||||
// recipe
|
||||
let mut recipe_brew_values_only = vec![0; recipe_table.clone().get_current_header().len()];
|
||||
let recipe_list = mr.1.clone().recipes;
|
||||
|
||||
let mut add_more_to_blender = 0i64;
|
||||
|
||||
for repl in recipe_list.iter() {
|
||||
let current_mat = repl.materialPathId.as_number().unwrap().as_i64().unwrap();
|
||||
if repl.isUse {
|
||||
recipe_table
|
||||
.clone()
|
||||
.get_current_header()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.for_each(|(header_idx, header)| {
|
||||
if header_idx > 1 && header.eq(¤t_mat.to_string()) {
|
||||
// search mat settings for further info
|
||||
let mat_setting =
|
||||
latest_recipe_sgp.search_material_settings(current_mat.to_string());
|
||||
|
||||
if let Some(mat_s) = mat_setting {
|
||||
match mat_s.clone().get_definition_type() {
|
||||
recipe::MaterialType::Bean => {
|
||||
recipe_brew_values_only[header_idx] +=
|
||||
repl.powderGram.as_i64().unwrap();
|
||||
|
||||
// check if there is mix order
|
||||
if repl
|
||||
.MixOrder
|
||||
.as_number()
|
||||
.unwrap()
|
||||
.as_i64()
|
||||
.unwrap()
|
||||
.eq(&0i64)
|
||||
{
|
||||
// add to blender index
|
||||
add_more_to_blender = repl.waterYield.as_i64().unwrap();
|
||||
}
|
||||
}
|
||||
recipe::MaterialType::Syrup => {
|
||||
// debug
|
||||
println!(
|
||||
"SYRUP [{}]: {} --> {} = {:?}",
|
||||
mr.1.productCode.clone(),
|
||||
header_idx,
|
||||
repl.materialPathId.clone(),
|
||||
repl.syrupGram
|
||||
);
|
||||
|
||||
recipe_brew_values_only[header_idx] +=
|
||||
repl.syrupGram.as_i64().unwrap();
|
||||
}
|
||||
recipe::MaterialType::Soda => {
|
||||
recipe_brew_values_only[header_idx] +=
|
||||
repl.syrupGram.as_i64().unwrap();
|
||||
}
|
||||
recipe::MaterialType::Powder => {
|
||||
recipe_brew_values_only[header_idx] +=
|
||||
repl.powderGram.as_i64().unwrap();
|
||||
}
|
||||
recipe::MaterialType::Water => todo!(),
|
||||
recipe::MaterialType::Ice => {
|
||||
recipe_brew_values_only[header_idx] +=
|
||||
repl.waterCold.as_i64().unwrap();
|
||||
}
|
||||
recipe::MaterialType::Cup => {
|
||||
recipe_brew_values_only[header_idx] += 1;
|
||||
}
|
||||
recipe::MaterialType::Lid => {}
|
||||
recipe::MaterialType::Straw => {}
|
||||
recipe::MaterialType::Whipper => {
|
||||
let water_yield = repl.waterYield.as_i64().unwrap();
|
||||
let water_cold = repl.waterCold.as_i64().unwrap();
|
||||
|
||||
recipe_brew_values_only[header_idx] +=
|
||||
water_yield + water_cold;
|
||||
|
||||
if add_more_to_blender > 0i64 {
|
||||
recipe_brew_values_only[header_idx] +=
|
||||
add_more_to_blender;
|
||||
add_more_to_blender = 0i64;
|
||||
}
|
||||
}
|
||||
recipe::MaterialType::Leaves => {}
|
||||
recipe::MaterialType::Clean => {
|
||||
recipe_brew_values_only[header_idx] +=
|
||||
repl.waterYield.as_i64().unwrap();
|
||||
}
|
||||
recipe::MaterialType::CleanV2 => {
|
||||
recipe_brew_values_only[header_idx] +=
|
||||
repl.waterYield.as_i64().unwrap();
|
||||
}
|
||||
recipe::MaterialType::Unknown => {
|
||||
recipe_brew_values_only[header_idx] += 0i64;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// suspect new mat or not found
|
||||
debug!("mat error [NotFound/Unknown] ==> {}", repl.materialPathId);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (vidx, val) in recipe_brew_values_only.iter().enumerate() {
|
||||
// skip first 2
|
||||
if vidx <= 1 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if val > &0i64 {
|
||||
used_mats.push(val.to_string());
|
||||
} else {
|
||||
used_mats.push(String::from(""));
|
||||
}
|
||||
}
|
||||
|
||||
// debug!("Used Materials: \n{:?}", used_mats);
|
||||
|
||||
recipe_table.add_row(used_mats);
|
||||
});
|
||||
|
||||
// Exclusive only for SGP
|
||||
if country_name.eq_ignore_ascii_case("sgp") {
|
||||
menu_no_sugar_table.generate_save_file("./test_result/menu_no_sugar.csv");
|
||||
|
||||
// process: generate table for price
|
||||
sg_price_table.generate_save_file("./test_result/sg_price.csv");
|
||||
|
||||
// process: generate table for recipe
|
||||
recipe_table.generate_save_file("./test_result/sg_recipe.csv");
|
||||
|
||||
// final process
|
||||
|
||||
let mut pr = PartialRecipe::default();
|
||||
pr.sync_new(modified_recipes, formatted_mat_settings.clone());
|
||||
// export file for recipe json
|
||||
pr.export("./test_result/grep_into_modified_new_recipe_for_sgp.json");
|
||||
} else {
|
||||
sg_price_table.generate_save_file("./test_result/o1_price.csv");
|
||||
|
||||
// process: generate table for recipe
|
||||
recipe_table.generate_save_file("./test_result/o1_recipe.csv");
|
||||
|
||||
// final process
|
||||
|
||||
let mut pr = PartialRecipe::default();
|
||||
pr.sync_new(modified_recipes, formatted_mat_settings.clone());
|
||||
// export file for recipe json
|
||||
pr.export("./test_result/grep_into_modified_new_recipe_for_o1.json");
|
||||
}
|
||||
|
||||
// WIP other countries
|
||||
}
|
||||
|
||||
pub fn generate_recipe_sheet_table(country_name: &str, into_version: usize) {
|
||||
let mut latest_recipe =
|
||||
create_recipe_model_from_file(create_recipe_path(country_name, into_version));
|
||||
|
||||
let mut recipe_table = Table::new();
|
||||
let insert_later_config = TableConfig {
|
||||
allow_row_length_exceeds_header: false,
|
||||
auto_fix_size: true,
|
||||
auto_fix_size_strategy: AutoFixSizeStrategy::FillRowToMatchHeader {
|
||||
default_value: "".to_string(),
|
||||
},
|
||||
};
|
||||
|
||||
recipe_table.set_config(insert_later_config);
|
||||
|
||||
match country_name {
|
||||
"sgp" => {
|
||||
info!("SGP last update [24.02.2025]");
|
||||
recipe_table.set_header(Headers::get_recipe_table_sgp_24022025());
|
||||
}
|
||||
"dubai" => {
|
||||
info!("UAE last update [16.05.2025]");
|
||||
recipe_table.set_header(Headers::get_recipe_table_dubai_16052025());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let mut material_names = Vec::new();
|
||||
|
||||
for (idx, header) in recipe_table.clone().get_current_header().iter().enumerate() {
|
||||
if idx <= 1 {
|
||||
material_names.push("".to_string());
|
||||
} else {
|
||||
// do search from recipe first
|
||||
|
||||
let searched_result = latest_recipe.search_material_settings(header.to_string());
|
||||
|
||||
// material_names.push(header.to_string());
|
||||
match searched_result {
|
||||
Some(sr) => {
|
||||
if sr.clone().materialOtherName.is_some() {
|
||||
material_names.push(format!(
|
||||
"{} ({})",
|
||||
sr.clone().materialOtherName.unwrap(),
|
||||
header
|
||||
));
|
||||
} else {
|
||||
material_names.push(format!(
|
||||
"{} ({})",
|
||||
sr.clone().materialName.unwrap(),
|
||||
header
|
||||
));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
material_names.push(header.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
latest_recipe.clone().Recipe01.iter_mut().for_each(|rpl| {
|
||||
let binding = rpl.clone();
|
||||
let mat_list = binding
|
||||
.recipes
|
||||
.iter()
|
||||
.filter(|r| r.isUse)
|
||||
.map(|r| {
|
||||
r.materialPathId
|
||||
.as_number()
|
||||
.unwrap()
|
||||
.as_i64()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
})
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let mut mapping_materials = Vec::new();
|
||||
|
||||
mat_list.iter().enumerate().for_each(|(idx, mat)| {
|
||||
if !mat.eq("0") {
|
||||
mapping_materials.push((idx, mat));
|
||||
}
|
||||
});
|
||||
|
||||
let inter_name_empty = rpl.otherName.clone().unwrap().is_empty();
|
||||
let used_name = if inter_name_empty {
|
||||
rpl.name.clone().unwrap().to_string().replace("\n", "\\n")
|
||||
} else {
|
||||
rpl.otherName
|
||||
.clone()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.replace("\n", "\\n")
|
||||
};
|
||||
|
||||
let mut used_materials = vec![used_name, rpl.productCode.clone()];
|
||||
let mut recipe_brewing_values_only =
|
||||
vec![0; recipe_table.clone().get_current_header().len()];
|
||||
let recipe_list = rpl.clone().recipes;
|
||||
|
||||
let mut add_more_to_blender = 0i64;
|
||||
|
||||
for repl in recipe_list.iter() {
|
||||
let current_material = repl.materialPathId.as_number().unwrap().as_i64().unwrap();
|
||||
if repl.isUse {
|
||||
recipe_table
|
||||
.clone()
|
||||
.get_current_header()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.for_each(|(header_idx, header)| {
|
||||
if header_idx > 1 && header.eq(¤t_material.to_string()) {
|
||||
// search mat settings for further info
|
||||
|
||||
if rpl.productCode.clone().ends_with("0090") {
|
||||
debug!(
|
||||
"MAT [{}]: {} --> {} = pow:{:?},sy:{:?}",
|
||||
rpl.productCode.clone(),
|
||||
header_idx,
|
||||
repl.materialPathId.clone(),
|
||||
repl.powderGram,
|
||||
repl.syrupGram
|
||||
);
|
||||
}
|
||||
|
||||
let mat_setting = latest_recipe
|
||||
.search_material_settings(current_material.to_string());
|
||||
|
||||
if let Some(mat_s) = mat_setting {
|
||||
match mat_s.clone().get_definition_type() {
|
||||
recipe::MaterialType::Bean => {
|
||||
recipe_brewing_values_only[header_idx] +=
|
||||
repl.powderGram.as_i64().unwrap();
|
||||
|
||||
// check if there is mix order
|
||||
if repl
|
||||
.MixOrder
|
||||
.as_number()
|
||||
.unwrap()
|
||||
.as_i64()
|
||||
.unwrap()
|
||||
.eq(&0i64)
|
||||
{
|
||||
// add to blender index
|
||||
add_more_to_blender = repl.waterYield.as_i64().unwrap();
|
||||
}
|
||||
}
|
||||
recipe::MaterialType::Syrup => {
|
||||
// debug
|
||||
if rpl.productCode.clone().ends_with("0090") {
|
||||
println!(
|
||||
"SYRUP [{}]: {} --> {} = {:?}",
|
||||
rpl.productCode.clone(),
|
||||
header_idx,
|
||||
repl.materialPathId.clone(),
|
||||
repl.syrupGram
|
||||
);
|
||||
}
|
||||
|
||||
recipe_brewing_values_only[header_idx] +=
|
||||
repl.syrupGram.as_i64().unwrap();
|
||||
}
|
||||
recipe::MaterialType::Soda => {
|
||||
println!(
|
||||
"SODA [{}]: {} --> {} = {:?}",
|
||||
rpl.productCode.clone(),
|
||||
header_idx,
|
||||
repl.materialPathId.clone(),
|
||||
repl.syrupGram
|
||||
);
|
||||
|
||||
recipe_brewing_values_only[header_idx] +=
|
||||
repl.syrupGram.as_i64().unwrap();
|
||||
}
|
||||
recipe::MaterialType::Powder => {
|
||||
recipe_brewing_values_only[header_idx] +=
|
||||
repl.powderGram.as_i64().unwrap();
|
||||
}
|
||||
recipe::MaterialType::Water => todo!(),
|
||||
recipe::MaterialType::Ice => {
|
||||
recipe_brewing_values_only[header_idx] +=
|
||||
repl.waterCold.as_i64().unwrap();
|
||||
}
|
||||
recipe::MaterialType::Cup => {
|
||||
recipe_brewing_values_only[header_idx] += 1;
|
||||
}
|
||||
recipe::MaterialType::Lid => {}
|
||||
recipe::MaterialType::Straw => {}
|
||||
recipe::MaterialType::Whipper => {
|
||||
let water_yield = repl.waterYield.as_i64().unwrap();
|
||||
let water_cold = repl.waterCold.as_i64().unwrap();
|
||||
|
||||
recipe_brewing_values_only[header_idx] +=
|
||||
water_yield + water_cold;
|
||||
|
||||
if add_more_to_blender > 0i64 {
|
||||
recipe_brewing_values_only[header_idx] +=
|
||||
add_more_to_blender;
|
||||
add_more_to_blender = 0i64;
|
||||
}
|
||||
}
|
||||
recipe::MaterialType::Leaves => {}
|
||||
recipe::MaterialType::Clean => {
|
||||
recipe_brewing_values_only[header_idx] +=
|
||||
repl.waterYield.as_i64().unwrap();
|
||||
}
|
||||
recipe::MaterialType::CleanV2 => {
|
||||
recipe_brewing_values_only[header_idx] +=
|
||||
repl.waterYield.as_i64().unwrap();
|
||||
}
|
||||
recipe::MaterialType::Unknown => {
|
||||
if rpl.productCode.clone().ends_with("0090") {
|
||||
println!(
|
||||
"UNK [{}]: {} --> {} = {:?}",
|
||||
rpl.productCode.clone(),
|
||||
header_idx,
|
||||
repl.materialPathId.clone(),
|
||||
repl.syrupGram
|
||||
);
|
||||
}
|
||||
recipe_brewing_values_only[header_idx] += 0i64;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// suspect new mat or not found
|
||||
debug!("mat error [NotFound/Unknown] ==> {}", repl.materialPathId);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (vidx, val) in recipe_brewing_values_only.iter().enumerate() {
|
||||
if vidx <= 1 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if val > &0i64 {
|
||||
used_materials.push(val.to_string());
|
||||
} else {
|
||||
used_materials.push(String::from(""));
|
||||
}
|
||||
}
|
||||
|
||||
recipe_table.add_row(used_materials);
|
||||
});
|
||||
|
||||
// generate row description
|
||||
recipe_table.add_column_desciption(0, material_names);
|
||||
recipe_table.generate_save_file("./test_result/recipe.csv");
|
||||
}
|
||||
122
src/recipe_functions/list_fields.rs
Normal file
122
src/recipe_functions/list_fields.rs
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
use crate::models::recipe::{Recipe, Recipe01};
|
||||
|
||||
impl Recipe {
|
||||
/// The function `list_menu_product_code` returns a vector of product codes from a list of recipes.
|
||||
///
|
||||
/// Returns:
|
||||
///
|
||||
/// a vector of string references (`&str`) representing the product codes of the recipes in
|
||||
/// `Recipe01`.
|
||||
pub fn list_menu_product_code(&self) -> Vec<&str> {
|
||||
self.Recipe01
|
||||
.iter()
|
||||
.map(|r| r.productCode.as_str())
|
||||
.collect()
|
||||
}
|
||||
/// The function `list_material_settings` returns a vector of strings containing the IDs of material
|
||||
/// settings.
|
||||
///
|
||||
/// Returns:
|
||||
///
|
||||
/// The function `list_material_settings` returns a `Vec<String>` which contains the IDs of the material
|
||||
/// settings.
|
||||
|
||||
pub fn list_material_settings(&self) -> Vec<String> {
|
||||
self.MaterialSetting
|
||||
.iter()
|
||||
.map(|r| r.id.to_string())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn list_topping_list(&self) -> Vec<String> {
|
||||
self.Topping
|
||||
.ToppingList
|
||||
.iter()
|
||||
.map(|r| r.id.clone().to_string())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn list_topping_group(&self) -> Vec<String> {
|
||||
self.Topping
|
||||
.ToppingGroup
|
||||
.iter()
|
||||
.map(|r| r.groupID.clone().to_string())
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(feature = "diff")]
|
||||
pub fn list_diff_pd_between_recipes(&self, another_recipe: &Recipe) -> Vec<String> {
|
||||
let mut list = Vec::new();
|
||||
|
||||
self.Recipe01.iter().for_each(|r| {
|
||||
if !another_recipe.diff_pd_between_recipes(another_recipe, r.productCode.clone()) {
|
||||
list.push(r.productCode.clone());
|
||||
}
|
||||
});
|
||||
|
||||
list
|
||||
}
|
||||
|
||||
pub fn list_diff_pd_ignore_country_code(&self, another_recipe: &Recipe) -> Vec<String> {
|
||||
let mut list = Vec::new();
|
||||
|
||||
self.Recipe01.iter().for_each(|r| {
|
||||
// clean before search
|
||||
let rpl = r.clone();
|
||||
|
||||
// grep only recipe without country code
|
||||
let pd = rpl.productCode;
|
||||
let pd_split = pd.split("-").collect::<Vec<&str>>();
|
||||
|
||||
// combine without first elem
|
||||
let mut new_pd = String::new();
|
||||
for (i, ele) in pd_split.iter().enumerate() {
|
||||
if i != 0 {
|
||||
new_pd.push_str(ele);
|
||||
if i != pd_split.len() - 1 {
|
||||
new_pd.push('-');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !another_recipe
|
||||
.diff_pd_between_recipes_ignore_country(another_recipe, new_pd.clone())
|
||||
{
|
||||
list.push(new_pd.clone());
|
||||
}
|
||||
});
|
||||
|
||||
list
|
||||
}
|
||||
|
||||
pub fn find_recipe_by_material_path_id(&self, material_path_id: &str) -> Vec<&Recipe01> {
|
||||
// let mut res = Vec::new();
|
||||
|
||||
let total: Vec<&Recipe01> = self
|
||||
.Recipe01
|
||||
.iter()
|
||||
.filter(|r| {
|
||||
for ele in r.recipes.clone() {
|
||||
if ele.materialPathId.as_i64().unwrap()
|
||||
== material_path_id.parse::<i64>().unwrap()
|
||||
{
|
||||
// res.push(r.clone());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.collect();
|
||||
|
||||
total
|
||||
}
|
||||
}
|
||||
|
||||
impl Recipe01 {
|
||||
pub fn list_recipe_only_id(&self) -> Vec<String> {
|
||||
self.recipes
|
||||
.iter()
|
||||
.map(|rpl| rpl.materialPathId.clone().to_string())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
159
src/recipe_functions/merge.rs
Normal file
159
src/recipe_functions/merge.rs
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
use std::{
|
||||
fmt,
|
||||
fs::File,
|
||||
io::{self, Read},
|
||||
};
|
||||
|
||||
use super::list_fields;
|
||||
use crate::models::{
|
||||
merge_model::{MergeResult, MergeStatus},
|
||||
recipe::{CommonRecipeTrait, Recipe, Recipe01Trait},
|
||||
};
|
||||
|
||||
// compare between two recipe, and wait for user input to continue compare or quit
|
||||
// return status
|
||||
pub fn precise_merge(master: &mut Recipe, dev: &mut Recipe) -> MergeResult {
|
||||
let mut merge_status = MergeResult::new();
|
||||
|
||||
if master.Timestamp != dev.Timestamp {
|
||||
println!(
|
||||
"\n\nmaster.Timestamp => {:?} but dev.Timestamp => {:?}",
|
||||
master.Timestamp, dev.Timestamp
|
||||
);
|
||||
|
||||
// do change if need
|
||||
merge_status.accept(dev.Timestamp.clone());
|
||||
}
|
||||
|
||||
let machine_setting_diff = master.MachineSetting.compare(&dev.MachineSetting);
|
||||
println!("machine_setting_diff => {:?}", machine_setting_diff);
|
||||
|
||||
let product_codes = dev.list_menu_product_code();
|
||||
|
||||
let mut user_input = String::new();
|
||||
println!("** Recip01 ** Sensitive data, enter 'm' (manual) or 'a' (auto) for safe comparing");
|
||||
match io::stdin().read_line(&mut user_input) {
|
||||
Ok(_m) => {}
|
||||
Err(e) => {
|
||||
println!("{:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
user_input = user_input.trim_end().to_string();
|
||||
|
||||
println!(
|
||||
"mode: {}, valid: {}",
|
||||
user_input,
|
||||
user_input.eq_ignore_ascii_case("m")
|
||||
);
|
||||
|
||||
for product_code in product_codes {
|
||||
println!("** {} **", product_code);
|
||||
let menu_master = master.clone();
|
||||
let menu1 = menu_master.search_pd(product_code.clone().to_owned());
|
||||
let menu_dev = dev.clone();
|
||||
let menu2 = menu_dev.search_pd(product_code.clone().to_owned());
|
||||
|
||||
// exist in both
|
||||
if menu1.is_some() && menu2.is_some() {
|
||||
let menu_diff = menu1.unwrap().compare(menu2.unwrap());
|
||||
|
||||
if !menu_diff.is_empty()
|
||||
&& menu1.unwrap().clone().is_replacable(
|
||||
menu2.unwrap().to_owned(),
|
||||
user_input.eq_ignore_ascii_case("m"),
|
||||
)
|
||||
{
|
||||
master.update_menu(dev, product_code.clone().to_owned(), None);
|
||||
println!("updated menu => {:?}\t\t{:?}", product_code, menu_diff);
|
||||
merge_status.change(product_code.to_owned());
|
||||
}
|
||||
|
||||
// check content of diff
|
||||
} else if menu1.is_none() && menu2.is_some() {
|
||||
// new menu
|
||||
|
||||
let new_menu = menu2.unwrap();
|
||||
if user_input.contains('m') {
|
||||
println!(
|
||||
"found new menu from dev => {}, add to master ? (y,n)",
|
||||
product_code
|
||||
);
|
||||
|
||||
let mut user_input2 = String::new();
|
||||
match io::stdin().read_line(&mut user_input2) {
|
||||
Ok(_m) => {}
|
||||
Err(e) => {
|
||||
println!("{:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
if user_input.eq_ignore_ascii_case("y") {
|
||||
master.Recipe01.push(new_menu.clone());
|
||||
merge_status.add(product_code.to_owned());
|
||||
}
|
||||
} else {
|
||||
master.Recipe01.push(new_menu.clone());
|
||||
merge_status.add(product_code.to_owned());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let material_settings = dev.list_material_settings();
|
||||
|
||||
for material_setting in material_settings {
|
||||
println!("material_setting => {}", material_setting);
|
||||
let menu_master = master.clone();
|
||||
let menu1 = menu_master.search_material_settings(material_setting.to_string());
|
||||
let menu_dev = dev.clone();
|
||||
let menu2 = menu_dev.search_material_settings(material_setting.to_string());
|
||||
|
||||
// exist in both
|
||||
if menu1.is_some() && menu2.is_some() {
|
||||
let menu_diff = menu1.unwrap().compare(menu2.unwrap());
|
||||
|
||||
if !menu_diff.is_empty() {
|
||||
master.update_material_settings(dev, material_setting.to_string());
|
||||
println!(
|
||||
"updated material => {:?}\t\t{:?}",
|
||||
material_setting, menu_diff
|
||||
);
|
||||
merge_status.change(material_setting.to_string());
|
||||
}
|
||||
|
||||
// check content of diff
|
||||
} else if menu1.is_none() && menu2.is_some() {
|
||||
// new material
|
||||
|
||||
let new_material = menu2.unwrap();
|
||||
if user_input.contains('m') {
|
||||
println!(
|
||||
"found new material from dev => {}, add to master ? (y,n)",
|
||||
material_setting
|
||||
);
|
||||
|
||||
let mut user_input2 = String::new();
|
||||
match io::stdin().read_line(&mut user_input2) {
|
||||
Ok(_m) => {}
|
||||
Err(e) => {
|
||||
println!("{:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
if user_input.eq_ignore_ascii_case("y") {
|
||||
master.MaterialSetting.push(new_material.clone());
|
||||
merge_status.add(material_setting.to_string());
|
||||
}
|
||||
} else {
|
||||
master.MaterialSetting.push(new_material.clone());
|
||||
merge_status.add(material_setting.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
// not exist in both
|
||||
}
|
||||
|
||||
for topp_list in dev.Topping.ToppingList.clone() {}
|
||||
|
||||
merge_status.finalize()
|
||||
}
|
||||
4
src/recipe_functions/mod.rs
Normal file
4
src/recipe_functions/mod.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
pub mod translator;
|
||||
|
||||
pub mod common;
|
||||
pub mod import;
|
||||
20
src/recipe_functions/translator.rs
Normal file
20
src/recipe_functions/translator.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
use serde_json::Number;
|
||||
|
||||
use crate::models::recipe::Recipe;
|
||||
|
||||
pub fn matIdToName(source: &mut Recipe, material_path_id: &str) -> String {
|
||||
let mut name = String::new();
|
||||
|
||||
source.MaterialSetting.iter().for_each(|r| {
|
||||
if r.id.as_i64().is_some()
|
||||
&& r.id.as_i64().unwrap() == material_path_id.parse::<i64>().unwrap()
|
||||
{
|
||||
// name = r.materialName;
|
||||
if let Some(mat_name) = r.materialName.clone() {
|
||||
name = mat_name;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
name
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue