This commit is contained in:
chaos 2024-11-24 21:12:38 +00:00
parent 956c3f6ad1
commit d7fb6f147a
3 changed files with 244 additions and 91 deletions

View file

@ -5,24 +5,29 @@ use crate::types::{
pub type JournalType = Box<dyn Journal>; pub type JournalType = Box<dyn Journal>;
pub trait Journal { pub trait Journal {
fn get_custom_unit(&self, id: i32) -> Option<CustomUnit>; fn save(&self) -> Result<(), Box<dyn std::error::Error>>;
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_substance(&self, name: &str) -> Option<Substance>; fn get_substance(&self, name: &str) -> Option<Substance>;
fn get_sustenance(&self, sustenance_id: &str) -> Option<Sustenance>; fn get_sustenance(&self, id: &str) -> Option<Sustenance>;
fn get_sustenance_ingestions(&self, sustenance_id: &str) -> Option<Vec<IngestionKind>>; fn get_sustenance_ingestions(&self, id: &str) -> Option<Vec<IngestionKind>>;
fn get_custom_unit(&self, id: i32) -> Option<CustomUnit>;
fn create_session(&mut self, title: &str) -> Session;
fn create_substance(&mut self, substance: Substance) -> Substance;
fn create_sustenance(&mut self, sustenance: Sustenance) -> Sustenance;
fn create_custom_unit(&mut self, unit: CustomUnit) -> CustomUnit;
fn update_session(&mut self, id: i32, session: Session, create: bool);
fn update_substance(&mut self, name: &str, substance: Substance, create: bool);
fn update_sustenance(&mut self, id: &str, sustenance: Sustenance, create: bool);
fn update_custom_unit(&mut self, id: i32, unit: CustomUnit, create: bool);
fn set_custom_unit(&mut self, unit: CustomUnit);
fn set_session(&mut self, session: Session);
fn set_session_ingestions(&mut self, session_id: i32, ingestions: Vec<Ingestion>); fn set_session_ingestions(&mut self, session_id: i32, ingestions: Vec<Ingestion>);
fn set_substance(&mut self, substance: Substance);
fn set_sustenance(&mut self, sustenance: Sustenance);
fn first_session_by_title(&self, title: &str) -> Option<Session>; fn first_session_by_title(&self, title: &str) -> Option<Session>;
fn resolve_substance_unit(&self, ingestion: &SubstanceIngestion) -> Option<Unit>; fn resolve_substance_unit(&self, ingestion: &SubstanceIngestion) -> Option<Unit>;
fn save(&self) -> Result<(), Box<dyn std::error::Error>>;
} }
mod json_journal; mod json_journal;

View file

@ -1,5 +1,8 @@
use std::{collections::HashMap, fs::File}; use std::{collections::HashMap, fs::File};
use chrono::Utc;
use rand::Rng;
use crate::types::{ use crate::types::{
CustomUnit, Ingestion, IngestionKind, Session, Substance, SubstanceIngestion, Sustenance, Unit, CustomUnit, Ingestion, IngestionKind, Session, Substance, SubstanceIngestion, Sustenance, Unit,
}; };
@ -59,8 +62,8 @@ impl JSONJournal {
} }
impl Journal for JSONJournal { impl Journal for JSONJournal {
fn get_custom_unit(&self, id: i32) -> Option<CustomUnit> { fn save(&self) -> Result<(), Box<dyn std::error::Error>> {
self.data.custom_units.get(&id).cloned() self.save()
} }
fn get_session(&self, id: i32) -> Option<Session> { fn get_session(&self, id: i32) -> Option<Session> {
@ -75,58 +78,12 @@ impl Journal for JSONJournal {
self.data.substances.get(name).cloned() self.data.substances.get(name).cloned()
} }
fn resolve_substance_unit(&self, ingestion: &SubstanceIngestion) -> Option<Unit> {
match ingestion.custom_unit_id {
None => match self.get_substance(&ingestion.substance_name) {
Some(substance) => Some(Unit::Simple(substance.unit)),
None => None,
},
Some(custom_unit_id) => self.get_custom_unit(custom_unit_id).map(Unit::Custom),
}
}
fn first_session_by_title(&self, title: &str) -> Option<Session> {
self.data
.sessions
.values()
.find(|&session| session.title == title)
.cloned()
}
fn set_custom_unit(&mut self, unit: CustomUnit) {
self.data.custom_units.insert(unit.id, unit);
}
fn set_session(&mut self, session: Session) {
self.data.sessions.insert(session.id, session);
}
fn set_session_ingestions(&mut self, session_id: i32, ingestions: Vec<Ingestion>) {
self.data.ingestions.insert(session_id, ingestions);
}
fn set_substance(&mut self, substance: Substance) {
self.data
.substances
.insert(substance.name.clone(), substance);
}
fn save(&self) -> Result<(), Box<dyn std::error::Error>> {
self.save()
}
fn get_sustenance(&self, sustenance_id: &str) -> Option<Sustenance> { fn get_sustenance(&self, sustenance_id: &str) -> Option<Sustenance> {
self.data.sustenances.get(sustenance_id).cloned() self.data.sustenances.get(sustenance_id).cloned()
} }
fn set_sustenance(&mut self, sustenance: Sustenance) {
self.data
.sustenances
.insert(sustenance.id.clone(), sustenance);
}
fn get_sustenance_ingestions(&self, sustenance_id: &str) -> Option<Vec<IngestionKind>> { fn get_sustenance_ingestions(&self, sustenance_id: &str) -> Option<Vec<IngestionKind>> {
let sustenance = self.get_sustenance(sustenance_id).unwrap(); let sustenance = self.get_sustenance(sustenance_id)?;
let mut ingestions: Vec<IngestionKind> = Vec::new(); let mut ingestions: Vec<IngestionKind> = Vec::new();
for include in sustenance.includes.into_iter() { for include in sustenance.includes.into_iter() {
@ -152,4 +109,107 @@ impl Journal for JSONJournal {
Some(ingestions) Some(ingestions)
} }
fn get_custom_unit(&self, id: i32) -> Option<CustomUnit> {
self.data.custom_units.get(&id).cloned()
}
fn create_session(&mut self, title: &str) -> Session {
let id = rand::thread_rng().gen::<i32>();
let session = Session {
id,
title: title.to_string(),
creation_time: Utc::now(),
..Session::default()
};
self.data.sessions.insert(id, session.clone());
session
}
fn create_substance(&mut self, substance: Substance) -> Substance {
self.data
.substances
.insert(substance.name.clone(), substance.clone());
substance
}
fn create_sustenance(&mut self, sustenance: Sustenance) -> Sustenance {
self.data
.sustenances
.insert(sustenance.id.clone(), sustenance.clone());
sustenance
}
fn create_custom_unit(&mut self, unit: CustomUnit) -> CustomUnit {
let id = rand::thread_rng().gen::<i32>();
let unit = CustomUnit { id, ..unit };
self.data.custom_units.insert(id, unit.clone());
unit
}
fn update_session(&mut self, id: i32, session: Session, create: bool) {
assert!(self.data.sessions.contains_key(&id) || create);
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);
self.data.substances.insert(
name.to_string(),
Substance {
name: name.to_string(),
..substance
},
);
}
fn update_sustenance(&mut self, id: &str, sustenance: Sustenance, create: bool) {
assert!(self.data.sustenances.contains_key(id) || create);
self.data.sustenances.insert(
id.to_string(),
Sustenance {
id: id.to_string(),
..sustenance
},
);
}
fn update_custom_unit(&mut self, id: i32, unit: CustomUnit, create: bool) {
assert!(self.data.custom_units.contains_key(&id) || create);
self.data.custom_units.insert(id, CustomUnit { id, ..unit });
}
fn set_session_ingestions(&mut self, session_id: i32, ingestions: Vec<Ingestion>) {
self.data.ingestions.insert(session_id, ingestions);
}
fn first_session_by_title(&self, title: &str) -> Option<Session> {
self.data
.sessions
.values()
.find(|&session| session.title == title)
.cloned()
}
fn resolve_substance_unit(&self, ingestion: &SubstanceIngestion) -> Option<Unit> {
match ingestion.custom_unit_id {
None => match self.get_substance(&ingestion.substance_name) {
Some(substance) => Some(Unit::Simple(substance.unit)),
None => None,
},
Some(custom_unit_id) => self.get_custom_unit(custom_unit_id).map(Unit::Custom),
}
}
} }

View file

@ -1,7 +1,7 @@
use std::{collections::HashMap, fs::File, path::Path}; use std::{collections::HashMap, fs::File, path::Path};
use journal::{ use journal::{
journal::{JSONJournal, Journal}, journal::{JSONJournal, Journal, JournalType},
types::{CustomUnit, Ingestion, Session, Substance}, types::{CustomUnit, Ingestion, Session, Substance},
}; };
use rand::Rng; use rand::Rng;
@ -22,8 +22,13 @@ pub struct JournalImportPsychonautArgs {
#[arg(long)] #[arg(long)]
pub force_create: bool, pub force_create: bool,
#[arg(long, value_hint = clap::ValueHint::FilePath)] #[arg(long)]
pub mappings_file: Option<String>, pub custom_unit_id: Option<i32>,
#[arg(long)]
pub custom_unit_name: Option<String>,
#[arg(long)]
pub custom_substance: Option<String>,
#[arg(long)] #[arg(long)]
pub infer_missing_substances: bool, pub infer_missing_substances: bool,
@ -52,49 +57,126 @@ pub fn journal_import_psychonaut(
let export_file = File::open(args.export_file.clone())?; let export_file = File::open(args.export_file.clone())?;
let export_data: psychonaut_journal_types::ExportData = serde_json::from_reader(export_file)?; let export_data: psychonaut_journal_types::ExportData = serde_json::from_reader(export_file)?;
let mappings: Mappings = match args.mappings_file.clone() {
Some(filename) => {
let mappings_file = File::open(filename)?;
let mappings_data: Mappings = serde_json::from_reader(mappings_file)?;
mappings_data
}
None => Mappings::default(),
};
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
for custom_unit in export_data.custom_units.into_iter() { fn create_or_update_custom_unit(journal: &mut JournalType, unit: CustomUnit) {
journal.set_custom_unit(CustomUnit::from(custom_unit)); match journal.get_custom_unit(unit.id) {
Some(custom_unit) => {
println!(
"Updating Custom Unit ID: {} Name: {}",
unit.id, custom_unit.name
);
journal.update_custom_unit(unit.id, unit, false);
}
None => {
println!("Creating Custom Unit ID: {} Name: {}", unit.id, unit.name);
journal.update_custom_unit(unit.id, unit, true);
}
}
} }
for custom_substance in export_data.custom_substances.into_iter() { if let Some(id) = &args.custom_unit_id {
let unit = export_data
.custom_units
.into_iter()
.find(|unit| unit.id == *id);
match unit {
Some(unit) => {
create_or_update_custom_unit(&mut journal, CustomUnit::from(unit));
}
None => {
panic!("Could not find custom unit with ID: {id}")
}
}
return Ok(());
} else if let Some(name) = &args.custom_unit_name {
let unit = export_data
.custom_units
.into_iter()
.find(|unit| unit.name == *name);
match unit {
Some(unit) => {
create_or_update_custom_unit(&mut journal, CustomUnit::from(unit));
}
None => {
panic!("Could not find custom unit with name: {name}")
}
}
return Ok(());
} else if args.custom_substance.is_none() {
for custom_unit in export_data.custom_units.into_iter() {
create_or_update_custom_unit(&mut journal, CustomUnit::from(custom_unit));
}
}
fn create_or_check_custom_substance(
journal: &mut JournalType,
args: &JournalImportPsychonautArgs,
custom_substance: Substance,
) {
match journal.get_substance(&custom_substance.name) { match journal.get_substance(&custom_substance.name) {
Some(substance) => { Some(substance) => {
if substance.unit != custom_substance.units { println!("Checking substance units {}", custom_substance.name);
if substance.unit != custom_substance.unit {
panic!( panic!(
"mismatch on units for {}: {} != {} ", "mismatch on unit for {}: {} != {} ",
custom_substance.name, substance.unit, custom_substance.units custom_substance.name, substance.unit, custom_substance.unit
) )
} }
} }
None => { None => {
if args.infer_missing_substances { if args.infer_missing_substances {
journal.set_substance(Substance { println!("Creating substance {}", custom_substance.name);
name: custom_substance.name.clone(), journal.create_substance(custom_substance);
description: custom_substance.description.clone(),
unit: custom_substance.units,
});
} }
} }
} }
} }
if let Some(substance_name) = &args.custom_substance {
let custom_substance: Option<psychonaut_journal_types::CustomSubstance> = export_data
.custom_substances
.into_iter()
.find(|substance| substance.name == *substance_name);
match custom_substance {
Some(custom_substance) => {
create_or_check_custom_substance(
&mut journal,
args,
Substance::from(Substance {
name: custom_substance.name.clone(),
description: custom_substance.description.clone(),
unit: custom_substance.units,
}),
);
}
None => {
panic!("Could not find substance with name: {substance_name}")
}
}
} else {
for custom_substance in export_data.custom_substances.into_iter() {
create_or_check_custom_substance(
&mut journal,
args,
Substance {
name: custom_substance.name.clone(),
description: custom_substance.description.clone(),
unit: custom_substance.units,
},
);
}
}
for experience in export_data.experiences.iter() { for experience in export_data.experiences.iter() {
for ingestion in experience.ingestions.iter() { for ingestion in experience.ingestions.iter() {
let substance_name = mappings let substance_name = &ingestion.substance_name;
.substance_names
.get(&ingestion.substance_name)
.unwrap_or(&ingestion.substance_name);
let ingestion_unit = match ingestion.custom_unit_id { let ingestion_unit = match ingestion.custom_unit_id {
Some(id) => journal.get_custom_unit(id).unwrap().original_unit, Some(id) => journal.get_custom_unit(id).unwrap().original_unit,
@ -111,7 +193,7 @@ pub fn journal_import_psychonaut(
} }
} }
None => { None => {
journal.set_substance(Substance { journal.create_substance(Substance {
name: substance_name.clone(), name: substance_name.clone(),
description: "".to_string(), description: "".to_string(),
unit: ingestion_unit, unit: ingestion_unit,
@ -125,14 +207,20 @@ pub fn journal_import_psychonaut(
if journal.first_session_by_title(&experience.title).is_some() { if journal.first_session_by_title(&experience.title).is_some() {
println!("Skipping {} as is already in journal", experience.title); println!("Skipping {} as is already in journal", experience.title);
continue; continue;
} else {
println!("Creating experience {}", experience.title);
} }
let session_id = rng.gen::<i32>(); let session_id = rng.gen::<i32>();
journal.set_session(Session { journal.update_session(
session_id,
Session {
id: session_id, id: session_id,
..Session::from(experience.clone()) ..Session::from(experience.clone())
}); },
true,
);
let mut ingestions: Vec<Ingestion> = Vec::new(); let mut ingestions: Vec<Ingestion> = Vec::new();
for ingestion in experience.ingestions.iter() { for ingestion in experience.ingestions.iter() {