Commit cad79fa6 authored by Abbassy's avatar Abbassy
Browse files

Update plugin to latest code and package

parent 7b9a500c
......@@ -22,8 +22,6 @@
* @copyright Gero Lueckemeyer and student project teams
*/
defined('MOODLE_INTERNAL') || die();
// General.
$string["pluginname"] = "Dockerized Testing Agent (DTA)";
$string["enabled"] = $string["pluginname"];
......@@ -40,6 +38,8 @@ $string["tests_successful"] = " successful";
$string["failures"] = " failure";
$string["compilation_errors"] = " compilation error";
$string["unknown_state"] = " with unknown state";
$string["unknownstate_message"] = "Results with unknown state present – not counted as success.";
$string["compilationerror_message"] = "Compilation errors reported by backend – grade and marking follow backend status.";
$string["success_competencies"] = "Successfully tested competency profile ";
$string["summary"] = "Summary";
$string["total_items"] = "Total items";
......@@ -181,15 +181,15 @@ $string['url'] = 'URL';
$string['difficulty'] = 'Difficulty';
$string['score'] = 'Score';
// Test summary cards and suggestion grade
// Report and grade summary strings
$string['dta_report_heading'] = 'Dockerized Test Agent report Summary';
$string['tests_total'] = 'Total tests';
$string['view_dta_report'] = 'View DTA Report';
$string['suggestion_grade_summary'] = 'Suggestion grade';
$string['suggestion_grade_formula'] = 'Grade calculation formula';
$string['suggestion_grade_formula_test'] = 'Test score = (Successful tests / Total tests) × 100%';
$string['suggestion_grade_formula_competency'] = 'Competency score = Average of competency block percentages';
$string['suggestion_grade_formula_final'] = 'Final score = (Test score × 80%) + (Competency score × 20%)';
$string['suggestion_grade_formula_final'] = 'Final score = (Test score × 50%) + (Competency score × 50%)';
$string['suggestion_grade_formula_final_no_competency'] = 'Final score = Test score × 100%';
$string['suggestion_grade_formula_compilation'] = 'If more than 1 compilation error exists, final grade = 0';
$string['suggestion_grade_value'] = 'Suggestion grade';
$string['suggestion_grade_value'] = 'Suggested Grade';
......@@ -86,3 +86,6 @@ function assignsubmission_dta_pluginfile(
// Download MUST be forced - security!
send_stored_file($file, 0, 0, true);
}
function assignsubmission_dta_get_file_areas() {
return ['tasks'];
}
\ No newline at end of file
......@@ -323,9 +323,6 @@ class assign_submission_dta extends assign_submission_plugin {
// Decode recommendations from response.
$recommendations = dta_recommendation::assignsubmission_dta_decode_json_recommendations($response);
// Use Moodle debugging instead of error_log/print_r (only in DEBUG_DEVELOPER mode).
// debugging('Recommendations: ' . json_encode($recommendations), DEBUG_DEVELOPER);
// Persist new results to database (split long lines).
dta_db_utils::assignsubmission_dta_store_result_summary_to_database(
$this->assignment->get_instance()->id,
......@@ -351,7 +348,7 @@ class assign_submission_dta extends assign_submission_plugin {
* @return string Summary results HTML.
*/
public function view_summary(stdClass $submission, &$showviewlink): string {
$showviewlink = false; // Disable the default magnifying glass icon
$showviewlink = false; // Disable magnifying glass icon (Lupe)
return dta_view_submission_utils::assignsubmission_dta_generate_summary_html(
$this->assignment->get_instance()->id,
$submission->id
......@@ -365,13 +362,10 @@ class assign_submission_dta extends assign_submission_plugin {
* @return string Detailed results HTML.
*/
public function view(stdClass $submission): string {
// Redirect to the new classes/view.php
$cmid = $this->assignment->get_course_module()->id;
$url = new moodle_url('/mod/assign/submission/dta/classes/view.php', [
'cmid' => $cmid,
'sid' => $submission->id
]);
redirect($url);
return dta_view_submission_utils::assignsubmission_dta_generate_detail_html(
$this->assignment->get_instance()->id,
$submission->id
);
}
/**
......
......@@ -73,96 +73,30 @@
border-bottom: 2px solid darkgray;
}
/* More Details Button - Match Grade button style */
.dtaMoreDetailsButton {
background-color: #0f6cbf;
color: white;
border: 1px solid #0f6cbf;
padding: 6px 12px;
border-radius: 3px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
text-decoration: none;
display: inline-block;
transition: all 0.2s ease;
margin: 0;
vertical-align: top;
line-height: 1.4;
/* View DTA Report Button - same styling as Grade button */
.dta-view-report-button {
margin-bottom: 10px;
width: 100%;
display: block;
text-align: center;
min-width: 80px;
}
.dtaMoreDetailsButton:hover {
background-color: #0a5a9c;
border-color: #0a5a9c;
color: white;
background-color: #0f6cbf !important; /* Same blue as Grade button */
border-color: #0f6cbf !important;
color: #fff !important;
padding: 0.375rem 0.75rem;
font-size: 0.875rem;
line-height: 1.5;
border-radius: 0.25rem;
text-decoration: none;
}
.dtaMoreDetailsButton:active {
background-color: #084a82;
border-color: #084a82;
}
.dtaMoreDetailsButton:focus {
outline: 2px solid #0f6cbf;
outline-offset: 2px;
}
/* Test Result Labels */
.dtaTestLabel {
display: inline-block;
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: bold;
margin-right: 8px;
margin-bottom: 4px;
}
.dtaTestLabelSuccess {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.dtaTestLabelError {
background-color: #fff3cd;
color: #856404;
border: 1px solid #ffeaa7;
}
.dtaTestLabelFailure {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
/* Test Results Container */
.dtaTestResults {
margin-top: 6px;
line-height: 1.4;
display: block;
padding-top: 0;
}
.dtaTestResults br {
margin-bottom: 4px;
}
/* Container for button and labels alignment */
.dtaSubmissionSummary {
white-space: nowrap;
vertical-align: top;
padding-top: 0;
margin-top: 0;
.dta-view-report-button:hover,
.dta-view-report-button:focus {
background-color: #0a5490 !important;
border-color: #0a5490 !important;
color: #fff !important;
text-decoration: none;
}
.dtaSubmissionSummary .dtaMoreDetailsButton {
margin-bottom: 8px;
margin-top: 0;
display: block;
position: relative;
top: -2px;
.dta-view-report-button:visited {
color: #fff !important;
}
\ No newline at end of file
......@@ -23,7 +23,7 @@
<!-- Final score formula with competencies -->
<div class="mb-2">
<strong>{{#str}}suggestion_grade_formula_final, assignsubmission_dta{{/str}}</strong><br/>
<span style="color: #666; font-size: 0.9em; margin-left: 1rem;">= ({{test_score}}% × 80%) + ({{competency_percent}}% × 20%) = {{final_score}}%</span>
<span style="color: #666; font-size: 0.9em; margin-left: 1rem;">= ({{test_score}}% × 50%) + ({{competency_percent}}% × 50%) = {{final_score}}%</span>
</div>
{{/has_competencies}}
......
<?php
// Set this if you want to run the script for one component only. Otherwise leave empty.
$CHECK_COMPONENT = '';
define('CLI_SCRIPT', true);
require_once('config.php');
$user = \core_user::get_user(2);
\core\session\manager::init_empty_session();
\core\session\manager::set_user($user);
$rc = new \ReflectionClass(\core_privacy\manager::class);
$rcm = $rc->getMethod('get_component_list');
$rcm->setAccessible(true);
$manager = new \core_privacy\manager();
$components = $rcm->invoke($manager);
$list = (object) [
'good' => [],
'bad' => [],
];
foreach ($components as $component) {
if ($CHECK_COMPONENT && $component !== $CHECK_COMPONENT) {
continue;
}
$compliant = $manager->component_is_compliant($component);
if ($compliant) {
$list->good[] = $component;
} else {
$list->bad[] = $component;
}
}
echo "The following plugins are not compliant:\n";
echo "=> " . implode("\n=> ", array_values($list->bad)) . "\n";
echo "\n";
echo "Testing the compliant plugins:\n";
foreach ($list->good as $component) {
$classname = \core_privacy\manager::get_provider_classname_for_component($component);
echo "== {$component} ($classname) ==\n";
if (check_implements($component, \core_privacy\local\metadata\null_provider::class)) {
echo " Claims not to store any data with reason:\n";
echo " '" . get_string($classname::get_reason(), $component) . "'\n";
}
else if (check_implements($component, \core_privacy\local\metadata\provider::class)) {
$collection = new \core_privacy\local\metadata\collection($component);
$classname::get_metadata($collection);
$count = count($collection->get_collection());
echo " Found {$count} items of metadata\n";
if (empty($count)) {
echo "!!! No metadata found!!! This an error.\n";
}
if (check_implements($component, \core_privacy\local\request\user_preference_provider::class)) {
$userprefdescribed = false;
foreach ($collection->get_collection() as $item) {
if ($item instanceof \core_privacy\local\metadata\types\user_preference) {
$userprefdescribed = true;
echo " ".$item->get_name()." : ".get_string($item->get_summary(), $component) . "\n";
}
}
if (!$userprefdescribed) {
echo "!!! User preference found, but was not described in metadata\n";
}
}
if (check_implements($component, \core_privacy\local\request\core_user_data_provider::class)) {
// No need to check the return type - it's enforced by the interface.
$contextlist = $classname::get_contexts_for_userid($user->id);
$approvedcontextlist = new \core_privacy\local\request\approved_contextlist($user, $contextlist->get_component(), $contextlist->get_contextids());
if (count($approvedcontextlist)) {
$classname::export_user_data($approvedcontextlist);
echo " Successfully ran a test export\n";
} else {
echo " Nothing to export.\n";
}
}
if (check_implements($component, \core_privacy\local\request\shared_data_provider::class)) {
echo " This is a shared data provider\n";
}
}
}
echo "\n\n== Done ==\n";
function check_implements($component, $interface) {
$manager = new \core_privacy\manager();
$rc = new \ReflectionClass(\core_privacy\manager::class);
$rcm = $rc->getMethod('component_implements');
$rcm->setAccessible(true);
return $rcm->invoke($manager, $component, $interface);
}
\ No newline at end of file
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
define('CLI_SCRIPT', true);
require_once('config.php');
require_once("$CFG->libdir/clilib.php");
list($options, $unrecognized) = cli_get_params(
[
'username' => '',
'userid' => '',
],
[]
);
$user = null;
$username = $options['username'];
$userid = $options['userid'];
if (!empty($options['username'])) {
$user = \core_user::get_user_by_username($options['username']);
} else if (!empty($options['userid'])) {
$user = \core_user::get_user($options['userid']);
}
while (empty($user)) {
if (!empty($username)) {
echo "Unable to find a user with username '{$username}'.\n";
echo "Try again.\n";
} else if (!empty($userid)) {
echo "Unable to find a user with userid '{$userid}'.\n";
echo "Try again.\n";
}
$username = readline("Username: ");
$user = \core_user::get_user_by_username($username);
}
echo "Processing delete for " . fullname($user) . "\n";
\core\session\manager::init_empty_session();
\core\session\manager::set_user($user);
$manager = new \core_privacy\manager();
$approvedlist = new \core_privacy\local\request\contextlist_collection($user->id);
$trace = new text_progress_trace();
$contextlists = $manager->get_contexts_for_userid($user->id, $trace);
foreach ($contextlists as $contextlist) {
$approvedlist->add_contextlist(new \core_privacy\local\request\approved_contextlist(
$user,
$contextlist->get_component(),
$contextlist->get_contextids()
));
}
$manager->delete_data_for_user($approvedlist, $trace);
\ No newline at end of file
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Helper utility to perform a test export.
*
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require_once('config.php');
require_once("$CFG->libdir/clilib.php");
list($options, $unrecognized) = cli_get_params(
[
'username' => '',
'userid' => '',
],
[]
);
$user = null;
$username = $options['username'];
$userid = $options['userid'];
if (!empty($options['username'])) {
$user = \core_user::get_user_by_username($options['username']);
} else if (!empty($options['userid'])) {
$user = \core_user::get_user($options['userid']);
}
while (empty($user)) {
if (!empty($username)) {
echo "Unable to find a user with username '{$username}'.\n";
echo "Try again.\n";
} else if (!empty($userid)) {
echo "Unable to find a user with userid '{$userid}'.\n";
echo "Try again.\n";
}
$username = readline("Username: ");
$user = \core_user::get_user_by_username($username);
}
echo "Processing export for " . fullname($user) . "\n";
\core\session\manager::init_empty_session();
\core\session\manager::set_user($user);
$PAGE = new moodle_page();
$OUTPUT = new core_renderer($PAGE, RENDERER_TARGET_GENERAL);
$manager = new \core_privacy\manager();
$approvedlist = new \core_privacy\local\request\contextlist_collection($user->id);
$contextlists = $manager->get_contexts_for_userid($user->id);
foreach ($contextlists as $contextlist) {
$approvedlist->add_contextlist(new \core_privacy\local\request\approved_contextlist(
$user,
$contextlist->get_component(),
$contextlist->get_contextids()
));
}
$exportedcontent = $manager->export_user_data($approvedlist);
$basedir = make_temp_directory('privacy');
$exportpath = make_unique_writable_directory($basedir, true);
$fp = get_file_packer();
$fp->extract_to_pathname($exportedcontent, $exportpath);
echo "\n";
echo "== File export was uncompressed to {$exportpath}\n";
echo "============================================================================\n";
\ No newline at end of file
#!/bin/bash
# Script zum automatischen Upload der Plugin-Dateien in den Docker-Container
# Verwendung: ./upload_to_docker.sh
set -e # Beende bei Fehlern
# Farben für bessere Ausgabe
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Container-Name (aus vorheriger Analyse)
CONTAINER_NAME="moodle-dta"
MOODLE_PATH="/var/www/html/moodle/mod/assign/submission/dta"
echo -e "${BLUE}=== Moodle Plugin Upload Script ===${NC}"
echo -e "${YELLOW}Container: ${CONTAINER_NAME}${NC}"
echo -e "${YELLOW}Ziel-Pfad: ${MOODLE_PATH}${NC}"
echo ""
# Prüfe ob Container läuft
echo -e "${BLUE}1. Prüfe Docker-Container...${NC}"
if ! docker ps | grep -q "$CONTAINER_NAME"; then
echo -e "${RED}Fehler: Container '$CONTAINER_NAME' läuft nicht!${NC}"
echo "Verfügbare Container:"
docker ps --format "table {{.Names}}\t{{.Status}}"
exit 1
fi
echo -e "${GREEN}✓ Container läuft${NC}"
# Erstelle Backup des aktuellen Zustands
echo -e "${BLUE}2. Erstelle Backup...${NC}"
BACKUP_DIR="/tmp/moodle_backup_$(date +%Y%m%d_%H%M%S)"
docker exec "$CONTAINER_NAME" mkdir -p "$BACKUP_DIR"
docker exec "$CONTAINER_NAME" cp -r "$MOODLE_PATH" "$BACKUP_DIR/"
echo -e "${GREEN}✓ Backup erstellt: $BACKUP_DIR${NC}"
# Upload der Dateien
echo -e "${BLUE}3. Lade Dateien hoch...${NC}"
# Hauptdateien
echo -e "${YELLOW} → view.php${NC}"
docker cp classes/view.php "$CONTAINER_NAME:$MOODLE_PATH/classes/"
echo -e "${YELLOW} → locallib.php${NC}"
docker cp locallib.php "$CONTAINER_NAME:$MOODLE_PATH/"
echo -e "${YELLOW} → settings.php${NC}"
docker cp settings.php "$CONTAINER_NAME:$MOODLE_PATH/"
echo -e "${YELLOW} → lib.php${NC}"
docker cp lib.php "$CONTAINER_NAME:$MOODLE_PATH/"
echo -e "${YELLOW} → version.php${NC}"
docker cp version.php "$CONTAINER_NAME:$MOODLE_PATH/"
# Output-Klassen
echo -e "${YELLOW} → Output-Klassen${NC}"
docker cp classes/output/summary_table.php "$CONTAINER_NAME:$MOODLE_PATH/classes/output/"
docker cp classes/output/generic_table.php "$CONTAINER_NAME:$MOODLE_PATH/classes/output/"
docker cp classes/output/sortable_table.php "$CONTAINER_NAME:$MOODLE_PATH/classes/output/"
# Model-Klassen
echo -e "${YELLOW} → Model-Klassen${NC}"
docker cp classes/models/dta_result.php "$CONTAINER_NAME:$MOODLE_PATH/classes/models/"
# Templates
echo -e "${YELLOW} → Templates${NC}"
docker cp templates/summary_table.mustache "$CONTAINER_NAME:$MOODLE_PATH/templates/"
docker cp templates/generic_table.mustache "$CONTAINER_NAME:$MOODLE_PATH/templates/"
docker cp templates/sortable_table.mustache "$CONTAINER_NAME:$MOODLE_PATH/templates/"
# Sprachdateien
echo -e "${YELLOW} → Sprachdateien${NC}"
docker cp lang/en/assignsubmission_dta.php "$CONTAINER_NAME:$MOODLE_PATH/lang/en/"
# CSS
echo -e "${YELLOW} → CSS${NC}"
docker cp styles.css "$CONTAINER_NAME:$MOODLE_PATH/"
# Weitere Klassen
echo -e "${YELLOW} → Weitere Klassen${NC}"
docker cp classes/backend.php "$CONTAINER_NAME:$MOODLE_PATH/classes/"
docker cp classes/database.php "$CONTAINER_NAME:$MOODLE_PATH/classes/"
docker cp classes/dta_backend_utils.php "$CONTAINER_NAME:$MOODLE_PATH/classes/"
docker cp classes/dta_db_utils.php "$CONTAINER_NAME:$MOODLE_PATH/classes/"
docker cp classes/dta_view_submission_utils.php "$CONTAINER_NAME:$MOODLE_PATH/classes/"
docker cp classes/view.php "$CONTAINER_NAME:$MOODLE_PATH/classes/"
# Weitere Model-Klassen
if [ -f "classes/models/dta_recommendation.php" ]; then
docker cp classes/models/dta_recommendation.php "$CONTAINER_NAME:$MOODLE_PATH/classes/models/"
fi
if [ -f "classes/models/dta_result_summary.php" ]; then
docker cp classes/models/dta_result_summary.php "$CONTAINER_NAME:$MOODLE_PATH/classes/models/"
fi
if [ -f "classes/models/dta_result.php" ]; then
docker cp classes/models/dta_result.php "$CONTAINER_NAME:$MOODLE_PATH/classes/models/"
fi
# Weitere Output-Klassen
if [ -f "classes/output/alt_view.php" ]; then
docker cp classes/output/alt_view.php "$CONTAINER_NAME:$MOODLE_PATH/classes/output/"
fi
# Weitere Dateien
if [ -f "classes/privacy/provider.php" ]; then
docker cp classes/privacy/provider.php "$CONTAINER_NAME:$MOODLE_PATH/classes/privacy/"
fi
echo -e "${GREEN}✓ Alle Dateien hochgeladen${NC}"
# Setze korrekte Berechtigungen
echo -e "${BLUE}4. Setze Berechtigungen...${NC}"
docker exec "$CONTAINER_NAME" chown -R www-data:www-data "$MOODLE_PATH"
docker exec "$CONTAINER_NAME" chmod -R 755 "$MOODLE_PATH"
echo -e "${GREEN}✓ Berechtigungen gesetzt${NC}"
# Leere Caches
echo -e "${BLUE}5. Leere Moodle-Caches...${NC}"
docker exec "$CONTAINER_NAME" php /var/www/html/moodle/admin/cli/purge_caches.php
echo -e "${GREEN}✓ Caches geleert${NC}"
# Optional: Upgrade durchführen
echo -e "${BLUE}6. Führe Plugin-Upgrade durch...${NC}"
docker exec "$CONTAINER_NAME" php /var/www/html/moodle/admin/cli/upgrade.php --non-interactive --allow-unstable
echo -e "${GREEN}✓ Upgrade abgeschlossen${NC}"
echo ""
echo -e "${GREEN}=== Upload erfolgreich abgeschlossen! ===${NC}"
echo -e "${YELLOW}Dein Plugin wurde aktualisiert und ist bereit zur Verwendung.${NC}"
echo ""
echo -e "${BLUE}Verfügbare Aktionen:${NC}"
echo -e " • Backup wiederherstellen: ${YELLOW}docker exec $CONTAINER_NAME cp -r $BACKUP_DIR/dta $MOODLE_PATH${NC}"
echo -e " • Container neu starten: ${YELLOW}docker restart $CONTAINER_NAME${NC}"
echo -e " • Logs anzeigen: ${YELLOW}docker logs $CONTAINER_NAME${NC}"
echo ""
echo -e "${GREEN}Fertig! 🎉${NC}"
......@@ -24,7 +24,7 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2023123001;
$plugin->version = 2023123002;
$plugin->requires = 2020061525; // Moodle 3.9 LTS. Older unsupported versions from the 3.x branch should also work.
$plugin->component = 'assignsubmission_dta';
$plugin->maturity = MATURITY_STABLE;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment