This commit is contained in:
chaos 2024-11-27 19:10:02 +00:00
parent 935672d716
commit 5e4a4cc54f
20 changed files with 416 additions and 330 deletions

124
journal/src/calculate.rs Normal file
View 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
}

View file

@ -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 {}

View file

@ -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")]

View file

@ -1,2 +1,3 @@
pub mod calculate;
pub mod journal;
pub mod types;

View 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"),
}
}
}

View file

@ -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
}
);
}
}

View file

@ -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(),
}
);
}
}

View 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)
}
}

View file

@ -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),
}

View file

@ -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,

View file

@ -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,
}

View file

@ -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()

View file

@ -1,4 +1,4 @@
#[derive(Debug, Clone)]
#[derive(PartialEq, Default, Debug, Clone)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Nutrients {
pub kcal: u64,

View file

@ -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,
}

View file

@ -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,

View file

@ -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);

View file

@ -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,

View file

@ -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}")

View 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,
}
}

View file

@ -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)
}
}