fractal-server 2.11.1__py3-none-any.whl → 2.12.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 (64) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/app/models/__init__.py +0 -2
  3. fractal_server/app/models/linkuserproject.py +0 -9
  4. fractal_server/app/routes/aux/_job.py +1 -3
  5. fractal_server/app/runner/executors/slurm/ssh/executor.py +9 -6
  6. fractal_server/app/runner/executors/slurm/sudo/executor.py +1 -5
  7. fractal_server/app/runner/filenames.py +0 -2
  8. fractal_server/app/runner/shutdown.py +3 -27
  9. fractal_server/app/schemas/_validators.py +0 -19
  10. fractal_server/config.py +1 -15
  11. fractal_server/main.py +1 -12
  12. fractal_server/migrations/versions/1eac13a26c83_drop_v1_tables.py +67 -0
  13. fractal_server/string_tools.py +0 -21
  14. fractal_server/tasks/utils.py +0 -28
  15. {fractal_server-2.11.1.dist-info → fractal_server-2.12.0a1.dist-info}/METADATA +1 -1
  16. {fractal_server-2.11.1.dist-info → fractal_server-2.12.0a1.dist-info}/RECORD +19 -63
  17. fractal_server/app/models/v1/__init__.py +0 -13
  18. fractal_server/app/models/v1/dataset.py +0 -71
  19. fractal_server/app/models/v1/job.py +0 -101
  20. fractal_server/app/models/v1/project.py +0 -29
  21. fractal_server/app/models/v1/state.py +0 -34
  22. fractal_server/app/models/v1/task.py +0 -85
  23. fractal_server/app/models/v1/workflow.py +0 -133
  24. fractal_server/app/routes/admin/v1.py +0 -377
  25. fractal_server/app/routes/api/v1/__init__.py +0 -26
  26. fractal_server/app/routes/api/v1/_aux_functions.py +0 -478
  27. fractal_server/app/routes/api/v1/dataset.py +0 -554
  28. fractal_server/app/routes/api/v1/job.py +0 -195
  29. fractal_server/app/routes/api/v1/project.py +0 -475
  30. fractal_server/app/routes/api/v1/task.py +0 -203
  31. fractal_server/app/routes/api/v1/task_collection.py +0 -239
  32. fractal_server/app/routes/api/v1/workflow.py +0 -355
  33. fractal_server/app/routes/api/v1/workflowtask.py +0 -187
  34. fractal_server/app/runner/async_wrap_v1.py +0 -27
  35. fractal_server/app/runner/v1/__init__.py +0 -415
  36. fractal_server/app/runner/v1/_common.py +0 -620
  37. fractal_server/app/runner/v1/_local/__init__.py +0 -186
  38. fractal_server/app/runner/v1/_local/_local_config.py +0 -105
  39. fractal_server/app/runner/v1/_local/_submit_setup.py +0 -48
  40. fractal_server/app/runner/v1/_local/executor.py +0 -100
  41. fractal_server/app/runner/v1/_slurm/__init__.py +0 -312
  42. fractal_server/app/runner/v1/_slurm/_submit_setup.py +0 -81
  43. fractal_server/app/runner/v1/_slurm/get_slurm_config.py +0 -163
  44. fractal_server/app/runner/v1/common.py +0 -117
  45. fractal_server/app/runner/v1/handle_failed_job.py +0 -141
  46. fractal_server/app/schemas/v1/__init__.py +0 -37
  47. fractal_server/app/schemas/v1/applyworkflow.py +0 -161
  48. fractal_server/app/schemas/v1/dataset.py +0 -165
  49. fractal_server/app/schemas/v1/dumps.py +0 -64
  50. fractal_server/app/schemas/v1/manifest.py +0 -126
  51. fractal_server/app/schemas/v1/project.py +0 -66
  52. fractal_server/app/schemas/v1/state.py +0 -18
  53. fractal_server/app/schemas/v1/task.py +0 -167
  54. fractal_server/app/schemas/v1/task_collection.py +0 -110
  55. fractal_server/app/schemas/v1/workflow.py +0 -212
  56. fractal_server/tasks/v1/_TaskCollectPip.py +0 -103
  57. fractal_server/tasks/v1/__init__.py +0 -0
  58. fractal_server/tasks/v1/background_operations.py +0 -352
  59. fractal_server/tasks/v1/endpoint_operations.py +0 -156
  60. fractal_server/tasks/v1/get_collection_data.py +0 -14
  61. fractal_server/tasks/v1/utils.py +0 -67
  62. {fractal_server-2.11.1.dist-info → fractal_server-2.12.0a1.dist-info}/LICENSE +0 -0
  63. {fractal_server-2.11.1.dist-info → fractal_server-2.12.0a1.dist-info}/WHEEL +0 -0
  64. {fractal_server-2.11.1.dist-info → fractal_server-2.12.0a1.dist-info}/entry_points.txt +0 -0
