diff --git a/dta/classes/backend.php b/dta/classes/backend.php
new file mode 100644
index 0000000000000000000000000000000000000000..7ca08f49e180ca0a2bd41bfe06c9bbf2bc15c01f
--- /dev/null
+++ b/dta/classes/backend.php
@@ -0,0 +1,144 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains the backend webservice contact functionality for the DTA plugin
+ *
+ * @package    assignsubmission_dta
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @copyright Gero Lueckemeyer and student project teams
+ */
+
+/**
+ * backend webservice contact utility class
+ *
+ * @package assignsubmission_dta
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @copyright Gero Lueckemeyer and student project teams
+ */
+class DtaBackendUtils {
+
+    /**
+     * Returns the base url of the backend webservice as configured in the administration settings.
+     * @return string backend host base url
+     */
+    private static function getbackendbaseurl(): string {
+        $backendaddress = get_config(assign_submission_dta::COMPONENT_NAME, "backendHost");
+
+        if (empty($backendaddress)) {
+            \core\notification::error(get_string("backendHost_not_set", assign_submission_dta::COMPONENT_NAME));
+        }
+
+        return $backendaddress;
+    }
+
+    /**
+     * Sends the configuration textfile uploaded by prof to the backend.
+     *
+     * @param stdClass $assignment assignment this test-config belongs to
+     * @param stdClass $file uploaded test-config
+     * @return bool true if no error occurred
+     */
+    public static function sendtestconfigtobackend($assignment, $file): bool {
+        $backendaddress = self::getbackendbaseurl();
+        if (empty($backendaddress)) {
+            return true;
+        }
+
+        // Set endpoint for test upload.
+        $url = $backendaddress . "/v1/unittest";
+
+        // Prepare params.
+        $params = [
+            "unitTestFile" => $file,
+            "assignmentId" => $assignment->get_instance()->id,
+        ];
+
+        // If request returned null, return false to indicate failure.
+        if (is_null(self::post($url, $params))) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    /**
+     * Sends submission config or archive to backend to be tested.
+     *
+     * @param stdClass $assignment assignment for the submission
+     * @param int $submissionid submissionid of the current file
+     * @param stdClass $file submission config file or archive with submission
+     * @return string json string with testresults or null on error
+     */
+    public static function sendsubmissiontobackend($assignment, $submissionid, $file): ?string {
+        $backendaddress = self::getbackendbaseurl();
+        if (empty($backendaddress)) {
+            return true;
+        }
+
+        // Set endpoint for test upload.
+        $url = $backendaddress . "/v1/task/" . $submissionid;
+
+        // Prepare params.
+        $params = [
+            "taskFile" => $file,
+            "assignmentId" => $assignment->get_instance()->id,
+        ];
+
+        return self::post($url, $params);
+    }
+
+    /**
+     * Posts the given params to the given url and returns the response as a string.
+     * @param string $url full url to request to
+     * @param array $params parameters for http-request
+     *
+     * @return string received body on success or null on error
+     */
+    private static function post($url, $params): ?string {
+        if (!isset($url) || !isset($params)) {
+            return false;
+        }
+
+        $options = ["CURLOPT_RETURNTRANSFER" => true];
+
+        $curl = new curl();
+        $response = $curl->post($url, $params, $options);
+
+        // Check state of request, if response code is a 2xx return the answer.
+        $info = $curl->get_info();
+        if ($info["http_code"] >= 200 && $info["http_code"] < 300) {
+            return $response;
+        }
+
+        // Something went wrong, return null and give an error message.
+        debugging(assign_submission_dta::COMPONENT_NAME . ": Post file to server was not successful: http_code=" .
+        $info["http_code"]);
+
+        if ($info['http_code'] >= 400 && $info['http_code'] < 500) {
+            \core\notification::error(get_string("http_client_error_msg", assign_submission_dta::COMPONENT_NAME));
+            return null;
+        } else if ($info['http_code'] >= 500 && $info['http_code'] < 600) {
+            \core\notification::error(get_string("http_server_error_msg", assign_submission_dta::COMPONENT_NAME));
+            return null;
+        } else {
+            \core\notification::error(get_string("http_unknown_error_msg", assign_submission_dta::COMPONENT_NAME) .
+            $info["http_code"] . $response);
+            return null;
+        }
+    }
+
+}
diff --git a/dta/classes/database.php b/dta/classes/database.php
new file mode 100644
index 0000000000000000000000000000000000000000..6f5c82a94f5545911657831ebbadbe1ca5f0ece0
--- /dev/null
+++ b/dta/classes/database.php
@@ -0,0 +1,165 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * persistence layer utility class
+ *
+ * @package assignsubmission_dta
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @copyright Gero Lueckemeyer and student project teams
+ */
+class DbUtils {
+
+    /**
+     * Summary database table name.
+     */
+    private const TABLE_SUMMARY = "assignsubmission_dta_summary";
+    /**
+     * Result database table name.
+     */
+    private const TABLE_RESULT = "assignsubmission_dta_result";
+
+    /**
+     * gets summary with all corresponding result entries
+     *
+     * @param int $assignmentid assignment id to search for
+     * @param int $submissionid submission id to search for
+     * @return DttResultSummary representing given submission
+     */
+    public static function getresultsummaryfromdatabase(
+        int $assignmentid,
+        int $submissionid
+    ): DtaResultSummary {
+        global $DB;
+
+        // Fetch data from database.
+        $summaryrecord = $DB->get_record(self::TABLE_SUMMARY, [
+            "assignment_id" => $assignmentid,
+            "submission_id" => $submissionid,
+        ]);
+
+        $resultsarray = $DB->get_records(self::TABLE_RESULT, [
+            "assignment_id" => $assignmentid,
+            "submission_id" => $submissionid,
+        ]);
+
+        // Create a summary instance.
+        $summary = new DtaResultSummary();
+        $summary->timestamp = $summaryrecord->timestamp;
+        $summary->globalstacktrace = $summaryrecord->global_stacktrace;
+        $summary->successfultestcompetencies = $summaryrecord->successful_competencies;
+        $summary->overalltestcompetencies = $summaryrecord->tested_competencies;
+        $summary->results = [];
+
+        // Create result instances and add to array of summary instance.
+        foreach ($resultsarray as $rr) {
+            $result = new DtaResult();
+            $result->packagename = $rr->package_name;
+            $result->classname = $rr->class_name;
+            $result->name = $rr->name;
+            $result->state = $rr->state;
+            $result->failuretype = $rr->failure_type;
+            $result->failurereason = $rr->failure_reason;
+            $result->stacktrace = $rr->stacktrace;
+            $result->columnnumber = $rr->column_number;
+            $result->linenumber = $rr->line_number;
+            $result->position = $rr->position;
+
+            $summary->results[] = $result;
+        }
+
+        return $summary;
+    }
+
+    /**
+     * save given result summary and single results to database
+     * under given assignment and submission id
+     *
+     * @param int $assignmentid assigment this is submission is linked to
+     * @param int $submissionid submission of this result
+     * @param DtaResultSummary $summary instance to persist
+     */
+    public static function storeresultsummarytodatabase(
+        int $assignmentid,
+        int $submissionid,
+        DtaResultSummary $summary
+    ): void {
+        global $DB;
+
+        // Prepare new database entries.
+        $summaryrecord = new stdClass();
+        $summaryrecord->assignment_id = $assignmentid;
+        $summaryrecord->submission_id = $submissionid;
+        $summaryrecord->timestamp = $summary->timestamp;
+        $summaryrecord->global_stacktrace = $summary->globalstacktrace;
+        $summaryrecord->successful_competencies = $summary->successfultestcompetencies;
+        $summaryrecord->tested_competencies = $summary->overalltestcompetencies;
+
+        // Prepare results to persist to array.
+        $resultrecords = [];
+        foreach ($summary->results as $r) {
+            $record = new stdClass();
+            $record->assignment_id = $assignmentid;
+            $record->submission_id = $submissionid;
+            $record->package_name = $r->packagename;
+            $record->class_name = $r->classname;
+            $record->name = $r->name;
+            $record->state = $r->state;
+            $record->failure_type = $r->failuretype;
+            $record->failure_reason = $r->failurereason;
+            $record->stacktrace = $r->stacktrace;
+            $record->column_number = $r->columnnumber;
+            $record->line_number = $r->linenumber;
+            $record->position = $r->position;
+            $resultrecords[] = $record;
+        }
+
+        // If results already exist, delete old values beforehand.
+        $submission = $DB->get_record(self::TABLE_SUMMARY, [
+            'assignment_id' => $assignmentid,
+            'submission_id' => $submissionid,
+        ]);
+
+        if ($submission) {
+            $DB->delete_records(self::TABLE_RESULT, [
+                'assignment_id' => $assignmentid,
+                'submission_id' => $submissionid,
+            ]);
+
+            $DB->delete_records(self::TABLE_SUMMARY, [
+                'assignment_id' => $assignmentid,
+                'submission_id' => $submissionid,
+            ]);
+        }
+
+        // Create summary and single result entries.
+        $DB->insert_record(self::TABLE_SUMMARY, $summaryrecord);
+        foreach ($resultrecords as $rr) {
+            $DB->insert_record(self::TABLE_RESULT, $rr);
+        }
+    }
+
+    /**
+     * cleans up database if plugin is uninstalled
+     */
+    public static function uninstallplugincleaup(): void {
+        global $DB;
+
+        $DB->delete_records(self::TABLE_RESULT, null);
+        $DB->delete_records(self::TABLE_SUMMARY, null);
+    }
+
+}
diff --git a/dta/classes/view.php b/dta/classes/view.php
new file mode 100644
index 0000000000000000000000000000000000000000..b93d3e57ead9c84bee90de33277b149217d65668
--- /dev/null
+++ b/dta/classes/view.php
@@ -0,0 +1,426 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * utility class for DTA submission plugin result display
+ *
+ * @package assignsubmission_dta
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @copyright Gero Lueckemeyer and student project teams
+ */
+class view_submission_utils {
+
+    /**
+     * Broadly used in logic, parametrized for easier change.
+     */
+    const COMPONENT_NAME = "assignsubmission_dta";
+
+    /**
+     * generates a short summary html
+     *
+     * @param int $assignmentid assignment
+     * @param int $submissionid submission to create a report for
+     * @return string html
+     */
+    public static function generatesummaryhtml(
+        int $assignmentid,
+        int $submissionid
+    ): string {
+
+        // Fetch data.
+        $summary = DbUtils::getResultSummaryFromDatabase($assignmentid, $submissionid);
+        $html = "";
+
+        // Calculate success rate, if no unknown result states or compilation errors.
+        $successrate = "?";
+        if ($summary->unknownCount() == 0 && $summary->compilationErrorCount() == 0) {
+            $successrate = round(($summary->successfulCount() / $summary->resultCount()) * 100, 2 );
+        }
+
+        // Generate html.
+        $html .= $summary->successfulCount() . "/";
+        $html .= ($summary->compilationErrorCount() == 0 && $summary->unknownCount() == 0)
+            ? $summary->resultCount() . " (" . $successrate . "%)"
+                : "?";
+        $html .= get_string("tests_successful", self::COMPONENT_NAME) . "<br />";
+
+        if ($summary->compilationErrorCount() > 0) {
+            $html .= $summary->compilationErrorCount() . get_string("compilation_errors", self::COMPONENT_NAME) . "<br />";
+        }
+
+        if ($summary->unknownCount() > 0) {
+            $html .= $summary->unknownCount() . get_string("unknown_state", self::COMPONENT_NAME) . "<br />";
+        }
+
+        $showncompetencies = explode(";", $summary->successfultestcompetencies);
+        $overallcompetencies = explode(";", $summary->overalltestcompetencies);
+
+        $tmp = "";
+        for ($index = 0, $size = count($showncompetencies); $index < $size; $index++) {
+            $shown = $showncompetencies[$index];
+            $comp = $overallcompetencies[$index];
+            // If the competency was actually assessed by the assignment and tests, add a summary entry.
+            if ($shown != "0") {
+                $tmp .= get_string("comp" . $index, self::COMPONENT_NAME) .
+                " " . 100 * floatval($shown) / floatval($comp) . "% " . "<br />";
+            }
+        }
+
+        $html .= get_string("success_competencies", self::COMPONENT_NAME) . "<br />" . $tmp . "<br />";
+
+        return html_writer::div($html, "dtaSubmissionSummary");
+    }
+
+    /**
+     * generates detailed view html
+     *
+     * @param int $assignmentid assignment
+     * @param int $submissionid submission to create a report for
+     */
+    public static function generatedetailhtml(
+        int $assignmentid,
+        int $submissionid
+    ): string {
+
+        // Fetch data.
+        $summary = DbUtils::getResultSummaryFromDatabase($assignmentid, $submissionid);
+        $html = "";
+
+        // Define a few css classes and prepare html attribute arrays to beautify the output.
+        $tableheaderrowattributes = ["class" => "dtaTableHeaderRow"];
+        $tablerowattributes = ["class" => "dtaTableRow"];
+        $resultrowattributes = $tablerowattributes;
+        $unknownattributes = 'dtaResultUnknown';
+        $successattributes = 'dtaResultSuccess';
+        $failureattributes = 'dtaResultFailure';
+        $compilationerrorattributes = 'dtaResultCompilationError';
+
+        // Summary table.
+        $tmp = "";
+        $tmp .= html_writer::tag("th", get_string("summary", self::COMPONENT_NAME), ["class" => "dtaTableHeader"]);
+        $tmp .= html_writer::empty_tag("th", ["class" => "dtaTableHeader"]);
+        $header = html_writer::tag("tr", $tmp, $tableheaderrowattributes);
+        $header = html_writer::tag("thead", $header);
+
+        $body = "";
+        $tmp = "";
+        $attributes = ["class" => "dtaTableData"];
+        $tmp .= html_writer::tag(
+            "td",
+            get_string("total_items", self::COMPONENT_NAME),
+            $attributes);
+
+        $tmp .= html_writer::tag(
+            "td",
+            $summary->resultCount(),
+            $attributes);
+
+        $resultrowattributes = $tablerowattributes;
+        $resultrowattributes['class'] = $resultrowattributes['class'] . " " . $unknownattributes;
+
+        $body .= html_writer::tag("tr", $tmp, $resultrowattributes);
+
+        $tmp = "";
+        $tmp .= html_writer::tag("td", get_string("tests_successful", self::COMPONENT_NAME), $attributes);
+        $tmp .= html_writer::tag( "td", $summary->successfulCount(), $attributes);
+
+        $resultrowattributes = $tablerowattributes;
+        $successrate = "?";
+
+        if ($summary->unknownCount() > 0 || $summary->compilationErrorCount() > 0) {
+            $resultrowattributes['class'] = $resultrowattributes['class'] . " " . $unknownattributes;
+        } else {
+            $successrate = round(($summary->successfulCount() / $summary->resultCount()) * 100, 2 );
+            if ($successrate < 50) {
+                $resultrowattributes['class'] = $resultrowattributes['class'] . " " . $compilationerrorattributes;
+            } else if ($successrate < 75) {
+                $resultrowattributes['class'] = $resultrowattributes['class'] . " " . $failureattributes;
+            } else {
+                $resultrowattributes['class'] = $resultrowattributes['class'] . " " . $successattributes;
+            }
+        }
+        $body .= html_writer::tag("tr", $tmp, $resultrowattributes);
+
+        $tmp = "";
+        $tmp .= html_writer::tag("td", get_string("failures", self::COMPONENT_NAME), $attributes);
+        $tmp .= html_writer::tag("td", $summary->failedCount(), $attributes);
+
+        $resultrowattributes = $tablerowattributes;
+        if ($summary->failedCount() > 0) {
+            $resultrowattributes['class'] = $resultrowattributes['class'] . " " . $failureattributes;
+        } else {
+            $resultrowattributes['class'] = $resultrowattributes['class'] . " " . $successattributes;
+        }
+        $body .= html_writer::tag("tr", $tmp, $resultrowattributes);
+
+        $tmp = "";
+        $tmp .= html_writer::tag("td", get_string("compilation_errors", self::COMPONENT_NAME), $attributes);
+        $tmp .= html_writer::tag("td", $summary->compilationErrorCount(), $attributes);
+
+        $resultrowattributes = $tablerowattributes;
+        if ($summary->compilationErrorCount() > 0) {
+            $resultrowattributes['class'] = $resultrowattributes['class'] . " " . $compilationerrorattributes;
+        } else {
+            $resultrowattributes['class'] = $resultrowattributes['class'] . " " . $successattributes;
+        }
+        $body .= html_writer::tag("tr", $tmp, $resultrowattributes);
+
+        $tmp = "";
+        $tmp .= html_writer::tag("td", get_string("unknown_state", self::COMPONENT_NAME), $attributes);
+        $tmp .= html_writer::tag("td", $summary->unknownCount(), $attributes);
+
+        $resultrowattributes = $tablerowattributes;
+        if ($summary->unknownCount() > 0) {
+            $resultrowattributes['class'] = $resultrowattributes['class'] . " " . $unknownattributes;
+        } else {
+            $resultrowattributes['class'] = $resultrowattributes['class'] . " " . $successattributes;
+        }
+        $body .= html_writer::tag("tr", $tmp, $resultrowattributes);
+
+        $tmp = "";
+        $tmp .= html_writer::tag("td", html_writer::tag("b", get_string("success_rate", self::COMPONENT_NAME)), $attributes);
+        $tmp .= html_writer::tag(
+            "td",
+            html_writer::tag("b", $summary->successfulCount()
+                . "/" . (($summary->compilationErrorCount() == 0 && $summary->unknownCount() == 0) ? $summary->resultCount()
+                . " (" . $successrate . "%)"
+                    : "?")),
+            $attributes);
+
+        $resultrowattributes = $tablerowattributes;
+        if ($summary->unknownCount() > 0 || $summary->compilationErrorCount() > 0) {
+            $resultrowattributes['class'] = $resultrowattributes['class'] . " " . $unknownattributes;
+        } else {
+            if ($successrate < 50) {
+                $resultrowattributes['class'] = $resultrowattributes['class'] . " " . $compilationerrorattributes;
+            } else if ($successrate < 75) {
+                $resultrowattributes['class'] = $resultrowattributes['class'] . " " . $failureattributes;
+            } else {
+                $resultrowattributes['class'] = $resultrowattributes['class'] . " " . $successattributes;
+            }
+        }
+        $body .= html_writer::tag("tr", $tmp, $resultrowattributes);
+
+        $body = html_writer::tag("tbody", $body);
+        $table = html_writer::tag("table", $header . $body, ["class" => "dtaTable"]);
+
+        $html .= $table;
+
+        // Add empty div for spacing between summary and compentency table.
+        $html .= html_writer::empty_tag("div", ["class" => "dtaSpacer"]);
+
+        // Competency assessment table.
+        $body = "";
+        $tmp = "";
+        $tmp .= html_writer::tag("th", get_string("competencies", self::COMPONENT_NAME), ["class" => "dtaTableHeader"]);
+        $tmp .= html_writer::empty_tag("th", ["class" => "dtaTableHeader"]);
+        $header = html_writer::tag("tr", $tmp, $tableheaderrowattributes);
+        $header = html_writer::tag("thead", $header);
+
+        $showncompetencies = explode(";", $summary->successfultestcompetencies);
+        $overallcompetencies = explode(";", $summary->overalltestcompetencies);
+
+        for ($index = 0, $size = count($overallcompetencies); $index < $size; $index++) {
+            $comp = $overallcompetencies[$index];
+            $shown = $showncompetencies[$index];
+            // If the competency was actually assessed by the assignment and tests, add a row in the table.
+            if ($comp != "0") {
+                // New copy of base attributes array.
+                $resultrowattributes = $tablerowattributes;
+                $tmp = "";
+                $tmp .= html_writer::tag("td", get_string("comp" . $index, self::COMPONENT_NAME), $resultrowattributes);
+                $tmp .= html_writer::tag("td", 100 * floatval($shown) / floatval($comp) . "% " .
+                    "(" . $shown . " / " . $comp . ")", $resultrowattributes);
+                $tmp .= html_writer::tag("td", get_string("comp_expl" . $index, self::COMPONENT_NAME), $resultrowattributes);
+
+                $body .= html_writer::tag("tr", $tmp, $resultrowattributes);
+            }
+        }
+        $body = html_writer::tag("tbody", $body);
+        $html .= html_writer::tag("table", $header . $body, ["class" => "dtaTable"]);
+
+        // Add empty div for spacing between competency and details table.
+        $html .= html_writer::empty_tag("div", ["class" => "dtaSpacer"]);
+
+        // Details table.
+        $tmp = "";
+        $tmp .= html_writer::tag("th", get_string("details", self::COMPONENT_NAME), ["class" => "dtaTableHeader"]);
+        $tmp .= html_writer::empty_tag("th", ["class" => "dtaTableHeader"]);
+        $header = html_writer::tag("tr", $tmp, $tableheaderrowattributes);
+        $header = html_writer::tag("thead", $header);
+
+        $body = "";
+        $spacerrow = null;
+        foreach ($summary->results as $r) {
+            // Add spacer first if not null.
+            if (!is_null($spacerrow)) {
+                $body .= $spacerrow;
+            }
+
+            // New copy of base attributes array.
+            $resultrowattributes = $tablerowattributes;
+
+            // Check which css class to add for the colored left-border according to resuls state.
+            if ($r->state == 0) {
+                $resultrowattributes['class'] = $resultrowattributes['class'] . ' dtaResultUnknown';
+            } else if ($r->state == 1) {
+                $resultrowattributes['class'] = $resultrowattributes['class'] . ' dtaResultSuccess';
+            } else if ($r->state == 2) {
+                $resultrowattributes['class'] = $resultrowattributes['class'] . ' dtaResultFailure';
+            } else if ($r->state == 3) {
+                $resultrowattributes['class'] = $resultrowattributes['class'] . ' dtaResultCompilationError';
+            }
+
+            $tmp = "";
+            $tmp .= html_writer::tag(
+                "td",
+                get_string("package_name", self::COMPONENT_NAME),
+                $attributes);
+
+            $tmp .= html_writer::tag(
+                "td",
+                $r->packagename,
+                $attributes);
+
+            $tmp .= html_writer::tag(
+                "td",
+                get_string("unit_name", self::COMPONENT_NAME),
+                $attributes);
+
+            $tmp .= html_writer::tag(
+                "td",
+                $r->classname,
+                $attributes);
+
+            $tmp .= html_writer::tag(
+                "td",
+                get_string("test_name", self::COMPONENT_NAME),
+                $attributes);
+
+            $tmp .= html_writer::tag(
+                "td",
+                $r->name,
+                $attributes);
+            $body .= html_writer::tag("tr", $tmp, $resultrowattributes);
+
+            $tmp = "";
+            $tmp .= html_writer::tag(
+                "td",
+                get_string("status", self::COMPONENT_NAME),
+                $attributes);
+
+            $tmp .= html_writer::tag(
+                "td",
+                DtaResult::getStateName($r->state),
+                $attributes);
+            $body .= html_writer::tag("tr", $tmp, $resultrowattributes);
+
+            // If state is something different than successful, show additional rows.
+            if ($r->state != 1) {
+                $tmp = "";
+                $tmp .= html_writer::tag(
+                    "td",
+                    get_string("failure_type", self::COMPONENT_NAME),
+                    $attributes);
+
+                $tmp .= html_writer::tag(
+                    "td",
+                    $r->failureType,
+                    $attributes);
+                $body .= html_writer::tag("tr", $tmp, $resultrowattributes);
+
+                $tmp = "";
+                $tmp .= html_writer::tag(
+                    "td",
+                    get_string("failure_reason", self::COMPONENT_NAME),
+                    $attributes);
+
+                $tmp .= html_writer::tag(
+                    "td",
+                    $r->failureReason,
+                    $attributes);
+                $body .= html_writer::tag("tr", $tmp, $resultrowattributes);
+
+                // Only show line, column and position if they have useful values.
+                if (!is_null($r->lineNumber) && $r->lineNumber > 0) {
+                    $tmp = "";
+                    $tmp .= html_writer::tag(
+                        "td",
+                        get_string("line_no", self::COMPONENT_NAME),
+                        $attributes);
+
+                    $tmp .= html_writer::tag(
+                        "td",
+                        $r->lineNumber,
+                        $attributes);
+                    $body .= html_writer::tag("tr", $tmp, $resultrowattributes);
+                }
+
+                if (!is_null($r->columnNumber) && $r->columnNumber > 0) {
+                    $tmp = "";
+                    $tmp .= html_writer::tag(
+                        "td",
+                        get_string("col_no", self::COMPONENT_NAME),
+                        $attributes);
+
+                    $tmp .= html_writer::tag(
+                        "td",
+                        $r->columnNumber,
+                        $attributes);
+                    $body .= html_writer::tag("tr", $tmp, $resultrowattributes);
+                }
+
+                if (!is_null($r->position) && $r->position > 0) {
+                    $tmp = "";
+                    $tmp .= html_writer::tag(
+                        "td",
+                        get_string("pos", self::COMPONENT_NAME),
+                        $attributes);
+
+                    $tmp .= html_writer::tag(
+                        "td",
+                        $r->position,
+                        $attributes);
+                    $body .= html_writer::tag("tr", $tmp, $resultrowattributes);
+                }
+
+                $tmp = "";
+                $tmp .= html_writer::tag(
+                    "td",
+                    get_string("stacktrace", self::COMPONENT_NAME),
+                    $attributes);
+
+                $tmp .= html_writer::tag(
+                    "td",
+                    html_writer::tag("details", $r->stacktrace, ["class" => "dtaStacktraceDetails"]),
+                    $attributes);
+                $body .= html_writer::tag("tr", $tmp, $resultrowattributes);
+            }
+
+            // Set spacerrow value if null for next round separation.
+            if (is_null($spacerrow)) {
+                $spacerrow = html_writer::empty_tag("tr", ["class" => "dtaTableSpacer"]);
+            }
+        }
+        $html .= html_writer::tag("table", $header . $body, ["class" => "dtaTable"]);
+
+        // Wrap generated html into final div.
+        $html = html_writer::div($html, "dtaSubmissionDetails");
+
+        return $html;
+    }
+
+}
diff --git a/dta/models/DtaResult.php b/dta/models/DtaResult.php
new file mode 100644
index 0000000000000000000000000000000000000000..bd7d4b0842ac26d8a94145426fcb096c0014b0ce
--- /dev/null
+++ b/dta/models/DtaResult.php
@@ -0,0 +1,247 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * entity classes for DTA submission plugin result summary and test results
+ *
+ * @package assignsubmission_dta
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @copyright Gero Lueckemeyer and student project teams
+ */
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * entity class for DTA submission plugin result
+ *
+ * @package assignsubmission_dta
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @copyright Gero Lueckemeyer and student project teams
+ */
+class DtaResult {
+
+    /**
+     * Broadly used in logic, parametrized for easier change.
+     */
+    const COMPONENT_NAME = "assignsubmission_dta";
+
+    /**
+     * @var $packagename Package name of the test.
+     */
+    public $packagename;
+
+    /**
+     * @var $classname Unit name of the test.
+     */
+    public $classname;
+
+    /**
+     * @var $name Name of the test.
+     */
+    public $name;
+
+    /**
+     * @var $state State is defined like below
+     *
+     *  0 UNKNOWN
+     *  1 SUCCESS
+     *  2 FAILURE
+     *  3 COMPILATIONERROR
+     */
+    public $state;
+
+    /**
+     * @var $failuretype Type of test failure if applicable, "" otherwise.
+     */
+    public $failuretype;
+
+    /**
+     * @var $failurereason Reason of test failure if applicable, "" otherwise.
+     */
+    public $failurereason;
+
+    /**
+     * @var $stacktrace Stack trace of test failure if applicable, "" otherwise.
+     */
+    public $stacktrace;
+
+    /**
+     * @var $columnnumber Column number of compile failure if applicable, "" otherwise.
+     */
+    public $columnnumber;
+    /**
+     * @var $linenumber Line number of compile failure if applicable, "" otherwise.
+     */
+    public $linenumber;
+    /**
+     * @var $position Position of compile failure if applicable, "" otherwise.
+     */
+    public $position;
+
+    /**
+     * Returns the name of a state with the given number of display.
+     * @param int $state number of the state
+     * @return string name of state as defined
+     */
+    public static function getstatename(int $state): string {
+        if ($state == 1) {
+            return get_string("tests_successful", self::COMPONENT_NAME);
+        } else if ($state == 2) {
+            return get_string("failures", self::COMPONENT_NAME);
+        } else if ($state == 3) {
+            return get_string("compilation_errors", self::COMPONENT_NAME);
+        } else {
+            return get_string("unknown_state", self::COMPONENT_NAME);
+        }
+    }
+}
+
+/**
+ * entity class for DTA submission plugin result
+ *
+ * @package assignsubmission_dta
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @copyright Gero Lueckemeyer and student project teams
+ */
+class DtaResultSummary {
+
+    /**
+     * @var $timestamp Result timestamp for chronological ordering and deletion of previous results.
+     */
+    public $timestamp;
+
+    /**
+     * @var $globalstacktrace Global stack trace if applicable, "" otherwise.
+     */
+    public $globalstacktrace;
+
+    /**
+     * @var $successfultestcompetencies Successfully tested competencies according to tests and weights, "" otherwise.
+     */
+    public $successfultestcompetencies;
+    /**
+     * @var overalltestcompetencies Overall tested competencies according to tests and weights, "" otherwise.
+     */
+    public $overalltestcompetencies;
+    /**
+     * @var results List of detail results.
+     */
+    public $results;
+
+    /**
+     * Decodes the JSON result summary returned by the backend service call into the plugin PHP data structure.
+     * @param string $jsonstring jsonString containing DtaResultSummary
+     * @return DtaResultSummary the result summary
+     */
+    public static function decodejson(string $jsonstring): DtaResultSummary {
+        $response = json_decode($jsonstring);
+
+        $summary = new DtaResultSummary();
+        $summary->timestamp = $response->timestamp;
+        $summary->globalstacktrace = $response->globalstacktrace;
+
+        $summary->successfultestcompetencies = $response->successfulTestCompetencyProfile;
+        $summary->overalltestcompetencies = $response->overallTestCompetencyProfile;
+
+        $summary->results = self::decodejsonresultarray($response->results);
+
+        return $summary;
+    }
+
+    /**
+     * Decodes the array of JSON detail results returned by the backend service call into the plugin PHP data structure.
+     * @param array $jsonarray decoded json array of results array
+     * @return array of DtaResult
+     */
+    private static function decodejsonresultarray($jsonarray): array {
+        $ret = [];
+        foreach ($jsonarray as $entry) {
+            $value = new DtaResult();
+            $value->packagename = $entry->packageName;
+            $value->classname = $entry->className;
+            $value->name = $entry->name;
+
+            $value->state = $entry->state;
+
+            $value->failuretype = $entry->failureType;
+            $value->failurereason = $entry->failureReason;
+            $value->stacktrace = $entry->stacktrace;
+
+            $value->columnnumber = $entry->columnNumber;
+            $value->linenumber = $entry->lineNumber;
+            $value->position = $entry->position;
+
+            $ret[] = $value;
+        }
+        return $ret;
+    }
+
+
+    /**
+     * Returns the number of detail results attached to the summary.
+     * @return int count of occurences
+     */
+    public function resultcount(): int {
+        return count($this->results);
+    }
+
+    /**
+     * Returns the number of detail results with the given state attached to the summary.
+     * @param int $state state ordinal number
+     * @return int count of occurences provided state has
+     */
+    public function stateoccurencecount(int $state): int {
+        $num = 0;
+        foreach ($this->results as $r) {
+            if ($r->state == $state) {
+                $num++;
+            }
+        }
+        return $num;
+    }
+
+    /**
+     * Returns the number of detail results with compilation errors attached to the summary.
+     * @return int count of occurences
+     */
+    public function compilationerrorcount(): int {
+        return $this->stateoccurencecount(3);
+    }
+
+    /**
+     * Returns the number of detail results with test failures attached to the summary.
+     * @return int count of occurences
+     */
+    public function failedcount(): int {
+        return $this->stateoccurencecount(2);
+    }
+
+    /**
+     * Returns the number of detail results with successful tests attached to the summary.
+     * @return int count of occurences
+     */
+    public function successfulcount(): int {
+        return $this->stateoccurencecount(1);
+    }
+
+    /**
+     * Returns the number of detail results with an unknown result - mostly due to compile errors - attached to the summary.
+     * @return int count of occurences
+     */
+    public function unknowncount(): int {
+        return $this->stateoccurencecount(0);
+    }
+
+}
diff --git a/test/privacy/apicompliance.php b/test/privacy/apicompliance.php
index 7a5761e87fa297e4ff842b50a9403794834dc1e8..121ad630786845a1575b9cb058284d6dd6279770 100644
--- a/test/privacy/apicompliance.php
+++ b/test/privacy/apicompliance.php
@@ -62,7 +62,7 @@ foreach ($list->good as $component) {
             foreach ($collection->get_collection() as $item) {
                 if ($item instanceof \core_privacy\local\metadata\types\user_preference) {
                     $userprefdescribed = true;
-                    echo "     ".$item->assignsubmission_dta_get_name()." : ".get_string($item->get_summary(), $component) . "\n";
+                    echo "     ".$item->get_name()." : ".get_string($item->get_summary(), $component) . "\n";
                 }
             }
             if (!$userprefdescribed) {