fractal-server 2.17.1a1__py3-none-any.whl → 2.18.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fractal_server/__init__.py +1 -1
- fractal_server/__main__.py +21 -19
- fractal_server/app/db/__init__.py +3 -3
- fractal_server/app/models/__init__.py +1 -0
- fractal_server/app/models/linkuserproject.py +43 -1
- fractal_server/app/models/security.py +28 -8
- 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 +17 -2
- 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 +17 -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 +12 -13
- fractal_server/app/routes/admin/v2/accounting.py +3 -3
- fractal_server/app/routes/admin/v2/job.py +35 -24
- 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/sharing.py +103 -0
- fractal_server/app/routes/admin/v2/task.py +37 -26
- fractal_server/app/routes/admin/v2/task_group.py +94 -17
- fractal_server/app/routes/admin/v2/task_group_lifecycle.py +21 -22
- fractal_server/app/routes/api/__init__.py +1 -9
- fractal_server/app/routes/api/v2/__init__.py +49 -50
- fractal_server/app/routes/api/v2/_aux_functions.py +132 -124
- fractal_server/app/routes/api/v2/_aux_functions_history.py +51 -23
- fractal_server/app/routes/api/v2/_aux_functions_sharing.py +97 -0
- fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +6 -8
- fractal_server/app/routes/api/v2/_aux_functions_tasks.py +7 -9
- fractal_server/app/routes/api/v2/_aux_task_group_disambiguation.py +1 -2
- fractal_server/app/routes/api/v2/dataset.py +95 -102
- fractal_server/app/routes/api/v2/history.py +59 -33
- fractal_server/app/routes/api/v2/images.py +24 -9
- fractal_server/app/routes/api/v2/job.py +52 -33
- fractal_server/app/routes/api/v2/pre_submission_checks.py +16 -8
- fractal_server/app/routes/api/v2/project.py +65 -37
- fractal_server/app/routes/api/v2/sharing.py +311 -0
- fractal_server/app/routes/api/v2/status_legacy.py +31 -41
- fractal_server/app/routes/api/v2/submit.py +82 -78
- fractal_server/app/routes/api/v2/task.py +19 -20
- fractal_server/app/routes/api/v2/task_collection.py +41 -43
- fractal_server/app/routes/api/v2/task_collection_custom.py +19 -20
- fractal_server/app/routes/api/v2/task_collection_pixi.py +10 -11
- fractal_server/app/routes/api/v2/task_group.py +25 -24
- fractal_server/app/routes/api/v2/task_group_lifecycle.py +32 -32
- fractal_server/app/routes/api/v2/task_version_update.py +23 -19
- fractal_server/app/routes/api/v2/workflow.py +50 -55
- fractal_server/app/routes/api/v2/workflow_import.py +37 -37
- fractal_server/app/routes/api/v2/workflowtask.py +32 -26
- fractal_server/app/routes/auth/__init__.py +1 -3
- fractal_server/app/routes/auth/_aux_auth.py +101 -2
- fractal_server/app/routes/auth/current_user.py +2 -66
- fractal_server/app/routes/auth/group.py +8 -35
- 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/router.py +2 -0
- fractal_server/app/routes/auth/users.py +19 -10
- fractal_server/app/routes/auth/viewer_paths.py +43 -0
- 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 +29 -12
- fractal_server/app/schemas/user_group.py +0 -15
- fractal_server/app/schemas/v2/__init__.py +55 -48
- fractal_server/app/schemas/v2/accounting.py +11 -0
- fractal_server/app/schemas/v2/dataset.py +57 -11
- fractal_server/app/schemas/v2/dumps.py +10 -9
- fractal_server/app/schemas/v2/job.py +11 -11
- fractal_server/app/schemas/v2/manifest.py +4 -3
- fractal_server/app/schemas/v2/profile.py +53 -2
- fractal_server/app/schemas/v2/project.py +3 -3
- fractal_server/app/schemas/v2/resource.py +121 -16
- fractal_server/app/schemas/v2/sharing.py +99 -0
- fractal_server/app/schemas/v2/status_legacy.py +3 -3
- fractal_server/app/schemas/v2/task.py +6 -7
- fractal_server/app/schemas/v2/task_collection.py +5 -5
- fractal_server/app/schemas/v2/task_group.py +16 -16
- fractal_server/app/schemas/v2/workflow.py +16 -16
- fractal_server/app/schemas/v2/workflowtask.py +16 -15
- fractal_server/app/security/__init__.py +5 -8
- fractal_server/app/security/signup_email.py +4 -5
- fractal_server/app/shutdown.py +6 -6
- fractal_server/config/__init__.py +0 -6
- fractal_server/config/_data.py +0 -68
- fractal_server/config/_database.py +19 -20
- fractal_server/config/_email.py +30 -38
- fractal_server/config/_main.py +38 -52
- fractal_server/config/_oauth.py +17 -21
- fractal_server/data_migrations/2_18_0.py +30 -0
- fractal_server/exceptions.py +4 -0
- fractal_server/images/models.py +4 -5
- fractal_server/images/status_tools.py +4 -2
- fractal_server/logger.py +1 -1
- fractal_server/main.py +75 -13
- 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/7910eed4cf97_user_project_dirs_and_usergroup_viewer_.py +60 -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/88270f589c9b_add_prevent_new_submissions.py +39 -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/bc0e8b3327a7_project_sharing.py +72 -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/f0702066b007_one_submitted_job_per_dataset.py +40 -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 +39 -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 +9 -20
- 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 +2 -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 +12 -6
- fractal_server/runner/v2/_slurm_ssh.py +14 -7
- fractal_server/runner/v2/_slurm_sudo.py +14 -7
- fractal_server/runner/v2/db_tools.py +0 -1
- fractal_server/runner/v2/deduplicate_list.py +2 -1
- fractal_server/runner/v2/runner.py +44 -28
- fractal_server/runner/v2/runner_functions.py +22 -28
- fractal_server/runner/v2/submit_workflow.py +29 -15
- 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 +3 -3
- fractal_server/tasks/v2/local/collect.py +15 -18
- fractal_server/tasks/v2/local/collect_pixi.py +14 -16
- fractal_server/tasks/v2/local/deactivate.py +14 -15
- fractal_server/tasks/v2/local/deactivate_pixi.py +7 -7
- fractal_server/tasks/v2/local/delete.py +6 -8
- fractal_server/tasks/v2/local/reactivate.py +12 -12
- fractal_server/tasks/v2/local/reactivate_pixi.py +12 -12
- fractal_server/tasks/v2/ssh/_utils.py +3 -3
- fractal_server/tasks/v2/ssh/collect.py +19 -24
- fractal_server/tasks/v2/ssh/collect_pixi.py +22 -24
- fractal_server/tasks/v2/ssh/deactivate.py +17 -15
- fractal_server/tasks/v2/ssh/deactivate_pixi.py +8 -7
- fractal_server/tasks/v2/ssh/delete.py +12 -10
- fractal_server/tasks/v2/ssh/reactivate.py +16 -16
- fractal_server/tasks/v2/ssh/reactivate_pixi.py +13 -14
- 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 +10 -10
- fractal_server/tasks/v2/utils_database.py +5 -5
- fractal_server/tasks/v2/utils_package_names.py +1 -2
- fractal_server/tasks/v2/utils_pixi.py +1 -3
- fractal_server/types/__init__.py +98 -1
- fractal_server/types/validators/__init__.py +3 -0
- fractal_server/types/validators/_common_validators.py +33 -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.18.0.dist-info}/METADATA +3 -2
- fractal_server-2.18.0.dist-info/RECORD +275 -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.18.0.dist-info}/WHEEL +0 -0
- {fractal_server-2.17.1a1.dist-info → fractal_server-2.18.0.dist-info}/entry_points.txt +0 -0
- {fractal_server-2.17.1a1.dist-info → fractal_server-2.18.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,28 +5,33 @@ from fastapi import Response
|
|
|
5
5
|
from fastapi import status
|
|
6
6
|
from sqlmodel import select
|
|
7
7
|
|
|
8
|
-
from
|
|
9
|
-
from
|
|
10
|
-
from ....db import AsyncSession
|
|
11
|
-
from ....db import get_async_db
|
|
12
|
-
from ....models.v2 import JobV2
|
|
13
|
-
from ....models.v2 import LinkUserProjectV2
|
|
14
|
-
from ....models.v2 import ProjectV2
|
|
15
|
-
from ....schemas.v2 import ProjectCreateV2
|
|
16
|
-
from ....schemas.v2 import ProjectReadV2
|
|
17
|
-
from ....schemas.v2 import ProjectUpdateV2
|
|
18
|
-
from ...aux.validate_user_profile import validate_user_profile
|
|
19
|
-
from ._aux_functions import _check_project_exists
|
|
20
|
-
from ._aux_functions import _get_project_check_owner
|
|
21
|
-
from ._aux_functions import _get_submitted_jobs_statement
|
|
8
|
+
from fractal_server.app.db import AsyncSession
|
|
9
|
+
from fractal_server.app.db import get_async_db
|
|
22
10
|
from fractal_server.app.models import UserOAuth
|
|
11
|
+
from fractal_server.app.models.v2 import JobV2
|
|
12
|
+
from fractal_server.app.models.v2 import LinkUserProjectV2
|
|
13
|
+
from fractal_server.app.models.v2 import ProjectV2
|
|
23
14
|
from fractal_server.app.routes.auth import current_user_act_ver_prof
|
|
15
|
+
from fractal_server.app.routes.aux.validate_user_profile import (
|
|
16
|
+
validate_user_profile,
|
|
17
|
+
)
|
|
18
|
+
from fractal_server.app.schemas.v2 import ProjectCreate
|
|
19
|
+
from fractal_server.app.schemas.v2 import ProjectPermissions
|
|
20
|
+
from fractal_server.app.schemas.v2 import ProjectRead
|
|
21
|
+
from fractal_server.app.schemas.v2 import ProjectUpdate
|
|
22
|
+
from fractal_server.logger import set_logger
|
|
24
23
|
|
|
24
|
+
from ._aux_functions import _check_project_exists
|
|
25
|
+
from ._aux_functions import _get_project_check_access
|
|
26
|
+
from ._aux_functions import _get_submitted_jobs_statement
|
|
27
|
+
|
|
28
|
+
logger = set_logger(__name__)
|
|
25
29
|
router = APIRouter()
|
|
26
30
|
|
|
27
31
|
|
|
28
|
-
@router.get("/project/", response_model=list[
|
|
32
|
+
@router.get("/project/", response_model=list[ProjectRead])
|
|
29
33
|
async def get_list_project(
|
|
34
|
+
is_owner: bool = True,
|
|
30
35
|
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
31
36
|
db: AsyncSession = Depends(get_async_db),
|
|
32
37
|
) -> list[ProjectV2]:
|
|
@@ -35,8 +40,10 @@ async def get_list_project(
|
|
|
35
40
|
"""
|
|
36
41
|
stm = (
|
|
37
42
|
select(ProjectV2)
|
|
38
|
-
.join(LinkUserProjectV2)
|
|
43
|
+
.join(LinkUserProjectV2, LinkUserProjectV2.project_id == ProjectV2.id)
|
|
39
44
|
.where(LinkUserProjectV2.user_id == user.id)
|
|
45
|
+
.where(LinkUserProjectV2.is_owner == is_owner)
|
|
46
|
+
.where(LinkUserProjectV2.is_verified.is_(True))
|
|
40
47
|
)
|
|
41
48
|
res = await db.execute(stm)
|
|
42
49
|
project_list = res.scalars().all()
|
|
@@ -44,12 +51,12 @@ async def get_list_project(
|
|
|
44
51
|
return project_list
|
|
45
52
|
|
|
46
53
|
|
|
47
|
-
@router.post("/project/", response_model=
|
|
54
|
+
@router.post("/project/", response_model=ProjectRead, status_code=201)
|
|
48
55
|
async def create_project(
|
|
49
|
-
project:
|
|
56
|
+
project: ProjectCreate,
|
|
50
57
|
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
51
58
|
db: AsyncSession = Depends(get_async_db),
|
|
52
|
-
) ->
|
|
59
|
+
) -> ProjectRead | None:
|
|
53
60
|
"""
|
|
54
61
|
Create new project
|
|
55
62
|
"""
|
|
@@ -67,41 +74,55 @@ async def create_project(
|
|
|
67
74
|
)
|
|
68
75
|
|
|
69
76
|
db_project = ProjectV2(**project.model_dump(), resource_id=resource_id)
|
|
70
|
-
db_project.user_list.append(user)
|
|
71
|
-
|
|
72
77
|
db.add(db_project)
|
|
78
|
+
await db.flush()
|
|
79
|
+
|
|
80
|
+
link = LinkUserProjectV2(
|
|
81
|
+
project_id=db_project.id,
|
|
82
|
+
user_id=user.id,
|
|
83
|
+
is_owner=True,
|
|
84
|
+
is_verified=True,
|
|
85
|
+
permissions=ProjectPermissions.EXECUTE,
|
|
86
|
+
)
|
|
87
|
+
db.add(link)
|
|
88
|
+
|
|
73
89
|
await db.commit()
|
|
74
90
|
await db.refresh(db_project)
|
|
75
|
-
await db.close()
|
|
76
91
|
|
|
77
92
|
return db_project
|
|
78
93
|
|
|
79
94
|
|
|
80
|
-
@router.get("/project/{project_id}/", response_model=
|
|
95
|
+
@router.get("/project/{project_id}/", response_model=ProjectRead)
|
|
81
96
|
async def read_project(
|
|
82
97
|
project_id: int,
|
|
83
98
|
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
84
99
|
db: AsyncSession = Depends(get_async_db),
|
|
85
|
-
) ->
|
|
100
|
+
) -> ProjectRead | None:
|
|
86
101
|
"""
|
|
87
102
|
Return info on an existing project
|
|
88
103
|
"""
|
|
89
|
-
project = await
|
|
90
|
-
project_id=project_id,
|
|
104
|
+
project = await _get_project_check_access(
|
|
105
|
+
project_id=project_id,
|
|
106
|
+
user_id=user.id,
|
|
107
|
+
required_permissions=ProjectPermissions.READ,
|
|
108
|
+
db=db,
|
|
91
109
|
)
|
|
92
110
|
await db.close()
|
|
93
111
|
return project
|
|
94
112
|
|
|
95
113
|
|
|
96
|
-
@router.patch("/project/{project_id}/", response_model=
|
|
114
|
+
@router.patch("/project/{project_id}/", response_model=ProjectRead)
|
|
97
115
|
async def update_project(
|
|
98
116
|
project_id: int,
|
|
99
|
-
project_update:
|
|
117
|
+
project_update: ProjectUpdate,
|
|
100
118
|
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
101
119
|
db: AsyncSession = Depends(get_async_db),
|
|
102
120
|
):
|
|
103
|
-
project = await
|
|
104
|
-
project_id=project_id,
|
|
121
|
+
project = await _get_project_check_access(
|
|
122
|
+
project_id=project_id,
|
|
123
|
+
user_id=user.id,
|
|
124
|
+
required_permissions=ProjectPermissions.WRITE,
|
|
125
|
+
db=db,
|
|
105
126
|
)
|
|
106
127
|
|
|
107
128
|
# Check that there is no project with the same user and name
|
|
@@ -129,10 +150,18 @@ async def delete_project(
|
|
|
129
150
|
Delete project
|
|
130
151
|
"""
|
|
131
152
|
|
|
132
|
-
project = await
|
|
133
|
-
project_id=project_id,
|
|
153
|
+
project = await _get_project_check_access(
|
|
154
|
+
project_id=project_id,
|
|
155
|
+
user_id=user.id,
|
|
156
|
+
required_permissions=ProjectPermissions.EXECUTE,
|
|
157
|
+
db=db,
|
|
134
158
|
)
|
|
135
|
-
|
|
159
|
+
link_user_project = await db.get(LinkUserProjectV2, (project_id, user.id))
|
|
160
|
+
if not link_user_project.is_owner:
|
|
161
|
+
raise HTTPException(
|
|
162
|
+
status_code=status.HTTP_403_FORBIDDEN,
|
|
163
|
+
detail="Only the owner can delete a Project.",
|
|
164
|
+
)
|
|
136
165
|
|
|
137
166
|
# Fail if there exist jobs that are submitted and in relation with the
|
|
138
167
|
# current project.
|
|
@@ -149,13 +178,12 @@ async def delete_project(
|
|
|
149
178
|
),
|
|
150
179
|
)
|
|
151
180
|
|
|
152
|
-
logger.
|
|
181
|
+
logger.debug(f"Add project {project.id} to deletion.")
|
|
153
182
|
await db.delete(project)
|
|
154
183
|
|
|
155
|
-
logger.
|
|
184
|
+
logger.debug("Commit changes to db")
|
|
156
185
|
await db.commit()
|
|
157
186
|
|
|
158
|
-
logger.
|
|
159
|
-
reset_logger_handlers(logger)
|
|
187
|
+
logger.debug("Everything has been deleted correctly.")
|
|
160
188
|
|
|
161
189
|
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
from fastapi import APIRouter
|
|
2
|
+
from fastapi import Depends
|
|
3
|
+
from fastapi import HTTPException
|
|
4
|
+
from fastapi import Response
|
|
5
|
+
from fastapi import status
|
|
6
|
+
from pydantic import EmailStr
|
|
7
|
+
from sqlmodel import select
|
|
8
|
+
|
|
9
|
+
from fractal_server.app.db import AsyncSession
|
|
10
|
+
from fractal_server.app.db import get_async_db
|
|
11
|
+
from fractal_server.app.models import UserOAuth
|
|
12
|
+
from fractal_server.app.models.v2 import LinkUserProjectV2
|
|
13
|
+
from fractal_server.app.models.v2 import ProjectV2
|
|
14
|
+
from fractal_server.app.routes.auth import current_user_act_ver_prof
|
|
15
|
+
from fractal_server.app.schemas.v2 import ProjectAccessRead
|
|
16
|
+
from fractal_server.app.schemas.v2 import ProjectGuestCreate
|
|
17
|
+
from fractal_server.app.schemas.v2 import ProjectGuestRead
|
|
18
|
+
from fractal_server.app.schemas.v2 import ProjectGuestUpdate
|
|
19
|
+
from fractal_server.app.schemas.v2 import ProjectInvitationRead
|
|
20
|
+
|
|
21
|
+
from ._aux_functions_sharing import get_link_or_404
|
|
22
|
+
from ._aux_functions_sharing import get_pending_invitation_or_404
|
|
23
|
+
from ._aux_functions_sharing import get_user_id_from_email_or_404
|
|
24
|
+
from ._aux_functions_sharing import raise_403_if_not_owner
|
|
25
|
+
from ._aux_functions_sharing import raise_422_if_link_exists
|
|
26
|
+
|
|
27
|
+
router = APIRouter()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@router.get(
|
|
31
|
+
"/project/{project_id}/guest/",
|
|
32
|
+
response_model=list[ProjectGuestRead],
|
|
33
|
+
)
|
|
34
|
+
async def get_project_guests(
|
|
35
|
+
project_id: int,
|
|
36
|
+
owner: UserOAuth = Depends(current_user_act_ver_prof),
|
|
37
|
+
db: AsyncSession = Depends(get_async_db),
|
|
38
|
+
) -> list[ProjectGuestRead]:
|
|
39
|
+
"""
|
|
40
|
+
Get the list of all the guests of your project (verified or not).
|
|
41
|
+
"""
|
|
42
|
+
await raise_403_if_not_owner(user_id=owner.id, project_id=project_id, db=db)
|
|
43
|
+
# Get (email, is_verified, permissions) for all guests
|
|
44
|
+
res = await db.execute(
|
|
45
|
+
select(
|
|
46
|
+
UserOAuth.email,
|
|
47
|
+
LinkUserProjectV2.is_verified,
|
|
48
|
+
LinkUserProjectV2.permissions,
|
|
49
|
+
)
|
|
50
|
+
.join(LinkUserProjectV2, LinkUserProjectV2.user_id == UserOAuth.id)
|
|
51
|
+
.where(LinkUserProjectV2.project_id == project_id)
|
|
52
|
+
.where(LinkUserProjectV2.is_owner.is_(False))
|
|
53
|
+
.order_by(UserOAuth.email)
|
|
54
|
+
)
|
|
55
|
+
guest_tuples = res.all()
|
|
56
|
+
return [
|
|
57
|
+
dict(
|
|
58
|
+
email=guest_email,
|
|
59
|
+
is_verified=is_verified,
|
|
60
|
+
permissions=permissions,
|
|
61
|
+
)
|
|
62
|
+
for guest_email, is_verified, permissions in guest_tuples
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@router.post("/project/{project_id}/guest/", status_code=201)
|
|
67
|
+
async def invite_guest(
|
|
68
|
+
project_id: int,
|
|
69
|
+
email: EmailStr,
|
|
70
|
+
project_invitation: ProjectGuestCreate,
|
|
71
|
+
owner: UserOAuth = Depends(current_user_act_ver_prof),
|
|
72
|
+
db: AsyncSession = Depends(get_async_db),
|
|
73
|
+
) -> Response:
|
|
74
|
+
"""
|
|
75
|
+
Add a guest to your project.
|
|
76
|
+
"""
|
|
77
|
+
await raise_403_if_not_owner(user_id=owner.id, project_id=project_id, db=db)
|
|
78
|
+
|
|
79
|
+
guest_id = await get_user_id_from_email_or_404(user_email=email, db=db)
|
|
80
|
+
|
|
81
|
+
await raise_422_if_link_exists(
|
|
82
|
+
user_id=guest_id,
|
|
83
|
+
project_id=project_id,
|
|
84
|
+
db=db,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
db.add(
|
|
88
|
+
LinkUserProjectV2(
|
|
89
|
+
project_id=project_id,
|
|
90
|
+
user_id=guest_id,
|
|
91
|
+
is_owner=False,
|
|
92
|
+
is_verified=False,
|
|
93
|
+
permissions=project_invitation.permissions,
|
|
94
|
+
)
|
|
95
|
+
)
|
|
96
|
+
await db.commit()
|
|
97
|
+
|
|
98
|
+
return Response(status_code=status.HTTP_201_CREATED)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@router.patch("/project/{project_id}/guest/", status_code=200)
|
|
102
|
+
async def patch_guest(
|
|
103
|
+
project_id: int,
|
|
104
|
+
email: EmailStr,
|
|
105
|
+
update: ProjectGuestUpdate,
|
|
106
|
+
owner: UserOAuth = Depends(current_user_act_ver_prof),
|
|
107
|
+
db: AsyncSession = Depends(get_async_db),
|
|
108
|
+
) -> Response:
|
|
109
|
+
"""
|
|
110
|
+
Change guest's permissions on your project.
|
|
111
|
+
"""
|
|
112
|
+
await raise_403_if_not_owner(user_id=owner.id, project_id=project_id, db=db)
|
|
113
|
+
|
|
114
|
+
guest_id = await get_user_id_from_email_or_404(user_email=email, db=db)
|
|
115
|
+
|
|
116
|
+
if guest_id == owner.id:
|
|
117
|
+
raise HTTPException(
|
|
118
|
+
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
|
119
|
+
detail="Cannot perform this operation on project owner.",
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
link = await get_link_or_404(
|
|
123
|
+
user_id=guest_id,
|
|
124
|
+
project_id=project_id,
|
|
125
|
+
db=db,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Update link and commit
|
|
129
|
+
for key, value in update.model_dump(exclude_unset=True).items():
|
|
130
|
+
setattr(link, key, value)
|
|
131
|
+
await db.commit()
|
|
132
|
+
|
|
133
|
+
return Response(status_code=status.HTTP_200_OK)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@router.delete("/project/{project_id}/guest/", status_code=204)
|
|
137
|
+
async def revoke_guest_access(
|
|
138
|
+
project_id: int,
|
|
139
|
+
email: EmailStr,
|
|
140
|
+
owner: UserOAuth = Depends(current_user_act_ver_prof),
|
|
141
|
+
db: AsyncSession = Depends(get_async_db),
|
|
142
|
+
) -> Response:
|
|
143
|
+
"""
|
|
144
|
+
Remove a guest from your project.
|
|
145
|
+
"""
|
|
146
|
+
await raise_403_if_not_owner(user_id=owner.id, project_id=project_id, db=db)
|
|
147
|
+
|
|
148
|
+
guest_id = await get_user_id_from_email_or_404(user_email=email, db=db)
|
|
149
|
+
|
|
150
|
+
if guest_id == owner.id:
|
|
151
|
+
raise HTTPException(
|
|
152
|
+
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
|
153
|
+
detail="Cannot perform this operation on project owner.",
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
link = await get_link_or_404(
|
|
157
|
+
user_id=guest_id,
|
|
158
|
+
project_id=project_id,
|
|
159
|
+
db=db,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# Delete link and commit
|
|
163
|
+
await db.delete(link)
|
|
164
|
+
await db.commit()
|
|
165
|
+
|
|
166
|
+
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@router.get(
|
|
170
|
+
"/project/invitation/",
|
|
171
|
+
response_model=list[ProjectInvitationRead],
|
|
172
|
+
)
|
|
173
|
+
async def get_pending_invitations(
|
|
174
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
175
|
+
db: AsyncSession = Depends(get_async_db),
|
|
176
|
+
) -> list[ProjectInvitationRead]:
|
|
177
|
+
"""
|
|
178
|
+
See your current invitations.
|
|
179
|
+
"""
|
|
180
|
+
|
|
181
|
+
res = await db.execute(
|
|
182
|
+
select(
|
|
183
|
+
ProjectV2.id,
|
|
184
|
+
ProjectV2.name,
|
|
185
|
+
LinkUserProjectV2.permissions,
|
|
186
|
+
(
|
|
187
|
+
select(UserOAuth.email)
|
|
188
|
+
.join(
|
|
189
|
+
LinkUserProjectV2,
|
|
190
|
+
UserOAuth.id == LinkUserProjectV2.user_id,
|
|
191
|
+
)
|
|
192
|
+
.where(LinkUserProjectV2.is_owner.is_(True))
|
|
193
|
+
.where(LinkUserProjectV2.project_id == ProjectV2.id)
|
|
194
|
+
.scalar_subquery()
|
|
195
|
+
.correlate(ProjectV2)
|
|
196
|
+
),
|
|
197
|
+
)
|
|
198
|
+
.join(LinkUserProjectV2, LinkUserProjectV2.project_id == ProjectV2.id)
|
|
199
|
+
.where(LinkUserProjectV2.user_id == user.id)
|
|
200
|
+
.where(LinkUserProjectV2.is_verified.is_(False))
|
|
201
|
+
.order_by(ProjectV2.name)
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
guest_project_info = res.all()
|
|
205
|
+
|
|
206
|
+
return [
|
|
207
|
+
dict(
|
|
208
|
+
project_id=project_id,
|
|
209
|
+
project_name=project_name,
|
|
210
|
+
guest_permissions=guest_permissions,
|
|
211
|
+
owner_email=owner_email,
|
|
212
|
+
)
|
|
213
|
+
for (
|
|
214
|
+
project_id,
|
|
215
|
+
project_name,
|
|
216
|
+
guest_permissions,
|
|
217
|
+
owner_email,
|
|
218
|
+
) in guest_project_info
|
|
219
|
+
]
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
@router.get(
|
|
223
|
+
"/project/{project_id}/access/",
|
|
224
|
+
response_model=ProjectAccessRead,
|
|
225
|
+
)
|
|
226
|
+
async def get_access_info(
|
|
227
|
+
project_id: int,
|
|
228
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
229
|
+
db: AsyncSession = Depends(get_async_db),
|
|
230
|
+
) -> ProjectAccessRead:
|
|
231
|
+
"""
|
|
232
|
+
Returns information on your relationship with Project[`project_id`].
|
|
233
|
+
"""
|
|
234
|
+
|
|
235
|
+
res = await db.execute(
|
|
236
|
+
select(
|
|
237
|
+
LinkUserProjectV2.is_owner,
|
|
238
|
+
LinkUserProjectV2.permissions,
|
|
239
|
+
(
|
|
240
|
+
select(UserOAuth.email)
|
|
241
|
+
.join(
|
|
242
|
+
LinkUserProjectV2,
|
|
243
|
+
UserOAuth.id == LinkUserProjectV2.user_id,
|
|
244
|
+
)
|
|
245
|
+
.where(LinkUserProjectV2.is_owner.is_(True))
|
|
246
|
+
.where(LinkUserProjectV2.project_id == project_id)
|
|
247
|
+
.scalar_subquery()
|
|
248
|
+
),
|
|
249
|
+
)
|
|
250
|
+
.where(LinkUserProjectV2.project_id == project_id)
|
|
251
|
+
.where(LinkUserProjectV2.user_id == user.id)
|
|
252
|
+
.where(LinkUserProjectV2.is_verified.is_(True))
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
guest_project_info = res.one_or_none()
|
|
256
|
+
|
|
257
|
+
if guest_project_info is None:
|
|
258
|
+
raise HTTPException(
|
|
259
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
260
|
+
detail=f"User has no access to project {project_id}.",
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
is_owner, permissions, owner_email = guest_project_info
|
|
264
|
+
|
|
265
|
+
return dict(
|
|
266
|
+
is_owner=is_owner,
|
|
267
|
+
permissions=permissions,
|
|
268
|
+
owner_email=owner_email,
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
@router.post("/project/{project_id}/access/accept/", status_code=200)
|
|
273
|
+
async def accept_project_invitation(
|
|
274
|
+
project_id: int,
|
|
275
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
276
|
+
db: AsyncSession = Depends(get_async_db),
|
|
277
|
+
) -> Response:
|
|
278
|
+
"""
|
|
279
|
+
Accept invitation to project `project_id`.
|
|
280
|
+
"""
|
|
281
|
+
link = await get_pending_invitation_or_404(
|
|
282
|
+
user_id=user.id, project_id=project_id, db=db
|
|
283
|
+
)
|
|
284
|
+
link.is_verified = True
|
|
285
|
+
await db.commit()
|
|
286
|
+
|
|
287
|
+
return Response(status_code=status.HTTP_200_OK)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
@router.delete("/project/{project_id}/access/", status_code=204)
|
|
291
|
+
async def leave_project(
|
|
292
|
+
project_id: int,
|
|
293
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
294
|
+
db: AsyncSession = Depends(get_async_db),
|
|
295
|
+
) -> Response:
|
|
296
|
+
"""
|
|
297
|
+
Decline invitation to project `project_id` or stop being a guest of that
|
|
298
|
+
project.
|
|
299
|
+
"""
|
|
300
|
+
link = await get_link_or_404(user_id=user.id, project_id=project_id, db=db)
|
|
301
|
+
|
|
302
|
+
if link.is_owner:
|
|
303
|
+
raise HTTPException(
|
|
304
|
+
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
|
305
|
+
detail=f"You are the owner of project {project_id}.",
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
await db.delete(link)
|
|
309
|
+
await db.commit()
|
|
310
|
+
|
|
311
|
+
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
from fastapi import APIRouter
|
|
2
2
|
from fastapi import Depends
|
|
3
|
-
|
|
4
|
-
from
|
|
5
|
-
|
|
6
|
-
from .....logger import set_logger
|
|
7
|
-
from ....db import AsyncSession
|
|
8
|
-
from ....db import get_async_db
|
|
9
|
-
from ....models.v2 import JobV2
|
|
10
|
-
from ....schemas.v2.status_legacy import LegacyStatusReadV2
|
|
11
|
-
from ....schemas.v2.status_legacy import WorkflowTaskStatusTypeV2
|
|
12
|
-
from ._aux_functions import _get_dataset_check_owner
|
|
13
|
-
from ._aux_functions import _get_submitted_jobs_statement
|
|
14
|
-
from ._aux_functions import _get_workflow_check_owner
|
|
3
|
+
|
|
4
|
+
from fractal_server.app.db import AsyncSession
|
|
5
|
+
from fractal_server.app.db import get_async_db
|
|
15
6
|
from fractal_server.app.models import UserOAuth
|
|
7
|
+
from fractal_server.app.models.v2 import JobV2
|
|
16
8
|
from fractal_server.app.routes.auth import current_user_act_ver_prof
|
|
9
|
+
from fractal_server.app.schemas.v2.sharing import ProjectPermissions
|
|
10
|
+
from fractal_server.app.schemas.v2.status_legacy import LegacyStatusRead
|
|
11
|
+
from fractal_server.app.schemas.v2.status_legacy import WorkflowTaskStatusType
|
|
12
|
+
from fractal_server.logger import set_logger
|
|
13
|
+
|
|
14
|
+
from ._aux_functions import _get_dataset_check_access
|
|
15
|
+
from ._aux_functions import _get_submitted_jobs_statement
|
|
16
|
+
from ._aux_functions import _get_workflow_check_access
|
|
17
17
|
|
|
18
18
|
router = APIRouter()
|
|
19
19
|
|
|
@@ -22,7 +22,7 @@ logger = set_logger(__name__)
|
|
|
22
22
|
|
|
23
23
|
@router.get(
|
|
24
24
|
"/project/{project_id}/status-legacy/",
|
|
25
|
-
response_model=
|
|
25
|
+
response_model=LegacyStatusRead,
|
|
26
26
|
)
|
|
27
27
|
async def get_workflowtask_status(
|
|
28
28
|
project_id: int,
|
|
@@ -30,7 +30,7 @@ async def get_workflowtask_status(
|
|
|
30
30
|
workflow_id: int,
|
|
31
31
|
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
32
32
|
db: AsyncSession = Depends(get_async_db),
|
|
33
|
-
) ->
|
|
33
|
+
) -> LegacyStatusRead | None:
|
|
34
34
|
"""
|
|
35
35
|
Extract the status of all `WorkflowTaskV2` of a given `WorkflowV2` that ran
|
|
36
36
|
on a given `DatasetV2`.
|
|
@@ -41,43 +41,33 @@ async def get_workflowtask_status(
|
|
|
41
41
|
order). See fractal-server GitHub issues: 793, 1083.
|
|
42
42
|
"""
|
|
43
43
|
# Get the dataset DB entry
|
|
44
|
-
output = await
|
|
44
|
+
output = await _get_dataset_check_access(
|
|
45
45
|
project_id=project_id,
|
|
46
46
|
dataset_id=dataset_id,
|
|
47
47
|
user_id=user.id,
|
|
48
|
+
required_permissions=ProjectPermissions.READ,
|
|
48
49
|
db=db,
|
|
49
50
|
)
|
|
50
51
|
dataset = output["dataset"]
|
|
51
52
|
|
|
52
53
|
# Get the workflow DB entry
|
|
53
|
-
workflow = await
|
|
54
|
+
workflow = await _get_workflow_check_access(
|
|
54
55
|
project_id=project_id,
|
|
55
56
|
workflow_id=workflow_id,
|
|
56
57
|
user_id=user.id,
|
|
58
|
+
required_permissions=ProjectPermissions.READ,
|
|
57
59
|
db=db,
|
|
58
60
|
)
|
|
59
61
|
|
|
60
62
|
# Check whether there exists a submitted job associated to this
|
|
61
63
|
# workflow/dataset pair. If it does exist, it will be used later.
|
|
62
64
|
# If there are multiple jobs, raise an error.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
running_job = None
|
|
70
|
-
elif len(running_jobs) == 1:
|
|
71
|
-
running_job = running_jobs[0]
|
|
72
|
-
else:
|
|
73
|
-
string_ids = str([job.id for job in running_jobs])[1:-1]
|
|
74
|
-
raise HTTPException(
|
|
75
|
-
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
|
76
|
-
detail=(
|
|
77
|
-
f"Cannot get WorkflowTaskV2 statuses as DatasetV2 {dataset.id}"
|
|
78
|
-
f" is linked to multiple active jobs: {string_ids}."
|
|
79
|
-
),
|
|
80
|
-
)
|
|
65
|
+
res = await db.execute(
|
|
66
|
+
_get_submitted_jobs_statement()
|
|
67
|
+
.where(JobV2.dataset_id == dataset_id)
|
|
68
|
+
.where(JobV2.workflow_id == workflow_id)
|
|
69
|
+
)
|
|
70
|
+
running_job = res.scalars().one_or_none()
|
|
81
71
|
|
|
82
72
|
# Initialize empty dictionary for WorkflowTaskV2 status
|
|
83
73
|
workflow_tasks_status_dict: dict = {}
|
|
@@ -112,19 +102,19 @@ async def get_workflowtask_status(
|
|
|
112
102
|
]
|
|
113
103
|
try:
|
|
114
104
|
first_submitted_index = running_job_statuses.index(
|
|
115
|
-
|
|
105
|
+
WorkflowTaskStatusType.SUBMITTED
|
|
116
106
|
)
|
|
117
107
|
except ValueError:
|
|
118
108
|
logger.warning(
|
|
119
109
|
f"Job {running_job.id} is submitted but its task list does not"
|
|
120
|
-
f" contain a {
|
|
110
|
+
f" contain a {WorkflowTaskStatusType.SUBMITTED} task."
|
|
121
111
|
)
|
|
122
112
|
first_submitted_index = 0
|
|
123
113
|
|
|
124
114
|
for wftask in running_job_wftasks[first_submitted_index:]:
|
|
125
|
-
workflow_tasks_status_dict[
|
|
126
|
-
|
|
127
|
-
|
|
115
|
+
workflow_tasks_status_dict[wftask.id] = (
|
|
116
|
+
WorkflowTaskStatusType.SUBMITTED
|
|
117
|
+
)
|
|
128
118
|
|
|
129
119
|
# The last workflow task that is included in the submitted job is also
|
|
130
120
|
# the positional-last workflow task to be included in the response.
|
|
@@ -153,7 +143,7 @@ async def get_workflowtask_status(
|
|
|
153
143
|
# If a wftask ID was not found, ignore it and continue
|
|
154
144
|
continue
|
|
155
145
|
clean_workflow_tasks_status_dict[str(wf_task.id)] = wf_task_status
|
|
156
|
-
if wf_task_status ==
|
|
146
|
+
if wf_task_status == WorkflowTaskStatusType.FAILED:
|
|
157
147
|
# Starting from the beginning of `workflow.task_list`, stop the
|
|
158
148
|
# first time that you hit a failed job
|
|
159
149
|
break
|
|
@@ -162,5 +152,5 @@ async def get_workflowtask_status(
|
|
|
162
152
|
# first time that you hit `last_valid_wftask_id``
|
|
163
153
|
break
|
|
164
154
|
|
|
165
|
-
response_body =
|
|
155
|
+
response_body = LegacyStatusRead(status=clean_workflow_tasks_status_dict)
|
|
166
156
|
return response_body
|