fractal-server 2.4.0a0__tar.gz → 2.4.0a2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/PKG-INFO +1 -1
  2. fractal_server-2.4.0a2/fractal_server/__init__.py +1 -0
  3. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/auth/group.py +25 -22
  4. fractal_server-2.4.0a2/fractal_server/app/routes/auth/users.py +192 -0
  5. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/user.py +9 -0
  6. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/user_group.py +8 -0
  7. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/security/__init__.py +0 -7
  8. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/pyproject.toml +2 -2
  9. fractal_server-2.4.0a0/fractal_server/__init__.py +0 -1
  10. fractal_server-2.4.0a0/fractal_server/app/routes/auth/users.py +0 -103
  11. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/LICENSE +0 -0
  12. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/README.md +0 -0
  13. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/__main__.py +0 -0
  14. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/alembic.ini +0 -0
  15. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/__init__.py +0 -0
  16. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/db/__init__.py +0 -0
  17. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/models/__init__.py +0 -0
  18. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/models/linkusergroup.py +0 -0
  19. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/models/linkuserproject.py +0 -0
  20. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/models/security.py +0 -0
  21. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/models/v1/__init__.py +0 -0
  22. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/models/v1/dataset.py +0 -0
  23. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/models/v1/job.py +0 -0
  24. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/models/v1/project.py +0 -0
  25. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/models/v1/state.py +0 -0
  26. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/models/v1/task.py +0 -0
  27. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/models/v1/workflow.py +0 -0
  28. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/models/v2/__init__.py +0 -0
  29. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/models/v2/collection_state.py +0 -0
  30. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/models/v2/dataset.py +0 -0
  31. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/models/v2/job.py +0 -0
  32. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/models/v2/project.py +0 -0
  33. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/models/v2/task.py +0 -0
  34. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/models/v2/workflow.py +0 -0
  35. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/models/v2/workflowtask.py +0 -0
  36. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/__init__.py +0 -0
  37. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/admin/__init__.py +0 -0
  38. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/admin/v1.py +0 -0
  39. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/admin/v2.py +0 -0
  40. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/__init__.py +0 -0
  41. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/v1/__init__.py +0 -0
  42. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/v1/_aux_functions.py +0 -0
  43. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/v1/dataset.py +0 -0
  44. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/v1/job.py +0 -0
  45. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/v1/project.py +0 -0
  46. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/v1/task.py +0 -0
  47. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/v1/task_collection.py +0 -0
  48. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/v1/workflow.py +0 -0
  49. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/v1/workflowtask.py +0 -0
  50. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/v2/__init__.py +0 -0
  51. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/v2/_aux_functions.py +0 -0
  52. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/v2/dataset.py +0 -0
  53. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/v2/images.py +0 -0
  54. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/v2/job.py +0 -0
  55. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/v2/project.py +0 -0
  56. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/v2/status.py +0 -0
  57. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/v2/submit.py +0 -0
  58. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/v2/task.py +0 -0
  59. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/v2/task_collection.py +0 -0
  60. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/v2/task_collection_custom.py +0 -0
  61. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/v2/task_legacy.py +0 -0
  62. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/v2/workflow.py +0 -0
  63. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/api/v2/workflowtask.py +0 -0
  64. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/auth/__init__.py +0 -0
  65. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/auth/_aux_auth.py +0 -0
  66. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/auth/current_user.py +0 -0
  67. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/auth/group_names.py +0 -0
  68. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/auth/login.py +0 -0
  69. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/auth/oauth.py +0 -0
  70. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/auth/register.py +0 -0
  71. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/auth/router.py +0 -0
  72. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/aux/__init__.py +0 -0
  73. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/aux/_job.py +0 -0
  74. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/routes/aux/_runner.py +0 -0
  75. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/.gitignore +0 -0
  76. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/__init__.py +0 -0
  77. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/async_wrap.py +0 -0
  78. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/components.py +0 -0
  79. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/compress_folder.py +0 -0
  80. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/exceptions.py +0 -0
  81. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/executors/__init__.py +0 -0
  82. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/executors/slurm/__init__.py +0 -0
  83. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/executors/slurm/_batching.py +0 -0
  84. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/executors/slurm/_slurm_config.py +0 -0
  85. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/executors/slurm/remote.py +0 -0
  86. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/executors/slurm/ssh/__init__.py +0 -0
  87. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/executors/slurm/ssh/_executor_wait_thread.py +0 -0
  88. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/executors/slurm/ssh/_slurm_job.py +0 -0
  89. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/executors/slurm/ssh/executor.py +0 -0
  90. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/executors/slurm/sudo/__init__.py +0 -0
  91. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/executors/slurm/sudo/_check_jobs_status.py +0 -0
  92. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/executors/slurm/sudo/_executor_wait_thread.py +0 -0
  93. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/executors/slurm/sudo/_subprocess_run_as_user.py +0 -0
  94. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/executors/slurm/sudo/executor.py +0 -0
  95. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/extract_archive.py +0 -0
  96. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/filenames.py +0 -0
  97. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/run_subprocess.py +0 -0
  98. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/set_start_and_last_task_index.py +0 -0
  99. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/shutdown.py +0 -0
  100. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/task_files.py +0 -0
  101. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v1/__init__.py +0 -0
  102. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v1/_common.py +0 -0
  103. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v1/_local/__init__.py +0 -0
  104. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v1/_local/_local_config.py +0 -0
  105. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v1/_local/_submit_setup.py +0 -0
  106. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v1/_local/executor.py +0 -0
  107. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v1/_slurm/__init__.py +0 -0
  108. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v1/_slurm/_submit_setup.py +0 -0
  109. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v1/_slurm/get_slurm_config.py +0 -0
  110. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v1/common.py +0 -0
  111. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v1/handle_failed_job.py +0 -0
  112. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v2/__init__.py +0 -0
  113. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v2/_local/__init__.py +0 -0
  114. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v2/_local/_local_config.py +0 -0
  115. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v2/_local/_submit_setup.py +0 -0
  116. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v2/_local/executor.py +0 -0
  117. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v2/_local_experimental/__init__.py +0 -0
  118. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v2/_local_experimental/_local_config.py +0 -0
  119. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v2/_local_experimental/_submit_setup.py +0 -0
  120. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v2/_local_experimental/executor.py +0 -0
  121. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v2/_slurm_common/__init__.py +0 -0
  122. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v2/_slurm_common/get_slurm_config.py +0 -0
  123. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v2/_slurm_ssh/__init__.py +0 -0
  124. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v2/_slurm_ssh/_submit_setup.py +0 -0
  125. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v2/_slurm_sudo/__init__.py +0 -0
  126. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v2/_slurm_sudo/_submit_setup.py +0 -0
  127. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v2/deduplicate_list.py +0 -0
  128. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v2/handle_failed_job.py +0 -0
  129. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v2/merge_outputs.py +0 -0
  130. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v2/runner.py +0 -0
  131. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v2/runner_functions.py +0 -0
  132. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v2/runner_functions_low_level.py +0 -0
  133. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v2/task_interface.py +0 -0
  134. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/v2/v1_compat.py +0 -0
  135. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/runner/versions.py +0 -0
  136. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/__init__.py +0 -0
  137. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/_validators.py +0 -0
  138. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/v1/__init__.py +0 -0
  139. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/v1/applyworkflow.py +0 -0
  140. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/v1/dataset.py +0 -0
  141. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/v1/dumps.py +0 -0
  142. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/v1/manifest.py +0 -0
  143. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/v1/project.py +0 -0
  144. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/v1/state.py +0 -0
  145. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/v1/task.py +0 -0
  146. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/v1/task_collection.py +0 -0
  147. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/v1/workflow.py +0 -0
  148. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/v2/__init__.py +0 -0
  149. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/v2/dataset.py +0 -0
  150. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/v2/dumps.py +0 -0
  151. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/v2/job.py +0 -0
  152. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/v2/manifest.py +0 -0
  153. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/v2/project.py +0 -0
  154. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/v2/status.py +0 -0
  155. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/v2/task.py +0 -0
  156. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/v2/task_collection.py +0 -0
  157. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/v2/workflow.py +0 -0
  158. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/app/schemas/v2/workflowtask.py +0 -0
  159. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/config.py +0 -0
  160. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/data_migrations/2_4_0.py +0 -0
  161. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/data_migrations/README.md +0 -0
  162. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/gunicorn_fractal.py +0 -0
  163. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/images/__init__.py +0 -0
  164. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/images/models.py +0 -0
  165. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/images/tools.py +0 -0
  166. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/logger.py +0 -0
  167. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/main.py +0 -0
  168. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/migrations/README +0 -0
  169. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/migrations/env.py +0 -0
  170. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/migrations/script.py.mako +0 -0
  171. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/migrations/versions/091b01f51f88_add_usergroup_and_linkusergroup_table.py +0 -0
  172. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py +0 -0
  173. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/migrations/versions/4cedeb448a53_workflowtask_foreign_keys_not_nullables.py +0 -0
  174. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/migrations/versions/50a13d6138fd_initial_schema.py +0 -0
  175. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/migrations/versions/5bf02391cfef_v2.py +0 -0
  176. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/migrations/versions/70e77f1c38b0_add_applyworkflow_first_task_index_and_.py +0 -0
  177. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/migrations/versions/71eefd1dd202_add_slurm_accounts.py +0 -0
  178. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/migrations/versions/84bf0fffde30_add_dumps_to_applyworkflow.py +0 -0
  179. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/migrations/versions/8f79bd162e35_add_docs_info_and_docs_link_to_task_.py +0 -0
  180. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/migrations/versions/97f444d47249_add_applyworkflow_project_dump.py +0 -0
  181. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/migrations/versions/99ea79d9e5d2_add_dataset_history.py +0 -0
  182. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/migrations/versions/9fd26a2b0de4_add_workflow_timestamp_created.py +0 -0
  183. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/migrations/versions/a7f4d6137b53_add_workflow_dump_to_applyworkflow.py +0 -0
  184. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/migrations/versions/d4fe3708d309_make_applyworkflow_workflow_dump_non_.py +0 -0
  185. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/migrations/versions/e75cac726012_make_applyworkflow_start_timestamp_not_.py +0 -0
  186. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/migrations/versions/efa89c30e0a4_add_project_timestamp_created.py +0 -0
  187. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/migrations/versions/f384e1c0cf5d_drop_task_default_args_columns.py +0 -0
  188. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/py.typed +0 -0
  189. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/ssh/__init__.py +0 -0
  190. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/ssh/_fabric.py +0 -0
  191. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/string_tools.py +0 -0
  192. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/syringe.py +0 -0
  193. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/tasks/__init__.py +0 -0
  194. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/tasks/utils.py +0 -0
  195. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/tasks/v1/_TaskCollectPip.py +0 -0
  196. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/tasks/v1/__init__.py +0 -0
  197. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/tasks/v1/background_operations.py +0 -0
  198. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/tasks/v1/endpoint_operations.py +0 -0
  199. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/tasks/v1/get_collection_data.py +0 -0
  200. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/tasks/v1/utils.py +0 -0
  201. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/tasks/v2/_TaskCollectPip.py +0 -0
  202. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/tasks/v2/__init__.py +0 -0
  203. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/tasks/v2/_venv_pip.py +0 -0
  204. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/tasks/v2/background_operations.py +0 -0
  205. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/tasks/v2/background_operations_ssh.py +0 -0
  206. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/tasks/v2/endpoint_operations.py +0 -0
  207. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/tasks/v2/templates/_1_create_venv.sh +0 -0
  208. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/tasks/v2/templates/_2_upgrade_pip.sh +0 -0
  209. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/tasks/v2/templates/_3_pip_install.sh +0 -0
  210. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/tasks/v2/templates/_4_pip_freeze.sh +0 -0
  211. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/tasks/v2/templates/_5_pip_show.sh +0 -0
  212. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/tasks/v2/utils.py +0 -0
  213. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/urls.py +0 -0
  214. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/utils.py +0 -0
  215. {fractal_server-2.4.0a0 → fractal_server-2.4.0a2}/fractal_server/zip_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fractal-server
