pybioos 0.0.15__py3-none-any.whl → 0.0.17__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 CHANGED
@@ -1,4 +1,4 @@
1
1
  # coding:utf-8
2
2
 
3
3
  # Package version
4
- __version__ = "0.0.15"
4
+ __version__ = "0.0.17"
bioos/bioos.py CHANGED
@@ -20,9 +20,9 @@ def status() -> Config.LoginInfo:
20
20
  return Config.login_info()
21
21
 
22
22
 
23
- def login(endpoint: str,
24
- access_key: str,
23
+ def login(access_key: str,
25
24
  secret_key: str,
25
+ endpoint: str = None,
26
26
  region: str = REGION_CN_NORTH1) -> bool:
27
27
  """Login to the given endpoint using specified account and password.
28
28
 
@@ -35,14 +35,16 @@ def login(endpoint: str,
35
35
  *Example*:
36
36
  ::
37
37
 
38
- bioos.login(endpoint="https://cloud.xxxxx.xxx.cn",access_key="xxxxxxxx",secret_key="xxxxxxxx")
38
+ bioos.login(access_key="xxxxxxxx", secret_key="xxxxxxxx")
39
+ # or specify endpoint explicitly:
40
+ bioos.login(access_key="xxxxxxxx", secret_key="xxxxxxxx", endpoint="https://cloud.xxxxx.xxx.cn")
39
41
 
40
- :param endpoint: The environment to be logged in
41
- :type endpoint: str
42
42
  :param access_key: The specified account's access key
43
43
  :type access_key: str
44
44
  :param secret_key: Corresponding secret key of the access key
45
45
  :type secret_key: str
46
+ :param endpoint: The environment to be logged in (optional, defaults to Config._endpoint)
47
+ :type endpoint: str
46
48
  :param region: The region to be logged in
47
49
  :type region: str
48
50
  :return: Login result
@@ -50,7 +52,8 @@ def login(endpoint: str,
50
52
  """
51
53
  Config.set_access_key(access_key)
52
54
  Config.set_secret_key(secret_key)
53
- Config.set_endpoint(endpoint)
55
+ if endpoint is not None:
56
+ Config.set_endpoint(endpoint)
54
57
  Config.set_region(region)
55
58
  return Config.login_info().login_status == "Already logged in"
56
59
 
bioos/bioos_workflow.py CHANGED
@@ -4,12 +4,25 @@ import logging
4
4
  import os
5
5
  import re
6
6
  import time
7
+ from typing import Dict, Any
7
8
 
8
9
  import pandas as pd
9
10
 
10
11
  from bioos import bioos
11
12
  from bioos.errors import NotFoundError, ParameterError
12
-
13
+ from bioos.config import DEFAULT_ENDPOINT
14
+
15
+ def uniquify_columns(cols: list[str]) -> list[str]:
16
+ seen, out = {}, []
17
+ for col in cols:
18
+ base = col.split(".")[-1]
19
+ if base not in seen:
20
+ seen[base] = 0
21
+ out.append(base)
22
+ else:
23
+ seen[base] += 1
24
+ out.append(f"{base}_{seen[base]}") # fastq → fastq_1 → fastq_2
25
+ return out
13
26
 
14
27
  def recognize_files_from_input_json(workflow_input_json: dict) -> dict:
15
28
  putative_files = {}
@@ -168,6 +181,9 @@ class Bioos_workflow:
168
181
  force_reupload: bool = False):
169
182
  if not os.path.isfile(input_json_file):
170
183
  raise ParameterError('Input_json_file is not found.')
184
+ #给每一个data_model加一个uuid,保证不重复
185
+ if data_model_name == "dm":
186
+ data_model_name = f"dm_{int(time.time())}"
171
187
 
172
188
  input_json = json.load(open(input_json_file))
173
189
  self.logger.info("Load json input successfully.")
@@ -231,7 +247,8 @@ class Bioos_workflow:
231
247
  df[id_col] = [f"tmp_{x}" for x in list(range(len(df)))]
232
248
  df = df.reindex(columns=columns)
233
249
  columns = [key.split(".")[-1] for key in df.columns.to_list()]
234
- df.columns = pd.Index(columns)
250
+ #df.columns = pd.Index(columns)
251
+ df.columns = pd.Index(uniquify_columns(df.columns.to_list()))
235
252
 
236
253
  # write data models
237
254
  self.ws.data_models.write({data_model_name: df.applymap(str)},
@@ -243,6 +260,7 @@ class Bioos_workflow:
243
260
  for key, _ in unupdate_dict.items():
244
261
  unupdate_dict[key] = f'this.{key.split(".")[-1]}'
245
262
 
263
+
246
264
  self.params_submit["inputs"] = json.dumps(unupdate_dict)
247
265
  self.params_submit["data_model_name"] = data_model_name
248
266
  self.params_submit["row_ids"] = df[id_col].to_list()
@@ -254,7 +272,7 @@ class Bioos_workflow:
254
272
  self.logger.info("Build params dict successfully.")
255
273
  return self.params_submit
256
274
 
257
- def postprocess(self, download=False):
275
+ def postprocess(self, download=False,download_dir="."):
258
276
  # 假设全部执行完毕
259
277
  # 对运行完成的目录进行下载
260
278
  # 证实bioos包只能对文件的list进行下载,不支持文件夹
@@ -268,9 +286,10 @@ class Bioos_workflow:
268
286
 
269
287
  files.append(file)
270
288
 
271
- if download:
289
+ if download and files:
290
+ os.makedirs(download_dir, exist_ok=True)
272
291
  try:
273
- self.ws.files.download(files, ".", flatten=False)
292
+ self.ws.files.download(files, download_dir, flatten=False)
274
293
  except Exception as e:
275
294
  print(f'Some file can not download. \n {e}')
276
295
 
@@ -305,7 +324,7 @@ def bioos_workflow():
305
324
  parser.add_argument("--endpoint",
306
325
  type=str,
307
326
  help="Bio-OS instance platform endpoint",
308
- default="https://bio-top.miracle.ac.cn")
327
+ default=DEFAULT_ENDPOINT)
309
328
  parser.add_argument(
310
329
  "--ak",
311
330
  type=str,
@@ -357,6 +376,11 @@ def bioos_workflow():
357
376
  "--download_results",
358
377
  action='store_true',
359
378
  help="Download the submission run result files to local current path.")
379
+ parser.add_argument(
380
+ "--download_dir",
381
+ type=str,
382
+ default=".",
383
+ help="本地保存下载结果的目录(默认当前目录)")
360
384
 
361
385
  parsed_args = parser.parse_args()
362
386
 
@@ -395,5 +419,5 @@ def bioos_workflow():
395
419
  print(bw.runs)
396
420
 
397
421
  bw.logger.info("Start to postprocess.")
398
- bw.postprocess(download=parsed_args.download_results)
422
+ bw.postprocess(download=parsed_args.download_results,download_dir = parsed_args.download_dir)
399
423
  bw.logger.info("Postprocess finished.")
bioos/bw_import.py CHANGED
@@ -1,17 +1,13 @@
1
1
  #!/usr/bin/env python3
2
2
  # coding: utf-8
3
-
4
3
  import argparse
5
4
  import logging
6
5
  import os
7
6
  import sys
8
7
  import time
9
-
10
8
  from bioos import bioos
11
- from bioos.config import Config
9
+ from bioos.config import Config, DEFAULT_ENDPOINT
12
10
  from bioos.resource.workflows import WorkflowResource
13
-
14
-
15
11
  def get_logger():
16
12
  """Setup logger"""
17
13
  logger = logging.getLogger('bw_import')
@@ -23,14 +19,11 @@ def get_logger():
23
19
  logger.addHandler(handler)
24
20
  logger.setLevel(logging.INFO)
25
21
  return logger
26
-
27
-
28
22
  def bioos_workflow_import():
29
23
  """Command line entry point"""
30
24
  parser = argparse.ArgumentParser(
31
25
  description='Bio-OS Workflow Import Tool',
32
26
  formatter_class=argparse.RawDescriptionHelpFormatter)
33
-
34
27
  # 必需参数
35
28
  parser.add_argument(
36
29
  '--ak',
@@ -49,8 +42,10 @@ def bioos_workflow_import():
49
42
  parser.add_argument('--workflow_source',
50
43
  required=True,
51
44
  help='Local WDL file path or git repository URL')
52
-
53
45
  # 可选参数
46
+ parser.add_argument('--endpoint',
47
+ help='Bio-OS instance platform endpoint',
48
+ default=DEFAULT_ENDPOINT)
54
49
  parser.add_argument('--workflow_desc',
55
50
  help='Description for the workflow',
56
51
  default='')
@@ -67,16 +62,13 @@ def bioos_workflow_import():
67
62
  type=int,
68
63
  default=60,
69
64
  help='Time interval in seconds for checking workflow status')
70
-
71
65
  args = parser.parse_args()
72
66
  logger = get_logger()
73
-
74
67
  try:
75
68
  # 配置Bio-OS
76
69
  Config.set_access_key(args.ak)
77
70
  Config.set_secret_key(args.sk)
78
- Config.set_endpoint("https://bio-top.miracle.ac.cn")
79
-
71
+ Config.set_endpoint(args.endpoint)
80
72
  # 获取workspace ID
81
73
  workspaces = bioos.list_workspaces()
82
74
  workspace_info = workspaces.query(f"Name=='{args.workspace_name}'")
@@ -84,10 +76,8 @@ def bioos_workflow_import():
84
76
  logger.error(f"Workspace {args.workspace_name} not found")
85
77
  sys.exit(1)
86
78
  workspace_id = workspace_info["ID"].iloc[0]
87
-
88
79
  # 创建WorkflowResource实例
89
80
  workflow_resource = WorkflowResource(workspace_id)
90
-
91
81
  # 导入workflow
92
82
  try:
