From 2b910b9de08a041de9c769eabfba41707de0f472 Mon Sep 17 00:00:00 2001
From: Konstantin Schulz <schulzkx@hu-berlin.de>
Date: Tue, 2 Jun 2020 15:13:22 +0200
Subject: [PATCH] documentation for the REST API is now complete

---
 mc_backend/csm/app/api/textcomplexityAPI.py   |   5 +-
 mc_backend/csm/csm_api.yaml                   |   2 +-
 .../mcserver/app/api/exerciseListAPI.py       |  11 +-
 mc_backend/mcserver/app/api/fileAPI.py        |   5 -
 mc_backend/mcserver/app/api/validReffAPI.py   |   8 +-
 .../app/services/customCorpusService.py       |  10 +-
 .../mcserver/app/services/networkService.py   |  13 +-
 .../app/services/textComplexityService.py     |   6 +-
 mc_backend/mcserver/config.py                 |   4 +
 mc_backend/mcserver/mcserver_api.yaml         |  58 +-
 mc_backend/mcserver/models_auto.py            |  18 +-
 mc_backend/mocks.py                           |   2 +-
 .../controllers/default_controller.py         |   6 +-
 .../openapi/openapi_server/models/__init__.py |   7 +-
 .../openapi_server/models/annis_response.py   |   2 +-
 .../annis_response_frequency_analysis.py      | 122 ----
 ...ercise_all_of.py => exercise_extension.py} | 110 ++-
 .../models/exercise_form_comp.py              | 356 ----------
 .../openapi_server/models/frequency_item.py   |   2 +-
 .../models/inline_response200.py              |  94 ---
 .../inline_response200_frequency_analysis.py  | 122 ----
 .../inline_response200_text_complexity.py     | 462 ------------
 .../openapi_server/models/learning_result.py  | 672 ------------------
 .../{exercise.py => matching_exercise.py}     | 266 ++++---
 .../models/matching_exercise_all_of.py        |  94 +++
 .../openapi/openapi_server/models/node.py     | 360 ----------
 .../models/text_complexity_form.py            |  14 +-
 .../models/text_complexity_form_base.py       | 126 ----
 ...2.py => text_complexity_form_extension.py} |  54 +-
 .../openapi_server/models/update_info.py      | 132 ----
 .../openapi_server/models/vocabulary.py       |  44 --
 .../openapi_server/openapi/openapi.yaml       | 148 ++--
 .../test/test_default_controller.py           | 284 +++++++-
 mc_backend/openapi_models.yaml                | 131 ++--
 mc_backend/tests.py                           |   9 +-
 mc_frontend/openapi/api/default.service.ts    |  24 +-
 ...{exerciseAllOf.ts => exerciseExtension.ts} |   8 +-
 mc_frontend/openapi/model/exerciseFormComp.ts |  62 --
 mc_frontend/openapi/model/learningResult.ts   | 107 ---
 .../{exercise.ts => matchingExercise.ts}      |  17 +-
 ...cyAnalysis.ts => matchingExerciseAllOf.ts} |  17 +-
 mc_frontend/openapi/model/models.ts           |   7 +-
 mc_frontend/openapi/model/node.ts             |  60 --
 .../openapi/model/textComplexityForm.ts       |  19 +-
 .../openapi/model/textComplexityForm2.ts      |  29 -
 .../openapi/model/textComplexityFormBase.ts   |  29 -
 ...e200.ts => textComplexityFormExtension.ts} |  11 +-
 mc_frontend/openapi/model/updateInfo.ts       |  40 --
 mc_frontend/openapi/model/vocabulary.ts       |  25 -
 mc_frontend/openapi/model/vocabularyMC.ts     |   2 +-
 .../author-detail/author-detail.page.spec.ts  |   6 +-
 .../src/app/author/author.page.spec.ts        |   3 +-
 mc_frontend/src/app/corpus.service.spec.ts    |  22 +-
 .../exercise-parameters.page.html             |   4 +-
 .../exercise-parameters.page.spec.ts          |   4 +-
 .../exercise-parameters.page.ts               |  24 +-
 mc_frontend/src/app/models/corpusMC.ts        |  15 +-
 mc_frontend/src/app/models/enum.ts            |   8 +-
 mc_frontend/src/app/models/mockMC.ts          |   6 +-
 mc_frontend/src/app/models/sentence.ts        |   8 -
 mc_frontend/src/app/models/textComplexity.ts  |  21 -
 mc_frontend/src/app/preview/preview.page.html |   6 +-
 mc_frontend/src/app/preview/preview.page.ts   |   4 +-
 .../src/app/ranking/ranking.page.spec.ts      |   5 +-
 mc_frontend/src/app/ranking/ranking.page.ts   |   3 +-
 .../src/app/semantics/semantics.page.ts       |   8 +-
 .../src/app/show-text/show-text.page.html     |   4 +-
 .../src/app/show-text/show-text.page.ts       |   3 +-
 .../app/text-range/text-range.page.spec.ts    |  54 +-
 .../vocabulary-check.page.spec.ts             |   4 +-
 .../vocabulary-check/vocabulary-check.page.ts |   2 +-
 .../src/app/vocabulary.service.spec.ts        |   5 +-
 mc_frontend/src/app/vocabulary.service.ts     |   7 +-
 mc_frontend/src/assets/i18n/de.json           |   1 +
 mc_frontend/src/assets/i18n/en.json           |   1 +
 75 files changed, 1012 insertions(+), 3432 deletions(-)
 delete mode 100644 mc_backend/openapi/openapi_server/models/annis_response_frequency_analysis.py
 rename mc_backend/openapi/openapi_server/models/{exercise_all_of.py => exercise_extension.py} (61%)
 delete mode 100644 mc_backend/openapi/openapi_server/models/exercise_form_comp.py
 delete mode 100644 mc_backend/openapi/openapi_server/models/inline_response200.py
 delete mode 100644 mc_backend/openapi/openapi_server/models/inline_response200_frequency_analysis.py
 delete mode 100644 mc_backend/openapi/openapi_server/models/inline_response200_text_complexity.py
 delete mode 100644 mc_backend/openapi/openapi_server/models/learning_result.py
 rename mc_backend/openapi/openapi_server/models/{exercise.py => matching_exercise.py} (62%)
 create mode 100644 mc_backend/openapi/openapi_server/models/matching_exercise_all_of.py
 delete mode 100644 mc_backend/openapi/openapi_server/models/node.py
 delete mode 100644 mc_backend/openapi/openapi_server/models/text_complexity_form_base.py
 rename mc_backend/openapi/openapi_server/models/{text_complexity_form2.py => text_complexity_form_extension.py} (59%)
 delete mode 100644 mc_backend/openapi/openapi_server/models/update_info.py
 delete mode 100644 mc_backend/openapi/openapi_server/models/vocabulary.py
 rename mc_frontend/openapi/model/{exerciseAllOf.ts => exerciseExtension.ts} (84%)
 delete mode 100644 mc_frontend/openapi/model/exerciseFormComp.ts
 delete mode 100644 mc_frontend/openapi/model/learningResult.ts
 rename mc_frontend/openapi/model/{exercise.ts => matchingExercise.ts} (88%)
 rename mc_frontend/openapi/model/{annisResponseFrequencyAnalysis.ts => matchingExerciseAllOf.ts} (54%)
 delete mode 100644 mc_frontend/openapi/model/node.ts
 delete mode 100644 mc_frontend/openapi/model/textComplexityForm2.ts
 delete mode 100644 mc_frontend/openapi/model/textComplexityFormBase.ts
 rename mc_frontend/openapi/model/{inlineResponse200.ts => textComplexityFormExtension.ts} (61%)
 delete mode 100644 mc_frontend/openapi/model/updateInfo.ts
 delete mode 100644 mc_frontend/openapi/model/vocabulary.ts
 delete mode 100644 mc_frontend/src/app/models/sentence.ts
 delete mode 100644 mc_frontend/src/app/models/textComplexity.ts

diff --git a/mc_backend/csm/app/api/textcomplexityAPI.py b/mc_backend/csm/app/api/textcomplexityAPI.py
index f0791a1..5cc8002 100644
--- a/mc_backend/csm/app/api/textcomplexityAPI.py
+++ b/mc_backend/csm/app/api/textcomplexityAPI.py
@@ -1,3 +1,4 @@
+import rapidjson as json
 from mcserver.app.models import AnnisResponse, TextComplexity
 from mcserver.app.services import NetworkService, CorpusService, TextComplexityService
 from openapi.openapi_server.models import TextComplexityForm
@@ -5,7 +6,7 @@ from openapi.openapi_server.models import TextComplexityForm
 
 def post(complexity_data: dict):
     tcf: TextComplexityForm = TextComplexityForm.from_dict(complexity_data)
-    ar: AnnisResponse = AnnisResponse.from_dict(
-        tcf.annis_response.to_dict()) if tcf.annis_response else CorpusService.get_corpus(tcf.urn, is_csm=True)
+    ar: AnnisResponse = AnnisResponse.from_dict(json.loads(tcf.annis_response)) if tcf.annis_response \
+        else CorpusService.get_corpus(tcf.urn, is_csm=True)
     tc: TextComplexity = TextComplexityService.text_complexity(tcf.measure, tcf.urn, True, ar.graph_data)
     return NetworkService.make_json_response(tc.to_dict())
diff --git a/mc_backend/csm/csm_api.yaml b/mc_backend/csm/csm_api.yaml
index 40abd19..79dc31a 100644
--- a/mc_backend/csm/csm_api.yaml
+++ b/mc_backend/csm/csm_api.yaml
@@ -39,4 +39,4 @@ paths:
         content:
           application/x-www-form-urlencoded:
             schema:
-              $ref: '../openapi_models.yaml#/components/schemas/TextComplexityFormBase'
+              $ref: '../openapi_models.yaml#/components/schemas/TextComplexityForm'
diff --git a/mc_backend/mcserver/app/api/exerciseListAPI.py b/mc_backend/mcserver/app/api/exerciseListAPI.py
index f06ecd6..ed55ff4 100644
--- a/mc_backend/mcserver/app/api/exerciseListAPI.py
+++ b/mc_backend/mcserver/app/api/exerciseListAPI.py
@@ -7,6 +7,7 @@ from mcserver.app import db
 from mcserver.app.models import Language, VocabularyCorpus, ResourceType
 from mcserver.app.services import NetworkService, FileService
 from mcserver.models_auto import Exercise, UpdateInfo
+from openapi.openapi_server.models import MatchingExercise
 
 
 def get(lang: str, frequency_upper_bound: int, last_update_time: int, vocabulary: str = ""):
@@ -29,13 +30,11 @@ def get(lang: str, frequency_upper_bound: int, last_update_time: int, vocabulary
         lang = Language.English
     exercises: List[Exercise] = db.session.query(Exercise).filter_by(language=lang.value)
     db.session.commit()
-    ret_val: List[dict] = [NetworkService.serialize_exercise(x, compress=True) for x in exercises]
-    matching_degrees: List[float] = []
+    matching_exercises: List[MatchingExercise] = [MatchingExercise.from_dict(x.to_dict()) for x in exercises]
     if len(vocabulary_set):
-        for exercise in exercises:
+        for exercise in matching_exercises:
             conll: List[TokenList] = conllu.parse(exercise.conll)
             lemmata: List[str] = [tok["lemma"] for sent in conll for tok in sent.tokens]
-            matching_degrees.append(sum((1 if x in vocabulary_set else 0) for x in lemmata) / len(lemmata) * 100)
-        for i in range(len(ret_val)):
-            ret_val[i]["matching_degree"] = matching_degrees[i]
+            exercise.matching_degree = sum((1 if x in vocabulary_set else 0) for x in lemmata) / len(lemmata) * 100
+    ret_val: List[dict] = [NetworkService.serialize_exercise(x, compress=True) for x in matching_exercises]
     return NetworkService.make_json_response(ret_val)
diff --git a/mc_backend/mcserver/app/api/fileAPI.py b/mc_backend/mcserver/app/api/fileAPI.py
index fea6907..40bcdf5 100644
--- a/mc_backend/mcserver/app/api/fileAPI.py
+++ b/mc_backend/mcserver/app/api/fileAPI.py
@@ -4,15 +4,10 @@ import os
 import uuid
 from datetime import datetime
 from typing import List, Union
-
 import connexion
-import flask
 from connexion.lifecycle import ConnexionResponse
 from flask import send_from_directory, Response
-from flask_restful import Resource, abort
-from flask_restful.reqparse import RequestParser
 from werkzeug.wrappers import ETagResponseMixin
-
 from mcserver.app import db
 from mcserver.app.models import FileType, ResourceType, DownloadableFile, MimeType, XapiStatement, LearningResultMC
 from mcserver.app.services import FileService, NetworkService
diff --git a/mc_backend/mcserver/app/api/validReffAPI.py b/mc_backend/mcserver/app/api/validReffAPI.py
index 13507e5..86b58db 100644
--- a/mc_backend/mcserver/app/api/validReffAPI.py
+++ b/mc_backend/mcserver/app/api/validReffAPI.py
@@ -2,15 +2,17 @@ from typing import List, Union
 import connexion
 from connexion.lifecycle import ConnexionResponse
 from flask import Response
-
 from mcserver import Config
 from mcserver.app.services import CorpusService, NetworkService, CustomCorpusService
 
 
 def get(urn: str) -> Union[Response, ConnexionResponse]:
     """The GET method for the valid references REST API. It provides references for the desired text."""
-    reff: List[str] = CustomCorpusService.get_custom_corpus_reff(urn) if CustomCorpusService.is_custom_corpus_urn(
-        urn) else CorpusService.get_standard_corpus_reff(urn)
+    try:
+        reff: List[str] = CustomCorpusService.get_custom_corpus_reff(urn) if CustomCorpusService.is_custom_corpus_urn(
+            urn) else CorpusService.get_standard_corpus_reff(urn)
+    except ValueError:
+        return connexion.problem(400, Config.ERROR_TITLE_BAD_REQUEST, Config.ERROR_MESSAGE_BAD_REQUEST)
     if not reff:
         return connexion.problem(404, Config.ERROR_TITLE_NOT_FOUND, Config.ERROR_MESSAGE_CORPUS_NOT_FOUND)
     return NetworkService.make_json_response(reff)
diff --git a/mc_backend/mcserver/app/services/customCorpusService.py b/mc_backend/mcserver/app/services/customCorpusService.py
index bc7e086..e90a5a6 100644
--- a/mc_backend/mcserver/app/services/customCorpusService.py
+++ b/mc_backend/mcserver/app/services/customCorpusService.py
@@ -2,12 +2,9 @@ import ntpath
 import os
 from collections import OrderedDict
 from typing import List, Tuple, Set, Dict
-
 import conllu
 import rapidjson as json
 from conllu import TokenList
-from flask_restful import abort
-
 from mcserver import Config
 from mcserver.app.models import CustomCorpus, CitationLevel, TextPart, Citation, CorpusMC
 from mcserver.app.services import AnnotationService, FileService
@@ -132,7 +129,10 @@ class CustomCorpusService:
         if cts_urn == target_corpus.corpus.source_urn:
             disk_reff = [(":".join([cts_urn, str(x.citation.value)])) for x in target_corpus.text_parts]
         else:
-            disk_reff = CustomCorpusService.get_custom_corpus_sub_reff(cts_urn, target_corpus)
+            try:
+                disk_reff = CustomCorpusService.get_custom_corpus_sub_reff(cts_urn, target_corpus)
+            except ValueError:
+                raise
         with open(os.path.join(Config.REFF_CACHE_DIRECTORY, disk_urn), "w+") as f:
             f.write(json.dumps(disk_reff))
         return disk_reff
@@ -145,7 +145,7 @@ class CustomCorpusService:
         try:
             citation_parts = [int(x) for x in urn_parts[-1].split(".")]
         except ValueError:
-            abort(400)
+            raise
         target_text_parts: List[TextPart] = target_corpus.text_parts
         if len(citation_parts) > 1:
             target_text_parts = next(
diff --git a/mc_backend/mcserver/app/services/networkService.py b/mc_backend/mcserver/app/services/networkService.py
index b6c7199..da020e2 100644
--- a/mc_backend/mcserver/app/services/networkService.py
+++ b/mc_backend/mcserver/app/services/networkService.py
@@ -9,7 +9,8 @@ from flask_restful.reqparse import RequestParser
 
 from mcserver import Config
 from mcserver.app.models import StaticExercise
-from mcserver.models_auto import Exercise, TExercise
+from mcserver.models_auto import Exercise
+from openapi.openapi_server.models import MatchingExercise
 
 
 class NetworkService:
@@ -33,12 +34,12 @@ class NetworkService:
         return response
 
     @staticmethod
-    def serialize_exercise(exercise: TExercise, compress: bool) -> dict:
+    def serialize_exercise(exercise: MatchingExercise, compress: bool) -> dict:
         """ Serializes an exercise to JSON format. """
-        ret_val: dict = exercise.to_dict()
-        ret_val["conll"] = "" if compress else exercise.conll
+        exercise.conll = "" if compress else exercise.conll
         # convert the POSIX timestamp to JSON / Javascript, i.e. from seconds to milliseconds
-        ret_val["last_access_time"] = exercise.last_access_time * 1000
+        exercise.last_access_time = exercise.last_access_time * 1000
+        ret_val: dict = exercise.to_dict()
         ret_val["search_values"] = json.loads(exercise.search_values)
-        ret_val["solutions"] = "[]" if compress else json.loads(exercise.solutions)
+        ret_val["solutions"] = [] if compress else json.loads(exercise.solutions)
         return ret_val
diff --git a/mc_backend/mcserver/app/services/textComplexityService.py b/mc_backend/mcserver/app/services/textComplexityService.py
index 12cca55..3cbb4c6 100644
--- a/mc_backend/mcserver/app/services/textComplexityService.py
+++ b/mc_backend/mcserver/app/services/textComplexityService.py
@@ -5,6 +5,7 @@ import requests
 from mcserver import Config
 from mcserver.app.models import GraphData, TextComplexity, TextComplexityMeasure, AnnisResponse
 from mcserver.app.services import TextService, AnnotationService, CorpusService
+from openapi.openapi_server.models import TextComplexityForm
 
 
 class TextComplexityService:
@@ -183,6 +184,7 @@ class TextComplexityService:
             url: str = f"{Config.INTERNET_PROTOCOL}{Config.HOST_IP_CSM}:" + \
                        f"{Config.CORPUS_STORAGE_MANAGER_PORT}{Config.SERVER_URI_TEXT_COMPLEXITY}"
             ar: AnnisResponse = AnnisResponse(graph_data=gd)
-            response: requests.Response = requests.post(url, data=json.dumps(
-                dict(urn=urn, measure=TextComplexityMeasure.all.name, annis_response=ar.to_dict())))
+            tcf: TextComplexityForm = TextComplexityForm(urn=urn, measure=TextComplexityMeasure.all.name,
+                                                         annis_response=json.dumps(ar.to_dict()))
+            response: requests.Response = requests.post(url, data=tcf.to_dict())
             return TextComplexity.from_dict(json.loads(response.text))
diff --git a/mc_backend/mcserver/config.py b/mc_backend/mcserver/config.py
index e26a76f..5ca5d8e 100644
--- a/mc_backend/mcserver/config.py
+++ b/mc_backend/mcserver/config.py
@@ -73,6 +73,9 @@ class Config(object):
     DEBUG = False
     DOCKER_SERVICE_NAME_CSM = "csm"
     DOCKER_SERVICE_NAME_MCSERVER = "mcserver"
+    ERROR_MESSAGE_BAD_REQUEST = \
+        "The server cannot or will not process the request due to something that is perceived to be a client " \
+        "error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing)."
     ERROR_MESSAGE_CORPUS_NOT_FOUND = "A corpus with the specified ID was not found!"
     ERROR_MESSAGE_EXERCISE_NOT_FOUND = "An exercise with the specified ID was not found!"
     ERROR_MESSAGE_INTERNAL_SERVER_ERROR = \
@@ -83,6 +86,7 @@ class Config(object):
     ERROR_MESSAGE_UNPROCESSABLE_ENTITY = \
         "The server understands the content type of the request entity, and the syntax of the request entity is " \
         "correct, but it was unable to process the contained instructions."
+    ERROR_TITLE_BAD_REQUEST = "Bad Request"
     ERROR_TITLE_INTERNAL_SERVER_ERROR = "Internal Server Error"
     ERROR_TITLE_NOT_FOUND = "Not found"
     ERROR_TITLE_SERVICE_UNAVAILABLE = "Service Unavailable"
diff --git a/mc_backend/mcserver/mcserver_api.yaml b/mc_backend/mcserver/mcserver_api.yaml
index 231b81a..f28cc1c 100644
--- a/mc_backend/mcserver/mcserver_api.yaml
+++ b/mc_backend/mcserver/mcserver_api.yaml
@@ -12,7 +12,7 @@ paths:
       summary: Returns a list of corpora.
       operationId: mcserver.app.api.corpusListAPI.get
       responses:
-        200:
+        "200":
           description: Corpus list
           content:
             application/json:
@@ -39,7 +39,7 @@ paths:
       summary: Deletes a single corpus by ID.
       operationId: mcserver.app.api.corpusAPI.delete
       responses:
-        200:
+        "200":
           description: Indication of success
           content:
             application/json:
@@ -50,7 +50,7 @@ paths:
       summary: Returns a single corpus by ID.
       operationId: mcserver.app.api.corpusAPI.get
       responses:
-        200:
+        "200":
           description: Corpus object
           content:
             application/json:
@@ -60,7 +60,7 @@ paths:
       summary: Updates a single corpus by ID.
       operationId: mcserver.app.api.corpusAPI.patch
       responses:
-        200:
+        "200":
           description: Corpus object
           content:
             application/json:
@@ -93,7 +93,7 @@ paths:
       summary: Returns exercise data by ID.
       operationId: mcserver.app.api.exerciseAPI.get
       responses:
-        200:
+        "200":
           description: Exercise data object, including a graph model for linguistic annotations.
           content:
             application/json:
@@ -105,7 +105,7 @@ paths:
       summary: Creates a new exercise.
       operationId: mcserver.app.api.exerciseAPI.post
       responses:
-        200:
+        "200":
           description: Exercise data object
           content:
             application/json:
@@ -122,12 +122,14 @@ paths:
       summary: Provides metadata for all available exercises.
       operationId: mcserver.app.api.exerciseListAPI.get
       responses:
-        200:
+        "200":
           description: Data for interactive exercises, excluding the linguistic details.
           content:
             application/json:
               schema:
-                $ref: '../openapi_models.yaml#/components/schemas/Exercise'
+                type: array
+                items:
+                  $ref: '../openapi_models.yaml#/components/schemas/MatchingExercise'
       parameters:
         - $ref: '../openapi_models.yaml#/components/parameters/LangParam'
         - name: frequency_upper_bound
@@ -157,12 +159,18 @@ paths:
       summary: Provides the URL to download a specific file.
       operationId: mcserver.app.api.fileAPI.get
       responses:
-        200:
+        "200":
           description: Data for interactive exercises, excluding the linguistic details.
           content:
-            application/json:
+            application/pdf:
+              schema:
+                type: object
+            application/vnd.openxmlformats-officedocument.wordprocessingml.document:
+              schema:
+                type: object
+            application/xml:
               schema:
-                $ref: '../openapi_models.yaml#/components/schemas/Exercise'
+                type: object
       parameters:
         - name: id
           in: query
@@ -182,7 +190,7 @@ paths:
       summary: Serializes and persists learning results or HTML content for later access.
       operationId: mcserver.app.api.fileAPI.post
       responses:
-        200:
+        "200":
           description: Indication of success, possibly a reference to the resulting file.
           content:
             application/json:
@@ -218,7 +226,7 @@ paths:
       summary: Returns results for a frequency query from ANNIS for a given CTS URN.
       operationId: mcserver.app.api.frequencyAPI.get
       responses:
-        200:
+        "200":
           description: Frequency analysis, i.e. a list of frequency items.
           content:
             application/json:
@@ -234,7 +242,7 @@ paths:
       summary: Provides JSON templates for client-side H5P exercise layouts.
       operationId: mcserver.app.api.h5pAPI.get
       responses:
-        200:
+        "200":
           description: JSON template for an interactive H5P exercise.
           content:
             application/json:
@@ -251,7 +259,7 @@ paths:
       summary: Provides example contexts for a given phenomenon in a given corpus.
       operationId: mcserver.app.api.kwicAPI.post
       responses:
-        200:
+        "200":
           description: HTML strings with SVG elements for rendering KWIC images.
           content:
             application/json:
@@ -268,7 +276,7 @@ paths:
       summary: Provides the raw text for a requested text passage.
       operationId: mcserver.app.api.rawTextAPI.get
       responses:
-        200:
+        "200":
           description: Graph data for the text passage, including raw text and annotations.
           content:
             application/json:
@@ -281,7 +289,7 @@ paths:
       summary: Returns metadata for static exercises.
       operationId: mcserver.app.api.staticExercisesAPI.get
       responses:
-        200:
+        "200":
           description: Metadata for static exercises, mapped to their respective URIs in the frontend.
           content:
             application/json:
@@ -294,7 +302,7 @@ paths:
       summary: Gives users measures of text complexity for a given text.
       operationId: mcserver.app.api.textcomplexityAPI.get
       responses:
-        200:
+        "200":
           description: Text complexity measures for a given text.
           content:
             application/json:
@@ -314,7 +322,7 @@ paths:
       summary: Gives users all the citable text references for a corpus.
       operationId: mcserver.app.api.validReffAPI.get
       responses:
-        200:
+        "200":
           description: Valid references for the desired text.
           content:
             application/json:
@@ -330,7 +338,7 @@ paths:
       summary: Provides network data for the vectors in an AI model.
       operationId: mcserver.app.api.vectorNetworkAPI.get
       responses:
-        200:
+        "200":
           description: HTML string with SVG elements for rendering the network graph.
           content:
             application/json:
@@ -369,7 +377,7 @@ paths:
       summary: Provides network data for the vectors in an AI model.
       operationId: mcserver.app.api.vectorNetworkAPI.post
       responses:
-        200:
+        "200":
           description: Sentences whose content is similar to a given word.
           content:
             application/json:
@@ -395,7 +403,7 @@ paths:
       summary: Shows how well the vocabulary of a text matches a predefined reference vocabulary.
       operationId: mcserver.app.api.vocabularyAPI.get
       responses:
-        200:
+        "200":
           description: Retrieves sentence ID and matching degree for each sentence in the query text.
           content:
             application/json:
@@ -428,7 +436,7 @@ paths:
       summary: Shows how well the vocabulary of a text matches a predefined reference vocabulary.
       operationId: mcserver.app.api.vocabularyAPI.post
       responses:
-        200:
+        "200":
           description: Indicates for each token of a corpus whether it is covered by a reference vocabulary.
           content:
             application/json:
@@ -443,7 +451,7 @@ paths:
 # include this here so the data model gets generated correctly
 components:
   schemas:
-    TextComplexityForm:
+    TextComplexityFormExtension:
       type: object
       allOf:
-        - $ref: '../openapi_models.yaml#/components/schemas/TextComplexityFormBase'
+        - $ref: '../openapi_models.yaml#/components/schemas/TextComplexityForm'
diff --git a/mc_backend/mcserver/models_auto.py b/mc_backend/mcserver/models_auto.py
index 8b00df3..b8a7d63 100644
--- a/mc_backend/mcserver/models_auto.py
+++ b/mc_backend/mcserver/models_auto.py
@@ -167,10 +167,10 @@ class ExerciseDict(_ExerciseDictBase, total=False):
     work_title: str
     conll: str
     exercise_type: str
-    exercise_type_translation: str
     solutions: str
     text_complexity: float
     urn: str
+    exercise_type_translation: str
 
 
 class TExercise(typing.Protocol):
@@ -197,13 +197,13 @@ class TExercise(typing.Protocol):
             string.
         eid: Unique identifier (UUID) for the exercise.
         exercise_type: Type of exercise, concerning interaction and layout.
-        exercise_type_translation: Localized expression of the exercise type.
         last_access_time: When the exercise was last accessed (as POSIX
             timestamp).
         solutions: Correct solutions for the exercise.
         text_complexity: Overall text complexity as measured by the software's
             internal language analysis.
         urn: CTS URN for the text passage from which the exercise was created.
+        exercise_type_translation: Localized expression of the exercise type.
 
     """
 
@@ -225,11 +225,11 @@ class TExercise(typing.Protocol):
     conll: str
     eid: str
     exercise_type: str
-    exercise_type_translation: str
     last_access_time: float
     solutions: str
     text_complexity: float
     urn: str
+    exercise_type_translation: str
 
     def __init__(
         self,
@@ -246,10 +246,10 @@ class TExercise(typing.Protocol):
         work_title: str = "",
         conll: str = "",
         exercise_type: str = "",
-        exercise_type_translation: str = "",
         solutions: str = "[]",
         text_complexity: float = 0,
         urn: str = "",
+        exercise_type_translation: str = "",
     ) -> None:
         """
         Construct.
@@ -273,8 +273,6 @@ class TExercise(typing.Protocol):
                 single string.
             eid: Unique identifier (UUID) for the exercise.
             exercise_type: Type of exercise, concerning interaction and layout.
-            exercise_type_translation: Localized expression of the exercise
-                type.
             last_access_time: When the exercise was last accessed (as POSIX
                 timestamp).
             solutions: Correct solutions for the exercise.
@@ -282,6 +280,8 @@ class TExercise(typing.Protocol):
                 software's internal language analysis.
             urn: CTS URN for the text passage from which the exercise was
                 created.
+            exercise_type_translation: Localized expression of the exercise
+                type.
 
         """
         ...
@@ -302,10 +302,10 @@ class TExercise(typing.Protocol):
         work_title: str = "",
         conll: str = "",
         exercise_type: str = "",
-        exercise_type_translation: str = "",
         solutions: str = "[]",
         text_complexity: float = 0,
         urn: str = "",
+        exercise_type_translation: str = "",
     ) -> "TExercise":
         """
         Construct from a dictionary (eg. a POST payload).
@@ -329,8 +329,6 @@ class TExercise(typing.Protocol):
                 single string.
             eid: Unique identifier (UUID) for the exercise.
             exercise_type: Type of exercise, concerning interaction and layout.
-            exercise_type_translation: Localized expression of the exercise
-                type.
             last_access_time: When the exercise was last accessed (as POSIX
                 timestamp).
             solutions: Correct solutions for the exercise.
@@ -338,6 +336,8 @@ class TExercise(typing.Protocol):
                 software's internal language analysis.
             urn: CTS URN for the text passage from which the exercise was
                 created.
+            exercise_type_translation: Localized expression of the exercise
+                type.
 
         Returns:
             Model instance based on the dictionary.
diff --git a/mc_backend/mocks.py b/mc_backend/mocks.py
index a3a9540..e6bdcd4 100644
--- a/mc_backend/mocks.py
+++ b/mc_backend/mocks.py
@@ -761,7 +761,7 @@ class Mocks:
                                'Romanus', 'Solomon', 'amor']
     raw_text: str = "Caesar fortis est. Galli moriuntur."
     static_exercises_udpipe_string: str = "1\tscribere\tscribere\n1\tcommovere\tcommovere\n1\tC\tC\n1\tgaudere\tgaudere\n1\tsignum\tsignum\n1\tvas\tvas\n1\tclarus\tclarus\n1\tcondicio\tcondicio\n1\tcom\tcum\n1\tprae\tprae\n1\tmovere\tmovere\n1\tducere\tducere\n1\tde\tde\n1\tcum\tcum\n1\tistam\tiste\n1\tnationum\tnatio\n1\tclarissimae\tclarus\n1\tmoderationem\tmoderatio\n1\tanimi\tanimus\n1\tomnium\tomnis\n1\tgentium\tgens\n1\tac\tac\n1\tvirtutem\tvirtus\n1\tprovinciae\tprovincia\n1\tCaesar\tCaesar\n1\test\tesse\n1\tsatis\tsatis\n1\tgovernment\tgovernment\n1\tsocius\tsocius\n1\tprovincia\tprovincia\n1\tpublicus\tpublicus\n1\tcivis\tcivis\n1\tatque\tatque"
-    subgraph_json: str = '{"exercise_id":"","exercise_type":null,"frequency_analysis":null,"graph_data":{"directed":true,"graph":{},"links":[],"multigraph":true,"nodes":[{"annis_node_name":"urn:cts:latinLit:phi0448.phi001.perseus-lat2:1.1.1-1.1.1/doc1#sent1tok3","annis_node_type":"node","annis_tok":"Galli","annis_type":"node","id":"salt:/urn:cts:latinLit:phi0448.phi001.perseus-lat2:1.1.1-1.1.1/doc1#sent1tok3","is_oov":null,"udep_lemma":"Gallo","udep_upostag":"VERB","udep_xpostag":"L3|modQ|tem1|stAC","udep_feats":"Tense=Pres|VerbForm=Inf|Voice=Pass","solution":null}]},"solutions":[],"text_complexity":null,"uri":""}'
+    subgraph_json: str = '{"exercise_id":"","exercise_type":"","frequency_analysis":null,"graph_data":{"directed":true,"graph":{},"links":[],"multigraph":true,"nodes":[{"annis_node_name":"urn:cts:latinLit:phi0448.phi001.perseus-lat2:1.1.1-1.1.1/doc1#sent1tok3","annis_node_type":"node","annis_tok":"Galli","annis_type":"node","id":"salt:/urn:cts:latinLit:phi0448.phi001.perseus-lat2:1.1.1-1.1.1/doc1#sent1tok3","is_oov":null,"udep_lemma":"Gallo","udep_upostag":"VERB","udep_xpostag":"L3|modQ|tem1|stAC","udep_feats":"Tense=Pres|VerbForm=Inf|Voice=Pass","solution":null}]},"solutions":[],"text_complexity":null,"uri":""}'
     test_args: List[str] = ["tests.py", "-test"]
     text_complexity_json_string: str = '{"all":54.53,"avg_w_len":5.79,"avg_w_per_sent":17.33,"lex_den":0.73,"n_abl_abs":0,"n_clause":1,"n_gerund":1,"n_inf":1,"n_part":1,"n_punct":3,"n_sent":3,"n_subclause":0,"n_types":48,"n_w":52,"pos":11}'
     text_list: List[Tuple[str, str]] = [("urn:cts:latinLit:phi0448.phi001.perseus-lat2:1.1.1", raw_text.split(".")[0]),
diff --git a/mc_backend/openapi/openapi_server/controllers/default_controller.py b/mc_backend/openapi/openapi_server/controllers/default_controller.py
index 602e22d..f070055 100644
--- a/mc_backend/openapi/openapi_server/controllers/default_controller.py
+++ b/mc_backend/openapi/openapi_server/controllers/default_controller.py
@@ -3,9 +3,9 @@ import six
 
 from openapi.openapi_server.models.annis_response import AnnisResponse  # noqa: E501
 from openapi.openapi_server.models.corpus import Corpus  # noqa: E501
-from openapi.openapi_server.models.exercise import Exercise  # noqa: E501
 from openapi.openapi_server.models.file_type import FileType  # noqa: E501
 from openapi.openapi_server.models.frequency_item import FrequencyItem  # noqa: E501
+from openapi.openapi_server.models.matching_exercise import MatchingExercise  # noqa: E501
 from openapi.openapi_server.models.sentence import Sentence  # noqa: E501
 from openapi.openapi_server.models.static_exercise import StaticExercise  # noqa: E501
 from openapi.openapi_server.models.text_complexity import TextComplexity  # noqa: E501
@@ -109,7 +109,7 @@ def mcserver_app_api_exercise_list_api_get(lang, frequency_upper_bound=None, las
     :param vocabulary: Identifier for a reference vocabulary.
     :type vocabulary: dict | bytes
 
-    :rtype: Exercise
+    :rtype: List[MatchingExercise]
     """
     if connexion.request.is_json:
         vocabulary =  VocabularyMC.from_dict(connexion.request.get_json())  # noqa: E501
@@ -128,7 +128,7 @@ def mcserver_app_api_file_api_get(id, type, solution_indices=None):  # noqa: E50
     :param solution_indices: Indices for the solutions that should be included in the download.
     :type solution_indices: List[int]
 
-    :rtype: Exercise
+    :rtype: object
     """
     if connexion.request.is_json:
         type =  FileType.from_dict(connexion.request.get_json())  # noqa: E501
diff --git a/mc_backend/openapi/openapi_server/models/__init__.py b/mc_backend/openapi/openapi_server/models/__init__.py
index 08e9d5a..af5d3a7 100644
--- a/mc_backend/openapi/openapi_server/models/__init__.py
+++ b/mc_backend/openapi/openapi_server/models/__init__.py
@@ -5,9 +5,8 @@ from __future__ import absolute_import
 # import models into model package
 from openapi.openapi_server.models.annis_response import AnnisResponse
 from openapi.openapi_server.models.corpus import Corpus
-from openapi.openapi_server.models.exercise import Exercise
-from openapi.openapi_server.models.exercise_all_of import ExerciseAllOf
 from openapi.openapi_server.models.exercise_base import ExerciseBase
+from openapi.openapi_server.models.exercise_extension import ExerciseExtension
 from openapi.openapi_server.models.exercise_form import ExerciseForm
 from openapi.openapi_server.models.exercise_form_all_of import ExerciseFormAllOf
 from openapi.openapi_server.models.file_type import FileType
@@ -16,6 +15,8 @@ from openapi.openapi_server.models.graph_data import GraphData
 from openapi.openapi_server.models.inline_object import InlineObject
 from openapi.openapi_server.models.kwic_form import KwicForm
 from openapi.openapi_server.models.link import Link
+from openapi.openapi_server.models.matching_exercise import MatchingExercise
+from openapi.openapi_server.models.matching_exercise_all_of import MatchingExerciseAllOf
 from openapi.openapi_server.models.node_mc import NodeMC
 from openapi.openapi_server.models.phenomenon import Phenomenon
 from openapi.openapi_server.models.sentence import Sentence
@@ -24,7 +25,7 @@ from openapi.openapi_server.models.solution_element import SolutionElement
 from openapi.openapi_server.models.static_exercise import StaticExercise
 from openapi.openapi_server.models.text_complexity import TextComplexity
 from openapi.openapi_server.models.text_complexity_form import TextComplexityForm
-from openapi.openapi_server.models.text_complexity_form_base import TextComplexityFormBase
+from openapi.openapi_server.models.text_complexity_form_extension import TextComplexityFormExtension
 from openapi.openapi_server.models.vector_network_form import VectorNetworkForm
 from openapi.openapi_server.models.vocabulary_form import VocabularyForm
 from openapi.openapi_server.models.vocabulary_mc import VocabularyMC
diff --git a/mc_backend/openapi/openapi_server/models/annis_response.py b/mc_backend/openapi/openapi_server/models/annis_response.py
index e09e97a..8bd53fa 100644
--- a/mc_backend/openapi/openapi_server/models/annis_response.py
+++ b/mc_backend/openapi/openapi_server/models/annis_response.py
@@ -23,7 +23,7 @@ class AnnisResponse(Model):
     Do not edit the class manually.
     """
 
-    def __init__(self, exercise_id=None, exercise_type=None, frequency_analysis=None, graph_data=None, solutions=None, text_complexity=None, uri=None):  # noqa: E501
+    def __init__(self, exercise_id='', exercise_type='', frequency_analysis=None, graph_data=None, solutions=None, text_complexity=None, uri=''):  # noqa: E501
         """AnnisResponse - a model defined in OpenAPI
 
         :param exercise_id: The exercise_id of this AnnisResponse.  # noqa: E501
diff --git a/mc_backend/openapi/openapi_server/models/annis_response_frequency_analysis.py b/mc_backend/openapi/openapi_server/models/annis_response_frequency_analysis.py
deleted file mode 100644
index f037f10..0000000
--- a/mc_backend/openapi/openapi_server/models/annis_response_frequency_analysis.py
+++ /dev/null
@@ -1,122 +0,0 @@
-# coding: utf-8
-
-from __future__ import absolute_import
-from datetime import date, datetime  # noqa: F401
-
-from typing import List, Dict  # noqa: F401
-
-from openapi.openapi_server.models.base_model_ import Model
-from openapi.openapi_server import util
-
-
-class AnnisResponseFrequencyAnalysis(Model):
-    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
-
-    Do not edit the class manually.
-    """
-
-    def __init__(self, count=None, phenomena=None, values=None):  # noqa: E501
-        """AnnisResponseFrequencyAnalysis - a model defined in OpenAPI
-
-        :param count: The count of this AnnisResponseFrequencyAnalysis.  # noqa: E501
-        :type count: int
-        :param phenomena: The phenomena of this AnnisResponseFrequencyAnalysis.  # noqa: E501
-        :type phenomena: List[str]
-        :param values: The values of this AnnisResponseFrequencyAnalysis.  # noqa: E501
-        :type values: List[str]
-        """
-        self.openapi_types = {
-            'count': int,
-            'phenomena': List[str],
-            'values': List[str]
-        }
-
-        self.attribute_map = {
-            'count': 'count',
-            'phenomena': 'phenomena',
-            'values': 'values'
-        }
-
-        self._count = count
-        self._phenomena = phenomena
-        self._values = values
-
-    @classmethod
-    def from_dict(cls, dikt) -> 'AnnisResponseFrequencyAnalysis':
-        """Returns the dict as a model
-
-        :param dikt: A dict.
-        :type: dict
-        :return: The AnnisResponse_frequency_analysis of this AnnisResponseFrequencyAnalysis.  # noqa: E501
-        :rtype: AnnisResponseFrequencyAnalysis
-        """
-        return util.deserialize_model(dikt, cls)
-
-    @property
-    def count(self):
-        """Gets the count of this AnnisResponseFrequencyAnalysis.
-
-        How often the given combination of values occurred.  # noqa: E501
-
-        :return: The count of this AnnisResponseFrequencyAnalysis.
-        :rtype: int
-        """
-        return self._count
-
-    @count.setter
-    def count(self, count):
-        """Sets the count of this AnnisResponseFrequencyAnalysis.
-
-        How often the given combination of values occurred.  # noqa: E501
-
-        :param count: The count of this AnnisResponseFrequencyAnalysis.
-        :type count: int
-        """
-
-        self._count = count
-
-    @property
-    def phenomena(self):
-        """Gets the phenomena of this AnnisResponseFrequencyAnalysis.
-
-        Labels for the phenomena described in this frequency entry.  # noqa: E501
-
-        :return: The phenomena of this AnnisResponseFrequencyAnalysis.
-        :rtype: List[str]
-        """
-        return self._phenomena
-
-    @phenomena.setter
-    def phenomena(self, phenomena):
-        """Sets the phenomena of this AnnisResponseFrequencyAnalysis.
-
-        Labels for the phenomena described in this frequency entry.  # noqa: E501
-
-        :param phenomena: The phenomena of this AnnisResponseFrequencyAnalysis.
-        :type phenomena: List[str]
-        """
-
-        self._phenomena = phenomena
-
-    @property
-    def values(self):
-        """Gets the values of this AnnisResponseFrequencyAnalysis.
-
-        Values for the phenomena described in this frequency entry.  # noqa: E501
-
-        :return: The values of this AnnisResponseFrequencyAnalysis.
-        :rtype: List[str]
-        """
-        return self._values
-
-    @values.setter
-    def values(self, values):
-        """Sets the values of this AnnisResponseFrequencyAnalysis.
-
-        Values for the phenomena described in this frequency entry.  # noqa: E501
-
-        :param values: The values of this AnnisResponseFrequencyAnalysis.
-        :type values: List[str]
-        """
-
-        self._values = values
diff --git a/mc_backend/openapi/openapi_server/models/exercise_all_of.py b/mc_backend/openapi/openapi_server/models/exercise_extension.py
similarity index 61%
rename from mc_backend/openapi/openapi_server/models/exercise_all_of.py
rename to mc_backend/openapi/openapi_server/models/exercise_extension.py
index deb236f..6551eb9 100644
--- a/mc_backend/openapi/openapi_server/models/exercise_all_of.py
+++ b/mc_backend/openapi/openapi_server/models/exercise_extension.py
@@ -9,37 +9,34 @@ from openapi.openapi_server.models.base_model_ import Model
 from openapi.openapi_server import util
 
 
-class ExerciseAllOf(Model):
+class ExerciseExtension(Model):
     """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 
     Do not edit the class manually.
     """
 
-    def __init__(self, conll='', eid=None, exercise_type='', exercise_type_translation='', last_access_time=None, solutions='[]', text_complexity=0, urn=''):  # noqa: E501
-        """ExerciseAllOf - a model defined in OpenAPI
+    def __init__(self, conll='', eid=None, exercise_type='', last_access_time=None, solutions='[]', text_complexity=0, urn=''):  # noqa: E501
+        """ExerciseExtension - a model defined in OpenAPI
 
-        :param conll: The conll of this ExerciseAllOf.  # noqa: E501
+        :param conll: The conll of this ExerciseExtension.  # noqa: E501
         :type conll: str
-        :param eid: The eid of this ExerciseAllOf.  # noqa: E501
+        :param eid: The eid of this ExerciseExtension.  # noqa: E501
         :type eid: str
-        :param exercise_type: The exercise_type of this ExerciseAllOf.  # noqa: E501
+        :param exercise_type: The exercise_type of this ExerciseExtension.  # noqa: E501
         :type exercise_type: str
-        :param exercise_type_translation: The exercise_type_translation of this ExerciseAllOf.  # noqa: E501
-        :type exercise_type_translation: str
-        :param last_access_time: The last_access_time of this ExerciseAllOf.  # noqa: E501
+        :param last_access_time: The last_access_time of this ExerciseExtension.  # noqa: E501
         :type last_access_time: float
-        :param solutions: The solutions of this ExerciseAllOf.  # noqa: E501
+        :param solutions: The solutions of this ExerciseExtension.  # noqa: E501
         :type solutions: str
-        :param text_complexity: The text_complexity of this ExerciseAllOf.  # noqa: E501
+        :param text_complexity: The text_complexity of this ExerciseExtension.  # noqa: E501
         :type text_complexity: float
-        :param urn: The urn of this ExerciseAllOf.  # noqa: E501
+        :param urn: The urn of this ExerciseExtension.  # noqa: E501
         :type urn: str
         """
         self.openapi_types = {
             'conll': str,
             'eid': str,
             'exercise_type': str,
-            'exercise_type_translation': str,
             'last_access_time': float,
             'solutions': str,
             'text_complexity': float,
@@ -50,7 +47,6 @@ class ExerciseAllOf(Model):
             'conll': 'conll',
             'eid': 'eid',
             'exercise_type': 'exercise_type',
-            'exercise_type_translation': 'exercise_type_translation',
             'last_access_time': 'last_access_time',
             'solutions': 'solutions',
             'text_complexity': 'text_complexity',
@@ -60,41 +56,40 @@ class ExerciseAllOf(Model):
         self._conll = conll
         self._eid = eid
         self._exercise_type = exercise_type
-        self._exercise_type_translation = exercise_type_translation
         self._last_access_time = last_access_time
         self._solutions = solutions
         self._text_complexity = text_complexity
         self._urn = urn
 
     @classmethod
-    def from_dict(cls, dikt) -> 'ExerciseAllOf':
+    def from_dict(cls, dikt) -> 'ExerciseExtension':
         """Returns the dict as a model
 
         :param dikt: A dict.
         :type: dict
-        :return: The Exercise_allOf of this ExerciseAllOf.  # noqa: E501
-        :rtype: ExerciseAllOf
+        :return: The ExerciseExtension of this ExerciseExtension.  # noqa: E501
+        :rtype: ExerciseExtension
         """
         return util.deserialize_model(dikt, cls)
 
     @property
     def conll(self):
-        """Gets the conll of this ExerciseAllOf.
+        """Gets the conll of this ExerciseExtension.
 
         CONLL-formatted linguistic annotations represented as a single string.  # noqa: E501
 
-        :return: The conll of this ExerciseAllOf.
+        :return: The conll of this ExerciseExtension.
         :rtype: str
         """
         return self._conll
 
     @conll.setter
     def conll(self, conll):
-        """Sets the conll of this ExerciseAllOf.
+        """Sets the conll of this ExerciseExtension.
 
         CONLL-formatted linguistic annotations represented as a single string.  # noqa: E501
 
-        :param conll: The conll of this ExerciseAllOf.
+        :param conll: The conll of this ExerciseExtension.
         :type conll: str
         """
 
@@ -102,22 +97,22 @@ class ExerciseAllOf(Model):
 
     @property
     def eid(self):
-        """Gets the eid of this ExerciseAllOf.
+        """Gets the eid of this ExerciseExtension.
 
         Unique identifier (UUID) for the exercise.  # noqa: E501
 
-        :return: The eid of this ExerciseAllOf.
+        :return: The eid of this ExerciseExtension.
         :rtype: str
         """
         return self._eid
 
     @eid.setter
     def eid(self, eid):
-        """Sets the eid of this ExerciseAllOf.
+        """Sets the eid of this ExerciseExtension.
 
         Unique identifier (UUID) for the exercise.  # noqa: E501
 
-        :param eid: The eid of this ExerciseAllOf.
+        :param eid: The eid of this ExerciseExtension.
         :type eid: str
         """
         if eid is None:
@@ -127,68 +122,45 @@ class ExerciseAllOf(Model):
 
     @property
     def exercise_type(self):
-        """Gets the exercise_type of this ExerciseAllOf.
+        """Gets the exercise_type of this ExerciseExtension.
 
         Type of exercise, concerning interaction and layout.  # noqa: E501
 
-        :return: The exercise_type of this ExerciseAllOf.
+        :return: The exercise_type of this ExerciseExtension.
         :rtype: str
         """
         return self._exercise_type
 
     @exercise_type.setter
     def exercise_type(self, exercise_type):
-        """Sets the exercise_type of this ExerciseAllOf.
+        """Sets the exercise_type of this ExerciseExtension.
 
         Type of exercise, concerning interaction and layout.  # noqa: E501
 
-        :param exercise_type: The exercise_type of this ExerciseAllOf.
+        :param exercise_type: The exercise_type of this ExerciseExtension.
         :type exercise_type: str
         """
 
         self._exercise_type = exercise_type
 
-    @property
-    def exercise_type_translation(self):
-        """Gets the exercise_type_translation of this ExerciseAllOf.
-
-        Localized expression of the exercise type.  # noqa: E501
-
-        :return: The exercise_type_translation of this ExerciseAllOf.
-        :rtype: str
-        """
-        return self._exercise_type_translation
-
-    @exercise_type_translation.setter
-    def exercise_type_translation(self, exercise_type_translation):
-        """Sets the exercise_type_translation of this ExerciseAllOf.
-
-        Localized expression of the exercise type.  # noqa: E501
-
-        :param exercise_type_translation: The exercise_type_translation of this ExerciseAllOf.
-        :type exercise_type_translation: str
-        """
-
-        self._exercise_type_translation = exercise_type_translation
-
     @property
     def last_access_time(self):
-        """Gets the last_access_time of this ExerciseAllOf.
+        """Gets the last_access_time of this ExerciseExtension.
 
         When the exercise was last accessed (as POSIX timestamp).  # noqa: E501
 
-        :return: The last_access_time of this ExerciseAllOf.
+        :return: The last_access_time of this ExerciseExtension.
         :rtype: float
         """
         return self._last_access_time
 
     @last_access_time.setter
     def last_access_time(self, last_access_time):
-        """Sets the last_access_time of this ExerciseAllOf.
+        """Sets the last_access_time of this ExerciseExtension.
 
         When the exercise was last accessed (as POSIX timestamp).  # noqa: E501
 
-        :param last_access_time: The last_access_time of this ExerciseAllOf.
+        :param last_access_time: The last_access_time of this ExerciseExtension.
         :type last_access_time: float
         """
         if last_access_time is None:
@@ -198,22 +170,22 @@ class ExerciseAllOf(Model):
 
     @property
     def solutions(self):
-        """Gets the solutions of this ExerciseAllOf.
+        """Gets the solutions of this ExerciseExtension.
 
         Correct solutions for the exercise.  # noqa: E501
 
-        :return: The solutions of this ExerciseAllOf.
+        :return: The solutions of this ExerciseExtension.
         :rtype: str
         """
         return self._solutions
 
     @solutions.setter
     def solutions(self, solutions):
-        """Sets the solutions of this ExerciseAllOf.
+        """Sets the solutions of this ExerciseExtension.
 
         Correct solutions for the exercise.  # noqa: E501
 
-        :param solutions: The solutions of this ExerciseAllOf.
+        :param solutions: The solutions of this ExerciseExtension.
         :type solutions: str
         """
 
@@ -221,22 +193,22 @@ class ExerciseAllOf(Model):
 
     @property
     def text_complexity(self):
-        """Gets the text_complexity of this ExerciseAllOf.
+        """Gets the text_complexity of this ExerciseExtension.
 
         Overall text complexity as measured by the software's internal language analysis.  # noqa: E501
 
-        :return: The text_complexity of this ExerciseAllOf.
+        :return: The text_complexity of this ExerciseExtension.
         :rtype: float
         """
         return self._text_complexity
 
     @text_complexity.setter
     def text_complexity(self, text_complexity):
-        """Sets the text_complexity of this ExerciseAllOf.
+        """Sets the text_complexity of this ExerciseExtension.
 
         Overall text complexity as measured by the software's internal language analysis.  # noqa: E501
 
-        :param text_complexity: The text_complexity of this ExerciseAllOf.
+        :param text_complexity: The text_complexity of this ExerciseExtension.
         :type text_complexity: float
         """
 
@@ -244,22 +216,22 @@ class ExerciseAllOf(Model):
 
     @property
     def urn(self):
-        """Gets the urn of this ExerciseAllOf.
+        """Gets the urn of this ExerciseExtension.
 
         CTS URN for the text passage from which the exercise was created.  # noqa: E501
 
-        :return: The urn of this ExerciseAllOf.
+        :return: The urn of this ExerciseExtension.
         :rtype: str
         """
         return self._urn
 
     @urn.setter
     def urn(self, urn):
-        """Sets the urn of this ExerciseAllOf.
+        """Sets the urn of this ExerciseExtension.
 
         CTS URN for the text passage from which the exercise was created.  # noqa: E501
 
-        :param urn: The urn of this ExerciseAllOf.
+        :param urn: The urn of this ExerciseExtension.
         :type urn: str
         """
 
diff --git a/mc_backend/openapi/openapi_server/models/exercise_form_comp.py b/mc_backend/openapi/openapi_server/models/exercise_form_comp.py
deleted file mode 100644
index 7bd9359..0000000
--- a/mc_backend/openapi/openapi_server/models/exercise_form_comp.py
+++ /dev/null
@@ -1,356 +0,0 @@
-# coding: utf-8
-
-from __future__ import absolute_import
-from datetime import date, datetime  # noqa: F401
-
-from typing import List, Dict  # noqa: F401
-
-from openapi.openapi_server.models.base_model_ import Model
-from openapi.openapi_server.models.exercise_base import ExerciseBase
-from openapi.openapi_server.models.exercise_form import ExerciseForm
-from openapi.openapi_server import util
-
-from openapi.openapi_server.models.exercise_base import ExerciseBase  # noqa: E501
-from openapi.openapi_server.models.exercise_form import ExerciseForm  # noqa: E501
-
-class ExerciseFormComp(Model):
-    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
-
-    Do not edit the class manually.
-    """
-
-    def __init__(self, correct_feedback='', general_feedback='', incorrect_feedback='', instructions='', partially_correct_feedback='', search_values='[]', work_author='', work_title='', type=None, type_translation=None, urn=None):  # noqa: E501
-        """ExerciseFormComp - a model defined in OpenAPI
-
-        :param correct_feedback: The correct_feedback of this ExerciseFormComp.  # noqa: E501
-        :type correct_feedback: str
-        :param general_feedback: The general_feedback of this ExerciseFormComp.  # noqa: E501
-        :type general_feedback: str
-        :param incorrect_feedback: The incorrect_feedback of this ExerciseFormComp.  # noqa: E501
-        :type incorrect_feedback: str
-        :param instructions: The instructions of this ExerciseFormComp.  # noqa: E501
-        :type instructions: str
-        :param partially_correct_feedback: The partially_correct_feedback of this ExerciseFormComp.  # noqa: E501
-        :type partially_correct_feedback: str
-        :param search_values: The search_values of this ExerciseFormComp.  # noqa: E501
-        :type search_values: str
-        :param work_author: The work_author of this ExerciseFormComp.  # noqa: E501
-        :type work_author: str
-        :param work_title: The work_title of this ExerciseFormComp.  # noqa: E501
-        :type work_title: str
-        :param type: The type of this ExerciseFormComp.  # noqa: E501
-        :type type: str
-        :param type_translation: The type_translation of this ExerciseFormComp.  # noqa: E501
-        :type type_translation: str
-        :param urn: The urn of this ExerciseFormComp.  # noqa: E501
-        :type urn: str
-        """
-        self.openapi_types = {
-            'correct_feedback': str,
-            'general_feedback': str,
-            'incorrect_feedback': str,
-            'instructions': str,
-            'partially_correct_feedback': str,
-            'search_values': str,
-            'work_author': str,
-            'work_title': str,
-            'type': str,
-            'type_translation': str,
-            'urn': str
-        }
-
-        self.attribute_map = {
-            'correct_feedback': 'correct_feedback',
-            'general_feedback': 'general_feedback',
-            'incorrect_feedback': 'incorrect_feedback',
-            'instructions': 'instructions',
-            'partially_correct_feedback': 'partially_correct_feedback',
-            'search_values': 'search_values',
-            'work_author': 'work_author',
-            'work_title': 'work_title',
-            'type': 'type',
-            'type_translation': 'type_translation',
-            'urn': 'urn'
-        }
-
-        self._correct_feedback = correct_feedback
-        self._general_feedback = general_feedback
-        self._incorrect_feedback = incorrect_feedback
-        self._instructions = instructions
-        self._partially_correct_feedback = partially_correct_feedback
-        self._search_values = search_values
-        self._work_author = work_author
-        self._work_title = work_title
-        self._type = type
-        self._type_translation = type_translation
-        self._urn = urn
-
-    @classmethod
-    def from_dict(cls, dikt) -> 'ExerciseFormComp':
-        """Returns the dict as a model
-
-        :param dikt: A dict.
-        :type: dict
-        :return: The ExerciseFormComp of this ExerciseFormComp.  # noqa: E501
-        :rtype: ExerciseFormComp
-        """
-        return util.deserialize_model(dikt, cls)
-
-    @property
-    def correct_feedback(self):
-        """Gets the correct_feedback of this ExerciseFormComp.
-
-        Feedback for successful completion of the exercise.  # noqa: E501
-
-        :return: The correct_feedback of this ExerciseFormComp.
-        :rtype: str
-        """
-        return self._correct_feedback
-
-    @correct_feedback.setter
-    def correct_feedback(self, correct_feedback):
-        """Sets the correct_feedback of this ExerciseFormComp.
-
-        Feedback for successful completion of the exercise.  # noqa: E501
-
-        :param correct_feedback: The correct_feedback of this ExerciseFormComp.
-        :type correct_feedback: str
-        """
-
-        self._correct_feedback = correct_feedback
-
-    @property
-    def general_feedback(self):
-        """Gets the general_feedback of this ExerciseFormComp.
-
-        Feedback for finishing the exercise.  # noqa: E501
-
-        :return: The general_feedback of this ExerciseFormComp.
-        :rtype: str
-        """
-        return self._general_feedback
-
-    @general_feedback.setter
-    def general_feedback(self, general_feedback):
-        """Sets the general_feedback of this ExerciseFormComp.
-
-        Feedback for finishing the exercise.  # noqa: E501
-
-        :param general_feedback: The general_feedback of this ExerciseFormComp.
-        :type general_feedback: str
-        """
-
-        self._general_feedback = general_feedback
-
-    @property
-    def incorrect_feedback(self):
-        """Gets the incorrect_feedback of this ExerciseFormComp.
-
-        Feedback for failing to complete the exercise successfully.  # noqa: E501
-
-        :return: The incorrect_feedback of this ExerciseFormComp.
-        :rtype: str
-        """
-        return self._incorrect_feedback
-
-    @incorrect_feedback.setter
-    def incorrect_feedback(self, incorrect_feedback):
-        """Sets the incorrect_feedback of this ExerciseFormComp.
-
-        Feedback for failing to complete the exercise successfully.  # noqa: E501
-
-        :param incorrect_feedback: The incorrect_feedback of this ExerciseFormComp.
-        :type incorrect_feedback: str
-        """
-
-        self._incorrect_feedback = incorrect_feedback
-
-    @property
-    def instructions(self):
-        """Gets the instructions of this ExerciseFormComp.
-
-        Hints for how to complete the exercise.  # noqa: E501
-
-        :return: The instructions of this ExerciseFormComp.
-        :rtype: str
-        """
-        return self._instructions
-
-    @instructions.setter
-    def instructions(self, instructions):
-        """Sets the instructions of this ExerciseFormComp.
-
-        Hints for how to complete the exercise.  # noqa: E501
-
-        :param instructions: The instructions of this ExerciseFormComp.
-        :type instructions: str
-        """
-        if instructions is None:
-            raise ValueError("Invalid value for `instructions`, must not be `None`")  # noqa: E501
-
-        self._instructions = instructions
-
-    @property
-    def partially_correct_feedback(self):
-        """Gets the partially_correct_feedback of this ExerciseFormComp.
-
-        Feedback for successfully completing certain parts of the exercise.  # noqa: E501
-
-        :return: The partially_correct_feedback of this ExerciseFormComp.
-        :rtype: str
-        """
-        return self._partially_correct_feedback
-
-    @partially_correct_feedback.setter
-    def partially_correct_feedback(self, partially_correct_feedback):
-        """Sets the partially_correct_feedback of this ExerciseFormComp.
-
-        Feedback for successfully completing certain parts of the exercise.  # noqa: E501
-
-        :param partially_correct_feedback: The partially_correct_feedback of this ExerciseFormComp.
-        :type partially_correct_feedback: str
-        """
-
-        self._partially_correct_feedback = partially_correct_feedback
-
-    @property
-    def search_values(self):
-        """Gets the search_values of this ExerciseFormComp.
-
-        Search queries that were used to build the exercise.  # noqa: E501
-
-        :return: The search_values of this ExerciseFormComp.
-        :rtype: str
-        """
-        return self._search_values
-
-    @search_values.setter
-    def search_values(self, search_values):
-        """Sets the search_values of this ExerciseFormComp.
-
-        Search queries that were used to build the exercise.  # noqa: E501
-
-        :param search_values: The search_values of this ExerciseFormComp.
-        :type search_values: str
-        """
-        if search_values is None:
-            raise ValueError("Invalid value for `search_values`, must not be `None`")  # noqa: E501
-
-        self._search_values = search_values
-
-    @property
-    def work_author(self):
-        """Gets the work_author of this ExerciseFormComp.
-
-        Name of the person who wrote the base text for the exercise.  # noqa: E501
-
-        :return: The work_author of this ExerciseFormComp.
-        :rtype: str
-        """
-        return self._work_author
-
-    @work_author.setter
-    def work_author(self, work_author):
-        """Sets the work_author of this ExerciseFormComp.
-
-        Name of the person who wrote the base text for the exercise.  # noqa: E501
-
-        :param work_author: The work_author of this ExerciseFormComp.
-        :type work_author: str
-        """
-
-        self._work_author = work_author
-
-    @property
-    def work_title(self):
-        """Gets the work_title of this ExerciseFormComp.
-
-        Title of the base text for the exercise.  # noqa: E501
-
-        :return: The work_title of this ExerciseFormComp.
-        :rtype: str
-        """
-        return self._work_title
-
-    @work_title.setter
-    def work_title(self, work_title):
-        """Sets the work_title of this ExerciseFormComp.
-
-        Title of the base text for the exercise.  # noqa: E501
-
-        :param work_title: The work_title of this ExerciseFormComp.
-        :type work_title: str
-        """
-
-        self._work_title = work_title
-
-    @property
-    def type(self):
-        """Gets the type of this ExerciseFormComp.
-
-        Type of exercise, concerning interaction and layout.  # noqa: E501
-
-        :return: The type of this ExerciseFormComp.
-        :rtype: str
-        """
-        return self._type
-
-    @type.setter
-    def type(self, type):
-        """Sets the type of this ExerciseFormComp.
-
-        Type of exercise, concerning interaction and layout.  # noqa: E501
-
-        :param type: The type of this ExerciseFormComp.
-        :type type: str
-        """
-        if type is None:
-            raise ValueError("Invalid value for `type`, must not be `None`")  # noqa: E501
-
-        self._type = type
-
-    @property
-    def type_translation(self):
-        """Gets the type_translation of this ExerciseFormComp.
-
-        Localized expression of the exercise type.  # noqa: E501
-
-        :return: The type_translation of this ExerciseFormComp.
-        :rtype: str
-        """
-        return self._type_translation
-
-    @type_translation.setter
-    def type_translation(self, type_translation):
-        """Sets the type_translation of this ExerciseFormComp.
-
-        Localized expression of the exercise type.  # noqa: E501
-
-        :param type_translation: The type_translation of this ExerciseFormComp.
-        :type type_translation: str
-        """
-
-        self._type_translation = type_translation
-
-    @property
-    def urn(self):
-        """Gets the urn of this ExerciseFormComp.
-
-        CTS URN for the text passage from which the exercise was created.  # noqa: E501
-
-        :return: The urn of this ExerciseFormComp.
-        :rtype: str
-        """
-        return self._urn
-
-    @urn.setter
-    def urn(self, urn):
-        """Sets the urn of this ExerciseFormComp.
-
-        CTS URN for the text passage from which the exercise was created.  # noqa: E501
-
-        :param urn: The urn of this ExerciseFormComp.
-        :type urn: str
-        """
-
-        self._urn = urn
diff --git a/mc_backend/openapi/openapi_server/models/frequency_item.py b/mc_backend/openapi/openapi_server/models/frequency_item.py
index 8a61b35..3b3f052 100644
--- a/mc_backend/openapi/openapi_server/models/frequency_item.py
+++ b/mc_backend/openapi/openapi_server/models/frequency_item.py
@@ -17,7 +17,7 @@ class FrequencyItem(Model):
     Do not edit the class manually.
     """
 
-    def __init__(self, count=None, phenomena=None, values=None):  # noqa: E501
+    def __init__(self, count=0, phenomena=None, values=None):  # noqa: E501
         """FrequencyItem - a model defined in OpenAPI
 
         :param count: The count of this FrequencyItem.  # noqa: E501
diff --git a/mc_backend/openapi/openapi_server/models/inline_response200.py b/mc_backend/openapi/openapi_server/models/inline_response200.py
deleted file mode 100644
index 9adccfa..0000000
--- a/mc_backend/openapi/openapi_server/models/inline_response200.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# coding: utf-8
-
-from __future__ import absolute_import
-from datetime import date, datetime  # noqa: F401
-
-from typing import List, Dict  # noqa: F401
-
-from openapi.openapi_server.models.base_model_ import Model
-from openapi.openapi_server import util
-
-
-class InlineResponse200(Model):
-    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
-
-    Do not edit the class manually.
-    """
-
-    def __init__(self, solutions=None, urn=None):  # noqa: E501
-        """InlineResponse200 - a model defined in OpenAPI
-
-        :param solutions: The solutions of this InlineResponse200.  # noqa: E501
-        :type solutions: List[List[str]]
-        :param urn: The urn of this InlineResponse200.  # noqa: E501
-        :type urn: str
-        """
-        self.openapi_types = {
-            'solutions': List[List[str]],
-            'urn': str
-        }
-
-        self.attribute_map = {
-            'solutions': 'solutions',
-            'urn': 'urn'
-        }
-
-        self._solutions = solutions
-        self._urn = urn
-
-    @classmethod
-    def from_dict(cls, dikt) -> 'InlineResponse200':
-        """Returns the dict as a model
-
-        :param dikt: A dict.
-        :type: dict
-        :return: The inline_response_200 of this InlineResponse200.  # noqa: E501
-        :rtype: InlineResponse200
-        """
-        return util.deserialize_model(dikt, cls)
-
-    @property
-    def solutions(self):
-        """Gets the solutions of this InlineResponse200.
-
-        Solutions for the exercise.  # noqa: E501
-
-        :return: The solutions of this InlineResponse200.
-        :rtype: List[List[str]]
-        """
-        return self._solutions
-
-    @solutions.setter
-    def solutions(self, solutions):
-        """Sets the solutions of this InlineResponse200.
-
-        Solutions for the exercise.  # noqa: E501
-
-        :param solutions: The solutions of this InlineResponse200.
-        :type solutions: List[List[str]]
-        """
-
-        self._solutions = solutions
-
-    @property
-    def urn(self):
-        """Gets the urn of this InlineResponse200.
-
-        CTS URN for the text passage from which the exercise was created.  # noqa: E501
-
-        :return: The urn of this InlineResponse200.
-        :rtype: str
-        """
-        return self._urn
-
-    @urn.setter
-    def urn(self, urn):
-        """Sets the urn of this InlineResponse200.
-
-        CTS URN for the text passage from which the exercise was created.  # noqa: E501
-
-        :param urn: The urn of this InlineResponse200.
-        :type urn: str
-        """
-
-        self._urn = urn
diff --git a/mc_backend/openapi/openapi_server/models/inline_response200_frequency_analysis.py b/mc_backend/openapi/openapi_server/models/inline_response200_frequency_analysis.py
deleted file mode 100644
index 8a5e967..0000000
--- a/mc_backend/openapi/openapi_server/models/inline_response200_frequency_analysis.py
+++ /dev/null
@@ -1,122 +0,0 @@
-# coding: utf-8
-
-from __future__ import absolute_import
-from datetime import date, datetime  # noqa: F401
-
-from typing import List, Dict  # noqa: F401
-
-from openapi.openapi_server.models.base_model_ import Model
-from openapi.openapi_server import util
-
-
-class InlineResponse200FrequencyAnalysis(Model):
-    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
-
-    Do not edit the class manually.
-    """
-
-    def __init__(self, count=None, phenomena=None, values=None):  # noqa: E501
-        """InlineResponse200FrequencyAnalysis - a model defined in OpenAPI
-
-        :param count: The count of this InlineResponse200FrequencyAnalysis.  # noqa: E501
-        :type count: int
-        :param phenomena: The phenomena of this InlineResponse200FrequencyAnalysis.  # noqa: E501
-        :type phenomena: List[str]
-        :param values: The values of this InlineResponse200FrequencyAnalysis.  # noqa: E501
-        :type values: List[str]
-        """
-        self.openapi_types = {
-            'count': int,
-            'phenomena': List[str],
-            'values': List[str]
-        }
-
-        self.attribute_map = {
-            'count': 'count',
-            'phenomena': 'phenomena',
-            'values': 'values'
-        }
-
-        self._count = count
-        self._phenomena = phenomena
-        self._values = values
-
-    @classmethod
-    def from_dict(cls, dikt) -> 'InlineResponse200FrequencyAnalysis':
-        """Returns the dict as a model
-
-        :param dikt: A dict.
-        :type: dict
-        :return: The inline_response_200_frequency_analysis of this InlineResponse200FrequencyAnalysis.  # noqa: E501
-        :rtype: InlineResponse200FrequencyAnalysis
-        """
-        return util.deserialize_model(dikt, cls)
-
-    @property
-    def count(self):
-        """Gets the count of this InlineResponse200FrequencyAnalysis.
-
-        How often the given combination of values occurred.  # noqa: E501
-
-        :return: The count of this InlineResponse200FrequencyAnalysis.
-        :rtype: int
-        """
-        return self._count
-
-    @count.setter
-    def count(self, count):
-        """Sets the count of this InlineResponse200FrequencyAnalysis.
-
-        How often the given combination of values occurred.  # noqa: E501
-
-        :param count: The count of this InlineResponse200FrequencyAnalysis.
-        :type count: int
-        """
-
-        self._count = count
-
-    @property
-    def phenomena(self):
-        """Gets the phenomena of this InlineResponse200FrequencyAnalysis.
-
-        Labels for the phenomena described in this frequency entry.  # noqa: E501
-
-        :return: The phenomena of this InlineResponse200FrequencyAnalysis.
-        :rtype: List[str]
-        """
-        return self._phenomena
-
-    @phenomena.setter
-    def phenomena(self, phenomena):
-        """Sets the phenomena of this InlineResponse200FrequencyAnalysis.
-
-        Labels for the phenomena described in this frequency entry.  # noqa: E501
-
-        :param phenomena: The phenomena of this InlineResponse200FrequencyAnalysis.
-        :type phenomena: List[str]
-        """
-
-        self._phenomena = phenomena
-
-    @property
-    def values(self):
-        """Gets the values of this InlineResponse200FrequencyAnalysis.
-
-        Values for the phenomena described in this frequency entry.  # noqa: E501
-
-        :return: The values of this InlineResponse200FrequencyAnalysis.
-        :rtype: List[str]
-        """
-        return self._values
-
-    @values.setter
-    def values(self, values):
-        """Sets the values of this InlineResponse200FrequencyAnalysis.
-
-        Values for the phenomena described in this frequency entry.  # noqa: E501
-
-        :param values: The values of this InlineResponse200FrequencyAnalysis.
-        :type values: List[str]
-        """
-
-        self._values = values
diff --git a/mc_backend/openapi/openapi_server/models/inline_response200_text_complexity.py b/mc_backend/openapi/openapi_server/models/inline_response200_text_complexity.py
deleted file mode 100644
index fdc9c0f..0000000
--- a/mc_backend/openapi/openapi_server/models/inline_response200_text_complexity.py
+++ /dev/null
@@ -1,462 +0,0 @@
-# coding: utf-8
-
-from __future__ import absolute_import
-from datetime import date, datetime  # noqa: F401
-
-from typing import List, Dict  # noqa: F401
-
-from openapi.openapi_server.models.base_model_ import Model
-from openapi.openapi_server import util
-
-
-class InlineResponse200TextComplexity(Model):
-    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
-
-    Do not edit the class manually.
-    """
-
-    def __init__(self, all=None, avg_w_len=None, avg_w_per_sent=None, lex_den=None, n_abl_abs=None, n_clause=None, n_gerund=None, n_inf=None, n_part=None, n_punct=None, n_sent=None, n_subclause=None, n_types=None, n_w=None, pos=None):  # noqa: E501
-        """InlineResponse200TextComplexity - a model defined in OpenAPI
-
-        :param all: The all of this InlineResponse200TextComplexity.  # noqa: E501
-        :type all: float
-        :param avg_w_len: The avg_w_len of this InlineResponse200TextComplexity.  # noqa: E501
-        :type avg_w_len: float
-        :param avg_w_per_sent: The avg_w_per_sent of this InlineResponse200TextComplexity.  # noqa: E501
-        :type avg_w_per_sent: float
-        :param lex_den: The lex_den of this InlineResponse200TextComplexity.  # noqa: E501
-        :type lex_den: float
-        :param n_abl_abs: The n_abl_abs of this InlineResponse200TextComplexity.  # noqa: E501
-        :type n_abl_abs: int
-        :param n_clause: The n_clause of this InlineResponse200TextComplexity.  # noqa: E501
-        :type n_clause: int
-        :param n_gerund: The n_gerund of this InlineResponse200TextComplexity.  # noqa: E501
-        :type n_gerund: int
-        :param n_inf: The n_inf of this InlineResponse200TextComplexity.  # noqa: E501
-        :type n_inf: int
-        :param n_part: The n_part of this InlineResponse200TextComplexity.  # noqa: E501
-        :type n_part: int
-        :param n_punct: The n_punct of this InlineResponse200TextComplexity.  # noqa: E501
-        :type n_punct: int
-        :param n_sent: The n_sent of this InlineResponse200TextComplexity.  # noqa: E501
-        :type n_sent: int
-        :param n_subclause: The n_subclause of this InlineResponse200TextComplexity.  # noqa: E501
-        :type n_subclause: int
-        :param n_types: The n_types of this InlineResponse200TextComplexity.  # noqa: E501
-        :type n_types: int
-        :param n_w: The n_w of this InlineResponse200TextComplexity.  # noqa: E501
-        :type n_w: int
-        :param pos: The pos of this InlineResponse200TextComplexity.  # noqa: E501
-        :type pos: int
-        """
-        self.openapi_types = {
-            'all': float,
-            'avg_w_len': float,
-            'avg_w_per_sent': float,
-            'lex_den': float,
-            'n_abl_abs': int,
-            'n_clause': int,
-            'n_gerund': int,
-            'n_inf': int,
-            'n_part': int,
-            'n_punct': int,
-            'n_sent': int,
-            'n_subclause': int,
-            'n_types': int,
-            'n_w': int,
-            'pos': int
-        }
-
-        self.attribute_map = {
-            'all': 'all',
-            'avg_w_len': 'avg_w_len',
-            'avg_w_per_sent': 'avg_w_per_sent',
-            'lex_den': 'lex_den',
-            'n_abl_abs': 'n_abl_abs',
-            'n_clause': 'n_clause',
-            'n_gerund': 'n_gerund',
-            'n_inf': 'n_inf',
-            'n_part': 'n_part',
-            'n_punct': 'n_punct',
-            'n_sent': 'n_sent',
-            'n_subclause': 'n_subclause',
-            'n_types': 'n_types',
-            'n_w': 'n_w',
-            'pos': 'pos'
-        }
-
-        self._all = all
-        self._avg_w_len = avg_w_len
-        self._avg_w_per_sent = avg_w_per_sent
-        self._lex_den = lex_den
-        self._n_abl_abs = n_abl_abs
-        self._n_clause = n_clause
-        self._n_gerund = n_gerund
-        self._n_inf = n_inf
-        self._n_part = n_part
-        self._n_punct = n_punct
-        self._n_sent = n_sent
-        self._n_subclause = n_subclause
-        self._n_types = n_types
-        self._n_w = n_w
-        self._pos = pos
-
-    @classmethod
-    def from_dict(cls, dikt) -> 'InlineResponse200TextComplexity':
-        """Returns the dict as a model
-
-        :param dikt: A dict.
-        :type: dict
-        :return: The inline_response_200_text_complexity of this InlineResponse200TextComplexity.  # noqa: E501
-        :rtype: InlineResponse200TextComplexity
-        """
-        return util.deserialize_model(dikt, cls)
-
-    @property
-    def all(self):
-        """Gets the all of this InlineResponse200TextComplexity.
-
-        Overall text complexity of the given corpus.  # noqa: E501
-
-        :return: The all of this InlineResponse200TextComplexity.
-        :rtype: float
-        """
-        return self._all
-
-    @all.setter
-    def all(self, all):
-        """Sets the all of this InlineResponse200TextComplexity.
-
-        Overall text complexity of the given corpus.  # noqa: E501
-
-        :param all: The all of this InlineResponse200TextComplexity.
-        :type all: float
-        """
-
-        self._all = all
-
-    @property
-    def avg_w_len(self):
-        """Gets the avg_w_len of this InlineResponse200TextComplexity.
-
-        Average length of a word in the given corpus.  # noqa: E501
-
-        :return: The avg_w_len of this InlineResponse200TextComplexity.
-        :rtype: float
-        """
-        return self._avg_w_len
-
-    @avg_w_len.setter
-    def avg_w_len(self, avg_w_len):
-        """Sets the avg_w_len of this InlineResponse200TextComplexity.
-
-        Average length of a word in the given corpus.  # noqa: E501
-
-        :param avg_w_len: The avg_w_len of this InlineResponse200TextComplexity.
-        :type avg_w_len: float
-        """
-
-        self._avg_w_len = avg_w_len
-
-    @property
-    def avg_w_per_sent(self):
-        """Gets the avg_w_per_sent of this InlineResponse200TextComplexity.
-
-        Average number of words per sentence.  # noqa: E501
-
-        :return: The avg_w_per_sent of this InlineResponse200TextComplexity.
-        :rtype: float
-        """
-        return self._avg_w_per_sent
-
-    @avg_w_per_sent.setter
-    def avg_w_per_sent(self, avg_w_per_sent):
-        """Sets the avg_w_per_sent of this InlineResponse200TextComplexity.
-
-        Average number of words per sentence.  # noqa: E501
-
-        :param avg_w_per_sent: The avg_w_per_sent of this InlineResponse200TextComplexity.
-        :type avg_w_per_sent: float
-        """
-
-        self._avg_w_per_sent = avg_w_per_sent
-
-    @property
-    def lex_den(self):
-        """Gets the lex_den of this InlineResponse200TextComplexity.
-
-        Lexical density of the given corpus.  # noqa: E501
-
-        :return: The lex_den of this InlineResponse200TextComplexity.
-        :rtype: float
-        """
-        return self._lex_den
-
-    @lex_den.setter
-    def lex_den(self, lex_den):
-        """Sets the lex_den of this InlineResponse200TextComplexity.
-
-        Lexical density of the given corpus.  # noqa: E501
-
-        :param lex_den: The lex_den of this InlineResponse200TextComplexity.
-        :type lex_den: float
-        """
-        if lex_den is not None and lex_den > 1:  # noqa: E501
-            raise ValueError("Invalid value for `lex_den`, must be a value less than or equal to `1`")  # noqa: E501
-        if lex_den is not None and lex_den < 0:  # noqa: E501
-            raise ValueError("Invalid value for `lex_den`, must be a value greater than or equal to `0`")  # noqa: E501
-
-        self._lex_den = lex_den
-
-    @property
-    def n_abl_abs(self):
-        """Gets the n_abl_abs of this InlineResponse200TextComplexity.
-
-        Number of ablativi absoluti in the given corpus.  # noqa: E501
-
-        :return: The n_abl_abs of this InlineResponse200TextComplexity.
-        :rtype: int
-        """
-        return self._n_abl_abs
-
-    @n_abl_abs.setter
-    def n_abl_abs(self, n_abl_abs):
-        """Sets the n_abl_abs of this InlineResponse200TextComplexity.
-
-        Number of ablativi absoluti in the given corpus.  # noqa: E501
-
-        :param n_abl_abs: The n_abl_abs of this InlineResponse200TextComplexity.
-        :type n_abl_abs: int
-        """
-
-        self._n_abl_abs = n_abl_abs
-
-    @property
-    def n_clause(self):
-        """Gets the n_clause of this InlineResponse200TextComplexity.
-
-        Number of clauses in the given corpus.  # noqa: E501
-
-        :return: The n_clause of this InlineResponse200TextComplexity.
-        :rtype: int
-        """
-        return self._n_clause
-
-    @n_clause.setter
-    def n_clause(self, n_clause):
-        """Sets the n_clause of this InlineResponse200TextComplexity.
-
-        Number of clauses in the given corpus.  # noqa: E501
-
-        :param n_clause: The n_clause of this InlineResponse200TextComplexity.
-        :type n_clause: int
-        """
-
-        self._n_clause = n_clause
-
-    @property
-    def n_gerund(self):
-        """Gets the n_gerund of this InlineResponse200TextComplexity.
-
-        Number of gerunds in the given corpus.  # noqa: E501
-
-        :return: The n_gerund of this InlineResponse200TextComplexity.
-        :rtype: int
-        """
-        return self._n_gerund
-
-    @n_gerund.setter
-    def n_gerund(self, n_gerund):
-        """Sets the n_gerund of this InlineResponse200TextComplexity.
-
-        Number of gerunds in the given corpus.  # noqa: E501
-
-        :param n_gerund: The n_gerund of this InlineResponse200TextComplexity.
-        :type n_gerund: int
-        """
-
-        self._n_gerund = n_gerund
-
-    @property
-    def n_inf(self):
-        """Gets the n_inf of this InlineResponse200TextComplexity.
-
-        Number of infinitives in the given corpus.  # noqa: E501
-
-        :return: The n_inf of this InlineResponse200TextComplexity.
-        :rtype: int
-        """
-        return self._n_inf
-
-    @n_inf.setter
-    def n_inf(self, n_inf):
-        """Sets the n_inf of this InlineResponse200TextComplexity.
-
-        Number of infinitives in the given corpus.  # noqa: E501
-
-        :param n_inf: The n_inf of this InlineResponse200TextComplexity.
-        :type n_inf: int
-        """
-
-        self._n_inf = n_inf
-
-    @property
-    def n_part(self):
-        """Gets the n_part of this InlineResponse200TextComplexity.
-
-        Number of participles in the given corpus.  # noqa: E501
-
-        :return: The n_part of this InlineResponse200TextComplexity.
-        :rtype: int
-        """
-        return self._n_part
-
-    @n_part.setter
-    def n_part(self, n_part):
-        """Sets the n_part of this InlineResponse200TextComplexity.
-
-        Number of participles in the given corpus.  # noqa: E501
-
-        :param n_part: The n_part of this InlineResponse200TextComplexity.
-        :type n_part: int
-        """
-
-        self._n_part = n_part
-
-    @property
-    def n_punct(self):
-        """Gets the n_punct of this InlineResponse200TextComplexity.
-
-        Number of punctuation signs in the given corpus.  # noqa: E501
-
-        :return: The n_punct of this InlineResponse200TextComplexity.
-        :rtype: int
-        """
-        return self._n_punct
-
-    @n_punct.setter
-    def n_punct(self, n_punct):
-        """Sets the n_punct of this InlineResponse200TextComplexity.
-
-        Number of punctuation signs in the given corpus.  # noqa: E501
-
-        :param n_punct: The n_punct of this InlineResponse200TextComplexity.
-        :type n_punct: int
-        """
-
-        self._n_punct = n_punct
-
-    @property
-    def n_sent(self):
-        """Gets the n_sent of this InlineResponse200TextComplexity.
-
-        Number of sentences in the given corpus.  # noqa: E501
-
-        :return: The n_sent of this InlineResponse200TextComplexity.
-        :rtype: int
-        """
-        return self._n_sent
-
-    @n_sent.setter
-    def n_sent(self, n_sent):
-        """Sets the n_sent of this InlineResponse200TextComplexity.
-
-        Number of sentences in the given corpus.  # noqa: E501
-
-        :param n_sent: The n_sent of this InlineResponse200TextComplexity.
-        :type n_sent: int
-        """
-
-        self._n_sent = n_sent
-
-    @property
-    def n_subclause(self):
-        """Gets the n_subclause of this InlineResponse200TextComplexity.
-
-        Number of subclauses in the given corpus.  # noqa: E501
-
-        :return: The n_subclause of this InlineResponse200TextComplexity.
-        :rtype: int
-        """
-        return self._n_subclause
-
-    @n_subclause.setter
-    def n_subclause(self, n_subclause):
-        """Sets the n_subclause of this InlineResponse200TextComplexity.
-
-        Number of subclauses in the given corpus.  # noqa: E501
-
-        :param n_subclause: The n_subclause of this InlineResponse200TextComplexity.
-        :type n_subclause: int
-        """
-
-        self._n_subclause = n_subclause
-
-    @property
-    def n_types(self):
-        """Gets the n_types of this InlineResponse200TextComplexity.
-
-        Number of distinct word forms in the given corpus.  # noqa: E501
-
-        :return: The n_types of this InlineResponse200TextComplexity.
-        :rtype: int
-        """
-        return self._n_types
-
-    @n_types.setter
-    def n_types(self, n_types):
-        """Sets the n_types of this InlineResponse200TextComplexity.
-
-        Number of distinct word forms in the given corpus.  # noqa: E501
-
-        :param n_types: The n_types of this InlineResponse200TextComplexity.
-        :type n_types: int
-        """
-
-        self._n_types = n_types
-
-    @property
-    def n_w(self):
-        """Gets the n_w of this InlineResponse200TextComplexity.
-
-        Number of words in the given corpus.  # noqa: E501
-
-        :return: The n_w of this InlineResponse200TextComplexity.
-        :rtype: int
-        """
-        return self._n_w
-
-    @n_w.setter
-    def n_w(self, n_w):
-        """Sets the n_w of this InlineResponse200TextComplexity.
-
-        Number of words in the given corpus.  # noqa: E501
-
-        :param n_w: The n_w of this InlineResponse200TextComplexity.
-        :type n_w: int
-        """
-
-        self._n_w = n_w
-
-    @property
-    def pos(self):
-        """Gets the pos of this InlineResponse200TextComplexity.
-
-        Number of distinct part of speech tags in the given corpus.  # noqa: E501
-
-        :return: The pos of this InlineResponse200TextComplexity.
-        :rtype: int
-        """
-        return self._pos
-
-    @pos.setter
-    def pos(self, pos):
-        """Sets the pos of this InlineResponse200TextComplexity.
-
-        Number of distinct part of speech tags in the given corpus.  # noqa: E501
-
-        :param pos: The pos of this InlineResponse200TextComplexity.
-        :type pos: int
-        """
-
-        self._pos = pos
diff --git a/mc_backend/openapi/openapi_server/models/learning_result.py b/mc_backend/openapi/openapi_server/models/learning_result.py
deleted file mode 100644
index 14aed0a..0000000
--- a/mc_backend/openapi/openapi_server/models/learning_result.py
+++ /dev/null
@@ -1,672 +0,0 @@
-# coding: utf-8
-
-from __future__ import absolute_import
-from datetime import date, datetime  # noqa: F401
-
-from typing import List, Dict  # noqa: F401
-
-from openapi.openapi_server.models.base_model_ import Model
-from openapi.openapi_server import util
-
-
-class LearningResult(Model):
-    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
-
-    Do not edit the class manually.
-    """
-
-    def __init__(self, actor_account_name='', actor_object_type='', category_id='', category_object_type='', choices='[]', completion=None, correct_responses_pattern=None, created_time=None, duration='PT0S', extensions='{}', interaction_type='', object_definition_description=None, object_definition_type='', object_object_type='', response=None, score_max=None, score_min=None, score_raw=None, score_scaled=0, success=None, verb_display='', verb_id=''):  # noqa: E501
-        """LearningResult - a model defined in OpenAPI
-
-        :param actor_account_name: The actor_account_name of this LearningResult.  # noqa: E501
-        :type actor_account_name: str
-        :param actor_object_type: The actor_object_type of this LearningResult.  # noqa: E501
-        :type actor_object_type: str
-        :param category_id: The category_id of this LearningResult.  # noqa: E501
-        :type category_id: str
-        :param category_object_type: The category_object_type of this LearningResult.  # noqa: E501
-        :type category_object_type: str
-        :param choices: The choices of this LearningResult.  # noqa: E501
-        :type choices: str
-        :param completion: The completion of this LearningResult.  # noqa: E501
-        :type completion: bool
-        :param correct_responses_pattern: The correct_responses_pattern of this LearningResult.  # noqa: E501
-        :type correct_responses_pattern: str
-        :param created_time: The created_time of this LearningResult.  # noqa: E501
-        :type created_time: float
-        :param duration: The duration of this LearningResult.  # noqa: E501
-        :type duration: str
-        :param extensions: The extensions of this LearningResult.  # noqa: E501
-        :type extensions: str
-        :param interaction_type: The interaction_type of this LearningResult.  # noqa: E501
-        :type interaction_type: str
-        :param object_definition_description: The object_definition_description of this LearningResult.  # noqa: E501
-        :type object_definition_description: str
-        :param object_definition_type: The object_definition_type of this LearningResult.  # noqa: E501
-        :type object_definition_type: str
-        :param object_object_type: The object_object_type of this LearningResult.  # noqa: E501
-        :type object_object_type: str
-        :param response: The response of this LearningResult.  # noqa: E501
-        :type response: str
-        :param score_max: The score_max of this LearningResult.  # noqa: E501
-        :type score_max: int
-        :param score_min: The score_min of this LearningResult.  # noqa: E501
-        :type score_min: int
-        :param score_raw: The score_raw of this LearningResult.  # noqa: E501
-        :type score_raw: int
-        :param score_scaled: The score_scaled of this LearningResult.  # noqa: E501
-        :type score_scaled: float
-        :param success: The success of this LearningResult.  # noqa: E501
-        :type success: bool
-        :param verb_display: The verb_display of this LearningResult.  # noqa: E501
-        :type verb_display: str
-        :param verb_id: The verb_id of this LearningResult.  # noqa: E501
-        :type verb_id: str
-        """
-        self.openapi_types = {
-            'actor_account_name': str,
-            'actor_object_type': str,
-            'category_id': str,
-            'category_object_type': str,
-            'choices': str,
-            'completion': bool,
-            'correct_responses_pattern': str,
-            'created_time': float,
-            'duration': str,
-            'extensions': str,
-            'interaction_type': str,
-            'object_definition_description': str,
-            'object_definition_type': str,
-            'object_object_type': str,
-            'response': str,
-            'score_max': int,
-            'score_min': int,
-            'score_raw': int,
-            'score_scaled': float,
-            'success': bool,
-            'verb_display': str,
-            'verb_id': str
-        }
-
-        self.attribute_map = {
-            'actor_account_name': 'actor_account_name',
-            'actor_object_type': 'actor_object_type',
-            'category_id': 'category_id',
-            'category_object_type': 'category_object_type',
-            'choices': 'choices',
-            'completion': 'completion',
-            'correct_responses_pattern': 'correct_responses_pattern',
-            'created_time': 'created_time',
-            'duration': 'duration',
-            'extensions': 'extensions',
-            'interaction_type': 'interaction_type',
-            'object_definition_description': 'object_definition_description',
-            'object_definition_type': 'object_definition_type',
-            'object_object_type': 'object_object_type',
-            'response': 'response',
-            'score_max': 'score_max',
-            'score_min': 'score_min',
-            'score_raw': 'score_raw',
-            'score_scaled': 'score_scaled',
-            'success': 'success',
-            'verb_display': 'verb_display',
-            'verb_id': 'verb_id'
-        }
-
-        self._actor_account_name = actor_account_name
-        self._actor_object_type = actor_object_type
-        self._category_id = category_id
-        self._category_object_type = category_object_type
-        self._choices = choices
-        self._completion = completion
-        self._correct_responses_pattern = correct_responses_pattern
-        self._created_time = created_time
-        self._duration = duration
-        self._extensions = extensions
-        self._interaction_type = interaction_type
-        self._object_definition_description = object_definition_description
-        self._object_definition_type = object_definition_type
-        self._object_object_type = object_object_type
-        self._response = response
-        self._score_max = score_max
-        self._score_min = score_min
-        self._score_raw = score_raw
-        self._score_scaled = score_scaled
-        self._success = success
-        self._verb_display = verb_display
-        self._verb_id = verb_id
-
-    @classmethod
-    def from_dict(cls, dikt) -> 'LearningResult':
-        """Returns the dict as a model
-
-        :param dikt: A dict.
-        :type: dict
-        :return: The LearningResult of this LearningResult.  # noqa: E501
-        :rtype: LearningResult
-        """
-        return util.deserialize_model(dikt, cls)
-
-    @property
-    def actor_account_name(self):
-        """Gets the actor_account_name of this LearningResult.
-
-        H5P user ID, usually unique per device.  # noqa: E501
-
-        :return: The actor_account_name of this LearningResult.
-        :rtype: str
-        """
-        return self._actor_account_name
-
-    @actor_account_name.setter
-    def actor_account_name(self, actor_account_name):
-        """Sets the actor_account_name of this LearningResult.
-
-        H5P user ID, usually unique per device.  # noqa: E501
-
-        :param actor_account_name: The actor_account_name of this LearningResult.
-        :type actor_account_name: str
-        """
-
-        self._actor_account_name = actor_account_name
-
-    @property
-    def actor_object_type(self):
-        """Gets the actor_object_type of this LearningResult.
-
-        Describes the kind of object that was recognized as actor.  # noqa: E501
-
-        :return: The actor_object_type of this LearningResult.
-        :rtype: str
-        """
-        return self._actor_object_type
-
-    @actor_object_type.setter
-    def actor_object_type(self, actor_object_type):
-        """Sets the actor_object_type of this LearningResult.
-
-        Describes the kind of object that was recognized as actor.  # noqa: E501
-
-        :param actor_object_type: The actor_object_type of this LearningResult.
-        :type actor_object_type: str
-        """
-
-        self._actor_object_type = actor_object_type
-
-    @property
-    def category_id(self):
-        """Gets the category_id of this LearningResult.
-
-        Link to the exercise type specification.  # noqa: E501
-
-        :return: The category_id of this LearningResult.
-        :rtype: str
-        """
-        return self._category_id
-
-    @category_id.setter
-    def category_id(self, category_id):
-        """Sets the category_id of this LearningResult.
-
-        Link to the exercise type specification.  # noqa: E501
-
-        :param category_id: The category_id of this LearningResult.
-        :type category_id: str
-        """
-
-        self._category_id = category_id
-
-    @property
-    def category_object_type(self):
-        """Gets the category_object_type of this LearningResult.
-
-        Describes the kind of object that was recognized as exercise.  # noqa: E501
-
-        :return: The category_object_type of this LearningResult.
-        :rtype: str
-        """
-        return self._category_object_type
-
-    @category_object_type.setter
-    def category_object_type(self, category_object_type):
-        """Sets the category_object_type of this LearningResult.
-
-        Describes the kind of object that was recognized as exercise.  # noqa: E501
-
-        :param category_object_type: The category_object_type of this LearningResult.
-        :type category_object_type: str
-        """
-
-        self._category_object_type = category_object_type
-
-    @property
-    def choices(self):
-        """Gets the choices of this LearningResult.
-
-        JSON string containing a list of possible choices, each with ID and description.  # noqa: E501
-
-        :return: The choices of this LearningResult.
-        :rtype: str
-        """
-        return self._choices
-
-    @choices.setter
-    def choices(self, choices):
-        """Sets the choices of this LearningResult.
-
-        JSON string containing a list of possible choices, each with ID and description.  # noqa: E501
-
-        :param choices: The choices of this LearningResult.
-        :type choices: str
-        """
-
-        self._choices = choices
-
-    @property
-    def completion(self):
-        """Gets the completion of this LearningResult.
-
-        Whether the exercise was fully processed or not.  # noqa: E501
-
-        :return: The completion of this LearningResult.
-        :rtype: bool
-        """
-        return self._completion
-
-    @completion.setter
-    def completion(self, completion):
-        """Sets the completion of this LearningResult.
-
-        Whether the exercise was fully processed or not.  # noqa: E501
-
-        :param completion: The completion of this LearningResult.
-        :type completion: bool
-        """
-        if completion is None:
-            raise ValueError("Invalid value for `completion`, must not be `None`")  # noqa: E501
-
-        self._completion = completion
-
-    @property
-    def correct_responses_pattern(self):
-        """Gets the correct_responses_pattern of this LearningResult.
-
-        JSON string containing a list of possible solutions to the exercise, given as patterns of answers.  # noqa: E501
-
-        :return: The correct_responses_pattern of this LearningResult.
-        :rtype: str
-        """
-        return self._correct_responses_pattern
-
-    @correct_responses_pattern.setter
-    def correct_responses_pattern(self, correct_responses_pattern):
-        """Sets the correct_responses_pattern of this LearningResult.
-
-        JSON string containing a list of possible solutions to the exercise, given as patterns of answers.  # noqa: E501
-
-        :param correct_responses_pattern: The correct_responses_pattern of this LearningResult.
-        :type correct_responses_pattern: str
-        """
-        if correct_responses_pattern is None:
-            raise ValueError("Invalid value for `correct_responses_pattern`, must not be `None`")  # noqa: E501
-
-        self._correct_responses_pattern = correct_responses_pattern
-
-    @property
-    def created_time(self):
-        """Gets the created_time of this LearningResult.
-
-        When the learner data was received (POSIX timestamp).  # noqa: E501
-
-        :return: The created_time of this LearningResult.
-        :rtype: float
-        """
-        return self._created_time
-
-    @created_time.setter
-    def created_time(self, created_time):
-        """Sets the created_time of this LearningResult.
-
-        When the learner data was received (POSIX timestamp).  # noqa: E501
-
-        :param created_time: The created_time of this LearningResult.
-        :type created_time: float
-        """
-        if created_time is None:
-            raise ValueError("Invalid value for `created_time`, must not be `None`")  # noqa: E501
-
-        self._created_time = created_time
-
-    @property
-    def duration(self):
-        """Gets the duration of this LearningResult.
-
-        How many seconds it took a learner to complete the exercise.  # noqa: E501
-
-        :return: The duration of this LearningResult.
-        :rtype: str
-        """
-        return self._duration
-
-    @duration.setter
-    def duration(self, duration):
-        """Sets the duration of this LearningResult.
-
-        How many seconds it took a learner to complete the exercise.  # noqa: E501
-
-        :param duration: The duration of this LearningResult.
-        :type duration: str
-        """
-
-        self._duration = duration
-
-    @property
-    def extensions(self):
-        """Gets the extensions of this LearningResult.
-
-        JSON string containing a mapping of keys and values (usually the local content ID, i.e. a versioning mechanism).  # noqa: E501
-
-        :return: The extensions of this LearningResult.
-        :rtype: str
-        """
-        return self._extensions
-
-    @extensions.setter
-    def extensions(self, extensions):
-        """Sets the extensions of this LearningResult.
-
-        JSON string containing a mapping of keys and values (usually the local content ID, i.e. a versioning mechanism).  # noqa: E501
-
-        :param extensions: The extensions of this LearningResult.
-        :type extensions: str
-        """
-
-        self._extensions = extensions
-
-    @property
-    def interaction_type(self):
-        """Gets the interaction_type of this LearningResult.
-
-        Exercise type.  # noqa: E501
-
-        :return: The interaction_type of this LearningResult.
-        :rtype: str
-        """
-        return self._interaction_type
-
-    @interaction_type.setter
-    def interaction_type(self, interaction_type):
-        """Sets the interaction_type of this LearningResult.
-
-        Exercise type.  # noqa: E501
-
-        :param interaction_type: The interaction_type of this LearningResult.
-        :type interaction_type: str
-        """
-
-        self._interaction_type = interaction_type
-
-    @property
-    def object_definition_description(self):
-        """Gets the object_definition_description of this LearningResult.
-
-        Exercise content, possibly including instructions.  # noqa: E501
-
-        :return: The object_definition_description of this LearningResult.
-        :rtype: str
-        """
-        return self._object_definition_description
-
-    @object_definition_description.setter
-    def object_definition_description(self, object_definition_description):
-        """Sets the object_definition_description of this LearningResult.
-
-        Exercise content, possibly including instructions.  # noqa: E501
-
-        :param object_definition_description: The object_definition_description of this LearningResult.
-        :type object_definition_description: str
-        """
-        if object_definition_description is None:
-            raise ValueError("Invalid value for `object_definition_description`, must not be `None`")  # noqa: E501
-
-        self._object_definition_description = object_definition_description
-
-    @property
-    def object_definition_type(self):
-        """Gets the object_definition_type of this LearningResult.
-
-        Type of object definition that is presented to the user.  # noqa: E501
-
-        :return: The object_definition_type of this LearningResult.
-        :rtype: str
-        """
-        return self._object_definition_type
-
-    @object_definition_type.setter
-    def object_definition_type(self, object_definition_type):
-        """Sets the object_definition_type of this LearningResult.
-
-        Type of object definition that is presented to the user.  # noqa: E501
-
-        :param object_definition_type: The object_definition_type of this LearningResult.
-        :type object_definition_type: str
-        """
-
-        self._object_definition_type = object_definition_type
-
-    @property
-    def object_object_type(self):
-        """Gets the object_object_type of this LearningResult.
-
-        Type of object that is presented to the user.  # noqa: E501
-
-        :return: The object_object_type of this LearningResult.
-        :rtype: str
-        """
-        return self._object_object_type
-
-    @object_object_type.setter
-    def object_object_type(self, object_object_type):
-        """Sets the object_object_type of this LearningResult.
-
-        Type of object that is presented to the user.  # noqa: E501
-
-        :param object_object_type: The object_object_type of this LearningResult.
-        :type object_object_type: str
-        """
-
-        self._object_object_type = object_object_type
-
-    @property
-    def response(self):
-        """Gets the response of this LearningResult.
-
-        Answer provided by the user, possibly as a pattern.  # noqa: E501
-
-        :return: The response of this LearningResult.
-        :rtype: str
-        """
-        return self._response
-
-    @response.setter
-    def response(self, response):
-        """Sets the response of this LearningResult.
-
-        Answer provided by the user, possibly as a pattern.  # noqa: E501
-
-        :param response: The response of this LearningResult.
-        :type response: str
-        """
-        if response is None:
-            raise ValueError("Invalid value for `response`, must not be `None`")  # noqa: E501
-
-        self._response = response
-
-    @property
-    def score_max(self):
-        """Gets the score_max of this LearningResult.
-
-        Maximum possible score to be achieved in this exercise.  # noqa: E501
-
-        :return: The score_max of this LearningResult.
-        :rtype: int
-        """
-        return self._score_max
-
-    @score_max.setter
-    def score_max(self, score_max):
-        """Sets the score_max of this LearningResult.
-
-        Maximum possible score to be achieved in this exercise.  # noqa: E501
-
-        :param score_max: The score_max of this LearningResult.
-        :type score_max: int
-        """
-        if score_max is None:
-            raise ValueError("Invalid value for `score_max`, must not be `None`")  # noqa: E501
-
-        self._score_max = score_max
-
-    @property
-    def score_min(self):
-        """Gets the score_min of this LearningResult.
-
-        Minimum score to be achieved in this exercise.  # noqa: E501
-
-        :return: The score_min of this LearningResult.
-        :rtype: int
-        """
-        return self._score_min
-
-    @score_min.setter
-    def score_min(self, score_min):
-        """Sets the score_min of this LearningResult.
-
-        Minimum score to be achieved in this exercise.  # noqa: E501
-
-        :param score_min: The score_min of this LearningResult.
-        :type score_min: int
-        """
-        if score_min is None:
-            raise ValueError("Invalid value for `score_min`, must not be `None`")  # noqa: E501
-
-        self._score_min = score_min
-
-    @property
-    def score_raw(self):
-        """Gets the score_raw of this LearningResult.
-
-        Score that was actually achieved by the user in this exercise.  # noqa: E501
-
-        :return: The score_raw of this LearningResult.
-        :rtype: int
-        """
-        return self._score_raw
-
-    @score_raw.setter
-    def score_raw(self, score_raw):
-        """Sets the score_raw of this LearningResult.
-
-        Score that was actually achieved by the user in this exercise.  # noqa: E501
-
-        :param score_raw: The score_raw of this LearningResult.
-        :type score_raw: int
-        """
-        if score_raw is None:
-            raise ValueError("Invalid value for `score_raw`, must not be `None`")  # noqa: E501
-
-        self._score_raw = score_raw
-
-    @property
-    def score_scaled(self):
-        """Gets the score_scaled of this LearningResult.
-
-        Relative score (between 0 and 1) that was actually achieved by the user in this exercise.  # noqa: E501
-
-        :return: The score_scaled of this LearningResult.
-        :rtype: float
-        """
-        return self._score_scaled
-
-    @score_scaled.setter
-    def score_scaled(self, score_scaled):
-        """Sets the score_scaled of this LearningResult.
-
-        Relative score (between 0 and 1) that was actually achieved by the user in this exercise.  # noqa: E501
-
-        :param score_scaled: The score_scaled of this LearningResult.
-        :type score_scaled: float
-        """
-
-        self._score_scaled = score_scaled
-
-    @property
-    def success(self):
-        """Gets the success of this LearningResult.
-
-        Whether the exercise was successfully completed or not.  # noqa: E501
-
-        :return: The success of this LearningResult.
-        :rtype: bool
-        """
-        return self._success
-
-    @success.setter
-    def success(self, success):
-        """Sets the success of this LearningResult.
-
-        Whether the exercise was successfully completed or not.  # noqa: E501
-
-        :param success: The success of this LearningResult.
-        :type success: bool
-        """
-        if success is None:
-            raise ValueError("Invalid value for `success`, must not be `None`")  # noqa: E501
-
-        self._success = success
-
-    @property
-    def verb_display(self):
-        """Gets the verb_display of this LearningResult.
-
-        Type of action that was performed by the user.  # noqa: E501
-
-        :return: The verb_display of this LearningResult.
-        :rtype: str
-        """
-        return self._verb_display
-
-    @verb_display.setter
-    def verb_display(self, verb_display):
-        """Sets the verb_display of this LearningResult.
-
-        Type of action that was performed by the user.  # noqa: E501
-
-        :param verb_display: The verb_display of this LearningResult.
-        :type verb_display: str
-        """
-
-        self._verb_display = verb_display
-
-    @property
-    def verb_id(self):
-        """Gets the verb_id of this LearningResult.
-
-        Link to the type of action that was performed by the user.  # noqa: E501
-
-        :return: The verb_id of this LearningResult.
-        :rtype: str
-        """
-        return self._verb_id
-
-    @verb_id.setter
-    def verb_id(self, verb_id):
-        """Sets the verb_id of this LearningResult.
-
-        Link to the type of action that was performed by the user.  # noqa: E501
-
-        :param verb_id: The verb_id of this LearningResult.
-        :type verb_id: str
-        """
-
-        self._verb_id = verb_id
diff --git a/mc_backend/openapi/openapi_server/models/exercise.py b/mc_backend/openapi/openapi_server/models/matching_exercise.py
similarity index 62%
rename from mc_backend/openapi/openapi_server/models/exercise.py
rename to mc_backend/openapi/openapi_server/models/matching_exercise.py
index 52174ee..25e9b18 100644
--- a/mc_backend/openapi/openapi_server/models/exercise.py
+++ b/mc_backend/openapi/openapi_server/models/matching_exercise.py
@@ -6,56 +6,60 @@ from datetime import date, datetime  # noqa: F401
 from typing import List, Dict  # noqa: F401
 
 from openapi.openapi_server.models.base_model_ import Model
-from openapi.openapi_server.models.exercise_all_of import ExerciseAllOf
 from openapi.openapi_server.models.exercise_base import ExerciseBase
+from openapi.openapi_server.models.exercise_extension import ExerciseExtension
+from openapi.openapi_server.models.matching_exercise_all_of import MatchingExerciseAllOf
 from openapi.openapi_server import util
 
-from openapi.openapi_server.models.exercise_all_of import ExerciseAllOf  # noqa: E501
 from openapi.openapi_server.models.exercise_base import ExerciseBase  # noqa: E501
+from openapi.openapi_server.models.exercise_extension import ExerciseExtension  # noqa: E501
+from openapi.openapi_server.models.matching_exercise_all_of import MatchingExerciseAllOf  # noqa: E501
 
-class Exercise(Model):
+class MatchingExercise(Model):
     """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 
     Do not edit the class manually.
     """
 
-    def __init__(self, correct_feedback='', general_feedback='', incorrect_feedback='', instructions='', language='de', partially_correct_feedback='', search_values='[]', work_author='', work_title='', conll='', eid=None, exercise_type='', exercise_type_translation='', last_access_time=None, solutions='[]', text_complexity=0, urn=''):  # noqa: E501
-        """Exercise - a model defined in OpenAPI
+    def __init__(self, correct_feedback='', general_feedback='', incorrect_feedback='', instructions='', language='de', partially_correct_feedback='', search_values='[]', work_author='', work_title='', conll='', eid=None, exercise_type='', last_access_time=None, solutions='[]', text_complexity=0, urn='', exercise_type_translation=None, matching_degree=None):  # noqa: E501
+        """MatchingExercise - a model defined in OpenAPI
 
-        :param correct_feedback: The correct_feedback of this Exercise.  # noqa: E501
+        :param correct_feedback: The correct_feedback of this MatchingExercise.  # noqa: E501
         :type correct_feedback: str
-        :param general_feedback: The general_feedback of this Exercise.  # noqa: E501
+        :param general_feedback: The general_feedback of this MatchingExercise.  # noqa: E501
         :type general_feedback: str
-        :param incorrect_feedback: The incorrect_feedback of this Exercise.  # noqa: E501
+        :param incorrect_feedback: The incorrect_feedback of this MatchingExercise.  # noqa: E501
         :type incorrect_feedback: str
-        :param instructions: The instructions of this Exercise.  # noqa: E501
+        :param instructions: The instructions of this MatchingExercise.  # noqa: E501
         :type instructions: str
-        :param language: The language of this Exercise.  # noqa: E501
+        :param language: The language of this MatchingExercise.  # noqa: E501
         :type language: str
-        :param partially_correct_feedback: The partially_correct_feedback of this Exercise.  # noqa: E501
+        :param partially_correct_feedback: The partially_correct_feedback of this MatchingExercise.  # noqa: E501
         :type partially_correct_feedback: str
-        :param search_values: The search_values of this Exercise.  # noqa: E501
+        :param search_values: The search_values of this MatchingExercise.  # noqa: E501
         :type search_values: str
-        :param work_author: The work_author of this Exercise.  # noqa: E501
+        :param work_author: The work_author of this MatchingExercise.  # noqa: E501
         :type work_author: str
-        :param work_title: The work_title of this Exercise.  # noqa: E501
+        :param work_title: The work_title of this MatchingExercise.  # noqa: E501
         :type work_title: str
-        :param conll: The conll of this Exercise.  # noqa: E501
+        :param conll: The conll of this MatchingExercise.  # noqa: E501
         :type conll: str
-        :param eid: The eid of this Exercise.  # noqa: E501
+        :param eid: The eid of this MatchingExercise.  # noqa: E501
         :type eid: str
-        :param exercise_type: The exercise_type of this Exercise.  # noqa: E501
+        :param exercise_type: The exercise_type of this MatchingExercise.  # noqa: E501
         :type exercise_type: str
-        :param exercise_type_translation: The exercise_type_translation of this Exercise.  # noqa: E501
-        :type exercise_type_translation: str
-        :param last_access_time: The last_access_time of this Exercise.  # noqa: E501
+        :param last_access_time: The last_access_time of this MatchingExercise.  # noqa: E501
         :type last_access_time: float
-        :param solutions: The solutions of this Exercise.  # noqa: E501
+        :param solutions: The solutions of this MatchingExercise.  # noqa: E501
         :type solutions: str
-        :param text_complexity: The text_complexity of this Exercise.  # noqa: E501
+        :param text_complexity: The text_complexity of this MatchingExercise.  # noqa: E501
         :type text_complexity: float
-        :param urn: The urn of this Exercise.  # noqa: E501
+        :param urn: The urn of this MatchingExercise.  # noqa: E501
         :type urn: str
+        :param exercise_type_translation: The exercise_type_translation of this MatchingExercise.  # noqa: E501
+        :type exercise_type_translation: str
+        :param matching_degree: The matching_degree of this MatchingExercise.  # noqa: E501
+        :type matching_degree: float
         """
         self.openapi_types = {
             'correct_feedback': str,
@@ -70,11 +74,12 @@ class Exercise(Model):
             'conll': str,
             'eid': str,
             'exercise_type': str,
-            'exercise_type_translation': str,
             'last_access_time': float,
             'solutions': str,
             'text_complexity': float,
-            'urn': str
+            'urn': str,
+            'exercise_type_translation': str,
+            'matching_degree': float
         }
 
         self.attribute_map = {
@@ -90,11 +95,12 @@ class Exercise(Model):
             'conll': 'conll',
             'eid': 'eid',
             'exercise_type': 'exercise_type',
-            'exercise_type_translation': 'exercise_type_translation',
             'last_access_time': 'last_access_time',
             'solutions': 'solutions',
             'text_complexity': 'text_complexity',
-            'urn': 'urn'
+            'urn': 'urn',
+            'exercise_type_translation': 'exercise_type_translation',
+            'matching_degree': 'matching_degree'
         }
 
         self._correct_feedback = correct_feedback
@@ -109,41 +115,42 @@ class Exercise(Model):
         self._conll = conll
         self._eid = eid
         self._exercise_type = exercise_type
-        self._exercise_type_translation = exercise_type_translation
         self._last_access_time = last_access_time
         self._solutions = solutions
         self._text_complexity = text_complexity
         self._urn = urn
+        self._exercise_type_translation = exercise_type_translation
+        self._matching_degree = matching_degree
 
     @classmethod
-    def from_dict(cls, dikt) -> 'Exercise':
+    def from_dict(cls, dikt) -> 'MatchingExercise':
         """Returns the dict as a model
 
         :param dikt: A dict.
         :type: dict
-        :return: The Exercise of this Exercise.  # noqa: E501
-        :rtype: Exercise
+        :return: The MatchingExercise of this MatchingExercise.  # noqa: E501
+        :rtype: MatchingExercise
         """
         return util.deserialize_model(dikt, cls)
 
     @property
     def correct_feedback(self):
-        """Gets the correct_feedback of this Exercise.
+        """Gets the correct_feedback of this MatchingExercise.
 
         Feedback for successful completion of the exercise.  # noqa: E501
 
-        :return: The correct_feedback of this Exercise.
+        :return: The correct_feedback of this MatchingExercise.
         :rtype: str
         """
         return self._correct_feedback
 
     @correct_feedback.setter
     def correct_feedback(self, correct_feedback):
-        """Sets the correct_feedback of this Exercise.
+        """Sets the correct_feedback of this MatchingExercise.
 
         Feedback for successful completion of the exercise.  # noqa: E501
 
-        :param correct_feedback: The correct_feedback of this Exercise.
+        :param correct_feedback: The correct_feedback of this MatchingExercise.
         :type correct_feedback: str
         """
 
@@ -151,22 +158,22 @@ class Exercise(Model):
 
     @property
     def general_feedback(self):
-        """Gets the general_feedback of this Exercise.
+        """Gets the general_feedback of this MatchingExercise.
 
         Feedback for finishing the exercise.  # noqa: E501
 
-        :return: The general_feedback of this Exercise.
+        :return: The general_feedback of this MatchingExercise.
         :rtype: str
         """
         return self._general_feedback
 
     @general_feedback.setter
     def general_feedback(self, general_feedback):
-        """Sets the general_feedback of this Exercise.
+        """Sets the general_feedback of this MatchingExercise.
 
         Feedback for finishing the exercise.  # noqa: E501
 
-        :param general_feedback: The general_feedback of this Exercise.
+        :param general_feedback: The general_feedback of this MatchingExercise.
         :type general_feedback: str
         """
 
@@ -174,22 +181,22 @@ class Exercise(Model):
 
     @property
     def incorrect_feedback(self):
-        """Gets the incorrect_feedback of this Exercise.
+        """Gets the incorrect_feedback of this MatchingExercise.
 
         Feedback for failing to complete the exercise successfully.  # noqa: E501
 
-        :return: The incorrect_feedback of this Exercise.
+        :return: The incorrect_feedback of this MatchingExercise.
         :rtype: str
         """
         return self._incorrect_feedback
 
     @incorrect_feedback.setter
     def incorrect_feedback(self, incorrect_feedback):
-        """Sets the incorrect_feedback of this Exercise.
+        """Sets the incorrect_feedback of this MatchingExercise.
 
         Feedback for failing to complete the exercise successfully.  # noqa: E501
 
-        :param incorrect_feedback: The incorrect_feedback of this Exercise.
+        :param incorrect_feedback: The incorrect_feedback of this MatchingExercise.
         :type incorrect_feedback: str
         """
 
@@ -197,22 +204,22 @@ class Exercise(Model):
 
     @property
     def instructions(self):
-        """Gets the instructions of this Exercise.
+        """Gets the instructions of this MatchingExercise.
 
         Hints for how to complete the exercise.  # noqa: E501
 
-        :return: The instructions of this Exercise.
+        :return: The instructions of this MatchingExercise.
         :rtype: str
         """
         return self._instructions
 
     @instructions.setter
     def instructions(self, instructions):
-        """Sets the instructions of this Exercise.
+        """Sets the instructions of this MatchingExercise.
 
         Hints for how to complete the exercise.  # noqa: E501
 
-        :param instructions: The instructions of this Exercise.
+        :param instructions: The instructions of this MatchingExercise.
         :type instructions: str
         """
         if instructions is None:
@@ -222,22 +229,22 @@ class Exercise(Model):
 
     @property
     def language(self):
-        """Gets the language of this Exercise.
+        """Gets the language of this MatchingExercise.
 
         ISO 639-1 Language Code for the localization of exercise content.  # noqa: E501
 
-        :return: The language of this Exercise.
+        :return: The language of this MatchingExercise.
         :rtype: str
         """
         return self._language
 
     @language.setter
     def language(self, language):
-        """Sets the language of this Exercise.
+        """Sets the language of this MatchingExercise.
 
         ISO 639-1 Language Code for the localization of exercise content.  # noqa: E501
 
-        :param language: The language of this Exercise.
+        :param language: The language of this MatchingExercise.
         :type language: str
         """
 
@@ -245,22 +252,22 @@ class Exercise(Model):
 
     @property
     def partially_correct_feedback(self):
-        """Gets the partially_correct_feedback of this Exercise.
+        """Gets the partially_correct_feedback of this MatchingExercise.
 
         Feedback for successfully completing certain parts of the exercise.  # noqa: E501
 
-        :return: The partially_correct_feedback of this Exercise.
+        :return: The partially_correct_feedback of this MatchingExercise.
         :rtype: str
         """
         return self._partially_correct_feedback
 
     @partially_correct_feedback.setter
     def partially_correct_feedback(self, partially_correct_feedback):
-        """Sets the partially_correct_feedback of this Exercise.
+        """Sets the partially_correct_feedback of this MatchingExercise.
 
         Feedback for successfully completing certain parts of the exercise.  # noqa: E501
 
-        :param partially_correct_feedback: The partially_correct_feedback of this Exercise.
+        :param partially_correct_feedback: The partially_correct_feedback of this MatchingExercise.
         :type partially_correct_feedback: str
         """
 
@@ -268,22 +275,22 @@ class Exercise(Model):
 
     @property
     def search_values(self):
-        """Gets the search_values of this Exercise.
+        """Gets the search_values of this MatchingExercise.
 
         Search queries that were used to build the exercise.  # noqa: E501
 
-        :return: The search_values of this Exercise.
+        :return: The search_values of this MatchingExercise.
         :rtype: str
         """
         return self._search_values
 
     @search_values.setter
     def search_values(self, search_values):
-        """Sets the search_values of this Exercise.
+        """Sets the search_values of this MatchingExercise.
 
         Search queries that were used to build the exercise.  # noqa: E501
 
-        :param search_values: The search_values of this Exercise.
+        :param search_values: The search_values of this MatchingExercise.
         :type search_values: str
         """
         if search_values is None:
@@ -293,22 +300,22 @@ class Exercise(Model):
 
     @property
     def work_author(self):
-        """Gets the work_author of this Exercise.
+        """Gets the work_author of this MatchingExercise.
 
         Name of the person who wrote the base text for the exercise.  # noqa: E501
 
-        :return: The work_author of this Exercise.
+        :return: The work_author of this MatchingExercise.
         :rtype: str
         """
         return self._work_author
 
     @work_author.setter
     def work_author(self, work_author):
-        """Sets the work_author of this Exercise.
+        """Sets the work_author of this MatchingExercise.
 
         Name of the person who wrote the base text for the exercise.  # noqa: E501
 
-        :param work_author: The work_author of this Exercise.
+        :param work_author: The work_author of this MatchingExercise.
         :type work_author: str
         """
 
@@ -316,22 +323,22 @@ class Exercise(Model):
 
     @property
     def work_title(self):
-        """Gets the work_title of this Exercise.
+        """Gets the work_title of this MatchingExercise.
 
         Title of the base text for the exercise.  # noqa: E501
 
-        :return: The work_title of this Exercise.
+        :return: The work_title of this MatchingExercise.
         :rtype: str
         """
         return self._work_title
 
     @work_title.setter
     def work_title(self, work_title):
-        """Sets the work_title of this Exercise.
+        """Sets the work_title of this MatchingExercise.
 
         Title of the base text for the exercise.  # noqa: E501
 
-        :param work_title: The work_title of this Exercise.
+        :param work_title: The work_title of this MatchingExercise.
         :type work_title: str
         """
 
@@ -339,22 +346,22 @@ class Exercise(Model):
 
     @property
     def conll(self):
-        """Gets the conll of this Exercise.
+        """Gets the conll of this MatchingExercise.
 
         CONLL-formatted linguistic annotations represented as a single string.  # noqa: E501
 
-        :return: The conll of this Exercise.
+        :return: The conll of this MatchingExercise.
         :rtype: str
         """
         return self._conll
 
     @conll.setter
     def conll(self, conll):
-        """Sets the conll of this Exercise.
+        """Sets the conll of this MatchingExercise.
 
         CONLL-formatted linguistic annotations represented as a single string.  # noqa: E501
 
-        :param conll: The conll of this Exercise.
+        :param conll: The conll of this MatchingExercise.
         :type conll: str
         """
 
@@ -362,22 +369,22 @@ class Exercise(Model):
 
     @property
     def eid(self):
-        """Gets the eid of this Exercise.
+        """Gets the eid of this MatchingExercise.
 
         Unique identifier (UUID) for the exercise.  # noqa: E501
 
-        :return: The eid of this Exercise.
+        :return: The eid of this MatchingExercise.
         :rtype: str
         """
         return self._eid
 
     @eid.setter
     def eid(self, eid):
-        """Sets the eid of this Exercise.
+        """Sets the eid of this MatchingExercise.
 
         Unique identifier (UUID) for the exercise.  # noqa: E501
 
-        :param eid: The eid of this Exercise.
+        :param eid: The eid of this MatchingExercise.
         :type eid: str
         """
         if eid is None:
@@ -387,68 +394,45 @@ class Exercise(Model):
 
     @property
     def exercise_type(self):
-        """Gets the exercise_type of this Exercise.
+        """Gets the exercise_type of this MatchingExercise.
 
         Type of exercise, concerning interaction and layout.  # noqa: E501
 
-        :return: The exercise_type of this Exercise.
+        :return: The exercise_type of this MatchingExercise.
         :rtype: str
         """
         return self._exercise_type
 
     @exercise_type.setter
     def exercise_type(self, exercise_type):
-        """Sets the exercise_type of this Exercise.
+        """Sets the exercise_type of this MatchingExercise.
 
         Type of exercise, concerning interaction and layout.  # noqa: E501
 
-        :param exercise_type: The exercise_type of this Exercise.
+        :param exercise_type: The exercise_type of this MatchingExercise.
         :type exercise_type: str
         """
 
         self._exercise_type = exercise_type
 
-    @property
-    def exercise_type_translation(self):
-        """Gets the exercise_type_translation of this Exercise.
-
-        Localized expression of the exercise type.  # noqa: E501
-
-        :return: The exercise_type_translation of this Exercise.
-        :rtype: str
-        """
-        return self._exercise_type_translation
-
-    @exercise_type_translation.setter
-    def exercise_type_translation(self, exercise_type_translation):
-        """Sets the exercise_type_translation of this Exercise.
-
-        Localized expression of the exercise type.  # noqa: E501
-
-        :param exercise_type_translation: The exercise_type_translation of this Exercise.
-        :type exercise_type_translation: str
-        """
-
-        self._exercise_type_translation = exercise_type_translation
-
     @property
     def last_access_time(self):
-        """Gets the last_access_time of this Exercise.
+        """Gets the last_access_time of this MatchingExercise.
 
         When the exercise was last accessed (as POSIX timestamp).  # noqa: E501
 
-        :return: The last_access_time of this Exercise.
+        :return: The last_access_time of this MatchingExercise.
         :rtype: float
         """
         return self._last_access_time
 
     @last_access_time.setter
     def last_access_time(self, last_access_time):
-        """Sets the last_access_time of this Exercise.
+        """Sets the last_access_time of this MatchingExercise.
 
         When the exercise was last accessed (as POSIX timestamp).  # noqa: E501
 
-        :param last_access_time: The last_access_time of this Exercise.
+        :param last_access_time: The last_access_time of this MatchingExercise.
         :type last_access_time: float
         """
         if last_access_time is None:
@@ -458,22 +442,22 @@ class Exercise(Model):
 
     @property
     def solutions(self):
-        """Gets the solutions of this Exercise.
+        """Gets the solutions of this MatchingExercise.
 
         Correct solutions for the exercise.  # noqa: E501
 
-        :return: The solutions of this Exercise.
+        :return: The solutions of this MatchingExercise.
         :rtype: str
         """
         return self._solutions
 
     @solutions.setter
     def solutions(self, solutions):
-        """Sets the solutions of this Exercise.
+        """Sets the solutions of this MatchingExercise.
 
         Correct solutions for the exercise.  # noqa: E501
 
-        :param solutions: The solutions of this Exercise.
+        :param solutions: The solutions of this MatchingExercise.
         :type solutions: str
         """
 
@@ -481,22 +465,22 @@ class Exercise(Model):
 
     @property
     def text_complexity(self):
-        """Gets the text_complexity of this Exercise.
+        """Gets the text_complexity of this MatchingExercise.
 
         Overall text complexity as measured by the software's internal language analysis.  # noqa: E501
 
-        :return: The text_complexity of this Exercise.
+        :return: The text_complexity of this MatchingExercise.
         :rtype: float
         """
         return self._text_complexity
 
     @text_complexity.setter
     def text_complexity(self, text_complexity):
-        """Sets the text_complexity of this Exercise.
+        """Sets the text_complexity of this MatchingExercise.
 
         Overall text complexity as measured by the software's internal language analysis.  # noqa: E501
 
-        :param text_complexity: The text_complexity of this Exercise.
+        :param text_complexity: The text_complexity of this MatchingExercise.
         :type text_complexity: float
         """
 
@@ -504,23 +488,69 @@ class Exercise(Model):
 
     @property
     def urn(self):
-        """Gets the urn of this Exercise.
+        """Gets the urn of this MatchingExercise.
 
         CTS URN for the text passage from which the exercise was created.  # noqa: E501
 
-        :return: The urn of this Exercise.
+        :return: The urn of this MatchingExercise.
         :rtype: str
         """
         return self._urn
 
     @urn.setter
     def urn(self, urn):
-        """Sets the urn of this Exercise.
+        """Sets the urn of this MatchingExercise.
 
         CTS URN for the text passage from which the exercise was created.  # noqa: E501
 
-        :param urn: The urn of this Exercise.
+        :param urn: The urn of this MatchingExercise.
         :type urn: str
         """
 
         self._urn = urn
+
+    @property
+    def exercise_type_translation(self):
+        """Gets the exercise_type_translation of this MatchingExercise.
+
+        Localized expression of the exercise type.  # noqa: E501
+
+        :return: The exercise_type_translation of this MatchingExercise.
+        :rtype: str
+        """
+        return self._exercise_type_translation
+
+    @exercise_type_translation.setter
+    def exercise_type_translation(self, exercise_type_translation):
+        """Sets the exercise_type_translation of this MatchingExercise.
+
+        Localized expression of the exercise type.  # noqa: E501
+
+        :param exercise_type_translation: The exercise_type_translation of this MatchingExercise.
+        :type exercise_type_translation: str
+        """
+
+        self._exercise_type_translation = exercise_type_translation
+
+    @property
+    def matching_degree(self):
+        """Gets the matching_degree of this MatchingExercise.
+
+        Percentage of words in the exercise that match a reference vocabulary.  # noqa: E501
+
+        :return: The matching_degree of this MatchingExercise.
+        :rtype: float
+        """
+        return self._matching_degree
+
+    @matching_degree.setter
+    def matching_degree(self, matching_degree):
+        """Sets the matching_degree of this MatchingExercise.
+
+        Percentage of words in the exercise that match a reference vocabulary.  # noqa: E501
+
+        :param matching_degree: The matching_degree of this MatchingExercise.
+        :type matching_degree: float
+        """
+
+        self._matching_degree = matching_degree
diff --git a/mc_backend/openapi/openapi_server/models/matching_exercise_all_of.py b/mc_backend/openapi/openapi_server/models/matching_exercise_all_of.py
new file mode 100644
index 0000000..0af8521
--- /dev/null
+++ b/mc_backend/openapi/openapi_server/models/matching_exercise_all_of.py
@@ -0,0 +1,94 @@
+# coding: utf-8
+
+from __future__ import absolute_import
+from datetime import date, datetime  # noqa: F401
+
+from typing import List, Dict  # noqa: F401
+
+from openapi.openapi_server.models.base_model_ import Model
+from openapi.openapi_server import util
+
+
+class MatchingExerciseAllOf(Model):
+    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
+
+    Do not edit the class manually.
+    """
+
+    def __init__(self, exercise_type_translation=None, matching_degree=None):  # noqa: E501
+        """MatchingExerciseAllOf - a model defined in OpenAPI
+
+        :param exercise_type_translation: The exercise_type_translation of this MatchingExerciseAllOf.  # noqa: E501
+        :type exercise_type_translation: str
+        :param matching_degree: The matching_degree of this MatchingExerciseAllOf.  # noqa: E501
+        :type matching_degree: float
+        """
+        self.openapi_types = {
+            'exercise_type_translation': str,
+            'matching_degree': float
+        }
+
+        self.attribute_map = {
+            'exercise_type_translation': 'exercise_type_translation',
+            'matching_degree': 'matching_degree'
+        }
+
+        self._exercise_type_translation = exercise_type_translation
+        self._matching_degree = matching_degree
+
+    @classmethod
+    def from_dict(cls, dikt) -> 'MatchingExerciseAllOf':
+        """Returns the dict as a model
+
+        :param dikt: A dict.
+        :type: dict
+        :return: The MatchingExercise_allOf of this MatchingExerciseAllOf.  # noqa: E501
+        :rtype: MatchingExerciseAllOf
+        """
+        return util.deserialize_model(dikt, cls)
+
+    @property
+    def exercise_type_translation(self):
+        """Gets the exercise_type_translation of this MatchingExerciseAllOf.
+
+        Localized expression of the exercise type.  # noqa: E501
+
+        :return: The exercise_type_translation of this MatchingExerciseAllOf.
+        :rtype: str
+        """
+        return self._exercise_type_translation
+
+    @exercise_type_translation.setter
+    def exercise_type_translation(self, exercise_type_translation):
+        """Sets the exercise_type_translation of this MatchingExerciseAllOf.
+
+        Localized expression of the exercise type.  # noqa: E501
+
+        :param exercise_type_translation: The exercise_type_translation of this MatchingExerciseAllOf.
+        :type exercise_type_translation: str
+        """
+
+        self._exercise_type_translation = exercise_type_translation
+
+    @property
+    def matching_degree(self):
+        """Gets the matching_degree of this MatchingExerciseAllOf.
+
+        Percentage of words in the exercise that match a reference vocabulary.  # noqa: E501
+
+        :return: The matching_degree of this MatchingExerciseAllOf.
+        :rtype: float
+        """
+        return self._matching_degree
+
+    @matching_degree.setter
+    def matching_degree(self, matching_degree):
+        """Sets the matching_degree of this MatchingExerciseAllOf.
+
+        Percentage of words in the exercise that match a reference vocabulary.  # noqa: E501
+
+        :param matching_degree: The matching_degree of this MatchingExerciseAllOf.
+        :type matching_degree: float
+        """
+
+        self._matching_degree = matching_degree
diff --git a/mc_backend/openapi/openapi_server/models/node.py b/mc_backend/openapi/openapi_server/models/node.py
deleted file mode 100644
index 16de072..0000000
--- a/mc_backend/openapi/openapi_server/models/node.py
+++ /dev/null
@@ -1,360 +0,0 @@
-# coding: utf-8
-
-from __future__ import absolute_import
-from datetime import date, datetime  # noqa: F401
-
-from typing import List, Dict  # noqa: F401
-
-from openapi.openapi_server.models.base_model_ import Model
-from openapi.openapi_server import util
-
-
-class Node(Model):
-    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
-
-    Do not edit the class manually.
-    """
-
-    def __init__(self, annis_node_name=None, annis_node_type=None, annis_tok=None, annis_type=None, id=None, is_oov=None, udep_lemma=None, udep_upostag=None, udep_xpostag=None, udep_feats=None, solution=None):  # noqa: E501
-        """Node - a model defined in OpenAPI
-
-        :param annis_node_name: The annis_node_name of this Node.  # noqa: E501
-        :type annis_node_name: str
-        :param annis_node_type: The annis_node_type of this Node.  # noqa: E501
-        :type annis_node_type: str
-        :param annis_tok: The annis_tok of this Node.  # noqa: E501
-        :type annis_tok: str
-        :param annis_type: The annis_type of this Node.  # noqa: E501
-        :type annis_type: str
-        :param id: The id of this Node.  # noqa: E501
-        :type id: str
-        :param is_oov: The is_oov of this Node.  # noqa: E501
-        :type is_oov: bool
-        :param udep_lemma: The udep_lemma of this Node.  # noqa: E501
-        :type udep_lemma: str
-        :param udep_upostag: The udep_upostag of this Node.  # noqa: E501
-        :type udep_upostag: str
-        :param udep_xpostag: The udep_xpostag of this Node.  # noqa: E501
-        :type udep_xpostag: str
-        :param udep_feats: The udep_feats of this Node.  # noqa: E501
-        :type udep_feats: str
-        :param solution: The solution of this Node.  # noqa: E501
-        :type solution: str
-        """
-        self.openapi_types = {
-            'annis_node_name': str,
-            'annis_node_type': str,
-            'annis_tok': str,
-            'annis_type': str,
-            'id': str,
-            'is_oov': bool,
-            'udep_lemma': str,
-            'udep_upostag': str,
-            'udep_xpostag': str,
-            'udep_feats': str,
-            'solution': str
-        }
-
-        self.attribute_map = {
-            'annis_node_name': 'annis_node_name',
-            'annis_node_type': 'annis_node_type',
-            'annis_tok': 'annis_tok',
-            'annis_type': 'annis_type',
-            'id': 'id',
-            'is_oov': 'is_oov',
-            'udep_lemma': 'udep_lemma',
-            'udep_upostag': 'udep_upostag',
-            'udep_xpostag': 'udep_xpostag',
-            'udep_feats': 'udep_feats',
-            'solution': 'solution'
-        }
-
-        self._annis_node_name = annis_node_name
-        self._annis_node_type = annis_node_type
-        self._annis_tok = annis_tok
-        self._annis_type = annis_type
-        self._id = id
-        self._is_oov = is_oov
-        self._udep_lemma = udep_lemma
-        self._udep_upostag = udep_upostag
-        self._udep_xpostag = udep_xpostag
-        self._udep_feats = udep_feats
-        self._solution = solution
-
-    @classmethod
-    def from_dict(cls, dikt) -> 'Node':
-        """Returns the dict as a model
-
-        :param dikt: A dict.
-        :type: dict
-        :return: The Node of this Node.  # noqa: E501
-        :rtype: Node
-        """
-        return util.deserialize_model(dikt, cls)
-
-    @property
-    def annis_node_name(self):
-        """Gets the annis_node_name of this Node.
-
-        Node name as given by ANNIS.  # noqa: E501
-
-        :return: The annis_node_name of this Node.
-        :rtype: str
-        """
-        return self._annis_node_name
-
-    @annis_node_name.setter
-    def annis_node_name(self, annis_node_name):
-        """Sets the annis_node_name of this Node.
-
-        Node name as given by ANNIS.  # noqa: E501
-
-        :param annis_node_name: The annis_node_name of this Node.
-        :type annis_node_name: str
-        """
-        if annis_node_name is None:
-            raise ValueError("Invalid value for `annis_node_name`, must not be `None`")  # noqa: E501
-
-        self._annis_node_name = annis_node_name
-
-    @property
-    def annis_node_type(self):
-        """Gets the annis_node_type of this Node.
-
-        Node type as given by ANNIS.  # noqa: E501
-
-        :return: The annis_node_type of this Node.
-        :rtype: str
-        """
-        return self._annis_node_type
-
-    @annis_node_type.setter
-    def annis_node_type(self, annis_node_type):
-        """Sets the annis_node_type of this Node.
-
-        Node type as given by ANNIS.  # noqa: E501
-
-        :param annis_node_type: The annis_node_type of this Node.
-        :type annis_node_type: str
-        """
-        if annis_node_type is None:
-            raise ValueError("Invalid value for `annis_node_type`, must not be `None`")  # noqa: E501
-
-        self._annis_node_type = annis_node_type
-
-    @property
-    def annis_tok(self):
-        """Gets the annis_tok of this Node.
-
-        Raw word form as given by ANNIS.  # noqa: E501
-
-        :return: The annis_tok of this Node.
-        :rtype: str
-        """
-        return self._annis_tok
-
-    @annis_tok.setter
-    def annis_tok(self, annis_tok):
-        """Sets the annis_tok of this Node.
-
-        Raw word form as given by ANNIS.  # noqa: E501
-
-        :param annis_tok: The annis_tok of this Node.
-        :type annis_tok: str
-        """
-        if annis_tok is None:
-            raise ValueError("Invalid value for `annis_tok`, must not be `None`")  # noqa: E501
-
-        self._annis_tok = annis_tok
-
-    @property
-    def annis_type(self):
-        """Gets the annis_type of this Node.
-
-        Node type as given by ANNIS (?).  # noqa: E501
-
-        :return: The annis_type of this Node.
-        :rtype: str
-        """
-        return self._annis_type
-
-    @annis_type.setter
-    def annis_type(self, annis_type):
-        """Sets the annis_type of this Node.
-
-        Node type as given by ANNIS (?).  # noqa: E501
-
-        :param annis_type: The annis_type of this Node.
-        :type annis_type: str
-        """
-        if annis_type is None:
-            raise ValueError("Invalid value for `annis_type`, must not be `None`")  # noqa: E501
-
-        self._annis_type = annis_type
-
-    @property
-    def id(self):
-        """Gets the id of this Node.
-
-        Unique identifier for the node in the SALT model.  # noqa: E501
-
-        :return: The id of this Node.
-        :rtype: str
-        """
-        return self._id
-
-    @id.setter
-    def id(self, id):
-        """Sets the id of this Node.
-
-        Unique identifier for the node in the SALT model.  # noqa: E501
-
-        :param id: The id of this Node.
-        :type id: str
-        """
-        if id is None:
-            raise ValueError("Invalid value for `id`, must not be `None`")  # noqa: E501
-
-        self._id = id
-
-    @property
-    def is_oov(self):
-        """Gets the is_oov of this Node.
-
-        Whether the raw word form is missing in a given vocabulary.  # noqa: E501
-
-        :return: The is_oov of this Node.
-        :rtype: bool
-        """
-        return self._is_oov
-
-    @is_oov.setter
-    def is_oov(self, is_oov):
-        """Sets the is_oov of this Node.
-
-        Whether the raw word form is missing in a given vocabulary.  # noqa: E501
-
-        :param is_oov: The is_oov of this Node.
-        :type is_oov: bool
-        """
-
-        self._is_oov = is_oov
-
-    @property
-    def udep_lemma(self):
-        """Gets the udep_lemma of this Node.
-
-        Lemmatized word form.  # noqa: E501
-
-        :return: The udep_lemma of this Node.
-        :rtype: str
-        """
-        return self._udep_lemma
-
-    @udep_lemma.setter
-    def udep_lemma(self, udep_lemma):
-        """Sets the udep_lemma of this Node.
-
-        Lemmatized word form.  # noqa: E501
-
-        :param udep_lemma: The udep_lemma of this Node.
-        :type udep_lemma: str
-        """
-        if udep_lemma is None:
-            raise ValueError("Invalid value for `udep_lemma`, must not be `None`")  # noqa: E501
-
-        self._udep_lemma = udep_lemma
-
-    @property
-    def udep_upostag(self):
-        """Gets the udep_upostag of this Node.
-
-        Universal part of speech tag for the word form.  # noqa: E501
-
-        :return: The udep_upostag of this Node.
-        :rtype: str
-        """
-        return self._udep_upostag
-
-    @udep_upostag.setter
-    def udep_upostag(self, udep_upostag):
-        """Sets the udep_upostag of this Node.
-
-        Universal part of speech tag for the word form.  # noqa: E501
-
-        :param udep_upostag: The udep_upostag of this Node.
-        :type udep_upostag: str
-        """
-        if udep_upostag is None:
-            raise ValueError("Invalid value for `udep_upostag`, must not be `None`")  # noqa: E501
-
-        self._udep_upostag = udep_upostag
-
-    @property
-    def udep_xpostag(self):
-        """Gets the udep_xpostag of this Node.
-
-        Language-specific part of speech tag for the word form.  # noqa: E501
-
-        :return: The udep_xpostag of this Node.
-        :rtype: str
-        """
-        return self._udep_xpostag
-
-    @udep_xpostag.setter
-    def udep_xpostag(self, udep_xpostag):
-        """Sets the udep_xpostag of this Node.
-
-        Language-specific part of speech tag for the word form.  # noqa: E501
-
-        :param udep_xpostag: The udep_xpostag of this Node.
-        :type udep_xpostag: str
-        """
-
-        self._udep_xpostag = udep_xpostag
-
-    @property
-    def udep_feats(self):
-        """Gets the udep_feats of this Node.
-
-        Additional morphological information.  # noqa: E501
-
-        :return: The udep_feats of this Node.
-        :rtype: str
-        """
-        return self._udep_feats
-
-    @udep_feats.setter
-    def udep_feats(self, udep_feats):
-        """Sets the udep_feats of this Node.
-
-        Additional morphological information.  # noqa: E501
-
-        :param udep_feats: The udep_feats of this Node.
-        :type udep_feats: str
-        """
-
-        self._udep_feats = udep_feats
-
-    @property
-    def solution(self):
-        """Gets the solution of this Node.
-
-        Solution value for this node in an exercise.  # noqa: E501
-
-        :return: The solution of this Node.
-        :rtype: str
-        """
-        return self._solution
-
-    @solution.setter
-    def solution(self, solution):
-        """Sets the solution of this Node.
-
-        Solution value for this node in an exercise.  # noqa: E501
-
-        :param solution: The solution of this Node.
-        :type solution: str
-        """
-
-        self._solution = solution
diff --git a/mc_backend/openapi/openapi_server/models/text_complexity_form.py b/mc_backend/openapi/openapi_server/models/text_complexity_form.py
index 225102e..08aede9 100644
--- a/mc_backend/openapi/openapi_server/models/text_complexity_form.py
+++ b/mc_backend/openapi/openapi_server/models/text_complexity_form.py
@@ -6,12 +6,8 @@ from datetime import date, datetime  # noqa: F401
 from typing import List, Dict  # noqa: F401
 
 from openapi.openapi_server.models.base_model_ import Model
-from openapi.openapi_server.models.annis_response import AnnisResponse
-from openapi.openapi_server.models.text_complexity_form_base import TextComplexityFormBase
 from openapi.openapi_server import util
 
-from openapi.openapi_server.models.annis_response import AnnisResponse  # noqa: E501
-from openapi.openapi_server.models.text_complexity_form_base import TextComplexityFormBase  # noqa: E501
 
 class TextComplexityForm(Model):
     """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
@@ -27,12 +23,12 @@ class TextComplexityForm(Model):
         :param urn: The urn of this TextComplexityForm.  # noqa: E501
         :type urn: str
         :param annis_response: The annis_response of this TextComplexityForm.  # noqa: E501
-        :type annis_response: AnnisResponse
+        :type annis_response: str
         """
         self.openapi_types = {
             'measure': str,
             'urn': str,
-            'annis_response': AnnisResponse
+            'annis_response': str
         }
 
         self.attribute_map = {
@@ -110,9 +106,10 @@ class TextComplexityForm(Model):
     def annis_response(self):
         """Gets the annis_response of this TextComplexityForm.
 
+        Serialized ANNIS response.  # noqa: E501
 
         :return: The annis_response of this TextComplexityForm.
-        :rtype: AnnisResponse
+        :rtype: str
         """
         return self._annis_response
 
@@ -120,9 +117,10 @@ class TextComplexityForm(Model):
     def annis_response(self, annis_response):
         """Sets the annis_response of this TextComplexityForm.
 
+        Serialized ANNIS response.  # noqa: E501
 
         :param annis_response: The annis_response of this TextComplexityForm.
-        :type annis_response: AnnisResponse
+        :type annis_response: str
         """
 
         self._annis_response = annis_response
diff --git a/mc_backend/openapi/openapi_server/models/text_complexity_form_base.py b/mc_backend/openapi/openapi_server/models/text_complexity_form_base.py
deleted file mode 100644
index 14eaa85..0000000
--- a/mc_backend/openapi/openapi_server/models/text_complexity_form_base.py
+++ /dev/null
@@ -1,126 +0,0 @@
-# coding: utf-8
-
-from __future__ import absolute_import
-from datetime import date, datetime  # noqa: F401
-
-from typing import List, Dict  # noqa: F401
-
-from openapi.openapi_server.models.base_model_ import Model
-from openapi.openapi_server.models.annis_response import AnnisResponse
-from openapi.openapi_server import util
-
-from openapi.openapi_server.models.annis_response import AnnisResponse  # noqa: E501
-
-class TextComplexityFormBase(Model):
-    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
-
-    Do not edit the class manually.
-    """
-
-    def __init__(self, measure=None, urn=None, annis_response=None):  # noqa: E501
-        """TextComplexityFormBase - a model defined in OpenAPI
-
-        :param measure: The measure of this TextComplexityFormBase.  # noqa: E501
-        :type measure: str
-        :param urn: The urn of this TextComplexityFormBase.  # noqa: E501
-        :type urn: str
-        :param annis_response: The annis_response of this TextComplexityFormBase.  # noqa: E501
-        :type annis_response: AnnisResponse
-        """
-        self.openapi_types = {
-            'measure': str,
-            'urn': str,
-            'annis_response': AnnisResponse
-        }
-
-        self.attribute_map = {
-            'measure': 'measure',
-            'urn': 'urn',
-            'annis_response': 'annis_response'
-        }
-
-        self._measure = measure
-        self._urn = urn
-        self._annis_response = annis_response
-
-    @classmethod
-    def from_dict(cls, dikt) -> 'TextComplexityFormBase':
-        """Returns the dict as a model
-
-        :param dikt: A dict.
-        :type: dict
-        :return: The TextComplexityFormBase of this TextComplexityFormBase.  # noqa: E501
-        :rtype: TextComplexityFormBase
-        """
-        return util.deserialize_model(dikt, cls)
-
-    @property
-    def measure(self):
-        """Gets the measure of this TextComplexityFormBase.
-
-        Label of the desired measure for text complexity.  # noqa: E501
-
-        :return: The measure of this TextComplexityFormBase.
-        :rtype: str
-        """
-        return self._measure
-
-    @measure.setter
-    def measure(self, measure):
-        """Sets the measure of this TextComplexityFormBase.
-
-        Label of the desired measure for text complexity.  # noqa: E501
-
-        :param measure: The measure of this TextComplexityFormBase.
-        :type measure: str
-        """
-        if measure is None:
-            raise ValueError("Invalid value for `measure`, must not be `None`")  # noqa: E501
-
-        self._measure = measure
-
-    @property
-    def urn(self):
-        """Gets the urn of this TextComplexityFormBase.
-
-        CTS URN for the text passage from which the text complexity should be calculated.  # noqa: E501
-
-        :return: The urn of this TextComplexityFormBase.
-        :rtype: str
-        """
-        return self._urn
-
-    @urn.setter
-    def urn(self, urn):
-        """Sets the urn of this TextComplexityFormBase.
-
-        CTS URN for the text passage from which the text complexity should be calculated.  # noqa: E501
-
-        :param urn: The urn of this TextComplexityFormBase.
-        :type urn: str
-        """
-        if urn is None:
-            raise ValueError("Invalid value for `urn`, must not be `None`")  # noqa: E501
-
-        self._urn = urn
-
-    @property
-    def annis_response(self):
-        """Gets the annis_response of this TextComplexityFormBase.
-
-
-        :return: The annis_response of this TextComplexityFormBase.
-        :rtype: AnnisResponse
-        """
-        return self._annis_response
-
-    @annis_response.setter
-    def annis_response(self, annis_response):
-        """Sets the annis_response of this TextComplexityFormBase.
-
-
-        :param annis_response: The annis_response of this TextComplexityFormBase.
-        :type annis_response: AnnisResponse
-        """
-
-        self._annis_response = annis_response
diff --git a/mc_backend/openapi/openapi_server/models/text_complexity_form2.py b/mc_backend/openapi/openapi_server/models/text_complexity_form_extension.py
similarity index 59%
rename from mc_backend/openapi/openapi_server/models/text_complexity_form2.py
rename to mc_backend/openapi/openapi_server/models/text_complexity_form_extension.py
index 5958c5b..a0d9781 100644
--- a/mc_backend/openapi/openapi_server/models/text_complexity_form2.py
+++ b/mc_backend/openapi/openapi_server/models/text_complexity_form_extension.py
@@ -6,31 +6,31 @@ from datetime import date, datetime  # noqa: F401
 from typing import List, Dict  # noqa: F401
 
 from openapi.openapi_server.models.base_model_ import Model
-from openapi.openapi_server.models.annis_response import AnnisResponse
+from openapi.openapi_server.models.text_complexity_form import TextComplexityForm
 from openapi.openapi_server import util
 
-from openapi.openapi_server.models.annis_response import AnnisResponse  # noqa: E501
+from openapi.openapi_server.models.text_complexity_form import TextComplexityForm  # noqa: E501
 
-class TextComplexityForm2(Model):
+class TextComplexityFormExtension(Model):
     """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 
     Do not edit the class manually.
     """
 
     def __init__(self, measure=None, urn=None, annis_response=None):  # noqa: E501
-        """TextComplexityForm2 - a model defined in OpenAPI
+        """TextComplexityFormExtension - a model defined in OpenAPI
 
-        :param measure: The measure of this TextComplexityForm2.  # noqa: E501
+        :param measure: The measure of this TextComplexityFormExtension.  # noqa: E501
         :type measure: str
-        :param urn: The urn of this TextComplexityForm2.  # noqa: E501
+        :param urn: The urn of this TextComplexityFormExtension.  # noqa: E501
         :type urn: str
-        :param annis_response: The annis_response of this TextComplexityForm2.  # noqa: E501
-        :type annis_response: AnnisResponse
+        :param annis_response: The annis_response of this TextComplexityFormExtension.  # noqa: E501
+        :type annis_response: str
         """
         self.openapi_types = {
             'measure': str,
             'urn': str,
-            'annis_response': AnnisResponse
+            'annis_response': str
         }
 
         self.attribute_map = {
@@ -44,34 +44,34 @@ class TextComplexityForm2(Model):
         self._annis_response = annis_response
 
     @classmethod
-    def from_dict(cls, dikt) -> 'TextComplexityForm2':
+    def from_dict(cls, dikt) -> 'TextComplexityFormExtension':
         """Returns the dict as a model
 
         :param dikt: A dict.
         :type: dict
-        :return: The TextComplexityForm_2 of this TextComplexityForm2.  # noqa: E501
-        :rtype: TextComplexityForm2
+        :return: The TextComplexityFormExtension of this TextComplexityFormExtension.  # noqa: E501
+        :rtype: TextComplexityFormExtension
         """
         return util.deserialize_model(dikt, cls)
 
     @property
     def measure(self):
-        """Gets the measure of this TextComplexityForm2.
+        """Gets the measure of this TextComplexityFormExtension.
 
         Label of the desired measure for text complexity.  # noqa: E501
 
-        :return: The measure of this TextComplexityForm2.
+        :return: The measure of this TextComplexityFormExtension.
         :rtype: str
         """
         return self._measure
 
     @measure.setter
     def measure(self, measure):
-        """Sets the measure of this TextComplexityForm2.
+        """Sets the measure of this TextComplexityFormExtension.
 
         Label of the desired measure for text complexity.  # noqa: E501
 
-        :param measure: The measure of this TextComplexityForm2.
+        :param measure: The measure of this TextComplexityFormExtension.
         :type measure: str
         """
         if measure is None:
@@ -81,22 +81,22 @@ class TextComplexityForm2(Model):
 
     @property
     def urn(self):
-        """Gets the urn of this TextComplexityForm2.
+        """Gets the urn of this TextComplexityFormExtension.
 
         CTS URN for the text passage from which the text complexity should be calculated.  # noqa: E501
 
-        :return: The urn of this TextComplexityForm2.
+        :return: The urn of this TextComplexityFormExtension.
         :rtype: str
         """
         return self._urn
 
     @urn.setter
     def urn(self, urn):
-        """Sets the urn of this TextComplexityForm2.
+        """Sets the urn of this TextComplexityFormExtension.
 
         CTS URN for the text passage from which the text complexity should be calculated.  # noqa: E501
 
-        :param urn: The urn of this TextComplexityForm2.
+        :param urn: The urn of this TextComplexityFormExtension.
         :type urn: str
         """
         if urn is None:
@@ -106,21 +106,23 @@ class TextComplexityForm2(Model):
 
     @property
     def annis_response(self):
-        """Gets the annis_response of this TextComplexityForm2.
+        """Gets the annis_response of this TextComplexityFormExtension.
 
+        Serialized ANNIS response.  # noqa: E501
 
-        :return: The annis_response of this TextComplexityForm2.
-        :rtype: AnnisResponse
+        :return: The annis_response of this TextComplexityFormExtension.
+        :rtype: str
         """
         return self._annis_response
 
     @annis_response.setter
     def annis_response(self, annis_response):
-        """Sets the annis_response of this TextComplexityForm2.
+        """Sets the annis_response of this TextComplexityFormExtension.
 
+        Serialized ANNIS response.  # noqa: E501
 
-        :param annis_response: The annis_response of this TextComplexityForm2.
-        :type annis_response: AnnisResponse
+        :param annis_response: The annis_response of this TextComplexityFormExtension.
+        :type annis_response: str
         """
 
         self._annis_response = annis_response
diff --git a/mc_backend/openapi/openapi_server/models/update_info.py b/mc_backend/openapi/openapi_server/models/update_info.py
deleted file mode 100644
index bb9c6a0..0000000
--- a/mc_backend/openapi/openapi_server/models/update_info.py
+++ /dev/null
@@ -1,132 +0,0 @@
-# coding: utf-8
-
-from __future__ import absolute_import
-from datetime import date, datetime  # noqa: F401
-
-from typing import List, Dict  # noqa: F401
-
-from openapi.openapi_server.models.base_model_ import Model
-from openapi.openapi_server import util
-
-
-class UpdateInfo(Model):
-    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
-
-    Do not edit the class manually.
-    """
-
-    def __init__(self, created_time=None, last_modified_time=None, resource_type=None):  # noqa: E501
-        """UpdateInfo - a model defined in OpenAPI
-
-        :param created_time: The created_time of this UpdateInfo.  # noqa: E501
-        :type created_time: float
-        :param last_modified_time: The last_modified_time of this UpdateInfo.  # noqa: E501
-        :type last_modified_time: float
-        :param resource_type: The resource_type of this UpdateInfo.  # noqa: E501
-        :type resource_type: str
-        """
-        self.openapi_types = {
-            'created_time': float,
-            'last_modified_time': float,
-            'resource_type': str
-        }
-
-        self.attribute_map = {
-            'created_time': 'created_time',
-            'last_modified_time': 'last_modified_time',
-            'resource_type': 'resource_type'
-        }
-
-        self._created_time = created_time
-        self._last_modified_time = last_modified_time
-        self._resource_type = resource_type
-
-    @classmethod
-    def from_dict(cls, dikt) -> 'UpdateInfo':
-        """Returns the dict as a model
-
-        :param dikt: A dict.
-        :type: dict
-        :return: The UpdateInfo of this UpdateInfo.  # noqa: E501
-        :rtype: UpdateInfo
-        """
-        return util.deserialize_model(dikt, cls)
-
-    @property
-    def created_time(self):
-        """Gets the created_time of this UpdateInfo.
-
-        When the resource was created (as POSIX timestamp).  # noqa: E501
-
-        :return: The created_time of this UpdateInfo.
-        :rtype: float
-        """
-        return self._created_time
-
-    @created_time.setter
-    def created_time(self, created_time):
-        """Sets the created_time of this UpdateInfo.
-
-        When the resource was created (as POSIX timestamp).  # noqa: E501
-
-        :param created_time: The created_time of this UpdateInfo.
-        :type created_time: float
-        """
-        if created_time is None:
-            raise ValueError("Invalid value for `created_time`, must not be `None`")  # noqa: E501
-
-        self._created_time = created_time
-
-    @property
-    def last_modified_time(self):
-        """Gets the last_modified_time of this UpdateInfo.
-
-        When the resource was last modified (as POSIX timestamp).  # noqa: E501
-
-        :return: The last_modified_time of this UpdateInfo.
-        :rtype: float
-        """
-        return self._last_modified_time
-
-    @last_modified_time.setter
-    def last_modified_time(self, last_modified_time):
-        """Sets the last_modified_time of this UpdateInfo.
-
-        When the resource was last modified (as POSIX timestamp).  # noqa: E501
-
-        :param last_modified_time: The last_modified_time of this UpdateInfo.
-        :type last_modified_time: float
-        """
-        if last_modified_time is None:
-            raise ValueError("Invalid value for `last_modified_time`, must not be `None`")  # noqa: E501
-
-        self._last_modified_time = last_modified_time
-
-    @property
-    def resource_type(self):
-        """Gets the resource_type of this UpdateInfo.
-
-        Name of the resource for which update timestamps are indexed.  # noqa: E501
-
-        :return: The resource_type of this UpdateInfo.
-        :rtype: str
-        """
-        return self._resource_type
-
-    @resource_type.setter
-    def resource_type(self, resource_type):
-        """Sets the resource_type of this UpdateInfo.
-
-        Name of the resource for which update timestamps are indexed.  # noqa: E501
-
-        :param resource_type: The resource_type of this UpdateInfo.
-        :type resource_type: str
-        """
-        allowed_values = ["cts_data", "exercise_list", "file_api_clean"]  # noqa: E501
-        if resource_type not in allowed_values:
-            raise ValueError(
-                "Invalid value for `resource_type` ({0}), must be one of {1}"
-                .format(resource_type, allowed_values)
-            )
-
-        self._resource_type = resource_type
diff --git a/mc_backend/openapi/openapi_server/models/vocabulary.py b/mc_backend/openapi/openapi_server/models/vocabulary.py
deleted file mode 100644
index 711dcda..0000000
--- a/mc_backend/openapi/openapi_server/models/vocabulary.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# coding: utf-8
-
-from __future__ import absolute_import
-from datetime import date, datetime  # noqa: F401
-
-from typing import List, Dict  # noqa: F401
-
-from openapi.openapi_server.models.base_model_ import Model
-from openapi.openapi_server import util
-
-
-class Vocabulary(Model):
-    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
-
-    Do not edit the class manually.
-    """
-
-    """
-    allowed enum values
-    """
-    AGLDT = "agldt"
-    BWS = "bws"
-    PROIEL = "proiel"
-    VIVA = "viva"
-    def __init__(self):  # noqa: E501
-        """Vocabulary - a model defined in OpenAPI
-
-        """
-        self.openapi_types = {
-        }
-
-        self.attribute_map = {
-        }
-
-    @classmethod
-    def from_dict(cls, dikt) -> 'Vocabulary':
-        """Returns the dict as a model
-
-        :param dikt: A dict.
-        :type: dict
-        :return: The Vocabulary of this Vocabulary.  # noqa: E501
-        :rtype: Vocabulary
-        """
-        return util.deserialize_model(dikt, cls)
diff --git a/mc_backend/openapi/openapi_server/openapi/openapi.yaml b/mc_backend/openapi/openapi_server/openapi/openapi.yaml
index 88cc247..88c4a84 100644
--- a/mc_backend/openapi/openapi_server/openapi/openapi.yaml
+++ b/mc_backend/openapi/openapi_server/openapi/openapi.yaml
@@ -201,7 +201,9 @@ paths:
           content:
             application/json:
               schema:
-                $ref: '#/components/schemas/Exercise'
+                items:
+                  $ref: '#/components/schemas/MatchingExercise'
+                type: array
           description: Data for interactive exercises, excluding the linguistic details.
       summary: Provides metadata for all available exercises.
       x-openapi-router-controller: openapi_server.controllers.default_controller
@@ -239,9 +241,15 @@ paths:
       responses:
         "200":
           content:
-            application/json:
+            application/pdf:
+              schema:
+                type: object
+            application/vnd.openxmlformats-officedocument.wordprocessingml.document:
+              schema:
+                type: object
+            application/xml:
               schema:
-                $ref: '#/components/schemas/Exercise'
+                type: object
           description: Data for interactive exercises, excluding the linguistic details.
       summary: Provides the URL to download a specific file.
       x-openapi-router-controller: openapi_server.controllers.default_controller
@@ -604,9 +612,9 @@ components:
             $ref: '#/components/schemas/inline_object'
       required: true
   schemas:
-    TextComplexityForm:
+    TextComplexityFormExtension:
       allOf:
-      - $ref: '#/components/schemas/TextComplexityFormBase'
+      - $ref: '#/components/schemas/TextComplexityForm'
       type: object
     Corpus:
       description: Collection of texts.
@@ -760,14 +768,17 @@ components:
         exercise_type: ddwtos
       properties:
         exercise_id:
+          default: ""
           description: Unique identifier (UUID) for the exercise.
           example: 12345678-1234-5678-1234-567812345678
           type: string
         exercise_type:
+          default: ""
           description: Type of exercise, concerning interaction and layout.
           example: ddwtos
           type: string
         frequency_analysis:
+          default: []
           description: List of items with frequency data for linguistic phenomena.
           items:
             $ref: '#/components/schemas/FrequencyItem'
@@ -775,6 +786,7 @@ components:
         graph_data:
           $ref: '#/components/schemas/GraphData'
         solutions:
+          default: []
           description: Correct solutions for this exercise.
           items:
             $ref: '#/components/schemas/Solution'
@@ -782,6 +794,7 @@ components:
         text_complexity:
           $ref: '#/components/schemas/TextComplexity'
         uri:
+          default: ""
           description: URI for accessing the exercise in this API.
           example: /mc/api/v1.0/file/fd97630c-1f5a-4102-af56-20eb0babdfee
           type: string
@@ -797,16 +810,19 @@ components:
         - upostag
       properties:
         count:
+          default: 0
           description: How often the given combination of values occurred.
           example: 1
           type: integer
         phenomena:
+          default: []
           description: Labels for the linguistic phenomena described in this frequency
             entry.
           items:
             $ref: '#/components/schemas/Phenomenon'
           type: array
         values:
+          default: []
           description: Values for the phenomena described in this frequency entry.
           items:
             type: string
@@ -1170,7 +1186,7 @@ components:
       type: object
     VocabularyMC:
       description: 'Reference vocabularies: Ancient Greek and Latin Dependency Treebank,
-        Bamberger Wortschatz, PROIEL treeban, VIVA textbook'
+        Bamberger Wortschatz, PROIEL treebank, VIVA textbook'
       enum:
       - agldt
       - bws
@@ -1178,10 +1194,64 @@ components:
       - viva
       example: agldt
       type: string
-    Exercise:
+    MatchingExercise:
       allOf:
       - $ref: '#/components/schemas/ExerciseBase'
-      - $ref: '#/components/schemas/Exercise_allOf'
+      - $ref: '#/components/schemas/ExerciseExtension'
+      - $ref: '#/components/schemas/MatchingExercise_allOf'
+    ExerciseExtension:
+      description: Additional data for creating and evaluating interactive exercises.
+      properties:
+        conll:
+          default: ""
+          description: CONLL-formatted linguistic annotations represented as a single
+            string.
+          example: \# newdoc id = ...\n# sent_id = 1\n# text = Caesar fortis est.\n1\tCaesar\tCaeso\tVERB
+            ...
+          nullable: false
+          type: string
+        eid:
+          description: Unique identifier (UUID) for the exercise.
+          example: 12345678-1234-5678-1234-567812345678
+          type: string
+          x-primary-key: true
+        exercise_type:
+          default: ""
+          description: Type of exercise, concerning interaction and layout.
+          example: markWords
+          nullable: false
+          type: string
+        last_access_time:
+          description: When the exercise was last accessed (as POSIX timestamp).
+          example: 1234567.789
+          format: float
+          type: number
+          x-index: true
+        solutions:
+          default: '[]'
+          description: Correct solutions for the exercise.
+          example: '[{''target'': {''sentence_id'': 1, ''token_id'': 7, ''salt_id'':
+            ''salt:/urn:...'', ''content'': ''eo''}, ''value'': {''sentence_id'':
+            0, ''token_id'': 0, ''content'': None, ''salt_id'': ''salt:/urn:...''}}]'
+          nullable: false
+          type: string
+        text_complexity:
+          default: 0
+          description: Overall text complexity as measured by the software's internal
+            language analysis.
+          example: 54.53
+          format: float
+          type: number
+        urn:
+          default: ""
+          description: CTS URN for the text passage from which the exercise was created.
+          example: urn:cts:latinLit:phi0448.phi001.perseus-lat2:1.1.1-1.1.1
+          nullable: false
+          type: string
+      required:
+      - eid
+      - last_access_time
+      type: object
     FileType:
       description: File format for the requested serialization.
       enum:
@@ -1304,7 +1374,7 @@ components:
       - vocabulary
       type: object
       x-body-name: vocabulary_data
-    TextComplexityFormBase:
+    TextComplexityForm:
       description: Relevant parameters for measuring the text complexity of a text
         passage.
       discriminator:
@@ -1320,7 +1390,9 @@ components:
           example: urn:cts:latinLit:phi0448.phi001.perseus-lat2:1.1.1-1.1.1
           type: string
         annis_response:
-          $ref: '#/components/schemas/AnnisResponse'
+          description: Serialized ANNIS response.
+          example: '{}'
+          type: string
       required:
       - measure
       - urn
@@ -1365,60 +1437,16 @@ components:
       required:
       - type
       - urn
-    Exercise_allOf:
-      description: Data for creating and evaluating interactive exercises.
+    MatchingExercise_allOf:
+      description: Extra data for comparison with a reference vocabulary.
       properties:
-        conll:
-          default: ""
-          description: CONLL-formatted linguistic annotations represented as a single
-            string.
-          example: \# newdoc id = ...\n# sent_id = 1\n# text = Caesar fortis est.\n1\tCaesar\tCaeso\tVERB
-            ...
-          nullable: false
-          type: string
-        eid:
-          description: Unique identifier (UUID) for the exercise.
-          example: 12345678-1234-5678-1234-567812345678
-          type: string
-          x-primary-key: true
-        exercise_type:
-          default: ""
-          description: Type of exercise, concerning interaction and layout.
-          example: markWords
-          nullable: false
-          type: string
         exercise_type_translation:
-          default: ""
           description: Localized expression of the exercise type.
           example: Cloze
           type: string
-        last_access_time:
-          description: When the exercise was last accessed (as POSIX timestamp).
-          example: 1234567.789
-          format: float
-          type: number
-          x-index: true
-        solutions:
-          default: '[]'
-          description: Correct solutions for the exercise.
-          example: '[{''target'': {''sentence_id'': 1, ''token_id'': 7, ''salt_id'':
-            ''salt:/urn:...'', ''content'': ''eo''}, ''value'': {''sentence_id'':
-            0, ''token_id'': 0, ''content'': None, ''salt_id'': ''salt:/urn:...''}}]'
-          nullable: false
-          type: string
-        text_complexity:
-          default: 0
-          description: Overall text complexity as measured by the software's internal
-            language analysis.
-          example: 54.53
+        matching_degree:
+          description: Percentage of words in the exercise that match a reference
+            vocabulary.
+          example: 76.34
           format: float
           type: number
-        urn:
-          default: ""
-          description: CTS URN for the text passage from which the exercise was created.
-          example: urn:cts:latinLit:phi0448.phi001.perseus-lat2:1.1.1-1.1.1
-          nullable: false
-          type: string
-      required:
-      - eid
-      - last_access_time
diff --git a/mc_backend/openapi/openapi_server/test/test_default_controller.py b/mc_backend/openapi/openapi_server/test/test_default_controller.py
index f00cee0..17dcf1c 100644
--- a/mc_backend/openapi/openapi_server/test/test_default_controller.py
+++ b/mc_backend/openapi/openapi_server/test/test_default_controller.py
@@ -6,10 +6,15 @@ import unittest
 from flask import json
 from six import BytesIO
 
+from openapi.openapi_server.models.annis_response import AnnisResponse  # noqa: E501
 from openapi.openapi_server.models.corpus import Corpus  # noqa: E501
-from openapi.openapi_server.models.exercise_base import ExerciseBase  # noqa: E501
-from openapi.openapi_server.models.inline_response200 import InlineResponse200  # noqa: E501
-from openapi.openapi_server.models.unknownbasetype import UNKNOWN_BASE_TYPE  # noqa: E501
+from openapi.openapi_server.models.file_type import FileType  # noqa: E501
+from openapi.openapi_server.models.frequency_item import FrequencyItem  # noqa: E501
+from openapi.openapi_server.models.matching_exercise import MatchingExercise  # noqa: E501
+from openapi.openapi_server.models.sentence import Sentence  # noqa: E501
+from openapi.openapi_server.models.static_exercise import StaticExercise  # noqa: E501
+from openapi.openapi_server.models.text_complexity import TextComplexity  # noqa: E501
+from openapi.openapi_server.models.vocabulary_mc import VocabularyMC  # noqa: E501
 from openapi.openapi_server.test import BaseTestCase
 
 