3
- Version: 2.4.0a0
3
+ Version: 2.4.0a2
4
4
  Summary: Server component of the Fractal analytics platform
5
5
  Home-page: https://github.com/fractal-analytics-platform/fractal-server
6
6
  License: BSD-3-Clause
@@ -0,0 +1 @@
1
+ __VERSION__ = "2.4.0a2"
@@ -5,8 +5,10 @@ from fastapi import APIRouter
5
5
  from fastapi import Depends
6
6
  from fastapi import HTTPException
7
7
  from fastapi import status
8
+ from sqlalchemy.exc import IntegrityError
8
9
  from sqlalchemy.ext.asyncio import AsyncSession
9
10
  from sqlmodel import col
11
+ from sqlmodel import func
10
12
  from sqlmodel import select
11
13
 
12
14
  from . import current_active_superuser
@@ -18,7 +20,9 @@ from ._aux_auth import _get_single_group_with_user_ids
18
20
  from fractal_server.app.models import LinkUserGroup
19
21
  from fractal_server.app.models import UserGroup
20
22
  from fractal_server.app.models import UserOAuth
23
+ from fractal_server.logger import set_logger
21
24
 
25
+ logger = set_logger(__name__)
22
26
 
23
27
  router_group = APIRouter()
24
28
 
@@ -31,9 +35,6 @@ async def get_list_user_groups(
31
35
  user: UserOAuth = Depends(current_active_superuser),
32
36
  db: AsyncSession = Depends(get_async_db),
33
37
  ) -> list[UserGroupRead]:
34
- """
35
- FIXME docstring
36
- """
37
38
 
38
39
  # Get all groups
39
40
  stm_all_groups = select(UserGroup)
@@ -46,7 +47,8 @@ async def get_list_user_groups(
46
47
  res = await db.execute(stm_all_links)
47
48
  links = res.scalars().all()
48
49
 
49
- # FIXME GROUPS: this must be optimized
50
+ # TODO: possible optimizations for this construction are listed in
51
+ # https://github.com/fractal-analytics-platform/fractal-server/issues/1742
50
52
  for ind, group in enumerate(groups):
51
53
  groups[ind] = dict(
52
54
  group.model_dump(),
@@ -68,9 +70,6 @@ async def get_single_user_group(
68
70
  user: UserOAuth = Depends(current_active_superuser),
69
71
  db: AsyncSession = Depends(get_async_db),
70
72
  ) -> UserGroupRead:
71
- """
72
- FIXME docstring
73
- """
74
73
  group = await _get_single_group_with_user_ids(group_id=group_id, db=db)
75
74
  return group
76
75
 
@@ -85,9 +84,6 @@ async def create_single_group(
85
84
  user: UserOAuth = Depends(current_active_superuser),
86
85
  db: AsyncSession = Depends(get_async_db),
87
86
  ) -> UserGroupRead:
88
- """
89
- FIXME docstring
90
- """
91
87
 
92
88
  # Check that name is not already in use
93
89
  existing_name_str = select(UserGroup).where(
@@ -119,24 +115,21 @@ async def update_single_group(
119
115
  user: UserOAuth = Depends(current_active_superuser),
120
116
  db: AsyncSession = Depends(get_async_db),
121
117
  ) -> UserGroupRead:
122
- """
123
- FIXME docstring
124
- """
125
118
 
126
119
  # Check that all required users exist
127
120
  # Note: The reason for introducing `col` is as in
128
121
  # https://sqlmodel.tiangolo.com/tutorial/where/#type-annotations-and-errors,
129
- stm = select(UserOAuth).where(
122
+ stm = select(func.count()).where(
130
123
  col(UserOAuth.id).in_(group_update.new_user_ids)
131
124
  )
132
125
  res = await db.execute(stm)
133
- matching_users = res.scalars().unique().all()
134
- if not len(matching_users) == len(group_update.new_user_ids):
126
+ number_matching_users = res.scalar()
127
+ if number_matching_users != len(group_update.new_user_ids):
135
128
  raise HTTPException(
136
129
  status_code=status.HTTP_404_NOT_FOUND,
137
130
  detail=(
138
- f"At least user with IDs {group_update.new_user_ids} "
139
- "does not exist."
131
+ f"Not all requested users (IDs {group_update.new_user_ids}) "
132
+ "exist."
140
133
  ),
141
134
  )
142
135
 
@@ -144,7 +137,20 @@ async def update_single_group(
144
137
  for user_id in group_update.new_user_ids:
145
138
  link = LinkUserGroup(user_id=user_id, group_id=group_id)
146
139
  db.add(link)
147
- await db.commit()
140
+ try:
141
+ await db.commit()
142
+ except IntegrityError as e:
143
+ error_msg = (
144
+ f"Cannot link users with IDs {group_update.new_user_ids} "
145
+ f"to group {group_id}. "
146
+ "Likely reason: one of these links already exists.\n"
147
+ f"Original error: {str(e)}"
148
+ )
149
+ logger.info(error_msg)
150
+ raise HTTPException(
151
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
152
+ detail=error_msg,
153
+ )
148
154
 
149
155
  updated_group = await _get_single_group_with_user_ids(
150
156
  group_id=group_id, db=db
@@ -161,9 +167,6 @@ async def delete_single_group(
161
167
  user: UserOAuth = Depends(current_active_superuser),
162
168
  db: AsyncSession = Depends(get_async_db),
163
169
  ) -> UserGroupRead:
164
- """
165
- FIXME docstring
166
- """
167
170
  raise HTTPException(
168
171
  status_code=status.HTTP_405_METHOD_NOT_ALLOWED,
169
172
  detail=(
@@ -0,0 +1,192 @@
1
+ """
2
+ Definition of `/auth/users/` routes
3
+ """
4
+ from fastapi import APIRouter
5
+ from fastapi import Depends
6
+ from fastapi import HTTPException
7
+ from fastapi import status
8
+ from fastapi_users import exceptions
9
+ from fastapi_users import schemas
10
+ from fastapi_users.router.common import ErrorCode
11
+ from sqlalchemy.exc import IntegrityError
12
+ from sqlalchemy.ext.asyncio import AsyncSession
13
+ from sqlmodel import col
14
+ from sqlmodel import func
15
+ from sqlmodel import select
16
+
17
+ from . import current_active_superuser
18
+ from ...db import get_async_db
19
+ from ...schemas.user import UserRead
20
+ from ...schemas.user import UserUpdate
21
+ from ...schemas.user import UserUpdateWithNewGroupIds
22
+ from ._aux_auth import _get_single_user_with_group_ids
23
+ from fractal_server.app.models import LinkUserGroup
24
+ from fractal_server.app.models import UserGroup
25
+ from fractal_server.app.models import UserOAuth
26
+ from fractal_server.app.routes.auth._aux_auth import _user_or_404
27
+ from fractal_server.app.security import get_user_manager
28
+ from fractal_server.app.security import UserManager
29
+ from fractal_server.logger import set_logger
30
+
31
+ router_users = APIRouter()
32
+
33
+
34
+ logger = set_logger(__name__)
35
+
36
+
37
+ @router_users.get("/users/{user_id}/", response_model=UserRead)
38
+ async def get_user(
39
+ user_id: int,
40
+ group_ids: bool = True,
41
+ superuser: UserOAuth = Depends(current_active_superuser),
42
+ db: AsyncSession = Depends(get_async_db),
43
+ ) -> UserRead:
44
+ user = await _user_or_404(user_id, db)
45
+ if group_ids:
46
+ user_with_group_ids = await _get_single_user_with_group_ids(user, db)
47
+ return user_with_group_ids
48
+ else:
49
+ return user
50
+
51
+
52
+ @router_users.patch("/users/{user_id}/", response_model=UserRead)
53
+ async def patch_user(
54
+ user_id: int,
55
+ user_update: UserUpdateWithNewGroupIds,
56
+ current_superuser: UserOAuth = Depends(current_active_superuser),
57
+ user_manager: UserManager = Depends(get_user_manager),
58
+ db: AsyncSession = Depends(get_async_db),
59
+ ):
60
+ """
61
+ Custom version of the PATCH-user route from `fastapi-users`.
62
+
63
+ In order to keep the fastapi-users logic in place (which is convenient to
64
+ update user attributes), we split the endpoint into two branches. We either
65
+ go through the fastapi-users-based attribute-update branch, or through the
66
+ branch where we establish new user/group relationships.
67
+
68
+ Note that we prevent making both changes at the same time, since it would
69
+ be more complex to guarantee that endpoint error would leave the database
70
+ in the same state as before the API call.
71
+ """
72
+
73
+ # We prevent simultaneous editing of both user attributes and user/group
74
+ # associations
75
+ user_update_dict_without_groups = user_update.dict(
76
+ exclude_unset=True, exclude={"new_group_ids"}
77
+ )
78
+ edit_attributes = user_update_dict_without_groups != {}
79
+ edit_groups = user_update.new_group_ids is not None
80
+ if edit_attributes and edit_groups:
81
+ raise HTTPException(
82
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
83
+ detail=(
84
+ "Cannot modify both user attributes and group membership. "
85
+ "Please make two independent PATCH calls"
86
+ ),
87
+ )
88
+
89
+ # Check that user exists
90
+ user_to_patch = await _user_or_404(user_id, db)
91
+
92
+ if edit_groups:
93
+ # Establish new user/group relationships
94
+
95
+ # Check that all required groups exist
96
+ # Note: The reason for introducing `col` is as in
97
+ # https://sqlmodel.tiangolo.com/tutorial/where/#type-annotations-and-errors,
98
+ stm = select(func.count()).where(
99
+ col(UserGroup.id).in_(user_update.new_group_ids)
100
+ )
101
+ res = await db.execute(stm)
102
+ number_matching_groups = res.scalar()
103
+ if number_matching_groups != len(user_update.new_group_ids):
104
+ raise HTTPException(
105
+ status_code=status.HTTP_404_NOT_FOUND,
106
+ detail=(
107
+ "Not all requested groups (IDs: "
108
+ f"{user_update.new_group_ids}) exist."
109
+ ),
110
+ )
111
+
112
+ for new_group_id in user_update.new_group_ids:
113
+ link = LinkUserGroup(user_id=user_id, group_id=new_group_id)
114
+ db.add(link)
115
+
116
+ try:
117
+ await db.commit()
118
+ except IntegrityError as e:
119
+ error_msg = (
120
+ f"Cannot link groups with IDs {user_update.new_group_ids} "
121
+ f"to user {user_id}. "
122
+ "Likely reason: one of these links already exists.\n"
123
+ f"Original error: {str(e)}"
124
+ )
125
+ logger.info(error_msg)
126
+ raise HTTPException(
127
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
128
+ detail=error_msg,
129
+ )
130
+
131
+ patched_user = user_to_patch
132
+
133
+ elif edit_attributes:
134
+ # Modify user attributes
135
+ try:
136
+ user_update_without_groups = UserUpdate(
137
+ **user_update_dict_without_groups
138
+ )
139
+ user = await user_manager.update(
140
+ user_update_without_groups,
141
+ user_to_patch,
142
+ safe=False,
143
+ request=None,
144
+ )
145
+ patched_user = schemas.model_validate(UserOAuth, user)
146
+ except exceptions.InvalidPasswordException as e:
147
+ raise HTTPException(
148
+ status_code=status.HTTP_400_BAD_REQUEST,
149
+ detail={
150
+ "code": ErrorCode.UPDATE_USER_INVALID_PASSWORD,
151
+ "reason": e.reason,
152
+ },
153
+ )
154
+ else:
155
+ # Nothing to do, just continue
156
+ patched_user = user_to_patch
157
+
158
+ # Enrich user object with `group_ids` attribute
159
+ patched_user_with_group_ids = await _get_single_user_with_group_ids(
160
+ patched_user, db
161
+ )
162
+
163
+ return patched_user_with_group_ids
164
+
165
+
166
+ @router_users.get("/users/", response_model=list[UserRead])
167
+ async def list_users(
168
+ user: UserOAuth = Depends(current_active_superuser),
169
+ db: AsyncSession = Depends(get_async_db),
170
+ ):
171
+ """
172
+ Return list of all users
173
+ """
174
+ stm = select(UserOAuth)
175
+ res = await db.execute(stm)
176
+ user_list = res.scalars().unique().all()
177
+
178
+ # Get all user/group links
179
+ stm_all_links = select(LinkUserGroup)
180
+ res = await db.execute(stm_all_links)
181
+ links = res.scalars().all()
182
+
183
+ # TODO: possible optimizations for this construction are listed in
184
+ # https://github.com/fractal-analytics-platform/fractal-server/issues/1742
185
+ for ind, user in enumerate(user_list):
186
+ user_list[ind] = dict(
187
+ user.model_dump(),
188
+ group_ids=[
189
+ link.group_id for link in links if link.user_id == user.id
190
+ ],
191
+ )
192
+ return user_list
@@ -16,6 +16,7 @@ __all__ = (
16
16
  "UserRead",
17
17
  "UserUpdate",
18
18
  "UserCreate",
19
+ "UserUpdateWithNewGroupIds",
19
20
  )
20
21
 
21
22
 
@@ -102,6 +103,14 @@ class UserUpdateStrict(BaseModel, extra=Extra.forbid):
102
103
  )
103
104
 
104
105
 
106
+ class UserUpdateWithNewGroupIds(UserUpdate):
107
+ new_group_ids: Optional[list[int]] = None
108
+
109
+ _val_unique = validator("new_group_ids", allow_reuse=True)(
110
+ val_unique_list("new_group_ids")
111
+ )
112
+
113
+
105
114
  class UserCreate(schemas.BaseUserCreate):
106
115
  """
107
116
  Schema for `User` creation.
@@ -4,6 +4,10 @@ from typing import Optional
4
4
  from pydantic import BaseModel
5
5
  from pydantic import Extra
6
6
  from pydantic import Field
7
+ from pydantic import validator
8
+
9
+ from ._validators import val_unique_list
10
+
7
11
 
8
12
  __all__ = (
9
13
  "UserGroupRead",
@@ -55,3 +59,7 @@ class UserGroupUpdate(BaseModel, extra=Extra.forbid):
55
59
  """
56
60
 
57
61
  new_user_ids: list[int] = Field(default_factory=list)
62
+
63
+ _val_unique = validator("new_user_ids", allow_reuse=True)(
64
+ val_unique_list("new_user_ids")
65
+ )
@@ -43,7 +43,6 @@ from fastapi_users.exceptions import UserAlreadyExists
43
43
  from fastapi_users.models import ID
44
44
  from fastapi_users.models import OAP
45
45
  from fastapi_users.models import UP
46
- from sqlalchemy.exc import IntegrityError
47
46
  from sqlalchemy.ext.asyncio import AsyncSession
48
47
  from sqlalchemy.orm import selectinload
49
48
  from sqlmodel import func
@@ -295,12 +294,6 @@ async def _create_first_user(
295
294
  user = await user_manager.create(UserCreate(**kwargs))
296
295
  logger.info(f"User {user.email} created")
297
296
 
298
- except IntegrityError:
299
- logger.warning(
300
- f"Creation of user {email} failed with IntegrityError "
301
- "(likely due to concurrent attempts from different workers)."
302
- )
303
-
304
297
  except UserAlreadyExists:
305
298
  logger.warning(f"User {email} already exists")
306
299
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "fractal-server"
3
- version = "2.4.0a0"
3
+ version = "2.4.0a2"
4
4
  description = "Server component of the Fractal analytics platform"
5
5
  authors = [
6
6
  "Tommaso Comparin <tommaso.comparin@exact-lab.it>",
@@ -92,7 +92,7 @@ filterwarnings = [
92
92
  ]
93
93
 
94
94
  [tool.bumpver]
95
- current_version = "2.4.0a0"
95
+ current_version = "2.4.0a2"
96
96
  version_pattern = "MAJOR.MINOR.PATCH[PYTAGNUM]"
97
97
  commit_message = "bump version {old_version} -> {new_version}"
98
98
  commit = true
@@ -1 +0,0 @@
1
- __VERSION__ = "2.4.0a0"
@@ -1,103 +0,0 @@
1
- """
2
- Definition of `/auth/users/` routes
3
- """
4
- from fastapi import APIRouter
5
- from fastapi import Depends
6
- from fastapi import HTTPException
7
- from fastapi import status
8
- from fastapi_users import exceptions
9
- from fastapi_users import schemas
10
- from fastapi_users.router.common import ErrorCode
11
- from sqlalchemy.ext.asyncio import AsyncSession
12
- from sqlmodel import select
13
-
14
- from . import current_active_superuser
15
- from ...db import get_async_db
16
- from ...schemas.user import UserRead
17
- from ...schemas.user import UserUpdate
18
- from ._aux_auth import _get_single_user_with_group_ids
19
- from fractal_server.app.models import LinkUserGroup
20
- from fractal_server.app.models import UserOAuth
21
- from fractal_server.app.routes.auth._aux_auth import _user_or_404
22
- from fractal_server.app.security import get_user_manager
23
- from fractal_server.app.security import UserManager
24
-
25
- router_users = APIRouter()
26
-
27
-
28
- @router_users.get("/users/{user_id}/", response_model=UserRead)
29
- async def get_user(
30
- user_id: int,
31
- group_ids: bool = True,
32
- superuser: UserOAuth = Depends(current_active_superuser),
33
- db: AsyncSession = Depends(get_async_db),
34
- ) -> UserRead:
35
- user = await _user_or_404(user_id, db)
36
- if group_ids:
37
- user_with_group_ids = await _get_single_user_with_group_ids(user, db)
38
- return user_with_group_ids
39
- else:
40
- return user
41
-
42
-
43
- @router_users.patch("/users/{user_id}/", response_model=UserRead)
44
- async def patch_user(
45
- user_id: int,
46
- user_update: UserUpdate,
47
- current_superuser: UserOAuth = Depends(current_active_superuser),
48
- user_manager: UserManager = Depends(get_user_manager),
49
- db: AsyncSession = Depends(get_async_db),
50
- ):
51
- """
52
- Custom version of the PATCH-user route from `fastapi-users`.
53
- """
54
-
55
- user_to_patch = await _user_or_404(user_id, db)
56
-
57
- try:
58
- user = await user_manager.update(
59
- user_update, user_to_patch, safe=False, request=None
60
- )
61
- patched_user = schemas.model_validate(UserOAuth, user)
62
- except exceptions.InvalidPasswordException as e:
63
- raise HTTPException(
64
- status_code=status.HTTP_400_BAD_REQUEST,
65
- detail={
66
- "code": ErrorCode.UPDATE_USER_INVALID_PASSWORD,
67
- "reason": e.reason,
68
- },
69
- )
70
-
71
- patched_user_with_group_ids = await _get_single_user_with_group_ids(
72
- patched_user, db
73
- )
74
-
75
- return patched_user_with_group_ids
76
-
77
-
78
- @router_users.get("/users/", response_model=list[UserRead])
79
- async def list_users(
80
- user: UserOAuth = Depends(current_active_superuser),
81
- db: AsyncSession = Depends(get_async_db),
82
- ):
83
- """
84
- Return list of all users
85
- """
86
- stm = select(UserOAuth)
87
- res = await db.execute(stm)
88
- user_list = res.scalars().unique().all()
89
-
90
- # Get all user/group links
91
- stm_all_links = select(LinkUserGroup)
92
- res = await db.execute(stm_all_links)
93
- links = res.scalars().all()
94
-
95
- # FIXME GROUPS: this must be optimized
96
- for ind, user in enumerate(user_list):
97
- user_list[ind] = dict(
98
- user.model_dump(),
99
- group_ids=[
100
- link.group_id for link in links if link.user_id == user.id
101
- ],
102
- )
103
- return user_list