93
83
  result = workflow_resource.import_workflow(
@@ -99,19 +89,15 @@ def bioos_workflow_import():
99
89
  logger.info(
100
90
  f"Successfully uploaded workflow: {result}, validating..., please wait..."
101
91
  )
102
-
103
92
  # 如果设置了monitor参数,则监控工作流状态
104
93
  if args.monitor:
105
94
  max_retries = 10 # 最大重试次数
106
95
  retry_count = 0
107
-
108
96
  while retry_count < max_retries:
109
97
  df = workflow_resource.list()
110
98
  workflow_info = df[df.Name == args.workflow_name]
111
-
112
99
  if len(workflow_info) == 1:
113
100
  status = workflow_info.iloc[0]["Status"]["Phase"]
114
-
115
101
  if status == "Succeeded":
116
102
  logger.info(
117
103
  f"Workflow {args.workflow_name} validated successfully"
@@ -138,7 +124,6 @@ def bioos_workflow_import():
138
124
  f"Workflow {args.workflow_name} not found after import"
139
125
  )
140
126
  sys.exit(1)
141
-
142
127
  logger.error(
143
128
  f"Workflow validation timeout after {max_retries} retries")
144
129
  sys.exit(1)
@@ -148,15 +133,11 @@ def bioos_workflow_import():
148
133
  f"Workflow {args.workflow_name} is still validating, {result}, please wait and check the status later."
149
134
  )
150
135
  sys.exit(0)
151
-
152
136
  except Exception as e:
153
137
  logger.error(f"Failed to import workflow: {str(e)}")
154
138
  sys.exit(1)
155
-
156
139
  except Exception as e:
157
140
  logger.error(f"Error: {str(e)}")
158
141
  sys.exit(1)
159
-
160
-
161
142
  if __name__ == '__main__':
162
143
  bioos_workflow_import()
@@ -6,7 +6,7 @@ import logging
6
6
  import sys
7
7
 
8
8
  from bioos import bioos
9
- from bioos.config import Config
9
+ from bioos.config import Config, DEFAULT_ENDPOINT
10
10
  from bioos.resource.workflows import WorkflowResource
11
11
 
12
12
 
@@ -44,6 +44,11 @@ def bioos_workflow_status_check():
44
44
  parser.add_argument('--workflow_id',
45
45
  required=True,
46
46
  help='ID of the workflow to check')
47
+
48
+ # 可选参数
49
+ parser.add_argument('--endpoint',
50
+ help='Bio-OS instance platform endpoint',
51
+ default=DEFAULT_ENDPOINT)
47
52
 
48
53
  args = parser.parse_args()
49
54
  logger = get_logger()
@@ -52,7 +57,7 @@ def bioos_workflow_status_check():
52
57
  # 配置Bio-OS
53
58
  Config.set_access_key(args.ak)
54
59
  Config.set_secret_key(args.sk)
55
- Config.set_endpoint("https://bio-top.miracle.ac.cn")
60
+ Config.set_endpoint(args.endpoint)
56
61
 
57
62
  # 获取workspace ID
58
63
  workspaces = bioos.list_workspaces()
bioos/bw_status_check.py CHANGED
@@ -6,7 +6,7 @@ import logging
6
6
  import sys
7
7
 
8
8
  from bioos import bioos
9
- from bioos.config import Config
9
+ from bioos.config import Config, DEFAULT_ENDPOINT
10
10
  from bioos.resource.workflows import WorkflowResource
11
11
 
12
12
 
@@ -44,6 +44,11 @@ def bioos_workflow_status_check():
44
44
  parser.add_argument('--submission_id',
45
45
  required=True,
46
46
  help='ID of the submission to check')
47
+
48
+ # 可选参数
49
+ parser.add_argument('--endpoint',
50
+ help='Bio-OS instance platform endpoint',
51
+ default=DEFAULT_ENDPOINT)
47
52
 
48
53
  args = parser.parse_args()
49
54
  logger = get_logger()
@@ -52,7 +57,7 @@ def bioos_workflow_status_check():
52
57
  # 配置Bio-OS
53
58
  Config.set_access_key(args.ak)
54
59
  Config.set_secret_key(args.sk)
55
- Config.set_endpoint("https://bio-top.miracle.ac.cn")
60
+ Config.set_endpoint(args.endpoint)
56
61
 
57
62
  # 获取workspace ID
58
63
  workspaces = bioos.list_workspaces()
bioos/config.py CHANGED
@@ -7,6 +7,9 @@ from bioos.errors import ConfigurationError
7
7
  from bioos.log import PyLogger
8
8
  from bioos.service.BioOsService import BioOsService
9
9
 
10
+ # 默认的 Bio-OS endpoint
11
+ DEFAULT_ENDPOINT = "https://bio-top.miracle.ac.cn"
12
+
10
13
  LOGIN_STATUS = Literal['Already logged in', 'Not logged in']
11
14
 
12
15
 
@@ -14,7 +17,7 @@ class Config:
14
17
  _service: BioOsService = None
15
18
  _access_key: str = os.environ.get('VOLC_ACCESSKEY')