@@ -105,15 +110,284 @@ class TestDefaultController(BaseTestCase):
 
         Creates a new exercise.
         """
-        unknown_base_type = {}
         headers = { 
+            'Accept': 'application/json',
             'Content-Type': 'application/x-www-form-urlencoded',
         }
         response = self.client.open(
             '/mc/api/v1.0/exercise',
             method='POST',
             headers=headers,
-            data=json.dumps(unknown_base_type),
+            content_type='application/x-www-form-urlencoded')
+        self.assert200(response,
+                       'Response body is : ' + response.data.decode('utf-8'))
+
+    def test_mcserver_app_api_exercise_list_api_get(self):
+        """Test case for mcserver_app_api_exercise_list_api_get
+
+        Provides metadata for all available exercises.
+        """
+        query_string = [('lang', en),
+                        ('frequency_upper_bound', 500),
+                        ('last_update_time', 123456789),
+                        ('vocabulary', {})]
+        headers = { 
+            'Accept': 'application/json',
+        }
+        response = self.client.open(
+            '/mc/api/v1.0/exerciseList',
+            method='GET',
+            headers=headers,
+            query_string=query_string)
+        self.assert200(response,
+                       'Response body is : ' + response.data.decode('utf-8'))
+
+    def test_mcserver_app_api_file_api_get(self):
+        """Test case for mcserver_app_api_file_api_get
+
+        Provides the URL to download a specific file.
+        """
+        query_string = [('id', 12345678-1234-5678-1234-567812345678),
+                        ('type', {}),
+                        ('solution_indices', 56)]
+        headers = { 
+            'Accept': 'application/xml',
+        }
+        response = self.client.open(
+            '/mc/api/v1.0/file',
+            method='GET',
+            headers=headers,
+            query_string=query_string)
+        self.assert200(response,
+                       'Response body is : ' + response.data.decode('utf-8'))
+
+    @unittest.skip("application/x-www-form-urlencoded not supported by Connexion")
+    def test_mcserver_app_api_file_api_post(self):
+        """Test case for mcserver_app_api_file_api_post
+
+        Serializes and persists learning results or HTML content for later access.
+        """
+        headers = { 
+            'Accept': 'application/json',
+            'Content-Type': 'application/x-www-form-urlencoded',
+        }
+        data = dict(file_type={},
+                    html_content='html_content_example',
+                    learning_result='learning_result_example',
+                    urn='urn_example')
+        response = self.client.open(
+            '/mc/api/v1.0/file',
+            method='POST',
+            headers=headers,
+            data=data,
+            content_type='application/x-www-form-urlencoded')
+        self.assert200(response,
+                       'Response body is : ' + response.data.decode('utf-8'))
+
+    def test_mcserver_app_api_frequency_api_get(self):
+        """Test case for mcserver_app_api_frequency_api_get
+
+        Returns results for a frequency query from ANNIS for a given CTS URN.
+        """
+        query_string = [('urn', urn:cts:latinLit:phi1254.phi001.perseus-lat2:5.6.21-5.6.21)]
+        headers = { 
+            'Accept': 'application/json',
+        }
+        response = self.client.open(
+            '/mc/api/v1.0/frequency',
+            method='GET',
+            headers=headers,
+            query_string=query_string)
+        self.assert200(response,
+                       'Response body is : ' + response.data.decode('utf-8'))
+
+    def test_mcserver_app_api_h5p_api_get(self):
+        """Test case for mcserver_app_api_h5p_api_get
+
+        Provides JSON templates for client-side H5P exercise layouts.
+        """
+        query_string = [('eid', 12345678-1234-5678-1234-567812345678),
+                        ('lang', en),
+                        ('solution_indices', 56)]
+        headers = { 
+            'Accept': 'application/json',
+        }
+        response = self.client.open(
+            '/mc/api/v1.0/h5p',
+            method='GET',
+            headers=headers,
+            query_string=query_string)
+        self.assert200(response,
+                       'Response body is : ' + response.data.decode('utf-8'))
+
+    @unittest.skip("application/x-www-form-urlencoded not supported by Connexion")
+    def test_mcserver_app_api_kwic_api_post(self):
+        """Test case for mcserver_app_api_kwic_api_post
+
+        Provides example contexts for a given phenomenon in a given corpus.
+        """
+        headers = { 
+            'Accept': 'application/json',
+            'Content-Type': 'application/x-www-form-urlencoded',
+        }
+        data = dict(search_values='[]',
+                    urn='urn_example',
+                    ctx_left=56,
+                    ctx_right=56)
+        response = self.client.open(
+            '/mc/api/v1.0/kwic',
+            method='POST',
+            headers=headers,
+            data=data,
+            content_type='application/x-www-form-urlencoded')
+        self.assert200(response,
+                       'Response body is : ' + response.data.decode('utf-8'))
+
+    def test_mcserver_app_api_raw_text_api_get(self):
+        """Test case for mcserver_app_api_raw_text_api_get
+
+        Provides the raw text for a requested text passage.
+        """
+        query_string = [('urn', urn:cts:latinLit:phi1254.phi001.perseus-lat2:5.6.21-5.6.21)]
+        headers = { 
+            'Accept': 'application/json',
+        }
+        response = self.client.open(
+            '/mc/api/v1.0/rawtext',
+            method='GET',
+            headers=headers,
+            query_string=query_string)
+        self.assert200(response,
+                       'Response body is : ' + response.data.decode('utf-8'))
+
+    def test_mcserver_app_api_static_exercises_api_get(self):
+        """Test case for mcserver_app_api_static_exercises_api_get
+
+        Returns metadata for static exercises.
+        """
+        headers = { 
+            'Accept': 'application/json',
+        }
+        response = self.client.open(
+            '/mc/api/v1.0/staticExercises',
+            method='GET',
+            headers=headers)
+        self.assert200(response,
+                       'Response body is : ' + response.data.decode('utf-8'))
+
+    def test_mcserver_app_api_textcomplexity_api_get(self):
+        """Test case for mcserver_app_api_textcomplexity_api_get
+
+        Gives users measures of text complexity for a given text.
+        """
+        query_string = [('measure', all),
+                        ('urn', urn:cts:latinLit:phi1254.phi001.perseus-lat2:5.6.21-5.6.21)]
+        headers = { 
+            'Accept': 'application/json',
+        }
+        response = self.client.open(
+            '/mc/api/v1.0/textcomplexity',
+            method='GET',
+            headers=headers,
+            query_string=query_string)
+        self.assert200(response,
+                       'Response body is : ' + response.data.decode('utf-8'))
+
+    def test_mcserver_app_api_valid_reff_api_get(self):
+        """Test case for mcserver_app_api_valid_reff_api_get
+
+        Gives users all the citable text references for a corpus.
+        """
+        query_string = [('urn', urn:cts:latinLit:phi1254.phi001.perseus-lat2:5.6.21-5.6.21)]
+        headers = { 
+            'Accept': 'application/json',
+        }
+        response = self.client.open(
+            '/mc/api/v1.0/validReff',
+            method='GET',
+            headers=headers,
+            query_string=query_string)
+        self.assert200(response,
+                       'Response body is : ' + response.data.decode('utf-8'))
+
+    def test_mcserver_app_api_vector_network_api_get(self):
+        """Test case for mcserver_app_api_vector_network_api_get
+
+        Provides network data for the vectors in an AI model.
+        """
+        query_string = [('search_regex', 'search_regex_example'),
+                        ('highlight_regex', ver[aoe]),
+                        ('min_count', 3),
+                        ('nearest_neighbor_count', 0)]
+        headers = { 
+            'Accept': 'application/json',
+        }
+        response = self.client.open(
+            '/mc/api/v1.0/vectorNetwork',
+            method='GET',
+            headers=headers,
+            query_string=query_string)
+        self.assert200(response,
+                       'Response body is : ' + response.data.decode('utf-8'))
+
+    @unittest.skip("application/x-www-form-urlencoded not supported by Connexion")
+    def test_mcserver_app_api_vector_network_api_post(self):
+        """Test case for mcserver_app_api_vector_network_api_post
+
+        Provides network data for the vectors in an AI model.
+        """
+        headers = { 
+            'Accept': 'application/json',
+            'Content-Type': 'application/x-www-form-urlencoded',
+        }
+        data = dict(search_regex='search_regex_example',
+                    nearest_neighbor_count=0)
+        response = self.client.open(
+            '/mc/api/v1.0/vectorNetwork',
+            method='POST',
+            headers=headers,
+            data=data,
+            content_type='application/x-www-form-urlencoded')
+        self.assert200(response,
+                       'Response body is : ' + response.data.decode('utf-8'))
+
+    def test_mcserver_app_api_vocabulary_api_get(self):
+        """Test case for mcserver_app_api_vocabulary_api_get
+
+        Shows how well the vocabulary of a text matches a predefined reference vocabulary.
+        """
+        query_string = [('frequency_upper_bound', 500),
+                        ('query_urn', urn:cts:latinLit:phi0448.phi001.perseus-lat2:1.1.1-1.1.1),
+                        ('vocabulary', {})]
+        headers = { 
+            'Accept': 'application/json',
+        }
+        response = self.client.open(
+            '/mc/api/v1.0/vocabulary',
+            method='GET',
+            headers=headers,
+            query_string=query_string)
+        self.assert200(response,
+                       'Response body is : ' + response.data.decode('utf-8'))
+
+    @unittest.skip("application/x-www-form-urlencoded not supported by Connexion")
+    def test_mcserver_app_api_vocabulary_api_post(self):
+        """Test case for mcserver_app_api_vocabulary_api_post
+
+        Shows how well the vocabulary of a text matches a predefined reference vocabulary.
+        """
+        headers = { 
+            'Accept': 'application/json',
+            'Content-Type': 'application/x-www-form-urlencoded',
+        }
+        data = dict(frequency_upper_bound=56,
+                    query_urn='query_urn_example',
+                    vocabulary={})
+        response = self.client.open(
+            '/mc/api/v1.0/vocabulary',
+            method='POST',
+            headers=headers,
+            data=data,
             content_type='application/x-www-form-urlencoded')
         self.assert200(response,
                        'Response body is : ' + response.data.decode('utf-8'))
diff --git a/mc_backend/openapi_models.yaml b/mc_backend/openapi_models.yaml
index fe89b63..f36daa1 100644
--- a/mc_backend/openapi_models.yaml
+++ b/mc_backend/openapi_models.yaml
@@ -44,15 +44,18 @@ components:
           type: string
           description: Unique identifier (UUID) for the exercise.
           example: 12345678-1234-5678-1234-567812345678
+          default: ""
         exercise_type:
           type: string
           description: Type of exercise, concerning interaction and layout.
           example: ddwtos
+          default: ""
         frequency_analysis:
           type: array
           description: List of items with frequency data for linguistic phenomena.
           items:
             $ref: "#/components/schemas/FrequencyItem"
+          default: []
         graph_data:
           $ref: "#/components/schemas/GraphData"
         solutions:
@@ -60,15 +63,17 @@ components:
           description: Correct solutions for this exercise.
           items:
             $ref: '#/components/schemas/Solution'
+          default: []
         text_complexity:
           $ref: '#/components/schemas/TextComplexity'
         uri:
           type: string
           description: URI for accessing the exercise in this API.
           example: /mc/api/v1.0/file/fd97630c-1f5a-4102-af56-20eb0babdfee
-    Corpus:  # Object definition
+          default: ""
+    Corpus:
       description: Collection of texts.
-      type: object   # Data type
+      type: object
       x-tablename: Corpus
       properties:
         author:
@@ -114,59 +119,16 @@ components:
     Exercise:
       allOf:
         - $ref: "#/components/schemas/ExerciseBase"
+        - $ref: "#/components/schemas/ExerciseExtension"
         - description: Data for creating and evaluating interactive exercises.
-          type: object   # Data type
+          type: object
           x-tablename: Exercise
           properties:
-            conll:
-              type: string
-              description: CONLL-formatted linguistic annotations represented as a single string.
-              example: \# newdoc id = ...\n# sent_id = 1\n# text = Caesar fortis est.\n1\tCaesar\tCaeso\tVERB ...
-              default: ""
-              nullable: false
-            eid:
-              type: string
-              description: Unique identifier (UUID) for the exercise.
-              example: 12345678-1234-5678-1234-567812345678
-              x-primary-key: true
-            exercise_type:
-              type: string
-              description: Type of exercise, concerning interaction and layout.
-              example: markWords
-              default: ""
-              nullable: false
             exercise_type_translation:
               type: string
               description: Localized expression of the exercise type.
               example: Cloze
               default: ""
-            last_access_time:
-              type: number
-              format: float
-              description: When the exercise was last accessed (as POSIX timestamp).
-              example: 1234567.789
-              x-index: true
-            solutions:
-              type: string
-              description: Correct solutions for the exercise.
-              example: "[{'target': {'sentence_id': 1, 'token_id': 7, 'salt_id': 'salt:/urn:...', 'content': 'eo'}, 'value': {'sentence_id': 0, 'token_id': 0, 'content': None, 'salt_id': 'salt:/urn:...'}}]"
-              default: "[]"
-              nullable: false
-            text_complexity:
-              type: number
-              format: float
-              description: Overall text complexity as measured by the software's internal language analysis.
-              example: 54.53
-              default: 0
-            urn:
-              type: string
-              description: CTS URN for the text passage from which the exercise was created.
-              example: urn:cts:latinLit:phi0448.phi001.perseus-lat2:1.1.1-1.1.1
-              default: ""
-              nullable: false
-          required:
-            - eid
-            - last_access_time
     ExerciseBase:
       description: Base data for creating and evaluating interactive exercises.
       type: object
@@ -219,6 +181,54 @@ components:
       required:
         - instructions
         - search_values
+    ExerciseExtension:
+      description: Additional data for creating and evaluating interactive exercises.
+      type: object
+      properties:
+        conll:
+          type: string
+          description: CONLL-formatted linguistic annotations represented as a single string.
+          example: \# newdoc id = ...\n# sent_id = 1\n# text = Caesar fortis est.\n1\tCaesar\tCaeso\tVERB ...
+          default: ""
+          nullable: false
+        eid:
+          type: string
+          description: Unique identifier (UUID) for the exercise.
+          example: 12345678-1234-5678-1234-567812345678
+          x-primary-key: true
+        exercise_type:
+          type: string
+          description: Type of exercise, concerning interaction and layout.
+          example: markWords
+          default: ""
+          nullable: false
+        last_access_time:
+          type: number
+          format: float
+          description: When the exercise was last accessed (as POSIX timestamp).
+          example: 1234567.789
+          x-index: true
+        solutions:
+          type: string
+          description: Correct solutions for the exercise.
+          example: "[{'target': {'sentence_id': 1, 'token_id': 7, 'salt_id': 'salt:/urn:...', 'content': 'eo'}, 'value': {'sentence_id': 0, 'token_id': 0, 'content': None, 'salt_id': 'salt:/urn:...'}}]"
+          default: "[]"
+          nullable: false
+        text_complexity:
+          type: number
+          format: float
+          description: Overall text complexity as measured by the software's internal language analysis.
+          example: 54.53
+          default: 0
+        urn:
+          type: string
+          description: CTS URN for the text passage from which the exercise was created.
+          example: urn:cts:latinLit:phi0448.phi001.perseus-lat2:1.1.1-1.1.1
+          default: ""
+          nullable: false
+      required:
+        - eid
+        - last_access_time
     ExerciseForm:
       x-body-name: exercise_data
       type: object
@@ -255,17 +265,20 @@ components:
           type: integer
           description: How often the given combination of values occurred.
           example: 1
+          default: 0
         phenomena:
           type: array
           description: Labels for the linguistic phenomena described in this frequency entry.
           items:
             $ref: '#/components/schemas/Phenomenon'
+          default: []
         values:
           type: array
           description: Values for the phenomena described in this frequency entry.
           items:
             type: string
             example: ""
+          default: []
     GraphData:
       type: object
       description: Nodes, edges and metadata for a graph.
@@ -465,6 +478,22 @@ components:
           type: string
           description: Dependency relation described by the edge.
           example: "det"
+    MatchingExercise:
+      allOf:
+        - $ref: "#/components/schemas/ExerciseBase"
+        - $ref: "#/components/schemas/ExerciseExtension"
+        - description: Extra data for comparison with a reference vocabulary.
+          type: object
+          properties:
+            exercise_type_translation:
+              type: string
+              description: Localized expression of the exercise type.
+              example: Cloze
+            matching_degree:
+              type: number
+              format: float
+              description: Percentage of words in the exercise that match a reference vocabulary.
+              example: 76.34
     NearestNeighborCount:
       type: integer
       description: Number of nearest neighbors that should be considered for each target node in a graph analysis.
@@ -658,9 +687,9 @@ components:
           type: integer
           description: Number of distinct part of speech tags in the given corpus.
           example: 1
-    TextComplexityFormBase:
-      x-body-name: complexity_data
+    TextComplexityForm:
       type: object
+      x-body-name: complexity_data
       description: Relevant parameters for measuring the text complexity of a text passage.
       properties:
         measure:
@@ -672,7 +701,9 @@ components:
           description: CTS URN for the text passage from which the text complexity should be calculated.
           example: urn:cts:latinLit:phi0448.phi001.perseus-lat2:1.1.1-1.1.1
         annis_response:
-          $ref: '#/components/schemas/AnnisResponse'
+          type: string
+          description: Serialized ANNIS response.
+          example: "{}"
       required:
         - measure
         - urn
diff --git a/mc_backend/tests.py b/mc_backend/tests.py
index edcd899..52e3226 100644
--- a/mc_backend/tests.py
+++ b/mc_backend/tests.py
@@ -45,7 +45,7 @@ from mcserver.config import TestingConfig, Config
 from mcserver.models_auto import Corpus, Exercise, UpdateInfo, LearningResult
 from mocks import Mocks, MockResponse, MockW2V, MockQuery, TestHelper
 from openapi.openapi_server.models import VocabularyForm, VocabularyMC, TextComplexityForm, ExerciseForm, KwicForm, \
-    VectorNetworkForm
+    VectorNetworkForm, MatchingExercise
 
 
 class McTestCase(unittest.TestCase):
@@ -249,10 +249,11 @@ class McTestCase(unittest.TestCase):
         db.session.add(Mocks.exercise)
         db.session.commit()
         response = Mocks.app_dict[self.class_name].client.get(TestingConfig.SERVER_URI_EXERCISE_LIST, query_string=args)
-        exercises: List[Exercise] = []
-        for exercise_dict in json.loads(response.get_data()):
+        exercises: List[MatchingExercise] = []
+        for exercise_dict in json.loads(response.get_data(as_text=True)):
             exercise_dict["search_values"] = json.dumps(exercise_dict["search_values"])
-            exercises.append(ExerciseMC.from_dict(**exercise_dict))
+            exercise_dict["solutions"] = json.dumps(exercise_dict["solutions"])
+            exercises.append(MatchingExercise.from_dict(exercise_dict))
         self.assertEqual(len(exercises), 1)
         args = dict(lang=Language.English.value, vocabulary=VocabularyCorpus.agldt.name, frequency_upper_bound=500)
         response = Mocks.app_dict[self.class_name].client.get(TestingConfig.SERVER_URI_EXERCISE_LIST, query_string=args)
diff --git a/mc_frontend/openapi/api/default.service.ts b/mc_frontend/openapi/api/default.service.ts
index a9e0c8b..b7cde88 100644
--- a/mc_frontend/openapi/api/default.service.ts
+++ b/mc_frontend/openapi/api/default.service.ts
@@ -19,9 +19,9 @@ import { Observable }                                        from 'rxjs';
 
 import { AnnisResponse } from '../model/models';
 import { Corpus } from '../model/models';
-import { Exercise } from '../model/models';
 import { FileType } from '../model/models';
 import { FrequencyItem } from '../model/models';
+import { MatchingExercise } from '../model/models';
 import { Sentence } from '../model/models';
 import { StaticExercise } from '../model/models';
 import { TextComplexity } from '../model/models';
@@ -415,9 +415,9 @@ export class DefaultService {
      * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
      * @param reportProgress flag to report request and response progress.
      */
-    public mcserverAppApiExerciseListAPIGet(lang: string, frequencyUpperBound?: number, lastUpdateTime?: number, vocabulary?: VocabularyMC, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<Exercise>;
-    public mcserverAppApiExerciseListAPIGet(lang: string, frequencyUpperBound?: number, lastUpdateTime?: number, vocabulary?: VocabularyMC, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpResponse<Exercise>>;
-    public mcserverAppApiExerciseListAPIGet(lang: string, frequencyUpperBound?: number, lastUpdateTime?: number, vocabulary?: VocabularyMC, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpEvent<Exercise>>;
+    public mcserverAppApiExerciseListAPIGet(lang: string, frequencyUpperBound?: number, lastUpdateTime?: number, vocabulary?: VocabularyMC, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<Array<MatchingExercise>>;
+    public mcserverAppApiExerciseListAPIGet(lang: string, frequencyUpperBound?: number, lastUpdateTime?: number, vocabulary?: VocabularyMC, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpResponse<Array<MatchingExercise>>>;
+    public mcserverAppApiExerciseListAPIGet(lang: string, frequencyUpperBound?: number, lastUpdateTime?: number, vocabulary?: VocabularyMC, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpEvent<Array<MatchingExercise>>>;
     public mcserverAppApiExerciseListAPIGet(lang: string, frequencyUpperBound?: number, lastUpdateTime?: number, vocabulary?: VocabularyMC, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable<any> {
         if (lang === null || lang === undefined) {
             throw new Error('Required parameter lang was null or undefined when calling mcserverAppApiExerciseListAPIGet.');
@@ -461,7 +461,7 @@ export class DefaultService {
             responseType = 'text';
         }
 
-        return this.httpClient.get<Exercise>(`${this.configuration.basePath}/exerciseList`,
+        return this.httpClient.get<Array<MatchingExercise>>(`${this.configuration.basePath}/exerciseList`,
             {
                 params: queryParameters,
                 responseType: <any>responseType,
@@ -481,10 +481,10 @@ export class DefaultService {
      * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
      * @param reportProgress flag to report request and response progress.
      */
-    public mcserverAppApiFileAPIGet(id: string, type: FileType, solutionIndices?: Array<number>, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<Exercise>;
-    public mcserverAppApiFileAPIGet(id: string, type: FileType, solutionIndices?: Array<number>, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpResponse<Exercise>>;
-    public mcserverAppApiFileAPIGet(id: string, type: FileType, solutionIndices?: Array<number>, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/json'}): Observable<HttpEvent<Exercise>>;
-    public mcserverAppApiFileAPIGet(id: string, type: FileType, solutionIndices?: Array<number>, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/json'}): Observable<any> {
+    public mcserverAppApiFileAPIGet(id: string, type: FileType, solutionIndices?: Array<number>, observe?: 'body', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/pdf' | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' | 'application/xml'}): Observable<object>;
+    public mcserverAppApiFileAPIGet(id: string, type: FileType, solutionIndices?: Array<number>, observe?: 'response', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/pdf' | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' | 'application/xml'}): Observable<HttpResponse<object>>;
+    public mcserverAppApiFileAPIGet(id: string, type: FileType, solutionIndices?: Array<number>, observe?: 'events', reportProgress?: boolean, options?: {httpHeaderAccept?: 'application/pdf' | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' | 'application/xml'}): Observable<HttpEvent<object>>;
+    public mcserverAppApiFileAPIGet(id: string, type: FileType, solutionIndices?: Array<number>, observe: any = 'body', reportProgress: boolean = false, options?: {httpHeaderAccept?: 'application/pdf' | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' | 'application/xml'}): Observable<any> {
         if (id === null || id === undefined) {
             throw new Error('Required parameter id was null or undefined when calling mcserverAppApiFileAPIGet.');
         }
@@ -512,7 +512,9 @@ export class DefaultService {
         if (httpHeaderAcceptSelected === undefined) {
             // to determine the Accept header
             const httpHeaderAccepts: string[] = [
-                'application/json'
+                'application/pdf',
+                'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+                'application/xml'
             ];
             httpHeaderAcceptSelected = this.configuration.selectHeaderAccept(httpHeaderAccepts);
         }
@@ -526,7 +528,7 @@ export class DefaultService {
             responseType = 'text';
         }
 
-        return this.httpClient.get<Exercise>(`${this.configuration.basePath}/file`,
+        return this.httpClient.get<object>(`${this.configuration.basePath}/file`,
             {
                 params: queryParameters,
                 responseType: <any>responseType,
diff --git a/mc_frontend/openapi/model/exerciseAllOf.ts b/mc_frontend/openapi/model/exerciseExtension.ts
similarity index 84%
rename from mc_frontend/openapi/model/exerciseAllOf.ts
rename to mc_frontend/openapi/model/exerciseExtension.ts
index 5052ff2..83e4fc8 100644
--- a/mc_frontend/openapi/model/exerciseAllOf.ts
+++ b/mc_frontend/openapi/model/exerciseExtension.ts
@@ -12,9 +12,9 @@
 
 
 /**
- * Data for creating and evaluating interactive exercises.
+ * Additional data for creating and evaluating interactive exercises.
  */
-export interface ExerciseAllOf { 
+export interface ExerciseExtension { 
     /**
      * CONLL-formatted linguistic annotations represented as a single string.
      */
@@ -27,10 +27,6 @@ export interface ExerciseAllOf {
      * Type of exercise, concerning interaction and layout.
      */
     exercise_type?: string;
-    /**
-     * Localized expression of the exercise type.
-     */
-    exercise_type_translation?: string;
     /**
      * When the exercise was last accessed (as POSIX timestamp).
      */
diff --git a/mc_frontend/openapi/model/exerciseFormComp.ts b/mc_frontend/openapi/model/exerciseFormComp.ts
deleted file mode 100644
index d9cb53b..0000000
--- a/mc_frontend/openapi/model/exerciseFormComp.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-/**
- * Machina Callida Backend REST API
- * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
- *
- * The version of the OpenAPI document: 1.0
- * 
- *
- * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
- * https://openapi-generator.tech
- * Do not edit the class manually.
- */
-import { ExerciseForm } from './exerciseForm';
-import { ExerciseBase } from './exerciseBase';
-
-
-export interface ExerciseFormComp { 
-    /**
-     * Feedback for successful completion of the exercise.
-     */
-    correct_feedback?: string;
-    /**
-     * Feedback for finishing the exercise.
-     */
-    general_feedback?: string;
-    /**
-     * Feedback for failing to complete the exercise successfully.
-     */
-    incorrect_feedback?: string;
-    /**
-     * Hints for how to complete the exercise.
-     */
-    instructions: string;
-    /**
-     * Feedback for successfully completing certain parts of the exercise.
-     */
-    partially_correct_feedback?: string;
-    /**
-     * Search queries that were used to build the exercise.
-     */
-    search_values: string;
-    /**
-     * Name of the person who wrote the base text for the exercise.
-     */
-    work_author?: string;
-    /**
-     * Title of the base text for the exercise.
-     */
-    work_title?: string;
-    /**
-     * Type of exercise, concerning interaction and layout.
-     */
-    type: string;
-    /**
-     * Localized expression of the exercise type.
-     */
-    type_translation?: string;
-    /**
-     * CTS URN for the text passage from which the exercise was created.
-     */
-    urn?: string;
-}
-
diff --git a/mc_frontend/openapi/model/learningResult.ts b/mc_frontend/openapi/model/learningResult.ts
deleted file mode 100644
index e6a1cde..0000000
--- a/mc_frontend/openapi/model/learningResult.ts
+++ /dev/null
@@ -1,107 +0,0 @@
-/**
- * Machina Callida Backend REST API
- * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
- *
- * The version of the OpenAPI document: 1.0
- * 
- *
- * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
- * https://openapi-generator.tech
- * Do not edit the class manually.
- */
-
-
-/**
- * Learner data for completed exercises.
- */
-export interface LearningResult { 
-    /**
-     * H5P user ID, usually unique per device.
-     */
-    actor_account_name?: string;
-    /**
-     * Describes the kind of object that was recognized as actor.
-     */
-    actor_object_type?: string;
-    /**
-     * Link to the exercise type specification.
-     */
-    category_id?: string;
-    /**
-     * Describes the kind of object that was recognized as exercise.
-     */
-    category_object_type?: string;
-    /**
-     * JSON string containing a list of possible choices, each with ID and description.
-     */
-    choices?: string;
-    /**
-     * Whether the exercise was fully processed or not.
-     */
-    completion: boolean;
-    /**
-     * JSON string containing a list of possible solutions to the exercise, given as patterns of answers.
-     */
-    correct_responses_pattern: string;
-    /**
-     * When the learner data was received (POSIX timestamp).
-     */
-    created_time: number;
-    /**
-     * How many seconds it took a learner to complete the exercise.
-     */
-    duration?: string;
-    /**
-     * JSON string containing a mapping of keys and values (usually the local content ID, i.e. a versioning mechanism).
-     */
-    extensions?: string;
-    /**
-     * Exercise type.
-     */
-    interaction_type?: string;
-    /**
-     * Exercise content, possibly including instructions.
-     */
-    object_definition_description: string;
-    /**
-     * Type of object definition that is presented to the user.
-     */
-    object_definition_type?: string;
-    /**
-     * Type of object that is presented to the user.
-     */
-    object_object_type?: string;
-    /**
-     * Answer provided by the user, possibly as a pattern.
-     */
-    response: string;
-    /**
-     * Maximum possible score to be achieved in this exercise.
-     */
-    score_max: number;
-    /**
-     * Minimum score to be achieved in this exercise.
-     */
-    score_min: number;
-    /**
-     * Score that was actually achieved by the user in this exercise.
-     */
-    score_raw: number;
-    /**
-     * Relative score (between 0 and 1) that was actually achieved by the user in this exercise.
-     */
-    score_scaled?: number;
-    /**
-     * Whether the exercise was successfully completed or not.
-     */
-    success: boolean;
-    /**
-     * Type of action that was performed by the user.
-     */
-    verb_display?: string;
-    /**
-     * Link to the type of action that was performed by the user.
-     */
-    verb_id?: string;
-}
-
diff --git a/mc_frontend/openapi/model/exercise.ts b/mc_frontend/openapi/model/matchingExercise.ts
similarity index 88%
rename from mc_frontend/openapi/model/exercise.ts
rename to mc_frontend/openapi/model/matchingExercise.ts
index c12689a..3d82616 100644
--- a/mc_frontend/openapi/model/exercise.ts
+++ b/mc_frontend/openapi/model/matchingExercise.ts
@@ -9,11 +9,12 @@
  * https://openapi-generator.tech
  * Do not edit the class manually.
  */
-import { ExerciseAllOf } from './exerciseAllOf';
+import { MatchingExerciseAllOf } from './matchingExerciseAllOf';
+import { ExerciseExtension } from './exerciseExtension';
 import { ExerciseBase } from './exerciseBase';
 
 
-export interface Exercise { 
+export interface MatchingExercise { 
     /**
      * Feedback for successful completion of the exercise.
      */
@@ -62,10 +63,6 @@ export interface Exercise {
      * Type of exercise, concerning interaction and layout.
      */
     exercise_type?: string;
-    /**
-     * Localized expression of the exercise type.
-     */
-    exercise_type_translation?: string;
     /**
      * When the exercise was last accessed (as POSIX timestamp).
      */
@@ -82,5 +79,13 @@ export interface Exercise {
      * CTS URN for the text passage from which the exercise was created.
      */
     urn?: string;
+    /**
+     * Localized expression of the exercise type.
+     */
+    exercise_type_translation?: string;
+    /**
+     * Percentage of words in the exercise that match a reference vocabulary.
+     */
+    matching_degree?: number;
 }
 
diff --git a/mc_frontend/openapi/model/annisResponseFrequencyAnalysis.ts b/mc_frontend/openapi/model/matchingExerciseAllOf.ts
similarity index 54%
rename from mc_frontend/openapi/model/annisResponseFrequencyAnalysis.ts
rename to mc_frontend/openapi/model/matchingExerciseAllOf.ts
index e9f2e78..894ec53 100644
--- a/mc_frontend/openapi/model/annisResponseFrequencyAnalysis.ts
+++ b/mc_frontend/openapi/model/matchingExerciseAllOf.ts
@@ -11,18 +11,17 @@
  */
 
 
-export interface AnnisResponseFrequencyAnalysis { 
-    /**
-     * How often the given combination of values occurred.
-     */
-    count?: number;
+/**
+ * Extra data for comparison with a reference vocabulary.
+ */
+export interface MatchingExerciseAllOf { 
     /**
-     * Labels for the phenomena described in this frequency entry.
+     * Localized expression of the exercise type.
      */
-    phenomena?: Array<string>;
+    exercise_type_translation?: string;
     /**
-     * Values for the phenomena described in this frequency entry.
+     * Percentage of words in the exercise that match a reference vocabulary.
      */
-    values?: Array<string>;
+    matching_degree?: number;
 }
 
diff --git a/mc_frontend/openapi/model/models.ts b/mc_frontend/openapi/model/models.ts
index 2a449c0..1fd415c 100644
--- a/mc_frontend/openapi/model/models.ts
+++ b/mc_frontend/openapi/model/models.ts
@@ -1,8 +1,7 @@
 export * from './annisResponse';
 export * from './corpus';
-export * from './exercise';
-export * from './exerciseAllOf';
 export * from './exerciseBase';
+export * from './exerciseExtension';
 export * from './exerciseForm';
 export * from './exerciseFormAllOf';
 export * from './fileType';
@@ -11,6 +10,8 @@ export * from './graphData';
 export * from './inlineObject';
 export * from './kwicForm';
 export * from './link';
+export * from './matchingExercise';
+export * from './matchingExerciseAllOf';
 export * from './nodeMC';
 export * from './phenomenon';
 export * from './sentence';
@@ -19,7 +20,7 @@ export * from './solutionElement';
 export * from './staticExercise';
 export * from './textComplexity';
 export * from './textComplexityForm';
-export * from './textComplexityFormBase';
+export * from './textComplexityFormExtension';
 export * from './vectorNetworkForm';
 export * from './vocabularyForm';
 export * from './vocabularyMC';
diff --git a/mc_frontend/openapi/model/node.ts b/mc_frontend/openapi/model/node.ts
deleted file mode 100644
index 894599f..0000000
--- a/mc_frontend/openapi/model/node.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- * Machina Callida Backend REST API
- * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
- *
- * The version of the OpenAPI document: 1.0
- * 
- *
- * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
- * https://openapi-generator.tech
- * Do not edit the class manually.
- */
-
-
-export interface Node { 
-    /**
-     * Node name as given by ANNIS.
-     */
-    annis_node_name: string;
-    /**
-     * Node type as given by ANNIS.
-     */
-    annis_node_type: string;
-    /**
-     * Raw word form as given by ANNIS.
-     */
-    annis_tok: string;
-    /**
-     * Node type as given by ANNIS (?).
-     */
-    annis_type: string;
-    /**
-     * Unique identifier for the node in the SALT model.
-     */
-    id: string;
-    /**
-     * Whether the raw word form is missing in a given vocabulary.
-     */
-    is_oov?: boolean;
-    /**
-     * Lemmatized word form.
-     */
-    udep_lemma: string;
-    /**
-     * Universal part of speech tag for the word form.
-     */
-    udep_upostag: string;
-    /**
-     * Language-specific part of speech tag for the word form.
-     */
-    udep_xpostag?: string;
-    /**
-     * Additional morphological information.
-     */
-    udep_feats?: string;
-    /**
-     * Solution value for this node in an exercise.
-     */
-    solution?: string;
-}
-
diff --git a/mc_frontend/openapi/model/textComplexityForm.ts b/mc_frontend/openapi/model/textComplexityForm.ts
index 1c7f598..4068023 100644
--- a/mc_frontend/openapi/model/textComplexityForm.ts
+++ b/mc_frontend/openapi/model/textComplexityForm.ts
@@ -9,10 +9,23 @@
  * https://openapi-generator.tech
  * Do not edit the class manually.
  */
-import { AnnisResponse } from './annisResponse';
-import { TextComplexityFormBase } from './textComplexityFormBase';
 
 
-export interface TextComplexityForm extends TextComplexityFormBase { 
+/**
+ * Relevant parameters for measuring the text complexity of a text passage.
+ */
+export interface TextComplexityForm { 
+    /**
+     * Label of the desired measure for text complexity.
+     */
+    measure: string;
+    /**
+     * CTS URN for the text passage from which the text complexity should be calculated.
+     */
+    urn: string;
+    /**
+     * Serialized ANNIS response.
+     */
+    annis_response?: string;
 }
 
diff --git a/mc_frontend/openapi/model/textComplexityForm2.ts b/mc_frontend/openapi/model/textComplexityForm2.ts
deleted file mode 100644
index ee4780a..0000000
--- a/mc_frontend/openapi/model/textComplexityForm2.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * Machina Callida Backend REST API
- * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
- *
- * The version of the OpenAPI document: 1.0
- * 
- *
- * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
- * https://openapi-generator.tech
- * Do not edit the class manually.
- */
-import { AnnisResponse } from './annisResponse';
-
-
-/**
- * Relevant parameters for measuring the text complexity of a text passage.
- */
-export interface TextComplexityForm2 { 
-    /**
-     * Label of the desired measure for text complexity.
-     */
-    measure: string;
-    /**
-     * CTS URN for the text passage from which the text complexity should be calculated.
-     */
-    urn: string;
-    annis_response?: AnnisResponse;
-}
-
diff --git a/mc_frontend/openapi/model/textComplexityFormBase.ts b/mc_frontend/openapi/model/textComplexityFormBase.ts
deleted file mode 100644
index 7f58ad2..0000000
--- a/mc_frontend/openapi/model/textComplexityFormBase.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * Machina Callida Backend REST API
- * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
- *
- * The version of the OpenAPI document: 1.0
- * 
- *
- * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
- * https://openapi-generator.tech
- * Do not edit the class manually.
- */
-import { AnnisResponse } from './annisResponse';
-
-
-/**
- * Relevant parameters for measuring the text complexity of a text passage.
- */
-export interface TextComplexityFormBase { 
-    /**
-     * Label of the desired measure for text complexity.
-     */
-    measure: string;
-    /**
-     * CTS URN for the text passage from which the text complexity should be calculated.
-     */
-    urn: string;
-    annis_response?: AnnisResponse;
-}
-
diff --git a/mc_frontend/openapi/model/inlineResponse200.ts b/mc_frontend/openapi/model/textComplexityFormExtension.ts
similarity index 61%
rename from mc_frontend/openapi/model/inlineResponse200.ts
rename to mc_frontend/openapi/model/textComplexityFormExtension.ts
index 6e6e82a..6337543 100644
--- a/mc_frontend/openapi/model/inlineResponse200.ts
+++ b/mc_frontend/openapi/model/textComplexityFormExtension.ts
@@ -9,16 +9,9 @@
  * https://openapi-generator.tech
  * Do not edit the class manually.
  */
+import { TextComplexityForm } from './textComplexityForm';
 
 
-export interface InlineResponse200 { 
-    /**
-     * Solutions for the exercise.
-     */
-    solutions?: Array<Array<string>>;
-    /**
-     * CTS URN for the text passage from which the exercise was created.
-     */
-    urn?: string;
+export interface TextComplexityFormExtension extends TextComplexityForm { 
 }
 
diff --git a/mc_frontend/openapi/model/updateInfo.ts b/mc_frontend/openapi/model/updateInfo.ts
deleted file mode 100644
index 61ac427..0000000
--- a/mc_frontend/openapi/model/updateInfo.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * Machina Callida Backend REST API
- * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
- *
- * The version of the OpenAPI document: 1.0
- * 
- *
- * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
- * https://openapi-generator.tech
- * Do not edit the class manually.
- */
-
-
-/**
- * Timestamps for updates of various resources.
- */
-export interface UpdateInfo { 
-    /**
-     * When the resource was created (as POSIX timestamp).
-     */
-    created_time: number;
-    /**
-     * When the resource was last modified (as POSIX timestamp).
-     */
-    last_modified_time: number;
-    /**
-     * Name of the resource for which update timestamps are indexed.
-     */
-    resource_type: UpdateInfo.ResourceTypeEnum;
-}
-export namespace UpdateInfo {
-    export type ResourceTypeEnum = 'cts_data' | 'exercise_list' | 'file_api_clean';
-    export const ResourceTypeEnum = {
-        CtsData: 'cts_data' as ResourceTypeEnum,
-        ExerciseList: 'exercise_list' as ResourceTypeEnum,
-        FileApiClean: 'file_api_clean' as ResourceTypeEnum
-    };
-}
-
-
diff --git a/mc_frontend/openapi/model/vocabulary.ts b/mc_frontend/openapi/model/vocabulary.ts
deleted file mode 100644
index ec151e2..0000000
--- a/mc_frontend/openapi/model/vocabulary.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/**
- * Machina Callida Backend REST API
- * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
- *
- * The version of the OpenAPI document: 1.0
- * 
- *
- * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
- * https://openapi-generator.tech
- * Do not edit the class manually.
- */
-
-
-/**
- * Reference vocabularies: Ancient Greek and Latin Dependency Treebank, Bamberger Wortschatz, PROIEL treeban, VIVA textbook
- */
-export type Vocabulary = 'agldt' | 'bws' | 'proiel' | 'viva';
-
-export const Vocabulary = {
-    Agldt: 'agldt' as Vocabulary,
-    Bws: 'bws' as Vocabulary,
-    Proiel: 'proiel' as Vocabulary,
-    Viva: 'viva' as Vocabulary
-};
-
diff --git a/mc_frontend/openapi/model/vocabularyMC.ts b/mc_frontend/openapi/model/vocabularyMC.ts
index 509729c..c50bcc1 100644
--- a/mc_frontend/openapi/model/vocabularyMC.ts
+++ b/mc_frontend/openapi/model/vocabularyMC.ts
@@ -12,7 +12,7 @@
 
 
 /**
- * Reference vocabularies: Ancient Greek and Latin Dependency Treebank, Bamberger Wortschatz, PROIEL treeban, VIVA textbook
+ * Reference vocabularies: Ancient Greek and Latin Dependency Treebank, Bamberger Wortschatz, PROIEL treebank, VIVA textbook
  */
 export type VocabularyMC = 'agldt' | 'bws' | 'proiel' | 'viva';
 
diff --git a/mc_frontend/src/app/author-detail/author-detail.page.spec.ts b/mc_frontend/src/app/author-detail/author-detail.page.spec.ts
index 773dd87..0f0762e 100644
--- a/mc_frontend/src/app/author-detail/author-detail.page.spec.ts
+++ b/mc_frontend/src/app/author-detail/author-detail.page.spec.ts
@@ -1,6 +1,5 @@
 import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
-import {async, ComponentFixture, inject, TestBed} from '@angular/core/testing';
-
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
 import {AuthorDetailPage} from './author-detail.page';
 import {RouterModule} from '@angular/router';
 import {IonicStorageModule} from '@ionic/storage';
@@ -8,7 +7,6 @@ import {TranslateTestingModule} from '../translate-testing/translate-testing.mod
 import {APP_BASE_HREF} from '@angular/common';
 import {HttpClientTestingModule} from '@angular/common/http/testing';
 import Spy = jasmine.Spy;
-import {CorpusMC} from '../models/corpusMC';
 
 describe('AuthorDetailPage', () => {
     let authorDetailPage: AuthorDetailPage;
@@ -44,7 +42,7 @@ describe('AuthorDetailPage', () => {
     it('should show possible references', () => {
         const currentCorpusSpy: Spy = spyOn(authorDetailPage.corpusService, 'setCurrentCorpus');
         const textRangeSpy: Spy = spyOn(authorDetailPage.helperService, 'goToTextRangePage').and.returnValue(Promise.resolve(true));
-        authorDetailPage.showPossibleReferences(new CorpusMC());
+        authorDetailPage.showPossibleReferences({source_urn: ''});
         expect(currentCorpusSpy).toHaveBeenCalledTimes(1);
         expect(textRangeSpy).toHaveBeenCalledTimes(1);
     });
diff --git a/mc_frontend/src/app/author/author.page.spec.ts b/mc_frontend/src/app/author/author.page.spec.ts
index bae1255..06ececf 100644
--- a/mc_frontend/src/app/author/author.page.spec.ts
+++ b/mc_frontend/src/app/author/author.page.spec.ts
@@ -9,7 +9,6 @@ import {IonicStorageModule} from '@ionic/storage';
 import {TranslateTestingModule} from '../translate-testing/translate-testing.module';
 import {APP_BASE_HREF} from '@angular/common';
 import {Author} from '../models/author';
-import {CorpusMC} from '../models/corpusMC';
 import Spy = jasmine.Spy;
 import MockMC from '../models/mockMC';
 
@@ -66,7 +65,7 @@ describe('AuthorPage', () => {
 
     it('should be initialized', () => {
         authorPage.corpusService.availableAuthors = [new Author({
-            corpora: [new CorpusMC({source_urn: 'proiel'})],
+            corpora: [{source_urn: 'proiel'}],
             name: 'name'
         })];
         authorPage.ngOnInit();
diff --git a/mc_frontend/src/app/corpus.service.spec.ts b/mc_frontend/src/app/corpus.service.spec.ts
index 7e50976..02829f9 100644
--- a/mc_frontend/src/app/corpus.service.spec.ts
+++ b/mc_frontend/src/app/corpus.service.spec.ts
@@ -250,7 +250,7 @@ describe('CorpusService', () => {
 
     it('should initialize the current corpus', fakeAsync(() => {
         helperService.applicationState.next(new ApplicationState());
-        let corpus: CorpusMC;
+        let corpus: CorpusMC = {source_urn: ''};
 
         function initCorpus(): void {
             corpusService.initCurrentCorpus().then(() => {
@@ -262,10 +262,10 @@ describe('CorpusService', () => {
         }
 
         initCorpus();
-        expect(corpus).toBeUndefined();
+        expect(corpus.source_urn).toBeFalsy();
         helperService.applicationState.next(helperService.deepCopy(MockMC.applicationState) as ApplicationState);
         initCorpus();
-        expect(corpus).toBeTruthy();
+        expect(corpus.source_urn).toBeTruthy();
         corpus = undefined;
         initCorpus();
         expect(corpus).toBeTruthy();
@@ -290,7 +290,7 @@ describe('CorpusService', () => {
     it('should load corpora from local storage', (done) => {
         corpusService.availableCorpora = [];
         spyOn(corpusService.storage, 'get').withArgs(configMC.localStorageKeyCorpora).and.returnValue(
-            Promise.resolve(JSON.stringify([new CorpusMC({source_urn: 'urn'})])));
+            Promise.resolve(JSON.stringify([{source_urn: 'urn'}])));
         corpusService.loadCorporaFromLocalStorage().then(() => {
             expect(corpusService.availableCorpora.length).toBe(1);
             done();
@@ -313,12 +313,12 @@ describe('CorpusService', () => {
 
     it('should process corpora', () => {
         const corpusList: CorpusMC[] = [
-            new CorpusMC({author: '', source_urn: 'proiel'}),
-            new CorpusMC({author: 'b', source_urn: 'b', title: 't1'}),
-            new CorpusMC({author: 'b', source_urn: 'b', title: 't1'}),
-            new CorpusMC({author: 'b', source_urn: 'c', title: 't3'}),
-            new CorpusMC({author: 'b', source_urn: 'e', title: 't2'}),
-            new CorpusMC({author: 'a', source_urn: 'd'}),
+            {author: '', source_urn: 'proiel'},
+            {author: 'b', source_urn: 'b', title: 't1'},
+            {author: 'b', source_urn: 'b', title: 't1'},
+            {author: 'b', source_urn: 'c', title: 't3'},
+            {author: 'b', source_urn: 'e', title: 't2'},
+            {author: 'a', source_urn: 'd'},
         ];
         corpusService.availableAuthors = [new Author({name: ' PROIEL', corpora: []})];
         corpusService.processCorpora(corpusList);
@@ -376,7 +376,7 @@ describe('CorpusService', () => {
     it('should save a new corpus', (done) => {
         corpusService.initCurrentTextRange();
         corpusService.initCurrentCorpus().then(() => {
-            corpusService.setCurrentCorpus(new CorpusMC());
+            corpusService.setCurrentCorpus({source_urn: ''});
             corpusService.currentCorpus.pipe(take(1)).subscribe((corpus: CorpusMC) => {
                 expect(corpus).toBeTruthy();
                 done();
diff --git a/mc_frontend/src/app/exercise-parameters/exercise-parameters.page.html b/mc_frontend/src/app/exercise-parameters/exercise-parameters.page.html
index 4c13f21..255ed77 100644
--- a/mc_frontend/src/app/exercise-parameters/exercise-parameters.page.html
+++ b/mc_frontend/src/app/exercise-parameters/exercise-parameters.page.html
@@ -54,8 +54,8 @@
                                 <h2 class="label">{{ 'QUERY_PHENOMENON' | translate }}</h2>
                                 <select [(ngModel)]="query.phenomenon" name="queryPhenomenon"
                                         (change)="corpusService.adjustQueryValue(query, i)">
-                                    <option *ngFor="let phenomenon of ObjectKeys(Phenomenon)" [value]=phenomenon>
-                                        {{ PhenomenonTranslation[phenomenon] | translate }}
+                                    <option *ngFor="let phenomenon of ObjectValues(Phenomenon)" [value]=phenomenon>
+                                        {{ PhenomenonTranslation[phenomenon.toString()] | translate }}
                                     </option>
                                 </select>
                             </label>
diff --git a/mc_frontend/src/app/exercise-parameters/exercise-parameters.page.spec.ts b/mc_frontend/src/app/exercise-parameters/exercise-parameters.page.spec.ts
index 4e47cb2..fef66dd 100644
--- a/mc_frontend/src/app/exercise-parameters/exercise-parameters.page.spec.ts
+++ b/mc_frontend/src/app/exercise-parameters/exercise-parameters.page.spec.ts
@@ -142,11 +142,11 @@ describe('ExerciseParametersPage', () => {
     it('should get a KWIC exercise', (done) => {
         const navSpy: Spy = spyOn(exerciseParametersPage.helperService, 'goToKwicPage').and.returnValue(Promise.resolve(true));
         const requestSpy: Spy = spyOn(exerciseParametersPage.helperService, 'makePostRequest').and.returnValue(Promise.resolve('svg'));
-        exerciseParametersPage.getKwicExercise(new FormData()).then(() => {
+        exerciseParametersPage.getKwicExercise('').then(() => {
             expect(exerciseParametersPage.exerciseService.kwicGraphs.length).toBe(3);
             expect(navSpy).toHaveBeenCalledTimes(1);
             requestSpy.and.callFake(() => Promise.reject());
-            exerciseParametersPage.getKwicExercise(new FormData()).then(() => {
+            exerciseParametersPage.getKwicExercise('').then(() => {
             }, () => {
                 expect(navSpy).toHaveBeenCalledTimes(1);
                 done();
diff --git a/mc_frontend/src/app/exercise-parameters/exercise-parameters.page.ts b/mc_frontend/src/app/exercise-parameters/exercise-parameters.page.ts
index ec78973..577fc3a 100644
--- a/mc_frontend/src/app/exercise-parameters/exercise-parameters.page.ts
+++ b/mc_frontend/src/app/exercise-parameters/exercise-parameters.page.ts
@@ -20,6 +20,7 @@ import {take} from 'rxjs/operators';
 import {TextRange} from '../models/textRange';
 import configMC from '../../configMC';
 import {AnnisResponse, FrequencyItem, Phenomenon} from '../../../openapi';
+import {KwicForm} from '../../../openapi';
 
 @Component({
     selector: 'app-exercise-parameters',
@@ -32,6 +33,7 @@ export class ExerciseParametersPage implements OnInit {
     public leftContextSize = 5;
     public Math = Math;
     ObjectKeys = Object.keys;
+    ObjectValues = Object.values;
     Phenomenon = Phenomenon;
     PhenomenonTranslation = PhenomenonTranslation;
     public rightContextSize = 5;
@@ -89,12 +91,9 @@ export class ExerciseParametersPage implements OnInit {
         return new Promise<void>(resolve => {
             const searchValues: string[] = this.corpusService.exercise.queryItems.map(
                 query => query.phenomenon + '=' + query.values.join('|'));
-            const formData = new FormData();
-            formData.append('urn', this.corpusService.currentUrn);
-            formData.append('search_values', JSON.stringify(searchValues));
             let instructions: string = this.corpusService.exercise.instructionsTranslation;
             if (this.corpusService.exercise.type === ExerciseType.kwic) {
-                this.getKwicExercise(formData).then();
+                this.getKwicExercise(JSON.stringify(searchValues)).then();
                 return resolve();
             } else if (this.corpusService.exercise.type === ExerciseType.markWords) {
                 const phenomenon: Phenomenon = this.corpusService.exercise.queryItems[0].phenomenon;
@@ -106,6 +105,9 @@ export class ExerciseParametersPage implements OnInit {
                 this.corpusService.currentTextRange.pipe(take(1)).subscribe((tr: TextRange) => {
                     // TODO: change the corpus title to something meaningful, e.g. concatenate user ID and wanted exercise title
                     const workTitle: string = cc.title + ', ' + tr.start.filter(x => x).join('.') + '-' + tr.end.filter(x => x).join('.');
+                    const formData = new FormData();
+                    formData.append('urn', this.corpusService.currentUrn);
+                    formData.append('search_values', JSON.stringify(searchValues));
                     formData.append('correct_feedback', this.corpusService.exercise.feedback.correct);
                     formData.append('instructions', instructions);
                     formData.append('general_feedback', this.corpusService.exercise.feedback.general);
@@ -145,10 +147,18 @@ export class ExerciseParametersPage implements OnInit {
         });
     }
 
-    getKwicExercise(formData: FormData): Promise<void> {
+    getKwicExercise(searchValues: string): Promise<void> {
         return new Promise<void>((resolve, reject) => {
-            formData.append('ctx_left', (Math.max(Math.round(this.leftContextSize), 1)).toString());
-            formData.append('ctx_right', (Math.max(Math.round(this.rightContextSize), 1)).toString());
+            const kf: KwicForm = {
+                ctx_left: (Math.max(Math.round(this.leftContextSize), 1)),
+                ctx_right: Math.max(Math.round(this.rightContextSize), 1),
+                search_values: searchValues,
+                urn: this.corpusService.currentUrn
+            };
+            const formData = new FormData();
+            Object.keys(kf).forEach((key: string) => {
+                formData.append(key, kf[key]);
+            });
             const kwicUrl: string = configMC.backendBaseUrl + configMC.backendApiKwicPath;
             this.helperService.makePostRequest(this.http, this.toastCtrl, kwicUrl, formData).then((svgString: string) => {
                 this.exerciseService.kwicGraphs = svgString;
diff --git a/mc_frontend/src/app/models/corpusMC.ts b/mc_frontend/src/app/models/corpusMC.ts
index b80b2a5..faeae60 100644
--- a/mc_frontend/src/app/models/corpusMC.ts
+++ b/mc_frontend/src/app/models/corpusMC.ts
@@ -1,16 +1,7 @@
 /* tslint:disable:variable-name */
 import {Citation} from 'src/app/models/citation';
+import {Corpus} from '../../../openapi';
 
-export class CorpusMC {
-    public author: string;
-    public cid: number;
-    public source_urn: string;
-    public title: string;
-    public citation_level_1: string;
-    public citation_level_2: string;
-    public citation_level_3: string;
-    public citations: { [label: string]: Citation; };
-    constructor(init?: Partial<CorpusMC>) {
-        Object.assign(this, init);
-    }
+export interface CorpusMC extends Corpus {
+    citations?: { [label: string]: Citation; };
 }
diff --git a/mc_frontend/src/app/models/enum.ts b/mc_frontend/src/app/models/enum.ts
index a0f5b8b..d0c4cc4 100644
--- a/mc_frontend/src/app/models/enum.ts
+++ b/mc_frontend/src/app/models/enum.ts
@@ -52,6 +52,7 @@ export enum DependencyValue {
     root = 'root' as any,
     punctuation = 'punctuation' as any,
     subject = 'subject' as any,
+    unspecified = 'unspecified' as any,
     vocative = 'vocative' as any,
 }
 
@@ -85,6 +86,7 @@ export enum DependencyTranslation {
     punctuation = 'DEPENDENCY_PUNCTUATION' as any,
     root = 'DEPENDENCY_ROOT' as any,
     subject = 'DEPENDENCY_SUBJECT' as any,
+    unspecified = 'DEPENDENCY_UNSPECIFIED' as any,
     vocative = 'DEPENDENCY_VOCATIVE' as any,
 }
 
@@ -102,12 +104,6 @@ export enum ExerciseTypeTranslation {
     matching = 'EXERCISE_TYPE_MATCHING' as any,
 }
 
-export enum FileType {
-    docx = 'docx' as any,
-    pdf = 'pdf' as any,
-    xml = 'xml' as any,
-}
-
 export enum InstructionsTranslation {
     cloze = 'INSTRUCTIONS_CLOZE' as any,
     kwic = 'INSTRUCTIONS_KWIC' as any,
diff --git a/mc_frontend/src/app/models/mockMC.ts b/mc_frontend/src/app/models/mockMC.ts
index af3964f..ab460d2 100644
--- a/mc_frontend/src/app/models/mockMC.ts
+++ b/mc_frontend/src/app/models/mockMC.ts
@@ -12,11 +12,11 @@ import {AnnisResponse, FrequencyItem} from '../../../openapi';
 import {Phenomenon} from '../../../openapi';
 
 export default class MockMC {
-    static apiResponseCorporaGet: CorpusMC[] = [new CorpusMC({
+    static apiResponseCorporaGet: CorpusMC[] = [{
         author: 'author',
         source_urn: 'urn',
         title: 'title',
-    })];
+    }];
     static apiResponseFrequencyAnalysisGet: FrequencyItem[] = [{
         phenomena: [Phenomenon.Upostag],
         values: [PartOfSpeechValue.adjective.toString()]
@@ -29,7 +29,7 @@ export default class MockMC {
     };
     static applicationState: ApplicationState = new ApplicationState({
         currentSetup: new TextData({
-            currentCorpus: new CorpusMC({citations: {}}),
+            currentCorpus: {citations: {}, source_urn: ''},
             currentTextRange: new TextRange({start: ['1', '2'], end: ['1', '2']})
         }),
         mostRecentSetup: new TextData({
diff --git a/mc_frontend/src/app/models/sentence.ts b/mc_frontend/src/app/models/sentence.ts
deleted file mode 100644
index 73bdddc..0000000
--- a/mc_frontend/src/app/models/sentence.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-export class Sentence {
-    public id: number;
-    public matching_degree: number;
-    constructor(init?: Partial<Sentence>) {
-        Object.assign(this, init);
-    }
-}
-
diff --git a/mc_frontend/src/app/models/textComplexity.ts b/mc_frontend/src/app/models/textComplexity.ts
deleted file mode 100644
index ccc41fa..0000000
--- a/mc_frontend/src/app/models/textComplexity.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-/* tslint:disable:variable-name */
-export class TextComplexity {
-    public avg_w_len: number;
-    public avg_w_per_sent: number;
-    public lex_den: number;
-    public n_abl_abs: number;
-    public n_clause: number;
-    public n_gerund: number;
-    public n_inf: number;
-    public n_part: number;
-    public n_punct: number;
-    public n_sent: number;
-    public n_subclause: number;
-    public n_types: number;
-    public n_w: number;
-    public pos: number;
-
-    constructor(init?: Partial<TextComplexity>) {
-        Object.assign(this, init);
-    }
-}
diff --git a/mc_frontend/src/app/preview/preview.page.html b/mc_frontend/src/app/preview/preview.page.html
index c9b890b..9b6785e 100644
--- a/mc_frontend/src/app/preview/preview.page.html
+++ b/mc_frontend/src/app/preview/preview.page.html
@@ -43,16 +43,16 @@
                 <!-- This is preferable compared to a button click event, because the browser knows from the
 beginning that it is going to be a download (instead of an ordinary link or click). -->
                 <ion-col>
-                    <a href="{{urlBase + FileType[FileType.docx] + solutionIndicesString}}" download>
+                    <a href="{{urlBase + FileType[FileType.Docx] + solutionIndicesString}}" download>
                         {{ 'FILE_TYPE_DOCX' | translate }}</a>
                 </ion-col>
                 <ion-col>
-                    <a href="{{urlBase + FileType[FileType.pdf] + solutionIndicesString}}"
+                    <a href="{{urlBase + FileType[FileType.Pdf] + solutionIndicesString}}"
                        download>{{ 'FILE_TYPE_PDF' | translate }}</a>
                 </ion-col>
                 <!--                TODO: ADD MOODLE SUPPORT FOR MARK WORDS EXERCISES -->
                 <ion-col *ngIf="corpusService.exercise.type !== ExerciseType.markWords">
-                    <a href="{{urlBase + FileType[FileType.xml] + solutionIndicesString}}" download>{{ 'FILE_TYPE_XML' |
+                    <a href="{{urlBase + FileType[FileType.Xml] + solutionIndicesString}}" download>{{ 'FILE_TYPE_XML' |
                         translate }}</a>
                 </ion-col>
                 <ion-col>
diff --git a/mc_frontend/src/app/preview/preview.page.ts b/mc_frontend/src/app/preview/preview.page.ts
index 1cd0343..949be1e 100644
--- a/mc_frontend/src/app/preview/preview.page.ts
+++ b/mc_frontend/src/app/preview/preview.page.ts
@@ -1,5 +1,5 @@
 /* tslint:disable:no-string-literal */
-import {ExerciseType, FileType} from 'src/app/models/enum';
+import {ExerciseType} from 'src/app/models/enum';
 import {HelperService} from 'src/app/helper.service';
 import {NavController, ToastController} from '@ionic/angular';
 import {ExerciseService} from 'src/app/exercise.service';
@@ -11,7 +11,7 @@ import {XAPIevent} from 'src/app/models/xAPIevent';
 import {TestResultMC} from 'src/app/models/testResultMC';
 import configMC from '../../configMC';
 import {Storage} from '@ionic/storage';
-import {AnnisResponse, Solution} from '../../../openapi';
+import {AnnisResponse, Solution, FileType} from '../../../openapi';
 
 @Component({
     selector: 'app-preview',
diff --git a/mc_frontend/src/app/ranking/ranking.page.spec.ts b/mc_frontend/src/app/ranking/ranking.page.spec.ts
index 0d5910c..24edda9 100644
--- a/mc_frontend/src/app/ranking/ranking.page.spec.ts
+++ b/mc_frontend/src/app/ranking/ranking.page.spec.ts
@@ -7,7 +7,6 @@ import {IonicStorageModule} from '@ionic/storage';
 import {RouterModule} from '@angular/router';
 import {TranslateTestingModule} from '../translate-testing/translate-testing.module';
 import {APP_BASE_HREF} from '@angular/common';
-import {Sentence} from '../models/sentence';
 import Spy = jasmine.Spy;
 import {AnnisResponse} from '../../../openapi';
 
@@ -48,11 +47,11 @@ describe('RankingPage', () => {
         const oovSpy: Spy = spyOn(rankingPage.vocService, 'getOOVwords').and.returnValue(Promise.resolve(ar));
         spyOn(rankingPage.corpusService, 'processAnnisResponse');
         spyOn(rankingPage.helperService, 'goToShowTextPage').and.returnValue(Promise.resolve(true));
-        rankingPage.showText([new Sentence({id: 1})]).then(() => {
+        rankingPage.showText([{id: 1}]).then(() => {
             expect(rankingPage.helperService.isVocabularyCheck).toBe(true);
             oovSpy.and.callFake(() => Promise.reject());
             rankingPage.helperService.isVocabularyCheck = false;
-            rankingPage.showText([new Sentence({id: 1})]).then(() => {
+            rankingPage.showText([{id: 1}]).then(() => {
             }, () => {
                 expect(rankingPage.helperService.isVocabularyCheck).toBe(false);
                 done();
diff --git a/mc_frontend/src/app/ranking/ranking.page.ts b/mc_frontend/src/app/ranking/ranking.page.ts
index 7afc779..4bb051c 100644
--- a/mc_frontend/src/app/ranking/ranking.page.ts
+++ b/mc_frontend/src/app/ranking/ranking.page.ts
@@ -5,8 +5,7 @@ import {HelperService} from 'src/app/helper.service';
 import {NavController, ToastController} from '@ionic/angular';
 import {CorpusService} from 'src/app/corpus.service';
 import {HttpErrorResponse} from '@angular/common/http';
-import {Sentence} from 'src/app/models/sentence';
-import {AnnisResponse} from '../../../openapi';
+import {AnnisResponse, Sentence} from '../../../openapi';
 
 @Component({
     selector: 'app-ranking',
diff --git a/mc_frontend/src/app/semantics/semantics.page.ts b/mc_frontend/src/app/semantics/semantics.page.ts
index aa4d7e7..7e9bed6 100644
--- a/mc_frontend/src/app/semantics/semantics.page.ts
+++ b/mc_frontend/src/app/semantics/semantics.page.ts
@@ -5,6 +5,7 @@ import configMC from '../../configMC';
 import {HttpClient, HttpErrorResponse, HttpParams} from '@angular/common/http';
 import {ActivatedRoute} from '@angular/router';
 import {CorpusService} from '../corpus.service';
+import {VectorNetworkForm} from '../../../openapi';
 
 @Component({
     selector: 'app-semantics',
@@ -38,9 +39,12 @@ export class SemanticsPage implements AfterViewInit {
 
     getSimilarContexts(): Promise<void> {
         return new Promise<void>((resolve, reject) => {
+            const vnf: VectorNetworkForm = {
+                search_regex: this.searchRegex,
+                nearest_neighbor_count: Math.max(Math.round(this.nearestNeighborCount), 1)
+            };
             const formData: FormData = new FormData();
-            formData.append('search_regex', this.searchRegex);
-            formData.append('nearest_neighbor_count', (Math.max(Math.round(this.nearestNeighborCount), 1)).toString());
+            Object.keys(vnf).forEach((key: string) => formData.append(key, vnf[key]));
             const semanticsUrl: string = configMC.backendBaseUrl + configMC.backendApiVectorNetworkPath;
             this.similarContexts = [];
             this.helperService.makePostRequest(this.http, this.toastCtrl, semanticsUrl, formData).then((contexts: string[][]) => {
diff --git a/mc_frontend/src/app/show-text/show-text.page.html b/mc_frontend/src/app/show-text/show-text.page.html
index c5b202f..6bea895 100644
--- a/mc_frontend/src/app/show-text/show-text.page.html
+++ b/mc_frontend/src/app/show-text/show-text.page.html
@@ -82,12 +82,12 @@
                         {{ 'TEXT_COMPLEXITY_DOCUMENTATION' | translate }}
                     </ion-row>
                     <ion-row>
-                        <a (click)="generateDownloadLink(FileType[FileType.docx])" style="padding:0.5em;">
+                        <a (click)="generateDownloadLink(FileType[FileType.Docx])" style="padding:0.5em;">
                             {{ 'GENERATE_FILE_DOCX' | translate }}
                         </a>
                     </ion-row>
                     <ion-row>
-                        <a (click)="generateDownloadLink(FileType[FileType.pdf])" style="padding:0.5em;">
+                        <a (click)="generateDownloadLink(FileType[FileType.Pdf])" style="padding:0.5em;">
                             {{ 'GENERATE_FILE_PDF' | translate }}
                         </a>
                     </ion-row>
diff --git a/mc_frontend/src/app/show-text/show-text.page.ts b/mc_frontend/src/app/show-text/show-text.page.ts
index 890e818..8a9e8f0 100644
--- a/mc_frontend/src/app/show-text/show-text.page.ts
+++ b/mc_frontend/src/app/show-text/show-text.page.ts
@@ -6,11 +6,12 @@ import {VocabularyService} from 'src/app/vocabulary.service';
 import {ExerciseService} from 'src/app/exercise.service';
 import {HelperService} from 'src/app/helper.service';
 import {TranslateService} from '@ngx-translate/core';
-import {FileType, VocabularyCorpus} from '../models/enum';
+import {VocabularyCorpus} from '../models/enum';
 import {HttpClient} from '@angular/common/http';
 import {CorpusMC} from '../models/corpusMC';
 import {take} from 'rxjs/operators';
 import configMC from '../../configMC';
+import { FileType } from 'openapi';
 
 @Component({
     selector: 'app-show-text',
diff --git a/mc_frontend/src/app/text-range/text-range.page.spec.ts b/mc_frontend/src/app/text-range/text-range.page.spec.ts
index 67892a4..b6aed1d 100644
--- a/mc_frontend/src/app/text-range/text-range.page.spec.ts
+++ b/mc_frontend/src/app/text-range/text-range.page.spec.ts
@@ -51,12 +51,13 @@ describe('TextRangePage', () => {
 
     it('should add level 3 references', (done) => {
         const addReferencesSpy: Spy = spyOn(textRangePage, 'addReferences').and.returnValue(Promise.resolve());
-        textRangePage.addLevel3References([], new CorpusMC()).then(() => {
+        textRangePage.addLevel3References([], {source_urn: ''}).then(() => {
             expect(addReferencesSpy).toHaveBeenCalledTimes(0);
             textRangePage.currentInputId = 2;
-            const corpus: CorpusMC = new CorpusMC({
+            const corpus: CorpusMC = {
+                source_urn: '',
                 citations: {1: new Citation({subcitations: {2: new Citation({subcitations: {3: new Citation()}})}})}
-            });
+            };
             textRangePage.addLevel3References(['1', '2'], corpus).then(() => {
                 expect(addReferencesSpy).toHaveBeenCalledTimes(0);
                 corpus.citations['1'].subcitations['2'].subcitations = {};
@@ -80,7 +81,7 @@ describe('TextRangePage', () => {
         }
 
         textRangePage.corpusService.currentCorpus = new ReplaySubject<CorpusMC>(1);
-        textRangePage.corpusService.currentCorpus.next(new CorpusMC({citations: {4: new Citation()}}));
+        textRangePage.corpusService.currentCorpus.next({citations: {4: new Citation()}, source_urn: ''});
         resetCitationValues();
         const citationLabels: string[] = ['1'];
         textRangePage.addMissingCitations(citationLabels, citationLabels).then(() => {
@@ -106,7 +107,7 @@ describe('TextRangePage', () => {
     });
 
     it('should add references', (done) => {
-        const corpus: CorpusMC = new CorpusMC({citations: {0: new Citation({subcitations: {}, label: ''})}});
+        const corpus: CorpusMC = {citations: {0: new Citation({subcitations: {}, label: ''})}, source_urn: ''};
         textRangePage.corpusService.currentCorpus = new ReplaySubject<CorpusMC>(1);
         textRangePage.corpusService.currentCorpus.next(corpus);
         const validReffSpy: Spy = spyOn(textRangePage.corpusService, 'getCTSvalidReff').and.callFake(() => Promise.reject());
@@ -147,13 +148,14 @@ describe('TextRangePage', () => {
         textRangePage.corpusService.initCurrentTextRange();
         textRangePage.helperService.applicationState.next(textRangePage.helperService.deepCopy(MockMC.applicationState));
         textRangePage.corpusService.currentCorpus = new ReplaySubject<CorpusMC>(1);
-        textRangePage.corpusService.currentCorpus.next(new CorpusMC({citations: {4: new Citation()}}));
+        textRangePage.corpusService.currentCorpus.next({citations: {4: new Citation()}, source_urn: ''});
         textRangePage.checkInputDisabled().then(() => {
             textRangePage.isInputDisabled[0].pipe(take(1)).subscribe((isDisabled: boolean) => {
                 expect(isDisabled).toBe(true);
-                textRangePage.corpusService.currentCorpus.next(new CorpusMC({
+                textRangePage.corpusService.currentCorpus.next({
+                    source_urn: '',
                     citations: {1: new Citation({subcitations: {2: new Citation()}})}
-                }));
+                });
                 textRangePage.checkInputDisabled().then(() => {
                     textRangePage.isInputDisabled[0].pipe(take(1)).subscribe((isDisabled2: boolean) => {
                         expect(isDisabled2).toBe(false);
@@ -172,11 +174,12 @@ describe('TextRangePage', () => {
             }));
         }
 
-        const corpus: CorpusMC = new CorpusMC({
+        const corpus: CorpusMC = {
+            source_urn: '',
             citations: {4: new Citation()},
             citation_level_2: CitationLevel.default.toString(),
             citation_level_3: CitationLevel.default.toString(),
-        });
+        };
         textRangePage.corpusService.currentCorpus = new ReplaySubject<CorpusMC>(1);
         textRangePage.corpusService.currentCorpus.next(corpus);
         const citationLabels: string[] = ['1', '2', '3'];
@@ -246,10 +249,11 @@ describe('TextRangePage', () => {
         expectNavigationCalled(checkSpy, 0).then(async () => {
             textRangePage.isTextRangeCheckRunning = false;
             textRangePage.corpusService.currentCorpus = new ReplaySubject<CorpusMC>(1);
-            textRangePage.corpusService.currentCorpus.next(new CorpusMC({
+            textRangePage.corpusService.currentCorpus.next({
+                source_urn: '',
                 citations: {4: new Citation()},
                 citation_level_2: CitationLevel.default.toString()
-            }));
+            });
             textRangePage.corpusService.initCurrentTextRange();
             textRangePage.helperService.applicationState.next(textRangePage.helperService.deepCopy(MockMC.applicationState));
             const getTextSpy: Spy = spyOn(textRangePage.corpusService, 'getText').and.callFake(() => Promise.reject());
@@ -274,10 +278,10 @@ describe('TextRangePage', () => {
     it('should initialize the page', (done) => {
         textRangePage.corpusService.initCurrentTextRange();
         textRangePage.helperService.applicationState.next(textRangePage.helperService.deepCopy(MockMC.applicationState));
-        textRangePage.initPage(new CorpusMC({
-            citation_level_2: CitationLevel.default.toString(),
+        textRangePage.initPage({
+            source_urn: '', citation_level_2: CitationLevel.default.toString(),
             citations: {1: new Citation({label: '1'})}
-        })).then(() => {
+        }).then(() => {
             textRangePage.corpusService.currentTextRange.pipe(take(1)).subscribe((tr: TextRange) => {
                 expect(tr.start[0]).toBe('1');
                 done();
@@ -296,7 +300,7 @@ describe('TextRangePage', () => {
             });
         }
 
-        const corpus: CorpusMC = new CorpusMC({citations: {4: new Citation({subcitations: {}, value: 4})}});
+        const corpus: CorpusMC = {citations: {4: new Citation({subcitations: {}, value: 4})}, source_urn: ''};
         textRangePage.corpusService.currentCorpus = new ReplaySubject<CorpusMC>(1);
         textRangePage.corpusService.currentCorpus.next(corpus);
         const valueList: number[] = [];
@@ -340,13 +344,13 @@ describe('TextRangePage', () => {
         const addReferencesSpy: Spy = spyOn(textRangePage, 'addReferences').and.callFake(() => Promise.reject());
         const initPageSpy: Spy = spyOn(textRangePage, 'initPage').and.returnValue(Promise.resolve());
         textRangePage.corpusService.currentCorpus = new ReplaySubject<CorpusMC>(1);
-        textRangePage.corpusService.currentCorpus.next(new CorpusMC({citations: {}}));
+        textRangePage.corpusService.currentCorpus.next({citations: {}, source_urn: ''});
         textRangePage.ngOnInit().then(async () => {
             expect(initPageSpy).toHaveBeenCalledTimes(0);
             addReferencesSpy.and.returnValue(Promise.resolve());
             await textRangePage.ngOnInit();
             expect(initPageSpy).toHaveBeenCalledTimes(1);
-            textRangePage.corpusService.currentCorpus.next(new CorpusMC({citations: {1: new Citation()}}));
+            textRangePage.corpusService.currentCorpus.next({citations: {1: new Citation()}, source_urn: ''});
             await textRangePage.ngOnInit();
             expect(initPageSpy).toHaveBeenCalledTimes(2);
             done();
@@ -369,7 +373,7 @@ describe('TextRangePage', () => {
         textRangePage.corpusService.initCurrentTextRange();
         textRangePage.helperService.applicationState.next(textRangePage.helperService.deepCopy(MockMC.applicationState));
         textRangePage.corpusService.currentCorpus = new ReplaySubject<CorpusMC>(1);
-        textRangePage.corpusService.currentCorpus.next(new CorpusMC({citations: {1: new Citation()}}));
+        textRangePage.corpusService.currentCorpus.next({citations: {1: new Citation()}, source_urn: ''});
         textRangePage.resetCitations().then(() => {
             textRangePage.corpusService.currentTextRange.pipe(take(1)).subscribe((tr: TextRange) => {
                 expect(tr.start[1]).toBeTruthy();
@@ -402,19 +406,19 @@ describe('TextRangePage', () => {
         textRangePage.corpusService.initCurrentTextRange();
         textRangePage.helperService.applicationState.next(textRangePage.helperService.deepCopy(MockMC.applicationState));
         textRangePage.corpusService.currentCorpus = new ReplaySubject<CorpusMC>(1);
-        textRangePage.corpusService.currentCorpus.next(new CorpusMC({
-            citations: {2: new Citation({subcitations: {2: new Citation()}})}
-        }));
+        textRangePage.corpusService.currentCorpus.next({
+            citations: {2: new Citation({subcitations: {2: new Citation()}})}, source_urn: ''
+        });
         textRangePage.showFurtherReferences(true).then(async () => {
             expect(addReferencesSpy).toHaveBeenCalledTimes(1);
             textRangePage.corpusService.setCurrentTextRange(0, null, new TextRange({end: [''], start: ['']}));
             await textRangePage.showFurtherReferences(false);
             expect(addLvl3Spy).toHaveBeenCalledTimes(1);
             textRangePage.corpusService.setCurrentTextRange(0, null, new TextRange({end: ['2'], start: ['2']}));
-            textRangePage.corpusService.currentCorpus.next(new CorpusMC({
+            textRangePage.corpusService.currentCorpus.next({
                 citations: {2: new Citation({subcitations: {}})},
-                citation_level_2: CitationLevel.default.toString()
-            }));
+                citation_level_2: CitationLevel.default.toString(), source_urn: ''
+            });
             await textRangePage.showFurtherReferences(false);
             expect(addReferencesSpy).toHaveBeenCalledTimes(1);
             expect(addLvl3Spy).toHaveBeenCalledTimes(2);
diff --git a/mc_frontend/src/app/vocabulary-check/vocabulary-check.page.spec.ts b/mc_frontend/src/app/vocabulary-check/vocabulary-check.page.spec.ts
index 38357f9..aa63b07 100644
--- a/mc_frontend/src/app/vocabulary-check/vocabulary-check.page.spec.ts
+++ b/mc_frontend/src/app/vocabulary-check/vocabulary-check.page.spec.ts
@@ -10,7 +10,7 @@ import {FormsModule} from '@angular/forms';
 import {APP_BASE_HREF} from '@angular/common';
 import MockMC from '../models/mockMC';
 import Spy = jasmine.Spy;
-import {Sentence} from '../models/sentence';
+import {Sentence} from '../../../openapi';
 
 describe('VocabularyCheckPage', () => {
     let vocabularyCheckPage: VocabularyCheckPage;
@@ -76,7 +76,7 @@ describe('VocabularyCheckPage', () => {
 
     it('should process sentences', () => {
         vocabularyCheckPage.currentRankingUnits = [];
-        const sentences: Sentence[] = Array(50).fill(null).map(x => new Sentence({matching_degree: 40}));
+        const sentences: Sentence[] = Array(50).fill(null).map(x => ({matching_degree: 40}));
         sentences[0].matching_degree = 49;
         sentences[10].matching_degree = 50;
         sentences[14].matching_degree = 80;
diff --git a/mc_frontend/src/app/vocabulary-check/vocabulary-check.page.ts b/mc_frontend/src/app/vocabulary-check/vocabulary-check.page.ts
index f6d224d..d2a2a4f 100644
--- a/mc_frontend/src/app/vocabulary-check/vocabulary-check.page.ts
+++ b/mc_frontend/src/app/vocabulary-check/vocabulary-check.page.ts
@@ -3,7 +3,6 @@ import {Component} from '@angular/core';
 import {VocabularyCorpus, VocabularyCorpusTranslation} from 'src/app/models/enum';
 import {VocabularyService} from 'src/app/vocabulary.service';
 import {HttpClient, HttpErrorResponse} from '@angular/common/http';
-import {Sentence} from 'src/app/models/sentence';
 import {TranslateService} from '@ngx-translate/core';
 import {HelperService} from '../helper.service';
 import {ExerciseService} from 'src/app/exercise.service';
@@ -11,6 +10,7 @@ import {CorpusService} from 'src/app/corpus.service';
 import {CorpusMC} from '../models/corpusMC';
 import {take} from 'rxjs/operators';
 import {TextRange} from '../models/textRange';
+import {Sentence} from '../../../openapi';
 
 @Component({
     selector: 'app-vocabulary-check',
diff --git a/mc_frontend/src/app/vocabulary.service.spec.ts b/mc_frontend/src/app/vocabulary.service.spec.ts
index e0cef7a..dd2b370 100644
--- a/mc_frontend/src/app/vocabulary.service.spec.ts
+++ b/mc_frontend/src/app/vocabulary.service.spec.ts
@@ -5,9 +5,8 @@ import {HttpClientTestingModule} from '@angular/common/http/testing';
 import {IonicStorageModule} from '@ionic/storage';
 import {TranslateTestingModule} from './translate-testing/translate-testing.module';
 import {VocabularyCorpus} from './models/enum';
-import {Sentence} from './models/sentence';
 import {HttpErrorResponse} from '@angular/common/http';
-import {AnnisResponse} from '../../openapi';
+import {AnnisResponse, Sentence} from '../../openapi';
 import Spy = jasmine.Spy;
 
 describe('VocabularyService', () => {
@@ -27,7 +26,7 @@ describe('VocabularyService', () => {
 
     it('should be created', () => {
         expect(vocabularyService).toBeTruthy();
-        const sentences: Sentence[] = [new Sentence({matching_degree: 3}), new Sentence({matching_degree: 7})];
+        const sentences: Sentence[] = [{matching_degree: 3}, {matching_degree: 7}];
         expect(vocabularyService.getMean(sentences)).toBe(5);
     });
     it('should get a vocabulary check', (done) => {
diff --git a/mc_frontend/src/app/vocabulary.service.ts b/mc_frontend/src/app/vocabulary.service.ts
index 10ee971..6d588c0 100644
--- a/mc_frontend/src/app/vocabulary.service.ts
+++ b/mc_frontend/src/app/vocabulary.service.ts
@@ -3,12 +3,11 @@ import {HttpClient, HttpErrorResponse, HttpParams} from '@angular/common/http';
 import {Injectable, OnInit} from '@angular/core';
 import {VocabularyCorpus} from 'src/app/models/enum';
 import {Vocabulary} from 'src/app/models/vocabulary';
-import {Sentence} from 'src/app/models/sentence';
 import {HelperService} from 'src/app/helper.service';
 import {TestResultMC} from 'src/app/models/testResultMC';
 import {ToastController} from '@ionic/angular';
 import configMC from '../configMC';
-import {AnnisResponse, VocabularyForm} from '../../openapi';
+import {AnnisResponse, Sentence, VocabularyForm} from '../../openapi';
 import {VocabularyMC} from '../../openapi';
 
 @Injectable({
@@ -42,9 +41,7 @@ export class VocabularyService implements OnInit {
                 vocabulary: VocabularyMC[this.currentReferenceVocabulary]
             };
             const formData: FormData = new FormData();
-            Object.keys(vf).forEach((key: string) => {
-                formData.append(key, vf[key]);
-            });
+            Object.keys(vf).forEach((key: string) => formData.append(key, vf[key]));
             this.helperService.makePostRequest(this.http, this.toastCtrl, url, formData).then((result: AnnisResponse) => {
                 return resolve(result);
             }, (error: HttpErrorResponse) => {
diff --git a/mc_frontend/src/assets/i18n/de.json b/mc_frontend/src/assets/i18n/de.json
index 5e0233c..4f065a1 100644
--- a/mc_frontend/src/assets/i18n/de.json
+++ b/mc_frontend/src/assets/i18n/de.json
@@ -54,6 +54,7 @@
   "DEPENDENCY_PUNCTUATION": "Zeichensetzung",
   "DEPENDENCY_ROOT": "Wurzel",
   "DEPENDENCY_SUBJECT": "Subjekt",
+  "DEPENDENCY_UNSPECIFIED": "Unbekannt",
   "DEPENDENCY_VOCATIVE": "Vokativ",
   "DEPENDENT_WORD": "Abhängiges Wort",
   "DOCUMENTATION": "Dokumentation",
diff --git a/mc_frontend/src/assets/i18n/en.json b/mc_frontend/src/assets/i18n/en.json
index 0f49a53..7e3ad8d 100644
--- a/mc_frontend/src/assets/i18n/en.json
+++ b/mc_frontend/src/assets/i18n/en.json
@@ -54,6 +54,7 @@
   "DEPENDENCY_PUNCTUATION": "Punctuation",
   "DEPENDENCY_ROOT": "Root",
   "DEPENDENCY_SUBJECT": "Subject",
+  "DEPENDENCY_UNSPECIFIED": "Unknown",
   "DEPENDENCY_VOCATIVE": "Vocative",
   "DEPENDENT_WORD": "Base word",
   "DOCUMENTATION": "Documentation",
-- 
GitLab