Skip to main content

A Python package to interact with OneDep APIs.

Project description

DSP Mock Package

PyPI version

Prepares data to be deposited into OneDep system through the Deposition API. JSON schemas served remotelly will be used to check metadata in mmCIF files and also check if the local deposition/session has all required files for the chosen experiment type. Checks will be carried before deposition is created on the server and files are uploaded.

Configuration

DepositConfig.load() resolves settings from three sources in order of increasing priority:

  1. ~/.config/onedep/config.toml (lowest, persistent defaults)
  2. Environment variables (override file values)
  3. Keyword arguments passed to DepositConfig.load() (highest priority)

After resolving the above, load() also reads the [auths.<fqdn>] section matching the resolved hostname and populates the access_token and refresh_token fields automatically (see Authentication).

The default values are:

Field Default Source
access_token None [auths.<fqdn>] / override
refresh_token None [auths.<fqdn>] / override
hostname https://deposit.wwpdb.org/deposition [default] / env / override
ssl_verify true [default] / env / override
redirect true [default] / env / override
schema_base_url https://schemas.wwpdb.org/nextdep [default] / env / override
schema_cache_dir ~/.onedep/schemas [default] / override
session_dir ~/.onedep/sessions [default] / override
config_path ~/.config/onedep/config.toml override only

Config file

The config file has two distinct sections:

  • [default] — general settings (hostname, SSL, API key, etc.)
  • [auths.<fqdn>] — auth tokens for a specific host, keyed by hostname FQDN

The FQDN key is derived from the hostname by stripping the URL scheme, port, and path, and replacing . and - with _. For example, https://deposit.wwpdb.org/depositiondeposit_wwpdb_org.

A complete config file looks like:

[default]
hostname = "https://deposit.wwpdb.org/deposition"
ssl_verify = true
redirect = true
schema_base_url = "https://schemas.wwpdb.org/nextdep"
schema_cache_dir = "/home/you/.onedep/schemas"
session_dir = "/home/you/.onedep/sessions"

[auths.deposit_wwpdb_org]
access_token = "eyJ..."
refresh_token = "opaque-string"

The [auths.<fqdn>] section is written by TokenStore.store_tokens() — see Authentication. access_token and refresh_token are never read from [default]; they always come from the per-host [auths] table. Multiple hosts can coexist in the same file under separate [auths.<fqdn>] tables without interfering with each other.

DepositConfig.load() reads [default] first, then reads the [auths.<fqdn>] section that matches the resolved hostname and populates access_token and refresh_token into the returned config object.

Unknown keys in [default] are ignored. An empty hostname is ignored so the default remains in effect. Invalid TOML raises ConfigError.

Load configuration with no arguments once the file is in place:

from onedep_lib.config import DepositConfig

cfg = DepositConfig.load()
print(cfg.access_token)   # populated from [auths.deposit_wwpdb_org] if present

Environment variables

Variable Field Type
ONEDEP_ACCESS_TOKEN access_token str
ONEDEP_REFRESH_TOKEN refresh_token str
ONEDEP_HOSTNAME hostname str
ONEDEP_SSL_VERIFY ssl_verify true, false, 1, or 0
ONEDEP_REDIRECT redirect true, false, 1, or 0
ONEDEP_SCHEMA_URL schema_base_url str
export ONEDEP_ACCESS_TOKEN="your.jwt.token"
export ONEDEP_REFRESH_TOKEN="opaque-token"
export ONEDEP_HOSTNAME="https://onedep-depui-test.wwpdb.org/deposition"
export ONEDEP_SSL_VERIFY="false"
export ONEDEP_SCHEMA_URL="http://localhost:8080/schemas"

Environment variables override config-file values. An empty ONEDEP_HOSTNAME is ignored so the default hostname remains in effect. Invalid boolean values raise ConfigError.

Runtime overrides

Pass keyword arguments to DepositConfig.load() to override both the config file and environment:

from pathlib import Path

from onedep_lib.config import DepositConfig

cfg = DepositConfig.load(
    api_key="your.jwt.token",
    ssl_verify=False,
    schema_cache_dir=Path("/tmp/onedep-schemas"),
    session_dir=Path("/tmp/onedep-sessions"),
)

To inject tokens directly (e.g. in tests or embedded applications):

cfg = DepositConfig.load(
    access_token="eyJ...",
    refresh_token="opaque-string",
)

When either token is supplied as an override, load() skips reading the [auths.<fqdn>] section entirely.

Authentication

OneDep uses short-lived JWT access tokens (30-minute TTL) paired with long-lived opaque refresh tokens (30-day TTL). The TokenStore class manages the full token lifecycle: storage, expiry detection, automatic refresh, and revocation.

Getting a token pair

Tokens are generated manually by a logged-in user in the OneDep web UI. There is no programmatic login flow in this library.

Storing tokens

Once you have a token pair from the web UI, store it with TokenStore.store_tokens():

from onedep_lib.auths.token import TokenStore
from onedep_lib.config import DepositConfig

cfg = DepositConfig.load()
store = TokenStore(config=cfg)
store.store_tokens(
    access_token="eyJ...",
    refresh_token="opaque-string",
)

This writes the tokens to the [auths.<fqdn>] section of the config file and updates the in-memory config fields. The FQDN key is derived from the resolved hostname by stripping the URL scheme, port, and path, and replacing . and - with _. For example, https://deposit.wwpdb.org/deposition becomes deposit_wwpdb_org.

The resulting config file looks like:

[default]
hostname = "https://deposit.wwpdb.org/deposition"

[auths.deposit_wwpdb_org]
access_token = "eyJ..."
refresh_token = "opaque-string"

Multiple hosts can coexist in the same file under separate [auths.<fqdn>] tables.

Accessing tokens

Call get_access_token() to retrieve a valid access token. If the stored token is expired or about to expire (within 60 seconds), TokenStore automatically calls the refresh endpoint and persists the rotated token pair before returning:

token = store.get_access_token()   # refreshes transparently if needed

On next startup, DepositConfig.load() reads the [auths.<fqdn>] section matching the hostname and populates access_token and refresh_token into the config object. No extra call is needed:

cfg = DepositConfig.load()
print(cfg.access_token)   # populated from [auths.deposit_wwpdb_org]

Token refresh

Refresh is automatic inside get_access_token(), or can be triggered explicitly:

new_access_token = store.refresh()

Refresh token rotation is mandatory: each successful refresh call invalidates the old refresh token and issues a new one. Both tokens are persisted to the config file immediately. If the refresh token is expired, revoked, or otherwise invalid, the server returns 401 and TokenStore raises AuthError with a message prompting the user to generate and paste a new token pair.

Revocation

store.revoke()

This posts the current refresh token to the server's revoke endpoint and, on success (204 No Content), removes the [auths.<fqdn>] entry from the config file and clears the in-memory fields. After revocation, get_access_token() raises AuthError.

To clear tokens locally without contacting the server:

store.clear_tokens()

Error handling

All auth errors raise onedep_lib.exceptions.AuthError. Config file parse errors raise ConfigError (surfaced as AuthError when they originate inside TokenStore).

Situation Error
No tokens stored AuthError("No access token stored...")
Refresh token expired / revoked AuthError("...generate and paste a new token pair")
Network failure during refresh/revoke AuthError("Token refresh/revoke failed: ...")
Malformed token values in config file AuthError("Malformed token data...") or ConfigError from load()

DSP API

The DSP (Deposition Software Provider) API is the high-level interface for third-party suites (CCP4, Phenix, GlobalPhasing) to stage files locally, run pre-submission checks, and submit depositions to OneDep. It persists session state in a local JSON file so workflows can be interrupted and resumed.

New deposition

import onedep_lib as dsp

with dsp.deposit_init(
    email="depositor@example.org",
    users=["0000-0002-5109-8728"],   # ORCID IDs
    country=dsp.Country.USA,
    experiment_type=dsp.ExperimentType.XRAY,
) as dep:
    print(dep.session_id)           # save this to resume later

    coord_id = dep.add_file("model.cif",   dsp.FileType.MMCIF_COORD)
    sf_id    = dep.add_file("data-sf.cif", dsp.FileType.CRYSTAL_STRUC_FACTORS)

    report = dep.check_required_files()
    if not report.ok:
        for issue in report.errors():
            print(issue.message)

    dep_id = dep.deposit()          # non-blocking; triggers upload + process
    print(dep.get_status())

See examples/xray_deposition.py for a complete walkthrough including per-file checks.

with dsp.deposit_init(
    email="depositor@example.org",
    users=["0000-0002-5109-8728"],   # ORCID IDs
    country=dsp.Country.USA,
) as dep:
    dep.set_experiment_type(dsp.ExperimentType.EM)
    dep.set_em_params(em_subtype=dsp.EMSubType.SPA, coordinates=True)

    coord_id = dep.add_file('emd_33233.cif',   dsp.FileType.MMCIF_COORD)
    map_id = dep.add_file('emd_33233.map.gz',     dsp.FileType.EM_MAP)
    half1_id = dep.add_file('emd_33233_half_map_1.map.gz',   dsp.FileType.EM_HALF_MAP)
    half2_id = dep.add_file('emd_33233_half_map_2.map.gz',   dsp.FileType.EM_HALF_MAP)
    dep.add_file('emd_33233.png',   dsp.FileType.ENTRY_IMAGE)
    dep.check_required_files()

    dep.set_voxel_values(map_id,   spacing_x=1.0825, spacing_y=1.0825, spacing_z=1.0825, contour=0.01)
    dep.set_voxel_values(half1_id, spacing_x=1.0825, spacing_y=1.0825, spacing_z=1.0825, contour=0.01)
    dep.set_voxel_values(half2_id, spacing_x=1.0825, spacing_y=1.0825, spacing_z=1.0825, contour=0.01)
    dep.check_file_type(fsc_xml_id, dsp.FileType.FSC_XML)
    dep.deposit()
    dep.get_status()

See examples/em_deposition.py for a complete walkthrough including per-file checks.

Resume an existing session

Sessions are identified by a UUID printed at creation time. Pass it to deposit_resume() to reload the full session state — registered files and remote deposition ID included.

dep = dsp.deposit_resume("your-session-uuid")

dep.add_file("extra.cif", dsp.FileType.CRYSTAL_STRUC_FACTORS)
dep.deposit()   # reuses the existing remote deposition if already submitted

See examples/resume_deposition.py for a complete example.

List all sessions

Use list_sessions() to retrieve all local sessions with their registered files, ordered newest first:

import onedep_lib as dsp
from pathlib import Path

sessions = dsp.list_sessions()

for session, files in sessions:
    print(session.session_id, session.created_at, session.email)
    print("  experiment:", session.experiment_type)
    print("  remote dep:", session.remote_dep_id or "(not submitted)")
    for f in files:
        print("  file:", f.file_path, f.file_type)

Sessions where remote_dep_id is None have not been submitted yet. To inspect sessions stored in a non-default location, pass a base_dir:

sessions = dsp.list_sessions(base_dir=Path("/custom/session/dir"))

Documentation

Documentation is built with Zensical and deployed to GitHub Pages.

  • Preview locally: just docs-serve (serves at http://localhost:8000)
  • Build: just docs-build

API documentation is auto-generated from docstrings using mkdocstrings.

Docs deploy automatically on push to main via GitHub Actions. To enable this, go to your repo's Settings > Pages and set the source to GitHub Actions.

Development

Run tests:

uv run pytest

Run quality checks (format, lint, type check, test):

just qa

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

onedep_lib-0.1.0.dev1.tar.gz (152.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

onedep_lib-0.1.0.dev1-py3-none-any.whl (37.6 kB view details)

Uploaded Python 3

File details

Details for the file onedep_lib-0.1.0.dev1.tar.gz.

File metadata

  • Download URL: onedep_lib-0.1.0.dev1.tar.gz
  • Upload date:
  • Size: 152.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for onedep_lib-0.1.0.dev1.tar.gz
Algorithm Hash digest
SHA256 8cc6f88b0ccfb708b8896614de9be6f831ebdaaf9f5ad25a0206e94f3df633d8
MD5 49144f8b7acb8914f815c461222e541b
BLAKE2b-256 64477e5a4544362de3ddb50648e856a31abf2f38ef6e03c9d71f7bb789b302c1

See more details on using hashes here.

File details

Details for the file onedep_lib-0.1.0.dev1-py3-none-any.whl.

File metadata

File hashes

Hashes for onedep_lib-0.1.0.dev1-py3-none-any.whl
Algorithm Hash digest
SHA256 541dd41e8022d49cdfdd39ae3864740834f1fdc31197b0166d697ffd2979e64b
MD5 236d4c2ae609655ca033b16a1049bcd5
BLAKE2b-256 c6d413367633d55fc804a6d27bfcd08402a56d131a0394fd947c3ca16f474a4c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page