fractal-server 2.16.6__tar.gz → 2.17.0a1__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 (269) hide show
  1. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/PKG-INFO +1 -1
  2. fractal_server-2.17.0a1/fractal_server/__init__.py +1 -0
  3. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/__main__.py +131 -22
  4. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/db/__init__.py +9 -11
  5. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/models/security.py +7 -3
  6. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/models/user_settings.py +0 -4
  7. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/models/v2/__init__.py +4 -0
  8. fractal_server-2.17.0a1/fractal_server/app/models/v2/profile.py +16 -0
  9. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/models/v2/project.py +3 -0
  10. fractal_server-2.17.0a1/fractal_server/app/models/v2/resource.py +130 -0
  11. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/models/v2/task_group.py +3 -0
  12. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/admin/v2/__init__.py +4 -0
  13. fractal_server-2.17.0a1/fractal_server/app/routes/admin/v2/_aux_functions.py +55 -0
  14. fractal_server-2.17.0a1/fractal_server/app/routes/admin/v2/profile.py +86 -0
  15. fractal_server-2.17.0a1/fractal_server/app/routes/admin/v2/resource.py +229 -0
  16. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/admin/v2/task_group_lifecycle.py +48 -82
  17. fractal_server-2.17.0a1/fractal_server/app/routes/api/__init__.py +47 -0
  18. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/_aux_functions.py +27 -1
  19. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/_aux_functions_history.py +2 -2
  20. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +3 -3
  21. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/_aux_functions_tasks.py +7 -7
  22. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/project.py +5 -1
  23. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/submit.py +32 -24
  24. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/task.py +5 -0
  25. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/task_collection.py +36 -47
  26. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/task_collection_custom.py +11 -5
  27. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/task_collection_pixi.py +34 -40
  28. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/task_group_lifecycle.py +39 -82
  29. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/auth/_aux_auth.py +3 -3
  30. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/auth/current_user.py +45 -7
  31. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/auth/oauth.py +1 -1
  32. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/auth/users.py +9 -0
  33. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/aux/_runner.py +2 -1
  34. fractal_server-2.17.0a1/fractal_server/app/routes/aux/validate_user_profile.py +62 -0
  35. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/aux/validate_user_settings.py +12 -9
  36. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/schemas/user.py +20 -13
  37. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/schemas/user_settings.py +0 -4
  38. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/schemas/v2/__init__.py +11 -0
  39. fractal_server-2.17.0a1/fractal_server/app/schemas/v2/profile.py +78 -0
  40. fractal_server-2.17.0a1/fractal_server/app/schemas/v2/resource.py +137 -0
  41. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/security/__init__.py +6 -13
  42. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/security/signup_email.py +2 -2
  43. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/user_settings.py +2 -12
  44. fractal_server-2.17.0a1/fractal_server/config/__init__.py +23 -0
  45. fractal_server-2.17.0a1/fractal_server/config/_database.py +58 -0
  46. fractal_server-2.17.0a1/fractal_server/config/_email.py +170 -0
  47. fractal_server-2.17.0a1/fractal_server/config/_init_data.py +27 -0
  48. fractal_server-2.17.0a1/fractal_server/config/_main.py +216 -0
  49. fractal_server-2.17.0a1/fractal_server/config/_settings_config.py +7 -0
  50. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/images/tools.py +3 -3
  51. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/logger.py +3 -3
  52. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/main.py +14 -21
  53. fractal_server-2.17.0a1/fractal_server/migrations/versions/90f6508c6379_drop_useroauth_username.py +36 -0
  54. fractal_server-2.17.0a1/fractal_server/migrations/versions/a80ac5a352bf_resource_profile.py +195 -0
  55. fractal_server-2.17.0a1/fractal_server/runner/config/__init__.py +2 -0
  56. fractal_server-2.17.0a1/fractal_server/runner/config/_local.py +21 -0
  57. fractal_server-2.17.0a1/fractal_server/runner/config/_slurm.py +128 -0
  58. fractal_server-2.17.0a1/fractal_server/runner/config/slurm_mem_to_MB.py +63 -0
  59. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/exceptions.py +4 -0
  60. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/executors/base_runner.py +17 -7
  61. fractal_server-2.17.0a1/fractal_server/runner/executors/local/get_local_config.py +51 -0
  62. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/executors/local/runner.py +48 -5
  63. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/executors/slurm_common/_batching.py +2 -2
  64. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/executors/slurm_common/base_slurm_runner.py +59 -25
  65. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/executors/slurm_common/get_slurm_config.py +38 -54
  66. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/executors/slurm_common/remote.py +1 -1
  67. fractal_server-2.16.6/fractal_server/runner/executors/slurm_common/_slurm_config.py → fractal_server-2.17.0a1/fractal_server/runner/executors/slurm_common/slurm_config.py +3 -254
  68. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/executors/slurm_common/slurm_job_task_models.py +1 -1
  69. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/executors/slurm_ssh/runner.py +12 -14
  70. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/executors/slurm_sudo/_subprocess_run_as_user.py +2 -2
  71. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/executors/slurm_sudo/runner.py +12 -12
  72. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/v2/_local.py +36 -21
  73. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/v2/_slurm_ssh.py +40 -4
  74. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/v2/_slurm_sudo.py +41 -11
  75. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/v2/db_tools.py +1 -1
  76. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/v2/runner.py +3 -11
  77. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/v2/runner_functions.py +42 -28
  78. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/v2/submit_workflow.py +87 -108
  79. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/versions.py +8 -3
  80. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/ssh/_fabric.py +6 -6
  81. fractal_server-2.17.0a1/fractal_server/tasks/config/__init__.py +3 -0
  82. fractal_server-2.17.0a1/fractal_server/tasks/config/_pixi.py +127 -0
  83. fractal_server-2.17.0a1/fractal_server/tasks/config/_python.py +51 -0
  84. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/local/_utils.py +7 -7
  85. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/local/collect.py +13 -5
  86. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/local/collect_pixi.py +26 -10
  87. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/local/deactivate.py +7 -1
  88. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/local/deactivate_pixi.py +5 -1
  89. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/local/delete.py +4 -0
  90. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/local/reactivate.py +13 -5
  91. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/local/reactivate_pixi.py +27 -9
  92. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/ssh/_pixi_slurm_ssh.py +11 -10
  93. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/ssh/_utils.py +6 -7
  94. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/ssh/collect.py +19 -12
  95. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/ssh/collect_pixi.py +34 -16
  96. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/ssh/deactivate.py +12 -8
  97. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/ssh/deactivate_pixi.py +14 -10
  98. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/ssh/delete.py +12 -9
  99. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/ssh/reactivate.py +18 -12
  100. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/ssh/reactivate_pixi.py +36 -17
  101. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/templates/4_pip_show.sh +4 -6
  102. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/utils_database.py +2 -2
  103. fractal_server-2.17.0a1/fractal_server/tasks/v2/utils_python_interpreter.py +25 -0
  104. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/utils_templates.py +7 -10
  105. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/utils.py +1 -1
  106. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/pyproject.toml +13 -2
  107. fractal_server-2.16.6/fractal_server/__init__.py +0 -1
  108. fractal_server-2.16.6/fractal_server/app/routes/api/__init__.py +0 -28
  109. fractal_server-2.16.6/fractal_server/config.py +0 -906
  110. fractal_server-2.16.6/fractal_server/runner/executors/local/get_local_config.py +0 -116
  111. fractal_server-2.16.6/fractal_server/tasks/v2/utils_python_interpreter.py +0 -33
  112. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/LICENSE +0 -0
  113. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/README.md +0 -0
  114. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/alembic.ini +0 -0
  115. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/__init__.py +0 -0
  116. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/models/__init__.py +0 -0
  117. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/models/linkusergroup.py +0 -0
  118. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/models/linkuserproject.py +0 -0
  119. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/models/v2/accounting.py +0 -0
  120. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/models/v2/dataset.py +0 -0
  121. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/models/v2/history.py +0 -0
  122. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/models/v2/job.py +0 -0
  123. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/models/v2/task.py +0 -0
  124. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/models/v2/workflow.py +0 -0
  125. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/models/v2/workflowtask.py +0 -0
  126. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/__init__.py +0 -0
  127. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/admin/__init__.py +0 -0
  128. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/admin/v2/accounting.py +0 -0
  129. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/admin/v2/impersonate.py +0 -0
  130. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/admin/v2/job.py +0 -0
  131. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/admin/v2/project.py +0 -0
  132. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/admin/v2/task.py +0 -0
  133. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/admin/v2/task_group.py +0 -0
  134. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/__init__.py +0 -0
  135. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/_aux_functions_task_version_update.py +0 -0
  136. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/_aux_task_group_disambiguation.py +0 -0
  137. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/dataset.py +0 -0
  138. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/history.py +0 -0
  139. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/images.py +0 -0
  140. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/job.py +0 -0
  141. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/pre_submission_checks.py +0 -0
  142. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/status_legacy.py +0 -0
  143. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/task_group.py +0 -0
  144. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/task_version_update.py +0 -0
  145. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/workflow.py +0 -0
  146. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/workflow_import.py +0 -0
  147. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/api/v2/workflowtask.py +0 -0
  148. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/auth/__init__.py +0 -0
  149. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/auth/group.py +0 -0
  150. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/auth/login.py +0 -0
  151. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/auth/register.py +0 -0
  152. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/auth/router.py +0 -0
  153. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/aux/__init__.py +0 -0
  154. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/aux/_job.py +0 -0
  155. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/routes/pagination.py +0 -0
  156. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/schemas/__init__.py +0 -0
  157. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/schemas/user_group.py +0 -0
  158. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/schemas/v2/accounting.py +0 -0
  159. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/schemas/v2/dataset.py +0 -0
  160. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/schemas/v2/dumps.py +0 -0
  161. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/schemas/v2/history.py +0 -0
  162. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/schemas/v2/job.py +0 -0
  163. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/schemas/v2/manifest.py +0 -0
  164. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/schemas/v2/project.py +0 -0
  165. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/schemas/v2/status_legacy.py +0 -0
  166. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/schemas/v2/task.py +0 -0
  167. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/schemas/v2/task_collection.py +0 -0
  168. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/schemas/v2/task_group.py +0 -0
  169. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/schemas/v2/workflow.py +0 -0
  170. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/app/schemas/v2/workflowtask.py +0 -0
  171. {fractal_server-2.16.6/fractal_server/runner → fractal_server-2.17.0a1/fractal_server/app}/shutdown.py +0 -0
  172. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/data_migrations/2_14_10.py +0 -0
  173. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/data_migrations/README.md +0 -0
  174. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/data_migrations/tools.py +0 -0
  175. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/exceptions.py +0 -0
  176. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/gunicorn_fractal.py +0 -0
  177. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/images/__init__.py +0 -0
  178. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/images/models.py +0 -0
  179. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/images/status_tools.py +0 -0
  180. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/env.py +0 -0
  181. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/naming_convention.py +0 -0
  182. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/034a469ec2eb_task_groups.py +0 -0
  183. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/091b01f51f88_add_usergroup_and_linkusergroup_table.py +0 -0
  184. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/0f5f85bb2ae7_add_pre_pinned_packages.py +0 -0
  185. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/19eca0dd47a9_user_settings_project_dir.py +0 -0
  186. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/1a83a5260664_rename.py +0 -0
  187. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/1eac13a26c83_drop_v1_tables.py +0 -0
  188. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/316140ff7ee1_remove_usersettings_cache_dir.py +0 -0
  189. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/47351f8c7ebc_drop_dataset_filters.py +0 -0
  190. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py +0 -0
  191. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/4cedeb448a53_workflowtask_foreign_keys_not_nullables.py +0 -0
  192. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/501961cfcd85_remove_link_between_v1_and_v2_tasks_.py +0 -0
  193. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/50a13d6138fd_initial_schema.py +0 -0
  194. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/5bf02391cfef_v2.py +0 -0
  195. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/70e77f1c38b0_add_applyworkflow_first_task_index_and_.py +0 -0
  196. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/71eefd1dd202_add_slurm_accounts.py +0 -0
  197. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/791ce783d3d8_add_indices.py +0 -0
  198. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/84bf0fffde30_add_dumps_to_applyworkflow.py +0 -0
  199. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/8e8f227a3e36_update_taskv2_post_2_7_0.py +0 -0
  200. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/8f79bd162e35_add_docs_info_and_docs_link_to_task_.py +0 -0
  201. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/94a47ea2d3ff_remove_cache_dir_slurm_user_and_slurm_.py +0 -0
  202. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/969d84257cac_add_historyrun_task_id.py +0 -0
  203. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/97f444d47249_add_applyworkflow_project_dump.py +0 -0
  204. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/981d588fe248_add_executor_error_log.py +0 -0
  205. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/99ea79d9e5d2_add_dataset_history.py +0 -0
  206. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/9c5ae74c9b98_add_user_settings_table.py +0 -0
  207. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/9db60297b8b2_set_ondelete.py +0 -0
  208. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/9fd26a2b0de4_add_workflow_timestamp_created.py +0 -0
  209. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/a7f4d6137b53_add_workflow_dump_to_applyworkflow.py +0 -0
  210. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/af1ef1c83c9b_add_accounting_tables.py +0 -0
  211. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/af8673379a5c_drop_old_filter_columns.py +0 -0
  212. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/b1e7f7a1ff71_task_group_for_pixi.py +0 -0
  213. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/b3ffb095f973_json_to_jsonb.py +0 -0
  214. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/c90a7c76e996_job_id_in_history_run.py +0 -0
  215. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/d256a7379ab8_taskgroup_activity_and_venv_info_to_.py +0 -0
  216. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/d4fe3708d309_make_applyworkflow_workflow_dump_non_.py +0 -0
  217. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/da2cb2ac4255_user_group_viewer_paths.py +0 -0
  218. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/db09233ad13a_split_filters_and_keep_old_columns.py +0 -0
  219. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/e75cac726012_make_applyworkflow_start_timestamp_not_.py +0 -0
  220. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/e81103413827_add_job_type_filters.py +0 -0
  221. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/efa89c30e0a4_add_project_timestamp_created.py +0 -0
  222. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/f37aceb45062_make_historyunit_logfile_required.py +0 -0
  223. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/f384e1c0cf5d_drop_task_default_args_columns.py +0 -0
  224. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/migrations/versions/fbce16ff4e47_new_history_items.py +0 -0
  225. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/py.typed +0 -0
  226. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/__init__.py +0 -0
  227. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/components.py +0 -0
  228. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/executors/__init__.py +0 -0
  229. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/executors/call_command_wrapper.py +0 -0
  230. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/executors/local/__init__.py +0 -0
  231. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/executors/slurm_common/__init__.py +0 -0
  232. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/executors/slurm_common/_job_states.py +0 -0
  233. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/executors/slurm_ssh/__init__.py +0 -0
  234. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/executors/slurm_ssh/run_subprocess.py +0 -0
  235. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/executors/slurm_ssh/tar_commands.py +0 -0
  236. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/executors/slurm_sudo/__init__.py +0 -0
  237. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/filenames.py +0 -0
  238. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/set_start_and_last_task_index.py +0 -0
  239. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/task_files.py +0 -0
  240. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/v2/__init__.py +0 -0
  241. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/v2/deduplicate_list.py +0 -0
  242. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/v2/merge_outputs.py +0 -0
  243. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/runner/v2/task_interface.py +0 -0
  244. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/ssh/__init__.py +0 -0
  245. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/string_tools.py +0 -0
  246. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/syringe.py +0 -0
  247. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/__init__.py +0 -0
  248. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/utils.py +0 -0
  249. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/__init__.py +0 -0
  250. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/local/__init__.py +0 -0
  251. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/ssh/__init__.py +0 -0
  252. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/templates/1_create_venv.sh +0 -0
  253. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/templates/2_pip_install.sh +0 -0
  254. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/templates/3_pip_freeze.sh +0 -0
  255. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/templates/5_get_venv_size_and_file_number.sh +0 -0
  256. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/templates/6_pip_install_from_freeze.sh +0 -0
  257. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/templates/pixi_1_extract.sh +0 -0
  258. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/templates/pixi_2_install.sh +0 -0
  259. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/templates/pixi_3_post_install.sh +0 -0
  260. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/utils_background.py +0 -0
  261. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/utils_package_names.py +0 -0
  262. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/tasks/v2/utils_pixi.py +0 -0
  263. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/types/__init__.py +0 -0
  264. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/types/validators/__init__.py +0 -0
  265. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/types/validators/_common_validators.py +0 -0
  266. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/types/validators/_filter_validators.py +0 -0
  267. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/types/validators/_workflow_task_arguments_validators.py +0 -0
  268. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/urls.py +0 -0
  269. {fractal_server-2.16.6 → fractal_server-2.17.0a1}/fractal_server/zip_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fractal-server
3
- Version: 2.16.6
3
+ Version: 2.17.0a1
4
4
  Summary: Backend component of the Fractal analytics platform
5
5
  License-Expression: BSD-3-Clause
6
6
  License-File: LICENSE
@@ -0,0 +1 @@
1
+ __VERSION__ = "2.17.0a1"
@@ -2,9 +2,12 @@ import argparse as ap
2
2
  import asyncio
3
3
  import json
4
4
  import sys
5
+ from pathlib import Path
5
6
 
6
7
  import uvicorn
8
+ from pydantic import ValidationError
7
9
 
10
+ from fractal_server.app.schemas.v2 import ResourceType
8
11
 
9
12
  parser = ap.ArgumentParser(description="fractal-server commands")
10
13
 
@@ -50,11 +53,23 @@ set_db_parser = subparsers.add_parser(
50
53
  "Initialise/upgrade database schemas and create first group&user."
51
54
  ),
52
55
  )
53
- set_db_parser.add_argument(
54
- "--skip-init-data",
55
- action="store_true",
56
- help="If set, do not try creating first group and user.",
57
- default=False,
56
+
57
+ # fractalctl init-db-data
58
+ init_db_data_parser = subparsers.add_parser(
59
+ "init-db-data",
60
+ description="Populate database with initial data.",
61
+ )
62
+ init_db_data_parser.add_argument(
63
+ "--resource",
64
+ type=str,
65
+ help=("Either `default` or path to the JSON file of the first resource."),
66
+ required=True,
67
+ )
68
+ init_db_data_parser.add_argument(
69
+ "--profile",
70
+ type=str,
71
+ help=("Either `default` or path to the JSON file of the first profile."),
72
+ required=True,
58
73
  )
59
74
 
60
75
  # fractalctl update-db-data
@@ -83,28 +98,22 @@ def save_openapi(dest="openapi.json"):
83
98
  json.dump(openapi_schema, f)
84
99
 
85
100
 
86
- def set_db(skip_init_data: bool = False):
101
+ def set_db():
87
102
  """
88
- Upgrade database schema *and* create first group/user
103
+ Upgrade database schemas.
89
104
 
90
105
  Call alembic to upgrade to the latest migration.
91
106
  Ref: https://stackoverflow.com/a/56683030/283972
92
-
93
- Arguments:
94
- skip_init_data: If `True`, skip creation of first group and user.
95
107
  """
96
- from fractal_server.app.security import _create_first_user
97
- from fractal_server.app.security import _create_first_group
98
108
  from fractal_server.syringe import Inject
99
- from fractal_server.config import get_settings
109
+ from fractal_server.config import get_db_settings
100
110
 
101
111
  import alembic.config
102
112
  from pathlib import Path
103
113
  import fractal_server
104
114
 
105
- # Check settings
106
- settings = Inject(get_settings)
107
- settings.check_db()
115
+ # Validate DB settings
116
+ Inject(get_db_settings)
108
117
 
109
118
  # Perform migrations
110
119
  alembic_ini = Path(fractal_server.__file__).parent / "alembic.ini"
@@ -113,8 +122,24 @@ def set_db(skip_init_data: bool = False):
113
122
  alembic.config.main(argv=alembic_args)
114
123
  print("END: alembic.config")
115
124
 
116
- if skip_init_data:
117
- return
125
+
126
+ def init_db_data(
127
+ *,
128
+ resource: str,
129
+ profile: str,
130
+ ) -> None:
131
+ from fractal_server.app.security import _create_first_user
132
+ from fractal_server.app.security import _create_first_group
133
+ from fractal_server.syringe import Inject
134
+ from fractal_server.config import get_init_data_settings
135
+ from fractal_server.app.db import get_sync_db
136
+ from sqlalchemy import select, func
137
+ from fractal_server.app.models.security import UserOAuth
138
+ from fractal_server.app.models import Resource, Profile
139
+ from fractal_server.app.schemas.v2.resource import cast_serialize_resource
140
+ from fractal_server.app.schemas.v2.profile import cast_serialize_profile
141
+
142
+ init_data_settings = Inject(get_init_data_settings)
118
143
 
119
144
  # Create default group and user
120
145
  print()
@@ -122,17 +147,96 @@ def set_db(skip_init_data: bool = False):
122
147
  print()
123
148
  asyncio.run(
124
149
  _create_first_user(
125
- email=settings.FRACTAL_DEFAULT_ADMIN_EMAIL,
150
+ email=init_data_settings.FRACTAL_DEFAULT_ADMIN_EMAIL,
126
151
  password=(
127
- settings.FRACTAL_DEFAULT_ADMIN_PASSWORD.get_secret_value()
152
+ init_data_settings.FRACTAL_DEFAULT_ADMIN_PASSWORD.get_secret_value() # noqa E501
128
153
  ),
129
- username=settings.FRACTAL_DEFAULT_ADMIN_USERNAME,
130
154
  is_superuser=True,
131
155
  is_verified=True,
132
156
  )
133
157
  )
134
158
  print()
135
159
 
160
+ # Create resource and profile
161
+ with next(get_sync_db()) as db:
162
+ # Preliminary check
163
+ num_resources = db.execute(select(func.count(Resource.id))).scalar()
164
+ if num_resources != 0:
165
+ print(f"There exist already {num_resources=} resources. Exit.")
166
+ sys.exit(1)
167
+
168
+ # Get resource/profile data
169
+ if resource == "default":
170
+ _python_version = (
171
+ f"{sys.version_info.major}.{sys.version_info.minor}"
172
+ )
173
+ resource_data = {
174
+ "name": "Local resource",
175
+ "type": ResourceType.LOCAL,
176
+ "jobs_local_dir": (Path.cwd() / "data-jobs").as_posix(),
177
+ "tasks_local_dir": (Path.cwd() / "data-tasks").as_posix(),
178
+ "tasks_python_config": {
179
+ "default_version": _python_version,
180
+ "versions": {
181
+ _python_version: sys.executable,
182
+ },
183
+ },
184
+ "jobs_poll_interval": 0,
185
+ "jobs_runner_config": {},
186
+ "tasks_pixi_config": {},
187
+ }
188
+ print("Prepared default resource data.")
189
+ else:
190
+ with open(resource) as f:
191
+ resource_data = json.load(f)
192
+ print(f"Read resource data from {resource}.")
193
+ if profile == "default":
194
+ profile_data = {
195
+ "resource_type": "local",
196
+ "name": "Local profile",
197
+ }
198
+ print("Prepared default profile data.")
199
+ else:
200
+ with open(profile) as f:
201
+ profile_data = json.load(f)
202
+ print(f"Read profile data from {profile}.")
203
+
204
+ # Validate resource/profile data
205
+ try:
206
+ resource_data = cast_serialize_resource(resource_data)
207
+ except ValidationError as e:
208
+ sys.exit(
209
+ f"ERROR: Invalid resource data.\nOriginal error:\n{str(e)}"
210
+ )
211
+ try:
212
+ profile_data = cast_serialize_profile(profile_data)
213
+ except ValidationError as e:
214
+ sys.exit(
215
+ f"ERROR: Invalid profile data.\nOriginal error:\n{str(e)}"
216
+ )
217
+
218
+ # Create resource/profile db objects
219
+ resource_obj = Resource(**resource_data)
220
+ db.add(resource_obj)
221
+ db.commit()
222
+ db.refresh(resource_obj)
223
+ profile_data["resource_id"] = resource_obj.id
224
+ profile_obj = Profile(**profile_data)
225
+ db.add(profile_obj)
226
+ db.commit()
227
+ db.refresh(profile_obj)
228
+
229
+ # Associate profile to users
230
+ res = db.execute(select(UserOAuth))
231
+ users = res.unique().scalars().all()
232
+ for user in users:
233
+ print(f"Now set profile_id={profile_obj.id} for {user.email}.")
234
+ user.profile_id = profile_obj.id
235
+ db.add(user)
236
+ db.commit()
237
+ db.expunge_all()
238
+ print()
239
+
136
240
 
137
241
  def update_db_data():
138
242
  """
@@ -219,7 +323,12 @@ def run():
219
323
  if args.cmd == "openapi":
220
324
  save_openapi(dest=args.openapi_file)
221
325
  elif args.cmd == "set-db":
222
- set_db(skip_init_data=args.skip_init_data)
326
+ set_db()
327
+ elif args.cmd == "init-db-data":
328
+ init_db_data(
329
+ resource=args.resource,
330
+ profile=args.profile,
331
+ )
223
332
  elif args.cmd == "update-db-data":
224
333
  update_db_data()
225
334
  elif args.cmd == "start":
@@ -11,9 +11,9 @@ from sqlalchemy.ext.asyncio import create_async_engine
11
11
  from sqlalchemy.orm import Session as DBSyncSession
12
12
  from sqlalchemy.orm import sessionmaker
13
13
 
14
- from ...config import get_settings
15
- from ...logger import set_logger
16
- from ...syringe import Inject
14
+ from fractal_server.config import get_db_settings
15
+ from fractal_server.logger import set_logger
16
+ from fractal_server.syringe import Inject
17
17
 
18
18
 
19
19
  logger = set_logger(__name__)
@@ -42,12 +42,11 @@ class DB:
42
42
 
43
43
  @classmethod
44
44
  def set_async_db(cls):
45
- settings = Inject(get_settings)
46
- settings.check_db()
45
+ db_settings = Inject(get_db_settings)
47
46
 
48
47
  cls._engine_async = create_async_engine(
49
- settings.DATABASE_ASYNC_URL,
50
- echo=settings.DB_ECHO,
48
+ db_settings.DATABASE_URL,
49
+ echo=db_settings.DB_ECHO,
51
50
  future=True,
52
51
  pool_pre_ping=True,
53
52
  )
@@ -60,12 +59,11 @@ class DB:
60
59
 
61
60
  @classmethod
62
61
  def set_sync_db(cls):
63
- settings = Inject(get_settings)
64
- settings.check_db()
62
+ db_settings = Inject(get_db_settings)
65
63
 
66
64
  cls._engine_sync = create_engine(
67
- settings.DATABASE_SYNC_URL,
68
- echo=settings.DB_ECHO,
65
+ db_settings.DATABASE_URL,
66
+ echo=db_settings.DB_ECHO,
69
67
  future=True,
70
68
  pool_pre_ping=True,
71
69
  )
@@ -73,8 +73,9 @@ class UserOAuth(SQLModel, table=True):
73
73
  is_active:
74
74
  is_superuser:
75
75
  is_verified:
76
- username:
77
76
  oauth_accounts:
77
+ user_settings_id:
78
+ profile_id:
78
79
  settings:
79
80
  """
80
81
 
@@ -90,8 +91,6 @@ class UserOAuth(SQLModel, table=True):
90
91
  is_superuser: bool = Field(default=False, nullable=False)
91
92
  is_verified: bool = Field(default=False, nullable=False)
92
93
 
93
- username: str | None = None
94
-
95
94
  oauth_accounts: list["OAuthAccount"] = Relationship(
96
95
  back_populates="user",
97
96
  sa_relationship_kwargs={"lazy": "joined", "cascade": "all, delete"},
@@ -100,6 +99,11 @@ class UserOAuth(SQLModel, table=True):
100
99
  user_settings_id: int | None = Field(
101
100
  foreign_key="user_settings.id", default=None
102
101
  )
102
+ profile_id: int | None = Field(
103
+ foreign_key="profile.id",
104
+ default=None,
105
+ ondelete="SET NULL",
106
+ )
103
107
  settings: UserSettings | None = Relationship(
104
108
  sa_relationship_kwargs=dict(lazy="selectin", cascade="all, delete")
105
109
  )
@@ -15,8 +15,6 @@ class UserSettings(SQLModel, table=True):
15
15
  ssh_host: SSH-reachable host where a SLURM client is available.
16
16
  ssh_username: User on `ssh_host`.
17
17
  ssh_private_key_path: Path of private SSH key for `ssh_username`.
18
- ssh_tasks_dir: Task-venvs base folder on `ssh_host`.
19
- ssh_jobs_dir: Jobs base folder on `ssh_host`.
20
18
  slurm_user: Local user, to be impersonated via `sudo -u`
21
19
  project_dir: Folder where `slurm_user` can write.
22
20
  """
@@ -30,7 +28,5 @@ class UserSettings(SQLModel, table=True):
30
28
  ssh_host: str | None = None
31
29
  ssh_username: str | None = None
32
30
  ssh_private_key_path: str | None = None
33
- ssh_tasks_dir: str | None = None
34
- ssh_jobs_dir: str | None = None
35
31
  slurm_user: str | None = None
36
32
  project_dir: str | None = None
@@ -9,7 +9,9 @@ from .history import HistoryImageCache
9
9
  from .history import HistoryRun
10
10
  from .history import HistoryUnit
11
11
  from .job import JobV2
12
+ from .profile import Profile
12
13
  from .project import ProjectV2
14
+ from .resource import Resource
13
15
  from .task import TaskV2
14
16
  from .task_group import TaskGroupActivityV2
15
17
  from .task_group import TaskGroupV2
@@ -31,4 +33,6 @@ __all__ = [
31
33
  "HistoryRun",
32
34
  "HistoryUnit",
33
35
  "HistoryImageCache",
36
+ "Resource",
37
+ "Profile",
34
38
  ]
@@ -0,0 +1,16 @@
1
+ from sqlmodel import Field
2
+ from sqlmodel import SQLModel
3
+
4
+
5
+ class Profile(SQLModel, table=True):
6
+ id: int | None = Field(default=None, primary_key=True)
7
+ resource_id: int = Field(foreign_key="resource.id", ondelete="CASCADE")
8
+ resource_type: str
9
+
10
+ name: str = Field(unique=True)
11
+
12
+ username: str | None = None
13
+ ssh_key_path: str | None = None
14
+
15
+ jobs_remote_dir: str | None = None
16
+ tasks_remote_dir: str | None = None
@@ -14,6 +14,9 @@ from fractal_server.utils import get_timestamp
14
14
  class ProjectV2(SQLModel, table=True):
15
15
  id: int | None = Field(default=None, primary_key=True)
16
16
  name: str
17
+ resource_id: int | None = Field(
18
+ foreign_key="resource.id", default=None, ondelete="SET NULL"
19
+ )
17
20
  timestamp_created: datetime = Field(
18
21
  default_factory=get_timestamp,
19
22
  sa_column=Column(DateTime(timezone=True), nullable=False),
@@ -0,0 +1,130 @@
1
+ from datetime import datetime
2
+ from typing import Any
3
+ from typing import Self
4
+
5
+ from sqlalchemy import Column
6
+ from sqlalchemy.dialects.postgresql import JSONB
7
+ from sqlalchemy.types import DateTime
8
+ from sqlmodel import CheckConstraint
9
+ from sqlmodel import Field
10
+ from sqlmodel import SQLModel
11
+
12
+ from fractal_server.utils import get_timestamp
13
+
14
+
15
+ class Resource(SQLModel, table=True):
16
+ id: int | None = Field(default=None, primary_key=True)
17
+
18
+ type: str
19
+ """
20
+ One of `local`, `slurm_sudo` or `slurm_ssh` - matching with
21
+ `settings.FRACTAL_RUNNER_BACKEND`.
22
+ """
23
+
24
+ name: str = Field(unique=True)
25
+ """
26
+ Resource name.
27
+ """
28
+
29
+ timestamp_created: datetime = Field(
30
+ default_factory=get_timestamp,
31
+ sa_column=Column(DateTime(timezone=True), nullable=False),
32
+ )
33
+ """
34
+ Creation timestamp (autogenerated).
35
+ """
36
+
37
+ host: str | None = None
38
+ """
39
+ Address for ssh connections, when `type="slurm_ssh"`.
40
+ """
41
+
42
+ jobs_local_dir: str
43
+ """
44
+ Base local folder for job subfolders (containing artifacts and logs).
45
+ """
46
+
47
+ jobs_runner_config: dict[str, Any] = Field(
48
+ sa_column=Column(JSONB, nullable=False, server_default="{}")
49
+ )
50
+ """
51
+ Runner configuration, matching one of `JobRunnerConfigLocal` or
52
+ `JobRunnerConfigSLURM` schemas.
53
+ """
54
+
55
+ jobs_slurm_python_worker: str | None = None
56
+ """
57
+ On SLURM deloyments, this is the Python interpreter that runs the
58
+ `fractal-server` worker from within the SLURM jobs.
59
+ """
60
+
61
+ jobs_poll_interval: int
62
+ """
63
+ On SLURM resources: the interval to wait before new `squeue` calls.
64
+ On local resources: ignored.
65
+ """
66
+
67
+ # task_settings
68
+ tasks_local_dir: str
69
+ """
70
+ Base local folder for task-package subfolders.
71
+ """
72
+
73
+ tasks_python_config: dict[str, Any] = Field(
74
+ sa_column=Column(JSONB, nullable=False, server_default="{}")
75
+ )
76
+ """
77
+ Python configuration for task collection. Example:
78
+ ```json
79
+ {
80
+ "default_version": "3.10",
81
+ "versions:{
82
+ "3.10": "/xxx/venv-3.10/bin/python",
83
+ "3.11": "/xxx/venv-3.11/bin/python",
84
+ "3.12": "/xxx/venv-3.12/bin/python"
85
+ }
86
+ }
87
+ ```
88
+ """
89
+
90
+ tasks_pixi_config: dict[str, Any] = Field(
91
+ sa_column=Column(JSONB, nullable=False, server_default="{}")
92
+ )
93
+ """
94
+ Pixi configuration for task collection. Basic example:
95
+ ```json
96
+ {
97
+ "default_version": "0.41.0",
98
+ "versions": {
99
+ "0.40.0": "/xxx/pixi/0.40.0/",
100
+ "0.41.0": "/xxx/pixi/0.41.0/"
101
+ },
102
+ }
103
+ ```
104
+ """
105
+
106
+ @property
107
+ def pip_cache_dir_arg(self: Self) -> str:
108
+ """
109
+ If `pip_cache_dir` is set (in `self.tasks_python_config`), then
110
+ return `--cache_dir /something`; else return `--no-cache-dir`.
111
+ """
112
+ _pip_cache_dir = self.tasks_python_config.get("pip_cache_dir", None)
113
+ if _pip_cache_dir is not None:
114
+ return f"--cache-dir {_pip_cache_dir}"
115
+ else:
116
+ return "--no-cache-dir"
117
+
118
+ # Check constraints
119
+ __table_args__ = (
120
+ # `type` column must be one of "local", "slurm_sudo" or "slurm_ssh"
121
+ CheckConstraint(
122
+ "type IN ('local', 'slurm_sudo', 'slurm_ssh')",
123
+ name="ck_resource_correct_type",
124
+ ),
125
+ # If `type` is not "local", `jobs_slurm_python_worker` must be set
126
+ CheckConstraint(
127
+ "(type = 'local') OR (jobs_slurm_python_worker IS NOT NULL)",
128
+ name="ck_resource_jobs_slurm_python_worker_set",
129
+ ),
130
+ )
@@ -42,6 +42,9 @@ class TaskGroupV2(SQLModel, table=True):
42
42
  user_group_id: int | None = Field(
43
43
  foreign_key="usergroup.id", default=None, ondelete="SET NULL"
44
44
  )
45
+ resource_id: int | None = Field(
46
+ foreign_key="resource.id", default=None, ondelete="SET NULL"
47
+ )
45
48
 
46
49
  origin: str
47
50
  pkg_name: str
@@ -6,7 +6,9 @@ from fastapi import APIRouter
6
6
  from .accounting import router as accounting_router
7
7
  from .impersonate import router as impersonate_router
8
8
  from .job import router as job_router
9
+ from .profile import router as profile_router
9
10
  from .project import router as project_router
11
+ from .resource import router as resource_router
10
12
  from .task import router as task_router
11
13
  from .task_group import router as task_group_router
12
14
  from .task_group_lifecycle import router as task_group_lifecycle_router
@@ -22,3 +24,5 @@ router_admin_v2.include_router(
22
24
  task_group_lifecycle_router, prefix="/task-group"
23
25
  )
24
26
  router_admin_v2.include_router(impersonate_router, prefix="/impersonate")
27
+ router_admin_v2.include_router(resource_router, prefix="/resource")
28
+ router_admin_v2.include_router(profile_router, prefix="/profile")
@@ -0,0 +1,55 @@
1
+ from fastapi import HTTPException
2
+ from fastapi import status
3
+ from sqlmodel import select
4
+
5
+ from fractal_server.app.db import AsyncSession
6
+ from fractal_server.app.models.v2 import Profile
7
+ from fractal_server.app.models.v2 import Resource
8
+
9
+
10
+ async def _get_resource_or_404(
11
+ *,
12
+ resource_id: int,
13
+ db: AsyncSession,
14
+ ) -> Resource:
15
+ resource = await db.get(Resource, resource_id)
16
+ if resource is None:
17
+ raise HTTPException(
18
+ status_code=status.HTTP_404_NOT_FOUND,
19
+ detail=f"Resource {resource_id} not found",
20
+ )
21
+ return resource
22
+
23
+
24
+ async def _get_profile_or_404(
25
+ *,
26
+ profile_id: int,
27
+ db: AsyncSession,
28
+ ) -> Profile:
29
+ profile = await db.get(Profile, profile_id)
30
+ if profile is None:
31
+ raise HTTPException(
32
+ status_code=status.HTTP_404_NOT_FOUND,
33
+ detail=f"Profile {profile_id} not found",
34
+ )
35
+ return profile
36
+
37
+
38
+ async def _check_profile_name(*, name: str, db: AsyncSession) -> None:
39
+ res = await db.execute(select(Profile).where(Profile.name == name))
40
+ namesake = res.scalars().one_or_none()
41
+ if namesake is not None:
42
+ raise HTTPException(
43
+ status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
44
+ detail=f"Profile with name '{name}' already exists.",
45
+ )
46
+
47
+
48
+ async def _check_resource_name(*, name: str, db: AsyncSession) -> None:
49
+ res = await db.execute(select(Resource).where(Resource.name == name))
50
+ namesake = res.scalars().one_or_none()
51
+ if namesake is not None:
52
+ raise HTTPException(
53
+ status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
54
+ detail=f"Resource with name '{name}' already exists.",
55
+ )
@@ -0,0 +1,86 @@
1
+ from fastapi import APIRouter
2
+ from fastapi import Depends
3
+ from fastapi import HTTPException
4
+ from fastapi import Response
5
+ from fastapi import status
6
+ from sqlmodel import func
7
+ from sqlmodel import select
8
+
9
+ from ._aux_functions import _check_profile_name
10
+ from ._aux_functions import _get_profile_or_404
11
+ from fractal_server.app.db import AsyncSession
12
+ from fractal_server.app.db import get_async_db
13
+ from fractal_server.app.models import UserOAuth
14
+ from fractal_server.app.routes.auth import current_active_superuser
15
+ from fractal_server.app.schemas.v2 import ProfileCreate
16
+ from fractal_server.app.schemas.v2 import ProfileRead
17
+
18
+ router = APIRouter()
19
+
20
+
21
+ @router.get("/{profile_id}/", response_model=ProfileRead, status_code=200)
22
+ async def get_single_profile(
23
+ profile_id: int,
24
+ superuser: UserOAuth = Depends(current_active_superuser),
25
+ db: AsyncSession = Depends(get_async_db),
26
+ ) -> ProfileRead:
27
+ """
28
+ Query single `Profile`.
29
+ """
30
+ profile = await _get_profile_or_404(profile_id=profile_id, db=db)
31
+ return profile
32
+
33
+
34
+ @router.put("/{profile_id}/", response_model=ProfileRead, status_code=200)
35
+ async def put_profile(
36
+ profile_id: int,
37
+ profile_update: ProfileCreate,
38
+ superuser: UserOAuth = Depends(current_active_superuser),
39
+ db: AsyncSession = Depends(get_async_db),
40
+ ) -> ProfileRead:
41
+ """
42
+ Override single `Profile`.
43
+ """
44
+ profile = await _get_profile_or_404(profile_id=profile_id, db=db)
45
+
46
+ if profile_update.name and profile_update.name != profile.name:
47
+ await _check_profile_name(name=profile_update.name, db=db)
48
+
49
+ for key, value in profile_update.model_dump().items():
50
+ setattr(profile, key, value)
51
+ await db.commit()
52
+ await db.refresh(profile)
53
+ return profile
54
+
55
+
56
+ @router.delete("/{profile_id}/", status_code=204)
57
+ async def delete_profile(
58
+ profile_id: int,
59
+ superuser: UserOAuth = Depends(current_active_superuser),
60
+ db: AsyncSession = Depends(get_async_db),
61
+ ):
62
+ """
63
+ Delete single `Profile`.
64
+ """
65
+ profile = await _get_profile_or_404(profile_id=profile_id, db=db)
66
+
67
+ # Fail if at least one UserOAuth is associated with the Profile.
68
+ res = await db.execute(
69
+ select(func.count(UserOAuth.id)).where(
70
+ UserOAuth.profile_id == profile.id
71
+ )
72
+ )
73
+ associated_users_count = res.scalar()
74
+ if associated_users_count > 0:
75
+ raise HTTPException(
76
+ status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
77
+ detail=(
78
+ f"Cannot delete Profile {profile_id} because it's associated"
79
+ f" with {associated_users_count} UserOAuths."
80
+ ),
81
+ )
82
+
83
+ # Delete
84
+ await db.delete(profile)
85
+ await db.commit()
86
+ return Response(status_code=status.HTTP_204_NO_CONTENT)