dedicated domain layer

This commit is contained in:
Daniel Roth 2026-05-11 15:37:51 +00:00
parent 3638c09a7b
commit 91df800233
81 changed files with 2593 additions and 112 deletions

View file

@ -1,5 +1,5 @@
from dataclasses import dataclass
import datatypes.epc as epc_datatypes
import domain.epc as epc_datatypes
from typing import Optional, Union

View file

@ -2,7 +2,7 @@ import warnings
import pandas as pd
from typing import Iterator
from backend.addresses.Address import Address
from datatypes.epc.property_type_built_form import PropertyType
from domain.epc.property_type_built_form import PropertyType
class Addresses:

View file

@ -4,7 +4,7 @@ from sqlalchemy import delete, select
from sqlalchemy.dialects.postgresql import insert as pg_insert
from sqlmodel import Session, col
from datatypes.magicplan.domain.models import Floor, Plan
from domain.magicplan.models import Floor, Plan
from backend.app.db.models.magic_plan import (
MagicPlanDoorModel,
MagicPlanFloorModel,

View file

@ -7,8 +7,8 @@ from sqlalchemy.orm import Session
from sqlmodel import SQLModel
from datatypes.magicplan.api.response import MagicPlanPlan
from datatypes.magicplan.domain.mapper import map_plan
from datatypes.magicplan.domain.models import Plan
from domain.magicplan.mapper import map_plan
from domain.magicplan.models import Plan
from backend.app.db.functions.magic_plan_functions import save_plan
from backend.app.db.models.magic_plan import (

View file

@ -3,7 +3,7 @@ from __future__ import annotations
from typing import Optional
from sqlmodel import SQLModel, Field
from datatypes.epc.domain.epc_property_data import (
from domain.epc.epc_property_data import (
EpcPropertyData,
EnergyElement,
MainHeatingDetail,

View file

@ -16,7 +16,7 @@ from sqlalchemy import (
from backend.app.db.base import Base
from backend.app.db.models.users import UserModel # noqa
from backend.app.db.models.materials import MaterialType
from datatypes.epc.domain.epc import Epc
from domain.epc.epc import Epc
class PortfolioStatus(enum.Enum):

View file

@ -18,7 +18,7 @@ from backend.app.db.base import Base
from backend.app.db.models.portfolio import Portfolio, PortfolioGoal, PropertyModel
from backend.app.db.models.materials import Material
from datatypes.enums import QuantityUnits
from datatypes.epc.domain.epc import Epc
from domain.epc.epc import Epc
def portfolio_goal_values(enum_cls: Type[PortfolioGoal]) -> List[str]:

View file

@ -3,7 +3,7 @@ from datetime import datetime
from typing import Optional
from backend.app.db.models.recommendations import PlanTypeEnum
from datatypes.epc.domain.epc import Epc
from domain.epc.epc import Epc
@dataclass(frozen=True)

View file

@ -0,0 +1,114 @@
# PRD: Extract Domain Layer to Root-Level `domain/` Package
## Problem Statement
Domain objects (models, enums, mappers) shared across microservices are nested under `datatypes/epc/domain/` and `datatypes/magicplan/domain/`. This creates two problems:
1. **Misleading location**: "Datatypes" implies low-level type definitions, but these folders contain rich domain models and business logic (mappers, historic EPC matching, property data aggregation). New developers cannot discover the domain layer by inspection.
2. **False coupling**: Domain objects belonging to the whole system are buried inside a folder that also contains source-data representations (versioned RdSAP schemas, Elmhurst/Pashub survey shapes, MagicPlan API responses). The domain has no clear home — it is mixed with external data adapters.
## Solution
Extract all domain objects (domain models, domain vocabulary enums, mappers) into a dedicated root-level `domain/` package. The `datatypes/` package is repurposed to hold only external/source data shapes: RdSAP schemas, survey provider types, and third-party API response types.
The result is a clear, discoverable split:
- `domain/` — canonical domain models, vocabulary enums, and mappers owned by this system
- `datatypes/` — shapes that mirror external systems (schemas, surveys, API responses)
## User Stories
1. As a backend engineer, I want domain models to live in a top-level `domain/` package, so that I can find the canonical representation of an EPC or MagicPlan floor plan without navigating into `datatypes/`.
2. As a backend engineer, I want to import EPC domain models with `from domain.epc import ...`, so that the import path reflects the conceptual ownership of the type.
3. As a backend engineer, I want EPC vocabulary enums (wall descriptions, roof descriptions, fuel types, etc.) to live in `domain/epc/`, so that all domain vocabulary is co-located with domain models.
4. As a backend engineer adding a new microservice, I want a clear pattern for importing shared domain objects, so that I do not accidentally reach into `datatypes/` for domain types.
5. As a backend engineer, I want the `datatypes/` package to contain only source-data representations, so that I understand it as the anti-corruption layer and not as the domain.
6. As a backend engineer, I want mappers (which transform external shapes into domain models) to live alongside domain models in `domain/`, so that the translation between external and domain is easy to find.
7. As a backend engineer running tests, I want the pytest test paths to reflect the new `domain/` location, so that `pytest` discovers and runs all domain tests correctly.
8. As a backend engineer, I want no backwards-compatibility shims or re-export stubs left behind in `datatypes/`, so that there is one canonical import path for each domain type.
9. As a code reviewer, I want imports from `domain.*` to clearly signal "this is a domain concept", so that the distinction from `datatypes.*` (external data) is immediately readable.
10. As a backend engineer, I want internal references within moved files (e.g. mapper.py importing models.py) to be updated to their new paths, so that no moved module has a stale import.
## Implementation Decisions
### Package structure
A root-level `domain/` package is created alongside `datatypes/`. It contains two sub-packages mirroring the existing subdomain split:
- `domain/epc/` — all EPC domain objects
- `domain/magicplan/` — all MagicPlan domain objects
### What moves into `domain/epc/`
All of the following move from their current `datatypes/epc/` locations:
- EPC vocabulary enums: `construction_age_band`, `efficiency`, `floor`, `fuel`, `heating_controls`, `hotwater`, `main_heating`, `property_type_built_form`, `roof`, `walls`, `windows`
- EPC domain models and logic: `epc`, `epc_property_data`, `field_mappings`, `historic_epc`, `historic_epc_matching`, `mapper`
- Domain tests (currently under `datatypes/epc/domain/tests/`)
`domain/epc/__init__.py` re-exports all vocabulary enums (matching the current `datatypes/epc/__init__.py` interface) so call-sites using the package-level import continue to work after the path change.
### What moves into `domain/magicplan/`
- Domain models: `models` (Window, Door, Room, Floor, Plan dataclasses)
- Domain mapper: `mapper` (transforms MagicPlan API response into domain models)
- Domain tests (currently under `datatypes/magicplan/domain/tests/`)
### What stays in `datatypes/`
- `datatypes/epc/schema/` — versioned RdSAP XML schemas (17.0 through 21.0.1)
- `datatypes/epc/surveys/` — Elmhurst and Pashub site notes shapes
- `datatypes/epc/loaders/` — historic EPC loading logic
- `datatypes/magicplan/api/` — MagicPlan API response types
- `datatypes/datatypes.py` and `datatypes/enums.py` — pre-existing shared primitives (out of scope for this change)
### Mapper placement
Mappers live in `domain/` alongside the models they produce. This reflects the view that a mapper is the domain's intake port — the boundary at which external data becomes domain data — and belongs with the domain, not with the external shapes.
### Import path changes
| Old import | New import |
|---|---|
| `from datatypes.epc.domain.X import Y` | `from domain.epc.X import Y` |
| `from datatypes.epc.walls import Y` | `from domain.epc.walls import Y` |
| `from datatypes.epc import EpcWallDescriptions` | `from domain.epc import EpcWallDescriptions` |
| `from datatypes.magicplan.domain.models import Y` | `from domain.magicplan.models import Y` |
| `from datatypes.magicplan.domain.mapper import Y` | `from domain.magicplan.mapper import Y` |
Imports from `datatypes.epc.schema.*`, `datatypes.epc.surveys.*`, and `datatypes.magicplan.api.*` are unchanged.
### pytest.ini update
`testpaths` entries for `datatypes/epc/domain/tests` and `datatypes/magicplan/domain/tests` are replaced with `domain/epc/tests` and `domain/magicplan/tests`.
### No backwards-compatibility shims
`datatypes/epc/` enum files and `datatypes/epc/domain/` files are deleted after the move. No re-export stubs are left behind. All consumers are updated in the same change.
## Testing Decisions
A good test for this change verifies that the domain objects behave identically under their new import paths — not that the files were moved.
**Modules to test:**
- `domain/epc/` — existing tests from `datatypes/epc/domain/tests/` cover mapper (`test_from_rdsap_schema`, `test_from_site_notes`) and historic EPC matching (`test_historic_epc_matching`). These tests are moved verbatim; passing them confirms the domain logic is intact.
- `domain/magicplan/` — existing tests from `datatypes/magicplan/domain/tests/test_mapper.py` are moved verbatim.
**Prior art:** `datatypes/epc/domain/tests/` and `datatypes/magicplan/domain/tests/` are the direct prior art — same test patterns, same fixtures, relocated.
**Verification grep:** After the change, `grep -r "from datatypes.epc.domain" . --include="*.py"` and `grep -r "from datatypes.magicplan.domain" . --include="*.py"` should return zero results (excluding `__pycache__`).
## Out of Scope
- Moving `datatypes/datatypes.py` (`OpenUprnCoordinateData`) or `datatypes/enums.py` (`QuantityUnits`) — these are pre-existing and will be addressed in a future refactor.
- Renaming `datatypes/` to `external/` or any other name — out of scope for this change.
- Moving `datatypes/epc/schema/`, `datatypes/epc/surveys/`, `datatypes/epc/loaders/`, or `datatypes/magicplan/api/` — these are source-data shapes and stay in `datatypes/`.
- Adding new domain objects or changing domain logic — this is a structural move only.
- Introducing Python packaging (`setup.py`, `pyproject.toml`) changes beyond `pytest.ini`.
## Further Notes
- `pytest.ini` sets `pythonpath = .`, so `domain/` at the repo root will be importable immediately without additional configuration.
- `datatypes/epc/__init__.py` currently aggregates enum exports via `__all__`. The equivalent `domain/epc/__init__.py` should preserve this interface so call-sites using `from datatypes.epc import EpcWallDescriptions`-style imports need only a path change, not a restructuring.
- ~40 files across `backend/`, `etl/`, and `scripts/` import from `datatypes.epc.*` domain paths. ~10 files import from `datatypes.magicplan.domain.*`. A single `sed` pass or IDE rename refactor covers the bulk; the verification grep confirms completeness.

View file

@ -7,7 +7,7 @@ from backend.app.domain.classes.scenario import Scenario
from backend.app.domain.records.plan_record import PlanRecord
from backend.app.domain.records.scenario_record import ScenarioRecord
from backend.app.db.models.portfolio import PortfolioGoal
from datatypes.epc.domain.epc import Epc
from domain.epc.epc import Epc
@pytest.fixture

View file

@ -8,7 +8,7 @@ from backend.app.domain.records.plan_record import PlanRecord
from backend.app.domain.records.scenario_record import ScenarioRecord
from backend.app.db.models.portfolio import PortfolioGoal
from backend.categorisation.processor import choose_cheapest_relevant_plan
from datatypes.epc.domain.epc import Epc
from domain.epc.epc import Epc
@pytest.fixture

View file

@ -12,7 +12,7 @@ from backend.app.db.models.epc_property import (
EpcPropertyModel,
EpcWindowModel,
)
from datatypes.epc.domain.epc_property_data import EpcPropertyData
from domain.epc.epc_property_data import EpcPropertyData
def save_epc_property_data(

View file

@ -21,8 +21,8 @@ from backend.app.db.models.epc_property import (
from backend.documents_parser.elmhurst_extractor import ElmhurstSiteNotesExtractor
from backend.documents_parser.extractor import PasHubRdSapSiteNotesExtractor
from backend.documents_parser.pdf import pdf_to_pages, pdf_to_text_list
from datatypes.epc.domain.epc_property_data import EnergyElement, EpcPropertyData
from datatypes.epc.domain.mapper import EpcPropertyDataMapper
from domain.epc.epc_property_data import EnergyElement, EpcPropertyData
from domain.epc.mapper import EpcPropertyDataMapper
def _parse_pdf(pdf_path: str) -> EpcPropertyData:

View file

@ -1,7 +1,7 @@
from typing import List
from datatypes.epc.domain.epc_property_data import EpcPropertyData
from datatypes.epc.domain.mapper import EpcPropertyDataMapper
from domain.epc.epc_property_data import EpcPropertyData
from domain.epc.mapper import EpcPropertyDataMapper
from backend.documents_parser.elmhurst_extractor import ElmhurstSiteNotesExtractor
from backend.documents_parser.extractor import PasHubRdSapSiteNotesExtractor

View file

@ -5,8 +5,8 @@ from datetime import date
import pytest
from backend.documents_parser.elmhurst_extractor import ElmhurstSiteNotesExtractor
from datatypes.epc.domain.epc_property_data import EpcPropertyData
from datatypes.epc.domain.mapper import EpcPropertyDataMapper
from domain.epc.epc_property_data import EpcPropertyData
from domain.epc.mapper import EpcPropertyDataMapper
FIXTURE_PATH = os.path.join(
os.path.dirname(__file__), "fixtures", "elmhurst_site_notes_1_text.json"

View file

@ -5,7 +5,7 @@ import pytest
from backend.documents_parser.extractor import PasHubRdSapSiteNotesExtractor
from backend.documents_parser.pdf import pdf_to_text_list
from datatypes.epc.domain.epc_property_data import (
from domain.epc.epc_property_data import (
EpcPropertyData,
InstantaneousWwhrs,
MainHeatingDetail,
@ -18,7 +18,7 @@ from datatypes.epc.domain.epc_property_data import (
ShowerOutlet,
ShowerOutlets,
)
from datatypes.epc.domain.mapper import EpcPropertyDataMapper
from domain.epc.mapper import EpcPropertyDataMapper
PDF_PATH = os.path.join(os.path.dirname(__file__), "fixtures", "PasHubSiteNotes_1.pdf")
PDF_PATH_2 = os.path.join(

View file

@ -3,7 +3,7 @@ from typing import Any, List, Optional, TypedDict
from backend.ecmk_fetcher.reports import build_property_id
from datatypes.epc.domain.field_mappings import PROPERTY_TYPE_LOOKUP
from domain.epc.field_mappings import PROPERTY_TYPE_LOOKUP
# This file should ultimately live somewhere different, probably

View file

@ -20,7 +20,7 @@ from backend.app.db.models.recommendations import (
RecommendationMaterials,
)
from backend.app.db.models.materials import Material
from datatypes.epc.domain.epc import Epc
from domain.epc.epc import Epc
from utils.logger import setup_logger
FIXTURE_PATH = Path("backend/export/tests/fixtures")

View file

@ -4,7 +4,7 @@ from backend.app.config import get_settings
from backend.magic_plan.magic_plan_client import MagicPlanClient
from backend.magic_plan.magic_plan_service import MagicPlanService
from backend.magic_plan.magic_plan_trigger_request import MagicPlanTriggerRequest
from datatypes.magicplan.domain.models import Plan
from domain.magicplan.models import Plan
from backend.utils.subtasks import task_handler
from utils.logger import setup_logger

View file

@ -5,8 +5,8 @@ from datatypes.magicplan.api.response import (
PlanSummary,
PlansListResponse,
)
from datatypes.magicplan.domain.mapper import map_plan
from datatypes.magicplan.domain.models import Plan
from domain.magicplan.mapper import map_plan
from domain.magicplan.models import Plan
from backend.app.db.connection import db_session
from backend.app.db.functions.magic_plan_functions import save_plan

View file

@ -5,8 +5,8 @@ from unittest.mock import MagicMock, patch
import pytest
from datatypes.magicplan.api.response import MagicPlanPlan, PlanSummary
from datatypes.magicplan.domain.mapper import map_plan
from datatypes.magicplan.domain.models import Plan
from domain.magicplan.mapper import map_plan
from domain.magicplan.models import Plan
from backend.magic_plan.magic_plan_client import MagicPlanClient
from backend.magic_plan.magic_plan_service import MagicPlanService

View file

@ -1,4 +1,4 @@
from datatypes.epc.construction_age_band import EpcConstructionAgeBand
from domain.epc.construction_age_band import EpcConstructionAgeBand
parity_map = {
"Before 1900": EpcConstructionAgeBand.before_1900,

View file

@ -1,5 +1,5 @@
from datatypes.epc.construction_age_band import EpcConstructionAgeBand
from datatypes.epc.floor import EpcFloorDescriptions
from domain.epc.construction_age_band import EpcConstructionAgeBand
from domain.epc.floor import EpcFloorDescriptions
def unknown_floor_as_built(age_band: EpcConstructionAgeBand) -> EpcFloorDescriptions:

View file

@ -1,5 +1,5 @@
from datatypes.epc.roof import EpcRoofDescriptions
from datatypes.epc.construction_age_band import EpcConstructionAgeBand
from domain.epc.roof import EpcRoofDescriptions
from domain.epc.construction_age_band import EpcConstructionAgeBand
def map_flat_roof(age_band: EpcConstructionAgeBand) -> EpcRoofDescriptions:

View file

@ -1,5 +1,5 @@
from datatypes.epc.construction_age_band import EpcConstructionAgeBand
from datatypes.epc.walls import EpcWallDescriptions
from domain.epc.construction_age_band import EpcConstructionAgeBand
from domain.epc.walls import EpcWallDescriptions
def map_cavity_wall_insulation(age_band: EpcConstructionAgeBand):

View file

@ -1,4 +1,4 @@
from datatypes.epc.property_type_built_form import BuiltForm
from domain.epc.property_type_built_form import BuiltForm
parity_map = {
"MidTerrace": BuiltForm.mid_terrace,

View file

@ -1,5 +1,5 @@
from numpy import nan
from datatypes.epc.floor import EpcFloorDescriptions
from domain.epc.floor import EpcFloorDescriptions
floor_map = {
# Solid floor

View file

@ -1,5 +1,5 @@
from datatypes.epc.efficiency import EpcEfficiency
from datatypes.epc.windows import EpcWindowDescriptions
from domain.epc.efficiency import EpcEfficiency
from domain.epc.windows import EpcWindowDescriptions
glazing_map = {
# (description, energy efficiency, multi_glaze_proportion, glazed_type, glazed_area

View file

@ -1,8 +1,8 @@
from datatypes.epc.main_heating import EpcHeatingSystems
from datatypes.epc.efficiency import EpcEfficiency
from datatypes.epc.fuel import EpcFuel
from datatypes.epc.heating_controls import EpcHeatingControls
from datatypes.epc.hotwater import EpcHotWaterSystems
from domain.epc.main_heating import EpcHeatingSystems
from domain.epc.efficiency import EpcEfficiency
from domain.epc.fuel import EpcFuel
from domain.epc.heating_controls import EpcHeatingControls
from domain.epc.hotwater import EpcHotWaterSystems
heating_map = {
# 0

View file

@ -1,4 +1,4 @@
from datatypes.epc.property_type_built_form import PropertyType
from domain.epc.property_type_built_form import PropertyType
parity_map = {
"Flat": PropertyType.flat,

View file

@ -2,9 +2,9 @@ import pandas as pd
from numpy import nan
from typing import Union, Callable
from collections.abc import Mapping
from datatypes.epc.roof import EpcRoofDescriptions
from datatypes.epc.efficiency import EpcEfficiency
from datatypes.epc.construction_age_band import EpcConstructionAgeBand
from domain.epc.roof import EpcRoofDescriptions
from domain.epc.efficiency import EpcEfficiency
from domain.epc.construction_age_band import EpcConstructionAgeBand
roof_map = {
# Dwelling above

View file

@ -1,8 +1,8 @@
from typing import Callable, Union
from collections.abc import Mapping
from datatypes.epc.walls import EpcWallDescriptions
from datatypes.epc.construction_age_band import EpcConstructionAgeBand
from datatypes.epc.efficiency import EpcEfficiency
from domain.epc.walls import EpcWallDescriptions
from domain.epc.construction_age_band import EpcConstructionAgeBand
from domain.epc.efficiency import EpcEfficiency
# Unique combinations
wall_map = {

View file

@ -16,11 +16,11 @@ from backend.onboarders.mappings.parity.as_built_roof_classifiers import as_buil
from backend.onboarders.mappings.parity.as_built_floor_classifiers import (
as_built_floor_classifiers, unknown_as_built_floor_classifiers
)
from datatypes.epc.roof import EpcRoofDescriptions
from datatypes.epc.floor import EpcFloorDescriptions
from datatypes.epc.construction_age_band import EpcConstructionAgeBand
from datatypes.epc.walls import EpcWallDescriptions
from datatypes.epc.efficiency import EpcEfficiency
from domain.epc.roof import EpcRoofDescriptions
from domain.epc.floor import EpcFloorDescriptions
from domain.epc.construction_age_band import EpcConstructionAgeBand
from domain.epc.walls import EpcWallDescriptions
from domain.epc.efficiency import EpcEfficiency
tqdm.pandas()

View file

@ -1,7 +1,7 @@
import pytest
from datatypes.epc.construction_age_band import EpcConstructionAgeBand
from datatypes.epc.floor import EpcFloorDescriptions
from domain.epc.construction_age_band import EpcConstructionAgeBand
from domain.epc.floor import EpcFloorDescriptions
from backend.onboarders.mappings.parity.as_built_floor_classifiers import (
unknown_floor_as_built,

View file

@ -1,8 +1,8 @@
import pytest
from datatypes.epc.construction_age_band import EpcConstructionAgeBand
from datatypes.epc.roof import EpcRoofDescriptions
from datatypes.epc.efficiency import EpcEfficiency
from domain.epc.construction_age_band import EpcConstructionAgeBand
from domain.epc.roof import EpcRoofDescriptions
from domain.epc.efficiency import EpcEfficiency
from backend.onboarders.mappings.parity.as_built_roof_classifiers import (
map_flat_roof,

View file

@ -1,8 +1,8 @@
import pytest
from datatypes.epc.construction_age_band import EpcConstructionAgeBand
from datatypes.epc.walls import EpcWallDescriptions
from datatypes.epc.efficiency import EpcEfficiency
from domain.epc.construction_age_band import EpcConstructionAgeBand
from domain.epc.walls import EpcWallDescriptions
from domain.epc.efficiency import EpcEfficiency
from backend.onboarders.mappings.parity.walls import resolve_wall_efficiency
from backend.onboarders.mappings.parity.as_built_wall_classifiers import (

View file

@ -16,7 +16,7 @@ from backend.pashub_fetcher.pashub_to_ara_trigger_request import (
PashubToAraTriggerRequest,
)
from backend.pashub_fetcher.sharepoint_subfolders import SharepointSubfolders
from datatypes.epc.domain.epc_property_data import EpcPropertyData
from domain.epc.epc_property_data import EpcPropertyData
from utils.logger import setup_logger
from utils.s3 import upload_file_to_s3
from utils.sharepoint.domna_sharepoint_client import DomnaSharepointClient

View file

@ -1,26 +1 @@
from .construction_age_band import EpcConstructionAgeBand
from .efficiency import EpcEfficiency
from .floor import EpcFloorDescriptions
from .fuel import EpcFuel
from .heating_controls import EpcHeatingControls
from .hotwater import EpcHotWaterSystems
from .main_heating import EpcHeatingSystems
from .property_type_built_form import PropertyType, BuiltForm
from .roof import EpcRoofDescriptions
from .walls import EpcWallDescriptions
from .windows import EpcWindowDescriptions
__all__ = [
"EpcConstructionAgeBand",
"EpcEfficiency",
"EpcFloorDescriptions",
"EpcFuel",
"EpcHeatingControls",
"EpcHotWaterSystems",
"EpcHeatingSystems",
"PropertyType",
"BuiltForm",
"EpcRoofDescriptions",
"EpcWallDescriptions",
"EpcWindowDescriptions",
]

View file

@ -1,6 +1,6 @@
import csv
from datatypes.epc.domain.historic_epc import HistoricEpc
from domain.epc.historic_epc import HistoricEpc
def _normalise(value: str | None) -> str:

View file

@ -3,7 +3,7 @@ import os
import pytest
from datatypes.epc.loaders.historic_epc import read_historic_epc_csv
from datatypes.epc.domain.historic_epc import HistoricEpc
from domain.epc.historic_epc import HistoricEpc
FIXTURES = os.path.join(os.path.dirname(__file__), "fixtures")

26
domain/epc/__init__.py Normal file
View file

@ -0,0 +1,26 @@
from .construction_age_band import EpcConstructionAgeBand
from .efficiency import EpcEfficiency
from .floor import EpcFloorDescriptions
from .fuel import EpcFuel
from .heating_controls import EpcHeatingControls
from .hotwater import EpcHotWaterSystems
from .main_heating import EpcHeatingSystems
from .property_type_built_form import PropertyType, BuiltForm
from .roof import EpcRoofDescriptions
from .walls import EpcWallDescriptions
from .windows import EpcWindowDescriptions
__all__ = [
"EpcConstructionAgeBand",
"EpcEfficiency",
"EpcFloorDescriptions",
"EpcFuel",
"EpcHeatingControls",
"EpcHotWaterSystems",
"EpcHeatingSystems",
"PropertyType",
"BuiltForm",
"EpcRoofDescriptions",
"EpcWallDescriptions",
"EpcWindowDescriptions",
]

View file

@ -2,7 +2,7 @@ from dataclasses import dataclass
from datetime import date
from typing import List, Optional, Union
from datatypes.epc.domain.epc import Epc
from domain.epc.epc import Epc
@dataclass

View file

@ -6,7 +6,7 @@ from botocore.exceptions import ClientError
from backend.address2UPRN.scoring import get_uprn_candidates
from backend.utils.addressMatch import AddressMatch
from datatypes.epc.domain.historic_epc import HistoricEpc
from domain.epc.historic_epc import HistoricEpc
from utils.pandas_utils import pandas_cell_to_str
from utils.s3 import parse_s3_uri, read_csv_gz_from_s3

View file

@ -1,7 +1,7 @@
from datetime import date
from typing import List, Optional, Sequence, Union
from datatypes.epc.domain.epc_property_data import (
from domain.epc.epc_property_data import (
EnergyElement,
EpcPropertyData,
InstantaneousWwhrs,

218
domain/epc/tests/fixtures/17_0.json vendored Normal file
View file

@ -0,0 +1,218 @@
{
"uprn": 12457,
"roofs": [
{
"description": {"value": "(another dwelling above)", "language": "1"},
"energy_efficiency_rating": 0,
"environmental_efficiency_rating": 0
}
],
"walls": [
{
"description": {"value": "System built, with internal insulation", "language": "1"},
"energy_efficiency_rating": 4,
"environmental_efficiency_rating": 4
}
],
"floors": [
{
"description": {"value": "(another dwelling below)", "language": "1"},
"energy_efficiency_rating": 0,
"environmental_efficiency_rating": 0
}
],
"status": "entered",
"tenure": 2,
"window": {
"description": {"value": "Fully double glazed", "language": "1"},
"energy_efficiency_rating": 3,
"environmental_efficiency_rating": 3
},
"lighting": {
"description": {"value": "Low energy lighting in 57% of fixed outlets", "language": "1"},
"energy_efficiency_rating": 4,
"environmental_efficiency_rating": 4
},
"postcode": "PT5 4RZ",
"hot_water": {
"description": {"value": "Electric immersion, off-peak", "language": "1"},
"energy_efficiency_rating": 3,
"environmental_efficiency_rating": 2
},
"post_town": "POSTTOWN",
"built_form": 2,
"door_count": 2,
"glazed_area": 1,
"glazing_gap": "16+",
"region_code": 3,
"report_type": 2,
"sap_heating": {
"cylinder_size": 2,
"water_heating_code": 903,
"water_heating_fuel": 29,
"instantaneous_wwhrs": {
"rooms_with_bath_and_or_shower": 1,
"rooms_with_mixer_shower_no_bath": 0,
"rooms_with_bath_and_mixer_shower": 0
},
"main_heating_details": [
{
"has_fghrs": "N",
"main_fuel_type": 29,
"heat_emitter_type": 0,
"emitter_temperature": "NA",
"main_heating_number": 1,
"main_heating_control": 2401,
"main_heating_category": 7,
"main_heating_fraction": 1,
"sap_main_heating_code": 402,
"main_heating_data_source": 2
}
],
"immersion_heating_type": 1,
"cylinder_insulation_type": 0,
"has_fixed_air_conditioning": "false"
},
"sap_version": 9.92,
"schema_type": "RdSAP-Schema-17.0",
"uprn_source": "Energy Assessor",
"country_code": "EAW",
"main_heating": [
{
"description": {"value": "Electric storage heaters", "language": "1"},
"energy_efficiency_rating": 3,
"environmental_efficiency_rating": 1
}
],
"dwelling_type": {"value": "Mid-floor flat", "language": "1"},
"language_code": 1,
"property_type": 2,
"address_line_1": "42, Moria Mines Lane",
"assessment_type": "RdSAP",
"completion_date": "2016-01-12",
"inspection_date": "2016-01-12",
"extensions_count": 0,
"measurement_type": 1,
"sap_flat_details": {
"level": 2,
"top_storey": "N",
"flat_location": 1,
"heat_loss_corridor": 0
},
"total_floor_area": 55,
"transaction_type": 8,
"conservatory_type": 1,
"heated_room_count": 1,
"pvc_window_frames": "true",
"registration_date": "2016-01-12",
"sap_energy_source": {
"mains_gas": "N",
"meter_type": 1,
"photovoltaic_supply": {
"none_or_no_details": {"pv_connection": 0, "percent_roof_area": 0}
},
"wind_turbines_count": 0,
"wind_turbines_terrain_type": 2
},
"secondary_heating": {
"description": {"value": "Portable electric heaters (assumed)", "language": "1"},
"energy_efficiency_rating": 0,
"environmental_efficiency_rating": 0
},
"lzc_energy_sources": [11],
"sap_building_parts": [
{
"identifier": "Main Dwelling",
"wall_dry_lined": "N",
"wall_thickness": 240,
"floor_heat_loss": 6,
"roof_construction": 3,
"wall_construction": 8,
"building_part_number": 1,
"sap_floor_dimensions": [
{
"floor": 0,
"room_height": {"value": 2.4, "quantity": "metres"},
"total_floor_area": {"value": 54.6, "quantity": "square metres"},
"party_wall_length": {"value": 7.3, "quantity": "metres"},
"heat_loss_perimeter": {"value": 23.3, "quantity": "metres"}
}
],
"wall_insulation_type": 3,
"construction_age_band": "D",
"party_wall_construction": 0,
"wall_thickness_measured": "Y",
"roof_insulation_location": "ND",
"roof_insulation_thickness": "ND",
"wall_insulation_thickness": "50mm"
}
],
"low_energy_lighting": 57,
"solar_water_heating": "N",
"habitable_room_count": 3,
"heating_cost_current": {"value": 214, "currency": "GBP"},
"insulated_door_count": 0,
"co2_emissions_current": 3.9,
"energy_rating_average": 60,
"energy_rating_current": 66,
"lighting_cost_current": {"value": 61, "currency": "GBP"},
"main_heating_controls": [
{
"description": {"value": "Manual charge control", "language": "1"},
"energy_efficiency_rating": 2,
"environmental_efficiency_rating": 2
}
],
"multiple_glazing_type": 3,
"open_fireplaces_count": 0,
"has_hot_water_cylinder": "true",
"heating_cost_potential": {"value": 216, "currency": "GBP"},
"hot_water_cost_current": {"value": 396, "currency": "GBP"},
"mechanical_ventilation": 0,
"percent_draughtproofed": 100,
"suggested_improvements": [
{
"sequence": 1,
"typical_saving": {"value": 158, "currency": "GBP"},
"indicative_cost": "£15 - £30",
"improvement_type": "C",
"improvement_details": {"improvement_number": 1},
"improvement_category": 5,
"energy_performance_rating": 74,
"environmental_impact_rating": 60
}
],
"co2_emissions_potential": 2.5,
"energy_rating_potential": 79,
"lighting_cost_potential": {"value": 42, "currency": "GBP"},
"schema_version_original": "LIG-17.0",
"alternative_improvements": [
{
"sequence": 1,
"typical_saving": {"value": 141, "currency": "GBP"},
"improvement_type": "J2",
"improvement_details": {"improvement_number": 54},
"improvement_category": 6,
"energy_performance_rating": 81,
"environmental_impact_rating": 96
}
],
"hot_water_cost_potential": {"value": 154, "currency": "GBP"},
"renewable_heat_incentive": {
"water_heating": 4818,
"space_heating_existing_dwelling": 2415
},
"energy_consumption_current": 427,
"has_fixed_air_conditioning": "false",
"multiple_glazed_proportion": 100,
"calculation_software_version": "9.0.0",
"energy_consumption_potential": 267,
"environmental_impact_current": 48,
"fixed_lighting_outlets_count": 7,
"current_energy_efficiency_band": "D",
"environmental_impact_potential": 66,
"has_heated_separate_conservatory": "false",
"potential_energy_efficiency_band": "C",
"co2_emissions_current_per_floor_area": 72,
"low_energy_fixed_lighting_outlets_count": 4
}

243
domain/epc/tests/fixtures/17_1.json vendored Normal file
View file

@ -0,0 +1,243 @@
{
"uprn": 12457,
"roofs": [
{
"description": {"value": "Pitched, 100 mm loft insulation", "language": "1"},
"energy_efficiency_rating": 3,
"environmental_efficiency_rating": 3
},
{
"description": {"value": "Pitched, insulated (assumed)", "language": "1"},
"energy_efficiency_rating": 4,
"environmental_efficiency_rating": 4
}
],
"walls": [
{
"description": {"value": "Cavity wall, filled cavity", "language": "1"},
"energy_efficiency_rating": 4,
"environmental_efficiency_rating": 4
}
],
"floors": [
{
"description": {"value": "Solid, no insulation (assumed)", "language": "1"},
"energy_efficiency_rating": 0,
"environmental_efficiency_rating": 0
}
],
"status": "entered",
"tenure": 1,
"window": {
"description": {"value": "Fully double glazed", "language": "1"},
"energy_efficiency_rating": 3,
"environmental_efficiency_rating": 3
},
"lighting": {
"description": {"value": "Low energy lighting in 23% of fixed outlets", "language": "1"},
"energy_efficiency_rating": 2,
"environmental_efficiency_rating": 2
},
"postcode": "PT42 5HL",
"hot_water": {
"description": {"value": "From main system", "language": "1"},
"energy_efficiency_rating": 3,
"environmental_efficiency_rating": 3
},
"post_town": "POSTTOWN",
"built_form": 1,
"door_count": 4,
"glazed_area": 1,
"glazing_gap": "16+",
"region_code": 17,
"report_type": 2,
"sap_heating": {
"cylinder_size": 2,
"water_heating_code": 901,
"water_heating_fuel": 28,
"cylinder_thermostat": "Y",
"instantaneous_wwhrs": {
"rooms_with_bath_and_or_shower": 1,
"rooms_with_mixer_shower_no_bath": 0,
"rooms_with_bath_and_mixer_shower": 1
},
"secondary_fuel_type": 29,
"main_heating_details": [
{
"has_fghrs": "N",
"main_fuel_type": 28,
"boiler_flue_type": 1,
"fan_flue_present": "N",
"heat_emitter_type": 1,
"emitter_temperature": "NA",
"main_heating_number": 1,
"main_heating_control": 2104,
"main_heating_category": 2,
"main_heating_fraction": 1,
"mcs_installed_heat_pump": "false",
"main_heating_data_source": 1,
"main_heating_index_number": 9049
}
],
"immersion_heating_type": "NA",
"secondary_heating_type": 691,
"cylinder_insulation_type": 1,
"has_fixed_air_conditioning": "false",
"cylinder_insulation_thickness": 12
},
"sap_version": 9.92,
"schema_type": "RdSAP-Schema-17.1",
"uprn_source": "Energy Assessor",
"country_code": "EAW",
"main_heating": [
{
"description": {"value": "Boiler and radiators, oil", "language": "1"},
"energy_efficiency_rating": 3,
"environmental_efficiency_rating": 3
}
],
"dwelling_type": {"value": "Detached house", "language": "1"},
"language_code": 1,
"property_type": 0,
"address_line_1": "15, Hedge Lane",
"address_line_2": "Lower Moria",
"assessment_type": "RdSAP",
"completion_date": "2018-05-29",
"inspection_date": "2018-05-29",
"extensions_count": 1,
"measurement_type": 2,
"total_floor_area": 101,
"transaction_type": 1,
"conservatory_type": 1,
"heated_room_count": 7,
"pvc_window_frames": "true",
"registration_date": "2018-05-27",
"sap_energy_source": {
"mains_gas": "N",
"meter_type": 2,
"photovoltaic_supply": {
"none_or_no_details": {"pv_connection": 0, "percent_roof_area": 0}
},
"wind_turbines_count": 0,
"wind_turbines_terrain_type": 2
},
"secondary_heating": {
"description": {"value": "Room heaters, electric", "language": "1"},
"energy_efficiency_rating": 0,
"environmental_efficiency_rating": 0
},
"lzc_energy_sources": [11],
"sap_building_parts": [
{
"identifier": "Main Dwelling",
"wall_dry_lined": "N",
"wall_thickness": 270,
"floor_heat_loss": 7,
"roof_construction": 4,
"wall_construction": 4,
"building_part_number": 1,
"sap_floor_dimensions": [
{
"floor": 0,
"room_height": {"value": 2.4, "quantity": "metres"},
"floor_insulation": 1,
"total_floor_area": {"value": 48.45, "quantity": "square metres"},
"party_wall_length": {"value": 0, "quantity": "metres"},
"floor_construction": 1,
"heat_loss_perimeter": {"value": 22.3, "quantity": "metres"}
}
],
"wall_insulation_type": 2,
"construction_age_band": "G",
"party_wall_construction": "NA",
"wall_thickness_measured": "Y",
"roof_insulation_location": 2,
"roof_insulation_thickness": "100mm"
},
{
"identifier": "Extension 1",
"wall_dry_lined": "N",
"wall_thickness": 260,
"floor_heat_loss": 7,
"roof_construction": 5,
"wall_construction": 4,
"building_part_number": 2,
"sap_floor_dimensions": [
{
"floor": 0,
"room_height": {"value": 2.4, "quantity": "metres"},
"floor_insulation": 1,
"total_floor_area": {"value": 21.84, "quantity": "square metres"},
"party_wall_length": 0,
"floor_construction": 1,
"heat_loss_perimeter": {"value": 16.6, "quantity": "metres"}
}
],
"wall_insulation_type": 4,
"construction_age_band": "H",
"party_wall_construction": "NA",
"wall_thickness_measured": "Y",
"roof_insulation_location": 4,
"roof_insulation_thickness": 0,
"wall_insulation_thickness": "NI"
}
],
"low_energy_lighting": 23,
"solar_water_heating": "N",
"habitable_room_count": 7,
"heating_cost_current": {"value": 659, "currency": "GBP"},
"insulated_door_count": 0,
"co2_emissions_current": 5.8,
"energy_rating_average": 60,
"energy_rating_current": 53,
"lighting_cost_current": {"value": 115, "currency": "GBP"},
"main_heating_controls": [
{
"description": {"value": "Programmer and room thermostat", "language": "1"},
"energy_efficiency_rating": 3,
"environmental_efficiency_rating": 3
}
],
"multiple_glazing_type": 3,
"open_fireplaces_count": 0,
"has_hot_water_cylinder": "true",
"heating_cost_potential": {"value": 470, "currency": "GBP"},
"hot_water_cost_current": {"value": 161, "currency": "GBP"},
"mechanical_ventilation": 0,
"percent_draughtproofed": 100,
"suggested_improvements": [
{
"sequence": 1,
"typical_saving": {"value": 25, "currency": "GBP"},
"indicative_cost": "£100 - £350",
"improvement_type": "A",
"improvement_details": {"improvement_number": 5},
"improvement_category": 5,
"energy_performance_rating": 54,
"environmental_impact_rating": 47
}
],
"co2_emissions_potential": 2.7,
"energy_rating_potential": 78,
"lighting_cost_potential": {"value": 65, "currency": "GBP"},
"schema_version_original": "LIG-17.1",
"hot_water_cost_potential": {"value": 77, "currency": "GBP"},
"renewable_heat_incentive": {
"water_heating": 3301,
"impact_of_loft_insulation": -565,
"space_heating_existing_dwelling": 11351
},
"energy_consumption_current": 234,
"has_fixed_air_conditioning": "false",
"multiple_glazed_proportion": 100,
"calculation_software_version": "v92.0.1.1",
"energy_consumption_potential": 95,
"environmental_impact_current": 46,
"fixed_lighting_outlets_count": 13,
"current_energy_efficiency_band": "E",
"environmental_impact_potential": 72,
"has_heated_separate_conservatory": "false",
"potential_energy_efficiency_band": "C",
"co2_emissions_current_per_floor_area": 57,
"low_energy_fixed_lighting_outlets_count": 3
}

251
domain/epc/tests/fixtures/18_0.json vendored Normal file
View file

@ -0,0 +1,251 @@
{
"uprn": 12457,
"roofs": [
{
"description": {"value": "Pitched, 100 mm loft insulation", "language": "1"},
"energy_efficiency_rating": 3,
"environmental_efficiency_rating": 3
},
{
"description": {"value": "Flat, insulated", "language": "1"},
"energy_efficiency_rating": 3,
"environmental_efficiency_rating": 3
},
{
"description": {"value": "Roof room(s), insulated (assumed)", "language": "1"},
"energy_efficiency_rating": 4,
"environmental_efficiency_rating": 4
}
],
"walls": [
{
"description": {"value": "Solid brick, as built, no insulation (assumed)", "language": "1"},
"energy_efficiency_rating": 1,
"environmental_efficiency_rating": 1
}
],
"floors": [
{
"description": {"value": "Solid, no insulation (assumed)", "language": "1"},
"energy_efficiency_rating": 0,
"environmental_efficiency_rating": 0
}
],
"status": "entered",
"tenure": 1,
"window": {
"description": {"value": "Fully double glazed", "language": "1"},
"energy_efficiency_rating": 3,
"environmental_efficiency_rating": 3
},
"lighting": {
"description": {"value": "Low energy lighting in 67% of fixed outlets", "language": "1"},
"energy_efficiency_rating": 4,
"environmental_efficiency_rating": 4
},
"postcode": "PT11 4RF",
"hot_water": {
"description": {"value": "From main system", "language": "1"},
"energy_efficiency_rating": 4,
"environmental_efficiency_rating": 4
},
"post_town": "POSTTOWN",
"built_form": 4,
"door_count": 2,
"glazed_area": 1,
"glazing_gap": 12,
"region_code": 1,
"report_type": 2,
"sap_heating": {
"cylinder_size": 1,
"water_heating_code": 901,
"water_heating_fuel": 26,
"instantaneous_wwhrs": {
"rooms_with_bath_and_or_shower": 1,
"rooms_with_mixer_shower_no_bath": 0,
"rooms_with_bath_and_mixer_shower": 0
},
"main_heating_details": [
{
"has_fghrs": "N",
"main_fuel_type": 26,
"boiler_flue_type": 2,
"heat_emitter_type": 1,
"emitter_temperature": 0,
"main_heating_number": 1,
"main_heating_control": 2106,
"main_heating_category": 2,
"main_heating_fraction": 1,
"central_heating_pump_age": 0,
"main_heating_data_source": 1,
"main_heating_index_number": 16137
}
],
"immersion_heating_type": "NA",
"has_fixed_air_conditioning": "false"
},
"sap_version": 9.92,
"schema_type": "RdSAP-Schema-18.0",
"uprn_source": "Energy Assessor",
"country_code": "EAW",
"main_heating": [
{
"description": {"value": "Boiler and radiators, mains gas", "language": "1"},
"energy_efficiency_rating": 4,
"environmental_efficiency_rating": 4
}
],
"dwelling_type": {"value": "Mid-terrace house", "language": "1"},
"language_code": 1,
"property_type": 0,
"address_line_1": "1, Bagshot Lane",
"address_line_2": "Village",
"assessment_type": "RdSAP",
"completion_date": "2017-03-19",
"inspection_date": "2017-03-19",
"extensions_count": 1,
"measurement_type": 1,
"total_floor_area": 93,
"transaction_type": 5,
"conservatory_type": 1,
"heated_room_count": 5,
"pvc_window_frames": "true",
"registration_date": "2017-03-19",
"sap_energy_source": {
"mains_gas": "Y",
"meter_type": 1,
"photovoltaic_supply": {
"none_or_no_details": {"pv_connection": 0, "percent_roof_area": 0}
},
"wind_turbines_count": 0,
"wind_turbines_terrain_type": 2
},
"secondary_heating": {
"description": {"value": "None", "language": "1"},
"energy_efficiency_rating": 0,
"environmental_efficiency_rating": 0
},
"lzc_energy_sources": [11],
"sap_building_parts": [
{
"identifier": "Main Dwelling",
"wall_dry_lined": "N",
"wall_thickness": 330,
"floor_heat_loss": 7,
"sap_room_in_roof": {
"floor_area": {"value": 9, "quantity": "square metres"},
"insulation": "AB",
"roof_room_connected": "N",
"construction_age_band": "G"
},
"roof_construction": 4,
"wall_construction": 3,
"building_part_number": 1,
"sap_floor_dimensions": [
{
"floor": 0,
"room_height": {"value": 2.4, "quantity": "metres"},
"floor_insulation": 1,
"total_floor_area": {"value": 29.12, "quantity": "square metres"},
"party_wall_length": {"value": 11.2, "quantity": "metres"},
"floor_construction": 1,
"heat_loss_perimeter": {"value": 5.2, "quantity": "metres"}
}
],
"wall_insulation_type": 4,
"construction_age_band": "C",
"party_wall_construction": 0,
"wall_thickness_measured": "Y",
"roof_insulation_location": 2,
"roof_insulation_thickness": "100mm",
"wall_insulation_thickness": "NI"
},
{
"identifier": "Extension",
"wall_dry_lined": "N",
"wall_thickness": 290,
"floor_heat_loss": 7,
"roof_construction": 1,
"wall_construction": 4,
"building_part_number": 2,
"sap_floor_dimensions": [
{
"floor": 0,
"room_height": {"value": 2.4, "quantity": "metres"},
"floor_insulation": 1,
"total_floor_area": {"value": 15.6, "quantity": "square metres"},
"party_wall_length": {"value": 6, "quantity": "metres"},
"floor_construction": 1,
"heat_loss_perimeter": {"value": 5.2, "quantity": "metres"}
}
],
"wall_insulation_type": 4,
"construction_age_band": "G",
"party_wall_construction": 0,
"wall_thickness_measured": "Y",
"roof_insulation_location": 6,
"roof_insulation_thickness": "NI",
"wall_insulation_thickness": "NI",
"flat_roof_insulation_thickness": "NI"
}
],
"low_energy_lighting": 67,
"solar_water_heating": "N",
"habitable_room_count": 5,
"heating_cost_current": {"value": 619, "currency": "GBP"},
"insulated_door_count": 0,
"co2_emissions_current": 3.4,
"energy_rating_average": 60,
"energy_rating_current": 69,
"lighting_cost_current": {"value": 81, "currency": "GBP"},
"main_heating_controls": [
{
"description": {"value": "Programmer, room thermostat and TRVs", "language": "1"},
"energy_efficiency_rating": 4,
"environmental_efficiency_rating": 4
}
],
"multiple_glazing_type": 3,
"open_fireplaces_count": 0,
"has_hot_water_cylinder": "false",
"heating_cost_potential": {"value": 534, "currency": "GBP"},
"hot_water_cost_current": {"value": 100, "currency": "GBP"},
"mechanical_ventilation": 0,
"percent_draughtproofed": 100,
"suggested_improvements": [
{
"sequence": 1,
"typical_saving": {"value": 88, "currency": "GBP"},
"indicative_cost": "£4,000 - £14,000",
"improvement_type": "Q",
"improvement_details": {"improvement_number": 7},
"improvement_category": 5,
"energy_performance_rating": 72,
"environmental_impact_rating": 71
}
],
"co2_emissions_potential": 1.7,
"energy_rating_potential": 85,
"lighting_cost_potential": {"value": 61, "currency": "GBP"},
"schema_version_original": "LIG-17.0",
"hot_water_cost_potential": {"value": 68, "currency": "GBP"},
"renewable_heat_incentive": {
"water_heating": 2087,
"impact_of_loft_insulation": -214,
"impact_of_solid_wall_insulation": -1864,
"space_heating_existing_dwelling": 10483
},
"energy_consumption_current": 230,
"has_fixed_air_conditioning": "false",
"multiple_glazed_proportion": 100,
"calculation_software_version": "2.0.0.0",
"energy_consumption_potential": 115,
"environmental_impact_current": 66,
"fixed_lighting_outlets_count": 9,
"current_energy_efficiency_band": "C",
"environmental_impact_potential": 83,
"has_heated_separate_conservatory": "false",
"potential_energy_efficiency_band": "B",
"co2_emissions_current_per_floor_area": 41,
"low_energy_fixed_lighting_outlets_count": 6
}

213
domain/epc/tests/fixtures/19_0.json vendored Normal file
View file

@ -0,0 +1,213 @@
{
"uprn": 12457,
"roofs": [
{
"description": {"value": "Pitched, 150 mm loft insulation", "language": "1"},
"energy_efficiency_rating": 4,
"environmental_efficiency_rating": 4
}
],
"walls": [
{
"description": {"value": "Cavity wall, filled cavity", "language": "1"},
"energy_efficiency_rating": 3,
"environmental_efficiency_rating": 3
}
],
"floors": [
{
"description": {"value": "Solid, no insulation (assumed)", "language": "1"},
"energy_efficiency_rating": 0,
"environmental_efficiency_rating": 0
}
],
"status": "entered",
"tenure": 3,
"window": {
"description": {"value": "Fully double glazed", "language": "1"},
"energy_efficiency_rating": 3,
"environmental_efficiency_rating": 3
},
"lighting": {
"description": {"value": "Low energy lighting in 87% of fixed outlets", "language": "1"},
"energy_efficiency_rating": 5,
"environmental_efficiency_rating": 5
},
"postcode": "A1 1AA",
"hot_water": {
"description": {"value": "From main system", "language": "1"},
"energy_efficiency_rating": 4,
"environmental_efficiency_rating": 4
},
"post_town": "Town",
"built_form": 2,
"door_count": 1,
"glazed_area": 1,
"region_code": 19,
"report_type": 2,
"sap_heating": {
"cylinder_size": 1,
"water_heating_code": 901,
"water_heating_fuel": 26,
"instantaneous_wwhrs": {
"rooms_with_bath_and_or_shower": 1,
"rooms_with_mixer_shower_no_bath": 0,
"rooms_with_bath_and_mixer_shower": 0
},
"secondary_fuel_type": 9,
"main_heating_details": [
{
"has_fghrs": "N",
"main_fuel_type": 26,
"boiler_flue_type": 2,
"heat_emitter_type": 1,
"emitter_temperature": 0,
"main_heating_number": 1,
"main_heating_control": 2106,
"main_heating_category": 2,
"main_heating_fraction": 1,
"central_heating_pump_age": 0,
"main_heating_data_source": 1,
"main_heating_index_number": 15274
}
],
"immersion_heating_type": "NA",
"secondary_heating_type": 631,
"has_fixed_air_conditioning": "false"
},
"sap_version": 9.94,
"schema_type": "RdSAP-Schema-19.0",
"uprn_source": "Energy Assessor",
"country_code": "EAW",
"main_heating": [
{
"description": {"value": "Boiler and radiators, mains gas", "language": "1"},
"energy_efficiency_rating": 4,
"environmental_efficiency_rating": 4
}
],
"dwelling_type": {"value": "Semi-detached house", "language": "1"},
"language_code": 1,
"property_type": 0,
"address_line_1": "15, Address Lane",
"address_line_2": "New Town",
"assessment_type": "RdSAP",
"completion_date": "2020-06-04",
"inspection_date": "2020-06-03",
"extensions_count": 1,
"measurement_type": 1,
"total_floor_area": 94,
"transaction_type": 8,
"conservatory_type": 2,
"heated_room_count": 5,
"pvc_window_frames": "false",
"registration_date": "2020-06-04",
"sap_energy_source": {
"mains_gas": "Y",
"meter_type": 3,
"photovoltaic_supply": {
"none_or_no_details": {"pv_connection": 0, "percent_roof_area": 0}
},
"wind_turbines_count": 0,
"wind_turbines_terrain_type": 2
},
"secondary_heating": {
"description": {"value": "Room heaters, dual fuel (mineral and wood)", "language": "1"},
"energy_efficiency_rating": 0,
"environmental_efficiency_rating": 0
},
"lzc_energy_sources": [11, 9],
"sap_building_parts": [
{
"identifier": "Main Dwelling",
"wall_dry_lined": "N",
"wall_thickness": 300,
"floor_heat_loss": 7,
"roof_construction": 4,
"wall_construction": 4,
"building_part_number": 1,
"sap_floor_dimensions": [
{
"floor": 0,
"room_height": {"value": 2.47, "quantity": "metres"},
"floor_insulation": 1,
"total_floor_area": {"value": 39.91, "quantity": "square metres"},
"party_wall_length": {"value": 8.81, "quantity": "metres"},
"floor_construction": 1,
"heat_loss_perimeter": {"value": 13.65, "quantity": "metres"}
}
],
"wall_insulation_type": 2,
"construction_age_band": "D",
"party_wall_construction": 1,
"wall_thickness_measured": "Y",
"roof_insulation_location": 2,
"roof_insulation_thickness": "150mm",
"wall_insulation_thickness": "NI",
"floor_insulation_thickness": "NI"
}
],
"low_energy_lighting": 87,
"solar_water_heating": "N",
"habitable_room_count": 5,
"heating_cost_current": {"value": 666, "currency": "GBP"},
"insulated_door_count": 0,
"co2_emissions_current": 3.8,
"energy_rating_average": 60,
"energy_rating_current": 66,
"lighting_cost_current": {"value": 81, "currency": "GBP"},
"main_heating_controls": [
{
"description": {"value": "Programmer, room thermostat and TRVs", "language": "1"},
"energy_efficiency_rating": 4,
"environmental_efficiency_rating": 4
}
],
"multiple_glazing_type": 3,
"open_fireplaces_count": 0,
"has_hot_water_cylinder": "false",
"heating_cost_potential": {"value": 615, "currency": "GBP"},
"hot_water_cost_current": {"value": 107, "currency": "GBP"},
"mechanical_ventilation": 0,
"percent_draughtproofed": 100,
"suggested_improvements": [
{
"sequence": 1,
"typical_saving": {"value": 51, "currency": "GBP"},
"indicative_cost": "£4,000 - £6,000",
"improvement_type": "W2",
"improvement_details": {"improvement_number": 58},
"improvement_category": 5,
"energy_performance_rating": 68,
"environmental_impact_rating": 65
}
],
"co2_emissions_potential": 2.4,
"energy_rating_potential": 79,
"lighting_cost_potential": {"value": 81, "currency": "GBP"},
"schema_version_original": "LIG-19.0",
"hot_water_cost_potential": {"value": 74, "currency": "GBP"},
"renewable_heat_incentive": {
"water_heating": 2207,
"impact_of_loft_insulation": -394,
"space_heating_existing_dwelling": 9825
},
"energy_consumption_current": 222,
"has_fixed_air_conditioning": "false",
"multiple_glazed_proportion": 100,
"calculation_software_version": "2.1.1.0",
"energy_consumption_potential": 137,
"environmental_impact_current": 62,
"fixed_lighting_outlets_count": 15,
"windows_transmission_details": {
"u_value": 3.1,
"data_source": 2,
"solar_transmittance": 0.76
},
"current_energy_efficiency_band": "D",
"environmental_impact_potential": 75,
"has_heated_separate_conservatory": "false",
"potential_energy_efficiency_band": "C",
"co2_emissions_current_per_floor_area": 41,
"low_energy_fixed_lighting_outlets_count": 13
}

225
domain/epc/tests/fixtures/20_0_0.json vendored Normal file
View file

@ -0,0 +1,225 @@
{
"uprn": 12457,
"roofs": [
{"description": "Pitched, 25 mm loft insulation", "energy_efficiency_rating": 2, "environmental_efficiency_rating": 2},
{"description": "Pitched, 250 mm loft insulation", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4}
],
"walls": [
{"description": "Solid brick, as built, no insulation (assumed)", "energy_efficiency_rating": 1, "environmental_efficiency_rating": 1},
{"description": "Cavity wall, as built, insulated (assumed)", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4}
],
"floors": [
{"description": "Suspended, no insulation (assumed)", "energy_efficiency_rating": 0, "environmental_efficiency_rating": 0},
{"description": "Solid, insulated (assumed)", "energy_efficiency_rating": 0, "environmental_efficiency_rating": 0}
],
"status": "entered",
"tenure": 1,
"window": {"description": "Fully double glazed", "energy_efficiency_rating": 3, "environmental_efficiency_rating": 3},
"addendum": {
"stone_walls": "true",
"system_build": "true",
"addendum_numbers": [1, 8]
},
"lighting": {"description": "Low energy lighting in 50% of fixed outlets", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4},
"postcode": "A0 0AA",
"hot_water": {"description": "From main system", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4},
"post_town": "Whitbury",
"built_form": 2,
"door_count": 2,
"glazed_area": 1,
"region_code": 1,
"report_type": 2,
"sap_heating": {
"cylinder_size": 1,
"water_heating_code": 901,
"water_heating_fuel": 26,
"instantaneous_wwhrs": {
"rooms_with_bath_and_or_shower": 1,
"rooms_with_mixer_shower_no_bath": 0,
"rooms_with_bath_and_mixer_shower": 0
},
"secondary_fuel_type": 25,
"main_heating_details": [
{
"has_fghrs": "N",
"main_fuel_type": 26,
"boiler_flue_type": 2,
"fan_flue_present": "N",
"heat_emitter_type": 1,
"emitter_temperature": 0,
"main_heating_number": 1,
"main_heating_control": 2106,
"main_heating_category": 2,
"main_heating_fraction": 1,
"sap_main_heating_code": 101,
"central_heating_pump_age": 0,
"main_heating_data_source": 1,
"main_heating_index_number": 17507
}
],
"immersion_heating_type": "NA",
"has_fixed_air_conditioning": "false"
},
"sap_version": 9.8,
"sap_windows": [
{"orientation": 1, "window_area": 200.1, "window_type": 2, "glazing_type": 1, "window_location": 0},
{"orientation": 2, "window_area": 180.2, "window_type": 1, "glazing_type": 2, "window_location": 1}
],
"schema_type": "RdSAP-Schema-20.0.0",
"uprn_source": "Energy Assessor",
"country_code": "EAW",
"main_heating": [
{"description": "Boiler and radiators, anthracite", "energy_efficiency_rating": 3, "environmental_efficiency_rating": 1},
{"description": "Boiler and radiators, mains gas", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4}
],
"dwelling_type": "Mid-terrace house",
"language_code": 1,
"property_type": 0,
"address_line_1": "1 Some Street",
"assessment_type": "RdSAP",
"completion_date": "2020-05-04",
"inspection_date": "2020-05-04",
"extensions_count": 0,
"measurement_type": 1,
"sap_flat_details": {
"level": 1,
"top_storey": "N",
"storey_count": 3,
"flat_location": 1,
"heat_loss_corridor": 2,
"unheated_corridor_length": 10
},
"total_floor_area": 55,
"transaction_type": 1,
"conservatory_type": 1,
"heated_room_count": 5,
"registration_date": "2020-05-04",
"sap_energy_source": {
"mains_gas": "Y",
"meter_type": 2,
"photovoltaic_supply": {
"none_or_no_details": {"pv_connection": 0, "percent_roof_area": 50}
},
"wind_turbines_count": 0,
"wind_turbines_terrain_type": 2
},
"secondary_heating": {"description": "Room heaters, electric", "energy_efficiency_rating": 0, "environmental_efficiency_rating": 0},
"lzc_energy_sources": [11],
"sap_building_parts": [
{
"identifier": "Main Dwelling",
"wall_dry_lined": "N",
"floor_heat_loss": 7,
"sap_room_in_roof": {
"floor_area": 100,
"insulation": "AB",
"roof_room_connected": "N",
"construction_age_band": "B"
},
"roof_construction": 4,
"wall_construction": 4,
"building_part_number": 1,
"sap_floor_dimensions": [
{
"floor": 0,
"room_height": {"value": 2.45, "quantity": "metres"},
"floor_insulation": 1,
"total_floor_area": {"value": 45.82, "quantity": "square metres"},
"party_wall_length": {"value": 7.9, "quantity": "metres"},
"floor_construction": 1,
"heat_loss_perimeter": {"value": 19.5, "quantity": "metres"}
}
],
"wall_insulation_type": 2,
"construction_age_band": "K",
"party_wall_construction": 0,
"wall_thickness_measured": "N",
"roof_insulation_location": 2,
"roof_insulation_thickness": "200mm",
"wall_insulation_thickness": "NI",
"floor_insulation_thickness": "NI"
}
],
"low_energy_lighting": 100,
"solar_water_heating": "N",
"habitable_room_count": 5,
"heating_cost_current": 365.98,
"insulated_door_count": 2,
"co2_emissions_current": 2.4,
"energy_rating_average": 60,
"energy_rating_current": 50,
"lighting_cost_current": 123.45,
"main_heating_controls": [
{"description": "Programmer, room thermostat and TRVs", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4},
{"description": "Time and temperature zone control", "energy_efficiency_rating": 5, "environmental_efficiency_rating": 5}
],
"multiple_glazing_type": 2,
"open_fireplaces_count": 0,
"heating_cost_potential": 250.34,
"hot_water_cost_current": 200.4,
"insulated_door_u_value": 3,
"mechanical_ventilation": 0,
"percent_draughtproofed": 100,
"suggested_improvements": [
{
"sequence": 1,
"typical_saving": 360,
"indicative_cost": "£100 - £350",
"improvement_type": "Z3",
"improvement_details": {"improvement_number": 5},
"improvement_category": 6,
"energy_performance_rating": 50,
"environmental_impact_rating": 50
},
{
"sequence": 2,
"typical_saving": 99,
"indicative_cost": 2000,
"improvement_type": "Z2",
"improvement_details": {"improvement_number": 1},
"improvement_category": 2,
"energy_performance_rating": 60,
"environmental_impact_rating": 64
},
{
"sequence": 3,
"typical_saving": 99,
"indicative_cost": 1000,
"improvement_type": "Z2",
"improvement_details": {
"improvement_texts": {
"improvement_summary": "An improvement summary",
"improvement_description": "An improvement desc"
}
},
"improvement_category": 2,
"energy_performance_rating": 60,
"environmental_impact_rating": 64
}
],
"co2_emissions_potential": 1.4,
"energy_rating_potential": 72,
"lighting_cost_potential": 84.23,
"schema_version_original": "SAP-19.0",
"hot_water_cost_potential": 180.43,
"renewable_heat_incentive": {
"water_heating": 2285,
"impact_of_loft_insulation": -2114,
"impact_of_cavity_insulation": -122,
"impact_of_solid_wall_insulation": -3560,
"space_heating_existing_dwelling": 13120
},
"energy_consumption_current": 230,
"multiple_glazed_proportion": 100,
"calculation_software_version": "13.05r16",
"energy_consumption_potential": 88,
"environmental_impact_current": 52,
"fixed_lighting_outlets_count": 16,
"windows_transmission_details": {"u_value": 2, "data_source": 2, "solar_transmittance": 0.72},
"multiple_glazed_proportion_nr": "NR",
"current_energy_efficiency_band": "E",
"environmental_impact_potential": 74,
"potential_energy_efficiency_band": "C",
"co2_emissions_current_per_floor_area": 20,
"low_energy_fixed_lighting_outlets_count": 16
}

245
domain/epc/tests/fixtures/21_0_0.json vendored Normal file
View file

@ -0,0 +1,245 @@
{
"uprn": 12457,
"roofs": [
{"description": "Pitched, 25 mm loft insulation", "energy_efficiency_rating": 2, "environmental_efficiency_rating": 2},
{"description": "Pitched, 250 mm loft insulation", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4}
],
"walls": [
{"description": "Solid brick, as built, no insulation (assumed)", "energy_efficiency_rating": 1, "environmental_efficiency_rating": 1},
{"description": "Cavity wall, as built, insulated (assumed)", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4}
],
"floors": [
{"description": "Suspended, no insulation (assumed)", "energy_efficiency_rating": 0, "environmental_efficiency_rating": 0},
{"description": "Solid, insulated (assumed)", "energy_efficiency_rating": 0, "environmental_efficiency_rating": 0}
],
"status": "entered",
"tenure": 1,
"window": {"description": "Fully double glazed", "energy_efficiency_rating": 3, "environmental_efficiency_rating": 3},
"addendum": {"stone_walls": "true", "system_build": "true", "addendum_numbers": [1, 8]},
"lighting": {"description": "Low energy lighting in 50% of fixed outlets", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4},
"postcode": "A0 0AA",
"hot_water": {"description": "From main system", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4},
"post_town": "Whitbury",
"built_form": 2,
"door_count": 3,
"region_code": 1,
"report_type": 2,
"sap_heating": {
"cylinder_size": 1,
"shower_outlets": {
"shower_outlet": {"shower_wwhrs": 1, "shower_outlet_type": 1}
},
"water_heating_code": 901,
"water_heating_fuel": 26,
"instantaneous_wwhrs": {"wwhrs_index_number1": 1, "wwhrs_index_number2": 2},
"secondary_fuel_type": 25,
"main_heating_details": [
{
"has_fghrs": "N",
"main_fuel_type": 26,
"boiler_flue_type": 2,
"fan_flue_present": "N",
"heat_emitter_type": 1,
"emitter_temperature": 0,
"main_heating_number": 1,
"boiler_ignition_type": 1,
"main_heating_control": 2106,
"main_heating_category": 2,
"main_heating_fraction": 1,
"sap_main_heating_code": 101,
"central_heating_pump_age": 0,
"main_heating_data_source": 1,
"main_heating_index_number": 17507
}
],
"immersion_heating_type": "NA",
"has_fixed_air_conditioning": "false"
},
"sap_version": 10.2,
"sap_windows": [
{
"pvc_frame": "false",
"glazing_gap": 6,
"orientation": 1,
"window_type": 2,
"frame_factor": 1.0,
"glazing_type": 14,
"window_width": 1.2,
"window_height": 2.0,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 6,
"permanent_shutters_present": "N",
"window_transmission_details": {"u_value": 1.0, "data_source": 2, "solar_transmittance": 1.0},
"permanent_shutters_insulated": "N"
}
],
"schema_type": "RdSAP-Schema-21.0.0",
"uprn_source": "Energy Assessor",
"country_code": "ENG",
"main_heating": [
{"description": "Boiler and radiators, anthracite", "energy_efficiency_rating": 3, "environmental_efficiency_rating": 1},
{"description": "Boiler and radiators, mains gas", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4}
],
"dwelling_type": "Mid-terrace house",
"language_code": 1,
"pressure_test": 6,
"property_type": 0,
"address_line_1": "1 Some Street",
"assessment_type": "RdSAP",
"completion_date": "2023-12-01",
"inspection_date": "2023-12-01",
"wet_rooms_count": 0,
"extensions_count": 0,
"measurement_type": 1,
"sap_flat_details": {
"level": 1,
"top_storey": "N",
"storey_count": 3,
"flat_location": 1,
"heat_loss_corridor": 2,
"unheated_corridor_length": 10
},
"total_floor_area": 55,
"transaction_type": 16,
"conservatory_type": 1,
"heated_room_count": 5,
"registration_date": "2023-12-01",
"sap_energy_source": {
"mains_gas": "Y",
"meter_type": 2,
"pv_batteries": {"pv_battery": {"battery_capacity": 5}},
"pv_connection": 0,
"pv_battery_count": 1,
"photovoltaic_supply": {"none_or_no_details": {"percent_roof_area": 0}},
"wind_turbines_count": 0,
"wind_turbine_details": {"hub_height": 0, "rotor_diameter": 0},
"gas_smart_meter_present": "false",
"is_dwelling_export_capable": "false",
"wind_turbines_terrain_type": 4,
"electricity_smart_meter_present": "true"
},
"secondary_heating": {"description": "Room heaters, electric", "energy_efficiency_rating": 0, "environmental_efficiency_rating": 0},
"sap_building_parts": [
{
"identifier": "Main Dwelling",
"wall_dry_lined": "N",
"floor_heat_loss": 7,
"sap_room_in_roof": {"floor_area": 100, "construction_age_band": "B"},
"roof_construction": 4,
"wall_construction": 4,
"building_part_number": 1,
"sap_floor_dimensions": [
{
"floor": 0,
"room_height": {"value": 2.45, "quantity": "metres"},
"floor_insulation": 1,
"total_floor_area": {"value": 45.82, "quantity": "square metres"},
"party_wall_length": {"value": 7.9, "quantity": "metres"},
"floor_construction": 1,
"heat_loss_perimeter": {"value": 19.5, "quantity": "metres"}
}
],
"wall_insulation_type": 2,
"construction_age_band": "M",
"sap_alternative_wall_1": {
"wall_area": 10.4,
"wall_dry_lined": "N",
"wall_construction": 4,
"wall_insulation_type": 2,
"wall_thickness_measured": "N"
},
"sap_alternative_wall_2": {
"wall_area": 10.8,
"wall_dry_lined": "N",
"wall_construction": 4,
"wall_insulation_type": 2,
"wall_thickness_measured": "N"
},
"party_wall_construction": 0,
"wall_thickness_measured": "N",
"roof_insulation_location": 2,
"roof_insulation_thickness": "200mm",
"wall_insulation_thickness": "NI",
"floor_insulation_thickness": "NI"
}
],
"open_chimneys_count": 1,
"solar_water_heating": "N",
"habitable_room_count": 5,
"heating_cost_current": 365.98,
"insulated_door_count": 2,
"co2_emissions_current": 2.4,
"energy_rating_average": 60,
"energy_rating_current": 50,
"lighting_cost_current": 123.45,
"main_heating_controls": [
{"description": "Programmer, room thermostat and TRVs", "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4},
{"description": "Time and temperature zone control", "energy_efficiency_rating": 5, "environmental_efficiency_rating": 5}
],
"has_hot_water_cylinder": "true",
"heating_cost_potential": 250.34,
"hot_water_cost_current": 200.4,
"insulated_door_u_value": 3,
"mechanical_ventilation": 6,
"percent_draughtproofed": 100,
"suggested_improvements": [
{
"sequence": 1,
"typical_saving": 360,
"indicative_cost": "£100 - £350",
"improvement_type": "Z3",
"improvement_details": {"improvement_number": 5},
"improvement_category": 6,
"energy_performance_rating": 50,
"environmental_impact_rating": 50
},
{
"sequence": 3,
"typical_saving": 99,
"indicative_cost": 1000,
"improvement_type": "Z2",
"improvement_details": {
"improvement_texts": {"improvement_description": "Improvement desc"}
},
"improvement_category": 2,
"energy_performance_rating": 60,
"environmental_impact_rating": 64
}
],
"co2_emissions_potential": 1.4,
"energy_rating_potential": 72,
"lighting_cost_potential": 84.23,
"schema_version_original": "SAP-19.0",
"hot_water_cost_potential": 180.43,
"renewable_heat_incentive": {
"water_heating": 2285,
"impact_of_loft_insulation": -2114,
"impact_of_cavity_insulation": -122,
"impact_of_solid_wall_insulation": -3560,
"space_heating_existing_dwelling": 13120
},
"draughtproofed_door_count": 1,
"mechanical_vent_duct_type": 3,
"energy_consumption_current": 230,
"has_fixed_air_conditioning": "false",
"multiple_glazed_proportion": 100,
"calculation_software_version": "13.05r16",
"energy_consumption_potential": 88,
"environmental_impact_current": 52,
"windows_transmission_details": {"u_value": 2, "data_source": 2, "solar_transmittance": 0.72},
"cfl_fixed_lighting_bulbs_count": 5,
"current_energy_efficiency_band": "E",
"environmental_impact_potential": 74,
"led_fixed_lighting_bulbs_count": 10,
"mechanical_vent_duct_placement": 2,
"mechanical_vent_duct_insulation": 2,
"potential_energy_efficiency_band": "C",
"pressure_test_certificate_number": 0,
"mechanical_ventilation_index_number": 12,
"co2_emissions_current_per_floor_area": 20,
"low_energy_fixed_lighting_bulbs_count": 16,
"mechanical_vent_duct_insulation_level": 2,
"mechanical_vent_measured_installation": "false",
"incandescent_fixed_lighting_bulbs_count": 5
}

222
domain/epc/tests/fixtures/21_0_1.json vendored Normal file
View file

@ -0,0 +1,222 @@
{
"uprn": 12457,
"roofs": [
{"description": {"value": "Pitched, 25 mm loft insulation", "language": "1"}, "energy_efficiency_rating": 2, "environmental_efficiency_rating": 2},
{"description": {"value": "Pitched, 250 mm loft insulation", "language": "1"}, "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4}
],
"walls": [
{"description": {"value": "Solid brick, as built, no insulation (assumed)", "language": "1"}, "energy_efficiency_rating": 1, "environmental_efficiency_rating": 1},
{"description": {"value": "Cavity wall, as built, insulated (assumed)", "language": "1"}, "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4}
],
"floors": [
{"description": {"value": "Suspended, no insulation (assumed)", "language": "1"}, "energy_efficiency_rating": 0, "environmental_efficiency_rating": 0}
],
"status": "entered",
"tenure": 1,
"window": {"description": {"value": "Fully double glazed", "language": "1"}, "energy_efficiency_rating": 3, "environmental_efficiency_rating": 3},
"addendum": {"stone_walls": "true", "system_build": "true", "addendum_numbers": [1, 13]},
"lighting": {"description": {"value": "Low energy lighting in 50% of fixed outlets", "language": "1"}, "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4},
"postcode": "A0 0AA",
"hot_water": {"description": {"value": "From main system", "language": "1"}, "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4},
"post_town": "Whitbury",
"built_form": 2,
"door_count": 3,
"region_code": 1,
"report_type": 2,
"sap_heating": {
"cylinder_size": 1,
"shower_outlets": {"shower_outlet": {"shower_wwhrs": 1, "shower_outlet_type": 1}},
"water_heating_code": 901,
"water_heating_fuel": 26,
"instantaneous_wwhrs": {"wwhrs_index_number1": 1, "wwhrs_index_number2": 2},
"secondary_fuel_type": 25,
"main_heating_details": [
{
"has_fghrs": "N",
"main_fuel_type": 26,
"boiler_flue_type": 2,
"fan_flue_present": "N",
"heat_emitter_type": 1,
"emitter_temperature": 0,
"main_heating_number": 1,
"boiler_ignition_type": 1,
"main_heating_control": 2106,
"main_heating_category": 2,
"main_heating_fraction": 1,
"sap_main_heating_code": 101,
"central_heating_pump_age": 0,
"main_heating_data_source": 1,
"main_heating_index_number": 17507
}
],
"immersion_heating_type": "NA",
"has_fixed_air_conditioning": "false"
},
"sap_version": 10.2,
"sap_windows": [
{
"pvc_frame": "false",
"glazing_gap": 6,
"orientation": 1,
"window_type": 2,
"frame_factor": 1.0,
"glazing_type": 14,
"window_width": 1.2,
"window_height": 2.0,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 6,
"permanent_shutters_present": "N",
"window_transmission_details": {"u_value": 1.0, "data_source": 2, "solar_transmittance": 1.0},
"permanent_shutters_insulated": "N"
}
],
"schema_type": "RdSAP-Schema-21.0.1",
"uprn_source": "Energy Assessor",
"country_code": "ENG",
"main_heating": [
{"description": {"value": "Boiler and radiators, anthracite", "language": "1"}, "energy_efficiency_rating": 3, "environmental_efficiency_rating": 1},
{"description": {"value": "Boiler and radiators, mains gas", "language": "1"}, "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4}
],
"dwelling_type": "Mid-terrace house",
"language_code": 1,
"pressure_test": 6,
"property_type": 0,
"address_line_1": "1 Some Street",
"assessment_type": "RdSAP",
"completion_date": "2025-04-04",
"inspection_date": "2025-04-04",
"wet_rooms_count": 0,
"extensions_count": 0,
"measurement_type": 1,
"sap_flat_details": {
"level": 1,
"top_storey": "N",
"storey_count": 3,
"flat_location": 1,
"heat_loss_corridor": 2,
"unheated_corridor_length": {"value": 10, "quantity": "metres"}
},
"total_floor_area": 55,
"transaction_type": 16,
"conservatory_type": 1,
"heated_room_count": 5,
"registration_date": "2025-04-04",
"sap_energy_source": {
"mains_gas": "Y",
"meter_type": 2,
"pv_batteries": {"pv_battery": {"battery_capacity": 5}},
"pv_connection": 0,
"pv_battery_count": 1,
"photovoltaic_supply": {"none_or_no_details": {"percent_roof_area": 0}},
"wind_turbines_count": 0,
"wind_turbine_details": {"hub_height": 0, "rotor_diameter": 0},
"gas_smart_meter_present": "false",
"is_dwelling_export_capable": "false",
"wind_turbines_terrain_type": 4,
"electricity_smart_meter_present": "true"
},
"secondary_heating": {
"description": {"value": "Room heaters, electric", "language": "1"},
"energy_efficiency_rating": 0,
"environmental_efficiency_rating": 0
},
"sap_building_parts": [
{
"identifier": "Main Dwelling",
"wall_dry_lined": "N",
"floor_heat_loss": 7,
"sap_room_in_roof": {"floor_area": 100, "construction_age_band": "B"},
"roof_construction": 4,
"wall_construction": 4,
"building_part_number": 1,
"sap_floor_dimensions": [
{
"floor": 0,
"room_height": {"value": 2.45, "quantity": "metres"},
"floor_insulation": 1,
"total_floor_area": {"value": 45.82, "quantity": "square metres"},
"party_wall_length": {"value": 7.9, "quantity": "metres"},
"floor_construction": 1,
"heat_loss_perimeter": {"value": 19.5, "quantity": "metres"}
}
],
"wall_insulation_type": 2,
"construction_age_band": "M",
"sap_alternative_wall_1": {"wall_area": 10.4, "wall_dry_lined": "N", "wall_construction": 4, "wall_insulation_type": 2, "wall_thickness_measured": "N"},
"sap_alternative_wall_2": {"wall_area": 10.8, "wall_dry_lined": "N", "wall_construction": 4, "wall_insulation_type": 2, "wall_thickness_measured": "N"},
"party_wall_construction": 0,
"wall_thickness_measured": "N",
"roof_insulation_location": 2,
"roof_insulation_thickness": "200mm",
"wall_insulation_thickness": "NI",
"floor_insulation_thickness": "NI"
}
],
"open_chimneys_count": 1,
"solar_water_heating": "N",
"habitable_room_count": 5,
"heating_cost_current": 365.98,
"insulated_door_count": 2,
"co2_emissions_current": 2.4,
"energy_rating_average": 60,
"energy_rating_current": 50,
"lighting_cost_current": 123.45,
"main_heating_controls": [
{"description": {"value": "Programmer, room thermostat and TRVs", "language": "1"}, "energy_efficiency_rating": 4, "environmental_efficiency_rating": 4},
{"description": {"value": "Time and temperature zone control", "language": "1"}, "energy_efficiency_rating": 5, "environmental_efficiency_rating": 5}
],
"has_hot_water_cylinder": "true",
"heating_cost_potential": 250.34,
"hot_water_cost_current": 200.4,
"insulated_door_u_value": 3,
"mechanical_ventilation": 6,
"percent_draughtproofed": 100,
"suggested_improvements": [
{
"sequence": 1,
"typical_saving": 139,
"indicative_cost": "£220 - £250",
"improvement_type": "G",
"improvement_details": {"improvement_number": 66},
"improvement_category": 5,
"energy_performance_rating": 70,
"environmental_impact_rating": 70
}
],
"co2_emissions_potential": 1.4,
"energy_rating_potential": 72,
"lighting_cost_potential": 84.23,
"schema_version_original": "SAP-19.0",
"hot_water_cost_potential": 180.43,
"renewable_heat_incentive": {
"water_heating": 2285,
"impact_of_loft_insulation": -2114,
"impact_of_cavity_insulation": -122,
"impact_of_solid_wall_insulation": -3560,
"space_heating_existing_dwelling": 13120
},
"draughtproofed_door_count": 1,
"mechanical_vent_duct_type": 3,
"energy_consumption_current": 230,
"has_fixed_air_conditioning": "false",
"multiple_glazed_proportion": 100,
"calculation_software_version": "13.05r16",
"energy_consumption_potential": 88,
"environmental_impact_current": 52,
"windows_transmission_details": {"u_value": 2, "data_source": 2, "solar_transmittance": 0.72},
"cfl_fixed_lighting_bulbs_count": 5,
"current_energy_efficiency_band": "E",
"environmental_impact_potential": 74,
"led_fixed_lighting_bulbs_count": 10,
"mechanical_vent_duct_placement": 2,
"mechanical_vent_duct_insulation": 2,
"potential_energy_efficiency_band": "C",
"pressure_test_certificate_number": 0,
"mechanical_ventilation_index_number": 12,
"co2_emissions_current_per_floor_area": 20,
"low_energy_fixed_lighting_bulbs_count": 16,
"mechanical_vent_duct_insulation_level": 2,
"mechanical_vent_measured_installation": "false",
"incandescent_fixed_lighting_bulbs_count": 0
}

View file

@ -0,0 +1,232 @@
{
"inspection_metadata": {
"inspection_surveyor": "test",
"email_address": "test@test.com",
"report_reference": "49D422A9-0779-44DD-9665-464D35DFF1A8",
"created_on": "2026-03-31",
"date_of_inspection": "2026-03-31",
"property_address": "1, Test Street, Test Town, Test County, TE1 1ST"
},
"general": {
"epc_checked_before_assessment": true,
"epc_exists_at_point_of_assessment": false,
"inspection_date": "2026-03-31",
"transaction_type": "None of the Above",
"tenure": "Rented Social",
"property_type": "House",
"detachment_type": "Mid-terrace",
"number_of_storeys": 2,
"terrain_type": "Suburban",
"number_of_extensions": 0,
"electricity_smart_meter": true,
"electric_meter_type": "Single",
"dwelling_export_capable": true,
"mains_gas_available": true,
"gas_smart_meter": true,
"gas_meter_accessible": true,
"measurements_location": "Internal"
},
"building_construction": {
"main_building": {
"age_range": "I: 1996 - 2002",
"age_indicators": "local knowledge",
"walls_construction_type": "Cavity",
"cavity_construction_indicators": "stretcher bond",
"walls_insulation_type": "As built",
"thermal_conductivity_of_wall_insulation": "Unknown",
"wall_u_value_known": false,
"wall_thickness_mm": 280,
"party_wall_construction_type": "Cavity Masonry, Unfilled"
},
"floor": {
"floor_type": "Ground Floor",
"floor_construction": "Suspended, not timber",
"floor_insulation_type": "As Built",
"floor_u_value_known": false
}
},
"building_measurements": {
"main_building": {
"floors": [
{
"name": "Floor 1",
"area_m2": 24.78,
"height_m": 2.37,
"heat_loss_perimeter_m": 14.21,
"pwl_m": 6.15
},
{
"name": "Floor 0",
"area_m2": 24.78,
"height_m": 2.35,
"heat_loss_perimeter_m": 14.21,
"pwl_m": 6.15
}
]
}
},
"roof_space": {
"main_building": {
"construction_type": "Pitched roof (Slates or tiles), Access to loft",
"insulation_at": "Joists",
"roof_u_value_known": false,
"insulation_thickness_mm": 100,
"cavity_wall_construction_indicators": "No indicator of construction visible",
"rooms_in_roof": false
}
},
"windows": [
{
"id": 1,
"location": "Main Building",
"wall_type": "External wall",
"glazing_type": "Double glazing, Unknown install date",
"window_type": "Window",
"frame_type": "Wooden or PVC",
"glazing_gap": "16 mm or more",
"draught_proofed": true,
"permanent_shutters": false,
"height_m": 1.36,
"width_m": 1.0,
"orientation": "South East"
},
{
"id": 2,
"location": "Main Building",
"wall_type": "External wall",
"glazing_type": "Double glazing, Unknown install date",
"window_type": "Window",
"frame_type": "Wooden or PVC",
"glazing_gap": "16 mm or more",
"draught_proofed": true,
"permanent_shutters": false,
"height_m": 1.33,
"width_m": 0.96,
"orientation": "South East"
},
{
"id": 3,
"location": "Main Building",
"wall_type": "External wall",
"glazing_type": "Double glazing, Unknown install date",
"window_type": "Window",
"frame_type": "Wooden or PVC",
"glazing_gap": "16 mm or more",
"draught_proofed": true,
"permanent_shutters": false,
"height_m": 1.04,
"width_m": 0.96,
"orientation": "North West"
},
{
"id": 4,
"location": "Main Building",
"wall_type": "External wall",
"glazing_type": "Double glazing, Unknown install date",
"window_type": "Window",
"frame_type": "Wooden or PVC",
"glazing_gap": "16 mm or more",
"draught_proofed": true,
"permanent_shutters": false,
"height_m": 1.02,
"width_m": 0.97,
"orientation": "North West"
}
],
"heating_and_hot_water": {
"main_heating": {
"selection_method": "PCDF Search",
"system_type": "Boiler with radiators or underfloor heating",
"product_id": 18400,
"manufacturer": "Vaillant",
"model": "ecoFIT sustain 415",
"orig_manufacturer": "Vaillant",
"fuel": "Mains gas",
"summer_efficiency": 0,
"type": "Regular",
"condensing": true,
"year": "2018 - current",
"mount": "Wall",
"open_flue": "Room-sealed",
"fan_assist": true,
"status": "Normal status for an actual product",
"central_heating_pump_age": "Unknown",
"controls": "Programmer, room thermostat and TRVs",
"flue_gas_heat_recovery_system": false,
"weather_compensator": false,
"emitter": "Radiators",
"emitter_temperature": "Unknown"
},
"secondary_heating": {
"secondary_fuel": "No Secondary Heating"
},
"water_heating": {
"type": "Regular",
"system": "From main heating 1",
"cylinder_size": "Normal (90-130 litres)",
"cylinder_measured_heat_loss": "Not known",
"insulation_type": "Factory fitted",
"insulation_thickness_mm": 12,
"has_thermostat": true
}
},
"ventilation": {
"ventilation_type": "Natural",
"has_fixed_air_conditioning": false,
"number_of_open_flues": 0,
"number_of_closed_flues": 0,
"number_of_boiler_flues": 0,
"number_of_other_flues": 0,
"number_of_extract_fans": 2,
"number_of_passive_vents": 0,
"number_of_flueless_gas_fires": 0,
"pressure_test": "No test",
"draught_lobby": false
},
"conservatories": {
"has_conservatory": false
},
"renewables": {
"wind_turbines": false,
"solar_hot_water": false,
"photovoltaic_array": false,
"number_of_pv_batteries": 0,
"hydro": false
},
"room_count_elements": {
"number_of_habitable_rooms": 2,
"any_unheated_rooms": true,
"number_of_heated_rooms": 0,
"number_of_external_doors": 2,
"number_of_insulated_external_doors": 0,
"number_of_draughtproofed_external_doors": 2,
"number_of_open_chimneys": 0,
"number_of_blocked_chimneys": 0,
"number_of_fixed_incandescent_bulbs": 0,
"exact_led_cfl_count_known": true,
"number_of_fixed_led_bulbs": 5,
"number_of_fixed_cfl_bulbs": 4,
"waste_water_heat_recovery": "None"
},
"water_use": {
"number_of_baths": 1,
"number_of_special_features": 0,
"showers": [
{
"id": 1,
"outlet_type": "Non-Electric Shower"
}
]
},
"customer_response": {
"customer_present": true,
"willing_to_answer_satisfaction_survey": false
},
"addendum": {
"addendum": "PV Recommended",
"related_party_disclosure": "No related party",
"hard_to_treat_cavity_access_issues": false,
"hard_to_treat_cavity_high_exposure": false,
"hard_to_treat_cavity_narrow_cavities": false
}
}

View file

@ -0,0 +1,330 @@
{
"inspection_metadata": {
"inspection_surveyor": "test",
"email_address": "test@test.com",
"report_reference": "6EA2A86D-94CE-4792-8D49-AB495C744EDD",
"created_on": "2025-11-10",
"date_of_inspection": "2025-09-25",
"property_address": "test",
"property_photo": true
},
"general": {
"epc_checked_before_assessment": true,
"epc_exists_at_point_of_assessment": false,
"inspection_date": "2025-09-25",
"transaction_type": "Grant-Scheme (ECO, RHI, etc.)",
"tenure": "Rented Social",
"property_type": "House",
"detachment_type": "Mid-terrace",
"number_of_storeys": 2,
"terrain_type": "Suburban",
"number_of_extensions": 1,
"electricity_smart_meter": true,
"electric_meter_type": "Single",
"dwelling_export_capable": true,
"mains_gas_available": true,
"gas_smart_meter": true,
"gas_meter_accessible": true,
"measurements_location": "Internal"
},
"building_construction": {
"main_building": {
"age_range": "1950-1966",
"age_indicators": "local knowledge, enquiries of owner",
"walls_construction_type": "Cavity",
"cavity_construction_indicators": "wall thickness over 270 mm",
"walls_insulation_type": "Filled Cavity",
"filled_cavity_indicators": "evidence of cavity fill drill holes",
"thermal_conductivity_of_wall_insulation": "Unknown",
"wall_u_value_known": false,
"wall_thickness_mm": 310,
"party_wall_construction_type": "Cavity Masonry, Filled"
},
"extensions": [
{
"id": 1,
"age_range": "2003-2006",
"age_indicators": "local knowledge, enquiries of owner",
"walls_construction_type": "Cavity",
"cavity_construction_indicators": "wall thickness over 270 mm",
"walls_insulation_type": "As built",
"thermal_conductivity_of_wall_insulation": "Unknown",
"wall_u_value_known": false,
"wall_thickness_mm": 310,
"party_wall_construction_type": "Cavity Masonry, Filled"
}
],
"floor": {
"floor_type": "Ground Floor",
"floor_construction": "Solid",
"floor_insulation_type": "As Built",
"floor_u_value_known": false
}
},
"building_measurements": {
"main_building": {
"floors": [
{
"name": "Floor 1",
"area_m2": 35.68,
"height_m": 2.19,
"heat_loss_perimeter_m": 13.44,
"pwl_m": 10.62
},
{
"name": "Floor 0",
"area_m2": 35.68,
"height_m": 2.17,
"heat_loss_perimeter_m": 11.0,
"pwl_m": 10.62
}
]
},
"extensions": [
{
"id": 1,
"floors": [
{
"name": "Floor 0",
"area_m2": 3.8,
"height_m": 2.0,
"heat_loss_perimeter_m": 5.7,
"pwl_m": 0.0
}
]
}
]
},
"roof_space": {
"main_building": {
"construction_type": "Pitched roof (Slates or tiles), Access to loft",
"insulation_at": "Joists",
"roof_u_value_known": false,
"insulation_thickness_mm": 100,
"cavity_wall_construction_indicators": "cavity visible in roof space",
"rooms_in_roof": false
},
"extensions": [
{
"id": 1,
"construction_type": "Pitched roof, Sloping ceiling",
"insulation_at": "Sloping ceiling insulation",
"roof_u_value_known": false,
"insulation_thickness": "As built",
"cavity_wall_construction_indicators": "No indicator of construction visible",
"rooms_in_roof": false
}
]
},
"windows": [
{
"id": 1,
"location": "Main Building",
"wall_type": "External wall",
"glazing_type": "Double glazing, Unknown install date",
"window_type": "Window",
"frame_type": "Wooden or PVC",
"glazing_gap": "16 mm or more",
"draught_proofed": true,
"permanent_shutters": false,
"height_m": 1.2,
"width_m": 2.3,
"orientation": "North West"
},
{
"id": 2,
"location": "Main Building",
"wall_type": "External wall",
"glazing_type": "Double glazing, Unknown install date",
"window_type": "Window",
"frame_type": "Wooden or PVC",
"glazing_gap": "16 mm or more",
"draught_proofed": true,
"permanent_shutters": false,
"height_m": 1.2,
"width_m": 1.0,
"orientation": "North West"
},
{
"id": 3,
"location": "Main Building",
"wall_type": "External wall",
"glazing_type": "Double glazing, Unknown install date",
"window_type": "Window",
"frame_type": "Wooden or PVC",
"glazing_gap": "16 mm or more",
"draught_proofed": true,
"permanent_shutters": false,
"height_m": 0.9,
"width_m": 1.0,
"orientation": "North East"
},
{
"id": 4,
"location": "Extension 1",
"wall_type": "External wall",
"glazing_type": "Double glazing, Unknown install date",
"window_type": "Window",
"frame_type": "Wooden or PVC",
"glazing_gap": "16 mm or more",
"draught_proofed": true,
"permanent_shutters": false,
"height_m": 0.9,
"width_m": 1.0,
"orientation": "North"
},
{
"id": 5,
"location": "Extension 1",
"wall_type": "External wall",
"glazing_type": "Double glazing, Unknown install date",
"window_type": "Window",
"frame_type": "Wooden or PVC",
"glazing_gap": "16 mm or more",
"draught_proofed": true,
"permanent_shutters": false,
"height_m": 0.9,
"width_m": 1.7,
"orientation": "North East"
},
{
"id": 6,
"location": "Extension 1",
"wall_type": "External wall",
"glazing_type": "Double glazing, Unknown install date",
"window_type": "Window",
"frame_type": "Wooden or PVC",
"glazing_gap": "16 mm or more",
"draught_proofed": true,
"permanent_shutters": false,
"height_m": 0.9,
"width_m": 2.3,
"orientation": "North West"
},
{
"id": 7,
"location": "Extension 1",
"wall_type": "External wall",
"glazing_type": "Double glazing, Unknown install date",
"window_type": "Window",
"frame_type": "Wooden or PVC",
"glazing_gap": "16 mm or more",
"draught_proofed": true,
"permanent_shutters": false,
"height_m": 1.0,
"width_m": 1.2,
"orientation": "North West"
},
{
"id": 8,
"location": "Extension 1",
"wall_type": "External wall",
"glazing_type": "Double glazing, Unknown install date",
"window_type": "Window",
"frame_type": "Wooden or PVC",
"glazing_gap": "16 mm or more",
"draught_proofed": true,
"permanent_shutters": false,
"height_m": 0.9,
"width_m": 1.0,
"orientation": "North East"
}
],
"heating_and_hot_water": {
"main_heating": {
"selection_method": "PCDF Search",
"system_type": "Boiler with radiators or underfloor heating",
"product_id": 16839,
"manufacturer": "Vaillant",
"model": "ecoTEC pro 28",
"orig_manufacturer": "Vaillant",
"fuel": "Mains gas",
"summer_efficiency": 0,
"type": "Combi",
"condensing": true,
"year": "2005 - 2015",
"mount": "Wall",
"open_flue": "Room-sealed",
"fan_assist": true,
"status": "Normal status for an actual product",
"central_heating_pump_age": "Unknown",
"controls": "Programmer, room thermostat and TRVs",
"flue_gas_heat_recovery_system": false,
"weather_compensator": false,
"emitter": "Radiators",
"emitter_temperature": "Unknown"
},
"secondary_heating": {
"secondary_fuel": "No Secondary Heating"
},
"water_heating": {
"type": "Regular",
"system": "From main heating 1",
"cylinder_size": "No Cylinder",
"cylinder_measured_heat_loss": null,
"insulation_type": null,
"insulation_thickness_mm": null,
"has_thermostat": null
}
},
"ventilation": {
"ventilation_type": "Mechanical Extract - Decentralised",
"ventilation_in_pcdf_database": false,
"has_fixed_air_conditioning": false,
"number_of_open_flues": 0,
"number_of_closed_flues": 0,
"number_of_boiler_flues": 0,
"number_of_other_flues": 0,
"number_of_extract_fans": 0,
"number_of_passive_vents": 0,
"number_of_flueless_gas_fires": 0,
"pressure_test": "No test",
"draught_lobby": false
},
"conservatories": {
"has_conservatory": false
},
"renewables": {
"wind_turbines": false,
"solar_hot_water": false,
"photovoltaic_array": false,
"number_of_pv_batteries": 0,
"hydro": false
},
"room_count_elements": {
"number_of_habitable_rooms": 3,
"any_unheated_rooms": false,
"number_of_heated_rooms": null,
"number_of_external_doors": 2,
"number_of_insulated_external_doors": 0,
"number_of_draughtproofed_external_doors": 2,
"number_of_open_chimneys": 0,
"number_of_blocked_chimneys": 0,
"number_of_fixed_incandescent_bulbs": 4,
"exact_led_cfl_count_known": true,
"number_of_fixed_led_bulbs": 0,
"number_of_fixed_cfl_bulbs": 1,
"waste_water_heat_recovery": "None"
},
"water_use": {
"number_of_baths": 1,
"number_of_special_features": 0,
"showers": [
{
"id": 1,
"outlet_type": "Non-Electric Shower"
}
]
},
"customer_response": {
"customer_present": true,
"willing_to_answer_satisfaction_survey": false
},
"addendum": {
"addendum": "None",
"related_party_disclosure": "No related party",
"hard_to_treat_cavity_access_issues": false,
"hard_to_treat_cavity_high_exposure": false,
"hard_to_treat_cavity_narrow_cavities": false
}
}

View file

@ -0,0 +1,190 @@
{
"inspection_metadata": {
"inspection_surveyor": "test",
"email_address": "test@test.com",
"report_reference": "49D422A9-0779-44DD-9665-464D35DFF1A8",
"created_on": "2026-03-31",
"date_of_inspection": "2026-03-31",
"property_address": "1, Test Street, Test Town, Test County, TE1 1ST"
},
"general": {
"epc_checked_before_assessment": true,
"epc_exists_at_point_of_assessment": false,
"inspection_date": "2026-03-31",
"transaction_type": "None of the Above",
"tenure": "Rented Social",
"property_type": "House",
"detachment_type": "Mid-terrace",
"number_of_storeys": 2,
"terrain_type": "Suburban",
"number_of_extensions": 0,
"electricity_smart_meter": true,
"electric_meter_type": "Single",
"dwelling_export_capable": true,
"mains_gas_available": true,
"gas_smart_meter": true,
"gas_meter_accessible": true,
"measurements_location": "Internal"
},
"building_construction": {
"main_building": {
"age_range": "I: 1996 - 2002",
"age_indicators": "local knowledge",
"walls_construction_type": "Cavity",
"cavity_construction_indicators": "stretcher bond",
"walls_insulation_type": "As built",
"thermal_conductivity_of_wall_insulation": "Unknown",
"wall_u_value_known": false,
"wall_thickness_mm": null,
"party_wall_construction_type": "Cavity Masonry, Unfilled"
},
"floor": {
"floor_type": "Ground Floor",
"floor_construction": "Suspended, not timber",
"floor_insulation_type": "As Built",
"floor_u_value_known": false
}
},
"building_measurements": {
"main_building": {
"floors": [
{
"name": "Floor 1",
"area_m2": 24.78,
"height_m": 2.37,
"heat_loss_perimeter_m": 14.21,
"pwl_m": 6.15
},
{
"name": "Floor 0",
"area_m2": 24.78,
"height_m": 2.35,
"heat_loss_perimeter_m": 14.21,
"pwl_m": 6.15
}
]
}
},
"roof_space": {
"main_building": {
"construction_type": "Pitched roof (Slates or tiles), Access to loft",
"insulation_at": "Joists",
"roof_u_value_known": false,
"insulation_thickness_mm": 100,
"cavity_wall_construction_indicators": "No indicator of construction visible",
"rooms_in_roof": false
}
},
"windows": [
{
"id": 1,
"location": "Main Building",
"wall_type": "External wall",
"glazing_type": "Double glazing, Unknown install date",
"window_type": "Window",
"frame_type": "Wooden or PVC",
"glazing_gap": "16 mm or more",
"draught_proofed": true,
"permanent_shutters": false,
"height_m": 1.36,
"width_m": 1.0,
"orientation": "South East"
}
],
"heating_and_hot_water": {
"main_heating": {
"selection_method": "PCDF Search",
"system_type": "Boiler with radiators or underfloor heating",
"product_id": 18400,
"manufacturer": "Vaillant",
"model": "ecoFIT sustain 415",
"orig_manufacturer": "Vaillant",
"fuel": "Mains gas",
"summer_efficiency": 0,
"type": "Regular",
"condensing": true,
"year": "2018 - current",
"mount": "Wall",
"open_flue": "Room-sealed",
"fan_assist": true,
"status": "Normal status for an actual product",
"central_heating_pump_age": "Unknown",
"controls": "Programmer, room thermostat and TRVs",
"flue_gas_heat_recovery_system": false,
"weather_compensator": false,
"emitter": "Radiators",
"emitter_temperature": "Unknown"
},
"secondary_heating": {
"secondary_fuel": "No Secondary Heating"
},
"water_heating": {
"type": "Regular",
"system": "From main heating 1",
"cylinder_size": "Normal (90-130 litres)",
"cylinder_measured_heat_loss": "Not known",
"insulation_type": "Factory fitted",
"insulation_thickness_mm": 12,
"has_thermostat": true
}
},
"ventilation": {
"ventilation_type": "Natural",
"has_fixed_air_conditioning": false,
"number_of_open_flues": 0,
"number_of_closed_flues": 0,
"number_of_boiler_flues": 0,
"number_of_other_flues": 0,
"number_of_extract_fans": 2,
"number_of_passive_vents": 0,
"number_of_flueless_gas_fires": 0,
"pressure_test": "No test",
"draught_lobby": false
},
"conservatories": {
"has_conservatory": false
},
"renewables": {
"wind_turbines": false,
"solar_hot_water": false,
"photovoltaic_array": false,
"number_of_pv_batteries": 0,
"hydro": false
},
"room_count_elements": {
"number_of_habitable_rooms": 2,
"any_unheated_rooms": true,
"number_of_heated_rooms": 0,
"number_of_external_doors": 2,
"number_of_insulated_external_doors": 0,
"number_of_draughtproofed_external_doors": 2,
"number_of_open_chimneys": 0,
"number_of_blocked_chimneys": 0,
"number_of_fixed_incandescent_bulbs": 0,
"exact_led_cfl_count_known": true,
"number_of_fixed_led_bulbs": 5,
"number_of_fixed_cfl_bulbs": 4,
"waste_water_heat_recovery": "None"
},
"water_use": {
"number_of_baths": 1,
"number_of_special_features": 0,
"showers": [
{
"id": 1,
"outlet_type": "Non-Electric Shower"
}
]
},
"customer_response": {
"customer_present": true,
"willing_to_answer_satisfaction_survey": false
},
"addendum": {
"addendum": "None",
"related_party_disclosure": "No related party",
"hard_to_treat_cavity_access_issues": false,
"hard_to_treat_cavity_high_exposure": false,
"hard_to_treat_cavity_narrow_cavities": false
}
}

View file

@ -5,8 +5,8 @@ from typing import Any, Dict
import pytest
from datatypes.epc.domain.epc_property_data import EpcPropertyData
from datatypes.epc.domain.mapper import EpcPropertyDataMapper
from domain.epc.epc_property_data import EpcPropertyData
from domain.epc.mapper import EpcPropertyDataMapper
from datatypes.epc.schema.rdsap_schema_17_0 import RdSapSchema17_0
from datatypes.epc.schema.rdsap_schema_17_1 import RdSapSchema17_1
from datatypes.epc.schema.rdsap_schema_18_0 import RdSapSchema18_0
@ -16,7 +16,7 @@ from datatypes.epc.schema.rdsap_schema_21_0_0 import RdSapSchema21_0_0
from datatypes.epc.schema.rdsap_schema_21_0_1 import RdSapSchema21_0_1
from datatypes.epc.schema.tests.helpers import from_dict
FIXTURES = os.path.join(os.path.dirname(__file__), "../../schema/tests/fixtures")
FIXTURES = os.path.join(os.path.dirname(__file__), "fixtures")
def load(filename: str) -> Dict[str, Any]:

View file

@ -5,7 +5,7 @@ from typing import Any, Dict
import pytest
from datatypes.epc.domain.epc_property_data import (
from domain.epc.epc_property_data import (
EpcPropertyData,
InstantaneousWwhrs,
MainHeatingDetail,
@ -18,14 +18,11 @@ from datatypes.epc.domain.epc_property_data import (
ShowerOutlet,
ShowerOutlets,
)
from datatypes.epc.domain.mapper import EpcPropertyDataMapper
from domain.epc.mapper import EpcPropertyDataMapper
from datatypes.epc.schema.tests.helpers import from_dict
from datatypes.epc.surveys.pashub_rdsap_site_notes import PasHubRdSapSiteNotes
FIXTURES = os.path.join(
os.path.dirname(__file__),
"../../surveys/tests/fixtures",
)
FIXTURES = os.path.join(os.path.dirname(__file__), "fixtures")
def load(filename: str) -> Dict[str, Any]:

View file

@ -5,8 +5,8 @@ import pandas as pd
import pytest
from botocore.exceptions import ClientError
from datatypes.epc.domain import historic_epc_matching as matcher_mod
from datatypes.epc.domain.historic_epc_matching import (
from domain.epc import historic_epc_matching as matcher_mod
from domain.epc.historic_epc_matching import (
HistoricEpcMatches,
ScoredHistoricEpc,
_sanitise_postcode,

View file

@ -2,7 +2,7 @@ from typing import Optional
import datatypes.magicplan.api.response as api
from datatypes.magicplan.api.response import MagicPlanPlan
from datatypes.magicplan.domain.models import Plan, Floor, Room, Window, Door
from domain.magicplan.models import Plan, Floor, Room, Window, Door
def map_plan(mp: MagicPlanPlan) -> Plan:

View file

@ -5,10 +5,10 @@ from typing import Any
import pytest
from datatypes.magicplan.api.response import MagicPlanPlan
from datatypes.magicplan.domain.mapper import map_plan
from datatypes.magicplan.domain.models import Plan
from domain.magicplan.mapper import map_plan
from domain.magicplan.models import Plan
FIXTURE_DIR = Path(__file__).parents[4] / "backend" / "magic_plan"
FIXTURE_DIR = Path(__file__).parents[3] / "backend" / "magic_plan"
PLAN_ID = "a7285ed1-878d-47eb-8aa6-85ef9e187516"
PLAN_ID_2 = "9f9889ff-793e-4e9a-a6f0-e22f5b0f5365"

View file

@ -12,7 +12,7 @@ from backend.app.db.models.recommendations import (
from backend.app.db.models.portfolio import PropertyModel, PropertyDetailsEpcModel
from backend.app.utils import sap_to_epc
from typing import Dict, List, Set
from datatypes.epc.domain.epc import Epc
from domain.epc.epc import Epc
from recommendations.Costs import Costs
pd.set_option("display.max_rows", 500)

View file

@ -3,6 +3,6 @@ pythonpath = .
log_cli = true
log_cli_level = INFO
addopts = --cov-report term-missing --cov=etl/epc --cov=recommendations --cov=backend --cov=etl/epc_clean --cov=etl/spatial
testpaths = recommendations/tests backend/tests etl/epc/tests etl/epc_clean/tests etl/spatial/tests backend/condition/tests backend/address2UPRN/tests backend/onboarders/tests backend/categorisation/tests backend/export/tests etl/hubspot/tests backend/hubspot_trigger_orchestrator/tests datatypes/epc/schema/tests datatypes/epc/surveys/tests datatypes/epc/domain/tests backend/ecmk_fetcher/tests/ backend/pashub_fetcher/tests backend/documents_parser/tests backend/magic_plan/tests datatypes/magicplan/api/tests datatypes/magicplan/domain/tests backend/app/db/functions/tests
testpaths = recommendations/tests backend/tests etl/epc/tests etl/epc_clean/tests etl/spatial/tests backend/condition/tests backend/address2UPRN/tests backend/onboarders/tests backend/categorisation/tests backend/export/tests etl/hubspot/tests backend/hubspot_trigger_orchestrator/tests datatypes/epc/schema/tests datatypes/epc/surveys/tests domain/epc/tests backend/ecmk_fetcher/tests/ backend/pashub_fetcher/tests backend/documents_parser/tests backend/magic_plan/tests datatypes/magicplan/api/tests domain/magicplan/tests backend/app/db/functions/tests
markers =
integration: mark a test as an integration test

View file

@ -11,7 +11,7 @@ Usage:
import sys
from datatypes.epc.domain.historic_epc_matching import match_addresses_for_postcode
from domain.epc.historic_epc_matching import match_addresses_for_postcode
def main(user_address: str, postcode: str) -> None: