fractal-server 2.17.2__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.
Files changed (108) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/__main__.py +2 -1
  3. fractal_server/app/models/linkuserproject.py +40 -0
  4. fractal_server/app/models/security.py +7 -5
  5. fractal_server/app/models/v2/job.py +13 -2
  6. fractal_server/app/models/v2/resource.py +13 -0
  7. fractal_server/app/routes/admin/v2/__init__.py +11 -11
  8. fractal_server/app/routes/admin/v2/accounting.py +2 -2
  9. fractal_server/app/routes/admin/v2/job.py +34 -23
  10. fractal_server/app/routes/admin/v2/sharing.py +103 -0
  11. fractal_server/app/routes/admin/v2/task.py +9 -8
  12. fractal_server/app/routes/admin/v2/task_group.py +94 -16
  13. fractal_server/app/routes/admin/v2/task_group_lifecycle.py +20 -20
  14. fractal_server/app/routes/api/__init__.py +0 -9
  15. fractal_server/app/routes/api/v2/__init__.py +47 -47
  16. fractal_server/app/routes/api/v2/_aux_functions.py +65 -64
  17. fractal_server/app/routes/api/v2/_aux_functions_history.py +8 -3
  18. fractal_server/app/routes/api/v2/_aux_functions_sharing.py +97 -0
  19. fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +4 -4
  20. fractal_server/app/routes/api/v2/_aux_functions_tasks.py +2 -2
  21. fractal_server/app/routes/api/v2/dataset.py +89 -77
  22. fractal_server/app/routes/api/v2/history.py +28 -16
  23. fractal_server/app/routes/api/v2/images.py +22 -8
  24. fractal_server/app/routes/api/v2/job.py +40 -24
  25. fractal_server/app/routes/api/v2/pre_submission_checks.py +13 -6
  26. fractal_server/app/routes/api/v2/project.py +48 -25
  27. fractal_server/app/routes/api/v2/sharing.py +311 -0
  28. fractal_server/app/routes/api/v2/status_legacy.py +22 -33
  29. fractal_server/app/routes/api/v2/submit.py +76 -71
  30. fractal_server/app/routes/api/v2/task.py +15 -17
  31. fractal_server/app/routes/api/v2/task_collection.py +18 -18
  32. fractal_server/app/routes/api/v2/task_collection_custom.py +11 -13
  33. fractal_server/app/routes/api/v2/task_collection_pixi.py +9 -9
  34. fractal_server/app/routes/api/v2/task_group.py +18 -18
  35. fractal_server/app/routes/api/v2/task_group_lifecycle.py +26 -26
  36. fractal_server/app/routes/api/v2/task_version_update.py +12 -9
  37. fractal_server/app/routes/api/v2/workflow.py +41 -29
  38. fractal_server/app/routes/api/v2/workflow_import.py +25 -23
  39. fractal_server/app/routes/api/v2/workflowtask.py +25 -17
  40. fractal_server/app/routes/auth/_aux_auth.py +100 -0
  41. fractal_server/app/routes/auth/current_user.py +0 -63
  42. fractal_server/app/routes/auth/group.py +1 -30
  43. fractal_server/app/routes/auth/router.py +2 -0
  44. fractal_server/app/routes/auth/users.py +9 -0
  45. fractal_server/app/routes/auth/viewer_paths.py +43 -0
  46. fractal_server/app/schemas/user.py +29 -12
  47. fractal_server/app/schemas/user_group.py +0 -15
  48. fractal_server/app/schemas/v2/__init__.py +55 -48
  49. fractal_server/app/schemas/v2/dataset.py +35 -13
  50. fractal_server/app/schemas/v2/dumps.py +9 -9
  51. fractal_server/app/schemas/v2/job.py +11 -11
  52. fractal_server/app/schemas/v2/project.py +3 -3
  53. fractal_server/app/schemas/v2/resource.py +13 -4
  54. fractal_server/app/schemas/v2/sharing.py +99 -0
  55. fractal_server/app/schemas/v2/status_legacy.py +3 -3
  56. fractal_server/app/schemas/v2/task.py +6 -6
  57. fractal_server/app/schemas/v2/task_collection.py +4 -4
  58. fractal_server/app/schemas/v2/task_group.py +16 -16
  59. fractal_server/app/schemas/v2/workflow.py +16 -16
  60. fractal_server/app/schemas/v2/workflowtask.py +14 -14
  61. fractal_server/app/security/__init__.py +1 -1
  62. fractal_server/app/shutdown.py +6 -6
  63. fractal_server/config/__init__.py +0 -6
  64. fractal_server/config/_data.py +0 -79
  65. fractal_server/config/_main.py +6 -1
  66. fractal_server/data_migrations/2_18_0.py +30 -0
  67. fractal_server/images/models.py +1 -2
  68. fractal_server/main.py +72 -11
  69. fractal_server/migrations/versions/7910eed4cf97_user_project_dirs_and_usergroup_viewer_.py +60 -0
  70. fractal_server/migrations/versions/88270f589c9b_add_prevent_new_submissions.py +39 -0
  71. fractal_server/migrations/versions/bc0e8b3327a7_project_sharing.py +72 -0
  72. fractal_server/migrations/versions/f0702066b007_one_submitted_job_per_dataset.py +40 -0
  73. fractal_server/runner/config/_slurm.py +2 -0
  74. fractal_server/runner/executors/slurm_common/_batching.py +4 -10
  75. fractal_server/runner/executors/slurm_common/slurm_config.py +1 -0
  76. fractal_server/runner/executors/slurm_ssh/runner.py +1 -1
  77. fractal_server/runner/executors/slurm_sudo/runner.py +1 -1
  78. fractal_server/runner/v2/_local.py +4 -3
  79. fractal_server/runner/v2/_slurm_ssh.py +4 -3
  80. fractal_server/runner/v2/_slurm_sudo.py +4 -3
  81. fractal_server/runner/v2/runner.py +36 -17
  82. fractal_server/runner/v2/runner_functions.py +11 -14
  83. fractal_server/runner/v2/submit_workflow.py +22 -9
  84. fractal_server/tasks/v2/local/_utils.py +2 -2
  85. fractal_server/tasks/v2/local/collect.py +5 -6
  86. fractal_server/tasks/v2/local/collect_pixi.py +5 -6
  87. fractal_server/tasks/v2/local/deactivate.py +7 -7
  88. fractal_server/tasks/v2/local/deactivate_pixi.py +3 -3
  89. fractal_server/tasks/v2/local/delete.py +5 -5
  90. fractal_server/tasks/v2/local/reactivate.py +5 -5
  91. fractal_server/tasks/v2/local/reactivate_pixi.py +5 -5
  92. fractal_server/tasks/v2/ssh/collect.py +5 -5
  93. fractal_server/tasks/v2/ssh/collect_pixi.py +5 -5
  94. fractal_server/tasks/v2/ssh/deactivate.py +7 -7
  95. fractal_server/tasks/v2/ssh/deactivate_pixi.py +2 -2
  96. fractal_server/tasks/v2/ssh/delete.py +5 -5
  97. fractal_server/tasks/v2/ssh/reactivate.py +5 -5
  98. fractal_server/tasks/v2/ssh/reactivate_pixi.py +5 -5
  99. fractal_server/tasks/v2/utils_background.py +7 -7
  100. fractal_server/tasks/v2/utils_database.py +5 -5
  101. fractal_server/types/__init__.py +22 -0
  102. fractal_server/types/validators/__init__.py +3 -0
  103. fractal_server/types/validators/_common_validators.py +32 -0
  104. {fractal_server-2.17.2.dist-info → fractal_server-2.18.0.dist-info}/METADATA +3 -2
  105. {fractal_server-2.17.2.dist-info → fractal_server-2.18.0.dist-info}/RECORD +108 -98
  106. {fractal_server-2.17.2.dist-info → fractal_server-2.18.0.dist-info}/WHEEL +0 -0
  107. {fractal_server-2.17.2.dist-info → fractal_server-2.18.0.dist-info}/entry_points.txt +0 -0
  108. {fractal_server-2.17.2.dist-info → fractal_server-2.18.0.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 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
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
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=TaskGroupActivityV2Read,
77
+ response_model=TaskGroupActivityRead,
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
- ) -> TaskGroupActivityV2Read:
88
+ ) -> TaskGroupActivityRead:
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=TaskGroupV2OriginEnum.PIXI,
139
+ origin=TaskGroupOriginEnum.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=TaskGroupActivityStatusV2.PENDING,
182
- action=TaskGroupActivityActionV2.COLLECT,
181
+ status=TaskGroupActivityStatus.PENDING,
182
+ action=TaskGroupActivityAction.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 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
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
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[TaskGroupActivityV2Read])
65
+ @router.get("/activity/", response_model=list[TaskGroupActivityRead])
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: TaskGroupActivityStatusV2 | None = None,
71
- action: TaskGroupActivityActionV2 | None = None,
70
+ status: TaskGroupActivityStatus | None = None,
71
+ action: TaskGroupActivityAction | 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[TaskGroupActivityV2Read]:
75
+ ) -> list[TaskGroupActivityRead]:
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=TaskGroupActivityV2Read,
101
+ response_model=TaskGroupActivityRead,
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
- ) -> TaskGroupActivityV2Read:
107
+ ) -> TaskGroupActivityRead:
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[TaskGroupReadV2]]])
127
+ @router.get("/", response_model=list[tuple[str, list[TaskGroupRead]]])
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[TaskGroupReadV2]]]:
134
+ ) -> list[tuple[str, list[TaskGroupRead]]]:
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=TaskGroupReadV2)
193
+ @router.get("/{task_group_id}/", response_model=TaskGroupRead)
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
- ) -> TaskGroupReadV2:
198
+ ) -> TaskGroupRead:
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=TaskGroupReadV2)
210
+ @router.patch("/{task_group_id}/", response_model=TaskGroupRead)
211
211
  async def patch_task_group(
212
212
  task_group_id: int,
213
- task_group_update: TaskGroupUpdateV2,
213
+ task_group_update: TaskGroupUpdate,
214
214
  user: UserOAuth = Depends(current_user_act_ver_prof),
215
215
  db: AsyncSession = Depends(get_async_db),
216
- ) -> TaskGroupReadV2:
216
+ ) -> TaskGroupRead:
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 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
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
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=TaskGroupActivityV2Read,
48
+ response_model=TaskGroupActivityRead,
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
- ) -> TaskGroupActivityV2Read:
56
+ ) -> TaskGroupActivityRead:
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 == TaskGroupV2OriginEnum.OTHER:
87
+ if task_group.origin == TaskGroupOriginEnum.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=TaskGroupActivityStatusV2.OK,
93
- action=TaskGroupActivityActionV2.DEACTIVATE,
92
+ status=TaskGroupActivityStatus.OK,
93
+ action=TaskGroupActivityAction.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=TaskGroupActivityStatusV2.PENDING,
113
- action=TaskGroupActivityActionV2.DEACTIVATE,
112
+ status=TaskGroupActivityStatus.PENDING,
113
+ action=TaskGroupActivityAction.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 == TaskGroupV2OriginEnum.PIXI:
125
+ if task_group.origin == TaskGroupOriginEnum.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 == TaskGroupV2OriginEnum.PIXI:
130
+ if task_group.origin == TaskGroupOriginEnum.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=TaskGroupActivityV2Read,
152
+ response_model=TaskGroupActivityRead,
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
- ) -> TaskGroupReadV2:
160
+ ) -> TaskGroupRead:
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 == TaskGroupV2OriginEnum.OTHER:
190
+ if task_group.origin == TaskGroupOriginEnum.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=TaskGroupActivityStatusV2.OK,
196
- action=TaskGroupActivityActionV2.REACTIVATE,
195
+ status=TaskGroupActivityStatus.OK,
196
+ action=TaskGroupActivityAction.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=TaskGroupActivityStatusV2.PENDING,
224
- action=TaskGroupActivityActionV2.REACTIVATE,
223
+ status=TaskGroupActivityStatus.PENDING,
224
+ action=TaskGroupActivityAction.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 == TaskGroupV2OriginEnum.PIXI:
234
+ if task_group.origin == TaskGroupOriginEnum.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 == TaskGroupV2OriginEnum.PIXI:
239
+ if task_group.origin == TaskGroupOriginEnum.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
- ) -> TaskGroupActivityV2Read:
268
+ ) -> TaskGroupActivityRead:
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=TaskGroupActivityStatusV2.PENDING,
287
- action=TaskGroupActivityActionV2.DELETE,
286
+ status=TaskGroupActivityStatus.PENDING,
287
+ action=TaskGroupActivityAction.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,11 +19,12 @@ 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 WorkflowTaskReadV2
23
- from fractal_server.app.schemas.v2 import WorkflowTaskReplaceV2
22
+ from fractal_server.app.schemas.v2 import WorkflowTaskRead
23
+ from fractal_server.app.schemas.v2 import WorkflowTaskReplace
24
+ from fractal_server.app.schemas.v2.sharing import ProjectPermissions
24
25
 
