How To’s#

Deployment#

Deployment using docker-compose#

In the repository there is a directory .docker that contains a docker-compose file. Starting a TaskManager is as simple as:

$ cd .docker
$ docker-compose build
$ docker-compose up

By default the TaskManager will be reachable at localhost:5000 and the account admin, password admin, will be available. The configuration of the TaskManager can be done by setting environment variables in the docker-compose file. For the list of variables to are used see the Configuration section.

Deployment using kubernetes#

Setting up a TaskManager on you own computer in K8s is as simple as executing a helm install:

$ kubectl create namespace taskmanager
$ helm install registry.gitlab.com/radiology/infrastructure/task-manager:6.6.0

The Task Manager should now be running on localhost/taskmanager and is ready to be setup.

Note

The repository contains helm charts for deployment using kubernetes. They can be found in the charts subdirectory.

Configuration values for kubernetes#

The following config values that map to environment variables are defined in the values.yaml:

Environment variable

Config value in Helm Charts

TASKMANAGER_PORT

taskmanager.port

GUNICORN_WORKER_CLASS

taskmanager.gunicorn.workerClass

GUNICORN_WORKER_CONNECTIONS

taskmanager.gunicorn.workerConnections

GUNICORN_TIMEOUT

taskmanager.gunicorn.timeout

TASKMAN_INSTANCE_NAME

taskmanager.instanceName

TASKMAN_PROJECT_NAME

taskmanager.projectName

DB_SCHEMA

taskmanager.db.schema

DB_NAME

taskmanager.db.name

DB_USER

taskmanager.db.user

DB_PASS

taskmanager.db.password

DB_HOST

taskmanager.db.host

DB_PORT

taskmanager.db.port

SECURITY_REGISTERABLE

taskmanager.security.registerable

SECURITY_CHANGEABLE

taskmanager.security.changable

SECURITY_RECOVERABLE

taskmanager.security.recoverable

SECURITY_CONFIRMABLE

taskmanager.security.confirmable

SECRET_KEY

taskmanager.security.secretKey

SECURITY_PASSWORD_SALT

taskmanager.security.passwordSalt

PROJECT_REPO

taskmanager.project.git.url

PROJECT_BRANCH

taskmanager.project.git.branch

PROJECT_RELATIVE_PATH

taskmanager.project.git.relativePath

For the explanation of these environment variables please see the Configuration section.

Setting up a development environment for kubernetes#

You need a number of tools to start developing for the TaskManager on kubernetes locally.

Once you have docker, k3d, helm and Tilt installed a kubernetes cluster need to be created. There is a Makefile that will create a basic cluster using k3d:

$ make up

The a development environment can be started by using the Tiltfile in the repository root:

$ tilt up

The TaskManager will be available at localhost/taskmanager by default. The admin account will be set to admin, password supersecretpassword.

To stop running the system use the following commands:

$ tilt down
$ make down

The first command stops all TaskManager containers and the second command clean up the kubernetes cluster.

Setting up a local development environment#

If you want to set up a local development environment for either developing or just testing the TaskManager, a database and rabbitmq need to be provided.

The database needs to be initialized, for example for MySQL/mariadb:

# Go the mysql command line (add the -p if you have set a root password).
$ sudo mysql (-p)

# Create user
mysql> CREATE USER '<username>'@'localhost' IDENTIFIED BY '<password>';

# Create database
mysql> CREATE DATABASE taskmanager;

# Grant all permissions of the database to the user.
mysql> GRANT ALL ON taskmanager.* TO '<username>'@'localhost'; ``

The <username> and <password> needs to be replaced with a username and password of your choice.

Then you can create a .env in the directory from which you want to run the TaskManager. A minimal example (matching the above database creation) would be:

FLASK_APP="taskmanager"
SQLALCHEMY_DATABASE_URI = "mysql+pymysql://<username>:<password>@localhost/taskmanager"
SECURITY_PASSWORD_SALT="<password salt>"
SECRET_KEY="<secret key>"

Note

This example uses mysql/mariadb in the SQLALCHEMY_DATABASE_URI, not that the correct drivers for the target database needs to be selected. For Postgres we tested using psycopg2.

The password salt and secret key are used for security purposes. The password salt is described in the FlaskSecurity documentation The secret key is described in the Flask documentation The TaskManager needs to be installed in your environment before you can run it, this can simply be done from pypi using pip or from sources in case you want to develop TaskManager. Installation from pypi using pip is as simples as:

pip install taskmanager

If you want to develop from source we assume that you know how to install a Python package yourself. Now everything is set up, starting the app can be done using Flask:

$ flask run

Manual installation#

Warning

The manual installation is STRONGLY discouraged, please consider using Kubernetes or docker-compose instead.

This section explains how to install the TaskManager manually without using containers. Note that this is somewhat outdated and uses MySQL as a database backend. It is possible to use Postgres, but this is not documented here.

Prerequisites#

The first step is to install all requirements to install the TaskManager

apt-get install -y build-essential python-dev python-pip mercurial mysql-server libmysqlclient-dev nginx
pip install virtualenv
adduser taskman
su taskman
(taskman)$ mkdir ~/taskmanager && cd ~/taskmanager
(taskman)$ git clone https://gitlab.com/radiology/infrastructure/task-manager.git
(taskman)$ virtualenv --prompt="(taskman)" venv
(taskman)$ ln -s venv/bin/activate
(taskman)$ source activate
(taskman)$ cd taskmanager
(taskman)$ pip install -e .

Mysql database initialization#

Create a database and a user, and grant permissions.

mysql -p  #[use -p if you have set a root password, which you should done anyway]
mysql> CREATE USER 'taskmanager'@'localhost' IDENTIFIED BY 'blaat123';
mysql> CREATE DATABASE taskmanager;
mysql> GRANT ALL ON taskmanager.* TO 'taskmanager'@'localhost';
(taskman)$ python ./create_config.py -d taskmanager -u taskmanager -p blaat123
(taskman)$ taskmanager-db-init [migrations should be implemented]

Initialize TaskManager db#

(taskman)$ taskmanager-db-init
(taskman)$ taskmanager-config taskmanager/tests/config/test_config.json
(taskman)$ taskmanager-test-tasks
(taskman)$ taskmanager-test-taskgroup

Running a test instance of the TaskManger#

(taskman)$ flask run

Deploy on a production server#

(taskman)$ pip install -r requirements_production.txt
service nginx start
rm /etc/nginx/sites-enabled/default
cp resources/nginx/taskmanager /etc/nginx/sites-available/taskmanager
ln -s /etc/nginx/sites-available/taskmanager /etc/nginx/sites-enabled/taskmanager

Startup scripts#

