sws-api-client 0.0.2__tar.gz → 0.0.3.dev1__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-0.0.2/sws_api_client.egg-info → sws_api_client-0.0.3.dev1}/PKG-INFO +2 -1
- {sws_api_client-0.0.2 → sws_api_client-0.0.3.dev1}/setup.py +2 -2
- sws_api_client-0.0.3.dev1/sws_api_client/__init__.py +11 -0
- sws_api_client-0.0.3.dev1/sws_api_client/datasets.py +15 -0
- sws_api_client-0.0.3.dev1/sws_api_client/discover.py +51 -0
- {sws_api_client-0.0.2 → sws_api_client-0.0.3.dev1}/sws_api_client/sws_api_client.py +29 -1
- sws_api_client-0.0.3.dev1/sws_api_client/tags.py +19 -0
- sws_api_client-0.0.3.dev1/sws_api_client/tasks.py +160 -0
- {sws_api_client-0.0.2 → sws_api_client-0.0.3.dev1/sws_api_client.egg-info}/PKG-INFO +2 -1
- {sws_api_client-0.0.2 → sws_api_client-0.0.3.dev1}/sws_api_client.egg-info/SOURCES.txt +4 -0
- {sws_api_client-0.0.2 → sws_api_client-0.0.3.dev1}/sws_api_client.egg-info/requires.txt +1 -0
- sws_api_client-0.0.2/sws_api_client/__init__.py +0 -1
- {sws_api_client-0.0.2 → sws_api_client-0.0.3.dev1}/LICENSE +0 -0
- {sws_api_client-0.0.2 → sws_api_client-0.0.3.dev1}/README.md +0 -0
- {sws_api_client-0.0.2 → sws_api_client-0.0.3.dev1}/setup.cfg +0 -0
- {sws_api_client-0.0.2 → sws_api_client-0.0.3.dev1}/sws_api_client.egg-info/dependency_links.txt +0 -0
- {sws_api_client-0.0.2 → sws_api_client-0.0.3.dev1}/sws_api_client.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sws_api_client
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3.dev1
|
|
4
4
|
Summary: A Python client to easily interact with the FAO SWS REST APIs
|
|
5
5
|
Home-page: https://bitbucket.org/cioapps/sws-it-python-api-client/src/main/
|
|
6
6
|
Author: Mansillo, Daniele (CSI)
|
|
@@ -16,6 +16,7 @@ Classifier: Operating System :: Microsoft :: Windows
|
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
18
|
Requires-Dist: requests>=2.31.0
|
|
19
|
+
Requires-Dist: dataclasses>=0.6
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
# SWS API Client
|
|
@@ -7,7 +7,7 @@ here = os.path.abspath(os.path.dirname(__file__))
|
|
|
7
7
|
with open(os.path.join(here, "README.md"), encoding="utf-8") as fh:
|
|
8
8
|
long_description = "\n" + fh.read()
|
|
9
9
|
|
|
10
|
-
VERSION = "0.0.
|
|
10
|
+
VERSION = "0.0.3-dev1"
|
|
11
11
|
|
|
12
12
|
# Setting up
|
|
13
13
|
setup(
|
|
@@ -19,7 +19,7 @@ setup(
|
|
|
19
19
|
long_description_content_type="text/markdown",
|
|
20
20
|
long_description=long_description,
|
|
21
21
|
packages=find_packages(),
|
|
22
|
-
install_requires=["requests>=2.31.0"],
|
|
22
|
+
install_requires=["requests>=2.31.0","dataclasses>=0.6"],
|
|
23
23
|
keywords=["python", "fao", "sws", "rest", "api", "client"],
|
|
24
24
|
classifiers=[
|
|
25
25
|
"Development Status :: 1 - Planning",
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from sws_api_client.sws_api_client import SwsApiClient
|
|
2
|
+
|
|
3
|
+
class Datasets:
|
|
4
|
+
|
|
5
|
+
def __init__(self, sws_client: SwsApiClient) -> None:
|
|
6
|
+
self.sws_client = sws_client
|
|
7
|
+
|
|
8
|
+
def get_dataset_export_details(self, dataset_id: str) -> dict:
|
|
9
|
+
|
|
10
|
+
url = f"/dataset/{dataset_id}/info"
|
|
11
|
+
params = {"extended": "true"}
|
|
12
|
+
|
|
13
|
+
response = self.sws_client.discoverable.get('session_api', url, params=params)
|
|
14
|
+
|
|
15
|
+
return response
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
class Discover:
|
|
4
|
+
|
|
5
|
+
def __init__(self,access_token:str, discover:dict) -> None:
|
|
6
|
+
self.discover = discover
|
|
7
|
+
self.access_token = access_token
|
|
8
|
+
|
|
9
|
+
def call(self, method: str, endpoint: str, path: str, params: dict = None, headers: dict = None, data: dict = None) -> dict:
|
|
10
|
+
if not endpoint:
|
|
11
|
+
raise ValueError("An endpoint must be provided.")
|
|
12
|
+
|
|
13
|
+
if endpoint not in self.discover or 'path' not in self.discover[endpoint]:
|
|
14
|
+
raise ValueError(f"endpoint '{endpoint}' not found")
|
|
15
|
+
|
|
16
|
+
x_api_key = self.discover[endpoint].get("key", "")
|
|
17
|
+
full_path = f"{self.discover[endpoint]['path']}{path}"
|
|
18
|
+
full_headers = {"Authorization": self.access_token}
|
|
19
|
+
if x_api_key:
|
|
20
|
+
full_headers["x-api-key"] = x_api_key
|
|
21
|
+
|
|
22
|
+
if headers:
|
|
23
|
+
full_headers.update(headers)
|
|
24
|
+
|
|
25
|
+
request_func = getattr(requests, method.lower())
|
|
26
|
+
response = request_func(full_path, params=params, headers=full_headers, json=data)
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
response.raise_for_status()
|
|
30
|
+
return response.json()
|
|
31
|
+
except requests.exceptions.HTTPError as errh:
|
|
32
|
+
print(f"HTTP Error: {errh}")
|
|
33
|
+
print(f"HTTP Status Code: {errh.response.status_code}")
|
|
34
|
+
print(f"Response Text: {errh.response.text}")
|
|
35
|
+
print(f"Request URL: {errh.request.url}")
|
|
36
|
+
except requests.exceptions.RequestException as err:
|
|
37
|
+
print(f"Request Exception: {err}")
|
|
38
|
+
|
|
39
|
+
return {}
|
|
40
|
+
|
|
41
|
+
def get(self, endpoint, path: str, params: dict = None, headers: dict = None) -> dict:
|
|
42
|
+
return self.call("GET", endpoint, path, params=params, headers=headers)
|
|
43
|
+
|
|
44
|
+
def post(self, endpoint, path: str, data: dict = None, params: dict = None, headers: dict = None) -> dict:
|
|
45
|
+
return self.call("POST", endpoint, path, params=params, headers=headers, data=data)
|
|
46
|
+
|
|
47
|
+
def put(self, endpoint, path: str, data: dict = None, params: dict = None, headers: dict = None) -> dict:
|
|
48
|
+
return self.call("PUT", endpoint, path, params=params, headers=headers, data=data)
|
|
49
|
+
|
|
50
|
+
def delete(self, endpoint, path: str, params: dict = None, headers: dict = None) -> dict:
|
|
51
|
+
return self.call("DELETE", endpoint, path, params=params, headers=headers)
|
|
@@ -2,15 +2,22 @@ import argparse
|
|
|
2
2
|
import json
|
|
3
3
|
import os
|
|
4
4
|
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
5
7
|
import requests
|
|
6
8
|
|
|
9
|
+
from sws_api_client.discover import Discover
|
|
7
10
|
|
|
8
11
|
class SwsApiClient:
|
|
9
12
|
|
|
10
|
-
def __init__(self, sws_endpoint: str, access_token: str) -> None:
|
|
13
|
+
def __init__(self, sws_endpoint: str, access_token: str, current_task_id:Optional[str], current_execution_id:Optional[str] ) -> None:
|
|
11
14
|
self.sws_endpoint = sws_endpoint
|
|
12
15
|
self.access_token = access_token
|
|
16
|
+
self.current_task_id = current_task_id
|
|
17
|
+
self.current_execution_id = current_execution_id
|
|
13
18
|
self.discover = self.__get_discover()
|
|
19
|
+
self.is_debug = self.check_debug()
|
|
20
|
+
self.discoverable = Discover(self.access_token, self.discover)
|
|
14
21
|
|
|
15
22
|
def __get_discover(self) -> dict:
|
|
16
23
|
discover_endpoint = f"{self.sws_endpoint}/discover?v=2"
|
|
@@ -22,6 +29,8 @@ class SwsApiClient:
|
|
|
22
29
|
return cls(
|
|
23
30
|
sws_endpoint=os.getenv(sws_endpoint_env),
|
|
24
31
|
access_token=os.getenv(access_token_env),
|
|
32
|
+
currentTaskId = os.getenv("TASK_ID"),
|
|
33
|
+
currentExecutionId = os.getenv("EXECUTION_ID")
|
|
25
34
|
)
|
|
26
35
|
|
|
27
36
|
@classmethod
|
|
@@ -41,10 +50,29 @@ class SwsApiClient:
|
|
|
41
50
|
parser.add_argument(
|
|
42
51
|
"--access_token", type=str, required=True, help="The access token"
|
|
43
52
|
)
|
|
53
|
+
parser.add_argument(
|
|
54
|
+
"--currentTaskId", type=str, required=False, help="The current task ID"
|
|
55
|
+
)
|
|
56
|
+
parser.add_argument(
|
|
57
|
+
"--currentExecutionId", type=str, required=False, help="The current execution ID"
|
|
58
|
+
)
|
|
44
59
|
|
|
45
60
|
args, _ = parser.parse_known_args()
|
|
46
61
|
|
|
47
62
|
return cls(**vars(args))
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def check_debug(self):
|
|
66
|
+
return os.getenv("DEBUG_MODE") == "TRUE" or os.getenv("DEBUG_MODE") is None
|
|
67
|
+
|
|
68
|
+
@classmethod
|
|
69
|
+
def auto(cls):
|
|
70
|
+
debug = cls.check_debug()
|
|
71
|
+
if debug:
|
|
72
|
+
return cls.from_conf()
|
|
73
|
+
else:
|
|
74
|
+
return cls.from_env()
|
|
75
|
+
|
|
48
76
|
|
|
49
77
|
def get_dataset_export_details(self, dataset_id: str) -> dict:
|
|
50
78
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# task_manager.py
|
|
2
|
+
from sws_api_client.sws_api_client import SwsApiClient
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import List, Dict, Optional
|
|
5
|
+
|
|
6
|
+
class Tags:
|
|
7
|
+
|
|
8
|
+
def __init__(self, sws_client: SwsApiClient, endpoint: str = 'tag_api') -> None:
|
|
9
|
+
self.sws_client = sws_client
|
|
10
|
+
self.endpoint = endpoint
|
|
11
|
+
|
|
12
|
+
def get_read_access_url(self, path: str, expiration: int) -> dict:
|
|
13
|
+
|
|
14
|
+
url = f"/tags/dissemination/getReadAccessUrl"
|
|
15
|
+
body = {"path": path, "expiration": expiration}
|
|
16
|
+
|
|
17
|
+
response = self.sws_client.discoverable.post(self.endpoint, url, data=body)
|
|
18
|
+
|
|
19
|
+
return response
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
from sws_api_client.sws_api_client import SwsApiClient
|
|
2
|
+
from dataclasses import (dataclass, asdict)
|
|
3
|
+
from typing import List, Dict, Optional, Literal
|
|
4
|
+
import json
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class TaskDataset:
|
|
9
|
+
domain: str
|
|
10
|
+
dataset: str
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class PluginPayload:
|
|
14
|
+
datasets: List[TaskDataset]
|
|
15
|
+
parameters: Dict
|
|
16
|
+
|
|
17
|
+
def to_dict(self) -> Dict:
|
|
18
|
+
return asdict(self)
|
|
19
|
+
|
|
20
|
+
# Define Status and DetailStatus as Literal types
|
|
21
|
+
Status = Literal['ACTIVE', 'ARCHIVED']
|
|
22
|
+
DetailStatus = Literal['CREATED', 'EXECUTION_PREPARED', 'EXECUTION_PROCESSING', 'EXECUTION_PROCESSED', 'STOP_REQUESTED', 'RETRIED', 'ENDED', 'ARCHIVED']
|
|
23
|
+
Outcome = Literal['SUCCESS', 'FAILURE']
|
|
24
|
+
@dataclass
|
|
25
|
+
class TaskInfo:
|
|
26
|
+
detail_status: DetailStatus
|
|
27
|
+
ended_on: Optional[str]
|
|
28
|
+
description: str
|
|
29
|
+
updated_on: str
|
|
30
|
+
created_on: str
|
|
31
|
+
service_user: str
|
|
32
|
+
tags: Dict[str, str]
|
|
33
|
+
output: Dict
|
|
34
|
+
input: Dict
|
|
35
|
+
task_type: str
|
|
36
|
+
context: str
|
|
37
|
+
progress: int
|
|
38
|
+
user: str
|
|
39
|
+
outcome: Outcome
|
|
40
|
+
group: Optional[str]
|
|
41
|
+
status: Status
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class TaskResponse:
|
|
45
|
+
task_id: str
|
|
46
|
+
info: TaskInfo
|
|
47
|
+
|
|
48
|
+
class TaskManager:
|
|
49
|
+
|
|
50
|
+
def __init__(self, sws_client: SwsApiClient, endpoint: str = 'task_manager_api') -> None:
|
|
51
|
+
self.sws_client = sws_client
|
|
52
|
+
self.endpoint = endpoint
|
|
53
|
+
|
|
54
|
+
def update_current(self, progress: Optional[int] = None):
|
|
55
|
+
if not self.sws_client.current_task_id:
|
|
56
|
+
raise ValueError("A current task ID must be provided.")
|
|
57
|
+
if not self.sws_client.current_execution_id:
|
|
58
|
+
raise ValueError("A current task ID must be provided.")
|
|
59
|
+
taskId = self.sws_client.current_task_id
|
|
60
|
+
executionId = self.sws_client.current_execution_id
|
|
61
|
+
|
|
62
|
+
path = f'/task/{taskId}/execution/{executionId}/status'
|
|
63
|
+
data = {
|
|
64
|
+
'progress': progress
|
|
65
|
+
}
|
|
66
|
+
self.sws_client.discoverable.put(self.endpoint, path, data=data)
|
|
67
|
+
|
|
68
|
+
def create_plugin_task(self,
|
|
69
|
+
pluginId:str,
|
|
70
|
+
slow:bool,
|
|
71
|
+
payload: PluginPayload,
|
|
72
|
+
description: Optional[str],
|
|
73
|
+
group: Optional[str] = None,
|
|
74
|
+
parentTaskId: Optional[str] = None,
|
|
75
|
+
user:Optional[str] = None
|
|
76
|
+
) -> TaskResponse:
|
|
77
|
+
|
|
78
|
+
path = 'task/create'
|
|
79
|
+
|
|
80
|
+
data = {
|
|
81
|
+
"user": user,
|
|
82
|
+
"context": "IS",
|
|
83
|
+
"type": "RUN_PLUGIN",
|
|
84
|
+
"description": f"Run plugin {pluginId}" if description is None else description,
|
|
85
|
+
"input":{
|
|
86
|
+
"slow": slow,
|
|
87
|
+
"pluginId": pluginId,
|
|
88
|
+
"payload": payload.to_dict()
|
|
89
|
+
},
|
|
90
|
+
"config": {
|
|
91
|
+
"repeatable":True
|
|
92
|
+
},
|
|
93
|
+
"parentTaskId": parentTaskId
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if group:
|
|
97
|
+
data['group'] = group
|
|
98
|
+
|
|
99
|
+
response = self.sws_client.discoverable.post(self.endpoint, path, data=data)
|
|
100
|
+
if response:
|
|
101
|
+
return self.get_task_response(response)
|
|
102
|
+
else:
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
def get_task_response(self, task: Dict) -> TaskResponse:
|
|
106
|
+
return TaskResponse(
|
|
107
|
+
task_id=task['taskId'],
|
|
108
|
+
info=TaskInfo(
|
|
109
|
+
detail_status=task['info'].get('detailStatus'),
|
|
110
|
+
ended_on=task['info'].get('endedOn'),
|
|
111
|
+
description=task['info'].get('description'),
|
|
112
|
+
updated_on=task['info'].get('updatedOn'),
|
|
113
|
+
created_on=task['info'].get('createdOn'),
|
|
114
|
+
service_user=task['info'].get('serviceUser'),
|
|
115
|
+
tags=task['info'].get('tags'),
|
|
116
|
+
output=task['info'].get('output'),
|
|
117
|
+
input=json.loads(task['info'].get('input', '{}')), # Default to an empty dictionary if 'input' is missing
|
|
118
|
+
task_type=task['info'].get('taskType'),
|
|
119
|
+
context=task['info'].get('context'),
|
|
120
|
+
progress=task['info'].get('progress'),
|
|
121
|
+
user=task['info'].get('user'),
|
|
122
|
+
outcome=task['info'].get('outcome'),
|
|
123
|
+
status=task['info'].get('status'),
|
|
124
|
+
group=task['info'].get('group')
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def get_task(self, task_id: str) -> Optional[TaskResponse]:
|
|
130
|
+
path = f'/task/{task_id}'
|
|
131
|
+
response = self.sws_client.discoverable.get(self.endpoint, path)
|
|
132
|
+
|
|
133
|
+
if response:
|
|
134
|
+
return self.get_task_response(response)
|
|
135
|
+
else:
|
|
136
|
+
return None
|
|
137
|
+
|
|
138
|
+
def wait_completion(self, task_id: str, poll_interval: int = 10) -> TaskResponse:
|
|
139
|
+
"""
|
|
140
|
+
Wait for a task to reach the 'ENDED' status and return the task outcome.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
task_id (str): The ID of the task.
|
|
144
|
+
poll_interval (int, optional): The interval (in seconds) between status checks. Defaults to 10.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
str: The final outcome of the task.
|
|
148
|
+
"""
|
|
149
|
+
while True:
|
|
150
|
+
task_response = self.get_task(task_id)
|
|
151
|
+
|
|
152
|
+
if not task_response:
|
|
153
|
+
raise ValueError(f"Task with ID {task_id} not found.")
|
|
154
|
+
|
|
155
|
+
task_status = task_response.info.detail_status
|
|
156
|
+
print(f"Task {task_id} status: {task_status}")
|
|
157
|
+
if task_status == 'ENDED':
|
|
158
|
+
return task_response
|
|
159
|
+
|
|
160
|
+
time.sleep(poll_interval)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sws_api_client
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3.dev1
|
|
4
4
|
Summary: A Python client to easily interact with the FAO SWS REST APIs
|
|
5
5
|
Home-page: https://bitbucket.org/cioapps/sws-it-python-api-client/src/main/
|
|
6
6
|
Author: Mansillo, Daniele (CSI)
|
|
@@ -16,6 +16,7 @@ Classifier: Operating System :: Microsoft :: Windows
|
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
18
|
Requires-Dist: requests>=2.31.0
|
|
19
|
+
Requires-Dist: dataclasses>=0.6
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
# SWS API Client
|
|
@@ -2,7 +2,11 @@ LICENSE
|
|
|
2
2
|
README.md
|
|
3
3
|
setup.py
|
|
4
4
|
sws_api_client/__init__.py
|
|
5
|
+
sws_api_client/datasets.py
|
|
6
|
+
sws_api_client/discover.py
|
|
5
7
|
sws_api_client/sws_api_client.py
|
|
8
|
+
sws_api_client/tags.py
|
|
9
|
+
sws_api_client/tasks.py
|
|
6
10
|
sws_api_client.egg-info/PKG-INFO
|
|
7
11
|
sws_api_client.egg-info/SOURCES.txt
|
|
8
12
|
sws_api_client.egg-info/dependency_links.txt
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .sws_api_client import SwsApiClient
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sws_api_client-0.0.2 → sws_api_client-0.0.3.dev1}/sws_api_client.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|