fractal-server 2.14.11__tar.gz → 2.14.13__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.14.11 → fractal_server-2.14.13}/PKG-INFO +1 -1
- fractal_server-2.14.13/fractal_server/__init__.py +1 -0
- fractal_server-2.14.13/fractal_server/app/routes/api/v2/_aux_task_group_disambiguation.py +163 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/history.py +31 -66
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/images.py +10 -3
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/task_group.py +52 -4
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/workflow_import.py +3 -70
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/v2/runner.py +32 -4
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/schemas/v2/__init__.py +0 -1
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/schemas/v2/history.py +0 -7
- fractal_server-2.14.13/fractal_server/exceptions.py +2 -0
- fractal_server-2.14.13/fractal_server/images/status_tools.py +174 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/images/tools.py +5 -2
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/v2/templates/2_pip_install.sh +13 -3
- {fractal_server-2.14.11 → fractal_server-2.14.13}/pyproject.toml +2 -2
- fractal_server-2.14.11/fractal_server/__init__.py +0 -1
- fractal_server-2.14.11/fractal_server/images/image_status.py +0 -85
- {fractal_server-2.14.11 → fractal_server-2.14.13}/LICENSE +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/README.md +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/__main__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/alembic.ini +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/db/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/models/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/models/linkusergroup.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/models/linkuserproject.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/models/security.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/models/user_settings.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/models/v2/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/models/v2/accounting.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/models/v2/dataset.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/models/v2/history.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/models/v2/job.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/models/v2/project.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/models/v2/task.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/models/v2/task_group.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/models/v2/workflow.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/models/v2/workflowtask.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/admin/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/admin/v2/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/admin/v2/accounting.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/admin/v2/impersonate.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/admin/v2/job.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/admin/v2/project.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/admin/v2/task.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/admin/v2/task_group.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/admin/v2/task_group_lifecycle.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/_aux_functions.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/_aux_functions_history.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/_aux_functions_task_version_update.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/_aux_functions_tasks.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/dataset.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/job.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/pre_submission_checks.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/project.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/status_legacy.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/submit.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/task.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/task_collection.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/task_collection_custom.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/task_group_lifecycle.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/task_version_update.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/workflow.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/workflowtask.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/auth/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/auth/_aux_auth.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/auth/current_user.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/auth/group.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/auth/login.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/auth/oauth.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/auth/register.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/auth/router.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/auth/users.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/aux/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/aux/_job.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/aux/_runner.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/aux/validate_user_settings.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/pagination.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/components.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/exceptions.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/executors/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/executors/base_runner.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/executors/call_command_wrapper.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/executors/local/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/executors/local/get_local_config.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/executors/local/runner.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/executors/slurm_common/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/executors/slurm_common/_batching.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/executors/slurm_common/_job_states.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/executors/slurm_common/_slurm_config.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/executors/slurm_common/base_slurm_runner.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/executors/slurm_common/get_slurm_config.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/executors/slurm_common/remote.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/executors/slurm_common/slurm_job_task_models.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/executors/slurm_ssh/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/executors/slurm_ssh/run_subprocess.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/executors/slurm_ssh/runner.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/executors/slurm_ssh/tar_commands.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/executors/slurm_sudo/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/executors/slurm_sudo/_subprocess_run_as_user.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/executors/slurm_sudo/runner.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/filenames.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/set_start_and_last_task_index.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/shutdown.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/task_files.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/v2/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/v2/_local.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/v2/_slurm_ssh.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/v2/_slurm_sudo.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/v2/db_tools.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/v2/deduplicate_list.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/v2/merge_outputs.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/v2/runner_functions.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/v2/submit_workflow.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/v2/task_interface.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/runner/versions.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/schemas/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/schemas/user.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/schemas/user_group.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/schemas/user_settings.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/schemas/v2/accounting.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/schemas/v2/dataset.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/schemas/v2/dumps.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/schemas/v2/job.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/schemas/v2/manifest.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/schemas/v2/project.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/schemas/v2/status_legacy.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/schemas/v2/task.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/schemas/v2/task_collection.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/schemas/v2/task_group.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/schemas/v2/workflow.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/schemas/v2/workflowtask.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/security/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/security/signup_email.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/user_settings.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/config.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/data_migrations/2_14_10.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/data_migrations/README.md +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/data_migrations/tools.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/gunicorn_fractal.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/images/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/images/models.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/logger.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/main.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/env.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/naming_convention.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/034a469ec2eb_task_groups.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/091b01f51f88_add_usergroup_and_linkusergroup_table.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/19eca0dd47a9_user_settings_project_dir.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/1eac13a26c83_drop_v1_tables.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/316140ff7ee1_remove_usersettings_cache_dir.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/47351f8c7ebc_drop_dataset_filters.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/4cedeb448a53_workflowtask_foreign_keys_not_nullables.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/501961cfcd85_remove_link_between_v1_and_v2_tasks_.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/50a13d6138fd_initial_schema.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/5bf02391cfef_v2.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/70e77f1c38b0_add_applyworkflow_first_task_index_and_.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/71eefd1dd202_add_slurm_accounts.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/84bf0fffde30_add_dumps_to_applyworkflow.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/8e8f227a3e36_update_taskv2_post_2_7_0.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/8f79bd162e35_add_docs_info_and_docs_link_to_task_.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/94a47ea2d3ff_remove_cache_dir_slurm_user_and_slurm_.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/969d84257cac_add_historyrun_task_id.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/97f444d47249_add_applyworkflow_project_dump.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/99ea79d9e5d2_add_dataset_history.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/9c5ae74c9b98_add_user_settings_table.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/9db60297b8b2_set_ondelete.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/9fd26a2b0de4_add_workflow_timestamp_created.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/a7f4d6137b53_add_workflow_dump_to_applyworkflow.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/af1ef1c83c9b_add_accounting_tables.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/af8673379a5c_drop_old_filter_columns.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/c90a7c76e996_job_id_in_history_run.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/d256a7379ab8_taskgroup_activity_and_venv_info_to_.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/d4fe3708d309_make_applyworkflow_workflow_dump_non_.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/da2cb2ac4255_user_group_viewer_paths.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/db09233ad13a_split_filters_and_keep_old_columns.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/e75cac726012_make_applyworkflow_start_timestamp_not_.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/e81103413827_add_job_type_filters.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/efa89c30e0a4_add_project_timestamp_created.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/f37aceb45062_make_historyunit_logfile_required.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/f384e1c0cf5d_drop_task_default_args_columns.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/migrations/versions/fbce16ff4e47_new_history_items.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/py.typed +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/ssh/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/ssh/_fabric.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/string_tools.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/syringe.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/utils.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/v2/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/v2/local/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/v2/local/_utils.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/v2/local/collect.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/v2/local/deactivate.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/v2/local/reactivate.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/v2/ssh/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/v2/ssh/_utils.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/v2/ssh/collect.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/v2/ssh/deactivate.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/v2/ssh/reactivate.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/v2/templates/1_create_venv.sh +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/v2/templates/3_pip_freeze.sh +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/v2/templates/4_pip_show.sh +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/v2/templates/5_get_venv_size_and_file_number.sh +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/v2/templates/6_pip_install_from_freeze.sh +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/v2/utils_background.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/v2/utils_database.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/v2/utils_package_names.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/v2/utils_python_interpreter.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/tasks/v2/utils_templates.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/types/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/types/validators/__init__.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/types/validators/_common_validators.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/types/validators/_filter_validators.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/types/validators/_workflow_task_arguments_validators.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/urls.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/utils.py +0 -0
- {fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/zip_tools.py +0 -0
@@ -0,0 +1 @@
|
|
1
|
+
__VERSION__ = "2.14.13"
|
@@ -0,0 +1,163 @@
|
|
1
|
+
import itertools
|
2
|
+
|
3
|
+
from sqlmodel import select
|
4
|
+
|
5
|
+
from fractal_server.app.db import AsyncSession
|
6
|
+
from fractal_server.app.models import LinkUserGroup
|
7
|
+
from fractal_server.app.models.v2 import TaskGroupV2
|
8
|
+
from fractal_server.exceptions import UnreachableBranchError
|
9
|
+
from fractal_server.logger import set_logger
|
10
|
+
|
11
|
+
|
12
|
+
logger = set_logger(__name__)
|
13
|
+
|
14
|
+
|
15
|
+
async def _disambiguate_task_groups(
|
16
|
+
*,
|
17
|
+
matching_task_groups: list[TaskGroupV2],
|
18
|
+
user_id: int,
|
19
|
+
default_group_id: int,
|
20
|
+
db: AsyncSession,
|
21
|
+
) -> TaskGroupV2 | None:
|
22
|
+
"""
|
23
|
+
Find ownership-based top-priority task group, if any.
|
24
|
+
|
25
|
+
Args:
|
26
|
+
matching_task_groups:
|
27
|
+
user_id:
|
28
|
+
default_group_id:
|
29
|
+
db:
|
30
|
+
|
31
|
+
Returns:
|
32
|
+
The task group or `None`.
|
33
|
+
"""
|
34
|
+
|
35
|
+
# Highest priority: task groups created by user
|
36
|
+
list_user_ids = [tg.user_id for tg in matching_task_groups]
|
37
|
+
try:
|
38
|
+
ind_user_id = list_user_ids.index(user_id)
|
39
|
+
task_group = matching_task_groups[ind_user_id]
|
40
|
+
logger.debug(
|
41
|
+
"[_disambiguate_task_groups] "
|
42
|
+
f"Found task group {task_group.id} with {user_id=}, return."
|
43
|
+
)
|
44
|
+
return task_group
|
45
|
+
except ValueError:
|
46
|
+
logger.debug(
|
47
|
+
"[_disambiguate_task_groups] "
|
48
|
+
f"No task group with {user_id=}, continue."
|
49
|
+
)
|
50
|
+
|
51
|
+
# Medium priority: task groups owned by default user group
|
52
|
+
list_user_group_ids = [tg.user_group_id for tg in matching_task_groups]
|
53
|
+
try:
|
54
|
+
ind_user_group_id = list_user_group_ids.index(default_group_id)
|
55
|
+
task_group = matching_task_groups[ind_user_group_id]
|
56
|
+
logger.debug(
|
57
|
+
"[_disambiguate_task_groups] "
|
58
|
+
f"Found task group {task_group.id} with {user_id=}, return."
|
59
|
+
)
|
60
|
+
return task_group
|
61
|
+
except ValueError:
|
62
|
+
logger.debug(
|
63
|
+
"[_disambiguate_task_groups] "
|
64
|
+
"No task group with user_group_id="
|
65
|
+
f"{default_group_id}, continue."
|
66
|
+
)
|
67
|
+
|
68
|
+
# Lowest priority: task groups owned by other groups, sorted
|
69
|
+
# according to age of the user/usergroup link
|
70
|
+
logger.debug(
|
71
|
+
"[_disambiguate_task_groups] "
|
72
|
+
"Sort remaining task groups by oldest-user-link."
|
73
|
+
)
|
74
|
+
stm = (
|
75
|
+
select(LinkUserGroup.group_id)
|
76
|
+
.where(LinkUserGroup.user_id == user_id)
|
77
|
+
.where(LinkUserGroup.group_id.in_(list_user_group_ids))
|
78
|
+
.order_by(LinkUserGroup.timestamp_created.asc())
|
79
|
+
)
|
80
|
+
res = await db.execute(stm)
|
81
|
+
oldest_user_group_id = res.scalars().first()
|
82
|
+
logger.debug(
|
83
|
+
"[_disambiguate_task_groups] " f"Result: {oldest_user_group_id=}."
|
84
|
+
)
|
85
|
+
task_group = next(
|
86
|
+
iter(
|
87
|
+
task_group
|
88
|
+
for task_group in matching_task_groups
|
89
|
+
if task_group.user_group_id == oldest_user_group_id
|
90
|
+
),
|
91
|
+
None,
|
92
|
+
)
|
93
|
+
return task_group
|
94
|
+
|
95
|
+
|
96
|
+
async def _disambiguate_task_groups_not_none(
|
97
|
+
*,
|
98
|
+
matching_task_groups: list[TaskGroupV2],
|
99
|
+
user_id: int,
|
100
|
+
default_group_id: int,
|
101
|
+
db: AsyncSession,
|
102
|
+
) -> TaskGroupV2:
|
103
|
+
"""
|
104
|
+
Find ownership-based top-priority task group, and fail otherwise.
|
105
|
+
|
106
|
+
Args:
|
107
|
+
matching_task_groups:
|
108
|
+
user_id:
|
109
|
+
default_group_id:
|
110
|
+
db:
|
111
|
+
|
112
|
+
Returns:
|
113
|
+
The top-priority task group.
|
114
|
+
"""
|
115
|
+
task_group = await _disambiguate_task_groups(
|
116
|
+
matching_task_groups=matching_task_groups,
|
117
|
+
user_id=user_id,
|
118
|
+
default_group_id=default_group_id,
|
119
|
+
db=db,
|
120
|
+
)
|
121
|
+
if task_group is None:
|
122
|
+
error_msg = (
|
123
|
+
"[_disambiguate_task_groups_not_none] Could not find a task "
|
124
|
+
f"group ({user_id=}, {default_group_id=})."
|
125
|
+
)
|
126
|
+
logger.error(f"UnreachableBranchError {error_msg}")
|
127
|
+
raise UnreachableBranchError(error_msg)
|
128
|
+
else:
|
129
|
+
return task_group
|
130
|
+
|
131
|
+
|
132
|
+
async def remove_duplicate_task_groups(
|
133
|
+
*,
|
134
|
+
task_groups: list[TaskGroupV2],
|
135
|
+
user_id: int,
|
136
|
+
default_group_id: int,
|
137
|
+
db: AsyncSession,
|
138
|
+
) -> list[TaskGroupV2]:
|
139
|
+
"""
|
140
|
+
Extract a single task group for each `version`.
|
141
|
+
|
142
|
+
Args:
|
143
|
+
task_groups: A list of task groups with identical `pkg_name`
|
144
|
+
user_id: User ID
|
145
|
+
|
146
|
+
Returns:
|
147
|
+
New list of task groups with no duplicate `(pkg_name,version)` entries
|
148
|
+
"""
|
149
|
+
|
150
|
+
new_task_groups = [
|
151
|
+
(
|
152
|
+
await _disambiguate_task_groups_not_none(
|
153
|
+
matching_task_groups=list(groups),
|
154
|
+
user_id=user_id,
|
155
|
+
default_group_id=default_group_id,
|
156
|
+
db=db,
|
157
|
+
)
|
158
|
+
)
|
159
|
+
for version, groups in itertools.groupby(
|
160
|
+
task_groups, key=lambda tg: tg.version
|
161
|
+
)
|
162
|
+
]
|
163
|
+
return new_task_groups
|
{fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/history.py
RENAMED
@@ -1,5 +1,4 @@
|
|
1
1
|
from copy import deepcopy
|
2
|
-
from typing import Any
|
3
2
|
|
4
3
|
from fastapi import APIRouter
|
5
4
|
from fastapi import Depends
|
@@ -16,6 +15,7 @@ from ._aux_functions_history import get_history_run_or_404
|
|
16
15
|
from ._aux_functions_history import get_history_unit_or_404
|
17
16
|
from ._aux_functions_history import get_wftask_check_owner
|
18
17
|
from ._aux_functions_history import read_log_file
|
18
|
+
from .images import ImagePage
|
19
19
|
from .images import ImageQuery
|
20
20
|
from fractal_server.app.db import AsyncSession
|
21
21
|
from fractal_server.app.db import get_async_db
|
@@ -34,13 +34,11 @@ from fractal_server.app.schemas.v2 import HistoryUnitRead
|
|
34
34
|
from fractal_server.app.schemas.v2 import HistoryUnitStatus
|
35
35
|
from fractal_server.app.schemas.v2 import HistoryUnitStatusWithUnset
|
36
36
|
from fractal_server.app.schemas.v2 import ImageLogsRequest
|
37
|
-
from fractal_server.
|
38
|
-
from fractal_server.images.
|
39
|
-
from fractal_server.images.image_status import IMAGE_STATUS_KEY
|
37
|
+
from fractal_server.images.status_tools import enrich_images_async
|
38
|
+
from fractal_server.images.status_tools import IMAGE_STATUS_KEY
|
40
39
|
from fractal_server.images.tools import aggregate_attributes
|
41
40
|
from fractal_server.images.tools import aggregate_types
|
42
41
|
from fractal_server.images.tools import filter_image_list
|
43
|
-
from fractal_server.images.tools import merge_type_filters
|
44
42
|
from fractal_server.logger import set_logger
|
45
43
|
|
46
44
|
|
@@ -62,11 +60,6 @@ def check_historyrun_related_to_dataset_and_wftask(
|
|
62
60
|
)
|
63
61
|
|
64
62
|
|
65
|
-
class ImageWithStatusPage(PaginationResponse[SingleImageWithStatus]):
|
66
|
-
attributes: dict[str, list[Any]]
|
67
|
-
types: list[str]
|
68
|
-
|
69
|
-
|
70
63
|
router = APIRouter()
|
71
64
|
logger = set_logger(__name__)
|
72
65
|
|
@@ -299,11 +292,11 @@ async def get_history_images(
|
|
299
292
|
dataset_id: int,
|
300
293
|
workflowtask_id: int,
|
301
294
|
request_body: ImageQuery,
|
302
|
-
unit_status: HistoryUnitStatusWithUnset | None = None,
|
303
295
|
user: UserOAuth = Depends(current_active_user),
|
304
296
|
db: AsyncSession = Depends(get_async_db),
|
305
297
|
pagination: PaginationRequest = Depends(get_pagination_params),
|
306
|
-
) ->
|
298
|
+
) -> ImagePage:
|
299
|
+
|
307
300
|
# Access control and object retrieval
|
308
301
|
wftask = await get_wftask_check_owner(
|
309
302
|
project_id=project_id,
|
@@ -320,82 +313,54 @@ async def get_history_images(
|
|
320
313
|
db=db,
|
321
314
|
)
|
322
315
|
dataset = res["dataset"]
|
323
|
-
workflow = res["workflow"]
|
324
316
|
|
325
317
|
# Setup prefix for logging
|
326
|
-
prefix = f"[DS{dataset.id}-WFT{
|
327
|
-
|
328
|
-
# (1)
|
329
|
-
|
330
|
-
# (1A) Reconstruct dataset type filters by starting from {} and making
|
331
|
-
# incremental updates with `output_types` of all previous tasks
|
332
|
-
inferred_dataset_type_filters = {}
|
333
|
-
for current_wftask in workflow.task_list[0 : wftask.order]:
|
334
|
-
inferred_dataset_type_filters.update(current_wftask.task.output_types)
|
335
|
-
logger.debug(f"{prefix} {inferred_dataset_type_filters=}")
|
336
|
-
# (1B) Compute type filters for the current wftask
|
337
|
-
type_filters_patch = merge_type_filters(
|
338
|
-
task_input_types=wftask.task.input_types,
|
339
|
-
wftask_type_filters=wftask.type_filters,
|
340
|
-
)
|
341
|
-
logger.debug(f"{prefix} {type_filters_patch=}")
|
342
|
-
# (1C) Combine dataset type filters (lower priority) and current-wftask
|
343
|
-
# filters (higher priority)
|
344
|
-
actual_filters = inferred_dataset_type_filters
|
345
|
-
actual_filters.update(type_filters_patch)
|
346
|
-
logger.debug(f"{prefix} {actual_filters=}")
|
347
|
-
# (1D) Get all matching images from the dataset
|
348
|
-
|
349
|
-
pre_filtered_dataset_images = filter_image_list(
|
318
|
+
prefix = f"[DS{dataset.id}-WFT{workflowtask_id}-images]"
|
319
|
+
|
320
|
+
# (1) Apply type filters
|
321
|
+
type_filtered_images = filter_image_list(
|
350
322
|
images=dataset.images,
|
351
|
-
type_filters=
|
323
|
+
type_filters=request_body.type_filters,
|
352
324
|
)
|
353
325
|
|
354
|
-
|
326
|
+
# (2) Extract valid values for attributes and types
|
327
|
+
attributes = aggregate_attributes(type_filtered_images)
|
328
|
+
attributes[IMAGE_STATUS_KEY] = [
|
329
|
+
HistoryUnitStatusWithUnset.DONE,
|
330
|
+
HistoryUnitStatusWithUnset.SUBMITTED,
|
331
|
+
HistoryUnitStatusWithUnset.FAILED,
|
332
|
+
HistoryUnitStatusWithUnset.UNSET,
|
333
|
+
]
|
334
|
+
types = aggregate_types(type_filtered_images)
|
335
|
+
|
336
|
+
# (3) Enrich images with status attribute
|
337
|
+
type_filtered_images_with_status = await enrich_images_async(
|
355
338
|
dataset_id=dataset_id,
|
356
339
|
workflowtask_id=workflowtask_id,
|
357
|
-
images=
|
340
|
+
images=type_filtered_images,
|
358
341
|
db=db,
|
359
342
|
)
|
360
343
|
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
filtered_dataset_images = filter_image_list(
|
365
|
-
full_images_list,
|
366
|
-
type_filters=request_body.type_filters,
|
344
|
+
# (4) Apply attribute filters
|
345
|
+
final_images_with_status = filter_image_list(
|
346
|
+
type_filtered_images_with_status,
|
367
347
|
attribute_filters=request_body.attribute_filters,
|
368
348
|
)
|
369
|
-
logger.debug(f"{prefix} {len(dataset.images)=}")
|
370
|
-
logger.debug(f"{prefix} {len(filtered_dataset_images)=}")
|
371
|
-
|
372
|
-
attributes = aggregate_attributes(pre_filtered_dataset_images)
|
373
|
-
types = aggregate_types(pre_filtered_dataset_images)
|
374
349
|
|
375
|
-
|
350
|
+
logger.debug(f"{prefix} {len(dataset.images)=}")
|
351
|
+
logger.debug(f"{prefix} {len(final_images_with_status)=}")
|
376
352
|
|
377
|
-
|
353
|
+
# (5) Apply pagination logic
|
354
|
+
total_count = len(final_images_with_status)
|
378
355
|
page_size = pagination.page_size or total_count
|
379
356
|
sorted_images_list = sorted(
|
380
|
-
|
357
|
+
final_images_with_status,
|
381
358
|
key=lambda image: image["zarr_url"],
|
382
359
|
)
|
383
360
|
paginated_images_list = sorted_images_list[
|
384
361
|
(pagination.page - 1) * page_size : pagination.page * page_size
|
385
362
|
]
|
386
363
|
|
387
|
-
# FIXME: This is only for backwards-compatibility. To remove when we
|
388
|
-
# update the webclient
|
389
|
-
paginated_images_list = [
|
390
|
-
{
|
391
|
-
**img,
|
392
|
-
"status": (
|
393
|
-
lambda x: None if x == HistoryUnitStatusWithUnset.UNSET else x
|
394
|
-
)(img["attributes"].pop(IMAGE_STATUS_KEY)),
|
395
|
-
}
|
396
|
-
for img in paginated_images_list
|
397
|
-
]
|
398
|
-
|
399
364
|
return dict(
|
400
365
|
current_page=pagination.page,
|
401
366
|
page_size=page_size,
|
{fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/images.py
RENAMED
@@ -1,5 +1,3 @@
|
|
1
|
-
from typing import Any
|
2
|
-
|
3
1
|
from fastapi import APIRouter
|
4
2
|
from fastapi import Depends
|
5
3
|
from fastapi import HTTPException
|
@@ -26,6 +24,7 @@ from fractal_server.images.tools import aggregate_types
|
|
26
24
|
from fractal_server.images.tools import find_image_by_zarr_url
|
27
25
|
from fractal_server.images.tools import match_filter
|
28
26
|
from fractal_server.types import AttributeFilters
|
27
|
+
from fractal_server.types import ImageAttributeValue
|
29
28
|
from fractal_server.types import TypeFilters
|
30
29
|
|
31
30
|
router = APIRouter()
|
@@ -33,11 +32,19 @@ router = APIRouter()
|
|
33
32
|
|
34
33
|
class ImagePage(PaginationResponse[SingleImage]):
|
35
34
|
|
36
|
-
attributes: dict[str, list[
|
35
|
+
attributes: dict[str, list[ImageAttributeValue]]
|
37
36
|
types: list[str]
|
38
37
|
|
39
38
|
|
40
39
|
class ImageQuery(BaseModel):
|
40
|
+
"""
|
41
|
+
Query for a list of images.
|
42
|
+
|
43
|
+
Attributes:
|
44
|
+
type_filters:
|
45
|
+
attribute_filters:
|
46
|
+
"""
|
47
|
+
|
41
48
|
type_filters: TypeFilters = Field(default_factory=dict)
|
42
49
|
attribute_filters: AttributeFilters = Field(default_factory=dict)
|
43
50
|
|
{fractal_server-2.14.11 → fractal_server-2.14.13}/fractal_server/app/routes/api/v2/task_group.py
RENAMED
@@ -1,8 +1,13 @@
|
|
1
|
+
import itertools
|
2
|
+
|
1
3
|
from fastapi import APIRouter
|
2
4
|
from fastapi import Depends
|
3
5
|
from fastapi import HTTPException
|
4
6
|
from fastapi import Response
|
5
7
|
from fastapi import status
|
8
|
+
from packaging.version import InvalidVersion
|
9
|
+
from packaging.version import parse
|
10
|
+
from packaging.version import Version
|
6
11
|
from pydantic.types import AwareDatetime
|
7
12
|
from sqlmodel import or_
|
8
13
|
from sqlmodel import select
|
@@ -10,6 +15,7 @@ from sqlmodel import select
|
|
10
15
|
from ._aux_functions_tasks import _get_task_group_full_access
|
11
16
|
from ._aux_functions_tasks import _get_task_group_read_access
|
12
17
|
from ._aux_functions_tasks import _verify_non_duplication_group_constraint
|
18
|
+
from ._aux_task_group_disambiguation import remove_duplicate_task_groups
|
13
19
|
from fractal_server.app.db import AsyncSession
|
14
20
|
from fractal_server.app.db import get_async_db
|
15
21
|
from fractal_server.app.models import LinkUserGroup
|
@@ -18,6 +24,7 @@ from fractal_server.app.models.v2 import TaskGroupActivityV2
|
|
18
24
|
from fractal_server.app.models.v2 import TaskGroupV2
|
19
25
|
from fractal_server.app.models.v2 import WorkflowTaskV2
|
20
26
|
from fractal_server.app.routes.auth import current_active_user
|
27
|
+
from fractal_server.app.routes.auth._aux_auth import _get_default_usergroup_id
|
21
28
|
from fractal_server.app.routes.auth._aux_auth import (
|
22
29
|
_verify_user_belongs_to_group,
|
23
30
|
)
|
@@ -33,6 +40,26 @@ router = APIRouter()
|
|
33
40
|
logger = set_logger(__name__)
|
34
41
|
|
35
42
|
|
43
|
+
def _version_sort_key(
|
44
|
+
task_group: TaskGroupV2,
|
45
|
+
) -> tuple[int, Version | str | None]:
|
46
|
+
"""
|
47
|
+
Returns a tuple used as (reverse) ordering key for TaskGroups in
|
48
|
+
`get_task_group_list`.
|
49
|
+
The TaskGroups with a parsable versions are the first in order,
|
50
|
+
sorted according to the sorting rules of packaging.version.Version.
|
51
|
+
Next in order we have the TaskGroups with non-null non-parsable versions,
|
52
|
+
sorted alphabetically.
|
53
|
+
Last we have the TaskGroups with null version.
|
54
|
+
"""
|
55
|
+
if task_group.version is None:
|
56
|
+
return (0, task_group.version)
|
57
|
+
try:
|
58
|
+
return (2, parse(task_group.version))
|
59
|
+
except InvalidVersion:
|
60
|
+
return (1, task_group.version)
|
61
|
+
|
62
|
+
|
36
63
|
@router.get("/activity/", response_model=list[TaskGroupActivityV2Read])
|
37
64
|
async def get_task_group_activity_list(
|
38
65
|
task_group_activity_id: int | None = None,
|
@@ -97,14 +124,14 @@ async def get_task_group_activity(
|
|
97
124
|
return activity
|
98
125
|
|
99
126
|
|
100
|
-
@router.get("/", response_model=list[TaskGroupReadV2])
|
127
|
+
@router.get("/", response_model=list[tuple[str, list[TaskGroupReadV2]]])
|
101
128
|
async def get_task_group_list(
|
102
129
|
user: UserOAuth = Depends(current_active_user),
|
103
130
|
db: AsyncSession = Depends(get_async_db),
|
104
131
|
only_active: bool = False,
|
105
132
|
only_owner: bool = False,
|
106
133
|
args_schema: bool = True,
|
107
|
-
) -> list[TaskGroupReadV2]:
|
134
|
+
) -> list[tuple[str, list[TaskGroupReadV2]]]:
|
108
135
|
"""
|
109
136
|
Get all accessible TaskGroups
|
110
137
|
"""
|
@@ -119,7 +146,7 @@ async def get_task_group_list(
|
|
119
146
|
)
|
120
147
|
),
|
121
148
|
)
|
122
|
-
stm = select(TaskGroupV2).where(condition)
|
149
|
+
stm = select(TaskGroupV2).where(condition).order_by(TaskGroupV2.pkg_name)
|
123
150
|
if only_active:
|
124
151
|
stm = stm.where(TaskGroupV2.active)
|
125
152
|
|
@@ -132,7 +159,28 @@ async def get_task_group_list(
|
|
132
159
|
setattr(task, "args_schema_non_parallel", None)
|
133
160
|
setattr(task, "args_schema_parallel", None)
|
134
161
|
|
135
|
-
|
162
|
+
default_group_id = await _get_default_usergroup_id(db)
|
163
|
+
grouped_result = [
|
164
|
+
(
|
165
|
+
pkg_name,
|
166
|
+
sorted(
|
167
|
+
(
|
168
|
+
await remove_duplicate_task_groups(
|
169
|
+
task_groups=list(groups),
|
170
|
+
user_id=user.id,
|
171
|
+
default_group_id=default_group_id,
|
172
|
+
db=db,
|
173
|
+
)
|
174
|
+
),
|
175
|
+
key=_version_sort_key,
|
176
|
+
reverse=True,
|
177
|
+
),
|
178
|
+
)
|
179
|
+
for pkg_name, groups in itertools.groupby(
|
180
|
+
task_groups, key=lambda tg: tg.pkg_name
|
181
|
+
)
|
182
|
+
]
|
183
|
+
return grouped_result
|
136
184
|
|
137
185
|
|
138
186
|
@router.get("/{task_group_id}/", response_model=TaskGroupReadV2)
|
@@ -21,6 +21,9 @@ from ._aux_functions_tasks import _check_type_filters_compatibility
|
|
21
21
|
from fractal_server.app.models import LinkUserGroup
|
22
22
|
from fractal_server.app.models import UserOAuth
|
23
23
|
from fractal_server.app.models.v2 import TaskGroupV2
|
24
|
+
from fractal_server.app.routes.api.v2._aux_task_group_disambiguation import (
|
25
|
+
_disambiguate_task_groups,
|
26
|
+
)
|
24
27
|
from fractal_server.app.routes.auth import current_active_user
|
25
28
|
from fractal_server.app.routes.auth._aux_auth import _get_default_usergroup_id
|
26
29
|
from fractal_server.app.schemas.v2 import TaskImportV2
|
@@ -85,76 +88,6 @@ async def _get_task_by_source(
|
|
85
88
|
return task_id
|
86
89
|
|
87
90
|
|
88
|
-
async def _disambiguate_task_groups(
|
89
|
-
*,
|
90
|
-
matching_task_groups: list[TaskGroupV2],
|
91
|
-
user_id: int,
|
92
|
-
db: AsyncSession,
|
93
|
-
default_group_id: int,
|
94
|
-
) -> TaskV2 | None:
|
95
|
-
"""
|
96
|
-
Disambiguate task groups based on ownership information.
|
97
|
-
"""
|
98
|
-
# Highest priority: task groups created by user
|
99
|
-
for task_group in matching_task_groups:
|
100
|
-
if task_group.user_id == user_id:
|
101
|
-
logger.info(
|
102
|
-
"[_disambiguate_task_groups] "
|
103
|
-
f"Found task group {task_group.id} with {user_id=}, return."
|
104
|
-
)
|
105
|
-
return task_group
|
106
|
-
logger.info(
|
107
|
-
"[_disambiguate_task_groups] "
|
108
|
-
f"No task group found with {user_id=}, continue."
|
109
|
-
)
|
110
|
-
|
111
|
-
# Medium priority: task groups owned by default user group
|
112
|
-
for task_group in matching_task_groups:
|
113
|
-
if task_group.user_group_id == default_group_id:
|
114
|
-
logger.info(
|
115
|
-
"[_disambiguate_task_groups] "
|
116
|
-
f"Found task group {task_group.id} with user_group_id="
|
117
|
-
f"{default_group_id}, return."
|
118
|
-
)
|
119
|
-
return task_group
|
120
|
-
logger.info(
|
121
|
-
"[_disambiguate_task_groups] "
|
122
|
-
"No task group found with user_group_id="
|
123
|
-
f"{default_group_id}, continue."
|
124
|
-
)
|
125
|
-
|
126
|
-
# Lowest priority: task groups owned by other groups, sorted
|
127
|
-
# according to age of the user/usergroup link
|
128
|
-
logger.info(
|
129
|
-
"[_disambiguate_task_groups] "
|
130
|
-
"Now sorting remaining task groups by oldest-user-link."
|
131
|
-
)
|
132
|
-
user_group_ids = [
|
133
|
-
task_group.user_group_id for task_group in matching_task_groups
|
134
|
-
]
|
135
|
-
stm = (
|
136
|
-
select(LinkUserGroup.group_id)
|
137
|
-
.where(LinkUserGroup.user_id == user_id)
|
138
|
-
.where(LinkUserGroup.group_id.in_(user_group_ids))
|
139
|
-
.order_by(LinkUserGroup.timestamp_created.asc())
|
140
|
-
)
|
141
|
-
res = await db.execute(stm)
|
142
|
-
oldest_user_group_id = res.scalars().first()
|
143
|
-
logger.info(
|
144
|
-
"[_disambiguate_task_groups] "
|
145
|
-
f"Result of sorting: {oldest_user_group_id=}."
|
146
|
-
)
|
147
|
-
task_group = next(
|
148
|
-
iter(
|
149
|
-
task_group
|
150
|
-
for task_group in matching_task_groups
|
151
|
-
if task_group.user_group_id == oldest_user_group_id
|
152
|
-
),
|
153
|
-
None,
|
154
|
-
)
|
155
|
-
return task_group
|
156
|
-
|
157
|
-
|
158
91
|
async def _get_task_by_taskimport(
|
159
92
|
*,
|
160
93
|
task_import: TaskImportV2,
|
@@ -33,10 +33,23 @@ from fractal_server.app.runner.v2.db_tools import update_status_of_history_run
|
|
33
33
|
from fractal_server.app.schemas.v2 import HistoryUnitStatus
|
34
34
|
from fractal_server.app.schemas.v2 import TaskDumpV2
|
35
35
|
from fractal_server.app.schemas.v2 import TaskGroupDumpV2
|
36
|
+
from fractal_server.images.status_tools import enrich_images_sync
|
37
|
+
from fractal_server.images.status_tools import IMAGE_STATUS_KEY
|
36
38
|
from fractal_server.images.tools import merge_type_filters
|
37
39
|
from fractal_server.types import AttributeFilters
|
38
40
|
|
39
41
|
|
42
|
+
def _remove_status_from_attributes(
|
43
|
+
images: list[dict[str, Any]],
|
44
|
+
) -> list[dict[str, Any]]:
|
45
|
+
"""
|
46
|
+
Drop attribute `IMAGE_STATUS_KEY` from all images.
|
47
|
+
"""
|
48
|
+
images_copy = deepcopy(images)
|
49
|
+
[img["attributes"].pop(IMAGE_STATUS_KEY) for img in images_copy]
|
50
|
+
return images_copy
|
51
|
+
|
52
|
+
|
40
53
|
def drop_none_attributes(attributes: dict[str, Any]) -> dict[str, Any]:
|
41
54
|
# Unset attributes with `None` value
|
42
55
|
non_none_attributes = {
|
@@ -106,7 +119,11 @@ def execute_tasks_v2(
|
|
106
119
|
tmp_images = deepcopy(dataset.images)
|
107
120
|
current_dataset_type_filters = copy(job_type_filters)
|
108
121
|
|
109
|
-
|
122
|
+
ENRICH_IMAGES_WITH_STATUS: bool = (
|
123
|
+
IMAGE_STATUS_KEY in job_attribute_filters.keys()
|
124
|
+
)
|
125
|
+
|
126
|
+
for ind_wftask, wftask in enumerate(wf_task_list):
|
110
127
|
task = wftask.task
|
111
128
|
task_name = task.name
|
112
129
|
logger.debug(f'SUBMIT {wftask.order}-th task (name="{task_name}")')
|
@@ -122,15 +139,22 @@ def execute_tasks_v2(
|
|
122
139
|
wftask_type_filters=wftask.type_filters,
|
123
140
|
)
|
124
141
|
type_filters.update(type_filters_patch)
|
142
|
+
|
143
|
+
if ind_wftask == 0 and ENRICH_IMAGES_WITH_STATUS:
|
144
|
+
# FIXME: Could this be done on `type_filtered_images`?
|
145
|
+
tmp_images = enrich_images_sync(
|
146
|
+
images=tmp_images,
|
147
|
+
dataset_id=dataset.id,
|
148
|
+
workflowtask_id=wftask.id,
|
149
|
+
)
|
125
150
|
type_filtered_images = filter_image_list(
|
126
151
|
images=tmp_images,
|
127
152
|
type_filters=type_filters,
|
128
|
-
attribute_filters=None,
|
129
153
|
)
|
130
154
|
num_available_images = len(type_filtered_images)
|
155
|
+
|
131
156
|
filtered_images = filter_image_list(
|
132
157
|
images=type_filtered_images,
|
133
|
-
type_filters=None,
|
134
158
|
attribute_filters=job_attribute_filters,
|
135
159
|
)
|
136
160
|
else:
|
@@ -379,7 +403,11 @@ def execute_tasks_v2(
|
|
379
403
|
with next(get_sync_db()) as db:
|
380
404
|
# Write current dataset images into the database.
|
381
405
|
db_dataset = db.get(DatasetV2, dataset.id)
|
382
|
-
|
406
|
+
if ENRICH_IMAGES_WITH_STATUS:
|
407
|
+
|
408
|
+
db_dataset.images = _remove_status_from_attributes(tmp_images)
|
409
|
+
else:
|
410
|
+
db_dataset.images = tmp_images
|
383
411
|
flag_modified(db_dataset, "images")
|
384
412
|
db.merge(db_dataset)
|
385
413
|
|
@@ -16,7 +16,6 @@ from .history import HistoryUnitRead # noqa F401
|
|
16
16
|
from .history import HistoryUnitStatus # noqa F401
|
17
17
|
from .history import HistoryUnitStatusWithUnset # noqa F401
|
18
18
|
from .history import ImageLogsRequest # noqa F401
|
19
|
-
from .history import SingleImageWithStatus # noqa F401
|
20
19
|
from .job import JobCreateV2 # noqa F401
|
21
20
|
from .job import JobReadV2 # noqa F401
|
22
21
|
from .job import JobStatusTypeV2 # noqa F401
|
@@ -6,8 +6,6 @@ from pydantic import AwareDatetime
|
|
6
6
|
from pydantic import BaseModel
|
7
7
|
from pydantic import field_serializer
|
8
8
|
|
9
|
-
from ....images import SingleImage
|
10
|
-
|
11
9
|
|
12
10
|
class HistoryUnitStatus(StrEnum):
|
13
11
|
"""
|
@@ -83,8 +81,3 @@ class ImageLogsRequest(BaseModel):
|
|
83
81
|
workflowtask_id: int
|
84
82
|
dataset_id: int
|
85
83
|
zarr_url: str
|
86
|
-
|
87
|
-
|
88
|
-
# FIXME: remove this when we update the webclient
|
89
|
-
class SingleImageWithStatus(SingleImage):
|
90
|
-
status: HistoryUnitStatus | None = None
|