dnastack-client-library 3.1.211__py3-none-any.whl → 3.1.213__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.
@@ -1,6 +1,7 @@
1
1
  from dnastack.cli.commands.workbench.runs.commands import init_runs_commands
2
2
  from dnastack.cli.commands.workbench.runs.events import events_command_group
3
3
  from dnastack.cli.commands.workbench.runs.hooks import hooks_command_group
4
+ from dnastack.cli.commands.workbench.runs.samples import samples_command_group
4
5
  from dnastack.cli.commands.workbench.runs.tasks import tasks_command_group
5
6
  from dnastack.cli.core.group import formatted_group
6
7
 
@@ -16,3 +17,4 @@ init_runs_commands(runs_command_group)
16
17
  runs_command_group.add_command(tasks_command_group)
17
18
  runs_command_group.add_command(events_command_group)
18
19
  runs_command_group.add_command(hooks_command_group)
20
+ runs_command_group.add_command(samples_command_group)
@@ -0,0 +1,10 @@
1
+ from dnastack.cli.commands.workbench.runs.samples.commands import init_samples_commands
2
+ from dnastack.cli.core.group import formatted_group
3
+
4
+
5
+ @formatted_group('samples')
6
+ def samples_command_group():
7
+ """Manage the samples associated with a run"""
8
+
9
+
10
+ init_samples_commands(samples_command_group)
@@ -0,0 +1,123 @@
1
+ from typing import Optional
2
+
3
+ import click
4
+
5
+ from dnastack.cli.commands.workbench.utils import get_ewes_client, NAMESPACE_ARG
6
+ from dnastack.cli.core.command import formatted_command
7
+ from dnastack.cli.core.command_spec import ArgumentSpec, CONTEXT_ARG, SINGLE_ENDPOINT_ID_ARG
8
+ from dnastack.cli.helpers.exporter import to_json, normalize
9
+ from dnastack.client.workbench.ewes.models import SimpleSample
10
+
11
+
12
+ def init_samples_commands(group):
13
+
14
+ @formatted_command(
15
+ group=group,
16
+ name='add',
17
+ specs=[
18
+ ArgumentSpec(
19
+ name='run_id',
20
+ arg_names=['--run-id'],
21
+ help='The ID of the run to add samples to',
22
+ required=True,
23
+ ),
24
+ ArgumentSpec(
25
+ name='samples',
26
+ arg_names=['--sample'],
27
+ help='Sample ID to add to the run. Can be specified multiple times.',
28
+ as_option=True,
29
+ multiple=True,
30
+ required=True,
31
+ ),
32
+ ArgumentSpec(
33
+ name='storage_account_id',
34
+ arg_names=['--storage-account'],
35
+ help='The storage account ID to associate with the samples.',
36
+ ),
37
+ NAMESPACE_ARG,
38
+ CONTEXT_ARG,
39
+ SINGLE_ENDPOINT_ID_ARG,
40
+ ]
41
+ )
42
+ def add_samples(context: Optional[str] = None,
43
+ endpoint_id: Optional[str] = None,
44
+ namespace: Optional[str] = None,
45
+ run_id: str = None,
46
+ samples: tuple = (),
47
+ storage_account_id: Optional[str] = None):
48
+ """Add samples to a run. Existing samples are preserved."""
49
+ client = get_ewes_client(context_name=context, endpoint_id=endpoint_id, namespace=namespace)
50
+ run = client.get_run(run_id)
51
+ existing = run.request.samples if run.request and run.request.samples else []
52
+
53
+ existing_ids = {s.id for s in existing}
54
+ new_samples = list(existing)
55
+ for sample_id in samples:
56
+ if sample_id not in existing_ids:
57
+ new_samples.append(SimpleSample(id=sample_id, storage_account_id=storage_account_id))
58
+
59
+ result = client.update_run_samples(run_id, new_samples)
60
+ click.echo(to_json(normalize(result)))
61
+
62
+ @formatted_command(
63
+ group=group,
64
+ name='remove',
65
+ specs=[
66
+ ArgumentSpec(
67
+ name='run_id',
68
+ arg_names=['--run-id'],
69
+ help='The ID of the run to remove samples from',
70
+ required=True,
71
+ ),
72
+ ArgumentSpec(
73
+ name='samples',
74
+ arg_names=['--sample'],
75
+ help='Sample ID to remove from the run. Can be specified multiple times.',
76
+ as_option=True,
77
+ multiple=True,
78
+ required=True,
79
+ ),
80
+ NAMESPACE_ARG,
81
+ CONTEXT_ARG,
82
+ SINGLE_ENDPOINT_ID_ARG,
83
+ ]
84
+ )
85
+ def remove_samples(context: Optional[str] = None,
86
+ endpoint_id: Optional[str] = None,
87
+ namespace: Optional[str] = None,
88
+ run_id: str = None,
89
+ samples: tuple = ()):
90
+ """Remove samples from a run."""
91
+ client = get_ewes_client(context_name=context, endpoint_id=endpoint_id, namespace=namespace)
92
+ run = client.get_run(run_id)
93
+ existing = run.request.samples if run.request and run.request.samples else []
94
+
95
+ remove_ids = set(samples)
96
+ remaining = [s for s in existing if s.id not in remove_ids]
97
+
98
+ result = client.update_run_samples(run_id, remaining)
99
+ click.echo(to_json(normalize(result)))
100
+
101
+ @formatted_command(
102
+ group=group,
103
+ name='clear',
104
+ specs=[
105
+ ArgumentSpec(
106
+ name='run_id',
107
+ arg_names=['--run-id'],
108
+ help='The ID of the run to clear all samples from',
109
+ required=True,
110
+ ),
111
+ NAMESPACE_ARG,
112
+ CONTEXT_ARG,
113
+ SINGLE_ENDPOINT_ID_ARG,
114
+ ]
115
+ )
116
+ def clear_samples(context: Optional[str] = None,
117
+ endpoint_id: Optional[str] = None,
118
+ namespace: Optional[str] = None,
119
+ run_id: str = None):
120
+ """Remove all samples from a run."""
121
+ client = get_ewes_client(context_name=context, endpoint_id=endpoint_id, namespace=namespace)
122
+ result = client.update_run_samples(run_id, [])
123
+ click.echo(to_json(normalize(result)))
@@ -12,7 +12,7 @@ from dnastack.client.workbench.ewes.models import ExtendedRunEvents, WesServiceI
12
12
  TaskListResponse, LogType, ExecutionEngineListOptions, ExecutionEngineListResponse, ExecutionEngine, \
