sws-api-client 1.3.0__tar.gz → 1.3.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.
- {sws_api_client-1.3.0 → sws_api_client-1.3.2}/PKG-INFO +1 -1
- {sws_api_client-1.3.0 → sws_api_client-1.3.2}/pyproject.toml +1 -1
- sws_api_client-1.3.2/sws_api_client/codelist.py +69 -0
- {sws_api_client-1.3.0 → sws_api_client-1.3.2}/sws_api_client/datasets.py +153 -6
- sws_api_client-1.3.2/sws_api_client/datatable.py +162 -0
- sws_api_client-1.3.2/sws_api_client/discover.py +201 -0
- {sws_api_client-1.3.0 → sws_api_client-1.3.2}/sws_api_client/plugins.py +73 -6
- {sws_api_client-1.3.0 → sws_api_client-1.3.2}/sws_api_client/tags.py +34 -0
- sws_api_client-1.3.2/sws_api_client/tasks.py +399 -0
- sws_api_client-1.3.0/sws_api_client/codelist.py +0 -30
- sws_api_client-1.3.0/sws_api_client/datatable.py +0 -79
- sws_api_client-1.3.0/sws_api_client/discover.py +0 -80
- sws_api_client-1.3.0/sws_api_client/tasks.py +0 -218
- {sws_api_client-1.3.0 → sws_api_client-1.3.2}/LICENSE +0 -0
- {sws_api_client-1.3.0 → sws_api_client-1.3.2}/README.md +0 -0
- {sws_api_client-1.3.0 → sws_api_client-1.3.2}/sws_api_client/__init__.py +0 -0
- {sws_api_client-1.3.0 → sws_api_client-1.3.2}/sws_api_client/auth.py +0 -0
- {sws_api_client-1.3.0 → sws_api_client-1.3.2}/sws_api_client/generic_models.py +0 -0
- {sws_api_client-1.3.0 → sws_api_client-1.3.2}/sws_api_client/sws_api_client.py +0 -0
- {sws_api_client-1.3.0 → sws_api_client-1.3.2}/sws_api_client/token.py +0 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Codelists Module for SWS API.
|
|
2
|
+
|
|
3
|
+
This module provides functionality for managing codelists and retrieving
|
|
4
|
+
codelist information through the SWS API client.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from time import sleep
|
|
9
|
+
import os
|
|
10
|
+
from pydantic import BaseModel, Extra
|
|
11
|
+
from typing import List, Optional, Dict
|
|
12
|
+
from sws_api_client.generic_models import Code, Multilanguage
|
|
13
|
+
from sws_api_client.sws_api_client import SwsApiClient
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
class CodelistModel(BaseModel, extra="allow"):
|
|
18
|
+
"""Model representing a codelist's metadata.
|
|
19
|
+
|
|
20
|
+
Attributes:
|
|
21
|
+
id (str): Unique identifier of the codelist
|
|
22
|
+
label (Multilanguage): Multilanguage labels for the codelist
|
|
23
|
+
type (str): Type of the codelist
|
|
24
|
+
"""
|
|
25
|
+
id: str
|
|
26
|
+
label: Multilanguage
|
|
27
|
+
type: str
|
|
28
|
+
|
|
29
|
+
class Codelist(BaseModel, extra="allow"):
|
|
30
|
+
"""Model representing a complete codelist with its codes.
|
|
31
|
+
|
|
32
|
+
Attributes:
|
|
33
|
+
model (CodelistModel): Metadata of the codelist
|
|
34
|
+
codes (List[Code]): List of codes belonging to this codelist
|
|
35
|
+
"""
|
|
36
|
+
model: CodelistModel
|
|
37
|
+
codes: List[Code]
|
|
38
|
+
|
|
39
|
+
class Codelists:
|
|
40
|
+
"""Class for managing codelist operations through the SWS API.
|
|
41
|
+
|
|
42
|
+
This class provides methods for retrieving codelist information
|
|
43
|
+
and managing codelist data.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
sws_client (SwsApiClient): An instance of the SWS API client
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(self, sws_client: SwsApiClient) -> None:
|
|
50
|
+
"""Initialize the Codelists manager with SWS client."""
|
|
51
|
+
self.sws_client = sws_client
|
|
52
|
+
|
|
53
|
+
def get_codelist(self, codelist_id: str, nocache: bool = False) -> Codelist:
|
|
54
|
+
"""Retrieve a codelist by its ID.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
codelist_id (str): The identifier of the codelist
|
|
58
|
+
nocache (bool, optional): Whether to bypass cache. Defaults to False.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Codelist: The requested codelist with its codes
|
|
62
|
+
|
|
63
|
+
Raises:
|
|
64
|
+
HTTPError: If the codelist cannot be retrieved
|
|
65
|
+
"""
|
|
66
|
+
url = f"/admin/reference/codelist/{codelist_id}?nocache={'true' if nocache else 'false'}"
|
|
67
|
+
|
|
68
|
+
response = self.sws_client.discoverable.get('is_api', url)
|
|
69
|
+
return Codelist(**response)
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
"""Datasets Module for SWS API.
|
|
2
|
+
|
|
3
|
+
This module provides functionality for managing datasets, including creation,
|
|
4
|
+
cloning, data import/export, and dimension management through the SWS API client.
|
|
5
|
+
"""
|
|
6
|
+
|
|
1
7
|
import logging
|
|
2
8
|
from time import sleep
|
|
3
9
|
import os
|
|
@@ -14,6 +20,16 @@ from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
|
14
20
|
logger = logging.getLogger(__name__)
|
|
15
21
|
|
|
16
22
|
class Lifecycle(BaseModel):
|
|
23
|
+
"""Model representing the lifecycle state of a dataset.
|
|
24
|
+
|
|
25
|
+
Attributes:
|
|
26
|
+
state (str): Current state of the dataset
|
|
27
|
+
type (str): Type of lifecycle
|
|
28
|
+
previousState (Optional[str]): Previous state if applicable
|
|
29
|
+
created (int): Creation timestamp
|
|
30
|
+
lastModified (int): Last modification timestamp
|
|
31
|
+
lastModifiedBy (str): User who last modified the dataset
|
|
32
|
+
"""
|
|
17
33
|
state: str
|
|
18
34
|
type: str
|
|
19
35
|
previousState: Optional[str]
|
|
@@ -22,14 +38,40 @@ class Lifecycle(BaseModel):
|
|
|
22
38
|
lastModifiedBy: str
|
|
23
39
|
|
|
24
40
|
class Domain(BaseModel):
|
|
41
|
+
"""Model representing a domain.
|
|
42
|
+
|
|
43
|
+
Attributes:
|
|
44
|
+
id (str): Domain identifier
|
|
45
|
+
label (Multilanguage): Multilanguage labels
|
|
46
|
+
description (Dict): Domain description in multiple languages
|
|
47
|
+
"""
|
|
25
48
|
id: str
|
|
26
49
|
label: Multilanguage
|
|
27
50
|
description: Dict
|
|
28
51
|
|
|
29
52
|
class Binding(BaseModel):
|
|
53
|
+
"""Model representing column binding information.
|
|
54
|
+
|
|
55
|
+
Attributes:
|
|
56
|
+
joinColumn (str): Column used for joining
|
|
57
|
+
"""
|
|
30
58
|
joinColumn: str
|
|
31
59
|
|
|
32
60
|
class Dimension(BaseModel):
|
|
61
|
+
"""Model representing a dataset dimension.
|
|
62
|
+
|
|
63
|
+
Attributes:
|
|
64
|
+
id (str): Dimension identifier
|
|
65
|
+
label (Multilanguage): Multilanguage labels
|
|
66
|
+
description (Dict): Dimension description
|
|
67
|
+
sdmxName (str): SDMX name
|
|
68
|
+
codelist (str): Associated codelist
|
|
69
|
+
roots (List[str]): Root codes
|
|
70
|
+
binding (Binding): Column binding information
|
|
71
|
+
checkValidityPeriod (bool): Whether to check validity period
|
|
72
|
+
formulas (List): Associated formulas
|
|
73
|
+
type (str): Dimension type
|
|
74
|
+
"""
|
|
33
75
|
id: str
|
|
34
76
|
label: Multilanguage
|
|
35
77
|
description: Dict
|
|
@@ -98,13 +140,29 @@ class MappedCode(BaseModel):
|
|
|
98
140
|
code: Code
|
|
99
141
|
include: bool
|
|
100
142
|
class Datasets:
|
|
143
|
+
"""Class for managing dataset operations through the SWS API.
|
|
144
|
+
|
|
145
|
+
This class provides methods for creating, cloning, importing data,
|
|
146
|
+
and managing dataset dimensions.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
sws_client (SwsApiClient): An instance of the SWS API client
|
|
150
|
+
"""
|
|
101
151
|
|
|
102
152
|
def __init__(self, sws_client: SwsApiClient) -> None:
|
|
153
|
+
"""Initialize the Datasets manager with SWS client."""
|
|
103
154
|
self.sws_client = sws_client
|
|
104
155
|
self.codelists = Codelists(sws_client)
|
|
105
156
|
|
|
106
157
|
def get_dataset_export_details(self, dataset_id: str) -> dict:
|
|
158
|
+
"""Get export details for a dataset.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
dataset_id (str): Dataset identifier
|
|
107
162
|
|
|
163
|
+
Returns:
|
|
164
|
+
dict: Dataset export information
|
|
165
|
+
"""
|
|
108
166
|
url = f"/dataset/{dataset_id}/info"
|
|
109
167
|
params = {"extended": "true"}
|
|
110
168
|
|
|
@@ -113,14 +171,28 @@ class Datasets:
|
|
|
113
171
|
return response
|
|
114
172
|
|
|
115
173
|
def get_dataset_info(self, dataset_id: str) -> DataModel:
|
|
174
|
+
"""Retrieve dataset information.
|
|
116
175
|
|
|
176
|
+
Args:
|
|
177
|
+
dataset_id (str): Dataset identifier
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
DataModel: Complete dataset information model
|
|
181
|
+
"""
|
|
117
182
|
url = f"/admin/dataset/{dataset_id}"
|
|
118
183
|
|
|
119
184
|
response = self.sws_client.discoverable.get('is_api', url)
|
|
120
185
|
return DataModel(**response)
|
|
121
186
|
|
|
122
187
|
def create_dataset(self, dataset: Dataset) -> DataModel:
|
|
188
|
+
"""Create a new dataset.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
dataset (Dataset): Dataset configuration
|
|
123
192
|
|
|
193
|
+
Returns:
|
|
194
|
+
DataModel: Created dataset model
|
|
195
|
+
"""
|
|
124
196
|
url = "/admin/dataset"
|
|
125
197
|
|
|
126
198
|
response = self.sws_client.discoverable.post('is_api', url, data=dataset.model_dump())
|
|
@@ -128,14 +200,29 @@ class Datasets:
|
|
|
128
200
|
return response
|
|
129
201
|
|
|
130
202
|
def clone_dataset(self, dataset_id: str, new_id: str) -> DataModel:
|
|
203
|
+
"""Clone an existing dataset with a new identifier.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
dataset_id (str): Source dataset identifier
|
|
207
|
+
new_id (str): New dataset identifier
|
|
131
208
|
|
|
209
|
+
Returns:
|
|
210
|
+
DataModel: Cloned dataset model
|
|
211
|
+
"""
|
|
132
212
|
dataset = self.get_dataset_info(dataset_id)
|
|
133
213
|
dataset.dataset.id = new_id
|
|
134
214
|
new_dataset = self.create_dataset(dataset.dataset)
|
|
135
215
|
return new_dataset
|
|
136
216
|
|
|
137
217
|
def get_job_status(self, jobId: str) -> dict:
|
|
218
|
+
"""Check status of a job.
|
|
138
219
|
|
|
220
|
+
Args:
|
|
221
|
+
jobId (str): Job identifier
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
dict: Job status information with result and success flags
|
|
225
|
+
"""
|
|
139
226
|
url = f"/job/status/{jobId}"
|
|
140
227
|
|
|
141
228
|
response = self.sws_client.discoverable.get('is_api', url)
|
|
@@ -145,10 +232,29 @@ class Datasets:
|
|
|
145
232
|
return dict(result=result, success=success)
|
|
146
233
|
|
|
147
234
|
def import_data(self, dataset_id: str, file_path, sessionId = None, zip=False ) -> bool:
|
|
235
|
+
"""Import data into a dataset.
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
dataset_id (str): Target dataset identifier
|
|
239
|
+
file_path: Path to data file
|
|
240
|
+
sessionId (Optional[int]): Session identifier
|
|
241
|
+
zip (bool): Whether to zip the file
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
bool: True if import successful
|
|
245
|
+
"""
|
|
148
246
|
return self.import_data_chunk(dataset_id, file_path, sessionId)
|
|
149
247
|
|
|
150
248
|
|
|
151
249
|
def get_dataset_dimension_codes(self, dataset_id: str) -> Dict[str, List[Code]]:
|
|
250
|
+
"""Get codes for all dimensions in a dataset.
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
dataset_id (str): Dataset identifier
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
Dict[str, List[Code]]: Dictionary mapping dimension IDs to their codes
|
|
257
|
+
"""
|
|
152
258
|
dataset_info = self.get_dataset_info(dataset_id)
|
|
153
259
|
dimensions = dataset_info.dataset.dimensions.dimensions
|
|
154
260
|
|
|
@@ -188,7 +294,19 @@ class Datasets:
|
|
|
188
294
|
return dimensions_codes
|
|
189
295
|
|
|
190
296
|
def import_data_chunk(self, dataset_id: str, file_path: str, sessionId: Optional[int] = None) -> dict:
|
|
191
|
-
"""
|
|
297
|
+
"""Import a single chunk of data, automatically handling file compression.
|
|
298
|
+
|
|
299
|
+
Args:
|
|
300
|
+
dataset_id (str): Target dataset identifier
|
|
301
|
+
file_path (str): Path to data file
|
|
302
|
+
sessionId (Optional[int]): Session identifier
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
dict: Import job result
|
|
306
|
+
|
|
307
|
+
Raises:
|
|
308
|
+
OSError: If file operations fail
|
|
309
|
+
"""
|
|
192
310
|
# Check file size and zip if greater than 10MB
|
|
193
311
|
zip_file_path = None
|
|
194
312
|
if os.path.getsize(file_path) > 10 * 1024 * 1024: # 10MB in bytes
|
|
@@ -232,7 +350,14 @@ class Datasets:
|
|
|
232
350
|
return self.get_job_result(job_id)
|
|
233
351
|
|
|
234
352
|
def get_job_result(self, job_id: str) -> bool:
|
|
235
|
-
"""
|
|
353
|
+
"""Poll for job completion and return result.
|
|
354
|
+
|
|
355
|
+
Args:
|
|
356
|
+
job_id (str): Job identifier
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
bool: True if job completed successfully
|
|
360
|
+
"""
|
|
236
361
|
while True:
|
|
237
362
|
logger.debug(f"Checking job status for job ID {job_id}")
|
|
238
363
|
job_status = self.get_job_status(job_id)
|
|
@@ -241,7 +366,19 @@ class Datasets:
|
|
|
241
366
|
sleep(5)
|
|
242
367
|
|
|
243
368
|
def chunk_csv_file(self, file_path: str, chunk_size: int) -> List[str]:
|
|
244
|
-
"""
|
|
369
|
+
"""Split CSV file into smaller chunks while preserving data format.
|
|
370
|
+
|
|
371
|
+
Args:
|
|
372
|
+
file_path (str): Path to CSV file
|
|
373
|
+
chunk_size (int): Number of rows per chunk
|
|
374
|
+
|
|
375
|
+
Returns:
|
|
376
|
+
List[str]: Paths to chunk files
|
|
377
|
+
|
|
378
|
+
Raises:
|
|
379
|
+
OSError: If file operations fail
|
|
380
|
+
pd.errors.EmptyDataError: If CSV file is empty
|
|
381
|
+
"""
|
|
245
382
|
temp_files = []
|
|
246
383
|
|
|
247
384
|
# Load the data in chunks and process each chunk
|
|
@@ -257,8 +394,19 @@ class Datasets:
|
|
|
257
394
|
return temp_files
|
|
258
395
|
|
|
259
396
|
def import_data_concurrent(self, dataset_id: str, file_path: str, sessionId: Optional[int] = None, chunk_size: int = 10000, max_workers: int = 5) -> None:
|
|
260
|
-
"""
|
|
261
|
-
|
|
397
|
+
"""Import data in parallel using multiple threads.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
dataset_id (str): Target dataset identifier
|
|
401
|
+
file_path (str): Path to data file
|
|
402
|
+
sessionId (Optional[int]): Session identifier
|
|
403
|
+
chunk_size (int): Rows per chunk
|
|
404
|
+
max_workers (int): Maximum number of concurrent imports
|
|
405
|
+
|
|
406
|
+
Raises:
|
|
407
|
+
OSError: If file operations fail
|
|
408
|
+
Exception: If import operations fail
|
|
409
|
+
"""
|
|
262
410
|
# Step 1: Split the file into chunks
|
|
263
411
|
chunk_files = self.chunk_csv_file(file_path, chunk_size)
|
|
264
412
|
total_chunks = len(chunk_files) # Total number of chunks for progress tracking
|
|
@@ -297,4 +445,3 @@ class Datasets:
|
|
|
297
445
|
logger.info("Importing chunks: completed")
|
|
298
446
|
|
|
299
447
|
|
|
300
|
-
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"""Datatables Module for SWS API.
|
|
2
|
+
|
|
3
|
+
This module provides functionality for managing datatables, including retrieving
|
|
4
|
+
information, exporting data, and handling CSV paths through the SWS API client.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from pydantic import BaseModel, Field, HttpUrl
|
|
9
|
+
from typing import List, Optional, Dict, Any
|
|
10
|
+
from sws_api_client.sws_api_client import SwsApiClient
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
from datetime import datetime, time
|
|
13
|
+
from time import sleep
|
|
14
|
+
|
|
15
|
+
class LabelModel(BaseModel):
|
|
16
|
+
"""Model for multilingual labels.
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
en (str): English label text
|
|
20
|
+
"""
|
|
21
|
+
en: str
|
|
22
|
+
|
|
23
|
+
class DescriptionModel(BaseModel):
|
|
24
|
+
"""Model for multilingual descriptions.
|
|
25
|
+
|
|
26
|
+
Attributes:
|
|
27
|
+
en (str): English description text
|
|
28
|
+
"""
|
|
29
|
+
en: str
|
|
30
|
+
|
|
31
|
+
class ColumnModel(BaseModel):
|
|
32
|
+
"""Model representing a datatable column.
|
|
33
|
+
|
|
34
|
+
Attributes:
|
|
35
|
+
id (str): Column identifier
|
|
36
|
+
label (LabelModel): Column labels in different languages
|
|
37
|
+
description (DescriptionModel): Column descriptions in different languages
|
|
38
|
+
type (str): Data type of the column
|
|
39
|
+
constraints (List[Any]): List of column constraints
|
|
40
|
+
defaultValue (Optional[Any]): Default value for the column
|
|
41
|
+
facets (List[Any]): List of column facets
|
|
42
|
+
"""
|
|
43
|
+
id: str
|
|
44
|
+
label: LabelModel
|
|
45
|
+
description: DescriptionModel
|
|
46
|
+
type: str
|
|
47
|
+
constraints: List[Any]
|
|
48
|
+
defaultValue: Optional[Any]
|
|
49
|
+
facets: List[Any]
|
|
50
|
+
|
|
51
|
+
class DataModel(BaseModel):
|
|
52
|
+
"""Model representing datatable data status.
|
|
53
|
+
|
|
54
|
+
Attributes:
|
|
55
|
+
url (Optional[str]): URL to access the data
|
|
56
|
+
available (bool): Whether the data is available
|
|
57
|
+
uptodate (bool): Whether the data is up to date
|
|
58
|
+
"""
|
|
59
|
+
url: Optional[str]
|
|
60
|
+
available: bool
|
|
61
|
+
uptodate: bool
|
|
62
|
+
|
|
63
|
+
class DatatableModel(BaseModel):
|
|
64
|
+
"""Model representing a complete datatable.
|
|
65
|
+
|
|
66
|
+
Attributes:
|
|
67
|
+
id (str): Datatable identifier
|
|
68
|
+
name (str): Name of the datatable
|
|
69
|
+
label (LabelModel): Labels in different languages
|
|
70
|
+
description (DescriptionModel): Descriptions in different languages
|
|
71
|
+
schema_name (str): Schema name
|
|
72
|
+
domains (List[str]): List of associated domains
|
|
73
|
+
plugins (List[Any]): List of associated plugins
|
|
74
|
+
columns (List[ColumnModel]): List of table columns
|
|
75
|
+
facets (List[Any]): List of table facets
|
|
76
|
+
last_update (datetime): Last update timestamp
|
|
77
|
+
data (DataModel): Data status information
|
|
78
|
+
"""
|
|
79
|
+
id: str
|
|
80
|
+
name: str
|
|
81
|
+
label: LabelModel
|
|
82
|
+
description: DescriptionModel
|
|
83
|
+
schema_name: str = Field(..., alias="schema")
|
|
84
|
+
domains: List[str]
|
|
85
|
+
plugins: List[Any]
|
|
86
|
+
columns: List[ColumnModel]
|
|
87
|
+
facets: List[Any]
|
|
88
|
+
last_update: datetime
|
|
89
|
+
data: DataModel
|
|
90
|
+
|
|
91
|
+
class Datatables:
|
|
92
|
+
"""Class for managing datatable operations through the SWS API.
|
|
93
|
+
|
|
94
|
+
This class provides methods for retrieving datatable information
|
|
95
|
+
and managing data exports.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
sws_client (SwsApiClient): An instance of the SWS API client
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
def __init__(self, sws_client: SwsApiClient) -> None:
|
|
102
|
+
"""Initialize the Datatables manager with SWS client."""
|
|
103
|
+
self.sws_client = sws_client
|
|
104
|
+
|
|
105
|
+
def get_datatable_info(self, datatable_id: str) -> DatatableModel:
|
|
106
|
+
"""Retrieve information about a specific datatable.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
datatable_id (str): The identifier of the datatable
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
DatatableModel: Datatable information if found, None otherwise
|
|
113
|
+
"""
|
|
114
|
+
url = f"/datatables/{datatable_id}"
|
|
115
|
+
|
|
116
|
+
response = self.sws_client.discoverable.get('datatable_api', url)
|
|
117
|
+
if(response.get('id') is not None):
|
|
118
|
+
return DatatableModel(**response)
|
|
119
|
+
else:
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
def invoke_export_datatable(self, datatable_id: str) -> Dict:
|
|
123
|
+
"""Trigger an export operation for a datatable.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
datatable_id (str): The identifier of the datatable
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Dict: Response containing export operation status
|
|
130
|
+
"""
|
|
131
|
+
url = f"/datatables/{datatable_id}/invoke_export_2_s3"
|
|
132
|
+
|
|
133
|
+
response = self.sws_client.discoverable.post('datatable_api', url)
|
|
134
|
+
|
|
135
|
+
return response
|
|
136
|
+
|
|
137
|
+
def get_datatable_csv_path(self, datatable, timeout=60*15, interval=2) -> HttpUrl:
|
|
138
|
+
"""Get the CSV file path for a datatable, waiting for export if necessary.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
datatable: The datatable identifier
|
|
142
|
+
timeout (int): Maximum time to wait for export in seconds (default: 15 minutes)
|
|
143
|
+
interval (int): Time between checks in seconds (default: 2)
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
HttpUrl: URL to the CSV file
|
|
147
|
+
|
|
148
|
+
Raises:
|
|
149
|
+
TimeoutError: If export doesn't complete within timeout period
|
|
150
|
+
"""
|
|
151
|
+
info = self.get_datatable_info(datatable)
|
|
152
|
+
if info.data.available and info.data.uptodate:
|
|
153
|
+
return info.data.url
|
|
154
|
+
else:
|
|
155
|
+
self.invoke_export_datatable(datatable)
|
|
156
|
+
# get the updated info every interval seconds until the data are available to a maximum of timeout seconds
|
|
157
|
+
start = datetime.now()
|
|
158
|
+
while (datetime.now() - start).seconds < timeout:
|
|
159
|
+
info = self.get_datatable_info(datatable)
|
|
160
|
+
if info.data.available:
|
|
161
|
+
return info.data.url
|
|
162
|
+
sleep(interval)
|