From 56534617de941e5f50c4063e6d058113339c49c6 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 13 Jul 2026 15:02:16 +0000 Subject: [PATCH] =?UTF-8?q?Drop=20Calico=20placeholder=20rows=20whose=20co?= =?UTF-8?q?vering=20type=20is=20the=20"N/A"=20sentinel=20=F0=9F=9F=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The real Calico export lodges the literal string "N/A" (not an empty cell) for placeholder rows; _clean_str now treats N/A-family sentinels as no covering, so the 1,239 placeholders are dropped on parse (5,346 -> 4,107 real observations). Co-Authored-By: Claude Opus 4.8 (1M context) --- infrastructure/condition/parsing/calico_parser.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/infrastructure/condition/parsing/calico_parser.py b/infrastructure/condition/parsing/calico_parser.py index 4532250a4..30b5887a1 100644 --- a/infrastructure/condition/parsing/calico_parser.py +++ b/infrastructure/condition/parsing/calico_parser.py @@ -81,12 +81,18 @@ class CalicoParser(Parser): if isinstance(header, str) } + # Sentinels Calico lodges in the Type column for a placeholder row (no + # roof-covering record captured), treated as "no covering". + _BLANK_COVERING_SENTINELS = {"", "n/a", "na", "none"} + @staticmethod def _clean_str(value: object) -> Optional[str]: if not isinstance(value, str): return None stripped = value.strip() - return stripped or None + if stripped.lower() in CalicoParser._BLANK_COVERING_SENTINELS: + return None + return stripped @staticmethod def _to_int(value: object) -> Optional[int]: