• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rl-institut / multi-vector-simulator / 8870538658

28 Apr 2024 09:31PM UTC coverage: 75.582% (-1.4%) from 76.96%
8870538658

push

github

web-flow
Merge pull request #971 from rl-institut/fix/black-vulnerability

Fix/black vulnerability

26 of 29 new or added lines in 15 files covered. (89.66%)

826 existing lines in 21 files now uncovered.

5977 of 7908 relevant lines covered (75.58%)

0.76 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.97
/src/multi_vector_simulator/utils/__init__.py
1
import os
1✔
2
import copy
1✔
3
import json
1✔
4
import shutil
1✔
5
import logging
1✔
6
import warnings
1✔
7
import pandas as pd
1✔
8
from .constants import (
1✔
9
    PACKAGE_DATA_PATH,
10
    JSON_FNAME,
11
    CSV_ELEMENTS,
12
    OUTPUT_FOLDER,
13
    JSON_EXT,
14
    CSV_EXT,
15
    REQUIRED_MVS_PARAMETERS,
16
    KNOWN_EXTRA_PARAMETERS,
17
    JSON_FNAME,
18
    MISSING_PARAMETERS_KEY,
19
    EXTRA_PARAMETERS_KEY,
20
    TEMPLATE_INPUT_FOLDER,
21
    DEFAULT_VALUE,
22
    WARNING_TEXT,
23
)
24

25
from .constants_json_strings import (
1✔
26
    UNIT,
27
    VALUE,
28
    PROJECT_DATA,
29
    ECONOMIC_DATA,
30
    SIMULATION_SETTINGS,
31
    CONSTRAINTS,
32
    ENERGY_CONSUMPTION,
33
    ENERGY_CONVERSION,
34
    ENERGY_PRODUCTION,
35
    ENERGY_STORAGE,
36
    ENERGY_BUSSES,
37
    ENERGY_PROVIDERS,
38
    FIX_COST,
39
)
40

41
from .exceptions import MissingParameterError
1✔
42

43

44
class ParameterDocumentation:
1✔
45
    """Helper to access a parameter's information given its variable name"""
46

47
    def __init__(
1✔
48
        self,
49
        param_info_file,
50
        label_header="label",
51
    ):
52
        self.param_doc = pd.read_csv(param_info_file).set_index(label_header)
1✔
53
        self.label_hdr = label_header
1✔
54
        self.fname = param_info_file
1✔
55
        self.param_format = {"numeric": float, "str": str, "boolean": bool}
1✔
56

57
    @property
1✔
58
    def where_to_find_param_documentation(self):
1✔
59
        return (
×
60
            "*" * 5
61
            + f" Note: The documentation about each of the MVS parameters can be found in the csv file {self.fname}. "
62
            + "*" * 5
63
        )
64

65
    def __get_doc_parameter_info(self, param_label, column_name):
1✔
66
        """Search the value of a parameter information in the parameter doc
67

68
        Parameters
69
        ----------
70
        param_label: str
71
            name of the variable as referenced in the column "label" of the
72
            documentation csv file
73
        column_name:
74
            name of the documentation csv file's column corresponding to the
75
            desired information about the parameter
76

77
        Returns
78
        -------
79
        str: value of the given parameter information
80
        """
81
        if isinstance(param_label, list):
×
82
            answer = []
×
83
            for p_name in param_label:
×
84
                answer.append(self.__get_doc_parameter_info(p_name, column_name))
×
85
        else:
86
            try:
×
87
                answer = self.param_doc.loc[param_label][column_name]
×
88
            except KeyError as e:
×
89
                raise KeyError(
×
90
                    f"Either {param_label} is not part of the {self.label_hdr} column of the file {self.fname}, or the column {column_name} does not exist in this file"
91
                ).with_traceback(e.__traceback__)
92
        return answer
×
93

94
    def get_doc_verbose(self, param_label):
1✔
95
        answer = self.__get_doc_parameter_info(param_label, "verbose")
×
96
        answer_is_list = True
×
97
        if not isinstance(param_label, list):
×
98
            answer_is_list = False
×
99
            answer = [answer]
×
100
            param_label = [param_label]
×
101

102
        for i in range(len(answer)):
×
103
            if answer[i] == "None":
×
104
                answer[i] = param_label[i].replace("_", " ").title()
×
105
        if answer_is_list is False:
×
106
            answer = answer[0]
×
107
        return answer
×
108

109
    def get_doc_definition(self, param_label):
1✔
110
        return self.__get_doc_parameter_info(param_label, ":Definition:")
×
111

112
    def get_doc_default(self, param_label):
1✔
113
        answer = self.__get_doc_parameter_info(param_label, ":Default:")
×
114
        param_type = self.get_doc_type(param_label)
×
115
        if answer == "None":
×
116
            answer = None
×
117
        else:
118
            answer = self.param_format[param_type](answer)
×
119
        return answer
×
120

121
    def get_doc_unit(self, param_label):
1✔
122
        return self.__get_doc_parameter_info(param_label, ":Unit:")
×
123

124
    def get_doc_type(self, param_label):
1✔
125
        return self.__get_doc_parameter_info(param_label, ":Type:")
×
126

127

128
try:
1✔
129
    mvs_parameter_file = "MVS_parameters_list.csv"
1✔
130
    DOC_PATH = os.path.join(
1✔
131
        os.path.dirname(
132
            os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
133
        ),
134
        "docs",
135
    )
136
    if os.path.exists(DOC_PATH):
1✔
137
        PARAMETERS_DOC = ParameterDocumentation(
1✔
138
            param_info_file=os.path.join(DOC_PATH, mvs_parameter_file)
139
        )
140
    elif os.path.exists(PACKAGE_DATA_PATH):
×
141
        PARAMETERS_DOC = ParameterDocumentation(
×
142
            param_info_file=os.path.join(PACKAGE_DATA_PATH, mvs_parameter_file)
143
        )
144
    else:
145
        PARAMETERS_DOC = None
×
146

147
except FileNotFoundError:
×
148
    PARAMETERS_DOC = None
×
149

150

151
def find_json_input_folders(
1✔
152
    path, specific_file_name=JSON_FNAME, ignore_folders=(OUTPUT_FOLDER,)
153
):
154
    """Recursively look in the folder structure until is sees a specific file
155

156
    Parameters
157
    ----------
158
    path: str
159
        the starting point of the search in the folder structure
160
    specific_file_name: str
161
        the name of the special file which should be present within a folder to add this folder
162
        name to the list of matching folders
163
    ignore_folders: tuple of str
164
        a tuple of folder names which should not be investigated by the function, nor added to
165
        the list of matching folders
166

167
    Returns
168
    -------
169
    A list of paths to folders containing a specific file
170
    """
171
    folder_list = [
1✔
172
        fn.name
173
        for fn in os.scandir(path)
174
        if fn.is_dir() and fn.name not in ignore_folders
175
    ]
176

177
    if os.path.exists(os.path.join(path, JSON_FNAME)):
1✔
178
        answer = [path]
1✔
179
    else:
180
        answer = []
1✔
181
        for folder in folder_list:
1✔
182
            answer = answer + find_json_input_folders(
1✔
183
                os.path.join(path, folder),
184
                specific_file_name=specific_file_name,
185
                ignore_folders=ignore_folders,
186
            )
187
    return answer
1✔
188

189

190
def find_csv_input_folders(
1✔
191
    path, specific_folder_name=CSV_ELEMENTS, ignore_folders=(OUTPUT_FOLDER,)
192
):
193
    """Recursively look in the folder structure until is sees a specific folder
194

195
    Parameters
196
    ----------
197
    path: str
198
        the starting point of the search in the folder structure
199
    specific_folder_name: str
200
        the name of the special folder which should be present within a folder to add this folder
201
        name to the list of matching folders
202
    ignore_folders: tuple of str
203
        a tuple of folder names which should not be investigated by the function, nor added to
204
        the list of matching folders
205

206
    Returns
207
    -------
208
    A list of paths to folders containing a specific folder
209
    """
210

211
    folder_list = [
1✔
212
        fn.name
213
        for fn in os.scandir(path)
214
        if fn.is_dir() and fn.name not in ignore_folders
215
    ]
