Get in touch

Get in touch

Prefer using email? Say hi at hello@moveshelf.com

This section explains how to upload data into a specific trial in a condition of a session for a subject with a specified MRN on Moveshelf using the Moveshelf API. Before uploading, the script first checks whether data file with the same name already exists within the trial on Moveshelf and will skip the upload if it's already present. Please refer to this section for supported data types.
Prerequisites
Before implementing this example, ensure that your processing script includes all necessary setup steps. In particular, you should have:
Implementation
To upload data into the trial identified by the "clip_id", add the following lines of code to your processing script:
## README: this example shows how we can upload date into a trial on 
# Moveshelf using the Moveshelf API.
# This code assumes you have implemented the 'Create subject' example, 
# that you have found the subject with a given EHR-id/MRN (my_subject_mrn)
# or name (my_subject_name) within a given project (my_project), and that 
# you have access to the subject ID.
# This code also assumes you have implemented the 'Create session' example, 
# and that you have found the session with a specific name (my_session)
# This code also assumes you have implemented the 'Create trial' example, 
# and that you have found the trial with a specific name (my_trial).

# For that trial as part of a subject and session, we need the "clip_id". 

## General configuration. Set values before running the script
my_project = "<organizationName/projectName>"  # e.g. support/demoProject
my_subject_mrn = "<subjectMRN>"  # subject MRN, e.g. "1234567" or None
my_subject_name = "<subjectName>"  # subject name, e.g. Subject1
my_session = "YYYY-MM-DD" # session name, e.g. 2025-09-05
my_condition = "<conditionName>"  # e.g. "Barefoot"
my_trial = "<trialName>"
filter_by_extension = None
data_file_name = None  # with None, the name of the actual file is used, if needed this name can be used
files_to_upload = [<pathToFile_1>, <pathToFile2>,...] # list of files to be added to the trial
data_type = "raw"

## Add here the code to retrieve the project and find an existing subject and its "subject_details"
# ... subject_found = True

## Add here the code to retrieve an existing session and get its details using "getSessionById"

## Add here the code to retrieve an existing trial and get the ID: clip_id

existing_additional_data = api.getAdditionalData(clip_id)
existing_additional_data = [
    data for data in existing_additional_data if not filter_by_extension
    or os.path.splitext(data["originalFileName"])[1].lower() == filter_by_extension]
existing_file_names = [data["originalFileName"] for data in existing_additional_data if
    len(existing_additional_data) > 0]

## Upload data
for file_to_upload in files_to_upload:
    file_name = data_file_name if data_file_name is not None else os.path.basename(file_to_upload)
    if file_name in existing_file_names:
        print(file_name + " was found in clip, will skip this data.")
        continue

    print("Uploading data for : " + my_condition + ", " + my_trial + ": " + file_name)

    dataId = api.uploadAdditionalData(file_to_upload, clip_id, data_type, file_name)    

It is also possible to upload trial data and metadata in batch using information from a JSON file stored on your local machine, e.g., 'moveshelf_config_import.json'. Instead of defining a list of files_to_upload, define the path to the folder containing session data as shown below:
## General configuration. Set values before running the script
session_folder_path = "<pathToDataFolder>"
file_extension_to_upload = "<extension>" # e.g., ".GCD"

# Define the path to the local JSON file
local_metadata_json = os.path.join(parent_folder, "moveshelf_config_import.json")

# Load JSON file
with open(local_metadata_json, "r") as file:
    local_metadata = json.load(file)

# Extract conditionDefinition, representativeTrials, and trialSideSelection
# from metadata JSON
my_condition_definition = local_metadata.get("conditionDefinition", {})
my_representative_trials = local_metadata.get("representativeTrials", {})
my_trial_side_selection = local_metadata.get("trialSideSelection", {})

## Add here the code to retrieve the project and find an existing subject and its "subject_details"
# ... subject_found = True

## Add here the code to retrieve an existing session and get its details using "getSessionById"

## Add here the code to get existing conditions and loop over conditions and trials 
# ... clip_id = util.addOrGetTrial(api, session, condition, my_trial)

## Upload data
file_name = data_file_name if data_file_name is not None else f"{my_trial}{file_extension_to_upload}"
print("Uploading data for : " + my_condition + ", " + my_trial + ": " + file_name)

dataId = api.uploadAdditionalData(
    os.path.join(session_folder_path, f"{my_trial}{file_extension_to_upload}"),
    clip_id,
    data_type,
    file_name
) 

metadata_dict = {
    "title": my_trial
}

# Initialize the trial template
custom_options_dict = {"trialTemplate": {}}
trial_template = custom_options_dict["trialTemplate"]

# Add side information if available
side_value = my_trial_side_selection.get(my_trial)
if side_value:
    trial_template["sideAdditionalData"] = {dataId: side_value}

# Handle representative trial flags
representative_trial = my_representative_trials.get(my_trial, "")

rep_flags = {}
if "left" in representative_trial:
    rep_flags["left"] = True
if "right" in representative_trial:
    rep_flags["right"] = True
if rep_flags:
    trial_template["representativeTrial"] = rep_flags

# Remove the trialTemplate key if it's still empty
if not trial_template:
    custom_options_dict = {}

# Convert to JSON string
custom_options = json.dumps(custom_options_dict)

api.updateClipMetadata(clip_id, metadata_dict, custom_options)

# Fetch trial using the trial ID
new_clip = api.getClipData(clip_id)
print(
    f"Found a clip with title: {new_clip['title']},\n"
    f"id: {new_clip['id']},\n"
    f"and custom options: {new_clip['customOptions']}"
)
        
PDF/image files
PDF files and image files (e.g. ".jpg" or ".png") can be stored on Movesehelf within a trial, but for this we have a dedicated place on Moveshelf, so the files are picked up e.g. when exporting to a Word document. The PDF/image files are stored in a condition called "Additional files" with a separate trial per PDF/image and a name that is typically the same as the PDF/image file name in that trial. To upload, the code as shown above can be used, with the following modifications:
## Modifications needed to upload PDF/image files into "Additional files"
my_condition = "Additional files"
my_trial = "<pdf_file_name>" 
filter_by_extension = ".pdf"  # for  images, use e.g. ".jpg"/".png" if needed.
files_to_upload = [
    "<path_to_additional_data.pdf>",
]
data_type = "doc"  # for images, set data_type to "img"
Raw motion data - ZIP
For raw motion data provided in a ZIP archive, we suggest to use the same "Additional files" condition that is also used for PDF/image files. Specifically, for ZIP files with raw motion data we suggest to use a trial named "Raw motion data". The additional data file can then be named e.g. "Raw motion data - my_session.zip". To upload, the code as shown above can be used, with the following modifications:
## Modifications needed to upload raw motion data in ZIP archive into "Additional files"
my_condition = "Additional files"
my_trial = "Raw motion data"
data_file_name = "Raw motion data - " + my_session + ".zip"  # Set to None for actual file name.
filter_by_extension = ".zip"  # set to None if used without filtering
files_to_upload = [
    "<path_to_additional_data.zip>",
]
data_type = "raw"