From 5e4a4cc54fc5b706bc9f83297cb7d66dfc08e6ad Mon Sep 17 00:00:00 2001 From: chaos Date: Wed, 27 Nov 2024 19:10:02 +0000 Subject: [PATCH] update --- journal/src/calculate.rs | 124 ++++++++++++++++++ journal/src/journal/json_journal.rs | 106 +++++---------- journal/src/journal/mod.rs | 20 +-- journal/src/lib.rs | 1 + journal/src/types/administration_route.rs | 43 ++++++ journal/src/types/custom_unit.rs | 70 ---------- journal/src/types/ingestion.rs | 91 +------------ journal/src/types/ingestion/hydration.rs | 13 ++ journal/src/types/ingestion/kind.rs | 22 +--- journal/src/types/ingestion/substance.rs | 6 +- journal/src/types/ingestion/sustenance.rs | 6 +- journal/src/types/mod.rs | 5 +- journal/src/types/nutrients.rs | 2 +- journal/src/types/substance.rs | 4 +- journal/src/types/sustenance.rs | 2 +- journal_cli/src/commands/psychonaut/import.rs | 56 ++++++-- .../psychonaut/import/custom_substance.rs | 2 +- .../commands/psychonaut/import/custom_unit.rs | 48 ++++++- .../src/commands/psychonaut/import/types.rs | 36 +++++ journal_cli/src/display.rs | 89 +++++-------- 20 files changed, 416 insertions(+), 330 deletions(-) create mode 100644 journal/src/calculate.rs create mode 100644 journal/src/types/administration_route.rs create mode 100644 journal/src/types/ingestion/hydration.rs create mode 100644 journal_cli/src/commands/psychonaut/import/types.rs diff --git a/journal/src/calculate.rs b/journal/src/calculate.rs new file mode 100644 index 0000000..31096cd --- /dev/null +++ b/journal/src/calculate.rs @@ -0,0 +1,124 @@ +use std::{ + collections::HashMap, + ops::{Add, Mul}, +}; + +use crate::types::{Dose, HydrationIngestion, IngestionKind, Nutrients, SubstanceIngestion}; + +#[derive(Debug, Default, PartialEq)] +#[derive(serde::Serialize, serde::Deserialize)] + +pub struct CalculatedValues { + pub doses: HashMap, + pub nutrients: Nutrients, + pub hydration: HydrationIngestion, +} + +impl Add for CalculatedValues { + type Output = CalculatedValues; + + fn add(self, rhs: CalculatedValues) -> Self::Output { + let mut doses = self.doses.clone(); + let mut nutrients = self.nutrients.clone(); + let mut hydration = self.hydration.clone(); + + for (substance_id, substance_dose) in rhs.doses.iter() { + let dose = self + .doses + .get(substance_id) + .cloned() + .or(Some(Dose::new_precise(0.0))) + .unwrap(); + + doses.insert(substance_id.clone(), dose + substance_dose.clone()); + } + + nutrients.kcal += rhs.nutrients.kcal; + hydration.amount_ml += rhs.hydration.amount_ml; + + CalculatedValues { + doses, + nutrients, + hydration, + } + } +} + +impl Mul for CalculatedValues { + type Output = CalculatedValues; + + fn mul(self, rhs: f64) -> Self::Output { + let mut doses = self.doses.clone(); + let mut nutrients = self.nutrients.clone(); + let mut hydration = self.hydration.clone(); + + for (_substance_id, substance_dose) in doses.iter_mut() { + *substance_dose = substance_dose.clone() * Dose::new_precise(rhs); + } + + nutrients.kcal = (rhs * nutrients.kcal as f64).round() as u64; + hydration.amount_ml *= rhs; + + CalculatedValues { + doses, + nutrients, + hydration, + } + } +} + +impl CalculatedValues { + fn new() -> Self { + Self { + ..Default::default() + } + } +} + +pub fn calculate_ingestion_kind(kind: IngestionKind) -> CalculatedValues { + let mut values = CalculatedValues::new(); + + let mut ingestions: Vec = Vec::new(); + ingestions.push(kind); + + while !ingestions.is_empty() { + let current_ingestions = ingestions.clone(); + for kind in current_ingestions { + match kind { + IngestionKind::Unknown => {} + IngestionKind::Substance(substance) => { + let dose = values + .doses + .get(&substance.id) + .cloned() + .or(Some(Dose::new_precise(0.0))) + .unwrap(); + + values + .doses + .insert(substance.id.clone(), dose + substance.dose.clone()); + } + IngestionKind::Sustenance(sustenance) => { + let amount = sustenance.amount; + let mut sustenance = sustenance.sustenance.clone().unwrap(); + + for include in sustenance.includes.iter_mut() { + match include { + IngestionKind::Unknown => {} + IngestionKind::Substance(ingestion) => { + ingestion.dose = ingestion.dose.clone() * Dose::new_precise(amount) + } + IngestionKind::Sustenance(ingestion) => ingestion.amount *= amount, + IngestionKind::Hydration(ingestion) => ingestion.amount_ml *= amount, + } + ingestions.push(include.clone()); + } + } + IngestionKind::Hydration(hydration) => { + values.hydration.amount_ml += hydration.amount_ml; + } + } + } + } + values +} diff --git a/journal/src/journal/json_journal.rs b/journal/src/journal/json_journal.rs index d18e92c..18da89d 100644 --- a/journal/src/journal/json_journal.rs +++ b/journal/src/journal/json_journal.rs @@ -4,11 +4,11 @@ use chrono::Utc; use rand::Rng; use crate::types::{ - CustomUnit, Dose, Ingestion, IngestionKind, IngestionKinds, Session, Substance, - SubstanceIngestion, Sustenance, SustenanceIngestion, Unit, + CustomUnit, Dose, Ingestion, IngestionKind, Session, Substance, SubstanceIngestion, Sustenance, + SustenanceIngestion, Unit, }; -use super::{Journal, JournalType}; +use super::{Journal, JournalIntegrityChecks, JournalTrait, JournalType}; mod data; use data::JSONJournalData; @@ -67,7 +67,7 @@ impl JSONJournal { } } -impl Journal for JSONJournal { +impl JournalTrait for JSONJournal { fn save(&mut self) -> Result<(), Box> { self.save() } @@ -80,83 +80,40 @@ impl Journal for JSONJournal { self.data.ingestions.get(&id).cloned() } - fn get_substance(&self, name: &str) -> Option { - self.data.substances.get(name).cloned() + fn get_substance(&self, id: &str) -> Option { + self.data.substances.get(id).cloned() } - fn get_sustenance(&self, sustenance_id: &str) -> Option { - self.data.sustenances.get(sustenance_id).cloned() + fn get_sustenance(&self, id: &str) -> Option { + self.data.sustenances.get(id).cloned() } fn get_custom_unit(&self, id: i32) -> Option { self.data.custom_units.get(&id).cloned() } - fn resolve_ingestion_kind( - &self, - kind: &IngestionKind, - calculate: bool, - ) -> Option { - println!("{kind:#?}"); - let mut kinds: IngestionKinds = IngestionKinds::from(kind); + fn resolve_ingestion_kind(&self, kind: IngestionKind) -> Option { + let mut kind = kind; - if let IngestionKind::Sustenance(sustenance) = kind { - let includes = self.get_sustenance(&sustenance.sustenance_id)?.includes; - - for include in includes.into_iter() { - if calculate { - match include { - IngestionKind::Sustenance(ingestion) => { - kinds.includes.push(IngestionKinds { - ingestion: IngestionKind::Sustenance(SustenanceIngestion { - amount: sustenance.amount * ingestion.amount, - ..ingestion.clone() - }), - includes: self - .resolve_ingestion_kind( - &IngestionKind::Sustenance(ingestion), - calculate, - ) - .unwrap() - .includes, - }); - } - IngestionKind::Unknown => { - kinds - .includes - .push(IngestionKinds::from(&IngestionKind::Unknown)); - } - IngestionKind::Substance(ingestion) => { - kinds - .includes - .push(IngestionKinds::from(&IngestionKind::Substance( - SubstanceIngestion { - dose: Dose::new_precise(sustenance.amount) - * ingestion.dose.clone(), - ..ingestion - }, - ))); - } - IngestionKind::Hydration { amount } => { - kinds - .includes - .push(IngestionKinds::from(&IngestionKind::Hydration { - amount: (sustenance.amount * amount as f64).round() as u64, - })); - } - } - } else { - kinds.includes.push(IngestionKinds::from(&include)) - } + if let IngestionKind::Sustenance(ingestion_type) = &mut kind { + if let Some(mut sustenance) = self.get_sustenance(&ingestion_type.id) { + sustenance.includes = sustenance + .includes + .into_iter() + .map(|include| self.resolve_ingestion_kind(include).unwrap()) + .collect(); + ingestion_type.sustenance = Some(sustenance); } + } else if let IngestionKind::Substance(ingestion_type) = &mut kind { + ingestion_type.substance = self.get_substance(&ingestion_type.id); } - Some(kinds) + Some(kind) } fn resolve_substance_unit(&self, ingestion: &SubstanceIngestion) -> Option { match ingestion.custom_unit_id { - None => match self.get_substance(&ingestion.substance_name) { + None => match self.get_substance(&ingestion.id) { Some(substance) => Some(Unit::Simple(substance.unit)), None => None, }, @@ -180,14 +137,18 @@ impl Journal for JSONJournal { } fn create_substance(&mut self, substance: Substance) -> Substance { + assert!(substance.id == substance.id.replace(" ", "_").to_ascii_uppercase()); + self.data .substances - .insert(substance.name.clone(), substance.clone()); + .insert(substance.id.clone(), substance.clone()); substance } fn create_sustenance(&mut self, sustenance: Sustenance) -> Sustenance { + assert!(sustenance.id == sustenance.id.replace(" ", "_").to_ascii_uppercase()); + self.data .sustenances .insert(sustenance.id.clone(), sustenance.clone()); @@ -211,13 +172,14 @@ impl Journal for JSONJournal { self.data.sessions.insert(id, Session { id, ..session }); } - fn update_substance(&mut self, name: &str, substance: Substance, create: bool) { - assert!(self.data.substances.contains_key(name) || create); + fn update_substance(&mut self, id: &str, substance: Substance, create: bool) { + assert!(self.data.substances.contains_key(id) || create); + assert!(id == id.replace(" ", "_").to_ascii_uppercase()); self.data.substances.insert( - name.to_string(), + id.to_string(), Substance { - name: name.to_string(), + id: id.to_string(), ..substance }, ); @@ -225,6 +187,7 @@ impl Journal for JSONJournal { fn update_sustenance(&mut self, id: &str, sustenance: Sustenance, create: bool) { assert!(self.data.sustenances.contains_key(id) || create); + assert!(id == id.replace(" ", "_").to_ascii_uppercase()); self.data.sustenances.insert( id.to_string(), @@ -253,3 +216,6 @@ impl Journal for JSONJournal { .cloned() } } + +impl JournalIntegrityChecks for JSONJournal {} +impl Journal for JSONJournal {} diff --git a/journal/src/journal/mod.rs b/journal/src/journal/mod.rs index c1349c9..7fc475f 100644 --- a/journal/src/journal/mod.rs +++ b/journal/src/journal/mod.rs @@ -1,8 +1,8 @@ use crate::types::{ - CustomUnit, Ingestion, IngestionKind, IngestionKinds, Session, Substance, SubstanceIngestion, - Sustenance, Unit, + CustomUnit, Ingestion, IngestionKind, Session, Substance, SubstanceIngestion, Sustenance, Unit, }; +pub trait Journal: JournalTrait + JournalIntegrityChecks {} pub type JournalType = Box; #[derive(thiserror::Error, Debug)] @@ -11,20 +11,16 @@ pub enum JournalError { Unknown, } -pub trait Journal { +pub trait JournalTrait { fn save(&mut self) -> Result<(), Box>; fn get_session(&self, id: i32) -> Option; fn get_session_ingestions(&self, id: i32) -> Option>; - fn get_substance(&self, name: &str) -> Option; + fn get_substance(&self, id: &str) -> Option; fn get_sustenance(&self, id: &str) -> Option; fn get_custom_unit(&self, id: i32) -> Option; - fn resolve_ingestion_kind( - &self, - ingestion: &IngestionKind, - calculate: bool, - ) -> Option; + fn resolve_ingestion_kind(&self, ingestion: IngestionKind) -> Option; fn resolve_substance_unit(&self, ingestion: &SubstanceIngestion) -> Option; fn create_session(&mut self, title: &str) -> Session; @@ -42,6 +38,12 @@ pub trait Journal { fn first_session_by_title(&self, title: &str) -> Option; } +pub trait JournalIntegrityChecks: JournalTrait { + fn check_sustenance_for_recursion(&self, _sustenance: Sustenance) -> Option> { + todo!() + } +} + #[cfg(feature = "json_journal")] mod json_journal; #[cfg(feature = "json_journal")] diff --git a/journal/src/lib.rs b/journal/src/lib.rs index f158672..7780fb0 100644 --- a/journal/src/lib.rs +++ b/journal/src/lib.rs @@ -1,2 +1,3 @@ +pub mod calculate; pub mod journal; pub mod types; diff --git a/journal/src/types/administration_route.rs b/journal/src/types/administration_route.rs new file mode 100644 index 0000000..ea42625 --- /dev/null +++ b/journal/src/types/administration_route.rs @@ -0,0 +1,43 @@ +use std::fmt::Display; + +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] +#[serde(rename_all = "UPPERCASE")] +pub enum AdministrationRoute { + Oral, + Sublingual, + Buccal, + Insufflated, + Rectal, + Transdermal, + Subcutaneous, + Intramuscular, + Intravenous, + Smoked, + Inhaled, +} + +impl Default for AdministrationRoute { + fn default() -> Self { + Self::Oral + } +} + +impl Display for AdministrationRoute { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + Self::Oral => f.write_str("Oral"), + Self::Sublingual => f.write_str("Sublingual"), + Self::Buccal => f.write_str("Buccal"), + Self::Insufflated => f.write_str("Insufflated"), + Self::Rectal => f.write_str("Rectal"), + Self::Transdermal => f.write_str("Transdermal"), + Self::Subcutaneous => f.write_str("Subcutaneous"), + Self::Intramuscular => f.write_str("Intramuscular"), + Self::Intravenous => f.write_str("Intravenous"), + Self::Smoked => f.write_str("Smoked"), + Self::Inhaled => f.write_str("Inhaled"), + } + } +} diff --git a/journal/src/types/custom_unit.rs b/journal/src/types/custom_unit.rs index 8d4fa22..cf6c0d0 100644 --- a/journal/src/types/custom_unit.rs +++ b/journal/src/types/custom_unit.rs @@ -17,73 +17,3 @@ pub struct CustomUnit { pub creation_time: DateTime, pub is_archived: bool, } - -impl From for CustomUnit { - fn from(custom_unit: psychonaut_journal_types::CustomUnit) -> Self { - CustomUnit { - id: custom_unit.id, - name: custom_unit.name, - substance_name: custom_unit.substance_name, - unit: custom_unit.unit, - original_unit: custom_unit.original_unit, - - dose: { - if custom_unit.estimate_standard_deviation.is_some() { - Dose::new_deviation( - custom_unit.dose.unwrap(), - custom_unit.estimate_standard_deviation.unwrap(), - ) - } else if custom_unit.is_estimate && custom_unit.dose.is_some() { - Dose::new_estimate(custom_unit.dose.unwrap()) - } else if custom_unit.dose.is_some() { - Dose::new_precise(custom_unit.dose.unwrap()) - } else { - Dose::new_unknown() - } - }, - - administration_route: custom_unit.administration_route, - - creation_time: from_unix_millis(custom_unit.creation_time), - is_archived: custom_unit.is_archived, - } - } -} - -#[cfg(test)] -mod tests { - use crate::types::{from_unix_millis, AdministrationRoute, Dose}; - - use super::CustomUnit; - use psychonaut_journal_types::CustomUnit as PsychonautCustomUnit; - - #[test] - fn psychonaut_journal_conversion() { - assert_eq!( - CustomUnit::from(PsychonautCustomUnit { - id: 0, - substance_name: "Caffeine".to_string(), - name: "Beverage".to_string(), - creation_time: 0, - administration_route: AdministrationRoute::Oral, - dose: Some(10.0), - unit: "sip".to_string(), - original_unit: "mg".to_string(), - is_estimate: true, - estimate_standard_deviation: Some(10.0), - is_archived: false - }), - CustomUnit { - id: 0, - name: "Beverage".to_string(), - substance_name: "Caffeine".to_string(), - unit: "sip".to_string(), - original_unit: "mg".to_string(), - dose: Dose::new_deviation(10.0, 10.0), - administration_route: AdministrationRoute::Oral, - creation_time: from_unix_millis(0), - is_archived: false - } - ); - } -} diff --git a/journal/src/types/ingestion.rs b/journal/src/types/ingestion.rs index 7b6eea5..e6951ff 100644 --- a/journal/src/types/ingestion.rs +++ b/journal/src/types/ingestion.rs @@ -1,9 +1,9 @@ use chrono::{DateTime, Utc}; -use super::{dose::Dose, from_unix_millis, Consumer}; +use super::Consumer; mod kind; -pub use kind::{IngestionKind, IngestionKinds}; +pub use kind::IngestionKind; mod substance; pub use substance::SubstanceIngestion; @@ -11,6 +11,9 @@ pub use substance::SubstanceIngestion; mod sustenance; pub use sustenance::SustenanceIngestion; +mod hydration; +pub use hydration::HydrationIngestion; + #[derive(PartialEq, Default, Debug, Clone)] #[derive(serde::Serialize, serde::Deserialize)] pub struct Ingestion { @@ -20,87 +23,3 @@ pub struct Ingestion { pub notes: String, pub kind: IngestionKind, } - -impl From for Ingestion { - fn from(ingestion: psychonaut_journal_types::Ingestion) -> Self { - Ingestion { - ingestion_time: from_unix_millis(ingestion.ingestion_time), - creation_time: from_unix_millis(ingestion.creation_time), - - kind: IngestionKind::Substance(SubstanceIngestion { - substance_name: ingestion.substance_name, - dose: { - if ingestion.estimate_standard_deviation.is_some() { - Dose::new_deviation( - ingestion.dose.unwrap(), - ingestion.estimate_standard_deviation.unwrap(), - ) - } else if ingestion.is_estimate && ingestion.dose.is_some() { - Dose::new_estimate(ingestion.dose.unwrap()) - } else if ingestion.dose.is_some() { - Dose::new_precise(ingestion.dose.unwrap()) - } else { - Dose::new_unknown() - } - }, - - custom_unit_id: ingestion.custom_unit_id, - - roa: ingestion.roa, - stomach_fullness: ingestion.stomach_fullness, - }), - - consumer: match ingestion.consumer_name { - Some(name) => Consumer::Named(name), - None => Consumer::Default, - }, - - notes: ingestion.notes, - } - } -} - -#[cfg(test)] -mod tests { - use crate::types::{ - from_unix_millis, - ingestion::{IngestionKind, SubstanceIngestion}, - AdministrationRoute, Consumer, Dose, - }; - - use super::Ingestion; - use psychonaut_journal_types::Ingestion as PsychonautIngestion; - - #[test] - fn psychonaut_journal_conversion() { - assert_eq!( - Ingestion::from(PsychonautIngestion { - substance_name: "Caffeine".to_string(), - ingestion_time: 0, - creation_time: 0, - dose: Some(10.0), - unit: "mg".to_string(), - is_estimate: false, - estimate_standard_deviation: None, - custom_unit_id: None, - roa: AdministrationRoute::Oral, - consumer_name: None, - notes: "".to_string(), - stomach_fullness: None, - }), - Ingestion { - ingestion_time: from_unix_millis(0), - creation_time: from_unix_millis(0), - kind: IngestionKind::Substance(SubstanceIngestion { - substance_name: "Caffeine".to_string(), - dose: Dose::new_precise(10.0), - custom_unit_id: None, - roa: AdministrationRoute::Oral, - stomach_fullness: None - }), - consumer: Consumer::Default, - notes: "".to_string(), - } - ); - } -} diff --git a/journal/src/types/ingestion/hydration.rs b/journal/src/types/ingestion/hydration.rs new file mode 100644 index 0000000..e195ec8 --- /dev/null +++ b/journal/src/types/ingestion/hydration.rs @@ -0,0 +1,13 @@ +use std::fmt::Display; + +#[derive(PartialEq, Default, Debug, Clone)] +#[derive(serde::Serialize, serde::Deserialize)] +pub struct HydrationIngestion { + pub amount_ml: f64, +} + +impl Display for HydrationIngestion { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} ml", self.amount_ml) + } +} diff --git a/journal/src/types/ingestion/kind.rs b/journal/src/types/ingestion/kind.rs index 12cb068..8473396 100644 --- a/journal/src/types/ingestion/kind.rs +++ b/journal/src/types/ingestion/kind.rs @@ -1,4 +1,4 @@ -use super::{SubstanceIngestion, SustenanceIngestion}; +use super::{HydrationIngestion, SubstanceIngestion, SustenanceIngestion}; #[derive(PartialEq, Default, Debug, Clone)] #[derive(serde::Serialize, serde::Deserialize)] @@ -8,23 +8,5 @@ pub enum IngestionKind { Unknown, Substance(SubstanceIngestion), Sustenance(SustenanceIngestion), - Hydration { - amount: u64, - }, -} - -#[derive(PartialEq, Default, Debug, Clone)] -#[derive(serde::Serialize, serde::Deserialize)] -pub struct IngestionKinds { - pub ingestion: IngestionKind, - pub includes: Vec, -} - -impl From<&IngestionKind> for IngestionKinds { - fn from(value: &IngestionKind) -> Self { - IngestionKinds { - ingestion: value.clone(), - ..Default::default() - } - } + Hydration(HydrationIngestion), } diff --git a/journal/src/types/ingestion/substance.rs b/journal/src/types/ingestion/substance.rs index 3a28e65..e87c8a0 100644 --- a/journal/src/types/ingestion/substance.rs +++ b/journal/src/types/ingestion/substance.rs @@ -1,9 +1,11 @@ -use crate::types::{AdministrationRoute, Dose}; +use crate::types::{AdministrationRoute, Dose, Substance}; #[derive(PartialEq, Default, Debug, Clone)] #[derive(serde::Serialize, serde::Deserialize)] pub struct SubstanceIngestion { - pub substance_name: String, + pub id: String, + pub substance: Option, + pub dose: Dose, pub custom_unit_id: Option, pub roa: AdministrationRoute, diff --git a/journal/src/types/ingestion/sustenance.rs b/journal/src/types/ingestion/sustenance.rs index 0a7e671..2fcd5f6 100644 --- a/journal/src/types/ingestion/sustenance.rs +++ b/journal/src/types/ingestion/sustenance.rs @@ -1,6 +1,10 @@ +use crate::types::Sustenance; + #[derive(PartialEq, Default, Debug, Clone)] #[derive(serde::Serialize, serde::Deserialize)] pub struct SustenanceIngestion { - pub sustenance_id: String, + pub id: String, + pub sustenance: Option, + pub amount: f64, } diff --git a/journal/src/types/mod.rs b/journal/src/types/mod.rs index 7b13f55..1aecb98 100644 --- a/journal/src/types/mod.rs +++ b/journal/src/types/mod.rs @@ -14,7 +14,7 @@ pub use consumer::Consumer; mod ingestion; pub use ingestion::{ - Ingestion, IngestionKind, IngestionKinds, SubstanceIngestion, SustenanceIngestion, + HydrationIngestion, Ingestion, IngestionKind, SubstanceIngestion, SustenanceIngestion, }; mod substance; @@ -32,7 +32,8 @@ pub use session::Session; mod custom_unit; pub use custom_unit::CustomUnit; -pub type AdministrationRoute = psychonaut_journal_types::AdministrationRoute; +pub mod administration_route; +pub use administration_route::AdministrationRoute; pub(crate) fn from_unix_millis(time: u64) -> DateTime { Utc.timestamp_millis_opt(time as i64).unwrap() diff --git a/journal/src/types/nutrients.rs b/journal/src/types/nutrients.rs index c6e9ef5..7a7acee 100644 --- a/journal/src/types/nutrients.rs +++ b/journal/src/types/nutrients.rs @@ -1,4 +1,4 @@ -#[derive(Debug, Clone)] +#[derive(PartialEq, Default, Debug, Clone)] #[derive(serde::Serialize, serde::Deserialize)] pub struct Nutrients { pub kcal: u64, diff --git a/journal/src/types/substance.rs b/journal/src/types/substance.rs index 31e33bc..5278614 100644 --- a/journal/src/types/substance.rs +++ b/journal/src/types/substance.rs @@ -1,7 +1,9 @@ -#[derive(Debug, Clone)] +#[derive(Debug, PartialEq, Clone)] #[derive(serde::Serialize, serde::Deserialize)] pub struct Substance { + pub id: String, pub name: String, + pub description: String, pub unit: String, } diff --git a/journal/src/types/sustenance.rs b/journal/src/types/sustenance.rs index 8dbcef4..a3f94ac 100644 --- a/journal/src/types/sustenance.rs +++ b/journal/src/types/sustenance.rs @@ -1,6 +1,6 @@ use super::{IngestionKind, Nutrients}; -#[derive(Debug, Clone)] +#[derive(PartialEq, Debug, Clone)] #[derive(serde::Serialize, serde::Deserialize)] pub struct Sustenance { pub id: String, diff --git a/journal_cli/src/commands/psychonaut/import.rs b/journal_cli/src/commands/psychonaut/import.rs index 35bc99c..72bdb67 100644 --- a/journal_cli/src/commands/psychonaut/import.rs +++ b/journal_cli/src/commands/psychonaut/import.rs @@ -2,13 +2,19 @@ use std::{collections::HashMap, fs::File}; use journal::{ journal::JournalType, - types::{CustomUnit, Ingestion, Session, Substance}, + types::{ + Consumer, CustomUnit, Dose, Ingestion, IngestionKind, Session, Substance, + SubstanceIngestion, + }, }; use psychonaut_journal_types::ExportData; use rand::Rng; +use types::{from_administration_route, from_unix_millis}; use crate::{args::JournalLocation, helpers::load_journal}; +mod types; + mod custom_substance; mod custom_unit; @@ -68,16 +74,14 @@ pub fn psychonaut_import(args: &PsychonautImportArgs) -> Result<(), Box Result<(), Box journal.get_custom_unit(id).unwrap().original_unit, None => ingestion.unit.clone(), }; - match journal.get_substance(substance_name) { + match journal.get_substance(&substance_id) { Some(substance) => { if substance.unit != ingestion_unit { panic!( @@ -105,6 +110,7 @@ pub fn psychonaut_import(args: &PsychonautImportArgs) -> Result<(), Box { journal.create_substance(Substance { + id: substance_id, name: substance_name.clone(), description: "".to_string(), unit: ingestion_unit, @@ -134,8 +140,42 @@ pub fn psychonaut_import(args: &PsychonautImportArgs) -> Result<(), Box = Vec::new(); - for ingestion in experience.ingestions.iter() { - ingestions.push(Ingestion::from(ingestion.clone())) + for ingestion in experience.ingestions.into_iter() { + ingestions.push(Ingestion { + ingestion_time: from_unix_millis(ingestion.ingestion_time), + creation_time: from_unix_millis(ingestion.creation_time), + + kind: IngestionKind::Substance(SubstanceIngestion { + id: ingestion.substance_name.replace(" ", "_").to_uppercase(), + substance: None, + dose: { + if ingestion.estimate_standard_deviation.is_some() { + Dose::new_deviation( + ingestion.dose.unwrap(), + ingestion.estimate_standard_deviation.unwrap(), + ) + } else if ingestion.is_estimate && ingestion.dose.is_some() { + Dose::new_estimate(ingestion.dose.unwrap()) + } else if ingestion.dose.is_some() { + Dose::new_precise(ingestion.dose.unwrap()) + } else { + Dose::new_unknown() + } + }, + + custom_unit_id: ingestion.custom_unit_id, + + roa: from_administration_route(ingestion.roa), + stomach_fullness: ingestion.stomach_fullness, + }), + + consumer: match ingestion.consumer_name { + Some(name) => Consumer::Named(name), + None => Consumer::Default, + }, + + notes: ingestion.notes, + }) } journal.set_session_ingestions(session_id, ingestions); diff --git a/journal_cli/src/commands/psychonaut/import/custom_substance.rs b/journal_cli/src/commands/psychonaut/import/custom_substance.rs index e69fb60..7e79ac1 100644 --- a/journal_cli/src/commands/psychonaut/import/custom_substance.rs +++ b/journal_cli/src/commands/psychonaut/import/custom_substance.rs @@ -22,7 +22,6 @@ pub fn create_or_check_custom_substance(journal: &mut JournalType, custom_substa } #[derive(Debug, Clone, clap::Args)] -#[group(args = vec!["id", "name"], required = true, multiple = false)] pub struct PsychonautImportCustomSubstanceArgs { #[command(flatten)] pub import_global_args: PsychonautImportGlobalArgs, @@ -48,6 +47,7 @@ pub fn psychonaut_import_custom_substance( create_or_check_custom_substance( &mut journal, Substance { + id: custom_substance.name.replace(" ", "_").to_ascii_uppercase(), name: custom_substance.name.clone(), description: custom_substance.description.clone(), unit: custom_substance.units, diff --git a/journal_cli/src/commands/psychonaut/import/custom_unit.rs b/journal_cli/src/commands/psychonaut/import/custom_unit.rs index 168960e..ae1acee 100644 --- a/journal_cli/src/commands/psychonaut/import/custom_unit.rs +++ b/journal_cli/src/commands/psychonaut/import/custom_unit.rs @@ -1,8 +1,46 @@ -use journal::{journal::JournalType, types::CustomUnit}; +use journal::{ + journal::JournalType, + types::{CustomUnit, Dose}, +}; -use super::{load_journal_and_export_data, PsychonautImportGlobalArgs}; +use super::{ + load_journal_and_export_data, + types::{from_administration_route, from_unix_millis}, + PsychonautImportGlobalArgs, +}; + +pub fn create_or_update_custom_unit( + journal: &mut JournalType, + custom_unit: psychonaut_journal_types::CustomUnit, +) { + let unit = CustomUnit { + id: custom_unit.id, + name: custom_unit.name, + substance_name: custom_unit.substance_name, + unit: custom_unit.unit, + original_unit: custom_unit.original_unit, + + dose: { + if custom_unit.estimate_standard_deviation.is_some() { + Dose::new_deviation( + custom_unit.dose.unwrap(), + custom_unit.estimate_standard_deviation.unwrap(), + ) + } else if custom_unit.is_estimate && custom_unit.dose.is_some() { + Dose::new_estimate(custom_unit.dose.unwrap()) + } else if custom_unit.dose.is_some() { + Dose::new_precise(custom_unit.dose.unwrap()) + } else { + Dose::new_unknown() + } + }, + + administration_route: from_administration_route(custom_unit.administration_route), + + creation_time: from_unix_millis(custom_unit.creation_time), + is_archived: custom_unit.is_archived, + }; -pub fn create_or_update_custom_unit(journal: &mut JournalType, unit: CustomUnit) { match journal.get_custom_unit(unit.id) { Some(custom_unit) => { println!( @@ -44,7 +82,7 @@ pub fn psychonaut_import_custom_unit( match unit { Some(unit) => { - create_or_update_custom_unit(&mut journal, CustomUnit::from(unit)); + create_or_update_custom_unit(&mut journal, unit); } None => { panic!("Could not find custom unit with ID: {id}") @@ -59,7 +97,7 @@ pub fn psychonaut_import_custom_unit( match unit { Some(unit) => { - create_or_update_custom_unit(&mut journal, CustomUnit::from(unit)); + create_or_update_custom_unit(&mut journal, unit); } None => { panic!("Could not find custom unit with name: {name}") diff --git a/journal_cli/src/commands/psychonaut/import/types.rs b/journal_cli/src/commands/psychonaut/import/types.rs new file mode 100644 index 0000000..8e9532b --- /dev/null +++ b/journal_cli/src/commands/psychonaut/import/types.rs @@ -0,0 +1,36 @@ +use chrono::{DateTime, TimeZone, Utc}; +use journal::types::AdministrationRoute; + +pub fn from_unix_millis(time: u64) -> DateTime { + Utc.timestamp_millis_opt(time as i64).unwrap() +} + +pub fn from_administration_route( + roa: psychonaut_journal_types::AdministrationRoute, +) -> AdministrationRoute { + match roa { + psychonaut_journal_types::AdministrationRoute::Oral => AdministrationRoute::Oral, + psychonaut_journal_types::AdministrationRoute::Sublingual => { + AdministrationRoute::Sublingual + } + psychonaut_journal_types::AdministrationRoute::Buccal => AdministrationRoute::Buccal, + psychonaut_journal_types::AdministrationRoute::Insufflated => { + AdministrationRoute::Insufflated + } + psychonaut_journal_types::AdministrationRoute::Rectal => AdministrationRoute::Rectal, + psychonaut_journal_types::AdministrationRoute::Transdermal => { + AdministrationRoute::Transdermal + } + psychonaut_journal_types::AdministrationRoute::Subcutaneous => { + AdministrationRoute::Subcutaneous + } + psychonaut_journal_types::AdministrationRoute::Intramuscular => { + AdministrationRoute::Intramuscular + } + psychonaut_journal_types::AdministrationRoute::Intravenous => { + AdministrationRoute::Intravenous + } + psychonaut_journal_types::AdministrationRoute::Smoked => AdministrationRoute::Smoked, + psychonaut_journal_types::AdministrationRoute::Inhaled => AdministrationRoute::Inhaled, + } +} diff --git a/journal_cli/src/display.rs b/journal_cli/src/display.rs index c8d5303..e0404cf 100644 --- a/journal_cli/src/display.rs +++ b/journal_cli/src/display.rs @@ -1,8 +1,8 @@ use journal::{ journal::JournalType, types::{ - format_dose, Consumer, Dose, Ingestion, IngestionKind, IngestionKinds, Session, - SubstanceIngestion, SustenanceIngestion, + format_dose, Consumer, Ingestion, IngestionKind, Session, SubstanceIngestion, + SustenanceIngestion, }, }; @@ -17,7 +17,11 @@ fn print_substance_ingestion( println!( "Substance|{}|{}|{}|{}|{}", - substance_ingestion.substance_name, + substance_ingestion + .substance + .as_ref() + .expect("substance_ingestion not provided with a substance") + .name, format_dose(&substance_ingestion.dose, &unit), format_substance_ingestion_roa(substance_ingestion, &unit), ingestion.consumer, @@ -30,9 +34,7 @@ fn print_sustenance_ingestion( ingestion: &Ingestion, sustenance_ingestion: &SustenanceIngestion, ) { - let sustenance = journal - .get_sustenance(&sustenance_ingestion.sustenance_id) - .unwrap(); + let sustenance = journal.get_sustenance(&sustenance_ingestion.id).unwrap(); println!( "Sustenance|{}|{} {}|{}|{}", @@ -44,35 +46,7 @@ fn print_sustenance_ingestion( ) } -pub fn calculate_ingestion_kinds(kind: &mut IngestionKinds) { - //println!("{kind:#?}"); - - if let IngestionKind::Sustenance(sustenance) = &kind.ingestion { - for kinds in kind.includes.iter_mut() { - match &mut kinds.ingestion { - IngestionKind::Substance(ingestion) => { - ingestion.dose = Dose::new_precise(sustenance.amount) * ingestion.dose.clone(); - } - IngestionKind::Sustenance(ingestion) => { - ingestion.amount *= sustenance.amount; - - calculate_ingestion_kinds(kinds) - } - IngestionKind::Hydration { amount } => { - *amount = (sustenance.amount * *amount as f64).round() as u64 - } - _ => {} - } - } - } -} - -pub fn print_ingestion_kinds( - journal: &JournalType, - ingestion: &Ingestion, - kinds: &IngestionKinds, - depth: u64, -) { +pub fn print_ingestion(journal: &JournalType, ingestion: &Ingestion, depth: u64) { for _ in 0..(depth) { print!(" "); } @@ -80,27 +54,35 @@ pub fn print_ingestion_kinds( print!("- "); } - match &kinds.ingestion { + match &ingestion.kind { IngestionKind::Unknown => {} - IngestionKind::Substance(substance_ingestion) => { - print_substance_ingestion(journal, ingestion, substance_ingestion); + IngestionKind::Substance(substance) => { + print_substance_ingestion(journal, ingestion, &substance); } - IngestionKind::Sustenance(sustenance_ingestion) => { - print_sustenance_ingestion(journal, ingestion, sustenance_ingestion); + IngestionKind::Sustenance(sustenance) => { + print_sustenance_ingestion(journal, ingestion, sustenance); + let sustenance = sustenance.sustenance.as_ref().unwrap(); + + for include in &sustenance.includes { + print_ingestion( + journal, + &Ingestion { + kind: include.clone(), + ..ingestion.clone() + }, + depth + 1, + ) + } } - IngestionKind::Hydration { amount } => { + IngestionKind::Hydration(hydration) => { println!( - "Hydration|{} ml|{}|{}", - amount, + "Hydration|{}|{}|{}", + hydration, ingestion.consumer, format_ingestion_time(ingestion) ) } } - - for kinds in &kinds.includes { - print_ingestion_kinds(journal, ingestion, kinds, depth + 1); - } } pub fn print_ingestion_log( @@ -108,22 +90,23 @@ pub fn print_ingestion_log( session: &Session, consumer_filter: Option<&Vec>, ) { - let ingestions = journal + let mut ingestions = journal .get_session_ingestions(session.id) .expect("could not find ingestions for session"); - for ingestion in ingestions.iter() { + for ingestion in ingestions.iter_mut() { if let Some(consumer_filter) = consumer_filter { if !consumer_filter.contains(&ingestion.consumer) { continue; } } - let ingestion_kinds = journal - .resolve_ingestion_kind(&ingestion.kind, true) + ingestion.kind = journal + .resolve_ingestion_kind(ingestion.kind.clone()) .unwrap(); - println!("{ingestion_kinds:#?}"); - print_ingestion_kinds(journal, ingestion, &ingestion_kinds, 0) + //let calculated_values = calculate_ingestion_kinds(ingestion_kinds); + + print_ingestion(journal, ingestion, 0) } }