Introduction
Background
Clinical, biomedical, psychological and other studies with human participants often require the ability to collect data from the participants in an ambulatory fashion using mobile devices. Here, we present the momenTUM Research Platform, which facilitates the task of data collection. This page documents the momenTUM Research Platform for both Developers and Researchers.
NOTE: If you have been invited to participate in a study using the momenTUM Research Platform, please contact the researcher who invited you for support.
Developers
The momenTUM Research Platform is developed at the Technical University of Munich in the Professorship of Chronobiology & Health and at the Max Planck Institute for Biological Cybernetics under the supervision of Prof. Dr. Manuel Spitschan. The developer team consists of Constantin Goeldel, Blen Daniel Assefa and Marco Ma.
Architecture
The momentum platform consists of 5 subsystems. 3 of them are developed by us, the momentum team and have their own documenation chapter in this book. For a visual representation of the whole momentum platform, see the sketch. Detailed documentation of each interface can be found in the respective docs of each subsystem.
Subsystems
Backend
Handles the interaction with the momentum server. Go to the chapter for its documentation.
Designer
Used by researcher to create momentum studies. Go to the chapter for its documentation.
Mobile App
Used by study participants to complete tasks. Tasks are defined in momentum studies. Go to the chapter for its documentation.
REDCap instance
Used by researchers to monitor and download the collected data. Go to their website for more information.
MongoDB instance
Backend data storage. Mainly used for mappings between studies and REDCap projects. Go to their website for more information.
Sketch
momenTUM Backend
The Backend was build using the following technologies:
- Rust - programming language
- Rocket Framework - webserver framework
- mongoDB - database
We recommend getting familiar with these technologies before developing for this project.
Chapters
Modules
The Backend project is structured into modules. Modules are the single Rust files (.rs) inside of the project. Each module groups related codeblocks together.
- main.rs - Entry point for the application
- error.rs - todo: short description
- redcap.rs - Functions for interacting with the REDCap instance
- study.rs - todo: short description
- users.rs - todo: short description
Each module has its own chapter. Click on any one of them to go to jump directly to their chapter.
main
This is a Rust program that defines a web API using the Rocket framework. The API provides endpoints for accessing studies and submitting responses.
The program defines a MongoDB database and a DB struct that represents a connection to this database. The #[database("mongodb")]
attribute macro specifies that the DB struct should use the MongoDB database.
The ACTIVE_DB
is set to the database if the debug_assertions
is true or false.
The Key
struct represents an API key used to access a study. The Study struct represents a study stored in the database.
The program defines the following endpoints:
docs_assets
: serves static files from the /docs/ directory.status
: returns a static string indicating that the API is live.fetch_study
: retrieves a study from the database by ID or by study_id property. Returns the study as JSON or an error if the study is not found.get_study_by_post
: legacy support for retrieving studies using POST requests. all_studies: retrieves all studies from the database. Returns the studies as JSON.all_studies_of_study_id
: retrieves all studies with a given study_id property from the database. Returns the studies as JSON.create_study
: inserts a new study into the database. Expects a Study object in the request body. Returns the ID of the newly created study.save_response
: saves a response to a study in the database. Expects a Response object in the request body.save_log
: saves a log message to the database. Expects a Log object in the request body.create_redcap_project
: creates a new REDCap project and saves the API key to the database. Expects a Study object in the request body and a username parameter in the URL.
error
The error.rs
file contains a Rust module that defines an error handling mechanism for a Rocket web framework application.
The module defines a custom error type, Error, which is an enumeration that represents the possible error states of the application. The #[derive(Debug, Error)]
attribute is used to automatically implement the Debug trait and the Error trait for the Error type. The thiserror crate is used to provide a convenient way to define error types in Rust.
The possible error states are defined using the #[error("error message")]
attribute on each variant of the Error enum. For example, StudyNotFound variant indicates that a study was not found.
The impl<'r> Responder<'r, 'static>
for Error implementation defines how the errors should be responded to by the web server. This allows for easy handling of errors and returning appropriate responses to clients.
The respond_to
method on the Responder trait is implemented to match on the various Error variants and return the appropriate HTTP response code and message.
- For example, the StudyNotFound variant returns a 404 Not Found response with the error message as the body.
redcap
study
users
API
The Backend implements a set of HTTP Routes. They serve as API endpoints that can be used by other applications to interact with the backend. The following routes are defined:
GET
/api/docs/<path..>
: This route is used to retrieve the documentation for the API. The <path..> parameter is used to specify the path to the documentation file./api/v1/status
: This route is used to retrieve the status of the API./api/v1/studies/<study_id>
: This route is used to retrieve a specific study by its ID./api/v1/studies
: This route is used to retrieve a list of all studies./api/v1/studies/all/<study_id>
: This route is used to retrieve all the data associated with a specific study.
POST
/api/v1/studies/<study_id>
: This route is used to update a specific study by its ID.Body
{ }
/api/v1/study
: This route is used to create a new study.Body
{ "data": <potential_study> }
/api/v1/response
: This route is used to submit a response to a study.Body
{ "data": <submission> }
/api/v1/log
: This route is used to log a submission.Body
{ "data": <submission> }
/api/v1/redcap/<username>
: This route is used to import a study from REDCap.Body
{ "data": <study> }
/api/v1/user
: This route is used to create a new user.Body
{ "data": <new_user> }
These routes define the different endpoints that a a researcher or a participant can use to interact with the API.
The HTTP methods used for these routes are GET and POST. GET is used to retrieve data from the server, while POST is used to submit data to the server.
By using these routes, the different components of the web application can work together to provide a complete functionality. The routes define the different parts of the application that can be accessed by the a researcher or a participant, and the components work together to provide the desired functionality.
Deployment
The backend's design includes numerous services orchestrated by docker-compose in the form of docker containers. At the moment, the following services exist:
frontend
directory contains the JSON-maker frontend, written in React + TypeScript.api
directory contains the JSON-maker server, written in RUST with Rocket as the web framework. It includes calling an Redcap instance to aggregate and funnels logs from the app to redcap. It also includes and endpoint for the mongodb.- Caddy, which is a web server and reverse proxy server that offers a load balancer, TLS terminator, and HTTP/2 server.
This is a Docker Compose file defining a multi-container application architecture that consists of three services: caddy
, api
, and web
. The caddy service is based on the lucaslorentz/caddy-docker-proxy:ci-alpine Docker image and serves as a reverse proxy for the other services. It is configured to listen on ports 80 and 443 and uses the CADDY_INGRESS_NETWORKS environment variable to specify the networks it can communicate with. The service is connected to the caddy network and mounts the Docker socket and a volume named caddy_data for storing configuration files.
The api
service is built from the Dockerfile located in the ./api directory and is also connected to the caddy network. It mounts the ./api directory as a volume and is labeled with caddy configuration information. This service listens on port 8000 and is reverse proxied by caddy at the /api/* path.
The web
service is built from the Dockerfile located in the ./frontend directory and is also connected to the caddy network. It listens on port 3000 and is reverse proxied by caddy at the root path. The service is labeled with caddy configuration information as well.
Steps to reprouce
Requirements
For running the backend on your server, you have to install Docker, MongoDB and REDCap.
Note: REDCap
is server software. It is not something that a user installs on a personal computer
Important: The
MongoDB
connection url and aREDCap
super api token are required to continue after this step.
To set the MongoDB
connection url, go inside the \api\Rocket.toml
file and change this variable to your url.
...
[default.databases.mongodb]
url = "mongodb://{YOUR_URL}"
...
To set the REDCap
super api token, then you need to create a .env
file inside the \api\
directory. The \api\.env
file should look like:
REDCAP_SUPER_API_TOKEN=ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ
After installing docker, start your docker
and check if docker is running by running this command:
docker info
If docker is active then run the following script for development:
docker network create caddy
docker compose -f docker-compose.local.yml up --build -d
This will start your services as close to production as possible, just not available at the normal domain but on localhost.
Docker will eat up all your disk space with build cache, so you might want to clean it up from time to time. To do so, run docker system prune
and confirm with y
.
To run manually, install dependencies and run:
cd frontend
pnpm
pnpm dev
pnpm
is used because of the advantages it has on Speed, and to save disk space. You can read more about pnpm
here. To install pnpm, run
npm install -g pnpm
(Note: You might need superuser-rights for this command).
Rust API
Install Rust: https://www.rust-lang.org/learn/get-started
Development
cd api
cargo watch -x run
Production
cd api
cargo run --release
Of course, you can also install dependencies with npm (then the start script is npm run dev). To install yarn, run
npm install -g yarn
(Note: You might need superuser-rights for this command).
Production
When deploying to production, cd into the momenTUM-json-maker directory (clone it first if you don't have it in your userspace), then run
git pull && docker network create caddy && docker compose up -d--build
which will automatically pull the latest changes from GitHub, build the project for production, and start the docker containers. You can then access the project at https://make.momentumresearch.eu. The API is running on https://make.momentumresearch.eu.
Interfaces
The Backend shares an interface with each of the following:
REDCap
The Backend has the following unidirectional interactions with the REDCap API:
- Creating REDCap Projects according to the study schema
- Sending Responses it received from the momenTUM app
mongoDB
Designer
App
Contributing
momenTUM Designer
The Designer was build using the following technologies:
- TypeScript - programming language
- React - frontend library
- ReactFlow - study graph
- ReactJsonSchemaForm - json based form
- Zustand - state management
- TailwindCSS - css framework
- TailwindUI - UI components
We recommend getting familiar with these technologies before developing for this project.
Chapters
Architecture
Components
Deployment
Interfaces
The Designer shares an interface with each of the following:
Researcher
Backend
Local Disk
Study Object
The study object is a complex object with key value pairs according to a certain protocol. The protocol is modelled using a class diagram.
Depending on the programming language, it has been implemented as interface
(TypeScript) and Struct
(Rust).
mermaid diagram
classDiagram
Study "1" -- "1..*" Module
Study "1" -- "1" Properties
Module "1" -- "1" Alerts
Module "1" -- "1" Graph
Module "1" -- "0..1" PvtBody
Module "1" -- "0..1" SurveyBody
Alerts "1" -- "1..*" Time
SurveyBody "1" -- "1..*" Section
Section "1" -- "1..*" Question
Question "1" -- "0..1" Instruction
Question -- "0..1" YesNo
Question -- "0..1" Text
Question -- "0..1" DateTime
Question -- "0..1" Slider
Question -- "0..1" Multi
Question -- "0..1" File
Question -- "0..1" Media
Question -- "0..1" External
class Study {
<<interface>>
properties: Properties,
modules: Module[],
}
class Module {
<<interface>>
type: 'pvt' | 'survey',
name: string,
submit_text: string,
condition: string,
alerts: Alerts,
graph: Graph,
id: string,
unlock_after: string[],
shuffle: boolean,
body: SurveyBody | PvtBody,
}
class Properties {
<<interface>>
study_id: string,
study_name: string,
instructions: string,
banner_url: string,
support_email: string,
support_url: string,
ethics: string,
pls: string,
created_by: string,
empty_msg: string,
post_url: string,
conditions: string[],
cache: boolean,
}
class Alerts {
<<interface>>
title: string,
message: string,
start_offset: number,
duration: number,
times: Time[],
random: boolean,
random_interval: number,
sticky: boolean,
sticky_label: string,
timeout: boolean,
timeout_after: number,
}
class Time {
<<interface>>
hours: number,
minutes: number,
}
class Graph {
<<interface>>
display: boolean,
variable: string,
title: string,
blurb: string,
type: 'bar' | 'line',
max_points: number,
}
class PvtBody {
<<interface>>
type: 'pvt',
trials: number,
min_waiting: number,
max_waiting: number,
show: boolean,
max_reaction: number,
exit: boolean,
}
class SurveyBody {
<<interface>>
type: 'survey',
sections: Section[],
}
class Section {
<<interface>>
id: string,
name: string,
shuffle: boolean,
questions: Question[],
}
class Question {
<<interface>>
id: string,
text: string,
body:
| Instruction
| DateTime
| Multi
| Text
| Slider
| Media
| YesNo
| External
| File,
required: boolean,
hide_id: string,
hide_value: boolean,
hide_if: boolean,
}
class Instruction {
<<interface>>
type: 'instruction',
}
class YesNo {
<<interface>>
type: 'yesno',
yes_text: string,
no_text: string,
}
class Text {
<<interface>>
type: 'text',
subtype: 'short' | 'long' | 'numeric',
}
class DateTime {
<<interface>>
type: 'datetime',
subtype: 'date' | 'time' | 'datetime',
}
class Slider {
<<interface>>
type: 'slider',
min: number,
max: number,
hint_left: string,
hint_right: string,
}
class Multi {
<<interface>>
type: 'multi',
radio: boolean,
modal: boolean,
options: string[],
shuffle: boolean,
}
class File {
<<interface>>
type: 'file',
src: string,
file_name: string,
}
class Media {
<<interface>>
type: 'media',
subtype: 'image' | 'video' | 'audio',
src: string,
thumb: string,
}
class External {
<<interface>>
type: 'external',
src: string,
}
Contributing
momenTUM App
The App was build using the following technologies:
We recommend getting familiar with these technologies before developing for this project.
Chapters
Architecture
Components
Deployment
This document will guide you through the steps for running the app on your machine.
Prerequisites
Before you start, install
or check if you have already installed both by running the following commands in the terminal:
node -v
npm -v
Install the Ionic CLI to use ionic commands:
npm i -g @ionic/cli
Navigate to the momenTUM-app folder and install the dependencies:
cd .../momenTUM-app
npm i
Run a development server with live-reload in your browser:
ionic serve
Native Platforms
The projects for iOS and Android are build using Capacitor. Consider looking into their documentation for more information.
Build and run on iOS
Note: iOS apps can only be developed on macOS with Xcode installed. Make sure the command-line tools are selected for use:
xcode-select --install
Build the native project:
ionic cap build ios
Run the app from Xcode first.
You may need to sign the app before being able to run it. You may also need to create an emulator beforehand.
Once you've managed to run the app in Xcode, you can close Xcode and run it from the command line with live reload functionality:
ionic cap run ios -l
For debugging, you can use the Safari Browser:
- Open Safari
- In the menubar select develop > name_of_emulator > localhost
If you encounter further problems or want more documenation on deployment, read the Ionic docs for iOS development.
Android
Install Android Studio and the Android SDK by opening Android Studio. It will lead you through the installation. Open ~/.bashrc, ~/.bash_profile, or similar bash startup scripts and add the following lines:
export ANDROID_SDK_ROOT=/Path/to/android/sdk
export PATH=$PATH:$ANDROID_SDK_ROOT/tools/bin
export PATH=$PATH:$ANDROID_SDK_ROOT/platform-tools
export PATH=$PATH:$ANDROID_SDK_ROOT/emulator
In Android Studio, open the Virtual Device Manager and create a Virtual Device. Run the Device and keep the emulator running.
Build the android native project by running the following inside of the momenTUM-app directory:
ionic cap build android
Run the app with live-reload:
ionic cap run android -l
For debugging, open chrome://inspect with the Chrome Web Browser.
If you encounter any problems or want further documentation, read the Ionic docs.
Interfaces
The Mobile app shares an interface with each of the following:
Participant
Backend
Data Collection
Contributing
Getting started
Expected behaviour when the participant enrolls in study
Participants should receive notifications from the moment they enrol in the study. This means that when newly enrolled, no pending notifications should appear.
Specification of random interval notifications
Random notifications can be specified in each module for a given study under "Scheduled times" > "Random interval". The random interval describes the time window (in minutes) from the specified alert time, in which a notification can be randomised. Example: if "Alert time" of the module = 15h 30min and "Random interval" = 360 (corresponding to 6h), the notification will be randomised between 09h 30min (15h 30min - 6h) and 21h 30min (15h 30min + 6h).