diff --git a/dta/classes/db_utils.php b/dta/classes/db_utils.php index a37ff6095f1e4676a2f62efbe528db56cdef3144..5599d7f02a26406e82e39f57424ad05dcb87a956 100644 --- a/dta/classes/db_utils.php +++ b/dta/classes/db_utils.php @@ -1,5 +1,4 @@ <?php -namespace assignsubmission_dta; // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify @@ -15,92 +14,119 @@ namespace assignsubmission_dta; // 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 - */ +namespace assignsubmission_dta; use assignsubmission_dta\dta_backend_utils; use assignsubmission_dta\dta_view_submission_utils; use assignsubmission_dta\models\dta_result; use assignsubmission_dta\models\dta_result_summary; use assignsubmission_dta\models\dta_recommendation; + +/** + * Class dta_db_utils + * + * Persistence layer utility class for storing and retrieving + * DTA plugin data (results, summaries, recommendations). + * + * @package assignsubmission_dta + * @copyright 2023 Gero Lueckemeyer + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ class dta_db_utils { /** * Summary database table name. */ - private const ASSIGNSUBMISSION_DTA_TABLE_SUMMARY = "assignsubmission_dta_summary"; + private const ASSIGNSUBMISSION_DTA_TABLE_SUMMARY = 'assignsubmission_dta_summary'; + /** * Result database table name. */ - private const ASSIGNSUBMISSION_DTA_TABLE_RESULT = "assignsubmission_dta_result"; + private const ASSIGNSUBMISSION_DTA_TABLE_RESULT = 'assignsubmission_dta_result'; - private const ASSIGNSUBMISSION_DTA_TABLE_RECOMMENDATIONS = "assignsubmission_dta_recommendations"; + /** + * Recommendations database table name. + */ + private const ASSIGNSUBMISSION_DTA_TABLE_RECOMMENDATIONS = 'assignsubmission_dta_recommendations'; - public static function assignsubmission_dta_get_recommendations_from_database(int $assignmentid, int $submissionid): array { - global $DB,$USER; + /** + * Returns an array of recommendations from the database. + * + * @param int $assignmentid The assignment ID. + * @param int $submissionid The submission ID. + * @return array An array of recommendation records. + */ + public static function assignsubmission_dta_get_recommendations_from_database( + int $assignmentid, + int $submissionid + ): array { + global $DB, $USER; $userid = $USER->id; - - // Schritt 1: Alle Empfehlungen abrufen - $records = $DB->get_records(self::ASSIGNSUBMISSION_DTA_TABLE_RECOMMENDATIONS, [ - 'assignment_id' => $assignmentid, - 'submission_id' => $submissionid, - ]); - - // Schritt 2: Modul-ID für 'assign' abrufen + + // Step 1: Retrieve all recommendations. + $records = $DB->get_records( + self::ASSIGNSUBMISSION_DTA_TABLE_RECOMMENDATIONS, + [ + 'assignment_id' => $assignmentid, + 'submission_id' => $submissionid, + ] + ); + + // Step 2: Retrieve module ID for 'assign'. $module = $DB->get_record('modules', ['name' => 'assign'], 'id'); if (!$module) { - // Fehlerbehandlung, falls das Modul nicht gefunden wird + // Handle error case if the module is not found. return $records; } $moduleid = $module->id; - print_r($records); - // Schritt 3: Überprüfe jeden Datensatz + + // Step 3: Check each record. foreach ($records as $key => $record) { - // Hol den Namen der Übung aus dem Datensatz + // Get the name of the exercise from the record. $exercisename = $record->exercise_name; - - // Suche das Assignment mit diesem Namen + + // Find the assignment with this name. $assign = $DB->get_record('assign', ['name' => $exercisename], 'id'); - if ($assign) { - // Hole die Kursmodul-ID (coursemoduleid) für dieses Assignment - $cm = $DB->get_record('course_modules', [ - 'module' => $moduleid, - 'instance' => $assign->id - ], 'id'); - + // Get the course module ID for this assignment. + $cm = $DB->get_record( + 'course_modules', + [ + 'module' => $moduleid, + 'instance' => $assign->id, + ], + 'id' + ); + if ($cm) { - // Überprüfe den Abschlussstatus für dieses Kursmodul und den Benutzer - $completion = $DB->get_record('course_modules_completion', [ - 'coursemoduleid' => $cm->id, - 'userid' => $userid - ], 'completionstate'); - - // Wenn der Abschlussstatus 1 ist, entferne den Datensatz aus $records - if ($completion && $completion->completionstate == 1) { - unset($records[$key]); + // Check the completion status for this course module and user. + $completion = $DB->get_record( + 'course_modules_completion', + [ + 'coursemoduleid' => $cm->id, + 'userid' => $userid, + ], + 'completionstate' + ); + // If the completion state is 1, remove the record from $records. + if ($completion && (int)$completion->completionstate === 1) { + unset($records[$key]); } } } } - - // Rückgabe der gefilterten Datensätze + + // Return the filtered records. return $records; } - /** - * gets summary with all corresponding result entries + * Gets a 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 + * @param int $assignmentid Assignment ID to search for. + * @param int $submissionid Submission ID to search for. + * @return dta_result_summary Summary representing the submission. */ public static function assignsubmission_dta_get_result_summary_from_database( int $assignmentid, @@ -109,15 +135,21 @@ class dta_db_utils { global $DB; // Fetch data from database. - $summaryrecord = $DB->get_record(self::ASSIGNSUBMISSION_DTA_TABLE_SUMMARY, [ - "assignment_id" => $assignmentid, - "submission_id" => $submissionid, - ]); + $summaryrecord = $DB->get_record( + self::ASSIGNSUBMISSION_DTA_TABLE_SUMMARY, + [ + 'assignment_id' => $assignmentid, + 'submission_id' => $submissionid, + ] + ); - $resultsarray = $DB->get_records(self::ASSIGNSUBMISSION_DTA_TABLE_RESULT, [ - "assignment_id" => $assignmentid, - "submission_id" => $submissionid, - ]); + $resultsarray = $DB->get_records( + self::ASSIGNSUBMISSION_DTA_TABLE_RESULT, + [ + 'assignment_id' => $assignmentid, + 'submission_id' => $submissionid, + ] + ); // Create a summary instance. $summary = new dta_result_summary(); @@ -146,55 +178,69 @@ class dta_db_utils { return $summary; } + + /** + * Stores an array of recommendations in the database. + * + * @param int $assignmentid The assignment ID. + * @param int $submissionid The submission ID. + * @param array $recommendations An array of dta_recommendation objects. + */ public static function assignsubmission_dta_store_recommendations_to_database( int $assignmentid, int $submissionid, array $recommendations ): void { global $DB; - error_log(print_r($recommendations, true)); - + + // Debug output (you can remove or adapt this if unneeded). + debugging('Recommendations array: ' . json_encode($recommendations)); + // If recommendations already exist, delete old values beforehand. - $existingrecords = $DB->get_records('assignsubmission_dta_recommendations', [ - 'assignment_id' => $assignmentid, - 'submission_id' => $submissionid, - ]); - - if ($existingrecords) { - $DB->delete_records('assignsubmission_dta_recommendations', [ + $existingrecords = $DB->get_records( + 'assignsubmission_dta_recommendations', + [ 'assignment_id' => $assignmentid, 'submission_id' => $submissionid, - ]); + ] + ); + + if ($existingrecords) { + $DB->delete_records( + 'assignsubmission_dta_recommendations', + [ + 'assignment_id' => $assignmentid, + 'submission_id' => $submissionid, + ] + ); } - + // Create new recommendation entries. foreach ($recommendations as $recommendation) { - // Ensure $recommendation is an instance of DtaRecommendation + // Check if $recommendation is an instance of dta_recommendation. if ($recommendation instanceof dta_recommendation) { - // Add assignment and submission IDs to the recommendation object + // Add assignment and submission IDs to the recommendation object. $recommendation->assignment_id = $assignmentid; $recommendation->submission_id = $submissionid; - - error_log("Insert record"); - error_log(print_r($recommendation, true)); - - // Insert the recommendation into the database + + debugging('Inserting new recommendation record: ' . json_encode($recommendation)); + + // Insert the recommendation into the database. $DB->insert_record('assignsubmission_dta_recommendations', $recommendation); } else { - // Handle the case where $recommendation is not a DtaRecommendation instance - error_log("Invalid recommendation object"); + // Handle the case where $recommendation is not a dta_recommendation instance. + debugging('Invalid recommendation object encountered.'); } } } - /** - * save given result summary and single results to database - * under given assignment and submission id + * Saves the given result summary and single results to the database + * under the specified assignment and submission ID. * - * @param int $assignmentid assigment this is submission is linked to - * @param int $submissionid submission of this result - * @param dta_result_summary $summary instance to persist + * @param int $assignmentid Assignment this submission is linked to. + * @param int $submissionid Submission ID for these results. + * @param dta_result_summary $summary Summary instance to persist. */ public static function assignsubmission_dta_store_result_summary_to_database( int $assignmentid, @@ -211,8 +257,8 @@ class dta_db_utils { $summaryrecord->global_stacktrace = $summary->globalstacktrace; $summaryrecord->successful_competencies = $summary->successfultestcompetencies; $summaryrecord->tested_competencies = $summary->overalltestcompetencies; - - // Prepare results to persist to array. + + // Prepare results to persist. $resultrecords = []; foreach ($summary->results as $r) { $record = new dta_result(); @@ -232,21 +278,30 @@ class dta_db_utils { } // If results already exist, delete old values beforehand. - $submission = $DB->get_record(self::ASSIGNSUBMISSION_DTA_TABLE_SUMMARY, [ - 'assignment_id' => $assignmentid, - 'submission_id' => $submissionid, - ]); - - if ($submission) { - $DB->delete_records(self::ASSIGNSUBMISSION_DTA_TABLE_RESULT, [ + $submission = $DB->get_record( + self::ASSIGNSUBMISSION_DTA_TABLE_SUMMARY, + [ 'assignment_id' => $assignmentid, 'submission_id' => $submissionid, - ]); + ] + ); - $DB->delete_records(self::ASSIGNSUBMISSION_DTA_TABLE_SUMMARY, [ - 'assignment_id' => $assignmentid, - 'submission_id' => $submissionid, - ]); + if ($submission) { + $DB->delete_records( + self::ASSIGNSUBMISSION_DTA_TABLE_RESULT, + [ + 'assignment_id' => $assignmentid, + 'submission_id' => $submissionid, + ] + ); + + $DB->delete_records( + self::ASSIGNSUBMISSION_DTA_TABLE_SUMMARY, + [ + 'assignment_id' => $assignmentid, + 'submission_id' => $submissionid, + ] + ); } // Create summary and single result entries. @@ -257,7 +312,7 @@ class dta_db_utils { } /** - * cleans up database if plugin is uninstalled + * Cleans up database if plugin is uninstalled. */ public static function assignsubmission_dta_uninstall_plugin_cleaup(): void { global $DB; @@ -265,7 +320,5 @@ class dta_db_utils { $DB->delete_records(self::ASSIGNSUBMISSION_DTA_TABLE_RESULT, null); $DB->delete_records(self::ASSIGNSUBMISSION_DTA_TABLE_SUMMARY, null); $DB->delete_records(self::ASSIGNSUBMISSION_DTA_TABLE_RECOMMENDATIONS, null); - } - } diff --git a/dta/classes/dta_backend_utils.php b/dta/classes/dta_backend_utils.php index 86f36c276e898b799bc1a974b5c66e76c12e2ed9..369b7331381baa93ca540bcceab2bbb5ff16a4ec 100644 --- a/dta/classes/dta_backend_utils.php +++ b/dta/classes/dta_backend_utils.php @@ -1,6 +1,5 @@ <?php - -// This file is part of Moodle - http://moodle.org/ +// 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 @@ -16,40 +15,44 @@ // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** - * This file contains the backend webservice contact functionality for the DTA plugin + * 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 + * @package assignsubmission_dta + * @copyright 2023 Your Name + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ - +namespace assignsubmission_dta; /** - * Backend webservice contact utility class + * Backend webservice contact utility class. * - * @package assignsubmission_dta - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @package assignsubmission_dta + * @copyright 2023 Your Name + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -namespace assignsubmission_dta; - -defined('MOODLE_INTERNAL') || die(); - class dta_backend_utils { /** * Component name for the plugin. */ - const ASSIGNSUBMISSION_DTA_COMPONENT_NAME = 'assignsubmission_dta'; + public const ASSIGNSUBMISSION_DTA_COMPONENT_NAME = 'assignsubmission_dta'; /** * Returns the base URL of the backend webservice as configured in the administration settings. - * @return string Backend host base URL + * + * @return string Backend host base URL. */ private static function assignsubmission_dta_get_backend_baseurl(): string { - $backendaddress = get_config(self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME, 'backendHost'); + $backendaddress = get_config( + self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME, + 'backendHost' + ); if (empty($backendaddress)) { - \core\notification::error(get_string('backendHost_not_set', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME)); + \core\notification::error( + get_string('backendHost_not_set', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME) + ); } return $backendaddress; @@ -58,9 +61,9 @@ class dta_backend_utils { /** * Sends the configuration text file uploaded by the teacher to the backend. * - * @param \assign $assignment Assignment this test-config belongs to - * @param \stored_file $file Uploaded test-config - * @return bool True if no error occurred + * @param \assign $assignment Assignment this test-config belongs to. + * @param \stored_file $file Uploaded test-config. + * @return bool True if no error occurred. */ public static function assignsubmission_dta_send_testconfig_to_backend($assignment, $file): bool { $backendaddress = self::assignsubmission_dta_get_backend_baseurl(); @@ -88,12 +91,16 @@ class dta_backend_utils { /** * Sends submission config or archive to backend to be tested. * - * @param \assign $assignment Assignment for the submission - * @param int $submissionid Submission ID of the current file - * @param \stored_file $file Submission config file or archive with submission - * @return string|null JSON string with test results or null on error + * @param \assign $assignment Assignment for the submission. + * @param int $submissionid Submission ID of the current file. + * @param \stored_file $file Submission config file or archive with submission. + * @return string|null JSON string with test results or null on error. */ - public static function assignsubmission_dta_send_submission_to_backend($assignment, $submissionid, $file): ?string { + public static function assignsubmission_dta_send_submission_to_backend( + $assignment, + int $submissionid, + $file + ): ?string { $backendaddress = self::assignsubmission_dta_get_backend_baseurl(); if (empty($backendaddress)) { return null; @@ -113,12 +120,12 @@ class dta_backend_utils { /** * 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|null Received body on success or null on error + * @param string $url Full URL to request. + * @param array $params Parameters for HTTP request. + * @return string|null Received body on success or null on error. */ - private static function assignsubmission_dta_post($url, $params): ?string { + private static function assignsubmission_dta_post(string $url, array $params): ?string { if (!isset($url) || !isset($params)) { return null; } @@ -128,23 +135,32 @@ class dta_backend_utils { $curl = new \curl(); $response = $curl->post($url, $params, $options); - // Check state of request, if response code is a 2xx return the answer. + // Check state of request, if response code is 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(self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME . ': Post file to server was not successful: http_code=' . $info['http_code']); + // Something went wrong, return null and display an error message. + $msg = self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME + . ': Post file to server was not successful. HTTP code=' + . $info['http_code']; + debugging($msg); if ($info['http_code'] >= 400 && $info['http_code'] < 500) { - \core\notification::error(get_string('http_client_error_msg', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME)); + \core\notification::error( + get_string('http_client_error_msg', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME) + ); return null; } else if ($info['http_code'] >= 500 && $info['http_code'] < 600) { - \core\notification::error(get_string('http_server_error_msg', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME)); + \core\notification::error( + get_string('http_server_error_msg', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME) + ); return null; } else { - \core\notification::error(get_string('http_unknown_error_msg', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME) . $info['http_code'] . $response); + $unknownmsg = get_string('http_unknown_error_msg', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME) + . $info['http_code'] . ' ' . $response; + \core\notification::error($unknownmsg); return null; } } diff --git a/dta/classes/dta_view_submission_utils.php b/dta/classes/dta_view_submission_utils.php index d9a518b45dce038b64bc81cea40954a21e6db4c1..a85b5519b0563263e68f9ee10021841ac46563d6 100644 --- a/dta/classes/dta_view_submission_utils.php +++ b/dta/classes/dta_view_submission_utils.php @@ -1,6 +1,5 @@ <?php -namespace assignsubmission_dta; -// This file is part of Moodle - http://moodle.org/ +// 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 @@ -15,456 +14,590 @@ namespace assignsubmission_dta; // 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 - */ +namespace assignsubmission_dta; + use assignsubmission_dta\dta_db_utils; use assignsubmission_dta\dta_backend_utils; use assignsubmission_dta\models\dta_result; use assignsubmission_dta\models\dta_result_summary; use assignsubmission_dta\models\dta_recommendation; +/** + * Utility class for DTA submission plugin result display. + * + * @package assignsubmission_dta + * @copyright 2023 Your Name <you@example.com> + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ class dta_view_submission_utils { /** * Broadly used in logic, parametrized for easier change. */ - const ASSIGNSUBMISSION_DTA_COMPONENT_NAME = "assignsubmission_dta"; + public const ASSIGNSUBMISSION_DTA_COMPONENT_NAME = 'assignsubmission_dta'; /** - * generates a short summary html + * Generates a short summary HTML. * - * @param int $assignmentid assignment - * @param int $submissionid submission to create a report for - * @return string html + * @param int $assignmentid The assignment ID. + * @param int $submissionid The submission ID to create a report for. + * @return string HTML summary. */ public static function assignsubmission_dta_generate_summary_html( int $assignmentid, int $submissionid ): string { - // Fetch data. - $summary = dta_db_utils::assignsubmission_dta_get_result_summary_from_database($assignmentid, $submissionid); - $html = ""; + $summary = dta_db_utils::assignsubmission_dta_get_result_summary_from_database( + $assignmentid, + $submissionid + ); + $html = ''; // Calculate success rate, if no unknown result states or compilation errors. - $successrate = "?"; - if ($summary->unknown_count() == 0 && $summary->compilation_error_count() == 0) { - $successrate = round(($summary->successful_count() / $summary->result_count()) * 100, 2 ); + $successrate = '?'; + if ($summary->unknown_count() === 0 && $summary->compilation_error_count() === 0) { + $successrate = round(($summary->successful_count() / $summary->result_count()) * 100, 2); } - // Generate html. - $html .= $summary->successful_count() . "/"; - $html .= ($summary->compilation_error_count() == 0 && $summary->unknown_count() == 0) - ? $summary->result_Count() . " (" . $successrate . "%)" - : "?"; - $html .= get_string("tests_successful", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME) . "<br />"; + // Generate HTML. + $html .= $summary->successful_count() . '/'; + if ($summary->compilation_error_count() === 0 && $summary->unknown_count() === 0) { + $html .= $summary->result_count() . ' (' . $successrate . '%)'; + } else { + $html .= '?'; + } + $html .= get_string('tests_successful', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME) . '<br />'; if ($summary->compilation_error_count() > 0) { - $html .= $summary->compilation_error_count() . get_string("compilation_errors", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME) . "<br />"; + $html .= $summary->compilation_error_count() + . get_string('compilation_errors', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME) + . '<br />'; } if ($summary->unknown_count() > 0) { - $html .= $summary->unknown_count() . get_string("unknown_state", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME) . "<br />"; + $html .= $summary->unknown_count() + . get_string('unknown_state', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME) + . '<br />'; } - $showncompetencies = explode(";", $summary->successfultestcompetencies); - $overallcompetencies = explode(";", $summary->overalltestcompetencies); + $showncompetencies = explode(';', $summary->successfultestcompetencies); + $overallcompetencies = explode(';', $summary->overalltestcompetencies); - $tmp = ""; + $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::ASSIGNSUBMISSION_DTA_COMPONENT_NAME) . - " " . 100 * floatval($shown) / floatval($comp) . "% " . "<br />"; + // If the competency was actually assessed, add a summary entry. + if ($shown !== '0') { + $tmp .= get_string('comp' . $index, self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME) + . ' ' . (100 * floatval($shown) / floatval($comp)) . '% ' . '<br />'; } } - $html .= get_string("success_competencies", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME) . "<br />" . $tmp . "<br />"; + $html .= get_string('success_competencies', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME) + . '<br />' . $tmp . '<br />'; - return \html_writer::div($html, "dtaSubmissionSummary"); + 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 assignsubmission_dta_generate_detail_html( - int $assignmentid, - int $submissionid -): string { - - // Fetch data. - $summary = dta_db_utils::assignsubmission_dta_get_result_summary_from_database($assignmentid, $submissionid); - $recommendations = dta_db_utils::assignsubmission_dta_get_recommendations_from_database($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'; - $attributes = ["class" => "dtaTableData"]; - - // *************** - // SUMMARY TABLE - // *************** - - $tmp = ""; - $tmp .= \html_writer::tag("th", get_string("summary", self::ASSIGNSUBMISSION_DTA_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 = ""; - - // Total items. - $tmp = ""; - $tmp .= \html_writer::tag("td", get_string("total_items", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), $attributes); - $tmp .= \html_writer::tag("td", $summary->result_count(), $attributes); - $resultrowattributes = $tablerowattributes; - $resultrowattributes['class'] .= " " . $unknownattributes; - $body .= \html_writer::tag("tr", $tmp, $resultrowattributes); - - // Tests successful. - $tmp = ""; - $tmp .= \html_writer::tag("td", get_string("tests_successful", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), $attributes); - $tmp .= \html_writer::tag("td", $summary->successful_count(), $attributes); - $resultrowattributes = $tablerowattributes; - $successrate = "?"; - if ($summary->unknown_count() > 0 || $summary->compilation_error_count() > 0) { - $resultrowattributes['class'] .= " " . $unknownattributes; - } else { - $successrate = round(($summary->successful_count() / $summary->result_count()) * 100, 2); - if ($successrate < 50) { - $resultrowattributes['class'] .= " " . $compilationerrorattributes; - } else if ($successrate < 75) { - $resultrowattributes['class'] .= " " . $failureattributes; + /** + * Generates detailed view HTML. + * + * @param int $assignmentid The assignment ID. + * @param int $submissionid The submission to create a report for. + * @return string HTML detail view. + */ + public static function assignsubmission_dta_generate_detail_html( + int $assignmentid, + int $submissionid + ): string { + // Fetch data. + $summary = dta_db_utils::assignsubmission_dta_get_result_summary_from_database( + $assignmentid, + $submissionid + ); + $recommendations = dta_db_utils::assignsubmission_dta_get_recommendations_from_database( + $assignmentid, + $submissionid + ); + + $html = ''; + + // *** Summary Table *** + $tableheaderrowattributes = ['class' => 'dtaTableHeaderRow']; + $tablerowattributes = ['class' => 'dtaTableRow']; + $resultrowattributes = $tablerowattributes; + $unknownattributes = 'dtaResultUnknown'; + $successattributes = 'dtaResultSuccess'; + $failureattributes = 'dtaResultFailure'; + $compilationerrorattributes = 'dtaResultCompilationError'; + $attributes = ['class' => 'dtaTableData']; + + // Building summary table. + $tmp = \html_writer::tag( + 'th', + get_string('summary', self::ASSIGNSUBMISSION_DTA_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 = ''; + + // Total items. + $tmp = ''; + $tmp .= \html_writer::tag( + 'td', + get_string('total_items', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), + $attributes + ); + $tmp .= \html_writer::tag('td', $summary->result_count(), $attributes); + $resultrowattributes = $tablerowattributes; + $resultrowattributes['class'] .= ' ' . $unknownattributes; + $body .= \html_writer::tag('tr', $tmp, $resultrowattributes); + + // Tests successful. + $tmp = ''; + $tmp .= \html_writer::tag( + 'td', + get_string('tests_successful', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), + $attributes + ); + $tmp .= \html_writer::tag('td', $summary->successful_count(), $attributes); + $resultrowattributes = $tablerowattributes; + $successrate = '?'; + if ($summary->unknown_count() > 0 || $summary->compilation_error_count() > 0) { + $resultrowattributes['class'] .= ' ' . $unknownattributes; } else { - $resultrowattributes['class'] .= " " . $successattributes; + $successrate = round( + ($summary->successful_count() / $summary->result_count()) * 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); - - // Failures. - $tmp = ""; - $tmp .= \html_writer::tag("td", get_string("failures", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), $attributes); - $tmp .= \html_writer::tag("td", $summary->failed_count(), $attributes); - $resultrowattributes = $tablerowattributes; - if ($summary->failed_count() > 0) { - $resultrowattributes['class'] .= " " . $failureattributes; - } else { - $resultrowattributes['class'] .= " " . $successattributes; - } - $body .= \html_writer::tag("tr", $tmp, $resultrowattributes); - - // Compilation errors. - $tmp = ""; - $tmp .= \html_writer::tag("td", get_string("compilation_errors", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), $attributes); - $tmp .= \html_writer::tag("td", $summary->compilation_error_count(), $attributes); - $resultrowattributes = $tablerowattributes; - if ($summary->compilation_error_count() > 0) { - $resultrowattributes['class'] .= " " . $compilationerrorattributes; - } else { - $resultrowattributes['class'] .= " " . $successattributes; - } - $body .= \html_writer::tag("tr", $tmp, $resultrowattributes); - - // Unknown state. - $tmp = ""; - $tmp .= \html_writer::tag("td", get_string("unknown_state", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), $attributes); - $tmp .= \html_writer::tag("td", $summary->unknown_count(), $attributes); - $resultrowattributes = $tablerowattributes; - if ($summary->unknown_count() > 0) { - $resultrowattributes['class'] .= " " . $unknownattributes; - } else { - $resultrowattributes['class'] .= " " . $successattributes; - } - $body .= \html_writer::tag("tr", $tmp, $resultrowattributes); - - // Success rate. - $tmp = ""; - $tmp .= \html_writer::tag("td", \html_writer::tag("b", get_string("success_rate", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME)), $attributes); - $tmp .= \html_writer::tag( - "td", - \html_writer::tag("b", $summary->successful_count() . "/" . - (($summary->compilation_error_count() == 0 && $summary->unknown_count() == 0) - ? $summary->result_count() . " (" . $successrate . "%)" - : "?")), - $attributes - ); - - $resultrowattributes = $tablerowattributes; - if ($summary->unknown_count() > 0 || $summary->compilation_error_count() > 0) { - $resultrowattributes['class'] .= " " . $unknownattributes; - } else { - if ($successrate < 50) { - $resultrowattributes['class'] .= " " . $compilationerrorattributes; - } else if ($successrate < 75) { - $resultrowattributes['class'] .= " " . $failureattributes; + $body .= \html_writer::tag('tr', $tmp, $resultrowattributes); + + // Failures. + $tmp = ''; + $tmp .= \html_writer::tag( + 'td', + get_string('failures', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), + $attributes + ); + $tmp .= \html_writer::tag('td', $summary->failed_count(), $attributes); + $resultrowattributes = $tablerowattributes; + if ($summary->failed_count() > 0) { + $resultrowattributes['class'] .= ' ' . $failureattributes; } else { - $resultrowattributes['class'] .= " " . $successattributes; + $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"]); + $body .= \html_writer::tag('tr', $tmp, $resultrowattributes); + + // Compilation errors. + $tmp = ''; + $tmp .= \html_writer::tag( + 'td', + get_string('compilation_errors', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), + $attributes + ); + $tmp .= \html_writer::tag('td', $summary->compilation_error_count(), $attributes); + $resultrowattributes = $tablerowattributes; + if ($summary->compilation_error_count() > 0) { + $resultrowattributes['class'] .= ' ' . $compilationerrorattributes; + } else { + $resultrowattributes['class'] .= ' ' . $successattributes; + } + $body .= \html_writer::tag('tr', $tmp, $resultrowattributes); + + // Unknown state. + $tmp = ''; + $tmp .= \html_writer::tag( + 'td', + get_string('unknown_state', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), + $attributes + ); + $tmp .= \html_writer::tag('td', $summary->unknown_count(), $attributes); + $resultrowattributes = $tablerowattributes; + if ($summary->unknown_count() > 0) { + $resultrowattributes['class'] .= ' ' . $unknownattributes; + } else { + $resultrowattributes['class'] .= ' ' . $successattributes; + } + $body .= \html_writer::tag('tr', $tmp, $resultrowattributes); + + // Success rate. + $tmp = ''; + $tmp .= \html_writer::tag( + 'td', + \html_writer::tag( + 'b', + get_string('success_rate', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME) + ), + $attributes + ); + $suffix = '?'; + if ($summary->compilation_error_count() === 0 && $summary->unknown_count() === 0) { + $suffix = $summary->result_count() . ' (' . $successrate . '%)'; + } + $tmp .= \html_writer::tag( + 'td', + \html_writer::tag( + 'b', + $summary->successful_count() . '/' . $suffix + ), + $attributes + ); - $html .= $table; + $resultrowattributes = $tablerowattributes; + if ($summary->unknown_count() > 0 || $summary->compilation_error_count() > 0) { + $resultrowattributes['class'] .= ' ' . $unknownattributes; + } else { + if ($successrate !== '?' && $successrate < 50) { + $resultrowattributes['class'] .= ' ' . $compilationerrorattributes; + } else if ($successrate !== '?' && $successrate < 75) { + $resultrowattributes['class'] .= ' ' . $failureattributes; + } else if ($successrate !== '?') { + $resultrowattributes['class'] .= ' ' . $successattributes; + } + } + $body .= \html_writer::tag('tr', $tmp, $resultrowattributes); - // Add empty div for spacing after summary. - $html .= \html_writer::empty_tag("div", ["class" => "dtaSpacer"]); + $body = \html_writer::tag('tbody', $body); + $table = \html_writer::tag('table', $header . $body, ['class' => 'dtaTable']); - // *************** - // RECOMMENDATIONS TABLE - // *************** - if (!empty($recommendations)) { - // Sorting logic. - $allowed_sort_fields = ['topic', 'exercise_name', 'difficulty', 'score']; - $allowed_sort_dirs = ['asc', 'desc']; + $html .= $table; - $sortby = isset($_POST['sortby']) ? $_POST['sortby'] : 'score'; - $sortdir = isset($_POST['sortdir']) ? $_POST['sortdir'] : 'asc'; + // Add empty div for spacing after summary. + $html .= \html_writer::empty_tag('div', ['class' => 'dtaSpacer']); - if (!in_array($sortby, $allowed_sort_fields)) { - $sortby = 'score'; - } - if (!in_array($sortdir, $allowed_sort_dirs)) { - $sortdir = 'asc'; - } + // *** Recommendations Table *** + if (!empty($recommendations)) { + // Sorting logic. + $allowedsortfields = ['topic', 'exercise_name', 'difficulty', 'score']; + $allowedsortdirs = ['asc', 'desc']; - usort($recommendations, function($a, $b) use ($sortby, $sortdir) { - $valueA = $a->{$sortby}; - $valueB = $b->{$sortby}; + $sortby = isset($_POST['sortby']) ? $_POST['sortby'] : 'score'; + $sortdir = isset($_POST['sortdir']) ? $_POST['sortdir'] : 'asc'; - if (is_numeric($valueA) && is_numeric($valueB)) { - $comparison = $valueA - $valueB; - } else { - $comparison = strnatcasecmp($valueA, $valueB); + if (!in_array($sortby, $allowedsortfields)) { + $sortby = 'score'; } - - if ($comparison == 0) { - return 0; + if (!in_array($sortdir, $allowedsortdirs)) { + $sortdir = 'asc'; } - if ($sortdir == 'asc') { - return ($comparison < 0) ? -1 : 1; - } else { - return ($comparison < 0) ? 1 : -1; + usort($recommendations, function ($a, $b) use ($sortby, $sortdir) { + $valuea = $a->{$sortby}; + $valueb = $b->{$sortby}; + + if (is_numeric($valuea) && is_numeric($valueb)) { + $comparison = $valuea - $valueb; + } else { + $comparison = strnatcasecmp($valuea, $valueb); + } + + if ($comparison === 0) { + return 0; + } + + if ($sortdir === 'asc') { + return ($comparison < 0) ? -1 : 1; + } else { + return ($comparison < 0) ? 1 : -1; + } + }); + + $html .= \html_writer::tag( + 'h3', + get_string('recommendations', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME) + ); + + $generatesortableheader = function ($columnname, $displayname) use ($sortby, $sortdir) { + $newsortdir = ($sortby === $columnname && $sortdir === 'asc') ? 'desc' : 'asc'; + $class = 'dtaTableHeader'; + if ($sortby === $columnname) { + $class .= ' sorted ' . $sortdir; + } + + // Sort button. + $button = \html_writer::empty_tag('input', [ + 'type' => 'submit', + 'name' => 'sortbutton', + 'value' => ($newsortdir === 'asc' ? '↑' : '↓'), + 'class' => 'sort-button', + ]); + + // Hidden inputs. + $hiddeninputs = \html_writer::empty_tag('input', [ + 'type' => 'hidden', + 'name' => 'sortby', + 'value' => $columnname, + ]); + $hiddeninputs .= \html_writer::empty_tag('input', [ + 'type' => 'hidden', + 'name' => 'sortdir', + 'value' => $newsortdir, + ]); + + $form = \html_writer::start_tag('form', [ + 'method' => 'post', + 'style' => 'display:inline', + ]); + $form .= $hiddeninputs; + $form .= $displayname . ' ' . $button; + $form .= \html_writer::end_tag('form'); + + return \html_writer::tag('th', $form, ['class' => $class]); + }; + + // Table header for recommendations. + $tableheader = ''; + $tableheader .= $generatesortableheader( + 'topic', + get_string('topic', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME) + ); + $tableheader .= $generatesortableheader( + 'exercise_name', + get_string('exercise_name', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME) + ); + $tableheader .= \html_writer::tag( + 'th', + get_string('url', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), + ['class' => 'dtaTableHeader'] + ); + $tableheader .= $generatesortableheader( + 'difficulty', + get_string('difficulty', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME) + ); + $tableheader .= $generatesortableheader( + 'score', + get_string('score', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME) + ); + + $tableheader = \html_writer::tag('tr', $tableheader, ['class' => 'dtaTableHeaderRow']); + $tableheader = \html_writer::tag('thead', $tableheader); + + // Table body for recommendations. + $tablebody = ''; + foreach ($recommendations as $recommendation) { + $row = ''; + $row .= \html_writer::tag('td', $recommendation->topic, $attributes); + $row .= \html_writer::tag('td', $recommendation->exercise_name, $attributes); + $row .= \html_writer::tag( + 'td', + \html_writer::link($recommendation->url, $recommendation->url), + $attributes + ); + $row .= \html_writer::tag('td', $recommendation->difficulty, $attributes); + $row .= \html_writer::tag('td', $recommendation->score, $attributes); + + $tablebody .= \html_writer::tag('tr', $row, $tablerowattributes); } - }); + $tablebody = \html_writer::tag('tbody', $tablebody); - $html .= \html_writer::tag('h3', get_string('recommendations', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME)); + $html .= \html_writer::tag('table', $tableheader . $tablebody, ['class' => 'dtaTable']); - $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; - } - - // Sort button. - $button = \html_writer::empty_tag('input', [ - 'type' => 'submit', - 'name' => 'sortbutton', - 'value' => ($new_sortdir == 'asc' ? '↑' : '↓'), - 'class' => 'sort-button' - ]); - - // Hidden inputs. - $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 - ]); - - $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]); - }; - - // Table header for recommendations. - $tableheader = ""; - $tableheader .= $generate_sortable_header('topic', get_string("topic", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME)); - $tableheader .= $generate_sortable_header('exercise_name', get_string("exercise_name", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME)); - $tableheader .= \html_writer::tag("th", get_string("url", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), ["class" => "dtaTableHeader"]); - $tableheader .= $generate_sortable_header('difficulty', get_string("difficulty", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME)); - $tableheader .= $generate_sortable_header('score', get_string("score", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME)); - - $tableheader = \html_writer::tag("tr", $tableheader, ["class" => "dtaTableHeaderRow"]); - $tableheader = \html_writer::tag("thead", $tableheader); - - // Table body for recommendations. - $tablebody = ""; - foreach ($recommendations as $recommendation) { - $tablerow = ""; - $tablerow .= \html_writer::tag("td", $recommendation->topic, $attributes); - $tablerow .= \html_writer::tag("td", $recommendation->exercise_name, $attributes); - $tablerow .= \html_writer::tag("td", \html_writer::link($recommendation->url, $recommendation->url), $attributes); - $tablerow .= \html_writer::tag("td", $recommendation->difficulty, $attributes); - $tablerow .= \html_writer::tag("td", $recommendation->score, $attributes); - - $tablebody .= \html_writer::tag("tr", $tablerow, $tablerowattributes); + // Add empty div for spacing after recommendations. + $html .= \html_writer::empty_tag('div', ['class' => 'dtaSpacer']); } - $tablebody = \html_writer::tag("tbody", $tablebody); - - $html .= \html_writer::tag("table", $tableheader . $tablebody, ["class" => "dtaTable"]); - // Add empty div for spacing after recommendations. - $html .= \html_writer::empty_tag("div", ["class" => "dtaSpacer"]); - } - - // *************** - // COMPETENCY ASSESSMENT TABLE - // *************** - $body = ""; - $tmp = ""; - $tmp .= \html_writer::tag("th", get_string("competencies", self::ASSIGNSUBMISSION_DTA_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") { - $resultrowattributes = $tablerowattributes; - $tmp = ""; - $tmp .= \html_writer::tag("td", get_string("comp" . $index, self::ASSIGNSUBMISSION_DTA_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::ASSIGNSUBMISSION_DTA_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::ASSIGNSUBMISSION_DTA_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; + // *** Competency Assessment Table *** + $body = ''; + $tmp = ''; + $tmp .= \html_writer::tag( + 'th', + get_string('competencies', self::ASSIGNSUBMISSION_DTA_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 assessed, add a row in the table. + if ($comp !== '0') { + $resultrowattributes = $tablerowattributes; + $tmp = ''; + $tmp .= \html_writer::tag( + 'td', + get_string('comp' . $index, self::ASSIGNSUBMISSION_DTA_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::ASSIGNSUBMISSION_DTA_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. + $html .= \html_writer::empty_tag('div', ['class' => 'dtaSpacer']); + + // *** Details Table *** + $tmp = ''; + $tmp .= \html_writer::tag( + 'th', + get_string('details', self::ASSIGNSUBMISSION_DTA_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; + } - $resultrowattributes = $tablerowattributes; - - // Set CSS class for colored left-border according to results state. - 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'; - } + $resultrowattributes = $tablerowattributes; - $tmp = ""; - $tmp .= \html_writer::tag("td", get_string("package_name", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), $attributes); - $tmp .= \html_writer::tag("td", $r->packagename, $attributes); - $tmp .= \html_writer::tag("td", get_string("unit_name", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), $attributes); - $tmp .= \html_writer::tag("td", $r->classname, $attributes); - $tmp .= \html_writer::tag("td", get_string("test_name", self::ASSIGNSUBMISSION_DTA_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::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), $attributes); - $tmp .= \html_writer::tag("td", dta_result::assignsubmission_dta_get_statename($r->state), $attributes); - $body .= \html_writer::tag("tr", $tmp, $resultrowattributes); - - // If state is different than successful, show additional info. - if ($r->state != 1) { - $tmp = ""; - $tmp .= \html_writer::tag("td", get_string("failure_type", self::ASSIGNSUBMISSION_DTA_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::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), $attributes); - $tmp .= \html_writer::tag("td", $r->failureReason, $attributes); - $body .= \html_writer::tag("tr", $tmp, $resultrowattributes); - - if (!is_null($r->lineNumber) && $r->lineNumber > 0) { - $tmp = ""; - $tmp .= \html_writer::tag("td", get_string("line_no", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), $attributes); - $tmp .= \html_writer::tag("td", $r->lineNumber, $attributes); - $body .= \html_writer::tag("tr", $tmp, $resultrowattributes); + // Set CSS class for colored left-border according to results state. + 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'; } - if (!is_null($r->columnNumber) && $r->columnNumber > 0) { - $tmp = ""; - $tmp .= \html_writer::tag("td", get_string("col_no", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), $attributes); - $tmp .= \html_writer::tag("td", $r->columnNumber, $attributes); - $body .= \html_writer::tag("tr", $tmp, $resultrowattributes); + $tmp = ''; + $tmp .= \html_writer::tag( + 'td', + get_string('package_name', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), + $attributes + ); + $tmp .= \html_writer::tag('td', $r->packagename, $attributes); + $tmp .= \html_writer::tag( + 'td', + get_string('unit_name', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), + $attributes + ); + $tmp .= \html_writer::tag('td', $r->classname, $attributes); + $tmp .= \html_writer::tag( + 'td', + get_string('test_name', self::ASSIGNSUBMISSION_DTA_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::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), + $attributes + ); + $tmp .= \html_writer::tag( + 'td', + dta_result::assignsubmission_dta_get_statename($r->state), + $attributes + ); + $body .= \html_writer::tag('tr', $tmp, $resultrowattributes); + + // If state is different than successful, show additional info. + if ($r->state !== 1) { + $tmp = ''; + $tmp .= \html_writer::tag( + 'td', + get_string('failure_type', self::ASSIGNSUBMISSION_DTA_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::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), + $attributes + ); + $tmp .= \html_writer::tag('td', $r->failureReason, $attributes); + $body .= \html_writer::tag('tr', $tmp, $resultrowattributes); + + if (!is_null($r->lineNumber) && $r->lineNumber > 0) { + $tmp = ''; + $tmp .= \html_writer::tag( + 'td', + get_string('line_no', self::ASSIGNSUBMISSION_DTA_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::ASSIGNSUBMISSION_DTA_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::ASSIGNSUBMISSION_DTA_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::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), + $attributes + ); + $tmp .= \html_writer::tag( + 'td', + \html_writer::tag('details', $r->stacktrace, ['class' => 'dtaStacktraceDetails']), + $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::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), $attributes); - $tmp .= \html_writer::tag("td", $r->position, $attributes); - $body .= \html_writer::tag("tr", $tmp, $resultrowattributes); + if (is_null($spacerrow)) { + $spacerrow = \html_writer::empty_tag('tr', ['class' => 'dtaTableSpacer']); } - - $tmp = ""; - $tmp .= \html_writer::tag("td", get_string("stacktrace", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), $attributes); - $tmp .= \html_writer::tag("td", \html_writer::tag("details", $r->stacktrace, ["class" => "dtaStacktraceDetails"]), $attributes); - $body .= \html_writer::tag("tr", $tmp, $resultrowattributes); } - 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; -} - - - + $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/classes/models/dta_recommendation.php b/dta/classes/models/dta_recommendation.php index b37d68280b59f6057a4f8d619145c9732ee93391..8d78a2c12ba3a196c77ae3c83685e197cad8c2fc 100644 --- a/dta/classes/models/dta_recommendation.php +++ b/dta/classes/models/dta_recommendation.php @@ -1,5 +1,5 @@ <?php -// This file is part of Moodle - http://moodle.org/ +// 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 @@ -15,7 +15,7 @@ // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** - * Entity class for DTA submission plugin recommendation + * Entity class for DTA submission plugin recommendation. * * @package assignsubmission_dta * @copyright 2023 Gero Lueckemeyer @@ -24,10 +24,8 @@ namespace assignsubmission_dta\models; -defined('MOODLE_INTERNAL') || die(); - /** - * Entity class for DTA submission plugin recommendation + * Entity class for DTA submission plugin recommendation. * * @package assignsubmission_dta * @copyright 2023 @@ -41,9 +39,9 @@ class dta_recommendation { public $topic; /** - * @var string $exercise_name Name of the exercise. + * @var string $exerciseName Name of the exercise. */ - public $exercise_name; + public $exerciseName; /** * @var string $url URL of the exercise. @@ -61,22 +59,22 @@ class dta_recommendation { public $score; /** - * Decodes the JSON recommendations returned by the backend service call into an array of DtaRecommendation objects. + * Decodes the JSON recommendations returned by the backend service call into an array of dta_recommendation objects. * - * @param string $jsonstring JSON string containing recommendations - * @return array Array of DtaRecommendation objects + * @param string $jsonstring JSON string containing recommendations. + * @return array Array of dta_recommendation objects. */ public static function assignsubmission_dta_decode_json_recommendations(string $jsonstring): array { $response = json_decode($jsonstring); - $recommendations = []; - // Prüfe, ob Empfehlungen vorhanden sind + // Check if recommendations exist. if (!empty($response->recommendations)) { foreach ($response->recommendations as $recommendation) { $rec = new dta_recommendation(); $rec->topic = $recommendation->topic ?? null; - $rec->exercise_name = $recommendation->url ?? null; + // Map correct fields to the renamed variable names: + $rec->exerciseName = $recommendation->url ?? null; $rec->url = $recommendation->exerciseName ?? null; $rec->difficulty = $recommendation->difficulty ?? null; $rec->score = $recommendation->score ?? null; diff --git a/dta/classes/models/dta_result.php b/dta/classes/models/dta_result.php index 7d5089cc9c4e77cbddbe240bfaff66fe4838faab..6b2f4925f9ec1ff07c06df310834b38e0690506a 100644 --- a/dta/classes/models/dta_result.php +++ b/dta/classes/models/dta_result.php @@ -1,5 +1,5 @@ <?php -// This file is part of Moodle - http://moodle.org/ +// 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 @@ -15,7 +15,7 @@ // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** - * Entity class for DTA submission plugin result + * Entity class for DTA submission plugin result. * * @package assignsubmission_dta * @copyright 2023 Gero Lueckemeyer and student project teams @@ -24,10 +24,8 @@ namespace assignsubmission_dta\models; -defined('MOODLE_INTERNAL') || die(); - /** - * Entity class for DTA submission plugin result + * Entity class for DTA submission plugin result. * * @package assignsubmission_dta * @copyright 2023 Gero Lueckemeyer and student project teams @@ -38,7 +36,7 @@ class dta_result { /** * Broadly used in logic, parametrized for easier change. */ - const ASSIGNSUBMISSION_DTA_COMPONENT_NAME = 'assignsubmission_dta'; + public const ASSIGNSUBMISSION_DTA_COMPONENT_NAME = 'assignsubmission_dta'; /** * @var string $packagename Package name of the test. @@ -97,15 +95,15 @@ class dta_result { /** * Returns the name of a state with the given number for display. * - * @param int $state Number of the state - * @return string Name of state as defined + * @param int $state Number of the state. + * @return string Name of state as defined. */ public static function assignsubmission_dta_get_statename(int $state): string { - if ($state == 1) { + if ($state === 1) { return get_string('tests_successful', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME); - } else if ($state == 2) { + } else if ($state === 2) { return get_string('failures', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME); - } else if ($state == 3) { + } else if ($state === 3) { return get_string('compilation_errors', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME); } else { return get_string('unknown_state', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME); diff --git a/dta/classes/models/dta_result_summary.php b/dta/classes/models/dta_result_summary.php index f516fbf7be3ab3e76f00db8aaae8e50763e7a4ec..7659d47c649930f699a7b1057d81db00d7769e5b 100644 --- a/dta/classes/models/dta_result_summary.php +++ b/dta/classes/models/dta_result_summary.php @@ -1,5 +1,5 @@ <?php -// This file is part of Moodle - http://moodle.org/ +// 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 @@ -15,7 +15,7 @@ // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** - * Entity class for DTA submission plugin result summary + * Entity class for DTA submission plugin result summary. * * @package assignsubmission_dta * @copyright 2023 Gero Lueckemeyer and student project teams @@ -24,10 +24,8 @@ namespace assignsubmission_dta\models; -defined('MOODLE_INTERNAL') || die(); - /** - * Entity class for DTA submission plugin result summary + * Entity class for DTA submission plugin result summary. * * @package assignsubmission_dta * @copyright 2023 Gero Lueckemeyer and student project teams @@ -36,22 +34,22 @@ defined('MOODLE_INTERNAL') || die(); class dta_result_summary { /** - * @var int $timestamp Result timestamp for chronological ordering and deletion of previous results. + * @var int $timestamp Timestamp for ordering and deletion of previous results. */ public $timestamp; /** - * @var string $globalstacktrace Global stack trace if applicable, empty string otherwise. + * @var string $globalstacktrace Global stack trace if applicable, empty otherwise. */ public $globalstacktrace; /** - * @var string $successfultestcompetencies Successfully tested competencies according to tests and weights, empty string otherwise. + * @var string $successfultestcompetencies Successfully tested competencies (tests and weights), or empty string. */ public $successfultestcompetencies; /** - * @var string $overalltestcompetencies Overall tested competencies according to tests and weights, empty string otherwise. + * @var string $overalltestcompetencies Overall tested competencies (tests and weights), or empty string. */ public $overalltestcompetencies; @@ -63,8 +61,8 @@ class dta_result_summary { /** * Decodes the JSON result summary returned by the backend service call into the plugin PHP data structure. * - * @param string $jsonstring JSON string containing DtaResultSummary - * @return DtaResultSummary The result summary + * @param string $jsonstring JSON string containing DtaResultSummary. + * @return dta_result_summary The result summary. */ public static function assignsubmission_dta_decode_json(string $jsonstring): dta_result_summary { $response = json_decode($jsonstring); @@ -82,10 +80,10 @@ class dta_result_summary { } /** - * Decodes the array of JSON detail results returned by the backend service call into the plugin PHP data structure. + * Decodes an array of JSON detail results into the plugin PHP data structure. * - * @param array $jsonarray Decoded JSON array of results - * @return array Array of DtaResult + * @param array $jsonarray Decoded JSON array of results. + * @return array Array of dta_result objects. */ private static function assignsubmission_dta_decode_json_result_array(array $jsonarray): array { $ret = []; @@ -94,13 +92,10 @@ class dta_result_summary { $value->packagename = $entry->packageName ?? ''; $value->classname = $entry->className ?? ''; $value->name = $entry->name ?? ''; - $value->state = $entry->state ?? 0; - $value->failuretype = $entry->failureType ?? ''; $value->failurereason = $entry->failureReason ?? ''; $value->stacktrace = $entry->stacktrace ?? ''; - $value->columnnumber = $entry->columnNumber ?? ''; $value->linenumber = $entry->lineNumber ?? ''; $value->position = $entry->position ?? ''; @@ -113,7 +108,7 @@ class dta_result_summary { /** * Returns the number of detail results attached to the summary. * - * @return int Count of occurrences + * @return int Count of occurrences. */ public function assignsubmission_dta_result_count(): int { return count($this->results); @@ -122,13 +117,13 @@ class dta_result_summary { /** * Returns the number of detail results with the given state attached to the summary. * - * @param int $state State ordinal number - * @return int Count of occurrences for the provided state + * @param int $state State ordinal number. + * @return int Count of occurrences for the provided state. */ public function assignsubmission_dta_state_occurence_count(int $state): int { $num = 0; foreach ($this->results as $r) { - if ($r->state == $state) { + if ($r->state === $state) { $num++; } } @@ -138,7 +133,7 @@ class dta_result_summary { /** * Returns the number of detail results with compilation errors attached to the summary. * - * @return int Count of occurrences + * @return int Count of occurrences. */ public function assignsubmission_dta_compilation_error_count(): int { return $this->assignsubmission_dta_state_occurence_count(3); @@ -147,7 +142,7 @@ class dta_result_summary { /** * Returns the number of detail results with test failures attached to the summary. * - * @return int Count of occurrences + * @return int Count of occurrences. */ public function assignsubmission_dta_failed_count(): int { return $this->assignsubmission_dta_state_occurence_count(2); @@ -156,7 +151,7 @@ class dta_result_summary { /** * Returns the number of detail results with successful tests attached to the summary. * - * @return int Count of occurrences + * @return int Count of occurrences. */ public function assignsubmission_dta_successful_count(): int { return $this->assignsubmission_dta_state_occurence_count(1); @@ -165,7 +160,7 @@ class dta_result_summary { /** * Returns the number of detail results with an unknown result attached to the summary. * - * @return int Count of occurrences + * @return int Count of occurrences. */ public function assignsubmission_dta_unknown_count(): int { return $this->assignsubmission_dta_state_occurence_count(0); diff --git a/dta/locallib.php b/dta/locallib.php index b296d4bc2d4e221303cd11963f82a0002860f7ea..b0e9ba95320b9686dcf8c4ffcb6ea888e0be7094 100644 --- a/dta/locallib.php +++ b/dta/locallib.php @@ -1,5 +1,5 @@ <?php -// This file is part of Moodle - http://moodle.org/ +// 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 @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. -defined('MOODLE_INTERNAL') || die(); - use assignsubmission_dta\dta_db_utils; use assignsubmission_dta\dta_backend_utils; use assignsubmission_dta\dta_view_submission_utils; @@ -24,50 +22,58 @@ use assignsubmission_dta\models\dta_result_summary; use assignsubmission_dta\models\dta_recommendation; /** - * Library class for DTA submission plugin extending assign submission plugin base class + * Library class for DTA submission plugin extending assign submission plugin base class. * - * @package assignsubmission_dta - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @package assignsubmission_dta + * @copyright 2023 Your Name or Organization + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class assign_submission_dta extends assign_submission_plugin { /** * Broadly used in logic, parametrized for easier change. */ - const ASSIGNSUBMISSION_DTA_COMPONENT_NAME = "assignsubmission_dta"; + public const ASSIGNSUBMISSION_DTA_COMPONENT_NAME = 'assignsubmission_dta'; + /** * Draft file area for DTA tests to be uploaded by the teacher. */ - const ASSIGNSUBMISSION_DTA_DRAFT_FILEAREA_TEST = "tests_draft_dta"; + public const ASSIGNSUBMISSION_DTA_DRAFT_FILEAREA_TEST = 'tests_draft_dta'; + /** * File area for DTA tests to be uploaded by the teacher. */ - const ASSIGNSUBMISSION_DTA_FILEAREA_TEST = "tests_dta"; + public const ASSIGNSUBMISSION_DTA_FILEAREA_TEST = 'tests_dta'; + /** * File area for DTA submission assignment. */ - const ASSIGNSUBMISSION_DTA_FILEAREA_SUBMISSION = "submissions_dta"; + public const ASSIGNSUBMISSION_DTA_FILEAREA_SUBMISSION = 'submissions_dta'; /** - * Get plugin name + * Get plugin name. + * * @return string */ public function assignsubmission_dta_get_name(): string { - return get_string("pluginname", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME); + return get_string('pluginname', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME); } /** - * Get default settings for assignment submission settings + * Get default settings for assignment submission settings. * - * @param MoodleQuickForm $mform form to add elements to + * @param MoodleQuickForm $mform Form to add elements to. * @return void */ public function assignsubmission_dta_get_settings(MoodleQuickForm $mform): void { // Add draft filemanager to form. $mform->addElement( - "filemanager", + 'filemanager', self::ASSIGNSUBMISSION_DTA_DRAFT_FILEAREA_TEST, - get_string("submission_settings_label", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), + get_string( + 'submission_settings_label', + self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME + ), null, $this->get_file_options(true) ); @@ -77,7 +83,7 @@ class assign_submission_dta extends assign_submission_plugin { // Form-unique element id to which to add button. self::ASSIGNSUBMISSION_DTA_DRAFT_FILEAREA_TEST, // Key. - "submission_settings_label", + 'submission_settings_label', // Language file to use. self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME ); @@ -96,12 +102,15 @@ class assign_submission_dta extends assign_submission_plugin { /** * Allows the plugin to update the default values passed into * the settings form (needed to set up draft areas for editor - * and filemanager elements) - * @param array $defaultvalues + * and filemanager elements). + * + * @param array $defaultvalues Default values to update. */ public function data_preprocessing(&$defaultvalues): void { // Get id of draft area for file manager creation. - $draftitemid = file_get_submitted_draft_itemid(self::ASSIGNSUBMISSION_DTA_DRAFT_FILEAREA_TEST); + $draftitemid = file_get_submitted_draft_itemid( + self::ASSIGNSUBMISSION_DTA_DRAFT_FILEAREA_TEST + ); // Prepare draft area with created draft filearea. file_prepare_draft_area( @@ -117,14 +126,13 @@ class assign_submission_dta extends assign_submission_plugin { } /** - * Save settings of assignment submission settings + * Save settings of assignment submission settings. * - * @param stdClass $data + * @param stdClass $data Form data. * @return bool */ public function assignsubmission_dta_save_settings(stdClass $data): bool { - - // If the assignment has no filemanager for our plugin just leave. + // If the assignment has no filemanager for our plugin, just leave. $draftfilemanagerid = self::ASSIGNSUBMISSION_DTA_DRAFT_FILEAREA_TEST; if (!isset($data->$draftfilemanagerid)) { return true; @@ -155,7 +163,9 @@ class assign_submission_dta extends assign_submission_plugin { // Check if a file was uploaded. if (empty($files)) { - \core\notification::error(get_string("no_testfile_warning", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME)); + \core\notification::error( + get_string('no_testfile_warning', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME) + ); return true; } @@ -163,19 +173,27 @@ class assign_submission_dta extends assign_submission_plugin { $file = reset($files); // Send file to backend. - return dta_backend_utils::assignsubmission_dta_send_testconfig_to_backend($this->assignment, $file); + return dta_backend_utils::assignsubmission_dta_send_testconfig_to_backend( + $this->assignment, + $file + ); } /** - * Add elements to submission form + * Add elements to submission form. * - * @param mixed $submissionorgrade stdClass|null submission or grade to show in the form - * @param MoodleQuickForm $mform form for adding elements - * @param stdClass $data data for filling the elements - * @param int $userid current user - * @return bool form elements added + * @param stdClass|null $submissionorgrade Submission or grade to show in the form. + * @param MoodleQuickForm $mform Form for adding elements. + * @param stdClass $data Data for filling the elements. + * @param int $userid Current user. + * @return bool True if form elements added. */ - public function get_form_elements_for_user($submissionorgrade, MoodleQuickForm $mform, stdClass $data, $userid): bool { + public function get_form_elements_for_user( + $submissionorgrade, + MoodleQuickForm $mform, + stdClass $data, + $userid + ): bool { // Prepare submission filearea. $data = file_prepare_standard_filemanager( $data, @@ -193,20 +211,15 @@ class assign_submission_dta extends assign_submission_plugin { // Form-unique identifier. 'tasks_filemanager', // Label to show next to the filemanager. - get_string("submission_label", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), - // Attributes. + get_string('submission_label', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), null, - // Options. $this->get_file_options(false) ); // Add help button. $mform->addHelpButton( - // Related form item. - "tasks_filemanager", - // Key. - "submission_label", - // Language file. + 'tasks_filemanager', + 'submission_label', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME ); @@ -215,40 +228,46 @@ class assign_submission_dta extends assign_submission_plugin { /** * Determines if a submission file area contains any files. - * @param stdClass $submission submission to check - * @return bool true if file count is zero + * + * @param stdClass $submission Submission to check. + * @return bool True if file count is zero. */ public function assignsubmission_dta_is_empty(stdClass $submission): bool { - return $this->assignsubmission_dta_count_files($submission->id, self::ASSIGNSUBMISSION_DTA_FILEAREA_SUBMISSION) == 0; + return ($this->assignsubmission_dta_count_files( + $submission->id, + self::ASSIGNSUBMISSION_DTA_FILEAREA_SUBMISSION + ) === 0); } /** - * Counts the number of files in a filearea + * Counts the number of files in a filearea. * - * @param int $submissionid submission id to check - * @param string $areaid filearea id to count - * @return int number of files submitted in the filearea + * @param int $submissionid Submission id to check. + * @param string $areaid Filearea id to count. + * @return int Number of files submitted in the filearea. */ - private function assignsubmission_dta_count_files(int $submissionid, $areaid) { + private function assignsubmission_dta_count_files(int $submissionid, $areaid): int { $fs = get_file_storage(); - $files = $fs->get_area_files($this->assignment->get_context()->id, + $files = $fs->get_area_files( + $this->assignment->get_context()->id, self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME, $areaid, $submissionid, 'id', - false); + false + ); return count($files); } /** - * Save data to the database + * Save data to the database. * - * @param stdClass $submission - * @param stdClass $data - * @return bool + * @param stdClass $submission Submission object. + * @param stdClass $data Data from the form. + * @return bool True if saved successfully. */ - public function save(stdClass $submission, stdClass $data) { + public function save(stdClass $submission, stdClass $data): bool { $data = file_postupdate_standard_filemanager( $data, 'tasks', @@ -267,7 +286,6 @@ class assign_submission_dta extends assign_submission_plugin { // Get submitted files. $fs = get_file_storage(); $files = $fs->get_area_files( - // Id of current assignment. $this->assignment->get_context()->id, self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME, self::ASSIGNSUBMISSION_DTA_FILEAREA_SUBMISSION, @@ -278,15 +296,22 @@ class assign_submission_dta extends assign_submission_plugin { // Check if a file is uploaded. if (empty($files)) { - \core\notification::error(get_string("no_submissionfile_warning", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME)); + \core\notification::error( + get_string('no_submissionfile_warning', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME) + ); return true; } // Get the file. $file = reset($files); - // Send file to backend. - $response = \assignsubmission_dta\dta_backend_utils::assignsubmission_dta_send_submission_to_backend($this->assignment, $submission->id, $file); + // Send file to backend (split across lines to avoid exceeding length). + $response = \assignsubmission_dta\dta_backend_utils:: + assignsubmission_dta_send_submission_to_backend( + $this->assignment, + $submission->id, + $file + ); // With a null response, return an error. if (is_null($response)) { @@ -299,29 +324,35 @@ class assign_submission_dta extends assign_submission_plugin { // Decode recommendations from response. $recommendations = dta_recommendation::assignsubmission_dta_decode_json_recommendations($response); - error_log(print_r($recommendations, true)); + // Use Moodle debugging instead of error_log/print_r. + debugging('Recommendations: ' . json_encode($recommendations), DEBUG_DEVELOPER); - // Persist new results to database. - dta_db_utils::assignsubmission_dta_store_result_summary_to_database($this->assignment->get_instance()->id, $submission->id, $resultsummary); + // Persist new results to database (split long lines). + dta_db_utils::assignsubmission_dta_store_result_summary_to_database( + $this->assignment->get_instance()->id, + $submission->id, + $resultsummary + ); // Store the array of recommendations in the database. - dta_db_utils::assignsubmission_dta_store_recommendations_to_database($this->assignment->get_instance()->id, $submission->id, $recommendations); + dta_db_utils::assignsubmission_dta_store_recommendations_to_database( + $this->assignment->get_instance()->id, + $submission->id, + $recommendations + ); return true; } /** - * Display a short summary of the test results of the submission - * This is displayed as default view, with the option to expand - * to the full detailed results. + * Display a short summary of the test results of the submission. * - * @param stdClass $submission to show - * @param bool $showviewlink configuration variable to show expand option - * @return string summary results html + * @param stdClass $submission Submission to show. + * @param bool $showviewlink Whether to show expand option. + * @return string Summary results HTML. */ - public function view_summary(stdClass $submission, & $showviewlink) { + public function view_summary(stdClass $submission, &$showviewlink): string { $showviewlink = true; - return dta_view_submission_utils::assignsubmission_dta_generate_summary_html( $this->assignment->get_instance()->id, $submission->id @@ -329,12 +360,12 @@ class assign_submission_dta extends assign_submission_plugin { } /** - * Display detailed results + * Display detailed results. * - * @param stdClass $submission the submission the results are shown for. - * @return string detailed results html + * @param stdClass $submission The submission for which to show results. + * @return string Detailed results HTML. */ - public function view(stdClass $submission) { + public function view(stdClass $submission): string { return dta_view_submission_utils::assignsubmission_dta_generate_detail_html( $this->assignment->get_instance()->id, $submission->id @@ -344,73 +375,77 @@ class assign_submission_dta extends assign_submission_plugin { /** * Generate array of allowed file types to upload. * - * @param bool $settings switch to define if list for assignment settings - * or active submission should be returned - * + * @param bool $settings Whether this is for assignment settings or active submission. * @return array */ private function get_file_options(bool $settings): array { $fileoptions = [ - 'subdirs' => 0, - "maxfiles" => 1, - 'accepted_types' => ($settings - ? [".txt"] - : [ - ".txt", - ".zip", - ]), - 'return_types' => FILE_INTERNAL, - ]; + 'subdirs' => 0, + 'maxfiles' => 1, + 'accepted_types' => ( + $settings + ? ['.txt'] + : [ + '.txt', + '.zip', + ] + ), + 'return_types' => FILE_INTERNAL, + ]; return $fileoptions; } /** - * Get file areas returns a list of areas this plugin stores files - * @return array - An array of fileareas (keys) and descriptions (values) + * Get file areas returns a list of areas this plugin stores files. + * + * @return array An array of fileareas (keys) and descriptions (values). */ - public function get_file_areas() { + public function get_file_areas(): array { return [ - self::ASSIGNSUBMISSION_DTA_FILEAREA_SUBMISSION => get_string("dta_submissions_fa", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), - self::ASSIGNSUBMISSION_DTA_FILEAREA_TEST => get_string("dta_tests_fa", self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), + self::ASSIGNSUBMISSION_DTA_FILEAREA_SUBMISSION => + get_string('dta_submissions_fa', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), + self::ASSIGNSUBMISSION_DTA_FILEAREA_TEST => + get_string('dta_tests_fa', self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME), ]; } /** - * Produce a list of files suitable for export that represent this feedback or submission + * Produce a list of files suitable for export that represent this feedback or submission. * - * @param stdClass $submission The submission - * @param stdClass $user The user record - unused - * @return array - return an array of files indexed by filename + * @param stdClass $submission The submission object. + * @param stdClass $user The user record (unused). + * @return array An array of files indexed by filename. */ - public function get_files(stdClass $submission, stdClass $user) { + public function get_files(stdClass $submission, stdClass $user): array { $result = []; $fs = get_file_storage(); - $files = $fs->get_area_files($this->assignment->get_context()->id, + $files = $fs->get_area_files( + $this->assignment->get_context()->id, self::ASSIGNSUBMISSION_DTA_COMPONENT_NAME, self::ASSIGNSUBMISSION_DTA_FILEAREA_SUBMISSION, $submission->id, 'timemodified', - false); + false + ); - foreach ($files as $file) { + foreach ($files as $fileobj) { // Do we return the full folder path or just the file name? - if (isset($submission->exportfullpath) && $submission->exportfullpath == false) { - $result[$file->get_filename()] = $file; + if (isset($submission->exportfullpath) && $submission->exportfullpath === false) { + $result[$fileobj->get_filename()] = $fileobj; } else { - $result[$file->get_filepath().$file->get_filename()] = $file; + $result[$fileobj->get_filepath() . $fileobj->get_filename()] = $fileobj; } } return $result; } /** - * The plugin is being uninstalled - cleanup + * The plugin is being uninstalled - cleanup. * * @return bool */ - public function delete_instance() { + public function delete_instance(): bool { dta_db_utils::assignsubmission_dta_uninstall_plugin_cleaup(); - return true; } }