cave-utils 2.0.1__tar.gz → 2.0.2__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.0.1 → cave_utils-2.0.2}/PKG-INFO +2 -3
- {cave_utils-2.0.1 → cave_utils-2.0.2}/README.md +1 -2
- {cave_utils-2.0.1 → cave_utils-2.0.2}/cave_utils/api/appBar.py +1 -1
- {cave_utils-2.0.1 → cave_utils-2.0.2}/cave_utils/api/groupedOutputs.py +73 -36
- {cave_utils-2.0.1 → cave_utils-2.0.2}/cave_utils/api/maps.py +9 -2
- {cave_utils-2.0.1 → cave_utils-2.0.2}/cave_utils/api_utils/general.py +45 -12
- {cave_utils-2.0.1 → cave_utils-2.0.2}/cave_utils/api_utils/validator_utils.py +12 -10
- {cave_utils-2.0.1 → cave_utils-2.0.2}/cave_utils.egg-info/PKG-INFO +2 -3
- {cave_utils-2.0.1 → cave_utils-2.0.2}/pyproject.toml +1 -1
- {cave_utils-2.0.1 → cave_utils-2.0.2}/setup.cfg +1 -1
- {cave_utils-2.0.1 → cave_utils-2.0.2}/LICENSE +0 -0
- {cave_utils-2.0.1 → cave_utils-2.0.2}/NOTICE.md +0 -0
- {cave_utils-2.0.1 → cave_utils-2.0.2}/cave_utils/__init__.py +0 -0
- {cave_utils-2.0.1 → cave_utils-2.0.2}/cave_utils/api/__init__.py +0 -0
- {cave_utils-2.0.1 → cave_utils-2.0.2}/cave_utils/api/extraKwargs.py +0 -0
- {cave_utils-2.0.1 → cave_utils-2.0.2}/cave_utils/api/globalOutputs.py +0 -0
- {cave_utils-2.0.1 → cave_utils-2.0.2}/cave_utils/api/mapFeatures.py +0 -0
- {cave_utils-2.0.1 → cave_utils-2.0.2}/cave_utils/api/pages.py +0 -0
- {cave_utils-2.0.1 → cave_utils-2.0.2}/cave_utils/api/panes.py +0 -0
- {cave_utils-2.0.1 → cave_utils-2.0.2}/cave_utils/api/settings.py +0 -0
- {cave_utils-2.0.1 → cave_utils-2.0.2}/cave_utils/api_utils/__init__.py +0 -0
- {cave_utils-2.0.1 → cave_utils-2.0.2}/cave_utils/api_utils/validator.py +0 -0
- {cave_utils-2.0.1 → cave_utils-2.0.2}/cave_utils/arguments.py +0 -0
- {cave_utils-2.0.1 → cave_utils-2.0.2}/cave_utils/log.py +0 -0
- {cave_utils-2.0.1 → cave_utils-2.0.2}/cave_utils/socket.py +0 -0
- {cave_utils-2.0.1 → cave_utils-2.0.2}/cave_utils.egg-info/SOURCES.txt +0 -0
- {cave_utils-2.0.1 → cave_utils-2.0.2}/cave_utils.egg-info/dependency_links.txt +0 -0
- {cave_utils-2.0.1 → cave_utils-2.0.2}/cave_utils.egg-info/requires.txt +0 -0
- {cave_utils-2.0.1 → cave_utils-2.0.2}/cave_utils.egg-info/top_level.txt +0 -0
- {cave_utils-2.0.1 → cave_utils-2.0.2}/test/test_arguments.py +0 -0
- {cave_utils-2.0.1 → cave_utils-2.0.2}/test/test_import.py +0 -0
- {cave_utils-2.0.1 → cave_utils-2.0.2}/test/test_log.py +0 -0
- {cave_utils-2.0.1 → cave_utils-2.0.2}/test/test_validator.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cave_utils
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.2
|
|
4
4
|
Summary: Python wrapper for api use in the cave_app
|
|
5
5
|
Author-email: Connor Makowski <conmak@mit.edu>
|
|
6
6
|
Project-URL: Homepage, https://github.com/mit-cave/cave_utils
|
|
@@ -80,9 +80,8 @@ pip install cave_utils
|
|
|
80
80
|
|
|
81
81
|
2. Remove `cave_utils` from the root `requirements.txt` file
|
|
82
82
|
|
|
83
|
-
3. In your cave_app, set `
|
|
83
|
+
3. In your cave_app, set `LIVE_API_VALIDATION_PRINT=True` in the `.env` file
|
|
84
84
|
- This will validate your data every time an API command is called for each session
|
|
85
|
-
- Outputs will be stored in `logs/validation/{session_name}.log`
|
|
86
85
|
|
|
87
86
|
4. Use the following command to run your cave_app:
|
|
88
87
|
`cave run --docker-args "--volume {local_path_to_cave_utils}/cave_utils:/cave_utils"`
|
|
@@ -64,9 +64,8 @@ pip install cave_utils
|
|
|
64
64
|
|
|
65
65
|
2. Remove `cave_utils` from the root `requirements.txt` file
|
|
66
66
|
|
|
67
|
-
3. In your cave_app, set `
|
|
67
|
+
3. In your cave_app, set `LIVE_API_VALIDATION_PRINT=True` in the `.env` file
|
|
68
68
|
- This will validate your data every time an API command is called for each session
|
|
69
|
-
- Outputs will be stored in `logs/validation/{session_name}.log`
|
|
70
69
|
|
|
71
70
|
4. Use the following command to run your cave_app:
|
|
72
71
|
`cave run --docker-args "--volume {local_path_to_cave_utils}/cave_utils:/cave_utils"`
|
|
@@ -78,7 +78,7 @@ class appBar_data_star(ApiValidator):
|
|
|
78
78
|
* `"lowerRight"`: Lower section of the right-side bar
|
|
79
79
|
* **`variant`**: `[str]` = `None` → The variant of the button.
|
|
80
80
|
* **Accepted Values**:
|
|
81
|
-
* When **`type`** == `"pane"
|
|
81
|
+
* When **`type`** == `"pane"`:
|
|
82
82
|
* `"modal"`: A [modal pane][]
|
|
83
83
|
* `"wall"`: A [wall pane][]
|
|
84
84
|
* Otherwise:
|
|
@@ -60,9 +60,10 @@ class groupedOutputs_data_star(ApiValidator):
|
|
|
60
60
|
* **`stats`**: `[dict]` → A dictionary of stats that are available for the data.
|
|
61
61
|
* **See**: `cave_utils.api.groupedOutputs.groupedOutputs_data_star_stats`
|
|
62
62
|
**`valueLists`**: `[dict]` → A dictionary of lists that make up the stats for the data.
|
|
63
|
+
* **See**: `cave_utils.api.groupedOutputs.groupedOutputs_data_star_valueLists`
|
|
64
|
+
* **Note**: Each key must also be a key in `groupedOutputs.data.*.stats`.
|
|
63
65
|
**`groupLists`**: `[dict]` → A dictionary of lists that make up the groupings for the data.
|
|
64
66
|
"""
|
|
65
|
-
# TODO: Flesh out valueLists and groupLists
|
|
66
67
|
return {"kwargs": kwargs, "accepted_values": {}}
|
|
67
68
|
|
|
68
69
|
def __extend_spec__(self, **kwargs):
|
|
@@ -74,45 +75,37 @@ class groupedOutputs_data_star(ApiValidator):
|
|
|
74
75
|
validator=groupedOutputs_data_star_stats,
|
|
75
76
|
**kwargs,
|
|
76
77
|
)
|
|
77
|
-
list_lengths = []
|
|
78
|
-
pass_list_lengths = True
|
|
79
78
|
# Ensure Valid Value Lists
|
|
80
79
|
valueLists_data = self.data.get("valueLists", {})
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
self.__check_type_list__(
|
|
89
|
-
data=value, types=(int, float), prepend_path=["valueLists", key]
|
|
90
|
-
)
|
|
80
|
+
groupedOutputs_data_star_valueLists(
|
|
81
|
+
data=valueLists_data,
|
|
82
|
+
log=self.log,
|
|
83
|
+
prepend_path=["valueLists"],
|
|
84
|
+
acceptable_data_keys=list(stats_data.keys()),
|
|
85
|
+
**kwargs,
|
|
86
|
+
)
|
|
91
87
|
# Ensure Valid Group Lists
|
|
92
|
-
available_groups = kwargs.get("available_groups", {})
|
|
93
88
|
groupLists_data = self.data.get("groupLists", {})
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
89
|
+
groupedOutputs_data_star_groupLists(
|
|
90
|
+
data=groupLists_data,
|
|
91
|
+
log=self.log,
|
|
97
92
|
prepend_path=["groupLists"],
|
|
93
|
+
**kwargs,
|
|
98
94
|
)
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
95
|
+
|
|
96
|
+
# Ensure that all lists are the same length
|
|
97
|
+
try:
|
|
98
|
+
if (
|
|
99
|
+
len(
|
|
100
|
+
set(
|
|
101
|
+
[len(v) for v in valueLists_data.values()]
|
|
102
|
+
+ [len(v) for v in groupLists_data.values()]
|
|
103
|
+
)
|
|
104
|
+
)
|
|
105
|
+
!= 1
|
|
109
106
|
):
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
subset=value, valid_values=valid_values, prepend_path=["groupLists", key]
|
|
113
|
-
)
|
|
114
|
-
# Ensure all lists are the same length
|
|
115
|
-
if not pass_list_lengths or len(set(list_lengths)) != 1:
|
|
107
|
+
raise Exception
|
|
108
|
+
except:
|
|
116
109
|
self.__error__(
|
|
117
110
|
msg="All values in groupedOutputs.data.*.groupLists and groupedOutputs.data.*.valueLists must be lists of the same length.",
|
|
118
111
|
)
|
|
@@ -180,14 +173,58 @@ class groupedOutputs_data_star_valueLists(ApiValidator):
|
|
|
180
173
|
@staticmethod
|
|
181
174
|
def spec(**kwargs):
|
|
182
175
|
"""
|
|
183
|
-
|
|
184
|
-
|
|
176
|
+
Arguments:
|
|
177
|
+
|
|
178
|
+
- **`yourCustomKeyHere`**: `[list]` →
|
|
179
|
+
- * **Note**: Each custom key passed must also be a key in `groupedOutputs.data.*.stats`.
|
|
180
|
+
- * **Note**: Each value must be a list of integers or floats.
|
|
185
181
|
"""
|
|
186
182
|
return {"kwargs": {}, "accepted_values": {}}
|
|
187
183
|
|
|
188
184
|
def __extend_spec__(self, **kwargs):
|
|
185
|
+
if not self.__check_subset_valid__(
|
|
186
|
+
subset=list(self.data.keys()),
|
|
187
|
+
valid_values=kwargs.get("acceptable_data_keys", []),
|
|
188
|
+
prepend_path=[],
|
|
189
|
+
):
|
|
190
|
+
return
|
|
189
191
|
for key, value in self.data.items():
|
|
190
|
-
self.
|
|
192
|
+
if self.__check_type__(value=value, check_type=(list,), prepend_path=[key]):
|
|
193
|
+
self.__check_type_list__(data=value, types=(int, float), prepend_path=[key])
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
@type_enforced.Enforcer
|
|
197
|
+
class groupedOutputs_data_star_groupLists(ApiValidator):
|
|
198
|
+
"""
|
|
199
|
+
The group lists are located under the path **`groupedOutputs.data.*.groupLists`**.
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
@staticmethod
|
|
203
|
+
def spec(**kwargs):
|
|
204
|
+
"""
|
|
205
|
+
Arguments:
|
|
206
|
+
|
|
207
|
+
- **`yourCustomKeyHere`**: `[list]` →
|
|
208
|
+
- * **Note**: Each value must be a list of strings
|
|
209
|
+
- * **Note**: Each item in the passed value must also be found in `groupedOutputs.groupings.{yourCustomKeyHere}.data.id`.
|
|
210
|
+
"""
|
|
211
|
+
return {"kwargs": {}, "accepted_values": {}}
|
|
212
|
+
|
|
213
|
+
def __extend_spec__(self, **kwargs):
|
|
214
|
+
available_groups = kwargs.get("available_groups", {})
|
|
215
|
+
if self.__check_subset_valid__(
|
|
216
|
+
subset=list(self.data.keys()),
|
|
217
|
+
valid_values=list(available_groups.keys()),
|
|
218
|
+
prepend_path=[],
|
|
219
|
+
):
|
|
220
|
+
for key, value in self.data.items():
|
|
221
|
+
if self.__check_type__(value=value, check_type=(list,), prepend_path=[key]):
|
|
222
|
+
self.__check_type_list__(data=value, types=(str,), prepend_path=[key])
|
|
223
|
+
self.__check_subset_valid__(
|
|
224
|
+
subset=value,
|
|
225
|
+
valid_values=available_groups.get(key, []),
|
|
226
|
+
prepend_path=[key],
|
|
227
|
+
)
|
|
191
228
|
|
|
192
229
|
|
|
193
230
|
@type_enforced.Enforcer
|
|
@@ -365,6 +365,7 @@ class maps_data_star_legendGroups_star_data_star(ApiValidator):
|
|
|
365
365
|
* **Notes**:
|
|
366
366
|
* If `None`, the data layer will not be colored.
|
|
367
367
|
* Does not apply to shape layers
|
|
368
|
+
* Only props of type `"num"`, `"toggle"`, and `"selector"` can be colored.
|
|
368
369
|
* **Example**:
|
|
369
370
|
```py
|
|
370
371
|
"colorByOptions": {
|
|
@@ -386,6 +387,7 @@ class maps_data_star_legendGroups_star_data_star(ApiValidator):
|
|
|
386
387
|
* **Notes**:
|
|
387
388
|
* If `None`, the data layer will not be sized.
|
|
388
389
|
* Does not apply to shape layers
|
|
390
|
+
* Only props of type `"num"` can be sized.
|
|
389
391
|
* **Example**:
|
|
390
392
|
```py
|
|
391
393
|
"sizeByOptions": {
|
|
@@ -447,7 +449,7 @@ class maps_data_star_legendGroups_star_data_star(ApiValidator):
|
|
|
447
449
|
colorBy_availableProps = {
|
|
448
450
|
k: v
|
|
449
451
|
for k, v in available_props.items()
|
|
450
|
-
if v.get("type") in ["num", "toggle", "selector"]
|
|
452
|
+
if v.get("type") in ["num", "toggle", "selector", "text"]
|
|
451
453
|
}
|
|
452
454
|
sizeBy_availableProps = {
|
|
453
455
|
k: v for k, v in available_props.items() if v.get("type") in ["num"]
|
|
@@ -591,8 +593,13 @@ class colorByOptions(ApiValidator):
|
|
|
591
593
|
):
|
|
592
594
|
return
|
|
593
595
|
self.__check_rgba_string_valid__(rgba_string=value, prepend_path=[key])
|
|
596
|
+
elif prop_type == "text":
|
|
597
|
+
for key, value in self.data.items():
|
|
598
|
+
self.__check_rgba_string_valid__(rgba_string=value, prepend_path=[key])
|
|
594
599
|
else:
|
|
595
|
-
self.__error__(
|
|
600
|
+
self.__error__(
|
|
601
|
+
msg=f"Invalid prop type ({prop_type}) for colorByOptions. Allowed props are `num`, `toggle`, `selector`, and `text`"
|
|
602
|
+
)
|
|
596
603
|
|
|
597
604
|
|
|
598
605
|
@type_enforced.Enforcer
|
|
@@ -40,6 +40,7 @@ class props(ApiValidator):
|
|
|
40
40
|
unitPlacement: [str, None] = None,
|
|
41
41
|
locale: [str, None] = None,
|
|
42
42
|
fallbackValue: [str, None] = None,
|
|
43
|
+
allowNone: [bool, None] = None,
|
|
43
44
|
**kwargs,
|
|
44
45
|
):
|
|
45
46
|
"""
|
|
@@ -88,11 +89,14 @@ class props(ApiValidator):
|
|
|
88
89
|
* `"picture"`: Show a PNG or JPG image
|
|
89
90
|
* `"video"`: Display a YouTube, Vimeo, or Dailymotion video clip
|
|
90
91
|
* **`enabled`**: `[bool]` = `True` → Whether or not the prop will be enabled.
|
|
92
|
+
* **Note**: This attribute is applicable to all props except `"head"` props.
|
|
91
93
|
* **`apiCommand`**: `[str]` = `None` → The name of the API command to trigger.
|
|
92
94
|
* **Note**: If `None`, no `apiCommand` is triggered.
|
|
95
|
+
* **Note**: This attribute is applicable to all props except `"head"` props.
|
|
93
96
|
* **`apiCommandKeys`**: `[list]` = `None` →
|
|
94
97
|
* The root API keys to pass to your `execute_command` function if an `apiCommand` is provided.
|
|
95
98
|
* **Note**: If `None`, all API keys are passed to your `execute_command`.
|
|
99
|
+
* **Note**: This attribute is applicable to all props except `"head"` props.
|
|
96
100
|
* **`icon`**: `[str]` = `None` → The icon to use for the prop.
|
|
97
101
|
* **Notes**:
|
|
98
102
|
* It must be a valid icon name from the [react-icons][] bundle, preceded by the abbreviated name of the icon library source.
|
|
@@ -146,8 +150,10 @@ class props(ApiValidator):
|
|
|
146
150
|
* This attribute is applicable exclusively to `"num"` props.
|
|
147
151
|
* **`fallbackValue`**: [str] = `None` → A value to show when the value is missing or invalid.
|
|
148
152
|
* **Notes**:
|
|
153
|
+
* This is only for display purposes as related to number formatting. It does not affect the actual value or any computations.
|
|
154
|
+
* For example, if the value passed is `None`, the fallback value will be displayed instead.
|
|
149
155
|
* If left unspecified (i.e., `None`), it will default to `settings.defaults.fallbackValue`.
|
|
150
|
-
* This attribute
|
|
156
|
+
* This attribute is applicable exclusively to `"num"` props.
|
|
151
157
|
* **`unit`**: `[str]` = `None` → The unit to use for the prop.
|
|
152
158
|
* **Notes**:
|
|
153
159
|
* If left unspecified (i.e., `None`), it will default to `settings.defaults.unit`.
|
|
@@ -229,6 +235,17 @@ class props(ApiValidator):
|
|
|
229
235
|
* Takes precedence over other formatting, except when used in a node cluster and the `cave_utils.api.maps.group` attribute is `True`. In this case, the max value within the node cluster is displayed.
|
|
230
236
|
* If left unspecified (i.e., `None`), it will default to `settings.defaults.legendMaxLabel`.
|
|
231
237
|
* This attribute is applicable exclusively to `"num"` props.
|
|
238
|
+
* **`allowNone`**: `[bool]` = `False` →
|
|
239
|
+
* Whether or not to allow `None` as a valid value for the prop. This is primarily used to help when validating `values` and `valueLists`.
|
|
240
|
+
* **Notes**:
|
|
241
|
+
* If `True`, `None` will be a valid value for the prop.
|
|
242
|
+
* `None` values will be treated differently in the front end
|
|
243
|
+
* For map display purposes: `None` values will be shown as a different color or ignored.
|
|
244
|
+
* See `nullColor` in: `/cave_utils/cave_utils/api/maps.html#colorByOptions`
|
|
245
|
+
* For prop purposes: `None` values will be left blank.
|
|
246
|
+
* If `False`, `None` will not be a valid value for the prop.
|
|
247
|
+
* This attribute is applicable to all props except `"head"` props.
|
|
248
|
+
|
|
232
249
|
|
|
233
250
|
[metric prefix]: https://en.wikipedia.org/wiki/Metric_prefix
|
|
234
251
|
[Scientific notation]: https://en.wikipedia.org/wiki/Scientific_notation
|
|
@@ -236,9 +253,9 @@ class props(ApiValidator):
|
|
|
236
253
|
"""
|
|
237
254
|
passed_values = {k: v for k, v in locals().items() if (v is not None) and k != "kwargs"}
|
|
238
255
|
required_fields = ["name", "type"]
|
|
239
|
-
optional_fields = ["help", "variant"
|
|
256
|
+
optional_fields = ["help", "variant"]
|
|
240
257
|
if type != "head":
|
|
241
|
-
optional_fields += ["apiCommand", "apiCommandKeys"]
|
|
258
|
+
optional_fields += ["enabled", "apiCommand", "apiCommandKeys", "allowNone"]
|
|
242
259
|
if type == "head":
|
|
243
260
|
if variant == "icon":
|
|
244
261
|
required_fields += ["icon"]
|
|
@@ -458,8 +475,12 @@ class values(ApiValidator):
|
|
|
458
475
|
)
|
|
459
476
|
continue
|
|
460
477
|
prop_type = prop_spec.get("type", None)
|
|
478
|
+
if prop_type == "head":
|
|
479
|
+
self.__error__(
|
|
480
|
+
msg=f"`{prop_key}` with the prop type of `{prop_type}` can not have an associated value."
|
|
481
|
+
)
|
|
482
|
+
continue
|
|
461
483
|
acceptable_types = {
|
|
462
|
-
"head": (str,),
|
|
463
484
|
"num": (int, float),
|
|
464
485
|
"toggle": (bool,),
|
|
465
486
|
"button": (str,),
|
|
@@ -467,9 +488,16 @@ class values(ApiValidator):
|
|
|
467
488
|
"selector": (list,),
|
|
468
489
|
"date": (str,),
|
|
469
490
|
"media": (str,),
|
|
470
|
-
}.get(prop_type,
|
|
491
|
+
}.get(prop_type, tuple())
|
|
492
|
+
# Add None to acceptable types if allowed
|
|
493
|
+
if prop_spec.get("allowNone", False):
|
|
494
|
+
acceptable_types += (type(None),)
|
|
495
|
+
# Validate types and continue if invalid
|
|
471
496
|
if not self.__check_type__(prop_value, acceptable_types, prepend_path=[prop_key]):
|
|
472
497
|
continue
|
|
498
|
+
# Continue if the value is None
|
|
499
|
+
if prop_value is None:
|
|
500
|
+
continue
|
|
473
501
|
if prop_type == "num":
|
|
474
502
|
min_value = prop_spec.get("minValue", float("-inf"))
|
|
475
503
|
max_value = prop_spec.get("maxValue", float("inf"))
|
|
@@ -480,10 +508,6 @@ class values(ApiValidator):
|
|
|
480
508
|
elif prop_type == "selector":
|
|
481
509
|
options = list(prop_spec.get("options", {}).keys())
|
|
482
510
|
self.__check_subset_valid__(prop_value, options, prepend_path=[prop_key])
|
|
483
|
-
elif prop_type == "head":
|
|
484
|
-
self.__error__(
|
|
485
|
-
msg=f"`{prop_key}` with the prop type of `{prop_type}` can not have an associated value."
|
|
486
|
-
)
|
|
487
511
|
elif prop_type == "date":
|
|
488
512
|
pass
|
|
489
513
|
# TODO: Validate date string
|
|
@@ -498,7 +522,7 @@ class valueLists(ApiValidator):
|
|
|
498
522
|
"""
|
|
499
523
|
Accepts all arbitrary values depending on what you have in your props as part of the API spec.
|
|
500
524
|
|
|
501
|
-
The valueLists you pass will be validated against the props
|
|
525
|
+
The valueLists you pass will be validated against the `props` from your API spec.
|
|
502
526
|
"""
|
|
503
527
|
return {
|
|
504
528
|
"kwargs": {},
|
|
@@ -520,8 +544,12 @@ class valueLists(ApiValidator):
|
|
|
520
544
|
)
|
|
521
545
|
continue
|
|
522
546
|
prop_type = prop_spec.get("type", None)
|
|
547
|
+
if prop_type == "head":
|
|
548
|
+
self.__error__(
|
|
549
|
+
msg=f"`{prop_key}` with the prop type of `{prop_type}` can not have an associated value."
|
|
550
|
+
)
|
|
551
|
+
continue
|
|
523
552
|
acceptable_types = {
|
|
524
|
-
"head": (str,),
|
|
525
553
|
"num": (int, float),
|
|
526
554
|
"toggle": (bool,),
|
|
527
555
|
"button": (str,),
|
|
@@ -529,11 +557,16 @@ class valueLists(ApiValidator):
|
|
|
529
557
|
"selector": (list,),
|
|
530
558
|
"date": (str,),
|
|
531
559
|
"media": (str,),
|
|
532
|
-
}.get(prop_type,
|
|
560
|
+
}.get(prop_type, tuple())
|
|
561
|
+
# Add None to acceptable types if allowed
|
|
562
|
+
if prop_spec.get("allowNone", False):
|
|
563
|
+
acceptable_types += (type(None),)
|
|
533
564
|
if not self.__check_type_list__(
|
|
534
565
|
data=prop_value_list, types=acceptable_types, prepend_path=[prop_key]
|
|
535
566
|
):
|
|
536
567
|
continue
|
|
568
|
+
if prop_spec.get("allowNone", False):
|
|
569
|
+
prop_value_list = [v for v in prop_value_list if v is not None]
|
|
537
570
|
if prop_type == "num":
|
|
538
571
|
# Validate minimum is met
|
|
539
572
|
min_value = prop_spec.get("minValue")
|
|
@@ -93,40 +93,42 @@ class ApiValidator:
|
|
|
93
93
|
"""
|
|
94
94
|
Validate an rgba string and if an issue is present, log an error
|
|
95
95
|
"""
|
|
96
|
+
msg = "Invalid RGBA string. Must be in the format 'rgba(0, 0, 0, 0)' where each value is an integer between 0 and 255."
|
|
96
97
|
try:
|
|
97
98
|
if "rgba(" != rgba_string[:5]:
|
|
98
|
-
self.__error__(path=prepend_path, msg=
|
|
99
|
+
self.__error__(path=prepend_path, msg=msg)
|
|
99
100
|
return
|
|
100
101
|
if ")" != rgba_string[-1]:
|
|
101
|
-
self.__error__(path=prepend_path, msg=
|
|
102
|
+
self.__error__(path=prepend_path, msg=msg)
|
|
102
103
|
return
|
|
103
104
|
rgba_list = rgba_string[5:-1].replace(" ", "").split(",")
|
|
104
105
|
for rgba in rgba_list:
|
|
105
106
|
if not rgba.isdigit():
|
|
106
|
-
self.__error__(path=prepend_path, msg=
|
|
107
|
+
self.__error__(path=prepend_path, msg=msg)
|
|
107
108
|
return
|
|
108
109
|
if int(rgba) < 0 or int(rgba) > 255:
|
|
109
|
-
self.__error__(path=prepend_path, msg=
|
|
110
|
+
self.__error__(path=prepend_path, msg=msg)
|
|
110
111
|
return
|
|
111
112
|
except:
|
|
112
|
-
self.__error__(path=prepend_path, msg=
|
|
113
|
+
self.__error__(path=prepend_path, msg=msg)
|
|
113
114
|
|
|
114
115
|
def __check_pixel_string_valid__(self, pixel_string: str, prepend_path: list = list()):
|
|
115
116
|
"""
|
|
116
117
|
Validate a pixel string and if an issue is present, log an error
|
|
117
118
|
"""
|
|
119
|
+
msg = "Invalid pixel string. Must be in the format '5px' where the value portion is a whole number."
|
|
118
120
|
try:
|
|
119
121
|
if "px" != pixel_string[-2:]:
|
|
120
|
-
self.__error__(path=prepend_path, msg=
|
|
122
|
+
self.__error__(path=prepend_path, msg=msg)
|
|
121
123
|
return
|
|
122
|
-
if int(pixel_string[:-2])
|
|
123
|
-
self.__error__(path=prepend_path, msg=
|
|
124
|
+
if int(pixel_string[:-2]) < 0:
|
|
125
|
+
self.__error__(path=prepend_path, msg=msg)
|
|
124
126
|
except:
|
|
125
|
-
self.__error__(path=prepend_path, msg=
|
|
127
|
+
self.__error__(path=prepend_path, msg=msg)
|
|
126
128
|
|
|
127
129
|
def __check_url_valid__(self, url: str, prepend_path: list = list()):
|
|
128
130
|
"""
|
|
129
|
-
Validate a url and if an issue is present, log an error
|
|
131
|
+
Validate a url and if an issue is present, log an error.
|
|
130
132
|
"""
|
|
131
133
|
# Use Django regex for URL validation
|
|
132
134
|
# See https://stackoverflow.com/a/7160778/12014156
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cave-utils
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.2
|
|
4
4
|
Summary: Python wrapper for api use in the cave_app
|
|
5
5
|
Author-email: Connor Makowski <conmak@mit.edu>
|
|
6
6
|
Project-URL: Homepage, https://github.com/mit-cave/cave_utils
|
|
@@ -80,9 +80,8 @@ pip install cave_utils
|
|
|
80
80
|
|
|
81
81
|
2. Remove `cave_utils` from the root `requirements.txt` file
|
|
82
82
|
|
|
83
|
-
3. In your cave_app, set `
|
|
83
|
+
3. In your cave_app, set `LIVE_API_VALIDATION_PRINT=True` in the `.env` file
|
|
84
84
|
- This will validate your data every time an API command is called for each session
|
|
85
|
-
- Outputs will be stored in `logs/validation/{session_name}.log`
|
|
86
85
|
|
|
87
86
|
4. Use the following command to run your cave_app:
|
|
88
87
|
`cave run --docker-args "--volume {local_path_to_cave_utils}/cave_utils:/cave_utils"`
|
|
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
|
|
File without changes
|