fractal-server 2.18.0__py3-none-any.whl → 2.18.0a0__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.
Files changed (97) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/__main__.py +1 -2
  3. fractal_server/app/models/security.py +5 -7
  4. fractal_server/app/models/v2/job.py +2 -13
  5. fractal_server/app/models/v2/resource.py +0 -13
  6. fractal_server/app/routes/admin/v2/__init__.py +12 -10
  7. fractal_server/app/routes/admin/v2/accounting.py +2 -2
  8. fractal_server/app/routes/admin/v2/job.py +17 -17
  9. fractal_server/app/routes/admin/v2/task.py +8 -8
  10. fractal_server/app/routes/admin/v2/task_group.py +16 -94
  11. fractal_server/app/routes/admin/v2/task_group_lifecycle.py +20 -20
  12. fractal_server/app/routes/api/__init__.py +9 -0
  13. fractal_server/app/routes/api/v2/__init__.py +49 -47
  14. fractal_server/app/routes/api/v2/_aux_functions.py +47 -22
  15. fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +4 -4
  16. fractal_server/app/routes/api/v2/_aux_functions_tasks.py +2 -2
  17. fractal_server/app/routes/api/v2/dataset.py +60 -66
  18. fractal_server/app/routes/api/v2/history.py +5 -7
  19. fractal_server/app/routes/api/v2/job.py +12 -12
  20. fractal_server/app/routes/api/v2/project.py +11 -11
  21. fractal_server/app/routes/api/v2/sharing.py +2 -1
  22. fractal_server/app/routes/api/v2/status_legacy.py +29 -15
  23. fractal_server/app/routes/api/v2/submit.py +66 -65
  24. fractal_server/app/routes/api/v2/task.py +17 -15
  25. fractal_server/app/routes/api/v2/task_collection.py +18 -18
  26. fractal_server/app/routes/api/v2/task_collection_custom.py +13 -11
  27. fractal_server/app/routes/api/v2/task_collection_pixi.py +9 -9
  28. fractal_server/app/routes/api/v2/task_group.py +18 -18
  29. fractal_server/app/routes/api/v2/task_group_lifecycle.py +26 -26
  30. fractal_server/app/routes/api/v2/task_version_update.py +5 -5
  31. fractal_server/app/routes/api/v2/workflow.py +18 -18
  32. fractal_server/app/routes/api/v2/workflow_import.py +11 -11
  33. fractal_server/app/routes/api/v2/workflowtask.py +36 -10
  34. fractal_server/app/routes/auth/_aux_auth.py +0 -100
  35. fractal_server/app/routes/auth/current_user.py +63 -0
  36. fractal_server/app/routes/auth/group.py +30 -1
  37. fractal_server/app/routes/auth/router.py +0 -2
  38. fractal_server/app/routes/auth/users.py +0 -9
  39. fractal_server/app/schemas/user.py +12 -29
  40. fractal_server/app/schemas/user_group.py +15 -0
  41. fractal_server/app/schemas/v2/__init__.py +48 -48
  42. fractal_server/app/schemas/v2/dataset.py +13 -35
  43. fractal_server/app/schemas/v2/dumps.py +9 -9
  44. fractal_server/app/schemas/v2/job.py +11 -11
  45. fractal_server/app/schemas/v2/project.py +3 -3
  46. fractal_server/app/schemas/v2/resource.py +4 -13
  47. fractal_server/app/schemas/v2/status_legacy.py +3 -3
  48. fractal_server/app/schemas/v2/task.py +6 -6
  49. fractal_server/app/schemas/v2/task_collection.py +4 -4
  50. fractal_server/app/schemas/v2/task_group.py +16 -16
  51. fractal_server/app/schemas/v2/workflow.py +16 -16
  52. fractal_server/app/schemas/v2/workflowtask.py +14 -14
  53. fractal_server/app/security/__init__.py +1 -1
  54. fractal_server/app/shutdown.py +6 -6
  55. fractal_server/config/__init__.py +6 -0
  56. fractal_server/config/_data.py +79 -0
  57. fractal_server/config/_main.py +1 -6
  58. fractal_server/images/models.py +2 -1
  59. fractal_server/main.py +11 -72
  60. fractal_server/runner/config/_slurm.py +0 -2
  61. fractal_server/runner/executors/slurm_common/slurm_config.py +0 -1
  62. fractal_server/runner/v2/_local.py +3 -4
  63. fractal_server/runner/v2/_slurm_ssh.py +3 -4
  64. fractal_server/runner/v2/_slurm_sudo.py +3 -4
  65. fractal_server/runner/v2/runner.py +17 -36
  66. fractal_server/runner/v2/runner_functions.py +14 -11
  67. fractal_server/runner/v2/submit_workflow.py +9 -22
  68. fractal_server/tasks/v2/local/_utils.py +2 -2
  69. fractal_server/tasks/v2/local/collect.py +6 -5
  70. fractal_server/tasks/v2/local/collect_pixi.py +6 -5
  71. fractal_server/tasks/v2/local/deactivate.py +7 -7
  72. fractal_server/tasks/v2/local/deactivate_pixi.py +3 -3
  73. fractal_server/tasks/v2/local/delete.py +5 -5
  74. fractal_server/tasks/v2/local/reactivate.py +5 -5
  75. fractal_server/tasks/v2/local/reactivate_pixi.py +5 -5
  76. fractal_server/tasks/v2/ssh/collect.py +5 -5
  77. fractal_server/tasks/v2/ssh/collect_pixi.py +5 -5
  78. fractal_server/tasks/v2/ssh/deactivate.py +7 -7
  79. fractal_server/tasks/v2/ssh/deactivate_pixi.py +2 -2
  80. fractal_server/tasks/v2/ssh/delete.py +5 -5
  81. fractal_server/tasks/v2/ssh/reactivate.py +5 -5
  82. fractal_server/tasks/v2/ssh/reactivate_pixi.py +5 -5
  83. fractal_server/tasks/v2/utils_background.py +7 -7
  84. fractal_server/tasks/v2/utils_database.py +5 -5
  85. fractal_server/types/__init__.py +0 -22
  86. fractal_server/types/validators/__init__.py +0 -3
  87. fractal_server/types/validators/_common_validators.py +0 -32
  88. {fractal_server-2.18.0.dist-info → fractal_server-2.18.0a0.dist-info}/METADATA +1 -1
  89. {fractal_server-2.18.0.dist-info → fractal_server-2.18.0a0.dist-info}/RECORD +92 -97
  90. fractal_server/app/routes/auth/viewer_paths.py +0 -43
  91. fractal_server/data_migrations/2_18_0.py +0 -30
  92. fractal_server/migrations/versions/7910eed4cf97_user_project_dirs_and_usergroup_viewer_.py +0 -60
  93. fractal_server/migrations/versions/88270f589c9b_add_prevent_new_submissions.py +0 -39
  94. fractal_server/migrations/versions/f0702066b007_one_submitted_job_per_dataset.py +0 -40
  95. {fractal_server-2.18.0.dist-info → fractal_server-2.18.0a0.dist-info}/WHEEL +0 -0
  96. {fractal_server-2.18.0.dist-info → fractal_server-2.18.0a0.dist-info}/entry_points.txt +0 -0
  97. {fractal_server-2.18.0.dist-info → fractal_server-2.18.0a0.dist-info}/licenses/LICENSE +0 -0
