cave-utils 2.1.2__tar.gz → 2.2.0__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.
Files changed (42) hide show
  1. {cave_utils-2.1.2/cave_utils.egg-info → cave_utils-2.2.0}/PKG-INFO +8 -8
  2. {cave_utils-2.1.2 → cave_utils-2.2.0}/README.md +5 -5
  3. cave_utils-2.2.0/cave_utils/__init__.py +13 -0
  4. {cave_utils-2.1.2 → cave_utils-2.2.0}/cave_utils/api/__init__.py +4 -0
  5. {cave_utils-2.1.2 → cave_utils-2.2.0}/cave_utils/api/appBar.py +3 -3
  6. {cave_utils-2.1.2 → cave_utils-2.2.0}/cave_utils/api/extraKwargs.py +1 -0
  7. {cave_utils-2.1.2 → cave_utils-2.2.0}/cave_utils/api/globalOutputs.py +1 -0
  8. {cave_utils-2.1.2 → cave_utils-2.2.0}/cave_utils/api/groupedOutputs.py +46 -17
  9. {cave_utils-2.1.2 → cave_utils-2.2.0}/cave_utils/api/mapFeatures.py +34 -10
  10. {cave_utils-2.1.2 → cave_utils-2.2.0}/cave_utils/api/maps.py +1 -0
  11. {cave_utils-2.1.2 → cave_utils-2.2.0}/cave_utils/api/pages.py +3 -0
  12. {cave_utils-2.1.2 → cave_utils-2.2.0}/cave_utils/api/panes.py +1 -0
  13. {cave_utils-2.1.2 → cave_utils-2.2.0}/cave_utils/api/settings.py +6 -0
  14. cave_utils-2.2.0/cave_utils/api_utils/__init__.py +22 -0
  15. {cave_utils-2.1.2 → cave_utils-2.2.0}/cave_utils/api_utils/general.py +57 -31
  16. {cave_utils-2.1.2 → cave_utils-2.2.0}/cave_utils/api_utils/validator.py +3 -2
  17. {cave_utils-2.1.2 → cave_utils-2.2.0}/cave_utils/api_utils/validator_utils.py +94 -15
  18. cave_utils-2.2.0/cave_utils/builders/__init__.py +7 -0
  19. cave_utils-2.2.0/cave_utils/builders/groups.py +358 -0
  20. cave_utils-2.2.0/cave_utils/geo_utils.py +115 -0
  21. cave_utils-2.2.0/cave_utils/socket.py +11 -0
  22. {cave_utils-2.1.2 → cave_utils-2.2.0/cave_utils.egg-info}/PKG-INFO +8 -8
  23. {cave_utils-2.1.2 → cave_utils-2.2.0}/cave_utils.egg-info/SOURCES.txt +5 -0
  24. cave_utils-2.2.0/cave_utils.egg-info/requires.txt +2 -0
  25. {cave_utils-2.1.2 → cave_utils-2.2.0}/pyproject.toml +2 -2
  26. {cave_utils-2.1.2 → cave_utils-2.2.0}/setup.cfg +1 -1
  27. cave_utils-2.2.0/test/test_builders_groups.py +168 -0
  28. cave_utils-2.2.0/test/test_geo_utils.py +17 -0
  29. cave_utils-2.1.2/cave_utils/__init__.py +0 -4
  30. cave_utils-2.1.2/cave_utils/api_utils/__init__.py +0 -3
  31. cave_utils-2.1.2/cave_utils/socket.py +0 -9
  32. cave_utils-2.1.2/cave_utils.egg-info/requires.txt +0 -2
  33. {cave_utils-2.1.2 → cave_utils-2.2.0}/LICENSE +0 -0
  34. {cave_utils-2.1.2 → cave_utils-2.2.0}/NOTICE.md +0 -0
  35. {cave_utils-2.1.2 → cave_utils-2.2.0}/cave_utils/arguments.py +0 -0
  36. {cave_utils-2.1.2 → cave_utils-2.2.0}/cave_utils/log.py +0 -0
  37. {cave_utils-2.1.2 → cave_utils-2.2.0}/cave_utils.egg-info/dependency_links.txt +0 -0
  38. {cave_utils-2.1.2 → cave_utils-2.2.0}/cave_utils.egg-info/top_level.txt +0 -0
  39. {cave_utils-2.1.2 → cave_utils-2.2.0}/test/test_arguments.py +0 -0
  40. {cave_utils-2.1.2 → cave_utils-2.2.0}/test/test_import.py +0 -0
  41. {cave_utils-2.1.2 → cave_utils-2.2.0}/test/test_log.py +0 -0
  42. {cave_utils-2.1.2 → cave_utils-2.2.0}/test/test_validator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cave_utils