@@ -1,377 +0,0 @@
1
- """
2
- Definition of `/admin` routes.
3
- """
4
- from datetime import datetime
5
- from pathlib import Path
6
- from typing import Optional
7
-
8
- from fastapi import APIRouter
9
- from fastapi import Depends
10
- from fastapi import HTTPException
11
- from fastapi import Response
12
- from fastapi import status
13
- from fastapi.responses import StreamingResponse
14
- from sqlalchemy import func
15
- from sqlmodel import select
16
-
17
- from ....utils import get_timestamp
18
- from ....zip_tools import _zip_folder_to_byte_stream_iterator
19
- from ...db import AsyncSession
20
- from ...db import get_async_db
21
- from ...models.v1 import ApplyWorkflow
22
- from ...models.v1 import Dataset
23
- from ...models.v1 import JobStatusTypeV1
24
- from ...models.v1 import Project
25
- from ...models.v1 import Workflow
26
- from ...runner.filenames import WORKFLOW_LOG_FILENAME
27
- from ...schemas.v1 import ApplyWorkflowReadV1
28
- from ...schemas.v1 import ApplyWorkflowUpdateV1
29
- from ...schemas.v1 import DatasetReadV1
30
- from ...schemas.v1 import ProjectReadV1
31
- from ...schemas.v1 import WorkflowReadV1
32
- from ..aux._job import _write_shutdown_file
33
- from ..aux._runner import _check_shutdown_is_supported
34
- from fractal_server.app.models import UserOAuth
35
- from fractal_server.app.routes.auth import current_active_superuser
36
- from fractal_server.app.routes.aux import _raise_if_naive_datetime
37
-
38
- router_admin_v1 = APIRouter()
39
-
40
-
41
- @router_admin_v1.get("/project/", response_model=list[ProjectReadV1])
42
- async def view_project(
43
- id: Optional[int] = None,
44
- user_id: Optional[int] = None,
45
- timestamp_created_min: Optional[datetime] = None,
46
- timestamp_created_max: Optional[datetime] = None,
47
- user: UserOAuth = Depends(current_active_superuser),
48
- db: AsyncSession = Depends(get_async_db),
49
- ) -> list[ProjectReadV1]:
50
- """
51
- Query `project` table.
52
-
53
- Args:
54
- id: If not `None`, select a given `project.id`.
55
- user_id: If not `None`, select a given `project.user_id`.
56
- """
57
- _raise_if_naive_datetime(timestamp_created_min, timestamp_created_max)
58
-
59
- stm = select(Project)
60
-
61
- if id is not None:
62
- stm = stm.where(Project.id == id)
63
-
64
- if user_id is not None:
65
- stm = stm.where(Project.user_list.any(UserOAuth.id == user_id))
66
- if timestamp_created_min is not None:
67
- stm = stm.where(Project.timestamp_created >= timestamp_created_min)
68
- if timestamp_created_max is not None:
69
- stm = stm.where(Project.timestamp_created <= timestamp_created_max)
70
-
71
- res = await db.execute(stm)
72
- project_list = res.scalars().all()
73
- await db.close()
74
-
75
- return project_list
76
-
77
-
78
- @router_admin_v1.get("/workflow/", response_model=list[WorkflowReadV1])
79
- async def view_workflow(
80
- id: Optional[int] = None,
81
- user_id: Optional[int] = None,
82
- project_id: Optional[int] = None,
83
- name_contains: Optional[str] = None,
84
- timestamp_created_min: Optional[datetime] = None,
85
- timestamp_created_max: Optional[datetime] = None,
86
- user: UserOAuth = Depends(current_active_superuser),
87
- db: AsyncSession = Depends(get_async_db),
88
- ) -> list[WorkflowReadV1]:
89
- """
90
- Query `workflow` table.
91
-
92
- Args:
93
- id: If not `None`, select a given `workflow.id`.
94
- project_id: If not `None`, select a given `workflow.project_id`.
95
- name_contains: If not `None`, select workflows such that their
96
- `name` attribute contains `name_contains` (case-insensitive).
97
- """
98
- _raise_if_naive_datetime(timestamp_created_min, timestamp_created_max)
99
-
100
- stm = select(Workflow)
101
-
102
- if user_id is not None:
103
- stm = stm.join(Project).where(
104
- Project.user_list.any(UserOAuth.id == user_id)
105
- )
106
- if id is not None:
107
- stm = stm.where(Workflow.id == id)
108
- if project_id is not None:
109
- stm = stm.where(Workflow.project_id == project_id)
110
- if name_contains is not None:
111
- # SQLAlchemy2: use icontains
112
- stm = stm.where(
113
- func.lower(Workflow.name).contains(name_contains.lower())
114
- )
115
- if timestamp_created_min is not None:
116
- stm = stm.where(Workflow.timestamp_created >= timestamp_created_min)
117
- if timestamp_created_max is not None:
118
- stm = stm.where(Workflow.timestamp_created <= timestamp_created_max)
119
-
120
- res = await db.execute(stm)
121
- workflow_list = res.scalars().all()
122
- await db.close()
123
-
124
- return workflow_list
125
-
126
-
127
- @router_admin_v1.get("/dataset/", response_model=list[DatasetReadV1])
128
- async def view_dataset(
129
- id: Optional[int] = None,
130
- user_id: Optional[int] = None,
131
- project_id: Optional[int] = None,
132
- name_contains: Optional[str] = None,
133
- type: Optional[str] = None,
134
- timestamp_created_min: Optional[datetime] = None,
135
- timestamp_created_max: Optional[datetime] = None,
136
- user: UserOAuth = Depends(current_active_superuser),
137
- db: AsyncSession = Depends(get_async_db),
138
- ) -> list[DatasetReadV1]:
139
- """
140
- Query `dataset` table.
141
-
142
- Args:
143
- id: If not `None`, select a given `dataset.id`.
144
- project_id: If not `None`, select a given `dataset.project_id`.
145
- name_contains: If not `None`, select datasets such that their
146
- `name` attribute contains `name_contains` (case-insensitive).
147
- type: If not `None`, select a given `dataset.type`.
148
- """
149
- _raise_if_naive_datetime(timestamp_created_min, timestamp_created_max)
150
-
151
- stm = select(Dataset)
152
-
153
- if user_id is not None:
154
- stm = stm.join(Project).where(
155
- Project.user_list.any(UserOAuth.id == user_id)
156
- )
157
- if id is not None:
158
- stm = stm.where(Dataset.id == id)
159
- if project_id is not None:
160
- stm = stm.where(Dataset.project_id == project_id)
161
- if name_contains is not None:
162
- # SQLAlchemy2: use icontains
163
- stm = stm.where(
164
- func.lower(Dataset.name).contains(name_contains.lower())
165
- )
166
- if type is not None:
167
- stm = stm.where(Dataset.type == type)
168
- if timestamp_created_min is not None:
169
- stm = stm.where(Dataset.timestamp_created >= timestamp_created_min)
170
- if timestamp_created_max is not None:
171
- stm = stm.where(Dataset.timestamp_created <= timestamp_created_max)
172
-
173
- res = await db.execute(stm)
174
- dataset_list = res.scalars().all()
175
- await db.close()
176
-
177
- return dataset_list
178
-
179
-
180
- @router_admin_v1.get("/job/", response_model=list[ApplyWorkflowReadV1])
181
- async def view_job(
182
- id: Optional[int] = None,
183
- user_id: Optional[int] = None,
184
- project_id: Optional[int] = None,
185
- input_dataset_id: Optional[int] = None,
186
- output_dataset_id: Optional[int] = None,
187
- workflow_id: Optional[int] = None,
188
- status: Optional[JobStatusTypeV1] = None,
189
- start_timestamp_min: Optional[datetime] = None,
190
- start_timestamp_max: Optional[datetime] = None,
191
- end_timestamp_min: Optional[datetime] = None,
192
- end_timestamp_max: Optional[datetime] = None,
193
- log: bool = True,
194
- user: UserOAuth = Depends(current_active_superuser),
195
- db: AsyncSession = Depends(get_async_db),
196
- ) -> list[ApplyWorkflowReadV1]:
197
- """
198
- Query `ApplyWorkflow` table.
199
-
200
- Args:
201
- id: If not `None`, select a given `applyworkflow.id`.
202
- project_id: If not `None`, select a given `applyworkflow.project_id`.
203
- input_dataset_id: If not `None`, select a given
204
- `applyworkflow.input_dataset_id`.
205
- output_dataset_id: If not `None`, select a given
206
- `applyworkflow.output_dataset_id`.
207
- workflow_id: If not `None`, select a given `applyworkflow.workflow_id`.
208
- status: If not `None`, select a given `applyworkflow.status`.
209
- start_timestamp_min: If not `None`, select a rows with
210
- `start_timestamp` after `start_timestamp_min`.
211
- start_timestamp_max: If not `None`, select a rows with
212
- `start_timestamp` before `start_timestamp_min`.
213
- end_timestamp_min: If not `None`, select a rows with `end_timestamp`
214
- after `end_timestamp_min`.
215
- end_timestamp_max: If not `None`, select a rows with `end_timestamp`
216
- before `end_timestamp_min`.
217
- log: If `True`, include `job.log`, if `False`
218
- `job.log` is set to `None`.
219
- """
220
- _raise_if_naive_datetime(
221
- start_timestamp_min,
222
- start_timestamp_max,
223
- end_timestamp_min,
224
- end_timestamp_max,
225
- )
226
-
227
- stm = select(ApplyWorkflow)
228
-
229
- if id is not None:
230
- stm = stm.where(ApplyWorkflow.id == id)
231
- if user_id is not None:
232
- stm = stm.join(Project).where(
233
- Project.user_list.any(UserOAuth.id == user_id)
234
- )
235
- if project_id is not None:
236
- stm = stm.where(ApplyWorkflow.project_id == project_id)
237
- if input_dataset_id is not None:
238
- stm = stm.where(ApplyWorkflow.input_dataset_id == input_dataset_id)
239
- if output_dataset_id is not None:
240
- stm = stm.where(ApplyWorkflow.output_dataset_id == output_dataset_id)
241
- if workflow_id is not None:
242
- stm = stm.where(ApplyWorkflow.workflow_id == workflow_id)
243
- if status is not None:
244
- stm = stm.where(ApplyWorkflow.status == status)
245
- if start_timestamp_min is not None:
246
- stm = stm.where(ApplyWorkflow.start_timestamp >= start_timestamp_min)
247
- if start_timestamp_max is not None:
248
- stm = stm.where(ApplyWorkflow.start_timestamp <= start_timestamp_max)
249
- if end_timestamp_min is not None:
250
- stm = stm.where(ApplyWorkflow.end_timestamp >= end_timestamp_min)
251
- if end_timestamp_max is not None:
252
- stm = stm.where(ApplyWorkflow.end_timestamp <= end_timestamp_max)
253
-
254
- res = await db.execute(stm)
255
- job_list = res.scalars().all()
256
- await db.close()
257
- if not log:
258
- for job in job_list:
259
- setattr(job, "log", None)
260
-
261
- return job_list
262
-
263
-
264
- @router_admin_v1.get("/job/{job_id}/", response_model=ApplyWorkflowReadV1)
265
- async def view_single_job(
266
- job_id: int = None,
267
- show_tmp_logs: bool = False,
268
- user: UserOAuth = Depends(current_active_superuser),
269
- db: AsyncSession = Depends(get_async_db),
270
- ) -> ApplyWorkflowReadV1:
271
-
272
- job = await db.get(ApplyWorkflow, job_id)
273
- if not job:
274
- raise HTTPException(
275
- status_code=status.HTTP_404_NOT_FOUND,
276
- detail=f"Job {job_id} not found",
277
- )
278
- await db.close()
279
-
280
- if show_tmp_logs and (job.status == JobStatusTypeV1.SUBMITTED):
281
- try:
282
- with open(f"{job.working_dir}/{WORKFLOW_LOG_FILENAME}", "r") as f:
283
- job.log = f.read()
284
- except FileNotFoundError:
285
- pass
286
-
287
- return job
288
-
289
-
290
- @router_admin_v1.patch(
291
- "/job/{job_id}/",
292
- response_model=ApplyWorkflowReadV1,
293
- )
294
- async def update_job(
295
- job_update: ApplyWorkflowUpdateV1,
296
- job_id: int,
297
- user: UserOAuth = Depends(current_active_superuser),
298
- db: AsyncSession = Depends(get_async_db),
299
- ) -> Optional[ApplyWorkflowReadV1]:
300
- """
301
- Change the status of an existing job.
302
-
303
- This endpoint is only open to superusers, and it does not apply
304
- project-based access-control to jobs.
305
- """
306
- job = await db.get(ApplyWorkflow, job_id)
307
- if job is None:
308
- raise HTTPException(
309
- status_code=status.HTTP_404_NOT_FOUND,
310
- detail=f"Job {job_id} not found",
311
- )
312
-
313
- if job_update.status != JobStatusTypeV1.FAILED:
314
- raise HTTPException(
315
- status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
316
- detail=f"Cannot set job status to {job_update.status}",
317
- )
318
-
319
- setattr(job, "status", job_update.status)
320
- setattr(job, "end_timestamp", get_timestamp())
321
- await db.commit()
322
- await db.refresh(job)
323
- await db.close()
324
- return job
325
-
326
-
327
- @router_admin_v1.get("/job/{job_id}/stop/", status_code=202)
328
- async def stop_job(
329
- job_id: int,
330
- user: UserOAuth = Depends(current_active_superuser),
331
- db: AsyncSession = Depends(get_async_db),
332
- ) -> Response:
333
- """
334
- Stop execution of a workflow job.
335
- """
336
-
337
- _check_shutdown_is_supported()
338
-
339
- job = await db.get(ApplyWorkflow, job_id)
340
- if job is None:
341
- raise HTTPException(
342
- status_code=status.HTTP_404_NOT_FOUND,
343
- detail=f"Job {job_id} not found",
344
- )
345
-
346
- _write_shutdown_file(job=job)
347
-
348
- return Response(status_code=status.HTTP_202_ACCEPTED)
349
-
350
-
351
- @router_admin_v1.get(
352
- "/job/{job_id}/download/",
353
- response_class=StreamingResponse,
354
- )
355
- async def download_job_logs(
356
- job_id: int,
357
- user: UserOAuth = Depends(current_active_superuser),
358
- db: AsyncSession = Depends(get_async_db),
359
- ) -> StreamingResponse:
360
- """
361
- Download job folder
362
- """
363
- # Get job from DB
364
- job = await db.get(ApplyWorkflow, job_id)
365
- if job is None:
366
- raise HTTPException(
367
- status_code=status.HTTP_404_NOT_FOUND,
368
- detail=f"Job {job_id} not found",
369
- )
370
- # Create and return byte stream for zipped log folder
371
- PREFIX_ZIP = Path(job.working_dir).name
372
- zip_filename = f"{PREFIX_ZIP}_archive.zip"
373
- return StreamingResponse(
374
- _zip_folder_to_byte_stream_iterator(folder=job.working_dir),
375
- media_type="application/x-zip-compressed",
376
- headers={"Content-Disposition": f"attachment;filename={zip_filename}"},
377
- )
@@ -1,26 +0,0 @@
1
- """
2
- `api/v1` module
3
- """
4
- from fastapi import APIRouter
5
-
6
- from .dataset import router as dataset_router
7
- from .job import router as job_router
8
- from .project import router as project_router
9
- from .task import router as task_router
10
- from .task_collection import router as taskcollection_router
11
- from .workflow import router as workflow_router
12
- from .workflowtask import router as workflowtask_router
13
-
14
- router_api_v1 = APIRouter()
15
-
16
- router_api_v1.include_router(
17
- project_router, prefix="/project", tags=["V1 Project"]
18
- )
19
- router_api_v1.include_router(task_router, prefix="/task", tags=["V1 Task"])
20
- router_api_v1.include_router(
21
- taskcollection_router, prefix="/task", tags=["V1 Task Collection"]
22
- )
23
- router_api_v1.include_router(dataset_router, tags=["V1 Dataset"])
24
- router_api_v1.include_router(workflow_router, tags=["V1 Workflow"])
25
- router_api_v1.include_router(workflowtask_router, tags=["V1 WorkflowTask"])
26
- router_api_v1.include_router(job_router, tags=["V1 Job"])