diff --git a/backend/app/db/models/recommendations.py b/backend/app/db/models/recommendations.py index 096cc1de..653e7051 100644 --- a/backend/app/db/models/recommendations.py +++ b/backend/app/db/models/recommendations.py @@ -1,13 +1,23 @@ +"""Re-export shim + remaining legacy models (ADR-0017 amendment). + +`plan`, `recommendation`, `recommendation_materials` and the retiring +`plan_recommendations` moved to `infrastructure/postgres/modelling/` as single +SQLModel definitions (the `epc_property` pattern). This module re-exports them +under their legacy names so the dying `backend/` callers keep working; new code +imports from `infrastructure.postgres.modelling` directly. `ScenarioModel` and +`InstalledMeasure` are not yet migrated and stay here for now. +""" + import enum from typing import Iterable, List, NamedTuple, Optional, Type from sqlalchemy import ( - Column, BigInteger, String, Float, Boolean, TIMESTAMP, ForeignKey, + Column, Enum, ) from sqlalchemy.orm import Mapped, mapped_column @@ -16,158 +26,28 @@ from datetime import datetime 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 infrastructure.postgres.modelling import ( + PlanRow, + PlanType, + PlanRecommendationRow, + RecommendationMaterialRow, + RecommendationRow, +) + +# Legacy names → the single SQLModel definitions now in +# `infrastructure/postgres/modelling/`. +Recommendation = RecommendationRow +RecommendationMaterials = RecommendationMaterialRow +PlanModel = PlanRow +PlanRecommendations = PlanRecommendationRow +PlanTypeEnum = PlanType def portfolio_goal_values(enum_cls: Type[PortfolioGoal]) -> List[str]: return [e.value for e in enum_cls] -class Recommendation(Base): - __tablename__ = "recommendation" - - id = Column(BigInteger, primary_key=True, autoincrement=True) - property_id = Column(BigInteger, ForeignKey(PropertyModel.id), nullable=False) - created_at = Column(TIMESTAMP, nullable=False, server_default=func.now()) - type = Column(String, nullable=False) - measure_type = Column(String) - description = Column(String, nullable=False) - estimated_cost = Column(Float) - default = Column(Boolean, nullable=False) - starting_u_value = Column(Float) - new_u_value = Column(Float) - sap_points = Column(Float) - heat_demand = Column(Float) - kwh_savings = Column(Float) - co2_equivalent_savings = Column(Float) - energy_savings = Column(Float) - energy_cost_savings = Column(Float) - property_valuation_increase = Column(Float) - rental_yield_increase = Column(Float) - total_work_hours = Column(Float) - labour_days = Column(Float) - already_installed = Column(Boolean, nullable=False, default=False) - - -class RecommendationMaterials(Base): - __tablename__ = "recommendation_materials" - - id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) - - recommendation_id: Mapped[int] = mapped_column( - BigInteger, - ForeignKey("recommendation.id"), - nullable=False, - ) - - material_id: Mapped[int] = mapped_column( - BigInteger, - ForeignKey(Material.id), - nullable=False, - ) - - created_at: Mapped[datetime] = mapped_column( - TIMESTAMP, - nullable=False, - server_default=func.now(), - ) - - depth: Mapped[float] = mapped_column( - Float, - nullable=False, - ) - - quantity: Mapped[float] = mapped_column( - Float, - nullable=False, - ) - - quantity_unit: Mapped[QuantityUnits] = mapped_column( - Enum(QuantityUnits, values_callable=lambda x: [e.value for e in x]), - nullable=False, - ) - - estimated_cost: Mapped[float] = mapped_column( - Float, - nullable=False, - ) - - -class PlanTypeEnum(enum.Enum): # TODO: move this to domain? - SOLAR_ECO4 = "solar_eco4" - SOLAR_HHRSH_ECO4 = "solar_hhrsh_eco4" - EMPTY_CAVITY_ECO = "empty_cavity_eco" - PARTIAL_CAVITY_ECO = "partial_cavity_eco" - EXTRACTION_ECO = "extraction_eco" - - -class PlanModel(Base): - __tablename__ = "plan" - - id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) - - name: Mapped[Optional[str]] = mapped_column(String, nullable=True, default="") - - portfolio_id: Mapped[int] = mapped_column( - BigInteger, ForeignKey(Portfolio.id), nullable=False - ) - - property_id: Mapped[int] = mapped_column( - BigInteger, ForeignKey(PropertyModel.id), nullable=False - ) - - scenario_id: Mapped[Optional[int]] = mapped_column( - BigInteger, ForeignKey("scenario.id") - ) - - created_at: Mapped[datetime] = mapped_column( # type: ignore - TIMESTAMP, nullable=False, server_default=func.now() - ) - - is_default: Mapped[bool] = mapped_column(Boolean, nullable=False) - - valuation_increase_lower_bound: Mapped[Optional[float]] = mapped_column(Float) - valuation_increase_upper_bound: Mapped[Optional[float]] = mapped_column(Float) - valuation_increase_average: Mapped[Optional[float]] = mapped_column(Float) - - plan_type: Mapped[Optional[PlanTypeEnum]] = mapped_column( - Enum( - PlanTypeEnum, - name="plan_type", - values_callable=lambda e: [m.value for m in e], - create_type=False, - ), - nullable=True, - ) - - post_sap_points: Mapped[Optional[float]] = mapped_column(Float) - post_epc_rating: Mapped[Optional[Epc]] = mapped_column(Enum(Epc)) - post_co2_emissions: Mapped[Optional[float]] = mapped_column(Float) - co2_savings: Mapped[Optional[float]] = mapped_column(Float) - post_energy_bill: Mapped[Optional[float]] = mapped_column(Float) - energy_bill_savings: Mapped[Optional[float]] = mapped_column(Float) - post_energy_consumption: Mapped[Optional[float]] = mapped_column(Float) - energy_consumption_savings: Mapped[Optional[float]] = mapped_column(Float) - valuation_post_retrofit: Mapped[Optional[float]] = mapped_column(Float) - valuation_increase: Mapped[Optional[float]] = mapped_column(Float) - - # Financial metrics, excluding funding - cost_of_works: Mapped[Optional[float]] = mapped_column(Float) - contingency_cost: Mapped[Optional[float]] = mapped_column(Float) - - -class PlanRecommendations(Base): - __tablename__ = "plan_recommendations" - - id = Column(BigInteger, primary_key=True, autoincrement=True) - plan_id = Column(BigInteger, ForeignKey("plan.id"), nullable=False) - recommendation_id = Column( - BigInteger, ForeignKey("recommendation.id"), nullable=False - ) - - class ScenarioModel(Base): __tablename__ = "scenario" @@ -282,10 +162,10 @@ class InstalledMeasure(Base): is_active = Column(Boolean, nullable=False, default=True) -def enum_values(e: Iterable[PlanTypeEnum]) -> list[str]: +def enum_values(e: Iterable[PlanType]) -> list[str]: return [m.value for m in e] class PlanPersistence(NamedTuple): - plan: PlanModel + plan: PlanRow scenario: ScenarioModel diff --git a/backend/export/tests/conftest.py b/backend/export/tests/conftest.py index 80344c5e..fc73ae2c 100644 --- a/backend/export/tests/conftest.py +++ b/backend/export/tests/conftest.py @@ -25,17 +25,23 @@ def engine(postgresql): engine = create_engine(connection_string) - # Create tables once per test session - Base.metadata.create_all(engine) + # Create tables once per test session. SQLModel first: the Modelling tables + # (`plan` / `recommendation` / …) are SQLModel definitions, and Base tables + # FK them (`funding_package` → `plan`), so they must exist before Base's + # create_all runs (ADR-0017 amendment — single model per table). SQLModel.metadata.create_all(engine) + Base.metadata.create_all(engine) # Yeild will split this function into two phase. 1) setup and 2) teardown, the latter of which will run after all # tests have completed yield engine - # Clean-up after entire test session - SQLModel.metadata.drop_all(engine) - Base.metadata.drop_all(engine) + # The `postgresql` fixture is function-scoped — a fresh, throwaway database + # per test — so an explicit drop_all is redundant. We skip it: the `epc` + # Postgres enum type is now shared across both metadatas (Base `portfolio` + # tables and the SQLModel `plan`), and a two-phase metadata drop cannot drop + # a cross-metadata type cleanly (ADR-0017 amendment). Disposing the engine + # and letting the fixture discard the database is correct and conflict-free. engine.dispose() diff --git a/infrastructure/postgres/modelling/__init__.py b/infrastructure/postgres/modelling/__init__.py new file mode 100644 index 00000000..c1c8cb8c --- /dev/null +++ b/infrastructure/postgres/modelling/__init__.py @@ -0,0 +1,24 @@ +"""SQLModel definitions of the Modelling stage's live persistence tables +(ADR-0017 amendment). + +One canonical SQLModel per physical table — `plan`, `recommendation`, +`recommendation_materials` — replacing the legacy SQLAlchemy `Base` models in +`backend/app/db/models/recommendations.py` (now a re-export shim, the +`epc_property` pattern). `recommendation` carries `plan_id`; the +`plan_recommendations` m2m is retired. +""" + +from infrastructure.postgres.modelling.plan_table import PlanRow, PlanType +from infrastructure.postgres.modelling.recommendation_table import ( + PlanRecommendationRow, + RecommendationMaterialRow, + RecommendationRow, +) + +__all__ = [ + "PlanRow", + "PlanType", + "RecommendationRow", + "RecommendationMaterialRow", + "PlanRecommendationRow", +] diff --git a/infrastructure/postgres/modelling/plan_table.py b/infrastructure/postgres/modelling/plan_table.py new file mode 100644 index 00000000..7cbed104 --- /dev/null +++ b/infrastructure/postgres/modelling/plan_table.py @@ -0,0 +1,106 @@ +from __future__ import annotations + +import enum +from datetime import datetime +from typing import ClassVar, Optional + +from sqlalchemy import Column, TIMESTAMP +from sqlalchemy import Enum as SAEnum +from sqlalchemy.sql import func +from sqlmodel import Field, SQLModel + +from datatypes.epc.domain.epc import Epc +from domain.modelling.plan import Plan + +# Calculator metrics are in kg CO₂/yr; the live ``plan`` columns are tonnes +# (legacy ``emissions_kg / 1000``). Convert on the way in. +_KG_PER_TONNE = 1000.0 + + +class PlanType(enum.Enum): + SOLAR_ECO4 = "solar_eco4" + SOLAR_HHRSH_ECO4 = "solar_hhrsh_eco4" + EMPTY_CAVITY_ECO = "empty_cavity_eco" + PARTIAL_CAVITY_ECO = "partial_cavity_eco" + EXTRACTION_ECO = "extraction_eco" + + +class PlanRow(SQLModel, table=True): + """The single SQLModel definition of the live ``plan`` table (ADR-0017 + amendment). Full legacy column parity; out-of-cluster references + (``portfolio_id`` / ``property_id`` / ``scenario_id``) are plain indexed + ints, not FK constraints (mirror convention — the live FKs are owned by the + Drizzle schema).""" + + __tablename__: ClassVar[str] = "plan" # pyright: ignore[reportIncompatibleVariableOverride] + + id: Optional[int] = Field(default=None, primary_key=True) + name: Optional[str] = Field(default="") + portfolio_id: int + property_id: int = Field(index=True) + scenario_id: Optional[int] = Field(default=None) + created_at: Optional[datetime] = Field( + default=None, + sa_column=Column(TIMESTAMP, nullable=False, server_default=func.now()), + ) + is_default: bool = False + + valuation_increase_lower_bound: Optional[float] = Field(default=None) + valuation_increase_upper_bound: Optional[float] = Field(default=None) + valuation_increase_average: Optional[float] = Field(default=None) + + plan_type: Optional[PlanType] = Field( + default=None, + sa_column=Column( + SAEnum( + PlanType, + name="plan_type", + values_callable=lambda cls: [m.value for m in cls], # pyright: ignore[reportUnknownLambdaType, reportUnknownMemberType, reportUnknownVariableType] + create_type=False, + ), + nullable=True, + ), + ) + + post_sap_points: Optional[float] = Field(default=None) + post_epc_rating: Optional[Epc] = Field( + default=None, + sa_column=Column(SAEnum(Epc, name="epc"), nullable=True), + ) + post_co2_emissions: Optional[float] = Field(default=None) # tonnes/yr + co2_savings: Optional[float] = Field(default=None) # tonnes/yr + post_energy_bill: Optional[float] = Field(default=None) # £/yr + energy_bill_savings: Optional[float] = Field(default=None) # £/yr + post_energy_consumption: Optional[float] = Field(default=None) # kWh/yr + energy_consumption_savings: Optional[float] = Field(default=None) # kWh/yr + valuation_post_retrofit: Optional[float] = Field(default=None) + valuation_increase: Optional[float] = Field(default=None) + cost_of_works: Optional[float] = Field(default=None) + contingency_cost: Optional[float] = Field(default=None) + + @classmethod + def from_domain( + cls, + plan: Plan, + *, + property_id: int, + scenario_id: int, + portfolio_id: int, + is_default: bool, + ) -> "PlanRow": + return cls( + portfolio_id=portfolio_id, + property_id=property_id, + scenario_id=scenario_id, + is_default=is_default, + post_sap_points=plan.post_sap_continuous, + post_epc_rating=plan.post_epc_rating, + post_co2_emissions=plan.post_retrofit.co2_kg_per_yr / _KG_PER_TONNE, + co2_savings=plan.co2_savings_kg_per_yr / _KG_PER_TONNE, + cost_of_works=plan.cost_of_works, + contingency_cost=plan.contingency_cost, + post_energy_bill=plan.post_energy_bill, + energy_bill_savings=plan.energy_bill_savings, + post_energy_consumption=plan.post_energy_consumption, + energy_consumption_savings=plan.energy_consumption_savings, + ) diff --git a/infrastructure/postgres/modelling/recommendation_table.py b/infrastructure/postgres/modelling/recommendation_table.py new file mode 100644 index 00000000..c50a2947 --- /dev/null +++ b/infrastructure/postgres/modelling/recommendation_table.py @@ -0,0 +1,139 @@ +from __future__ import annotations + +from datetime import datetime +from typing import ClassVar, Optional + +from sqlalchemy import BigInteger, Column, ForeignKey, TIMESTAMP +from sqlalchemy import Enum as SAEnum +from sqlalchemy.sql import func +from sqlmodel import Field, SQLModel + +from datatypes.enums import QuantityUnits +from domain.modelling.plan import PlanMeasure + +# Calculator metrics are in kg CO₂/yr; the live ``recommendation`` column is +# tonnes (legacy ``emissions_kg / 1000``). Convert on the way in. +_KG_PER_TONNE = 1000.0 + + +class RecommendationRow(SQLModel, table=True): + """The single SQLModel definition of the live ``recommendation`` table + (ADR-0017 amendment) — one row per persisted Plan Measure. + + Carries full legacy column parity (the readers iterate the columns / sum + them) **plus** ``plan_id``, the FK that links a measure to its Plan and + replaces the retired ``plan_recommendations`` m2m. Out-of-cluster columns + (``property_id``) are plain indexed ints, not FK constraints, matching the + mirror convention so ``SQLModel.metadata.create_all`` needs no foreign + table to exist (the live FKs are owned by the Drizzle schema). + """ + + __tablename__: ClassVar[str] = "recommendation" # pyright: ignore[reportIncompatibleVariableOverride] + + id: Optional[int] = Field(default=None, primary_key=True) + property_id: int = Field(index=True) + plan_id: Optional[int] = Field( + default=None, + sa_column=Column( + BigInteger, + ForeignKey("plan.id", ondelete="CASCADE"), + nullable=True, + index=True, + ), + ) + created_at: Optional[datetime] = Field( + default=None, + sa_column=Column(TIMESTAMP, nullable=False, server_default=func.now()), + ) + + type: str + measure_type: Optional[str] = Field(default=None) + description: str + estimated_cost: Optional[float] = Field(default=None) + starting_u_value: Optional[float] = Field(default=None) + new_u_value: Optional[float] = Field(default=None) + sap_points: Optional[float] = Field(default=None) + heat_demand: Optional[float] = Field(default=None) + kwh_savings: Optional[float] = Field(default=None) # delivered kWh/yr + co2_equivalent_savings: Optional[float] = Field(default=None) # tonnes/yr + energy_savings: Optional[float] = Field(default=None) + energy_cost_savings: Optional[float] = Field(default=None) # £/yr + property_valuation_increase: Optional[float] = Field(default=None) + rental_yield_increase: Optional[float] = Field(default=None) + total_work_hours: Optional[float] = Field(default=None) + labour_days: Optional[float] = Field(default=None) + default: bool = True + already_installed: bool = False + + @classmethod + def from_domain( + cls, measure: PlanMeasure, *, property_id: int, plan_id: int + ) -> "RecommendationRow": + return cls( + property_id=property_id, + plan_id=plan_id, + type=measure.measure_type, + measure_type=measure.measure_type, + description=measure.description, + estimated_cost=measure.cost.total, + sap_points=measure.impact.sap_points, + co2_equivalent_savings=( + measure.impact.co2_savings_kg_per_yr / _KG_PER_TONNE + ), + kwh_savings=measure.kwh_savings, + energy_cost_savings=measure.energy_cost_savings, + default=True, + already_installed=False, + ) + + +class RecommendationMaterialRow(SQLModel, table=True): + """The live ``recommendation_materials`` table — one row per material used + by a Recommendation. ``recommendation_id`` is an intra-cluster FK; + ``material_id`` is a plain int (out-of-cluster, mirror convention).""" + + __tablename__: ClassVar[str] = "recommendation_materials" # pyright: ignore[reportIncompatibleVariableOverride] + + id: Optional[int] = Field(default=None, primary_key=True) + recommendation_id: int = Field( + sa_column=Column( + BigInteger, ForeignKey("recommendation.id"), nullable=False + ) + ) + material_id: int = Field(index=True) + created_at: Optional[datetime] = Field( + default=None, + sa_column=Column(TIMESTAMP, nullable=False, server_default=func.now()), + ) + depth: Optional[float] = Field(default=None) + quantity: Optional[float] = Field(default=None) + quantity_unit: Optional[QuantityUnits] = Field( + default=None, + sa_column=Column( + SAEnum( + QuantityUnits, + values_callable=lambda cls: [m.value for m in cls], # pyright: ignore[reportUnknownLambdaType, reportUnknownMemberType, reportUnknownVariableType] + ), + nullable=True, + ), + ) + estimated_cost: Optional[float] = Field(default=None) + + +class PlanRecommendationRow(SQLModel, table=True): + """The legacy ``plan_recommendations`` m2m — **being retired** (ADR-0017 + amendment). Kept as an intra-cluster SQLModel row only for the transition + window while readers/writers move onto ``recommendation.plan_id``; dropped + once no caller remains. Both FKs are intra-cluster.""" + + __tablename__: ClassVar[str] = "plan_recommendations" # pyright: ignore[reportIncompatibleVariableOverride] + + id: Optional[int] = Field(default=None, primary_key=True) + plan_id: int = Field( + sa_column=Column(BigInteger, ForeignKey("plan.id"), nullable=False) + ) + recommendation_id: int = Field( + sa_column=Column( + BigInteger, ForeignKey("recommendation.id"), nullable=False + ) + ) diff --git a/infrastructure/postgres/plan_table.py b/infrastructure/postgres/plan_table.py deleted file mode 100644 index b76c32d1..00000000 --- a/infrastructure/postgres/plan_table.py +++ /dev/null @@ -1,130 +0,0 @@ -from __future__ import annotations - -from typing import ClassVar, Optional - -from sqlalchemy import BigInteger, Column, ForeignKey -from sqlalchemy import Enum as SAEnum -from sqlmodel import Field, SQLModel - -from datatypes.epc.domain.epc import Epc -from domain.modelling.plan import Plan, PlanMeasure - -# Calculator metrics are in kg CO₂/yr; the live `plan` / `recommendation` -# columns are tonnes (legacy `emissions_kg / 1000`). Convert on the way in. -_KG_PER_TONNE = 1000.0 - - -class PlanRow(SQLModel, table=True): - """SQLModel mirror of the live ``plan`` table (ADR-0017). - - Declares only the columns the rebuild writes — identity, the flat - post-retrofit headline figures, and the cost aggregates. The legacy - SQLAlchemy model owns the live reads and the columns left for later - slices (valuation, plan_type, the energy/bill cluster). The physical - table is the shared contract. - """ - - __tablename__: ClassVar[str] = "plan" # pyright: ignore[reportIncompatibleVariableOverride] - - id: Optional[int] = Field(default=None, primary_key=True) - portfolio_id: int - property_id: int = Field(index=True) - scenario_id: Optional[int] = Field(default=None) - is_default: bool = False - - post_sap_points: Optional[float] = Field(default=None) - post_epc_rating: Optional[Epc] = Field( - default=None, - sa_column=Column(SAEnum(Epc, name="epc"), nullable=True), - ) - post_co2_emissions: Optional[float] = Field(default=None) # tonnes/yr - co2_savings: Optional[float] = Field(default=None) # tonnes/yr - cost_of_works: Optional[float] = Field(default=None) - contingency_cost: Optional[float] = Field(default=None) - post_energy_bill: Optional[float] = Field(default=None) # £/yr - energy_bill_savings: Optional[float] = Field(default=None) # £/yr - post_energy_consumption: Optional[float] = Field(default=None) # delivered kWh/yr - energy_consumption_savings: Optional[float] = Field(default=None) # kWh/yr - - @classmethod - def from_domain( - cls, - plan: Plan, - *, - property_id: int, - scenario_id: int, - portfolio_id: int, - is_default: bool, - ) -> "PlanRow": - return cls( - portfolio_id=portfolio_id, - property_id=property_id, - scenario_id=scenario_id, - is_default=is_default, - post_sap_points=plan.post_sap_continuous, - post_epc_rating=plan.post_epc_rating, - post_co2_emissions=plan.post_retrofit.co2_kg_per_yr / _KG_PER_TONNE, - co2_savings=plan.co2_savings_kg_per_yr / _KG_PER_TONNE, - cost_of_works=plan.cost_of_works, - contingency_cost=plan.contingency_cost, - post_energy_bill=plan.post_energy_bill, - energy_bill_savings=plan.energy_bill_savings, - post_energy_consumption=plan.post_energy_consumption, - energy_consumption_savings=plan.energy_consumption_savings, - ) - - -class RecommendationRow(SQLModel, table=True): - """SQLModel mirror of the live ``recommendation`` table — one row per - persisted Plan Measure (ADR-0017). Adds the new ``plan_id`` FK linking the - measure to its Plan (ON DELETE CASCADE), replacing the ``plan_recommendations`` - m2m for new writes. Only the impact + cost columns the tracer fills are - declared; the energy/bill, U-value, valuation and labour columns are left - to later slices. - """ - - __tablename__: ClassVar[str] = "recommendation" # pyright: ignore[reportIncompatibleVariableOverride] - - id: Optional[int] = Field(default=None, primary_key=True) - property_id: int = Field(index=True) - plan_id: Optional[int] = Field( - default=None, - sa_column=Column( - BigInteger, - ForeignKey("plan.id", ondelete="CASCADE"), - nullable=True, - index=True, - ), - ) - - type: str - measure_type: Optional[str] = Field(default=None) - description: str - estimated_cost: Optional[float] = Field(default=None) - sap_points: Optional[float] = Field(default=None) - co2_equivalent_savings: Optional[float] = Field(default=None) # tonnes/yr - kwh_savings: Optional[float] = Field(default=None) # delivered kWh/yr - energy_cost_savings: Optional[float] = Field(default=None) # £/yr - default: bool = True - already_installed: bool = False - - @classmethod - def from_domain( - cls, measure: PlanMeasure, *, property_id: int, plan_id: int - ) -> "RecommendationRow": - return cls( - property_id=property_id, - plan_id=plan_id, - type=measure.measure_type, - measure_type=measure.measure_type, - description=measure.description, - estimated_cost=measure.cost.total, - sap_points=measure.impact.sap_points, - co2_equivalent_savings=( - measure.impact.co2_savings_kg_per_yr / _KG_PER_TONNE - ), - kwh_savings=measure.kwh_savings, - energy_cost_savings=measure.energy_cost_savings, - default=True, - already_installed=False, - ) diff --git a/repositories/plan/plan_postgres_repository.py b/repositories/plan/plan_postgres_repository.py index 401ec087..75e9096c 100644 --- a/repositories/plan/plan_postgres_repository.py +++ b/repositories/plan/plan_postgres_repository.py @@ -3,7 +3,7 @@ from __future__ import annotations from sqlmodel import Session, col, delete from domain.modelling.plan import Plan -from infrastructure.postgres.plan_table import PlanRow, RecommendationRow +from infrastructure.postgres.modelling import PlanRow, RecommendationRow from repositories.plan.plan_repository import PlanRepository diff --git a/tests/orchestration/test_ara_first_run_pipeline_integration.py b/tests/orchestration/test_ara_first_run_pipeline_integration.py index 5cbc4fbb..70f2087c 100644 --- a/tests/orchestration/test_ara_first_run_pipeline_integration.py +++ b/tests/orchestration/test_ara_first_run_pipeline_integration.py @@ -26,7 +26,7 @@ from infrastructure.postgres.property_baseline_performance_table import ( PropertyBaselinePerformanceModel, ) from infrastructure.postgres.epc_property_table import EpcPropertyModel -from infrastructure.postgres.plan_table import PlanRow, RecommendationRow +from infrastructure.postgres.modelling import PlanRow, RecommendationRow from infrastructure.postgres.product_table import MaterialRow from infrastructure.postgres.property_table import PropertyRow from tests.domain.sap10_calculator.worksheet._elmhurst_worksheet_000490 import ( diff --git a/tests/repositories/plan/test_plan_postgres_repository.py b/tests/repositories/plan/test_plan_postgres_repository.py index 050c9b55..94033b00 100644 --- a/tests/repositories/plan/test_plan_postgres_repository.py +++ b/tests/repositories/plan/test_plan_postgres_repository.py @@ -18,7 +18,7 @@ from domain.modelling.scoring.package_scorer import Score from domain.modelling.plan import Plan, PlanMeasure from domain.modelling.recommendation import Cost from domain.modelling.scoring.scoring import MeasureImpact -from infrastructure.postgres.plan_table import PlanRow, RecommendationRow +from infrastructure.postgres.modelling import PlanRow, RecommendationRow from repositories.plan.plan_postgres_repository import PlanPostgresRepository