16
19
  _secret_key: str = os.environ.get('VOLC_SECRETKEY')
17
- _endpoint: str = os.environ.get('BIOOS_ENDPOINT')
20
+ _endpoint: str = os.environ.get('BIOOS_ENDPOINT', DEFAULT_ENDPOINT)
18
21
  _region: str = REGION_CN_NORTH1
19
22
  Logger = PyLogger() # 这里是把类赋给了Logger变量
20
23
 
@@ -7,7 +7,7 @@ import os
7
7
  import sys
8
8
 
9
9
  from bioos import bioos
10
- from bioos.config import Config
10
+ from bioos.config import Config, DEFAULT_ENDPOINT
11
11
 
12
12
 
13
13
  def get_logger():
@@ -48,6 +48,11 @@ def get_submission_logs():
48
48
  '--output_dir',
49
49
  default='.',
50
50
  help='Local directory to save the logs (default: current directory)')
51
+
52
+ # 可选参数
53
+ parser.add_argument('--endpoint',
54
+ help='Bio-OS instance platform endpoint',
55
+ default=DEFAULT_ENDPOINT)
51
56
 
52
57
  args = parser.parse_args()
53
58
  logger = get_logger()
@@ -56,7 +61,7 @@ def get_submission_logs():
56
61
  # 配置Bio-OS
57
62
  Config.set_access_key(args.ak)
58
63
  Config.set_secret_key(args.sk)
59
- Config.set_endpoint("https://bio-top.miracle.ac.cn")
64
+ Config.set_endpoint(args.endpoint)
60
65
 
61
66
  # 获取workspace ID
62
67
  workspaces = bioos.list_workspaces()
@@ -3,7 +3,7 @@ import os
3
3
  import zipfile
4
4
  from datetime import datetime
5
5
  from io import BytesIO
6
- from typing import List
6
+ from typing import List, Dict, Optional, Any
7
7
 
8
8
  import pandas as pd
9
9
  from cachetools import TTLCache, cached
@@ -548,17 +548,62 @@ class WorkflowResource(metaclass=SingletonType):
548
548
 
549
549
 
550
550
  class Workflow(metaclass=SingletonType):
551
+ """Represents a workflow in Bio-OS.
552
+
553
+ This class encapsulates all the information and operations related to a workflow,
554
+ including its metadata, inputs, outputs, and execution capabilities.
555
+ """
551
556
 
552
557
  def __init__(self,
553
558
  name: str,
554
559
  workspace_id: str,
555
560
  bucket: str,
556
561
  check: bool = False):
562
+ """Initialize a workflow instance.
563
+
564
+ Args:
565
+ name: The name of the workflow
566
+ workspace_id: The ID of the workspace containing this workflow
567
+ bucket: The S3 bucket associated with this workflow
568
+ check: Whether to check the workflow existence immediately
569
+ """
557
570
  self.name = name
558
571
  self.workspace_id = workspace_id
559
572
  self.bucket = bucket
573
+ self._description: str = ""
574
+ self._create_time: int = 0
575
+ self._update_time: int = 0
576
+ self._language: str = "WDL"
577
+ self._source: str = ""
578
+ self._tag: str = ""
579
+ self._token: Optional[str] = None
580
+ self._main_workflow_path: str = ""
581
+ self._status: Dict[str, Optional[str]] = {"Phase": "", "Message": None}
582
+ self._inputs: List[Dict[str, Any]] = []
583
+ self._outputs: List[Dict[str, Any]] = []
584
+ self._owner_name: str = ""
585
+ self._graph: str = ""
586
+ self._source_type: str = ""
587
+
560
588
  if check:
561
- self.id
589
+ self.sync()
590
+
591
+ def __repr__(self):
592
+ """Return a string representation of the workflow."""
593
+ info_dict = dict_str({
594
+ "id": self.id,
595
+ "name": self.name,
596
+ "description": self.description,
597
+ "language": self.language,
598
+ "source": self.source,
599
+ "tag": self.tag,
600
+ "main_workflow_path": self.main_workflow_path,
601
+ "status": self.status,
602
+ "owner_name": self.owner_name,
603
+ "create_time": self.create_time,
604
+ "update_time": self.update_time
605
+ })
606
+ return f"WorkflowInfo:\n{info_dict}"
562
607
 
563
608
  @property
564
609
  @cached(cache=TTLCache(maxsize=100, ttl=1))
@@ -574,6 +619,140 @@ class Workflow(metaclass=SingletonType):
574
619
  raise ParameterError("name")
575
620
  return res["ID"].iloc[0]
576
621
 
