diff --git a/journal/src/helpers.rs b/journal/src/helpers.rs index 92944d3..e202e2d 100644 --- a/journal/src/helpers.rs +++ b/journal/src/helpers.rs @@ -1,6 +1,8 @@ -use crate::types::{CustomUnit, Estimation, IngestionDose, StandardIngestionDose}; +use crate::types::{ + CustomUnitDose, CustomUnitIngestionDose, Estimation, IngestionDose, StandardIngestionDose, +}; -fn add_standard_deviation( +pub fn add_standard_deviation( expectation_x: f64, standard_deviation_x: f64, expectation_y: f64, @@ -9,64 +11,52 @@ fn add_standard_deviation( let sum_x = standard_deviation_x.powi(2) + expectation_x.powi(2); let sum_y = standard_deviation_y.powi(2) + expectation_y.powi(2); - let expectations = expectation_x.powi(2) + expectation_y.powi(2); + let expectations = expectation_x.powi(2) * expectation_y.powi(2); let product_variance = (sum_x * sum_y) - expectations; - + println!("{} {} {} {}", sum_x, sum_y, expectations, product_variance); if product_variance > 0.0000001 { Some((product_variance.sqrt() * 100.0).round() / 100.0) } else { - println!( - "{} {} {} {}", - expectation_x, standard_deviation_x, expectation_y, standard_deviation_y, - ); - None } } -pub fn canonical_dose(dose: &IngestionDose, custom_unit: Option<&CustomUnit>) -> IngestionDose { - if let IngestionDose::Custom(dose) = dose { - let custom_unit = custom_unit.expect("custom unit not provided when dose is type custom"); - let custom_unit_dose = &custom_unit.dose; +pub fn calulate_custom_unit_ingestion_dose( + dose: &CustomUnitIngestionDose, + custom_unit_dose: &CustomUnitDose, +) -> IngestionDose { + let estimation = match (&dose.estimation, &custom_unit_dose.estimation) { + ( + &Estimation::StandardDeviation(dose_standard_deviation), + &Estimation::StandardDeviation(custom_unit_standard_deviation), + ) => { + let result = add_standard_deviation( + dose.dose, + dose_standard_deviation, + custom_unit_dose.dose, + custom_unit_standard_deviation, + ); + if let Some(result) = result { + Estimation::StandardDeviation(result) + } else { + Estimation::Estimate + } + } + (&Estimation::StandardDeviation(dose_standard_deviation), _) => { + Estimation::StandardDeviation(dose_standard_deviation) + } + (_, &Estimation::StandardDeviation(custom_unit_standard_deviation)) => { + Estimation::StandardDeviation(custom_unit_standard_deviation) + } + (Estimation::Precise, Estimation::Precise) => Estimation::Precise, + _ => Estimation::Estimate, + }; - let estimation = match (&dose.estimation, &custom_unit_dose.estimation) { - ( - &Estimation::StandardDeviation(dose_standard_deviation), - &Estimation::StandardDeviation(custom_unit_standard_deviation), - ) => { - let result = add_standard_deviation( - dose.dose, - dose_standard_deviation, - custom_unit_dose.dose, - custom_unit_standard_deviation, - ); - if let Some(result) = result { - Estimation::StandardDeviation(result) - } else { - Estimation::Estimate - } - } - (&Estimation::StandardDeviation(dose_standard_deviation), _) => { - Estimation::StandardDeviation(dose_standard_deviation) - } - (_, &Estimation::StandardDeviation(custom_unit_standard_deviation)) => { - Estimation::StandardDeviation(custom_unit_standard_deviation) - } - (Estimation::Precise, Estimation::Precise) => Estimation::Precise, - _ => Estimation::Estimate, - }; - - IngestionDose::Standard(StandardIngestionDose { - dose: dose.dose * custom_unit_dose.dose, - unit: custom_unit.original_unit.clone(), - estimation, - }) - } else { - dose.clone() - } -} - -pub fn canonical_unit(dose: &IngestionDose, custom_unit: Option<&CustomUnit>) -> String { - canonical_dose(dose, custom_unit).unit() + IngestionDose::Standard(StandardIngestionDose { + dose: dose.dose * custom_unit_dose.dose, + unit: custom_unit_dose.original_unit.clone(), + contains_unknown: false, + estimation, + }) } diff --git a/journal/src/lib.rs b/journal/src/lib.rs index 96c1cb9..52af0f9 100644 --- a/journal/src/lib.rs +++ b/journal/src/lib.rs @@ -1,2 +1,2 @@ -pub mod types; pub mod helpers; +pub mod types; diff --git a/journal/src/types.rs b/journal/src/types.rs index bf2b947..e684d25 100644 --- a/journal/src/types.rs +++ b/journal/src/types.rs @@ -1,11 +1,12 @@ use chrono::{DateTime, TimeZone, Utc}; use std::collections::HashMap; use std::fmt::{Debug, Display}; +use std::ops::Add; use std::str::FromStr; pub type AdministrationRoute = psychonaut_journal_types::AdministrationRoute; -#[derive(PartialEq, Debug, Clone)] +#[derive(PartialEq, Debug, Copy, Clone)] pub enum Estimation { Precise, Estimate, @@ -24,6 +25,23 @@ impl Estimation { } } +impl Add for Estimation { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (Estimation::StandardDeviation(x), Estimation::StandardDeviation(y)) => { + Estimation::StandardDeviation(x + y) + } + (Estimation::StandardDeviation(x), _) | (_, Estimation::StandardDeviation(x)) => { + Estimation::StandardDeviation(x) + } + (Estimation::Estimate, _) | (_, Estimation::Estimate) => Estimation::Estimate, + (Estimation::Precise, Estimation::Precise) => Estimation::Precise, + } + } +} + #[derive(Debug, Clone)] pub struct UnknownIngestionDose { pub unit: String, @@ -33,52 +51,60 @@ pub struct UnknownIngestionDose { pub struct StandardIngestionDose { pub dose: f64, pub unit: String, + pub contains_unknown: bool, pub estimation: Estimation, } -impl StandardIngestionDose { - pub fn is_estimate(&self) -> bool { - self.estimation.is_estimate() +impl Add for StandardIngestionDose { + type Output = Self; + + fn add(self, _: UnknownIngestionDose) -> Self::Output { + StandardIngestionDose { + dose: self.dose, + unit: self.unit, + contains_unknown: true, + estimation: self.estimation, + } + } +} + +impl Add for StandardIngestionDose { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + StandardIngestionDose { + dose: self.dose + rhs.dose, + unit: self.unit, + contains_unknown: self.contains_unknown || rhs.contains_unknown, + estimation: self.estimation + rhs.estimation, + } + } +} + +impl From for StandardIngestionDose { + fn from(value: CustomUnitDose) -> StandardIngestionDose { + StandardIngestionDose { + dose: value.dose, + unit: value.original_unit, + contains_unknown: false, + estimation: value.estimation, + } } } #[derive(Debug, Clone)] -pub struct CustomIngestionDose { +pub struct CustomUnitIngestionDose { pub dose: f64, pub unit: String, - pub original_unit: String, pub estimation: Estimation, pub custom_unit_id: i64, } -impl CustomIngestionDose { - pub fn is_estimate(&self) -> bool { - self.estimation.is_estimate() - } -} - #[derive(Debug, Clone)] pub enum IngestionDose { Unknown(UnknownIngestionDose), Standard(StandardIngestionDose), - Custom(CustomIngestionDose), -} - -impl IngestionDose { - pub fn is_estimate(&self) -> bool { - match self { - IngestionDose::Unknown(_) => false, - IngestionDose::Standard(dose) => dose.is_estimate(), - IngestionDose::Custom(dose) => dose.is_estimate(), - } - } - pub fn unit(&self) -> String { - match self { - IngestionDose::Unknown(dose) => dose.unit.clone(), - IngestionDose::Standard(dose) => dose.unit.clone(), - IngestionDose::Custom(dose) => dose.unit.clone(), - } - } + CustomUnit(CustomUnitIngestionDose), } #[derive(PartialEq, Debug, Clone)] @@ -136,6 +162,14 @@ pub struct Experience { pub ingestions: Vec, } +#[derive(Debug, Clone)] +pub struct CustomUnitDose { + pub dose: f64, + pub unit: String, + pub original_unit: String, + pub estimation: Estimation, +} + #[derive(Debug, Clone)] pub struct CustomUnit { pub id: i64, @@ -143,8 +177,7 @@ pub struct CustomUnit { pub substance_name: String, pub administration_route: AdministrationRoute, - pub dose: StandardIngestionDose, - pub original_unit: String, + pub dose: CustomUnitDose, pub creation_time: DateTime, pub is_archived: bool, @@ -163,9 +196,9 @@ impl Journal { self.custom_units.get(&id).cloned() } - pub fn maybe_get_custom_unit_for(&self, ingestion: &Ingestion) -> Option { + pub fn maybe_custom_unit(&self, ingestion: &Ingestion) -> Option { match &ingestion.dose { - IngestionDose::Custom(dose) => self.get_custom_unit(dose.custom_unit_id), + IngestionDose::CustomUnit(dose) => self.get_custom_unit(dose.custom_unit_id), _ => None, } } @@ -196,9 +229,10 @@ impl Journal { substance_name: custom_unit.substance_name, administration_route: custom_unit.administration_route, - dose: StandardIngestionDose { + dose: CustomUnitDose { dose: custom_unit.dose, unit: custom_unit.unit, + original_unit: custom_unit.original_unit, estimation: if custom_unit.is_estimate { if let Some(standard_deviation) = custom_unit.estimate_standard_deviation @@ -212,8 +246,6 @@ impl Journal { }, }, - original_unit: custom_unit.original_unit, - creation_time: from_unix_millis(custom_unit.creation_time), is_archived: custom_unit.is_archived, }, @@ -245,15 +277,14 @@ impl Journal { dose: { if let Some(dose) = ingestion.dose { if let Some(custom_unit_id) = ingestion.custom_unit_id { - IngestionDose::Custom(CustomIngestionDose { + let custom_unit = journal + .custom_units + .get(&custom_unit_id) + .expect("custom unit not found"); + + IngestionDose::CustomUnit(CustomUnitIngestionDose { dose, - unit: ingestion.unit.clone(), - original_unit: journal - .custom_units - .get(&custom_unit_id) - .expect("custom unit not found") - .original_unit - .clone(), + unit: custom_unit.dose.unit.clone(), estimation, custom_unit_id, }) @@ -262,6 +293,7 @@ impl Journal { dose, unit: ingestion.unit, estimation, + contains_unknown: false, }) } } else { diff --git a/journal_cli/src/commands/mod.rs b/journal_cli/src/commands/mod.rs index 899875e..d27ed5e 100644 --- a/journal_cli/src/commands/mod.rs +++ b/journal_cli/src/commands/mod.rs @@ -1 +1 @@ -pub mod print_experience; \ No newline at end of file +pub mod print_experience; diff --git a/journal_cli/src/commands/print_experience.rs b/journal_cli/src/commands/print_experience.rs index ddc853c..ee4ca4e 100644 --- a/journal_cli/src/commands/print_experience.rs +++ b/journal_cli/src/commands/print_experience.rs @@ -46,6 +46,8 @@ pub fn print_experience( .first_experience_by_title(&args.experience_title) .expect("could not find experience"); + //println!("{:#?}", &experience); + println!("{}", format_experience_title(&experience)); let substance_filter = args.substance_filter.clone(); diff --git a/journal_cli/src/display.rs b/journal_cli/src/display.rs index 23f785e..44f5f46 100644 --- a/journal_cli/src/display.rs +++ b/journal_cli/src/display.rs @@ -21,12 +21,14 @@ pub fn print_ingestion_log( } } - let custom_unit = journal.maybe_get_custom_unit_for(ingestion); + let custom_unit = journal.maybe_custom_unit(ingestion); + + //println!("{:#?} {:#?}", &ingestion, &custom_unit); println!( "{}|{}|{}|{}|{}", ingestion.substance_name, - format_ingestion_dose(&ingestion.dose, custom_unit.as_ref()), + format_ingestion_dose(&ingestion.dose, custom_unit.as_ref().map(|f| &f.dose)), format_ingestion_roa(ingestion, custom_unit.as_ref()), ingestion.consumer, format_ingestion_time(ingestion) diff --git a/journal_cli/src/formatting.rs b/journal_cli/src/formatting.rs index 3f1d430..1ca0c02 100644 --- a/journal_cli/src/formatting.rs +++ b/journal_cli/src/formatting.rs @@ -1,63 +1,60 @@ use journal::{ - helpers::canonical_dose, - types::{CustomUnit, Experience, Ingestion, IngestionDose, StandardIngestionDose}, + helpers::calulate_custom_unit_ingestion_dose, + types::{ + CustomUnit, CustomUnitDose, Estimation, Experience, Ingestion, IngestionDose, + StandardIngestionDose, + }, }; pub fn format_experience_title(experience: &Experience) -> String { format!("{}: {}", experience.title, experience.creation_time) } -pub fn format_ingestion_dose(dose: &IngestionDose, custom_unit: Option<&CustomUnit>) -> String { +pub fn format_ingestion_dose( + dose: &IngestionDose, + custom_unit_dose: Option<&CustomUnitDose>, +) -> String { match dose { IngestionDose::Unknown(dose) => format!("Unknown {}", dose.unit), IngestionDose::Standard(dose) => { - let is_estimate = match dose.estimation { - journal::types::Estimation::Precise => false, - journal::types::Estimation::Estimate - | journal::types::Estimation::StandardDeviation(_) => true, - }; + let is_estimate = dose.estimation.is_estimate(); let estimate = if is_estimate { "~" } else { "" }; - let standard_deviation = - if let journal::types::Estimation::StandardDeviation(standard_deviation) = - dose.estimation - { + let standard_deviation = { + if let Estimation::StandardDeviation(standard_deviation) = dose.estimation { format!("±{}", (standard_deviation * 100.0).round() / 100.0) } else { "".to_string() - }; + } + }; let dose_value = (dose.dose * 100.0).round() / 100.0; let unit = dose.unit.clone(); format!("{estimate}{dose_value}{standard_deviation} {unit}") } - IngestionDose::Custom(dose) => { - let custom_unit: &CustomUnit = custom_unit.expect("custom unit required for dose type"); + IngestionDose::CustomUnit(dose) => { + let custom_unit_dose = custom_unit_dose.expect("custom unit dose required"); - let canonical_dose = - canonical_dose(&IngestionDose::Custom(dose.clone()), Some(custom_unit)); + let ingestion_dose = calulate_custom_unit_ingestion_dose(dose, custom_unit_dose); - let canonical_dose = format_ingestion_dose(&canonical_dose, None); + let ingestion_dose = format_ingestion_dose(&ingestion_dose, None); - let custom_unit_dose_per = format_ingestion_dose( - &IngestionDose::Standard(StandardIngestionDose { - dose: custom_unit.dose.dose, - unit: custom_unit.original_unit.clone(), - estimation: custom_unit.dose.estimation.clone(), - }), + let dose_per_unit = format_ingestion_dose( + &IngestionDose::Standard(StandardIngestionDose::from(custom_unit_dose.clone())), None, ); let custom_unit_dose = format_ingestion_dose( &IngestionDose::Standard(StandardIngestionDose { dose: dose.dose, - unit: custom_unit.dose.unit.clone(), - estimation: custom_unit.dose.estimation.clone(), + unit: custom_unit_dose.unit.clone(), + contains_unknown: false, + estimation: dose.estimation, }), None, ); - format!("{canonical_dose} ({custom_unit_dose_per} * {custom_unit_dose})") + format!("{ingestion_dose} ({dose_per_unit} * {custom_unit_dose})") } } } diff --git a/journal_cli/src/utils.rs b/journal_cli/src/utils.rs index 2182337..f06aa38 100644 --- a/journal_cli/src/utils.rs +++ b/journal_cli/src/utils.rs @@ -11,4 +11,3 @@ pub fn load_journal(filename: &String) -> Result