diff --git a/essaydownload_form.php b/essaydownload_form.php index 35b5f1f..e7f5681 100644 --- a/essaydownload_form.php +++ b/essaydownload_form.php @@ -87,6 +87,17 @@ protected function standard_preference_fields(MoodleQuickForm $mform) { $mform->addElement('advcheckbox', 'responsetext', get_string('includeresponsetext', 'quiz_essaydownload')); $mform->addElement('advcheckbox', 'attachments', get_string('includeattachments', 'quiz_essaydownload')); + $mform->addElement( + 'select', + 'nameordering', + get_string('nameordering', 'quiz_essaydownload'), + [ + 'lastfirst' => get_string('lastfirst', 'quiz_essaydownload'), + 'firstlast' => get_string('firstlast', 'quiz_essaydownload'), + ] + ); + $mform->setType('nameordering', PARAM_ALPHA); + $mform->addElement( 'advcheckbox', 'shortennames', diff --git a/essaydownload_options.php b/essaydownload_options.php index 5ea30ac..75d1d21 100644 --- a/essaydownload_options.php +++ b/essaydownload_options.php @@ -54,12 +54,15 @@ class quiz_essaydownload_options extends quiz_essaydownload_options_parent_class /** @var bool whether to include attachments (if there are) in the archive */ public $attachments = true; - /** @var bool whether to shorten file and path names to workaround a Windows issue */ + /** @var string whether to shorten file and path names to workaround a Windows issue */ public $shortennames = false; /** @var string how to organise the sub folders in the archive (by question or by attempt) */ public $groupby = 'byattempt'; + /** @var string whether to have the last name or the first name first */ + public $nameordering = 'lastfirst'; + /** * Constructor * @@ -86,6 +89,7 @@ public function get_initial_form_data() { $toform->attachments = $this->attachments; $toform->shortennames = $this->shortennames; $toform->groupby = $this->groupby; + $toform->nameordering = $this->nameordering; return $toform; } @@ -101,6 +105,7 @@ public function setup_from_form_data($fromform): void { $this->attachments = $fromform->attachments; $this->shortennames = $fromform->shortennames; $this->groupby = $fromform->groupby; + $this->nameordering = $fromform->nameordering; } /** @@ -112,6 +117,7 @@ public function setup_from_params() { $this->attachments = optional_param('attachments', $this->attachments, PARAM_BOOL); $this->shortennames = optional_param('shortennames', $this->shortennames, PARAM_BOOL); $this->groupby = optional_param('groupby', $this->groupby, PARAM_ALPHA); + $this->nameordering = optional_param('nameordering', $this->nameordering, PARAM_ALPHA); } /** diff --git a/lang/en/quiz_essaydownload.php b/lang/en/quiz_essaydownload.php index 03c91e6..a21ac1c 100644 --- a/lang/en/quiz_essaydownload.php +++ b/lang/en/quiz_essaydownload.php @@ -29,11 +29,14 @@ $string['errorfilename'] = 'error-{$a}.txt'; $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['firstlast'] = 'First name - Last name'; $string['groupby'] = 'Group by'; $string['groupby_help'] = 'The archive can be structured by question or by attempt:'; $string['includeattachments'] = 'Include attachments, if there are any'; $string['includequestiontext'] = 'Include question text'; $string['includeresponsetext'] = 'Include response text'; +$string['lastfirst'] = 'Last name - First name'; +$string['nameordering'] = 'Name format'; $string['noessayquestion'] = 'This quiz does not contain any essay questions.'; $string['nothingtodownload'] = 'Nothing to download'; $string['options'] = 'Options'; diff --git a/report.php b/report.php index dbcaa31..b57053b 100644 --- a/report.php +++ b/report.php @@ -250,8 +250,15 @@ public function get_attempts_and_names(sql_join $joins): array { $result->firstname = substr($result->firstname, 0, 40); } + // The user can choose whether to start with the first name or the last name. + if ($this->options->nameordering === 'firstlast') { + $name = $result->firstname . '_' . $result->lastname; + } else { + $name = $result->lastname . '_' . $result->firstname; + } + // Build the path for this attempt: __. - $path = $result->lastname . '_' . $result->firstname . '_' . $result->attemptid; + $path = $name . '_' . $result->attemptid; $path = $path . '_' . date('Ymd_His', $result->timefinish); $path = self::clean_filename($path); diff --git a/tests/report_test.php b/tests/report_test.php index 12fc025..62558b6 100644 --- a/tests/report_test.php +++ b/tests/report_test.php @@ -174,6 +174,63 @@ public function test_long_names_being_shortened(): void { } } + public function test_custom_name_order(): void { + $this->resetAfterTest(); + + // Create a course and a quiz with an essay question. + $generator = $this->getDataGenerator(); + $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); + $course = $generator->create_course(); + $quiz = $this->create_test_quiz($course); + $quiz->name = 'ThisQuizHasAnExtremelyLongTitleBecauseLongTitlesAreJustSoCoolToHave'; + quiz_essaydownload_test_helper::add_essay_question($questiongenerator, $quiz); + + // Add a student and an attempt. + $student = \phpunit_util::get_data_generator()->create_user(['firstname' => 'First', 'lastname' => 'Last']); + \phpunit_util::get_data_generator()->enrol_user($student->id, $course->id, 'student'); + $attempt = $this->attempt_quiz($quiz, $student); + + $cm = get_coursemodule_from_id('quiz', $quiz->cmid); + $report = new quiz_essaydownload_report(); + + list($currentgroup, $allstudentjoins, $groupstudentjoins, $allowedjoins) = + $report->init('essaydownload', 'quiz_essaydownload_form', $quiz, $cm, $course); + + // Use reflection to force other name format. + $reflectedreport = new \ReflectionClass($report); + $reflectedoptions = $reflectedreport->getProperty('options'); + $reflectedoptions->setAccessible(true); + $options = new quiz_essaydownload_options('essaydownload', $quiz, $cm, $course); + $options->nameordering = 'firstlast'; + $reflectedoptions->setValue($report, $options); + + // Fetch the attemps using the report's API. + $fetchedattempts = $report->get_attempts_and_names($groupstudentjoins); + + // There should be exactly one attempt. + self::assertCount(1, $fetchedattempts); + + $i = 0; + foreach ($fetchedattempts as $fetchedid => $fetchedname) { + // The attempt is stored in a somewhat obscure way. + $attemptobj = $attempt[2]->get_attempt(); + + $id = $attemptobj->id; + self::assertEquals($id, $fetchedid); + + $firstname = clean_filename(str_replace(' ', '_', $student->firstname)); + $lastname = clean_filename(str_replace(' ', '_', $student->lastname)); + + $name = $firstname . '_' . $lastname . '_' . $id . '_' . date('Ymd_His', $attemptobj->timefinish); + + // We will not compare the minutes and seconds, because there might be a small difference and + // we don't really care. If the timestamp is correct up to the hours, we can safely assume the + // conversion worked. + self::assertStringStartsWith(substr($name, 0, -4), $fetchedname); + $i++; + } + } + public function test_get_attempts_and_names_without_groups(): void { $this->resetAfterTest();