622
+ @property
623
+ def description(self) -> str:
624
+ """Get the workflow description."""
625
+ if not self._description:
626
+ self.sync()
627
+ return self._description
628
+
629
+ @property
630
+ def create_time(self) -> int:
631
+ """Get the workflow creation timestamp."""
632
+ if not self._create_time:
633
+ self.sync()
634
+ return self._create_time
635
+
636
+ @property
637
+ def update_time(self) -> int:
638
+ """Get the workflow last update timestamp."""
639
+ if not self._update_time:
640
+ self.sync()
641
+ return self._update_time
642
+
643
+ @property
644
+ def language(self) -> str:
645
+ """Get the workflow language (e.g., WDL)."""
646
+ if not self._language:
647
+ self.sync()
648
+ return self._language
649
+
650
+ @property
651
+ def source(self) -> str:
652
+ """Get the workflow source location."""
653
+ if not self._source:
654
+ self.sync()
655
+ return self._source
656
+
657
+ @property
658
+ def tag(self) -> str:
659
+ """Get the workflow version tag."""
660
+ if not self._tag:
661
+ self.sync()
662
+ return self._tag
663
+
664
+ @property
665
+ def token(self) -> Optional[str]:
666
+ """Get the workflow access token if any."""
667
+ if not self._token:
668
+ self.sync()
669
+ return self._token
670
+
671
+ @property
672
+ def main_workflow_path(self) -> str:
673
+ """Get the main workflow file path."""
674
+ if not self._main_workflow_path:
675
+ self.sync()
676
+ return self._main_workflow_path
677
+
678
+ @property
679
+ def status(self) -> Dict[str, Optional[str]]:
680
+ """Get the workflow status information."""
681
+ if not self._status["Phase"]:
682
+ self.sync()
683
+ return self._status
684
+ @property
685
+ def inputs(self) -> List[Dict[str, Any]]:
686
+ """Get the workflow input parameters."""
687
+ if not self._inputs:
688
+ self.sync()
689
+ return self._inputs
690
+
691
+ @property
692
+ def outputs(self) -> List[Dict[str, Any]]:
693
+ """Get the workflow output parameters."""
694
+ if not self._outputs:
695
+ self.sync()
696
+ return self._outputs
697
+
698
+ @property
699
+ def owner_name(self) -> str:
700
+ """Get the workflow owner's name."""
701
+ if not self._owner_name:
702
+ self.sync()
703
+ return self._owner_name
704
+
705
+ @property
706
+ def graph(self) -> str:
707
+ """Get the workflow graph representation."""
708
+ if not self._graph:
709
+ self.sync()
710
+ return self._graph
711
+
712
+ @property
713
+ def source_type(self) -> str:
714
+ """Get the workflow source type."""
715
+ if not self._source_type:
716
+ self.sync()
717
+ return self._source_type
718
+
719
+ @cached(cache=TTLCache(maxsize=100, ttl=1))
720
+ def sync(self):
721
+ """Synchronize workflow information with the remote service."""
722
+ res = WorkflowResource(self.workspace_id). \
723
+ list().query(f"Name=='{self.name}'")
724
+ if res.empty:
725
+ raise ParameterError("name")
726
+
727
+ # Get detailed workflow information
728
+ params = {
729
+ 'WorkspaceID': self.workspace_id,
730
+ 'Filter': {
731
+ 'IDs': [res["ID"].iloc[0]]
732
+ }
733
+ }
734
+ workflows = Config.service().list_workflows(params).get('Items')
735
+ if len(workflows) != 1:
736
+ raise NotFoundError("workflow", self.name)
737
+
738
+ detail = workflows[0]
739
+
740
+ # Update all properties
741
+ self._description = detail.get("Description", "")
742
+ self._create_time = detail.get("CreateTime", 0)
743
+ self._update_time = detail.get("UpdateTime", 0)
744
+ self._language = detail.get("Language", "WDL")
745
+ self._source = detail.get("Source", "")
746
+ self._tag = detail.get("Tag", "")
747
+ self._token = detail.get("Token")
748
+ self._main_workflow_path = detail.get("MainWorkflowPath", "")
749
+ self._status = detail.get("Status", {"Phase": "", "Message": None})
750
+ self._inputs = detail.get("Inputs", [])
751
+ self._outputs = detail.get("Outputs", [])
752
+ self._owner_name = detail.get("OwnerName", "")
753
+ self._graph = detail.get("Graph", "")
754
+ self._source_type = detail.get("SourceType", "")
755
+
577
756
  @property
578
757
  @cached(cache=TTLCache(maxsize=100, ttl=1))
579
758
  def get_cluster(self):
@@ -594,11 +773,14 @@ class Workflow(metaclass=SingletonType):
594
773
  return info['ID']
595
774
  raise NotFoundError("cluster", "workflow")
596
775
 
597
- def query_data_model_id(self, name: str) -> "":
776
+ def query_data_model_id(self, name: str) -> str:
598
777
  """Gets the id of given data_models among those accessible
599
778
 
600
- :param name:
601
- :return:
779
+ Args:
780
+ name: The name of the data model
781
+
782
+ Returns:
783
+ str: The ID of the data model, or empty string if not found
602
784
  """
603
785
  res = DataModelResource(self.workspace_id).list(). \
604
786
  query(f"Name=='{name}'")
@@ -682,3 +864,4 @@ class Workflow(metaclass=SingletonType):
682
864
  submission_id = Config.service().create_submission(params).get("ID")