@@ -33,10 +33,10 @@ from fractal_server.app.routes.aux.validate_user_profile import (
33
33
  )
34
34
  from fractal_server.app.schemas.v2 import FractalUploadedFile
35
35
  from fractal_server.app.schemas.v2 import ResourceType
36
- from fractal_server.app.schemas.v2 import TaskGroupActivityAction
37
- from fractal_server.app.schemas.v2 import TaskGroupActivityRead
38
- from fractal_server.app.schemas.v2 import TaskGroupActivityStatus
39
- from fractal_server.app.schemas.v2.task_group import TaskGroupOriginEnum
36
+ from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
37
+ from fractal_server.app.schemas.v2 import TaskGroupActivityStatusV2
38
+ from fractal_server.app.schemas.v2 import TaskGroupActivityV2Read
39
+ from fractal_server.app.schemas.v2.task_group import TaskGroupV2OriginEnum
40
40
  from fractal_server.logger import set_logger
41
41
  from fractal_server.tasks.v2.local import collect_local_pixi
42
42
  from fractal_server.tasks.v2.ssh import collect_ssh_pixi
@@ -74,7 +74,7 @@ def validate_pkgname_and_version(filename: str) -> tuple[str, str]:
74
74
  @router.post(
75
75
  "/collect/pixi/",
76
76
  status_code=202,
77
- response_model=TaskGroupActivityRead,
77
+ response_model=TaskGroupActivityV2Read,
78
78
  )
79
79
  async def collect_task_pixi(
80
80
  response: Response,
@@ -85,7 +85,7 @@ async def collect_task_pixi(
85
85
  user_group_id: int | None = None,
86
86
  user: UserOAuth = Depends(current_user_act_ver_prof),
87
87
  db: AsyncSession = Depends(get_async_db),
88
- ) -> TaskGroupActivityRead:
88
+ ) -> TaskGroupActivityV2Read:
89
89
  # Get validated resource and profile
90
90
  resource, profile = await validate_user_profile(user=user, db=db)
91
91
  resource_id = resource.id
