cave-utils 2.2.0b2__tar.gz → 2.2.0b4__tar.gz
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.
- {cave_utils-2.2.0b2/cave_utils.egg-info → cave_utils-2.2.0b4}/PKG-INFO +1 -1
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/cave_utils/api/__init__.py +3 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/cave_utils/api/appBar.py +2 -3
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/cave_utils/api/groupedOutputs.py +32 -17
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/cave_utils/api/mapFeatures.py +19 -10
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/cave_utils/api/pages.py +3 -1
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/cave_utils/api/settings.py +5 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/cave_utils/api_utils/general.py +57 -32
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/cave_utils/api_utils/validator.py +2 -2
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/cave_utils/api_utils/validator_utils.py +90 -16
- cave_utils-2.2.0b4/cave_utils/socket.py +12 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4/cave_utils.egg-info}/PKG-INFO +1 -1
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/pyproject.toml +1 -1
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/setup.cfg +1 -1
- cave_utils-2.2.0b2/cave_utils/socket.py +0 -9
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/LICENSE +0 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/NOTICE.md +0 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/README.md +0 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/cave_utils/__init__.py +0 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/cave_utils/api/extraKwargs.py +0 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/cave_utils/api/globalOutputs.py +0 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/cave_utils/api/maps.py +0 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/cave_utils/api/panes.py +0 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/cave_utils/api_utils/__init__.py +0 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/cave_utils/arguments.py +0 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/cave_utils/builders/__init__.py +0 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/cave_utils/builders/groups.py +0 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/cave_utils/log.py +0 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/cave_utils.egg-info/SOURCES.txt +0 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/cave_utils.egg-info/dependency_links.txt +0 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/cave_utils.egg-info/requires.txt +0 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/cave_utils.egg-info/top_level.txt +0 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/test/test_arguments.py +0 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/test/test_builders_groups.py +0 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/test/test_import.py +0 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/test/test_log.py +0 -0
- {cave_utils-2.2.0b2 → cave_utils-2.2.0b4}/test/test_validator.py +0 -0
|
@@ -95,6 +95,9 @@ class Root(ApiValidator):
|
|
|
95
95
|
root_data=self.data,
|
|
96
96
|
**kwargs,
|
|
97
97
|
)
|
|
98
|
+
# Special logic to add timeLength to kwargs
|
|
99
|
+
# This is used to validate timeValues across the app
|
|
100
|
+
kwargs["timeLength"] = pamda.path(["settings","time","timeLength"], self.data)
|
|
98
101
|
# Validate panes
|
|
99
102
|
panes_data = self.data.get("panes")
|
|
100
103
|
pane_validPaneIds = []
|
|
@@ -52,9 +52,8 @@ class appBar_data_star(ApiValidator):
|
|
|
52
52
|
bar: str,
|
|
53
53
|
variant: [str, None] = None,
|
|
54
54
|
color: [str, None] = None,
|
|
55
|
-
order: [dict, None] = None,
|
|
56
55
|
apiCommand: [str, None] = None,
|
|
57
|
-
apiCommandKeys: [list, None] = None,
|
|
56
|
+
apiCommandKeys: [list[str], None] = None,
|
|
58
57
|
**kwargs,
|
|
59
58
|
):
|
|
60
59
|
"""
|
|
@@ -88,7 +87,7 @@ class appBar_data_star(ApiValidator):
|
|
|
88
87
|
* **Note**: It must be a valid RGBA string.
|
|
89
88
|
* **Example**: `"rgba(255, 255, 255, 1)"`.
|
|
90
89
|
* **`apiCommand`**: `[str]` = `None` → The name of the [API command][] to trigger.
|
|
91
|
-
* **`apiCommandKeys`**: `[list]` = `None` →
|
|
90
|
+
* **`apiCommandKeys`**: `[list[str]]` = `None` →
|
|
92
91
|
* The root API keys to pass to your `execute_command` function if an
|
|
93
92
|
`apiCommand` is provided. If omitted, all API keys are
|
|
94
93
|
passed to `execute_command`.
|
|
@@ -279,13 +279,13 @@ class groupedOutputs_groupings_star(ApiValidator):
|
|
|
279
279
|
levels_data = self.data.get("levels", {})
|
|
280
280
|
levels_data_keys = list(levels_data.keys())
|
|
281
281
|
data_data = self.data.get("data", {})
|
|
282
|
-
|
|
283
282
|
CustomKeyValidator(
|
|
284
283
|
data=levels_data,
|
|
285
284
|
log=self.log,
|
|
286
285
|
prepend_path=["levels"],
|
|
287
286
|
validator=groupedOutputs_groupings_star_levels_star,
|
|
288
287
|
acceptable_parents=levels_data_keys,
|
|
288
|
+
acceptable_data_levels=data_data,
|
|
289
289
|
**kwargs,
|
|
290
290
|
)
|
|
291
291
|
groupedOutputs_groupings_star_data(
|
|
@@ -348,7 +348,7 @@ class groupedOutputs_groupings_star_levels_star(ApiValidator):
|
|
|
348
348
|
"""
|
|
349
349
|
|
|
350
350
|
@staticmethod
|
|
351
|
-
def spec(name: str, parent: [str, None] = None, ordering: [list, None] = None, **kwargs):
|
|
351
|
+
def spec(name: str, parent: [str, None] = None, ordering: [list, None] = None, orderWithParent: bool = True, coloring: [dict, None] = None, **kwargs):
|
|
352
352
|
"""
|
|
353
353
|
Arguments:
|
|
354
354
|
|
|
@@ -358,16 +358,22 @@ class groupedOutputs_groupings_star_levels_star(ApiValidator):
|
|
|
358
358
|
* **Notes**:
|
|
359
359
|
* The parent level key must be defined in `groupedOutputs.groupings.*.levels.*`
|
|
360
360
|
* If `None`, this will be considered to be the root of the hierarchy.
|
|
361
|
+
* **`ordering`**: `[list]` →
|
|
362
|
+
* The ordering of individual values for this level in charts and tables.
|
|
363
|
+
* **Note**: If none, the ordering will be alphabetical.
|
|
364
|
+
* **Note**: If a partial ordering is provided, the provided values will be placed first in order.
|
|
365
|
+
* **Note**: If a partial ordering is provided, the remaining values will be placed in alphabetical order.
|
|
366
|
+
* **Note**: All items in this list must be defined in `groupedOutputs.groupings.*.levels.*.values.*`
|
|
367
|
+
* **`orderWithParent`**: `[bool]`=True →
|
|
368
|
+
* Weather or not to order this level based on the parent level.
|
|
369
|
+
* If `True`, the ordering of this level will also be based on the parent level.
|
|
370
|
+
* If `False`, the ordering will be based on the ordering of this level only.
|
|
371
|
+
* **`coloring`**: `[dict]` →
|
|
372
|
+
* A dictionary of colors to be used for the level.
|
|
373
|
+
* Each key in this dictionary is a value in the level.
|
|
374
|
+
* Each value in this dictionary is an rgba string.
|
|
375
|
+
* **See**: `cave_utils.api.groupedOutputs.groupedOutputs_groupings_star_levels_star_coloring`
|
|
361
376
|
"""
|
|
362
|
-
# TODO: Figure out new way for ordering
|
|
363
|
-
# - `ordering`:
|
|
364
|
-
# - Type: list
|
|
365
|
-
# - What: The ordering of individual values for this level in charts and tables.
|
|
366
|
-
# - Note: If none, the ordering will be alphabetical.
|
|
367
|
-
# - Note: If a partial ordering is provided, the provided values will be placed first in order.
|
|
368
|
-
# - Note: If a partial ordering is provided, the remaining values will be placed in alphabetical order.
|
|
369
|
-
# - Note: All items in this list must be defined in `groupedOutputs.groupings.*.levels.*.values.*`
|
|
370
|
-
# """
|
|
371
377
|
return {"kwargs": kwargs, "accepted_values": {}}
|
|
372
378
|
|
|
373
379
|
def __extend_spec__(self, **kwargs):
|
|
@@ -378,9 +384,18 @@ class groupedOutputs_groupings_star_levels_star(ApiValidator):
|
|
|
378
384
|
valid_values=kwargs.get("acceptable_parents", []),
|
|
379
385
|
prepend_path=["parent"],
|
|
380
386
|
)
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
+
ordering = self.data.get("ordering")
|
|
388
|
+
if ordering is not None:
|
|
389
|
+
self.__check_subset_valid__(
|
|
390
|
+
subset=ordering, valid_values=kwargs.get("acceptable_data_levels", {}).get(kwargs.get('CustomKeyValidatorFieldId'),[]), prepend_path=["ordering"]
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
coloring = self.data.get("coloring")
|
|
394
|
+
if coloring is not None:
|
|
395
|
+
self.__check_subset_valid__(
|
|
396
|
+
subset=list(coloring.keys()),
|
|
397
|
+
valid_values=kwargs.get("acceptable_data_levels", {}).get(kwargs.get('CustomKeyValidatorFieldId'),[]),
|
|
398
|
+
prepend_path=["coloring"],
|
|
399
|
+
)
|
|
400
|
+
for key, value in coloring.items():
|
|
401
|
+
self.__check_rgba_string_valid__(rgba_string=value, prepend_path=['coloring', key])
|
|
@@ -155,8 +155,10 @@ class mapFeatures_data_star_data(ApiValidator):
|
|
|
155
155
|
**kwargs,
|
|
156
156
|
)
|
|
157
157
|
# Validate that all lengths are the same
|
|
158
|
-
lengths = [
|
|
159
|
-
len(v) for k, v in
|
|
158
|
+
lengths = [
|
|
159
|
+
len(v) for k, v in location_data.items() if k not in ['timeValues', 'order']
|
|
160
|
+
] + [
|
|
161
|
+
len(v) for k, v in valueLists_data.items() if k not in ['timeValues', 'order']
|
|
160
162
|
]
|
|
161
163
|
if len(set(lengths)) > 1:
|
|
162
164
|
self.__error__(msg=f"location and valueLists keys must have the same length.", path=[])
|
|
@@ -185,16 +187,19 @@ class mapFeatures_data_star_data_location(ApiValidator):
|
|
|
185
187
|
layer_type = kwargs.get("layer_type")
|
|
186
188
|
layer_geoJson = kwargs.get("layer_geoJson")
|
|
187
189
|
passed_keys = list(self.data.keys())
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
if layer_type == "geo" or (layer_type == "arc" and layer_geoJson):
|
|
190
|
+
optional_keys = []
|
|
191
|
+
if layer_type == "geo":
|
|
191
192
|
required_keys = ["geoJsonValue"]
|
|
192
193
|
elif layer_type == "arc":
|
|
193
|
-
if
|
|
194
|
+
if layer_geoJson is not None:
|
|
195
|
+
required_keys = ["geoJsonValue"]
|
|
196
|
+
optional_keys += ["path", "startLatitude", "startLongitude", "endLatitude", "endLongitude","startAltitude", "endAltitude"]
|
|
197
|
+
elif "path" in passed_keys:
|
|
194
198
|
required_keys = ["path"]
|
|
199
|
+
optional_keys += ["startLatitude", "startLongitude", "endLatitude", "endLongitude", "geoJsonValue","startAltitude", "endAltitude"]
|
|
195
200
|
else:
|
|
196
201
|
required_keys = ["startLatitude", "startLongitude", "endLatitude", "endLongitude"]
|
|
197
|
-
optional_keys += ["startAltitude", "endAltitude"]
|
|
202
|
+
optional_keys += ["startAltitude", "endAltitude", "geoJsonValue", "path"]
|
|
198
203
|
else:
|
|
199
204
|
required_keys = ["latitude", "longitude"]
|
|
200
205
|
optional_keys += ["altitude"]
|
|
@@ -209,8 +214,12 @@ class mapFeatures_data_star_data_location(ApiValidator):
|
|
|
209
214
|
path=[],
|
|
210
215
|
)
|
|
211
216
|
continue
|
|
212
|
-
if key
|
|
213
|
-
|
|
217
|
+
if key == "geoJsonValue":
|
|
218
|
+
if len(value_list)!=len(set(value_list)):
|
|
219
|
+
self.__warn__(
|
|
220
|
+
msg=f"`geoJsonValue` should be a list of unique values. Otherwise, the corresponding map feature may not render correctly.",
|
|
221
|
+
path=[key],
|
|
222
|
+
)
|
|
214
223
|
if not isinstance(value_list, list):
|
|
215
224
|
self.__error__(
|
|
216
225
|
msg=f"`{key}` must be a list but got {type(value_list)} instead.", path=[key]
|
|
@@ -240,7 +249,7 @@ class mapFeatures_data_star_data_location(ApiValidator):
|
|
|
240
249
|
altitudes = None
|
|
241
250
|
except:
|
|
242
251
|
self.__error__(
|
|
243
|
-
msg=f"`path` must be a list of lists of lists of length 2 or 3. EG: `[[[0,0],[1,1]],[[2,2],[3,3],[4,4],[5,5]]]`",
|
|
252
|
+
msg=f"`path` must be a list of lists of lists of length 2 [long,lat] or 3 [long,lat,alt]. EG: `[[[0,0],[1,1]],[[2,2],[3,3],[4,4],[5,5]]]`",
|
|
244
253
|
path=[key],
|
|
245
254
|
)
|
|
246
255
|
continue
|
|
@@ -82,6 +82,7 @@ class pages_data_star_pageLayout(ApiValidator):
|
|
|
82
82
|
statId: [str, list, None] = None,
|
|
83
83
|
showToolbar: bool = True,
|
|
84
84
|
maximized: bool = False,
|
|
85
|
+
defaultToZero: bool = False,
|
|
85
86
|
**kwargs,
|
|
86
87
|
):
|
|
87
88
|
"""
|
|
@@ -135,7 +136,8 @@ class pages_data_star_pageLayout(ApiValidator):
|
|
|
135
136
|
* **Note**: If left unspecified (i.e., `None`), it will default to `settings.showToolbar`.
|
|
136
137
|
* **`maximized`**: `[bool]` = `False` → Whether or not the layout should be maximized.
|
|
137
138
|
* **Note**: If more than one chart belonging to the same page layout is set to `True`, the first one found in the list will take precedence.
|
|
138
|
-
|
|
139
|
+
* **`defaultToZero`**: `[bool]` = `False` → Whether or not the chart should default missing values to zero.
|
|
140
|
+
|
|
139
141
|
[area chart]: https://en.wikipedia.org/wiki/Area_chart
|
|
140
142
|
[bar chart]: https://en.wikipedia.org/wiki/Bar_chart
|
|
141
143
|
[stacked bar chart]: https://en.wikipedia.org/wiki/Bar_chart
|
|
@@ -291,3 +291,8 @@ class settings_time(ApiValidator):
|
|
|
291
291
|
* **Example**: `"Decade"`.
|
|
292
292
|
"""
|
|
293
293
|
return {"kwargs": kwargs, "accepted_values": {}}
|
|
294
|
+
|
|
295
|
+
def __extend_spec__(self, **kwargs):
|
|
296
|
+
timeLength = self.data.get("timeLength")
|
|
297
|
+
if timeLength < 1:
|
|
298
|
+
self.__error__(f"Time length must be greater than 0.", path=["timeLength"])
|
|
@@ -4,6 +4,7 @@ General API Spec items that are found in multiple places. This is not a key that
|
|
|
4
4
|
from pamda import pamda
|
|
5
5
|
import type_enforced
|
|
6
6
|
from cave_utils.api_utils.validator_utils import ApiValidator, CustomKeyValidator
|
|
7
|
+
from typing import Literal
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
@type_enforced.Enforcer
|
|
@@ -17,7 +18,7 @@ class props(ApiValidator):
|
|
|
17
18
|
display: [bool, None] = None,
|
|
18
19
|
enabled: [bool, None] = None,
|
|
19
20
|
apiCommand: [str, None] = None,
|
|
20
|
-
apiCommandKeys: [list, None] = None,
|
|
21
|
+
apiCommandKeys: [list[str], None] = None,
|
|
21
22
|
options: [dict, None] = None,
|
|
22
23
|
placeholder: [str, None] = None,
|
|
23
24
|
maxValue: [float, int, None] = None,
|
|
@@ -29,7 +30,7 @@ class props(ApiValidator):
|
|
|
29
30
|
precision: [int, None] = None,
|
|
30
31
|
notationDisplay: [str, None] = None,
|
|
31
32
|
unit: [str, None] = None,
|
|
32
|
-
views: [list, None] = None,
|
|
33
|
+
views: [list[str], None] = None,
|
|
33
34
|
legendNotation: [str, None] = None,
|
|
34
35
|
legendPrecision: [int, None] = None,
|
|
35
36
|
legendNotationDisplay: [str, None] = None,
|
|
@@ -99,7 +100,7 @@ class props(ApiValidator):
|
|
|
99
100
|
* **`apiCommand`**: `[str]` = `None` → The name of the API command to trigger.
|
|
100
101
|
* **Note**: If `None`, no `apiCommand` is triggered.
|
|
101
102
|
* **Note**: This attribute is applicable to all props except `"head"` props.
|
|
102
|
-
* **`apiCommandKeys`**: `[list]` = `None` →
|
|
103
|
+
* **`apiCommandKeys`**: `[list[str]]` = `None` →
|
|
103
104
|
* The root API keys to pass to your `execute_command` function if an `apiCommand` is provided.
|
|
104
105
|
* **Note**: If `None`, all API keys are passed to your `execute_command`.
|
|
105
106
|
* **Note**: This attribute is applicable to all props except `"head"` props.
|
|
@@ -126,15 +127,28 @@ class props(ApiValidator):
|
|
|
126
127
|
* **`rows`**: `[int]` = `None` →
|
|
127
128
|
* The fixed number of rows to show for a `"textarea"` variant.
|
|
128
129
|
* **Note**: This attribute is applicable exclusively to `"text"` props.
|
|
129
|
-
* **`views`**: `[list
|
|
130
|
+
* **`views`**: `[list[str]]` →
|
|
130
131
|
* The available time units for the represented date and/or time.
|
|
132
|
+
* **Default Value**:
|
|
133
|
+
* When **`variant`** == `"date"`: `["year", "day"]`
|
|
134
|
+
* When **`variant`** == `"time"`: `["hours", "minutes"]`
|
|
135
|
+
* When **`variant`** == `"datetime"`: `["year", "day", "hours", "minutes"]`
|
|
131
136
|
* **Accepted Values**:
|
|
132
|
-
* `"
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
* `"
|
|
137
|
-
|
|
137
|
+
* When **`variant`** == `"date"`:
|
|
138
|
+
* `"year"`: The year view
|
|
139
|
+
* `"month"`: The month view
|
|
140
|
+
* `"day"`: The day view
|
|
141
|
+
* When **`variant`** == `"time"`:
|
|
142
|
+
* `"hours"`: The hours view
|
|
143
|
+
* `"minutes"`: The minutes view
|
|
144
|
+
* `"seconds"`: The seconds view
|
|
145
|
+
* When **`variant`** == `"datetime"`:
|
|
146
|
+
* `"year"`: The year view
|
|
147
|
+
* `"month"`: The month view
|
|
148
|
+
* `"day"`: The day view
|
|
149
|
+
* `"hours"`: The hours view
|
|
150
|
+
* `"minutes"`: The minutes view
|
|
151
|
+
* `"seconds"`: The seconds view
|
|
138
152
|
* **Notes**:
|
|
139
153
|
* The views will be presented in the order specified in the `views` array.
|
|
140
154
|
* This attribute is applicable exclusively to `"date"` props.
|
|
@@ -173,7 +187,7 @@ class props(ApiValidator):
|
|
|
173
187
|
* **Notes**:
|
|
174
188
|
* If left unspecified (i.e., `None`), it will default to `settings.defaults.unitPlacement`.
|
|
175
189
|
* This attribute is applicable exclusively to `"num"` props.
|
|
176
|
-
* **`notation`**: `[
|
|
190
|
+
* **`notation`**: `[str]` = `"standard"` → The formatting style of a numeric value.
|
|
177
191
|
* **Accepted Values**:
|
|
178
192
|
* `"standard"`: Plain number formatting
|
|
179
193
|
* `"compact"`: Resembles the [metric prefix][] system
|
|
@@ -184,14 +198,18 @@ class props(ApiValidator):
|
|
|
184
198
|
* This attribute is applicable exclusively to `"num"` props.
|
|
185
199
|
* **`notationDisplay`**: `[str]` = `"e+"` | `"short"` → Further customize the formatting within the selected `notation`.
|
|
186
200
|
* **Accepted Values**:
|
|
187
|
-
*
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
* `"
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
201
|
+
* When **`notation`** == `"compact"`:
|
|
202
|
+
* `"short"`: Add symbols `K`, `M`, `B`, and `T` (in `"en-US"`) to denote thousands, millions, billions, and trillions, respectively.
|
|
203
|
+
* `"long"`: Present numeric values with the informal suffix words `thousand`, `million`, `billion`, and `trillion` (in `"en-US"`).
|
|
204
|
+
* When **`notation`** == `"scientific"` or `"engineering"`:
|
|
205
|
+
* `"e"`: Exponent symbol in lowercase as per the chosen `locale` identifier
|
|
206
|
+
* `"e+"`: Similar to `"e"`, but with a plus sign for positive exponents.
|
|
207
|
+
* `"E"`: Exponent symbol in uppercase as per the chosen `locale` identifier
|
|
208
|
+
* `"E+"`: Similar to `"E"`, but with a plus sign for positive exponents.
|
|
209
|
+
* `"x10^"`: Formal scientific notation representation
|
|
210
|
+
* `"x10^+"`: Similar to `"x10^"`, with a plus sign for positive exponents.
|
|
211
|
+
* When **`notation`** == `"standard"`:
|
|
212
|
+
* No `notationDisplay` option is allowed for a `"standard"` notation
|
|
195
213
|
* **Notes**:
|
|
196
214
|
* No `notationDisplay` option is provided for a `"standard"` notation
|
|
197
215
|
* The options `"short"` and `"long"` are only provided for the `"compact"` notation
|
|
@@ -304,20 +322,30 @@ class props(ApiValidator):
|
|
|
304
322
|
for k, v in passed_values.items():
|
|
305
323
|
if k not in required_fields + optional_fields:
|
|
306
324
|
kwargs[k] = v
|
|
325
|
+
notationDisplay_options_dict = {
|
|
326
|
+
'compact': ['short', 'long'],
|
|
327
|
+
'scientific': ['e', 'e+', 'E', 'E+', 'x10^', 'x10^+'],
|
|
328
|
+
'engineering': ['e', 'e+', 'E', 'E+', 'x10^', 'x10^+'],
|
|
329
|
+
'standard': []
|
|
330
|
+
}
|
|
331
|
+
notation = passed_values.get('notation', 'standard')
|
|
332
|
+
legendNotation = passed_values.get('legendNotation', 'standard')
|
|
333
|
+
view_options_dict = {
|
|
334
|
+
"date": ["year", "month", "day"],
|
|
335
|
+
"time": ["hours", "minutes", "seconds"],
|
|
336
|
+
"datetime": ["year", "month", "day", "hours", "minutes", "seconds"],
|
|
337
|
+
}
|
|
338
|
+
variant = passed_values.get("variant", None)
|
|
307
339
|
return {
|
|
308
340
|
"kwargs": kwargs,
|
|
309
341
|
"accepted_values": {
|
|
310
342
|
"type": ["head", "num", "toggle", "button", "text", "selector", "date", "media"],
|
|
311
|
-
"views":
|
|
343
|
+
"views": view_options_dict.get(variant, []),
|
|
312
344
|
"unitPlacement": ["after", "afterWithSpace", "before", "beforeWithSpace"],
|
|
313
|
-
# TODO: Validate
|
|
314
|
-
# compact: allowed notation displays -> "short", "long"
|
|
315
|
-
# scientific|engineering: allowed notation displays -> "e", "e+", "E", "E+", "x10^", "x10^+"
|
|
316
|
-
# standard: allowed notation displays -> None
|
|
317
345
|
"notation": ["compact", "precision", "scientific", "engineering"],
|
|
318
|
-
"notationDisplay":
|
|
346
|
+
"notationDisplay": notationDisplay_options_dict.get(notation, []),
|
|
319
347
|
"legendNotation": ["compact", "precision", "scientific", "engineering"],
|
|
320
|
-
"legendNotationDisplay":
|
|
348
|
+
"legendNotationDisplay": notationDisplay_options_dict.get(legendNotation, []),
|
|
321
349
|
"variant": {
|
|
322
350
|
"head": ["column", "row", "icon", "iconRow"],
|
|
323
351
|
"text": ["textarea"],
|
|
@@ -349,15 +377,15 @@ class props(ApiValidator):
|
|
|
349
377
|
**kwargs,
|
|
350
378
|
)
|
|
351
379
|
|
|
352
|
-
|
|
380
|
+
@type_enforced.Enforcer
|
|
353
381
|
class props_options(ApiValidator):
|
|
354
382
|
@staticmethod
|
|
355
|
-
def spec(name: str, path: [list, None] = None, **kwargs):
|
|
383
|
+
def spec(name: str, path: [list[str], None] = None, **kwargs):
|
|
356
384
|
"""
|
|
357
385
|
Arguments:
|
|
358
386
|
|
|
359
387
|
* **`name`**: `[str]` → The name of the option.
|
|
360
|
-
* **`path`**: `[list]` = `None` → The path to an option.
|
|
388
|
+
* **`path`**: `[list[str]]` = `None` → The path to an option.
|
|
361
389
|
* **Notes**:
|
|
362
390
|
* If `None`, the option will not be selectable
|
|
363
391
|
* This attribute is applicable exclusively to `"nested"` props
|
|
@@ -379,9 +407,6 @@ class props_options(ApiValidator):
|
|
|
379
407
|
msg="`path` must be specified and a list of strings for nested options"
|
|
380
408
|
)
|
|
381
409
|
return
|
|
382
|
-
self.__check_type_list__(
|
|
383
|
-
data=self.data.get("path", []), types=(str,), prepend_path=["path"]
|
|
384
|
-
)
|
|
385
410
|
|
|
386
411
|
|
|
387
412
|
@type_enforced.Enforcer
|
|
@@ -8,7 +8,7 @@ import type_enforced
|
|
|
8
8
|
|
|
9
9
|
@type_enforced.Enforcer
|
|
10
10
|
class Validator:
|
|
11
|
-
def __init__(self, session_data, ignore_keys: list = list(), **kwargs):
|
|
11
|
+
def __init__(self, session_data, ignore_keys: list[str] = list(), **kwargs):
|
|
12
12
|
"""
|
|
13
13
|
Util to validate your session_data against the API spec.
|
|
14
14
|
|
|
@@ -16,7 +16,7 @@ class Validator:
|
|
|
16
16
|
|
|
17
17
|
* **`session_data`**: `[dict]` → The data to validate.
|
|
18
18
|
* **Note**: This should be the data you are sending to the server.
|
|
19
|
-
* **`ignore_keys`**: `[list]` = `None` → Keys to ignore when validating.
|
|
19
|
+
* **`ignore_keys`**: `[list[str]]` = `None` → Keys to ignore when validating.
|
|
20
20
|
* **Note**: Any keys specified here will be not be validated if encountered in the data at any level.
|
|
21
21
|
"""
|
|
22
22
|
self.session_data = session_data
|
|
@@ -24,23 +24,23 @@ class ApiValidator:
|
|
|
24
24
|
"accepted_values": {},
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
def __validate__(self, data, log: LogObject, prepend_path: list = list(), **kwargs):
|
|
27
|
+
def __validate__(self, data:dict, log: LogObject, prepend_path: list[str] = list(), **kwargs):
|
|
28
28
|
"""
|
|
29
29
|
Run the API validation process for the passed data.
|
|
30
30
|
"""
|
|
31
|
-
|
|
31
|
+
# Make a copy of the data to avoid modifying the original
|
|
32
|
+
self.data = {**data}
|
|
32
33
|
self.ignore_keys = kwargs.get("ignore_keys", set())
|
|
33
34
|
self.log = LogHelper(log=log, prepend_path=prepend_path)
|
|
34
35
|
try:
|
|
36
|
+
self.__genericKeyValidation__(**kwargs)
|
|
35
37
|
spec_output = self.spec(**self.data)
|
|
36
38
|
extra_kwargs = spec_output.get("kwargs", {})
|
|
37
|
-
# TODO: Find way to custom check timeValues and order
|
|
38
|
-
extra_kwargs.pop("order", None)
|
|
39
|
-
extra_kwargs.pop("timeValues", None)
|
|
40
39
|
if extra_kwargs != {}:
|
|
41
40
|
self.__warn__(
|
|
42
41
|
msg=f"Unknown Fields: {str(list(extra_kwargs.keys()))}",
|
|
43
|
-
)
|
|
42
|
+
)
|
|
43
|
+
|
|
44
44
|
except Exception as e:
|
|
45
45
|
self.__error__(
|
|
46
46
|
msg=f"Error validating spec: {e}",
|
|
@@ -75,21 +75,95 @@ class ApiValidator:
|
|
|
75
75
|
def __extend_spec__(self, **kwargs):
|
|
76
76
|
pass
|
|
77
77
|
|
|
78
|
+
# Additional core validations for generic terms like `order` and `timeValues`
|
|
79
|
+
def __genericKeyValidation__(self, **kwargs):
|
|
80
|
+
# Remove `timeValues` out prior to each level validation
|
|
81
|
+
data_timeValues = self.data.pop("timeValues", None)
|
|
82
|
+
# Remove `order` out prior to each level validation
|
|
83
|
+
data_order = self.data.pop("order", None)
|
|
84
|
+
if data_timeValues is not None:
|
|
85
|
+
timeLength = kwargs.get("timeLength")
|
|
86
|
+
if timeLength is None:
|
|
87
|
+
self.__error__(
|
|
88
|
+
path=['timeValues'],
|
|
89
|
+
msg="`settings.time.timeLength` must be specified to validate `timeValues`",
|
|
90
|
+
)
|
|
91
|
+
else:
|
|
92
|
+
self.__timeValues_validation__(timeValues=data_timeValues, timeLength=timeLength)
|
|
93
|
+
if data_order is not None:
|
|
94
|
+
self.__order_validation__(order=data_order)
|
|
95
|
+
|
|
96
|
+
@type_enforced.Enforcer
|
|
97
|
+
def __order_validation__(self, order:dict[list[str,int]]):
|
|
98
|
+
"""
|
|
99
|
+
Check that the ordering options are valid
|
|
100
|
+
"""
|
|
101
|
+
orderable_data_keys = {key: list(value.keys()) for key, value in self.data.items() if isinstance(value, dict)}
|
|
102
|
+
if self.__check_subset_valid__(
|
|
103
|
+
subset=list(order.keys()),
|
|
104
|
+
valid_values=list(orderable_data_keys.keys()),
|
|
105
|
+
prepend_path=["order"],
|
|
106
|
+
):
|
|
107
|
+
for order_key, order_list in order.items():
|
|
108
|
+
self.__check_subset_valid__(
|
|
109
|
+
subset=order_list,
|
|
110
|
+
valid_values=orderable_data_keys[order_key],
|
|
111
|
+
prepend_path=["order", order_key],
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
@type_enforced.Enforcer
|
|
115
|
+
def __timeValues_validation__(self, timeValues: [dict[dict],list[dict]], timeLength:int):
|
|
116
|
+
if len(timeValues) == 0:
|
|
117
|
+
return
|
|
118
|
+
if isinstance(timeValues, list):
|
|
119
|
+
if len(timeValues) != timeLength:
|
|
120
|
+
self.__error__(
|
|
121
|
+
path=['timeValues'],
|
|
122
|
+
msg=f"The length of `timeValues` (as a list) must be equal to `settings.time.timeLength` ({timeLength})",
|
|
123
|
+
)
|
|
124
|
+
return
|
|
125
|
+
if isinstance(timeValues, dict):
|
|
126
|
+
keys = list(timeValues.keys())
|
|
127
|
+
if not all(isinstance(key, int) for key in keys):
|
|
128
|
+
self.__error__(
|
|
129
|
+
path=['timeValues'],
|
|
130
|
+
msg="`timeValues` (as a dict) keys must be integers",
|
|
131
|
+
)
|
|
132
|
+
return
|
|
133
|
+
if not all(key >= 0 and key<timeLength for key in keys):
|
|
134
|
+
self.__error__(
|
|
135
|
+
path=['timeValues'],
|
|
136
|
+
msg=f"`timeValues` (as a dict) keys must be integers between 0 and {timeLength-1} inclusive (1 minus the value at `settings.time.timeLength`)",
|
|
137
|
+
)
|
|
138
|
+
return
|
|
139
|
+
timeValues = list(timeValues.values())
|
|
140
|
+
timeValueTypes = {k:type(v) for k,v in timeValues[0].items()}
|
|
141
|
+
for timeValue in timeValues:
|
|
142
|
+
if timeValueTypes != {k:type(v) for k,v in timeValue.items()}:
|
|
143
|
+
self.__error__(
|
|
144
|
+
path=['timeValues'],
|
|
145
|
+
msg="All timeValues must have the same keys and each key must have the same type",
|
|
146
|
+
)
|
|
147
|
+
return
|
|
148
|
+
# Update the data with the first timeValue prioritizing original data
|
|
149
|
+
# over the first timeValue
|
|
150
|
+
self.data = {**timeValues[0], **self.data}
|
|
151
|
+
|
|
78
152
|
# Error and Warning Helpers
|
|
79
|
-
def __error__(self, msg: str, path: list = list()):
|
|
153
|
+
def __error__(self, msg: str, path: list[str] = list()):
|
|
80
154
|
"""
|
|
81
155
|
Raise an error for the log the log
|
|
82
156
|
"""
|
|
83
157
|
self.log.add(path=path, msg=msg)
|
|
84
158
|
|
|
85
|
-
def __warn__(self, msg: str, path: list = list()):
|
|
159
|
+
def __warn__(self, msg: str, path: list[str] = list()):
|
|
86
160
|
"""
|
|
87
161
|
Raise a warning for the log the log
|
|
88
162
|
"""
|
|
89
163
|
self.log.add(path=path, msg=msg, level="warning")
|
|
90
164
|
|
|
91
165
|
# Useful Validator Checks
|
|
92
|
-
def __check_rgba_string_valid__(self, rgba_string: str, prepend_path: list = list()):
|
|
166
|
+
def __check_rgba_string_valid__(self, rgba_string: str, prepend_path: list[str] = list()):
|
|
93
167
|
"""
|
|
94
168
|
Validate an rgba string and if an issue is present, log an error
|
|
95
169
|
"""
|
|
@@ -112,7 +186,7 @@ class ApiValidator:
|
|
|
112
186
|
except:
|
|
113
187
|
self.__error__(path=prepend_path, msg=msg)
|
|
114
188
|
|
|
115
|
-
def __check_pixel_string_valid__(self, pixel_string: str, prepend_path: list = list()):
|
|
189
|
+
def __check_pixel_string_valid__(self, pixel_string: str, prepend_path: list[str] = list()):
|
|
116
190
|
"""
|
|
117
191
|
Validate a pixel string and if an issue is present, log an error
|
|
118
192
|
"""
|
|
@@ -126,7 +200,7 @@ class ApiValidator:
|
|
|
126
200
|
except:
|
|
127
201
|
self.__error__(path=prepend_path, msg=msg)
|
|
128
202
|
|
|
129
|
-
def __check_url_valid__(self, url: str, prepend_path: list = list()):
|
|
203
|
+
def __check_url_valid__(self, url: str, prepend_path: list[str] = list()):
|
|
130
204
|
"""
|
|
131
205
|
Validate a url and if an issue is present, log an error.
|
|
132
206
|
"""
|
|
@@ -146,7 +220,7 @@ class ApiValidator:
|
|
|
146
220
|
return False
|
|
147
221
|
return True
|
|
148
222
|
|
|
149
|
-
def __check_date_valid__(self, input: str, date_variant: str, prepend_path: list = list()):
|
|
223
|
+
def __check_date_valid__(self, input: str, date_variant: str, prepend_path: list[str] = list()):
|
|
150
224
|
"""
|
|
151
225
|
Validate a date string and if an issue is present, log an error.
|
|
152
226
|
"""
|
|
@@ -189,7 +263,7 @@ class ApiValidator:
|
|
|
189
263
|
self,
|
|
190
264
|
subset: list,
|
|
191
265
|
valid_values: list,
|
|
192
|
-
prepend_path: list = list(),
|
|
266
|
+
prepend_path: list[str] = list(),
|
|
193
267
|
valid_values_count: int = 6,
|
|
194
268
|
):
|
|
195
269
|
"""
|
|
@@ -211,7 +285,7 @@ class ApiValidator:
|
|
|
211
285
|
return False
|
|
212
286
|
return True
|
|
213
287
|
|
|
214
|
-
def __check_coord_path_valid__(self, coord_path: list, prepend_path: list = list()):
|
|
288
|
+
def __check_coord_path_valid__(self, coord_path: list[list[int,float]], prepend_path: list[str] = list()):
|
|
215
289
|
"""
|
|
216
290
|
Validate a coordinate path and if an issue is present, log an error
|
|
217
291
|
"""
|
|
@@ -257,7 +331,7 @@ class ApiValidator:
|
|
|
257
331
|
return False
|
|
258
332
|
return True
|
|
259
333
|
|
|
260
|
-
def __check_type_dict__(self, data: dict, types: tuple, prepend_path: list = list()):
|
|
334
|
+
def __check_type_dict__(self, data: dict, types: tuple, prepend_path: list[str] = list()):
|
|
261
335
|
"""
|
|
262
336
|
Validate a dict only contains certain object types for values and if an issue is present, log an error
|
|
263
337
|
|
|
@@ -274,7 +348,7 @@ class ApiValidator:
|
|
|
274
348
|
return False
|
|
275
349
|
return True
|
|
276
350
|
|
|
277
|
-
def __check_type__(self, value, check_type, prepend_path: list = list()):
|
|
351
|
+
def __check_type__(self, value, check_type, prepend_path: list[str] = list()):
|
|
278
352
|
"""
|
|
279
353
|
Validate a value is a certain type and if an issue is present, log an error
|
|
280
354
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class Socket:
|
|
2
|
+
def __init__(self, silent=False):
|
|
3
|
+
self.silent = silent
|
|
4
|
+
pass
|
|
5
|
+
|
|
6
|
+
def broadcast(self, *args, **kwargs):
|
|
7
|
+
if not self.silent:
|
|
8
|
+
print("broadcast: ", {"args": args, "kwargs": kwargs})
|
|
9
|
+
|
|
10
|
+
def notify(self, *args, **kwargs):
|
|
11
|
+
if not self.silent:
|
|
12
|
+
print("notify: ", {"args": args, "kwargs": kwargs})
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|