diff --git a/domain/epc/property_overrides/glazing_mix_guard.py b/domain/epc/property_overrides/glazing_mix_guard.py index 83f4c45c0..36d8fce58 100644 --- a/domain/epc/property_overrides/glazing_mix_guard.py +++ b/domain/epc/property_overrides/glazing_mix_guard.py @@ -1,9 +1,17 @@ from __future__ import annotations +import re from typing import Optional from domain.epc.property_overrides.glazing_type import GlazingType +# "N% " — the era (2002 / pre-2002) is irrelevant to whether it +# is a mix, so only the base type is captured. +_PERCENT_TYPE = re.compile(r"(\d+)%\s*(single|double|triple|secondary)") +# One base type at or above this share is a uniform assertion, not a mix — applied +# as that type (a near-uniform reglaze), not deferred. +_UNIFORM_THRESHOLD_PERCENT = 90 + def glazing_mix_guard(description: str) -> Optional[GlazingType]: """Deterministically resolve an aggregate-mix glazing description to ``MIXED``. @@ -21,4 +29,12 @@ def glazing_mix_guard(description: str) -> Optional[GlazingType]: uniform assertion or an unparseable description, so the LLM classifier still resolves it (a uniform reglaze is applied; varied phrasings are the LLM's job). """ - raise NotImplementedError + matches = _PERCENT_TYPE.findall(description.lower()) + base_types = {base_type for _, base_type in matches} + if len(base_types) < 2: + # One (or no) glazing type named — a uniform assertion, not a mix. + return None + if max(int(percent) for percent, _ in matches) >= _UNIFORM_THRESHOLD_PERCENT: + # One type dominates — treat as that (near-)uniform type, not a mix. + return None + return GlazingType.MIXED