dcicutils 8.8.4.1b36__tar.gz → 8.8.4.1b39__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/PKG-INFO +1 -1
  2. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/scripts/view_portal_object.py +87 -5
  3. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/structured_data.py +12 -1
  4. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/pyproject.toml +1 -1
  5. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/LICENSE.txt +0 -0
  6. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/README.rst +0 -0
  7. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/__init__.py +0 -0
  8. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/base.py +0 -0
  9. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/beanstalk_utils.py +0 -0
  10. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/bundle_utils.py +0 -0
  11. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/captured_output.py +0 -0
  12. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/cloudformation_utils.py +0 -0
  13. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/codebuild_utils.py +0 -0
  14. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/command_utils.py +0 -0
  15. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/common.py +0 -0
  16. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/contribution_scripts.py +0 -0
  17. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/contribution_utils.py +0 -0
  18. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/creds_utils.py +0 -0
  19. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/data_readers.py +0 -0
  20. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/data_utils.py +0 -0
  21. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/datetime_utils.py +0 -0
  22. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/deployment_utils.py +0 -0
  23. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/diff_utils.py +0 -0
  24. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/docker_utils.py +0 -0
  25. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/ecr_scripts.py +0 -0
  26. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/ecr_utils.py +0 -0
  27. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/ecs_utils.py +0 -0
  28. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/env_base.py +0 -0
  29. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/env_manager.py +0 -0
  30. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/env_scripts.py +0 -0
  31. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/env_utils.py +0 -0
  32. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/env_utils_legacy.py +0 -0
  33. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/es_utils.py +0 -0
  34. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/exceptions.py +0 -0
  35. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/ff_mocks.py +0 -0
  36. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/ff_utils.py +0 -0
  37. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/file_utils.py +0 -0
  38. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/function_cache_decorator.py +0 -0
  39. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/glacier_utils.py +0 -0
  40. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/http_utils.py +0 -0
  41. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/jh_utils.py +0 -0
  42. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/kibana/dashboards.json +0 -0
  43. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/kibana/readme.md +0 -0
  44. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/lang_utils.py +0 -0
  45. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/license_policies/c4-infrastructure.jsonc +0 -0
  46. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/license_policies/c4-python-infrastructure.jsonc +0 -0
  47. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/license_policies/park-lab-common-server.jsonc +0 -0
  48. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/license_policies/park-lab-common.jsonc +0 -0
  49. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/license_policies/park-lab-gpl-pipeline.jsonc +0 -0
  50. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/license_policies/park-lab-pipeline.jsonc +0 -0
  51. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/license_utils.py +0 -0
  52. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/log_utils.py +0 -0
  53. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/misc_utils.py +0 -0
  54. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/obfuscation_utils.py +0 -0
  55. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/opensearch_utils.py +0 -0
  56. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/portal_object_utils.py +0 -0
  57. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/portal_utils.py +0 -0
  58. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/progress_bar.py +0 -0
  59. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/project_utils.py +0 -0
  60. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/qa_checkers.py +0 -0
  61. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/qa_utils.py +0 -0
  62. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/redis_tools.py +0 -0
  63. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/redis_utils.py +0 -0
  64. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/s3_utils.py +0 -0
  65. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/schema_utils.py +0 -0
  66. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/scripts/publish_to_pypi.py +0 -0
  67. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/scripts/run_license_checker.py +0 -0
  68. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/secrets_utils.py +0 -0
  69. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/sheet_utils.py +0 -0
  70. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/snapshot_utils.py +0 -0
  71. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/ssl_certificate_utils.py +0 -0
  72. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/submitr/progress_constants.py +0 -0
  73. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/submitr/ref_lookup_strategy.py +0 -0
  74. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/task_utils.py +0 -0
  75. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/tmpfile_utils.py +0 -0
  76. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/trace_utils.py +0 -0
  77. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/validation_utils.py +0 -0
  78. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/variant_utils.py +0 -0
  79. {dcicutils-8.8.4.1b36 → dcicutils-8.8.4.1b39}/dcicutils/zip_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dcicutils
3
- Version: 8.8.4.1b36
3
+ Version: 8.8.4.1b39
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
@@ -57,6 +57,7 @@
57
57
 
58
58
  import argparse
59
59
  from functools import lru_cache
60
+ import io
60
61
  import json
61
62
  import pyperclip
62
63
  import os
@@ -97,11 +98,18 @@ def main():
97
98
  help="Include all properties for schema usage.")
