fractal-server 2.18.0__py3-none-any.whl → 2.18.0a1__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 (95) 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/job.py +15 -15
  8. fractal_server/app/routes/admin/v2/task.py +7 -7
  9. fractal_server/app/routes/admin/v2/task_group.py +12 -14
  10. fractal_server/app/routes/admin/v2/task_group_lifecycle.py +20 -20
  11. fractal_server/app/routes/api/__init__.py +9 -0
  12. fractal_server/app/routes/api/v2/__init__.py +49 -47
  13. fractal_server/app/routes/api/v2/_aux_functions.py +47 -22
  14. fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +4 -4
  15. fractal_server/app/routes/api/v2/_aux_functions_tasks.py +2 -2
  16. fractal_server/app/routes/api/v2/dataset.py +60 -66
  17. fractal_server/app/routes/api/v2/history.py +5 -7
  18. fractal_server/app/routes/api/v2/job.py +12 -12
  19. fractal_server/app/routes/api/v2/project.py +11 -11
  20. fractal_server/app/routes/api/v2/status_legacy.py +29 -15
  21. fractal_server/app/routes/api/v2/submit.py +66 -65
  22. fractal_server/app/routes/api/v2/task.py +17 -15
  23. fractal_server/app/routes/api/v2/task_collection.py +18 -18
  24. fractal_server/app/routes/api/v2/task_collection_custom.py +13 -11
  25. fractal_server/app/routes/api/v2/task_collection_pixi.py +9 -9
  26. fractal_server/app/routes/api/v2/task_group.py +18 -18
  27. fractal_server/app/routes/api/v2/task_group_lifecycle.py +26 -26
  28. fractal_server/app/routes/api/v2/task_version_update.py +5 -5
  29. fractal_server/app/routes/api/v2/workflow.py +18 -18
  30. fractal_server/app/routes/api/v2/workflow_import.py +11 -11
  31. fractal_server/app/routes/api/v2/workflowtask.py +37 -10
  32. fractal_server/app/routes/auth/_aux_auth.py +0 -100
  33. fractal_server/app/routes/auth/current_user.py +63 -0
  34. fractal_server/app/routes/auth/group.py +30 -1
  35. fractal_server/app/routes/auth/router.py +0 -2
  36. fractal_server/app/routes/auth/users.py +0 -9
  37. fractal_server/app/schemas/user.py +12 -29
  38. fractal_server/app/schemas/user_group.py +15 -0
  39. fractal_server/app/schemas/v2/__init__.py +48 -48
  40. fractal_server/app/schemas/v2/dataset.py +13 -35
  41. fractal_server/app/schemas/v2/dumps.py +9 -9
  42. fractal_server/app/schemas/v2/job.py +11 -11
  43. fractal_server/app/schemas/v2/project.py +3 -3
  44. fractal_server/app/schemas/v2/resource.py +4 -13
  45. fractal_server/app/schemas/v2/status_legacy.py +3 -3
  46. fractal_server/app/schemas/v2/task.py +6 -6
  47. fractal_server/app/schemas/v2/task_collection.py +4 -4
  48. fractal_server/app/schemas/v2/task_group.py +16 -16
  49. fractal_server/app/schemas/v2/workflow.py +16 -16
  50. fractal_server/app/schemas/v2/workflowtask.py +14 -14
  51. fractal_server/app/security/__init__.py +1 -1
  52. fractal_server/app/shutdown.py +6 -6
  53. fractal_server/config/__init__.py +6 -0
  54. fractal_server/config/_data.py +79 -0
  55. fractal_server/config/_main.py +1 -6
  56. fractal_server/images/models.py +2 -1
  57. fractal_server/main.py +11 -72
  58. fractal_server/runner/config/_slurm.py +0 -2
  59. fractal_server/runner/executors/slurm_common/slurm_config.py +0 -1
  60. fractal_server/runner/v2/_local.py +3 -4
  61. fractal_server/runner/v2/_slurm_ssh.py +3 -4
  62. fractal_server/runner/v2/_slurm_sudo.py +3 -4
  63. fractal_server/runner/v2/runner.py +17 -36
  64. fractal_server/runner/v2/runner_functions.py +14 -11
  65. fractal_server/runner/v2/submit_workflow.py +9 -22
  66. fractal_server/tasks/v2/local/_utils.py +2 -2
  67. fractal_server/tasks/v2/local/collect.py +6 -5
  68. fractal_server/tasks/v2/local/collect_pixi.py +6 -5
  69. fractal_server/tasks/v2/local/deactivate.py +7 -7
  70. fractal_server/tasks/v2/local/deactivate_pixi.py +3 -3
  71. fractal_server/tasks/v2/local/delete.py +5 -5
  72. fractal_server/tasks/v2/local/reactivate.py +5 -5
  73. fractal_server/tasks/v2/local/reactivate_pixi.py +5 -5
  74. fractal_server/tasks/v2/ssh/collect.py +5 -5
  75. fractal_server/tasks/v2/ssh/collect_pixi.py +5 -5
  76. fractal_server/tasks/v2/ssh/deactivate.py +7 -7
  77. fractal_server/tasks/v2/ssh/deactivate_pixi.py +2 -2
  78. fractal_server/tasks/v2/ssh/delete.py +5 -5
  79. fractal_server/tasks/v2/ssh/reactivate.py +5 -5
  80. fractal_server/tasks/v2/ssh/reactivate_pixi.py +5 -5
  81. fractal_server/tasks/v2/utils_background.py +7 -7
  82. fractal_server/tasks/v2/utils_database.py +5 -5
  83. fractal_server/types/__init__.py +0 -22
  84. fractal_server/types/validators/__init__.py +0 -3
  85. fractal_server/types/validators/_common_validators.py +0 -32
  86. {fractal_server-2.18.0.dist-info → fractal_server-2.18.0a1.dist-info}/METADATA +1 -1
  87. {fractal_server-2.18.0.dist-info → fractal_server-2.18.0a1.dist-info}/RECORD +90 -95
  88. fractal_server/app/routes/auth/viewer_paths.py +0 -43
  89. fractal_server/data_migrations/2_18_0.py +0 -30
  90. fractal_server/migrations/versions/7910eed4cf97_user_project_dirs_and_usergroup_viewer_.py +0 -60
  91. fractal_server/migrations/versions/88270f589c9b_add_prevent_new_submissions.py +0 -39
  92. fractal_server/migrations/versions/f0702066b007_one_submitted_job_per_dataset.py +0 -40
  93. {fractal_server-2.18.0.dist-info → fractal_server-2.18.0a1.dist-info}/WHEEL +0 -0
  94. {fractal_server-2.18.0.dist-info → fractal_server-2.18.0a1.dist-info}/entry_points.txt +0 -0
  95. {fractal_server-2.18.0.dist-info → fractal_server-2.18.0a1.dist-info}/licenses/LICENSE +0 -0