216
    if CSV_ELEMENTS in folder_list:
1✔
217
        return [path]
1✔
218
    else:
219
        answer = []
1✔
220
        for folder in folder_list:
1✔
221
            answer = answer + find_csv_input_folders(
1✔
222
                os.path.join(path, folder), specific_folder_name=specific_folder_name
223
            )
224
        return answer
1✔
225

226

227
def compare_input_parameters_with_reference(
1✔
228
    folder_path, ext=JSON_EXT, flag_missing=False, set_default=False
229
):
230
    """Compare provided MVS input parameters with the required parameters
231

232
    Extra parameters listed in KNOWN_EXTRA_PARAMETERS are not flagged as missing if
233
    not provided, instead they take their default value defined in KNOWN_EXTRA_PARAMETERS
234

235
    Parameters
236
    ----------
237
    folder_path: str
238
        path to the mvs input folder
239
    ext: str
240
        one of {JSON_EXT} or {CSV_EXT}
241
    flag_missing: bool
242
        if True, raise MissingParameterError for each missing required parameter
243
    set_default: bool
244
        if True, set the default value of a missing required parameter which is listed in
245
        KNOWN_EXTRA_PARAMETERS
246

247
    Returns
248
    -------
249
    A dict with the missing parameters under the key {MISSING_PARAMETERS_KEY} and extra parameters
250
    under the key {EXTRA_PARAMETERS_KEY}
251

252
    Notes
253
    -----
254
    #todo This function does not check whether the storage-subassets have all necessary parameters, or if excess parameters are provided.
255
    """
256
    if ext == JSON_EXT:
1✔
257

258
        if isinstance(folder_path, dict):
1✔
259
            # the folder_path argument is already a dict
260
            main_parameters = folder_path
1✔
261
        else:
262
            # load the mvs input json file into a dict
263
            json_file_path = os.path.join(folder_path, JSON_FNAME)
1✔
264
            with open(json_file_path) as fp:
1✔
265
                main_parameters = json.load(fp)
1✔
266

267
    elif ext == CSV_EXT:
1✔
268
        # list the mvs input csv files
269
        folder_csv_path = os.path.join(folder_path, CSV_ELEMENTS)
1✔
270
        main_parameters = [
1✔
271
            fn[:-4] for fn in os.listdir(folder_csv_path) if fn.endswith(".csv")
272
        ]
273

274
    extra_parameters = {}
1✔
275
    missing_parameters = {}
1✔
276

277
    required_parameters = REQUIRED_MVS_PARAMETERS[ext]
1✔
278

279
    for mp in main_parameters:
1✔
280

281
        if mp not in required_parameters.keys():
1✔
282
            # the main parameter is provided but is not required --> extra
283
            extra_parameters[mp] = []
1✔
284
        else:
285
            # the main parameter is provided and required
286
            # --> comparison of the sub parameters with the reference
287
            if ext == JSON_EXT:
1✔
288
                # get the sub parameters from the json structure
289
                if mp in [
1✔
290
                    PROJECT_DATA,
291
                    ECONOMIC_DATA,
292
                    SIMULATION_SETTINGS,
293
                    CONSTRAINTS,
294
                    FIX_COST,
295
                ]:
296
                    # project parameters only
297
                    sub_params = {"non-asset": main_parameters[mp].keys()}
1✔
298
                elif mp in [
1✔
299
                    ENERGY_CONSUMPTION,
300
                    ENERGY_CONVERSION,
301
                    ENERGY_PRODUCTION,
302
                    ENERGY_STORAGE,
303
                    ENERGY_BUSSES,
304
                    ENERGY_PROVIDERS,
305
                ]:
306
                    # dict containing assets
307
                    # TODO this should be modified if the assets are inside a list instead
308
                    # of inside a dict
309
                    sub_params = main_parameters[mp]
1✔
310

311
            elif ext == CSV_EXT:
1✔
312
                # read the csv file, each line corresponds to a sub_parameter
313
                df = pd.read_csv(os.path.join(folder_csv_path, mp + ".csv"))
1✔
314
                sub_params = {"non-asset": df.iloc[:, 0].unique().tolist()}
1✔
315

316
            for k in sub_params:
