willisapi-client 1.9.1__tar.gz → 1.9.2__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.
Files changed (47) hide show
  1. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/PKG-INFO +2 -10
  2. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/README.md +1 -9
  3. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/__init__.py +2 -0
  4. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/__version__.py +2 -3
  5. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/services/api.py +1 -0
  6. willisapi_client-1.9.2/willisapi_client/services/metadata/__init__.py +4 -0
  7. willisapi_client-1.9.2/willisapi_client/services/metadata/upload.py +83 -0
  8. willisapi_client-1.9.2/willisapi_client/services/metadata/utils.py +336 -0
  9. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/willisapi_client.py +8 -0
  10. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client.egg-info/PKG-INFO +2 -10
  11. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client.egg-info/SOURCES.txt +3 -0
  12. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/LICENSE +0 -0
  13. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/MANIFEST.in +0 -0
  14. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/RELEASE.md +0 -0
  15. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/requirements.txt +0 -0
  16. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/setup.cfg +0 -0
  17. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/setup.py +0 -0
  18. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/tests/test_auth.py +0 -0
  19. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/tests/test_csv_validation.py +0 -0
  20. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/tests/test_diarize.py +0 -0
  21. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/tests/test_diarize_call_remaining.py +0 -0
  22. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/tests/test_download.py +0 -0
  23. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/tests/test_upload.py +0 -0
  24. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/logging_setup.py +0 -0
  25. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/services/__init__.py +0 -0
  26. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/services/auth/__init__.py +0 -0
  27. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/services/auth/auth_utils.py +0 -0
  28. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/services/auth/login_manager.py +0 -0
  29. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/services/diarize/__init__.py +0 -0
  30. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/services/diarize/diarize_utils.py +0 -0
  31. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/services/diarize/willisdiarize.py +0 -0
  32. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/services/diarize/willisdiarize_call_remaining.py +0 -0
  33. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/services/download/__init__.py +0 -0
  34. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/services/download/download_handler.py +0 -0
  35. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/services/download/download_utils.py +0 -0
  36. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/services/exceptions.py +0 -0
  37. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/services/upload/__init__.py +0 -0
  38. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/services/upload/csv_validation.py +0 -0
  39. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/services/upload/language_choices.py +0 -0
  40. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/services/upload/multipart_upload_handler.py +0 -0
  41. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/services/upload/upload_utils.py +0 -0
  42. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/services/upload/utils.py +0 -0
  43. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client/timer.py +0 -0
  44. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client.egg-info/dependency_links.txt +0 -0
  45. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client.egg-info/not-zip-safe +0 -0
  46. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client.egg-info/requires.txt +0 -0
  47. {willisapi_client-1.9.1 → willisapi_client-1.9.2}/willisapi_client.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: willisapi_client
3
- Version: 1.9.1
3
+ Version: 1.9.2
4
4
  Summary: A Python client for willisapi
5
5
  Home-page: https://github.com/bklynhlth/willsiapi_client
6
6
  Author: bklynhlth
@@ -67,21 +67,13 @@ key, expiration = willisapi.login(username, password)
67
67
 
68
68
 
69
69
  ```
70
- summary = willisapi.upload(key, 'data.csv')
70
+ summary = willisapi.metadata_upload(key, 'data.csv')
71
71
  ```
72
72
 
73
73
 