3
- Version: 2.1.2
3
+ Version: 2.2.0
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
@@ -11,8 +11,8 @@ Requires-Python: >=3.9
11
11
  Description-Content-Type: text/markdown
12
12
  License-File: LICENSE
13
13
  License-File: NOTICE.md
14
- Requires-Dist: pamda>=2.3.0
15
- Requires-Dist: type_enforced>=1.2.0
14
+ Requires-Dist: pamda>=2.5.0
15
+ Requires-Dist: type_enforced>=1.4.0
16
16
 
17
17
  Cave Utilities for the Cave App
18
18
  ==========
@@ -58,9 +58,9 @@ pip install cave_utils
58
58
 
59
59
  # cave_utils development
60
60
 
61
- ## Using Live Validation
61
+ ## Using Local Hotloading
62
62
 
63
- 1. In your cave_app, update the following file:
63
+ 1. In your `cave_app`, update the following file:
64
64
 
65
65
  `utils/run_server.sh`
66
66
  ```
@@ -80,12 +80,12 @@ 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 `LIVE_API_VALIDATION_PRINT=True` in the `.env` file
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
85
 
86
- 4. Use the following command to run your cave_app:
86
+ 4. Use the following command to run your `cave_app`:
87
87
  `cave run --docker-args "--volume {local_path_to_cave_utils}/cave_utils:/cave_utils"`
88
- - As you edit cave_utils, the logs will be updated live
88
+ - As you edit `cave_utils`, any changes will be hotloaded into your running `cave_app`
89
89
 
90
90
  ## Using interactive mode and running tests
91
91
 
@@ -42,9 +42,9 @@ pip install cave_utils
42
42
 
43
43
  # cave_utils development
44
44
 
45
- ## Using Live Validation
45
+ ## Using Local Hotloading
46
46
 
47
- 1. In your cave_app, update the following file:
47
+ 1. In your `cave_app`, update the following file:
48
48
 
49
49
  `utils/run_server.sh`
50
50
  ```
@@ -64,12 +64,12 @@ 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 `LIVE_API_VALIDATION_PRINT=True` in the `.env` file
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
69
 
70
- 4. Use the following command to run your cave_app:
70
+ 4. Use the following command to run your `cave_app`:
71
71
  `cave run --docker-args "--volume {local_path_to_cave_utils}/cave_utils:/cave_utils"`
72
- - As you edit cave_utils, the logs will be updated live
72
+ - As you edit `cave_utils`, any changes will be hotloaded into your running `cave_app`
73
73
 
74
74
  ## Using interactive mode and running tests
75
75
 
@@ -0,0 +1,13 @@
1
+ """
2
+ The cave_utils package is a collection of utility classes and functions that are used in the CAVE project.
3
+
4
+ On the left hand side, you will find all available submodules, and their associated docs.
5
+
6
+ Click on a submodule to see the documentation for that submodule.
7
+ """
8
+
9
+ from .log import LogObject, LogHelper
10
+ from .socket import Socket
11
+ from .api_utils.validator import Validator
12
+ from .arguments import Arguments
13
+ from .geo_utils import GeoUtils
@@ -11,6 +11,7 @@ Submodules (and their classes) are used to define the api at each level. You can
11
11
 
