Model/domain/geospatial/planning_restrictions.py
Khalim Conn-Kowlessar dab2e759bf feat(geospatial): read planning restrictions co-located with coordinates
Slice 3a (ADR-0020). PlanningRestrictions relocated out of the solid-wall
generator into domain/geospatial/ as the shared, Property-level value object
(three distinct flags + measure-specific blocks_external/blocks_internal).
GeospatialRepository gains a non-abstract planning_restrictions_for defaulting
to None (sources without the flags need not implement it); GeospatialS3Repository
reads conservation_status/is_listed_building/is_heritage_building from the same
Open-UPRN partition as the coordinates (legacy column names — to confirm in the
S3 deep-dive). Shared _row_for helper dedups the partition lookup.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 16:26:51 +00:00

35 lines
1.4 KiB
Python

"""A Property's planning protections, resolved from geospatial reference data.
Three distinct flags (never the legacy collapsed `restricted_measures` boolean
— ADR-0020): a conservation area, a listed building, a heritage building. They
gate retrofit measures differently — a conservation area blocks external work
only, while listed/heritage protect the fabric itself — so the
measure-specific interpretation (`blocks_external` / `blocks_internal`) lives
here as derived queries. Sourced onto the Property from the geospatial layer
(co-located with the coordinates); defaults to unrestricted.
"""
from dataclasses import dataclass
@dataclass(frozen=True)
class PlanningRestrictions:
"""The planning protections on a Property that gate wall insulation
(ADR-0019). Defaults to unrestricted."""
in_conservation_area: bool = False
is_listed: bool = False
is_heritage: bool = False
@property
def blocks_external(self) -> bool:
"""External wall insulation is blocked by any protection (it alters the
external appearance / protected fabric)."""
return self.in_conservation_area or self.is_listed or self.is_heritage
@property
def blocks_internal(self) -> bool:
"""Internal wall insulation is blocked only where the fabric itself is
protected — a listed or heritage building, not a plain conservation
area."""
return self.is_listed or self.is_heritage