74
74
  For more information on how to organize the `data.csv`, visit the [Github Wiki](http://www.github.com/bklynhlth/willisapi_client/wiki).
75
75
 
76
76
 
77
- #### Download
78
-
79
-
80
- ```
81
- measures = willisapi.download(key, 'project_name')
82
- ```
83
-
84
-
85
77
  If you run into trouble while using the client, please raise it in the [Issues](http://www.github.com/bklynhlth/willisapi_client/issues) tab.
86
78
 
87
79
  ***
@@ -55,21 +55,13 @@ key, expiration = willisapi.login(username, password)
55
55
 
56
56
 
57
57
  ```
58
- summary = willisapi.upload(key, 'data.csv')
58
+ summary = willisapi.metadata_upload(key, 'data.csv')
59
59
  ```
60
60
 
61
61
 
62
62
  For more information on how to organize the `data.csv`, visit the [Github Wiki](http://www.github.com/bklynhlth/willisapi_client/wiki).
63
63
 
64
64
 
65
- #### Download
66
-
67
-
68
- ```
69
- measures = willisapi.download(key, 'project_name')
70
- ```
71
-
72
-
73
65
  If you run into trouble while using the client, please raise it in the [Issues](http://www.github.com/bklynhlth/willisapi_client/issues) tab.
74
66
 
75
67
  ***
@@ -7,6 +7,7 @@ from willisapi_client.services.api import (
7
7
  download,
8
8
  willis_diarize_call_remaining,
9
9
  willis_diarize,
10
+ metadata_upload,
10
11
  )
11
12
 
12
13
  __all__ = [
@@ -15,4 +16,5 @@ __all__ = [
15
16
  "download",
16
17
  "willis_diarize_call_remaining",
17
18
  "willis_diarize",
19
+ "metadata_upload",
18
20
  ]
@@ -1,8 +1,7 @@
1
- """Version details for willisapi_client
2
- """
1
+ """Version details for willisapi_client"""
3
2
 
4
3
  __client__ = "willisapi_client"
5
- __latestVersion__ = "1.9.1"
4
+ __latestVersion__ = "1.9.2"
6
5
  __url__ = "https://github.com/bklynhlth/willsiapi_client"
7
6
  __short_description__ = "A Python client for willisapi"
8
7
  __content_type__ = "text/markdown"
@@ -8,3 +8,4 @@ from willisapi_client.services.diarize import (
8
8
  willis_diarize_call_remaining,
9
9
  willis_diarize,
10
10
  )
11
+ from willisapi_client.services.metadata import metadata_upload
@@ -0,0 +1,4 @@
1
+ # website: https://www.brooklyn.health
2
+ from .upload import metadata_upload
3
+
4
+ __all__ = ["metadata_upload"]
@@ -0,0 +1,83 @@
1
+ import pandas as pd
2
+ from tqdm import tqdm
3
+ from datetime import datetime
4
+ import requests
5
+
6
+ from willisapi_client.timer import measure
7
+ from willisapi_client.willisapi_client import WillisapiClient
8
+ from willisapi_client.logging_setup import logger as logger
9
+ from willisapi_client.services.metadata.utils import MetadataValidation, UploadUtils
10
+
11
+
12
+ @measure
13
+ def metadata_upload(api_key: str, csv_path: str, **kwargs):
14
+
15
+ force_upload = kwargs.get("force_upload", False)
16
+ csv = MetadataValidation(csv_path=csv_path, force_upload=force_upload)
17
+ if csv.load_and_validate():
18
+ logger.info(f'{datetime.now().strftime("%H:%M:%S")}: csv check passed')
19
+ csv.create_final_csv()
20
+
21
+ wc = WillisapiClient(env=kwargs.get("env"))
22
+ url = wc.get_metadata_upload_url()
23
+ headers = wc.get_headers()
24
+ headers["Authorization"] = f"token {api_key}"
25
+ logger.info(f'{datetime.now().strftime("%H:%M:%S")}: beginning upload')
26
+
27
+ results = []
28
+ for index, row in tqdm(
29
+ csv.transformed_df.iterrows(), total=csv.transformed_df.shape[0]
30
+ ):
31
+ u = UploadUtils(row)
32
+ valid, err = u.validate_row()
33
+ result_row = row.to_dict()
34
+ if valid:
35
+ payload = u.generate_payload()
36
+ res = u.post(api_key, url, headers, payload)
37
+ if res.get("upload_status") == "Success":
38
+ result_row["upload_status"] = "Success"
39
+ result_row["error"] = None
40
+
41
+ # Handle S3 upload if presigned URL is provided
42
+ presigned = res.get("response", {}).get("presigned")
43
+ if presigned:
44
+ try:
45
+ with open(row.file_path, "rb") as f:
46
+ files = {"file": f}
47
+ response = requests.put(
48
+ presigned,
49
+ data=files["file"],
50
+ headers={"Content-Type": "audio/ogg"},
51
+ )
52
+ if response.status_code == 200:
53
+ result_row["upload_status"] = "Success"
54
+ else:
55
+ result_row["upload_status"] = "Failed"
56
+ result_row["error"] = (
57
+ f"S3 upload failed with status code {response.status_code}"
58
+ )
59
+ except Exception as ex:
60
+ result_row["upload_status"] = "Failed"
61
+ result_row["error"] = str(ex)
62
+ else:
63
+ result_row["upload_status"] = "Failed"
64
+ result_row["error"] = (
65
+ "Collect recording upload URL not received"
66
+ )
67
+ else:
68
+ result_row["upload_status"] = "Failed"
69
+ result_row["error"] = res.get("error")
70
+ else:
71
+ result_row["upload_status"] = "Failed"
72
+ result_row["error"] = f"{err}"
73
+ results.append(result_row)
74
+
75
+ results_df = pd.DataFrame(results)
76
+ results_df.to_csv(
77
+ f"metadata_upload_results_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
78
+ index=False,
79
+ )
80
+ return results_df
81
+ else:
82
+ logger.error(f'{datetime.now().strftime("%H:%M:%S")}: csv check failed')
83
+ return None
@@ -0,0 +1,336 @@
1
+ import pandas as pd
2
+ from typing import List, Dict, Any, Tuple
3
+ import json
4
+ import os
5
+ import requests
6
+ from willisapi_client.services.upload.language_choices import (
7
+ LANGUAGE_CHOICES,
8
+ )
9
+
10
+
11
+ class MetadataValidation:
12
+ REQUIRED_COLUMNS = [
13
+ "study_id",
14
+ "site_id",
15
+ "rater_email",
16
+ "participant_id",
17
+ "visit_name",
18
+ "visit_order",
19
+ "coa_name",
20
+ "coa_item_number",
21
+ "coa_item_value",
22
+ "file_path",
23
+ "time_collected",
24
+ ]
25
+
26
+ OPTIONAL_COLUMNS = ["age", "sex", "race", "language"]
27
+
28
+ ALLOWED_COA_NAMES = ["MADRS", "YMRS", "PHQ-9", "GAD-7"]
29
+
30
+ COA_ITEM_COUNTS = {"MADRS": 10, "YMRS": 10, "PHQ-9": 9, "GAD-7": 7}
31
+
32
+ def __init__(self, csv_path: str, force_upload: bool = False):
33
+ """
34
+ Initialize validator with CSV file path.
35
+
36
+ Args:
37
+ csv_path: Path to the CSV file
38
+ """
39
+ self.csv_path = csv_path
40
+ self.df = None
41
+ self.errors = []
42
+ self.transformed_df = None
43
+ self.force_upload = force_upload
44
+
45
+ def validate_columns(self) -> bool:
46
+ """
47
+ Validate that all required columns are present.
48
+
49
+ Returns:
50
+ bool: True if validation passes, False otherwise
51
+ """
52
+ missing_cols = [
53
+ col for col in self.REQUIRED_COLUMNS if col not in self.df.columns
54
+ ]
55
+
56
+ if missing_cols:
57
+ self.errors.append(f"Missing required columns: {', '.join(missing_cols)}")
58
+ return False
59
+ return True
60
+
61
+ def validate_data_types(self) -> bool:
62
+ """
63
+ Validate data types for key columns.
64
+
65
+ Returns:
66
+ bool: True if validation passes, False otherwise
67
+ """
68
+ valid = True
69
+
70
+ # Check visit_order is numeric
71
+ if not pd.api.types.is_numeric_dtype(self.df["visit_order"]):
72
+ try:
73
+ self.df["visit_order"] = pd.to_numeric(self.df["visit_order"])
74
+ except:
75
+ self.errors.append("visit_order must be numeric")
76
+ valid = False
77
+
78
+ # Check age is numeric (if present)
79
+ if "age" in self.df.columns and not self.df["age"].isna().all():
80
+ if not pd.api.types.is_numeric_dtype(self.df["age"]):
81
+ try:
82
+ self.df["age"] = pd.to_numeric(self.df["age"], errors="coerce")
83
+ except:
84
+ self.errors.append("age must be numeric")
85
+ valid = False
86
+
87
+ # Check coa_item_number is numeric
88
+ if not pd.api.types.is_numeric_dtype(self.df["coa_item_number"]):
89
+ try:
90
+ self.df["coa_item_number"] = pd.to_numeric(self.df["coa_item_number"])
91
+ except:
92
+ self.errors.append("coa_item_number must be numeric")
93
+ valid = False
94
+
95
+ # Check coa_item_value is numeric
96
+ if not pd.api.types.is_numeric_dtype(self.df["coa_item_value"]):
97
+ try:
98
+ self.df["coa_item_value"] = pd.to_numeric(
99
+ self.df["coa_item_value"], errors="coerce"
100
+ )
101
+ except:
102
+ self.errors.append("coa_item_value must be numeric")
103
+ valid = False
104
+
105
+ return valid
106
+
107
+ def validate_email(self) -> bool:
108
+ """
109
+ Basic email validation for rater_email column.
110
+
111
+ Returns:
112
+ bool: True if validation passes, False otherwise
113
+ """
114
+ invalid_emails = self.df[~self.df["rater_email"].str.contains("@", na=False)]
115
+
116
+ if not invalid_emails.empty:
117
+ self.errors.append(
118
+ f"Invalid email addresses found in rows: {invalid_emails.index.tolist()}"
119
+ )
120
+ return False
121
+ return True
122
+
123
+ def validate_coa_names(self) -> bool:
124
+ """
125
+ Validate that coa_name values are in the allowed list.
126
+
127
+ Returns:
128
+ bool: True if validation passes, False otherwise
129
+ """
130
+ # Convert to lowercase for comparison
131
+ self.df["coa_name"] = self.df["coa_name"].str.strip()
132
+
133
+ invalid_coa = self.df[~self.df["coa_name"].isin(self.ALLOWED_COA_NAMES)]
134
+
135
+ if not invalid_coa.empty:
136
+ invalid_values = invalid_coa["coa_name"].unique().tolist()
137
+ self.errors.append(
138
+ f"Invalid coa_name values found: {invalid_values}. "
139
+ f"Allowed values are: {', '.join(self.ALLOWED_COA_NAMES)}"
140
+ )
141
+ return False
142
+ return True
143
+
144
+ def load_and_validate(self) -> bool:
145
+ """
146
+ Load CSV and run all validations.
147
+
148
+ Returns:
149
+ bool: True if all validations pass, False otherwise
150
+ """
151
+ try:
152
+ self.df = pd.read_csv(self.csv_path)
153
+ except Exception as e:
154
+ self.errors.append(f"Failed to load CSV: {str(e)}")
155
+ return False
156
+
157
+ # Strip whitespace from column names
158
+ self.df.columns = self.df.columns.str.strip()
159
+
160
+ validations = [
161
+ self.validate_columns(),
162
+ self.validate_data_types(),
163
+ self.validate_email(),
164
+ self.validate_coa_names(),
165
+ ]
166
+
167
+ return all(validations)
168
+
169
+ def transform_to_serializer_format(self) -> List[Dict[str, Any]]:
170
+ """
171
+ Transform CSV data to match BulkUploadSerializer format.
172
+ Groups rows by unique assessment (study, site, participant, visit, coa)
173
+ and creates actual_scores JSON structure.
174
+ Missing items are filled with score of 0.
175
+
176
+ Returns:
177
+ List of dictionaries matching the serializer format
178
+ """
179
+ if self.df is None:
180
+ raise ValueError("CSV not loaded. Call load_and_validate() first.")
181
+
182
+ # Group by unique assessment identifiers
183
+ grouping_cols = [
184
+ "study_id",
185
+ "site_id",
186
+ "rater_email",
187
+ "participant_id",
188
+ "visit_name",
189
+ "visit_order",
190
+ "coa_name",
191
+ "file_path",
192
+ ]
193
+
194
+ # Add optional columns that are present
195
+ optional_present = [
196
+ col for col in self.OPTIONAL_COLUMNS if col in self.df.columns
197
+ ]
198
+
199
+ results = []
200
+
201
+ for group_key, group_df in self.df.groupby(grouping_cols, dropna=False):
202
+ # Create base record
203
+ record = {}
204
+ for i, col in enumerate(grouping_cols):
205
+ record[col] = group_key[i]
206
+
207
+ # Add optional fields from first row of group
208
+ first_row = group_df.iloc[0]
209
+ for col in optional_present:
210
+ if col not in ["time_collected"]: # Exclude metadata
211
+ value = first_row[col]
212
+ if pd.notna(value):
213
+ record[col] = value
214
+
215
+ # Get expected item count for this COA
216
+ coa_name = record["coa_name"]
217
+ expected_items = self.COA_ITEM_COUNTS.get(coa_name, 10)
218
+
219
+ # Create a dictionary to store item scores
220
+ item_scores = {}
221
+
222
+ for _, row in group_df.iterrows():
223
+ item_num = int(row["coa_item_number"])
224
+ item_score = row["coa_item_value"]
225
+
226
+ # Convert to int if not null, otherwise use 0 as default
227
+ if pd.notna(item_score):
228
+ item_scores[item_num] = int(item_score)
229
+ else:
230
+ item_scores[item_num] = None
231
+
232
+ # Build sections with all expected items (fill missing with 0)
233
+ sections = []
234
+ total_score = 0
235
+
236
+ for item_num in range(1, expected_items + 1):
237
+ item_score = item_scores.get(item_num, None) # Default to 0 if missing
238
+ total_score = total_score + (
239
+ item_score if item_score is not None else 0
240
+ )
241
+
242
+ section = {
243
+ "section_id": f"s{item_num:02d}",
244
+ "section_notes": None,
245
+ "items": [
246
+ {"item_id": f"i{item_num:02d}", "item_score": item_score}
247
+ ],
248
+ }
249
+ sections.append(section)
250
+
251
+ actual_scores = {
252
+ "sections": sections,
253
+ "total_score": total_score,
254
+ "total_severity": None,
255
+ }
256
+
257
+ record["actual_scores"] = actual_scores
258
+ results.append(record)
259
+
260
+ return results
261
+
262
+ def create_final_csv(self) -> pd.DataFrame:
263
+ """
264
+ Create a final grouped CSV with actual_scores as JSON string.
265
+
266
+ Args:
267
+ output_path: Path where the final CSV should be saved
268
+
269
+ Returns:
270
+ DataFrame containing the grouped data
271
+ """
272
+ transformed_data = self.transform_to_serializer_format()
273
+
274
+ # Convert actual_scores dict to JSON string for CSV storage
275
+ for record in transformed_data:
276
+ record["actual_scores"] = json.dumps(record["actual_scores"])
277
+ record["force_upload"] = self.force_upload
278
+
279
+ self.transformed_df = pd.DataFrame(transformed_data)
280
+ return self.transformed_df
281
+
282
+ def get_errors(self) -> List[str]:
283
+ """
284
+ Get list of validation errors.
285
+
286
+ Returns:
287
+ List of error messages
288
+ """
289
+ return self.errors
290
+
291
+
292
+ class UploadUtils:
293
+ def __init__(self, row):
294
+ self.row = row
295
+
296
+ def validate_row(self) -> Tuple[bool, str | None]:
297
+ if not os.path.exists(self.row.file_path):
298
+ return (False, "File path does not exist")
299
+ if self.row.language not in LANGUAGE_CHOICES:
300
+ return (False, f"Invalid language: {self.row.language}")
301
+ return (True, None)
302
+
303
+ def generate_payload(self) -> Dict[str, Any]:
304
+ payload = {
305
+ "study_id": self.row.study_id,
306
+ "site_id": self.row.site_id,
307
+ "rater_email": self.row.rater_email,
308
+ "participant_id": self.row.participant_id,
309
+ "age": self.row.age,
310
+ "sex": self.row.sex,
311
+ "race": self.row.race,
312
+ "language": self.row.language,
313
+ "visit_name": self.row.visit_name,
314
+ "visit_order": int(self.row.visit_order),
315
+ "coa_name": self.row.coa_name,
316
+ "file_path": self.row.file_path,
317
+ "filename": os.path.basename(self.row.file_path),
318
+ "force_upload": self.row.force_upload,
319
+ "actual_scores": json.loads(self.row.actual_scores),
320
+ }
321
+ return payload
322
+
323
+ def post(
324
+ self, api_key: str, url: str, headers: Dict[str, str], payload: Dict[str, Any]
325
+ ) -> Dict[str, Any]:
326
+ try:
327
+ response = requests.post(url, headers=headers, json=payload)
328
+ res_json = response.json()
329
+
330
+ except Exception as ex:
331
+ return {"upload_status": "Failed", "error": str(ex), "response": None}
332
+ else:
333
+ if response.status_code not in [200, 201]:
334
+ return {"upload_status": "Failed", "error": res_json, "response": None}
335
+ else:
336
+ return {"upload_status": "Success", "response": res_json, "error": None}
@@ -22,6 +22,11 @@ class WillisapiClient:
22
22
  return f"https://{self.env}-{self.api_uri}/v{self.api_version}/"
23
23
  return f"https://{self.api_uri}/v{self.api_version}/"
24
24
 
25
+ def get_base_v2_url(self):
26
+ if self.env:
27
+ return f"https://{self.env}-{self.api_uri}/api/v2/"
28
+ return f"https://{self.api_uri}/api/v2/"
29
+
25
30
  def get_login_url(self):
26
31
  return self.get_base_url() + "login"
27
32
 
@@ -37,5 +42,8 @@ class WillisapiClient:
37
42
  def get_diarize(self):
38
43
  return self.get_base_url() + "diarize"
39
44
 
45
+ def get_metadata_upload_url(self):
46
+ return self.get_base_v2_url() + "metadata/upload"
47
+
40
48
  def get_headers(self):
41
49
  return {"Content-Type": "application/json", "Accept": "application/json"}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: willisapi-client
3
- Version: 1.9.1
3
+ Version: 1.9.2
4
4
  Summary: A Python client for willisapi
5
5
  Home-page: https://github.com/bklynhlth/willsiapi_client
6
6
  Author: bklynhlth
@@ -67,21 +67,13 @@ key, expiration = willisapi.login(username, password)
67
67
 
68
68
 
69
69
  ```
70
- summary = willisapi.upload(key, 'data.csv')
70
+ summary = willisapi.metadata_upload(key, 'data.csv')
71
71
  ```
72
72
 
73
73
 
74
74
  For more information on how to organize the `data.csv`, visit the [Github Wiki](http://www.github.com/bklynhlth/willisapi_client/wiki).
75
75
 
76
76
 
77
- #### Download
78
-
79
-
80
- ```
81
- measures = willisapi.download(key, 'project_name')
82
- ```
83
-
84
-
85
77
  If you run into trouble while using the client, please raise it in the [Issues](http://www.github.com/bklynhlth/willisapi_client/issues) tab.
86
78
 
87
79
  ***
@@ -35,6 +35,9 @@ willisapi_client/services/diarize/willisdiarize_call_remaining.py
35
35
  willisapi_client/services/download/__init__.py
36
36
  willisapi_client/services/download/download_handler.py
37
37
  willisapi_client/services/download/download_utils.py
38
+ willisapi_client/services/metadata/__init__.py
39
+ willisapi_client/services/metadata/upload.py
40
+ willisapi_client/services/metadata/utils.py
38
41
  willisapi_client/services/upload/__init__.py
39
42
  willisapi_client/services/upload/csv_validation.py
40
43
  willisapi_client/services/upload/language_choices.py