This commit is contained in:
chaos 2024-11-24 14:26:48 +00:00
parent f4696e0b31
commit 93105359f9
8 changed files with 168 additions and 71 deletions

80
Cargo.lock generated
View file

@ -229,6 +229,18 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
[[package]]
name = "fraction"
version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f158e3ff0a1b334408dc9fb811cd99b446986f4d8b741bb08f9df1604085ae7"
dependencies = [
"lazy_static",
"num",
"serde",
"serde_derive",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.15" version = "0.2.15"
@ -303,6 +315,7 @@ name = "journal"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"chrono", "chrono",
"fraction",
"psychonaut_journal_types", "psychonaut_journal_types",
"rand", "rand",
"serde", "serde",
@ -367,6 +380,73 @@ version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "num"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
"serde",
]
[[package]]
name = "num-complex"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
dependencies = [
"num-traits",
"serde",
]
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
"serde",
]
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.19" version = "0.2.19"

View file

@ -9,3 +9,4 @@ serde_json = "1.0.132"
chrono = { version = "0.4.38", features = ["serde"] } chrono = { version = "0.4.38", features = ["serde"] }
psychonaut_journal_types = { path = "../psychonaut_journal_types" } psychonaut_journal_types = { path = "../psychonaut_journal_types" }
rand = "0.8.5" rand = "0.8.5"
fraction = { version = "0.15.3", features = ["with-serde-support"] }

View file

