Artemis: Interactive Learning with Individual Feedback

Main features

Artemis supports the following exercises:

  1. Programming exercises with version control and automatic assessment with test cases and continuous integration

  2. Quiz exercises with multiple choice, drag and drop and short answer quiz questions

  3. Modeling exercises with semi-automatic assessment using machine learning concepts

  4. Textual exercises with manual (and experimental semi-automatic) assessment

  5. File upload exercises with manual assessment

All these exercises are supposed to be run either live in the lecture with instant feedback or as homework. Students can submit their solutions multiple times within the due date and use the (semi-)automatically provided feedback to improve their solution.

Introduction

Exercises

Artemis supports the following exercises:

Programming Exercise

Conducting a programming exercise consists of 7 steps distributed among instructor, Artemis and students:

  1. Instructor prepares exercise: Set up a repository containing the exercise code and test cases, build instructions on the CI server, and configures the exercise in Artemis.

  2. Student starts exercise: Click on start exercise on Artemis which automatically generates a copy of the repository with the exercise code and configures a build plan accordingly.

  3. Optional: Student clones repository: Clone the personalized repository from the remote VCS to the local machine.

  4. Student solves exercise: Solve the exercise with an IDE of choice on the local computer or in the online editor.

  5. Student uploads solution: Upload changes of the source code to the VCS by committing and pushing them to the remote server (or by clicking submit in the online editor).

  6. CI server verifies solution: verify the student’s submission by executing the test cases (see step 1) and provide feedback which parts are correct or wrong.

  7. Student reviews personal result: Reviews build result and feedback using Artemis. In case of a failed build, reattempt to solve the exercise (step 4).

  8. Instructor reviews course results: Review overall results of all students, and react to common errors and problems.

The following activity diagram shows this exercise workflow.

Exercise Workflow

Exercise Workflow

Tutorial: Create Programming Exercises

Online Editor

The following screenshot shows the online code editor with interactive and dynamic exercise instructions on the right side. Tasks and UML diagram elements are referenced by test cases and update their color from red to green after students submit a new version and all test cases associated with a task or diagram element pass. This allows the students to immediately recognize which tasks are already fulfilled and is particularly helpful for programming beginners.

Online Editor

Online Editor

Testing with Artemis Java Test Sandbox

Artemis Java Test Sandbox (abbr. AJTS) is a JUnit 5 extension for easy and secure Java testing on Artemis.

Its main features are

  • a security manager to prevent students crashing the tests or cheating

  • more robust tests and builds due to limits on time, threads and io

  • support for public and hidden Artemis tests, where hidden ones obey a custom deadline

  • utilities for improved feedback in Artemis like processing multiline error messages or pointing to a possible location that caused an Exception

  • utilities to test exercises using System.out and System.in comfortably

For more information see https://github.com/ls1intum/artemis-java-test-sandbox

Using adapters to support multiple VCS

The following UML component diagram shows the details of the Version Control Adapter that allows to connect to multiple Version Control Systems. The other adapters for Continuous Integration and User Management have a similar structure

Version Control Adapter

Version Control Adapter

The Version Control Adapter includes abstract interface definitions. Among others, concrete connectors have to implement the following methods:

+ copyRepository(baseRepository, user)
+ configureRepository(repository, user)
+ deleteRepository(repository)
+ getRepositoryWebUrl(repository)
+ ...

The Continuous Integration Adapter includes abstract interface definitions. Among others, concrete connectors have to implement the following methods:

+ copyBuildPlan(baseBuildPlan, user)
+ configureBuildPlan(buildPlan, repository, user)
+ deleteBuildPlan(buildPlan)
+ onBuildCompleted(buildPlan)
+ getBuildStatus(buildPlan)
+ getBuildDetails(buildPlan)
+ ...

Quiz exercise

Modeling exercise

Textual exercise

File upload exercise

System Design

Top-Level Design

The following diagram shows the top-level design of Artemis which is decomposed into an application client (running as Angular web app in the browser) and an application server (based on Spring Boot). For programming exercises, the application server connects to a version control system (VCS) and a continuous integration system (CIS). Authentication is handled by an external user management system (UMS).

Top-Level Design

Top-Level Design

While Artemis includes generic adapters to these three external systems with a defined protocol that can be instantiated to connect to any VCS, CIS or UMS, it also provides 3 concrete implementations for these adapters to connect to:

  1. VCS: Atlassian Bitbucket Server

  2. CIS: Atlassian Bamboo Server

  3. UMS: Atlassian JIRA Server (more specifically Atlassian Crowd on the JIRA Server)

Deployment

The following UML deployment diagram shows a typical deployment of Artemis application server and application client. Student, Instructor and Teaching Assistant (TA) computers are all equipped equally with the Artemis application client being displayed in the browser.

The Continuous Integration Server typically delegates the build jobs to local build agents within the university infrastructure or to remote build agents, e.g. hosted in the Amazon Cloud (AWS).

Deployment Overview

Deployment Overview

Data Model

The Artemis application server used the following data model in the MySQL database. It supports multiple courses with multiple exercises. Each student in the participating student group can participate in the exercise by clicking the Start Exercise button. Then a repository and a build plan for the student (User) will be created and configured. The initialization state variable (Enum) helps to track the progress of this complex operation and allows to recover from errors. A student can submit multiple solutions by committing and pushing the source code changes to a given example code into the version control system or using the user interface. Each submission is automatically tested by the continuous integration server, which notifies the Artemis application server, when a new result exists. In addition, teaching assistants can assess student solutions and “manually” create results. The current data model is more complex and supports different types of exercises such as programming exercises, modeling exercises, quiz, and text exercises.

Data Model

Data Model

Server Architecture

The following UML component diagram shows more details of the Artemis application server architecture and its REST interfaces to the application client.

Server Architecture

Server Architecture

Setup Guide

In this guide you learn how to setup the development environment of Artemis. Artemis is based on JHipster, i.e. Spring Boot development on the application server using Java 15, and TypeScript development on the application client in the browser using Angular and Webpack. To get an overview of the used technology, have a look at the JHipster Technology stack and other tutorials on the JHipster homepage.

You can find tutorials how to setup JHipster in an IDE (IntelliJ IDEA Ultimate is recommended) on https://jhipster.github.io/configuring-ide. Note that the Community Edition of IntelliJ IDEA does not provide Spring Boot support (see the comparison matrix). Before you can build Artemis, you must install and configure the following dependencies/tools on your machine:

  1. Java JDK: We use Java (JDK 15) to develop and run the Artemis application server which is based on Spring Boot.

  2. MySQL Database Server 8: Artemis uses Hibernate to store entities in a MySQL database. Download and install the MySQL Community Server (8.0.x) and configure the ‘root’ user with an empty password. (In case you want to use a different password, make sure to change the value in application-dev.yml and in liquibase.gradle). The required Artemis scheme will be created / updated automatically at startup time of the server application. Alternatively, you can run the MySQL Database Server inside a Docker container using e.g. docker-compose -f src/main/docker/mysql.yml up

  3. Node.js: We use Node (>=13.12.0) to compile and run the client Angular application. Depending on your system, you can install Node either from source or as a pre-packaged bundle.

  4. Yarn: We use Yarn 1.x (>=1.22.0) to manage client side Node dependencies. Depending on your system, you can install Yarn either from source or as a pre-packaged bundle. To do so, please follow the instructions on the Yarn installation page.