25
- from ._aux_functions import _get_workflow_check_owner
26
- from ._aux_functions import _get_workflow_task_check_owner
26
+ from ._aux_functions import _get_workflow_check_access
27
+ from ._aux_functions import _get_workflow_task_check_access
27
28
  from ._aux_functions_task_version_update import get_new_workflow_task_meta
28
29
  from ._aux_functions_tasks import _check_type_filters_compatibility
29
30
  from ._aux_functions_tasks import _get_task_group_or_404
@@ -79,10 +80,11 @@ async def get_workflow_version_update_candidates(
79
80
  user: UserOAuth = Depends(current_user_act_ver_prof),
80
81
  db: AsyncSession = Depends(get_async_db),
81
82
  ) -> list[list[TaskVersionRead]]:
82
- workflow = await _get_workflow_check_owner(
83
+ workflow = await _get_workflow_check_access(
83
84
  project_id=project_id,
84
85
  workflow_id=workflow_id,
85
86
  user_id=user.id,
87
+ required_permissions=ProjectPermissions.READ,
86
88
  db=db,
87
89
  )
88
90
 
@@ -169,7 +171,7 @@ async def get_workflow_version_update_candidates(
169
171
 
170
172
  @router.post(
171
173
  "/project/{project_id}/workflow/{workflow_id}/wftask/replace-task/",
172
- response_model=WorkflowTaskReadV2,
174
+ response_model=WorkflowTaskRead,
173
175
  status_code=status.HTTP_201_CREATED,
174
176
  )
175
177
  async def replace_workflowtask(
@@ -177,16 +179,17 @@ async def replace_workflowtask(
177
179
  workflow_id: int,
178
180
  workflow_task_id: int,
179
181
  task_id: int,
180
- replace: WorkflowTaskReplaceV2,
182
+ replace: WorkflowTaskReplace,
181
183
  user: UserOAuth = Depends(current_user_act_ver_prof),
182
184
  db: AsyncSession = Depends(get_async_db),
183
- ) -> WorkflowTaskReadV2:
185
+ ) -> WorkflowTaskRead:
184
186
  # Get objects from database
185
- workflow_task, workflow = await _get_workflow_task_check_owner(
187
+ workflow_task, workflow = await _get_workflow_task_check_access(
186
188
  project_id=project_id,
187
189
  workflow_id=workflow_id,
188
190
  workflow_task_id=workflow_task_id,
189
191
  user_id=user.id,
192
+ required_permissions=ProjectPermissions.WRITE,
190
193
  db=db,
191
194
  )
192
195
  new_task = await _get_task_read_access(
@@ -15,17 +15,18 @@ 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 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
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
23
+ from fractal_server.app.schemas.v2.sharing import ProjectPermissions
23
24
  from fractal_server.images.tools import merge_type_filters
24
25
 
25
26
  from ._aux_functions import _check_workflow_exists
26
- from ._aux_functions import _get_project_check_owner
27
+ from ._aux_functions import _get_project_check_access
27
28
  from ._aux_functions import _get_submitted_jobs_statement
28
- from ._aux_functions import _get_workflow_check_owner
29
+ from ._aux_functions import _get_workflow_check_access
29
30
  from ._aux_functions import _workflow_has_submitted_job
30
31
  from ._aux_functions_tasks import _add_warnings_to_workflow_tasks
31
32
 
@@ -34,19 +35,22 @@ router = APIRouter()
34
35
 
35
36
  @router.get(
36
37
  "/project/{project_id}/workflow/",
37
- response_model=list[WorkflowReadV2],
38
+ response_model=list[WorkflowRead],
38
39
  )
39
40
  async def get_workflow_list(
40
41
  project_id: int,
41
42
  user: UserOAuth = Depends(current_user_act_ver_prof),
42
43
  db: AsyncSession = Depends(get_async_db),
43
- ) -> list[WorkflowReadV2] | None:
44
+ ) -> list[WorkflowRead] | None:
44
45
  """
45
46
  Get workflow list for given project
46
47
  """
47
48
  # Access control
48
- project = await _get_project_check_owner(
49
- project_id=project_id, user_id=user.id, db=db
49
+ project = await _get_project_check_access(
50
+ project_id=project_id,
51
+ user_id=user.id,
52
+ required_permissions=ProjectPermissions.READ,
53
+ db=db,
50
54
  )
51
55
  # Find workflows of the current project. Note: this select/where approach
52
56
  # has much better scaling than refreshing all elements of
@@ -59,20 +63,23 @@ async def get_workflow_list(
59
63
 
60
64
  @router.post(
61
65
  "/project/{project_id}/workflow/",
62
- response_model=WorkflowReadV2,
66
+ response_model=WorkflowRead,
63
67
  status_code=status.HTTP_201_CREATED,
64
68
  )
65
69
  async def create_workflow(
66
70
  project_id: int,
67
- workflow: WorkflowCreateV2,
71
+ workflow: WorkflowCreate,
68
72
  user: UserOAuth = Depends(current_user_act_ver_prof),
69
73
  db: AsyncSession = Depends(get_async_db),
70
- ) -> WorkflowReadV2 | None:
74
+ ) -> WorkflowRead | None:
71
75
  """
72
76
  Create a workflow, associate to a project
73
77
  """
74
- await _get_project_check_owner(
75
- project_id=project_id, user_id=user.id, db=db
78
+ await _get_project_check_access(
79
+ project_id=project_id,
80
+ user_id=user.id,
81
+ required_permissions=ProjectPermissions.WRITE,
82
+ db=db,
76
83
  )
77
84
  await _check_workflow_exists(
78
85
  name=workflow.name, project_id=project_id, db=db
@@ -88,22 +95,23 @@ async def create_workflow(
88
95
 
89
96
  @router.get(
90
97
  "/project/{project_id}/workflow/{workflow_id}/",
91
- response_model=WorkflowReadV2WithWarnings,
98
+ response_model=WorkflowReadWithWarnings,
92
99
  )
93
100
  async def read_workflow(
94
101
  project_id: int,
95
102
  workflow_id: int,
96
103
  user: UserOAuth = Depends(current_user_act_ver_prof),
97
104
  db: AsyncSession = Depends(get_async_db),
98
- ) -> WorkflowReadV2WithWarnings | None:
105
+ ) -> WorkflowReadWithWarnings | None:
99
106
  """
100
107
  Get info on an existing workflow
101
108
  """
102
109
 
103
- workflow = await _get_workflow_check_owner(
110
+ workflow = await _get_workflow_check_access(
104
111
  project_id=project_id,
105
112
  workflow_id=workflow_id,
106
113
  user_id=user.id,
114
+ required_permissions=ProjectPermissions.READ,
107
115
  db=db,
108
116
  )
109
117
 
@@ -121,22 +129,23 @@ async def read_workflow(
121
129
 
122
130
  @router.patch(
123
131
  "/project/{project_id}/workflow/{workflow_id}/",
124
- response_model=WorkflowReadV2WithWarnings,
132
+ response_model=WorkflowReadWithWarnings,
125
133
  )
126
134
  async def update_workflow(
127
135
  project_id: int,
128
136
  workflow_id: int,
129
- patch: WorkflowUpdateV2,
137
+ patch: WorkflowUpdate,
130
138
  user: UserOAuth = Depends(current_user_act_ver_prof),
131
139
  db: AsyncSession = Depends(get_async_db),
132
- ) -> WorkflowReadV2WithWarnings | None:
140
+ ) -> WorkflowReadWithWarnings | None:
133
141
  """
134
142
  Edit a workflow
135
143
  """
136
- workflow = await _get_workflow_check_owner(
144
+ workflow = await _get_workflow_check_access(
137
145
  project_id=project_id,
138
146
  workflow_id=workflow_id,
139
147
  user_id=user.id,
148
+ required_permissions=ProjectPermissions.WRITE,
140
149
  db=db,
141
150
  )
142
151
 
@@ -208,10 +217,11 @@ async def delete_workflow(
208
217
  Delete a workflow
209
218
  """
210
219
 
211
- workflow = await _get_workflow_check_owner(
220
+ workflow = await _get_workflow_check_access(
212
221
  project_id=project_id,
213
222
  workflow_id=workflow_id,
214
223
  user_id=user.id,
224
+ required_permissions=ProjectPermissions.WRITE,
215
225
  db=db,
216
226
  )
217
227
 
@@ -241,21 +251,22 @@ async def delete_workflow(
241
251
 
242
252
  @router.get(
243
253
  "/project/{project_id}/workflow/{workflow_id}/export/",
244
- response_model=WorkflowExportV2,
254
+ response_model=WorkflowExport,
245
255
  )
246
256
  async def export_workflow(
247
257
  project_id: int,
248
258
  workflow_id: int,
249
259
  user: UserOAuth = Depends(current_user_act_ver_prof),
250
260
  db: AsyncSession = Depends(get_async_db),
251
- ) -> WorkflowExportV2 | None:
261
+ ) -> WorkflowExport | None:
252
262
  """
253
263
  Export an existing workflow, after stripping all IDs
254
264
  """
255
- workflow = await _get_workflow_check_owner(
265
+ workflow = await _get_workflow_check_access(
256
266
  project_id=project_id,
257
267
  workflow_id=workflow_id,
258
268
  user_id=user.id,
269
+ required_permissions=ProjectPermissions.READ,
259
270
  db=db,
260
271
  )
261
272
  wf_task_list = []
@@ -268,7 +279,7 @@ async def export_workflow(
268
279
  name=wftask.task.name,
269
280
  )
270
281
 
271
- wf = WorkflowExportV2(
282
+ wf = WorkflowExport(
272
283
  **workflow.model_dump(),
273
284
  task_list=wf_task_list,
274
285
  )
@@ -293,10 +304,11 @@ async def get_workflow_type_filters(
293
304
  Get info on type/type-filters flow for a workflow.
294
305
  """
295
306
 
296
- workflow = await _get_workflow_check_owner(
307
+ workflow = await _get_workflow_check_access(
297
308
  project_id=project_id,
298
309
  workflow_id=workflow_id,
299
310
  user_id=user.id,
311
+ required_permissions=ProjectPermissions.READ,
300
312
  db=db,
301
313
  )
302
314