dcicutils 8.4.0.1b11__py3-none-any.whl → 8.4.0.1b12__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- dcicutils/portal_utils.py +267 -0
- dcicutils/structured_data.py +11 -247
- {dcicutils-8.4.0.1b11.dist-info → dcicutils-8.4.0.1b12.dist-info}/METADATA +1 -1
- {dcicutils-8.4.0.1b11.dist-info → dcicutils-8.4.0.1b12.dist-info}/RECORD +7 -6
- {dcicutils-8.4.0.1b11.dist-info → dcicutils-8.4.0.1b12.dist-info}/LICENSE.txt +0 -0
- {dcicutils-8.4.0.1b11.dist-info → dcicutils-8.4.0.1b12.dist-info}/WHEEL +0 -0
- {dcicutils-8.4.0.1b11.dist-info → dcicutils-8.4.0.1b12.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,267 @@
|
|
1
|
+
from collections import deque
|
2
|
+
from pyramid.paster import get_app
|
3
|
+
from pyramid.router import Router
|
4
|
+
import re
|
5
|
+
import requests
|
6
|
+
from requests.models import Response as RequestResponse
|
7
|
+
from typing import Optional, Type, Union
|
8
|
+
from webtest.app import TestApp, TestResponse
|
9
|
+
from dcicutils.common import OrchestratedApp, APP_CGAP, APP_FOURFRONT, APP_SMAHT, ORCHESTRATED_APPS
|
10
|
+
from dcicutils.creds_utils import CGAPKeyManager, FourfrontKeyManager, SMaHTKeyManager
|
11
|
+
from dcicutils.ff_utils import get_metadata, get_schema, patch_metadata, post_metadata
|
12
|
+
from dcicutils.misc_utils import to_camel_case, VirtualApp
|
13
|
+
from dcicutils.zip_utils import temporary_file
|
14
|
+
|
15
|
+
Portal = Type["Portal"] # Forward type reference for type hints.
|
16
|
+
FILE_SCHEMA_NAME = "File"
|
17
|
+
|
18
|
+
|
19
|
+
class Portal:
|
20
|
+
|
21
|
+
def __init__(self,
|
22
|
+
arg: Optional[Union[VirtualApp, TestApp, Router, Portal, dict, tuple, str]] = None,
|
23
|
+
env: Optional[str] = None, app: Optional[OrchestratedApp] = None, server: Optional[str] = None,
|
24
|
+
key: Optional[Union[dict, tuple]] = None,
|
25
|
+
vapp: Optional[Union[VirtualApp, TestApp, Router, Portal, str]] = None,
|
26
|
+
portal: Optional[Union[VirtualApp, TestApp, Router, Portal, str]] = None) -> Portal:
|
27
|
+
if vapp and not portal:
|
28
|
+
portal = vapp
|
29
|
+
if ((isinstance(arg, (VirtualApp, TestApp, Router, Portal)) or
|
30
|
+
isinstance(arg, str) and arg.endswith(".ini")) and not portal):
|
31
|
+
portal = arg
|
32
|
+
elif isinstance(arg, str) and not env:
|
33
|
+
env = arg
|
34
|
+
elif (isinstance(arg, dict) or isinstance(arg, tuple)) and not key:
|
35
|
+
key = arg
|
36
|
+
if not app and env:
|
37
|
+
if env.startswith(APP_SMAHT):
|
38
|
+
app = APP_SMAHT
|
39
|
+
elif env.startswith(APP_CGAP):
|
40
|
+
app = APP_CGAP
|
41
|
+
elif env.startswith(APP_FOURFRONT):
|
42
|
+
app = APP_FOURFRONT
|
43
|
+
if isinstance(portal, Portal):
|
44
|
+
self._vapp = portal._vapp
|
45
|
+
self._env = portal._env
|
46
|
+
self._app = portal._app
|
47
|
+
self._server = portal._server
|
48
|
+
self._key = portal._key
|
49
|
+
self._key_pair = portal._key_pair
|
50
|
+
self._key_file = portal._key_file
|
51
|
+
return
|
52
|
+
self._vapp = None
|
53
|
+
self._env = env
|
54
|
+
self._app = app
|
55
|
+
self._server = server
|
56
|
+
self._key = None
|
57
|
+
self._key_pair = None
|
58
|
+
self._key_file = None
|
59
|
+
if isinstance(portal, (VirtualApp, TestApp)):
|
60
|
+
self._vapp = portal
|
61
|
+
elif isinstance(portal, (Router, str)):
|
62
|
+
self._vapp = Portal._create_testapp(portal)
|
63
|
+
elif isinstance(key, dict):
|
64
|
+
self._key = key
|
65
|
+
self._key_pair = (key.get("key"), key.get("secret")) if key else None
|
66
|
+
if key_server := key.get("server"):
|
67
|
+
self._server = key_server
|
68
|
+
elif isinstance(key, tuple) and len(key) >= 2:
|
69
|
+
self._key = {"key": key[0], "secret": key[1]}
|
70
|
+
self._key_pair = key
|
71
|
+
elif isinstance(env, str):
|
72
|
+
key_managers = {APP_CGAP: CGAPKeyManager, APP_FOURFRONT: FourfrontKeyManager, APP_SMAHT: SMaHTKeyManager}
|
73
|
+
if not (key_manager := key_managers.get(self._app)) or not (key_manager := key_manager()):
|
74
|
+
raise Exception(f"Invalid app name: {self._app} (valid: {', '.join(ORCHESTRATED_APPS)}).")
|
75
|
+
if isinstance(env, str):
|
76
|
+
self._key = key_manager.get_keydict_for_env(env)
|
77
|
+
if key_server := self._key.get("server"):
|
78
|
+
self._server = key_server
|
79
|
+
elif isinstance(self._server, str):
|
80
|
+
self._key = key_manager.get_keydict_for_server(self._server)
|
81
|
+
self._key_pair = key_manager.keydict_to_keypair(self._key) if self._key else None
|
82
|
+
self._key_file = key_manager.keys_file
|
83
|
+
|
84
|
+
@property
|
85
|
+
def env(self):
|
86
|
+
return self._env
|
87
|
+
|
88
|
+
@property
|
89
|
+
def app(self):
|
90
|
+
return self._app
|
91
|
+
|
92
|
+
@property
|
93
|
+
def server(self):
|
94
|
+
return self._server
|
95
|
+
|
96
|
+
@property
|
97
|
+
def key(self):
|
98
|
+
return self._key
|
99
|
+
|
100
|
+
@property
|
101
|
+
def key_pair(self):
|
102
|
+
return self._key_pair
|
103
|
+
|
104
|
+
@property
|
105
|
+
def key_file(self):
|
106
|
+
return self._key_file
|
107
|
+
|
108
|
+
@property
|
109
|
+
def vapp(self):
|
110
|
+
return self._vapp
|
111
|
+
|
112
|
+
def get_metadata(self, object_id: str) -> Optional[dict]:
|
113
|
+
return get_metadata(obj_id=object_id, vapp=self._vapp, key=self._key)
|
114
|
+
|
115
|
+
def patch_metadata(self, object_id: str, data: str) -> Optional[dict]:
|
116
|
+
if self._key:
|
117
|
+
return patch_metadata(obj_id=object_id, patch_item=data, key=self._key)
|
118
|
+
return self.patch(f"/{object_id}", data)
|
119
|
+
|
120
|
+
def post_metadata(self, object_type: str, data: str) -> Optional[dict]:
|
121
|
+
if self._key:
|
122
|
+
return post_metadata(schema_name=object_type, post_item=data, key=self._key)
|
123
|
+
return self.post(f"/{object_type}", data)
|
124
|
+
|
125
|
+
def get(self, uri: str, follow: bool = True, **kwargs) -> Optional[Union[RequestResponse, TestResponse]]:
|
126
|
+
if isinstance(self._vapp, (VirtualApp, TestApp)):
|
127
|
+
response = self._vapp.get(self._uri(uri), **self._kwargs(**kwargs))
|
128
|
+
if response and response.status_code in [301, 302, 303, 307, 308] and follow:
|
129
|
+
response = response.follow()
|
130
|
+
return self._response(response)
|
131
|
+
return requests.get(self._uri(uri), allow_redirects=follow, **self._kwargs(**kwargs))
|
132
|
+
|
133
|
+
def patch(self, uri: str, data: Optional[dict] = None,
|
134
|
+
json: Optional[dict] = None, **kwargs) -> Optional[Union[RequestResponse, TestResponse]]:
|
135
|
+
if isinstance(self._vapp, (VirtualApp, TestApp)):
|
136
|
+
return self._vapp.patch_json(self._uri(uri), json or data, **self._kwargs(**kwargs))
|
137
|
+
return requests.patch(self._uri(uri), json=json or data, **self._kwargs(**kwargs))
|
138
|
+
|
139
|
+
def post(self, uri: str, data: Optional[dict] = None, json: Optional[dict] = None,
|
140
|
+
files: Optional[dict] = None, **kwargs) -> Optional[Union[RequestResponse, TestResponse]]:
|
141
|
+
if isinstance(self._vapp, (VirtualApp, TestApp)):
|
142
|
+
if files:
|
143
|
+
return self._vapp.post(self._uri(uri), json or data, upload_files=files, **self._kwargs(**kwargs))
|
144
|
+
else:
|
145
|
+
return self._vapp.post_json(self._uri(uri), json or data, upload_files=files, **self._kwargs(**kwargs))
|
146
|
+
return requests.post(self._uri(uri), json=json or data, files=files, **self._kwargs(**kwargs))
|
147
|
+
|
148
|
+
def get_schema(self, schema_name: str) -> Optional[dict]:
|
149
|
+
return get_schema(self.schema_name(schema_name), portal_vapp=self._vapp, key=self._key)
|
150
|
+
|
151
|
+
def get_schemas(self) -> dict:
|
152
|
+
return self.get("/profiles/").json()
|
153
|
+
|
154
|
+
@staticmethod
|
155
|
+
def schema_name(name: str) -> str:
|
156
|
+
return to_camel_case(name)
|
157
|
+
|
158
|
+
def is_file_schema(self, schema_name: str) -> bool:
|
159
|
+
if super_type_map := self.get_schemas_super_type_map():
|
160
|
+
if file_super_type := super_type_map.get(FILE_SCHEMA_NAME):
|
161
|
+
return self.schema_name(schema_name) in file_super_type
|
162
|
+
return False
|
163
|
+
|
164
|
+
def get_schemas_super_type_map(self) -> dict:
|
165
|
+
"""
|
166
|
+
Returns the "super type map" for all of the known schemas (via /profiles).
|
167
|
+
This is a dictionary of all types which have (one or more) sub-types whose value is
|
168
|
+
an array of all of those sub-types (direct and all descendents), in breadth first order.
|
169
|
+
"""
|
170
|
+
def breadth_first(super_type_map: dict, super_type_name: str) -> dict:
|
171
|
+
result = []
|
172
|
+
queue = deque(super_type_map.get(super_type_name, []))
|
173
|
+
while queue:
|
174
|
+
result.append(sub_type_name := queue.popleft())
|
175
|
+
if sub_type_name in super_type_map:
|
176
|
+
queue.extend(super_type_map[sub_type_name])
|
177
|
+
return result
|
178
|
+
if not (schemas := self.get_schemas()):
|
179
|
+
return {}
|
180
|
+
super_type_map = {}
|
181
|
+
for type_name in schemas:
|
182
|
+
if super_type_name := schemas[type_name].get("rdfs:subClassOf"):
|
183
|
+
super_type_name = super_type_name.replace("/profiles/", "").replace(".json", "")
|
184
|
+
if super_type_name != "Item":
|
185
|
+
if not super_type_map.get(super_type_name):
|
186
|
+
super_type_map[super_type_name] = [type_name]
|
187
|
+
elif type_name not in super_type_map[super_type_name]:
|
188
|
+
super_type_map[super_type_name].append(type_name)
|
189
|
+
super_type_map_flattened = {}
|
190
|
+
for super_type_name in super_type_map:
|
191
|
+
super_type_map_flattened[super_type_name] = breadth_first(super_type_map, super_type_name)
|
192
|
+
return super_type_map_flattened
|
193
|
+
|
194
|
+
def _uri(self, uri: str) -> str:
|
195
|
+
if not isinstance(uri, str) or not uri:
|
196
|
+
return "/"
|
197
|
+
if uri.lower().startswith("http://") or uri.lower().startswith("https://"):
|
198
|
+
return uri
|
199
|
+
uri = re.sub(r"/+", "/", uri)
|
200
|
+
return (self._server + ("/" if uri.startswith("/") else "") + uri) if self._server else uri
|
201
|
+
|
202
|
+
def _kwargs(self, **kwargs) -> dict:
|
203
|
+
result_kwargs = {"headers":
|
204
|
+
kwargs.get("headers", {"Content-type": "application/json", "Accept": "application/json"})}
|
205
|
+
if self._key_pair:
|
206
|
+
result_kwargs["auth"] = self._key_pair
|
207
|
+
if isinstance(timeout := kwargs.get("timeout"), int):
|
208
|
+
result_kwargs["timeout"] = timeout
|
209
|
+
return result_kwargs
|
210
|
+
|
211
|
+
def _response(self, response) -> Optional[RequestResponse]:
|
212
|
+
if response and isinstance(getattr(response.__class__, "json"), property):
|
213
|
+
class RequestResponseWrapper: # For consistency change json property to method.
|
214
|
+
def __init__(self, response, **kwargs):
|
215
|
+
super().__init__(**kwargs)
|
216
|
+
self._response = response
|
217
|
+
def __getattr__(self, attr): # noqa
|
218
|
+
return getattr(self._response, attr)
|
219
|
+
def json(self): # noqa
|
220
|
+
return self._response.json
|
221
|
+
response = RequestResponseWrapper(response)
|
222
|
+
return response
|
223
|
+
|
224
|
+
@staticmethod
|
225
|
+
def create_for_testing(ini_file: Optional[str] = None) -> Portal:
|
226
|
+
if isinstance(ini_file, str):
|
227
|
+
return Portal(Portal._create_testapp(ini_file))
|
228
|
+
minimal_ini_for_unit_testing = "[app:app]\nuse = egg:encoded\nsqlalchemy.url = postgresql://dummy\n"
|
229
|
+
with temporary_file(content=minimal_ini_for_unit_testing, suffix=".ini") as ini_file:
|
230
|
+
return Portal(Portal._create_testapp(ini_file))
|
231
|
+
|
232
|
+
@staticmethod
|
233
|
+
def create_for_testing_local(ini_file: Optional[str] = None) -> Portal:
|
234
|
+
if isinstance(ini_file, str) and ini_file:
|
235
|
+
return Portal(Portal._create_testapp(ini_file))
|
236
|
+
minimal_ini_for_testing_local = "\n".join([
|
237
|
+
"[app:app]\nuse = egg:encoded\nfile_upload_bucket = dummy",
|
238
|
+
"sqlalchemy.url = postgresql://postgres@localhost:5441/postgres?host=/tmp/snovault/pgdata",
|
239
|
+
"multiauth.groupfinder = encoded.authorization.smaht_groupfinder",
|
240
|
+
"multiauth.policies = auth0 session remoteuser accesskey",
|
241
|
+
"multiauth.policy.session.namespace = mailto",
|
242
|
+
"multiauth.policy.session.use = encoded.authentication.NamespacedAuthenticationPolicy",
|
243
|
+
"multiauth.policy.session.base = pyramid.authentication.SessionAuthenticationPolicy",
|
244
|
+
"multiauth.policy.remoteuser.namespace = remoteuser",
|
245
|
+
"multiauth.policy.remoteuser.use = encoded.authentication.NamespacedAuthenticationPolicy",
|
246
|
+
"multiauth.policy.remoteuser.base = pyramid.authentication.RemoteUserAuthenticationPolicy",
|
247
|
+
"multiauth.policy.accesskey.namespace = accesskey",
|
248
|
+
"multiauth.policy.accesskey.use = encoded.authentication.NamespacedAuthenticationPolicy",
|
249
|
+
"multiauth.policy.accesskey.base = encoded.authentication.BasicAuthAuthenticationPolicy",
|
250
|
+
"multiauth.policy.accesskey.check = encoded.authentication.basic_auth_check",
|
251
|
+
"multiauth.policy.auth0.use = encoded.authentication.NamespacedAuthenticationPolicy",
|
252
|
+
"multiauth.policy.auth0.namespace = auth0",
|
253
|
+
"multiauth.policy.auth0.base = encoded.authentication.Auth0AuthenticationPolicy"
|
254
|
+
])
|
255
|
+
with temporary_file(content=minimal_ini_for_testing_local, suffix=".ini") as minimal_ini_file:
|
256
|
+
return Portal(Portal._create_testapp(minimal_ini_file))
|
257
|
+
|
258
|
+
@staticmethod
|
259
|
+
def _create_testapp(value: Union[str, Router, TestApp] = "development.ini") -> TestApp:
|
260
|
+
"""
|
261
|
+
Creates and returns a TestApp. Refactored out of above loadxl code to consolidate at a
|
262
|
+
single point; also for use by the generate_local_access_key and view_local_object scripts.
|
263
|
+
"""
|
264
|
+
if isinstance(value, TestApp):
|
265
|
+
return value
|
266
|
+
app = value if isinstance(value, Router) else get_app(value, "app")
|
267
|
+
return TestApp(app, {"HTTP_ACCEPT": "application/json", "REMOTE_USER": "TEST"})
|
dcicutils/structured_data.py
CHANGED
@@ -1,24 +1,19 @@
|
|
1
|
-
from collections import deque
|
2
1
|
import copy
|
3
2
|
from functools import lru_cache
|
4
3
|
import json
|
5
4
|
from jsonschema import Draft7Validator as SchemaValidator
|
6
5
|
import os
|
7
|
-
from pyramid.paster import get_app
|
8
6
|
from pyramid.router import Router
|
9
7
|
import re
|
10
|
-
import requests
|
11
|
-
from requests.models import Response as RequestResponse
|
12
8
|
import sys
|
13
9
|
from typing import Any, Callable, List, Optional, Tuple, Type, Union
|
14
|
-
from webtest.app import TestApp
|
15
|
-
from dcicutils.common import OrchestratedApp
|
16
|
-
from dcicutils.creds_utils import CGAPKeyManager, FourfrontKeyManager, SMaHTKeyManager
|
10
|
+
from webtest.app import TestApp
|
11
|
+
from dcicutils.common import OrchestratedApp
|
17
12
|
from dcicutils.data_readers import CsvReader, Excel, RowReader
|
18
|
-
from dcicutils.ff_utils import get_metadata, get_schema, patch_metadata, post_metadata
|
19
13
|
from dcicutils.misc_utils import (create_object, load_json_if, merge_objects, remove_empty_properties, right_trim,
|
20
|
-
split_string, to_boolean,
|
21
|
-
from dcicutils.
|
14
|
+
split_string, to_boolean, to_enum, to_float, to_integer, VirtualApp)
|
15
|
+
from dcicutils.portal_utils import Portal as PortalBase
|
16
|
+
from dcicutils.zip_utils import unpack_gz_file_to_temporary_file, unpack_files
|
22
17
|
|
23
18
|
|
24
19
|
# Classes/functions to parse a CSV or Excel Spreadsheet into structured data, using a specialized
|
@@ -36,12 +31,10 @@ ARRAY_VALUE_DELIMITER_ESCAPE_CHAR = "\\"
|
|
36
31
|
ARRAY_NAME_SUFFIX_CHAR = "#"
|
37
32
|
ARRAY_NAME_SUFFIX_REGEX = re.compile(rf"{ARRAY_NAME_SUFFIX_CHAR}\d+")
|
38
33
|
DOTTED_NAME_DELIMITER_CHAR = "."
|
39
|
-
FILE_SCHEMA_NAME = "File"
|
40
34
|
FILE_SCHEMA_NAME_PROPERTY = "filename"
|
41
35
|
|
42
36
|
# Forward type references for type hints.
|
43
37
|
Portal = Type["Portal"]
|
44
|
-
PortalBase = Type["PortalBase"]
|
45
38
|
Schema = Type["Schema"]
|
46
39
|
StructuredDataSet = Type["StructuredDataSet"]
|
47
40
|
|
@@ -526,7 +519,7 @@ class Schema:
|
|
526
519
|
@staticmethod
|
527
520
|
def type_name(value: str) -> str: # File or other name.
|
528
521
|
name = os.path.basename(value).replace(" ", "") if isinstance(value, str) else ""
|
529
|
-
return
|
522
|
+
return PortalBase.schema_name(name[0:dot] if (dot := name.rfind(".")) > 0 else name)
|
530
523
|
|
531
524
|
@staticmethod
|
532
525
|
def array_indices(name: str) -> Tuple[Optional[str], Optional[List[int]]]:
|
@@ -540,211 +533,15 @@ class Schema:
|
|
540
533
|
return (name, indices) if indices else (None, None)
|
541
534
|
|
542
535
|
|
543
|
-
class PortalBase:
|
544
|
-
|
545
|
-
def __init__(self,
|
546
|
-
arg: Optional[Union[VirtualApp, TestApp, Router, PortalBase, dict, tuple, str]] = None,
|
547
|
-
env: Optional[str] = None, app: OrchestratedApp = APP_SMAHT, server: Optional[str] = None,
|
548
|
-
key: Optional[Union[dict, tuple]] = None,
|
549
|
-
portal: Optional[Union[VirtualApp, TestApp, Router, PortalBase, str]] = None) -> PortalBase:
|
550
|
-
if ((isinstance(arg, (VirtualApp, TestApp, Router, PortalBase)) or
|
551
|
-
isinstance(arg, str) and arg.endswith(".ini")) and not portal):
|
552
|
-
portal = arg
|
553
|
-
elif isinstance(arg, str) and not env:
|
554
|
-
env = arg
|
555
|
-
elif (isinstance(arg, dict) or isinstance(arg, tuple)) and not key:
|
556
|
-
key = arg
|
557
|
-
self._vapp = None
|
558
|
-
self._key = None
|
559
|
-
self._key_pair = None
|
560
|
-
self._key_file = None
|
561
|
-
self._env = env
|
562
|
-
self._app = app
|
563
|
-
self._server = None
|
564
|
-
if isinstance(portal, PortalBase):
|
565
|
-
self._vapp = portal._vapp
|
566
|
-
self._key = portal._key
|
567
|
-
self._key_pair = portal._key_pair
|
568
|
-
self._server = portal._server
|
569
|
-
elif isinstance(portal, (VirtualApp, TestApp)):
|
570
|
-
self._vapp = portal
|
571
|
-
elif isinstance(portal, (Router, str)):
|
572
|
-
self._vapp = PortalBase._create_testapp(portal)
|
573
|
-
elif isinstance(key, dict):
|
574
|
-
self._key = key
|
575
|
-
self._key_pair = (key.get("key"), key.get("secret")) if key else None
|
576
|
-
self._server = key.get("server")
|
577
|
-
elif isinstance(key, tuple) and len(key) >= 2:
|
578
|
-
self._key = {"key": key[0], "secret": key[1]}
|
579
|
-
self._key_pair = key
|
580
|
-
elif isinstance(env, str):
|
581
|
-
key_managers = {APP_CGAP: CGAPKeyManager, APP_FOURFRONT: FourfrontKeyManager, APP_SMAHT: SMaHTKeyManager}
|
582
|
-
if not (key_manager := key_managers.get(app)) or not (key_manager := key_manager()):
|
583
|
-
raise Exception(f"Invalid app name: {app} (valid: {', '.join(ORCHESTRATED_APPS)}).")
|
584
|
-
if isinstance(env, str):
|
585
|
-
self._key = key_manager.get_keydict_for_env(env)
|
586
|
-
self._server = self._key.get("server") if self._key else None
|
587
|
-
elif isinstance(server, str):
|
588
|
-
self._key = key_manager.get_keydict_for_server(server)
|
589
|
-
self._server = server
|
590
|
-
self._key_pair = key_manager.keydict_to_keypair(self._key) if self._key else None
|
591
|
-
self._key_file = key_manager.keys_file
|
592
|
-
|
593
|
-
@property
|
594
|
-
def env(self):
|
595
|
-
return self._env
|
596
|
-
|
597
|
-
@property
|
598
|
-
def app(self):
|
599
|
-
return self._app
|
600
|
-
|
601
|
-
@property
|
602
|
-
def key(self):
|
603
|
-
return self._key
|
604
|
-
|
605
|
-
@property
|
606
|
-
def key_pair(self):
|
607
|
-
return self._key_pair
|
608
|
-
|
609
|
-
@property
|
610
|
-
def key_file(self):
|
611
|
-
return self._key_file
|
612
|
-
|
613
|
-
@property
|
614
|
-
def server(self):
|
615
|
-
return self._server
|
616
|
-
|
617
|
-
@property
|
618
|
-
def vapp(self):
|
619
|
-
return self._vapp
|
620
|
-
|
621
|
-
def get_metadata(self, object_id: str) -> Optional[dict]:
|
622
|
-
return get_metadata(obj_id=object_id, vapp=self._vapp, key=self._key)
|
623
|
-
|
624
|
-
def patch_metadata(self, object_id: str, data: str) -> Optional[dict]:
|
625
|
-
if self._key:
|
626
|
-
return patch_metadata(obj_id=object_id, patch_item=data, key=self._key)
|
627
|
-
return self.patch(f"/{object_id}", data)
|
628
|
-
|
629
|
-
def post_metadata(self, object_type: str, data: str) -> Optional[dict]:
|
630
|
-
if self._key:
|
631
|
-
return post_metadata(schema_name=object_type, post_item=data, key=self._key)
|
632
|
-
return self.post(f"/{object_type}", data)
|
633
|
-
|
634
|
-
def get(self, uri: str, follow: bool = True, **kwargs) -> Optional[Union[RequestResponse, TestResponse]]:
|
635
|
-
if isinstance(self._vapp, (VirtualApp, TestApp)):
|
636
|
-
response = self._vapp.get(self._uri(uri), **self._kwargs(**kwargs))
|
637
|
-
if response and response.status_code in [301, 302, 303, 307, 308] and follow:
|
638
|
-
response = response.follow()
|
639
|
-
return self._response(response)
|
640
|
-
return requests.get(self._uri(uri), allow_redirects=follow, **self._kwargs(**kwargs))
|
641
|
-
|
642
|
-
def patch(self, uri: str, data: Optional[dict] = None,
|
643
|
-
json: Optional[dict] = None, **kwargs) -> Optional[Union[RequestResponse, TestResponse]]:
|
644
|
-
if isinstance(self._vapp, (VirtualApp, TestApp)):
|
645
|
-
return self._vapp.patch_json(self._uri(uri), json or data, **self._kwargs(**kwargs))
|
646
|
-
return requests.patch(self._uri(uri), json=json or data, **self._kwargs(**kwargs))
|
647
|
-
|
648
|
-
def post(self, uri: str, data: Optional[dict] = None, json: Optional[dict] = None,
|
649
|
-
files: Optional[dict] = None, **kwargs) -> Optional[Union[RequestResponse, TestResponse]]:
|
650
|
-
if isinstance(self._vapp, (VirtualApp, TestApp)):
|
651
|
-
if files:
|
652
|
-
return self._vapp.post(self._uri(uri), json or data, upload_files=files, **self._kwargs(**kwargs))
|
653
|
-
else:
|
654
|
-
return self._vapp.post_json(self._uri(uri), json or data, upload_files=files, **self._kwargs(**kwargs))
|
655
|
-
return requests.post(self._uri(uri), json=json or data, files=files, **self._kwargs(**kwargs))
|
656
|
-
|
657
|
-
def get_schema(self, schema_name: str) -> Optional[dict]:
|
658
|
-
return get_schema(schema_name, portal_vapp=self._vapp, key=self._key)
|
659
|
-
|
660
|
-
def get_schemas(self) -> dict:
|
661
|
-
return self.get("/profiles/").json()
|
662
|
-
|
663
|
-
def _uri(self, uri: str) -> str:
|
664
|
-
if not isinstance(uri, str) or not uri:
|
665
|
-
return "/"
|
666
|
-
if uri.lower().startswith("http://") or uri.lower().startswith("https://"):
|
667
|
-
return uri
|
668
|
-
uri = re.sub(r"/+", "/", uri)
|
669
|
-
return (self._server + ("/" if uri.startswith("/") else "") + uri) if self._server else uri
|
670
|
-
|
671
|
-
def _kwargs(self, **kwargs) -> dict:
|
672
|
-
result_kwargs = {"headers":
|
673
|
-
kwargs.get("headers", {"Content-type": "application/json", "Accept": "application/json"})}
|
674
|
-
if self._key_pair:
|
675
|
-
result_kwargs["auth"] = self._key_pair
|
676
|
-
if isinstance(timeout := kwargs.get("timeout"), int):
|
677
|
-
result_kwargs["timeout"] = timeout
|
678
|
-
return result_kwargs
|
679
|
-
|
680
|
-
def _response(self, response) -> Optional[RequestResponse]:
|
681
|
-
if response and isinstance(getattr(response.__class__, "json"), property):
|
682
|
-
class RequestResponseWrapper: # For consistency change json property to method.
|
683
|
-
def __init__(self, response, **kwargs):
|
684
|
-
super().__init__(**kwargs)
|
685
|
-
self._response = response
|
686
|
-
def __getattr__(self, attr): # noqa
|
687
|
-
return getattr(self._response, attr)
|
688
|
-
def json(self): # noqa
|
689
|
-
return self._response.json
|
690
|
-
response = RequestResponseWrapper(response)
|
691
|
-
return response
|
692
|
-
|
693
|
-
@staticmethod
|
694
|
-
def create_for_testing(ini_file: Optional[str] = None) -> PortalBase:
|
695
|
-
if isinstance(ini_file, str):
|
696
|
-
return PortalBase(PortalBase._create_testapp(ini_file))
|
697
|
-
minimal_ini_for_unit_testing = "[app:app]\nuse = egg:encoded\nsqlalchemy.url = postgresql://dummy\n"
|
698
|
-
with temporary_file(content=minimal_ini_for_unit_testing, suffix=".ini") as ini_file:
|
699
|
-
return PortalBase(PortalBase._create_testapp(ini_file))
|
700
|
-
|
701
|
-
@staticmethod
|
702
|
-
def create_for_testing_local(ini_file: Optional[str] = None) -> PortalBase:
|
703
|
-
if isinstance(ini_file, str) and ini_file:
|
704
|
-
return PortalBase(PortalBase._create_testapp(ini_file))
|
705
|
-
minimal_ini_for_testing_local = "\n".join([
|
706
|
-
"[app:app]\nuse = egg:encoded\nfile_upload_bucket = dummy",
|
707
|
-
"sqlalchemy.url = postgresql://postgres@localhost:5441/postgres?host=/tmp/snovault/pgdata",
|
708
|
-
"multiauth.groupfinder = encoded.authorization.smaht_groupfinder",
|
709
|
-
"multiauth.policies = auth0 session remoteuser accesskey",
|
710
|
-
"multiauth.policy.session.namespace = mailto",
|
711
|
-
"multiauth.policy.session.use = encoded.authentication.NamespacedAuthenticationPolicy",
|
712
|
-
"multiauth.policy.session.base = pyramid.authentication.SessionAuthenticationPolicy",
|
713
|
-
"multiauth.policy.remoteuser.namespace = remoteuser",
|
714
|
-
"multiauth.policy.remoteuser.use = encoded.authentication.NamespacedAuthenticationPolicy",
|
715
|
-
"multiauth.policy.remoteuser.base = pyramid.authentication.RemoteUserAuthenticationPolicy",
|
716
|
-
"multiauth.policy.accesskey.namespace = accesskey",
|
717
|
-
"multiauth.policy.accesskey.use = encoded.authentication.NamespacedAuthenticationPolicy",
|
718
|
-
"multiauth.policy.accesskey.base = encoded.authentication.BasicAuthAuthenticationPolicy",
|
719
|
-
"multiauth.policy.accesskey.check = encoded.authentication.basic_auth_check",
|
720
|
-
"multiauth.policy.auth0.use = encoded.authentication.NamespacedAuthenticationPolicy",
|
721
|
-
"multiauth.policy.auth0.namespace = auth0",
|
722
|
-
"multiauth.policy.auth0.base = encoded.authentication.Auth0AuthenticationPolicy"
|
723
|
-
])
|
724
|
-
with temporary_file(content=minimal_ini_for_testing_local, suffix=".ini") as minimal_ini_file:
|
725
|
-
return PortalBase(PortalBase._create_testapp(minimal_ini_file))
|
726
|
-
|
727
|
-
@staticmethod
|
728
|
-
def _create_testapp(value: Union[str, Router, TestApp] = "development.ini") -> TestApp:
|
729
|
-
"""
|
730
|
-
Creates and returns a TestApp. Refactored out of above loadxl code to consolidate at a
|
731
|
-
single point; also for use by the generate_local_access_key and view_local_object scripts.
|
732
|
-
"""
|
733
|
-
if isinstance(value, TestApp):
|
734
|
-
return value
|
735
|
-
app = value if isinstance(value, Router) else get_app(value, "app")
|
736
|
-
return TestApp(app, {"HTTP_ACCEPT": "application/json", "REMOTE_USER": "TEST"})
|
737
|
-
|
738
|
-
|
739
536
|
class Portal(PortalBase):
|
740
537
|
|
741
538
|
def __init__(self,
|
742
539
|
arg: Optional[Union[VirtualApp, TestApp, Router, Portal, dict, tuple, str]] = None,
|
743
|
-
env: Optional[str] = None, app: OrchestratedApp =
|
540
|
+
env: Optional[str] = None, app: OrchestratedApp = None, server: Optional[str] = None,
|
744
541
|
key: Optional[Union[dict, tuple]] = None,
|
745
542
|
portal: Optional[Union[VirtualApp, TestApp, Router, Portal, str]] = None,
|
746
543
|
data: Optional[dict] = None, schemas: Optional[List[dict]] = None) -> Optional[Portal]:
|
747
|
-
super(
|
544
|
+
super().__init__(arg, env=env, app=app, server=server, key=key, portal=portal)
|
748
545
|
if isinstance(arg, Portal) and not portal:
|
749
546
|
portal = arg
|
750
547
|
if isinstance(portal, Portal):
|
@@ -757,7 +554,7 @@ class Portal(PortalBase):
|
|
757
554
|
@lru_cache(maxsize=256)
|
758
555
|
def get_metadata(self, object_name: str) -> Optional[dict]:
|
759
556
|
try:
|
760
|
-
return super(
|
557
|
+
return super().get_metadata(object_name)
|
761
558
|
except Exception:
|
762
559
|
return None
|
763
560
|
|
@@ -772,7 +569,7 @@ class Portal(PortalBase):
|
|
772
569
|
|
773
570
|
@lru_cache(maxsize=1)
|
774
571
|
def get_schemas(self) -> dict:
|
775
|
-
schemas = super(
|
572
|
+
schemas = super().get_schemas()
|
776
573
|
if self._schemas:
|
777
574
|
schemas = copy.deepcopy(schemas)
|
778
575
|
for user_specified_schema in self._schemas:
|
@@ -780,42 +577,9 @@ class Portal(PortalBase):
|
|
780
577
|
schemas[user_specified_schema["title"]] = user_specified_schema
|
781
578
|
return schemas
|
782
579
|
|
783
|
-
def is_file_schema(self, schema_name: str) -> bool:
|
784
|
-
if super_type_map := self.get_schemas_super_type_map():
|
785
|
-
if file_super_type := super_type_map.get(FILE_SCHEMA_NAME):
|
786
|
-
return Schema.type_name(schema_name) in file_super_type
|
787
|
-
return False
|
788
|
-
|
789
580
|
@lru_cache(maxsize=1)
|
790
581
|
def get_schemas_super_type_map(self) -> dict:
|
791
|
-
|
792
|
-
Returns the "super type map" for all of the known schemas (via /profiles).
|
793
|
-
This is a dictionary of all types which have (one or more) sub-types whose value is
|
794
|
-
an array of all of those sub-types (direct and all descendents), in breadth first order.
|
795
|
-
"""
|
796
|
-
def breadth_first(super_type_map: dict, super_type_name: str) -> dict:
|
797
|
-
result = []
|
798
|
-
queue = deque(super_type_map.get(super_type_name, []))
|
799
|
-
while queue:
|
800
|
-
result.append(sub_type_name := queue.popleft())
|
801
|
-
if sub_type_name in super_type_map:
|
802
|
-
queue.extend(super_type_map[sub_type_name])
|
803
|
-
return result
|
804
|
-
if not (schemas := self.get_schemas()):
|
805
|
-
return {}
|
806
|
-
super_type_map = {}
|
807
|
-
for type_name in schemas:
|
808
|
-
if super_type_name := schemas[type_name].get("rdfs:subClassOf"):
|
809
|
-
super_type_name = super_type_name.replace("/profiles/", "").replace(".json", "")
|
810
|
-
if super_type_name != "Item":
|
811
|
-
if not super_type_map.get(super_type_name):
|
812
|
-
super_type_map[super_type_name] = [type_name]
|
813
|
-
elif type_name not in super_type_map[super_type_name]:
|
814
|
-
super_type_map[super_type_name].append(type_name)
|
815
|
-
super_type_map_flattened = {}
|
816
|
-
for super_type_name in super_type_map:
|
817
|
-
super_type_map_flattened[super_type_name] = breadth_first(super_type_map, super_type_name)
|
818
|
-
return super_type_map_flattened
|
582
|
+
return super().get_schemas_super_type_map()
|
819
583
|
|
820
584
|
def ref_exists(self, type_name: str, value: str) -> List[str]:
|
821
585
|
resolved = []
|
@@ -43,6 +43,7 @@ dcicutils/log_utils.py,sha256=7pWMc6vyrorUZQf-V-M3YC6zrPgNhuV_fzm9xqTPph0,10883
|
|
43
43
|
dcicutils/misc_utils.py,sha256=jfyWDrHAlx2REun51i3igEApfEMAsmakRDo2VKUr0LQ,99818
|
44
44
|
dcicutils/obfuscation_utils.py,sha256=fo2jOmDRC6xWpYX49u80bVNisqRRoPskFNX3ymFAmjw,5963
|
45
45
|
dcicutils/opensearch_utils.py,sha256=V2exmFYW8Xl2_pGFixF4I2Cc549Opwe4PhFi5twC0M8,1017
|
46
|
+
dcicutils/portal_utils.py,sha256=s918K2f8ZuMD-2-H2GM6foiGe_YtzgG23xHtYe4gjlk,13021
|
46
47
|
dcicutils/project_utils.py,sha256=qPdCaFmWUVBJw4rw342iUytwdQC0P-XKpK4mhyIulMM,31250
|
47
48
|
dcicutils/qa_checkers.py,sha256=cdXjeL0jCDFDLT8VR8Px78aS10hwNISOO5G_Zv2TZ6M,20534
|
48
49
|
dcicutils/qa_utils.py,sha256=TT0SiJWiuxYvbsIyhK9VO4uV_suxhB6CpuC4qPacCzQ,160208
|
@@ -55,14 +56,14 @@ dcicutils/secrets_utils.py,sha256=8dppXAsiHhJzI6NmOcvJV5ldvKkQZzh3Fl-cb8Wm7MI,19
|
|
55
56
|
dcicutils/sheet_utils.py,sha256=VlmzteONW5VF_Q4vo0yA5vesz1ViUah1MZ_yA1rwZ0M,33629
|
56
57
|
dcicutils/snapshot_utils.py,sha256=ymP7PXH6-yEiXAt75w0ldQFciGNqWBClNxC5gfX2FnY,22961
|
57
58
|
dcicutils/ssl_certificate_utils.py,sha256=F0ifz_wnRRN9dfrfsz7aCp4UDLgHEY8LaK7PjnNvrAQ,9707
|
58
|
-
dcicutils/structured_data.py,sha256=
|
59
|
+
dcicutils/structured_data.py,sha256=LGfNwJXYC5SHs92HA27XijbFZcCXfOatYVI1oo0tKu4,32120
|
59
60
|
dcicutils/task_utils.py,sha256=MF8ujmTD6-O2AC2gRGPHyGdUrVKgtr8epT5XU8WtNjk,8082
|
60
61
|
dcicutils/trace_utils.py,sha256=g8kwV4ebEy5kXW6oOrEAUsurBcCROvwtZqz9fczsGRE,1769
|
61
62
|
dcicutils/validation_utils.py,sha256=cMZIU2cY98FYtzK52z5WUYck7urH6JcqOuz9jkXpqzg,14797
|
62
63
|
dcicutils/variant_utils.py,sha256=2H9azNx3xAj-MySg-uZ2SFqbWs4kZvf61JnK6b-h4Qw,4343
|
63
64
|
dcicutils/zip_utils.py,sha256=0OXR0aLNwyLIZOzIFTM_5DOun7dxIv6TIZbFiithkO0,3276
|
64
|
-
dcicutils-8.4.0.
|
65
|
-
dcicutils-8.4.0.
|
66
|
-
dcicutils-8.4.0.
|
67
|
-
dcicutils-8.4.0.
|
68
|
-
dcicutils-8.4.0.
|
65
|
+
dcicutils-8.4.0.1b12.dist-info/LICENSE.txt,sha256=t0_-jIjqxNnymZoNJe-OltRIuuF8qfhN0ATlHyrUJPk,1102
|
66
|
+
dcicutils-8.4.0.1b12.dist-info/METADATA,sha256=f_Btb9g9ursc6-9DzbsrcbYRUMx4Te9rc2aotcDsphM,3315
|
67
|
+
dcicutils-8.4.0.1b12.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
|
68
|
+
dcicutils-8.4.0.1b12.dist-info/entry_points.txt,sha256=8wbw5csMIgBXhkwfgsgJeuFcoUc0WsucUxmOyml2aoA,209
|
69
|
+
dcicutils-8.4.0.1b12.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|