@@ -136,7 +136,7 @@ async def collect_task_pixi(
136
136
  user_id=user.id,
137
137
  user_group_id=user_group_id,
138
138
  resource_id=resource_id,
139
- origin=TaskGroupOriginEnum.PIXI,
139
+ origin=TaskGroupV2OriginEnum.PIXI,
140
140
  pixi_version=pixi_version,
141
141
  pkg_name=pkg_name,
142
142
  version=version,
@@ -178,8 +178,8 @@ async def collect_task_pixi(
178
178
  task_group_activity = TaskGroupActivityV2(
179
179
  user_id=task_group.user_id,
180
180
  taskgroupv2_id=task_group.id,
181
- status=TaskGroupActivityStatus.PENDING,
182
- action=TaskGroupActivityAction.COLLECT,
181
+ status=TaskGroupActivityStatusV2.PENDING,
182
+ action=TaskGroupActivityActionV2.COLLECT,
183
183
  pkg_name=task_group.pkg_name,
184
184
  version=task_group.version,
185
185
  )
@@ -24,11 +24,11 @@ from fractal_server.app.routes.auth._aux_auth import (
24
24
  from fractal_server.app.routes.auth._aux_auth import (
25
25
  _verify_user_belongs_to_group,
26
26
  )
27
- from fractal_server.app.schemas.v2 import TaskGroupActivityAction
28
- from fractal_server.app.schemas.v2 import TaskGroupActivityRead
29
- from fractal_server.app.schemas.v2 import TaskGroupActivityStatus
30
- from fractal_server.app.schemas.v2 import TaskGroupRead
31
- from fractal_server.app.schemas.v2 import TaskGroupUpdate
27
+ from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
28
+ from fractal_server.app.schemas.v2 import TaskGroupActivityStatusV2
29
+ from fractal_server.app.schemas.v2 import TaskGroupActivityV2Read
30
+ from fractal_server.app.schemas.v2 import TaskGroupReadV2
31
+ from fractal_server.app.schemas.v2 import TaskGroupUpdateV2
32
32
  from fractal_server.logger import set_logger
33
33
 
34
34
  from ._aux_functions import _get_user_resource_id
@@ -62,17 +62,17 @@ def _version_sort_key(
62
62
  return (1, task_group.version)
63
63
 
64
64
 
65
- @router.get("/activity/", response_model=list[TaskGroupActivityRead])
65
+ @router.get("/activity/", response_model=list[TaskGroupActivityV2Read])
66
66
  async def get_task_group_activity_list(
67
67
  task_group_activity_id: int | None = None,
68
68
  taskgroupv2_id: int | None = None,
69
69
  pkg_name: str | None = None,
70
- status: TaskGroupActivityStatus | None = None,
71
- action: TaskGroupActivityAction | None = None,
70
+ status: TaskGroupActivityStatusV2 | None = None,
71
+ action: TaskGroupActivityActionV2 | None = None,
72
72
  timestamp_started_min: AwareDatetime | None = None,
73
73
  user: UserOAuth = Depends(current_user_act_ver_prof),
74
74
  db: AsyncSession = Depends(get_async_db),
75
- ) -> list[TaskGroupActivityRead]:
75
+ ) -> list[TaskGroupActivityV2Read]:
76
76
  stm = select(TaskGroupActivityV2).where(
77
77
  TaskGroupActivityV2.user_id == user.id
78
78
  )
@@ -98,13 +98,13 @@ async def get_task_group_activity_list(
98
98
 
99
99
  @router.get(
100
100
  "/activity/{task_group_activity_id}/",
101
- response_model=TaskGroupActivityRead,
101
+ response_model=TaskGroupActivityV2Read,
102
102
  )
103
103
  async def get_task_group_activity(
104
104
  task_group_activity_id: int,
105
105
  user: UserOAuth = Depends(current_user_act_ver_prof),
106
106
  db: AsyncSession = Depends(get_async_db),
107
- ) -> TaskGroupActivityRead:
107
+ ) -> TaskGroupActivityV2Read:
108
108
  activity = await db.get(TaskGroupActivityV2, task_group_activity_id)
109
109
 
110
110
  if activity is None:
@@ -124,14 +124,14 @@ async def get_task_group_activity(
124
124
  return activity
125
125
 
126
126
 
127
- @router.get("/", response_model=list[tuple[str, list[TaskGroupRead]]])
127
+ @router.get("/", response_model=list[tuple[str, list[TaskGroupReadV2]]])
128
128
  async def get_task_group_list(
129
129
  user: UserOAuth = Depends(current_user_act_ver_prof),
130
130
  db: AsyncSession = Depends(get_async_db),
131
131
  only_active: bool = False,
132
132
  only_owner: bool = False,
133
133
  args_schema: bool = True,
134
- ) -> list[tuple[str, list[TaskGroupRead]]]:
134
+ ) -> list[tuple[str, list[TaskGroupReadV2]]]:
135
135
  """
136
136
  Get all accessible TaskGroups
137
137
  """
@@ -190,12 +190,12 @@ async def get_task_group_list(
190
190
  return grouped_result
191
191
 
192
192
 
193
- @router.get("/{task_group_id}/", response_model=TaskGroupRead)
193
+ @router.get("/{task_group_id}/", response_model=TaskGroupReadV2)
194
194
  async def get_task_group(
195
195
  task_group_id: int,
196
196
  user: UserOAuth = Depends(current_user_act_ver_prof),
197
197
  db: AsyncSession = Depends(get_async_db),
198
- ) -> TaskGroupRead:
198
+ ) -> TaskGroupReadV2:
199
199
  """
200
200
  Get single TaskGroup
201
201
  """
@@ -207,13 +207,13 @@ async def get_task_group(
207
207
  return task_group
208
208
 
209
209
 
210
- @router.patch("/{task_group_id}/", response_model=TaskGroupRead)
210
+ @router.patch("/{task_group_id}/", response_model=TaskGroupReadV2)
211
211
  async def patch_task_group(
212
212
  task_group_id: int,
213
- task_group_update: TaskGroupUpdate,
213
+ task_group_update: TaskGroupUpdateV2,
214
214
  user: UserOAuth = Depends(current_user_act_ver_prof),
215
215
  db: AsyncSession = Depends(get_async_db),
216
- ) -> TaskGroupRead:
216
+ ) -> TaskGroupReadV2:
217
217
  """
218
218
  Patch single TaskGroup
219
219
  """
@@ -14,11 +14,11 @@ from fractal_server.app.routes.aux.validate_user_profile import (
14
14
  validate_user_profile,
15
15
  )
16
16
  from fractal_server.app.schemas.v2 import ResourceType
17
- from fractal_server.app.schemas.v2 import TaskGroupActivityAction
18
- from fractal_server.app.schemas.v2 import TaskGroupActivityRead
19
- from fractal_server.app.schemas.v2 import TaskGroupActivityStatus
20
- from fractal_server.app.schemas.v2 import TaskGroupOriginEnum
21
- from fractal_server.app.schemas.v2 import TaskGroupRead
17
+ from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
18
+ from fractal_server.app.schemas.v2 import TaskGroupActivityStatusV2
19
+ from fractal_server.app.schemas.v2 import TaskGroupActivityV2Read
20
+ from fractal_server.app.schemas.v2 import TaskGroupReadV2
21
+ from fractal_server.app.schemas.v2 import TaskGroupV2OriginEnum
22
22
  from fractal_server.logger import set_logger
23
23
  from fractal_server.tasks.v2.local import deactivate_local
24
24
  from fractal_server.tasks.v2.local import deactivate_local_pixi
@@ -45,7 +45,7 @@ logger = set_logger(__name__)
45
45
 
46
46
  @router.post(
47
47
  "/{task_group_id}/deactivate/",
48
- response_model=TaskGroupActivityRead,
48
+ response_model=TaskGroupActivityV2Read,
49
49
  )
50
50
  async def deactivate_task_group(
51
51
  task_group_id: int,
@@ -53,7 +53,7 @@ async def deactivate_task_group(
53
53
  response: Response,
54
54
  user: UserOAuth = Depends(current_user_act_ver_prof),
55
55
  db: AsyncSession = Depends(get_async_db),
56
- ) -> TaskGroupActivityRead:
56
+ ) -> TaskGroupActivityV2Read:
57
57
  """
58
58
  Deactivate task-group venv
59
59
  """
@@ -84,13 +84,13 @@ async def deactivate_task_group(
84
84
  )
85
85
 
86
86
  # Shortcut for task-group with origin="other"
87
- if task_group.origin == TaskGroupOriginEnum.OTHER:
87
+ if task_group.origin == TaskGroupV2OriginEnum.OTHER:
88
88
  task_group.active = False
89
89
  task_group_activity = TaskGroupActivityV2(
90
90
  user_id=task_group.user_id,
91
91
  taskgroupv2_id=task_group.id,
92
- status=TaskGroupActivityStatus.OK,
93
- action=TaskGroupActivityAction.DEACTIVATE,
92
+ status=TaskGroupActivityStatusV2.OK,
93
+ action=TaskGroupActivityActionV2.DEACTIVATE,
94
94
  pkg_name=task_group.pkg_name,
95
95
  version=(task_group.version or "N/A"),
96
96
  log=(
@@ -109,8 +109,8 @@ async def deactivate_task_group(
109
109
  task_group_activity = TaskGroupActivityV2(
110
110
  user_id=task_group.user_id,
111
111
  taskgroupv2_id=task_group.id,
112
- status=TaskGroupActivityStatus.PENDING,
113
- action=TaskGroupActivityAction.DEACTIVATE,
112
+ status=TaskGroupActivityStatusV2.PENDING,
113
+ action=TaskGroupActivityActionV2.DEACTIVATE,
114
114
  pkg_name=task_group.pkg_name,
115
115
  version=task_group.version,
116
116
  timestamp_started=get_timestamp(),
@@ -122,12 +122,12 @@ async def deactivate_task_group(
122
122
 
123
123
  # Submit background task
124
124
  if resource.type == ResourceType.SLURM_SSH:
125
- if task_group.origin == TaskGroupOriginEnum.PIXI:
125
+ if task_group.origin == TaskGroupV2OriginEnum.PIXI:
126
126
  deactivate_function = deactivate_ssh_pixi
127
127
  else:
128
128
  deactivate_function = deactivate_ssh
129
129
  else:
130
- if task_group.origin == TaskGroupOriginEnum.PIXI:
130
+ if task_group.origin == TaskGroupV2OriginEnum.PIXI:
131
131
  deactivate_function = deactivate_local_pixi
132
132
  else:
133
133
  deactivate_function = deactivate_local
@@ -149,7 +149,7 @@ async def deactivate_task_group(
149
149
 
150
150
  @router.post(
151
151
  "/{task_group_id}/reactivate/",
152
- response_model=TaskGroupActivityRead,
152
+ response_model=TaskGroupActivityV2Read,
153
153
  )
154
154
  async def reactivate_task_group(
155
155
  task_group_id: int,
@@ -157,7 +157,7 @@ async def reactivate_task_group(
157
157
  response: Response,
158
158
  user: UserOAuth = Depends(current_user_act_ver_prof),
159
159
  db: AsyncSession = Depends(get_async_db),
160
- ) -> TaskGroupRead:
160
+ ) -> TaskGroupReadV2:
161
161
  """
162
162
  Deactivate task-group venv
163
163
  """
@@ -187,13 +187,13 @@ async def reactivate_task_group(
187
187
  await check_no_submitted_job(task_group_id=task_group.id, db=db)
188
188
 
189
189
  # Shortcut for task-group with origin="other"
190
- if task_group.origin == TaskGroupOriginEnum.OTHER:
190
+ if task_group.origin == TaskGroupV2OriginEnum.OTHER:
191
191
  task_group.active = True
192
192
  task_group_activity = TaskGroupActivityV2(
193
193
  user_id=task_group.user_id,
194
194
  taskgroupv2_id=task_group.id,
195
- status=TaskGroupActivityStatus.OK,
196
- action=TaskGroupActivityAction.REACTIVATE,
195
+ status=TaskGroupActivityStatusV2.OK,
196
+ action=TaskGroupActivityActionV2.REACTIVATE,
197
197
  pkg_name=task_group.pkg_name,
198
198
  version=(task_group.version or "N/A"),
199
199
  log=(
@@ -220,8 +220,8 @@ async def reactivate_task_group(
220
220
  task_group_activity = TaskGroupActivityV2(
221
221
  user_id=task_group.user_id,
222
222
  taskgroupv2_id=task_group.id,
223
- status=TaskGroupActivityStatus.PENDING,
224
- action=TaskGroupActivityAction.REACTIVATE,
223
+ status=TaskGroupActivityStatusV2.PENDING,
224
+ action=TaskGroupActivityActionV2.REACTIVATE,
225
225
  pkg_name=task_group.pkg_name,
226
226
  version=task_group.version,
227
227
  timestamp_started=get_timestamp(),
@@ -231,12 +231,12 @@ async def reactivate_task_group(
231
231
 
232
232
  # Submit background task
233
233
  if resource.type == ResourceType.SLURM_SSH:
234
- if task_group.origin == TaskGroupOriginEnum.PIXI:
234
+ if task_group.origin == TaskGroupV2OriginEnum.PIXI:
235
235
  reactivate_function = reactivate_ssh_pixi
236
236
  else:
237
237
  reactivate_function = reactivate_ssh
238
238
  else:
239
- if task_group.origin == TaskGroupOriginEnum.PIXI:
239
+ if task_group.origin == TaskGroupV2OriginEnum.PIXI:
240
240
  reactivate_function = reactivate_local_pixi
241
241
  else:
242
242
  reactivate_function = reactivate_local
@@ -265,7 +265,7 @@ async def delete_task_group(
265
265
  response: Response,
266
266
  user: UserOAuth = Depends(current_user_act_ver_prof),
267
267
  db: AsyncSession = Depends(get_async_db),
268
- ) -> TaskGroupActivityRead:
268
+ ) -> TaskGroupActivityV2Read:
269
269
  """
270
270
  Deletion of task-group from db and file system
271
271
  """
@@ -283,8 +283,8 @@ async def delete_task_group(
283
283
  task_group_activity = TaskGroupActivityV2(
284
284
  user_id=task_group.user_id,
285
285
  taskgroupv2_id=task_group.id,
286
- status=TaskGroupActivityStatus.PENDING,
287
- action=TaskGroupActivityAction.DELETE,
286
+ status=TaskGroupActivityStatusV2.PENDING,
287
+ action=TaskGroupActivityActionV2.DELETE,
288
288
  pkg_name=task_group.pkg_name,
289
289
  version=(task_group.version or "N/A"),
290
290
  timestamp_started=get_timestamp(),
@@ -19,8 +19,8 @@ from fractal_server.app.models.v2 import TaskGroupV2
19
19
  from fractal_server.app.models.v2 import TaskV2
20
20
  from fractal_server.app.routes.auth import current_user_act_ver_prof
21
21
  from fractal_server.app.schemas.v2 import TaskType
22
- from fractal_server.app.schemas.v2 import WorkflowTaskRead
23
- from fractal_server.app.schemas.v2 import WorkflowTaskReplace
22
+ from fractal_server.app.schemas.v2 import WorkflowTaskReadV2
23
+ from fractal_server.app.schemas.v2 import WorkflowTaskReplaceV2
24
24
  from fractal_server.app.schemas.v2.sharing import ProjectPermissions
25
25
 
26
26
  from ._aux_functions import _get_workflow_check_access
@@ -171,7 +171,7 @@ async def get_workflow_version_update_candidates(
171
171
 
172
172
  @router.post(
173
173
  "/project/{project_id}/workflow/{workflow_id}/wftask/replace-task/",
174
- response_model=WorkflowTaskRead,
174
+ response_model=WorkflowTaskReadV2,
175
175
  status_code=status.HTTP_201_CREATED,
176
176
  )
177
177
  async def replace_workflowtask(
@@ -179,10 +179,10 @@ async def replace_workflowtask(
179
179
  workflow_id: int,
180
180
  workflow_task_id: int,
181
181
  task_id: int,
182
- replace: WorkflowTaskReplace,
182
+ replace: WorkflowTaskReplaceV2,
183
183
  user: UserOAuth = Depends(current_user_act_ver_prof),
184
184
  db: AsyncSession = Depends(get_async_db),
185
- ) -> WorkflowTaskRead:
185
+ ) -> WorkflowTaskReadV2:
186
186
  # Get objects from database
187
187
  workflow_task, workflow = await _get_workflow_task_check_access(
188
188
  project_id=project_id,
@@ -15,11 +15,11 @@ from fractal_server.app.models.v2 import JobV2
15
15
  from fractal_server.app.models.v2 import TaskGroupV2
16
16
  from fractal_server.app.models.v2 import WorkflowV2
17
17
  from fractal_server.app.routes.auth import current_user_act_ver_prof
18
- from fractal_server.app.schemas.v2 import WorkflowCreate
19
- from fractal_server.app.schemas.v2 import WorkflowExport
20
- from fractal_server.app.schemas.v2 import WorkflowRead
21
- from fractal_server.app.schemas.v2 import WorkflowReadWithWarnings
22
- from fractal_server.app.schemas.v2 import WorkflowUpdate
18
+ from fractal_server.app.schemas.v2 import WorkflowCreateV2
19
+ from fractal_server.app.schemas.v2 import WorkflowExportV2
20
+ from fractal_server.app.schemas.v2 import WorkflowReadV2
21
+ from fractal_server.app.schemas.v2 import WorkflowReadV2WithWarnings
22
+ from fractal_server.app.schemas.v2 import WorkflowUpdateV2
23
23
  from fractal_server.app.schemas.v2.sharing import ProjectPermissions
24
24
  from fractal_server.images.tools import merge_type_filters
25
25
 
@@ -35,13 +35,13 @@ router = APIRouter()
35
35
 
36
36
  @router.get(
37
37
  "/project/{project_id}/workflow/",
38
- response_model=list[WorkflowRead],
38
+ response_model=list[WorkflowReadV2],
39
39
  )
40
40
  async def get_workflow_list(
41
41
  project_id: int,
42
42
  user: UserOAuth = Depends(current_user_act_ver_prof),
43
43
  db: AsyncSession = Depends(get_async_db),
44
- ) -> list[WorkflowRead] | None:
44
+ ) -> list[WorkflowReadV2] | None:
45
45
  """
46
46
  Get workflow list for given project
47
47
  """
@@ -63,15 +63,15 @@ async def get_workflow_list(
63
63
 
64
64
  @router.post(
65
65
  "/project/{project_id}/workflow/",
66
- response_model=WorkflowRead,
66
+ response_model=WorkflowReadV2,
67
67
  status_code=status.HTTP_201_CREATED,
68
68
  )
69
69
  async def create_workflow(
70
70
  project_id: int,
71
- workflow: WorkflowCreate,
71
+ workflow: WorkflowCreateV2,
72
72
  user: UserOAuth = Depends(current_user_act_ver_prof),
73
73
  db: AsyncSession = Depends(get_async_db),
74
- ) -> WorkflowRead | None:
74
+ ) -> WorkflowReadV2 | None:
75
75
  """
76
76
  Create a workflow, associate to a project
77
77
  """
@@ -95,14 +95,14 @@ async def create_workflow(
95
95
 
96
96
  @router.get(
97
97
  "/project/{project_id}/workflow/{workflow_id}/",
98
- response_model=WorkflowReadWithWarnings,
98
+ response_model=WorkflowReadV2WithWarnings,
99
99
  )
100
100
  async def read_workflow(
101
101
  project_id: int,
102
102
  workflow_id: int,
103
103
  user: UserOAuth = Depends(current_user_act_ver_prof),
104
104
  db: AsyncSession = Depends(get_async_db),
105
- ) -> WorkflowReadWithWarnings | None:
105
+ ) -> WorkflowReadV2WithWarnings | None:
106
106
  """
107
107
  Get info on an existing workflow
108
108
  """
@@ -129,15 +129,15 @@ async def read_workflow(
129
129
 
130
130
  @router.patch(
131
131
  "/project/{project_id}/workflow/{workflow_id}/",
132
- response_model=WorkflowReadWithWarnings,
132
+ response_model=WorkflowReadV2WithWarnings,
133
133
  )
134
134
  async def update_workflow(
135
135
  project_id: int,
136
136
  workflow_id: int,
137
- patch: WorkflowUpdate,
137
+ patch: WorkflowUpdateV2,
138
138
  user: UserOAuth = Depends(current_user_act_ver_prof),
139
139
  db: AsyncSession = Depends(get_async_db),
140
- ) -> WorkflowReadWithWarnings | None:
140
+ ) -> WorkflowReadV2WithWarnings | None:
141
141
  """
142
142
  Edit a workflow
143
143
  """
@@ -251,14 +251,14 @@ async def delete_workflow(
251
251
 
252
252
  @router.get(
253
253
  "/project/{project_id}/workflow/{workflow_id}/export/",
254
- response_model=WorkflowExport,
254
+ response_model=WorkflowExportV2,
255
255
  )
256
256
  async def export_workflow(
257
257
  project_id: int,
258
258
  workflow_id: int,
259
259
  user: UserOAuth = Depends(current_user_act_ver_prof),
260
260
  db: AsyncSession = Depends(get_async_db),
261
- ) -> WorkflowExport | None:
261
+ ) -> WorkflowExportV2 | None:
262
262
  """
263
263
  Export an existing workflow, after stripping all IDs
264
264
  """
@@ -279,7 +279,7 @@ async def export_workflow(
279
279
  name=wftask.task.name,
280
280
  )
281
281
 
282
- wf = WorkflowExport(
282
+ wf = WorkflowExportV2(
283
283
  **workflow.model_dump(),
284
284
  task_list=wf_task_list,
285
285
  )
@@ -19,11 +19,11 @@ from fractal_server.app.routes.auth import current_user_act_ver_prof
19
19
  from fractal_server.app.routes.auth._aux_auth import (
20
20
  _get_default_usergroup_id_or_none,
21
21
  )
22
- from fractal_server.app.schemas.v2 import TaskImport
23
- from fractal_server.app.schemas.v2 import TaskImportLegacy
24
- from fractal_server.app.schemas.v2 import WorkflowImport
25
- from fractal_server.app.schemas.v2 import WorkflowReadWithWarnings
26
- from fractal_server.app.schemas.v2 import WorkflowTaskCreate
22
+ from fractal_server.app.schemas.v2 import TaskImportV2
23
+ from fractal_server.app.schemas.v2 import TaskImportV2Legacy
24
+ from fractal_server.app.schemas.v2 import WorkflowImportV2
25
+ from fractal_server.app.schemas.v2 import WorkflowReadV2WithWarnings
26
+ from fractal_server.app.schemas.v2 import WorkflowTaskCreateV2
27
27
  from fractal_server.app.schemas.v2.sharing import ProjectPermissions
28
28
  from fractal_server.logger import set_logger
29
29
 
@@ -101,7 +101,7 @@ async def _get_task_by_source(
101
101
 
102
102
  async def _get_task_by_taskimport(
103
103
  *,
104
- task_import: TaskImport,
104
+ task_import: TaskImportV2,
105
105
  task_groups_list: list[TaskGroupV2],
106
106
  user_id: int,
107
107
  default_group_id: int | None,
@@ -207,15 +207,15 @@ async def _get_task_by_taskimport(
207
207
 
208
208
  @router.post(
209
209
  "/project/{project_id}/workflow/import/",
210
- response_model=WorkflowReadWithWarnings,
210
+ response_model=WorkflowReadV2WithWarnings,
211
211
  status_code=status.HTTP_201_CREATED,
212
212
  )
213
213
  async def import_workflow(
214
214
  project_id: int,
215
- workflow_import: WorkflowImport,
215
+ workflow_import: WorkflowImportV2,
216
216
  user: UserOAuth = Depends(current_user_act_ver_prof),
217
217
  db: AsyncSession = Depends(get_async_db),
218
- ) -> WorkflowReadWithWarnings:
218
+ ) -> WorkflowReadV2WithWarnings:
219
219
  """
220
220
  Import an existing workflow into a project and create required objects.
221
221
  """
@@ -246,7 +246,7 @@ async def import_workflow(
246
246
  list_task_ids = []
247
247
  for wf_task in workflow_import.task_list:
248
248
  task_import = wf_task.task
249
- if isinstance(task_import, TaskImportLegacy):
249
+ if isinstance(task_import, TaskImportV2Legacy):
250
250
  task_id = await _get_task_by_source(
251
251
  source=task_import.source,
252
252
  task_groups_list=task_group_list,
@@ -264,7 +264,7 @@ async def import_workflow(
264
264
  status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
265
265
  detail=f"Could not find a task matching with {wf_task.task}.",
266
266
  )
267
- new_wf_task = WorkflowTaskCreate(
267
+ new_wf_task = WorkflowTaskCreateV2(
268
268
  **wf_task.model_dump(exclude_none=True, exclude={"task"})
269
269
  )
270
270
  list_wf_tasks.append(new_wf_task)
@@ -5,15 +5,17 @@ from fastapi import Depends
5
5
  from fastapi import HTTPException
6
6
  from fastapi import Response
7
7
  from fastapi import status
8
+ from sqlmodel import select
8
9
 
9
10
  from fractal_server.app.db import AsyncSession
10
11
  from fractal_server.app.db import get_async_db
11
12
  from fractal_server.app.models import UserOAuth
13
+ from fractal_server.app.models.linkuserproject import LinkUserProjectV2
12
14
  from fractal_server.app.routes.auth import current_user_act_ver_prof
13
15
  from fractal_server.app.schemas.v2 import TaskType
14
- from fractal_server.app.schemas.v2 import WorkflowTaskCreate
15
- from fractal_server.app.schemas.v2 import WorkflowTaskRead
16
- from fractal_server.app.schemas.v2 import WorkflowTaskUpdate
16
+ from fractal_server.app.schemas.v2 import WorkflowTaskCreateV2
17
+ from fractal_server.app.schemas.v2 import WorkflowTaskReadV2
18
+ from fractal_server.app.schemas.v2 import WorkflowTaskUpdateV2
17
19
  from fractal_server.app.schemas.v2.sharing import ProjectPermissions
18
20
 
19
21
  from ._aux_functions import _get_workflow_check_access
@@ -28,17 +30,17 @@ router = APIRouter()
28
30
 
29
31
  @router.post(
30
32
  "/project/{project_id}/workflow/{workflow_id}/wftask/",
31
- response_model=WorkflowTaskRead,
33
+ response_model=WorkflowTaskReadV2,
32
34
  status_code=status.HTTP_201_CREATED,
33
35
  )
34
36
  async def create_workflowtask(
35
37
  project_id: int,
36
38
  workflow_id: int,
37
39
  task_id: int,
38
- wftask: WorkflowTaskCreate,
40
+ wftask: WorkflowTaskCreateV2,
39
41
  user: UserOAuth = Depends(current_user_act_ver_prof),
40
42
  db: AsyncSession = Depends(get_async_db),
41
- ) -> WorkflowTaskRead | None:
43
+ ) -> WorkflowTaskReadV2 | None:
42
44
  """
43
45
  Add a WorkflowTask to a Workflow
44
46
  """
@@ -51,6 +53,30 @@ async def create_workflowtask(
51
53
  db=db,
52
54
  )
53
55
 
56
+ res = await db.execute(
57
+ select(UserOAuth.id)
58
+ .join(LinkUserProjectV2, LinkUserProjectV2.user_id == UserOAuth.id)
59
+ .where(LinkUserProjectV2.project_id == project_id)
60
+ .where(LinkUserProjectV2.is_owner.is_(True))
61
+ )
62
+ project_owner_id = res.scalar_one()
63
+ if project_owner_id != user.id:
64
+ try:
65
+ await _get_task_read_access(
66
+ task_id=task_id,
67
+ user_id=project_owner_id,
68
+ db=db,
69
+ require_active=True,
70
+ )
71
+ except HTTPException as e:
72
+ if e.status_code == 403:
73
+ raise HTTPException(
74
+ status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
75
+ detail="The task must be accessible to the project owner.",
76
+ )
77
+ else:
78
+ raise e
79
+
54
80
  task = await _get_task_read_access(
55
81
  task_id=task_id, user_id=user.id, db=db, require_active=True
56
82
  )
@@ -100,7 +126,7 @@ async def create_workflowtask(
100
126
 
101
127
  @router.get(
102
128
  "/project/{project_id}/workflow/{workflow_id}/wftask/{workflow_task_id}/",
103
- response_model=WorkflowTaskRead,
129
+ response_model=WorkflowTaskReadV2,
104
130
  )
105
131
  async def read_workflowtask(
106
132
  project_id: int,
@@ -122,16 +148,16 @@ async def read_workflowtask(
122
148
 
123
149
  @router.patch(
124
150
  "/project/{project_id}/workflow/{workflow_id}/wftask/{workflow_task_id}/",
125
- response_model=WorkflowTaskRead,
151
+ response_model=WorkflowTaskReadV2,
126
152
  )
127
153
  async def update_workflowtask(
128
154
  project_id: int,
129
155
  workflow_id: int,
130
156
  workflow_task_id: int,
131
- workflow_task_update: WorkflowTaskUpdate,
157
+ workflow_task_update: WorkflowTaskUpdateV2,
132
158
  user: UserOAuth = Depends(current_user_act_ver_prof),
133
159
  db: AsyncSession = Depends(get_async_db),
134
- ) -> WorkflowTaskRead | None:
160
+ ) -> WorkflowTaskReadV2 | None:
135
161
  """
136
162
  Edit a WorkflowTask of a Workflow
137
163
  """