diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
deleted file mode 100644
index 690d7794fec8401bbbe2cee1fe4516a5b198d5ce..0000000000000000000000000000000000000000
--- a/.gitlab-ci.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-stages:
- - test
-
-pytest:
-  stage: test
-  image: python:3.9
-  script:
-    - pip install -r requirements.txt
-    - pytest
\ No newline at end of file
diff --git a/Makefile b/Makefile
index dc8068a75ca0ca15d0be5ab79572fd89e34f403b..0ee469108d4d00429d5f07d1919031fa752a87bb 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,10 @@
 out = clean_data
 
 clean:
-	@$(RM) -rf ./$(out)/*
\ No newline at end of file
+	@$(RM) -rf ./$(out)/*
+
+streamlit:
+	streamlit run src/dashboard/dashboard.py
+
+docker_rebuild: 
+	cd ./src/db && docker compose up -d --build
\ No newline at end of file
diff --git a/src/assets/table_template.html b/src/assets/table_template.html
new file mode 100644
index 0000000000000000000000000000000000000000..5d8a1354daff431f419f38d701cc86755fe7e774
--- /dev/null
+++ b/src/assets/table_template.html
@@ -0,0 +1,170 @@
+<style>
+	table {
+		border-collapse: collapse;
+		width: 100%;
+	}
+
+	td, th {
+		border: 1px solid black;
+		padding: 8px;
+	}
+
+	th {
+		background: rgba(91,91,91, 0.4)
+	}
+
+	.indicator {
+		font-weight: bold;
+		font-size: large;
+	}
+
+	.cell {
+		display: flex;
+		align-items: center;
+		justify-content: space-around;
+	}
+	.arrow-container {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: space-around;
+	}
+
+	.arrow-indicator {
+		font-size: small;
+	}
+</style>
+<table>
+	<tr>
+		<th>Institut/Typ</th>
+		<th>Vorlesungen</th>
+		<th>Seminare, SWP</th>
+		<th>Übungen/Tutorien</th>
+	</tr>
+	<tr>
+		<th>Bioinformatik</th>
+		<td>
+			<div class="cell">
+				<span class="indicator">%BIO-VL%</span>
+				<div class="arrow-container">
+					<div class="arrow" style="transform: rotate(%BIO-VL-ROT%deg)">
+						<svg xmlns="http://www.w3.org/2000/svg" width="48" height="24" viewBox="0 0 24 12" fill="%BIO-VL-ARROW%">
+							<path d="M17.8356 4.67433H0.35791L0.35791 7.32568L17.8356 7.32568V11.3027L23.6421 6L17.8356 0.697296V4.67433Z"/>
+						</svg>
+					</div>
+					<span class="arrow-indicator">%BIO-VL-IND%</span>
+				</div>
+			</div>
+		</td>
+		<td>
+			<div class="cell">
+				<span class="indicator">%BIO-S%</span>
+				<div class="arrow-container">
+					<div class="arrow" style="transform: rotate(%BIO-S-ROT%deg)">
+						<svg xmlns="http://www.w3.org/2000/svg" width="48" height="24" viewBox="0 0 24 12" fill="%BIO-S-ARROW%">
+							<path d="M17.8356 4.67433H0.35791L0.35791 7.32568L17.8356 7.32568V11.3027L23.6421 6L17.8356 0.697296V4.67433Z"/>
+						</svg>
+					</div>
+					<span class="arrow-indicator">%BIO-S-IND%</span>
+				</div>
+			</div>
+		</td>
+		<td>
+			<div class="cell">
+				<span class="indicator">%BIO-U%</span>
+				<div class="arrow-container">
+					<div class="arrow" style="transform: rotate(%BIO-U-ROT%deg)">
+						<svg xmlns="http://www.w3.org/2000/svg" width="48" height="24" viewBox="0 0 24 12" fill="%BIO-U-ARROW%">
+							<path d="M17.8356 4.67433H0.35791L0.35791 7.32568L17.8356 7.32568V11.3027L23.6421 6L17.8356 0.697296V4.67433Z"/>
+						</svg>
+					</div>
+					<span class="arrow-indicator">%BIO-U-IND%</span>
+				</div>
+			</div>
+		</td>
+	</tr>
+	<tr>
+		<th>Informatik</th>
+		<td>
+			<div class="cell">
+				<span class="indicator">%CS-VL%</span>
+				<div class="arrow-container">
+					<div class="arrow" style="transform: rotate(%CS-VL-ROT%deg)">
+						<svg xmlns="http://www.w3.org/2000/svg" width="48" height="24" viewBox="0 0 24 12" fill="%CS-VL-ARROW%">
+							<path d="M17.8356 4.67433H0.35791L0.35791 7.32568L17.8356 7.32568V11.3027L23.6421 6L17.8356 0.697296V4.67433Z"/>
+						</svg>
+					</div>
+					<span class="arrow-indicator">%CS-VL-IND%</span>
+				</div>
+			</div>
+		</td>
+		<td>
+			<div class="cell">
+				<span class="indicator">%CS-S%</span>
+				<div class="arrow-container">
+					<div class="arrow" style="transform: rotate(%CS-S-ROT%deg)">
+						<svg xmlns="http://www.w3.org/2000/svg" width="48" height="24" viewBox="0 0 24 12" fill="%CS-S-ARROW%">
+							<path d="M17.8356 4.67433H0.35791L0.35791 7.32568L17.8356 7.32568V11.3027L23.6421 6L17.8356 0.697296V4.67433Z"/>
+						</svg>
+					</div>
+					<span class="arrow-indicator">%CS-S-IND%</span>
+				</div>
+			</div>
+		</td>
+		<td>
+			<div class="cell">
+				<span class="indicator">%CS-U%</span>
+				<div class="arrow-container">
+					<div class="arrow" style="transform: rotate(%CS-U-ROT%deg)">
+						<svg xmlns="http://www.w3.org/2000/svg" width="48" height="24" viewBox="0 0 24 12" fill="%CS-U-ARROW%">
+							<path d="M17.8356 4.67433H0.35791L0.35791 7.32568L17.8356 7.32568V11.3027L23.6421 6L17.8356 0.697296V4.67433Z"/>
+						</svg>
+					</div>
+					<span class="arrow-indicator">%CS-U-IND%</span>
+				</div>
+			</div>
+		</td>
+	</tr>
+	<tr>
+		<th>Mathematik</th>
+		<td>
+			<div class="cell">
+				<span class="indicator">%MATH-VL%</span>
+				<div class="arrow-container">
+					<div class="arrow" style="transform: rotate(%MATH-VL-ROT%deg)">
+						<svg xmlns="http://www.w3.org/2000/svg" width="48" height="24" viewBox="0 0 24 12" fill="%MATH-VL-ARROW%">
+							<path d="M17.8356 4.67433H0.35791L0.35791 7.32568L17.8356 7.32568V11.3027L23.6421 6L17.8356 0.697296V4.67433Z"/>
+						</svg>
+					</div>
+					<span class="arrow-indicator">%MATH-VL-IND%</span>
+				</div>
+			</div>
+		</td>
+		<td>
+			<div class="cell">
+				<span class="indicator">%MATH-S%</span>
+				<div class="arrow-container">
+					<div class="arrow" style="transform: rotate(%MATH-S-ROT%deg)">
+						<svg xmlns="http://www.w3.org/2000/svg" width="48" height="24" viewBox="0 0 24 12" fill="%MATH-S-ARROW%">
+							<path d="M17.8356 4.67433H0.35791L0.35791 7.32568L17.8356 7.32568V11.3027L23.6421 6L17.8356 0.697296V4.67433Z"/>
+						</svg>
+					</div>
+					<span class="arrow-indicator">%MATH-S-IND%</span>
+				</div>
+			</div>
+		</td>
+		<td>
+			<div class="cell">
+				<span class="indicator">%MATH-U%</span>
+				<div class="arrow-container">
+					<div class="arrow" style="transform: rotate(%MATH-U-ROT%deg)">
+						<svg xmlns="http://www.w3.org/2000/svg" width="48" height="24" viewBox="0 0 24 12" fill="%MATH-U-ARROW%">
+							<path d="M17.8356 4.67433H0.35791L0.35791 7.32568L17.8356 7.32568V11.3027L23.6421 6L17.8356 0.697296V4.67433Z"/>
+						</svg>
+					</div>
+					<span class="arrow-indicator">%MATH-U-IND%</span>
+				</div>
+			</div>
+		</td>
+	</tr>
+</table>
\ No newline at end of file
diff --git a/src/cleanup.ipynb b/src/cleanup.ipynb
index f18fd073e3c2e818b63b14f804ef9307a99015c2..0e8a6c6d0aedddd348895a4116e89cc01b98c27b 100644
--- a/src/cleanup.ipynb
+++ b/src/cleanup.ipynb
@@ -10,19 +10,19 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 425,
+   "execution_count": 1,
    "metadata": {},
    "outputs": [],
    "source": [
     "# Imports and constants\n",
-    "import pandas, os, re, requests, json, math, numbers\n",
-    "from lib.scraping import get_course, get_complete_catalogue_for\n",
+    "import pandas, os, re, math, numbers\n",
+    "import lib.api as api\n",
     "import lib.constants as CONSTANTS"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 426,
+   "execution_count": 2,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -46,10 +46,6 @@
     "\n",
     "\treturn plural_list.get(category, category)\n",
     "\n",
-    "def request_questions():\n",
-    "\tres = requests.get(\"http://localhost:50000/v1/questions\")\n",
-    "\treturn json.loads(res.text)\n",
-    "\n",
     "def choose(a,b):\n",
     "\tif type(a) == str:\n",
     "\t\treturn b if a == \"\" else a\n",
@@ -69,7 +65,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 427,
+   "execution_count": 3,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -90,14 +86,14 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 428,
+   "execution_count": 4,
    "metadata": {},
    "outputs": [],
    "source": [
     "to_remove = [\"language\", \"Zulassungsbeschr.\", \"Bearbeitungsstand\", \"zeit\", \"Ausfülldauer\", \"Ausfülldauer (s)\"]\n",
     "multiples = [\"Was hat Ihnen an dieser Lehrveranstaltung gut gefallen?\", \"Was könnte der Dozent/die Dozentin an dieser Lehrveranstaltung verbessern?\"]\n",
     "\n",
-    "# Easier handling for doubles columns\n",
+    "# Easier handling for duplicated columns\n",
     "for i in range(1, 8):\n",
     "\tfor j in multiples:\n",
     "\t\tto_remove.append(j + \".\" + str(i))\n",
@@ -120,7 +116,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 429,
+   "execution_count": 5,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -143,7 +139,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 430,
+   "execution_count": 6,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -179,7 +175,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 431,
+   "execution_count": 7,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -187,6 +183,32 @@
     "\tdf[\"Veranstaltung-Nr.\"] = df[\"Veranstaltung-Nr.\"].map(lambda x: str(x).strip())"
    ]
   },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Fix Column Names\n",
+    "Sometimes questions contain a whitespace, which causes pandas and the cleanup script to handle them as different questions"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "for (path, df) in paths:\n",
+    "\tfor name in df.columns:\n",
+    "\t\tif re.compile(\"\\s$\").search(name):\n",
+    "\t\t\t# KeyError means that the question simply has a whitespace at the end without having a duplicate\n",
+    "\t\t\ttry:\n",
+    "\t\t\t\tdf[name.strip()]\n",
+    "\t\t\t\t# Pandas starts marking duplicates with from index 1, zero will always be free\n",
+    "\t\t\t\tdf.rename(columns={name: name.strip() + \".0\"}, inplace=True)\n",
+    "\t\t\texcept KeyError:\n",
+    "\t\t\t\tdf.rename(columns={name: name.strip()}, inplace=True)"
+   ]
+  },
   {
    "cell_type": "markdown",
    "metadata": {},
@@ -197,7 +219,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 432,
+   "execution_count": 9,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -223,11 +245,11 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 433,
+   "execution_count": 10,
    "metadata": {},
    "outputs": [],
    "source": [
-    "questions = request_questions()\n",
+    "questions = api.request_questions()\n",
     "not_indexable = []\n",
     "for (path, df) in paths:\n",
     "\n",
@@ -254,7 +276,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 434,
+   "execution_count": 11,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -285,8 +307,36 @@
     "\t\trest.to_csv(os.path.join(semester_path, \"rest.csv\"), encoding=\"utf-8\", index=False)\n",
     "\n",
     "\t# If everything needs to be handled at once\n",
-    "\tdf.to_csv(os.path.join(semester_path, \"all.csv\"), encoding=\"utf-8\", index=False)\n",
-    "\n"
+    "\tdf.to_csv(os.path.join(semester_path, \"all.csv\"), encoding=\"utf-8\", index=False)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Participation Cleanup\n",
+    "Names in the participation files are written as `Surname, Name` instead of `Name Surname` as in the evaluation files. This prevents the data from merging, since the pipeline treats the name columns as if they were different"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "for file in os.listdir(os.path.join(CONSTANTS.RAW_DATA_PATH, \"participation\")):\n",
+    "\tif not file.endswith(\".csv\") or \"example\" in file: continue\n",
+    "\n",
+    "\tdf = pandas.read_csv(os.path.join(CONSTANTS.RAW_DATA_PATH, \"participation\", file), sep=\";\")\n",
+    "\tnames = df[\"Person-Name\"]\n",
+    "\t# Join split and reversed name with a whitespace if it contains a comma\n",
+    "\tdf[\"Person-Name\"] = names.map(lambda x: \" \".join(x.split(\", \")[::-1]) if ', ' in x else x)\n",
+    "\n",
+    "\tif not os.path.isdir(os.path.join(CONSTANTS.CLEAN_DATA_PATH, \"participation\")):\n",
+    "\t\tos.mkdir(os.path.join(CONSTANTS.CLEAN_DATA_PATH, \"participation\"))\n",
+    "\n",
+    "\tout_path = os.path.join(CONSTANTS.CLEAN_DATA_PATH, \"participation\", file)\n",
+    "\tdf.to_csv(out_path, encoding=\"utf-8\", index=False)"
    ]
   }
  ],
diff --git a/src/dashboard/dashboard.py b/src/dashboard/dashboard.py
new file mode 100644
index 0000000000000000000000000000000000000000..5d79e69b3ea10eca58e935d9dfc288ce0e2d8828
--- /dev/null
+++ b/src/dashboard/dashboard.py
@@ -0,0 +1,565 @@
+import streamlit as st
+import pandas as pd
+import altair as alt
+
+import os, re, sys
+
+from functools import cmp_to_key
+
+
+# Allows access to lib folder
+path = os.path.abspath(__file__)
+steps = 2
+for i in range(steps):
+	path = os.path.dirname(path)
+
+sys.path.append(path)
+
+import lib.api as api
+import lib.constants as CONSTANTS
+
+# TODO: Institute Colors
+
+## Globals
+# Allows generation of streamlit interaction elements with unique IDs
+global curr_key
+curr_key = 0
+
+# Value cache
+value_cache = {}
+
+uuid_regex = re.compile('\\b[0-9a-f]{8}\\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\\b[0-9a-f]{12}\\b')
+
+dimensions = {
+	"A": "Vermittlung von Wissen und Unterstützen von Verstehen",
+	"B": "Motivieren und lerndienliche Atmosphäre herstellen",
+	"C": "Steuerung und Interaktion in der Lerngruppe"
+}
+
+
+map = {
+	"cs": "Informatik",
+	"bio": "Bioinformatik",
+	"math": "Mathematik"
+}
+
+## Functions
+
+def api_response_to_dataframe_compatibility(response):
+	res = {}
+	for course in response:
+		course_obj = {
+			"Veranstaltungsnummer": course["course_number"],
+			"Veranstaltungsname": course["course_name"],
+			"Veranstaltungstyp": course["course_type"],
+			"Lehrperson": course["lecturer"],
+			"Semester": course["semester"],
+			"Institut": map[course["institute"]],
+			"Antworten": course["answers"],
+			"Teilnehmende": course["participants"]
+		}
+
+		for question in course["scores"]:
+			course_obj[question["question"]] = question["score"]
+		res[course["id"]] = course_obj
+	return res
+
+def get_interval(percentage):
+	initial = [pd.Interval(left=0, right=(1/7), closed="both")]
+	if percentage in initial: return 1
+	left = (1 / 7)
+	for i in range(2, 8):
+		interval = pd.Interval(left=left, right=left + (1 / 7), closed="right")
+		if percentage in interval: return i
+		left += (1 / 7)
+
+def cache_question(question):
+	scale = api.get_scale_for(question)
+	dimension = api.get_dimension_for(question)["id"]
+	if dimension == None:
+		dimension = "no_dim"
+	possibilities = api.get_answers_for(scale["id"])
+	weights = [answer["value"] for answer in possibilities]
+	value_cache[question] = {
+		"value": max(weights),
+		"dimension": dimension
+	}
+
+def convert_scores(scores):
+	result = {}
+	for course in scores:
+		id = course["id"]
+		dimensions = []
+		result[id] = {
+			"Typ": course["course_type"],
+			"Institut": map[course["institute"]],
+			"Semester": course["semester"],
+		}
+
+		maximum = {}
+
+		for score in course["scores"]:
+			question = score["question"]
+
+			if question not in value_cache:
+				cache_question(question)
+			dimension = value_cache[question]["dimension"]
+
+			# Creates initial values
+			if dimension not in result[id]:
+				result[id][dimension] = 0
+				dimensions.append(dimension)
+			if dimension not in maximum:
+				maximum[dimension] = 0
+
+			result[id][dimension] += score["score"]
+			maximum[dimension] += value_cache[question]["value"] * course["answers"]
+
+		# All over score
+		for dimension in dimensions:
+			if dimension.endswith("1"):
+				counterpart = dimension.replace("1", "2")
+				if counterpart in dimensions: 
+					combined_score = result[id][dimension] + result[id][counterpart]
+					combined_maximum = maximum[dimension] + maximum[counterpart]
+					result[id][dimension[0]] = round(((combined_score + combined_maximum) / (combined_maximum * 2)), 2)
+				else:
+					result[id][dimension[0]] = round(((result[id][dimension] + maximum[dimension]) / (maximum[dimension] * 2)), 2)
+			elif dimension.endswith("2") and dimension.replace("2", "1") not in dimensions:
+				result[id][dimension[0]] = round(((result[id][dimension] + maximum[dimension]) / (maximum[dimension] * 2)), 2)
+
+			# Dimension was not evaluated
+			if maximum[dimension] == 0 and result[id][dimension] == 0:
+				continue
+			# Maximum and minimum possible Score based on answers numbers
+			# Moves all values into positive integers, simplifying percentage calculation
+			result[id][dimension] = round(((result[id][dimension] + maximum[dimension]) / (maximum[dimension] * 2)), 2)
+				
+	return result
+
+def percentages_to_interval(scores):
+	res = {}
+	dimensions = ["A", "A1", "A2", "B", "B1", "B2", "C", "C1", "C2"]
+	for course in scores:
+		for dimension in dimensions:
+			updated = scores[course]
+			updated[dimension] = get_interval(updated[dimension])
+			res[course] = updated
+	return res
+
+def compare_semesters(semester1, semester2):
+	if semester1 == semester2:
+		return 0
+	
+
+	year1 = int(semester1[4:8])
+	year2 = int(semester2[4:8])
+
+	if year1 == year2:
+		return 1 if semester1.startswith("Wi") else -1
+	elif year1 > year2:
+		return 1
+	else:
+		return -1
+
+def get_min_max_semester(df: pd.DataFrame):
+	semester_list = df["Semester"].to_list()
+	semester_sorted = sorted(semester_list, key=cmp_to_key(compare_semesters))
+	# Oldest, Newsest
+	return (semester_sorted[0], semester_sorted[-1])
+
+def generate_semester_interval(left, right):
+	interval = []
+	start_year = int(left[4:8])
+	gen_sose = left.startswith("SoSe")
+	year = start_year
+	semester = f"SoSe{str(start_year)}" if gen_sose else f"WiSe{str(start_year)}/{str(start_year + 1)[2:4]}"
+	while semester != right:
+		interval.append(semester)
+		gen_sose = not gen_sose
+		if gen_sose:
+			year += 1
+		semester = f"SoSe{str(year)}" if gen_sose else f"WiSe{str(year)}/{str(year + 1)[2:4]}"
+	interval.append(semester)
+
+	return interval
+
+# Genrate initial values
+
+# Used by graphs with no names
+scores = pd.DataFrame(convert_scores(api.get_all_scores())).T
+
+# For graphs with full names
+full_dataset = pd.DataFrame(api_response_to_dataframe_compatibility(api.get_all_scores())).T
+
+
+# Basic Page config
+st.set_page_config(
+	page_title="Evaluationsbericht",
+	layout="wide"
+)
+
+with st.sidebar:
+	st.header("Inhaltsverzeichnis")
+	st.markdown('''
+		- [Scatter Plot](#scatter-plot)
+		- [Boxplot](#boxplot)
+		- [Violinplot](#violinplot)
+		- [Linechart](#linechart)
+		- [Barchart](#barchart)
+		- [Tabelle](#tabelle)
+	''')
+
+st.title("Evaluationsbericht Grafiken")
+with st.expander(":orange[ :warning: Achtung!]", expanded=True):
+	st.write('''
+		Dieses Dashboard ist nur ein triviales Beispiel dafür, wie die Daten die während der Pipeline generiert werden, auf einem Dashboard angezeigt werden können. Dieses Dahboard ist keineswegs ein komplett fertiges Produkt, funktioniert aber minimal genug für die Demonstration.
+	''')
+present_mode = st.toggle("Datenschutz Modus (Versteckt Abschnitte mit Personenbezogenen Daten)")
+
+if not present_mode:
+	st.sidebar.markdown("- [Ranking](#ranking)")
+# If both is chosen, append scale for every semester
+semester_select = st.multiselect("Generierung für folgende Semester:", ["Sommersemester", "Wintersemester"], ["Sommersemester"])
+
+def generate_plot_section__with_subdimensions(title, markdown, chart):
+	global curr_key
+	st.header(title, divider="gray")
+	with st.expander(":information_source: Hinweis", expanded=True):
+		st.write(markdown)
+	column_multiselect, column_slider = st.columns(2)
+
+	with column_multiselect:
+		selected_dimensions = st.multiselect(key=curr_key, label="Dimensionen, die generiert werden sollen",options=["A", "B", "C"], default=["A", "B", "C"])
+		curr_key += 1
+	with column_slider:
+		min_semester, max_semester = get_min_max_semester(chart.data)
+		interval = generate_semester_interval(min_semester, max_semester)
+		interval = [sem for sem in interval if (sem.startswith("So") and "Sommersemester" in semester_select) or (sem.startswith("Wi") and "Wintersemester" in semester_select)]
+
+		semester_start, semester_end = st.select_slider(key=curr_key,
+			label="Generation für Semester zwischen",
+			options=interval,
+			value=(interval[0], interval[-1])
+		)
+		curr_key += 1
+		semester_range = interval[interval.index(semester_start):interval.index(semester_end) + 1]
+
+	chart_columns = st.columns(len(selected_dimensions))
+
+	for (i, dimension) in enumerate(selected_dimensions):
+		with chart_columns[i]:
+			chart_copy = (chart
+								 .transform_filter(alt.FieldOneOfPredicate(field="Semester", oneOf=semester_range))
+								 .encode(
+									 x=alt.X(f"{dimension}1").scale(domain=(.20,1.05)).axis(format="%"),
+									 y=alt.Y(f"{dimension}2").scale(domain=(0,1.05)).axis(format="%"),
+									 tooltip=[alt.Tooltip(f"{dimension}1", format=".2%"), alt.Tooltip(f"{dimension}2", format=".2%")]
+								 )
+								 .properties(
+									 title=f"Dimension {dimension} - {dimensions[dimension]}"
+								 )
+								)
+			st.altair_chart(chart_copy, use_container_width=True)
+
+
+def generate_section_dimensions(title, markdown, chart, type):
+	global curr_key
+	st.header(title, divider="gray")
+	with st.expander(":information_source: Hinweis", expanded=True):
+		st.write(markdown)
+	column_multiselect, column_slider, column_institute = st.columns(3)
+
+	with column_multiselect:
+		selected_dimensions = st.multiselect(key=curr_key, label="Dimensionen, die generiert werden sollen",options=["A", "B", "C"], default=["A", "B", "C"])
+		curr_key += 1
+	with column_slider:
+		min_semester, max_semester = get_min_max_semester(chart.data)
+		interval = generate_semester_interval(min_semester, max_semester)
+		interval = [sem for sem in interval if (sem.startswith("So") and "Sommersemester" in semester_select) or (sem.startswith("Wi") and "Wintersemester" in semester_select)]
+		semester_start, semester_end = st.select_slider(key=curr_key,
+			label="Generation für Semester zwischen",
+			options=interval,
+			value=(interval[0], interval[-1])
+		)
+		curr_key += 1
+		semester_range = interval[interval.index(semester_start):interval.index(semester_end) + 1]
+
+	with column_institute:
+		selected_institutes = st.multiselect(key=curr_key, label="Generieren für die Institute:", options=["Bioinformatik", "Informatik", "Mathematik"], default=["Bioinformatik", "Informatik", "Mathematik"])
+		curr_key += 1
+	for dimension in selected_dimensions:
+		st.subheader("Dimension {0}".format(dimension))
+		if type == "box":
+			smallest = pd.to_numeric(chart.data[dimension]).nsmallest(1).values[0]
+			largest = pd.to_numeric(chart.data[dimension]).nlargest(1).values[0]
+			chart_dim = chart.encode(y=alt.Y(dimension).scale(domain=(smallest, largest)).title("Bewertung"))
+
+			columns = st.columns(len(selected_institutes))
+			for (i, institute) in enumerate(selected_institutes):
+				chart_inst = chart_dim.transform_filter({
+					"and": [
+						alt.FieldOneOfPredicate(field="Semester", oneOf=semester_range),
+						alt.FieldEqualPredicate(field="Institut", equal=institute)
+					]
+				})
+				with columns[i]:
+					st.altair_chart(chart_inst, use_container_width=True)
+
+		elif type == "violin":
+			smallest = pd.to_numeric(chart.data[dimension]).nsmallest(1).values[0]
+			largest = pd.to_numeric(chart.data[dimension]).nlargest(1).values[0]
+			chart_dim = chart.transform_density(
+    		dimension,
+    		as_=[dimension, 'density'],
+				groupby=["Semester", "Dimension", "Institut"],
+				extent=[0, 7]
+			).mark_area(orient="horizontal").encode(
+    		x=alt.X("density:Q").stack("center").impute(None).title(None).axis(labels=False, values=[0], grid=False, ticks=True),
+    		y=alt.Y('{0}:Q'.format(dimension)).scale(domain=(int(smallest - 1), int(largest + 1))).title("Bewertung"),
+				tooltip=[alt.Tooltip("density:Q", title="Dichte", format=".2")],
+				color=alt.Color("Semester").legend(None),
+				# width 1000 / len ensures that every facet is 200 in width
+			).properties(width=1000 / len(semester_range)).facet(column="Semester", row="Institut")
+	
+			chart_inst = chart_dim.transform_filter({
+				"and": [
+					alt.FieldOneOfPredicate(field="Semester", oneOf=semester_range),
+					alt.FieldOneOfPredicate(field="Institut", oneOf=selected_institutes)
+				]
+			})
+			st.altair_chart(chart_inst)
+
+def generate_semester_slider_with_chart(title, markdown, chart):
+	global curr_key
+	st.header(title, divider="gray")
+	with st.expander(":information_source: Hinweis", expanded=True):
+		st.write(markdown)
+	
+	min_semester, max_semester = get_min_max_semester(chart.data)
+	interval = generate_semester_interval(min_semester, max_semester)
+	interval = [sem for sem in interval if (sem.startswith("So") and "Sommersemester" in semester_select) or (sem.startswith("Wi") and "Wintersemester" in semester_select)]
+	semester_start, semester_end = st.select_slider(key=curr_key,
+		label="Generation für Semester zwischen",
+		options=interval,
+		value=(interval[0], interval[-1])
+	)
+	curr_key += 1
+	semester_range = interval[interval.index(semester_start):interval.index(semester_end) + 1]
+
+	chart = chart.transform_filter(alt.FieldOneOfPredicate(field="Semester", oneOf=semester_range))
+
+	st.altair_chart(chart, use_container_width=True)
+
+# Scatterplot
+selection = alt.selection_point(fields=["Typ"], bind="legend")
+c = (alt.Chart(scores)
+	 		.mark_circle(size=100)
+			.encode(					
+				color=alt.Color("Typ").legend(orient="bottom"),
+				opacity=alt.condition(selection, alt.value(1.0), alt.value(0.1))
+			)
+			.interactive()
+			.add_params(
+				selection
+			)
+		)
+generate_plot_section__with_subdimensions("Scatter Plot", "Dieser Graph zeigt die Verteilung der Lehrveranstaltungen entlang der entsprechenden Unterdimensionen. Die abgegebenen Punkte werden für jede Frage in der dazugehörigen Dimension addiert und dann mit der maximal erreichbaren Punktzahl verrechnet um den Prozentsatz zu ermitteln. Jeder Punkt repräsentiert eine Lehrveranstaltung.", c)
+
+dimension_explanation_columns = st.columns(3)
+
+dimension_explanation = api.get_dimension_description()
+
+for j in range(2):
+	for (i, dimension) in  enumerate(["A", "B", "C"]):
+		subdimension = "{0}{1}".format(dimension, str(j + 1))
+		desc = [dim for dim in dimension_explanation if subdimension in dim][0]
+		with dimension_explanation_columns[i]:
+			st.markdown(''' 
+									### Dimension {0}
+							 {1}
+									'''.format(subdimension, desc[subdimension]["de"]))
+			
+
+## Boxplot
+box = pd.DataFrame(percentages_to_interval(scores.T.to_dict())).T
+c = (alt.Chart(box)
+	 		.mark_boxplot(extent='min-max')
+			.encode(
+				x="Semester", 
+				color=alt.Color("Institut").legend(orient="bottom"),
+			)
+			.interactive()
+		)
+generate_section_dimensions("Boxplot", "Dieser Graph zeigt für die Dimensionen des Fragebogens die historische Entwicklung der Fachbereiche. Jeweils eine Spalte bildet einen Fachbereich ab. Die Bewertung entsteht durch aufsummieren aller Studierendenantworten in der jeweiligen Dimension und anschließende Umrechnung in einen Prozentwert mithilfe der maximal erreichbaren Punktzahl. Danach wird der Wert in ein Intervall ähnlich zur Likert Scale von 1-7 eingeordnet. Die Boxen erstecken sich von den unteren 25% bis zu den oberen 75% der Antworten. Alle davor und danach bilden die Schnurrhaare der Box. Der Median ist mit einem Querstrich hervorgehoben.", c, "box")
+
+# Violin Plots
+c = (alt.Chart(box).encode(tooltip="Institut").interactive())
+
+generate_section_dimensions("Violinplot", "Dieser Graph zeigt für die Dimensionen des Fragebogens die historische Entwicklung der Fachbereiche. Jeweils eine Spalte bildet einen Fachbereich ab. Die Bewertung entsteht durch aufsummieren aller Studierendenantworten in der jeweiligen Dimension und anschließende Umrechnung in einen Prozentwert mithilfe der maximal erreichbaren Punktzahl. Danach wird der Wert in ein Intervall ähnlich zur Likert Scale von 1-7 eingeordnet. Die Violine erstreckt sich über alle Antworten. Die Breite an den Bewertungen geben an, wie oft diese Bewertung vorkam. Je dicker die Violine an einem Punkt, desto öfter kam die Bewertung vor.", c, "violin")
+
+
+## Line chart
+groups = full_dataset.groupby(by=["Semester", "Institut"])
+results = {
+	"Semester": [],
+	"Institut": [],
+	"Beteiligung": []
+}
+for (header, group) in groups:
+	percentage_value = group["Antworten"].sum()
+	# TODO: TELL ABOUT THE EXCEPTION with - 1
+	basic_value = group[group["Teilnehmende"] > 0]["Teilnehmende"].sum()
+	# TODO: write about how this happens:
+	# (empty dataframe due to missing data)
+	if basic_value <= 0:
+		continue
+	results["Semester"].append(header[0])
+	results["Institut"].append(header[1])
+	results["Beteiligung"].append(round((percentage_value / basic_value), 2))
+participation_data = pd.DataFrame(results)
+linechart = (
+	alt.Chart(participation_data)
+		.mark_line(point=True)
+		.encode(x="Semester", y=alt.Y("Beteiligung").axis(format="%"), color="Institut")
+		.interactive()
+)
+
+generate_semester_slider_with_chart("Linechart", "Dieser Graph zeigt den Verlauf der Beteiligung der Studierenden an den Fachbereichen. Die Punkte auf dem Graph sind feste Werte, die nach der Erhebung der Evaluation berechnet werden können, die Verbindungslinien stellen keinen zeitlichen verlauf da, sondern vereinfachen die Visualisierung des An- oder Abstiegs.", linechart)
+
+barchart = (
+	alt.Chart(participation_data)
+	.mark_bar()
+	.encode(x="Semester", y=alt.Y("Beteiligung").axis(format="%"), color="Institut", xOffset="Institut")
+	.interactive()
+)
+
+generate_semester_slider_with_chart("Barchart", "Dieser Graph zeigt die historische Beteiligung an Lehrevaluationen im direkten Vergleich der Fachbereiche pro Semester an.", barchart)
+
+# Table
+st.header("Tabelle", divider="gray")
+with st.expander(":information_source: Hinweis", expanded=True):
+	st.write('''
+		Diese Tabelle zeigt den historischen Verlauf der Beteiligung an Lehrevaluationen über alle Semester (unabhängig vom Filter ganz oben auf der Seite).
+		Eine Zelle besteht aus dem aktuellen Beteiligungswert (links) und wie er im historischen Verlauf seit Datenbeginn sich verändert hat (recht).
+		Die Richtung des Pfeils indiziert das Wachstum, die Prozentzahl darunter zeigt es genau an.
+		Die Farbe des Pfeils zeigt an wie die Entwicklung einzuschätzen ist, nach einem Ampelsystem.
+	''')
+template = ""
+
+# This has as precondition that the code is run by the makefile
+with open(os.path.join("src", "assets", "table_template.html"), "r") as file:
+ template = file.read()
+
+
+groups = full_dataset.groupby(by=["Semester", "Institut", "Veranstaltungstyp"])
+
+results = {
+	"Semester": [],
+	"Institut": [],
+	"Veranstaltungstyp": [],
+	"Beteiligung": []
+}
+for (header, group) in groups:
+	percentage_value = group["Antworten"].sum()
+	# TODO: TELL ABOUT THE EXCEPTION with - 1
+	basic_value = group[group["Teilnehmende"] > 0]["Teilnehmende"].sum()
+	# TODO: write how this happens:
+	# (empty dataframe due to missing data)
+	if basic_value <= 0:
+		continue
+	results["Semester"].append(header[0])
+	results["Institut"].append(header[1])
+	results["Veranstaltungstyp"].append(header[2])
+	results["Beteiligung"].append(round((percentage_value / basic_value) * 100, 2))
+participation_data = pd.DataFrame(results)
+
+for inst in ["Bioinformatik", "Informatik", "Mathematik"]:
+	inst_df = participation_data[participation_data["Institut"] == inst]
+
+	semester_list = inst_df["Semester"].to_list()
+	oldest_semester = sorted(semester_list, key=cmp_to_key(compare_semesters))[0]
+	newest_semester = sorted(semester_list, key=cmp_to_key(compare_semesters))[-1]
+
+	for category in ["Vorlesungen", "Seminare", "Übungen"]:
+		oldest_participation = inst_df[(inst_df["Semester"] == oldest_semester) & (inst_df["Veranstaltungstyp"] == category)]["Beteiligung"].values[0]
+		newest_participation = inst_df[(inst_df["Semester"] == newest_semester) & (inst_df["Veranstaltungstyp"] == category)]["Beteiligung"].values[0]
+		difference = round(newest_participation - oldest_participation, 2)
+
+		# -difference ensures correct rotation
+		rotation = round(90 * (-difference / 100),2)
+
+		color = "yellow"
+
+		if difference in pd.Interval(left=5, right=100, closed="both"):
+			color = "green"
+		elif difference in pd.Interval(left=-100, right=-5, closed="both"):
+			color = "red"
+
+		cat_map = {
+			"Vorlesungen": "VL",
+			"Seminare": "S",
+			"Übungen": "U"
+		}
+		shorthand_start = "%{0}-{1}".format(CONSTANTS.INSTITUTE_MAP[inst].upper(), cat_map[category])
+
+		template = template.replace(shorthand_start + "%", str(newest_participation) + "%")
+		template = template.replace(shorthand_start + "-ROT%", str(rotation))
+		template = template.replace(shorthand_start + "-ARROW%", color)
+		template = template.replace(shorthand_start + "-IND%", str(difference) + "%")
+
+st.write(template, unsafe_allow_html=True)
+
+## Ranking
+# Slider for no of ranks
+if not present_mode:
+	st.header("Ranking", divider="gray")
+	with st.expander(":information_source: Hinweis", expanded=True):
+		st.markdown("Der Ranking Score wird berechnet indem alle erreichten Punkte für jede Frage summiert werden, diese dann durch die Anzahl an Rückmeldungen geteilt wird und somit die Durchschnittspunkte pro Evaluation erhalten werden")
+	# Auto generate
+	semester_ranking_select = st.multiselect("Ranking generieren für Semester:", ["SoSe2019", "SoSe2020", "SoSe2021", "SoSe2022", "SoSe2023"], ["SoSe2023"])
+	col1, col2 = st.columns(2)
+	col3, col4 = st.columns(2)
+	with col1:
+		type_select = st.multiselect("Generieren für Kategorien:", ["Vorlesungen", "Übungen", "Seminare"], ["Vorlesungen"])
+	with col2:
+		inst_select = st.multiselect("Generieren für die Institute:", ["Bioinformatik", "Informatik", "Mathematik"], ["Informatik"])
+	with col3:
+		combine_type = st.toggle("Kategorien kombinieren")
+	with col4:	
+		combine_inst = st.toggle("Institute kombinieren")
+
+	filtered = full_dataset[(full_dataset["Semester"].isin(semester_ranking_select)) & (full_dataset["Institut"].isin(inst_select)) & (full_dataset["Veranstaltungstyp"].isin(type_select))]
+	cols = [col for col in filtered.columns if uuid_regex.match(col) != None]
+	filtered["sum"] = filtered[cols].sum(axis=1)
+	dropped = filtered.drop(columns=cols)
+	dropped["Punkte"] = dropped["sum"] / dropped["Antworten"]
+	dropped["Punkte"] = dropped["Punkte"].apply(lambda x: round(x, 2))
+	dropped = dropped.drop(columns=["Veranstaltungsnummer", "Antworten", "Teilnehmende", "sum"])
+	if not combine_inst:
+		for institute in inst_select:
+			st.subheader(institute)
+			if not combine_type:
+				columns = st.columns(len(type_select))
+				for i in range(len(type_select)):
+					with columns[i]:
+						st.subheader(type_select[i])
+						frame = dropped[(dropped["Institut"] == institute) & (dropped["Veranstaltungstyp"] == type_select[i])]
+						frame.sort_values("Punkte", ascending=False, inplace=True)
+						st.dataframe(frame.head(10), hide_index=True, use_container_width=True)
+			else:
+					frame = dropped[dropped["Institut"] == institute]
+					frame.sort_values("Punkte", ascending=False, inplace=True)
+					st.dataframe(frame.head(10), hide_index=True, use_container_width=True)
+	else:
+		if not combine_type:
+			columns = st.columns(len(type_select))
+			for i in range(len(type_select)):
+				with columns[i]:
+					st.header(type_select[i])
+					frame = dropped[dropped["Veranstaltungstyp"] == type_select[i]]
+					frame.sort_values("Punkte", ascending=False, inplace=True)
+					st.dataframe(frame.head(10), hide_index=True, use_container_width=True)
+		else:
+			frame = dropped.sort_values("Punkte", ascending=False)
+			st.dataframe(frame.head(10), hide_index=True, use_container_width=True)
\ No newline at end of file
diff --git a/src/db/api/api-v1/api-doc.js b/src/db/api/api-v1/api-doc.js
index 288012ddfcb83e28fa7e1293afcfcabe2d00a900..c8cea0f82c50a5a6470a17bea1cc61fc571f8547 100644
--- a/src/db/api/api-v1/api-doc.js
+++ b/src/db/api/api-v1/api-doc.js
@@ -1,3 +1,5 @@
+import { course, score } from "./open-api-types.js";
+
 const apiDoc = {
   swagger: "2.0",
   basePath: "/v1",
@@ -17,8 +19,12 @@ const apiDoc = {
           type: "string",
           description: "Answer Choice",
         },
+        value: {
+          type: "integer",
+          description: "Number value representing the weight of the answer",
+        },
       },
-      required: ["id", "content"],
+      required: ["id", "content", "value"],
     },
     Course: {
       type: "object",
@@ -39,7 +45,8 @@ const apiDoc = {
       properties: {
         id: {
           type: "string",
-          description: "Dimension shorthand, first a letter between A, B or C and then 1 or 2"
+          description:
+            "Dimension shorthand, first a letter between A, B or C and then 1 or 2",
         },
         description: {
           type: "object",
@@ -47,25 +54,16 @@ const apiDoc = {
           properties: {
             en: {
               type: "string",
-              description: "English description"
+              description: "English description",
             },
             de: {
               type: "string",
-              description: "German description"
-            }
-          }
-        }
-      },
-      required: ["id", "description"]
-    },
-    Possibility: {
-      type: "object",
-      properties: {
-        id: {
-          type: "string",
-          description: "Key of the possibility",
+              description: "German description",
+            },
+          },
         },
       },
+      required: ["id", "description"],
     },
     Question: {
       type: "object",
@@ -78,17 +76,45 @@ const apiDoc = {
           type: "string",
           description: "Full question text",
         },
-        possibilities: {
+        scale: {
           type: "string",
-          description: "References Possibility id",
+          description: "References scale key",
         },
         dimension: {
           type: ["string", "null"],
-          description: "References Dimension id"
-        }
+          description: "References Dimension id",
+        },
       },
       required: ["name"],
     },
+    Scale: {
+      type: "object",
+      properties: {
+        id: {
+          type: "string",
+          description: "Key of the scale",
+        },
+      },
+    },
+    Score: {
+      type: "object",
+      properties: {
+        id: {
+          type: "string",
+          description: "Course UUID",
+        },
+        ...course,
+        scores: {
+          type: "array",
+          items: {
+            type: "object",
+            properties: {
+              ...score,
+            },
+          },
+        },
+      },
+    },
   },
   paths: {},
 };
diff --git a/src/db/api/api-v1/open-api-types.js b/src/db/api/api-v1/open-api-types.js
new file mode 100644
index 0000000000000000000000000000000000000000..62309aeda6f52faf7dc7690359a144812cd4d215
--- /dev/null
+++ b/src/db/api/api-v1/open-api-types.js
@@ -0,0 +1,50 @@
+//Provides Type Definitions for Open-API that should not show up as a model
+
+export const course = {
+  course_number: {
+    type: "string",
+    description: "Course Number according to the Campus Management",
+  },
+  course_name: {
+    type: "string",
+    description: "Name of the course",
+  },
+  course_type: {
+    type: "string",
+    description:
+      "Type the course was evaluated as (either Lecture, Tutorial or Seminar)",
+  },
+  lecturer: {
+    type: "string",
+    description: "Name of the Person that was evaluated",
+  },
+  semester: {
+    type: "string",
+    description: "Semester the course was evaluated in",
+    example: ["WiSe2020/21", "SoSe2020"],
+  },
+  institute: {
+    type: "string",
+    description: "Institute that the course is in",
+    example: ["bio", "math", "cs"],
+  },
+  answers: {
+    type: "integer",
+    description: "Amount of answers to the question",
+  },
+  participants: {
+    type: "integer",
+    description: "Number of students that visited the course",
+  },
+};
+
+export const score = {
+  question: {
+    type: "string",
+    description: "UUID of the scored question",
+  },
+  score: {
+    type: "integer",
+    description: "Calculated score for question",
+  },
+};
diff --git a/src/db/api/api-v1/paths/answer-possibilities.js b/src/db/api/api-v1/paths/answer-possibilities.js
deleted file mode 100644
index c0a782c4c36f2d6223275f051877354d651ba08e..0000000000000000000000000000000000000000
--- a/src/db/api/api-v1/paths/answer-possibilities.js
+++ /dev/null
@@ -1,32 +0,0 @@
-export default function (answerPossibilitiesService) {
-  let operations = {
-    GET,
-  };
-
-  async function GET(req, res, next) {
-    res
-      .status(200)
-      .json(await answerPossibilitiesService.getAnswerPossibilities());
-  }
-
-  GET.apiDoc = {
-    summary: "Returns all keys for answer possibilities",
-    operationId: "getAnswerPossibilities",
-    responses: {
-      200: {
-        description: "A list of all answer possibilities",
-        schema: {
-          type: "array",
-          items: {
-            $ref: "#/definitions/Possibility",
-          },
-        },
-      },
-      default: {
-        description: "An error occurred",
-      },
-    },
-  };
-
-  return operations;
-}
diff --git a/src/db/api/api-v1/paths/answers/{possibilityId}.js b/src/db/api/api-v1/paths/answers/{scale}.js
similarity index 66%
rename from src/db/api/api-v1/paths/answers/{possibilityId}.js
rename to src/db/api/api-v1/paths/answers/{scale}.js
index a38d0b5d4ca03f05ed10fbb5acd6085f27ac5724..7e195ecb2916fab2b138a697845e0f968c8b4755 100644
--- a/src/db/api/api-v1/paths/answers/{possibilityId}.js
+++ b/src/db/api/api-v1/paths/answers/{scale}.js
@@ -3,22 +3,21 @@ export default function (answersService) {
     GET,
   };
 
-  async function GET(req, res, next) {
-    res
-      .status(200)
-      .json(await answersService.getAnswers(req.params.possibilityId));
+  async function GET(req, res, _next) {
+    res.status(200).json(await answersService.getAnswers(req.params.scale));
   }
 
   GET.apiDoc = {
-    summary: "Returns all answers belonging to the provided possibility key",
+    summary: "Returns all answers belonging to the provided scale key",
+    tags: ["questions"],
     operationId: "getAnswers",
     parameters: [
       {
         in: "path",
-        name: "possibilityId",
+        name: "scale",
         required: true,
         type: "string",
-        description: "The Id of the possibility",
+        description: "The key of the scale",
       },
     ],
     responses: {
diff --git a/src/db/api/api-v1/paths/course-types.js b/src/db/api/api-v1/paths/course-types.js
index 902a22beeff0cca80e9fd4d223b401164607c646..9eddc1998a24551a0679eaf2b5f6184bcad66905 100644
--- a/src/db/api/api-v1/paths/course-types.js
+++ b/src/db/api/api-v1/paths/course-types.js
@@ -3,12 +3,13 @@ export default function (courseTypesService) {
     GET,
   };
 
-  async function GET(req, res, next) {
+  async function GET(_req, res, _next) {
     res.status(200).json(await courseTypesService.getCourseTypes());
   }
 
   GET.apiDoc = {
     summary: "Returns all course Types",
+    tags: ["questions"],
     operationId: "getCourseTypes",
     responses: {
       200: {
diff --git a/src/db/api/api-v1/paths/dimensions.js b/src/db/api/api-v1/paths/dimensions.js
index a65cd8ce420fe6125df7a59349dc65849158f8be..7fecf239cc7811e9a00ed62062cf225d8a8fa64e 100644
--- a/src/db/api/api-v1/paths/dimensions.js
+++ b/src/db/api/api-v1/paths/dimensions.js
@@ -3,12 +3,13 @@ export default function (dimensionsService) {
     GET,
   };
 
-  async function GET(req, res, next) {
+  async function GET(_req, res, _next) {
     res.status(200).json(await dimensionsService.getAllDimensions());
   }
 
   GET.apiDoc = {
     summary: "Returns all dimensions",
+    tags: ["questions"],
     operationId: "getAllDimensions",
     responses: {
       200: {
diff --git a/src/db/api/api-v1/paths/dimensions/questions/{dimensionId}.js b/src/db/api/api-v1/paths/dimensions/questions/{dimension}.js
similarity index 64%
rename from src/db/api/api-v1/paths/dimensions/questions/{dimensionId}.js
rename to src/db/api/api-v1/paths/dimensions/questions/{dimension}.js
index 92acc12bf6558e12e6673f897930580fe4107dde..5ca1d4d26d40639ee706f48b9d093b4c7821cf08 100644
--- a/src/db/api/api-v1/paths/dimensions/questions/{dimensionId}.js
+++ b/src/db/api/api-v1/paths/dimensions/questions/{dimension}.js
@@ -3,23 +3,25 @@ export default function (dimensionsService) {
     GET,
   };
 
-  async function GET(req, res, next) {
-		let answer = await dimensionsService.getQuestionsForDimension(req.params.dimensionId);
-		if(answer.length > 0) {
-			res.status(200).json(answer);
-		}
-		else {
-			res.status(404).end();
-		}
+  async function GET(req, res, _next) {
+    let answer = await dimensionsService.getQuestionsForDimension(
+      req.params.dimension
+    );
+    if (answer.length > 0) {
+      res.status(200).json(answer);
+    } else {
+      res.status(404).end();
+    }
   }
 
   GET.apiDoc = {
     summary: "Returns the questions belonging to the provided dimension",
+    tags: ["questions"],
     operationId: "getQuestionsForDimension",
     parameters: [
       {
         in: "path",
-        name: "dimensionId",
+        name: "dimension",
         required: true,
         type: "string",
         description: "The Id of the dimension",
@@ -29,7 +31,7 @@ export default function (dimensionsService) {
       200: {
         description: "Questions with the corresponding dimension",
         schema: {
-          "$ref": "#/definitions/Question",
+          $ref: "#/definitions/Question",
         },
       },
       default: {
diff --git a/src/db/api/api-v1/paths/dimensions/{dimensionId}.js b/src/db/api/api-v1/paths/dimensions/{dimension}.js
similarity index 66%
rename from src/db/api/api-v1/paths/dimensions/{dimensionId}.js
rename to src/db/api/api-v1/paths/dimensions/{dimension}.js
index c4f82ac924b62b34b2d38dc1265668c47d698234..752897c16266d3d70aeac26c38179c3881696a5e 100644
--- a/src/db/api/api-v1/paths/dimensions/{dimensionId}.js
+++ b/src/db/api/api-v1/paths/dimensions/{dimension}.js
@@ -3,17 +3,20 @@ export default function (dimensionsService) {
     GET,
   };
 
-  async function GET(req, res, next) {
-			res.status(200).json(await dimensionsService.getDimension(req.params.dimensionId));
+  async function GET(req, res, _next) {
+    res
+      .status(200)
+      .json(await dimensionsService.getDimension(req.params.dimension));
   }
 
   GET.apiDoc = {
     summary: "Returns the dimensions belonging to the provided id",
+    tags: ["questions"],
     operationId: "getDimension",
     parameters: [
       {
         in: "path",
-        name: "dimensionId",
+        name: "dimension",
         required: true,
         type: "string",
         description: "The Id of the dimension",
@@ -23,12 +26,12 @@ export default function (dimensionsService) {
       200: {
         description: "The dimension",
         schema: {
-          "$ref": "#/definitions/Dimension",
+          $ref: "#/definitions/Dimension",
         },
       },
-			404: {
-				description: "Not found"
-			},
+      404: {
+        description: "Not found",
+      },
       default: {
         description: "An error occurred",
       },
diff --git a/src/db/api/api-v1/paths/healthy.js b/src/db/api/api-v1/paths/healthy.js
new file mode 100644
index 0000000000000000000000000000000000000000..27b730e8d1e60ad45db645f73f420cf34e1bafdd
--- /dev/null
+++ b/src/db/api/api-v1/paths/healthy.js
@@ -0,0 +1,22 @@
+export default function () {
+  let operations = {
+    GET,
+  };
+
+  function GET(_req, res, _next) {
+    res.status(200).send("Healthy");
+  }
+
+  GET.apiDoc = {
+    summary: "Returns if the service runs",
+    tags: ["telemetry"],
+    operationId: "healthy",
+    responses: {
+      200: {
+        description: "Returns the word 'healthy'",
+      },
+    },
+  };
+
+  return operations;
+}
diff --git a/src/db/api/api-v1/paths/questions.js b/src/db/api/api-v1/paths/questions.js
index 03d1d69ee5c99b99497b167c58fa73503dd6f155..d8dadf9e2d23d1808ca0e68c6f6d38138156e530 100644
--- a/src/db/api/api-v1/paths/questions.js
+++ b/src/db/api/api-v1/paths/questions.js
@@ -3,12 +3,13 @@ export default function (questionsService) {
     GET,
   };
 
-  async function GET(req, res, next) {
+  async function GET(_req, res, _next) {
     res.status(200).json(await questionsService.getAllQuestions());
   }
 
   GET.apiDoc = {
     summary: "Returns all questions",
+    tags: ["questions"],
     operationId: "getAllQuestions",
     responses: {
       200: {
diff --git a/src/db/api/api-v1/paths/questions/{courseId}.js b/src/db/api/api-v1/paths/questions/{course}.js
similarity index 89%
rename from src/db/api/api-v1/paths/questions/{courseId}.js
rename to src/db/api/api-v1/paths/questions/{course}.js
index 958a57ca36b992dfee394cc370b54317230587b1..df1d207a95ea77147e02e6e33e7d5a73adbfedf5 100644
--- a/src/db/api/api-v1/paths/questions/{courseId}.js
+++ b/src/db/api/api-v1/paths/questions/{course}.js
@@ -3,19 +3,20 @@ export default function (questionsService) {
     GET,
   };
 
-  async function GET(req, res, next) {
+  async function GET(req, res, _next) {
     res
       .status(200)
-      .json(await questionsService.getQuestions(req.params.courseId));
+      .json(await questionsService.getQuestions(req.params.course));
   }
 
   GET.apiDoc = {
     summary: "Returns all questions belonging to the provided course type id",
+    tags: ["questions"],
     operationId: "getQuestions",
     parameters: [
       {
         in: "path",
-        name: "courseId",
+        name: "course",
         required: true,
         type: "string",
         description: "The Id of the course",
diff --git a/src/db/api/api-v1/paths/questions/{question}/dimension.js b/src/db/api/api-v1/paths/questions/{question}/dimension.js
new file mode 100644
index 0000000000000000000000000000000000000000..04e7a3f4a3bafbedf82937a958e5ffbd7355f3c0
--- /dev/null
+++ b/src/db/api/api-v1/paths/questions/{question}/dimension.js
@@ -0,0 +1,45 @@
+export default function (dimensionsService) {
+  let operations = {
+    GET,
+  };
+
+  async function GET(req, res, _next) {
+    res
+      .status(200)
+      .json(await dimensionsService.getDimensionForQuestion(req.params.question));
+  }
+
+  GET.apiDoc = {
+    summary: "Returns the dimension that belongs to the specified question",
+    tags: ["questions"],
+    operationId: "getDimensionForQuestion",
+    parameters: [
+      {
+        in: "path",
+        name: "question",
+        required: true,
+        type: "string",
+        description: "The Id of the question",
+      },
+    ],
+    responses: {
+      200: {
+        description: "A list of questions that match the requested id",
+        schema: {
+					type: "object",
+					properties: {
+						id: {
+							type: "string",
+							description: "Dimension Id"
+						}
+					}
+        },
+      },
+      default: {
+        description: "An error occurred",
+      },
+    },
+  };
+
+  return operations;
+}
diff --git a/src/db/api/api-v1/paths/scales.js b/src/db/api/api-v1/paths/scales.js
new file mode 100644
index 0000000000000000000000000000000000000000..b1c25adf45bb9e0162ecf228b50ec5f639fccc2f
--- /dev/null
+++ b/src/db/api/api-v1/paths/scales.js
@@ -0,0 +1,31 @@
+export default function (scalesService) {
+  let operations = {
+    GET,
+  };
+
+  async function GET(_req, res, _next) {
+    res.status(200).json(await scalesService.getScales());
+  }
+
+  GET.apiDoc = {
+    summary: "Returns all keys for the scales",
+    tags: ["questions"],
+    operationId: "getScales",
+    responses: {
+      200: {
+        description: "A list of all scales",
+        schema: {
+          type: "array",
+          items: {
+            $ref: "#/definitions/Scale",
+          },
+        },
+      },
+      default: {
+        description: "An error occurred",
+      },
+    },
+  };
+
+  return operations;
+}
diff --git a/src/db/api/api-v1/paths/scales/{question}.js b/src/db/api/api-v1/paths/scales/{question}.js
new file mode 100644
index 0000000000000000000000000000000000000000..9333f9c8e1e4d1935b6076a6a15a0e31ddc85162
--- /dev/null
+++ b/src/db/api/api-v1/paths/scales/{question}.js
@@ -0,0 +1,47 @@
+export default function (scalesService) {
+  let operations = {
+    GET,
+  };
+
+  async function GET(req, res, _next) {
+    let scale = await scalesService.getScaleForQuestion(req.params.question);
+    if (scale == null) {
+      res
+        .status(404)
+        .send(`${req.params.question} does not exist or doesn't have a scale`);
+    } else {
+      res.status(200).json(scale);
+    }
+  }
+
+  GET.apiDoc = {
+    summary: "Returns the answer scale belonging to the provided question id",
+    tags: ["questions"],
+    operationId: "getScaleForQuestion",
+    parameters: [
+      {
+        in: "path",
+        name: "question",
+        required: true,
+        type: "string",
+        description: "The Id of the question",
+      },
+    ],
+    responses: {
+      200: {
+        description: "The key belonging to the question",
+        schema: {
+          $ref: "#/definitions/Scale",
+        },
+      },
+      default: {
+        description: "An error occurred",
+      },
+      404: {
+        description: "Not found",
+      },
+    },
+  };
+
+  return operations;
+}
diff --git a/src/db/api/api-v1/paths/scores.js b/src/db/api/api-v1/paths/scores.js
new file mode 100644
index 0000000000000000000000000000000000000000..c987dec611eb0c8c5323c4a6567848a7d77b3fed
--- /dev/null
+++ b/src/db/api/api-v1/paths/scores.js
@@ -0,0 +1,148 @@
+import { getCourse } from "../../postgres.js";
+import { course, score } from "../open-api-types.js";
+
+export default function (scoresService) {
+  let operations = {
+    GET,
+    POST,
+    PUT,
+  };
+
+  async function GET(_req, res, _next) {
+    res.status(200).json(await scoresService.getAllScores());
+  }
+
+  async function POST(req, res, _next) {
+    let success = await scoresService.postScores(req.body);
+    // !success would always be true due to javascript object conversion
+    if (success !== true) {
+      const [course, question] = success;
+      let course_info = (await getCourse(course))[0];
+      return res.status(409).json({
+        course_number: course_info.number,
+        course_name: course_info.name,
+        course_type: course_info.type,
+        lecturer: course_info.lecturer,
+        semester: course_info.semester,
+        question: question,
+      });
+    }
+    res.status(201).end();
+  }
+
+  async function PUT(req, res, _next) {
+    res.status(201).json(await scoresService.postScores(req.body, true));
+  }
+
+  GET.apiDoc = {
+    summary: "Returns scores for every course from every semester",
+    tags: ["scores"],
+    operationId: "getAllScores",
+    responses: {
+      200: {
+        description: "A list of Scores",
+        schema: {
+          type: "array",
+          items: {
+            $ref: "#/definitions/Score",
+          },
+        },
+      },
+      default: {
+        description: "An error occurred",
+      },
+    },
+  };
+
+  POST.apiDoc = {
+    summary: "Inserts provided scores into the database",
+    tags: ["scores"],
+    operationId: "postScores",
+    parameters: [
+      {
+        name: "scores",
+        in: "body",
+        schema: {
+          type: "array",
+          items: {
+            type: "object",
+            properties: {
+              ...course,
+              scores: {
+                type: "array",
+                items: {
+                  type: "object",
+                  properties: {
+                    ...score,
+                  },
+                },
+              },
+            },
+          },
+        },
+      },
+    ],
+    responses: {
+      201: {
+        description: "Gets returned if all scores were insterted successfully",
+      },
+      409: {
+        description:
+          "Gets returned if scores already exist. Returns duplicated scores",
+        schema: {
+          type: "object",
+          properties: {
+            ...course,
+            question: {
+              type: "string",
+              description: "UUID of duplicated question",
+            },
+          },
+        },
+      },
+      default: {
+        description: "An error occurred",
+      },
+    },
+  };
+
+  PUT.apiDoc = {
+    summary: "Inserts provided scores into the database. Overrides duplicates",
+    tags: ["scores"],
+    operationId: "postScoresForce",
+    parameters: [
+      {
+        name: "scores",
+        in: "body",
+        schema: {
+          type: "array",
+          items: {
+            type: "object",
+            properties: {
+              ...course,
+              scores: {
+                type: "array",
+                items: {
+                  type: "object",
+                  properties: {
+                    ...score,
+                  },
+                },
+              },
+            },
+          },
+        },
+      },
+    ],
+    responses: {
+      201: {
+        description: "Gets returned if all scores were insterted successfully",
+      },
+      default: {
+        description: "An error occurred",
+      },
+    },
+  };
+
+  return operations;
+}
diff --git a/src/db/api/api-v1/paths/scores/course/{question}.js b/src/db/api/api-v1/paths/scores/course/{question}.js
new file mode 100644
index 0000000000000000000000000000000000000000..82a4f085dd2c0ec8452287088379f3ca39429990
--- /dev/null
+++ b/src/db/api/api-v1/paths/scores/course/{question}.js
@@ -0,0 +1,70 @@
+import { course } from "../../../open-api-types.js";
+
+export default function (scoresService) {
+  let operations = {
+    DELETE,
+  };
+
+  async function DELETE(req, res, _next) {
+    res
+      .status(200)
+      .json(
+        await scoresService.deleteScoreForQuestion(
+          req.body,
+          req.params.question
+        )
+      );
+  }
+
+  DELETE.apiDoc = {
+    summary:
+      "Removes the score for the provided Questions for the provided course",
+    tags: ["scores"],
+    operationId: "deleteScoreForQuestion",
+    parameters: [
+      {
+        in: "body",
+        name: "course",
+        required: true,
+        schema: {
+          type: "array",
+          items: {
+            type: "object",
+            properties: {
+              ...course,
+            },
+          },
+        },
+      },
+      {
+        in: "path",
+        name: "question",
+        required: true,
+        description: "Question UUID",
+        type: "string",
+      },
+    ],
+    responses: {
+      200: {
+        description: "Deleted Score",
+        schema: {
+          type: "object",
+          properties: {
+            question: {
+              type: "string",
+              description: "UUID of the scored question",
+            },
+            score: {
+              type: "integer",
+              description: "Calculated score for question",
+            },
+          },
+        },
+      },
+      default: {
+        description: "An error occurred",
+      },
+    },
+  };
+  return operations;
+}
diff --git a/src/db/api/api-v1/paths/scores/dimensions/{dimension}.js b/src/db/api/api-v1/paths/scores/dimensions/{dimension}.js
new file mode 100644
index 0000000000000000000000000000000000000000..1b0bdcdfb32d899c3f92b3be9661fcedb5b883ac
--- /dev/null
+++ b/src/db/api/api-v1/paths/scores/dimensions/{dimension}.js
@@ -0,0 +1,43 @@
+export default function (scoresService) {
+  let operations = {
+    GET,
+  };
+
+  async function GET(req, res, _next) {
+    res
+      .status(200)
+      .json(await scoresService.getAllScoresForDimension(req.params.dimension));
+  }
+
+  GET.apiDoc = {
+    summary: "Returns scores for every course for the specified dimension",
+    tags: ["scores"],
+    operationId: "getAllScoresForDimension",
+    parameters: [
+      {
+        in: "path",
+        name: "dimension",
+        required: true,
+        description:
+          "Dimension Name (accepts 'A', 'B', 'C' or subdimensions with 'A1', 'A2', 'B1', etc.)",
+        type: "string",
+      },
+    ],
+    responses: {
+      200: {
+        description: "A list of Scores",
+        schema: {
+          type: "array",
+          items: {
+            $ref: "#/definitions/Score",
+          },
+        },
+      },
+      default: {
+        description: "An error occurred",
+      },
+    },
+  };
+
+  return operations;
+}
diff --git a/src/db/api/api-v1/paths/scores/{semester}.js b/src/db/api/api-v1/paths/scores/{semester}.js
new file mode 100644
index 0000000000000000000000000000000000000000..6d4681f507d2c1afd6175a7ee004e404eaed0142
--- /dev/null
+++ b/src/db/api/api-v1/paths/scores/{semester}.js
@@ -0,0 +1,72 @@
+export default function (scoresService) {
+  let operations = {
+    GET,
+    DELETE,
+  };
+
+  async function GET(req, res, _next) {
+    res
+      .status(200)
+      .json(await scoresService.getAllScoresForSemester(req.params.semester));
+  }
+
+  async function DELETE(req, res, _next) {
+    res
+      .status(200)
+      .json(await scoresService.deleteScoresForSemester(req.params.semester));
+  }
+
+  GET.apiDoc = {
+    summary: "Returns scores for every course from the specified semester",
+    tags: ["scores"],
+    operationId: "getAllScoresForSemester",
+    parameters: [
+      {
+        in: "path",
+        name: "semester",
+        required: true,
+        description: "Semester Name",
+        type: "string",
+      },
+    ],
+    responses: {
+      200: {
+        description: "A list of Scores",
+        schema: {
+          type: "array",
+          items: {
+            $ref: "#/definitions/Score",
+          },
+        },
+      },
+      default: {
+        description: "An error occurred",
+      },
+    },
+  };
+
+  DELETE.apiDoc = {
+    summary: "Deletes scores for every course from the specified semester",
+    tags: ["scores"],
+    operationId: "deleteScoresForSemester",
+    parameters: [
+      {
+        in: "path",
+        name: "semester",
+        required: true,
+        description: "Semester Name",
+        type: "string",
+      },
+    ],
+    responses: {
+      200: {
+        description: "Returned after everything is deleted successfully",
+      },
+      default: {
+        description: "An error occurred",
+      },
+    },
+  };
+
+  return operations;
+}
diff --git a/src/db/api/api-v1/paths/scores/{semester}/{dimension}.js b/src/db/api/api-v1/paths/scores/{semester}/{dimension}.js
new file mode 100644
index 0000000000000000000000000000000000000000..8d613d0e3e824eb500d8cccfef3fe3ff4785a9d3
--- /dev/null
+++ b/src/db/api/api-v1/paths/scores/{semester}/{dimension}.js
@@ -0,0 +1,54 @@
+export default function (scoresService) {
+  let operations = {
+    GET,
+  };
+
+  async function GET(req, res, _next) {
+    res
+      .status(200)
+      .json(
+        await scoresService.getAllScoresForDimensionInSemester(
+          req.params.semester,
+          req.params.dimension
+        )
+      );
+  }
+
+  GET.apiDoc = {
+    summary:
+      "Returns scores for every course from the specified semester from the provided dimension",
+    tags: ["scores"],
+    operationId: "getAllScoresForDimensionInSemester",
+    parameters: [
+      {
+        in: "path",
+        name: "semester",
+        required: true,
+        description: "Semester Name",
+        type: "string",
+      },
+      {
+        in: "path",
+        name: "dimension",
+        required: true,
+        description: "Dimension Name",
+        type: "string",
+      },
+    ],
+    responses: {
+      200: {
+        description: "A list of Scores",
+        schema: {
+          type: "array",
+          items: {
+            $ref: "#/definitions/Score",
+          },
+        },
+      },
+      default: {
+        description: "An error occurred",
+      },
+    },
+  };
+  return operations;
+}
diff --git a/src/db/api/api-v1/paths/scores/{semester}/{dimension}/{institute}.js b/src/db/api/api-v1/paths/scores/{semester}/{dimension}/{institute}.js
new file mode 100644
index 0000000000000000000000000000000000000000..775898b7a51896b49d798a2b13ea485434040bda
--- /dev/null
+++ b/src/db/api/api-v1/paths/scores/{semester}/{dimension}/{institute}.js
@@ -0,0 +1,62 @@
+export default function (scoresService) {
+  let operations = {
+    GET,
+  };
+
+  async function GET(req, res, _next) {
+    res
+      .status(200)
+      .json(
+        await scoresService.getAllScoresForDimensionInInstitute(
+          req.params.semester,
+          req.params.dimension,
+          req.params.institute
+        )
+      );
+  }
+
+  GET.apiDoc = {
+    summary:
+      "Returns scores for every course of the institute from the specified semester from the provided dimension",
+    tags: ["scores"],
+    operationId: "getAllScoresForDimensionInInstitute",
+    parameters: [
+      {
+        in: "path",
+        name: "semester",
+        required: true,
+        description: "Semester Name",
+        type: "string",
+      },
+      {
+        in: "path",
+        name: "dimension",
+        required: true,
+        description: "Dimension Name",
+        type: "string",
+      },
+      {
+        in: "path",
+        name: "institute",
+        required: true,
+        description: "Institute Name",
+        type: "string",
+      },
+    ],
+    responses: {
+      200: {
+        description: "A list of Scores",
+        schema: {
+          type: "array",
+          items: {
+            $ref: "#/definitions/Score",
+          },
+        },
+      },
+      default: {
+        description: "An error occurred",
+      },
+    },
+  };
+  return operations;
+}
diff --git a/src/db/api/api-v1/paths/{institute}/score.js b/src/db/api/api-v1/paths/{institute}/score.js
new file mode 100644
index 0000000000000000000000000000000000000000..32335cc70ae5c9c8660b13dc136556fff0cbe456
--- /dev/null
+++ b/src/db/api/api-v1/paths/{institute}/score.js
@@ -0,0 +1,42 @@
+export default function (scoresService) {
+  let operations = {
+    GET,
+  };
+
+  async function GET(req, res, _next) {
+    res
+      .status(200)
+      .json(await scoresService.getAllScoresForInstitute(req.params.institute));
+  }
+
+  GET.apiDoc = {
+    summary: "Returns scores for every course from the specified institute",
+    tags: ["scores"],
+    operationId: "getAllScoresForInstitute",
+    parameters: [
+      {
+        in: "path",
+        name: "institute",
+        required: true,
+        description: "Institute Name",
+        type: "string",
+      },
+    ],
+    responses: {
+      200: {
+        description: "A list of Scores",
+        schema: {
+          type: "array",
+          items: {
+            $ref: "#/definitions/Score",
+          },
+        },
+      },
+      default: {
+        description: "An error occurred",
+      },
+    },
+  };
+
+  return operations;
+}
diff --git a/src/db/api/api-v1/paths/{institute}/{semester}/score.js b/src/db/api/api-v1/paths/{institute}/{semester}/score.js
new file mode 100644
index 0000000000000000000000000000000000000000..1484b9de5746876374440714aaa33feb78ca23a8
--- /dev/null
+++ b/src/db/api/api-v1/paths/{institute}/{semester}/score.js
@@ -0,0 +1,55 @@
+export default function (scoresService) {
+  let operations = {
+    GET,
+  };
+
+  async function GET(req, res, _next) {
+    res
+      .status(200)
+      .json(
+        await scoresService.getAllScoresForInstituteFromSemester(
+          req.params.institute,
+          req.params.semester
+        )
+      );
+  }
+
+  GET.apiDoc = {
+    summary:
+      "Returns scores for every course from the specified semester and institute",
+    tags: ["scores"],
+    operationId: "getAllScoresForInstituteFromSemester",
+    parameters: [
+      {
+        in: "path",
+        name: "institute",
+        required: true,
+        description: "Institute Name",
+        type: "string",
+      },
+      {
+        in: "path",
+        name: "semester",
+        required: true,
+        description: "Semester Name",
+        type: "string",
+      },
+    ],
+    responses: {
+      200: {
+        description: "A list of Scores",
+        schema: {
+          type: "array",
+          items: {
+            $ref: "#/definitions/Score",
+          },
+        },
+      },
+      default: {
+        description: "An error occurred",
+      },
+    },
+  };
+
+  return operations;
+}
diff --git a/src/db/api/api-v1/services/answerPossibilitiesService.js b/src/db/api/api-v1/services/answerPossibilitiesService.js
deleted file mode 100644
index a312e1eae0a20c77533e5abe350eedf6e32bf4bc..0000000000000000000000000000000000000000
--- a/src/db/api/api-v1/services/answerPossibilitiesService.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import { getAll } from "../../postgres.js";
-
-const answerPossibilitiesService = {
-  async getAnswerPossibilities() {
-    return await getAll("answer-possibility");
-  },
-};
-
-export default answerPossibilitiesService;
diff --git a/src/db/api/api-v1/services/dimensionsService.js b/src/db/api/api-v1/services/dimensionsService.js
index bff2c1ac58a650b65d703482922336531ffaa80e..c32a7980d3e8ce3127495d1df3cd8eebb9766b81 100644
--- a/src/db/api/api-v1/services/dimensionsService.js
+++ b/src/db/api/api-v1/services/dimensionsService.js
@@ -1,4 +1,4 @@
-import { getQuestionsWithDimension } from "../../postgres.js";
+import { getQuestionsWithDimension, getDimensionForQuestion } from "../../postgres.js";
 
 const dimensions = [
   {
@@ -41,13 +41,17 @@ const dimensions = [
 
 const dimensionsService = {
   getDimension(id) {
-    return dimensions.filter(dim => Object.keys(dim)[0] == id);
+    return dimensions.filter((dim) => Object.keys(dim)[0] == id);
   },
   getAllDimensions() {
     return dimensions;
   },
   async getQuestionsForDimension(id) {
     return getQuestionsWithDimension(id);
+  },
+  async getDimensionForQuestion(question) {
+    let res = await getDimensionForQuestion(question);
+    return {id: res.id}
   }
 };
 
diff --git a/src/db/api/api-v1/services/scalesService.js b/src/db/api/api-v1/services/scalesService.js
new file mode 100644
index 0000000000000000000000000000000000000000..b84c8f979b919799e65ee2d04556fc45654ad6e3
--- /dev/null
+++ b/src/db/api/api-v1/services/scalesService.js
@@ -0,0 +1,12 @@
+import { getAll, getScaleForQuestion } from "../../postgres.js";
+
+const scalesService = {
+  async getScales() {
+    return await getAll("scale");
+  },
+  async getScaleForQuestion(question) {
+    return await getScaleForQuestion(question);
+  },
+};
+
+export default scalesService;
diff --git a/src/db/api/api-v1/services/scoresService.js b/src/db/api/api-v1/services/scoresService.js
new file mode 100644
index 0000000000000000000000000000000000000000..504ba0f80e3894361a095ef14fb0257aec871322
--- /dev/null
+++ b/src/db/api/api-v1/services/scoresService.js
@@ -0,0 +1,73 @@
+import {
+  createCourseIfNotExists,
+  getAllScores,
+  insertScores,
+  getAllScoresForDimensionInInstitute,
+  getAllScoresForDimension,
+  getAllScoresForSemester,
+  getAllScoresForInstitute,
+  getAllScoresForInstituteFromSemester,
+  getAllScoresForDimensionInSemester,
+  deleteScoresForSemester,
+  deleteScoreForQuestion,
+} from "../../postgres.js";
+
+const scoresService = {
+  async getAllScores() {
+    return await getAllScores();
+  },
+  async getAllScoresForSemester(semester) {
+    return getAllScoresForSemester(semester);
+  },
+  async getAllScoresForInstitute(institute) {
+    return await getAllScoresForInstitute(institute);
+  },
+  async getAllScoresForInstituteFromSemester(institute, semester) {
+    return getAllScoresForInstituteFromSemester(institute, semester);
+  },
+  async getAllScoresForDimensionInSemester(semester, dimension) {
+    return await getAllScoresForDimensionInSemester(semester, dimension);
+  },
+  async getAllScoresForDimension(dimension) {
+    return await getAllScoresForDimension(dimension);
+  },
+  async getAllScoresForDimensionInInstitute(semester, dimension, institute) {
+    return await getAllScoresForDimensionInInstitute(
+      semester,
+      dimension,
+      institute
+    );
+  },
+  async postScores(scores, force = false) {
+    // Transpose format
+    let queries = [];
+
+    for (let score of scores) {
+      // Gets course ID
+      let courseId = await createCourseIfNotExists(
+        score.course_number,
+        score.course_name,
+        score.course_type,
+        score.lecturer,
+        score.semester,
+        score.institute,
+        score.answers,
+        score.participants
+      );
+
+      for (let subpoints of score.scores) {
+        queries.push([courseId, subpoints.question, subpoints.score]);
+      }
+    }
+
+    return await insertScores(queries, force);
+  },
+  async deleteScoresForSemester(semester) {
+    return await deleteScoresForSemester(semester);
+  },
+  async deleteScoreForQuestion(courses, question) {
+    return await deleteScoreForQuestion(courses, question);
+  },
+};
+
+export default scoresService;
diff --git a/src/db/api/index.js b/src/db/api/index.js
index 9d230ad1d4f1c84434c22bb3109e45515298f01a..89197b73a05a11aedd4279a30e2442b1e73314c9 100644
--- a/src/db/api/index.js
+++ b/src/db/api/index.js
@@ -5,10 +5,14 @@ import swaggerUi from "swagger-ui-express";
 import v1Docs from "./api-v1/api-doc.js";
 //Services
 import v1QuestionsService from "./api-v1/services/questionsService.js";
-import v1AnswerPossibilitiesService from "./api-v1/services/answerPossibilitiesService.js";
+import v1ScalesService from "./api-v1/services/scalesService.js";
 import v1AnswersService from "./api-v1/services/answersService.js";
 import v1CourseTypesService from "./api-v1/services/courseTypesService.js";
 import v1DimensionsService from "./api-v1/services/dimensionsService.js";
+import v1ScoresService from "./api-v1/services/scoresService.js";
+
+//TODO: examples?
+//TODO: Documentation
 
 const app = express();
 const port = 3000;
@@ -18,15 +22,18 @@ initialize({
   apiDoc: v1Docs,
   dependencies: {
     questionsService: v1QuestionsService,
-    answerPossibilitiesService: v1AnswerPossibilitiesService,
+    scalesService: v1ScalesService,
     answersService: v1AnswersService,
     courseTypesService: v1CourseTypesService,
-    dimensionsService: v1DimensionsService
+    dimensionsService: v1DimensionsService,
+    scoresService: v1ScoresService,
   },
   paths: "./api-v1/paths/",
   docsPath: "/api-docs",
 });
 
+app.use(express.json());
+
 app.use(
   "/v1/docs",
   swaggerUi.serve,
@@ -37,8 +44,8 @@ app.use(
   })
 );
 
-app.get("/", (req, res) => {
+app.get("/", (_req, res) => {
   res.redirect("/v1/docs");
-})
+});
 
 app.listen(port);
diff --git a/src/db/api/package-lock.json b/src/db/api/package-lock.json
index eb0082f241916cecb4a75f1956d22412370312c8..2bfa19e4faf6a2fdf62ef752c5c2967335095b35 100644
--- a/src/db/api/package-lock.json
+++ b/src/db/api/package-lock.json
@@ -13,6 +13,7 @@
         "express": "^4.19.1",
         "express-openapi": "^12.1.3",
         "pg": "^8.11.3",
+        "pg-format": "^1.0.4",
         "swagger-ui-express": "^5.0.0"
       }
     },
@@ -976,6 +977,14 @@
       "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz",
       "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA=="
     },
+    "node_modules/pg-format": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/pg-format/-/pg-format-1.0.4.tgz",
+      "integrity": "sha512-YyKEF78pEA6wwTAqOUaHIN/rWpfzzIuMh9KdAhc3rSLQ/7zkRFcCgYBAEGatDstLyZw4g0s9SNICmaTGnBVeyw==",
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
     "node_modules/pg-int8": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
diff --git a/src/db/api/package.json b/src/db/api/package.json
index b0a6c6b780a2675c517ef03dbc3c63daf26b9194..e96b6b6d8ea32b18385dbb91221e30569eabeca8 100644
--- a/src/db/api/package.json
+++ b/src/db/api/package.json
@@ -14,6 +14,7 @@
     "express": "^4.19.1",
     "express-openapi": "^12.1.3",
     "pg": "^8.11.3",
+    "pg-format": "^1.0.4",
     "swagger-ui-express": "^5.0.0"
   }
 }
diff --git a/src/db/api/postgres.js b/src/db/api/postgres.js
index 62fb29245492c0a999a00bc335e0ad3e5f9362a2..6b7dcbd84fd4c74a1d44e4e1b60c282ccf16aa05 100644
--- a/src/db/api/postgres.js
+++ b/src/db/api/postgres.js
@@ -1,4 +1,5 @@
 import pg from "pg";
+import format from "pg-format";
 import "dotenv/config";
 
 const client = new pg.Client({
@@ -18,27 +19,236 @@ export async function getAll(table) {
 
 export async function getQuestions(courseId) {
   const res = await client.query(
-    'SELECT id, content, possibilities, dimension FROM question INNER JOIN (SELECT question FROM "question-belongs-to" WHERE course = $1) ON question.id = question',
+    'SELECT id, content, scale, dimension FROM question INNER JOIN (SELECT question FROM "question-belongs-to" WHERE course = $1) ON question.id = question',
     [courseId]
   );
   return res.rows;
 }
 
-export async function getAnswers(possibilityId) {
+export async function getAnswers(scale) {
   const res = await client.query(
-    'SELECT id, content FROM answer INNER JOIN (SELECT answer FROM "answer-belongs-to" WHERE possibility = $1) ON answer.id = answer',
-    [possibilityId]
+    'SELECT id, content, value FROM answer INNER JOIN (SELECT answer FROM "answer-belongs-to" WHERE scale = $1) ON answer.id = answer',
+    [scale]
   );
   return res.rows;
 }
 
 export async function getQuestionsWithDimension(dimensionId) {
-  const res = await client.query(
-    "SELECT * FROM question WHERE dimension = $1", [dimensionId]
+  let res;
+  if (["A", "B", "C"].includes(dimensionId)) {
+    res = await client.query(
+      "SELECT * FROM question WHERE dimension LIKE $1 || '%'",
+      [dimensionId]
+    );
+  } else {
+    res = await client.query("SELECT * FROM question WHERE dimension = $1", [
+      dimensionId,
+    ]);
+  }
+  return res.rows;
+}
+
+export async function getScaleForQuestion(questionId) {
+  let res = await client.query("SELECT scale FROM question WHERE id = $1", [
+    questionId,
+  ]);
+  if (res.rows.length > 0) {
+    return { id: res.rows[0].scale };
+  } else {
+    return null;
+  }
+}
+
+export async function getDimensionForQuestion(questionId) {
+  let res = await client.query("SELECT dimension FROM question WHERE id = $1", [questionId]);
+  
+  if (res.rows.length > 0) {
+    return { id: res.rows[0].dimension }
+  }
+  else {
+    return null;
+  }
+}
+
+function generateScoreStructure(rows) {
+  let courses = [];
+
+  for (let row of rows) {
+    if (courses.filter((course) => course.id == row.id).length > 0) {
+      courses
+        .filter((course) => course.id == row.id)[0]
+        .scores.push({
+          question: row.question,
+          score: row.score,
+        });
+    } else {
+      courses.push({
+        id: row.id,
+        course_number: row.number,
+        course_name: row.name,
+        course_type: row.type,
+        lecturer: row.lecturer,
+        semester: row.semester,
+        institute: row.institute,
+        answers: row.answers,
+        participants: row.participants,
+        scores: [
+          {
+            question: row.question,
+            score: row.score,
+          },
+        ],
+      });
+    }
+  }
+
+  return courses;
+}
+
+export async function getCourse(id) {
+  console.log(id);
+  let res = await client.query(
+    "SELECT number, name, type, lecturer, semester FROM course WHERE id = $1",
+    [id]
   );
+
   return res.rows;
 }
 
+export async function getAllScores() {
+  let res = await client.query(
+    "SELECT * FROM course INNER JOIN (SELECT * FROM score) on id = course"
+  );
+  if (res.rows.length == 0) {
+    return [];
+  }
+
+  return generateScoreStructure(res.rows);
+}
+
+export async function createCourseIfNotExists(
+  number,
+  name,
+  type,
+  lecturer,
+  semester,
+  institute,
+  answers,
+  participants
+) {
+  // Exists check
+  let course = await client.query(
+    "SELECT id FROM course where number = $1 AND name = $2 AND type = $3 AND lecturer = $4 and semester = $5 AND institute = $6",
+    [number, name, type, lecturer, semester, institute]
+  );
+
+  if (course.rows.length == 0) {
+    let res = await client.query(
+      "INSERT INTO course(number, name, type, lecturer, semester, institute, answers, participants) VALUES($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id",
+      [number, name, type, lecturer, semester, institute, answers, participants]
+    );
+    return res.rows[0].id;
+  } else {
+    return course.rows[0].id;
+  }
+}
+
+export async function insertScores(scores, force = false) {
+  if (!force) {
+    try {
+      await client.query(format("INSERT INTO score VALUES %L", scores));
+      return true;
+    } catch (e) {
+      let lastPart = e.detail.split("=")[1];
+      let tupleRegex = /\(.+,\s.+\)/g;
+      let tuple = lastPart.match(tupleRegex)[0];
+      return tuple.replace("(", "").replace(")", "").split(", ");
+    }
+  }
+
+  await client.query(
+    format(
+      "INSERT INTO score VALUES %L ON CONFLICT (course, question) DO UPDATE SET course=EXCLUDED.course, question=EXCLUDED.question, score=EXCLUDED.score",
+      scores
+    )
+  );
+}
+
+export async function getAllScoresForDimensionInInstitute(
+  semester,
+  dimension,
+  institute
+) {
+  let res = await client.query(
+    "SELECT * FROM course INNER JOIN (SELECT * FROM score INNER JOIN (SELECT * FROM question) ON question=id) ON course.id = course WHERE semester=$1 AND institute=$2 AND dimension LIKE $3 || '%'",
+    [semester, institute, dimension]
+  );
+  return generateScoreStructure(res.rows);
+}
+
+export async function getAllScoresForDimensionInSemester(semester, dimension) {
+  let res = await client.query(
+    "SELECT * FROM course INNER JOIN (SELECT * FROM score INNER JOIN (SELECT * FROM question) ON question=id) ON course.id = course WHERE semester=$1 AND dimension LIKE $2 || '%'",
+    [semester, dimension]
+  );
+  return generateScoreStructure(res.rows);
+}
+
+export async function getAllScoresForDimension(dimension) {
+  let res = await client.query(
+    "SELECT * FROM course INNER JOIN (SELECT * FROM score INNER JOIN (SELECT id as questionId, dimension FROM question) ON question=questionId) ON course.id = course WHERE dimension LIKE $1 || '%'",
+    [dimension]
+  );
+  return generateScoreStructure(res.rows);
+}
+
+export async function getAllScoresForSemester(semester) {
+  let res = await client.query(
+    "SELECT * FROM course INNER JOIN (SELECT * FROM score) on id = course WHERE semester = $1",
+    [semester]
+  );
+  return generateScoreStructure(res.rows);
+}
+
+export async function getAllScoresForInstitute(institute) {
+  let res = await client.query(
+    "SELECT * FROM course INNER JOIN (SELECT * FROM score) on id = course WHERE institute = $1",
+    [institute]
+  );
+  return generateScoreStructure(res.rows);
+}
+
+export async function getAllScoresForInstituteFromSemester(
+  institute,
+  semester
+) {
+  let res = await client.query(
+    "SELECT * FROM course INNER JOIN (SELECT * FROM score) on id = course WHERE institute = $1 AND semester = $2",
+    [institute, semester]
+  );
+  return generateScoreStructure(res.rows);
+}
+
+export async function deleteScoresForSemester(semester) {
+  await client.query("DELETE FROM course WHERE semester = $1", [semester]);
+}
+
+export async function deleteScoreForQuestion(courses, question) {
+  for (let course of courses) {
+    await client.query(
+      "DELETE FROM score WHERE question = $1 AND course IN (SELECT id FROM course WHERE number = $2 AND name = $3 AND type = $4 AND lecturer = $5 AND semester = $6)",
+      [
+        question,
+        course.course_number,
+        course.course_name,
+        course.course_type,
+        course.lecturer,
+        course.semester,
+      ]
+    );
+  }
+}
+
 export async function kill() {
   await client.end();
 }
diff --git a/src/db/docker-compose.yaml b/src/db/docker-compose.yaml
index f11b1a23be19c4f1c85d82d8f927108dd6329e51..3b06f5e0aca0881eb0e6d05afe47cc72e4daa353 100644
--- a/src/db/docker-compose.yaml
+++ b/src/db/docker-compose.yaml
@@ -34,4 +34,4 @@ services:
 
 networks:
   evaluation-network:
-    driver: bridge
\ No newline at end of file
+    driver: bridge
diff --git a/src/db/questions.json b/src/db/questions.json
index 0d217a4839ca18a857ce71742aac404399acfe44..b90ccc72b524b1db26d36c0c6cbcc0cdbb907d35 100644
--- a/src/db/questions.json
+++ b/src/db/questions.json
@@ -1,152 +1,528 @@
 {
-	"_comment": "Questions are stored as n-tuples, where the first value is the question, the second the key of answers dict",
-	"seminar": [
-		["Das Thema der LV hat mich schon vorher interessiert", "scale_applies"],
-		["Ich verfügte bereits vor dem Veranstaltungsbesuch über umfangreiches Wissen zu den in der Lehrveranstaltung behandelten Themengebieten", "scale_applies"],
-		["Ich war an mindestens zwei Drittel der Termine dieser Lehrveranstaltung anwesend", "y_n"],
-		["Der Stoffumfang der in der LV behandelt wird, ist für mich", "scale_height"],
-		["Der Schwierigkeitsgrad der Veranstaltung ist für mich", "scale_height"],
-		["Das Tempo der Veranstaltung ist für mich", "scale_height"],
-		["Der/die Lehrende hat die gesamte Lehrveranstaltung gut strukturiert und nachvollziehbar gegliedert", "scale_applies"],
-		["Der/die Lehrende gibt hilfreiche und zielführende Antworten auf die Nachfragen der Studierenden", "scale_applies"],
-		["Der/die Lehrende fasst regelmäßig die wichtigsten Inhalte der Lehrveranstaltung zusammen", "scale_applies"],
-		["Der/die Lehrende stellt zu Beginn einer Sitzung den Zusammenhang zur letzen Sitzung her", "scale_applies"],
-		["Der/die Lehrende stellt immer wieder Bezüge zu den bereits behandelten Lerninhalten her", "scale_applies"],
-		["Der/die Lehrende fesselt die Studierenden durch eine anregende und engagierte Vortragsweise", "scale_applies"],
-		["Der/die Lehrende vergewissert sich, dass die Studierenden zentrale Aspekte verstanden haben, bevor er/sie im Stoff weitergeht", "scale_applies"],
-		["Der/die Lehrende regt die Studierenden dazu an, die Richtigkeit ihrer Beitrage/Antworten selbst zu überprüfen", "scale_applies"],
-		["Der/die Lehrende gibt ein konstruktives Feedback auf die Beiträge/Antworten der Studierenden", "scale_applies"],
-		["Der/die Lehrende gibt den Studierenden zu wenige Rückmeldungen zu ihren Beiträgen/Antworten", "scale_applies"],
-		["Der/die Lehrende sorgt für eine angenehme Lernatmosphäre", "scale_applies"],
-		["Der/die Lehrende geht auf die Interessen der Studierenden ein", "scale_applies"],
-		["Der/die Lehrende nimmt die Beiträge der Studierenden ernst", "scale_applies"],
-		["Der/die Lehrende ist in der Lage die Studierenden für die in der Lehrveranstaltung behandelten Inhalte zu interessieren", "scale_applies"],
-		["Der/die Lehrende eröffnet den Studierenden Möglichkeiten, sich mit interessanten Inhalten eingehender zu beschäftigen", "scale_applies"],
-		["Der/die Lehrende regt die Studierenden an, sich mit den Lehrinhalten auch außerhalb der Veranstaltung auseinanderzusetzen", "scale_applies"],
-		["Der/die Lehrende macht den Stellenwert der Lerninhalte für das weitere Studium klar", "scale_applies"],
-		["Der/die Lehrende verdeutlicht den Anwendungsbezug der Lerninhalte", "scale_applies"],
-		["Der/die Lehrende hat ein sehr gutes Zeitmanagement", "scale_applies"],
-		["Der/die Lehrende steuert die Diskussion in der Lerngruppe zielführend", "scale_applies"],
-		["Der/die Lehrende passt das Anforderungsniveau der Lehrveranstaltung dem Voraussetzungen der Studierenden gut an", "scale_applies"], 
-		["Der/die Lehrende unterstützt Studierende bei Lernschwierigkeiten", "scale_applies"],
-		["Der/die Lehrende ermutigt die Studierenden bei der Aneignung schwieriger Inhalte", "scale_applies"],
-		["Ich habe die im Rahmen der Lehrveranstaltung gestellten Aufgaben umfassend bearbeitet", "scale_applies"],
-		["Ich hatte jederzeit einen Überblick über anstehende Termine und zu erledigende Aufgaben", "scale_applies"],
-		["Werden im Rahmen der Lehrveranstaltung Lernmaterialien (z.B. Videoaufzeichnungen) online zur Verfügung gestellt", "y_n"],
-		["Wenn ja, sind die Lernmaterialien hilfreich", "scale_applies"],
-		["Der Umgang mit rein digitalen Kursinhalten im Vergleich zu regelmäßigen wöchentlichen Präsenzveranstaltungen ist", "scale_diff"],
-		["In dieser Lehrveranstaltung habe ich viel dazugelernt", "scale_applies"],
-		["Insgesamt bin ich mit dieser Lehrveranstaltung zufrieden", "scale_applies"]		
-	], 
-	"tutorial": [
-		["Ich war an mindestens zwei Drittel der Termine dieser Lehrveranstaltung anwesend", "y_n"],
-		["Der/die Lehrende hat die gesamte Lehrveranstaltung gut strukturiert und nachvollziehbar gegliedert", "scale_applies"],
-		["Der/die Lehrende erklärt neue Begriffe und Konzepte klar und nachvollziehbar", "scale_applies"],
-		["Der/die Lehrende gibt hilfreiche und zielführende Antworten auf die Nachfragen der Studierenden", "scale_applies"],
-		["Der/die Lehrende stellt immer wieder Bezüge zu den bereits behandelten Lerninhalten her", "scale_applies"],
-		["Der/die Lehrende vergewissert sich, dass die Studierenden zentrale Aspekte verstanden haben, bevor er/sie im Stoff weitergeht", "scale_applies"],
-		["Der/die Lehrende setzt Modelle, Graphiken oder Schemata so ein, dass sie das Verständnis komplexer Sachverhalte erleichtern", "scale_applies"],
-		["Der/die Lehrende gestaltet Tafelbild, Folien oder Powerpoint-Präsentationen leserlich und übersichtlich", "scale_applies"],
-		["Der/die Lehrende gestaltet seine/ihre Lehrveranstaltung abwechslungsreich", "scale_applies"],
-		["Der/die Lehrende erreicht, dass die Studierenden der Lehrveranstaltung aufmerksam folgen", "scale_applies"],
-		["Der/die Lehrende ist in der Lage, die Studierenden für die in der Lehrveranstaltung behandelten Inhalte zu interessieren", "scale_applies"],
-		["Der/die Lehrende hat ein sehr gutes Zeitmanagement", "scale_applies"],
-		["Der/die Lehrende passt das Anforderungsniveau der Lehrveranstaltung den Voraussetzungen der Studierenden gut an", "scale_applies"],
-		["Der/die Lehrende verdeutlicht den Anwendungsbezug der Lerninhalte/des Stoffs", "scale_applies"],
-		["Der/die Lehrende macht den Stellenwert der Lerninhalte für das weitere Studium klar", "scale_applies"],
-		["Der/die Lehrende fesselt die Studierenden durch eine anregende und engagierte Vortragsweise", "scale_applies"],
-		["Der/die Lehrende regt die Studierenden an, sich mit den Lerninhalten auch außerhalb der Veranstaltung auseinanderzusetzen", "scale_applies"],
-		["Ich habe die im Rahmen der Lehrveranstaltung gestellten Aufgaben umfassend bearbeitet", "scale_applies"],
-		["Ich hatte jederzeit einen Überblick über anstehende Termine und zu erledigende Aufgaben", "scale_applies"],
-		["Wieviele Stunden pro Woche brauchen Sie ungefähr für die Vor- und Nachbereitung der Lehrveranstaltung (inklusive Hausaufgaben)", "scale_time"],
-		["Werden im Rahmen der Lehrveranstaltung Lernmaterialien (z.B. Videoaufzeichnungen) online zur Verfügung gestellt", "y_n"],
-		["Wenn ja, sind die Lernmaterialien hilfreich", "scale_applies"],
-		["Der Umgang mit rein digitalen Kursinhalten im Vergleich zu regelmäßigen wöchentlichen Präsenzveranstaltungen ist", "scale_diff"],
-		["In dieser Lehrveranstaltung habe ich viel dazugelernt", "scale_applies"],
-		["Insgesamt bin ich mit dieser Lehrveranstaltung zufrieden", "scale_applies"]
-	],
-	"lecture": [
-		["Ich war an mindestens zwei Drittel der Termine dieser Lehrveranstaltung anwesend", "y_n"],
-		["Der Stoffumfang, der in der Veranstaltung behandelt wird, ist für mich", "scale_height"],
-		["Der Schwierigkeitsgrad der Veranstaltung ist für mich", "scale_height"],
-		["Das Tempo der Veranstaltung ist für mich", "scale_height"],
-		["Der/die Lehrende hat die gesamte Lehrveranstaltung gut strukturiert und nachvollziehbar gegliedert", "scale_applies"],
-		["Der/die Lehrende erklärt neue Begriffe und Konzepte klar und nachvollziehbar", "scale_applies"],
-		["Der/die Lehrende gibt hilfreiche und zielführende Antworten auf die Nachfragen der Studierenden", "scale_applies"],
-		["Der/die Lehrende stellt immer wieder Bezüge zu den bereits behandelten Lerninhalten her", "scale_applies"],
-		["Der/die Lehrende vergewissert sich, dass die Studierenden zentrale Aspekte verstanden haben, bevor er/sie im Stoff weitergeht", "scale_applies"],
-		["Der/die Lehrende setzt Modelle, Graphiken oder Schemata so ein, dass sie das Verständnis komplexer Sachverhalte erleichtern", "scale_applies"],
-		["Der/die Lehrende gestaltet Tafelbild, Folien oder Powerpoint-Präsentationen leserlich und übersichtlich", "scale_applies"],
-		["Der/die Lehrende gestaltet seine/ihre Lehrveranstaltung abwechslungsreich", "scale_applies"],
-		["Der/die Lehrende erreicht, dass die Studierenden der Lehrveranstaltung aufmerksam folgen", "scale_applies"],
-		["Der/die Lehrende ist in der Lage, die Studierenden für die in der Lehrveranstaltung behandelten Inhalte zu interessieren", "scale_applies"],
-		["Der/die Lehrende hat ein sehr gutes Zeitmanagement", "scale_applies"],
-		["Der/die Lehrende passt das Anforderungsniveau der Lehrveranstaltung den Voraussetzungen der Studierenden gut an", "scale_applies"],
-		["Der/die Lehrende verdeutlicht den Anwendungsbezug der Lerninhalte/des Stoffs", "scale_applies"],
-		["Der/die Lehrende macht den Stellenwert der Lerninhalte für das weitere Studium klar", "scale_applies"],
-		["Der/die Lehrende fesselt die Studierenden durch eine anregende und engagierte Vortragsweise", "scale_applies"],
-		["Der/die Lehrende regt die Studierenden an, sich mit den Lerninhalten auch außerhalb der Veranstaltung auseinanderzusetzen", "scale_applies"],
-		["Ich habe die im Rahmen der Lehrveranstaltung gestellten Aufgaben umfassend bearbeitet", "scale_applies"],
-		["Ich hatte jederzeit einen Überblick über anstehende Termine und zu erledigende Aufgaben", "scale_applies"],
-		["Wie viele Stunden pro Woche brauchen Sie ungefähr für die Vor- und Nachbereitung der Lehrveranstaltung (inklusive Hausaufgaben)", "scale_time"],
-		["Werden im Rahmen der Lehrveranstaltung Lernmaterialien (z.B. Videoaufzeichnungen) online zur Verfügung gestellt", "y_n"],
-		["Wenn ja, sind die Lernmaterialien hilfreich", "scale_applies"],
-		["Der Umgang mit rein digitalen Kursinhalten im Vergleich zu regelmäßigen wöchentlichen Präsenzveranstaltungen ist", "scale_diff"],
-		["In dieser Lehrveranstaltung habe ich viel dazugelernt", "scale_applies"],
-		["Insgesamt bin ich mit dieser Lehrveranstaltung zufrieden", "scale_applies"]
-	],
-	"LeKo": [
-		["Das Thema der Lehrveranstaltung hat mich schon vorher interessiert", "scale_applies_alt"],
-		["Ich verfügte bereits vor dem Lehrveranstaltungsbesuch über umfangreiches Wissen zu den in der LV behandelten Themengebieten", "scale_applies_alt"],
-		["Ich verfügte bereits vor der Lehrveranstaltung über viel Erfahrung mit E-Learning", "scale_applies_alt"],
-		["Ich habe an mindestens zwei Drittel der Termine dieser Lehrveranstaltung teilgenommen bzw. die Inhalte der Lerneinheiten bearbeitet", "y_n"],
-		["Der Stoffumfang, der in der Veranstaltung behandelt wird, ist für mich", "scale_height"],
-		["Der Schwierigkeitsgrad der Veranstaltung ist für mich", "scale_height"],
-		["Der/die Lehrende hat die gesamte Lehrveranstaltung gut strukturiert und nachvollziehbar gegliedert", "scale_applies_alt"],
-		["Der/die Lehrende verdeutlicht die Lernziele zu Beginn jedes Termins/jeder Lerneinheit", "scale_applies_alt"],
-		["Der/die Lehrende präsentiert die Lerninhalte/Lernmaterialien stimmig und kohärent", "scale_applies_alt"],
-		["Der/die Lehrende erklärt neue Begriffe und Konzepte klar und nachvollziehbar", "scale_applies_alt"],
-		["Der/die Lehrende ist in der Lage, auch einen komplexen Sachverhalt verständlich zu erklären", "scale_applies_alt"],
-		["Der/die Lehrende gibt anschauliche Beispiele, die zum Verständnis des Lerninhaltes/Stoffs beitragen", "scale_applies_alt"],
-		["Der/die Lehrende gibt hilfreiche und zielführende Antworten auf die Nachfragen der Studierenden", "scale_applies_alt"],
-		["Der/die Lehrende fasst regelmäßig die wichtigsten Inhalte der LV zusammen", "scale_applies_alt"],
-		["Der/die Lehrende stellt zu Beginn eines Termins/einer Lerneinheit den Zusammenhang zu dem Termin/der Lerneinheit davor her", "scale_applies_alt"],
-		["Der/die Lehrende stellt immer wieder Bezüge zu bereits behandelten Lerninhalten her", "scale_applies_alt"],
-		["Der/die Lehrende stellt Fragen, die den Studierenden die Gelegenheit geben zu überprüfen, ob sie den Inhalt verstanden haben", "scale_applies_alt"],
-		["Der/die Lehrende vergewissert sich, dass die Studierenden zentrale Aspekte verstanden haben, bevor er/sie im Stoff weitergeht", "scale_applies_alt"],
-		["Der/die Lehrende ist bei Fragen gut erreichbar", "scale_applies_alt"],
-		["Der/die Lehrende gibt mir ein konstruktives Feedback zu meinen Beiträgen/Lösungen", "scale_applies_alt"],
-		["Der/die Lehrende gibt mir zu wenige Rückmeldungen zu meinen Beiträgen/Lösungen", "scale_applies_alt"],
-		["Der/die Lehrende gibt mir konkrete Hinweise zur Verbesserung meiner Leistungen", "scale_applies_alt"],
-		["Der/die Lehrende setzt Modelle, Graphiken oder Schemata so ein, dass sie das Verständnis komplexer Sachverhalte erleichtern", "scale_applies_alt"],
-		["Der/die Lehrende setzt zielführend audiovisuelle Medien (z.B. Powerpoint-Präsentationen, Ton- oder Bildmaterial) zur Vermittlung von Sachverhalten ein", "scale_applies_alt"],
-		["Der/die Lehrende gestaltet Tafelbild/Whiteboard, Folien oder Powerpoint-Präsentationen leserlich und übersichtlich", "scale_applies_alt"],
-		["Der/die Lehrende sorgt für eine angenehme Lernatmosphäre", "scale_applies_alt"],
-		["Der/die Lehrende geht auf die Interessen der Studierenden ein", "scale_applies_alt"],
-		["Der/die Lehrende nimmt die Fragen/Anregungen der Studierenden ernst", "scale_applies_alt"],
-		["Der/die Lehrende gestaltet seine/ihre Lehrveranstaltung abwechslungsreich", "scale_applies_alt"],
-		["Der/die Lehrende trägt anregend und engagiert vor", "scale_applies_alt"],
-		["Der/die Lehrende erreicht, dass ich der Lehrveranstaltung aufmerksam folge", "scale_applies_alt"],
-		["Der/die Lehrende ist in der Lage, mich für die in der Lehrveranstaltung behandelten Inhalte zu interessieren", "scale_applies_alt"],
-		["Der/die Lehrende erreicht, dass es mir Spaß macht, an der Lehrveranstaltung teilzunehmen bzw. die Inhalte der Lerneinheiten zu bearbeiten", "scale_applies_alt"],
-		["Der/die Lehrende eröffnet den Studierenden Möglichkeiten, sich mit interessanten Inhalten eingehender zu beschäftigen", "scale_applies_alt"],
-		["Der/die Lehrende regt die Studierenden an, sich mit den Lehrinhalten auch über die Lehrveranstaltung hinaus auseinanderzusetzen", "scale_applies_alt"],
-		["Der/die Lehrende macht den Stellenwert der Lerninhalte für das weitere Studium klar", "scale_applies_alt"],
-		["Der/die Lehrende verdeutlicht den Anwendungsbezug der Lerninhalte/des Stoffs", "scale_applies_alt"],
-		["Der/die Lehrende passt das Anforderungsniveau der Lehrveranstaltung den Voraussetzungen der Studierenden gut an", "scale_applies_alt"],
-		["Der/die Lehrende hat klare Verhaltensregeln für unsere Zusammenarbeit in dieser Lehrveranstaltung kommuniziert", "scale_applies_alt"],
-		["Der/die Lehrende achtet darauf, dass wir die aufgestellten Verhaltensregeln einhalten", "scale_applies_alt"],
-		["Der/die Lehrende bringt wichtige Inhalte gut auf den Punkt", "scale_applies_alt"],
-		["Der/die Lehrende nutzt die verfügbare Zeit effektiv", "scale_applies_alt"],
-		["In dieser Lehrveranstaltung habe ich viel dazugelernt", "scale_applies_alt"],
-		["Insgesamt bin ich mit dieser Lehrveranstaltung zufrieden", "scale_applies_alt"]
-	],
-	"answers": {
-		"scale_applies": ["trifft gar nicht zu", "trifft nicht zu", "trifft eher nicht zu", "trifft teils, teils zu", "trifft eher zu", "trifft zu", "trifft völlig zu"],
-		"scale_applies_alt": ["trifft gar nicht zu", "trifft nicht zu", "trifft eher nicht zu", "trifft teils, teils zu", "trifft eher zu", "trifft zu", "trifft völlig zu", "kann ich nicht beurteilen"],
-		"y_n": ["Ja", "Nein"],
-		"scale_height": ["viel zu niedrig", "eher zu niedrig", "genau richtig", "eher zu hoch", "viel zu hoch"],
-		"scale_diff": ["schwerer", "kein Unterschied", "leichter", "kann ich nicht beurteilen"],
-		"scale_time": ["0-2", ">2-4", ">4-6", ">6-8", ">8-10", ">10-12", ">12"]
-	}
-}
\ No newline at end of file
+  "_comment": "Questions are stored as n-tuples, where the first value is the question, the second the key of answers dict",
+  "seminar": [
+    ["Das Thema der LV hat mich schon vorher interessiert", "scale_applies"],
+    [
+      "Ich verfügte bereits vor dem Veranstaltungsbesuch über umfangreiches Wissen zu den in der Lehrveranstaltung behandelten Themengebieten",
+      "scale_applies"
+    ],
+    [
+      "Ich war an mindestens zwei Drittel der Termine dieser Lehrveranstaltung anwesend",
+      "y_n"
+    ],
+    [
+      "Der Stoffumfang der in der LV behandelt wird, ist für mich",
+      "scale_height"
+    ],
+    ["Der Schwierigkeitsgrad der Veranstaltung ist für mich", "scale_height"],
+    ["Das Tempo der Veranstaltung ist für mich", "scale_height"],
+    [
+      "Der/die Lehrende hat die gesamte Lehrveranstaltung gut strukturiert und nachvollziehbar gegliedert",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende gibt hilfreiche und zielführende Antworten auf die Nachfragen der Studierenden",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende fasst regelmäßig die wichtigsten Inhalte der Lehrveranstaltung zusammen",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende stellt zu Beginn einer Sitzung den Zusammenhang zur letzen Sitzung her",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende stellt immer wieder Bezüge zu den bereits behandelten Lerninhalten her",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende fesselt die Studierenden durch eine anregende und engagierte Vortragsweise",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende vergewissert sich, dass die Studierenden zentrale Aspekte verstanden haben, bevor er/sie im Stoff weitergeht",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende regt die Studierenden dazu an, die Richtigkeit ihrer Beitrage/Antworten selbst zu überprüfen",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende gibt ein konstruktives Feedback auf die Beiträge/Antworten der Studierenden",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende gibt den Studierenden zu wenige Rückmeldungen zu ihren Beiträgen/Antworten",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende sorgt für eine angenehme Lernatmosphäre",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende geht auf die Interessen der Studierenden ein",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende nimmt die Beiträge der Studierenden ernst",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende ist in der Lage die Studierenden für die in der Lehrveranstaltung behandelten Inhalte zu interessieren",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende eröffnet den Studierenden Möglichkeiten, sich mit interessanten Inhalten eingehender zu beschäftigen",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende regt die Studierenden an, sich mit den Lehrinhalten auch außerhalb der Veranstaltung auseinanderzusetzen",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende macht den Stellenwert der Lerninhalte für das weitere Studium klar",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende verdeutlicht den Anwendungsbezug der Lerninhalte",
+      "scale_applies"
+    ],
+    ["Der/die Lehrende hat ein sehr gutes Zeitmanagement", "scale_applies"],
+    [
+      "Der/die Lehrende steuert die Diskussion in der Lerngruppe zielführend",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende passt das Anforderungsniveau der Lehrveranstaltung dem Voraussetzungen der Studierenden gut an",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende unterstützt Studierende bei Lernschwierigkeiten",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende ermutigt die Studierenden bei der Aneignung schwieriger Inhalte",
+      "scale_applies"
+    ],
+    [
+      "Ich habe die im Rahmen der Lehrveranstaltung gestellten Aufgaben umfassend bearbeitet",
+      "scale_applies"
+    ],
+    [
+      "Ich hatte jederzeit einen Überblick über anstehende Termine und zu erledigende Aufgaben",
+      "scale_applies"
+    ],
+    [
+      "Werden im Rahmen der Lehrveranstaltung Lernmaterialien (z.B. Videoaufzeichnungen) online zur Verfügung gestellt",
+      "y_n"
+    ],
+    ["Wenn ja, sind die Lernmaterialien hilfreich", "scale_applies"],
+    [
+      "Der Umgang mit rein digitalen Kursinhalten im Vergleich zu regelmäßigen wöchentlichen Präsenzveranstaltungen ist",
+      "scale_diff"
+    ],
+    ["In dieser Lehrveranstaltung habe ich viel dazugelernt", "scale_applies"],
+    [
+      "Insgesamt bin ich mit dieser Lehrveranstaltung zufrieden",
+      "scale_applies"
+    ]
+  ],
+  "tutorial": [
+    [
+      "Ich war an mindestens zwei Drittel der Termine dieser Lehrveranstaltung anwesend",
+      "y_n"
+    ],
+    [
+      "Der/die Lehrende hat die gesamte Lehrveranstaltung gut strukturiert und nachvollziehbar gegliedert",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende erklärt neue Begriffe und Konzepte klar und nachvollziehbar",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende gibt hilfreiche und zielführende Antworten auf die Nachfragen der Studierenden",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende stellt immer wieder Bezüge zu den bereits behandelten Lerninhalten her",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende vergewissert sich, dass die Studierenden zentrale Aspekte verstanden haben, bevor er/sie im Stoff weitergeht",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende setzt Modelle, Graphiken oder Schemata so ein, dass sie das Verständnis komplexer Sachverhalte erleichtern",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende gestaltet Tafelbild, Folien oder Powerpoint-Präsentationen leserlich und übersichtlich",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende gestaltet seine/ihre Lehrveranstaltung abwechslungsreich",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende erreicht, dass die Studierenden der Lehrveranstaltung aufmerksam folgen",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende ist in der Lage, die Studierenden für die in der Lehrveranstaltung behandelten Inhalte zu interessieren",
+      "scale_applies"
+    ],
+    ["Der/die Lehrende hat ein sehr gutes Zeitmanagement", "scale_applies"],
+    [
+      "Der/die Lehrende passt das Anforderungsniveau der Lehrveranstaltung den Voraussetzungen der Studierenden gut an",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende verdeutlicht den Anwendungsbezug der Lerninhalte/des Stoffs",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende macht den Stellenwert der Lerninhalte für das weitere Studium klar",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende fesselt die Studierenden durch eine anregende und engagierte Vortragsweise",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende regt die Studierenden an, sich mit den Lerninhalten auch außerhalb der Veranstaltung auseinanderzusetzen",
+      "scale_applies"
+    ],
+    [
+      "Ich habe die im Rahmen der Lehrveranstaltung gestellten Aufgaben umfassend bearbeitet",
+      "scale_applies"
+    ],
+    [
+      "Ich hatte jederzeit einen Überblick über anstehende Termine und zu erledigende Aufgaben",
+      "scale_applies"
+    ],
+    [
+      "Wieviele Stunden pro Woche brauchen Sie ungefähr für die Vor- und Nachbereitung der Lehrveranstaltung (inklusive Hausaufgaben)",
+      "scale_time"
+    ],
+    [
+      "Werden im Rahmen der Lehrveranstaltung Lernmaterialien (z.B. Videoaufzeichnungen) online zur Verfügung gestellt",
+      "y_n"
+    ],
+    ["Wenn ja, sind die Lernmaterialien hilfreich", "scale_applies"],
+    [
+      "Der Umgang mit rein digitalen Kursinhalten im Vergleich zu regelmäßigen wöchentlichen Präsenzveranstaltungen ist",
+      "scale_diff"
+    ],
+    ["In dieser Lehrveranstaltung habe ich viel dazugelernt", "scale_applies"],
+    [
+      "Insgesamt bin ich mit dieser Lehrveranstaltung zufrieden",
+      "scale_applies"
+    ]
+  ],
+  "lecture": [
+    [
+      "Ich war an mindestens zwei Drittel der Termine dieser Lehrveranstaltung anwesend",
+      "y_n"
+    ],
+    [
+      "Der Stoffumfang, der in der Veranstaltung behandelt wird, ist für mich",
+      "scale_height"
+    ],
+    ["Der Schwierigkeitsgrad der Veranstaltung ist für mich", "scale_height"],
+    ["Das Tempo der Veranstaltung ist für mich", "scale_height"],
+    [
+      "Der/die Lehrende hat die gesamte Lehrveranstaltung gut strukturiert und nachvollziehbar gegliedert",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende erklärt neue Begriffe und Konzepte klar und nachvollziehbar",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende gibt hilfreiche und zielführende Antworten auf die Nachfragen der Studierenden",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende stellt immer wieder Bezüge zu den bereits behandelten Lerninhalten her",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende vergewissert sich, dass die Studierenden zentrale Aspekte verstanden haben, bevor er/sie im Stoff weitergeht",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende setzt Modelle, Graphiken oder Schemata so ein, dass sie das Verständnis komplexer Sachverhalte erleichtern",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende gestaltet Tafelbild, Folien oder Powerpoint-Präsentationen leserlich und übersichtlich",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende gestaltet seine/ihre Lehrveranstaltung abwechslungsreich",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende erreicht, dass die Studierenden der Lehrveranstaltung aufmerksam folgen",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende ist in der Lage, die Studierenden für die in der Lehrveranstaltung behandelten Inhalte zu interessieren",
+      "scale_applies"
+    ],
+    ["Der/die Lehrende hat ein sehr gutes Zeitmanagement", "scale_applies"],
+    [
+      "Der/die Lehrende passt das Anforderungsniveau der Lehrveranstaltung den Voraussetzungen der Studierenden gut an",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende verdeutlicht den Anwendungsbezug der Lerninhalte/des Stoffs",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende macht den Stellenwert der Lerninhalte für das weitere Studium klar",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende fesselt die Studierenden durch eine anregende und engagierte Vortragsweise",
+      "scale_applies"
+    ],
+    [
+      "Der/die Lehrende regt die Studierenden an, sich mit den Lerninhalten auch außerhalb der Veranstaltung auseinanderzusetzen",
+      "scale_applies"
+    ],
+    [
+      "Ich habe die im Rahmen der Lehrveranstaltung gestellten Aufgaben umfassend bearbeitet",
+      "scale_applies"
+    ],
+    [
+      "Ich hatte jederzeit einen Überblick über anstehende Termine und zu erledigende Aufgaben",
+      "scale_applies"
+    ],
+    [
+      "Wie viele Stunden pro Woche brauchen Sie ungefähr für die Vor- und Nachbereitung der Lehrveranstaltung (inklusive Hausaufgaben)",
+      "scale_time"
+    ],
+    [
+      "Werden im Rahmen der Lehrveranstaltung Lernmaterialien (z.B. Videoaufzeichnungen) online zur Verfügung gestellt",
+      "y_n"
+    ],
+    ["Wenn ja, sind die Lernmaterialien hilfreich", "scale_applies"],
+    [
+      "Der Umgang mit rein digitalen Kursinhalten im Vergleich zu regelmäßigen wöchentlichen Präsenzveranstaltungen ist",
+      "scale_diff"
+    ],
+    ["In dieser Lehrveranstaltung habe ich viel dazugelernt", "scale_applies"],
+    [
+      "Insgesamt bin ich mit dieser Lehrveranstaltung zufrieden",
+      "scale_applies"
+    ]
+  ],
+  "LeKo": [
+    [
+      "Das Thema der Lehrveranstaltung hat mich schon vorher interessiert",
+      "scale_applies_alt"
+    ],
+    [
+      "Ich verfügte bereits vor dem Lehrveranstaltungsbesuch über umfangreiches Wissen zu den in der LV behandelten Themengebieten",
+      "scale_applies_alt"
+    ],
+    [
+      "Ich verfügte bereits vor der Lehrveranstaltung über viel Erfahrung mit E-Learning",
+      "scale_applies_alt"
+    ],
+    [
+      "Ich habe an mindestens zwei Drittel der Termine dieser Lehrveranstaltung teilgenommen bzw. die Inhalte der Lerneinheiten bearbeitet",
+      "y_n"
+    ],
+    [
+      "Der Stoffumfang, der in der Veranstaltung behandelt wird, ist für mich",
+      "scale_height"
+    ],
+    ["Der Schwierigkeitsgrad der Veranstaltung ist für mich", "scale_height"],
+    [
+      "Der/die Lehrende hat die gesamte Lehrveranstaltung gut strukturiert und nachvollziehbar gegliedert",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende verdeutlicht die Lernziele zu Beginn jedes Termins/jeder Lerneinheit",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende präsentiert die Lerninhalte/Lernmaterialien stimmig und kohärent",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende erklärt neue Begriffe und Konzepte klar und nachvollziehbar",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende ist in der Lage, auch einen komplexen Sachverhalt verständlich zu erklären",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende gibt anschauliche Beispiele, die zum Verständnis des Lerninhaltes/Stoffs beitragen",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende gibt hilfreiche und zielführende Antworten auf die Nachfragen der Studierenden",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende fasst regelmäßig die wichtigsten Inhalte der LV zusammen",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende stellt zu Beginn eines Termins/einer Lerneinheit den Zusammenhang zu dem Termin/der Lerneinheit davor her",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende stellt immer wieder Bezüge zu bereits behandelten Lerninhalten her",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende stellt Fragen, die den Studierenden die Gelegenheit geben zu überprüfen, ob sie den Inhalt verstanden haben",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende vergewissert sich, dass die Studierenden zentrale Aspekte verstanden haben, bevor er/sie im Stoff weitergeht",
+      "scale_applies_alt"
+    ],
+    ["Der/die Lehrende ist bei Fragen gut erreichbar", "scale_applies_alt"],
+    [
+      "Der/die Lehrende gibt mir ein konstruktives Feedback zu meinen Beiträgen/Lösungen",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende gibt mir zu wenige Rückmeldungen zu meinen Beiträgen/Lösungen",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende gibt mir konkrete Hinweise zur Verbesserung meiner Leistungen",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende setzt Modelle, Graphiken oder Schemata so ein, dass sie das Verständnis komplexer Sachverhalte erleichtern",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende setzt zielführend audiovisuelle Medien (z.B. Powerpoint-Präsentationen, Ton- oder Bildmaterial) zur Vermittlung von Sachverhalten ein",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende gestaltet Tafelbild/Whiteboard, Folien oder Powerpoint-Präsentationen leserlich und übersichtlich",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende sorgt für eine angenehme Lernatmosphäre",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende geht auf die Interessen der Studierenden ein",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende nimmt die Fragen/Anregungen der Studierenden ernst",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende gestaltet seine/ihre Lehrveranstaltung abwechslungsreich",
+      "scale_applies_alt"
+    ],
+    ["Der/die Lehrende trägt anregend und engagiert vor", "scale_applies_alt"],
+    [
+      "Der/die Lehrende erreicht, dass ich der Lehrveranstaltung aufmerksam folge",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende ist in der Lage, mich für die in der Lehrveranstaltung behandelten Inhalte zu interessieren",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende erreicht, dass es mir Spaß macht, an der Lehrveranstaltung teilzunehmen bzw. die Inhalte der Lerneinheiten zu bearbeiten",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende eröffnet den Studierenden Möglichkeiten, sich mit interessanten Inhalten eingehender zu beschäftigen",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende regt die Studierenden an, sich mit den Lehrinhalten auch über die Lehrveranstaltung hinaus auseinanderzusetzen",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende macht den Stellenwert der Lerninhalte für das weitere Studium klar",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende verdeutlicht den Anwendungsbezug der Lerninhalte/des Stoffs",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende passt das Anforderungsniveau der Lehrveranstaltung den Voraussetzungen der Studierenden gut an",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende hat klare Verhaltensregeln für unsere Zusammenarbeit in dieser Lehrveranstaltung kommuniziert",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende achtet darauf, dass wir die aufgestellten Verhaltensregeln einhalten",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende bringt wichtige Inhalte gut auf den Punkt",
+      "scale_applies_alt"
+    ],
+    [
+      "Der/die Lehrende nutzt die verfügbare Zeit effektiv",
+      "scale_applies_alt"
+    ],
+    [
+      "In dieser Lehrveranstaltung habe ich viel dazugelernt",
+      "scale_applies_alt"
+    ],
+    [
+      "Insgesamt bin ich mit dieser Lehrveranstaltung zufrieden",
+      "scale_applies_alt"
+    ]
+  ],
+  "answers": {
+    "scale_applies": [
+      "trifft gar nicht zu",
+      "trifft nicht zu",
+      "trifft eher nicht zu",
+      "trifft teils, teils zu",
+      "trifft eher zu",
+      "trifft zu",
+      "trifft völlig zu"
+    ],
+    "scale_applies_alt": [
+      "trifft gar nicht zu",
+      "trifft nicht zu",
+      "trifft eher nicht zu",
+      "trifft teils, teils zu",
+      "trifft eher zu",
+      "trifft zu",
+      "trifft völlig zu",
+      "kann ich nicht beurteilen"
+    ],
+    "y_n": ["Ja", "Nein"],
+    "scale_height": [
+      "viel zu niedrig",
+      "eher zu niedrig",
+      "genau richtig",
+      "eher zu hoch",
+      "viel zu hoch"
+    ],
+    "scale_diff": [
+      "schwerer",
+      "kein Unterschied",
+      "leichter",
+      "kann ich nicht beurteilen"
+    ],
+    "scale_time": ["0-2", ">2-4", ">4-6", ">6-8", ">8-10", ">10-12", ">12"]
+  }
+}
diff --git a/src/db/schema.sql b/src/db/schema.sql
index d5dc83de1e553bca1403b53eee3dfdc9e41bc482..4129f904e985ded6460486a02e68defbb3a87d02 100644
--- a/src/db/schema.sql
+++ b/src/db/schema.sql
@@ -2,84 +2,63 @@ CREATE DATABASE evaluation;
 -- Ensures Database connection
 \connect evaluation
 
-
-
-
 CREATE TABLE "answer" (
-  "id" varchar(36) NOT NULL PRIMARY KEY,
-  "content" text NOT NULL
+  "id" uuid NOT NULL PRIMARY KEY,
+  "content" text NOT NULL,
+  "value" integer NOT NULL
 );
 
-
-
-
-
-INSERT INTO "answer" ("id", "content") VALUES
-('ebd484ba-e7bf-11ee-9404-00620b2c9060', 'trifft gar nicht zu'),
-('ebd5ac7e-e7bf-11ee-9404-00620b2c9060', 'trifft nicht zu'),
-('ebd6ba7d-e7bf-11ee-9404-00620b2c9060', 'trifft eher nicht zu'),
-('ebd7cd53-e7bf-11ee-9404-00620b2c9060', 'trifft teils, teils zu'),
-('ebd91145-e7bf-11ee-9404-00620b2c9060', 'trifft eher zu'),
-('ebda3a5c-e7bf-11ee-9404-00620b2c9060', 'trifft zu'),
-('ebdb9745-e7bf-11ee-9404-00620b2c9060', 'trifft völlig zu'),
-('ebdcd500-e7bf-11ee-9404-00620b2c9060', 'Ja'),
-('ebde1a47-e7bf-11ee-9404-00620b2c9060', 'Nein'),
-('ebdf7344-e7bf-11ee-9404-00620b2c9060', 'viel zu niedrig'),
-('ebe0d460-e7bf-11ee-9404-00620b2c9060', 'eher zu niedrig'),
-('ebe243e9-e7bf-11ee-9404-00620b2c9060', 'genau richtig'),
-('ebe3b50b-e7bf-11ee-9404-00620b2c9060', 'eher zu hoch'),
-('ebe4fa46-e7bf-11ee-9404-00620b2c9060', 'viel zu hoch'),
-('ebe63af1-e7bf-11ee-9404-00620b2c9060', 'schwerer'),
-('ebe7964b-e7bf-11ee-9404-00620b2c9060', 'kein Unterschied'),
-('ebe8ea7c-e7bf-11ee-9404-00620b2c9060', 'leichter'),
-('ebea3f96-e7bf-11ee-9404-00620b2c9060', 'kann ich nicht beurteilen'),
-('ebeb9a3c-e7bf-11ee-9404-00620b2c9060', '0-2'),
-('ebecf201-e7bf-11ee-9404-00620b2c9060', '>2-4'),
-('ebee3a3e-e7bf-11ee-9404-00620b2c9060', '>4-6'),
-('ebef5a7a-e7bf-11ee-9404-00620b2c9060', '>6-8'),
-('ebf0b7d8-e7bf-11ee-9404-00620b2c9060', '>8-10'),
-('ebf1fe80-e7bf-11ee-9404-00620b2c9060', '>10-12'),
-('ebf30aa3-e7bf-11ee-9404-00620b2c9060', '>12');
-
-
-
-CREATE TABLE "answer-possibility" (
+INSERT INTO "answer" ("id", "content", "value") VALUES
+('ebd484ba-e7bf-11ee-9404-00620b2c9060', 'trifft gar nicht zu', -3),
+('ebd5ac7e-e7bf-11ee-9404-00620b2c9060', 'trifft nicht zu', -2),
+('ebd6ba7d-e7bf-11ee-9404-00620b2c9060', 'trifft eher nicht zu', -1),
+('ebd7cd53-e7bf-11ee-9404-00620b2c9060', 'trifft teils, teils zu', 0),
+('ebd91145-e7bf-11ee-9404-00620b2c9060', 'trifft eher zu', 1),
+('ebda3a5c-e7bf-11ee-9404-00620b2c9060', 'trifft zu', 2),
+('ebdb9745-e7bf-11ee-9404-00620b2c9060', 'trifft völlig zu', 3),
+('ebdcd500-e7bf-11ee-9404-00620b2c9060', 'Ja', 1),
+('ebde1a47-e7bf-11ee-9404-00620b2c9060', 'Nein', 0),
+('ebdf7344-e7bf-11ee-9404-00620b2c9060', 'viel zu niedrig', -2),
+('ebe0d460-e7bf-11ee-9404-00620b2c9060', 'eher zu niedrig', -1),
+('ebe243e9-e7bf-11ee-9404-00620b2c9060', 'genau richtig', 2),
+('ebe3b50b-e7bf-11ee-9404-00620b2c9060', 'eher zu hoch', -1),
+('ebe4fa46-e7bf-11ee-9404-00620b2c9060', 'viel zu hoch', -2),
+('ebe63af1-e7bf-11ee-9404-00620b2c9060', 'schwerer', -1),
+('ebe7964b-e7bf-11ee-9404-00620b2c9060', 'kein Unterschied', 1),
+('ebe8ea7c-e7bf-11ee-9404-00620b2c9060', 'leichter', 1),
+('ebea3f96-e7bf-11ee-9404-00620b2c9060', 'kann ich nicht beurteilen', 0),
+('ebeb9a3c-e7bf-11ee-9404-00620b2c9060', '0-2', 0),
+('ebecf201-e7bf-11ee-9404-00620b2c9060', '>2-4', 0),
+('ebee3a3e-e7bf-11ee-9404-00620b2c9060', '>4-6', 0),
+('ebef5a7a-e7bf-11ee-9404-00620b2c9060', '>6-8', 0),
+('ebf0b7d8-e7bf-11ee-9404-00620b2c9060', '>8-10', 0),
+('ebf1fe80-e7bf-11ee-9404-00620b2c9060', '>10-12', 0),
+('ebf30aa3-e7bf-11ee-9404-00620b2c9060', '>12', 0);
+
+CREATE TABLE scale (
   "id" varchar(256) NOT NULL PRIMARY KEY
 );
 
-
-
-
-
-INSERT INTO "answer-possibility" ("id") VALUES
+INSERT INTO scale ("id") VALUES
 ('scale_applies'),
 ('scale_difficulty'),
 ('scale_height'),
 ('scale_time'),
 ('y_n');
 
-
-
-
-
-
 CREATE TABLE "answer-belongs-to" (
-  "answer" varchar(36) NOT NULL,
-  "possibility" varchar(36) NOT NULL,
-  PRIMARY KEY("answer", "possibility"),
+  "answer" uuid NOT NULL,
+  "scale" varchar(256) NOT NULL,
+  PRIMARY KEY("answer", "scale"),
   CONSTRAINT fk_answer
     FOREIGN KEY("answer")
       REFERENCES "answer"("id"),
-  CONSTRAINT fk_possibility
-    FOREIGN KEY("possibility")
-      REFERENCES "answer-possibility"("id")
+  CONSTRAINT fk_scale
+    FOREIGN KEY("scale")
+      REFERENCES "scale"("id")
 );
 
-
-
-
-
-INSERT INTO "answer-belongs-to" ("answer", "possibility") VALUES
+INSERT INTO "answer-belongs-to" ("answer", "scale") VALUES
 ('ebd484ba-e7bf-11ee-9404-00620b2c9060', 'scale_applies'),
 ('ebd5ac7e-e7bf-11ee-9404-00620b2c9060', 'scale_applies'),
 ('ebd6ba7d-e7bf-11ee-9404-00620b2c9060', 'scale_applies'),
@@ -106,53 +85,28 @@ INSERT INTO "answer-belongs-to" ("answer", "possibility") VALUES
 ('ebf1fe80-e7bf-11ee-9404-00620b2c9060', 'scale_time'),
 ('ebf30aa3-e7bf-11ee-9404-00620b2c9060', 'scale_time');
 
-
-
-
-
-
-
-
-
-
-
-
 CREATE TABLE "course-type" (
-  "id" varchar(36) NOT NULL PRIMARY KEY,
+  "id" uuid NOT NULL PRIMARY KEY,
   "type" text NOT NULL
 );
 
-
-
-
-
 INSERT INTO "course-type" ("id", "type") VALUES
 ('a131b6b0-e7c4-11ee-9404-00620b2c9060', 'lecture'),
 ('a131d453-e7c4-11ee-9404-00620b2c9060', 'tutorial'),
 ('a131f499-e7c4-11ee-9404-00620b2c9060', 'seminar'),
 ('a1320e71-e7c4-11ee-9404-00620b2c9060', 'leko');
 
-
-
-
-
-
-
 CREATE TABLE "question" (
-  "id" varchar(36) NOT NULL PRIMARY KEY,
+  "id" uuid NOT NULL PRIMARY KEY,
   "content" text NOT NULL,
-  "possibilities" varchar(256) NOT NULL,
+  "scale" varchar(256) NOT NULL,
   "dimension" varchar(2),
-  CONSTRAINT fk_possibilities
-    FOREIGN KEY("possibilities")
-      REFERENCES "answer-possibility"("id")
+  CONSTRAINT fk_scale
+    FOREIGN KEY("scale")
+      REFERENCES "scale"("id")
 );
 
-
-
-
-
-INSERT INTO "question" ("id", "content", "possibilities", "dimension") VALUES
+INSERT INTO "question" ("id", "content", "scale", "dimension") VALUES
 ('fb95e4c0-e7c6-11ee-9404-00620b2c9060', 'Ich verfügte bereits vor dem Lehrveranstaltungsbesuch über umfangreiches Wissen zu den in der LV behandelten Themengebieten', 'scale_applies', NULL),
 ('fb9665de-e7c6-11ee-9404-00620b2c9060', 'Der/die Lehrende macht den Stellenwert der Lerninhalte für das weitere Studium klar', 'scale_applies', 'B2'),
 ('fb969730-e7c6-11ee-9404-00620b2c9060', 'Ich hatte jederzeit einen Überblick über anstehende Termine und zu erledigende Aufgaben', 'scale_applies', 'A1'),
@@ -229,15 +183,9 @@ INSERT INTO "question" ("id", "content", "possibilities", "dimension") VALUES
 ('fba02cac-e7c6-11ee-9404-00620b2c9060', 'Der/die Lehrende hat ein sehr gutes Zeitmanagement', 'scale_applies', 'C2'),
 ('fba05299-e7c6-11ee-9404-00620b2c9060', 'Der/die Lehrende gestaltet Tafelbild/Whiteboard, Folien oder Powerpoint-Präsentationen leserlich und übersichtlich', 'scale_applies', 'A1');
 
-
-
-
-
-
-
 CREATE TABLE "question-belongs-to" (
-  "question" varchar(36) NOT NULL,
-  "course" varchar(36) NOT NULL,
+  "question" uuid NOT NULL,
+  "course" uuid NOT NULL,
   PRIMARY KEY("question", "course"),
   CONSTRAINT fk_question
     FOREIGN KEY("question")
@@ -247,10 +195,6 @@ CREATE TABLE "question-belongs-to" (
       REFERENCES "course-type"("id")
 );
 
-
-
-
-
 INSERT INTO "question-belongs-to" ("question", "course") VALUES
 ('fb95e4c0-e7c6-11ee-9404-00620b2c9060', 'a1320e71-e7c4-11ee-9404-00620b2c9060'),
 ('fb9665de-e7c6-11ee-9404-00620b2c9060', 'a131b6b0-e7c4-11ee-9404-00620b2c9060'),
@@ -384,4 +328,35 @@ INSERT INTO "question-belongs-to" ("question", "course") VALUES
 ('fba02cac-e7c6-11ee-9404-00620b2c9060', 'a131b6b0-e7c4-11ee-9404-00620b2c9060'),
 ('fba02cac-e7c6-11ee-9404-00620b2c9060', 'a131d453-e7c4-11ee-9404-00620b2c9060'),
 ('fba02cac-e7c6-11ee-9404-00620b2c9060', 'a131f499-e7c4-11ee-9404-00620b2c9060'),
-('fba05299-e7c6-11ee-9404-00620b2c9060', 'a1320e71-e7c4-11ee-9404-00620b2c9060');
\ No newline at end of file
+('fba05299-e7c6-11ee-9404-00620b2c9060', 'a1320e71-e7c4-11ee-9404-00620b2c9060');
+
+-- Evaluation results
+CREATE TABLE "course" (
+  "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
+  "number" varchar(256) NOT NULL,
+  "name" varchar(256) NOT NULL,
+  "type" varchar(256) NOT NULL,
+  "lecturer" varchar(256) NOT NULL,
+  "semester" varchar(256) NOT NULL,
+  "institute" varchar(4) NOT NULL,
+  "answers" integer NOT NULL,
+  "participants" integer NOT NULL,
+  -- Technically this could be a PK, but id makes access easier
+  UNIQUE("number", "name", "type", "lecturer", "semester")
+);
+
+-- Cascade ensures that scores are deleted with courses
+CREATE TABLE "score" (
+  "course" uuid NOT NULL,
+  "question" uuid NOT NULL,
+  "score" integer NOT NULL,
+  PRIMARY KEY("course", "question"),
+  CONSTRAINT fk_course
+    FOREIGN KEY("course")
+      REFERENCES "course"("id")
+        ON DELETE cascade,
+  CONSTRAINT fk_question
+    FOREIGN KEY("question")
+      REFERENCES "question"("id")
+        ON DELETE cascade
+);
\ No newline at end of file
diff --git a/src/lib/api.py b/src/lib/api.py
new file mode 100644
index 0000000000000000000000000000000000000000..113feff2d4674d17352fc63b21e6a48d370afa10
--- /dev/null
+++ b/src/lib/api.py
@@ -0,0 +1,173 @@
+import requests
+BASE_URL = "http://localhost:50000"
+
+def request_questions() -> list[dict]:
+	"""Requests all questions that exist in the question API.
+	Calls the `/questions` Endpoint
+
+	Returns
+	-------
+	list[dict]: Array of questions, where the dict represents the question. 
+	    Contains `id` (uuid), `content` (string), `scale` (string), `dimension` (string or null)
+	"""
+	res = requests.get("{0}/v1/questions".format(BASE_URL))
+	if res.status_code != 200:
+		raise requests.RequestException(f"Request was not fullfilled successfully. Server responded with {res.status_code}")
+	return res.json()
+
+def get_questions_for_dimension(dimension: str) -> list[dict]:
+	"""Requests all questions that belong to the specified dimension in the question API
+	Calls the `/dimensions/questions/{dimension}` Endpoint
+
+	Parameters
+	----------
+	dimension: str 
+	    Represents dimension. Can be `A`, `B`, `C` or any of these three in combination with `1` or `2`
+
+	Returns
+	-------
+	list[dict]: Array of questions, where the dict represents the question. 
+	    Contains `id` (uuid), `content` (string), `scale` (string), `dimension` (string or null)
+	"""
+	res = requests.get("{0}/v1/dimensions/questions/{1}".format(BASE_URL, dimension))
+	if res.status_code != 200:
+		raise requests.RequestException(f"Request was not fullfilled successfully. Server responded with {res.status_code}")
+	return res.json()
+
+def get_scales() -> list[dict]:
+	"""Requests all scales in the question API
+	Calls the `/scales` Endpoint
+
+	Returns
+	-------
+	list[dict]: Array of objects, where the dict represents the scales. 
+	    Contains `id` (string) 
+	"""
+	res = requests.get("{0}/v1/scales".format(BASE_URL))
+	if res.status_code != 200:
+		raise requests.RequestException(f"Request was not fullfilled successfully. Server responded with {res.status_code}")
+	return res.json()
+
+def get_answers_for(scale: str) -> list[dict]:
+	""" Requests answers for the provided scale in the question API
+	Calls the `/answers/{scale}` Endpoint
+
+	Parameters
+	----------
+	scale: str
+	    The scale you want the answers for
+
+	Returns
+	-------
+	list[dict]: Array of objects, where the dict represents the answers. 
+	    Contains `id` (string), `content` (string), `value` (integer)
+	"""
+	res = requests.get("{0}/v1/answers/{1}".format(BASE_URL, scale))
+	if res.status_code != 200:
+		raise requests.RequestException(f"Request was not fullfilled successfully. Server responded with {res.status_code}")
+	return res.json()
+
+def get_scale_for(question: str) -> dict:
+	""" Requests scale for the provided question in question API
+	Calls the `/scales/{question}` Endpoint
+	
+	Parameters
+	----------
+	question: str
+	    The question you want the scale for. Needs to be provided as an UUID
+
+	Returns
+	-------
+	dict: Object representing the scale 
+	    Contains `id` (string)
+	"""
+	res = requests.get("{0}/v1/scales/{1}".format(BASE_URL, question))
+	if res.status_code != 200:
+		raise requests.RequestException(f"Request was not fullfilled successfully. Server responded with {res.status_code}")
+	return res.json()
+
+def get_dimension_description() -> list[dict]:
+	"""Requests the dimension objects from the question API
+	Calls the `/dimensions` Endpoint
+
+	Returns
+	-------
+	list[dict]: A list with objects, where the dict represents the dimension
+	    Contains `id` (string), `description` (object) containing `en` (string, english description) and `de` (string, german description)
+	"""
+	res = requests.get("{0}/v1/dimensions".format(BASE_URL))
+	if res.status_code != 200:
+		raise requests.RequestException(f"Request was not fullfilled successfully. Server responded with {res.status_code}")
+
+	return res.json()
+
+def get_dimension_for(question: str) -> dict:
+	"""Requests the dimension for the provided question from the question API
+	Calls the `/questions/{question}/dimension` Endpoint
+
+	Parameters
+	----------
+	question: str
+	    Id of the questions
+
+	Returns
+	--------
+	dict: Dictionary containing the id of the dimension or None
+	""" 
+	res = requests.get("{0}/v1/questions/{1}/dimension".format(BASE_URL, question))
+	if res.status_code != 200:
+		raise requests.RequestException(f"Request was not fullfilled successfully. Server responded with {res.status_code}")
+
+	return res.json()
+
+# Results
+def force_courses(courses):
+	"""Inserts the provided courses with their scores into the score API. Overrides existing data
+	Calls the `/scores` Endpoint
+
+	Parameters
+	----------
+	courses: list[dict]
+	    List of objects where each object represents a course with it's scores.
+	"""
+	res = requests.put("{0}/v1/scores".format(BASE_URL), json=courses)
+
+	# If requests are too large, split them
+	if res.status_code == 413:
+		head = courses[len(courses) // 2:]
+		tail = courses[:len(courses) // 2]
+		force_courses(head)
+		force_courses(tail)
+	elif res.status_code != 201:
+		raise requests.RequestException(f"Request was not fullfilled successfully. Server responded with {res.status_code}")
+
+def get_all_scores() -> list[dict]:
+	"""Returns all scores that exist in the scores API
+	Calls the `/scores` Endpoint
+
+	Returns
+	-------
+	list[dict]: A list of courses with their score for questions
+	"""
+	res = requests.get("{0}/v1/scores".format(BASE_URL))
+	if res.status_code != 200:
+		raise requests.RequestException(f"Request was not fullfilled successfully. Server responded with {res.status_code}")
+	return res.json()
+
+def get_scores_for_dimension(dimension: str) -> list[dict]:
+	"""Returns all scores that exist in the scores API for the specified dimension
+	Calls the `/scores/dimension/{dimension}` Endpoint
+
+	Parameters
+	----------
+	dimension: str
+	    The desired dimension. Can be either `A`, `B`, `C` or any of the three in combination with `1` or `2`
+
+	Returns
+	-------
+  list[dict]: A list representing the courses with their scores 
+	"""
+	res = requests.get("{0}/v1/scores/dimensions/{1}".format(BASE_URL, dimension))
+	if res.status_code != 200:
+		raise requests.RequestException(f"Request was not fullfilled successfully. Server responded with {res.status_code}")
+	return res.json()
\ No newline at end of file
diff --git a/src/lib/constants.py b/src/lib/constants.py
index c15f429f1eca7bbab9f203ded705581613d14c88..e77b95b5ae6ae7171cdb6f4fcf074139dc4f74cc 100644
--- a/src/lib/constants.py
+++ b/src/lib/constants.py
@@ -2,10 +2,10 @@
 import os
 
 RAW_DATA_PATH = os.path.normpath(os.path.join(os.getcwd(), "..", "raw_data"))
-COMPUTER_SCIENCE_VALUES = os.path.join(RAW_DATA_PATH, "Informatik")
-BIOINFORMATICS_VALUES = os.path.join(RAW_DATA_PATH, "Bioinformatik")
-MATHEMATICS_VALUES = os.path.join(RAW_DATA_PATH, "Mathematik")
 CLEAN_DATA_PATH = os.path.normpath(os.path.join(os.getcwd(), "..", "clean_data"))
+COMPUTER_SCIENCE_VALUES = os.path.join(CLEAN_DATA_PATH, "Informatik")
+BIOINFORMATICS_VALUES = os.path.join(CLEAN_DATA_PATH, "Bioinformatik")
+MATHEMATICS_VALUES = os.path.join(CLEAN_DATA_PATH, "Mathematik")
 OUT_PATH = os.path.normpath(os.path.join(os.getcwd(), '..', "outputs"))
 
 INSTITUTE_MAP = {
diff --git a/src/lib/scraping.py b/src/lib/scraping.py
deleted file mode 100644
index 6454eb028f12d87bd3dc221a51ead85ea46558e3..0000000000000000000000000000000000000000
--- a/src/lib/scraping.py
+++ /dev/null
@@ -1,169 +0,0 @@
-import requests, re
-from bs4 import BeautifulSoup
-from typing import Union
-
-# In the VV the Ids for the Institutes are fixed every semester
-INSTITUTE_IDS = {
-	"cs": "130142",
-	"bio": "130146",
-	"math": "130123"
-}
-'''NOTE
-It is very well possible, that this could break once the newer semesters get pushed into the
-archive. There is no way of knowing if the naming convention for ids changes recently or 
-if the archive really uses a separate naming system
-'''
-
-# Archives use different Ids
-INSTITUTE_IDS_ARCHIVE = {
-	"cs": "E24n",
-	"bio": "E61a",
-	"math": "E17o"
-}
-
-# This will break as soon as one class or id in the VV changes
-def get_semester_urls() -> dict:
-	result = {}
-
-	# Current semesters
-	url = "https://www.fu-berlin.de/vv/de/fb"
-	res = requests.get(url)
-
-	soup = BeautifulSoup(res.text, 'html.parser')
-
-	# Scrapes all links to semesters in the dropdown menu
-	main_menu = soup.find(id="main_menu")
-
-	children = main_menu.children
-	semester_dropdown = ""
-	for child in children:
-		if("Semester" in child.text):
-			semester_dropdown = child
-
-	# Dropdown menu is split into list items, where <li class="nav-header"> marks a heading
-	semesters = semester_dropdown.find("ul").select("li:not(.nav-header)")
-
-	for semester in semesters:
-		if("Archivierte Semester"  == semester.text.strip()): continue # Archived semesters are handled later
-		link = semester.find("a")
-		result[link.text] = "https://www.fu-berlin.de/vv/de/modul?sm="+ link.get("href").split("&sm=")[1]
-
-	# Archive
-	url = "https://archiv.vv.fu-berlin.de"
-	res = requests.get(url)
-	soup = BeautifulSoup(res.text, 'html.parser')
-	
-	sem_list = soup.select("#main_content ul li")
-
-	# Regex for detecting non normalized names
-	names_regex = re.compile(r'\d+\/\d{4}')
-
-	for child in sem_list:
-		# Until summer term 2012 the archive uses a different format. The data will not be relevant anyways
-		if(child.text == "Wintersemester 2012/2013"): break 
-		link = child.find("a")
-		name = link.text
-		# "Normalizes" semesters that have e.g. 2012/2013 instead of 2012/13 name
-		if not(names_regex.search(name) == None):
-			name = name[:-4] + name[-2:]
-		result[name] = link.get("href")
-	return result
-
-
-def get_detail_tutorials(module_url: str) -> int:
-	res = requests.get(module_url)
-
-	soup = BeautifulSoup(res.text, 'html.parser')
-
-	return len(soup.find_all("div", { "class": "appointments"}))
-
-def get_complete_catalogue_for(semester: str, institute: str) -> dict:
-	ids = get_semester_urls()
-	if semester not in ids:
-		raise AttributeError(semester + " is not supported. Try any semester from the winter term 2012/2013")
-	if institute not in INSTITUTE_IDS.keys():
-		raise AttributeError(institute + " is not supported. Try any of these institutes: " + ','.join(INSTITUTE_IDS.keys()))
-	base_url = ids[semester]
-	if("archiv" in base_url):
-		link = base_url + "module/" + INSTITUTE_IDS_ARCHIVE[institute]
-	else:
-		link = base_url + "&id=" + INSTITUTE_IDS[institute]
-	res = requests.get(link)
-
-	soup = BeautifulSoup(res.text, 'html.parser')
-
-	li = soup.find_all('ul', {"class": "list"})[0]
-	children = li.findChildren("li")
-	catalogue = {}
-	for child in children:
-		# Skips cancelled
-		cancel = child.find("span", {"class": "cancelled"})
-		if cancel != None: continue
-		id = child.find("b").text
-		# Some lectures have no instructor in the VV
-		try:
-			lecturers = child.find("span", {"class": "course_instructor"}).text.strip().replace("(","").replace(")","").split(", ")
-		except AttributeError:
-			lecturers = []
-
-		# Normalizes course type to the three available types in dataset
-		course_type = child.find("span", {"class": "label-info"}).text.strip()
-
-		if course_type in ["Seminar am PC", "Übung"]:
-			course_type = "Übungen"
-		elif course_type in ["Proseminar", "Seminaristischer Unterricht", "Projektseminar", "Seminar/Proseminar", "Seminar"]:
-			course_type = "Seminare"
-		elif course_type in ["Integrierte Veranstaltung", "Vorlesung"]:
-			course_type = "Vorlesungen"
-
-		catalogue[id] = {
-			"name": child.find("span", {"class": "course_name"}).text.strip(),
-			"lecturers": lecturers,
-			"type": course_type
-		}
-		# Tutorials happen more often are counted as individuals
-		if(course_type == "Übungen"):
-			
-			module_href = child.find("a").get("href")
-			module_url = base_url.split(".de/")[0] + ".de" + module_href
-			
-			times = get_detail_tutorials(module_url)
-			catalogue[id]["amount"] = times
-	return catalogue
-
-def append_dict(dictionary, key, value) -> dict:
-	res = dictionary
-	res[key] = value
-	return res
-
-def get_catalogue_for(semester: str) -> dict:
-	cs = get_complete_catalogue_for(semester, "cs")
-	cs = {k: append_dict(v, "institute", "cs") for k, v in cs.items()}
-
-	bio = get_complete_catalogue_for(semester, "bio")
-	bio = {k: append_dict(v, "institute", "bio") for k, v in bio.items()}
-
-	math = get_complete_catalogue_for(semester, "math")
-	math = {k: append_dict(v, "institute", "math") for k, v in math.items()}
-
-	merge = math | bio | cs
-
-	return merge
-
-
-def get_course(id: str) -> Union[dict, None]:
-	search_url = "https://www.fu-berlin.de/vv/de/search?utf8=✓&query={0}".format(id)
-	res = requests.get(search_url)
-
-	soup = BeautifulSoup(res.text, 'html.parser')
-
-	main_content = soup.select('#main_content .well')[0]
-	name = main_content.select("h1")[0].text
-	if name == "Erweiterte Suche": return None
-	
-	ret = {
-		"id": id,
-		"name": main_content.select("h1")[0].text,
-		"type": main_content.select(".label_container span")[0].text.strip()
-	}
-	return ret
\ No newline at end of file
diff --git a/src/old-report.ipynb b/src/old-report.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..53baa32317dc5a5e9897200a96d64578b210c3b9
--- /dev/null
+++ b/src/old-report.ipynb
@@ -0,0 +1,391 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Imports and constants\n",
+    "import os, pandas\n",
+    "import lib.constants as CONSTANTS\n",
+    "\n",
+    "# Add semester here in the form of \"Sommersemester YYYY\" or \"Wintersemester YYYY/YY\"\n",
+    "# e.g.: Sommersemester 2023 or Wintersemester 2022/23\n",
+    "SEMESTER = \"Sommersemester 2023\"\n",
+    "\n",
+    "# Generates Semester name based on set constant\n",
+    "SEMESTER_NAME = SEMESTER[0:2] + \"Se\"\n",
+    "split = SEMESTER.split(\" \")[1].split(\"/\")\n",
+    "SEMESTER_NAME += '-'.join(split)\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Helper Functions\n",
+    "def norm_name(name: str) -> str:\n",
+    "\tif ', ' not in name: return name\n",
+    "\tsplit = name.split(\", \")\n",
+    "\treturn split[1] + \" \" + split[0]\n",
+    "\n",
+    "\n",
+    "def get_percentage_for_institute(all: pandas.DataFrame, min_five: pandas.DataFrame, institute: str) -> float:\n",
+    "\treturn (len(min_five[min_five[\"institute\"] == institute]) / len(all[all[\"institute\"] == institute])) * 100\n",
+    "\n",
+    "def get_percentages_for(all: pandas.DataFrame, min_five: pandas.DataFrame, category: str) -> list:\n",
+    "\tcategory_five = min_five[min_five[\"Veranstaltung-Kategorie\"] == category]\n",
+    "\tcategory_all = all[all[\"Veranstaltung-Kategorie\"] == category]\n",
+    "\t\n",
+    "\tpercentage_all = (len(category_five) / len(category_all)) * 100\n",
+    "\tpercentage_cs = get_percentage_for_institute(category_all, category_five, \"cs\")\n",
+    "\tpercentage_bio = get_percentage_for_institute(category_all, category_five, \"bio\")\n",
+    "\tpercentage_math = get_percentage_for_institute(category_all, category_five, \"math\")\n",
+    "\treturn [round(percentage_all,2), round(percentage_bio, 2), round(percentage_cs, 2), round(percentage_math, 2)]\n",
+    "\n",
+    "def get_median_for(df: pandas.DataFrame, category: str, institute: str) -> float:\n",
+    "\tmin_five_category = df[df[\"Veranstaltung-Kategorie\"] == category]\n",
+    "\n",
+    "\treturn min_five_category[min_five_category[\"institute\"] == institute][\"size\"].median()\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Get registered courses\n",
+    "This section generates the data that corresponds to the first column of the result matrix"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "all_registered = pandas.read_csv(os.path.join(CONSTANTS.CLEAN_DATA_PATH, \"participation\", (SEMESTER_NAME + '.csv')))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Retrieve data\n",
+    "Uses the semester to retrieve the corresponding data "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "\n",
+    "# Build path\n",
+    "cs_path = os.path.join(CONSTANTS.COMPUTER_SCIENCE_VALUES, SEMESTER_NAME, \"all.csv\")\n",
+    "bio_path = os.path.join(CONSTANTS.BIOINFORMATICS_VALUES, SEMESTER_NAME, \"all.csv\")\n",
+    "math_path = os.path.join(CONSTANTS.MATHEMATICS_VALUES, SEMESTER_NAME, \"all.csv\")\n",
+    "\n",
+    "# Catch rouge semesters\n",
+    "assert os.path.exists(cs_path) & os.path.exists(bio_path) & os.path.exists(math_path), \"Semester doesn't exist for every institute\"\n",
+    "\n",
+    "# Read data and append institute\n",
+    "cs = pandas.read_csv(cs_path)\n",
+    "bio = pandas.read_csv(bio_path)\n",
+    "math = pandas.read_csv(math_path)\n",
+    "cs = cs.assign(institute=\"cs\")\n",
+    "bio = bio.assign(institute=\"bio\")\n",
+    "math = math.assign(institute=\"math\")\n",
+    "\n",
+    "# Ignoring index prevents multiple courses having the same index\n",
+    "all = pandas.concat([bio,cs,math], ignore_index=True)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Group evaluations by course\n",
+    "Grouping by these four columns is the only way to group truly unique, because\n",
+    "- tutorials and lectures of the same course have the same course number\n",
+    "- two people could hold the same lecture, but they are still counted as different lectures, grouping by the name of the person ensures a split\n",
+    "- grouping by course type splits tutorials, lectures and seminars\n",
+    "- considering the course name ensures that two individual tutorials held by the same person in a course can be identified"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Institute here is only used to not lose the relation\n",
+    "grouped = all.groupby([\"Veranstaltung-Nr.\", \"Person-Name\", \"Veranstaltung-Kategorie\", \"Veranstaltung-Name\", \"institute\"])\n",
+    "\n",
+    "# reset_index() merges both header lines into one, so size doesn't float alone\n",
+    "all_size = grouped.size().reset_index(name=\"size\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Get collegial"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "collegial = all_registered[all_registered[\"Anzahl Lehrende\"] > 1]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Courses with less than 5 registered students"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "less_five_registered = all_registered[all_registered[\"Anzahl Teilnehmende\"] < 5]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Courses with at least five evaluations"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "min_five_eval = all_size[all_size[\"size\"] > 4]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### % of evaluated courses"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "vl = get_percentages_for(all_size, min_five_eval, \"Vorlesungen\")\n",
+    "ueb = get_percentages_for(all_size, min_five_eval, \"Übungen\")\n",
+    "sem = get_percentages_for(all_size, min_five_eval, \"Seminare\")\n",
+    "\n",
+    "percantages_eval = {\n",
+    "\t\"Vorlesung\": {\n",
+    "\t\t\"all\": vl[0],\n",
+    "\t\t\"Bioinformatik\": vl[1],\n",
+    "\t\t\"Informatik\": vl[2],\n",
+    "\t\t\"Mathematik\": vl[3]\n",
+    "\t},\n",
+    "\t\"Seminar, SWP\": {\n",
+    "\t\t\"all\": sem[0],\n",
+    "\t\t\"Bioinformatik\": sem[1],\n",
+    "\t\t\"Informatik\": sem[2],\n",
+    "\t\t\"Mathematik\": sem[3]\n",
+    "\t},\n",
+    "\t\"Übung/Tutorium\": {\n",
+    "\t\t\"all\": ueb[0],\n",
+    "\t\t\"Bioinformatik\": ueb[1],\n",
+    "\t\t\"Informatik\": ueb[2],\n",
+    "\t\t\"Mathematik\":ueb[3]\n",
+    "\t},\n",
+    "\t\"Gesamt\": len(min_five_eval) / len(all_size) * 100\n",
+    "}\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "join = min_five_eval.merge(all_registered[['Veranstaltung-Nr.', 'Anzahl Teilnehmende', 'Person-Name', 'Veranstaltung-Name']], on=[\"Veranstaltung-Nr.\", \"Person-Name\", \"Veranstaltung-Name\"], how=\"left\")\n",
+    "bio = join[(join[\"institute\"] == \"bio\")]\n",
+    "cs = join[(join[\"institute\"] == \"cs\")]\n",
+    "math = join[(join[\"institute\"] == \"math\")]\n",
+    "# Median differs, because the evaluation report calculates it wrong\n"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Median number of students per Course"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "median_stud = {\n",
+    "\t\"Vorlesung\": {\n",
+    "\t\t\"Bioinformatik\": (bio[bio[\"Veranstaltung-Kategorie\"] == \"Vorlesungen\"])[\"Anzahl Teilnehmende\"].median(),\n",
+    "\t\t\"Informatik\": (cs[cs[\"Veranstaltung-Kategorie\"] == \"Vorlesungen\"])[\"Anzahl Teilnehmende\"].median(),\n",
+    "\t\t\"Mathematik\": (math[math[\"Veranstaltung-Kategorie\"] == \"Vorlesungen\"])[\"Anzahl Teilnehmende\"].median()\n",
+    "\t},\n",
+    "\t\"Seminar, SWP\": {\n",
+    "\t\t\"Bioinformatik\": (bio[bio[\"Veranstaltung-Kategorie\"] == \"Seminare\"])[\"Anzahl Teilnehmende\"].median(),\n",
+    "\t\t\"Informatik\": (cs[cs[\"Veranstaltung-Kategorie\"] == \"Seminare\"])[\"Anzahl Teilnehmende\"].median(),\n",
+    "\t\t\"Mathematik\": (math[math[\"Veranstaltung-Kategorie\"] == \"Seminare\"])[\"Anzahl Teilnehmende\"].median()\n",
+    "\t},\n",
+    "\t\"Übung/Tutorium\": {\n",
+    "\t\t\"Bioinformatik\": (bio[bio[\"Veranstaltung-Kategorie\"] == \"Übungen\"])[\"Anzahl Teilnehmende\"].median(),\n",
+    "\t\t\"Informatik\": (cs[cs[\"Veranstaltung-Kategorie\"] == \"Übungen\"])[\"Anzahl Teilnehmende\"].median(),\n",
+    "\t\t\"Mathematik\": (math[math[\"Veranstaltung-Kategorie\"] == \"Übungen\"])[\"Anzahl Teilnehmende\"].median()\n",
+    "\t}\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Median filled out questionnaire of courses"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "median_questionnaire = {\n",
+    "\t\"Vorlesung\": {\n",
+    "\t\t\"Bioinformatik\": get_median_for(min_five_eval, \"Vorlesungen\", \"bio\"),\n",
+    "\t\t\"Informatik\": get_median_for(min_five_eval, \"Vorlesungen\", \"cs\"),\n",
+    "\t\t\"Mathematik\": get_median_for(min_five_eval, \"Vorlesungen\", \"math\")\n",
+    "\t},\n",
+    "\t\"Seminar, SWP\": {\n",
+    "\t\t\"Bioinformatik\": get_median_for(min_five_eval, \"Seminare\", \"bio\"),\n",
+    "\t\t\"Informatik\": get_median_for(min_five_eval, \"Seminare\", \"cs\"),\n",
+    "\t\t\"Mathematik\": get_median_for(min_five_eval, \"Seminare\", \"math\")\n",
+    "\t},\n",
+    "\t\"Übung/Tutorium\": {\n",
+    "\t\t\"Bioinformatik\": get_median_for(min_five_eval, \"Übungen\", \"bio\"),\n",
+    "\t\t\"Informatik\": get_median_for(min_five_eval, \"Übungen\", \"cs\"),\n",
+    "\t\t\"Mathematik\": get_median_for(min_five_eval, \"Übungen\", \"math\")\n",
+    "\t}\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Result generation"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "map = {\n",
+    "\t\"Bioinformatik\": \"bio\",\n",
+    "\t\"Informatik\": \"cs\",\n",
+    "\t\"Mathematik\": \"math\"\n",
+    "}\n",
+    "\n",
+    "institutes = [\"Bioinformatik\", \"Informatik\", \"Mathematik\"]\n",
+    "type_map = {\n",
+    "\t\"Vorlesung\": \"Vorlesungen\",\n",
+    "\t\"Seminar, SWP\": \"Seminare\",\n",
+    "\t\"Übung/Tutorium\": \"Übungen\"\n",
+    "}\n",
+    "\n",
+    "schema =  {\n",
+    "\t\"Vorlesung\": institutes,\n",
+    "\t\"Seminar, SWP\": institutes,\n",
+    "\t\"Übung/Tutorium\": institutes\n",
+    "}\n",
+    "\n",
+    "header = [\"Veranstaltungstyp\", \"Angemeldete LVen dieses Typs\", \"Kollegial gehaltene LVen\", \"LVen <5 Anmeldungen\", \"Bewertete LVen\", \"Davon LVen mit mind. 5 Bewertungen\", \"Evaluierte LVen* in %\", \"Anzahl angemeldeter Studierender je evaluierter LV* (Median)\", \"Anzahl ausgefüllter Fragebögen in evaluierten LVen* (Median)\"]\n",
+    "\n",
+    "result_matrix = []\n",
+    "for k, v in type_map.items():\n",
+    "\n",
+    "\tregistered = all_registered[all_registered[\"Kategorie\"] == v]\n",
+    "\n",
+    "\tcollegial_num = collegial[collegial[\"Kategorie\"] == v]\n",
+    "\tless_five = less_five_registered[less_five_registered[\"Kategorie\"] == v]\n",
+    "\t# First key\n",
+    "\tresult_matrix.append([k,len(registered), len(collegial_num), len(less_five), \n",
+    "\t\t\t\t\t\t\t\t\t\tlen(all_size[all_size[\"Veranstaltung-Kategorie\"] == v]), \n",
+    "\t\t\t\t\t\t\t\t\t\tlen(min_five_eval[min_five_eval[\"Veranstaltung-Kategorie\"] == v]), \n",
+    "\t\t\t\t\t\t\t\t\t\tpercantages_eval[k][\"all\"],\n",
+    "\t\t\t\t\t\t\t\t\t\t'-', '-'])\n",
+    "\tfor institute in institutes:\n",
+    "\t\tshort = map[institute]\n",
+    "\t\tinst_size = all_size[(all_size[\"Veranstaltung-Kategorie\"] == v) & (all_size[\"institute\"] == short)]\n",
+    "\t\tinst_five = min_five_eval[(min_five_eval[\"Veranstaltung-Kategorie\"] == v) & (min_five_eval[\"institute\"] == short)]\n",
+    "\n",
+    "\t\tinst_reg = registered[registered[\"Bereich\"] == institute]\n",
+    "\t\tcol = collegial_num[collegial_num[\"Bereich\"] == institute]\n",
+    "\t\tl_five = less_five[less_five[\"Bereich\"] == institute]\n",
+    "\n",
+    "\t\tresult_matrix.append([institute, len(inst_reg), len(col), \n",
+    "\t\t\t\t\t\t\t\t\t\t\t\tlen(l_five), len(inst_size), len(inst_five), \n",
+    "\t\t\t\t\t\t\t\t\t\t\t\tpercantages_eval[k][institute], median_stud[k][institute], \n",
+    "\t\t\t\t\t\t\t\t\t\t\t\tmedian_questionnaire[k][institute]])\n",
+    "\t\t\n",
+    "together = [\"Gesamt\"] + [result_matrix[0][i] + result_matrix[4][i] + result_matrix[8][i] for i in range(1,6)]\n",
+    "together.append(round(together[5] / together[4] * 100))\n",
+    "together += ['-'] * 2\n",
+    "result_matrix.append(together)\n",
+    "\n",
+    "res = pandas.DataFrame(data=result_matrix, columns=header)\n",
+    "\n",
+    "out_file = os.path.join(CONSTANTS.OUT_PATH, SEMESTER_NAME + \".md\")\n",
+    "res.to_markdown(out_file, index=False)"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.9.15"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/src/pipeline.ipynb b/src/pipeline.ipynb
index 487a43432d9b4106944b445a6e7fb06f357893ac..4946ca4751182510a5d935dd65ad885119872f49 100644
--- a/src/pipeline.ipynb
+++ b/src/pipeline.ipynb
@@ -1,405 +1,198 @@
 {
  "cells": [
-  {
-   "cell_type": "code",
-   "execution_count": 14,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "# Imports and constants\n",
-    "import os, pandas\n",
-    "from lib.scraping import get_catalogue_for\n",
-    "import lib.constants as CONSTANTS\n",
-    "\n",
-    "# Add semester here in the form of \"Sommersemester YYYY\" or \"Wintersemester YYYY/YY\"\n",
-    "# e.g.: Sommersemester 2023 or Wintersemester 2022/23\n",
-    "SEMESTER = \"Sommersemester 2023\"\n",
-    "\n",
-    "# Generates Semester name based on set constant\n",
-    "SEMESTER_NAME = SEMESTER[0:2] + \"Se\"\n",
-    "split = SEMESTER.split(\" \")[1].split(\"/\")\n",
-    "SEMESTER_NAME += '-'.join(split)\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 15,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "# Helper Functions\n",
-    "def norm_name(name: str) -> str:\n",
-    "\tif ', ' not in name: return name\n",
-    "\tsplit = name.split(\", \")\n",
-    "\treturn split[1] + \" \" + split[0]\n",
-    "\n",
-    "\n",
-    "def get_percentage_for_institute(all: pandas.DataFrame, min_five: pandas.DataFrame, institute: str) -> float:\n",
-    "\treturn (len(min_five[min_five[\"institute\"] == institute]) / len(all[all[\"institute\"] == institute])) * 100\n",
-    "\n",
-    "def get_percentages_for(all: pandas.DataFrame, min_five: pandas.DataFrame, category: str) -> list:\n",
-    "\tcategory_five = min_five[min_five[\"Veranstaltung-Kategorie\"] == category]\n",
-    "\tcategory_all = all[all[\"Veranstaltung-Kategorie\"] == category]\n",
-    "\t\n",
-    "\tpercentage_all = (len(category_five) / len(category_all)) * 100\n",
-    "\tpercentage_cs = get_percentage_for_institute(category_all, category_five, \"cs\")\n",
-    "\tpercentage_bio = get_percentage_for_institute(category_all, category_five, \"bio\")\n",
-    "\tpercentage_math = get_percentage_for_institute(category_all, category_five, \"math\")\n",
-    "\treturn [round(percentage_all,2), round(percentage_bio, 2), round(percentage_cs, 2), round(percentage_math, 2)]\n",
-    "\n",
-    "def get_median_for(df: pandas.DataFrame, category: str, institute: str) -> float:\n",
-    "\tmin_five_category = df[df[\"Veranstaltung-Kategorie\"] == category]\n",
-    "\n",
-    "\treturn min_five_category[min_five_category[\"institute\"] == institute][\"size\"].median()\n"
-   ]
-  },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "### Get registered courses\n",
-    "This section generates the data that corresponds to the first column of the result matrix"
+    "# Evaluation Report Pipeline\n",
+    "This file hosts the pipeline, which generates all images containing the final evaluation results"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 16,
+   "execution_count": 1,
    "metadata": {},
    "outputs": [],
    "source": [
-    "all_registered = pandas.read_csv(os.path.join(CONSTANTS.RAW_DATA_PATH, \"participation\", (SEMESTER_NAME + '.csv')), sep=\";\")\n",
+    "import os, pandas, math,json\n",
+    "import regex as re\n",
+    "import lib.constants as CONSTANTS\n",
+    "import lib.api as api\n",
     "\n",
-    "# Names are written Surname, Name but the other files have Name Surname, this normalizes that\n",
-    "names = all_registered[\"Person-Name\"]\n",
-    "all_registered[\"Person-Name\"] = names.map(norm_name)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "### Retrieve data\n",
-    "Uses the semester to retrieve the corresponding data "
+    "question_cache = {}\n",
+    "intervals = {}\n",
+    "uuid_regex = re.compile('\\\\b[0-9a-f]{8}\\\\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\\\\b[0-9a-f]{12}\\\\b')"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 17,
+   "execution_count": 2,
    "metadata": {},
    "outputs": [],
    "source": [
+    "def file_iterator():\n",
+    "\tfor (root, _dirs, files) in os.walk(CONSTANTS.CLEAN_DATA_PATH):\n",
+    "\t\tif len(files) > 0 and 'all.csv' in files:\n",
+    "\t\t\tfile_path = os.path.join(root, \"all.csv\")\n",
+    "\t\t\tdf = pandas.read_csv(file_path)\n",
+    "\t\t\tyield root.split(\"/\")[-2:] + [df]\n",
+    "\n",
+    "def generate_scales():\n",
+    "\tresult = {}\n",
+    "\tscales = api.get_scales()\n",
+    "\tfor scale in scales:\n",
+    "\t\tanswers = api.get_answers_for(scale[\"id\"])\n",
+    "\t\tresult[scale[\"id\"]] = answers\n",
+    "\treturn result\n",
+    "\n",
+    "def generate_intervals():\n",
+    "\tglobal intervals\n",
+    "\tintervals = {key: [x['value'] for x in value] for key, value in generate_scales().items()}\n",
+    "\n",
+    "\n",
+    "def get_factors():\n",
+    "\tfactor = {}\n",
+    "\n",
+    "\tfor interval in intervals:\n",
+    "\t\t# These intervals either just have two steps or the answers have no weight\n",
+    "\t\tif interval in [\"scale_time\", \"y_n\"]: continue\n",
+    "\t\t(a,b) = intervals[interval]\n",
+    "\t\tfactor[interval] = round(abs(a-b-1)/2, 0)\n",
+    "\treturn factor\n",
+    "\n",
+    "# The type of the value is unknown\n",
+    "def convert_score(value, factor) -> int:\n",
+    "\t# Matches Numbers from 0-9 an optional (floating) point and optional trailing zero\n",
+    "\tnumber_regex = re.compile('^\\d(\\.0)?$')\n",
+    "\t\n",
+    "\t# Minimum requirement for a number\n",
+    "\t# Cast to string safeguards real floats\n",
+    "\tif number_regex.match(str(value)) != None:\n",
+    "\t\treturn factor[int(float(value) - 1)]\n",
+    "\telse:\n",
+    "\t\treturn 0\n",
     "\n",
-    "# Build path\n",
-    "cs_path = os.path.join(CONSTANTS.COMPUTER_SCIENCE_VALUES, (\"Informatik_\" + SEMESTER_NAME), \"daten.csv\")\n",
-    "bio_path = os.path.join(CONSTANTS.BIOINFORMATICS_VALUES, (\"Bioinformatik_\" + SEMESTER_NAME), \"daten.csv\")\n",
-    "math_path = os.path.join(CONSTANTS.MATHEMATICS_VALUES, (\"Mathematik_\" + SEMESTER_NAME), \"daten.csv\")\n",
-    "\n",
-    "# Catch\n",
-    "assert os.path.exists(cs_path) & os.path.exists(bio_path) & os.path.exists(math_path), \"Semester doesn't exist for every institute\"\n",
-    "\n",
-    "# Read data and append institute\n",
-    "cs = pandas.read_csv(cs_path, sep=\";\", encoding=\"ISO-8859-1\")\n",
-    "bio = pandas.read_csv(bio_path, sep=\";\", encoding=\"ISO-8859-1\")\n",
-    "math = pandas.read_csv(math_path, sep=\";\", encoding=\"ISO-8859-1\")\n",
-    "cs = cs.assign(institute=\"cs\")\n",
-    "bio = bio.assign(institute=\"bio\")\n",
-    "math = math.assign(institute=\"math\")\n",
-    "\n",
-    "# Ignoring index prevents multiple courses having the same index\n",
-    "all = pandas.concat([bio,cs,math], ignore_index=True)\n",
-    "#TODO: Remove this when it's in cleanup\n",
-    "numbers = all[\"Veranstaltung-Nr.\"]\n",
-    "all[\"Veranstaltung-Nr.\"] = numbers.map(lambda x: str(x).strip())\n",
+    "def calc_score(series: pandas.Series) -> float:\n",
     "\n",
-    "# TODO: export to cleanup\n",
-    "# Replaces Keys with values in Dataframe\n",
-    "to_clean = {\n",
-    "\t\"Vorlesung\": \"Vorlesungen\",\n",
-    "\t\"Übung\": \"Übungen\",\n",
-    "\t\"Seminar\": \"Seminare\",\n",
-    "\t\"V\": \"Vorlesungen\",\n",
-    "\t\"Ü\": \"Übungen\",\n",
-    "\t\"S\": \"Seminare\",\n",
-    "}\n",
+    "\tif uuid_regex.match(series.name) != None:\n",
+    "\t\t# TODO: Negate that one question\n",
+    "\t\tif series.name not in question_cache:\n",
+    "\t\t\tquestion_cache[series.name] = intervals[api.get_scale_for(series.name)[\"id\"]]\n",
     "\n",
-    "to_replace = all[\"Veranstaltung-Kategorie\"]\n",
-    "all[\"Veranstaltung-Kategorie\"] = to_replace.map(lambda x: to_clean.get(x, x))\n",
     "\n",
-    "outliers = all[all[\"Veranstaltung-Kategorie\"] == \"Projekte / Praktika\"]\n",
+    "\t\tfactor = question_cache[series.name]\n",
+    "\t\t# Catches weightless questions\n",
+    "\t\tif len([x for x in factor if x != 0]) == 0:\n",
+    "\t\t\treturn 0\n",
+    "\t\t\n",
+    "\t\tif factor == [1, 0]:\n",
+    "\t\t\treturn series.map(lambda x: 1 if x == \"Ja\" else 0).sum()\n",
     "\n",
-    "for i in all.index:\n",
-    "\tvalue = all.loc[i]\n",
+    "\t\t# Pandas saves values as float, so they need to be converted\n",
+    "\t\tseries = series.map(lambda x: convert_score(x, factor), na_action=\"ignore\")\n",
     "\n",
-    "\tevaluation_type = value.loc[\"Veranstaltung-Typ\"]\n",
-    "\tif value.loc[\"Veranstaltung-Kategorie\"] in [\"Vorlesungen\", \"Übungen\", \"Seminare\"]: continue\n",
-    "\tif(evaluation_type == \"Ü\"):\n",
-    "\t\tall.at[i, 'Veranstaltung-Kategorie'] = \"Übungen\"\n",
-    "\telif(evaluation_type == \"V\" or evaluation_type == \"Vl\" or evaluation_type == \"VL\"):\n",
-    "\t\tall.at[i, \"Veranstaltung-Kategorie\"] = \"Vorlesungen\"\n",
-    "\telif (evaluation_type == \"S\"):\n",
-    "\t\tall.at[i, \"Veranstaltung-Kategorie\"] = \"Seminare\"\n"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "### Group evaluations by course\n",
-    "Grouping by these four columns is the only way to group truly unique, because\n",
-    "- tutorials and lectures of the same course have the same course number\n",
-    "- two people could hold the same lecture, but they are still counted as different lectures, grouping by the name of the person ensures a split\n",
-    "- grouping by course type splits tutorials, lectures and seminars\n",
-    "- considering the course name ensures that two individual tutorials held by the same person in a course can be identified"
+    "\t\treturn series.sum()"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 18,
+   "execution_count": 3,
    "metadata": {},
    "outputs": [],
    "source": [
-    "# Institute here is only used to not lose the relation\n",
-    "grouped = all.groupby([\"Veranstaltung-Nr.\", \"Person-Name\", \"Veranstaltung-Kategorie\", \"Veranstaltung-Name\", \"institute\"])\n",
+    "# Generate static values\n",
+    "generate_intervals()\n",
     "\n",
-    "# reset_index() merges both header lines into one, so size doesn't float alone\n",
-    "all_size = grouped.size().to_frame('size').reset_index()"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "### Get collegial"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 19,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "collegial = all_registered[all_registered[\"Anzahl Lehrende\"] > 1]"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "### Courses with less than 5 registered students"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 20,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "less_five_registered = all_registered[all_registered[\"Anzahl Teilnehmende\"] < 5]"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "### Courses with at least five evaluations"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 21,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "min_five_eval = all_size[all_size[\"size\"] > 4]"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "### % of evaluated courses"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 22,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "vl = get_percentages_for(all_size, min_five_eval, \"Vorlesungen\")\n",
-    "ueb = get_percentages_for(all_size, min_five_eval, \"Übungen\")\n",
-    "sem = get_percentages_for(all_size, min_five_eval, \"Seminare\")\n",
+    "participation = {}\n",
     "\n",
-    "percantages_eval = {\n",
-    "\t\"Vorlesung\": {\n",
-    "\t\t\"all\": vl[0],\n",
-    "\t\t\"Bioinformatik\": vl[1],\n",
-    "\t\t\"Informatik\": vl[2],\n",
-    "\t\t\"Mathematik\": vl[3]\n",
-    "\t},\n",
-    "\t\"Seminar, SWP\": {\n",
-    "\t\t\"all\": sem[0],\n",
-    "\t\t\"Bioinformatik\": sem[1],\n",
-    "\t\t\"Informatik\": sem[2],\n",
-    "\t\t\"Mathematik\": sem[3]\n",
-    "\t},\n",
-    "\t\"Übung/Tutorium\": {\n",
-    "\t\t\"all\": ueb[0],\n",
-    "\t\t\"Bioinformatik\": ueb[1],\n",
-    "\t\t\"Informatik\": ueb[2],\n",
-    "\t\t\"Mathematik\":ueb[3]\n",
-    "\t},\n",
-    "\t\"Gesamt\": len(min_five_eval) / len(all_size) * 100\n",
-    "}\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 23,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "join = min_five_eval.merge(all_registered[['Veranstaltung-Nr.', 'Anzahl Teilnehmende', 'Person-Name', 'Veranstaltung-Name']], on=[\"Veranstaltung-Nr.\", \"Person-Name\", \"Veranstaltung-Name\"], how=\"left\")\n",
-    "bio = join[(join[\"institute\"] == \"bio\")]\n",
-    "cs = join[(join[\"institute\"] == \"cs\")]\n",
-    "math = join[(join[\"institute\"] == \"math\")]\n",
-    "# Median differs, because the evaluation report calculates it wrong\n"
+    "participation_path = os.path.join(CONSTANTS.RAW_DATA_PATH, \"participation\")\n",
+    "for file in os.listdir(participation_path):\n",
+    "\tif not file.endswith(\".csv\") or file.startswith(\"example\"): continue\n",
+    "\tsemester = file.replace(\".csv\", \"\")\n",
+    "\tparticipation[semester.replace(\"-\", \"/\")] = pandas.read_csv(os.path.join(participation_path, file), sep=\";\")"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "### Median number of students per Course"
+    "## Calculate weight for Dimension\n",
+    "Calculates the individual score for all dimensions for every course"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 24,
+   "execution_count": 4,
    "metadata": {},
    "outputs": [],
    "source": [
-    "median_stud = {\n",
-    "\t\"Vorlesung\": {\n",
-    "\t\t\"Bioinformatik\": (bio[bio[\"Veranstaltung-Kategorie\"] == \"Vorlesungen\"])[\"Anzahl Teilnehmende\"].median(),\n",
-    "\t\t\"Informatik\": (cs[cs[\"Veranstaltung-Kategorie\"] == \"Vorlesungen\"])[\"Anzahl Teilnehmende\"].median(),\n",
-    "\t\t\"Mathematik\": (math[math[\"Veranstaltung-Kategorie\"] == \"Vorlesungen\"])[\"Anzahl Teilnehmende\"].median()\n",
-    "\t},\n",
-    "\t\"Seminar, SWP\": {\n",
-    "\t\t\"Bioinformatik\": (bio[bio[\"Veranstaltung-Kategorie\"] == \"Seminare\"])[\"Anzahl Teilnehmende\"].median(),\n",
-    "\t\t\"Informatik\": (cs[cs[\"Veranstaltung-Kategorie\"] == \"Seminare\"])[\"Anzahl Teilnehmende\"].median(),\n",
-    "\t\t\"Mathematik\": (math[math[\"Veranstaltung-Kategorie\"] == \"Seminare\"])[\"Anzahl Teilnehmende\"].median()\n",
-    "\t},\n",
-    "\t\"Übung/Tutorium\": {\n",
-    "\t\t\"Bioinformatik\": (bio[bio[\"Veranstaltung-Kategorie\"] == \"Übungen\"])[\"Anzahl Teilnehmende\"].median(),\n",
-    "\t\t\"Informatik\": (cs[cs[\"Veranstaltung-Kategorie\"] == \"Übungen\"])[\"Anzahl Teilnehmende\"].median(),\n",
-    "\t\t\"Mathematik\": (math[math[\"Veranstaltung-Kategorie\"] == \"Übungen\"])[\"Anzahl Teilnehmende\"].median()\n",
-    "\t}\n",
-    "}"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "### Median filled out questionnaire of courses"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 25,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "median_questionnaire = {\n",
-    "\t\"Vorlesung\": {\n",
-    "\t\t\"Bioinformatik\": get_median_for(min_five_eval, \"Vorlesungen\", \"bio\"),\n",
-    "\t\t\"Informatik\": get_median_for(min_five_eval, \"Vorlesungen\", \"cs\"),\n",
-    "\t\t\"Mathematik\": get_median_for(min_five_eval, \"Vorlesungen\", \"math\")\n",
-    "\t},\n",
-    "\t\"Seminar, SWP\": {\n",
-    "\t\t\"Bioinformatik\": get_median_for(min_five_eval, \"Seminare\", \"bio\"),\n",
-    "\t\t\"Informatik\": get_median_for(min_five_eval, \"Seminare\", \"cs\"),\n",
-    "\t\t\"Mathematik\": get_median_for(min_five_eval, \"Seminare\", \"math\")\n",
-    "\t},\n",
-    "\t\"Übung/Tutorium\": {\n",
-    "\t\t\"Bioinformatik\": get_median_for(min_five_eval, \"Übungen\", \"bio\"),\n",
-    "\t\t\"Informatik\": get_median_for(min_five_eval, \"Übungen\", \"cs\"),\n",
-    "\t\t\"Mathematik\": get_median_for(min_five_eval, \"Übungen\", \"math\")\n",
-    "\t}\n",
-    "}"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "### Result generation"
+    "result_frames = {}\n",
+    "for (institute, semester, df) in file_iterator():\n",
+    "\tres_df = pandas.DataFrame(data={})\n",
+    "\tgrouped = df.groupby([\"Veranstaltung-Nr.\", \"Person-Name\", \"Veranstaltung-Kategorie\", \"Veranstaltung-Name\"])\n",
+    "\tfor group in grouped:\n",
+    "\t\t# Keeps the at least five responses constraint\n",
+    "\t\tif len(group[1]) < 5:\n",
+    "\t\t\tcontinue\n",
+    "\t\thead = pandas.Series({\"Veranstaltung-Nr.\": group[0][0], \"Person-Name\": group[0][1], \"Veranstaltung-Typ\": group[0][2], \"Veranstaltung-Name\": group[0][3], \"Antworten\": len(group[1])})\n",
+    "\t\tresult = group[1].dropna(axis=\"columns\", how=\"all\").apply(calc_score).dropna(how=\"all\")\n",
+    "\t\tcombined = pandas.concat([head, result])\n",
+    "\t\tres_df = pandas.concat([res_df, combined.to_frame().T])\n",
+    "\tresult_frames[\"{0}_{1}\".format(institute,semester)] = res_df"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 27,
+   "execution_count": null,
    "metadata": {},
    "outputs": [],
    "source": [
-    "map = {\n",
-    "\t\"Bioinformatik\": \"bio\",\n",
-    "\t\"Informatik\": \"cs\",\n",
-    "\t\"Mathematik\": \"math\"\n",
-    "}\n",
-    "\n",
-    "institutes = [\"Bioinformatik\", \"Informatik\", \"Mathematik\"]\n",
-    "type_map = {\n",
-    "\t\"Vorlesung\": \"Vorlesungen\",\n",
-    "\t\"Seminar, SWP\": \"Seminare\",\n",
-    "\t\"Übung/Tutorium\": \"Übungen\"\n",
-    "}\n",
-    "\n",
-    "schema =  {\n",
-    "\t\"Vorlesung\": institutes,\n",
-    "\t\"Seminar, SWP\": institutes,\n",
-    "\t\"Übung/Tutorium\": institutes\n",
-    "}\n",
-    "\n",
-    "header = [\"Veranstaltungstyp\", \"Angemeldete LVen dieses Typs\", \"Kollegial gehaltene LVen\", \"LVen <5 Anmeldungen\", \"Bewertete LVen\", \"Davon LVen mit mind. 5 Bewertungen\", \"Evaluierte LVen* in %\", \"Anzahl angemeldeter Studierender je evaluierter LV* (Median)\", \"Anzahl ausgefüllter Fragebögen in evaluierten LVen* (Median)\"]\n",
-    "\n",
-    "result_matrix = []\n",
-    "for k, v in type_map.items():\n",
-    "\n",
-    "\tregistered = all_registered[all_registered[\"Kategorie\"] == v]\n",
-    "\n",
-    "\tcollegial_num = collegial[collegial[\"Kategorie\"] == v]\n",
-    "\tless_five = less_five_registered[less_five_registered[\"Kategorie\"] == v]\n",
-    "\t# First key\n",
-    "\tresult_matrix.append([k,len(registered), len(collegial_num), len(less_five), \n",
-    "\t\t\t\t\t\t\t\t\t\tlen(all_size[all_size[\"Veranstaltung-Kategorie\"] == v]), \n",
-    "\t\t\t\t\t\t\t\t\t\tlen(min_five_eval[min_five_eval[\"Veranstaltung-Kategorie\"] == v]), \n",
-    "\t\t\t\t\t\t\t\t\t\tpercantages_eval[k][\"all\"],\n",
-    "\t\t\t\t\t\t\t\t\t\t'-', '-'])\n",
-    "\tfor institute in institutes:\n",
-    "\t\tshort = map[institute]\n",
-    "\t\tinst_size = all_size[(all_size[\"Veranstaltung-Kategorie\"] == v) & (all_size[\"institute\"] == short)]\n",
-    "\t\tinst_five = min_five_eval[(min_five_eval[\"Veranstaltung-Kategorie\"] == v) & (min_five_eval[\"institute\"] == short)]\n",
-    "\n",
-    "\t\tinst_reg = registered[registered[\"Bereich\"] == institute]\n",
-    "\t\tcol = collegial_num[collegial_num[\"Bereich\"] == institute]\n",
-    "\t\tl_five = less_five[less_five[\"Bereich\"] == institute]\n",
-    "\n",
-    "\t\tresult_matrix.append([institute, len(inst_reg), len(col), \n",
-    "\t\t\t\t\t\t\t\t\t\t\t\tlen(l_five), len(inst_size), len(inst_five), \n",
-    "\t\t\t\t\t\t\t\t\t\t\t\tpercantages_eval[k][institute], median_stud[k][institute], \n",
-    "\t\t\t\t\t\t\t\t\t\t\t\tmedian_questionnaire[k][institute]])\n",
-    "\t\t\n",
-    "together = [\"Gesamt\"] + [result_matrix[0][i] + result_matrix[4][i] + result_matrix[8][i] for i in range(1,6)]\n",
-    "together.append(round(together[5] / together[4] * 100))\n",
-    "together += ['-'] * 2\n",
-    "result_matrix.append(together)\n",
-    "\n",
-    "res = pandas.DataFrame(data=result_matrix, columns=header)\n",
-    "\n",
-    "out_file = os.path.join(CONSTANTS.OUT_PATH, SEMESTER_NAME + \".md\")\n",
-    "res.to_markdown(out_file, index=False)"
+    "#print(result_frames)\n",
+    "def norm_name(name: str) -> str:\n",
+    "\tif ', ' not in name: return name\n",
+    "\tsplit = name.split(\", \")\n",
+    "\treturn split[1] + \" \" + split[0]\n",
+    "for (institute_semester, df) in result_frames.items():\n",
+    "\tcollection = []\n",
+    "\tinstitute, semester = institute_semester.split(\"_\") \n",
+    "\tinstitute = CONSTANTS.INSTITUTE_MAP[institute]\n",
+    "\tif \"WiSe\" in semester:\n",
+    "\t\tsemester = semester.replace(\"-\", \"/\")\n",
+    "\t\n",
+    "\tif semester in participation:\n",
+    "\t\t# This may lead to data loss but is impossible to work around\n",
+    "\t\tsemester_obj = participation[semester]\n",
+    "\t\tsemester_obj.drop_duplicates(inplace=True)\n",
+    "\t\tnames = semester_obj[\"Person-Name\"]\n",
+    "\t\tsemester_obj[\"Person-Name\"] = names.map(norm_name)\n",
+    "\t\tdf = df.merge(semester_obj[['Veranstaltung-Nr.', 'Anzahl Teilnehmende', 'Person-Name', 'Veranstaltung-Name']], on=[\"Veranstaltung-Nr.\", \"Person-Name\", \"Veranstaltung-Name\"], how=\"left\")\n",
+    "\n",
+    "\tfor row in df.iterrows():\n",
+    "\t\tcourse_object = {\n",
+    "\t\t\t\"course_number\": str(row[1][\"Veranstaltung-Nr.\"]),\n",
+    "\t\t\t\"course_name\": row[1][\"Veranstaltung-Name\"],\n",
+    "\t\t\t\"course_type\": row[1][\"Veranstaltung-Typ\"],\n",
+    "\t\t\t\"lecturer\": row[1][\"Person-Name\"],\n",
+    "\t\t\t\"semester\": semester,\n",
+    "\t\t\t\"institute\": institute,\n",
+    "\t\t\t\"answers\": row[1][\"Antworten\"],\n",
+    "\t\t\t# -1  indicated that no data is available\n",
+    "\t\t\t\"participants\": -1,\n",
+    "\t\t\t\"scores\": []\n",
+    "\t\t}\n",
+    "\n",
+    "\t\tif \"Anzahl Teilnehmende\" in df.columns:\n",
+    "\t\t\tif not math.isnan(row[1][\"Anzahl Teilnehmende\"]):\n",
+    "\t\t\t\tcourse_object[\"participants\"] = int(row[1][\"Anzahl Teilnehmende\"])\n",
+    "\n",
+    "\t\tfor col in row[1].index:\n",
+    "\t\t\tif uuid_regex.match(col) != None:\n",
+    "\t\t\t\t# Early Break for irrelevant columns\n",
+    "\t\t\t\tif math.isnan(row[1][col]):\n",
+    "\t\t\t\t\tcontinue\n",
+    "\t\t\t\tcourse_object[\"scores\"].append({\"question\": col, \"score\": int(row[1][col])})\n",
+    "\t\tcollection.append(course_object)\n",
+    "\tapi.force_courses(collection)"
    ]
   }
  ],
diff --git a/tests/scraping/complete_catalogue/test_edge_cases_catalogue.py b/tests/scraping/complete_catalogue/test_edge_cases_catalogue.py
deleted file mode 100644
index 15130dd806061c5c3ff7c019d18c70a5e44159ce..0000000000000000000000000000000000000000
--- a/tests/scraping/complete_catalogue/test_edge_cases_catalogue.py
+++ /dev/null
@@ -1,59 +0,0 @@
-import os, sys, pytest
-
-from schema import Schema, Regex, Optional
-from datetime import datetime
-
-# Makes the lib folder visible
-path = os.path.abspath(__file__)
-steps = 4
-for i in range(steps):
-	path = os.path.dirname(path)
-
-sys.path.append(path)
-import src.lib.scraping as scraping
-
-# Tests all working and non working edge cases
-@pytest.mark.parametrize('input', ['Wintersemester 2012/13', 'Sommersemester 2008', 'Sommersemester 2007'])
-def test_non_working_edge_cases_get_complete_catalogue_for(input):
-
-	with pytest.raises(AttributeError) as execinfo:
-		scraping.get_complete_catalogue_for(input, "cs")
-	assert (input + " is not supported") in str(execinfo.value)
-
-@pytest.mark.skip(reason="Utility Function")
-def calc_semester(month, year):
-	if(month > 3 and month < 10):
-		return "Sommersemester " + str(year)
-	elif month <= 3:
-		return "Wintersemester " + str(year - 1) + "/" + str(year)[-2:]
-	return "Wintersemester " + str(year) + "/" + str(year + 1)[-2:]
-
-# Tests with edge cases of month = 2, = 10 and = 3
-# Also tests regular cases
-@pytest.mark.parametrize("month, year, expected", [(3, 2023, "Wintersemester 2022/23"), 
-																									 (4, 2024, "Sommersemester 2024"), 
-																									 (10, 2024, "Wintersemester 2024/25"),
-																									 (12, 2022, "Wintersemester 2022/23"),
-																									 (1, 2012, "Wintersemester 2011/12"),
-																									 (6, 2014, "Sommersemester 2014")])
-def test_calc_semester(month, year, expected):
-	assert calc_semester(month, year) == expected
-
-# 2013 is the oldest working semester, newest working semester gets calculated
-semesters = ["Sommersemester 2013", calc_semester(datetime.now().month, datetime.now().year)]
-tuples = [(semester, institute) for semester in semesters for institute in ["cs", "bio", "math"]]
-
-@pytest.mark.parametrize("semester,institute", tuples)
-def test_working_edge_cases_get_complete_catalogue_for(semester, institute):
-	result_schema = Schema({
-		Regex(r'\d+'): {
-			'name': str,
-			'lecturers': [str],
-			'type': str,
-			Optional('amount'): int
-		}
-	})
-
-	result = scraping.get_complete_catalogue_for(semester, institute)
-
-	assert result_schema.is_valid(result)
\ No newline at end of file
diff --git a/tests/scraping/complete_catalogue/test_function_catalogue.py b/tests/scraping/complete_catalogue/test_function_catalogue.py
deleted file mode 100644
index a1d4c444ba936bef06ac7293cb8dbe94375fe781..0000000000000000000000000000000000000000
--- a/tests/scraping/complete_catalogue/test_function_catalogue.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import os, sys, pytest
-
-# Makes the lib folder visible
-path = os.path.abspath(__file__)
-steps = 4
-for i in range(steps):
-	path = os.path.dirname(path)
-
-sys.path.append(path)
-import src.lib.scraping as scraping
-
-@pytest.mark.parametrize("semester, institute, expected", [("Sommersemester 2016", "cs", 90), 
-																													 ("Wintersemester 2018/19", "math", 173),
-																													 ("Wintersemester 2023/24", "bio", 64)])
-def test_get_complete_catalogue_for(semester, institute, expected):
-	result = scraping.get_complete_catalogue_for(semester, institute)
-	assert len(result) == expected
\ No newline at end of file
diff --git a/tests/scraping/complete_catalogue/test_invalid_input_catalogue.py b/tests/scraping/complete_catalogue/test_invalid_input_catalogue.py
deleted file mode 100644
index 52753e907dcbde812bcb9d0baa188c89cf8f542f..0000000000000000000000000000000000000000
--- a/tests/scraping/complete_catalogue/test_invalid_input_catalogue.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import os, sys, pytest
-
-# Makes the lib folder visible
-path = os.path.abspath(__file__)
-steps = 4
-for i in range(steps):
-	path = os.path.dirname(path)
-
-sys.path.append(path)
-import src.lib.scraping as scraping
-
-@pytest.mark.parametrize("semester, institute", [("Sommersemester 2005", "cs"),
-																								("Sommersemester 2016", "Lorem Ipsum")])
-def test_invalid_input_get_complete_catalogue_for(semester, institute):
-	with pytest.raises(AttributeError) as execinfo:
-		scraping.get_complete_catalogue_for(semester, institute)
-	assert (semester + " is not supported.") in str(execinfo.value) or (institute + " is not supported") in str(execinfo.value)
diff --git a/tests/scraping/get_from_id/test_function_get_from_id.py b/tests/scraping/get_from_id/test_function_get_from_id.py
deleted file mode 100644
index 4aa5740f7ecbfe9044b608334e9e5b5ffe67737f..0000000000000000000000000000000000000000
--- a/tests/scraping/get_from_id/test_function_get_from_id.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import os, sys
-
-from schema import Schema, Regex
-
-# Makes the lib folder visible
-path = os.path.abspath(__file__)
-steps = 4
-for i in range(steps):
-	path = os.path.dirname(path)
-
-sys.path.append(path)
-import src.lib.scraping as scraping
-
-def test_valid_id():
-	id = "19201401"
-
-	result_schema = Schema({
-		'id': str,
-		'name': str,
-		'type': str
-	})
-
-	res = scraping.get_course(id)
-	assert result_schema.is_valid(res)
-
-def test_invalid_id():
-	res = scraping.get_course("0000000")
-	assert res == None
\ No newline at end of file
diff --git a/tests/scraping/semester_urls/test_function_semester_urls.py b/tests/scraping/semester_urls/test_function_semester_urls.py
deleted file mode 100644
index 8108189f7794cadd866f1f71c13a6ab867781f82..0000000000000000000000000000000000000000
--- a/tests/scraping/semester_urls/test_function_semester_urls.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import os, sys
-
-from schema import Schema, Regex
-
-# Makes the lib folder visible
-path = os.path.abspath(__file__)
-steps = 4
-for i in range(steps):
-	path = os.path.dirname(path)
-
-sys.path.append(path)
-import src.lib.scraping as scraping
-
-def test_get_semester_ids():
-	semester_regex = r"((Sommer)|(Winter))semester \d+(\/\d+)?"
-	url_regex = r"(http)s?:\/\/((www)|archiv.vv).fu-berlin.de\/((vv\/de\/modul\?sm=\d+)|((w|s)s\d+\/de\/))"
-
-	result = scraping.get_semester_urls()
-	result_schema = Schema({Regex(semester_regex):Regex(url_regex)})
-
-	assert result_schema.is_valid(result), "Output should be [Winter or Sommer]semester [Number]: [Url]"
\ No newline at end of file