diff --git a/frontend/examples/csv-test/POLLFNR_WOECHENTLICH.txt b/frontend/examples/csv-test/POLLFNR_WOECHENTLICH.txt new file mode 120000 index 0000000..09930c0 --- /dev/null +++ b/frontend/examples/csv-test/POLLFNR_WOECHENTLICH.txt @@ -0,0 +1 @@ +../../../backend/data/POLLFNR_WOECHENTLICH.txt \ No newline at end of file diff --git a/frontend/examples/csv-test/locales/csv-test.json b/frontend/examples/csv-test/locales/csv-test.json new file mode 100644 index 0000000..8740de9 --- /dev/null +++ b/frontend/examples/csv-test/locales/csv-test.json @@ -0,0 +1,98 @@ +{ + "err.lang.not_found": { + "de_DE.UTF-8": "Konnte die Umgebungsvarialbe LANG nicht auslesen", + "de": "Konnte die Umgebungsvarialbe LANG nicht auslesen", + "en": "Couldn't read LANG" + }, + "err.user.not_found": { + "fr": "Utilisateur introuvable: $email, $id", + "de-DE.UTF-8": "Anwender nicht gefunden: $email, $id", + "de": "Anwender nicht gefunden: $email, $id", + "en": "User not found: $email, $id" + }, + "main.started": { + "de_DE.UTF-8": "Programmlogik starten", + "de": "Programmlogik starten", + "en": "Program logic started" + }, + "main.finished": { + "de_DE.UTF-8": "Programmlogik beendet", + "de": "Programmlogik beendet", + "en": "Program logic finished" + }, + "parse.arguments": { + "de_DE.UTF-8": "Programmargumente prüfen", + "de": "Programmargumente prüfen", + "en": "Parsing arguments" + }, + "parse.environment": { + "de_DE.UTF-8": "Umgebungsvariablen prüfen", + "de": "Umgebungsvariablen prüfen", + "en": "Parsing environment" + }, + "parse.results": { + "de_DE.UTF-8": "Ergebnisse der Konfigurations-Parameterprüfung", + "de": "Ergebnisse der Konfigurationsparameterprüfung", + "en": "Config parsing results" + }, + "config.name": { + "de_DE.UTF-8": "Konfigurationswert für", + "de": "Konfigurationswert für", + "en": "Config Value for" + }, + "config.name.lang": { + "de_DE.UTF-8": "Sprach-Code", + "de": "Sprach-Code", + "en": "Language code" + }, + "config.name.verbositylevel": { + "de_DE.UTF-8": "Ausgabe-Ebene", + "de": "Ausgabe-Ebene", + "en": "verbosity level" + }, + "config.name.environment": { + "de_DE.UTF-8": "Umgebungsvariablen", + "de": "Umgebungsvariablen", + "en": "environment" + }, + "config.name.configfile": { + "de_DE.UTF-8": "Konfigurations-Datei", + "de": "Konfigurations-Datei", + "en": "config file" + }, + "config.name.dbdriver": { + "de_DE.UTF-8": "Datenbank-Treiber", + "de": "Datenbank-Treiber", + "en": "database driver" + }, + "csv_import.started": { + "de_DE.UTF-8": "importieren von einer csv-datei gestartet", + "de": "importieren von einer csv-datei gestartet", + "en": "import from a csv-file started" + }, + "csv_import.finished": { + "de_DE.UTF-8": "importieren von einer csv-datei beendet", + "de": "importieren von einer csv-datei beendet", + "en": "import from a csv-file finished" + }, + "csv_export.started": { + "de_DE.UTF-8": "exportieren in eine csv-datei gestartet", + "de": "exportieren in eine csv-datei gestartet", + "en": "export to csv-file started" + }, + "csv_export.finished": { + "de_DE.UTF-8": "exportieren in eine csv-datei beendet", + "de": "exportieren in eine csv-datei beendet", + "en": "export to csv-file finished" + }, + "state.started": { + "de_DE.UTF-8": "gestartet", + "de": "gestartet", + "en": "started" + }, + "state.finished": { + "de_DE.UTF-8": "beendet", + "de": "beended", + "en": "finished" + } +} diff --git a/frontend/examples/csv-test/main.rs b/frontend/examples/csv-test/main.rs new file mode 100644 index 0000000..e2fe737 --- /dev/null +++ b/frontend/examples/csv-test/main.rs @@ -0,0 +1,242 @@ +/* + * 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; +use std::env; +use std::{error::Error, process}; +use tracing::{debug, trace, Level}; + +//use crate::db::data::CsvImportRecord; + +// 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, +} + +#[derive(Debug, Deserialize)] +pub struct CsvImportRecord { + // dion => Allianz Dion: 1-9 + // policy_code => Policy Typ: "AS" + // policy_number => Versicherungsscheinnummer: "1515735810" + pub dion: String, + pub policy_code: String, + pub policy_number: String, +} + +#[derive(Default, Debug, Deserialize)] +pub struct RecordList { + records: Vec +} + +/// 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<(), Box> { + 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); + trace!(target: "csv-test", process = ?res, state = ?state); + + // Note: slash syntax also works on Windows! + let path = Path::new(p); + + // only create files with a '.txt' extensions + //let extension = path.extension(); + // match extension { + // //Some(String) => println!("file extension ok!"), + // //_ => extension = OsStr::new("txt") + // _ => println!("got file extension {:?}", extension) + // }; + + // open the file + let file = File::open(path)?; + + trace!(target: "csv-export", extension = ?path.extension(), file = ?file); + state = t!("state.finished", lang); + res = t!("csv_import.finished", lang); + trace!(target: "csv-test", process = ?res, state = ?state); + + Ok(()) +} + +/// 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, 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); + + // Build the CSV reader and iterate over each record. + let mut csv_reader = csv::ReaderBuilder::new() + .has_headers(false) + .delimiter(b' ') + .comment(Some(b'#')) + //.has_headers(true) + //.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); + } + //for result in csv_reader.records() { + let mut count = 0; + for result in csv_reader.deserialize() { + // The iterator yields Result, so we check the + // error here. + //let record = result?; + let _record: CsvImportRecord = result?; + //println!("{:?}", record); + count +=1; + } + + let dt_end: DateTime = Local::now(); + let duration = dt_end.signed_duration_since(dt_start); + println!("Duration: {:#?}", duration); + //let seconds = duration.secs; + //println!("Seconds: {:#?}", seconds); + 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, duration.seconds()) + Ok(count) +} + +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(); + + // initialize logger + //env_logger::init(); + //info!("Commencing the import Test-Run!"); + + 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); + + // importing policy code elements from csv-file + let mut csv_import_path = v.get::("import_file").unwrap(); + match import(&mut csv_import_path, &lang) { + Ok(count) => { + println!("Imported {:?} records", count); + } + Err(err) => { + println!("error running Csv-Test: {}", err); + process::exit(1); + } + } + + // 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(()) +} diff --git a/frontend/examples/csv-test/parse_args.rs b/frontend/examples/csv-test/parse_args.rs new file mode 100644 index 0000000..104e19a --- /dev/null +++ b/frontend/examples/csv-test/parse_args.rs @@ -0,0 +1,201 @@ +/// The given Viperus structure will be muted according to the +/// processed default, environment and commandline arguments + +// parse CLI commandline arguments with clap +use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg}; + +//use log::{debug, info, trace, warn}; +use std::env; +use tracing::trace; +use viperus::Viperus; + +// Parse the commandline arguments and preset default values +// Precedence: defaults -> config-file -> environment -> commandline +pub fn parse_args(v: &mut Viperus) -> Result<(), Box> { + if cfg!(feature = "fmt-clap") { + trace!(target: "Viperus", "Viperus feature 'fmt-clap' enabled."); + println!("Using feature fmt-clap"); + } + + // preset default key/value pairs (lowest priority) + v.add_default("config_file", String::from("csv_import.ron")); + v.add_default("import_file", String::from("POLLFNR_WOECHENTLICH.txt")); + v.add_default("export_file", String::from("")); + v.add_default("to_email_address_file", String::from("Allianz RA-Hotline ")); + v.add_default("from_email_address_file", String::from("Allianz-Hotline RA-Hiedemann ")); + //v.add_default("username", String::from("nctalkbot")); + //v.add_default("password", String::from("botpassword")); + v.add_default("verbose", 0); + + + // CLI arguments are defined inline + let matches = App::new("csv-test") + .name(crate_name!()) + .version(crate_version!()) + .author(crate_authors!()) + .about(crate_description!()) + .after_help(" +Testprogramm: Allianz Online-Beratung Im/Export CSV-Daten +Direct-Call via IVR-System (Interactive Voice Response) +SMR Deckungssummen-Prüfung: 089 92529 60211 +SMR Unerledigt: 089 92529 60222") + .template( + "\ + {bin} v{version} +{about} + +{all-args} + +(C) 2020 {author} +{after-help}", + ) + .arg( + Arg::with_name("configFile") + .short("c") + .long("configFile") + .value_name("FILE") + .help("Select a config file") + .default_value("csv_import.ron") + .takes_value(true), + ) + .arg( + Arg::with_name("importFile") + .short("i") + .long("importFile") + .help("Select source file for the csv-import") + .default_value("POLLFNR_WOECHENTLICH.txt") + .takes_value(true), + ) + .arg( + Arg::with_name("exportFile") + .short("e") + .long("exportFile") + .help("Select target file for the csv-export") + .default_value("RA-Hiedemann_DirectCall.txt") + .takes_value(true), + ) + .arg( + Arg::with_name("toEmailAddress") + .short("t") + .long("toEmailAddress") + .help("Select the target email-address (To:)") + .default_value("Allianz RA-Hotline ") + .takes_value(true), + ) + .arg( + Arg::with_name("fromEmailAddress") + .short("f") + .long("fromEmailAddress") + .help("Select the sender email-address (From:)") + .default_value("Allianz-Hotline RA-Hiedemann ") + .takes_value(true), + ) + // .arg( + // Arg::with_name("username") + // .short("u") + // .long("username") + // .help("Sets username") + // .takes_value(true), + // ) + // .arg( + // Arg::with_name("password") + // .short("P") + // .long("password") + // .help("Sets password") + // .takes_value(true), + // ) + .arg( + Arg::with_name("verbose") + .short("v") + .long("verbose") + .help("Sets verbosity level") + .multiple(true), + ) + .get_matches(); + + if matches.occurrences_of("verbose") > 0 { + // clap is using i64, viperus i32 + let n = matches.occurrences_of("verbose") as i32; + v.add("verbose", n); + } + + // preset the prefix for relevant environment variables ("ADVOTRACKER_") + let mut env_prefix: String = crate_name!().to_uppercase(); + env_prefix.push_str("_"); + v.set_env_prefix(&env_prefix); + + // respect dotenv environment (e.g for testing) + // -> overwrites the preset default values + println!( + "RUST_LOG={}", + dotenv::var("RUST_LOG").unwrap_or_else(|_| String::from("None")) + ); + + // enable caching and automatic update of environment values + if cfg!(feature = "fmt-cache") { + v.cache(true); + } + if cfg!(feature = "fmt-env") { + v.automatic_env(true); + } + + // load user selected call arguments + // -> overwrites values given via environment variables + v.load_clap(matches)?; + + // bond the clap names to camel_case rust variable names + v.bond_clap("configFile", "config_file"); + v.bond_clap("importFile", "import_file"); + v.bond_clap("exportFile", "export_file"); + v.bond_clap("toEmailAddress", "to_email_address"); + v.bond_clap("fromEmailAddress", "from_email_address"); + //v.bond_clap("username", "username"); + //v.bond_clap("password", "password"); + v.bond_clap("verbose", "verbose"); + + trace!("verbose {:?}", v.get::("verbose").unwrap()); + if v.get::("verbose").unwrap() > 0 { + println!( + "config_file: {:?}", + v.get::("config_file").unwrap_or_default() + ); + println!( + "import_file: {:?}", + v.get::("import_file").unwrap_or_default() + ); + println!( + "export_file: {:?}", + v.get::("export_file").unwrap_or_default() + ); + println!( + "to_email_address: {:?}", + v.get::("to_email_address").unwrap_or_default() + ); + println!( + "from_email_address: {:?}", + v.get::("from_email_address").unwrap_or_default() + ); + // println!( + // "username: {:?}", + // v.get::("username").unwrap_or_default() + // ); + // println!( + // "password: {:?}", + // v.get::("password").unwrap_or_default() + // ); // only for testing now + + println!( + "verbosity level: {:?}", + v.get::("verbose").unwrap_or_default() + ); + } + + if v.get::("verbose").unwrap() > 1 { + println!("\nEnvironment:"); + for (key, value) in env::vars() { + println!("{}={}", key, value); + } + } + + Ok(()) +}