fractal-server 2.7.0a4__tar.gz → 2.7.0a6__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 (231) hide show
  1. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/PKG-INFO +1 -1
  2. fractal_server-2.7.0a6/fractal_server/__init__.py +1 -0
  3. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/admin/v2/task.py +0 -5
  4. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v1/task_collection.py +2 -2
  5. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v2/__init__.py +4 -0
  6. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v2/_aux_functions.py +1 -7
  7. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v2/_aux_functions_tasks.py +72 -11
  8. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v2/task_collection.py +49 -39
  9. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v2/task_group.py +10 -6
  10. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v2/workflow.py +14 -82
  11. fractal_server-2.7.0a6/fractal_server/app/routes/api/v2/workflow_import.py +355 -0
  12. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v2/workflowtask.py +0 -1
  13. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/auth/group.py +27 -0
  14. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v2/__init__.py +13 -7
  15. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/v2/__init__.py +1 -0
  16. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/v2/dumps.py +0 -1
  17. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/v2/manifest.py +13 -0
  18. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/v2/task.py +20 -10
  19. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/v2/workflowtask.py +3 -4
  20. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/data_migrations/2_7_0.py +62 -3
  21. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/tasks/utils.py +19 -5
  22. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/tasks/v1/background_operations.py +3 -3
  23. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/tasks/v1/get_collection_data.py +2 -2
  24. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/tasks/v2/background_operations.py +4 -4
  25. fractal_server-2.7.0a6/fractal_server/tasks/v2/endpoint_operations.py +124 -0
  26. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/pyproject.toml +2 -2
  27. fractal_server-2.7.0a4/fractal_server/__init__.py +0 -1
  28. fractal_server-2.7.0a4/fractal_server/tasks/v2/endpoint_operations.py +0 -44
  29. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/LICENSE +0 -0
  30. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/README.md +0 -0
  31. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/__main__.py +0 -0
  32. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/alembic.ini +0 -0
  33. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/__init__.py +0 -0
  34. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/db/__init__.py +0 -0
  35. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/models/__init__.py +0 -0
  36. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/models/linkusergroup.py +0 -0
  37. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/models/linkuserproject.py +0 -0
  38. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/models/security.py +0 -0
  39. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/models/user_settings.py +0 -0
  40. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/models/v1/__init__.py +0 -0
  41. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/models/v1/dataset.py +0 -0
  42. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/models/v1/job.py +0 -0
  43. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/models/v1/project.py +0 -0
  44. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/models/v1/state.py +0 -0
  45. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/models/v1/task.py +0 -0
  46. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/models/v1/workflow.py +0 -0
  47. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/models/v2/__init__.py +0 -0
  48. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/models/v2/collection_state.py +0 -0
  49. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/models/v2/dataset.py +0 -0
  50. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/models/v2/job.py +0 -0
  51. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/models/v2/project.py +0 -0
  52. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/models/v2/task.py +0 -0
  53. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/models/v2/workflow.py +0 -0
  54. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/models/v2/workflowtask.py +0 -0
  55. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/__init__.py +0 -0
  56. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/admin/__init__.py +0 -0
  57. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/admin/v1.py +0 -0
  58. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/admin/v2/__init__.py +0 -0
  59. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/admin/v2/job.py +0 -0
  60. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/admin/v2/project.py +0 -0
  61. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/admin/v2/task_group.py +0 -0
  62. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/__init__.py +0 -0
  63. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v1/__init__.py +0 -0
  64. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v1/_aux_functions.py +0 -0
  65. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v1/dataset.py +0 -0
  66. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v1/job.py +0 -0
  67. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v1/project.py +0 -0
  68. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v1/task.py +0 -0
  69. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v1/workflow.py +0 -0
  70. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v1/workflowtask.py +0 -0
  71. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v2/dataset.py +0 -0
  72. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v2/images.py +0 -0
  73. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v2/job.py +0 -0
  74. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v2/project.py +0 -0
  75. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v2/status.py +0 -0
  76. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v2/submit.py +0 -0
  77. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v2/task.py +0 -0
  78. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/api/v2/task_collection_custom.py +0 -0
  79. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/auth/__init__.py +0 -0
  80. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/auth/_aux_auth.py +0 -0
  81. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/auth/current_user.py +0 -0
  82. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/auth/login.py +0 -0
  83. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/auth/oauth.py +0 -0
  84. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/auth/register.py +0 -0
  85. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/auth/router.py +0 -0
  86. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/auth/users.py +0 -0
  87. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/aux/__init__.py +0 -0
  88. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/aux/_job.py +0 -0
  89. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/aux/_runner.py +0 -0
  90. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/routes/aux/validate_user_settings.py +0 -0
  91. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/.gitignore +0 -0
  92. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/__init__.py +0 -0
  93. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/async_wrap.py +0 -0
  94. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/components.py +0 -0
  95. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/compress_folder.py +0 -0
  96. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/exceptions.py +0 -0
  97. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/executors/__init__.py +0 -0
  98. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/executors/slurm/__init__.py +0 -0
  99. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/executors/slurm/_batching.py +0 -0
  100. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/executors/slurm/_slurm_config.py +0 -0
  101. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/executors/slurm/remote.py +0 -0
  102. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/executors/slurm/ssh/__init__.py +0 -0
  103. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/executors/slurm/ssh/_executor_wait_thread.py +0 -0
  104. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/executors/slurm/ssh/_slurm_job.py +0 -0
  105. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/executors/slurm/ssh/executor.py +0 -0
  106. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/executors/slurm/sudo/__init__.py +0 -0
  107. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/executors/slurm/sudo/_check_jobs_status.py +0 -0
  108. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/executors/slurm/sudo/_executor_wait_thread.py +0 -0
  109. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/executors/slurm/sudo/_subprocess_run_as_user.py +0 -0
  110. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/executors/slurm/sudo/executor.py +0 -0
  111. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/extract_archive.py +0 -0
  112. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/filenames.py +0 -0
  113. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/run_subprocess.py +0 -0
  114. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/set_start_and_last_task_index.py +0 -0
  115. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/shutdown.py +0 -0
  116. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/task_files.py +0 -0
  117. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v1/__init__.py +0 -0
  118. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v1/_common.py +0 -0
  119. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v1/_local/__init__.py +0 -0
  120. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v1/_local/_local_config.py +0 -0
  121. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v1/_local/_submit_setup.py +0 -0
  122. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v1/_local/executor.py +0 -0
  123. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v1/_slurm/__init__.py +0 -0
  124. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v1/_slurm/_submit_setup.py +0 -0
  125. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v1/_slurm/get_slurm_config.py +0 -0
  126. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v1/common.py +0 -0
  127. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v1/handle_failed_job.py +0 -0
  128. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v2/_local/__init__.py +0 -0
  129. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v2/_local/_local_config.py +0 -0
  130. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v2/_local/_submit_setup.py +0 -0
  131. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v2/_local/executor.py +0 -0
  132. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v2/_local_experimental/__init__.py +0 -0
  133. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v2/_local_experimental/_local_config.py +0 -0
  134. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v2/_local_experimental/_submit_setup.py +0 -0
  135. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v2/_local_experimental/executor.py +0 -0
  136. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v2/_slurm_common/__init__.py +0 -0
  137. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v2/_slurm_common/get_slurm_config.py +0 -0
  138. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v2/_slurm_ssh/__init__.py +0 -0
  139. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v2/_slurm_ssh/_submit_setup.py +0 -0
  140. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v2/_slurm_sudo/__init__.py +0 -0
  141. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v2/_slurm_sudo/_submit_setup.py +0 -0
  142. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v2/deduplicate_list.py +0 -0
  143. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v2/handle_failed_job.py +0 -0
  144. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v2/merge_outputs.py +0 -0
  145. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v2/runner.py +0 -0
  146. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v2/runner_functions.py +0 -0
  147. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v2/runner_functions_low_level.py +0 -0
  148. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/v2/task_interface.py +0 -0
  149. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/runner/versions.py +0 -0
  150. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/__init__.py +0 -0
  151. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/_validators.py +0 -0
  152. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/user.py +0 -0
  153. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/user_group.py +0 -0
  154. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/user_settings.py +0 -0
  155. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/v1/__init__.py +0 -0
  156. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/v1/applyworkflow.py +0 -0
  157. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/v1/dataset.py +0 -0
  158. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/v1/dumps.py +0 -0
  159. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/v1/manifest.py +0 -0
  160. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/v1/project.py +0 -0
  161. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/v1/state.py +0 -0
  162. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/v1/task.py +0 -0
  163. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/v1/task_collection.py +0 -0
  164. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/v1/workflow.py +0 -0
  165. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/v2/dataset.py +0 -0
  166. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/v2/job.py +0 -0
  167. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/v2/project.py +0 -0
  168. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/v2/status.py +0 -0
  169. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/v2/task_collection.py +0 -0
  170. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/v2/task_group.py +0 -0
  171. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/schemas/v2/workflow.py +0 -0
  172. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/security/__init__.py +0 -0
  173. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/app/user_settings.py +0 -0
  174. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/config.py +0 -0
  175. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/data_migrations/README.md +0 -0
  176. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/data_migrations/tools.py +0 -0
  177. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/gunicorn_fractal.py +0 -0
  178. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/images/__init__.py +0 -0
  179. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/images/models.py +0 -0
  180. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/images/tools.py +0 -0
  181. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/logger.py +0 -0
  182. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/main.py +0 -0
  183. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/README +0 -0
  184. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/env.py +0 -0
  185. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/naming_convention.py +0 -0
  186. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/script.py.mako +0 -0
  187. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/versions/034a469ec2eb_task_groups.py +0 -0
  188. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/versions/091b01f51f88_add_usergroup_and_linkusergroup_table.py +0 -0
  189. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py +0 -0
  190. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/versions/4cedeb448a53_workflowtask_foreign_keys_not_nullables.py +0 -0
  191. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/versions/501961cfcd85_remove_link_between_v1_and_v2_tasks_.py +0 -0
  192. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/versions/50a13d6138fd_initial_schema.py +0 -0
  193. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/versions/5bf02391cfef_v2.py +0 -0
  194. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/versions/70e77f1c38b0_add_applyworkflow_first_task_index_and_.py +0 -0
  195. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/versions/71eefd1dd202_add_slurm_accounts.py +0 -0
  196. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/versions/84bf0fffde30_add_dumps_to_applyworkflow.py +0 -0
  197. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/versions/8f79bd162e35_add_docs_info_and_docs_link_to_task_.py +0 -0
  198. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/versions/94a47ea2d3ff_remove_cache_dir_slurm_user_and_slurm_.py +0 -0
  199. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/versions/97f444d47249_add_applyworkflow_project_dump.py +0 -0
  200. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/versions/99ea79d9e5d2_add_dataset_history.py +0 -0
  201. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/versions/9c5ae74c9b98_add_user_settings_table.py +0 -0
  202. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/versions/9fd26a2b0de4_add_workflow_timestamp_created.py +0 -0
  203. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/versions/a7f4d6137b53_add_workflow_dump_to_applyworkflow.py +0 -0
  204. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/versions/d4fe3708d309_make_applyworkflow_workflow_dump_non_.py +0 -0
  205. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/versions/da2cb2ac4255_user_group_viewer_paths.py +0 -0
  206. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/versions/e75cac726012_make_applyworkflow_start_timestamp_not_.py +0 -0
  207. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/versions/efa89c30e0a4_add_project_timestamp_created.py +0 -0
  208. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/migrations/versions/f384e1c0cf5d_drop_task_default_args_columns.py +0 -0
  209. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/py.typed +0 -0
  210. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/ssh/__init__.py +0 -0
  211. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/ssh/_fabric.py +0 -0
  212. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/string_tools.py +0 -0
  213. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/syringe.py +0 -0
  214. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/tasks/__init__.py +0 -0
  215. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/tasks/v1/_TaskCollectPip.py +0 -0
  216. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/tasks/v1/__init__.py +0 -0
  217. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/tasks/v1/endpoint_operations.py +0 -0
  218. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/tasks/v1/utils.py +0 -0
  219. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/tasks/v2/__init__.py +0 -0
  220. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/tasks/v2/_venv_pip.py +0 -0
  221. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/tasks/v2/background_operations_ssh.py +0 -0
  222. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/tasks/v2/database_operations.py +0 -0
  223. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/tasks/v2/templates/_1_create_venv.sh +0 -0
  224. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/tasks/v2/templates/_2_upgrade_pip.sh +0 -0
  225. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/tasks/v2/templates/_3_pip_install.sh +0 -0
  226. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/tasks/v2/templates/_4_pip_freeze.sh +0 -0
  227. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/tasks/v2/templates/_5_pip_show.sh +0 -0
  228. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/tasks/v2/utils.py +0 -0
  229. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/urls.py +0 -0
  230. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/fractal_server/utils.py +0 -0
  231. {fractal_server-2.7.0a4 → fractal_server-2.7.0a6}/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.7.0a4
