fractal-server 1.4.9__py3-none-any.whl → 2.0.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 +4 -7
- fractal_server/app/models/linkuserproject.py +9 -0
- fractal_server/app/models/security.py +6 -0
- fractal_server/app/models/state.py +1 -1
- fractal_server/app/models/v1/__init__.py +10 -0
- fractal_server/app/models/{dataset.py → v1/dataset.py} +5 -5
- fractal_server/app/models/{job.py → v1/job.py} +5 -5
- fractal_server/app/models/{project.py → v1/project.py} +5 -5
- fractal_server/app/models/{task.py → v1/task.py} +7 -2
- fractal_server/app/models/{workflow.py → v1/workflow.py} +5 -5
- fractal_server/app/models/v2/__init__.py +20 -0
- fractal_server/app/models/v2/dataset.py +55 -0
- fractal_server/app/models/v2/job.py +51 -0
- fractal_server/app/models/v2/project.py +31 -0
- fractal_server/app/models/v2/task.py +93 -0
- fractal_server/app/models/v2/workflow.py +43 -0
- fractal_server/app/models/v2/workflowtask.py +90 -0
- fractal_server/app/routes/{admin.py → admin/v1.py} +42 -42
- fractal_server/app/routes/admin/v2.py +275 -0
- fractal_server/app/routes/api/v1/__init__.py +7 -7
- fractal_server/app/routes/api/v1/_aux_functions.py +2 -2
- fractal_server/app/routes/api/v1/dataset.py +44 -37
- fractal_server/app/routes/api/v1/job.py +12 -12
- fractal_server/app/routes/api/v1/project.py +23 -21
- fractal_server/app/routes/api/v1/task.py +24 -14
- fractal_server/app/routes/api/v1/task_collection.py +16 -14
- fractal_server/app/routes/api/v1/workflow.py +24 -24
- fractal_server/app/routes/api/v1/workflowtask.py +10 -10
- fractal_server/app/routes/api/v2/__init__.py +28 -0
- fractal_server/app/routes/api/v2/_aux_functions.py +497 -0
- fractal_server/app/routes/api/v2/apply.py +220 -0
- fractal_server/app/routes/api/v2/dataset.py +310 -0
- fractal_server/app/routes/api/v2/images.py +212 -0
- fractal_server/app/routes/api/v2/job.py +200 -0
- fractal_server/app/routes/api/v2/project.py +205 -0
- fractal_server/app/routes/api/v2/task.py +222 -0
- fractal_server/app/routes/api/v2/task_collection.py +229 -0
- fractal_server/app/routes/api/v2/workflow.py +398 -0
- fractal_server/app/routes/api/v2/workflowtask.py +269 -0
- fractal_server/app/routes/aux/_job.py +1 -1
- fractal_server/app/runner/async_wrap.py +27 -0
- fractal_server/app/runner/exceptions.py +129 -0
- fractal_server/app/runner/executors/local/__init__.py +3 -0
- fractal_server/app/runner/{_local → executors/local}/executor.py +2 -2
- fractal_server/app/runner/executors/slurm/__init__.py +3 -0
- fractal_server/app/runner/{_slurm → executors/slurm}/_batching.py +1 -1
- fractal_server/app/runner/executors/slurm/_check_jobs_status.py +72 -0
- fractal_server/app/runner/{_slurm → executors/slurm}/_executor_wait_thread.py +3 -4
- fractal_server/app/runner/{_slurm → executors/slurm}/_slurm_config.py +3 -152
- fractal_server/app/runner/{_slurm → executors/slurm}/_subprocess_run_as_user.py +1 -1
- fractal_server/app/runner/{_slurm → executors/slurm}/executor.py +9 -9
- fractal_server/app/runner/filenames.py +6 -0
- fractal_server/app/runner/set_start_and_last_task_index.py +39 -0
- fractal_server/app/runner/task_files.py +105 -0
- fractal_server/app/runner/{__init__.py → v1/__init__.py} +36 -49
- fractal_server/app/runner/{_common.py → v1/_common.py} +13 -120
- fractal_server/app/runner/{_local → v1/_local}/__init__.py +6 -6
- fractal_server/app/runner/{_local → v1/_local}/_local_config.py +6 -7
- fractal_server/app/runner/{_local → v1/_local}/_submit_setup.py +1 -5
- fractal_server/app/runner/v1/_slurm/__init__.py +310 -0
- fractal_server/app/runner/{_slurm → v1/_slurm}/_submit_setup.py +3 -9
- fractal_server/app/runner/v1/_slurm/get_slurm_config.py +163 -0
- fractal_server/app/runner/v1/common.py +117 -0
- fractal_server/app/runner/{handle_failed_job.py → v1/handle_failed_job.py} +8 -8
- fractal_server/app/runner/v2/__init__.py +337 -0
- fractal_server/app/runner/v2/_local/__init__.py +169 -0
- fractal_server/app/runner/v2/_local/_local_config.py +118 -0
- fractal_server/app/runner/v2/_local/_submit_setup.py +52 -0
- fractal_server/app/runner/v2/_slurm/__init__.py +157 -0
- fractal_server/app/runner/v2/_slurm/_submit_setup.py +83 -0
- fractal_server/app/runner/v2/_slurm/get_slurm_config.py +179 -0
- fractal_server/app/runner/v2/components.py +5 -0
- fractal_server/app/runner/v2/deduplicate_list.py +24 -0
- fractal_server/app/runner/v2/handle_failed_job.py +156 -0
- fractal_server/app/runner/v2/merge_outputs.py +41 -0
- fractal_server/app/runner/v2/runner.py +264 -0
- fractal_server/app/runner/v2/runner_functions.py +339 -0
- fractal_server/app/runner/v2/runner_functions_low_level.py +134 -0
- fractal_server/app/runner/v2/task_interface.py +43 -0
- fractal_server/app/runner/v2/v1_compat.py +21 -0
- fractal_server/app/schemas/__init__.py +4 -42
- fractal_server/app/schemas/v1/__init__.py +42 -0
- fractal_server/app/schemas/{applyworkflow.py → v1/applyworkflow.py} +18 -18
- fractal_server/app/schemas/{dataset.py → v1/dataset.py} +30 -30
- fractal_server/app/schemas/{dumps.py → v1/dumps.py} +8 -8
- fractal_server/app/schemas/{manifest.py → v1/manifest.py} +5 -5
- fractal_server/app/schemas/{project.py → v1/project.py} +9 -9
- fractal_server/app/schemas/{task.py → v1/task.py} +12 -12
- fractal_server/app/schemas/{task_collection.py → v1/task_collection.py} +7 -7
- fractal_server/app/schemas/{workflow.py → v1/workflow.py} +38 -38
- fractal_server/app/schemas/v2/__init__.py +34 -0
- fractal_server/app/schemas/v2/dataset.py +88 -0
- fractal_server/app/schemas/v2/dumps.py +87 -0
- fractal_server/app/schemas/v2/job.py +113 -0
- fractal_server/app/schemas/v2/manifest.py +109 -0
- fractal_server/app/schemas/v2/project.py +36 -0
- fractal_server/app/schemas/v2/task.py +121 -0
- fractal_server/app/schemas/v2/task_collection.py +105 -0
- fractal_server/app/schemas/v2/workflow.py +78 -0
- fractal_server/app/schemas/v2/workflowtask.py +118 -0
- fractal_server/config.py +5 -10
- fractal_server/images/__init__.py +50 -0
- fractal_server/images/tools.py +86 -0
- fractal_server/main.py +11 -3
- fractal_server/migrations/versions/4b35c5cefbe3_tmp_is_v2_compatible.py +39 -0
- fractal_server/migrations/versions/56af171b0159_v2.py +217 -0
- fractal_server/migrations/versions/876f28db9d4e_tmp_split_task_and_wftask_meta.py +68 -0
- fractal_server/migrations/versions/974c802f0dd0_tmp_workflowtaskv2_type_in_db.py +37 -0
- fractal_server/migrations/versions/9cd305cd6023_tmp_workflowtaskv2.py +40 -0
- fractal_server/migrations/versions/a6231ed6273c_tmp_args_schemas_in_taskv2.py +42 -0
- fractal_server/migrations/versions/b9e9eed9d442_tmp_taskv2_type.py +37 -0
- fractal_server/migrations/versions/e3e639454d4b_tmp_make_task_meta_non_optional.py +50 -0
- fractal_server/tasks/__init__.py +0 -5
- fractal_server/tasks/endpoint_operations.py +13 -19
- fractal_server/tasks/utils.py +35 -0
- fractal_server/tasks/{_TaskCollectPip.py → v1/_TaskCollectPip.py} +3 -3
- fractal_server/tasks/{background_operations.py → v1/background_operations.py} +18 -50
- fractal_server/tasks/v1/get_collection_data.py +14 -0
- fractal_server/tasks/v2/_TaskCollectPip.py +103 -0
- fractal_server/tasks/v2/background_operations.py +382 -0
- fractal_server/tasks/v2/get_collection_data.py +14 -0
- {fractal_server-1.4.9.dist-info → fractal_server-2.0.0a0.dist-info}/METADATA +3 -4
- fractal_server-2.0.0a0.dist-info/RECORD +166 -0
- fractal_server/app/runner/_slurm/.gitignore +0 -2
- fractal_server/app/runner/_slurm/__init__.py +0 -150
- fractal_server/app/runner/common.py +0 -311
- fractal_server-1.4.9.dist-info/RECORD +0 -97
- /fractal_server/app/runner/{_slurm → executors/slurm}/remote.py +0 -0
- {fractal_server-1.4.9.dist-info → fractal_server-2.0.0a0.dist-info}/LICENSE +0 -0
- {fractal_server-1.4.9.dist-info → fractal_server-2.0.0a0.dist-info}/WHEEL +0 -0
- {fractal_server-1.4.9.dist-info → fractal_server-2.0.0a0.dist-info}/entry_points.txt +0 -0
@@ -1,4 +1,5 @@
|
|
1
1
|
import json
|
2
|
+
from json.decoder import JSONDecodeError
|
2
3
|
from pathlib import Path
|
3
4
|
from typing import Optional
|
4
5
|
|
@@ -16,16 +17,16 @@ from ....models import ApplyWorkflow
|
|
16
17
|
from ....models import Dataset
|
17
18
|
from ....models import Project
|
18
19
|
from ....models import Resource
|
19
|
-
from ....runner.
|
20
|
-
from ....schemas import
|
21
|
-
from ....schemas import
|
22
|
-
from ....schemas import
|
23
|
-
from ....schemas import
|
24
|
-
from ....schemas import
|
25
|
-
from ....schemas import
|
26
|
-
from ....schemas import
|
27
|
-
from ....schemas import
|
28
|
-
from ....schemas import
|
20
|
+
from ....runner.filenames import HISTORY_FILENAME
|
21
|
+
from ....schemas.v1 import DatasetCreateV1
|
22
|
+
from ....schemas.v1 import DatasetReadV1
|
23
|
+
from ....schemas.v1 import DatasetStatusReadV1
|
24
|
+
from ....schemas.v1 import DatasetUpdateV1
|
25
|
+
from ....schemas.v1 import ResourceCreateV1
|
26
|
+
from ....schemas.v1 import ResourceReadV1
|
27
|
+
from ....schemas.v1 import ResourceUpdateV1
|
28
|
+
from ....schemas.v1 import WorkflowExportV1
|
29
|
+
from ....schemas.v1 import WorkflowTaskExportV1
|
29
30
|
from ....security import current_active_user
|
30
31
|
from ....security import User
|
31
32
|
from ._aux_functions import _get_dataset_check_owner
|
@@ -39,15 +40,15 @@ router = APIRouter()
|
|
39
40
|
|
40
41
|
@router.post(
|
41
42
|
"/project/{project_id}/dataset/",
|
42
|
-
response_model=
|
43
|
+
response_model=DatasetReadV1,
|
43
44
|
status_code=status.HTTP_201_CREATED,
|
44
45
|
)
|
45
46
|
async def create_dataset(
|
46
47
|
project_id: int,
|
47
|
-
dataset:
|
48
|
+
dataset: DatasetCreateV1,
|
48
49
|
user: User = Depends(current_active_user),
|
49
50
|
db: AsyncSession = Depends(get_async_db),
|
50
|
-
) -> Optional[
|
51
|
+
) -> Optional[DatasetReadV1]:
|
51
52
|
"""
|
52
53
|
Add new dataset to current project
|
53
54
|
"""
|
@@ -65,14 +66,14 @@ async def create_dataset(
|
|
65
66
|
|
66
67
|
@router.get(
|
67
68
|
"/project/{project_id}/dataset/",
|
68
|
-
response_model=list[
|
69
|
+
response_model=list[DatasetReadV1],
|
69
70
|
)
|
70
71
|
async def read_dataset_list(
|
71
72
|
project_id: int,
|
72
73
|
history: bool = True,
|
73
74
|
user: User = Depends(current_active_user),
|
74
75
|
db: AsyncSession = Depends(get_async_db),
|
75
|
-
) -> Optional[list[
|
76
|
+
) -> Optional[list[DatasetReadV1]]:
|
76
77
|
"""
|
77
78
|
Get dataset list for given project
|
78
79
|
"""
|
@@ -96,14 +97,14 @@ async def read_dataset_list(
|
|
96
97
|
|
97
98
|
@router.get(
|
98
99
|
"/project/{project_id}/dataset/{dataset_id}/",
|
99
|
-
response_model=
|
100
|
+
response_model=DatasetReadV1,
|
100
101
|
)
|
101
102
|
async def read_dataset(
|
102
103
|
project_id: int,
|
103
104
|
dataset_id: int,
|
104
105
|
user: User = Depends(current_active_user),
|
105
106
|
db: AsyncSession = Depends(get_async_db),
|
106
|
-
) -> Optional[
|
107
|
+
) -> Optional[DatasetReadV1]:
|
107
108
|
"""
|
108
109
|
Get info on a dataset associated to the current project
|
109
110
|
"""
|
@@ -120,15 +121,15 @@ async def read_dataset(
|
|
120
121
|
|
121
122
|
@router.patch(
|
122
123
|
"/project/{project_id}/dataset/{dataset_id}/",
|
123
|
-
response_model=
|
124
|
+
response_model=DatasetReadV1,
|
124
125
|
)
|
125
126
|
async def update_dataset(
|
126
127
|
project_id: int,
|
127
128
|
dataset_id: int,
|
128
|
-
dataset_update:
|
129
|
+
dataset_update: DatasetUpdateV1,
|
129
130
|
user: User = Depends(current_active_user),
|
130
131
|
db: AsyncSession = Depends(get_async_db),
|
131
|
-
) -> Optional[
|
132
|
+
) -> Optional[DatasetReadV1]:
|
132
133
|
"""
|
133
134
|
Edit a dataset associated to the current project
|
134
135
|
"""
|
@@ -229,16 +230,16 @@ async def delete_dataset(
|
|
229
230
|
|
230
231
|
@router.post(
|
231
232
|
"/project/{project_id}/dataset/{dataset_id}/resource/",
|
232
|
-
response_model=
|
233
|
+
response_model=ResourceReadV1,
|
233
234
|
status_code=status.HTTP_201_CREATED,
|
234
235
|
)
|
235
236
|
async def create_resource(
|
236
237
|
project_id: int,
|
237
238
|
dataset_id: int,
|
238
|
-
resource:
|
239
|
+
resource: ResourceCreateV1,
|
239
240
|
user: User = Depends(current_active_user),
|
240
241
|
db: AsyncSession = Depends(get_async_db),
|
241
|
-
) -> Optional[
|
242
|
+
) -> Optional[ResourceReadV1]:
|
242
243
|
"""
|
243
244
|
Add resource to an existing dataset
|
244
245
|
"""
|
@@ -259,14 +260,14 @@ async def create_resource(
|
|
259
260
|
|
260
261
|
@router.get(
|
261
262
|
"/project/{project_id}/dataset/{dataset_id}/resource/",
|
262
|
-
response_model=list[
|
263
|
+
response_model=list[ResourceReadV1],
|
263
264
|
)
|
264
265
|
async def get_resource_list(
|
265
266
|
project_id: int,
|
266
267
|
dataset_id: int,
|
267
268
|
user: User = Depends(current_active_user),
|
268
269
|
db: AsyncSession = Depends(get_async_db),
|
269
|
-
) -> Optional[list[
|
270
|
+
) -> Optional[list[ResourceReadV1]]:
|
270
271
|
"""
|
271
272
|
Get resources from a dataset
|
272
273
|
"""
|
@@ -285,16 +286,16 @@ async def get_resource_list(
|
|
285
286
|
|
286
287
|
@router.patch(
|
287
288
|
"/project/{project_id}/dataset/{dataset_id}/resource/{resource_id}/",
|
288
|
-
response_model=
|
289
|
+
response_model=ResourceReadV1,
|
289
290
|
)
|
290
291
|
async def update_resource(
|
291
292
|
project_id: int,
|
292
293
|
dataset_id: int,
|
293
294
|
resource_id: int,
|
294
|
-
resource_update:
|
295
|
+
resource_update: ResourceUpdateV1,
|
295
296
|
user: User = Depends(current_active_user),
|
296
297
|
db: AsyncSession = Depends(get_async_db),
|
297
|
-
) -> Optional[
|
298
|
+
) -> Optional[ResourceReadV1]:
|
298
299
|
"""
|
299
300
|
Edit a resource of a dataset
|
300
301
|
"""
|
@@ -360,14 +361,14 @@ async def delete_resource(
|
|
360
361
|
|
361
362
|
@router.get(
|
362
363
|
"/project/{project_id}/dataset/{dataset_id}/export_history/",
|
363
|
-
response_model=
|
364
|
+
response_model=WorkflowExportV1,
|
364
365
|
)
|
365
366
|
async def export_history_as_workflow(
|
366
367
|
project_id: int,
|
367
368
|
dataset_id: int,
|
368
369
|
user: User = Depends(current_active_user),
|
369
370
|
db: AsyncSession = Depends(get_async_db),
|
370
|
-
) -> Optional[
|
371
|
+
) -> Optional[WorkflowExportV1]:
|
371
372
|
"""
|
372
373
|
Extract a reproducible workflow from the dataset history.
|
373
374
|
"""
|
@@ -412,7 +413,7 @@ async def export_history_as_workflow(
|
|
412
413
|
wftask = history_item["workflowtask"]
|
413
414
|
wftask_status = history_item["status"]
|
414
415
|
if wftask_status == "done":
|
415
|
-
task_list.append(
|
416
|
+
task_list.append(WorkflowTaskExportV1(**wftask))
|
416
417
|
|
417
418
|
def _slugify_dataset_name(_name: str) -> str:
|
418
419
|
_new_name = _name
|
@@ -422,20 +423,20 @@ async def export_history_as_workflow(
|
|
422
423
|
|
423
424
|
name = f"history_{_slugify_dataset_name(dataset.name)}"
|
424
425
|
|
425
|
-
workflow =
|
426
|
+
workflow = WorkflowExportV1(name=name, task_list=task_list)
|
426
427
|
return workflow
|
427
428
|
|
428
429
|
|
429
430
|
@router.get(
|
430
431
|
"/project/{project_id}/dataset/{dataset_id}/status/",
|
431
|
-
response_model=
|
432
|
+
response_model=DatasetStatusReadV1,
|
432
433
|
)
|
433
434
|
async def get_workflowtask_status(
|
434
435
|
project_id: int,
|
435
436
|
dataset_id: int,
|
436
437
|
user: User = Depends(current_active_user),
|
437
438
|
db: AsyncSession = Depends(get_async_db),
|
438
|
-
) -> Optional[
|
439
|
+
) -> Optional[DatasetStatusReadV1]:
|
439
440
|
"""
|
440
441
|
Extract the status of all `WorkflowTask`s that ran on a given `Dataset`.
|
441
442
|
"""
|
@@ -511,21 +512,27 @@ async def get_workflowtask_status(
|
|
511
512
|
history = json.load(f)
|
512
513
|
except FileNotFoundError:
|
513
514
|
history = []
|
515
|
+
except JSONDecodeError:
|
516
|
+
raise HTTPException(
|
517
|
+
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
518
|
+
detail="History file does not include a valid JSON.",
|
519
|
+
)
|
520
|
+
|
514
521
|
for history_item in history:
|
515
522
|
wftask_id = history_item["workflowtask"]["id"]
|
516
523
|
wftask_status = history_item["status"]
|
517
524
|
workflow_tasks_status_dict[wftask_id] = wftask_status
|
518
525
|
|
519
|
-
response_body =
|
526
|
+
response_body = DatasetStatusReadV1(status=workflow_tasks_status_dict)
|
520
527
|
return response_body
|
521
528
|
|
522
529
|
|
523
|
-
@router.get("/dataset/", response_model=list[
|
530
|
+
@router.get("/dataset/", response_model=list[DatasetReadV1])
|
524
531
|
async def get_user_datasets(
|
525
532
|
history: bool = True,
|
526
533
|
user: User = Depends(current_active_user),
|
527
534
|
db: AsyncSession = Depends(get_async_db),
|
528
|
-
) -> list[
|
535
|
+
) -> list[DatasetReadV1]:
|
529
536
|
"""
|
530
537
|
Returns all the datasets of the current user
|
531
538
|
"""
|
@@ -11,10 +11,10 @@ from sqlmodel import select
|
|
11
11
|
from ....db import AsyncSession
|
12
12
|
from ....db import get_async_db
|
13
13
|
from ....models import ApplyWorkflow
|
14
|
-
from ....models import
|
14
|
+
from ....models import JobStatusTypeV1
|
15
15
|
from ....models import Project
|
16
|
-
from ....runner.
|
17
|
-
from ....schemas import
|
16
|
+
from ....runner.filenames import WORKFLOW_LOG_FILENAME
|
17
|
+
from ....schemas.v1 import ApplyWorkflowReadV1
|
18
18
|
from ....security import current_active_user
|
19
19
|
from ....security import User
|
20
20
|
from ...aux._job import _write_shutdown_file
|
@@ -27,12 +27,12 @@ from ._aux_functions import _get_workflow_check_owner
|
|
27
27
|
router = APIRouter()
|
28
28
|
|
29
29
|
|
30
|
-
@router.get("/job/", response_model=list[
|
30
|
+
@router.get("/job/", response_model=list[ApplyWorkflowReadV1])
|
31
31
|
async def get_user_jobs(
|
32
32
|
user: User = Depends(current_active_user),
|
33
33
|
log: bool = True,
|
34
34
|
db: AsyncSession = Depends(get_async_db),
|
35
|
-
) -> list[
|
35
|
+
) -> list[ApplyWorkflowReadV1]:
|
36
36
|
"""
|
37
37
|
Returns all the jobs of the current user
|
38
38
|
"""
|
@@ -50,14 +50,14 @@ async def get_user_jobs(
|
|
50
50
|
|
51
51
|
@router.get(
|
52
52
|
"/project/{project_id}/workflow/{workflow_id}/job/",
|
53
|
-
response_model=list[
|
53
|
+
response_model=list[ApplyWorkflowReadV1],
|
54
54
|
)
|
55
55
|
async def get_workflow_jobs(
|
56
56
|
project_id: int,
|
57
57
|
workflow_id: int,
|
58
58
|
user: User = Depends(current_active_user),
|
59
59
|
db: AsyncSession = Depends(get_async_db),
|
60
|
-
) -> Optional[list[
|
60
|
+
) -> Optional[list[ApplyWorkflowReadV1]]:
|
61
61
|
"""
|
62
62
|
Returns all the jobs related to a specific workflow
|
63
63
|
"""
|
@@ -72,7 +72,7 @@ async def get_workflow_jobs(
|
|
72
72
|
|
73
73
|
@router.get(
|
74
74
|
"/project/{project_id}/job/{job_id}/",
|
75
|
-
response_model=
|
75
|
+
response_model=ApplyWorkflowReadV1,
|
76
76
|
)
|
77
77
|
async def read_job(
|
78
78
|
project_id: int,
|
@@ -80,7 +80,7 @@ async def read_job(
|
|
80
80
|
show_tmp_logs: bool = False,
|
81
81
|
user: User = Depends(current_active_user),
|
82
82
|
db: AsyncSession = Depends(get_async_db),
|
83
|
-
) -> Optional[
|
83
|
+
) -> Optional[ApplyWorkflowReadV1]:
|
84
84
|
"""
|
85
85
|
Return info on an existing job
|
86
86
|
"""
|
@@ -94,7 +94,7 @@ async def read_job(
|
|
94
94
|
job = output["job"]
|
95
95
|
await db.close()
|
96
96
|
|
97
|
-
if show_tmp_logs and (job.status ==
|
97
|
+
if show_tmp_logs and (job.status == JobStatusTypeV1.SUBMITTED):
|
98
98
|
try:
|
99
99
|
with open(f"{job.working_dir}/{WORKFLOW_LOG_FILENAME}", "r") as f:
|
100
100
|
job.log = f.read()
|
@@ -140,14 +140,14 @@ async def download_job_logs(
|
|
140
140
|
|
141
141
|
@router.get(
|
142
142
|
"/project/{project_id}/job/",
|
143
|
-
response_model=list[
|
143
|
+
response_model=list[ApplyWorkflowReadV1],
|
144
144
|
)
|
145
145
|
async def get_job_list(
|
146
146
|
project_id: int,
|
147
147
|
user: User = Depends(current_active_user),
|
148
148
|
log: bool = True,
|
149
149
|
db: AsyncSession = Depends(get_async_db),
|
150
|
-
) -> Optional[list[
|
150
|
+
) -> Optional[list[ApplyWorkflowReadV1]]:
|
151
151
|
"""
|
152
152
|
Get job list for given project
|
153
153
|
"""
|
@@ -23,15 +23,17 @@ from ....models import Dataset
|
|
23
23
|
from ....models import LinkUserProject
|
24
24
|
from ....models import Project
|
25
25
|
from ....models import Workflow
|
26
|
-
from ....runner import
|
27
|
-
|
28
|
-
|
29
|
-
from ....
|
30
|
-
from ....
|
31
|
-
from ....schemas import
|
32
|
-
from ....schemas import
|
33
|
-
from ....schemas import
|
34
|
-
from ....schemas import
|
26
|
+
from ....runner.set_start_and_last_task_index import (
|
27
|
+
set_start_and_last_task_index,
|
28
|
+
)
|
29
|
+
from ....runner.v1 import submit_workflow
|
30
|
+
from ....runner.v1 import validate_workflow_compatibility
|
31
|
+
from ....schemas.v1 import ApplyWorkflowCreateV1
|
32
|
+
from ....schemas.v1 import ApplyWorkflowReadV1
|
33
|
+
from ....schemas.v1 import JobStatusTypeV1
|
34
|
+
from ....schemas.v1 import ProjectCreateV1
|
35
|
+
from ....schemas.v1 import ProjectReadV1
|
36
|
+
from ....schemas.v1 import ProjectUpdateV1
|
35
37
|
from ....security import current_active_user
|
36
38
|
from ....security import current_active_verified_user
|
37
39
|
from ....security import User
|
@@ -48,7 +50,7 @@ def _encode_as_utc(dt: datetime):
|
|
48
50
|
return dt.replace(tzinfo=timezone.utc).isoformat()
|
49
51
|
|
50
52
|
|
51
|
-
@router.get("/", response_model=list[
|
53
|
+
@router.get("/", response_model=list[ProjectReadV1])
|
52
54
|
async def get_list_project(
|
53
55
|
user: User = Depends(current_active_user),
|
54
56
|
db: AsyncSession = Depends(get_async_db),
|
@@ -67,12 +69,12 @@ async def get_list_project(
|
|
67
69
|
return project_list
|
68
70
|
|
69
71
|
|
70
|
-
@router.post("/", response_model=
|
72
|
+
@router.post("/", response_model=ProjectReadV1, status_code=201)
|
71
73
|
async def create_project(
|
72
|
-
project:
|
74
|
+
project: ProjectCreateV1,
|
73
75
|
user: User = Depends(current_active_user),
|
74
76
|
db: AsyncSession = Depends(get_async_db),
|
75
|
-
) -> Optional[
|
77
|
+
) -> Optional[ProjectReadV1]:
|
76
78
|
"""
|
77
79
|
Create new poject
|
78
80
|
"""
|
@@ -102,12 +104,12 @@ async def create_project(
|
|
102
104
|
return db_project
|
103
105
|
|
104
106
|
|
105
|
-
@router.get("/{project_id}/", response_model=
|
107
|
+
@router.get("/{project_id}/", response_model=ProjectReadV1)
|
106
108
|
async def read_project(
|
107
109
|
project_id: int,
|
108
110
|
user: User = Depends(current_active_user),
|
109
111
|
db: AsyncSession = Depends(get_async_db),
|
110
|
-
) -> Optional[
|
112
|
+
) -> Optional[ProjectReadV1]:
|
111
113
|
"""
|
112
114
|
Return info on an existing project
|
113
115
|
"""
|
@@ -118,10 +120,10 @@ async def read_project(
|
|
118
120
|
return project
|
119
121
|
|
120
122
|
|
121
|
-
@router.patch("/{project_id}/", response_model=
|
123
|
+
@router.patch("/{project_id}/", response_model=ProjectReadV1)
|
122
124
|
async def update_project(
|
123
125
|
project_id: int,
|
124
|
-
project_update:
|
126
|
+
project_update: ProjectUpdateV1,
|
125
127
|
user: User = Depends(current_active_user),
|
126
128
|
db: AsyncSession = Depends(get_async_db),
|
127
129
|
):
|
@@ -241,18 +243,18 @@ async def delete_project(
|
|
241
243
|
@router.post(
|
242
244
|
"/{project_id}/workflow/{workflow_id}/apply/",
|
243
245
|
status_code=status.HTTP_202_ACCEPTED,
|
244
|
-
response_model=
|
246
|
+
response_model=ApplyWorkflowReadV1,
|
245
247
|
)
|
246
248
|
async def apply_workflow(
|
247
249
|
project_id: int,
|
248
250
|
workflow_id: int,
|
249
|
-
apply_workflow:
|
251
|
+
apply_workflow: ApplyWorkflowCreateV1,
|
250
252
|
background_tasks: BackgroundTasks,
|
251
253
|
input_dataset_id: int,
|
252
254
|
output_dataset_id: int,
|
253
255
|
user: User = Depends(current_active_verified_user),
|
254
256
|
db: AsyncSession = Depends(get_async_db),
|
255
|
-
) -> Optional[
|
257
|
+
) -> Optional[ApplyWorkflowReadV1]:
|
256
258
|
|
257
259
|
output = await _get_dataset_check_owner(
|
258
260
|
project_id=project_id,
|
@@ -362,7 +364,7 @@ async def apply_workflow(
|
|
362
364
|
stm = (
|
363
365
|
select(ApplyWorkflow)
|
364
366
|
.where(ApplyWorkflow.output_dataset_id == output_dataset_id)
|
365
|
-
.where(ApplyWorkflow.status ==
|
367
|
+
.where(ApplyWorkflow.status == JobStatusTypeV1.SUBMITTED)
|
366
368
|
)
|
367
369
|
res = await db.execute(stm)
|
368
370
|
if res.scalars().all():
|
@@ -13,9 +13,10 @@ from ....db import AsyncSession
|
|
13
13
|
from ....db import get_async_db
|
14
14
|
from ....models import Task
|
15
15
|
from ....models import WorkflowTask
|
16
|
-
from ....
|
17
|
-
from ....schemas import
|
18
|
-
from ....schemas import
|
16
|
+
from ....models.v2 import TaskV2
|
17
|
+
from ....schemas.v1 import TaskCreateV1
|
18
|
+
from ....schemas.v1 import TaskReadV1
|
19
|
+
from ....schemas.v1 import TaskUpdateV1
|
19
20
|
from ....security import current_active_user
|
20
21
|
from ....security import current_active_verified_user
|
21
22
|
from ....security import User
|
@@ -26,12 +27,12 @@ router = APIRouter()
|
|
26
27
|
logger = set_logger(__name__)
|
27
28
|
|
28
29
|
|
29
|
-
@router.get("/", response_model=list[
|
30
|
+
@router.get("/", response_model=list[TaskReadV1])
|
30
31
|
async def get_list_task(
|
31
32
|
user: User = Depends(current_active_user),
|
32
33
|
args_schema: bool = True,
|
33
34
|
db: AsyncSession = Depends(get_async_db),
|
34
|
-
) -> list[
|
35
|
+
) -> list[TaskReadV1]:
|
35
36
|
"""
|
36
37
|
Get list of available tasks
|
37
38
|
"""
|
@@ -46,12 +47,12 @@ async def get_list_task(
|
|
46
47
|
return task_list
|
47
48
|
|
48
49
|
|
49
|
-
@router.get("/{task_id}/", response_model=
|
50
|
+
@router.get("/{task_id}/", response_model=TaskReadV1)
|
50
51
|
async def get_task(
|
51
52
|
task_id: int,
|
52
53
|
user: User = Depends(current_active_user),
|
53
54
|
db: AsyncSession = Depends(get_async_db),
|
54
|
-
) ->
|
55
|
+
) -> TaskReadV1:
|
55
56
|
"""
|
56
57
|
Get info on a specific task
|
57
58
|
"""
|
@@ -64,13 +65,13 @@ async def get_task(
|
|
64
65
|
return task
|
65
66
|
|
66
67
|
|
67
|
-
@router.patch("/{task_id}/", response_model=
|
68
|
+
@router.patch("/{task_id}/", response_model=TaskReadV1)
|
68
69
|
async def patch_task(
|
69
70
|
task_id: int,
|
70
|
-
task_update:
|
71
|
+
task_update: TaskUpdateV1,
|
71
72
|
user: User = Depends(current_active_verified_user),
|
72
73
|
db: AsyncSession = Depends(get_async_db),
|
73
|
-
) -> Optional[
|
74
|
+
) -> Optional[TaskReadV1]:
|
74
75
|
"""
|
75
76
|
Edit a specific task (restricted to superusers and task owner)
|
76
77
|
"""
|
@@ -109,12 +110,14 @@ async def patch_task(
|
|
109
110
|
return db_task
|
110
111
|
|
111
112
|
|
112
|
-
@router.post(
|
113
|
+
@router.post(
|
114
|
+
"/", response_model=TaskReadV1, status_code=status.HTTP_201_CREATED
|
115
|
+
)
|
113
116
|
async def create_task(
|
114
|
-
task:
|
117
|
+
task: TaskCreateV1,
|
115
118
|
user: User = Depends(current_active_verified_user),
|
116
119
|
db: AsyncSession = Depends(get_async_db),
|
117
|
-
) -> Optional[
|
120
|
+
) -> Optional[TaskReadV1]:
|
118
121
|
"""
|
119
122
|
Create a new task
|
120
123
|
"""
|
@@ -143,7 +146,14 @@ async def create_task(
|
|
143
146
|
if res.scalars().all():
|
144
147
|
raise HTTPException(
|
145
148
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
146
|
-
detail=f'
|
149
|
+
detail=f"Source '{task.source}' already used by some TaskV1",
|
150
|
+
)
|
151
|
+
stm = select(TaskV2).where(TaskV2.source == task.source)
|
152
|
+
res = await db.execute(stm)
|
153
|
+
if res.scalars().all():
|
154
|
+
raise HTTPException(
|
155
|
+
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
156
|
+
detail=f"Source '{task.source}' already used by some TaskV2",
|
147
157
|
)
|
148
158
|
|
149
159
|
# Add task
|
@@ -15,24 +15,26 @@ from .....config import get_settings
|
|
15
15
|
from .....logger import close_logger
|
16
16
|
from .....logger import set_logger
|
17
17
|
from .....syringe import Inject
|
18
|
-
from .....tasks._TaskCollectPip import _TaskCollectPip
|
19
|
-
from .....tasks.background_operations import background_collect_pip
|
20
|
-
from .....tasks.endpoint_operations import create_package_dir_pip
|
21
|
-
from .....tasks.endpoint_operations import download_package
|
22
|
-
from .....tasks.endpoint_operations import get_collection_data
|
23
|
-
from .....tasks.endpoint_operations import inspect_package
|
24
|
-
from .....tasks.utils import get_collection_log
|
25
|
-
from .....tasks.utils import slugify_task_name
|
26
18
|
from ....db import AsyncSession
|
27
19
|
from ....db import get_async_db
|
28
20
|
from ....models import State
|
29
21
|
from ....models import Task
|
30
|
-
from ....schemas import StateRead
|
31
|
-
from ....schemas import
|
32
|
-
from ....schemas import
|
22
|
+
from ....schemas.v1 import StateRead
|
23
|
+
from ....schemas.v1 import TaskCollectPipV1
|
24
|
+
from ....schemas.v1 import TaskCollectStatusV1
|
33
25
|
from ....security import current_active_user
|
34
26
|
from ....security import current_active_verified_user
|
35
27
|
from ....security import User
|
28
|
+
from fractal_server.tasks.endpoint_operations import create_package_dir_pip
|
29
|
+
from fractal_server.tasks.endpoint_operations import download_package
|
30
|
+
from fractal_server.tasks.endpoint_operations import inspect_package
|
31
|
+
from fractal_server.tasks.utils import get_collection_log
|
32
|
+
from fractal_server.tasks.utils import slugify_task_name
|
33
|
+
from fractal_server.tasks.v1._TaskCollectPip import _TaskCollectPip
|
34
|
+
from fractal_server.tasks.v1.background_operations import (
|
35
|
+
background_collect_pip,
|
36
|
+
)
|
37
|
+
from fractal_server.tasks.v1.get_collection_data import get_collection_data
|
36
38
|
|
37
39
|
router = APIRouter()
|
38
40
|
|
@@ -57,7 +59,7 @@ logger = set_logger(__name__)
|
|
57
59
|
},
|
58
60
|
)
|
59
61
|
async def collect_tasks_pip(
|
60
|
-
task_collect:
|
62
|
+
task_collect: TaskCollectPipV1,
|
61
63
|
background_tasks: BackgroundTasks,
|
62
64
|
response: Response,
|
63
65
|
user: User = Depends(current_active_verified_user),
|
@@ -162,7 +164,7 @@ async def collect_tasks_pip(
|
|
162
164
|
|
163
165
|
# All checks are OK, proceed with task collection
|
164
166
|
full_venv_path = venv_path.relative_to(settings.FRACTAL_TASKS_DIR)
|
165
|
-
collection_status =
|
167
|
+
collection_status = TaskCollectStatusV1(
|
166
168
|
status="pending", venv_path=full_venv_path, package=task_pkg.package
|
167
169
|
)
|
168
170
|
|
@@ -214,7 +216,7 @@ async def check_collection_status(
|
|
214
216
|
status_code=status.HTTP_404_NOT_FOUND,
|
215
217
|
detail=f"No task collection info with id={state_id}",
|
216
218
|
)
|
217
|
-
data =
|
219
|
+
data = TaskCollectStatusV1(**state.data)
|
218
220
|
|
219
221
|
# In some cases (i.e. a successful or ongoing task collection), data.log is
|
220
222
|
# not set; if so, we collect the current logs
|