dcicutils 8.5.0.1b4__tar.gz → 8.6.0.0b0__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/PKG-INFO +1 -1
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/misc_utils.py +7 -16
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/portal_utils.py +8 -23
- dcicutils-8.6.0.0b0/dcicutils/schema_utils.py +142 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/structured_data.py +23 -40
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/pyproject.toml +1 -1
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/LICENSE.txt +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/README.rst +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/__init__.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/base.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/beanstalk_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/bundle_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/cloudformation_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/codebuild_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/command_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/common.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/contribution_scripts.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/contribution_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/creds_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/data_readers.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/data_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/deployment_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/diff_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/docker_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/ecr_scripts.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/ecr_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/ecs_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/env_base.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/env_manager.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/env_scripts.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/env_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/env_utils_legacy.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/es_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/exceptions.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/ff_mocks.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/ff_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/function_cache_decorator.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/glacier_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/jh_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/kibana/dashboards.json +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/kibana/readme.md +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/lang_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/license_policies/c4-infrastructure.jsonc +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/license_policies/c4-python-infrastructure.jsonc +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/license_policies/park-lab-common-server.jsonc +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/license_policies/park-lab-common.jsonc +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/license_policies/park-lab-gpl-pipeline.jsonc +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/license_policies/park-lab-pipeline.jsonc +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/license_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/log_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/obfuscation_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/opensearch_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/project_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/qa_checkers.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/qa_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/redis_tools.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/redis_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/s3_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/scripts/publish_to_pypi.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/scripts/run_license_checker.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/secrets_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/sheet_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/snapshot_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/ssl_certificate_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/task_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/trace_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/validation_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/variant_utils.py +0 -0
- {dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/zip_utils.py +0 -0
@@ -982,11 +982,7 @@ def to_integer(value: str, fallback: Optional[Any] = None) -> Optional[Any]:
|
|
982
982
|
try:
|
983
983
|
return int(value)
|
984
984
|
except Exception:
|
985
|
-
|
986
|
-
return int(float(value))
|
987
|
-
except Exception:
|
988
|
-
pass
|
989
|
-
return fallback
|
985
|
+
return fallback
|
990
986
|
|
991
987
|
|
992
988
|
def to_float(value: str, fallback: Optional[Any] = None) -> Optional[Any]:
|
@@ -1469,33 +1465,28 @@ def string_list(s):
|
|
1469
1465
|
return [p for p in [part.strip() for part in s.split(",")] if p]
|
1470
1466
|
|
1471
1467
|
|
1472
|
-
def split_string(value: str, delimiter: str, escape: Optional[str] = None
|
1468
|
+
def split_string(value: str, delimiter: str, escape: Optional[str] = None) -> List[str]:
|
1473
1469
|
"""
|
1474
1470
|
Splits the given string into an array of string based on the given delimiter, and an optional escape character.
|
1475
1471
|
"""
|
1476
1472
|
if not isinstance(value, str) or not (value := value.strip()):
|
1477
1473
|
return []
|
1478
|
-
result = []
|
1479
1474
|
if not isinstance(escape, str) or not escape:
|
1480
|
-
for item in value.split(delimiter)
|
1481
|
-
|
1482
|
-
result.append(item)
|
1483
|
-
return result
|
1475
|
+
return [item.strip() for item in value.split(delimiter)]
|
1476
|
+
result = []
|
1484
1477
|
item = r""
|
1485
1478
|
escaped = False
|
1486
1479
|
for c in value:
|
1487
1480
|
if c == delimiter and not escaped:
|
1488
|
-
|
1489
|
-
result.append(item)
|
1481
|
+
result.append(item.strip())
|
1490
1482
|
item = r""
|
1491
1483
|
elif c == escape and not escaped:
|
1492
1484
|
escaped = True
|
1493
1485
|
else:
|
1494
1486
|
item += c
|
1495
1487
|
escaped = False
|
1496
|
-
|
1497
|
-
|
1498
|
-
return result
|
1488
|
+
result.append(item.strip())
|
1489
|
+
return [item for item in result if item]
|
1499
1490
|
|
1500
1491
|
|
1501
1492
|
def right_trim(list_or_tuple: Union[List[Any], Tuple[Any]],
|
@@ -23,14 +23,15 @@ class Portal:
|
|
23
23
|
2. From a key dictionary, containing "key" and "secret" property values.
|
24
24
|
3. From a key tuple, containing (in order) a key and secret values.
|
25
25
|
4. From a keys file assumed to reside in ~/.{app}-keys.json where the given "app" value is either "smaht", "cgap",
|
26
|
-
or "fourfront"; where is assumed to contain a dictionary with a key
|
27
|
-
and with a dictionary value containing "key" and "secret" property values
|
28
|
-
|
29
|
-
will be used, i.e. e.g. if
|
26
|
+
or "fourfront"; and where this file is assumed to contain a dictionary with a key equal to the given "env"
|
27
|
+
value (e.g. smaht-localhost) and with a dictionary value containing "key" and "secret" property values; if
|
28
|
+
an "app" value is not specified but the given "env" value begins with one of the app values then that value
|
29
|
+
will be used, i.e. e.g. if env is "smaht-localhost" and app is unspecified than it is assumed to be "smaht".
|
30
30
|
5. From a keys file as described above (#4) but rather than be identified by the given "env" value it
|
31
|
-
is looked up
|
31
|
+
is looked up by the given "server" name and the "server" key dictionary value in the key file.
|
32
32
|
6. From a given "vapp" value (which is assumed to be a TestApp or VirtualApp).
|
33
|
-
7. From another Portal object
|
33
|
+
7. From another Portal object.
|
34
|
+
8. From a a pyramid Router object.
|
34
35
|
"""
|
35
36
|
def __init__(self,
|
36
37
|
arg: Optional[Union[VirtualApp, TestApp, Router, Portal, dict, tuple, str]] = None,
|
@@ -61,7 +62,6 @@ class Portal:
|
|
61
62
|
self._server = portal._server
|
62
63
|
self._key = portal._key
|
63
64
|
self._key_pair = portal._key_pair
|
64
|
-
self._key_id = portal._key_id
|
65
65
|
self._key_file = portal._key_file
|
66
66
|
return
|
67
67
|
self._vapp = None
|
@@ -70,7 +70,6 @@ class Portal:
|
|
70
70
|
self._server = server
|
71
71
|
self._key = None
|
72
72
|
self._key_pair = None
|
73
|
-
self._key_id = None
|
74
73
|
self._key_file = None
|
75
74
|
if isinstance(portal, (VirtualApp, TestApp)):
|
76
75
|
self._vapp = portal
|
@@ -96,10 +95,6 @@ class Portal:
|
|
96
95
|
self._key = key_manager.get_keydict_for_server(self._server)
|
97
96
|
self._key_pair = key_manager.keydict_to_keypair(self._key) if self._key else None
|
98
97
|
self._key_file = key_manager.keys_file
|
99
|
-
if self._key and (key_id := self._key.get("key")):
|
100
|
-
self._key_id = key_id
|
101
|
-
elif self._key_pair and (key_id := self._key_pair[1]):
|
102
|
-
self._key_id = key_id
|
103
98
|
|
104
99
|
@property
|
105
100
|
def env(self):
|
@@ -121,10 +116,6 @@ class Portal:
|
|
121
116
|
def key_pair(self):
|
122
117
|
return self._key_pair
|
123
118
|
|
124
|
-
@property
|
125
|
-
def key_id(self):
|
126
|
-
return self._key_id
|
127
|
-
|
128
119
|
@property
|
129
120
|
def key_file(self):
|
130
121
|
return self._key_file
|
@@ -215,19 +206,13 @@ class Portal:
|
|
215
206
|
super_type_map_flattened[super_type_name] = breadth_first(super_type_map, super_type_name)
|
216
207
|
return super_type_map_flattened
|
217
208
|
|
218
|
-
def ping(self) -> bool:
|
219
|
-
try:
|
220
|
-
return self.get("/health").status_code == 200
|
221
|
-
except Exception:
|
222
|
-
return False
|
223
|
-
|
224
209
|
def _uri(self, uri: str) -> str:
|
225
210
|
if not isinstance(uri, str) or not uri:
|
226
211
|
return "/"
|
227
212
|
if uri.lower().startswith("http://") or uri.lower().startswith("https://"):
|
228
213
|
return uri
|
229
214
|
uri = re.sub(r"/+", "/", uri)
|
230
|
-
return (self._server + ("/" if
|
215
|
+
return (self._server + ("/" if uri.startswith("/") else "") + uri) if self._server else uri
|
231
216
|
|
232
217
|
def _kwargs(self, **kwargs) -> dict:
|
233
218
|
result_kwargs = {"headers":
|
@@ -0,0 +1,142 @@
|
|
1
|
+
from typing import Any, Dict, List
|
2
|
+
|
3
|
+
|
4
|
+
class JsonSchemaConstants:
|
5
|
+
ANY_OF = "anyOf"
|
6
|
+
ARRAY = "array"
|
7
|
+
BOOLEAN = "boolean"
|
8
|
+
ENUM = "enum"
|
9
|
+
INTEGER = "integer"
|
10
|
+
ITEMS = "items"
|
11
|
+
NUMBER = "number"
|
12
|
+
OBJECT = "object"
|
13
|
+
ONE_OF = "oneOf"
|
14
|
+
PATTERN = "pattern"
|
15
|
+
PROPERTIES = "properties"
|
16
|
+
REQUIRED = "required"
|
17
|
+
STRING = "string"
|
18
|
+
TYPE = "type"
|
19
|
+
|
20
|
+
|
21
|
+
class EncodedSchemaConstants:
|
22
|
+
DEFAULT = "default"
|
23
|
+
FORMAT = "format"
|
24
|
+
IDENTIFYING_PROPERTIES = "identifyingProperties"
|
25
|
+
LINK_TO = "linkTo"
|
26
|
+
MERGE_REF = "$merge"
|
27
|
+
MIXIN_PROPERTIES = "mixinProperties"
|
28
|
+
REF = "$ref"
|
29
|
+
UNIQUE_KEY = "uniqueKey"
|
30
|
+
|
31
|
+
|
32
|
+
class SchemaConstants(JsonSchemaConstants, EncodedSchemaConstants):
|
33
|
+
pass
|
34
|
+
|
35
|
+
|
36
|
+
def get_properties(schema: Dict[str, Any]) -> Dict[str, Any]:
|
37
|
+
"""Return the properties of a schema."""
|
38
|
+
return schema.get(SchemaConstants.PROPERTIES, {})
|
39
|
+
|
40
|
+
|
41
|
+
def get_required(schema: Dict[str, Any]) -> List[str]:
|
42
|
+
"""Return the required properties of a schema."""
|
43
|
+
return schema.get(SchemaConstants.REQUIRED, [])
|
44
|
+
|
45
|
+
|
46
|
+
def get_any_of(schema: Dict[str, Any]) -> List[Dict[str, Any]]:
|
47
|
+
"""Return the anyOf properties of a schema."""
|
48
|
+
return schema.get(SchemaConstants.ANY_OF, [])
|
49
|
+
|
50
|
+
|
51
|
+
def get_one_of(schema: Dict[str, Any]) -> List[Dict[str, Any]]:
|
52
|
+
"""Return the oneOf properties of a schema."""
|
53
|
+
return schema.get(SchemaConstants.ONE_OF, [])
|
54
|
+
|
55
|
+
|
56
|
+
def get_conditionally_required_properties(schema: Dict[str, Any]) -> List[str]:
|
57
|
+
"""Get required + possibly required properties.
|
58
|
+
|
59
|
+
Using heuristics here; update as needed.
|
60
|
+
"""
|
61
|
+
return sorted(
|
62
|
+
list(
|
63
|
+
set(
|
64
|
+
get_required(schema)
|
65
|
+
+ get_any_of_required_properties(schema)
|
66
|
+
+ get_one_of_required_properties(schema)
|
67
|
+
)
|
68
|
+
)
|
69
|
+
)
|
70
|
+
|
71
|
+
|
72
|
+
def get_any_of_required_properties(schema: Dict[str, Any]) -> List[str]:
|
73
|
+
"""Get required properties from anyOf."""
|
74
|
+
return [
|
75
|
+
property_name
|
76
|
+
for any_of_schema in get_any_of(schema)
|
77
|
+
for property_name in get_required(any_of_schema)
|
78
|
+
]
|
79
|
+
|
80
|
+
|
81
|
+
def get_one_of_required_properties(schema: Dict[str, Any]) -> List[str]:
|
82
|
+
"""Get required properties from oneOf."""
|
83
|
+
return [
|
84
|
+
property_name
|
85
|
+
for one_of_schema in get_one_of(schema)
|
86
|
+
for property_name in get_required(one_of_schema)
|
87
|
+
]
|
88
|
+
|
89
|
+
|
90
|
+
def get_mixin_properties(schema: Dict[str, Any]) -> List[Dict[str, Any]]:
|
91
|
+
"""Return the mixin properties of a schema."""
|
92
|
+
return schema.get(EncodedSchemaConstants.MIXIN_PROPERTIES, [])
|
93
|
+
|
94
|
+
|
95
|
+
def get_identifying_properties(schema: Dict[str, Any]) -> List[str]:
|
96
|
+
"""Return the identifying properties of a schema."""
|
97
|
+
return schema.get(EncodedSchemaConstants.IDENTIFYING_PROPERTIES, [])
|
98
|
+
|
99
|
+
|
100
|
+
def get_schema_type(schema: Dict[str, Any]) -> str:
|
101
|
+
"""Return the type of a schema."""
|
102
|
+
return schema.get(SchemaConstants.TYPE, "")
|
103
|
+
|
104
|
+
|
105
|
+
def is_array_schema(schema: Dict[str, Any]) -> bool:
|
106
|
+
"""Return True if the schema is an array."""
|
107
|
+
return get_schema_type(schema) == SchemaConstants.ARRAY
|
108
|
+
|
109
|
+
|
110
|
+
def is_object_schema(schema: Dict[str, Any]) -> bool:
|
111
|
+
"""Return True if the schema is an object."""
|
112
|
+
return get_schema_type(schema) == SchemaConstants.OBJECT
|
113
|
+
|
114
|
+
|
115
|
+
def is_string_schema(schema: Dict[str, Any]) -> bool:
|
116
|
+
"""Return True if the schema is a string."""
|
117
|
+
return get_schema_type(schema) == SchemaConstants.STRING
|
118
|
+
|
119
|
+
|
120
|
+
def is_number_schema(schema: Dict[str, Any]) -> bool:
|
121
|
+
"""Return True if the schema is a number."""
|
122
|
+
return get_schema_type(schema) == SchemaConstants.NUMBER
|
123
|
+
|
124
|
+
|
125
|
+
def is_integer_schema(schema: Dict[str, Any]) -> bool:
|
126
|
+
"""Return True if the schema is an integer."""
|
127
|
+
return get_schema_type(schema) == SchemaConstants.INTEGER
|
128
|
+
|
129
|
+
|
130
|
+
def is_boolean_schema(schema: Dict[str, Any]) -> bool:
|
131
|
+
"""Return True if the schema is a boolean."""
|
132
|
+
return get_schema_type(schema) == SchemaConstants.BOOLEAN
|
133
|
+
|
134
|
+
|
135
|
+
def get_items(schema: Dict[str, Any]) -> Dict[str, Any]:
|
136
|
+
"""Return the items of a schema."""
|
137
|
+
return schema.get(SchemaConstants.ITEMS, {})
|
138
|
+
|
139
|
+
|
140
|
+
def has_property(schema: Dict[str, Any], property_name: str) -> bool:
|
141
|
+
"""Return True if the schema has the given property."""
|
142
|
+
return property_name in get_properties(schema)
|
@@ -42,24 +42,23 @@ StructuredDataSet = Type["StructuredDataSet"]
|
|
42
42
|
class StructuredDataSet:
|
43
43
|
|
44
44
|
def __init__(self, file: Optional[str] = None, portal: Optional[Union[VirtualApp, TestApp, Portal]] = None,
|
45
|
-
schemas: Optional[List[dict]] = None,
|
46
|
-
|
45
|
+
schemas: Optional[List[dict]] = None, data: Optional[List[dict]] = None,
|
46
|
+
order: Optional[List[str]] = None, prune: bool = True) -> None:
|
47
47
|
self.data = {} if not data else data # If portal is None then no schemas nor refs.
|
48
48
|
self._portal = Portal(portal, data=self.data, schemas=schemas) if portal else None
|
49
49
|
self._order = order
|
50
50
|
self._prune = prune
|
51
51
|
self._warnings = {}
|
52
52
|
self._errors = {}
|
53
|
-
self._resolved_refs =
|
53
|
+
self._resolved_refs = []
|
54
54
|
self._validated = False
|
55
|
-
self._autoadd_properties = autoadd if isinstance(autoadd, dict) and autoadd else None
|
56
55
|
self._load_file(file) if file else None
|
57
56
|
|
58
57
|
@staticmethod
|
59
58
|
def load(file: str, portal: Optional[Union[VirtualApp, TestApp, Portal]] = None,
|
60
|
-
schemas: Optional[List[dict]] = None,
|
59
|
+
schemas: Optional[List[dict]] = None,
|
61
60
|
order: Optional[List[str]] = None, prune: bool = True) -> StructuredDataSet:
|
62
|
-
return StructuredDataSet(file=file, portal=portal, schemas=schemas,
|
61
|
+
return StructuredDataSet(file=file, portal=portal, schemas=schemas, order=order, prune=prune)
|
63
62
|
|
64
63
|
def validate(self, force: bool = False) -> None:
|
65
64
|
if self._validated and not force:
|
@@ -97,7 +96,7 @@ class StructuredDataSet:
|
|
97
96
|
|
98
97
|
@property
|
99
98
|
def resolved_refs(self) -> List[str]:
|
100
|
-
return
|
99
|
+
return self._resolved_refs
|
101
100
|
|
102
101
|
@property
|
103
102
|
def upload_files(self) -> List[str]:
|
@@ -164,13 +163,11 @@ class StructuredDataSet:
|
|
164
163
|
structured_row = structured_row_template.create_row()
|
165
164
|
for column_name, value in row.items():
|
166
165
|
structured_row_template.set_value(structured_row, column_name, value, reader.file, reader.row_number)
|
167
|
-
if self._autoadd_properties:
|
168
|
-
self._add_properties(structured_row, self._autoadd_properties, schema)
|
169
166
|
self._add(type_name, structured_row)
|
170
167
|
self._note_warning(reader.warnings, "reader")
|
171
168
|
if schema:
|
172
169
|
self._note_error(schema._unresolved_refs, "ref")
|
173
|
-
self._resolved_refs
|
170
|
+
self._resolved_refs = schema._resolved_refs
|
174
171
|
|
175
172
|
def _add(self, type_name: str, data: Union[dict, List[dict]]) -> None:
|
176
173
|
if self._prune:
|
@@ -180,11 +177,6 @@ class StructuredDataSet:
|
|
180
177
|
else:
|
181
178
|
self.data[type_name] = [data] if isinstance(data, dict) else data
|
182
179
|
|
183
|
-
def _add_properties(self, structured_row: dict, properties: dict, schema: Optional[dict] = None) -> None:
|
184
|
-
for name in properties:
|
185
|
-
if name not in structured_row and (not schema or schema.data.get("properties", {}).get(name)):
|
186
|
-
structured_row[name] = properties[name]
|
187
|
-
|
188
180
|
def _note_warning(self, item: Optional[Union[dict, List[dict]]], group: str) -> None:
|
189
181
|
self._note_issue(self._warnings, item, group)
|
190
182
|
|
@@ -245,7 +237,7 @@ class _StructuredRowTemplate:
|
|
245
237
|
return {array_name: array} if array_name else {column_component: value}
|
246
238
|
|
247
239
|
def set_value_internal(data: Union[dict, list], value: Optional[Any], src: Optional[str],
|
248
|
-
path: List[Union[str, int]],
|
240
|
+
path: List[Union[str, int]], mapv: Optional[Callable]) -> None:
|
249
241
|
|
250
242
|
def set_value_backtrack_object(path_index: int, path_element: str) -> None:
|
251
243
|
nonlocal data, path, original_data
|
@@ -265,7 +257,7 @@ class _StructuredRowTemplate:
|
|
265
257
|
set_value_backtrack_object(i, p)
|
266
258
|
data = data[p]
|
267
259
|
if (p := path[-1]) == -1 and isinstance(value, str):
|
268
|
-
values = _split_array_string(value
|
260
|
+
values = _split_array_string(value)
|
269
261
|
if mapv:
|
270
262
|
values = [mapv(value, src) for value in values]
|
271
263
|
merge_objects(data, values)
|
@@ -296,13 +288,11 @@ class _StructuredRowTemplate:
|
|
296
288
|
for column_name in column_names or []:
|
297
289
|
ensure_column_consistency(column_name)
|
298
290
|
rational_column_name = self._schema.rationalize_column_name(column_name) if self._schema else column_name
|
299
|
-
|
300
|
-
map_value_function = column_typeinfo.get("map") if column_typeinfo else None
|
291
|
+
map_value_function = self._schema.get_map_value_function(rational_column_name) if self._schema else None
|
301
292
|
if (column_components := _split_dotted_string(rational_column_name)):
|
302
293
|
merge_objects(structured_row_template, parse_components(column_components, path := []), True)
|
303
|
-
self._set_value_functions[column_name] = (
|
304
|
-
|
305
|
-
set_value_internal(data, value, src, path, typeinfo, mapv))
|
294
|
+
self._set_value_functions[column_name] = (lambda data, value, src, path=path, mapv=map_value_function:
|
295
|
+
set_value_internal(data, value, src, path, mapv))
|
306
296
|
return structured_row_template
|
307
297
|
|
308
298
|
|
@@ -325,8 +315,7 @@ class Schema:
|
|
325
315
|
|
326
316
|
@staticmethod
|
327
317
|
def load_by_name(name: str, portal: Portal) -> Optional[dict]:
|
328
|
-
|
329
|
-
return Schema(schema_json, portal) if schema_json else None
|
318
|
+
return Schema(portal.get_schema(Schema.type_name(name)), portal) if portal else None
|
330
319
|
|
331
320
|
def validate(self, data: dict) -> List[str]:
|
332
321
|
errors = []
|
@@ -342,7 +331,10 @@ class Schema:
|
|
342
331
|
def resolved_refs(self) -> List[str]:
|
343
332
|
return list(self._resolved_refs)
|
344
333
|
|
345
|
-
def
|
334
|
+
def get_map_value_function(self, column_name: str) -> Optional[Any]:
|
335
|
+
return (self._get_typeinfo(column_name) or {}).get("map")
|
336
|
+
|
337
|
+
def _get_typeinfo(self, column_name: str) -> Optional[dict]:
|
346
338
|
if isinstance(info := self._typeinfo.get(column_name), str):
|
347
339
|
info = self._typeinfo.get(info)
|
348
340
|
if not info and isinstance(info := self._typeinfo.get(self.unadorn_column_name(column_name)), str):
|
@@ -475,15 +467,9 @@ class Schema:
|
|
475
467
|
raise Exception(f"Array of undefined or multiple types in JSON schema NOT supported: {key}")
|
476
468
|
raise Exception(f"Invalid array type specifier in JSON schema: {key}")
|
477
469
|
key = key + ARRAY_NAME_SUFFIX_CHAR
|
478
|
-
if unique := (property_value.get("uniqueItems") is True):
|
479
|
-
pass
|
480
470
|
property_value = array_property_items
|
481
471
|
property_value_type = property_value.get("type")
|
482
|
-
|
483
|
-
if unique:
|
484
|
-
typeinfo[key]["unique"] = True
|
485
|
-
result.update(typeinfo)
|
486
|
-
# result.update(self._create_typeinfo(array_property_items, parent_key=key))
|
472
|
+
result.update(self._create_typeinfo(array_property_items, parent_key=key))
|
487
473
|
continue
|
488
474
|
result[key] = {"type": property_value_type, "map": self._map_function({**property_value, "column": key})}
|
489
475
|
if ARRAY_NAME_SUFFIX_CHAR in key:
|
@@ -574,9 +560,7 @@ class Portal(PortalBase):
|
|
574
560
|
|
575
561
|
@lru_cache(maxsize=256)
|
576
562
|
def get_schema(self, schema_name: str) -> Optional[dict]:
|
577
|
-
if
|
578
|
-
return None
|
579
|
-
if schema := schemas.get(schema_name := Schema.type_name(schema_name)):
|
563
|
+
if (schemas := self.get_schemas()) and (schema := schemas.get(schema_name := Schema.type_name(schema_name))):
|
580
564
|
return schema
|
581
565
|
if schema_name == schema_name.upper() and (schema := schemas.get(schema_name.lower().title())):
|
582
566
|
return schema
|
@@ -584,9 +568,8 @@ class Portal(PortalBase):
|
|
584
568
|
return schema
|
585
569
|
|
586
570
|
@lru_cache(maxsize=1)
|
587
|
-
def get_schemas(self) ->
|
588
|
-
|
589
|
-
return None
|
571
|
+
def get_schemas(self) -> dict:
|
572
|
+
schemas = super().get_schemas()
|
590
573
|
if self._schemas:
|
591
574
|
schemas = copy.deepcopy(schemas)
|
592
575
|
for user_specified_schema in self._schemas:
|
@@ -632,5 +615,5 @@ def _split_dotted_string(value: str):
|
|
632
615
|
return split_string(value, DOTTED_NAME_DELIMITER_CHAR)
|
633
616
|
|
634
617
|
|
635
|
-
def _split_array_string(value: str
|
636
|
-
return split_string(value, ARRAY_VALUE_DELIMITER_CHAR, ARRAY_VALUE_DELIMITER_ESCAPE_CHAR
|
618
|
+
def _split_array_string(value: str):
|
619
|
+
return split_string(value, ARRAY_VALUE_DELIMITER_CHAR, ARRAY_VALUE_DELIMITER_ESCAPE_CHAR)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "dcicutils"
|
3
|
-
version = "8.
|
3
|
+
version = "8.6.0.0b0"
|
4
4
|
description = "Utility package for interacting with the 4DN Data Portal and other 4DN resources"
|
5
5
|
authors = ["4DN-DCIC Team <support@4dnucleome.org>"]
|
6
6
|
license = "MIT"
|
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
|
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
|
{dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/license_policies/c4-infrastructure.jsonc
RENAMED
File without changes
|
File without changes
|
{dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/license_policies/park-lab-common-server.jsonc
RENAMED
File without changes
|
{dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/license_policies/park-lab-common.jsonc
RENAMED
File without changes
|
{dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/license_policies/park-lab-gpl-pipeline.jsonc
RENAMED
File without changes
|
{dcicutils-8.5.0.1b4 → dcicutils-8.6.0.0b0}/dcicutils/license_policies/park-lab-pipeline.jsonc
RENAMED
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
|