12
12
  See the left hand side for all available submodules.
13
13
  """
14
+
14
15
  from cave_utils.api_utils.validator_utils import *
15
16
  from cave_utils.api.extraKwargs import extraKwargs
16
17
  from cave_utils.api.settings import settings
@@ -95,6 +96,9 @@ class Root(ApiValidator):
95
96
  root_data=self.data,
96
97
  **kwargs,
97
98
  )
99
+ # Special logic to add timeLength to kwargs
100
+ # This is used to validate timeValues across the app
101
+ kwargs["timeLength"] = pamda.path(["settings", "time", "timeLength"], self.data)
98
102
  # Validate panes
99
103
  panes_data = self.data.get("panes")
100
104
  pane_validPaneIds = []
@@ -12,6 +12,7 @@ simultaneously.
12
12
  [predefined]: #appBar_data_star.spec
13
13
  [custom]: #appBar_data_star.spec
14
14
  """
15
+
15
16
  from cave_utils.api_utils.validator_utils import ApiValidator, CustomKeyValidator
16
17
  import type_enforced
17
18
 
@@ -52,9 +53,8 @@ class appBar_data_star(ApiValidator):
52
53
  bar: str,
53
54
  variant: [str, None] = None,
54
55
  color: [str, None] = None,
55
- order: [dict, None] = None,
56
56
  apiCommand: [str, None] = None,
57
- apiCommandKeys: [list, None] = None,
57
+ apiCommandKeys: [list[str], None] = None,
58
58
  **kwargs,
59
59
  ):
60
60
  """
@@ -88,7 +88,7 @@ class appBar_data_star(ApiValidator):
88
88
  * **Note**: It must be a valid RGBA string.
89
89
  * **Example**: `"rgba(255, 255, 255, 1)"`.
90
90
  * **`apiCommand`**: `[str]` = `None` &rarr; The name of the [API command][] to trigger.
91
- * **`apiCommandKeys`**: `[list]` = `None` &rarr;
91
+ * **`apiCommandKeys`**: `[list[str]]` = `None` &rarr;
92
92
  * The root API keys to pass to your `execute_command` function if an
93
93
  `apiCommand` is provided. If omitted, all API keys are
94
94
  passed to `execute_command`.
@@ -2,6 +2,7 @@
2
2
  Optionally, pass special arguments to adjust some options related to
3
3
  how the CAVE API server handles data.
4
4
  """
5
+
5
6
  from cave_utils.api_utils.validator_utils import ApiValidator
6
7
  import type_enforced
7
8
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  These outputs should be general to the entire application and they can be compared across sessions.
4
4
  """
5
+
5
6
  from cave_utils.api_utils.validator_utils import ApiValidator, CustomKeyValidator
6
7
  from cave_utils.api_utils.general import props, values, layout
7
8
  import type_enforced
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Create grouped outputs for building generalized charts and tables.
3
3
  """
4
+
4
5
  from cave_utils.api_utils.validator_utils import ApiValidator, CustomKeyValidator
5
6
  import type_enforced
6
7
  from pamda import pamda
@@ -279,13 +280,13 @@ class groupedOutputs_groupings_star(ApiValidator):
279
280
  levels_data = self.data.get("levels", {})
280
281
  levels_data_keys = list(levels_data.keys())
281
282
  data_data = self.data.get("data", {})
282
-
283
283
  CustomKeyValidator(
284
284
  data=levels_data,
285
285
  log=self.log,
286
286
  prepend_path=["levels"],
287
287
  validator=groupedOutputs_groupings_star_levels_star,
288
288
  acceptable_parents=levels_data_keys,
289
+ acceptable_data_levels=data_data,
289
290
  **kwargs,
290
291
  )
