Skip to content

Commit cd4d6a8

Browse files
authored
Merge pull request #295 from PowerGridModel/feature/use-enum
Code quality improvements: Use new enums of dataset and component type
2 parents 9062334 + 6d2d31d commit cd4d6a8

26 files changed

+621
-509
lines changed

src/power_grid_model_io/converters/base_converter.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from typing import Generic, Optional, Tuple, TypeVar
1111

1212
import structlog
13+
from power_grid_model import DatasetType
1314
from power_grid_model.data_types import Dataset, SingleDataset
1415

1516
from power_grid_model_io.data_stores.base_data_store import BaseDataStore
@@ -54,7 +55,9 @@ def load_input_data(
5455

5556
data = self._load_data(data)
5657
extra_info: ExtraInfo = {}
57-
parsed_data = self._parse_data(data=data, data_type="input", extra_info=extra_info if make_extra_info else None)
58+
parsed_data = self._parse_data(
59+
data=data, data_type=DatasetType.input, extra_info=extra_info if make_extra_info else None
60+
)
5861
if isinstance(parsed_data, list):
5962
raise TypeError("Input data can not be batch data")
6063
return parsed_data, extra_info
@@ -71,7 +74,7 @@ def load_update_data(self, data: Optional[T] = None) -> Dataset:
7174
7275
"""
7376
data = self._load_data(data)
74-
return self._parse_data(data=data, data_type="update", extra_info=None)
77+
return self._parse_data(data=data, data_type=DatasetType.update, extra_info=None)
7578

7679
def load_sym_output_data(self, data: Optional[T] = None) -> Dataset:
7780
"""Load symmetric output data
@@ -85,7 +88,7 @@ def load_sym_output_data(self, data: Optional[T] = None) -> Dataset:
8588
8689
"""
8790
data = self._load_data(data)
88-
return self._parse_data(data=data, data_type="sym_output", extra_info=None)
91+
return self._parse_data(data=data, data_type=DatasetType.sym_output, extra_info=None)
8992

9093
def load_asym_output_data(self, data: Optional[T] = None) -> Dataset:
9194
"""Load asymmetric output data
@@ -99,7 +102,7 @@ def load_asym_output_data(self, data: Optional[T] = None) -> Dataset:
99102
100103
"""
101104
data = self._load_data(data)
102-
return self._parse_data(data=data, data_type="asym_output", extra_info=None)
105+
return self._parse_data(data=data, data_type=DatasetType.asym_output, extra_info=None)
103106

104107
def load_sc_output_data(self, data: Optional[T] = None) -> Dataset:
105108
"""Load sc output data
@@ -113,7 +116,7 @@ def load_sc_output_data(self, data: Optional[T] = None) -> Dataset:
113116
114117
"""
115118
data = self._load_data(data)
116-
return self._parse_data(data=data, data_type="sc_output", extra_info=None)
119+
return self._parse_data(data=data, data_type=DatasetType.sc_output, extra_info=None)
117120

118121
def convert(self, data: Dataset, extra_info: Optional[ExtraInfo] = None) -> T:
119122
"""Convert input/update/(a)sym_output data and optionally extra info.
@@ -182,7 +185,7 @@ def _load_data(self, data: Optional[T]) -> T:
182185
raise ValueError("No data supplied!")
183186

184187
@abstractmethod # pragma: nocover
185-
def _parse_data(self, data: T, data_type: str, extra_info: Optional[ExtraInfo]) -> Dataset:
188+
def _parse_data(self, data: T, data_type: DatasetType, extra_info: Optional[ExtraInfo]) -> Dataset:
186189
pass
187190

188191
@abstractmethod # pragma: nocover

src/power_grid_model_io/converters/pandapower_converter.py

Lines changed: 155 additions & 117 deletions
Large diffs are not rendered by default.

src/power_grid_model_io/converters/pgm_json_converter.py

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@
88
import json
99
import logging
1010
import warnings
11+
from enum import Enum
1112
from pathlib import Path
12-
from typing import Any, Dict, List, Optional, Union
13+
from typing import Any, Dict, List, Optional
1314

1415
import numpy as np
15-
from power_grid_model import initialize_array
16+
from power_grid_model import ComponentType, DatasetType, initialize_array
1617
from power_grid_model.data_types import ComponentList, Dataset, SingleDataset, SinglePythonDataset
1718
from power_grid_model.utils import json_deserialize, json_serialize
1819

@@ -44,23 +45,24 @@ class PgmJsonConverter(BaseConverter[StructuredData]):
4445

4546
def __init__(
4647
self,
47-
source_file: Optional[Union[Path, str]] = None,
48-
destination_file: Optional[Union[Path, str]] = None,
48+
source_file: Optional[Path | str] = None,
49+
destination_file: Optional[Path | str] = None,
4950
log_level: int = logging.INFO,
5051
):
5152
source = JsonFileStore(file_path=Path(source_file)) if source_file else None
5253
destination = JsonFileStore(file_path=Path(destination_file)) if destination_file else None
5354
super().__init__(source=source, destination=destination, log_level=log_level)
5455

55-
def _parse_data(self, data: StructuredData, data_type: str, extra_info: Optional[ExtraInfo]) -> Dataset:
56+
def _parse_data(self, data: StructuredData, data_type: DatasetType, extra_info: Optional[ExtraInfo]) -> Dataset:
5657
"""This function expects Structured data, which can either be a dictionary (single dataset) or a list of
5758
dictionaries (batch dataset). The structured dataset consists of components + attributes that exist within
5859
power-grid-model, but can also contain other data. If this data should be saved for later usage an extra_info
5960
dictionary can be provided when calling this function
6061
6162
Args:
6263
data: Structured data, which can either be a dictionary or a list of dictionaries
63-
data_type: the data type of the dataset, i.e. "input", "update", "sym_output" or "asym_output"
64+
data_type: the data type of the dataset, i.e. DatasetType.input, DatasetType.update,
65+
DatasetType.sym_output or DatasetType.asym_output
6466
extra_info: an optional dictionary where extra component info (that can't be specified in
6567
power-grid-model data) can be specified
6668
data: StructuredData:
@@ -69,7 +71,7 @@ def _parse_data(self, data: StructuredData, data_type: str, extra_info: Optional
6971
7072
Returns:
7173
a dictionary containing the components as keys and their corresponding numpy arrays as values: a
72-
power-grid-model "input" or "update" dataset
74+
power-grid-model DatasetType.input or DatasetType.update dataset
7375
7476
"""
7577
self._log.debug(f"Loading PGM {data_type} data")
@@ -92,13 +94,13 @@ def _parse_data(self, data: StructuredData, data_type: str, extra_info: Optional
9294
return result
9395

9496
def _parse_dataset(
95-
self, data: SinglePythonDataset, data_type: str, extra_info: Optional[ExtraInfo]
97+
self, data: SinglePythonDataset, data_type: DatasetType, extra_info: Optional[ExtraInfo]
9698
) -> SingleDataset:
9799
"""This function parses a single Python dataset and returns a power-grid-model input or update dictionary
98100
99101
Args:
100102
data: a single Python dataset
101-
data_type: the data type of the dataset, i.e. "input" or "update"
103+
data_type: the data type of the dataset, i.e. DatasetType.input or DatasetType.update
102104
extra_info: an optional dictionary where extra component info (that can't be specified in
103105
power-grid-model data) can be specified
104106
data: SinglePythonDataset:
@@ -107,7 +109,7 @@ def _parse_dataset(
107109
108110
Returns:
109111
a dictionary containing the components as keys and their corresponding numpy arrays as values: a
110-
power-grid-model "input" or "update" dataset
112+
power-grid-model DatasetType.input or DatasetType.update dataset
111113
112114
"""
113115
return {
@@ -119,15 +121,15 @@ def _parse_dataset(
119121

120122
@staticmethod
121123
def _parse_component(
122-
objects: ComponentList, component: str, data_type: str, extra_info: Optional[ExtraInfo]
124+
objects: ComponentList, component: ComponentType, data_type: DatasetType, extra_info: Optional[ExtraInfo]
123125
) -> np.ndarray:
124126
"""This function generates a structured numpy array (power-grid-model native) from a structured dataset
125127
126128
Args:
127129
objects: a list with dictionaries, where each dictionary contains all attributes of a component
128130
component: the type of component, eg. node, line, etc. Note: it should be a valid power-grid-model
129131
component
130-
data_type: a string specifying the data type: input/update
132+
data_type: a string specifying the data type: DatasetType.input/DatasetType.update
131133
extra_info: an optional dictionary where extra component info (that can't be specified in
132134
power-grid-model data) can be specified
133135
objects: ComponentList:
@@ -152,13 +154,15 @@ def _parse_component(
152154
try:
153155
array[i][attribute] = value
154156
except ValueError as ex:
155-
raise ValueError(f"Invalid '{attribute}' value for {component} {data_type} data: {ex}") from ex
157+
raise ValueError(
158+
f"Invalid '{attribute}' value for {component.value} {data_type.value} data: {ex}"
159+
) from ex
156160

157161
# If an attribute doesn't exist, it is added to the extra_info lookup table
158162
elif extra_info is not None:
159163
obj_id = obj["id"]
160164
if not isinstance(obj_id, int):
161-
raise ValueError(f"Invalid 'id' value for {component} {data_type} data")
165+
raise ValueError(f"Invalid 'id' value for {component.value} {data_type.value} data")
162166
if obj_id not in extra_info:
163167
extra_info[obj_id] = {}
164168
extra_info[obj_id][attribute] = value
@@ -215,8 +219,9 @@ def _is_batch(data: Dataset) -> bool:
215219
is_sparse_batch = isinstance(array, dict) and "indptr" in array and "data" in array
216220
if is_batch is not None and is_batch != (is_dense_batch or is_sparse_batch):
217221
raise ValueError(
218-
f"Mixed {'' if is_batch else 'non-'}batch data "
219-
f"with {'non-' if is_batch else ''}batch data ({component})."
222+
f"Mixed {'' if is_batch else 'non-'}batch data with "
223+
f"{'non-' if is_batch else ''}batch data ("
224+
f"{component.value if isinstance(component, Enum) else component})."
220225
)
221226
is_batch = is_dense_batch or is_sparse_batch
222227
return bool(is_batch)

src/power_grid_model_io/converters/tabular_converter.py

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@
77

88
import inspect
99
import logging
10+
from enum import Enum
1011
from pathlib import Path
11-
from typing import Any, Collection, Dict, List, Mapping, Optional, Union, cast
12+
from typing import Any, Collection, Dict, List, Mapping, Optional, cast
1213

1314
import numpy as np
1415
import pandas as pd
1516
import yaml
16-
from power_grid_model import initialize_array
17+
from power_grid_model import DatasetType, initialize_array
1718
from power_grid_model.data_types import Dataset
1819

1920
from power_grid_model_io.converters.base_converter import BaseConverter
@@ -89,13 +90,13 @@ def set_mapping(self, mapping: Mapping[str, Any]) -> None:
8990
if "multipliers" in mapping:
9091
self._multipliers = MultiplierMapping(cast(Multipliers, mapping["multipliers"]), logger=self._log)
9192

92-
def _parse_data(self, data: TabularData, data_type: str, extra_info: Optional[ExtraInfo]) -> Dataset:
93+
def _parse_data(self, data: TabularData, data_type: DatasetType, extra_info: Optional[ExtraInfo]) -> Dataset:
9394
"""This function parses tabular data and returns power-grid-model data
9495
9596
Args:
9697
data: TabularData, i.e. a dictionary with the components as keys and pd.DataFrames as values, with
9798
attribute names as columns and their values in the table
98-
data_type: power-grid-model data type, i.e. "input" or "update"
99+
data_type: power-grid-model data type, i.e. DatasetType.input or DatasetType.update
99100
extra_info: an optional dictionary where extra component info (that can't be specified in
100101
power-grid-model data) can be specified
101102
data: TabularData:
@@ -145,9 +146,9 @@ def _parse_data(self, data: TabularData, data_type: str, extra_info: Optional[Ex
145146
def _convert_table_to_component( # pylint: disable = too-many-arguments,too-many-positional-arguments
146147
self,
147148
data: TabularData,
148-
data_type: str,
149+
data_type: str | Enum,
149150
table: str,
150-
component: str,
151+
component: str | Enum,
151152
attributes: InstanceAttributes,
152153
extra_info: Optional[ExtraInfo],
153154
) -> Optional[np.ndarray]:
@@ -157,17 +158,17 @@ def _convert_table_to_component( # pylint: disable = too-many-arguments,too-man
157158
158159
Args:
159160
data: The full dataset with tabular data
160-
data_type: The data type, i.e. "input" or "update"
161+
data_type: The data type, i.e. DatasetType.input or DatasetType.update
161162
table: The name of the table that should be converter
162163
component: the component for which a power-grid-model array should be made
163164
attributes: a dictionary with a mapping from the attribute names in the table to the corresponding
164165
power-grid-model attribute names
165166
extra_info: an optional dictionary where extra component info (that can't be specified in
166167
power-grid-model data) can be specified
167168
data: TabularData:
168-
data_type: str:
169+
data_type: str | Enum:
169170
table: str:
170-
component: str:
171+
component: str | Enum:
171172
attributes: InstanceAttributes:
172173
extra_info: Optional[ExtraInfo]:
173174
@@ -187,13 +188,16 @@ def _convert_table_to_component( # pylint: disable = too-many-arguments,too-man
187188

188189
n_records = np.sum(table_mask) if table_mask is not None else len(data[table])
189190

191+
component_str = component.value if isinstance(component, Enum) else component
192+
data_type_str = data_type.value if isinstance(data_type, Enum) else data_type
193+
190194
try:
191-
pgm_data = initialize_array(data_type=data_type, component_type=component, shape=n_records)
195+
pgm_data = initialize_array(data_type=data_type_str, component_type=component_str, shape=n_records)
192196
except KeyError as ex:
193-
raise KeyError(f"Invalid component type '{component}' or data type '{data_type}'") from ex
197+
raise KeyError(f"Invalid component type '{component_str}' or data type '{data_type_str}'") from ex
194198

195199
if "id" not in attributes:
196-
raise KeyError(f"No mapping for the attribute 'id' for '{component}s'!")
200+
raise KeyError(f"No mapping for the attribute 'id' for '{component_str}s'!")
197201

198202
# Make sure that the "id" column is always parsed first (at least before "extra" is parsed)
199203
attributes_without_filter = {k: v for k, v in attributes.items() if k != "filters"}
@@ -207,7 +211,7 @@ def _convert_table_to_component( # pylint: disable = too-many-arguments,too-man
207211
data=data,
208212
pgm_data=pgm_data,
209213
table=table,
210-
component=component,
214+
component=component_str,
211215
attr=attr,
212216
col_def=col_def,
213217
table_mask=table_mask,
@@ -232,7 +236,7 @@ def _convert_col_def_to_attribute( # pylint: disable = too-many-arguments,too-m
232236
data: TabularData,
233237
pgm_data: np.ndarray,
234238
table: str,
235-
component: str,
239+
component: str | Enum,
236240
attr: str,
237241
col_def: Any,
238242
table_mask: Optional[np.ndarray],
@@ -254,7 +258,7 @@ def _convert_col_def_to_attribute( # pylint: disable = too-many-arguments,too-m
254258
data: TabularData:
255259
pgm_data: np.ndarray:
256260
table: str:
257-
component: str:
261+
component: str | Enum:
258262
attr: str:
259263
col_def: Any:
260264
extra_info: Optional[ExtraInfo]:
@@ -265,12 +269,15 @@ def _convert_col_def_to_attribute( # pylint: disable = too-many-arguments,too-m
265269
"""
266270
# To avoid mistakes, the attributes in the mapping should exist. There is one extra attribute called
267271
# 'extra' in which extra information can be captured.
272+
273+
component_str = component.value if isinstance(component, Enum) else component
274+
268275
if pgm_data.dtype.names is None:
269-
raise ValueError(f"pgm_data for '{component}s' has no attributes defined. (dtype.names is None)")
276+
raise ValueError(f"pgm_data for '{component_str}s' has no attributes defined. (dtype.names is None)")
270277

271278
if attr not in pgm_data.dtype.names and attr not in ["extra", "filters"]:
272279
attrs = ", ".join(pgm_data.dtype.names)
273-
raise KeyError(f"Could not find attribute '{attr}' for '{component}s'. (choose from: {attrs})")
280+
raise KeyError(f"Could not find attribute '{attr}' for '{component_str}s'. (choose from: {attrs})")
274281

275282
if attr == "extra":
276283
# Extra info must be linked to the object IDs, therefore the uuids should be known before extra info can
@@ -421,16 +428,15 @@ def _parse_col_def( # pylint: disable = too-many-arguments,too-many-positional-
421428
def _parse_col_def_const(
422429
data: TabularData,
423430
table: str,
424-
col_def: Union[int, float],
431+
col_def: int | float,
425432
table_mask: Optional[np.ndarray] = None,
426433
) -> pd.DataFrame:
427434
"""Create a single column pandas DataFrame containing the const value.
428435
429436
Args:
430437
data: TabularData:
431438
table: str:
432-
col_def: Union[int:
433-
float]:
439+
col_def: int | float:
434440
435441
Returns:
436442
@@ -602,7 +608,7 @@ def _parse_auto_id( # pylint: disable = too-many-arguments,too-many-positional-
602608
table: str,
603609
ref_table: Optional[str],
604610
ref_name: Optional[str],
605-
key_col_def: Union[str, List[str], Dict[str, str]],
611+
key_col_def: str | List[str] | Dict[str, str],
606612
table_mask: Optional[np.ndarray],
607613
extra_info: Optional[ExtraInfo],
608614
) -> pd.DataFrame:
@@ -845,7 +851,7 @@ def get_id(row: pd.Series) -> int:
845851

846852
return keys.apply(get_id, axis=1).to_list()
847853

848-
def lookup_id(self, pgm_id: int) -> Dict[str, Union[str, Dict[str, int]]]:
854+
def lookup_id(self, pgm_id: int) -> Dict[str, str | Dict[str, int]]:
849855
"""
850856
Retrieve the original name / key combination of a pgm object
851857
Args:

src/power_grid_model_io/converters/vision_excel_converter.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import logging
99
from dataclasses import dataclass
1010
from pathlib import Path
11-
from typing import Any, Mapping, Optional, Union
11+
from typing import Any, Mapping, Optional
1212

1313
from power_grid_model_io.converters.tabular_converter import TabularConverter
1414
from power_grid_model_io.data_stores.base_data_store import LANGUAGE_EN
@@ -37,10 +37,10 @@ class VisionExcelConverter(TabularConverter):
3737

3838
def __init__( # pylint: disable=too-many-arguments,too-many-positional-arguments
3939
self,
40-
source_file: Optional[Union[Path, str]] = None,
40+
source_file: Optional[Path | str] = None,
4141
language: str = LANGUAGE_EN,
4242
terms_changed: Optional[dict] = None,
43-
mapping_file: Optional[Union[Path, str]] = None,
43+
mapping_file: Optional[Path | str] = None,
4444
log_level: int = logging.INFO,
4545
):
4646
_mapping_file = Path(

0 commit comments

Comments
 (0)