13
13
  EngineParamPreset, EngineParamPresetListOptions, EngineParamPresetListResponse, \
14
14
  EngineHealthCheck, EngineHealthCheckListOptions, EngineHealthCheckListResponse, \
15
- Hook, HookListResponse
15
+ Hook, HookListResponse, SimpleSample, UpdateRunSamplesRequest
16
16
  from dnastack.common.tracing import Span
17
17
  from dnastack.http.session import HttpSession
18
18
 
@@ -300,6 +300,18 @@ class EWesClient(BaseWorkbenchClient):
300
300
  )
301
301
  return Hook(**response.json())
302
302
 
303
+ def update_run_samples(self, run_id: str, samples: List[SimpleSample], trace: Optional[Span] = None) -> ExtendedRunStatus:
304
+ trace = trace or Span(origin=self)
305
+ with self.create_http_session() as session:
306
+ request = UpdateRunSamplesRequest(samples=samples)
307
+ response = session.submit(
308
+ method='put',
309
+ url=urljoin(self.endpoint.url, f'{self.namespace}/ga4gh/wes/v1/runs/{run_id}/samples'),
310
+ json=request.model_dump(by_alias=True),
311
+ trace_context=trace
312
+ )
313
+ return ExtendedRunStatus(**response.json())
314
+
303
315
  def list_engines(self,
304
316
  list_options: ExecutionEngineListOptions,
305
317
  max_results: int = None,
@@ -35,6 +35,10 @@ class SimpleSample(BaseModel):
35
35
  storage_account_id: Optional[str] = None
36
36
 
37
37
 
38
+ class UpdateRunSamplesRequest(BaseModel):
39
+ samples: List[SimpleSample]
40
+
41
+
38
42
  class Hook(BaseModel):
39
43
  id: Optional[str] = None
40
44
  type: Optional[str] = None
@@ -173,6 +177,22 @@ class RunSubmittedToEngineMetadata(BaseModel):
173
177
  external_id: Optional[str] = None
174
178
 
175
179
 
180
+ class SamplesUpdatedMetadata(BaseModel):
181
+ event_type: Literal["SAMPLES_UPDATED"]
182
+ message: Optional[str] = None
183
+ old_sample_ids: Optional[List[SampleId]] = None
184
+ new_sample_ids: Optional[List[SampleId]] = None
185
+ state: Optional[State] = None
186
+ start_time: Optional[str] = None
187
+ end_time: Optional[str] = None
188
+ submitted_by: Optional[str] = None
189
+ workflow_id: Optional[str] = None
190
+ workflow_version_id: Optional[str] = None
191
+ workflow_name: Optional[str] = None
192
+ workflow_version: Optional[str] = None
193
+ tags: Optional[dict[str, str]] = None
194
+
195
+
176
196
  # Custom validator to handle unknown event types gracefully
177
197
  def parse_event_metadata(data: Dict[str, Any]) -> Union[
178
198
  RunSubmittedMetadata,
@@ -181,6 +201,7 @@ def parse_event_metadata(data: Dict[str, Any]) -> Union[
181
201
  StateTransitionMetadata,
182
202
  EngineStatusUpdateMetadata,
183
203
  RunSubmittedToEngineMetadata,
204
+ SamplesUpdatedMetadata,
184
205
  UnknownEventMetadata
185
206
  ]:
186
207
  """Parse event metadata, falling back to UnknownEventMetadata for unknown types."""
@@ -199,6 +220,7 @@ def parse_event_metadata(data: Dict[str, Any]) -> Union[
199
220
  "STATE_TRANSITION": StateTransitionMetadata,
200
221
  "ENGINE_STATUS_UPDATE": EngineStatusUpdateMetadata,
201
222
  "RUN_SUBMITTED_TO_ENGINE": RunSubmittedToEngineMetadata,
223
+ "SAMPLES_UPDATED": SamplesUpdatedMetadata,
202
224
  }
203
225
 
204
226
  metadata_class = type_mapping.get(event_type)
@@ -220,6 +242,7 @@ class RunEvent(BaseModel):
220
242
  StateTransitionMetadata,
221
243
  EngineStatusUpdateMetadata,
222
244
  RunSubmittedToEngineMetadata,
245
+ SamplesUpdatedMetadata,
223
246
  UnknownEventMetadata
224
247
  ]
225
248
 
@@ -1,7 +1,9 @@
1
+ from typing import Optional
2
+
1
3
  from pydantic import BaseModel
2
4
 
3
5
 
4
6
  class WorkbenchUser(BaseModel):
5
7
  email: str
6
- full_name: str
8
+ full_name: Optional[str] = None
7
9
  default_namespace: str
dnastack/constants.py CHANGED
@@ -1,5 +1,5 @@
1
1
  import os
2
2
 
3
- __version__ = "3.1.211"
3
+ __version__ = "3.1.213"
4
4
 
5
5
  LOCAL_STORAGE_DIRECTORY = os.path.join(os.path.expanduser("~"), '.dnastack')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dnastack-client-library
3
- Version: 3.1.211
3
+ Version: 3.1.213
4
4
  Summary: DNAstack's GA4GH library and CLI
5
5
  Author-email: DNAstack <devs@dnastack.com>
6
6
  License: Apache License, Version 2.0
@@ -1,6 +1,6 @@
1
1
  dnastack/__init__.py,sha256=mslf7se8vBSK_HkqWTGPdibeVhT4xyKXgzQBV7dEK1M,333
2
2
  dnastack/__main__.py,sha256=EKmtIs4TBseQJi-OT_U6LqRyKLiyrGTBuTQg9zE-G2I,4376
3
- dnastack/constants.py,sha256=luSsCyuSoskdvEGWRY8zDG3Gt1ncP7S-1MdshbV0_Bs,113
3
+ dnastack/constants.py,sha256=b8sniKgFeXepJPRzikcxFUOWeDdagxeJE6sIvbmR1Sc,113
4
4
  dnastack/feature_flags.py,sha256=UMNDB07R_ep6Fx3iClhzwOpkWfyYnb418FpoJo50CGs,1411
5
5
  dnastack/json_path.py,sha256=TyghhDf7nGQmnsUWBhenU_fKsE_Ez-HLVER6HgH5-hU,2700
6
6
  dnastack/omics_cli.py,sha256=ZppKZTHv_XjUUZyRIzSkx0Ug5ODAYrCOTsU0ezCOVrA,3694
@@ -74,13 +74,15 @@ dnastack/cli/commands/workbench/instruments/__init__.py,sha256=ZuRgZxCgqYWkFQdmo
74
74
  dnastack/cli/commands/workbench/instruments/commands.py,sha256=MOC1hkZzhwTtcYRk000nUS4uaDpOkSOkx5IWb6nfaBs,2206
75
75
  dnastack/cli/commands/workbench/namespaces/__init__.py,sha256=D4b8BMxuVb7tuQ8ZY-vQracVp-wPv7RrIHhOe9DeAvA,309
76
76
  dnastack/cli/commands/workbench/namespaces/commands.py,sha256=36WHG4h6U69_32tnTWvTFRzNtRvTCbF-u0P4U5qsCoc,887
77
- dnastack/cli/commands/workbench/runs/__init__.py,sha256=8M_Hd6nikM7YrxOQXbshGCOAxZAfdI9ZPYeajMXsEhg,727
77
+ dnastack/cli/commands/workbench/runs/__init__.py,sha256=O8RlOTe3edomgPAXV5WqaOI-r-CuKD7Fmg2WESGQhRk,860
78
78
  dnastack/cli/commands/workbench/runs/commands.py,sha256=eosuaLoT-UEebK7nnZqzxrqy1pweXl04bcXVy478MK8,27599
79
79
  dnastack/cli/commands/workbench/runs/events.py,sha256=eJL8zApopJKGumZlDlFGxNbToCgnzKltanbUUeXjUw8,1244
80
80
  dnastack/cli/commands/workbench/runs/tasks.py,sha256=aYLgSAAv3XqN36gLw-YeJ4_gQ-csiFp7bF4yLEX1QMw,1719
81
81
  dnastack/cli/commands/workbench/runs/utils.py,sha256=5ROpUn9JIG5J6oHNQjDIPUHjLvKOuddYOesDL3PTT24,233
82
82
  dnastack/cli/commands/workbench/runs/hooks/__init__.py,sha256=HgaQqV_BIq0F8HaSKOafhlvNwv-8fDdlA9oOUuNffYs,296
83
83
  dnastack/cli/commands/workbench/runs/hooks/commands.py,sha256=8elGEH7Rj5sva2Lv580Q2SSpP4k0Tl81jp5xGvYcG-w,2387
84
+ dnastack/cli/commands/workbench/runs/samples/__init__.py,sha256=AQISCkkLz1u0CaVX8qzHUzJDo5YNTFMUFQoVEkcbDY8,297
85
+ dnastack/cli/commands/workbench/runs/samples/commands.py,sha256=8MyN0PS9R-dzXPxJKG-df93TXYgm_g55LykJokt8VDM,4585
84
86
  dnastack/cli/commands/workbench/samples/__init__.py,sha256=cRfeKCdHO-0Dq3PFoVcqO7OrQspajIO3h1zNqknKP2U,456
85
87
  dnastack/cli/commands/workbench/samples/commands.py,sha256=4o3MwcFib9km23weEOiz_JHGj-BDeWvMO8hItB5W3ng,9521
86
88
  dnastack/cli/commands/workbench/samples/files.py,sha256=ihZgHV04fXC8-B0EFZ463SesgI93YSbXXlEtYyaUn8U,3021
@@ -143,8 +145,8 @@ dnastack/client/workbench/models.py,sha256=d0OzcPKePDuyJnOnoScSRQqzHx7SMu_qq1t_b
143
145
  dnastack/client/workbench/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
144
146
  dnastack/client/workbench/common/models.py,sha256=cOlwpZGxA23CkjYQ7Uox2-uOA_MwqszHt10OSjXT6AQ,1070
145
147
  dnastack/client/workbench/ewes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
146
- dnastack/client/workbench/ewes/client.py,sha256=deaG6vCb2r5q7Habfp9QfCVbXQJNjl2GC5dxm_PnDwE,16524
147
- dnastack/client/workbench/ewes/models.py,sha256=BLG0Jd-8N8r_0yKqb94oZglZ3ymAaS2gdqb0kPcunoY,12729
148
+ dnastack/client/workbench/ewes/client.py,sha256=Vh_KYwlUygQO3N_TW9Ye9o5b6XWXqhC-NcOMivpEQB8,17184
149
+ dnastack/client/workbench/ewes/models.py,sha256=t9VyZ5fUikiTv1GGyll1Wt2B2dkQtNh6UsL8FCSRLyY,13494
148
150
  dnastack/client/workbench/samples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
149
151
  dnastack/client/workbench/samples/client.py,sha256=2X34SYTjV6n4yZz0q7Kaa4NPWDHRi2ut0uJWL3zXZWA,5901
150
152
  dnastack/client/workbench/samples/models.py,sha256=oExpYx32nunvO2rBUCIZQcU-dUZmLLpSTOAFdKAzk1k,3785
@@ -153,7 +155,7 @@ dnastack/client/workbench/storage/client.py,sha256=SdvY3ieUjFeBoX0AvdeNjVBx5gM5B
153
155
  dnastack/client/workbench/storage/models.py,sha256=O2Z6mlUZU7Xtp6ehXYthEh3qZNvj8DH2FDOW7WM5beA,2198
154
156
  dnastack/client/workbench/workbench_user_service/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
155
157
  dnastack/client/workbench/workbench_user_service/client.py,sha256=ZpMOFw5L3NxbC2WtKpH3OJ435zEjy0-m4p0WgzQEOB0,1219
156
- dnastack/client/workbench/workbench_user_service/models.py,sha256=P8WmocouYthi4gnHzNJT2F3iExWTt_2MUnskexN6Rxs,126
158
+ dnastack/client/workbench/workbench_user_service/models.py,sha256=WPiXK9sTzsD6OPJ7zQPRbz0ArsQx7BcAPIEd1qhSU0c,172
157
159
  dnastack/client/workbench/workflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
158
160
  dnastack/client/workbench/workflow/client.py,sha256=UCNGy9SsIOIQuP5AFgFB0WH1ZUvO0ShSaPjrcTN4aMU,22351
159
161
  dnastack/client/workbench/workflow/models.py,sha256=3xT8kbG8dDmDzc9_0XXIW8tC1U_sC2sg1N4sJ0i3iEk,6044
@@ -200,9 +202,9 @@ dnastack/http/authenticators/oauth2_adapter/device_code_flow.py,sha256=dXI5CyUcs
200
202
  dnastack/http/authenticators/oauth2_adapter/factory.py,sha256=ZtNXOklWEim-26ooNoPp3ji_hRg1vf4fHHnY94F0wLI,1087
201
203
  dnastack/http/authenticators/oauth2_adapter/models.py,sha256=7UsC8DkStMBx7Bz_xHQNi_J1UO_H4nfHme7oBhokj2I,1339
202
204
  dnastack/http/authenticators/oauth2_adapter/token_exchange.py,sha256=nSuAsSKWa_UNqHSbPMOEk4komaFITYAnE04Sk5WOrLc,6332
203
- dnastack_client_library-3.1.211.dist-info/licenses/LICENSE,sha256=uwybO-wUbQhxkosgjhJlxmYATMy-AzoULFO9FUedE34,11580
204
- dnastack_client_library-3.1.211.dist-info/METADATA,sha256=igkuH5IVMlkRQQRU6hhRnj68fD1-xxxut3S-Ljp0p1o,1791
205
- dnastack_client_library-3.1.211.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
206
- dnastack_client_library-3.1.211.dist-info/entry_points.txt,sha256=Y6OeicsiyGn3-8D-SiV4NiKlJgXfkSqK88kFBR6R1rY,89
207
- dnastack_client_library-3.1.211.dist-info/top_level.txt,sha256=P2RgRyqJ7hfNy1wLVRoVLJYEppUVkCX3syGK9zBqkt8,9
208
- dnastack_client_library-3.1.211.dist-info/RECORD,,
205
+ dnastack_client_library-3.1.213.dist-info/licenses/LICENSE,sha256=uwybO-wUbQhxkosgjhJlxmYATMy-AzoULFO9FUedE34,11580
206
+ dnastack_client_library-3.1.213.dist-info/METADATA,sha256=ubFSa3FAuDwsq30tgJXLajs1WHYwAqqmE0cTzgyIxIE,1791
207
+ dnastack_client_library-3.1.213.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
208
+ dnastack_client_library-3.1.213.dist-info/entry_points.txt,sha256=Y6OeicsiyGn3-8D-SiV4NiKlJgXfkSqK88kFBR6R1rY,89
209
+ dnastack_client_library-3.1.213.dist-info/top_level.txt,sha256=P2RgRyqJ7hfNy1wLVRoVLJYEppUVkCX3syGK9zBqkt8,9
210
+ dnastack_client_library-3.1.213.dist-info/RECORD,,