dcicutils 8.7.0.1b26__tar.gz → 8.7.0.1b27__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/PKG-INFO +1 -1
  2. dcicutils-8.7.0.1b27/dcicutils/portal_object_utils.py +105 -0
  3. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/portal_utils.py +47 -32
  4. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/pyproject.toml +1 -1
  5. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/LICENSE.txt +0 -0
  6. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/README.rst +0 -0
  7. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/__init__.py +0 -0
  8. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/base.py +0 -0
  9. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/beanstalk_utils.py +0 -0
  10. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/bundle_utils.py +0 -0
  11. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/cloudformation_utils.py +0 -0
  12. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/codebuild_utils.py +0 -0
  13. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/command_utils.py +0 -0
  14. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/common.py +0 -0
  15. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/contribution_scripts.py +0 -0
  16. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/contribution_utils.py +0 -0
  17. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/creds_utils.py +0 -0
  18. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/data_readers.py +0 -0
  19. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/data_utils.py +0 -0
  20. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/deployment_utils.py +0 -0
  21. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/diff_utils.py +0 -0
  22. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/docker_utils.py +0 -0
  23. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/ecr_scripts.py +0 -0
  24. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/ecr_utils.py +0 -0
  25. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/ecs_utils.py +0 -0
  26. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/env_base.py +0 -0
  27. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/env_manager.py +0 -0
  28. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/env_scripts.py +0 -0
  29. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/env_utils.py +0 -0
  30. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/env_utils_legacy.py +0 -0
  31. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/es_utils.py +0 -0
  32. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/exceptions.py +0 -0
  33. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/ff_mocks.py +0 -0
  34. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/ff_utils.py +0 -0
  35. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/function_cache_decorator.py +0 -0
  36. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/glacier_utils.py +0 -0
  37. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/jh_utils.py +0 -0
  38. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/kibana/dashboards.json +0 -0
  39. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/kibana/readme.md +0 -0
  40. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/lang_utils.py +0 -0
  41. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/license_policies/c4-infrastructure.jsonc +0 -0
  42. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/license_policies/c4-python-infrastructure.jsonc +0 -0
  43. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/license_policies/park-lab-common-server.jsonc +0 -0
  44. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/license_policies/park-lab-common.jsonc +0 -0
  45. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/license_policies/park-lab-gpl-pipeline.jsonc +0 -0
  46. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/license_policies/park-lab-pipeline.jsonc +0 -0
  47. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/license_utils.py +0 -0
  48. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/log_utils.py +0 -0
  49. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/misc_utils.py +0 -0
  50. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/obfuscation_utils.py +0 -0
  51. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/opensearch_utils.py +0 -0
  52. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/project_utils.py +0 -0
  53. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/qa_checkers.py +0 -0
  54. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/qa_utils.py +0 -0
  55. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/redis_tools.py +0 -0
  56. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/redis_utils.py +0 -0
  57. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/s3_utils.py +0 -0
  58. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/schema_utils.py +0 -0
  59. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/scripts/publish_to_pypi.py +0 -0
  60. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/scripts/run_license_checker.py +0 -0
  61. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/secrets_utils.py +0 -0
  62. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/sheet_utils.py +0 -0
  63. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/snapshot_utils.py +0 -0
  64. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/ssl_certificate_utils.py +0 -0
  65. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/structured_data.py +0 -0
  66. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/task_utils.py +0 -0
  67. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/tmpfile_utils.py +0 -0
  68. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/trace_utils.py +0 -0
  69. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/validation_utils.py +0 -0
  70. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/variant_utils.py +0 -0
  71. {dcicutils-8.7.0.1b26 → dcicutils-8.7.0.1b27}/dcicutils/zip_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dcicutils
3
- Version: 8.7.0.1b26
3
+ Version: 8.7.0.1b27
4
4
  Summary: Utility package for interacting with the 4DN Data Portal and other 4DN resources
5
5
  Home-page: https://github.com/4dn-dcic/utils
6
6
  License: MIT
