fractal-server 2.9.0a10__tar.gz → 2.9.0a12__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 (246) hide show
  1. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/PKG-INFO +2 -3
  2. fractal_server-2.9.0a12/fractal_server/__init__.py +1 -0
  3. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/auth/group.py +20 -0
  4. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/auth/users.py +97 -99
  5. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/executors/slurm/_slurm_config.py +0 -17
  6. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/executors/slurm/ssh/executor.py +41 -173
  7. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/executors/slurm/sudo/executor.py +26 -109
  8. fractal_server-2.9.0a12/fractal_server/app/runner/executors/slurm/utils_executors.py +58 -0
  9. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/user.py +16 -10
  10. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/security/__init__.py +17 -0
  11. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/pyproject.toml +3 -4
  12. fractal_server-2.9.0a10/fractal_server/__init__.py +0 -1
  13. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/LICENSE +0 -0
  14. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/README.md +0 -0
  15. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/__main__.py +0 -0
  16. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/alembic.ini +0 -0
  17. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/__init__.py +0 -0
  18. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/db/__init__.py +0 -0
  19. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/models/__init__.py +0 -0
  20. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/models/linkusergroup.py +0 -0
  21. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/models/linkuserproject.py +0 -0
  22. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/models/security.py +0 -0
  23. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/models/user_settings.py +0 -0
  24. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/models/v1/__init__.py +0 -0
  25. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/models/v1/dataset.py +0 -0
  26. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/models/v1/job.py +0 -0
  27. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/models/v1/project.py +0 -0
  28. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/models/v1/state.py +0 -0
  29. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/models/v1/task.py +0 -0
  30. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/models/v1/workflow.py +0 -0
  31. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/models/v2/__init__.py +0 -0
  32. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/models/v2/dataset.py +0 -0
  33. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/models/v2/job.py +0 -0
  34. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/models/v2/project.py +0 -0
  35. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/models/v2/task.py +0 -0
  36. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/models/v2/task_group.py +0 -0
  37. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/models/v2/workflow.py +0 -0
  38. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/models/v2/workflowtask.py +0 -0
  39. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/__init__.py +0 -0
  40. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/admin/__init__.py +0 -0
  41. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/admin/v1.py +0 -0
  42. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/admin/v2/__init__.py +0 -0
  43. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/admin/v2/job.py +0 -0
  44. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/admin/v2/project.py +0 -0
  45. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/admin/v2/task.py +0 -0
  46. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/admin/v2/task_group.py +0 -0
  47. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/admin/v2/task_group_lifecycle.py +0 -0
  48. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/__init__.py +0 -0
  49. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v1/__init__.py +0 -0
  50. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v1/_aux_functions.py +0 -0
  51. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v1/dataset.py +0 -0
  52. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v1/job.py +0 -0
  53. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v1/project.py +0 -0
  54. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v1/task.py +0 -0
  55. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v1/task_collection.py +0 -0
  56. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v1/workflow.py +0 -0
  57. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v1/workflowtask.py +0 -0
  58. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v2/__init__.py +0 -0
  59. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v2/_aux_functions.py +0 -0
  60. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +0 -0
  61. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v2/_aux_functions_tasks.py +0 -0
  62. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v2/dataset.py +0 -0
  63. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v2/images.py +0 -0
  64. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v2/job.py +0 -0
  65. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v2/project.py +0 -0
  66. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v2/status.py +0 -0
  67. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v2/submit.py +0 -0
  68. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v2/task.py +0 -0
  69. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v2/task_collection.py +0 -0
  70. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v2/task_collection_custom.py +0 -0
  71. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v2/task_group.py +0 -0
  72. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v2/task_group_lifecycle.py +0 -0
  73. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v2/workflow.py +0 -0
  74. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v2/workflow_import.py +0 -0
  75. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/api/v2/workflowtask.py +0 -0
  76. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/auth/__init__.py +0 -0
  77. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/auth/_aux_auth.py +0 -0
  78. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/auth/current_user.py +0 -0
  79. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/auth/login.py +0 -0
  80. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/auth/oauth.py +0 -0
  81. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/auth/register.py +0 -0
  82. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/auth/router.py +0 -0
  83. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/aux/__init__.py +0 -0
  84. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/aux/_job.py +0 -0
  85. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/aux/_runner.py +0 -0
  86. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/routes/aux/validate_user_settings.py +0 -0
  87. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/.gitignore +0 -0
  88. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/__init__.py +0 -0
  89. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/async_wrap.py +0 -0
  90. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/components.py +0 -0
  91. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/compress_folder.py +0 -0
  92. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/exceptions.py +0 -0
  93. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/executors/__init__.py +0 -0
  94. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/executors/slurm/__init__.py +0 -0
  95. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/executors/slurm/_batching.py +0 -0
  96. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/executors/slurm/remote.py +0 -0
  97. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/executors/slurm/ssh/__init__.py +0 -0
  98. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/executors/slurm/ssh/_executor_wait_thread.py +0 -0
  99. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/executors/slurm/ssh/_slurm_job.py +0 -0
  100. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/executors/slurm/sudo/__init__.py +0 -0
  101. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/executors/slurm/sudo/_check_jobs_status.py +0 -0
  102. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/executors/slurm/sudo/_executor_wait_thread.py +0 -0
  103. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/executors/slurm/sudo/_subprocess_run_as_user.py +0 -0
  104. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/extract_archive.py +0 -0
  105. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/filenames.py +0 -0
  106. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/run_subprocess.py +0 -0
  107. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/set_start_and_last_task_index.py +0 -0
  108. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/shutdown.py +0 -0
  109. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/task_files.py +0 -0
  110. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v1/__init__.py +0 -0
  111. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v1/_common.py +0 -0
  112. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v1/_local/__init__.py +0 -0
  113. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v1/_local/_local_config.py +0 -0
  114. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v1/_local/_submit_setup.py +0 -0
  115. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v1/_local/executor.py +0 -0
  116. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v1/_slurm/__init__.py +0 -0
  117. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v1/_slurm/_submit_setup.py +0 -0
  118. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v1/_slurm/get_slurm_config.py +0 -0
  119. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v1/common.py +0 -0
  120. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v1/handle_failed_job.py +0 -0
  121. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v2/__init__.py +0 -0
  122. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v2/_local/__init__.py +0 -0
  123. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v2/_local/_local_config.py +0 -0
  124. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v2/_local/_submit_setup.py +0 -0
  125. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v2/_local/executor.py +0 -0
  126. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v2/_local_experimental/__init__.py +0 -0
  127. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v2/_local_experimental/_local_config.py +0 -0
  128. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v2/_local_experimental/_submit_setup.py +0 -0
  129. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v2/_local_experimental/executor.py +0 -0
  130. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v2/_slurm_common/__init__.py +0 -0
  131. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v2/_slurm_common/get_slurm_config.py +0 -0
  132. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v2/_slurm_ssh/__init__.py +0 -0
  133. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v2/_slurm_ssh/_submit_setup.py +0 -0
  134. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v2/_slurm_sudo/__init__.py +0 -0
  135. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v2/_slurm_sudo/_submit_setup.py +0 -0
  136. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v2/deduplicate_list.py +0 -0
  137. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v2/handle_failed_job.py +0 -0
  138. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v2/merge_outputs.py +0 -0
  139. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v2/runner.py +0 -0
  140. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v2/runner_functions.py +0 -0
  141. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v2/runner_functions_low_level.py +0 -0
  142. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/v2/task_interface.py +0 -0
  143. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/runner/versions.py +0 -0
  144. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/__init__.py +0 -0
  145. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/_validators.py +0 -0
  146. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/user_group.py +0 -0
  147. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/user_settings.py +0 -0
  148. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/v1/__init__.py +0 -0
  149. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/v1/applyworkflow.py +0 -0
  150. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/v1/dataset.py +0 -0
  151. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/v1/dumps.py +0 -0
  152. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/v1/manifest.py +0 -0
  153. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/v1/project.py +0 -0
  154. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/v1/state.py +0 -0
  155. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/v1/task.py +0 -0
  156. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/v1/task_collection.py +0 -0
  157. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/v1/workflow.py +0 -0
  158. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/v2/__init__.py +0 -0
  159. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/v2/dataset.py +0 -0
  160. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/v2/dumps.py +0 -0
  161. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/v2/job.py +0 -0
  162. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/v2/manifest.py +0 -0
  163. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/v2/project.py +0 -0
  164. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/v2/status.py +0 -0
  165. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/v2/task.py +0 -0
  166. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/v2/task_collection.py +0 -0
  167. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/v2/task_group.py +0 -0
  168. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/v2/workflow.py +0 -0
  169. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/schemas/v2/workflowtask.py +0 -0
  170. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/app/user_settings.py +0 -0
  171. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/config.py +0 -0
  172. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/data_migrations/README.md +0 -0
  173. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/data_migrations/tools.py +0 -0
  174. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/gunicorn_fractal.py +0 -0
  175. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/images/__init__.py +0 -0
  176. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/images/models.py +0 -0
  177. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/images/tools.py +0 -0
  178. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/logger.py +0 -0
  179. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/main.py +0 -0
  180. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/README +0 -0
  181. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/env.py +0 -0
  182. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/naming_convention.py +0 -0
  183. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/script.py.mako +0 -0
  184. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/034a469ec2eb_task_groups.py +0 -0
  185. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/091b01f51f88_add_usergroup_and_linkusergroup_table.py +0 -0
  186. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/19eca0dd47a9_user_settings_project_dir.py +0 -0
  187. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py +0 -0
  188. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/4cedeb448a53_workflowtask_foreign_keys_not_nullables.py +0 -0
  189. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/501961cfcd85_remove_link_between_v1_and_v2_tasks_.py +0 -0
  190. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/50a13d6138fd_initial_schema.py +0 -0
  191. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/5bf02391cfef_v2.py +0 -0
  192. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/70e77f1c38b0_add_applyworkflow_first_task_index_and_.py +0 -0
  193. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/71eefd1dd202_add_slurm_accounts.py +0 -0
  194. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/84bf0fffde30_add_dumps_to_applyworkflow.py +0 -0
  195. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/8e8f227a3e36_update_taskv2_post_2_7_0.py +0 -0
  196. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/8f79bd162e35_add_docs_info_and_docs_link_to_task_.py +0 -0
  197. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/94a47ea2d3ff_remove_cache_dir_slurm_user_and_slurm_.py +0 -0
  198. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/97f444d47249_add_applyworkflow_project_dump.py +0 -0
  199. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/99ea79d9e5d2_add_dataset_history.py +0 -0
  200. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/9c5ae74c9b98_add_user_settings_table.py +0 -0
  201. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/9fd26a2b0de4_add_workflow_timestamp_created.py +0 -0
  202. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/a7f4d6137b53_add_workflow_dump_to_applyworkflow.py +0 -0
  203. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/d256a7379ab8_taskgroup_activity_and_venv_info_to_.py +0 -0
  204. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/d4fe3708d309_make_applyworkflow_workflow_dump_non_.py +0 -0
  205. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/da2cb2ac4255_user_group_viewer_paths.py +0 -0
  206. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/e75cac726012_make_applyworkflow_start_timestamp_not_.py +0 -0
  207. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/efa89c30e0a4_add_project_timestamp_created.py +0 -0
  208. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/migrations/versions/f384e1c0cf5d_drop_task_default_args_columns.py +0 -0
  209. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/py.typed +0 -0
  210. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/ssh/__init__.py +0 -0
  211. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/ssh/_fabric.py +0 -0
  212. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/string_tools.py +0 -0
  213. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/syringe.py +0 -0
  214. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/__init__.py +0 -0
  215. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/utils.py +0 -0
  216. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v1/_TaskCollectPip.py +0 -0
  217. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v1/__init__.py +0 -0
  218. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v1/background_operations.py +0 -0
  219. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v1/endpoint_operations.py +0 -0
  220. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v1/get_collection_data.py +0 -0
  221. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v1/utils.py +0 -0
  222. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v2/__init__.py +0 -0
  223. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v2/local/__init__.py +0 -0
  224. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v2/local/_utils.py +0 -0
  225. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v2/local/collect.py +0 -0
  226. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v2/local/deactivate.py +0 -0
  227. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v2/local/reactivate.py +0 -0
  228. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v2/ssh/__init__.py +0 -0
  229. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v2/ssh/_utils.py +0 -0
  230. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v2/ssh/collect.py +0 -0
  231. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v2/ssh/deactivate.py +0 -0
  232. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v2/ssh/reactivate.py +0 -0
  233. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v2/templates/1_create_venv.sh +0 -0
  234. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v2/templates/2_pip_install.sh +0 -0
  235. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v2/templates/3_pip_freeze.sh +0 -0
  236. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v2/templates/4_pip_show.sh +0 -0
  237. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v2/templates/5_get_venv_size_and_file_number.sh +0 -0
  238. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v2/templates/6_pip_install_from_freeze.sh +0 -0
  239. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v2/utils_background.py +0 -0
  240. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v2/utils_database.py +0 -0
  241. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v2/utils_package_names.py +0 -0
  242. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v2/utils_python_interpreter.py +0 -0
  243. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/tasks/v2/utils_templates.py +0 -0
  244. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/urls.py +0 -0
  245. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/utils.py +0 -0
  246. {fractal_server-2.9.0a10 → fractal_server-2.9.0a12}/fractal_server/zip_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fractal-server
3
- Version: 2.9.0a10
3
+ Version: 2.9.0a12
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
@@ -13,12 +13,11 @@ Classifier: Programming Language :: Python :: 3.10
13
13
  Classifier: Programming Language :: Python :: 3.11
14
14
  Classifier: Programming Language :: Python :: 3.12
15
15
  Requires-Dist: alembic (>=1.13.1,<2.0.0)
16
- Requires-Dist: bcrypt (==4.0.1)
17
16
  Requires-Dist: cloudpickle (>=3.0.0,<3.1.0)
18
17
  Requires-Dist: clusterfutures (>=0.5,<0.6)
19
18
  Requires-Dist: fabric (>=3.2.2,<4.0.0)
20
19
  Requires-Dist: fastapi (>=0.115.0,<0.116.0)
21
- Requires-Dist: fastapi-users[oauth] (>=12.1.0,<13.0.0)
20
+ Requires-Dist: fastapi-users[oauth] (>=14,<15)
22
21
  Requires-Dist: gunicorn (>=21.2,<23.0)
23
22
  Requires-Dist: packaging (>=23.2,<24.0)
24
23
  Requires-Dist: psutil (>=5.9.8,<6.0.0)
@@ -0,0 +1 @@
1
+ __VERSION__ = "2.9.0a12"
@@ -10,6 +10,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
10
10
  from sqlmodel import select
