from __future__ import annotations from app.repositories.scheme_groups import list_scheme_version_groups from app.repositories.scheme_seats import list_scheme_version_seats from app.repositories.scheme_sectors import list_scheme_version_sectors from app.services.baseline_selector import select_baseline_scheme_version def _sector_compare_value(row) -> dict: return { "element_id": row.element_id, "sector_id": row.sector_id, "name": row.name, "classes_raw": row.classes_raw, } def _sector_response_value(row) -> dict: payload = _sector_compare_value(row) payload["sector_record_id"] = row.sector_record_id return payload def _group_compare_value(row) -> dict: return { "element_id": row.element_id, "group_id": row.group_id, "name": row.name, "classes_raw": row.classes_raw, } def _group_response_value(row) -> dict: payload = _group_compare_value(row) payload["group_record_id"] = row.group_record_id return payload def _seat_compare_value(row) -> dict: return { "element_id": row.element_id, "seat_id": row.seat_id, "sector_id": row.sector_id, "group_id": row.group_id, "row_label": row.row_label, "seat_number": row.seat_number, } def _seat_response_value(row) -> dict: payload = _seat_compare_value(row) payload["seat_record_id"] = row.seat_record_id return payload def _build_diff( *, before_compare_map: dict, after_compare_map: dict, before_payload_map: dict, after_payload_map: dict, ) -> list[dict]: keys = sorted(set(before_payload_map.keys()) | set(after_payload_map.keys())) result: list[dict] = [] for key in keys: before_compare = before_compare_map.get(key) after_compare = after_compare_map.get(key) before_payload = before_payload_map.get(key) after_payload = after_payload_map.get(key) if before_compare is None and after_compare is not None: status = "added" elif before_compare is not None and after_compare is None: status = "removed" elif before_compare != after_compare: status = "changed" else: status = "unchanged" result.append( { "key": key, "status": status, "before": before_payload, "after": after_payload, } ) return result def _sector_key(row) -> str: return row.sector_id if row.sector_id else (row.element_id if row.element_id else row.sector_record_id) def _group_key(row) -> str: return row.group_id if row.group_id else (row.element_id if row.element_id else row.group_record_id) def _seat_key(row) -> str: return row.seat_id if row.seat_id else (row.element_id if row.element_id else row.seat_record_id) async def build_structure_diff( *, scheme_id: str, draft_scheme_version_id: str, baseline_override_scheme_version_id: str | None = None, ) -> dict: baseline, baseline_strategy = await select_baseline_scheme_version( scheme_id=scheme_id, draft_scheme_version_id=draft_scheme_version_id, override_scheme_version_id=baseline_override_scheme_version_id, ) draft_sectors = await list_scheme_version_sectors(draft_scheme_version_id) draft_groups = await list_scheme_version_groups(draft_scheme_version_id) draft_seats = await list_scheme_version_seats(draft_scheme_version_id) if baseline is None: baseline_sector_compare_map = {} baseline_group_compare_map = {} baseline_seat_compare_map = {} baseline_sector_payload_map = {} baseline_group_payload_map = {} baseline_seat_payload_map = {} baseline_scheme_version_id = None else: baseline_scheme_version_id = baseline.scheme_version_id baseline_sectors = await list_scheme_version_sectors(baseline.scheme_version_id) baseline_groups = await list_scheme_version_groups(baseline.scheme_version_id) baseline_seats = await list_scheme_version_seats(baseline.scheme_version_id) baseline_sector_compare_map = { _sector_key(row): _sector_compare_value(row) for row in baseline_sectors } baseline_sector_payload_map = { _sector_key(row): _sector_response_value(row) for row in baseline_sectors } baseline_group_compare_map = { _group_key(row): _group_compare_value(row) for row in baseline_groups } baseline_group_payload_map = { _group_key(row): _group_response_value(row) for row in baseline_groups } baseline_seat_compare_map = { _seat_key(row): _seat_compare_value(row) for row in baseline_seats } baseline_seat_payload_map = { _seat_key(row): _seat_response_value(row) for row in baseline_seats } draft_sector_compare_map = {_sector_key(row): _sector_compare_value(row) for row in draft_sectors} draft_sector_payload_map = {_sector_key(row): _sector_response_value(row) for row in draft_sectors} draft_group_compare_map = {_group_key(row): _group_compare_value(row) for row in draft_groups} draft_group_payload_map = {_group_key(row): _group_response_value(row) for row in draft_groups} draft_seat_compare_map = {_seat_key(row): _seat_compare_value(row) for row in draft_seats} draft_seat_payload_map = {_seat_key(row): _seat_response_value(row) for row in draft_seats} sector_diff = _build_diff( before_compare_map=baseline_sector_compare_map, after_compare_map=draft_sector_compare_map, before_payload_map=baseline_sector_payload_map, after_payload_map=draft_sector_payload_map, ) group_diff = _build_diff( before_compare_map=baseline_group_compare_map, after_compare_map=draft_group_compare_map, before_payload_map=baseline_group_payload_map, after_payload_map=draft_group_payload_map, ) seat_diff = _build_diff( before_compare_map=baseline_seat_compare_map, after_compare_map=draft_seat_compare_map, before_payload_map=baseline_seat_payload_map, after_payload_map=draft_seat_payload_map, ) return { "baseline_scheme_version_id": baseline_scheme_version_id, "baseline_strategy": baseline_strategy, "summary": { "sectors_added": sum(1 for item in sector_diff if item["status"] == "added"), "sectors_removed": sum(1 for item in sector_diff if item["status"] == "removed"), "sectors_changed": sum(1 for item in sector_diff if item["status"] == "changed"), "groups_added": sum(1 for item in group_diff if item["status"] == "added"), "groups_removed": sum(1 for item in group_diff if item["status"] == "removed"), "groups_changed": sum(1 for item in group_diff if item["status"] == "changed"), "seats_added": sum(1 for item in seat_diff if item["status"] == "added"), "seats_removed": sum(1 for item in seat_diff if item["status"] == "removed"), "seats_changed": sum(1 for item in seat_diff if item["status"] == "changed"), }, "sectors": sector_diff, "groups": group_diff, "seats": seat_diff, }