pybioos 0.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pybioos might be problematic. Click here for more details.
- bioos/__about__.py +4 -0
- bioos/__init__.py +1 -0
- bioos/bioos.py +90 -0
- bioos/bioos_workflow.py +284 -0
- bioos/config.py +147 -0
- bioos/errors.py +89 -0
- bioos/internal/__init__.py +1 -0
- bioos/internal/tos.py +306 -0
- bioos/log.py +125 -0
- bioos/models/__init__.py +1 -0
- bioos/models/models.py +13 -0
- bioos/resource/__init__.py +1 -0
- bioos/resource/data_models.py +157 -0
- bioos/resource/files.py +229 -0
- bioos/resource/utility.py +45 -0
- bioos/resource/workflows.py +590 -0
- bioos/resource/workspaces.py +123 -0
- bioos/service/BioOsService.py +191 -0
- bioos/service/__init__.py +1 -0
- bioos/service/api.py +291 -0
- bioos/service/config.py +37 -0
- bioos/tests/__init__.py +0 -0
- bioos/tests/base.py +21 -0
- bioos/tests/bioos.py +43 -0
- bioos/tests/data_models.py +259 -0
- bioos/tests/files.py +174 -0
- bioos/tests/utils.py +68 -0
- bioos/tests/workflows.py +287 -0
- bioos/tests/workspaces.py +115 -0
- bioos/utils/__init__.py +0 -0
- bioos/utils/common_tools.py +57 -0
- bioos/utils/workflows.py +2 -0
- pybioos-0.0.3.dist-info/LICENSE +21 -0
- pybioos-0.0.3.dist-info/METADATA +24 -0
- pybioos-0.0.3.dist-info/RECORD +38 -0
- pybioos-0.0.3.dist-info/WHEEL +5 -0
- pybioos-0.0.3.dist-info/entry_points.txt +2 -0
- pybioos-0.0.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from cachetools import TTLCache, cached
|
|
5
|
+
|
|
6
|
+
from bioos.config import Config
|
|
7
|
+
from bioos.resource.data_models import DataModelResource
|
|
8
|
+
from bioos.resource.files import FileResource
|
|
9
|
+
from bioos.resource.workflows import Workflow, WorkflowResource
|
|
10
|
+
from bioos.utils.common_tools import SingletonType, dict_str
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Workspace(metaclass=SingletonType):
|
|
14
|
+
|
|
15
|
+
def __init__(self, id_: str):
|
|
16
|
+
self._id = id_
|
|
17
|
+
self._bucket = None
|
|
18
|
+
|
|
19
|
+
def __repr__(self) -> str:
|
|
20
|
+
return f"WorkspaceID: {self._id}\n\n" \
|
|
21
|
+
f"BasicInfo:\n{dict_str(self.basic_info)}\n\n" \
|
|
22
|
+
f"EnvInfo:\n{self.env_info}"
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
@cached(cache=TTLCache(maxsize=10, ttl=1)) #这里为了做缓存,同时定义property,调用时生效
|
|
26
|
+
def basic_info(self) -> dict:
|
|
27
|
+
""" Returns basic info of the workspace
|
|
28
|
+
|
|
29
|
+
:return: Basic info
|
|
30
|
+
:rtype: dict
|
|
31
|
+
"""
|
|
32
|
+
workspace_infos = Config.service().list_workspaces(
|
|
33
|
+
{ #这里是BioOsService对象的list_workspaces方法,非对外暴露的全局方法
|
|
34
|
+
"Filter": {
|
|
35
|
+
"IDs": [self._id]
|
|
36
|
+
}
|
|
37
|
+
}).get("Items")
|
|
38
|
+
if len(workspace_infos) != 1:
|
|
39
|
+
return {}
|
|
40
|
+
workspace_info = workspace_infos[0]
|
|
41
|
+
s3_bucket = workspace_info.get("S3Bucket")
|
|
42
|
+
create_time = datetime.fromtimestamp(workspace_info.get("CreateTime"))
|
|
43
|
+
owner = workspace_info.get("OwnerName")
|
|
44
|
+
description = workspace_info.get("Description")
|
|
45
|
+
name = workspace_info.get("Name")
|
|
46
|
+
return {
|
|
47
|
+
"name": name,
|
|
48
|
+
"description": description,
|
|
49
|
+
"s3_bucket": s3_bucket,
|
|
50
|
+
"owner": owner,
|
|
51
|
+
"create_time": create_time
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
@cached(cache=TTLCache(maxsize=10, ttl=1))
|
|
56
|
+
def env_info(self) -> pd.DataFrame:
|
|
57
|
+
""" Returns cluster info of the workspace
|
|
58
|
+
|
|
59
|
+
:return: Cluster info
|
|
60
|
+
:rtype: pandas.DataFrame
|
|
61
|
+
"""
|
|
62
|
+
notebook_env_info = Config.service().list_cluster(params={
|
|
63
|
+
'Type': "notebook",
|
|
64
|
+
"ID": self._id
|
|
65
|
+
})
|
|
66
|
+
workflow_env_info = Config.service().list_cluster(params={
|
|
67
|
+
'Type': "workflow",
|
|
68
|
+
"ID": self._id
|
|
69
|
+
})
|
|
70
|
+
res = []
|
|
71
|
+
for cluster in notebook_env_info.get('Items') + workflow_env_info.get(
|
|
72
|
+
'Items'):
|
|
73
|
+
info = cluster["ClusterInfo"]
|
|
74
|
+
if info['Status'] == "Running":
|
|
75
|
+
res.append({
|
|
76
|
+
"cluster_id": info['ID'],
|
|
77
|
+
"name": info['Name'],
|
|
78
|
+
"description": info['Description'],
|
|
79
|
+
"type": cluster["Type"]
|
|
80
|
+
})
|
|
81
|
+
return pd.DataFrame(res)
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def data_models(self) -> DataModelResource:
|
|
85
|
+
"""Returns DataModelResource object .
|
|
86
|
+
|
|
87
|
+
:return: DataModelResource object
|
|
88
|
+
:rtype: DataModelResource
|
|
89
|
+
"""
|
|
90
|
+
return DataModelResource(self._id)
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def workflows(self) -> WorkflowResource:
|
|
94
|
+
"""Returns WorkflowResource object.
|
|
95
|
+
|
|
96
|
+
:return: WorkflowResource object
|
|
97
|
+
:rtype: WorkflowResource
|
|
98
|
+
"""
|
|
99
|
+
return WorkflowResource(self._id)
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def files(self) -> FileResource:
|
|
103
|
+
"""Returns FileResource object .
|
|
104
|
+
|
|
105
|
+
:return: FileResource object
|
|
106
|
+
:rtype: FileResource
|
|
107
|
+
"""
|
|
108
|
+
if not self._bucket:
|
|
109
|
+
self._bucket = self.basic_info.get("s3_bucket")
|
|
110
|
+
|
|
111
|
+
return FileResource(self._id, self._bucket)
|
|
112
|
+
|
|
113
|
+
def workflow(self, name: str) -> Workflow: # 通过这里执行的选择workflow生成wf的操作
|
|
114
|
+
"""Returns the workflow for the given name
|
|
115
|
+
|
|
116
|
+
:param name: Workflow name
|
|
117
|
+
:type name: str
|
|
118
|
+
:return: Specified workflow object
|
|
119
|
+
:rtype: Workflow
|
|
120
|
+
"""
|
|
121
|
+
if not self._bucket:
|
|
122
|
+
self._bucket = self.basic_info.get("s3_bucket")
|
|
123
|
+
return Workflow(name, self._id, self._bucket)
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# coding:utf-8
|
|
2
|
+
import json
|
|
3
|
+
import threading
|
|
4
|
+
from urllib.parse import urlparse
|
|
5
|
+
|
|
6
|
+
from volcengine.ApiInfo import ApiInfo
|
|
7
|
+
from volcengine.base.Service import Service
|
|
8
|
+
from volcengine.Credentials import Credentials
|
|
9
|
+
from volcengine.ServiceInfo import ServiceInfo
|
|
10
|
+
|
|
11
|
+
from bioos.errors import ParameterError
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class BioOsService(Service):
|
|
15
|
+
_instance_lock = threading.Lock()
|
|
16
|
+
|
|
17
|
+
def __new__(cls, *args, **kwargs):
|
|
18
|
+
if not hasattr(BioOsService, '_instance'):
|
|
19
|
+
with BioOsService._instance_lock:
|
|
20
|
+
if not hasattr(BioOsService, '_instance'):
|
|
21
|
+
BioOsService._instance = object.__new__(cls)
|
|
22
|
+
return BioOsService._instance
|
|
23
|
+
|
|
24
|
+
def __init__(self, endpoint, region):
|
|
25
|
+
self.service_info = BioOsService.get_service_info(endpoint, region)
|
|
26
|
+
self.api_info = BioOsService.get_api_info()
|
|
27
|
+
super(BioOsService, self).__init__(self.service_info, self.api_info)
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def get_service_info(endpoint, region):
|
|
31
|
+
parsed = urlparse(endpoint)
|
|
32
|
+
scheme, hostname = parsed.scheme, parsed.hostname
|
|
33
|
+
if not scheme or not hostname:
|
|
34
|
+
raise ParameterError("ENDPOINT")
|
|
35
|
+
service_info = ServiceInfo(hostname, {'Accept': 'application/json'},
|
|
36
|
+
Credentials('', '', 'bio', region),
|
|
37
|
+
5,
|
|
38
|
+
5,
|
|
39
|
+
scheme=scheme)
|
|
40
|
+
return service_info
|
|
41
|
+
|
|
42
|
+
@staticmethod
|
|
43
|
+
def get_api_info(): #新增加的API接口可以配置到这里
|
|
44
|
+
api_info = {
|
|
45
|
+
'ListWorkspaces':
|
|
46
|
+
ApiInfo('POST', '/', {
|
|
47
|
+
'Action': 'ListWorkspaces',
|
|
48
|
+
'Version': '2021-03-04'
|
|
49
|
+
}, {}, {}),
|
|
50
|
+
'CreateDataModel':
|
|
51
|
+
ApiInfo('POST', '/', {
|
|
52
|
+
'Action': 'CreateDataModel',
|
|
53
|
+
'Version': '2021-03-04'
|
|
54
|
+
}, {}, {}),
|
|
55
|
+
'ListDataModels':
|
|
56
|
+
ApiInfo('POST', '/', {
|
|
57
|
+
'Action': 'ListDataModels',
|
|
58
|
+
'Version': '2021-03-04'
|
|
59
|
+
}, {}, {}),
|
|
60
|
+
'ListDataModelRows':
|
|
61
|
+
ApiInfo('POST', '/', {
|
|
62
|
+
'Action': 'ListDataModelRows',
|
|
63
|
+
'Version': '2021-03-04'
|
|
64
|
+
}, {}, {}),
|
|
65
|
+
'ListAllDataModelRowIDs':
|
|
66
|
+
ApiInfo('POST', '/', {
|
|
67
|
+
'Action': 'ListAllDataModelRowIDs',
|
|
68
|
+
'Version': '2021-03-04'
|
|
69
|
+
}, {}, {}),
|
|
70
|
+
'DeleteDataModelRowsAndHeaders':
|
|
71
|
+
ApiInfo('POST', '/', {
|
|
72
|
+
'Action': 'DeleteDataModelRowsAndHeaders',
|
|
73
|
+
'Version': '2021-03-04'
|
|
74
|
+
}, {}, {}),
|
|
75
|
+
'ListWorkflows':
|
|
76
|
+
ApiInfo('POST', '/', {
|
|
77
|
+
'Action': 'ListWorkflows',
|
|
78
|
+
'Version': '2021-03-04'
|
|
79
|
+
}, {}, {}),
|
|
80
|
+
'CreateSubmission':
|
|
81
|
+
ApiInfo('POST', '/', {
|
|
82
|
+
'Action': 'CreateSubmission',
|
|
83
|
+
'Version': '2021-03-04'
|
|
84
|
+
}, {}, {}),
|
|
85
|
+
'ListSubmissions':
|
|
86
|
+
ApiInfo('POST', '/', {
|
|
87
|
+
'Action': 'ListSubmissions',
|
|
88
|
+
'Version': '2021-03-04'
|
|
89
|
+
}, {}, {}),
|
|
90
|
+
'DeleteSubmission':
|
|
91
|
+
ApiInfo('POST', '/', {
|
|
92
|
+
'Action': 'DeleteSubmission',
|
|
93
|
+
'Version': '2021-03-04'
|
|
94
|
+
}, {}, {}),
|
|
95
|
+
'ListRuns':
|
|
96
|
+
ApiInfo('POST', '/', {
|
|
97
|
+
'Action': 'ListRuns',
|
|
98
|
+
'Version': '2021-03-04'
|
|
99
|
+
}, {}, {}),
|
|
100
|
+
'ListTasks':
|
|
101
|
+
ApiInfo('POST', '/', {
|
|
102
|
+
'Action': 'ListTasks',
|
|
103
|
+
'Version': '2021-03-04'
|
|
104
|
+
}, {}, {}),
|
|
105
|
+
'GetTOSAccess':
|
|
106
|
+
ApiInfo('POST', '/', {
|
|
107
|
+
'Action': 'GetTOSAccess',
|
|
108
|
+
'Version': '2021-03-04'
|
|
109
|
+
}, {}, {}),
|
|
110
|
+
'ListClustersOfWorkspace':
|
|
111
|
+
ApiInfo('POST', '/', {
|
|
112
|
+
'Action': 'ListClustersOfWorkspace',
|
|
113
|
+
'Version': '2021-03-04'
|
|
114
|
+
}, {}, {}),
|
|
115
|
+
'CreateWorkflow':
|
|
116
|
+
ApiInfo('POST', '/', {
|
|
117
|
+
'Action': 'CreateWorkflow',
|
|
118
|
+
'Version': '2021-03-04'
|
|
119
|
+
}, {}, {}),
|
|
120
|
+
'CheckCreateWorkflow':
|
|
121
|
+
ApiInfo('POST', '/', {
|
|
122
|
+
'Action': 'CheckCreateWorkflow',
|
|
123
|
+
'Version': '2021-03-04'
|
|
124
|
+
}, {}, {}),
|
|
125
|
+
'DeleteWorkflow':
|
|
126
|
+
ApiInfo('POST', '/', {
|
|
127
|
+
'Action': 'DeleteWorkflow',
|
|
128
|
+
'Version': '2021-03-04'
|
|
129
|
+
}, {}, {}),
|
|
130
|
+
}
|
|
131
|
+
return api_info
|
|
132
|
+
|
|
133
|
+
def list_workspaces(self, params): # 以下各方法的params需要在外部使用时构建
|
|
134
|
+
return self.__request('ListWorkspaces', params)
|
|
135
|
+
|
|
136
|
+
def create_data_model(self, params):
|
|
137
|
+
return self.__request('CreateDataModel', params)
|
|
138
|
+
|
|
139
|
+
def list_data_models(self, params):
|
|
140
|
+
return self.__request('ListDataModels', params)
|
|
141
|
+
|
|
142
|
+
def list_data_model_rows(self, params):
|
|
143
|
+
return self.__request('ListDataModelRows', params)
|
|
144
|
+
|
|
145
|
+
def list_data_model_row_ids(self, params):
|
|
146
|
+
return self.__request('ListAllDataModelRowIDs', params)
|
|
147
|
+
|
|
148
|
+
def delete_data_model_rows_and_headers(self, params):
|
|
149
|
+
return self.__request('DeleteDataModelRowsAndHeaders', params)
|
|
150
|
+
|
|
151
|
+
def list_workflows(self, params):
|
|
152
|
+
return self.__request('ListWorkflows', params)
|
|
153
|
+
|
|
154
|
+
def create_submission(self, params):
|
|
155
|
+
return self.__request('CreateSubmission', params)
|
|
156
|
+
|
|
157
|
+
def list_submissions(self, params):
|
|
158
|
+
return self.__request('ListSubmissions', params)
|
|
159
|
+
|
|
160
|
+
def delete_submission(self, params):
|
|
161
|
+
return self.__request('DeleteSubmission', params)
|
|
162
|
+
|
|
163
|
+
def list_runs(self, params):
|
|
164
|
+
return self.__request('ListRuns', params)
|
|
165
|
+
|
|
166
|
+
def list_tasks(self, params):
|
|
167
|
+
return self.__request('ListTasks', params)
|
|
168
|
+
|
|
169
|
+
def get_tos_access(self, params):
|
|
170
|
+
return self.__request('GetTOSAccess', params)
|
|
171
|
+
|
|
172
|
+
def list_cluster(self, params):
|
|
173
|
+
return self.__request('ListClustersOfWorkspace', params)
|
|
174
|
+
|
|
175
|
+
def create_workflow(self, params):
|
|
176
|
+
return self.__request('CreateWorkflow', params)
|
|
177
|
+
|
|
178
|
+
def check_workflow(self, params):
|
|
179
|
+
return self.__request("CheckCreateWorkflow", params)
|
|
180
|
+
|
|
181
|
+
def delete_workflow(self, params):
|
|
182
|
+
return self.__request("DeleteWorkflow", params)
|
|
183
|
+
|
|
184
|
+
def __request(self, action, params):
|
|
185
|
+
res = self.json(
|
|
186
|
+
action, dict(),
|
|
187
|
+
json.dumps(params)) # 这里的json来源于其父类,是字节Service中的函数。处理的是和后端交互后的返回值
|
|
188
|
+
if res == '':
|
|
189
|
+
raise Exception('empty response')
|
|
190
|
+
res_json = json.loads(res)
|
|
191
|
+
return res_json['Result']
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
bioos/service/api.py
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# coding:utf-8
|
|
2
|
+
import csv
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import re
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
|
|
8
|
+
from bioos.errors import NotFoundError, ParameterError
|
|
9
|
+
from bioos.service.config import BioOsServiceConfig as conf
|
|
10
|
+
from bioos.utils import workflows
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def __set_env():
|
|
14
|
+
conf.set_env()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def set_credential(accesskey, secret):
|
|
18
|
+
conf.set_access_key(accesskey)
|
|
19
|
+
conf.set_secret_key(secret)
|
|
20
|
+
conf.set_env()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def upload_entity_table(csvfile, table_name=None):
|
|
24
|
+
__set_env()
|
|
25
|
+
if not table_name:
|
|
26
|
+
table_name = re.sub('\.csv$', '', os.path.basename(csvfile))
|
|
27
|
+
headers = None
|
|
28
|
+
rows = []
|
|
29
|
+
with open(csvfile, 'rt', newline='') as f:
|
|
30
|
+
reader = csv.reader(f)
|
|
31
|
+
for row in reader:
|
|
32
|
+
if headers is None:
|
|
33
|
+
headers = row
|
|
34
|
+
else:
|
|
35
|
+
rows.append(row)
|
|
36
|
+
params = {
|
|
37
|
+
'WorkspaceID': conf.workspace_id(),
|
|
38
|
+
'Name': table_name,
|
|
39
|
+
'Headers': headers,
|
|
40
|
+
'Rows': rows
|
|
41
|
+
}
|
|
42
|
+
conf.service().create_data_model(params)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def list_entity_tables():
|
|
46
|
+
__set_env()
|
|
47
|
+
params = {'WorkspaceID': conf.workspace_id()}
|
|
48
|
+
result = conf.service().list_data_models(params)
|
|
49
|
+
entities = []
|
|
50
|
+
for table in result['Items']:
|
|
51
|
+
if table['Type'] == 'normal':
|
|
52
|
+
entities.append(table)
|
|
53
|
+
return entities
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def get_entity_table(table_name, page_number=1, page_size=10):
|
|
57
|
+
l = list_entity_tables()
|
|
58
|
+
res = None
|
|
59
|
+
for table in l:
|
|
60
|
+
if table['Name'] == table_name:
|
|
61
|
+
res = table
|
|
62
|
+
if not res:
|
|
63
|
+
return None
|
|
64
|
+
params = {
|
|
65
|
+
'WorkspaceID': conf.workspace_id(),
|
|
66
|
+
'ID': res['ID'],
|
|
67
|
+
'PageNumber': page_number,
|
|
68
|
+
'PageSize': page_size
|
|
69
|
+
}
|
|
70
|
+
content = conf.service().list_data_model_rows(params)
|
|
71
|
+
res['Headers'] = content['Headers']
|
|
72
|
+
res['Rows'] = content['Rows']
|
|
73
|
+
res['RowTotalCount'] = content['TotalCount']
|
|
74
|
+
return res
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def delete_entity_table(table_name):
|
|
78
|
+
__set_env()
|
|
79
|
+
table = get_entity_table(table_name, page_size=1)
|
|
80
|
+
if not table:
|
|
81
|
+
raise NotFoundError('Table', table_name)
|
|
82
|
+
params = {'WorkspaceID': conf.workspace_id(), 'ID': table['ID']}
|
|
83
|
+
ids = conf.service().list_data_model_row_ids(params)
|
|
84
|
+
params = {
|
|
85
|
+
'WorkspaceID': conf.workspace_id(),
|
|
86
|
+
'ID': table['ID'],
|
|
87
|
+
'RowIDs': ids['RowIDs']
|
|
88
|
+
}
|
|
89
|
+
conf.service().delete_data_model_rows_and_headers(params)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def delete_entities(table_name, row_ids):
|
|
93
|
+
if not row_ids:
|
|
94
|
+
return
|
|
95
|
+
__set_env()
|
|
96
|
+
table = get_entity_table(table_name, page_size=1)
|
|
97
|
+
if not table:
|
|
98
|
+
raise NotFoundError('Table', table_name)
|
|
99
|
+
params = {
|
|
100
|
+
'WorkspaceID': conf.workspace_id(),
|
|
101
|
+
'ID': table['ID'],
|
|
102
|
+
'RowIDs': row_ids
|
|
103
|
+
}
|
|
104
|
+
conf.service().delete_data_model_rows_and_headers(params)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def delete_entity_table_headers(table_name, headers):
|
|
108
|
+
if not headers:
|
|
109
|
+
return
|
|
110
|
+
__set_env()
|
|
111
|
+
table = get_entity_table(table_name, page_size=1)
|
|
112
|
+
if not table:
|
|
113
|
+
raise NotFoundError('Table', table_name)
|
|
114
|
+
params = {
|
|
115
|
+
'WorkspaceID': conf.workspace_id(),
|
|
116
|
+
'ID': table['ID'],
|
|
117
|
+
'Headers': headers
|
|
118
|
+
}
|
|
119
|
+
conf.service().delete_data_model_rows_and_headers(params)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def list_workflows(search_keyword=None, page_number=1, page_size=10):
|
|
123
|
+
__set_env()
|
|
124
|
+
params = {
|
|
125
|
+
'WorkspaceID': conf.workspace_id(),
|
|
126
|
+
'SortBy': 'CreateTime',
|
|
127
|
+
'PageNumber': page_number,
|
|
128
|
+
'PageSize': page_size
|
|
129
|
+
}
|
|
130
|
+
if search_keyword:
|
|
131
|
+
params['Filter'] = {'Keyword': search_keyword}
|
|
132
|
+
return conf.service().list_workflows(params).get('Items')
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def get_workflow(workflow_name):
|
|
136
|
+
lis = list_workflows(search_keyword=workflow_name)
|
|
137
|
+
if not lis:
|
|
138
|
+
return None
|
|
139
|
+
lis = [x for x in lis if x['Name'] == workflow_name]
|
|
140
|
+
if not lis:
|
|
141
|
+
return None
|
|
142
|
+
params = {
|
|
143
|
+
'WorkspaceID': conf.workspace_id(),
|
|
144
|
+
'Filter': {
|
|
145
|
+
'IDs': [lis[0].get('ID')]
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
workflows = conf.service().list_workflows(params).get('Items')
|
|
149
|
+
if len(workflows) != 1:
|
|
150
|
+
return None
|
|
151
|
+
detail = workflows[0]
|
|
152
|
+
res = lis[0]
|
|
153
|
+
for k, v in detail.items():
|
|
154
|
+
if k != 'Item':
|
|
155
|
+
res[k] = v
|
|
156
|
+
return res
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def add_submission(workflow_name,
|
|
160
|
+
table_name,
|
|
161
|
+
row_ids,
|
|
162
|
+
cluster_id,
|
|
163
|
+
inputs={},
|
|
164
|
+
outputs={},
|
|
165
|
+
submission_name_suffix=None,
|
|
166
|
+
submission_desc=None,
|
|
167
|
+
call_caching=True):
|
|
168
|
+
if not row_ids or not isinstance(row_ids, list):
|
|
169
|
+
raise ParameterError('row_ids')
|
|
170
|
+
if not inputs and not isinstance(inputs, dict):
|
|
171
|
+
raise ParameterError('inputs')
|
|
172
|
+
if not outputs and not isinstance(outputs, dict):
|
|
173
|
+
raise ParameterError('outputs')
|
|
174
|
+
|
|
175
|
+
table = get_entity_table(table_name, page_size=1)
|
|
176
|
+
if not table:
|
|
177
|
+
raise NotFoundError('Table', table_name)
|
|
178
|
+
workflow = get_workflow(workflow_name)
|
|
179
|
+
if not workflow:
|
|
180
|
+
raise NotFoundError('Workflow', workflow_name)
|
|
181
|
+
if not submission_name_suffix:
|
|
182
|
+
submission_name_suffix = datetime.now().strftime('%Y-%m-%d-%H-%M-%S')
|
|
183
|
+
params = {
|
|
184
|
+
"ClusterID": cluster_id,
|
|
185
|
+
'WorkspaceID': conf.workspace_id(),
|
|
186
|
+
'WorkflowID': workflow['ID'],
|
|
187
|
+
'Name': submission_name(workflow_name, submission_name_suffix),
|
|
188
|
+
'Description': submission_desc,
|
|
189
|
+
'DataModelID': table['ID'],
|
|
190
|
+
'DataModelRowIDs': row_ids,
|
|
191
|
+
'Inputs': json.dumps(inputs),
|
|
192
|
+
'ExposedOptions': {
|
|
193
|
+
"ReadFromCache": call_caching
|
|
194
|
+
},
|
|
195
|
+
'Outputs': json.dumps(outputs)
|
|
196
|
+
}
|
|
197
|
+
conf.service().create_submission(params)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def list_submissions(workflow_name=None,
|
|
201
|
+
search_keyword=None,
|
|
202
|
+
status=None,
|
|
203
|
+
cluster_id=None,
|
|
204
|
+
page_number=1,
|
|
205
|
+
page_size=10):
|
|
206
|
+
__set_env()
|
|
207
|
+
params = {
|
|
208
|
+
'WorkspaceID': conf.workspace_id(),
|
|
209
|
+
'PageNumber': page_number,
|
|
210
|
+
'PageSize': page_size,
|
|
211
|
+
'Filter': {}
|
|
212
|
+
}
|
|
213
|
+
if workflow_name:
|
|
214
|
+
workflow = get_workflow(workflow_name)
|
|
215
|
+
if not workflow:
|
|
216
|
+
raise NotFoundError('Workflow', workflow_name)
|
|
217
|
+
params['Filter']['WorkflowID'] = workflow['ID']
|
|
218
|
+
if search_keyword:
|
|
219
|
+
params['Filter']['Keyword'] = search_keyword
|
|
220
|
+
if status:
|
|
221
|
+
params['Filter']['Status'] = status
|
|
222
|
+
if cluster_id:
|
|
223
|
+
params['Filter']['ClusterID'] = cluster_id
|
|
224
|
+
|
|
225
|
+
return conf.service().list_submissions(params).get('Items')
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def get_submission(submission_name):
|
|
229
|
+
lis = list_submissions(search_keyword=submission_name)
|
|
230
|
+
if not lis:
|
|
231
|
+
raise NotFoundError('Submission', submission_name)
|
|
232
|
+
lis = [x for x in lis if x['Name'] == submission_name]
|
|
233
|
+
if not lis or len(lis) < 1:
|
|
234
|
+
return None
|
|
235
|
+
submission = lis[0]
|
|
236
|
+
# list data entity rows by call list runs
|
|
237
|
+
runs = conf.service().list_runs({
|
|
238
|
+
'WorkspaceID': submission.get("WorkflowID"),
|
|
239
|
+
"SubmissionID": submission.get("ID"),
|
|
240
|
+
}).get("Items")
|
|
241
|
+
data_entity_row_ids = set()
|
|
242
|
+
for run in runs:
|
|
243
|
+
if run.get("DataEntityRowID") != "":
|
|
244
|
+
data_entity_row_ids.add(run.get("DataEntityRowID"))
|
|
245
|
+
# get data model name by call list data models
|
|
246
|
+
models = conf.service().list_data_models({
|
|
247
|
+
'WorkspaceID':
|
|
248
|
+
submission.get("WorkflowID"),
|
|
249
|
+
}).get("Items")
|
|
250
|
+
data_model = ""
|
|
251
|
+
for model in models:
|
|
252
|
+
if model["ID"] == submission.get["DataModelID"]:
|
|
253
|
+
data_model = model.get("Name")
|
|
254
|
+
break
|
|
255
|
+
submission["DataEntity"] = {
|
|
256
|
+
"ID": submission.get["DataModelID"],
|
|
257
|
+
"Name": data_model,
|
|
258
|
+
"RowIDs": list(data_entity_row_ids)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return submission
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def delete_submission(submission_name):
|
|
265
|
+
submission = get_submission(submission_name)
|
|
266
|
+
if not submission:
|
|
267
|
+
raise NotFoundError('Submission', submission_name)
|
|
268
|
+
params = {'WorkspaceID': conf.workspace_id(), 'ID': submission['ID']}
|
|
269
|
+
conf.service().delete_submission(params)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def list_cluster(cluster_type):
|
|
273
|
+
if cluster_type not in ("notebook", "workflow"):
|
|
274
|
+
raise ParameterError("cluster_type")
|
|
275
|
+
params = {'Type': cluster_type, 'ID': conf.workspace_id()}
|
|
276
|
+
clusters = conf.service().list_cluster(params).get('Items')
|
|
277
|
+
res = []
|
|
278
|
+
for cluster in clusters:
|
|
279
|
+
info = cluster["ClusterInfo"]
|
|
280
|
+
if info['Status'] == "Running":
|
|
281
|
+
res.append({
|
|
282
|
+
"cluster_id": info['ID'],
|
|
283
|
+
"name": info['Name'],
|
|
284
|
+
"description": info['Description'],
|
|
285
|
+
"type": cluster["Type"]
|
|
286
|
+
})
|
|
287
|
+
return res
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def submission_name(workflow_name, submission_name_suffix):
|
|
291
|
+
return workflows.submission_name(workflow_name, submission_name_suffix)
|
bioos/service/config.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from bioos.config import Config
|
|
4
|
+
from bioos.errors import EnvironmentConfigurationError
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_endpoint_env():
|
|
8
|
+
return os.environ.get('BIOOS_ENDPOINT')
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_workspace_id_env():
|
|
12
|
+
workspace_id = os.environ.get('BIOOS_WORKSPACE_ID')
|
|
13
|
+
if not workspace_id:
|
|
14
|
+
# use in MiracleCloud Notebook editor(jupyterhub)
|
|
15
|
+
workspace_id = os.environ.get('JUPYTERHUB_SERVER_NAME')
|
|
16
|
+
return workspace_id
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class BioOsServiceConfig(Config):
|
|
20
|
+
_workspace_id: str = None
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def set_env(cls):
|
|
24
|
+
if cls._service is None:
|
|
25
|
+
endpoint = get_endpoint_env()
|
|
26
|
+
if not endpoint:
|
|
27
|
+
raise EnvironmentConfigurationError('BIOOS_ENDPOINT')
|
|
28
|
+
cls.set_endpoint(endpoint)
|
|
29
|
+
|
|
30
|
+
workspace_id = get_workspace_id_env()
|
|
31
|
+
if not workspace_id:
|
|
32
|
+
raise EnvironmentConfigurationError('BIOOS_WORKSPACE_ID')
|
|
33
|
+
cls._workspace_id = workspace_id
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def workspace_id(cls):
|
|
37
|
+
return cls._workspace_id
|
bioos/tests/__init__.py
ADDED
|
File without changes
|
bioos/tests/base.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from unittest.mock import patch
|
|
3
|
+
|
|
4
|
+
from volcengine.const.Const import REGION_CN_NORTH1
|
|
5
|
+
|
|
6
|
+
from bioos import bioos
|
|
7
|
+
from bioos.config import Config
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BaseInit(unittest.TestCase):
|
|
11
|
+
ak = "ak"
|
|
12
|
+
sk = "sk"
|
|
13
|
+
endpoint = "http://endpoint"
|
|
14
|
+
region = REGION_CN_NORTH1
|
|
15
|
+
workspace_id = "wccxxxxxxxxxxxxxno80"
|
|
16
|
+
really_login = bioos.login(endpoint, ak, sk, region)
|
|
17
|
+
|
|
18
|
+
def __init__(self, *args, **kwargs):
|
|
19
|
+
with patch.object(Config, "_ping_func"):
|
|
20
|
+
bioos.login(self.endpoint, self.ak, self.sk, self.region)
|
|
21
|
+
super(BaseInit, self).__init__(*args, **kwargs)
|