update
This commit is contained in:
parent
935672d716
commit
5e4a4cc54f
124
journal/src/calculate.rs
Normal file
124
journal/src/calculate.rs
Normal file
|
@ -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<String, Dose>,
|
||||
pub nutrients: Nutrients,
|
||||
pub hydration: HydrationIngestion,
|
||||
}
|
||||
|
||||
impl Add<CalculatedValues> 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<f64> 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<IngestionKind> = 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
|
||||
}
|
|
@ -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<dyn std::error::Error>> {
|
||||
self.save()
|
||||
}
|
||||
|
@ -80,83 +80,40 @@ impl Journal for JSONJournal {
|
|||
self.data.ingestions.get(&id).cloned()
|
||||
}
|
||||
|
||||
fn get_substance(&self, name: &str) -> Option<Substance> {
|
||||
self.data.substances.get(name).cloned()
|
||||
fn get_substance(&self, id: &str) -> Option<Substance> {
|
||||
self.data.substances.get(id).cloned()
|
||||
}
|
||||
|
||||
fn get_sustenance(&self, sustenance_id: &str) -> Option<Sustenance> {
|
||||
self.data.sustenances.get(sustenance_id).cloned()
|
||||
fn get_sustenance(&self, id: &str) -> Option<Sustenance> {
|
||||
self.data.sustenances.get(id).cloned()
|
||||
}
|
||||
|
||||
fn get_custom_unit(&self, id: i32) -> Option<CustomUnit> {
|
||||
self.data.custom_units.get(&id).cloned()
|
||||
}
|
||||
|
||||
fn resolve_ingestion_kind(
|
||||
&self,
|
||||
kind: &IngestionKind,
|
||||
calculate: bool,
|
||||
) -> Option<IngestionKinds> {
|
||||
println!("{kind:#?}");
|
||||
let mut kinds: IngestionKinds = IngestionKinds::from(kind);
|
||||
fn resolve_ingestion_kind(&self, kind: IngestionKind) -> Option<IngestionKind> {
|
||||
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
|
||||
if let IngestionKind::Sustenance(ingestion_type) = &mut kind {
|
||||
if let Some(mut sustenance) = self.get_sustenance(&ingestion_type.id) {
|
||||
sustenance.includes = sustenance
|
||||
.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))
|
||||
}
|
||||
.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<Unit> {
|
||||
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 {}
|
||||
|
|
|
@ -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<dyn Journal>;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
|
@ -11,20 +11,16 @@ pub enum JournalError {
|
|||
Unknown,
|
||||
}
|
||||
|
||||
pub trait Journal {
|
||||
pub trait JournalTrait {
|
||||
fn save(&mut self) -> Result<(), Box<dyn std::error::Error>>;
|
||||
|
||||
fn get_session(&self, id: i32) -> Option<Session>;
|
||||
fn get_session_ingestions(&self, id: i32) -> Option<Vec<Ingestion>>;
|
||||
fn get_substance(&self, name: &str) -> Option<Substance>;
|
||||
fn get_substance(&self, id: &str) -> Option<Substance>;
|
||||
fn get_sustenance(&self, id: &str) -> Option<Sustenance>;
|
||||
fn get_custom_unit(&self, id: i32) -> Option<CustomUnit>;
|
||||
|
||||
fn resolve_ingestion_kind(
|
||||
&self,
|
||||
ingestion: &IngestionKind,
|
||||
calculate: bool,
|
||||
) -> Option<IngestionKinds>;
|
||||
fn resolve_ingestion_kind(&self, ingestion: IngestionKind) -> Option<IngestionKind>;
|
||||
fn resolve_substance_unit(&self, ingestion: &SubstanceIngestion) -> Option<Unit>;
|
||||
|
||||
fn create_session(&mut self, title: &str) -> Session;
|
||||
|
@ -42,6 +38,12 @@ pub trait Journal {
|
|||
fn first_session_by_title(&self, title: &str) -> Option<Session>;
|
||||
}
|
||||
|
||||
pub trait JournalIntegrityChecks: JournalTrait {
|
||||
fn check_sustenance_for_recursion(&self, _sustenance: Sustenance) -> Option<Vec<String>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "json_journal")]
|
||||
mod json_journal;
|
||||
#[cfg(feature = "json_journal")]
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
pub mod calculate;
|
||||
pub mod journal;
|
||||
pub mod types;
|
||||
|
|
43
journal/src/types/administration_route.rs
Normal file
43
journal/src/types/administration_route.rs
Normal file
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,73 +17,3 @@ pub struct CustomUnit {
|
|||
pub creation_time: DateTime<Utc>,
|
||||
pub is_archived: bool,
|
||||
}
|
||||
|
||||
impl From<psychonaut_journal_types::CustomUnit> 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
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<psychonaut_journal_types::Ingestion> 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(),
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
13
journal/src/types/ingestion/hydration.rs
Normal file
13
journal/src/types/ingestion/hydration.rs
Normal file
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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<IngestionKinds>,
|
||||
}
|
||||
|
||||
impl From<&IngestionKind> for IngestionKinds {
|
||||
fn from(value: &IngestionKind) -> Self {
|
||||
IngestionKinds {
|
||||
ingestion: value.clone(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
Hydration(HydrationIngestion),
|
||||
}
|
||||
|
|
|
@ -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<Substance>,
|
||||
|
||||
pub dose: Dose,
|
||||
pub custom_unit_id: Option<i32>,
|
||||
pub roa: AdministrationRoute,
|
||||
|
|
|
@ -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<Sustenance>,
|
||||
|
||||
pub amount: f64,
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
Utc.timestamp_millis_opt(time as i64).unwrap()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#[derive(Debug, Clone)]
|
||||
#[derive(PartialEq, Default, Debug, Clone)]
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct Nutrients {
|
||||
pub kcal: u64,
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<dyn std:
|
|||
let mut rng = rand::thread_rng();
|
||||
|
||||
for custom_unit in export_data.custom_units.into_iter() {
|
||||
custom_unit::create_or_update_custom_unit(
|
||||
&mut journal,
|
||||
CustomUnit::from(custom_unit),
|
||||
);
|
||||
custom_unit::create_or_update_custom_unit(&mut journal, custom_unit);
|
||||
}
|
||||
|
||||
for custom_substance in export_data.custom_substances.into_iter() {
|
||||
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,
|
||||
|
@ -88,13 +92,14 @@ pub fn psychonaut_import(args: &PsychonautImportArgs) -> Result<(), Box<dyn std:
|
|||
for experience in export_data.experiences.iter() {
|
||||
for ingestion in experience.ingestions.iter() {
|
||||
let substance_name = &ingestion.substance_name;
|
||||
let substance_id = substance_name.replace(" ", "_").to_ascii_uppercase();
|
||||
|
||||
let ingestion_unit = match ingestion.custom_unit_id {
|
||||
Some(id) => 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<dyn std:
|
|||
}
|
||||
None => {
|
||||
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<dyn std:
|
|||
);
|
||||
|
||||
let mut ingestions: Vec<Ingestion> = 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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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}")
|
||||
|
|
36
journal_cli/src/commands/psychonaut/import/types.rs
Normal file
36
journal_cli/src/commands/psychonaut/import/types.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use chrono::{DateTime, TimeZone, Utc};
|
||||
use journal::types::AdministrationRoute;
|
||||
|
||||
pub fn from_unix_millis(time: u64) -> DateTime<Utc> {
|
||||
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,
|
||||
}
|
||||
}
|
|
@ -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<Consumer>>,
|
||||
) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue