momenTUM banner

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.

link to the Backend chapter

Designer

Used by researcher to create momentum studies. Go to the chapter for its documentation.

link to the Designer chapter

Mobile App

Used by study participants to complete tasks. Tasks are defined in momentum studies. Go to the chapter for its documentation.

link to the Mobile App chapter

REDCap instance

Used by researchers to monitor and download the collected data. Go to their website for more information.

link to the REDCap website

MongoDB instance

Backend data storage. Mainly used for mappings between studies and REDCap projects. Go to their website for more information.

link to the MongoDB website

Sketch

architecture-sketch

momenTUM Backend

The Backend was build using the following technologies:

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 a REDCap 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:

backend_interface_sketch

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:

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:

designer_interfaces_sketch.svg

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:

  1. Open Safari
  2. 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:

mobile_app_interfaces_sketch.svg

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).