Skip to content

Commit

Permalink
feat: 213 extend summary report with reinforcement order (#215)
Browse files Browse the repository at this point in the history
  • Loading branch information
ArdtK authored Nov 5, 2024
1 parent 19c3834 commit 731b57b
Show file tree
Hide file tree
Showing 55 changed files with 180 additions and 61 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ coverage.xml
# Koswat ignore
tests/test_results/
site/
*.log

# Local configurations files covered in pyproject.toml
pytest.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import logging
import math
from collections import defaultdict
from dataclasses import dataclass
from itertools import groupby
Expand Down Expand Up @@ -31,7 +30,11 @@ def dict_to_csv_row(csv_to_convert: dict) -> list[list[str]]:
]

def build(self) -> KoswatCsvFom:
def get_index(items: list, item):
return list(items).index(item) if item in items else -1

_profile_type_key = "Profile type"
_reinforcement_order_key = "Strategy reinforcement order"
_cost_per_km_key = "Cost per km (Euro/km)"
_cost_per_km_incl_surtax_key = "Cost per km incl surtax (Euro/km)"

Expand All @@ -40,11 +43,19 @@ def build(self) -> KoswatCsvFom:
_dict_of_entries[_profile_type_key].append(
_loc_prof_report.profile_type_name
)
_dict_of_entries[_reinforcement_order_key].append(
str(
get_index(
self.koswat_summary.reinforcement_order,
type(_loc_prof_report.profile_cost_report.reinforced_profile),
)
)
)
_dict_of_entries[_cost_per_km_key].append(
round(_loc_prof_report.cost_per_km, self._decimals)
str(round(_loc_prof_report.cost_per_km, self._decimals))
)
_dict_of_entries[_cost_per_km_incl_surtax_key].append(
round(_loc_prof_report.cost_per_km_with_surtax, self._decimals)
str(round(_loc_prof_report.cost_per_km_with_surtax, self._decimals))
)
self._get_quantity_cost_parameters(
_loc_prof_report.profile_cost_report.quantity_cost_parameters.__dict__,
Expand All @@ -55,6 +66,10 @@ def build(self) -> KoswatCsvFom:
logging.error("No entries generated for the CSV Matrix.")
return KoswatCsvFom()

_reinforcement_order_row = [
[_reinforcement_order_key] + _dict_of_entries[_reinforcement_order_key]
]

_cost_per_km_rows = [
[_cost_per_km_key] + _dict_of_entries[_cost_per_km_key],
[_cost_per_km_incl_surtax_key]
Expand Down Expand Up @@ -84,7 +99,8 @@ def build(self) -> KoswatCsvFom:
return KoswatCsvFom(
headers=[_profile_type_key] + _dict_of_entries[_profile_type_key],
entries=(
_cost_per_km_rows
_reinforcement_order_row
+ _cost_per_km_rows
+ _quantity_costs_rows
+ _selected_measure_cost_rows
+ _infrastructure_cost_rows
Expand Down
7 changes: 7 additions & 0 deletions koswat/cost_report/summary/koswat_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,19 @@

@dataclass
class KoswatSummary:
"""
Represents the summary of the KOSWAT analysis.
"""

locations_profile_report_list: list[MultiLocationProfileCostReport] = field(
default_factory=lambda: []
)
reinforcement_per_locations: list[StrategyLocationReinforcement] = field(
default_factory=lambda: []
)
reinforcement_order: list[type[ReinforcementProfileProtocol]] = field(
default_factory=lambda: []
)

def get_report_by_profile(
self, profile_type: type[ReinforcementProfileProtocol]
Expand Down
11 changes: 6 additions & 5 deletions koswat/cost_report/summary/koswat_summary_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,14 @@
from koswat.dike.profile.koswat_input_profile_base import KoswatInputProfileBase
from koswat.dike.surroundings.point.point_surroundings import PointSurroundings
from koswat.dike_reinforcements import ReinforcementProfileBuilderFactory
from koswat.dike_reinforcements.reinforcement_profile.reinforcement_profile import (
ReinforcementProfile,
)
from koswat.dike_reinforcements.reinforcement_profile.reinforcement_profile_protocol import (
ReinforcementProfileProtocol,
)
from koswat.strategies.infra_priority_strategy.infra_priority_strategy import (
InfraPriorityStrategy,
)
from koswat.strategies.strategy_input import StrategyInput
from koswat.strategies.strategy_output import StrategyOutput
from koswat.strategies.strategy_protocol import StrategyProtocol


Expand Down Expand Up @@ -100,7 +98,7 @@ def _get_final_reinforcement_per_location(
self,
locations_profile_report_list: list[MultiLocationProfileCostReport],
available_locations: list[PointSurroundings],
) -> dict[PointSurroundings, ReinforcementProfile]:
) -> StrategyOutput:
_matrix, _reinforcements = KoswatSummaryLocationMatrixBuilder(
available_locations=available_locations,
locations_profile_report_list=locations_profile_report_list,
Expand Down Expand Up @@ -129,8 +127,11 @@ def build(self) -> KoswatSummary:
_mlpc_builder.reinforced_profile = _calc_profile
_summary.locations_profile_report_list.append(_mlpc_builder.build())

_summary.reinforcement_per_locations = self._get_final_reinforcement_per_location(
_strategy_output = self._get_final_reinforcement_per_location(
_summary.locations_profile_report_list,
self.run_scenario_settings.surroundings.obstacle_surroundings_wrapper.obstacle_locations,
)
_summary.reinforcement_per_locations = _strategy_output.location_reinforcements
_summary.reinforcement_order = _strategy_output.reinforcement_order

return _summary
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from koswat.strategies.strategy_location_reinforcement import (
StrategyLocationReinforcement,
)
from koswat.strategies.strategy_output import StrategyOutput
from koswat.strategies.strategy_protocol import StrategyProtocol


Expand Down Expand Up @@ -137,9 +138,7 @@ def get_cluster_option(
),
)

def apply_strategy(
self, strategy_input: StrategyInput
) -> list[StrategyLocationReinforcement]:
def apply_strategy(self, strategy_input: StrategyInput) -> StrategyOutput:
_clustered_locations = []
# 1. Run `OrderStrategy` to generate an initial cluster formation.
self._order_strategy = OrderStrategy()
Expand All @@ -148,13 +147,16 @@ def apply_strategy(
):
self._set_cheapest_measure_per_cluster(_infra_cluster, strategy_input)
_clustered_locations.extend(_infra_cluster.cluster)
return _clustered_locations
return StrategyOutput(
location_reinforcements=_clustered_locations,
reinforcement_order=self._order_strategy.reinforcement_order,
)

def _get_initial_state(
self, order_strategy: OrderStrategy, strategy_input: StrategyInput
) -> Iterator[InfraCluster]:
for _grouped_by, _grouping in groupby(
order_strategy.apply_strategy(strategy_input),
order_strategy.apply_strategy(strategy_input).location_reinforcements,
key=lambda x: x.current_selected_measure,
):
_grouping_data = list(_grouping)
Expand Down
10 changes: 6 additions & 4 deletions koswat/strategies/order_strategy/order_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from koswat.strategies.strategy_location_reinforcement import (
StrategyLocationReinforcement,
)
from koswat.strategies.strategy_output import StrategyOutput
from koswat.strategies.strategy_protocol import StrategyProtocol
from koswat.strategies.strategy_reinforcement_input import StrategyReinforcementInput
from koswat.strategies.strategy_step.strategy_step_enum import StrategyStepEnum
Expand Down Expand Up @@ -154,9 +155,7 @@ def get_strategy_reinforcements(
_strategy_reinforcements.append(_slr)
return _strategy_reinforcements

def apply_strategy(
self, strategy_input: StrategyInput
) -> list[StrategyLocationReinforcement]:
def apply_strategy(self, strategy_input: StrategyInput) -> StrategyOutput:
self.reinforcement_order = self.get_strategy_order_for_reinforcements(
strategy_input.strategy_reinforcements
)
Expand All @@ -169,4 +168,7 @@ def apply_strategy(
OrderStrategyClustering.with_strategy(
self.reinforcement_order, strategy_input.reinforcement_min_length
).apply(_strategy_reinforcements)
return _strategy_reinforcements
return StrategyOutput(
location_reinforcements=_strategy_reinforcements,
reinforcement_order=self.reinforcement_order,
)
16 changes: 11 additions & 5 deletions koswat/strategies/strategy_input.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import math
from dataclasses import dataclass
from dataclasses import dataclass, field

from koswat.strategies.strategy_location_input import StrategyLocationInput
from koswat.strategies.strategy_reinforcement_input import StrategyReinforcementInput


@dataclass
class StrategyInput:
strategy_locations: list[StrategyLocationInput]
strategy_reinforcements: list[StrategyReinforcementInput]
reinforcement_min_buffer: float
reinforcement_min_length: float
"""
Represents the input data structure for a strategy.
"""

strategy_locations: list[StrategyLocationInput] = field(default_factory=lambda: [])
strategy_reinforcements: list[StrategyReinforcementInput] = field(
default_factory=lambda: []
)
reinforcement_min_buffer: float = 0.0
reinforcement_min_length: float = 0.0

@property
def reinforcement_min_cluster(self) -> int:
Expand Down
22 changes: 22 additions & 0 deletions koswat/strategies/strategy_output.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from dataclasses import dataclass, field

from koswat.dike_reinforcements.reinforcement_profile.reinforcement_profile_protocol import (
ReinforcementProfileProtocol,
)
from koswat.strategies.strategy_location_reinforcement import (
StrategyLocationReinforcement,
)


@dataclass
class StrategyOutput:
"""
Represents the output data structure for a strategy.
"""

location_reinforcements: list[StrategyLocationReinforcement] = field(
default_factory=lambda: []
)
reinforcement_order: list[type[ReinforcementProfileProtocol]] = field(
default_factory=lambda: []
)
9 changes: 3 additions & 6 deletions koswat/strategies/strategy_protocol.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
from typing import Protocol, runtime_checkable

from koswat.strategies.strategy_input import StrategyInput
from koswat.strategies.strategy_location_reinforcement import (
StrategyLocationReinforcement,
)
from koswat.strategies.strategy_output import StrategyOutput


@runtime_checkable
class StrategyProtocol(Protocol):
def apply_strategy(
self,
strategy_input: StrategyInput,
) -> list[StrategyLocationReinforcement]:
) -> StrategyOutput:
"""
Applies a specific strategy by matching each location (`PointSurroundings`)
to a valid reinforcement type (`ReinforcementProfileProtocol`).
Expand All @@ -20,6 +18,5 @@ def apply_strategy(
strategy_input (`StrategyInput`): Input data structure containing locations and available reinforcements for each of them.
Returns:
list[StrategyLocationReinforcement]: List of mapped locations to applied reinforcement.
StrategyOutput: Output data structure containing the selected reinforcements for each location.
"""
pass
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ def test_summary_cost_csv_exporter_export(

# 3. Validate results
assert _export_path.exists()
_read_text = _export_path.read_text()
_read_text = _export_path.read_text(encoding="utf-8")
_expected_text = """Profile type;Kistdam;Kwelscherm;Grondmaatregel profiel;Stabiliteitswand
Strategy reinforcement order;-1;-1;-1;-1
Cost per km (Euro/km);0.0;8144.4;16288.8;24433.2
Cost per km incl surtax (Euro/km);0.0;12216.6;24433.2;36649.8
New grass volume (quantity):;0.0;0.0;0.0;0.0
Expand Down
9 changes: 7 additions & 2 deletions tests/cost_report/io/summary/test_koswat_summary_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ def test_koswat_summary_exporter_export_given_valid_data(

# 3. Validate results
assert _expected_costs_summary.exists()
assert len(_expected_costs_summary.read_text().splitlines()) == 46
assert (
len(_expected_costs_summary.read_text(encoding="utf-8").splitlines()) == 47
)
assert _expected_locations_summary.exists()
assert len(_expected_locations_summary.read_text().splitlines()) == 5
assert (
len(_expected_locations_summary.read_text(encoding="utf-8").splitlines())
== 5
)
Loading

0 comments on commit 731b57b

Please sign in to comment.