98
99
  parser.add_argument("--raw", action="store_true", required=False, default=False, help="Raw output.")
99
100
  parser.add_argument("--tree", action="store_true", required=False, default=False, help="Tree output for schemas.")
101
+ parser.add_argument("--post", type=str, required=False, default=None,
102
+ help="POST data of the main arg type with data from file specified with this option.")
103
+ parser.add_argument("--patch", type=str, required=False, default=None,
104
+ help="PATCH data of the main arg type with data from file specified with this option.")
100
105
  parser.add_argument("--database", action="store_true", required=False, default=False,
101
106
  help="Read from database output.")
107
+ parser.add_argument("--bool", action="store_true", required=False,
108
+ default=False, help="Only return whether found or not.")
102
109
  parser.add_argument("--yaml", action="store_true", required=False, default=False, help="YAML output.")
103
110
  parser.add_argument("--copy", "-c", action="store_true", required=False, default=False,
104
111
  help="Copy object data to clipboard.")
112
+ parser.add_argument("--indent", required=False, default=False, help="Indent output.", type=int)
105
113
  parser.add_argument("--details", action="store_true", required=False, default=False, help="Detailed output.")
106
114
  parser.add_argument("--more-details", action="store_true", required=False, default=False,
107
115
  help="More detailed output.")
@@ -151,6 +159,18 @@ def main():
151
159
  args.schema = True
152
160
 
153
161
  if args.schema:
162
+ if args.post:
163
+ if post_data := _read_json_from_file(args.post):
164
+ if args.verbose:
165
+ _print(f"POSTing data from file ({args.post}) as type: {args.uuid}")
166
+ if isinstance(post_data, dict):
167
+ post_data = [post_data]
168
+ elif not isinstance(post_data, list):
169
+ _print(f"POST data neither list nor dictionary: {args.post}")
170
+ for item in post_data:
171
+ portal.post_metadata(args.uuid, item)
172
+ if args.verbose:
173
+ _print(f"Done POSTing data from file ({args.post}) as type: {args.uuid}")
154
174
  schema, schema_name = _get_schema(portal, args.uuid)
155
175
  if schema:
156
176
  if args.copy:
@@ -166,14 +186,50 @@ def main():
166
186
  _print_schema(schema, details=args.details, more_details=args.details,
167
187
  all=args.all, raw=args.raw, raw_yaml=args.yaml)
168
188
  return
169
-
170
- data = _get_portal_object(portal=portal, uuid=args.uuid, raw=args.raw, database=args.database, verbose=args.verbose)
189
+ elif args.patch:
190
+ if patch_data := _read_json_from_file(args.patch):
191
+ if args.verbose:
192
+ _print(f"PATCHing data from file ({args.patch}) for object: {args.uuid}")
193
+ if isinstance(patch_data, dict):
194
+ patch_data = [patch_data]
195
+ elif not isinstance(patch_data, list):
196
+ _print(f"PATCH data neither list nor dictionary: {args.patch}")
197
+ for item in patch_data:
198
+ portal.patch_metadata(args.uuid, item)
199
+ if args.verbose:
200
+ _print(f"Done PATCHing data from file ({args.patch}) as type: {args.uuid}")
201
+ return
202
+ else:
203
+ _print(f"No PATCH data found in file: {args.patch}")
204
+ exit(1)
205
+
206
+ data = _get_portal_object(portal=portal, uuid=args.uuid, raw=args.raw,
207
+ database=args.database, check=args.bool, verbose=args.verbose)
208
+ if args.bool:
209
+ if data:
210
+ _print(f"{args.uuid}: found")
211
+ exit(0)
212
+ else:
213
+ _print(f"{args.uuid}: not found")
214
+ exit(1)
171
215
  if args.copy:
172
216
  pyperclip.copy(json.dumps(data, indent=4))
173
217
  if args.yaml:
174
218
  _print(yaml.dump(data))
175
219
  else:
176
- _print(json.dumps(data, default=str, indent=4))
220
+ if args.indent > 0:
221
+ _print(_format_json_with_indent(data, indent=args.indent))
222
+ else:
223
+ _print(json.dumps(data, default=str, indent=4))
224
+
225
+
226
+ def _format_json_with_indent(value: dict, indent: int = 0) -> Optional[str]:
227
+ if isinstance(value, dict):
228
+ result = json.dumps(value, indent=4)
229
+ if indent > 0:
230
+ result = f"{indent * ' '}{result}"
231
+ result = result.replace("\n", f"\n{indent * ' '}")
232
+ return result
177
233
 
