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

rl-institut / multi-vector-simulator / 4086231192

pending completion
4086231192

push

github

pierre-francois.duc
Fix constraint bug

5 of 5 new or added lines in 1 file covered. (100.0%)

5941 of 7822 relevant lines covered (75.95%)

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, param_info_file, label_header="label",
49
    ):
50
        self.param_doc = pd.read_csv(param_info_file).set_index(label_header)
1✔
51
        self.label_hdr = label_header
1✔
52
        self.fname = param_info_file
1✔
53
        self.param_format = {"numeric": float, "str": str, "boolean": bool}
1✔
54

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

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

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

75
        Returns
76
        -------
77
        str: value of the given parameter information
78
        """
79
        if isinstance(param_label, list):
×
80
            answer = []
×
81
            for p_name in param_label:
×
82
                answer.append(self.__get_doc_parameter_info(p_name, column_name))
×
83
        else:
84
            try:
×
85
                answer = self.param_doc.loc[param_label][column_name]
×
86
            except KeyError as e:
×
87
                raise KeyError(
×
88
                    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"
89
                ).with_traceback(e.__traceback__)
90
        return answer
×
91

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

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

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

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

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

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

125

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

145
except FileNotFoundError:
×
146
    PARAMETERS_DOC = None
×
147

148

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

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

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

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

187

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

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

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

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

224

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

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

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

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

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

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

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

272
    extra_parameters = {}
1✔
273
    missing_parameters = {}
1✔
274

275
    required_parameters = REQUIRED_MVS_PARAMETERS[ext]
1✔
276

277
    for mp in main_parameters:
1✔
278

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

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

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

325
                for sp in not_matching_params:
1✔
326
                    if sp in required_parameters[mp]:
1✔
327

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

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

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

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

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

401
    if flag_missing is True:
1✔
402
        warn_missing_parameters(answer)
1✔
403

404
    return answer
1✔
405

406

407
def warn_missing_parameters(comparison_with_reference):
1✔
408
    """Raise error for missing parameters
409

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

418
    """
419
    if MISSING_PARAMETERS_KEY in comparison_with_reference:
1✔
420
        error_msg = []
1✔
421

422
        d = comparison_with_reference[MISSING_PARAMETERS_KEY]
1✔
423

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

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

436
        raise (MissingParameterError("\n".join(error_msg)))
1✔
437

438

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

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

451
    Returns
452
    -------
453
    The value under the path within the (potentially nested) dict
454

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

492

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

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

503
    Returns
504
    -------
505
    The value under the path within the (potentially nested) dict
506

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

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

536

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

540

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

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

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

573
    return keys_list
×
574

575

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

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

626

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

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

635
    Returns
636
    -------
637
    Path of the destination folder
638

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

658

659
def copy_inputs_template(path_destination_folder):
1✔
660
    """Copy the inputs template folder
661

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

667
    Returns
668
    -------
669
    Path of the destination folder
670

671
    """
672
    inputs_template_folder = os.path.join(
×
673
        path_destination_folder, TEMPLATE_INPUT_FOLDER
674
    )
675
    if os.path.exists(inputs_template_folder) is False:
×
676
        # copy from the default asset folder from mvs package
677
        try:
×
678
            inputs_template_folder = shutil.copytree(
×
679
                os.path.join(PACKAGE_DATA_PATH, TEMPLATE_INPUT_FOLDER),
680
                inputs_template_folder,
681
            )
682
            logging.info(
×
683
                f"The following folder was successfully created into your local "
684
                f"directory {inputs_template_folder}"
685
            )
686
        except FileNotFoundError:
×
687
            logging.warning(
×
688
                "If you installed the package in develop mode, then you cannot use this command"
689
            )
690
    else:
691
        logging.warning(
×
692
            "The inputs template folder {} exists already, it will not be replaced by default "
693
            "folder from multi_vector_simulator's package".format(TEMPLATE_INPUT_FOLDER)
694
        )
695
    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