fractal-server 2.7.0a3__tar.gz → 2.7.0a5__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 (236) hide show
  1. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/PKG-INFO +1 -1
  2. fractal_server-2.7.0a5/fractal_server/__init__.py +1 -0
  3. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/__main__.py +3 -9
  4. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/models/v2/collection_state.py +1 -0
  5. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/models/v2/task.py +27 -3
  6. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/admin/v2/task.py +4 -17
  7. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/admin/v2/task_group.py +21 -0
  8. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/v1/task_collection.py +4 -4
  9. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/v2/_aux_functions.py +1 -7
  10. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/v2/_aux_functions_tasks.py +75 -2
  11. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/v2/task.py +16 -42
  12. fractal_server-2.7.0a5/fractal_server/app/routes/api/v2/task_collection.py +336 -0
  13. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/v2/task_collection_custom.py +31 -58
  14. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/v2/task_group.py +29 -1
  15. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/v2/workflow.py +11 -46
  16. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/v2/workflowtask.py +0 -1
  17. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/auth/_aux_auth.py +15 -12
  18. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/auth/group.py +46 -23
  19. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v2/task_interface.py +4 -9
  20. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/v2/dataset.py +2 -7
  21. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/v2/dumps.py +1 -2
  22. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/v2/job.py +1 -1
  23. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/v2/project.py +1 -1
  24. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/v2/task.py +5 -10
  25. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/v2/task_collection.py +8 -6
  26. fractal_server-2.7.0a5/fractal_server/app/schemas/v2/task_group.py +78 -0
  27. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/v2/workflow.py +2 -2
  28. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/v2/workflowtask.py +2 -5
  29. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/data_migrations/2_7_0.py +1 -11
  30. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/images/models.py +2 -4
  31. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/main.py +1 -1
  32. fractal_server-2.7.0a5/fractal_server/migrations/versions/034a469ec2eb_task_groups.py +184 -0
  33. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/string_tools.py +6 -2
  34. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/tasks/utils.py +19 -5
  35. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/tasks/v1/_TaskCollectPip.py +1 -1
  36. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/tasks/v1/background_operations.py +5 -5
  37. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/tasks/v1/get_collection_data.py +2 -2
  38. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/tasks/v2/_venv_pip.py +62 -70
  39. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/tasks/v2/background_operations.py +170 -51
  40. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/tasks/v2/background_operations_ssh.py +35 -77
  41. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/tasks/v2/database_operations.py +7 -17
  42. fractal_server-2.7.0a5/fractal_server/tasks/v2/endpoint_operations.py +124 -0
  43. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/tasks/v2/templates/_1_create_venv.sh +9 -5
  44. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/tasks/v2/utils.py +5 -0
  45. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/utils.py +3 -2
  46. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/pyproject.toml +2 -2
  47. fractal_server-2.7.0a3/fractal_server/__init__.py +0 -1
  48. fractal_server-2.7.0a3/fractal_server/app/routes/api/v2/task_collection.py +0 -365
  49. fractal_server-2.7.0a3/fractal_server/app/schemas/v2/task_group.py +0 -50
  50. fractal_server-2.7.0a3/fractal_server/migrations/versions/742b74e1cc6e_revamp_taskv2_and_taskgroupv2.py +0 -101
  51. fractal_server-2.7.0a3/fractal_server/migrations/versions/7cf1baae8fb4_task_group_v2.py +0 -66
  52. fractal_server-2.7.0a3/fractal_server/migrations/versions/df7cc3501bf7_linkusergroup_timestamp_created.py +0 -42
  53. fractal_server-2.7.0a3/fractal_server/tasks/v2/_TaskCollectPip.py +0 -132
  54. fractal_server-2.7.0a3/fractal_server/tasks/v2/endpoint_operations.py +0 -178
  55. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/LICENSE +0 -0
  56. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/README.md +0 -0
  57. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/alembic.ini +0 -0
  58. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/__init__.py +0 -0
  59. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/db/__init__.py +0 -0
  60. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/models/__init__.py +0 -0
  61. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/models/linkusergroup.py +0 -0
  62. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/models/linkuserproject.py +0 -0
  63. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/models/security.py +0 -0
  64. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/models/user_settings.py +0 -0
  65. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/models/v1/__init__.py +0 -0
  66. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/models/v1/dataset.py +0 -0
  67. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/models/v1/job.py +0 -0
  68. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/models/v1/project.py +0 -0
  69. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/models/v1/state.py +0 -0
  70. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/models/v1/task.py +0 -0
  71. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/models/v1/workflow.py +0 -0
  72. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/models/v2/__init__.py +0 -0
  73. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/models/v2/dataset.py +0 -0
  74. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/models/v2/job.py +0 -0
  75. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/models/v2/project.py +0 -0
  76. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/models/v2/workflow.py +0 -0
  77. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/models/v2/workflowtask.py +0 -0
  78. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/__init__.py +0 -0
  79. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/admin/__init__.py +0 -0
  80. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/admin/v1.py +0 -0
  81. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/admin/v2/__init__.py +0 -0
  82. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/admin/v2/job.py +0 -0
  83. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/admin/v2/project.py +0 -0
  84. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/__init__.py +0 -0
  85. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/v1/__init__.py +0 -0
  86. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/v1/_aux_functions.py +0 -0
  87. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/v1/dataset.py +0 -0
  88. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/v1/job.py +0 -0
  89. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/v1/project.py +0 -0
  90. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/v1/task.py +0 -0
  91. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/v1/workflow.py +0 -0
  92. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/v1/workflowtask.py +0 -0
  93. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/v2/__init__.py +0 -0
  94. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/v2/dataset.py +0 -0
  95. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/v2/images.py +0 -0
  96. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/v2/job.py +0 -0
  97. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/v2/project.py +0 -0
  98. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/v2/status.py +0 -0
  99. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/api/v2/submit.py +0 -0
  100. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/auth/__init__.py +0 -0
  101. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/auth/current_user.py +0 -0
  102. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/auth/login.py +0 -0
  103. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/auth/oauth.py +0 -0
  104. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/auth/register.py +0 -0
  105. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/auth/router.py +0 -0
  106. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/auth/users.py +0 -0
  107. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/aux/__init__.py +0 -0
  108. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/aux/_job.py +0 -0
  109. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/aux/_runner.py +0 -0
  110. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/routes/aux/validate_user_settings.py +0 -0
  111. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/.gitignore +0 -0
  112. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/__init__.py +0 -0
  113. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/async_wrap.py +0 -0
  114. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/components.py +0 -0
  115. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/compress_folder.py +0 -0
  116. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/exceptions.py +0 -0
  117. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/executors/__init__.py +0 -0
  118. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/executors/slurm/__init__.py +0 -0
  119. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/executors/slurm/_batching.py +0 -0
  120. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/executors/slurm/_slurm_config.py +0 -0
  121. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/executors/slurm/remote.py +0 -0
  122. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/executors/slurm/ssh/__init__.py +0 -0
  123. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/executors/slurm/ssh/_executor_wait_thread.py +0 -0
  124. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/executors/slurm/ssh/_slurm_job.py +0 -0
  125. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/executors/slurm/ssh/executor.py +0 -0
  126. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/executors/slurm/sudo/__init__.py +0 -0
  127. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/executors/slurm/sudo/_check_jobs_status.py +0 -0
  128. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/executors/slurm/sudo/_executor_wait_thread.py +0 -0
  129. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/executors/slurm/sudo/_subprocess_run_as_user.py +0 -0
  130. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/executors/slurm/sudo/executor.py +0 -0
  131. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/extract_archive.py +0 -0
  132. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/filenames.py +0 -0
  133. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/run_subprocess.py +0 -0
  134. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/set_start_and_last_task_index.py +0 -0
  135. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/shutdown.py +0 -0
  136. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/task_files.py +0 -0
  137. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v1/__init__.py +0 -0
  138. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v1/_common.py +0 -0
  139. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v1/_local/__init__.py +0 -0
  140. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v1/_local/_local_config.py +0 -0
  141. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v1/_local/_submit_setup.py +0 -0
  142. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v1/_local/executor.py +0 -0
  143. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v1/_slurm/__init__.py +0 -0
  144. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v1/_slurm/_submit_setup.py +0 -0
  145. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v1/_slurm/get_slurm_config.py +0 -0
  146. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v1/common.py +0 -0
  147. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v1/handle_failed_job.py +0 -0
  148. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v2/__init__.py +0 -0
  149. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v2/_local/__init__.py +0 -0
  150. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v2/_local/_local_config.py +0 -0
  151. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v2/_local/_submit_setup.py +0 -0
  152. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v2/_local/executor.py +0 -0
  153. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v2/_local_experimental/__init__.py +0 -0
  154. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v2/_local_experimental/_local_config.py +0 -0
  155. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v2/_local_experimental/_submit_setup.py +0 -0
  156. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v2/_local_experimental/executor.py +0 -0
  157. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v2/_slurm_common/__init__.py +0 -0
  158. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v2/_slurm_common/get_slurm_config.py +0 -0
  159. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v2/_slurm_ssh/__init__.py +0 -0
  160. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v2/_slurm_ssh/_submit_setup.py +0 -0
  161. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v2/_slurm_sudo/__init__.py +0 -0
  162. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v2/_slurm_sudo/_submit_setup.py +0 -0
  163. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v2/deduplicate_list.py +0 -0
  164. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v2/handle_failed_job.py +0 -0
  165. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v2/merge_outputs.py +0 -0
  166. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v2/runner.py +0 -0
  167. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v2/runner_functions.py +0 -0
  168. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/v2/runner_functions_low_level.py +0 -0
  169. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/runner/versions.py +0 -0
  170. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/__init__.py +0 -0
  171. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/_validators.py +0 -0
  172. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/user.py +0 -0
  173. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/user_group.py +0 -0
  174. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/user_settings.py +0 -0
  175. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/v1/__init__.py +0 -0
  176. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/v1/applyworkflow.py +0 -0
  177. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/v1/dataset.py +0 -0
  178. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/v1/dumps.py +0 -0
  179. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/v1/manifest.py +0 -0
  180. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/v1/project.py +0 -0
  181. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/v1/state.py +0 -0
  182. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/v1/task.py +0 -0
  183. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/v1/task_collection.py +0 -0
  184. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/v1/workflow.py +0 -0
  185. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/v2/__init__.py +0 -0
  186. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/v2/manifest.py +0 -0
  187. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/schemas/v2/status.py +0 -0
  188. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/security/__init__.py +0 -0
  189. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/app/user_settings.py +0 -0
  190. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/config.py +0 -0
  191. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/data_migrations/README.md +0 -0
  192. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/data_migrations/tools.py +0 -0
  193. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/gunicorn_fractal.py +0 -0
  194. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/images/__init__.py +0 -0
  195. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/images/tools.py +0 -0
  196. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/logger.py +0 -0
  197. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/README +0 -0
  198. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/env.py +0 -0
  199. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/naming_convention.py +0 -0
  200. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/script.py.mako +0 -0
  201. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/versions/091b01f51f88_add_usergroup_and_linkusergroup_table.py +0 -0
  202. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py +0 -0
  203. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/versions/4cedeb448a53_workflowtask_foreign_keys_not_nullables.py +0 -0
  204. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/versions/501961cfcd85_remove_link_between_v1_and_v2_tasks_.py +0 -0
  205. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/versions/50a13d6138fd_initial_schema.py +0 -0
  206. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/versions/5bf02391cfef_v2.py +0 -0
  207. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/versions/70e77f1c38b0_add_applyworkflow_first_task_index_and_.py +0 -0
  208. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/versions/71eefd1dd202_add_slurm_accounts.py +0 -0
  209. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/versions/84bf0fffde30_add_dumps_to_applyworkflow.py +0 -0
  210. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/versions/8f79bd162e35_add_docs_info_and_docs_link_to_task_.py +0 -0
  211. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/versions/94a47ea2d3ff_remove_cache_dir_slurm_user_and_slurm_.py +0 -0
  212. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/versions/97f444d47249_add_applyworkflow_project_dump.py +0 -0
  213. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/versions/99ea79d9e5d2_add_dataset_history.py +0 -0
  214. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/versions/9c5ae74c9b98_add_user_settings_table.py +0 -0
  215. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/versions/9fd26a2b0de4_add_workflow_timestamp_created.py +0 -0
  216. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/versions/a7f4d6137b53_add_workflow_dump_to_applyworkflow.py +0 -0
  217. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/versions/d4fe3708d309_make_applyworkflow_workflow_dump_non_.py +0 -0
  218. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/versions/da2cb2ac4255_user_group_viewer_paths.py +0 -0
  219. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/versions/e75cac726012_make_applyworkflow_start_timestamp_not_.py +0 -0
  220. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/versions/efa89c30e0a4_add_project_timestamp_created.py +0 -0
  221. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/migrations/versions/f384e1c0cf5d_drop_task_default_args_columns.py +0 -0
  222. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/py.typed +0 -0
  223. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/ssh/__init__.py +0 -0
  224. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/ssh/_fabric.py +0 -0
  225. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/syringe.py +0 -0
  226. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/tasks/__init__.py +0 -0
  227. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/tasks/v1/__init__.py +0 -0
  228. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/tasks/v1/endpoint_operations.py +0 -0
  229. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/tasks/v1/utils.py +0 -0
  230. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/tasks/v2/__init__.py +0 -0
  231. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/tasks/v2/templates/_2_upgrade_pip.sh +0 -0
  232. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/tasks/v2/templates/_3_pip_install.sh +0 -0
  233. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/tasks/v2/templates/_4_pip_freeze.sh +0 -0
  234. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/tasks/v2/templates/_5_pip_show.sh +0 -0
  235. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/fractal_server/urls.py +0 -0
  236. {fractal_server-2.7.0a3 → fractal_server-2.7.0a5}/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.0a3
3
+ Version: 2.7.0a5
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.0a5"
@@ -61,12 +61,6 @@ update_db_data_parser = subparsers.add_parser(
61
61
  "update-db-data",
62
62
  description="Apply data-migration script to an existing database.",
63
63
  )
64
- update_db_data_parser.add_argument(
65
- "--dry-run",
66
- action="store_true",
67
- help="If set, perform a dry run of the data migration.",
68
- default=False,
69
- )
70
64
 
71
65
 
72
66
  def save_openapi(dest="openapi.json"):
@@ -126,7 +120,7 @@ def set_db(skip_init_data: bool = False):
126
120
  print()
127
121
 
128
122
 
129
- def update_db_data(dry_run: bool = False):
123
+ def update_db_data():
130
124
  """
131
125
  Apply data migrations.
132
126
  """
@@ -191,7 +185,7 @@ def update_db_data(dry_run: bool = False):
191
185
  sys.exit()
192
186
 
193
187
  print("OK, now starting data-migration script\n")
194
- current_update_db_data_module.fix_db(dry_run=dry_run)
188
+ current_update_db_data_module.fix_db()
195
189
 
196
190
 
197
191
  def run():
@@ -202,7 +196,7 @@ def run():
202
196
  elif args.cmd == "set-db":
203
197
  set_db(skip_init_data=args.skip_init_data)