@@ -1,21 +1,12 @@
1
- from os.path import normpath
2
- from pathlib import Path
3
-
4
1
  from fastapi import HTTPException
5
2
  from fastapi import status
6
3
  from sqlalchemy.ext.asyncio import AsyncSession
7
- from sqlmodel import and_
8
4
  from sqlmodel import asc
9
- from sqlmodel import not_
10
- from sqlmodel import or_
11
5
  from sqlmodel import select
12
6
 
13
7
  from fractal_server.app.models.linkusergroup import LinkUserGroup
14
- from fractal_server.app.models.linkuserproject import LinkUserProjectV2
15
8
  from fractal_server.app.models.security import UserGroup
16
9
  from fractal_server.app.models.security import UserOAuth
17
- from fractal_server.app.models.v2.dataset import DatasetV2
18
- from fractal_server.app.models.v2.project import ProjectV2
19
10
  from fractal_server.app.schemas.user import UserRead
20
11
  from fractal_server.app.schemas.user_group import UserGroupRead
21
12
  from fractal_server.config import get_settings
@@ -187,94 +178,3 @@ async def _verify_user_belongs_to_group(
187
178
  f"to UserGroup {user_group_id}"
188
179
  ),
189
180
  )
190
-
191
-
192
- async def _check_project_dirs_update(
193
- *,
194
- old_project_dirs: list[str],
195
- new_project_dirs: list[str],
196
- user_id: int,
197
- db: AsyncSession,
198
- ) -> None:
199
- """
200
- Raises 422 if by replacing user's `project_dirs` with new ones we are
201
- removing the access to a `zarr_dir` used by some dataset.
202
-
203
- Note both `old_project_dirs` and `new_project_dirs` have been
204
- normalized through `os.path.normpath`, which notably strips any trailing
205
- `/` character. To be safe, we also re-normalize them within this function.
206
- """
207
- # Create a list of all the old project dirs that will lose privileges.
208
- # E.g.:
209
- # old_project_dirs = ["/a", "/b", "/c/d", "/e/f"]
210
- # new_project_dirs = ["/a", "/c", "/e/f/g1", "/e/f/g2"]
211
- # removed_project_dirs == ["/b", "/e/f"]
212
- removed_project_dirs = [
213
- old_project_dir
214
- for old_project_dir in old_project_dirs
215
- if not any(
216
- Path(old_project_dir).is_relative_to(new_project_dir)
217
- for new_project_dir in new_project_dirs
218
- )
219
- ]
220
- if removed_project_dirs:
221
- # Query all the `zarr_dir`s linked to the user such that `zarr_dir`
222
- # starts with one of the project dirs in `removed_project_dirs`.
223
- stmt = (
224
- select(DatasetV2.zarr_dir)
225
- .join(ProjectV2, ProjectV2.id == DatasetV2.project_id)
226
- .join(
227
- LinkUserProjectV2,
228
- LinkUserProjectV2.project_id == ProjectV2.id,
229
- )
230
- .where(LinkUserProjectV2.user_id == user_id)
231
- .where(LinkUserProjectV2.is_verified.is_(True))
232
- .where(
233
- or_(
234
- *[
235
- DatasetV2.zarr_dir.startswith(normpath(old_project_dir))
236
- for old_project_dir in removed_project_dirs
237
- ]
238
- )
239
- )
240
- )
241
- if new_project_dirs:
242
- stmt = stmt.where(
243
- and_(
244
- *[
245
- not_(
246
- DatasetV2.zarr_dir.startswith(
247
- normpath(new_project_dir)
248
- )
249
- )
250
- for new_project_dir in new_project_dirs
251
- ]
252
- )
253
- )
254
- res = await db.execute(stmt)
255
-
256
- # Raise 422 if one of the query results is relative to a path in
257
- # `removed_project_dirs`, but its not relative to any path in
258
- # `new_project_dirs`.
259
- if any(
260
- (
261
- any(
262
- Path(zarr_dir).is_relative_to(old_project_dir)
263
- for old_project_dir in removed_project_dirs
264
- )
265
- and not any(
266
- Path(zarr_dir).is_relative_to(new_project_dir)
267
- for new_project_dir in new_project_dirs
268
- )
269
- )
270
- for zarr_dir in res.scalars().all()
271
- ):
272
- raise HTTPException(
273
- status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
274
- detail=(
275
- "You tried updating the user project_dirs, removing "
276
- f"{removed_project_dirs}. This operation is not possible, "
277
- "because it would make the user loose access to some of "
278
- "their dataset zarr directories."
279
- ),
280
- )
@@ -2,16 +2,21 @@
2
2
  Definition of `/auth/current-user/` endpoints
