fractal-server 2.13.1__tar.gz → 2.14.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.13.1 → fractal_server-2.14.0a0}/PKG-INFO +1 -1
- fractal_server-2.14.0a0/fractal_server/__init__.py +1 -0
- fractal_server-2.14.0a0/fractal_server/app/history/__init__.py +4 -0
- fractal_server-2.14.0a0/fractal_server/app/history/image_updates.py +142 -0
- fractal_server-2.14.0a0/fractal_server/app/history/status_enum.py +16 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/models/v2/__init__.py +5 -1
- fractal_server-2.14.0a0/fractal_server/app/models/v2/history.py +53 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/api/v2/__init__.py +2 -2
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/api/v2/_aux_functions.py +78 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/api/v2/dataset.py +12 -9
- fractal_server-2.14.0a0/fractal_server/app/routes/api/v2/history.py +247 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/api/v2/workflow.py +18 -3
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/api/v2/workflowtask.py +22 -0
- fractal_server-2.14.0a0/fractal_server/app/runner/executors/base_runner.py +114 -0
- {fractal_server-2.13.1/fractal_server/app/runner/v2/_local → fractal_server-2.14.0a0/fractal_server/app/runner/executors/local}/_local_config.py +3 -3
- fractal_server-2.14.0a0/fractal_server/app/runner/executors/local/_submit_setup.py +54 -0
- fractal_server-2.14.0a0/fractal_server/app/runner/executors/local/runner.py +200 -0
- {fractal_server-2.13.1/fractal_server/app/runner/executors/slurm → fractal_server-2.14.0a0/fractal_server/app/runner/executors/slurm_common}/_batching.py +1 -1
- {fractal_server-2.13.1/fractal_server/app/runner/executors/slurm → fractal_server-2.14.0a0/fractal_server/app/runner/executors/slurm_common}/_slurm_config.py +3 -3
- {fractal_server-2.13.1/fractal_server/app/runner/v2/_slurm_sudo → fractal_server-2.14.0a0/fractal_server/app/runner/executors/slurm_common}/_submit_setup.py +13 -12
- {fractal_server-2.13.1/fractal_server/app/runner/v2/_slurm_common → fractal_server-2.14.0a0/fractal_server/app/runner/executors/slurm_common}/get_slurm_config.py +9 -15
- {fractal_server-2.13.1/fractal_server/app/runner/executors/slurm/ssh → fractal_server-2.14.0a0/fractal_server/app/runner/executors/slurm_ssh}/_executor_wait_thread.py +1 -1
- {fractal_server-2.13.1/fractal_server/app/runner/executors/slurm/ssh → fractal_server-2.14.0a0/fractal_server/app/runner/executors/slurm_ssh}/_slurm_job.py +1 -1
- {fractal_server-2.13.1/fractal_server/app/runner/executors/slurm/ssh → fractal_server-2.14.0a0/fractal_server/app/runner/executors/slurm_ssh}/executor.py +13 -14
- {fractal_server-2.13.1/fractal_server/app/runner/executors/slurm/sudo → fractal_server-2.14.0a0/fractal_server/app/runner/executors/slurm_sudo}/_check_jobs_status.py +11 -9
- {fractal_server-2.13.1/fractal_server/app/runner/executors/slurm/sudo → fractal_server-2.14.0a0/fractal_server/app/runner/executors/slurm_sudo}/_executor_wait_thread.py +3 -3
- {fractal_server-2.13.1/fractal_server/app/runner/executors/slurm/sudo → fractal_server-2.14.0a0/fractal_server/app/runner/executors/slurm_sudo}/_subprocess_run_as_user.py +2 -68
- fractal_server-2.14.0a0/fractal_server/app/runner/executors/slurm_sudo/runner.py +632 -0
- fractal_server-2.14.0a0/fractal_server/app/runner/task_files.py +98 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/runner/v2/__init__.py +5 -19
- fractal_server-2.14.0a0/fractal_server/app/runner/v2/_local.py +84 -0
- fractal_server-2.13.1/fractal_server/app/runner/v2/_slurm_ssh/__init__.py → fractal_server-2.14.0a0/fractal_server/app/runner/v2/_slurm_ssh.py +10 -13
- fractal_server-2.13.1/fractal_server/app/runner/v2/_slurm_sudo/__init__.py → fractal_server-2.14.0a0/fractal_server/app/runner/v2/_slurm_sudo.py +10 -12
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/runner/v2/runner.py +93 -28
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/runner/v2/runner_functions.py +85 -62
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/runner/v2/runner_functions_low_level.py +20 -20
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/schemas/v2/dataset.py +0 -17
- fractal_server-2.14.0a0/fractal_server/app/schemas/v2/history.py +23 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/config.py +2 -2
- fractal_server-2.14.0a0/fractal_server/migrations/versions/8223fcef886c_image_status.py +63 -0
- fractal_server-2.14.0a0/fractal_server/migrations/versions/87cd72a537a2_add_historyitem_table.py +68 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/pyproject.toml +2 -2
- fractal_server-2.13.1/fractal_server/__init__.py +0 -1
- fractal_server-2.13.1/fractal_server/app/routes/api/v2/status.py +0 -168
- fractal_server-2.13.1/fractal_server/app/runner/executors/slurm/sudo/executor.py +0 -1281
- fractal_server-2.13.1/fractal_server/app/runner/task_files.py +0 -124
- fractal_server-2.13.1/fractal_server/app/runner/v2/_local/__init__.py +0 -132
- fractal_server-2.13.1/fractal_server/app/runner/v2/_local/_submit_setup.py +0 -52
- fractal_server-2.13.1/fractal_server/app/runner/v2/_local/executor.py +0 -100
- fractal_server-2.13.1/fractal_server/app/runner/v2/_slurm_ssh/_submit_setup.py +0 -83
- fractal_server-2.13.1/fractal_server/app/runner/v2/handle_failed_job.py +0 -59
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/LICENSE +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/README.md +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/__main__.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/alembic.ini +0 -0
- {fractal_server-2.13.1/fractal_server/tasks/v2 → fractal_server-2.14.0a0/fractal_server/app}/__init__.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/db/__init__.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/models/__init__.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/models/linkusergroup.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/models/linkuserproject.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/models/security.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/models/user_settings.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/models/v2/accounting.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/models/v2/dataset.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/models/v2/job.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/models/v2/project.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/models/v2/task.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/models/v2/task_group.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/models/v2/workflow.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/models/v2/workflowtask.py +0 -0
- {fractal_server-2.13.1/fractal_server/app/runner/v2/_slurm_common → fractal_server-2.14.0a0/fractal_server/app/routes}/__init__.py +0 -0
- {fractal_server-2.13.1/fractal_server/app/runner/executors/slurm/sudo → fractal_server-2.14.0a0/fractal_server/app/routes/admin}/__init__.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/admin/v2/__init__.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/admin/v2/accounting.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/admin/v2/impersonate.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/admin/v2/job.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/admin/v2/project.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/admin/v2/task.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/admin/v2/task_group.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/admin/v2/task_group_lifecycle.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/api/__init__.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/api/v2/_aux_functions_tasks.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/api/v2/images.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/api/v2/job.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/api/v2/project.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/api/v2/submit.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/api/v2/task.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/api/v2/task_collection.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/api/v2/task_collection_custom.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/api/v2/task_group.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/api/v2/task_group_lifecycle.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/api/v2/workflow_import.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/auth/__init__.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/auth/_aux_auth.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/auth/current_user.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/auth/group.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/auth/login.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/auth/oauth.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/auth/register.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/auth/router.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/auth/users.py +0 -0
- {fractal_server-2.13.1/fractal_server/app/runner/executors/slurm/ssh → fractal_server-2.14.0a0/fractal_server/app/routes/aux}/__init__.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/aux/_job.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/aux/_runner.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/aux/validate_user_settings.py +0 -0
- {fractal_server-2.13.1/fractal_server/app/runner/executors/slurm → fractal_server-2.14.0a0/fractal_server/app/runner}/__init__.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/runner/components.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/runner/compress_folder.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/runner/exceptions.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/runner/executors/__init__.py +0 -0
- {fractal_server-2.13.1/fractal_server/app/runner → fractal_server-2.14.0a0/fractal_server/app/runner/executors/local}/__init__.py +0 -0
- {fractal_server-2.13.1/fractal_server/app/routes/aux → fractal_server-2.14.0a0/fractal_server/app/runner/executors/slurm_common}/__init__.py +0 -0
- {fractal_server-2.13.1/fractal_server/app/runner/executors → fractal_server-2.14.0a0/fractal_server/app/runner/executors/slurm_common}/_job_states.py +0 -0
- {fractal_server-2.13.1/fractal_server/app/runner/executors/slurm → fractal_server-2.14.0a0/fractal_server/app/runner/executors/slurm_common}/remote.py +0 -0
- {fractal_server-2.13.1/fractal_server/app/runner/executors/slurm → fractal_server-2.14.0a0/fractal_server/app/runner/executors/slurm_common}/utils_executors.py +0 -0
- {fractal_server-2.13.1/fractal_server/app/routes/admin → fractal_server-2.14.0a0/fractal_server/app/runner/executors/slurm_ssh}/__init__.py +0 -0
- {fractal_server-2.13.1/fractal_server/app/routes → fractal_server-2.14.0a0/fractal_server/app/runner/executors/slurm_sudo}/__init__.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/runner/extract_archive.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/runner/filenames.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/runner/run_subprocess.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/runner/set_start_and_last_task_index.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/runner/shutdown.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/runner/v2/deduplicate_list.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/runner/v2/merge_outputs.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/runner/v2/task_interface.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/runner/versions.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/schemas/__init__.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/schemas/_filter_validators.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/schemas/_validators.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/schemas/user.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/schemas/user_group.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/schemas/user_settings.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/schemas/v2/__init__.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/schemas/v2/accounting.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/schemas/v2/dumps.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/schemas/v2/job.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/schemas/v2/manifest.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/schemas/v2/project.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/schemas/v2/status.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/schemas/v2/task.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/schemas/v2/task_collection.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/schemas/v2/task_group.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/schemas/v2/workflow.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/schemas/v2/workflowtask.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/security/__init__.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/security/signup_email.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/user_settings.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/data_migrations/README.md +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/data_migrations/tools.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/gunicorn_fractal.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/images/__init__.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/images/models.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/images/tools.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/logger.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/main.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/env.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/naming_convention.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/034a469ec2eb_task_groups.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/091b01f51f88_add_usergroup_and_linkusergroup_table.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/19eca0dd47a9_user_settings_project_dir.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/1eac13a26c83_drop_v1_tables.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/316140ff7ee1_remove_usersettings_cache_dir.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/4cedeb448a53_workflowtask_foreign_keys_not_nullables.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/501961cfcd85_remove_link_between_v1_and_v2_tasks_.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/50a13d6138fd_initial_schema.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/5bf02391cfef_v2.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/70e77f1c38b0_add_applyworkflow_first_task_index_and_.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/71eefd1dd202_add_slurm_accounts.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/84bf0fffde30_add_dumps_to_applyworkflow.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/8e8f227a3e36_update_taskv2_post_2_7_0.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/8f79bd162e35_add_docs_info_and_docs_link_to_task_.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/94a47ea2d3ff_remove_cache_dir_slurm_user_and_slurm_.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/97f444d47249_add_applyworkflow_project_dump.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/99ea79d9e5d2_add_dataset_history.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/9c5ae74c9b98_add_user_settings_table.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/9fd26a2b0de4_add_workflow_timestamp_created.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/a7f4d6137b53_add_workflow_dump_to_applyworkflow.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/af1ef1c83c9b_add_accounting_tables.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/af8673379a5c_drop_old_filter_columns.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/d256a7379ab8_taskgroup_activity_and_venv_info_to_.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/d4fe3708d309_make_applyworkflow_workflow_dump_non_.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/da2cb2ac4255_user_group_viewer_paths.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/db09233ad13a_split_filters_and_keep_old_columns.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/e75cac726012_make_applyworkflow_start_timestamp_not_.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/efa89c30e0a4_add_project_timestamp_created.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/migrations/versions/f384e1c0cf5d_drop_task_default_args_columns.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/py.typed +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/ssh/__init__.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/ssh/_fabric.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/string_tools.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/syringe.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/tasks/__init__.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/tasks/utils.py +0 -0
- {fractal_server-2.13.1/fractal_server/app → fractal_server-2.14.0a0/fractal_server/tasks/v2}/__init__.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/tasks/v2/local/__init__.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/tasks/v2/local/_utils.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/tasks/v2/local/collect.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/tasks/v2/local/deactivate.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/tasks/v2/local/reactivate.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/tasks/v2/ssh/__init__.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/tasks/v2/ssh/_utils.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/tasks/v2/ssh/collect.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/tasks/v2/ssh/deactivate.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/tasks/v2/ssh/reactivate.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/tasks/v2/templates/1_create_venv.sh +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/tasks/v2/templates/2_pip_install.sh +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/tasks/v2/templates/3_pip_freeze.sh +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/tasks/v2/templates/4_pip_show.sh +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/tasks/v2/templates/5_get_venv_size_and_file_number.sh +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/tasks/v2/templates/6_pip_install_from_freeze.sh +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/tasks/v2/utils_background.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/tasks/v2/utils_database.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/tasks/v2/utils_package_names.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/tasks/v2/utils_python_interpreter.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/tasks/v2/utils_templates.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/urls.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/utils.py +0 -0
- {fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/zip_tools.py +0 -0
@@ -0,0 +1 @@
|
|
1
|
+
__VERSION__ = "2.14.0a0"
|
@@ -0,0 +1,142 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
|
3
|
+
from sqlalchemy.orm import Session
|
4
|
+
from sqlalchemy.orm.attributes import flag_modified
|
5
|
+
from sqlmodel import select
|
6
|
+
|
7
|
+
from fractal_server.app.db import get_sync_db
|
8
|
+
from fractal_server.app.history.status_enum import HistoryItemImageStatus
|
9
|
+
from fractal_server.app.models.v2 import HistoryItemV2
|
10
|
+
from fractal_server.app.models.v2 import ImageStatus
|
11
|
+
from fractal_server.logger import set_logger
|
12
|
+
|
13
|
+
logger = set_logger(__name__)
|
14
|
+
|
15
|
+
|
16
|
+
def _update_single_image_status(
|
17
|
+
*,
|
18
|
+
zarr_url: str,
|
19
|
+
workflowtask_id: int,
|
20
|
+
dataset_id: int,
|
21
|
+
status: HistoryItemImageStatus,
|
22
|
+
db: Session,
|
23
|
+
commit: bool = True,
|
24
|
+
logfile: Optional[str] = None,
|
25
|
+
) -> None:
|
26
|
+
image_status = db.get(
|
27
|
+
ImageStatus,
|
28
|
+
(
|
29
|
+
zarr_url,
|
30
|
+
workflowtask_id,
|
31
|
+
dataset_id,
|
32
|
+
),
|
33
|
+
)
|
34
|
+
if image_status is None:
|
35
|
+
raise RuntimeError("This should have not happened")
|
36
|
+
image_status.status = status
|
37
|
+
if logfile is not None:
|
38
|
+
image_status.logfile = logfile
|
39
|
+
db.add(image_status)
|
40
|
+
if commit:
|
41
|
+
db.commit()
|
42
|
+
|
43
|
+
|
44
|
+
def update_single_image(
|
45
|
+
*,
|
46
|
+
history_item_id: int,
|
47
|
+
zarr_url: str,
|
48
|
+
status: HistoryItemImageStatus,
|
49
|
+
) -> None:
|
50
|
+
|
51
|
+
logger.debug(
|
52
|
+
f"[update_single_image] {history_item_id=}, {status=}, {zarr_url=}"
|
53
|
+
)
|
54
|
+
|
55
|
+
# Note: thanks to `with_for_update`, a lock is acquired and kept
|
56
|
+
# until `db.commit()`
|
57
|
+
with next(get_sync_db()) as db:
|
58
|
+
stm = (
|
59
|
+
select(HistoryItemV2)
|
60
|
+
.where(HistoryItemV2.id == history_item_id)
|
61
|
+
.with_for_update(nowait=False)
|
62
|
+
)
|
63
|
+
history_item = db.execute(stm).scalar_one()
|
64
|
+
history_item.images[zarr_url] = status
|
65
|
+
flag_modified(history_item, "images")
|
66
|
+
db.commit()
|
67
|
+
|
68
|
+
_update_single_image_status(
|
69
|
+
zarr_url=zarr_url,
|
70
|
+
dataset_id=history_item.dataset_id,
|
71
|
+
workflowtask_id=history_item.workflowtask_id,
|
72
|
+
commit=True,
|
73
|
+
status=status,
|
74
|
+
db=db,
|
75
|
+
)
|
76
|
+
|
77
|
+
|
78
|
+
def update_single_image_logfile(
|
79
|
+
*,
|
80
|
+
history_item_id: int,
|
81
|
+
zarr_url: str,
|
82
|
+
logfile: str,
|
83
|
+
) -> None:
|
84
|
+
|
85
|
+
logger.debug(
|
86
|
+
f"[update_single_image_logfile] {history_item_id=}, {logfile=}, {zarr_url=}"
|
87
|
+
)
|
88
|
+
|
89
|
+
with next(get_sync_db()) as db:
|
90
|
+
history_item = db.get(HistoryItemV2, history_item_id)
|
91
|
+
image_status = db.get(
|
92
|
+
ImageStatus,
|
93
|
+
(
|
94
|
+
zarr_url,
|
95
|
+
history_item.workflowtask_id,
|
96
|
+
history_item.dataset_id,
|
97
|
+
),
|
98
|
+
)
|
99
|
+
if image_status is None:
|
100
|
+
raise RuntimeError("This should have not happened")
|
101
|
+
image_status.logfile = logfile
|
102
|
+
db.merge(image_status)
|
103
|
+
db.commit()
|
104
|
+
|
105
|
+
|
106
|
+
def update_all_images(
|
107
|
+
*,
|
108
|
+
history_item_id: int,
|
109
|
+
status: HistoryItemImageStatus,
|
110
|
+
logfile: Optional[str] = None,
|
111
|
+
) -> None:
|
112
|
+
|
113
|
+
logger.debug(f"[update_all_images] {history_item_id=}, {status=}")
|
114
|
+
|
115
|
+
# Note: thanks to `with_for_update`, a lock is acquired and kept
|
116
|
+
# until `db.commit()`
|
117
|
+
stm = (
|
118
|
+
select(HistoryItemV2)
|
119
|
+
.where(HistoryItemV2.id == history_item_id)
|
120
|
+
.with_for_update(nowait=False)
|
121
|
+
)
|
122
|
+
with next(get_sync_db()) as db:
|
123
|
+
history_item = db.execute(stm).scalar_one()
|
124
|
+
new_images = {
|
125
|
+
zarr_url: status for zarr_url in history_item.images.keys()
|
126
|
+
}
|
127
|
+
history_item.images = new_images
|
128
|
+
flag_modified(history_item, "images")
|
129
|
+
db.commit()
|
130
|
+
|
131
|
+
# FIXME: Make this a bulk edit, if possible
|
132
|
+
for ind, zarr_url in enumerate(history_item.images.keys()):
|
133
|
+
_update_single_image_status(
|
134
|
+
zarr_url=zarr_url,
|
135
|
+
dataset_id=history_item.dataset_id,
|
136
|
+
workflowtask_id=history_item.workflowtask_id,
|
137
|
+
commit=False,
|
138
|
+
status=status,
|
139
|
+
logfile=logfile,
|
140
|
+
db=db,
|
141
|
+
)
|
142
|
+
db.commit()
|
@@ -0,0 +1,16 @@
|
|
1
|
+
from enum import Enum
|
2
|
+
|
3
|
+
|
4
|
+
class HistoryItemImageStatus(str, Enum):
|
5
|
+
"""
|
6
|
+
Available image-status values within a `HistoryItemV2`
|
7
|
+
|
8
|
+
Attributes:
|
9
|
+
SUBMITTED:
|
10
|
+
DONE:
|
11
|
+
FAILED:
|
12
|
+
"""
|
13
|
+
|
14
|
+
SUBMITTED = "submitted"
|
15
|
+
DONE = "done"
|
16
|
+
FAILED = "failed"
|
@@ -5,6 +5,8 @@ from ..linkuserproject import LinkUserProjectV2
|
|
5
5
|
from .accounting import AccountingRecord
|
6
6
|
from .accounting import AccountingRecordSlurm
|
7
7
|
from .dataset import DatasetV2
|
8
|
+
from .history import HistoryItemV2
|
9
|
+
from .history import ImageStatus
|
8
10
|
from .job import JobV2
|
9
11
|
from .project import ProjectV2
|
10
12
|
from .task import TaskV2
|
@@ -23,6 +25,8 @@ __all__ = [
|
|
23
25
|
"TaskGroupV2",
|
24
26
|
"TaskGroupActivityV2",
|
25
27
|
"TaskV2",
|
26
|
-
"WorkflowTaskV2",
|
27
28
|
"WorkflowV2",
|
29
|
+
"WorkflowTaskV2",
|
30
|
+
"HistoryItemV2",
|
31
|
+
"ImageStatus",
|
28
32
|
]
|
@@ -0,0 +1,53 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
from typing import Any
|
3
|
+
from typing import Optional
|
4
|
+
|
5
|
+
from pydantic import ConfigDict
|
6
|
+
from sqlalchemy import Column
|
7
|
+
from sqlalchemy.dialects.postgresql import JSONB
|
8
|
+
from sqlalchemy.types import DateTime
|
9
|
+
from sqlmodel import Field
|
10
|
+
from sqlmodel import SQLModel
|
11
|
+
|
12
|
+
from ....utils import get_timestamp
|
13
|
+
|
14
|
+
|
15
|
+
class HistoryItemV2(SQLModel, table=True):
|
16
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
17
|
+
|
18
|
+
id: Optional[int] = Field(default=None, primary_key=True)
|
19
|
+
dataset_id: int = Field(foreign_key="datasetv2.id")
|
20
|
+
workflowtask_id: Optional[int] = Field(
|
21
|
+
foreign_key="workflowtaskv2.id",
|
22
|
+
default=None,
|
23
|
+
)
|
24
|
+
timestamp_started: datetime = Field(
|
25
|
+
default_factory=get_timestamp,
|
26
|
+
sa_column=Column(
|
27
|
+
DateTime(timezone=True),
|
28
|
+
nullable=False,
|
29
|
+
),
|
30
|
+
)
|
31
|
+
workflowtask_dump: dict[str, Any] = Field(
|
32
|
+
sa_column=Column(JSONB, nullable=False)
|
33
|
+
)
|
34
|
+
task_group_dump: dict[str, Any] = Field(
|
35
|
+
sa_column=Column(JSONB, nullable=False)
|
36
|
+
)
|
37
|
+
parameters_hash: str
|
38
|
+
num_available_images: int
|
39
|
+
num_current_images: int
|
40
|
+
images: dict[str, str] = Field(sa_column=Column(JSONB, nullable=False))
|
41
|
+
|
42
|
+
|
43
|
+
class ImageStatus(SQLModel, table=True):
|
44
|
+
|
45
|
+
zarr_url: str = Field(primary_key=True)
|
46
|
+
workflowtask_id: int = Field(
|
47
|
+
primary_key=True, foreign_key="workflowtaskv2.id"
|
48
|
+
)
|
49
|
+
dataset_id: int = Field(primary_key=True, foreign_key="datasetv2.id")
|
50
|
+
|
51
|
+
parameters_hash: str
|
52
|
+
status: str
|
53
|
+
logfile: str
|
{fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/api/v2/__init__.py
RENAMED
@@ -4,10 +4,10 @@
|
|
4
4
|
from fastapi import APIRouter
|
5
5
|
|
6
6
|
from .dataset import router as dataset_router_v2
|
7
|
+
from .history import router as history_router_v2
|
7
8
|
from .images import router as images_routes_v2
|
8
9
|
from .job import router as job_router_v2
|
9
10
|
from .project import router as project_router_v2
|
10
|
-
from .status import router as status_router_v2
|
11
11
|
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
|
@@ -28,6 +28,7 @@ router_api_v2.include_router(job_router_v2, tags=["V2 Job"])
|
|
28
28
|
router_api_v2.include_router(images_routes_v2, tags=["V2 Images"])
|
29
29
|
router_api_v2.include_router(project_router_v2, tags=["V2 Project"])
|
30
30
|
router_api_v2.include_router(submit_job_router_v2, tags=["V2 Job"])
|
31
|
+
router_api_v2.include_router(history_router_v2, tags=["V2 History"])
|
31
32
|
|
32
33
|
|
33
34
|
settings = Inject(get_settings)
|
@@ -56,4 +57,3 @@ router_api_v2.include_router(
|
|
56
57
|
workflow_import_router_v2, tags=["V2 Workflow Import"]
|
57
58
|
)
|
58
59
|
router_api_v2.include_router(workflowtask_router_v2, tags=["V2 WorkflowTask"])
|
59
|
-
router_api_v2.include_router(status_router_v2, tags=["V2 Status"])
|
{fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/api/v2/_aux_functions.py
RENAMED
@@ -417,3 +417,81 @@ async def clean_app_job_list_v2(
|
|
417
417
|
if job.status == JobStatusTypeV2.SUBMITTED
|
418
418
|
]
|
419
419
|
return submitted_job_ids
|
420
|
+
|
421
|
+
|
422
|
+
async def _get_workflow_check_history_owner(
|
423
|
+
*,
|
424
|
+
workflow_id: int,
|
425
|
+
dataset_id: int,
|
426
|
+
user_id: int,
|
427
|
+
db: AsyncSession,
|
428
|
+
) -> list[int]:
|
429
|
+
"""
|
430
|
+
Verify user access for the history of this dataset and workflowtask.
|
431
|
+
|
432
|
+
Args:
|
433
|
+
dataset_id:
|
434
|
+
workflow_task_id:
|
435
|
+
user_id:
|
436
|
+
db:
|
437
|
+
|
438
|
+
Returns:
|
439
|
+
List of WorkflowTask IDs
|
440
|
+
"""
|
441
|
+
workflow = await db.get(WorkflowV2, workflow_id)
|
442
|
+
if workflow is None:
|
443
|
+
raise HTTPException(
|
444
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
445
|
+
detail="Workflow not found.",
|
446
|
+
)
|
447
|
+
await _get_project_check_owner(
|
448
|
+
project_id=workflow.project_id,
|
449
|
+
user_id=user_id,
|
450
|
+
db=db,
|
451
|
+
)
|
452
|
+
dataset = await db.get(DatasetV2, dataset_id)
|
453
|
+
if dataset is None:
|
454
|
+
raise HTTPException(
|
455
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
456
|
+
detail="Dataset not found.",
|
457
|
+
)
|
458
|
+
if workflow.project_id != dataset.project_id:
|
459
|
+
raise HTTPException(
|
460
|
+
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
461
|
+
detail="Dataset and workflow belong to different projects.",
|
462
|
+
)
|
463
|
+
|
464
|
+
return [wftask.id for wftask in workflow.task_list]
|
465
|
+
|
466
|
+
|
467
|
+
async def _get_workflowtask_check_history_owner(
|
468
|
+
*,
|
469
|
+
workflowtask_id: int,
|
470
|
+
dataset_id: int,
|
471
|
+
user_id: int,
|
472
|
+
db: AsyncSession,
|
473
|
+
) -> list[int]:
|
474
|
+
"""
|
475
|
+
Verify user access for the history of this dataset and workflowtask.
|
476
|
+
|
477
|
+
Args:
|
478
|
+
dataset_id:
|
479
|
+
workflow_task_id:
|
480
|
+
user_id:
|
481
|
+
db:
|
482
|
+
|
483
|
+
Returns:
|
484
|
+
List of WorkflowTask IDs
|
485
|
+
"""
|
486
|
+
workflowtask = await db.get(WorkflowTaskV2, workflowtask_id)
|
487
|
+
if workflowtask is None:
|
488
|
+
raise HTTPException(
|
489
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
490
|
+
detail="WorkflowTask not found.",
|
491
|
+
)
|
492
|
+
await _get_workflow_check_history_owner(
|
493
|
+
workflow_id=workflowtask.workflow_id,
|
494
|
+
dataset_id=dataset_id,
|
495
|
+
user_id=user_id,
|
496
|
+
db=db,
|
497
|
+
)
|
{fractal_server-2.13.1 → fractal_server-2.14.0a0}/fractal_server/app/routes/api/v2/dataset.py
RENAMED
@@ -5,11 +5,14 @@ from fastapi import Depends
|
|
5
5
|
from fastapi import HTTPException
|
6
6
|
from fastapi import Response
|
7
7
|
from fastapi import status
|
8
|
+
from sqlmodel import delete
|
8
9
|
from sqlmodel import select
|
9
10
|
|
10
11
|
from ....db import AsyncSession
|
11
12
|
from ....db import get_async_db
|
12
13
|
from ....models.v2 import DatasetV2
|
14
|
+
from ....models.v2 import HistoryItemV2
|
15
|
+
from ....models.v2 import ImageStatus
|
13
16
|
from ....models.v2 import JobV2
|
14
17
|
from ....models.v2 import ProjectV2
|
15
18
|
from ....schemas.v2 import DatasetCreateV2
|
@@ -47,7 +50,6 @@ async def create_dataset(
|
|
47
50
|
)
|
48
51
|
|
49
52
|
if dataset.zarr_dir is None:
|
50
|
-
|
51
53
|
if user.settings.project_dir is None:
|
52
54
|
raise HTTPException(
|
53
55
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
@@ -91,7 +93,6 @@ async def create_dataset(
|
|
91
93
|
)
|
92
94
|
async def read_dataset_list(
|
93
95
|
project_id: int,
|
94
|
-
history: bool = True,
|
95
96
|
user: UserOAuth = Depends(current_active_user),
|
96
97
|
db: AsyncSession = Depends(get_async_db),
|
97
98
|
) -> Optional[list[DatasetReadV2]]:
|
@@ -110,9 +111,6 @@ async def read_dataset_list(
|
|
110
111
|
res = await db.execute(stm)
|
111
112
|
dataset_list = res.scalars().all()
|
112
113
|
await db.close()
|
113
|
-
if not history:
|
114
|
-
for ds in dataset_list:
|
115
|
-
setattr(ds, "history", [])
|
116
114
|
return dataset_list
|
117
115
|
|
118
116
|
|
@@ -225,6 +223,15 @@ async def delete_dataset(
|
|
225
223
|
for job in jobs:
|
226
224
|
job.dataset_id = None
|
227
225
|
|
226
|
+
# Cascade operations: delete history items and image status which are in
|
227
|
+
# relationship with the current dataset
|
228
|
+
|
229
|
+
stm = delete(HistoryItemV2).where(HistoryItemV2.dataset_id == dataset_id)
|
230
|
+
await db.execute(stm)
|
231
|
+
|
232
|
+
stm = delete(ImageStatus).where(ImageStatus.dataset_id == dataset_id)
|
233
|
+
await db.execute(stm)
|
234
|
+
|
228
235
|
# Delete dataset
|
229
236
|
await db.delete(dataset)
|
230
237
|
await db.commit()
|
@@ -234,7 +241,6 @@ async def delete_dataset(
|
|
234
241
|
|
235
242
|
@router.get("/dataset/", response_model=list[DatasetReadV2])
|
236
243
|
async def get_user_datasets(
|
237
|
-
history: bool = True,
|
238
244
|
user: UserOAuth = Depends(current_active_user),
|
239
245
|
db: AsyncSession = Depends(get_async_db),
|
240
246
|
) -> list[DatasetReadV2]:
|
@@ -249,9 +255,6 @@ async def get_user_datasets(
|
|
249
255
|
res = await db.execute(stm)
|
250
256
|
dataset_list = res.scalars().all()
|
251
257
|
await db.close()
|
252
|
-
if not history:
|
253
|
-
for ds in dataset_list:
|
254
|
-
setattr(ds, "history", [])
|
255
258
|
return dataset_list
|
256
259
|
|
257
260
|
|
@@ -0,0 +1,247 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
|
3
|
+
from fastapi import APIRouter
|
4
|
+
from fastapi import Depends
|
5
|
+
from fastapi import HTTPException
|
6
|
+
from fastapi import Query
|
7
|
+
from fastapi import status
|
8
|
+
from fastapi.responses import JSONResponse
|
9
|
+
from sqlmodel import func
|
10
|
+
from sqlmodel import select
|
11
|
+
|
12
|
+
from ._aux_functions import _get_dataset_check_owner
|
13
|
+
from ._aux_functions import _get_workflow_check_owner
|
14
|
+
from ._aux_functions import _get_workflow_task_check_owner
|
15
|
+
from fractal_server.app.db import AsyncSession
|
16
|
+
from fractal_server.app.db import get_async_db
|
17
|
+
from fractal_server.app.history.status_enum import HistoryItemImageStatus
|
18
|
+
from fractal_server.app.models import UserOAuth
|
19
|
+
from fractal_server.app.models.v2 import HistoryItemV2
|
20
|
+
from fractal_server.app.models.v2 import ImageStatus
|
21
|
+
from fractal_server.app.models.v2 import WorkflowTaskV2
|
22
|
+
from fractal_server.app.routes.auth import current_active_user
|
23
|
+
from fractal_server.app.schemas.v2.history import HistoryItemV2Read
|
24
|
+
|
25
|
+
router = APIRouter()
|
26
|
+
|
27
|
+
|
28
|
+
@router.get(
|
29
|
+
"/project/{project_id}/dataset/{dataset_id}/history/",
|
30
|
+
response_model=list[HistoryItemV2Read],
|
31
|
+
)
|
32
|
+
async def get_dataset_history(
|
33
|
+
project_id: int,
|
34
|
+
dataset_id: int,
|
35
|
+
user: UserOAuth = Depends(current_active_user),
|
36
|
+
db: AsyncSession = Depends(get_async_db),
|
37
|
+
) -> list[HistoryItemV2Read]:
|
38
|
+
await _get_dataset_check_owner(
|
39
|
+
project_id=project_id,
|
40
|
+
dataset_id=dataset_id,
|
41
|
+
user_id=user.id,
|
42
|
+
db=db,
|
43
|
+
)
|
44
|
+
|
45
|
+
stm = (
|
46
|
+
select(HistoryItemV2)
|
47
|
+
.where(HistoryItemV2.dataset_id == dataset_id)
|
48
|
+
.order_by(HistoryItemV2.timestamp_started)
|
49
|
+
)
|
50
|
+
res = await db.execute(stm)
|
51
|
+
items = res.scalars().all()
|
52
|
+
return items
|
53
|
+
|
54
|
+
|
55
|
+
@router.get("/project/{project_id}/status/")
|
56
|
+
async def get_per_workflow_aggregated_info(
|
57
|
+
project_id: int,
|
58
|
+
workflow_id: int,
|
59
|
+
dataset_id: int,
|
60
|
+
user: UserOAuth = Depends(current_active_user),
|
61
|
+
db: AsyncSession = Depends(get_async_db),
|
62
|
+
) -> JSONResponse:
|
63
|
+
workflow = await _get_workflow_check_owner(
|
64
|
+
project_id=project_id,
|
65
|
+
workflow_id=workflow_id,
|
66
|
+
user_id=user.id,
|
67
|
+
db=db,
|
68
|
+
)
|
69
|
+
|
70
|
+
wft_ids = [wftask.id for wftask in workflow.task_list]
|
71
|
+
|
72
|
+
# num_available_images
|
73
|
+
stm = (
|
74
|
+
select(
|
75
|
+
HistoryItemV2.workflowtask_id, HistoryItemV2.num_available_images
|
76
|
+
)
|
77
|
+
.where(HistoryItemV2.dataset_id == dataset_id)
|
78
|
+
.where(HistoryItemV2.workflowtask_id.in_(wft_ids))
|
79
|
+
.order_by(
|
80
|
+
HistoryItemV2.workflowtask_id,
|
81
|
+
HistoryItemV2.timestamp_started.desc(),
|
82
|
+
)
|
83
|
+
# https://www.postgresql.org/docs/current/sql-select.html#SQL-DISTINCT
|
84
|
+
.distinct(HistoryItemV2.workflowtask_id)
|
85
|
+
)
|
86
|
+
res = await db.execute(stm)
|
87
|
+
num_available_images = {k: v for k, v in res.all()}
|
88
|
+
|
89
|
+
count = {}
|
90
|
+
for _status in HistoryItemImageStatus:
|
91
|
+
stm = (
|
92
|
+
select(ImageStatus.workflowtask_id, func.count())
|
93
|
+
.where(ImageStatus.dataset_id == dataset_id)
|
94
|
+
.where(ImageStatus.workflowtask_id.in_(wft_ids))
|
95
|
+
.where(ImageStatus.status == _status)
|
96
|
+
# https://docs.sqlalchemy.org/en/20/tutorial/data_select.html#tutorial-group-by-w-aggregates
|
97
|
+
.group_by(ImageStatus.workflowtask_id)
|
98
|
+
)
|
99
|
+
res = await db.execute(stm)
|
100
|
+
count[_status] = {k: v for k, v in res.all()}
|
101
|
+
|
102
|
+
result = {
|
103
|
+
str(_id): None
|
104
|
+
if _id not in num_available_images
|
105
|
+
else {
|
106
|
+
"num_available_images": num_available_images[_id],
|
107
|
+
"num_done_images": count["done"].get(_id, 0),
|
108
|
+
"num_submitted_images": count["submitted"].get(_id, 0),
|
109
|
+
"num_failed_images": count["failed"].get(_id, 0),
|
110
|
+
}
|
111
|
+
for _id in wft_ids
|
112
|
+
}
|
113
|
+
|
114
|
+
return JSONResponse(content=result, status_code=200)
|
115
|
+
|
116
|
+
|
117
|
+
@router.get("/project/{project_id}/status/subsets/")
|
118
|
+
async def get_per_workflowtask_subsets_aggregated_info(
|
119
|
+
project_id: int,
|
120
|
+
workflowtask_id: int,
|
121
|
+
dataset_id: int,
|
122
|
+
user: UserOAuth = Depends(current_active_user),
|
123
|
+
db: AsyncSession = Depends(get_async_db),
|
124
|
+
) -> JSONResponse:
|
125
|
+
wftask = await db.get(WorkflowTaskV2, workflowtask_id)
|
126
|
+
if wftask is None:
|
127
|
+
raise HTTPException(
|
128
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
129
|
+
detail="WorkflowTask not found",
|
130
|
+
)
|
131
|
+
await _get_workflow_task_check_owner(
|
132
|
+
project_id=project_id,
|
133
|
+
workflow_id=wftask.workflow_id,
|
134
|
+
workflow_task_id=workflowtask_id,
|
135
|
+
user_id=user.id,
|
136
|
+
db=db,
|
137
|
+
)
|
138
|
+
|
139
|
+
stm = (
|
140
|
+
select(ImageStatus.parameters_hash, func.array_agg(ImageStatus.status))
|
141
|
+
.where(ImageStatus.dataset_id == dataset_id)
|
142
|
+
.where(ImageStatus.workflowtask_id == workflowtask_id)
|
143
|
+
.group_by(ImageStatus.parameters_hash)
|
144
|
+
)
|
145
|
+
res = await db.execute(stm)
|
146
|
+
hash_statuses = res.all()
|
147
|
+
|
148
|
+
result = []
|
149
|
+
for _hash, statuses in hash_statuses:
|
150
|
+
dump = await db.execute(
|
151
|
+
select(HistoryItemV2.workflowtask_dump)
|
152
|
+
.where(HistoryItemV2.workflowtask_id == workflowtask_id)
|
153
|
+
.where(HistoryItemV2.dataset_id == dataset_id)
|
154
|
+
.where(HistoryItemV2.parameters_hash == _hash)
|
155
|
+
)
|
156
|
+
result.append(
|
157
|
+
{
|
158
|
+
"workflowtask_dump": dump.scalar_one(),
|
159
|
+
"parameters_hash": _hash,
|
160
|
+
"info": {
|
161
|
+
"num_done_images": statuses.count(
|
162
|
+
HistoryItemImageStatus.DONE
|
163
|
+
),
|
164
|
+
"num_failed_images": statuses.count(
|
165
|
+
HistoryItemImageStatus.FAILED
|
166
|
+
),
|
167
|
+
"num_submitted_images": statuses.count(
|
168
|
+
HistoryItemImageStatus.SUBMITTED
|
169
|
+
),
|
170
|
+
},
|
171
|
+
}
|
172
|
+
)
|
173
|
+
|
174
|
+
return JSONResponse(content=result, status_code=200)
|
175
|
+
|
176
|
+
|
177
|
+
@router.get("/project/{project_id}/status/images/")
|
178
|
+
async def get_per_workflowtask_images(
|
179
|
+
project_id: int,
|
180
|
+
workflowtask_id: int,
|
181
|
+
dataset_id: int,
|
182
|
+
status: HistoryItemImageStatus,
|
183
|
+
parameters_hash: Optional[str] = None,
|
184
|
+
# Pagination
|
185
|
+
page: int = Query(default=1, ge=1),
|
186
|
+
page_size: Optional[int] = Query(default=None, ge=1),
|
187
|
+
# Dependencies
|
188
|
+
user: UserOAuth = Depends(current_active_user),
|
189
|
+
db: AsyncSession = Depends(get_async_db),
|
190
|
+
) -> JSONResponse:
|
191
|
+
|
192
|
+
if page_size is None and page > 1:
|
193
|
+
raise HTTPException(
|
194
|
+
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
195
|
+
detail=(f"Invalid pagination parameters: {page=}, {page_size=}."),
|
196
|
+
)
|
197
|
+
|
198
|
+
wftask = await db.get(WorkflowTaskV2, workflowtask_id)
|
199
|
+
if wftask is None:
|
200
|
+
raise HTTPException(
|
201
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
202
|
+
detail="WorkflowTask not found",
|
203
|
+
)
|
204
|
+
await _get_workflow_task_check_owner(
|
205
|
+
project_id=project_id,
|
206
|
+
workflow_id=wftask.workflow_id,
|
207
|
+
workflow_task_id=workflowtask_id,
|
208
|
+
user_id=user.id,
|
209
|
+
db=db,
|
210
|
+
)
|
211
|
+
|
212
|
+
total_count_stm = (
|
213
|
+
select(func.count(ImageStatus.zarr_url))
|
214
|
+
.where(ImageStatus.dataset_id == dataset_id)
|
215
|
+
.where(ImageStatus.workflowtask_id == workflowtask_id)
|
216
|
+
.where(ImageStatus.status == status)
|
217
|
+
)
|
218
|
+
query = (
|
219
|
+
select(ImageStatus.zarr_url)
|
220
|
+
.where(ImageStatus.dataset_id == dataset_id)
|
221
|
+
.where(ImageStatus.workflowtask_id == workflowtask_id)
|
222
|
+
.where(ImageStatus.status == status)
|
223
|
+
)
|
224
|
+
|
225
|
+
if parameters_hash is not None:
|
226
|
+
total_count_stm = total_count_stm.where(
|
227
|
+
ImageStatus.parameters_hash == parameters_hash
|
228
|
+
)
|
229
|
+
query = query.where(ImageStatus.parameters_hash == parameters_hash)
|
230
|
+
|
231
|
+
if page_size is not None:
|
232
|
+
query = query.limit(page_size)
|
233
|
+
if page > 1:
|
234
|
+
query = query.offset((page - 1) * page_size)
|
235
|
+
|
236
|
+
res_total_count = await db.execute(total_count_stm)
|
237
|
+
total_count = res_total_count.scalar()
|
238
|
+
|
239
|
+
res = await db.execute(query)
|
240
|
+
images = res.scalars().all()
|
241
|
+
|
242
|
+
return {
|
243
|
+
"total_count": total_count,
|
244
|
+
"page_size": page_size,
|
245
|
+
"current_page": page,
|
246
|
+
"images": images,
|
247
|
+
}
|