From 7308979825d8274190ac43d70d5e66b23102bdf1 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Mon, 13 Jul 2026 14:59:04 +0000 Subject: [PATCH] =?UTF-8?q?Parse=20a=20Calico=20workbook=20and=20drop=20ro?= =?UTF-8?q?ws=20with=20no=20covering=20type=20=F0=9F=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- .../condition/parsing/calico_parser.py | 13 ++++ tests/condition/parsing/test_calico_parser.py | 63 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 infrastructure/condition/parsing/calico_parser.py create mode 100644 tests/condition/parsing/test_calico_parser.py diff --git a/infrastructure/condition/parsing/calico_parser.py b/infrastructure/condition/parsing/calico_parser.py new file mode 100644 index 000000000..5d4edad0b --- /dev/null +++ b/infrastructure/condition/parsing/calico_parser.py @@ -0,0 +1,13 @@ +from typing import BinaryIO, List + +from domain.condition.records.calico.calico_property import CalicoProperty +from infrastructure.condition.parsing.parser import Parser +from repositories.condition.uprn_lookup import UprnLookup + + +class CalicoParser(Parser): + def __init__(self, uprn_lookup: UprnLookup) -> None: + self._uprn_lookup = uprn_lookup + + def parse(self, file_stream: BinaryIO) -> List[CalicoProperty]: + raise NotImplementedError diff --git a/tests/condition/parsing/test_calico_parser.py b/tests/condition/parsing/test_calico_parser.py new file mode 100644 index 000000000..563b7394e --- /dev/null +++ b/tests/condition/parsing/test_calico_parser.py @@ -0,0 +1,63 @@ +from datetime import date +from io import BytesIO +from typing import Dict, List, Optional + +from openpyxl import Workbook + +from infrastructure.condition.parsing.calico_parser import CalicoParser +from repositories.condition.uprn_lookup import UprnLookup + +HEADERS = [ + "Asset Reference", + "Asset Type", + "Address", + "Path", + "Type", + "Fitted / Renewed Date", + "Quantity", + "Remaining Life", +] + + +class FakeUprnLookup(UprnLookup): + def __init__(self, mapping: Dict[str, int]) -> None: + self._mapping = mapping + + def get_property_ref_to_uprn_lookup(self) -> Dict[str, int]: + return self._mapping + + +def _workbook(rows: List[List[object]]) -> BytesIO: + wb = Workbook() + ws = wb.active + assert ws is not None + ws.append(HEADERS) + for row in rows: + ws.append(row) + buffer = BytesIO() + wb.save(buffer) + buffer.seek(0) + return buffer + + +def test_calico_parser_drops_rows_with_no_covering_type(): + # Arrange + workbook = _workbook( + [ + [443, "HOUSE", "31 Venice Avenue", "Roof Covering", "Concrete Tiles", + date(1998, 4, 1), 55, 36], + [438, "FLAT", "137 Brownhill Avenue", "Roof Covering", None, + date(2000, 4, 1), 0, 973], + ] + ) + parser = CalicoParser(FakeUprnLookup({"443": 100093305101, "438": 100093388053})) + + # Act + properties = parser.parse(workbook) + + # Assert + all_assets = [asset for prop in properties for asset in prop.assets] + references: List[int] = [asset.asset_reference for asset in all_assets] + coverings: List[Optional[str]] = [asset.covering_type for asset in all_assets] + assert references == [443] + assert coverings == ["Concrete Tiles"]