3
3
  """
4
4
 
5
+ import os
6
+
5
7
  from fastapi import APIRouter
6
8
  from fastapi import Depends
7
9
  from sqlalchemy.ext.asyncio import AsyncSession
8
10
  from sqlmodel import select
9
11
 
10
12
  from fractal_server.app.db import get_async_db
13
+ from fractal_server.app.models import LinkUserGroup
11
14
  from fractal_server.app.models import Profile
12
15
  from fractal_server.app.models import Resource
16
+ from fractal_server.app.models import UserGroup
13
17
  from fractal_server.app.models import UserOAuth
14
18
  from fractal_server.app.routes.auth import current_user_act
19
+ from fractal_server.app.routes.auth import current_user_act_ver
15
20
  from fractal_server.app.routes.auth._aux_auth import (
16
21
  _get_single_user_with_groups,
17
22
  )
@@ -21,6 +26,9 @@ from fractal_server.app.schemas.user import UserUpdate
21
26
  from fractal_server.app.schemas.user import UserUpdateStrict
22
27
  from fractal_server.app.security import UserManager
23
28
  from fractal_server.app.security import get_user_manager
29
+ from fractal_server.config import DataAuthScheme
30
+ from fractal_server.config import get_data_settings
31
+ from fractal_server.syringe import Inject
24
32
 
25
33
  router_current_user = APIRouter()
26
34
 
@@ -98,3 +106,58 @@ async def get_current_user_profile_info(
98
106
  )
99
107
 
100
108
  return response_data
109
+
110
+
111
+ @router_current_user.get(
112
+ "/current-user/allowed-viewer-paths/", response_model=list[str]
113
+ )
114
+ async def get_current_user_allowed_viewer_paths(
115
+ current_user: UserOAuth = Depends(current_user_act_ver),
116
+ db: AsyncSession = Depends(get_async_db),
117
+ ) -> list[str]:
118
+ """
119
+ Returns the allowed viewer paths for current user, according to the
120
+ selected FRACTAL_DATA_AUTH_SCHEME
121
+ """
122
+
123
+ data_settings = Inject(get_data_settings)
124
+
125
+ authorized_paths = []
126
+
127
+ if data_settings.FRACTAL_DATA_AUTH_SCHEME == DataAuthScheme.NONE:
128
+ return authorized_paths
129
+
130
+ # Append `project_dir` to the list of authorized paths
131
+ authorized_paths.append(current_user.project_dir)
132
+
133
+ # If auth scheme is "users-folders" and `slurm_user` is set,
134
+ # build and append the user folder
135
+ if (
136
+ data_settings.FRACTAL_DATA_AUTH_SCHEME == DataAuthScheme.USERS_FOLDERS
137
+ and current_user.profile_id is not None
138
+ ):
139
+ profile = await db.get(Profile, current_user.profile_id)
140
+ if profile is not None and profile.username is not None:
141
+ base_folder = data_settings.FRACTAL_DATA_BASE_FOLDER
142
+ user_folder = os.path.join(base_folder, profile.username)
143
+ authorized_paths.append(user_folder)
144
+
145
+ if data_settings.FRACTAL_DATA_AUTH_SCHEME == DataAuthScheme.VIEWER_PATHS:
146
+ # Returns the union of `viewer_paths` for all user's groups
147
+ cmd = (
148
+ select(UserGroup.viewer_paths)
149
+ .join(LinkUserGroup, LinkUserGroup.group_id == UserGroup.id)
150
+ .where(LinkUserGroup.user_id == current_user.id)
151
+ )
152
+ res = await db.execute(cmd)
153
+ viewer_paths_nested = res.scalars().all()
154
+
155
+ # Flatten a nested object and make its elements unique
156
+ all_viewer_paths_set = {
157
+ path
158
+ for _viewer_paths in viewer_paths_nested
159
+ for path in _viewer_paths
160
+ }
161
+ authorized_paths.extend(all_viewer_paths_set)
162
+
163
+ return authorized_paths
@@ -16,6 +16,7 @@ from fractal_server.app.models import UserGroup
16
16
  from fractal_server.app.models import UserOAuth
17
17
  from fractal_server.app.schemas.user_group import UserGroupCreate
18
18
  from fractal_server.app.schemas.user_group import UserGroupRead
19
+ from fractal_server.app.schemas.user_group import UserGroupUpdate
19
20
  from fractal_server.config import get_settings
20
21
  from fractal_server.logger import set_logger
21
22
  from fractal_server.syringe import Inject
@@ -100,13 +101,41 @@ async def create_single_group(
100
101
  )
101
102
 
102
103
  # Create and return new group
103
- new_group = UserGroup(name=group_create.name)
104
+ new_group = UserGroup(
105
+ name=group_create.name, viewer_paths=group_create.viewer_paths
106
+ )
104
107
  db.add(new_group)
105
108
  await db.commit()
106
109
 
107
110
  return dict(new_group.model_dump(), user_ids=[])
108
111
 
109
112
 
113
+ @router_group.patch(
114
+ "/group/{group_id}/",
115
+ response_model=UserGroupRead,
116
+ status_code=status.HTTP_200_OK,
117
+ )
118
+ async def update_single_group(
119
+ group_id: int,
120
+ group_update: UserGroupUpdate,
121
+ user: UserOAuth = Depends(current_superuser_act),
122
+ db: AsyncSession = Depends(get_async_db),
123
+ ) -> UserGroupRead:
124
+ group = await _usergroup_or_404(group_id, db)
125
+
126
+ # Patch `viewer_paths`
127
+ if group_update.viewer_paths is not None:
128
+ group.viewer_paths = group_update.viewer_paths
129
+ db.add(group)
130
+ await db.commit()
131
+
132
+ updated_group = await _get_single_usergroup_with_user_ids(
133
+ group_id=group_id, db=db
134
+ )
135
+
136
+ return updated_group
137
+
138
+
110
139
  @router_group.delete("/group/{group_id}/", status_code=204)
111
140
  async def delete_single_group(
112
141
  group_id: int,
@@ -6,7 +6,6 @@ from .login import router_login
6
6
  from .oauth import get_oauth_router
7
7
  from .register import router_register
8
8
  from .users import router_users
9
- from .viewer_paths import router_viewer_paths
10
9
 
11
10
  router_auth = APIRouter()
12
11
 
@@ -15,7 +14,6 @@ router_auth.include_router(router_current_user)
15
14
  router_auth.include_router(router_login)
16
15
  router_auth.include_router(router_users)
17
16
  router_auth.include_router(router_group)
18
- router_auth.include_router(router_viewer_paths)
19
17
  router_oauth = get_oauth_router()
20
18
  if router_oauth is not None:
21
19
  router_auth.include_router(router_oauth)
@@ -28,7 +28,6 @@ from fractal_server.logger import set_logger
28
28
  from fractal_server.syringe import Inject
29
29
 
30
30
  from . import current_superuser_act
31
- from ._aux_auth import _check_project_dirs_update
32
31
  from ._aux_auth import _get_default_usergroup_id_or_none
33
32
  from ._aux_auth import _get_single_user_with_groups
34
33
 
@@ -75,14 +74,6 @@ async def patch_user(
75
74
  detail=f"Profile {user_update.profile_id} not found.",
76
75
  )
77
76
 
78
- if user_update.project_dirs is not None:
79
- await _check_project_dirs_update(
80
- old_project_dirs=user_to_patch.project_dirs,
81
- new_project_dirs=user_update.project_dirs,
82
- user_id=user_id,
83
- db=db,
84
- )
85
-
86
77
  # Modify user attributes
87
78
  try:
88
79
  user = await user_manager.update(
@@ -8,18 +8,12 @@ from pydantic import EmailStr
8
8
  from pydantic import Field
9
9
 
10
10
  from fractal_server.string_tools import validate_cmd
11
- from fractal_server.types import ListUniqueAbsolutePathStr
11
+ from fractal_server.types import AbsolutePathStr
12
12
  from fractal_server.types import ListUniqueNonEmptyString
13
13
  from fractal_server.types import ListUniqueNonNegativeInt
14
14
  from fractal_server.types import NonEmptyStr
15
15
 
16
16
 
17
- def _validate_cmd_list(value: list[str]) -> list[str]:
18
- for v in value:
19
- validate_cmd(v)
20
- return value
21
-
22
-
23
17
  class OAuthAccountRead(BaseModel):
24
18
  """