1✔
317
                sub_parameters = sub_params[k]
1✔
318
                if required_parameters[mp] is not None:
1✔
319
                    # intersect the set of provided sub_parameters with the set of required sub parameters
320
                    not_matching_params = list(
1✔
321
                        set(sub_parameters) ^ set(required_parameters[mp])
322
                    )
323
                else:
324
                    # the parameter is expected to contain user defined names --> those are not checked
325
                    not_matching_params = []
1✔
326

327
                for sp in not_matching_params:
1✔
328
                    if sp in required_parameters[mp]:
1✔
329

330
                        if sp in KNOWN_EXTRA_PARAMETERS and set_default is True:
1✔
331
                            # the sub parameter is not provided but is listed in known extra parameters
332
                            # --> default value is set for this parameter
333
                            if k == "non-asset":
1✔
334
                                main_parameters[mp][sp] = {
×
335
                                    UNIT: KNOWN_EXTRA_PARAMETERS[sp][UNIT],
336
                                    VALUE: KNOWN_EXTRA_PARAMETERS[sp][DEFAULT_VALUE],
337
                                }
338
                                logging.warning(
×
339
                                    f"You are not using the parameter '{sp}' for asset group '{mp}', which "
340
                                    + KNOWN_EXTRA_PARAMETERS[sp][WARNING_TEXT]
341
                                    + f"This parameter is set to it's default value ({KNOWN_EXTRA_PARAMETERS[sp][DEFAULT_VALUE]}),"
342
                                    + " which can influence the results."
343
                                    + "In the next release, this parameter will required."
344
                                )
345
                            else:
346
                                main_parameters[mp][k][sp] = {
1✔
347
                                    UNIT: KNOWN_EXTRA_PARAMETERS[sp][UNIT],
348
                                    VALUE: KNOWN_EXTRA_PARAMETERS[sp][DEFAULT_VALUE],
349
                                }
350
                                logging.warning(
1✔
351
                                    f"You are not using the parameter '{sp}' for asset '{k}' of asset group '{mp}', which "
352
                                    + KNOWN_EXTRA_PARAMETERS[sp][WARNING_TEXT]
353
                                    + f"This parameter is set to it's default value ({KNOWN_EXTRA_PARAMETERS[sp][DEFAULT_VALUE]}),"
354
                                    + " which can influence the results."
355
                                    + "In the next release, this parameter will required."
356
                                )
357
                        elif set_default is True:
1✔
358

359
                            if k == "non-asset":
×
UNCOV
360
                                main_parameters[mp][sp] = {
×
361
                                    VALUE: PARAMETERS_DOC.get_doc_default(sp),
362
                                    UNIT: PARAMETERS_DOC.get_doc_unit(sp),
363
                                }
UNCOV
364
                                logging.warning(
×
365
                                    f"You are not providing a value for the parameter '{sp}' in parameter group '{mp}'"
366
                                    + f"This parameter is then set to it's default value ({PARAMETERS_DOC.get_doc_default(sp)}).\n"
367
                                    + PARAMETERS_DOC.where_to_find_param_documentation
368
                                )
369
                            else:
UNCOV
370
                                main_parameters[mp][k][sp] = {
×
371
                                    VALUE: PARAMETERS_DOC.get_doc_default(sp),
372
                                    UNIT: PARAMETERS_DOC.get_doc_unit(sp),
373
                                }
374

UNCOV
375
                                logging.warning(
×
376
                                    f"You are not providing a value for the parameter '{sp}' of asset '{k}' in asset group '{mp}'"
377
                                    + f"This parameter is then set to it's default value ({PARAMETERS_DOC.get_doc_default(sp)}).\n"
378
                                    + PARAMETERS_DOC.where_to_find_param_documentation
379
                                )
380
                        else:
381
                            # the sub parameter is not provided but is required --> missing
382
                            param_list = missing_parameters.get(mp, [])
1✔
383
                            param_list.append(sp)
1✔
384
                            missing_parameters[mp] = param_list
1✔
385
                    else:
386
                        # the sub parameter is provided but is not required --> extra
387
                        param_list = extra_parameters.get(mp, [])