291
292
  groupedOutputs_groupings_star_data(
@@ -348,7 +349,14 @@ class groupedOutputs_groupings_star_levels_star(ApiValidator):
348
349
  """
349
350
 
350
351
  @staticmethod
351
- def spec(name: str, parent: [str, None] = None, ordering: [list, None] = None, **kwargs):
352
+ def spec(
353
+ name: str,
354
+ parent: [str, None] = None,
355
+ ordering: [list, None] = None,
356
+ orderWithParent: bool = True,
357
+ coloring: [dict, None] = None,
358
+ **kwargs,
359
+ ):
352
360
  """
353
361
  Arguments:
354
362
 
@@ -358,16 +366,22 @@ class groupedOutputs_groupings_star_levels_star(ApiValidator):
358
366
  * **Notes**:
359
367
  * The parent level key must be defined in `groupedOutputs.groupings.*.levels.*`
360
368
  * If `None`, this will be considered to be the root of the hierarchy.
369
+ * **`ordering`**: `[list]` &rarr;
370
+ * The ordering of individual values for this level in charts and tables.
371
+ * **Note**: If none, the ordering will be alphabetical.
372
+ * **Note**: If a partial ordering is provided, the provided values will be placed first in order.
373
+ * **Note**: If a partial ordering is provided, the remaining values will be placed in alphabetical order.
374
+ * **Note**: All items in this list must be defined in `groupedOutputs.groupings.*.levels.*.values.*`
375
+ * **`orderWithParent`**: `[bool]`=True &rarr;
376
+ * Weather or not to order this level based on the parent level.
377
+ * If `True`, the ordering of this level will also be based on the parent level.
378
+ * If `False`, the ordering will be based on the ordering of this level only.
379
+ * **`coloring`**: `[dict]` &rarr;
380
+ * A dictionary of colors to be used for the level.
381
+ * Each key in this dictionary is a value in the level.
382
+ * Each value in this dictionary is an rgba string.
383
+ * **See**: `cave_utils.api.groupedOutputs.groupedOutputs_groupings_star_levels_star_coloring`
361
384
  """
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
385
  return {"kwargs": kwargs, "accepted_values": {}}
372
386
 
373
387
  def __extend_spec__(self, **kwargs):
@@ -378,9 +392,24 @@ class groupedOutputs_groupings_star_levels_star(ApiValidator):
378
392
  valid_values=kwargs.get("acceptable_parents", []),
379
393
  prepend_path=["parent"],
380
394
  )
381
- # ordering = self.data.get("ordering")
382
- # if ordering is not None:
383
- # print(ordering, kwargs.get("acceptable_data_keys", []))
384
- # self.__check_subset_valid__(
385
- # subset=ordering, valid_values=kwargs.get("acceptable_data_keys", []), prepend_path=["ordering"]
386
- # )
395
+ ordering = self.data.get("ordering")
396
+ if ordering is not None:
397
+ self.__check_subset_valid__(
398
+ subset=ordering,
399
+ valid_values=kwargs.get("acceptable_data_levels", {}).get(
400
+ kwargs.get("CustomKeyValidatorFieldId"), []
401
+ ),
402
+ prepend_path=["ordering"],
403
+ )
404
+
405
+ coloring = self.data.get("coloring")
406
+ if coloring is not None:
407
+ self.__check_subset_valid__(
408
+ subset=list(coloring.keys()),
409
+ valid_values=kwargs.get("acceptable_data_levels", {}).get(
410
+ kwargs.get("CustomKeyValidatorFieldId"), []
411
+ ),
412
+ prepend_path=["coloring"],
413
+ )
414
+ for key, value in coloring.items():
415
+ self.__check_rgba_string_valid__(rgba_string=value, prepend_path=["coloring", key])
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Create visualizations for your map, including `arc`s, `node`s, and `geo`s, and customize their appearance.
3
3
  """
4
+
4
5
  from cave_utils.api_utils.validator_utils import ApiValidator, CustomKeyValidator
5
6
  from cave_utils.api_utils.general import props, valueLists, layout
6
7
  import type_enforced
@@ -155,8 +156,8 @@ class mapFeatures_data_star_data(ApiValidator):
155
156
  **kwargs,
156
157
  )