Find out which process management system you are using: `stat /proc/1/exe`. If this outputs something along the lines of ‘/lib/systemd/systemd’ skip to the systemd version otherwise you probably are running on an upstart system. See [this StackExchange post](http://unix.stackexchange.com/questions/196166/how-to-find-out-if-a-system-uses-sysv-upstart-or-systemd-initsystem) for more details. Below are configurations for the upstart and systemd provided. (Make sure you should only follow 1 of those).

systemd#

There is script that is called from the systemd unit in `resources/systemd/taskmanager-run`. If you have changed the install location in the first steps please make sure to update this file accordingly.

cp resources/systemd/taskmanager.service /etc/systemd/system/taskmanager.service
systemctl enable taskmanager.service
systemctl start taskmanager
systemctl restart nginx

upstart#

If you have changed the install location in the first steps please make sure to update the upstart file accordingly.

cp resources/upstart/taskmanager.conf /etc/systemd/system/taskmanager.conf
initctl reload-configuration
service taskmanager start
service nginx restart

You can now reach the TaskManager on port 80 from your network.

Add authentication to the TaskManager (optional)#

You can add Basic Authentication to the TaskManager by creating credentials and uncommenting 2 lines from the provided nginx config

apt install apache2-utils
htpasswd -c /etc/nginx/.htpasswd username

Uncomment auth_basic ... and auth_basic_user_file ... in the nginx config (/etc/nginx/sites-available/taskmanager).

systemctl restart nginx

Add sentry.io to the TaskManager (optional)#

Install the dependencies in the TaskManager virtualenv:

(taskman)$ pip install raven[flask]

Create a drop-in configuration for the systemd unit:

mkdir /etc/systemd/system/taskmanager.service.d
touch /etc/systemd/system/taskmanager.service.d/environment.conf
vim /etc/systemd/system/taskmanager.service.d/environment.conf

Add the following to the file and close it:

[service]
Environment="SENTRY_DSN=YOUR_SENTRY_DSN"
systemctl daemon-reload
systemctl restart taskmanager

Developing and installing a task session#

Let’s consider a relatively simple application, as an example to give more insight in the workflow of the TaskManager. Suppose you want to assign the task to a group of users to carry out a few measurements in an MR sequence of the brain. And suppose that these measurements can be done by indicating a set of anatomical landmarks in the MR data. The 3D coordinates of these landmarks can then be used afterwards to calculate some anatomical distances in the brain. Let’s consider that these measurements should be carried out in a cohort of 1000 patients. This measurement application can be realized with (for example) the following steps:

1. Add the group of users to the selected TaskManager.#

This means that the group should be defined in the TaskManager (if it is not there already), the users should be defined (if they are not there already), and these specific users should be added to (become a member of) the group just created/defined.

Adding a new group and new users is done using the REST API. To add a user, click on POST   /users. You will see an example of the input object:

{
  "username": "string",
  "name": "string",
  "active": true,
  "email": "string",
  "password": "string",
  "assignment_weight": 0
}

Click on “Try it out”. After that you can edit the six fields shown above. Fill in the username, name (real name of the person, the email address, some password, and the assignment_weight. The assignement weight is an indication of how many tasks (of the total number available) will be shown to this user. If the assignment_weight is 0, no task will be assigned to that specific user. If the assignment_weight equals 1, all tasks will be assigned (shown) to that user. If the user is a group member and the tasks are assigned to the group, also other users can pick up the task. If the assignment_weight is for example 0.1, only 10 percent of the uploaded tasks (assigned to the group) will be shown to this specific user. If a user is inactive (active == false), he or she is currently not involved in carrying out tasks. After filling in all the desired in a correct way, click on ‘execute’. Repeat this procedure for every new user you want to add.

To define a new group, click on ‘POST group’. You will see the following example model:

{
  "groupname": "string",
  "name": "string"
}

Fill in (edit) the “groupname” (used as referring ‘id’) and also give a more descriptive “name” in the second field. The click on execute. Now you are at the fase of making the new users a member of the group. At the moment this is not yet possible with the REST API. It should be done from ipython or by executing python scripts directly on the database.

Note

Adding a user to a group (make him/her a group member) is currently not yet possible with the REST API. For now it should done directly from the ipython command line or by python scripts.

2. Write the task_template#

Writing a JSON file with the ‘description/definition’ of the TaskManager task_template, needed to give the ViewR the optimal appearance in which the desired measurements can be carried out.

Details on how to write a viewr task template can be found at ViewR template documentation.

3. Upload the task_template to the TaskManager#

This means adding the template, mentioned/written in step 2 to the collection of templates known by (stored in) the TaskManager. This can be done using the REST API, but it is more convenient to use another approach, namely the TaskManager command line scripts mentioned before. If you use the API you have to click on ‘POST /task_templates’. You will see a model like this:

{
  "label": "string",
  "content": {}
}

You have to fill in the label. That’s just the name of the task_template that will be used as a kind of referred name/id. The contents should be the complete template (JSON) as shown in step 2. It is more convenient to put this contents into a JSON file, and upload the file with command line scripts (executables). For more details see the section Task Manager commands from the command line.

4. Write the specific task needed#

This means writing a JSON file with the overall ‘description/definition’ of the task to be carried out by the users. This task has to be linked to the desired template. In general a ‘task’ is less complex than a ‘template’ to write. An important issue to pay attention to is that the task should be linked to a template. This doesn’t mean only that the name of the task_template that should be used for this task should be filled in to the model (JSON file), but also that specific field in the task and task_template should be coherent and consistently linked to each other. More details will follow using the next example.

In this (example) case, the task could look this:

{
    "scan_sources": {
        "T1":"http://10.20:30:40/data/experiments/E1234/scans/T1W/resources/NIFTI/files/T1W.nii.gz"
    },
    "_vars": {
        "SYSTEM_URL": "http://10.20:3:4",
        "EXPERIMENT_ID": "E1234",
        "LABEL": "L100"
    },
    "name": "L100 pinealis",
    "fields": {},
    "template": "glandula_pinealis",
    "fields_file": "http://10.20:3:4/data/experiments/E1234/resources/FIELDS/files/out{timestamp}.json",
    "windowing": {
    "width": 50.0,
    "center": 100.0
    }
}

Important in this task example is the definition of the scan_sources. In this example only one MR sequence will be used and shown in the ViewR during execution of the task. The MR sequence is a T1-weighted scan. The URL after T1: points to the location on a specific XNAT host where the input data (T1) for this task can be retrieved.

If you look at the template definition above, you will see that there is a field scans. Also there is only 1 sequence defined, a T1. The protocol and modality are additional info.

Note

Keep in mind the name(s) of the input scan(s), in this case T1 should be equal (consistent) in both the task.json and the template.json! If not the task will not run properly when loaded in the ViewR

5. Upload the tasks to the TaskManager#

This means that for every dataset, specific MR sequence present in the cohort of 1000 patients, a task should be defined and uploaded to the Task Manager. This can almost be done using the REST API. With the REST API you have to post all 1000 tasks (one measurement task per data set) one by one. It is therefore more convenient to use the Task Manager command line scripts.

6. Apply the measurements#

This means: start the ViewR (as one of the users) and connect it to the TaskManager containing the info generated in step 1 to 5. This will be described in more detail in the following section.

Items to be discussed#

  • Roles and privileges (at least user and admin)

  • Description of the model Task

  • How to create a Task

  • How to assign a Task to a user

  • How to upload a Task to the TaskManager

  • How to link a Task with a Template

  • Status of a Task (queued, locked, done, etc)

  • Relation ViewR, Task Manager, XNAT

  • How to deal with Tasks in de ViewR

  • TaskGroups, TaskTemplates, TaskTags

  • Creating a group and/or adding a user to the group is still difficult.

  • Relation taskmodel with usermodel

  • Usergroups

  • Assignment weights

  • Installation of the TaskManager? With a VirtualEnv? Or is that not needed?

  • REST-API! … Flask … What can you post put get delete etc.

  • How to connect the viewer to a specific Task Manager

  • Callback to the Syncotron.

  • With PUT you can change particular contents of tasks

  • With status you can make a task done (REST API)

  • Exploring the contents of the TaskManager

  • Roles! What can a user do … and what can admin do … and intermediate … superuser