Merge branch 'MDL-31936-27-workshop-reset' of git://github.com/mudrd8mz/moodle into MOODLE_27_STABLE

This commit is contained in:
Marina Glancy
2014-10-07 10:10:06 +08:00
6 changed files with 437 additions and 9 deletions
+6
View File
@@ -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';
+77
View File
@@ -1710,3 +1710,80 @@ function workshop_calendar_update(stdClass $workshop, $cmid) {
$oldevent->delete();
}
}
////////////////////////////////////////////////////////////////////////////////
// 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');
}
/**
* 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;
}
/**
* 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;
if (empty($data->reset_workshop_submissions)
and empty($data->reset_workshop_assessments)
and empty($data->reset_workshop_phase) ) {
// Nothing to do here.
return array();
}
$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 ($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;
}
+149 -9
View File
@@ -1046,8 +1046,14 @@ 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));
$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 +1257,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;
}
/**
@@ -2354,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) //
////////////////////////////////////////////////////////////////////////////////
@@ -2611,6 +2698,59 @@ class workshop {
return substr($fullurl->out(), strlen($baseurl));
}
/**
* 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.
*/
protected function reset_userdata_assessments(stdClass $data) {
global $DB;
$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))";
$assessments = $DB->get_records_sql($sql, array('workshopid' => $this->id));
$this->delete_assessment(array_keys($assessments));
$DB->delete_records('workshop_aggregations', array('workshopid' => $this->id));
return true;
}
/**
* 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_userdata_submissions(stdClass $data) {
global $DB;
$submissions = $this->get_submissions();
foreach ($submissions as $submission) {
$this->delete_submission($submission);
}
return true;
}
/**
* Hard set the workshop phase to the setup one.
*/
protected function reset_phase() {
global $DB;
$DB->set_field('workshop', 'phase', self::PHASE_SETUP, array('id' => $this->id));
$this->phase = self::PHASE_SETUP;
}
}
////////////////////////////////////////////////////////////////////////////////
@@ -3428,10 +3568,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;
}
+59
View File
@@ -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;
}
}
+53
View File
@@ -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);
}
}
+93
View File
@@ -416,4 +416,97 @@ class mod_workshop_internal_api_testcase extends advanced_testcase {
// excersise SUT
$a = $this->workshop->prepare_example_reference_assessment($fakerawrecord);
}
/**
* Test the workshop reset feature.
*/
public function test_reset_phase() {
$this->resetAfterTest(true);
$this->workshop->switch_phase(workshop::PHASE_CLOSED);
$this->assertEquals(workshop::PHASE_CLOSED, $this->workshop->phase);
$settings = (object)array(
'reset_workshop_phase' => 0,
);
$status = $this->workshop->reset_userdata($settings);
$this->assertEquals(workshop::PHASE_CLOSED, $this->workshop->phase);
$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']);
}
}
/**
* 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'));
}
}