157
158
  # Validate that all lengths are the same
158
- lengths = [len(v) for k, v in location_data.items() if k not in ["order", "timeValues"]] + [
159
- len(v) for k, v in valueLists_data.items() if k not in ["order", "timeValues"]
159
+ lengths = [len(v) for k, v in location_data.items() if k not in ["timeValues", "order"]] + [
160
+ len(v) for k, v in valueLists_data.items() if k not in ["timeValues", "order"]
160
161
  ]
161
162
  if len(set(lengths)) > 1:
162
163
  self.__error__(msg=f"location and valueLists keys must have the same length.", path=[])
@@ -185,16 +186,35 @@ class mapFeatures_data_star_data_location(ApiValidator):
185
186
  layer_type = kwargs.get("layer_type")
186
187
  layer_geoJson = kwargs.get("layer_geoJson")
187
188
  passed_keys = list(self.data.keys())
188
- # TODO Figure out way to handle timeValues and order
189
- optional_keys = ["timeValues", "order"]
190
- if layer_type == "geo" or (layer_type == "arc" and layer_geoJson):
189
+ optional_keys = []
190
+ if layer_type == "geo":
191
191
  required_keys = ["geoJsonValue"]
192
192
  elif layer_type == "arc":
193
- if "path" in passed_keys:
193
+ if layer_geoJson is not None:
194
+ required_keys = ["geoJsonValue"]
195
+ optional_keys += [
196
+ "path",
197
+ "startLatitude",
198
+ "startLongitude",
199
+ "endLatitude",
200
+ "endLongitude",
201
+ "startAltitude",
202
+ "endAltitude",
203
+ ]
204
+ elif "path" in passed_keys:
194
205
  required_keys = ["path"]
206
+ optional_keys += [
207
+ "startLatitude",
208
+ "startLongitude",
209
+ "endLatitude",
210
+ "endLongitude",
211
+ "geoJsonValue",
212
+ "startAltitude",
213
+ "endAltitude",
214
+ ]
195
215
  else:
196
216
  required_keys = ["startLatitude", "startLongitude", "endLatitude", "endLongitude"]
197
- optional_keys += ["startAltitude", "endAltitude"]
217
+ optional_keys += ["startAltitude", "endAltitude", "geoJsonValue", "path"]
198
218
  else:
199
219
  required_keys = ["latitude", "longitude"]
200
220
  optional_keys += ["altitude"]
@@ -209,8 +229,12 @@ class mapFeatures_data_star_data_location(ApiValidator):
209
229
  path=[],
210
230
  )
211
231
  continue
212
- if key in ["timeValues", "order"]:
213
- continue
232
+ if key == "geoJsonValue":
233
+ if len(value_list) != len(set(value_list)):
234
+ self.__warn__(
235
+ msg=f"`geoJsonValue` should be a list of unique values. Otherwise, the corresponding map feature may not render correctly.",
236
+ path=[key],
237
+ )
214
238
  if not isinstance(value_list, list):
