use crate::{
Address, AddressErrorKind, FireInspection, FireInspections, Geographic, IntoCsv, Io,
MatchPartialRecord, MatchPartialRecords, MatchStatus, from_csv, to_csv,
};
use derive_more::{Deref, DerefMut};
use indicatif::ParallelProgressIterator;
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use tracing::info;
#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct FireInspectionMatch {
inspection: FireInspection,
record: MatchPartialRecords,
}
impl FireInspectionMatch {
#[tracing::instrument(skip_all)]
pub fn compare<T: Address + Geographic>(inspection: &FireInspection, addresses: &[T]) -> Self {
let record = MatchPartialRecord::compare(inspection.address(), addresses);
FireInspectionMatch {
inspection: inspection.clone(),
record,
}
}
pub fn inspection(&self) -> FireInspection {
self.inspection.to_owned()
}
pub fn record(&self) -> MatchPartialRecords {
self.record.to_owned()
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize, Serialize, Deref, DerefMut)]
pub struct FireInspectionMatches(Vec<FireInspectionMatch>);
impl FireInspectionMatches {
pub fn compare<T: Address + Geographic + Send + Sync>(
inspections: &FireInspections,
addresses: &[T],
) -> Self {
let style = indicatif::ProgressStyle::with_template(
"[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {'Comparing addresses.'}",
)
.unwrap();
let records = inspections
.par_iter()
.map(|r| FireInspectionMatch::compare(r, addresses))
.progress_with_style(style)
.collect::<Vec<FireInspectionMatch>>();
FireInspectionMatches(records)
}
pub fn filter(&mut self, filter: &str) {
match filter {
"missing" => self.retain(|r| r.record()[0].match_status() == MatchStatus::Missing),
"divergent" => self.retain(|r| r.record()[0].match_status() == MatchStatus::Divergent),
"matching" => self.retain(|r| r.record()[0].match_status() == MatchStatus::Matching),
_ => info!("Invalid filter provided."),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct FireInspectionMatchRecord {
status: MatchStatus,
name: String,
address_label: String,
other_label: Option<String>,
longitude: Option<f64>,
latitude: Option<f64>,
}
impl FireInspectionMatchRecord {
pub fn status(&self) -> MatchStatus {
self.status.to_owned()
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize, Deref, DerefMut)]
pub struct FireInspectionMatchRecords(Vec<FireInspectionMatchRecord>);
impl FireInspectionMatchRecords {
pub fn filter(&mut self, filter: &str) {
match filter {
"missing" => self.retain(|r| r.status() == MatchStatus::Missing),
"divergent" => self.retain(|r| r.status() == MatchStatus::Divergent),
"matching" => self.retain(|r| r.status() == MatchStatus::Matching),
_ => info!("Invalid filter provided."),
}
}
}
impl IntoCsv<FireInspectionMatchRecords> for FireInspectionMatchRecords {
fn from_csv<P: AsRef<std::path::Path>>(path: P) -> Result<Self, Io> {
let records = from_csv(path)?;
Ok(Self(records))
}
fn to_csv<P: AsRef<std::path::Path>>(&mut self, path: P) -> Result<(), AddressErrorKind> {
to_csv(&mut self.0, path.as_ref().into())
}
}
impl From<&FireInspectionMatch> for FireInspectionMatchRecords {
fn from(inspection: &FireInspectionMatch) -> Self {
let mut records = Vec::new();
let name = inspection.inspection().name().clone();
let address_label = inspection.inspection().address().label();
for record in inspection.record().iter() {
records.push(FireInspectionMatchRecord {
status: record.match_status(),
name: name.to_owned(),
address_label: address_label.to_owned(),
other_label: record.other_label(),
longitude: record.longitude(),
latitude: record.latitude(),
});
}
FireInspectionMatchRecords(records)
}
}
impl From<&FireInspectionMatches> for FireInspectionMatchRecords {
fn from(inspections: &FireInspectionMatches) -> Self {
let mut records = Vec::new();
for record in inspections.iter() {
let matches = FireInspectionMatchRecords::from(record);
for item in matches.iter() {
records.push(item.clone());
}
}
FireInspectionMatchRecords(records)
}
}