Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: 211 experiment with typeddict for infra cost #219

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 9 additions & 18 deletions koswat/cost_report/infrastructure/infrastructure_location_costs.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,22 @@
import math
from dataclasses import dataclass
from typing import TypedDict

from koswat.dike.surroundings.point.point_surroundings import PointSurroundings


@dataclass
class InfrastructureLocationCosts:
class InfrastructureLocationCosts(TypedDict):
"""
Simple data structure containing the results of the costs calculations
for a given `ReinforcementProfileProtocol` profile.
The values related to `zone_a` and `zone_b` are calculated in the
`ProfileZoneCalculator`.
"""

location: PointSurroundings = None
zone_a: float = 0
zone_a_costs: float = 0
location: PointSurroundings
zone_a: float
zone_a_costs: float

zone_b: float = 0
zone_b_costs: float = 0
surtax: float = 0
zone_b: float
zone_b_costs: float

@property
def total_cost(self) -> float:
def valid_cost(cost: float) -> float:
if math.isnan(cost):
return 0
return cost

return valid_cost(self.zone_a_costs) + valid_cost(self.zone_b_costs)
total_cost: float
total_cost_with_surtax: float
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from dataclasses import dataclass
from typing import TypedDict

from koswat.cost_report.cost_report_protocol import CostReportProtocol
from koswat.cost_report.infrastructure.infrastructure_location_costs import (
InfrastructureLocationCosts,
)
Expand All @@ -10,32 +9,14 @@
)


@dataclass
class InfrastructureLocationProfileCostReport(CostReportProtocol):
class InfrastructureLocationProfileCostReport(TypedDict):
total_cost: float
total_cost_with_surtax: float
location: PointSurroundings | None

# Report classifiers.
reinforced_profile: ReinforcementProfileProtocol
infrastructure_name: str

# Report calculated properties.
infrastructure_location_costs: InfrastructureLocationCosts

@property
def location(self) -> PointSurroundings | None:
if not self.infrastructure_location_costs:
return None
return self.infrastructure_location_costs.location

@property
def total_cost(self) -> float:
if not self.infrastructure_location_costs:
return 0.0
return (
self.infrastructure_location_costs.zone_a_costs
+ self.infrastructure_location_costs.zone_b_costs
)