215
239
  self.__error__(
216
240
  msg=f"`{key}` must be a list but got {type(value_list)} instead.", path=[key]
@@ -240,7 +264,7 @@ class mapFeatures_data_star_data_location(ApiValidator):
240
264
  altitudes = None
241
265
  except:
242
266
  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]]]`",
267
+ 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
268
  path=[key],
245
269
  )
246
270
  continue
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Configure the style and UI elements of your application's maps.
3
3
  """
4
+
4
5
  from cave_utils.api_utils.validator_utils import ApiValidator, CustomKeyValidator
5
6
  import type_enforced
6
7
 
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Configure your application's pages.
3
3
  """
4
+
4
5
  from cave_utils.api_utils.validator_utils import ApiValidator, CustomKeyValidator
5
6
  import type_enforced
6
7
 
@@ -82,6 +83,7 @@ class pages_data_star_pageLayout(ApiValidator):
82
83
  statId: [str, list, None] = None,
83
84
  showToolbar: bool = True,
84
85
  maximized: bool = False,
86
+ defaultToZero: bool = False,
85
87
  **kwargs,
86
88
  ):
87
89
  """
@@ -135,6 +137,7 @@ class pages_data_star_pageLayout(ApiValidator):
135
137
  * **Note**: If left unspecified (i.e., `None`), it will default to `settings.showToolbar`.
136
138
  * **`maximized`**: `[bool]` = `False` &rarr; Whether or not the layout should be maximized.
137
139
  * **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.
140
+ * **`defaultToZero`**: `[bool]` = `False` &rarr; Whether or not the chart should default missing values to zero.
138
141
 
139
142
  [area chart]: https://en.wikipedia.org/wiki/Area_chart
140
143
  [bar chart]: https://en.wikipedia.org/wiki/Bar_chart
@@ -3,6 +3,7 @@ Panes serve as main containers for UI controls such as toggles, text
3
3
  and number fields, sliders, etc. They can also contain buttons that
4
4
  facilitate interaction with actionable data.
5
5
  """
6
+
6
7
  from cave_utils.api_utils.validator_utils import ApiValidator, CustomKeyValidator
7
8
  from cave_utils.api_utils.general import props, values, layout
8
9
  import type_enforced
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Configure general settings for your application like the icons to use, how to sync data with the server, and more.
3
3
  """
4
+
4
5
  from cave_utils.api_utils.validator_utils import *
5
6
  import type_enforced
6
7
 
@@ -291,3 +292,8 @@ class settings_time(ApiValidator):
291
292
  * **Example**: `"Decade"`.
292
293
  """
293
294
  return {"kwargs": kwargs, "accepted_values": {}}
295
+
296
+ def __extend_spec__(self, **kwargs):
297
+ timeLength = self.data.get("timeLength")
298
+ if timeLength < 1:
299
+ self.__error__(f"Time length must be greater than 0.", path=["timeLength"])
@@ -0,0 +1,22 @@
1
+ """
2
+ ## Validation
3
+
4
+ This code can be used directly for validation purposes:
5
+
6
+ ```py
7
+ from cave_utils import Validator
8
+
9
+ session_data = {
10
+ "kwargs": {
11
+ "wipeExisting": True,
12
+ },
13
+ # All of your session data to validate here
14
+ }
15
+
16
+ x = Validator(
17
+ session_data=session_data,
18
+ )
19
+
20
+ print(x.log.log)
21
+ ```
22
+ """
@@ -1,6 +1,7 @@
1
1
  """
2
2
  General API Spec items that are found in multiple places. This is not a key that should be passed as part of your `session_data`.
3
3
  """
4
+
4
5
  from pamda import pamda
5
6
  import type_enforced
6
7
  from cave_utils.api_utils.validator_utils import ApiValidator, CustomKeyValidator
@@ -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` &rarr; 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` &rarr;
103
+ * **`apiCommandKeys`**: `[list[str]]` = `None` &rarr;
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` &rarr;
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]` = `["year", "day", "hours", "minutes"]` &rarr;
130
+ * **`views`**: `[list[str]]` &rarr;
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
- * `"year"`: The year view
133
- * `"month"`: The month view
134
- * `"day"`: The day view
135
- * `"hours"`: The hours view
136
- * `"minutes"`: The minutes view
137
- * `"seconds"`: The seconds view
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`**: `[int]` = `"standard"` &rarr; The formatting style of a numeric value.
190
+ * **`notation`**: `[str]` = `"standard"` &rarr; 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"` &rarr; Further customize the formatting within the selected `notation`.
186
200
  * **Accepted Values**:
187
- * `"short"`: Add symbols `K`, `M`, `B`, and `T` (in `"en-US"`) to denote thousands, millions, billions, and trillions, respectively.
188
- * `"long"`: Present numeric values with the informal suffix words `thousand`, `million`, `billion`, and `trillion` (in `"en-US"`).
189
- * `"e"`: Exponent symbol in lowercase as per the chosen `locale` identifier
190
- * `"e+"`: Similar to `"e"`, but with a plus sign for positive exponents.
191
- * `"E"`: Exponent symbol in uppercase as per the chosen `locale` identifier
192
- * `"E+"`: Similar to `"E"`, but with a plus sign for positive exponents.
193
- * `"x10^"`: Formal scientific notation representation
194
- * `"x10^+"`: Similar to `"x10^"`, with a plus sign for positive exponents.
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": ["year", "month", "day", "hours", "minutes", "seconds"],
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": ["short", "long", "e", "e+", "E", "E+", "x10^", "x10^+"],
346
+ "notationDisplay": notationDisplay_options_dict.get(notation, []),
319
347
  "legendNotation": ["compact", "precision", "scientific", "engineering"],
320
- "legendNotationDisplay": ["short", "long", "e", "e+", "E", "E+", "x10^", "x10^+"],
348
+ "legendNotationDisplay": notationDisplay_options_dict.get(legendNotation, []),
321
349
  "variant": {
322
350
  "head": ["column", "row", "icon", "iconRow"],
323
351
  "text": ["textarea"],
@@ -350,14 +378,15 @@ class props(ApiValidator):
350
378
  )
351
379
 
352
380
 
381
+ @type_enforced.Enforcer
353
382
  class props_options(ApiValidator):
354
383
  @staticmethod
355
- def spec(name: str, path: [list, None] = None, **kwargs):
384
+ def spec(name: str, path: [list[str], None] = None, **kwargs):
356
385
  """
357
386
  Arguments:
358
387
 
359
388
  * **`name`**: `[str]` &rarr; The name of the option.
360
- * **`path`**: `[list]` = `None` &rarr; The path to an option.
389
+ * **`path`**: `[list[str]]` = `None` &rarr; The path to an option.
361
390
  * **Notes**:
362
391
  * If `None`, the option will not be selectable
363
392
  * This attribute is applicable exclusively to `"nested"` props
@@ -379,9 +408,6 @@ class props_options(ApiValidator):
379
408
  msg="`path` must be specified and a list of strings for nested options"
380
409
  )
381
410
  return
382
- self.__check_type_list__(
383
- data=self.data.get("path", []), types=(str,), prepend_path=["path"]
384
- )
385
411
 
386
412
 
387
413
  @type_enforced.Enforcer
@@ -1,6 +1,7 @@
1
1
  """
2
2
  This module contains the primary Validator class that is used to validate your session_data against the API spec.
3
3
  """
4
+
4
5
  from cave_utils.api_utils.validator_utils import LogObject
5
6
  from cave_utils.api import Root
6
7
  import type_enforced
@@ -8,7 +9,7 @@ import type_enforced
8
9
 
9
10
  @type_enforced.Enforcer
10
11
  class Validator:
11
- def __init__(self, session_data, ignore_keys: list = list(), **kwargs):
12
+ def __init__(self, session_data, ignore_keys: list[str] = list(), **kwargs):
12
13
  """
13
14
  Util to validate your session_data against the API spec.
14
15
 
@@ -16,7 +17,7 @@ class Validator:
16
17
 
17
18
  * **`session_data`**: `[dict]` &rarr; The data to validate.
18
19
  * **Note**: This should be the data you are sending to the server.
19
- * **`ignore_keys`**: `[list]` = `None` &rarr; Keys to ignore when validating.
20
+ * **`ignore_keys`**: `[list[str]]` = `None` &rarr; Keys to ignore when validating.
20
21
  * **Note**: Any keys specified here will be not be validated if encountered in the data at any level.
21
22
  """
22
23
  self.session_data = session_data