fractal-server 2.17.1a1__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 -0
- fractal_server/app/models/linkuserproject.py +3 -1
- fractal_server/app/models/security.py +21 -3
- 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 +4 -10
- fractal_server/app/models/v2/resource.py +4 -0
- fractal_server/app/models/v2/task_group.py +4 -3
- 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/config/_data.py +36 -25
- fractal_server/config/_database.py +19 -20
- fractal_server/config/_email.py +30 -38
- fractal_server/config/_main.py +33 -52
- 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 +1 -1
- fractal_server/migrations/versions/47351f8c7ebc_drop_dataset_filters.py +1 -0
- fractal_server/migrations/versions/49d0856e9569_drop_table.py +2 -3
- 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 +1 -1
- 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 +1 -1
- 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.1a1.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/routes/admin/v2/project.py +0 -41
- fractal_server-2.17.1a1.dist-info/RECORD +0 -264
- {fractal_server-2.17.1a1.dist-info → fractal_server-2.17.2.dist-info}/WHEEL +0 -0
- {fractal_server-2.17.1a1.dist-info → fractal_server-2.17.2.dist-info}/entry_points.txt +0 -0
- {fractal_server-2.17.1a1.dist-info → fractal_server-2.17.2.dist-info}/licenses/LICENSE +0 -0
fractal_server/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__VERSION__ = "2.17.
|
|
1
|
+
__VERSION__ = "2.17.2"
|
fractal_server/__main__.py
CHANGED
|
@@ -7,7 +7,6 @@ from pathlib import Path
|
|
|
7
7
|
import uvicorn
|
|
8
8
|
from pydantic import ValidationError
|
|
9
9
|
|
|
10
|
-
|
|
11
10
|
parser = ap.ArgumentParser(description="fractal-server commands")
|
|
12
11
|
|
|
13
12
|
subparsers = parser.add_subparsers(title="Commands", dest="cmd", required=True)
|
|
@@ -48,9 +47,7 @@ openapi_parser.add_argument(
|
|
|
48
47
|
# fractalctl set-db
|
|
49
48
|
set_db_parser = subparsers.add_parser(
|
|
50
49
|
"set-db",
|
|
51
|
-
description=
|
|
52
|
-
"Initialise/upgrade database schemas and create first group&user."
|
|
53
|
-
),
|
|
50
|
+
description="Initialise/upgrade database schemas.",
|
|
54
51
|
)
|
|
55
52
|
|
|
56
53
|
# fractalctl init-db-data
|
|
@@ -114,12 +111,13 @@ def set_db():
|
|
|
114
111
|
Call alembic to upgrade to the latest migration.
|
|
115
112
|
Ref: https://stackoverflow.com/a/56683030/283972
|
|
116
113
|
"""
|
|
117
|
-
from
|
|
118
|
-
from fractal_server.config import get_db_settings
|
|
114
|
+
from pathlib import Path
|
|
119
115
|
|
|
120
116
|
import alembic.config
|
|
121
|
-
|
|
117
|
+
|
|
122
118
|
import fractal_server
|
|
119
|
+
from fractal_server.config import get_db_settings
|
|
120
|
+
from fractal_server.syringe import Inject
|
|
123
121
|
|
|
124
122
|
# Validate DB settings
|
|
125
123
|
Inject(get_db_settings)
|
|
@@ -140,15 +138,18 @@ def init_db_data(
|
|
|
140
138
|
admin_password: str | None = None,
|
|
141
139
|
admin_project_dir: str | None = None,
|
|
142
140
|
) -> None:
|
|
143
|
-
from
|
|
144
|
-
from
|
|
141
|
+
from sqlalchemy import func
|
|
142
|
+
from sqlalchemy import select
|
|
143
|
+
|
|
145
144
|
from fractal_server.app.db import get_sync_db
|
|
146
|
-
from
|
|
145
|
+
from fractal_server.app.models import Profile
|
|
146
|
+
from fractal_server.app.models import Resource
|
|
147
147
|
from fractal_server.app.models.security import UserOAuth
|
|
148
|
-
from fractal_server.app.models import Resource, Profile
|
|
149
|
-
from fractal_server.app.schemas.v2.resource import cast_serialize_resource
|
|
150
|
-
from fractal_server.app.schemas.v2.profile import cast_serialize_profile
|
|
151
148
|
from fractal_server.app.schemas.v2 import ResourceType
|
|
149
|
+
from fractal_server.app.schemas.v2.profile import cast_serialize_profile
|
|
150
|
+
from fractal_server.app.schemas.v2.resource import cast_serialize_resource
|
|
151
|
+
from fractal_server.app.security import _create_first_group
|
|
152
|
+
from fractal_server.app.security import _create_first_user
|
|
152
153
|
|
|
153
154
|
# Create default group and user
|
|
154
155
|
print()
|
|
@@ -185,9 +186,7 @@ def init_db_data(
|
|
|
185
186
|
if resource and profile:
|
|
186
187
|
with next(get_sync_db()) as db:
|
|
187
188
|
# Preliminary check
|
|
188
|
-
num_resources = db.execute(
|
|
189
|
-
select(func.count(Resource.id))
|
|
190
|
-
).scalar()
|
|
189
|
+
num_resources = db.execute(select(func.count(Resource.id))).scalar()
|
|
191
190
|
if num_resources != 0:
|
|
192
191
|
print(f"There exist already {num_resources=} resources. Exit.")
|
|
193
192
|
sys.exit(1)
|
|
@@ -270,10 +269,12 @@ def update_db_data():
|
|
|
270
269
|
Apply data migrations.
|
|
271
270
|
"""
|
|
272
271
|
|
|
273
|
-
import
|
|
272
|
+
import os
|
|
274
273
|
from importlib import import_module
|
|
274
|
+
|
|
275
275
|
from packaging.version import parse
|
|
276
|
-
|
|
276
|
+
|
|
277
|
+
import fractal_server
|
|
277
278
|
|
|
278
279
|
def _slugify_version(raw_version: str) -> str:
|
|
279
280
|
v = parse(raw_version)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
`db` module, loosely adapted from
|
|
3
3
|
https://testdriven.io/blog/fastapi-sqlmodel/#async-sqlmodel
|
|
4
4
|
"""
|
|
5
|
+
|
|
5
6
|
from collections.abc import AsyncGenerator
|
|
6
7
|
from collections.abc import Generator
|
|
7
8
|
|
|
@@ -15,7 +16,6 @@ from fractal_server.config import get_db_settings
|
|
|
15
16
|
from fractal_server.logger import set_logger
|
|
16
17
|
from fractal_server.syringe import Inject
|
|
17
18
|
|
|
18
|
-
|
|
19
19
|
logger = set_logger(__name__)
|
|
20
20
|
|
|
21
21
|
|
|
@@ -46,7 +46,7 @@ class DB:
|
|
|
46
46
|
|
|
47
47
|
cls._engine_async = create_async_engine(
|
|
48
48
|
db_settings.DATABASE_URL,
|
|
49
|
-
echo=db_settings.DB_ECHO,
|
|
49
|
+
echo=(db_settings.DB_ECHO == "true"),
|
|
50
50
|
future=True,
|
|
51
51
|
pool_pre_ping=True,
|
|
52
52
|
)
|
|
@@ -63,7 +63,7 @@ class DB:
|
|
|
63
63
|
|
|
64
64
|
cls._engine_sync = create_engine(
|
|
65
65
|
db_settings.DATABASE_URL,
|
|
66
|
-
echo=db_settings.DB_ECHO,
|
|
66
|
+
echo=(db_settings.DB_ECHO == "true"),
|
|
67
67
|
future=True,
|
|
68
68
|
pool_pre_ping=True,
|
|
69
69
|
)
|
|
@@ -3,6 +3,7 @@ Note that this module is imported from `fractal_server/migrations/env.py`,
|
|
|
3
3
|
thus we should always export all relevant database models from here or they
|
|
4
4
|
will not be picked up by alembic.
|
|
5
5
|
"""
|
|
6
|
+
|
|
6
7
|
from .linkusergroup import LinkUserGroup # noqa: F401
|
|
7
8
|
from .linkuserproject import LinkUserProjectV2 # noqa: F401
|
|
8
9
|
from .security import * # noqa
|
|
@@ -7,5 +7,7 @@ class LinkUserProjectV2(SQLModel, table=True):
|
|
|
7
7
|
Crossing table between User and ProjectV2
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
project_id: int = Field(
|
|
10
|
+
project_id: int = Field(
|
|
11
|
+
foreign_key="projectv2.id", primary_key=True, ondelete="CASCADE"
|
|
12
|
+
)
|
|
11
13
|
user_id: int = Field(foreign_key="user_oauth.id", primary_key=True)
|
|
@@ -52,21 +52,39 @@ class UserOAuth(SQLModel, table=True):
|
|
|
52
52
|
"""
|
|
53
53
|
ORM model for the `user_oauth` database table.
|
|
54
54
|
|
|
55
|
-
This class is a modification of
|
|
56
|
-
fastapi_users_db_sqlmodel.
|
|
57
|
-
|
|
55
|
+
This class is a modification of
|
|
56
|
+
[`SQLModelBaseUserDB`](https://github.com/fastapi-users/fastapi-users-db-sqlmodel/blob/83980d7f20886120f4636a102ab1822b4c366f63/fastapi_users_db_sqlmodel/__init__.py#L15-L32)
|
|
57
|
+
from `fastapi_users_db_sqlmodel`.
|
|
58
|
+
Original Copyright: 2022 François Voron, released under MIT licence.
|
|
59
|
+
|
|
60
|
+
Note that several class attributes are
|
|
61
|
+
[the default ones from `fastapi-users`
|
|
62
|
+
](https://fastapi-users.github.io/fastapi-users/latest/configuration/schemas/).
|
|
58
63
|
|
|
59
64
|
Attributes:
|
|
60
65
|
id:
|
|
61
66
|
email:
|
|
62
67
|
hashed_password:
|
|
63
68
|
is_active:
|
|
69
|
+
If this is `False`, the user has no access to the `/api/v2/`
|
|
70
|
+
endpoints.
|
|
64
71
|
is_superuser:
|
|
65
72
|
is_verified:
|
|
73
|
+
If this is `False`, the user has no access to the `/api/v2/`
|
|
74
|
+
endpoints.
|
|
66
75
|
oauth_accounts:
|
|
67
76
|
profile_id:
|
|
77
|
+
Foreign key linking the user to a `Profile`. If this is unset,
|
|
78
|
+
the user has no access to the `/api/v2/` endpoints.
|
|
68
79
|
project_dir:
|
|
80
|
+
Absolute path of the user's project directory. This is used (A) as
|
|
81
|
+
a default base folder for the `zarr_dir` of new datasets (where
|
|
82
|
+
the output Zarr are located), and (B) as a folder which is included
|
|
83
|
+
by default in the paths that a user is allowed to stream (if the
|
|
84
|
+
`fractal-data` integration is set up).
|
|
85
|
+
two goals:
|
|
69
86
|
slurm_accounts:
|
|
87
|
+
List of SLURM accounts that the user can select upon running a job.
|
|
70
88
|
"""
|
|
71
89
|
|
|
72
90
|
model_config = ConfigDict(from_attributes=True)
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"""
|
|
2
2
|
v2 `models` module
|
|
3
3
|
"""
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
from fractal_server.app.models.linkuserproject import LinkUserProjectV2
|
|
6
|
+
|
|
5
7
|
from .accounting import AccountingRecord
|
|
6
8
|
from .accounting import AccountingRecordSlurm
|
|
7
9
|
from .dataset import DatasetV2
|
|
@@ -7,10 +7,14 @@ from sqlalchemy.types import DateTime
|
|
|
7
7
|
from sqlmodel import Field
|
|
8
8
|
from sqlmodel import SQLModel
|
|
9
9
|
|
|
10
|
-
from
|
|
10
|
+
from fractal_server.utils import get_timestamp
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class AccountingRecord(SQLModel, table=True):
|
|
14
|
+
"""
|
|
15
|
+
AccountingRecord table.
|
|
16
|
+
"""
|
|
17
|
+
|
|
14
18
|
id: int | None = Field(default=None, primary_key=True)
|
|
15
19
|
user_id: int = Field(foreign_key="user_oauth.id", nullable=False)
|
|
16
20
|
timestamp: datetime = Field(
|
|
@@ -22,6 +26,10 @@ class AccountingRecord(SQLModel, table=True):
|
|
|
22
26
|
|
|
23
27
|
|
|
24
28
|
class AccountingRecordSlurm(SQLModel, table=True):
|
|
29
|
+
"""
|
|
30
|
+
AccountingRecordSlurm table.
|
|
31
|
+
"""
|
|
32
|
+
|
|
25
33
|
id: int | None = Field(default=None, primary_key=True)
|
|
26
34
|
user_id: int = Field(foreign_key="user_oauth.id", nullable=False)
|
|
27
35
|
timestamp: datetime = Field(
|
|
@@ -9,10 +9,14 @@ from sqlmodel import Field
|
|
|
9
9
|
from sqlmodel import Relationship
|
|
10
10
|
from sqlmodel import SQLModel
|
|
11
11
|
|
|
12
|
-
from
|
|
12
|
+
from fractal_server.utils import get_timestamp
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class DatasetV2(SQLModel, table=True):
|
|
16
|
+
"""
|
|
17
|
+
Dataset table.
|
|
18
|
+
"""
|
|
19
|
+
|
|
16
20
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
17
21
|
|
|
18
22
|
id: int | None = Field(default=None, primary_key=True)
|
|
@@ -10,10 +10,14 @@ from sqlalchemy.types import DateTime
|
|
|
10
10
|
from sqlmodel import Field
|
|
11
11
|
from sqlmodel import SQLModel
|
|
12
12
|
|
|
13
|
-
from
|
|
13
|
+
from fractal_server.utils import get_timestamp
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class HistoryRun(SQLModel, table=True):
|
|
17
|
+
"""
|
|
18
|
+
HistoryRun table.
|
|
19
|
+
"""
|
|
20
|
+
|
|
17
21
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
18
22
|
|
|
19
23
|
id: int | None = Field(default=None, primary_key=True)
|
|
@@ -45,10 +49,15 @@ class HistoryRun(SQLModel, table=True):
|
|
|
45
49
|
|
|
46
50
|
|
|
47
51
|
class HistoryUnit(SQLModel, table=True):
|
|
52
|
+
"""
|
|
53
|
+
HistoryUnit table.
|
|
54
|
+
"""
|
|
55
|
+
|
|
48
56
|
id: int | None = Field(default=None, primary_key=True)
|
|
49
57
|
history_run_id: int = Field(
|
|
50
58
|
foreign_key="historyrun.id",
|
|
51
59
|
ondelete="CASCADE",
|
|
60
|
+
index=True,
|
|
52
61
|
)
|
|
53
62
|
|
|
54
63
|
logfile: str
|
|
@@ -60,6 +69,10 @@ class HistoryUnit(SQLModel, table=True):
|
|
|
60
69
|
|
|
61
70
|
|
|
62
71
|
class HistoryImageCache(SQLModel, table=True):
|
|
72
|
+
"""
|
|
73
|
+
HistoryImageCache table.
|
|
74
|
+
"""
|
|
75
|
+
|
|
63
76
|
zarr_url: str = Field(primary_key=True)
|
|
64
77
|
dataset_id: int = Field(
|
|
65
78
|
primary_key=True,
|
|
@@ -77,4 +90,5 @@ class HistoryImageCache(SQLModel, table=True):
|
|
|
77
90
|
latest_history_unit_id: int = Field(
|
|
78
91
|
foreign_key="historyunit.id",
|
|
79
92
|
ondelete="CASCADE",
|
|
93
|
+
index=True,
|
|
80
94
|
)
|
|
@@ -3,14 +3,43 @@ from sqlmodel import SQLModel
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class Profile(SQLModel, table=True):
|
|
6
|
+
"""
|
|
7
|
+
Profile table.
|
|
8
|
+
"""
|
|
9
|
+
|
|
6
10
|
id: int | None = Field(default=None, primary_key=True)
|
|
11
|
+
|
|
7
12
|
resource_id: int = Field(foreign_key="resource.id", ondelete="RESTRICT")
|
|
13
|
+
|
|
8
14
|
resource_type: str
|
|
15
|
+
"""
|
|
16
|
+
Type of resource (either `local`, `slurm_sudo` or `slurm_ssh`).
|
|
17
|
+
"""
|
|
9
18
|
|
|
10
19
|
name: str = Field(unique=True)
|
|
20
|
+
"""
|
|
21
|
+
Profile name.
|
|
22
|
+
"""
|
|
11
23
|
|
|
12
24
|
username: str | None = None
|
|
25
|
+
"""
|
|
26
|
+
Username to be impersonated, either via `sudo -u` or via `ssh`.
|
|
27
|
+
"""
|
|
28
|
+
|
|
13
29
|
ssh_key_path: str | None = None
|
|
30
|
+
"""
|
|
31
|
+
Path to the private SSH key of user `username` (only relevant if
|
|
32
|
+
`resource_type="slurm_ssh"`).
|
|
33
|
+
"""
|
|
14
34
|
|
|
15
35
|
jobs_remote_dir: str | None = None
|
|
36
|
+
"""
|
|
37
|
+
Remote path of the job folder (only relevant if
|
|
38
|
+
`resource_type="slurm_ssh"`).
|
|
39
|
+
"""
|
|
40
|
+
|
|
16
41
|
tasks_remote_dir: str | None = None
|
|
42
|
+
"""
|
|
43
|
+
Remote path of the task folder (only relevant if
|
|
44
|
+
`resource_type="slurm_ssh"`).
|
|
45
|
+
"""
|
|
@@ -3,15 +3,16 @@ from datetime import datetime
|
|
|
3
3
|
from sqlalchemy import Column
|
|
4
4
|
from sqlalchemy.types import DateTime
|
|
5
5
|
from sqlmodel import Field
|
|
6
|
-
from sqlmodel import Relationship
|
|
7
6
|
from sqlmodel import SQLModel
|
|
8
7
|
|
|
9
|
-
from .. import LinkUserProjectV2
|
|
10
|
-
from fractal_server.app.models import UserOAuth
|
|
11
8
|
from fractal_server.utils import get_timestamp
|
|
12
9
|
|
|
13
10
|
|
|
14
11
|
class ProjectV2(SQLModel, table=True):
|
|
12
|
+
"""
|
|
13
|
+
Project table.
|
|
14
|
+
"""
|
|
15
|
+
|
|
15
16
|
id: int | None = Field(default=None, primary_key=True)
|
|
16
17
|
name: str
|
|
17
18
|
|
|
@@ -20,10 +21,3 @@ class ProjectV2(SQLModel, table=True):
|
|
|
20
21
|
default_factory=get_timestamp,
|
|
21
22
|
sa_column=Column(DateTime(timezone=True), nullable=False),
|
|
22
23
|
)
|
|
23
|
-
|
|
24
|
-
user_list: list[UserOAuth] = Relationship(
|
|
25
|
-
link_model=LinkUserProjectV2,
|
|
26
|
-
sa_relationship_kwargs={
|
|
27
|
-
"lazy": "selectin",
|
|
28
|
-
},
|
|
29
|
-
)
|
|
@@ -8,9 +8,10 @@ from sqlmodel import Field
|
|
|
8
8
|
from sqlmodel import Relationship
|
|
9
9
|
from sqlmodel import SQLModel
|
|
10
10
|
|
|
11
|
-
from .task import TaskV2
|
|
12
11
|
from fractal_server.utils import get_timestamp
|
|
13
12
|
|
|
13
|
+
from .task import TaskV2
|
|
14
|
+
|
|
14
15
|
|
|
15
16
|
def _check_origin_not_pixi(origin: str):
|
|
16
17
|
"""
|
|
@@ -22,10 +23,10 @@ def _check_origin_not_pixi(origin: str):
|
|
|
22
23
|
|
|
23
24
|
def _create_dependency_string(pinned_versions: dict[str, str]) -> str:
|
|
24
25
|
"""
|
|
25
|
-
Expand e.g. `{"a": "1.2", "b": "3"}` into `"a==1.2 b==3"`.
|
|
26
|
+
Expand e.g. `{"a": "1.2", "b": "3"}` into `'"a==1.2" "b==3"'`.
|
|
26
27
|
"""
|
|
27
28
|
output = " ".join(
|
|
28
|
-
[f"{key}=={value}" for key, value in pinned_versions.items()]
|
|
29
|
+
[f'"{key}=={value}"' for key, value in pinned_versions.items()]
|
|
29
30
|
)
|
|
30
31
|
return output
|
|
31
32
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"""
|
|
2
2
|
`admin/v2` module
|
|
3
3
|
"""
|
|
4
|
+
|
|
4
5
|
from fastapi import APIRouter
|
|
5
6
|
|
|
6
7
|
from .accounting import router as accounting_router
|
|
7
8
|
from .impersonate import router as impersonate_router
|
|
8
9
|
from .job import router as job_router
|
|
9
10
|
from .profile import router as profile_router
|
|
10
|
-
from .project import router as project_router
|
|
11
11
|
from .resource import router as resource_router
|
|
12
12
|
from .task import router as task_router
|
|
13
13
|
from .task_group import router as task_group_router
|
|
@@ -17,7 +17,6 @@ router_admin_v2 = APIRouter()
|
|
|
17
17
|
|
|
18
18
|
router_admin_v2.include_router(accounting_router, prefix="/accounting")
|
|
19
19
|
router_admin_v2.include_router(job_router, prefix="/job")
|
|
20
|
-
router_admin_v2.include_router(project_router, prefix="/project")
|
|
21
20
|
router_admin_v2.include_router(task_router, prefix="/task")
|
|
22
21
|
router_admin_v2.include_router(task_group_router, prefix="/task-group")
|
|
23
22
|
router_admin_v2.include_router(
|
|
@@ -14,9 +14,9 @@ from fractal_server.app.models import UserOAuth
|
|
|
14
14
|
from fractal_server.app.models.v2 import AccountingRecord
|
|
15
15
|
from fractal_server.app.models.v2 import AccountingRecordSlurm
|
|
16
16
|
from fractal_server.app.routes.auth import current_superuser_act
|
|
17
|
-
from fractal_server.app.routes.pagination import get_pagination_params
|
|
18
17
|
from fractal_server.app.routes.pagination import PaginationRequest
|
|
19
18
|
from fractal_server.app.routes.pagination import PaginationResponse
|
|
19
|
+
from fractal_server.app.routes.pagination import get_pagination_params
|
|
20
20
|
from fractal_server.app.schemas.v2 import AccountingRecordRead
|
|
21
21
|
|
|
22
22
|
|
|
@@ -12,17 +12,17 @@ from sqlmodel import select
|
|
|
12
12
|
|
|
13
13
|
from fractal_server.app.db import AsyncSession
|
|
14
14
|
from fractal_server.app.db import get_async_db
|
|
15
|
+
from fractal_server.app.models import LinkUserProjectV2
|
|
15
16
|
from fractal_server.app.models import UserOAuth
|
|
16
17
|
from fractal_server.app.models.v2 import HistoryRun
|
|
17
18
|
from fractal_server.app.models.v2 import HistoryUnit
|
|
18
19
|
from fractal_server.app.models.v2 import JobV2
|
|
19
|
-
from fractal_server.app.models.v2 import ProjectV2
|
|
20
20
|
from fractal_server.app.routes.auth import current_superuser_act
|
|
21
21
|
from fractal_server.app.routes.aux._job import _write_shutdown_file
|
|
22
22
|
from fractal_server.app.routes.aux._runner import _check_shutdown_is_supported
|
|
23
|
-
from fractal_server.app.routes.pagination import get_pagination_params
|
|
24
23
|
from fractal_server.app.routes.pagination import PaginationRequest
|
|
25
24
|
from fractal_server.app.routes.pagination import PaginationResponse
|
|
25
|
+
from fractal_server.app.routes.pagination import get_pagination_params
|
|
26
26
|
from fractal_server.app.schemas.v2 import HistoryUnitStatus
|
|
27
27
|
from fractal_server.app.schemas.v2 import JobReadV2
|
|
28
28
|
from fractal_server.app.schemas.v2 import JobStatusTypeV2
|
|
@@ -52,7 +52,7 @@ async def view_job(
|
|
|
52
52
|
db: AsyncSession = Depends(get_async_db),
|
|
53
53
|
) -> PaginationResponse[JobReadV2]:
|
|
54
54
|
"""
|
|
55
|
-
Query `
|
|
55
|
+
Query `JobV2` table.
|
|
56
56
|
|
|
57
57
|
Args:
|
|
58
58
|
id: If not `None`, select a given `applyworkflow.id`.
|
|
@@ -84,12 +84,12 @@ async def view_job(
|
|
|
84
84
|
stm = stm.where(JobV2.id == id)
|
|
85
85
|
stm_count = stm_count.where(JobV2.id == id)
|
|
86
86
|
if user_id is not None:
|
|
87
|
-
stm = stm.join(
|
|
88
|
-
|
|
89
|
-
)
|
|
90
|
-
stm_count = stm_count.join(
|
|
91
|
-
|
|
92
|
-
)
|
|
87
|
+
stm = stm.join(
|
|
88
|
+
LinkUserProjectV2, LinkUserProjectV2.project_id == JobV2.project_id
|
|
89
|
+
).where(LinkUserProjectV2.user_id == user_id)
|
|
90
|
+
stm_count = stm_count.join(
|
|
91
|
+
LinkUserProjectV2, LinkUserProjectV2.project_id == JobV2.project_id
|
|
92
|
+
).where(LinkUserProjectV2.user_id == user_id)
|
|
93
93
|
if project_id is not None:
|
|
94
94
|
stm = stm.where(JobV2.project_id == project_id)
|
|
95
95
|
stm_count = stm_count.where(JobV2.project_id == project_id)
|
|
@@ -6,8 +6,6 @@ from fastapi import status
|
|
|
6
6
|
from sqlmodel import func
|
|
7
7
|
from sqlmodel import select
|
|
8
8
|
|
|
9
|
-
from ._aux_functions import _check_profile_name
|
|
10
|
-
from ._aux_functions import _get_profile_or_404
|
|
11
9
|
from fractal_server.app.db import AsyncSession
|
|
12
10
|
from fractal_server.app.db import get_async_db
|
|
13
11
|
from fractal_server.app.models import Profile
|
|
@@ -16,6 +14,9 @@ from fractal_server.app.routes.auth import current_superuser_act
|
|
|
16
14
|
from fractal_server.app.schemas.v2 import ProfileCreate
|
|
17
15
|
from fractal_server.app.schemas.v2 import ProfileRead
|
|
18
16
|
|
|
17
|
+
from ._aux_functions import _check_profile_name
|
|
18
|
+
from ._aux_functions import _get_profile_or_404
|
|
19
|
+
|
|
19
20
|
router = APIRouter()
|
|
20
21
|
|
|
21
22
|
|
|
@@ -6,9 +6,6 @@ from fastapi import status
|
|
|
6
6
|
from sqlalchemy.exc import IntegrityError
|
|
7
7
|
from sqlmodel import select
|
|
8
8
|
|
|
9
|
-
from ._aux_functions import _check_resource_name
|
|
10
|
-
from ._aux_functions import _get_resource_or_404
|
|
11
|
-
from .profile import _check_profile_name
|
|
12
9
|
from fractal_server.app.db import AsyncSession
|
|
13
10
|
from fractal_server.app.db import get_async_db
|
|
14
11
|
from fractal_server.app.models import UserOAuth
|
|
@@ -22,6 +19,10 @@ from fractal_server.app.schemas.v2 import ResourceRead
|
|
|
22
19
|
from fractal_server.config import get_settings
|
|
23
20
|
from fractal_server.syringe import Inject
|
|
24
21
|
|
|
22
|
+
from ._aux_functions import _check_resource_name
|
|
23
|
+
from ._aux_functions import _get_resource_or_404
|
|
24
|
+
from .profile import _check_profile_name
|
|
25
|
+
|
|
25
26
|
router = APIRouter()
|
|
26
27
|
|
|
27
28
|
|
|
@@ -47,8 +48,7 @@ def _check_type_match_or_422(new_resource: ResourceCreate) -> None:
|
|
|
47
48
|
raise HTTPException(
|
|
48
49
|
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
|
49
50
|
detail=(
|
|
50
|
-
f"{settings.FRACTAL_RUNNER_BACKEND=} != "
|
|
51
|
-
f"{new_resource.type=}"
|
|
51
|
+
f"{settings.FRACTAL_RUNNER_BACKEND=} != {new_resource.type=}"
|
|
52
52
|
),
|
|
53
53
|
)
|
|
54
54
|
|
|
@@ -8,15 +8,16 @@ from sqlmodel import select
|
|
|
8
8
|
|
|
9
9
|
from fractal_server.app.db import AsyncSession
|
|
10
10
|
from fractal_server.app.db import get_async_db
|
|
11
|
+
from fractal_server.app.models import LinkUserProjectV2
|
|
11
12
|
from fractal_server.app.models import TaskGroupV2
|
|
12
13
|
from fractal_server.app.models import UserOAuth
|
|
13
14
|
from fractal_server.app.models.v2 import TaskV2
|
|
14
15
|
from fractal_server.app.models.v2 import WorkflowTaskV2
|
|
15
16
|
from fractal_server.app.models.v2 import WorkflowV2
|
|
16
17
|
from fractal_server.app.routes.auth import current_superuser_act
|
|
17
|
-
from fractal_server.app.routes.pagination import get_pagination_params
|
|
18
18
|
from fractal_server.app.routes.pagination import PaginationRequest
|
|
19
19
|
from fractal_server.app.routes.pagination import PaginationResponse
|
|
20
|
+
from fractal_server.app.routes.pagination import get_pagination_params
|
|
20
21
|
from fractal_server.app.schemas.v2.task import TaskType
|
|
21
22
|
|
|
22
23
|
router = APIRouter()
|
|
@@ -106,16 +107,12 @@ async def query_tasks(
|
|
|
106
107
|
stm = stm.where(TaskV2.authors.icontains(author))
|
|
107
108
|
stm_count = stm_count.where(TaskV2.authors.icontains(author))
|
|
108
109
|
if resource_id is not None:
|
|
109
|
-
stm = (
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
stm_count.join(TaskGroupV2)
|
|
116
|
-
.where(TaskGroupV2.id == TaskV2.taskgroupv2_id)
|
|
117
|
-
.where(TaskGroupV2.resource_id == resource_id)
|
|
118
|
-
)
|
|
110
|
+
stm = stm.join(
|
|
111
|
+
TaskGroupV2, TaskGroupV2.id == TaskV2.taskgroupv2_id
|
|
112
|
+
).where(TaskGroupV2.resource_id == resource_id)
|
|
113
|
+
stm_count = stm_count.join(
|
|
114
|
+
TaskGroupV2, TaskGroupV2.id == TaskV2.taskgroupv2_id
|
|
115
|
+
).where(TaskGroupV2.resource_id == resource_id)
|
|
119
116
|
|
|
120
117
|
# Find total number of elements
|
|
121
118
|
res_total_count = await db.execute(stm_count)
|
|
@@ -133,13 +130,30 @@ async def query_tasks(
|
|
|
133
130
|
for task in task_list:
|
|
134
131
|
stm = (
|
|
135
132
|
select(WorkflowV2)
|
|
136
|
-
.join(
|
|
137
|
-
|
|
133
|
+
.join(
|
|
134
|
+
WorkflowTaskV2,
|
|
135
|
+
WorkflowTaskV2.workflow_id == WorkflowV2.id,
|
|
136
|
+
)
|
|
138
137
|
.where(WorkflowTaskV2.task_id == task.id)
|
|
139
138
|
)
|
|
140
139
|
res = await db.execute(stm)
|
|
141
140
|
wf_list = res.scalars().all()
|
|
142
141
|
|
|
142
|
+
project_users = {}
|
|
143
|
+
for project_id in set([workflow.project_id for workflow in wf_list]):
|
|
144
|
+
res = await db.execute(
|
|
145
|
+
select(UserOAuth.id, UserOAuth.email)
|
|
146
|
+
.join(
|
|
147
|
+
LinkUserProjectV2,
|
|
148
|
+
LinkUserProjectV2.user_id == UserOAuth.id,
|
|
149
|
+
)
|
|
150
|
+
.where(LinkUserProjectV2.project_id == project_id)
|
|
151
|
+
)
|
|
152
|
+
project_users[project_id] = [
|
|
153
|
+
ProjectUser(id=p_user[0], email=p_user[1])
|
|
154
|
+
for p_user in res.all()
|
|
155
|
+
]
|
|
156
|
+
|
|
143
157
|
task_info_list.append(
|
|
144
158
|
dict(
|
|
145
159
|
task=task.model_dump(),
|
|
@@ -149,16 +163,12 @@ async def query_tasks(
|
|
|
149
163
|
workflow_name=workflow.name,
|
|
150
164
|
project_id=workflow.project.id,
|
|
151
165
|
project_name=workflow.project.name,
|
|
152
|
-
project_users=[
|
|
153
|
-
dict(id=user.id, email=user.email)
|
|
154
|
-
for user in workflow.project.user_list
|
|
155
|
-
],
|
|
166
|
+
project_users=project_users[workflow.project_id],
|
|
156
167
|
)
|
|
157
168
|
for workflow in wf_list
|
|
158
169
|
],
|
|
159
170
|
)
|
|
160
171
|
)
|
|
161
|
-
|
|
162
172
|
return PaginationResponse[TaskV2Info](
|
|
163
173
|
total_count=total_count,
|
|
164
174
|
page_size=page_size,
|
|
@@ -199,8 +199,7 @@ async def reactivate_task_group(
|
|
|
199
199
|
raise HTTPException(
|
|
200
200
|
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
|
201
201
|
detail=(
|
|
202
|
-
"Cannot reactivate a task group with "
|
|
203
|
-
f"{task_group.env_info=}."
|
|
202
|
+
f"Cannot reactivate a task group with {task_group.env_info=}."
|
|
204
203
|
),
|
|
205
204
|
)
|
|
206
205
|
|