fractal-server 2.6.4__tar.gz → 2.7.0a0__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.
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/PKG-INFO +1 -1
- fractal_server-2.7.0a0/fractal_server/__init__.py +1 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/models/v2/__init__.py +2 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/models/v2/task.py +27 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v2/__init__.py +4 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v2/_aux_functions.py +0 -61
- fractal_server-2.7.0a0/fractal_server/app/routes/api/v2/_aux_functions_tasks.py +209 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v2/submit.py +16 -3
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v2/task.py +59 -72
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v2/task_collection.py +20 -4
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v2/task_collection_custom.py +44 -18
- fractal_server-2.7.0a0/fractal_server/app/routes/api/v2/task_group.py +130 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v2/workflow.py +24 -3
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v2/workflowtask.py +4 -7
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/auth/_aux_auth.py +42 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/v2/__init__.py +5 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/v2/task.py +2 -1
- fractal_server-2.7.0a0/fractal_server/app/schemas/v2/task_group.py +23 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/v2/workflow.py +5 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/v2/workflowtask.py +4 -0
- fractal_server-2.7.0a0/fractal_server/migrations/versions/7cf1baae8fb4_task_group_v2.py +66 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/tasks/v2/background_operations.py +16 -35
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/tasks/v2/background_operations_ssh.py +15 -2
- fractal_server-2.7.0a0/fractal_server/tasks/v2/database_operations.py +54 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/pyproject.toml +2 -2
- fractal_server-2.6.4/fractal_server/__init__.py +0 -1
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/LICENSE +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/README.md +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/__main__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/alembic.ini +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/db/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/models/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/models/linkusergroup.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/models/linkuserproject.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/models/security.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/models/user_settings.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/models/v1/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/models/v1/dataset.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/models/v1/job.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/models/v1/project.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/models/v1/state.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/models/v1/task.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/models/v1/workflow.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/models/v2/collection_state.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/models/v2/dataset.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/models/v2/job.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/models/v2/project.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/models/v2/workflow.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/models/v2/workflowtask.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/admin/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/admin/v1.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/admin/v2.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v1/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v1/_aux_functions.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v1/dataset.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v1/job.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v1/project.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v1/task.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v1/task_collection.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v1/workflow.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v1/workflowtask.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v2/dataset.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v2/images.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v2/job.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v2/project.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v2/status.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/auth/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/auth/current_user.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/auth/group.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/auth/group_names.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/auth/login.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/auth/oauth.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/auth/register.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/auth/router.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/auth/users.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/aux/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/aux/_job.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/aux/_runner.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/aux/validate_user_settings.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/.gitignore +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/async_wrap.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/components.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/compress_folder.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/exceptions.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/executors/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/executors/slurm/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/executors/slurm/_batching.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/executors/slurm/_slurm_config.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/executors/slurm/remote.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/executors/slurm/ssh/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/executors/slurm/ssh/_executor_wait_thread.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/executors/slurm/ssh/_slurm_job.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/executors/slurm/ssh/executor.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/executors/slurm/sudo/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/executors/slurm/sudo/_check_jobs_status.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/executors/slurm/sudo/_executor_wait_thread.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/executors/slurm/sudo/_subprocess_run_as_user.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/executors/slurm/sudo/executor.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/extract_archive.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/filenames.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/run_subprocess.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/set_start_and_last_task_index.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/shutdown.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/task_files.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v1/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v1/_common.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v1/_local/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v1/_local/_local_config.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v1/_local/_submit_setup.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v1/_local/executor.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v1/_slurm/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v1/_slurm/_submit_setup.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v1/_slurm/get_slurm_config.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v1/common.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v1/handle_failed_job.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v2/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v2/_local/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v2/_local/_local_config.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v2/_local/_submit_setup.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v2/_local/executor.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v2/_local_experimental/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v2/_local_experimental/_local_config.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v2/_local_experimental/_submit_setup.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v2/_local_experimental/executor.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v2/_slurm_common/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v2/_slurm_common/get_slurm_config.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v2/_slurm_ssh/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v2/_slurm_ssh/_submit_setup.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v2/_slurm_sudo/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v2/_slurm_sudo/_submit_setup.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v2/deduplicate_list.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v2/handle_failed_job.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v2/merge_outputs.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v2/runner.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v2/runner_functions.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v2/runner_functions_low_level.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/v2/task_interface.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/runner/versions.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/_validators.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/user.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/user_group.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/user_settings.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/v1/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/v1/applyworkflow.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/v1/dataset.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/v1/dumps.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/v1/manifest.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/v1/project.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/v1/state.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/v1/task.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/v1/task_collection.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/v1/workflow.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/v2/dataset.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/v2/dumps.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/v2/job.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/v2/manifest.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/v2/project.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/v2/status.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/schemas/v2/task_collection.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/security/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/user_settings.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/config.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/data_migrations/README.md +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/data_migrations/tools.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/gunicorn_fractal.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/images/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/images/models.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/images/tools.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/logger.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/main.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/README +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/env.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/naming_convention.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/script.py.mako +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/versions/091b01f51f88_add_usergroup_and_linkusergroup_table.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/versions/4cedeb448a53_workflowtask_foreign_keys_not_nullables.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/versions/501961cfcd85_remove_link_between_v1_and_v2_tasks_.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/versions/50a13d6138fd_initial_schema.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/versions/5bf02391cfef_v2.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/versions/70e77f1c38b0_add_applyworkflow_first_task_index_and_.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/versions/71eefd1dd202_add_slurm_accounts.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/versions/84bf0fffde30_add_dumps_to_applyworkflow.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/versions/8f79bd162e35_add_docs_info_and_docs_link_to_task_.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/versions/94a47ea2d3ff_remove_cache_dir_slurm_user_and_slurm_.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/versions/97f444d47249_add_applyworkflow_project_dump.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/versions/99ea79d9e5d2_add_dataset_history.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/versions/9c5ae74c9b98_add_user_settings_table.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/versions/9fd26a2b0de4_add_workflow_timestamp_created.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/versions/a7f4d6137b53_add_workflow_dump_to_applyworkflow.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/versions/d4fe3708d309_make_applyworkflow_workflow_dump_non_.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/versions/da2cb2ac4255_user_group_viewer_paths.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/versions/e75cac726012_make_applyworkflow_start_timestamp_not_.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/versions/efa89c30e0a4_add_project_timestamp_created.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/migrations/versions/f384e1c0cf5d_drop_task_default_args_columns.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/py.typed +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/ssh/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/ssh/_fabric.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/string_tools.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/syringe.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/tasks/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/tasks/utils.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/tasks/v1/_TaskCollectPip.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/tasks/v1/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/tasks/v1/background_operations.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/tasks/v1/endpoint_operations.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/tasks/v1/get_collection_data.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/tasks/v1/utils.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/tasks/v2/_TaskCollectPip.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/tasks/v2/__init__.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/tasks/v2/_venv_pip.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/tasks/v2/endpoint_operations.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/tasks/v2/templates/_1_create_venv.sh +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/tasks/v2/templates/_2_upgrade_pip.sh +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/tasks/v2/templates/_3_pip_install.sh +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/tasks/v2/templates/_4_pip_freeze.sh +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/tasks/v2/templates/_5_pip_show.sh +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/tasks/v2/utils.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/urls.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/utils.py +0 -0
- {fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/zip_tools.py +0 -0
@@ -0,0 +1 @@
|
|
1
|
+
__VERSION__ = "2.7.0a0"
|
@@ -6,6 +6,7 @@ from .collection_state import CollectionStateV2
|
|
6
6
|
from .dataset import DatasetV2
|
7
7
|
from .job import JobV2
|
8
8
|
from .project import ProjectV2
|
9
|
+
from .task import TaskGroupV2
|
9
10
|
from .task import TaskV2
|
10
11
|
from .workflow import WorkflowV2
|
11
12
|
from .workflowtask import WorkflowTaskV2
|
@@ -16,6 +17,7 @@ __all__ = [
|
|
16
17
|
"JobV2",
|
17
18
|
"ProjectV2",
|
18
19
|
"CollectionStateV2",
|
20
|
+
"TaskGroupV2",
|
19
21
|
"TaskV2",
|
20
22
|
"WorkflowTaskV2",
|
21
23
|
"WorkflowV2",
|
@@ -1,12 +1,17 @@
|
|
1
|
+
from datetime import datetime
|
1
2
|
from typing import Any
|
2
3
|
from typing import Optional
|
3
4
|
|
4
5
|
from pydantic import HttpUrl
|
5
6
|
from sqlalchemy import Column
|
7
|
+
from sqlalchemy.types import DateTime
|
6
8
|
from sqlalchemy.types import JSON
|
7
9
|
from sqlmodel import Field
|
10
|
+
from sqlmodel import Relationship
|
8
11
|
from sqlmodel import SQLModel
|
9
12
|
|
13
|
+
from fractal_server.utils import get_timestamp
|
14
|
+
|
10
15
|
|
11
16
|
class TaskV2(SQLModel, table=True):
|
12
17
|
|
@@ -39,3 +44,25 @@ class TaskV2(SQLModel, table=True):
|
|
39
44
|
|
40
45
|
input_types: dict[str, bool] = Field(sa_column=Column(JSON), default={})
|
41
46
|
output_types: dict[str, bool] = Field(sa_column=Column(JSON), default={})
|
47
|
+
|
48
|
+
taskgroupv2_id: Optional[int] = Field(foreign_key="taskgroupv2.id")
|
49
|
+
|
50
|
+
|
51
|
+
class TaskGroupV2(SQLModel, table=True):
|
52
|
+
|
53
|
+
id: Optional[int] = Field(default=None, primary_key=True)
|
54
|
+
|
55
|
+
user_id: int = Field(foreign_key="user_oauth.id")
|
56
|
+
user_group_id: Optional[int] = Field(foreign_key="usergroup.id")
|
57
|
+
|
58
|
+
active: bool = True
|
59
|
+
task_list: list[TaskV2] = Relationship(
|
60
|
+
sa_relationship_kwargs=dict(
|
61
|
+
lazy="selectin", cascade="all, delete-orphan"
|
62
|
+
),
|
63
|
+
)
|
64
|
+
|
65
|
+
timestamp_created: datetime = Field(
|
66
|
+
default_factory=get_timestamp,
|
67
|
+
sa_column=Column(DateTime(timezone=True), nullable=False),
|
68
|
+
)
|
{fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v2/__init__.py
RENAMED
@@ -12,6 +12,7 @@ from .submit import router as submit_job_router_v2
|
|
12
12
|
from .task import router as task_router_v2
|
13
13
|
from .task_collection import router as task_collection_router_v2
|
14
14
|
from .task_collection_custom import router as task_collection_router_v2_custom
|
15
|
+
from .task_group import router as task_group_router_v2
|
15
16
|
from .workflow import router as workflow_router_v2
|
16
17
|
from .workflowtask import router as workflowtask_router_v2
|
17
18
|
from fractal_server.config import get_settings
|
@@ -37,6 +38,9 @@ router_api_v2.include_router(
|
|
37
38
|
tags=["V2 Task Collection"],
|
38
39
|
)
|
39
40
|
router_api_v2.include_router(task_router_v2, prefix="/task", tags=["V2 Task"])
|
41
|
+
router_api_v2.include_router(
|
42
|
+
task_group_router_v2, prefix="/task-group", tags=["V2 TaskGroup"]
|
43
|
+
)
|
40
44
|
router_api_v2.include_router(workflow_router_v2, tags=["V2 Workflow"])
|
41
45
|
router_api_v2.include_router(workflowtask_router_v2, tags=["V2 WorkflowTask"])
|
42
46
|
router_api_v2.include_router(status_router_v2, tags=["V2 Status"])
|
{fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v2/_aux_functions.py
RENAMED
@@ -21,8 +21,6 @@ from ....models.v2 import TaskV2
|
|
21
21
|
from ....models.v2 import WorkflowTaskV2
|
22
22
|
from ....models.v2 import WorkflowV2
|
23
23
|
from ....schemas.v2 import JobStatusTypeV2
|
24
|
-
from ...aux.validate_user_settings import verify_user_has_settings
|
25
|
-
from fractal_server.app.models import UserOAuth
|
26
24
|
from fractal_server.images import Filters
|
27
25
|
|
28
26
|
|
@@ -320,65 +318,6 @@ async def _get_job_check_owner(
|
|
320
318
|
return dict(job=job, project=project)
|
321
319
|
|
322
320
|
|
323
|
-
async def _get_task_check_owner(
|
324
|
-
*,
|
325
|
-
task_id: int,
|
326
|
-
user: UserOAuth,
|
327
|
-
db: AsyncSession,
|
328
|
-
) -> TaskV2:
|
329
|
-
"""
|
330
|
-
Get a task, after access control.
|
331
|
-
|
332
|
-
This check constitutes a preliminary version of access control:
|
333
|
-
if the current user is not a superuser and differs from the task owner
|
334
|
-
(including when `owner is None`), we raise an 403 HTTP Exception.
|
335
|
-
|
336
|
-
Args:
|
337
|
-
task_id:
|
338
|
-
user:
|
339
|
-
db:
|
340
|
-
|
341
|
-
Returns:
|
342
|
-
The task object.
|
343
|
-
|
344
|
-
Raises:
|
345
|
-
HTTPException(status_code=404_NOT_FOUND):
|
346
|
-
If the task does not exist
|
347
|
-
HTTPException(status_code=403_FORBIDDEN):
|
348
|
-
If the user does not have rights to edit this task.
|
349
|
-
"""
|
350
|
-
task = await db.get(TaskV2, task_id)
|
351
|
-
if not task:
|
352
|
-
raise HTTPException(
|
353
|
-
status_code=status.HTTP_404_NOT_FOUND,
|
354
|
-
detail=f"TaskV2 {task_id} not found.",
|
355
|
-
)
|
356
|
-
|
357
|
-
if not user.is_superuser:
|
358
|
-
if task.owner is None:
|
359
|
-
raise HTTPException(
|
360
|
-
status_code=status.HTTP_403_FORBIDDEN,
|
361
|
-
detail=(
|
362
|
-
"Only a superuser can modify a TaskV2 with `owner=None`."
|
363
|
-
),
|
364
|
-
)
|
365
|
-
else:
|
366
|
-
if user.username:
|
367
|
-
owner = user.username
|
368
|
-
else:
|
369
|
-
verify_user_has_settings(user)
|
370
|
-
owner = user.settings.slurm_user
|
371
|
-
if owner != task.owner:
|
372
|
-
raise HTTPException(
|
373
|
-
status_code=status.HTTP_403_FORBIDDEN,
|
374
|
-
detail=(
|
375
|
-
f"Current user ({owner}) cannot modify TaskV2 "
|
376
|
-
f"{task.id} with different owner ({task.owner})."
|
377
|
-
),
|
378
|
-
)
|
379
|
-
return task
|
380
|
-
|
381
|
-
|
382
321
|
def _get_submitted_jobs_statement() -> SelectOfScalar:
|
383
322
|
"""
|
384
323
|
Returns:
|
@@ -0,0 +1,209 @@
|
|
1
|
+
"""
|
2
|
+
Auxiliary functions to get task and task-group object from the database or
|
3
|
+
perform simple checks
|
4
|
+
"""
|
5
|
+
from typing import Optional
|
6
|
+
|
7
|
+
from fastapi import HTTPException
|
8
|
+
from fastapi import status
|
9
|
+
from sqlmodel import select
|
10
|
+
|
11
|
+
from ....db import AsyncSession
|
12
|
+
from ....models import LinkUserGroup
|
13
|
+
from ....models.v2 import TaskGroupV2
|
14
|
+
from ....models.v2 import TaskV2
|
15
|
+
from ...auth._aux_auth import _get_default_user_group_id
|
16
|
+
from ...auth._aux_auth import _verify_user_belongs_to_group
|
17
|
+
|
18
|
+
|
19
|
+
async def _get_task_group_or_404(
|
20
|
+
*, task_group_id: int, db: AsyncSession
|
21
|
+
) -> TaskGroupV2:
|
22
|
+
"""
|
23
|
+
Get an existing task group or raise a 404.
|
24
|
+
|
25
|
+
Arguments:
|
26
|
+
task_group_id: The TaskGroupV2 id
|
27
|
+
db: An asynchronous db session
|
28
|
+
"""
|
29
|
+
task_group = await db.get(TaskGroupV2, task_group_id)
|
30
|
+
if task_group is None:
|
31
|
+
raise HTTPException(
|
32
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
33
|
+
detail=f"TaskGroupV2 {task_group_id} not found",
|
34
|
+
)
|
35
|
+
return task_group
|
36
|
+
|
37
|
+
|
38
|
+
async def _get_task_group_read_access(
|
39
|
+
*,
|
40
|
+
task_group_id: int,
|
41
|
+
user_id: int,
|
42
|
+
db: AsyncSession,
|
43
|
+
) -> TaskGroupV2:
|
44
|
+
"""
|
45
|
+
Get a task group or raise a 403 if user has no read access.
|
46
|
+
|
47
|
+
Arguments:
|
48
|
+
task_group_id: ID of the required task group.
|
49
|
+
user_id: ID of the current user.
|
50
|
+
db: An asynchronous db session.
|
51
|
+
"""
|
52
|
+
task_group = await _get_task_group_or_404(
|
53
|
+
task_group_id=task_group_id, db=db
|
54
|
+
)
|
55
|
+
|
56
|
+
# Prepare exception to be used below
|
57
|
+
forbidden_exception = HTTPException(
|
58
|
+
status_code=status.HTTP_403_FORBIDDEN,
|
59
|
+
detail=(
|
60
|
+
"Current user has no read access to TaskGroupV2 "
|
61
|
+
f"{task_group_id}.",
|
62
|
+
),
|
63
|
+
)
|
64
|
+
|
65
|
+
if task_group.user_id == user_id:
|
66
|
+
return task_group
|
67
|
+
elif task_group.user_group_id is None:
|
68
|
+
raise forbidden_exception
|
69
|
+
else:
|
70
|
+
stm = (
|
71
|
+
select(LinkUserGroup)
|
72
|
+
.where(LinkUserGroup.group_id == task_group.user_group_id)
|
73
|
+
.where(LinkUserGroup.user_id == user_id)
|
74
|
+
)
|
75
|
+
res = await db.execute(stm)
|
76
|
+
link = res.scalar_one_or_none()
|
77
|
+
if link is None:
|
78
|
+
raise forbidden_exception
|
79
|
+
else:
|
80
|
+
return task_group
|
81
|
+
|
82
|
+
|
83
|
+
async def _get_task_group_full_access(
|
84
|
+
*,
|
85
|
+
task_group_id: int,
|
86
|
+
user_id: int,
|
87
|
+
db: AsyncSession,
|
88
|
+
) -> TaskGroupV2:
|
89
|
+
"""
|
90
|
+
Get a task group or raise a 403 if user has no full access.
|
91
|
+
|
92
|
+
Arguments:
|
93
|
+
task_group_id: ID of the required task group.
|
94
|
+
user_id: ID of the current user.
|
95
|
+
db: An asynchronous db session
|
96
|
+
"""
|
97
|
+
task_group = await _get_task_group_or_404(
|
98
|
+
task_group_id=task_group_id, db=db
|
99
|
+
)
|
100
|
+
|
101
|
+
if task_group.user_id == user_id:
|
102
|
+
return task_group
|
103
|
+
else:
|
104
|
+
raise HTTPException(
|
105
|
+
status_code=status.HTTP_403_FORBIDDEN,
|
106
|
+
detail=(
|
107
|
+
"Current user has no full access to "
|
108
|
+
f"TaskGroupV2 {task_group_id}.",
|
109
|
+
),
|
110
|
+
)
|
111
|
+
|
112
|
+
|
113
|
+
async def _get_task_or_404(*, task_id: int, db: AsyncSession) -> TaskV2:
|
114
|
+
"""
|
115
|
+
Get an existing task or raise a 404.
|
116
|
+
|
117
|
+
Arguments:
|
118
|
+
task_id: ID of the required task.
|
119
|
+
db: An asynchronous db session
|
120
|
+
"""
|
121
|
+
task = await db.get(TaskV2, task_id)
|
122
|
+
if task is None:
|
123
|
+
raise HTTPException(
|
124
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
125
|
+
detail=f"TaskV2 {task_id} not found",
|
126
|
+
)
|
127
|
+
return task
|
128
|
+
|
129
|
+
|
130
|
+
async def _get_task_full_access(
|
131
|
+
*,
|
132
|
+
task_id: int,
|
133
|
+
user_id: int,
|
134
|
+
db: AsyncSession,
|
135
|
+
) -> TaskV2:
|
136
|
+
"""
|
137
|
+
Get an existing task or raise a 404.
|
138
|
+
|
139
|
+
Arguments:
|
140
|
+
task_id: ID of the required task.
|
141
|
+
user_id: ID of the current user.
|
142
|
+
db: An asynchronous db session.
|
143
|
+
"""
|
144
|
+
task = await _get_task_or_404(task_id=task_id, db=db)
|
145
|
+
await _get_task_group_full_access(
|
146
|
+
task_group_id=task.taskgroupv2_id, user_id=user_id, db=db
|
147
|
+
)
|
148
|
+
return task
|
149
|
+
|
150
|
+
|
151
|
+
async def _get_task_read_access(
|
152
|
+
*,
|
153
|
+
task_id: int,
|
154
|
+
user_id: int,
|
155
|
+
db: AsyncSession,
|
156
|
+
require_active: bool = False,
|
157
|
+
) -> TaskV2:
|
158
|
+
"""
|
159
|
+
Get an existing task or raise a 404.
|
160
|
+
|
161
|
+
Arguments:
|
162
|
+
task_id: ID of the required task.
|
163
|
+
user_id: ID of the current user.
|
164
|
+
db: An asynchronous db session.
|
165
|
+
require_active: If set, fail when the task group is not `active`
|
166
|
+
"""
|
167
|
+
task = await _get_task_or_404(task_id=task_id, db=db)
|
168
|
+
task_group = await _get_task_group_read_access(
|
169
|
+
task_group_id=task.taskgroupv2_id, user_id=user_id, db=db
|
170
|
+
)
|
171
|
+
if require_active:
|
172
|
+
if not task_group.active:
|
173
|
+
raise HTTPException(
|
174
|
+
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
175
|
+
detail="Cannot insert non-active tasks into a workflow.",
|
176
|
+
)
|
177
|
+
return task
|
178
|
+
|
179
|
+
|
180
|
+
async def _get_valid_user_group_id(
|
181
|
+
*,
|
182
|
+
user_group_id: Optional[int] = None,
|
183
|
+
private: bool,
|
184
|
+
user_id: int,
|
185
|
+
db: AsyncSession,
|
186
|
+
) -> Optional[int]:
|
187
|
+
"""
|
188
|
+
Validate query parameters for endpoints that create some task(s).
|
189
|
+
|
190
|
+
Arguments:
|
191
|
+
user_group_id:
|
192
|
+
private:
|
193
|
+
user_id: ID of the current user
|
194
|
+
db: An asynchronous db session.
|
195
|
+
"""
|
196
|
+
if (user_group_id is not None) and (private is True):
|
197
|
+
raise HTTPException(
|
198
|
+
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
199
|
+
detail=f"Cannot set both {user_group_id=} and {private=}",
|
200
|
+
)
|
201
|
+
elif private is True:
|
202
|
+
user_group_id = None
|
203
|
+
elif user_group_id is None:
|
204
|
+
user_group_id = await _get_default_user_group_id(db=db)
|
205
|
+
else:
|
206
|
+
await _verify_user_belongs_to_group(
|
207
|
+
user_id=user_id, user_group_id=user_group_id, db=db
|
208
|
+
)
|
209
|
+
return user_group_id
|
@@ -32,6 +32,9 @@ from ._aux_functions import _get_dataset_check_owner
|
|
32
32
|
from ._aux_functions import _get_workflow_check_owner
|
33
33
|
from ._aux_functions import clean_app_job_list_v2
|
34
34
|
from fractal_server.app.models import UserOAuth
|
35
|
+
from fractal_server.app.routes.api.v2._aux_functions_tasks import (
|
36
|
+
_get_task_read_access,
|
37
|
+
)
|
35
38
|
from fractal_server.app.routes.auth import current_active_verified_user
|
36
39
|
|
37
40
|
|
@@ -83,15 +86,14 @@ async def apply_workflow(
|
|
83
86
|
workflow = await _get_workflow_check_owner(
|
84
87
|
project_id=project_id, workflow_id=workflow_id, user_id=user.id, db=db
|
85
88
|
)
|
86
|
-
|
87
|
-
if
|
89
|
+
num_tasks = len(workflow.task_list)
|
90
|
+
if num_tasks == 0:
|
88
91
|
raise HTTPException(
|
89
92
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
90
93
|
detail=f"Workflow {workflow_id} has empty task list",
|
91
94
|
)
|
92
95
|
|
93
96
|
# Set values of first_task_index and last_task_index
|
94
|
-
num_tasks = len(workflow.task_list)
|
95
97
|
try:
|
96
98
|
first_task_index, last_task_index = set_start_and_last_task_index(
|
97
99
|
num_tasks,
|
@@ -110,6 +112,17 @@ async def apply_workflow(
|
|
110
112
|
),
|
111
113
|
)
|
112
114
|
|
115
|
+
# Check that tasks have read-access and are `active`
|
116
|
+
for wftask in workflow.task_list[
|
117
|
+
first_task_index : last_task_index + 1 # noqa: E203
|
118
|
+
]:
|
119
|
+
await _get_task_read_access(
|
120
|
+
user_id=user.id,
|
121
|
+
task_id=wftask.task_id,
|
122
|
+
require_active=True,
|
123
|
+
db=db,
|
124
|
+
)
|
125
|
+
|
113
126
|
# Validate user settings
|
114
127
|
FRACTAL_RUNNER_BACKEND = settings.FRACTAL_RUNNER_BACKEND
|
115
128
|
user_settings = await validate_user_settings(
|
@@ -6,23 +6,26 @@ from fastapi import Depends
|
|
6
6
|
from fastapi import HTTPException
|
7
7
|
from fastapi import Response
|
8
8
|
from fastapi import status
|
9
|
+
from sqlmodel import or_
|
9
10
|
from sqlmodel import select
|
10
11
|
|
11
|
-
from .....logger import set_logger
|
12
|
-
from ....db import AsyncSession
|
13
|
-
from ....db import get_async_db
|
14
|
-
from ....models.v1 import Task as TaskV1
|
15
|
-
from ....models.v2 import TaskV2
|
16
|
-
from ....models.v2 import WorkflowTaskV2
|
17
|
-
from ....models.v2 import WorkflowV2
|
18
|
-
from ....schemas.v2 import TaskCreateV2
|
19
|
-
from ....schemas.v2 import TaskReadV2
|
20
|
-
from ....schemas.v2 import TaskUpdateV2
|
21
12
|
from ...aux.validate_user_settings import verify_user_has_settings
|
22
|
-
from .
|
13
|
+
from ._aux_functions_tasks import _get_task_full_access
|
14
|
+
from ._aux_functions_tasks import _get_task_read_access
|
15
|
+
from ._aux_functions_tasks import _get_valid_user_group_id
|
16
|
+
from fractal_server.app.db import AsyncSession
|
17
|
+
from fractal_server.app.db import get_async_db
|
18
|
+
from fractal_server.app.models import LinkUserGroup
|
23
19
|
from fractal_server.app.models import UserOAuth
|
20
|
+
from fractal_server.app.models.v1 import Task as TaskV1
|
21
|
+
from fractal_server.app.models.v2 import TaskGroupV2
|
22
|
+
from fractal_server.app.models.v2 import TaskV2
|
24
23
|
from fractal_server.app.routes.auth import current_active_user
|
25
24
|
from fractal_server.app.routes.auth import current_active_verified_user
|
25
|
+
from fractal_server.app.schemas.v2 import TaskCreateV2
|
26
|
+
from fractal_server.app.schemas.v2 import TaskReadV2
|
27
|
+
from fractal_server.app.schemas.v2 import TaskUpdateV2
|
28
|
+
from fractal_server.logger import set_logger
|
26
29
|
|
27
30
|
router = APIRouter()
|
28
31
|
|
@@ -39,7 +42,21 @@ async def get_list_task(
|
|
39
42
|
"""
|
40
43
|
Get list of available tasks
|
41
44
|
"""
|
42
|
-
stm =
|
45
|
+
stm = (
|
46
|
+
select(TaskV2)
|
47
|
+
.join(TaskGroupV2)
|
48
|
+
.where(TaskGroupV2.id == TaskV2.taskgroupv2_id)
|
49
|
+
.where(
|
50
|
+
or_(
|
51
|
+
TaskGroupV2.user_id == user.id,
|
52
|
+
TaskGroupV2.user_group_id.in_(
|
53
|
+
select(LinkUserGroup.group_id).where(
|
54
|
+
LinkUserGroup.user_id == user.id
|
55
|
+
)
|
56
|
+
),
|
57
|
+
)
|
58
|
+
)
|
59
|
+
)
|
43
60
|
res = await db.execute(stm)
|
44
61
|
task_list = res.scalars().all()
|
45
62
|
await db.close()
|
@@ -62,12 +79,7 @@ async def get_task(
|
|
62
79
|
"""
|
63
80
|
Get info on a specific task
|
64
81
|
"""
|
65
|
-
task = await
|
66
|
-
await db.close()
|
67
|
-
if not task:
|
68
|
-
raise HTTPException(
|
69
|
-
status_code=status.HTTP_404_NOT_FOUND, detail="TaskV2 not found"
|
70
|
-
)
|
82
|
+
task = await _get_task_read_access(task_id=task_id, user_id=user.id, db=db)
|
71
83
|
return task
|
72
84
|
|
73
85
|
|
@@ -83,7 +95,9 @@ async def patch_task(
|
|
83
95
|
"""
|
84
96
|
|
85
97
|
# Retrieve task from database
|
86
|
-
db_task = await
|
98
|
+
db_task = await _get_task_full_access(
|
99
|
+
task_id=task_id, user_id=user.id, db=db
|
100
|
+
)
|
87
101
|
update = task_update.dict(exclude_unset=True)
|
88
102
|
|
89
103
|
# Forbid changes that set a previously unset command
|
@@ -112,6 +126,8 @@ async def patch_task(
|
|
112
126
|
)
|
113
127
|
async def create_task(
|
114
128
|
task: TaskCreateV2,
|
129
|
+
user_group_id: Optional[int] = None,
|
130
|
+
private: bool = False,
|
115
131
|
user: UserOAuth = Depends(current_active_verified_user),
|
116
132
|
db: AsyncSession = Depends(get_async_db),
|
117
133
|
) -> Optional[TaskReadV2]:
|
@@ -119,6 +135,14 @@ async def create_task(
|
|
119
135
|
Create a new task
|
120
136
|
"""
|
121
137
|
|
138
|
+
# Validate query parameters related to user-group ownership
|
139
|
+
user_group_id = await _get_valid_user_group_id(
|
140
|
+
user_group_id=user_group_id,
|
141
|
+
private=private,
|
142
|
+
user_id=user.id,
|
143
|
+
db=db,
|
144
|
+
)
|
145
|
+
|
122
146
|
if task.command_non_parallel is None:
|
123
147
|
task_type = "parallel"
|
124
148
|
elif task.command_parallel is None:
|
@@ -148,7 +172,7 @@ async def create_task(
|
|
148
172
|
),
|
149
173
|
)
|
150
174
|
|
151
|
-
# Set task.owner attribute
|
175
|
+
# Set task.owner attribute - FIXME: remove this block
|
152
176
|
if user.username:
|
153
177
|
owner = user.username
|
154
178
|
else:
|
@@ -186,7 +210,14 @@ async def create_task(
|
|
186
210
|
)
|
187
211
|
# Add task
|
188
212
|
db_task = TaskV2(**task.dict(), owner=owner, type=task_type)
|
189
|
-
|
213
|
+
|
214
|
+
db_task_group = TaskGroupV2(
|
215
|
+
user_id=user.id,
|
216
|
+
user_group_id=user_group_id,
|
217
|
+
active=True,
|
218
|
+
task_list=[db_task],
|
219
|
+
)
|
220
|
+
db.add(db_task_group)
|
190
221
|
await db.commit()
|
191
222
|
await db.refresh(db_task)
|
192
223
|
await db.close()
|
@@ -202,54 +233,10 @@ async def delete_task(
|
|
202
233
|
"""
|
203
234
|
Delete a task
|
204
235
|
"""
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
if workflow_tasks:
|
214
|
-
# Find IDs of all affected workflows
|
215
|
-
workflow_ids = set(wftask.workflow_id for wftask in workflow_tasks)
|
216
|
-
# Fetch all affected workflows from DB
|
217
|
-
stm = select(WorkflowV2).where(WorkflowV2.id.in_(workflow_ids))
|
218
|
-
res = await db.execute(stm)
|
219
|
-
workflows = res.scalars().all()
|
220
|
-
|
221
|
-
# Find which workflows are associated to the current user
|
222
|
-
workflows_current_user = [
|
223
|
-
wf for wf in workflows if user in wf.project.user_list
|
224
|
-
]
|
225
|
-
if workflows_current_user:
|
226
|
-
current_user_msg = (
|
227
|
-
"For the current-user workflows (listed below),"
|
228
|
-
" you can update the task or remove the workflows.\n"
|
229
|
-
)
|
230
|
-
current_user_msg += "\n".join(
|
231
|
-
[
|
232
|
-
f"* '{wf.name}' (id={wf.id})"
|
233
|
-
for wf in workflows_current_user
|
234
|
-
]
|
235
|
-
)
|
236
|
-
else:
|
237
|
-
current_user_msg = ""
|
238
|
-
|
239
|
-
# Count workflows of current users or other users
|
240
|
-
num_workflows_current_user = len(workflows_current_user)
|
241
|
-
num_workflows_other_users = len(workflows) - num_workflows_current_user
|
242
|
-
|
243
|
-
raise HTTPException(
|
244
|
-
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
245
|
-
detail=(
|
246
|
-
f"Cannot remove Task with id={task_id}: it is currently in "
|
247
|
-
f"use in {num_workflows_current_user} current-user workflows "
|
248
|
-
f"and in {num_workflows_other_users} other-users workflows.\n"
|
249
|
-
f"{current_user_msg}"
|
250
|
-
),
|
251
|
-
)
|
252
|
-
|
253
|
-
await db.delete(db_task)
|
254
|
-
await db.commit()
|
255
|
-
return Response(status_code=status.HTTP_204_NO_CONTENT)
|
236
|
+
raise HTTPException(
|
237
|
+
status_code=status.HTTP_405_METHOD_NOT_ALLOWED,
|
238
|
+
detail=(
|
239
|
+
"Cannot delete single tasks, "
|
240
|
+
"please operate directly on task groups."
|
241
|
+
),
|
242
|
+
)
|
{fractal_server-2.6.4 → fractal_server-2.7.0a0}/fractal_server/app/routes/api/v2/task_collection.py
RENAMED
@@ -2,6 +2,7 @@ import json
|
|
2
2
|
from pathlib import Path
|
3
3
|
from shutil import copy as shell_copy
|
4
4
|
from tempfile import TemporaryDirectory
|
5
|
+
from typing import Optional
|
5
6
|
|
6
7
|
from fastapi import APIRouter
|
7
8
|
from fastapi import BackgroundTasks
|
@@ -26,6 +27,7 @@ from ....schemas.v2 import CollectionStatusV2
|
|
26
27
|
from ....schemas.v2 import TaskCollectPipV2
|
27
28
|
from ....schemas.v2 import TaskReadV2
|
28
29
|
from ...aux.validate_user_settings import validate_user_settings
|
30
|
+
from ._aux_functions_tasks import _get_valid_user_group_id
|
29
31
|
from fractal_server.app.models import UserOAuth
|
30
32
|
from fractal_server.app.routes.auth import current_active_user
|
31
33
|
from fractal_server.app.routes.auth import current_active_verified_user
|
@@ -69,6 +71,8 @@ async def collect_tasks_pip(
|
|
69
71
|
background_tasks: BackgroundTasks,
|
70
72
|
response: Response,
|
71
73
|
request: Request,
|
74
|
+
private: bool = False,
|
75
|
+
user_group_id: Optional[int] = None,
|
72
76
|
user: UserOAuth = Depends(current_active_verified_user),
|
73
77
|
db: AsyncSession = Depends(get_async_db),
|
74
78
|
) -> CollectionStateReadV2:
|
@@ -112,6 +116,14 @@ async def collect_tasks_pip(
|
|
112
116
|
user=user, backend=settings.FRACTAL_RUNNER_BACKEND, db=db
|
113
117
|
)
|
114
118
|
|
119
|
+
# Validate query parameters related to user-group ownership
|
120
|
+
user_group_id = await _get_valid_user_group_id(
|
121
|
+
user_group_id=user_group_id,
|
122
|
+
private=private,
|
123
|
+
user_id=user.id,
|
124
|
+
db=db,
|
125
|
+
)
|
126
|
+
|
115
127
|
# END of SSH/non-SSH common part
|
116
128
|
|
117
129
|
if settings.FRACTAL_RUNNER_BACKEND == "slurm_ssh":
|
@@ -140,10 +152,12 @@ async def collect_tasks_pip(
|
|
140
152
|
|
141
153
|
background_tasks.add_task(
|
142
154
|
background_collect_pip_ssh,
|
143
|
-
state.id,
|
144
|
-
task_pkg,
|
145
|
-
fractal_ssh,
|
146
|
-
user_settings.ssh_tasks_dir,
|
155
|
+
state_id=state.id,
|
156
|
+
task_pkg=task_pkg,
|
157
|
+
fractal_ssh=fractal_ssh,
|
158
|
+
tasks_base_dir=user_settings.ssh_tasks_dir,
|
159
|
+
user_id=user.id,
|
160
|
+
user_group_id=user_group_id,
|
147
161
|
)
|
148
162
|
|
149
163
|
response.status_code = status.HTTP_201_CREATED
|
@@ -284,6 +298,8 @@ async def collect_tasks_pip(
|
|
284
298
|
state_id=state.id,
|
285
299
|
venv_path=venv_path,
|
286
300
|
task_pkg=task_pkg,
|
301
|
+
user_id=user.id,
|
302
|
+
user_group_id=user_group_id,
|
287
303
|
)
|
288
304
|
logger.debug(
|
289
305
|
"Task-collection endpoint: start background collection "
|