@ -1,5 +1,6 @@
use crate::types::{ use crate::types::{
AdministrationRoute, Consumer, CustomUnit, Ingestion, Session, Substance, Unit, AdministrationRoute, Consumer, CustomUnit, Ingestion, Session, Substance, SubstanceIngestion,
Unit,
}; };
pub type JournalType = Box<dyn Journal>; pub type JournalType = Box<dyn Journal>;
@ -8,7 +9,7 @@ pub trait Journal {
fn get_custom_unit(&self, id: i32) -> Option<CustomUnit>; fn get_custom_unit(&self, id: i32) -> Option<CustomUnit>;
fn get_session(&self, id: i32) -> Option<Session>; fn get_session(&self, id: i32) -> Option<Session>;
fn get_session_ingestions(&self, id: i32) -> Option<Vec<Ingestion>>; fn get_session_ingestions(&self, id: i32) -> Option<Vec<Ingestion>>;
fn get_session_ingestions_by( fn get_session_substance_ingestions_by(
&self, &self,
id: i32, id: i32,
substance_filter: Option<&Vec<String>>, substance_filter: Option<&Vec<String>>,
@ -23,7 +24,7 @@ pub trait Journal {
fn set_substance(&mut self, name: &String, substance: Substance); fn set_substance(&mut self, name: &String, substance: Substance);
fn first_session_by_title(&self, title: &str) -> Option<Session>; fn first_session_by_title(&self, title: &str) -> Option<Session>;
fn resolve_unit(&self, ingestion: &Ingestion) -> Option<Unit>; fn resolve_substance_unit(&self, ingestion: &SubstanceIngestion) -> Option<Unit>;
fn save(&self) -> Result<(), Box<dyn std::error::Error>>; fn save(&self) -> Result<(), Box<dyn std::error::Error>>;
} }

View file

@ -1,7 +1,8 @@
use std::{collections::HashMap, fs::File}; use std::{collections::HashMap, fs::File};
use crate::types::{ use crate::types::{
AdministrationRoute, Consumer, CustomUnit, Ingestion, Session, Substance, Unit, AdministrationRoute, Consumer, CustomUnit, Ingestion, IngestionKind, Session, Substance,
SubstanceIngestion, Unit,
}; };
use super::{Journal, JournalType}; use super::{Journal, JournalType};
@ -68,7 +69,7 @@ impl Journal for JSONJournal {
self.data.ingestions.get(&id).cloned() self.data.ingestions.get(&id).cloned()
} }
fn get_session_ingestions_by( fn get_session_substance_ingestions_by(
&self, &self,
id: i32, id: i32,
substance_filter: Option<&Vec<String>>, substance_filter: Option<&Vec<String>>,
@ -80,6 +81,17 @@ impl Journal for JSONJournal {
.clone() .clone()
.into_iter() .into_iter()
.filter(|ingestion| { .filter(|ingestion| {
if let Some(consumer_filter) = consumer_filter {
if !consumer_filter.contains(&ingestion.consumer) {
return false;
}
}
let ingestion = match &ingestion.kind {
IngestionKind::Substance(ingestion) => ingestion,
_ => return false,
};
if let Some(substance_filter) = substance_filter { if let Some(substance_filter) = substance_filter {
if !substance_filter.contains(&ingestion.substance_name) { if !substance_filter.contains(&ingestion.substance_name) {
return false; return false;
@ -92,12 +104,6 @@ impl Journal for JSONJournal {
} }
} }
if let Some(consumer_filter) = consumer_filter {
if !consumer_filter.contains(&ingestion.consumer) {
return false;
}
}
true true
}) })
.collect::<Vec<Ingestion>>(); .collect::<Vec<Ingestion>>();
@ -112,7 +118,7 @@ impl Journal for JSONJournal {
self.data.substances.get(name).cloned() self.data.substances.get(name).cloned()
} }
fn resolve_unit(&self, ingestion: &Ingestion) -> Option<Unit> { fn resolve_substance_unit(&self, ingestion: &SubstanceIngestion) -> Option<Unit> {
match ingestion.custom_unit_id { match ingestion.custom_unit_id {
None => match self.get_substance(&ingestion.substance_name) { None => match self.get_substance(&ingestion.substance_name) {
Some(substance) => Some(Unit::Simple(substance.unit)), Some(substance) => Some(Unit::Simple(substance.unit)),

View file

@ -5,19 +5,21 @@ use super::{dose::Dose, from_unix_millis, AdministrationRoute, Consumer};
#[derive(PartialEq, Default, Debug, Clone)] #[derive(PartialEq, Default, Debug, Clone)]
#[derive(serde::Serialize, serde::Deserialize)] #[derive(serde::Serialize, serde::Deserialize)]
pub struct Ingestion { pub struct Ingestion {
pub substance_name: String,
pub ingestion_time: DateTime<Utc>, pub ingestion_time: DateTime<Utc>,
pub creation_time: DateTime<Utc>, pub creation_time: DateTime<Utc>,
pub dose: Dose,
pub custom_unit_id: Option<i32>,
pub roa: AdministrationRoute,
pub consumer: Consumer, pub consumer: Consumer,
pub notes: String, pub notes: String,
pub stomach_fullness: Option<String>, pub kind: IngestionKind,
} }
#[derive(PartialEq, Default, Debug, Clone)]
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum IngestionKind { pub enum IngestionKind {
#[default]
Unknown,
Substance(SubstanceIngestion), Substance(SubstanceIngestion),
Sustenance(SustenanceIngestion),
} }
#[derive(PartialEq, Default, Debug, Clone)] #[derive(PartialEq, Default, Debug, Clone)]
@ -27,36 +29,45 @@ pub struct SubstanceIngestion {
pub dose: Dose, pub dose: Dose,
pub custom_unit_id: Option<i32>, pub custom_unit_id: Option<i32>,
pub roa: AdministrationRoute, pub roa: AdministrationRoute,
pub consumer: Consumer,
pub notes: String,
pub stomach_fullness: Option<String>, pub stomach_fullness: Option<String>,
} }
#[derive(PartialEq, Default, Debug, Clone)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct SustenanceIngestion {
pub sustenance_name: String,
pub sustenance_category: String,
pub amount: (u8, u8, u8),
}
impl From<psychonaut_journal_types::Ingestion> for Ingestion { impl From<psychonaut_journal_types::Ingestion> for Ingestion {
fn from(ingestion: psychonaut_journal_types::Ingestion) -> Self { fn from(ingestion: psychonaut_journal_types::Ingestion) -> Self {
Ingestion { Ingestion {
substance_name: ingestion.substance_name,
ingestion_time: from_unix_millis(ingestion.ingestion_time), ingestion_time: from_unix_millis(ingestion.ingestion_time),
creation_time: from_unix_millis(ingestion.creation_time), creation_time: from_unix_millis(ingestion.creation_time),
dose: { kind: IngestionKind::Substance(SubstanceIngestion {
if ingestion.estimate_standard_deviation.is_some() { substance_name: ingestion.substance_name,
Dose::new_deviation( dose: {
ingestion.dose.unwrap(), if ingestion.estimate_standard_deviation.is_some() {
ingestion.estimate_standard_deviation.unwrap(), Dose::new_deviation(
) ingestion.dose.unwrap(),
} else if ingestion.is_estimate && ingestion.dose.is_some() { ingestion.estimate_standard_deviation.unwrap(),
Dose::new_estimate(ingestion.dose.unwrap()) )
} else if ingestion.dose.is_some() { } else if ingestion.is_estimate && ingestion.dose.is_some() {
Dose::new_precise(ingestion.dose.unwrap()) Dose::new_estimate(ingestion.dose.unwrap())
} else { } else if ingestion.dose.is_some() {
Dose::new_unknown() Dose::new_precise(ingestion.dose.unwrap())
} } else {
}, Dose::new_unknown()
}
},
custom_unit_id: ingestion.custom_unit_id, custom_unit_id: ingestion.custom_unit_id,
roa: ingestion.roa, roa: ingestion.roa,
stomach_fullness: ingestion.stomach_fullness,
}),
consumer: match ingestion.consumer_name { consumer: match ingestion.consumer_name {
Some(name) => Consumer::Named(name), Some(name) => Consumer::Named(name),
@ -64,14 +75,17 @@ impl From<psychonaut_journal_types::Ingestion> for Ingestion {
}, },
notes: ingestion.notes, notes: ingestion.notes,
stomach_fullness: ingestion.stomach_fullness,
} }
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::types::{from_unix_millis, AdministrationRoute, Consumer, Dose}; use crate::types::{
from_unix_millis,
ingestion::{IngestionKind, SubstanceIngestion},
AdministrationRoute, Consumer, Dose,
};
use super::Ingestion; use super::Ingestion;
use psychonaut_journal_types::Ingestion as PsychonautIngestion; use psychonaut_journal_types::Ingestion as PsychonautIngestion;
@ -94,15 +108,17 @@ mod tests {
stomach_fullness: None, stomach_fullness: None,
}), }),
Ingestion { Ingestion {
substance_name: "Caffeine".to_string(),
ingestion_time: from_unix_millis(0), ingestion_time: from_unix_millis(0),
creation_time: from_unix_millis(0), creation_time: from_unix_millis(0),
dose: Dose::new_precise(10.0), kind: IngestionKind::Substance(SubstanceIngestion {
custom_unit_id: None, substance_name: "Caffeine".to_string(),
roa: AdministrationRoute::Oral, dose: Dose::new_precise(10.0),
custom_unit_id: None,
roa: AdministrationRoute::Oral,
stomach_fullness: None
}),
consumer: Consumer::Default, consumer: Consumer::Default,
notes: "".to_string(), notes: "".to_string(),
stomach_fullness: None
} }
); );
} }

View file

@ -13,7 +13,7 @@ mod consumer;
pub use consumer::Consumer; pub use consumer::Consumer;
mod ingestion; mod ingestion;
pub use ingestion::Ingestion; pub use ingestion::{Ingestion, IngestionKind, SubstanceIngestion};
mod substance; mod substance;
pub use substance::Substance; pub use substance::Substance;

View file

@ -1,9 +1,9 @@
use journal::{ use journal::{
journal::JournalType, journal::JournalType,
types::{format_dose, Consumer, Session}, types::{format_dose, Consumer, IngestionKind, Session},
}; };
use crate::formatting::{format_ingestion_roa, format_ingestion_time}; use crate::formatting::{format_ingestion_time, format_substance_ingestion_roa};
pub fn print_ingestion_log( pub fn print_ingestion_log(
journal: &JournalType, journal: &JournalType,
@ -12,30 +12,23 @@ pub fn print_ingestion_log(
consumer_filter: Option<&Vec<Consumer>>, consumer_filter: Option<&Vec<Consumer>>,
) { ) {
for ingestion in journal for ingestion in journal
.get_session_ingestions(session.id) .get_session_substance_ingestions_by(session.id, substance_filter, None, consumer_filter)
.expect("could not find ingestions for session") .expect("could not find ingestions for session")
.iter() .iter()
{ {
if let Some(substance_filter) = substance_filter { let substance_ingestion = match &ingestion.kind {
if !substance_filter.contains(&ingestion.substance_name) { IngestionKind::Substance(substance_ingestion) => substance_ingestion,
continue; _ => continue,
} };
}
if let Some(consumer_filter) = consumer_filter { let unit = journal.resolve_substance_unit(substance_ingestion).unwrap();
if !consumer_filter.contains(&ingestion.consumer) {
continue;
}
}
let unit = journal.resolve_unit(ingestion).unwrap();
// println!("{:#?} {:#?}", &ingestion, &custom_unit); // println!("{:#?} {:#?}", &ingestion, &custom_unit);
println!( println!(
"{}|{}|{}|{}|{}", "{}|{}|{}|{}|{}",
ingestion.substance_name, substance_ingestion.substance_name,
format_dose(&ingestion.dose, &unit), format_dose(&substance_ingestion.dose, &unit),
format_ingestion_roa(ingestion, &unit), format_substance_ingestion_roa(substance_ingestion, &unit),
ingestion.consumer, ingestion.consumer,
format_ingestion_time(ingestion) format_ingestion_time(ingestion)
) )

View file

@ -1,4 +1,4 @@
use journal::types::{Ingestion, Session, Unit}; use journal::types::{Ingestion, Session, SubstanceIngestion, Unit};
pub fn format_session_title(session: &Session) -> String { pub fn format_session_title(session: &Session) -> String {
format!("{}: {}", session.title, session.creation_time) format!("{}: {}", session.title, session.creation_time)
@ -8,7 +8,7 @@ pub fn format_ingestion_time(ingestion: &Ingestion) -> String {
ingestion.ingestion_time.format("%a %I:%M %p").to_string() ingestion.ingestion_time.format("%a %I:%M %p").to_string()
} }
pub fn format_ingestion_roa(ingestion: &Ingestion, unit: &Unit) -> String { pub fn format_substance_ingestion_roa(ingestion: &SubstanceIngestion, unit: &Unit) -> String {
if let Unit::Custom(unit) = unit { if let Unit::Custom(unit) = unit {
format!("{:?} ({})", ingestion.roa, &unit.name) format!("{:?} ({})", ingestion.roa, &unit.name)
} else { } else {
@ -43,11 +43,11 @@ mod tests {
} }
#[test] #[test]
fn format_ingestion_roa() { fn format_substance_ingestion_roa() {
let result = super::format_ingestion_roa( let result: String = super::format_substance_ingestion_roa(
&Ingestion { &SubstanceIngestion {
roa: AdministrationRoute::Oral, roa: AdministrationRoute::Oral,
..Ingestion::default() ..SubstanceIngestion::default()
}, },
&Unit::default(), &Unit::default(),
); );
@ -56,10 +56,10 @@ mod tests {
#[test] #[test]
fn format_ingestion_roa_with_custom_unit() { fn format_ingestion_roa_with_custom_unit() {
let result = super::format_ingestion_roa( let result = super::format_substance_ingestion_roa(
&Ingestion { &SubstanceIngestion {
roa: AdministrationRoute::Oral, roa: AdministrationRoute::Oral,
..Ingestion::default() ..SubstanceIngestion::default()
}, },
&Unit::Custom(CustomUnit { &Unit::Custom(CustomUnit {
name: "32mg/ml".to_string(), name: "32mg/ml".to_string(),