fractal-server 1.4.0a0__tar.gz → 1.4.0a1__tar.gz

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 (81) hide show
  1. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/PKG-INFO +1 -1
  2. fractal_server-1.4.0a1/fractal_server/__init__.py +1 -0
  3. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/api/__init__.py +1 -0
  4. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/api/v1/dataset.py +1 -1
  5. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/api/v1/job.py +43 -3
  6. fractal_server-1.4.0a1/fractal_server/app/api/v1/monitoring.py +150 -0
  7. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/api/v1/project.py +2 -2
  8. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/models/job.py +2 -27
  9. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/runner/__init__.py +1 -1
  10. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/schemas/__init__.py +1 -0
  11. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/schemas/applyworkflow.py +26 -0
  12. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/main.py +5 -0
  13. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/pyproject.toml +4 -4
  14. fractal_server-1.4.0a0/fractal_server/__init__.py +0 -1
  15. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/LICENSE +0 -0
  16. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/README.md +0 -0
  17. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/__main__.py +0 -0
  18. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/alembic.ini +0 -0
  19. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/__init__.py +0 -0
  20. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/api/v1/__init__.py +0 -0
  21. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/api/v1/_aux_functions.py +0 -0
  22. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/api/v1/task.py +0 -0
  23. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/api/v1/task_collection.py +0 -0
  24. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/api/v1/workflow.py +0 -0
  25. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/api/v1/workflowtask.py +0 -0
  26. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/db/__init__.py +0 -0
  27. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/models/__init__.py +0 -0
  28. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/models/dataset.py +0 -0
  29. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/models/linkuserproject.py +0 -0
  30. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/models/project.py +0 -0
  31. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/models/security.py +0 -0
  32. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/models/state.py +0 -0
  33. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/models/task.py +0 -0
  34. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/models/workflow.py +0 -0
  35. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/runner/.gitignore +0 -0
  36. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/runner/_common.py +0 -0
  37. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/runner/_local/__init__.py +0 -0
  38. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/runner/_local/_local_config.py +0 -0
  39. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/runner/_local/_submit_setup.py +0 -0
  40. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/runner/_local/executor.py +0 -0
  41. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/runner/_slurm/.gitignore +0 -0
  42. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/runner/_slurm/__init__.py +0 -0
  43. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/runner/_slurm/_batching.py +0 -0
  44. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/runner/_slurm/_executor_wait_thread.py +0 -0
  45. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/runner/_slurm/_slurm_config.py +0 -0
  46. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/runner/_slurm/_submit_setup.py +0 -0
  47. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/runner/_slurm/_subprocess_run_as_user.py +0 -0
  48. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/runner/_slurm/executor.py +0 -0
  49. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/runner/_slurm/remote.py +0 -0
  50. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/runner/common.py +0 -0
  51. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/runner/handle_failed_job.py +0 -0
  52. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/schemas/_validators.py +0 -0
  53. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/schemas/dataset.py +0 -0
  54. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/schemas/json_schemas/manifest.json +0 -0
  55. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/schemas/manifest.py +0 -0
  56. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/schemas/project.py +0 -0
  57. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/schemas/state.py +0 -0
  58. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/schemas/task.py +0 -0
  59. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/schemas/task_collection.py +0 -0
  60. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/schemas/user.py +0 -0
  61. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/schemas/workflow.py +0 -0
  62. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/app/security/__init__.py +0 -0
  63. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/config.py +0 -0
  64. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/logger.py +0 -0
  65. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/migrations/README +0 -0
  66. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/migrations/env.py +0 -0
  67. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/migrations/script.py.mako +0 -0
  68. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py +0 -0
  69. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/migrations/versions/50a13d6138fd_initial_schema.py +0 -0
  70. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/migrations/versions/70e77f1c38b0_add_applyworkflow_first_task_index_and_.py +0 -0
  71. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/migrations/versions/84bf0fffde30_add_dumps_to_applyworkflow.py +0 -0
  72. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/migrations/versions/8f79bd162e35_add_docs_info_and_docs_link_to_task_.py +0 -0
  73. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/migrations/versions/99ea79d9e5d2_add_dataset_history.py +0 -0
  74. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/migrations/versions/a7f4d6137b53_add_workflow_dump_to_applyworkflow.py +0 -0
  75. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/migrations/versions/e75cac726012_make_applyworkflow_start_timestamp_not_.py +0 -0
  76. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/migrations/versions/f384e1c0cf5d_drop_task_default_args_columns.py +0 -0
  77. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/py.typed +0 -0
  78. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/syringe.py +0 -0
  79. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/tasks/__init__.py +0 -0
  80. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/tasks/collection.py +0 -0
  81. {fractal_server-1.4.0a0 → fractal_server-1.4.0a1}/fractal_server/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fractal-server