683
865
 
684
866
  return Submission(self.workspace_id, submission_id).runs
867
+
bioos/workflow_info.py ADDED
@@ -0,0 +1,207 @@
1
+ from typing import Dict, Any, List, Optional
2
+ import pandas as pd
3
+
4
+ from bioos import bioos
5
+ from bioos.config import Config, DEFAULT_ENDPOINT
6
+ from bioos.errors import NotFoundError
7
+
8
+ class WorkflowInfo:
9
+ """Bio-OS 工作流信息查询类"""
10
+
11
+ def __init__(self, ak: str, sk: str, endpoint: str = DEFAULT_ENDPOINT):
12
+ """
13
+ 初始化工作流信息查询类
14
+
15
+ Args:
16
+ ak: Bio-OS 访问密钥
17
+ sk: Bio-OS 私钥
18
+ endpoint: Bio-OS API 端点,默认为 https://bio-top.miracle.ac.cn
19
+ """
20
+ self.ak = ak
21
+ self.sk = sk
22
+ self.endpoint = endpoint
23
+ # 配置 Bio-OS
24
+ Config.set_access_key(ak)
25
+ Config.set_secret_key(sk)
26
+ Config.set_endpoint(endpoint)
27
+
28
+ @staticmethod
29
+ def _fmt_default(raw: Any) -> str | None:
30
+ """
31
+ 将 Default 字段格式化成人类可读形式:
32
+ - None → None
33
+ - int/float → 123 / 1.23
34
+ - bool → true / false
35
+ - 其余字符串 → "value"(保留双引号)
36
+ """
37
+ if raw is None:
38
+ return None
39
+ if isinstance(raw, (int, float, bool)):
40
+ return str(raw).lower() # bool 转成 'true' / 'false'
41
+ if isinstance(raw, str):
42
+ lo = raw.lower()
43
+ # 尝试将字符串视为数字或布尔
44
+ if lo in {"true", "false"}:
45
+ return lo
46
+ try:
47
+ int(raw); return raw
48
+ except ValueError:
49
+ pass
50
+ try:
51
+ float(raw); return raw
52
+ except ValueError:
53
+ pass
54
+ return f"\"{raw}\""
55
+ return str(raw)
56
+
57
+ def get_workspace_id(self, workspace_name: str) -> str:
58
+ """
59
+ 获取工作区ID
60
+ Args:
61
+ workspace_name: 工作区名称
62
+
63
+ Returns:
64
+ str: 工作区ID
65
+
66
+ Raises:
67
+ NotFoundError: 未找到指定工作区
68
+ """
69
+ df = bioos.list_workspaces()
70
+ ser = df[df.Name == workspace_name].ID
71
+ if len(ser) != 1:
72
+ raise NotFoundError("Workspace", workspace_name)
73
+ return ser.to_list()[0]
74
+
75
+ def get_workflow(self, workspace_name: str, workflow_name: str):
76
+ """
77
+ 获取工作流对象
78
+
79
+ Args:
80
+ workspace_name: 工作区名称
81
+ workflow_name: 工作流名称
82
+
83
+ Returns:
84
+ Workflow: 工作流对象
85
+ Raises:
86
+ NotFoundError: 未找到指定工作区或工作流
87
+ """
88
+ workspace_id = self.get_workspace_id(workspace_name)
89
+ ws = bioos.workspace(workspace_id)
90
+ return ws.workflow(name=workflow_name)
91
+
92
+ def list_workflows(self, workspace_name: str) -> List[Dict[str, Any]]:
93
+ """
94
+ 列出工作区下的所有工作流
95
+
96
+ Args:
97
+ workspace_name: 工作区名称
98
+
99
+ Returns:
100
+ List[Dict[str, Any]]: 工作流列表
101
+ """
102
+ workspace_id = self.get_workspace_id(workspace_name)
103
+ ws = bioos.workspace(workspace_id)
104
+ return ws.list_workflows()
105
+
106
+ def get_workflow_inputs(self, workspace_name: str, workflow_name: str) -> Dict[str, str]:
107
+ """
108
+ 获取工作流的输入参数模板
109
+
110
+ Args:
111
+ workspace_name: 工作区名称
112
+ workflow_name: 工作流名称
113
+
114
+ Returns:
115
+ Dict[str, str]: 包含工作流输入参数模板的字典,格式为:
116
+ {
117
+ "param_name": "Type (optional, default = value)", # 对于可选参数
118
+ "param_name": "Type" # 对于必需参数
119
+ }
120
+ 其中:
121
+ - Type 为参数类型(如 String, Int, File 等)
122
+ - optional 表示参数为可选
123
+ - value 为默认值(数字和布尔值直接显示,字符串加引号)
124
+ """
125
+ try:
126
+ wf = self.get_workflow(workspace_name, workflow_name)
127
+ result = {}
128
+
129
+ for item in wf.inputs:
130
+ type_str = item.get("Type", "")
131
+ optional = item.get("Optional", False)
132
+ default = self._fmt_default(item.get("Default"))
133
+
134
+ if optional:
135
+ value = f"{type_str} (optional" + (f", default = {default})" if default is not None else ")")
136
+ else:
137
+ value = type_str
138
+
139
+ result[item["Name"]] = value
140
+
141
+ return result
142
+
143
+ except NotFoundError as e:
144
+ return {"error": str(e)}
145
+ except Exception as e:
146
+ return {"error": str(e)}
147
+
148
+ def get_workflow_outputs(self, workspace_name: str, workflow_name: str) -> Dict[str, str]:
149
+ """
150
+ 获取工作流的输出参数信息
151
+
152
+ Args:
153
+ workspace_name: 工作区名称
154
+ workflow_name: 工作流名称
155
+
156
+ Returns:
157
+ Dict[str, str]: 包含工作流输出参数的字典
158
+ """
159
+ try:
160
+ wf = self.get_workflow(workspace_name, workflow_name)
161
+ return {output["Name"]: output["Type"] for output in wf.outputs}
162
+ except NotFoundError as e:
163
+ return {"error": str(e)}
164
+ except Exception as e:
165
+ return {"error": str(e)}
166
+
167
+ def get_workflow_metadata(self, workspace_name: str, workflow_name: str) -> Dict[str, Any]:
168
+ """
169
+ 获取工作流的元数据信息
170
+
171
+ Args:
172
+ workspace_name: 工作区名称
173
+ workflow_name: 工作流名称
174
+
175
+ Returns:
176
+ Dict[str, Any]: 包含工作流元数据的字典,包括:
177
+ - name: 工作流名称
178
+ - description: 工作流描述
179
+ - language: 工作流语言
180
+ - source: 工作流源
181
+ - tag: 版本标签
182
+ - status: 工作流状态
183
+ - owner_name: 所有者
184
+ - create_time: 创建时间
185
+ - update_time: 更新时间
186
+ - main_workflow_path: 主工作流文件路径
187
+ - source_type: 源类型
188
+ """
189
+ try:
190
+ wf = self.get_workflow(workspace_name, workflow_name)
191
+ return {
192
+ "name": wf.name,
193
+ "description": wf.description,
194
+ "language": wf.language,
195
+ "source": wf.source,
196
+ "tag": wf.tag,
197
+ "status": wf.status,
198
+ "owner_name": wf.owner_name,
199
+ "create_time": wf.create_time,
200
+ "update_time": wf.update_time,
201
+ "main_workflow_path": wf.main_workflow_path,
202
+ "source_type": wf.source_type
203
+ }
204
+ except NotFoundError as e:
205
+ return {"error": str(e)}
206
+ except Exception as e:
207
+ return {"error": str(e)}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pybioos
3
- Version: 0.0.15
3
+ Version: 0.0.17
4
4
  Summary: BioOS SDK for Python