25
19
  Schema for storing essential `OAuthAccount` information within
@@ -44,17 +38,20 @@ class UserRead(schemas.BaseUser[int]):
44
38
  group_ids_names:
45
39
  oauth_accounts:
46
40
  profile_id:
47
- project_dirs:
48
- slurm_accounts:
49
41
  """
50
42
 
51
43
  group_ids_names: list[tuple[int, str]] | None = None
52
44
  oauth_accounts: list[OAuthAccountRead]
53
45
  profile_id: int | None = None
54
- project_dirs: list[str]
46
+ project_dir: str
55
47
  slurm_accounts: list[str]
56
48
 
57
49
 
50
+ def _validate_cmd(value: str) -> str:
51
+ validate_cmd(value)
52
+ return value
53
+
54
+
58
55
  class UserUpdate(schemas.BaseUserUpdate):
59
56
  """
60
57
  Schema for `User` update.
@@ -66,7 +63,7 @@ class UserUpdate(schemas.BaseUserUpdate):
66
63
  is_superuser:
67
64
  is_verified:
68
65
  profile_id:
69
- project_dirs:
66
+ project_dir:
70
67
  slurm_accounts:
71
68
  """
72
69
 
@@ -77,9 +74,9 @@ class UserUpdate(schemas.BaseUserUpdate):
77
74
  is_superuser: bool = None
78
75
  is_verified: bool = None
79
76
  profile_id: int | None = None
80
- project_dirs: Annotated[
81
- ListUniqueAbsolutePathStr, AfterValidator(_validate_cmd_list)
82
- ] = Field(default=None, min_length=1)
77
+ project_dir: Annotated[AbsolutePathStr, AfterValidator(_validate_cmd)] = (
78
+ None
79
+ )
83
80
  slurm_accounts: ListUniqueNonEmptyString = None
84
81
 
85
82
 
@@ -101,14 +98,10 @@ class UserCreate(schemas.BaseUserCreate):
101
98
 
102
99
  Attributes:
103
100
  profile_id:
104
- project_dirs:
105
- slurm_accounts:
106
101
  """