Server Setup

To start the Artemis application server from the development environment, first import the project into IntelliJ and then make sure to install the Spring Boot plugins to run the main class de.tum.in.www1.artemis.ArtemisApp. Before the application runs, you have to configure the file application-artemis.yml in the folder src/main/resources/config.

artemis:
    repo-clone-path: ./repos/
    repo-download-clone-path: ./repos-download/
    encryption-password: <encrypt-password>     # arbitrary password for encrypting database values
    user-management:
        use-external: true
        external:
            url: https://jira.ase.in.tum.de
            user: <username>    # e.g. ga12abc
            password: <password>
            admin-group-name: tumuser
        ldap:
            url: <url>
            user-dn: <user-dn>
            password: <password>
            base: <base>
    version-control:
        url: https://bitbucket.ase.in.tum.de
        user: <username>    # e.g. ga12abc
        password: <password>
        secret: <token>                 # VCS API token giving Artemis full Admin access. Not needed for Bamboo+Bitbucket
        ci-token: <token from the CI>   # Token generated by the CI (e.g. Jenkins) for webhooks from the VCS to the CI. Not needed for Bamboo+Bitbucket
    continuous-integration:
        url: https://bamboo.ase.in.tum.de
        user: <username>    # e.g. ga12abc
        password: <password>
        vcs-application-link-name: LS1 Bitbucket Server     # If the VCS and CI are directly linked (normally only for Bitbucket + Bamboo)
        empty-commit-necessary: true                        # Do we need an empty commit for new exercises/repositories in order for the CI to register the repo
        # Hash/key of the ci-token, equivalent e.g. to the ci-token in version-control
        # Some CI systems, like Jenkins, offer a specific token that gets checked against any incoming notifications
        # from a VCS trying to trigger a build plan. Only if the notification request contains the correct token, the plan
        # is triggered. This can be seen as an alternative to sending an authenticated request to a REST API and then
        # triggering the plan.
        # In the case of Artemis, this is only really needed for the Jenkins + GitLab setup, since the GitLab plugin in
        # Jenkins only allows triggering the Jenkins jobs using such a token. Furthermore, in this case, the value of the
        # hudson.util.Secret is stored in the build plan, so you also have to specify this encrypted string here and NOT the actual token value itself!
        # You can get this by GETting any job.xml for a job with an activated GitLab step and your token value of choice.
        secret-push-token: <token hash>
        # Key of the saved credentials for the VCS service
        # Bamboo: not needed
        # Jenkins: You have to specify the key from the credentials page in Jenkins under which the user and
        #          password for the VCS are stored
        vcs-credentials: <credentials key>
        # Key of the credentials for the Artemis notification token
        # Bamboo: not needed
        # Jenkins: You have to specify the key from the credentials page in Jenkins under which the notification token is stored
        notification-token: <credentials key>
        # The actual value of the notification token to check against in Artemis. This is the token that gets send with
        # every request the CI system makes to Artemis containing a new result after a build.
        # Bamboo: The token value you use for the Server Notification Plugin
        # Jenkins: The token value you use for the Server Notification Plugin and is stored under the notification-token credential above
        authentication-token: <token>
    lti:
        id: artemis_lti
        oauth-key: artemis_lti_key
        oauth-secret: <secret>    # only important for online courses on the edX platform, can typically be ignored
        user-prefix-edx: edx_
        user-prefix-u4i: u4i_
        user-group-name-edx: edx
        user-group-name-u4i: u4i
    git:
        name: Artemis
        email: artemis@in.tum.de
    automatic-text:
        segmentation-url: http://localhost:8000/segment
        material-upload-url: http://localhost:8001/upload
        embedding-url: http://localhost:8001/embed
        embedding-chunk-size: 50
        clustering-url: http://localhost:8002/cluster
        secret: null

Change all entries with <...> with proper values, e.g. your TUM Online account credentials to connect to the given instances of JIRA, Bitbucket and Bamboo. Alternatively, you can connect to your local JIRA, Bitbucket and Bamboo instances. It’s not necessary to fill all the fields, most of them can be left blank. Note that there is additional information about the setup for programming exercises provided:

Setup for Programming Exercises with Bamboo, Bitbucket and Jira

This page describes how to set up a programming exercise environment based on Bamboo, Bitbucket and Jira.

Please note that this setup will create a deployment that is very similiar to the one used in production but has one difference:
In production, the builds are performed within Docker containers that are created by Bamboo (or its build agents). As we run Bamboo in a Docker container in this setup, creating new Docker containers within that container is not recommended (e.g. see this article). There are some solution where one can pass the Docker socket to the Bamboo container, but none of these approachs work quite well here as Bamboo uses mounted directories that cause issues.

Therefore, a check is included within the BambooBuildPlanService that ensures that builds are not started in Docker agents if the development setup is present.

Prerequisites:

Docker-Compose

Before you start the docker-compose, check if the bamboo version in the build.gradle (search for com.atlassian.bamboo:bamboo-specs) is equal to the bamboo version number in the Dockerfile of bamboo stored in src/main/docker/bamboo/Dockerfile. If the version number is not equal adjust the version number in the Dockerfile.

Execute the docker-compose file atlassian.yml stored in src/main/docker e.g. with docker-compose -f src/main/docker/atlassian.yml up -d

Error Handling: It can happen that there is an overload with other docker networks ERROR: Pool overlaps with other one on this address space. Use the command docker network prune to resolve this issue.

Make also sure that docker has enough memory (~ 6GB). To adapt it, go to Preferecences -> Resources

Configure Bamboo, Bitbucket and Jira

By default, the Jira instance is reachable under localhost:8081, the Bamboo instance under localhost:8085 and the Bitbucket instance under localhost:7990.

Get evaluation licenses for Atlassian products: Atlassian Licenses

  1. Create an admin user with the same credentials in all 3 applications. Create a sample project in Jira. Also, you can select the evaluation/internal/test/dev setups if you are asked. Select a Bitbucket (Server) license if asked. Do not connect Bitbucket with Jira yet.

  2. Execute the shell script atlassian-setup.sh in the src/main/docker directory (e.g. with src/main/docker/./atlassian-setup.sh). This script creates groups, users ([STRIKEOUT:and adds them to the created groups] NOT YET) and disabled application links between the 3 applications
  3. Enable the created application links between all 3 application (OAuth Impersonate). The links should open automatically after the shell script has finished. If not open them manually:

You manually have to adjust the Display URL for the Bamboo → Bitbucket AND Bitbucket → Bamboo URl to http://localhost:7990 and http://localhost:8085 .

Bamboo:

_images/bamboo_bitbucket_applicationLink.png

