fameio 3.1.1__py3-none-any.whl → 3.3.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.
- fameio/cli/convert_results.py +10 -10
- fameio/cli/make_config.py +9 -9
- fameio/cli/options.py +6 -4
- fameio/cli/parser.py +87 -51
- fameio/cli/reformat.py +58 -0
- fameio/input/__init__.py +4 -4
- fameio/input/loader/__init__.py +13 -13
- fameio/input/loader/controller.py +64 -18
- fameio/input/loader/loader.py +25 -16
- fameio/input/metadata.py +57 -38
- fameio/input/resolver.py +9 -10
- fameio/input/scenario/agent.py +62 -26
- fameio/input/scenario/attribute.py +93 -40
- fameio/input/scenario/contract.py +160 -56
- fameio/input/scenario/exception.py +41 -18
- fameio/input/scenario/fameiofactory.py +57 -6
- fameio/input/scenario/generalproperties.py +22 -12
- fameio/input/scenario/scenario.py +117 -38
- fameio/input/scenario/stringset.py +29 -11
- fameio/input/schema/agenttype.py +27 -10
- fameio/input/schema/attribute.py +108 -45
- fameio/input/schema/java_packages.py +14 -12
- fameio/input/schema/schema.py +39 -15
- fameio/input/validator.py +198 -54
- fameio/input/writer.py +137 -46
- fameio/logs.py +28 -47
- fameio/output/__init__.py +5 -1
- fameio/output/agent_type.py +89 -28
- fameio/output/conversion.py +52 -37
- fameio/output/csv_writer.py +107 -27
- fameio/output/data_transformer.py +17 -24
- fameio/output/execution_dao.py +170 -0
- fameio/output/input_dao.py +71 -33
- fameio/output/output_dao.py +33 -11
- fameio/output/reader.py +64 -21
- fameio/output/yaml_writer.py +16 -8
- fameio/scripts/__init__.py +22 -4
- fameio/scripts/convert_results.py +126 -52
- fameio/scripts/convert_results.py.license +1 -1
- fameio/scripts/exception.py +7 -0
- fameio/scripts/make_config.py +34 -13
- fameio/scripts/make_config.py.license +1 -1
- fameio/scripts/reformat.py +71 -0
- fameio/scripts/reformat.py.license +3 -0
- fameio/series.py +174 -59
- fameio/time.py +79 -25
- fameio/tools.py +48 -8
- {fameio-3.1.1.dist-info → fameio-3.3.0.dist-info}/METADATA +50 -34
- fameio-3.3.0.dist-info/RECORD +60 -0
- {fameio-3.1.1.dist-info → fameio-3.3.0.dist-info}/WHEEL +1 -1
- {fameio-3.1.1.dist-info → fameio-3.3.0.dist-info}/entry_points.txt +1 -0
- CHANGELOG.md +0 -288
- fameio-3.1.1.dist-info/RECORD +0 -56
- {fameio-3.1.1.dist-info → fameio-3.3.0.dist-info}/LICENSE.txt +0 -0
- {fameio-3.1.1.dist-info → fameio-3.3.0.dist-info}/LICENSES/Apache-2.0.txt +0 -0
- {fameio-3.1.1.dist-info → fameio-3.3.0.dist-info}/LICENSES/CC-BY-4.0.txt +0 -0
- {fameio-3.1.1.dist-info → fameio-3.3.0.dist-info}/LICENSES/CC0-1.0.txt +0 -0
fameio/input/validator.py
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
# SPDX-FileCopyrightText: 2025 German Aerospace Center <fame@dlr.de>
|
2
2
|
#
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
4
|
+
from __future__ import annotations
|
5
|
+
|
4
6
|
import math
|
5
7
|
from collections import Counter
|
6
|
-
from typing import Any
|
8
|
+
from typing import Any
|
7
9
|
|
10
|
+
from fameio.input import InputError
|
8
11
|
from fameio.input.resolver import PathResolver
|
9
12
|
from fameio.input.scenario import Agent, Attribute, Contract, Scenario, StringSet
|
10
13
|
from fameio.input.schema import Schema, AttributeSpecs, AttributeType, AgentType
|
@@ -13,12 +16,12 @@ from fameio.series import TimeSeriesManager, TimeSeriesError
|
|
13
16
|
from fameio.time import FameTime
|
14
17
|
|
15
18
|
|
16
|
-
class ValidationError(
|
17
|
-
"""Indicates an error occurred during validation of any data with a connected schema"""
|
19
|
+
class ValidationError(InputError):
|
20
|
+
"""Indicates an error occurred during validation of any data with a connected schema."""
|
18
21
|
|
19
22
|
|
20
23
|
class SchemaValidator:
|
21
|
-
"""Handles validation of scenarios based on a connected `schema
|
24
|
+
"""Handles validation of scenarios based on a connected `schema`."""
|
22
25
|
|
23
26
|
_AGENT_ID_NOT_UNIQUE = "Agent ID(s) not unique: '{}'."
|
24
27
|
_AGENT_TYPE_UNKNOWN = "Agent type '{}' not declared in Schema."
|
@@ -26,8 +29,8 @@ class SchemaValidator:
|
|
26
29
|
_TYPE_NOT_IMPLEMENTED = "Validation not implemented for AttributeType '{}'."
|
27
30
|
_INCOMPATIBLE = "Value '{}' incompatible with {} of Attribute '{}'."
|
28
31
|
_DISALLOWED = "Value '{}' not in list of allowed values of Attribute '{}'"
|
29
|
-
_AGENT_MISSING = "
|
30
|
-
_PRODUCT_MISSING = "
|
32
|
+
_AGENT_MISSING = "Agent with ID '{}' was not declared in Scenario but used in Contract: '{}'"
|
33
|
+
_PRODUCT_MISSING = "'{}' is no product of AgentType '{}'. Contract invalid: '{}'"
|
31
34
|
_KEY_MISSING = "Required key '{}' missing in dictionary '{}'."
|
32
35
|
_ATTRIBUTE_MISSING = "Mandatory attribute '{}' is missing."
|
33
36
|
_DEFAULT_IGNORED = "Optional Attribute: '{}': not specified - provided Default ignored for optional Attributes."
|
@@ -42,9 +45,7 @@ class SchemaValidator:
|
|
42
45
|
def validate_scenario_and_timeseries(
|
43
46
|
scenario: Scenario, path_resolver: PathResolver = PathResolver()
|
44
47
|
) -> TimeSeriesManager:
|
45
|
-
"""
|
46
|
-
Validates the given `scenario` and its timeseries using given `path_resolver`
|
47
|
-
Raises an exception if schema requirements are not met or timeseries data are erroneous.
|
48
|
+
"""Validates the given `scenario` and its timeseries using given `path_resolver`.
|
48
49
|
|
49
50
|
Args:
|
50
51
|
scenario: to be validated against the encompassed schema
|
@@ -52,8 +53,9 @@ class SchemaValidator:
|
|
52
53
|
|
53
54
|
Returns:
|
54
55
|
a new TimeSeriesManager initialised with validated time series from scenario
|
56
|
+
|
55
57
|
Raises:
|
56
|
-
|
58
|
+
ValidationError: if schema requirements are not met or timeseries are erroneous, logged with level "ERROR"
|
57
59
|
"""
|
58
60
|
schema = scenario.schema
|
59
61
|
agents = scenario.agents
|
@@ -72,28 +74,63 @@ class SchemaValidator:
|
|
72
74
|
|
73
75
|
@staticmethod
|
74
76
|
def ensure_unique_agent_ids(agents: list[Agent]) -> None:
|
75
|
-
"""
|
77
|
+
"""Ensures that IDs of given agents are unique.
|
78
|
+
|
79
|
+
Args:
|
80
|
+
agents: whose IDs are to be checked to uniqueness
|
81
|
+
|
82
|
+
Raises:
|
83
|
+
ValidationError: if any id for given `agents` is not unique, logged with level "ERROR"
|
84
|
+
"""
|
76
85
|
list_of_ids = [agent.id for agent in agents]
|
77
86
|
non_unique_ids = [agent_id for agent_id, count in Counter(list_of_ids).items() if count > 1]
|
78
87
|
if non_unique_ids:
|
79
88
|
raise log_error(ValidationError(SchemaValidator._AGENT_ID_NOT_UNIQUE.format(non_unique_ids)))
|
80
89
|
|
81
90
|
@staticmethod
|
82
|
-
def ensure_agent_and_timeseries_are_valid(
|
83
|
-
|
91
|
+
def ensure_agent_and_timeseries_are_valid(
|
92
|
+
agent: Agent, schema: Schema, timeseries_manager: TimeSeriesManager
|
93
|
+
) -> None:
|
94
|
+
"""Validates given `agent` against `schema`, loads and validates its timeseries.
|
95
|
+
|
96
|
+
Args:
|
97
|
+
agent: to be checked
|
98
|
+
schema: to check the agent against
|
99
|
+
timeseries_manager: to register new timeseries at
|
100
|
+
|
101
|
+
Raises:
|
102
|
+
ValidationError: if agent is not in schema, has missing or invalid data; logged with level "ERROR"
|
103
|
+
"""
|
84
104
|
SchemaValidator.ensure_agent_type_in_schema(agent, schema)
|
85
105
|
SchemaValidator.ensure_is_valid_agent(agent, schema, timeseries_manager)
|
86
106
|
SchemaValidator.load_and_validate_timeseries(agent, schema, timeseries_manager)
|
87
107
|
|
88
108
|
@staticmethod
|
89
109
|
def ensure_agent_type_in_schema(agent: Agent, schema: Schema) -> None:
|
90
|
-
"""
|
110
|
+
"""Makes sure that the given agent is contained in the given schema.
|
111
|
+
|
112
|
+
Args:
|
113
|
+
agent: to be checked
|
114
|
+
schema: that ought to contain the agent
|
115
|
+
|
116
|
+
Raises:
|
117
|
+
ValidationError: if type for given `agent` is not specified in given `schema`, logged with level "ERROR"
|
118
|
+
"""
|
91
119
|
if agent.type_name not in schema.agent_types:
|
92
120
|
raise log_error(ValidationError(SchemaValidator._AGENT_TYPE_UNKNOWN.format(agent.type_name)))
|
93
121
|
|
94
122
|
@staticmethod
|
95
123
|
def ensure_is_valid_agent(agent: Agent, schema: Schema, timeseries_manager: TimeSeriesManager) -> None:
|
96
|
-
"""
|
124
|
+
"""Ensures that given `agent` meets the specified `schema` requirements and registers new timeseries
|
125
|
+
|
126
|
+
Args:
|
127
|
+
agent: to be checked
|
128
|
+
schema: to check against
|
129
|
+
timeseries_manager: to register new timeseries at
|
130
|
+
|
131
|
+
Raises:
|
132
|
+
ValidationError: if the agent doesn't meet the schema's requirements, logged with level "ERROR"
|
133
|
+
"""
|
97
134
|
scenario_attributes = agent.attributes
|
98
135
|
schema_attributes = SchemaValidator._get_agent(schema, agent.type_name).attributes
|
99
136
|
missing_default_series = SchemaValidator._check_mandatory_or_default(scenario_attributes, schema_attributes)
|
@@ -104,7 +141,18 @@ class SchemaValidator:
|
|
104
141
|
|
105
142
|
@staticmethod
|
106
143
|
def _get_agent(schema: Schema, name: str) -> AgentType:
|
107
|
-
"""Returns agent specified by `name
|
144
|
+
"""Returns agent type as specified by `name`.
|
145
|
+
|
146
|
+
Args:
|
147
|
+
schema: to obtain the agent type from
|
148
|
+
name: of the agent type to obtain
|
149
|
+
|
150
|
+
Returns:
|
151
|
+
AgentType corresponding to given name
|
152
|
+
|
153
|
+
Raises:
|
154
|
+
ValidationError: if this agent is not present in given `schema`, logged with level "ERROR"
|
155
|
+
"""
|
108
156
|
if name in schema.agent_types:
|
109
157
|
return schema.agent_types[name]
|
110
158
|
raise log_error(ValidationError(SchemaValidator._AGENT_TYPE_UNKNOWN.format(name)))
|
@@ -113,15 +161,22 @@ class SchemaValidator:
|
|
113
161
|
def _check_mandatory_or_default(
|
114
162
|
attributes: dict[str, Attribute],
|
115
163
|
specifications: dict[str, AttributeSpecs],
|
116
|
-
) -> list[
|
117
|
-
"""
|
118
|
-
|
119
|
-
|
164
|
+
) -> list[str | float]:
|
165
|
+
"""Ensures that each attribute that is mandatory has either a value specified or a default value available.
|
166
|
+
|
167
|
+
Also gathers and returns all default values of time series attributes.
|
168
|
+
|
169
|
+
Args:
|
170
|
+
attributes: to check for completeness
|
171
|
+
specifications: to check attributes against
|
120
172
|
|
121
173
|
Returns:
|
122
174
|
list of time series defaults used in scenario
|
175
|
+
|
176
|
+
Raises:
|
177
|
+
ValidationError: if any mandatory attribute is missing and has no default
|
123
178
|
"""
|
124
|
-
missing_series_defaults = []
|
179
|
+
missing_series_defaults: list[str | float] = []
|
125
180
|
for name, specification in specifications.items():
|
126
181
|
if name not in attributes:
|
127
182
|
if specification.is_mandatory:
|
@@ -130,7 +185,7 @@ class SchemaValidator:
|
|
130
185
|
ValidationError(SchemaValidator._ATTRIBUTE_MISSING.format(specification.full_name))
|
131
186
|
)
|
132
187
|
if specification.attr_type == AttributeType.TIME_SERIES:
|
133
|
-
missing_series_defaults.append(specification.default_value)
|
188
|
+
missing_series_defaults.append(specification.default_value) # type: ignore[arg-type]
|
134
189
|
else:
|
135
190
|
if specification.has_default_value:
|
136
191
|
log().warning(SchemaValidator._DEFAULT_IGNORED.format(specification.full_name))
|
@@ -151,7 +206,16 @@ class SchemaValidator:
|
|
151
206
|
|
152
207
|
@staticmethod
|
153
208
|
def _ensure_attributes_exist(attributes: dict[str, Attribute], specifications: dict[str, AttributeSpecs]) -> None:
|
154
|
-
"""
|
209
|
+
"""Ensures that each attribute has a corresponding entry in given specifications.
|
210
|
+
|
211
|
+
Args:
|
212
|
+
attributes: to search specifications for
|
213
|
+
specifications: describing the attributes
|
214
|
+
|
215
|
+
Raises:
|
216
|
+
ValidationError: if any entry of given `attributes` has no corresponding type `specification`,
|
217
|
+
logged with level "ERROR"
|
218
|
+
"""
|
155
219
|
for name, attribute in attributes.items():
|
156
220
|
if name not in specifications:
|
157
221
|
raise log_error(ValidationError(SchemaValidator._ATTRIBUTE_UNKNOWN.format(attribute)))
|
@@ -167,7 +231,16 @@ class SchemaValidator:
|
|
167
231
|
def _ensure_value_and_type_match(
|
168
232
|
attributes: dict[str, Attribute], specifications: dict[str, AttributeSpecs]
|
169
233
|
) -> None:
|
170
|
-
"""
|
234
|
+
"""Ensure that the value of an attribute match the attribute's type and are allowed.
|
235
|
+
|
236
|
+
Args:
|
237
|
+
attributes: to check the values for
|
238
|
+
specifications: describing the attribute (and potential value restrictions)
|
239
|
+
|
240
|
+
Raises:
|
241
|
+
ValidationError: if in given list of `attributes` any value does not match associated type `specification`,
|
242
|
+
logged with level "ERROR"
|
243
|
+
"""
|
171
244
|
for name, attribute in attributes.items():
|
172
245
|
specification = specifications[name]
|
173
246
|
if attribute.has_value:
|
@@ -187,7 +260,18 @@ class SchemaValidator:
|
|
187
260
|
|
188
261
|
@staticmethod
|
189
262
|
def _is_compatible(specification: AttributeSpecs, value_or_values: Any) -> bool:
|
190
|
-
"""
|
263
|
+
"""Checks if given `value_or_values` is compatible with the given `specification`.
|
264
|
+
|
265
|
+
Args:
|
266
|
+
specification: of the attribute for which to check the values
|
267
|
+
value_or_values: singe value or list of values that is to be checked for compatibility
|
268
|
+
|
269
|
+
Returns:
|
270
|
+
True if given `value_or_values` is compatible the to specified `attribute_type`, False otherwise
|
271
|
+
|
272
|
+
Raises:
|
273
|
+
ValidationError: if an unknown attribute type is encountered, logged with level "ERROR"
|
274
|
+
"""
|
191
275
|
is_list = isinstance(value_or_values, list)
|
192
276
|
attribute_type = specification.attr_type
|
193
277
|
if specification.is_list:
|
@@ -202,7 +286,19 @@ class SchemaValidator:
|
|
202
286
|
|
203
287
|
@staticmethod
|
204
288
|
def _is_compatible_value(attribute_type: AttributeType, value) -> bool:
|
205
|
-
"""
|
289
|
+
"""Checks if given value is compatible with the specifications of the `attribute_type`.
|
290
|
+
|
291
|
+
Args:
|
292
|
+
attribute_type: specification to test the value against
|
293
|
+
value: to be tested for compatibility
|
294
|
+
|
295
|
+
Returns:
|
296
|
+
True if given single value is compatible to specified `attribute_type` and is not a NaN float,
|
297
|
+
False otherwise
|
298
|
+
|
299
|
+
Raises:
|
300
|
+
ValidationError: if checks for the attribute type are not implemented, logged with level "ERROR"
|
301
|
+
"""
|
206
302
|
if attribute_type is AttributeType.INTEGER:
|
207
303
|
if isinstance(value, int):
|
208
304
|
return -2147483648 < value < 2147483647
|
@@ -221,15 +317,20 @@ class SchemaValidator:
|
|
221
317
|
|
222
318
|
@staticmethod
|
223
319
|
def _is_allowed_value(attribute: AttributeSpecs, value) -> bool:
|
224
|
-
"""
|
225
|
-
|
226
|
-
|
227
|
-
|
320
|
+
"""Checks if given value is on the list of allowed values for an attribute type.
|
321
|
+
|
322
|
+
Args:
|
323
|
+
attribute: type description of an attribute
|
324
|
+
value: to be checked if compatible with the attribute type's value restrictions
|
325
|
+
|
326
|
+
Returns:
|
327
|
+
True if `value` matches an entry of given `Attribute`'s value list or if this list is empty
|
328
|
+
"""
|
329
|
+
return not attribute.values or value in attribute.values
|
228
330
|
|
229
331
|
@staticmethod
|
230
332
|
def load_and_validate_timeseries(agent: Agent, schema: Schema, timeseries_manager: TimeSeriesManager) -> None:
|
231
|
-
"""
|
232
|
-
Loads all timeseries specified in given `schema` of given `agent` into given `timeseries_manager`
|
333
|
+
"""Loads all timeseries in given `schema` for given `agent`. Uses `timeseries_manager` to validates them.
|
233
334
|
|
234
335
|
Args:
|
235
336
|
agent: definition in scenario
|
@@ -237,39 +338,55 @@ class SchemaValidator:
|
|
237
338
|
timeseries_manager: to be filled with timeseries
|
238
339
|
|
239
340
|
Raises:
|
240
|
-
|
341
|
+
ValidationError: if timeseries is not found, ill-formatted or invalid
|
241
342
|
"""
|
242
343
|
scenario_attributes = agent.attributes
|
243
344
|
schema_attributes = SchemaValidator._get_agent(schema, agent.type_name).attributes
|
244
|
-
SchemaValidator.
|
345
|
+
SchemaValidator._register_timeseries(scenario_attributes, schema_attributes, timeseries_manager)
|
245
346
|
|
246
347
|
@staticmethod
|
247
|
-
def
|
348
|
+
def _register_timeseries(
|
248
349
|
attributes: dict[str, Attribute], specifications: dict[str, AttributeSpecs], manager: TimeSeriesManager
|
249
350
|
) -> None:
|
250
|
-
"""Recursively searches for
|
351
|
+
"""Recursively searches for timeseries in agent attributes and registers them at given `manager`.
|
352
|
+
|
353
|
+
Args:
|
354
|
+
attributes: to search timeseries in
|
355
|
+
specifications: corresponding to the attributes
|
356
|
+
manager: to register new timeseries at
|
357
|
+
|
358
|
+
Raises:
|
359
|
+
ValidationError: if a timeseries could not be registered, logged at level "ERROR"
|
360
|
+
"""
|
251
361
|
for name, attribute in attributes.items():
|
252
362
|
specification = specifications[name]
|
253
363
|
if attribute.has_value:
|
254
364
|
attribute_type = specification.attr_type
|
255
365
|
if attribute_type is AttributeType.TIME_SERIES:
|
256
366
|
try:
|
257
|
-
manager.register_and_validate(attribute.value)
|
367
|
+
manager.register_and_validate(attribute.value) # type: ignore[arg-type]
|
258
368
|
except TimeSeriesError as e:
|
259
369
|
message = SchemaValidator._TIME_SERIES_INVALID.format(specification.full_name)
|
260
|
-
raise log_error(ValidationError(message
|
370
|
+
raise log_error(ValidationError(message)) from e
|
261
371
|
if attribute.has_nested:
|
262
|
-
SchemaValidator.
|
372
|
+
SchemaValidator._register_timeseries(attribute.nested, specification.nested_attributes, manager)
|
263
373
|
if attribute.has_nested_list:
|
264
374
|
for entry in attribute.nested_list:
|
265
|
-
SchemaValidator.
|
375
|
+
SchemaValidator._register_timeseries(entry, specification.nested_attributes, manager)
|
266
376
|
|
267
377
|
@staticmethod
|
268
378
|
def ensure_string_set_consistency(agent: Agent, schema: Schema, string_sets: dict[str, StringSet]) -> None:
|
269
|
-
"""
|
270
|
-
|
271
|
-
|
272
|
-
|
379
|
+
"""Checks consistency of an `agent's` StringSet attributes as mentioned in `schema` with provided `string_sets`.
|
380
|
+
|
381
|
+
Args:
|
382
|
+
agent: whose StringSet attributes are to be checked for consistency
|
383
|
+
schema: describing the agent's attributes
|
384
|
+
string_sets: as defined in the scenario and to test the agents attribute against
|
385
|
+
|
386
|
+
Raises:
|
387
|
+
ValidationError: logged with level "ERROR", occur when either
|
388
|
+
a) an agent's attribute is type StringSet but the corresponding StringSet is not defined in the scenario, or
|
389
|
+
b) the value assigned to an attribute of type StringSet is not contained in the corresponding StringSet
|
273
390
|
"""
|
274
391
|
scenario_attributes = agent.attributes
|
275
392
|
schema_attributes = SchemaValidator._get_agent(schema, agent.type_name).attributes
|
@@ -279,12 +396,19 @@ class SchemaValidator:
|
|
279
396
|
def _ensure_string_set_consistency(
|
280
397
|
attributes: dict[str, Attribute], specifications: dict[str, AttributeSpecs], string_sets: dict[str, StringSet]
|
281
398
|
) -> None:
|
282
|
-
"""
|
283
|
-
|
399
|
+
"""Recursively iterates through all attributes of an agent checking consistency of `StringSet` type attributes.
|
400
|
+
|
401
|
+
Checks consistency of agent `StringSet` attributes with provided `string_sets` in the scenario and schema.
|
402
|
+
|
403
|
+
Args:
|
404
|
+
attributes: attributes of an agent
|
405
|
+
specifications: corresponding to the provided attributes
|
406
|
+
string_sets: to check attributes of type string_set against
|
407
|
+
|
284
408
|
Raises:
|
285
|
-
|
286
|
-
a) StringSet
|
287
|
-
b)
|
409
|
+
ValidationError: logged with level "ERROR", occur when
|
410
|
+
a) StringSet declared in schema is not defined in the section "StringSet" in the scenario, or
|
411
|
+
b) value assigned to an attribute of type StringSet is not contained in the corresponding StringSet
|
288
412
|
"""
|
289
413
|
for name, attribute in attributes.items():
|
290
414
|
specification = specifications[name]
|
@@ -310,23 +434,43 @@ class SchemaValidator:
|
|
310
434
|
|
311
435
|
@staticmethod
|
312
436
|
def ensure_is_valid_contract(contract: Contract, schema: Schema, agent_types_by_id: dict[int, str]) -> None:
|
313
|
-
"""
|
437
|
+
"""Checks validity of a contract's IDs and product.
|
438
|
+
|
439
|
+
Ensures that for a given `contract` sender and receiver IDs are valid, and that the sender offers the
|
440
|
+
contracted product.
|
441
|
+
|
442
|
+
Args:
|
443
|
+
contract: to be checked
|
444
|
+
schema: to extract the sender's available products from
|
445
|
+
agent_types_by_id: to test if sender and receiver IDs are contained
|
446
|
+
|
447
|
+
Raises:
|
448
|
+
ValidationError: if given `contract` uses unknown agent IDs or an unknown product, logged with level "ERROR"
|
449
|
+
"""
|
314
450
|
sender_id = contract.sender_id
|
315
451
|
if sender_id not in agent_types_by_id:
|
316
|
-
raise log_error(ValidationError(SchemaValidator._AGENT_MISSING.format(sender_id)))
|
452
|
+
raise log_error(ValidationError(SchemaValidator._AGENT_MISSING.format(sender_id, contract.to_dict())))
|
317
453
|
if contract.receiver_id not in agent_types_by_id:
|
318
|
-
raise log_error(
|
454
|
+
raise log_error(
|
455
|
+
ValidationError(SchemaValidator._AGENT_MISSING.format(contract.receiver_id, contract.to_dict()))
|
456
|
+
)
|
319
457
|
sender_type_name = agent_types_by_id[sender_id]
|
320
458
|
if sender_type_name not in schema.agent_types:
|
321
459
|
raise log_error(ValidationError(SchemaValidator._AGENT_TYPE_UNKNOWN.format(sender_type_name)))
|
322
460
|
sender_type = schema.agent_types[sender_type_name]
|
323
461
|
product = contract.product_name
|
324
462
|
if product not in sender_type.products:
|
325
|
-
raise log_error(
|
463
|
+
raise log_error(
|
464
|
+
ValidationError(SchemaValidator._PRODUCT_MISSING.format(product, sender_type_name, contract.to_dict()))
|
465
|
+
)
|
326
466
|
|
327
467
|
@staticmethod
|
328
468
|
def check_agents_have_contracts(scenario: Scenario) -> None:
|
329
|
-
"""
|
469
|
+
"""Loads a warning for each agent without any assigned contract.
|
470
|
+
|
471
|
+
Args:
|
472
|
+
scenario: to search for agents without any contract
|
473
|
+
"""
|
330
474
|
senders = [contract.sender_id for contract in scenario.contracts]
|
331
475
|
receivers = [contract.receiver_id for contract in scenario.contracts]
|
332
476
|
active_agents = set(senders + receivers)
|