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.
Files changed (82) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/app/db/__init__.py +2 -35
  3. fractal_server/app/models/v2/__init__.py +3 -3
  4. fractal_server/app/models/v2/task.py +0 -72
  5. fractal_server/app/models/v2/task_group.py +113 -0
  6. fractal_server/app/routes/admin/v1.py +13 -30
  7. fractal_server/app/routes/admin/v2/__init__.py +4 -0
  8. fractal_server/app/routes/admin/v2/job.py +13 -24
  9. fractal_server/app/routes/admin/v2/task.py +13 -0
  10. fractal_server/app/routes/admin/v2/task_group.py +75 -14
  11. fractal_server/app/routes/admin/v2/task_group_lifecycle.py +267 -0
  12. fractal_server/app/routes/api/v1/project.py +7 -19
  13. fractal_server/app/routes/api/v2/__init__.py +11 -2
  14. fractal_server/app/routes/api/v2/{_aux_functions_task_collection.py → _aux_functions_task_lifecycle.py} +83 -0
  15. fractal_server/app/routes/api/v2/_aux_functions_tasks.py +27 -17
  16. fractal_server/app/routes/api/v2/submit.py +19 -24
  17. fractal_server/app/routes/api/v2/task_collection.py +33 -65
  18. fractal_server/app/routes/api/v2/task_collection_custom.py +3 -3
  19. fractal_server/app/routes/api/v2/task_group.py +86 -14
  20. fractal_server/app/routes/api/v2/task_group_lifecycle.py +272 -0
  21. fractal_server/app/routes/api/v2/workflow.py +1 -1
  22. fractal_server/app/routes/api/v2/workflow_import.py +2 -2
  23. fractal_server/app/routes/auth/current_user.py +60 -17
  24. fractal_server/app/routes/auth/group.py +67 -39
  25. fractal_server/app/routes/auth/users.py +97 -99
  26. fractal_server/app/routes/aux/__init__.py +20 -0
  27. fractal_server/app/runner/executors/slurm/_slurm_config.py +0 -17
  28. fractal_server/app/runner/executors/slurm/ssh/executor.py +49 -204
  29. fractal_server/app/runner/executors/slurm/sudo/executor.py +26 -109
  30. fractal_server/app/runner/executors/slurm/utils_executors.py +58 -0
  31. fractal_server/app/runner/v2/_local_experimental/executor.py +2 -1
  32. fractal_server/app/schemas/_validators.py +1 -16
  33. fractal_server/app/schemas/user.py +16 -10
  34. fractal_server/app/schemas/user_group.py +0 -11
  35. fractal_server/app/schemas/v1/applyworkflow.py +0 -8
  36. fractal_server/app/schemas/v1/dataset.py +0 -5
  37. fractal_server/app/schemas/v1/project.py +0 -5
  38. fractal_server/app/schemas/v1/state.py +0 -5
  39. fractal_server/app/schemas/v1/workflow.py +0 -5
  40. fractal_server/app/schemas/v2/__init__.py +4 -2
  41. fractal_server/app/schemas/v2/dataset.py +1 -7
  42. fractal_server/app/schemas/v2/job.py +0 -8
  43. fractal_server/app/schemas/v2/project.py +0 -5
  44. fractal_server/app/schemas/v2/task_collection.py +13 -31
  45. fractal_server/app/schemas/v2/task_group.py +59 -8
  46. fractal_server/app/schemas/v2/workflow.py +0 -5
  47. fractal_server/app/security/__init__.py +17 -0
  48. fractal_server/config.py +61 -59
  49. fractal_server/migrations/versions/d256a7379ab8_taskgroup_activity_and_venv_info_to_.py +117 -0
  50. fractal_server/ssh/_fabric.py +156 -83
  51. fractal_server/string_tools.py +10 -3
  52. fractal_server/tasks/utils.py +2 -12
  53. fractal_server/tasks/v2/local/__init__.py +3 -0
  54. fractal_server/tasks/v2/local/_utils.py +70 -0
  55. fractal_server/tasks/v2/local/collect.py +291 -0
  56. fractal_server/tasks/v2/local/deactivate.py +218 -0
  57. fractal_server/tasks/v2/local/reactivate.py +159 -0
  58. fractal_server/tasks/v2/ssh/__init__.py +3 -0
  59. fractal_server/tasks/v2/ssh/_utils.py +87 -0
  60. fractal_server/tasks/v2/ssh/collect.py +311 -0
  61. fractal_server/tasks/v2/ssh/deactivate.py +253 -0
  62. fractal_server/tasks/v2/ssh/reactivate.py +202 -0
  63. fractal_server/tasks/v2/templates/{_2_preliminary_pip_operations.sh → 1_create_venv.sh} +6 -7
  64. fractal_server/tasks/v2/templates/{_3_pip_install.sh → 2_pip_install.sh} +8 -1
  65. fractal_server/tasks/v2/templates/{_4_pip_freeze.sh → 3_pip_freeze.sh} +0 -7
  66. fractal_server/tasks/v2/templates/{_5_pip_show.sh → 4_pip_show.sh} +5 -6
  67. fractal_server/tasks/v2/templates/5_get_venv_size_and_file_number.sh +10 -0
  68. fractal_server/tasks/v2/templates/6_pip_install_from_freeze.sh +35 -0
  69. fractal_server/tasks/v2/utils_background.py +42 -127
  70. fractal_server/tasks/v2/utils_templates.py +32 -2
  71. fractal_server/utils.py +4 -2
  72. fractal_server/zip_tools.py +21 -4
  73. {fractal_server-2.8.0.dist-info → fractal_server-2.9.0.dist-info}/METADATA +3 -5
  74. {fractal_server-2.8.0.dist-info → fractal_server-2.9.0.dist-info}/RECORD +78 -65
  75. fractal_server/app/models/v2/collection_state.py +0 -22
  76. fractal_server/tasks/v2/collection_local.py +0 -357
  77. fractal_server/tasks/v2/collection_ssh.py +0 -352
  78. fractal_server/tasks/v2/templates/_1_create_venv.sh +0 -42
  79. /fractal_server/tasks/v2/{database_operations.py → utils_database.py} +0 -0
  80. {fractal_server-2.8.0.dist-info → fractal_server-2.9.0.dist-info}/LICENSE +0 -0
  81. {fractal_server-2.8.0.dist-info → fractal_server-2.9.0.dist-info}/WHEEL +0 -0
  82. {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=dict(
167
- **dataset.model_dump(
168
- exclude={"images", "history", "timestamp_created"}
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 = get_timestamp().strftime("%Y%m%d_%H%M%S")
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 TaskGroupCreateV2
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 ._aux_functions_task_collection import get_package_version_from_pypi
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.routes.auth import current_active_user
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.collection_local import (
36
- collect_package_local,
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=CollectionStateReadV2,
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
- ) -> CollectionStateReadV2:
65
+ ) -> TaskGroupActivityV2Read:
63
66
  """
64
- Task collection endpoint
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
- TaskGroupCreateV2(**task_group_attrs)
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
- collection_state_data = dict(
231
- status=CollectionStatusV2.PENDING,
232
- package=task_group.pkg_name,
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
- state = CollectionStateV2(
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(state)
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
- collect_package_ssh,
266
- state_id=state.id,
267
- task_group=task_group,
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
- collect_package_local,
276
- state_id=state.id,
277
- task_group=task_group,
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 state"
273
+ "and return task_group_activity"
282
274
  )
283
275
  reset_logger_handlers(logger)
284
- info = (
285
- "Collecting tasks in the background. "
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 CollectionStateV2
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 CollectionStateV2 which
116
- # are in relationship with the current TaskGroupV2
117
- logger.debug("Start of cascade operations on CollectionStateV2.")
118
- stm = select(CollectionStateV2).where(
119
- CollectionStateV2.taskgroupv2_id == task_group_id
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
- collection_states = res.scalars().all()
123
- for collection_state in collection_states:
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 CollectionStateV2[{collection_state.id}].taskgroupv2_id "
126
- "to None."
197
+ f"Setting TaskGroupActivityV2[{task_group_activity.id}]"
198
+ ".taskgroupv2_id to None."
127
199
  )
128
- collection_state.taskgroupv2_id = None
129
- db.add(collection_state)
130
- logger.debug("End of cascade operations on CollectionStateV2.")
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()