From a55a982746de4a77d45e962c398a1450e3dcde14 Mon Sep 17 00:00:00 2001 From: Michael Hughes Date: Mon, 1 Sep 2014 12:31:06 +0100 Subject: [PATCH 1/7] MDL-31936 workshop: Initial implementation of the course reset support --- mod/workshop/lang/en/workshop.php | 1 + mod/workshop/lib.php | 37 +++++++++++++++ mod/workshop/locallib.php | 79 +++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) diff --git a/mod/workshop/lang/en/workshop.php b/mod/workshop/lang/en/workshop.php index 2a9bd345244..911d74fb4f5 100644 --- a/mod/workshop/lang/en/workshop.php +++ b/mod/workshop/lang/en/workshop.php @@ -319,6 +319,7 @@ $string['workshop:viewauthornames'] = 'View author names'; $string['workshop:viewauthorpublished'] = 'View authors of published submissions'; $string['workshop:viewpublishedsubmissions'] = 'View published submissions'; $string['workshop:viewreviewernames'] = 'View reviewer names'; +$string['resetworkshopall'] = 'Reset all'; $string['yourassessment'] = 'Your assessment'; $string['yourgrades'] = 'Your grades'; $string['yoursubmission'] = 'Your submission'; diff --git a/mod/workshop/lib.php b/mod/workshop/lib.php index af9061cd354..d6bbf8343e5 100644 --- a/mod/workshop/lib.php +++ b/mod/workshop/lib.php @@ -1696,3 +1696,40 @@ function workshop_calendar_update(stdClass $workshop, $cmid) { $oldevent->delete(); } } + +/** Started MDL-31936 Reset work*/ +function workshop_reset_course_form_definition(&$mform) { + $mform->addElement('header', 'workshopheader', get_string('modulenameplural', 'workshop')); + $mform->addElement('checkbox', 'reset_workshop_all', get_string('resetworkshopall', 'workshop')); + // ... @todo selective reset. + +} + +function workshop_reset_course_form_defaults($course) { + return array( + 'reset_workshop_all' => 1 + ); +} + +function workshop_reset_userdata($data) { + global $CFG, $DB; + + // Only if we have workshop modules. + if (!$workshops = $DB->get_records('workshop', array('course' => $data->courseid))) { + return false; + } + require_once($CFG->dirroot . '/mod/workshop/locallib.php'); + $course = $DB->get_record('course', array('id' => $data->courseid)); + $componentstr = get_string('modulenameplural', 'workshop'); + + $status = array(); + + foreach ($workshops as $workshop) { + $cm = get_coursemodule_from_instance("workshop", $workshop->id, $data->courseid); + $wx = new workshop($workshop, $cm, $course); + $status = array_merge($status, $wx->reset_userdata($data)); + } + + return $status; +} + diff --git a/mod/workshop/locallib.php b/mod/workshop/locallib.php index a9221c902b5..2840b9071a7 100644 --- a/mod/workshop/locallib.php +++ b/mod/workshop/locallib.php @@ -2611,6 +2611,85 @@ class workshop { return substr($fullurl->out(), strlen($baseurl)); } + + /** + * Resets this activity's content + * @param array The reset data object. + * @return bool True if the reset was successful, false if there was an error + */ + public function reset_userdata($data) { + $componentstr = get_string('modulenameplural', 'workshop'); + $status = array(); + $this->reset_submissions(); + $delaggregations = $DB->delete_records('workshop_aggregations', array('workshopid' => $this->id)); + $this->reset_allocators(); + $this->reset_evaluators(); + $this->reset_strategies(); + + $events = $DB->get_records('event', array('modulename' => 'workshop', 'instance' => $this->id)); + foreach ($events as $event) { + $event = calendar_event::load($event); + $event->delete(); + } + + // Set the phase. + // $DB->set_field('workshop', 'phase', 0, array('id' => $workshop->id)); // Left in to give option to switch back after discussion. + $this->switch_phase(self::PHASE_SETUP); // Use the API but we may not want to raise events about this.... + + $status[] = array('component' => $componentstr, 'item' => get_string('resetworkshopall', 'workshop'), 'error' => false); + return $status; + } + + /** + * Remove user content, including grades + */ + protected function reset_submissions() { + $submissions = $this->get_submissions(); + foreach ($submissions as $submission) { + $this->delete_submission($submission); + } + // Clean up submission files. + $fs = get_file_storage(); + $files = $fs->get_area_files($this->context, 'mod_workshop', 'submission_attachment', false); + $numberfilestodelete = count($files); + $numfilesdeleted = 0; + foreach ($files as $file) { + try { + $file->delete(); + $numfilesdeleted += 1; + } + catch (Exception $exception) { + // The delete can fail if necessary it just leaves an orphan in the DB. The record its attached to will have rady been removed. + } + } + } + + protected function reset_allocators() { + $allocators = core_component::get_plugin_list('workshopallocation'); + foreach ($allocators as $allocator => $path) { + require_once($path.'/lib.php'); + $classname = 'workshop_'.$allocator.'_allocator'; + call_user_func($classname.'::reset_user_data', $this->id); + } + } + + protected function reset_strategies() { + $evaluators = core_component::get_plugin_list('workshopeval'); + foreach ($evaluators as $evaluator => $path) { + require_once($path.'/lib.php'); + $classname = 'workshop_'.$evaluator.'_evaluation'; + call_user_func($classname.'::reset_user_data', $this->id); + } + } + + protected function reset_evaluators() { + $evaluators = core_component::get_plugin_list('workshopeval'); + foreach ($evaluators as $evaluator => $path) { + require_once($path.'/lib.php'); + $classname = 'workshop_'.$evaluator.'_evaluation'; + call_user_func($classname.'::reset_user_data', $this->id); + } + } } //////////////////////////////////////////////////////////////////////////////// From 079944a8787343ff915c817cf7a8c71d2769f891 Mon Sep 17 00:00:00 2001 From: Michael Hughes Date: Fri, 5 Sep 2014 10:05:55 +0100 Subject: [PATCH 2/7] MDL-31936 workshop: Add unit tests for the workshop reset --- mod/workshop/tests/fixtures/testable.php | 15 +++++++++ mod/workshop/tests/locallib_test.php | 41 ++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/mod/workshop/tests/fixtures/testable.php b/mod/workshop/tests/fixtures/testable.php index e7e1cd3e20b..50a0906c48f 100644 --- a/mod/workshop/tests/fixtures/testable.php +++ b/mod/workshop/tests/fixtures/testable.php @@ -38,4 +38,19 @@ class testable_workshop extends workshop { parent::aggregate_grading_grades_process($assessments, $timegraded); } + public function reset_submissions() { + parent::reset_submissions(); + } + + public function reset_allocators() { + parent::reset_allocators(); + } + + public function reset_strategies() { + parent::reset_strategies(); + } + + public function reset_evaluators() { + parent::reset_evaulators(); + } } diff --git a/mod/workshop/tests/locallib_test.php b/mod/workshop/tests/locallib_test.php index 08f5a028d11..d182f26ca48 100644 --- a/mod/workshop/tests/locallib_test.php +++ b/mod/workshop/tests/locallib_test.php @@ -416,4 +416,45 @@ class mod_workshop_internal_api_testcase extends advanced_testcase { // excersise SUT $a = $this->workshop->prepare_example_reference_assessment($fakerawrecord); } + + public function test_reset_userdata() { + $this->resetAfterTest(true); + // $this->workshop is the instance we can use to test. + $this->workshop->switch_phase(workshop::PHASE_CLOSED); // Ensure workshop is a state that indicates work done + $this->assertEquals(workshop::PHASE_CLOSED, $this->workshop->phase); + $originaleventcount = $DB->count_records('event', array('modulename' => 'workshop', 'instance' => $this->workshop->id)); + + // Setup some data + /* + * Need to : + * 1. Setup (already done) + * 2. Move to submission phase + * 3. Submit + * 4. Move to assessmetn phase. + * 5. Record Assess + * 6. Move to Evaulation phase + * 7. Evaluate + */ + + // Setup reset data + $data = new stdClass(); + $data->reset_workshop_all = 1; + $data->reset_gradebook_grades = 1; + $data->courseid = $this->course->id; + $data->timeshift = 24 * 60 * 60; + $this->setUser($this->editingteachers[0]); + + // Get various counts. + $status = $this->workshop->reset_userdata($data); + $posteventcount = $DB->count_records('event', array('modulename' => 'workshop', 'instance' => $this->workshop->id)); + $countaggregations = $DB->count_records('workshop_aggregations', array('workshopid' => $this->workshop->id)); + $fs = get_file_storage(); + $files = $fs->get_area_files($this->context, 'mod_workshop', 'submission_attachment', false); + + // Check results + $this->assertEquals(0, $countaggregations, 'Aggregations should be empty'); + $this->assertEquals(workshop::PHASE_SETUP, $this->workshop->phase, 'Phase should have changed to SETUP'); + $this->assertEquals(0, $posteventcount, 'Calendar event count should equal 0.'); + $this->assertEqual(0, count($files), 'Number of files should be 0.'); + } } From 9d873205b17ddb3f8568132b650f80156c5f270d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Mudr=C3=A1k?= Date: Wed, 17 Sep 2014 23:36:13 +0200 Subject: [PATCH 3/7] MDL-31936 workshop: Fix overall feedback content processing order This is unrelated to the issue but was spotted while testing it. The order of format_text() and file_rewrite_pluginfile_urls() was incorrect and threw debugging message. Fixed now. --- mod/workshop/locallib.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mod/workshop/locallib.php b/mod/workshop/locallib.php index 2840b9071a7..77ddb5e2155 100644 --- a/mod/workshop/locallib.php +++ b/mod/workshop/locallib.php @@ -3507,10 +3507,10 @@ class workshop_assessment extends workshop_assessment_base implements renderable return null; } - $content = format_text($this->feedbackauthor, $this->feedbackauthorformat, - array('overflowdiv' => true, 'context' => $this->workshop->context)); - $content = file_rewrite_pluginfile_urls($content, 'pluginfile.php', $this->workshop->context->id, + $content = file_rewrite_pluginfile_urls($this->feedbackauthor, 'pluginfile.php', $this->workshop->context->id, 'mod_workshop', 'overallfeedback_content', $this->id); + $content = format_text($content, $this->feedbackauthorformat, + array('overflowdiv' => true, 'context' => $this->workshop->context)); return $content; } From 5d0c6ae61e5ceac0e01f2476c27248ffc2856dd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Mudr=C3=A1k?= Date: Wed, 17 Sep 2014 23:04:42 +0200 Subject: [PATCH 4/7] MDL-31936 workshop: Delete attachments on record removal The methods workshop::delete_submission() and workshop::delete_assessment() did not delete files (embedded and attachments) associated with the given submission or assessment. This is fixed now. Additionally, the delete_assessment() method now cleans-up records from the table workshop_grades, too. This internal workshop API still does not give workshop subplugins a chance to clean up their data, should they store them in their own tables instead of the workshop_grades one. This should be improved in the future yet. --- mod/workshop/locallib.php | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/mod/workshop/locallib.php b/mod/workshop/locallib.php index 77ddb5e2155..646d55d172c 100644 --- a/mod/workshop/locallib.php +++ b/mod/workshop/locallib.php @@ -1048,6 +1048,11 @@ class workshop { global $DB; $assessments = $DB->get_records('workshop_assessments', array('submissionid' => $submission->id), '', 'id'); $this->delete_assessment(array_keys($assessments)); + + $fs = get_file_storage(); + $fs->delete_area_files($this->context->id, 'mod_workshop', 'submission_content', $submission->id); + $fs->delete_area_files($this->context->id, 'mod_workshop', 'submission_attachment', $submission->id); + $DB->delete_records('workshop_submissions', array('id' => $submission->id)); } @@ -1251,21 +1256,39 @@ class workshop { } /** - * Delete assessment record or records + * Delete assessment record or records. * - * @param mixed $id int|array assessment id or array of assessments ids - * @return bool false if $id not a valid parameter, true otherwise + * Removes associated records from the workshop_grades table, too. + * + * @param int|array $id assessment id or array of assessments ids + * @todo Give grading strategy plugins a chance to clean up their data, too. + * @return bool true */ public function delete_assessment($id) { global $DB; - // todo remove all given grades from workshop_grades; + if (empty($id)) { + return true; + } + + $fs = get_file_storage(); if (is_array($id)) { - return $DB->delete_records_list('workshop_assessments', 'id', $id); + $DB->delete_records_list('workshop_grades', 'assessmentid', $id); + foreach ($id as $itemid) { + $fs->delete_area_files($this->context->id, 'mod_workshop', 'overallfeedback_content', $itemid); + $fs->delete_area_files($this->context->id, 'mod_workshop', 'overallfeedback_attachment', $itemid); + } + $DB->delete_records_list('workshop_assessments', 'id', $id); + } else { - return $DB->delete_records('workshop_assessments', array('id' => $id)); + $DB->delete_records('workshop_grades', array('assessmentid' => $id)); + $fs->delete_area_files($this->context->id, 'mod_workshop', 'overallfeedback_content', $id); + $fs->delete_area_files($this->context->id, 'mod_workshop', 'overallfeedback_attachment', $id); + $DB->delete_records('workshop_assessments', array('id' => $id)); } + + return true; } /** From fcb3fe72380128a25a8c56890df8c60c1fc17ecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Mudr=C3=A1k?= Date: Wed, 17 Sep 2014 21:36:13 +0200 Subject: [PATCH 5/7] MDL-31936 workshop: Add more options to the course reset form This is built upon original work by Michael Hughes. We now have separate settings for deleting workshop submissions, assessments and for resetting the workshop phase. --- mod/workshop/lang/en/workshop.php | 7 +- mod/workshop/lib.php | 82 +++++++++++---- mod/workshop/locallib.php | 162 ++++++++++++++++++------------ 3 files changed, 167 insertions(+), 84 deletions(-) diff --git a/mod/workshop/lang/en/workshop.php b/mod/workshop/lang/en/workshop.php index 911d74fb4f5..3baa29371c6 100644 --- a/mod/workshop/lang/en/workshop.php +++ b/mod/workshop/lang/en/workshop.php @@ -224,6 +224,12 @@ $string['reassess'] = 'Re-assess'; $string['receivedgrades'] = 'Grades received'; $string['recentassessments'] = 'Workshop assessments:'; $string['recentsubmissions'] = 'Workshop submissions:'; +$string['resetassessments'] = 'Delete all assessments'; +$string['resetassessments_help'] = 'You can choose to delete just allocated assessments without affecting submissions. If submissions are to be deleted, their assessments will be deleted implicitly and this option is ignored. Note this also includes assessments of example submissions.'; +$string['resetsubmissions'] = 'Delete all submissions'; +$string['resetsubmissions_help'] = 'All the submissions and their assessments will be deleted. This does not affect example submissions.'; +$string['resetphase'] = 'Switch to the setup phase'; +$string['resetphase_help'] = 'If enabled, all workshops will be put into the initial setup phase.'; $string['saveandclose'] = 'Save and close'; $string['saveandcontinue'] = 'Save and continue editing'; $string['saveandpreview'] = 'Save and preview'; @@ -319,7 +325,6 @@ $string['workshop:viewauthornames'] = 'View author names'; $string['workshop:viewauthorpublished'] = 'View authors of published submissions'; $string['workshop:viewpublishedsubmissions'] = 'View published submissions'; $string['workshop:viewreviewernames'] = 'View reviewer names'; -$string['resetworkshopall'] = 'Reset all'; $string['yourassessment'] = 'Your assessment'; $string['yourgrades'] = 'Your grades'; $string['yoursubmission'] = 'Your submission'; diff --git a/mod/workshop/lib.php b/mod/workshop/lib.php index d6bbf8343e5..cc9250901ac 100644 --- a/mod/workshop/lib.php +++ b/mod/workshop/lib.php @@ -1697,39 +1697,79 @@ function workshop_calendar_update(stdClass $workshop, $cmid) { } } -/** Started MDL-31936 Reset work*/ -function workshop_reset_course_form_definition(&$mform) { - $mform->addElement('header', 'workshopheader', get_string('modulenameplural', 'workshop')); - $mform->addElement('checkbox', 'reset_workshop_all', get_string('resetworkshopall', 'workshop')); - // ... @todo selective reset. +//////////////////////////////////////////////////////////////////////////////// +// Course reset API // +//////////////////////////////////////////////////////////////////////////////// +/** + * Extends the course reset form with workshop specific settings. + * + * @param MoodleQuickForm $mform + */ +function workshop_reset_course_form_definition($mform) { + + $mform->addElement('header', 'workshopheader', get_string('modulenameplural', 'mod_workshop')); + + $mform->addElement('advcheckbox', 'reset_workshop_submissions', get_string('resetsubmissions', 'mod_workshop')); + $mform->addHelpButton('reset_workshop_submissions', 'resetsubmissions', 'mod_workshop'); + + $mform->addElement('advcheckbox', 'reset_workshop_assessments', get_string('resetassessments', 'mod_workshop')); + $mform->addHelpButton('reset_workshop_assessments', 'resetassessments', 'mod_workshop'); + $mform->disabledIf('reset_workshop_assessments', 'reset_workshop_submissions', 'checked'); + + $mform->addElement('advcheckbox', 'reset_workshop_phase', get_string('resetphase', 'mod_workshop')); + $mform->addHelpButton('reset_workshop_phase', 'resetphase', 'mod_workshop'); } -function workshop_reset_course_form_defaults($course) { - return array( - 'reset_workshop_all' => 1 - ); +/** + * Provides default values for the workshop settings in the course reset form. + * + * @param stdClass $course The course to be reset. + */ +function workshop_reset_course_form_defaults(stdClass $course) { + + $defaults = array( + 'reset_workshop_submissions' => 1, + 'reset_workshop_assessments' => 1, + 'reset_workshop_phase' => 1, + ); + + return $defaults; } -function workshop_reset_userdata($data) { +/** + * Performs the reset of all workshop instances in the course. + * + * @param stdClass $data The actual course reset settings. + * @return array List of results, each being array[(string)component, (string)item, (string)error] + */ +function workshop_reset_userdata(stdClass $data) { global $CFG, $DB; - // Only if we have workshop modules. - if (!$workshops = $DB->get_records('workshop', array('course' => $data->courseid))) { - return false; + if (empty($data->reset_workshop_submissions) + and empty($data->reset_workshop_assessments) + and empty($data->reset_workshop_phase) ) { + // Nothing to do here. + return array(); } - require_once($CFG->dirroot . '/mod/workshop/locallib.php'); - $course = $DB->get_record('course', array('id' => $data->courseid)); - $componentstr = get_string('modulenameplural', 'workshop'); + $workshoprecords = $DB->get_records('workshop', array('course' => $data->courseid)); + + if (empty($workshoprecords)) { + // What a boring course - no workshops here! + return array(); + } + + require_once($CFG->dirroot . '/mod/workshop/locallib.php'); + + $course = $DB->get_record('course', array('id' => $data->courseid), '*', MUST_EXIST); $status = array(); - foreach ($workshops as $workshop) { - $cm = get_coursemodule_from_instance("workshop", $workshop->id, $data->courseid); - $wx = new workshop($workshop, $cm, $course); - $status = array_merge($status, $wx->reset_userdata($data)); + foreach ($workshoprecords as $workshoprecord) { + $cm = get_coursemodule_from_instance('workshop', $workshoprecord->id, $course->id, false, MUST_EXIST); + $workshop = new workshop($workshoprecord, $cm, $course); + $status = array_merge($status, $workshop->reset_userdata($data)); } return $status; } - diff --git a/mod/workshop/locallib.php b/mod/workshop/locallib.php index 646d55d172c..1a3a9e703e9 100644 --- a/mod/workshop/locallib.php +++ b/mod/workshop/locallib.php @@ -1046,6 +1046,7 @@ class workshop { */ public function delete_submission(stdclass $submission) { global $DB; + $assessments = $DB->get_records('workshop_assessments', array('submissionid' => $submission->id), '', 'id'); $this->delete_assessment(array_keys($assessments)); @@ -2377,6 +2378,69 @@ class workshop { ); } + /** + * Performs the reset of this workshop instance. + * + * @param stdClass $data The actual course reset settings. + * @return array List of results, each being array[(string)component, (string)item, (string)error] + */ + public function reset_userdata(stdClass $data) { + + $componentstr = get_string('pluginname', 'workshop').': '.format_string($this->name); + $status = array(); + + if (!empty($data->reset_workshop_assessments) or !empty($data->reset_workshop_submissions)) { + // Reset all data related to assessments, including assessments of + // example submissions. + $result = $this->reset_userdata_assessments($data); + if ($result === true) { + $status[] = array( + 'component' => $componentstr, + 'item' => get_string('resetassessments', 'mod_workshop'), + 'error' => false, + ); + } else { + $status[] = array( + 'component' => $componentstr, + 'item' => get_string('resetassessments', 'mod_workshop'), + 'error' => $result, + ); + } + } + + if (!empty($data->reset_workshop_submissions)) { + // Reset all remaining data related to submissions. + $result = $this->reset_userdata_submissions($data); + if ($result === true) { + $status[] = array( + 'component' => $componentstr, + 'item' => get_string('resetsubmissions', 'mod_workshop'), + 'error' => false, + ); + } else { + $status[] = array( + 'component' => $componentstr, + 'item' => get_string('resetsubmissions', 'mod_workshop'), + 'error' => $result, + ); + } + } + + if (!empty($data->reset_workshop_phase)) { + // Do not use the {@link workshop::switch_phase()} here, we do not + // want to trigger events. + $this->reset_phase(); + $status[] = array( + 'component' => $componentstr, + 'item' => get_string('resetsubmissions', 'mod_workshop'), + 'error' => false, + ); + } + + return $status; + } + + //////////////////////////////////////////////////////////////////////////////// // Internal methods (implementation details) // //////////////////////////////////////////////////////////////////////////////// @@ -2634,84 +2698,58 @@ class workshop { return substr($fullurl->out(), strlen($baseurl)); } - + /** - * Resets this activity's content - * @param array The reset data object. - * @return bool True if the reset was successful, false if there was an error + * Removes all user data related to assessments (including allocations). + * + * This includes assessments of example submissions as long as they are not + * referential assessments. + * + * @param stdClass $data The actual course reset settings. + * @return bool|string True on success, error message otherwise. */ - public function reset_userdata($data) { - $componentstr = get_string('modulenameplural', 'workshop'); - $status = array(); - $this->reset_submissions(); - $delaggregations = $DB->delete_records('workshop_aggregations', array('workshopid' => $this->id)); - $this->reset_allocators(); - $this->reset_evaluators(); - $this->reset_strategies(); + protected function reset_userdata_assessments(stdClass $data) { + global $DB; - $events = $DB->get_records('event', array('modulename' => 'workshop', 'instance' => $this->id)); - foreach ($events as $event) { - $event = calendar_event::load($event); - $event->delete(); - } + $sql = "SELECT a.id + FROM {workshop_assessments} a + JOIN {workshop_submissions} s ON (a.submissionid = s.id) + WHERE s.workshopid = :workshopid + AND (s.example = 0 OR (s.example = 1 AND a.weight = 0))"; - // Set the phase. - // $DB->set_field('workshop', 'phase', 0, array('id' => $workshop->id)); // Left in to give option to switch back after discussion. - $this->switch_phase(self::PHASE_SETUP); // Use the API but we may not want to raise events about this.... + $assessments = $DB->get_records_sql($sql, array('workshopid' => $this->id)); + $this->delete_assessment(array_keys($assessments)); - $status[] = array('component' => $componentstr, 'item' => get_string('resetworkshopall', 'workshop'), 'error' => false); - return $status; + $DB->delete_records('workshop_aggregations', array('workshopid' => $this->id)); + + return true; } /** - * Remove user content, including grades + * Removes all user data related to participants' submissions. + * + * @param stdClass $data The actual course reset settings. + * @return bool|string True on success, error message otherwise. */ - protected function reset_submissions() { + protected function reset_userdata_submissions(stdClass $data) { + global $DB; + $submissions = $this->get_submissions(); foreach ($submissions as $submission) { $this->delete_submission($submission); } - // Clean up submission files. - $fs = get_file_storage(); - $files = $fs->get_area_files($this->context, 'mod_workshop', 'submission_attachment', false); - $numberfilestodelete = count($files); - $numfilesdeleted = 0; - foreach ($files as $file) { - try { - $file->delete(); - $numfilesdeleted += 1; - } - catch (Exception $exception) { - // The delete can fail if necessary it just leaves an orphan in the DB. The record its attached to will have rady been removed. - } - } - } - - protected function reset_allocators() { - $allocators = core_component::get_plugin_list('workshopallocation'); - foreach ($allocators as $allocator => $path) { - require_once($path.'/lib.php'); - $classname = 'workshop_'.$allocator.'_allocator'; - call_user_func($classname.'::reset_user_data', $this->id); - } + + return true; } - protected function reset_strategies() { - $evaluators = core_component::get_plugin_list('workshopeval'); - foreach ($evaluators as $evaluator => $path) { - require_once($path.'/lib.php'); - $classname = 'workshop_'.$evaluator.'_evaluation'; - call_user_func($classname.'::reset_user_data', $this->id); - } - } + /** + * Hard set the workshop phase to the setup one. + */ + protected function reset_phase() { + global $DB; - protected function reset_evaluators() { - $evaluators = core_component::get_plugin_list('workshopeval'); - foreach ($evaluators as $evaluator => $path) { - require_once($path.'/lib.php'); - $classname = 'workshop_'.$evaluator.'_evaluation'; - call_user_func($classname.'::reset_user_data', $this->id); - } + $DB->set_field('workshop', 'phase', self::PHASE_SETUP, array('id' => $this->id)); + $this->phase = self::PHASE_SETUP; } } From 5de218bcb395891e84c2ef063a8dc06790c37b6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Mudr=C3=A1k?= Date: Thu, 18 Sep 2014 13:48:40 +0200 Subject: [PATCH 6/7] MDL-31936 workshop: Generator able to create submissions and assessments This is a basic implementation of the actual content generation in the workshop generator. Note the we do not generate actual grading form data (which is what the grading strategy plugins would do), just their results. --- mod/workshop/tests/generator/lib.php | 59 +++++++++++++++++++++++++++ mod/workshop/tests/generator_test.php | 53 ++++++++++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/mod/workshop/tests/generator/lib.php b/mod/workshop/tests/generator/lib.php index 7f3713588a9..3774b8c96c9 100644 --- a/mod/workshop/tests/generator/lib.php +++ b/mod/workshop/tests/generator/lib.php @@ -97,4 +97,63 @@ class mod_workshop_generator extends testing_module_generator { return parent::create_instance($record, (array)$options); } + + /** + * Generates a submission authored by the given user. + * + * @param int $workshopid Workshop instance id. + * @param int $authorid Author user id. + * @param stdClass|array $options Optional explicit properties. + * @return int The new submission id. + */ + public function create_submission($workshopid, $authorid, $options = null) { + global $DB; + + $timenow = time(); + $options = (array)$options; + + $record = $options + array( + 'workshopid' => $workshopid, + 'example' => 0, + 'authorid' => $authorid, + 'timecreated' => $timenow, + 'timemodified' => $timenow, + 'title' => 'Generated submission', + 'content' => 'Generated content', + 'contentformat' => FORMAT_MARKDOWN, + 'contenttrust' => 0, + ); + + $id = $DB->insert_record('workshop_submissions', $record); + + return $id; + } + + /** + * Generates an allocation of the given submission for peer-assessment by the given user + * + * @param int $submissionid Submission id. + * @param int $reviewerid Reviewer's user id. + * @param stdClass|array $options Optional explicit properties. + * @return int The new assessment id. + */ + public function create_assessment($submissionid, $reviewerid, $options = null) { + global $DB; + + $timenow = time(); + $options = (array)$options; + + $record = $options + array( + 'submissionid' => $submissionid, + 'reviewerid' => $reviewerid, + 'weight' => 1, + 'timecreated' => $timenow, + 'timemodified' => $timenow, + 'grade' => null, + ); + + $id = $DB->insert_record('workshop_assessments', $record); + + return $id; + } } diff --git a/mod/workshop/tests/generator_test.php b/mod/workshop/tests/generator_test.php index ef39f387567..d0961fb819f 100644 --- a/mod/workshop/tests/generator_test.php +++ b/mod/workshop/tests/generator_test.php @@ -52,4 +52,57 @@ class mod_workshop_generator_testcase extends advanced_testcase { $this->assertEquals(2, count($records)); $this->assertEquals('Another workshop', $records[$workshop->id]->name); } + + public function test_create_submission() { + global $DB; + $this->resetAfterTest(); + $this->setAdminUser(); + + $course = $this->getDataGenerator()->create_course(); + $workshop = $this->getDataGenerator()->create_module('workshop', array('course' => $course)); + $user = $this->getDataGenerator()->create_user(); + $this->getDataGenerator()->enrol_user($user->id, $course->id); + $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop'); + + $id = $workshopgenerator->create_submission($workshop->id, $user->id, array( + 'title' => 'My custom title', + )); + + $submissions = $DB->get_records('workshop_submissions', array('workshopid' => $workshop->id)); + $this->assertEquals(1, count($submissions)); + $this->assertTrue(isset($submissions[$id])); + $this->assertEquals($submissions[$id]->authorid, $user->id); + $this->assertSame('My custom title', $submissions[$id]->title); + } + + public function test_create_assessment() { + global $DB; + $this->resetAfterTest(); + $this->setAdminUser(); + + $course = $this->getDataGenerator()->create_course(); + $workshop = $this->getDataGenerator()->create_module('workshop', array('course' => $course)); + $user1 = $this->getDataGenerator()->create_user(); + $user2 = $this->getDataGenerator()->create_user(); + $this->getDataGenerator()->enrol_user($user1->id, $course->id); + $this->getDataGenerator()->enrol_user($user2->id, $course->id); + $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop'); + + $submissionid1 = $workshopgenerator->create_submission($workshop->id, $user1->id); + $submissionid2 = $workshopgenerator->create_submission($workshop->id, $user2->id); + + $assessmentid1 = $workshopgenerator->create_assessment($submissionid1, $user2->id, array( + 'weight' => 3, + 'grade' => 95.00000, + )); + $assessmentid2 = $workshopgenerator->create_assessment($submissionid2, $user1->id); + + $assessments = $DB->get_records('workshop_assessments'); + $this->assertTrue(isset($assessments[$assessmentid1])); + $this->assertTrue(isset($assessments[$assessmentid2])); + $this->assertEquals(3, $assessments[$assessmentid1]->weight); + $this->assertEquals(95.00000, $assessments[$assessmentid1]->grade); + $this->assertEquals(1, $assessments[$assessmentid2]->weight); + $this->assertNull($assessments[$assessmentid2]->grade); + } } From abb876c3345414f2b0c9abef62237b9175f83651 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Mudr=C3=A1k?= Date: Thu, 18 Sep 2014 13:52:47 +0200 Subject: [PATCH 7/7] MDL-31936 workshop: Improve unit tests for the course reset support --- mod/workshop/tests/fixtures/testable.php | 15 --- mod/workshop/tests/locallib_test.php | 120 ++++++++++++++++------- 2 files changed, 86 insertions(+), 49 deletions(-) diff --git a/mod/workshop/tests/fixtures/testable.php b/mod/workshop/tests/fixtures/testable.php index 50a0906c48f..e7e1cd3e20b 100644 --- a/mod/workshop/tests/fixtures/testable.php +++ b/mod/workshop/tests/fixtures/testable.php @@ -38,19 +38,4 @@ class testable_workshop extends workshop { parent::aggregate_grading_grades_process($assessments, $timegraded); } - public function reset_submissions() { - parent::reset_submissions(); - } - - public function reset_allocators() { - parent::reset_allocators(); - } - - public function reset_strategies() { - parent::reset_strategies(); - } - - public function reset_evaluators() { - parent::reset_evaulators(); - } } diff --git a/mod/workshop/tests/locallib_test.php b/mod/workshop/tests/locallib_test.php index d182f26ca48..e79fcc5e062 100644 --- a/mod/workshop/tests/locallib_test.php +++ b/mod/workshop/tests/locallib_test.php @@ -417,44 +417,96 @@ class mod_workshop_internal_api_testcase extends advanced_testcase { $a = $this->workshop->prepare_example_reference_assessment($fakerawrecord); } - public function test_reset_userdata() { + /** + * Test the workshop reset feature. + */ + public function test_reset_phase() { $this->resetAfterTest(true); - // $this->workshop is the instance we can use to test. - $this->workshop->switch_phase(workshop::PHASE_CLOSED); // Ensure workshop is a state that indicates work done + + $this->workshop->switch_phase(workshop::PHASE_CLOSED); $this->assertEquals(workshop::PHASE_CLOSED, $this->workshop->phase); - $originaleventcount = $DB->count_records('event', array('modulename' => 'workshop', 'instance' => $this->workshop->id)); - // Setup some data - /* - * Need to : - * 1. Setup (already done) - * 2. Move to submission phase - * 3. Submit - * 4. Move to assessmetn phase. - * 5. Record Assess - * 6. Move to Evaulation phase - * 7. Evaluate - */ - - // Setup reset data - $data = new stdClass(); - $data->reset_workshop_all = 1; - $data->reset_gradebook_grades = 1; - $data->courseid = $this->course->id; - $data->timeshift = 24 * 60 * 60; - $this->setUser($this->editingteachers[0]); + $settings = (object)array( + 'reset_workshop_phase' => 0, + ); + $status = $this->workshop->reset_userdata($settings); + $this->assertEquals(workshop::PHASE_CLOSED, $this->workshop->phase); - // Get various counts. - $status = $this->workshop->reset_userdata($data); - $posteventcount = $DB->count_records('event', array('modulename' => 'workshop', 'instance' => $this->workshop->id)); - $countaggregations = $DB->count_records('workshop_aggregations', array('workshopid' => $this->workshop->id)); - $fs = get_file_storage(); - $files = $fs->get_area_files($this->context, 'mod_workshop', 'submission_attachment', false); + $settings = (object)array( + 'reset_workshop_phase' => 1, + ); + $status = $this->workshop->reset_userdata($settings); + $this->assertEquals(workshop::PHASE_SETUP, $this->workshop->phase); + foreach ($status as $result) { + $this->assertFalse($result['error']); + } + } - // Check results - $this->assertEquals(0, $countaggregations, 'Aggregations should be empty'); - $this->assertEquals(workshop::PHASE_SETUP, $this->workshop->phase, 'Phase should have changed to SETUP'); - $this->assertEquals(0, $posteventcount, 'Calendar event count should equal 0.'); - $this->assertEqual(0, count($files), 'Number of files should be 0.'); + /** + * Test deleting assessments related data on workshop reset. + */ + public function test_reset_userdata_assessments() { + global $DB; + $this->resetAfterTest(true); + + $student1 = $this->getDataGenerator()->create_user(); + $student2 = $this->getDataGenerator()->create_user(); + + $this->getDataGenerator()->enrol_user($student1->id, $this->workshop->course->id); + $this->getDataGenerator()->enrol_user($student2->id, $this->workshop->course->id); + + $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop'); + + $subid1 = $workshopgenerator->create_submission($this->workshop->id, $student1->id); + $subid2 = $workshopgenerator->create_submission($this->workshop->id, $student2->id); + + $asid1 = $workshopgenerator->create_assessment($subid1, $student2->id); + $asid2 = $workshopgenerator->create_assessment($subid2, $student1->id); + + $settings = (object)array( + 'reset_workshop_assessments' => 1, + ); + $status = $this->workshop->reset_userdata($settings); + + foreach ($status as $result) { + $this->assertFalse($result['error']); + } + + $this->assertEquals(2, $DB->count_records('workshop_submissions', array('workshopid' => $this->workshop->id))); + $this->assertEquals(0, $DB->count_records('workshop_assessments')); + } + + /** + * Test deleting submissions related data on workshop reset. + */ + public function test_reset_userdata_submissions() { + global $DB; + $this->resetAfterTest(true); + + $student1 = $this->getDataGenerator()->create_user(); + $student2 = $this->getDataGenerator()->create_user(); + + $this->getDataGenerator()->enrol_user($student1->id, $this->workshop->course->id); + $this->getDataGenerator()->enrol_user($student2->id, $this->workshop->course->id); + + $workshopgenerator = $this->getDataGenerator()->get_plugin_generator('mod_workshop'); + + $subid1 = $workshopgenerator->create_submission($this->workshop->id, $student1->id); + $subid2 = $workshopgenerator->create_submission($this->workshop->id, $student2->id); + + $asid1 = $workshopgenerator->create_assessment($subid1, $student2->id); + $asid2 = $workshopgenerator->create_assessment($subid2, $student1->id); + + $settings = (object)array( + 'reset_workshop_submissions' => 1, + ); + $status = $this->workshop->reset_userdata($settings); + + foreach ($status as $result) { + $this->assertFalse($result['error']); + } + + $this->assertEquals(0, $DB->count_records('workshop_submissions', array('workshopid' => $this->workshop->id))); + $this->assertEquals(0, $DB->count_records('workshop_assessments')); } }