Skip to content

Commit

Permalink
Add support for PDF export of responses (#16)
Browse files Browse the repository at this point in the history
* revamp settings form
* allow exporting formatted responses to PDF
* testing
  • Loading branch information
PhilippImhof authored Sep 20, 2024
1 parent b3ed3db commit 1485bb8
Show file tree
Hide file tree
Showing 6 changed files with 684 additions and 77 deletions.
131 changes: 120 additions & 11 deletions essaydownload_form.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,13 @@ class quiz_essaydownload_form extends moodleform {
public function definition() {
$mform = $this->_form;

$mform->addElement('header', 'preferencespage', get_string('options', 'quiz_essaydownload'));
$mform->addElement('header', 'generaloptions', get_string('generaloptions', 'quiz_essaydownload'));
$this->standard_preference_fields($mform);

$mform->addElement('header', 'pdfoptions', get_string('pdfoptions', 'quiz_essaydownload'));
$this->pdf_layout_fields($mform);
$mform->closeHeaderBefore('download');

$mform->addElement('submit', 'download', get_string('download'));
}

Expand All @@ -78,15 +83,6 @@ protected function standard_preference_fields(MoodleQuickForm $mform) {
$mform->setType('groupby', PARAM_ALPHA);
$mform->addHelpButton('groupby', 'groupby', 'quiz_essaydownload');

$mform->addElement(
'advcheckbox',
'questiontext',
get_string('whattoinclude', 'quiz_essaydownload'),
get_string('includequestiontext', 'quiz_essaydownload')
);
$mform->addElement('advcheckbox', 'responsetext', get_string('includeresponsetext', 'quiz_essaydownload'));
$mform->addElement('advcheckbox', 'attachments', get_string('includeattachments', 'quiz_essaydownload'));

$mform->addElement(
'select',
'nameordering',
Expand All @@ -98,12 +94,125 @@ protected function standard_preference_fields(MoodleQuickForm $mform) {
);
$mform->setType('nameordering', PARAM_ALPHA);

$mform->addElement(
'advcheckbox',
'attachments',
get_string('attachments', 'quiz_essaydownload'),
get_string('includeattachments', 'quiz_essaydownload')
);
$mform->addHelpButton('attachments', 'includeattachments', 'quiz_essaydownload');

$mform->addElement(
'advcheckbox',
'questiontext',
get_string('questiontext', 'question'),
get_string('includequestiontext', 'quiz_essaydownload')
);
$mform->addHelpButton('questiontext', 'includequestiontext', 'quiz_essaydownload');

$mform->addElement('select', 'fileformat', get_string('fileformat', 'quiz_essaydownload'), [
'txt' => get_string('fileformattxt', 'quiz_essaydownload'),
'pdf' => get_string('fileformatpdf', 'quiz_essaydownload'),
]);
$mform->setType('fileformat', PARAM_ALPHA);
$mform->setDefault('fileformat', 'pdf');
$mform->addHelpButton('fileformat', 'fileformat', 'quiz_essaydownload');

$mform->addElement('select', 'source', get_string('source', 'quiz_essaydownload'), [
'plain' => get_string('sourcesummary', 'quiz_essaydownload'),
'html' => get_string('sourceoriginal', 'quiz_essaydownload'),
]);
$mform->disabledIf('source', 'fileformat', 'neq', 'pdf');
$mform->setType('source', PARAM_ALPHA);
$mform->setDefault('source', 'html');
$mform->addHelpButton('source', 'source', 'quiz_essaydownload');

$mform->addElement(
'advcheckbox',
'shortennames',
get_string('additionalsettings', 'quiz_essaydownload'),
get_string('compatibility', 'quiz_essaydownload'),
get_string('shortennames', 'quiz_essaydownload')
);
$mform->addHelpButton('shortennames', 'shortennames', 'quiz_essaydownload');
}

/**
* Fields to configure the PDF layout.
*
* @param MoodleQuickForm $mform the form
* @return void
*/
protected function pdf_layout_fields(MoodleQuickForm $mform) {
$mform->addElement('select', 'page', get_string('page', 'quiz_essaydownload'), [
'a4' => get_string('pagea4', 'quiz_essaydownload'),
'letter' => get_string('pageletter', 'quiz_essaydownload'),
]);
$mform->setType('page', PARAM_ALPHA);
$mform->setDefault('page', 'a4');
$mform->disabledIf('page', 'fileformat', 'neq', 'pdf');

$margingroup = [];
$margingroup[] = $mform->createElement('text', 'marginleft', '', ['size' => 3]);
$mform->setType('marginleft', PARAM_INT);
$margingroup[] = $mform->createElement('text', 'marginright', '', ['size' => 3]);
$mform->setType('marginright', PARAM_INT);
$margingroup[] = $mform->createElement('text', 'margintop', '', ['size' => 3]);
$mform->setType('margintop', PARAM_INT);
$margingroup[] = $mform->createElement('text', 'marginbottom', '', ['size' => 3]);
$mform->setType('marginbottom', PARAM_INT);
$mform->addGroup($margingroup, 'margingroup', get_string('margins', 'quiz_essaydownload'), ' ', false);
$mform->disabledIf('margingroup', 'fileformat', 'neq', 'pdf');

$mform->addElement('select', 'linespacing', get_string('linespacing', 'quiz_essaydownload'), [
'1' => get_string('linesingle', 'quiz_essaydownload'),
'1.5' => get_string('lineoneandhalf', 'quiz_essaydownload'),
'2' => get_string('linedouble', 'quiz_essaydownload'),
]);
$mform->setType('linespacing', PARAM_FLOAT);
$mform->disabledIf('linespacing', 'fileformat', 'neq', 'pdf');

$mform->addElement('select', 'font', get_string('font', 'quiz_essaydownload'), [
'sans' => get_string('fontsans', 'quiz_essaydownload'),
'serif' => get_string('fontserif', 'quiz_essaydownload'),
'mono' => get_string('fontmono', 'quiz_essaydownload'),
]);
$mform->setType('font', PARAM_ALPHA);
$mform->setDefault('font', 'serif');
$mform->disabledIf('font', 'fileformat', 'neq', 'pdf');
$mform->addHelpButton('font', 'font', 'quiz_essaydownload');

$mform->addElement('text', 'fontsize', get_string('fontsize', 'quiz_essaydownload'), ['size' => 3]);
$mform->setType('fontsize', PARAM_INT);
$mform->disabledIf('fontsize', 'fileformat', 'neq', 'pdf');
$mform->addHelpButton('fontsize', 'fontsize', 'quiz_essaydownload');
}

/**
* Validation of our settings form, e. g. font size or page margins.
*
* @param array $data submitted data in form ['fieldname' => value]
* @param array $files array of uploaded files ['element_name' => tmp_file_path]
* @return array errors in form ['element_name' => 'error message'] or [] if no errors
*/
public function validation($data, $files) {
$errors = parent::validation($data, $files);

// No further validation to be done if using plain text format.
if ($data['fileformat'] === 'txt') {
return $errors;
}

$margins = [$data['marginleft'], $data['marginright'], $data['margintop'], $data['marginbottom']];
foreach ($margins as $margin) {
if ($margin > 80 || $margin < 0) {
$errors['margingroup'] = get_string('errormargin', 'quiz_essaydownload');
}
}

if ($data['fontsize'] > 50 || $data['fontsize'] < 6) {
$errors['fontsize'] = get_string('errorfontsize', 'quiz_essaydownload');
}

return $errors;
}
}
95 changes: 75 additions & 20 deletions essaydownload_options.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

use mod_quiz\local\reports\attempts_report_options;

defined('MOODLE_INTERNAL') || die();

// This work-around is required until Moodle 4.2 is the lowest version we support.
Expand All @@ -45,24 +43,51 @@ class_alias('\mod_quiz_attempts_report_options', '\quiz_essaydownload_options_pa
*/
class quiz_essaydownload_options extends quiz_essaydownload_options_parent_class_alias {

/** @var bool whether to include the text response files in the archive */
public $responsetext = true;

/** @var bool whether to include the question text in the archive */
public $questiontext = true;

/** @var bool whether to include attachments (if there are) in the archive */
public $attachments = true;

/** @var string whether to shorten file and path names to workaround a Windows issue */
public $shortennames = false;
/** @var string file format TXT or PDF */
public $fileformat = 'pdf';

/** @var string base font family for PDF export */
public $font = 'sansserif';

/** @var int font size for PDF export */
public $fontsize = 12;

/** @var string how to organise the sub folders in the archive (by question or by attempt) */
public $groupby = 'byattempt';

/** @var float line spacing for PDF export */
public $linespacing = 1;

/** @var int bottom margin for PDF export */
public $marginbottom = 20;

/** @var int left margin for PDF export */
public $marginleft = 20;

/** @var int right margin for PDF export */
public $marginright = 20;

/** @var int top margin for PDF export */
public $margintop = 20;

/** @var string whether to have the last name or the first name first */
public $nameordering = 'lastfirst';

/** @var string page format for PDF export */
public $pageformat = 'a4';

/** @var bool whether to include the question text in the archive */
public $questiontext = true;

/** @var bool whether to shorten file and path names to workaround a Windows issue */
public $shortennames = false;

/** @var string which source to use: plain-text summary or original HTML text */
public $source = 'html';

/**
* Constructor
*
Expand All @@ -84,12 +109,21 @@ public function __construct($mode, $quiz, $cm, $course) {
public function get_initial_form_data() {
$toform = new stdClass();

$toform->responsetext = $this->responsetext;
$toform->questiontext = $this->questiontext;
$toform->attachments = $this->attachments;
$toform->shortennames = $this->shortennames;
$toform->fileformat = $this->fileformat;
$toform->font = $this->font;
$toform->fontsize = $this->fontsize;
$toform->groupby = $this->groupby;
$toform->linespacing = $this->linespacing;
$toform->marginbottom = $this->marginbottom;
$toform->marginleft = $this->marginleft;
$toform->marginright = $this->marginright;
$toform->margintop = $this->margintop;
$toform->nameordering = $this->nameordering;
$toform->pageformat = $this->pageformat;
$toform->questiontext = $this->questiontext;
$toform->shortennames = $this->shortennames;
$toform->source = $this->source;

return $toform;
}
Expand All @@ -100,24 +134,42 @@ public function get_initial_form_data() {
* @param object $fromform data from the settings form
*/
public function setup_from_form_data($fromform): void {
$this->responsetext = $fromform->responsetext;
$this->questiontext = $fromform->questiontext;
$this->attachments = $fromform->attachments;
$this->shortennames = $fromform->shortennames;
$this->fileformat = $fromform->fileformat;
$this->font = $fromform->font ?? '';
$this->fontsize = $fromform->fontsize ?? '';
$this->groupby = $fromform->groupby;
$this->linespacing = $fromform->linespacing ?? '';
$this->marginbottom = $fromform->marginbottom ?? '';
$this->marginleft = $fromform->marginleft ?? '';
$this->marginright = $fromform->marginright ?? '';
$this->margintop = $fromform->margintop ?? '';
$this->nameordering = $fromform->nameordering;
$this->pageformat = $fromform->pageformat ?? '';
$this->questiontext = $fromform->questiontext;
$this->shortennames = $fromform->shortennames;
$this->source = $fromform->source ?? '';
}

/**
* Set the fields of this object from the URL parameters.
*/
public function setup_from_params() {
$this->responsetext = optional_param('responsetext', $this->responsetext, PARAM_BOOL);
$this->questiontext = optional_param('questiontext', $this->questiontext, PARAM_BOOL);
$this->attachments = optional_param('attachments', $this->attachments, PARAM_BOOL);
$this->shortennames = optional_param('shortennames', $this->shortennames, PARAM_BOOL);
$this->fileformat = optional_param('fileformat', $this->fileformat, PARAM_ALPHA);
$this->font = optional_param('font', $this->font, PARAM_ALPHA);
$this->fontsize = optional_param('fontsize', $this->fontsize, PARAM_INT);
$this->groupby = optional_param('groupby', $this->groupby, PARAM_ALPHA);
$this->linespacing = optional_param('linespacing', $this->linespacing, PARAM_FLOAT);
$this->marginbottom = optional_param('marginbottom', $this->marginbottom, PARAM_INT);
$this->marginleft = optional_param('marginleft', $this->marginleft, PARAM_INT);
$this->marginright = optional_param('marginright', $this->marginright, PARAM_INT);
$this->margintop = optional_param('margintop', $this->margintop, PARAM_INT);
$this->nameordering = optional_param('nameordering', $this->nameordering, PARAM_ALPHA);
$this->pageformat = optional_param('pageformat', $this->pageformat, PARAM_ALPHA);
$this->questiontext = optional_param('questiontext', $this->questiontext, PARAM_BOOL);
$this->shortennames = optional_param('shortennames', $this->shortennames, PARAM_BOOL);
$this->source = optional_param('source', $this->source, PARAM_ALPHA);
}

/**
Expand All @@ -135,8 +187,11 @@ public function update_user_preferences() {
}

/**
* Override parent method, because our settings cannot be incompatible.
* Deal with conflicting options, e.g. user requesting TXT output, but HTML source.
*/
public function resolve_dependencies() {
if ($this->fileformat === 'txt') {
$this->source = 'plain';
}
}
}
45 changes: 37 additions & 8 deletions lang/en/quiz_essaydownload.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,55 @@
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

$string['additionalsettings'] = 'Additional settings';
$string['attachments'] = 'Attachments';
$string['byattempt'] = 'Attempt';
$string['byquestion'] = 'Question';
$string['compatibility'] = 'Compatibility setting';
$string['errorfilename'] = 'error-{$a}.txt';
$string['errorfontsize'] = 'Font size should be an integer between 6 and 50.';
$string['errormargin'] = 'All page margins must be integers between 0 and 80.';
$string['errormessage'] = 'An internal error occurred. The archive is probably incomplete. Please contact the developers of the Essay responses downloader plugin (quiz_essaydownload) and send them the details below:';
$string['essaydownload'] = 'Download essay responses';
$string['fileformat'] = 'File format';
$string['fileformat_help'] = 'You can choose between two formats:<ul><li>Portable Document Format (PDF) allows you to directly obtain a formatted document for every answer, ready for on-screen correction or printing.</li><li>Plain-text (TXT) export is faster and results in a smaller archive, which might be important for large-scale quizzes. Those files can be read with any text editor, or opened with word processor for further formatting. You might also want to choose this format, if you have some custom script to automatically convert or treat student answers in a certain way.</li></ul>';
$string['fileformatpdf'] = 'Portable Document Format (PDF)';
$string['fileformattxt'] = 'Plain-text (TXT)';
$string['firstlast'] = 'First name - Last name';
$string['font'] = 'Font';
$string['font_help'] = 'Note that when using the original HTML formatted text, the actual font may still be different, according to the formatting.<br><br>When using the plain-text summary, you might want to use a monospaced font.';
$string['fontmono'] = 'Monospaced';
$string['fontsans'] = 'Sans-serif';
$string['fontserif'] = 'Serif';
$string['fontsize'] = 'Font size (points)';
$string['fontsize_help'] = 'Note that when using the original HTML formatted text, the actual font size may still be different, according to the formatting';
$string['generaloptions'] = 'General options';
$string['groupby'] = 'Group by';
$string['groupby_help'] = 'The archive can be structured by question or by attempt:<ul><li>If you group by question, the archive will have a folder for every question. Inside each folder, you will have a folder for every attempt.</li><li>If you group by attempt, the archive will have a folder for every attempt. Inside each folder, you will have a folder for every question.</li></ul>';
$string['includeattachments'] = 'Include attachments, if there are any';
$string['includequestiontext'] = 'Include question text';
$string['includeresponsetext'] = 'Include response text';
$string['includeattachments'] = 'Also download possible attachments included in a student\'s answer.';
$string['includeattachments_help'] = 'Any attachment is provided as-is. Please note that attachments might contain malware.';
$string['includequestiontext'] = 'Also include question text.';
$string['includequestiontext_help'] = 'Including the question text might be useful if your quiz uses random questions.';
$string['lastfirst'] = 'Last name - First name';
$string['linedouble'] = 'Double';
$string['lineoneandhalf'] = '1.5 lines';
$string['linesingle'] = 'Single';
$string['linespacing'] = 'Line spacing';
$string['margins'] = 'Page margins (mm): left, right, top, bottom';
$string['nameordering'] = 'Name format';
$string['noessayquestion'] = 'This quiz does not contain any essay questions.';
$string['nothingtodownload'] = 'Nothing to download';
$string['options'] = 'Options';
$string['page'] = 'Page format';
$string['pagea4'] = 'A4';
$string['pageletter'] = 'Letter';
$string['pdfoptions'] = 'PDF settings';
$string['plugindescription'] = 'Download text answers and attachment files submitted in response to essay questions in a quiz.';
$string['pluginname'] = 'Essay responses downloader plugin (quiz_essaydownload)';
$string['presentedto'] = 'Presented to: {$a}';
$string['privacy:metadata'] = 'The quiz essay download plugin does not store any personal data about any user.';
$string['shortennames'] = 'Shorten archive name and subfolder names';
$string['shortennames_help'] = 'If the total path name of an extracted file is larger than 260 characters, this may cause problems with Windows\' built-in extraction tool. In this case, activating this checkbox may help. It might, however, make it more difficult to identify your students, if they have very long names.';
$string['whattoinclude'] = 'What to include';
$string['response'] = 'Response';
$string['shortennames'] = 'Shorten archive name and subfolder names.';
$string['shortennames_help'] = 'If the total path name of an extracted file is longer than 260 characters, this may cause problems with Windows\' built-in extraction tool. In this case, activating this checkbox may help. It might, however, make it more difficult to identify your students, if they have very long names.';
$string['source'] = 'Text source to use';
$string['source_help'] = 'If the question text and/or the student\'s response is written in HTML format, Moodle will automatically generate a plain-text summary of the formatted text. That summary will have all HTML tags removed and some basic formatting applied (e. g. headings and bold font transformed to ALL CAPS).<br><br>When generating PDF files, you can choose whether you want to use that summary or the original question text / student answer with its formatting. If you choose the summary, you should probably use a monospaced font as well.<br><br>Note that you cannot use the formatted original text when generating TXT files. Also note that the setting will not have any effect if the student was asked to write their answer in non-HTML format, e. g. plain-text.';
$string['sourceoriginal'] = 'Original HTML formatted text';
$string['sourcesummary'] = 'Plain-text summary';
Loading

0 comments on commit 1485bb8

Please sign in to comment.