107
102
 
108
103
  profile_id: int | None = None
109
- project_dirs: Annotated[
110
- ListUniqueAbsolutePathStr, AfterValidator(_validate_cmd_list)
111
- ] = Field(min_length=1)
104
+ project_dir: Annotated[AbsolutePathStr, AfterValidator(_validate_cmd)]
112
105
  slurm_accounts: list[str] = Field(default_factory=list)
113
106
 
114
107
 
@@ -116,8 +109,6 @@ class UserUpdateGroups(BaseModel):
116
109
  """
117
110
  Schema for `POST /auth/users/{user_id}/set-groups/`
118
111
 
119
- Attributes:
120
- group_ids:
121
112
  """
122
113
 
123
114
  model_config = ConfigDict(extra="forbid")
@@ -126,14 +117,6 @@ class UserUpdateGroups(BaseModel):
126
117
 
127
118
 
128
119
  class UserProfileInfo(BaseModel):
129
- """
130
- Attributes:
131
- has_profile:
132
- resource_name:
133
- profile_name:
134
- username:
135
- """
136
-
137
120
  has_profile: bool
138
121
  resource_name: str | None = None
139
122
  profile_name: str | None = None
@@ -2,13 +2,16 @@ from datetime import datetime
2
2
 
3
3
  from pydantic import BaseModel
