seqslab-cli 3.3.5__tar.gz → 3.3.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. {seqslab-cli-3.3.5/python/seqslab_cli.egg-info → seqslab-cli-3.3.7}/PKG-INFO +1 -1
  2. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/__init__.py +1 -1
  3. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/drs/commands.py +125 -48
  4. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/runsheet/runsheet.py +11 -2
  5. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/trs/commands.py +1 -3
  6. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/wes/commands.py +41 -32
  7. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/wes/internal/parameters.py +86 -40
  8. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/wes/resource/base.py +15 -0
  9. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/wes/template/base.py +5 -1
  10. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7/python/seqslab_cli.egg-info}/PKG-INFO +1 -1
  11. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/LICENSE +0 -0
  12. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/MANIFEST.in +0 -0
  13. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/README.md +0 -0
  14. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/auth/__init__.py +0 -0
  15. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/auth/azuread.py +0 -0
  16. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/auth/commands.py +0 -0
  17. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/cli.py +0 -0
  18. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/color.py +0 -0
  19. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/context.py +0 -0
  20. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/drs/__init__.py +0 -0
  21. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/drs/api/__init__.py +0 -0
  22. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/drs/api/azure.py +0 -0
  23. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/drs/api/base.py +0 -0
  24. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/drs/api/common.py +0 -0
  25. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/drs/api/template.py +0 -0
  26. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/drs/internal/__init__.py +0 -0
  27. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/drs/internal/aiocopy.py +0 -0
  28. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/drs/internal/common.py +0 -0
  29. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/drs/internal/utils.py +0 -0
  30. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/drs/storage/__init__.py +0 -0
  31. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/drs/storage/azure.py +0 -0
  32. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/drs/storage/base.py +0 -0
  33. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/drs/utils/__init__.py +0 -0
  34. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/drs/utils/atgxmetadata.py +0 -0
  35. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/drs/utils/biomimetype.py +0 -0
  36. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/drs/utils/progressbar.py +0 -0
  37. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/exceptions.py +0 -0
  38. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/organization/__init__.py +0 -0
  39. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/organization/commands.py +0 -0
  40. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/organization/resource/__init__.py +0 -0
  41. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/organization/resource/base.py +0 -0
  42. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/plugin.py +0 -0
  43. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/role/__init__.py +0 -0
  44. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/role/commands.py +0 -0
  45. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/role/internal/__init__.py +0 -0
  46. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/role/internal/common.py +0 -0
  47. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/role/resource/__init__.py +0 -0
  48. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/role/resource/azure.py +0 -0
  49. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/role/resource/base.py +0 -0
  50. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/runsheet/__init__.py +0 -0
  51. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/sample_sheet/__init__.py +0 -0
  52. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/sample_sheet/_version.py +0 -0
  53. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/sample_sheet/util.py +0 -0
  54. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/scr/__init__.py +0 -0
  55. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/scr/commands.py +0 -0
  56. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/scr/internal/__init__.py +0 -0
  57. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/scr/internal/common.py +0 -0
  58. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/scr/resource/__init__.py +0 -0
  59. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/scr/resource/azure.py +0 -0
  60. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/scr/resource/base.py +0 -0
  61. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/session_logger.py +0 -0
  62. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/settings.py +0 -0
  63. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/statusbar.py +0 -0
  64. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/trs/__init__.py +0 -0
  65. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/trs/internal/__init__.py +0 -0
  66. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/trs/internal/utils.py +0 -0
  67. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/trs/register/__init__.py +0 -0
  68. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/trs/register/azure.py +0 -0
  69. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/trs/register/base.py +0 -0
  70. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/trs/register/common.py +0 -0
  71. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/trs/register/template.py +0 -0
  72. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/trs/resource/__init__.py +0 -0
  73. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/trs/resource/azure.py +0 -0
  74. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/trs/resource/base.py +0 -0
  75. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/trs/resource/common.py +0 -0
  76. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/trs/template/__init__.py +0 -0
  77. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/trs/template/base.py +0 -0
  78. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/trs/template/template.py +0 -0
  79. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/usage_logger.py +0 -0
  80. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/user/__init__.py +0 -0
  81. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/user/commands.py +0 -0
  82. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/user/internal/__init__.py +0 -0
  83. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/user/internal/common.py +0 -0
  84. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/user/resource/__init__.py +0 -0
  85. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/user/resource/azure.py +0 -0
  86. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/user/resource/base.py +0 -0
  87. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/wes/__init__.py +0 -0
  88. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/wes/internal/__init__.py +0 -0
  89. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/wes/resource/__init__.py +0 -0
  90. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/wes/resource/azure.py +0 -0
  91. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/wes/resource/common.py +0 -0
  92. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/wes/template/__init__.py +0 -0
  93. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/wes/template/template.py +0 -0
  94. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/workspace/__init__.py +0 -0
  95. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/workspace/commands.py +0 -0
  96. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/workspace/internal/__init__.py +0 -0
  97. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/workspace/internal/common.py +0 -0
  98. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/workspace/resource/__init__.py +0 -0
  99. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/workspace/resource/azure.py +0 -0
  100. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab/workspace/resource/base.py +0 -0
  101. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab_cli.egg-info/SOURCES.txt +0 -0
  102. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab_cli.egg-info/dependency_links.txt +0 -0
  103. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab_cli.egg-info/entry_points.txt +0 -0
  104. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab_cli.egg-info/requires.txt +0 -0
  105. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab_cli.egg-info/top_level.txt +0 -0
  106. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/python/seqslab_cli.egg-info/zip-safe +0 -0
  107. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/requirements.txt +0 -0
  108. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/setup.cfg +0 -0
  109. {seqslab-cli-3.3.5 → seqslab-cli-3.3.7}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: seqslab-cli
3
- Version: 3.3.5
3
+ Version: 3.3.7
4
4
  Summary: Atgenomix SeqsLab Command Line Tool
5
5
  Home-page: https://github.com/AnomeGAP/seqslab-cli
6
6
  Author: Allen Chang
@@ -21,6 +21,6 @@ name = "seqslab"
21
21
  __all__ = []
22
22
 
23
23
 
24
- __version__ = "3.3.5"
24
+ __version__ = "3.3.7"
25
25
 
26
26
  LOGGING = {"DIR_PATH": "/var/log/seqslab"}
@@ -886,52 +886,6 @@ class BaseDatahub:
886
886
 
887
887
  return results
888
888
 
889
- @command(aliases=["deregister"])
890
- @argument(
891
- "id",
892
- type=List[str],
893
- positional=False,
894
- description="Specify the IDs of the DRS objects that you want to delete "
895
- "(optional).",
896
- )
897
- @argument(
898
- "names",
899
- type=List[str],
900
- positional=False,
901
- description="Specify the names of the DRS objects that you want to delete "
902
- "(optional).",
903
- )
904
- @argument(
905
- "tags",
906
- type=List[str],
907
- positional=False,
908
- description="Specify the labels of the DRS objects that you want to delete "
909
- "(optional).",
910
- )
911
- def deregister(
912
- self,
913
- id: List[str] = [],
914
- tags: List[str] = [],
915
- names: List[str] = [],
916
- ) -> int:
917
- """
918
- Deregister DRS objects by DRS ID, DRS name, or DRS tag.
919
- """
920
- if not id and not names and not tags:
921
- cprint(
922
- "Must specify one of the IDs, names or tags to identify DRS objects for deletion",
923
- "red",
924
- )
925
- return errno.ENOENT
926
-
927
- resps = asyncio.run(
928
- utils.drs_delete(id, names, tags, query_opts="?backend_content=false")
929
- )
930
- for r in resps:
931
- cprint(r, "yellow")
932
-
933
- return 0
934
-
935
889
  @command(aliases=["clean"])
