fractal-server 2.12.1__py3-none-any.whl → 2.13.1__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/security.py +9 -12
- fractal_server/app/models/v2/__init__.py +4 -0
- fractal_server/app/models/v2/accounting.py +35 -0
- fractal_server/app/models/v2/dataset.py +2 -2
- fractal_server/app/models/v2/job.py +11 -9
- fractal_server/app/models/v2/task.py +2 -3
- fractal_server/app/models/v2/task_group.py +6 -2
- fractal_server/app/models/v2/workflowtask.py +15 -8
- fractal_server/app/routes/admin/v2/__init__.py +4 -0
- fractal_server/app/routes/admin/v2/accounting.py +108 -0
- fractal_server/app/routes/admin/v2/impersonate.py +35 -0
- fractal_server/app/routes/admin/v2/job.py +5 -13
- fractal_server/app/routes/admin/v2/task.py +1 -1
- fractal_server/app/routes/admin/v2/task_group.py +5 -13
- fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +3 -3
- fractal_server/app/routes/api/v2/dataset.py +4 -4
- fractal_server/app/routes/api/v2/images.py +11 -11
- fractal_server/app/routes/api/v2/project.py +2 -2
- fractal_server/app/routes/api/v2/status.py +1 -1
- fractal_server/app/routes/api/v2/submit.py +9 -6
- fractal_server/app/routes/api/v2/task.py +4 -2
- fractal_server/app/routes/api/v2/task_collection.py +3 -2
- fractal_server/app/routes/api/v2/task_group.py +4 -7
- fractal_server/app/routes/api/v2/workflow.py +3 -3
- fractal_server/app/routes/api/v2/workflow_import.py +3 -3
- fractal_server/app/routes/api/v2/workflowtask.py +3 -1
- fractal_server/app/routes/auth/_aux_auth.py +4 -1
- fractal_server/app/routes/auth/current_user.py +3 -5
- fractal_server/app/routes/auth/group.py +1 -1
- fractal_server/app/routes/auth/users.py +2 -4
- fractal_server/app/routes/aux/__init__.py +0 -20
- fractal_server/app/routes/aux/_runner.py +1 -1
- fractal_server/app/routes/aux/validate_user_settings.py +1 -2
- fractal_server/app/runner/executors/_job_states.py +13 -0
- fractal_server/app/runner/executors/slurm/_slurm_config.py +26 -18
- fractal_server/app/runner/executors/slurm/ssh/__init__.py +0 -3
- fractal_server/app/runner/executors/slurm/ssh/_executor_wait_thread.py +31 -22
- fractal_server/app/runner/executors/slurm/ssh/_slurm_job.py +2 -5
- fractal_server/app/runner/executors/slurm/ssh/executor.py +21 -27
- fractal_server/app/runner/executors/slurm/sudo/__init__.py +0 -3
- fractal_server/app/runner/executors/slurm/sudo/_check_jobs_status.py +1 -2
- fractal_server/app/runner/executors/slurm/sudo/_executor_wait_thread.py +37 -47
- fractal_server/app/runner/executors/slurm/sudo/executor.py +25 -24
- fractal_server/app/runner/v2/__init__.py +4 -9
- fractal_server/app/runner/v2/_local/__init__.py +3 -0
- fractal_server/app/runner/v2/_local/_local_config.py +5 -4
- fractal_server/app/runner/v2/_slurm_common/get_slurm_config.py +4 -4
- fractal_server/app/runner/v2/_slurm_ssh/__init__.py +2 -0
- fractal_server/app/runner/v2/_slurm_sudo/__init__.py +4 -2
- fractal_server/app/runner/v2/deduplicate_list.py +1 -1
- fractal_server/app/runner/v2/runner.py +25 -10
- fractal_server/app/runner/v2/runner_functions.py +12 -11
- fractal_server/app/runner/v2/task_interface.py +15 -7
- fractal_server/app/schemas/_filter_validators.py +6 -3
- fractal_server/app/schemas/_validators.py +7 -5
- fractal_server/app/schemas/user.py +23 -18
- fractal_server/app/schemas/user_group.py +25 -11
- fractal_server/app/schemas/user_settings.py +31 -24
- fractal_server/app/schemas/v2/__init__.py +1 -0
- fractal_server/app/schemas/v2/accounting.py +18 -0
- fractal_server/app/schemas/v2/dataset.py +48 -35
- fractal_server/app/schemas/v2/dumps.py +16 -14
- fractal_server/app/schemas/v2/job.py +49 -29
- fractal_server/app/schemas/v2/manifest.py +32 -28
- fractal_server/app/schemas/v2/project.py +18 -8
- fractal_server/app/schemas/v2/task.py +86 -75
- fractal_server/app/schemas/v2/task_collection.py +41 -30
- fractal_server/app/schemas/v2/task_group.py +39 -20
- fractal_server/app/schemas/v2/workflow.py +24 -12
- fractal_server/app/schemas/v2/workflowtask.py +63 -61
- fractal_server/app/security/__init__.py +1 -1
- fractal_server/config.py +86 -73
- fractal_server/images/models.py +18 -12
- fractal_server/main.py +1 -1
- fractal_server/migrations/versions/af1ef1c83c9b_add_accounting_tables.py +57 -0
- fractal_server/tasks/v2/utils_background.py +2 -2
- fractal_server/tasks/v2/utils_database.py +1 -1
- {fractal_server-2.12.1.dist-info → fractal_server-2.13.1.dist-info}/METADATA +9 -10
- {fractal_server-2.12.1.dist-info → fractal_server-2.13.1.dist-info}/RECORD +83 -81
- fractal_server/app/runner/v2/_local_experimental/__init__.py +0 -121
- fractal_server/app/runner/v2/_local_experimental/_local_config.py +0 -108
- fractal_server/app/runner/v2/_local_experimental/_submit_setup.py +0 -42
- fractal_server/app/runner/v2/_local_experimental/executor.py +0 -157
- {fractal_server-2.12.1.dist-info → fractal_server-2.13.1.dist-info}/LICENSE +0 -0
- {fractal_server-2.12.1.dist-info → fractal_server-2.13.1.dist-info}/WHEEL +0 -0
- {fractal_server-2.12.1.dist-info → fractal_server-2.13.1.dist-info}/entry_points.txt +0 -0
@@ -3,11 +3,11 @@ from typing import Literal
|
|
3
3
|
from typing import Optional
|
4
4
|
|
5
5
|
from pydantic import BaseModel
|
6
|
-
from pydantic import
|
6
|
+
from pydantic import ConfigDict
|
7
7
|
from pydantic import Field
|
8
|
+
from pydantic import field_validator
|
8
9
|
from pydantic import HttpUrl
|
9
|
-
from pydantic import
|
10
|
-
from pydantic import validator
|
10
|
+
from pydantic import model_validator
|
11
11
|
|
12
12
|
from fractal_server.app.schemas._validators import val_unique_list
|
13
13
|
from fractal_server.app.schemas._validators import valdict_keys
|
@@ -15,7 +15,8 @@ from fractal_server.app.schemas._validators import valstr
|
|
15
15
|
from fractal_server.string_tools import validate_cmd
|
16
16
|
|
17
17
|
|
18
|
-
class TaskCreateV2(BaseModel
|
18
|
+
class TaskCreateV2(BaseModel):
|
19
|
+
model_config = ConfigDict(extra="forbid")
|
19
20
|
|
20
21
|
name: str
|
21
22
|
|
@@ -29,7 +30,7 @@ class TaskCreateV2(BaseModel, extra=Extra.forbid):
|
|
29
30
|
args_schema_parallel: Optional[dict[str, Any]] = None
|
30
31
|
args_schema_version: Optional[str] = None
|
31
32
|
docs_info: Optional[str] = None
|
32
|
-
docs_link: Optional[
|
33
|
+
docs_link: Optional[str] = None
|
33
34
|
|
34
35
|
input_types: dict[str, bool] = Field(default={})
|
35
36
|
output_types: dict[str, bool] = Field(default={})
|
@@ -40,10 +41,10 @@ class TaskCreateV2(BaseModel, extra=Extra.forbid):
|
|
40
41
|
authors: Optional[str] = None
|
41
42
|
|
42
43
|
# Validators
|
43
|
-
@
|
44
|
-
def validate_commands(
|
45
|
-
command_parallel =
|
46
|
-
command_non_parallel =
|
44
|
+
@model_validator(mode="after")
|
45
|
+
def validate_commands(self):
|
46
|
+
command_parallel = self.command_parallel
|
47
|
+
command_non_parallel = self.command_non_parallel
|
47
48
|
if (command_parallel is None) and (command_non_parallel is None):
|
48
49
|
raise ValueError(
|
49
50
|
"Task must have at least one valid command "
|
@@ -54,58 +55,65 @@ class TaskCreateV2(BaseModel, extra=Extra.forbid):
|
|
54
55
|
if command_non_parallel is not None:
|
55
56
|
validate_cmd(command_non_parallel)
|
56
57
|
|
57
|
-
return
|
58
|
+
return self
|
58
59
|
|
59
|
-
_name =
|
60
|
-
_command_non_parallel =
|
61
|
-
"command_non_parallel"
|
62
|
-
)(valstr("command_non_parallel"))
|
63
|
-
_command_parallel = validator("command_parallel", allow_reuse=True)(
|
64
|
-
valstr("command_parallel")
|
60
|
+
_name = field_validator("name")(classmethod(valstr("name")))
|
61
|
+
_command_non_parallel = field_validator("command_non_parallel")(
|
62
|
+
classmethod(valstr("command_non_parallel"))
|
65
63
|
)
|
66
|
-
|
64
|
+
_command_parallel = field_validator("command_parallel")(
|
65
|
+
classmethod(valstr("command_parallel"))
|
66
|
+
)
|
67
|
+
_version = field_validator("version")(classmethod(valstr("version")))
|
67
68
|
|
68
|
-
_meta_non_parallel =
|
69
|
-
valdict_keys("meta_non_parallel")
|
69
|
+
_meta_non_parallel = field_validator("meta_non_parallel")(
|
70
|
+
classmethod(valdict_keys("meta_non_parallel"))
|
71
|
+
)
|
72
|
+
_meta_parallel = field_validator("meta_parallel")(
|
73
|
+
classmethod(valdict_keys("meta_parallel"))
|
70
74
|
)
|
71
|
-
|
72
|
-
valdict_keys("
|
75
|
+
_args_schema_non_parallel = field_validator("args_schema_non_parallel")(
|
76
|
+
classmethod(valdict_keys("args_schema_non_parallel"))
|
73
77
|
)
|
74
|
-
|
75
|
-
"
|
76
|
-
)(valdict_keys("args_schema_non_parallel"))
|
77
|
-
_args_schema_parallel = validator(
|
78
|
-
"args_schema_parallel", allow_reuse=True
|
79
|
-
)(valdict_keys("args_schema_parallel"))
|
80
|
-
_args_schema_version = validator("args_schema_version", allow_reuse=True)(
|
81
|
-
valstr("args_schema_version")
|
78
|
+
_args_schema_parallel = field_validator("args_schema_parallel")(
|
79
|
+
classmethod(valdict_keys("args_schema_parallel"))
|
82
80
|
)
|
83
|
-
|
84
|
-
|
81
|
+
_args_schema_version = field_validator("args_schema_version")(
|
82
|
+
classmethod(valstr("args_schema_version"))
|
85
83
|
)
|
86
|
-
|
87
|
-
valdict_keys("
|
84
|
+
_input_types = field_validator("input_types")(
|
85
|
+
classmethod(valdict_keys("input_types"))
|
86
|
+
)
|
87
|
+
_output_types = field_validator("output_types")(
|
88
|
+
classmethod(valdict_keys("output_types"))
|
88
89
|
)
|
89
90
|
|
90
|
-
_category =
|
91
|
-
valstr("category", accept_none=True)
|
91
|
+
_category = field_validator("category")(
|
92
|
+
classmethod(valstr("category", accept_none=True))
|
92
93
|
)
|
93
|
-
_modality =
|
94
|
-
valstr("modality", accept_none=True)
|
94
|
+
_modality = field_validator("modality")(
|
95
|
+
classmethod(valstr("modality", accept_none=True))
|
95
96
|
)
|
96
|
-
_authors =
|
97
|
-
valstr("authors", accept_none=True)
|
97
|
+
_authors = field_validator("authors")(
|
98
|
+
classmethod(valstr("authors", accept_none=True))
|
98
99
|
)
|
99
100
|
|
100
|
-
@
|
101
|
+
@field_validator("tags")
|
102
|
+
@classmethod
|
101
103
|
def validate_list_of_strings(cls, value):
|
102
104
|
for i, tag in enumerate(value):
|
103
|
-
value[i] = valstr(f"tags[{i}]")(tag)
|
104
|
-
return val_unique_list("tags")(value)
|
105
|
+
value[i] = valstr(f"tags[{i}]")(cls, tag)
|
106
|
+
return val_unique_list("tags")(cls, value)
|
105
107
|
|
108
|
+
@field_validator("docs_link", mode="after")
|
109
|
+
@classmethod
|
110
|
+
def validate_docs_link(cls, value):
|
111
|
+
if value is not None:
|
112
|
+
HttpUrl(value)
|
113
|
+
return value
|
106
114
|
|
107
|
-
class TaskReadV2(BaseModel):
|
108
115
|
|
116
|
+
class TaskReadV2(BaseModel):
|
109
117
|
id: int
|
110
118
|
name: str
|
111
119
|
type: Literal["parallel", "non_parallel", "compound"]
|
@@ -120,7 +128,7 @@ class TaskReadV2(BaseModel):
|
|
120
128
|
args_schema_parallel: Optional[dict[str, Any]] = None
|
121
129
|
args_schema_version: Optional[str] = None
|
122
130
|
docs_info: Optional[str] = None
|
123
|
-
docs_link: Optional[
|
131
|
+
docs_link: Optional[str] = None
|
124
132
|
input_types: dict[str, bool]
|
125
133
|
output_types: dict[str, bool]
|
126
134
|
|
@@ -132,7 +140,8 @@ class TaskReadV2(BaseModel):
|
|
132
140
|
tags: list[str]
|
133
141
|
|
134
142
|
|
135
|
-
class TaskUpdateV2(BaseModel
|
143
|
+
class TaskUpdateV2(BaseModel):
|
144
|
+
model_config = ConfigDict(extra="forbid")
|
136
145
|
|
137
146
|
command_parallel: Optional[str] = None
|
138
147
|
command_non_parallel: Optional[str] = None
|
@@ -145,67 +154,69 @@ class TaskUpdateV2(BaseModel, extra=Extra.forbid):
|
|
145
154
|
tags: Optional[list[str]] = None
|
146
155
|
|
147
156
|
# Validators
|
148
|
-
@
|
157
|
+
@field_validator("input_types", "output_types")
|
158
|
+
@classmethod
|
149
159
|
def val_is_dict(cls, v):
|
150
160
|
if not isinstance(v, dict):
|
151
161
|
raise ValueError
|
152
162
|
return v
|
153
163
|
|
154
|
-
_command_parallel =
|
155
|
-
valstr("command_parallel")
|
164
|
+
_command_parallel = field_validator("command_parallel")(
|
165
|
+
classmethod(valstr("command_parallel"))
|
166
|
+
)
|
167
|
+
_command_non_parallel = field_validator("command_non_parallel")(
|
168
|
+
classmethod(valstr("command_non_parallel"))
|
156
169
|
)
|
157
|
-
|
158
|
-
"
|
159
|
-
)(valstr("command_non_parallel"))
|
160
|
-
_input_types = validator("input_types", allow_reuse=True)(
|
161
|
-
valdict_keys("input_types")
|
170
|
+
_input_types = field_validator("input_types")(
|
171
|
+
classmethod(valdict_keys("input_types"))
|
162
172
|
)
|
163
|
-
_output_types =
|
164
|
-
valdict_keys("output_types")
|
173
|
+
_output_types = field_validator("output_types")(
|
174
|
+
classmethod(valdict_keys("output_types"))
|
165
175
|
)
|
166
176
|
|
167
|
-
_category =
|
168
|
-
valstr("category", accept_none=True)
|
177
|
+
_category = field_validator("category")(
|
178
|
+
classmethod(valstr("category", accept_none=True))
|
169
179
|
)
|
170
|
-
_modality =
|
171
|
-
valstr("modality", accept_none=True)
|
180
|
+
_modality = field_validator("modality")(
|
181
|
+
classmethod(valstr("modality", accept_none=True))
|
172
182
|
)
|
173
|
-
_authors =
|
174
|
-
valstr("authors", accept_none=True)
|
183
|
+
_authors = field_validator("authors")(
|
184
|
+
classmethod(valstr("authors", accept_none=True))
|
175
185
|
)
|
176
186
|
|
177
|
-
@
|
187
|
+
@field_validator("tags")
|
188
|
+
@classmethod
|
178
189
|
def validate_tags(cls, value):
|
179
190
|
for i, tag in enumerate(value):
|
180
|
-
value[i] = valstr(f"tags[{i}]")(tag)
|
181
|
-
return val_unique_list("tags")(value)
|
191
|
+
value[i] = valstr(f"tags[{i}]")(cls, tag)
|
192
|
+
return val_unique_list("tags")(cls, value)
|
182
193
|
|
183
194
|
|
184
|
-
class TaskImportV2(BaseModel
|
195
|
+
class TaskImportV2(BaseModel):
|
196
|
+
model_config = ConfigDict(extra="forbid")
|
185
197
|
|
186
198
|
pkg_name: str
|
187
199
|
version: Optional[str] = None
|
188
200
|
name: str
|
189
|
-
_pkg_name =
|
190
|
-
_version =
|
191
|
-
valstr("version", accept_none=True)
|
201
|
+
_pkg_name = field_validator("pkg_name")(classmethod(valstr("pkg_name")))
|
202
|
+
_version = field_validator("version")(
|
203
|
+
classmethod(valstr("version", accept_none=True))
|
192
204
|
)
|
193
|
-
_name =
|
205
|
+
_name = field_validator("name")(classmethod(valstr("name")))
|
194
206
|
|
195
207
|
|
196
208
|
class TaskImportV2Legacy(BaseModel):
|
197
209
|
source: str
|
198
|
-
_source =
|
210
|
+
_source = field_validator("source")(classmethod(valstr("source")))
|
199
211
|
|
200
212
|
|
201
213
|
class TaskExportV2(BaseModel):
|
202
|
-
|
203
214
|
pkg_name: str
|
204
215
|
version: Optional[str] = None
|
205
216
|
name: str
|
206
217
|
|
207
|
-
_pkg_name =
|
208
|
-
_version =
|
209
|
-
valstr("version", accept_none=True)
|
218
|
+
_pkg_name = field_validator("pkg_name")(classmethod(valstr("pkg_name")))
|
219
|
+
_version = field_validator("version")(
|
220
|
+
classmethod(valstr("version", accept_none=True))
|
210
221
|
)
|
211
|
-
_name =
|
222
|
+
_name = field_validator("name")(classmethod(valstr("name")))
|
@@ -3,9 +3,9 @@ from typing import Literal
|
|
3
3
|
from typing import Optional
|
4
4
|
|
5
5
|
from pydantic import BaseModel
|
6
|
-
from pydantic import
|
7
|
-
from pydantic import
|
8
|
-
from pydantic import
|
6
|
+
from pydantic import ConfigDict
|
7
|
+
from pydantic import field_validator
|
8
|
+
from pydantic import model_validator
|
9
9
|
|
10
10
|
from .._validators import valstr
|
11
11
|
from fractal_server.app.schemas.v2 import ManifestV2
|
@@ -21,7 +21,7 @@ class WheelFile(BaseModel):
|
|
21
21
|
contents: bytes
|
22
22
|
|
23
23
|
|
24
|
-
class TaskCollectPipV2(BaseModel
|
24
|
+
class TaskCollectPipV2(BaseModel):
|
25
25
|
"""
|
26
26
|
TaskCollectPipV2 class
|
27
27
|
|
@@ -45,35 +45,40 @@ class TaskCollectPipV2(BaseModel, extra=Extra.forbid):
|
|
45
45
|
|
46
46
|
"""
|
47
47
|
|
48
|
+
model_config = ConfigDict(extra="forbid")
|
48
49
|
package: Optional[str] = None
|
49
50
|
package_version: Optional[str] = None
|
50
51
|
package_extras: Optional[str] = None
|
51
52
|
python_version: Optional[Literal["3.9", "3.10", "3.11", "3.12"]] = None
|
52
53
|
pinned_package_versions: Optional[dict[str, str]] = None
|
53
54
|
|
54
|
-
@
|
55
|
+
@field_validator("package")
|
56
|
+
@classmethod
|
55
57
|
def package_validator(cls, value: Optional[str]) -> Optional[str]:
|
56
58
|
if value is None:
|
57
59
|
return value
|
58
|
-
value = valstr("package")(value)
|
60
|
+
value = valstr("package")(cls, value)
|
59
61
|
validate_cmd(value, attribute_name="package")
|
60
62
|
return value
|
61
63
|
|
62
|
-
@
|
64
|
+
@field_validator("package_version")
|
65
|
+
@classmethod
|
63
66
|
def package_version_validator(cls, value: Optional[str]) -> Optional[str]:
|
64
67
|
if value is None:
|
65
68
|
return value
|
66
|
-
value = valstr("package_version")(value)
|
69
|
+
value = valstr("package_version")(cls, value)
|
67
70
|
validate_cmd(value, attribute_name="package_version")
|
68
71
|
return value
|
69
72
|
|
70
|
-
@
|
73
|
+
@field_validator("pinned_package_versions")
|
74
|
+
@classmethod
|
71
75
|
def pinned_package_versions_validator(cls, value):
|
72
76
|
if value is None:
|
73
77
|
return value
|
74
78
|
old_keys = list(value.keys())
|
75
79
|
new_keys = [
|
76
|
-
valstr(f"pinned_package_versions[{key}]")(key)
|
80
|
+
valstr(f"pinned_package_versions[{key}]")(cls, key)
|
81
|
+
for key in old_keys
|
77
82
|
]
|
78
83
|
if len(new_keys) != len(set(new_keys)):
|
79
84
|
raise ValueError(
|
@@ -87,16 +92,17 @@ class TaskCollectPipV2(BaseModel, extra=Extra.forbid):
|
|
87
92
|
validate_cmd(version)
|
88
93
|
return value
|
89
94
|
|
90
|
-
@
|
95
|
+
@field_validator("package_extras")
|
96
|
+
@classmethod
|
91
97
|
def package_extras_validator(cls, value: Optional[str]) -> Optional[str]:
|
92
98
|
if value is None:
|
93
99
|
return value
|
94
|
-
value = valstr("package_extras")(value)
|
100
|
+
value = valstr("package_extras")(cls, value)
|
95
101
|
validate_cmd(value, attribute_name="package_extras")
|
96
102
|
return value
|
97
103
|
|
98
104
|
|
99
|
-
class TaskCollectCustomV2(BaseModel
|
105
|
+
class TaskCollectCustomV2(BaseModel):
|
100
106
|
"""
|
101
107
|
Attributes:
|
102
108
|
manifest: Manifest of a Fractal task package (this is typically the
|
@@ -114,29 +120,31 @@ class TaskCollectCustomV2(BaseModel, extra=Extra.forbid):
|
|
114
120
|
version: Optional version of tasks to be collected.
|
115
121
|
"""
|
116
122
|
|
123
|
+
model_config = ConfigDict(extra="forbid")
|
117
124
|
manifest: ManifestV2
|
118
125
|
python_interpreter: str
|
119
126
|
label: str
|
120
|
-
package_root: Optional[str]
|
121
|
-
package_name: Optional[str]
|
122
|
-
version: Optional[str]
|
127
|
+
package_root: Optional[str] = None
|
128
|
+
package_name: Optional[str] = None
|
129
|
+
version: Optional[str] = None
|
123
130
|
|
124
131
|
# Valstr
|
125
|
-
_python_interpreter =
|
126
|
-
valstr("python_interpreter")
|
132
|
+
_python_interpreter = field_validator("python_interpreter")(
|
133
|
+
classmethod(valstr("python_interpreter"))
|
127
134
|
)
|
128
|
-
_label =
|
129
|
-
_package_root =
|
130
|
-
valstr("package_root", accept_none=True)
|
135
|
+
_label = field_validator("label")(classmethod(valstr("label")))
|
136
|
+
_package_root = field_validator("package_root")(
|
137
|
+
classmethod(valstr("package_root", accept_none=True))
|
131
138
|
)
|
132
|
-
_package_name =
|
133
|
-
valstr("package_name", accept_none=True)
|
139
|
+
_package_name = field_validator("package_name")(
|
140
|
+
classmethod(valstr("package_name", accept_none=True))
|
134
141
|
)
|
135
|
-
_version =
|
136
|
-
valstr("version", accept_none=True)
|
142
|
+
_version = field_validator("version")(
|
143
|
+
classmethod(valstr("version", accept_none=True))
|
137
144
|
)
|
138
145
|
|
139
|
-
@
|
146
|
+
@model_validator(mode="before")
|
147
|
+
@classmethod
|
140
148
|
def one_of_package_root_or_name(cls, values):
|
141
149
|
package_root = values.get("package_root")
|
142
150
|
package_name = values.get("package_name")
|
@@ -149,18 +157,20 @@ class TaskCollectCustomV2(BaseModel, extra=Extra.forbid):
|
|
149
157
|
)
|
150
158
|
return values
|
151
159
|
|
152
|
-
@
|
160
|
+
@field_validator("package_name")
|
161
|
+
@classmethod
|
153
162
|
def package_name_validator(cls, value: str):
|
154
163
|
"""
|
155
164
|
Remove all whitespace characters, then check for invalid code.
|
156
165
|
"""
|
157
166
|
if value is not None:
|
158
167
|
validate_cmd(value)
|
159
|
-
value = valstr("package_name")(value)
|
168
|
+
value = valstr("package_name")(cls, value)
|
160
169
|
value = value.replace(" ", "")
|
161
170
|
return value
|
162
171
|
|
163
|
-
@
|
172
|
+
@field_validator("package_root")
|
173
|
+
@classmethod
|
164
174
|
def package_root_validator(cls, value):
|
165
175
|
if (value is not None) and (not Path(value).is_absolute()):
|
166
176
|
raise ValueError(
|
@@ -168,7 +178,8 @@ class TaskCollectCustomV2(BaseModel, extra=Extra.forbid):
|
|
168
178
|
)
|
169
179
|
return value
|
170
180
|
|
171
|
-
@
|
181
|
+
@field_validator("python_interpreter")
|
182
|
+
@classmethod
|
172
183
|
def python_interpreter_validator(cls, value):
|
173
184
|
if not Path(value).is_absolute():
|
174
185
|
raise ValueError(
|
@@ -3,9 +3,11 @@ from enum import Enum
|
|
3
3
|
from typing import Optional
|
4
4
|
|
5
5
|
from pydantic import BaseModel
|
6
|
-
from pydantic import
|
6
|
+
from pydantic import ConfigDict
|
7
7
|
from pydantic import Field
|
8
|
-
from pydantic import
|
8
|
+
from pydantic import field_serializer
|
9
|
+
from pydantic import field_validator
|
10
|
+
from pydantic.types import AwareDatetime
|
9
11
|
|
10
12
|
from .._validators import val_absolute_path
|
11
13
|
from .._validators import valdict_keys
|
@@ -32,7 +34,8 @@ class TaskGroupActivityActionV2(str, Enum):
|
|
32
34
|
REACTIVATE = "reactivate"
|
33
35
|
|
34
36
|
|
35
|
-
class TaskGroupCreateV2(BaseModel
|
37
|
+
class TaskGroupCreateV2(BaseModel):
|
38
|
+
model_config = ConfigDict(extra="forbid")
|
36
39
|
user_id: int
|
37
40
|
user_group_id: Optional[int] = None
|
38
41
|
active: bool = True
|
@@ -48,21 +51,21 @@ class TaskGroupCreateV2(BaseModel, extra=Extra.forbid):
|
|
48
51
|
pinned_package_versions: dict[str, str] = Field(default_factory=dict)
|
49
52
|
|
50
53
|
# Validators
|
51
|
-
_path =
|
52
|
-
_venv_path =
|
53
|
-
val_absolute_path("venv_path")
|
54
|
+
_path = field_validator("path")(classmethod(val_absolute_path("path")))
|
55
|
+
_venv_path = field_validator("venv_path")(
|
56
|
+
classmethod(val_absolute_path("venv_path"))
|
54
57
|
)
|
55
|
-
_wheel_path =
|
56
|
-
val_absolute_path("wheel_path")
|
58
|
+
_wheel_path = field_validator("wheel_path")(
|
59
|
+
classmethod(val_absolute_path("wheel_path"))
|
57
60
|
)
|
58
|
-
_pinned_package_versions =
|
59
|
-
"pinned_package_versions"
|
60
|
-
)(valdict_keys("pinned_package_versions"))
|
61
|
-
_pip_extras = validator("pip_extras", allow_reuse=True)(
|
62
|
-
valstr("pip_extras")
|
61
|
+
_pinned_package_versions = field_validator("pinned_package_versions")(
|
62
|
+
valdict_keys("pinned_package_versions")
|
63
63
|
)
|
64
|
-
|
65
|
-
valstr("
|
64
|
+
_pip_extras = field_validator("pip_extras")(
|
65
|
+
classmethod(valstr("pip_extras"))
|
66
|
+
)
|
67
|
+
_python_version = field_validator("python_version")(
|
68
|
+
classmethod(valstr("python_version"))
|
66
69
|
)
|
67
70
|
|
68
71
|
|
@@ -99,11 +102,16 @@ class TaskGroupReadV2(BaseModel):
|
|
99
102
|
venv_file_number: Optional[int] = None
|
100
103
|
|
101
104
|
active: bool
|
102
|
-
timestamp_created:
|
103
|
-
timestamp_last_used:
|
105
|
+
timestamp_created: AwareDatetime
|
106
|
+
timestamp_last_used: AwareDatetime
|
107
|
+
|
108
|
+
@field_serializer("timestamp_created", "timestamp_last_used")
|
109
|
+
def serialize_datetime(v: datetime) -> str:
|
110
|
+
return v.isoformat()
|
104
111
|
|
105
112
|
|
106
|
-
class TaskGroupUpdateV2(BaseModel
|
113
|
+
class TaskGroupUpdateV2(BaseModel):
|
114
|
+
model_config = ConfigDict(extra="forbid")
|
107
115
|
user_group_id: Optional[int] = None
|
108
116
|
|
109
117
|
|
@@ -111,10 +119,21 @@ class TaskGroupActivityV2Read(BaseModel):
|
|
111
119
|
id: int
|
112
120
|
user_id: int
|
113
121
|
taskgroupv2_id: Optional[int] = None
|
114
|
-
timestamp_started:
|
115
|
-
timestamp_ended: Optional[
|
122
|
+
timestamp_started: AwareDatetime
|
123
|
+
timestamp_ended: Optional[AwareDatetime] = None
|
116
124
|
pkg_name: str
|
117
125
|
version: str
|
118
126
|
status: TaskGroupActivityStatusV2
|
119
127
|
action: TaskGroupActivityActionV2
|
120
128
|
log: Optional[str] = None
|
129
|
+
|
130
|
+
@field_serializer("timestamp_started")
|
131
|
+
def serialize_datetime_start(v: datetime) -> str:
|
132
|
+
return v.isoformat()
|
133
|
+
|
134
|
+
@field_serializer("timestamp_ended")
|
135
|
+
def serialize_datetime_end(v: Optional[datetime]) -> Optional[str]:
|
136
|
+
if v is None:
|
137
|
+
return None
|
138
|
+
else:
|
139
|
+
return v.isoformat()
|
@@ -2,8 +2,10 @@ from datetime import datetime
|
|
2
2
|
from typing import Optional
|
3
3
|
|
4
4
|
from pydantic import BaseModel
|
5
|
-
from pydantic import
|
6
|
-
from pydantic import
|
5
|
+
from pydantic import ConfigDict
|
6
|
+
from pydantic import field_serializer
|
7
|
+
from pydantic import field_validator
|
8
|
+
from pydantic.types import AwareDatetime
|
7
9
|
|
8
10
|
from .._validators import valstr
|
9
11
|
from .project import ProjectReadV2
|
@@ -13,12 +15,14 @@ from .workflowtask import WorkflowTaskReadV2
|
|
13
15
|
from .workflowtask import WorkflowTaskReadV2WithWarning
|
14
16
|
|
15
17
|
|
16
|
-
class WorkflowCreateV2(BaseModel
|
18
|
+
class WorkflowCreateV2(BaseModel):
|
19
|
+
|
20
|
+
model_config = ConfigDict(extra="forbid")
|
17
21
|
|
18
22
|
name: str
|
19
23
|
|
20
24
|
# Validators
|
21
|
-
_name =
|
25
|
+
_name = field_validator("name")(classmethod(valstr("name")))
|
22
26
|
|
23
27
|
|
24
28
|
class WorkflowReadV2(BaseModel):
|
@@ -28,22 +32,29 @@ class WorkflowReadV2(BaseModel):
|
|
28
32
|
project_id: int
|
29
33
|
task_list: list[WorkflowTaskReadV2]
|
30
34
|
project: ProjectReadV2
|
31
|
-
timestamp_created:
|
35
|
+
timestamp_created: AwareDatetime
|
36
|
+
|
37
|
+
@field_serializer("timestamp_created")
|
38
|
+
def serialize_datetime(v: datetime) -> str:
|
39
|
+
return v.isoformat()
|
32
40
|
|
33
41
|
|
34
42
|
class WorkflowReadV2WithWarnings(WorkflowReadV2):
|
35
43
|
task_list: list[WorkflowTaskReadV2WithWarning]
|
36
44
|
|
37
45
|
|
38
|
-
class WorkflowUpdateV2(BaseModel
|
46
|
+
class WorkflowUpdateV2(BaseModel):
|
47
|
+
|
48
|
+
model_config = ConfigDict(extra="forbid")
|
39
49
|
|
40
|
-
name: Optional[str]
|
41
|
-
reordered_workflowtask_ids: Optional[list[int]]
|
50
|
+
name: Optional[str] = None
|
51
|
+
reordered_workflowtask_ids: Optional[list[int]] = None
|
42
52
|
|
43
53
|
# Validators
|
44
|
-
_name =
|
54
|
+
_name = field_validator("name")(classmethod(valstr("name")))
|
45
55
|
|
46
|
-
@
|
56
|
+
@field_validator("reordered_workflowtask_ids")
|
57
|
+
@classmethod
|
47
58
|
def check_positive_and_unique(cls, value):
|
48
59
|
if any(i < 0 for i in value):
|
49
60
|
raise ValueError("Negative `id` in `reordered_workflowtask_ids`")
|
@@ -52,7 +63,7 @@ class WorkflowUpdateV2(BaseModel, extra=Extra.forbid):
|
|
52
63
|
return value
|
53
64
|
|
54
65
|
|
55
|
-
class WorkflowImportV2(BaseModel
|
66
|
+
class WorkflowImportV2(BaseModel):
|
56
67
|
"""
|
57
68
|
Class for `Workflow` import.
|
58
69
|
|
@@ -60,11 +71,12 @@ class WorkflowImportV2(BaseModel, extra=Extra.forbid):
|
|
60
71
|
task_list:
|
61
72
|
"""
|
62
73
|
|
74
|
+
model_config = ConfigDict(extra="forbid")
|
63
75
|
name: str
|
64
76
|
task_list: list[WorkflowTaskImportV2]
|
65
77
|
|
66
78
|
# Validators
|
67
|
-
_name =
|
79
|
+
_name = field_validator("name")(classmethod(valstr("name")))
|
68
80
|
|
69
81
|
|
70
82
|
class WorkflowExportV2(BaseModel):
|