fractal-server 2.17.1a0__py3-none-any.whl → 2.17.2__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 +19 -18
- fractal_server/app/db/__init__.py +3 -3
- fractal_server/app/models/__init__.py +1 -1
- fractal_server/app/models/linkuserproject.py +3 -1
- fractal_server/app/models/security.py +22 -17
- fractal_server/app/models/v2/__init__.py +3 -1
- fractal_server/app/models/v2/accounting.py +9 -1
- fractal_server/app/models/v2/dataset.py +5 -1
- fractal_server/app/models/v2/history.py +15 -1
- fractal_server/app/models/v2/job.py +4 -0
- fractal_server/app/models/v2/profile.py +29 -0
- fractal_server/app/models/v2/project.py +5 -14
- fractal_server/app/models/v2/resource.py +4 -0
- fractal_server/app/models/v2/task_group.py +5 -7
- fractal_server/app/models/v2/workflow.py +2 -1
- fractal_server/app/routes/admin/v2/__init__.py +1 -2
- fractal_server/app/routes/admin/v2/accounting.py +1 -1
- fractal_server/app/routes/admin/v2/job.py +9 -9
- fractal_server/app/routes/admin/v2/profile.py +3 -2
- fractal_server/app/routes/admin/v2/resource.py +5 -5
- fractal_server/app/routes/admin/v2/task.py +28 -18
- fractal_server/app/routes/admin/v2/task_group.py +0 -1
- fractal_server/app/routes/admin/v2/task_group_lifecycle.py +1 -2
- fractal_server/app/routes/api/__init__.py +1 -0
- fractal_server/app/routes/api/v2/__init__.py +5 -6
- fractal_server/app/routes/api/v2/_aux_functions.py +70 -63
- fractal_server/app/routes/api/v2/_aux_functions_history.py +43 -20
- fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +2 -4
- fractal_server/app/routes/api/v2/_aux_functions_tasks.py +5 -7
- fractal_server/app/routes/api/v2/_aux_task_group_disambiguation.py +1 -2
- fractal_server/app/routes/api/v2/dataset.py +13 -32
- fractal_server/app/routes/api/v2/history.py +35 -21
- fractal_server/app/routes/api/v2/images.py +3 -2
- fractal_server/app/routes/api/v2/job.py +17 -14
- fractal_server/app/routes/api/v2/pre_submission_checks.py +5 -4
- fractal_server/app/routes/api/v2/project.py +22 -17
- fractal_server/app/routes/api/v2/status_legacy.py +12 -11
- fractal_server/app/routes/api/v2/submit.py +11 -12
- fractal_server/app/routes/api/v2/task.py +4 -3
- fractal_server/app/routes/api/v2/task_collection.py +28 -30
- fractal_server/app/routes/api/v2/task_collection_custom.py +8 -7
- fractal_server/app/routes/api/v2/task_collection_pixi.py +1 -2
- fractal_server/app/routes/api/v2/task_group.py +7 -6
- fractal_server/app/routes/api/v2/task_group_lifecycle.py +6 -6
- fractal_server/app/routes/api/v2/task_version_update.py +13 -12
- fractal_server/app/routes/api/v2/workflow.py +14 -31
- fractal_server/app/routes/api/v2/workflow_import.py +17 -19
- fractal_server/app/routes/api/v2/workflowtask.py +10 -12
- fractal_server/app/routes/auth/__init__.py +1 -3
- fractal_server/app/routes/auth/_aux_auth.py +1 -2
- fractal_server/app/routes/auth/current_user.py +4 -5
- fractal_server/app/routes/auth/group.py +7 -5
- fractal_server/app/routes/auth/login.py +1 -0
- fractal_server/app/routes/auth/oauth.py +4 -3
- fractal_server/app/routes/auth/register.py +4 -2
- fractal_server/app/routes/auth/users.py +10 -10
- fractal_server/app/routes/aux/_job.py +1 -1
- fractal_server/app/routes/aux/_runner.py +2 -2
- fractal_server/app/routes/pagination.py +1 -1
- fractal_server/app/schemas/user.py +3 -3
- fractal_server/app/schemas/v2/accounting.py +11 -0
- fractal_server/app/schemas/v2/dataset.py +28 -4
- fractal_server/app/schemas/v2/dumps.py +1 -0
- fractal_server/app/schemas/v2/manifest.py +4 -3
- fractal_server/app/schemas/v2/profile.py +53 -2
- fractal_server/app/schemas/v2/resource.py +109 -13
- fractal_server/app/schemas/v2/task.py +0 -1
- fractal_server/app/schemas/v2/task_collection.py +1 -1
- fractal_server/app/schemas/v2/workflowtask.py +4 -3
- fractal_server/app/security/__init__.py +4 -7
- fractal_server/app/security/signup_email.py +4 -5
- fractal_server/app/shutdown.py +23 -19
- fractal_server/config/_data.py +36 -25
- fractal_server/config/_database.py +19 -20
- fractal_server/config/_email.py +30 -38
- fractal_server/config/_main.py +34 -53
- fractal_server/config/_oauth.py +17 -21
- fractal_server/exceptions.py +4 -0
- fractal_server/images/models.py +3 -3
- fractal_server/images/status_tools.py +4 -2
- fractal_server/logger.py +1 -1
- fractal_server/main.py +4 -3
- fractal_server/migrations/versions/034a469ec2eb_task_groups.py +4 -8
- fractal_server/migrations/versions/091b01f51f88_add_usergroup_and_linkusergroup_table.py +1 -1
- fractal_server/migrations/versions/0f5f85bb2ae7_add_pre_pinned_packages.py +1 -0
- fractal_server/migrations/versions/19eca0dd47a9_user_settings_project_dir.py +1 -1
- fractal_server/migrations/versions/1a83a5260664_rename.py +1 -1
- fractal_server/migrations/versions/1eac13a26c83_drop_v1_tables.py +1 -0
- fractal_server/migrations/versions/316140ff7ee1_remove_usersettings_cache_dir.py +1 -1
- fractal_server/migrations/versions/40d6d6511b20_add_index_to_history_models.py +47 -0
- fractal_server/migrations/versions/45fbb391d7af_make_resource_id_fk_non_nullable.py +46 -0
- fractal_server/migrations/versions/47351f8c7ebc_drop_dataset_filters.py +1 -0
- fractal_server/migrations/versions/49d0856e9569_drop_table.py +62 -0
- fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py +1 -1
- fractal_server/migrations/versions/4cedeb448a53_workflowtask_foreign_keys_not_nullables.py +1 -1
- fractal_server/migrations/versions/501961cfcd85_remove_link_between_v1_and_v2_tasks_.py +2 -1
- fractal_server/migrations/versions/50a13d6138fd_initial_schema.py +7 -19
- fractal_server/migrations/versions/5bf02391cfef_v2.py +4 -10
- fractal_server/migrations/versions/70e77f1c38b0_add_applyworkflow_first_task_index_and_.py +1 -0
- fractal_server/migrations/versions/71eefd1dd202_add_slurm_accounts.py +1 -1
- fractal_server/migrations/versions/7673fe18c05d_remove_project_dir_server_default.py +29 -0
- fractal_server/migrations/versions/791ce783d3d8_add_indices.py +1 -1
- fractal_server/migrations/versions/83bc2ad3ffcc_2_17_0.py +1 -0
- fractal_server/migrations/versions/84bf0fffde30_add_dumps_to_applyworkflow.py +1 -0
- fractal_server/migrations/versions/8e8f227a3e36_update_taskv2_post_2_7_0.py +2 -4
- fractal_server/migrations/versions/8f79bd162e35_add_docs_info_and_docs_link_to_task_.py +1 -1
- fractal_server/migrations/versions/94a47ea2d3ff_remove_cache_dir_slurm_user_and_slurm_.py +1 -0
- fractal_server/migrations/versions/969d84257cac_add_historyrun_task_id.py +1 -1
- fractal_server/migrations/versions/97f444d47249_add_applyworkflow_project_dump.py +1 -1
- fractal_server/migrations/versions/981d588fe248_add_executor_error_log.py +1 -1
- fractal_server/migrations/versions/99ea79d9e5d2_add_dataset_history.py +2 -4
- fractal_server/migrations/versions/9c5ae74c9b98_add_user_settings_table.py +1 -1
- fractal_server/migrations/versions/9db60297b8b2_set_ondelete.py +1 -1
- fractal_server/migrations/versions/9fd26a2b0de4_add_workflow_timestamp_created.py +1 -1
- fractal_server/migrations/versions/a7f4d6137b53_add_workflow_dump_to_applyworkflow.py +1 -1
- fractal_server/migrations/versions/af1ef1c83c9b_add_accounting_tables.py +1 -0
- fractal_server/migrations/versions/af8673379a5c_drop_old_filter_columns.py +1 -0
- fractal_server/migrations/versions/b1e7f7a1ff71_task_group_for_pixi.py +1 -1
- fractal_server/migrations/versions/b3ffb095f973_json_to_jsonb.py +1 -0
- fractal_server/migrations/versions/c90a7c76e996_job_id_in_history_run.py +1 -1
- fractal_server/migrations/versions/caba9fb1ea5e_drop_useroauth_user_settings_id.py +49 -0
- fractal_server/migrations/versions/d256a7379ab8_taskgroup_activity_and_venv_info_to_.py +4 -9
- fractal_server/migrations/versions/d4fe3708d309_make_applyworkflow_workflow_dump_non_.py +1 -0
- fractal_server/migrations/versions/da2cb2ac4255_user_group_viewer_paths.py +1 -1
- fractal_server/migrations/versions/db09233ad13a_split_filters_and_keep_old_columns.py +1 -0
- fractal_server/migrations/versions/e0e717ae2f26_delete_linkuserproject_ondelete_project.py +50 -0
- fractal_server/migrations/versions/e75cac726012_make_applyworkflow_start_timestamp_not_.py +1 -0
- fractal_server/migrations/versions/e81103413827_add_job_type_filters.py +1 -1
- fractal_server/migrations/versions/efa89c30e0a4_add_project_timestamp_created.py +1 -0
- fractal_server/migrations/versions/f37aceb45062_make_historyunit_logfile_required.py +1 -1
- fractal_server/migrations/versions/f384e1c0cf5d_drop_task_default_args_columns.py +1 -0
- fractal_server/migrations/versions/fbce16ff4e47_new_history_items.py +4 -9
- fractal_server/runner/config/_local.py +8 -5
- fractal_server/runner/config/_slurm.py +37 -33
- fractal_server/runner/config/slurm_mem_to_MB.py +0 -1
- fractal_server/runner/executors/base_runner.py +29 -4
- fractal_server/runner/executors/local/get_local_config.py +1 -0
- fractal_server/runner/executors/local/runner.py +14 -13
- fractal_server/runner/executors/slurm_common/_batching.py +5 -10
- fractal_server/runner/executors/slurm_common/base_slurm_runner.py +53 -27
- fractal_server/runner/executors/slurm_common/get_slurm_config.py +14 -7
- fractal_server/runner/executors/slurm_common/remote.py +3 -1
- fractal_server/runner/executors/slurm_common/slurm_config.py +1 -0
- fractal_server/runner/executors/slurm_common/slurm_job_task_models.py +1 -3
- fractal_server/runner/executors/slurm_ssh/runner.py +16 -11
- fractal_server/runner/executors/slurm_ssh/tar_commands.py +1 -0
- fractal_server/runner/executors/slurm_sudo/_subprocess_run_as_user.py +1 -0
- fractal_server/runner/executors/slurm_sudo/runner.py +16 -11
- fractal_server/runner/task_files.py +9 -3
- fractal_server/runner/v2/_local.py +9 -4
- fractal_server/runner/v2/_slurm_ssh.py +11 -5
- fractal_server/runner/v2/_slurm_sudo.py +11 -5
- fractal_server/runner/v2/db_tools.py +0 -1
- fractal_server/runner/v2/deduplicate_list.py +2 -1
- fractal_server/runner/v2/runner.py +11 -14
- fractal_server/runner/v2/runner_functions.py +11 -14
- fractal_server/runner/v2/submit_workflow.py +7 -6
- fractal_server/ssh/_fabric.py +6 -13
- fractal_server/string_tools.py +0 -1
- fractal_server/syringe.py +1 -1
- fractal_server/tasks/config/_pixi.py +1 -1
- fractal_server/tasks/config/_python.py +16 -9
- fractal_server/tasks/utils.py +0 -1
- fractal_server/tasks/v2/local/_utils.py +1 -1
- fractal_server/tasks/v2/local/collect.py +10 -12
- fractal_server/tasks/v2/local/collect_pixi.py +9 -10
- fractal_server/tasks/v2/local/deactivate.py +7 -8
- fractal_server/tasks/v2/local/deactivate_pixi.py +4 -4
- fractal_server/tasks/v2/local/delete.py +1 -3
- fractal_server/tasks/v2/local/reactivate.py +7 -7
- fractal_server/tasks/v2/local/reactivate_pixi.py +7 -7
- fractal_server/tasks/v2/ssh/_utils.py +3 -3
- fractal_server/tasks/v2/ssh/collect.py +14 -19
- fractal_server/tasks/v2/ssh/collect_pixi.py +17 -19
- fractal_server/tasks/v2/ssh/deactivate.py +10 -8
- fractal_server/tasks/v2/ssh/deactivate_pixi.py +6 -5
- fractal_server/tasks/v2/ssh/delete.py +7 -5
- fractal_server/tasks/v2/ssh/reactivate.py +11 -11
- fractal_server/tasks/v2/ssh/reactivate_pixi.py +8 -9
- fractal_server/tasks/v2/templates/1_create_venv.sh +2 -0
- fractal_server/tasks/v2/templates/2_pip_install.sh +2 -0
- fractal_server/tasks/v2/templates/3_pip_freeze.sh +2 -0
- fractal_server/tasks/v2/templates/4_pip_show.sh +2 -0
- fractal_server/tasks/v2/templates/5_get_venv_size_and_file_number.sh +3 -1
- fractal_server/tasks/v2/templates/6_pip_install_from_freeze.sh +2 -0
- fractal_server/tasks/v2/templates/pixi_1_extract.sh +2 -0
- fractal_server/tasks/v2/templates/pixi_2_install.sh +2 -0
- fractal_server/tasks/v2/templates/pixi_3_post_install.sh +2 -0
- fractal_server/tasks/v2/utils_background.py +3 -3
- fractal_server/tasks/v2/utils_package_names.py +1 -2
- fractal_server/tasks/v2/utils_pixi.py +1 -3
- fractal_server/types/__init__.py +76 -1
- fractal_server/types/validators/_common_validators.py +1 -3
- fractal_server/types/validators/_workflow_task_arguments_validators.py +1 -2
- fractal_server/utils.py +1 -0
- fractal_server/zip_tools.py +34 -0
- {fractal_server-2.17.1a0.dist-info → fractal_server-2.17.2.dist-info}/METADATA +1 -1
- fractal_server-2.17.2.dist-info/RECORD +265 -0
- fractal_server/app/models/user_settings.py +0 -37
- fractal_server/app/routes/admin/v2/project.py +0 -41
- fractal_server/data_migrations/2_17_0.py +0 -339
- fractal_server-2.17.1a0.dist-info/RECORD +0 -262
- {fractal_server-2.17.1a0.dist-info → fractal_server-2.17.2.dist-info}/WHEEL +0 -0
- {fractal_server-2.17.1a0.dist-info → fractal_server-2.17.2.dist-info}/entry_points.txt +0 -0
- {fractal_server-2.17.1a0.dist-info → fractal_server-2.17.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -7,8 +7,8 @@ from typing import Self
|
|
|
7
7
|
from pydantic import AfterValidator
|
|
8
8
|
from pydantic import BaseModel
|
|
9
9
|
from pydantic import Discriminator
|
|
10
|
-
from pydantic import model_validator
|
|
11
10
|
from pydantic import Tag
|
|
11
|
+
from pydantic import model_validator
|
|
12
12
|
from pydantic import validate_call
|
|
13
13
|
from pydantic.types import AwareDatetime
|
|
14
14
|
|
|
@@ -21,23 +21,46 @@ from fractal_server.types import NonEmptyStr
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class ResourceType(StrEnum):
|
|
24
|
+
"""
|
|
25
|
+
Enum for the possible resource types.
|
|
26
|
+
"""
|
|
27
|
+
|
|
24
28
|
SLURM_SUDO = "slurm_sudo"
|
|
29
|
+
"""
|
|
30
|
+
Enum entry for resource type `slurm_sudo`.
|
|
31
|
+
"""
|
|
32
|
+
|
|
25
33
|
SLURM_SSH = "slurm_ssh"
|
|
34
|
+
"""
|
|
35
|
+
Enum entry for resource type `slurm_ssh`.
|
|
36
|
+
"""
|
|
37
|
+
|
|
26
38
|
LOCAL = "local"
|
|
39
|
+
"""
|
|
40
|
+
Enum entry for resource type `local`.
|
|
41
|
+
"""
|
|
27
42
|
|
|
28
43
|
|
|
29
44
|
def cast_serialize_pixi_settings(
|
|
30
|
-
|
|
45
|
+
value: dict[NonEmptyStr, Any],
|
|
31
46
|
) -> dict[NonEmptyStr, Any]:
|
|
32
47
|
"""
|
|
33
|
-
|
|
48
|
+
Cast/serialize round trip for `tasks_pixi_config` through the
|
|
49
|
+
`TasksPixiSettings` schema.
|
|
50
|
+
|
|
51
|
+
Arguments:
|
|
52
|
+
value: Current `tasks_pixi_config` value.
|
|
34
53
|
"""
|
|
35
|
-
if
|
|
36
|
-
|
|
37
|
-
return
|
|
54
|
+
if value != {}:
|
|
55
|
+
value = TasksPixiSettings(**value).model_dump()
|
|
56
|
+
return value
|
|
38
57
|
|
|
39
58
|
|
|
40
|
-
class
|
|
59
|
+
class ValidResourceBase(BaseModel):
|
|
60
|
+
"""
|
|
61
|
+
Base resource schema.
|
|
62
|
+
"""
|
|
63
|
+
|
|
41
64
|
type: ResourceType
|
|
42
65
|
name: NonEmptyStr
|
|
43
66
|
|
|
@@ -61,27 +84,90 @@ class _ValidResourceBase(BaseModel):
|
|
|
61
84
|
and self.type == ResourceType.SLURM_SSH
|
|
62
85
|
and self.tasks_pixi_config["SLURM_CONFIG"] is None
|
|
63
86
|
):
|
|
64
|
-
raise ValueError(
|
|
65
|
-
"`tasks_pixi_config` must include `SLURM_CONFIG`."
|
|
66
|
-
)
|
|
87
|
+
raise ValueError("`tasks_pixi_config` must include `SLURM_CONFIG`.")
|
|
67
88
|
return self
|
|
68
89
|
|
|
69
90
|
|
|
70
|
-
class ValidResourceLocal(
|
|
91
|
+
class ValidResourceLocal(ValidResourceBase):
|
|
92
|
+
"""
|
|
93
|
+
Valid local resource.
|
|
94
|
+
|
|
95
|
+
Attributes:
|
|
96
|
+
name: Resource name.
|
|
97
|
+
type: Resource type.
|
|
98
|
+
tasks_python_config:
|
|
99
|
+
Configuration of Python interpreters used for task collection.
|
|
100
|
+
tasks_pixi_config:
|
|
101
|
+
Configuration of `pixi` interpreters used for task collection.
|
|
102
|
+
tasks_local_dir:
|
|
103
|
+
Local base folder for task environments.
|
|
104
|
+
jobs_local_dir:
|
|
105
|
+
Local base folder for job folders.
|
|
106
|
+
jobs_runner_config:
|
|
107
|
+
Runner configuration.
|
|
108
|
+
|
|
109
|
+
"""
|
|
110
|
+
|
|
71
111
|
type: Literal[ResourceType.LOCAL]
|
|
72
112
|
jobs_runner_config: JobRunnerConfigLocal
|
|
73
113
|
jobs_slurm_python_worker: None = None
|
|
74
114
|
host: None = None
|
|
75
115
|
|
|
76
116
|
|
|
77
|
-
class ValidResourceSlurmSudo(
|
|
117
|
+
class ValidResourceSlurmSudo(ValidResourceBase):
|
|
118
|
+
"""
|
|
119
|
+
Valid SLURM-sudo resource.
|
|
120
|
+
|
|
121
|
+
Attributes:
|
|
122
|
+
name: Resource name.
|
|
123
|
+
type: Resource type.
|
|
124
|
+
tasks_python_config:
|
|
125
|
+
Configuration of Python interpreters used for task collection.
|
|
126
|
+
tasks_pixi_config:
|
|
127
|
+
Configuration of `pixi` interpreters used for task collection.
|
|
128
|
+
tasks_local_dir:
|
|
129
|
+
Local base folder for task environments.
|
|
130
|
+
jobs_local_dir:
|
|
131
|
+
Local base folder for job folders.
|
|
132
|
+
jobs_runner_config:
|
|
133
|
+
Runner configuration.
|
|
134
|
+
jobs_poll_interval:
|
|
135
|
+
`squeue` polling interval.
|
|
136
|
+
jobs_slurm_python_worker:
|
|
137
|
+
Python worker to be used in SLURM jobs.
|
|
138
|
+
"""
|
|
139
|
+
|
|
78
140
|
type: Literal[ResourceType.SLURM_SUDO]
|
|
79
141
|
jobs_slurm_python_worker: AbsolutePathStr
|
|
80
142
|
jobs_runner_config: JobRunnerConfigSLURM
|
|
81
143
|
host: None = None
|
|
82
144
|
|
|
83
145
|
|
|
84
|
-
class ValidResourceSlurmSSH(
|
|
146
|
+
class ValidResourceSlurmSSH(ValidResourceBase):
|
|
147
|
+
"""
|
|
148
|
+
Valid SLURM-SSH resource.
|
|
149
|
+
|
|
150
|
+
Attributes:
|
|
151
|
+
name: Resource name
|
|
152
|
+
type: Resource type.
|
|
153
|
+
tasks_python_config:
|
|
154
|
+
Configuration of Python interpreters used for task collection.
|
|
155
|
+
tasks_pixi_config:
|
|
156
|
+
Configuration of `pixi` interpreters used for task collection.
|
|
157
|
+
tasks_local_dir:
|
|
158
|
+
Local base folder for task environments.
|
|
159
|
+
jobs_local_dir:
|
|
160
|
+
Local base folder for job folders.
|
|
161
|
+
jobs_runner_config:
|
|
162
|
+
Runner configuration.
|
|
163
|
+
jobs_poll_interval:
|
|
164
|
+
`squeue` polling interval.
|
|
165
|
+
jobs_slurm_python_worker:
|
|
166
|
+
Python worker to be used in SLURM jobs.
|
|
167
|
+
host:
|
|
168
|
+
Hostname or IP address of remote SLURM cluster.
|
|
169
|
+
"""
|
|
170
|
+
|
|
85
171
|
type: Literal[ResourceType.SLURM_SSH]
|
|
86
172
|
host: NonEmptyStr
|
|
87
173
|
jobs_slurm_python_worker: AbsolutePathStr
|
|
@@ -101,9 +187,16 @@ ResourceCreate = Annotated[
|
|
|
101
187
|
| Annotated[ValidResourceSlurmSSH, Tag(ResourceType.SLURM_SSH)],
|
|
102
188
|
Discriminator(get_discriminator_value),
|
|
103
189
|
]
|
|
190
|
+
"""
|
|
191
|
+
Schema for resources in API request bodies.
|
|
192
|
+
"""
|
|
104
193
|
|
|
105
194
|
|
|
106
195
|
class ResourceRead(BaseModel):
|
|
196
|
+
"""
|
|
197
|
+
Schema for resources in API response bodies.
|
|
198
|
+
"""
|
|
199
|
+
|
|
107
200
|
id: int
|
|
108
201
|
|
|
109
202
|
type: str
|
|
@@ -131,6 +224,9 @@ def cast_serialize_resource(_data: ResourceCreate) -> dict[str, Any]:
|
|
|
131
224
|
We use `@validate_call` because `ResourceCreate` is a `Union` type and it
|
|
132
225
|
cannot be instantiated directly.
|
|
133
226
|
|
|
227
|
+
Args:
|
|
228
|
+
_data:
|
|
229
|
+
|
|
134
230
|
Return:
|
|
135
231
|
Serialized version of a valid resource object.
|
|
136
232
|
"""
|
|
@@ -5,14 +5,15 @@ from pydantic import ConfigDict
|
|
|
5
5
|
from pydantic import Field
|
|
6
6
|
from pydantic import model_validator
|
|
7
7
|
|
|
8
|
+
from fractal_server.types import DictStrAny
|
|
9
|
+
from fractal_server.types import TypeFilters
|
|
10
|
+
from fractal_server.types import WorkflowTaskArgument
|
|
11
|
+
|
|
8
12
|
from .task import TaskExportV2
|
|
9
13
|
from .task import TaskImportV2
|
|
10
14
|
from .task import TaskImportV2Legacy
|
|
11
15
|
from .task import TaskReadV2
|
|
12
16
|
from .task import TaskType
|
|
13
|
-
from fractal_server.types import DictStrAny
|
|
14
|
-
from fractal_server.types import TypeFilters
|
|
15
|
-
from fractal_server.types import WorkflowTaskArgument
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
class WorkflowTaskCreateV2(BaseModel):
|
|
@@ -15,6 +15,7 @@ registers the client and the relative routes.
|
|
|
15
15
|
|
|
16
16
|
All routes are registered under the `auth/` prefix.
|
|
17
17
|
"""
|
|
18
|
+
|
|
18
19
|
import contextlib
|
|
19
20
|
from collections.abc import AsyncGenerator
|
|
20
21
|
from typing import Any
|
|
@@ -39,7 +40,7 @@ from sqlalchemy.orm import selectinload
|
|
|
39
40
|
from sqlmodel import func
|
|
40
41
|
from sqlmodel import select
|
|
41
42
|
|
|
42
|
-
from
|
|
43
|
+
from fractal_server.app.db import get_async_db
|
|
43
44
|
from fractal_server.app.db import get_sync_db
|
|
44
45
|
from fractal_server.app.models import LinkUserGroup
|
|
45
46
|
from fractal_server.app.models import OAuthAccount
|
|
@@ -275,9 +276,7 @@ class UserManager(IntegerIDMixin, BaseUserManager[UserOAuth, int]):
|
|
|
275
276
|
)
|
|
276
277
|
except exceptions.UserNotExists:
|
|
277
278
|
# (0) Log
|
|
278
|
-
logger.warning(
|
|
279
|
-
f"Self-registration attempt by {account_email}."
|
|
280
|
-
)
|
|
279
|
+
logger.warning(f"Self-registration attempt by {account_email}.")
|
|
281
280
|
|
|
282
281
|
# (1) Prepare user-facing error message
|
|
283
282
|
error_msg = (
|
|
@@ -459,9 +458,7 @@ def _create_first_group():
|
|
|
459
458
|
)
|
|
460
459
|
return
|
|
461
460
|
|
|
462
|
-
function_logger.info(
|
|
463
|
-
f"START, name '{settings.FRACTAL_DEFAULT_GROUP_NAME}'"
|
|
464
|
-
)
|
|
461
|
+
function_logger.info(f"START, name '{settings.FRACTAL_DEFAULT_GROUP_NAME}'")
|
|
465
462
|
with next(get_sync_db()) as db:
|
|
466
463
|
group_all = db.execute(
|
|
467
464
|
select(UserGroup).where(
|
|
@@ -36,9 +36,9 @@ def send_fractal_email_or_log_failure(
|
|
|
36
36
|
for recipient in email_settings.recipients
|
|
37
37
|
]
|
|
38
38
|
)
|
|
39
|
-
mail_msg[
|
|
40
|
-
"
|
|
41
|
-
|
|
39
|
+
mail_msg["Subject"] = (
|
|
40
|
+
f"[Fractal, {email_settings.instance_name}] {subject}"
|
|
41
|
+
)
|
|
42
42
|
with smtplib.SMTP(
|
|
43
43
|
email_settings.smtp_server,
|
|
44
44
|
email_settings.port,
|
|
@@ -61,6 +61,5 @@ def send_fractal_email_or_log_failure(
|
|
|
61
61
|
|
|
62
62
|
except Exception as e:
|
|
63
63
|
logger.error(
|
|
64
|
-
"Could not send self-registration email, "
|
|
65
|
-
f"original error: {str(e)}."
|
|
64
|
+
f"Could not send self-registration email, original error: {str(e)}."
|
|
66
65
|
)
|
fractal_server/app/shutdown.py
CHANGED
|
@@ -12,44 +12,48 @@ from fractal_server.syringe import Inject
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
async def cleanup_after_shutdown(*, jobsV2: list[int], logger_name: str):
|
|
15
|
+
settings = Inject(get_settings)
|
|
15
16
|
logger = get_logger(logger_name)
|
|
16
17
|
logger.info("Cleanup function after shutdown")
|
|
17
|
-
|
|
18
|
+
stm_objects = (
|
|
18
19
|
select(JobV2)
|
|
19
20
|
.where(JobV2.id.in_(jobsV2))
|
|
20
21
|
.where(JobV2.status == JobStatusTypeV2.SUBMITTED)
|
|
21
22
|
)
|
|
23
|
+
stm_ids = (
|
|
24
|
+
select(JobV2.id)
|
|
25
|
+
.where(JobV2.id.in_(jobsV2))
|
|
26
|
+
.where(JobV2.status == JobStatusTypeV2.SUBMITTED)
|
|
27
|
+
)
|
|
22
28
|
|
|
23
29
|
async for session in get_async_db():
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
for job in
|
|
30
|
+
# Write shutdown file for all jobs
|
|
31
|
+
jobs = (await session.execute(stm_objects)).scalars().all()
|
|
32
|
+
for job in jobs:
|
|
27
33
|
_write_shutdown_file(job=job)
|
|
28
34
|
|
|
29
|
-
|
|
30
|
-
|
|
35
|
+
# Wait for completion of all job - with a timeout
|
|
36
|
+
interval = settings.FRACTAL_GRACEFUL_SHUTDOWN_TIME / 20
|
|
31
37
|
t_start = time.perf_counter()
|
|
32
38
|
while (
|
|
33
39
|
time.perf_counter() - t_start
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if len(jobsV2_db) == 0:
|
|
40
|
-
logger.info(
|
|
41
|
-
"All jobs associated to this app are "
|
|
42
|
-
"either done or failed. Exit."
|
|
43
|
-
)
|
|
40
|
+
) <= settings.FRACTAL_GRACEFUL_SHUTDOWN_TIME:
|
|
41
|
+
job_ids = (await session.execute(stm_ids)).scalars().all()
|
|
42
|
+
if len(job_ids) == 0:
|
|
43
|
+
logger.info("All jobs are either done or failed. Exit.")
|
|
44
44
|
return
|
|
45
45
|
else:
|
|
46
|
-
logger.info(f"Some jobs are still 'submitted' {
|
|
46
|
+
logger.info(f"Some jobs are still 'submitted': {job_ids=}")
|
|
47
|
+
logger.info(f"Wait {interval:.4f} seconds before next check.")
|
|
48
|
+
time.sleep(interval)
|
|
47
49
|
logger.info(
|
|
48
50
|
"Graceful shutdown reached its maximum time, "
|
|
49
|
-
"but some jobs are still submitted"
|
|
51
|
+
"but some jobs are still submitted."
|
|
50
52
|
)
|
|
51
53
|
|
|
52
|
-
|
|
54
|
+
# Mark jobs as failed and update their logs.
|
|
55
|
+
jobs = (await session.execute(stm_objects)).scalars().all()
|
|
56
|
+
for job in jobs:
|
|
53
57
|
job.status = "failed"
|
|
54
58
|
job.log = (job.log or "") + "\nJob stopped due to app shutdown\n"
|
|
55
59
|
session.add(job)
|
fractal_server/config/_data.py
CHANGED
|
@@ -5,9 +5,10 @@ from pydantic import model_validator
|
|
|
5
5
|
from pydantic_settings import BaseSettings
|
|
6
6
|
from pydantic_settings import SettingsConfigDict
|
|
7
7
|
|
|
8
|
-
from ._settings_config import SETTINGS_CONFIG_DICT
|
|
9
8
|
from fractal_server.types import AbsolutePathStr
|
|
10
9
|
|
|
10
|
+
from ._settings_config import SETTINGS_CONFIG_DICT
|
|
11
|
+
|
|
11
12
|
|
|
12
13
|
class DataAuthScheme(StrEnum):
|
|
13
14
|
VIEWER_PATHS = "viewer-paths"
|
|
@@ -18,37 +19,47 @@ class DataAuthScheme(StrEnum):
|
|
|
18
19
|
class DataSettings(BaseSettings):
|
|
19
20
|
"""
|
|
20
21
|
Settings for the `fractal-data` integration.
|
|
21
|
-
"""
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
FRACTAL_DATA_AUTH_SCHEME: DataAuthScheme = "none"
|
|
26
|
-
"""
|
|
27
|
-
Defines how the list of allowed viewer paths is built.
|
|
23
|
+
See https://github.com/fractal-analytics-platform/fractal-data.
|
|
28
24
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
Attributes:
|
|
26
|
+
FRACTAL_DATA_AUTH_SCHEME:
|
|
27
|
+
Defines how the list of allowed viewer paths is built.
|
|
32
28
|
|
|
33
|
-
|
|
29
|
+
This variable affects the
|
|
30
|
+
`GET /auth/current-user/allowed-viewer-paths/` response, which is
|
|
31
|
+
then consumed by
|
|
32
|
+
[fractal-data](https://github.com/fractal-analytics-platform/fractal-data).
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
Options:
|
|
35
|
+
<ul>
|
|
36
|
+
<li> `"viewer-paths"`: The list of allowed viewer paths will
|
|
37
|
+
include the user's `project_dir` along with any path
|
|
38
|
+
defined in UserGroups `viewer_paths` attributes.
|
|
39
|
+
</li>
|
|
40
|
+
<li> `"users-folders"`: The list will consist of the user's
|
|
41
|
+
`project_dir` and a user-specific folder. The user folder
|
|
42
|
+
is constructed by concatenating the base folder
|
|
43
|
+
`FRACTAL_DATA_BASE_FOLDER` with the user's profile
|
|
44
|
+
`username`.
|
|
45
|
+
</li>
|
|
46
|
+
<li> `"none"`: An empty list will be returned, indicating no
|
|
47
|
+
access to viewer paths. Useful when vizarr viewer is not
|
|
48
|
+
used.
|
|
49
|
+
</li>
|
|
50
|
+
</ul>
|
|
51
|
+
FRACTAL_DATA_BASE_FOLDER:
|
|
52
|
+
Base path to Zarr files that will be served by
|
|
53
|
+
fractal-vizarr-viewer.
|
|
54
|
+
This variable is required and used only when
|
|
55
|
+
`FRACTAL_DATA_AUTHORIZATION_SCHEME` is set to `"users-folders"`.
|
|
44
56
|
"""
|
|
45
57
|
|
|
58
|
+
model_config = SettingsConfigDict(**SETTINGS_CONFIG_DICT)
|
|
59
|
+
|
|
60
|
+
FRACTAL_DATA_AUTH_SCHEME: DataAuthScheme = "none"
|
|
61
|
+
|
|
46
62
|
FRACTAL_DATA_BASE_FOLDER: AbsolutePathStr | None = None
|
|
47
|
-
"""
|
|
48
|
-
Base path to Zarr files that will be served by fractal-vizarr-viewer;
|
|
49
|
-
This variable is required and used only when
|
|
50
|
-
FRACTAL_DATA_AUTHORIZATION_SCHEME is set to "users-folders".
|
|
51
|
-
"""
|
|
52
63
|
|
|
53
64
|
@model_validator(mode="after")
|
|
54
65
|
def check(self: Self) -> Self:
|
|
@@ -1,45 +1,44 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
1
3
|
from pydantic import SecretStr
|
|
2
4
|
from pydantic.types import NonNegativeInt
|
|
3
5
|
from pydantic_settings import BaseSettings
|
|
4
6
|
from pydantic_settings import SettingsConfigDict
|
|
5
7
|
from sqlalchemy.engine import URL
|
|
6
8
|
|
|
7
|
-
from ._settings_config import SETTINGS_CONFIG_DICT
|
|
8
9
|
from fractal_server.types import NonEmptyStr
|
|
9
10
|
|
|
11
|
+
from ._settings_config import SETTINGS_CONFIG_DICT
|
|
12
|
+
|
|
10
13
|
|
|
11
14
|
class DatabaseSettings(BaseSettings):
|
|
12
15
|
"""
|
|
13
16
|
Minimal set of configurations needed for operating on the database (e.g
|
|
14
17
|
for schema migrations).
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
DB_ECHO:
|
|
21
|
+
If `"true"`, make database operations verbose.
|
|
22
|
+
POSTGRES_USER:
|
|
23
|
+
User to use when connecting to the PostgreSQL database.
|
|
24
|
+
POSTGRES_PASSWORD:
|
|
25
|
+
Password to use when connecting to the PostgreSQL database.
|
|
26
|
+
POSTGRES_HOST:
|
|
27
|
+
URL to the PostgreSQL server or path to a UNIX domain socket.
|
|
28
|
+
POSTGRES_PORT:
|
|
29
|
+
Port number to use when connecting to the PostgreSQL server.
|
|
30
|
+
POSTGRES_DB:
|
|
31
|
+
Name of the PostgreSQL database to connect to.
|
|
15
32
|
"""
|
|
16
33
|
|
|
17
34
|
model_config = SettingsConfigDict(**SETTINGS_CONFIG_DICT)
|
|
18
35
|
|
|
19
|
-
DB_ECHO:
|
|
20
|
-
"""
|
|
21
|
-
If `True`, make database operations verbose.
|
|
22
|
-
"""
|
|
36
|
+
DB_ECHO: Literal["true", "false"] = "false"
|
|
23
37
|
POSTGRES_USER: NonEmptyStr | None = None
|
|
24
|
-
"""
|
|
25
|
-
User to use when connecting to the PostgreSQL database.
|
|
26
|
-
"""
|
|
27
38
|
POSTGRES_PASSWORD: SecretStr | None = None
|
|
28
|
-
"""
|
|
29
|
-
Password to use when connecting to the PostgreSQL database.
|
|
30
|
-
"""
|
|
31
39
|
POSTGRES_HOST: NonEmptyStr = "localhost"
|
|
32
|
-
"""
|
|
33
|
-
URL to the PostgreSQL server or path to a UNIX domain socket.
|
|
34
|
-
"""
|
|
35
40
|
POSTGRES_PORT: NonNegativeInt = 5432
|
|
36
|
-
"""
|
|
37
|
-
Port number to use when connecting to the PostgreSQL server.
|
|
38
|
-
"""
|
|
39
41
|
POSTGRES_DB: NonEmptyStr
|
|
40
|
-
"""
|
|
41
|
-
Name of the PostgreSQL database to connect to.
|
|
42
|
-
"""
|
|
43
42
|
|
|
44
43
|
@property
|
|
45
44
|
def DATABASE_URL(self) -> URL:
|
fractal_server/config/_email.py
CHANGED
|
@@ -4,8 +4,8 @@ from typing import Self
|
|
|
4
4
|
from pydantic import BaseModel
|
|
5
5
|
from pydantic import EmailStr
|
|
6
6
|
from pydantic import Field
|
|
7
|
-
from pydantic import model_validator
|
|
8
7
|
from pydantic import SecretStr
|
|
8
|
+
from pydantic import model_validator
|
|
9
9
|
from pydantic_settings import BaseSettings
|
|
10
10
|
from pydantic_settings import SettingsConfigDict
|
|
11
11
|
|
|
@@ -17,14 +17,14 @@ class PublicEmailSettings(BaseModel):
|
|
|
17
17
|
Schema for `EmailSettings.public`, namely the ready-to-use settings.
|
|
18
18
|
|
|
19
19
|
Attributes:
|
|
20
|
-
sender: Sender email address
|
|
21
|
-
recipients: List of recipients email address
|
|
22
|
-
smtp_server: SMTP server address
|
|
23
|
-
port: SMTP server port
|
|
24
|
-
password: Sender password
|
|
25
|
-
instance_name: Name of SMTP server instance
|
|
26
|
-
use_starttls: Whether to use the security protocol
|
|
27
|
-
use_login: Whether to use login
|
|
20
|
+
sender: Sender email address.
|
|
21
|
+
recipients: List of recipients email address.
|
|
22
|
+
smtp_server: SMTP server address.
|
|
23
|
+
port: SMTP server port.
|
|
24
|
+
password: Sender password.
|
|
25
|
+
instance_name: Name of SMTP server instance.
|
|
26
|
+
use_starttls: Whether to use the security protocol.
|
|
27
|
+
use_login: Whether to use login.
|
|
28
28
|
"""
|
|
29
29
|
|
|
30
30
|
sender: EmailStr
|
|
@@ -40,50 +40,42 @@ class PublicEmailSettings(BaseModel):
|
|
|
40
40
|
class EmailSettings(BaseSettings):
|
|
41
41
|
"""
|
|
42
42
|
Class with settings for email-sending feature.
|
|
43
|
+
|
|
44
|
+
Attributes:
|
|
45
|
+
FRACTAL_EMAIL_SENDER:
|
|
46
|
+
Address of the OAuth-signup email sender.
|
|
47
|
+
FRACTAL_EMAIL_PASSWORD:
|
|
48
|
+
Password for the OAuth-signup email sender.
|
|
49
|
+
FRACTAL_EMAIL_SMTP_SERVER:
|
|
50
|
+
SMTP server for the OAuth-signup emails.
|
|
51
|
+
FRACTAL_EMAIL_SMTP_PORT:
|
|
52
|
+
SMTP server port for the OAuth-signup emails.
|
|
53
|
+
FRACTAL_EMAIL_INSTANCE_NAME:
|
|
54
|
+
Fractal instance name, to be included in the OAuth-signup emails.
|
|
55
|
+
FRACTAL_EMAIL_RECIPIENTS:
|
|
56
|
+
Comma-separated list of recipients of the OAuth-signup emails.
|
|
57
|
+
FRACTAL_EMAIL_USE_STARTTLS:
|
|
58
|
+
Whether to use StartTLS when using the SMTP server.
|
|
59
|
+
FRACTAL_EMAIL_USE_LOGIN:
|
|
60
|
+
Whether to use login when using the SMTP server.
|
|
61
|
+
If 'true', FRACTAL_EMAIL_PASSWORD must be provided.
|
|
43
62
|
"""
|
|
44
63
|
|
|
45
64
|
model_config = SettingsConfigDict(**SETTINGS_CONFIG_DICT)
|
|
46
65
|
|
|
47
66
|
FRACTAL_EMAIL_SENDER: EmailStr | None = None
|
|
48
|
-
"""
|
|
49
|
-
Address of the OAuth-signup email sender.
|
|
50
|
-
"""
|
|
51
67
|
FRACTAL_EMAIL_PASSWORD: SecretStr | None = None
|
|
52
|
-
"""
|
|
53
|
-
Password for the OAuth-signup email sender.
|
|
54
|
-
"""
|
|
55
68
|
FRACTAL_EMAIL_SMTP_SERVER: str | None = None
|
|
56
|
-
"""
|
|
57
|
-
SMTP server for the OAuth-signup emails.
|
|
58
|
-
"""
|
|
59
69
|
FRACTAL_EMAIL_SMTP_PORT: int | None = None
|
|
60
|
-
"""
|
|
61
|
-
SMTP server port for the OAuth-signup emails.
|
|
62
|
-
"""
|
|
63
70
|
FRACTAL_EMAIL_INSTANCE_NAME: str | None = None
|
|
64
|
-
"""
|
|
65
|
-
Fractal instance name, to be included in the OAuth-signup emails.
|
|
66
|
-
"""
|
|
67
71
|
FRACTAL_EMAIL_RECIPIENTS: str | None = None
|
|
68
|
-
"""
|
|
69
|
-
Comma-separated list of recipients of the OAuth-signup emails.
|
|
70
|
-
"""
|
|
71
72
|
FRACTAL_EMAIL_USE_STARTTLS: Literal["true", "false"] = "true"
|
|
72
|
-
"""
|
|
73
|
-
Whether to use StartTLS when using the SMTP server.
|
|
74
|
-
Accepted values: 'true', 'false'.
|
|
75
|
-
"""
|
|
76
73
|
FRACTAL_EMAIL_USE_LOGIN: Literal["true", "false"] = "true"
|
|
77
|
-
"""
|
|
78
|
-
Whether to use login when using the SMTP server.
|
|
79
|
-
If 'true', FRACTAL_EMAIL_PASSWORD must be provided.
|
|
80
|
-
Accepted values: 'true', 'false'.
|
|
81
|
-
"""
|
|
82
74
|
|
|
83
75
|
public: PublicEmailSettings | None = None
|
|
84
76
|
"""
|
|
85
|
-
The validated field which is actually used in `fractal-server
|
|
86
|
-
|
|
77
|
+
The validated field which is actually used in `fractal-server`,
|
|
78
|
+
automatically populated upon creation.
|
|
87
79
|
"""
|
|
88
80
|
|
|
89
81
|
@model_validator(mode="after")
|