4
4
  from pydantic import ConfigDict
5
+ from pydantic import Field
5
6
  from pydantic import field_serializer
6
7
  from pydantic.types import AwareDatetime
7
8
 
9
+ from fractal_server.types import ListUniqueAbsolutePathStr
8
10
  from fractal_server.types import NonEmptyStr
9
11
 
10
12
  __all__ = (
11
13
  "UserGroupRead",
14
+ "UserGroupUpdate",
12
15
  "UserGroupCreate",
13
16
  )
14
17
 
@@ -31,6 +34,7 @@ class UserGroupRead(BaseModel):
31
34
  name: str
32
35
  timestamp_created: AwareDatetime
33
36
  user_ids: list[int] | None = None
37
+ viewer_paths: list[str]
34
38
 
35
39
  @field_serializer("timestamp_created")
36
40
  def serialize_datetime(v: datetime) -> str:
@@ -48,3 +52,14 @@ class UserGroupCreate(BaseModel):
48
52
  model_config = ConfigDict(extra="forbid")
49
53
 
50
54
  name: NonEmptyStr
55
+ viewer_paths: ListUniqueAbsolutePathStr = Field(default_factory=list)
56
+
57
+
58
+ class UserGroupUpdate(BaseModel):
59
+ """
60
+ Schema for `UserGroup` update
61
+ """
62
+
63
+ model_config = ConfigDict(extra="forbid")
64
+
65
+ viewer_paths: ListUniqueAbsolutePathStr = None
@@ -1,25 +1,25 @@
1
1
  from .accounting import AccountingRecordRead # noqa F401
2
- from .dataset import DatasetCreate # noqa F401
3
- from .dataset import DatasetExport # noqa F401
4
- from .dataset import DatasetImport # noqa F401
5
- from .dataset import DatasetRead # noqa F401
6
- from .dataset import DatasetUpdate # noqa F401
7
- from .dumps import DatasetDump # noqa F401
8
- from .dumps import ProjectDump # noqa F401
9
- from .dumps import TaskDump # noqa F401
10
- from .dumps import TaskGroupDump # noqa F401
11
- from .dumps import WorkflowDump # noqa F401
12
- from .dumps import WorkflowTaskDump # noqa F401
2
+ from .dataset import DatasetCreateV2 # noqa F401
3
+ from .dataset import DatasetExportV2 # noqa F401
4
+ from .dataset import DatasetImportV2 # noqa F401
5
+ from .dataset import DatasetReadV2 # noqa F401
6
+ from .dataset import DatasetUpdateV2 # noqa F401
7
+ from .dumps import DatasetDumpV2 # noqa F401
8
+ from .dumps import ProjectDumpV2 # noqa F401
9
+ from .dumps import TaskDumpV2 # noqa F401
10
+ from .dumps import TaskGroupDumpV2 # noqa F401
11
+ from .dumps import WorkflowDumpV2 # noqa F401
12
+ from .dumps import WorkflowTaskDumpV2 # noqa F401
13
13
  from .history import HistoryRunRead # noqa F401
14
14
  from .history import HistoryRunReadAggregated # noqa F401
15
15
  from .history import HistoryUnitRead # noqa F401
16
16
  from .history import HistoryUnitStatus # noqa F401
17
17
  from .history import HistoryUnitStatusWithUnset # noqa F401
18
18
  from .history import ImageLogsRequest # noqa F401
19
- from .job import JobCreate # noqa F401
20
- from .job import JobRead # noqa F401
21
- from .job import JobStatusType # noqa F401
22
- from .job import JobUpdate # noqa F401
19
+ from .job import JobCreateV2 # noqa F401
20
+ from .job import JobReadV2 # noqa F401
21
+ from .job import JobStatusTypeV2 # noqa F401
22
+ from .job import JobUpdateV2 # noqa F401
23
23
  from .manifest import ManifestV2 # noqa F401
24
24
  from .manifest import TaskManifestV2 # noqa F401
25
25
  from .profile import ProfileCreate # noqa F401
@@ -27,9 +27,9 @@ from .profile import ProfileRead # noqa F401
27
27
  from .profile import ValidProfileLocal # noqa F401
28
28
  from .profile import ValidProfileSlurmSSH # noqa F401
