Examples

Some examples illustrating the basics of the API.

You can find the source code and data files for these examples in the examples directory of the squonk2-python-client repository.

get_token

An example that illustrates how to use the client get a token that can be used in other examples.

#!/usr/bin/env python
"""Get and print a Squonk2 access token.
"""
import argparse
import os

from squonk2.auth import Auth

# Username and password are taken from environment variables...
keycloak_user: str = os.environ["SQUONK2_KEYCLOAK_USER"]
keycloak_user_password: str = os.environ["SQUONK2_KEYCLOAK_USER_PASSWORD"]

# Less sensitive information is extracted from the command-line...
parser = argparse.ArgumentParser(
    description="Get a user token. SQUONK2_KEYCLOAK_USER and"
    " SQUONK2_KEYCLOAK_USER_PASSWORD must be set."
)
parser.add_argument(
    "--keycloak-hostname", "-k", help='The API URL, i.e. "example.com"', required=True
)
parser.add_argument(
    "--keycloak-realm", "-r", help='The Keycloak realm, i.e. "blob"', required=True
)
parser.add_argument(
    "--keycloak-client-id",
    "-i",
    help='The Keycloak client ID, i.e. "data-manager-api-dev"'
    ' or "account-server-api-dev"',
    required=True,
)
args = parser.parse_args()

# Now get an API token.
# It should be valid for the remainder of the utility...
token: str = Auth.get_access_token(
    keycloak_url="https://" + args.keycloak_hostname + "/auth",
    keycloak_realm=args.keycloak_realm,
    keycloak_client_id=args.keycloak_client_id,
    username=keycloak_user,
    password=keycloak_user_password,
)

assert token
print(token)

Note

Tokens typically have a limited lifespan and you may need to regenerate it at intervals.

To run this example set these environment variables (your parameters may differ): -

export SQUONK2_KEYCLOAK_USER=<keycloak username>
export SQUONK2_KEYCLOAK_USER_PASSWORD=<keycloak password>

Then run it like this, storing the token in the KEYCLOAK_TOKEN environment variable: -

export KEYCLOAK_TOKEN=$(./examples/get_token.py \
    --keycloak-hostname keycloak.xchem-dev.diamond.ac.uk \
    --keycloak-realm xchem \
    --keycloak-client-id data-manager-api-dev)

calc_rdkit_props

An example that illustrates how to use the client to run a job that Illustrates how to: -

  • Upload a file

  • Run a simple job (with options) using that file

  • Wait for the job to complete

  • Download the results and, finally…

  • Cleanup (delete) the job instance

It uploads a .smi file, a text file containing lines of tab-separated SMILES and a Compound ID strings. The one used here can be found in the examples directory of this repository.

This example calculates molecular properties using RDKit and uses the rdkit-molprops Job, which is typically available on a DM server.

Read the rdkit-molprops documentation in our Virtual Screening collection for further details.

#!/usr/bin/env python
"""An example that illustrates how to use the client to run a job that
calculates molecular properties using RDKit.

Remember that you need to have the SQUONK2_DMAPI_URL environment variable set to
the Squonk2 Data Manager API URL, e.g.
'https://data-manager.xchem-dev.diamond.ac.uk/data-manager-api' or set the
API URL programmatically with a call to DmApi.set_api_url().
"""
import os
import sys
import time

from squonk2.dm_api import DmApi, DmApiRv

# Squonk2 authentication token, project id and the job's input file
# are taken from environment variables...
token: str = os.environ.get("KEYCLOAK_TOKEN")
project_id: str = os.environ.get("PROJECT_ID")
job_input: str = os.environ.get("JOB_INPUT")

if token:
    print("TOKEN present")
else:
    print("No token provided")
    sys.exit(1)

if project_id:
    print("PROJECT_ID present")
else:
    print("No project_id provided")
    sys.exit(1)

if job_input:
    print("JOB_INPUT present")
else:
    print("No job_input provided")
    sys.exit(1)

# The 'ping()' is a handy, simple, API method
# to check the Data Manager is responding.
rv: DmApiRv = DmApi.ping(token)
if rv.success:
    print("API OK")
else:
    print("API not responding")
    sys.exit(1)

# Put some files in a pre-existing DM Project.
# The project is identified by the project_id.
# We simply name the file (or files) and the project-relative
# destination path.
rv = DmApi.put_unmanaged_project_files(
    token, project_id=project_id, project_files=job_input, project_path="/work"
)
if rv.success:
    print("FILE UPLOAD OK")
else:
    print("FILE UPLOAD FAILED")
    sys.exit(1)

# Now, run a Job.
# We identify jobs by using a 'collection', 'job' and 'version'
# and then pass variables expected by the Job in a 'variables' block...
spec = {
    "collection": "rdkit",
    "job": "rdkit-molprops",
    "version": "1.0.0",
    "variables": {
        "separator": "tab",
        "outputFile": "work/foo.smi",
        "inputFile": "work/100.smi",
    },
}
rv = DmApi.start_job_instance(
    token, project_id=project_id, name="My Job", specification=spec
)
# If successful the DM returns an instance ID
# (the instance identity of our specific Job)
# and a Task ID, which is responsible for running the Job.
if rv.success:
    instance_id = rv.msg["instance_id"]
    task_id = rv.msg["task_id"]
    print(f"JOB STARTED (instance_id={instance_id})")
else:
    print("JOB FAILED")
    sys.exit(1)

# We can now use the 'task_id' to query the state of the running Job
# (an instance). When we receive 'done' the Job's finished.
ITERATIONS = 0
while True:
    if ITERATIONS > 10:
        print("TIMEOUT")
        sys.exit(1)
    rv = DmApi.get_task(token, task_id=task_id)
    if rv.msg["done"]:
        break
    print("waiting ...")
    ITERATIONS += 1
    time.sleep(5)
print("DONE")

# Here we get Files from the project.
# These might be files the Job's created.
rv = DmApi.get_unmanaged_project_file(
    token,
    project_id=project_id,
    project_file="foo.smi",
    project_path="/work",
    local_file="examples/foo.smi",
)
if rv.success:
    print("DOWNLOAD OK")
else:
    print("DOWNLOAD FAILED")
    print(rv)

# Now, as the Job remains in the DM until deleted
# we tidy up by removing the Job using the instance ID we were given.
rv = DmApi.delete_instance(token, instance_id=instance_id)
if rv.success:
    print("CLEANUP OK")
else:
    print("CLEANUP FAILED")
    print(rv)
    sys.exit(1)

Assuming you’ve set a token in the environment variable KEYCLOAK_TOKEN set these additional environment variables. You will need access to pre-existing Squonk2 Project, we’ve used project-6c54641f-00b3-4cfa-97f7-363a7b76230a but you will need to provide ione that’s valid with your environment: -

export SQUONK2_DMAPI_URL=https://data-manager.xchem-dev.diamond.ac.uk/data-manager-api
export PROJECT_ID=project-6c54641f-00b3-4cfa-97f7-363a7b76230a
export JOB_INPUT=examples/100.smi

Then run the example like this :

./examples/calc_rdkit_props.py