diracx 0.0.1a47__tar.gz → 0.0.1a48__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.
- {diracx-0.0.1a47 → diracx-0.0.1a48}/PKG-INFO +1 -1
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/job/db.py +13 -21
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/job/schema.py +42 -15
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/pilot_agents/schema.py +5 -4
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/utils/base.py +2 -1
- diracx-0.0.1a48/diracx-db/src/diracx/db/sql/utils/types.py +137 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/tests/jobs/test_job_db.py +53 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/factory.py +2 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/tests/jobs/test_query.py +20 -2
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/tests/jobs/test_status.py +6 -12
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/explanations/components/db.md +8 -0
- diracx-0.0.1a48/release.notes.new +9 -0
- diracx-0.0.1a47/diracx-db/src/diracx/db/sql/utils/types.py +0 -43
- diracx-0.0.1a47/release.notes.new +0 -31
- {diracx-0.0.1a47 → diracx-0.0.1a48}/.coveragerc +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/.gitattributes +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/.github/dependabot.yml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/.github/workflows/deployment.yml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/.github/workflows/integration.yml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/.github/workflows/main.yml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/.github/workflows/make_release.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/.github/workflows/update_security_txt_expiry.yml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/.github/workflows/vulnerabilities.yml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/.gitignore +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/.pre-commit-config.yaml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/.readthedocs.yaml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/LICENSE +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/MANIFEST.in +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/README.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/codecov.yml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/containers/client/Dockerfile +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/containers/services/Dockerfile +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-api/README.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-api/pyproject.toml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-api/src/diracx/api/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-api/src/diracx/api/jobs.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-api/src/diracx/api/py.typed +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-api/src/diracx/api/utils.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-api/tests/test_jobs.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-api/tests/test_utils.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-cli/README.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-cli/pyproject.toml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-cli/src/diracx/cli/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-cli/src/diracx/cli/__main__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-cli/src/diracx/cli/auth.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-cli/src/diracx/cli/config.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-cli/src/diracx/cli/internal/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-cli/src/diracx/cli/internal/config.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-cli/src/diracx/cli/internal/legacy.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-cli/src/diracx/cli/jobs.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-cli/src/diracx/cli/py.typed +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-cli/src/diracx/cli/utils.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-cli/tests/legacy/cs_sync/integration_test.cfg +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-cli/tests/legacy/cs_sync/integration_test.yaml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-cli/tests/legacy/cs_sync/integration_test_buggy.cfg +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-cli/tests/legacy/cs_sync/integration_test_secret.cfg +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-cli/tests/legacy/cs_sync/test_cssync.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-cli/tests/legacy/test_legacy.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-cli/tests/test_internal.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-cli/tests/test_jobs.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-cli/tests/test_login.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/README.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/pyproject.toml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/_diracx_client_importer.pth +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/_client_importer.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/_client.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/_configuration.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/_patch.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/_serialization.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/_utils/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/_utils/serialization.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/_utils/utils.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/_vendor.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/aio/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/aio/_client.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/aio/_configuration.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/aio/_patch.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/aio/_vendor.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/aio/operations/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/aio/operations/_operations.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/aio/operations/_patch.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/models/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/models/_enums.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/models/_models.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/models/_patch.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/operations/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/operations/_operations.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/operations/_patch.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/_generated/py.typed +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/aio.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/models.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/patches/auth/aio.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/patches/auth/common.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/patches/auth/sync.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/patches/client/aio.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/patches/client/common.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/patches/client/sync.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/patches/jobs/aio.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/patches/jobs/common.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/patches/jobs/sync.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/patches/utils.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/py.typed +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/src/diracx/client/sync.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-client/tests/test_auth.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-core/README.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-core/pyproject.toml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-core/src/diracx/core/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-core/src/diracx/core/config/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-core/src/diracx/core/config/schema.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-core/src/diracx/core/config/sources.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-core/src/diracx/core/exceptions.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-core/src/diracx/core/extensions.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-core/src/diracx/core/models.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-core/src/diracx/core/preferences.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-core/src/diracx/core/properties.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-core/src/diracx/core/py.typed +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-core/src/diracx/core/s3.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-core/src/diracx/core/settings.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-core/src/diracx/core/utils.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-core/tests/test_config_source.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-core/tests/test_extensions.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-core/tests/test_s3.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-core/tests/test_secrets.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-core/tests/test_utils.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/README.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/pyproject.toml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/__main__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/exceptions.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/os/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/os/job_parameters.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/os/utils.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/py.typed +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/auth/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/auth/db.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/auth/schema.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/dummy/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/dummy/db.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/dummy/schema.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/job/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/job_logging/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/job_logging/db.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/job_logging/schema.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/pilot_agents/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/pilot_agents/db.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/sandbox_metadata/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/sandbox_metadata/db.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/sandbox_metadata/schema.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/task_queue/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/task_queue/db.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/task_queue/schema.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/utils/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/src/diracx/db/sql/utils/functions.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/tests/auth/test_authorization_flow.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/tests/auth/test_device_flow.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/tests/auth/test_refresh_token.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/tests/jobs/test_job_logging_db.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/tests/jobs/test_sandbox_metadata.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/tests/opensearch/test_connection.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/tests/opensearch/test_index_template.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/tests/opensearch/test_search.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/tests/pilot_agents/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/tests/pilot_agents/test_pilot_agents_db.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/tests/test_dummy_db.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-db/tests/test_freeze_time.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-logic/README.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-logic/pyproject.toml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-logic/src/diracx/logic/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-logic/src/diracx/logic/__main__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-logic/src/diracx/logic/auth/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-logic/src/diracx/logic/auth/authorize_code_flow.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-logic/src/diracx/logic/auth/device_flow.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-logic/src/diracx/logic/auth/management.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-logic/src/diracx/logic/auth/token.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-logic/src/diracx/logic/auth/utils.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-logic/src/diracx/logic/auth/well_known.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-logic/src/diracx/logic/jobs/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-logic/src/diracx/logic/jobs/query.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-logic/src/diracx/logic/jobs/sandboxes.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-logic/src/diracx/logic/jobs/status.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-logic/src/diracx/logic/jobs/submission.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-logic/src/diracx/logic/jobs/utils.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-logic/src/diracx/logic/py.typed +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-logic/src/diracx/logic/task_queues/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-logic/src/diracx/logic/task_queues/priority.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-logic/tests/logic/test_sandboxes.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/README.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/pyproject.toml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/access_policies.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/auth/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/auth/authorize_code_flow.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/auth/device_flow.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/auth/management.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/auth/token.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/auth/utils.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/auth/well_known.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/configuration.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/dependencies.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/fastapi_classes.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/health/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/health/probes.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/jobs/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/jobs/access_policies.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/jobs/legacy.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/jobs/query.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/jobs/sandboxes.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/jobs/status.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/jobs/submission.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/otel.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/py.typed +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/utils/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/src/diracx/routers/utils/users.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/tests/auth/test_legacy_exchange.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/tests/auth/test_standard.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/tests/data/idp-server.invalid/.well-known/openid-configuration +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/tests/health/test_probes.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/tests/jobs/conftest.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/tests/jobs/test_sandboxes.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/tests/jobs/test_wms_access_policy.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/tests/test_config_manager.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/tests/test_generic.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-routers/tests/test_policy.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-testing/README.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-testing/pyproject.toml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-testing/src/diracx/testing/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-testing/src/diracx/testing/__main__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-testing/src/diracx/testing/client_generation.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-testing/src/diracx/testing/client_generation_pytest.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-testing/src/diracx/testing/dummy_osdb.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-testing/src/diracx/testing/entrypoints.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-testing/src/diracx/testing/mock_osdb.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-testing/src/diracx/testing/osdb.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-testing/src/diracx/testing/routers.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-testing/src/diracx/testing/scripts/collect_demo_coverage.sh +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-testing/src/diracx/testing/time.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/diracx-testing/src/diracx/testing/utils.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/SECURITY.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/admin/explanations/authentication.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/admin/explanations/configuration.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/admin/explanations/opentelemetry.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/admin/explanations/sandbox-store.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/admin/explanations/user-management.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/admin/how-to/install/convert-cs.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/admin/how-to/install/register-a-vo.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/admin/how-to/install/register-the-admin-vo.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/admin/index.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/admin/reference/security_model.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/admin/reference/settings-and-preferences.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/admin/tutorials/authentication.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/assets/css/extra.css +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/assets/css/fontawesome-all.min.css +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/assets/css/images/arrow.svg +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/assets/css/images/overlay.png +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/assets/css/main.css +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/assets/home.html +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/assets/images/diracx-logo-square.svg +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/assets/images/logo.svg +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/assets/images/pic02.jpg +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/assets/styles/extra.css +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/assets/webfonts/fa-brands-400.woff2 +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/assets/webfonts/fa-solid-900.woff +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/explanations/client.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/explanations/components/api.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/explanations/components/cli.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/explanations/components/index.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/explanations/components/routes.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/explanations/designing-functionality.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/explanations/extensions.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/explanations/index.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/explanations/testing.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/how-to/add-a-cli-command.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/how-to/add-a-db.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/how-to/add-a-route.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/how-to/add-a-setting.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/how-to/add-a-task.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/how-to/add-a-test.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/how-to/add-functionality/index.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/how-to/client-customization.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/how-to/client-extension.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/how-to/client-generation.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/how-to/contribute.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/how-to/develop-legacy-dirac.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/how-to/extend-diracx/index.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/how-to/use-the-demo/index.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/how-to/use-the-demo/swagger.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/how-to/use-the-demo/web.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/index.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/reference/application-state.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/reference/client-metapathfinder.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/reference/coding-conventions.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/reference/configuration.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/reference/db-transaction-model.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/reference/dependency-injection.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/reference/entrypoints.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/reference/pixi-tasks.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/reference/security-policies.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/reference/security-properties.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/reference/test-recipes.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/reference/writing-tests.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/tutorials/advanced-tutorial.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/tutorials/develop-web.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/tutorials/getting-started.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/tutorials/larger-developments.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/tutorials/making-changes.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/tutorials/play-with-auth.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/tutorials/run-locally.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/dev/tutorials/write-docs.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/index.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/overrides/main.html +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/roadmap.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/user/index.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/user/reference/client-configuration.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/user/reference/known-installations.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/user/reference/programmatic-usage/command-line-interface.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/user/reference/programmatic-usage/https-interface.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/user/reference/programmatic-usage/index.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/user/reference/programmatic-usage/python-interface.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/docs/user/tutorials/getting-started.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/containers/client/Dockerfile +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/containers/services/Dockerfile +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/.github/workflows/main.yml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/README.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-api/README.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-api/pyproject.toml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-api/src/gubbins/api/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-api/src/gubbins/api/py.typed +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-api/tests/test_gubbins_api.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-cli/README.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-cli/pyproject.toml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-cli/src/gubbins/cli/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-cli/src/gubbins/cli/config.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-cli/src/gubbins/cli/lollygag.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-cli/src/gubbins/cli/py.typed +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-cli/tests/test_gubbins_cli.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/README.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/pyproject.toml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/_client.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/_configuration.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/_patch.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/_serialization.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/_utils/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/_utils/serialization.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/_utils/utils.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/_vendor.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/aio/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/aio/_client.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/aio/_configuration.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/aio/_patch.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/aio/_vendor.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/aio/operations/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/aio/operations/_operations.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/aio/operations/_patch.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/models/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/models/_enums.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/models/_models.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/operations/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/operations/_operations.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/operations/_patch.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/_generated/py.typed +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/aio.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/models.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/py.typed +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/src/gubbins/client/sync.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-client/tests/test_gubbins_client.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-core/README.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-core/pyproject.toml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-core/src/gubbins/core/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-core/src/gubbins/core/config/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-core/src/gubbins/core/config/schema.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-core/src/gubbins/core/models.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-core/src/gubbins/core/properties.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-core/src/gubbins/core/py.typed +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-core/tests/test_config.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-core/tests/test_properties.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-db/README.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-db/pyproject.toml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-db/src/gubbins/db/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-db/src/gubbins/db/py.typed +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-db/src/gubbins/db/sql/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-db/src/gubbins/db/sql/jobs/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-db/src/gubbins/db/sql/jobs/db.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-db/src/gubbins/db/sql/jobs/schema.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-db/src/gubbins/db/sql/lollygag/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-db/src/gubbins/db/sql/lollygag/db.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-db/src/gubbins/db/sql/lollygag/schema.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-db/tests/test_gubbins_job_db.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-db/tests/test_lollygag_db.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-logic/README.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-logic/pyproject.toml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-logic/src/gubbins/logic/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-logic/src/gubbins/logic/auth/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-logic/src/gubbins/logic/auth/well_known.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-logic/src/gubbins/logic/lollygag/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-logic/src/gubbins/logic/lollygag/lollygag.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-logic/src/gubbins/logic/py.typed +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-logic/tests/test_gubbins_logic.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-routers/README.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-routers/pyproject.toml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-routers/src/gubbins/routers/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-routers/src/gubbins/routers/dependencies.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-routers/src/gubbins/routers/lollygag/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-routers/src/gubbins/routers/lollygag/access_policy.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-routers/src/gubbins/routers/lollygag/lollygag.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-routers/src/gubbins/routers/py.typed +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-routers/src/gubbins/routers/well_known.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-routers/tests/data/idp-server.invalid/.well-known/openid-configuration +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-routers/tests/test_gubbins_job_manager.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-routers/tests/test_lollybag.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-routers/tests/test_wellknown.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-testing/README.md +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-testing/pyproject.toml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/gubbins-testing/src/gubbins/testing/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/pixi.toml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/pyproject.toml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins/release.notes +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/extensions/gubbins_values.yaml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/mkdocs.yml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/pixi.toml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/pyproject.toml +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/release.notes +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/release.notes.old +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/run_local.sh +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/tests/__init__.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/tests/make_token_local.py +0 -0
- {diracx-0.0.1a47 → diracx-0.0.1a48}/tests/test_generic.py +0 -0
|
@@ -5,10 +5,11 @@ __all__ = ["JobDB"]
|
|
|
5
5
|
from datetime import datetime, timezone
|
|
6
6
|
from typing import TYPE_CHECKING, Any, Iterable
|
|
7
7
|
|
|
8
|
-
from sqlalchemy import bindparam, case, delete,
|
|
8
|
+
from sqlalchemy import bindparam, case, delete, literal, select, update
|
|
9
9
|
|
|
10
10
|
if TYPE_CHECKING:
|
|
11
11
|
from sqlalchemy.sql.elements import BindParameter
|
|
12
|
+
from sqlalchemy.sql import expression
|
|
12
13
|
|
|
13
14
|
from diracx.core.exceptions import InvalidQueryError
|
|
14
15
|
from diracx.core.models import JobCommand, SearchSpec, SortSpec
|
|
@@ -128,27 +129,14 @@ class JobDB(BaseSQLDB):
|
|
|
128
129
|
],
|
|
129
130
|
)
|
|
130
131
|
|
|
131
|
-
@staticmethod
|
|
132
|
-
def _set_job_attributes_fix_value(column, value):
|
|
133
|
-
"""Apply corrections to the values before inserting them into the database.
|
|
134
|
-
|
|
135
|
-
TODO: Move this logic into the sqlalchemy model.
|
|
136
|
-
"""
|
|
137
|
-
if column == "VerifiedFlag":
|
|
138
|
-
value_str = str(value)
|
|
139
|
-
if value_str in ("True", "False"):
|
|
140
|
-
return value_str
|
|
141
|
-
if column == "AccountedFlag":
|
|
142
|
-
value_str = str(value)
|
|
143
|
-
if value_str in ("True", "False", "Failed"):
|
|
144
|
-
return value_str
|
|
145
|
-
else:
|
|
146
|
-
return value
|
|
147
|
-
raise NotImplementedError(f"Unrecognized value for column {column}: {value}")
|
|
148
|
-
|
|
149
132
|
async def set_job_attributes(self, job_data):
|
|
150
133
|
"""Update the parameters of the given jobs."""
|
|
151
134
|
# TODO: add myDate and force parameters.
|
|
135
|
+
|
|
136
|
+
if not job_data:
|
|
137
|
+
# nothing to do!
|
|
138
|
+
raise ValueError("job_data is empty")
|
|
139
|
+
|
|
152
140
|
for job_id in job_data.keys():
|
|
153
141
|
if "Status" in job_data[job_id]:
|
|
154
142
|
job_data[job_id].update(
|
|
@@ -160,7 +148,11 @@ class JobDB(BaseSQLDB):
|
|
|
160
148
|
*[
|
|
161
149
|
(
|
|
162
150
|
Jobs.__table__.c.JobID == job_id,
|
|
163
|
-
|
|
151
|
+
# Since the setting of the new column value is obscured by the CASE statement,
|
|
152
|
+
# ensure that SQLAlchemy renders the new column value with the correct type
|
|
153
|
+
literal(attrs[column], type_=Jobs.__table__.c[column].type)
|
|
154
|
+
if not isinstance(attrs[column], expression.FunctionElement)
|
|
155
|
+
else attrs[column],
|
|
164
156
|
)
|
|
165
157
|
for job_id, attrs in job_data.items()
|
|
166
158
|
if column in attrs
|
|
@@ -193,7 +185,7 @@ class JobDB(BaseSQLDB):
|
|
|
193
185
|
async def set_job_commands(self, commands: list[tuple[int, str, str]]) -> None:
|
|
194
186
|
"""Store a command to be passed to the job together with the next heart beat."""
|
|
195
187
|
await self.conn.execute(
|
|
196
|
-
insert(
|
|
188
|
+
JobCommands.__table__.insert(),
|
|
197
189
|
[
|
|
198
190
|
{
|
|
199
191
|
"JobID": job_id,
|
|
@@ -2,7 +2,6 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import sqlalchemy.types as types
|
|
4
4
|
from sqlalchemy import (
|
|
5
|
-
DateTime,
|
|
6
5
|
ForeignKey,
|
|
7
6
|
Index,
|
|
8
7
|
Integer,
|
|
@@ -11,6 +10,8 @@ from sqlalchemy import (
|
|
|
11
10
|
)
|
|
12
11
|
from sqlalchemy.orm import declarative_base
|
|
13
12
|
|
|
13
|
+
from diracx.db.sql.utils.types import SmarterDateTime
|
|
14
|
+
|
|
14
15
|
from ..utils import Column, EnumBackedBool, NullColumn
|
|
15
16
|
|
|
16
17
|
JobDBBase = declarative_base()
|
|
@@ -19,11 +20,8 @@ JobDBBase = declarative_base()
|
|
|
19
20
|
class AccountedFlagEnum(types.TypeDecorator):
|
|
20
21
|
"""Maps a ``AccountedFlagEnum()`` column to True/False in Python."""
|
|
21
22
|
|
|
22
|
-
impl = types.Enum
|
|
23
|
-
cache_ok
|
|
24
|
-
|
|
25
|
-
def __init__(self) -> None:
|
|
26
|
-
super().__init__("True", "False", "Failed")
|
|
23
|
+
impl = types.Enum("True", "False", "Failed", name="accounted_flag_enum")
|
|
24
|
+
cache_ok = True
|
|
27
25
|
|
|
28
26
|
def process_bind_param(self, value, dialect) -> str:
|
|
29
27
|
if value is True:
|
|
@@ -63,12 +61,30 @@ class Jobs(JobDBBase):
|
|
|
63
61
|
owner = Column("Owner", String(64), default="Unknown")
|
|
64
62
|
owner_group = Column("OwnerGroup", String(128), default="Unknown")
|
|
65
63
|
vo = Column("VO", String(32))
|
|
66
|
-
submission_time = NullColumn(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
submission_time = NullColumn(
|
|
65
|
+
"SubmissionTime",
|
|
66
|
+
SmarterDateTime(),
|
|
67
|
+
)
|
|
68
|
+
reschedule_time = NullColumn(
|
|
69
|
+
"RescheduleTime",
|
|
70
|
+
SmarterDateTime(),
|
|
71
|
+
)
|
|
72
|
+
last_update_time = NullColumn(
|
|
73
|
+
"LastUpdateTime",
|
|
74
|
+
SmarterDateTime(),
|
|
75
|
+
)
|
|
76
|
+
start_exec_time = NullColumn(
|
|
77
|
+
"StartExecTime",
|
|
78
|
+
SmarterDateTime(),
|
|
79
|
+
)
|
|
80
|
+
heart_beat_time = NullColumn(
|
|
81
|
+
"HeartBeatTime",
|
|
82
|
+
SmarterDateTime(),
|
|
83
|
+
)
|
|
84
|
+
end_exec_time = NullColumn(
|
|
85
|
+
"EndExecTime",
|
|
86
|
+
SmarterDateTime(),
|
|
87
|
+
)
|
|
72
88
|
status = Column("Status", String(32), default="Received")
|
|
73
89
|
minor_status = Column("MinorStatus", String(128), default="Unknown")
|
|
74
90
|
application_status = Column("ApplicationStatus", String(255), default="Unknown")
|
|
@@ -143,7 +159,11 @@ class HeartBeatLoggingInfo(JobDBBase):
|
|
|
143
159
|
)
|
|
144
160
|
name = Column("Name", String(100), primary_key=True)
|
|
145
161
|
value = Column("Value", Text)
|
|
146
|
-
heart_beat_time = Column(
|
|
162
|
+
heart_beat_time = Column(
|
|
163
|
+
"HeartBeatTime",
|
|
164
|
+
SmarterDateTime(),
|
|
165
|
+
primary_key=True,
|
|
166
|
+
)
|
|
147
167
|
|
|
148
168
|
|
|
149
169
|
class JobCommands(JobDBBase):
|
|
@@ -154,5 +174,12 @@ class JobCommands(JobDBBase):
|
|
|
154
174
|
command = Column("Command", String(100))
|
|
155
175
|
arguments = Column("Arguments", String(100))
|
|
156
176
|
status = Column("Status", String(64), default="Received")
|
|
157
|
-
reception_time = Column(
|
|
158
|
-
|
|
177
|
+
reception_time = Column(
|
|
178
|
+
"ReceptionTime",
|
|
179
|
+
SmarterDateTime(),
|
|
180
|
+
primary_key=True,
|
|
181
|
+
)
|
|
182
|
+
execution_time = NullColumn(
|
|
183
|
+
"ExecutionTime",
|
|
184
|
+
SmarterDateTime(),
|
|
185
|
+
)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from sqlalchemy import (
|
|
4
|
-
DateTime,
|
|
5
4
|
Double,
|
|
6
5
|
Index,
|
|
7
6
|
Integer,
|
|
@@ -10,6 +9,8 @@ from sqlalchemy import (
|
|
|
10
9
|
)
|
|
11
10
|
from sqlalchemy.orm import declarative_base
|
|
12
11
|
|
|
12
|
+
from diracx.db.sql.utils.types import SmarterDateTime
|
|
13
|
+
|
|
13
14
|
from ..utils import Column, EnumBackedBool, NullColumn
|
|
14
15
|
|
|
15
16
|
PilotAgentsDBBase = declarative_base()
|
|
@@ -29,8 +30,8 @@ class PilotAgents(PilotAgentsDBBase):
|
|
|
29
30
|
vo = Column("VO", String(128))
|
|
30
31
|
grid_type = Column("GridType", String(32), default="LCG")
|
|
31
32
|
benchmark = Column("BenchMark", Double, default=0.0)
|
|
32
|
-
submission_time = NullColumn("SubmissionTime",
|
|
33
|
-
last_update_time = NullColumn("LastUpdateTime",
|
|
33
|
+
submission_time = NullColumn("SubmissionTime", SmarterDateTime)
|
|
34
|
+
last_update_time = NullColumn("LastUpdateTime", SmarterDateTime)
|
|
34
35
|
status = Column("Status", String(32), default="Unknown")
|
|
35
36
|
status_reason = Column("StatusReason", String(255), default="Unknown")
|
|
36
37
|
accounting_sent = Column("AccountingSent", EnumBackedBool(), default=False)
|
|
@@ -47,7 +48,7 @@ class JobToPilotMapping(PilotAgentsDBBase):
|
|
|
47
48
|
|
|
48
49
|
pilot_id = Column("PilotID", Integer, primary_key=True)
|
|
49
50
|
job_id = Column("JobID", Integer, primary_key=True)
|
|
50
|
-
start_time = Column("StartTime",
|
|
51
|
+
start_time = Column("StartTime", SmarterDateTime)
|
|
51
52
|
|
|
52
53
|
__table_args__ = (Index("JobID", "JobID"), Index("PilotID", "PilotID"))
|
|
53
54
|
|
|
@@ -24,6 +24,7 @@ from diracx.core.models import (
|
|
|
24
24
|
)
|
|
25
25
|
from diracx.core.settings import SqlalchemyDsn
|
|
26
26
|
from diracx.db.exceptions import DBUnavailableError
|
|
27
|
+
from diracx.db.sql.utils.types import SmarterDateTime
|
|
27
28
|
|
|
28
29
|
from .functions import date_trunc
|
|
29
30
|
|
|
@@ -345,7 +346,7 @@ def apply_search_filters(column_mapping, stmt, search):
|
|
|
345
346
|
except KeyError as e:
|
|
346
347
|
raise InvalidQueryError(f"Unknown column {query['parameter']}") from e
|
|
347
348
|
|
|
348
|
-
if isinstance(column.type, DateTime):
|
|
349
|
+
if isinstance(column.type, (DateTime, SmarterDateTime)):
|
|
349
350
|
if "value" in query and isinstance(query["value"], str):
|
|
350
351
|
resolution, value = find_time_resolution(query["value"])
|
|
351
352
|
if resolution:
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from functools import partial
|
|
5
|
+
from zoneinfo import ZoneInfo
|
|
6
|
+
|
|
7
|
+
import sqlalchemy.types as types
|
|
8
|
+
from sqlalchemy import Column as RawColumn
|
|
9
|
+
from sqlalchemy import DateTime, Enum
|
|
10
|
+
|
|
11
|
+
from .functions import utcnow
|
|
12
|
+
|
|
13
|
+
Column: partial[RawColumn] = partial(RawColumn, nullable=False)
|
|
14
|
+
NullColumn: partial[RawColumn] = partial(RawColumn, nullable=True)
|
|
15
|
+
DateNowColumn = partial(Column, type_=DateTime(timezone=True), server_default=utcnow())
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def EnumColumn(name, enum_type, **kwargs): # noqa: N802
|
|
19
|
+
return Column(name, Enum(enum_type, native_enum=False, length=16), **kwargs)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class EnumBackedBool(types.TypeDecorator):
|
|
23
|
+
"""Maps a ``EnumBackedBool()`` column to True/False in Python."""
|
|
24
|
+
|
|
25
|
+
impl = types.Enum("True", "False", name="enum_backed_bool")
|
|
26
|
+
cache_ok = True
|
|
27
|
+
|
|
28
|
+
def process_bind_param(self, value, dialect) -> str:
|
|
29
|
+
if value is True:
|
|
30
|
+
return "True"
|
|
31
|
+
elif value is False:
|
|
32
|
+
return "False"
|
|
33
|
+
else:
|
|
34
|
+
raise NotImplementedError(value, dialect)
|
|
35
|
+
|
|
36
|
+
def process_result_value(self, value, dialect) -> bool:
|
|
37
|
+
if value == "True":
|
|
38
|
+
return True
|
|
39
|
+
elif value == "False":
|
|
40
|
+
return False
|
|
41
|
+
else:
|
|
42
|
+
raise NotImplementedError(f"Unknown {value=}")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class SmarterDateTime(types.TypeDecorator):
|
|
46
|
+
"""A DateTime type that also accepts ISO8601 strings.
|
|
47
|
+
|
|
48
|
+
Takes into account converting timezone aware datetime objects into
|
|
49
|
+
naive form and back when needed.
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
impl = DateTime()
|
|
54
|
+
cache_ok = True
|
|
55
|
+
|
|
56
|
+
def __init__(
|
|
57
|
+
self,
|
|
58
|
+
stored_tz: ZoneInfo | None = ZoneInfo("UTC"),
|
|
59
|
+
returned_tz: ZoneInfo = ZoneInfo("UTC"),
|
|
60
|
+
stored_naive_sqlite=True,
|
|
61
|
+
stored_naive_mysql=True,
|
|
62
|
+
stored_naive_postgres=False, # Forces timezone-awareness
|
|
63
|
+
):
|
|
64
|
+
self._stored_naive_dialect = {
|
|
65
|
+
"sqlite": stored_naive_sqlite,
|
|
66
|
+
"mysql": stored_naive_mysql,
|
|
67
|
+
"postgres": stored_naive_postgres,
|
|
68
|
+
}
|
|
69
|
+
self._stored_tz: ZoneInfo | None = stored_tz # None = Local timezone
|
|
70
|
+
self._returned_tz: ZoneInfo = returned_tz
|
|
71
|
+
|
|
72
|
+
def _stored_naive(self, dialect):
|
|
73
|
+
if dialect.name not in self._stored_naive_dialect:
|
|
74
|
+
raise NotImplementedError(dialect.name)
|
|
75
|
+
return self._stored_naive_dialect.get(dialect.name)
|
|
76
|
+
|
|
77
|
+
def process_bind_param(self, value, dialect):
|
|
78
|
+
if value is None:
|
|
79
|
+
return None
|
|
80
|
+
|
|
81
|
+
if isinstance(value, str):
|
|
82
|
+
try:
|
|
83
|
+
value: datetime = datetime.fromisoformat(value)
|
|
84
|
+
except ValueError as err:
|
|
85
|
+
raise ValueError(f"Unable to parse datetime string: {value}") from err
|
|
86
|
+
|
|
87
|
+
if not isinstance(value, datetime):
|
|
88
|
+
raise ValueError(f"Expected datetime or ISO8601 string, but got {value!r}")
|
|
89
|
+
|
|
90
|
+
if not value.tzinfo:
|
|
91
|
+
raise ValueError(
|
|
92
|
+
f"Provided timestamp {value=} has no tzinfo -"
|
|
93
|
+
" this is problematic and may cause inconsistencies in stored timestamps.\n"
|
|
94
|
+
" Please always work with tz-aware datetimes / attach tzinfo to your datetime objects:"
|
|
95
|
+
" e.g. datetime.now(tz=timezone.utc) or use datetime_obj.astimezone() with no arguments if you need to "
|
|
96
|
+
"attach the local timezone to a local naive timestamp."
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Check that we need to convert the timezone to match self._stored_tz timezone:
|
|
100
|
+
if self._stored_naive(dialect):
|
|
101
|
+
# if self._stored_tz is None, we use our local/system timezone.
|
|
102
|
+
stored_tz = self._stored_tz
|
|
103
|
+
|
|
104
|
+
# astimezone converts to the stored timezone (local timezone if None)
|
|
105
|
+
# replace strips the TZ info --> naive datetime object
|
|
106
|
+
value = value.astimezone(tz=stored_tz).replace(tzinfo=None)
|
|
107
|
+
|
|
108
|
+
return value
|
|
109
|
+
|
|
110
|
+
def process_result_value(self, value, dialect):
|
|
111
|
+
if value is None:
|
|
112
|
+
return None
|
|
113
|
+
if not isinstance(value, datetime):
|
|
114
|
+
raise NotImplementedError(f"{value=} not a datetime object")
|
|
115
|
+
|
|
116
|
+
if self._stored_naive(dialect):
|
|
117
|
+
# Here we add back the tzinfo to the naive timestamp
|
|
118
|
+
# from the DB to make it aware again.
|
|
119
|
+
if value.tzinfo is None:
|
|
120
|
+
# we are definitely given a naive timestamp, so handle it.
|
|
121
|
+
# add back the timezone info if stored_tz is set
|
|
122
|
+
if self._stored_tz:
|
|
123
|
+
value = value.replace(tzinfo=self._stored_tz)
|
|
124
|
+
else:
|
|
125
|
+
# if stored as a local time, add back the system timezone info...
|
|
126
|
+
value = value.astimezone()
|
|
127
|
+
else:
|
|
128
|
+
raise ValueError(
|
|
129
|
+
f"stored_naive is True for {dialect.name=}, but the database engine returned "
|
|
130
|
+
"a tz-aware datetime. You need to check the SQLAlchemy model is consistent with the DB schema."
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# finally, convert the datetime according to the "returned_tz"
|
|
134
|
+
value = value.astimezone(self._returned_tz)
|
|
135
|
+
|
|
136
|
+
# phew...
|
|
137
|
+
return value
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from zoneinfo import ZoneInfo
|
|
5
|
+
|
|
3
6
|
import pytest
|
|
7
|
+
import sqlalchemy
|
|
4
8
|
from sqlalchemy.exc import IntegrityError
|
|
5
9
|
|
|
6
10
|
from diracx.core.exceptions import InvalidQueryError
|
|
@@ -47,6 +51,55 @@ async def populated_job_db(job_db):
|
|
|
47
51
|
yield job_db
|
|
48
52
|
|
|
49
53
|
|
|
54
|
+
async def test_bad_naive_datetime_used(populated_job_db):
|
|
55
|
+
async with populated_job_db as db:
|
|
56
|
+
compressed_jdl = "CompressedJDL0001111BadJob"
|
|
57
|
+
job_id = await db.create_job(compressed_jdl)
|
|
58
|
+
jobs_to_insert = {}
|
|
59
|
+
jobs_to_insert[job_id] = {
|
|
60
|
+
"JobID": job_id,
|
|
61
|
+
"Status": "New",
|
|
62
|
+
"Owner": "owner0101010101",
|
|
63
|
+
"OwnerGroup": "owner_group1",
|
|
64
|
+
"VO": "lhcb",
|
|
65
|
+
"HeartBeatTime": datetime.now().replace(tzinfo=None), # noqa
|
|
66
|
+
}
|
|
67
|
+
with pytest.raises(sqlalchemy.exc.StatementError):
|
|
68
|
+
await db.insert_job_attributes(jobs_to_insert) # should complain
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
async def test_timezone_converted_back_to_utc(populated_job_db):
|
|
72
|
+
current_utc_dt = datetime.now(tz=ZoneInfo("UTC"))
|
|
73
|
+
|
|
74
|
+
async with populated_job_db as db:
|
|
75
|
+
compressed_jdl = "CompressedJDL0001111BadJob"
|
|
76
|
+
job_id = await db.create_job(compressed_jdl)
|
|
77
|
+
jobs_to_insert = {}
|
|
78
|
+
jobs_to_insert[job_id] = {
|
|
79
|
+
"JobID": job_id,
|
|
80
|
+
"Status": "New",
|
|
81
|
+
"Owner": "owner0101010101",
|
|
82
|
+
"OwnerGroup": "owner_group1",
|
|
83
|
+
"VO": "lhcb",
|
|
84
|
+
"HeartBeatTime": current_utc_dt.astimezone(ZoneInfo("Asia/Tokyo")),
|
|
85
|
+
}
|
|
86
|
+
await db.insert_job_attributes(jobs_to_insert)
|
|
87
|
+
|
|
88
|
+
total, result = await db.search(
|
|
89
|
+
["JobID", "HeartBeatTime"],
|
|
90
|
+
[
|
|
91
|
+
ScalarSearchSpec(
|
|
92
|
+
parameter="JobID",
|
|
93
|
+
operator=ScalarSearchOperator.EQUAL,
|
|
94
|
+
value=int(job_id),
|
|
95
|
+
)
|
|
96
|
+
],
|
|
97
|
+
[],
|
|
98
|
+
)
|
|
99
|
+
assert total == 1
|
|
100
|
+
assert result[0]["HeartBeatTime"] == current_utc_dt, result
|
|
101
|
+
|
|
102
|
+
|
|
50
103
|
async def test_search_parameters(populated_job_db):
|
|
51
104
|
"""Test that we can search specific parameters for jobs in the database."""
|
|
52
105
|
async with populated_job_db as job_db:
|
|
@@ -192,7 +192,21 @@ def test_insert_malformed_jdl(normal_user_client):
|
|
|
192
192
|
assert r.status_code == 400, r.json()
|
|
193
193
|
|
|
194
194
|
|
|
195
|
-
|
|
195
|
+
# "2024-01-01 00:00:00.123456"
|
|
196
|
+
@freeze_time(
|
|
197
|
+
datetime(
|
|
198
|
+
year=2024,
|
|
199
|
+
month=1,
|
|
200
|
+
day=1,
|
|
201
|
+
hour=0,
|
|
202
|
+
minute=0,
|
|
203
|
+
second=0,
|
|
204
|
+
microsecond=123456,
|
|
205
|
+
# astimezone call below adds the local tz
|
|
206
|
+
# to this otherwise naive object
|
|
207
|
+
tzinfo=None,
|
|
208
|
+
).astimezone()
|
|
209
|
+
)
|
|
196
210
|
def test_insert_and_search_by_datetime(normal_user_client):
|
|
197
211
|
"""Test inserting a job and then searching for it.
|
|
198
212
|
|
|
@@ -204,6 +218,10 @@ def test_insert_and_search_by_datetime(normal_user_client):
|
|
|
204
218
|
listed_jobs = r.json()
|
|
205
219
|
assert r.status_code == 200, listed_jobs
|
|
206
220
|
assert len(listed_jobs) == len(job_definitions)
|
|
221
|
+
r = normal_user_client.post("/api/jobs/search")
|
|
222
|
+
assert len(r.json()) == 1, "No jobs submitted"
|
|
223
|
+
|
|
224
|
+
submitted_jobs_info = r.json()
|
|
207
225
|
|
|
208
226
|
# 1.1 Search for all jobs submitted in 2024
|
|
209
227
|
r = normal_user_client.post(
|
|
@@ -219,7 +237,7 @@ def test_insert_and_search_by_datetime(normal_user_client):
|
|
|
219
237
|
},
|
|
220
238
|
)
|
|
221
239
|
assert r.status_code == 200, r.json()
|
|
222
|
-
assert len(r.json()) == 1
|
|
240
|
+
assert len(r.json()) == 1, f"submitted jobs were: {submitted_jobs_info!r}"
|
|
223
241
|
|
|
224
242
|
# 1.2 Search for all jobs submitted before 2024
|
|
225
243
|
r = normal_user_client.post(
|
|
@@ -889,10 +889,8 @@ def test_patch_metadata(normal_user_client: TestClient, valid_job_id: int):
|
|
|
889
889
|
)
|
|
890
890
|
assert r.status_code == 200, r.json()
|
|
891
891
|
|
|
892
|
-
# TODO: This should be timezone aware
|
|
893
892
|
hbt1 = datetime.fromisoformat(r.json()[0]["HeartBeatTime"])
|
|
894
|
-
|
|
895
|
-
hbt1 = hbt1.replace(tzinfo=timezone.utc)
|
|
893
|
+
hbt1 = hbt1.astimezone(tz=timezone.utc)
|
|
896
894
|
|
|
897
895
|
assert r.json()[0]["JobID"] == valid_job_id
|
|
898
896
|
assert r.json()[0]["JobType"] == "VerySpecialIndeed"
|
|
@@ -983,9 +981,9 @@ def test_heartbeat(normal_user_client: TestClient, valid_job_id: int):
|
|
|
983
981
|
new_data = r.json()[0]
|
|
984
982
|
|
|
985
983
|
hbt = datetime.fromisoformat(new_data["HeartBeatTime"])
|
|
986
|
-
#
|
|
987
|
-
|
|
988
|
-
|
|
984
|
+
# This should be timezone aware due to the enforced tzinfo from
|
|
985
|
+
# the SQLAlchemy type used for datetime fields in JobDB
|
|
986
|
+
assert hbt.tzinfo is not None
|
|
989
987
|
assert hbt >= datetime.now(tz=timezone.utc) - timedelta(seconds=15)
|
|
990
988
|
|
|
991
989
|
# Kill the job by setting the status on it
|
|
@@ -1075,10 +1073,8 @@ def test_patch_metadata_doc_example(normal_user_client: TestClient, valid_job_id
|
|
|
1075
1073
|
)
|
|
1076
1074
|
assert r.status_code == 200, r.json()
|
|
1077
1075
|
|
|
1078
|
-
# TODO: This should be timezone aware
|
|
1079
1076
|
hbt1 = datetime.fromisoformat(r.json()[0]["HeartBeatTime"])
|
|
1080
|
-
|
|
1081
|
-
hbt1 = hbt1.replace(tzinfo=timezone.utc)
|
|
1077
|
+
hbt1 = hbt1.astimezone(tz=timezone.utc)
|
|
1082
1078
|
|
|
1083
1079
|
assert r.json()[0]["JobID"] == valid_job_id
|
|
1084
1080
|
assert r.json()[0]["Status"] == payload["Status"]
|
|
@@ -1133,9 +1129,7 @@ def test_patch_heartbeat_doc_example(normal_user_client: TestClient, valid_job_i
|
|
|
1133
1129
|
new_data = r.json()[0]
|
|
1134
1130
|
|
|
1135
1131
|
hbt = datetime.fromisoformat(new_data["HeartBeatTime"])
|
|
1136
|
-
|
|
1137
|
-
assert hbt.tzinfo is None
|
|
1138
|
-
hbt = hbt.replace(tzinfo=timezone.utc)
|
|
1132
|
+
hbt = hbt.astimezone(tz=timezone.utc)
|
|
1139
1133
|
assert hbt >= datetime.now(tz=timezone.utc) - timedelta(seconds=15)
|
|
1140
1134
|
|
|
1141
1135
|
|
|
@@ -30,6 +30,14 @@ See the DiracX helm chart for more details about configuring access to databases
|
|
|
30
30
|
|
|
31
31
|
See the services/tasks documentation for details about how the database classes should be used.
|
|
32
32
|
|
|
33
|
+
### Aside on datetimes in MySQL
|
|
34
|
+
|
|
35
|
+
Dates and times in MySQL (`DATETIME` type) are stored as naive UTC timestamps. They are returned as UTC.
|
|
36
|
+
|
|
37
|
+
MySQL `TIMESTAMP` types should be treated with caution as they are stored as naive UTC timestamps but returned in the local timezone according to the server's timezone setting. https://dev.mysql.com/doc/refman/8.4/en/server-system-variables.html#sysvar_time_zone
|
|
38
|
+
|
|
39
|
+
See MySQL docs: https://dev.mysql.com/doc/refman/8.4/en/datetime.html
|
|
40
|
+
|
|
33
41
|
### API
|
|
34
42
|
|
|
35
43
|
TODO: Document the basic API for SQL databases once things settle
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
bd00e77 fix: from DateTime to SmarterDateTime
|
|
2
|
+
84b6d7b fix: tests
|
|
3
|
+
620e26c fix (JobDB): heartbeat timestamp is now returned with tzinfo, fix the test.
|
|
4
|
+
71dcc25 fix (JobDB): handle FunctionElement passed to inside CASE
|
|
5
|
+
abfb257 fix (JobDB): use SmarterDateTime defaults
|
|
6
|
+
b9c4d16 docs: document how MySQL timestamp and datetimes work for word of caution
|
|
7
|
+
5681b35 feat (JobDB): added SmarterDateTime
|
|
8
|
+
bd1bfbf fix (JobDB): use literal to hint to typeengine the correct type when rendering bind parameters
|
|
9
|
+
4374553 fix: add CORS origins for https:// local dev instances
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from functools import partial
|
|
4
|
-
|
|
5
|
-
import sqlalchemy.types as types
|
|
6
|
-
from sqlalchemy import Column as RawColumn
|
|
7
|
-
from sqlalchemy import DateTime, Enum
|
|
8
|
-
|
|
9
|
-
from .functions import utcnow
|
|
10
|
-
|
|
11
|
-
Column: partial[RawColumn] = partial(RawColumn, nullable=False)
|
|
12
|
-
NullColumn: partial[RawColumn] = partial(RawColumn, nullable=True)
|
|
13
|
-
DateNowColumn = partial(Column, type_=DateTime(timezone=True), server_default=utcnow())
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def EnumColumn(name, enum_type, **kwargs): # noqa: N802
|
|
17
|
-
return Column(name, Enum(enum_type, native_enum=False, length=16), **kwargs)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class EnumBackedBool(types.TypeDecorator):
|
|
21
|
-
"""Maps a ``EnumBackedBool()`` column to True/False in Python."""
|
|
22
|
-
|
|
23
|
-
impl = types.Enum
|
|
24
|
-
cache_ok: bool = True
|
|
25
|
-
|
|
26
|
-
def __init__(self) -> None:
|
|
27
|
-
super().__init__("True", "False")
|
|
28
|
-
|
|
29
|
-
def process_bind_param(self, value, dialect) -> str:
|
|
30
|
-
if value is True:
|
|
31
|
-
return "True"
|
|
32
|
-
elif value is False:
|
|
33
|
-
return "False"
|
|
34
|
-
else:
|
|
35
|
-
raise NotImplementedError(value, dialect)
|
|
36
|
-
|
|
37
|
-
def process_result_value(self, value, dialect) -> bool:
|
|
38
|
-
if value == "True":
|
|
39
|
-
return True
|
|
40
|
-
elif value == "False":
|
|
41
|
-
return False
|
|
42
|
-
else:
|
|
43
|
-
raise NotImplementedError(f"Unknown {value=}")
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
85636cc [pre-commit.ci] auto fixes from pre-commit.com hooks
|
|
2
|
-
8cbaeb3 docs: start installation doc
|
|
3
|
-
6ce950f docs: Tidy up entrypoints to the docs
|
|
4
|
-
bbaa589 docs: Add extensions documentation
|
|
5
|
-
46d54fe fix: change endpoint and add documentation
|
|
6
|
-
5ef1986 docs: Add more pages from the charts repo
|
|
7
|
-
67f3491 docs: Add rotate a secret docs
|
|
8
|
-
a0e9c4e docs: Add dependency injection reference
|
|
9
|
-
972446e docs: Clarify TODO for DiracX storage interactions
|
|
10
|
-
9b52213 docs: Finish CS conversion documentation
|
|
11
|
-
060ad8c docs: Add edit buttons to documentation pages
|
|
12
|
-
1e690d5 docs: Add writing documentation tutorial
|
|
13
|
-
f3cc1fc fix: job admins cannot get summaries including jobs from other users
|
|
14
|
-
76bfad0 docs: slowly building the structure
|
|
15
|
-
380da62 fix: Change from Any,Any to str,Any as search returns columns to value mapping
|
|
16
|
-
8d00976 refactor: Made search and summary generic to reuse it easily
|
|
17
|
-
0fa02ae feat: implement zstandard compression for sandbox files
|
|
18
|
-
49ac251 fix: pin joserfc to minimum version 1.1.0
|
|
19
|
-
2152613 [pre-commit.ci] pre-commit autoupdate
|
|
20
|
-
d46d6d2 feat: Add filtered requests logs when integration tests are a success
|
|
21
|
-
d844e65 feat: Add legacy checks in DiracX
|
|
22
|
-
f4351e6 fix: Fixed WMS to remove JOB_ADMINISTRATOR from pilot properties
|
|
23
|
-
cec97dc fix: check AUTOREST_HOME directory is created if it doesn't exist
|
|
24
|
-
71753a2 fix: handle bad regex and test
|
|
25
|
-
d4d0a54 [pre-commit.ci] pre-commit autoupdate
|
|
26
|
-
17da6d5 feat: not like and regex operators for the job db
|
|
27
|
-
485fc0e build(deps): bump the actions group with 2 updates
|
|
28
|
-
562f95b fix (JobDB): remove DELETE endpoint that would cause inconsistencies
|
|
29
|
-
74c680b fix: job admins cannot see jobs from other users
|
|
30
|
-
3c3e378 fix: freeze pytest-asyncio
|
|
31
|
-
78c172a fix: job_parameters mapping update
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|