Skip to content

API Usage

For the API during an active test mission, we'll generally be concerned with creating one of the following: Poses, Events, Notes, Images. As opposed to the configuration which was largely done through the Django custom command, these will be created through their respective API endpoints.

Poses

Poses can be used to track entities during each test run.

Here is an example of posting a pose.

import datetime
import requests

pose_data = {
    "lat": 32.67,
    "lon":  -117.24,
    "entity": "/api/entities/alpha_1/",
    "pose_source": "/api/pose_sources/1/",
    "timestamp": datetime.datetime.now().isoformat(),
    "trial": 1,
}

requests.post("http://localhost/api/poses/", json=pose_data, auth=("admin", "admin"))
import datetime
import requests

pose_data = {
    "lat": 32.67,
    "lon":  -117.24,
    "entity": "/api/entities/alpha_1/",
    "pose_source": "/api/pose_sources/1/",
    "timestamp": datetime.datetime.now().isoformat(),
    "trial": 1,
    "elevation": 0.0,
    "heading": 23.0,
    "speed": 23,
    "velocity": [133.0, 2.0],
}

requests.post("http://localhost/api/poses/", json=pose_data, auth=("admin", "admin"))

entity is the entity that this pose is for.

pose_source indicates what type of pose it is. This string should contain the id of a pose source.

timestamp represents the datetime at which this pose happened.

trial indicates which specific test run you want to associate this pose with. The same entity can have different paths through the test environment so this isolates the poses to their respective trial. This field would be assigned the trial id.

elevation is an optional arbitrary float value. This could be used to represent meters from sea level or levels in a building.

heading is an optional arbitrary float value. A good rule of thumb is to use 0.0 and/or 360.0 as north and go clockwise (i.e. east would be 90.0)

speed and velocity are both optional fields. velocity is an array, allowing to provide either 2D or 3D vectors. speed isn't automatically calculated so you'll have to manually set the scalar value. There is no validation that the speed field is the correct speed for the velocity field.

Events

Events will be the primary mode of recording data. These will use the EventTypes created during the configuration and can include any other specific data and/or metadata that you wish to attach.

The following is an example of posting an event.

import datetime
import requests

json_to_post = {
    "start_datetime": datetime.datetime.now().isoformat(),
    "event_type": "/api/event_types/1/",
    "trial": "/api/trials/1/",
}
requests.post("http://<Mole_IP>/api/events/", json=json_to_post, auth=("admin", "admin"))
import datetime
import requests

pose_data = {
    "lat": 32.67,
    "lon":  -117.24,
    "entity": "/api/entities/alpha_1/",
    "pose_source": "/api/pose_sources/1/",
    "timestamp": datetime.datetime.now().isoformat(),
    "trial": 1,
}

pose_post = requests.post("http://localhost/api/poses/", json=pose_data, auth=("admin", "admin"))
pose_url = pose_post.headers['Location']

json_to_post = {
    "start_datetime": datetime.datetime.now().isoformat(),
    "end_datetime": "2022-01-01T08:00:00+00:00",
    "event_type": "/api/event_types/1/",
    "trial": "/api/trials/1/",
    "start_pose": pose_url,
    "metadata": {
        "entity": "usv1",
        "extra_info": 25.2,
        "list_of_important_things": [
            "item1",
            "item2",
        ],
    }
}
requests.post("http://<Mole_IP>/api/events/", json=json_to_post, auth=("admin", "admin"))

start_datetime and end_datetime should be a string in the format of ISO 8601. start_datetime is a required field. end_datetime is optional and intended for events with a duration of some kind. It can be omitted and added later or omitted entirely.

event_type should be a string with the corresponding id for the event type you want to create.

trial should be a string with the corresponding id for the trial you want to attach this event to.

start_pose is an optional field that ties a pose to this event. Its value should be the url of a previously created pose. Currently, it is not possible to create both a pose and event in the same HTTP request. You can use this start pose field to indicate something like an interaction happening at a specific location.

metadata should be a Python dictionary of relevent data and metadata about the event. If not supplied, it will default to an empty dictionary.

The target url should be whatever IP the Mole server is hosted on. This could be an IP or http://localhost if posting locally from the same host.

Note

The crendentials used are based off the example configuration in the basic configuration. If these have changed, make sure to use the correct username and password.

Notes

Notes provide an area to supplement events with free-form notes. All notes must be attached to an event.

import datetime
import requests

json_to_post = {
    "tester": "/api/testers/1/",
    "note": "Test note here",
    "event": "/api/events/1/",
}

requests.post("http://localhost/api/notes/", json=json_to_post, auth=("admin", "admin"))

Images

Images can be useful for traceability and debugging. You can attach an image(s) to any event.

import datetime
import requests

files = {
    "image": open('/directory/to/image.png', 'rb'),
}
data = {
    "image_type": "/api/image_types/1/",
    "event": "/api/events/1/",
    "timestamp": datetime.datetime.now().isoformat(),
}

r = requests.post("http://localhost/api/images/", files=files, data=data, auth=("admin", "admin"))

Bulk POSTs

Mole also has the ability to create multiple instances for a single HTTP POST request. Currently this ability only exists for events and poses. To use this ability, simply pass a list of objects rather than a single object. This feature is useful if single posts are too slow for the level of throughput desired and increasing the number of Gunicorn/Django workers is not possible. Testing might be required to determine the optimal number of objects to post during each request. Also note that Pulsar messages will not be sent for any bulk events or poses. Any Pulsar functions that are tracking the event log will not run for these events.

POST /api/events
{
    "start_datetime": <ISO_datetime>,
    "start_pose": <pose_url>,
    "event_type": <event_type_url>,
    "metadata": <metadata>,
}
POST /api/events
[
    {
        "start_datetime": <ISO_datetime>,
        "start_pose": <pose_url>,
        "event_type": <event_type_url>,
        "metadata": <metadata>,
    },
    {
        "start_datetime": <ISO_datetime>,
        "start_pose": <pose_url>,
        "event_type": <event_type_url>,
        "metadata": <metadata>,
    },
    {
        "start_datetime": <ISO_datetime>,
        "start_pose": <pose_url>,
        "event_type": <event_type_url>,
        "metadata": <metadata>,
    }
]