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
@@ -8,12 +8,18 @@ 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 AbsolutePathStr
11
+ from fractal_server.types import ListUniqueAbsolutePathStr
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
+
17
23
  class OAuthAccountRead(BaseModel):
18
24
  """
19
25
  Schema for storing essential `OAuthAccount` information within
@@ -38,20 +44,17 @@ class UserRead(schemas.BaseUser[int]):
38
44
  group_ids_names:
39
45
  oauth_accounts:
40
46
  profile_id:
47
+ project_dirs:
48
+ slurm_accounts:
41
49
  """
42
50
 
43
51
  group_ids_names: list[tuple[int, str]] | None = None
44
52
  oauth_accounts: list[OAuthAccountRead]
45
53
  profile_id: int | None = None
46
- project_dir: str
54
+ project_dirs: list[str]
47
55
  slurm_accounts: list[str]
48
56
 
49
57
 
50
- def _validate_cmd(value: str) -> str:
51
- validate_cmd(value)
52
- return value
53
-
54
-
55
58
  class UserUpdate(schemas.BaseUserUpdate):
56
59
  """
57
60
  Schema for `User` update.
@@ -63,7 +66,7 @@ class UserUpdate(schemas.BaseUserUpdate):
63
66
  is_superuser:
64
67
  is_verified:
65
68
  profile_id:
66
- project_dir:
69
+ project_dirs:
67
70
  slurm_accounts:
68
71
  """
69
72
 
@@ -74,9 +77,9 @@ class UserUpdate(schemas.BaseUserUpdate):
74
77
  is_superuser: bool = None
75
78
  is_verified: bool = None
76
79
  profile_id: int | None = None
77
- project_dir: Annotated[AbsolutePathStr, AfterValidator(_validate_cmd)] = (
78
- None
79
- )
80
+ project_dirs: Annotated[
81
+ ListUniqueAbsolutePathStr, AfterValidator(_validate_cmd_list)
82
+ ] = Field(default=None, min_length=1)
80
83
  slurm_accounts: ListUniqueNonEmptyString = None
81
84
 
82
85
 
@@ -98,10 +101,14 @@ class UserCreate(schemas.BaseUserCreate):
98
101
 
99
102
  Attributes:
100
103
  profile_id:
104
+ project_dirs:
105
+ slurm_accounts:
101
106
  """
102
107
 
103
108
  profile_id: int | None = None
104
- project_dir: Annotated[AbsolutePathStr, AfterValidator(_validate_cmd)]
109
+ project_dirs: Annotated[
110
+ ListUniqueAbsolutePathStr, AfterValidator(_validate_cmd_list)
111
+ ] = Field(min_length=1)
105
112
  slurm_accounts: list[str] = Field(default_factory=list)
106
113
 
107
114
 
@@ -109,6 +116,8 @@ class UserUpdateGroups(BaseModel):
109
116
  """
110
117
  Schema for `POST /auth/users/{user_id}/set-groups/`
111
118
 
119
+ Attributes:
120
+ group_ids:
112
121
  """
113
122
 
114
123
  model_config = ConfigDict(extra="forbid")
@@ -117,6 +126,14 @@ class UserUpdateGroups(BaseModel):
117
126
 
118
127
 
119
128
  class UserProfileInfo(BaseModel):
129
+ """
130
+ Attributes:
131
+ has_profile:
132
+ resource_name:
133
+ profile_name:
134
+ username:
135
+ """
136
+
120
137
  has_profile: bool
121
138
  resource_name: str | None = None
122
139
  profile_name: str | None = None
@@ -2,16 +2,13 @@ from datetime import datetime
2
2
 
3
3
  from pydantic import BaseModel
4
4
  from pydantic import ConfigDict
5
- from pydantic import Field
6
5
  from pydantic import field_serializer
7
6
  from pydantic.types import AwareDatetime
8
7
 
9
- from fractal_server.types import ListUniqueAbsolutePathStr
10
8
  from fractal_server.types import NonEmptyStr
11
9
 
12
10
  __all__ = (
13
11
  "UserGroupRead",
14
- "UserGroupUpdate",
15
12
  "UserGroupCreate",
16
13
  )
17
14
 
@@ -34,7 +31,6 @@ class UserGroupRead(BaseModel):
34
31
  name: str
35
32
  timestamp_created: AwareDatetime
36
33
  user_ids: list[int] | None = None
37
- viewer_paths: list[str]
38
34
 
39
35
  @field_serializer("timestamp_created")
40
36
  def serialize_datetime(v: datetime) -> str:
@@ -52,14 +48,3 @@ class UserGroupCreate(BaseModel):
52
48
  model_config = ConfigDict(extra="forbid")
53
49
 
54
50
  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 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
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
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 JobCreateV2 # noqa F401
20
- from .job import JobReadV2 # noqa F401
21
- from .job import JobStatusTypeV2 # noqa F401
22
- from .job import JobUpdateV2 # 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
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,45 +27,52 @@ 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 ProjectCreateV2 # noqa F401
31
- from .project import ProjectReadV2 # noqa F401
32
- from .project import ProjectUpdateV2 # noqa F401
30
+ from .project import ProjectCreate # noqa F401
31
+ from .project import ProjectRead # noqa F401
32
+ from .project import ProjectUpdate # noqa F401
33
+ from .sharing import ProjectPermissions # noqa F401
34
+ from .sharing import ProjectGuestCreate # noqa F401
35
+ from .sharing import ProjectAccessRead # noqa F401
36
+ from .sharing import ProjectInvitationRead # noqa F401
37
+ from .sharing import ProjectGuestRead # noqa F401
38
+ from .sharing import ProjectGuestUpdate # noqa F401
39
+ from .sharing import LinkUserProjectRead # noqa F401
33
40
  from .resource import ResourceCreate # noqa F401
34
41
  from .resource import ResourceRead # noqa F401
35
42
  from .resource import ResourceType # noqa F401
36
43
  from .resource import ValidResourceLocal # noqa F401
37
44
  from .resource import ValidResourceSlurmSSH # noqa F401
38
45
  from .resource import ValidResourceSlurmSudo # noqa F401
39
- from .status_legacy import WorkflowTaskStatusTypeV2 # noqa F401
40
- from .task import TaskCreateV2 # noqa F401
41
- from .task import TaskExportV2 # noqa F401
42
- from .task import TaskImportV2 # noqa F401
43
- from .task import TaskImportV2Legacy # noqa F401
44
- from .task import TaskReadV2 # 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
45
52
  from .task import TaskType # noqa F401
46
- from .task import TaskUpdateV2 # noqa F401
53
+ from .task import TaskUpdate # noqa F401
47
54
  from .task_collection import FractalUploadedFile # noqa F401
48
- from .task_collection import TaskCollectCustomV2 # noqa F401
49
- from .task_collection import TaskCollectPipV2 # noqa F401
50
- from .task_group import TaskGroupActivityActionV2 # noqa F401
51
- from .task_group import TaskGroupActivityStatusV2 # noqa F401
52
- from .task_group import TaskGroupActivityV2Read # noqa F401
53
- from .task_group import TaskGroupCreateV2 # noqa F401
54
- from .task_group import TaskGroupCreateV2Strict # 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
62
  from .task_group import TaskGroupReadSuperuser # noqa F401
56
- from .task_group import TaskGroupReadV2 # noqa F401
57
- from .task_group import TaskGroupUpdateV2 # noqa F401
58
- from .task_group import TaskGroupV2OriginEnum # noqa F401
59
- from .workflow import WorkflowCreateV2 # noqa F401
60
- from .workflow import WorkflowExportV2 # noqa F401
61
- from .workflow import WorkflowImportV2 # noqa F401
62
- from .workflow import WorkflowReadV2 # noqa F401
63
- from .workflow import WorkflowReadV2WithWarnings # noqa F401
64
- from .workflow import WorkflowUpdateV2 # noqa F401
65
- from .workflowtask import WorkflowTaskCreateV2 # noqa F401
66
- from .workflowtask import WorkflowTaskExportV2 # noqa F401
67
- from .workflowtask import WorkflowTaskImportV2 # noqa F401
68
- from .workflowtask import WorkflowTaskReadV2 # noqa F401
69
- from .workflowtask import WorkflowTaskReadV2WithWarning # noqa F401
70
- from .workflowtask import WorkflowTaskReplaceV2 # noqa F401
71
- from .workflowtask import WorkflowTaskUpdateV2 # 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
@@ -1,35 +1,49 @@
1
1
  from datetime import datetime
2
+ from pathlib import Path
2
3
 
3
4
  from pydantic import BaseModel
4
5
  from pydantic import ConfigDict
5
6
  from pydantic import Field
6
7
  from pydantic import field_serializer
8
+ from pydantic import model_validator
7
9
  from pydantic.types import AwareDatetime
8
10
 
9
- from fractal_server.app.schemas.v2.project import ProjectReadV2
11
+ from fractal_server.app.schemas.v2.project import ProjectRead
10
12
  from fractal_server.images import SingleImage
13
+ from fractal_server.types import AbsolutePathStr
11
14
  from fractal_server.types import NonEmptyStr
15
+ from fractal_server.types import RelativePathStr
12
16
  from fractal_server.types import ZarrDirStr
13
17
 
14
18
 
15
- class DatasetCreateV2(BaseModel):
19
+ class DatasetCreate(BaseModel):
16
20
  """
17
- DatasetCreateV2
21
+ DatasetCreate
18
22
 
19
23
  Attributes:
20
24
  name:
21
- zarr_dir:
25
+ project_dir:
26
+ zarr_subfolder:
22
27
  """
23
28
 
24
29
  model_config = ConfigDict(extra="forbid")
25
30
 
26
31
  name: NonEmptyStr
27
- zarr_dir: ZarrDirStr | None = None
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
28
42
 
29
43
 
30
- class DatasetReadV2(BaseModel):
44
+ class DatasetRead(BaseModel):
31
45
  """
32
- DatasetReadV2
46
+ DatasetRead
33
47
 
34
48
  Attributes:
35
49
  id:
@@ -44,7 +58,7 @@ class DatasetReadV2(BaseModel):
44
58
  name: str
45
59
 
46
60
  project_id: int
47
- project: ProjectReadV2
61
+ project: ProjectRead
48
62
 
49
63
  timestamp_created: AwareDatetime
50
64
 
@@ -55,9 +69,9 @@ class DatasetReadV2(BaseModel):
55
69
  return v.isoformat()
56
70
 
57
71
 
58
- class DatasetUpdateV2(BaseModel):
72
+ class DatasetUpdate(BaseModel):
59
73
  """
60
- DatasetUpdateV2
74
+ DatasetUpdate
61
75
 
62
76
  Attributes:
63
77
  name:
@@ -67,10 +81,9 @@ class DatasetUpdateV2(BaseModel):
67
81
  model_config = ConfigDict(extra="forbid")
68
82
 
69
83
  name: NonEmptyStr = None
70
- zarr_dir: ZarrDirStr | None = None
71
84
 
72
85
 
73
- class DatasetImportV2(BaseModel):
86
+ class DatasetImport(BaseModel):
74
87
  """
75
88
  Class for `Dataset` import.
76
89
 
@@ -88,8 +101,17 @@ class DatasetImportV2(BaseModel):
88
101
  zarr_dir: ZarrDirStr
89
102
  images: list[SingleImage] = Field(default_factory=list)
90
103
 
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
+
91
113
 
92
- class DatasetExportV2(BaseModel):
114
+ class DatasetExport(BaseModel):
93
115
  """
94
116
  Class for `Dataset` export.
95
117
 
@@ -13,17 +13,17 @@ from pydantic import ConfigDict
13
13
  from pydantic import Field
14
14
 
15
15
  from .task import TaskType
16
- from .task_group import TaskGroupV2OriginEnum
16
+ from .task_group import TaskGroupOriginEnum
17
17
 
18
18
 
19
- class ProjectDumpV2(BaseModel):
19
+ class ProjectDump(BaseModel):
20
20
  model_config = ConfigDict(extra="forbid")
21
21
  id: int
22
22
  name: str
23
23
  timestamp_created: str
24
24
 
25
25
 
26
- class TaskDumpV2(BaseModel):
26
+ class TaskDump(BaseModel):
27
27
  id: int
28
28
  name: str
29
29
  type: TaskType
@@ -37,7 +37,7 @@ class TaskDumpV2(BaseModel):
37
37
  output_types: dict[str, bool]
38
38
 
39
39
 
40
- class WorkflowTaskDumpV2(BaseModel):
40
+ class WorkflowTaskDump(BaseModel):
41
41
  """
42
42
  We do not include 'model_config = ConfigDict(extra="forbid")'
43
43
  because legacy data may include 'input_filters' field and we want to avoid
@@ -51,10 +51,10 @@ class WorkflowTaskDumpV2(BaseModel):
51
51
  type_filters: dict[str, bool]
52
52
 
53
53
  task_id: int | None = None
54
- task: TaskDumpV2 | None = None
54
+ task: TaskDump | None = None
55
55
 
56
56
 
57
- class WorkflowDumpV2(BaseModel):
57
+ class WorkflowDump(BaseModel):
58
58
  model_config = ConfigDict(extra="forbid")
59
59
  id: int
60
60
  name: str
@@ -62,7 +62,7 @@ class WorkflowDumpV2(BaseModel):
62
62
  timestamp_created: str
63
63
 
64
64
 
65
- class DatasetDumpV2(BaseModel):
65
+ class DatasetDump(BaseModel):
66
66
  """
67
67
  We do not include 'model_config = ConfigDict(extra="forbid")' because
68
68
  legacy data may include 'type_filters' or 'attribute_filters' and we
@@ -76,9 +76,9 @@ class DatasetDumpV2(BaseModel):
76
76
  zarr_dir: str
77
77
 
78
78
 
79
- class TaskGroupDumpV2(BaseModel):
79
+ class TaskGroupDump(BaseModel):
80
80
  id: int
81
- origin: TaskGroupV2OriginEnum
81
+ origin: TaskGroupOriginEnum
82
82
  pkg_name: str
83
83
  version: str | None = None
84
84
  python_version: str | None = None
@@ -10,15 +10,15 @@ from pydantic.types import AwareDatetime
10
10
  from pydantic.types import NonNegativeInt
11
11
  from pydantic.types import StrictStr
12
12
 
13
- from fractal_server.app.schemas.v2.dumps import DatasetDumpV2
14
- from fractal_server.app.schemas.v2.dumps import ProjectDumpV2
15
- from fractal_server.app.schemas.v2.dumps import WorkflowDumpV2
13
+ from fractal_server.app.schemas.v2.dumps import DatasetDump
14
+ from fractal_server.app.schemas.v2.dumps import ProjectDump
15
+ from fractal_server.app.schemas.v2.dumps import WorkflowDump
16
16
  from fractal_server.types import AttributeFilters
17
17
  from fractal_server.types import NonEmptyStr
18
18
  from fractal_server.types import TypeFilters
19
19
 
20
20
 
21
- class JobStatusTypeV2(StrEnum):
21
+ class JobStatusType(StrEnum):
22
22
  """
23
23
  Define the available job statuses
24
24
 
@@ -39,7 +39,7 @@ class JobStatusTypeV2(StrEnum):
39
39
  FAILED = "failed"
40
40
 
41
41
 
42
- class JobCreateV2(BaseModel):
42
+ class JobCreate(BaseModel):
43
43
  model_config = ConfigDict(extra="forbid")
44
44
 
45
45
  first_task_index: NonNegativeInt | None = None
@@ -65,16 +65,16 @@ class JobCreateV2(BaseModel):
65
65
  return values
66
66
 
67
67
 
68
- class JobReadV2(BaseModel):
68
+ class JobRead(BaseModel):
69
69
  id: int
70
70
  project_id: int | None = None
71
- project_dump: ProjectDumpV2
71
+ project_dump: ProjectDump
72
72
  user_email: str
73
73
  slurm_account: str | None = None
74
74
  workflow_id: int | None = None
75
- workflow_dump: WorkflowDumpV2
75
+ workflow_dump: WorkflowDump
76
76
  dataset_id: int | None = None
77
- dataset_dump: DatasetDumpV2
77
+ dataset_dump: DatasetDump
78
78
  start_timestamp: AwareDatetime
79
79
  end_timestamp: AwareDatetime | None = None
80
80
  status: str
@@ -100,7 +100,7 @@ class JobReadV2(BaseModel):
100
100
  return v.isoformat()
101
101
 
102
102
 
103
- class JobUpdateV2(BaseModel):
103
+ class JobUpdate(BaseModel):
104
104
  model_config = ConfigDict(extra="forbid")
105
105
 
106
- status: JobStatusTypeV2
106
+ status: JobStatusType
@@ -8,13 +8,13 @@ from pydantic.types import AwareDatetime
8
8
  from fractal_server.types import NonEmptyStr
9
9
 
10
10
 
11
- class ProjectCreateV2(BaseModel):
11
+ class ProjectCreate(BaseModel):
12
12
  model_config = ConfigDict(extra="forbid")
13
13
 
14
14
  name: NonEmptyStr
15
15
 
16
16
 
17
- class ProjectReadV2(BaseModel):
17
+ class ProjectRead(BaseModel):
18
18
  id: int
19
19
  name: str
20
20
  timestamp_created: AwareDatetime
@@ -24,7 +24,7 @@ class ProjectReadV2(BaseModel):
24
24
  return v.isoformat()
25
25
 
26
26
 
27
- class ProjectUpdateV2(BaseModel):
27
+ class ProjectUpdate(BaseModel):
28
28
  model_config = ConfigDict(extra="forbid")
29
29
 
30
30
  name: NonEmptyStr = None
@@ -77,6 +77,8 @@ class ValidResourceBase(BaseModel):
77
77
  jobs_runner_config: dict[NonEmptyStr, Any]
78
78
  jobs_poll_interval: int = 5
79
79
 
80
+ prevent_new_submissions: bool = False
81
+
80
82
  @model_validator(mode="after")
81
83
  def _pixi_slurm_config(self) -> Self:
82
84
  if (
@@ -95,6 +97,9 @@ class ValidResourceLocal(ValidResourceBase):
95
97
  Attributes:
96
98
  name: Resource name.
97
99
  type: Resource type.
100
+ prevent_new_submissions:
101
+ When set to true: Prevent new job submissions and stop execution of
102
+ ongoing jobs as soon as the current task is complete.
98
103
  tasks_python_config:
99
104
  Configuration of Python interpreters used for task collection.
100
105
  tasks_pixi_config:
@@ -105,7 +110,6 @@ class ValidResourceLocal(ValidResourceBase):
105
110
  Local base folder for job folders.
106
111
  jobs_runner_config:
107
112
  Runner configuration.
108
-
109
113
  """
110
114
 
111
115
  type: Literal[ResourceType.LOCAL]
@@ -121,6 +125,9 @@ class ValidResourceSlurmSudo(ValidResourceBase):
121
125
  Attributes:
122
126
  name: Resource name.
123
127
  type: Resource type.
128
+ prevent_new_submissions:
129
+ When set to true: Prevent new job submissions and stop execution of
130
+ ongoing jobs as soon as the current task is complete.
124
131
  tasks_python_config:
125
132
  Configuration of Python interpreters used for task collection.
126
133
  tasks_pixi_config:
@@ -150,6 +157,9 @@ class ValidResourceSlurmSSH(ValidResourceBase):
150
157
  Attributes:
151
158
  name: Resource name
152
159
  type: Resource type.
160
+ prevent_new_submissions:
161
+ When set to true: Prevent new job submissions and stop execution of
162
+ ongoing jobs as soon as the current task is complete.
153
163
  tasks_python_config:
154
164
  Configuration of Python interpreters used for task collection.
155
165
  tasks_pixi_config:
@@ -198,10 +208,9 @@ class ResourceRead(BaseModel):
198
208
  """
199
209
 
200
210
  id: int
201
-
202
- type: str
203
-
204
211
  name: str
212
+ type: str
213
+ prevent_new_submissions: bool
205
214
  timestamp_created: AwareDatetime
206
215
 
207
216
  host: str | None
@@ -0,0 +1,99 @@
1
+ from enum import StrEnum
2
+
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class ProjectPermissions(StrEnum):
7
+ """
8
+ Available permissions for accessing Project
9
+ Attributes:
10
+ READ:
11
+ WRITE:
12
+ EXECUTE:
13
+ """
14
+
15
+ READ = "r"
16
+ WRITE = "rw"
17
+ EXECUTE = "rwx"
18
+
19
+
20
+ class ProjectGuestCreate(BaseModel):
21
+ """
22
+ Request body for project-sharing invitation.
23
+
24
+ Attributes:
25
+ permissions:
26
+ """
27
+
28
+ permissions: ProjectPermissions
29
+
30
+
31
+ class ProjectGuestRead(BaseModel):
32
+ """
33
+ Information about a guest.
34
+
35
+ Attributes:
36
+ email: Guest email.
37
+ is_verified: Project/guest verification status.
38
+ permissions: Guest permissions for project.
39
+ """
40
+
41
+ email: str
42
+ is_verified: bool
43
+ permissions: str
44
+
45
+
46
+ class ProjectGuestUpdate(BaseModel):
47
+ """
48
+ Request body for updating permissions of an existing guest.
49
+
50
+ Attributes:
51
+ permissions: New permissions for guest.
52
+ """
53
+
54
+ permissions: ProjectPermissions
55
+
56
+
57
+ class ProjectAccessRead(BaseModel):
58
+ """
59
+ Project-access information for current user.
60
+
61
+ Attributes:
62
+ is_owner: Whether current user is owner.
63
+ permissions: Current user permissions.
64
+ owner_email: Email of project owner
65
+ """
66
+
67
+ is_owner: bool
68
+ permissions: str
69
+ owner_email: str
70
+
71
+
72
+ class ProjectInvitationRead(BaseModel):
73
+ """
74
+ Info about a pending invitation.
75
+
76
+ Attributes:
77
+ project_id:
78
+ project_name:
79
+ owner_email:
80
+ guest_permissions:
81
+ """
82
+
83
+ project_id: int
84
+ project_name: str
85
+ owner_email: str
86
+ guest_permissions: str
87
+
88
+
89
+ class LinkUserProjectRead(BaseModel):
90
+ # User info
91
+ user_id: int
92
+ user_email: str
93
+ # Project info
94
+ project_id: int
95
+ project_name: str
96
+ # Permissions
97
+ is_verified: bool
98
+ is_owner: bool
99
+ permissions: str