1✔
388
                        if sp not in param_list:
1✔
389
                            param_list.append(sp)
1✔
390
                            extra_parameters[mp] = param_list
1✔
391

392
    for mp in required_parameters.keys():
1✔
393
        if mp not in main_parameters:
1✔
394
            # the main parameter is not provided but is required --> missing
395
            missing_parameters[mp] = required_parameters[mp]
1✔
396

397
    answer = {}
1✔
398
    if len(missing_parameters) > 0:
1✔
399
        answer[MISSING_PARAMETERS_KEY] = missing_parameters
1✔
400
    if len(extra_parameters) > 0:
1✔
401
        answer[EXTRA_PARAMETERS_KEY] = extra_parameters
1✔
402

403
    if flag_missing is True:
1✔
404
        warn_missing_parameters(answer)
1✔
405

406
    return answer
1✔
407

408

409
def warn_missing_parameters(comparison_with_reference):
1✔
410
    """Raise error for missing parameters
411

412
    Parameters
413
    ----------
414
    comparison_with_reference: dict
415
        dict with possibly two keys: MISSING_PARAMETERS_KEY, EXTRA_PARAMETERS_KEY
416
    Returns
417
    -------
418
    Nothing
419

420
    """
421
    if MISSING_PARAMETERS_KEY in comparison_with_reference:
1✔
422
        error_msg = []
1✔
423

424
        d = comparison_with_reference[MISSING_PARAMETERS_KEY]
1✔
425

426
        error_msg.append(" ")
1✔
427
        error_msg.append(" ")
1✔
428
        error_msg.append(
1✔
429
            "The following parameter groups and sub parameters are missing from input parameters:"
430
        )
431

432
        for asset_group in d.keys():
1✔
433
            error_msg.append(asset_group)
1✔
434
            if d[asset_group] is not None:
1✔
435
                for k in d[asset_group]:
1✔
436
                    error_msg.append(f"\t`{k}` parameter")
1✔
437

438
        raise (MissingParameterError("\n".join(error_msg)))
1✔
439

440

441
def set_nested_value(dct, value, keys):
1✔
442
    r"""Set a value within a nested dict structure given the path within the dict
443

444
    Parameters
445
    ----------
446
    dct: dict
447
        the (potentially nested) dict from which we want to get a value
448
    value: variable type
449
        value to assign within the dict
450
    keys: tuple
451
        Tuple containing the succession of keys which lead to the value within the nested dict
452

453
    Returns
454
    -------
455
    The value under the path within the (potentially nested) dict
456

457
    Example
458
    -------
459
    >>> dct = dict(a=dict(a1=1, a2=2),b=dict(b1=dict(b11=11,b12=dict(b121=121))))
460
    >>> print(set_nested_value(dct, 400,("b", "b1", "b12","b121")))
461
    {'a': {'a1': 1, 'a2': 2}, 'b': {'b1': {'b11': 11, 'b12': {'b121': 400}}}}
462
    """
463
    if isinstance(keys, tuple) is True:
1✔
464
        try:
1✔
465
            answer = copy.deepcopy(dct)
1✔
466
            if keys[0] not in answer:
1✔
467
                raise KeyError(
1✔
468
                    ": pathError: that path does not exist in the nested dict"
469
                )
470
            if len(keys) > 1:
1✔
471
                answer[keys[0]] = set_nested_value(dct[keys[0]], value, keys[1:])
1✔
472
            elif len(keys) == 1:
1✔
473
                # if the value is a dict with structure {VALUE: ..., UNIT: ...}
474
                if isinstance(answer[keys[0]], dict):
1✔
UNCOV
475
                    if VALUE in answer[keys[0]]:
×
476
                        answer[keys[0]][VALUE] = value
×
477
                    else:
UNCOV
478
                        answer[keys[0]] = value
×
479
                else:
480
                    answer[keys[0]] = value
1✔
481
            else:
UNCOV
482
                raise ValueError(
×
483
                    "The tuple argument 'keys' from set_nested_value() should not be empty"
484
                )
485
        except KeyError as e:
1✔
486
            if "pathError" in str(e):
1✔
487
                raise KeyError(keys[0] + ", " + e.args[0])
