From f105c21d352deda90bb4f60eca838f378bcd9fa7 Mon Sep 17 00:00:00 2001 From: Daniel Roth Date: Fri, 15 May 2026 14:49:59 +0000 Subject: [PATCH] =?UTF-8?q?Wire=20audit=5Fscript.py=20with=20DB=20read,=20?= =?UTF-8?q?row=20builder,=20and=20openpyxl=20spreadsheet=20output=20?= =?UTF-8?q?=F0=9F=9F=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/magic_plan/audit_script.py | 126 +++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/backend/magic_plan/audit_script.py b/backend/magic_plan/audit_script.py index 73412915..753b8553 100644 --- a/backend/magic_plan/audit_script.py +++ b/backend/magic_plan/audit_script.py @@ -1,7 +1,20 @@ from dataclasses import dataclass from typing import Optional +import openpyxl +from openpyxl.styles import Alignment, Font, PatternFill +from openpyxl.worksheet.worksheet import Worksheet + from datatypes.magicplan.domain.models import Plan +from backend.app.db.connection import db_read_session +from backend.app.db.functions.magic_plan_functions import get_plan_by_uploaded_file_id + +UPLOADED_FILE_ID: int = 123 + +_HEADER_FILL = PatternFill("solid", fgColor="D9D9D9") +_FLOOR_FILL = PatternFill("solid", fgColor="BDD7EE") +_BOLD = Font(bold=True) +_TOTAL_COLS = 31 # A–AE @dataclass @@ -58,3 +71,116 @@ def _build_rows(plan: Plan) -> list[AuditRow]: rows.append(row) return rows + + +def _write_headers(ws: Worksheet) -> None: + # Row 1: group labels (merged) + _merge_bold_fill(ws, 1, 1, 1, 4, "Room") + # E1 blank separator + _merge_bold_fill(ws, 1, 6, 1, 11, "Window Area") + _merge_bold_fill(ws, 1, 12, 1, 21, "Openings") + _merge_bold_fill(ws, 1, 22, 1, 26, "Trickle Vents") + # AA (27) blank separator + _merge_bold_fill(ws, 1, 28, 1, 31, "Door Undercuts") + + # Row 2: column labels + _header_cell(ws, 2, 1, "Room") + _header_cell(ws, 2, 2, "Width m") + _header_cell(ws, 2, 3, "Length m") + _header_cell(ws, 2, 4, "Area m²") + _header_cell(ws, 2, 6, "Label") + _header_cell(ws, 2, 7, "Location") + _header_cell(ws, 2, 8, "Width m") + _header_cell(ws, 2, 9, "Height m") + _header_cell(ws, 2, 10, "Area m²") + _header_cell(ws, 2, 11, "Opening Type") + _header_cell(ws, 2, 12, "No. of Openings") + for i, col in enumerate([13, 15, 17, 19]): + _merge_bold_fill(ws, 2, col, 2, col + 1, f"Opening {i + 1}") + _header_cell(ws, 2, 21, "Total Area m²") + _header_cell(ws, 2, 22, "Blocked") + _header_cell(ws, 2, 23, "Pictured") + _header_cell(ws, 2, 24, "Effective Area") + _header_cell(ws, 2, 25, "Count") + _header_cell(ws, 2, 26, "Total Effective Area") + _header_cell(ws, 2, 28, "Location") + _header_cell(ws, 2, 29, "Width mm") + _header_cell(ws, 2, 30, "Undercut mm") + _header_cell(ws, 2, 31, "Area mm²") + + # Row 3: Width/Height sub-headers under Opening 1–4 + for col in [13, 15, 17, 19]: + _header_cell(ws, 3, col, "Width m") + _header_cell(ws, 3, col + 1, "Height m") + + # Apply fill to all header cells in rows 1–3 + for row in range(1, 4): + for col in range(1, _TOTAL_COLS + 1): + cell = ws.cell(row=row, column=col) + cell.fill = _HEADER_FILL + + +def _write_data_rows(ws: Worksheet, rows: list[AuditRow]) -> None: + xl_row = 4 + 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, + ) + else: + ws.cell(row=xl_row, column=1, value=row.room_name) + ws.cell(row=xl_row, column=2, value=row.room_width_m) + ws.cell(row=xl_row, column=3, value=row.room_length_m) + ws.cell(row=xl_row, column=4, value=row.room_area_m2) + ws.cell(row=xl_row, column=6, value=row.window_label) + ws.cell(row=xl_row, column=7, value=row.window_location) + ws.cell(row=xl_row, column=8, value=row.window_width_m) + ws.cell(row=xl_row, column=9, value=row.window_height_m) + ws.cell(row=xl_row, column=10, value=row.window_area_m2) + ws.cell(row=xl_row, column=11, value=row.window_opening_type) + ws.cell(row=xl_row, column=28, value=row.door_location) + ws.cell(row=xl_row, column=29, value=row.door_width_mm) + xl_row += 1 + + +def _merge_bold_fill( + ws: Worksheet, r1: int, c1: int, r2: int, c2: int, value: str +) -> None: + ws.merge_cells(start_row=r1, start_column=c1, end_row=r2, end_column=c2) + cell = ws.cell(row=r1, column=c1, value=value) + cell.font = _BOLD + cell.alignment = Alignment(horizontal="center") + + +def _header_cell(ws: Worksheet, row: int, col: int, value: str) -> None: + cell = ws.cell(row=row, column=col, value=value) + cell.font = _BOLD + + +def main() -> None: + with db_read_session() as session: + plan: Optional[Plan] = get_plan_by_uploaded_file_id(session, UPLOADED_FILE_ID) + + if plan is None: + raise ValueError(f"No plan found for uploaded_file_id={UPLOADED_FILE_ID}") + + rows = _build_rows(plan) + + wb = openpyxl.Workbook() + ws: Worksheet = wb.active # type: ignore[assignment] + ws.title = "Audit" + + _write_headers(ws) + _write_data_rows(ws, rows) + + output_path = f"/tmp/magic_plan_audit_{UPLOADED_FILE_ID}.xlsx" + wb.save(output_path) + print(output_path) + + +if __name__ == "__main__": + main()