From 9750770c7cf7225cc8b3736e0c475cd96f12d220 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Tue, 23 Jun 2026 11:24:25 +0000 Subject: [PATCH] fix(building-passport): fill storeys and habitable rooms from EPC graph On the overview page, Storeys read epc_property.number_of_storeys (null for some properties) and Habitable rooms read the property-row number_of_rooms (null for new-approach properties). - Habitable rooms: backfill propertyMeta.numberOfRooms from epc_property.habitable_rooms_count (always populated). - Storeys: fall back to the main building part's floor-dimension count when epc_property.number_of_storeys is null. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/lib/services/epcSources.ts | 43 ++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/src/lib/services/epcSources.ts b/src/lib/services/epcSources.ts index 5fdea8b0..e4987ab7 100644 --- a/src/lib/services/epcSources.ts +++ b/src/lib/services/epcSources.ts @@ -253,15 +253,18 @@ async function resolveNewConditionReport( }), ]); - // Floor height: room height of the main building part (fall back to any part). + // Floor height + storey count from the main building part's floor dimensions + // (fall back to any part). Each floor dimension is one storey of that part. const mainPart = buildingParts.find((b) => b.identifier === "main") ?? buildingParts[0]; - const mainFloor = mainPart - ? await db.query.epcFloorDimension.findFirst({ + const mainFloors = mainPart + ? await db.query.epcFloorDimension.findMany({ columns: { roomHeightM: true }, where: eq(epcFloorDimension.epcBuildingPartId, mainPart.id), }) - : null; + : []; + const floorHeight = mainFloors[0]?.roomHeightM ?? null; + const storeyCountFromFloors = mainFloors.length || null; const byType = (t: string) => elements.find((el) => el.elementType === t); const desc = (t: string) => byType(t)?.description ?? null; @@ -305,10 +308,11 @@ async function resolveNewConditionReport( heatLossCorridor: flat?.heatLossCorridor != null ? flat.heatLossCorridor > 0 : null, unheatedCorridorLength: flat?.unheatedCorridorLengthM ?? null, - numberStoreys: chosen.numberOfStoreys ?? null, + // Storeys: epc_property value if present, else the main part's floor count. + numberStoreys: chosen.numberOfStoreys ?? storeyCountFromFloors, // Room height of the main building part (the new graph stores height per // building part rather than one dwelling value). - floorHeight: mainFloor?.roomHeightM ?? null, + floorHeight, numberHeatedRooms: chosen.heatedRoomsCount ?? null, numberOpenFireplaces: chosen.openChimneysCount ?? null, numberExtensions: chosen.extensionsCount ?? null, @@ -528,17 +532,25 @@ async function resolvePropertyDescriptors( propertyType: string | null; builtForm: string | null; yearBuilt: string | null; + numberOfRooms: number | null; }> { + const cols = { + id: true, + propertyType: true, + builtForm: true, + dwellingType: true, + habitableRoomsCount: true, + } as const; const ep = (await db.query.epcProperty.findFirst({ - columns: { id: true, propertyType: true, builtForm: true, dwellingType: true }, + columns: cols, where: and( eq(epcProperty.propertyId, propertyId), eq(epcProperty.source, "lodged"), ), })) ?? (await db.query.epcProperty.findFirst({ - columns: { id: true, propertyType: true, builtForm: true, dwellingType: true }, + columns: cols, where: and( eq(epcProperty.propertyId, propertyId), eq(epcProperty.source, "predicted"), @@ -546,7 +558,12 @@ async function resolvePropertyDescriptors( })); if (!ep) { - return { propertyType: null, builtForm: null, yearBuilt: null }; + return { + propertyType: null, + builtForm: null, + yearBuilt: null, + numberOfRooms: null, + }; } const mainPart = await db.query.epcBuildingPart.findFirst({ @@ -566,7 +583,12 @@ async function resolvePropertyDescriptors( const ageBand = mainPart?.constructionAgeBand ?? null; const yearBuilt = ageBand ? AGE_BAND_YEARS[ageBand] ?? ageBand : null; - return { propertyType, builtForm, yearBuilt }; + return { + propertyType, + builtForm, + yearBuilt, + numberOfRooms: ep.habitableRoomsCount ?? null, + }; } /** @@ -619,6 +641,7 @@ export async function resolvePropertyMeta(propertyId: string) { propertyType: descriptors?.propertyType ?? propertyMeta.propertyType, builtForm: descriptors?.builtForm ?? propertyMeta.builtForm, yearBuilt: descriptors?.yearBuilt ?? propertyMeta.yearBuilt, + numberOfRooms: descriptors?.numberOfRooms ?? propertyMeta.numberOfRooms, currentSapPoints: headline.currentSapPoints ?? propertyMeta.currentSapPoints, currentEpcRating: