diff --git a/src/create_products.rs b/src/create_products.rs index a382e20..8a361ab 100644 --- a/src/create_products.rs +++ b/src/create_products.rs @@ -9,16 +9,36 @@ use chrono::NaiveDateTime; use rayon::prelude::{IntoParallelRefIterator, ParallelBridge, ParallelIterator}; use serde::Serialize; -struct Filter {} +struct Filter { + // Equal/not equal + equal: bool, + field: String, + value: String, +} -struct Constraint {} +enum ConstraintType { + Equal, + GreaterThan, + GreaterThanOrEqualTo, + LessThan, + LessThanOrEqualTo, + NotEqualTo, +} + +struct Constraint { + source_type: String, + field: String, + constraint_type: ConstraintType, + value: String, +} enum Component { Constant(String), - Field(String), + // Even extras are allowed here, just specify the field type (encounter, service, etc) and the field name (incl Extra: or Classification: as appropriate) + Field(String, String), } -#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone)] enum BuildFrom { Service, Transfer, @@ -27,6 +47,7 @@ enum BuildFrom { CodingDiagnosis, // TODO: This is hard/expensive, ignore for now as we don't have test data LinkedDataset, + Revenue, } impl From<&String> for BuildFrom { @@ -38,29 +59,101 @@ impl From<&String> for BuildFrom { "CD" => BuildFrom::CodingDiagnosis, "T" => BuildFrom::Transfer, "BS" => BuildFrom::LinkedDataset, + "R" => BuildFrom::Revenue, + _ => panic!(), } } } +// Frequency per type: +// Linked Dataset: One Per Area, One per Built Service code, One per source item +// Coding Diagnosis, Coding Procedure, Encounter, Revenue, Service: One per source item +// Transfer: Change in bed number, change in clinic, change in consultant, change in consultant specialty, change in consultant specialty rollup, change in unit, change in ward, daily, daily or Change in bed number, daily or change in clinic, daily or change in consultant, daily or change in consultant specialty, cdaily or hange in consultant specialty rollup, daily or change in unit, daily or change in ward, daily except on admission day, daily except on admission day (incl same day), daily except on discharge day, daily except on discharge day (incl same day), Only admission location, only discharge location, one per source item enum Frequency { - OnePerSource, - DailyOrChangeInWard, + ChangeInBedNumber, + ChangeInClinic, + ChangeInConsultant, + ChangeInConsultantSpecialty, + ChangeInConsultantSpecialtyRollup, + ChangeInUnit, + ChangeInWard, Daily, + DailyOrChangeInBedNumber, + DailyOrChangeInClinic, + DailyOrChangeInConsultant, + DailyOrChangeInConsultantSpecialty, + DailyOrChangeInConsultantSpecialtyRollup, + DailyOrChangeInUnit, + DailyOrChangeInWard, + DailyExceptOnAdmissionDay, + DailyExceptOnAdmissionDayInclSameDay, + DailyExceptOnDischargeDay, + DailyExceptOnDischargeDayInclSameDay, + OnlyAdmissionLocation, + OnlyDischargeLocation, + OnePerSource, } impl From<&String> for Frequency { fn from(frequency: &String) -> Self { match frequency.as_str() { - "S" => Frequency::OnePerSource, + "O" => Frequency::OnePerSource, "DOCW" => Frequency::DailyOrChangeInWard, "D" => Frequency::Daily, + "DOCC" => Frequency::DailyOrChangeInClinic, + "DEAD" => Frequency::DailyExceptOnAdmissionDay, + "OAL" => Frequency::OnlyAdmissionLocation, + "CIW" => Frequency::ChangeInWard, + _ => panic!(), } } } +enum RoundingMode { + ToClosestWhole, + UpToClosestWhole, + DownToClosestWhole, + None, +} + +impl From<&String> for RoundingMode { + fn from(rounding: &String) -> Self { + match rounding.as_str() { + "U" => RoundingMode::UpToClosestWhole, + "N" => RoundingMode::None, + "D" => RoundingMode::DownToClosestWhole, + "T" => RoundingMode::ToClosestWhole, + _ => panic!(), + } + } +} + +// enum ExtraValue { +// string(String), +// numeric(f64), +// datetime(NaiveDateTime), +// } + +// struct Extra { +// extraType: String, +// value: ExtraValue, +// } + +// Quantities per type: +// Built Service: Constant, SourceQuantity +// Coding Diagnosis: Costant, Extra +// Coding Procedure: Costant, Extra +// Encounter: Admission Weight, Age, Constant, Days, Expected Length of Stay, Extra, Hours, ICU Hours, Length of Stay, Mech Vent Hours, Revenue, Weighted Separation +// Revenue: Constant, Extra, SourceQuantity +// Service: Constant, Extra, SourceQuantity +// Transfer: Constant, Days, Extra, Hours enum Quantity { Constant(f64), + // Name of the extra Extra(String), + SourceQuantity, + Hours(RoundingMode), + Days(RoundingMode), } enum DurationFallback { @@ -132,11 +225,10 @@ where for record in definitions.deserialize::>() { let record = record?; // Get the type, then switch based on that, as that's how we determine whether we've got a definition/filter/component/constraint (definition should always come first) - let recordType = record.get("Type").unwrap(); - match recordType.as_str() { + let record_type = record.get("Type").unwrap(); + match record_type.as_str() { "Definition" => { - let build_quantity = - all_definitions.insert( + let build_quantity = all_definitions.insert( record.get("Name").unwrap().to_owned(), Definition { name: record.get("Name").unwrap().to_owned(), @@ -145,19 +237,19 @@ where constraints: vec![], build_from: BuildFrom::from(record.get("BuildFrom").unwrap()), frequency: Frequency::from(record.get("Frequency").unwrap()), - quantity: , - duration_fallback: (), + quantity: Quantity::Constant(1.), + duration_fallback: DurationFallback::BuiltService, }, ); } "Filter" => {} "Component" => {} "Constraint" => {} - _ => continue, + unknown => println!("Invalid type found: {}", unknown), } } - let mut mapped_definitions = all_definitions + let mut mapped_definitions: HashMap = all_definitions .into_values() .map(|value| (value.build_from, value)) .collect();