Bamboo → Bitbucket

_images/bamboo_jira_applicationLink.png

Bamboo → Jira

Bitbucket:

_images/bitbucket_bamboo_applicationLink.png

Bitbucket → Bamboo

_images/bitbucket_jira_applicationLink.png

Bitbucket → Jira

Jira:

_images/jira_bamboo_applicationLink.png

Jira → Bamboo

_images/jira_bitbucket_applicationLink.png

Jira → Bitbucket

  1. The script has already created users and groups but you need to manually assign the users into their respective group in Jira. In our test setup, users 1-5 are students, 6-10 are tutors and 11-15 are instructors. The usernames are artemis_test_user_{1-15} and the password is again the username. When you create a course in artemis you have to manually choose the created groups(students, tutors, instructors).

  2. Use the user directories in Jira to synchronize the users in bitbucket and bamboo:

  • Go to Jira → User management → Jira user server → Add application → Create one application for bitbucket and one for bamboo → add the IP-address 0.0.0.0/0 to IP Addresses

    _images/jira_add_application.png
  • Go to Bitbucket and Bamboo → User Directories → Add Directories → Atlassian Crowd → use the URL http://jira:8080 as Server URL → use the application name and password which you used in the previous step. Also, you should decrease the synchronisation period (e.g. to 2 minutes). Press synchronise after adding the directory, the users and groups should now be available.

    _images/user_directories.png

6. In Bamboo create a global variable named SERVER_PLUGIN_SECRET_PASSWORD, the value of this variable will be used as the secret. The value of this variable should be then stored in src/main/resources/config/application-artemis.yml as the value of artemis-authentication-token-value.

7. Download the bamboo-server-notifaction-plugin and add it to bamboo. Go to Bamboo → Manage apps → Upload app → select the downloaded .jar file → Upload

  1. Add Maven and JDK:

  • Go to Bamboo → Server capabilities → Add capabilities menu → Capability type Executable → select type Maven 3.x → insert Maven 3 as executable label → insert /artemis as path.

  • Add capabilities menu → Capability type JDK → insert JDK 15 as JDK label → insert /usr/lib/jvm/java-15-oracle as Java home.

Configure Artemis
  1. Modify src/main/resources/config/application-artemis.yml

repo-clone-path: ./repos/
repo-download-clone-path: ./repos-download/
encryption-password: artemis-encrypt     # arbitrary password for encrypting database values
user-management:
    use-external: true
    external:
        url: http://localhost:8081
        user:  <jira-admin-user>
        password: <jira-admin-password>
        admin-group-name: instructors
    internal-admin:
        username: artemis_admin
        password: artemis_admin
version-control:
    url: http://localhost:7990
    user:  <bitbucket-admin-user>
    password: <bitbuckt-admin-password>
continuous-integration:
    url: http://localhost:8085
    user:  <bamboo-admin-user>
    password: <bamboo-admin-password>
    vcs-application-link-name: LS1 Bitbucket Server
    empty-commit-necessary: true
    artemis-authentication-token-value: <artemis-authentication-token-value>
  1. Modify the application-dev.yml

server:
    port: 8080                                         # The port of artemis
    url: http://172.20.0.1:8080                        # needs to be an ip

In addition, you have to start Artemis with the profiles bamboo, bitbucket and jira so that the correct adapters will be used, e.g.:

--spring.profiles.active=dev,bamboo,bitbucket,jira,artemis

Please read Setup Guide for more details.

How to verify the connection works?
Artemis → Jira

You can login to Artemis with the admin user you created in Jira

Artemis → Bitbucket

You can create a programming exercise

Artemis → Bamboo

You can create a programming exercise

Bitbucket → Bamboo

The build of the students repository gets started after pushing to it

Bitbucket → Artemis

When using the code editor, after clicking on Submit, the text Building and testing… should appear.

Bamboo → Artemis

The build result is displayed in the code editor.

Setup for Programming Exercises with Jenkins and GitLab

This page describes how to set up a programming exercise environment based on Jenkins and GitLab. Optional commands are in curly brackets {}.

The following assumes that all instances run on separate servers. If you have one single server, or your own NGINX instance, just skip all NGINX related steps and use the configurations provided under Separate NGINX Configurations

If you want to setup everything on your local machine, you can also just ignore all NGINX related steps. Just make sure that you use unique port mappings for your Docker containers (e.g. 80 for GitLab, 8080 for Jenkins, 8081 for Artemis)

Prerequisites:

Artemis

In order to use Artemis with Jenkins as Continuous Integration Server and Gitlab as Version Control Server, you have to configure the file application-prod.yml (Production Server) or application-artemis.yml (Local Development) accordingly. Please note that all values in <..> have to be configured properly. These values will be explained below in the corresponding sections.

artemis:
    repo-clone-path: ./repos/
    repo-download-clone-path: ./repos-download/
    encryption-password: artemis-encrypt     # arbitrary password for encrypting database values
    user-management:
        use-external: false
        internal-admin:
            username: artemis_admin
            password: artemis_admin
    version-control:
        url: <https://gitlab-url>
        user: <gitlab-admin-user>
        password: <gitlab-admin-password>
        secret: <secret>
        ci-token: <ci-token>
    continuous-integration:
        url: <https://jenkins-url>
        user: <jenkins-admin-user>
        password: <jenkins-admin-password>
        empty-commit-necessary: false
        secret-push-token: <secret push token>
        vcs-credentials: <vcs-credentials>
        artemis-authentication-token-key: <artemis-authentication-token-key>
        artemis-authentication-token-value: <artemis-authentication-token-value>

In addition, you have to start Artemis with the profiles gitlab and jenkins so that the correct adapters will be used, e.g.:

--spring.profiles.active=dev,jenkins,gitlab,artemis

Please read Setup Guide for more details.

Make sure to change the server.url value in application-dev.yml or application-prod.yml accordingly. This value will be used for the communication hooks from Gitlab to Artemis and from Jenkins to Artemis. In case you use a different port than 80 (http) or 443 (https) for the communication, you have to append it to the server.url value, e.g. 127.0.0.1:8081.

When you start Artemis for the first time, it will automatically create an admin user based on the default encryption password specified in the yml file above. In case you want to use a different encryption password, you can insert users manually into the jhi_user table. You can use Jasypt Online Encryption Tool to generate encryption strings. Use Two Way Encryption (With Secret Text).

GitLab
Gitlab Server Setup
  1. Pull the latest GitLab Docker image

    docker pull gitlab/gitlab-ce:latest
    