204
198
  elif args.cmd == "update-db-data":
205
- update_db_data(dry_run=args.dry_run)
199
+ update_db_data()
206
200
  elif args.cmd == "start":
207
201
  uvicorn.run(
208
202
  "fractal_server.main:app",
@@ -14,6 +14,7 @@ from ....utils import get_timestamp
14
14
  class CollectionStateV2(SQLModel, table=True):
15
15
 
16
16
  id: Optional[int] = Field(default=None, primary_key=True)
17
+ taskgroupv2_id: Optional[int] = Field(foreign_key="taskgroupv2.id")
17
18
  data: dict[str, Any] = Field(sa_column=Column(JSON), default={})
18
19
  timestamp: datetime = Field(
19
20
  default_factory=get_timestamp,
@@ -14,14 +14,13 @@ from fractal_server.utils import get_timestamp
14
14
 
15
15
 
16
16
  class TaskV2(SQLModel, table=True):
17
-
18
17
  id: Optional[int] = Field(default=None, primary_key=True)
19
18
  name: str
20
19
 
21
20
  type: str
22
21
  command_non_parallel: Optional[str] = None
23
22
  command_parallel: Optional[str] = None
24
- source: str = Field(unique=True)
23
+ source: Optional[str] = None
25
24
 
26
25
  meta_non_parallel: dict[str, Any] = Field(
27
26
  sa_column=Column(JSON, server_default="{}", default={}, nullable=False)
@@ -56,7 +55,6 @@ class TaskV2(SQLModel, table=True):
56
55
 
57
56
 
58
57
  class TaskGroupV2(SQLModel, table=True):
59
-
60
58
  id: Optional[int] = Field(default=None, primary_key=True)
61
59
  task_list: list[TaskV2] = Relationship(
62
60
  sa_relationship_kwargs=dict(
@@ -73,10 +71,36 @@ class TaskGroupV2(SQLModel, table=True):
73
71
  python_version: Optional[str] = None
74
72
  path: Optional[str] = None
75
73
  venv_path: Optional[str] = None
74
+ wheel_path: Optional[str] = None
76
75
  pip_extras: Optional[str] = None
76
+ pinned_package_versions: dict[str, str] = Field(
77
+ sa_column=Column(
78
+ JSON,
79
+ server_default="{}",
80
+ default={},
81
+ nullable=True,
82
+ ),
83
+ )
77
84
 
78
85
  active: bool = True
79
86
  timestamp_created: datetime = Field(
80
87
  default_factory=get_timestamp,
81
88
  sa_column=Column(DateTime(timezone=True), nullable=False),
82
89
  )
90
+
91
+ @property
92
+ def pip_install_string(self) -> str:
93
+ """
94
+ Prepare string to be used in `python -m pip install`.
95
+ """
96
+ extras = f"[{self.pip_extras}]" if self.pip_extras is not None else ""
97
+
98
+ if self.wheel_path is not None:
99
+ return f"{self.wheel_path}{extras}"
100
+ else:
101
+ if self.version is None:
102
+ raise ValueError(
103
+ "Cannot run `pip_install_string` with "
104
+ f"{self.pkg_name=}, {self.wheel_path=}, {self.version=}."
105
+ )
106
+ return f"{self.pkg_name}{extras}=={self.version}"
@@ -1,4 +1,3 @@
1
- from typing import Literal
2
1
  from typing import Optional
3
2
 
4
3
  from fastapi import APIRouter
@@ -26,11 +25,11 @@ class TaskV2Minimal(BaseModel):
26
25
  id: int
27
26
  name: str
28
27
  type: str
29
- command_non_parallel: Optional[str]
28
+ taskgroupv2_id: int
29
+ command_non_parallel: Optional[str] = None
30
30
  command_parallel: Optional[str]
31
- source: str
32
- owner: Optional[str]
33
- version: Optional[str]
31
+ source: Optional[str] = None
32
+ version: Optional[str] = None
34
33
 
35
34
 
36
35
  class ProjectUser(BaseModel):
@@ -60,8 +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
- kind: Optional[Literal["common", "users"]] = None,
65
62
  max_number_of_results: int = 25,
66
63
  user: UserOAuth = Depends(current_active_superuser),
67
64
  db: AsyncSession = Depends(get_async_db),
@@ -76,9 +73,6 @@ async def query_tasks(
76
73
  `task.source`.
77
74
  version: If not `None`, query for matching `task.version`.
78
75
  name: If not `None`, query for contained case insensitive `task.name`.
79
- owner: If not `None`, query for matching `task.owner`.
80
- kind: If not `None`, query for TaskV2s that have (`users`) or don't
81
- have (`common`) a `task.owner`.
82
76
  max_number_of_results: The maximum length of the response.
83
77
  """
84
78
 
@@ -92,13 +86,6 @@ async def query_tasks(
92
86
  stm = stm.where(TaskV2.version == version)
93
87
  if name is not None:
94
88
  stm = stm.where(TaskV2.name.icontains(name))
95
- if owner is not None:
96
- stm = stm.where(TaskV2.owner == owner)
97
-
98
- if kind == "common":
99
- stm = stm.where(TaskV2.owner == None) # noqa E711
100
- elif kind == "users":
101
- stm = stm.where(TaskV2.owner != None) # noqa E711
102
89
 
103
90
  res = await db.execute(stm)
104
91
  task_list = res.scalars().all()
@@ -12,6 +12,7 @@ from sqlmodel import select
12
12
  from fractal_server.app.db import AsyncSession
13
13
  from fractal_server.app.db import get_async_db
14
14
  from fractal_server.app.models import UserOAuth
15
+ from fractal_server.app.models.v2 import CollectionStateV2
15
16
  from fractal_server.app.models.v2 import TaskGroupV2
16
17
  from fractal_server.app.models.v2 import WorkflowTaskV2
17
18
  from fractal_server.app.routes.auth import current_active_superuser
@@ -20,9 +21,12 @@ from fractal_server.app.routes.auth._aux_auth import (
20
21
  )
21
22
  from fractal_server.app.schemas.v2 import TaskGroupReadV2
22
23
  from fractal_server.app.schemas.v2 import TaskGroupUpdateV2
24
+ from fractal_server.logger import set_logger
23
25
 
24
26
  router = APIRouter()
25
27
 
28
+ logger = set_logger(__name__)
29
+
26
30
 
27
31
  @router.get("/{task_group_id}/", response_model=TaskGroupReadV2)
28
32
  async def query_task_group(
@@ -128,6 +132,23 @@ async def delete_task_group(
128
132
  detail=f"TaskV2 {workflow_tasks[0].task_id} is still in use",
129
133
  )
130
134
 
135
+ # Cascade operations: set foreign-keys to null for CollectionStateV2 which
136
+ # are in relationship with the current TaskGroupV2
137
+ logger.debug("Start of cascade operations on CollectionStateV2.")
138
+ stm = select(CollectionStateV2).where(
139
+ CollectionStateV2.taskgroupv2_id == task_group_id
140
+ )
141
+ res = await db.execute(stm)
142
+ collection_states = res.scalars().all()
143
+ for collection_state in collection_states:
144
+ logger.debug(
145
+ f"Setting CollectionStateV2[{collection_state.id}].taskgroupv2_id "
146
+ "to None."
147
+ )
148
+ collection_state.taskgroupv2_id = None
149
+ db.add(collection_state)
150
+ logger.debug("End of cascade operations on CollectionStateV2.")
151
+
131
152
  await db.delete(task_group)
132
153
  await db.commit()
133
154
 
@@ -26,8 +26,8 @@ from ._aux_functions import _raise_if_v1_is_read_only
26
26
  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
- from fractal_server.string_tools import slugify_task_name_for_source
30
- from fractal_server.tasks.utils import get_collection_log
29
+ from fractal_server.string_tools import slugify_task_name_for_source_v1
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,
@@ -160,7 +160,7 @@ async def collect_tasks_pip(
160
160
 
161
161
  # Check that tasks are not already in the DB
162
162
  for new_task in task_pkg.package_manifest.task_list:
163
- new_task_name_slug = slugify_task_name_for_source(new_task.name)
163
+ new_task_name_slug = slugify_task_name_for_source_v1(new_task.name)
164
164
  new_task_source = f"{task_pkg.package_source}:{new_task_name_slug}"
165
165
  stm = select(Task).where(Task.source == new_task_source)
166
166
  res = await db.execute(stm)
@@ -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()
@@ -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
 
@@ -2,6 +2,7 @@
2
2
  Auxiliary functions to get task and task-group object from the database or
3
3
  perform simple checks
4
4
  """
5
+ from typing import Any
5
6
  from typing import Optional
6
7
 
7
8
  from fastapi import HTTPException
@@ -12,7 +13,8 @@ from ....db import AsyncSession
12
13
  from ....models import LinkUserGroup
13
14
  from ....models.v2 import TaskGroupV2
14
15
  from ....models.v2 import TaskV2
15
- from ...auth._aux_auth import _get_default_user_group_id
16
+ from ....models.v2 import WorkflowTaskV2
17
+ from ...auth._aux_auth import _get_default_usergroup_id
16
18
  from ...auth._aux_auth import _verify_user_belongs_to_group
17
19
 
18
20
 
@@ -201,9 +203,80 @@ async def _get_valid_user_group_id(
201
203
  elif private is True:
202
204
  user_group_id = None
203
205
  elif user_group_id is None:
204
- user_group_id = await _get_default_user_group_id(db=db)
206
+ user_group_id = await _get_default_usergroup_id(db=db)
205
207
  else:
206
208
  await _verify_user_belongs_to_group(
207
209
  user_id=user_id, user_group_id=user_group_id, db=db
208
210
  )
209
211
  return user_group_id
212
+
213
+
214
+ async def _verify_non_duplication_user_constraint(
215
+ db: AsyncSession,
216
+ user_id: int,
217
+ pkg_name: str,
218
+ version: Optional[str],
219
+ ):
220
+ stm = (
221
+ select(TaskGroupV2)
222
+ .where(TaskGroupV2.user_id == user_id)
223
+ .where(TaskGroupV2.pkg_name == pkg_name)
224
+ .where(TaskGroupV2.version == version)
225
+ )
226
+ res = await db.execute(stm)
227
+ duplicate = res.scalars().all()
228
+ if duplicate:
229
+ raise HTTPException(
230
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
231
+ detail=(
232
+ "There is already a TaskGroupV2 with "
233
+ f"({pkg_name=}, {version=}, {user_id=})."
234
+ ),
235
+ )
236
+
237
+
238
+ async def _verify_non_duplication_group_constraint(
239
+ db: AsyncSession,
240
+ user_group_id: Optional[int],
241
+ pkg_name: str,
242
+ version: Optional[str],
243
+ ):
244
+ if user_group_id is None:
245
+ return
246
+
247
+ stm = (
248
+ select(TaskGroupV2)
249
+ .where(TaskGroupV2.user_group_id == user_group_id)
250
+ .where(TaskGroupV2.pkg_name == pkg_name)
251
+ .where(TaskGroupV2.version == version)
252
+ )
253
+ res = await db.execute(stm)
254
+ duplicate = res.scalars().all()
255
+ if duplicate:
256
+ raise HTTPException(
257
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
258
+ detail=(
259
+ "There is already a TaskGroupV2 with "
260
+ f"({pkg_name=}, {version=}, {user_group_id=})."
261
+ ),
262
+ )
263
+
264
+
265
+ async def _add_warnings_to_workflow_tasks(
266
+ wftask_list: list[WorkflowTaskV2], user_id: int, db: AsyncSession
267
+ ) -> list[dict[str, Any]]:
268
+ wftask_list_with_warnings = []
269
+ for wftask in wftask_list:
270
+ wftask_data = dict(wftask.model_dump(), task=wftask.task)
271
+ try:
272
+ task_group = await _get_task_group_read_access(
273
+ task_group_id=wftask.task.taskgroupv2_id,
274
+ user_id=user_id,
275
+ db=db,
276
+ )
277
+ if not task_group.active:
278
+ wftask_data["warning"] = "Task is not active."
279
+ except HTTPException:
280
+ wftask_data["warning"] = "Current user has no access to this task."
281
+ wftask_list_with_warnings.append(wftask_data)
282
+ return wftask_list_with_warnings
@@ -10,15 +10,15 @@ from sqlmodel import func
10
10
  from sqlmodel import or_
11
11
  from sqlmodel import select
12
12
 
13
- from ...aux.validate_user_settings import verify_user_has_settings
14
13
  from ._aux_functions_tasks import _get_task_full_access
15
14
  from ._aux_functions_tasks import _get_task_read_access
16
15
  from ._aux_functions_tasks import _get_valid_user_group_id
16
+ from ._aux_functions_tasks import _verify_non_duplication_group_constraint
17
+ from ._aux_functions_tasks import _verify_non_duplication_user_constraint
17
18
  from fractal_server.app.db import AsyncSession
18
19
  from fractal_server.app.db import get_async_db
19
20
  from fractal_server.app.models import LinkUserGroup
20
21
  from fractal_server.app.models import UserOAuth
21
- from fractal_server.app.models.v1 import Task as TaskV1
22
22
  from fractal_server.app.models.v2 import TaskGroupV2
23
23
  from fractal_server.app.models.v2 import TaskV2
24
24
  from fractal_server.app.routes.auth import current_active_user
@@ -102,7 +102,7 @@ async def patch_task(
102
102
  db: AsyncSession = Depends(get_async_db),
103
103
  ) -> Optional[TaskReadV2]:
104
104
  """
105
- Edit a specific task (restricted to superusers and task owner)
105
+ Edit a specific task (restricted to task owner)
106
106
  """
107
107
 
108
108
  # Retrieve task from database
@@ -183,52 +183,26 @@ async def create_task(
183
183
  ),
184
184
  )
185
185
 
186
- # Set task.owner attribute - FIXME: remove this block
187
- if user.username:
188
- owner = user.username
189
- else:
190
- verify_user_has_settings(user)
191
- if user.settings.slurm_user:
192
- owner = user.settings.slurm_user
193
- else:
194
- raise HTTPException(
195
- status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
196
- detail=(
197
- "Cannot add a new task because current user does not "
198
- "have `username` or `slurm_user` attributes."
199
- ),
200
- )
201
-
202
- # Prepend owner to task.source
203
- task.source = f"{owner}:{task.source}"
204
-
205
- # Verify that source is not already in use (note: this check is only useful
206
- # to provide a user-friendly error message, but `task.source` uniqueness is
207
- # already guaranteed by a constraint in the table definition).
208
- stm = select(TaskV2).where(TaskV2.source == task.source)
209
- res = await db.execute(stm)
210
- if res.scalars().all():
211
- raise HTTPException(
212
- status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
213
- detail=f"Source '{task.source}' already used by some TaskV2",
214
- )
215
- stm = select(TaskV1).where(TaskV1.source == task.source)
216
- res = await db.execute(stm)
217
- if res.scalars().all():
218
- raise HTTPException(
219
- status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
220
- detail=f"Source '{task.source}' already used by some TaskV1",
221
- )
222
186
  # Add task
223
- db_task = TaskV2(**task.dict(), owner=owner, type=task_type)
224
-
187
+ db_task = TaskV2(**task.dict(), type=task_type)
188
+ pkg_name = db_task.name
189
+ await _verify_non_duplication_user_constraint(
190
+ db=db, pkg_name=pkg_name, user_id=user.id, version=db_task.version
191
+ )
192
+ await _verify_non_duplication_group_constraint(
193
+ db=db,
194
+ pkg_name=pkg_name,
195
+ user_group_id=user_group_id,
196
+ version=db_task.version,
197
+ )
225
198
  db_task_group = TaskGroupV2(
226
199
  user_id=user.id,
227
200
  user_group_id=user_group_id,
228
201
  active=True,
229
202
  task_list=[db_task],
230
203
  origin="other",
231
- pkg_name=task.name,
204
+ version=db_task.version,
205
+ pkg_name=pkg_name,
232
206
  )
233
207
  db.add(db_task_group)
234
208
  await db.commit()