Would you like to hear about webinars we're
doing, new features we're adding and projects we're undertaking? Sign up
here to our pleasantly infrequent newsletter!
Moveshelf provides a public GitHub repository that contains several complete example scripts that are ready to be used.
To use these example scripts, clone Moveshelf's public GitHub repository to your local machine. You can do this by following these steps.
The sections below explain how to run the different examples scripts, e.g. to query and download or upload data.
This section explains how to retrieve data (files) for all subjects in a project on Moveshelf using the Moveshelf API in an efficient way. The key points to speed up execution in this use case are:
Reduce the number of API calls to retrieve data
Download data in parallel
When to use parallel retrieval by Subject or Session ID: Use this pattern when (1) you already have a list of specific IDs to retrieve, (2) you need to perform multiple filtering iterations before downloading data, or (3) you need to programmatically select which items to download. For simple "filter once and download all" workflows, using include_additional_data=True directly in getFilteredProjectSubjects or getFilteredProjectSessions is more efficient with fewer API calls.
Prerequisites
Before implementing these examples, ensure that your processing script includes all necessary setup steps. In particular, you should have:
Below are two recommended patterns for retrieving large numbers of files in parallel. Use whichever matches your workflow.
Example 1: Retrieve subject data in parallel (from a list of subject IDs)
Use this pattern when you have a list of subject IDs and need to retrieve full subject data with all sessions, clips, and additional data files. Subject IDs can be obtained from filtered subjects, manual selection, or other custom sources.
importos,sys,jsonparent_folder=os.path.dirname(os.path.dirname(__file__))sys.path.append(parent_folder)importrequestsfrommoveshelf_api.apiimportMoveshelfApifromconcurrent.futuresimportThreadPoolExecutor# Use a requests.Session for connection pooling
MAX_WORKERS=5# Number of threads for parallel processing.
POOL_MAXSIZE=MAX_WORKERS+2# Set slightly higher than max_workers to avoid connection issues
requests_session=requests.Session()adapter=requests.adapters.HTTPAdapter(pool_maxsize=POOL_MAXSIZE)requests_session.mount('https://',adapter)defdownload_with_session(url:str)->dict|None:returndownload_json_file(url,session=requests_session)defdownload_json_file(url:str,session:requests.Session|None=None)->dict|None:try:response=session.get(url)ifsessionelserequests.get(url)decoded_content=response.content.decode()returnjson.loads(decoded_content)exceptExceptionase:print(f"Failed to download or parse {url}: {e}")returnNone## Setup the API
# Load config
personal_config=os.path.join(parent_folder,"mvshlf-config.json")ifnotos.path.isfile(personal_config):raiseFileNotFoundError(f"Configuration file '{personal_config}' is missing.\n""Ensure the file exists with the correct name and path.")withopen(personal_config,"r")asconfig_file:data=json.load(config_file)api=MoveshelfApi(api_key_file=os.path.join(parent_folder,data["apiKeyFileName"]),api_url=data["apiUrl"],)# Increase connection pool size globally for urllib3 to match MAX_WORKERS thread workers
api.http.connection_pool_kw['maxsize']=POOL_MAXSIZE## Get available projects
projects=api.getUserProjects()project_names=[project['name']forprojectinprojectsiflen(projects)>0]my_project="<organizationName/projectName>"# e.g. support/demoProject
idx_my_project=project_names.index(my_project)my_project_id=projects[idx_my_project]["id"]file_extension_to_download='.json'# Only download json files
# Example: List of subject IDs (see note below on how to obtain these through filtering)
subject_ids=["<subjectId_1>","<subjectId_2>","<subjectId_3>"]print(f"Processing {len(subject_ids)} subjects")# Step 1: Retrieve full subject data in parallel
withThreadPoolExecutor(max_workers=MAX_WORKERS)asexecutor:subjects_with_clips=list(executor.map(api.getSubjectData,subject_ids))## Extract URLs and file paths for additional data
URLs=[]forsubject_detailsinsubjects_with_clips:forsessioninsubject_details.get("sessions",[]):forclipinsession.get("clips",[]):foradinclip.get("additionalData",[]):ifad["originalDataDownloadUri"].endswith(file_extension_to_download):URLs.append(ad["originalDataDownloadUri"])print(f"Found {len(URLs)} files to download")# Step 2: Download additional data files in parallel
withThreadPoolExecutor(max_workers=MAX_WORKERS)asexecutor:additional_data=list(executor.map(download_with_session,URLs))print(f"Downloaded {sum(1fordatainadditional_dataifdataisnotNone)} files successfully")
How to obtain subject IDs: You can retrieve subject IDs by using getFilteredProjectSubjects with include_additional_data=False for fast filtering, then extract the IDs with: subject_ids = [subject['id'] for subject in filtered_subjects]. This is useful when you need to iterate through multiple filters or programmatically select which subjects to download before retrieving their data.
Example 2: Retrieve session data in parallel (from a list of session IDs)
Use this pattern when you have a list of session IDs and need to retrieve session data with clips and additional data files. Session IDs can be obtained from filtered sessions, manual selection, or other custom sources.
importos,sys,jsonparent_folder=os.path.dirname(os.path.dirname(__file__))sys.path.append(parent_folder)importrequestsfrommoveshelf_api.apiimportMoveshelfApifromconcurrent.futuresimportThreadPoolExecutor# Use a requests.Session for connection pooling
MAX_WORKERS=5# Number of threads for parallel processing.
POOL_MAXSIZE=MAX_WORKERS+2# Set slightly higher than max_workers to avoid connection issues
requests_session=requests.Session()adapter=requests.adapters.HTTPAdapter(pool_maxsize=POOL_MAXSIZE)requests_session.mount('https://',adapter)defdownload_with_session(url:str)->dict|None:returndownload_json_file(url,session=requests_session)defdownload_json_file(url:str,session:requests.Session|None=None)->dict|None:try:response=session.get(url)ifsessionelserequests.get(url)decoded_content=response.content.decode()returnjson.loads(decoded_content)exceptExceptionase:print(f"Failed to download or parse {url}: {e}")returnNone## Setup the API
# Load config
personal_config=os.path.join(parent_folder,"mvshlf-config.json")ifnotos.path.isfile(personal_config):raiseFileNotFoundError(f"Configuration file '{personal_config}' is missing.\n""Ensure the file exists with the correct name and path.")withopen(personal_config,"r")asconfig_file:data=json.load(config_file)api=MoveshelfApi(api_key_file=os.path.join(parent_folder,data["apiKeyFileName"]),api_url=data["apiUrl"],)# Increase connection pool size globally for urllib3 to match MAX_WORKERS thread workers
api.http.connection_pool_kw['maxsize']=POOL_MAXSIZE## Get available projects
projects=api.getUserProjects()project_names=[project['name']forprojectinprojectsiflen(projects)>0]my_project="<organizationName/projectName>"# e.g. support/demoProject
idx_my_project=project_names.index(my_project)my_project_id=projects[idx_my_project]["id"]file_extension_to_download='.json'# Only download json files
# Example: List of session IDs (see note below on how to obtain these through filtering)
session_ids=["<sessionId_1>","<sessionId_2>","<sessionId_3>"]print(f"Processing {len(session_ids)} sessions")# Step 1: Retrieve session data with additional data in parallel
withThreadPoolExecutor(max_workers=MAX_WORKERS)asexecutor:sessions_with_data=list(executor.map(lambdasid:api.getSessionById(sid,include_additional_data=True),session_ids))## Extract URLs and file paths for additional data
URLs=[]forsessioninsessions_with_data:forclipinsession.get("clips",[]):foradinclip.get("additionalData",[]):ifad["originalDataDownloadUri"].endswith(file_extension_to_download):URLs.append(ad["originalDataDownloadUri"])print(f"Found {len(URLs)} files to download")# Step 2: Download additional data files in parallel
withThreadPoolExecutor(max_workers=MAX_WORKERS)asexecutor:additional_data=list(executor.map(download_with_session,URLs))print(f"Downloaded {sum(1fordatainadditional_dataifdataisnotNone)} files successfully")
How to obtain session IDs: You can retrieve session IDs by using getFilteredProjectSessions with include_additional_data=False for fast filtering, then extract the IDs with: session_ids = [session['id'] for session in filtered_sessions]. This is useful when you need to iterate through multiple filters or programmatically select which sessions to download before retrieving their data.
In this example we provide a complete workflow for analyzing intervention effects. This includes defining clear inclusion and exclusion criteria for subjects, sessions, and clips/trials, applying efficient server-side filtering to narrow down relevant data, and performing additional local filtering in Python for more advanced logic. The script also demonstrates how to identify and extract consecutive sessions surrounding a specific intervention (e.g., sessions immediately before and after), and how to organize and export the resulting dataset to an Excel file for further analysis or reporting. Refer to the script's README section for detailed instructions and explanation.
Prerequisites
Before implementing this example, ensure that you have performed all necessary setup steps. In particular, you should have:
Open pre_vs_post_intervention_query_example.py in Visual Studio Code, follow the instructions detailed in the README section, add all relevant information to the script and save the file
To run the script, press F5 or type python scripts/pre_vs_post_intervention_query_example.py in the terminal and press 'Enter'