populate_sheet writes to new Sero template column layout 🟩

This commit is contained in:
Daniel Roth 2026-06-10 13:21:50 +00:00
parent e7954ad83a
commit 0edeeaefa6
3 changed files with 129 additions and 22 deletions

View file

@ -11,9 +11,10 @@ from domain.magicplan.models import Door, Plan, Room, Window
_DATA_START_ROW = 6
_MAX_ROWS = 50
_Y_CF_RANGE = f"Y{_DATA_START_ROW}:Y{_DATA_START_ROW + _MAX_ROWS - 1}"
_DOOR_AREA_COL = "U"
_DOOR_AREA_CF_RANGE = f"{_DOOR_AREA_COL}{_DATA_START_ROW}:{_DOOR_AREA_COL}{_DATA_START_ROW + _MAX_ROWS - 1}"
_Y_THRESHOLD = 7600
_Y_HEADER = CellRichText(
_DOOR_AREA_HEADER = CellRichText(
TextBlock(InlineFont(b=True, sz=11, rFont="Aptos Narrow"), "Area (mm2)\n"),
TextBlock(InlineFont(b=True, sz=11, color=Color(rgb="FF0000"), rFont="Aptos Narrow"), "<"),
TextBlock(InlineFont(b=True, sz=11, rFont="Aptos Narrow"), " 7600 "),
@ -23,14 +24,14 @@ _Y_HEADER = CellRichText(
def _apply_column_y_formatting(sheet: Any) -> None:
sheet.conditional_formatting.add(
_Y_CF_RANGE,
_DOOR_AREA_CF_RANGE,
CellIsRule(operator="lessThan", formula=[str(_Y_THRESHOLD)], font=Font(color=Color(rgb="FF0000"))),
)
sheet.conditional_formatting.add(
_Y_CF_RANGE,
_DOOR_AREA_CF_RANGE,
CellIsRule(operator="greaterThan", formula=[str(_Y_THRESHOLD)], font=Font(color=Color(rgb="196B24"))),
)
sheet["Y3"] = _Y_HEADER
sheet[f"{_DOOR_AREA_COL}3"] = _DOOR_AREA_HEADER
def _write_cell(sheet: Any, row: int, col: str, value: Any) -> None:
@ -61,26 +62,25 @@ def populate_sheet(sheet: Any, plan: Plan) -> None:
for i, (room_name, window) in enumerate(windows):
row = _DATA_START_ROW + i
vent = window.ventilation
_write_cell(sheet, row, "G", room_name)
_write_cell(sheet, row, "H", window.width_m)
_write_cell(sheet, row, "I", window.height_m)
# J = formula =H*I — do not write
_write_cell(sheet, row, "K", vent.opening_type if vent else 0)
_write_cell(sheet, row, "L", vent.num_openings if vent else 0)
_write_cell(sheet, row, "F", room_name)
_write_cell(sheet, row, "G", window.width_m)
_write_cell(sheet, row, "H", window.height_m)
# I = formula =G*H — do not write
_write_cell(sheet, row, "J", vent.opening_type if vent else 0)
_write_cell(sheet, row, "K", vent.num_openings if vent else 0)
pct = vent.pct_openable if vent else None
_write_cell(sheet, row, "M", (pct / 100) if pct is not None else 0)
# N = formula =J*M — do not write
# O, P = blank (visual check by auditor)
_write_cell(sheet, row, "Q", vent.trickle_vent_area_mm2 if vent else 0)
_write_cell(sheet, row, "R", vent.num_trickle_vents if vent else 0)
# S = formula =Q*R — do not write
_write_cell(sheet, row, "L", (pct / 100) if pct is not None else 0)
# M = formula =I*L — do not write
_write_cell(sheet, row, "N", vent.trickle_vent_area_mm2 if vent else 0)
_write_cell(sheet, row, "O", vent.num_trickle_vents if vent else 0)
# P = formula =N*O — do not write
for i, (room_name, door) in enumerate(doors):
row = _DATA_START_ROW + i
vent = door.ventilation
_write_cell(sheet, row, "V", room_name)
_write_cell(sheet, row, "W", door.width_mm)
_write_cell(sheet, row, "X", vent.undercut_mm if vent else 0)
# Y = formula =W*X — do not write
_write_cell(sheet, row, "R", room_name)
_write_cell(sheet, row, "S", door.width_mm)
_write_cell(sheet, row, "T", vent.undercut_mm if vent else 0)
# U = formula =S*T — do not write
_apply_column_y_formatting(sheet)

View file

@ -19,7 +19,7 @@ from infrastructure.s3.s3_client import S3Client
if TYPE_CHECKING:
from orchestration.audit_generator_unit_of_work import AuditGeneratorUnitOfWork
_TEMPLATE_PATH = Path(__file__).parent.parent / "applications" / "audit_generator" / "d1_ventilation_template.xlsx"
_TEMPLATE_PATH = Path(__file__).parent.parent / "applications" / "audit_generator" / "Master Sero Template - Data Extraction.xlsx"
_SHEET_NAME = "D1 Ventilation"

View file

@ -92,3 +92,110 @@ def test_raises_when_doors_exceed_50() -> None:
# Act / Assert
with pytest.raises(ValueError, match="50"):
populate_sheet(sheet, plan)
def test_writes_window_room_name_to_column_F() -> None:
# Arrange
plan = _make_plan(num_rooms=1, num_windows_per_room=1, num_doors_per_room=0)
sheet = _blank_sheet()
# Act
populate_sheet(sheet, plan)
# Assert
assert sheet["F6"].value == "Room 0"
def test_writes_window_dimensions_to_columns_G_and_H() -> None:
# Arrange
plan = _make_plan(num_rooms=1, num_windows_per_room=1, num_doors_per_room=0)
sheet = _blank_sheet()
# Act
populate_sheet(sheet, plan)
# Assert
assert sheet["G6"].value == 1.0
assert sheet["H6"].value == 1.2
def test_writes_window_opening_data_to_columns_J_K_L() -> None:
# Arrange
plan = _make_plan(num_rooms=1, num_windows_per_room=1, num_doors_per_room=0)
sheet = _blank_sheet()
# Act
populate_sheet(sheet, plan)
# Assert
assert sheet["J6"].value == "Hinged"
assert sheet["K6"].value == 1
assert sheet["L6"].value == pytest.approx(0.5)
def test_writes_trickle_vent_data_to_columns_N_and_O() -> None:
# Arrange
plan = _make_plan(num_rooms=1, num_windows_per_room=1, num_doors_per_room=0)
sheet = _blank_sheet()
# Act
populate_sheet(sheet, plan)
# Assert
assert sheet["N6"].value == 1000
assert sheet["O6"].value == 2
def test_writes_door_room_name_to_column_R() -> None:
# Arrange
plan = _make_plan(num_rooms=1, num_windows_per_room=0, num_doors_per_room=1)
sheet = _blank_sheet()
# Act
populate_sheet(sheet, plan)
# Assert
assert sheet["R6"].value == "Room 0"
def test_writes_door_dimensions_to_columns_S_and_T() -> None:
# Arrange
plan = _make_plan(num_rooms=1, num_windows_per_room=0, num_doors_per_room=1)
sheet = _blank_sheet()
# Act
populate_sheet(sheet, plan)
# Assert
assert sheet["S6"].value == 800.0
assert sheet["T6"].value == 10.0
def test_writes_zeros_when_window_has_no_ventilation() -> None:
# Arrange
rooms = [Room(name="Room 0", width_m=3.0, length_m=4.0, area_m2=12.0, windows=[_make_window(with_ventilation=False)], doors=[])]
plan = Plan(uid="u", name="n", address="a", postcode="p", floors=[Floor(level=0, name="G", rooms=rooms)])
sheet = _blank_sheet()
# Act
populate_sheet(sheet, plan)
# Assert
assert sheet["J6"].value == 0
assert sheet["K6"].value == 0
assert sheet["L6"].value == 0
assert sheet["N6"].value == 0
assert sheet["O6"].value == 0
def test_writes_zeros_when_door_has_no_ventilation() -> None:
# Arrange
rooms = [Room(name="Room 0", width_m=3.0, length_m=4.0, area_m2=12.0, windows=[], doors=[_make_door(with_ventilation=False)])]
plan = Plan(uid="u", name="n", address="a", postcode="p", floors=[Floor(level=0, name="G", rooms=rooms)])
sheet = _blank_sheet()
# Act
populate_sheet(sheet, plan)
# Assert
assert sheet["T6"].value == 0