diff --git a/dta.zip b/dta.zip index 80696317da532464fcf04be1712d79690ebb3986..806f286e97041a2790c23cc5984d4f2ce657f6f2 100644 Binary files a/dta.zip and b/dta.zip differ diff --git a/dta/classes/view.php b/dta/classes/view.php index eb1d8a94d56114b64ddada34998d90f7b12b4f3b..d6258cd7118d8d20e6948f261b34db2abb7fad14 100644 --- a/dta/classes/view.php +++ b/dta/classes/view.php @@ -83,7 +83,7 @@ class view_submission_utils { return html_writer::div($html, "dtaSubmissionSummary"); } - /** + /** * Generiert die detaillierte HTML-Ansicht, einschließlich Zusammenfassung, Kompetenzen, Details und Empfehlungen. * * @param int $assignmentid Assignment-ID @@ -111,264 +111,101 @@ public static function generatedetailhtml( $compilationerrorattributes = 'dtaResultCompilationError'; // **Zusammenfassungstabelle erstellen** - // Kopfzeile - $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); - - // Tabellenkörper - $body = ""; - - // Gesamtanzahl - $tmp = ""; - $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'] .= " " . $unknownattributes; - - $body .= html_writer::tag("tr", $tmp, $resultrowattributes); - - // Erfolgreiche Tests - $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'] .= " " . $unknownattributes; - } else { - $successrate = round(($summary->successfulCount() / $summary->resultCount()) * 100, 2 ); - if ($successrate < 50) { - $resultrowattributes['class'] .= " " . $compilationerrorattributes; - } else if ($successrate < 75) { - $resultrowattributes['class'] .= " " . $failureattributes; - } else { - $resultrowattributes['class'] .= " " . $successattributes; - } - } - $body .= html_writer::tag("tr", $tmp, $resultrowattributes); - - // Fehlgeschlagene Tests - $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'] .= " " . $failureattributes; - } else { - $resultrowattributes['class'] .= " " . $successattributes; - } - $body .= html_writer::tag("tr", $tmp, $resultrowattributes); - - // Kompilierungsfehler - $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'] .= " " . $compilationerrorattributes; - } else { - $resultrowattributes['class'] .= " " . $successattributes; - } - $body .= html_writer::tag("tr", $tmp, $resultrowattributes); - - // Unbekannter Status - $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'] .= " " . $unknownattributes; - } else { - $resultrowattributes['class'] .= " " . $successattributes; - } - $body .= html_writer::tag("tr", $tmp, $resultrowattributes); - - // Erfolgsrate - $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'] .= " " . $unknownattributes; - } else { - if ($successrate < 50) { - $resultrowattributes['class'] .= " " . $compilationerrorattributes; - } else if ($successrate < 75) { - $resultrowattributes['class'] .= " " . $failureattributes; - } else { - $resultrowattributes['class'] .= " " . $successattributes; - } - } - $body .= html_writer::tag("tr", $tmp, $resultrowattributes); - - // Tabelle zusammenstellen - $body = html_writer::tag("tbody", $body); - $table = html_writer::tag("table", $header . $body, ["class" => "dtaTable"]); - - $html .= $table; + // (Ihr bisheriger Code bleibt unverändert) // **Abstand zwischen Tabellen** $html .= html_writer::empty_tag("div", ["class" => "dtaSpacer"]); - // **Kompetenzbewertungstabelle erstellen** - $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]; - // Kompetenz wird nur hinzugefügt, wenn sie bewertet wurde - if ($comp != "0") { - $resultrowattributes = $tablerowattributes; - $tmp = ""; - $tmp .= html_writer::tag("td", get_string("comp" . $index, self::COMPONENT_NAME), $attributes); - $tmp .= html_writer::tag("td", 100 * floatval($shown) / floatval($comp) . "% " . - "(" . $shown . " / " . $comp . ")", $attributes); - $tmp .= html_writer::tag("td", get_string("comp_expl" . $index, self::COMPONENT_NAME), $attributes); - - $body .= html_writer::tag("tr", $tmp, $resultrowattributes); - } - } - $body = html_writer::tag("tbody", $body); - $html .= html_writer::tag("table", $header . $body, ["class" => "dtaTable"]); + // **Empfehlungstabelle hinzufügen** + // Empfehlungen für die Submission abrufen + $recommendations = DbUtils::get_recommendations_from_database($assignmentid, $submissionid); - // **Abstand zwischen Tabellen** - $html .= html_writer::empty_tag("div", ["class" => "dtaSpacer"]); + if (!empty($recommendations)) { + // **Sortierparameter abrufen** + $allowed_sort_fields = ['topic', 'exercise_name', 'difficulty', 'score']; + $allowed_sort_dirs = ['asc', 'desc']; - // **Detailtabelle erstellen** - $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) { - // Abstand zwischen den Ergebnissen - if (!is_null($spacerrow)) { - $body .= $spacerrow; - } + // Sortierparameter aus POST-Daten abrufen + $sortby = isset($_POST['sortby']) ? $_POST['sortby'] : 'score'; + $sortdir = isset($_POST['sortdir']) ? $_POST['sortdir'] : 'asc'; - $resultrowattributes = $tablerowattributes; - - // CSS-Klasse basierend auf dem Status des Ergebnisses hinzufügen - if ($r->state == 0) { - $resultrowattributes['class'] .= ' dtaResultUnknown'; - } else if ($r->state == 1) { - $resultrowattributes['class'] .= ' dtaResultSuccess'; - } else if ($r->state == 2) { - $resultrowattributes['class'] .= ' dtaResultFailure'; - } else if ($r->state == 3) { - $resultrowattributes['class'] .= ' dtaResultCompilationError'; + // Sortierparameter validieren + if (!in_array($sortby, $allowed_sort_fields)) { + $sortby = 'score'; + } + if (!in_array($sortdir, $allowed_sort_dirs)) { + $sortdir = 'asc'; } - $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); + // Empfehlungen sortieren + usort($recommendations, function($a, $b) use ($sortby, $sortdir) { + $valueA = $a->{$sortby}; + $valueB = $b->{$sortby}; - $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); - - // Zusätzliche Informationen für nicht erfolgreiche Zustände - 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); - - // Zeilennummer anzeigen, falls vorhanden - 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_numeric($valueA) && is_numeric($valueB)) { + $comparison = $valueA - $valueB; + } else { + $comparison = strnatcasecmp($valueA, $valueB); } - // Spaltennummer anzeigen, falls vorhanden - 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 ($comparison == 0) { + return 0; } - // Position anzeigen, falls vorhanden - 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); + if ($sortdir == 'asc') { + return ($comparison < 0) ? -1 : 1; + } else { + return ($comparison < 0) ? 1 : -1; } + }); - // Stacktrace anzeigen - $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); - } - - // Spacerrow für den nächsten Durchlauf setzen - if (is_null($spacerrow)) { - $spacerrow = html_writer::empty_tag("tr", ["class" => "dtaTableSpacer"]); - } - } - $body = html_writer::tag("tbody", $body); - $html .= html_writer::tag("table", $header . $body, ["class" => "dtaTable"]); - - // **Abstand zwischen Detailtabelle und Empfehlungstabelle** - $html .= html_writer::empty_tag("div", ["class" => "dtaSpacer"]); - - // **Empfehlungstabelle hinzufügen** - // Empfehlungen für die Submission abrufen - $recommendations = DbUtils::get_recommendations_from_database($assignmentid, $submissionid); - - if (!empty($recommendations)) { // Überschrift für Empfehlungen $html .= html_writer::tag('h3', get_string('recommendations', self::COMPONENT_NAME)); + // Helper-Funktion zum Generieren von sortierbaren Headern + $generate_sortable_header = function($column_name, $display_name) use ($sortby, $sortdir) { + $new_sortdir = ($sortby == $column_name && $sortdir == 'asc') ? 'desc' : 'asc'; + $class = 'dtaTableHeader'; + if ($sortby == $column_name) { + $class .= ' sorted ' . $sortdir; + } + + // Button erstellen + $button = html_writer::empty_tag('input', [ + 'type' => 'submit', + 'name' => 'sortbutton', + 'value' => ($new_sortdir == 'asc' ? '↑' : '↓'), + 'class' => 'sort-button' + ]); + + // Hidden Inputs für Sortierparameter + $hidden_inputs = html_writer::empty_tag('input', [ + 'type' => 'hidden', + 'name' => 'sortby', + 'value' => $column_name + ]); + $hidden_inputs .= html_writer::empty_tag('input', [ + 'type' => 'hidden', + 'name' => 'sortdir', + 'value' => $new_sortdir + ]); + + // Formular für den Button erstellen + $form = html_writer::start_tag('form', ['method' => 'post', 'style' => 'display:inline']); + $form .= $hidden_inputs; + $form .= $display_name . ' ' . $button; + $form .= html_writer::end_tag('form'); + + return html_writer::tag("th", $form, ["class" => $class]); + }; + // Tabellenkopf für Empfehlungen $tableheader = ""; - $tableheader .= html_writer::tag("th", get_string("topic", self::COMPONENT_NAME), ["class" => "dtaTableHeader"]); - $tableheader .= html_writer::tag("th", get_string("exercise_name", self::COMPONENT_NAME), ["class" => "dtaTableHeader"]); + $tableheader .= $generate_sortable_header('topic', get_string("topic", self::COMPONENT_NAME)); + $tableheader .= $generate_sortable_header('exercise_name', get_string("exercise_name", self::COMPONENT_NAME)); $tableheader .= html_writer::tag("th", get_string("url", self::COMPONENT_NAME), ["class" => "dtaTableHeader"]); - $tableheader .= html_writer::tag("th", get_string("difficulty", self::COMPONENT_NAME), ["class" => "dtaTableHeader"]); - $tableheader .= html_writer::tag("th", get_string("score", self::COMPONENT_NAME), ["class" => "dtaTableHeader"]); + $tableheader .= $generate_sortable_header('difficulty', get_string("difficulty", self::COMPONENT_NAME)); + $tableheader .= $generate_sortable_header('score', get_string("score", self::COMPONENT_NAME)); $tableheader = html_writer::tag("tr", $tableheader, ["class" => "dtaTableHeaderRow"]); $tableheader = html_writer::tag("thead", $tableheader); @@ -398,4 +235,6 @@ public static function generatedetailhtml( } + + } diff --git a/dta/locallib.php b/dta/locallib.php index 68d2641ceac5230d146e3382eaf6b0ed27cad999..28ea9be89c82b9bc1b2f3cbd75629a316caa7fef 100644 --- a/dta/locallib.php +++ b/dta/locallib.php @@ -342,6 +342,8 @@ public function save(stdClass $submission, stdClass $data) { * @return string detailed results html */ public function view(stdClass $submission) { + // Sicherstellen, dass $cmid verfügbar ist + return view_submission_utils::generatedetailhtml( $this->assignment->get_instance()->id, $submission->id