Start Gitlab
  1. Run the image (and change the values for hostname and ports). Add -p 2222:22 if cloning/pushing via ssh should be possible. As Gitlab runs in a docker container and the default port for SSH (22) is typically used by the host running Docker, we change the port Gitlab uses for SSH to 2222. This can be adjusted if needed.

    Make sure to remove the comments from the command before running it and that docker has enough memory (~ 6GB). To adapt it, go to Preferecences -> Resources

    docker run -itd --name gitlab \
        --hostname your.gitlab.domain.com \   # Specify the hostname
        --restart always \
        -m 3000m \                            # Optional argument to limit the memory usage of Gitlab
        -p 2222:22 \                          # Remove this if cloning via SSH should not be supported
        -p 80:80 -p 443:443 \                 # Alternative 1: If you are NOT running your own NGINX instance
        -p <some port of your choosing>:80 \  # Alternative 2: If you ARE running your own NGINX instance
        -v gitlab_data:/var/opt/gitlab \
        -v gitlab_logs:/var/log/gitlab \
        -v gitlab_config:/etc/gitlab \
        gitlab/gitlab-ce:latest
    
  2. Wait a couple of minutes until the container is deployed and GitLab is set up, then open the instance in you browser and set a first admin password of your choosing. You can then login using the username “root” and you password.

  3. We recommend to rename the “root” admin user to “artemis”. To rename the user, click on the image on the top right and select “Settings”. Now select “Account” on the left and change the username. Use the same password in the Artemis configuration file application-artemis.yml

    artemis:
        version-control:
            user: artemis
            password: the.password.you.chose
    
  4. If you run your own NGINX, then skip the next steps (6-7)

  5. Configure Gitlab to automatically generate certificates using LetsEncrypt. Edit the Gitlab configuration

    docker exec -it gitlab /bin/bash
    nano /etc/gitlab/gitlab.rb
    

    And add the following part

    letsencrypt['enable'] = true                          # GitLab 10.5 and 10.6 require this option
    external_url "https://your.gitlab.domain.com"         # Must use https protocol
    letsencrypt['contact_emails'] = ['gitlab@your.gitlab.domain.com'] # Optional
    
    nginx['redirect_http_to_https'] = true
    nginx['redirect_http_to_https_port'] = 80
    
  6. Reconfigure gitlab to generate the certificate.

    # Save your changes and finally run
    gitlab-ctl reconfigure
    

    If this command fails, try using

    gitlab-ctl renew-le-certs
    
  7. Login to GitLab using the Artemis admin account and go to the profile settings (upper right corned → Settings)

    _images/gitlab_setting_button.png
Gitlab Access Token
  1. Go to Access Tokens

_images/gitlab_access_tokens_button.png
  1. Create a new token named “Artemis” and give it all rights.

_images/artemis_gitlab_access_token.png
  1. Copy the generated token and insert it into the Artemis configuration file application-artemis.yml

    artemis:
        version-control:
            token: your.generated.api.token
    
  2. (Optional) Allow outbound requests to local network

    There is a known limitation for the local setup: webhook URLs for the communication between Gitlab and Artemis and between Gitlab and Jenkins cannot include local IP addresses. This option can be deactivate in Gitlab on <https://gitlab-url>/admin/application_settings/network → Outbound requests. Another possible solution is to register a local URL, e.g. using ngrok, to be available over a domain the Internet.

  3. Adjust the monitoring-endpoint whitelist. Run the following command

    docker exec -it gitlab /bin/bash
    

    Then edit the Gitlab configuration

    nano /etc/gitlab/gitlab.rb
    

    Add the following lines

    gitlab_rails['monitoring_whitelist'] = ['0.0.0.0/0']
    gitlab_rails['gitlab_shell_ssh_port'] = 2222
    

    This will disable the firewall for all IP addresses. If you only want to allow the server that runs Artemis to query the information, replace 0.0.0.0/0 with ARTEMIS.SERVER.IP.ADRESS/32

    If you use SSH and use a different port than 2222, you have to adjust the port above.

  4. Disable prometheus. As we encountered issues with the prometheus log files not being deleted and therefore filling up the disk space, we decided to disable prometheus within Gitlab. If you also want to disable prometheus, edit the configuration again using

    nano /etc/gitlab/gitlab.rb
    

    and add the following line

    prometheus_monitoring['enable'] = false
    

    The issue with more details can be found here.

Reconfigure Gitlab

gitlab-ctl reconfigure
Upgrade GitLab

You can upgrade GitLab by downloading the latest Docker image and starting a new container with the old volumes:

docker stop gitlab
docker rename gitlab gitlab_old
docker pull gitlab/gitlab-ce:latest

See https://hub.docker.com/r/gitlab/gitlab-ce/ for the latest version. You can also specify an earlier one.

Start a GitLab container just as described in Start-Gitlab and wait for a couple of minutes. GitLab should configure itself automatically. If there are no issues, you can delete the old container using docker rm gitlab_old and the olf image (see docker images) using docker rmi <old-image-id>.

Jenkins
Jenkins Server Setup
  1. Pull the latest Jenkins LTS Docker image

    sudo docker pull jenkins/jenkins:lts
    
  2. Create a custom docker image

    Run the following command to get the latest jenkins LTS docker image.

    In order to install and use Maven with Java in the Jenkins container, you have to first install maven, then download Java and finally configure Maven to use Java instead of the default version.

    To perform all these steps automatically, you can prepare a Docker image:

    Create a dockerfile with the content found here <src/main/docker/jenkins/Dockerfile>. Copy it in a file named Dockerfile, e.g. in the folder /opt/jenkins/ using vim Dockerfile.

    Now run the command docker build --no-cache -t jenkins-artemis .

    This might take a while because Docker will download Java, but this is only required once.

  3. Run steps 4-6 only if you are not using a separate instance, otherwise continue with Start-Jenkins.

  4. Create a file increasing the maximum file size for the nginx proxy. The nginx-proxy uses a default file limit that is too small for the plugin that will be uploaded later. Skip this step if you have your own NGINX instance.

    echo "client_max_body_size 16m;" > client_max_body_size.conf
    
  5. Run the NGINX proxy docker container, this will automatically setup all reverse proxies and force https on all connections. (This image would also setup proxies for all other running containers that have the VIRTUAL_HOST and VIRTUAL_PORT environment variables). Skip this step if you have your own NGINX instance.

    docker run -itd --name nginx_proxy \
        -p 80:80 -p 443:443 \
        --restart always \
        -v /var/run/docker.sock:/tmp/docker.sock:ro \
        -v /etc/nginx/certs \
        -v /etc/nginx/vhost.d \
        -v /usr/share/nginx/html \
        -v $(pwd)/client_max_body_size.conf:/etc/nginx/conf.d/client_max_body_size.conf:ro \
        jwilder/nginx-proxy
    
  6. The nginx proxy needs another docker-container to generate letsencrypt certificates. Run the following command to start it (make sure to change the email-address). Skip this step if you have your own NGINX instance.

    docker run --detach \
        --name nginx_proxy-letsencrypt \
        --volumes-from nginx_proxy \
        --volume /var/run/docker.sock:/var/run/docker.sock:ro \
        --env "DEFAULT_EMAIL=mail@yourdomain.tld" \
        jrcs/letsencrypt-nginx-proxy-companion
    