29
29
  from .profile import ValidProfileSlurmSudo # noqa F401
30
- from .project import ProjectCreate # noqa F401
31
- from .project import ProjectRead # noqa F401
32
- from .project import ProjectUpdate # noqa F401
30
+ from .project import ProjectCreateV2 # noqa F401
31
+ from .project import ProjectReadV2 # noqa F401
32
+ from .project import ProjectUpdateV2 # noqa F401
33
33
  from .sharing import ProjectPermissions # noqa F401
34
34
  from .sharing import ProjectGuestCreate # noqa F401
35
35
  from .sharing import ProjectAccessRead # noqa F401
@@ -43,36 +43,36 @@ from .resource import ResourceType # noqa F401
43
43
  from .resource import ValidResourceLocal # noqa F401
44
44
  from .resource import ValidResourceSlurmSSH # noqa F401
45
45
  from .resource import ValidResourceSlurmSudo # noqa F401
46
- from .status_legacy import WorkflowTaskStatusType # noqa F401
47
- from .task import TaskCreate # noqa F401
48
- from .task import TaskExport # noqa F401
49
- from .task import TaskImport # noqa F401
50
- from .task import TaskImportLegacy # noqa F401
51
- from .task import TaskRead # noqa F401
46
+ from .status_legacy import WorkflowTaskStatusTypeV2 # noqa F401
47
+ from .task import TaskCreateV2 # noqa F401
48
+ from .task import TaskExportV2 # noqa F401
49
+ from .task import TaskImportV2 # noqa F401
50
+ from .task import TaskImportV2Legacy # noqa F401
51
+ from .task import TaskReadV2 # noqa F401
52
52
  from .task import TaskType # noqa F401
53
- from .task import TaskUpdate # noqa F401
53
+ from .task import TaskUpdateV2 # noqa F401
54
54
  from .task_collection import FractalUploadedFile # noqa F401
55
- from .task_collection import TaskCollectCustom # noqa F401
56
- from .task_collection import TaskCollectPip # noqa F401
57
- from .task_group import TaskGroupActivityAction # noqa F401
58
- from .task_group import TaskGroupActivityStatus # noqa F401
59
- from .task_group import TaskGroupActivityRead # noqa F401
60
- from .task_group import TaskGroupCreate # noqa F401
61
- from .task_group import TaskGroupCreateStrict # noqa F401
55
+ from .task_collection import TaskCollectCustomV2 # noqa F401
56
+ from .task_collection import TaskCollectPipV2 # noqa F401
57
+ from .task_group import TaskGroupActivityActionV2 # noqa F401
58
+ from .task_group import TaskGroupActivityStatusV2 # noqa F401
59
+ from .task_group import TaskGroupActivityV2Read # noqa F401
60
+ from .task_group import TaskGroupCreateV2 # noqa F401
61
+ from .task_group import TaskGroupCreateV2Strict # noqa F401
62
62
  from .task_group import TaskGroupReadSuperuser # noqa F401
63
- from .task_group import TaskGroupRead # noqa F401
64
- from .task_group import TaskGroupUpdate # noqa F401
65
- from .task_group import TaskGroupOriginEnum # noqa F401
66
- from .workflow import WorkflowCreate # noqa F401
67
- from .workflow import WorkflowExport # noqa F401
68
- from .workflow import WorkflowImport # noqa F401
69
- from .workflow import WorkflowRead # noqa F401
70
- from .workflow import WorkflowReadWithWarnings # noqa F401
71
- from .workflow import WorkflowUpdate # noqa F401
72
- from .workflowtask import WorkflowTaskCreate # noqa F401
73
- from .workflowtask import WorkflowTaskExport # noqa F401
74
- from .workflowtask import WorkflowTaskImport # noqa F401
75
- from .workflowtask import WorkflowTaskRead # noqa F401
76
- from .workflowtask import WorkflowTaskReadWithWarning # noqa F401
77
- from .workflowtask import WorkflowTaskReplace # noqa F401
78
- from .workflowtask import WorkflowTaskUpdate # noqa F401
63
+ from .task_group import TaskGroupReadV2 # noqa F401
64
+ from .task_group import TaskGroupUpdateV2 # noqa F401
65
+ from .task_group import TaskGroupV2OriginEnum # noqa F401
66
+ from .workflow import WorkflowCreateV2 # noqa F401
67
+ from .workflow import WorkflowExportV2 # noqa F401
68
+ from .workflow import WorkflowImportV2 # noqa F401
69
+ from .workflow import WorkflowReadV2 # noqa F401
70
+ from .workflow import WorkflowReadV2WithWarnings # noqa F401
71
+ from .workflow import WorkflowUpdateV2 # noqa F401
72
+ from .workflowtask import WorkflowTaskCreateV2 # noqa F401
73
+ from .workflowtask import WorkflowTaskExportV2 # noqa F401
74
+ from .workflowtask import WorkflowTaskImportV2 # noqa F401
75
+ from .workflowtask import WorkflowTaskReadV2 # noqa F401
76
+ from .workflowtask import WorkflowTaskReadV2WithWarning # noqa F401
77
+ from .workflowtask import WorkflowTaskReplaceV2 # noqa F401
78
+ from .workflowtask import WorkflowTaskUpdateV2 # noqa F401
@@ -1,49 +1,35 @@
1
1
  from datetime import datetime