3
+ Version: 2.7.0a6
4
4
  Summary: Server component of the Fractal analytics platform
5
5
  Home-page: https://github.com/fractal-analytics-platform/fractal-server
6
6
  License: BSD-3-Clause
@@ -0,0 +1 @@
1
+ __VERSION__ = "2.7.0a6"
@@ -29,7 +29,6 @@ class TaskV2Minimal(BaseModel):
29
29
  command_non_parallel: Optional[str] = None
30
30
  command_parallel: Optional[str]
31
31
  source: Optional[str] = None
32
- owner: Optional[str] = None
33
32
  version: Optional[str] = None
34
33
 
35
34
 
@@ -60,7 +59,6 @@ async def query_tasks(
60
59
  source: Optional[str] = None,
61
60
  version: Optional[str] = None,
62
61
  name: Optional[str] = None,
63
- owner: Optional[str] = None,
64
62
  max_number_of_results: int = 25,
65
63
  user: UserOAuth = Depends(current_active_superuser),
66
64
  db: AsyncSession = Depends(get_async_db),
@@ -75,7 +73,6 @@ async def query_tasks(
75
73
  `task.source`.
76
74
  version: If not `None`, query for matching `task.version`.
77
75
  name: If not `None`, query for contained case insensitive `task.name`.
78
- owner: If not `None`, query for matching `task.owner`.
79
76
  max_number_of_results: The maximum length of the response.
80
77
  """
81
78
 
@@ -89,8 +86,6 @@ async def query_tasks(
89
86
  stm = stm.where(TaskV2.version == version)
90
87
  if name is not None:
91
88
  stm = stm.where(TaskV2.name.icontains(name))
92
- if owner is not None:
93
- stm = stm.where(TaskV2.owner == owner)
94
89
 
95
90
  res = await db.execute(stm)
96
91
  task_list = res.scalars().all()
@@ -27,7 +27,7 @@ from fractal_server.app.models import UserOAuth
27
27
  from fractal_server.app.routes.auth import current_active_user
28
28
  from fractal_server.app.routes.auth import current_active_verified_user
29
29
  from fractal_server.string_tools import slugify_task_name_for_source_v1
30
- from fractal_server.tasks.utils import get_collection_log
30
+ from fractal_server.tasks.utils import get_collection_log_v1
31
31
  from fractal_server.tasks.v1._TaskCollectPip import _TaskCollectPip
32
32
  from fractal_server.tasks.v1.background_operations import (
33
33
  background_collect_pip,
@@ -232,7 +232,7 @@ async def check_collection_status(
232
232
  # In some cases (i.e. a successful or ongoing task collection), data.log is
233
233
  # not set; if so, we collect the current logs
234
234
  if verbose and not data.log:
235
- data.log = get_collection_log(data.venv_path)
235
+ data.log = get_collection_log_v1(data.venv_path)
236
236
  state.data = data.sanitised_dict()
237
237
  close_logger(logger)
238
238
  await db.close()
@@ -14,6 +14,7 @@ from .task_collection import router as task_collection_router_v2
14
14
  from .task_collection_custom import router as task_collection_router_v2_custom
15
15
  from .task_group import router as task_group_router_v2
16
16
  from .workflow import router as workflow_router_v2
17
+ from .workflow_import import router as workflow_import_router_v2
17
18
  from .workflowtask import router as workflowtask_router_v2
18
19
  from fractal_server.config import get_settings
19
20
  from fractal_server.syringe import Inject
@@ -42,5 +43,8 @@ router_api_v2.include_router(
42
43
  task_group_router_v2, prefix="/task-group", tags=["V2 TaskGroup"]
43
44
  )
44
45
  router_api_v2.include_router(workflow_router_v2, tags=["V2 Workflow"])
46
+ router_api_v2.include_router(
47
+ workflow_import_router_v2, tags=["V2 Workflow Import"]
48
+ )
45
49
  router_api_v2.include_router(workflowtask_router_v2, tags=["V2 WorkflowTask"])
46
50
  router_api_v2.include_router(status_router_v2, tags=["V2 Status"])
@@ -332,7 +332,6 @@ async def _workflow_insert_task(
332
332
  *,
333
333
  workflow_id: int,
334
334
  task_id: int,
335
- order: Optional[int] = None,
336
335
  meta_parallel: Optional[dict[str, Any]] = None,
337
336
  meta_non_parallel: Optional[dict[str, Any]] = None,
338
337
  args_non_parallel: Optional[dict[str, Any]] = None,
@@ -347,7 +346,6 @@ async def _workflow_insert_task(
347
346
  workflow_id:
348
347
  task_id:
349
348
 
350
- order:
351
349
  meta_parallel:
352
350
  meta_non_parallel:
353
351
  args_non_parallel:
@@ -359,9 +357,6 @@ async def _workflow_insert_task(
359
357
  if db_workflow is None:
360
358
  raise ValueError(f"Workflow {workflow_id} does not exist")
361
359
 
362
- if order is None:
363
- order = len(db_workflow.task_list)
364
-
365
360
  # Get task from db
366
361
  db_task = await db.get(TaskV2, task_id)
367
362
  if db_task is None:
@@ -397,8 +392,7 @@ async def _workflow_insert_task(
397
392
  meta_non_parallel=final_meta_non_parallel,
398
393
  **input_filters_kwarg,
399
394
  )
400
- db_workflow.task_list.insert(order, wf_task)
401
- db_workflow.task_list.reorder() # type: ignore
395
+ db_workflow.task_list.append(wf_task)
402
396
  flag_modified(db_workflow, "task_list")
403
397
  await db.commit()
404
398
 
@@ -9,13 +9,21 @@ from fastapi import HTTPException
9
9
  from fastapi import status
10
10
  from sqlmodel import select
11
11
 
12
- from ....db import AsyncSession
13
- from ....models import LinkUserGroup
14
- from ....models.v2 import TaskGroupV2
15
- from ....models.v2 import TaskV2
16
- from ....models.v2 import WorkflowTaskV2
17
- from ...auth._aux_auth import _get_default_usergroup_id
18
- from ...auth._aux_auth import _verify_user_belongs_to_group
12
+ from fractal_server.app.db import AsyncSession
13
+ from fractal_server.app.models import LinkUserGroup
14
+ from fractal_server.app.models import UserGroup
15
+ from fractal_server.app.models import UserOAuth
16
+ from fractal_server.app.models.v2 import CollectionStateV2
17
+ from fractal_server.app.models.v2 import TaskGroupV2
18
+ from fractal_server.app.models.v2 import TaskV2
19
+ from fractal_server.app.models.v2 import WorkflowTaskV2
20
+ from fractal_server.app.routes.auth._aux_auth import _get_default_usergroup_id
21
+ from fractal_server.app.routes.auth._aux_auth import (
22
+ _verify_user_belongs_to_group,
23
+ )
24
+ from fractal_server.logger import set_logger
25
+
26
+ logger = set_logger(__name__)
19
27
 
20
28
 
21
29
  async def _get_task_group_or_404(
@@ -211,6 +219,33 @@ async def _get_valid_user_group_id(
211
219
  return user_group_id
212
220
 
213
221
 
222
+ async def _get_collection_status_message(
223
+ task_group: TaskGroupV2, db: AsyncSession
224
+ ) -> str:
225
+ res = await db.execute(
226
+ select(CollectionStateV2).where(
227
+ CollectionStateV2.taskgroupv2_id == task_group.id
228
+ )
229
+ )
230
+ states = res.scalars().all()
231
+ if len(states) > 1:
232
+ msg = (
233
+ "Expected one CollectionStateV2 associated to TaskGroup "
234
+ f"{task_group.id}, found {len(states)} "
235
+ f"(IDs: {[state.id for state in states]}).\n"
236
+ "Warning: this should have not happened, please contact an admin."
237
+ )
238
+ elif len(states) == 1:
239
+ msg = (
240
+ f"\nThere exists a task collection state (ID={states[0].id}) for "
241
+ f"this task group (ID={task_group.id}), with status "
242
+ f"{states[0].data.get('status')}."
243
+ )
244
+ else:
245
+ msg = ""
246
+ return msg
247
+
248
+
214
249
  async def _verify_non_duplication_user_constraint(
215
250
  db: AsyncSession,
216
251
  user_id: int,
@@ -226,11 +261,24 @@ async def _verify_non_duplication_user_constraint(
226
261
  res = await db.execute(stm)
227
262
  duplicate = res.scalars().all()
228
263
  if duplicate:
264
+ user = await db.get(UserOAuth, user_id)
265
+ if len(duplicate) > 1:
266
+ raise HTTPException(
267
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
268
+ detail=(
269
+ "Invalid state:\n"
270
+ f"User '{user.email}' already owns {len(duplicate)} task "
271
+ f"groups with name='{pkg_name}' and {version=} "
272
+ f"(IDs: {[group.id for group in duplicate]}).\n"
273
+ "This should have not happened: please contact an admin."
274
+ ),
275
+ )
276
+ state_msg = await _get_collection_status_message(duplicate[0], db)
229
277
  raise HTTPException(
230
278
  status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
231
279
  detail=(
232
- "There is already a TaskGroupV2 with "
233
- f"({pkg_name=}, {version=}, {user_id=})."
280
+ f"User '{user.email}' already owns a task group "
281
+ f"with name='{pkg_name}' and {version=}.{state_msg}"
234
282
  ),
235
283
  )
236
284
 
@@ -253,11 +301,24 @@ async def _verify_non_duplication_group_constraint(
253
301
  res = await db.execute(stm)
254
302
  duplicate = res.scalars().all()
255
303
  if duplicate:
304
+ user_group = await db.get(UserGroup, user_group_id)
305
+ if len(duplicate) > 1:
306
+ raise HTTPException(
307
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
308
+ detail=(
309
+ "Invalid state:\n"
310
+ f"UserGroup '{user_group.name}' already owns "
311
+ f"{len(duplicate)} task groups with name='{pkg_name}' and "
312
+ f"{version=} (IDs: {[group.id for group in duplicate]}).\n"
313
+ "This should have not happened: please contact an admin."
314
+ ),
315
+ )
316
+ state_msg = await _get_collection_status_message(duplicate[0], db)
256
317
  raise HTTPException(
257
318
  status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
258
319
  detail=(
259
- "There is already a TaskGroupV2 with "
260
- f"({pkg_name=}, {version=}, {user_group_id=})."
320
+ f"UserGroup {user_group.name} already owns a task group "
321
+ f"with {pkg_name=} and {version=}.{state_msg}"
261
322
  ),
262
323
  )
263
324
 
@@ -31,7 +31,7 @@ from fractal_server.app.models import UserOAuth
31
31
  from fractal_server.app.routes.auth import current_active_user
32
32
  from fractal_server.app.routes.auth import current_active_verified_user
33
33
  from fractal_server.tasks.utils import _normalize_package_name
34
- from fractal_server.tasks.utils import get_collection_log
34
+ from fractal_server.tasks.utils import get_collection_log_v2
35
35
  from fractal_server.tasks.v2.background_operations import (
36
36
  background_collect_pip,
37
37
  )
@@ -70,33 +70,40 @@ async def collect_tasks_pip(
70
70
  # Get settings
71
71
  settings = Inject(get_settings)
72
72
 
73
+ # Initialize task-group attributes
74
+ task_group_attrs = dict(user_id=user.id)
75
+
73
76
  # Set/check python version
74
77
  if task_collect.python_version is None:
75
- task_collect.python_version = (
76
- settings.FRACTAL_TASKS_PYTHON_DEFAULT_VERSION
77
- )
78
+ task_group_attrs[
79
+ "python_version"
80
+ ] = settings.FRACTAL_TASKS_PYTHON_DEFAULT_VERSION
81
+ else:
82
+ task_group_attrs["python_version"] = task_collect.python_version
78
83
  try:
79
- get_python_interpreter_v2(python_version=task_collect.python_version)
84
+ get_python_interpreter_v2(
85
+ python_version=task_group_attrs["python_version"]
86
+ )
80
87
  except ValueError:
81
88
  raise HTTPException(
82
89
  status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
83
90
  detail=(
84
- f"Python version {task_collect.python_version} is "
91
+ f"Python version {task_group_attrs['python_version']} is "
85
92
  "not available for Fractal task collection."
86
93
  ),
87
94
  )
88
95
 
89
- # Populate task-group attributes
90
- task_group_attrs = dict(
91
- user_id=user.id,
92
- python_version=task_collect.python_version,
93
- )
96
+ # Set pip_extras
94
97
  if task_collect.package_extras is not None:
95
98
  task_group_attrs["pip_extras"] = task_collect.package_extras
99
+
100
+ # Set pinned_package_versions
96
101
  if task_collect.pinned_package_versions is not None:
97
102
  task_group_attrs[
98
103
  "pinned_package_versions"
99
104
  ] = task_collect.pinned_package_versions
105
+
106
+ # Set pkg_name, version, origin and wheel_path
100
107
  if task_collect.package.endswith(".whl"):
101
108
  try:
102
109
  task_group_attrs["wheel_path"] = task_collect.package
@@ -119,19 +126,11 @@ async def collect_tasks_pip(
119
126
  pkg_name = task_collect.package
120
127
  task_group_attrs["pkg_name"] = _normalize_package_name(pkg_name)
121
128
  task_group_attrs["origin"] = "pypi"
122
- if task_collect.package_version is None:
123
- latest_version = await get_package_version_from_pypi(
124
- task_collect.package
125
- )
126
- task_group_attrs["version"] = latest_version
127
- task_collect.package_version = latest_version
128
- else:
129
- task_group_attrs["version"] = task_collect.package_version
130
-
131
- # Validate user settings (backend-specific)
132
- user_settings = await validate_user_settings(
133
- user=user, backend=settings.FRACTAL_RUNNER_BACKEND, db=db
134
- )
129
+ latest_version = await get_package_version_from_pypi(
130
+ task_collect.package,
131
+ task_collect.package_version,
132
+ )
133
+ task_group_attrs["version"] = latest_version
135
134
 
136
135
  # Validate query parameters related to user-group ownership
137
136
  user_group_id = await _get_valid_user_group_id(
@@ -140,9 +139,16 @@ async def collect_tasks_pip(
140
139
  user_id=user.id,
141
140
  db=db,
142
141
  )
142
+
143
+ # Set user_group_id
143
144
  task_group_attrs["user_group_id"] = user_group_id
144
145
 
145
- # Construct task_group.path
146
+ # Validate user settings (backend-specific)
147
+ user_settings = await validate_user_settings(
148
+ user=user, backend=settings.FRACTAL_RUNNER_BACKEND, db=db
149
+ )
150
+
151
+ # Set path and venv_path
146
152
  if settings.FRACTAL_RUNNER_BACKEND == "slurm_ssh":
147
153
  base_tasks_path = user_settings.ssh_tasks_dir
148
154
  else:
@@ -165,6 +171,8 @@ async def collect_tasks_pip(
165
171
  detail=f"Invalid task-group object. Original error: {e}",
166
172
  )
167
173
 
174
+ # Database checks
175
+
168
176
  # Verify non-duplication constraints
169
177
  await _verify_non_duplication_user_constraint(
170
178
  user_id=user.id,
@@ -191,15 +199,18 @@ async def collect_tasks_pip(
191
199
  ),
192
200
  )
193
201
 
194
- # Verify that folder does not exist (for local collection)
202
+ # On-disk checks
203
+
195
204
  if settings.FRACTAL_RUNNER_BACKEND != "slurm_ssh":
205
+
206
+ # Verify that folder does not exist (for local collection)
196
207
  if Path(task_group_path).exists():
197
208
  raise HTTPException(
198
209
  status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
199
210
  detail=f"{task_group_path} already exists.",
200
211
  )
201
212
 
202
- if settings.FRACTAL_RUNNER_BACKEND != "slurm_ssh":
213
+ # Verify that wheel file exists
203
214
  wheel_path = task_group_attrs.get("wheel_path", None)
204
215
  if wheel_path is not None:
205
216
  if not Path(wheel_path).exists():
@@ -216,13 +227,15 @@ async def collect_tasks_pip(
216
227
  db.expunge(task_group)
217
228
 
218
229
  # All checks are OK, proceed with task collection
219
- collection_status = dict(
230
+ collection_state_data = dict(
220
231
  status=CollectionStatusV2.PENDING,
221
- venv_path=task_group_attrs["venv_path"],
222
- package=task_collect.package,
232
+ package=task_group.pkg_name,
233
+ version=task_group.version,
234
+ path=task_group.path,
235
+ venv_path=task_group.venv_path,
223
236
  )
224
237
  state = CollectionStateV2(
225
- data=collection_status, taskgroupv2_id=task_group.id
238
+ data=collection_state_data, taskgroupv2_id=task_group.id
226
239
  )
227
240
  db.add(state)
228
241
  await db.commit()
@@ -270,7 +283,7 @@ async def collect_tasks_pip(
270
283
  reset_logger_handlers(logger)
271
284
  info = (
272
285
  "Collecting tasks in the background. "
273
- f"GET /task/collect/{state.id} to query collection status"
286
+ f"GET /task/collect/{state.id}/ to query collection status"
274
287
  )
275
288
  state.data["info"] = info
276
289
  response.status_code = status.HTTP_201_CREATED
@@ -306,20 +319,17 @@ async def check_collection_status(
306
319
  else:
307
320
  # Non-SSH mode
308
321
  # In some cases (i.e. a successful or ongoing task collection),
309
- # state.data.log is not set; if so, we collect the current logs.
322
+ # state.data["log"] is not set; if so, we collect the current logs.
310
323
  if verbose and not state.data.get("log"):
311
- if "venv_path" not in state.data.keys():
324
+ if "path" not in state.data.keys():
312
325
  await db.close()
313
326
  raise HTTPException(
314
327
  status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
315
328
  detail=(
316
- f"No 'venv_path' in CollectionStateV2[{state_id}].data"
329
+ f"No 'path' in CollectionStateV2[{state_id}].data"
317
330
  ),
318
331
  )
319
- state.data["log"] = get_collection_log(
320
- Path(state.data["venv_path"])
321
- )
322
- state.data["venv_path"] = str(state.data["venv_path"])
332
+ state.data["log"] = get_collection_log_v2(Path(state.data["path"]))
323
333
 
324
334
  reset_logger_handlers(logger)
325
335
  await db.close()
@@ -143,12 +143,16 @@ async def patch_task_group(
143
143
  user_id=user.id,
144
144
  db=db,
145
145
  )
146
- await _verify_non_duplication_group_constraint(
147
- db=db,
148
- pkg_name=task_group.pkg_name,
149
- version=task_group.version,
150
- user_group_id=task_group_update.user_group_id,
151
- )
146
+ if (
147
+ "user_group_id" in task_group_update.dict(exclude_unset=True)
148
+ and task_group_update.user_group_id != task_group.user_group_id
149
+ ):
150
+ await _verify_non_duplication_group_constraint(
151
+ db=db,
152
+ pkg_name=task_group.pkg_name,
153
+ version=task_group.version,
154
+ user_group_id=task_group_update.user_group_id,
155
+ )
152
156
  for key, value in task_group_update.dict(exclude_unset=True).items():
153
157
  if (key == "user_group_id") and (value is not None):
154
158
  await _verify_user_belongs_to_group(
@@ -11,25 +11,21 @@ from ....db import AsyncSession
11
11
  from ....db import get_async_db
12
12
  from ....models.v2 import JobV2
13
13
  from ....models.v2 import ProjectV2
14
- from ....models.v2 import TaskV2
15
14
  from ....models.v2 import WorkflowV2
16
15
  from ....schemas.v2 import WorkflowCreateV2
17
16
  from ....schemas.v2 import WorkflowExportV2
18
- from ....schemas.v2 import WorkflowImportV2
19
17
  from ....schemas.v2 import WorkflowReadV2
20
18
  from ....schemas.v2 import WorkflowReadV2WithWarnings
21
- from ....schemas.v2 import WorkflowTaskCreateV2
22
19
  from ....schemas.v2 import WorkflowUpdateV2
23
20
  from ._aux_functions import _check_workflow_exists
24
21
  from ._aux_functions import _get_project_check_owner
25
22
  from ._aux_functions import _get_submitted_jobs_statement
26
23
  from ._aux_functions import _get_workflow_check_owner
27
- from ._aux_functions import _workflow_insert_task
28
24
  from ._aux_functions_tasks import _add_warnings_to_workflow_tasks
29
25
  from fractal_server.app.models import UserOAuth
26
+ from fractal_server.app.models.v2.task import TaskGroupV2
30
27
  from fractal_server.app.routes.auth import current_active_user
31
28
 
32
-
33
29
  router = APIRouter()
34
30
 
35
31
 
@@ -256,85 +252,21 @@ async def export_worfklow(
256
252
  user_id=user.id,
257
253
  db=db,
258
254
  )
259
- return workflow
260
-
261
-
262
- @router.post(
263
- "/project/{project_id}/workflow/import/",
264
- response_model=WorkflowReadV2,
265
- status_code=status.HTTP_201_CREATED,
266
- )
267
- async def import_workflow(
268
- project_id: int,
269
- workflow: WorkflowImportV2,
270
- user: UserOAuth = Depends(current_active_user),
271
- db: AsyncSession = Depends(get_async_db),
272
- ) -> Optional[WorkflowReadV2]:
273
- """
274
- Import an existing workflow into a project
275
-
276
- Also create all required objects (i.e. Workflow and WorkflowTask's) along
277
- the way.
278
- """
279
-
280
- # Preliminary checks
281
- await _get_project_check_owner(
282
- project_id=project_id,
283
- user_id=user.id,
284
- db=db,
285
- )
286
-
287
- await _check_workflow_exists(
288
- name=workflow.name, project_id=project_id, db=db
289
- )
290
-
291
- # Check that all required tasks are available
292
- source_to_id = {}
293
-
294
- for wf_task in workflow.task_list:
295
-
296
- source = wf_task.task.source
297
- if source not in source_to_id.keys():
298
- stm = select(TaskV2).where(TaskV2.source == source)
299
- tasks_by_source = (await db.execute(stm)).scalars().all()
300
- if len(tasks_by_source) != 1:
301
- raise HTTPException(
302
- status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
303
- detail=(
304
- f"Found {len(tasks_by_source)} tasks "
305
- f"with {source=}."
306
- ),
307
- )
308
- source_to_id[source] = tasks_by_source[0].id
309
-
310
- # Create new Workflow (with empty task_list)
311
- db_workflow = WorkflowV2(
312
- project_id=project_id,
313
- **workflow.dict(exclude_none=True, exclude={"task_list"}),
314
- )
315
- db.add(db_workflow)
316
- await db.commit()
317
- await db.refresh(db_workflow)
318
-
319
- # Insert tasks
320
-
321
- for wf_task in workflow.task_list:
322
- source = wf_task.task.source
323
- task_id = source_to_id[source]
324
-
325
- new_wf_task = WorkflowTaskCreateV2(
326
- **wf_task.dict(exclude_none=True, exclude={"task"})
327
- )
328
- # Insert task
329
- await _workflow_insert_task(
330
- **new_wf_task.dict(),
331
- workflow_id=db_workflow.id,
332
- task_id=task_id,
333
- db=db,
255
+ wf_task_list = []
256
+ for wftask in workflow.task_list:
257
+ task_group = await db.get(TaskGroupV2, wftask.task.taskgroupv2_id)
258
+ wf_task_list.append(wftask.dict())
259
+ wf_task_list[-1]["task"] = dict(
260
+ pkg_name=task_group.pkg_name,
261
+ version=task_group.version,
262
+ name=wftask.task.name,
334
263
  )
335
264
 
336
- await db.close()
337
- return db_workflow
265
+ wf = WorkflowExportV2(
266
+ **workflow.model_dump(),
267
+ task_list=wf_task_list,
268
+ )
269
+ return wf
338
270
 
339
271
 
340
272
  @router.get("/workflow/", response_model=list[WorkflowReadV2])