v0.1 init
This commit is contained in:
commit
8068810af6
18 changed files with 3859 additions and 0 deletions
BIN
.DS_Store
vendored
Normal file
BIN
.DS_Store
vendored
Normal file
Binary file not shown.
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/target
|
||||||
402
Cargo.lock
generated
Normal file
402
Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,402 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android-tzdata"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android_system_properties"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.2.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5f4ac86a9e5bc1e2b3449ab9d7d3a6a405e3d1bb28d7b9be8614f55846ae3766"
|
||||||
|
dependencies = [
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.41"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
|
||||||
|
dependencies = [
|
||||||
|
"android-tzdata",
|
||||||
|
"iana-time-zone",
|
||||||
|
"js-sys",
|
||||||
|
"num-traits",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation-sys"
|
||||||
|
version = "0.8.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.8.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone"
|
||||||
|
version = "0.1.63"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
|
||||||
|
dependencies = [
|
||||||
|
"android_system_properties",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"iana-time-zone-haiku",
|
||||||
|
"js-sys",
|
||||||
|
"log",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone-haiku"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.77"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.172"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libtbr"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"log",
|
||||||
|
"rayon",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.95"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"rayon-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon-core"
|
||||||
|
version = "1.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.219"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.219"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.140"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.101"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"rustversion",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"log",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-core"
|
||||||
|
version = "0.61.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46ec44dc15085cea82cf9c78f85a9114c463a369786585ad2882d1ff0b0acf40"
|
||||||
|
dependencies = [
|
||||||
|
"windows-implement",
|
||||||
|
"windows-interface",
|
||||||
|
"windows-link",
|
||||||
|
"windows-result",
|
||||||
|
"windows-strings",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-implement"
|
||||||
|
version = "0.60.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-interface"
|
||||||
|
version = "0.59.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-result"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4b895b5356fc36103d0f64dd1e94dfa7ac5633f1c9dd6e80fe9ec4adef69e09d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-strings"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2a7ab927b2637c19b3dbe0965e75d8f2d30bdd697a1516191cad2ec4df8fb28a"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
11
Cargo.toml
Normal file
11
Cargo.toml
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "libtbr"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
chrono = "0.4.41"
|
||||||
|
log = "0.4.27"
|
||||||
|
rayon = "1.10.0"
|
||||||
|
serde = { version = "1.0.219", features = ["derive", "serde_derive"] }
|
||||||
|
serde_json = "1.0.140"
|
||||||
BIN
src/.DS_Store
vendored
Normal file
BIN
src/.DS_Store
vendored
Normal file
Binary file not shown.
3
src/lib.rs
Normal file
3
src/lib.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod models;
|
||||||
|
pub mod previews;
|
||||||
|
pub mod recipe_functions;
|
||||||
2
src/models/mod.rs
Normal file
2
src/models/mod.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod recipe;
|
||||||
|
pub mod recipev2;
|
||||||
1897
src/models/recipe.rs
Normal file
1897
src/models/recipe.rs
Normal file
File diff suppressed because it is too large
Load diff
93
src/models/recipev2.rs
Normal file
93
src/models/recipev2.rs
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::Value;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::models::recipe::*;
|
||||||
|
use crate::recipe_functions::common::*;
|
||||||
|
|
||||||
|
/// Version 2 of `models::Recipe`
|
||||||
|
///
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct VersionRecipe {
|
||||||
|
pub currentVersion: Value,
|
||||||
|
pub knownVersions: Vec<Value>,
|
||||||
|
pub recipesByCategory: HashMap<String, Vec<RecipeV2>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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(RECIPES_DIR);
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn break_down_recipe_into_category(recipes: Vec<Recipe>) -> HashMap<String, Vec<RecipeV2>> {
|
||||||
|
let mut result = HashMap::new();
|
||||||
|
|
||||||
|
// Steps
|
||||||
|
// 1.
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Based on `models::Recipe`, this excluded some fields
|
||||||
|
/// for more efficient serialization and deserialization.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct RecipeV2 {
|
||||||
|
pub Description: Option<String>,
|
||||||
|
pub LastChange: Option<Value>,
|
||||||
|
pub id: Value,
|
||||||
|
pub isUse: bool,
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub otherName: Option<String>,
|
||||||
|
pub productCode: String,
|
||||||
|
pub recipes: Vec<RecipeListWithHistory>,
|
||||||
|
pub ToppingSet: Option<Vec<MenuToppingListWithHistory>>,
|
||||||
|
pub total_time: Value,
|
||||||
|
pub changeLog: Vec<HashMap<String, Value>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
pub struct RecipeListWithHistory {
|
||||||
|
pub recipeList: RecipeList,
|
||||||
|
pub history: Vec<HashMap<String, Value>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
pub struct MenuToppingListWithHistory {
|
||||||
|
pub menuToppingList: MenuToppingList,
|
||||||
|
pub history: Vec<HashMap<String, Value>>,
|
||||||
|
}
|
||||||
97
src/previews/csvf.rs
Normal file
97
src/previews/csvf.rs
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
// format function
|
||||||
|
//
|
||||||
|
// target csv
|
||||||
|
|
||||||
|
use log::warn;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub enum AutoFixSizeStrategy {
|
||||||
|
#[default]
|
||||||
|
Strict,
|
||||||
|
FillHeaderToMatchRow {
|
||||||
|
default_value: String,
|
||||||
|
},
|
||||||
|
FillRowToMatchHeader {
|
||||||
|
default_value: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct TableConfig {
|
||||||
|
pub allow_row_length_exceeds_header: bool,
|
||||||
|
pub auto_fix_size: bool,
|
||||||
|
pub auto_fix_size_strategy: AutoFixSizeStrategy,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
pub struct Table {
|
||||||
|
pub header: Vec<String>,
|
||||||
|
pub rows: Vec<Vec<String>>,
|
||||||
|
pub config: TableConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Table {
|
||||||
|
pub fn new() -> Table {
|
||||||
|
Table::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_config(&mut self, config: TableConfig) {
|
||||||
|
self.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_header(&mut self, header: &str) {
|
||||||
|
self.header.push(header.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_header(&mut self, header: Vec<&str>) {
|
||||||
|
self.header = header
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.to_string())
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_current_header(self) -> Vec<String> {
|
||||||
|
self.header
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_row(&mut self, mut row: Vec<String>) {
|
||||||
|
if (self.clone().config.auto_fix_size) {
|
||||||
|
match self.clone().config.auto_fix_size_strategy {
|
||||||
|
AutoFixSizeStrategy::Strict => {
|
||||||
|
if (row.len() != self.header.len()) {
|
||||||
|
panic!("Row length does not match header length");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AutoFixSizeStrategy::FillHeaderToMatchRow { default_value } => {
|
||||||
|
if (row.len() > self.header.len()) {
|
||||||
|
warn!("Row length exceeds header length");
|
||||||
|
}
|
||||||
|
self.add_header(&default_value);
|
||||||
|
}
|
||||||
|
AutoFixSizeStrategy::FillRowToMatchHeader { default_value } => {
|
||||||
|
if (row.len() < self.header.len()) {
|
||||||
|
warn!("Row length is less than header length");
|
||||||
|
}
|
||||||
|
row.push(default_value.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.rows.push(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_column_desciption(&mut self, row_pos: usize, row: Vec<String>) {
|
||||||
|
self.rows.insert(row_pos, row);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_save_file(&mut self, output: &str) {
|
||||||
|
let mut csv = String::new();
|
||||||
|
csv.push_str(&self.header.join("\t"));
|
||||||
|
csv.push('\n');
|
||||||
|
for row in &self.rows {
|
||||||
|
csv.push_str(&row.join("\t"));
|
||||||
|
csv.push('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
std::fs::write(output, csv).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
255
src/previews/header.rs
Normal file
255
src/previews/header.rs
Normal file
|
|
@ -0,0 +1,255 @@
|
||||||
|
// sample header for csvf
|
||||||
|
pub struct Headers {}
|
||||||
|
|
||||||
|
impl Headers {
|
||||||
|
// will be deprecated by next version
|
||||||
|
pub fn get_recipe_table_sgp_24022025() -> Vec<&'static str> {
|
||||||
|
[
|
||||||
|
"Name",
|
||||||
|
"ProductCode",
|
||||||
|
"521001",
|
||||||
|
"521002",
|
||||||
|
"521004",
|
||||||
|
"521005",
|
||||||
|
"521006",
|
||||||
|
"521003",
|
||||||
|
"521007",
|
||||||
|
"521008",
|
||||||
|
"521009",
|
||||||
|
"521101",
|
||||||
|
"521102",
|
||||||
|
"521103",
|
||||||
|
"521104",
|
||||||
|
"521105",
|
||||||
|
"521106",
|
||||||
|
"521107",
|
||||||
|
"521108",
|
||||||
|
"521020",
|
||||||
|
"521021",
|
||||||
|
"521022",
|
||||||
|
"521023",
|
||||||
|
"521024",
|
||||||
|
"521025",
|
||||||
|
"521027",
|
||||||
|
"521028",
|
||||||
|
"521029",
|
||||||
|
"521030",
|
||||||
|
"521200",
|
||||||
|
"521201",
|
||||||
|
"521202",
|
||||||
|
"521203",
|
||||||
|
"521205",
|
||||||
|
"521206",
|
||||||
|
"521207",
|
||||||
|
"521208",
|
||||||
|
"521210",
|
||||||
|
"521211",
|
||||||
|
"521212",
|
||||||
|
"521213",
|
||||||
|
"521214",
|
||||||
|
"521215",
|
||||||
|
"521216",
|
||||||
|
"521301",
|
||||||
|
"521041",
|
||||||
|
"521042",
|
||||||
|
"521043",
|
||||||
|
"521044",
|
||||||
|
"521045",
|
||||||
|
"521046",
|
||||||
|
"521048",
|
||||||
|
"521047",
|
||||||
|
"521049",
|
||||||
|
"521050",
|
||||||
|
"521051",
|
||||||
|
"521052",
|
||||||
|
"521053",
|
||||||
|
"521054",
|
||||||
|
"521055",
|
||||||
|
"521056",
|
||||||
|
"521057",
|
||||||
|
"521058",
|
||||||
|
"521059",
|
||||||
|
"521061",
|
||||||
|
"521060",
|
||||||
|
"521062",
|
||||||
|
"521063",
|
||||||
|
"521064",
|
||||||
|
"521065",
|
||||||
|
"521066",
|
||||||
|
"521067",
|
||||||
|
"521068",
|
||||||
|
"521069",
|
||||||
|
"521070",
|
||||||
|
"521071",
|
||||||
|
"521072",
|
||||||
|
"521073",
|
||||||
|
"2101",
|
||||||
|
"1031",
|
||||||
|
"521032",
|
||||||
|
"521036",
|
||||||
|
"521034",
|
||||||
|
"521035",
|
||||||
|
"521033",
|
||||||
|
"529501",
|
||||||
|
"529505",
|
||||||
|
"529502",
|
||||||
|
"529503",
|
||||||
|
"529506",
|
||||||
|
"529601",
|
||||||
|
"529701",
|
||||||
|
"529702",
|
||||||
|
"1",
|
||||||
|
"9100",
|
||||||
|
"8101",
|
||||||
|
"8102",
|
||||||
|
"8111",
|
||||||
|
"8112",
|
||||||
|
"8113",
|
||||||
|
"8114",
|
||||||
|
"8115",
|
||||||
|
"8116",
|
||||||
|
"8117",
|
||||||
|
"8118",
|
||||||
|
"8119",
|
||||||
|
"8120",
|
||||||
|
"8001",
|
||||||
|
"8002",
|
||||||
|
"8888",
|
||||||
|
"8889",
|
||||||
|
"200000",
|
||||||
|
"201001",
|
||||||
|
"201002",
|
||||||
|
"100004",
|
||||||
|
"522201",
|
||||||
|
"521217",
|
||||||
|
"521038",
|
||||||
|
"521037",
|
||||||
|
]
|
||||||
|
.to_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_recipe_table_dubai_16052025() -> Vec<&'static str> {
|
||||||
|
[
|
||||||
|
"Name",
|
||||||
|
"ProductCode",
|
||||||
|
"531001",
|
||||||
|
"531002",
|
||||||
|
"531004",
|
||||||
|
"531005",
|
||||||
|
"531006",
|
||||||
|
"531003",
|
||||||
|
"531007",
|
||||||
|
"531008",
|
||||||
|
"531009",
|
||||||
|
"531101",
|
||||||
|
"531102",
|
||||||
|
"531103",
|
||||||
|
"531104",
|
||||||
|
"531105",
|
||||||
|
"531106",
|
||||||
|
"531107",
|
||||||
|
"531108",
|
||||||
|
"531020",
|
||||||
|
"531021",
|
||||||
|
"531022",
|
||||||
|
"531023",
|
||||||
|
"531024",
|
||||||
|
"531025",
|
||||||
|
"531027",
|
||||||
|
"531028",
|
||||||
|
"531029",
|
||||||
|
"531030",
|
||||||
|
"531200",
|
||||||
|
"531201",
|
||||||
|
"531202",
|
||||||
|
"531203",
|
||||||
|
"531205",
|
||||||
|
"531206",
|
||||||
|
"531207",
|
||||||
|
"531208",
|
||||||
|
"531210",
|
||||||
|
"531211",
|
||||||
|
"531212",
|
||||||
|
"531213",
|
||||||
|
"531214",
|
||||||
|
"531215",
|
||||||
|
"531216",
|
||||||
|
"531301",
|
||||||
|
"531041",
|
||||||
|
"531042",
|
||||||
|
"531043",
|
||||||
|
"531044",
|
||||||
|
"531045",
|
||||||
|
"531046",
|
||||||
|
"531048",
|
||||||
|
"531047",
|
||||||
|
"531049",
|
||||||
|
"531050",
|
||||||
|
"531051",
|
||||||
|
"531052",
|
||||||
|
"531053",
|
||||||
|
"531054",
|
||||||
|
"531055",
|
||||||
|
"531056",
|
||||||
|
"531057",
|
||||||
|
"531058",
|
||||||
|
"531059",
|
||||||
|
"531061",
|
||||||
|
"531060",
|
||||||
|
"531062",
|
||||||
|
"531063",
|
||||||
|
"531064",
|
||||||
|
"531065",
|
||||||
|
"531066",
|
||||||
|
"531067",
|
||||||
|
"531068",
|
||||||
|
"531069",
|
||||||
|
"531070",
|
||||||
|
"531071",
|
||||||
|
"531072",
|
||||||
|
"531073",
|
||||||
|
"2101",
|
||||||
|
"1031",
|
||||||
|
"531032",
|
||||||
|
"531036",
|
||||||
|
"531034",
|
||||||
|
"531035",
|
||||||
|
"531033",
|
||||||
|
"539501",
|
||||||
|
"539505",
|
||||||
|
"539502",
|
||||||
|
"539503",
|
||||||
|
"539506",
|
||||||
|
"539601",
|
||||||
|
"539701",
|
||||||
|
"539702",
|
||||||
|
"1",
|
||||||
|
"9100",
|
||||||
|
"8101",
|
||||||
|
"8102",
|
||||||
|
"8111",
|
||||||
|
"8112",
|
||||||
|
"8113",
|
||||||
|
"8114",
|
||||||
|
"8115",
|
||||||
|
"8116",
|
||||||
|
"8117",
|
||||||
|
"8118",
|
||||||
|
"8119",
|
||||||
|
"8120",
|
||||||
|
"8001",
|
||||||
|
"8002",
|
||||||
|
"8888",
|
||||||
|
"8889",
|
||||||
|
"200000",
|
||||||
|
"201001",
|
||||||
|
"201002",
|
||||||
|
"100004",
|
||||||
|
"532201",
|
||||||
|
"531217",
|
||||||
|
"531038",
|
||||||
|
"531037",
|
||||||
|
]
|
||||||
|
.to_vec()
|
||||||
|
}
|
||||||
|
}
|
||||||
2
src/previews/mod.rs
Normal file
2
src/previews/mod.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod csvf;
|
||||||
|
pub mod header;
|
||||||
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