diff --git a/backend/magic_plan/audit_script.py b/backend/magic_plan/audit_script.py index 2eccdb27..deea7b8d 100644 --- a/backend/magic_plan/audit_script.py +++ b/backend/magic_plan/audit_script.py @@ -17,7 +17,11 @@ _BOLD = Font(bold=True) _TOTAL_COLS = 25 # A–Y _BLANK_SEPARATOR_COLS = {5, 21} _MEDIUM_RIGHT = Border(right=Side(style="medium")) +_MEDIUM_LEFT = Border(left=Side(style="medium")) +_MEDIUM_BOTTOM = Side(style="medium") _SECTION_BOUNDARY_COLS = {4, 11, 15, 20} +_SECTION_START_COLS = {6, 22} +_FLOOR_MERGE_SEGMENTS = [(1, 4), (6, 20), (22, 25)] @dataclass @@ -35,6 +39,7 @@ class AuditRow: window_opening_type: Optional[str] = None door_location: Optional[str] = None door_width_mm: Optional[float] = None + is_last_room_row: bool = False opening_count: Optional[int] = None opening_width_m: Optional[float] = None opening_height_m: Optional[float] = None @@ -80,6 +85,9 @@ def _build_rows(plan: Plan) -> list[AuditRow]: row.door_location = room.name row.door_width_mm = door.width_mm + if i == n_rows - 1: + row.is_last_room_row = True + rows.append(row) return rows @@ -88,6 +96,19 @@ def _build_rows(plan: Plan) -> list[AuditRow]: def _apply_section_borders(ws: Worksheet, row: int) -> None: for col in _SECTION_BOUNDARY_COLS: ws.cell(row=row, column=col).border = _MEDIUM_RIGHT + for col in _SECTION_START_COLS: + ws.cell(row=row, column=col).border = _MEDIUM_LEFT + + +def _apply_room_bottom_border(ws: Worksheet, row: int) -> None: + medium = Side(style="medium") + for col in range(1, _TOTAL_COLS + 1): + if col in _SECTION_BOUNDARY_COLS: + ws.cell(row=row, column=col).border = Border(right=medium, bottom=medium) + elif col in _SECTION_START_COLS: + ws.cell(row=row, column=col).border = Border(left=medium, bottom=medium) + else: + ws.cell(row=row, column=col).border = Border(bottom=medium) def _write_headers(ws: Worksheet) -> None: @@ -137,15 +158,13 @@ def _write_data_rows(ws: Worksheet, rows: list[AuditRow]) -> None: xl_row = 3 for row in rows: if row.floor_level is not None: - cell = ws.cell(row=xl_row, column=1, value=f"Floor {row.floor_level}") - cell.font = _BOLD - cell.fill = _FLOOR_FILL - ws.merge_cells( - start_row=xl_row, - start_column=1, - end_row=xl_row, - end_column=_TOTAL_COLS, - ) + for idx, (c1, c2) in enumerate(_FLOOR_MERGE_SEGMENTS): + ws.merge_cells(start_row=xl_row, start_column=c1, end_row=xl_row, end_column=c2) + label = f"Floor {row.floor_level}" if idx == 0 else None + cell = ws.cell(row=xl_row, column=c1, value=label) + cell.fill = _FLOOR_FILL + if idx == 0: + cell.font = _BOLD else: ws.cell(row=xl_row, column=1, value=row.room_name) ws.cell(row=xl_row, column=2, value=row.room_width_m) @@ -159,7 +178,9 @@ def _write_data_rows(ws: Worksheet, rows: list[AuditRow]) -> None: ws.cell(row=xl_row, column=11, value=row.window_opening_type) ws.cell(row=xl_row, column=22, value=row.door_location) ws.cell(row=xl_row, column=23, value=row.door_width_mm) - _apply_section_borders(ws, xl_row) + _apply_section_borders(ws, xl_row) + if row.is_last_room_row: + _apply_room_bottom_border(ws, xl_row) xl_row += 1 diff --git a/backend/magic_plan/tests/test_audit_script.py b/backend/magic_plan/tests/test_audit_script.py index fea487a5..e56b6977 100644 --- a/backend/magic_plan/tests/test_audit_script.py +++ b/backend/magic_plan/tests/test_audit_script.py @@ -273,9 +273,10 @@ def test_apply_section_borders_sets_medium_right_border_on_boundary_columns() -> # Assert — medium right-border on last col of each subtable for col in (4, 11, 15, 20): assert ws.cell(row=1, column=col).border.right.style == "medium", f"col {col}" - # Assert — non-boundary columns are untouched + # Assert — non-boundary columns have no medium right-border for col in (1, 6, 12, 22): - assert ws.cell(row=1, column=col).border.right.style is None, f"col {col}" + right = ws.cell(row=1, column=col).border.right + assert right is None or right.style != "medium", f"col {col}" def test_write_data_rows_places_door_data_at_cols_22_to_25() -> None: