fameio 2.3.0__py3-none-any.whl → 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- CHANGELOG.md +28 -0
- fameio/__init__.py +4 -1
- fameio/{source/cli → cli}/__init__.py +2 -0
- fameio/{source/cli → cli}/convert_results.py +8 -8
- fameio/{source/cli → cli}/make_config.py +5 -5
- fameio/{source/cli → cli}/options.py +0 -8
- fameio/{source/cli → cli}/parser.py +26 -63
- fameio/input/__init__.py +27 -0
- fameio/input/loader/__init__.py +68 -0
- fameio/input/loader/controller.py +129 -0
- fameio/input/loader/loader.py +109 -0
- fameio/input/metadata.py +149 -0
- fameio/input/resolver.py +44 -0
- fameio/{source → input}/scenario/__init__.py +1 -2
- fameio/{source → input}/scenario/agent.py +24 -38
- fameio/input/scenario/attribute.py +203 -0
- fameio/{source → input}/scenario/contract.py +50 -61
- fameio/{source → input}/scenario/exception.py +8 -13
- fameio/{source → input}/scenario/fameiofactory.py +6 -6
- fameio/{source → input}/scenario/generalproperties.py +22 -47
- fameio/{source → input}/scenario/scenario.py +34 -31
- fameio/input/scenario/stringset.py +48 -0
- fameio/{source → input}/schema/__init__.py +2 -2
- fameio/input/schema/agenttype.py +125 -0
- fameio/input/schema/attribute.py +268 -0
- fameio/{source → input}/schema/java_packages.py +26 -22
- fameio/{source → input}/schema/schema.py +25 -22
- fameio/{source → input}/validator.py +32 -35
- fameio/{source → input}/writer.py +86 -86
- fameio/{source/logs.py → logs.py} +25 -9
- fameio/{source/results → output}/agent_type.py +21 -22
- fameio/{source/results → output}/conversion.py +34 -31
- fameio/{source/results → output}/csv_writer.py +7 -7
- fameio/{source/results → output}/data_transformer.py +24 -24
- fameio/{source/results → output}/input_dao.py +51 -49
- fameio/{source/results → output}/output_dao.py +16 -17
- fameio/{source/results → output}/reader.py +30 -31
- fameio/{source/results → output}/yaml_writer.py +2 -3
- fameio/scripts/__init__.py +2 -2
- fameio/scripts/convert_results.py +16 -15
- fameio/scripts/make_config.py +9 -9
- fameio/{source/series.py → series.py} +28 -26
- fameio/{source/time.py → time.py} +8 -8
- fameio/{source/tools.py → tools.py} +2 -2
- {fameio-2.3.0.dist-info → fameio-3.0.0.dist-info}/METADATA +277 -72
- fameio-3.0.0.dist-info/RECORD +56 -0
- fameio/source/__init__.py +0 -8
- fameio/source/loader.py +0 -181
- fameio/source/metadata.py +0 -32
- fameio/source/path_resolver.py +0 -34
- fameio/source/scenario/attribute.py +0 -130
- fameio/source/scenario/stringset.py +0 -51
- fameio/source/schema/agenttype.py +0 -132
- fameio/source/schema/attribute.py +0 -203
- fameio/source/schema/exception.py +0 -9
- fameio-2.3.0.dist-info/RECORD +0 -55
- /fameio/{source/results → output}/__init__.py +0 -0
- {fameio-2.3.0.dist-info → fameio-3.0.0.dist-info}/LICENSE.txt +0 -0
- {fameio-2.3.0.dist-info → fameio-3.0.0.dist-info}/LICENSES/Apache-2.0.txt +0 -0
- {fameio-2.3.0.dist-info → fameio-3.0.0.dist-info}/LICENSES/CC-BY-4.0.txt +0 -0
- {fameio-2.3.0.dist-info → fameio-3.0.0.dist-info}/LICENSES/CC0-1.0.txt +0 -0
- {fameio-2.3.0.dist-info → fameio-3.0.0.dist-info}/WHEEL +0 -0
- {fameio-2.3.0.dist-info → fameio-3.0.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
CHANGELOG.md,sha256=PLSHTL6fgGuJ_zdIDiJi_LoJ5HpekTlG2MooflQ14ls,13801
|
2
|
+
fameio/__init__.py,sha256=LiE7kRXW0pMIB4hTPC0T_ppGz9O0swd0Ca1-b99hOMc,229
|
3
|
+
fameio/cli/__init__.py,sha256=xAS0gRfzq1qepCW6PjIozRC6t3DOxzdNvHU9beFOGHU,167
|
4
|
+
fameio/cli/convert_results.py,sha256=-nAAuO_CznggZnZHeGctP_uXcQsQGjTDDZmHAqlBMJQ,3438
|
5
|
+
fameio/cli/make_config.py,sha256=Dbn_ITlhpumc-bK9ONKZyGnyYLXsc8YIWQN9OHLBHe4,2712
|
6
|
+
fameio/cli/options.py,sha256=2XNwCAloiy_lC77C7LEngMvvT7rVmzASeE_JxQlpHak,1257
|
7
|
+
fameio/cli/parser.py,sha256=fR28uzjf6MV3do4AefrXbRu2m4-x0bR-Xn-1Di1e0Lk,8391
|
8
|
+
fameio/input/__init__.py,sha256=mDpz4hKZqpzrxtuvwK-eZl-zwHhegRbiH5ew1LpjaAg,550
|
9
|
+
fameio/input/loader/__init__.py,sha256=ShZq2zBGz3-423Fd6DLCi1wAtD2JDgFiaknJ24lGToU,2659
|
10
|
+
fameio/input/loader/controller.py,sha256=Ph7QvGwbJJ4MNb21nPiYJNTdjpPcRFms95Fd-evzGD0,5800
|
11
|
+
fameio/input/loader/loader.py,sha256=uVCUXN2FqtO058zxDBQth2rabP8BPtInMZnAcI7SX8Q,5060
|
12
|
+
fameio/input/metadata.py,sha256=l28BPnwjb62RRt-tBJ-HVmOJVO1QIRlKdMx-gjoP_mw,5957
|
13
|
+
fameio/input/resolver.py,sha256=86APkFnupWSJNAaX4RP_SOYNv5W3WUwV_I6Iptkur_Q,1917
|
14
|
+
fameio/input/scenario/__init__.py,sha256=azglGZTK39jpkDOmeVAhiLH9zIsUx2SZ_umQIlJiq6Q,322
|
15
|
+
fameio/input/scenario/agent.py,sha256=Zl83x3U1f4nVv1gbCOhjQ911u-GwWXoWR5_QW0cRHtM,4377
|
16
|
+
fameio/input/scenario/attribute.py,sha256=-u-dCzs8lUhSEc98hDot28nl6BLA7sEzAg9GzmNci4k,9635
|
17
|
+
fameio/input/scenario/contract.py,sha256=D6iy-oVNwIqQxeZQ3rITERwIgFRrhzYHRysygn-ceDk,9046
|
18
|
+
fameio/input/scenario/exception.py,sha256=zavHELqzpIJvb_4GhskYQyi2u5Y6LppwWQt-yGecMQY,1434
|
19
|
+
fameio/input/scenario/fameiofactory.py,sha256=_8R3q_e5lHGd75DDbgJvTX6PJNn0i6nZSnW4DyGgdOA,1558
|
20
|
+
fameio/input/scenario/generalproperties.py,sha256=gTSLS0bgoZYAMDYhaFTGcJxipgik9AntSPihQBIcX6k,3504
|
21
|
+
fameio/input/scenario/scenario.py,sha256=f9gIV_FdtS52L_nWdstwSoXlN8gL7ovxGPAaWpEzOjc,4608
|
22
|
+
fameio/input/scenario/stringset.py,sha256=ORW0_4M9CGtB5NQAzQhzZJliHmyW0yQjG_tzMjnRxUw,1814
|
23
|
+
fameio/input/schema/__init__.py,sha256=V1m0Cx97lNeZwaQBFo-a5WMbohdsCwDfvmjtxD2vaBo,271
|
24
|
+
fameio/input/schema/agenttype.py,sha256=NLcdIuMKjFsqhHG9zisYaHDnP7eDOctHWG1yEH9_ugg,5149
|
25
|
+
fameio/input/schema/attribute.py,sha256=4nH-lA0Wh7pe8ZPOWXjuVkPa6Jl5cYQnxU5_e4Y54L0,11369
|
26
|
+
fameio/input/schema/java_packages.py,sha256=jkwEgR3mOGky-cvSgR3pGQEJ84hXN5NZS_oyoeYt-9g,2845
|
27
|
+
fameio/input/schema/schema.py,sha256=-Hm7r9Hka8NDnNZLe9GCeaULcBgvvhHQhEYjxQ4VzTg,3039
|
28
|
+
fameio/input/validator.py,sha256=-X0aEEzKj6s5laxXhDzGcwAa0f4Ssp5waLmuriO-3u0,18359
|
29
|
+
fameio/input/writer.py,sha256=TyFkS3KKdAaXiMq-yLDriwOcy65-P5ZeEtzOtUM1y38,12810
|
30
|
+
fameio/logs.py,sha256=JxT4JkyKCxxB9QybGOhAcncCexmoIx6_dkGczjG-h8A,3992
|
31
|
+
fameio/output/__init__.py,sha256=IQm0MNOXkhBexiMXBoNZDK5xHUYgazH7oXm-lc0Vm04,109
|
32
|
+
fameio/output/agent_type.py,sha256=6niFUKOhxUdGw98NUVNtIM543hAQaCkyy7hacxlc9dU,4328
|
33
|
+
fameio/output/conversion.py,sha256=2DQ6T2AuU0iDrKsUQ1HcM1TO_prqlENtq2n28wPKwcU,3991
|
34
|
+
fameio/output/csv_writer.py,sha256=hU3TM_c8bltY_j_6NZInNB7fyLsNZsYsOrfVvDNYwwE,5117
|
35
|
+
fameio/output/data_transformer.py,sha256=2fBXQ8byS3Lhsmocr2AB5B_I9NmDQrwzIViUtQeKYYg,5473
|
36
|
+
fameio/output/input_dao.py,sha256=aTNdRM_uaVXB8X7EkWZ3eEd1IMH93Bfx8jCpFvhlBVE,6983
|
37
|
+
fameio/output/output_dao.py,sha256=f7xnsjY80E6OolHlFDzWkJG2J86wxXJJ2nRRmgUnVVc,4085
|
38
|
+
fameio/output/reader.py,sha256=wiTCkmETtcZR_ybb4CxnfyTtBF74bTNp2w96fFD8Qo0,5097
|
39
|
+
fameio/output/yaml_writer.py,sha256=YIUKQB10BnDF9LghLf5bD23ryUI340lGxslB5j88_dk,805
|
40
|
+
fameio/scripts/__init__.py,sha256=Owg46sNUIkq7qLJpfa75Cuhjtl3HGkR6l1-LEaNE54A,727
|
41
|
+
fameio/scripts/__init__.py.license,sha256=2-OqCNxP4504xY2XQqseYypJi1_Qx4xJSzO3t7c3ACM,107
|
42
|
+
fameio/scripts/convert_results.py,sha256=Olrw4l9nGzstgdVyhJJthHCKyaTubVXSlM26729Sjmk,4173
|
43
|
+
fameio/scripts/convert_results.py.license,sha256=2-OqCNxP4504xY2XQqseYypJi1_Qx4xJSzO3t7c3ACM,107
|
44
|
+
fameio/scripts/make_config.py,sha256=LvwXbBlaGdKC25BRlk4LJDEwvZzxzCzYyVewtyHhIMM,1351
|
45
|
+
fameio/scripts/make_config.py.license,sha256=2-OqCNxP4504xY2XQqseYypJi1_Qx4xJSzO3t7c3ACM,107
|
46
|
+
fameio/series.py,sha256=BMnJMAdx38duao5w9165b9XB1JRIFOLZ83cjzN5wRUg,9110
|
47
|
+
fameio/time.py,sha256=iiCVpEmBSxHgKft_X-E_D-dpOT-L2Y_xN-6pVFtJhDQ,6949
|
48
|
+
fameio/tools.py,sha256=8Ia-J-mgjf1NCXMvjLDj10hDwEKzp6jS6eq6z8W005w,1056
|
49
|
+
fameio-3.0.0.dist-info/entry_points.txt,sha256=jvQVfwJjZXPWQjJlhj1Dt6PTeblryTc1GxjKeK90twI,123
|
50
|
+
fameio-3.0.0.dist-info/LICENSE.txt,sha256=eGHBZnhr9CWjE95SWjRfmhtK1lvVn5X4Fpf3KrrAZDg,10391
|
51
|
+
fameio-3.0.0.dist-info/LICENSES/Apache-2.0.txt,sha256=eGHBZnhr9CWjE95SWjRfmhtK1lvVn5X4Fpf3KrrAZDg,10391
|
52
|
+
fameio-3.0.0.dist-info/LICENSES/CC-BY-4.0.txt,sha256=y9WvMYKGt0ZW8UXf9QkZB8wj1tjJrQngKR7CSXeSukE,19051
|
53
|
+
fameio-3.0.0.dist-info/LICENSES/CC0-1.0.txt,sha256=9Ofzc7m5lpUDN-jUGkopOcLZC3cl6brz1QhKInF60yg,7169
|
54
|
+
fameio-3.0.0.dist-info/METADATA,sha256=q0ibxLgLicduMZ0Xe9fvYAPaY8FMjOu66_0pTC-QRAQ,37270
|
55
|
+
fameio-3.0.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
56
|
+
fameio-3.0.0.dist-info/RECORD,,
|
fameio/source/__init__.py
DELETED
@@ -1,8 +0,0 @@
|
|
1
|
-
# SPDX-FileCopyrightText: 2023 German Aerospace Center <fame@dlr.de>
|
2
|
-
#
|
3
|
-
# SPDX-License-Identifier: Apache-2.0
|
4
|
-
|
5
|
-
from .path_resolver import PathResolver
|
6
|
-
from .time import FameTime
|
7
|
-
from .validator import SchemaValidator
|
8
|
-
from .writer import ProtoWriter, ProtoWriterException
|
fameio/source/loader.py
DELETED
@@ -1,181 +0,0 @@
|
|
1
|
-
# SPDX-FileCopyrightText: 2024 German Aerospace Center <fame@dlr.de>
|
2
|
-
#
|
3
|
-
# SPDX-License-Identifier: Apache-2.0
|
4
|
-
|
5
|
-
import os
|
6
|
-
from pathlib import Path
|
7
|
-
from fnmatch import fnmatch
|
8
|
-
from typing import IO, Any, Callable
|
9
|
-
|
10
|
-
import yaml
|
11
|
-
from fameio.source.logs import log_and_raise_critical, log
|
12
|
-
from fameio.source.path_resolver import PathResolver
|
13
|
-
|
14
|
-
DISABLING_YAML_FILE_PREFIX = "IGNORE_"
|
15
|
-
|
16
|
-
CRIT_NO_YAML_SUFFIX = "File must have a `.yaml` or `.yml` suffix. Received `-f/--file {}` instead."
|
17
|
-
|
18
|
-
FILE_ENCODINGS = [None]
|
19
|
-
|
20
|
-
|
21
|
-
class Args:
|
22
|
-
def __init__(self, file_string, node_string):
|
23
|
-
self.file_string = file_string
|
24
|
-
self.node_string = node_string
|
25
|
-
|
26
|
-
|
27
|
-
def read_args(loader, args):
|
28
|
-
"""Returns two Strings to be interpreted as files to be read and a sub-node_address from the given `args`"""
|
29
|
-
node_string = ""
|
30
|
-
file_string = None
|
31
|
-
if isinstance(args, yaml.nodes.ScalarNode):
|
32
|
-
file_string = loader.construct_scalar(args)
|
33
|
-
log().debug("Found instance `ScalarNode` in {}".format(file_string))
|
34
|
-
elif isinstance(args, yaml.nodes.SequenceNode):
|
35
|
-
argument_list = loader.construct_sequence(args)
|
36
|
-
if len(argument_list) not in [1, 2]:
|
37
|
-
log_and_raise_critical("!include supports but one or two arguments in list")
|
38
|
-
elif len(argument_list) == 2:
|
39
|
-
node_string = argument_list[1]
|
40
|
-
file_string = argument_list[0]
|
41
|
-
log().debug("Found instance `SequenceNode` in {}".format(file_string))
|
42
|
-
elif isinstance(args, yaml.nodes.MappingNode):
|
43
|
-
argument_map = loader.construct_mapping(args)
|
44
|
-
for key, value in argument_map.items():
|
45
|
-
if key.lower() == "file":
|
46
|
-
file_string = value
|
47
|
-
elif key.lower() == "node":
|
48
|
-
node_string = value
|
49
|
-
else:
|
50
|
-
log_and_raise_critical("!include supports only keys 'file' and 'node'")
|
51
|
-
if not file_string:
|
52
|
-
log_and_raise_critical("!include: file must be specified.")
|
53
|
-
else:
|
54
|
-
log_and_raise_critical("YAML node type not implemented: {}".format(args))
|
55
|
-
return Args(file_string, node_string)
|
56
|
-
|
57
|
-
|
58
|
-
def split_nodes(node_string):
|
59
|
-
"""Returns a list of nodes created from the given `node_string`"""
|
60
|
-
log().debug("Splitting given node_string `{}`".format(node_string))
|
61
|
-
return node_string.split(":")
|
62
|
-
|
63
|
-
|
64
|
-
class FameYamlLoader(yaml.SafeLoader):
|
65
|
-
"""Custom YAML Loader for `!include` constructor"""
|
66
|
-
|
67
|
-
def __init__(self, stream: IO, path_resolver=PathResolver()) -> None:
|
68
|
-
log().debug("Initialize custom YAML loader")
|
69
|
-
self._path_resolver = path_resolver
|
70
|
-
try:
|
71
|
-
self._root_path = os.path.split(stream.name)[0]
|
72
|
-
except AttributeError:
|
73
|
-
self._root_path = os.path.curdir
|
74
|
-
super().__init__(stream)
|
75
|
-
|
76
|
-
@property
|
77
|
-
def root_path(self) -> str:
|
78
|
-
return self._root_path
|
79
|
-
|
80
|
-
@property
|
81
|
-
def path_resolver(self) -> PathResolver:
|
82
|
-
return self._path_resolver
|
83
|
-
|
84
|
-
|
85
|
-
def make_yaml_loader_builder(
|
86
|
-
path_resolver: PathResolver,
|
87
|
-
) -> Callable[[Any], FameYamlLoader]:
|
88
|
-
"""Utility function used to control the creation of a FameYamlLoader by the YAML library."""
|
89
|
-
return lambda stream: FameYamlLoader(stream, path_resolver)
|
90
|
-
|
91
|
-
|
92
|
-
def resolve_imported_path(loader: FameYamlLoader, included_path: str):
|
93
|
-
"""
|
94
|
-
Returns a list of file paths matching the given `included_path` based on path resolution performed by the loader.
|
95
|
-
|
96
|
-
Ignores the files starting with `DISABLING_YAML_FILE_PREFIX`
|
97
|
-
"""
|
98
|
-
file_list = loader.path_resolver.resolve_yaml_imported_file_pattern(loader.root_path, included_path)
|
99
|
-
|
100
|
-
ignore_filter = "*" + DISABLING_YAML_FILE_PREFIX + "*"
|
101
|
-
cleaned_file_list = []
|
102
|
-
for file in file_list:
|
103
|
-
if fnmatch(file, ignore_filter):
|
104
|
-
log().debug("Ignoring file {} due to prefix {}".format(file, DISABLING_YAML_FILE_PREFIX))
|
105
|
-
else:
|
106
|
-
cleaned_file_list.append(file)
|
107
|
-
if not cleaned_file_list:
|
108
|
-
log_and_raise_critical("Failed to find any file matching the `!include` directive `{}`".format(included_path))
|
109
|
-
log().debug("Collected file(s) `{}` from given included path `{}`".format(cleaned_file_list, included_path))
|
110
|
-
return cleaned_file_list
|
111
|
-
|
112
|
-
|
113
|
-
def read_data_from_file(file, node_address, path_resolver: PathResolver):
|
114
|
-
"""Returns data of the specified `node_address` from the specified `file`"""
|
115
|
-
data = yaml.load(file, make_yaml_loader_builder(path_resolver))
|
116
|
-
for node in node_address:
|
117
|
-
if node:
|
118
|
-
try:
|
119
|
-
data = data[node]
|
120
|
-
except KeyError:
|
121
|
-
log_and_raise_critical("'!include_node [{}, {}]': Cannot find '{}'.".format(file, node_address, node))
|
122
|
-
log().debug("Searched file `{}` for node `{}`".format(file, node_address))
|
123
|
-
return data
|
124
|
-
|
125
|
-
|
126
|
-
def join_data(new_data, previous_data):
|
127
|
-
"""Joins data from multiple files if both are in list format, otherwise returns"""
|
128
|
-
if not previous_data:
|
129
|
-
return new_data
|
130
|
-
if isinstance(new_data, list) and isinstance(previous_data, list):
|
131
|
-
previous_data.extend(new_data)
|
132
|
-
return previous_data
|
133
|
-
else:
|
134
|
-
log_and_raise_critical("!include can only combine list-like elements from multiple files!")
|
135
|
-
|
136
|
-
|
137
|
-
def construct_include(loader: FameYamlLoader, args: yaml.Node) -> Any:
|
138
|
-
"""
|
139
|
-
Loads one or many YAML file(s) with specifications provided in `args` in different formats
|
140
|
-
To load all content of a specified file, use:
|
141
|
-
!include "path/to/file.yaml"
|
142
|
-
To load only specific content (e.g. data of "Super:Sub:Node") from a given file, use:
|
143
|
-
!include ["path/to/file.yml", "Super:Sub:Node"]
|
144
|
-
For a slightly more verbose version of the above commands, use a dictionary argument:
|
145
|
-
!include {"file":"path/to/file.yml", "node": "Super:Sub:Node"}
|
146
|
-
|
147
|
-
Instead of "path/to/file.yaml" one can also use asterisks to select multiple files, e.g. "path/to/files/*.yaml"
|
148
|
-
The given file path is either
|
149
|
-
* relative to the path of the including YAML file, if it starts with a character other than "/"
|
150
|
-
* an absolute path if its starts with "/"
|
151
|
-
"""
|
152
|
-
args = read_args(loader, args)
|
153
|
-
nodes = split_nodes(args.node_string)
|
154
|
-
files = resolve_imported_path(loader, args.file_string)
|
155
|
-
|
156
|
-
joined_data = None
|
157
|
-
for file_name in files:
|
158
|
-
with open(file_name, "r", encoding=FILE_ENCODINGS[0]) as open_file:
|
159
|
-
data = read_data_from_file(open_file, nodes, loader.path_resolver)
|
160
|
-
joined_data = join_data(data, joined_data)
|
161
|
-
log().debug("Joined all files `{}` to joined data `{}`".format(files, joined_data))
|
162
|
-
return joined_data
|
163
|
-
|
164
|
-
|
165
|
-
FameYamlLoader.add_constructor("!include", construct_include)
|
166
|
-
|
167
|
-
|
168
|
-
def load_yaml(yaml_file_path: Path, path_resolver=PathResolver(), encoding: str = None) -> dict:
|
169
|
-
"""Loads the yaml file from given `yaml_file_path` and returns its content"""
|
170
|
-
log().info("Loading yaml from {}".format(yaml_file_path))
|
171
|
-
FILE_ENCODINGS[0] = encoding
|
172
|
-
with open(yaml_file_path, "r", encoding=encoding) as configfile:
|
173
|
-
data = yaml.load(configfile, make_yaml_loader_builder(path_resolver))
|
174
|
-
FILE_ENCODINGS[0] = None
|
175
|
-
return data
|
176
|
-
|
177
|
-
|
178
|
-
def check_for_yaml_file_type(yaml_file: Path) -> None:
|
179
|
-
"""Raises Exception if given `yaml_file` has no YAML-associated file suffix"""
|
180
|
-
if yaml_file.suffix.lower() not in [".yaml", ".yml"]:
|
181
|
-
log_and_raise_critical(CRIT_NO_YAML_SUFFIX.format(yaml_file))
|
fameio/source/metadata.py
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
# SPDX-FileCopyrightText: 2024 German Aerospace Center <fame@dlr.de>
|
2
|
-
#
|
3
|
-
# SPDX-License-Identifier: Apache-2.0
|
4
|
-
from abc import ABC
|
5
|
-
from typing import Dict, Any
|
6
|
-
|
7
|
-
from fameio.source.tools import keys_to_lower
|
8
|
-
|
9
|
-
|
10
|
-
class Metadata(ABC):
|
11
|
-
"""Hosts Metadata"""
|
12
|
-
|
13
|
-
_KEY_METADATA = "MetaData".lower()
|
14
|
-
|
15
|
-
def __init__(self):
|
16
|
-
self._metadata = {}
|
17
|
-
|
18
|
-
@property
|
19
|
-
def metadata(self) -> dict:
|
20
|
-
"""Returns list of metadata or an empty list if no metadata are defined"""
|
21
|
-
return self._metadata
|
22
|
-
|
23
|
-
def _extract_metadata(self, definitions: Dict[str, Any]) -> None:
|
24
|
-
"""If metadata is found in definitions, it is extracted and set"""
|
25
|
-
definitions = keys_to_lower(definitions)
|
26
|
-
if self._KEY_METADATA in definitions:
|
27
|
-
self._metadata = definitions[self._KEY_METADATA]
|
28
|
-
|
29
|
-
def _enrich_with_metadata(self, data: Dict) -> Dict:
|
30
|
-
"""Returns data enriched with metadata field"""
|
31
|
-
data[self._KEY_METADATA] = self._metadata
|
32
|
-
return data
|
fameio/source/path_resolver.py
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
# SPDX-FileCopyrightText: 2023 German Aerospace Center <fame@dlr.de>
|
2
|
-
#
|
3
|
-
# SPDX-License-Identifier: Apache-2.0
|
4
|
-
|
5
|
-
import glob
|
6
|
-
import os
|
7
|
-
from typing import List, Optional
|
8
|
-
|
9
|
-
|
10
|
-
class PathResolver:
|
11
|
-
"""Class responsible for locating files referenced in a scenario.
|
12
|
-
|
13
|
-
Such files can be the ones referenced via the YAML `!include` extension, or simply the data files (time_series)
|
14
|
-
referenced in attributes.
|
15
|
-
|
16
|
-
This class provides a default behaviour that can easily be customized by the caller."""
|
17
|
-
|
18
|
-
def resolve_yaml_imported_file_pattern(self, root_path: str, file_pattern: str) -> List[str]:
|
19
|
-
"""Returns a list of file paths matching the given `file_pattern` based on the configured resolver."""
|
20
|
-
absolute_path = os.path.abspath(os.path.join(root_path, file_pattern))
|
21
|
-
return glob.glob(absolute_path)
|
22
|
-
|
23
|
-
def resolve_series_file_path(self, file_name: str) -> Optional[str]:
|
24
|
-
"""Returns the absolute file path for the given series (relative) file name, or None on failure."""
|
25
|
-
if os.path.isabs(file_name):
|
26
|
-
return file_name
|
27
|
-
|
28
|
-
# try to locate in the current dir
|
29
|
-
file_path = os.path.join(os.path.curdir, file_name)
|
30
|
-
if os.path.exists(file_path):
|
31
|
-
return file_path
|
32
|
-
|
33
|
-
# not found
|
34
|
-
return None
|
@@ -1,130 +0,0 @@
|
|
1
|
-
# SPDX-FileCopyrightText: 2024 German Aerospace Center <fame@dlr.de>
|
2
|
-
#
|
3
|
-
# SPDX-License-Identifier: Apache-2.0
|
4
|
-
from __future__ import annotations
|
5
|
-
|
6
|
-
from typing import Any, Dict, List
|
7
|
-
|
8
|
-
from fameio.source.scenario.exception import log_and_raise
|
9
|
-
|
10
|
-
|
11
|
-
class Attribute:
|
12
|
-
"""An Attribute of an agent in a scenario"""
|
13
|
-
|
14
|
-
_VALUE_MISSING = "Value not specified for Attribute '{}' - leave out if default shall be used (if defined)."
|
15
|
-
_LIST_EMPTY = "Attribute '{}' was assigned an empty list - please remove or fill empty assignments."
|
16
|
-
_DICT_EMPTY = "Attribute '{}' was assigned an empty dictionary - please remove or fill empty assignments."
|
17
|
-
_MIXED_DATA = "Attribute '{}' was assigned a list with mixed complex and simple entries - please fix."
|
18
|
-
|
19
|
-
_NAME_STRING_SEPARATOR = "."
|
20
|
-
|
21
|
-
def __init__(self, name: str, definitions) -> None:
|
22
|
-
"""Parses an Attribute's definition"""
|
23
|
-
self._full_name = name
|
24
|
-
|
25
|
-
if definitions is None:
|
26
|
-
log_and_raise(Attribute._VALUE_MISSING.format(name))
|
27
|
-
|
28
|
-
if isinstance(definitions, dict):
|
29
|
-
self._value = None
|
30
|
-
self._nested_list = None
|
31
|
-
self._nested = Attribute._build_attribute_dict(name, definitions)
|
32
|
-
elif Attribute._is_list_of_dict(name, definitions):
|
33
|
-
self._nested = None
|
34
|
-
self._value = None
|
35
|
-
self._nested_list = []
|
36
|
-
for entry in definitions:
|
37
|
-
self._nested_list.append(Attribute._build_attribute_dict(name, entry))
|
38
|
-
else:
|
39
|
-
self._nested = None
|
40
|
-
self._nested_list = None
|
41
|
-
self._value = definitions
|
42
|
-
|
43
|
-
@staticmethod
|
44
|
-
def _build_attribute_dict(name: str, definitions: Dict[str, Any]) -> Dict[str, "Attribute"]:
|
45
|
-
"""Returns a new dictionary containing Attributes generated from given `definitions`"""
|
46
|
-
if not definitions:
|
47
|
-
log_and_raise(Attribute._DICT_EMPTY.format(name))
|
48
|
-
|
49
|
-
inner_elements = {}
|
50
|
-
for nested_name, value in definitions.items():
|
51
|
-
full_name = name + Attribute._NAME_STRING_SEPARATOR + nested_name
|
52
|
-
inner_elements[nested_name] = Attribute(full_name, value)
|
53
|
-
return inner_elements
|
54
|
-
|
55
|
-
@staticmethod
|
56
|
-
def _is_list_of_dict(name: str, definitions: Any) -> bool:
|
57
|
-
"""Returns True if given `definitions` is a list of dict"""
|
58
|
-
if isinstance(definitions, list):
|
59
|
-
if not definitions:
|
60
|
-
log_and_raise(Attribute._LIST_EMPTY.format(name))
|
61
|
-
|
62
|
-
all_dicts = no_dicts = True
|
63
|
-
for item in definitions:
|
64
|
-
if not isinstance(item, dict):
|
65
|
-
all_dicts = False
|
66
|
-
else:
|
67
|
-
no_dicts = False
|
68
|
-
if (not all_dicts) and (not no_dicts):
|
69
|
-
log_and_raise(Attribute._MIXED_DATA.format(name))
|
70
|
-
return all_dicts
|
71
|
-
return False
|
72
|
-
|
73
|
-
@property
|
74
|
-
def generic_content(self) -> Any:
|
75
|
-
"""Returns the full content of the attribute (and its children) as a generic value"""
|
76
|
-
if self.has_value:
|
77
|
-
return self.value
|
78
|
-
elif self.has_nested_list:
|
79
|
-
result = []
|
80
|
-
for attr_dict in self.nested_list:
|
81
|
-
inner_elements = {}
|
82
|
-
for name, attr in attr_dict.items():
|
83
|
-
inner_elements[name] = attr.generic_content
|
84
|
-
result.append(inner_elements)
|
85
|
-
return result
|
86
|
-
elif self.has_nested:
|
87
|
-
result = {}
|
88
|
-
for name, attr in self.nested.items():
|
89
|
-
result[name] = attr.generic_content
|
90
|
-
return result
|
91
|
-
else:
|
92
|
-
log_and_raise(Attribute._VALUE_MISSING.format(self._full_name))
|
93
|
-
|
94
|
-
@property
|
95
|
-
def has_value(self) -> bool:
|
96
|
-
"""Returns True if Attribute has any value assigned"""
|
97
|
-
return self._value is not None
|
98
|
-
|
99
|
-
@property
|
100
|
-
def value(self) -> Any:
|
101
|
-
return self._value
|
102
|
-
|
103
|
-
@property
|
104
|
-
def has_nested(self) -> bool:
|
105
|
-
"""Returns True if nested Attributes are present"""
|
106
|
-
return bool(self._nested)
|
107
|
-
|
108
|
-
@property
|
109
|
-
def nested(self) -> Dict[str, Attribute]:
|
110
|
-
"""Returns dictionary of all nested Attributes"""
|
111
|
-
assert self.has_nested
|
112
|
-
return self._nested
|
113
|
-
|
114
|
-
def get_nested_by_name(self, key: str) -> Attribute:
|
115
|
-
"""Returns nested Attribute by specified name"""
|
116
|
-
return self._nested[key]
|
117
|
-
|
118
|
-
@property
|
119
|
-
def has_nested_list(self) -> bool:
|
120
|
-
"""Returns True if list of nested items is present"""
|
121
|
-
return bool(self._nested_list)
|
122
|
-
|
123
|
-
@property
|
124
|
-
def nested_list(self) -> List[Dict[str, "Attribute"]]:
|
125
|
-
"""Return list of all nested Attribute dictionaries"""
|
126
|
-
assert self.has_nested_list
|
127
|
-
return self._nested_list
|
128
|
-
|
129
|
-
def __repr__(self) -> str:
|
130
|
-
return self._full_name
|
@@ -1,51 +0,0 @@
|
|
1
|
-
# SPDX-FileCopyrightText: 2024 German Aerospace Center <fame@dlr.de>
|
2
|
-
#
|
3
|
-
# SPDX-License-Identifier: Apache-2.0
|
4
|
-
from typing import Dict, List, Union
|
5
|
-
|
6
|
-
from fameio.source.metadata import Metadata
|
7
|
-
from fameio.source.scenario.exception import log_and_raise
|
8
|
-
from fameio.source.tools import keys_to_lower
|
9
|
-
|
10
|
-
|
11
|
-
class StringSet(Metadata):
|
12
|
-
"""Hosts a StringSet in the given format"""
|
13
|
-
|
14
|
-
ValueType = Union[List[str], Dict[str, Dict]]
|
15
|
-
StringSetType = Dict[str, Union[Dict, ValueType]]
|
16
|
-
|
17
|
-
_ERR_NO_STRING_SET_VALUES = "Missing mandatory key '{}' in StringSet definition {}."
|
18
|
-
|
19
|
-
_KEY_VALUES = "Values".lower()
|
20
|
-
|
21
|
-
def __init__(self):
|
22
|
-
super().__init__()
|
23
|
-
self._values = {}
|
24
|
-
|
25
|
-
@classmethod
|
26
|
-
def from_dict(cls, definition: StringSetType) -> "StringSet":
|
27
|
-
"""Returns StringSet initialised from `definition`"""
|
28
|
-
string_set = cls()
|
29
|
-
string_set._extract_metadata(definition)
|
30
|
-
definition = keys_to_lower(definition)
|
31
|
-
if cls._KEY_VALUES in definition:
|
32
|
-
string_set._values = string_set._read_values(definition)
|
33
|
-
else:
|
34
|
-
log_and_raise(cls._ERR_NO_STRING_SET_VALUES.format(cls._KEY_VALUES, definition))
|
35
|
-
return string_set
|
36
|
-
|
37
|
-
def _read_values(self, definition: ValueType) -> Dict[str, Dict]:
|
38
|
-
"""Ensures values are returned as dictionary representation by converting `definitions` of type 'List[str]'"""
|
39
|
-
values = definition[self._KEY_VALUES]
|
40
|
-
if isinstance(values, list):
|
41
|
-
return {name: {} for name in values}
|
42
|
-
return values
|
43
|
-
|
44
|
-
def to_dict(self) -> Dict:
|
45
|
-
"""Serializes the StringSet to a dict"""
|
46
|
-
result = {self._KEY_VALUES: self._values}
|
47
|
-
return self._enrich_with_metadata(result)
|
48
|
-
|
49
|
-
def is_in_set(self, key: str) -> bool:
|
50
|
-
"""Returns True if `key` is a valid name in this StringSet"""
|
51
|
-
return key in self._values
|
@@ -1,132 +0,0 @@
|
|
1
|
-
# SPDX-FileCopyrightText: 2023 German Aerospace Center <fame@dlr.de>
|
2
|
-
#
|
3
|
-
# SPDX-License-Identifier: Apache-2.0
|
4
|
-
from __future__ import annotations
|
5
|
-
|
6
|
-
from typing import Dict, List, Any
|
7
|
-
|
8
|
-
from fameio.source.logs import log_error_and_raise, log
|
9
|
-
from fameio.source.schema.exception import SchemaException
|
10
|
-
from fameio.source.schema.attribute import AttributeSpecs
|
11
|
-
from fameio.source.tools import keys_to_lower
|
12
|
-
|
13
|
-
|
14
|
-
class AgentType:
|
15
|
-
"""Schema definitions for an Agent type"""
|
16
|
-
|
17
|
-
_ERR_NAME_INVALID = "'{}' is not a valid name for AgentTypes"
|
18
|
-
_ERR_PRODUCTS_NO_STRING = "Product definition of AgentType '{}' contains item(s) / key(s) other than string: '{}'"
|
19
|
-
_ERR_PRODUCTS_UNKNOWN_STRUCTURE = "Product definition of AgentType '{}' is neither list nor dictionary: '{}'"
|
20
|
-
|
21
|
-
_NO_ATTRIBUTES = "Agent '{}' has no specified 'Attributes'."
|
22
|
-
_NO_PRODUCTS = "Agent '{}' has no specified 'Products'."
|
23
|
-
_NO_OUTPUTS = "Agent '{}' has no specified 'Outputs'."
|
24
|
-
_NO_METADATA = "Agent '{}' has no specified 'Metadata'."
|
25
|
-
|
26
|
-
_KEY_ATTRIBUTES = "Attributes".lower()
|
27
|
-
_KEY_PRODUCTS = "Products".lower()
|
28
|
-
_KEY_OUTPUTS = "Outputs".lower()
|
29
|
-
_KEY_METADATA = "MetaData".lower()
|
30
|
-
|
31
|
-
def __init__(self, name: str):
|
32
|
-
"""
|
33
|
-
Initialise a new AgentType
|
34
|
-
|
35
|
-
Args:
|
36
|
-
name: name of the AgenType - must not be None or empty
|
37
|
-
"""
|
38
|
-
if not name or name.isspace():
|
39
|
-
log_error_and_raise(SchemaException(AgentType._ERR_NAME_INVALID.format(name)))
|
40
|
-
self._name = name
|
41
|
-
self._attributes = {}
|
42
|
-
self._products = {}
|
43
|
-
self._outputs = {}
|
44
|
-
self._metadata = {}
|
45
|
-
|
46
|
-
@classmethod
|
47
|
-
def from_dict(cls, name: str, definitions: dict) -> AgentType:
|
48
|
-
"""
|
49
|
-
Creates AgentType with given `name` from specified dictionary
|
50
|
-
|
51
|
-
Args:
|
52
|
-
name: of the agent type
|
53
|
-
definitions: of the agent type specifying, e.g., its attributes and products
|
54
|
-
|
55
|
-
Returns:
|
56
|
-
a new instance of AgentType
|
57
|
-
"""
|
58
|
-
agent_type = cls(name)
|
59
|
-
|
60
|
-
definition = keys_to_lower(definitions)
|
61
|
-
if AgentType._KEY_ATTRIBUTES in definition:
|
62
|
-
for attribute_name, attribute_details in definition[AgentType._KEY_ATTRIBUTES].items():
|
63
|
-
full_name = name + "." + attribute_name
|
64
|
-
agent_type._attributes[attribute_name] = AttributeSpecs(full_name, attribute_details)
|
65
|
-
else:
|
66
|
-
log().info(AgentType._NO_ATTRIBUTES.format(name))
|
67
|
-
|
68
|
-
if AgentType._KEY_PRODUCTS in definition and definition[AgentType._KEY_PRODUCTS]:
|
69
|
-
agent_type._products.update(AgentType._read_products(definition[AgentType._KEY_PRODUCTS], name))
|
70
|
-
else:
|
71
|
-
log().info(AgentType._NO_PRODUCTS.format(name))
|
72
|
-
|
73
|
-
if AgentType._KEY_OUTPUTS in definition:
|
74
|
-
outputs_to_add = definition[AgentType._KEY_OUTPUTS]
|
75
|
-
if outputs_to_add:
|
76
|
-
agent_type._outputs.update(outputs_to_add)
|
77
|
-
else:
|
78
|
-
log().debug(AgentType._NO_OUTPUTS.format(name))
|
79
|
-
|
80
|
-
if AgentType._KEY_METADATA in definition:
|
81
|
-
metadata_to_add = definition[AgentType._KEY_METADATA]
|
82
|
-
if metadata_to_add:
|
83
|
-
agent_type._metadata.update(metadata_to_add)
|
84
|
-
else:
|
85
|
-
log().debug(AgentType._NO_METADATA.format(name))
|
86
|
-
|
87
|
-
return agent_type
|
88
|
-
|
89
|
-
@staticmethod
|
90
|
-
def _read_products(products: Any, agent_type: str) -> Dict[str, Any]:
|
91
|
-
"""Returns a dict obtained from given `products` defined for `agent_type`"""
|
92
|
-
product_names = None
|
93
|
-
if isinstance(products, dict):
|
94
|
-
product_names = products
|
95
|
-
elif isinstance(products, list):
|
96
|
-
product_names = {key: None for key in products}
|
97
|
-
else:
|
98
|
-
log_error_and_raise(SchemaException(AgentType._ERR_PRODUCTS_UNKNOWN_STRUCTURE.format(agent_type, products)))
|
99
|
-
|
100
|
-
if all([isinstance(item, str) for item in product_names.keys()]):
|
101
|
-
return product_names
|
102
|
-
else:
|
103
|
-
log_error_and_raise(SchemaException(AgentType._ERR_PRODUCTS_NO_STRING.format(agent_type, product_names)))
|
104
|
-
|
105
|
-
@property
|
106
|
-
def name(self) -> str:
|
107
|
-
"""Returns the agent type name"""
|
108
|
-
return self._name
|
109
|
-
|
110
|
-
@property
|
111
|
-
def products(self) -> dict:
|
112
|
-
"""Returns dict of products or an empty dict if no products are defined"""
|
113
|
-
return self._products
|
114
|
-
|
115
|
-
def get_product_names(self) -> List[str]:
|
116
|
-
"""Returns list of product names or an empty list if no products are defined"""
|
117
|
-
return list(self._products.keys())
|
118
|
-
|
119
|
-
@property
|
120
|
-
def attributes(self) -> Dict[str, AttributeSpecs]:
|
121
|
-
"""Returns list of Attributes of this agent or an empty list if no attributes are defined"""
|
122
|
-
return self._attributes
|
123
|
-
|
124
|
-
@property
|
125
|
-
def outputs(self) -> dict:
|
126
|
-
"""Returns list of outputs or an empty list if no outputs are defined"""
|
127
|
-
return self._outputs
|
128
|
-
|
129
|
-
@property
|
130
|
-
def metadata(self) -> dict:
|
131
|
-
"""Returns list of metadata or an empty list if no metadata are defined"""
|
132
|
-
return self._metadata
|