11
11
 
12
12
  from . import current_active_superuser
13
+ from ._aux_auth import _get_default_usergroup_id
13
14
  from ._aux_auth import _get_single_usergroup_with_user_ids
14
15
  from ._aux_auth import _user_or_404
15
16
  from ._aux_auth import _usergroup_or_404
@@ -234,16 +235,35 @@ async def remove_user_from_group(
234
235
  superuser: UserOAuth = Depends(current_active_superuser),
235
236
  db: AsyncSession = Depends(get_async_db),
236
237
  ) -> UserGroupRead:
238
+
239
+ # Check that user and group exist
237
240
  await _usergroup_or_404(group_id, db)
238
241
  user = await _user_or_404(user_id, db)
242
+
243
+ # Check that group is not the default one
244
+ default_user_group_id = await _get_default_usergroup_id(db=db)
245
+ if default_user_group_id == group_id:
246
+ raise HTTPException(
247
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
248
+ detail=(
249
+ f"Cannot remove user from '{FRACTAL_DEFAULT_GROUP_NAME}' "
250
+ "group.",
251
+ ),
252
+ )
253
+
239
254
  link = await db.get(LinkUserGroup, (group_id, user_id))
240
255
  if link is None:
256
+ # If user and group are not linked, fail
241
257
  raise HTTPException(
242
258
  status_code=422,
243
259
  detail=f"User '{user.email}' is not a member of group {group_id}.",
244
260
  )
245
261
  else:
262
+ # If user and group are linked, delete the link
246
263
  await db.delete(link)
247
264
  await db.commit()
265
+
266
+ # Enrich the response object with user_ids
248
267
  group = await _get_single_usergroup_with_user_ids(group_id=group_id, db=db)
268
+
249
269
  return group
@@ -8,9 +8,7 @@ from fastapi import status
8
8
  from fastapi_users import exceptions
9
9
  from fastapi_users import schemas
10
10
  from fastapi_users.router.common import ErrorCode
11
- from sqlalchemy.exc import IntegrityError
12
11
  from sqlalchemy.ext.asyncio import AsyncSession
13
- from sqlmodel import col
14
12
  from sqlmodel import func
15
13
  from sqlmodel import select
16
14
 
@@ -18,9 +16,10 @@ from . import current_active_superuser
18
16
  from ...db import get_async_db
19
17
  from ...schemas.user import UserRead
20
18
  from ...schemas.user import UserUpdate
21
- from ...schemas.user import UserUpdateWithNewGroupIds
22
19
  from ..aux.validate_user_settings import verify_user_has_settings
20
+ from ._aux_auth import _get_default_usergroup_id
23
21
  from ._aux_auth import _get_single_user_with_groups
22
+ from ._aux_auth import FRACTAL_DEFAULT_GROUP_NAME
24
23
  from fractal_server.app.models import LinkUserGroup
25
24
  from fractal_server.app.models import UserGroup
26
25
  from fractal_server.app.models import UserOAuth
@@ -28,6 +27,7 @@ from fractal_server.app.models import UserSettings
28
27
  from fractal_server.app.routes.auth._aux_auth import _user_or_404
29
28
  from fractal_server.app.schemas import UserSettingsRead
30
29
  from fractal_server.app.schemas import UserSettingsUpdate
30
+ from fractal_server.app.schemas.user import UserUpdateGroups
31
31
  from fractal_server.app.security import get_user_manager
32
32
  from fractal_server.app.security import UserManager
33
33
  from fractal_server.logger import set_logger
@@ -55,114 +55,43 @@ async def get_user(
55
55
  @router_users.patch("/users/{user_id}/", response_model=UserRead)
56
56
  async def patch_user(
57
57
  user_id: int,
58
- user_update: UserUpdateWithNewGroupIds,
58
+ user_update: UserUpdate,
59
59
  current_superuser: UserOAuth = Depends(current_active_superuser),
60
60
  user_manager: UserManager = Depends(get_user_manager),
61
61
  db: AsyncSession = Depends(get_async_db),
62
62
  ):
63
63
  """
64
64
  Custom version of the PATCH-user route from `fastapi-users`.
65
-
66
- In order to keep the fastapi-users logic in place (which is convenient to
67
- update user attributes), we split the endpoint into two branches. We either
68
- go through the fastapi-users-based attribute-update branch, or through the
69
- branch where we establish new user/group relationships.
70
-
71
- Note that we prevent making both changes at the same time, since it would
72
- be more complex to guarantee that endpoint error would leave the database
73
- in the same state as before the API call.
74
65
  """
75
66
 
76
- # We prevent simultaneous editing of both user attributes and user/group
77
- # associations
78
- user_update_dict_without_groups = user_update.dict(
79
- exclude_unset=True, exclude={"new_group_ids"}
80
- )
81
- edit_attributes = user_update_dict_without_groups != {}
82
- edit_groups = user_update.new_group_ids is not None
83
- if edit_attributes and edit_groups:
84
- raise HTTPException(
85
- status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
86
- detail=(
87
- "Cannot modify both user attributes and group membership. "
88
- "Please make two independent PATCH calls"
89
- ),
90
- )
91
-
92
67
  # Check that user exists
93
68
  user_to_patch = await _user_or_404(user_id, db)
94
69
 
95
- if edit_groups:
96
- # Establish new user/group relationships
97
-
98
- # Check that all required groups exist
99
- # Note: The reason for introducing `col` is as in
100
- # https://sqlmodel.tiangolo.com/tutorial/where/#type-annotations-and-errors,
101
- stm = select(func.count()).where(
102
- col(UserGroup.id).in_(user_update.new_group_ids)
70
+ # Modify user attributes
71
+ try:
72
+ user = await user_manager.update(
73
+ user_update,
74
+ user_to_patch,
75
+ safe=False,
76
+ request=None,
77
+ )
78
+ validated_user = schemas.model_validate(UserOAuth, user)
79
+ patched_user = await db.get(
80
+ UserOAuth, validated_user.id, populate_existing=True
81
+ )
82
+ except exceptions.InvalidPasswordException as e:
83
+ raise HTTPException(
84
+ status_code=status.HTTP_400_BAD_REQUEST,
85
+ detail={
86
+ "code": ErrorCode.UPDATE_USER_INVALID_PASSWORD,
87
+ "reason": e.reason,
88
+ },
89
+ )
90
+ except exceptions.UserAlreadyExists:
91
+ raise HTTPException(
92
+ status.HTTP_400_BAD_REQUEST,
93
+ detail=ErrorCode.UPDATE_USER_EMAIL_ALREADY_EXISTS,
103
94
  )
104
- res = await db.execute(stm)
105
- number_matching_groups = res.scalar()
106
- if number_matching_groups != len(user_update.new_group_ids):
107
- raise HTTPException(
108
- status_code=status.HTTP_404_NOT_FOUND,
109
- detail=(
110
- "Not all requested groups (IDs: "
111
- f"{user_update.new_group_ids}) exist."
112
- ),
113
- )
114
-
115
- for new_group_id in user_update.new_group_ids:
116
- link = LinkUserGroup(user_id=user_id, group_id=new_group_id)
117
- db.add(link)
118
-
119
- try:
120
- await db.commit()
121
- except IntegrityError as e:
122
- error_msg = (
123
- f"Cannot link groups with IDs {user_update.new_group_ids} "
124
- f"to user {user_id}. "
125
- "Likely reason: one of these links already exists.\n"
126
- f"Original error: {str(e)}"
127
- )
128
- logger.info(error_msg)
129
- raise HTTPException(
130
- status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
131
- detail=error_msg,
132
- )
133
- patched_user = user_to_patch
134
- elif edit_attributes:
135
- # Modify user attributes
136
- try:
137
- user_update_without_groups = UserUpdate(
138
- **user_update_dict_without_groups
139
- )
140
- user = await user_manager.update(
141
- user_update_without_groups,
142
- user_to_patch,
143
- safe=False,
144
- request=None,
145
- )
146
- validated_user = schemas.model_validate(UserOAuth, user)
147
- patched_user = await db.get(
148
- UserOAuth, validated_user.id, populate_existing=True
149
- )
150
- except exceptions.InvalidPasswordException as e:
151
- raise HTTPException(
152
- status_code=status.HTTP_400_BAD_REQUEST,
153
- detail={
154
- "code": ErrorCode.UPDATE_USER_INVALID_PASSWORD,
155
- "reason": e.reason,
156
- },
157
- )
158
- except exceptions.UserAlreadyExists:
159
- raise HTTPException(
160
- status.HTTP_400_BAD_REQUEST,
161
- detail=ErrorCode.UPDATE_USER_EMAIL_ALREADY_EXISTS,
162
- )
163
- else:
164
- # Nothing to do, just continue
165
- patched_user = user_to_patch
166
95
 
167
96
  # Enrich user object with `group_ids_names` attribute
168
97
  patched_user_with_groups = await _get_single_user_with_groups(
@@ -203,6 +132,75 @@ async def list_users(
203
132
  return user_list
204
133
 
205
134
 
135
+ @router_users.post("/users/{user_id}/set-groups/", response_model=UserRead)
136
+ async def set_user_groups(
137
+ user_id: int,
138
+ user_update: UserUpdateGroups,
139
+ superuser: UserOAuth = Depends(current_active_superuser),
140
+ db: AsyncSession = Depends(get_async_db),
141
+ ) -> UserRead:
142
+
143
+ # Preliminary check that all objects exist in the db
144
+ user = await _user_or_404(user_id=user_id, db=db)
145
+ target_group_ids = user_update.group_ids
146
+ stm = select(func.count(UserGroup.id)).where(
147
+ UserGroup.id.in_(target_group_ids)
148
+ )
149
+ res = await db.execute(stm)
150
+ count = res.scalar()
151
+ if count != len(target_group_ids):
152
+ raise HTTPException(
153
+ status_code=status.HTTP_404_NOT_FOUND,
154
+ detail=f"Some UserGroups in {target_group_ids} do not exist.",
155
+ )
156
+
157
+ # Check that default group is not being removed
158
+ default_group_id = await _get_default_usergroup_id(db=db)
159
+ if default_group_id not in target_group_ids:
160
+ raise HTTPException(
161
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
162
+ detail=(
163
+ f"Cannot remove user from "
164
+ f"'{FRACTAL_DEFAULT_GROUP_NAME}' group.",
165
+ ),
166
+ )
167
+
168
+ # Prepare lists of links to be removed
169
+ res = await db.execute(
170
+ select(LinkUserGroup)
171
+ .where(LinkUserGroup.user_id == user_id)
172
+ .where(LinkUserGroup.group_id.not_in(target_group_ids))
173
+ )
174
+ links_to_remove = res.scalars().all()
175
+
176
+ # Prepare lists of links to be added
177
+ res = await db.execute(
178
+ select(LinkUserGroup.group_id)
179
+ .where(LinkUserGroup.user_id == user_id)
180
+ .where(LinkUserGroup.group_id.in_(target_group_ids))
181
+ )
182
+ ids_links_already_in = res.scalars().all()
183
+ ids_links_to_add = set(target_group_ids) - set(ids_links_already_in)
184
+
185
+ # Remove/create links as needed
186
+ for link in links_to_remove:
187
+ logger.info(
188
+ f"Removing LinkUserGroup with {link.user_id=} "
189
+ f"and {link.group_id=}."
190
+ )
191
+ await db.delete(link)
192
+ for group_id in ids_links_to_add:
193
+ logger.info(
194
+ f"Creating new LinkUserGroup with {user_id=} " f"and {group_id=}."
195
+ )
196
+ db.add(LinkUserGroup(user_id=user_id, group_id=group_id))
197
+ await db.commit()
198
+
199
+ user_with_groups = await _get_single_user_with_groups(user, db)
200
+
201
+ return user_with_groups
202
+
203
+
206
204
  @router_users.get(
207
205
  "/users/{user_id}/settings/", response_model=UserSettingsRead
208
206
  )
@@ -456,20 +456,3 @@ def _parse_mem_value(raw_mem: Union[str, int]) -> int:
456
456
 
457
457
  logger.debug(f"{info}, return {mem_MB}")
458
458
  return mem_MB
459
-
460
-
461
- def get_default_slurm_config():
462
- """
463
- Return a default `SlurmConfig` configuration object
464
- """
465
- return SlurmConfig(
466
- partition="main",
467
- cpus_per_task=1,
468
- mem_per_task_MB=100,
469
- target_cpus_per_job=1,
470
- max_cpus_per_job=2,
471
- target_mem_per_job=100,
472
- max_mem_per_job=500,
473
- target_num_jobs=2,
474
- max_num_jobs=4,
475
- )
@@ -31,9 +31,11 @@ from ....filenames import SHUTDOWN_FILENAME
31
31
  from ....task_files import get_task_file_paths
32
32
  from ....task_files import TaskFiles
33
33
  from ....versions import get_versions
34
- from ...slurm._slurm_config import get_default_slurm_config
35
34
  from ...slurm._slurm_config import SlurmConfig
36
35
  from .._batching import heuristics
36
+ from ..utils_executors import get_pickle_file_path
37
+ from ..utils_executors import get_slurm_file_path
38
+ from ..utils_executors import get_slurm_script_file_path
37
39
  from ._executor_wait_thread import FractalSlurmWaitThread
38
40
  from fractal_server.app.runner.components import _COMPONENT_KEY_
39
41
  from fractal_server.app.runner.compress_folder import compress_folder
@@ -223,132 +225,12 @@ class FractalSlurmSSHExecutor(SlurmExecutor):
223
225
  with self.jobs_lock:
224
226
  self.map_jobid_to_slurm_files_local.pop(jobid)
225
227
 
226
- def get_input_pickle_file_path_local(
227
- self, *, arg: str, subfolder_name: str, prefix: Optional[str] = None
228
- ) -> Path:
229
-
230
- prefix = prefix or "cfut"
231
- output = (
232
- self.workflow_dir_local
233
- / subfolder_name
234
- / f"{prefix}_in_{arg}.pickle"
235
- )
236
- return output
237
-
238
- def get_input_pickle_file_path_remote(
239
- self, *, arg: str, subfolder_name: str, prefix: Optional[str] = None
240
- ) -> Path:
241
-
242
- prefix = prefix or "cfut"
243
- output = (
244
- self.workflow_dir_remote
245
- / subfolder_name
246
- / f"{prefix}_in_{arg}.pickle"
247
- )
248
- return output
249
-
250
- def get_output_pickle_file_path_local(
251
- self, *, arg: str, subfolder_name: str, prefix: Optional[str] = None
252
- ) -> Path:
253
- prefix = prefix or "cfut"
254
- return (
255
- self.workflow_dir_local
256
- / subfolder_name
257
- / f"{prefix}_out_{arg}.pickle"
258
- )
259
-
260
- def get_output_pickle_file_path_remote(
261
- self, *, arg: str, subfolder_name: str, prefix: Optional[str] = None
262
- ) -> Path:
263
- prefix = prefix or "cfut"
264
- return (
265
- self.workflow_dir_remote
266
- / subfolder_name
267
- / f"{prefix}_out_{arg}.pickle"
268
- )
269
-
270
- def get_slurm_script_file_path_local(
271
- self, *, subfolder_name: str, prefix: Optional[str] = None
272
- ) -> Path:
273
- prefix = prefix or "_temp"
274
- return (
275
- self.workflow_dir_local
276
- / subfolder_name
277
- / f"{prefix}_slurm_submit.sbatch"
278
- )
279
-
280
- def get_slurm_script_file_path_remote(
281
- self, *, subfolder_name: str, prefix: Optional[str] = None
282
- ) -> Path:
283
- prefix = prefix or "_temp"
284
- return (
285
- self.workflow_dir_remote
286
- / subfolder_name
287
- / f"{prefix}_slurm_submit.sbatch"
288
- )
289
-
290
- def get_slurm_stdout_file_path_local(
291
- self,
292
- *,
293
- subfolder_name: str,
294
- arg: str = "%j",
295
- prefix: Optional[str] = None,
296
- ) -> Path:
297
- prefix = prefix or "slurmpy.stdout"
298
- return (
299
- self.workflow_dir_local
300
- / subfolder_name
301
- / f"{prefix}_slurm_{arg}.out"
302
- )
303
-
304
- def get_slurm_stdout_file_path_remote(
305
- self,
306
- *,
307
- subfolder_name: str,
308
- arg: str = "%j",
309
- prefix: Optional[str] = None,
310
- ) -> Path:
311
- prefix = prefix or "slurmpy.stdout"
312
- return (
313
- self.workflow_dir_remote
314
- / subfolder_name
315
- / f"{prefix}_slurm_{arg}.out"
316
- )
317
-
318
- def get_slurm_stderr_file_path_local(
319
- self,
320
- *,
321
- subfolder_name: str,
322
- arg: str = "%j",
323
- prefix: Optional[str] = None,
324
- ) -> Path:
325
- prefix = prefix or "slurmpy.stderr"
326
- return (
327
- self.workflow_dir_local
328
- / subfolder_name
329
- / f"{prefix}_slurm_{arg}.err"
330
- )
331
-
332
- def get_slurm_stderr_file_path_remote(
333
- self,
334
- *,
335
- subfolder_name: str,
336
- arg: str = "%j",
337
- prefix: Optional[str] = None,
338
- ) -> Path:
339
- prefix = prefix or "slurmpy.stderr"
340
- return (
341
- self.workflow_dir_remote
342
- / subfolder_name
343
- / f"{prefix}_slurm_{arg}.err"
344
- )
345
-
346
228
  def submit(
347
229
  self,
348
230
  fun: Callable[..., Any],
349
231
  *fun_args: Sequence[Any],
350
- slurm_config: Optional[SlurmConfig] = None,
351
- task_files: Optional[TaskFiles] = None,
232
+ slurm_config: SlurmConfig,
233
+ task_files: TaskFiles,
352
234
  **fun_kwargs: dict,
353
235
  ) -> Future:
354
236
  """
@@ -359,11 +241,9 @@ class FractalSlurmSSHExecutor(SlurmExecutor):
359
241
  fun_args: Function positional arguments
360
242
  fun_kwargs: Function keyword arguments
361
243
  slurm_config:
362
- A `SlurmConfig` object; if `None`, use
363
- `get_default_slurm_config()`.
244
+ A `SlurmConfig` object.
364
245
  task_files:
365
- A `TaskFiles` object; if `None`, use
366
- `self.get_default_task_files()`.
246
+ A `TaskFiles` object.
367
247
 
368
248
  Returns:
369
249
  Future representing the execution of the current SLURM job.
@@ -375,12 +255,6 @@ class FractalSlurmSSHExecutor(SlurmExecutor):
375
255
  logger.warning(error_msg)
376
256
  raise JobExecutionError(info=error_msg)
377
257
 
378
- # Set defaults, if needed
379
- if slurm_config is None:
380
- slurm_config = get_default_slurm_config()
381
- if task_files is None:
382
- task_files = self.get_default_task_files()
383
-
384
258
  # Set slurm_file_prefix
385
259
  slurm_file_prefix = task_files.file_prefix
386
260
 
@@ -418,8 +292,8 @@ class FractalSlurmSSHExecutor(SlurmExecutor):
418
292
  fn: Callable[..., Any],
419
293
  iterable: list[Sequence[Any]],
420
294
  *,
421
- slurm_config: Optional[SlurmConfig] = None,
422
- task_files: Optional[TaskFiles] = None,
295
+ slurm_config: SlurmConfig,
296
+ task_files: TaskFiles,
423
297
  ):
424
298
  """
425
299
  Return an iterator with the results of several execution of a function
@@ -442,12 +316,9 @@ class FractalSlurmSSHExecutor(SlurmExecutor):
442
316
  An iterable such that each element is the list of arguments to
443
317
  be passed to `fn`, as in `fn(*args)`.
444
318
  slurm_config:
445
- A `SlurmConfig` object; if `None`, use
446
- `get_default_slurm_config()`.
319
+ A `SlurmConfig` object.
447
320
  task_files:
448
- A `TaskFiles` object; if `None`, use
449
- `self.get_default_task_files()`.
450
-
321
+ A `TaskFiles` object.
451
322
  """
452
323
 
453
324
  # Do not continue if auxiliary thread was shut down
@@ -472,12 +343,6 @@ class FractalSlurmSSHExecutor(SlurmExecutor):
472
343
  # self._exception
473
344
  del fut
474
345
 
475
- # Set defaults, if needed
476
- if not slurm_config:
477
- slurm_config = get_default_slurm_config()
478
- if task_files is None:
479
- task_files = self.get_default_task_files()
480
-
481
346
  # Include common_script_lines in extra_lines
482
347
  logger.debug(
483
348
  f"Adding {self.common_script_lines=} to "
@@ -710,63 +575,80 @@ class FractalSlurmSSHExecutor(SlurmExecutor):
710
575
  f"Missing folder {subfolder_path.as_posix()}."
711
576
  )
712
577
 
713
- # Define I/O pickle file local/remote paths
714
578
  job.input_pickle_files_local = tuple(
715
- self.get_input_pickle_file_path_local(
579
+ get_pickle_file_path(
716
580
  arg=job.workerids[ind],
581
+ workflow_dir=self.workflow_dir_local,
717
582
  subfolder_name=job.wftask_subfolder_name,
583
+ in_or_out="in",
718
584
  prefix=job.wftask_file_prefixes[ind],
719
585
  )
720
586
  for ind in range(job.num_tasks_tot)
721
587
  )
588
+
722
589
  job.input_pickle_files_remote = tuple(
723
- self.get_input_pickle_file_path_remote(
590
+ get_pickle_file_path(
724
591
  arg=job.workerids[ind],
592
+ workflow_dir=self.workflow_dir_remote,
725
593
  subfolder_name=job.wftask_subfolder_name,
594
+ in_or_out="in",
726
595
  prefix=job.wftask_file_prefixes[ind],
727
596
  )
728
597
  for ind in range(job.num_tasks_tot)
729
598
  )
730
599
  job.output_pickle_files_local = tuple(
731
- self.get_output_pickle_file_path_local(
600
+ get_pickle_file_path(
732
601
  arg=job.workerids[ind],
602
+ workflow_dir=self.workflow_dir_local,
733
603
  subfolder_name=job.wftask_subfolder_name,
604
+ in_or_out="out",
734
605
  prefix=job.wftask_file_prefixes[ind],
735
606
  )
736
607
  for ind in range(job.num_tasks_tot)
737
608
  )
738
609
  job.output_pickle_files_remote = tuple(
739
- self.get_output_pickle_file_path_remote(
610
+ get_pickle_file_path(
740
611
  arg=job.workerids[ind],
612
+ workflow_dir=self.workflow_dir_remote,
741
613
  subfolder_name=job.wftask_subfolder_name,
614
+ in_or_out="out",
742
615
  prefix=job.wftask_file_prefixes[ind],
743
616
  )
744
617
  for ind in range(job.num_tasks_tot)
745
618
  )
746
-
747
- # Define SLURM-job file local/remote paths
748
- job.slurm_script_local = self.get_slurm_script_file_path_local(
619
+ # define slurm-job file local/remote paths
620
+ job.slurm_script_local = get_slurm_script_file_path(
621
+ workflow_dir=self.workflow_dir_local,
749
622
  subfolder_name=job.wftask_subfolder_name,
750
623
  prefix=job.slurm_file_prefix,
751
624
  )
752
- job.slurm_script_remote = self.get_slurm_script_file_path_remote(
625
+ job.slurm_script_remote = get_slurm_script_file_path(
626
+ workflow_dir=self.workflow_dir_remote,
753
627
  subfolder_name=job.wftask_subfolder_name,
754
628
  prefix=job.slurm_file_prefix,
755
629
  )
756
- job.slurm_stdout_local = self.get_slurm_stdout_file_path_local(
630
+ job.slurm_stdout_local = get_slurm_file_path(
631
+ workflow_dir=self.workflow_dir_local,
757
632
  subfolder_name=job.wftask_subfolder_name,
633
+ out_or_err="out",
758
634
  prefix=job.slurm_file_prefix,
759
635
  )
760
- job.slurm_stdout_remote = self.get_slurm_stdout_file_path_remote(
636
+ job.slurm_stdout_remote = get_slurm_file_path(
637
+ workflow_dir=self.workflow_dir_remote,
761
638
  subfolder_name=job.wftask_subfolder_name,
639
+ out_or_err="out",
762
640
  prefix=job.slurm_file_prefix,
763
641
  )
764
- job.slurm_stderr_local = self.get_slurm_stderr_file_path_local(
642
+ job.slurm_stderr_local = get_slurm_file_path(
643
+ workflow_dir=self.workflow_dir_local,
765
644
  subfolder_name=job.wftask_subfolder_name,
645
+ out_or_err="err",
766
646
  prefix=job.slurm_file_prefix,
767
647
  )
768
- job.slurm_stderr_remote = self.get_slurm_stderr_file_path_remote(
648
+ job.slurm_stderr_remote = get_slurm_file_path(
649
+ workflow_dir=self.workflow_dir_remote,
769
650
  subfolder_name=job.wftask_subfolder_name,
651
+ out_or_err="err",
770
652
  prefix=job.slurm_file_prefix,
771
653
  )
772
654
 
@@ -1294,7 +1176,6 @@ class FractalSlurmSSHExecutor(SlurmExecutor):
1294
1176
  slurm_err_path: str,
1295
1177
  slurm_config: SlurmConfig,
1296
1178
  ):
1297
-
1298
1179
  num_tasks_max_running = slurm_config.parallel_tasks_per_job
1299
1180
  mem_per_task_MB = slurm_config.mem_per_task_MB
1300
1181
 
@@ -1346,19 +1227,6 @@ class FractalSlurmSSHExecutor(SlurmExecutor):
1346
1227
  script = "\n".join(script_lines)
1347
1228
  return script
1348
1229
 
1349
- def get_default_task_files(self) -> TaskFiles:
1350
- """
1351
- This will be called when self.submit or self.map are called from
1352
- outside fractal-server, and then lack some optional arguments.
1353
- """
1354
- task_files = TaskFiles(
1355
- workflow_dir_local=self.workflow_dir_local,
1356
- workflow_dir_remote=self.workflow_dir_remote,
1357
- task_order=None,
1358
- task_name="name",
1359
- )
1360
- return task_files
1361
-
1362
1230
  def shutdown(self, wait=True, *, cancel_futures=False):
1363
1231
  """
1364
1232
  Clean up all executor variables. Note that this function is executed on