1✔
UNCOV
488
    elif isinstance(keys, str) is True:
×
UNCOV
489
        return set_nested_value(dct, value, split_nested_path(keys))
×
490
    else:
UNCOV
491
        raise TypeError("The argument 'keys' from set_nested_value() should be a tuple")
×
492
    return answer
1✔
493

494

495
def get_nested_value(dct, keys):
1✔
496
    r"""Get a value from a succession of keys within a nested dict structure
497

498
    Parameters
499
    ----------
500
    dct: dict
501
        the (potentially nested) dict from which we want to get a value
502
    keys: tuple
503
        Tuple containing the succession of keys which lead to the value within the nested dict
504

505
    Returns
506
    -------
507
    The value under the path within the (potentially nested) dict
508

509
    Example
510
    -------
511
    >>> dct = dict(a=dict(a1=1, a2=2),b=dict(b1=dict(b11=11,b12=dict(b121=121))))
512
    >>> print(get_nested_value(dct, ("b", "b1", "b12","b121")))
513
    121
514
    """
515
    if isinstance(keys, tuple) is True:
1✔
516
        try:
1✔
517
            if len(keys) > 1:
1✔
518
                answer = get_nested_value(dct[keys[0]], keys[1:])
1✔
519
            elif len(keys) == 1:
1✔
520
                answer = dct[keys[0]]
1✔
521
            else:
UNCOV
522
                raise ValueError(
×
523
                    "The tuple argument 'keys' from get_nested_value() should not be empty"
524
                )
525
        except KeyError as e:
1✔
526
            if "pathError" in str(e):
1✔
527
                raise KeyError(str(keys[0]) + ", " + str(e))
1✔
528
            else:
529
                raise KeyError(
1✔
530
                    str(keys[0])
531
                    + ": pathError: that path does not exist in the nested dict"
532
                )
533

534
    else:
UNCOV
535
        raise TypeError("The argument 'keys' from get_nested_value() should be a tuple")
×
UNCOV
536
    return answer
×
537

538

539
def split_nested_path(path):
1✔
540
    r"""Separate a single-string path in a nested dict in a list of keys
541

542

543
    Parameters
544
    ----------
545
    path: str or tuple
546
        path within a nested dict which is expressed as a str of a succession of keys separated by
547
        a `.` or a `,`. The order of keys is to be read from left to right.
548

549
    Returns
550
    -------
551
    Tuple containing the succession of keys which lead to the value within the nested dict
552

553
    """
554
    SEPARATORS = (".", ",")
×
555
    keys_list = None
×
556
    if isinstance(path, str):
×
557
        separator_count = 0
×
558
        keys_separator = None
×
559
        for separator in SEPARATORS:
×
UNCOV
560
            if separator in path:
×
561
                if path.count(separator) > 0:
×
UNCOV
562
                    if separator_count > 0:
×
563
                        raise ValueError(
×
564
                            f"The separator of the nested dict's path is not unique"
565
                        )
UNCOV
566
                    separator_count = path.count(separator)
×
UNCOV
567
                    keys_separator = separator
×
UNCOV
568
        if keys_separator is not None:
×
UNCOV
569
            keys_list = tuple(path.split(keys_separator))
×
UNCOV
570
    elif isinstance(path, tuple):
×
UNCOV
571
        keys_list = path
×
572
    else:
UNCOV
573
        raise TypeError("The argument path is not tuple or str type")
×
574

UNCOV
575
    return keys_list
×
576

577

578
def nested_dict_crawler(dct, path=None, path_dct=None):
1✔
579
    r"""A recursive algorithm that crawls through a (nested) dictionary and returns
580
    a dictionary of endkeys mapped to a list of the paths leading to each endkey.
581
    An endkey is defined the last key in the nested dict before a value of type different than dict or the last key
582
    before a dict containing only {"unit": ..., "value": ...}
583
            Parameters
584
            ----------
585
            dct: dict
586
                the (potentially nested) dict from which we want to get the endkeys
587
            path: list
588
                storing the current path that the algorithm is on
589
            path_dct: dict
590
                result dictionary where each key is assigned to its (multiple) paths within the (nested) dictionary
591
            Returns
592
            -------
593
            Dictionary of key and paths to the respective key within the nested dictionary structure
594
            Example
595
            -------
596
            >>> dct = dict(a=dict(a1=1, a2=2),b=dict(b1=dict(b11=11,b12=dict(b121=121))))
597
            >>> nested_dict_crawler(dct)
598
            {
599
                "a1": [("a", "a1")],
600
                "a2": [("a", "a2")],
601
                "b11": [("b", "b1", "b11")],
602
                "b121": [("b", "b1", "b12", "b121")],
603
            }
604
    """
