fractal-server 2.9.0a0__tar.gz → 2.9.0a2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/PKG-INFO +1 -2
- fractal_server-2.9.0a2/fractal_server/__init__.py +1 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/db/__init__.py +2 -35
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v2/task_group.py +4 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/_aux_functions_tasks.py +16 -13
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/submit.py +17 -3
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/task_collection.py +4 -4
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/aux/_timestamp.py +0 -7
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/_validators.py +1 -2
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/task_group.py +2 -2
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/config.py +11 -56
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/3082479ac4ea_taskgroup_activity_and_venv_info_to_.py +8 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/ssh/_fabric.py +49 -16
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/local/__init__.py +1 -1
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/local/collect.py +1 -1
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/local/deactivate.py +71 -23
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/local/utils_local.py +2 -9
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/ssh/collect.py +2 -3
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/pyproject.toml +2 -3
- fractal_server-2.9.0a0/fractal_server/__init__.py +0 -1
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/LICENSE +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/README.md +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/__main__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/alembic.ini +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/linkusergroup.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/linkuserproject.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/security.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/user_settings.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v1/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v1/dataset.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v1/job.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v1/project.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v1/state.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v1/task.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v1/workflow.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v2/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v2/dataset.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v2/job.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v2/project.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v2/task.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v2/workflow.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v2/workflowtask.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/admin/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/admin/v1.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/admin/v2/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/admin/v2/job.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/admin/v2/project.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/admin/v2/task.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/admin/v2/task_group.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v1/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v1/_aux_functions.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v1/dataset.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v1/job.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v1/project.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v1/task.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v1/task_collection.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v1/workflow.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v1/workflowtask.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/_aux_functions.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/dataset.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/images.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/job.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/project.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/status.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/task.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/task_collection_custom.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/task_group.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/task_group_lifecycle.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/workflow.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/workflow_import.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/workflowtask.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/auth/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/auth/_aux_auth.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/auth/current_user.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/auth/group.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/auth/login.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/auth/oauth.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/auth/register.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/auth/router.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/auth/users.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/aux/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/aux/_job.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/aux/_runner.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/aux/validate_user_settings.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/.gitignore +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/async_wrap.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/components.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/compress_folder.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/exceptions.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/_batching.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/_slurm_config.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/remote.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/ssh/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/ssh/_executor_wait_thread.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/ssh/_slurm_job.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/ssh/executor.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/sudo/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/sudo/_check_jobs_status.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/sudo/_executor_wait_thread.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/sudo/_subprocess_run_as_user.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/executors/slurm/sudo/executor.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/extract_archive.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/filenames.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/run_subprocess.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/set_start_and_last_task_index.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/shutdown.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/task_files.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v1/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v1/_common.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v1/_local/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v1/_local/_local_config.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v1/_local/_submit_setup.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v1/_local/executor.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v1/_slurm/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v1/_slurm/_submit_setup.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v1/_slurm/get_slurm_config.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v1/common.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v1/handle_failed_job.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_local/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_local/_local_config.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_local/_submit_setup.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_local/executor.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_local_experimental/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_local_experimental/_local_config.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_local_experimental/_submit_setup.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_local_experimental/executor.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_slurm_common/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_slurm_common/get_slurm_config.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_slurm_ssh/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_slurm_ssh/_submit_setup.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_slurm_sudo/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/_slurm_sudo/_submit_setup.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/deduplicate_list.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/handle_failed_job.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/merge_outputs.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/runner.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/runner_functions.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/runner_functions_low_level.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/v2/task_interface.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/runner/versions.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/user.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/user_group.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/user_settings.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v1/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v1/applyworkflow.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v1/dataset.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v1/dumps.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v1/manifest.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v1/project.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v1/state.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v1/task.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v1/task_collection.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v1/workflow.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/dataset.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/dumps.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/job.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/manifest.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/project.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/status.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/task.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/task_collection.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/workflow.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/workflowtask.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/security/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/user_settings.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/data_migrations/README.md +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/data_migrations/tools.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/gunicorn_fractal.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/images/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/images/models.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/images/tools.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/logger.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/main.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/README +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/env.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/naming_convention.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/script.py.mako +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/034a469ec2eb_task_groups.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/091b01f51f88_add_usergroup_and_linkusergroup_table.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/19eca0dd47a9_user_settings_project_dir.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/4cedeb448a53_workflowtask_foreign_keys_not_nullables.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/501961cfcd85_remove_link_between_v1_and_v2_tasks_.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/50a13d6138fd_initial_schema.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/5bf02391cfef_v2.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/70e77f1c38b0_add_applyworkflow_first_task_index_and_.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/71eefd1dd202_add_slurm_accounts.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/84bf0fffde30_add_dumps_to_applyworkflow.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/8e8f227a3e36_update_taskv2_post_2_7_0.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/8f79bd162e35_add_docs_info_and_docs_link_to_task_.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/94a47ea2d3ff_remove_cache_dir_slurm_user_and_slurm_.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/97f444d47249_add_applyworkflow_project_dump.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/99ea79d9e5d2_add_dataset_history.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/9c5ae74c9b98_add_user_settings_table.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/9fd26a2b0de4_add_workflow_timestamp_created.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/a7f4d6137b53_add_workflow_dump_to_applyworkflow.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/d4fe3708d309_make_applyworkflow_workflow_dump_non_.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/da2cb2ac4255_user_group_viewer_paths.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/e75cac726012_make_applyworkflow_start_timestamp_not_.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/efa89c30e0a4_add_project_timestamp_created.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/migrations/versions/f384e1c0cf5d_drop_task_default_args_columns.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/py.typed +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/ssh/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/string_tools.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/syringe.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/utils.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v1/_TaskCollectPip.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v1/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v1/background_operations.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v1/endpoint_operations.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v1/get_collection_data.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v1/utils.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/local/reactivate.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/ssh/__init__.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/ssh/deactivate.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/ssh/reactivate.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/templates/1_create_venv.sh +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/templates/2_pip_install.sh +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/templates/3_pip_freeze.sh +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/templates/4_pip_show.sh +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/templates/5_get_venv_size_and_file_number.sh +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/templates/6_pip_install_from_freeze.sh +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/utils_background.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/utils_database.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/utils_package_names.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/utils_python_interpreter.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/tasks/v2/utils_templates.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/urls.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/utils.py +0 -0
- {fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/zip_tools.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: fractal-server
|
3
|
-
Version: 2.9.
|
3
|
+
Version: 2.9.0a2
|
4
4
|
Summary: Server component of the Fractal analytics platform
|
5
5
|
Home-page: https://github.com/fractal-analytics-platform/fractal-server
|
6
6
|
License: BSD-3-Clause
|
@@ -12,7 +12,6 @@ Classifier: Programming Language :: Python :: 3
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.10
|
13
13
|
Classifier: Programming Language :: Python :: 3.11
|
14
14
|
Classifier: Programming Language :: Python :: 3.12
|
15
|
-
Requires-Dist: aiosqlite (>=0.19.0,<0.20.0)
|
16
15
|
Requires-Dist: alembic (>=1.13.1,<2.0.0)
|
17
16
|
Requires-Dist: bcrypt (==4.0.1)
|
18
17
|
Requires-Dist: cloudpickle (>=3.0.0,<3.1.0)
|
@@ -0,0 +1 @@
|
|
1
|
+
__VERSION__ = "2.9.0a2"
|
@@ -2,17 +2,14 @@
|
|
2
2
|
`db` module, loosely adapted from
|
3
3
|
https://testdriven.io/blog/fastapi-sqlmodel/#async-sqlmodel
|
4
4
|
"""
|
5
|
-
import sqlite3
|
6
5
|
from typing import AsyncGenerator
|
7
6
|
from typing import Generator
|
8
7
|
|
9
8
|
from sqlalchemy import create_engine
|
10
|
-
from sqlalchemy import event
|
11
9
|
from sqlalchemy.ext.asyncio import AsyncSession
|
12
10
|
from sqlalchemy.ext.asyncio import create_async_engine
|
13
11
|
from sqlalchemy.orm import Session as DBSyncSession
|
14
12
|
from sqlalchemy.orm import sessionmaker
|
15
|
-
from sqlalchemy.pool import StaticPool
|
16
13
|
|
17
14
|
from ...config import get_settings
|
18
15
|
from ...logger import set_logger
|
@@ -21,14 +18,6 @@ from ...syringe import Inject
|
|
21
18
|
|
22
19
|
logger = set_logger(__name__)
|
23
20
|
|
24
|
-
SQLITE_WARNING_MESSAGE = (
|
25
|
-
"SQLite is supported (supported version >=3.37, "
|
26
|
-
f"current {sqlite3.sqlite_version=}) "
|
27
|
-
"but discouraged in production. "
|
28
|
-
"Given its partial support for ForeignKey constraints, "
|
29
|
-
"database consistency cannot be guaranteed."
|
30
|
-
)
|
31
|
-
|
32
21
|
|
33
22
|
class DB:
|
34
23
|
"""
|
@@ -56,14 +45,7 @@ class DB:
|
|
56
45
|
settings = Inject(get_settings)
|
57
46
|
settings.check_db()
|
58
47
|
|
59
|
-
|
60
|
-
logger.warning(SQLITE_WARNING_MESSAGE)
|
61
|
-
# Set some sqlite-specific options
|
62
|
-
engine_kwargs_async = dict(poolclass=StaticPool)
|
63
|
-
else:
|
64
|
-
engine_kwargs_async = {
|
65
|
-
"pool_pre_ping": True,
|
66
|
-
}
|
48
|
+
engine_kwargs_async = {"pool_pre_ping": True}
|
67
49
|
|
68
50
|
cls._engine_async = create_async_engine(
|
69
51
|
settings.DATABASE_ASYNC_URL,
|
@@ -83,15 +65,7 @@ class DB:
|
|
83
65
|
settings = Inject(get_settings)
|
84
66
|
settings.check_db()
|
85
67
|
|
86
|
-
|
87
|
-
logger.warning(SQLITE_WARNING_MESSAGE)
|
88
|
-
# Set some sqlite-specific options
|
89
|
-
engine_kwargs_sync = dict(
|
90
|
-
poolclass=StaticPool,
|
91
|
-
connect_args={"check_same_thread": False},
|
92
|
-
)
|
93
|
-
else:
|
94
|
-
engine_kwargs_sync = {}
|
68
|
+
engine_kwargs_sync = {}
|
95
69
|
|
96
70
|
cls._engine_sync = create_engine(
|
97
71
|
settings.DATABASE_SYNC_URL,
|
@@ -107,13 +81,6 @@ class DB:
|
|
107
81
|
future=True,
|
108
82
|
)
|
109
83
|
|
110
|
-
@event.listens_for(cls._engine_sync, "connect")
|
111
|
-
def set_sqlite_pragma(dbapi_connection, connection_record):
|
112
|
-
if settings.DB_ENGINE == "sqlite":
|
113
|
-
cursor = dbapi_connection.cursor()
|
114
|
-
cursor.execute("PRAGMA journal_mode=WAL")
|
115
|
-
cursor.close()
|
116
|
-
|
117
84
|
@classmethod
|
118
85
|
async def get_async_db(cls) -> AsyncGenerator[AsyncSession, None]:
|
119
86
|
"""
|
{fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/models/v2/task_group.py
RENAMED
@@ -48,6 +48,10 @@ class TaskGroupV2(SQLModel, table=True):
|
|
48
48
|
default_factory=get_timestamp,
|
49
49
|
sa_column=Column(DateTime(timezone=True), nullable=False),
|
50
50
|
)
|
51
|
+
timestamp_last_used: Optional[datetime] = Field(
|
52
|
+
default=None,
|
53
|
+
sa_column=Column(DateTime(timezone=True), nullable=True),
|
54
|
+
)
|
51
55
|
|
52
56
|
@property
|
53
57
|
def pip_install_string(self) -> str:
|
@@ -221,27 +221,30 @@ async def _get_valid_user_group_id(
|
|
221
221
|
|
222
222
|
|
223
223
|
async def _get_collection_task_group_activity_status_message(
|
224
|
-
|
224
|
+
task_group_id: int,
|
225
|
+
db: AsyncSession,
|
225
226
|
) -> str:
|
227
|
+
|
226
228
|
res = await db.execute(
|
227
|
-
select(TaskGroupActivityV2)
|
228
|
-
|
229
|
-
|
230
|
-
)
|
229
|
+
select(TaskGroupActivityV2)
|
230
|
+
.where(TaskGroupActivityV2.taskgroupv2_id == task_group_id)
|
231
|
+
.where(TaskGroupActivityV2.action == TaskGroupActivityActionV2.COLLECT)
|
231
232
|
)
|
232
233
|
task_group_activity_list = res.scalars().all()
|
233
234
|
if len(task_group_activity_list) > 1:
|
234
235
|
msg = (
|
235
|
-
"
|
236
|
-
|
237
|
-
f"
|
236
|
+
"\nWarning: "
|
237
|
+
"Expected only one TaskGroupActivityV2 associated to TaskGroup "
|
238
|
+
f"{task_group_id}, found {len(task_group_activity_list)} "
|
239
|
+
f"(IDs: {[tga.id for tga in task_group_activity_list]})."
|
238
240
|
"Warning: this should have not happened, please contact an admin."
|
239
241
|
)
|
240
242
|
elif len(task_group_activity_list) == 1:
|
241
243
|
msg = (
|
242
|
-
"\
|
243
|
-
|
244
|
-
f"
|
244
|
+
"\nNote:"
|
245
|
+
"There exists another task-group collection "
|
246
|
+
f"(activity ID={task_group_activity_list[0].id}) for "
|
247
|
+
f"this task group (ID={task_group_id}), with status "
|
245
248
|
f"'{task_group_activity_list[0].status}'."
|
246
249
|
)
|
247
250
|
else:
|
@@ -277,7 +280,7 @@ async def _verify_non_duplication_user_constraint(
|
|
277
280
|
),
|
278
281
|
)
|
279
282
|
state_msg = await _get_collection_task_group_activity_status_message(
|
280
|
-
duplicate[0], db
|
283
|
+
duplicate[0].id, db
|
281
284
|
)
|
282
285
|
raise HTTPException(
|
283
286
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
@@ -319,7 +322,7 @@ async def _verify_non_duplication_group_constraint(
|
|
319
322
|
),
|
320
323
|
)
|
321
324
|
state_msg = await _get_collection_task_group_activity_status_message(
|
322
|
-
duplicate[0], db
|
325
|
+
duplicate[0].id, db
|
323
326
|
)
|
324
327
|
raise HTTPException(
|
325
328
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
{fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/api/v2/submit.py
RENAMED
@@ -30,6 +30,7 @@ from ...aux.validate_user_settings import validate_user_settings
|
|
30
30
|
from ._aux_functions import _get_dataset_check_owner
|
31
31
|
from ._aux_functions import _get_workflow_check_owner
|
32
32
|
from ._aux_functions import clean_app_job_list_v2
|
33
|
+
from fractal_server.app.models import TaskGroupV2
|
33
34
|
from fractal_server.app.models import UserOAuth
|
34
35
|
from fractal_server.app.routes.api.v2._aux_functions_tasks import (
|
35
36
|
_get_task_read_access,
|
@@ -61,6 +62,8 @@ async def apply_workflow(
|
|
61
62
|
db: AsyncSession = Depends(get_async_db),
|
62
63
|
) -> Optional[JobReadV2]:
|
63
64
|
|
65
|
+
now = get_timestamp()
|
66
|
+
|
64
67
|
# Remove non-submitted V2 jobs from the app state when the list grows
|
65
68
|
# beyond a threshold
|
66
69
|
settings = Inject(get_settings)
|
@@ -112,22 +115,23 @@ async def apply_workflow(
|
|
112
115
|
)
|
113
116
|
|
114
117
|
# Check that tasks have read-access and are `active`
|
118
|
+
used_task_group_ids = set()
|
115
119
|
for wftask in workflow.task_list[
|
116
120
|
first_task_index : last_task_index + 1 # noqa: E203
|
117
121
|
]:
|
118
|
-
await _get_task_read_access(
|
122
|
+
task = await _get_task_read_access(
|
119
123
|
user_id=user.id,
|
120
124
|
task_id=wftask.task_id,
|
121
125
|
require_active=True,
|
122
126
|
db=db,
|
123
127
|
)
|
128
|
+
used_task_group_ids.add(task.taskgroupv2_id)
|
124
129
|
|
125
130
|
# Validate user settings
|
126
131
|
FRACTAL_RUNNER_BACKEND = settings.FRACTAL_RUNNER_BACKEND
|
127
132
|
user_settings = await validate_user_settings(
|
128
133
|
user=user, backend=FRACTAL_RUNNER_BACKEND, db=db
|
129
134
|
)
|
130
|
-
|
131
135
|
# Check that no other job with the same dataset_id is SUBMITTED
|
132
136
|
stm = (
|
133
137
|
select(JobV2)
|
@@ -184,8 +188,18 @@ async def apply_workflow(
|
|
184
188
|
await db.commit()
|
185
189
|
await db.refresh(job)
|
186
190
|
|
191
|
+
# Update TaskGroupV2.timestamp_last_used
|
192
|
+
res = await db.execute(
|
193
|
+
select(TaskGroupV2).where(TaskGroupV2.id.in_(used_task_group_ids))
|
194
|
+
)
|
195
|
+
used_task_groups = res.scalars().all()
|
196
|
+
for used_task_group in used_task_groups:
|
197
|
+
used_task_group.timestamp_last_used = now
|
198
|
+
db.add(used_task_group)
|
199
|
+
await db.commit()
|
200
|
+
|
187
201
|
# Define server-side job directory
|
188
|
-
timestamp_string =
|
202
|
+
timestamp_string = now.strftime("%Y%m%d_%H%M%S")
|
189
203
|
WORKFLOW_DIR_LOCAL = settings.FRACTAL_RUNNER_WORKING_BASE_DIR / (
|
190
204
|
f"proj_v2_{project_id:07d}_wf_{workflow_id:07d}_job_{job.id:07d}"
|
191
205
|
f"_{timestamp_string}"
|
@@ -35,7 +35,7 @@ from fractal_server.app.schemas.v2 import (
|
|
35
35
|
)
|
36
36
|
from fractal_server.app.schemas.v2 import TaskGroupV2OriginEnum
|
37
37
|
from fractal_server.tasks.v2.local.collect import (
|
38
|
-
|
38
|
+
collect_local,
|
39
39
|
)
|
40
40
|
from fractal_server.tasks.v2.utils_package_names import _parse_wheel_filename
|
41
41
|
from fractal_server.tasks.v2.utils_package_names import normalize_package_name
|
@@ -248,7 +248,7 @@ async def collect_tasks_pip(
|
|
248
248
|
# SSH task collection
|
249
249
|
|
250
250
|
from fractal_server.tasks.v2.ssh.collect import (
|
251
|
-
|
251
|
+
collect_ssh,
|
252
252
|
)
|
253
253
|
|
254
254
|
# User appropriate FractalSSH object
|
@@ -261,7 +261,7 @@ async def collect_tasks_pip(
|
|
261
261
|
fractal_ssh = fractal_ssh_list.get(**ssh_credentials)
|
262
262
|
|
263
263
|
background_tasks.add_task(
|
264
|
-
|
264
|
+
collect_ssh,
|
265
265
|
task_group_id=task_group.id,
|
266
266
|
task_group_activity_id=task_group_activity.id,
|
267
267
|
fractal_ssh=fractal_ssh,
|
@@ -271,7 +271,7 @@ async def collect_tasks_pip(
|
|
271
271
|
else:
|
272
272
|
# Local task collection
|
273
273
|
background_tasks.add_task(
|
274
|
-
|
274
|
+
collect_local,
|
275
275
|
task_group_id=task_group.id,
|
276
276
|
task_group_activity_id=task_group_activity.id,
|
277
277
|
)
|
{fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/routes/aux/_timestamp.py
RENAMED
@@ -4,15 +4,10 @@ from datetime import timezone
|
|
4
4
|
from fastapi import HTTPException
|
5
5
|
from fastapi import status
|
6
6
|
|
7
|
-
from fractal_server.config import get_settings
|
8
|
-
from fractal_server.syringe import Inject
|
9
|
-
|
10
7
|
|
11
8
|
def _convert_to_db_timestamp(dt: datetime) -> datetime:
|
12
9
|
"""
|
13
10
|
This function takes a timezone-aware datetime and converts it to UTC.
|
14
|
-
If using SQLite, it also removes the timezone information in order to make
|
15
|
-
the datetime comparable with datetimes in the database.
|
16
11
|
"""
|
17
12
|
if dt.tzinfo is None:
|
18
13
|
raise HTTPException(
|
@@ -20,6 +15,4 @@ def _convert_to_db_timestamp(dt: datetime) -> datetime:
|
|
20
15
|
detail=f"The timestamp provided has no timezone information: {dt}",
|
21
16
|
)
|
22
17
|
_dt = dt.astimezone(timezone.utc)
|
23
|
-
if Inject(get_settings).DB_ENGINE == "sqlite":
|
24
|
-
return _dt.replace(tzinfo=None)
|
25
18
|
return _dt
|
@@ -108,8 +108,7 @@ def val_unique_list(attribute: str):
|
|
108
108
|
def valutc(attribute: str):
|
109
109
|
def val(timestamp: Optional[datetime]) -> Optional[datetime]:
|
110
110
|
"""
|
111
|
-
|
112
|
-
If using Postgres, this function leaves the datetime exactly as it is.
|
111
|
+
Replace `tzinfo` with `timezone.utc`.
|
113
112
|
"""
|
114
113
|
if timestamp is not None:
|
115
114
|
return timestamp.replace(tzinfo=timezone.utc)
|
{fractal_server-2.9.0a0 → fractal_server-2.9.0a2}/fractal_server/app/schemas/v2/task_group.py
RENAMED
@@ -1,6 +1,5 @@
|
|
1
1
|
from datetime import datetime
|
2
2
|
from enum import Enum
|
3
|
-
from typing import Literal
|
4
3
|
from typing import Optional
|
5
4
|
|
6
5
|
from pydantic import BaseModel
|
@@ -74,7 +73,7 @@ class TaskGroupReadV2(BaseModel):
|
|
74
73
|
user_id: int
|
75
74
|
user_group_id: Optional[int] = None
|
76
75
|
|
77
|
-
origin:
|
76
|
+
origin: TaskGroupV2OriginEnum
|
78
77
|
pkg_name: str
|
79
78
|
version: Optional[str] = None
|
80
79
|
python_version: Optional[str] = None
|
@@ -90,6 +89,7 @@ class TaskGroupReadV2(BaseModel):
|
|
90
89
|
|
91
90
|
active: bool
|
92
91
|
timestamp_created: datetime
|
92
|
+
timestamp_last_used: Optional[datetime] = None
|
93
93
|
|
94
94
|
|
95
95
|
class TaskGroupUpdateV2(BaseModel, extra=Extra.forbid):
|
@@ -16,7 +16,6 @@ import shutil
|
|
16
16
|
import sys
|
17
17
|
from os import environ
|
18
18
|
from os import getenv
|
19
|
-
from os.path import abspath
|
20
19
|
from pathlib import Path
|
21
20
|
from typing import Literal
|
22
21
|
from typing import Optional
|
@@ -167,10 +166,6 @@ class Settings(BaseSettings):
|
|
167
166
|
###########################################################################
|
168
167
|
# DATABASE
|
169
168
|
###########################################################################
|
170
|
-
DB_ENGINE: Literal["sqlite", "postgres-psycopg"] = "sqlite"
|
171
|
-
"""
|
172
|
-
Database engine to use (supported: `sqlite`, `postgres-psycopg`).
|
173
|
-
"""
|
174
169
|
DB_ECHO: bool = False
|
175
170
|
"""
|
176
171
|
If `True`, make database operations verbose.
|
@@ -196,44 +191,21 @@ class Settings(BaseSettings):
|
|
196
191
|
Name of the PostgreSQL database to connect to.
|
197
192
|
"""
|
198
193
|
|
199
|
-
SQLITE_PATH: Optional[str]
|
200
|
-
"""
|
201
|
-
File path where the SQLite database is located (or will be located).
|
202
|
-
"""
|
203
|
-
|
204
194
|
@property
|
205
195
|
def DATABASE_ASYNC_URL(self) -> URL:
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
)
|
215
|
-
else:
|
216
|
-
if not self.SQLITE_PATH:
|
217
|
-
raise FractalConfigurationError(
|
218
|
-
"SQLITE_PATH path cannot be None"
|
219
|
-
)
|
220
|
-
sqlite_path = abspath(self.SQLITE_PATH)
|
221
|
-
url = URL.create(
|
222
|
-
drivername="sqlite+aiosqlite",
|
223
|
-
database=sqlite_path,
|
224
|
-
)
|
196
|
+
url = URL.create(
|
197
|
+
drivername="postgresql+psycopg",
|
198
|
+
username=self.POSTGRES_USER,
|
199
|
+
password=self.POSTGRES_PASSWORD,
|
200
|
+
host=self.POSTGRES_HOST,
|
201
|
+
port=self.POSTGRES_PORT,
|
202
|
+
database=self.POSTGRES_DB,
|
203
|
+
)
|
225
204
|
return url
|
226
205
|
|
227
206
|
@property
|
228
207
|
def DATABASE_SYNC_URL(self):
|
229
|
-
|
230
|
-
return self.DATABASE_ASYNC_URL.set(drivername="postgresql+psycopg")
|
231
|
-
else:
|
232
|
-
if not self.SQLITE_PATH:
|
233
|
-
raise FractalConfigurationError(
|
234
|
-
"SQLITE_PATH path cannot be None"
|
235
|
-
)
|
236
|
-
return self.DATABASE_ASYNC_URL.set(drivername="sqlite")
|
208
|
+
return self.DATABASE_ASYNC_URL.set(drivername="postgresql+psycopg")
|
237
209
|
|
238
210
|
###########################################################################
|
239
211
|
# FRACTAL SPECIFIC
|
@@ -533,25 +505,8 @@ class Settings(BaseSettings):
|
|
533
505
|
"""
|
534
506
|
Checks that db environment variables are properly set.
|
535
507
|
"""
|
536
|
-
if self.
|
537
|
-
|
538
|
-
raise FractalConfigurationError(
|
539
|
-
"POSTGRES_DB cannot be None when DB_ENGINE="
|
540
|
-
"postgres-psycopg."
|
541
|
-
)
|
542
|
-
|
543
|
-
try:
|
544
|
-
import psycopg # noqa: F401
|
545
|
-
except ModuleNotFoundError:
|
546
|
-
raise FractalConfigurationError(
|
547
|
-
"DB engine is `postgres-psycopg` but `psycopg` is not "
|
548
|
-
"available"
|
549
|
-
)
|
550
|
-
else:
|
551
|
-
if not self.SQLITE_PATH:
|
552
|
-
raise FractalConfigurationError(
|
553
|
-
"SQLITE_PATH cannot be None when DB_ENGINE=sqlite."
|
554
|
-
)
|
508
|
+
if not self.POSTGRES_DB:
|
509
|
+
raise FractalConfigurationError("POSTGRES_DB cannot be None.")
|
555
510
|
|
556
511
|
def check_runner(self) -> None:
|
557
512
|
|
@@ -67,6 +67,13 @@ def upgrade() -> None:
|
|
67
67
|
batch_op.add_column(
|
68
68
|
sa.Column("venv_file_number", sa.Integer(), nullable=True)
|
69
69
|
)
|
70
|
+
batch_op.add_column(
|
71
|
+
sa.Column(
|
72
|
+
"timestamp_last_used",
|
73
|
+
sa.DateTime(timezone=True),
|
74
|
+
nullable=True,
|
75
|
+
)
|
76
|
+
)
|
70
77
|
|
71
78
|
# ### end Alembic commands ###
|
72
79
|
|
@@ -74,6 +81,7 @@ def upgrade() -> None:
|
|
74
81
|
def downgrade() -> None:
|
75
82
|
# ### commands auto generated by Alembic - please adjust! ###
|
76
83
|
with op.batch_alter_table("taskgroupv2", schema=None) as batch_op:
|
84
|
+
batch_op.drop_column("timestamp_last_used")
|
77
85
|
batch_op.drop_column("venv_file_number")
|
78
86
|
batch_op.drop_column("venv_size_in_kB")
|
79
87
|
batch_op.drop_column("pip_freeze")
|
@@ -196,23 +196,57 @@ class FractalSSH(object):
|
|
196
196
|
"""
|
197
197
|
Open the SSH connection and handle exceptions.
|
198
198
|
|
199
|
-
This
|
200
|
-
|
201
|
-
|
199
|
+
This method should always be called at the beginning of background
|
200
|
+
operations that use FractalSSH, so that:
|
201
|
+
|
202
|
+
1. We try to restore unusable connections (e.g. due to closed socket).
|
203
|
+
2. We provide an informative error if connection cannot be established.
|
202
204
|
"""
|
203
|
-
|
205
|
+
self.logger.debug(
|
206
|
+
f"[check_connection] {self._connection.is_connected=}"
|
207
|
+
)
|
208
|
+
if self._connection.is_connected:
|
209
|
+
# Even if the connection appears open, it could be broken for
|
210
|
+
# external reasons (e.g. the socket is closed because the SSH
|
211
|
+
# server was restarted). In these cases, we catch the error and
|
212
|
+
# try to re-open the connection.
|
204
213
|
try:
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
+
self.logger.info(
|
215
|
+
"[check_connection] Run dummy command to check connection."
|
216
|
+
)
|
217
|
+
# Run both an SFTP and an SSH command, as they correspond to
|
218
|
+
# different sockets
|
219
|
+
self.remote_exists("/dummy/path/")
|
220
|
+
self.run_command(cmd="whoami")
|
221
|
+
self.logger.info(
|
222
|
+
"[check_connection] SSH connection is already OK, exit."
|
223
|
+
)
|
224
|
+
return
|
225
|
+
except (OSError, EOFError) as e:
|
226
|
+
self.logger.warning(
|
227
|
+
f"[check_connection] Detected error {str(e)}, re-open."
|
228
|
+
)
|
229
|
+
# Try opening the connection (if it was closed) or to re-open it (if
|
230
|
+
# an error happened).
|
231
|
+
try:
|
232
|
+
self.close()
|
233
|
+
with _acquire_lock_with_timeout(
|
234
|
+
lock=self._lock,
|
235
|
+
label="_connection.open",
|
236
|
+
timeout=self.default_lock_timeout,
|
237
|
+
logger_name=self.logger_name,
|
238
|
+
):
|
239
|
+
self._connection.open()
|
240
|
+
self._connection.client.open_sftp()
|
241
|
+
self.logger.info(
|
242
|
+
"[check_connection] SSH connection opened, exit."
|
214
243
|
)
|
215
244
|
|
245
|
+
except Exception as e:
|
246
|
+
raise RuntimeError(
|
247
|
+
f"Cannot open SSH connection. Original error:\n{str(e)}"
|
248
|
+
)
|
249
|
+
|
216
250
|
def close(self) -> None:
|
217
251
|
"""
|
218
252
|
Aggressively close `self._connection`.
|
@@ -228,9 +262,8 @@ class FractalSSH(object):
|
|
228
262
|
timeout=self.default_lock_timeout,
|
229
263
|
):
|
230
264
|
self._connection.close()
|
231
|
-
|
232
|
-
|
233
|
-
self._connection.client.close()
|
265
|
+
if self._connection.client is not None:
|
266
|
+
self._connection.client.close()
|
234
267
|
|
235
268
|
def run_command(
|
236
269
|
self,
|