/* * advotracker - Hotline tackingtool for Advocats * * Copyright 2020 Ralf Zerres * SPDX-License-Identifier: (0BSD or MIT) */ use chrono::{Local, DateTime}; use locales::t; //use serde::{Deserialize, Serialize}; use serde::Deserialize; use std::{ collections::HashMap, env, {error::Error, process}, //path::{Path, PathBuf}, }; use tracing::{debug, trace, Level}; use advotracker::data::structures::{PolicyCode, PolicyList, PolicyDataList, PolicyData}; // include modules mod parse_args; /// respect environment variables set in .env files /// located in the current call directory /// this is primarily used in testing scenarios (eg. debugging) #[derive(Debug, Deserialize)] struct Environment { test_lang: String, log: String, } /// export as csv format /// https://docs.rs/csv/1.1.3/csv/cookbook/index.html /// https://blog.burntsushi.net/csv/ fn export(p: &mut String, lang: &String) -> Result> { use std::fs::File; use std::path::Path; //use std::ffi::OsStr; use std::io::prelude::*; let mut res = t!("csv.export.started", lang); let mut state = t!("state.started", lang); let dt_start: DateTime = Local::now(); trace!(target: "csv-test", process = ?res, state = ?state, date_start = ?dt_start.to_string()); // Note: slash syntax also works on Windows! let path = Path::new(p); // open the file descriptor let mut file = File::create(path)?; trace!(target: "csv-export", extension = ?path.extension(), file = ?file); // Build the CSV writer and push selected records. //for result in csv_reader.records() { let mut count = 0; file.write_all(b"Allianz DirectCall Protokoll!")?; count += 1; let dt_end: DateTime = Local::now(); let duration = dt_end.signed_duration_since(dt_start); println!("Duration: {:#?}", duration); trace!(target: "csv-test", record_count = ?count, duration = ?duration); state = t!("state.finished", lang); res = t!("csv.import.finished", lang); trace!(target: "csv-test", process = ?res, state = ?state); Ok(count) } /// import from csv format /// https://docs.rs/csv/1.1.3/csv/cookbook/index.html /// https://blog.burntsushi.net/csv/ fn import(p: &mut String, data_list: &mut PolicyDataList, policy_numbers: &mut HashMap, lang: &String) -> Result> { use std::fs::File; use std::path::Path; use std::ffi::OsStr; let mut res = t!("csv.import.started", lang); let mut state = t!("state.started", lang); let dt_start: DateTime = Local::now(); trace!(target: "csv-test", process = ?res, state = ?state, date_start = ?dt_start.to_string()); // Note: slash syntax also workd on Windows! let path = Path::new(p); // must be a readable file trace!(target: "csv-test", path = ?path); assert_eq!(path.is_file(), true); // only accept files with '.txt' extensions let extension = path.extension(); assert_eq!(extension, Some(OsStr::new("txt"))); // open the file let file = File::open(path)?; trace!(target: "csv-test", extension = ?extension, file = ?file, data_list = ?data_list.name); // Build the CSV reader and iterate over each record. let mut csv_reader = csv::ReaderBuilder::new() .has_headers(true) .delimiter(b' ') .flexible(true) //.comment(Some(b'#')) //.from_reader(io::stdin()); //.from_path(path); .from_reader(file); { // We nest this call in its own scope because of lifetimes. let headers = csv_reader.headers()?; trace!(target: "csv-test", header = ?headers); } // Deserialize the input data and push result to target vector let mut count = 0; for result in csv_reader.deserialize() { // The iterator yields Result, so we check the // error here. let record: PolicyData = result?; //println!("{:?}", record); // WIP: write to redis backend // append the policy_number to the HashMap policy_numbers.insert(record.policy_number, record.policy_code); // push record as new vector elements data_list.push(record); count +=1; } let dt_end: DateTime = Local::now(); let duration = dt_end.signed_duration_since(dt_start); trace!(target: "csv-test", record_count = ?count, duration = ?duration); state = t!("state.finished", lang); res = t!("csv.import.finished", lang); trace!(target: "csv-test", process = ?res, state = ?state, date_stop = ?dt_end.to_string()); Ok(count) } #[allow(dead_code)] /// validate a given policy number /// result will return true or false fn is_valid(policy_number: &u64, policy_list: &PolicyDataList, policy_numbers: &mut HashMap, lang: &String) -> Result> { let mut res = t!("policy.validation.started", lang); let mut state = t!("state.started", lang); let dt_start: DateTime = Local::now(); trace!(target: "csv-test", process = ?res, state = ?state, policy_number = ?policy_number, policy_list = ?policy_list.name, elements = ?policy_list.policy_data.len(), date_start = ?dt_start.to_string()); //println!("Policy_number list: {:?}", policy_list.policy_data); // WIP: testcode to collect policy_number via iterator //println!("policy_number: {:?}", policy_list.policy_data[1].policy_number); //println!("Policy_number list: {:?}", policy_list.policy_data); // println!("policy_list: {:?} (with {:?} elements)", // policy_list.name, policy_list.policy_data.len()); // policy_list.into_iter() // .filter(|num| matches(w, w1)) // .clone // .collect::>() // let my_num = policy_list.policy_data.iter() // .map(|policy_number| { // policy_number // }) // .collect::>(); //println!("My policy_numbers: {:?}", my_num); // let mut my_policy_list = [ // [ ("Jack", 20), ("Jane", 23), ("Jill", 18), ("John", 19), ], // [ ("Bill", 17), ("Brenda", 16), ("Brad", 18), ("Barbara", 17), ] // ]; // let teams_in_score_order = teams // .iter_mut() // .map(|team| { // team.sort_by(|&a, &b| a.1.cmp(&b.1).reverse()); // team // }) // .collect::>(); //println!("Teams: {:?}", teams_in_score_order); // if policy_list.policy_data.iter().any(|v| v == policy_number) { // println!("{:?} contains {}", policy_list.name, policy_number); // } else { // println!("{:?} doesn't contain {}", policy_list, policy_number); //} // let test: Vec<_> = vec!["one", "two", "three"]; // let index: usize = test.iter().enumerate().find(|&r| r.1.to_string() == "two".to_string()).unwrap().0; // println!("index: {:?} -> {:?}", index, test[index]); //let index: usize = test.iter().enumerate().find(|&r| r.policy_number == "two".to_string()).unwrap().0; let mut result = false; match policy_numbers.get(&policy_number) { Some(&policy_code) => { let res = t!("policy.validation.success", lang); println!("policy_number: {} ({:?})", policy_number, policy_code); result = true; trace!(target: "csv-test", policy_number = ?policy_number, validation = ?res, policy_code = ?policy_code); }, _ => { let res = t!("policy.validation.failed", lang); //println!("Noop! Number isn't valid!"); println!("{:?}", res); trace!(target: "csv-test", policy_number = ?policy_number, validation = ?res); }, } let dt_end: DateTime = Local::now(); let duration = dt_end.signed_duration_since(dt_start); res = t!("policy.validation.finished", lang); state = t!("state.finished", lang); trace!(target: "csv-test", process = ?res, state = ?state, date_stop = ?dt_end.to_string(), duration = ?duration); Ok(result) } fn main() -> Result<(), Box> { use dotenv::dotenv; use parse_args::parse_args; //use std::process; //use std::sync::Arc; use tracing_subscriber::fmt; use viperus::Viperus; //static DEFAULT_FILTER: &str = concat!(module_path!(), "=", "trace"); // initialize the tracing subsystem // a drop in replacement for classical logging // reference: https://tokio.rs/blog/2019-08-tracing/ let span = tracing::span!(Level::TRACE, "csv-test"); let _enter = span.enter(); let subscriber = fmt::Subscriber::builder() .with_env_filter("trace") //.with_max_level(tracing::Level::DEBUG) .finish(); tracing::subscriber::with_default(subscriber, || { // get system environment let mut lang = env::var("LANG").unwrap_or("en".to_string()); let mut res = t!("parse.environment", lang); let mut state = t!("state.started", lang); trace!(target: "csv-test", message = ?res, state = ?state); //debug!(message = ?res, state = ?state); trace!(target: "csv-test", environment = "system", lang = ?lang); // testing environment: read from .env file dotenv().ok(); match envy::from_env::() { Ok(environment) => { if environment.test_lang != lang { lang = environment.test_lang; } }, Err(e) => { debug!(target: "csv-test", "{}", e); } } // how to handle unumplemented lang resources?? res = t!("parse.environment", lang); trace!(target: "csv-test", environment = "envy", lang = ?lang); state = t!("state.finished", lang); trace!(target: "csv-test", message = ?res, state = ?state); // initialize viperus structure let mut v = Viperus::new(); // parse commandline arguments res = t!("parse.arguments", lang); state = t!("state.started", lang); trace!(target: "csv-test", process = ?res, state = ?state); let _ = parse_args(&mut v); state = t!("state.finished", lang); trace!(target: "csv-test", process = ?res, state = ?state); //trace!(target: "Viperus", "Config results: {:?}", v); // main tasks res = t!("main.started", lang); state = t!("state.started", lang); trace!(target: "csv-test", process = ?res, state = ?state); // create policy structures let policy_list = PolicyList::new("Allianz Versicherungsnummen-Liste"); println!("Policy List {:?} ", policy_list.name); let mut policy_data = PolicyDataList::new("Allianz-Import 20200628"); println!("Policy Data List {:?} ", policy_data.name); let mut policy_numbers : HashMap = HashMap::new(); let mut csv_import_path = v.get::("import_file").unwrap(); match import(&mut csv_import_path, &mut policy_data, &mut policy_numbers, &lang) { Ok(count) => { println!("Imported {:?} records", count); } Err(err) => { println!("error running Csv-Test: {}", err); process::exit(1); } } // test if policy_number is_valid // type conversion (viperus String -> u64) let test_policy_number = v.get::("test_policy_number").unwrap().parse::().unwrap(); trace!(target: "csv-test", test_policy_number = ?test_policy_number); //match is_valid(&policy_number, &policy_data, &mut policy_numbers, &lang) { // Ok(true) => { // use Hashmap method 'get' to check if we have the given key match policy_numbers.get(&test_policy_number) { Some(&policy_code) => { let res = t!("policy.validation.success", lang); println!("{:?}", res); println!("policy_number: {} ({:?})", test_policy_number, policy_code); } _ => { let res = t!("policy.validation.failed", lang); println!("{:?}", res); //println!("Nuup! Number isn't valid!"); }, } // export policy code elements to csv-file let mut csv_export_path = v.get::("export_file").unwrap(); match export(&mut csv_export_path, &lang) { Ok(count) => { println!("Exported {:?} records", count); } Err(err) => { println!("error running CSV-Export: {}", err); process::exit(1); } } state = t!("state.finished", lang); res = t!("main.finished", lang); trace!(target: "csv-test", process = ?res, state = ?state); }); Ok(()) } #[test] fn test_policy_number() { // Takes a reference and returns Option<&V> let my_policy_numbers : [u64; 2] = [1511111111, 9999999993]; assert_eq!(my_policy_numbers, [1511111111, 9999999993]); //let mut csv_import_path = v.get::("import_file").unwrap(); let mut csv_import_path = String::from("data/POLLFNR_TEST.txt"); let mut policy_data = PolicyDataList::new("PolicyDataList"); let mut policy_numbers : HashMap = HashMap::new(); let lang = "en".to_string(); println!("import with Path: {:?} PolicyData: {:?} PolicyNumbers: {:?}, Lang: {:?}", csv_import_path, policy_data, policy_numbers, lang); let count = import(&mut csv_import_path, &mut policy_data, &mut policy_numbers, &lang); assert_eq!(count.unwrap(), 15498); }