605
    if path is None:
1✔
606
        path = []
1✔
607
    if path_dct is None:
1✔
608
        path_dct = dict()
1✔
609

610
    for key, value in dct.items():
1✔
611
        path.append(key)
1✔
612
        if isinstance(value, dict):
1✔
613
            if "value" in value.keys() and "unit" in value.keys():
1✔
614
                if path[-1] in path_dct:
1✔
UNCOV
615
                    path_dct[path[-1]].append(tuple(path))
×
616
                else:
617
                    path_dct[path[-1]] = [tuple(path)]
1✔
618
            else:
619
                nested_dict_crawler(value, path, path_dct)
1✔
620
        else:
621
            if path[-1] in path_dct:
1✔
622
                path_dct[path[-1]].append(tuple(path))
1✔
623
            else:
624
                path_dct[path[-1]] = [tuple(path)]
1✔
625
        path.pop()
1✔
626
    return path_dct
1✔
627

628

629
def copy_report_assets(path_destination_folder):
1✔
630
    """Copy the css file and eland logo to the path_destination_folder
631

632
    Parameters
633
    ----------
634
    path_destination_folder: str
635
        path where the default report css files and logo should be copied
636

637
    Returns
638
    -------
639
    Path of the destination folder
640

641
    """
642
    assets_folder = os.path.join(path_destination_folder, "report", "assets")
×
UNCOV
643
    if os.path.exists(assets_folder) is False:
×
644
        # copy from the default asset folder from mvs package
UNCOV
645
        try:
×
646
            assets_folder = shutil.copytree(
×
647
                os.path.join(PACKAGE_DATA_PATH, "assets"), assets_folder
648
            )
UNCOV
649
        except FileNotFoundError:
×
UNCOV
650
            assets_folder = shutil.copytree(
×
651
                os.path.join(".", "report", "assets"), assets_folder
652
            )
653
    else:
UNCOV
654
        logging.warning(
×
655
            "The assets folder {} exists already, it will not be replaced by default folder "
656
            "from multi_vector_simulator's package".format(assets_folder)
657
        )
UNCOV
658
    return assets_folder
×
659

660

661
def copy_inputs_template(path_destination_folder):
1✔
662
    """Copy the inputs template folder
663

664
    Parameters
665
    ----------
666
    path_destination_folder: str
667
        path where the inputs template folder should be copied to
668

669
    Returns
670
    -------
671
    Path of the destination folder
672

673
    """
UNCOV
674
    inputs_template_folder = os.path.join(
×
675
        path_destination_folder, TEMPLATE_INPUT_FOLDER
676
    )
677
    if os.path.exists(inputs_template_folder) is False:
×
678
        # copy from the default asset folder from mvs package
UNCOV
679
        try:
×
UNCOV
680
            inputs_template_folder = shutil.copytree(
×
681
                os.path.join(PACKAGE_DATA_PATH, TEMPLATE_INPUT_FOLDER),
682
                inputs_template_folder,
683
            )
UNCOV
684
            logging.info(
×
685
                f"The following folder was successfully created into your local "
686
                f"directory {inputs_template_folder}"
687
            )
UNCOV
688
        except FileNotFoundError:
×
UNCOV
689
            logging.warning(
×
690
                "If you installed the package in develop mode, then you cannot use this command"
691
            )
692
    else:
UNCOV
693
        logging.warning(
×
694
            "The inputs template folder {} exists already, it will not be replaced by default "
695
            "folder from multi_vector_simulator's package".format(TEMPLATE_INPUT_FOLDER)
696
        )
UNCOV
697
    return inputs_template_folder
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc