lgtm
This commit is contained in:
parent
2efa4d9f47
commit
5e73287eaf
99 changed files with 6392 additions and 1462 deletions
|
|
@ -11,8 +11,8 @@ use crate::data::{FeatureStats, PostcodePoiMetrics, PropertyData};
|
|||
use crate::utils::{postcode_outcode, postcode_sector};
|
||||
|
||||
use super::hexagon_stats::{
|
||||
CrimeAreaAverage, CrimeYearPoint, CrimeYearStats, EnumFeatureStats, HistogramStats,
|
||||
NumericFeatureStats, PricePoint,
|
||||
CrimeAreaAverage, CrimeYearPoint, CrimeYearStats, EnumFeatureStats,
|
||||
HistogramStats, NumericFeatureStats, PricePoint,
|
||||
};
|
||||
|
||||
/// Extract price history (year, price) pairs from matching rows, downsampled if needed.
|
||||
|
|
@ -352,11 +352,14 @@ pub fn compute_crime_by_year(
|
|||
let mut out = Vec::new();
|
||||
for (type_idx, name) in crime_by_year.crime_types.iter().enumerate() {
|
||||
// Crime types in the by-year side table are bare (e.g. "Burglary"), while
|
||||
// the configured feature names carry an " (avg/yr)" suffix. Match either
|
||||
// form so callers can pass the feature names they already know.
|
||||
// the configured feature names carry a window suffix ("Burglary (/yr,
|
||||
// 7y)"). Emit the bare-type trend if the bare name is requested directly or
|
||||
// any of its windowed features is.
|
||||
if fields_specified {
|
||||
let with_suffix = format!("{name} (avg/yr)");
|
||||
if !field_set.contains(name.as_str()) && !field_set.contains(with_suffix.as_str()) {
|
||||
let prefix = format!("{name} (");
|
||||
if !field_set.contains(name.as_str())
|
||||
&& !field_set.iter().any(|f| f.starts_with(&prefix))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
@ -395,6 +398,7 @@ pub fn compute_crime_by_year(
|
|||
out
|
||||
}
|
||||
|
||||
|
||||
/// Latest year present anywhere in the by-year crime dataset. The client
|
||||
/// compares each selection's last charted year against this to caption
|
||||
/// force-level publication gaps (e.g. Greater Manchester ends mid-2019) as
|
||||
|
|
@ -440,13 +444,10 @@ pub fn area_crime_averages_for(
|
|||
|
||||
let mut out = Vec::new();
|
||||
for (idx, name) in averages.crime_types.iter().enumerate() {
|
||||
// Crime types are bare here ("Burglary"); requested fields may carry the
|
||||
// " (avg/yr)" suffix — accept either form, like compute_crime_by_year.
|
||||
if fields_specified {
|
||||
let with_suffix = format!("{name} (avg/yr)");
|
||||
if !field_set.contains(name.as_str()) && !field_set.contains(with_suffix.as_str()) {
|
||||
continue;
|
||||
}
|
||||
// `name` is the full crime-feature name here (e.g. "Burglary (/yr,
|
||||
// 7y)"), matching exactly the feature fields the caller requests.
|
||||
if fields_specified && !field_set.contains(name.as_str()) {
|
||||
continue;
|
||||
}
|
||||
let national_val = finite_at(Some(&averages.national), idx);
|
||||
let outcode_val = finite_at(outcode_means, idx);
|
||||
|
|
@ -595,7 +596,10 @@ mod tests {
|
|||
let mut by_sector = rustc_hash::FxHashMap::default();
|
||||
by_sector.insert("E14 2".to_string(), vec![5.0, 7.0]);
|
||||
AreaCrimeAverages {
|
||||
crime_types: vec!["Burglary".to_string(), "Robbery".to_string()],
|
||||
crime_types: vec![
|
||||
"Burglary (/yr, 7y)".to_string(),
|
||||
"Robbery (/yr, 7y)".to_string(),
|
||||
],
|
||||
national: vec![8.0, 6.0],
|
||||
by_outcode,
|
||||
by_sector,
|
||||
|
|
@ -611,12 +615,18 @@ mod tests {
|
|||
assert_eq!(sector.as_deref(), Some("E14 2"));
|
||||
assert_eq!(out.len(), 2);
|
||||
|
||||
let burglary = out.iter().find(|c| c.name == "Burglary").unwrap();
|
||||
let burglary = out
|
||||
.iter()
|
||||
.find(|c| c.name == "Burglary (/yr, 7y)")
|
||||
.unwrap();
|
||||
assert_eq!(burglary.national, Some(8.0));
|
||||
assert_eq!(burglary.outcode, Some(10.0));
|
||||
assert_eq!(burglary.sector, Some(5.0));
|
||||
|
||||
let robbery = out.iter().find(|c| c.name == "Robbery").unwrap();
|
||||
let robbery = out
|
||||
.iter()
|
||||
.find(|c| c.name == "Robbery (/yr, 7y)")
|
||||
.unwrap();
|
||||
assert_eq!(robbery.national, Some(6.0));
|
||||
// The outcode value was NaN — dropped to None; the sector value is finite.
|
||||
assert_eq!(robbery.outcode, None);
|
||||
|
|
@ -626,11 +636,13 @@ mod tests {
|
|||
#[test]
|
||||
fn area_crime_averages_respect_fields_filter() {
|
||||
let avgs = sample_averages();
|
||||
// The suffixed feature-name form is accepted, like compute_crime_by_year.
|
||||
let fields: HashSet<String> = ["Burglary (avg/yr)".to_string()].into_iter().collect();
|
||||
// Area averages are keyed by the full crime-feature name.
|
||||
let fields: HashSet<String> = ["Burglary (/yr, 7y)".to_string()]
|
||||
.into_iter()
|
||||
.collect();
|
||||
let (_, _, out) = area_crime_averages_for(Some("E14 2DG"), &avgs, true, &fields);
|
||||
assert_eq!(out.len(), 1);
|
||||
assert_eq!(out[0].name, "Burglary");
|
||||
assert_eq!(out[0].name, "Burglary (/yr, 7y)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue