dcicutils 8.7.2.1b2__py3-none-any.whl → 8.7.2.1b3__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/captured_output.py +70 -0
- dcicutils/data_readers.py +3 -2
- dcicutils/datetime_utils.py +1 -1
- dcicutils/scripts/view_portal_object.py +155 -0
- {dcicutils-8.7.2.1b2.dist-info → dcicutils-8.7.2.1b3.dist-info}/METADATA +2 -1
- {dcicutils-8.7.2.1b2.dist-info → dcicutils-8.7.2.1b3.dist-info}/RECORD +9 -7
- {dcicutils-8.7.2.1b2.dist-info → dcicutils-8.7.2.1b3.dist-info}/entry_points.txt +1 -0
- {dcicutils-8.7.2.1b2.dist-info → dcicutils-8.7.2.1b3.dist-info}/LICENSE.txt +0 -0
- {dcicutils-8.7.2.1b2.dist-info → dcicutils-8.7.2.1b3.dist-info}/WHEEL +0 -0
@@ -0,0 +1,70 @@
|
|
1
|
+
from collections import namedtuple
|
2
|
+
from contextlib import contextmanager
|
3
|
+
import io
|
4
|
+
import sys
|
5
|
+
from typing import Optional
|
6
|
+
|
7
|
+
_real_stdout = sys.stdout
|
8
|
+
_real_stderr = sys.stderr
|
9
|
+
|
10
|
+
|
11
|
+
@contextmanager
|
12
|
+
def captured_output(capture: bool = True):
|
13
|
+
"""
|
14
|
+
Context manager to capture any/all output to stdout or stderr, and not actually output it to stdout
|
15
|
+
or stderr. Yields and object with a get_captured_output() method to get the output captured thus far,
|
16
|
+
and another uncaptured_print() method to actually print the given output to stdout, even though output
|
17
|
+
to stdout is being captured. Can be useful, for example, in creating command-line scripts which invoke
|
18
|
+
code which outputs a lot of info, warning, error, etc to stdout or stderr, and we want to suprress that
|
19
|
+
output; but with the yielded uncaptured_print() method output specific to the script can actually be
|
20
|
+
output (to stdout); and/or can also optionally output any/all captured output, e.g. for debugging or
|
21
|
+
troubleshooting purposes. Disable this capture, without having to restructure your code WRT the usage
|
22
|
+
of the with-clause with this context manager, pass False as an argument to this context manager.
|
23
|
+
"""
|
24
|
+
|
25
|
+
original_stdout = _real_stdout
|
26
|
+
original_stderr = _real_stderr
|
27
|
+
captured_output = io.StringIO()
|
28
|
+
|
29
|
+
def set_original_output() -> None:
|
30
|
+
sys.stdout = original_stdout
|
31
|
+
sys.stderr = original_stderr
|
32
|
+
|
33
|
+
def set_captured_output() -> None:
|
34
|
+
if capture:
|
35
|
+
sys.stdout = captured_output
|
36
|
+
sys.stderr = captured_output
|
37
|
+
|
38
|
+
def uncaptured_print(*args, **kwargs) -> None:
|
39
|
+
set_original_output()
|
40
|
+
print(*args, **kwargs)
|
41
|
+
set_captured_output()
|
42
|
+
|
43
|
+
def uncaptured_input(message: str) -> str:
|
44
|
+
set_original_output()
|
45
|
+
value = input(message)
|
46
|
+
set_captured_output()
|
47
|
+
return value
|
48
|
+
|
49
|
+
def get_captured_output() -> Optional[str]:
|
50
|
+
return captured_output.getvalue() if capture else None
|
51
|
+
|
52
|
+
try:
|
53
|
+
set_captured_output()
|
54
|
+
Result = namedtuple("Result", ["get_captured_output", "uncaptured_print", "uncaptured_input"])
|
55
|
+
yield Result(get_captured_output, uncaptured_print, uncaptured_input)
|
56
|
+
finally:
|
57
|
+
set_original_output()
|
58
|
+
|
59
|
+
|
60
|
+
@contextmanager
|
61
|
+
def uncaptured_output():
|
62
|
+
original_stdout = sys.stdout
|
63
|
+
original_stderr = sys.stderr
|
64
|
+
sys.stdout = _real_stdout
|
65
|
+
sys.stderr = _real_stderr
|
66
|
+
try:
|
67
|
+
yield
|
68
|
+
finally:
|
69
|
+
sys.stdout = original_stdout
|
70
|
+
sys.stderr = original_stderr
|
dcicutils/data_readers.py
CHANGED
@@ -152,9 +152,10 @@ class ExcelSheetReader(RowReader):
|
|
152
152
|
|
153
153
|
class Excel:
|
154
154
|
|
155
|
-
def __init__(self, file: str, reader_class: Optional[Type] = None) -> None:
|
155
|
+
def __init__(self, file: str, reader_class: Optional[Type] = None, include_hidden_sheets: bool = False) -> None:
|
156
156
|
self._file = file
|
157
157
|
self._workbook = None
|
158
|
+
self._include_hidden_sheets = include_hidden_sheets
|
158
159
|
self.sheet_names = None
|
159
160
|
if isinstance(reader_class, Type) and issubclass(reader_class, ExcelSheetReader):
|
160
161
|
self._reader_class = reader_class
|
@@ -169,7 +170,7 @@ class Excel:
|
|
169
170
|
if self._workbook is None:
|
170
171
|
self._workbook = openpyxl.load_workbook(self._file, data_only=True)
|
171
172
|
self.sheet_names = [sheet_name for sheet_name in self._workbook.sheetnames
|
172
|
-
if self._workbook[sheet_name].sheet_state != "hidden"]
|
173
|
+
if self._include_hidden_sheets or (self._workbook[sheet_name].sheet_state != "hidden")]
|
173
174
|
|
174
175
|
def __del__(self) -> None:
|
175
176
|
if (workbook := self._workbook) is not None:
|
dcicutils/datetime_utils.py
CHANGED
@@ -65,7 +65,7 @@ def normalize_datetime_string(value: str) -> Optional[str]:
|
|
65
65
|
or if ill-formated then returns None. The given string is assumed to be in the format "YYYY-MM-DD hh:mm:ss"
|
66
66
|
and with an optional timezone suffix in format "+hh:mm" or "+hh". Also allowed is just a date of the
|
67
67
|
format "YYYY-MM-DD" in which case a time of "00:00:00" is assumed. If no timezone is specified then
|
68
|
-
the local timezone is assumed.
|
68
|
+
the local timezone is assumed. The returned format looks like this: "2024-02-08T10:37:51-05:00"
|
69
69
|
"""
|
70
70
|
dt = parse_datetime_string(value)
|
71
71
|
return dt.isoformat() if dt else None
|
@@ -0,0 +1,155 @@
|
|
1
|
+
# ------------------------------------------------------------------------------------------------------
|
2
|
+
# Command-line utility to retrieve and print the given object (UUID) from a SMaHT/CGAP/Fourfront Portal.
|
3
|
+
# ------------------------------------------------------------------------------------------------------
|
4
|
+
# Example command:
|
5
|
+
# view-portal-object 4483b19d-62e7-4e7f-a211-0395343a35df --yaml
|
6
|
+
#
|
7
|
+
# Example output:
|
8
|
+
# '@context': /terms/
|
9
|
+
# '@id': /access-keys/3968e38e-c11f-472e-8531-8650e2e296d4/
|
10
|
+
# '@type':
|
11
|
+
# - AccessKey
|
12
|
+
# - Item
|
13
|
+
# access_key_id: NSVCZ75O
|
14
|
+
# date_created: '2023-09-06T13:11:59.704005+00:00'
|
15
|
+
# description: Manually generated local access-key for testing.
|
16
|
+
# display_title: AccessKey from 2023-09-06
|
17
|
+
# expiration_date: '2023-12-05T13:11:59.714106'
|
18
|
+
# last_modified:
|
19
|
+
# date_modified: '2023-09-06T13:11:59.711367+00:00'
|
20
|
+
# modified_by:
|
21
|
+
# '@id': /users/3202fd57-44d2-44fb-a131-afb1e43d8ae5/
|
22
|
+
# '@type':
|
23
|
+
# - User
|
24
|
+
# - Item
|
25
|
+
# status: current
|
26
|
+
# uuid: 3202fd57-44d2-44fb-a131-afb1e43d8ae5
|
27
|
+
# principals_allowed:
|
28
|
+
# edit:
|
29
|
+
# - group.admin
|
30
|
+
# - userid.74fef71a-dfc1-4aa4-acc0-cedcb7ac1d68
|
31
|
+
# view:
|
32
|
+
# - group.admin
|
33
|
+
# - group.read-only-admin
|
34
|
+
# - userid.74fef71a-dfc1-4aa4-acc0-cedcb7ac1d68
|
35
|
+
# schema_version: '1'
|
36
|
+
# status: current
|
37
|
+
# user:
|
38
|
+
# '@id': /users/74fef71a-dfc1-4aa4-acc0-cedcb7ac1d68/
|
39
|
+
# '@type':
|
40
|
+
# - User
|
41
|
+
# - Item
|
42
|
+
# display_title: David Michaels
|
43
|
+
# principals_allowed:
|
44
|
+
# edit:
|
45
|
+
# - group.admin
|
46
|
+
# view:
|
47
|
+
# - group.admin
|
48
|
+
# - group.read-only-admin
|
49
|
+
# status: current
|
50
|
+
# uuid: 74fef71a-dfc1-4aa4-acc0-cedcb7ac1d68
|
51
|
+
# uuid: 3968e38e-c11f-472e-8531-8650e2e296d4
|
52
|
+
#
|
53
|
+
# Note that instead of a uuid you can also actually use a path, for example:
|
54
|
+
# view-local-object /file-formats/vcf_gz_tbi
|
55
|
+
#
|
56
|
+
# --------------------------------------------------------------------------------------------------
|
57
|
+
|
58
|
+
import argparse
|
59
|
+
import json
|
60
|
+
import pyperclip
|
61
|
+
import sys
|
62
|
+
from typing import Optional
|
63
|
+
import yaml
|
64
|
+
from dcicutils.misc_utils import get_error_message
|
65
|
+
from dcicutils.portal_utils import Portal
|
66
|
+
from dcicutils.captured_output import captured_output, uncaptured_output
|
67
|
+
|
68
|
+
|
69
|
+
def main():
|
70
|
+
|
71
|
+
parser = argparse.ArgumentParser(description="View Portal object.")
|
72
|
+
parser.add_argument("uuid", type=str,
|
73
|
+
help=f"The uuid (or path) of the object to fetch and view. ")
|
74
|
+
parser.add_argument("--ini", type=str, required=False, default=None,
|
75
|
+
help=f"Name of the application .ini file.")
|
76
|
+
parser.add_argument("--env", "-e", type=str, required=False, default=None,
|
77
|
+
help=f"Environment name (key from ~/.smaht-keys.json).")
|
78
|
+
parser.add_argument("--server", "-s", type=str, required=False, default=None,
|
79
|
+
help=f"Environment server name (server from key in ~/.smaht-keys.json).")
|
80
|
+
parser.add_argument("--app", type=str, required=False, default=None,
|
81
|
+
help=f"Application name (one of: smaht, cgap, fourfront).")
|
82
|
+
parser.add_argument("--raw", action="store_true", required=False, default=False, help="Raw output.")
|
83
|
+
parser.add_argument("--database", action="store_true", required=False, default=False,
|
84
|
+
help="Read from database output.")
|
85
|
+
parser.add_argument("--yaml", action="store_true", required=False, default=False, help="YAML output.")
|
86
|
+
parser.add_argument("--copy", "-c", action="store_true", required=False, default=False,
|
87
|
+
help="Copy object data to clipboard.")
|
88
|
+
parser.add_argument("--verbose", action="store_true", required=False, default=False, help="Verbose output.")
|
89
|
+
parser.add_argument("--debug", action="store_true", required=False, default=False, help="Debugging output.")
|
90
|
+
args = parser.parse_args()
|
91
|
+
|
92
|
+
portal = _create_portal(ini=args.ini, env=args.env, server=args.server, app=args.app, debug=args.debug)
|
93
|
+
data = _get_portal_object(portal=portal, uuid=args.uuid, raw=args.raw, database=args.database, verbose=args.verbose)
|
94
|
+
|
95
|
+
if args.copy:
|
96
|
+
pyperclip.copy(json.dumps(data, indent=4))
|
97
|
+
if args.yaml:
|
98
|
+
_print(yaml.dump(data))
|
99
|
+
else:
|
100
|
+
_print(json.dumps(data, default=str, indent=4))
|
101
|
+
|
102
|
+
|
103
|
+
def _create_portal(ini: str, env: Optional[str] = None,
|
104
|
+
server: Optional[str] = None, app: Optional[str] = None, debug: bool = False) -> Portal:
|
105
|
+
with captured_output(not debug):
|
106
|
+
return Portal(env, server=server, app=app) if env or app else Portal(ini)
|
107
|
+
|
108
|
+
|
109
|
+
def _get_portal_object(portal: Portal, uuid: str,
|
110
|
+
raw: bool = False, database: bool = False, verbose: bool = False) -> dict:
|
111
|
+
if verbose:
|
112
|
+
_print(f"Getting object ({uuid}) from portal ... ", end="")
|
113
|
+
response = None
|
114
|
+
try:
|
115
|
+
if not uuid.startswith("/"):
|
116
|
+
path = f"/{uuid}"
|
117
|
+
else:
|
118
|
+
path = uuid
|
119
|
+
response = portal.get(path, raw=raw, database=database)
|
120
|
+
except Exception as e:
|
121
|
+
if "404" in str(e) and "not found" in str(e).lower():
|
122
|
+
if verbose:
|
123
|
+
_print("Not found!")
|
124
|
+
else:
|
125
|
+
_print(f"Object ({uuid}) not found!")
|
126
|
+
_exit_without_action()
|
127
|
+
_exit_without_action(f"Exception getting object ({uuid}) -> {get_error_message(e)}", newline=verbose)
|
128
|
+
if not response:
|
129
|
+
_exit_without_action(f"Null response getting object {uuid}).")
|
130
|
+
if response.status_code not in [200, 307]:
|
131
|
+
# TODO: Understand why the /me endpoint returns HTTP status code 307, which is only why we mention it above.
|
132
|
+
_exit_without_action(f"Invalid status code ({response.status_code}) getting object: {uuid}")
|
133
|
+
if not response.json:
|
134
|
+
_exit_without_action(f"Invalid JSON getting object {uuid}).")
|
135
|
+
if verbose:
|
136
|
+
_print("OK")
|
137
|
+
return response.json()
|
138
|
+
|
139
|
+
|
140
|
+
def _print(*args, **kwargs):
|
141
|
+
with uncaptured_output():
|
142
|
+
print(*args, **kwargs)
|
143
|
+
sys.stdout.flush()
|
144
|
+
|
145
|
+
|
146
|
+
def _exit_without_action(message: Optional[str] = None, newline: bool = True) -> None:
|
147
|
+
if message:
|
148
|
+
if newline:
|
149
|
+
_print()
|
150
|
+
_print(f"ERROR: {message}")
|
151
|
+
exit(1)
|
152
|
+
|
153
|
+
|
154
|
+
if __name__ == "__main__":
|
155
|
+
main()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: dcicutils
|
3
|
-
Version: 8.7.2.
|
3
|
+
Version: 8.7.2.1b3
|
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
|
@@ -36,6 +36,7 @@ Requires-Dist: jsonschema (>=4.19.0,<5.0.0)
|
|
36
36
|
Requires-Dist: openpyxl (>=3.1.2,<4.0.0)
|
37
37
|
Requires-Dist: opensearch-py (>=2.0.1,<3.0.0)
|
38
38
|
Requires-Dist: pyOpenSSL (>=23.1.1,<24.0.0)
|
39
|
+
Requires-Dist: pyperclip (>=1.8.2,<2.0.0)
|
39
40
|
Requires-Dist: pyramid (==1.10.4)
|
40
41
|
Requires-Dist: pytz (>=2020.4)
|
41
42
|
Requires-Dist: redis (>=4.5.1,<5.0.0)
|
@@ -2,6 +2,7 @@ dcicutils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
dcicutils/base.py,sha256=gxNEv3DSVUfoX3NToWw7pcCdguxsJ75NDqsPi3wdFG4,5115
|
3
3
|
dcicutils/beanstalk_utils.py,sha256=nHMWfFnZAXFiJh60oVouwbAPMKsQfHnDtkwz_PDE6S4,51434
|
4
4
|
dcicutils/bundle_utils.py,sha256=ZVQcqlt7Yly8-YbL3A-5DW859_hMWpTL6dXtknEYZIw,34669
|
5
|
+
dcicutils/captured_output.py,sha256=9UorUoA-RSGf7fUILHy73JiZ-mJ1IV-or_JGMyncaMs,2522
|
5
6
|
dcicutils/cloudformation_utils.py,sha256=MtWJrSTXyiImgbPHgRvfH9bWso20ZPLTFJAfhDQSVj4,13786
|
6
7
|
dcicutils/codebuild_utils.py,sha256=CKpmhJ-Z8gYbkt1I2zyMlKtFdsg7T8lqrx3V5ieta-U,1155
|
7
8
|
dcicutils/command_utils.py,sha256=JExll5TMqIcmuiGvuS8q4XDUvoEfi2oSH0E2FVF6suU,15285
|
@@ -9,9 +10,9 @@ dcicutils/common.py,sha256=YE8Mt5-vaZWWz4uaChSVhqGFbFtW5QKtnIyOr4zG4vM,3955
|
|
9
10
|
dcicutils/contribution_scripts.py,sha256=0k5Gw1TumcD5SAcXVkDd6-yvuMEw-jUp5Kfb7FJH6XQ,2015
|
10
11
|
dcicutils/contribution_utils.py,sha256=vYLS1JUB3sKd24BUxZ29qUBqYeQBLK9cwo8x3k64uPg,25653
|
11
12
|
dcicutils/creds_utils.py,sha256=xrLekD49Ex0GOpL9n7LlJA4gvNcY7txTVFOSYD7LvEU,11113
|
12
|
-
dcicutils/data_readers.py,sha256=
|
13
|
+
dcicutils/data_readers.py,sha256=6k2Pgcw62JbKcAYFUkMUUxoqAw2OTQTYyTW0qAYhSNA,6369
|
13
14
|
dcicutils/data_utils.py,sha256=k2OxOlsx7AJ6jF-YNlMyGus_JqSUBe4_n1s65Mv1gQQ,3098
|
14
|
-
dcicutils/datetime_utils.py,sha256=
|
15
|
+
dcicutils/datetime_utils.py,sha256=EODDGAngp1yh2ZlDIuI7tB74JBJucw2DljqfPknzK0Y,4666
|
15
16
|
dcicutils/deployment_utils.py,sha256=rcNUFMe_tsrG4CHEtgBe41cZx4Pk4JqISPsjrJRMoEs,68891
|
16
17
|
dcicutils/diff_utils.py,sha256=sQx-yz56DHAcQWOChYbAG3clXu7TbiZKlw-GggeveO0,8118
|
17
18
|
dcicutils/docker_utils.py,sha256=30gUiqz7X9rJwSPXTPn4ewjQibUgoSJqhP9o9vn5X-A,1747
|
@@ -56,6 +57,7 @@ dcicutils/s3_utils.py,sha256=LauLFQGvZLfpBJ81tYMikjLd3SJRz2R_FrL1n4xSlyI,28868
|
|
56
57
|
dcicutils/schema_utils.py,sha256=Ky1KCrHYbDR4qd1prHBKJvO8Z_1x1xVUup1SsQsVP24,10002
|
57
58
|
dcicutils/scripts/publish_to_pypi.py,sha256=LFzNHIQK2EXFr88YcfctyA_WKEBFc1ElnSjWrCXedPM,13889
|
58
59
|
dcicutils/scripts/run_license_checker.py,sha256=z2keYnRDZsHQbTeo1XORAXSXNJK5axVzL5LjiNqZ7jE,4184
|
60
|
+
dcicutils/scripts/view_portal_object.py,sha256=XC6a-5QnEUJHNviPa40JckwwvQnfltZeioletxnnPLY,6163
|
59
61
|
dcicutils/secrets_utils.py,sha256=8dppXAsiHhJzI6NmOcvJV5ldvKkQZzh3Fl-cb8Wm7MI,19745
|
60
62
|
dcicutils/sheet_utils.py,sha256=VlmzteONW5VF_Q4vo0yA5vesz1ViUah1MZ_yA1rwZ0M,33629
|
61
63
|
dcicutils/snapshot_utils.py,sha256=ymP7PXH6-yEiXAt75w0ldQFciGNqWBClNxC5gfX2FnY,22961
|
@@ -67,8 +69,8 @@ dcicutils/trace_utils.py,sha256=g8kwV4ebEy5kXW6oOrEAUsurBcCROvwtZqz9fczsGRE,1769
|
|
67
69
|
dcicutils/validation_utils.py,sha256=cMZIU2cY98FYtzK52z5WUYck7urH6JcqOuz9jkXpqzg,14797
|
68
70
|
dcicutils/variant_utils.py,sha256=2H9azNx3xAj-MySg-uZ2SFqbWs4kZvf61JnK6b-h4Qw,4343
|
69
71
|
dcicutils/zip_utils.py,sha256=rnjNv_k6L9jT2SjDSgVXp4BEJYLtz9XN6Cl2Fy-tqnM,2027
|
70
|
-
dcicutils-8.7.2.
|
71
|
-
dcicutils-8.7.2.
|
72
|
-
dcicutils-8.7.2.
|
73
|
-
dcicutils-8.7.2.
|
74
|
-
dcicutils-8.7.2.
|
72
|
+
dcicutils-8.7.2.1b3.dist-info/LICENSE.txt,sha256=qnwSmfnEWMl5l78VPDEzAmEbLVrRqQvfUQiHT0ehrOo,1102
|
73
|
+
dcicutils-8.7.2.1b3.dist-info/METADATA,sha256=b0rm54SK4Vo5VVnvegPx7Cd3P0Nz1OoQOgW2BGoR81g,3356
|
74
|
+
dcicutils-8.7.2.1b3.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
|
75
|
+
dcicutils-8.7.2.1b3.dist-info/entry_points.txt,sha256=51Q4F_2V10L0282W7HFjP4jdzW4K8lnWDARJQVFy_hw,270
|
76
|
+
dcicutils-8.7.2.1b3.dist-info/RECORD,,
|
File without changes
|
File without changes
|