Accept official Statbel geometry date format
This commit is contained in:
@@ -266,6 +266,13 @@ def test_preflight_reports_bounded_topology_repairs(tmp_path: Path) -> None:
|
|||||||
assert result.geometry.payload["features"][0]["geometry"]["type"] == "Polygon"
|
assert result.geometry.payload["features"][0]["geometry"]["type"] == "Polygon"
|
||||||
|
|
||||||
|
|
||||||
|
def test_preflight_accepts_official_slash_geometry_date(tmp_path: Path) -> None:
|
||||||
|
result = validate(tmp_path, geometry_content=geometry_archive(date="2025/01/01"))
|
||||||
|
|
||||||
|
assert len(result.geometry.payload["features"]) == 3
|
||||||
|
assert result.manifest["import_eligible"] is True
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("population_kwargs", "error_code"),
|
("population_kwargs", "error_code"),
|
||||||
[
|
[
|
||||||
@@ -292,6 +299,7 @@ def test_preflight_rejects_population_contract_breaks(
|
|||||||
[
|
[
|
||||||
({"crs": "EPSG:4326"}, "STATBEL_GEOMETRY_CRS_REJECTED"),
|
({"crs": "EPSG:4326"}, "STATBEL_GEOMETRY_CRS_REJECTED"),
|
||||||
({"date": "2026-01-01"}, "STATBEL_GEOMETRY_DATE_MISMATCH"),
|
({"date": "2026-01-01"}, "STATBEL_GEOMETRY_DATE_MISMATCH"),
|
||||||
|
({"date": "2025/13/01"}, "STATBEL_GEOMETRY_DATE_MISMATCH"),
|
||||||
({"municipality_mismatch": True}, "STATBEL_JOIN_MUNICIPALITY_MISMATCH"),
|
({"municipality_mismatch": True}, "STATBEL_JOIN_MUNICIPALITY_MISMATCH"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ GEOMETRY_MEMBER_PATTERN = re.compile(
|
|||||||
r"^sh_statbel_statistical_sectors_(?:31370_)?(20[0-9]{2})0101\.geojson$",
|
r"^sh_statbel_statistical_sectors_(?:31370_)?(20[0-9]{2})0101\.geojson$",
|
||||||
re.IGNORECASE,
|
re.IGNORECASE,
|
||||||
)
|
)
|
||||||
|
GEOMETRY_DATE_PATTERN = re.compile(r"^(20[0-9]{2})([-/])([0-9]{2})\2([0-9]{2})$")
|
||||||
POPULATION_SOURCE_PATTERN = re.compile(
|
POPULATION_SOURCE_PATTERN = re.compile(
|
||||||
r"^/sites/default/files/files/opendata/bevolking/sectoren/"
|
r"^/sites/default/files/files/opendata/bevolking/sectoren/"
|
||||||
r"OPENDATA_SECTOREN_(20[0-9]{2})(?:_(NEW|OLD))?\.zip$",
|
r"OPENDATA_SECTOREN_(20[0-9]{2})(?:_(NEW|OLD))?\.zip$",
|
||||||
@@ -123,6 +124,18 @@ def _layout_from_variant(variant: str | None) -> str:
|
|||||||
return variant.lower()
|
return variant.lower()
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_geometry_date(value: Any) -> str | None:
|
||||||
|
raw_value = str(value or "").strip()
|
||||||
|
match = GEOMETRY_DATE_PATTERN.fullmatch(raw_value)
|
||||||
|
if not match:
|
||||||
|
return None
|
||||||
|
normalized = f"{match.group(1)}-{match.group(3)}-{match.group(4)}"
|
||||||
|
try:
|
||||||
|
return datetime.strptime(normalized, "%Y-%m-%d").date().isoformat()
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _validate_source_url(url: str, pattern: re.Pattern[str], *, code: str) -> re.Match[str]:
|
def _validate_source_url(url: str, pattern: re.Pattern[str], *, code: str) -> re.Match[str]:
|
||||||
parsed = urlsplit(url)
|
parsed = urlsplit(url)
|
||||||
match = pattern.fullmatch(parsed.path)
|
match = pattern.fullmatch(parsed.path)
|
||||||
@@ -428,7 +441,7 @@ def parse_geometry_archive(content: bytes, year: int) -> GeometryArchiveData:
|
|||||||
"Geometry artifact contains duplicate sector codes.",
|
"Geometry artifact contains duplicate sector codes.",
|
||||||
sector_code=sector_code,
|
sector_code=sector_code,
|
||||||
)
|
)
|
||||||
if str(properties.get("dt_situation") or "") != expected_date:
|
if _normalize_geometry_date(properties.get("dt_situation")) != expected_date:
|
||||||
_fail(
|
_fail(
|
||||||
"STATBEL_GEOMETRY_DATE_MISMATCH",
|
"STATBEL_GEOMETRY_DATE_MISMATCH",
|
||||||
"Geometry situation date does not match the population reference year.",
|
"Geometry situation date does not match the population reference year.",
|
||||||
|
|||||||
Reference in New Issue
Block a user