936
890
  @argument(
937
891
  "id",
@@ -974,6 +928,10 @@ class BaseDatahub:
974
928
  resps = asyncio.run(
975
929
  utils.drs_delete(id, names, tags, query_opts="?backend_content=true")
976
930
  )
931
+ if isinstance(resps, int):
932
+ cprint(f"Delete with return code {resps}", "red")
933
+ return resps
934
+
977
935
  for r in resps:
978
936
  cprint(r, "yellow")
979
937
 
@@ -1090,7 +1048,9 @@ class BaseDatahub:
1090
1048
  fq_paths = BaseDatahub.find_fastq_paths(fastq_path, fastq_signature)
1091
1049
 
1092
1050
  if run_sheet.SampleSheet.is_single_end:
1093
- assert len(fq_paths) == 1
1051
+ assert (
1052
+ len(fq_paths) == 1
1053
+ ), f"{str(fq_paths)} does not contain exactly one fastq path"
1094
1054
  sample_meta["Pair"] = "1"
1095
1055
  upload_info.append(
1096
1056
  {
@@ -1110,7 +1070,9 @@ class BaseDatahub:
1110
1070
  )
1111
1071
 
1112
1072
  if run_sheet.SampleSheet.is_paired_end:
1113
- assert len(fq_paths) == 2
1073
+ assert (
1074
+ len(fq_paths) == 2
1075
+ ), f"{str(fq_paths)} does not contain exactly two fastq paths"
1114
1076
  fq_paths.sort()
1115
1077
  sample_meta["Pair"] = "1"
1116
1078
  upload_info.append(
@@ -1455,6 +1417,121 @@ class BaseDatahub:
1455
1417
  api_backend = drs_register().load_register(workspace)
1456
1418
  return api_backend.get_drs(drs_id=id)
1457
1419
 
1420
+ @exception_handler
1421
+ def _patch_add_read_drs(self, add_reads_id: str, workspace: str, tag: str):
1422
+ results = self._get(add_reads_id, workspace)
1423
+ if isinstance(results, int):
1424
+ return results
1425
+ cprint(f"Found Add Reads target DRS object {add_reads_id}", color="yellow")
1426
+ tags = [item["name"] for item in results["tags"]] + [tag]
1427
+ patch_payload = {"tags": tags}
1428
+ results = self._change(add_reads_id, **patch_payload)
1429
+ if isinstance(results, int):
1430
+ return results
1431
+ cprint(
1432
+ f"Add Reads target DRS object {add_reads_id} patched with tags {tags}",
1433
+ color="yellow",
1434
+ )
1435
+ return add_reads_id
1436
+
1437
+ @command(aliases=["add-reads"])
1438
+ @argument(
1439
+ "workspace",
1440
+ type=str,
1441
+ positional=False,
1442
+ description="Specify the workspace based on the signed in account (required).",
1443
+ )
1444
+ @argument(
1445
+ "run_sheet",
1446
+ type=str,
1447
+ positional=False,
1448
+ description="Specify the Run Sheet file path (required).",
1449
+ )
1450
+ @argument(
1451
+ "seq_run_id",
1452
+ type=str,
1453
+ positional=False,
1454
+ description="Specify a runsheet header field as a sequencer run identifier; the specified value will be "
1455
+ "used as a sequencer run specific label for future dataset management (optional).",
1456
+ )
1457
+ def add_reads_runsheet(
1458
+ self,
1459
+ workspace,
1460
+ run_sheet: str,
1461
+ seq_run_id: str = "",
1462
+ ) -> int:
1463
+ """
1464
+ Identify and Patch existing DRS object with add-reads runsheet label for runsheet-based run triggering for
1465
+ DRS auto-matching
1466
+ """
1467
+ if not self._valide_workspace(workspace):
1468
+ logging.error("Workspace not found")
1469
+ cprint("Workspace not found.", "red")
1470
+ return errno.EINVAL
1471
+
1472
+ # SampleSheetV2 based RunSheet SeqsLab fields
1473
+ seqslab_section = "SeqsLabRunSheet"
1474
+ seqslab_format = "SeqsLabColumnFormat"
1475
+ seqslab_sep = "#"
1476
+ rs = RunSheet(run_sheet, seqslab_section, seqslab_format, seqslab_sep)
1477
+
1478
+ # set base tags
1479
+ base_tgs = ""
1480
+ try:
1481
+ if seq_run_id:
1482
+ base_tgs = f"{rs.SampleSheet.Header[seq_run_id]}/"
1483
+ except KeyError:
1484
+ print(
1485
+ f"Given base_label_field not found in SampleSheet Header {seq_run_id}"
1486
+ )
1487
+ return errno.EINVAL
1488
+
1489
+ add_reads = False
1490
+ for sa in rs.SampleSheet.samples:
1491
+ if rs.SampleSheet.is_single_end:
1492
+ assert not sa.to_json().get("Add_Read2_ID") and not sa.to_json().get(
1493
+ "Add_Read2_Label"
1494
+ ), "columns Add_Read2_ID and Add_Read2_Label should be blank for single_end sequencer run"
1495
+ if (add_read1_id := sa.to_json().get("Add_Read1_ID")) and (
1496
+ add_read1_label := sa.to_json().get("Add_Read1_Label")
1497
+ ):
1498
+ add_reads = True
1499
+ ret = self._patch_add_read_drs(
1500
+ add_read1_id,
1501
+ workspace,
1502
+ f"{base_tgs}{sa.get('Run_Name', rs.SampleSheet.Header.Date)}/"
1503
+ f"{add_read1_label}",
1504
+ )
1505
+ if isinstance(ret, int):
1506
+ return ret
1507
+ if rs.SampleSheet.is_paired_end:
1508
+ if (add_read1_id := sa.to_json().get("Add_Read1_ID")) and (
1509
+ add_read1_label := sa.to_json().get("Add_Read1_Label")
1510
+ ):
1511
+ add_reads = True
1512
+ ret = self._patch_add_read_drs(
1513
+ add_read1_id,
1514
+ workspace,
1515
+ f"{base_tgs}{sa.get('Run_Name', rs.SampleSheet.Header.Date)}/{add_read1_label}",
1516
+ )
1517
+ if isinstance(ret, int):
1518
+ return ret
1519
+ if (add_read2_id := sa.to_json().get("Add_Read2_ID")) and (
1520
+ add_read2_label := sa.to_json().get("Add_Read2_Label")
1521
+ ):
1522
+ add_reads = True
1523
+ ret = self._patch_add_read_drs(
1524
+ add_read2_id,
1525
+ workspace,
1526
+ f"{base_tgs}{sa.get('Run_Name', rs.SampleSheet.Header.Date)}/{add_read2_label}",
1527
+ )
1528
+ if isinstance(ret, int):
1529
+ return ret
1530
+
1531
+ if not add_reads:
1532
+ cprint("No DRS object is patched in this operation", color="yellow")
1533
+ return 0
1534
+
1458
1535
 
1459
1536
  @command
1460
1537
  class Datahub(BaseDatahub):
@@ -16,18 +16,25 @@ class Run:
16
16
  - ``"Run_Name"``
17
17
  - ``"Workflow_URL"``
18
18
  - ``"Runtimes"``
19
+ - ``"Run_Schedule_ID``
19
20
 
20
21
  A run may include multiple samples. For samples in a Run Sheet, samples with the same Run_Name will be clustered
21
22
  as a single run.
22
23
  """
23
24
 
24
25
  def __init__(
25
- self, samples: List[Sample], run_name: str, workflow_url: str, runtimes: str
26
+ self,
27
+ samples: List[Sample],
28
+ run_name: str,
29
+ workflow_url: str,
30
+ runtimes: str,
31
+ run_schedule_id: str = None,
26
32
  ) -> None:
27
33
  self.sample_sheet: Optional[SampleSheet] = None
28
34
  self.run_name = run_name
29
35
  self.workflow_url = workflow_url
30
36
  self.runtimes = runtimes
37
+ self.run_schedule_id = run_schedule_id
31
38
  self.samples = []
32
39
  if not workflow_url.endswith("/"):
33
40
  raise ValueError(
@@ -40,6 +47,7 @@ class Run:
40
47
  s.get("Run_Name") == self.run_name
41
48
  and s.get("Workflow_URL") == self.workflow_url
42
49
  and s.get("Runtimes") == self.runtimes
50
+ and s.get("Run_Schedule_ID") == self.run_schedule_id
43
51
  ):
44
52
  self.samples.append(s)
45
53
 
@@ -162,6 +170,7 @@ class RunSheet:
162
170
  sample.get("Run_Name"),
163
171
  sample.get("Workflow_URL"),
164
172
  sample.get("Runtimes"),
173
+ sample.get("Run_Schedule_ID"),
165
174
  )
166
175
  rn = sample.get("Run_Name")
167
176
  if rsig in runs and rn in run_name_set:
@@ -174,7 +183,7 @@ class RunSheet:
174
183
  f"Inconsistent run_name/run_name_set {rn}/{run_name_set} and run sig/runs {rsig}/{runs.keys()}"
175
184
  )
176
185
  for k, v in runs.items():
177
- self._runs.append(Run(v, k[0], k[1], k[2]))
186
+ self._runs.append(Run(v, k[0], k[1], k[2], k[3]))
178
187
 
179
188
  @property
180
189
  def runs(self) -> List:
@@ -408,9 +408,7 @@ class BaseTools:
408
408
  positional=False,
409
409
  description="Specify whether to force reload system cache for SCR (optional, default = False).",
410
410
  )
411
- def images(
412
- self, scr_id: str, repositories: List[str] = [], reload: bool = False
413
- ) -> int:
411
+ def images(self, scr_id: str, repositories: List[str], reload: bool = False) -> int:
414
412
  """
415
413
  List image tags and details of given repositories in an SCR.
416
414
  """
@@ -19,13 +19,13 @@ from seqslab.auth.commands import BaseAuth
19
19
  from seqslab.exceptions import exception_handler
20
20
  from seqslab.runsheet.runsheet import Run, RunSheet
21
21
  from seqslab.trs.register.common import trs_register
22
- from seqslab.wes import API_HOSTNAME, __version__
23
- from seqslab.wes.internal import parameters
24
22
  from seqslab.workspace.internal.common import get_factory as get_workspace_factory
25
23
  from tenacity import retry, stop_after_attempt, wait_fixed
26
24
  from termcolor import cprint
27
25
  from tzlocal import get_localzone
28
26
 
27
+ from . import API_HOSTNAME, __version__
28
+ from .internal import parameters
29
29
  from .resource.common import get_factory
30
30
 
31
31
  """
@@ -494,38 +494,32 @@ class BaseJobs:
494
494
  request_path = f"{working_dir}/{rpath}-request.json"
495
495
  wf_info = run.workflow_url.split("versions")[1].strip("/").split("/")
496
496
 
497
- if not execs:
498
- trs_register().load_resource().get_execs_json(
499
- workflow_url=run.workflow_url, download_path=execs_path
500
- )
497
+ resource = get_factory().load_resource()
498
+ if run.run_schedule_id:
499
+ req = resource.get_schedule(run.run_schedule_id).get("request")
500
+ params = req.get("workflow_params")
501
+ backend_params = req.get("workflow_backend_params")
501
502
  else:
502
- execs_path = f"{working_dir}/{execs}"
503
503
 
504
- resource = get_factory().load_resource()
505
- ops = resource.list_operator_pipelines(page=1, page_size=1000)["results"]
506
- opp_w_args = [
507
- op["id"]
508
- for op in ops
509
- for operator in op["operators"]
510
- if isinstance(operator, dict) and operator.get("arguments")
511
- ]
512
- params = parameters.workflow_params(
513
- execs_path,
514
- run,
515
- is_runsheet_template,
516
- is_single_end,
517
- fq_signature,
518
- opp_w_args,
519
- )
520
- if not isinstance(params, dict):
521
- raise Exception(
522
- f"Unable to generate workflow_params based on given exec_path, with error code {params}"
504
+ if not execs:
505
+ trs_register().load_resource().get_execs_json(
506
+ workflow_url=run.workflow_url, download_path=execs_path
507
+ )
508
+ else:
509
+ execs_path = f"{working_dir}/{execs}"
510
+
511
+ ops = resource.list_operator_pipelines(page=1, page_size=1000)["results"]
512
+ opp_w_args = [
513
+ op["id"]
514
+ for op in ops
515
+ for operator in op["operators"]
516
+ if isinstance(operator, dict) and operator.get("arguments")
517
+ ]
518
+ params = parameters.workflow_params(
519
+ execs_path,
520
+ opp_w_args,
523
521
  )
524
-
525
- request = {
526
- "name": run.run_name,
527
- "workflow_params": params,
528
- "workflow_backend_params": parameters.workflow_backend_params(
522
+ backend_params = parameters.workflow_backend_params(
529
523
  execs_path,
530
524
  workspace,
531
525
  run.runtimes,
@@ -533,7 +527,22 @@ class BaseJobs:
533
527
  trust,
534
528
  kernel_version,
535
529
  token_lifetime,
536
- ),
530
+ )
531
+
532
+ if is_runsheet_template:
533
+ params = parameters.runsheet_rendering(
534
+ params, run, fq_signature, is_single_end
535
+ )
536
+
537
+ if not isinstance(params, dict):
538
+ raise Exception(
539
+ f"Unable to generate workflow_params based on given exec_path, with error code {params}"
540
+ )
541
+
542
+ request = {
543
+ "name": run.run_name,
544
+ "workflow_params": params,
545
+ "workflow_backend_params": backend_params,
537
546
  "workflow_url": run.workflow_url,
538
547
  "workflow_type_version": "1.0",
539
548
  "workflow_type": wf_info[1],
@@ -31,26 +31,38 @@ laws and state trade secret laws, punishable by civil and criminal penalties.
31
31
  """
32
32
 
33
33
 
34
- def validate_label_column(run: Run):
34
+ def label2fqn(run: Run):
35
35
  r1fqn = set(
36
- [
37
- ".".join(re.sub(r"(/\d+)*$", "", sample.Read1_Label).split("/"))
38
- for sample in run.samples
39
- ]
36
+ list(
37
+ map(
38
+ lambda sample: (
39
+ re.sub(r"(/\d+)*$", "", sample.Read1_Label),
40
+ ".".join(re.sub(r"(/\d+)*$", "", sample.Read1_Label).split("/")),
41
+ ),
42
+ run.samples,
43
+ )
44
+ )
40
45
  )
41
- assert len(r1fqn) == 1
42
46
  r2fqn = set(
43
- [
44
- ".".join(re.sub(r"(/\d+)*$", "", sample.Read2_Label).split("/"))
45
- for sample in run.samples
46
- ]
47
+ list(
48
+ map(
49
+ lambda sample: (
50
+ re.sub(r"(/\d+)*$", "", sample.Read2_Label),
51
+ ".".join(re.sub(r"(/\d+)*$", "", sample.Read2_Label).split("/")),
52
+ ),
53
+ run.samples,
54
+ )
55
+ )
47
56
  )
48
- assert len(r2fqn) == 1
49
57
  if not r1fqn and not r2fqn:
50
58
  raise Exception(
51
59
  "runsheet template without Read1_Label and Read2_Label assignment"
52
60
  )
53
- return r1fqn.pop(), r2fqn.pop()
61
+ # normalize r1fqn and r2fqn length
62
+ list_r2fqn = (
63
+ list(r2fqn) if len(r2fqn) == len(r1fqn) else [("", "") for item in r1fqn]
64
+ )
65
+ return list(r1fqn), list_r2fqn
54
66
 
55
67
 
56
68
  def is_castable_to_int(s):
@@ -113,13 +125,68 @@ def _fastq_expr(rule: str, meta: dict) -> str:
113
125
  return expr
114
126
 
115
127
 
128
+ def validate_execs(exec_content: dict):
129
+ inputs = exec_content.get("inputs")
130
+ datasets = exec_content.get("connections")
131
+ for fqn in datasets:
132
+ if fqn not in inputs:
133
+ cprint(f"datasets FQN {fqn} not in inputs.json", "red")
134
+ return False
135
+ return True
136
+
137
+
138
+ def runsheet_rendering(params: dict, run: Run, fq_sig: str, is_single_end: bool):
139
+ # inputs.json rendering based on runsheet info for both normal runs and add-reads runs, also reset datasets section
140
+ tpl_r1fqn, tpl_r2fqn = label2fqn(run)
141
+ for idx in range(0, len(tpl_r1fqn)):
142
+ r1lbpfx = tpl_r1fqn[idx][0]
143
+ r2lbpfx = tpl_r2fqn[idx][0]
144
+ lb1 = []
145
+ lb2 = []
146
+ na1 = []
147
+ na2 = []
148
+ for sa in run.samples:
149
+ info_dic = {k.replace(" ", "_"): v for k, v in sa.to_json().items()}
150
+
151
+ lbo1 = sa.Read1_Label
152
+ lba1 = sa.to_json().get("Add_Read1_Label", "")
153
+ if r1lbpfx in lbo1:
154
+ lb1.append(lbo1)
155
+ na1.append(f"{_fastq_expr(fq_sig, info_dic)}_r1")
156
+ if r1lbpfx in lba1:
157
+ lb1.append(lba1)
158
+ na1.append(f"{_fastq_expr(fq_sig, info_dic)}_org_r1")
159
+
160
+ lbo2 = sa.Read2_Label
161
+ lba2 = sa.to_json().get("Add_Read2_Label", "")
162
+ if r2lbpfx in lbo2:
163
+ lb2.append(lbo2)
164
+ na2.append(f"{_fastq_expr(fq_sig, info_dic)}_r2")
165
+ if r2lbpfx in lba2:
166
+ lb2.append(lba2)
167
+ na2.append(f"{_fastq_expr(fq_sig, info_dic)}_org_r2")
168
+
169
+ r1fqn = tpl_r1fqn[idx][1]
170
+ r2fqn = tpl_r2fqn[idx][1]
171
+ if r1fqn == r2fqn:
172
+ params["inputs"][r1fqn] = populate_fqn(lb1 + lb2, na1 + na2)
173
+ params["datasets"][r1fqn] = None
174
+ return params
175
+ else:
176
+ params["inputs"][r1fqn] = populate_fqn(lb1, na1)
177
+ params["datasets"][r1fqn] = None
178
+
179
+ if r2fqn:
180
+ assert is_single_end is False, "R2FQN should not be set for single end run"
181
+ params["inputs"][r2fqn] = populate_fqn(lb2, na2)
182
+ params["datasets"][r2fqn] = None
183
+
184
+ return params
185
+
186
+
116
187
  def workflow_params(
117
188
  execs_json: str,
118
- run: Run,
119
- is_runsheet_template: bool,
120
- is_single_end: bool,
121
- fq_sig: str,
122
- opp_w_args: dict,
189
+ opp_w_args: list,
123
190
  ) -> dict:
124
191
  """
125
192
  Create workflow_params.json.
@@ -132,33 +199,12 @@ def workflow_params(
132
199
  try:
133
200
  with open(execs_json, "r") as f:
134
201
  t_content = json.loads(f.read())
202
+ if not validate_execs(t_content):
203
+ return errno.EINVAL
135
204
 
136
205
  params = WorkflowParamsTemplate().create(
137
206
  ex_template=t_content, opp_w_args=opp_w_args
138
207
  )
139
-
140
- if is_runsheet_template:
141
- r1fqn, r2fqn = validate_label_column(run)
142
- lb1 = [sa.Read1_Label for sa in run.samples]
143
- lb2 = [sa.Read2_Label for sa in run.samples]
144
- na1 = [
145
- f'{_fastq_expr(fq_sig, {k.replace(" ", "_"): v for k, v in sa.to_json().items()})}_r1'
146
- for sa in run.samples
147
- ]
148
- na2 = [
149
- f'{_fastq_expr(fq_sig, {k.replace(" ", "_"): v for k, v in sa.to_json().items()})}_r2'
150
- for sa in run.samples
151
- ]
152
-
153
- if r1fqn == r2fqn:
154
- params["inputs"][r1fqn] = populate_fqn(lb1 + lb2, na1 + na2)
155
- return params
156
- else:
157
- params["inputs"][r1fqn] = populate_fqn(lb1, na1)
158
-
159
- if r2fqn:
160
- assert is_single_end is False
161
- params["inputs"][r2fqn] = populate_fqn(lb2, na2)
162
208
  return params
163
209
  except zipfile.BadZipfile as error:
164
210
  cprint(f"{error}", "red")
@@ -56,6 +56,9 @@ class BaseResource(ABC):
56
56
  f"{WES_BASE_URL}runtime-options/{{name}}?backend={{backend}}"
57
57
  )
58
58
  WES_SCHEDULES_URL = f"{WES_BASE_URL}schedules/?backend={{backend}}"
59
+ WES_SCHEDULES_OBJECT_URL = (
60
+ f"{WES_BASE_URL}schedules/{{obj_id}}/?backend={{backend}}"
61
+ )
59
62
 
60
63
  class Response(NamedTuple):
61
64
  status: int
@@ -376,3 +379,15 @@ class BaseResource(ABC):
376
379
  raise err
377
380
 
378
381
  return response
382
+
383
+ def get_schedule(self, schedule_id) -> dict:
384
+ ctx = context.get_context()
385
+ backend = ctx.args.backend
386
+ token = BaseAuth.get_token().get("tokens").get("access")
387
+ with requests.get(
388
+ url=f"{self.WES_SCHEDULES_OBJECT_URL.format(obj_id=schedule_id, backend=backend)}",
389
+ headers={"Authorization": f"Bearer {token}"},
390
+ ) as response:
391
+ if response.status_code not in [requests.codes.ok]:
392
+ raise requests.HTTPError(response.text)
393
+ return json.loads(response.content)
@@ -1,6 +1,10 @@
1
1
  class WorkflowParamsTemplate:
2
2
  def create(self, ex_template: dict, opp_w_args: dict) -> dict:
3
- operator_pipelines = self.operator_pipelines(ex_template, opp_w_args)
3
+ operator_pipelines = (
4
+ self.operator_pipelines(ex_template, opp_w_args)
5
+ if "tasks" not in ex_template
6
+ else (ex_template["tasks"])
7
+ )
4
8
  return {
5
9
  "inputs": ex_template.get("inputs"),
6
10
  "datasets": ex_template.get("connections", None),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: seqslab-cli
3
- Version: 3.3.5
3
+ Version: 3.3.7
4
4
  Summary: Atgenomix SeqsLab Command Line Tool
5
5
  Home-page: https://github.com/AnomeGAP/seqslab-cli
6
6
  Author: Allen Chang
File without changes
File without changes
File without changes
File without changes
File without changes