From b9900c2b4526bb19e4d03cb0575f7f19ac8fe79e Mon Sep 17 00:00:00 2001 From: Ferran Recio Date: Wed, 7 Sep 2022 16:04:39 +0200 Subject: [PATCH] MDL-64231 mod_assign: download submissions with group folder --- mod/assign/classes/downloader.php | 289 +++++++++++ mod/assign/locallib.php | 187 +------- mod/assign/tests/downloader_test.php | 447 ++++++++++++++++++ .../tests/fixtures/submissionsample01.txt | 1 + .../tests/fixtures/submissionsample02.txt | 1 + 5 files changed, 762 insertions(+), 163 deletions(-) create mode 100644 mod/assign/classes/downloader.php create mode 100644 mod/assign/tests/downloader_test.php create mode 100644 mod/assign/tests/fixtures/submissionsample01.txt create mode 100644 mod/assign/tests/fixtures/submissionsample02.txt diff --git a/mod/assign/classes/downloader.php b/mod/assign/classes/downloader.php new file mode 100644 index 00000000000..dc0d7145e1e --- /dev/null +++ b/mod/assign/classes/downloader.php @@ -0,0 +1,289 @@ +. + +namespace mod_assign; + +use assign; +use core_php_time_limit; +use mod_assign\event\all_submissions_downloaded; +use core\session\manager as sessionmanager; +use core_files\archive_writer; +use stdClass; +use assign_plugin; +use stored_file; + +/** + * Class to download user submissions. + * + * @package mod_assign + * @copyright 2022 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class downloader { + /** @var assign the module manager instance. */ + private $manager; + + /** @var stdClass the assign instance record. */ + private $instance; + + /** @var array|null the selected user ids, if any. */ + private $userids = null; + + /** @var int $groupmode the activity group mode. */ + private $groupmode = ''; + + /** @var int $groupid the exported groupid. */ + private $groupid = 0; + + /** @var array $filesforzipping the files to zipo (path => file) */ + protected $filesforzipping; + + /** @var array $prefixes all loaded the student prefixes. + * + * A prefix will be converted into a file prefix or a folder name (depends on downloadasfolders). + */ + private $prefixes; + + /** @var int $downloadasfolders the files to zipo (path => file) */ + private $downloadasfolders; + + /** + * Class constructor. + * + * @param assign $manager the instance manager + * @param array|null $userids the user ids to download. + */ + public function __construct(assign $manager, ?array $userids = null) { + $this->manager = $manager; + $this->userids = $userids; + $this->instance = $manager->get_instance(); + + $this->downloadasfolders = get_user_preferences('assign_downloadasfolders', 1); + + $cm = $manager->get_course_module(); + $this->groupmode = groups_get_activity_groupmode($cm); + if ($this->groupmode) { + $this->groupid = groups_get_activity_group($cm, true); + } + } + + /** + * Load the filelist. + * + * @return bool true if there are some files to zip. + */ + public function load_filelist(): bool { + $manager = $this->manager; + $groupid = $this->groupid; + + // Increase the server timeout to handle the creation and sending of large zip files. + core_php_time_limit::raise(); + + $manager->require_view_grades(); + + // Load all users with submit. + $students = get_enrolled_users( + $manager->get_context(), + "mod/assign:submit", + 0, + 'u.*', + null, + 0, + 0, + $manager->show_only_active_users() + ); + + // Build a list of files to zip. + $this->filesforzipping = []; + + // Get all the files for each student. + foreach ($students as $student) { + // Download all assigments submission or only selected users. + if ($this->userids && !in_array($student->id, $this->userids)) { + continue; + } + if (!groups_is_member($groupid, $student->id) && $this->groupmode && $groupid) { + continue; + } + $this->load_student_filelist($student); + } + return !empty($this->filesforzipping); + } + + /** + * Load an individual student filelist. + * + * @param stdClass $student the user record + */ + private function load_student_filelist(stdClass $student) { + $submission = $this->get_student_submission($student); + if (!$submission) { + return; + } + $prefix = $this->get_student_prefix($student); + if (isset($this->prefixes[$prefix])) { + // We already send that file (in group mode). + return; + } + $this->prefixes[$prefix] = $student->id; + + foreach ($this->manager->get_submission_plugins() as $plugin) { + if (!$plugin->is_enabled() || !$plugin->is_visible()) { + continue; + } + $this->load_submissionplugin_filelist($student, $plugin, $submission, $prefix); + } + } + + /** + * Return the student submission if any. + * + * @param stdClass $student the user record + * @return stdClass|null the user submission or null if none + */ + private function get_student_submission(stdClass $student): ?stdClass { + if ($this->instance->teamsubmission) { + $submission = $this->manager->get_group_submission($student->id, 0, false); + } else { + $submission = $this->manager->get_user_submission($student->id, false); + } + return $submission ?: null; + } + + /** + * Return the file prefix used to generate the each submission folder or file. + * + * @param stdClass $student the user record + * @return string the submission prefix + */ + private function get_student_prefix(stdClass $student): string { + $manager = $this->manager; + + // Team submissions are by group, not by student. + if ($this->instance->teamsubmission) { + $submissiongroup = $manager->get_submission_group($student->id); + if ($submissiongroup) { + $groupname = $submissiongroup->name; + $groupinfo = '_' . $submissiongroup->id; + } else { + $groupname = get_string('defaultteam', 'mod_assign'); + $groupinfo = ''; + } + $prefix = str_replace('_', ' ', $groupname); + return clean_filename($prefix . $groupinfo); + } + // Individual submissions are by user. + if ($manager->is_blind_marking()) { + $fullname = get_string('participant', 'mod_assign'); + } else { + $fullname = fullname($student, has_capability('moodle/site:viewfullnames', $manager->get_context())); + } + $prefix = str_replace('_', ' ', $fullname); + $prefix = clean_filename($prefix . '_' . $manager->get_uniqueid_for_user($student->id)); + return $prefix; + } + + /** + * Load a submission plugin filelist for a specific user. + * + * @param stdClass $student the user record + * @param assign_plugin $plugin the submission plugin instance + * @param stdClass $submission the submission object + * @param string $prefix the files prefix + */ + private function load_submissionplugin_filelist( + stdClass $student, + assign_plugin $plugin, + stdClass $submission, + string $prefix + ) { + $subtype = $plugin->get_subtype(); + $type = $plugin->get_type(); + + if ($this->downloadasfolders) { + // Create a folder for each user for each assignment plugin. + // This is the default behavior for version of Moodle >= 3.1. + $submission->exportfullpath = true; + $pluginfiles = $plugin->get_files($submission, $student); + foreach ($pluginfiles as $zipfilepath => $file) { + $zipfilename = basename($zipfilepath); + $prefixedfilename = clean_filename($prefix . '_' . $subtype . '_' . $type); + if ($type == 'file') { + $pathfilename = $prefixedfilename . $file->get_filepath() . $zipfilename; + } else { + $pathfilename = $prefixedfilename . '/' . $zipfilename; + } + $pathfilename = clean_param($pathfilename, PARAM_PATH); + $this->filesforzipping[$pathfilename] = $file; + } + } else { + // Create a single folder for all users of all assignment plugins. + // This was the default behavior for version of Moodle < 3.1. + $submission->exportfullpath = false; + $pluginfiles = $plugin->get_files($submission, $student); + foreach ($pluginfiles as $zipfilename => $file) { + $prefixedfilename = clean_filename($prefix . '_' . $subtype . '_' . $type . '_' . $zipfilename); + $this->filesforzipping[$prefixedfilename] = $file; + } + } + } + + /** + * Download the exported zip. + * + * This method will terminate the current script when the file is send. + */ + public function download_zip() { + $filename = $this->get_zip_filename(); + all_submissions_downloaded::create_from_assign($this->manager)->trigger(); + sessionmanager::write_close(); + $zipwriter = archive_writer::get_stream_writer($filename, archive_writer::ZIP_WRITER); + + // Stream the files into the zip. + foreach ($this->filesforzipping as $pathinzip => $file) { + if ($file instanceof stored_file) { + // Most of cases are stored_file. + $zipwriter->add_file_from_stored_file($pathinzip, $file); + } else if (is_array($file)) { + // Save $file as contents, from onlinetext subplugin. + $content = reset($file); + $zipwriter->add_file_from_string($pathinzip, $content); + } + } + // Finish the archive. + $zipwriter->finish(); + exit(); + } + + /** + * Generate the zip filename. + * + * @return string the zip filename + */ + private function get_zip_filename(): string { + $manager = $this->manager; + $filenameparts = [ + $manager->get_course()->shortname, + $this->instance->name, + ]; + if (!empty($this->groupid)) { + $filenameparts[] = groups_get_group_name($this->groupid); + } + $filenameparts[] = $manager->get_course_module()->id; + + return clean_filename(implode('-', $filenameparts). '.zip'); + } +} diff --git a/mod/assign/locallib.php b/mod/assign/locallib.php index b5eb7ce2b98..59ac73e4e7a 100644 --- a/mod/assign/locallib.php +++ b/mod/assign/locallib.php @@ -98,6 +98,7 @@ use \mod_assign\output\grading_app; use \mod_assign\output\assign_header; use \mod_assign\output\assign_submission_status; use mod_assign\output\timelimit_panel; +use mod_assign\downloader; /** * Standard base class for mod_assign (assignment types). @@ -3103,7 +3104,7 @@ class assign { * @param bool $create If set to true a new submission object will be created in the database * with the status set to "new". * @param int $attemptnumber - -1 means the latest attempt - * @return stdClass The submission + * @return stdClass|false The submission */ public function get_group_submission($userid, $groupid, $create, $attemptnumber=-1) { global $DB; @@ -3661,170 +3662,30 @@ class assign { /** * Download a zip file of all assignment submissions. * - * @param array $userids Array of user ids to download assignment submissions in a zip file + * @param array|null $userids Array of user ids to download assignment submissions in a zip file * @return string - If an error occurs, this will contain the error page. */ - protected function download_submissions($userids = false) { - global $CFG, $DB; - - // More efficient to load this here. - require_once($CFG->libdir.'/filelib.php'); - - // Increase the server timeout to handle the creation and sending of large zip files. - core_php_time_limit::raise(); - - $this->require_view_grades(); - - // Load all users with submit. - $students = get_enrolled_users($this->context, "mod/assign:submit", null, 'u.*', null, null, null, - $this->show_only_active_users()); - - // Build a list of files to zip. - $filesforzipping = array(); - $fs = get_file_storage(); - - $groupmode = groups_get_activity_groupmode($this->get_course_module()); - // All users. - $groupid = 0; - $groupname = ''; - if ($groupmode) { - $groupid = groups_get_activity_group($this->get_course_module(), true); - if (!empty($groupid)) { - $groupname = groups_get_group_name($groupid) . '-'; - } + protected function download_submissions($userids = null) { + $downloader = new downloader($this, $userids ?: null); + if ($downloader->load_filelist()) { + $downloader->download_zip(); } - - // Construct the zip file name. - $filename = clean_filename($this->get_course()->shortname . '-' . - $this->get_instance()->name . '-' . - $groupname.$this->get_course_module()->id . '.zip'); - - // Get all the files for each student. - foreach ($students as $student) { - $userid = $student->id; - // Download all assigments submission or only selected users. - if ($userids and !in_array($userid, $userids)) { - continue; - } - - if ((groups_is_member($groupid, $userid) or !$groupmode or !$groupid)) { - // Get the plugins to add their own files to the zip. - - $submissiongroup = false; - $groupname = ''; - if ($this->get_instance()->teamsubmission) { - $submission = $this->get_group_submission($userid, 0, false); - $submissiongroup = $this->get_submission_group($userid); - if ($submissiongroup) { - $groupname = $submissiongroup->name . '-'; - } else { - $groupname = get_string('defaultteam', 'assign') . '-'; - } - } else { - $submission = $this->get_user_submission($userid, false); - } - - if ($this->is_blind_marking()) { - $prefix = str_replace('_', ' ', $groupname . get_string('participant', 'assign')); - $prefix = clean_filename($prefix . '_' . $this->get_uniqueid_for_user($userid)); - } else { - $fullname = fullname($student, has_capability('moodle/site:viewfullnames', $this->get_context())); - $prefix = str_replace('_', ' ', $groupname . $fullname); - $prefix = clean_filename($prefix . '_' . $this->get_uniqueid_for_user($userid)); - } - - if ($submission) { - $downloadasfolders = get_user_preferences('assign_downloadasfolders', 1); - foreach ($this->submissionplugins as $plugin) { - if ($plugin->is_enabled() && $plugin->is_visible()) { - if ($downloadasfolders) { - // Create a folder for each user for each assignment plugin. - // This is the default behavior for version of Moodle >= 3.1. - $submission->exportfullpath = true; - $pluginfiles = $plugin->get_files($submission, $student); - foreach ($pluginfiles as $zipfilepath => $file) { - $subtype = $plugin->get_subtype(); - $type = $plugin->get_type(); - $zipfilename = basename($zipfilepath); - $prefixedfilename = clean_filename($prefix . - '_' . - $subtype . - '_' . - $type . - '_'); - if ($type == 'file') { - $pathfilename = $prefixedfilename . $file->get_filepath() . $zipfilename; - } else if ($type == 'onlinetext') { - $pathfilename = $prefixedfilename . '/' . $zipfilename; - } else { - $pathfilename = $prefixedfilename . '/' . $zipfilename; - } - $pathfilename = clean_param($pathfilename, PARAM_PATH); - $filesforzipping[$pathfilename] = $file; - } - } else { - // Create a single folder for all users of all assignment plugins. - // This was the default behavior for version of Moodle < 3.1. - $submission->exportfullpath = false; - $pluginfiles = $plugin->get_files($submission, $student); - foreach ($pluginfiles as $zipfilename => $file) { - $subtype = $plugin->get_subtype(); - $type = $plugin->get_type(); - $prefixedfilename = clean_filename($prefix . - '_' . - $subtype . - '_' . - $type . - '_' . - $zipfilename); - $filesforzipping[$prefixedfilename] = $file; - } - } - } - } - } - } - } - $result = ''; - if (count($filesforzipping) == 0) { - $header = new assign_header($this->get_instance(), - $this->get_context(), - '', - $this->get_course_module()->id, - get_string('downloadall', 'assign')); - $result .= $this->get_renderer()->render($header); - $result .= $this->get_renderer()->notification(get_string('nosubmission', 'assign')); - $url = new moodle_url('/mod/assign/view.php', array('id'=>$this->get_course_module()->id, - 'action'=>'grading')); - $result .= $this->get_renderer()->continue_button($url); - $result .= $this->view_footer(); - - return $result; - } - - // Log zip as downloaded. - \mod_assign\event\all_submissions_downloaded::create_from_assign($this)->trigger(); - - // Close the session. - \core\session\manager::write_close(); - - $zipwriter = \core_files\archive_writer::get_stream_writer($filename, \core_files\archive_writer::ZIP_WRITER); - - // Stream the files into the zip. - foreach ($filesforzipping as $pathinzip => $file) { - if ($file instanceof \stored_file) { - // Most of cases are \stored_file. - $zipwriter->add_file_from_stored_file($pathinzip, $file); - } else if (is_array($file)) { - // Save $file as contents, from onlinetext subplugin. - $content = reset($file); - $zipwriter->add_file_from_string($pathinzip, $content); - } - } - - // Finish the archive. - $zipwriter->finish(); - exit(); + // Show some notification if we have nothing to download. + $cm = $this->get_course_module(); + $renderer = $this->get_renderer(); + $header = new assign_header( + $this->get_instance(), + $this->get_context(), + '', + $cm->id, + get_string('downloadall', 'mod_assign') + ); + $result = $renderer->render($header); + $result .= $renderer->notification(get_string('nosubmission', 'mod_assign')); + $url = new moodle_url('/mod/assign/view.php', ['id' => $cm->id, 'action' => 'grading']); + $result .= $renderer->continue_button($url); + $result .= $this->view_footer(); + return $result; } /** @@ -3890,7 +3751,7 @@ class assign { * @param int $userid The id of the user whose submission we want or 0 in which case USER->id is used * @param bool $create If set to true a new submission object will be created in the database with the status set to "new". * @param int $attemptnumber - -1 means the latest attempt - * @return stdClass The submission + * @return stdClass|false The submission */ public function get_user_submission($userid, $create, $attemptnumber=-1) { global $DB, $USER; diff --git a/mod/assign/tests/downloader_test.php b/mod/assign/tests/downloader_test.php new file mode 100644 index 00000000000..d7716ec5113 --- /dev/null +++ b/mod/assign/tests/downloader_test.php @@ -0,0 +1,447 @@ +. + +namespace mod_assign; + +use context_module; +use assign; + +/** + * Downloader tests class for mod_assign. + * + * @package mod_assign + * @category test + * @copyright 2022 Ferran Recio + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @coversDefaultClass \mod_assign\downloader + */ +class downloader_test extends \advanced_testcase { + /** + * Setup to ensure that fixtures are loaded. + */ + public static function setupBeforeClass(): void { + global $CFG; + require_once($CFG->dirroot . '/mod/assign/locallib.php'); + } + + /** + * Test for load_filelist method. + * + * @covers ::load_filelist + * @dataProvider load_filelist_provider + * + * @param bool $teamsubmission if the assign must have team submissions + * @param array $groupmembers the groups definition + * @param array|null $filterusers the filtered users (null for all users) + * @param bool $blindmarking if the assign has blind marking + * @param bool $downloadasfolder if the download as folder preference is set + * @param array $expected the expected file list + */ + public function test_load_filelist( + bool $teamsubmission, + array $groupmembers, + ?array $filterusers, + bool $blindmarking, + bool $downloadasfolder, + array $expected + ) { + global $CFG; + $this->resetAfterTest(); + $this->setAdminUser(); + + if (!$downloadasfolder) { + set_user_preference('assign_downloadasfolders', 0); + } + + // Create course and enrols. + $course = $this->getDataGenerator()->create_course(); + $users = [ + 'student1' => $this->getDataGenerator()->create_and_enrol($course, 'student'), + 'student2' => $this->getDataGenerator()->create_and_enrol($course, 'student'), + 'student3' => $this->getDataGenerator()->create_and_enrol($course, 'student'), + 'student4' => $this->getDataGenerator()->create_and_enrol($course, 'student'), + 'student5' => $this->getDataGenerator()->create_and_enrol($course, 'student'), + ]; + + // Generate groups. + $groups = []; + foreach ($groupmembers as $groupname => $groupusers) { + $group = $this->getDataGenerator()->create_group(['courseid' => $course->id, 'name' => $groupname]); + foreach ($groupusers as $user) { + groups_add_member($group, $users[$user]); + } + $groups[$groupname] = $group; + } + + // Create activity. + $params = [ + 'course' => $course, + 'assignsubmission_file_enabled' => 1, + 'assignsubmission_file_maxfiles' => 12, + 'assignsubmission_file_maxsizebytes' => 1024 * 1024, + ]; + if ($teamsubmission) { + $params['teamsubmission'] = 1; + $params['preventsubmissionnotingroup'] = false; + } + if ($blindmarking) { + $params['blindmarking'] = 1; + } + $activity = $this->getDataGenerator()->create_module('assign', $params); + $cm = get_coursemodule_from_id('assign', $activity->cmid, 0, false, MUST_EXIST); + $context = context_module::instance($cm->id); + + // Generate submissions. + $datagenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign'); + $files = [ + "mod/assign/tests/fixtures/submissionsample01.txt", + "mod/assign/tests/fixtures/submissionsample02.txt" + ]; + foreach ($users as $key => $user) { + if ($key == 'student5') { + continue; + } + $datagenerator->create_submission([ + 'userid' => $user->id, + 'assignid' => $cm->id, + 'file' => implode(',', $files), + ]); + } + + // Generate file list. + if ($filterusers) { + foreach ($filterusers as $key => $identifier) { + $filterusers[$key] = $users[$identifier]->id; + } + } + $manager = new assign($context, $cm, $course); + $downloader = new downloader($manager, $filterusers); + $hasfiles = $downloader->load_filelist(); + + // Expose protected filelist attribute. + $rc = new \ReflectionClass(downloader::class); + $rcp = $rc->getProperty('filesforzipping'); + $rcp->setAccessible(true); + + // Add some replacements. + $search = ['PARTICIPANT', 'DEFAULTTEAM']; + $replace = [get_string('participant', 'mod_assign'), get_string('defaultteam', 'mod_assign')]; + foreach ($users as $identifier => $user) { + $search[] = strtoupper($identifier . '.ID'); + $replace[] = $manager->get_uniqueid_for_user($user->id); + $search[] = strtoupper($identifier); + $replace[] = $this->prepare_filename_text(fullname($user)); + } + foreach ($groups as $identifier => $group) { + $search[] = strtoupper($identifier . '.ID'); + $replace[] = strtoupper($group->id); + $search[] = strtoupper($identifier); + $replace[] = $this->prepare_filename_text($group->name); + } + + // Validate values. + $filelist = $rcp->getValue($downloader); + $result = array_keys($filelist); + + $this->assertEquals($hasfiles, !empty($expected)); + $this->assertCount(count($expected), $result); + foreach ($expected as $path) { + $value = str_replace($search, $replace, $path); + $this->assertTrue(in_array($value, $result)); + } + } + + /** + * Internal helper to clean a filename text. + * + * @param string $text the text to transform + * @return string the clean string + */ + private function prepare_filename_text(string $text): string { + return clean_filename(str_replace('_', ' ', $text)); + } + + /** + * Data provider for test_load_filelist(). + * + * @return array of scenarios + */ + public function load_filelist_provider(): array { + $downloadasfoldertests = $this->load_filelist_downloadasfolder_scenarios(); + $downloadasfilestests = $this->load_filelist_downloadasfiles_scenarios(); + return array_merge( + $downloadasfoldertests, + $downloadasfilestests, + ); + } + + /** + * Generate the standard test scenarios for load_filelist with download as file. + * + * The scenarios are the same as download as folder but replacing the "/" of the files + * by a "_" and setting the downloadasfolder to false. + * + * @return array of scenarios + */ + private function load_filelist_downloadasfiles_scenarios(): array { + $result = $this->load_filelist_downloadasfolder_scenarios("Download as files:"); + // Transform paths from files. + foreach ($result as $scenario => $info) { + $info['downloadasfolder'] = false; + foreach ($info['expected'] as $key => $path) { + $info['expected'][$key] = str_replace('/', '_', $path); + } + $result[$scenario] = $info; + } + return $result; + } + + /** + * Generate the standard test scenarios for load_filelist with download as folder. + * + * @param string $prefix the scenarios prefix + * @return array of scenarios + */ + private function load_filelist_downloadasfolder_scenarios(string $prefix = "Download as folders:"): array { + return [ + // Test without team submissions. + $prefix . ' All users without groups' => [ + 'teamsubmission' => false, + 'groupmembers' => [], + 'filterusers' => null, + 'blindmarking' => false, + 'downloadasfolder' => true, + 'expected' => [ + 'STUDENT1_STUDENT1.ID_assignsubmission_file/submissionsample01.txt', + 'STUDENT1_STUDENT1.ID_assignsubmission_file/submissionsample02.txt', + 'STUDENT2_STUDENT2.ID_assignsubmission_file/submissionsample01.txt', + 'STUDENT2_STUDENT2.ID_assignsubmission_file/submissionsample02.txt', + 'STUDENT3_STUDENT3.ID_assignsubmission_file/submissionsample01.txt', + 'STUDENT3_STUDENT3.ID_assignsubmission_file/submissionsample02.txt', + 'STUDENT4_STUDENT4.ID_assignsubmission_file/submissionsample01.txt', + 'STUDENT4_STUDENT4.ID_assignsubmission_file/submissionsample02.txt', + ], + ], + $prefix . ' Filtered users' => [ + 'teamsubmission' => false, + 'groupmembers' => [], + 'filterusers' => ['student1', 'student2'], + 'blindmarking' => false, + 'downloadasfolder' => true, + 'expected' => [ + 'STUDENT1_STUDENT1.ID_assignsubmission_file/submissionsample01.txt', + 'STUDENT1_STUDENT1.ID_assignsubmission_file/submissionsample02.txt', + 'STUDENT2_STUDENT2.ID_assignsubmission_file/submissionsample01.txt', + 'STUDENT2_STUDENT2.ID_assignsubmission_file/submissionsample02.txt', + ], + ], + $prefix . ' Filtering users without submissions' => [ + 'teamsubmission' => false, + 'groupmembers' => [], + 'filterusers' => ['student1', 'student5'], + 'blindmarking' => false, + 'downloadasfolder' => true, + 'expected' => [ + 'STUDENT1_STUDENT1.ID_assignsubmission_file/submissionsample01.txt', + 'STUDENT1_STUDENT1.ID_assignsubmission_file/submissionsample02.txt', + ], + ], + $prefix . ' Asking only for users without submissions' => [ + 'teamsubmission' => false, + 'groupmembers' => [], + 'filterusers' => ['student5'], + 'blindmarking' => false, + 'downloadasfolder' => true, + 'expected' => [], + ], + // Test with team submissions and no default team. + $prefix . ' All users with all users in groups' => [ + 'teamsubmission' => true, + 'groupmembers' => [ + 'group1' => ['student1'], + 'group2' => ['student2', 'student3'], + 'group3' => ['student4', 'student5'], + ], + 'filterusers' => null, + 'blindmarking' => false, + 'downloadasfolder' => true, + 'expected' => [ + 'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample01.txt', + 'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample02.txt', + 'GROUP2_GROUP2.ID_assignsubmission_file/submissionsample01.txt', + 'GROUP2_GROUP2.ID_assignsubmission_file/submissionsample02.txt', + 'GROUP3_GROUP3.ID_assignsubmission_file/submissionsample01.txt', + 'GROUP3_GROUP3.ID_assignsubmission_file/submissionsample02.txt', + ], + ], + $prefix . ' Filtering users with disjoined groups' => [ + 'teamsubmission' => true, + 'groupmembers' => [ + 'group1' => ['student1'], + 'group2' => ['student2', 'student3'], + 'group3' => ['student4', 'student5'], + ], + 'filterusers' => ['student1', 'student2'], + 'blindmarking' => false, + 'downloadasfolder' => true, + 'expected' => [ + 'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample01.txt', + 'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample02.txt', + 'GROUP2_GROUP2.ID_assignsubmission_file/submissionsample01.txt', + 'GROUP2_GROUP2.ID_assignsubmission_file/submissionsample02.txt', + ], + ], + $prefix . ' Filtering users with default teams who does not do a submission' => [ + 'teamsubmission' => true, + 'groupmembers' => [ + 'group1' => ['student1'], + 'group2' => ['student2', 'student3'], + 'group3' => ['student4', 'student5'], + ], + 'filterusers' => ['student1', 'student5'], + 'blindmarking' => false, + 'downloadasfolder' => true, + 'expected' => [ + 'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample01.txt', + 'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample02.txt', + 'GROUP3_GROUP3.ID_assignsubmission_file/submissionsample01.txt', + 'GROUP3_GROUP3.ID_assignsubmission_file/submissionsample02.txt', + ], + ], + $prefix . ' Filtering users without submission but member of a group' => [ + 'teamsubmission' => true, + 'groupmembers' => [ + 'group1' => ['student1'], + 'group2' => ['student2', 'student3'], + 'group3' => ['student4', 'student5'], + ], + 'filterusers' => [ + 'student5' + ], + 'blindmarking' => false, + 'downloadasfolder' => true, + 'expected' => [ + 'GROUP3_GROUP3.ID_assignsubmission_file/submissionsample01.txt', + 'GROUP3_GROUP3.ID_assignsubmission_file/submissionsample02.txt', + ], + ], + // Test with default team. + $prefix . ' All users with users in the default team' => [ + 'teamsubmission' => true, + 'groupmembers' => [ + 'group1' => ['student1', 'student2'], + ], + 'filterusers' => null, + 'blindmarking' => false, + 'downloadasfolder' => true, + 'expected' => [ + 'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample01.txt', + 'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample02.txt', + 'DEFAULTTEAM_assignsubmission_file/submissionsample01.txt', + 'DEFAULTTEAM_assignsubmission_file/submissionsample02.txt', + ], + ], + $prefix . ' Filtered users in groups with users in the default team' => [ + 'teamsubmission' => true, + 'groupmembers' => [ + 'group1' => ['student1', 'student2'], + ], + 'filterusers' => ['student1', 'student2'], + 'blindmarking' => false, + 'downloadasfolder' => true, + 'expected' => [ + 'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample01.txt', + 'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample02.txt', + ], + ], + $prefix . ' Filtered users without groups with users in the default team' => [ + 'teamsubmission' => true, + 'groupmembers' => [ + 'group1' => ['student1', 'student2'], + ], + 'filterusers' => ['student3', 'student4'], + 'blindmarking' => false, + 'downloadasfolder' => true, + 'expected' => [ + 'DEFAULTTEAM_assignsubmission_file/submissionsample01.txt', + 'DEFAULTTEAM_assignsubmission_file/submissionsample02.txt', + ], + ], + $prefix . ' Filtered users with some users in the default team' => [ + 'teamsubmission' => true, + 'groupmembers' => [ + 'group1' => ['student1', 'student2'], + ], + 'filterusers' => ['student1', 'student3'], + 'blindmarking' => false, + 'downloadasfolder' => true, + 'expected' => [ + 'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample01.txt', + 'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample02.txt', + 'DEFAULTTEAM_assignsubmission_file/submissionsample01.txt', + 'DEFAULTTEAM_assignsubmission_file/submissionsample02.txt', + ], + ], + $prefix . ' Filtering users with joined groups' => [ + 'teamsubmission' => true, + 'groupmembers' => [ + 'group1' => ['student1', 'student2'], + 'group2' => ['student2', 'student3'], + ], + 'filterusers' => ['student1', 'student2'], + 'blindmarking' => false, + 'downloadasfolder' => true, + 'expected' => [ + 'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample01.txt', + 'GROUP1_GROUP1.ID_assignsubmission_file/submissionsample02.txt', + 'DEFAULTTEAM_assignsubmission_file/submissionsample01.txt', + 'DEFAULTTEAM_assignsubmission_file/submissionsample02.txt', + ], + ], + // Tests with blind marking. + $prefix . ' All users without groups and blindmarking' => [ + 'teamsubmission' => false, + 'groupmembers' => [], + 'filterusers' => null, + 'blindmarking' => true, + 'downloadasfolder' => true, + 'expected' => [ + 'PARTICIPANT_STUDENT1.ID_assignsubmission_file/submissionsample01.txt', + 'PARTICIPANT_STUDENT1.ID_assignsubmission_file/submissionsample02.txt', + 'PARTICIPANT_STUDENT2.ID_assignsubmission_file/submissionsample01.txt', + 'PARTICIPANT_STUDENT2.ID_assignsubmission_file/submissionsample02.txt', + 'PARTICIPANT_STUDENT3.ID_assignsubmission_file/submissionsample01.txt', + 'PARTICIPANT_STUDENT3.ID_assignsubmission_file/submissionsample02.txt', + 'PARTICIPANT_STUDENT4.ID_assignsubmission_file/submissionsample01.txt', + 'PARTICIPANT_STUDENT4.ID_assignsubmission_file/submissionsample02.txt', + ], + ], + $prefix . ' Filtered users without groups and blindmarking' => [ + 'teamsubmission' => false, + 'groupmembers' => [], + 'filterusers' => ['student1', 'student2'], + 'blindmarking' => true, + 'downloadasfolder' => true, + 'expected' => [ + 'PARTICIPANT_STUDENT1.ID_assignsubmission_file/submissionsample01.txt', + 'PARTICIPANT_STUDENT1.ID_assignsubmission_file/submissionsample02.txt', + 'PARTICIPANT_STUDENT2.ID_assignsubmission_file/submissionsample01.txt', + 'PARTICIPANT_STUDENT2.ID_assignsubmission_file/submissionsample02.txt', + ], + ], + ]; + } +} diff --git a/mod/assign/tests/fixtures/submissionsample01.txt b/mod/assign/tests/fixtures/submissionsample01.txt new file mode 100644 index 00000000000..11c85db59e8 --- /dev/null +++ b/mod/assign/tests/fixtures/submissionsample01.txt @@ -0,0 +1 @@ +This is just a submission testing sample. diff --git a/mod/assign/tests/fixtures/submissionsample02.txt b/mod/assign/tests/fixtures/submissionsample02.txt new file mode 100644 index 00000000000..11c85db59e8 --- /dev/null +++ b/mod/assign/tests/fixtures/submissionsample02.txt @@ -0,0 +1 @@ +This is just a submission testing sample.