fractal-server 2.14.0a9__tar.gz → 2.14.0a11__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 (221) hide show
  1. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/PKG-INFO +1 -1
  2. fractal_server-2.14.0a11/fractal_server/__init__.py +1 -0
  3. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/models/v2/dataset.py +0 -10
  4. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/models/v2/job.py +3 -0
  5. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/api/v2/__init__.py +2 -0
  6. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/api/v2/history.py +14 -9
  7. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/api/v2/images.py +5 -2
  8. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/api/v2/submit.py +16 -14
  9. fractal_server-2.14.0a11/fractal_server/app/routes/api/v2/verify_image_types.py +64 -0
  10. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/api/v2/workflow.py +11 -7
  11. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/components.py +0 -3
  12. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/exceptions.py +4 -0
  13. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/executors/base_runner.py +16 -17
  14. fractal_server-2.14.0a9/fractal_server/app/runner/executors/local/_local_config.py → fractal_server-2.14.0a11/fractal_server/app/runner/executors/local/get_local_config.py +0 -7
  15. fractal_server-2.14.0a11/fractal_server/app/runner/executors/local/runner.py +215 -0
  16. {fractal_server-2.14.0a9/fractal_server/app/runner/executors/slurm_sudo → fractal_server-2.14.0a11/fractal_server/app/runner/executors/slurm_common}/_check_jobs_status.py +4 -0
  17. fractal_server-2.14.0a11/fractal_server/app/runner/executors/slurm_ssh/_check_job_status_ssh.py +67 -0
  18. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/executors/slurm_ssh/executor.py +7 -5
  19. {fractal_server-2.14.0a9/fractal_server/app/runner/executors/slurm_sudo → fractal_server-2.14.0a11/fractal_server/app/runner/executors/slurm_ssh}/runner.py +191 -124
  20. fractal_server-2.14.0a11/fractal_server/app/runner/executors/slurm_sudo/runner.py +791 -0
  21. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/task_files.py +8 -0
  22. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/v2/_local.py +4 -2
  23. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/v2/_slurm_ssh.py +4 -2
  24. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/v2/_slurm_sudo.py +4 -2
  25. fractal_server-2.14.0a11/fractal_server/app/runner/v2/db_tools.py +87 -0
  26. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/v2/runner.py +83 -89
  27. fractal_server-2.14.0a11/fractal_server/app/runner/v2/runner_functions.py +546 -0
  28. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/v2/runner_functions_low_level.py +37 -39
  29. fractal_server-2.14.0a9/fractal_server/app/runner/v2/__init__.py → fractal_server-2.14.0a11/fractal_server/app/runner/v2/submit_workflow.py +1 -0
  30. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/v2/task_interface.py +31 -0
  31. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/schemas/v2/dataset.py +4 -71
  32. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/schemas/v2/dumps.py +6 -5
  33. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/schemas/v2/job.py +6 -3
  34. fractal_server-2.14.0a11/fractal_server/migrations/versions/47351f8c7ebc_drop_dataset_filters.py +50 -0
  35. fractal_server-2.14.0a11/fractal_server/migrations/versions/e81103413827_add_job_type_filters.py +36 -0
  36. fractal_server-2.14.0a11/fractal_server/tasks/v2/__init__.py +0 -0
  37. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/pyproject.toml +2 -2
  38. fractal_server-2.14.0a9/fractal_server/__init__.py +0 -1
  39. fractal_server-2.14.0a9/fractal_server/app/runner/executors/local/_submit_setup.py +0 -46
  40. fractal_server-2.14.0a9/fractal_server/app/runner/executors/local/runner.py +0 -156
  41. fractal_server-2.14.0a9/fractal_server/app/runner/executors/slurm_common/_submit_setup.py +0 -84
  42. fractal_server-2.14.0a9/fractal_server/app/runner/v2/_db_tools.py +0 -48
  43. fractal_server-2.14.0a9/fractal_server/app/runner/v2/runner_functions.py +0 -703
  44. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/LICENSE +0 -0
  45. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/README.md +0 -0
  46. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/__main__.py +0 -0
  47. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/alembic.ini +0 -0
  48. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/__init__.py +0 -0
  49. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/db/__init__.py +0 -0
  50. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/history/__init__.py +0 -0
  51. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/models/__init__.py +0 -0
  52. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/models/linkusergroup.py +0 -0
  53. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/models/linkuserproject.py +0 -0
  54. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/models/security.py +0 -0
  55. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/models/user_settings.py +0 -0
  56. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/models/v2/__init__.py +0 -0
  57. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/models/v2/accounting.py +0 -0
  58. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/models/v2/history.py +0 -0
  59. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/models/v2/project.py +0 -0
  60. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/models/v2/task.py +0 -0
  61. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/models/v2/task_group.py +0 -0
  62. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/models/v2/workflow.py +0 -0
  63. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/models/v2/workflowtask.py +0 -0
  64. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/__init__.py +0 -0
  65. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/admin/__init__.py +0 -0
  66. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/admin/v2/__init__.py +0 -0
  67. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/admin/v2/accounting.py +0 -0
  68. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/admin/v2/impersonate.py +0 -0
  69. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/admin/v2/job.py +0 -0
  70. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/admin/v2/project.py +0 -0
  71. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/admin/v2/task.py +0 -0
  72. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/admin/v2/task_group.py +0 -0
  73. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/admin/v2/task_group_lifecycle.py +0 -0
  74. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/api/__init__.py +0 -0
  75. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/api/v2/_aux_functions.py +0 -0
  76. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/api/v2/_aux_functions_history.py +0 -0
  77. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +0 -0
  78. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/api/v2/_aux_functions_tasks.py +0 -0
  79. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/api/v2/dataset.py +0 -0
  80. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/api/v2/job.py +0 -0
  81. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/api/v2/project.py +0 -0
  82. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/api/v2/status_legacy.py +0 -0
  83. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/api/v2/task.py +0 -0
  84. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/api/v2/task_collection.py +0 -0
  85. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/api/v2/task_collection_custom.py +0 -0
  86. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/api/v2/task_group.py +0 -0
  87. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/api/v2/task_group_lifecycle.py +0 -0
  88. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/api/v2/workflow_import.py +0 -0
  89. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/api/v2/workflowtask.py +0 -0
  90. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/auth/__init__.py +0 -0
  91. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/auth/_aux_auth.py +0 -0
  92. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/auth/current_user.py +0 -0
  93. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/auth/group.py +0 -0
  94. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/auth/login.py +0 -0
  95. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/auth/oauth.py +0 -0
  96. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/auth/register.py +0 -0
  97. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/auth/router.py +0 -0
  98. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/auth/users.py +0 -0
  99. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/aux/__init__.py +0 -0
  100. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/aux/_job.py +0 -0
  101. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/aux/_runner.py +0 -0
  102. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/aux/validate_user_settings.py +0 -0
  103. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/routes/pagination.py +0 -0
  104. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/__init__.py +0 -0
  105. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/compress_folder.py +0 -0
  106. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/executors/__init__.py +0 -0
  107. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/executors/local/__init__.py +0 -0
  108. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/executors/slurm_common/__init__.py +0 -0
  109. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/executors/slurm_common/_batching.py +0 -0
  110. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/executors/slurm_common/_job_states.py +0 -0
  111. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/executors/slurm_common/_slurm_config.py +0 -0
  112. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/executors/slurm_common/get_slurm_config.py +0 -0
  113. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/executors/slurm_common/remote.py +0 -0
  114. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/executors/slurm_common/utils_executors.py +0 -0
  115. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/executors/slurm_ssh/__init__.py +0 -0
  116. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/executors/slurm_ssh/_executor_wait_thread.py +0 -0
  117. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/executors/slurm_ssh/_slurm_job.py +0 -0
  118. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/executors/slurm_sudo/__init__.py +0 -0
  119. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/executors/slurm_sudo/_subprocess_run_as_user.py +0 -0
  120. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/extract_archive.py +0 -0
  121. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/filenames.py +0 -0
  122. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/run_subprocess.py +0 -0
  123. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/set_start_and_last_task_index.py +0 -0
  124. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/shutdown.py +0 -0
  125. {fractal_server-2.14.0a9/fractal_server/tasks → fractal_server-2.14.0a11/fractal_server/app/runner}/v2/__init__.py +0 -0
  126. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/v2/deduplicate_list.py +0 -0
  127. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/v2/merge_outputs.py +0 -0
  128. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/runner/versions.py +0 -0
  129. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/schemas/__init__.py +0 -0
  130. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/schemas/_filter_validators.py +0 -0
  131. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/schemas/_validators.py +0 -0
  132. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/schemas/user.py +0 -0
  133. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/schemas/user_group.py +0 -0
  134. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/schemas/user_settings.py +0 -0
  135. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/schemas/v2/__init__.py +0 -0
  136. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/schemas/v2/accounting.py +0 -0
  137. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/schemas/v2/history.py +0 -0
  138. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/schemas/v2/manifest.py +0 -0
  139. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/schemas/v2/project.py +0 -0
  140. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/schemas/v2/status_legacy.py +0 -0
  141. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/schemas/v2/task.py +0 -0
  142. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/schemas/v2/task_collection.py +0 -0
  143. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/schemas/v2/task_group.py +0 -0
  144. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/schemas/v2/workflow.py +0 -0
  145. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/schemas/v2/workflowtask.py +0 -0
  146. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/security/__init__.py +0 -0
  147. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/security/signup_email.py +0 -0
  148. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/app/user_settings.py +0 -0
  149. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/config.py +0 -0
  150. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/data_migrations/README.md +0 -0
  151. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/data_migrations/tools.py +0 -0
  152. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/gunicorn_fractal.py +0 -0
  153. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/images/__init__.py +0 -0
  154. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/images/models.py +0 -0
  155. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/images/tools.py +0 -0
  156. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/logger.py +0 -0
  157. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/main.py +0 -0
  158. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/env.py +0 -0
  159. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/naming_convention.py +0 -0
  160. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/034a469ec2eb_task_groups.py +0 -0
  161. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/091b01f51f88_add_usergroup_and_linkusergroup_table.py +0 -0
  162. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/19eca0dd47a9_user_settings_project_dir.py +0 -0
  163. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/1eac13a26c83_drop_v1_tables.py +0 -0
  164. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/316140ff7ee1_remove_usersettings_cache_dir.py +0 -0
  165. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py +0 -0
  166. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/4cedeb448a53_workflowtask_foreign_keys_not_nullables.py +0 -0
  167. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/501961cfcd85_remove_link_between_v1_and_v2_tasks_.py +0 -0
  168. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/50a13d6138fd_initial_schema.py +0 -0
  169. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/5bf02391cfef_v2.py +0 -0
  170. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/70e77f1c38b0_add_applyworkflow_first_task_index_and_.py +0 -0
  171. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/71eefd1dd202_add_slurm_accounts.py +0 -0
  172. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/84bf0fffde30_add_dumps_to_applyworkflow.py +0 -0
  173. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/8e8f227a3e36_update_taskv2_post_2_7_0.py +0 -0
  174. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/8f79bd162e35_add_docs_info_and_docs_link_to_task_.py +0 -0
  175. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/94a47ea2d3ff_remove_cache_dir_slurm_user_and_slurm_.py +0 -0
  176. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/97f444d47249_add_applyworkflow_project_dump.py +0 -0
  177. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/99ea79d9e5d2_add_dataset_history.py +0 -0
  178. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/9c5ae74c9b98_add_user_settings_table.py +0 -0
  179. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/9fd26a2b0de4_add_workflow_timestamp_created.py +0 -0
  180. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/a7f4d6137b53_add_workflow_dump_to_applyworkflow.py +0 -0
  181. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/af1ef1c83c9b_add_accounting_tables.py +0 -0
  182. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/af8673379a5c_drop_old_filter_columns.py +0 -0
  183. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/d256a7379ab8_taskgroup_activity_and_venv_info_to_.py +0 -0
  184. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/d4fe3708d309_make_applyworkflow_workflow_dump_non_.py +0 -0
  185. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/da2cb2ac4255_user_group_viewer_paths.py +0 -0
  186. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/db09233ad13a_split_filters_and_keep_old_columns.py +0 -0
  187. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/e75cac726012_make_applyworkflow_start_timestamp_not_.py +0 -0
  188. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/efa89c30e0a4_add_project_timestamp_created.py +0 -0
  189. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/f384e1c0cf5d_drop_task_default_args_columns.py +0 -0
  190. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/migrations/versions/fbce16ff4e47_new_history_items.py +0 -0
  191. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/py.typed +0 -0
  192. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/ssh/__init__.py +0 -0
  193. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/ssh/_fabric.py +0 -0
  194. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/string_tools.py +0 -0
  195. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/syringe.py +0 -0
  196. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/tasks/__init__.py +0 -0
  197. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/tasks/utils.py +0 -0
  198. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/tasks/v2/local/__init__.py +0 -0
  199. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/tasks/v2/local/_utils.py +0 -0
  200. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/tasks/v2/local/collect.py +0 -0
  201. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/tasks/v2/local/deactivate.py +0 -0
  202. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/tasks/v2/local/reactivate.py +0 -0
  203. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/tasks/v2/ssh/__init__.py +0 -0
  204. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/tasks/v2/ssh/_utils.py +0 -0
  205. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/tasks/v2/ssh/collect.py +0 -0
  206. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/tasks/v2/ssh/deactivate.py +0 -0
  207. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/tasks/v2/ssh/reactivate.py +0 -0
  208. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/tasks/v2/templates/1_create_venv.sh +0 -0
  209. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/tasks/v2/templates/2_pip_install.sh +0 -0
  210. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/tasks/v2/templates/3_pip_freeze.sh +0 -0
  211. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/tasks/v2/templates/4_pip_show.sh +0 -0
  212. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/tasks/v2/templates/5_get_venv_size_and_file_number.sh +0 -0
  213. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/tasks/v2/templates/6_pip_install_from_freeze.sh +0 -0
  214. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/tasks/v2/utils_background.py +0 -0
  215. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/tasks/v2/utils_database.py +0 -0
  216. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/tasks/v2/utils_package_names.py +0 -0
  217. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/tasks/v2/utils_python_interpreter.py +0 -0
  218. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/tasks/v2/utils_templates.py +0 -0
  219. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/urls.py +0 -0
  220. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/fractal_server/utils.py +0 -0
  221. {fractal_server-2.14.0a9 → fractal_server-2.14.0a11}/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.14.0a9
3
+ Version: 2.14.0a11
4
4
  Summary: Backend component of the Fractal analytics platform
5
5
  License: BSD-3-Clause
6
6
  Author: Tommaso Comparin
@@ -0,0 +1 @@
1
+ __VERSION__ = "2.14.0a11"
@@ -11,7 +11,6 @@ from sqlmodel import Relationship
11
11
  from sqlmodel import SQLModel
12
12
 
13
13
  from ....utils import get_timestamp
14
- from fractal_server.images.models import AttributeFiltersType
15
14
 
16
15
 
17
16
  class DatasetV2(SQLModel, table=True):
@@ -34,20 +33,11 @@ class DatasetV2(SQLModel, table=True):
34
33
  sa_column=Column(DateTime(timezone=True), nullable=False),
35
34
  )
36
35
 
37
- # New in V2
38
-
39
36
  zarr_dir: str
40
37
  images: list[dict[str, Any]] = Field(
41
38
  sa_column=Column(JSON, server_default="[]", nullable=False)
42
39
  )
43
40
 
44
- type_filters: dict[str, bool] = Field(
45
- sa_column=Column(JSON, nullable=False, server_default="{}")
46
- )
47
- attribute_filters: AttributeFiltersType = Field(
48
- sa_column=Column(JSON, nullable=False, server_default="{}")
49
- )
50
-
51
41
  @property
52
42
  def image_zarr_urls(self) -> list[str]:
53
43
  return [image["zarr_url"] for image in self.images]
@@ -56,3 +56,6 @@ class JobV2(SQLModel, table=True):
56
56
  attribute_filters: AttributeFiltersType = Field(
57
57
  sa_column=Column(JSON, nullable=False, server_default="{}")
58
58
  )
59
+ type_filters: dict[str, bool] = Field(
60
+ sa_column=Column(JSON, nullable=False, server_default="{}")
61
+ )
@@ -15,6 +15,7 @@ from .task_collection import router as task_collection_router_v2
15
15
  from .task_collection_custom import router as task_collection_router_v2_custom
16
16
  from .task_group import router as task_group_router_v2
17
17
  from .task_group_lifecycle import router as task_group_lifecycle_router_v2
18
+ from .verify_image_types import router as verify_image_types_router
18
19
  from .workflow import router as workflow_router_v2
19
20
  from .workflow_import import router as workflow_import_router_v2
20
21
  from .workflowtask import router as workflowtask_router_v2
@@ -25,6 +26,7 @@ from fractal_server.syringe import Inject
25
26
  router_api_v2 = APIRouter()
26
27
 
27
28
  router_api_v2.include_router(dataset_router_v2, tags=["V2 Dataset"])
29
+ router_api_v2.include_router(verify_image_types_router, tags=["V2 Job"])
28
30
  router_api_v2.include_router(job_router_v2, tags=["V2 Job"])
29
31
  router_api_v2.include_router(images_routes_v2, tags=["V2 Images"])
30
32
  router_api_v2.include_router(project_router_v2, tags=["V2 Project"])
@@ -1,3 +1,5 @@
1
+ from typing import Optional
2
+
1
3
  from fastapi import APIRouter
2
4
  from fastapi import Depends
3
5
  from fastapi import HTTPException
@@ -165,6 +167,7 @@ async def get_history_run_units(
165
167
  dataset_id: int,
166
168
  workflowtask_id: int,
167
169
  history_run_id: int,
170
+ unit_status: Optional[HistoryUnitStatus] = None,
168
171
  user: UserOAuth = Depends(current_active_user),
169
172
  db: AsyncSession = Depends(get_async_db),
170
173
  pagination: PaginationRequest = Depends(get_pagination_params),
@@ -183,21 +186,23 @@ async def get_history_run_units(
183
186
  await get_history_run_or_404(history_run_id=history_run_id, db=db)
184
187
 
185
188
  # Count `HistoryUnit`s
186
- res = await db.execute(
187
- select(func.count(HistoryUnit.id)).where(
188
- HistoryUnit.history_run_id == history_run_id
189
- )
189
+ stmt = select(func.count(HistoryUnit.id)).where(
190
+ HistoryUnit.history_run_id == history_run_id
190
191
  )
192
+ if unit_status:
193
+ stmt = stmt.where(HistoryUnit.status == unit_status)
194
+ res = await db.execute(stmt)
191
195
  total_count = res.scalar()
192
196
  page_size = pagination.page_size or total_count
193
197
 
194
198
  # Query `HistoryUnit`s
195
- res = await db.execute(
196
- select(HistoryUnit)
197
- .where(HistoryUnit.history_run_id == history_run_id)
198
- .offset((pagination.page - 1) * page_size)
199
- .limit(page_size)
199
+ stmt = select(HistoryUnit).where(
200
+ HistoryUnit.history_run_id == history_run_id
200
201
  )
202
+ if unit_status:
203
+ stmt = stmt.where(HistoryUnit.status == unit_status)
204
+ stmt = stmt.offset((pagination.page - 1) * page_size).limit(page_size)
205
+ res = await db.execute(stmt)
201
206
  units = res.scalars().all()
202
207
 
203
208
  return dict(
@@ -41,7 +41,6 @@ class ImagePage(PaginationResponse[SingleImage]):
41
41
 
42
42
 
43
43
  class ImageQuery(BaseModel):
44
- zarr_url: Optional[str] = None
45
44
  type_filters: dict[str, bool] = Field(default_factory=dict)
46
45
  attribute_filters: AttributeFiltersType = Field(default_factory=dict)
47
46
 
@@ -56,6 +55,10 @@ class ImageQuery(BaseModel):
56
55
  )
57
56
 
58
57
 
58
+ class ImageQueryWithZarrUrl(ImageQuery):
59
+ zarr_url: Optional[str] = None
60
+
61
+
59
62
  @router.post(
60
63
  "/project/{project_id}/dataset/{dataset_id}/images/",
61
64
  status_code=status.HTTP_201_CREATED,
@@ -115,7 +118,7 @@ async def post_new_image(
115
118
  async def query_dataset_images(
116
119
  project_id: int,
117
120
  dataset_id: int,
118
- query: Optional[ImageQuery] = None,
121
+ query: Optional[ImageQueryWithZarrUrl] = None,
119
122
  pagination: PaginationRequest = Depends(get_pagination_params),
120
123
  user: UserOAuth = Depends(current_active_user),
121
124
  db: AsyncSession = Depends(get_async_db),
@@ -11,30 +11,32 @@ from fastapi import Request
11
11
  from fastapi import status
12
12
  from sqlmodel import select
13
13
 
14
- from .....config import get_settings
15
- from .....logger import set_logger
16
- from .....syringe import Inject
17
- from ....db import AsyncSession
18
- from ....db import get_async_db
19
- from ....models.v2 import JobV2
20
- from ....runner.set_start_and_last_task_index import (
21
- set_start_and_last_task_index,
22
- )
23
- from ....runner.v2 import submit_workflow
24
- from ....schemas.v2 import JobCreateV2
25
- from ....schemas.v2 import JobReadV2
26
- from ....schemas.v2 import JobStatusTypeV2
27
- from ...aux.validate_user_settings import validate_user_settings
28
14
  from ._aux_functions import _get_dataset_check_owner
29
15
  from ._aux_functions import _get_workflow_check_owner
30
16
  from ._aux_functions import clean_app_job_list_v2
31
17
  from ._aux_functions_tasks import _check_type_filters_compatibility
18
+ from fractal_server.app.db import AsyncSession
19
+ from fractal_server.app.db import get_async_db
32
20
  from fractal_server.app.models import TaskGroupV2
33
21
  from fractal_server.app.models import UserOAuth
22
+ from fractal_server.app.models.v2 import JobV2
34
23
  from fractal_server.app.routes.api.v2._aux_functions_tasks import (
35
24
  _get_task_read_access,
36
25
  )
37
26
  from fractal_server.app.routes.auth import current_active_verified_user
27
+ from fractal_server.app.routes.aux.validate_user_settings import (
28
+ validate_user_settings,
29
+ )
30
+ from fractal_server.app.runner.set_start_and_last_task_index import (
31
+ set_start_and_last_task_index,
32
+ )
33
+ from fractal_server.app.runner.v2.submit_workflow import submit_workflow
34
+ from fractal_server.app.schemas.v2 import JobCreateV2
35
+ from fractal_server.app.schemas.v2 import JobReadV2
36
+ from fractal_server.app.schemas.v2 import JobStatusTypeV2
37
+ from fractal_server.config import get_settings
38
+ from fractal_server.logger import set_logger
39
+ from fractal_server.syringe import Inject
38
40
 
39
41
 
40
42
  router = APIRouter()
@@ -0,0 +1,64 @@
1
+ from typing import Optional
2
+
3
+ from fastapi import APIRouter
4
+ from fastapi import Depends
5
+ from fastapi import status
6
+
7
+ from ._aux_functions import _get_dataset_check_owner
8
+ from .images import ImageQuery
9
+ from fractal_server.app.db import AsyncSession
10
+ from fractal_server.app.db import get_async_db
11
+ from fractal_server.app.models import UserOAuth
12
+ from fractal_server.app.routes.auth import current_active_user
13
+ from fractal_server.images.tools import filter_image_list
14
+
15
+ router = APIRouter()
16
+
17
+
18
+ @router.post(
19
+ "/project/{project_id}/dataset/{dataset_id}/images/verify-unique-types/",
20
+ status_code=status.HTTP_200_OK,
21
+ )
22
+ async def verify_unique_types(
23
+ project_id: int,
24
+ dataset_id: int,
25
+ query: Optional[ImageQuery] = None,
26
+ user: UserOAuth = Depends(current_active_user),
27
+ db: AsyncSession = Depends(get_async_db),
28
+ ) -> list[str]:
29
+ # Get dataset
30
+ output = await _get_dataset_check_owner(
31
+ project_id=project_id, dataset_id=dataset_id, user_id=user.id, db=db
32
+ )
33
+ dataset = output["dataset"]
34
+
35
+ # Filter images
36
+ if query is None:
37
+ filtered_images = dataset.images
38
+ else:
39
+ filtered_images = filter_image_list(
40
+ images=dataset.images,
41
+ attribute_filters=query.attribute_filters,
42
+ type_filters=query.type_filters,
43
+ )
44
+
45
+ # Get all available types (#FIXME use aux function)
46
+ available_types = set(
47
+ _type for _img in filtered_images for _type in _img["types"].keys()
48
+ )
49
+
50
+ # Get actual values for each available type
51
+ values_per_type: dict[str, set] = {
52
+ _type: set() for _type in available_types
53
+ }
54
+ for _img in filtered_images:
55
+ for _type in available_types:
56
+ values_per_type[_type].add(_img["types"].get(_type, False))
57
+
58
+ # Find types with non-unique value
59
+ non_unique_types = [
60
+ key for key, value in values_per_type.items() if len(value) > 1
61
+ ]
62
+ non_unique_types = sorted(non_unique_types)
63
+
64
+ return non_unique_types
@@ -290,6 +290,7 @@ async def get_user_workflows(
290
290
 
291
291
 
292
292
  class WorkflowTaskTypeFiltersInfo(BaseModel):
293
+ workflowtask_id: int
293
294
  current_type_filters: dict[str, bool]
294
295
  input_type_filters: dict[str, bool]
295
296
  output_type_filters: dict[str, bool]
@@ -301,7 +302,7 @@ async def get_workflow_type_filters(
301
302
  workflow_id: int,
302
303
  user: UserOAuth = Depends(current_active_user),
303
304
  db: AsyncSession = Depends(get_async_db),
304
- ) -> dict[str, WorkflowTaskTypeFiltersInfo]:
305
+ ) -> list[WorkflowTaskTypeFiltersInfo]:
305
306
  """
306
307
  Get info on type/type-filters flow for a workflow.
307
308
  """
@@ -322,7 +323,7 @@ async def get_workflow_type_filters(
322
323
 
323
324
  current_type_filters = {}
324
325
 
325
- response = {}
326
+ response_items = []
326
327
  for wftask in workflow.task_list:
327
328
 
328
329
  # Compute input_type_filters, based on wftask and task manifest
@@ -332,13 +333,16 @@ async def get_workflow_type_filters(
332
333
  )
333
334
 
334
335
  # Append current item to response list
335
- response[str(wftask.id)] = dict(
336
- current_type_filters=copy(current_type_filters),
337
- input_type_filters=copy(input_type_filters),
338
- output_type_filters=copy(wftask.task.output_types),
336
+ response_items.append(
337
+ dict(
338
+ workflowtask_id=wftask.id,
339
+ current_type_filters=copy(current_type_filters),
340
+ input_type_filters=copy(input_type_filters),
341
+ output_type_filters=copy(wftask.task.output_types),
342
+ )
339
343
  )
340
344
 
341
345
  # Update `current_type_filters`
342
346
  current_type_filters.update(wftask.task.output_types)
343
347
 
344
- return response
348
+ return response_items
@@ -1,5 +1,2 @@
1
1
  def _index_to_component(ind: int) -> str:
2
2
  return f"{ind:07d}"
3
-
4
-
5
- _COMPONENT_KEY_ = "__FRACTAL_PARALLEL_COMPONENT__"
@@ -37,6 +37,10 @@ class TaskExecutionError(RuntimeError):
37
37
  self.task_name = task_name
38
38
 
39
39
 
40
+ class TaskOutputValidationError(ValueError):
41
+ pass
42
+
43
+
40
44
  class JobExecutionError(RuntimeError):
41
45
  """
42
46
  Forwards errors in the execution of a task that are due to external factors
@@ -1,6 +1,6 @@
1
1
  from typing import Any
2
2
 
3
- from fractal_server.app.runner.components import _COMPONENT_KEY_
3
+ from fractal_server.app.runner.task_files import TaskFiles
4
4
  from fractal_server.app.schemas.v2.task import TaskTypeType
5
5
 
6
6
 
@@ -29,9 +29,10 @@ class BaseRunner(object):
29
29
  self,
30
30
  func: callable,
31
31
  parameters: dict[str, Any],
32
- history_item_id: int,
32
+ history_unit_id: int,
33
+ task_files: TaskFiles,
33
34
  task_type: TaskTypeType,
34
- **kwargs,
35
+ config: Any,
35
36
  ) -> tuple[Any, BaseException]:
36
37
  """
37
38
  Run a single fractal task.
@@ -45,7 +46,7 @@ class BaseRunner(object):
45
46
  history_item_id:
46
47
  Database ID of the corresponding `HistoryItemV2` entry.
47
48
  task_type: Task type.
48
- kwargs: Runner-specific parameters.
49
+ config: Runner-specific parameters.
49
50
  """
50
51
  raise NotImplementedError()
51
52
 
@@ -53,9 +54,10 @@ class BaseRunner(object):
53
54
  self,
54
55
  func: callable,
55
56
  list_parameters: list[dict[str, Any]],
56
- history_item_id: int,
57
+ history_unit_ids: list[int],
58
+ list_task_files: list[TaskFiles],
57
59
  task_type: TaskTypeType,
58
- **kwargs,
60
+ config: Any,
59
61
  ) -> tuple[dict[int, Any], dict[int, BaseException]]:
60
62
  """
61
63
  Run a parallel fractal task.
@@ -70,7 +72,7 @@ class BaseRunner(object):
70
72
  history_item_id:
71
73
  Database ID of the corresponding `HistoryItemV2` entry.
72
74
  task_type: Task type.
73
- kwargs: Runner-specific parameters.
75
+ config: Runner-specific parameters.
74
76
  """
75
77
  raise NotImplementedError()
76
78
 
@@ -101,15 +103,11 @@ class BaseRunner(object):
101
103
  f"Forbidden 'zarr_urls' key in {list(parameters.keys())}"
102
104
  )
103
105
 
104
- if _COMPONENT_KEY_ not in parameters.keys():
105
- raise ValueError(
106
- f"No '{_COMPONENT_KEY_}' key in in {list(parameters.keys())}"
107
- )
108
-
109
106
  def validate_multisubmit_parameters(
110
107
  self,
111
108
  list_parameters: list[dict[str, Any]],
112
109
  task_type: TaskTypeType,
110
+ list_task_files: list[TaskFiles],
113
111
  ) -> None:
114
112
  """
115
113
  Validate parameters for `multi_submit` method
@@ -121,6 +119,12 @@ class BaseRunner(object):
121
119
  if task_type not in TASK_TYPES_MULTISUBMIT:
122
120
  raise ValueError(f"Invalid {task_type=} for `multisubmit`.")
123
121
 
122
+ subfolders = set(
123
+ task_file.wftask_subfolder_local for task_file in list_task_files
124
+ )
125
+ if len(subfolders) != 1:
126
+ raise ValueError(f"More than one subfolders: {subfolders}.")
127
+
124
128
  if not isinstance(list_parameters, list):
125
129
  raise ValueError("`parameters` must be a list.")
126
130
 
@@ -131,11 +135,6 @@ class BaseRunner(object):
131
135
  raise ValueError(
132
136
  f"No 'zarr_url' key in in {list(single_kwargs.keys())}"
133
137
  )
134
- if _COMPONENT_KEY_ not in single_kwargs.keys():
135
- raise ValueError(
136
- f"No '{_COMPONENT_KEY_}' key "
137
- f"in {list(single_kwargs.keys())}"
138
- )
139
138
  if task_type == "parallel":
140
139
  zarr_urls = [kwargs["zarr_url"] for kwargs in list_parameters]
141
140
  if len(zarr_urls) != len(set(zarr_urls)):
@@ -48,13 +48,6 @@ class LocalBackendConfig(BaseModel):
48
48
  parallel_tasks_per_job: Optional[int] = None
49
49
 
50
50
 
51
- def get_default_local_backend_config():
52
- """
53
- Return a default `LocalBackendConfig` configuration object
54
- """
55
- return LocalBackendConfig(parallel_tasks_per_job=None)
56
-
57
-
58
51
  def get_local_backend_config(
59
52
  wftask: WorkflowTaskV2,
60
53
  which_type: Literal["non_parallel", "parallel"],
@@ -0,0 +1,215 @@
1
+ from concurrent.futures import Future
2
+ from concurrent.futures import ThreadPoolExecutor
3
+ from pathlib import Path
4
+ from typing import Any
5
+ from typing import Literal
6
+
7
+ from .get_local_config import LocalBackendConfig
8
+ from fractal_server.app.db import get_sync_db
9
+ from fractal_server.app.runner.executors.base_runner import BaseRunner
10
+ from fractal_server.app.runner.task_files import TaskFiles
11
+ from fractal_server.app.runner.v2.db_tools import update_status_of_history_unit
12
+ from fractal_server.app.schemas.v2 import HistoryUnitStatus
13
+ from fractal_server.logger import set_logger
14
+
15
+ logger = set_logger(__name__)
16
+
17
+
18
+ class LocalRunner(BaseRunner):
19
+ executor: ThreadPoolExecutor
20
+ root_dir_local: Path
21
+
22
+ def __init__(
23
+ self,
24
+ root_dir_local: Path,
25
+ ):
26
+
27
+ self.root_dir_local = root_dir_local
28
+ self.root_dir_local.mkdir(parents=True, exist_ok=True)
29
+ self.executor = ThreadPoolExecutor()
30
+ logger.debug("Create LocalRunner")
31
+
32
+ def __enter__(self):
33
+ logger.debug("Enter LocalRunner")
34
+ return self
35
+
36
+ def shutdown(self):
37
+ logger.debug("Now shut LocalRunner.executor down")
38
+ self.executor.shutdown(
39
+ wait=False,
40
+ cancel_futures=True,
41
+ )
42
+
43
+ def __exit__(self, exc_type, exc_val, exc_tb):
44
+ logger.debug("Exit LocalRunner")
45
+ self.shutdown()
46
+ return self.executor.__exit__(exc_type, exc_val, exc_tb)
47
+
48
+ def submit(
49
+ self,
50
+ func: callable,
51
+ parameters: dict[str, Any],
52
+ history_unit_id: int,
53
+ task_files: TaskFiles,
54
+ task_type: Literal[
55
+ "non_parallel",
56
+ "converter_non_parallel",
57
+ "compound",
58
+ "converter_compound",
59
+ ],
60
+ config: LocalBackendConfig,
61
+ ) -> tuple[Any, Exception]:
62
+ logger.debug("[submit] START")
63
+
64
+ self.validate_submit_parameters(parameters, task_type=task_type)
65
+ workdir_local = task_files.wftask_subfolder_local
66
+ workdir_local.mkdir()
67
+
68
+ # SUBMISSION PHASE
69
+ future = self.executor.submit(
70
+ func,
71
+ parameters=parameters,
72
+ remote_files=task_files.remote_files_dict,
73
+ )
74
+
75
+ # RETRIEVAL PHASE
76
+ with next(get_sync_db()) as db:
77
+ try:
78
+ result = future.result()
79
+ logger.debug("[submit] END with result")
80
+ if task_type not in ["compound", "converter_compound"]:
81
+ update_status_of_history_unit(
82
+ history_unit_id=history_unit_id,
83
+ status=HistoryUnitStatus.DONE,
84
+ db_sync=db,
85
+ )
86
+ return result, None
87
+ except Exception as e:
88
+ exception = e
89
+ logger.debug("[submit] END with exception")
90
+ update_status_of_history_unit(
91
+ history_unit_id=history_unit_id,
92
+ status=HistoryUnitStatus.FAILED,
93
+ db_sync=db,
94
+ )
95
+
96
+ return None, exception
97
+
98
+ def multisubmit(
99
+ self,
100
+ func: callable,
101
+ list_parameters: list[dict],
102
+ history_unit_ids: list[int],
103
+ list_task_files: list[TaskFiles],
104
+ task_type: Literal["parallel", "compound", "converter_compound"],
105
+ config: LocalBackendConfig,
106
+ ):
107
+ """
108
+ Note:
109
+
110
+ 1. The number of sruns and futures is equal to `len(list_parameters)`.
111
+ 2. The number of `HistoryUnit`s is equal to `len(history_unit_ids)`.
112
+ 3. For compound tasks, these two numbers are not the same.
113
+
114
+ For this reason, we defer database updates to the caller function,
115
+ when we are in one of the "compound" cases
116
+
117
+ """
118
+ # FIXME: De-duplicate this check
119
+ if task_type in ["compound", "converter_compound"]:
120
+ if len(history_unit_ids) != 1:
121
+ raise NotImplementedError(
122
+ "We are breaking the assumption that compound/multisubmit "
123
+ "is associated to a single HistoryUnit. This is not "
124
+ "supported."
125
+ )
126
+ elif task_type == "parallel" and len(history_unit_ids) != len(
127
+ list_parameters
128
+ ):
129
+ raise ValueError(
130
+ f"{len(history_unit_ids)=} differs from "
131
+ f"{len(list_parameters)=}."
132
+ )
133
+
134
+ logger.debug(f"[multisubmit] START, {len(list_parameters)=}")
135
+
136
+ self.validate_multisubmit_parameters(
137
+ list_parameters=list_parameters,
138
+ task_type=task_type,
139
+ list_task_files=list_task_files,
140
+ )
141
+
142
+ workdir_local = list_task_files[0].wftask_subfolder_local
143
+ if task_type == "parallel":
144
+ workdir_local.mkdir()
145
+
146
+ # Set `n_elements` and `parallel_tasks_per_job`
147
+ n_elements = len(list_parameters)
148
+ parallel_tasks_per_job = config.parallel_tasks_per_job
149
+ if parallel_tasks_per_job is None:
150
+ parallel_tasks_per_job = n_elements
151
+
152
+ # Execute tasks, in chunks of size `parallel_tasks_per_job`
153
+ results: dict[int, Any] = {}
154
+ exceptions: dict[int, BaseException] = {}
155
+ for ind_chunk in range(0, n_elements, parallel_tasks_per_job):
156
+ list_parameters_chunk = list_parameters[
157
+ ind_chunk : ind_chunk + parallel_tasks_per_job
158
+ ]
159
+
160
+ active_futures: dict[int, Future] = {}
161
+ for ind_within_chunk, kwargs in enumerate(list_parameters_chunk):
162
+ positional_index = ind_chunk + ind_within_chunk
163
+ future = self.executor.submit(
164
+ func,
165
+ parameters=kwargs,
166
+ remote_files=list_task_files[
167
+ positional_index
168
+ ].remote_files_dict,
169
+ )
170
+ active_futures[positional_index] = future
171
+
172
+ while active_futures:
173
+ # FIXME: add shutdown detection
174
+ # if file exists: cancel all futures, and raise
175
+ finished_futures = [
176
+ index_and_future
177
+ for index_and_future in active_futures.items()
178
+ if not index_and_future[1].running()
179
+ ]
180
+ if len(finished_futures) == 0:
181
+ continue
182
+
183
+ with next(get_sync_db()) as db:
184
+ for positional_index, fut in finished_futures:
185
+ active_futures.pop(positional_index)
186
+ if task_type == "parallel":
187
+ current_history_unit_id = history_unit_ids[
188
+ positional_index
189
+ ]
190
+
191
+ try:
192
+ results[positional_index] = fut.result()
193
+ if task_type == "parallel":
194
+ update_status_of_history_unit(
195
+ history_unit_id=current_history_unit_id,
196
+ status=HistoryUnitStatus.DONE,
197
+ db_sync=db,
198
+ )
199
+
200
+ except Exception as e:
201
+ exceptions[positional_index] = e
202
+ if task_type == "parallel":
203
+ update_status_of_history_unit(
204
+ history_unit_id=current_history_unit_id,
205
+ status=HistoryUnitStatus.FAILED,
206
+ db_sync=db,
207
+ )
208
+
209
+ # FIXME: what should happen here? Option 1: stop
210
+ # all existing tasks and shutdown runner (for the
211
+ # compound-task case)
212
+
213
+ logger.debug(f"[multisubmit] END, {results=}, {exceptions=}")
214
+
215
+ return results, exceptions
@@ -32,6 +32,10 @@ def run_squeue(job_ids: list[str]) -> subprocess.CompletedProcess:
32
32
  return res
33
33
 
34
34
 
35
+ def are_all_jobs_on_squeue(job_ids: list[str]) -> bool:
36
+ pass
37
+
38
+
35
39
  def get_finished_jobs(job_ids: list[str]) -> set[str]:
36
40
  """
37
41
  Check which ones of the given Slurm jobs already finished