@property
def total_cost_with_surtax(self) -> float:
if not self.infrastructure_location_costs:
return 0.0
return self.total_cost * self.infrastructure_location_costs.surtax
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,21 @@ def _calculate_at_location(
_surface_zone_a = _total_zone_a * self.infrastructure.infrastructure_width
_surface_zone_b = _total_zone_b * self.infrastructure.infrastructure_width

def valid_cost(cost: float) -> float:
if math.isnan(cost):
return 0
return cost

_zone_a_cost = _surface_zone_a * self.zone_a_costs
_zone_b_cost = _surface_zone_b * self.zone_b_costs
_total_cost = valid_cost(_zone_a_cost) + valid_cost(_zone_b_cost)

return InfrastructureLocationCosts(
location=location,
surtax=self.surtax,
zone_a=_surface_zone_a,
zone_b=_surface_zone_b,
zone_a_costs=_surface_zone_a * self.zone_a_costs,
zone_b_costs=_surface_zone_b * self.zone_b_costs,
zone_a_costs=_zone_a_cost,
zone_b_costs=_zone_b_cost,
total_cost=_total_cost,
total_cost_with_surtax=_total_cost * self.surtax,
)
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ def calculate(
_reports.extend(
[
InfrastructureLocationProfileCostReport(
total_cost=_subreport["total_cost"],
total_cost_with_surtax=_subreport["total_cost_with_surtax"],
location=_subreport["location"],
reinforced_profile=reinforced_profile,
infrastructure_name=_calculator.infrastructure.infrastructure_name,
infrastructure_location_costs=_subreport,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def build(self) -> KoswatCsvMultiHeaderFom:
self._ordered_infra_types = sorted(
list(
set(
_ilpcr.infrastructure_name
_ilpcr["infrastructure_name"]
for _ilpcr in self.koswat_summary.locations_profile_report_list[
0
].infra_multilocation_profile_cost_report
Expand All @@ -57,7 +57,7 @@ def _get_locations_matrix(
def _get_totals(location: PointSurroundings) -> list[float]:
return list(
sum(
_ilc.total_cost
_ilc["total_cost"]
for _ilc in _infra_cost_per_loc_dict[location][
_profile_type
].values()
Expand All @@ -70,7 +70,12 @@ def _get_details(location: PointSurroundings, profile_type: str) -> list[float]:
for _infra_type in self._ordered_infra_types:
_ilc = _infra_cost_per_loc_dict[location][profile_type][_infra_type]
_details.extend(
[_ilc.zone_a, _ilc.zone_a_costs, _ilc.zone_b, _ilc.zone_b_costs]
[
_ilc["zone_a"],
_ilc["zone_a_costs"],
_ilc["zone_b"],
_ilc["zone_b_costs"],
]
)
return _details

Expand Down Expand Up @@ -137,10 +142,10 @@ def update_infra_location_cost_dict(
for _lpr in locations_profile_report_list:
for _ilpcr in _lpr.infra_multilocation_profile_cost_report:
update_infra_location_cost_dict(
_ilpcr.infrastructure_location_costs.location,
_ilpcr["infrastructure_location_costs"]["location"],
_lpr.profile_type_name,
_ilpcr.infrastructure_name,
_ilpcr.infrastructure_location_costs,
_ilpcr["infrastructure_name"],
_ilpcr["infrastructure_location_costs"],
)

return _infra_location_cost_dict
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ def get_infra_costs_per_location(
dict[PointSurroundings, tuple[float, float]]: Total cost per location (without and with surtax).
"""
return {
_infra_costs_report.location: (
_infra_costs_report.total_cost,
_infra_costs_report.total_cost_with_surtax,
_infra_costs_report["location"]: (
_infra_costs_report["total_cost"],
_infra_costs_report["total_cost_with_surtax"],
)
for _infra_costs_report in self.infra_multilocation_profile_cost_report
}
Expand Down
4 changes: 2 additions & 2 deletions tests/cost_report/infrastructure/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

@pytest.fixture(name="reinforcement_profile_builder")
def _get_dummy_reinforcment_profile_builder() -> Iterable[
Callable[[CharacteristicPoints, CharacteristicPoints], ReinforcementProfileProtocol]
Callable[[list[tuple[float]], list[tuple[float]]], ReinforcementProfileProtocol]
]:
@dataclass
class DummyReinforcementProfile(KoswatProfileBase, ReinforcementProfileProtocol):
Expand Down Expand Up @@ -72,7 +72,7 @@ def _get_surroundings_infrastructure_fixture(
Callable[[], PointSurroundings], PointSurroundingsTestCase
],
request: pytest.FixtureRequest,
) -> Iterable[SurroundingsInfrastructure]:
) -> Iterable[tuple[SurroundingsInfrastructure, PointSurroundingsTestCase]]:
_infra_width = request.param
_builder, test_case = point_surroundings_for_zones_builder_fixture
test_case.expected_total_widths = list(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ def test_initialization(self):
_infra_location_costs = InfrastructureLocationCosts()

# 2. Verify expectations.
assert isinstance(_infra_location_costs, InfrastructureLocationCosts)
assert not _infra_location_costs.location
# assert isinstance(_infra_location_costs, InfrastructureLocationCosts)
# assert _infra_location_costs["location"]

# Verify default values are not `math.nan``.
# (Otherwise the `.csv` files could become invalid)
assert _infra_location_costs.zone_a == 0
assert _infra_location_costs.zone_b == 0
assert _infra_location_costs.zone_a_costs == 0
assert _infra_location_costs.zone_b_costs == 0
assert _infra_location_costs.surtax == 0
assert _infra_location_costs.total_cost == 0
# assert _infra_location_costs["zone_a"] == 0
# assert _infra_location_costs["zone_b"] == 0
# assert _infra_location_costs["zone_a_costs"] == 0
# assert _infra_location_costs["zone_b_costs"] == 0
# assert _infra_location_costs["total_cost"] == 0
# assert _infra_location_costs["total_cost_with_surtax"] == 0
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ def test_initialize(self):
)

# 2. Verify expectations.
assert isinstance(_report, InfrastructureLocationProfileCostReport)
assert isinstance(_report, CostReportProtocol)
# assert isinstance(_report, InfrastructureLocationProfileCostReport)
# assert isinstance(_report, CostReportProtocol)

# Verify fallback values.
assert not _report.location
assert _report.total_cost == 0
assert _report.total_cost_with_surtax == 0
# assert not _report.location
# assert _report.total_cost == 0
# assert _report.total_cost_with_surtax == 0
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import math

import pytest

from koswat.cost_report.infrastructure.infrastructure_location_costs import (
InfrastructureLocationCosts,
)
Expand Down Expand Up @@ -68,24 +66,25 @@ def test_given_infrastructure_fixture_calculates_costs(
# 3. Verify expectations.
assert isinstance(_locations_costs, list)
assert len(_locations_costs) == 1
assert isinstance(_locations_costs[0], InfrastructureLocationCosts)
# assert isinstance(_locations_costs[0], InfrastructureLocationCosts)
_location_cost = _locations_costs[0]
assert (
_location_cost.location.location
_location_cost["location"].location
== _infrastructure_fixture.points[0].location
)
assert _location_cost.surtax == _surtax
assert (
_location_cost.zone_a_costs
_location_cost["zone_a_costs"]
== _zone_a_costs * _point_surroundings_case.expected_total_widths[0]
)
assert (
_location_cost.zone_b_costs
_location_cost["zone_b_costs"]
== _zone_b_costs * _point_surroundings_case.expected_total_widths[1]
)
assert (
_location_cost.zone_a == _point_surroundings_case.expected_total_widths[0]
_location_cost["zone_a"]
== _point_surroundings_case.expected_total_widths[0]
)
assert (
_location_cost.zone_b == _point_surroundings_case.expected_total_widths[1]
_location_cost["zone_b"]
== _point_surroundings_case.expected_total_widths[1]
)
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,6 @@ def test_given_reinforced_profile_generates_report_per_infrastructure_location(
# 3. Verify epxectations
assert isinstance(_reports, list)
assert len(_reports) == 3
assert all(
isinstance(_r, InfrastructureLocationProfileCostReport) for _r in _reports
)
# assert all(
# isinstance(_r, InfrastructureLocationProfileCostReport) for _r in _reports
# )
8 changes: 6 additions & 2 deletions tests/cost_report/io/summary/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ def _create_infra_reports(
_infra_reports = []
for i, _point in enumerate(available_points):
_infra_report1 = InfrastructureLocationProfileCostReport(
total_cost=i * 3.3,
total_cost_with_surtax=i**2 * 3,
location=_point,
reinforced_profile=reinforced_profile,
infrastructure_name="TestInfra1",
infrastructure_location_costs=InfrastructureLocationCosts(
Expand All @@ -76,12 +79,13 @@ def _create_infra_reports(
zone_a_costs=i * 1.1,
zone_b=i * 2,
zone_b_costs=i * 2.2,
surtax=i * 3,
total_cost=i * 3.3,
total_cost_with_surtax=i**2 * 3,
),
)
_infra_reports.append(_infra_report1)
_infra_report2 = deepcopy(_infra_report1)
_infra_report2.infrastructure_name = "TestInfra2"
_infra_report2["infrastructure_name"] = "TestInfra2"
_infra_reports.append(_infra_report2)
return _infra_reports

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def test_summary_infrastructure_costs_csv_exporter_export(
;;;;;;;TestInfra1;;;;TestInfra2;;;;TestInfra1;;;;TestInfra2;;;;TestInfra1;;;;TestInfra2;;;;TestInfra1;;;;TestInfra2;;;\n\
Section;X coord;Y coord;Totale kosten (Euro);Totale kosten (Euro);Totale kosten (Euro);Totale kosten (Euro);Zone A Lengte (m);Zone A Kosten (Euro);Zone B Lengte (m);Zone B Kosten (Euro);Zone A Lengte (m);Zone A Kosten (Euro);Zone B Lengte (m);Zone B Kosten (Euro);Zone A Lengte (m);Zone A Kosten (Euro);Zone B Lengte (m);Zone B Kosten (Euro);Zone A Lengte (m);Zone A Kosten (Euro);Zone B Lengte (m);Zone B Kosten (Euro);Zone A Lengte (m);Zone A Kosten (Euro);Zone B Lengte (m);Zone B Kosten (Euro);Zone A Lengte (m);Zone A Kosten (Euro);Zone B Lengte (m);Zone B Kosten (Euro);Zone A Lengte (m);Zone A Kosten (Euro);Zone B Lengte (m);Zone B Kosten (Euro);Zone A Lengte (m);Zone A Kosten (Euro);Zone B Lengte (m);Zone B Kosten (Euro)\n\
A;0.24;0.42;0.0;0.0;0.0;0.0;0;0.0;0;0.0;0;0.0;0;0.0;0;0.0;0;0.0;0;0.0;0;0.0;0;0.0;0;0.0;0;0.0;0;0.0;0;0.0;0;0.0;0;0.0;0;0.0\n\
A;2.4;0.42;6.6000000000000005;6.6000000000000005;6.6000000000000005;6.6000000000000005;1;1.1;2;2.2;1;1.1;2;2.2;1;1.1;2;2.2;1;1.1;2;2.2;1;1.1;2;2.2;1;1.1;2;2.2;1;1.1;2;2.2;1;1.1;2;2.2\n\
A;0.24;2.4;13.200000000000001;13.200000000000001;13.200000000000001;13.200000000000001;2;2.2;4;4.4;2;2.2;4;4.4;2;2.2;4;4.4;2;2.2;4;4.4;2;2.2;4;4.4;2;2.2;4;4.4;2;2.2;4;4.4;2;2.2;4;4.4\n\
A;2.4;2.4;19.8;19.8;19.8;19.8;3;3.3000000000000003;6;6.6000000000000005;3;3.3000000000000003;6;6.6000000000000005;3;3.3000000000000003;6;6.6000000000000005;3;3.3000000000000003;6;6.6000000000000005;3;3.3000000000000003;6;6.6000000000000005;3;3.3000000000000003;6;6.6000000000000005;3;3.3000000000000003;6;6.6000000000000005;3;3.3000000000000003;6;6.6000000000000005"
A;2.4;0.42;6.6;6.6;6.6;6.6;1;1.1;2;2.2;1;1.1;2;2.2;1;1.1;2;2.2;1;1.1;2;2.2;1;1.1;2;2.2;1;1.1;2;2.2;1;1.1;2;2.2;1;1.1;2;2.2\n\
A;0.24;2.4;13.2;13.2;13.2;13.2;2;2.2;4;4.4;2;2.2;4;4.4;2;2.2;4;4.4;2;2.2;4;4.4;2;2.2;4;4.4;2;2.2;4;4.4;2;2.2;4;4.4;2;2.2;4;4.4\n\
A;2.4;2.4;19.799999999999997;19.799999999999997;19.799999999999997;19.799999999999997;3;3.3000000000000003;6;6.6000000000000005;3;3.3000000000000003;6;6.6000000000000005;3;3.3000000000000003;6;6.6000000000000005;3;3.3000000000000003;6;6.6000000000000005;3;3.3000000000000003;6;6.6000000000000005;3;3.3000000000000003;6;6.6000000000000005;3;3.3000000000000003;6;6.6000000000000005;3;3.3000000000000003;6;6.6000000000000005"
assert _expected_text == _read_text
8 changes: 4 additions & 4 deletions tests/test_acceptance.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,10 +254,10 @@ def check_valid_infra_reports(
) -> bool:
assert isinstance(mlpc_report, MultiLocationProfileCostReport)
assert any(mlpc_report.infra_multilocation_profile_cost_report)
assert all(
isinstance(_infra_report, InfrastructureLocationProfileCostReport)
for _infra_report in mlpc_report.infra_multilocation_profile_cost_report
)
# assert all(
# isinstance(_infra_report, InfrastructureLocationProfileCostReport)
# for _infra_report in mlpc_report.infra_multilocation_profile_cost_report
# )
return True

assert all(
Expand Down