MDL-74533 core_completion: Update using new grade pass for completion
This commit is contained in:
+1
-2
@@ -678,6 +678,7 @@ function update_moduleinfo($cm, $moduleinfo, $course, $mform = null) {
|
||||
if (core_tag_tag::is_enabled('core', 'course_modules') && isset($moduleinfo->tags)) {
|
||||
core_tag_tag::set_item_tags('core', 'course_modules', $moduleinfo->coursemodule, $modcontext, $moduleinfo->tags);
|
||||
}
|
||||
$moduleinfo = edit_module_post_actions($moduleinfo, $course);
|
||||
|
||||
// Now that module is fully updated, also update completion data if required.
|
||||
// (this will wipe all user completion data and recalculate it)
|
||||
@@ -691,8 +692,6 @@ function update_moduleinfo($cm, $moduleinfo, $course, $mform = null) {
|
||||
$cm->name = $moduleinfo->name;
|
||||
\core\event\course_module_updated::create_from_cm($cm, $modcontext)->trigger();
|
||||
|
||||
$moduleinfo = edit_module_post_actions($moduleinfo, $course);
|
||||
|
||||
return array($cm, $moduleinfo);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
@mod @mod_quiz @core_completion
|
||||
Feature: Activity completion in the quiz activity with unlocked and re-grading.
|
||||
In order to have visibility of quiz completion requirements
|
||||
As a student
|
||||
I need to be able to view my quiz completion progress even teacher have re-grading the grade pass.
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| student1 | Student | 1 | student1@example.com |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | category | enablecompletion |
|
||||
| Course 1 | C1 | 0 | 1 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
And the following config values are set as admin:
|
||||
| grade_item_advanced | hiddenuntil |
|
||||
And the following "question categories" exist:
|
||||
| contextlevel | reference | name |
|
||||
| Course | C1 | Test questions |
|
||||
And the following "questions" exist:
|
||||
| questioncategory | qtype | name | questiontext | defaultmark |
|
||||
| Test questions | truefalse | First question | Answer the first question | 8 |
|
||||
| Test questions | truefalse | Second question | Answer the second question | 2 |
|
||||
And the following "activity" exists:
|
||||
| activity | quiz |
|
||||
| course | C1 |
|
||||
| idnumber | quiz1 |
|
||||
| name | Test quiz name |
|
||||
| section | 1 |
|
||||
| gradepass | 10.00 |
|
||||
| grade | 10 |
|
||||
| grademethod | 1 |
|
||||
| completion | 2 |
|
||||
| completionusegrade | 1 |
|
||||
| completionpassgrade | 1 |
|
||||
And quiz "Test quiz name" contains the following questions:
|
||||
| question | page |
|
||||
| First question | 1 |
|
||||
| Second question | 2 |
|
||||
|
||||
@javascript
|
||||
Scenario: Student will receive correct completion even when teacher unlocked completion and re-grading.
|
||||
Given I am on the "Test quiz name" "quiz activity" page logged in as student1
|
||||
And the "Receive a grade" completion condition of "Test quiz name" is displayed as "todo"
|
||||
And the "Receive a passing grade" completion condition of "Test quiz name" is displayed as "todo"
|
||||
And user "student1" has attempted "Test quiz name" with responses:
|
||||
| slot | response |
|
||||
| 1 | True |
|
||||
| 2 | False |
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Test quiz name"
|
||||
And the "Receive a grade" completion condition of "Test quiz name" is displayed as "done"
|
||||
And the "Receive a passing grade" completion condition of "Test quiz name" is displayed as "failed"
|
||||
And I log out
|
||||
When I am on the "Test quiz name" "quiz activity" page logged in as teacher1
|
||||
And I navigate to "Settings" in current page administration
|
||||
And I expand all fieldsets
|
||||
And I press "Unlock completion options"
|
||||
And I set the following fields to these values:
|
||||
| gradepass | 8 |
|
||||
And I press "Save and return to course"
|
||||
And I navigate to "Reports > Activity completion" in current page administration
|
||||
Then "Completed (achieved pass grade)" "icon" should exist in the "Student 1" "table_row"
|
||||
@@ -46,7 +46,7 @@ class custom_completion_test extends advanced_testcase {
|
||||
* Setup function for all tests.
|
||||
*
|
||||
* @param array $completionoptions ['nbstudents'] => int, ['qtype'] => string, ['quizoptions'] => array
|
||||
* @return array [$students, $quiz, $cm]
|
||||
* @return array [$students, $quiz, $cm, $litecm]
|
||||
*/
|
||||
private function setup_quiz_for_testing_completion(array $completionoptions): array {
|
||||
global $CFG, $DB;
|
||||
@@ -60,6 +60,8 @@ class custom_completion_test extends advanced_testcase {
|
||||
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
|
||||
$course = $this->getDataGenerator()->create_course(['enablecompletion' => true]);
|
||||
$students = [];
|
||||
$sumgrades = $completionoptions['sumgrades'] ?? 1;
|
||||
$nbquestions = $completionoptions['nbquestions'] ?? 1;
|
||||
for ($i = 0; $i < $completionoptions['nbstudents']; $i++) {
|
||||
$students[$i] = $this->getDataGenerator()->create_user();
|
||||
$this->assertTrue($this->getDataGenerator()->enrol_user($students[$i]->id, $course->id, $studentrole->id));
|
||||
@@ -71,30 +73,36 @@ class custom_completion_test extends advanced_testcase {
|
||||
'course' => $course->id,
|
||||
'grade' => 100.0,
|
||||
'questionsperpage' => 0,
|
||||
'sumgrades' => 1,
|
||||
'sumgrades' => $sumgrades,
|
||||
'completion' => COMPLETION_TRACKING_AUTOMATIC
|
||||
], $completionoptions['quizoptions']);
|
||||
$quiz = $quizgenerator->create_instance($data);
|
||||
$cm = get_coursemodule_from_id('quiz', $quiz->cmid);
|
||||
$cm = cm_info::create($cm);
|
||||
$litecm = get_coursemodule_from_id('quiz', $quiz->cmid);
|
||||
$cm = cm_info::create($litecm);
|
||||
|
||||
// Create a question.
|
||||
$questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||
|
||||
$cat = $questiongenerator->create_question_category();
|
||||
$question = $questiongenerator->create_question($completionoptions['qtype'], null, ['category' => $cat->id]);
|
||||
quiz_add_quiz_question($question->id, $quiz);
|
||||
for ($i = 0; $i < $nbquestions; $i++) {
|
||||
$overrideparams = ['category' => $cat->id];
|
||||
if (isset($completionoptions['questiondefaultmarks'][$i])) {
|
||||
$overrideparams['defaultmark'] = $completionoptions['questiondefaultmarks'][$i];
|
||||
}
|
||||
$question = $questiongenerator->create_question($completionoptions['qtype'], null, $overrideparams);
|
||||
quiz_add_quiz_question($question->id, $quiz);
|
||||
}
|
||||
|
||||
// Set grade to pass.
|
||||
$item = grade_item::fetch(['courseid' => $course->id, 'itemtype' => 'mod', 'itemmodule' => 'quiz',
|
||||
'iteminstance' => $quiz->id, 'outcomeid' => null]);
|
||||
$item->gradepass = 80;
|
||||
$item->update();
|
||||
|
||||
return [
|
||||
$students,
|
||||
$quiz,
|
||||
$cm
|
||||
$cm,
|
||||
$litecm
|
||||
];
|
||||
}
|
||||
|
||||
@@ -324,4 +332,173 @@ class custom_completion_test extends advanced_testcase {
|
||||
$rules
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test update moduleinfo.
|
||||
*
|
||||
* @covers \update_moduleinfo
|
||||
*/
|
||||
public function test_update_moduleinfo() {
|
||||
$this->setAdminUser();
|
||||
// We need lite cm object not a full cm because update_moduleinfo is not allow some properties to be updated.
|
||||
list($students, $quiz, $cm, $litecm) = $this->setup_quiz_for_testing_completion([
|
||||
'nbstudents' => 1,
|
||||
'qtype' => 'numerical',
|
||||
'nbquestions' => 2,
|
||||
'sumgrades' => 100,
|
||||
'questiondefaultmarks' => [20, 80],
|
||||
'quizoptions' => [
|
||||
'completionusegrade' => 1,
|
||||
'completionpassgrade' => 1,
|
||||
'completionview' => 0,
|
||||
]
|
||||
]);
|
||||
$course = $cm->get_course();
|
||||
|
||||
list($student) = $students;
|
||||
// Do a first attempt with a pass marks = 20.
|
||||
$this->do_attempt_quiz([
|
||||
'quiz' => $quiz,
|
||||
'student' => $student,
|
||||
'attemptnumber' => 1,
|
||||
'tosubmit' => [1 => ['answer' => '3.14']]
|
||||
]);
|
||||
$completioninfo = new \completion_info($course);
|
||||
$cminfo = \cm_info::create($cm);
|
||||
$completiondetails = new cm_completion_details($completioninfo, $cminfo, (int) $student->id);
|
||||
|
||||
// Check the results. Completion is fail because gradepass = 80.
|
||||
$this->assertEquals(COMPLETION_COMPLETE_FAIL, $completiondetails->get_details()['completionpassgrade']->status);
|
||||
$this->assertEquals(
|
||||
'Receive a passing grade',
|
||||
$completiondetails->get_details()['completionpassgrade']->description
|
||||
);
|
||||
|
||||
// Update quiz with passgrade = 20 and use highest grade to calculate.
|
||||
$moduleinfo = $this->prepare_module_info($cm, $quiz, $course, 20, QUIZ_GRADEHIGHEST);
|
||||
update_moduleinfo($litecm, $moduleinfo, $course, null);
|
||||
|
||||
$completiondetails = new cm_completion_details($completioninfo, $cminfo, (int) $student->id);
|
||||
|
||||
// Check the results. Completion is pass.
|
||||
$this->assertEquals(COMPLETION_COMPLETE_PASS, $completiondetails->get_details()['completionpassgrade']->status);
|
||||
$this->assertEquals(
|
||||
'Receive a passing grade',
|
||||
$completiondetails->get_details()['completionpassgrade']->description
|
||||
);
|
||||
|
||||
// Do a second attempt with pass marks = 80.
|
||||
$this->do_attempt_quiz([
|
||||
'quiz' => $quiz,
|
||||
'student' => $student,
|
||||
'attemptnumber' => 2,
|
||||
'tosubmit' => [2 => ['answer' => '3.14']]
|
||||
]);
|
||||
|
||||
// Update quiz with gradepass = 80 and use highest grade to calculate completion.
|
||||
$moduleinfo = $this->prepare_module_info($cm, $quiz, $course, 80, QUIZ_GRADEHIGHEST);
|
||||
update_moduleinfo($litecm, $moduleinfo, $course, null);
|
||||
|
||||
$completiondetails = new cm_completion_details($completioninfo, $cminfo, (int) $student->id);
|
||||
|
||||
// Check the results. Completion is pass.
|
||||
$this->assertEquals(COMPLETION_COMPLETE_PASS, $completiondetails->get_details()['completionpassgrade']->status);
|
||||
$this->assertEquals(
|
||||
'Receive a passing grade',
|
||||
$completiondetails->get_details()['completionpassgrade']->description
|
||||
);
|
||||
|
||||
// Update quiz with gradepass = 80 and use average grade to calculate completion.
|
||||
$moduleinfo = $this->prepare_module_info($cm, $quiz, $course, 80, QUIZ_GRADEAVERAGE);
|
||||
update_moduleinfo($litecm, $moduleinfo, $course, null);
|
||||
|
||||
$completiondetails = new cm_completion_details($completioninfo, $cminfo, (int) $student->id);
|
||||
|
||||
// Check the results. Completion is fail because student grade = 50.
|
||||
$this->assertEquals(COMPLETION_COMPLETE_FAIL, $completiondetails->get_details()['completionpassgrade']->status);
|
||||
$this->assertEquals(
|
||||
'Receive a passing grade',
|
||||
$completiondetails->get_details()['completionpassgrade']->description
|
||||
);
|
||||
|
||||
// Update quiz with gradepass = 50 and use average grade to calculate completion.
|
||||
$moduleinfo = $this->prepare_module_info($cm, $quiz, $course, 50, QUIZ_GRADEAVERAGE);
|
||||
update_moduleinfo($litecm, $moduleinfo, $course, null);
|
||||
|
||||
$completiondetails = new cm_completion_details($completioninfo, $cminfo, (int) $student->id);
|
||||
|
||||
// Check the results. Completion is pass.
|
||||
$this->assertEquals(COMPLETION_COMPLETE_PASS, $completiondetails->get_details()['completionpassgrade']->status);
|
||||
$this->assertEquals(
|
||||
'Receive a passing grade',
|
||||
$completiondetails->get_details()['completionpassgrade']->description
|
||||
);
|
||||
|
||||
// Update quiz with gradepass = 50 and use first attempt grade to calculate completion.
|
||||
$moduleinfo = $this->prepare_module_info($cm, $quiz, $course, 50, QUIZ_ATTEMPTFIRST);
|
||||
update_moduleinfo($litecm, $moduleinfo, $course, null);
|
||||
|
||||
$completiondetails = new cm_completion_details($completioninfo, $cminfo, (int) $student->id);
|
||||
|
||||
// Check the results. Completion is fail.
|
||||
$this->assertEquals(COMPLETION_COMPLETE_FAIL, $completiondetails->get_details()['completionpassgrade']->status);
|
||||
$this->assertEquals(
|
||||
'Receive a passing grade',
|
||||
$completiondetails->get_details()['completionpassgrade']->description
|
||||
);
|
||||
// Update quiz with gradepass = 50 and use last attempt grade to calculate completion.
|
||||
$moduleinfo = $this->prepare_module_info($cm, $quiz, $course, 50, QUIZ_ATTEMPTLAST);
|
||||
update_moduleinfo($litecm, $moduleinfo, $course, null);
|
||||
|
||||
$completiondetails = new cm_completion_details($completioninfo, $cminfo, (int) $student->id);
|
||||
|
||||
// Check the results. Completion is fail.
|
||||
$this->assertEquals(COMPLETION_COMPLETE_PASS, $completiondetails->get_details()['completionpassgrade']->status);
|
||||
$this->assertEquals(
|
||||
'Receive a passing grade',
|
||||
$completiondetails->get_details()['completionpassgrade']->description
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up moduleinfo object sample data for quiz instance.
|
||||
*
|
||||
* @param object $cm course-module instance
|
||||
* @param object $quiz quiz instance data.
|
||||
* @param object $course Course related data.
|
||||
* @param int $gradepass Grade to pass and completed completion.
|
||||
* @param string $grademethod grade attempt method.
|
||||
* @return \stdClass
|
||||
*/
|
||||
private function prepare_module_info(object $cm, object $quiz, object $course, int $gradepass, string $grademethod): \stdClass {
|
||||
$grouping = $this->getDataGenerator()->create_grouping(['courseid' => $course->id]);
|
||||
// Module test values.
|
||||
$moduleinfo = new \stdClass();
|
||||
$moduleinfo->coursemodule = $cm->id;
|
||||
$moduleinfo->section = 1;
|
||||
$moduleinfo->course = $course->id;
|
||||
$moduleinfo->groupingid = $grouping->id;
|
||||
$draftideditor = 0;
|
||||
file_prepare_draft_area($draftideditor, null, null, null, null);
|
||||
$moduleinfo->introeditor = ['text' => 'This is a module', 'format' => FORMAT_HTML, 'itemid' => $draftideditor];
|
||||
$moduleinfo->modulename = 'quiz';
|
||||
$moduleinfo->quizpassword = '';
|
||||
$moduleinfo->cmidnumber = '';
|
||||
$moduleinfo->marksopen = 1;
|
||||
$moduleinfo->visible = 1;
|
||||
$moduleinfo->visibleoncoursepage = 1;
|
||||
$moduleinfo->completion = COMPLETION_TRACKING_AUTOMATIC;
|
||||
$moduleinfo->completionview = COMPLETION_VIEW_NOT_REQUIRED;
|
||||
$moduleinfo->name = $quiz->name;
|
||||
$moduleinfo->timeopen = $quiz->timeopen;
|
||||
$moduleinfo->timeclose = $quiz->timeclose;
|
||||
$moduleinfo->timelimit = $quiz->timelimit;
|
||||
$moduleinfo->graceperiod = $quiz->graceperiod;
|
||||
$moduleinfo->decimalpoints = $quiz->decimalpoints;
|
||||
$moduleinfo->questiondecimalpoints = $quiz->questiondecimalpoints;
|
||||
$moduleinfo->gradepass = $gradepass;
|
||||
$moduleinfo->grademethod = $grademethod;
|
||||
|
||||
return $moduleinfo;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user