@@ -0,0 +1,105 @@
1
+ from dcicutils.schema_utils import get_identifying_properties
2
+ from dcicutils.portal_utils import Portal
3
+ from functools import lru_cache
4
+ from typing import List, Optional, Tuple, Union
5
+
6
+
7
+ class PortalObject:
8
+
9
+ def __init__(self, portal: Portal, portal_object: dict, portal_object_type: Optional[str] = None) -> None:
10
+ self._portal = portal
11
+ self._object = portal_object
12
+ self._type = portal_object_type if isinstance(portal_object_type, str) and portal_object_type else None
13
+
14
+ @property
15
+ @lru_cache(maxsize=1)
16
+ def schema(self):
17
+ return self._portal.get_schema(self.schema_type)
18
+
19
+ @property
20
+ @lru_cache(maxsize=1)
21
+ def schema_type(self):
22
+ return self._type or Portal.get_schema_type(self._object)
23
+
24
+ @property
25
+ @lru_cache(maxsize=1)
26
+ def schema_types(self):
27
+ return self._type or Portal.get_schema_types(self._object)
28
+
29
+ @property
30
+ @lru_cache(maxsize=1)
31
+ def schema_identifying_properties(self) -> list:
32
+ if not (schema := self.schema):
33
+ return []
34
+ return get_identifying_properties(schema)
35
+
36
+ @property
37
+ @lru_cache(maxsize=1)
38
+ def identifying_properties(self) -> List[str]:
39
+ """
40
+ Returns the list of all identifying property names of this Portal object which actually have values.
41
+ Implicitly include "uuid" and "identifier" properties as identifying properties if they are actually
42
+ properties in the object schema, and favor these (first); defavor "aliases"; no other ordering defined.
43
+ """
44
+ identifying_properties = []
45
+ for identifying_property in self.schema_identifying_properties:
46
+ if identifying_property not in ["uuid", "identifier", "aliases"]:
47
+ if self._object.get(identifying_property):
48
+ identifying_properties.append(identifying_property)
49
+ if self._object.get("identifier"):
50
+ identifying_properties.insert(0, "identifier")
51
+ if self._object.get("uuid"):
52
+ identifying_properties.insert(0, "uuid")
53
+ if "aliases" in self.schema_identifying_properties and self._object.get("aliases"):
54
+ identifying_properties.append("aliases")
55
+ return identifying_properties
56
+
57
+ @property
58
+ @lru_cache(maxsize=1)
59
+ def identifying_paths(self) -> List[str]:
60
+ """
61
+ Returns a list of the possible Portal URL paths identifying this Portal object.
62
+ """
63
+ if not (identifying_properties := self.identifying_properties):
64
+ return []
65
+ identifying_paths = []
66
+ for identifying_property in identifying_properties:
67
+ if (identifying_value := self._object.get(identifying_property)):
68
+ if identifying_property == "uuid":
69
+ identifying_paths.append(f"/{identifying_value}")
70
+ # For now at least we include the path both with and without the schema type component
71
+ # as for some identifying values it works (only) with and some it works (only) without.
72
+ # For example: If we have FileSet with "accession", an identifying property, with value
73
+ # SMAFSFXF1RO4 then /SMAFSFXF1RO4 works but /FileSet/SMAFSFXF1RO4 does not; and
74
+ # conversely using "submitted_id", also an identifying property, with value
75
+ # UW_FILE-SET_COLO-829BL_HI-C_1 then /UW_FILE-SET_COLO-829BL_HI-C_1 does
76
+ # not work but /FileSet/UW_FILE-SET_COLO-829BL_HI-C_1 does work.
77
+ elif isinstance(identifying_value, list):
78
+ for identifying_value_item in identifying_value:
79
+ identifying_paths.append(f"/{self.schema_type}/{identifying_value_item}")
80
+ identifying_paths.append(f"/{identifying_value_item}")
81
+ else:
82
+ identifying_paths.append(f"/{self.schema_type}/{identifying_value}")
83
+ identifying_paths.append(f"/{identifying_value}")
84
+ return identifying_paths
85
+
86
+ @property
87
+ @lru_cache(maxsize=1)
88
+ def identifying_path(self) -> Optional[str]:
89
+ if identifying_paths := self.identifying_paths:
90
+ return identifying_paths[0]
91
+
92
+ def lookup(self, include_identifying_path: bool = False) -> Optional[Union[Tuple[dict, str], dict]]:
93
+ return self._lookup() if include_identifying_path else self._lookup()[0]
94
+
95
+ def lookup_identifying_path(self) -> Optional[str]:
96
+ return self._lookup()[1]
97
+
98
+ def _lookup(self) -> Tuple[Optional[dict], Optional[str]]:
99
+ try:
100
+ for identifying_path in self.identifying_paths:
101
+ if (value := self._portal.get(identifying_path)) and (value.status_code == 200):
102
+ return value.json(), identifying_path
103
+ except Exception:
104
+ pass
105
+ return None, self.identifying_path
@@ -211,8 +211,9 @@ class Portal:
211
211
  def vapp(self) -> Optional[TestApp]:
