feat: Add XLSX export for user grades report

Introduces XLSX export functionality to the user grades report.
Adds a checkbox for selecting XLSX export in the form interface.
Uses PhpSpreadsheet to generate and download detailed Excel
files containing user grades and quiz attempts.

Enhancement enables convenient data export for offline analysis
and sharing.
This commit is contained in:
Kumi 2024-11-25 10:09:36 +01:00
parent 7252b729b2
commit 80ac83db92
Signed by: kumi
GPG key ID: ECBCC9082395383F
4 changed files with 147 additions and 54 deletions

View file

@ -15,6 +15,9 @@ class usergrades_form extends moodleform
$mform->addElement('select', 'userid', get_string('selectuser', 'report_usergrades'), $users[get_string('users')]); $mform->addElement('select', 'userid', get_string('selectuser', 'report_usergrades'), $users[get_string('users')]);
// XLSX Export checkbox
$mform->addElement('checkbox', 'exportxls', get_string('exportxls', 'report_usergrades'));
// Add form action buttons // Add form action buttons
$this->add_action_buttons(false, get_string('showgrades', 'report_usergrades')); $this->add_action_buttons(false, get_string('showgrades', 'report_usergrades'));
} }

View file

@ -8,97 +8,184 @@ require_once($CFG->dirroot . '/user/selector/lib.php');
require_once('classes/user_selector.php'); require_once('classes/user_selector.php');
require_once('classes/usergrades_form.php'); require_once('classes/usergrades_form.php');
// Set up the page context and other configurations use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
admin_externalpage_setup('report_usergrades_details', '', null, '', array('capability' => 'report/usergrades:view')); admin_externalpage_setup('report_usergrades_details', '', null, '', array('capability' => 'report/usergrades:view'));
// Instantiate the form // Instantiate the form
$mform = new usergrades_form(); $mform = new usergrades_form();
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('usergradesreport', 'report_usergrades'));
// Form processing and displaying is done here // Check if form data is submitted
if ($mform->is_cancelled()) { if ($data = $mform->get_data()) {
// Handle form cancellation, if necessary
redirect(new moodle_url('/admin/report.php'));
} else if ($data = $mform->get_data()) {
$userid = $data->userid; $userid = $data->userid;
// Fetch and display user grades if a user is selected
if (!empty($userid)) { if (!empty($userid)) {
$user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST); $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
$courses = enrol_get_users_courses($user->id, true); $courses = enrol_get_users_courses($user->id, true);
$questions = $DB->get_records('question'); $questions = $DB->get_records('question');
// User details if (isset($data->exportxls)) { // Assume a form field named 'exportxls' for Excel export action
// Create a new Spreadsheet object
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$row = 1;
echo $OUTPUT->heading(get_string('userdetails', 'report_usergrades'), 3); // Add the user details
$sheet->setCellValue('A' . $row, get_string('username', 'report_usergrades'));
$sheet->setCellValue('B' . $row, $user->username);
$row++;
$sheet->setCellValue('A' . $row, get_string('firstname', 'report_usergrades'));
$sheet->setCellValue('B' . $row, $user->firstname);
$row++;
$sheet->setCellValue('A' . $row, get_string('lastname', 'report_usergrades'));
$sheet->setCellValue('B' . $row, $user->lastname);
$row++;
$sheet->setCellValue('A' . $row, get_string('email', 'report_usergrades'));
$sheet->setCellValue('B' . $row, $user->email);
$row += 2; // Extra space before courses
$user_table = new html_table(); // Start iterating the courses
$user_table->head = array(get_string('field', 'report_usergrades'), get_string('value', 'report_usergrades')); foreach ($courses as $course) {
$sheet->setCellValue('A' . $row, get_string('course', 'report_usergrades') . ': ' . $course->fullname);
$row++;
$user_table->data[] = array(get_string('username', 'report_usergrades'), $user->username); $quizzes = $DB->get_records('quiz', array('course' => $course->id));
$user_table->data[] = array(get_string('firstname', 'report_usergrades'), $user->firstname);
$user_table->data[] = array(get_string('lastname', 'report_usergrades'), $user->lastname);
$user_table->data[] = array(get_string('email', 'report_usergrades'), $user->email);
echo html_writer::table($user_table); if ($quizzes) {
foreach ($quizzes as $quiz) {
$quiz_attempts = $DB->get_records('quiz_attempts', array('quiz' => $quiz->id, 'userid' => $user->id));
foreach ($courses as $course) { if ($quiz_attempts) {
echo $OUTPUT->heading($course->fullname, 3); $sheet->setCellValue('A' . $row, get_string('quiz', 'report_usergrades') . ': ' . $quiz->name);
$row++;
$quizzes = $DB->get_records('quiz', array('course' => $course->id)); foreach ($quiz_attempts as $attempt) {
$sheet->setCellValue('A' . $row, get_string('attempt', 'report_usergrades') . ' ' . $attempt->attempt);
$row++;
if ($quizzes) { $sheet->setCellValue('A' . $row, get_string('question', 'report_usergrades'));
echo $OUTPUT->heading(get_string('quizzes', 'report_usergrades'), 4); $sheet->setCellValue('B' . $row, get_string('response', 'report_usergrades'));
$sheet->setCellValue('C' . $row, get_string('grade', 'report_usergrades'));
$row++;
foreach ($quizzes as $quiz) { $question_usages = $DB->get_records('question_usages', array('id' => $attempt->uniqueid));
$quiz_questions = $DB->get_records('quiz_slots', array('quizid' => $quiz->id));
$quiz_attempts = $DB->get_records('quiz_attempts', array('quiz' => $quiz->id, 'userid' => $user->id));
if ($quiz_attempts) { foreach ($question_usages as $question_usage) {
echo $OUTPUT->heading($quiz->name, 5); $question_attempts = $DB->get_records('question_attempts', array('questionusageid' => $question_usage->id));
foreach ($quiz_attempts as $attempt) { foreach ($question_attempts as $question_attempt) {
echo $OUTPUT->heading(get_string('attempt', 'report_usergrades') . ' ' . $attempt->attempt, 6); $question = $questions[$question_attempt->questionid];
$response = $question_attempt->responsesummary;
$attempt_table = new html_table(); $question_grades = $DB->get_records('question_attempt_steps', array('questionattemptid' => $question_attempt->id));
$attempt_table->head = array(get_string('question', 'report_usergrades'), get_string('response', 'report_usergrades'), get_string('grade', 'report_usergrades'));
$question_usages = $DB->get_records('question_usages', array('id' => $attempt->uniqueid)); foreach ($question_grades as $question_grade) {
if ($question_grade->fraction) {
foreach ($question_usages as $question_usage) { $sheet->setCellValue('A' . $row, $question->name . ': ' . $question->questiontext);
$question_attempts = $DB->get_records('question_attempts', array('questionusageid' => $question_usage->id)); $sheet->setCellValue('B' . $row, $response);
$sheet->setCellValue('C' . $row, $question_grade->fraction);
foreach ($question_attempts as $question_attempt) { $row++;
$question = $questions[$question_attempt->questionid]; }
$response = $question_attempt->responsesummary;
$question_grades = $DB->get_records('question_attempt_steps', array('questionattemptid' => $question_attempt->id));
foreach ($question_grades as $question_grade) {
if ($question_grade->fraction) {
$attempt_table->data[] = array($question->name . ': ' . $question->questiontext, $response, $question_grade->fraction);
} }
} }
} }
$total_grade = $DB->get_record('quiz_grades', array('quiz' => $quiz->id, 'userid' => $user->id));
$sheet->setCellValue('A' . $row, get_string('totalgrade', 'report_usergrades') . ': ' . $total_grade->grade . ' / ' . $quiz->grade);
$row += 2; // Extra space before next attempt or quiz
} }
}
}
}
$row += 2; // Extra space between courses
}
echo html_writer::table($attempt_table); // Create writer and output the file
$writer = new Xlsx($spreadsheet);
$filename = 'user_grades_' . $user->id . '.xlsx';
$total_grade = $DB->get_record('quiz_grades', array('quiz' => $quiz->id, 'userid' => $user->id)); // Redirect output to a clients web browser (Excel2007)
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="' . $filename . '"');
header('Cache-Control: max-age=0');
echo $OUTPUT->heading(get_string('totalgrade', 'report_usergrades') . ': ' . $total_grade->grade . ' / ' . $quiz->grade, 6); $writer->save('php://output');
exit();
} else {
// HTML output if not exporting
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('usergradesreport', 'report_usergrades'));
echo $OUTPUT->heading(get_string('userdetails', 'report_usergrades'), 3);
$user_table = new html_table();
$user_table->head = array(get_string('field', 'report_usergrades'), get_string('value', 'report_usergrades'));
$user_table->data[] = array(get_string('username', 'report_usergrades'), $user->username);
$user_table->data[] = array(get_string('firstname', 'report_usergrades'), $user->firstname);
$user_table->data[] = array(get_string('lastname', 'report_usergrades'), $user->lastname);
$user_table->data[] = array(get_string('email', 'report_usergrades'), $user->email);
echo html_writer::table($user_table);
foreach ($courses as $course) {
echo $OUTPUT->heading($course->fullname, 3);
$quizzes = $DB->get_records('quiz', array('course' => $course->id));
if ($quizzes) {
echo $OUTPUT->heading(get_string('quizzes', 'report_usergrades'), 4);
foreach ($quizzes as $quiz) {
$quiz_questions = $DB->get_records('quiz_slots', array('quizid' => $quiz->id));
$quiz_attempts = $DB->get_records('quiz_attempts', array('quiz' => $quiz->id, 'userid' => $user->id));
if ($quiz_attempts) {
echo $OUTPUT->heading($quiz->name, 5);
foreach ($quiz_attempts as $attempt) {
echo $OUTPUT->heading(get_string('attempt', 'report_usergrades') . ' ' . $attempt->attempt, 6);
$attempt_table = new html_table();
$attempt_table->head = array(get_string('question', 'report_usergrades'), get_string('response', 'report_usergrades'), get_string('grade', 'report_usergrades'));
$question_usages = $DB->get_records('question_usages', array('id' => $attempt->uniqueid));
foreach ($question_usages as $question_usage) {
$question_attempts = $DB->get_records('question_attempts', array('questionusageid' => $question_usage->id));
foreach ($question_attempts as $question_attempt) {
$question = $questions[$question_attempt->questionid];
$response = $question_attempt->responsesummary;
$question_grades = $DB->get_records('question_attempt_steps', array('questionattemptid' => $question_attempt->id));
foreach ($question_grades as $question_grade) {
if ($question_grade->fraction) {
$attempt_table->data[] = array($question->name . ': ' . $question->questiontext, $response, $question_grade->fraction);
}
}
}
}
echo html_writer::table($attempt_table);
$total_grade = $DB->get_record('quiz_grades', array('quiz' => $quiz->id, 'userid' => $user->id));
echo $OUTPUT->heading(get_string('totalgrade', 'report_usergrades') . ': ' . $total_grade->grade . ' / ' . $quiz->grade, 6);
}
} }
} }
} }
} }
echo $OUTPUT->footer();
} }
} else { } else {
echo $OUTPUT->notification(get_string('nouserselected', 'report_usergrades'), 'notifyproblem'); echo $OUTPUT->notification(get_string('nouserselected', 'report_usergrades'), 'notifyproblem');
} }
} else { } else {
// Display form if no data is submitted // Show the form
echo $OUTPUT->header();
$mform->display(); $mform->display();
echo $OUTPUT->footer();
} }
echo $OUTPUT->footer();

View file

@ -18,4 +18,7 @@ $string['firstname'] = 'First Name';
$string['lastname'] = 'Last Name'; $string['lastname'] = 'Last Name';
$string['email'] = 'Email'; $string['email'] = 'Email';
$string['field'] = 'Field'; $string['field'] = 'Field';
$string['value'] = 'Value'; $string['value'] = 'Value';
$string['exportxls'] = 'Export to XLSX';
$string['course'] = 'Course';
$string['quiz'] = 'Quiz';

View file

@ -2,7 +2,7 @@
defined('MOODLE_INTERNAL') || die(); defined('MOODLE_INTERNAL') || die();
$plugin->component = 'report_usergrades'; $plugin->component = 'report_usergrades';
$plugin->version = 2024112101; $plugin->version = 2024112500;
$plugin->requires = 2022041900; $plugin->requires = 2022041900;
$plugin->maturity = MATURITY_ALPHA; $plugin->maturity = MATURITY_ALPHA;
$plugin->release = 'v1.0'; $plugin->release = 'v1.0';