fractal-server 2.14.0a5__py3-none-any.whl → 2.14.0a7__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/routes/api/v2/task.py +3 -10
- fractal_server/app/routes/api/v2/workflowtask.py +13 -2
- fractal_server/app/runner/executors/base_runner.py +53 -26
- fractal_server/app/runner/executors/local/runner.py +6 -5
- fractal_server/app/runner/executors/slurm_sudo/runner.py +5 -60
- fractal_server/app/runner/v2/_db_tools.py +48 -0
- fractal_server/app/runner/v2/runner.py +55 -17
- fractal_server/app/runner/v2/runner_functions.py +307 -62
- fractal_server/app/schemas/v2/manifest.py +11 -0
- fractal_server/app/schemas/v2/task.py +33 -0
- fractal_server/app/schemas/v2/workflowtask.py +2 -1
- fractal_server/tasks/v2/utils_database.py +1 -16
- {fractal_server-2.14.0a5.dist-info → fractal_server-2.14.0a7.dist-info}/METADATA +1 -1
- {fractal_server-2.14.0a5.dist-info → fractal_server-2.14.0a7.dist-info}/RECORD +18 -17
- {fractal_server-2.14.0a5.dist-info → fractal_server-2.14.0a7.dist-info}/LICENSE +0 -0
- {fractal_server-2.14.0a5.dist-info → fractal_server-2.14.0a7.dist-info}/WHEEL +0 -0
- {fractal_server-2.14.0a5.dist-info → fractal_server-2.14.0a7.dist-info}/entry_points.txt +0 -0
fractal_server/__init__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__VERSION__ = "2.14.
|
1
|
+
__VERSION__ = "2.14.0a7"
|
@@ -152,14 +152,7 @@ async def create_task(
|
|
152
152
|
db=db,
|
153
153
|
)
|
154
154
|
|
155
|
-
if task.
|
156
|
-
task_type = "parallel"
|
157
|
-
elif task.command_parallel is None:
|
158
|
-
task_type = "non_parallel"
|
159
|
-
else:
|
160
|
-
task_type = "compound"
|
161
|
-
|
162
|
-
if task_type == "parallel" and (
|
155
|
+
if task.type == "parallel" and (
|
163
156
|
task.args_schema_non_parallel is not None
|
164
157
|
or task.meta_non_parallel is not None
|
165
158
|
):
|
@@ -170,7 +163,7 @@ async def create_task(
|
|
170
163
|
"`TaskV2.args_schema_non_parallel` if TaskV2 is parallel"
|
171
164
|
),
|
172
165
|
)
|
173
|
-
elif
|
166
|
+
elif task.type == "non_parallel" and (
|
174
167
|
task.args_schema_parallel is not None or task.meta_parallel is not None
|
175
168
|
):
|
176
169
|
raise HTTPException(
|
@@ -183,7 +176,7 @@ async def create_task(
|
|
183
176
|
|
184
177
|
# Add task
|
185
178
|
|
186
|
-
db_task = TaskV2(**task.model_dump(exclude_unset=True)
|
179
|
+
db_task = TaskV2(**task.model_dump(exclude_unset=True))
|
187
180
|
pkg_name = db_task.name
|
188
181
|
await _verify_non_duplication_user_constraint(
|
189
182
|
db=db, pkg_name=pkg_name, user_id=user.id, version=db_task.version
|
@@ -56,7 +56,14 @@ async def replace_workflowtask(
|
|
56
56
|
)
|
57
57
|
|
58
58
|
# Preliminary checks
|
59
|
-
|
59
|
+
EQUIVALENT_TASK_TYPES = [
|
60
|
+
{"non_parallel", "converter_non_parallel"},
|
61
|
+
{"compound", "converter_compound"},
|
62
|
+
]
|
63
|
+
if (
|
64
|
+
old_wftask.task_type != new_task.type
|
65
|
+
and {old_wftask.task_type, new_task.type} not in EQUIVALENT_TASK_TYPES
|
66
|
+
):
|
60
67
|
raise HTTPException(
|
61
68
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
62
69
|
detail=(
|
@@ -64,6 +71,7 @@ async def replace_workflowtask(
|
|
64
71
|
f"{old_wftask.task_type} to {new_task.type}."
|
65
72
|
),
|
66
73
|
)
|
74
|
+
|
67
75
|
if replace.args_non_parallel is not None and new_task.type == "parallel":
|
68
76
|
raise HTTPException(
|
69
77
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
@@ -259,7 +267,10 @@ async def update_workflowtask(
|
|
259
267
|
"parallel."
|
260
268
|
),
|
261
269
|
)
|
262
|
-
elif db_wf_task.task_type
|
270
|
+
elif db_wf_task.task_type in [
|
271
|
+
"non_parallel",
|
272
|
+
"converter_non_parallel",
|
273
|
+
] and (
|
263
274
|
workflow_task_update.args_parallel is not None
|
264
275
|
or workflow_task_update.meta_parallel is not None
|
265
276
|
):
|
@@ -1,6 +1,20 @@
|
|
1
1
|
from typing import Any
|
2
2
|
|
3
3
|
from fractal_server.app.runner.components import _COMPONENT_KEY_
|
4
|
+
from fractal_server.app.schemas.v2.task import TaskTypeType
|
5
|
+
|
6
|
+
|
7
|
+
TASK_TYPES_SUBMIT: list[TaskTypeType] = [
|
8
|
+
"compound",
|
9
|
+
"converter_compound",
|
10
|
+
"non_parallel",
|
11
|
+
"converter_non_parallel",
|
12
|
+
]
|
13
|
+
TASK_TYPES_MULTISUBMIT: list[TaskTypeType] = [
|
14
|
+
"compound",
|
15
|
+
"converter_compound",
|
16
|
+
"parallel",
|
17
|
+
]
|
4
18
|
|
5
19
|
|
6
20
|
class BaseRunner(object):
|
@@ -16,7 +30,7 @@ class BaseRunner(object):
|
|
16
30
|
func: callable,
|
17
31
|
parameters: dict[str, Any],
|
18
32
|
history_item_id: int,
|
19
|
-
|
33
|
+
task_type: TaskTypeType,
|
20
34
|
**kwargs,
|
21
35
|
) -> tuple[Any, BaseException]:
|
22
36
|
"""
|
@@ -25,16 +39,13 @@ class BaseRunner(object):
|
|
25
39
|
# FIXME: Describe more in detail
|
26
40
|
|
27
41
|
Args:
|
28
|
-
func:
|
29
|
-
Function to be executed.
|
42
|
+
func: Function to be executed.
|
30
43
|
parameters:
|
31
44
|
Dictionary of parameters. Must include `zarr_urls` key.
|
32
45
|
history_item_id:
|
33
46
|
Database ID of the corresponding `HistoryItemV2` entry.
|
34
|
-
|
35
|
-
|
36
|
-
kwargs:
|
37
|
-
Runner-specific parameters.
|
47
|
+
task_type: Task type.
|
48
|
+
kwargs: Runner-specific parameters.
|
38
49
|
"""
|
39
50
|
raise NotImplementedError()
|
40
51
|
|
@@ -43,7 +54,7 @@ class BaseRunner(object):
|
|
43
54
|
func: callable,
|
44
55
|
list_parameters: list[dict[str, Any]],
|
45
56
|
history_item_id: int,
|
46
|
-
|
57
|
+
task_type: TaskTypeType,
|
47
58
|
**kwargs,
|
48
59
|
) -> tuple[dict[int, Any], dict[int, BaseException]]:
|
49
60
|
"""
|
@@ -52,33 +63,44 @@ class BaseRunner(object):
|
|
52
63
|
# FIXME: Describe more in detail
|
53
64
|
|
54
65
|
Args:
|
55
|
-
func:
|
56
|
-
Function to be executed.
|
66
|
+
func: Function to be executed.
|
57
67
|
list_parameters:
|
58
68
|
List of dictionaries of parameters. Each one must include a
|
59
69
|
`zarr_url` key.
|
60
70
|
history_item_id:
|
61
71
|
Database ID of the corresponding `HistoryItemV2` entry.
|
62
|
-
|
63
|
-
|
64
|
-
kwargs:
|
65
|
-
Runner-specific parameters.
|
72
|
+
task_type: Task type.
|
73
|
+
kwargs: Runner-specific parameters.
|
66
74
|
"""
|
67
75
|
raise NotImplementedError()
|
68
76
|
|
69
|
-
def validate_submit_parameters(
|
77
|
+
def validate_submit_parameters(
|
78
|
+
self,
|
79
|
+
parameters: dict[str, Any],
|
80
|
+
task_type: TaskTypeType,
|
81
|
+
) -> None:
|
70
82
|
"""
|
71
83
|
Validate parameters for `submit` method
|
72
84
|
|
73
85
|
Args:
|
74
86
|
parameters: Parameters dictionary.
|
87
|
+
task_type: Task type.s
|
75
88
|
"""
|
89
|
+
if task_type not in TASK_TYPES_SUBMIT:
|
90
|
+
raise ValueError(f"Invalid {task_type=} for `submit`.")
|
76
91
|
if not isinstance(parameters, dict):
|
77
92
|
raise ValueError("`parameters` must be a dictionary.")
|
78
|
-
if "
|
79
|
-
|
80
|
-
|
81
|
-
|
93
|
+
if task_type in ["non_parallel", "compound"]:
|
94
|
+
if "zarr_urls" not in parameters.keys():
|
95
|
+
raise ValueError(
|
96
|
+
f"No 'zarr_urls' key in in {list(parameters.keys())}"
|
97
|
+
)
|
98
|
+
elif task_type in ["converter_non_parallel", "converter_compound"]:
|
99
|
+
if "zarr_urls" in parameters.keys():
|
100
|
+
raise ValueError(
|
101
|
+
f"Forbidden 'zarr_urls' key in {list(parameters.keys())}"
|
102
|
+
)
|
103
|
+
|
82
104
|
if _COMPONENT_KEY_ not in parameters.keys():
|
83
105
|
raise ValueError(
|
84
106
|
f"No '{_COMPONENT_KEY_}' key in in {list(parameters.keys())}"
|
@@ -87,21 +109,26 @@ class BaseRunner(object):
|
|
87
109
|
def validate_multisubmit_parameters(
|
88
110
|
self,
|
89
111
|
list_parameters: list[dict[str, Any]],
|
90
|
-
|
112
|
+
task_type: TaskTypeType,
|
91
113
|
) -> None:
|
92
114
|
"""
|
93
115
|
Validate parameters for `multi_submit` method
|
94
116
|
|
95
117
|
Args:
|
96
118
|
list_parameters: List of parameters dictionaries.
|
97
|
-
|
98
|
-
Whether this is the compute part of a compound task.
|
119
|
+
task_type: Task type.
|
99
120
|
"""
|
121
|
+
if task_type not in TASK_TYPES_MULTISUBMIT:
|
122
|
+
raise ValueError(f"Invalid {task_type=} for `multisubmit`.")
|
123
|
+
|
124
|
+
if not isinstance(list_parameters, list):
|
125
|
+
raise ValueError("`parameters` must be a list.")
|
126
|
+
|
100
127
|
for single_kwargs in list_parameters:
|
101
128
|
if not isinstance(single_kwargs, dict):
|
102
|
-
raise
|
129
|
+
raise ValueError("kwargs itemt must be a dictionary.")
|
103
130
|
if "zarr_url" not in single_kwargs.keys():
|
104
|
-
raise
|
131
|
+
raise ValueError(
|
105
132
|
f"No 'zarr_url' key in in {list(single_kwargs.keys())}"
|
106
133
|
)
|
107
134
|
if _COMPONENT_KEY_ not in single_kwargs.keys():
|
@@ -109,7 +136,7 @@ class BaseRunner(object):
|
|
109
136
|
f"No '{_COMPONENT_KEY_}' key "
|
110
137
|
f"in {list(single_kwargs.keys())}"
|
111
138
|
)
|
112
|
-
if
|
139
|
+
if task_type == "parallel":
|
113
140
|
zarr_urls = [kwargs["zarr_url"] for kwargs in list_parameters]
|
114
141
|
if len(zarr_urls) != len(set(zarr_urls)):
|
115
|
-
raise
|
142
|
+
raise ValueError("Non-unique zarr_urls")
|
@@ -9,9 +9,9 @@ from ._local_config import LocalBackendConfig
|
|
9
9
|
from fractal_server.app.runner.components import _COMPONENT_KEY_
|
10
10
|
from fractal_server.app.runner.executors.base_runner import BaseRunner
|
11
11
|
from fractal_server.app.runner.task_files import TaskFiles
|
12
|
+
from fractal_server.app.schemas.v2.task import TaskTypeType
|
12
13
|
from fractal_server.logger import set_logger
|
13
14
|
|
14
|
-
|
15
15
|
logger = set_logger(__name__)
|
16
16
|
|
17
17
|
|
@@ -50,6 +50,7 @@ class LocalRunner(BaseRunner):
|
|
50
50
|
func: callable,
|
51
51
|
parameters: dict[str, Any],
|
52
52
|
task_files: TaskFiles,
|
53
|
+
task_type: TaskTypeType,
|
53
54
|
local_backend_config: Optional[LocalBackendConfig] = None,
|
54
55
|
) -> tuple[Any, Exception]:
|
55
56
|
logger.debug("[submit] START")
|
@@ -61,7 +62,7 @@ class LocalRunner(BaseRunner):
|
|
61
62
|
component=parameters[_COMPONENT_KEY_],
|
62
63
|
)
|
63
64
|
|
64
|
-
self.validate_submit_parameters(parameters)
|
65
|
+
self.validate_submit_parameters(parameters, task_type=task_type)
|
65
66
|
workdir_local = current_task_files.wftask_subfolder_local
|
66
67
|
workdir_local.mkdir()
|
67
68
|
|
@@ -83,18 +84,18 @@ class LocalRunner(BaseRunner):
|
|
83
84
|
func: callable,
|
84
85
|
list_parameters: list[dict],
|
85
86
|
task_files: TaskFiles,
|
86
|
-
|
87
|
+
task_type: TaskTypeType,
|
87
88
|
local_backend_config: Optional[LocalBackendConfig] = None,
|
88
89
|
):
|
89
90
|
logger.debug(f"[multisubmit] START, {len(list_parameters)=}")
|
90
91
|
|
91
92
|
self.validate_multisubmit_parameters(
|
92
93
|
list_parameters=list_parameters,
|
93
|
-
|
94
|
+
task_type=task_type,
|
94
95
|
)
|
95
96
|
|
96
97
|
workdir_local = task_files.wftask_subfolder_local
|
97
|
-
if not
|
98
|
+
if task_type not in ["compound", "converter_compound"]:
|
98
99
|
workdir_local.mkdir()
|
99
100
|
|
100
101
|
# Get local_backend_config
|
@@ -30,15 +30,11 @@ from fractal_server.app.runner.executors.slurm_common._slurm_config import (
|
|
30
30
|
)
|
31
31
|
from fractal_server.app.runner.filenames import SHUTDOWN_FILENAME
|
32
32
|
from fractal_server.app.runner.task_files import TaskFiles
|
33
|
+
from fractal_server.app.schemas.v2.task import TaskTypeType
|
33
34
|
from fractal_server.config import get_settings
|
34
35
|
from fractal_server.logger import set_logger
|
35
36
|
from fractal_server.syringe import Inject
|
36
37
|
|
37
|
-
# from fractal_server.app.history import ImageStatus
|
38
|
-
# from fractal_server.app.history import update_all_images
|
39
|
-
# from fractal_server.app.history import update_single_image
|
40
|
-
# from fractal_server.app.history import update_single_image_logfile
|
41
|
-
|
42
38
|
|
43
39
|
logger = set_logger(__name__)
|
44
40
|
|
@@ -426,7 +422,7 @@ class RunnerSlurmSudo(BaseRunner):
|
|
426
422
|
history_item_id: int,
|
427
423
|
task_files: TaskFiles,
|
428
424
|
slurm_config: SlurmConfig,
|
429
|
-
|
425
|
+
task_type: TaskTypeType,
|
430
426
|
) -> tuple[Any, Exception]:
|
431
427
|
workdir_local = task_files.wftask_subfolder_local
|
432
428
|
workdir_remote = task_files.wftask_subfolder_remote
|
@@ -439,21 +435,9 @@ class RunnerSlurmSudo(BaseRunner):
|
|
439
435
|
)
|
440
436
|
|
441
437
|
if self.jobs != {}:
|
442
|
-
if not in_compound_task:
|
443
|
-
pass
|
444
|
-
# update_all_images(
|
445
|
-
# history_item_id=history_item_id,
|
446
|
-
# status=ImageStatus.FAILED,
|
447
|
-
# )
|
448
438
|
raise JobExecutionError("Unexpected branch: jobs should be empty.")
|
449
439
|
|
450
440
|
if self.is_shutdown():
|
451
|
-
if not in_compound_task:
|
452
|
-
pass
|
453
|
-
# update_all_images(
|
454
|
-
# history_item_id=history_item_id,
|
455
|
-
# status=ImageStatus.FAILED,
|
456
|
-
# )
|
457
441
|
raise JobExecutionError("Cannot continue after shutdown.")
|
458
442
|
|
459
443
|
# Validation phase
|
@@ -505,22 +489,6 @@ class RunnerSlurmSudo(BaseRunner):
|
|
505
489
|
)
|
506
490
|
time.sleep(self.slurm_poll_interval)
|
507
491
|
|
508
|
-
if not in_compound_task:
|
509
|
-
if exception is None:
|
510
|
-
pass
|
511
|
-
# update_all_images(
|
512
|
-
# history_item_id=history_item_id,
|
513
|
-
# status=ImageStatus.DONE,
|
514
|
-
# logfile=LOGFILE,
|
515
|
-
# )
|
516
|
-
else:
|
517
|
-
pass
|
518
|
-
# update_all_images(
|
519
|
-
# history_item_id=history_item_id,
|
520
|
-
# status=ImageStatus.FAILED,
|
521
|
-
# logfile=LOGFILE,
|
522
|
-
# )
|
523
|
-
|
524
492
|
return result, exception
|
525
493
|
|
526
494
|
def multisubmit(
|
@@ -530,20 +498,19 @@ class RunnerSlurmSudo(BaseRunner):
|
|
530
498
|
history_item_id: int,
|
531
499
|
task_files: TaskFiles,
|
532
500
|
slurm_config: SlurmConfig,
|
533
|
-
|
501
|
+
task_type: TaskTypeType,
|
534
502
|
):
|
535
503
|
# self.scancel_jobs()
|
536
504
|
|
537
505
|
self.validate_multisubmit_parameters(
|
538
|
-
list_parameters=list_parameters,
|
539
|
-
in_compound_task=in_compound_task,
|
506
|
+
list_parameters=list_parameters, task_type=task_type
|
540
507
|
)
|
541
508
|
|
542
509
|
workdir_local = task_files.wftask_subfolder_local
|
543
510
|
workdir_remote = task_files.wftask_subfolder_remote
|
544
511
|
|
545
512
|
# Create local&remote task subfolders
|
546
|
-
if not
|
513
|
+
if task_type not in ["converter_compound", "compound"]:
|
547
514
|
original_umask = os.umask(0)
|
548
515
|
workdir_local.mkdir(parents=True, mode=0o755)
|
549
516
|
os.umask(original_umask)
|
@@ -640,28 +607,6 @@ class RunnerSlurmSudo(BaseRunner):
|
|
640
607
|
result, exception = self._postprocess_single_task(
|
641
608
|
task=task
|
642
609
|
)
|
643
|
-
if not in_compound_task:
|
644
|
-
pass
|
645
|
-
# update_single_image_logfile(
|
646
|
-
# history_item_id=history_item_id,
|
647
|
-
# zarr_url=task.zarr_url,
|
648
|
-
# logfile=task.task_files.log_file_local,
|
649
|
-
# )
|
650
|
-
if not in_compound_task:
|
651
|
-
if exception is None:
|
652
|
-
pass
|
653
|
-
# update_single_image(
|
654
|
-
# zarr_url=task.zarr_url,
|
655
|
-
# history_item_id=history_item_id,
|
656
|
-
# status=ImageStatus.DONE,
|
657
|
-
# )
|
658
|
-
else:
|
659
|
-
pass
|
660
|
-
# update_single_image(
|
661
|
-
# zarr_url=task.zarr_url,
|
662
|
-
# history_item_id=history_item_id,
|
663
|
-
# status=ImageStatus.FAILED,
|
664
|
-
# )
|
665
610
|
if exception is None:
|
666
611
|
results[task.index] = result
|
667
612
|
else:
|
@@ -0,0 +1,48 @@
|
|
1
|
+
from typing import Any
|
2
|
+
|
3
|
+
from sqlalchemy.dialects.postgresql import insert as pg_insert
|
4
|
+
from sqlalchemy.orm import Session
|
5
|
+
|
6
|
+
from fractal_server.app.models.v2 import HistoryImageCache
|
7
|
+
|
8
|
+
|
9
|
+
def bulk_upsert_image_cache_fast(
|
10
|
+
*,
|
11
|
+
list_upsert_objects: list[dict[str, Any]],
|
12
|
+
db: Session,
|
13
|
+
) -> None:
|
14
|
+
"""
|
15
|
+
Insert or update many objects into `HistoryImageCache` and commit
|
16
|
+
|
17
|
+
This function is an optimized version of
|
18
|
+
|
19
|
+
```python
|
20
|
+
for obj in list_upsert_objects:
|
21
|
+
db.merge(**obj)
|
22
|
+
db.commit()
|
23
|
+
```
|
24
|
+
|
25
|
+
See docs at
|
26
|
+
https://docs.sqlalchemy.org/en/20/dialects/postgresql.html#insert-on-conflict-upsert
|
27
|
+
|
28
|
+
FIXME: we tried to replace `index_elements` with
|
29
|
+
`constraint="pk_historyimagecache"`, but it did not work as expected.
|
30
|
+
|
31
|
+
Arguments:
|
32
|
+
list_upsert_objects:
|
33
|
+
List of dictionaries for objects to be upsert-ed.
|
34
|
+
db: A sync database session
|
35
|
+
"""
|
36
|
+
if len(list_upsert_objects) == 0:
|
37
|
+
return None
|
38
|
+
stmt = pg_insert(HistoryImageCache).values(list_upsert_objects)
|
39
|
+
stmt = stmt.on_conflict_do_update(
|
40
|
+
index_elements=[
|
41
|
+
HistoryImageCache.zarr_url,
|
42
|
+
HistoryImageCache.dataset_id,
|
43
|
+
HistoryImageCache.workflowtask_id,
|
44
|
+
],
|
45
|
+
set_=dict(latest_history_unit_id=stmt.excluded.latest_history_unit_id),
|
46
|
+
)
|
47
|
+
db.execute(stmt)
|
48
|
+
db.commit()
|
@@ -14,6 +14,8 @@ from ....images.tools import find_image_by_zarr_url
|
|
14
14
|
from ..exceptions import JobExecutionError
|
15
15
|
from .runner_functions import no_op_submit_setup_call
|
16
16
|
from .runner_functions import run_v2_task_compound
|
17
|
+
from .runner_functions import run_v2_task_converter_compound
|
18
|
+
from .runner_functions import run_v2_task_converter_non_parallel
|
17
19
|
from .runner_functions import run_v2_task_non_parallel
|
18
20
|
from .runner_functions import run_v2_task_parallel
|
19
21
|
from .task_interface import TaskOutput
|
@@ -63,22 +65,26 @@ def execute_tasks_v2(
|
|
63
65
|
# PRE TASK EXECUTION
|
64
66
|
|
65
67
|
# Filter images by types and attributes (in two steps)
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
68
|
+
if wftask.task_type in ["compound", "parallel", "non_parallel"]:
|
69
|
+
type_filters = copy(current_dataset_type_filters)
|
70
|
+
type_filters_patch = merge_type_filters(
|
71
|
+
task_input_types=task.input_types,
|
72
|
+
wftask_type_filters=wftask.type_filters,
|
73
|
+
)
|
74
|
+
type_filters.update(type_filters_patch)
|
75
|
+
type_filtered_images = filter_image_list(
|
76
|
+
images=tmp_images,
|
77
|
+
type_filters=type_filters,
|
78
|
+
attribute_filters=None,
|
79
|
+
)
|
80
|
+
num_available_images = len(type_filtered_images)
|
81
|
+
filtered_images = filter_image_list(
|
82
|
+
images=type_filtered_images,
|
83
|
+
type_filters=None,
|
84
|
+
attribute_filters=job_attribute_filters,
|
85
|
+
)
|
86
|
+
else:
|
87
|
+
num_available_images = 0
|
82
88
|
|
83
89
|
# Create history item
|
84
90
|
with next(get_sync_db()) as db:
|
@@ -100,7 +106,7 @@ def execute_tasks_v2(
|
|
100
106
|
workflowtask_id=wftask.id,
|
101
107
|
workflowtask_dump=workflowtask_dump,
|
102
108
|
task_group_dump=task_group_dump,
|
103
|
-
num_available_images=
|
109
|
+
num_available_images=num_available_images,
|
104
110
|
status=XXXStatus.SUBMITTED,
|
105
111
|
)
|
106
112
|
db.add(history_run)
|
@@ -126,6 +132,22 @@ def execute_tasks_v2(
|
|
126
132
|
history_run_id=history_run_id,
|
127
133
|
dataset_id=dataset.id,
|
128
134
|
)
|
135
|
+
elif task.type == "converter_non_parallel":
|
136
|
+
(
|
137
|
+
current_task_output,
|
138
|
+
num_tasks,
|
139
|
+
exceptions,
|
140
|
+
) = run_v2_task_converter_non_parallel(
|
141
|
+
zarr_dir=zarr_dir,
|
142
|
+
wftask=wftask,
|
143
|
+
task=task,
|
144
|
+
workflow_dir_local=workflow_dir_local,
|
145
|
+
workflow_dir_remote=workflow_dir_remote,
|
146
|
+
executor=runner,
|
147
|
+
submit_setup_call=submit_setup_call,
|
148
|
+
history_run_id=history_run_id,
|
149
|
+
dataset_id=dataset.id,
|
150
|
+
)
|
129
151
|
elif task.type == "parallel":
|
130
152
|
current_task_output, num_tasks, exceptions = run_v2_task_parallel(
|
131
153
|
images=filtered_images,
|
@@ -151,6 +173,22 @@ def execute_tasks_v2(
|
|
151
173
|
history_run_id=history_run_id,
|
152
174
|
dataset_id=dataset.id,
|
153
175
|
)
|
176
|
+
elif task.type == "converter_compound":
|
177
|
+
(
|
178
|
+
current_task_output,
|
179
|
+
num_tasks,
|
180
|
+
exceptions,
|
181
|
+
) = run_v2_task_converter_compound(
|
182
|
+
zarr_dir=zarr_dir,
|
183
|
+
wftask=wftask,
|
184
|
+
task=task,
|
185
|
+
workflow_dir_local=workflow_dir_local,
|
186
|
+
workflow_dir_remote=workflow_dir_remote,
|
187
|
+
executor=runner,
|
188
|
+
submit_setup_call=submit_setup_call,
|
189
|
+
history_run_id=history_run_id,
|
190
|
+
dataset_id=dataset.id,
|
191
|
+
)
|
154
192
|
else:
|
155
193
|
raise ValueError(f"Unexpected error: Invalid {task.type=}.")
|
156
194
|
|
@@ -16,13 +16,13 @@ from .task_interface import InitTaskOutput
|
|
16
16
|
from .task_interface import TaskOutput
|
17
17
|
from fractal_server.app.db import get_sync_db
|
18
18
|
from fractal_server.app.history.status_enum import XXXStatus
|
19
|
-
from fractal_server.app.models.v2 import HistoryImageCache
|
20
19
|
from fractal_server.app.models.v2 import HistoryUnit
|
21
20
|
from fractal_server.app.models.v2 import TaskV2
|
22
21
|
from fractal_server.app.models.v2 import WorkflowTaskV2
|
23
22
|
from fractal_server.app.runner.components import _COMPONENT_KEY_
|
24
23
|
from fractal_server.app.runner.components import _index_to_component
|
25
24
|
from fractal_server.app.runner.executors.base_runner import BaseRunner
|
25
|
+
from fractal_server.app.runner.v2._db_tools import bulk_upsert_image_cache_fast
|
26
26
|
|
27
27
|
|
28
28
|
__all__ = [
|
@@ -114,12 +114,12 @@ def run_v2_task_non_parallel(
|
|
114
114
|
which_type="non_parallel",
|
115
115
|
)
|
116
116
|
|
117
|
-
function_kwargs =
|
118
|
-
zarr_urls
|
119
|
-
zarr_dir
|
117
|
+
function_kwargs = {
|
118
|
+
"zarr_urls": [image["zarr_url"] for image in images],
|
119
|
+
"zarr_dir": zarr_dir,
|
120
|
+
_COMPONENT_KEY_: _index_to_component(0),
|
120
121
|
**(wftask.args_non_parallel or {}),
|
121
|
-
|
122
|
-
function_kwargs[_COMPONENT_KEY_] = _index_to_component(0)
|
122
|
+
}
|
123
123
|
|
124
124
|
# Database History operations
|
125
125
|
with next(get_sync_db()) as db:
|
@@ -133,16 +133,103 @@ def run_v2_task_non_parallel(
|
|
133
133
|
db.commit()
|
134
134
|
db.refresh(history_unit)
|
135
135
|
history_unit_id = history_unit.id
|
136
|
-
|
137
|
-
db
|
138
|
-
|
136
|
+
bulk_upsert_image_cache_fast(
|
137
|
+
db=db,
|
138
|
+
list_upsert_objects=[
|
139
|
+
dict(
|
139
140
|
workflowtask_id=wftask.id,
|
140
141
|
dataset_id=dataset_id,
|
141
142
|
zarr_url=zarr_url,
|
142
143
|
latest_history_unit_id=history_unit_id,
|
143
144
|
)
|
145
|
+
for zarr_url in history_unit.zarr_urls
|
146
|
+
],
|
147
|
+
)
|
148
|
+
|
149
|
+
result, exception = executor.submit(
|
150
|
+
functools.partial(
|
151
|
+
run_single_task,
|
152
|
+
wftask=wftask,
|
153
|
+
command=task.command_non_parallel,
|
154
|
+
root_dir_local=workflow_dir_local,
|
155
|
+
root_dir_remote=workflow_dir_remote,
|
156
|
+
),
|
157
|
+
parameters=function_kwargs,
|
158
|
+
task_type="non_parallel",
|
159
|
+
**executor_options,
|
160
|
+
)
|
161
|
+
|
162
|
+
num_tasks = 1
|
163
|
+
with next(get_sync_db()) as db:
|
164
|
+
if exception is None:
|
165
|
+
db.execute(
|
166
|
+
update(HistoryUnit)
|
167
|
+
.where(HistoryUnit.id == history_unit_id)
|
168
|
+
.values(status=XXXStatus.DONE)
|
169
|
+
)
|
170
|
+
db.commit()
|
171
|
+
if result is None:
|
172
|
+
return (TaskOutput(), num_tasks, {})
|
173
|
+
else:
|
174
|
+
return (_cast_and_validate_TaskOutput(result), num_tasks, {})
|
175
|
+
else:
|
176
|
+
db.execute(
|
177
|
+
update(HistoryUnit)
|
178
|
+
.where(HistoryUnit.id == history_unit_id)
|
179
|
+
.values(status=XXXStatus.FAILED)
|
144
180
|
)
|
181
|
+
db.commit()
|
182
|
+
return (TaskOutput(), num_tasks, {0: exception})
|
183
|
+
|
184
|
+
|
185
|
+
def run_v2_task_converter_non_parallel(
|
186
|
+
*,
|
187
|
+
zarr_dir: str,
|
188
|
+
task: TaskV2,
|
189
|
+
wftask: WorkflowTaskV2,
|
190
|
+
workflow_dir_local: Path,
|
191
|
+
workflow_dir_remote: Optional[Path] = None,
|
192
|
+
executor: BaseRunner,
|
193
|
+
submit_setup_call: callable = no_op_submit_setup_call,
|
194
|
+
dataset_id: int,
|
195
|
+
history_run_id: int,
|
196
|
+
) -> tuple[TaskOutput, int, dict[int, BaseException]]:
|
197
|
+
"""
|
198
|
+
This runs server-side (see `executor` argument)
|
199
|
+
"""
|
200
|
+
|
201
|
+
if workflow_dir_remote is None:
|
202
|
+
workflow_dir_remote = workflow_dir_local
|
203
|
+
logging.warning(
|
204
|
+
"In `run_single_task`, workflow_dir_remote=None. Is this right?"
|
205
|
+
)
|
206
|
+
workflow_dir_remote = workflow_dir_local
|
207
|
+
|
208
|
+
executor_options = submit_setup_call(
|
209
|
+
wftask=wftask,
|
210
|
+
root_dir_local=workflow_dir_local,
|
211
|
+
root_dir_remote=workflow_dir_remote,
|
212
|
+
which_type="non_parallel",
|
213
|
+
)
|
214
|
+
|
215
|
+
function_kwargs = {
|
216
|
+
"zarr_dir": zarr_dir,
|
217
|
+
_COMPONENT_KEY_: _index_to_component(0),
|
218
|
+
**(wftask.args_non_parallel or {}),
|
219
|
+
}
|
220
|
+
|
221
|
+
# Database History operations
|
222
|
+
with next(get_sync_db()) as db:
|
223
|
+
history_unit = HistoryUnit(
|
224
|
+
history_run_id=history_run_id,
|
225
|
+
status=XXXStatus.SUBMITTED,
|
226
|
+
logfile=None, # FIXME
|
227
|
+
zarr_urls=[],
|
228
|
+
)
|
229
|
+
db.add(history_unit)
|
145
230
|
db.commit()
|
231
|
+
db.refresh(history_unit)
|
232
|
+
history_unit_id = history_unit.id
|
146
233
|
|
147
234
|
result, exception = executor.submit(
|
148
235
|
functools.partial(
|
@@ -152,6 +239,7 @@ def run_v2_task_non_parallel(
|
|
152
239
|
root_dir_local=workflow_dir_local,
|
153
240
|
root_dir_remote=workflow_dir_remote,
|
154
241
|
),
|
242
|
+
task_type="converter_non_parallel",
|
155
243
|
parameters=function_kwargs,
|
156
244
|
**executor_options,
|
157
245
|
)
|
@@ -191,7 +279,6 @@ def run_v2_task_parallel(
|
|
191
279
|
dataset_id: int,
|
192
280
|
history_run_id: int,
|
193
281
|
) -> tuple[TaskOutput, int, dict[int, BaseException]]:
|
194
|
-
|
195
282
|
if len(images) == 0:
|
196
283
|
# FIXME: Do something with history units/images?
|
197
284
|
return (TaskOutput(), 0, {})
|
@@ -205,39 +292,45 @@ def run_v2_task_parallel(
|
|
205
292
|
which_type="parallel",
|
206
293
|
)
|
207
294
|
|
208
|
-
list_function_kwargs = [
|
209
|
-
|
295
|
+
list_function_kwargs = [
|
296
|
+
{
|
297
|
+
"zarr_url": image["zarr_url"],
|
298
|
+
_COMPONENT_KEY_: _index_to_component(ind),
|
299
|
+
**(wftask.args_parallel or {}),
|
300
|
+
}
|
301
|
+
for ind, image in enumerate(images)
|
302
|
+
]
|
303
|
+
history_units = [
|
304
|
+
HistoryUnit(
|
305
|
+
history_run_id=history_run_id,
|
306
|
+
status=XXXStatus.SUBMITTED,
|
307
|
+
logfile=None, # FIXME
|
308
|
+
zarr_urls=[image["zarr_url"]],
|
309
|
+
)
|
310
|
+
for image in images
|
311
|
+
]
|
312
|
+
|
210
313
|
with next(get_sync_db()) as db:
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
**(wftask.args_parallel or {}),
|
216
|
-
),
|
217
|
-
)
|
218
|
-
list_function_kwargs[-1][_COMPONENT_KEY_] = _index_to_component(
|
219
|
-
ind
|
220
|
-
)
|
221
|
-
history_unit = HistoryUnit(
|
222
|
-
history_run_id=history_run_id,
|
223
|
-
status=XXXStatus.SUBMITTED,
|
224
|
-
logfile=None, # FIXME
|
225
|
-
zarr_urls=[image["zarr_url"]],
|
226
|
-
)
|
227
|
-
# FIXME: this should be a bulk operation
|
228
|
-
db.add(history_unit)
|
229
|
-
db.commit()
|
314
|
+
db.add_all(history_units)
|
315
|
+
db.commit()
|
316
|
+
|
317
|
+
for history_unit in history_units:
|
230
318
|
db.refresh(history_unit)
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
319
|
+
history_unit_ids = [history_unit.id for history_unit in history_units]
|
320
|
+
|
321
|
+
history_image_caches = [
|
322
|
+
dict(
|
323
|
+
workflowtask_id=wftask.id,
|
324
|
+
dataset_id=dataset_id,
|
325
|
+
zarr_url=history_unit.zarr_urls[0],
|
326
|
+
latest_history_unit_id=history_unit.id,
|
238
327
|
)
|
239
|
-
|
240
|
-
|
328
|
+
for history_unit in history_units
|
329
|
+
]
|
330
|
+
|
331
|
+
bulk_upsert_image_cache_fast(
|
332
|
+
db=db, list_upsert_objects=history_image_caches
|
333
|
+
)
|
241
334
|
|
242
335
|
results, exceptions = executor.multisubmit(
|
243
336
|
functools.partial(
|
@@ -248,6 +341,7 @@ def run_v2_task_parallel(
|
|
248
341
|
root_dir_remote=workflow_dir_remote,
|
249
342
|
),
|
250
343
|
list_parameters=list_function_kwargs,
|
344
|
+
task_type="parallel",
|
251
345
|
**executor_options,
|
252
346
|
)
|
253
347
|
|
@@ -300,7 +394,6 @@ def run_v2_task_compound(
|
|
300
394
|
dataset_id: int,
|
301
395
|
history_run_id: int,
|
302
396
|
) -> tuple[TaskOutput, int, dict[int, BaseException]]:
|
303
|
-
|
304
397
|
executor_options_init = submit_setup_call(
|
305
398
|
wftask=wftask,
|
306
399
|
root_dir_local=workflow_dir_local,
|
@@ -315,12 +408,12 @@ def run_v2_task_compound(
|
|
315
408
|
)
|
316
409
|
|
317
410
|
# 3/A: non-parallel init task
|
318
|
-
function_kwargs =
|
319
|
-
zarr_urls
|
320
|
-
zarr_dir
|
411
|
+
function_kwargs = {
|
412
|
+
"zarr_urls": [image["zarr_url"] for image in images],
|
413
|
+
"zarr_dir": zarr_dir,
|
414
|
+
_COMPONENT_KEY_: f"init_{_index_to_component(0)}",
|
321
415
|
**(wftask.args_non_parallel or {}),
|
322
|
-
|
323
|
-
function_kwargs[_COMPONENT_KEY_] = f"init_{_index_to_component(0)}"
|
416
|
+
}
|
324
417
|
|
325
418
|
# Create database History entries
|
326
419
|
input_image_zarr_urls = function_kwargs["zarr_urls"]
|
@@ -337,17 +430,171 @@ def run_v2_task_compound(
|
|
337
430
|
db.refresh(history_unit)
|
338
431
|
history_unit_id = history_unit.id
|
339
432
|
# Create one `HistoryImageCache` for each input image
|
340
|
-
|
341
|
-
db
|
342
|
-
|
433
|
+
bulk_upsert_image_cache_fast(
|
434
|
+
db=db,
|
435
|
+
list_upsert_objects=[
|
436
|
+
dict(
|
343
437
|
workflowtask_id=wftask.id,
|
344
438
|
dataset_id=dataset_id,
|
345
439
|
zarr_url=zarr_url,
|
346
440
|
latest_history_unit_id=history_unit_id,
|
347
441
|
)
|
442
|
+
for zarr_url in input_image_zarr_urls
|
443
|
+
],
|
444
|
+
)
|
445
|
+
|
446
|
+
result, exception = executor.submit(
|
447
|
+
functools.partial(
|
448
|
+
run_single_task,
|
449
|
+
wftask=wftask,
|
450
|
+
command=task.command_non_parallel,
|
451
|
+
root_dir_local=workflow_dir_local,
|
452
|
+
root_dir_remote=workflow_dir_remote,
|
453
|
+
),
|
454
|
+
parameters=function_kwargs,
|
455
|
+
task_type="compound",
|
456
|
+
**executor_options_init,
|
457
|
+
)
|
458
|
+
|
459
|
+
num_tasks = 1
|
460
|
+
if exception is None:
|
461
|
+
if result is None:
|
462
|
+
init_task_output = InitTaskOutput()
|
463
|
+
else:
|
464
|
+
init_task_output = _cast_and_validate_InitTaskOutput(result)
|
465
|
+
else:
|
466
|
+
with next(get_sync_db()) as db:
|
467
|
+
db.execute(
|
468
|
+
update(HistoryUnit)
|
469
|
+
.where(HistoryUnit.id == history_unit_id)
|
470
|
+
.values(status=XXXStatus.FAILED)
|
471
|
+
)
|
472
|
+
db.commit()
|
473
|
+
return (TaskOutput(), num_tasks, {0: exception})
|
474
|
+
|
475
|
+
parallelization_list = init_task_output.parallelization_list
|
476
|
+
parallelization_list = deduplicate_list(parallelization_list)
|
477
|
+
|
478
|
+
num_tasks = 1 + len(parallelization_list)
|
479
|
+
|
480
|
+
# 3/B: parallel part of a compound task
|
481
|
+
_check_parallelization_list_size(parallelization_list)
|
482
|
+
|
483
|
+
if len(parallelization_list) == 0:
|
484
|
+
with next(get_sync_db()) as db:
|
485
|
+
db.execute(
|
486
|
+
update(HistoryUnit)
|
487
|
+
.where(HistoryUnit.id == history_unit_id)
|
488
|
+
.values(status=XXXStatus.DONE)
|
489
|
+
)
|
490
|
+
db.commit()
|
491
|
+
return (TaskOutput(), 0, {})
|
492
|
+
|
493
|
+
list_function_kwargs = [
|
494
|
+
{
|
495
|
+
"zarr_url": parallelization_item.zarr_url,
|
496
|
+
"init_args": parallelization_item.init_args,
|
497
|
+
_COMPONENT_KEY_: f"compute_{_index_to_component(ind)}",
|
498
|
+
**(wftask.args_parallel or {}),
|
499
|
+
}
|
500
|
+
for ind, parallelization_item in enumerate(parallelization_list)
|
501
|
+
]
|
502
|
+
|
503
|
+
results, exceptions = executor.multisubmit(
|
504
|
+
functools.partial(
|
505
|
+
run_single_task,
|
506
|
+
wftask=wftask,
|
507
|
+
command=task.command_parallel,
|
508
|
+
root_dir_local=workflow_dir_local,
|
509
|
+
root_dir_remote=workflow_dir_remote,
|
510
|
+
),
|
511
|
+
list_parameters=list_function_kwargs,
|
512
|
+
task_type="compound",
|
513
|
+
**executor_options_compute,
|
514
|
+
)
|
515
|
+
|
516
|
+
outputs = []
|
517
|
+
failure = False
|
518
|
+
for ind in range(len(list_function_kwargs)):
|
519
|
+
if ind in results.keys():
|
520
|
+
result = results[ind]
|
521
|
+
if result is None:
|
522
|
+
output = TaskOutput()
|
523
|
+
else:
|
524
|
+
output = _cast_and_validate_TaskOutput(result)
|
525
|
+
outputs.append(output)
|
526
|
+
|
527
|
+
elif ind in exceptions.keys():
|
528
|
+
print(f"Bad: {exceptions[ind]}")
|
529
|
+
failure = True
|
530
|
+
else:
|
531
|
+
print("VERY BAD - should have not reached this point")
|
532
|
+
|
533
|
+
with next(get_sync_db()) as db:
|
534
|
+
if failure:
|
535
|
+
db.execute(
|
536
|
+
update(HistoryUnit)
|
537
|
+
.where(HistoryUnit.id == history_unit_id)
|
538
|
+
.values(status=XXXStatus.FAILED)
|
539
|
+
)
|
540
|
+
else:
|
541
|
+
db.execute(
|
542
|
+
update(HistoryUnit)
|
543
|
+
.where(HistoryUnit.id == history_unit_id)
|
544
|
+
.values(status=XXXStatus.DONE)
|
348
545
|
)
|
349
546
|
db.commit()
|
350
547
|
|
548
|
+
merged_output = merge_outputs(outputs)
|
549
|
+
return (merged_output, num_tasks, exceptions)
|
550
|
+
|
551
|
+
|
552
|
+
def run_v2_task_converter_compound(
|
553
|
+
*,
|
554
|
+
zarr_dir: str,
|
555
|
+
task: TaskV2,
|
556
|
+
wftask: WorkflowTaskV2,
|
557
|
+
executor: BaseRunner,
|
558
|
+
workflow_dir_local: Path,
|
559
|
+
workflow_dir_remote: Optional[Path] = None,
|
560
|
+
submit_setup_call: callable = no_op_submit_setup_call,
|
561
|
+
dataset_id: int,
|
562
|
+
history_run_id: int,
|
563
|
+
) -> tuple[TaskOutput, int, dict[int, BaseException]]:
|
564
|
+
executor_options_init = submit_setup_call(
|
565
|
+
wftask=wftask,
|
566
|
+
root_dir_local=workflow_dir_local,
|
567
|
+
root_dir_remote=workflow_dir_remote,
|
568
|
+
which_type="non_parallel",
|
569
|
+
)
|
570
|
+
executor_options_compute = submit_setup_call(
|
571
|
+
wftask=wftask,
|
572
|
+
root_dir_local=workflow_dir_local,
|
573
|
+
root_dir_remote=workflow_dir_remote,
|
574
|
+
which_type="parallel",
|
575
|
+
)
|
576
|
+
|
577
|
+
# 3/A: non-parallel init task
|
578
|
+
function_kwargs = {
|
579
|
+
"zarr_dir": zarr_dir,
|
580
|
+
_COMPONENT_KEY_: f"init_{_index_to_component(0)}",
|
581
|
+
**(wftask.args_non_parallel or {}),
|
582
|
+
}
|
583
|
+
|
584
|
+
# Create database History entries
|
585
|
+
with next(get_sync_db()) as db:
|
586
|
+
# Create a single `HistoryUnit` for the whole compound task
|
587
|
+
history_unit = HistoryUnit(
|
588
|
+
history_run_id=history_run_id,
|
589
|
+
status=XXXStatus.SUBMITTED,
|
590
|
+
logfile=None, # FIXME
|
591
|
+
zarr_urls=[],
|
592
|
+
)
|
593
|
+
db.add(history_unit)
|
594
|
+
db.commit()
|
595
|
+
db.refresh(history_unit)
|
596
|
+
history_unit_id = history_unit.id
|
597
|
+
|
351
598
|
result, exception = executor.submit(
|
352
599
|
functools.partial(
|
353
600
|
run_single_task,
|
@@ -357,6 +604,7 @@ def run_v2_task_compound(
|
|
357
604
|
root_dir_remote=workflow_dir_remote,
|
358
605
|
),
|
359
606
|
parameters=function_kwargs,
|
607
|
+
task_type="converter_compound",
|
360
608
|
**executor_options_init,
|
361
609
|
)
|
362
610
|
|
@@ -394,18 +642,15 @@ def run_v2_task_compound(
|
|
394
642
|
db.commit()
|
395
643
|
return (TaskOutput(), 0, {})
|
396
644
|
|
397
|
-
list_function_kwargs = [
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
list_function_kwargs[-1][
|
407
|
-
_COMPONENT_KEY_
|
408
|
-
] = f"compute_{_index_to_component(ind)}"
|
645
|
+
list_function_kwargs = [
|
646
|
+
{
|
647
|
+
"zarr_url": parallelization_item.zarr_url,
|
648
|
+
"init_args": parallelization_item.init_args,
|
649
|
+
_COMPONENT_KEY_: f"compute_{_index_to_component(ind)}",
|
650
|
+
**(wftask.args_parallel or {}),
|
651
|
+
}
|
652
|
+
for ind, parallelization_item in enumerate(parallelization_list)
|
653
|
+
]
|
409
654
|
|
410
655
|
results, exceptions = executor.multisubmit(
|
411
656
|
functools.partial(
|
@@ -416,7 +661,7 @@ def run_v2_task_compound(
|
|
416
661
|
root_dir_remote=workflow_dir_remote,
|
417
662
|
),
|
418
663
|
list_parameters=list_function_kwargs,
|
419
|
-
|
664
|
+
task_type="converter_compound",
|
420
665
|
**executor_options_compute,
|
421
666
|
)
|
422
667
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
from typing import Any
|
2
|
+
from typing import Literal
|
2
3
|
from typing import Optional
|
3
4
|
|
4
5
|
from pydantic import BaseModel
|
@@ -56,6 +57,16 @@ class TaskManifestV2(BaseModel):
|
|
56
57
|
modality: Optional[str] = None
|
57
58
|
tags: list[str] = Field(default_factory=list)
|
58
59
|
|
60
|
+
type: Optional[
|
61
|
+
Literal[
|
62
|
+
"compound",
|
63
|
+
"converter_compound",
|
64
|
+
"non_parallel",
|
65
|
+
"converter_non_parallel",
|
66
|
+
"parallel",
|
67
|
+
]
|
68
|
+
] = None
|
69
|
+
|
59
70
|
@model_validator(mode="after")
|
60
71
|
def validate_executable_args_meta(self):
|
61
72
|
executable_non_parallel = self.executable_non_parallel
|
@@ -13,8 +13,20 @@ from .._validators import cant_set_none
|
|
13
13
|
from fractal_server.app.schemas._validators import NonEmptyString
|
14
14
|
from fractal_server.app.schemas._validators import val_unique_list
|
15
15
|
from fractal_server.app.schemas._validators import valdict_keys
|
16
|
+
from fractal_server.logger import set_logger
|
16
17
|
from fractal_server.string_tools import validate_cmd
|
17
18
|
|
19
|
+
TaskTypeType = Literal[
|
20
|
+
"compound",
|
21
|
+
"converter_compound",
|
22
|
+
"non_parallel",
|
23
|
+
"converter_non_parallel",
|
24
|
+
"parallel",
|
25
|
+
]
|
26
|
+
|
27
|
+
|
28
|
+
logger = set_logger(__name__)
|
29
|
+
|
18
30
|
|
19
31
|
class TaskCreateV2(BaseModel):
|
20
32
|
model_config = ConfigDict(extra="forbid")
|
@@ -41,6 +53,8 @@ class TaskCreateV2(BaseModel):
|
|
41
53
|
tags: list[NonEmptyString] = Field(default_factory=list)
|
42
54
|
authors: Optional[NonEmptyString] = None
|
43
55
|
|
56
|
+
type: Optional[TaskTypeType] = None
|
57
|
+
|
44
58
|
# Validators
|
45
59
|
|
46
60
|
@field_validator(
|
@@ -69,6 +83,23 @@ class TaskCreateV2(BaseModel):
|
|
69
83
|
|
70
84
|
return self
|
71
85
|
|
86
|
+
@model_validator(mode="after")
|
87
|
+
def set_task_type(self):
|
88
|
+
if self.type is None:
|
89
|
+
logger.warning(
|
90
|
+
f"Task type is not set for task '{self.name}', "
|
91
|
+
"which will be deprecated in a future version. "
|
92
|
+
"Please move to `fractal-task-tools`."
|
93
|
+
)
|
94
|
+
if self.command_non_parallel is None:
|
95
|
+
self.type = "parallel"
|
96
|
+
elif self.command_parallel is None:
|
97
|
+
self.type = "non_parallel"
|
98
|
+
else:
|
99
|
+
self.type = "compound"
|
100
|
+
|
101
|
+
return self
|
102
|
+
|
72
103
|
_meta_non_parallel = field_validator("meta_non_parallel")(
|
73
104
|
classmethod(valdict_keys("meta_non_parallel"))
|
74
105
|
)
|
@@ -127,6 +158,8 @@ class TaskReadV2(BaseModel):
|
|
127
158
|
authors: Optional[str] = None
|
128
159
|
tags: list[str]
|
129
160
|
|
161
|
+
type: Optional[TaskTypeType] = None
|
162
|
+
|
130
163
|
|
131
164
|
class TaskUpdateV2(BaseModel):
|
132
165
|
model_config = ConfigDict(extra="forbid")
|
@@ -16,6 +16,7 @@ from .task import TaskExportV2
|
|
16
16
|
from .task import TaskImportV2
|
17
17
|
from .task import TaskImportV2Legacy
|
18
18
|
from .task import TaskReadV2
|
19
|
+
from .task import TaskTypeType
|
19
20
|
|
20
21
|
RESERVED_ARGUMENTS = {"zarr_dir", "zarr_url", "zarr_urls", "init_args"}
|
21
22
|
|
@@ -113,7 +114,7 @@ class WorkflowTaskReadV2(BaseModel):
|
|
113
114
|
|
114
115
|
type_filters: dict[str, bool]
|
115
116
|
|
116
|
-
task_type:
|
117
|
+
task_type: TaskTypeType
|
117
118
|
task_id: int
|
118
119
|
task: TaskReadV2
|
119
120
|
|
@@ -5,15 +5,6 @@ from fractal_server.app.models.v2 import TaskV2
|
|
5
5
|
from fractal_server.app.schemas.v2 import TaskCreateV2
|
6
6
|
|
7
7
|
|
8
|
-
def _get_task_type(task: TaskCreateV2) -> str:
|
9
|
-
if task.command_non_parallel is None:
|
10
|
-
return "parallel"
|
11
|
-
elif task.command_parallel is None:
|
12
|
-
return "non_parallel"
|
13
|
-
else:
|
14
|
-
return "compound"
|
15
|
-
|
16
|
-
|
17
8
|
def create_db_tasks_and_update_task_group(
|
18
9
|
*,
|
19
10
|
task_group_id: int,
|
@@ -31,13 +22,7 @@ def create_db_tasks_and_update_task_group(
|
|
31
22
|
Returns:
|
32
23
|
Updated `TaskGroupV2` object.
|
33
24
|
"""
|
34
|
-
actual_task_list = [
|
35
|
-
TaskV2(
|
36
|
-
**task.model_dump(),
|
37
|
-
type=_get_task_type(task),
|
38
|
-
)
|
39
|
-
for task in task_list
|
40
|
-
]
|
25
|
+
actual_task_list = [TaskV2(**task.model_dump()) for task in task_list]
|
41
26
|
task_group = db.get(TaskGroupV2, task_group_id)
|
42
27
|
task_group.task_list = actual_task_list
|
43
28
|
db.add(task_group)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
fractal_server/__init__.py,sha256=
|
1
|
+
fractal_server/__init__.py,sha256=1JU9zoVeJhUgQtGc2wlEGMoG-HAWT8I6UZKOOCDkSGA,25
|
2
2
|
fractal_server/__main__.py,sha256=rkM8xjY1KeS3l63irB8yCrlVobR-73uDapC4wvrIlxI,6957
|
3
3
|
fractal_server/alembic.ini,sha256=MWwi7GzjzawI9cCAK1LW7NxIBQDUqD12-ptJoq5JpP0,3153
|
4
4
|
fractal_server/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -44,14 +44,14 @@ fractal_server/app/routes/api/v2/job.py,sha256=m89FTh9Px25oXCeWj2k2NdGWQaO2oxMh-
|
|
44
44
|
fractal_server/app/routes/api/v2/project.py,sha256=hMvL9QLPUcAAiPGy6ta2LBLTVRozJsfvBPl5D06_MHg,6666
|
45
45
|
fractal_server/app/routes/api/v2/status_legacy.py,sha256=sJLHGGHI9so_Sa4-8JuhMGBPeE6n4K2DmDuiw6IB4XY,6317
|
46
46
|
fractal_server/app/routes/api/v2/submit.py,sha256=K4OjcSg476JXIeeMUaYdTDk8Qpj5IO5UULvfErI7Y5Y,8624
|
47
|
-
fractal_server/app/routes/api/v2/task.py,sha256=
|
47
|
+
fractal_server/app/routes/api/v2/task.py,sha256=O7pquZhXIS4lRs5XqHvstiwe8BiCuS-B3ZKJI1g6EJU,6985
|
48
48
|
fractal_server/app/routes/api/v2/task_collection.py,sha256=IDNF6sjDuU37HIQ0TuQA-TZIuf7nfHAQXUUNmkrlhLM,12706
|
49
49
|
fractal_server/app/routes/api/v2/task_collection_custom.py,sha256=cctW61-C2QYF2KXluS15lLhZJS_kt30Ca6UGLFO32z0,6207
|
50
50
|
fractal_server/app/routes/api/v2/task_group.py,sha256=j3zDvVZizB7NWEgVgZU42JCXETkaVkk2ImJPr0jS7BQ,8164
|
51
51
|
fractal_server/app/routes/api/v2/task_group_lifecycle.py,sha256=3o9bCC8ubMwffQPPaxQZy-CjH9IB2RkIReIecI6L2_w,9300
|
52
52
|
fractal_server/app/routes/api/v2/workflow.py,sha256=U3iZX5IiFAJ20-R8IjlYToOdm9gXsmtr1lW7ASEH9P8,11689
|
53
53
|
fractal_server/app/routes/api/v2/workflow_import.py,sha256=INmnhlMEBJp-vHPR0f940DANPmIidts3OfcooeM_aNA,11205
|
54
|
-
fractal_server/app/routes/api/v2/workflowtask.py,sha256=
|
54
|
+
fractal_server/app/routes/api/v2/workflowtask.py,sha256=7_syX2EO7ibF6Xkm7HBPhsUYq6aYnKNeC5iSaafQhG4,11342
|
55
55
|
fractal_server/app/routes/auth/__init__.py,sha256=fao6CS0WiAjHDTvBzgBVV_bSXFpEAeDBF6Z6q7rRkPc,1658
|
56
56
|
fractal_server/app/routes/auth/_aux_auth.py,sha256=UZgauY0V6mSqjte_sYI1cBl2h8bcbLaeWzgpl1jdJlk,4883
|
57
57
|
fractal_server/app/routes/auth/current_user.py,sha256=FUegTahlxT3BdPVCQYir0-ogg2YAaZ1DYuLcE_5NC9Y,5906
|
@@ -71,11 +71,11 @@ fractal_server/app/runner/components.py,sha256=ZF8ct_Ky5k8IAcrmpYOZ-bc6OBgdELEig
|
|
71
71
|
fractal_server/app/runner/compress_folder.py,sha256=HSc1tv7x2DBjBoXwugZlC79rm9GNBIWtQKK9yWn5ZBI,3991
|
72
72
|
fractal_server/app/runner/exceptions.py,sha256=_qZ_t8O4umAdJ1ikockiF5rDJuxnEskrGrLjZcnQl7A,4159
|
73
73
|
fractal_server/app/runner/executors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
74
|
-
fractal_server/app/runner/executors/base_runner.py,sha256=
|
74
|
+
fractal_server/app/runner/executors/base_runner.py,sha256=v-uUHEvJgRnipTEvU9AmIMkYbkkazqkjm4iAF1GXHEM,4562
|
75
75
|
fractal_server/app/runner/executors/local/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
76
76
|
fractal_server/app/runner/executors/local/_local_config.py,sha256=8dyg2Gh8L2FlG_jJRYLMkcMgVHGEY2w7DME9aaKXFFo,3688
|
77
77
|
fractal_server/app/runner/executors/local/_submit_setup.py,sha256=pDc9Q6axXL8_5JAV0byXzGOLOB0bZF88_L9LZykOgwM,1220
|
78
|
-
fractal_server/app/runner/executors/local/runner.py,sha256=
|
78
|
+
fractal_server/app/runner/executors/local/runner.py,sha256=LNql8q6M-Cn_hEV4IMkNP57XFPQJ6eaVd0YIDKJLk60,5621
|
79
79
|
fractal_server/app/runner/executors/slurm_common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
80
80
|
fractal_server/app/runner/executors/slurm_common/_batching.py,sha256=ZY020JZlDS5mfpgpWTChQkyHU7iLE5kx2HVd57_C6XA,8850
|
81
81
|
fractal_server/app/runner/executors/slurm_common/_job_states.py,sha256=nuV-Zba38kDrRESOVB3gaGbrSPZc4q7YGichQaeqTW0,238
|
@@ -91,7 +91,7 @@ fractal_server/app/runner/executors/slurm_ssh/executor.py,sha256=JW6zguEy9XsHebS
|
|
91
91
|
fractal_server/app/runner/executors/slurm_sudo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
92
92
|
fractal_server/app/runner/executors/slurm_sudo/_check_jobs_status.py,sha256=eZd9lxbObsqc1M3B96IGMJ-1oC0jo8lBOX4Nto97VvE,2036
|
93
93
|
fractal_server/app/runner/executors/slurm_sudo/_subprocess_run_as_user.py,sha256=O1bNg1DiSDJmQE0RmOk2Ii47DagiXp5ryd0R6KxO2OM,3177
|
94
|
-
fractal_server/app/runner/executors/slurm_sudo/runner.py,sha256=
|
94
|
+
fractal_server/app/runner/executors/slurm_sudo/runner.py,sha256=niDcezFNQ7awfv_7nqABmVhG3oLIf8Z7BwlLxM-chZo,21833
|
95
95
|
fractal_server/app/runner/extract_archive.py,sha256=tLpjDrX47OjTNhhoWvm6iNukg8KoieWyTb7ZfvE9eWU,2483
|
96
96
|
fractal_server/app/runner/filenames.py,sha256=lPnxKHtdRizr6FqG3zOdjDPyWA7GoaJGTtiuJV0gA8E,70
|
97
97
|
fractal_server/app/runner/run_subprocess.py,sha256=c3JbYXq3hX2aaflQU19qJ5Xs6J6oXGNvnTEoAfv2bxc,959
|
@@ -99,13 +99,14 @@ fractal_server/app/runner/set_start_and_last_task_index.py,sha256=-q4zVybAj8ek2X
|
|
99
99
|
fractal_server/app/runner/shutdown.py,sha256=9pfSKHDNdIcm0eY-opgRTi7y0HmvfPmYiu9JR6Idark,2082
|
100
100
|
fractal_server/app/runner/task_files.py,sha256=5enzBqiQct1AUGwrGX-rxFCxnhW3SPYIUylMYwyVfrE,2482
|
101
101
|
fractal_server/app/runner/v2/__init__.py,sha256=ro5C4ro7gXuV-H7p8SrV90vBsS52IfukWn-b2FxJ9F4,13293
|
102
|
+
fractal_server/app/runner/v2/_db_tools.py,sha256=_8Mt4WbItew8EEtRwHdZidfIm4MvfM49FsMPf2JYol4,1361
|
102
103
|
fractal_server/app/runner/v2/_local.py,sha256=Zas2RS_f9mfdkXszBpzISHylLX1bX8pFuoLA1fHLFqQ,2945
|
103
104
|
fractal_server/app/runner/v2/_slurm_ssh.py,sha256=5w_lwQzySx-R3kVg2Bf-21n5JpWjJAgMtYP2BROvWJo,3227
|
104
105
|
fractal_server/app/runner/v2/_slurm_sudo.py,sha256=CzWUeC6at_Sj-wU1myjA68ZRKMiLZYBTLv9I9odUxBU,2914
|
105
106
|
fractal_server/app/runner/v2/deduplicate_list.py,sha256=IVTE4abBU1bUprFTkxrTfYKnvkNTanWQ-KWh_etiT08,645
|
106
107
|
fractal_server/app/runner/v2/merge_outputs.py,sha256=D1L4Taieq9i71SPQyNc1kMokgHh-sV_MqF3bv7QMDBc,907
|
107
|
-
fractal_server/app/runner/v2/runner.py,sha256=
|
108
|
-
fractal_server/app/runner/v2/runner_functions.py,sha256=
|
108
|
+
fractal_server/app/runner/v2/runner.py,sha256=3M6xGeccY_AZKua305MgWLuCuhkACoqwKgTV4vuNguk,15534
|
109
|
+
fractal_server/app/runner/v2/runner_functions.py,sha256=tztuEF7m0iE-ca5n6Vb-l1dDUldQXAGxZkD1OTL6I6w,21889
|
109
110
|
fractal_server/app/runner/v2/runner_functions_low_level.py,sha256=dvvRK7od8iQ8vdPf80uGUxs3i5i0buGjCodBxSjZ7PQ,3671
|
110
111
|
fractal_server/app/runner/v2/task_interface.py,sha256=e1GGQSYd0MyBj1EZvEVzqv-HpVE4YffXOq82WLrCaOc,1866
|
111
112
|
fractal_server/app/runner/versions.py,sha256=dSaPRWqmFPHjg20kTCHmi_dmGNcCETflDtDLronNanU,852
|
@@ -120,14 +121,14 @@ fractal_server/app/schemas/v2/accounting.py,sha256=Wylt7uWTiDIFlHJOh4XEtYitk2FjF
|
|
120
121
|
fractal_server/app/schemas/v2/dataset.py,sha256=9yc-tte70yPPk4CSfy2imykYVbCW8-23K499pi9z2e0,5206
|
121
122
|
fractal_server/app/schemas/v2/dumps.py,sha256=2GUjoqeblUvrSoojBz5odoUUf53IABtbY_5GvFZoMVc,1782
|
122
123
|
fractal_server/app/schemas/v2/job.py,sha256=KhxQOfncpE_SAu7Wed8CXS2G6onh0v875GkotBvKBTY,4304
|
123
|
-
fractal_server/app/schemas/v2/manifest.py,sha256=
|
124
|
+
fractal_server/app/schemas/v2/manifest.py,sha256=8mmB0QwxEgAeGgwKD_fT-o-wFy7lb6HxNXbp17IJqNY,7281
|
124
125
|
fractal_server/app/schemas/v2/project.py,sha256=ulgCmUnX0w-0jrSjVYIT7sxeK95CSNGh2msXydhsgYI,885
|
125
126
|
fractal_server/app/schemas/v2/status.py,sha256=SQaUpQkjFq5c5k5J4rOjNhuQaDOEg8lksPhkKmPU5VU,332
|
126
|
-
fractal_server/app/schemas/v2/task.py,sha256
|
127
|
+
fractal_server/app/schemas/v2/task.py,sha256=-oBE5-2tSUd6u75duAcaFyEFGk3MsQzrdBr_mFldOBc,6627
|
127
128
|
fractal_server/app/schemas/v2/task_collection.py,sha256=dLu4sy-su5k5vDJqCZdJMW8mLT5TX2SV60l_RAvKhwY,5930
|
128
129
|
fractal_server/app/schemas/v2/task_group.py,sha256=A3SFHNHLKPJyrnDz-wbnQvycetafKltp6UsH1u-euwA,3850
|
129
130
|
fractal_server/app/schemas/v2/workflow.py,sha256=ZpM43zTMyLRnEUtkbr_J5DYP00NwjItaC8gweB7GGAA,2172
|
130
|
-
fractal_server/app/schemas/v2/workflowtask.py,sha256=
|
131
|
+
fractal_server/app/schemas/v2/workflowtask.py,sha256=qOzpsWQ7QUNey-rK9hPB3PKFrRxXrMvcRzqZxRuEYAI,8096
|
131
132
|
fractal_server/app/security/__init__.py,sha256=e2cveg5hQpieGD3bSPd5GTOMthvJ-HXH3buSb9WVfEU,14096
|
132
133
|
fractal_server/app/security/signup_email.py,sha256=Xd6QYxcdmg0PHpDwmUE8XQmPcOj3Xjy5oROcIMhmltM,1472
|
133
134
|
fractal_server/app/user_settings.py,sha256=OP1yiYKtPadxwM51_Q0hdPk3z90TCN4z1BLpQsXyWiU,1316
|
@@ -198,15 +199,15 @@ fractal_server/tasks/v2/templates/4_pip_show.sh,sha256=qm1vPy6AkKhWDjCJGXS8LqCLY
|
|
198
199
|
fractal_server/tasks/v2/templates/5_get_venv_size_and_file_number.sh,sha256=q-6ZUvA6w6FDVEoSd9O63LaJ9tKZc7qAFH72SGPrd_k,284
|
199
200
|
fractal_server/tasks/v2/templates/6_pip_install_from_freeze.sh,sha256=A2y8RngEjAcRhG-_owA6P7tAdrS_AszFuGXnaeMV8u0,1122
|
200
201
|
fractal_server/tasks/v2/utils_background.py,sha256=W_RvihI1aiYPJNsPo8z4wKuA_bPs0UT2huzLihRpjU4,4248
|
201
|
-
fractal_server/tasks/v2/utils_database.py,sha256=
|
202
|
+
fractal_server/tasks/v2/utils_database.py,sha256=h4Pa-JxcVk7WA4_Pz8CxFT9sX3sA43p2rVRg7FVSgvY,967
|
202
203
|
fractal_server/tasks/v2/utils_package_names.py,sha256=RDg__xrvQs4ieeVzmVdMcEh95vGQYrv9Hfal-5EDBM8,2393
|
203
204
|
fractal_server/tasks/v2/utils_python_interpreter.py,sha256=5_wrlrTqXyo1YuLZvAW9hrSoh5MyLOzdPVUlUwM7uDQ,955
|
204
205
|
fractal_server/tasks/v2/utils_templates.py,sha256=Kc_nSzdlV6KIsO0CQSPs1w70zLyENPqJeTQEFiz4bOg,3124
|
205
206
|
fractal_server/urls.py,sha256=QjIKAC1a46bCdiPMu3AlpgFbcv6a4l3ABcd5xz190Og,471
|
206
207
|
fractal_server/utils.py,sha256=PMwrxWFxRTQRl1b9h-NRIbFGPKqpH_hXnkAT3NfZdpY,3571
|
207
208
|
fractal_server/zip_tools.py,sha256=GjDgo_sf6V_DDg6wWeBlZu5zypIxycn_l257p_YVKGc,4876
|
208
|
-
fractal_server-2.14.
|
209
|
-
fractal_server-2.14.
|
210
|
-
fractal_server-2.14.
|
211
|
-
fractal_server-2.14.
|
212
|
-
fractal_server-2.14.
|
209
|
+
fractal_server-2.14.0a7.dist-info/LICENSE,sha256=QKAharUuhxL58kSoLizKJeZE3mTCBnX6ucmz8W0lxlk,1576
|
210
|
+
fractal_server-2.14.0a7.dist-info/METADATA,sha256=-H-lBPwP4S9jTTot-k3weBihbHTA2utPTxzC1z7h8so,4550
|
211
|
+
fractal_server-2.14.0a7.dist-info/WHEEL,sha256=RaoafKOydTQ7I_I3JTrPCg6kUmTgtm4BornzOqyEfJ8,88
|
212
|
+
fractal_server-2.14.0a7.dist-info/entry_points.txt,sha256=8tV2kynvFkjnhbtDnxAqImL6HMVKsopgGfew0DOp5UY,58
|
213
|
+
fractal_server-2.14.0a7.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|