fractal-server 2.13.1__py3-none-any.whl → 2.14.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 (119) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/__main__.py +3 -1
  3. fractal_server/app/models/linkusergroup.py +6 -2
  4. fractal_server/app/models/v2/__init__.py +7 -1
  5. fractal_server/app/models/v2/dataset.py +1 -11
  6. fractal_server/app/models/v2/history.py +78 -0
  7. fractal_server/app/models/v2/job.py +10 -3
  8. fractal_server/app/models/v2/task_group.py +2 -2
  9. fractal_server/app/models/v2/workflow.py +1 -1
  10. fractal_server/app/models/v2/workflowtask.py +1 -1
  11. fractal_server/app/routes/admin/v2/accounting.py +18 -28
  12. fractal_server/app/routes/admin/v2/task.py +1 -1
  13. fractal_server/app/routes/admin/v2/task_group.py +0 -17
  14. fractal_server/app/routes/api/__init__.py +1 -1
  15. fractal_server/app/routes/api/v2/__init__.py +8 -2
  16. fractal_server/app/routes/api/v2/_aux_functions.py +66 -0
  17. fractal_server/app/routes/api/v2/_aux_functions_history.py +166 -0
  18. fractal_server/app/routes/api/v2/dataset.py +0 -17
  19. fractal_server/app/routes/api/v2/history.py +544 -0
  20. fractal_server/app/routes/api/v2/images.py +31 -43
  21. fractal_server/app/routes/api/v2/job.py +30 -0
  22. fractal_server/app/routes/api/v2/project.py +1 -53
  23. fractal_server/app/routes/api/v2/{status.py → status_legacy.py} +6 -6
  24. fractal_server/app/routes/api/v2/submit.py +16 -14
  25. fractal_server/app/routes/api/v2/task.py +3 -10
  26. fractal_server/app/routes/api/v2/task_collection_custom.py +4 -9
  27. fractal_server/app/routes/api/v2/task_group.py +0 -17
  28. fractal_server/app/routes/api/v2/verify_image_types.py +61 -0
  29. fractal_server/app/routes/api/v2/workflow.py +28 -69
  30. fractal_server/app/routes/api/v2/workflowtask.py +53 -50
  31. fractal_server/app/routes/auth/group.py +0 -16
  32. fractal_server/app/routes/auth/oauth.py +5 -3
  33. fractal_server/app/routes/pagination.py +47 -0
  34. fractal_server/app/runner/components.py +0 -3
  35. fractal_server/app/runner/compress_folder.py +57 -29
  36. fractal_server/app/runner/exceptions.py +4 -0
  37. fractal_server/app/runner/executors/base_runner.py +157 -0
  38. fractal_server/app/runner/{v2/_local/_local_config.py → executors/local/get_local_config.py} +7 -9
  39. fractal_server/app/runner/executors/local/runner.py +248 -0
  40. fractal_server/app/runner/executors/{slurm → slurm_common}/_batching.py +1 -1
  41. fractal_server/app/runner/executors/{slurm → slurm_common}/_slurm_config.py +9 -7
  42. fractal_server/app/runner/executors/slurm_common/base_slurm_runner.py +868 -0
  43. fractal_server/app/runner/{v2/_slurm_common → executors/slurm_common}/get_slurm_config.py +48 -17
  44. fractal_server/app/runner/executors/{slurm → slurm_common}/remote.py +36 -47
  45. fractal_server/app/runner/executors/slurm_common/slurm_job_task_models.py +134 -0
  46. fractal_server/app/runner/executors/slurm_ssh/runner.py +268 -0
  47. fractal_server/app/runner/executors/slurm_sudo/__init__.py +0 -0
  48. fractal_server/app/runner/executors/{slurm/sudo → slurm_sudo}/_subprocess_run_as_user.py +2 -83
  49. fractal_server/app/runner/executors/slurm_sudo/runner.py +193 -0
  50. fractal_server/app/runner/extract_archive.py +1 -3
  51. fractal_server/app/runner/task_files.py +134 -87
  52. fractal_server/app/runner/v2/__init__.py +0 -399
  53. fractal_server/app/runner/v2/_local.py +88 -0
  54. fractal_server/app/runner/v2/{_slurm_ssh/__init__.py → _slurm_ssh.py} +20 -19
  55. fractal_server/app/runner/v2/{_slurm_sudo/__init__.py → _slurm_sudo.py} +17 -15
  56. fractal_server/app/runner/v2/db_tools.py +119 -0
  57. fractal_server/app/runner/v2/runner.py +206 -95
  58. fractal_server/app/runner/v2/runner_functions.py +488 -187
  59. fractal_server/app/runner/v2/runner_functions_low_level.py +40 -43
  60. fractal_server/app/runner/v2/submit_workflow.py +358 -0
  61. fractal_server/app/runner/v2/task_interface.py +31 -0
  62. fractal_server/app/schemas/_validators.py +13 -24
  63. fractal_server/app/schemas/user.py +10 -7
  64. fractal_server/app/schemas/user_settings.py +9 -21
  65. fractal_server/app/schemas/v2/__init__.py +9 -1
  66. fractal_server/app/schemas/v2/dataset.py +12 -94
  67. fractal_server/app/schemas/v2/dumps.py +26 -9
  68. fractal_server/app/schemas/v2/history.py +80 -0
  69. fractal_server/app/schemas/v2/job.py +15 -8
  70. fractal_server/app/schemas/v2/manifest.py +14 -7
  71. fractal_server/app/schemas/v2/project.py +9 -7
  72. fractal_server/app/schemas/v2/status_legacy.py +35 -0
  73. fractal_server/app/schemas/v2/task.py +72 -77
  74. fractal_server/app/schemas/v2/task_collection.py +14 -32
  75. fractal_server/app/schemas/v2/task_group.py +10 -9
  76. fractal_server/app/schemas/v2/workflow.py +10 -11
  77. fractal_server/app/schemas/v2/workflowtask.py +2 -21
  78. fractal_server/app/security/__init__.py +3 -3
  79. fractal_server/app/security/signup_email.py +2 -2
  80. fractal_server/config.py +41 -46
  81. fractal_server/images/tools.py +23 -0
  82. fractal_server/migrations/versions/47351f8c7ebc_drop_dataset_filters.py +50 -0
  83. fractal_server/migrations/versions/9db60297b8b2_set_ondelete.py +250 -0
  84. fractal_server/migrations/versions/c90a7c76e996_job_id_in_history_run.py +41 -0
  85. fractal_server/migrations/versions/e81103413827_add_job_type_filters.py +36 -0
  86. fractal_server/migrations/versions/f37aceb45062_make_historyunit_logfile_required.py +39 -0
  87. fractal_server/migrations/versions/fbce16ff4e47_new_history_items.py +120 -0
  88. fractal_server/ssh/_fabric.py +28 -14
  89. fractal_server/tasks/v2/local/collect.py +2 -2
  90. fractal_server/tasks/v2/ssh/collect.py +2 -2
  91. fractal_server/tasks/v2/templates/2_pip_install.sh +1 -1
  92. fractal_server/tasks/v2/templates/4_pip_show.sh +1 -1
  93. fractal_server/tasks/v2/utils_background.py +0 -19
  94. fractal_server/tasks/v2/utils_database.py +30 -17
  95. fractal_server/tasks/v2/utils_templates.py +6 -0
  96. {fractal_server-2.13.1.dist-info → fractal_server-2.14.0.dist-info}/METADATA +4 -4
  97. {fractal_server-2.13.1.dist-info → fractal_server-2.14.0.dist-info}/RECORD +106 -96
  98. {fractal_server-2.13.1.dist-info → fractal_server-2.14.0.dist-info}/WHEEL +1 -1
  99. fractal_server/app/runner/executors/slurm/ssh/_executor_wait_thread.py +0 -126
  100. fractal_server/app/runner/executors/slurm/ssh/_slurm_job.py +0 -116
  101. fractal_server/app/runner/executors/slurm/ssh/executor.py +0 -1386
  102. fractal_server/app/runner/executors/slurm/sudo/_check_jobs_status.py +0 -71
  103. fractal_server/app/runner/executors/slurm/sudo/_executor_wait_thread.py +0 -130
  104. fractal_server/app/runner/executors/slurm/sudo/executor.py +0 -1281
  105. fractal_server/app/runner/v2/_local/__init__.py +0 -132
  106. fractal_server/app/runner/v2/_local/_submit_setup.py +0 -52
  107. fractal_server/app/runner/v2/_local/executor.py +0 -100
  108. fractal_server/app/runner/v2/_slurm_ssh/_submit_setup.py +0 -83
  109. fractal_server/app/runner/v2/_slurm_sudo/_submit_setup.py +0 -83
  110. fractal_server/app/runner/v2/handle_failed_job.py +0 -59
  111. fractal_server/app/schemas/v2/status.py +0 -16
  112. /fractal_server/app/{runner/executors/slurm → history}/__init__.py +0 -0
  113. /fractal_server/app/runner/executors/{slurm/ssh → local}/__init__.py +0 -0
  114. /fractal_server/app/runner/executors/{slurm/sudo → slurm_common}/__init__.py +0 -0
  115. /fractal_server/app/runner/executors/{_job_states.py → slurm_common/_job_states.py} +0 -0
  116. /fractal_server/app/runner/executors/{slurm → slurm_common}/utils_executors.py +0 -0
  117. /fractal_server/app/runner/{v2/_slurm_common → executors/slurm_ssh}/__init__.py +0 -0
  118. {fractal_server-2.13.1.dist-info → fractal_server-2.14.0.dist-info}/LICENSE +0 -0
  119. {fractal_server-2.13.1.dist-info → fractal_server-2.14.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,120 @@
1
+ """new history items
2
+
3
+ Revision ID: fbce16ff4e47
4
+ Revises: af1ef1c83c9b
5
+ Create Date: 2025-03-14 15:25:01.083619
6
+
7
+ """
8
+ import sqlalchemy as sa
9
+ import sqlmodel
10
+ from alembic import op
11
+ from sqlalchemy.dialects import postgresql
12
+
13
+ # revision identifiers, used by Alembic.
14
+ revision = "fbce16ff4e47"
15
+ down_revision = "af1ef1c83c9b"
16
+ branch_labels = None
17
+ depends_on = None
18
+
19
+
20
+ def upgrade() -> None:
21
+ # ### commands auto generated by Alembic - please adjust! ###
22
+ op.create_table(
23
+ "historyrun",
24
+ sa.Column("id", sa.Integer(), nullable=False),
25
+ sa.Column("dataset_id", sa.Integer(), nullable=False),
26
+ sa.Column("workflowtask_id", sa.Integer(), nullable=True),
27
+ sa.Column(
28
+ "workflowtask_dump",
29
+ postgresql.JSONB(astext_type=sa.Text()),
30
+ nullable=False,
31
+ ),
32
+ sa.Column(
33
+ "task_group_dump",
34
+ postgresql.JSONB(astext_type=sa.Text()),
35
+ nullable=False,
36
+ ),
37
+ sa.Column(
38
+ "timestamp_started", sa.DateTime(timezone=True), nullable=False
39
+ ),
40
+ sa.Column(
41
+ "status", sqlmodel.sql.sqltypes.AutoString(), nullable=False
42
+ ),
43
+ sa.Column("num_available_images", sa.Integer(), nullable=False),
44
+ sa.ForeignKeyConstraint(
45
+ ["dataset_id"],
46
+ ["datasetv2.id"],
47
+ name=op.f("fk_historyrun_dataset_id_datasetv2"),
48
+ ondelete="CASCADE",
49
+ ),
50
+ sa.ForeignKeyConstraint(
51
+ ["workflowtask_id"],
52
+ ["workflowtaskv2.id"],
53
+ name=op.f("fk_historyrun_workflowtask_id_workflowtaskv2"),
54
+ ondelete="SET NULL",
55
+ ),
56
+ sa.PrimaryKeyConstraint("id", name=op.f("pk_historyrun")),
57
+ )
58
+ op.create_table(
59
+ "historyunit",
60
+ sa.Column("id", sa.Integer(), nullable=False),
61
+ sa.Column("history_run_id", sa.Integer(), nullable=False),
62
+ sa.Column(
63
+ "logfile", sqlmodel.sql.sqltypes.AutoString(), nullable=True
64
+ ),
65
+ sa.Column(
66
+ "status", sqlmodel.sql.sqltypes.AutoString(), nullable=False
67
+ ),
68
+ sa.Column("zarr_urls", postgresql.ARRAY(sa.String()), nullable=True),
69
+ sa.ForeignKeyConstraint(
70
+ ["history_run_id"],
71
+ ["historyrun.id"],
72
+ name=op.f("fk_historyunit_history_run_id_historyrun"),
73
+ ondelete="CASCADE",
74
+ ),
75
+ sa.PrimaryKeyConstraint("id", name=op.f("pk_historyunit")),
76
+ )
77
+ op.create_table(
78
+ "historyimagecache",
79
+ sa.Column(
80
+ "zarr_url", sqlmodel.sql.sqltypes.AutoString(), nullable=False
81
+ ),
82
+ sa.Column("dataset_id", sa.Integer(), nullable=False),
83
+ sa.Column("workflowtask_id", sa.Integer(), nullable=False),
84
+ sa.Column("latest_history_unit_id", sa.Integer(), nullable=False),
85
+ sa.ForeignKeyConstraint(
86
+ ["dataset_id"],
87
+ ["datasetv2.id"],
88
+ name=op.f("fk_historyimagecache_dataset_id_datasetv2"),
89
+ ondelete="CASCADE",
90
+ ),
91
+ sa.ForeignKeyConstraint(
92
+ ["latest_history_unit_id"],
93
+ ["historyunit.id"],
94
+ name=op.f(
95
+ "fk_historyimagecache_latest_history_unit_id_historyunit"
96
+ ),
97
+ ondelete="CASCADE",
98
+ ),
99
+ sa.ForeignKeyConstraint(
100
+ ["workflowtask_id"],
101
+ ["workflowtaskv2.id"],
102
+ name=op.f("fk_historyimagecache_workflowtask_id_workflowtaskv2"),
103
+ ondelete="CASCADE",
104
+ ),
105
+ sa.PrimaryKeyConstraint(
106
+ "zarr_url",
107
+ "dataset_id",
108
+ "workflowtask_id",
109
+ name=op.f("pk_historyimagecache"),
110
+ ),
111
+ )
112
+ # ### end Alembic commands ###
113
+
114
+
115
+ def downgrade() -> None:
116
+ # ### commands auto generated by Alembic - please adjust! ###
117
+ op.drop_table("historyimagecache")
118
+ op.drop_table("historyunit")
119
+ op.drop_table("historyrun")
120
+ # ### end Alembic commands ###
@@ -23,6 +23,18 @@ class FractalSSHTimeoutError(RuntimeError):
23
23
  pass
24
24
 
25
25
 
26
+ class FractalSSHConnectionError(RuntimeError):
27
+ pass
28
+
29
+
30
+ class FractalSSHCommandError(RuntimeError):
31
+ pass
32
+
33
+
34
+ class FractalSSHUnknownError(RuntimeError):
35
+ pass
36
+
37
+
26
38
  logger = set_logger(__name__)
27
39
 
28
40
 
@@ -170,7 +182,6 @@ class FractalSSH(object):
170
182
  label="read_remote_json_file",
171
183
  timeout=self.default_lock_timeout,
172
184
  ):