5
5
  Home-page: https://github.com/GBA-BI/pybioos
6
6
  Author: Jilong Liu
@@ -1,14 +1,15 @@
1
- bioos/__about__.py,sha256=zsL_PukbnoBYjoIOAjFEp5bxiw0QpluTLypKg_3j1mw,57
1
+ bioos/__about__.py,sha256=FvqUBfyX_OkxcuTYr1_lcBcEd13i1ew8F8tEpRfcor8,57
2
2
  bioos/__init__.py,sha256=4GZKi13lDTD25YBkGakhZyEQZWTER_OWQMNPoH_UM2c,22
3
- bioos/bioos.py,sha256=fHzOb1l5wYxw6NVYYZDiFcgk4V28BAgWEc3ev12reWs,2409
4
- bioos/bioos_workflow.py,sha256=BzEPOyAjgdK7Wafbl2b1_qG_VTEdp8xDwKS68tBovjs,14327
5
- bioos/bw_import.py,sha256=lQk_ch_tTz8l4bnWniOzWZ1IxI6ZvKlaASkNMsdDGfA,5697
6
- bioos/bw_import_status_check.py,sha256=sJuso2SAfZWvPzypnGge25Ayv5PsSGRXqSNNwIhNu-E,2794
7
- bioos/bw_status_check.py,sha256=FVilkawRA7GD1JXUBeaR28W1DfN9bAzYBIAjqi4JIno,2916
8
- bioos/config.py,sha256=CvFabYqV1BkFWO8fnr5vBf6xNtNzA8hAEVeEIbvAOm8,4307
3
+ bioos/bioos.py,sha256=yhaWaJMwP23fMtid2mNWUetGwDUbUtCKwugXt27OLTs,2600
4
+ bioos/bioos_workflow.py,sha256=LRzYxuNnbF3R7urrMRjMRwR2hGDmH_4kP52D2Gger8k,15259
5
+ bioos/bw_import.py,sha256=kYS4BDbyIXyaFIOCUyOnAzIbLdHvY3l3KLPDVfAnFSM,5829
6
+ bioos/bw_import_status_check.py,sha256=x9Y_0I8huoC7aBgObuox-0z6foSr99TXQB7jozyQ7Eo,2972
7
+ bioos/bw_status_check.py,sha256=Etj_zp6tS0cj4t-nksUq221pPIWUFS0Lz0q0Uqflpes,3094
8
+ bioos/config.py,sha256=-1qS0uYgV-h3d67LyOuJ3lzDskEytXHI7T65-p8tnhA,4405
9
9
  bioos/errors.py,sha256=p0fH6JSMYBjul88lMJ7PPwGNh4SYg62-7VMNuUXWl-E,2540
