willisapi-client 1.6__tar.gz → 1.8__tar.gz
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.
- {willisapi_client-1.6 → willisapi_client-1.8}/PKG-INFO +1 -1
- {willisapi_client-1.6 → willisapi_client-1.8}/tests/test_auth.py +2 -60
- {willisapi_client-1.6 → willisapi_client-1.8}/tests/test_download.py +9 -2
- {willisapi_client-1.6 → willisapi_client-1.8}/tests/test_upload.py +1 -1
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/__init__.py +1 -3
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/__version__.py +1 -1
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/services/api.py +1 -1
- willisapi_client-1.8/willisapi_client/services/auth/__init__.py +6 -0
- willisapi_client-1.8/willisapi_client/services/auth/auth_utils.py +52 -0
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/services/auth/login_manager.py +3 -3
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/services/download/download_handler.py +2 -1
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/services/download/download_utils.py +46 -20
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/services/upload/multipart_upload_handler.py +2 -2
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/services/upload/upload_utils.py +5 -4
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/willisapi_client.py +1 -10
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client.egg-info/PKG-INFO +1 -1
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client.egg-info/SOURCES.txt +0 -3
- willisapi_client-1.6/tests/test_user_permissions.py +0 -72
- willisapi_client-1.6/willisapi_client/services/auth/__init__.py +0 -8
- willisapi_client-1.6/willisapi_client/services/auth/account_manager.py +0 -49
- willisapi_client-1.6/willisapi_client/services/auth/auth_utils.py +0 -143
- willisapi_client-1.6/willisapi_client/services/auth/permissions_manager.py +0 -44
- {willisapi_client-1.6 → willisapi_client-1.8}/LICENSE +0 -0
- {willisapi_client-1.6 → willisapi_client-1.8}/MANIFEST.in +0 -0
- {willisapi_client-1.6 → willisapi_client-1.8}/README.md +0 -0
- {willisapi_client-1.6 → willisapi_client-1.8}/RELEASE.md +0 -0
- {willisapi_client-1.6 → willisapi_client-1.8}/requirements.txt +0 -0
- {willisapi_client-1.6 → willisapi_client-1.8}/setup.cfg +0 -0
- {willisapi_client-1.6 → willisapi_client-1.8}/setup.py +0 -0
- {willisapi_client-1.6 → willisapi_client-1.8}/tests/test_csv_validation.py +0 -0
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/logging_setup.py +0 -0
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/services/__init__.py +0 -0
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/services/download/__init__.py +0 -0
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/services/exceptions.py +0 -0
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/services/upload/__init__.py +0 -0
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/services/upload/csv_validation.py +0 -0
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/services/upload/language_choices.py +0 -0
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/services/upload/utils.py +0 -0
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/timer.py +0 -0
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client.egg-info/dependency_links.txt +0 -0
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client.egg-info/not-zip-safe +0 -0
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client.egg-info/requires.txt +0 -0
- {willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client.egg-info/top_level.txt +0 -0
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
from willisapi_client.services.auth.login_manager import login
|
|
2
2
|
|
|
3
|
-
from willisapi_client.services.auth.account_manager import account_create
|
|
4
3
|
from unittest.mock import patch
|
|
5
|
-
from datetime import timedelta, datetime
|
|
4
|
+
from datetime import timedelta, datetime, timezone
|
|
6
5
|
|
|
7
6
|
|
|
8
7
|
class TestLoginFunction:
|
|
9
8
|
def setup_method(self):
|
|
10
|
-
self.dt = datetime.now()
|
|
9
|
+
self.dt = datetime.now(timezone.utc)
|
|
11
10
|
self.username = "dummy"
|
|
12
11
|
self.password = "password"
|
|
13
12
|
self.id_token = "dummy_token"
|
|
@@ -68,60 +67,3 @@ class TestLoginFunction:
|
|
|
68
67
|
key, expire_in = login(self.username, self.password)
|
|
69
68
|
assert key == None
|
|
70
69
|
assert expire_in == None
|
|
71
|
-
|
|
72
|
-
@patch("willisapi_client.services.auth.account_manager.AuthUtils.account_create")
|
|
73
|
-
def test_create_account_success(self, mocked_create_account):
|
|
74
|
-
mocked_create_account.return_value = {
|
|
75
|
-
"status_code": 200,
|
|
76
|
-
"message": "Account Created Successfully",
|
|
77
|
-
}
|
|
78
|
-
result = account_create(
|
|
79
|
-
self.id_token,
|
|
80
|
-
self.account,
|
|
81
|
-
)
|
|
82
|
-
assert result == "Account Created Successfully"
|
|
83
|
-
|
|
84
|
-
@patch("willisapi_client.services.auth.account_manager.AuthUtils.account_create")
|
|
85
|
-
def test_create_account_failed(self, mocked_create_account):
|
|
86
|
-
mocked_create_account.return_value = {
|
|
87
|
-
"status_code": 400,
|
|
88
|
-
"message": "Validation Error",
|
|
89
|
-
}
|
|
90
|
-
result = account_create(
|
|
91
|
-
self.id_token,
|
|
92
|
-
self.account,
|
|
93
|
-
)
|
|
94
|
-
assert result == "Validation Error"
|
|
95
|
-
|
|
96
|
-
@patch("willisapi_client.services.auth.account_manager.AuthUtils.account_create")
|
|
97
|
-
def test_create_account_failed_for_non_admin_user(self, mocked_create_account):
|
|
98
|
-
mocked_create_account.return_value = {
|
|
99
|
-
"status_code": 401,
|
|
100
|
-
"message": "Not an Admin",
|
|
101
|
-
}
|
|
102
|
-
result = account_create(
|
|
103
|
-
self.id_token,
|
|
104
|
-
self.account,
|
|
105
|
-
)
|
|
106
|
-
assert result == "Not an Admin"
|
|
107
|
-
|
|
108
|
-
@patch("willisapi_client.services.auth.account_manager.AuthUtils.account_create")
|
|
109
|
-
def test_create_account_failed_unexpected(self, mocked_create_account):
|
|
110
|
-
mocked_create_account.return_value = {
|
|
111
|
-
"status_code": 500,
|
|
112
|
-
"message": None,
|
|
113
|
-
}
|
|
114
|
-
result = account_create(
|
|
115
|
-
self.id_token,
|
|
116
|
-
self.account,
|
|
117
|
-
)
|
|
118
|
-
assert result == None
|
|
119
|
-
|
|
120
|
-
@patch("willisapi_client.services.auth.account_manager.AuthUtils.account_create")
|
|
121
|
-
def test_create_account_failed_no_response_from_server(self, mocked_create_account):
|
|
122
|
-
mocked_create_account.return_value = {}
|
|
123
|
-
result = account_create(
|
|
124
|
-
self.id_token,
|
|
125
|
-
self.account,
|
|
126
|
-
)
|
|
127
|
-
assert result == None
|
|
@@ -38,16 +38,23 @@ class TestDownloadFunction:
|
|
|
38
38
|
def test_download_no_items_from_api(self, mocked_data):
|
|
39
39
|
mocked_data.return_value = {
|
|
40
40
|
"status_code": 200,
|
|
41
|
-
"
|
|
41
|
+
"presigned_url": None,
|
|
42
42
|
}
|
|
43
43
|
data = download(self.key, self.project_name)
|
|
44
44
|
assert data.empty == True
|
|
45
45
|
|
|
46
46
|
@patch("willisapi_client.services.download.download_utils.DownloadUtils.request")
|
|
47
|
-
|
|
47
|
+
@patch(
|
|
48
|
+
"willisapi_client.services.download.download_utils.DownloadUtils.get_data_from_presigned_url"
|
|
49
|
+
)
|
|
50
|
+
def test_download_success(self, mocked_data, mocked_response):
|
|
48
51
|
with open("tests/test.json") as json_file:
|
|
49
52
|
data = json.load(json_file)
|
|
50
53
|
mocked_data.return_value = data
|
|
54
|
+
mocked_response.return_value = {
|
|
55
|
+
"status_code": 200,
|
|
56
|
+
"presigned_url": "https://google.com",
|
|
57
|
+
}
|
|
51
58
|
data = download(self.key, self.project_name)
|
|
52
59
|
assert data.empty == False
|
|
53
60
|
assert data.filename.tolist()[0] == "test_video.mp4"
|
|
@@ -95,7 +95,7 @@ class TestUpload:
|
|
|
95
95
|
mocked_df.return_value = pd.DataFrame([self.df_row], columns=self.df_cols)
|
|
96
96
|
mock_row_validation.return_value = True, None
|
|
97
97
|
mocked_upload.return_value = True, None
|
|
98
|
-
df = upload(self.key, self.metadata,
|
|
98
|
+
df = upload(self.key, self.metadata, force_upload="true")
|
|
99
99
|
num = len(df[df["upload_status"] == "success"])
|
|
100
100
|
assert num == 1
|
|
101
101
|
assert list(df.columns) == self.response_df_cols
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Version details for willisapi_client
|
|
2
2
|
"""
|
|
3
3
|
__client__ = "willisapi_client"
|
|
4
|
-
__latestVersion__ = "1.
|
|
4
|
+
__latestVersion__ = "1.8"
|
|
5
5
|
__url__ = "https://github.com/bklynhlth/willsiapi_client"
|
|
6
6
|
__short_description__ = "A Python client for willisapi"
|
|
7
7
|
__content_type__ = "text/markdown"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# website: https://www.brooklyn.health
|
|
2
2
|
|
|
3
3
|
# import the required packages
|
|
4
|
-
from willisapi_client.services.auth import login
|
|
4
|
+
from willisapi_client.services.auth import login
|
|
5
5
|
from willisapi_client.services.upload import upload
|
|
6
6
|
from willisapi_client.services.download import download
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import time
|
|
3
|
+
import json
|
|
4
|
+
import random
|
|
5
|
+
|
|
6
|
+
from willisapi_client.services.exceptions import (
|
|
7
|
+
UnableToLoginClientError,
|
|
8
|
+
UnableToCreateAccountError,
|
|
9
|
+
UnableToUpdatePermissionsClientError,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AuthUtils:
|
|
14
|
+
@staticmethod
|
|
15
|
+
def login(url, data, headers, try_number):
|
|
16
|
+
"""
|
|
17
|
+
------------------------------------------------------------------------------------------------------
|
|
18
|
+
Class: AuthUtils
|
|
19
|
+
|
|
20
|
+
Function: login
|
|
21
|
+
|
|
22
|
+
Description: This function makes a POST API call to the brooklyn.health API server to authenticate a user.
|
|
23
|
+
|
|
24
|
+
Parameters:
|
|
25
|
+
----------
|
|
26
|
+
url: The URL of the API endpoint.
|
|
27
|
+
data: The data to be sent in the request body.
|
|
28
|
+
headers: The headers to be sent in the request.
|
|
29
|
+
try_number: The number of times the function has been tried.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
----------
|
|
33
|
+
json: The JSON response from the API server.
|
|
34
|
+
|
|
35
|
+
Raises:
|
|
36
|
+
----------
|
|
37
|
+
UnableToLoginClientError: If the function fails to login after 3 tries.
|
|
38
|
+
------------------------------------------------------------------------------------------------------
|
|
39
|
+
"""
|
|
40
|
+
try:
|
|
41
|
+
response = requests.post(url, json=data, headers=headers)
|
|
42
|
+
res_json = response.json()
|
|
43
|
+
except (
|
|
44
|
+
requests.exceptions.ConnectionError,
|
|
45
|
+
json.decoder.JSONDecodeError,
|
|
46
|
+
) as ex:
|
|
47
|
+
if try_number == 3:
|
|
48
|
+
raise UnableToLoginClientError
|
|
49
|
+
time.sleep(random.random() * 2)
|
|
50
|
+
return AuthUtils.login(url, data, headers, try_number=try_number + 1)
|
|
51
|
+
else:
|
|
52
|
+
return res_json
|
{willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/services/auth/login_manager.py
RENAMED
|
@@ -3,7 +3,7 @@ from typing import Tuple
|
|
|
3
3
|
from http import HTTPStatus
|
|
4
4
|
|
|
5
5
|
# import datetime
|
|
6
|
-
from datetime import datetime, timedelta
|
|
6
|
+
from datetime import datetime, timedelta, timezone
|
|
7
7
|
|
|
8
8
|
from willisapi_client.willisapi_client import WillisapiClient
|
|
9
9
|
from willisapi_client.services.auth.auth_utils import AuthUtils
|
|
@@ -41,10 +41,10 @@ def login(username: str, password: str, **kwargs) -> Tuple[str, int]:
|
|
|
41
41
|
):
|
|
42
42
|
logger.info("Login Successful; Key acquired")
|
|
43
43
|
logger.info(
|
|
44
|
-
f"Key expiration: {datetime.now() + timedelta(seconds=response['result']['expires_in'])}"
|
|
44
|
+
f"Key expiration: {datetime.now(timezone.utc) + timedelta(seconds=response['result']['expires_in'])}"
|
|
45
45
|
)
|
|
46
46
|
required_format = (
|
|
47
|
-
f"{datetime.now() + timedelta(seconds=response['result']['expires_in'])}"
|
|
47
|
+
f"{datetime.now(timezone.utc) + timedelta(seconds=response['result']['expires_in'])}"
|
|
48
48
|
)
|
|
49
49
|
return (response["result"]["id_token"], required_format)
|
|
50
50
|
else:
|
{willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/services/download/download_handler.py
RENAMED
|
@@ -43,7 +43,8 @@ def download(key: str, project_name: str, **kwargs):
|
|
|
43
43
|
logger.error("No access to project/data for download.")
|
|
44
44
|
if response["status_code"] == HTTPStatus.OK:
|
|
45
45
|
logger.info(f'{datetime.now().strftime("%H:%M:%S")}: Download Complete')
|
|
46
|
-
|
|
46
|
+
data = DownloadUtils.get_data_from_presigned_url(response["presigned_url"])
|
|
47
|
+
response_df, err = DownloadUtils.generate_response_df(data)
|
|
47
48
|
if not err:
|
|
48
49
|
return response_df
|
|
49
50
|
return empty_response_df
|
{willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/services/download/download_utils.py
RENAMED
|
@@ -4,6 +4,8 @@ import time
|
|
|
4
4
|
import random
|
|
5
5
|
import pandas as pd
|
|
6
6
|
from typing import Tuple
|
|
7
|
+
from http import HTTPStatus
|
|
8
|
+
|
|
7
9
|
|
|
8
10
|
vocal_acoustic_summary = "vocal_acoustics_summary"
|
|
9
11
|
speech_characteristics_summary = "speech_characteristics_summary"
|
|
@@ -64,8 +66,8 @@ class DownloadUtils:
|
|
|
64
66
|
------------------------------------------------------------------------------------------------------
|
|
65
67
|
"""
|
|
66
68
|
return (
|
|
67
|
-
response["
|
|
68
|
-
len(response["
|
|
69
|
+
response["project"]["project_name"],
|
|
70
|
+
len(response["project"]["participant"]),
|
|
69
71
|
)
|
|
70
72
|
|
|
71
73
|
def _get_pt_id_ext_and_num_records(response, pt):
|
|
@@ -88,8 +90,8 @@ class DownloadUtils:
|
|
|
88
90
|
------------------------------------------------------------------------------------------------------
|
|
89
91
|
"""
|
|
90
92
|
return (
|
|
91
|
-
response["
|
|
92
|
-
len(response["
|
|
93
|
+
response["project"]["participant"][pt]["participant_Id"],
|
|
94
|
+
len(response["project"]["participant"][pt]["results"]),
|
|
93
95
|
)
|
|
94
96
|
|
|
95
97
|
def _get_filename_and_timestamp(response, pt, rec):
|
|
@@ -113,12 +115,8 @@ class DownloadUtils:
|
|
|
113
115
|
------------------------------------------------------------------------------------------------------
|
|
114
116
|
"""
|
|
115
117
|
return (
|
|
116
|
-
response["
|
|
117
|
-
|
|
118
|
-
],
|
|
119
|
-
response["items"]["project"]["participant"][pt]["results"][rec][
|
|
120
|
-
"timestamp"
|
|
121
|
-
],
|
|
118
|
+
response["project"]["participant"][pt]["results"][rec]["file_name"],
|
|
119
|
+
response["project"]["participant"][pt]["results"][rec]["timestamp"],
|
|
122
120
|
)
|
|
123
121
|
|
|
124
122
|
def _get_defined_columns():
|
|
@@ -158,7 +156,7 @@ class DownloadUtils:
|
|
|
158
156
|
df: A pandas dataframe of specific workflow tag
|
|
159
157
|
------------------------------------------------------------------------------------------------------
|
|
160
158
|
"""
|
|
161
|
-
measures_dict = response["
|
|
159
|
+
measures_dict = response["project"]["participant"][pt]["results"][rec][
|
|
162
160
|
"measures"
|
|
163
161
|
]
|
|
164
162
|
if (
|
|
@@ -174,7 +172,35 @@ class DownloadUtils:
|
|
|
174
172
|
return pd.read_json(json.dumps(measures_dict[workflow_tag][0]))
|
|
175
173
|
return pd.DataFrame()
|
|
176
174
|
|
|
177
|
-
def
|
|
175
|
+
def get_data_from_presigned_url(url: str):
|
|
176
|
+
"""
|
|
177
|
+
------------------------------------------------------------------------------------------------------
|
|
178
|
+
Class: DownloadUtils
|
|
179
|
+
|
|
180
|
+
Function: get_data_from_presigned_url
|
|
181
|
+
|
|
182
|
+
Description: This function downloads the json data using S3 preisgned URL
|
|
183
|
+
|
|
184
|
+
Parameters:
|
|
185
|
+
----------
|
|
186
|
+
response: S3 Presigned URL
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
----------
|
|
190
|
+
(data, error): Generates response data and error message
|
|
191
|
+
------------------------------------------------------------------------------------------------------
|
|
192
|
+
"""
|
|
193
|
+
response = {}
|
|
194
|
+
try:
|
|
195
|
+
data = requests.get(url)
|
|
196
|
+
if data.status_code == HTTPStatus.OK:
|
|
197
|
+
response = data.json()
|
|
198
|
+
except Exception:
|
|
199
|
+
pass
|
|
200
|
+
|
|
201
|
+
return response
|
|
202
|
+
|
|
203
|
+
def generate_response_df(data) -> Tuple[pd.DataFrame, str]:
|
|
178
204
|
"""
|
|
179
205
|
------------------------------------------------------------------------------------------------------
|
|
180
206
|
Class: DownloadUtils
|
|
@@ -185,7 +211,7 @@ class DownloadUtils:
|
|
|
185
211
|
|
|
186
212
|
Parameters:
|
|
187
213
|
----------
|
|
188
|
-
|
|
214
|
+
data: The JSON data from the API server.
|
|
189
215
|
|
|
190
216
|
Returns:
|
|
191
217
|
----------
|
|
@@ -194,20 +220,20 @@ class DownloadUtils:
|
|
|
194
220
|
"""
|
|
195
221
|
response_df = pd.DataFrame()
|
|
196
222
|
try:
|
|
197
|
-
if not
|
|
223
|
+
if not data:
|
|
198
224
|
return response_df, "No Data Found"
|
|
199
225
|
(project_name, num_pts) = DownloadUtils._get_project_name_and_pts_count(
|
|
200
|
-
|
|
226
|
+
data
|
|
201
227
|
)
|
|
202
228
|
for pt in range(0, num_pts):
|
|
203
229
|
(pt_id_ext, num_records) = DownloadUtils._get_pt_id_ext_and_num_records(
|
|
204
|
-
|
|
230
|
+
data, pt
|
|
205
231
|
)
|
|
206
232
|
for rec in range(0, num_records):
|
|
207
233
|
(
|
|
208
234
|
filename,
|
|
209
235
|
time_collected,
|
|
210
|
-
) = DownloadUtils._get_filename_and_timestamp(
|
|
236
|
+
) = DownloadUtils._get_filename_and_timestamp(data, pt, rec)
|
|
211
237
|
main_df = pd.DataFrame(
|
|
212
238
|
[[project_name, pt_id_ext, filename, time_collected]],
|
|
213
239
|
columns=DownloadUtils._get_defined_columns(),
|
|
@@ -215,16 +241,16 @@ class DownloadUtils:
|
|
|
215
241
|
|
|
216
242
|
vocal_acoustics_summary_df = (
|
|
217
243
|
DownloadUtils._get_summary_df_from_json(
|
|
218
|
-
|
|
244
|
+
data, pt, rec, vocal_acoustic_summary
|
|
219
245
|
)
|
|
220
246
|
)
|
|
221
247
|
speech_characteristics_summary_df = (
|
|
222
248
|
DownloadUtils._get_summary_df_from_json(
|
|
223
|
-
|
|
249
|
+
data, pt, rec, speech_characteristics_summary
|
|
224
250
|
)
|
|
225
251
|
)
|
|
226
252
|
rater_qa_summary_df = DownloadUtils._get_summary_df_from_json(
|
|
227
|
-
|
|
253
|
+
data, pt, rec, rater_qa_summary
|
|
228
254
|
)
|
|
229
255
|
df = pd.concat(
|
|
230
256
|
[
|
|
@@ -33,7 +33,7 @@ def upload(key, data, **kwargs):
|
|
|
33
33
|
logger.info(f'{datetime.now().strftime("%H:%M:%S")}: csv check passed')
|
|
34
34
|
dataframe = csv.get_dataframe()
|
|
35
35
|
wc = WillisapiClient(env=kwargs.get("env"))
|
|
36
|
-
|
|
36
|
+
force_upload = "true" if kwargs.get("force_upload") == True else False
|
|
37
37
|
url = wc.get_upload_url()
|
|
38
38
|
headers = wc.get_headers()
|
|
39
39
|
headers["Authorization"] = key
|
|
@@ -43,7 +43,7 @@ def upload(key, data, **kwargs):
|
|
|
43
43
|
(is_valid_row, error) = csv.validate_row(row)
|
|
44
44
|
if is_valid_row:
|
|
45
45
|
(uploaded, error) = UploadUtils.upload(
|
|
46
|
-
index, row, url, headers,
|
|
46
|
+
index, row, url, headers, force_upload
|
|
47
47
|
)
|
|
48
48
|
if uploaded:
|
|
49
49
|
summary.append([row.file_path, "success", None])
|
{willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/services/upload/upload_utils.py
RENAMED
|
@@ -102,7 +102,7 @@ class UploadUtils:
|
|
|
102
102
|
return number_of_parts
|
|
103
103
|
|
|
104
104
|
def initiate_multi_part_upload(
|
|
105
|
-
index: int, row, url: str, headers: dict,
|
|
105
|
+
index: int, row, url: str, headers: dict, force_upload: str
|
|
106
106
|
) -> Tuple[str, str]:
|
|
107
107
|
"""
|
|
108
108
|
------------------------------------------------------------------------------------------------------
|
|
@@ -146,7 +146,7 @@ class UploadUtils:
|
|
|
146
146
|
|
|
147
147
|
try:
|
|
148
148
|
response = requests.post(
|
|
149
|
-
f"{url}?type=initiate&
|
|
149
|
+
f"{url}?type=initiate&force_upload={force_upload}", json=data, headers=headers
|
|
150
150
|
)
|
|
151
151
|
res_json = response.json()
|
|
152
152
|
except Exception as ex:
|
|
@@ -171,6 +171,7 @@ class UploadUtils:
|
|
|
171
171
|
|
|
172
172
|
else:
|
|
173
173
|
if "message" in res_json and res_json["message"] == "Unauthorized":
|
|
174
|
+
error = res_json["message"]
|
|
174
175
|
logger.error(
|
|
175
176
|
"Your Key is expired. Login again to generate a new key"
|
|
176
177
|
)
|
|
@@ -279,7 +280,7 @@ class UploadUtils:
|
|
|
279
280
|
return SORTED_PARTS
|
|
280
281
|
|
|
281
282
|
@staticmethod
|
|
282
|
-
def upload(index, row, url, headers,
|
|
283
|
+
def upload(index, row, url, headers, force_upload) -> Tuple[bool, str]:
|
|
283
284
|
"""
|
|
284
285
|
------------------------------------------------------------------------------------------------------
|
|
285
286
|
Class: UploadUtils
|
|
@@ -302,7 +303,7 @@ class UploadUtils:
|
|
|
302
303
|
------------------------------------------------------------------------------------------------------
|
|
303
304
|
"""
|
|
304
305
|
(upload_id, record_id, error) = UploadUtils.initiate_multi_part_upload(
|
|
305
|
-
index, row, url, headers,
|
|
306
|
+
index, row, url, headers, force_upload
|
|
306
307
|
)
|
|
307
308
|
uploaded = False
|
|
308
309
|
if upload_id and record_id:
|
|
@@ -15,7 +15,6 @@ class WillisapiClient:
|
|
|
15
15
|
self.client_version = get_client_version()
|
|
16
16
|
self.api_version = math.floor(self.client_version)
|
|
17
17
|
self.api_uri = "api.brooklyn.health"
|
|
18
|
-
self.app_url = f"app.brooklyn.health/api/v{self.api_version}/"
|
|
19
18
|
self.env = kwargs["env"] if "env" in kwargs else None
|
|
20
19
|
|
|
21
20
|
def get_base_url(self):
|
|
@@ -33,15 +32,7 @@ class WillisapiClient:
|
|
|
33
32
|
return self.get_base_url() + "upload"
|
|
34
33
|
|
|
35
34
|
def get_download_url(self):
|
|
36
|
-
|
|
37
|
-
return f"https://{self.env}-{self.app_url}download"
|
|
38
|
-
return f"https://{self.app_url}download"
|
|
39
|
-
|
|
40
|
-
def get_account_create_url(self):
|
|
41
|
-
return self.get_base_url() + "account-create"
|
|
42
|
-
|
|
43
|
-
def get_user_permissions_url(self):
|
|
44
|
-
return self.get_base_url() + "user-permissions"
|
|
35
|
+
return self.get_base_url() + "download"
|
|
45
36
|
|
|
46
37
|
def get_headers(self):
|
|
47
38
|
return {"Content-Type": "application/json", "Accept": "application/json"}
|
|
@@ -9,7 +9,6 @@ tests/test_auth.py
|
|
|
9
9
|
tests/test_csv_validation.py
|
|
10
10
|
tests/test_download.py
|
|
11
11
|
tests/test_upload.py
|
|
12
|
-
tests/test_user_permissions.py
|
|
13
12
|
willisapi_client/__init__.py
|
|
14
13
|
willisapi_client/__version__.py
|
|
15
14
|
willisapi_client/logging_setup.py
|
|
@@ -25,10 +24,8 @@ willisapi_client/services/__init__.py
|
|
|
25
24
|
willisapi_client/services/api.py
|
|
26
25
|
willisapi_client/services/exceptions.py
|
|
27
26
|
willisapi_client/services/auth/__init__.py
|
|
28
|
-
willisapi_client/services/auth/account_manager.py
|
|
29
27
|
willisapi_client/services/auth/auth_utils.py
|
|
30
28
|
willisapi_client/services/auth/login_manager.py
|
|
31
|
-
willisapi_client/services/auth/permissions_manager.py
|
|
32
29
|
willisapi_client/services/download/__init__.py
|
|
33
30
|
willisapi_client/services/download/download_handler.py
|
|
34
31
|
willisapi_client/services/download/download_utils.py
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
from unittest.mock import patch
|
|
2
|
-
from willisapi_client.services.auth.permissions_manager import user_permissions
|
|
3
|
-
|
|
4
|
-
import sys
|
|
5
|
-
import logging
|
|
6
|
-
|
|
7
|
-
logger = logging.getLogger()
|
|
8
|
-
logger.level = logging.INFO
|
|
9
|
-
stream_handler = logging.StreamHandler(sys.stdout)
|
|
10
|
-
logger.addHandler(stream_handler)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class TestUserPermissions:
|
|
14
|
-
def setup_method(self):
|
|
15
|
-
self.username = "dummy"
|
|
16
|
-
self.password = "password"
|
|
17
|
-
self.id_token = "dummy_token"
|
|
18
|
-
self.user_email = "dummy@gmail.com"
|
|
19
|
-
self.client_name = "dummy_group"
|
|
20
|
-
self.expires_in = 100
|
|
21
|
-
self.failed_message = "Something went wrong"
|
|
22
|
-
self.invalid_role = "Invalid role"
|
|
23
|
-
|
|
24
|
-
@patch("willisapi_client.services.auth.login_manager.AuthUtils.user_permissions")
|
|
25
|
-
def test_user_permissions_failed(self, mocked_user_permissions):
|
|
26
|
-
mocked_user_permissions.return_value = {
|
|
27
|
-
"status_code": 500,
|
|
28
|
-
"message": self.failed_message,
|
|
29
|
-
}
|
|
30
|
-
message = user_permissions(
|
|
31
|
-
self.id_token, self.user_email, self.client_name, "rw"
|
|
32
|
-
)
|
|
33
|
-
assert message == self.failed_message
|
|
34
|
-
|
|
35
|
-
def test_user_permissions_invalidrole(self):
|
|
36
|
-
message = user_permissions(
|
|
37
|
-
self.id_token, self.user_email, self.client_name, "rww"
|
|
38
|
-
)
|
|
39
|
-
assert message == self.invalid_role
|
|
40
|
-
|
|
41
|
-
@patch("willisapi_client.services.auth.login_manager.AuthUtils.user_permissions")
|
|
42
|
-
def test_user_permissions_unauthoristed(self, mocked_user_permissions):
|
|
43
|
-
mocked_user_permissions.return_value = {
|
|
44
|
-
"status_code": 401,
|
|
45
|
-
"message": "not an admin",
|
|
46
|
-
}
|
|
47
|
-
message = user_permissions(
|
|
48
|
-
self.id_token, self.user_email, self.client_name, "rw"
|
|
49
|
-
)
|
|
50
|
-
assert message == "not an admin"
|
|
51
|
-
|
|
52
|
-
@patch("willisapi_client.services.auth.login_manager.AuthUtils.user_permissions")
|
|
53
|
-
def test_user_permissions_success(self, mocked_user_permissions):
|
|
54
|
-
mocked_user_permissions.return_value = {
|
|
55
|
-
"status_code": 200,
|
|
56
|
-
"message": "success",
|
|
57
|
-
}
|
|
58
|
-
message = user_permissions(
|
|
59
|
-
self.id_token, self.user_email, self.client_name, "rw"
|
|
60
|
-
)
|
|
61
|
-
assert message == "success"
|
|
62
|
-
|
|
63
|
-
@patch("willisapi_client.services.auth.login_manager.AuthUtils.user_permissions")
|
|
64
|
-
def test_user_permissions_invalid_group(self, mocked_user_permissions):
|
|
65
|
-
mocked_user_permissions.return_value = {
|
|
66
|
-
"status_code": 404,
|
|
67
|
-
"message": "Account not found",
|
|
68
|
-
}
|
|
69
|
-
message = user_permissions(
|
|
70
|
-
self.id_token, self.user_email, self.client_name, "rw"
|
|
71
|
-
)
|
|
72
|
-
assert message == "Account not found"
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
# website: https://www.brooklyn.health
|
|
2
|
-
from willisapi_client.services.auth.login_manager import (
|
|
3
|
-
login,
|
|
4
|
-
)
|
|
5
|
-
from willisapi_client.services.auth.permissions_manager import user_permissions
|
|
6
|
-
from willisapi_client.services.auth.account_manager import account_create
|
|
7
|
-
|
|
8
|
-
__all__ = ["login", "user_permissions", "account_create"]
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
# website: https://www.brooklyn.health
|
|
2
|
-
from http import HTTPStatus
|
|
3
|
-
|
|
4
|
-
from willisapi_client.willisapi_client import WillisapiClient
|
|
5
|
-
from willisapi_client.services.auth.auth_utils import AuthUtils
|
|
6
|
-
from willisapi_client.logging_setup import logger as logger
|
|
7
|
-
|
|
8
|
-
from datetime import datetime
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def account_create(
|
|
12
|
-
key: str,
|
|
13
|
-
account: str,
|
|
14
|
-
**kwargs,
|
|
15
|
-
) -> str:
|
|
16
|
-
"""
|
|
17
|
-
---------------------------------------------------------------------------------------------------
|
|
18
|
-
Function: account_create
|
|
19
|
-
|
|
20
|
-
Description: This function to creates account in RDS and AWS Cognito using willis account create API
|
|
21
|
-
|
|
22
|
-
Parameters:
|
|
23
|
-
----------
|
|
24
|
-
key: Admin access token
|
|
25
|
-
account: Client Name
|
|
26
|
-
|
|
27
|
-
Returns:
|
|
28
|
-
----------
|
|
29
|
-
None
|
|
30
|
-
---------------------------------------------------------------------------------------------------
|
|
31
|
-
"""
|
|
32
|
-
wc = WillisapiClient(env=kwargs.get("env"))
|
|
33
|
-
url = wc.get_account_create_url()
|
|
34
|
-
headers = wc.get_headers()
|
|
35
|
-
headers["Authorization"] = key
|
|
36
|
-
data = dict(
|
|
37
|
-
account=account.lower(),
|
|
38
|
-
)
|
|
39
|
-
logger.info(f'{datetime.now().strftime("%H:%M:%S")}: Creating account')
|
|
40
|
-
response = AuthUtils.account_create(url, data, headers, try_number=1)
|
|
41
|
-
if response and "status_code" in response:
|
|
42
|
-
if response["status_code"] == HTTPStatus.OK:
|
|
43
|
-
logger.info(response["message"])
|
|
44
|
-
else:
|
|
45
|
-
logger.error(response["message"])
|
|
46
|
-
return response["message"]
|
|
47
|
-
else:
|
|
48
|
-
logger.error(f"Failed")
|
|
49
|
-
return None
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import requests
|
|
2
|
-
import time
|
|
3
|
-
import json
|
|
4
|
-
import random
|
|
5
|
-
|
|
6
|
-
from willisapi_client.services.exceptions import (
|
|
7
|
-
UnableToLoginClientError,
|
|
8
|
-
UnableToCreateAccountError,
|
|
9
|
-
UnableToUpdatePermissionsClientError,
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class AuthUtils:
|
|
14
|
-
@staticmethod
|
|
15
|
-
def login(url, data, headers, try_number):
|
|
16
|
-
"""
|
|
17
|
-
------------------------------------------------------------------------------------------------------
|
|
18
|
-
Class: AuthUtils
|
|
19
|
-
|
|
20
|
-
Function: login
|
|
21
|
-
|
|
22
|
-
Description: This function makes a POST API call to the brooklyn.health API server to authenticate a user.
|
|
23
|
-
|
|
24
|
-
Parameters:
|
|
25
|
-
----------
|
|
26
|
-
url: The URL of the API endpoint.
|
|
27
|
-
data: The data to be sent in the request body.
|
|
28
|
-
headers: The headers to be sent in the request.
|
|
29
|
-
try_number: The number of times the function has been tried.
|
|
30
|
-
|
|
31
|
-
Returns:
|
|
32
|
-
----------
|
|
33
|
-
json: The JSON response from the API server.
|
|
34
|
-
|
|
35
|
-
Raises:
|
|
36
|
-
----------
|
|
37
|
-
UnableToLoginClientError: If the function fails to login after 3 tries.
|
|
38
|
-
------------------------------------------------------------------------------------------------------
|
|
39
|
-
"""
|
|
40
|
-
try:
|
|
41
|
-
response = requests.post(url, json=data, headers=headers)
|
|
42
|
-
res_json = response.json()
|
|
43
|
-
except (
|
|
44
|
-
requests.exceptions.ConnectionError,
|
|
45
|
-
json.decoder.JSONDecodeError,
|
|
46
|
-
) as ex:
|
|
47
|
-
if try_number == 3:
|
|
48
|
-
raise UnableToLoginClientError
|
|
49
|
-
time.sleep(random.random() * 2)
|
|
50
|
-
return AuthUtils.login(url, data, headers, try_number=try_number + 1)
|
|
51
|
-
else:
|
|
52
|
-
return res_json
|
|
53
|
-
|
|
54
|
-
@staticmethod
|
|
55
|
-
def account_create(url, data, headers, try_number):
|
|
56
|
-
"""
|
|
57
|
-
------------------------------------------------------------------------------------------------------
|
|
58
|
-
Class: AuthUtils
|
|
59
|
-
|
|
60
|
-
Function: account_create
|
|
61
|
-
|
|
62
|
-
Description: This is an internal account_create function which makes a POST API call to the brooklyn.health API server.
|
|
63
|
-
|
|
64
|
-
Parameters:
|
|
65
|
-
----------
|
|
66
|
-
url: The URL of the API endpoint.
|
|
67
|
-
data: The data to be sent in the request body.
|
|
68
|
-
headers: The headers to be sent in the request.
|
|
69
|
-
try_number: The number of times the function has been tried.
|
|
70
|
-
|
|
71
|
-
Returns:
|
|
72
|
-
----------
|
|
73
|
-
json: The JSON response from the API server.
|
|
74
|
-
|
|
75
|
-
Raises:
|
|
76
|
-
----------
|
|
77
|
-
UnableToCreateAccountError: If the function fails to signup after 3 tries.
|
|
78
|
-
------------------------------------------------------------------------------------------------------
|
|
79
|
-
"""
|
|
80
|
-
try:
|
|
81
|
-
response = requests.post(url, json=data, headers=headers)
|
|
82
|
-
res_json = response.json()
|
|
83
|
-
except (
|
|
84
|
-
requests.exceptions.ConnectionError,
|
|
85
|
-
json.decoder.JSONDecodeError,
|
|
86
|
-
) as ex:
|
|
87
|
-
if try_number == 3:
|
|
88
|
-
raise UnableToCreateAccountError
|
|
89
|
-
time.sleep(random.random() * 2)
|
|
90
|
-
return AuthUtils.account_create(url, data, headers, try_number)
|
|
91
|
-
else:
|
|
92
|
-
return res_json
|
|
93
|
-
|
|
94
|
-
@staticmethod
|
|
95
|
-
def user_permissions(url, data, headers, try_number):
|
|
96
|
-
"""
|
|
97
|
-
------------------------------------------------------------------------------------------------------
|
|
98
|
-
Class: AuthUtils
|
|
99
|
-
|
|
100
|
-
Function: user_permissions
|
|
101
|
-
|
|
102
|
-
Description: This function makes a POST API call to the brooklyn.health API server to update user permissions.
|
|
103
|
-
|
|
104
|
-
Parameters:
|
|
105
|
-
----------
|
|
106
|
-
url: The URL of the API endpoint.
|
|
107
|
-
data: The data to be sent in the request body.
|
|
108
|
-
headers: The headers to be sent in the request.
|
|
109
|
-
try_number: The number of times the function has been tried.
|
|
110
|
-
|
|
111
|
-
Returns:
|
|
112
|
-
----------
|
|
113
|
-
json: The JSON response from the API server.
|
|
114
|
-
|
|
115
|
-
Raises:
|
|
116
|
-
----------
|
|
117
|
-
UnableToLoginClientError: If the function fails to login after 3 tries.
|
|
118
|
-
------------------------------------------------------------------------------------------------------
|
|
119
|
-
"""
|
|
120
|
-
try:
|
|
121
|
-
response = requests.post(url, json=data, headers=headers)
|
|
122
|
-
res_json = response.json()
|
|
123
|
-
except (
|
|
124
|
-
requests.exceptions.ConnectionError,
|
|
125
|
-
json.decoder.JSONDecodeError,
|
|
126
|
-
) as ex:
|
|
127
|
-
if try_number == 3:
|
|
128
|
-
raise UnableToUpdatePermissionsClientError
|
|
129
|
-
time.sleep(random.random() * 2)
|
|
130
|
-
return AuthUtils.user_permissions(
|
|
131
|
-
url, data, headers, try_number=try_number + 1
|
|
132
|
-
)
|
|
133
|
-
else:
|
|
134
|
-
return res_json
|
|
135
|
-
|
|
136
|
-
@staticmethod
|
|
137
|
-
def validate_role(role: str):
|
|
138
|
-
READ = "r"
|
|
139
|
-
READ_WRITE = "rw"
|
|
140
|
-
FULL_ACCESS = "*"
|
|
141
|
-
if role in [READ, READ_WRITE, FULL_ACCESS]:
|
|
142
|
-
return True
|
|
143
|
-
return False
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
from http import HTTPStatus
|
|
2
|
-
|
|
3
|
-
from willisapi_client.willisapi_client import WillisapiClient
|
|
4
|
-
from willisapi_client.services.auth.auth_utils import AuthUtils
|
|
5
|
-
from willisapi_client.logging_setup import logger as logger
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def user_permissions(
|
|
9
|
-
key: str, user_email: str, account: str, role: str, **kwargs
|
|
10
|
-
) -> str:
|
|
11
|
-
"""
|
|
12
|
-
---------------------------------------------------------------------------------------------------
|
|
13
|
-
Function: user_permissions
|
|
14
|
-
|
|
15
|
-
Description: This function is responsible for making a call to the /user_permissions API for access control
|
|
16
|
-
|
|
17
|
-
Parameters:
|
|
18
|
-
----------
|
|
19
|
-
key: string representation of id_token generate after login
|
|
20
|
-
user_email: string representation of user's email
|
|
21
|
-
account: string representation of account/client name
|
|
22
|
-
role: r/rw/*
|
|
23
|
-
|
|
24
|
-
Returns:
|
|
25
|
-
----------
|
|
26
|
-
None
|
|
27
|
-
|
|
28
|
-
---------------------------------------------------------------------------------------------------
|
|
29
|
-
"""
|
|
30
|
-
wc = WillisapiClient(env=kwargs.get("env"))
|
|
31
|
-
url = wc.get_user_permissions_url()
|
|
32
|
-
headers = wc.get_headers()
|
|
33
|
-
headers["Authorization"] = key
|
|
34
|
-
data = dict(user_email=user_email, account=account, role=role)
|
|
35
|
-
is_role_valid = AuthUtils.validate_role(role)
|
|
36
|
-
if is_role_valid:
|
|
37
|
-
response = AuthUtils.user_permissions(url, data, headers, try_number=1)
|
|
38
|
-
message = response["message"]
|
|
39
|
-
logger.info(message)
|
|
40
|
-
else:
|
|
41
|
-
message = "Invalid role"
|
|
42
|
-
logger.info(message)
|
|
43
|
-
|
|
44
|
-
return message
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/services/download/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/services/upload/csv_validation.py
RENAMED
|
File without changes
|
{willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client/services/upload/language_choices.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{willisapi_client-1.6 → willisapi_client-1.8}/willisapi_client.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|