use fathom_function::forms::TableCellValue; use fathom_function::tracing; use pipeline_application::application::{ Application, CrossingConfiguration, CrossingKind, FacilityType, SegmentationConfiguration, uom::si::f64::Length, uom::si::length::meter, }; use serde::{Deserialize, Serialize}; use uuid::Uuid; #[fathom_function::function] async fn segment(input: Input) -> Result { let app = Application::new_from_compile_env(input.org_id, input.project_id).unwrap(); for pipeline_id in input.pipeline_id { app.segment(pipeline_id, &input.configuration) .await .map_err(|err| { tracing::error!(%pipeline_id, ?err, "Error running segmentation algorithm"); format!("{err:?}") })?; } Ok(Output { status: "Success".to_owned(), }) } #[derive(Debug, Serialize)] struct Output { status: String, } #[derive(Debug, Deserialize)] struct Input { org_id: Uuid, project_id: String, pipeline_id: Vec, #[serde(flatten)] configuration: Configuration, } #[derive(Debug, Deserialize)] struct Configuration { facilities: Vec, local_data: Vec, crossings: Crossings, } #[derive(Debug, Deserialize)] struct Crossings { highway: CrossingRow, road: CrossingRow, river: CrossingRow, railroad: CrossingRow, overhead: CrossingRow, pipeline: CrossingRow, } impl Crossings { fn to_crossing_configurations(&self) -> impl Iterator { [ self.highway .to_crossing_configuration(CrossingKind::Highway), self.road.to_crossing_configuration(CrossingKind::Road), self.river.to_crossing_configuration(CrossingKind::River), self.railroad .to_crossing_configuration(CrossingKind::Railroad), self.overhead .to_crossing_configuration(CrossingKind::Overhead), self.pipeline .to_crossing_configuration(CrossingKind::Pipeline), ] .into_iter() .flatten() } } #[derive(Debug, Deserialize)] struct CrossingRow { longer_than: TableCellValue, selected: TableCellValue, } impl CrossingRow { fn to_crossing_configuration( &self, crossing_kind: CrossingKind, ) -> Option { if (&self.selected).try_into().unwrap_or(false) { Some(CrossingConfiguration { crossing_kind, longer_than: Length::new::( (&self.longer_than).try_into().ok().unwrap_or(0.0), ), }) } else { None } } } impl From<&Configuration> for SegmentationConfiguration { fn from(value: &Configuration) -> Self { let mut config = Self::default_false(); config = value .facilities .iter() .fold(config, |config, fac| match fac { FacilityType::InsulationJoint => config.by_insulation_joints(true), FacilityType::Repair => config.by_repairs(true), FacilityType::Valve => config.by_valves(true), _ => config, }); config = value.crossings.to_crossing_configurations().fold( config, SegmentationConfiguration::with_crossing_configuration, ); value .local_data .iter() .fold(config, |config, loc| match loc.as_str() { "material_grade" => config.by_material_grade(true), "coating_type" => config.by_coating_type(true), "design_factor" => config.by_design_factor(true), "high_consequence_area" => config.by_high_consequence_area(true), "unusual_sensitive_area" => config.by_unusual_sensitive_area(true), "joint_type" => config.by_joint_type(true), "soil_type" => config.by_soil_type(true), "soil_ph" => config.by_soil_ph(true), _ => config, }) } }