flood-adapt 0.3.2__py3-none-any.whl → 0.3.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flood_adapt/__init__.py +1 -1
- flood_adapt/adapter/fiat_adapter.py +35 -1
- flood_adapt/config/config.py +58 -36
- flood_adapt/config/gui.py +189 -82
- flood_adapt/database_builder/database_builder.py +86 -59
- flood_adapt/dbs_classes/database.py +16 -53
- flood_adapt/dbs_classes/dbs_event.py +4 -1
- flood_adapt/dbs_classes/dbs_measure.py +7 -7
- flood_adapt/dbs_classes/dbs_projection.py +4 -1
- flood_adapt/dbs_classes/dbs_scenario.py +17 -8
- flood_adapt/dbs_classes/dbs_strategy.py +7 -5
- flood_adapt/dbs_classes/dbs_template.py +21 -22
- flood_adapt/dbs_classes/interface/database.py +0 -8
- flood_adapt/dbs_classes/interface/element.py +1 -1
- flood_adapt/flood_adapt.py +20 -13
- flood_adapt/workflows/benefit_runner.py +4 -1
- flood_adapt/workflows/scenario_runner.py +8 -7
- flood_adapt-0.3.3.dist-info/LICENSE +674 -0
- flood_adapt-0.3.3.dist-info/METADATA +859 -0
- {flood_adapt-0.3.2.dist-info → flood_adapt-0.3.3.dist-info}/RECORD +23 -23
- flood_adapt-0.3.2.dist-info/LICENSE +0 -21
- flood_adapt-0.3.2.dist-info/METADATA +0 -182
- /flood_adapt/database_builder/templates/{mapbox_layers → output_layers}/bin_colors.toml +0 -0
- {flood_adapt-0.3.2.dist-info → flood_adapt-0.3.3.dist-info}/WHEEL +0 -0
- {flood_adapt-0.3.2.dist-info → flood_adapt-0.3.3.dist-info}/top_level.txt +0 -0
|
@@ -34,12 +34,16 @@ from flood_adapt.config.fiat import (
|
|
|
34
34
|
SVIModel,
|
|
35
35
|
)
|
|
36
36
|
from flood_adapt.config.gui import (
|
|
37
|
+
AggregationDmgLayer,
|
|
38
|
+
BenefitsLayer,
|
|
39
|
+
FloodMapLayer,
|
|
40
|
+
FootprintsDmgLayer,
|
|
37
41
|
GuiModel,
|
|
38
42
|
GuiUnitModel,
|
|
39
|
-
|
|
43
|
+
OutputLayers,
|
|
40
44
|
PlottingModel,
|
|
41
45
|
SyntheticTideModel,
|
|
42
|
-
|
|
46
|
+
VisualizationLayers,
|
|
43
47
|
)
|
|
44
48
|
from flood_adapt.config.sfincs import (
|
|
45
49
|
Cstype,
|
|
@@ -645,6 +649,8 @@ class DatabaseBuilder:
|
|
|
645
649
|
# Update elevations
|
|
646
650
|
self.update_fiat_elevation()
|
|
647
651
|
|
|
652
|
+
self._svi = self.create_svi()
|
|
653
|
+
|
|
648
654
|
config = FiatConfigModel(
|
|
649
655
|
exposure_crs=self.fiat_model.exposure.crs,
|
|
650
656
|
floodmap_type=self.read_floodmap_type(),
|
|
@@ -657,7 +663,7 @@ class DatabaseBuilder:
|
|
|
657
663
|
save_simulation=False, # TODO
|
|
658
664
|
infographics=self.config.infographics,
|
|
659
665
|
aggregation=self._aggregation_areas,
|
|
660
|
-
svi=self.
|
|
666
|
+
svi=self._svi,
|
|
661
667
|
)
|
|
662
668
|
|
|
663
669
|
# Update output geoms names
|
|
@@ -1489,23 +1495,28 @@ class DatabaseBuilder:
|
|
|
1489
1495
|
|
|
1490
1496
|
### SITE ###
|
|
1491
1497
|
def create_site_config(self) -> Site:
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1498
|
+
"""Create the site configuration for the FloodAdapt model.
|
|
1499
|
+
|
|
1500
|
+
The order of these functions is important!
|
|
1501
|
+
1. Create the SFINCS model.
|
|
1502
|
+
needs: water level references
|
|
1503
|
+
provides: updated water level references with optional tide gauge
|
|
1504
|
+
2. Create the FIAT model.
|
|
1505
|
+
needs: water level references and optional probabilistic event set
|
|
1506
|
+
provides: svi and exposure geometries
|
|
1507
|
+
3. Create the GUI model. (requires water level references and FIAT model to be updated)
|
|
1508
|
+
needs: water level references and FIAT model to be updated
|
|
1509
|
+
provides: gui model with output layers, visualization layers and plotting.
|
|
1497
1510
|
|
|
1498
|
-
|
|
1511
|
+
"""
|
|
1512
|
+
sfincs = self.create_sfincs_config()
|
|
1499
1513
|
self.add_probabilistic_set()
|
|
1500
|
-
|
|
1501
|
-
# Create the FIAT configuration
|
|
1502
1514
|
fiat = self.create_fiat_model()
|
|
1503
|
-
|
|
1515
|
+
gui = self.create_gui_config()
|
|
1504
1516
|
|
|
1505
|
-
#
|
|
1517
|
+
# Order doesnt matter from here
|
|
1518
|
+
lon, lat = self.read_location()
|
|
1506
1519
|
std_objs = self.set_standard_objects()
|
|
1507
|
-
|
|
1508
|
-
# Description of site
|
|
1509
1520
|
description = (
|
|
1510
1521
|
self.config.description if self.config.description else self.config.name
|
|
1511
1522
|
)
|
|
@@ -1538,7 +1549,7 @@ class DatabaseBuilder:
|
|
|
1538
1549
|
gui = GuiModel(
|
|
1539
1550
|
units=self.unit_system,
|
|
1540
1551
|
plotting=self.create_hazard_plotting_config(),
|
|
1541
|
-
|
|
1552
|
+
output_layers=self.create_output_layers_config(),
|
|
1542
1553
|
visualization_layers=self.create_visualization_layers(),
|
|
1543
1554
|
)
|
|
1544
1555
|
|
|
@@ -1554,57 +1565,73 @@ class DatabaseBuilder:
|
|
|
1554
1565
|
f"Unit system {self.config.unit_system} not recognized. Please choose 'imperial' or 'metric'."
|
|
1555
1566
|
)
|
|
1556
1567
|
|
|
1557
|
-
def create_visualization_layers(self) ->
|
|
1558
|
-
visualization_layers =
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1568
|
+
def create_visualization_layers(self) -> VisualizationLayers:
|
|
1569
|
+
visualization_layers = VisualizationLayers()
|
|
1570
|
+
if self._svi is not None:
|
|
1571
|
+
visualization_layers.add_layer(
|
|
1572
|
+
name="svi",
|
|
1573
|
+
long_name="Social Vulnerability Index (SVI)",
|
|
1574
|
+
path=str(self.static_path / self._svi.geom),
|
|
1575
|
+
database_path=self.root,
|
|
1576
|
+
field_name="SVI",
|
|
1577
|
+
bins=[0.05, 0.2, 0.4, 0.6, 0.8],
|
|
1578
|
+
)
|
|
1569
1579
|
return visualization_layers
|
|
1570
1580
|
|
|
1571
|
-
def
|
|
1581
|
+
def create_output_layers_config(self) -> OutputLayers:
|
|
1572
1582
|
# Read default colors from template
|
|
1573
1583
|
fd_max = self.config.gui.max_flood_depth
|
|
1574
1584
|
ad_max = self.config.gui.max_aggr_dmg
|
|
1575
1585
|
ftd_max = self.config.gui.max_footprint_dmg
|
|
1576
1586
|
b_max = self.config.gui.max_benefits
|
|
1577
1587
|
|
|
1578
|
-
|
|
1579
|
-
if self.config.
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
0.
|
|
1591
|
-
|
|
1592
|
-
0.5 * ad_max,
|
|
1593
|
-
ad_max,
|
|
1594
|
-
],
|
|
1595
|
-
footprints_dmg_bins=[
|
|
1596
|
-
0.00001,
|
|
1597
|
-
0.06 * ftd_max,
|
|
1598
|
-
0.2 * ftd_max,
|
|
1599
|
-
0.4 * ftd_max,
|
|
1600
|
-
ftd_max,
|
|
1601
|
-
],
|
|
1602
|
-
benefits_bins=[0, 0.01, 0.02 * b_max, 0.2 * b_max, b_max],
|
|
1603
|
-
svi_bins=svi_bins,
|
|
1604
|
-
**self._get_bin_colors(),
|
|
1605
|
-
)
|
|
1588
|
+
benefits_layer = None
|
|
1589
|
+
if self.config.probabilistic_set is not None:
|
|
1590
|
+
benefits_layer = BenefitsLayer(
|
|
1591
|
+
bins=[0, 0.01, 0.02 * b_max, 0.2 * b_max, b_max],
|
|
1592
|
+
colors=[
|
|
1593
|
+
"#FF7D7D",
|
|
1594
|
+
"#FFFFFF",
|
|
1595
|
+
"#DCEDC8",
|
|
1596
|
+
"#AED581",
|
|
1597
|
+
"#7CB342",
|
|
1598
|
+
"#33691E",
|
|
1599
|
+
],
|
|
1600
|
+
threshold=0.0,
|
|
1601
|
+
)
|
|
1606
1602
|
|
|
1607
|
-
|
|
1603
|
+
output_layers = OutputLayers(
|
|
1604
|
+
floodmap=FloodMapLayer(
|
|
1605
|
+
bins=[0.2 * fd_max, 0.6 * fd_max, fd_max],
|
|
1606
|
+
colors=["#D7ECFB", "#8ABDDD", "#1C73A4", "#081D58"],
|
|
1607
|
+
zbmax=-9999,
|
|
1608
|
+
depth_min=0.0,
|
|
1609
|
+
),
|
|
1610
|
+
aggregation_dmg=AggregationDmgLayer(
|
|
1611
|
+
bins=[0.00001, 0.1 * ad_max, 0.25 * ad_max, 0.5 * ad_max, ad_max],
|
|
1612
|
+
colors=[
|
|
1613
|
+
"#FFFFFF",
|
|
1614
|
+
"#FEE9CE",
|
|
1615
|
+
"#FDBB84",
|
|
1616
|
+
"#FC844E",
|
|
1617
|
+
"#E03720",
|
|
1618
|
+
"#860000",
|
|
1619
|
+
],
|
|
1620
|
+
),
|
|
1621
|
+
footprints_dmg=FootprintsDmgLayer(
|
|
1622
|
+
bins=[0.00001, 0.06 * ftd_max, 0.2 * ftd_max, 0.4 * ftd_max, ftd_max],
|
|
1623
|
+
colors=[
|
|
1624
|
+
"#FFFFFF",
|
|
1625
|
+
"#FEE9CE",
|
|
1626
|
+
"#FDBB84",
|
|
1627
|
+
"#FC844E",
|
|
1628
|
+
"#E03720",
|
|
1629
|
+
"#860000",
|
|
1630
|
+
],
|
|
1631
|
+
),
|
|
1632
|
+
benefits=benefits_layer,
|
|
1633
|
+
)
|
|
1634
|
+
return output_layers
|
|
1608
1635
|
|
|
1609
1636
|
def create_hazard_plotting_config(self) -> PlottingModel:
|
|
1610
1637
|
datum_names = [datum.name for datum in self.water_level_references.datums]
|
|
@@ -2159,7 +2186,7 @@ class DatabaseBuilder:
|
|
|
2159
2186
|
"""
|
|
2160
2187
|
templates_path = Path(__file__).parent.resolve().joinpath("templates")
|
|
2161
2188
|
with open(
|
|
2162
|
-
templates_path.joinpath("
|
|
2189
|
+
templates_path.joinpath("output_layers", "bin_colors.toml"), "rb"
|
|
2163
2190
|
) as f:
|
|
2164
2191
|
bin_colors = tomli.load(f)
|
|
2165
2192
|
return bin_colors
|
|
@@ -381,14 +381,6 @@ class Database(IDatabase):
|
|
|
381
381
|
runner = BenefitRunner(self, benefit=benefit)
|
|
382
382
|
runner.run_cost_benefit()
|
|
383
383
|
|
|
384
|
-
def update(self) -> None:
|
|
385
|
-
self.projections_list = self._projections.list_objects()
|
|
386
|
-
self.events_list = self._events.list_objects()
|
|
387
|
-
self.measures_list = self._measures.list_objects()
|
|
388
|
-
self.strategies_list = self._strategies.list_objects()
|
|
389
|
-
self.scenarios_list = self._scenarios.list_objects()
|
|
390
|
-
self.benefits_list = self._benefits.list_objects()
|
|
391
|
-
|
|
392
384
|
def get_outputs(self) -> dict[str, Any]:
|
|
393
385
|
"""Return a dictionary with info on the outputs that currently exist in the database.
|
|
394
386
|
|
|
@@ -397,7 +389,7 @@ class Database(IDatabase):
|
|
|
397
389
|
dict[str, Any]
|
|
398
390
|
Includes 'name', 'path', 'last_modification_date' and "finished" info
|
|
399
391
|
"""
|
|
400
|
-
all_scenarios = pd.DataFrame(self._scenarios.
|
|
392
|
+
all_scenarios = pd.DataFrame(self._scenarios.summarize_objects())
|
|
401
393
|
if len(all_scenarios) > 0:
|
|
402
394
|
df = all_scenarios[all_scenarios["finished"]]
|
|
403
395
|
else:
|
|
@@ -585,17 +577,17 @@ class Database(IDatabase):
|
|
|
585
577
|
"""
|
|
586
578
|
match object_type:
|
|
587
579
|
case "projections":
|
|
588
|
-
return self.projections.
|
|
580
|
+
return self.projections.summarize_objects()
|
|
589
581
|
case "events":
|
|
590
|
-
return self.events.
|
|
582
|
+
return self.events.summarize_objects()
|
|
591
583
|
case "measures":
|
|
592
|
-
return self.measures.
|
|
584
|
+
return self.measures.summarize_objects()
|
|
593
585
|
case "strategies":
|
|
594
|
-
return self.strategies.
|
|
586
|
+
return self.strategies.summarize_objects()
|
|
595
587
|
case "scenarios":
|
|
596
|
-
return self.scenarios.
|
|
588
|
+
return self.scenarios.summarize_objects()
|
|
597
589
|
case "benefits":
|
|
598
|
-
return self.benefits.
|
|
590
|
+
return self.benefits.summarize_objects()
|
|
599
591
|
case _:
|
|
600
592
|
raise ValueError(
|
|
601
593
|
f"Object type '{object_type}' is not valid. Must be one of 'projections', 'events', 'measures', 'strategies' or 'scenarios'."
|
|
@@ -611,16 +603,20 @@ class Database(IDatabase):
|
|
|
611
603
|
scenario_name : str
|
|
612
604
|
name of the scenario to check if needs to be rerun for hazard
|
|
613
605
|
"""
|
|
614
|
-
scenario = self.
|
|
606
|
+
scenario = self.scenarios.get(scenario_name)
|
|
615
607
|
runner = ScenarioRunner(self, scenario=scenario)
|
|
616
608
|
|
|
617
609
|
# Dont do anything if the hazard model has already been run in itself
|
|
618
610
|
if runner.impacts.hazard.has_run:
|
|
619
611
|
return
|
|
620
612
|
|
|
613
|
+
scenarios = [
|
|
614
|
+
self.scenarios.get(scn)
|
|
615
|
+
for scn in self.scenarios.summarize_objects()["name"]
|
|
616
|
+
]
|
|
621
617
|
scns_simulated = [
|
|
622
618
|
sim
|
|
623
|
-
for sim in
|
|
619
|
+
for sim in scenarios
|
|
624
620
|
if self.scenarios.output_path.joinpath(sim.name, "Flooding").is_dir()
|
|
625
621
|
]
|
|
626
622
|
|
|
@@ -630,8 +626,9 @@ class Database(IDatabase):
|
|
|
630
626
|
path_new = self.scenarios.output_path.joinpath(
|
|
631
627
|
scenario.name, "Flooding"
|
|
632
628
|
)
|
|
633
|
-
|
|
634
|
-
|
|
629
|
+
_runner = ScenarioRunner(self, scenario=scn)
|
|
630
|
+
|
|
631
|
+
if _runner.impacts.hazard.has_run: # only copy results if the hazard model has actually finished and skip simulation folders
|
|
635
632
|
shutil.copytree(
|
|
636
633
|
existing,
|
|
637
634
|
path_new,
|
|
@@ -642,40 +639,6 @@ class Database(IDatabase):
|
|
|
642
639
|
f"Hazard simulation is used from the '{scn.name}' scenario"
|
|
643
640
|
)
|
|
644
641
|
|
|
645
|
-
def run_scenario(self, scenario_name: Union[str, list[str]]) -> None:
|
|
646
|
-
"""Run a scenario hazard and impacts.
|
|
647
|
-
|
|
648
|
-
Parameters
|
|
649
|
-
----------
|
|
650
|
-
scenario_name : Union[str, list[str]]
|
|
651
|
-
name(s) of the scenarios to run.
|
|
652
|
-
|
|
653
|
-
Raises
|
|
654
|
-
------
|
|
655
|
-
RuntimeError
|
|
656
|
-
If an error occurs while running one of the scenarios
|
|
657
|
-
"""
|
|
658
|
-
if not isinstance(scenario_name, list):
|
|
659
|
-
scenario_name = [scenario_name]
|
|
660
|
-
|
|
661
|
-
errors = []
|
|
662
|
-
|
|
663
|
-
for scn in scenario_name:
|
|
664
|
-
try:
|
|
665
|
-
self.has_run_hazard(scn)
|
|
666
|
-
scenario = self.scenarios.get(scn)
|
|
667
|
-
runner = ScenarioRunner(self, scenario=scenario)
|
|
668
|
-
runner.run(scenario)
|
|
669
|
-
except RuntimeError as e:
|
|
670
|
-
errors.append(scn)
|
|
671
|
-
self.logger.error(f"Error running scenario {scn}: {e}")
|
|
672
|
-
if errors:
|
|
673
|
-
raise RuntimeError(
|
|
674
|
-
"FloodAdapt failed to run for the following scenarios: "
|
|
675
|
-
+ ", ".join(errors)
|
|
676
|
-
+ ". Check the logs for more information."
|
|
677
|
-
)
|
|
678
|
-
|
|
679
642
|
def cleanup(self) -> None:
|
|
680
643
|
"""
|
|
681
644
|
Remove corrupted scenario output.
|
|
@@ -67,7 +67,10 @@ class DbsEvent(DbsTemplate[Event]):
|
|
|
67
67
|
list of scenarios that use the event
|
|
68
68
|
"""
|
|
69
69
|
# Get all the scenarios
|
|
70
|
-
scenarios =
|
|
70
|
+
scenarios = [
|
|
71
|
+
self._database.scenarios.get(scn)
|
|
72
|
+
for scn in self._database.scenarios.summarize_objects()["name"]
|
|
73
|
+
]
|
|
71
74
|
|
|
72
75
|
# Check if event is used in a scenario
|
|
73
76
|
used_in_scenario = [
|
|
@@ -37,20 +37,17 @@ class DbsMeasure(DbsTemplate[Measure]):
|
|
|
37
37
|
# Load and return the object
|
|
38
38
|
return MeasureFactory.get_measure_object(full_path)
|
|
39
39
|
|
|
40
|
-
def
|
|
40
|
+
def summarize_objects(self) -> dict[str, list[Any]]:
|
|
41
41
|
"""Return a dictionary with info on the measures that currently exist in the database.
|
|
42
42
|
|
|
43
43
|
Returns
|
|
44
44
|
-------
|
|
45
45
|
dict[str, Any]
|
|
46
|
-
Includes 'name', 'description', 'path' and 'last_modification_date' info
|
|
46
|
+
Includes 'name', 'description', 'path' and 'last_modification_date' and 'geometry' info
|
|
47
47
|
"""
|
|
48
|
-
measures = self.
|
|
48
|
+
measures = self._get_object_summary()
|
|
49
49
|
objects = [self.get(name) for name in measures["name"]]
|
|
50
50
|
|
|
51
|
-
measures["description"] = [obj.description for obj in objects]
|
|
52
|
-
measures["objects"] = objects
|
|
53
|
-
|
|
54
51
|
geometries = []
|
|
55
52
|
for obj in objects:
|
|
56
53
|
# If polygon is used read the polygon file
|
|
@@ -99,7 +96,10 @@ class DbsMeasure(DbsTemplate[Measure]):
|
|
|
99
96
|
list of strategies that use the measure
|
|
100
97
|
"""
|
|
101
98
|
# Get all the strategies
|
|
102
|
-
strategies =
|
|
99
|
+
strategies = [
|
|
100
|
+
self._database.strategies.get(strategy)
|
|
101
|
+
for strategy in self._database.strategies.summarize_objects()["name"]
|
|
102
|
+
]
|
|
103
103
|
|
|
104
104
|
# Check if measure is used in a strategy
|
|
105
105
|
used_in_strategy = [
|
|
@@ -42,7 +42,10 @@ class DbsProjection(DbsTemplate[Projection]):
|
|
|
42
42
|
list of scenarios that use the projection
|
|
43
43
|
"""
|
|
44
44
|
# Get all the scenarios
|
|
45
|
-
scenarios =
|
|
45
|
+
scenarios = [
|
|
46
|
+
self._database.scenarios.get(scn)
|
|
47
|
+
for scn in self._database.scenarios.summarize_objects()["name"]
|
|
48
|
+
]
|
|
46
49
|
|
|
47
50
|
# Check if projection is used in a scenario
|
|
48
51
|
used_in_scenario = [
|
|
@@ -12,7 +12,7 @@ class DbsScenario(DbsTemplate[Scenario]):
|
|
|
12
12
|
display_name = "Scenario"
|
|
13
13
|
_object_class = Scenario
|
|
14
14
|
|
|
15
|
-
def
|
|
15
|
+
def summarize_objects(self) -> dict[str, list[Any]]:
|
|
16
16
|
"""Return a dictionary with info on the events that currently exist in the database.
|
|
17
17
|
|
|
18
18
|
Returns
|
|
@@ -20,12 +20,18 @@ class DbsScenario(DbsTemplate[Scenario]):
|
|
|
20
20
|
dict[str, Any]
|
|
21
21
|
Includes 'name', 'description', 'path' and 'last_modification_date' info
|
|
22
22
|
"""
|
|
23
|
-
scenarios = super().
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
scenarios["
|
|
23
|
+
scenarios = super().summarize_objects()
|
|
24
|
+
scenarios["Projection"] = [
|
|
25
|
+
self._read_variable_in_toml("projection", path)
|
|
26
|
+
for path in scenarios["path"]
|
|
27
|
+
]
|
|
28
|
+
scenarios["Event"] = [
|
|
29
|
+
self._read_variable_in_toml("event", path) for path in scenarios["path"]
|
|
30
|
+
]
|
|
31
|
+
scenarios["Strategy"] = [
|
|
32
|
+
self._read_variable_in_toml("strategy", path) for path in scenarios["path"]
|
|
33
|
+
]
|
|
34
|
+
scenarios["finished"] = [self.has_run_check(scn) for scn in scenarios["name"]]
|
|
29
35
|
|
|
30
36
|
return scenarios
|
|
31
37
|
|
|
@@ -87,7 +93,10 @@ class DbsScenario(DbsTemplate[Scenario]):
|
|
|
87
93
|
list[str]
|
|
88
94
|
list of benefits that use the scenario
|
|
89
95
|
"""
|
|
90
|
-
benefits =
|
|
96
|
+
benefits = [
|
|
97
|
+
self._database.benefits.get(benefit)
|
|
98
|
+
for benefit in self._database.benefits.summarize_objects()["name"]
|
|
99
|
+
]
|
|
91
100
|
used_in_benefit = []
|
|
92
101
|
for benefit in benefits:
|
|
93
102
|
runner = BenefitRunner(database=self._database, benefit=benefit)
|
|
@@ -40,7 +40,7 @@ class DbsStrategy(DbsTemplate[Strategy]):
|
|
|
40
40
|
ValueError
|
|
41
41
|
Raise error if name is already in use.
|
|
42
42
|
"""
|
|
43
|
-
object_exists = object_model.name in self.
|
|
43
|
+
object_exists = object_model.name in self.summarize_objects()["name"]
|
|
44
44
|
|
|
45
45
|
# If you want to overwrite the object, and the object already exists, first delete it. If it exists and you
|
|
46
46
|
# don't want to overwrite, raise an error.
|
|
@@ -137,11 +137,13 @@ class DbsStrategy(DbsTemplate[Strategy]):
|
|
|
137
137
|
list[str]
|
|
138
138
|
list of scenarios that use the strategy
|
|
139
139
|
"""
|
|
140
|
-
|
|
140
|
+
scenarios = [
|
|
141
|
+
self._database.scenarios.get(scn)
|
|
142
|
+
for scn in self._database.scenarios.summarize_objects()["name"]
|
|
143
|
+
]
|
|
144
|
+
|
|
141
145
|
used_in_scenario = [
|
|
142
|
-
scenario.name
|
|
143
|
-
for scenario in self._database.scenarios.list_objects()["objects"]
|
|
144
|
-
if name == scenario.strategy
|
|
146
|
+
scenario.name for scenario in scenarios if name == scenario.strategy
|
|
145
147
|
]
|
|
146
148
|
|
|
147
149
|
return used_in_scenario
|
|
@@ -3,6 +3,7 @@ from datetime import datetime
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Any, TypeVar
|
|
5
5
|
|
|
6
|
+
import tomli
|
|
6
7
|
import tomli_w
|
|
7
8
|
|
|
8
9
|
from flood_adapt.dbs_classes.interface.database import IDatabase
|
|
@@ -47,25 +48,16 @@ class DbsTemplate(AbstractDatabaseElement[T_OBJECTMODEL]):
|
|
|
47
48
|
# Load and return the object
|
|
48
49
|
return self._object_class.load_file(full_path)
|
|
49
50
|
|
|
50
|
-
def
|
|
51
|
+
def summarize_objects(self) -> dict[str, list[Any]]:
|
|
51
52
|
"""Return a dictionary with info on the objects that currently exist in the database.
|
|
52
53
|
|
|
53
54
|
Returns
|
|
54
55
|
-------
|
|
55
56
|
dict[str, list[Any]]
|
|
56
|
-
A dictionary that contains the keys: `name`,
|
|
57
|
+
A dictionary that contains the keys: `name`, `description`, `path` and `last_modification_date`.
|
|
57
58
|
Each key has a list of the corresponding values, where the index of the values corresponds to the same object.
|
|
58
59
|
"""
|
|
59
|
-
|
|
60
|
-
object_list = self._get_object_list()
|
|
61
|
-
|
|
62
|
-
# Load all objects
|
|
63
|
-
objects = [self.get(name) for name in object_list["name"]]
|
|
64
|
-
|
|
65
|
-
# From the loaded objects, get the description and add them to the object_list
|
|
66
|
-
object_list["description"] = [obj.description for obj in objects]
|
|
67
|
-
object_list["objects"] = objects
|
|
68
|
-
return object_list
|
|
60
|
+
return self._get_object_summary()
|
|
69
61
|
|
|
70
62
|
def copy(self, old_name: str, new_name: str, new_description: str):
|
|
71
63
|
"""Copy (duplicate) an existing object, and give it a new name.
|
|
@@ -155,7 +147,7 @@ class DbsTemplate(AbstractDatabaseElement[T_OBJECTMODEL]):
|
|
|
155
147
|
Raise error if name is already in use.
|
|
156
148
|
"""
|
|
157
149
|
# Check if the object exists
|
|
158
|
-
if object_model.name not in self.
|
|
150
|
+
if object_model.name not in self.summarize_objects()["name"]:
|
|
159
151
|
raise ValueError(
|
|
160
152
|
f"{self.display_name}: '{object_model.name}' does not exist. You cannot edit an {self.display_name.lower()} that does not exist."
|
|
161
153
|
)
|
|
@@ -241,13 +233,13 @@ class DbsTemplate(AbstractDatabaseElement[T_OBJECTMODEL]):
|
|
|
241
233
|
# level object. By default, return an empty list
|
|
242
234
|
return []
|
|
243
235
|
|
|
244
|
-
def
|
|
236
|
+
def _get_object_summary(self) -> dict[str, list[Any]]:
|
|
245
237
|
"""Get a dictionary with all the toml paths and last modification dates that exist in the database of the given object_type.
|
|
246
238
|
|
|
247
239
|
Returns
|
|
248
240
|
-------
|
|
249
241
|
dict[str, Any]
|
|
250
|
-
A dictionary that contains the keys: `name`
|
|
242
|
+
A dictionary that contains the keys: `name`, `description`, `path` and `last_modification_date`.
|
|
251
243
|
Each key has a list of the corresponding values, where the index of the values corresponds to the same object.
|
|
252
244
|
"""
|
|
253
245
|
# If the toml doesnt exist, we might be in the middle of saving a new object or could be a broken object.
|
|
@@ -258,19 +250,30 @@ class DbsTemplate(AbstractDatabaseElement[T_OBJECTMODEL]):
|
|
|
258
250
|
if (dir / f"{dir.name}.toml").is_file()
|
|
259
251
|
]
|
|
260
252
|
paths = [Path(dir / f"{dir.name}.toml") for dir in directories]
|
|
261
|
-
|
|
253
|
+
|
|
254
|
+
names = [self._read_variable_in_toml("name", path) for path in paths]
|
|
255
|
+
descriptions = [
|
|
256
|
+
self._read_variable_in_toml("description", path) for path in paths
|
|
257
|
+
]
|
|
258
|
+
|
|
262
259
|
last_modification_date = [
|
|
263
260
|
datetime.fromtimestamp(file.stat().st_mtime) for file in paths
|
|
264
261
|
]
|
|
265
262
|
|
|
266
263
|
objects = {
|
|
267
264
|
"name": names,
|
|
265
|
+
"description": descriptions,
|
|
268
266
|
"path": paths,
|
|
269
267
|
"last_modification_date": last_modification_date,
|
|
270
268
|
}
|
|
271
|
-
|
|
272
269
|
return objects
|
|
273
270
|
|
|
271
|
+
@staticmethod
|
|
272
|
+
def _read_variable_in_toml(variable_name: str, toml_path: Path) -> str:
|
|
273
|
+
with open(toml_path, "rb") as f:
|
|
274
|
+
data = tomli.load(f)
|
|
275
|
+
return data.get(variable_name, "")
|
|
276
|
+
|
|
274
277
|
def _validate_to_save(self, object_model: T_OBJECTMODEL, overwrite: bool) -> None:
|
|
275
278
|
"""Validate if the object can be saved.
|
|
276
279
|
|
|
@@ -285,11 +288,7 @@ class DbsTemplate(AbstractDatabaseElement[T_OBJECTMODEL]):
|
|
|
285
288
|
Raise error if name is already in use.
|
|
286
289
|
"""
|
|
287
290
|
# Check if the object exists
|
|
288
|
-
|
|
289
|
-
raise ValueError(
|
|
290
|
-
f"{self.display_name}: '{object_model.name}' already exists. Choose a different name."
|
|
291
|
-
)
|
|
292
|
-
object_exists = object_model.name in self.list_objects()["name"]
|
|
291
|
+
object_exists = object_model.name in self.summarize_objects()["name"]
|
|
293
292
|
|
|
294
293
|
# If you want to overwrite the object, and the object already exists, first delete it. If it exists and you
|
|
295
294
|
# don't want to overwrite, raise an error.
|
|
@@ -86,10 +86,6 @@ class IDatabase(ABC):
|
|
|
86
86
|
def run_benefit(self, benefit_name: Union[str, list[str]]) -> None:
|
|
87
87
|
pass
|
|
88
88
|
|
|
89
|
-
@abstractmethod
|
|
90
|
-
def update(self) -> None:
|
|
91
|
-
pass
|
|
92
|
-
|
|
93
89
|
@abstractmethod
|
|
94
90
|
def get_outputs(self) -> dict[str, Any]:
|
|
95
91
|
pass
|
|
@@ -138,10 +134,6 @@ class IDatabase(ABC):
|
|
|
138
134
|
def has_run_hazard(self, scenario_name: str) -> None:
|
|
139
135
|
pass
|
|
140
136
|
|
|
141
|
-
@abstractmethod
|
|
142
|
-
def run_scenario(self, scenario_name: Union[str, list[str]]) -> None:
|
|
143
|
-
pass
|
|
144
|
-
|
|
145
137
|
@abstractmethod
|
|
146
138
|
def cleanup(self) -> None:
|
|
147
139
|
pass
|
|
@@ -33,7 +33,7 @@ class AbstractDatabaseElement(ABC, Generic[T_OBJECT_MODEL]):
|
|
|
33
33
|
pass
|
|
34
34
|
|
|
35
35
|
@abstractmethod
|
|
36
|
-
def
|
|
36
|
+
def summarize_objects(self) -> dict[str, list[Any]]:
|
|
37
37
|
"""Return a dictionary with info on the objects that currently exist in the database.
|
|
38
38
|
|
|
39
39
|
Returns
|