Start Jenkins
  1. Run Jenkins by executing the following command (change the hostname and choose which port alternative you need)

    docker run -itd --name jenkins \
        --restart always \
        -v jenkins_data:/var/jenkins_home \
        -v /var/run/docker.sock:/var/run/docker.sock \
        -e VIRTUAL_HOST=your.jenkins.domain -e VIRTUAL_PORT=8080 \    # Alternative 1: If you are NOT using a separate NGINX instance
        -e LETSENCRYPT_HOST=your.jenkins.domain \                     # Only needed if Alternative 1 is used
        -p <some port of your choosing>:8080 \                        # Alternative 2: If you ARE using a separate NGINX instance
        jenkins-artemis
    

    For jenkins to be able to read data from the volume you might need to allow the jenkins user to read the jenkins_data folder. One way to do that is transfer the ownership to the user with id 1000 which is normally the user the jenkins process runs with.

    sudo chown -R 1000 jenkins_data/
    
  2. Wait until the docker container has started and Jenkins is running.

  3. Run the following commands to navigate into the docker container and check the Maven and JDK version

    sudo docker exec -it jenkins /bin/bash
    
    mvn -version
    

    This should print Maven 3.x as Maven version, Java 15 as Java version and /usr/lib/jvm/java-15-openjdk-amd64 as Java home.

  4. Open Jenkins in your browser (e.g. localhost:8080) and setup the admin user account (install all suggested plugins). You can get the initial admin password using the following command.

    # Jenkins highlights the password in the logs, you can't miss it
    docker logs -f jenkins
    or alternatively
    docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
    
  5. Set the chosen credentials in the Artemis configuration application-artemis.yml

    artemis:
        continuous-integration:
            user: your.chosen.username
            password: your.chosen.password
    
  6. Setup JDK 15 in Jenkins Settings

    Navigate in your browser into Jenkins → Manage Jenkins → Global Tool Configuration → JDK. Change the existing JDK installation or click on Add JDK.

    Use OpenJDK 15 as Name and /usr/lib/jvm/java-15-openjdk-amd64 as JAVA_HOME

_images/jenkins_jdk_config.png
Required Jenkins Plugins

You will need to install the following plugins (apart from the recommended ones that got installed during the setup process):

1. GitLab for enabling webhooks to and from GitLab 2. Multiple SCMs for combining the exercise test and assignment repositories in one build 3. Post Build Task for preparing build results to be exported to Artemis 4. Xvfb for exercises based on GUI libraries, for which tests have to have some virtual display 5. Timestamper for adding the time to every line of the build output (Timestamper might already be installed)

Choose “Download now and install after restart” and checking the “Restart Jenkins when installation is complete and no jobs are running” box

Timestamper Configuration

Go to Manage Jenkins → Configure System. There you will find the Timestamper configuration, use the following value for both formats:

'<b>'yyyy-MM-dd'T'HH:mm:ssX'</b> '
_images/timestamper_config.png
Server Notification Plugin

Artemis needs to receive a notification after every build, which contains the test results and additional commit information. For that purpose, we developed a Jenkins plugin, that can aggregate and POST JUnit formatted results to any URL.

You can download the current release of the plugin here (Download the .hpi file). Go to the Jenkins plugin page (Manage Jenkins → Manage Plugins) and install the downloaded file under the Advanced tab under Upload Plugin

_images/jenkins_custom_plugin.png
Jenkins Credentials

Go to Credentials → Jenkins → Global credentials and create the following credentials

GitLab API Token
  1. Create a new access token in GitLab named “Jenkins” and give it api rights and read_repository rights. For detailed instructions on how to create such a token follow Gitlab Access Token.

    _images/gitlab_jenkins_token_rights.png
  2. Copy the generated token and create new Jenkins credentials:

    1. Kind: GitLab API token

    2. API token: your.copied.token

    3. Leave the ID field blank

    4. The description is up to you

  3. Go to the Jenkins settings Manage Jenkins → Configure System. There you will find the GitLab settings. Fill in the URL of your GitLab instance and select the just created API token in the credentials dropdown. After you click on “Test Connection”, everything should work fine.

    _images/jenkins_gitlab_configuration.png
Server Notification Token
  1. Create a new Jenkins credential containing the token, which gets send by the server notification plugin to Artemis with every build result:

    1. Kind: Secret text

    2. Secret: your.secret_token_value (choose any value you want, copy it for the nex step)

    3. Leave the ID field blank

    4. The description is up to you

  2. Copy the generated ID of the new credentials and put it into the Artemis configuration application-artemis.yml

    artemis:
        continuous-integration:
            artemis-authentication-token-key: the.id.of.the.notification.token.credential
    
  3. Copy the actual value you chose for the token and put it into the Artemis configuration application-artemis.yml

    artemis:
        continuous-integration:
            artemis-authentication-token-value: the.actual.value.of.the.notification.token
    
GitLab Repository Access
  1. Create a new Jenkins credentials containing the username and password of the GitLab administrator account:

    1. Kind: Username with password

    2. Scope: global

    3. Username: the_username_you_chose_for_the_gitlab_admin_user

    4. Password: the_password_you_chose_for_the_gitlab_admin_user

    5. Leave the ID field blank

    6. The description is up to you

  2. Copy the generated ID (e.g. ea0e3c08-4110-4g2f-9c83-fb2cdf6345fa) of the new credentials and put it into the Artemis configuration file application-artemis.yml

    artemis:
        continuous-integration:
            vcs-credentials: the.id.of.the.username.and.password.credentials.from.jenkins
    
GitLab to Jenkins push notification token

GitLab has to notify Jenkins build plans if there are any new commits to the repository. The push notification that gets sent here is secured by a by Jenkins generated token. In order to get this token, you have to do the following steps:

  1. Create a new item in Jenkins (use the Freestyle project type) and name it TestProject

  2. In the project configuration, go to Build Triggers → Build when a change is pushed to GitLab and activate this option

  3. Click on Advanced.

  4. You will now have a couple of new options here, one of them being a “Secret token”.

  5. Click on the “Generate” button right below the text box for that token.

  6. Copy the generated value, let’s call it $gitlab-push-token

  7. Apply these change to the plan (i.e. click on Apply)

_images/jenkins_test_project.png
  1. Perform a GET request to the following URL (e.g. with Postman) using Basic Authentication and the username and password you chose for the Jenkins admin account:

    GET https://your.jenkins.domain/job/TestProject/config.xml
    
  2. You will get the whole configuration XML of the just created build plan, there you will find the following tag:

    <secretToken>{$some-long-encrypted-value}</secretToken>
    
_images/jenkins_project_config_xml.png

Job configuration XML

  1. Copy the value of :math:`some-long-encrypted-value without the curly brackets!. This is the encrypted value of the `gitlab-push-token you generated in step 5.

  2. Now, you can delete this test project and input the following values into your Artemis configuration application-artemis.yml (replace the placeholders with the actual values you wrote down)

    artemis:
        version-control:
            ci-token: $gitlab-push-token
        continuous-integration:
            secret-push-token: $some-long-encrytped-value
    
  3. In a local setup, you might want to disable CSRF by going to: “Manage Jenkins” - “Configure Global Security” and uncheck “Prevent Cross Site Request Forgery exploits”. Also disable the option use-crumb in application-jenkins.yml.

    Depending on the version this setting might not be available anymore. Have a look `here<https://unix.stackexchange.com/questions/444177/how-to-disable-the-csrf-protection-in-jenkins-by-default>`_ on how you can disable CSRF protection.