10
- bioos/get_submission_logs.py,sha256=jUtT8Vic8h_VOcqrqJsTBSonve64RjbKNAyp0wUtIpg,3934
10
+ bioos/get_submission_logs.py,sha256=e0OlWwMso8bB6u1TepJpRim2L-trxM6x3IdgZSwo7jc,4112
11
11
  bioos/log.py,sha256=twiCvf5IgJB7uvzANwBluSlztJN8ZrxbGZUBGlZ0vps,3204
12
+ bioos/workflow_info.py,sha256=5PT45Lv-Js84ljZxjb9uNl0Jd5l-LYnXyKHYKrOhcjI,6840
12
13
  bioos/internal/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
13
14
  bioos/internal/tos.py,sha256=0R6YN2lxjjZsuMfv0yLSkBmz_LqmzQGb8GagnUMc8EY,12264
14
15
  bioos/models/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
@@ -17,7 +18,7 @@ bioos/resource/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
17
18
  bioos/resource/data_models.py,sha256=enKp8yyQI8IbRqe--0Xtyg1XzOwQQPQzoQsx_hNuZ6E,5089
18
19
  bioos/resource/files.py,sha256=1HY0IHvq8H843VM2XZIHDdCuXXNcMrlEFhSNqWXmFzE,8456
19
20
  bioos/resource/utility.py,sha256=emY7qVLLLvGmQYlVj-_bLAxU7i1GfQOUybdRkfEDwVA,1300
20
- bioos/resource/workflows.py,sha256=v2jTAEBhVjp4U-iLaIswYBMkHZ7uQ96dZUE0zvjnrWQ,23426
21
+ bioos/resource/workflows.py,sha256=RZkREd7EzyRqk3gP09HSzeuI3i7Cn7VMhqjp4jj_cjg,29418
21
22
  bioos/resource/workspaces.py,sha256=Gmr8y_sjK7TQbhMhQ_7rxqR1KFcwU72I95YYCFrrLBQ,3995
22
23
  bioos/service/BioOsService.py,sha256=HuYUEwomHCLpA1MYgVqGyWAQWHM-_BHB-jmy9VsOlnQ,6724
23
24
  bioos/service/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
@@ -34,9 +35,9 @@ bioos/tests/workspaces.py,sha256=LuuRrTs2XqfE5mGQyJNl9RBtuMb4NZHBJFoO8HMZVYQ,522
34
35
  bioos/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
36
  bioos/utils/common_tools.py,sha256=fgMoE_-qZjgfQtUj_pmCTyYDtbJasyfH4Gm3VQsbgBQ,1651
36
37
  bioos/utils/workflows.py,sha256=zRbwTUigoM5V5LFOgzQPm3kwxt5Ogz95OFfefJc6Fjo,133
37
- pybioos-0.0.15.dist-info/LICENSE,sha256=cPkGXsgfPgEhIns7Lt3Avxx0Uy-VbdsoP8jvNGuj3cE,1063
38
- pybioos-0.0.15.dist-info/METADATA,sha256=mtaPib2vulNih35eFfmzWcpz8sdMHOWjZ-IYYwxqHDE,830
39
- pybioos-0.0.15.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92
40
- pybioos-0.0.15.dist-info/entry_points.txt,sha256=Sc5H0_X7r03Mef4Qd70bebqgdIbVAxLU7nV7qP7cKD4,328
41
- pybioos-0.0.15.dist-info/top_level.txt,sha256=llpzydkKVDSaWZgz3bsTUsQmhoQpc_JcRJg2-H-5a2U,6
42
- pybioos-0.0.15.dist-info/RECORD,,
38
+ pybioos-0.0.17.dist-info/LICENSE,sha256=cPkGXsgfPgEhIns7Lt3Avxx0Uy-VbdsoP8jvNGuj3cE,1063
39
+ pybioos-0.0.17.dist-info/METADATA,sha256=thfygW7I6xtSkuKrMuzRw00fzmtPRtvFB5yfNIHRoj8,830
40
+ pybioos-0.0.17.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92
41
+ pybioos-0.0.17.dist-info/entry_points.txt,sha256=Sc5H0_X7r03Mef4Qd70bebqgdIbVAxLU7nV7qP7cKD4,328
42
+ pybioos-0.0.17.dist-info/top_level.txt,sha256=llpzydkKVDSaWZgz3bsTUsQmhoQpc_JcRJg2-H-5a2U,6
43
+ pybioos-0.0.17.dist-info/RECORD,,