fractal-server 2.12.0a1__tar.gz → 2.12.1__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 (206) hide show
  1. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/PKG-INFO +2 -2
  2. fractal_server-2.12.1/fractal_server/__init__.py +1 -0
  3. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/__main__.py +17 -63
  4. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/api/v2/images.py +0 -12
  5. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/executors/slurm/ssh/_slurm_job.py +0 -1
  6. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/executors/slurm/ssh/executor.py +14 -23
  7. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/executors/slurm/sudo/executor.py +52 -17
  8. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/security/__init__.py +6 -3
  9. fractal_server-2.12.1/fractal_server/app/security/signup_email.py +48 -0
  10. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/config.py +92 -51
  11. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/main.py +12 -9
  12. fractal_server-2.12.1/fractal_server/migrations/env.py +49 -0
  13. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/v2/local/collect.py +9 -8
  14. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/v2/local/deactivate.py +3 -0
  15. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/v2/local/reactivate.py +3 -0
  16. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/v2/ssh/collect.py +8 -8
  17. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/v2/ssh/deactivate.py +3 -0
  18. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/v2/ssh/reactivate.py +9 -6
  19. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/pyproject.toml +4 -3
  20. fractal_server-2.12.0a1/fractal_server/__init__.py +0 -1
  21. fractal_server-2.12.0a1/fractal_server/app/security/signup_email.py +0 -39
  22. fractal_server-2.12.0a1/fractal_server/migrations/env.py +0 -96
  23. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/LICENSE +0 -0
  24. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/README.md +0 -0
  25. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/alembic.ini +0 -0
  26. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/__init__.py +0 -0
  27. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/db/__init__.py +0 -0
  28. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/models/__init__.py +0 -0
  29. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/models/linkusergroup.py +0 -0
  30. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/models/linkuserproject.py +0 -0
  31. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/models/security.py +0 -0
  32. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/models/user_settings.py +0 -0
  33. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/models/v2/__init__.py +0 -0
  34. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/models/v2/dataset.py +0 -0
  35. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/models/v2/job.py +0 -0
  36. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/models/v2/project.py +0 -0
  37. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/models/v2/task.py +0 -0
  38. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/models/v2/task_group.py +0 -0
  39. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/models/v2/workflow.py +0 -0
  40. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/models/v2/workflowtask.py +0 -0
  41. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/__init__.py +0 -0
  42. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/admin/__init__.py +0 -0
  43. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/admin/v2/__init__.py +0 -0
  44. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/admin/v2/job.py +0 -0
  45. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/admin/v2/project.py +0 -0
  46. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/admin/v2/task.py +0 -0
  47. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/admin/v2/task_group.py +0 -0
  48. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/admin/v2/task_group_lifecycle.py +0 -0
  49. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/api/__init__.py +0 -0
  50. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/api/v2/__init__.py +0 -0
  51. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/api/v2/_aux_functions.py +0 -0
  52. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +0 -0
  53. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/api/v2/_aux_functions_tasks.py +0 -0
  54. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/api/v2/dataset.py +0 -0
  55. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/api/v2/job.py +0 -0
  56. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/api/v2/project.py +0 -0
  57. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/api/v2/status.py +0 -0
  58. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/api/v2/submit.py +0 -0
  59. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/api/v2/task.py +0 -0
  60. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/api/v2/task_collection.py +0 -0
  61. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/api/v2/task_collection_custom.py +0 -0
  62. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/api/v2/task_group.py +0 -0
  63. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/api/v2/task_group_lifecycle.py +0 -0
  64. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/api/v2/workflow.py +0 -0
  65. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/api/v2/workflow_import.py +0 -0
  66. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/api/v2/workflowtask.py +0 -0
  67. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/auth/__init__.py +0 -0
  68. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/auth/_aux_auth.py +0 -0
  69. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/auth/current_user.py +0 -0
  70. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/auth/group.py +0 -0
  71. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/auth/login.py +0 -0
  72. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/auth/oauth.py +0 -0
  73. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/auth/register.py +0 -0
  74. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/auth/router.py +0 -0
  75. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/auth/users.py +0 -0
  76. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/aux/__init__.py +0 -0
  77. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/aux/_job.py +0 -0
  78. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/aux/_runner.py +0 -0
  79. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/routes/aux/validate_user_settings.py +0 -0
  80. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/__init__.py +0 -0
  81. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/components.py +0 -0
  82. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/compress_folder.py +0 -0
  83. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/exceptions.py +0 -0
  84. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/executors/__init__.py +0 -0
  85. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/executors/slurm/__init__.py +0 -0
  86. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/executors/slurm/_batching.py +0 -0
  87. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/executors/slurm/_slurm_config.py +0 -0
  88. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/executors/slurm/remote.py +0 -0
  89. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/executors/slurm/ssh/__init__.py +0 -0
  90. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/executors/slurm/ssh/_executor_wait_thread.py +0 -0
  91. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/executors/slurm/sudo/__init__.py +0 -0
  92. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/executors/slurm/sudo/_check_jobs_status.py +0 -0
  93. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/executors/slurm/sudo/_executor_wait_thread.py +0 -0
  94. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/executors/slurm/sudo/_subprocess_run_as_user.py +0 -0
  95. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/executors/slurm/utils_executors.py +0 -0
  96. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/extract_archive.py +0 -0
  97. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/filenames.py +0 -0
  98. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/run_subprocess.py +0 -0
  99. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/set_start_and_last_task_index.py +0 -0
  100. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/shutdown.py +0 -0
  101. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/task_files.py +0 -0
  102. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/v2/__init__.py +0 -0
  103. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/v2/_local/__init__.py +0 -0
  104. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/v2/_local/_local_config.py +0 -0
  105. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/v2/_local/_submit_setup.py +0 -0
  106. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/v2/_local/executor.py +0 -0
  107. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/v2/_local_experimental/__init__.py +0 -0
  108. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/v2/_local_experimental/_local_config.py +0 -0
  109. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/v2/_local_experimental/_submit_setup.py +0 -0
  110. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/v2/_local_experimental/executor.py +0 -0
  111. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/v2/_slurm_common/__init__.py +0 -0
  112. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/v2/_slurm_common/get_slurm_config.py +0 -0
  113. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/v2/_slurm_ssh/__init__.py +0 -0
  114. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/v2/_slurm_ssh/_submit_setup.py +0 -0
  115. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/v2/_slurm_sudo/__init__.py +0 -0
  116. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/v2/_slurm_sudo/_submit_setup.py +0 -0
  117. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/v2/deduplicate_list.py +0 -0
  118. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/v2/handle_failed_job.py +0 -0
  119. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/v2/merge_outputs.py +0 -0
  120. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/v2/runner.py +0 -0
  121. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/v2/runner_functions.py +0 -0
  122. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/v2/runner_functions_low_level.py +0 -0
  123. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/v2/task_interface.py +0 -0
  124. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/runner/versions.py +0 -0
  125. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/schemas/__init__.py +0 -0
  126. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/schemas/_filter_validators.py +0 -0
  127. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/schemas/_validators.py +0 -0
  128. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/schemas/user.py +0 -0
  129. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/schemas/user_group.py +0 -0
  130. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/schemas/user_settings.py +0 -0
  131. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/schemas/v2/__init__.py +0 -0
  132. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/schemas/v2/dataset.py +0 -0
  133. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/schemas/v2/dumps.py +0 -0
  134. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/schemas/v2/job.py +0 -0
  135. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/schemas/v2/manifest.py +0 -0
  136. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/schemas/v2/project.py +0 -0
  137. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/schemas/v2/status.py +0 -0
  138. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/schemas/v2/task.py +0 -0
  139. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/schemas/v2/task_collection.py +0 -0
  140. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/schemas/v2/task_group.py +0 -0
  141. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/schemas/v2/workflow.py +0 -0
  142. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/schemas/v2/workflowtask.py +0 -0
  143. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/app/user_settings.py +0 -0
  144. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/data_migrations/README.md +0 -0
  145. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/data_migrations/tools.py +0 -0
  146. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/gunicorn_fractal.py +0 -0
  147. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/images/__init__.py +0 -0
  148. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/images/models.py +0 -0
  149. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/images/tools.py +0 -0
  150. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/logger.py +0 -0
  151. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/naming_convention.py +0 -0
  152. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/034a469ec2eb_task_groups.py +0 -0
  153. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/091b01f51f88_add_usergroup_and_linkusergroup_table.py +0 -0
  154. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/19eca0dd47a9_user_settings_project_dir.py +0 -0
  155. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/1eac13a26c83_drop_v1_tables.py +0 -0
  156. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/316140ff7ee1_remove_usersettings_cache_dir.py +0 -0
  157. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py +0 -0
  158. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/4cedeb448a53_workflowtask_foreign_keys_not_nullables.py +0 -0
  159. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/501961cfcd85_remove_link_between_v1_and_v2_tasks_.py +0 -0
  160. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/50a13d6138fd_initial_schema.py +0 -0
  161. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/5bf02391cfef_v2.py +0 -0
  162. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/70e77f1c38b0_add_applyworkflow_first_task_index_and_.py +0 -0
  163. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/71eefd1dd202_add_slurm_accounts.py +0 -0
  164. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/84bf0fffde30_add_dumps_to_applyworkflow.py +0 -0
  165. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/8e8f227a3e36_update_taskv2_post_2_7_0.py +0 -0
  166. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/8f79bd162e35_add_docs_info_and_docs_link_to_task_.py +0 -0
  167. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/94a47ea2d3ff_remove_cache_dir_slurm_user_and_slurm_.py +0 -0
  168. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/97f444d47249_add_applyworkflow_project_dump.py +0 -0
  169. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/99ea79d9e5d2_add_dataset_history.py +0 -0
  170. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/9c5ae74c9b98_add_user_settings_table.py +0 -0
  171. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/9fd26a2b0de4_add_workflow_timestamp_created.py +0 -0
  172. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/a7f4d6137b53_add_workflow_dump_to_applyworkflow.py +0 -0
  173. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/af8673379a5c_drop_old_filter_columns.py +0 -0
  174. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/d256a7379ab8_taskgroup_activity_and_venv_info_to_.py +0 -0
  175. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/d4fe3708d309_make_applyworkflow_workflow_dump_non_.py +0 -0
  176. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/da2cb2ac4255_user_group_viewer_paths.py +0 -0
  177. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/db09233ad13a_split_filters_and_keep_old_columns.py +0 -0
  178. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/e75cac726012_make_applyworkflow_start_timestamp_not_.py +0 -0
  179. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/efa89c30e0a4_add_project_timestamp_created.py +0 -0
  180. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/migrations/versions/f384e1c0cf5d_drop_task_default_args_columns.py +0 -0
  181. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/py.typed +0 -0
  182. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/ssh/__init__.py +0 -0
  183. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/ssh/_fabric.py +0 -0
  184. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/string_tools.py +0 -0
  185. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/syringe.py +0 -0
  186. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/__init__.py +0 -0
  187. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/utils.py +0 -0
  188. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/v2/__init__.py +0 -0
  189. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/v2/local/__init__.py +0 -0
  190. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/v2/local/_utils.py +0 -0
  191. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/v2/ssh/__init__.py +0 -0
  192. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/v2/ssh/_utils.py +0 -0
  193. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/v2/templates/1_create_venv.sh +0 -0
  194. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/v2/templates/2_pip_install.sh +0 -0
  195. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/v2/templates/3_pip_freeze.sh +0 -0
  196. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/v2/templates/4_pip_show.sh +0 -0
  197. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/v2/templates/5_get_venv_size_and_file_number.sh +0 -0
  198. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/v2/templates/6_pip_install_from_freeze.sh +0 -0
  199. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/v2/utils_background.py +0 -0
  200. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/v2/utils_database.py +0 -0
  201. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/v2/utils_package_names.py +0 -0
  202. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/v2/utils_python_interpreter.py +0 -0
  203. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/tasks/v2/utils_templates.py +0 -0
  204. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/urls.py +0 -0
  205. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/utils.py +0 -0
  206. {fractal_server-2.12.0a1 → fractal_server-2.12.1}/fractal_server/zip_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: fractal-server
3
- Version: 2.12.0a1
3
+ Version: 2.12.1
4
4
  Summary: Backend component of the Fractal analytics platform
5
5
  Home-page: https://github.com/fractal-analytics-platform/fractal-server
6
6
  License: BSD-3-Clause
@@ -27,7 +27,7 @@ Requires-Dist: pydantic (>=1.10.8,<2)
27
27
  Requires-Dist: python-dotenv (>=1.0.0,<1.1.0)
28
28
  Requires-Dist: sqlalchemy[asyncio] (>=2.0.23,<2.1)
29
29
  Requires-Dist: sqlmodel (==0.0.21)
30
- Requires-Dist: uvicorn (==0.29.0)
30
+ Requires-Dist: uvicorn (>=0.29.0,<0.35.0)
31
31
  Requires-Dist: uvicorn-worker (==0.2.0)
32
32
  Project-URL: Documentation, https://fractal-analytics-platform.github.io/fractal-server
33
33
  Project-URL: Repository, https://github.com/fractal-analytics-platform/fractal-server
@@ -0,0 +1 @@
1
+ __VERSION__ = "2.12.1"
@@ -63,45 +63,18 @@ update_db_data_parser = subparsers.add_parser(
63
63
  description="Apply data-migration script to an existing database.",
64
64
  )
65
65
 
66
- # fractalctl email-settings
67
- email_settings_parser = subparsers.add_parser(
68
- "email-settings",
66
+ # fractalctl encrypt-email-password
67
+ encrypt_email_password_parser = subparsers.add_parser(
68
+ "encrypt-email-password",
69
69
  description=(
70
70
  "Generate valid values for environment variables "
71
- "`FRACTAL_EMAIL_SETTINGS` and `FRACTAL_EMAIL_SETTINGS_KEY`."
71
+ "FRACTAL_EMAIL_PASSWORD and FRACTAL_EMAIL_PASSWORD_KEY."
72
72
  ),
73
73
  )
74
- email_settings_parser.add_argument(
75
- "sender",
76
- type=str,
77
- help="Email of the sender",
78
- )
79
- email_settings_parser.add_argument(
80
- "server",
81
- type=str,
82
- help="SMPT server used to send emails",
83
- )
84
- email_settings_parser.add_argument(
85
- "port",
86
- type=int,
87
- help="Port of the SMPT server",
88
- )
89
- email_settings_parser.add_argument(
90
- "instance",
91
- type=str,
92
- help="Name of the Fractal instance sending emails",
93
- )
94
- email_settings_parser.add_argument(
95
- "--skip-starttls",
96
- action="store_true",
97
- default=False,
98
- help="If set, skip the execution of `starttls` when sending emails",
99
- )
100
74
 
101
75
 
102
76
  def save_openapi(dest="openapi.json"):
103
77
  from fractal_server.main import start_application
104
- import json
105
78
 
106
79
  app = start_application()
107
80
  openapi_schema = app.openapi()
@@ -129,6 +102,11 @@ def set_db(skip_init_data: bool = False):
129
102
  from pathlib import Path
130
103
  import fractal_server
131
104
 
105
+ # Check settings
106
+ settings = Inject(get_settings)
107
+ settings.check_db()
108
+
109
+ # Perform migrations
132
110
  alembic_ini = Path(fractal_server.__file__).parent / "alembic.ini"
133
111
  alembic_args = ["-c", alembic_ini.as_posix(), "upgrade", "head"]
134
112
  print(f"START: Run alembic.config, with argv={alembic_args}")
@@ -138,12 +116,10 @@ def set_db(skip_init_data: bool = False):
138
116
  if skip_init_data:
139
117
  return
140
118
 
141
- # Insert default group
119
+ # Create default group and user
142
120
  print()
143
121
  _create_first_group()
144
122
  print()
145
- # NOTE: It will be fixed with #1739
146
- settings = Inject(get_settings)
147
123
  asyncio.run(
148
124
  _create_first_user(
149
125
  email=settings.FRACTAL_DEFAULT_ADMIN_EMAIL,
@@ -224,31 +200,15 @@ def update_db_data():
224
200
  current_update_db_data_module.fix_db()
225
201
 
226
202
 
227
- def print_mail_settings(
228
- sender: str,
229
- server: str,
230
- port: int,
231
- instance: str,
232
- skip_starttls: bool,
233
- ):
203
+ def print_encrypted_password():
234
204
  from cryptography.fernet import Fernet
235
205
 
236
- password = input(f"Insert email password for sender '{sender}': ")
206
+ password = input("Insert email password: ").encode("utf-8")
237
207
  key = Fernet.generate_key().decode("utf-8")
238
- fractal_mail_settings = json.dumps(
239
- dict(
240
- sender=sender,
241
- password=password,
242
- smtp_server=server,
243
- port=port,
244
- instance_name=instance,
245
- use_starttls=(not skip_starttls),
246
- )
247
- ).encode("utf-8")
248
- email_settings = Fernet(key).encrypt(fractal_mail_settings).decode("utf-8")
208
+ encrypted_password = Fernet(key).encrypt(password).decode("utf-8")
249
209
 
250
- print(f"\nFRACTAL_EMAIL_SETTINGS: {email_settings}")
251
- print(f"FRACTAL_EMAIL_SETTINGS_KEY: {key}")
210
+ print(f"\nFRACTAL_EMAIL_PASSWORD={encrypted_password}")
211
+ print(f"FRACTAL_EMAIL_PASSWORD_KEY={key}")
252
212
 
253
213
 
254
214
  def run():
@@ -267,14 +227,8 @@ def run():
267
227
  port=args.port,
268
228
  reload=args.reload,
269
229
  )
270
- elif args.cmd == "email-settings":
271
- print_mail_settings(
272
- sender=args.sender,
273
- server=args.server,
274
- port=args.port,
275
- instance=args.instance,
276
- skip_starttls=args.skip_starttls,
277
- )
230
+ elif args.cmd == "encrypt-email-password":
231
+ print_encrypted_password()
278
232
  else:
279
233
  sys.exit(f"Error: invalid command '{args.cmd}'.")
280
234
 
@@ -118,7 +118,6 @@ async def post_new_image(
118
118
  async def query_dataset_images(
119
119
  project_id: int,
120
120
  dataset_id: int,
121
- use_dataset_filters: bool = False, # query param
122
121
  page: int = 1, # query param
123
122
  page_size: Optional[int] = None, # query param
124
123
  query: Optional[ImageQuery] = None, # body
@@ -138,17 +137,6 @@ async def query_dataset_images(
138
137
  dataset = output["dataset"]
139
138
  images = dataset.images
140
139
 
141
- if use_dataset_filters is True:
142
- images = [
143
- image
144
- for image in images
145
- if match_filter(
146
- image=image,
147
- type_filters=dataset.type_filters,
148
- attribute_filters=dataset.attribute_filters,
149
- )
150
- ]
151
-
152
140
  attributes = {}
153
141
  for image in images:
154
142
  for k, v in image["attributes"].items():
@@ -88,7 +88,6 @@ class SlurmJob:
88
88
  self,
89
89
  num_tasks_tot: int,
90
90
  slurm_config: SlurmConfig,
91
- workflow_task_file_prefix: Optional[str] = None,
92
91
  slurm_file_prefix: Optional[str] = None,
93
92
  wftask_file_prefixes: Optional[tuple[str, ...]] = None,
94
93
  single_task_submission: bool = False,
@@ -62,7 +62,6 @@ class FractalSlurmSSHExecutor(SlurmExecutor):
62
62
  shutdown_file:
63
63
  python_remote: Equal to `settings.FRACTAL_SLURM_WORKER_PYTHON`
64
64
  wait_thread_cls: Class for waiting thread
65
- keep_pickle_files:
66
65
  workflow_dir_local:
67
66
  Directory for both the cfut/SLURM and fractal-server files and logs
68
67
  workflow_dir_remote:
@@ -84,7 +83,6 @@ class FractalSlurmSSHExecutor(SlurmExecutor):
84
83
  python_remote: str
85
84
 
86
85
  wait_thread_cls = FractalSlurmWaitThread
87
- keep_pickle_files: bool
88
86
 
89
87
  common_script_lines: list[str]
90
88
  slurm_account: Optional[str]
@@ -100,8 +98,6 @@ class FractalSlurmSSHExecutor(SlurmExecutor):
100
98
  # Folders and files
101
99
  workflow_dir_local: Path,
102
100
  workflow_dir_remote: Path,
103
- # Runner options
104
- keep_pickle_files: bool = False,
105
101
  # Monitoring options
106
102
  slurm_poll_interval: Optional[int] = None,
107
103
  # SLURM submission script options
@@ -120,7 +116,6 @@ class FractalSlurmSSHExecutor(SlurmExecutor):
120
116
  fractal_ssh:
121
117
  workflow_dir_local:
122
118
  workflow_dir_remote:
123
- keep_pickle_files:
124
119
  slurm_poll_interval:
125
120
  common_script_lines:
126
121
  slurm_account:
@@ -194,7 +189,6 @@ class FractalSlurmSSHExecutor(SlurmExecutor):
194
189
  raise e
195
190
 
196
191
  # Set/initialize some more options
197
- self.keep_pickle_files = keep_pickle_files
198
192
  self.map_jobid_to_slurm_files_local = {}
199
193
 
200
194
  def _validate_common_script_lines(self):
@@ -901,12 +895,11 @@ class FractalSlurmSSHExecutor(SlurmExecutor):
901
895
  pass
902
896
  for job_id in remaining_job_ids:
903
897
  self._cleanup(job_id)
904
- if not self.keep_pickle_files:
905
- for job in remaining_jobs:
906
- for path in job.output_pickle_files_local:
907
- path.unlink()
908
- for path in job.input_pickle_files_local:
909
- path.unlink()
898
+ for job in remaining_jobs:
899
+ for path in job.output_pickle_files_local:
900
+ path.unlink()
901
+ for path in job.input_pickle_files_local:
902
+ path.unlink()
910
903
 
911
904
  def _completion(self, job_ids: list[str]) -> None:
912
905
  """
@@ -1001,8 +994,7 @@ class FractalSlurmSSHExecutor(SlurmExecutor):
1001
994
  f"Future {future} (SLURM job ID: {job_id}) "
1002
995
  "was already cancelled."
1003
996
  )
1004
- if not self.keep_pickle_files:
1005
- in_path.unlink()
997
+ in_path.unlink()
1006
998
  self._cleanup(job_id)
1007
999
  self._handle_remaining_jobs(
1008
1000
  remaining_futures=remaining_futures,
@@ -1062,17 +1054,15 @@ class FractalSlurmSSHExecutor(SlurmExecutor):
1062
1054
  remaining_job_ids=remaining_job_ids,
1063
1055
  )
1064
1056
  return
1065
- if not self.keep_pickle_files:
1066
- out_path.unlink()
1057
+ out_path.unlink()
1067
1058
  except InvalidStateError:
1068
1059
  logger.warning(
1069
1060
  f"Future {future} (SLURM job ID: {job_id}) was "
1070
1061
  "already cancelled, exit from "
1071
1062
  "FractalSlurmSSHExecutor._completion."
1072
1063
  )
1073
- if not self.keep_pickle_files:
1074
- out_path.unlink()
1075
- in_path.unlink()
1064
+ out_path.unlink()
1065
+ in_path.unlink()
1076
1066
 
1077
1067
  self._cleanup(job_id)
1078
1068
  self._handle_remaining_jobs(
@@ -1082,8 +1072,7 @@ class FractalSlurmSSHExecutor(SlurmExecutor):
1082
1072
  return
1083
1073
 
1084
1074
  # Clean up input pickle file
1085
- if not self.keep_pickle_files:
1086
- in_path.unlink()
1075
+ in_path.unlink()
1087
1076
  self._cleanup(job_id)
1088
1077
  if job.single_task_submission:
1089
1078
  future.set_result(outputs[0])
@@ -1210,8 +1199,10 @@ class FractalSlurmSSHExecutor(SlurmExecutor):
1210
1199
  script_lines = slurm_config.sort_script_lines(script_lines)
1211
1200
  logger.debug(script_lines)
1212
1201
 
1213
- # Always print output of `pwd`
1214
- script_lines.append('echo "Working directory (pwd): `pwd`"\n')
1202
+ # Always print output of `uname -n` and `pwd`
1203
+ script_lines.append(
1204
+ '"Hostname: `uname -n`; current directory: `pwd`"\n'
1205
+ )
1215
1206
 
1216
1207
  # Complete script preamble
1217
1208
  script_lines.append("\n")
@@ -10,6 +10,7 @@
10
10
  #
11
11
  # Copyright 2022 (C) Friedrich Miescher Institute for Biomedical Research and
12
12
  # University of Zurich
13
+ import json
13
14
  import math
14
15
  import shlex
15
16
  import subprocess # nosec
@@ -161,7 +162,6 @@ class SlurmJob:
161
162
  self,
162
163
  num_tasks_tot: int,
163
164
  slurm_config: SlurmConfig,
164
- workflow_task_file_prefix: Optional[str] = None,
165
165
  slurm_file_prefix: Optional[str] = None,
166
166
  wftask_file_prefixes: Optional[tuple[str, ...]] = None,
167
167
  single_task_submission: bool = False,
@@ -219,7 +219,6 @@ class FractalSlurmExecutor(SlurmExecutor):
219
219
  workflow_dir_local: Path
220
220
  workflow_dir_remote: Path
221
221
  map_jobid_to_slurm_files: dict[str, tuple[str, str, str]]
222
- keep_pickle_files: bool
223
222
  slurm_account: Optional[str]
224
223
  jobs: dict[str, tuple[Future, SlurmJob]]
225
224
 
@@ -232,7 +231,6 @@ class FractalSlurmExecutor(SlurmExecutor):
232
231
  user_cache_dir: Optional[str] = None,
233
232
  common_script_lines: Optional[list[str]] = None,
234
233
  slurm_poll_interval: Optional[int] = None,
235
- keep_pickle_files: bool = False,
236
234
  slurm_account: Optional[str] = None,
237
235
  *args,
238
236
  **kwargs,
@@ -253,11 +251,18 @@ class FractalSlurmExecutor(SlurmExecutor):
253
251
  # raised within `__init__`).
254
252
  self.wait_thread.shutdown_callback = self.shutdown
255
253
 
256
- self.keep_pickle_files = keep_pickle_files
257
254
  self.slurm_user = slurm_user
258
255
  self.slurm_account = slurm_account
259
256
 
260
257
  self.common_script_lines = common_script_lines or []
258
+ settings = Inject(get_settings)
259
+
260
+ if settings.FRACTAL_SLURM_WORKER_PYTHON is not None:
261
+ try:
262
+ self.check_remote_python_interpreter()
263
+ except Exception as e:
264
+ self._stop_and_join_wait_thread()
265
+ raise RuntimeError(f"Original error {str(e)}")
261
266
 
262
267
  # Check that SLURM account is not set here
263
268
  try:
@@ -289,7 +294,6 @@ class FractalSlurmExecutor(SlurmExecutor):
289
294
  # Set the attribute slurm_poll_interval for self.wait_thread (see
290
295
  # cfut.SlurmWaitThread)
291
296
  if not slurm_poll_interval:
292
- settings = Inject(get_settings)
293
297
  slurm_poll_interval = settings.FRACTAL_SLURM_POLL_INTERVAL
294
298
  self.wait_thread.slurm_poll_interval = slurm_poll_interval
295
299
  self.wait_thread.slurm_user = self.slurm_user
@@ -608,7 +612,14 @@ class FractalSlurmExecutor(SlurmExecutor):
608
612
  _prefixes = []
609
613
  _subfolder_names = []
610
614
  for component in components:
611
- actual_component = component.get(_COMPONENT_KEY_, None)
615
+ # In Fractal, `component` is a `dict` by construction (e.g.
616
+ # `component = {"zarr_url": "/something", "param": 1}``). The
617
+ # try/except covers the case of e.g. `executor.map([1, 2])`,
618
+ # which is useful for testing.
619
+ try:
620
+ actual_component = component.get(_COMPONENT_KEY_, None)
621
+ except AttributeError:
622
+ actual_component = str(component)
612
623
  _task_file_paths = get_task_file_paths(
613
624
  workflow_dir_local=task_files.workflow_dir_local,
614
625
  workflow_dir_remote=task_files.workflow_dir_remote,
@@ -860,8 +871,7 @@ class FractalSlurmExecutor(SlurmExecutor):
860
871
  " cancelled, exit from"
861
872
  " FractalSlurmExecutor._completion."
862
873
  )
863
- if not self.keep_pickle_files:
864
- in_path.unlink()
874
+ in_path.unlink()
865
875
  self._cleanup(jobid)
866
876
  return
867
877
 
@@ -903,23 +913,20 @@ class FractalSlurmExecutor(SlurmExecutor):
903
913
  exc = TaskExecutionError(proxy.tb, **kwargs)
904
914
  fut.set_exception(exc)
905
915
  return
906
- if not self.keep_pickle_files:
907
- out_path.unlink()
916
+ out_path.unlink()
908
917
  except InvalidStateError:
909
918
  logger.warning(
910
919
  f"Future {fut} (SLURM job ID: {jobid}) was already"
911
920
  " cancelled, exit from"
912
921
  " FractalSlurmExecutor._completion."
913
922
  )
914
- if not self.keep_pickle_files:
915
- out_path.unlink()
916
- in_path.unlink()
923
+ out_path.unlink()
924
+ in_path.unlink()
917
925
  self._cleanup(jobid)
918
926
  return
919
927
 
920
928
  # Clean up input pickle file
921
- if not self.keep_pickle_files:
922
- in_path.unlink()
929
+ in_path.unlink()
923
930
  self._cleanup(jobid)
924
931
  if job.single_task_submission:
925
932
  fut.set_result(outputs[0])
@@ -1155,8 +1162,10 @@ class FractalSlurmExecutor(SlurmExecutor):
1155
1162
  script_lines = slurm_config.sort_script_lines(script_lines)
1156
1163
  logger.debug(script_lines)
1157
1164
 
1158
- # Always print output of `pwd`
1159
- script_lines.append('echo "Working directory (pwd): `pwd`"\n')
1165
+ # Always print output of `uname -n` and `pwd`
1166
+ script_lines.append(
1167
+ '"Hostname: `uname -n`; current directory: `pwd`"\n'
1168
+ )
1160
1169
 
1161
1170
  # Complete script preamble
1162
1171
  script_lines.append("\n")
@@ -1243,3 +1252,29 @@ class FractalSlurmExecutor(SlurmExecutor):
1243
1252
  )
1244
1253
  self._stop_and_join_wait_thread()
1245
1254
  logger.debug("[FractalSlurmExecutor.__exit__] End")
1255
+
1256
+ def check_remote_python_interpreter(self):
1257
+ """
1258
+ Check fractal-server version on the _remote_ Python interpreter.
1259
+ """
1260
+ settings = Inject(get_settings)
1261
+ output = _subprocess_run_or_raise(
1262
+ (
1263
+ f"{settings.FRACTAL_SLURM_WORKER_PYTHON} "
1264
+ "-m fractal_server.app.runner.versions"
1265
+ )
1266
+ )
1267
+ runner_version = json.loads(output.stdout.strip("\n"))[
1268
+ "fractal_server"
1269
+ ]
1270
+
1271
+ if runner_version != __VERSION__:
1272
+ error_msg = (
1273
+ "Fractal-server version mismatch.\n"
1274
+ "Local interpreter: "
1275
+ f"({sys.executable}): {__VERSION__}.\n"
1276
+ "Remote interpreter: "
1277
+ f"({settings.FRACTAL_SLURM_WORKER_PYTHON}): {runner_version}."
1278
+ )
1279
+ logger.error(error_msg)
1280
+ raise ValueError(error_msg)
@@ -252,15 +252,18 @@ class UserManager(IntegerIDMixin, BaseUserManager[UserOAuth, int]):
252
252
  # Send mail section
253
253
  settings = Inject(get_settings)
254
254
 
255
- if this_user.oauth_accounts and settings.MAIL_SETTINGS is not None:
255
+ if (
256
+ this_user.oauth_accounts
257
+ and settings.email_settings is not None
258
+ ):
256
259
  try:
257
260
  logger.info(
258
261
  "START sending email about new signup to "
259
- f"{settings.MAIL_SETTINGS.recipients}."
262
+ f"{settings.email_settings.recipients}."
260
263
  )
261
264
  mail_new_oauth_signup(
262
265
  msg=f"New user registered: '{this_user.email}'.",
263
- mail_settings=settings.MAIL_SETTINGS,
266
+ email_settings=settings.email_settings,
264
267
  )
265
268
  logger.info("END sending email about new signup.")
266
269
  except Exception as e:
@@ -0,0 +1,48 @@
1
+ import smtplib
2
+ from email.message import EmailMessage
3
+ from email.utils import formataddr
4
+
5
+ from cryptography.fernet import Fernet
6
+
7
+ from fractal_server.config import MailSettings
8
+
9
+
10
+ def mail_new_oauth_signup(msg: str, email_settings: MailSettings):
11
+ """
12
+ Send an email using the specified settings to notify a new OAuth signup.
13
+ """
14
+
15
+ mail_msg = EmailMessage()
16
+ mail_msg.set_content(msg)
17
+ mail_msg["From"] = formataddr(
18
+ (email_settings.sender, email_settings.sender)
19
+ )
20
+ mail_msg["To"] = ", ".join(
21
+ [
22
+ formataddr((recipient, recipient))
23
+ for recipient in email_settings.recipients
24
+ ]
25
+ )
26
+ mail_msg[
27
+ "Subject"
28
+ ] = f"[Fractal, {email_settings.instance_name}] New OAuth signup"
29
+
30
+ with smtplib.SMTP(
31
+ email_settings.smtp_server, email_settings.port
32
+ ) as server:
33
+ server.ehlo()
34
+ if email_settings.use_starttls:
35
+ server.starttls()
36
+ server.ehlo()
37
+ if email_settings.use_login:
38
+ password = (
39
+ Fernet(email_settings.encryption_key)
40
+ .decrypt(email_settings.encrypted_password)
41
+ .decode("utf-8")
42
+ )
43
+ server.login(user=email_settings.sender, password=password)
44
+ server.sendmail(
45
+ from_addr=email_settings.sender,
46
+ to_addrs=email_settings.recipients,
47
+ msg=mail_msg.as_string(),
48
+ )