fractal-server 2.8.0__py3-none-any.whl → 2.9.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.
- fractal_server/__init__.py +1 -1
- fractal_server/app/db/__init__.py +2 -35
- fractal_server/app/models/v2/__init__.py +3 -3
- fractal_server/app/models/v2/task.py +0 -72
- fractal_server/app/models/v2/task_group.py +113 -0
- fractal_server/app/routes/admin/v1.py +13 -30
- fractal_server/app/routes/admin/v2/__init__.py +4 -0
- fractal_server/app/routes/admin/v2/job.py +13 -24
- fractal_server/app/routes/admin/v2/task.py +13 -0
- fractal_server/app/routes/admin/v2/task_group.py +75 -14
- fractal_server/app/routes/admin/v2/task_group_lifecycle.py +267 -0
- fractal_server/app/routes/api/v1/project.py +7 -19
- fractal_server/app/routes/api/v2/__init__.py +11 -2
- fractal_server/app/routes/api/v2/{_aux_functions_task_collection.py → _aux_functions_task_lifecycle.py} +83 -0
- fractal_server/app/routes/api/v2/_aux_functions_tasks.py +27 -17
- fractal_server/app/routes/api/v2/submit.py +19 -24
- fractal_server/app/routes/api/v2/task_collection.py +33 -65
- fractal_server/app/routes/api/v2/task_collection_custom.py +3 -3
- fractal_server/app/routes/api/v2/task_group.py +86 -14
- fractal_server/app/routes/api/v2/task_group_lifecycle.py +272 -0
- fractal_server/app/routes/api/v2/workflow.py +1 -1
- fractal_server/app/routes/api/v2/workflow_import.py +2 -2
- fractal_server/app/routes/auth/current_user.py +60 -17
- fractal_server/app/routes/auth/group.py +67 -39
- fractal_server/app/routes/auth/users.py +97 -99
- fractal_server/app/routes/aux/__init__.py +20 -0
- fractal_server/app/runner/executors/slurm/_slurm_config.py +0 -17
- fractal_server/app/runner/executors/slurm/ssh/executor.py +49 -204
- fractal_server/app/runner/executors/slurm/sudo/executor.py +26 -109
- fractal_server/app/runner/executors/slurm/utils_executors.py +58 -0
- fractal_server/app/runner/v2/_local_experimental/executor.py +2 -1
- fractal_server/app/schemas/_validators.py +1 -16
- fractal_server/app/schemas/user.py +16 -10
- fractal_server/app/schemas/user_group.py +0 -11
- fractal_server/app/schemas/v1/applyworkflow.py +0 -8
- fractal_server/app/schemas/v1/dataset.py +0 -5
- fractal_server/app/schemas/v1/project.py +0 -5
- fractal_server/app/schemas/v1/state.py +0 -5
- fractal_server/app/schemas/v1/workflow.py +0 -5
- fractal_server/app/schemas/v2/__init__.py +4 -2
- fractal_server/app/schemas/v2/dataset.py +1 -7
- fractal_server/app/schemas/v2/job.py +0 -8
- fractal_server/app/schemas/v2/project.py +0 -5
- fractal_server/app/schemas/v2/task_collection.py +13 -31
- fractal_server/app/schemas/v2/task_group.py +59 -8
- fractal_server/app/schemas/v2/workflow.py +0 -5
- fractal_server/app/security/__init__.py +17 -0
- fractal_server/config.py +61 -59
- fractal_server/migrations/versions/d256a7379ab8_taskgroup_activity_and_venv_info_to_.py +117 -0
- fractal_server/ssh/_fabric.py +156 -83
- fractal_server/string_tools.py +10 -3
- fractal_server/tasks/utils.py +2 -12
- fractal_server/tasks/v2/local/__init__.py +3 -0
- fractal_server/tasks/v2/local/_utils.py +70 -0
- fractal_server/tasks/v2/local/collect.py +291 -0
- fractal_server/tasks/v2/local/deactivate.py +218 -0
- fractal_server/tasks/v2/local/reactivate.py +159 -0
- fractal_server/tasks/v2/ssh/__init__.py +3 -0
- fractal_server/tasks/v2/ssh/_utils.py +87 -0
- fractal_server/tasks/v2/ssh/collect.py +311 -0
- fractal_server/tasks/v2/ssh/deactivate.py +253 -0
- fractal_server/tasks/v2/ssh/reactivate.py +202 -0
- fractal_server/tasks/v2/templates/{_2_preliminary_pip_operations.sh → 1_create_venv.sh} +6 -7
- fractal_server/tasks/v2/templates/{_3_pip_install.sh → 2_pip_install.sh} +8 -1
- fractal_server/tasks/v2/templates/{_4_pip_freeze.sh → 3_pip_freeze.sh} +0 -7
- fractal_server/tasks/v2/templates/{_5_pip_show.sh → 4_pip_show.sh} +5 -6
- fractal_server/tasks/v2/templates/5_get_venv_size_and_file_number.sh +10 -0
- fractal_server/tasks/v2/templates/6_pip_install_from_freeze.sh +35 -0
- fractal_server/tasks/v2/utils_background.py +42 -127
- fractal_server/tasks/v2/utils_templates.py +32 -2
- fractal_server/utils.py +4 -2
- fractal_server/zip_tools.py +21 -4
- {fractal_server-2.8.0.dist-info → fractal_server-2.9.0.dist-info}/METADATA +3 -5
- {fractal_server-2.8.0.dist-info → fractal_server-2.9.0.dist-info}/RECORD +78 -65
- fractal_server/app/models/v2/collection_state.py +0 -22
- fractal_server/tasks/v2/collection_local.py +0 -357
- fractal_server/tasks/v2/collection_ssh.py +0 -352
- fractal_server/tasks/v2/templates/_1_create_venv.sh +0 -42
- /fractal_server/tasks/v2/{database_operations.py → utils_database.py} +0 -0
- {fractal_server-2.8.0.dist-info → fractal_server-2.9.0.dist-info}/LICENSE +0 -0
- {fractal_server-2.8.0.dist-info → fractal_server-2.9.0.dist-info}/WHEEL +0 -0
- {fractal_server-2.8.0.dist-info → fractal_server-2.9.0.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,5 @@
|
|
1
|
+
import json
|
1
2
|
import os
|
2
|
-
from datetime import datetime
|
3
|
-
from datetime import timezone
|
4
3
|
from pathlib import Path
|
5
4
|
from typing import Optional
|
6
5
|
|
@@ -15,7 +14,6 @@ from sqlmodel import select
|
|
15
14
|
from .....config import get_settings
|
16
15
|
from .....logger import set_logger
|
17
16
|
from .....syringe import Inject
|
18
|
-
from .....utils import get_timestamp
|
19
17
|
from ....db import AsyncSession
|
20
18
|
from ....db import get_async_db
|
21
19
|
from ....models.v2 import JobV2
|
@@ -30,6 +28,7 @@ from ...aux.validate_user_settings import validate_user_settings
|
|
30
28
|
from ._aux_functions import _get_dataset_check_owner
|
31
29
|
from ._aux_functions import _get_workflow_check_owner
|
32
30
|
from ._aux_functions import clean_app_job_list_v2
|
31
|
+
from fractal_server.app.models import TaskGroupV2
|
33
32
|
from fractal_server.app.models import UserOAuth
|
34
33
|
from fractal_server.app.routes.api.v2._aux_functions_tasks import (
|
35
34
|
_get_task_read_access,
|
@@ -37,10 +36,6 @@ from fractal_server.app.routes.api.v2._aux_functions_tasks import (
|
|
37
36
|
from fractal_server.app.routes.auth import current_active_verified_user
|
38
37
|
|
39
38
|
|
40
|
-
def _encode_as_utc(dt: datetime):
|
41
|
-
return dt.replace(tzinfo=timezone.utc).isoformat()
|
42
|
-
|
43
|
-
|
44
39
|
router = APIRouter()
|
45
40
|
logger = set_logger(__name__)
|
46
41
|
|
@@ -112,22 +107,23 @@ async def apply_workflow(
|
|
112
107
|
)
|
113
108
|
|
114
109
|
# Check that tasks have read-access and are `active`
|
110
|
+
used_task_group_ids = set()
|
115
111
|
for wftask in workflow.task_list[
|
116
112
|
first_task_index : last_task_index + 1 # noqa: E203
|
117
113
|
]:
|
118
|
-
await _get_task_read_access(
|
114
|
+
task = await _get_task_read_access(
|
119
115
|
user_id=user.id,
|
120
116
|
task_id=wftask.task_id,
|
121
117
|
require_active=True,
|
122
118
|
db=db,
|
123
119
|
)
|
120
|
+
used_task_group_ids.add(task.taskgroupv2_id)
|
124
121
|
|
125
122
|
# Validate user settings
|
126
123
|
FRACTAL_RUNNER_BACKEND = settings.FRACTAL_RUNNER_BACKEND
|
127
124
|
user_settings = await validate_user_settings(
|
128
125
|
user=user, backend=FRACTAL_RUNNER_BACKEND, db=db
|
129
126
|
)
|
130
|
-
|
131
127
|
# Check that no other job with the same dataset_id is SUBMITTED
|
132
128
|
stm = (
|
133
129
|
select(JobV2)
|
@@ -163,20 +159,9 @@ async def apply_workflow(
|
|
163
159
|
dataset_id=dataset_id,
|
164
160
|
workflow_id=workflow_id,
|
165
161
|
user_email=user.email,
|
166
|
-
dataset_dump=
|
167
|
-
|
168
|
-
|
169
|
-
),
|
170
|
-
timestamp_created=_encode_as_utc(dataset.timestamp_created),
|
171
|
-
),
|
172
|
-
workflow_dump=dict(
|
173
|
-
**workflow.model_dump(exclude={"task_list", "timestamp_created"}),
|
174
|
-
timestamp_created=_encode_as_utc(workflow.timestamp_created),
|
175
|
-
),
|
176
|
-
project_dump=dict(
|
177
|
-
**project.model_dump(exclude={"user_list", "timestamp_created"}),
|
178
|
-
timestamp_created=_encode_as_utc(project.timestamp_created),
|
179
|
-
),
|
162
|
+
dataset_dump=json.loads(dataset.json(exclude={"images", "history"})),
|
163
|
+
workflow_dump=json.loads(workflow.json(exclude={"task_list"})),
|
164
|
+
project_dump=json.loads(project.json(exclude={"user_list"})),
|
180
165
|
**job_create.dict(),
|
181
166
|
)
|
182
167
|
|
@@ -184,8 +169,18 @@ async def apply_workflow(
|
|
184
169
|
await db.commit()
|
185
170
|
await db.refresh(job)
|
186
171
|
|
172
|
+
# Update TaskGroupV2.timestamp_last_used
|
173
|
+
res = await db.execute(
|
174
|
+
select(TaskGroupV2).where(TaskGroupV2.id.in_(used_task_group_ids))
|
175
|
+
)
|
176
|
+
used_task_groups = res.scalars().all()
|
177
|
+
for used_task_group in used_task_groups:
|
178
|
+
used_task_group.timestamp_last_used = job.start_timestamp
|
179
|
+
db.add(used_task_group)
|
180
|
+
await db.commit()
|
181
|
+
|
187
182
|
# Define server-side job directory
|
188
|
-
timestamp_string =
|
183
|
+
timestamp_string = job.start_timestamp.strftime("%Y%m%d_%H%M%S")
|
189
184
|
WORKFLOW_DIR_LOCAL = settings.FRACTAL_RUNNER_WORKING_BASE_DIR / (
|
190
185
|
f"proj_v2_{project_id:07d}_wf_{workflow_id:07d}_job_{job.id:07d}"
|
191
186
|
f"_{timestamp_string}"
|
@@ -17,24 +17,27 @@ from .....logger import set_logger
|
|
17
17
|
from .....syringe import Inject
|
18
18
|
from ....db import AsyncSession
|
19
19
|
from ....db import get_async_db
|
20
|
-
from ....models.v2 import CollectionStateV2
|
21
20
|
from ....models.v2 import TaskGroupV2
|
22
|
-
from ....schemas.v2 import CollectionStateReadV2
|
23
|
-
from ....schemas.v2 import CollectionStatusV2
|
24
21
|
from ....schemas.v2 import TaskCollectPipV2
|
25
|
-
from ....schemas.v2 import
|
22
|
+
from ....schemas.v2 import TaskGroupActivityStatusV2
|
23
|
+
from ....schemas.v2 import TaskGroupActivityV2Read
|
24
|
+
from ....schemas.v2 import TaskGroupCreateV2Strict
|
26
25
|
from ...aux.validate_user_settings import validate_user_settings
|
27
|
-
from .
|
26
|
+
from ._aux_functions_task_lifecycle import get_package_version_from_pypi
|
28
27
|
from ._aux_functions_tasks import _get_valid_user_group_id
|
29
28
|
from ._aux_functions_tasks import _verify_non_duplication_group_constraint
|
30
29
|
from ._aux_functions_tasks import _verify_non_duplication_user_constraint
|
31
30
|
from fractal_server.app.models import UserOAuth
|
32
|
-
from fractal_server.app.
|
31
|
+
from fractal_server.app.models.v2 import TaskGroupActivityV2
|
33
32
|
from fractal_server.app.routes.auth import current_active_verified_user
|
33
|
+
from fractal_server.app.schemas.v2 import (
|
34
|
+
TaskGroupActivityActionV2,
|
35
|
+
)
|
34
36
|
from fractal_server.app.schemas.v2 import TaskGroupV2OriginEnum
|
35
|
-
from fractal_server.tasks.v2.
|
36
|
-
|
37
|
+
from fractal_server.tasks.v2.local.collect import (
|
38
|
+
collect_local,
|
37
39
|
)
|
40
|
+
from fractal_server.tasks.v2.ssh import collect_ssh
|
38
41
|
from fractal_server.tasks.v2.utils_package_names import _parse_wheel_filename
|
39
42
|
from fractal_server.tasks.v2.utils_package_names import normalize_package_name
|
40
43
|
from fractal_server.tasks.v2.utils_python_interpreter import (
|
@@ -48,7 +51,7 @@ logger = set_logger(__name__)
|
|
48
51
|
|
49
52
|
@router.post(
|
50
53
|
"/collect/pip/",
|
51
|
-
response_model=
|
54
|
+
response_model=TaskGroupActivityV2Read,
|
52
55
|
)
|
53
56
|
async def collect_tasks_pip(
|
54
57
|
task_collect: TaskCollectPipV2,
|
@@ -59,12 +62,9 @@ async def collect_tasks_pip(
|
|
59
62
|
user_group_id: Optional[int] = None,
|
60
63
|
user: UserOAuth = Depends(current_active_verified_user),
|
61
64
|
db: AsyncSession = Depends(get_async_db),
|
62
|
-
) ->
|
65
|
+
) -> TaskGroupActivityV2Read:
|
63
66
|
"""
|
64
|
-
Task
|
65
|
-
|
66
|
-
Trigger the creation of a dedicated virtual environment, the installation
|
67
|
-
of a package and the collection of tasks as advertised in the manifest.
|
67
|
+
Task-collection endpoint
|
68
68
|
"""
|
69
69
|
|
70
70
|
# Get settings
|
@@ -164,7 +164,7 @@ async def collect_tasks_pip(
|
|
164
164
|
|
165
165
|
# Validate TaskGroupV2 attributes
|
166
166
|
try:
|
167
|
-
|
167
|
+
TaskGroupCreateV2Strict(**task_group_attrs)
|
168
168
|
except ValidationError as e:
|
169
169
|
raise HTTPException(
|
170
170
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
@@ -227,32 +227,24 @@ async def collect_tasks_pip(
|
|
227
227
|
db.expunge(task_group)
|
228
228
|
|
229
229
|
# All checks are OK, proceed with task collection
|
230
|
-
|
231
|
-
|
232
|
-
|
230
|
+
task_group_activity = TaskGroupActivityV2(
|
231
|
+
user_id=task_group.user_id,
|
232
|
+
taskgroupv2_id=task_group.id,
|
233
|
+
status=TaskGroupActivityStatusV2.PENDING,
|
234
|
+
action=TaskGroupActivityActionV2.COLLECT,
|
235
|
+
pkg_name=task_group.pkg_name,
|
233
236
|
version=task_group.version,
|
234
|
-
path=task_group.path,
|
235
|
-
venv_path=task_group.venv_path,
|
236
237
|
)
|
237
|
-
|
238
|
-
data=collection_state_data, taskgroupv2_id=task_group.id
|
239
|
-
)
|
240
|
-
db.add(state)
|
238
|
+
db.add(task_group_activity)
|
241
239
|
await db.commit()
|
242
|
-
await db.refresh(
|
243
|
-
|
240
|
+
await db.refresh(task_group_activity)
|
244
241
|
logger = set_logger(logger_name="collect_tasks_pip")
|
245
242
|
|
246
243
|
# END of SSH/non-SSH common part
|
247
244
|
|
248
245
|
if settings.FRACTAL_RUNNER_BACKEND == "slurm_ssh":
|
249
246
|
# SSH task collection
|
250
|
-
|
251
|
-
from fractal_server.tasks.v2.collection_ssh import (
|
252
|
-
collect_package_ssh,
|
253
|
-
)
|
254
|
-
|
255
|
-
# User appropriate FractalSSH object
|
247
|
+
# Use appropriate FractalSSH object
|
256
248
|
ssh_credentials = dict(
|
257
249
|
user=user_settings.ssh_username,
|
258
250
|
host=user_settings.ssh_host,
|
@@ -262,9 +254,9 @@ async def collect_tasks_pip(
|
|
262
254
|
fractal_ssh = fractal_ssh_list.get(**ssh_credentials)
|
263
255
|
|
264
256
|
background_tasks.add_task(
|
265
|
-
|
266
|
-
|
267
|
-
|
257
|
+
collect_ssh,
|
258
|
+
task_group_id=task_group.id,
|
259
|
+
task_group_activity_id=task_group_activity.id,
|
268
260
|
fractal_ssh=fractal_ssh,
|
269
261
|
tasks_base_dir=user_settings.ssh_tasks_dir,
|
270
262
|
)
|
@@ -272,38 +264,14 @@ async def collect_tasks_pip(
|
|
272
264
|
else:
|
273
265
|
# Local task collection
|
274
266
|
background_tasks.add_task(
|
275
|
-
|
276
|
-
|
277
|
-
|
267
|
+
collect_local,
|
268
|
+
task_group_id=task_group.id,
|
269
|
+
task_group_activity_id=task_group_activity.id,
|
278
270
|
)
|
279
271
|
logger.debug(
|
280
272
|
"Task-collection endpoint: start background collection "
|
281
|
-
"and return
|
273
|
+
"and return task_group_activity"
|
282
274
|
)
|
283
275
|
reset_logger_handlers(logger)
|
284
|
-
|
285
|
-
|
286
|
-
f"GET /task/collect/{state.id}/ to query collection status"
|
287
|
-
)
|
288
|
-
state.data["info"] = info
|
289
|
-
response.status_code = status.HTTP_201_CREATED
|
290
|
-
|
291
|
-
return state
|
292
|
-
|
293
|
-
|
294
|
-
@router.get("/collect/{state_id}/", response_model=CollectionStateReadV2)
|
295
|
-
async def check_collection_status(
|
296
|
-
state_id: int,
|
297
|
-
user: UserOAuth = Depends(current_active_user),
|
298
|
-
db: AsyncSession = Depends(get_async_db),
|
299
|
-
) -> CollectionStateReadV2: # State[TaskCollectStatus]
|
300
|
-
"""
|
301
|
-
Check status of background task collection
|
302
|
-
"""
|
303
|
-
state = await db.get(CollectionStateV2, state_id)
|
304
|
-
if state is None:
|
305
|
-
raise HTTPException(
|
306
|
-
status_code=status.HTTP_404_NOT_FOUND,
|
307
|
-
detail=f"No task collection info with id={state_id}",
|
308
|
-
)
|
309
|
-
return state
|
276
|
+
response.status_code = status.HTTP_202_ACCEPTED
|
277
|
+
return task_group_activity
|
@@ -27,12 +27,12 @@ from fractal_server.config import get_settings
|
|
27
27
|
from fractal_server.logger import set_logger
|
28
28
|
from fractal_server.string_tools import validate_cmd
|
29
29
|
from fractal_server.syringe import Inject
|
30
|
-
from fractal_server.tasks.v2.database_operations import (
|
31
|
-
create_db_tasks_and_update_task_group,
|
32
|
-
)
|
33
30
|
from fractal_server.tasks.v2.utils_background import (
|
34
31
|
_prepare_tasks_metadata,
|
35
32
|
)
|
33
|
+
from fractal_server.tasks.v2.utils_database import (
|
34
|
+
create_db_tasks_and_update_task_group,
|
35
|
+
)
|
36
36
|
|
37
37
|
router = APIRouter()
|
38
38
|
|
@@ -1,3 +1,6 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
from typing import Optional
|
3
|
+
|
1
4
|
from fastapi import APIRouter
|
2
5
|
from fastapi import Depends
|
3
6
|
from fastapi import HTTPException
|
@@ -13,13 +16,17 @@ from fractal_server.app.db import AsyncSession
|
|
13
16
|
from fractal_server.app.db import get_async_db
|
14
17
|
from fractal_server.app.models import LinkUserGroup
|
15
18
|
from fractal_server.app.models import UserOAuth
|
16
|
-
from fractal_server.app.models.v2 import
|
19
|
+
from fractal_server.app.models.v2 import TaskGroupActivityV2
|
17
20
|
from fractal_server.app.models.v2 import TaskGroupV2
|
18
21
|
from fractal_server.app.models.v2 import WorkflowTaskV2
|
19
22
|
from fractal_server.app.routes.auth import current_active_user
|
20
23
|
from fractal_server.app.routes.auth._aux_auth import (
|
21
24
|
_verify_user_belongs_to_group,
|
22
25
|
)
|
26
|
+
from fractal_server.app.routes.aux import _raise_if_naive_datetime
|
27
|
+
from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
|
28
|
+
from fractal_server.app.schemas.v2 import TaskGroupActivityStatusV2
|
29
|
+
from fractal_server.app.schemas.v2 import TaskGroupActivityV2Read
|
23
30
|
from fractal_server.app.schemas.v2 import TaskGroupReadV2
|
24
31
|
from fractal_server.app.schemas.v2 import TaskGroupUpdateV2
|
25
32
|
from fractal_server.logger import set_logger
|
@@ -29,6 +36,72 @@ router = APIRouter()
|
|
29
36
|
logger = set_logger(__name__)
|
30
37
|
|
31
38
|
|
39
|
+
@router.get("/activity/", response_model=list[TaskGroupActivityV2Read])
|
40
|
+
async def get_task_group_activity_list(
|
41
|
+
task_group_activity_id: Optional[int] = None,
|
42
|
+
taskgroupv2_id: Optional[int] = None,
|
43
|
+
pkg_name: Optional[str] = None,
|
44
|
+
status: Optional[TaskGroupActivityStatusV2] = None,
|
45
|
+
action: Optional[TaskGroupActivityActionV2] = None,
|
46
|
+
timestamp_started_min: Optional[datetime] = None,
|
47
|
+
user: UserOAuth = Depends(current_active_user),
|
48
|
+
db: AsyncSession = Depends(get_async_db),
|
49
|
+
) -> list[TaskGroupActivityV2Read]:
|
50
|
+
|
51
|
+
_raise_if_naive_datetime(timestamp_started_min)
|
52
|
+
|
53
|
+
stm = select(TaskGroupActivityV2).where(
|
54
|
+
TaskGroupActivityV2.user_id == user.id
|
55
|
+
)
|
56
|
+
if task_group_activity_id is not None:
|
57
|
+
stm = stm.where(TaskGroupActivityV2.id == task_group_activity_id)
|
58
|
+
if taskgroupv2_id is not None:
|
59
|
+
stm = stm.where(TaskGroupActivityV2.taskgroupv2_id == taskgroupv2_id)
|
60
|
+
if pkg_name is not None:
|
61
|
+
stm = stm.where(TaskGroupActivityV2.pkg_name.icontains(pkg_name))
|
62
|
+
if status is not None:
|
63
|
+
stm = stm.where(TaskGroupActivityV2.status == status)
|
64
|
+
if action is not None:
|
65
|
+
stm = stm.where(TaskGroupActivityV2.action == action)
|
66
|
+
if timestamp_started_min is not None:
|
67
|
+
stm = stm.where(
|
68
|
+
TaskGroupActivityV2.timestamp_started >= timestamp_started_min
|
69
|
+
)
|
70
|
+
|
71
|
+
res = await db.execute(stm)
|
72
|
+
activities = res.scalars().all()
|
73
|
+
return activities
|
74
|
+
|
75
|
+
|
76
|
+
@router.get(
|
77
|
+
"/activity/{task_group_activity_id}/",
|
78
|
+
response_model=TaskGroupActivityV2Read,
|
79
|
+
)
|
80
|
+
async def get_task_group_activity(
|
81
|
+
task_group_activity_id: int,
|
82
|
+
user: UserOAuth = Depends(current_active_user),
|
83
|
+
db: AsyncSession = Depends(get_async_db),
|
84
|
+
) -> TaskGroupActivityV2Read:
|
85
|
+
|
86
|
+
activity = await db.get(TaskGroupActivityV2, task_group_activity_id)
|
87
|
+
|
88
|
+
if activity is None:
|
89
|
+
raise HTTPException(
|
90
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
91
|
+
detail=f"TaskGroupActivityV2 {task_group_activity_id} not found",
|
92
|
+
)
|
93
|
+
if activity.user_id != user.id:
|
94
|
+
raise HTTPException(
|
95
|
+
status_code=status.HTTP_403_FORBIDDEN,
|
96
|
+
detail=(
|
97
|
+
"You are not the owner of TaskGroupActivityV2 "
|
98
|
+
f"{task_group_activity_id}",
|
99
|
+
),
|
100
|
+
)
|
101
|
+
|
102
|
+
return activity
|
103
|
+
|
104
|
+
|
32
105
|
@router.get("/", response_model=list[TaskGroupReadV2])
|
33
106
|
async def get_task_group_list(
|
34
107
|
user: UserOAuth = Depends(current_active_user),
|
@@ -40,7 +113,6 @@ async def get_task_group_list(
|
|
40
113
|
"""
|
41
114
|
Get all accessible TaskGroups
|
42
115
|
"""
|
43
|
-
|
44
116
|
if only_owner:
|
45
117
|
condition = TaskGroupV2.user_id == user.id
|
46
118
|
else:
|
@@ -112,22 +184,22 @@ async def delete_task_group(
|
|
112
184
|
detail=f"TaskV2 {workflow_tasks[0].task_id} is still in use",
|
113
185
|
)
|
114
186
|
|
115
|
-
# Cascade operations: set foreign-keys to null for
|
116
|
-
# are in relationship with the current TaskGroupV2
|
117
|
-
logger.debug("Start of cascade operations on
|
118
|
-
stm = select(
|
119
|
-
|
187
|
+
# Cascade operations: set foreign-keys to null for TaskGroupActivityV2
|
188
|
+
# which are in relationship with the current TaskGroupV2
|
189
|
+
logger.debug("Start of cascade operations on TaskGroupActivityV2.")
|
190
|
+
stm = select(TaskGroupActivityV2).where(
|
191
|
+
TaskGroupActivityV2.taskgroupv2_id == task_group_id
|
120
192
|
)
|
121
193
|
res = await db.execute(stm)
|
122
|
-
|
123
|
-
for
|
194
|
+
task_group_activity_list = res.scalars().all()
|
195
|
+
for task_group_activity in task_group_activity_list:
|
124
196
|
logger.debug(
|
125
|
-
f"Setting
|
126
|
-
"to None."
|
197
|
+
f"Setting TaskGroupActivityV2[{task_group_activity.id}]"
|
198
|
+
".taskgroupv2_id to None."
|
127
199
|
)
|
128
|
-
|
129
|
-
db.add(
|
130
|
-
logger.debug("End of cascade operations on
|
200
|
+
task_group_activity.taskgroupv2_id = None
|
201
|
+
db.add(task_group_activity)
|
202
|
+
logger.debug("End of cascade operations on TaskGroupActivityV2.")
|
131
203
|
|
132
204
|
await db.delete(task_group)
|
133
205
|
await db.commit()
|