Small fixes
This commit is contained in:
parent
54fbcb1ea6
commit
083f8a982e
24 changed files with 1505 additions and 79 deletions
|
|
@ -3,9 +3,12 @@ import json
|
|||
import polars as pl
|
||||
|
||||
from pipeline.transform.transform_poi import (
|
||||
BUS_STOP_DEDUP_RADIUS_M,
|
||||
_load_ofsted_ratings,
|
||||
_school_icon_category_expr,
|
||||
_significant_tokens,
|
||||
osm_groceries_colocated_with_geolytix,
|
||||
osm_stops_near_naptan,
|
||||
transform,
|
||||
transform_grocery_retail_points,
|
||||
)
|
||||
|
|
@ -71,6 +74,61 @@ def test_osm_groceries_colocated_with_geolytix_handles_empty_inputs():
|
|||
assert osm_groceries_colocated_with_geolytix(osm, empty_glx) == []
|
||||
|
||||
|
||||
def test_significant_tokens_keeps_ms_short_brand():
|
||||
# "M&S" strips to "ms" (len 2); without the short-brand allowlist it would
|
||||
# tokenise to set() and never dedupe against the GEOLYTIX "M&S" chain.
|
||||
assert _significant_tokens("M&S") == {"ms"}
|
||||
assert _significant_tokens("M&S Simply Food") == {"ms", "simply", "food"}
|
||||
# Other sub-3-char tokens stay excluded (allowlist must remain minimal).
|
||||
assert _significant_tokens("Co") == set()
|
||||
|
||||
|
||||
def test_osm_groceries_colocated_with_geolytix_dedupes_m_and_s():
|
||||
# GEOLYTIX brand "M&S" must now match a colocated OSM "M&S Simply Food".
|
||||
geolytix = pl.DataFrame({"category": ["M&S"], "lat": [53.0], "lng": [-1.5]})
|
||||
osm = pl.DataFrame(
|
||||
{
|
||||
"id": ["ms-dup"],
|
||||
"name": ["M&S Simply Food"],
|
||||
"lat": [53.00001],
|
||||
"lng": [-1.5],
|
||||
}
|
||||
)
|
||||
assert osm_groceries_colocated_with_geolytix(osm, geolytix, radius_m=50.0) == [
|
||||
"ms-dup"
|
||||
]
|
||||
|
||||
|
||||
def test_osm_groceries_colocated_default_radius_catches_60m_offset():
|
||||
# OSM and GEOLYTIX routinely place the same store 50-100m apart. The widened
|
||||
# 100m default catches a ~60m same-brand duplicate that the old 50m missed.
|
||||
geolytix = pl.DataFrame({"category": ["Tesco"], "lat": [53.0], "lng": [-1.5]})
|
||||
osm = pl.DataFrame(
|
||||
# ~60 m north of the GEOLYTIX point.
|
||||
{"id": ["dup"], "name": ["Tesco Express"], "lat": [53.00054], "lng": [-1.5]}
|
||||
)
|
||||
assert osm_groceries_colocated_with_geolytix(osm, geolytix) == ["dup"]
|
||||
assert osm_groceries_colocated_with_geolytix(osm, geolytix, radius_m=50.0) == []
|
||||
|
||||
|
||||
def test_osm_stops_near_naptan_drops_within_radius_keeps_far():
|
||||
# A ~60m-offset OSM platform duplicates the NaPTAN stop (dropped at the 100m
|
||||
# default); a genuine opposite-direction stop ~160m away is kept.
|
||||
naptan = pl.DataFrame({"id": ["n1"], "lat": [51.5000], "lng": [-0.1000]})
|
||||
osm = pl.DataFrame(
|
||||
{
|
||||
"id": ["dup", "far"],
|
||||
# ~60 m and ~160 m north of the NaPTAN stop.
|
||||
"lat": [51.50054, 51.50144],
|
||||
"lng": [-0.1000, -0.1000],
|
||||
}
|
||||
)
|
||||
assert BUS_STOP_DEDUP_RADIUS_M == 100.0
|
||||
assert osm_stops_near_naptan(osm, naptan) == ["dup"]
|
||||
# At the old 50m radius the duplicate survived (the bug this fix closes).
|
||||
assert osm_stops_near_naptan(osm, naptan, radius_m=50.0) == []
|
||||
|
||||
|
||||
def _write_boundary(tmp_path):
|
||||
"""A FeatureCollection whose single feature covers the London-area test
|
||||
coords used by the transform() fixtures, so in_england_mask keeps them."""
|
||||
|
|
@ -505,6 +563,32 @@ def test_osm_supermarkets_dropped(tmp_path):
|
|||
assert convenience.height == 1
|
||||
|
||||
|
||||
def test_transform_relabels_osm_iceland_to_brand(tmp_path):
|
||||
# OSM tags Iceland as shop/frozen_food -> "Deli & Specialty". A genuine
|
||||
# Iceland store GEOLYTIX lacks (away from any GEOLYTIX point) must survive
|
||||
# AND be relabelled to the "Iceland" brand, not ship mislabelled as a deli.
|
||||
raw = pl.DataFrame(
|
||||
{
|
||||
"id": ["ice1"],
|
||||
"name": ["Iceland"],
|
||||
"category": ["shop/frozen_food"],
|
||||
"lat": [51.40],
|
||||
"lng": [-0.05],
|
||||
}
|
||||
)
|
||||
inputs = _write_transform_inputs(tmp_path, raw)
|
||||
|
||||
out = transform(**inputs).collect()
|
||||
|
||||
row = out.filter(pl.col("name") == "Iceland")
|
||||
assert row.height == 1
|
||||
assert row["group"][0] == "Groceries"
|
||||
assert row["category"][0] == "Iceland"
|
||||
assert row["icon_category"][0] == "Iceland"
|
||||
# No deli pin remains for the relabelled store.
|
||||
assert out.filter(pl.col("category") == "Deli & Specialty").height == 0
|
||||
|
||||
|
||||
def test_transform_grocery_dedup_drops_only_grocery_aspect(tmp_path):
|
||||
# The _write_transform_inputs fixture seeds 5 GEOLYTIX "Tesco" points at
|
||||
# (51.52, -0.14). An OSM object colocated there carrying "Tesco" in its name
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue