Logo

Documentation

API Documentation

The RESTful API of the Data Mining Lab Freiberg is implemented with Django REST framework. Two automatic API documentations are provided through drf-spectacular:

Using the API with Python

Package Imports

import requests
import json

Endpoints

Note that list of institutes, methods, funding bodies, and projects may be retrieved from the API. Staff data is only available to administrator users via the web frontend.

Only samples and experiments may be created, updated, or deleted via the API. All other entries in the database need to be made through the web frontend.

# API base URL
BASE_URL = 'https://dmlf.de/'

# endpoint for obtaining the auth token and refreshing it
TOKEN_ENDPOINT = BASE_URL + 'api/token/'
REFRESH_TOKEN_ENDPOINT = BASE_URL + 'api/token/refresh/'

# endpoints for retrieving data
INSTITUTES_ENDPOINT = BASE_URL + 'institutes/'
METHODS_ENDPOINT = BASE_URL + 'methods/'
FUNDINGBODIES_ENDPOINT = BASE_URL + 'fundingbodies/'
PROJECTS_ENDPOINT = BASE_URL + 'projects/'

# endpoints for creating and reading samples and experiments
SAMPLETYPES_ENDPOINT = BASE_URL + 'sampletypes/'
SAMPLES_ENDPOINT = BASE_URL + 'samples/'
EXPERIMENTS_ENDPOINT = BASE_URL + 'experiments/'

Token Authentication

Getting a Token

Authentication in the RESTful API of the DataMiningLabFreiberg is handled through tokens. You need to have a working user account to access the API!

credentials = {
    'username': username,
    'password': password
}

headers = {
    'Content-Type': 'application/json'
}

# Request to obtain token
response = requests.post(TOKEN_ENDPOINT, data=json.dumps(credentials), headers=headers)
response_data = response.json()
access_token = response_data.get('access')
refresh_token = response_data.get('refresh')

# set up an authenticated session
session = requests.Session()
session.headers.update({
    'Authorization': f'Bearer {access_token}'
})

You can always check if your request was successful by checking response.status_code, response.reason, and response.text.

Do not forget to update your session as above whenever you have refreshed your token.

Refreshing a Token

JSON Web Tokens expire after one hour. Implement a check for when a token expires. In this case, you refresh like so:


refresh_data = {
    'refresh': refresh_token
}

response = requests.post(REFRESH_TOKEN_ENDPOINT, json=refresh_data, headers=headers)
response_data = response.json()

access_token = response_data.get('access')

session = requests.Session()
session.headers.update({
    'Authorization': f'Bearer {access_token}'
})

If a token expired, you will receive a 401 error. You may handle expiration checks as in the example below:


def refresh_access_token(refresh_token):
    """Request a new access token using the refresh token."""
    response = requests.post(REFRESH_TOKEN_ENDPOINT, json={'refresh': refresh_token}, headers={'Content-Type': 'application/json'})
    return response.json().get('access')

def make_api_request(url, tokens, method='GET', data=None):
    """Make an API request and handle 401 by refreshing the token."""
    access_token = tokens['access']
    headers = {
        'Authorization': f'Bearer {access_token}',
        'Content-Type': 'application/json'
    }
    response = requests.request(method, url, json=data, headers=headers)

    if response.status_code == 401:
        # Token might have expired, attempt to refresh
        print("Access token expired, refreshing...")
        new_access_token = refresh_access_token(tokens['refresh'])
        if new_access_token:
            access_token = new_access_token
            headers['Authorization'] = f'Bearer {new_access_token}'
            response = requests.request(method, url, json=data, headers=headers)  # Retry the request with the new token
        else:
            print("Failed to refresh token.")

        session.headers.update({
            'Authorization': f'Bearer {access_token}'
        })

    return response

tokens = {
    'access': access_token,
    'refresh': refresh_token
}

response = make_api_request(INSTITUTES_ENDPOINT, tokens)

Data Retrieval

Retrieve a list of all entries by using a endpoint directly:

response = session.get(INSTITUTES_ENDPOINT)

Request single entries by providing the ID:

id = '1'

response = session.get(INSTITUTES_ENDPOINT + id)

Request filtered list by adding parameters:

params = {
    'city': 'Freiberg'
}

response = session.get(INSTITUTES_ENDPOINT, params=params)

You can use the json package for proper printing:

json.loads(response.text)

Sample Creation

Sample Info File

Every sample requires a sample info file, which is a JSON containing metadata of the sample. You may use the API to get the required structure of the JSON file. There are several different Sample types, all possible ones are available at:

response = session.get(SAMPLETYPES_ENDPOINT)

Get the JSON structure for a given sample type:

sample_type_name = 'Battery'
sample_type_info_url = f"{BASE_URL}api/sample-type-info/?sample_type_name={sample_type_name}"
response = session.get(sample_type_info_url)

You can now create a sample_info object according to the given JSON structure:

sample_info = {
    "name": "mock battery",
    "composition": "NMC111",
    "manufacturer": "car manufacturer",
    "produced": "",
    "comment": "here be dragons"
}

sample_info_json = json.dumps(sample_info)

Sample POST Request

sample_info and supplementary_file need to be supplied as files in the HTTP request.

# files tuple/list has to be constructed as 
# (file_name, (file_name, file_content, mime_type))
files = {
    'sample_info': ('sample_info.json', sample_info_file, 'application/json')
}

# sample data to be sent in the POST request
sample_data = {
    'sample_id': '240616_125138_010102',
    'name': 'mock sample',
    'institute': 1,
    'project': 1,
    'method': 1,
    'parent': '',
    'sample_type': 1,
    'date_created': '2024-06-16',
}

# POST request
response = session.post(SAMPLES_ENDPOINT, data=sample_data, files=files)

Make sure your response is (201, 'Created').

Sample POST Request with Existing Files

# Open JSON and ZIP files for request
with open('sample_info.json', 'rb') as json_file, open('sample.zip', 'rb') as zip_file:
    # The key in the files dictionary ('sample_info' in this case) should match
    # the name of the field expected by the server for the file upload
    files = {
        'sample_info': ('sample_info.json', json_file, 'application/json'),
        'supplementary_file': ('sample.zip', zip_file, 'application/zip'),
    }

    # Make the POST request with the file and any other data
    response = session.post(SAMPLES_ENDPOINT, data=sample_data, files=files)

Experiment Creation

experiment_data = {
    'name': 'mock experiment',
    'method': '1',
    'date_created': '2023-04-01',
    'sample': '240616_125138_010102',
    'staff': '1',
    'project': '1',
}

with open('experiment.zip', 'rb') as zip_file:
    # The key in the files dictionary ('sample_info' in this case) should match
    # the name of the field expected by the server for the file upload
    files = {'experiment_file': ('experiment.zip', zip_file, 'application/zip')}

    # Make the POST request with the file and any other data
    response = session.post(EXPERIMENTS_ENDPOINT, data=experiment_data, files=files)