212
212
  return self._vapp
213
213
 
214
- def get(self, url: str, follow: bool = True, raise_for_status: bool = False, **kwargs) -> OptionalResponse:
215
- url = self.url(url)
214
+ def get(self, url: str, follow: bool = True,
215
+ raw: bool = False, database: bool = False, raise_for_status: bool = False, **kwargs) -> OptionalResponse:
216
+ url = self.url(url, raw, database)
216
217
  if not self.vapp:
217
218
  response = requests.get(url, allow_redirects=follow, **self._kwargs(**kwargs))
218
219
  else:
@@ -254,8 +255,14 @@ class Portal:
254
255
  response.raise_for_status()
255
256
  return response
256
257
 
257
- def get_metadata(self, object_id: str) -> Optional[dict]:
258
- return get_metadata(obj_id=object_id, vapp=self.vapp, key=self.key)
258
+ def get_metadata(self, object_id: str, raw: bool = False, database: bool = False) -> Optional[dict]:
259
+ if isinstance(raw, bool) and raw:
260
+ add_on = "frame=raw" + ("&datastore=database" if isinstance(database, bool) and database else "")
261
+ elif database:
262
+ add_on = "datastore=database"
263
+ else:
264
+ add_on = ""
265
+ return get_metadata(obj_id=object_id, vapp=self.vapp, key=self.key, add_on=add_on)
259
266
 
260
267
  def patch_metadata(self, object_id: str, data: str) -> Optional[dict]:
261
268
  if self.key:
@@ -287,20 +294,20 @@ class Portal:
287
294
  def schema_name(name: str) -> str:
288
295
  return to_camel_case(name.replace(" ", "") if not name.endswith(".json") else name[:-5])
289
296
 