2
- from pathlib import Path
3
2
 
4
3
  from pydantic import BaseModel
5
4
  from pydantic import ConfigDict
6
5
  from pydantic import Field
7
6
  from pydantic import field_serializer
8
- from pydantic import model_validator
9
7
  from pydantic.types import AwareDatetime
10
8
 
11
- from fractal_server.app.schemas.v2.project import ProjectRead
9
+ from fractal_server.app.schemas.v2.project import ProjectReadV2
12
10
  from fractal_server.images import SingleImage
13
- from fractal_server.types import AbsolutePathStr
14
11
  from fractal_server.types import NonEmptyStr
15
- from fractal_server.types import RelativePathStr
16
12
  from fractal_server.types import ZarrDirStr
17
13
 
18
14
 
19
- class DatasetCreate(BaseModel):
15
+ class DatasetCreateV2(BaseModel):
20
16
  """
21
- DatasetCreate
17
+ DatasetCreateV2
22
18
 
23
19
  Attributes:
24
20
  name:
25
- project_dir:
26
- zarr_subfolder:
21
+ zarr_dir:
27
22
  """
28
23
 
29
24
  model_config = ConfigDict(extra="forbid")
30
25
 
31
26
  name: NonEmptyStr
32
- project_dir: AbsolutePathStr | None = None
33
- zarr_subfolder: RelativePathStr | None = None
34
-
35
- @model_validator(mode="after")
36
- def validate_zarr_dir(self):
37
- if (self.project_dir is None) and (self.zarr_subfolder is not None):
38
- raise ValueError(
39
- "Cannot provide `zarr_subfolder` without `project_dir`"
40
- )
41
- return self
27
+ zarr_dir: ZarrDirStr | None = None
42
28
 
43
29
 
44
- class DatasetRead(BaseModel):
30
+ class DatasetReadV2(BaseModel):
45
31
  """
46
- DatasetRead
32
+ DatasetReadV2
47
33
 
48
34
  Attributes:
49
35
  id:
@@ -58,7 +44,7 @@ class DatasetRead(BaseModel):
58
44
  name: str
59
45
 
60
46
  project_id: int
61
- project: ProjectRead
47
+ project: ProjectReadV2
62
48
 
63
49
  timestamp_created: AwareDatetime
64
50
 
@@ -69,9 +55,9 @@ class DatasetRead(BaseModel):
69
55
  return v.isoformat()
70
56
 
71
57
 
72
- class DatasetUpdate(BaseModel):
58
+ class DatasetUpdateV2(BaseModel):
73
59
  """
74
- DatasetUpdate
60
+ DatasetUpdateV2
75
61
 
76
62
  Attributes:
77
63
  name:
@@ -81,9 +67,10 @@ class DatasetUpdate(BaseModel):
81
67
  model_config = ConfigDict(extra="forbid")
82
68
 
83
69
  name: NonEmptyStr = None
70
+ zarr_dir: ZarrDirStr | None = None
84
71
 
85
72
 
86
- class DatasetImport(BaseModel):
73
+ class DatasetImportV2(BaseModel):
87
74
  """
88
75
  Class for `Dataset` import.
89
76
 
@@ -101,17 +88,8 @@ class DatasetImport(BaseModel):
101
88
  zarr_dir: ZarrDirStr
102
89
  images: list[SingleImage] = Field(default_factory=list)
103
90
 
104
- @model_validator(mode="after")
105
- def validate_image_zarr_url(self):
106
- for image in self.images:
107
- if not Path(image.zarr_url).is_relative_to(self.zarr_dir):
108
- raise ValueError(
109
- f"{image.zarr_url=} is not relative to {self.zarr_dir=}."
110
- )
111
- return self
112
-
113
91
 
114
- class DatasetExport(BaseModel):
92
+ class DatasetExportV2(BaseModel):
115
93
  """
116
94
  Class for `Dataset` export.
117
95