Upgrade Jenkins

Build the latest version of the jenkins-artemis Docker image, stop the running container and mount the Jenkins data volume to the new LTS container. Make sure to perform this command in the folder where the Dockerfile was created (e.g. /opt/jenkins/):

docker stop jenkins
docker rename jenkins jenkins_old
docker build --no-cache -t jenkins-artemis .

Now start a new Jenkins container just as described in Start Jenkins.

Jenkins should be up and running again. If there are no issues, you can delete the old container using docker rm jenkins_old and the old image (see docker images) using docker rmi <old-image-id>.

You should also update the Jenkins plugins regularly due to security reasons. You can update them directly in the Web User Interface in the Plugin Manager.

Separate NGINX Configurations

There are some placeholders in the following configurations. Replace them with your setup specific values ### GitLab

server {
    listen 443 ssl http2;
    server_name your.gitlab.domain;
    ssl_session_cache shared:GitLabSSL:10m;
    include /etc/nginx/common/common_ssl.conf;
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
    add_header X-Frame-Options DENY;
    add_header Referrer-Policy same-origin;
    client_max_body_size 10m;
    client_body_buffer_size 1m;

    location / {
        proxy_pass              http://localhost:<your exposed GitLab HTTP port (default 80)>;
        proxy_read_timeout      300;
        proxy_connect_timeout   300;
        proxy_http_version      1.1;
        proxy_redirect          http://         https://;

        proxy_set_header    Host                $http_host;
        proxy_set_header    X-Real-IP           $remote_addr;
        proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Proto   $scheme;

        gzip off;
    }
}
Jenkins
server {
    listen 443 ssl http2;
    server_name your.jenkins.domain;
    ssl_session_cache shared:JenkinsSSL:10m;
    include /etc/nginx/common/common_ssl.conf;
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
    add_header X-Frame-Options DENY;
    add_header Referrer-Policy same-origin;
    client_max_body_size 10m;
    client_body_buffer_size 1m;

    location / {
        proxy_pass              http://localhost:<your exposed Jenkins HTTP port (default 8080)>;
        proxy_set_header        Host                $host:$server_port;
        proxy_set_header        X-Real-IP           $remote_addr;
        proxy_set_header        X-Forwarded-For     $proxy_add_x_forwarded_for;
        proxy_set_header        X-Forwarded-Proto   $scheme;
        proxy_redirect          http://             https://;

        # Required for new HTTP-based CLI
        proxy_http_version 1.1;
        proxy_request_buffering off;
        proxy_buffering off; # Required for HTTP-based CLI to work over SSL

        # workaround for https://issues.jenkins-ci.org/browse/JENKINS-45651
        add_header 'X-SSH-Endpoint' 'your.jenkins.domain.com:50022' always;
    }

    error_page 502 /502.html;
    location /502.html {
        root /usr/share/nginx/html;
        internal;
    }
}
/etc/nginx/common/common_ssl.conf

If you haven’t done so, generate the DH param file: sudo openssl dhparam -out /etc/nginx/dhparam.pem 4096

ssl_certificate     <path to your fullchain certificate>;
ssl_certificate_key <path to the private key of your certificate>;
ssl_protocols       TLSv1.2 TLSv1.3;
ssl_dhparam /etc/nginx/dhparam.pem;
ssl_prefer_server_ciphers   on;
ssl_ciphers ECDH+CHACHA20:EECDH+AESGCM:EDH+AESGCM:!AES128;
ssl_ecdh_curve secp384r1;
ssl_session_timeout  10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver <if you have any, specify them here> valid=300s;
resolver_timeout 5s;

#Deployment Artemis / GitLab / Jenkins using Docker on Local machine

Execute the following steps in addition to the ones described above:

Preparation
  1. Create a Docker network named “artemis” with docker network create artemis

Gitlab
  1. Add the Gitlab container to the created network with docker network connect artemis gitlab

  2. Get the URL of the Gitlab container with the first IP returned by docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' gitlab

  3. Use this IP in the application-artemis.yml file at artemis.version-control.url

Jenkins
  1. Add the Jenkins container to the created network with docker network connect artemis jenkins

  2. Get the URL of the Gitlab container with the first IP returned by docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' jenkins

  3. Use this IP in the application-artemis.yml file at artemis.continuous-integration.url

Artemis
  1. In docker-compose.yml

    1. Change the 8080:8080 port to 8081:8081 because Jenkins is using the port 8080

    2. Change the SPRING_PROFILES_ACTIVE to dev,jenkins,gitlab,artemis

  2. In src/main/resources/config/application-dev.yml

    1. At spring.profiles.active: add & gitlab & jenkins

    2. At spring.liquibase: add the new property change-log: classpath:config/liquibase/master.xml

    3. At server: change port to 8081 and

  3. Run docker-compose up

  4. After the container has been deployed run docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' artemis_artemis-server and copy the first resulting IP.

  5. In src/main/resources/config/application-dev.yml at server: change the port to 8081 and at url: paste the copied IP

  6. Stop the Artemis docker container with Control-C and re-run docker-compose up

Note

Be careful that you don’t commit changes to application-artemis.yml. To avoid this, follow the best practice when configuring your local development environment:

  1. Create a file named application-local.yml under src/main/resources/config.

  2. Copy the contents of application-artemis.yml into the new file.

  3. Update configuration values in application-local.yml.

By default, changes to application-local.yml will be ignored by git so you don’t accidentally share your credentials or other local configuration options.

If you use a password, you need to adapt it in gradle/liquibase.gradle.

Run the server via a run configuration in IntelliJ

The project comes with some pre-configured run / debug configurations that are stored in the .idea directory. When you import the project into IntelliJ the run configurations will also be imported.

The recommended way is to run the server and the client separated. This provides fast rebuilds of the server and hot module replacement in the client.

  • Artemis (Server): The server will be started separated from the client. The startup time decreases significantly.

  • Artemis (Client): Will execute yarn install and yarn start. The client will be available at http://localhost:9000/ with hot module replacement enabled (also see Client Setup).

Other run / debug configurations
  • Artemis (Server & Client): Will start the server and the client. The client will be available at http://localhost:8080/ with hot module replacement disabled.

  • Artemis (Server, Jenkins & Gitlab): The server will be started separated from the client with the profiles dev,jenkins,gitlab,artemis instead of dev,bamboo,bitbucket,jira,artemis.

  • Artemis (Server, Text Clustering): The server will be started separated from the client with automaticText profile enabled (see Text Assessment Clustering Service).

Typical problems with Liquibase checksums