290
- def is_schema_type(self, schema_name_or_object: Union[str, dict], target_schema_name: str,
297
+ def is_schema_type(self, schema_name_or_portal_object: Union[str, dict], target_schema_name: str,
291
298
  _schemas_super_type_map: Optional[list] = None) -> bool:
292
299
  """
293
- If the given (first) schema_name_or_object argument is a string then returns True iff the
294
- given schema (type) name isa type of the given target schema (type) name, i.e. the given
300
+ If the given (first) schema_name_or_portal_object argument is a string then returns True iff
301
+ the given schema (type) name isa type of the given target schema (type) name, i.e. the given
295
302
  schema type is the given target schema type or has an ancestor which is that type.
296
- If the given (first) schema_name_or_object argument is a dictionary then
303
+ If the given (first) schema_name_or_portal_object argument is a dictionary then
297
304
  returns True iff this object value isa type of the given target schema type.
298
305
  """
299
- if isinstance(schema_name_or_object, dict):
300
- return self.isinstance_schema(schema_name_or_object, target_schema_name)
301
- elif not isinstance(schema_name_or_object, str) or not schema_name_or_object:
306
+ if isinstance(schema_name_or_portal_object, dict):
307
+ return self.isinstance_schema(schema_name_or_portal_object, target_schema_name)
308
+ elif not isinstance(schema_name_or_portal_object, str) or not schema_name_or_portal_object:
302
309
  return False
303
- schema_name = self.schema_name(schema_name_or_object).lower()
310
+ schema_name = self.schema_name(schema_name_or_portal_object).lower()
304
311
  target_schema_name = self.schema_name(target_schema_name).lower()
305
312
  if schema_name == target_schema_name:
306
313
  return True
@@ -312,31 +319,34 @@ class Portal:
312
319
  return True
313
320
  return False
314
321
 
315
- def isinstance_schema(self, value: dict, target_schema_name: str) -> bool:
322
+ def isinstance_schema(self, portal_object: dict, target_schema_name: str) -> bool:
316
323
  """
317
324
  Returns True iff the given object isa type of the given schema type.
318
325
  """
319
- if isinstance(value, dict):
320
- if isinstance(value_types := value.get("@type"), str):
321
- value_types = [value_types]
326
+ if value_types := self.get_schema_types(portal_object):
327
+ schemas_super_type_map = self.get_schemas_super_type_map()
328
+ for value_type in value_types:
329
+ if self.is_schema_type(value_type, target_schema_name, schemas_super_type_map):
330
+ return True
331
+ return False
332
+
333
+ @staticmethod
334
+ def get_schema_types(portal_object: dict) -> Optional[List[str]]:
335
+ if isinstance(portal_object, dict):
336
+ if isinstance(value_types := portal_object.get("@type"), str):
337
+ value_types = [value_types] if value_types else []
322
338
  elif not isinstance(value_types, list):
323
339
  value_types = []
324
- if isinstance(data_type := value.get("data_type"), list):
340
+ if isinstance(data_type := portal_object.get("data_type"), list):
325
341
  value_types.extend(data_type)
326
342
  elif isinstance(data_type, str):
327
343
  value_types.append(data_type)
328
- if value_types:
329
- schemas_super_type_map = self.get_schemas_super_type_map()
330
- for value_type in value_types:
331
- if self.is_schema_type(value_type, target_schema_name, schemas_super_type_map):
332
- return True
333
- return False
344
+ return value_types if value_types else None
334
345
 
335
- def get_schema_type(self, value: dict) -> Optional[str]:
336
- value_type = value.get("@type", value.get("data_type")) if isinstance(value, dict) else None
337
- if isinstance(value_type, list):
338
- value_type = value_type[0] if value_type else None
339
- return value_type
346
+ @staticmethod
347
+ def get_schema_type(portal_object: dict) -> Optional[str]:
348
+ if value_types := Portal.get_schema_types(portal_object):
349
+ return value_types[0]
340
350
 
341
351
  @lru_cache(maxsize=1)
342
352
  def get_schemas_super_type_map(self) -> dict:
@@ -371,14 +381,19 @@ class Portal:
371
381
  super_type_map_flattened[super_type_name] = list_breadth_first(super_type_map, super_type_name)
372
382
  return super_type_map_flattened
373
383
 
374
- def url(self, url: str) -> str:
384
+ def url(self, url: str, raw: bool = False, database: bool = False) -> str:
375
385
  if not isinstance(url, str) or not url:
376
386
  return "/"
377
- if (lowercase_url := url.lower()).startswith("http://") or lowercase_url.startswith("https://"):
387
+ elif (lowercase_url := url.lower()).startswith("http://") or lowercase_url.startswith("https://"):
378
388
  return url
379
- if not (url := re.sub(r"/+", "/", url)).startswith("/"):
389
+ elif not (url := re.sub(r"/+", "/", url)).startswith("/"):
380
390
  url = "/"
381
- return self.server + url if self.server else url
391
+ url = self.server + url if self.server else url
392
+ if isinstance(raw, bool) and raw:
393
+ url += ("&" if "?" in url else "?") + "frame=raw"
394
+ if isinstance(database, bool) and database:
395
+ url += ("&" if "?" in url else "?") + "datastore=database"
396
+ return url
382
397
 
383
398
  def _kwargs(self, **kwargs) -> dict:
384
399
  if "headers" in kwargs:
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "dcicutils"
3
- version = "8.7.0.1b26" # TODO: To become 8.7.1
3
+ version = "8.7.0.1b27" # TODO: To become 8.7.1
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"