dcicutils 8.7.1.1b6__py3-none-any.whl → 8.7.1.1b9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dcicutils/portal_object_utils.py +7 -10
- dcicutils/portal_utils.py +16 -11
- dcicutils/structured_data.py +28 -1
- {dcicutils-8.7.1.1b6.dist-info → dcicutils-8.7.1.1b9.dist-info}/METADATA +1 -1
- {dcicutils-8.7.1.1b6.dist-info → dcicutils-8.7.1.1b9.dist-info}/RECORD +8 -8
- {dcicutils-8.7.1.1b6.dist-info → dcicutils-8.7.1.1b9.dist-info}/LICENSE.txt +0 -0
- {dcicutils-8.7.1.1b6.dist-info → dcicutils-8.7.1.1b9.dist-info}/WHEEL +0 -0
- {dcicutils-8.7.1.1b6.dist-info → dcicutils-8.7.1.1b9.dist-info}/entry_points.txt +0 -0
dcicutils/portal_object_utils.py
CHANGED
@@ -137,23 +137,20 @@ class PortalObject:
|
|
137
137
|
comparing_data = value
|
138
138
|
else:
|
139
139
|
return {}
|
140
|
-
return PortalObject._compare(this_data, comparing_data
|
140
|
+
return PortalObject._compare(this_data, comparing_data)
|
141
141
|
|
142
142
|
_ARRAY_KEY_REGULAR_EXPRESSION = re.compile(rf"^({Schema._ARRAY_NAME_SUFFIX_CHAR}\d+)$")
|
143
143
|
|
144
144
|
@staticmethod
|
145
|
-
def _compare(a: Any, b: Any,
|
145
|
+
def _compare(a: Any, b: Any, _path: Optional[str] = None) -> dict:
|
146
146
|
def diff_creating(value: Any) -> object: # noqa
|
147
|
-
|
148
|
-
return create_readonly_object(value=value, type=value_type,
|
147
|
+
return create_readonly_object(value=value,
|
149
148
|
creating_value=True, updating_value=None, deleting_value=False)
|
150
149
|
def diff_updating(value: Any, updating_value: Any) -> object: # noqa
|
151
|
-
|
152
|
-
return create_readonly_object(value=value, type=value_type,
|
150
|
+
return create_readonly_object(value=value,
|
153
151
|
creating_value=False, updating_value=updating_value, deleting_value=False)
|
154
152
|
def diff_deleting(value: Any) -> object: # noqa
|
155
|
-
|
156
|
-
return create_readonly_object(value=value, type=value_type,
|
153
|
+
return create_readonly_object(value=value,
|
157
154
|
creating_value=False, updating_value=None, deleting_value=True)
|
158
155
|
diffs = {}
|
159
156
|
if isinstance(a, dict) and isinstance(b, dict):
|
@@ -163,7 +160,7 @@ class PortalObject:
|
|
163
160
|
if a[key] != PortalObject._PROPERTY_DELETION_SENTINEL:
|
164
161
|
diffs[path] = diff_creating(a[key])
|
165
162
|
else:
|
166
|
-
diffs.update(PortalObject._compare(a[key], b[key],
|
163
|
+
diffs.update(PortalObject._compare(a[key], b[key], _path=path))
|
167
164
|
elif isinstance(a, list) and isinstance(b, list):
|
168
165
|
# Ignore order of array elements; not absolutely technically correct but suits our purpose.
|
169
166
|
for index in range(len(a)):
|
@@ -179,7 +176,7 @@ class PortalObject:
|
|
179
176
|
if index < len(b):
|
180
177
|
diffs[path] = diff_deleting(b[index])
|
181
178
|
elif len(b) < index:
|
182
|
-
diffs.update(PortalObject._compare(a[index], b[index],
|
179
|
+
diffs.update(PortalObject._compare(a[index], b[index], _path=path))
|
183
180
|
else:
|
184
181
|
diffs[path] = diff_creating(a[index])
|
185
182
|
elif a != b:
|
dcicutils/portal_utils.py
CHANGED
@@ -15,7 +15,7 @@ from typing import Callable, Dict, List, Optional, Tuple, Type, Union
|
|
15
15
|
from uuid import uuid4 as uuid
|
16
16
|
from webtest.app import TestApp, TestResponse
|
17
17
|
from wsgiref.simple_server import make_server as wsgi_make_server
|
18
|
-
from dcicutils.common import OrchestratedApp, ORCHESTRATED_APPS
|
18
|
+
from dcicutils.common import APP_SMAHT, OrchestratedApp, ORCHESTRATED_APPS
|
19
19
|
from dcicutils.ff_utils import get_metadata, get_schema, patch_metadata, post_metadata
|
20
20
|
from dcicutils.misc_utils import to_camel_case, VirtualApp
|
21
21
|
from dcicutils.tmpfile_utils import temporary_file
|
@@ -42,6 +42,7 @@ class Portal:
|
|
42
42
|
or a dcicutils.misc_utils.VirtualApp, or even a pyramid.router.Router.
|
43
43
|
8. From another Portal object (i.e. copy constructor).
|
44
44
|
"""
|
45
|
+
DEFAULT_APP = APP_SMAHT
|
45
46
|
KEYS_FILE_DIRECTORY = "~"
|
46
47
|
MIME_TYPE_JSON = "application/json"
|
47
48
|
|
@@ -60,7 +61,7 @@ class Portal:
|
|
60
61
|
self._vapp = None
|
61
62
|
for arg in unspecified:
|
62
63
|
if arg is not None:
|
63
|
-
raise Exception("Portal
|
64
|
+
raise Exception("Portal initialization error; extraneous arguments.")
|
64
65
|
|
65
66
|
def init_from_portal(portal: Portal, unspecified: Optional[list] = None) -> None:
|
66
67
|
init(unspecified=unspecified)
|
@@ -93,13 +94,13 @@ class Portal:
|
|
93
94
|
raise Exception(f"Portal server inconsistency: {server} vs {key_server}")
|
94
95
|
self._key["server"] = self._server = server
|
95
96
|
if not self._key:
|
96
|
-
raise Exception("Portal
|
97
|
+
raise Exception("Portal initialization error; from key.")
|
97
98
|
|
98
99
|
def init_from_key_pair(key_pair: tuple, server: Optional[str], unspecified: Optional[list] = []) -> None:
|
99
100
|
if len(key_pair) >= 2:
|
100
101
|
init_from_key({"key": key_pair[0], "secret": key_pair[1]}, server, unspecified=unspecified)
|
101
102
|
else:
|
102
|
-
raise Exception("Portal
|
103
|
+
raise Exception("Portal initialization error; from key-pair.")
|
103
104
|
|
104
105
|
def init_from_keys_file(keys_file: str, env: Optional[str], server: Optional[str],
|
105
106
|
unspecified: Optional[list] = []) -> None:
|
@@ -107,7 +108,7 @@ class Portal:
|
|
107
108
|
with io.open(keys_file := os.path.expanduser(keys_file)) as f:
|
108
109
|
keys = json.load(f)
|
109
110
|
except Exception:
|
110
|
-
raise Exception(f"Portal
|
111
|
+
raise Exception(f"Portal initialization error; cannot open keys-file: {keys_file}")
|
111
112
|
if isinstance(env, str) and env and isinstance(key := keys.get(env), dict):
|
112
113
|
init_from_key(key, server)
|
113
114
|
self._keys_file = keys_file
|
@@ -121,7 +122,8 @@ class Portal:
|
|
121
122
|
self._keys_file = keys_file
|
122
123
|
self._env = env
|
123
124
|
else:
|
124
|
-
raise Exception(f"Portal
|
125
|
+
raise Exception(f"Portal initialization error;"
|
126
|
+
f" {env or server or None} not found in keys-file: {keys_file}")
|
125
127
|
|
126
128
|
def init_from_env_server_app(env: str, server: str, app: Optional[str],
|
127
129
|
unspecified: Optional[list] = None) -> None:
|
@@ -146,7 +148,7 @@ class Portal:
|
|
146
148
|
return prefix + server if server else None
|
147
149
|
|
148
150
|
if (valid_app := app) and not (valid_app := Portal._valid_app(app)):
|
149
|
-
raise Exception(f"Portal
|
151
|
+
raise Exception(f"Portal initialization error; invalid app: {app}")
|
150
152
|
self._app = valid_app
|
151
153
|
if isinstance(arg, Portal):
|
152
154
|
init_from_portal(arg, unspecified=[env, server, app])
|
@@ -164,12 +166,15 @@ class Portal:
|
|
164
166
|
init_from_env_server_app(arg, server, app, unspecified=[env])
|
165
167
|
elif (isinstance(env, str) and env) or (isinstance(server, str) and server):
|
166
168
|
init_from_env_server_app(env, server, app, unspecified=[arg])
|
169
|
+
elif not arg and (keys_file := self._default_keys_file(app=self._app or Portal.DEFAULT_APP, env=env)):
|
170
|
+
# If no initial arg then look for default app keys file.
|
171
|
+
init_from_keys_file(keys_file, env, server)
|
167
172
|
elif raise_exception:
|
168
|
-
raise Exception("Portal
|
173
|
+
raise Exception("Portal initialization error; insufficient arguments.")
|
169
174
|
else:
|
170
175
|
init()
|
171
176
|
if not self.vapp and not self.key and raise_exception:
|
172
|
-
raise Exception("Portal
|
177
|
+
raise Exception("Portal initialization error; neither key nor vapp defined.")
|
173
178
|
|
174
179
|
@property
|
175
180
|
def ini_file(self) -> Optional[str]:
|
@@ -443,7 +448,7 @@ class Portal:
|
|
443
448
|
if isinstance(arg, list) or isinstance(arg, dict) or isinstance(arg, Callable):
|
444
449
|
return Portal(Portal._create_router_for_testing(arg))
|
445
450
|
elif isinstance(arg, str) and arg.endswith(".ini"):
|
446
|
-
return Portal(
|
451
|
+
return Portal(arg)
|
447
452
|
elif arg == "local" or arg is True:
|
448
453
|
minimal_ini_for_testing = "\n".join([
|
449
454
|
"[app:app]\nuse = egg:encoded\nfile_upload_bucket = dummy",
|
@@ -467,7 +472,7 @@ class Portal:
|
|
467
472
|
else:
|
468
473
|
minimal_ini_for_testing = "[app:app]\nuse = egg:encoded\nsqlalchemy.url = postgresql://dummy\n"
|
469
474
|
with temporary_file(content=minimal_ini_for_testing, suffix=".ini") as ini_file:
|
470
|
-
return Portal(
|
475
|
+
return Portal(ini_file)
|
471
476
|
|
472
477
|
@staticmethod
|
473
478
|
def _create_vapp(arg: Union[TestApp, VirtualApp, PyramidRouter, str] = None) -> TestApp:
|
dcicutils/structured_data.py
CHANGED
@@ -11,8 +11,10 @@ from webtest.app import TestApp
|
|
11
11
|
from dcicutils.common import OrchestratedApp
|
12
12
|
from dcicutils.data_readers import CsvReader, Excel, RowReader
|
13
13
|
from dcicutils.file_utils import search_for_file
|
14
|
-
from dcicutils.misc_utils import (create_dict,
|
14
|
+
from dcicutils.misc_utils import (create_dict, create_readonly_object, load_json_if,
|
15
|
+
merge_objects, remove_empty_properties, right_trim,
|
15
16
|
split_string, to_boolean, to_enum, to_float, to_integer, VirtualApp)
|
17
|
+
from dcicutils.portal_object_utils import PortalObject
|
16
18
|
from dcicutils.portal_utils import Portal as PortalBase
|
17
19
|
from dcicutils.zip_utils import unpack_gz_file_to_temporary_file, unpack_files
|
18
20
|
|
@@ -61,6 +63,10 @@ class StructuredDataSet:
|
|
61
63
|
def data(self) -> dict:
|
62
64
|
return self._data
|
63
65
|
|
66
|
+
@property
|
67
|
+
def portal(self) -> Optional[Portal]:
|
68
|
+
return self._portal
|
69
|
+
|
64
70
|
@staticmethod
|
65
71
|
def load(file: str, portal: Optional[Union[VirtualApp, TestApp, Portal]] = None,
|
66
72
|
schemas: Optional[List[dict]] = None, autoadd: Optional[dict] = None,
|
@@ -140,6 +146,27 @@ class StructuredDataSet:
|
|
140
146
|
upload_file["path"] = file_path
|
141
147
|
return upload_files
|
142
148
|
|
149
|
+
def compare(self) -> dict:
|
150
|
+
diffs = {}
|
151
|
+
if self.data or self.portal:
|
152
|
+
refs = self.resolved_refs_with_uuids
|
153
|
+
for object_type in self.data:
|
154
|
+
if not diffs.get(object_type):
|
155
|
+
diffs[object_type] = []
|
156
|
+
for portal_object in self.data[object_type]:
|
157
|
+
portal_object = PortalObject(self.portal, portal_object, object_type)
|
158
|
+
existing_object, identifying_path = portal_object.lookup(include_identifying_path=True, raw=True)
|
159
|
+
if existing_object:
|
160
|
+
object_diffs = portal_object.compare(existing_object, consider_refs=True, resolved_refs=refs)
|
161
|
+
diffs[object_type].append(create_readonly_object(path=identifying_path,
|
162
|
+
uuid=existing_object.uuid,
|
163
|
+
diffs=object_diffs or None))
|
164
|
+
else:
|
165
|
+
# If there is no existing object we still create a record for this object
|
166
|
+
# but with no uuid which will be the indication that it does not exist.
|
167
|
+
diffs[object_type].append(create_readonly_object(path=identifying_path, uuid=None, diffs=None))
|
168
|
+
return diffs
|
169
|
+
|
143
170
|
def _load_file(self, file: str) -> None:
|
144
171
|
# Returns a dictionary where each property is the name (i.e. the type) of the data,
|
145
172
|
# and the value is array of dictionaries for the data itself. Handle these kinds of files:
|
@@ -44,8 +44,8 @@ dcicutils/log_utils.py,sha256=7pWMc6vyrorUZQf-V-M3YC6zrPgNhuV_fzm9xqTPph0,10883
|
|
44
44
|
dcicutils/misc_utils.py,sha256=9JqdVjHLkZUDTryngF3Dbu0m7XcbitbR7izWnxUSWc4,101953
|
45
45
|
dcicutils/obfuscation_utils.py,sha256=fo2jOmDRC6xWpYX49u80bVNisqRRoPskFNX3ymFAmjw,5963
|
46
46
|
dcicutils/opensearch_utils.py,sha256=V2exmFYW8Xl2_pGFixF4I2Cc549Opwe4PhFi5twC0M8,1017
|
47
|
-
dcicutils/portal_object_utils.py,sha256=
|
48
|
-
dcicutils/portal_utils.py,sha256=
|
47
|
+
dcicutils/portal_object_utils.py,sha256=KsOvrC2LM3OFX8GoHkqdO88td79v_Xvj1gVptZ5IiK4,12252
|
48
|
+
dcicutils/portal_utils.py,sha256=VLTofvk9z5wAQg4PZUi9GmvMCcAeYAX1R3qU1H3CNjw,27065
|
49
49
|
dcicutils/project_utils.py,sha256=qPdCaFmWUVBJw4rw342iUytwdQC0P-XKpK4mhyIulMM,31250
|
50
50
|
dcicutils/qa_checkers.py,sha256=cdXjeL0jCDFDLT8VR8Px78aS10hwNISOO5G_Zv2TZ6M,20534
|
51
51
|
dcicutils/qa_utils.py,sha256=TT0SiJWiuxYvbsIyhK9VO4uV_suxhB6CpuC4qPacCzQ,160208
|
@@ -59,15 +59,15 @@ dcicutils/secrets_utils.py,sha256=8dppXAsiHhJzI6NmOcvJV5ldvKkQZzh3Fl-cb8Wm7MI,19
|
|
59
59
|
dcicutils/sheet_utils.py,sha256=VlmzteONW5VF_Q4vo0yA5vesz1ViUah1MZ_yA1rwZ0M,33629
|
60
60
|
dcicutils/snapshot_utils.py,sha256=ymP7PXH6-yEiXAt75w0ldQFciGNqWBClNxC5gfX2FnY,22961
|
61
61
|
dcicutils/ssl_certificate_utils.py,sha256=F0ifz_wnRRN9dfrfsz7aCp4UDLgHEY8LaK7PjnNvrAQ,9707
|
62
|
-
dcicutils/structured_data.py,sha256=
|
62
|
+
dcicutils/structured_data.py,sha256=M5y5z5rnD-rXN2Ynf3hoJ8kagABUmVX98BisXdHg1C8,37201
|
63
63
|
dcicutils/task_utils.py,sha256=MF8ujmTD6-O2AC2gRGPHyGdUrVKgtr8epT5XU8WtNjk,8082
|
64
64
|
dcicutils/tmpfile_utils.py,sha256=n95XF8dZVbQRSXBZTGToXXfSs3JUVRyN6c3ZZ0nhAWI,1403
|
65
65
|
dcicutils/trace_utils.py,sha256=g8kwV4ebEy5kXW6oOrEAUsurBcCROvwtZqz9fczsGRE,1769
|
66
66
|
dcicutils/validation_utils.py,sha256=cMZIU2cY98FYtzK52z5WUYck7urH6JcqOuz9jkXpqzg,14797
|
67
67
|
dcicutils/variant_utils.py,sha256=2H9azNx3xAj-MySg-uZ2SFqbWs4kZvf61JnK6b-h4Qw,4343
|
68
68
|
dcicutils/zip_utils.py,sha256=rnjNv_k6L9jT2SjDSgVXp4BEJYLtz9XN6Cl2Fy-tqnM,2027
|
69
|
-
dcicutils-8.7.1.
|
70
|
-
dcicutils-8.7.1.
|
71
|
-
dcicutils-8.7.1.
|
72
|
-
dcicutils-8.7.1.
|
73
|
-
dcicutils-8.7.1.
|
69
|
+
dcicutils-8.7.1.1b9.dist-info/LICENSE.txt,sha256=qnwSmfnEWMl5l78VPDEzAmEbLVrRqQvfUQiHT0ehrOo,1102
|
70
|
+
dcicutils-8.7.1.1b9.dist-info/METADATA,sha256=6NZZJZyyO_0mZfDU0xU948JwXitVSbKiup22bPx6be8,3314
|
71
|
+
dcicutils-8.7.1.1b9.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
|
72
|
+
dcicutils-8.7.1.1b9.dist-info/entry_points.txt,sha256=8wbw5csMIgBXhkwfgsgJeuFcoUc0WsucUxmOyml2aoA,209
|
73
|
+
dcicutils-8.7.1.1b9.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|