3
- Version: 1.4.0a0
3
+ Version: 1.4.0a1
4
4
  Summary: Server component of the Fractal analytics platform
5
5
  Home-page: https://github.com/fractal-analytics-platform/fractal-server
6
6
  License: BSD-3-Clause
@@ -0,0 +1 @@
1
+ __VERSION__ = "1.4.0a1"
@@ -7,6 +7,7 @@ from ...config import get_settings
7
7
  from ...syringe import Inject
8
8
  from .v1.dataset import router as dataset_router
9
9
  from .v1.job import router as job_router
10
+ from .v1.monitoring import router as router_monitoring # noqa
10
11
  from .v1.project import router as project_router
11
12
  from .v1.task import router as task_router
12
13
  from .v1.task_collection import router as taskcollection_router
@@ -13,13 +13,13 @@ from ...db import AsyncSession
13
13
  from ...db import get_db
14
14
  from ...models import ApplyWorkflow
15
15
  from ...models import Dataset
16
- from ...models import JobStatusType
17
16
  from ...models import Resource
18
17
  from ...runner._common import HISTORY_FILENAME
19
18
  from ...schemas import DatasetCreate
20
19
  from ...schemas import DatasetRead
21
20
  from ...schemas import DatasetStatusRead
22
21
  from ...schemas import DatasetUpdate
22
+ from ...schemas import JobStatusType
23
23
  from ...schemas import ResourceCreate
24
24
  from ...schemas import ResourceRead
25
25
  from ...schemas import ResourceUpdate
@@ -7,6 +7,7 @@ from zipfile import ZipFile
7
7
  from fastapi import APIRouter
8
8
  from fastapi import Depends
9
9
  from fastapi import HTTPException
10
+ from fastapi import Response
10
11
  from fastapi import status
11
12
  from fastapi.responses import StreamingResponse
12
13
  from sqlmodel import select
@@ -22,11 +23,50 @@ from ...security import current_active_user
22
23
  from ...security import User
23
24
  from ._aux_functions import _get_job_check_owner
24
25
  from ._aux_functions import _get_project_check_owner
26
+ from ._aux_functions import _get_workflow_check_owner
25
27
 
26
28
 
27
29
  router = APIRouter()
28
30
 
29
31
 
32
+ @router.get("/project/job/", response_model=list[ApplyWorkflowRead])
33
+ async def get_user_jobs(
34
+ user: User = Depends(current_active_user),
35
+ ) -> list[ApplyWorkflowRead]:
36
+ """
37
+ Returns all the jobs of the current user
38
+ """
39
+
40
+ job_list = [
41
+ job for project in user.project_list for job in project.job_list
42
+ ]
43
+
44
+ return job_list
45
+
46
+
47
+ @router.get(
48
+ "/project/{project_id}/workflow/{workflow_id}/job/",
49
+ response_model=list[ApplyWorkflowRead],
50
+ )
51
+ async def get_workflow_jobs(
52
+ project_id: int,
53
+ workflow_id: int,
54
+ user: User = Depends(current_active_user),
55
+ db: AsyncSession = Depends(get_db),
56
+ ) -> Optional[list[ApplyWorkflowRead]]:
57
+ """
58
+ Returns all the jobs related to a specific workflow
59
+ """
60
+
61
+ workflow = await _get_workflow_check_owner(
62
+ project_id=project_id, workflow_id=workflow_id, user_id=user.id, db=db
63
+ )
64
+ job_list = workflow.job_list
65
+ await db.close()
66
+
67
+ return job_list
68
+
69
+
30
70
  @router.get(
31
71
  "/project/{project_id}/job/{job_id}",
32
72
  response_model=ApplyWorkflowRead,
@@ -119,14 +159,14 @@ async def get_job_list(
119
159
 
120
160
  @router.get(
121
161
  "/project/{project_id}/job/{job_id}/stop/",
122
- status_code=200,
162
+ status_code=204,
123
163
  )
124
164
  async def stop_job(
125
165
  project_id: int,
126
166
  job_id: int,
127
167
  user: User = Depends(current_active_user),
128
168
  db: AsyncSession = Depends(get_db),
129
- ) -> Optional[ApplyWorkflow]:
169
+ ) -> Response:
130
170
  """
131
171
  Stop execution of a workflow job (only available for slurm backend)
132
172
  """
@@ -161,4 +201,4 @@ async def stop_job(
161
201
  with shutdown_file.open("w") as f:
162
202
  f.write(f"Trigger executor shutdown for {job.id=}, {project_id=}.")
163
203
 
164
- return job
204
+ return Response(status_code=status.HTTP_204_NO_CONTENT)
@@ -0,0 +1,150 @@
1
+ from datetime import datetime
2
+ from typing import Optional
3
+
4
+ from fastapi import APIRouter
5
+ from fastapi import Depends
6
+ from sqlalchemy import func
7
+ from sqlmodel import select
8
+
9
+ from ...db import AsyncSession
10
+ from ...db import get_db
11
+ from ...models import ApplyWorkflow
12
+ from ...models import Dataset
13
+ from ...models import JobStatusType
14
+ from ...models import Project
15
+ from ...models import Workflow
16
+ from ...schemas import ApplyWorkflowRead
17
+ from ...schemas import DatasetRead
18
+ from ...schemas import ProjectRead
19
+ from ...schemas import WorkflowRead
20
+ from ...security import current_active_superuser
21
+ from ...security import User
22
+
23
+
24
+ router = APIRouter()
25
+
26
+
27
+ @router.get("/project/", response_model=list[ProjectRead])
28
+ async def monitor_project(
29
+ id: Optional[int] = None,
30
+ user_id: Optional[int] = None,
31
+ user: User = Depends(current_active_superuser),
32
+ db: AsyncSession = Depends(get_db),
33
+ ) -> list[ProjectRead]:
34
+
35
+ stm = select(Project)
36
+
37
+ if id is not None:
38
+ stm = stm.where(Project.id == id)
39
+
40
+ if user_id is not None:
41
+ stm = stm.where(Project.user_list.any(User.id == user_id))
42
+
43
+ res = await db.execute(stm)
44
+ project_list = res.scalars().all()
45
+ await db.close()
46
+
47
+ return project_list
48
+
49
+
50
+ @router.get("/workflow/", response_model=list[WorkflowRead])
51
+ async def monitor_workflow(
52
+ id: Optional[int] = None,
53
+ project_id: Optional[int] = None,
54
+ name_contains: Optional[str] = None,
55
+ user: User = Depends(current_active_superuser),
56
+ db: AsyncSession = Depends(get_db),
57
+ ) -> list[WorkflowRead]:
58
+ stm = select(Workflow)
59
+
60
+ if id is not None:
61
+ stm = stm.where(Workflow.id == id)
62
+ if project_id is not None:
63
+ stm = stm.where(Workflow.project_id == project_id)
64
+ if name_contains is not None:
65
+ # SQLAlchemy2: use icontains
66
+ stm = stm.where(
67
+ func.lower(Workflow.name).contains(name_contains.lower())
68
+ )
69
+
70
+ res = await db.execute(stm)
71
+ workflow_list = res.scalars().all()
72
+ await db.close()
73
+
74
+ return workflow_list
75
+
76
+
77
+ @router.get("/dataset/", response_model=list[DatasetRead])
78
+ async def monitor_dataset(
79
+ id: Optional[int] = None,
80
+ project_id: Optional[int] = None,
81
+ name_contains: Optional[str] = None,
82
+ type: Optional[str] = None,
83
+ user: User = Depends(current_active_superuser),
84
+ db: AsyncSession = Depends(get_db),
85
+ ) -> list[DatasetRead]:
86
+ stm = select(Dataset)
87
+
88
+ if id is not None:
89
+ stm = stm.where(Dataset.id == id)
90
+ if project_id is not None:
91
+ stm = stm.where(Dataset.project_id == project_id)
92
+ if name_contains is not None:
93
+ # SQLAlchemy2: use icontains
94
+ stm = stm.where(
95
+ func.lower(Dataset.name).contains(name_contains.lower())
96
+ )
97
+ if type is not None:
98
+ stm = stm.where(Dataset.type == type)
99
+
100
+ res = await db.execute(stm)
101
+ dataset_list = res.scalars().all()
102
+ await db.close()
103
+
104
+ return dataset_list
105
+
106
+
107
+ @router.get("/job/", response_model=list[ApplyWorkflowRead])
108
+ async def monitor_job(
109
+ id: Optional[int] = None,
110
+ project_id: Optional[int] = None,
111
+ input_dataset_id: Optional[int] = None,
112
+ output_dataset_id: Optional[int] = None,
113
+ workflow_id: Optional[int] = None,
114
+ status: Optional[JobStatusType] = None,
115
+ start_timestamp_min: Optional[datetime] = None,
116
+ start_timestamp_max: Optional[datetime] = None,
117
+ end_timestamp_min: Optional[datetime] = None,
118
+ end_timestamp_max: Optional[datetime] = None,
119
+ user: User = Depends(current_active_superuser),
120
+ db: AsyncSession = Depends(get_db),
121
+ ) -> list[ApplyWorkflowRead]:
122
+
123
+ stm = select(ApplyWorkflow)
124
+
125
+ if id is not None:
126
+ stm = stm.where(ApplyWorkflow.id == id)
127
+ if project_id is not None:
128
+ stm = stm.where(ApplyWorkflow.project_id == project_id)
129
+ if input_dataset_id is not None:
130
+ stm = stm.where(ApplyWorkflow.input_dataset_id == input_dataset_id)
131
+ if output_dataset_id is not None:
132
+ stm = stm.where(ApplyWorkflow.output_dataset_id == output_dataset_id)
133
+ if workflow_id is not None:
134
+ stm = stm.where(ApplyWorkflow.workflow_id == workflow_id)
135
+ if status is not None:
136
+ stm = stm.where(ApplyWorkflow.status == status)
137
+ if start_timestamp_min is not None:
138
+ stm = stm.where(ApplyWorkflow.start_timestamp >= start_timestamp_min)
139
+ if start_timestamp_max is not None:
140
+ stm = stm.where(ApplyWorkflow.start_timestamp <= start_timestamp_max)
141
+ if end_timestamp_min is not None:
142
+ stm = stm.where(ApplyWorkflow.end_timestamp >= end_timestamp_min)
143
+ if end_timestamp_max is not None:
144
+ stm = stm.where(ApplyWorkflow.end_timestamp <= end_timestamp_max)
145
+
146
+ res = await db.execute(stm)
147
+ job_list = res.scalars().all()
148
+ await db.close()
149
+
150
+ return job_list
@@ -18,7 +18,6 @@ from ...db import DBSyncSession
18
18
  from ...db import get_db
19
19
  from ...db import get_sync_db
20
20
  from ...models import ApplyWorkflow
21
- from ...models import JobStatusType
22
21
  from ...models import LinkUserProject
23
22
  from ...models import Project
24
23
  from ...runner import submit_workflow
@@ -26,6 +25,7 @@ from ...runner import validate_workflow_compatibility
26
25
  from ...runner.common import set_start_and_last_task_index
27
26
  from ...schemas import ApplyWorkflowCreate
28
27
  from ...schemas import ApplyWorkflowRead
28
+ from ...schemas import JobStatusType
29
29
  from ...schemas import ProjectCreate
30
30
  from ...schemas import ProjectRead
31
31
  from ...schemas import ProjectUpdate
@@ -308,7 +308,7 @@ async def apply_workflow(
308
308
  workflow_dump=dict(
309
309
  workflow.dict(exclude={"task_list"}),
310
310
  task_list=[
311
- dict(wf_task.task.dict(exclude={"task"}), task=wf_task.dict())
311
+ dict(wf_task.dict(exclude={"task"}), task=wf_task.task.dict())
312
312
  for wf_task in workflow.task_list
313
313
  ],
314
314
  ),
@@ -1,5 +1,4 @@
1
1
  from datetime import datetime
2
- from enum import Enum
3
2
  from typing import Any
4
3
  from typing import Optional
5
4
 
@@ -10,34 +9,10 @@ from sqlmodel import Field
10
9
  from sqlmodel import SQLModel
11
10
 
12
11
  from ...utils import get_timestamp
12
+ from ..schemas import JobStatusType
13
13
  from ..schemas.applyworkflow import _ApplyWorkflowBase
14
14
 
15
15
 
16
- class JobStatusType(str, Enum):
17
- """
18
- Define the job status available
19
-
20
- Attributes:
21
- SUBMITTED:
22
- The workflow has been applied but not yet scheduled with an
23
- executor. In this phase, due diligence takes place, such as
24
- creating working directory, assemblying arguments, etc.
25
- RUNNING:
26
- The workflow was scheduled with an executor. Note that it might not
27
- yet be running within the executor, e.g., jobs could still be
28
- pending within a SLURM executor.
29
- DONE:
30
- The workflow was applied successfully
31
- FAILED:
32
- The workflow terminated with an error.
33
- """
34
-
35
- SUBMITTED = "submitted"
36
- RUNNING = "running"
37
- DONE = "done"
38
- FAILED = "failed"
39
-
40
-
41
16
  class ApplyWorkflow(_ApplyWorkflowBase, SQLModel, table=True):
42
17
  """
43
18
  Represent a workflow run
@@ -115,5 +90,5 @@ class ApplyWorkflow(_ApplyWorkflowBase, SQLModel, table=True):
115
90
  end_timestamp: Optional[datetime] = Field(
116
91
  default=None, sa_column=Column(DateTime(timezone=True))
117
92
  )
118
- status: JobStatusType = JobStatusType.SUBMITTED
93
+ status: str = JobStatusType.SUBMITTED
119
94
  log: Optional[str] = None
@@ -30,9 +30,9 @@ from ...utils import get_timestamp
30
30
  from ..db import DB
31
31
  from ..models import ApplyWorkflow
32
32
  from ..models import Dataset
33
- from ..models import JobStatusType
34
33
  from ..models import Workflow
35
34
  from ..models import WorkflowTask
35
+ from ..schemas import JobStatusType
36
36
  from ._local import process_workflow as local_process_workflow
37
37
  from .common import close_job_logger
38
38
  from .common import JobExecutionError
@@ -3,6 +3,7 @@ Schemas for API request/response bodies
3
3
  """
4
4
  from .applyworkflow import ApplyWorkflowCreate # noqa: F401
5
5
  from .applyworkflow import ApplyWorkflowRead # noqa: F401
6
+ from .applyworkflow import JobStatusType # noqa: F401
6
7
  from .dataset import DatasetCreate # noqa: F401
7
8
  from .dataset import DatasetRead # noqa: F401
8
9
  from .dataset import DatasetStatusRead # noqa: F401
@@ -1,4 +1,5 @@
1
1
  from datetime import datetime
2
+ from enum import Enum
2
3
  from typing import Any
3
4
  from typing import Optional
4
5
 
@@ -14,6 +15,31 @@ __all__ = (
14
15
  )
15
16
 
16
17
 
18
+ class JobStatusType(str, Enum):
19
+ """
20
+ Define the available job statuses
21
+
22
+ Attributes:
23
+ SUBMITTED:
24
+ The workflow has been applied but not yet scheduled with an
25
+ executor. In this phase, due diligence takes place, such as
26
+ creating working directory, assemblying arguments, etc.
27
+ RUNNING:
28
+ The workflow was scheduled with an executor. Note that it might not
29
+ yet be running within the executor, e.g., jobs could still be
30
+ pending within a SLURM executor.
31
+ DONE:
32
+ The workflow was applied successfully
33
+ FAILED:
34
+ The workflow terminated with an error.
35
+ """
36
+
37
+ SUBMITTED = "submitted"
38
+ RUNNING = "running"
39
+ DONE = "done"
40
+ FAILED = "failed"
41
+
42
+
17
43
  class _ApplyWorkflowBase(BaseModel):
18
44
  """
19
45
  Base class for `ApplyWorkflow`.
@@ -48,10 +48,15 @@ def collect_routers(app: FastAPI) -> None:
48
48
  """
49
49
  from .app.api import router_default
50
50
  from .app.api import router_v1
51
+ from .app.api import router_monitoring
52
+
51
53
  from .app.security import auth_router
52
54
 
53
55
  app.include_router(router_default, prefix="/api")
54
56
  app.include_router(router_v1, prefix="/api/v1")
57
+ app.include_router(
58
+ router_monitoring, prefix="/monitoring", tags=["Monitoring"]
59
+ )
55
60
  app.include_router(auth_router, prefix="/auth", tags=["auth"])
56
61
 
57
62
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "fractal-server"
3
- version = "1.4.0a0"
3
+ version = "1.4.0a1"
4
4
  description = "Server component of the Fractal analytics platform"
5
5
  authors = [
6
6
  "Jacopo Nespolo <jacopo.nespolo@exact-lab.it>",
@@ -48,7 +48,7 @@ gunicorn = ["gunicorn"]
48
48
  asgi-lifespan = "^2"
49
49
  pytest = "^7.2"
50
50
  httpx = "^0.23"
51
- devtools = "^0.10"
51
+ devtools = "^0.12"
52
52
  pytest-asyncio = "^0.20"
53
53
  bumpver = "^2022.1120"
54
54
  pre-commit = "^2.19"
@@ -82,7 +82,7 @@ filterwarnings = [
82
82
  ]
83
83
 
84
84
  [tool.bumpver]
85
- current_version = "1.4.0a0"
85
+ current_version = "1.4.0a1"
86
86
  version_pattern = "MAJOR.MINOR.PATCH[PYTAGNUM]"
87
87
  commit_message = "bump version {old_version} -> {new_version}"
88
88
  commit = true
@@ -107,5 +107,5 @@ relative_files = true
107
107
  omit = ["tests/*"]
108
108
 
109
109
  [[tool.mypy.overrides]]
110
- module = ["devtools", "uvicorn"]
110
+ module = ["devtools", "uvicorn", "pytest", "asgi_lifespan", "asyncpg"]
111
111
  ignore_missing_imports = true
@@ -1 +0,0 @@
1
- __VERSION__ = "1.4.0a0"