fractal-server 2.11.0a10__py3-none-any.whl → 2.12.0a0__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.
- fractal_server/__init__.py +1 -1
- fractal_server/app/models/__init__.py +0 -2
- fractal_server/app/models/linkuserproject.py +0 -9
- fractal_server/app/models/v2/dataset.py +0 -4
- fractal_server/app/models/v2/workflowtask.py +0 -4
- fractal_server/app/routes/aux/_job.py +1 -3
- fractal_server/app/runner/filenames.py +0 -2
- fractal_server/app/runner/shutdown.py +3 -27
- fractal_server/config.py +1 -15
- fractal_server/main.py +1 -12
- fractal_server/migrations/versions/1eac13a26c83_drop_v1_tables.py +67 -0
- fractal_server/migrations/versions/af8673379a5c_drop_old_filter_columns.py +54 -0
- fractal_server/string_tools.py +0 -21
- fractal_server/tasks/utils.py +0 -24
- {fractal_server-2.11.0a10.dist-info → fractal_server-2.12.0a0.dist-info}/METADATA +1 -1
- {fractal_server-2.11.0a10.dist-info → fractal_server-2.12.0a0.dist-info}/RECORD +19 -63
- fractal_server/app/models/v1/__init__.py +0 -13
- fractal_server/app/models/v1/dataset.py +0 -71
- fractal_server/app/models/v1/job.py +0 -101
- fractal_server/app/models/v1/project.py +0 -29
- fractal_server/app/models/v1/state.py +0 -34
- fractal_server/app/models/v1/task.py +0 -85
- fractal_server/app/models/v1/workflow.py +0 -133
- fractal_server/app/routes/admin/v1.py +0 -377
- fractal_server/app/routes/api/v1/__init__.py +0 -26
- fractal_server/app/routes/api/v1/_aux_functions.py +0 -478
- fractal_server/app/routes/api/v1/dataset.py +0 -554
- fractal_server/app/routes/api/v1/job.py +0 -195
- fractal_server/app/routes/api/v1/project.py +0 -475
- fractal_server/app/routes/api/v1/task.py +0 -203
- fractal_server/app/routes/api/v1/task_collection.py +0 -239
- fractal_server/app/routes/api/v1/workflow.py +0 -355
- fractal_server/app/routes/api/v1/workflowtask.py +0 -187
- fractal_server/app/runner/async_wrap_v1.py +0 -27
- fractal_server/app/runner/v1/__init__.py +0 -415
- fractal_server/app/runner/v1/_common.py +0 -620
- fractal_server/app/runner/v1/_local/__init__.py +0 -186
- fractal_server/app/runner/v1/_local/_local_config.py +0 -105
- fractal_server/app/runner/v1/_local/_submit_setup.py +0 -48
- fractal_server/app/runner/v1/_local/executor.py +0 -100
- fractal_server/app/runner/v1/_slurm/__init__.py +0 -312
- fractal_server/app/runner/v1/_slurm/_submit_setup.py +0 -81
- fractal_server/app/runner/v1/_slurm/get_slurm_config.py +0 -163
- fractal_server/app/runner/v1/common.py +0 -117
- fractal_server/app/runner/v1/handle_failed_job.py +0 -141
- fractal_server/app/schemas/v1/__init__.py +0 -37
- fractal_server/app/schemas/v1/applyworkflow.py +0 -161
- fractal_server/app/schemas/v1/dataset.py +0 -165
- fractal_server/app/schemas/v1/dumps.py +0 -64
- fractal_server/app/schemas/v1/manifest.py +0 -126
- fractal_server/app/schemas/v1/project.py +0 -66
- fractal_server/app/schemas/v1/state.py +0 -18
- fractal_server/app/schemas/v1/task.py +0 -167
- fractal_server/app/schemas/v1/task_collection.py +0 -110
- fractal_server/app/schemas/v1/workflow.py +0 -212
- fractal_server/data_migrations/2_11_0.py +0 -168
- fractal_server/tasks/v1/_TaskCollectPip.py +0 -103
- fractal_server/tasks/v1/__init__.py +0 -0
- fractal_server/tasks/v1/background_operations.py +0 -352
- fractal_server/tasks/v1/endpoint_operations.py +0 -156
- fractal_server/tasks/v1/get_collection_data.py +0 -14
- fractal_server/tasks/v1/utils.py +0 -67
- {fractal_server-2.11.0a10.dist-info → fractal_server-2.12.0a0.dist-info}/LICENSE +0 -0
- {fractal_server-2.11.0a10.dist-info → fractal_server-2.12.0a0.dist-info}/WHEEL +0 -0
- {fractal_server-2.11.0a10.dist-info → fractal_server-2.12.0a0.dist-info}/entry_points.txt +0 -0
@@ -1,478 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Auxiliary functions to get object from the database or perform simple checks
|
3
|
-
"""
|
4
|
-
from typing import Any
|
5
|
-
from typing import Literal
|
6
|
-
from typing import Optional
|
7
|
-
from typing import Union
|
8
|
-
|
9
|
-
from fastapi import HTTPException
|
10
|
-
from fastapi import status
|
11
|
-
from sqlmodel import select
|
12
|
-
from sqlmodel.sql.expression import SelectOfScalar
|
13
|
-
|
14
|
-
from .....config import get_settings
|
15
|
-
from .....syringe import Inject
|
16
|
-
from ....db import AsyncSession
|
17
|
-
from ....models.v1 import ApplyWorkflow
|
18
|
-
from ....models.v1 import Dataset
|
19
|
-
from ....models.v1 import LinkUserProject
|
20
|
-
from ....models.v1 import Project
|
21
|
-
from ....models.v1 import Task
|
22
|
-
from ....models.v1 import Workflow
|
23
|
-
from ....models.v1 import WorkflowTask
|
24
|
-
from ....schemas.v1 import JobStatusTypeV1
|
25
|
-
from ...aux.validate_user_settings import verify_user_has_settings
|
26
|
-
from fractal_server.app.models import UserOAuth
|
27
|
-
|
28
|
-
|
29
|
-
def _raise_if_v1_is_read_only() -> None:
|
30
|
-
settings = Inject(get_settings)
|
31
|
-
if settings.FRACTAL_API_V1_MODE == "include_read_only":
|
32
|
-
raise HTTPException(
|
33
|
-
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
34
|
-
detail="Legacy API is in read-only mode.",
|
35
|
-
)
|
36
|
-
|
37
|
-
|
38
|
-
async def _get_project_check_owner(
|
39
|
-
*,
|
40
|
-
project_id: int,
|
41
|
-
user_id: int,
|
42
|
-
db: AsyncSession,
|
43
|
-
) -> Project:
|
44
|
-
"""
|
45
|
-
Check that user is a member of project and return the project.
|
46
|
-
|
47
|
-
Args:
|
48
|
-
project_id:
|
49
|
-
user_id:
|
50
|
-
db:
|
51
|
-
|
52
|
-
Returns:
|
53
|
-
The project object
|
54
|
-
|
55
|
-
Raises:
|
56
|
-
HTTPException(status_code=403_FORBIDDEN):
|
57
|
-
If the user is not a member of the project
|
58
|
-
HTTPException(status_code=404_NOT_FOUND):
|
59
|
-
If the project does not exist
|
60
|
-
"""
|
61
|
-
project = await db.get(Project, project_id)
|
62
|
-
link_user_project = await db.get(LinkUserProject, (project_id, user_id))
|
63
|
-
if not project:
|
64
|
-
raise HTTPException(
|
65
|
-
status_code=status.HTTP_404_NOT_FOUND, detail="Project not found"
|
66
|
-
)
|
67
|
-
if not link_user_project:
|
68
|
-
raise HTTPException(
|
69
|
-
status_code=status.HTTP_403_FORBIDDEN,
|
70
|
-
detail=f"Not allowed on project {project_id}",
|
71
|
-
)
|
72
|
-
return project
|
73
|
-
|
74
|
-
|
75
|
-
async def _get_workflow_check_owner(
|
76
|
-
*,
|
77
|
-
workflow_id: int,
|
78
|
-
project_id: int,
|
79
|
-
user_id: int,
|
80
|
-
db: AsyncSession,
|
81
|
-
) -> Workflow:
|
82
|
-
"""
|
83
|
-
Get a workflow and a project, after access control on the project.
|
84
|
-
|
85
|
-
Args:
|
86
|
-
workflow_id:
|
87
|
-
project_id:
|
88
|
-
user_id:
|
89
|
-
db:
|
90
|
-
|
91
|
-
Returns:
|
92
|
-
The workflow object.
|
93
|
-
|
94
|
-
Raises:
|
95
|
-
HTTPException(status_code=404_NOT_FOUND):
|
96
|
-
If the workflow does not exist
|
97
|
-
HTTPException(status_code=422_UNPROCESSABLE_ENTITY):
|
98
|
-
If the workflow is not associated to the project
|
99
|
-
"""
|
100
|
-
|
101
|
-
# Access control for project
|
102
|
-
project = await _get_project_check_owner(
|
103
|
-
project_id=project_id, user_id=user_id, db=db
|
104
|
-
)
|
105
|
-
# Get workflow
|
106
|
-
workflow = await db.get(Workflow, workflow_id)
|
107
|
-
if not workflow:
|
108
|
-
raise HTTPException(
|
109
|
-
status_code=status.HTTP_404_NOT_FOUND, detail="Workflow not found"
|
110
|
-
)
|
111
|
-
if workflow.project_id != project.id:
|
112
|
-
raise HTTPException(
|
113
|
-
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
114
|
-
detail=(f"Invalid {project_id=} for {workflow_id=}."),
|
115
|
-
)
|
116
|
-
|
117
|
-
# Refresh so that workflow.project relationship is loaded (see discussion
|
118
|
-
# in issue #1063)
|
119
|
-
await db.refresh(workflow)
|
120
|
-
|
121
|
-
return workflow
|
122
|
-
|
123
|
-
|
124
|
-
async def _get_workflow_task_check_owner(
|
125
|
-
*,
|
126
|
-
project_id: int,
|
127
|
-
workflow_id: int,
|
128
|
-
workflow_task_id: int,
|
129
|
-
user_id: int,
|
130
|
-
db: AsyncSession,
|
131
|
-
) -> tuple[WorkflowTask, Workflow]:
|
132
|
-
"""
|
133
|
-
Check that user has access to Workflow and WorkflowTask.
|
134
|
-
|
135
|
-
Args:
|
136
|
-
project_id:
|
137
|
-
workflow_id:
|
138
|
-
workflow_task_id:
|
139
|
-
user_id:
|
140
|
-
db:
|
141
|
-
|
142
|
-
Returns:
|
143
|
-
Tuple of WorkflowTask and Workflow objects.
|
144
|
-
|
145
|
-
Raises:
|
146
|
-
HTTPException(status_code=404_NOT_FOUND):
|
147
|
-
If the WorkflowTask does not exist
|
148
|
-
HTTPException(status_code=422_UNPROCESSABLE_ENTITY):
|
149
|
-
If the WorkflowTask is not associated to the Workflow
|
150
|
-
"""
|
151
|
-
|
152
|
-
# Access control for workflow
|
153
|
-
workflow = await _get_workflow_check_owner(
|
154
|
-
workflow_id=workflow_id, project_id=project_id, user_id=user_id, db=db
|
155
|
-
)
|
156
|
-
|
157
|
-
# If WorkflowTask is not in the db, exit
|
158
|
-
workflow_task = await db.get(WorkflowTask, workflow_task_id)
|
159
|
-
if not workflow_task:
|
160
|
-
raise HTTPException(
|
161
|
-
status_code=status.HTTP_404_NOT_FOUND,
|
162
|
-
detail="WorkflowTask not found",
|
163
|
-
)
|
164
|
-
|
165
|
-
# If WorkflowTask is not part of the expected Workflow, exit
|
166
|
-
if workflow_id != workflow_task.workflow_id:
|
167
|
-
raise HTTPException(
|
168
|
-
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
169
|
-
detail=f"Invalid {workflow_id=} for {workflow_task_id=}",
|
170
|
-
)
|
171
|
-
|
172
|
-
return workflow_task, workflow
|
173
|
-
|
174
|
-
|
175
|
-
async def _check_workflow_exists(
|
176
|
-
*,
|
177
|
-
name: str,
|
178
|
-
project_id: int,
|
179
|
-
db: AsyncSession,
|
180
|
-
) -> None:
|
181
|
-
"""
|
182
|
-
Check that no other workflow of this project has the same name.
|
183
|
-
|
184
|
-
Args:
|
185
|
-
name: Workflow name
|
186
|
-
project_id: Project ID
|
187
|
-
db:
|
188
|
-
|
189
|
-
Raises:
|
190
|
-
HTTPException(status_code=422_UNPROCESSABLE_ENTITY):
|
191
|
-
If such a workflow already exists
|
192
|
-
"""
|
193
|
-
stm = (
|
194
|
-
select(Workflow)
|
195
|
-
.where(Workflow.name == name)
|
196
|
-
.where(Workflow.project_id == project_id)
|
197
|
-
)
|
198
|
-
res = await db.execute(stm)
|
199
|
-
if res.scalars().all():
|
200
|
-
raise HTTPException(
|
201
|
-
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
202
|
-
detail=f"Workflow with {name=} and {project_id=} already exists.",
|
203
|
-
)
|
204
|
-
|
205
|
-
|
206
|
-
async def _check_project_exists(
|
207
|
-
*,
|
208
|
-
project_name: str,
|
209
|
-
user_id: int,
|
210
|
-
db: AsyncSession,
|
211
|
-
) -> None:
|
212
|
-
"""
|
213
|
-
Check that no other project with this name exists for this user.
|
214
|
-
|
215
|
-
Args:
|
216
|
-
project_name: Project name
|
217
|
-
user_id: User ID
|
218
|
-
db:
|
219
|
-
|
220
|
-
Raises:
|
221
|
-
HTTPException(status_code=422_UNPROCESSABLE_ENTITY):
|
222
|
-
If such a project already exists
|
223
|
-
"""
|
224
|
-
stm = (
|
225
|
-
select(Project)
|
226
|
-
.join(LinkUserProject)
|
227
|
-
.where(Project.name == project_name)
|
228
|
-
.where(LinkUserProject.user_id == user_id)
|
229
|
-
)
|
230
|
-
res = await db.execute(stm)
|
231
|
-
if res.scalars().all():
|
232
|
-
raise HTTPException(
|
233
|
-
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
234
|
-
detail=f"Project name ({project_name}) already in use",
|
235
|
-
)
|
236
|
-
|
237
|
-
|
238
|
-
async def _get_dataset_check_owner(
|
239
|
-
*,
|
240
|
-
project_id: int,
|
241
|
-
dataset_id: int,
|
242
|
-
user_id: int,
|
243
|
-
db: AsyncSession,
|
244
|
-
) -> dict[Literal["dataset", "project"], Union[Dataset, Project]]:
|
245
|
-
"""
|
246
|
-
Get a dataset and a project, after access control on the project
|
247
|
-
|
248
|
-
Args:
|
249
|
-
project_id:
|
250
|
-
dataset_id:
|
251
|
-
user_id:
|
252
|
-
db:
|
253
|
-
|
254
|
-
Returns:
|
255
|
-
Dictionary with the dataset and project objects (keys: `dataset`,
|
256
|
-
`project`).
|
257
|
-
|
258
|
-
Raises:
|
259
|
-
HTTPException(status_code=422_UNPROCESSABLE_ENTITY):
|
260
|
-
If the dataset is not associated to the project
|
261
|
-
"""
|
262
|
-
|
263
|
-
# Access control for project
|
264
|
-
project = await _get_project_check_owner(
|
265
|
-
project_id=project_id, user_id=user_id, db=db
|
266
|
-
)
|
267
|
-
# Get dataset
|
268
|
-
dataset = await db.get(Dataset, dataset_id)
|
269
|
-
if not dataset:
|
270
|
-
raise HTTPException(
|
271
|
-
status_code=status.HTTP_404_NOT_FOUND, detail="Dataset not found"
|
272
|
-
)
|
273
|
-
if dataset.project_id != project_id:
|
274
|
-
raise HTTPException(
|
275
|
-
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
276
|
-
detail=f"Invalid {project_id=} for {dataset_id=}",
|
277
|
-
)
|
278
|
-
|
279
|
-
# Refresh so that dataset.project relationship is loaded (see discussion
|
280
|
-
# in issue #1063)
|
281
|
-
await db.refresh(dataset)
|
282
|
-
|
283
|
-
return dict(dataset=dataset, project=project)
|
284
|
-
|
285
|
-
|
286
|
-
async def _get_job_check_owner(
|
287
|
-
*,
|
288
|
-
project_id: int,
|
289
|
-
job_id: int,
|
290
|
-
user_id: int,
|
291
|
-
db: AsyncSession,
|
292
|
-
) -> dict[Literal["job", "project"], Union[ApplyWorkflow, Project]]:
|
293
|
-
"""
|
294
|
-
Get a job and a project, after access control on the project
|
295
|
-
|
296
|
-
Args:
|
297
|
-
project_id:
|
298
|
-
job_id:
|
299
|
-
user_id:
|
300
|
-
db:
|
301
|
-
|
302
|
-
Returns:
|
303
|
-
Dictionary with the job and project objects (keys: `job`,
|
304
|
-
`project`).
|
305
|
-
|
306
|
-
Raises:
|
307
|
-
HTTPException(status_code=422_UNPROCESSABLE_ENTITY):
|
308
|
-
If the job is not associated to the project
|
309
|
-
"""
|
310
|
-
# Access control for project
|
311
|
-
project = await _get_project_check_owner(
|
312
|
-
project_id=project_id, user_id=user_id, db=db
|
313
|
-
)
|
314
|
-
# Get dataset
|
315
|
-
job = await db.get(ApplyWorkflow, job_id)
|
316
|
-
if not job:
|
317
|
-
raise HTTPException(
|
318
|
-
status_code=status.HTTP_404_NOT_FOUND, detail="Job not found"
|
319
|
-
)
|
320
|
-
if job.project_id != project_id:
|
321
|
-
raise HTTPException(
|
322
|
-
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
323
|
-
detail=f"Invalid {project_id=} for {job_id=}",
|
324
|
-
)
|
325
|
-
return dict(job=job, project=project)
|
326
|
-
|
327
|
-
|
328
|
-
async def _get_task_check_owner(
|
329
|
-
*,
|
330
|
-
task_id: int,
|
331
|
-
user: UserOAuth,
|
332
|
-
db: AsyncSession,
|
333
|
-
) -> Task:
|
334
|
-
"""
|
335
|
-
Get a task, after access control.
|
336
|
-
|
337
|
-
This check constitutes a preliminary version of access control:
|
338
|
-
if the current user is not a superuser and differs from the task owner
|
339
|
-
(including when `owner is None`), we raise an 403 HTTP Exception.
|
340
|
-
|
341
|
-
Args:
|
342
|
-
task_id:
|
343
|
-
user:
|
344
|
-
db:
|
345
|
-
|
346
|
-
Returns:
|
347
|
-
The task object.
|
348
|
-
|
349
|
-
Raises:
|
350
|
-
HTTPException(status_code=404_NOT_FOUND):
|
351
|
-
If the task does not exist
|
352
|
-
HTTPException(status_code=403_FORBIDDEN):
|
353
|
-
If the user does not have rights to edit this task.
|
354
|
-
"""
|
355
|
-
task = await db.get(Task, task_id)
|
356
|
-
if not task:
|
357
|
-
raise HTTPException(
|
358
|
-
status_code=status.HTTP_404_NOT_FOUND,
|
359
|
-
detail=f"Task {task_id} not found.",
|
360
|
-
)
|
361
|
-
|
362
|
-
if not user.is_superuser:
|
363
|
-
if task.owner is None:
|
364
|
-
raise HTTPException(
|
365
|
-
status_code=status.HTTP_403_FORBIDDEN,
|
366
|
-
detail=(
|
367
|
-
"Only a superuser can modify a Task with `owner=None`."
|
368
|
-
),
|
369
|
-
)
|
370
|
-
else:
|
371
|
-
if user.username:
|
372
|
-
owner = user.username
|
373
|
-
else:
|
374
|
-
verify_user_has_settings(user)
|
375
|
-
owner = user.settings.slurm_user
|
376
|
-
if owner != task.owner:
|
377
|
-
raise HTTPException(
|
378
|
-
status_code=status.HTTP_403_FORBIDDEN,
|
379
|
-
detail=(
|
380
|
-
f"Current user ({owner}) cannot modify Task {task.id} "
|
381
|
-
f"with different owner ({task.owner})."
|
382
|
-
),
|
383
|
-
)
|
384
|
-
return task
|
385
|
-
|
386
|
-
|
387
|
-
def _get_submitted_jobs_statement() -> SelectOfScalar:
|
388
|
-
"""
|
389
|
-
Returns:
|
390
|
-
A sqlmodel statement that selects all `ApplyWorkflow`s with
|
391
|
-
`ApplyWorkflow.status` equal to `submitted`.
|
392
|
-
"""
|
393
|
-
stm = select(ApplyWorkflow).where(
|
394
|
-
ApplyWorkflow.status == JobStatusTypeV1.SUBMITTED
|
395
|
-
)
|
396
|
-
return stm
|
397
|
-
|
398
|
-
|
399
|
-
async def _workflow_insert_task(
|
400
|
-
*,
|
401
|
-
workflow_id: int,
|
402
|
-
task_id: int,
|
403
|
-
args: Optional[dict[str, Any]] = None,
|
404
|
-
meta: Optional[dict[str, Any]] = None,
|
405
|
-
order: Optional[int] = None,
|
406
|
-
db: AsyncSession,
|
407
|
-
) -> WorkflowTask:
|
408
|
-
"""
|
409
|
-
Insert a new WorkflowTask into Workflow.task_list
|
410
|
-
|
411
|
-
Args:
|
412
|
-
task_id: TBD
|
413
|
-
args: TBD
|
414
|
-
meta: TBD
|
415
|
-
order: TBD
|
416
|
-
db: TBD
|
417
|
-
"""
|
418
|
-
db_workflow = await db.get(Workflow, workflow_id)
|
419
|
-
if db_workflow is None:
|
420
|
-
raise ValueError(f"Workflow {workflow_id} does not exist")
|
421
|
-
|
422
|
-
if order is None:
|
423
|
-
order = len(db_workflow.task_list)
|
424
|
-
|
425
|
-
# Get task from db, and extract default arguments via a Task property
|
426
|
-
# method
|
427
|
-
db_task = await db.get(Task, task_id)
|
428
|
-
if db_task is None:
|
429
|
-
raise ValueError(f"Task {task_id} does not exist")
|
430
|
-
|
431
|
-
default_args = db_task.default_args_from_args_schema
|
432
|
-
# Override default_args with args
|
433
|
-
actual_args = default_args.copy()
|
434
|
-
if args is not None:
|
435
|
-
for k, v in args.items():
|
436
|
-
actual_args[k] = v
|
437
|
-
if not actual_args:
|
438
|
-
actual_args = None
|
439
|
-
|
440
|
-
# Combine meta (higher priority) and db_task.meta (lower priority)
|
441
|
-
wt_meta = (db_task.meta or {}).copy()
|
442
|
-
wt_meta.update(meta or {})
|
443
|
-
if not wt_meta:
|
444
|
-
wt_meta = None
|
445
|
-
|
446
|
-
# Create DB entry
|
447
|
-
wf_task = WorkflowTask(task_id=task_id, args=actual_args, meta=wt_meta)
|
448
|
-
db.add(wf_task)
|
449
|
-
db_workflow.task_list.insert(order, wf_task)
|
450
|
-
db_workflow.task_list.reorder() # type: ignore
|
451
|
-
await db.commit()
|
452
|
-
await db.refresh(wf_task)
|
453
|
-
|
454
|
-
return wf_task
|
455
|
-
|
456
|
-
|
457
|
-
async def clean_app_job_list_v1(
|
458
|
-
db: AsyncSession, jobs_list: list[int]
|
459
|
-
) -> list[int]:
|
460
|
-
"""
|
461
|
-
Remove from a job list all jobs with status different from submitted.
|
462
|
-
|
463
|
-
Args:
|
464
|
-
db: Async database session
|
465
|
-
jobs_list: List of job IDs currently associated to the app.
|
466
|
-
|
467
|
-
Return:
|
468
|
-
List of IDs for submitted jobs.
|
469
|
-
"""
|
470
|
-
stmt = select(ApplyWorkflow).where(ApplyWorkflow.id.in_(jobs_list))
|
471
|
-
result = await db.execute(stmt)
|
472
|
-
db_jobs_list = result.scalars().all()
|
473
|
-
submitted_job_ids = [
|
474
|
-
job.id
|
475
|
-
for job in db_jobs_list
|
476
|
-
if job.status == JobStatusTypeV1.SUBMITTED
|
477
|
-
]
|
478
|
-
return submitted_job_ids
|