One typical problem in the development setup is that an exception occurs during the database initialization. Artemis uses Liquibase to automatically upgrade the database scheme after changes to the data model. This ensures that the changes can also be applied to the production server. In case you encounter errors with liquibase checksum values, run the following command in your terminal / command line:

./gradlew liquibaseClearChecksums
Run the server with Spring Boot and Spring profiles

The Artemis server should startup by running the main class de.tum.in.www1.artemis.ArtemisApp using Spring Boot.

Note

Artemis uses Spring profiles to segregate parts of the application configuration and make it only available in certain environments. For development purposes, the following program arguments can be used to enable the dev profile and the profiles for JIRA, Bitbucket and Bamboo:

--spring.profiles.active=dev,bamboo,bitbucket,jira,artemis,scheduling

If you use IntelliJ (Community or Ultimate) you can set the active profiles by

  • Choosing Run | Edit Configurations...

  • Going to the Configuration Tab

  • Expanding the Environment section to reveal VM Options and setting them to

-Dspring.profiles.active=dev,bamboo,bitbucket,jira,artemis,scheduling

Set Spring profiles with IntelliJ Ultimate

If you use IntelliJ Ultimate, add the following entry to the section Active Profiles (within Spring Boot) in the server run configuration:

dev,bamboo,bitbucket,jira,artemis,scheduling
Run the server with the command line (Gradle wrapper)

If you want to run the application via the command line instead, make sure to pass the active profiles to the gradlew command like this:

./gradlew bootRun --args='--spring.profiles.active=dev,bamboo,bitbucket,jira,artemis,scheduling'

As an alternative, you might want to use Jenkins and Gitlab with an internal user management in Artemis, then you would use the profiles:

dev,jenkins,gitlab,artemis,scheduling

Client Setup

You need to install Node and Yarn on your local machine.

Using IntelliJ

If you are using IntelliJ you can use the pre-configured Artemis (Client) run configuration that will be delivered with this repository:

  • Choose Run | Edit Configurations...

  • Select the Artemis (Client) configuration from the npm section

  • Now you can run the configuration in the upper right corner of IntelliJ

Using the command line

You should be able to run the following command to install development tools and dependencies. You will only need to run this command when dependencies change in package.json.

yarn install

To start the client application in the browser, use the following command:

yarn start

This compiles TypeScript code to JavaScript code, starts the hot module replacement feature in Webpack (i.e. whenever you change a TypeScript file and save, the client is automatically reloaded with the new code) and will start the client application in your browser on http://localhost:9000. If you have activated the JIRA profile (see above in Server Setup) and if you have configured application-artemis.yml correctly, then you should be able to login with your TUM Online account.

For more information, review Working with Angular. For further instructions on how to develop with JHipster, have a look at Using JHipster in development.

Customize your Artemis instance

You can define the following custom assets for Artemis to be used instead of the TUM defaults:

  • The logo next to the “Artemis” heading on the navbar → ${artemisRunDirectory}/public/images/logo.png

  • The favicon → ${artemisRunDirectory}/public/images/favicon.ico

  • The privacy statement HTML → ${artemisRunDirectory}/public/content/privacy_statement.html

  • The contact email address in the application-{dev,prod}.yml configuration file under the key info.contact

  • The imprint link in the application-{dev,prod}.yml configuration file under the key info.imprint

Alternative: Using docker-compose

A full functioning development environment can also be set up using docker-compose:

  1. Install docker and docker-compose

  2. Configure the credentials in application-artemis.yml in the folder src/main/resources/config as described above

  3. Run docker-compose up

  4. Go to http://localhost:9000

The client and the server will run in different containers. As yarn is used with its live reload mode to build and run the client, any change in the client’s codebase will trigger a rebuild automatically. In case of changes in the codebase of the server one has to restart the artemis-server container via docker-compose restart artemis-server.

(Native) Running and Debugging from IDEs is currently not supported.

Get a shell into the containers:
  • app container: docker exec -it $(docker-compose ps -q artemis-app) sh

  • mysql container: docker exec -it $(docker-compose ps -q artemis-mysql) mysql

Other useful commands:
  • Stop the server: docker-compose stop artemis-server (restart via docker-compose start artemis-server)

  • Stop the client: docker-compose stop artemis-client (restart via docker-compose start artemis-client)

Text Assessment Clustering Service

The semi-automatic text assessment relies on the Athene service. To enable automatic text assessments, special configuration is required:

Enable the automaticText Spring profile:
--spring.profiles.active=dev,bamboo,bitbucket,jira,artemis,scheduling,automaticText
Configure API Endpoints:

The Athene service is running on a dedicated machine and is adressed via HTTP. We need to extend the configuration in the file src/main/resources/config/application-artemis.yml like so:

artemis:
  # ...
  automatic-text:
    segmentation-url: http://localhost:8000/segment
    material-upload-url: http://localhost:8001/upload
    embedding-url: http://localhost:8001/embed
    embedding-chunk-size: 50
    clustering-url: http://localhost:8002/cluster
    secret: null

Setup Guide for Guided Tutorials in Artemis

This guide gives you instructions on how to setup and create guided tutorials for Artemis:

Create GuidedTour object

A guided tutorial can be created by instantiating a GuidedTour object. This object has the mandatory attributes settingsKey, the identifier for the tutorial which will be stored in the database, and steps, which is an array that stores all tutorial steps. A tutorial can have different types of tutorial steps:

  1. TextTourStep: tutorial step with only text content

  2. ImageTourStep: tutorial step with text content and embedded image

  3. VideoTourStep: tutorial step with text content and embedded video

  4. UserInteractionTourStep: tutorial step which requires a certain interaction from the user to proceed to the next step.

  5. ModelingTaskTourStep: tutorial step with text content and modeling task for the Apollon editor that is assessed for the step

  6. AssessmentTaskTourStep: tutorial step with text content and a tutor assessment task for example submissions (currently only implemented for text assessments).

TextTourStep with highlighted element
TextTourStep with highlighted element

TextTourStep with highlighted element

TextTourStep with multiple markdown elements
TextTourStep with multiple markdown elements

TextTourStep with multiple markdown elements

ImageTourStep
ImageTourStep

ImageTourStep

VideoTourStep
VideoTourStep

VideoTourStep

UserInteractionTourStep
UserInteractionTourStep

UserInteractionTourStep

ModelingTaskTourStep
ModelingTaskTourStep

ModelingTaskTourStep

AssessmentTaskTourStep
AssessmentTaskTourStep

AssessmentTaskTourStep

Example implementation of a GuidedTour object

In this example, the GuidedTour object is created and assigned to the constant exampleTutorial, which one can use to embed the tutorial to a component of choice.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import { Orientation, UserInteractionEvent } from '../../src/main/webapp/app/guided-tour/guided-tour.constants';
import { GuidedTour } from '../../src/main/webapp/app/guided-tour/guided-tour.model';
import { ImageTourStep, ModelingTaskTourStep, TextTourStep, VideoTourStep } from '../../src/main/webapp/app/guided-tour/guided-tour-step.model';
import { GuidedTourModelingTask, personUML } from '../../src/main/webapp/app/guided-tour/guided-tour-task.model';