173
-
174
185
  try:
175
186
  with self._sftp_unsafe().open(filepath, "r") as f:
176
187
  data = json.load(f)
@@ -263,7 +274,7 @@ class FractalSSH(object):
263
274
  cmd: str,
264
275
  allow_char: Optional[str] = None,
265
276
  max_attempts: Optional[int] = None,
266
- base_interval: Optional[int] = None,
277
+ base_interval: Optional[float] = None,
267
278
  lock_timeout: Optional[int] = None,
268
279
  ) -> str:
269
280
  """
@@ -311,7 +322,7 @@ class FractalSSH(object):
311
322
  t_1 = time.perf_counter()
312
323
  self.logger.info(
313
324
  f"{prefix} END running '{cmd}' over SSH, "
314
- f"elapsed {t_1-t_0:.3f}"
325
+ f"elapsed={t_1 - t_0:.3f}"
315
326
  )
316
327
  self.logger.debug("STDOUT:")
317
328
  self.logger.debug(res.stdout)
@@ -329,12 +340,16 @@ class FractalSSH(object):
329
340
  sleeptime = actual_base_interval**ind_attempt
330
341
  self.logger.warning(
331
342
  f"{prefix} Now sleep {sleeptime:.3f} "
332
- "seconds and continue."
343
+ "seconds and retry."
333
344
  )
334
345
  time.sleep(sleeptime)
335
346
  else:
336
347
  self.logger.error(f"{prefix} Reached last attempt")
337
- break
348
+ raise FractalSSHConnectionError(
349
+ f"Reached last attempt "
350
+ f"({max_attempts=}) for running "
351
+ f"'{cmd}' over SSH"
352
+ )
338
353
  except UnexpectedExit as e:
339
354
  # Case 3: Command fails with an actual error
340
355
  error_msg = (
@@ -342,18 +357,15 @@ class FractalSSH(object):
342
357
  f"Original error:\n{str(e)}."
343
358
  )
344
359
  self.logger.error(error_msg)
345
- raise RuntimeError(error_msg)
360
+ raise FractalSSHCommandError(error_msg)
361
+ except FractalSSHTimeoutError as e:
362
+ raise e
346
363
  except Exception as e:
347
364
  self.logger.error(
348
365
  f"Running command `{cmd}` over SSH failed.\n"
349
366
  f"Original Error:\n{str(e)}."
350
367
  )
351
- raise e
352
-
353
- raise RuntimeError(
354
- f"Reached last attempt ({max_attempts=}) for running "
355
- f"'{cmd}' over SSH"
356
- )
368
+ raise FractalSSHUnknownError(f"{type(e)}: {str(e)}")
357
369
 
358
370
  def send_file(
359
371
  self,
@@ -501,7 +513,8 @@ class FractalSSH(object):
501
513
  content: Contents to be written to file.
502
514
  lock_timeout: Timeout for lock acquisition (overrides default).
503
515
  """
504
- self.logger.info(f"START writing to remote file {path}.")
516
+ t_start = time.perf_counter()
517
+ self.logger.info(f"[write_remote_file] START ({path}).")
505
518
  actual_lock_timeout = self.default_lock_timeout
506
519
  if lock_timeout is not None:
507
520
  actual_lock_timeout = lock_timeout
@@ -518,7 +531,8 @@ class FractalSSH(object):
518
531
  e=e, message=f"Error in `write_remote_file`, for {path=}."
519
532
  )
520
533
 
521
- self.logger.info(f"END writing to remote file {path}.")
534
+ elapsed = time.perf_counter() - t_start
535
+ self.logger.info(f"[write_remote_file] END, {elapsed=} s ({path}).")
522
536
 
523
537
  def remote_exists(self, path: str) -> bool:
524
538
  """
@@ -6,7 +6,7 @@ from pathlib import Path
6
6
  from tempfile import TemporaryDirectory
7
7
  from typing import Optional
8
8
 
9
- from ..utils_database import create_db_tasks_and_update_task_group
9
+ from ..utils_database import create_db_tasks_and_update_task_group_sync
10
10
  from ._utils import _customize_and_run_template
11
11
  from fractal_server.app.db import get_sync_db
12
12
  from fractal_server.app.models.v2 import TaskGroupActivityV2
@@ -233,7 +233,7 @@ def collect_local(
233
233
  activity = add_commit_refresh(obj=activity, db=db)
234
234
 
235
235
  logger.info("create_db_tasks_and_update_task_group - " "start")
236
- create_db_tasks_and_update_task_group(
236
+ create_db_tasks_and_update_task_group_sync(
237
237
  task_list=task_list,
238
238
  task_group_id=task_group.id,
239
239
  db=db,
@@ -6,7 +6,7 @@ from typing import Optional
6
6
 
7
7
  from ..utils_background import _prepare_tasks_metadata
8
8
  from ..utils_background import fail_and_cleanup
9
- from ..utils_database import create_db_tasks_and_update_task_group
9
+ from ..utils_database import create_db_tasks_and_update_task_group_sync
10
10
  from fractal_server.app.db import get_sync_db
11
11
  from fractal_server.app.models.v2 import TaskGroupActivityV2
12
12
  from fractal_server.app.models.v2 import TaskGroupV2
@@ -265,7 +265,7 @@ def collect_ssh(
265
265
  logger.info("_prepare_tasks_metadata - end")
266
266
 
267
267
  logger.info("create_db_tasks_and_update_task_group - " "start")
268
- create_db_tasks_and_update_task_group(
268
+ create_db_tasks_and_update_task_group_sync(
269
269
  task_list=task_list,
270
270
  task_group_id=task_group.id,
271
271
  db=db,
@@ -25,7 +25,7 @@ echo
25
25
 
26
26
  # Install package
27
27
  write_log "START install ${INSTALL_STRING}"
28
- "$VENVPYTHON" -m pip install --no-cache-dir "$INSTALL_STRING"
28
+ "$VENVPYTHON" -m pip install ${FRACTAL_PIP_CACHE_DIR_ARG} "$INSTALL_STRING"
29
29
  write_log "END install ${INSTALL_STRING}"
30
30
  echo
31
31
 
@@ -20,7 +20,7 @@ echo
20
20
  # FIXME: only run pip-show once!
21
21
 
22
22
  # Extract information about paths
23
- # WARNING: this block will fail for paths which inlcude whitespace characters
23
+ # WARNING: this block will fail for paths which include whitespace characters
24
24
  write_log "START pip show"
25
25
  $VENVPYTHON -m pip show ${PACKAGE_NAME}
26
26
  write_log "END pip show"
@@ -3,7 +3,6 @@ from typing import Optional
3
3
  from typing import TypeVar
4
4
 
5
5
  from sqlalchemy.orm import Session as DBSyncSession
6
- from sqlmodel import select
7
6
 
8
7
  from fractal_server.app.models.v2 import TaskGroupActivityV2
9
8
  from fractal_server.app.models.v2 import TaskGroupV2
@@ -44,24 +43,6 @@ def fail_and_cleanup(
44
43
  task_group_activity.log = get_current_log(log_file_path)
45
44
  task_group_activity = add_commit_refresh(obj=task_group_activity, db=db)
46
45
  if task_group_activity.action == TaskGroupActivityActionV2.COLLECT:
47
- logger.info(f"Now delete TaskGroupV2 with {task_group.id=}")
48
-
49
- logger.info("Start of TaskGroupActivityV2 cascade operations.")
50
- stm = select(TaskGroupActivityV2).where(
51
- TaskGroupActivityV2.taskgroupv2_id == task_group.id
52
- )
53
- res = db.execute(stm)
54
- task_group_activity_list = res.scalars().all()
55
- for task_group_activity in task_group_activity_list:
56
- logger.info(
57
- f"Setting TaskGroupActivityV2[{task_group_activity.id}]"
58
- ".taskgroupv2_id to None."
59
- )
60
- task_group_activity.taskgroupv2_id = None
61
- db.add(task_group_activity)
62
- logger.info("End of TaskGroupActivityV2 cascade operations.")
63
- logger.info(f"TaskGroupV2 with {task_group.id=} deleted")
64
-
65
46
  db.delete(task_group)
66
47
  db.commit()
67
48
  reset_logger_handlers(logger)
@@ -1,3 +1,4 @@
1
+ from sqlalchemy.ext.asyncio import AsyncSession
1
2
  from sqlalchemy.orm import Session as DBSyncSession
2
3
 
3
4
  from fractal_server.app.models.v2 import TaskGroupV2
@@ -5,16 +6,7 @@ from fractal_server.app.models.v2 import TaskV2
5
6
  from fractal_server.app.schemas.v2 import TaskCreateV2
6
7
 
7
8
 
8
- def _get_task_type(task: TaskCreateV2) -> str:
9
- if task.command_non_parallel is None:
10
- return "parallel"
11
- elif task.command_parallel is None:
12
- return "non_parallel"
13
- else:
14
- return "compound"
15
-
16
-
17
- def create_db_tasks_and_update_task_group(
9
+ def create_db_tasks_and_update_task_group_sync(
18
10
  *,
19
11
  task_group_id: int,
20
12
  task_list: list[TaskCreateV2],
@@ -31,13 +23,7 @@ def create_db_tasks_and_update_task_group(
31
23
  Returns:
32
24
  Updated `TaskGroupV2` object.
33
25
  """
34
- actual_task_list = [
35
- TaskV2(
36
- **task.model_dump(),
37
- type=_get_task_type(task),
38
- )
39
- for task in task_list
40
- ]
26
+ actual_task_list = [TaskV2(**task.model_dump()) for task in task_list]
41
27
  task_group = db.get(TaskGroupV2, task_group_id)
42
28
  task_group.task_list = actual_task_list
43
29
  db.add(task_group)
@@ -45,3 +31,30 @@ def create_db_tasks_and_update_task_group(
45
31
  db.refresh(task_group)
46
32
 
47
33
  return task_group
34
+
35
+
36
+ async def create_db_tasks_and_update_task_group_async(
37
+ *,
38
+ task_group_id: int,
39
+ task_list: list[TaskCreateV2],
40
+ db: AsyncSession,
41
+ ) -> TaskGroupV2:
42
+ """
43
+ Create a `TaskGroupV2` with N `TaskV2`s, and insert them into the database.
44
+
45
+ Arguments:
46
+ task_group_id: ID of an existing `TaskGroupV2` object.
47
+ task_list: List of `TaskCreateV2` objects to be inserted into the db.
48
+ db: Synchronous database session
49
+
50
+ Returns:
51
+ Updated `TaskGroupV2` object.
52
+ """
53
+ actual_task_list = [TaskV2(**task.model_dump()) for task in task_list]
54
+ task_group = await db.get(TaskGroupV2, task_group_id)
55
+ task_group.task_list = actual_task_list
56
+ db.add(task_group)
57
+ await db.commit()
58
+ await db.refresh(task_group)
59
+
60
+ return task_group
@@ -2,12 +2,15 @@ from pathlib import Path
2
2
 
3
3
  from fractal_server.app.models.v2 import TaskGroupV2
4
4
  from fractal_server.config import get_settings
5
+ from fractal_server.logger import set_logger
5
6
  from fractal_server.syringe import Inject
6
7
 
7
8
  TEMPLATES_DIR = Path(__file__).parent / "templates"
8
9
 
9
10
  SCRIPTS_SUBFOLDER = "scripts"
10
11
 
12
+ logger = set_logger(__name__)
13
+
11
14
 
12
15
  def customize_template(
13
16
  *,
@@ -87,4 +90,7 @@ def get_collection_replacements(
87
90
  task_group.pinned_package_versions_string,
88
91
  ),
89
92
  ]
93
+ logger.info(
94
+ f"Cache-dir argument for `pip install`: {settings.PIP_CACHE_DIR_ARG}"
95
+ )
90
96
  return replacements
@@ -1,8 +1,7 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: fractal-server
3
- Version: 2.13.1
3
+ Version: 2.14.0
4
4
  Summary: Backend component of the Fractal analytics platform
5
- Home-page: https://github.com/fractal-analytics-platform/fractal-server
6
5
  License: BSD-3-Clause
7
6
  Author: Tommaso Comparin
8
7
  Author-email: tommaso.comparin@exact-lab.it
@@ -21,14 +20,15 @@ Requires-Dist: fastapi-users[oauth] (>=14,<15)
21
20
  Requires-Dist: gunicorn (>=23.0,<24.0)
22
21
  Requires-Dist: packaging (>=24.0.0,<25.0.0)
23
22
  Requires-Dist: psycopg[binary] (>=3.1.0,<4.0.0)
24
- Requires-Dist: pydantic (>=2.10.0,<2.11.0)
23
+ Requires-Dist: pydantic (>=2.11.0,<2.12.0)
25
24
  Requires-Dist: pydantic-settings (>=2.7.0)
26
25
  Requires-Dist: python-dotenv (>=1.0.0,<1.1.0)
27
26
  Requires-Dist: sqlalchemy[asyncio] (>=2.0.23,<2.1)
28
- Requires-Dist: sqlmodel (==0.0.22)
27
+ Requires-Dist: sqlmodel (==0.0.24)
29
28
  Requires-Dist: uvicorn (>=0.29.0,<0.35.0)
30
29
  Requires-Dist: uvicorn-worker (==0.3.0)
31
30
  Project-URL: Documentation, https://fractal-analytics-platform.github.io/fractal-server
31
+ Project-URL: Homepage, https://github.com/fractal-analytics-platform/fractal-server
32
32
  Project-URL: Repository, https://github.com/fractal-analytics-platform/fractal-server
33
33
  Project-URL: changelog, https://github.com/fractal-analytics-platform/fractal-server/blob/main/CHANGELOG.md
34
34
  Description-Content-Type: text/markdown