fractal-server 2.6.4__py3-none-any.whl → 2.7.0__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/__main__.py +1 -1
- fractal_server/app/models/linkusergroup.py +11 -0
- fractal_server/app/models/v2/__init__.py +2 -0
- fractal_server/app/models/v2/collection_state.py +1 -0
- fractal_server/app/models/v2/task.py +67 -2
- fractal_server/app/routes/admin/v2/__init__.py +16 -0
- fractal_server/app/routes/admin/{v2.py → v2/job.py} +20 -191
- fractal_server/app/routes/admin/v2/project.py +43 -0
- fractal_server/app/routes/admin/v2/task.py +133 -0
- fractal_server/app/routes/admin/v2/task_group.py +162 -0
- fractal_server/app/routes/api/v1/task_collection.py +4 -4
- fractal_server/app/routes/api/v2/__init__.py +8 -0
- fractal_server/app/routes/api/v2/_aux_functions.py +1 -68
- fractal_server/app/routes/api/v2/_aux_functions_tasks.py +343 -0
- fractal_server/app/routes/api/v2/submit.py +16 -35
- fractal_server/app/routes/api/v2/task.py +85 -110
- fractal_server/app/routes/api/v2/task_collection.py +184 -196
- fractal_server/app/routes/api/v2/task_collection_custom.py +70 -64
- fractal_server/app/routes/api/v2/task_group.py +173 -0
- fractal_server/app/routes/api/v2/workflow.py +39 -102
- fractal_server/app/routes/api/v2/workflow_import.py +360 -0
- fractal_server/app/routes/api/v2/workflowtask.py +4 -8
- fractal_server/app/routes/auth/_aux_auth.py +86 -40
- fractal_server/app/routes/auth/current_user.py +5 -5
- fractal_server/app/routes/auth/group.py +73 -23
- fractal_server/app/routes/auth/router.py +0 -2
- fractal_server/app/routes/auth/users.py +8 -7
- fractal_server/app/runner/executors/slurm/ssh/executor.py +82 -63
- fractal_server/app/runner/v2/__init__.py +13 -7
- fractal_server/app/runner/v2/task_interface.py +4 -9
- fractal_server/app/schemas/user.py +1 -2
- fractal_server/app/schemas/v2/__init__.py +7 -0
- fractal_server/app/schemas/v2/dataset.py +2 -7
- fractal_server/app/schemas/v2/dumps.py +1 -2
- fractal_server/app/schemas/v2/job.py +1 -1
- fractal_server/app/schemas/v2/manifest.py +25 -1
- fractal_server/app/schemas/v2/project.py +1 -1
- fractal_server/app/schemas/v2/task.py +95 -36
- fractal_server/app/schemas/v2/task_collection.py +8 -6
- fractal_server/app/schemas/v2/task_group.py +85 -0
- fractal_server/app/schemas/v2/workflow.py +7 -2
- fractal_server/app/schemas/v2/workflowtask.py +9 -6
- fractal_server/app/security/__init__.py +8 -1
- fractal_server/config.py +8 -28
- fractal_server/data_migrations/2_7_0.py +323 -0
- fractal_server/images/models.py +2 -4
- fractal_server/main.py +1 -1
- fractal_server/migrations/versions/034a469ec2eb_task_groups.py +184 -0
- fractal_server/ssh/_fabric.py +186 -73
- fractal_server/string_tools.py +6 -2
- fractal_server/tasks/utils.py +19 -5
- fractal_server/tasks/v1/_TaskCollectPip.py +1 -1
- fractal_server/tasks/v1/background_operations.py +5 -5
- fractal_server/tasks/v1/get_collection_data.py +2 -2
- fractal_server/tasks/v2/_venv_pip.py +67 -70
- fractal_server/tasks/v2/background_operations.py +180 -69
- fractal_server/tasks/v2/background_operations_ssh.py +57 -70
- fractal_server/tasks/v2/database_operations.py +44 -0
- fractal_server/tasks/v2/endpoint_operations.py +104 -116
- fractal_server/tasks/v2/templates/_1_create_venv.sh +9 -5
- fractal_server/tasks/v2/templates/{_2_upgrade_pip.sh → _2_preliminary_pip_operations.sh} +1 -0
- fractal_server/tasks/v2/utils.py +5 -0
- fractal_server/utils.py +3 -2
- {fractal_server-2.6.4.dist-info → fractal_server-2.7.0.dist-info}/METADATA +3 -7
- {fractal_server-2.6.4.dist-info → fractal_server-2.7.0.dist-info}/RECORD +69 -60
- fractal_server/app/routes/auth/group_names.py +0 -34
- fractal_server/tasks/v2/_TaskCollectPip.py +0 -132
- {fractal_server-2.6.4.dist-info → fractal_server-2.7.0.dist-info}/LICENSE +0 -0
- {fractal_server-2.6.4.dist-info → fractal_server-2.7.0.dist-info}/WHEEL +0 -0
- {fractal_server-2.6.4.dist-info → fractal_server-2.7.0.dist-info}/entry_points.txt +0 -0
@@ -9,8 +9,9 @@ from pydantic import HttpUrl
|
|
9
9
|
from pydantic import root_validator
|
10
10
|
from pydantic import validator
|
11
11
|
|
12
|
-
from
|
13
|
-
from
|
12
|
+
from fractal_server.app.schemas._validators import val_unique_list
|
13
|
+
from fractal_server.app.schemas._validators import valdictkeys
|
14
|
+
from fractal_server.app.schemas._validators import valstr
|
14
15
|
from fractal_server.string_tools import validate_cmd
|
15
16
|
|
16
17
|
|
@@ -18,22 +19,26 @@ class TaskCreateV2(BaseModel, extra=Extra.forbid):
|
|
18
19
|
|
19
20
|
name: str
|
20
21
|
|
21
|
-
command_non_parallel: Optional[str]
|
22
|
-
command_parallel: Optional[str]
|
23
|
-
source: str
|
22
|
+
command_non_parallel: Optional[str] = None
|
23
|
+
command_parallel: Optional[str] = None
|
24
24
|
|
25
|
-
meta_non_parallel: Optional[dict[str, Any]]
|
26
|
-
meta_parallel: Optional[dict[str, Any]]
|
27
|
-
version: Optional[str]
|
28
|
-
args_schema_non_parallel: Optional[dict[str, Any]]
|
29
|
-
args_schema_parallel: Optional[dict[str, Any]]
|
30
|
-
args_schema_version: Optional[str]
|
31
|
-
docs_info: Optional[str]
|
32
|
-
docs_link: Optional[HttpUrl]
|
25
|
+
meta_non_parallel: Optional[dict[str, Any]] = None
|
26
|
+
meta_parallel: Optional[dict[str, Any]] = None
|
27
|
+
version: Optional[str] = None
|
28
|
+
args_schema_non_parallel: Optional[dict[str, Any]] = None
|
29
|
+
args_schema_parallel: Optional[dict[str, Any]] = None
|
30
|
+
args_schema_version: Optional[str] = None
|
31
|
+
docs_info: Optional[str] = None
|
32
|
+
docs_link: Optional[HttpUrl] = None
|
33
33
|
|
34
34
|
input_types: dict[str, bool] = Field(default={})
|
35
35
|
output_types: dict[str, bool] = Field(default={})
|
36
36
|
|
37
|
+
category: Optional[str] = None
|
38
|
+
modality: Optional[str] = None
|
39
|
+
tags: list[str] = Field(default_factory=list)
|
40
|
+
authors: Optional[str] = None
|
41
|
+
|
37
42
|
# Validators
|
38
43
|
@root_validator
|
39
44
|
def validate_commands(cls, values):
|
@@ -58,7 +63,6 @@ class TaskCreateV2(BaseModel, extra=Extra.forbid):
|
|
58
63
|
_command_parallel = validator("command_parallel", allow_reuse=True)(
|
59
64
|
valstr("command_parallel")
|
60
65
|
)
|
61
|
-
_source = validator("source", allow_reuse=True)(valstr("source"))
|
62
66
|
_version = validator("version", allow_reuse=True)(valstr("version"))
|
63
67
|
|
64
68
|
_meta_non_parallel = validator("meta_non_parallel", allow_reuse=True)(
|
@@ -83,37 +87,62 @@ class TaskCreateV2(BaseModel, extra=Extra.forbid):
|
|
83
87
|
valdictkeys("output_types")
|
84
88
|
)
|
85
89
|
|
90
|
+
_category = validator("category", allow_reuse=True)(
|
91
|
+
valstr("category", accept_none=True)
|
92
|
+
)
|
93
|
+
_modality = validator("modality", allow_reuse=True)(
|
94
|
+
valstr("modality", accept_none=True)
|
95
|
+
)
|
96
|
+
_authors = validator("authors", allow_reuse=True)(
|
97
|
+
valstr("authors", accept_none=True)
|
98
|
+
)
|
99
|
+
|
100
|
+
@validator("tags")
|
101
|
+
def validate_list_of_strings(cls, value):
|
102
|
+
for i, tag in enumerate(value):
|
103
|
+
value[i] = valstr(f"tags[{i}]")(tag)
|
104
|
+
return val_unique_list("tags")(value)
|
105
|
+
|
86
106
|
|
87
107
|
class TaskReadV2(BaseModel):
|
88
108
|
|
89
109
|
id: int
|
90
110
|
name: str
|
91
111
|
type: Literal["parallel", "non_parallel", "compound"]
|
92
|
-
source: str
|
93
|
-
|
94
|
-
version: Optional[str]
|
112
|
+
source: Optional[str] = None
|
113
|
+
version: Optional[str] = None
|
95
114
|
|
96
|
-
command_non_parallel: Optional[str]
|
97
|
-
command_parallel: Optional[str]
|
115
|
+
command_non_parallel: Optional[str] = None
|
116
|
+
command_parallel: Optional[str] = None
|
98
117
|
meta_parallel: dict[str, Any]
|
99
118
|
meta_non_parallel: dict[str, Any]
|
100
119
|
args_schema_non_parallel: Optional[dict[str, Any]] = None
|
101
120
|
args_schema_parallel: Optional[dict[str, Any]] = None
|
102
|
-
args_schema_version: Optional[str]
|
103
|
-
docs_info: Optional[str]
|
104
|
-
docs_link: Optional[HttpUrl]
|
121
|
+
args_schema_version: Optional[str] = None
|
122
|
+
docs_info: Optional[str] = None
|
123
|
+
docs_link: Optional[HttpUrl] = None
|
105
124
|
input_types: dict[str, bool]
|
106
125
|
output_types: dict[str, bool]
|
107
126
|
|
127
|
+
taskgroupv2_id: Optional[int] = None
|
128
|
+
|
129
|
+
category: Optional[str] = None
|
130
|
+
modality: Optional[str] = None
|
131
|
+
authors: Optional[str] = None
|
132
|
+
tags: list[str]
|
133
|
+
|
108
134
|
|
109
|
-
class TaskUpdateV2(BaseModel):
|
135
|
+
class TaskUpdateV2(BaseModel, extra=Extra.forbid):
|
110
136
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
137
|
+
command_parallel: Optional[str] = None
|
138
|
+
command_non_parallel: Optional[str] = None
|
139
|
+
input_types: Optional[dict[str, bool]] = None
|
140
|
+
output_types: Optional[dict[str, bool]] = None
|
141
|
+
|
142
|
+
category: Optional[str] = None
|
143
|
+
modality: Optional[str] = None
|
144
|
+
authors: Optional[str] = None
|
145
|
+
tags: Optional[list[str]] = None
|
117
146
|
|
118
147
|
# Validators
|
119
148
|
@validator("input_types", "output_types")
|
@@ -122,10 +151,6 @@ class TaskUpdateV2(BaseModel):
|
|
122
151
|
raise ValueError
|
123
152
|
return v
|
124
153
|
|
125
|
-
_name = validator("name", allow_reuse=True)(valstr("name"))
|
126
|
-
_version = validator("version", allow_reuse=True)(
|
127
|
-
valstr("version", accept_none=True)
|
128
|
-
)
|
129
154
|
_command_parallel = validator("command_parallel", allow_reuse=True)(
|
130
155
|
valstr("command_parallel")
|
131
156
|
)
|
@@ -139,14 +164,48 @@ class TaskUpdateV2(BaseModel):
|
|
139
164
|
valdictkeys("output_types")
|
140
165
|
)
|
141
166
|
|
167
|
+
_category = validator("category", allow_reuse=True)(
|
168
|
+
valstr("category", accept_none=True)
|
169
|
+
)
|
170
|
+
_modality = validator("modality", allow_reuse=True)(
|
171
|
+
valstr("modality", accept_none=True)
|
172
|
+
)
|
173
|
+
_authors = validator("authors", allow_reuse=True)(
|
174
|
+
valstr("authors", accept_none=True)
|
175
|
+
)
|
176
|
+
|
177
|
+
@validator("tags")
|
178
|
+
def validate_tags(cls, value):
|
179
|
+
for i, tag in enumerate(value):
|
180
|
+
value[i] = valstr(f"tags[{i}]")(tag)
|
181
|
+
return val_unique_list("tags")(value)
|
182
|
+
|
183
|
+
|
184
|
+
class TaskImportV2(BaseModel, extra=Extra.forbid):
|
142
185
|
|
143
|
-
|
186
|
+
pkg_name: str
|
187
|
+
version: Optional[str] = None
|
188
|
+
name: str
|
189
|
+
_pkg_name = validator("pkg_name", allow_reuse=True)(valstr("pkg_name"))
|
190
|
+
_version = validator("version", allow_reuse=True)(
|
191
|
+
valstr("version", accept_none=True)
|
192
|
+
)
|
193
|
+
_name = validator("name", allow_reuse=True)(valstr("name"))
|
144
194
|
|
195
|
+
|
196
|
+
class TaskImportV2Legacy(BaseModel):
|
145
197
|
source: str
|
146
198
|
_source = validator("source", allow_reuse=True)(valstr("source"))
|
147
199
|
|
148
200
|
|
149
201
|
class TaskExportV2(BaseModel):
|
150
202
|
|
151
|
-
|
152
|
-
|
203
|
+
pkg_name: str
|
204
|
+
version: Optional[str] = None
|
205
|
+
name: str
|
206
|
+
|
207
|
+
_pkg_name = validator("pkg_name", allow_reuse=True)(valstr("pkg_name"))
|
208
|
+
_version = validator("version", allow_reuse=True)(
|
209
|
+
valstr("version", accept_none=True)
|
210
|
+
)
|
211
|
+
_name = validator("name", allow_reuse=True)(valstr("name"))
|
@@ -6,6 +6,7 @@ from typing import Literal
|
|
6
6
|
from typing import Optional
|
7
7
|
|
8
8
|
from pydantic import BaseModel
|
9
|
+
from pydantic import Extra
|
9
10
|
from pydantic import root_validator
|
10
11
|
from pydantic import validator
|
11
12
|
|
@@ -24,7 +25,7 @@ class CollectionStatusV2(str, Enum):
|
|
24
25
|
OK = "OK"
|
25
26
|
|
26
27
|
|
27
|
-
class TaskCollectPipV2(BaseModel):
|
28
|
+
class TaskCollectPipV2(BaseModel, extra=Extra.forbid):
|
28
29
|
"""
|
29
30
|
TaskCollectPipV2 class
|
30
31
|
|
@@ -70,7 +71,7 @@ class TaskCollectPipV2(BaseModel):
|
|
70
71
|
|
71
72
|
@validator("package")
|
72
73
|
def package_validator(cls, value):
|
73
|
-
if "/" in value:
|
74
|
+
if "/" in value or value.endswith(".whl"):
|
74
75
|
if not value.endswith(".whl"):
|
75
76
|
raise ValueError(
|
76
77
|
"Local-package path must be a wheel file "
|
@@ -92,14 +93,15 @@ class TaskCollectPipV2(BaseModel):
|
|
92
93
|
return v
|
93
94
|
|
94
95
|
|
95
|
-
class TaskCollectCustomV2(BaseModel):
|
96
|
+
class TaskCollectCustomV2(BaseModel, extra=Extra.forbid):
|
96
97
|
"""
|
97
98
|
Attributes:
|
98
99
|
manifest: Manifest of a Fractal task package (this is typically the
|
99
100
|
content of `__FRACTAL_MANIFEST__.json`).
|
100
101
|
python_interpreter: Absolute path to the Python interpreter to be used
|
101
102
|
for running tasks.
|
102
|
-
|
103
|
+
name: A name identifying this package, that will fill the
|
104
|
+
`TaskGroupV2.pkg_name` column.
|
103
105
|
package_root: The folder where the package is installed.
|
104
106
|
If not provided, it will be extracted via `pip show`
|
105
107
|
(requires `package_name` to be set).
|
@@ -111,7 +113,7 @@ class TaskCollectCustomV2(BaseModel):
|
|
111
113
|
|
112
114
|
manifest: ManifestV2
|
113
115
|
python_interpreter: str
|
114
|
-
|
116
|
+
label: str
|
115
117
|
package_root: Optional[str]
|
116
118
|
package_name: Optional[str]
|
117
119
|
version: Optional[str]
|
@@ -120,7 +122,7 @@ class TaskCollectCustomV2(BaseModel):
|
|
120
122
|
_python_interpreter = validator("python_interpreter", allow_reuse=True)(
|
121
123
|
valstr("python_interpreter")
|
122
124
|
)
|
123
|
-
|
125
|
+
_label = validator("label", allow_reuse=True)(valstr("label"))
|
124
126
|
_package_root = validator("package_root", allow_reuse=True)(
|
125
127
|
valstr("package_root", accept_none=True)
|
126
128
|
)
|
@@ -0,0 +1,85 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
from enum import Enum
|
3
|
+
from typing import Literal
|
4
|
+
from typing import Optional
|
5
|
+
|
6
|
+
from pydantic import BaseModel
|
7
|
+
from pydantic import Extra
|
8
|
+
from pydantic import Field
|
9
|
+
from pydantic import validator
|
10
|
+
|
11
|
+
from .._validators import val_absolute_path
|
12
|
+
from .._validators import valdictkeys
|
13
|
+
from .._validators import valstr
|
14
|
+
from .task import TaskReadV2
|
15
|
+
|
16
|
+
|
17
|
+
class TaskGroupV2OriginEnum(str, Enum):
|
18
|
+
PYPI = "pypi"
|
19
|
+
WHEELFILE = "wheel-file"
|
20
|
+
OTHER = "other"
|
21
|
+
|
22
|
+
|
23
|
+
class TaskGroupCreateV2(BaseModel, extra=Extra.forbid):
|
24
|
+
user_id: int
|
25
|
+
user_group_id: Optional[int] = None
|
26
|
+
active: bool = True
|
27
|
+
origin: TaskGroupV2OriginEnum
|
28
|
+
pkg_name: str
|
29
|
+
version: Optional[str] = None
|
30
|
+
python_version: Optional[str] = None
|
31
|
+
path: Optional[str] = None
|
32
|
+
venv_path: Optional[str] = None
|
33
|
+
wheel_path: Optional[str] = None
|
34
|
+
pip_extras: Optional[str] = None
|
35
|
+
pinned_package_versions: dict[str, str] = Field(default_factory=dict)
|
36
|
+
|
37
|
+
# Validators
|
38
|
+
_path = validator("path", allow_reuse=True)(val_absolute_path("path"))
|
39
|
+
_venv_path = validator("venv_path", allow_reuse=True)(
|
40
|
+
val_absolute_path("venv_path")
|
41
|
+
)
|
42
|
+
_wheel_path = validator("wheel_path", allow_reuse=True)(
|
43
|
+
val_absolute_path("wheel_path")
|
44
|
+
)
|
45
|
+
_pinned_package_versions = validator(
|
46
|
+
"pinned_package_versions", allow_reuse=True
|
47
|
+
)(valdictkeys("pinned_package_versions"))
|
48
|
+
_pip_extras = validator("pip_extras", allow_reuse=True)(
|
49
|
+
valstr("pip_extras")
|
50
|
+
)
|
51
|
+
_python_version = validator("python_version", allow_reuse=True)(
|
52
|
+
valstr("python_version")
|
53
|
+
)
|
54
|
+
|
55
|
+
|
56
|
+
class TaskGroupReadV2(BaseModel):
|
57
|
+
id: int
|
58
|
+
task_list: list[TaskReadV2]
|
59
|
+
|
60
|
+
user_id: int
|
61
|
+
user_group_id: Optional[int] = None
|
62
|
+
|
63
|
+
origin: Literal["pypi", "wheel-file", "other"]
|
64
|
+
pkg_name: str
|
65
|
+
version: Optional[str] = None
|
66
|
+
python_version: Optional[str] = None
|
67
|
+
path: Optional[str] = None
|
68
|
+
venv_path: Optional[str] = None
|
69
|
+
wheel_path: Optional[str] = None
|
70
|
+
pip_extras: Optional[str] = None
|
71
|
+
pinned_package_versions: dict[str, str] = Field(default_factory=dict)
|
72
|
+
|
73
|
+
active: bool
|
74
|
+
timestamp_created: datetime
|
75
|
+
|
76
|
+
|
77
|
+
class TaskGroupUpdateV2(BaseModel, extra=Extra.forbid):
|
78
|
+
user_group_id: Optional[int] = None
|
79
|
+
active: Optional[bool] = None
|
80
|
+
|
81
|
+
@validator("active")
|
82
|
+
def active_cannot_be_None(cls, value):
|
83
|
+
if value is None:
|
84
|
+
raise ValueError("`active` cannot be set to None")
|
85
|
+
return value
|
@@ -11,6 +11,7 @@ from .project import ProjectReadV2
|
|
11
11
|
from .workflowtask import WorkflowTaskExportV2
|
12
12
|
from .workflowtask import WorkflowTaskImportV2
|
13
13
|
from .workflowtask import WorkflowTaskReadV2
|
14
|
+
from .workflowtask import WorkflowTaskReadV2WithWarning
|
14
15
|
|
15
16
|
|
16
17
|
class WorkflowCreateV2(BaseModel, extra=Extra.forbid):
|
@@ -35,7 +36,11 @@ class WorkflowReadV2(BaseModel):
|
|
35
36
|
)
|
36
37
|
|
37
38
|
|
38
|
-
class
|
39
|
+
class WorkflowReadV2WithWarnings(WorkflowReadV2):
|
40
|
+
task_list: list[WorkflowTaskReadV2WithWarning]
|
41
|
+
|
42
|
+
|
43
|
+
class WorkflowUpdateV2(BaseModel, extra=Extra.forbid):
|
39
44
|
|
40
45
|
name: Optional[str]
|
41
46
|
reordered_workflowtask_ids: Optional[list[int]]
|
@@ -52,7 +57,7 @@ class WorkflowUpdateV2(BaseModel):
|
|
52
57
|
return value
|
53
58
|
|
54
59
|
|
55
|
-
class WorkflowImportV2(BaseModel):
|
60
|
+
class WorkflowImportV2(BaseModel, extra=Extra.forbid):
|
56
61
|
"""
|
57
62
|
Class for `Workflow` import.
|
58
63
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
from enum import Enum
|
2
2
|
from typing import Any
|
3
3
|
from typing import Optional
|
4
|
+
from typing import Union
|
4
5
|
|
5
6
|
from pydantic import BaseModel
|
6
7
|
from pydantic import Extra
|
@@ -8,9 +9,9 @@ from pydantic import Field
|
|
8
9
|
from pydantic import validator
|
9
10
|
|
10
11
|
from .._validators import valdictkeys
|
11
|
-
from .._validators import valint
|
12
12
|
from .task import TaskExportV2
|
13
13
|
from .task import TaskImportV2
|
14
|
+
from .task import TaskImportV2Legacy
|
14
15
|
from .task import TaskReadV2
|
15
16
|
from fractal_server.images import Filters
|
16
17
|
|
@@ -42,7 +43,6 @@ class WorkflowTaskCreateV2(BaseModel, extra=Extra.forbid):
|
|
42
43
|
meta_parallel: Optional[dict[str, Any]]
|
43
44
|
args_non_parallel: Optional[dict[str, Any]]
|
44
45
|
args_parallel: Optional[dict[str, Any]]
|
45
|
-
order: Optional[int]
|
46
46
|
input_filters: Filters = Field(default_factory=Filters)
|
47
47
|
|
48
48
|
# Validators
|
@@ -52,7 +52,6 @@ class WorkflowTaskCreateV2(BaseModel, extra=Extra.forbid):
|
|
52
52
|
_meta_parallel = validator("meta_parallel", allow_reuse=True)(
|
53
53
|
valdictkeys("meta_parallel")
|
54
54
|
)
|
55
|
-
_order = validator("order", allow_reuse=True)(valint("order", min_val=0))
|
56
55
|
|
57
56
|
@validator("args_non_parallel")
|
58
57
|
def validate_args_non_parallel(cls, value):
|
@@ -102,7 +101,11 @@ class WorkflowTaskReadV2(BaseModel):
|
|
102
101
|
task: TaskReadV2
|
103
102
|
|
104
103
|
|
105
|
-
class
|
104
|
+
class WorkflowTaskReadV2WithWarning(WorkflowTaskReadV2):
|
105
|
+
warning: Optional[str] = None
|
106
|
+
|
107
|
+
|
108
|
+
class WorkflowTaskUpdateV2(BaseModel, extra=Extra.forbid):
|
106
109
|
|
107
110
|
meta_non_parallel: Optional[dict[str, Any]]
|
108
111
|
meta_parallel: Optional[dict[str, Any]]
|
@@ -147,7 +150,7 @@ class WorkflowTaskUpdateV2(BaseModel):
|
|
147
150
|
return value
|
148
151
|
|
149
152
|
|
150
|
-
class WorkflowTaskImportV2(BaseModel):
|
153
|
+
class WorkflowTaskImportV2(BaseModel, extra=Extra.forbid):
|
151
154
|
|
152
155
|
meta_non_parallel: Optional[dict[str, Any]] = None
|
153
156
|
meta_parallel: Optional[dict[str, Any]] = None
|
@@ -156,7 +159,7 @@ class WorkflowTaskImportV2(BaseModel):
|
|
156
159
|
|
157
160
|
input_filters: Optional[Filters] = None
|
158
161
|
|
159
|
-
task: TaskImportV2
|
162
|
+
task: Union[TaskImportV2, TaskImportV2Legacy]
|
160
163
|
|
161
164
|
_meta_non_parallel = validator("meta_non_parallel", allow_reuse=True)(
|
162
165
|
valdictkeys("meta_non_parallel")
|
@@ -315,13 +315,20 @@ async def _create_first_user(
|
|
315
315
|
|
316
316
|
|
317
317
|
def _create_first_group():
|
318
|
+
"""
|
319
|
+
Create a `UserGroup` with `name=FRACTAL_DEFAULT_GROUP_NAME`, if missing.
|
320
|
+
"""
|
318
321
|
function_logger = set_logger("fractal_server.create_first_group")
|
319
322
|
|
320
323
|
function_logger.info(
|
321
324
|
f"START _create_first_group, with name '{FRACTAL_DEFAULT_GROUP_NAME}'"
|
322
325
|
)
|
323
326
|
with next(get_sync_db()) as db:
|
324
|
-
group_all = db.execute(
|
327
|
+
group_all = db.execute(
|
328
|
+
select(UserGroup).where(
|
329
|
+
UserGroup.name == FRACTAL_DEFAULT_GROUP_NAME
|
330
|
+
)
|
331
|
+
)
|
325
332
|
if group_all.scalars().one_or_none() is None:
|
326
333
|
first_group = UserGroup(name=FRACTAL_DEFAULT_GROUP_NAME)
|
327
334
|
db.add(first_group)
|
fractal_server/config.py
CHANGED
@@ -167,9 +167,9 @@ class Settings(BaseSettings):
|
|
167
167
|
###########################################################################
|
168
168
|
# DATABASE
|
169
169
|
###########################################################################
|
170
|
-
DB_ENGINE: Literal["sqlite", "postgres
|
170
|
+
DB_ENGINE: Literal["sqlite", "postgres-psycopg"] = "sqlite"
|
171
171
|
"""
|
172
|
-
|
172
|
+
Database engine to use (supported: `sqlite`, `postgres-psycopg`).
|
173
173
|
"""
|
174
174
|
DB_ECHO: bool = False
|
175
175
|
"""
|
@@ -203,16 +203,7 @@ class Settings(BaseSettings):
|
|
203
203
|
|
204
204
|
@property
|
205
205
|
def DATABASE_ASYNC_URL(self) -> URL:
|
206
|
-
if self.DB_ENGINE == "postgres":
|
207
|
-
url = URL.create(
|
208
|
-
drivername="postgresql+asyncpg",
|
209
|
-
username=self.POSTGRES_USER,
|
210
|
-
password=self.POSTGRES_PASSWORD,
|
211
|
-
host=self.POSTGRES_HOST,
|
212
|
-
port=self.POSTGRES_PORT,
|
213
|
-
database=self.POSTGRES_DB,
|
214
|
-
)
|
215
|
-
elif self.DB_ENGINE == "postgres-psycopg":
|
206
|
+
if self.DB_ENGINE == "postgres-psycopg":
|
216
207
|
url = URL.create(
|
217
208
|
drivername="postgresql+psycopg",
|
218
209
|
username=self.POSTGRES_USER,
|
@@ -235,11 +226,7 @@ class Settings(BaseSettings):
|
|
235
226
|
|
236
227
|
@property
|
237
228
|
def DATABASE_SYNC_URL(self):
|
238
|
-
if self.DB_ENGINE == "postgres":
|
239
|
-
return self.DATABASE_ASYNC_URL.set(
|
240
|
-
drivername="postgresql+psycopg2"
|
241
|
-
)
|
242
|
-
elif self.DB_ENGINE == "postgres-psycopg":
|
229
|
+
if self.DB_ENGINE == "postgres-psycopg":
|
243
230
|
return self.DATABASE_ASYNC_URL.set(drivername="postgresql+psycopg")
|
244
231
|
else:
|
245
232
|
if not self.SQLITE_PATH:
|
@@ -546,20 +533,13 @@ class Settings(BaseSettings):
|
|
546
533
|
"""
|
547
534
|
Checks that db environment variables are properly set.
|
548
535
|
"""
|
549
|
-
if self.DB_ENGINE == "postgres":
|
536
|
+
if self.DB_ENGINE == "postgres-psycopg":
|
550
537
|
if not self.POSTGRES_DB:
|
551
538
|
raise FractalConfigurationError(
|
552
|
-
"POSTGRES_DB cannot be None when DB_ENGINE=
|
553
|
-
|
554
|
-
try:
|
555
|
-
import psycopg2 # noqa: F401
|
556
|
-
import asyncpg # noqa: F401
|
557
|
-
except ModuleNotFoundError:
|
558
|
-
raise FractalConfigurationError(
|
559
|
-
"DB engine is `postgres` but `psycopg2` or `asyncpg` "
|
560
|
-
"are not available"
|
539
|
+
"POSTGRES_DB cannot be None when DB_ENGINE="
|
540
|
+
"postgres-psycopg."
|
561
541
|
)
|
562
|
-
|
542
|
+
|
563
543
|
try:
|
564
544
|
import psycopg # noqa: F401
|
565
545
|
except ModuleNotFoundError:
|