178
234
 
179
235
  def _create_portal(ini: str, env: Optional[str] = None,
@@ -198,7 +254,8 @@ def _create_portal(ini: str, env: Optional[str] = None,
198
254
 
199
255
 
200
256
  def _get_portal_object(portal: Portal, uuid: str,
201
- raw: bool = False, database: bool = False, verbose: bool = False) -> dict:
257
+ raw: bool = False, database: bool = False,
258
+ check: bool = False, verbose: bool = False) -> dict:
202
259
  response = None
203
260
  try:
204
261
  if not uuid.startswith("/"):
@@ -212,13 +269,18 @@ def _get_portal_object(portal: Portal, uuid: str,
212
269
  _exit()
213
270
  _exit(f"Exception getting Portal object from {portal.server}: {uuid}\n{get_error_message(e)}")
214
271
  if not response:
272
+ if check:
273
+ return None
215
274
  _exit(f"Null response getting Portal object from {portal.server}: {uuid}")
216
275
  if response.status_code not in [200, 307]:
217
276
  # TODO: Understand why the /me endpoint returns HTTP status code 307, which is only why we mention it above.
218
277
  _exit(f"Invalid status code ({response.status_code}) getting Portal object from {portal.server}: {uuid}")
219
278
  if not response.json:
220
279
  _exit(f"Invalid JSON getting Portal object: {uuid}")
221
- return response.json()
280
+ response = response.json()
281
+ if raw:
282
+ response.pop("schema_version", None)
283
+ return response
222
284
 
223
285
 
224
286
  @lru_cache(maxsize=1)
@@ -257,6 +319,7 @@ def _print_schema_info(schema: dict, level: int = 0,
257
319
  required: Optional[List[str]] = None) -> None:
258
320
  if not schema or not isinstance(schema, dict):
259
321
  return
322
+ identifying_properties = schema.get("identifyingProperties")
260
323
  if level == 0:
261
324
  if required_properties := schema.get("required"):
262
325
  _print("- required properties:")
@@ -383,6 +446,8 @@ def _print_schema_info(schema: dict, level: int = 0,
383
446
  suffix += f" | enum"
384
447
  if property_required:
385
448
  suffix += f" | required"
449
+ if property_name in (identifying_properties or []):
450
+ suffix += f" | identifying"
386
451
  if property.get("uniqueKey"):
387
452
  suffix += f" | unique"
388
453
  if pattern := property.get("pattern"):
@@ -529,6 +594,23 @@ def _print_tree(root_name: Optional[str],
529
594
  print(line)
530
595
 
531
596
 
597
+ def _read_json_from_file(file: str) -> Optional[dict]:
598
+ if not os.path.exists(file):
599
+ _print(f"Cannot find file: {file}")
600
+ exit(1)
601
+ try:
602
+ with io.open(file, "r") as f:
603
+ try:
604
+ return json.load(f)
605
+ except Exception:
606
+ _print(f"Cannot parse JSON in file: {file}")
607
+ exit(1)
608
+ except Exception as e:
609
+ print(e)
610
+ _print(f"Cannot open file: {file}")
611
+ exit(1)
612
+
613
+
532
614
  def _print(*args, **kwargs):
533
615
  with uncaptured_output():
534
616
  PRINT(*args, **kwargs)
@@ -350,7 +350,18 @@ class StructuredDataSet:
350
350
 
351
351
  def _load_json_file(self, file: str) -> None:
352
352
  with open(file) as f:
353
- self._add(Schema.type_name(file), json.load(f))
353
+ file_json = json.load(f)
354
+ schema_inferred_from_file_name = Schema.type_name(file)
355
+ if self._portal.get_schema(schema_inferred_from_file_name) is not None:
356
+ # If the JSON file name looks like a schema name then assume it
357
+ # contains an object or an array of object of that schema type.
358
+ self._add(Schema.type_name(file), file_json)
359
+ elif isinstance(file_json, dict):
360
+ # Otherwise if the JSON file name does not look like a schema name then
361
+ # assume it a dictionary where each property is the name of a schema, and
362
+ # which (each property) contains a list of object of that schema type.
363
+ for schema_name in file_json:
364
+ self._add(schema_name, file_json[schema_name])
354
365
 
355
366
  def _load_reader(self, reader: RowReader, type_name: str) -> None:
356
367
  schema = None
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "dcicutils"
3
- version = "8.8.4.1b36" # TODO: To become 8.8.5
3
+ version = "8.8.4.1b39" # TODO: To become 8.8.5
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"