fractal-server 2.13.0__tar.gz → 2.13.1__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.0 → fractal_server-2.13.1}/PKG-INFO +1 -1
- fractal_server-2.13.1/fractal_server/__init__.py +1 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/models/v2/__init__.py +4 -0
- fractal_server-2.13.1/fractal_server/app/models/v2/accounting.py +35 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/admin/v2/__init__.py +4 -0
- fractal_server-2.13.1/fractal_server/app/routes/admin/v2/accounting.py +108 -0
- fractal_server-2.13.1/fractal_server/app/routes/admin/v2/impersonate.py +35 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/admin/v2/job.py +5 -13
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/admin/v2/task_group.py +4 -12
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +3 -3
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/api/v2/submit.py +1 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/api/v2/task_group.py +2 -5
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/v2/__init__.py +4 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/v2/_local/__init__.py +3 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/v2/_slurm_ssh/__init__.py +2 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/v2/_slurm_sudo/__init__.py +2 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/v2/runner.py +16 -6
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/v2/runner_functions.py +12 -11
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/schemas/v2/__init__.py +1 -0
- fractal_server-2.13.1/fractal_server/app/schemas/v2/accounting.py +18 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/config.py +56 -50
- fractal_server-2.13.1/fractal_server/migrations/versions/af1ef1c83c9b_add_accounting_tables.py +57 -0
- fractal_server-2.13.1/fractal_server/tasks/v2/__init__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/tasks/v2/utils_background.py +1 -1
- {fractal_server-2.13.0 → fractal_server-2.13.1}/pyproject.toml +3 -3
- fractal_server-2.13.0/fractal_server/__init__.py +0 -1
- fractal_server-2.13.0/fractal_server/app/routes/aux/__init__.py +0 -20
- {fractal_server-2.13.0 → fractal_server-2.13.1}/LICENSE +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/README.md +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/__main__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/alembic.ini +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/__init__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/db/__init__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/models/__init__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/models/linkusergroup.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/models/linkuserproject.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/models/security.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/models/user_settings.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/models/v2/dataset.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/models/v2/job.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/models/v2/project.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/models/v2/task.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/models/v2/task_group.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/models/v2/workflow.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/models/v2/workflowtask.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/__init__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/admin/__init__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/admin/v2/project.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/admin/v2/task.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/admin/v2/task_group_lifecycle.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/api/__init__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/api/v2/__init__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/api/v2/_aux_functions.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/api/v2/_aux_functions_tasks.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/api/v2/dataset.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/api/v2/images.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/api/v2/job.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/api/v2/project.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/api/v2/status.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/api/v2/task.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/api/v2/task_collection.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/api/v2/task_collection_custom.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/api/v2/task_group_lifecycle.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/api/v2/workflow.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/api/v2/workflow_import.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/api/v2/workflowtask.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/auth/__init__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/auth/_aux_auth.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/auth/current_user.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/auth/group.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/auth/login.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/auth/oauth.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/auth/register.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/auth/router.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/auth/users.py +0 -0
- {fractal_server-2.13.0/fractal_server/app/runner → fractal_server-2.13.1/fractal_server/app/routes/aux}/__init__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/aux/_job.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/aux/_runner.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/aux/validate_user_settings.py +0 -0
- {fractal_server-2.13.0/fractal_server/app/runner/executors → fractal_server-2.13.1/fractal_server/app/runner}/__init__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/components.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/compress_folder.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/exceptions.py +0 -0
- {fractal_server-2.13.0/fractal_server/app/runner/executors/slurm → fractal_server-2.13.1/fractal_server/app/runner/executors}/__init__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/executors/_job_states.py +0 -0
- {fractal_server-2.13.0/fractal_server/app/runner/executors/slurm/ssh → fractal_server-2.13.1/fractal_server/app/runner/executors/slurm}/__init__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/executors/slurm/_batching.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/executors/slurm/_slurm_config.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/executors/slurm/remote.py +0 -0
- {fractal_server-2.13.0/fractal_server/app/runner/executors/slurm/sudo → fractal_server-2.13.1/fractal_server/app/runner/executors/slurm/ssh}/__init__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/executors/slurm/ssh/_executor_wait_thread.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/executors/slurm/ssh/_slurm_job.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/executors/slurm/ssh/executor.py +0 -0
- {fractal_server-2.13.0/fractal_server/app/runner/v2/_slurm_common → fractal_server-2.13.1/fractal_server/app/runner/executors/slurm/sudo}/__init__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/executors/slurm/sudo/_check_jobs_status.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/executors/slurm/sudo/_executor_wait_thread.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/executors/slurm/sudo/_subprocess_run_as_user.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/executors/slurm/sudo/executor.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/executors/slurm/utils_executors.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/extract_archive.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/filenames.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/run_subprocess.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/set_start_and_last_task_index.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/shutdown.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/task_files.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/v2/_local/_local_config.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/v2/_local/_submit_setup.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/v2/_local/executor.py +0 -0
- {fractal_server-2.13.0/fractal_server/tasks/v2 → fractal_server-2.13.1/fractal_server/app/runner/v2/_slurm_common}/__init__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/v2/_slurm_common/get_slurm_config.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/v2/_slurm_ssh/_submit_setup.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/v2/_slurm_sudo/_submit_setup.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/v2/deduplicate_list.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/v2/handle_failed_job.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/v2/merge_outputs.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/v2/runner_functions_low_level.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/v2/task_interface.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/versions.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/schemas/__init__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/schemas/_filter_validators.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/schemas/_validators.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/schemas/user.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/schemas/user_group.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/schemas/user_settings.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/schemas/v2/dataset.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/schemas/v2/dumps.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/schemas/v2/job.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/schemas/v2/manifest.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/schemas/v2/project.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/schemas/v2/status.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/schemas/v2/task.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/schemas/v2/task_collection.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/schemas/v2/task_group.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/schemas/v2/workflow.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/schemas/v2/workflowtask.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/security/__init__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/security/signup_email.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/user_settings.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/data_migrations/README.md +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/data_migrations/tools.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/gunicorn_fractal.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/images/__init__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/images/models.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/images/tools.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/logger.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/main.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/env.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/naming_convention.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/034a469ec2eb_task_groups.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/091b01f51f88_add_usergroup_and_linkusergroup_table.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/19eca0dd47a9_user_settings_project_dir.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/1eac13a26c83_drop_v1_tables.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/316140ff7ee1_remove_usersettings_cache_dir.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/4cedeb448a53_workflowtask_foreign_keys_not_nullables.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/501961cfcd85_remove_link_between_v1_and_v2_tasks_.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/50a13d6138fd_initial_schema.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/5bf02391cfef_v2.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/70e77f1c38b0_add_applyworkflow_first_task_index_and_.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/71eefd1dd202_add_slurm_accounts.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/84bf0fffde30_add_dumps_to_applyworkflow.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/8e8f227a3e36_update_taskv2_post_2_7_0.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/8f79bd162e35_add_docs_info_and_docs_link_to_task_.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/94a47ea2d3ff_remove_cache_dir_slurm_user_and_slurm_.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/97f444d47249_add_applyworkflow_project_dump.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/99ea79d9e5d2_add_dataset_history.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/9c5ae74c9b98_add_user_settings_table.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/9fd26a2b0de4_add_workflow_timestamp_created.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/a7f4d6137b53_add_workflow_dump_to_applyworkflow.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/af8673379a5c_drop_old_filter_columns.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/d256a7379ab8_taskgroup_activity_and_venv_info_to_.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/d4fe3708d309_make_applyworkflow_workflow_dump_non_.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/da2cb2ac4255_user_group_viewer_paths.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/db09233ad13a_split_filters_and_keep_old_columns.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/e75cac726012_make_applyworkflow_start_timestamp_not_.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/efa89c30e0a4_add_project_timestamp_created.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/migrations/versions/f384e1c0cf5d_drop_task_default_args_columns.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/py.typed +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/ssh/__init__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/ssh/_fabric.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/string_tools.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/syringe.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/tasks/__init__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/tasks/utils.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/tasks/v2/local/__init__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/tasks/v2/local/_utils.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/tasks/v2/local/collect.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/tasks/v2/local/deactivate.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/tasks/v2/local/reactivate.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/tasks/v2/ssh/__init__.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/tasks/v2/ssh/_utils.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/tasks/v2/ssh/collect.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/tasks/v2/ssh/deactivate.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/tasks/v2/ssh/reactivate.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/tasks/v2/templates/1_create_venv.sh +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/tasks/v2/templates/2_pip_install.sh +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/tasks/v2/templates/3_pip_freeze.sh +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/tasks/v2/templates/4_pip_show.sh +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/tasks/v2/templates/5_get_venv_size_and_file_number.sh +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/tasks/v2/templates/6_pip_install_from_freeze.sh +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/tasks/v2/utils_database.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/tasks/v2/utils_package_names.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/tasks/v2/utils_python_interpreter.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/tasks/v2/utils_templates.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/urls.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/utils.py +0 -0
- {fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/zip_tools.py +0 -0
@@ -0,0 +1 @@
|
|
1
|
+
__VERSION__ = "2.13.1"
|
@@ -2,6 +2,8 @@
|
|
2
2
|
v2 `models` module
|
3
3
|
"""
|
4
4
|
from ..linkuserproject import LinkUserProjectV2
|
5
|
+
from .accounting import AccountingRecord
|
6
|
+
from .accounting import AccountingRecordSlurm
|
5
7
|
from .dataset import DatasetV2
|
6
8
|
from .job import JobV2
|
7
9
|
from .project import ProjectV2
|
@@ -12,6 +14,8 @@ from .workflow import WorkflowV2
|
|
12
14
|
from .workflowtask import WorkflowTaskV2
|
13
15
|
|
14
16
|
__all__ = [
|
17
|
+
"AccountingRecord",
|
18
|
+
"AccountingRecordSlurm",
|
15
19
|
"LinkUserProjectV2",
|
16
20
|
"DatasetV2",
|
17
21
|
"JobV2",
|
@@ -0,0 +1,35 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
from typing import Optional
|
3
|
+
|
4
|
+
from sqlalchemy import Column
|
5
|
+
from sqlalchemy import Integer
|
6
|
+
from sqlalchemy.dialects.postgresql import ARRAY
|
7
|
+
from sqlalchemy.types import DateTime
|
8
|
+
from sqlmodel import Field
|
9
|
+
from sqlmodel import SQLModel
|
10
|
+
|
11
|
+
from ....utils import get_timestamp
|
12
|
+
|
13
|
+
|
14
|
+
class AccountingRecord(SQLModel, table=True):
|
15
|
+
id: Optional[int] = Field(default=None, primary_key=True)
|
16
|
+
user_id: int = Field(foreign_key="user_oauth.id", nullable=False)
|
17
|
+
timestamp: datetime = Field(
|
18
|
+
default_factory=get_timestamp,
|
19
|
+
sa_column=Column(DateTime(timezone=True), nullable=False),
|
20
|
+
)
|
21
|
+
num_tasks: int
|
22
|
+
num_new_images: int
|
23
|
+
|
24
|
+
|
25
|
+
class AccountingRecordSlurm(SQLModel, table=True):
|
26
|
+
id: Optional[int] = Field(default=None, primary_key=True)
|
27
|
+
user_id: int = Field(foreign_key="user_oauth.id", nullable=False)
|
28
|
+
timestamp: datetime = Field(
|
29
|
+
default_factory=get_timestamp,
|
30
|
+
sa_column=Column(DateTime(timezone=True), nullable=False),
|
31
|
+
)
|
32
|
+
slurm_job_ids: list[int] = Field(
|
33
|
+
default_factory=list,
|
34
|
+
sa_column=Column(ARRAY(Integer)),
|
35
|
+
)
|
{fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/admin/v2/__init__.py
RENAMED
@@ -3,6 +3,8 @@
|
|
3
3
|
"""
|
4
4
|
from fastapi import APIRouter
|
5
5
|
|
6
|
+
from .accounting import router as accounting_router
|
7
|
+
from .impersonate import router as impersonate_router
|
6
8
|
from .job import router as job_router
|
7
9
|
from .project import router as project_router
|
8
10
|
from .task import router as task_router
|
@@ -11,6 +13,7 @@ from .task_group_lifecycle import router as task_group_lifecycle_router
|
|
11
13
|
|
12
14
|
router_admin_v2 = APIRouter()
|
13
15
|
|
16
|
+
router_admin_v2.include_router(accounting_router, prefix="/accounting")
|
14
17
|
router_admin_v2.include_router(job_router, prefix="/job")
|
15
18
|
router_admin_v2.include_router(project_router, prefix="/project")
|
16
19
|
router_admin_v2.include_router(task_router, prefix="/task")
|
@@ -18,3 +21,4 @@ router_admin_v2.include_router(task_group_router, prefix="/task-group")
|
|
18
21
|
router_admin_v2.include_router(
|
19
22
|
task_group_lifecycle_router, prefix="/task-group"
|
20
23
|
)
|
24
|
+
router_admin_v2.include_router(impersonate_router, prefix="/impersonate")
|
@@ -0,0 +1,108 @@
|
|
1
|
+
from itertools import chain
|
2
|
+
from typing import Optional
|
3
|
+
|
4
|
+
from fastapi import APIRouter
|
5
|
+
from fastapi import Depends
|
6
|
+
from fastapi import HTTPException
|
7
|
+
from fastapi import Query
|
8
|
+
from fastapi import status
|
9
|
+
from fastapi.responses import JSONResponse
|
10
|
+
from pydantic import BaseModel
|
11
|
+
from pydantic.types import AwareDatetime
|
12
|
+
from sqlmodel import func
|
13
|
+
from sqlmodel import select
|
14
|
+
|
15
|
+
from fractal_server.app.db import AsyncSession
|
16
|
+
from fractal_server.app.db import get_async_db
|
17
|
+
from fractal_server.app.models import UserOAuth
|
18
|
+
from fractal_server.app.models.v2 import AccountingRecord
|
19
|
+
from fractal_server.app.models.v2 import AccountingRecordSlurm
|
20
|
+
from fractal_server.app.routes.auth import current_active_superuser
|
21
|
+
from fractal_server.app.schemas.v2 import AccountingRecordRead
|
22
|
+
|
23
|
+
|
24
|
+
class AccountingQuery(BaseModel):
|
25
|
+
user_id: Optional[int] = None
|
26
|
+
timestamp_min: Optional[AwareDatetime] = None
|
27
|
+
timestamp_max: Optional[AwareDatetime] = None
|
28
|
+
|
29
|
+
|
30
|
+
class AccountingPage(BaseModel):
|
31
|
+
total_count: int
|
32
|
+
page_size: int
|
33
|
+
current_page: int
|
34
|
+
records: list[AccountingRecordRead]
|
35
|
+
|
36
|
+
|
37
|
+
router = APIRouter()
|
38
|
+
|
39
|
+
|
40
|
+
@router.post("/", response_model=AccountingPage)
|
41
|
+
async def query_accounting(
|
42
|
+
query: AccountingQuery,
|
43
|
+
# pagination
|
44
|
+
page: int = Query(default=1, ge=1),
|
45
|
+
page_size: Optional[int] = Query(default=None, ge=1),
|
46
|
+
# dependencies
|
47
|
+
superuser: UserOAuth = Depends(current_active_superuser),
|
48
|
+
db: AsyncSession = Depends(get_async_db),
|
49
|
+
) -> AccountingPage:
|
50
|
+
|
51
|
+
if page_size is None and page > 1:
|
52
|
+
raise HTTPException(
|
53
|
+
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
54
|
+
detail=(f"Invalid pagination parameters: {page=}, {page_size=}."),
|
55
|
+
)
|
56
|
+
|
57
|
+
stm = select(AccountingRecord).order_by(AccountingRecord.id)
|
58
|
+
stm_count = select(func.count(AccountingRecord.id))
|
59
|
+
if query.user_id is not None:
|
60
|
+
stm = stm.where(AccountingRecord.user_id == query.user_id)
|
61
|
+
stm_count = stm_count.where(AccountingRecord.user_id == query.user_id)
|
62
|
+
if query.timestamp_min is not None:
|
63
|
+
stm = stm.where(AccountingRecord.timestamp >= query.timestamp_min)
|
64
|
+
stm_count = stm_count.where(
|
65
|
+
AccountingRecord.timestamp >= query.timestamp_min
|
66
|
+
)
|
67
|
+
if query.timestamp_max is not None:
|
68
|
+
stm = stm.where(AccountingRecord.timestamp <= query.timestamp_max)
|
69
|
+
stm_count = stm_count.where(
|
70
|
+
AccountingRecord.timestamp <= query.timestamp_max
|
71
|
+
)
|
72
|
+
if page_size is not None:
|
73
|
+
stm = stm.offset((page - 1) * page_size).limit(page_size)
|
74
|
+
|
75
|
+
res = await db.execute(stm)
|
76
|
+
records = res.scalars().all()
|
77
|
+
res_total_count = await db.execute(stm_count)
|
78
|
+
total_count = res_total_count.scalar()
|
79
|
+
|
80
|
+
actual_page_size = page_size or len(records)
|
81
|
+
return AccountingPage(
|
82
|
+
total_count=total_count,
|
83
|
+
page_size=actual_page_size,
|
84
|
+
current_page=page,
|
85
|
+
records=[record.model_dump() for record in records],
|
86
|
+
)
|
87
|
+
|
88
|
+
|
89
|
+
@router.post("/slurm/")
|
90
|
+
async def query_accounting_slurm(
|
91
|
+
query: AccountingQuery,
|
92
|
+
# dependencies
|
93
|
+
superuser: UserOAuth = Depends(current_active_superuser),
|
94
|
+
db: AsyncSession = Depends(get_async_db),
|
95
|
+
) -> JSONResponse:
|
96
|
+
|
97
|
+
stm = select(AccountingRecordSlurm.slurm_job_ids)
|
98
|
+
if query.user_id is not None:
|
99
|
+
stm = stm.where(AccountingRecordSlurm.user_id == query.user_id)
|
100
|
+
if query.timestamp_min is not None:
|
101
|
+
stm = stm.where(AccountingRecordSlurm.timestamp >= query.timestamp_min)
|
102
|
+
if query.timestamp_max is not None:
|
103
|
+
stm = stm.where(AccountingRecordSlurm.timestamp <= query.timestamp_max)
|
104
|
+
|
105
|
+
res = await db.execute(stm)
|
106
|
+
nested_slurm_job_ids = res.scalars().all()
|
107
|
+
aggregated_slurm_job_ids = list(chain(*nested_slurm_job_ids))
|
108
|
+
return JSONResponse(content=aggregated_slurm_job_ids, status_code=200)
|
@@ -0,0 +1,35 @@
|
|
1
|
+
from fastapi import APIRouter
|
2
|
+
from fastapi import Depends
|
3
|
+
from fastapi.responses import JSONResponse
|
4
|
+
from fastapi_users.authentication import JWTStrategy
|
5
|
+
|
6
|
+
from fractal_server.app.db import AsyncSession
|
7
|
+
from fractal_server.app.db import get_async_db
|
8
|
+
from fractal_server.app.models import UserOAuth
|
9
|
+
from fractal_server.app.routes.auth import current_active_superuser
|
10
|
+
from fractal_server.app.routes.auth._aux_auth import _user_or_404
|
11
|
+
from fractal_server.config import get_settings
|
12
|
+
from fractal_server.syringe import Inject
|
13
|
+
|
14
|
+
router = APIRouter()
|
15
|
+
|
16
|
+
|
17
|
+
@router.get("/{user_id}/")
|
18
|
+
async def impersonate_user(
|
19
|
+
user_id: int,
|
20
|
+
superuser: UserOAuth = Depends(current_active_superuser),
|
21
|
+
db: AsyncSession = Depends(get_async_db),
|
22
|
+
) -> JSONResponse:
|
23
|
+
user = await _user_or_404(user_id, db)
|
24
|
+
|
25
|
+
settings = Inject(get_settings)
|
26
|
+
jwt_strategy = JWTStrategy(
|
27
|
+
secret=settings.JWT_SECRET_KEY, # type: ignore
|
28
|
+
lifetime_seconds=7200, # 2 hours
|
29
|
+
)
|
30
|
+
token = await jwt_strategy.write_token(user)
|
31
|
+
|
32
|
+
return JSONResponse(
|
33
|
+
content={"access_token": token, "token_type": "bearer"},
|
34
|
+
status_code=200,
|
35
|
+
)
|
@@ -1,4 +1,3 @@
|
|
1
|
-
from datetime import datetime
|
2
1
|
from pathlib import Path
|
3
2
|
from typing import Optional
|
4
3
|
|
@@ -8,6 +7,7 @@ from fastapi import HTTPException
|
|
8
7
|
from fastapi import Response
|
9
8
|
from fastapi import status
|
10
9
|
from fastapi.responses import StreamingResponse
|
10
|
+
from pydantic.types import AwareDatetime
|
11
11
|
from sqlmodel import select
|
12
12
|
|
13
13
|
from fractal_server.app.db import AsyncSession
|
@@ -16,7 +16,6 @@ from fractal_server.app.models import UserOAuth
|
|
16
16
|
from fractal_server.app.models.v2 import JobV2
|
17
17
|
from fractal_server.app.models.v2 import ProjectV2
|
18
18
|
from fractal_server.app.routes.auth import current_active_superuser
|
19
|
-
from fractal_server.app.routes.aux import _raise_if_naive_datetime
|
20
19
|
from fractal_server.app.routes.aux._job import _write_shutdown_file
|
21
20
|
from fractal_server.app.routes.aux._runner import _check_shutdown_is_supported
|
22
21
|
from fractal_server.app.runner.filenames import WORKFLOW_LOG_FILENAME
|
@@ -37,10 +36,10 @@ async def view_job(
|
|
37
36
|
dataset_id: Optional[int] = None,
|
38
37
|
workflow_id: Optional[int] = None,
|
39
38
|
status: Optional[JobStatusTypeV2] = None,
|
40
|
-
start_timestamp_min: Optional[
|
41
|
-
start_timestamp_max: Optional[
|
42
|
-
end_timestamp_min: Optional[
|
43
|
-
end_timestamp_max: Optional[
|
39
|
+
start_timestamp_min: Optional[AwareDatetime] = None,
|
40
|
+
start_timestamp_max: Optional[AwareDatetime] = None,
|
41
|
+
end_timestamp_min: Optional[AwareDatetime] = None,
|
42
|
+
end_timestamp_max: Optional[AwareDatetime] = None,
|
44
43
|
log: bool = True,
|
45
44
|
user: UserOAuth = Depends(current_active_superuser),
|
46
45
|
db: AsyncSession = Depends(get_async_db),
|
@@ -67,13 +66,6 @@ async def view_job(
|
|
67
66
|
`job.log` is set to `None`.
|
68
67
|
"""
|
69
68
|
|
70
|
-
_raise_if_naive_datetime(
|
71
|
-
start_timestamp_min,
|
72
|
-
start_timestamp_max,
|
73
|
-
end_timestamp_min,
|
74
|
-
end_timestamp_max,
|
75
|
-
)
|
76
|
-
|
77
69
|
stm = select(JobV2)
|
78
70
|
|
79
71
|
if id is not None:
|
{fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/admin/v2/task_group.py
RENAMED
@@ -1,4 +1,3 @@
|
|
1
|
-
from datetime import datetime
|
2
1
|
from typing import Optional
|
3
2
|
|
4
3
|
from fastapi import APIRouter
|
@@ -6,6 +5,7 @@ from fastapi import Depends
|
|
6
5
|
from fastapi import HTTPException
|
7
6
|
from fastapi import Response
|
8
7
|
from fastapi import status
|
8
|
+
from pydantic.types import AwareDatetime
|
9
9
|
from sqlalchemy.sql.operators import is_
|
10
10
|
from sqlalchemy.sql.operators import is_not
|
11
11
|
from sqlmodel import select
|
@@ -20,7 +20,6 @@ from fractal_server.app.routes.auth import current_active_superuser
|
|
20
20
|
from fractal_server.app.routes.auth._aux_auth import (
|
21
21
|
_verify_user_belongs_to_group,
|
22
22
|
)
|
23
|
-
from fractal_server.app.routes.aux import _raise_if_naive_datetime
|
24
23
|
from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
|
25
24
|
from fractal_server.app.schemas.v2 import TaskGroupActivityStatusV2
|
26
25
|
from fractal_server.app.schemas.v2 import TaskGroupActivityV2Read
|
@@ -42,13 +41,11 @@ async def get_task_group_activity_list(
|
|
42
41
|
pkg_name: Optional[str] = None,
|
43
42
|
status: Optional[TaskGroupActivityStatusV2] = None,
|
44
43
|
action: Optional[TaskGroupActivityActionV2] = None,
|
45
|
-
timestamp_started_min: Optional[
|
44
|
+
timestamp_started_min: Optional[AwareDatetime] = None,
|
46
45
|
superuser: UserOAuth = Depends(current_active_superuser),
|
47
46
|
db: AsyncSession = Depends(get_async_db),
|
48
47
|
) -> list[TaskGroupActivityV2Read]:
|
49
48
|
|
50
|
-
_raise_if_naive_datetime(timestamp_started_min)
|
51
|
-
|
52
49
|
stm = select(TaskGroupActivityV2)
|
53
50
|
if task_group_activity_id is not None:
|
54
51
|
stm = stm.where(TaskGroupActivityV2.id == task_group_activity_id)
|
@@ -96,19 +93,14 @@ async def query_task_group_list(
|
|
96
93
|
active: Optional[bool] = None,
|
97
94
|
pkg_name: Optional[str] = None,
|
98
95
|
origin: Optional[TaskGroupV2OriginEnum] = None,
|
99
|
-
timestamp_last_used_min: Optional[
|
100
|
-
timestamp_last_used_max: Optional[
|
96
|
+
timestamp_last_used_min: Optional[AwareDatetime] = None,
|
97
|
+
timestamp_last_used_max: Optional[AwareDatetime] = None,
|
101
98
|
user: UserOAuth = Depends(current_active_superuser),
|
102
99
|
db: AsyncSession = Depends(get_async_db),
|
103
100
|
) -> list[TaskGroupReadV2]:
|
104
101
|
|
105
102
|
stm = select(TaskGroupV2)
|
106
103
|
|
107
|
-
_raise_if_naive_datetime(
|
108
|
-
timestamp_last_used_max,
|
109
|
-
timestamp_last_used_min,
|
110
|
-
)
|
111
|
-
|
112
104
|
if user_group_id is not None and private is True:
|
113
105
|
raise HTTPException(
|
114
106
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
@@ -55,7 +55,7 @@ async def get_package_version_from_pypi(
|
|
55
55
|
f"A TimeoutException occurred while getting {url}.\n"
|
56
56
|
f"Original error: {str(e)}."
|
57
57
|
)
|
58
|
-
logger.
|
58
|
+
logger.warning(error_msg)
|
59
59
|
raise HTTPException(
|
60
60
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
61
61
|
detail=error_msg,
|
@@ -65,7 +65,7 @@ async def get_package_version_from_pypi(
|
|
65
65
|
f"An unknown error occurred while getting {url}. "
|
66
66
|
f"Original error: {str(e)}."
|
67
67
|
)
|
68
|
-
logger.
|
68
|
+
logger.warning(error_msg)
|
69
69
|
raise HTTPException(
|
70
70
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
71
71
|
detail=error_msg,
|
@@ -85,7 +85,7 @@ async def get_package_version_from_pypi(
|
|
85
85
|
latest_version = response_data["info"]["version"]
|
86
86
|
available_releases = response_data["releases"].keys()
|
87
87
|
except KeyError as e:
|
88
|
-
logger.
|
88
|
+
logger.warning(
|
89
89
|
f"A KeyError occurred while getting {url}. "
|
90
90
|
f"Original error: {str(e)}."
|
91
91
|
)
|
{fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/routes/api/v2/task_group.py
RENAMED
@@ -1,4 +1,3 @@
|
|
1
|
-
from datetime import datetime
|
2
1
|
from typing import Optional
|
3
2
|
|
4
3
|
from fastapi import APIRouter
|
@@ -6,6 +5,7 @@ from fastapi import Depends
|
|
6
5
|
from fastapi import HTTPException
|
7
6
|
from fastapi import Response
|
8
7
|
from fastapi import status
|
8
|
+
from pydantic.types import AwareDatetime
|
9
9
|
from sqlmodel import or_
|
10
10
|
from sqlmodel import select
|
11
11
|
|
@@ -23,7 +23,6 @@ from fractal_server.app.routes.auth import current_active_user
|
|
23
23
|
from fractal_server.app.routes.auth._aux_auth import (
|
24
24
|
_verify_user_belongs_to_group,
|
25
25
|
)
|
26
|
-
from fractal_server.app.routes.aux import _raise_if_naive_datetime
|
27
26
|
from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
|
28
27
|
from fractal_server.app.schemas.v2 import TaskGroupActivityStatusV2
|
29
28
|
from fractal_server.app.schemas.v2 import TaskGroupActivityV2Read
|
@@ -43,13 +42,11 @@ async def get_task_group_activity_list(
|
|
43
42
|
pkg_name: Optional[str] = None,
|
44
43
|
status: Optional[TaskGroupActivityStatusV2] = None,
|
45
44
|
action: Optional[TaskGroupActivityActionV2] = None,
|
46
|
-
timestamp_started_min: Optional[
|
45
|
+
timestamp_started_min: Optional[AwareDatetime] = None,
|
47
46
|
user: UserOAuth = Depends(current_active_user),
|
48
47
|
db: AsyncSession = Depends(get_async_db),
|
49
48
|
) -> list[TaskGroupActivityV2Read]:
|
50
49
|
|
51
|
-
_raise_if_naive_datetime(timestamp_started_min)
|
52
|
-
|
53
50
|
stm = select(TaskGroupActivityV2).where(
|
54
51
|
TaskGroupActivityV2.user_id == user.id
|
55
52
|
)
|
@@ -70,6 +70,7 @@ def submit_workflow(
|
|
70
70
|
workflow_id: int,
|
71
71
|
dataset_id: int,
|
72
72
|
job_id: int,
|
73
|
+
user_id: int,
|
73
74
|
user_settings: UserSettings,
|
74
75
|
worker_init: Optional[str] = None,
|
75
76
|
slurm_user: Optional[str] = None,
|
@@ -90,6 +91,8 @@ def submit_workflow(
|
|
90
91
|
job_id:
|
91
92
|
Id of the job record which stores the state for the current
|
92
93
|
workflow application.
|
94
|
+
user_id:
|
95
|
+
User ID.
|
93
96
|
worker_init:
|
94
97
|
Custom executor parameters that get parsed before the execution of
|
95
98
|
each task.
|
@@ -312,6 +315,7 @@ def submit_workflow(
|
|
312
315
|
process_workflow(
|
313
316
|
workflow=workflow,
|
314
317
|
dataset=dataset,
|
318
|
+
user_id=user_id,
|
315
319
|
workflow_dir_local=WORKFLOW_DIR_LOCAL,
|
316
320
|
workflow_dir_remote=WORKFLOW_DIR_REMOTE,
|
317
321
|
logger_name=logger_name,
|
{fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/v2/_local/__init__.py
RENAMED
@@ -41,6 +41,7 @@ def process_workflow(
|
|
41
41
|
last_task_index: Optional[int] = None,
|
42
42
|
logger_name: str,
|
43
43
|
job_attribute_filters: AttributeFiltersType,
|
44
|
+
user_id: int,
|
44
45
|
# Slurm-specific
|
45
46
|
user_cache_dir: Optional[str] = None,
|
46
47
|
slurm_user: Optional[str] = None,
|
@@ -75,6 +76,7 @@ def process_workflow(
|
|
75
76
|
Positional index of the last task to execute; if `None`, proceed
|
76
77
|
until the last task.
|
77
78
|
logger_name: Logger name
|
79
|
+
user_id:
|
78
80
|
slurm_user:
|
79
81
|
Username to impersonate to run the workflow. This argument is
|
80
82
|
present for compatibility with the standard backend interface, but
|
@@ -126,4 +128,5 @@ def process_workflow(
|
|
126
128
|
logger_name=logger_name,
|
127
129
|
submit_setup_call=_local_submit_setup,
|
128
130
|
job_attribute_filters=job_attribute_filters,
|
131
|
+
user_id=user_id,
|
129
132
|
)
|
{fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/v2/_slurm_ssh/__init__.py
RENAMED
@@ -45,6 +45,7 @@ def process_workflow(
|
|
45
45
|
job_attribute_filters: AttributeFiltersType,
|
46
46
|
fractal_ssh: FractalSSH,
|
47
47
|
worker_init: Optional[str] = None,
|
48
|
+
user_id: int,
|
48
49
|
# Not used
|
49
50
|
user_cache_dir: Optional[str] = None,
|
50
51
|
slurm_user: Optional[str] = None,
|
@@ -94,4 +95,5 @@ def process_workflow(
|
|
94
95
|
logger_name=logger_name,
|
95
96
|
submit_setup_call=_slurm_submit_setup,
|
96
97
|
job_attribute_filters=job_attribute_filters,
|
98
|
+
user_id=user_id,
|
97
99
|
)
|
{fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/v2/_slurm_sudo/__init__.py
RENAMED
@@ -38,6 +38,7 @@ def process_workflow(
|
|
38
38
|
last_task_index: Optional[int] = None,
|
39
39
|
logger_name: str,
|
40
40
|
job_attribute_filters: AttributeFiltersType,
|
41
|
+
user_id: int,
|
41
42
|
# Slurm-specific
|
42
43
|
user_cache_dir: Optional[str] = None,
|
43
44
|
slurm_user: Optional[str] = None,
|
@@ -85,4 +86,5 @@ def process_workflow(
|
|
85
86
|
logger_name=logger_name,
|
86
87
|
submit_setup_call=_slurm_submit_setup,
|
87
88
|
job_attribute_filters=job_attribute_filters,
|
89
|
+
user_id=user_id,
|
88
90
|
)
|
@@ -18,6 +18,7 @@ from .runner_functions import run_v2_task_non_parallel
|
|
18
18
|
from .runner_functions import run_v2_task_parallel
|
19
19
|
from .task_interface import TaskOutput
|
20
20
|
from fractal_server.app.db import get_sync_db
|
21
|
+
from fractal_server.app.models.v2 import AccountingRecord
|
21
22
|
from fractal_server.app.models.v2 import DatasetV2
|
22
23
|
from fractal_server.app.models.v2 import WorkflowTaskV2
|
23
24
|
from fractal_server.app.schemas.v2.dataset import _DatasetHistoryItemV2
|
@@ -31,6 +32,7 @@ def execute_tasks_v2(
|
|
31
32
|
wf_task_list: list[WorkflowTaskV2],
|
32
33
|
dataset: DatasetV2,
|
33
34
|
executor: ThreadPoolExecutor,
|
35
|
+
user_id: int,
|
34
36
|
workflow_dir_local: Path,
|
35
37
|
workflow_dir_remote: Optional[Path] = None,
|
36
38
|
logger_name: Optional[str] = None,
|
@@ -88,7 +90,7 @@ def execute_tasks_v2(
|
|
88
90
|
db.commit()
|
89
91
|
# TASK EXECUTION (V2)
|
90
92
|
if task.type == "non_parallel":
|
91
|
-
current_task_output = run_v2_task_non_parallel(
|
93
|
+
current_task_output, num_tasks = run_v2_task_non_parallel(
|
92
94
|
images=filtered_images,
|
93
95
|
zarr_dir=zarr_dir,
|
94
96
|
wftask=wftask,
|
@@ -96,22 +98,20 @@ def execute_tasks_v2(
|
|
96
98
|
workflow_dir_local=workflow_dir_local,
|
97
99
|
workflow_dir_remote=workflow_dir_remote,
|
98
100
|
executor=executor,
|
99
|
-
logger_name=logger_name,
|
100
101
|
submit_setup_call=submit_setup_call,
|
101
102
|
)
|
102
103
|
elif task.type == "parallel":
|
103
|
-
current_task_output = run_v2_task_parallel(
|
104
|
+
current_task_output, num_tasks = run_v2_task_parallel(
|
104
105
|
images=filtered_images,
|
105
106
|
wftask=wftask,
|
106
107
|
task=task,
|
107
108
|
workflow_dir_local=workflow_dir_local,
|
108
109
|
workflow_dir_remote=workflow_dir_remote,
|
109
110
|
executor=executor,
|
110
|
-
logger_name=logger_name,
|
111
111
|
submit_setup_call=submit_setup_call,
|
112
112
|
)
|
113
113
|
elif task.type == "compound":
|
114
|
-
current_task_output = run_v2_task_compound(
|
114
|
+
current_task_output, num_tasks = run_v2_task_compound(
|
115
115
|
images=filtered_images,
|
116
116
|
zarr_dir=zarr_dir,
|
117
117
|
wftask=wftask,
|
@@ -119,7 +119,6 @@ def execute_tasks_v2(
|
|
119
119
|
workflow_dir_local=workflow_dir_local,
|
120
120
|
workflow_dir_remote=workflow_dir_remote,
|
121
121
|
executor=executor,
|
122
|
-
logger_name=logger_name,
|
123
122
|
submit_setup_call=submit_setup_call,
|
124
123
|
)
|
125
124
|
else:
|
@@ -144,6 +143,7 @@ def execute_tasks_v2(
|
|
144
143
|
)
|
145
144
|
|
146
145
|
# Update image list
|
146
|
+
num_new_images = 0
|
147
147
|
current_task_output.check_zarr_urls_are_unique()
|
148
148
|
for image_obj in current_task_output.image_list_updates:
|
149
149
|
image = image_obj.model_dump()
|
@@ -246,6 +246,7 @@ def execute_tasks_v2(
|
|
246
246
|
SingleImage(**new_image)
|
247
247
|
# Add image into the dataset image list
|
248
248
|
tmp_images.append(new_image)
|
249
|
+
num_new_images += 1
|
249
250
|
|
250
251
|
# Remove images from tmp_images
|
251
252
|
for img_zarr_url in current_task_output.image_list_removals:
|
@@ -281,4 +282,13 @@ def execute_tasks_v2(
|
|
281
282
|
db.merge(db_dataset)
|
282
283
|
db.commit()
|
283
284
|
|
285
|
+
# Create accounting record
|
286
|
+
record = AccountingRecord(
|
287
|
+
user_id=user_id,
|
288
|
+
num_tasks=num_tasks,
|
289
|
+
num_new_images=num_new_images,
|
290
|
+
)
|
291
|
+
db.add(record)
|
292
|
+
db.commit()
|
293
|
+
|
284
294
|
logger.debug(f'END {wftask.order}-th task (name="{task_name}")')
|
{fractal_server-2.13.0 → fractal_server-2.13.1}/fractal_server/app/runner/v2/runner_functions.py
RENAMED
@@ -115,9 +115,8 @@ def run_v2_task_non_parallel(
|
|
115
115
|
workflow_dir_local: Path,
|
116
116
|
workflow_dir_remote: Optional[Path] = None,
|
117
117
|
executor: Executor,
|
118
|
-
logger_name: Optional[str] = None,
|
119
118
|
submit_setup_call: Callable = no_op_submit_setup_call,
|
120
|
-
) -> TaskOutput:
|
119
|
+
) -> tuple[TaskOutput, int]:
|
121
120
|
"""
|
122
121
|
This runs server-side (see `executor` argument)
|
123
122
|
"""
|
@@ -154,10 +153,11 @@ def run_v2_task_non_parallel(
|
|
154
153
|
**executor_options,
|
155
154
|
)
|
156
155
|
output = future.result()
|
156
|
+
num_tasks = 1
|
157
157
|
if output is None:
|
158
|
-
return TaskOutput()
|
158
|
+
return (TaskOutput(), num_tasks)
|
159
159
|
else:
|
160
|
-
return _cast_and_validate_TaskOutput(output)
|
160
|
+
return (_cast_and_validate_TaskOutput(output), num_tasks)
|
161
161
|
|
162
162
|
|
163
163
|
def run_v2_task_parallel(
|
@@ -168,12 +168,11 @@ def run_v2_task_parallel(
|
|
168
168
|
executor: Executor,
|
169
169
|
workflow_dir_local: Path,
|
170
170
|
workflow_dir_remote: Optional[Path] = None,
|
171
|
-
logger_name: Optional[str] = None,
|
172
171
|
submit_setup_call: Callable = no_op_submit_setup_call,
|
173
|
-
) -> TaskOutput:
|
172
|
+
) -> tuple[TaskOutput, int]:
|
174
173
|
|
175
174
|
if len(images) == 0:
|
176
|
-
return TaskOutput()
|
175
|
+
return (TaskOutput(), 0)
|
177
176
|
|
178
177
|
_check_parallelization_list_size(images)
|
179
178
|
|
@@ -216,8 +215,9 @@ def run_v2_task_parallel(
|
|
216
215
|
else:
|
217
216
|
outputs[ind] = _cast_and_validate_TaskOutput(output)
|
218
217
|
|
218
|
+
num_tasks = len(images)
|
219
219
|
merged_output = merge_outputs(outputs)
|
220
|
-
return merged_output
|
220
|
+
return (merged_output, num_tasks)
|
221
221
|
|
222
222
|
|
223
223
|
def run_v2_task_compound(
|
@@ -229,7 +229,6 @@ def run_v2_task_compound(
|
|
229
229
|
executor: Executor,
|
230
230
|
workflow_dir_local: Path,
|
231
231
|
workflow_dir_remote: Optional[Path] = None,
|
232
|
-
logger_name: Optional[str] = None,
|
233
232
|
submit_setup_call: Callable = no_op_submit_setup_call,
|
234
233
|
) -> TaskOutput:
|
235
234
|
|
@@ -273,11 +272,13 @@ def run_v2_task_compound(
|
|
273
272
|
parallelization_list = init_task_output.parallelization_list
|
274
273
|
parallelization_list = deduplicate_list(parallelization_list)
|
275
274
|
|
275
|
+
num_task = 1 + len(parallelization_list)
|
276
|
+
|
276
277
|
# 3/B: parallel part of a compound task
|
277
278
|
_check_parallelization_list_size(parallelization_list)
|
278
279
|
|
279
280
|
if len(parallelization_list) == 0:
|
280
|
-
return TaskOutput()
|
281
|
+
return (TaskOutput(), 0)
|
281
282
|
|
282
283
|
list_function_kwargs = []
|
283
284
|
for ind, parallelization_item in enumerate(parallelization_list):
|
@@ -313,4 +314,4 @@ def run_v2_task_compound(
|
|
313
314
|
outputs[ind] = validated_output
|
314
315
|
|
315
316
|
merged_output = merge_outputs(outputs)
|
316
|
-
return merged_output
|
317
|
+
return (merged_output, num_task)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
from datetime import datetime
|
2
|
+
|
3
|
+
from pydantic import BaseModel
|
4
|
+
from pydantic import field_serializer
|
5
|
+
from pydantic.types import AwareDatetime
|
6
|
+
|
7
|
+
|
8
|
+
class AccountingRecordRead(BaseModel):
|
9
|
+
|
10
|
+
id: int
|
11
|
+
user_id: int
|
12
|
+
timestamp: AwareDatetime
|
13
|
+
num_tasks: int
|
14
|
+
num_new_images: int
|
15
|
+
|
16
|
+
@field_serializer("timestamp")
|
17
|
+
def serialize_datetime(v: datetime) -> str:
|
18
|
+
return v.isoformat()
|