Skip to content

Commit 43161d4

Browse files
committed
feat: Add config backward compatibility support
1 parent aa36f2e commit 43161d4

File tree

11 files changed

+76
-25
lines changed

11 files changed

+76
-25
lines changed

cesnet_tszoo/benchmarks.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ def _get_built_in_benchmark(identifier: str, data_root: str) -> Benchmark:
190190
logger.debug("Loading config file from '%s'.", config_file_path)
191191
config = pickle_load(config_file_path)
192192
config.import_identifier = export_benchmark.config_identifier
193+
config._try_update_version()
193194

194195
# Check and load annotations if available
195196
if export_benchmark.annotations_ts_identifier is not None:
@@ -254,6 +255,7 @@ def _get_custom_benchmark(identifier: str, data_root: str) -> Benchmark:
254255
config = pickle_load(config_file_path)
255256

256257
config.import_identifier = export_benchmark.config_identifier
258+
config._try_update_version()
257259

258260
# Load annotations if available
259261
if export_benchmark.annotations_ts_identifier is not None:

cesnet_tszoo/configs/base_config.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77

88
import numpy as np
99
import numpy.typing as npt
10+
from packaging.version import Version
1011
from sklearn.model_selection import train_test_split
1112

13+
import cesnet_tszoo.version as version
1214
from cesnet_tszoo.utils.constants import ROW_END, ROW_START, ID_TIME_COLUMN_NAME, TIME_COLUMN_NAME
1315
from cesnet_tszoo.utils.enums import AgreggationType, FillerType, TimeFormat, ScalerType, DataloaderOrder
1416
from cesnet_tszoo.utils.scaler import Scaler
@@ -60,6 +62,8 @@ class DatasetConfig(ABC):
6062
test_fillers: Fillers used in the test set. `None` if no filler is used or test set is not used.
6163
all_fillers: Fillers used for the all set. `None` if no filler is used or all set is not used.
6264
is_initialized: Flag indicating if the configuration has already been initialized. If true, config initialization will be skipped.
65+
version: Version of cesnet-tszoo this config was made in.
66+
export_update_needed: Whether config was updated to newer version and should be exported.
6367
6468
# Configuration options
6569
@@ -184,6 +188,8 @@ def __init__(self,
184188
self.test_fillers = None
185189
self.all_fillers = None
186190
self.is_initialized = False
191+
self.version = version.current_version
192+
self.export_update_needed = False
187193

188194
self._validate_construction()
189195

@@ -617,3 +623,26 @@ def _set_fillers(self) -> None:
617623
def _validate_finalization(self) -> None:
618624
"""Performs final validation of the configuration. """
619625
...
626+
627+
def _try_update_version(self) -> None:
628+
"""Tries to update config to match newer version of library. """
629+
630+
self.logger.debug("Trying to update config if necessary.")
631+
632+
if not hasattr(self, "version"):
633+
self.logger.warning("Config attribute 'version' is missing in this instance. Default version '%s' will be set.", version.DEFAULT_VERSION)
634+
self.version = version.DEFAULT_VERSION
635+
636+
if Version(self.version) < Version(version.current_version):
637+
self.logger.warning("Imported config was made for cesnet-tszoo package of version '%s', but current used cesnet-tszoo package version is '%s'!", self.version, version.current_version)
638+
self.logger.warning("Package will try to update the config. It is recommended to recreate this config or at least export this config alone or through benchmark to create updated config file.")
639+
self.export_update_needed = True
640+
elif Version(self.version) > Version(version.current_version):
641+
self.logger.error("Imported config was made for cesnet-tszoo package of version '%s', but current used cesnet-tszoo package version is '%s'!", self.version, version.current_version)
642+
self.logger.error("Update cesnet-tszoo package to use this config.")
643+
raise ValueError(f"Imported config was made for cesnet-tszoo package of version '{self.version}', but current used cesnet-tszoo package version is '{version.current_version}'!")
644+
else:
645+
self.export_update_needed = False
646+
647+
self.logger.debug("Setting config version to current used cesnet-tszoo package version.")
648+
self.version = version.current_version

cesnet_tszoo/configs/series_based_config.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,9 @@ def _validate_finalization(self) -> None:
414414
self.logger.error("Overlap detected! Train, Val, and Test sets can't have the same IDs.")
415415
raise ValueError("Train, Val, and Test can't have the same IDs.")
416416

417+
def _try_update_version(self):
418+
super()._try_update_version()
419+
417420
def __str__(self) -> str:
418421

419422
if self.scale_with is None:
@@ -465,5 +468,6 @@ def __str__(self) -> str:
465468
Other
466469
Nan threshold: {str(self.nan_threshold)}
467470
Random state: {self.random_state}
468-
Train dataloader order {str(self.train_dataloader_order)}
471+
Train dataloader order: {str(self.train_dataloader_order)}
472+
Version: {self.version}
469473
'''

cesnet_tszoo/configs/time_based_config.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,9 @@ def _validate_finalization(self) -> None:
631631
self.logger.error("ts_ids and test_ts_ids can't have the same IDs!")
632632
raise ValueError(f"ts_ids and test_ts_ids can't have the same IDs. Overlapping IDs: {self.ts_ids[mask]}")
633633

634+
def _try_update_version(self):
635+
super()._try_update_version()
636+
634637
def __str__(self) -> str:
635638

636639
if self.scale_with is None:
@@ -689,4 +692,5 @@ def __str__(self) -> str:
689692
Other
690693
Nan threshold: {str(self.nan_threshold)}
691694
Random state: {self.random_state}
695+
Version: {self.version}
692696
'''

cesnet_tszoo/datasets/cesnet_dataset.py

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from torch.utils.data import DataLoader, BatchSampler, SequentialSampler, Dataset, RandomSampler
1717
import torch
1818

19+
import cesnet_tszoo.version as version
1920
from cesnet_tszoo.files.utils import get_annotations_path_and_whether_it_is_built_in, get_config_path_and_whether_it_is_built_in, exists_built_in_annotations, exists_built_in_benchmark, exists_built_in_config
2021
from cesnet_tszoo.configs.base_config import DatasetConfig
2122
from cesnet_tszoo.annotation import Annotations
@@ -260,8 +261,8 @@ def get_train_dataloader(self, ts_id: int | None = None, workers: int | Literal[
260261

261262
assert self.train_dataset is not None, "The train_dataset must be initialized before accessing data from training set."
262263

263-
defaultKwargs = {'take_all': False, "cache_loader": True}
264-
kwargs = {**defaultKwargs, **kwargs}
264+
default_kwargs = {'take_all': False, "cache_loader": True}
265+
kwargs = {**default_kwargs, **kwargs}
265266

266267
if ts_id is not None:
267268

@@ -364,8 +365,8 @@ def get_val_dataloader(self, ts_id: int | None = None, workers: int | Literal["c
364365

365366
assert self.val_dataset is not None, "The val_dataset must be initialized before accessing data from validation set."
366367

367-
defaultKwargs = {'take_all': False, "cache_loader": True}
368-
kwargs = {**defaultKwargs, **kwargs}
368+
default_kwargs = {'take_all': False, "cache_loader": True}
369+
kwargs = {**default_kwargs, **kwargs}
369370

370371
if ts_id is not None:
371372

@@ -463,13 +464,13 @@ def get_test_dataloader(self, ts_id: int | None = None, workers: int | Literal["
463464
if self.dataset_config is None or not self.dataset_config.is_initialized:
464465
raise ValueError("Dataset is not initialized. Please call set_dataset_config_and_initialize() before attempting to access test_dataloader.")
465466

466-
if not self.dataset_config.has_all:
467+
if not self.dataset_config.has_test:
467468
raise ValueError("Dataloader for test set is not available in the dataset configuration.")
468469

469470
assert self.test_dataset is not None, "The test_dataset must be initialized before accessing data from test set."
470471

471-
defaultKwargs = {'take_all': False, "cache_loader": True}
472-
kwargs = {**defaultKwargs, **kwargs}
472+
default_kwargs = {'take_all': False, "cache_loader": True}
473+
kwargs = {**default_kwargs, **kwargs}
473474

474475
if ts_id is not None:
475476

@@ -572,8 +573,8 @@ def get_all_dataloader(self, ts_id: int | None = None, workers: int | Literal["c
572573

573574
assert self.all_dataset is not None, "The all_dataset must be initialized before accessing data from all set."
574575

575-
defaultKwargs = {'take_all': False, "cache_loader": True}
576-
kwargs = {**defaultKwargs, **kwargs}
576+
default_kwargs = {'take_all': False, "cache_loader": True}
577+
kwargs = {**default_kwargs, **kwargs}
577578

578579
if ts_id is not None:
579580

@@ -705,7 +706,7 @@ def get_test_df(self, workers: int | Literal["config"] = "config", as_single_dat
705706
if self.dataset_config is None or not self.dataset_config.is_initialized:
706707
raise ValueError("Dataset is not initialized. Please call set_dataset_config_and_initialize() before attempting to access test_dataloader.")
707708

708-
if not self.dataset_config.has_all:
709+
if not self.dataset_config.has_test:
709710
raise ValueError("Dataloader for test set is not available in the dataset configuration.")
710711

711712
assert self.test_dataset is not None, "The test_dataset must be initialized before accessing data from test set."
@@ -823,7 +824,7 @@ def get_test_numpy(self, workers: int | Literal["config"] = "config") -> np.ndar
823824
if self.dataset_config is None or not self.dataset_config.is_initialized:
824825
raise ValueError("Dataset is not initialized. Please call set_dataset_config_and_initialize() before attempting to access test_dataloader.")
825826

826-
if not self.dataset_config.has_all:
827+
if not self.dataset_config.has_test:
827828
raise ValueError("Dataloader for test set is not available in the dataset configuration.")
828829

829830
assert self.test_dataset is not None, "The test_dataset must be initialized before accessing data from test set."
@@ -1544,6 +1545,8 @@ def import_config(self, identifier: str, display_config_details: bool = True, wo
15441545
self.logger.info("Custom config found: %s. Loading it.", identifier)
15451546
config = pickle_load(config_file_path)
15461547

1548+
config._try_update_version()
1549+
15471550
self.logger.info("Initializing dataset configuration with the imported config.")
15481551
self.set_dataset_config_and_initialize(config, display_config_details, workers)
15491552

@@ -1638,6 +1641,7 @@ def save_config(self, identifier: str, create_with_details_file: bool = True, fo
16381641
self.logger.info("Config details saved to %s", path_details)
16391642

16401643
self._update_config_imported_status(identifier)
1644+
self.dataset_config.export_update_needed = False
16411645
self.logger.info("Config successfully saved")
16421646

16431647
def save_benchmark(self, identifier: str, force_write: bool = False) -> None:
@@ -1677,8 +1681,8 @@ def save_benchmark(self, identifier: str, force_write: bool = False) -> None:
16771681
else:
16781682
annotations_both_name = None
16791683

1680-
# Use the imported identifier if available, otherwise default to the current identifier
1681-
config_name = self.dataset_config.import_identifier if self.dataset_config.import_identifier is not None else identifier
1684+
# Use the imported identifier if available and update is not necessary, otherwise default to the current identifier
1685+
config_name = self.dataset_config.import_identifier if (self.dataset_config.import_identifier is not None and not self.dataset_config.export_update_needed) else identifier
16821686

16831687
export_benchmark = ExportBenchmark(self.database_name,
16841688
self.is_series_based,
@@ -1687,10 +1691,11 @@ def save_benchmark(self, identifier: str, force_write: bool = False) -> None:
16871691
config_name,
16881692
annotations_ts_name,
16891693
annotations_time_name,
1690-
annotations_both_name)
1694+
annotations_both_name,
1695+
version=version.current_version)
16911696

16921697
# If the config was not imported, save it
1693-
if self.dataset_config.import_identifier is None:
1698+
if self.dataset_config.import_identifier is None or self.dataset_config.export_update_needed:
16941699
self.save_config(export_benchmark.config_identifier, force_write=force_write)
16951700
else:
16961701
self.logger.info("Using already existing config with identifier: %s", self.dataset_config.import_identifier)

cesnet_tszoo/datasets/series_based_cesnet_dataset.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ def _get_singular_time_series_dataset(self, parent_dataset: SeriesBasedDataset,
498498
def _get_dataloader(self, dataset: SeriesBasedDataset, workers: int | Literal["config"], take_all: bool, batch_size: int, **kwargs) -> DataLoader:
499499
"""Set series based dataloader for this dataset. """
500500

501-
defaultKwargs = {'order': DataloaderOrder.SEQUENTIAL}
502-
kwargs = {**defaultKwargs, **kwargs}
501+
default_kwargs = {'order': DataloaderOrder.SEQUENTIAL}
502+
kwargs = {**default_kwargs, **kwargs}
503503

504504
return self._get_series_based_dataloader(dataset, workers, take_all, batch_size, kwargs["order"])

cesnet_tszoo/datasets/time_based_cesnet_dataset.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -235,13 +235,13 @@ def get_test_other_dataloader(self, ts_id: int | None = None, workers: int | Lit
235235
if self.dataset_config is None or not self.dataset_config.is_initialized:
236236
raise ValueError("Dataset is not initialized. Please call set_dataset_config_and_initialize() before attempting to access test_other_dataloader.")
237237

238-
if not self.dataset_config.has_all:
238+
if not self.dataset_config.has_test or not self.dataset_config.has_test_ts_ids:
239239
raise ValueError("Dataloader for test_other set is not available in the dataset configuration.")
240240

241241
assert self.test_dataset is not None, "The test_other_dataset must be initialized before accessing data from test_other set."
242242

243-
defaultKwargs = {'take_all': False, "cache_loader": True}
244-
kwargs = {**defaultKwargs, **kwargs}
243+
default_kwargs = {'take_all': False, "cache_loader": True}
244+
kwargs = {**default_kwargs, **kwargs}
245245

246246
if ts_id is not None:
247247

@@ -313,7 +313,7 @@ def get_test_other_df(self, workers: int | Literal["config"] = "config", as_sing
313313
if self.dataset_config is None or not self.dataset_config.is_initialized:
314314
raise ValueError("Dataset is not initialized. Please call set_dataset_config_and_initialize() before attempting to access test_other_dataloader.")
315315

316-
if not self.dataset_config.has_all:
316+
if not self.dataset_config.has_test or not self.dataset_config.has_test_ts_ids:
317317
raise ValueError("Dataloader for test_other set is not available in the dataset configuration.")
318318

319319
assert self.test_dataset is not None, "The test_other_dataset must be initialized before accessing data from test_other set."

cesnet_tszoo/files/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def get_benchmark_path_and_whether_it_is_built_in(identifier: str, data_root: st
2121
is_built_in = exists_built_in_benchmark(identifier)
2222

2323
if not is_built_in:
24-
logger.warning("Built-in benchmark %s not found.", identifier)
24+
logger.info("Built-in benchmark %s not found.", identifier)
2525

2626
if is_built_in:
2727
return path_for_built_in_benchmark, True
@@ -31,7 +31,7 @@ def get_benchmark_path_and_whether_it_is_built_in(identifier: str, data_root: st
3131
is_custom = os.path.exists(path_for_custom_benchmark)
3232

3333
if not is_custom:
34-
logger.warning("No benchmark with identifier %s found at expected path: %s.", identifier, path_for_custom_benchmark)
34+
logger.info("No benchmark with identifier %s found at expected path: %s.", identifier, path_for_custom_benchmark)
3535

3636
if is_custom:
3737
return path_for_custom_benchmark, False

cesnet_tszoo/utils/utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class ExportBenchmark:
4545
annotations_time_identifier: str
4646
annotations_both_identifier: str
4747
related_results_identifier: Optional[str] = None
48+
version: str = None
4849
description: Optional[str] = None
4950

5051
def to_dict(self):

cesnet_tszoo/version.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import importlib.metadata
2+
3+
current_version = importlib.metadata.version("cesnet-tszoo")
4+
DEFAULT_VERSION = "0.1.2"

0 commit comments

Comments
 (0)