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
@@ -1 +1 @@
1
- __VERSION__ = "2.18.0"
1
+ __VERSION__ = "2.18.0a0"
@@ -167,8 +167,7 @@ def init_db_data(
167
167
  "`--admin-pwd` and `--admin-project-dir`. Exit."
168
168
  )
169
169
  sys.exit(1)
170
-
171
- if admin_email:
170
+ if admin_password and admin_email:
172
171
  asyncio.run(
173
172
  _create_first_user(
174
173
  email=admin_email,
@@ -6,6 +6,7 @@ from pydantic import EmailStr
6
6
  from sqlalchemy import Column
7
7
  from sqlalchemy import String
8
8
  from sqlalchemy.dialects.postgresql import ARRAY
9
+ from sqlalchemy.dialects.postgresql import JSONB
9
10
  from sqlalchemy.types import DateTime
10
11
  from sqlmodel import Field
11
12
  from sqlmodel import Relationship
@@ -112,13 +113,7 @@ class UserOAuth(SQLModel, table=True):
112
113
  ondelete="RESTRICT",
113
114
  )
114
115
 
115
- # TODO-2.18.1: drop `project_dir`
116
- project_dir: str | None = Field(default=None, nullable=True)
117
- # TODO-2.18.1: `project_dirs: list[str] = Field(min_length=1)`
118
- project_dirs: list[str] = Field(
119
- sa_column=Column(ARRAY(String), nullable=False, server_default="{}"),
120
- )
121
-
116
+ project_dir: str
122
117
  slurm_accounts: list[str] = Field(
123
118
  sa_column=Column(ARRAY(String), server_default="{}"),
124
119
  )
@@ -140,3 +135,6 @@ class UserGroup(SQLModel, table=True):
140
135
  default_factory=get_timestamp,
141
136
  sa_column=Column(DateTime(timezone=True), nullable=False),
142
137
  )
138
+ viewer_paths: list[str] = Field(
139
+ sa_column=Column(JSONB, server_default="[]", nullable=False)
140
+ )
@@ -6,11 +6,9 @@ from sqlalchemy import Column
6
6
  from sqlalchemy.dialects.postgresql import JSONB
7
7
  from sqlalchemy.types import DateTime
8
8
  from sqlmodel import Field
9
- from sqlmodel import Index
10
9
  from sqlmodel import SQLModel
11
- from sqlmodel import text
12
10
 
13
- from fractal_server.app.schemas.v2 import JobStatusType
11
+ from fractal_server.app.schemas.v2 import JobStatusTypeV2
14
12
  from fractal_server.utils import get_timestamp
15
13
 
16
14
 
@@ -58,7 +56,7 @@ class JobV2(SQLModel, table=True):
58
56
  end_timestamp: datetime | None = Field(
59
57
  default=None, sa_column=Column(DateTime(timezone=True))
60
58
  )
61
- status: str = JobStatusType.SUBMITTED
59
+ status: str = JobStatusTypeV2.SUBMITTED
62
60
  log: str | None = None
63
61
  executor_error_log: str | None = None
64
62
 
@@ -68,12 +66,3 @@ class JobV2(SQLModel, table=True):
68
66
  type_filters: dict[str, bool] = Field(
69
67
  sa_column=Column(JSONB, nullable=False, server_default="{}")
70
68
  )
71
-
72
- __table_args__ = (
73
- Index(
74
- "ix_jobv2_one_submitted_job_per_dataset",
75
- "dataset_id",
76
- unique=True,
77
- postgresql_where=text(f"status = '{JobStatusType.SUBMITTED}'"),
78
- ),
79
- )
@@ -5,7 +5,6 @@ from typing import Self
5
5
  from sqlalchemy import Column
6
6
  from sqlalchemy.dialects.postgresql import JSONB
7
7
  from sqlalchemy.types import DateTime
8
- from sqlmodel import BOOLEAN
9
8
  from sqlmodel import CheckConstraint
10
9
  from sqlmodel import Field
11
10
  from sqlmodel import SQLModel
@@ -44,18 +43,6 @@ class Resource(SQLModel, table=True):
44
43
  Address for ssh connections, when `type="slurm_ssh"`.
45
44
  """
46
45
 
47
- prevent_new_submissions: bool = Field(
48
- sa_column=Column(
49
- BOOLEAN,
50
- server_default="false",
51
- nullable=False,
52
- ),
53
- )
54
- """
55
- When set to true: Prevent new job submissions and stop execution of
56
- ongoing jobs as soon as the current task is complete.
57
- """
58
-
59
46
  jobs_local_dir: str
60
47
  """
61
48
  Base local folder for job subfolders (containing artifacts and logs).
@@ -14,14 +14,16 @@ from .task import router as task_router
14
14
  from .task_group import router as task_group_router
15
15
  from .task_group_lifecycle import router as task_group_lifecycle_router
16
16
 
17
- router_admin = APIRouter()
17
+ router_admin_v2 = APIRouter()
18
18
 
19
- router_admin.include_router(accounting_router, prefix="/accounting")
20
- router_admin.include_router(job_router, prefix="/job")
21
- router_admin.include_router(task_router, prefix="/task")
22
- router_admin.include_router(task_group_router, prefix="/task-group")
23
- router_admin.include_router(task_group_lifecycle_router, prefix="/task-group")
24
- router_admin.include_router(impersonate_router, prefix="/impersonate")
25
- router_admin.include_router(resource_router, prefix="/resource")
26
- router_admin.include_router(profile_router, prefix="/profile")
27
- router_admin.include_router(sharing_router, prefix="/linkuserproject")
19
+ router_admin_v2.include_router(accounting_router, prefix="/accounting")
20
+ router_admin_v2.include_router(job_router, prefix="/job")
21
+ router_admin_v2.include_router(task_router, prefix="/task")
22
+ router_admin_v2.include_router(task_group_router, prefix="/task-group")
23
+ router_admin_v2.include_router(
24
+ task_group_lifecycle_router, prefix="/task-group"
25
+ )
26
+ router_admin_v2.include_router(impersonate_router, prefix="/impersonate")
27
+ router_admin_v2.include_router(resource_router, prefix="/resource")
28
+ router_admin_v2.include_router(profile_router, prefix="/profile")
29
+ router_admin_v2.include_router(sharing_router, prefix="/linkuserproject")
@@ -67,11 +67,11 @@ async def query_accounting(
67
67
  res = await db.execute(stm)
68
68
  records = res.scalars().all()
69
69
 
70
- return dict(
70
+ return PaginationResponse[AccountingRecordRead](
71
71
  total_count=total_count,
72
72
  page_size=page_size,
73
73
  current_page=page,
74
- items=records,
74
+ items=[record.model_dump() for record in records],
75
75
  )
76
76
 
77
77
 
@@ -24,9 +24,9 @@ from fractal_server.app.routes.pagination import PaginationRequest
24
24
  from fractal_server.app.routes.pagination import PaginationResponse
25
25
  from fractal_server.app.routes.pagination import get_pagination_params
26
26
  from fractal_server.app.schemas.v2 import HistoryUnitStatus
27
- from fractal_server.app.schemas.v2 import JobRead
28
- from fractal_server.app.schemas.v2 import JobStatusType
29
- from fractal_server.app.schemas.v2 import JobUpdate
27
+ from fractal_server.app.schemas.v2 import JobReadV2
28
+ from fractal_server.app.schemas.v2 import JobStatusTypeV2
29
+ from fractal_server.app.schemas.v2 import JobUpdateV2
30
30
  from fractal_server.runner.filenames import WORKFLOW_LOG_FILENAME
31
31
  from fractal_server.utils import get_timestamp
32
32
  from fractal_server.zip_tools import _zip_folder_to_byte_stream_iterator
@@ -34,14 +34,14 @@ from fractal_server.zip_tools import _zip_folder_to_byte_stream_iterator
34
34
  router = APIRouter()
35
35
 
36
36
 
37
- @router.get("/", response_model=PaginationResponse[JobRead])
37
+ @router.get("/", response_model=PaginationResponse[JobReadV2])
38
38
  async def view_job(
39
39
  id: int | None = None,
40
40
  user_id: int | None = None,
41
41
  project_id: int | None = None,
42
42
  dataset_id: int | None = None,
43
43
  workflow_id: int | None = None,
44
- status: JobStatusType | None = None,
44
+ status: JobStatusTypeV2 | None = None,
45
45
  start_timestamp_min: AwareDatetime | None = None,
46
46
  start_timestamp_max: AwareDatetime | None = None,
47
47
  end_timestamp_min: AwareDatetime | None = None,
@@ -50,7 +50,7 @@ async def view_job(
50
50
  pagination: PaginationRequest = Depends(get_pagination_params),
51
51
  user: UserOAuth = Depends(current_superuser_act),
52
52
  db: AsyncSession = Depends(get_async_db),
53
- ) -> PaginationResponse[JobRead]:
53
+ ) -> PaginationResponse[JobReadV2]:
54
54
  """
55
55
  Query `JobV2` table.
56
56
 
@@ -146,21 +146,21 @@ async def view_job(
146
146
  for job in job_list:
147
147
  setattr(job, "log", None)
148
148
 
149
- return dict(
149
+ return PaginationResponse[JobReadV2](
150
150
  total_count=total_count,
151
151
  page_size=page_size,
152
152
  current_page=page,
153
- items=job_list,
153
+ items=[job.model_dump() for job in job_list],
154
154
  )
155
155
 
156
156
 
157
- @router.get("/{job_id}/", response_model=JobRead)
157
+ @router.get("/{job_id}/", response_model=JobReadV2)
158
158
  async def view_single_job(
159
159
  job_id: int,
160
160
  show_tmp_logs: bool = False,
161
161
  user: UserOAuth = Depends(current_superuser_act),
162
162
  db: AsyncSession = Depends(get_async_db),
163
- ) -> JobRead:
163
+ ) -> JobReadV2:
164
164
  job = await db.get(JobV2, job_id)
165
165
  if not job:
166
166
  raise HTTPException(
@@ -169,7 +169,7 @@ async def view_single_job(
169
169
  )
170
170
  await db.close()
171
171
 
172
- if show_tmp_logs and (job.status == JobStatusType.SUBMITTED):
172
+ if show_tmp_logs and (job.status == JobStatusTypeV2.SUBMITTED):
173
173
  try:
174
174
  with open(f"{job.working_dir}/{WORKFLOW_LOG_FILENAME}") as f:
175
175
  job.log = f.read()
@@ -179,13 +179,13 @@ async def view_single_job(
179
179
  return job
180
180
 
181
181
 
182
- @router.patch("/{job_id}/", response_model=JobRead)
182
+ @router.patch("/{job_id}/", response_model=JobReadV2)
183
183
  async def update_job(
184
- job_update: JobUpdate,
184
+ job_update: JobUpdateV2,
185
185
  job_id: int,
186
186
  user: UserOAuth = Depends(current_superuser_act),
187
187
  db: AsyncSession = Depends(get_async_db),
188
- ) -> JobRead | None:
188
+ ) -> JobReadV2 | None:
189
189
  """
190
190
  Change the status of an existing job.
191
191
 
@@ -198,13 +198,13 @@ async def update_job(
198
198
  status_code=status.HTTP_404_NOT_FOUND,
199
199
  detail=f"Job {job_id} not found",
200
200
  )
201
- if job.status != JobStatusType.SUBMITTED:
201
+ if job.status != JobStatusTypeV2.SUBMITTED:
202
202
  raise HTTPException(
203
203
  status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
204
204
  detail=f"Job {job_id} has status {job.status=} != 'submitted'.",
205
205
  )
206
206
 
207
- if job_update.status != JobStatusType.FAILED:
207
+ if job_update.status != JobStatusTypeV2.FAILED:
208
208
  raise HTTPException(
209
209
  status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
210
210
  detail=f"Cannot set job status to {job_update.status}",
@@ -217,7 +217,7 @@ async def update_job(
217
217
  job,
218
218
  "log",
219
219
  f"{job.log or ''}\nThis job was manually marked as "
220
- f"'{JobStatusType.FAILED}' by an admin ({timestamp.isoformat()}).",
220
+ f"'{JobStatusTypeV2.FAILED}' by an admin ({timestamp.isoformat()}).",
221
221
  )
222
222
 
223
223
  res = await db.execute(
@@ -23,7 +23,7 @@ from fractal_server.app.schemas.v2.task import TaskType
23
23
  router = APIRouter()
24
24
 
25
25
 
26
- class TaskMinimal(BaseModel):
26
+ class TaskV2Minimal(BaseModel):
27
27
  id: int
28
28
  name: str
29
29
  type: str
@@ -39,7 +39,7 @@ class ProjectUser(BaseModel):
39
39
  email: EmailStr
40
40
 
41
41
 
42
- class TaskRelationship(BaseModel):
42
+ class TaskV2Relationship(BaseModel):
43
43
  workflow_id: int
44
44
  workflow_name: str
45
45
  project_id: int
@@ -47,12 +47,12 @@ class TaskRelationship(BaseModel):
47
47
  project_users: list[ProjectUser] = Field(default_factory=list)
48
48
 
49
49
 
50
- class TaskInfo(BaseModel):
51
- task: TaskMinimal
52
- relationships: list[TaskRelationship]
50
+ class TaskV2Info(BaseModel):
51
+ task: TaskV2Minimal
52
+ relationships: list[TaskV2Relationship]
53
53
 
54
54
 
55
- @router.get("/", response_model=PaginationResponse[TaskInfo])
55
+ @router.get("/", response_model=PaginationResponse[TaskV2Info])
56
56
  async def query_tasks(
57
57
  id: int | None = None,
58
58
  source: str | None = None,
@@ -66,7 +66,7 @@ async def query_tasks(
66
66
  pagination: PaginationRequest = Depends(get_pagination_params),
67
67
  user: UserOAuth = Depends(current_superuser_act),
68
68
  db: AsyncSession = Depends(get_async_db),
69
- ) -> PaginationResponse[TaskInfo]:
69
+ ) -> PaginationResponse[TaskV2Info]:
70
70
  """
71
71
  Query `TaskV2` and get information about related workflows and projects.
72
72
  """
@@ -170,7 +170,7 @@ async def query_tasks(
170
170
  ],
171
171
  )
172
172
  )
173
- return dict(
173
+ return PaginationResponse[TaskV2Info](
174
174
  total_count=total_count,
175
175
  page_size=page_size,
176
176
  current_page=page,
@@ -5,7 +5,6 @@ from fastapi import status
5
5
  from pydantic.types import AwareDatetime
6
6
  from sqlalchemy.sql.operators import is_
7
7
  from sqlalchemy.sql.operators import is_not
8
- from sqlmodel import func
9
8
  from sqlmodel import select
10
9
 
11
10
  from fractal_server.app.db import AsyncSession
@@ -17,15 +16,12 @@ from fractal_server.app.routes.auth import current_superuser_act
17
16
  from fractal_server.app.routes.auth._aux_auth import (
18
17
  _verify_user_belongs_to_group,
19
18
  )
20
- from fractal_server.app.routes.pagination import PaginationRequest
21
- from fractal_server.app.routes.pagination import PaginationResponse
22
- from fractal_server.app.routes.pagination import get_pagination_params
23
- from fractal_server.app.schemas.v2 import TaskGroupActivityAction
24
- from fractal_server.app.schemas.v2 import TaskGroupActivityRead
25
- from fractal_server.app.schemas.v2 import TaskGroupActivityStatus
26
- from fractal_server.app.schemas.v2 import TaskGroupOriginEnum
19
+ from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
20
+ from fractal_server.app.schemas.v2 import TaskGroupActivityStatusV2
21
+ from fractal_server.app.schemas.v2 import TaskGroupActivityV2Read
27
22
  from fractal_server.app.schemas.v2 import TaskGroupReadSuperuser
28
- from fractal_server.app.schemas.v2 import TaskGroupUpdate
23
+ from fractal_server.app.schemas.v2 import TaskGroupUpdateV2
24
+ from fractal_server.app.schemas.v2 import TaskGroupV2OriginEnum
29
25
  from fractal_server.logger import set_logger
30
26
 
31
27
  router = APIRouter()
@@ -33,78 +29,39 @@ router = APIRouter()
33
29
  logger = set_logger(__name__)
34
30
 
35
31
 
36
- @router.get(
37
- "/activity/", response_model=PaginationResponse[TaskGroupActivityRead]
38
- )
32
+ @router.get("/activity/", response_model=list[TaskGroupActivityV2Read])
39
33
  async def get_task_group_activity_list(
40
34
  task_group_activity_id: int | None = None,
41
35
  user_id: int | None = None,
42
36
  taskgroupv2_id: int | None = None,
43
37
  pkg_name: str | None = None,
44
- status: TaskGroupActivityStatus | None = None,
45
- action: TaskGroupActivityAction | None = None,
38
+ status: TaskGroupActivityStatusV2 | None = None,
39
+ action: TaskGroupActivityActionV2 | None = None,
46
40
  timestamp_started_min: AwareDatetime | None = None,
47
- pagination: PaginationRequest = Depends(get_pagination_params),
48
41
  superuser: UserOAuth = Depends(current_superuser_act),
49
42
  db: AsyncSession = Depends(get_async_db),
50
- ) -> PaginationResponse[TaskGroupActivityRead]:
51
- # Assign pagination parameters
52
- page = pagination.page
53
- page_size = pagination.page_size
54
-
55
- stm = select(TaskGroupActivityV2).order_by(
56
- TaskGroupActivityV2.timestamp_started.desc()
57
- )
58
- stm_count = select(func.count(TaskGroupActivityV2.id))
43
+ ) -> list[TaskGroupActivityV2Read]:
44
+ stm = select(TaskGroupActivityV2)
59
45
  if task_group_activity_id is not None:
60
46
  stm = stm.where(TaskGroupActivityV2.id == task_group_activity_id)
61
- stm_count = stm_count.where(
62
- TaskGroupActivityV2.id == task_group_activity_id
63
- )
64
47
  if user_id:
65
48
  stm = stm.where(TaskGroupActivityV2.user_id == user_id)
66
- stm_count = stm_count.where(TaskGroupActivityV2.user_id == user_id)
67
49
  if taskgroupv2_id:
68
50
  stm = stm.where(TaskGroupActivityV2.taskgroupv2_id == taskgroupv2_id)
69
- stm_count = stm_count.where(
70
- TaskGroupActivityV2.taskgroupv2_id == taskgroupv2_id
71
- )
72
51
  if pkg_name:
73
52
  stm = stm.where(TaskGroupActivityV2.pkg_name.icontains(pkg_name))
74
- stm_count = stm_count.where(
75
- TaskGroupActivityV2.pkg_name.icontains(pkg_name)
76
- )
77
53
  if status:
78
54
  stm = stm.where(TaskGroupActivityV2.status == status)
79
- stm_count = stm_count.where(TaskGroupActivityV2.status == status)
80
55
  if action:
81
56
  stm = stm.where(TaskGroupActivityV2.action == action)
82
- stm_count = stm_count.where(TaskGroupActivityV2.action == action)
83
57
  if timestamp_started_min is not None:
84
58
  stm = stm.where(
85
59
  TaskGroupActivityV2.timestamp_started >= timestamp_started_min
86
60
  )
87
- stm_count = stm_count.where(
88
- TaskGroupActivityV2.timestamp_started >= timestamp_started_min
89
- )
90
-
91
- # Find total number of elements
92
- res_total_count = await db.execute(stm_count)
93
- total_count = res_total_count.scalar()
94
- if page_size is None:
95
- page_size = total_count
96
- else:
97
- stm = stm.offset((page - 1) * page_size).limit(page_size)
98
61
 
99
62
  res = await db.execute(stm)
100
63
  activities = res.scalars().all()
101
-
102
- return dict(
103
- total_count=total_count,
104
- page_size=page_size,
105
- current_page=page,
106
- items=activities,
107
- )
64
+ return activities
108
65
 
109
66
 
110
67
  @router.get("/{task_group_id}/", response_model=TaskGroupReadSuperuser)
@@ -122,27 +79,21 @@ async def query_task_group(
122
79
  return task_group
123
80
 
124
81
 
125
- @router.get("/", response_model=PaginationResponse[TaskGroupReadSuperuser])
82
+ @router.get("/", response_model=list[TaskGroupReadSuperuser])
126
83
  async def query_task_group_list(
127
84
  user_id: int | None = None,
128
85
  user_group_id: int | None = None,
129
86
  private: bool | None = None,
130
87
  active: bool | None = None,
131
88
  pkg_name: str | None = None,
132
- origin: TaskGroupOriginEnum | None = None,
89
+ origin: TaskGroupV2OriginEnum | None = None,
133
90
  timestamp_last_used_min: AwareDatetime | None = None,
134
91
  timestamp_last_used_max: AwareDatetime | None = None,
135
92
  resource_id: int | None = None,
136
- pagination: PaginationRequest = Depends(get_pagination_params),
137
93
  user: UserOAuth = Depends(current_superuser_act),
138
94
  db: AsyncSession = Depends(get_async_db),
139
- ) -> PaginationResponse[TaskGroupReadSuperuser]:
140
- # Assign pagination parameters
141
- page = pagination.page
142
- page_size = pagination.page_size
143
-
95
+ ) -> list[TaskGroupReadSuperuser]:
144
96
  stm = select(TaskGroupV2)
145
- stm_count = select(func.count(TaskGroupV2.id))
146
97
 
147
98
  if user_group_id is not None and private is True:
148
99
  raise HTTPException(
@@ -154,72 +105,43 @@ async def query_task_group_list(
154
105
  )
155
106
  if user_id is not None:
156
107
  stm = stm.where(TaskGroupV2.user_id == user_id)
157
- stm_count = stm_count.where(TaskGroupV2.user_id == user_id)
158
108
  if user_group_id is not None:
159
109
  stm = stm.where(TaskGroupV2.user_group_id == user_group_id)
160
- stm_count = stm_count.where(TaskGroupV2.user_group_id == user_group_id)
161
110
  if private is not None:
162
111
  if private is True:
163
112
  stm = stm.where(is_(TaskGroupV2.user_group_id, None))
164
- stm_count = stm_count.where(is_(TaskGroupV2.user_group_id, None))
165
113
  else:
166
114
  stm = stm.where(is_not(TaskGroupV2.user_group_id, None))
167
- stm_count = stm_count.where(is_not(TaskGroupV2.user_group_id, None))
168
115
  if active is not None:
169
116
  if active is True:
170
117
  stm = stm.where(is_(TaskGroupV2.active, True))
171
- stm_count = stm_count.where(is_(TaskGroupV2.active, True))
172
118
  else:
173
119
  stm = stm.where(is_(TaskGroupV2.active, False))
174
- stm_count = stm_count.where(is_(TaskGroupV2.active, False))
175
120
  if origin is not None:
176
121
  stm = stm.where(TaskGroupV2.origin == origin)
177
- stm_count = stm_count.where(TaskGroupV2.origin == origin)
178
122
  if pkg_name is not None:
179
123
  stm = stm.where(TaskGroupV2.pkg_name.icontains(pkg_name))
180
- stm_count = stm_count.where(TaskGroupV2.pkg_name.icontains(pkg_name))
181
124
  if timestamp_last_used_min is not None:
182
125
  stm = stm.where(
183
126
  TaskGroupV2.timestamp_last_used >= timestamp_last_used_min
184
127
  )
185
- stm_count = stm_count.where(
186
- TaskGroupV2.timestamp_last_used >= timestamp_last_used_min
187
- )
188
128
  if timestamp_last_used_max is not None:
189
129
  stm = stm.where(
190
130
  TaskGroupV2.timestamp_last_used <= timestamp_last_used_max
191
131
  )
192
- stm_count = stm_count.where(
193
- TaskGroupV2.timestamp_last_used <= timestamp_last_used_max
194
- )
195
132
  if resource_id is not None:
196
133
  stm = stm.where(TaskGroupV2.resource_id == resource_id)
197
- stm_count = stm_count.where(TaskGroupV2.resource_id == resource_id)
198
-
199
- # Find total number of elements
200
- res_total_count = await db.execute(stm_count)
201
- total_count = res_total_count.scalar()
202
- if page_size is None:
203
- page_size = total_count
204
- else:
205
- stm = stm.offset((page - 1) * page_size).limit(page_size)
206
134
 
207
135
  stm = stm.order_by(TaskGroupV2.id)
208
136
  res = await db.execute(stm)
209
137
  task_groups_list = res.scalars().all()
210
-
211
- return dict(
212
- total_count=total_count,
213
- page_size=page_size,
214
- current_page=page,
215
- items=task_groups_list,
216
- )
138
+ return task_groups_list
217
139
 
218
140
 
219
141
  @router.patch("/{task_group_id}/", response_model=TaskGroupReadSuperuser)
220
142
  async def patch_task_group(
221
143
  task_group_id: int,
222
- task_group_update: TaskGroupUpdate,
144
+ task_group_update: TaskGroupUpdateV2,
223
145
  user: UserOAuth = Depends(current_superuser_act),
224
146
  db: AsyncSession = Depends(get_async_db),
225
147
  ) -> list[TaskGroupReadSuperuser]:
@@ -26,10 +26,10 @@ from fractal_server.app.routes.aux.validate_user_profile import (
26
26
  validate_user_profile,
27
27
  )
28
28
  from fractal_server.app.schemas.v2 import ResourceType
29
- from fractal_server.app.schemas.v2 import TaskGroupActivityAction
30
- from fractal_server.app.schemas.v2 import TaskGroupActivityRead
31
- from fractal_server.app.schemas.v2 import TaskGroupActivityStatus
32
- from fractal_server.app.schemas.v2 import TaskGroupOriginEnum
29
+ from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
30
+ from fractal_server.app.schemas.v2 import TaskGroupActivityStatusV2
31
+ from fractal_server.app.schemas.v2 import TaskGroupActivityV2Read
32
+ from fractal_server.app.schemas.v2 import TaskGroupV2OriginEnum
33
33
  from fractal_server.logger import set_logger
34
34
  from fractal_server.tasks.v2.local import deactivate_local
35
35
  from fractal_server.tasks.v2.local import delete_local
@@ -46,7 +46,7 @@ logger = set_logger(__name__)
46
46
 
47
47
  @router.post(
48
48
  "/{task_group_id}/deactivate/",
49
- response_model=TaskGroupActivityRead,
49
+ response_model=TaskGroupActivityV2Read,
50
50
  )
51
51
  async def deactivate_task_group(
52
52
  task_group_id: int,
@@ -54,7 +54,7 @@ async def deactivate_task_group(
54
54
  response: Response,
55
55
  superuser: UserOAuth = Depends(current_superuser_act),
56
56
  db: AsyncSession = Depends(get_async_db),
57
- ) -> TaskGroupActivityRead:
57
+ ) -> TaskGroupActivityV2Read:
58
58
  """
59
59
  Deactivate task-group venv
60
60
  """
@@ -78,13 +78,13 @@ async def deactivate_task_group(
78
78
  await check_no_submitted_job(task_group_id=task_group.id, db=db)
79
79
 
80
80
  # Shortcut for task-group with origin="other"
81
- if task_group.origin == TaskGroupOriginEnum.OTHER:
81
+ if task_group.origin == TaskGroupV2OriginEnum.OTHER:
82
82
  task_group.active = False
83
83
  task_group_activity = TaskGroupActivityV2(
84
84
  user_id=task_group.user_id,
85
85
  taskgroupv2_id=task_group.id,
86
- status=TaskGroupActivityStatus.OK,
87
- action=TaskGroupActivityAction.DEACTIVATE,
86
+ status=TaskGroupActivityStatusV2.OK,
87
+ action=TaskGroupActivityActionV2.DEACTIVATE,
88
88
  pkg_name=task_group.pkg_name,
89
89
  version=(task_group.version or "N/A"),
90
90
  log=(
@@ -103,8 +103,8 @@ async def deactivate_task_group(
103
103
  task_group_activity = TaskGroupActivityV2(
104
104
  user_id=task_group.user_id,
105
105
  taskgroupv2_id=task_group.id,
106
- status=TaskGroupActivityStatus.PENDING,
107
- action=TaskGroupActivityAction.DEACTIVATE,
106
+ status=TaskGroupActivityStatusV2.PENDING,
107
+ action=TaskGroupActivityActionV2.DEACTIVATE,
108
108
  pkg_name=task_group.pkg_name,
109
109
  version=task_group.version,
110
110
  timestamp_started=get_timestamp(),
@@ -140,7 +140,7 @@ async def deactivate_task_group(
140
140
 
141
141
  @router.post(
142
142
  "/{task_group_id}/reactivate/",
143
- response_model=TaskGroupActivityRead,
143
+ response_model=TaskGroupActivityV2Read,
144
144
  )
145
145
  async def reactivate_task_group(
146
146
  task_group_id: int,
@@ -148,7 +148,7 @@ async def reactivate_task_group(
148
148
  response: Response,
149
149
  superuser: UserOAuth = Depends(current_superuser_act),
150
150
  db: AsyncSession = Depends(get_async_db),
151
- ) -> TaskGroupActivityRead:
151
+ ) -> TaskGroupActivityV2Read:
152
152
  """
153
153
  Deactivate task-group venv
154
154
  """
@@ -173,13 +173,13 @@ async def reactivate_task_group(
173
173
  await check_no_submitted_job(task_group_id=task_group.id, db=db)
174
174
 
175
175
  # Shortcut for task-group with origin="other"
176
- if task_group.origin == TaskGroupOriginEnum.OTHER:
176
+ if task_group.origin == TaskGroupV2OriginEnum.OTHER:
177
177
  task_group.active = True
178
178
  task_group_activity = TaskGroupActivityV2(
179
179
  user_id=task_group.user_id,
180
180
  taskgroupv2_id=task_group.id,
181
- status=TaskGroupActivityStatus.OK,
182
- action=TaskGroupActivityAction.REACTIVATE,
181
+ status=TaskGroupActivityStatusV2.OK,
182
+ action=TaskGroupActivityActionV2.REACTIVATE,
183
183
  pkg_name=task_group.pkg_name,
184
184
  version=(task_group.version or "N/A"),
185
185
  log=(
@@ -206,8 +206,8 @@ async def reactivate_task_group(
206
206
  task_group_activity = TaskGroupActivityV2(
207
207
  user_id=task_group.user_id,
208
208
  taskgroupv2_id=task_group.id,
209
- status=TaskGroupActivityStatus.PENDING,
210
- action=TaskGroupActivityAction.REACTIVATE,
209
+ status=TaskGroupActivityStatusV2.PENDING,
210
+ action=TaskGroupActivityActionV2.REACTIVATE,
211
211
  pkg_name=task_group.pkg_name,
212
212
  version=task_group.version,
213
213
  timestamp_started=get_timestamp(),
@@ -259,8 +259,8 @@ async def delete_task_group(
259
259
  task_group_activity = TaskGroupActivityV2(
260
260
  user_id=task_group.user_id,
261
261
  taskgroupv2_id=task_group.id,
262
- status=TaskGroupActivityStatus.PENDING,
263
- action=TaskGroupActivityAction.DELETE,
262
+ status=TaskGroupActivityStatusV2.PENDING,
263
+ action=TaskGroupActivityActionV2.DELETE,
264
264
  pkg_name=task_group.pkg_name,
265
265
  version=(task_group.version or "N/A"),
266
266
  timestamp_started=get_timestamp(),
@@ -8,6 +8,7 @@ from fastapi import Depends
8
8
  import fractal_server
9
9
  from fractal_server.app.models import UserOAuth
10
10
  from fractal_server.app.routes.auth import current_superuser_act
11
+ from fractal_server.config import get_data_settings
11
12
  from fractal_server.config import get_db_settings
12
13
  from fractal_server.config import get_email_settings
13
14
  from fractal_server.config import get_oauth_settings
@@ -49,6 +50,14 @@ async def view_email_settings(
49
50
  return settings.model_dump()
50
51
 
51
52
 
53
+ @router_api.get("/settings/data/")
54
+ async def view_data_settings(
55
+ user: UserOAuth = Depends(current_superuser_act),
56
+ ):
57
+ settings = Inject(get_data_settings)
58
+ return settings.model_dump()
59
+
60
+
52
61
  @router_api.get("/settings/oauth/")
53
62
  async def view_oauth_settings(
54
63
  user: UserOAuth = Depends(current_superuser_act),