fractal-server 2.9.0a0__tar.gz → 2.9.0a2__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 (244) hide show
  1. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/PKG-INFO +1 -2
  2. fractal_server-2.9.0a2/fractal_server/__init__.py +1 -0
  3. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/db/__init__.py +2 -35
  4. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v2/task_group.py +4 -0
  5. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/_aux_functions_tasks.py +16 -13
  6. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/submit.py +17 -3
  7. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/task_collection.py +4 -4
  8. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/aux/_timestamp.py +0 -7
  9. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/_validators.py +1 -2
  10. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/task_group.py +2 -2
  11. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/config.py +11 -56
  12. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/3082479ac4ea_taskgroup_activity_and_venv_info_to_.py +8 -0
  13. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/ssh/_fabric.py +49 -16
  14. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/local/__init__.py +1 -1
  15. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/local/collect.py +1 -1
  16. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/local/deactivate.py +71 -23
  17. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/local/utils_local.py +2 -9
  18. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/ssh/collect.py +2 -3
  19. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/pyproject.toml +2 -3
  20. fractal_server-2.9.0a0/fractal_server/__init__.py +0 -1
  21. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/LICENSE +0 -0
  22. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/README.md +0 -0
  23. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/__main__.py +0 -0
  24. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/alembic.ini +0 -0
  25. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/__init__.py +0 -0
  26. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/__init__.py +0 -0
  27. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/linkusergroup.py +0 -0
  28. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/linkuserproject.py +0 -0
  29. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/security.py +0 -0
  30. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/user_settings.py +0 -0
  31. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v1/__init__.py +0 -0
  32. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v1/dataset.py +0 -0
  33. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v1/job.py +0 -0
  34. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v1/project.py +0 -0
  35. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v1/state.py +0 -0
  36. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v1/task.py +0 -0
  37. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v1/workflow.py +0 -0
  38. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v2/__init__.py +0 -0
  39. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v2/dataset.py +0 -0
  40. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v2/job.py +0 -0
  41. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v2/project.py +0 -0
  42. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v2/task.py +0 -0
  43. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v2/workflow.py +0 -0
  44. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v2/workflowtask.py +0 -0
  45. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/__init__.py +0 -0
  46. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/admin/__init__.py +0 -0
  47. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/admin/v1.py +0 -0
  48. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/admin/v2/__init__.py +0 -0
  49. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/admin/v2/job.py +0 -0
  50. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/admin/v2/project.py +0 -0
  51. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/admin/v2/task.py +0 -0
  52. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/admin/v2/task_group.py +0 -0
  53. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/__init__.py +0 -0
  54. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v1/__init__.py +0 -0
  55. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v1/_aux_functions.py +0 -0
  56. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v1/dataset.py +0 -0
  57. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v1/job.py +0 -0
  58. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v1/project.py +0 -0
  59. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v1/task.py +0 -0
  60. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v1/task_collection.py +0 -0
  61. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v1/workflow.py +0 -0
  62. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v1/workflowtask.py +0 -0
  63. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/__init__.py +0 -0
  64. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/_aux_functions.py +0 -0
  65. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +0 -0
  66. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/dataset.py +0 -0
  67. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/images.py +0 -0
  68. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/job.py +0 -0
  69. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/project.py +0 -0
  70. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/status.py +0 -0
  71. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/task.py +0 -0
  72. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/task_collection_custom.py +0 -0
  73. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/task_group.py +0 -0
  74. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/task_group_lifecycle.py +0 -0
  75. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/workflow.py +0 -0
  76. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/workflow_import.py +0 -0
  77. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/workflowtask.py +0 -0
  78. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/auth/__init__.py +0 -0
  79. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/auth/_aux_auth.py +0 -0
  80. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/auth/current_user.py +0 -0
  81. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/auth/group.py +0 -0
  82. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/auth/login.py +0 -0
  83. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/auth/oauth.py +0 -0
  84. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/auth/register.py +0 -0
  85. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/auth/router.py +0 -0
  86. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/auth/users.py +0 -0
  87. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/aux/__init__.py +0 -0
  88. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/aux/_job.py +0 -0
  89. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/aux/_runner.py +0 -0
  90. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/aux/validate_user_settings.py +0 -0
  91. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/.gitignore +0 -0
  92. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/__init__.py +0 -0
  93. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/async_wrap.py +0 -0
  94. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/components.py +0 -0
  95. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/compress_folder.py +0 -0
  96. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/exceptions.py +0 -0
  97. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/__init__.py +0 -0
  98. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/__init__.py +0 -0
  99. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/_batching.py +0 -0
  100. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/_slurm_config.py +0 -0
  101. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/remote.py +0 -0
  102. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/ssh/__init__.py +0 -0
  103. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/ssh/_executor_wait_thread.py +0 -0
  104. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/ssh/_slurm_job.py +0 -0
  105. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/ssh/executor.py +0 -0
  106. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/sudo/__init__.py +0 -0
  107. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/sudo/_check_jobs_status.py +0 -0
  108. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/sudo/_executor_wait_thread.py +0 -0
  109. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/sudo/_subprocess_run_as_user.py +0 -0
  110. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/sudo/executor.py +0 -0
  111. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/extract_archive.py +0 -0
  112. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/filenames.py +0 -0
  113. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/run_subprocess.py +0 -0
  114. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/set_start_and_last_task_index.py +0 -0
  115. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/shutdown.py +0 -0
  116. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/task_files.py +0 -0
  117. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v1/__init__.py +0 -0
  118. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v1/_common.py +0 -0
  119. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v1/_local/__init__.py +0 -0
  120. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v1/_local/_local_config.py +0 -0
  121. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v1/_local/_submit_setup.py +0 -0
  122. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v1/_local/executor.py +0 -0
  123. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v1/_slurm/__init__.py +0 -0
  124. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v1/_slurm/_submit_setup.py +0 -0
  125. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v1/_slurm/get_slurm_config.py +0 -0
  126. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v1/common.py +0 -0
  127. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v1/handle_failed_job.py +0 -0
  128. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/__init__.py +0 -0
  129. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_local/__init__.py +0 -0
  130. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_local/_local_config.py +0 -0
  131. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_local/_submit_setup.py +0 -0
  132. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_local/executor.py +0 -0
  133. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_local_experimental/__init__.py +0 -0
  134. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_local_experimental/_local_config.py +0 -0
  135. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_local_experimental/_submit_setup.py +0 -0
  136. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_local_experimental/executor.py +0 -0
  137. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_slurm_common/__init__.py +0 -0
  138. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_slurm_common/get_slurm_config.py +0 -0
  139. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_slurm_ssh/__init__.py +0 -0
  140. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_slurm_ssh/_submit_setup.py +0 -0
  141. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_slurm_sudo/__init__.py +0 -0
  142. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_slurm_sudo/_submit_setup.py +0 -0
  143. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/deduplicate_list.py +0 -0
  144. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/handle_failed_job.py +0 -0
  145. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/merge_outputs.py +0 -0
  146. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/runner.py +0 -0
  147. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/runner_functions.py +0 -0
  148. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/runner_functions_low_level.py +0 -0
  149. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/task_interface.py +0 -0
  150. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/versions.py +0 -0
  151. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/__init__.py +0 -0
  152. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/user.py +0 -0
  153. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/user_group.py +0 -0
  154. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/user_settings.py +0 -0
  155. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v1/__init__.py +0 -0
  156. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v1/applyworkflow.py +0 -0
  157. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v1/dataset.py +0 -0
  158. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v1/dumps.py +0 -0
  159. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v1/manifest.py +0 -0
  160. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v1/project.py +0 -0
  161. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v1/state.py +0 -0
  162. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v1/task.py +0 -0
  163. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v1/task_collection.py +0 -0
  164. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v1/workflow.py +0 -0
  165. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/__init__.py +0 -0
  166. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/dataset.py +0 -0
  167. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/dumps.py +0 -0
  168. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/job.py +0 -0
  169. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/manifest.py +0 -0
  170. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/project.py +0 -0
  171. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/status.py +0 -0
  172. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/task.py +0 -0
  173. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/task_collection.py +0 -0
  174. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/workflow.py +0 -0
  175. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/workflowtask.py +0 -0
  176. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/security/__init__.py +0 -0
  177. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/user_settings.py +0 -0
  178. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/data_migrations/README.md +0 -0
  179. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/data_migrations/tools.py +0 -0
  180. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/gunicorn_fractal.py +0 -0
  181. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/images/__init__.py +0 -0
  182. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/images/models.py +0 -0
  183. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/images/tools.py +0 -0
  184. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/logger.py +0 -0
  185. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/main.py +0 -0
  186. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/README +0 -0
  187. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/env.py +0 -0
  188. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/naming_convention.py +0 -0
  189. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/script.py.mako +0 -0
  190. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/034a469ec2eb_task_groups.py +0 -0
  191. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/091b01f51f88_add_usergroup_and_linkusergroup_table.py +0 -0
  192. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/19eca0dd47a9_user_settings_project_dir.py +0 -0
  193. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py +0 -0
  194. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/4cedeb448a53_workflowtask_foreign_keys_not_nullables.py +0 -0
  195. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/501961cfcd85_remove_link_between_v1_and_v2_tasks_.py +0 -0
  196. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/50a13d6138fd_initial_schema.py +0 -0
  197. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/5bf02391cfef_v2.py +0 -0
  198. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/70e77f1c38b0_add_applyworkflow_first_task_index_and_.py +0 -0
  199. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/71eefd1dd202_add_slurm_accounts.py +0 -0
  200. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/84bf0fffde30_add_dumps_to_applyworkflow.py +0 -0
  201. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/8e8f227a3e36_update_taskv2_post_2_7_0.py +0 -0
  202. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/8f79bd162e35_add_docs_info_and_docs_link_to_task_.py +0 -0
  203. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/94a47ea2d3ff_remove_cache_dir_slurm_user_and_slurm_.py +0 -0
  204. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/97f444d47249_add_applyworkflow_project_dump.py +0 -0
  205. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/99ea79d9e5d2_add_dataset_history.py +0 -0
  206. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/9c5ae74c9b98_add_user_settings_table.py +0 -0
  207. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/9fd26a2b0de4_add_workflow_timestamp_created.py +0 -0
  208. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/a7f4d6137b53_add_workflow_dump_to_applyworkflow.py +0 -0
  209. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/d4fe3708d309_make_applyworkflow_workflow_dump_non_.py +0 -0
  210. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/da2cb2ac4255_user_group_viewer_paths.py +0 -0
  211. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/e75cac726012_make_applyworkflow_start_timestamp_not_.py +0 -0
  212. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/efa89c30e0a4_add_project_timestamp_created.py +0 -0
  213. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/f384e1c0cf5d_drop_task_default_args_columns.py +0 -0
  214. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/py.typed +0 -0
  215. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/ssh/__init__.py +0 -0
  216. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/string_tools.py +0 -0
  217. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/syringe.py +0 -0
  218. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/__init__.py +0 -0
  219. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/utils.py +0 -0
  220. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v1/_TaskCollectPip.py +0 -0
  221. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v1/__init__.py +0 -0
  222. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v1/background_operations.py +0 -0
  223. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v1/endpoint_operations.py +0 -0
  224. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v1/get_collection_data.py +0 -0
  225. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v1/utils.py +0 -0
  226. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/__init__.py +0 -0
  227. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/local/reactivate.py +0 -0
  228. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/ssh/__init__.py +0 -0
  229. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/ssh/deactivate.py +0 -0
  230. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/ssh/reactivate.py +0 -0
  231. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/templates/1_create_venv.sh +0 -0
  232. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/templates/2_pip_install.sh +0 -0
  233. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/templates/3_pip_freeze.sh +0 -0
  234. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/templates/4_pip_show.sh +0 -0
  235. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/templates/5_get_venv_size_and_file_number.sh +0 -0
  236. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/templates/6_pip_install_from_freeze.sh +0 -0
  237. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/utils_background.py +0 -0
  238. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/utils_database.py +0 -0
  239. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/utils_package_names.py +0 -0
  240. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/utils_python_interpreter.py +0 -0
  241. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/utils_templates.py +0 -0
  242. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/urls.py +0 -0
  243. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/utils.py +0 -0
  244. {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/zip_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fractal-server
3
- Version: 2.9.0a0
3
+ Version: 2.9.0a2
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
@@ -12,7 +12,6 @@ Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Programming Language :: Python :: 3.10
13
13
  Classifier: Programming Language :: Python :: 3.11
14
14
  Classifier: Programming Language :: Python :: 3.12
15
- Requires-Dist: aiosqlite (>=0.19.0,<0.20.0)
16
15
  Requires-Dist: alembic (>=1.13.1,<2.0.0)
17
16
  Requires-Dist: bcrypt (==4.0.1)
18
17
  Requires-Dist: cloudpickle (>=3.0.0,<3.1.0)
@@ -0,0 +1 @@
1
+ __VERSION__ = "2.9.0a2"
@@ -2,17 +2,14 @@
2
2
  `db` module, loosely adapted from
3
3
  https://testdriven.io/blog/fastapi-sqlmodel/#async-sqlmodel
4
4
  """
5
- import sqlite3
6
5
  from typing import AsyncGenerator
7
6
  from typing import Generator
8
7
 
9
8
  from sqlalchemy import create_engine
10
- from sqlalchemy import event
11
9
  from sqlalchemy.ext.asyncio import AsyncSession
12
10
  from sqlalchemy.ext.asyncio import create_async_engine
13
11
  from sqlalchemy.orm import Session as DBSyncSession
14
12
  from sqlalchemy.orm import sessionmaker
15
- from sqlalchemy.pool import StaticPool
16
13
 
17
14
  from ...config import get_settings
18
15
  from ...logger import set_logger
@@ -21,14 +18,6 @@ from ...syringe import Inject
21
18
 
22
19
  logger = set_logger(__name__)
23
20
 
24
- SQLITE_WARNING_MESSAGE = (
25
- "SQLite is supported (supported version >=3.37, "
26
- f"current {sqlite3.sqlite_version=}) "
27
- "but discouraged in production. "
28
- "Given its partial support for ForeignKey constraints, "
29
- "database consistency cannot be guaranteed."
30
- )
31
-
32
21
 
33
22
  class DB:
34
23
  """
@@ -56,14 +45,7 @@ class DB:
56
45
  settings = Inject(get_settings)
57
46
  settings.check_db()
58
47
 
59
- if settings.DB_ENGINE == "sqlite":
60
- logger.warning(SQLITE_WARNING_MESSAGE)
61
- # Set some sqlite-specific options
62
- engine_kwargs_async = dict(poolclass=StaticPool)
63
- else:
64
- engine_kwargs_async = {
65
- "pool_pre_ping": True,
66
- }
48
+ engine_kwargs_async = {"pool_pre_ping": True}
67
49
 
68
50
  cls._engine_async = create_async_engine(
69
51
  settings.DATABASE_ASYNC_URL,
@@ -83,15 +65,7 @@ class DB:
83
65
  settings = Inject(get_settings)
84
66
  settings.check_db()
85
67
 
86
- if settings.DB_ENGINE == "sqlite":
87
- logger.warning(SQLITE_WARNING_MESSAGE)
88
- # Set some sqlite-specific options
89
- engine_kwargs_sync = dict(
90
- poolclass=StaticPool,
91
- connect_args={"check_same_thread": False},
92
- )
93
- else:
94
- engine_kwargs_sync = {}
68
+ engine_kwargs_sync = {}
95
69
 
96
70
  cls._engine_sync = create_engine(
97
71
  settings.DATABASE_SYNC_URL,
@@ -107,13 +81,6 @@ class DB:
107
81
  future=True,
108
82
  )
109
83
 
110
- @event.listens_for(cls._engine_sync, "connect")
111
- def set_sqlite_pragma(dbapi_connection, connection_record):
112
- if settings.DB_ENGINE == "sqlite":
113
- cursor = dbapi_connection.cursor()
114
- cursor.execute("PRAGMA journal_mode=WAL")
115
- cursor.close()
116
-
117
84
  @classmethod
118
85
  async def get_async_db(cls) -> AsyncGenerator[AsyncSession, None]:
119
86
  """
@@ -48,6 +48,10 @@ class TaskGroupV2(SQLModel, table=True):
48
48
  default_factory=get_timestamp,
49
49
  sa_column=Column(DateTime(timezone=True), nullable=False),
50
50
  )
51
+ timestamp_last_used: Optional[datetime] = Field(
52
+ default=None,
53
+ sa_column=Column(DateTime(timezone=True), nullable=True),
54
+ )
51
55
 
52
56
  @property
53
57
  def pip_install_string(self) -> str:
@@ -221,27 +221,30 @@ async def _get_valid_user_group_id(
221
221
 
222
222
 
223
223
  async def _get_collection_task_group_activity_status_message(
224
- task_group: TaskGroupV2, db: AsyncSession
224
+ task_group_id: int,
225
+ db: AsyncSession,
225
226
  ) -> str:
227
+
226
228
  res = await db.execute(
227
- select(TaskGroupActivityV2).where(
228
- TaskGroupActivityV2.taskgroupv2_id == task_group.id
229
- and TaskGroupActivityV2.action == TaskGroupActivityActionV2.COLLECT
230
- )
229
+ select(TaskGroupActivityV2)
230
+ .where(TaskGroupActivityV2.taskgroupv2_id == task_group_id)
231
+ .where(TaskGroupActivityV2.action == TaskGroupActivityActionV2.COLLECT)
231
232
  )
232
233
  task_group_activity_list = res.scalars().all()
233
234
  if len(task_group_activity_list) > 1:
234
235
  msg = (
235
- "Expected one TaskGroupActivityV2 associated to TaskGroup "
236
- f"{task_group.id}, found {len(task_group_activity_list)} "
237
- f"(IDs: {[tga.id for tga in task_group_activity_list]}).\n"
236
+ "\nWarning: "
237
+ "Expected only one TaskGroupActivityV2 associated to TaskGroup "
238
+ f"{task_group_id}, found {len(task_group_activity_list)} "
239
+ f"(IDs: {[tga.id for tga in task_group_activity_list]})."
238
240
  "Warning: this should have not happened, please contact an admin."
239
241
  )
240
242
  elif len(task_group_activity_list) == 1:
241
243
  msg = (
242
- "\nThere exists a task-group activity"
243
- f"(ID={task_group_activity_list[0].id}) for "
244
- f"such task group (ID={task_group.id}), with status "
244
+ "\nNote:"
245
+ "There exists another task-group collection "
246
+ f"(activity ID={task_group_activity_list[0].id}) for "
247
+ f"this task group (ID={task_group_id}), with status "
245
248
  f"'{task_group_activity_list[0].status}'."
246
249
  )
247
250
  else:
@@ -277,7 +280,7 @@ async def _verify_non_duplication_user_constraint(
277
280
  ),
278
281
  )
279
282
  state_msg = await _get_collection_task_group_activity_status_message(
280
- duplicate[0], db
283
+ duplicate[0].id, db
281
284
  )
282
285
  raise HTTPException(
283
286
  status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
@@ -319,7 +322,7 @@ async def _verify_non_duplication_group_constraint(
319
322
  ),
320
323
  )
321
324
  state_msg = await _get_collection_task_group_activity_status_message(
322
- duplicate[0], db
325
+ duplicate[0].id, db
323
326
  )
324
327
  raise HTTPException(
325
328
  status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
@@ -30,6 +30,7 @@ from ...aux.validate_user_settings import validate_user_settings
30
30
  from ._aux_functions import _get_dataset_check_owner
31
31
  from ._aux_functions import _get_workflow_check_owner
32
32
  from ._aux_functions import clean_app_job_list_v2
33
+ from fractal_server.app.models import TaskGroupV2
33
34
  from fractal_server.app.models import UserOAuth
34
35
  from fractal_server.app.routes.api.v2._aux_functions_tasks import (
35
36
  _get_task_read_access,
@@ -61,6 +62,8 @@ async def apply_workflow(
61
62
  db: AsyncSession = Depends(get_async_db),
62
63
  ) -> Optional[JobReadV2]:
63
64
 
65
+ now = get_timestamp()
66
+
64
67
  # Remove non-submitted V2 jobs from the app state when the list grows
65
68
  # beyond a threshold
66
69
  settings = Inject(get_settings)
@@ -112,22 +115,23 @@ async def apply_workflow(
112
115
  )
113
116
 
114
117
  # Check that tasks have read-access and are `active`
118
+ used_task_group_ids = set()
115
119
  for wftask in workflow.task_list[
116
120
  first_task_index : last_task_index + 1 # noqa: E203
117
121
  ]:
118
- await _get_task_read_access(
122
+ task = await _get_task_read_access(
119
123
  user_id=user.id,
120
124
  task_id=wftask.task_id,
121
125
  require_active=True,
122
126
  db=db,
123
127
  )
128
+ used_task_group_ids.add(task.taskgroupv2_id)
124
129
 
125
130
  # Validate user settings
126
131
  FRACTAL_RUNNER_BACKEND = settings.FRACTAL_RUNNER_BACKEND
127
132
  user_settings = await validate_user_settings(
128
133
  user=user, backend=FRACTAL_RUNNER_BACKEND, db=db
129
134
  )
130
-
131
135
  # Check that no other job with the same dataset_id is SUBMITTED
132
136
  stm = (
133
137
  select(JobV2)
@@ -184,8 +188,18 @@ async def apply_workflow(
184
188
  await db.commit()
185
189
  await db.refresh(job)
186
190
 
191
+ # Update TaskGroupV2.timestamp_last_used
192
+ res = await db.execute(
193
+ select(TaskGroupV2).where(TaskGroupV2.id.in_(used_task_group_ids))
194
+ )
195
+ used_task_groups = res.scalars().all()
196
+ for used_task_group in used_task_groups:
197
+ used_task_group.timestamp_last_used = now
198
+ db.add(used_task_group)
199
+ await db.commit()
200
+
187
201
  # Define server-side job directory
188
- timestamp_string = get_timestamp().strftime("%Y%m%d_%H%M%S")
202
+ timestamp_string = now.strftime("%Y%m%d_%H%M%S")
189
203
  WORKFLOW_DIR_LOCAL = settings.FRACTAL_RUNNER_WORKING_BASE_DIR / (
190
204
  f"proj_v2_{project_id:07d}_wf_{workflow_id:07d}_job_{job.id:07d}"
191
205
  f"_{timestamp_string}"
@@ -35,7 +35,7 @@ from fractal_server.app.schemas.v2 import (
35
35
  )
36
36
  from fractal_server.app.schemas.v2 import TaskGroupV2OriginEnum
37
37
  from fractal_server.tasks.v2.local.collect import (
38
- collect_package_local,
38
+ collect_local,
39
39
  )
40
40
  from fractal_server.tasks.v2.utils_package_names import _parse_wheel_filename
41
41
  from fractal_server.tasks.v2.utils_package_names import normalize_package_name
@@ -248,7 +248,7 @@ async def collect_tasks_pip(
248
248
  # SSH task collection
249
249
 
250
250
  from fractal_server.tasks.v2.ssh.collect import (
251
- collect_package_ssh,
251
+ collect_ssh,
252
252
  )
253
253
 
254
254
  # User appropriate FractalSSH object
@@ -261,7 +261,7 @@ async def collect_tasks_pip(
261
261
  fractal_ssh = fractal_ssh_list.get(**ssh_credentials)
262
262
 
263
263
  background_tasks.add_task(
264
- collect_package_ssh,
264
+ collect_ssh,
265
265
  task_group_id=task_group.id,
266
266
  task_group_activity_id=task_group_activity.id,
267
267
  fractal_ssh=fractal_ssh,
@@ -271,7 +271,7 @@ async def collect_tasks_pip(
271
271
  else:
272
272
  # Local task collection
273
273
  background_tasks.add_task(
274
- collect_package_local,
274
+ collect_local,
275
275
  task_group_id=task_group.id,
276
276
  task_group_activity_id=task_group_activity.id,
277
277
  )
@@ -4,15 +4,10 @@ from datetime import timezone
4
4
  from fastapi import HTTPException
5
5
  from fastapi import status
6
6
 
7
- from fractal_server.config import get_settings
8
- from fractal_server.syringe import Inject
9
-
10
7
 
11
8
  def _convert_to_db_timestamp(dt: datetime) -> datetime:
12
9
  """
13
10
  This function takes a timezone-aware datetime and converts it to UTC.
14
- If using SQLite, it also removes the timezone information in order to make
15
- the datetime comparable with datetimes in the database.
16
11
  """
17
12
  if dt.tzinfo is None:
18
13
  raise HTTPException(
@@ -20,6 +15,4 @@ def _convert_to_db_timestamp(dt: datetime) -> datetime:
20
15
  detail=f"The timestamp provided has no timezone information: {dt}",
21
16
  )
22
17
  _dt = dt.astimezone(timezone.utc)
23
- if Inject(get_settings).DB_ENGINE == "sqlite":
24
- return _dt.replace(tzinfo=None)
25
18
  return _dt
@@ -108,8 +108,7 @@ def val_unique_list(attribute: str):
108
108
  def valutc(attribute: str):
109
109
  def val(timestamp: Optional[datetime]) -> Optional[datetime]:
110
110
  """
111
- Replacing `tzinfo` with `timezone.utc` is just required by SQLite data.
112
- If using Postgres, this function leaves the datetime exactly as it is.
111
+ Replace `tzinfo` with `timezone.utc`.
113
112
  """
114
113
  if timestamp is not None:
115
114
  return timestamp.replace(tzinfo=timezone.utc)
@@ -1,6 +1,5 @@
1
1
  from datetime import datetime
2
2
  from enum import Enum
3
- from typing import Literal
4
3
  from typing import Optional
5
4
 
6
5
  from pydantic import BaseModel
@@ -74,7 +73,7 @@ class TaskGroupReadV2(BaseModel):
74
73
  user_id: int
75
74
  user_group_id: Optional[int] = None
76
75
 
77
- origin: Literal["pypi", "wheel-file", "other"]
76
+ origin: TaskGroupV2OriginEnum
78
77
  pkg_name: str
79
78
  version: Optional[str] = None
80
79
  python_version: Optional[str] = None
@@ -90,6 +89,7 @@ class TaskGroupReadV2(BaseModel):
90
89
 
91
90
  active: bool
92
91
  timestamp_created: datetime
92
+ timestamp_last_used: Optional[datetime] = None
93
93
 
94
94
 
95
95
  class TaskGroupUpdateV2(BaseModel, extra=Extra.forbid):
@@ -16,7 +16,6 @@ import shutil
16
16
  import sys
17
17
  from os import environ
18
18
  from os import getenv
19
- from os.path import abspath
20
19
  from pathlib import Path
21
20
  from typing import Literal
22
21
  from typing import Optional
@@ -167,10 +166,6 @@ class Settings(BaseSettings):
167
166
  ###########################################################################
168
167
  # DATABASE
169
168
  ###########################################################################
170
- DB_ENGINE: Literal["sqlite", "postgres-psycopg"] = "sqlite"
171
- """
172
- Database engine to use (supported: `sqlite`, `postgres-psycopg`).
173
- """
174
169
  DB_ECHO: bool = False
175
170
  """
176
171
  If `True`, make database operations verbose.
@@ -196,44 +191,21 @@ class Settings(BaseSettings):
196
191
  Name of the PostgreSQL database to connect to.
197
192
  """
198
193
 
199
- SQLITE_PATH: Optional[str]
200
- """
201
- File path where the SQLite database is located (or will be located).
202
- """
203
-
204
194
  @property
205
195
  def DATABASE_ASYNC_URL(self) -> URL:
206
- if self.DB_ENGINE == "postgres-psycopg":
207
- url = URL.create(
208
- drivername="postgresql+psycopg",
209
- username=self.POSTGRES_USER,
210
- password=self.POSTGRES_PASSWORD,
211
- host=self.POSTGRES_HOST,
212
- port=self.POSTGRES_PORT,
213
- database=self.POSTGRES_DB,
214
- )
215
- else:
216
- if not self.SQLITE_PATH:
217
- raise FractalConfigurationError(
218
- "SQLITE_PATH path cannot be None"
219
- )
220
- sqlite_path = abspath(self.SQLITE_PATH)
221
- url = URL.create(
222
- drivername="sqlite+aiosqlite",
223
- database=sqlite_path,
224
- )
196
+ url = URL.create(
197
+ drivername="postgresql+psycopg",
198
+ username=self.POSTGRES_USER,
199
+ password=self.POSTGRES_PASSWORD,
200
+ host=self.POSTGRES_HOST,
201
+ port=self.POSTGRES_PORT,
202
+ database=self.POSTGRES_DB,
203
+ )
225
204
  return url
226
205
 
227
206
  @property
228
207
  def DATABASE_SYNC_URL(self):
229
- if self.DB_ENGINE == "postgres-psycopg":
230
- return self.DATABASE_ASYNC_URL.set(drivername="postgresql+psycopg")
231
- else:
232
- if not self.SQLITE_PATH:
233
- raise FractalConfigurationError(
234
- "SQLITE_PATH path cannot be None"
235
- )
236
- return self.DATABASE_ASYNC_URL.set(drivername="sqlite")
208
+ return self.DATABASE_ASYNC_URL.set(drivername="postgresql+psycopg")
237
209
 
238
210
  ###########################################################################
239
211
  # FRACTAL SPECIFIC
@@ -533,25 +505,8 @@ class Settings(BaseSettings):
533
505
  """
534
506
  Checks that db environment variables are properly set.
535
507
  """
536
- if self.DB_ENGINE == "postgres-psycopg":
537
- if not self.POSTGRES_DB:
538
- raise FractalConfigurationError(
539
- "POSTGRES_DB cannot be None when DB_ENGINE="
540
- "postgres-psycopg."
541
- )
542
-
543
- try:
544
- import psycopg # noqa: F401
545
- except ModuleNotFoundError:
546
- raise FractalConfigurationError(
547
- "DB engine is `postgres-psycopg` but `psycopg` is not "
548
- "available"
549
- )
550
- else:
551
- if not self.SQLITE_PATH:
552
- raise FractalConfigurationError(
553
- "SQLITE_PATH cannot be None when DB_ENGINE=sqlite."
554
- )
508
+ if not self.POSTGRES_DB:
509
+ raise FractalConfigurationError("POSTGRES_DB cannot be None.")
555
510
 
556
511
  def check_runner(self) -> None:
557
512
 
@@ -67,6 +67,13 @@ def upgrade() -> None:
67
67
  batch_op.add_column(
68
68
  sa.Column("venv_file_number", sa.Integer(), nullable=True)
69
69
  )
70
+ batch_op.add_column(
71
+ sa.Column(
72
+ "timestamp_last_used",
73
+ sa.DateTime(timezone=True),
74
+ nullable=True,
75
+ )
76
+ )
70
77
 
71
78
  # ### end Alembic commands ###
72
79
 
@@ -74,6 +81,7 @@ def upgrade() -> None:
74
81
  def downgrade() -> None:
75
82
  # ### commands auto generated by Alembic - please adjust! ###
76
83
  with op.batch_alter_table("taskgroupv2", schema=None) as batch_op:
84
+ batch_op.drop_column("timestamp_last_used")
77
85
  batch_op.drop_column("venv_file_number")
78
86
  batch_op.drop_column("venv_size_in_kB")
79
87
  batch_op.drop_column("pip_freeze")
@@ -196,23 +196,57 @@ class FractalSSH(object):
196
196
  """
197
197
  Open the SSH connection and handle exceptions.
198
198
 
199
- This function can be called from within other functions that use
200
- `connection`, so that we can provide a meaningful error in case the
201
- SSH connection cannot be opened.
199
+ This method should always be called at the beginning of background
200
+ operations that use FractalSSH, so that:
201
+
202
+ 1. We try to restore unusable connections (e.g. due to closed socket).
203
+ 2. We provide an informative error if connection cannot be established.
202
204
  """
203
- if not self._connection.is_connected:
205
+ self.logger.debug(
206
+ f"[check_connection] {self._connection.is_connected=}"
207
+ )
208
+ if self._connection.is_connected:
209
+ # Even if the connection appears open, it could be broken for
210
+ # external reasons (e.g. the socket is closed because the SSH
211
+ # server was restarted). In these cases, we catch the error and
212
+ # try to re-open the connection.
204
213
  try:
205
- with _acquire_lock_with_timeout(
206
- lock=self._lock,
207
- label="_connection.open",
208
- timeout=self.default_lock_timeout,
209
- ):
210
- self._connection.open()
211
- except Exception as e:
212
- raise RuntimeError(
213
- f"Cannot open SSH connection. Original error:\n{str(e)}"
214
+ self.logger.info(
215
+ "[check_connection] Run dummy command to check connection."
216
+ )
217
+ # Run both an SFTP and an SSH command, as they correspond to
218
+ # different sockets
219
+ self.remote_exists("/dummy/path/")
220
+ self.run_command(cmd="whoami")
221
+ self.logger.info(
222
+ "[check_connection] SSH connection is already OK, exit."
223
+ )
224
+ return
225
+ except (OSError, EOFError) as e:
226
+ self.logger.warning(
227
+ f"[check_connection] Detected error {str(e)}, re-open."
228
+ )
229
+ # Try opening the connection (if it was closed) or to re-open it (if
230
+ # an error happened).
231
+ try:
232
+ self.close()
233
+ with _acquire_lock_with_timeout(
234
+ lock=self._lock,
235
+ label="_connection.open",
236
+ timeout=self.default_lock_timeout,
237
+ logger_name=self.logger_name,
238
+ ):
239
+ self._connection.open()
240
+ self._connection.client.open_sftp()
241
+ self.logger.info(
242
+ "[check_connection] SSH connection opened, exit."
214
243
  )
215
244
 
245
+ except Exception as e:
246
+ raise RuntimeError(
247
+ f"Cannot open SSH connection. Original error:\n{str(e)}"
248
+ )
249
+
216
250
  def close(self) -> None:
217
251
  """
218
252
  Aggressively close `self._connection`.
@@ -228,9 +262,8 @@ class FractalSSH(object):
228
262
  timeout=self.default_lock_timeout,
229
263
  ):
230
264
  self._connection.close()
231
-
232
- if self._connection.client is not None:
233
- self._connection.client.close()
265
+ if self._connection.client is not None:
266
+ self._connection.client.close()
234
267
 
235
268
  def run_command(
236
269
  self,
@@ -1,3 +1,3 @@
1
- from .collect import collect_package_local # noqa
1
+ from .collect import collect_local # noqa
2
2
  from .deactivate import deactivate_local # noqa
3
3
  from .reactivate import reactivate_local # noqa
@@ -47,7 +47,7 @@ def _copy_wheel_file_local(task_group: TaskGroupV2) -> str:
47
47
  return dest
48
48
 
49
49
 
50
- def collect_package_local(
50
+ def collect_local(
51
51
  *,
52
52
  task_group_activity_id: int,
53
53
  task_group_id: int,