export const exampleTutorial: GuidedTour = {
    settingsKey: 'example_tutorial',
    steps: [
        new TextTourStep({
            highlightSelector: '.guided-tour-overview',
            headlineTranslateKey: 'tour.courseOverview.overviewMenu.headline',
            contentTranslateKey: 'tour.courseOverview.overviewMenu.content',
            highlightPadding: 10,
            orientation: Orientation.BOTTOM,
        }),
        new ImageTourStep({
            headlineTranslateKey: 'tour.courseOverview.welcome.headline',
            subHeadlineTranslateKey: 'tour.courseOverview.welcome.subHeadline',
            contentTranslateKey: 'tour.courseOverview.welcome.content',
            imageUrl: 'https://ase.in.tum.de/lehrstuhl_1/images/teaching/interactive/InteractiveLearning.png',
        }),
        new VideoTourStep({
            headlineTranslateKey: 'tour.courseExerciseOverview.installPrerequisites.sourceTreeSetup.headline',
            contentTranslateKey: 'tour.courseExerciseOverview.installPrerequisites.sourceTreeSetup.content',
            hintTranslateKey: 'tour.courseExerciseOverview.installPrerequisites.sourceTreeSetup.hint',
            videoUrl: 'tour.courseExerciseOverview.installPrerequisites.sourceTreeSetup.videoUrl',
        }),
        new ModelingTaskTourStep({
            highlightSelector: 'jhi-modeling-editor .guided-tour.modeling-editor .modeling-editor',
            headlineTranslateKey: 'tour.modelingExercise.executeTasks.headline',
            contentTranslateKey: 'tour.modelingExercise.executeTasks.content',
            highlightPadding: 5,
            orientation: Orientation.TOP,
            userInteractionEvent: UserInteractionEvent.MODELING,
            modelingTask: new GuidedTourModelingTask(personUML.name, 'tour.modelingExercise.executeTasks.personClass'),
        }),
        // ...
    ],
};

Mandatory attributes

  1. TextTourStep: The mandatory fields are headlineTranslateKey and contentTranslateKey.

  2. ImageTourStep: The ImageTourStep extends the TextTourStep and has imageUrl as an additional mandatory attribute.

  3. VideoTourStep: The VideoTourStep extends the TextTourStep and has videoUrl as an additional mandatory attribute.

  4. UserInterActionTourStep: The UserInterActionTourStep extends the TextTourStep and is used to include interactions tasks for the user during the tour step. It has the additional mandatory attribute userInteractionEvent, which defines the interaction type, and the optional attribute triggerNextStep.

  5. ModelingTaskTourStep: The ModelingTaskTourStep extends the UserInterActionTourStep and has modelingTask as an additional mandatory attribute.

  6. AssessmentTaskTourStep: The AssessmentTaskTourStep extends the UserInterActionTourStep and has assessmentTask as an additional mandatory attribute.

Optional attributes

There are many optional attributes that can be defined for a tour step. These attributes and their definition can be found in the abstract class TourStep. Below, you can find a list of attributes that are used more often:

  1. highlightSelector: For the highlightSelector you have to enter a CSS selector for the HTML element that you want to highlight for this step. For better maintainability of the guided tutorials, it is strongly advised to create new selectors with the prefix guided-tour within the DOM and use it as the highlight selector.

  2. orientation: We can define an orientation for every tour step individually. The tour step orientation is used to define the position of the tour step next to highlighted element.

  3. highlightPadding: This attribute sets the additional padding around the highlight element.

  4. userInteractionEvent: Some steps require user interactions, e.g. certain click events, before the next tour step can be enabled. The supported user interactions are defined in the enum UserInteractionEvent.

  5. pageUrl: If you want to create a multi-page tutorial, i.e. a tutorial that guides the user through multiple component pages, then you have to use this attribute. The pageUrl should be added to the first tutorial step of every page and if the URL has identifiers in the URL such as course or exercise ids then these numbers should be replaced with the regex (\d+)+. An example of multi-page tutorials can be found in the tutor-assessment-tour.ts file.

Add translations

In order to allow internationalization, the values for the attributes headlineTranslateKey, subHeadlineTranslateKey, contentTranslateKey and hintTranslateKey reference the text snippets which are stored in JSON translation document. Further attributes that need translations are videoUrl for VideoTourStep and taskTranslateKey for the modelingTask in the ModelingTaskTourStep. One JSON document that is used for the translations of guided tutorials is the file guidedTour.json.

Embed in component file

There are multiple service methods to embed a guided tutorial in an application component file. We use the GuidedTutorialService in the component through dependency injection and invoke the fitting method to enable the tutorial for the component:

The enableTourForCourseOverview method is used when the tutorial should be enabled for a certain course in a component, which displays a list of courses (e.g. overview.component.ts). It returns the course for which the tutorial is enabled, if available, otherwise null.

public enableTourForCourseOverview(courses: Course[], guidedTour: GuidedTour, init: boolean): Course | null {

The enableTourForCourseExerciseComponent method is used when the tutorial should be enabled for a certain course and exercise in a component, which displays a list of exercises for a course (e.g. course-exercises.component.ts). It returns the exercise for which the tutorial is enabled, if available, otherwise null.

public enableTourForCourseExerciseComponent(course: Course | null, guidedTour: GuidedTour, init: boolean): Exercise | null {

The enableTourForExercise method is used when the tutorial should be enabled for a certain exercise (e.g. course-exercise-details.component.ts). It returns the exercise for which the tutorial is enabled, if available, otherwise null.

public enableTourForExercise(exercise: Exercise, guidedTour: GuidedTour, init: boolean) {
Example of integrating the GuidedTour exampleTutorial into a component file
constructor( private guidedTourService: GuidedTourService ) {}

...

this.courseForGuidedTour = this.guidedTourService.enableTourForCourseOverview(this.courses, exampleTutorial, true);

Extend configuration file

The mapping of guided tutorials to certain courses and exercises is configured in the application-dev.yml and application-prod.yml files. The yaml configuration below shows that the guided tutorials are only enabled for the course with the short name artemistutorial. The configuration for tours shows a list of mappings tutorialSettingsKeyexerciseIdentifier. The exerciseIdentifier for programming exercises is the exercise short name, otherwise it’s the exercise title.

info:
    guided-tour:
        courseShortName: 'artemistutorial'
        tours:
            - cancel_tour: ''
            - code_editor_tour: 'tutorial'
            - course_overview_tour: ''
            - course_exercise_overview_tour: 'tutorial'
            - modeling_tour: 'UML Class Diagram'
            - programming_exercise_fail_tour: 'tutorial'
            - programming_exercise_success_tour: 'tutorial'
            - tutor_assessment_tour: 'Patterns in Software Engineering'

Writing test cases for guided tutorials

Through Jest client tests it is possible to start the guided tutorials and go through all the tutorial steps while checking for the highlight selectors. An example test suite for the courseOverviewTour can be found in the overview.component.spec.ts file.