diff --git a/docs/openapi/openapi.yaml b/docs/openapi/openapi.yaml index c2238eb..bc8424d 100644 --- a/docs/openapi/openapi.yaml +++ b/docs/openapi/openapi.yaml @@ -420,6 +420,9 @@ components: Year2_Building are the earliest/latest construction year of this variant's period band (0 and 9999 are the same open-ended sentinels used by /api/v1/periods). + BasicParameters.BuildingAppearance.n_Apartment is the number of + dwellings in the archetype building: 1 for SFH and TH, the block's + dwelling count for MFH and AB, 0 where TABULA gives no count. properties: BasicParameters: type: object diff --git a/internal/db/repository/repository_integration_test.go b/internal/db/repository/repository_integration_test.go index 82d1ff0..850942f 100644 --- a/internal/db/repository/repository_integration_test.go +++ b/internal/db/repository/repository_integration_test.go @@ -125,6 +125,16 @@ func seedFixtures(ctx context.Context, pool *pgxpool.Pool) error { ('AT.N.MFH.02.Gen', 'AT.02', 1919, 1944), ('AT.N.TH.01.Gen', 'AT.01', 0, 1918), ('AT.N.TH.05.Gen', 'AT.05', 2000, 9999)`, + // netherlands carries the dwelling count. REAL matches the type the + // workbook loader gives the column in every country table. + `CREATE TABLE tabula.netherlands ( + id SERIAL PRIMARY KEY, + "Code_BuildingVariant" VARCHAR, + "n_Apartment" REAL + )`, + `INSERT INTO tabula.netherlands ("Code_BuildingVariant", "n_Apartment") VALUES + ('NL.N.AB.03.Gen.ReEx.001.001', 15), + ('NL.N.SFH.01.Gen.ReEx.001.001', 1)`, } for _, stmt := range statements { if _, err := pool.Exec(ctx, stmt); err != nil { @@ -257,6 +267,25 @@ func TestTabulaRepository_GetVariant_success(t *testing.T) { } } +func TestTabulaRepository_GetVariant_nApartment(t *testing.T) { + r := repository.NewTabulaRepository(testPool, "tabula") + for _, tc := range []struct { + code string + want int + }{ + {"NL.N.AB.03.Gen.ReEx.001.001", 15}, + {"NL.N.SFH.01.Gen.ReEx.001.001", 1}, + } { + params, _, _, err := r.GetVariant(context.Background(), "netherlands", tc.code) + if err != nil { + t.Fatalf("%s: unexpected error: %v", tc.code, err) + } + if got := params.BasicParameters.BuildingAppearance.N_Apartment; got != tc.want { + t.Errorf("%s: N_Apartment = %d, want %d", tc.code, got, tc.want) + } + } +} + func TestTabulaRepository_GetVariant_notFound(t *testing.T) { r := repository.NewTabulaRepository(testPool, "tabula") _, _, _, err := r.GetVariant(context.Background(), "germany", "DE.N.SFH.99.Gen") diff --git a/internal/db/repository/struct_mapper_test.go b/internal/db/repository/struct_mapper_test.go index d79d9b5..53c5335 100644 --- a/internal/db/repository/struct_mapper_test.go +++ b/internal/db/repository/struct_mapper_test.go @@ -30,6 +30,7 @@ func TestPopulateStructFromMap_setsFieldsByJSONTag(t *testing.T) { "n_air_infiltration": float64(0.5), "Year1_Building": int32(0), "Year2_Building": int32(1918), + "n_Apartment": float32(15), } populateStructFromMap(data, dataMap) @@ -58,6 +59,9 @@ func TestPopulateStructFromMap_setsFieldsByJSONTag(t *testing.T) { if data.BasicParameters.BuildingAppearance.Year2_Building != 1918 { t.Errorf("Year2_Building = %d, want 1918", data.BasicParameters.BuildingAppearance.Year2_Building) } + if data.BasicParameters.BuildingAppearance.N_Apartment != 15 { + t.Errorf("N_Apartment = %d, want 15", data.BasicParameters.BuildingAppearance.N_Apartment) + } } func TestPopulateStructFromMap_missingAndNilKeysAreSkipped(t *testing.T) { diff --git a/internal/models/tabula.go b/internal/models/tabula.go index 9778800..82b3ac0 100644 --- a/internal/models/tabula.go +++ b/internal/models/tabula.go @@ -55,6 +55,7 @@ type BuildingThematic struct { Code_ComplexFootprint string `json:"Code_ComplexFootprint"` // Complexity of building footprint (e.g. "Simple", "Standard", "Complex") Code_ComplexRoof string `json:"Code_ComplexRoof"` // Complexity of roof shape (e.g. "Simple", "Standard", "Complex") N_Storey int `json:"n_Storey"` // Number of storeys (used for calculating conditioned volume if not provided in dataset) + N_Apartment int `json:"n_Apartment"` // Number of dwellings in the archetype building (1 for SFH and TH, 0 where TABULA gives no count) H_room float64 `json:"h_room"` // Room height in meters (used for calculating conditioned volume if not provided in dataset) Code_AtticCond string `json:"Code_AtticCond"` // Attic condition code Code_CellarCond string `json:"Code_CellarCond"` // Cellar condition code