MDL-37993 Quiz: completion upon all passing grade or attempts exhausted

This patch adds completion options to Quiz similar to what is available in scorm.
One can have the quiz marked complete when either a passing grade is achieved or
all attempts are used up. This will allow a quiz to complete when the user "passes
or fails".  (Where "fail" means "using up all attempts without passing".)
This commit is contained in:
Ray Morris 2014-05-30 11:39:42 -05:00
parent 7784c3ad18
commit db3686d546
10 changed files with 298 additions and 2 deletions

View File

@ -49,7 +49,7 @@ class backup_quiz_activity_structure_step extends backup_questions_activity_stru
'questionsperpage', 'navmethod', 'shufflequestions', 'shuffleanswers',
'sumgrades', 'grade', 'timecreated',
'timemodified', 'password', 'subnet', 'browsersecurity',
'delay1', 'delay2', 'showuserpicture', 'showblocks'));
'delay1', 'delay2', 'showuserpicture', 'showblocks', 'completionattemptsexhausted', 'completionpass'));
// Define elements for access rule subplugin settings.
$this->add_subplugin_structure('quizaccess', $quiz, true);

View File

@ -44,6 +44,8 @@
<FIELD NAME="delay2" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Delay that must be left between the second and subsequent attempt, in seconds."/>
<FIELD NAME="showuserpicture" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Option to show the user's picture during the attempt and on the review page."/>
<FIELD NAME="showblocks" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Whether blocks should be shown on the attempt.php and review.php pages."/>
<FIELD NAME="completionattemptsexhausted" TYPE="int" LENGTH="1" NOTNULL="false" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="completionpass" TYPE="int" LENGTH="1" NOTNULL="false" DEFAULT="0" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>

View File

@ -776,6 +776,33 @@ function xmldb_quiz_upgrade($oldversion) {
// Moodle v2.7.0 release upgrade line.
// Put any upgrade step following this.
if ($oldversion < 2014052800) {
// Define field completionattemptsexhausted to be added to quiz.
$table = new xmldb_table('quiz');
$field = new xmldb_field('completionattemptsexhausted', XMLDB_TYPE_INTEGER, '1', null, null, null, '0', 'showblocks');
// Conditionally launch add field completionattemptsexhausted.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Quiz savepoint reached.
upgrade_mod_savepoint(true, 2014052800, 'quiz');
}
if ($oldversion < 2014052801) {
// Define field completionpass to be added to quiz.
$table = new xmldb_table('quiz');
$field = new xmldb_field('completionpass', XMLDB_TYPE_INTEGER, '1', null, null, null, 0, 'completionattemptsexhausted');
// Conditionally launch add field completionpass.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Quiz savepoint reached.
upgrade_mod_savepoint(true, 2014052801, 'quiz');
}
return true;
}

View File

@ -152,6 +152,10 @@ $string['comment'] = 'Comment';
$string['commentorgrade'] = 'Make comment or override grade';
$string['comments'] = 'Comments';
$string['completedon'] = 'Completed on';
$string['completionpass'] = 'Require passing grade';
$string['completionpass_help'] = 'If enabled, this activity is considered complete when the student receives a passing grade, with the pass grade set in the gradebook.';
$string['completionattemptsexhausted'] = 'Or all available attempts completed';
$string['completionattemptsexhausted_help'] = 'Mark quiz complete when the student has exhausted the maximum number of attempts.';
$string['configadaptive'] = 'If you choose Yes for this option then the student will be allowed multiple responses to a question even within the same attempt at the quiz.';
$string['configattemptsallowed'] = 'Restriction on the number of attempts students are allowed at the quiz.';
$string['configdecimaldigits'] = 'Number of digits that should be shown after the decimal point when displaying grades.';

View File

@ -1560,6 +1560,7 @@ function quiz_supports($feature) {
case FEATURE_GROUPMEMBERSONLY: return true;
case FEATURE_MOD_INTRO: return true;
case FEATURE_COMPLETION_TRACKS_VIEWS: return true;
case FEATURE_COMPLETION_HAS_RULES: return true;
case FEATURE_GRADE_HAS_GRADE: return true;
case FEATURE_GRADE_OUTCOMES: return true;
case FEATURE_BACKUP_MOODLE2: return true;
@ -1790,3 +1791,52 @@ function quiz_get_navigation_options() {
QUIZ_NAVMETHOD_SEQ => get_string('navmethod_seq', 'quiz')
);
}
/**
* Obtains the automatic completion state for this quiz on any conditions
* in quiz settings, such as if all attempts are used or a certain grade is achieved.
*
* @param object $course Course
* @param object $cm Course-module
* @param int $userid User ID
* @param bool $type Type of comparison (or/and; can be used as return value if no conditions)
* @return bool True if completed, false if not. (If no conditions, then return
* value depends on comparison type)
*/
function quiz_get_completion_state($course, $cm, $userid, $type) {
global $DB;
global $CFG;
$quiz = $DB->get_record('quiz', array('id' => $cm->instance), '*', MUST_EXIST);
if (!$quiz->completionattemptsexhausted && !$quiz->completionpass) {
return $type;
}
// Check if the user has used up all attempts.
if ($quiz->completionattemptsexhausted) {
$attempts = quiz_get_user_attempts($quiz->id, $userid, 'finished', true);
if ($attempts) {
$lastfinishedattempt = end($attempts);
$context = context_module::instance($cm->id);
$quizobj = quiz::create($quiz->id, $userid);
$accessmanager = new quiz_access_manager($quizobj, time(),
has_capability('mod/quiz:ignoretimelimits', $context, $userid, false));
if ($accessmanager->is_finished(count($attempts), $lastfinishedattempt)) {
return true;
}
}
}
// Check for passing grade.
if ($quiz->completionpass) {
require_once($CFG->libdir . '/gradelib.php');
$item = grade_item::fetch(array('courseid' => $course->id, 'itemtype' => 'mod',
'itemmodule' => 'quiz', 'iteminstance' => $cm->instance));
if ($item) {
$grades = grade_grade::fetch_users_grades($item, array($userid), false);
return $grades[$userid]->is_passed($item);
}
}
return false;
}

View File

@ -1641,6 +1641,11 @@ function quiz_attempt_submitted_handler($event) {
return true;
}
// Update completion state.
$completion = new completion_info($course);
if ($completion->is_enabled($cm) && ($quiz->completionattemptsexhausted || $quiz->completionpass)) {
$completion->update_state($cm, COMPLETION_COMPLETE, $event->userid);
}
return quiz_send_notification_messages($course, $quiz, $attempt,
context_module::instance($cm->id), $cm);
}

View File

@ -585,4 +585,37 @@ class mod_quiz_mod_form extends moodleform_mod {
return $errors;
}
/**
* Display module-specific activity completion rules.
* Part of the API defined by moodleform_mod
* @return array Array of string IDs of added items, empty array if none
*/
public function add_completion_rules() {
$mform = $this->_form;
$items = array();
$group = array();
$group[] = $mform->createElement('advcheckbox', 'completionpass', null, get_string('completionpass', 'quiz'),
array('group' => 'cpass'));
$group[] = $mform->createElement('advcheckbox', 'completionattemptsexhausted', null,
get_string('completionattemptsexhausted', 'quiz'),
array('group' => 'cattempts'));
$mform->disabledIf('completionattemptsexhausted', 'completionpass', 'notchecked');
$mform->addGroup($group, 'completionpassgroup', get_string('completionpass', 'quiz'), '', false);
$mform->addHelpButton('completionpassgroup', 'completionpass', 'quiz');
$items[] = 'completionpassgroup';
return $items;
}
/**
* Called during validation. Indicates whether a module-specific completion rule is selected.
*
* @param array $data Input data (not yet validated)
* @return bool True if one or more rules is enabled, false if none are.
*/
public function completion_rule_enabled($data) {
return !empty($data['completionattemptsexhausted']) || !empty($data['completionpass']);
}
}

View File

@ -0,0 +1,88 @@
@mod @mod_quiz
Feature: Set a quiz to be marked complete when the student uses all attempts allowed
In order to ensure a student has learned the material before being marked complete
As a teacher
I need to set a quiz to complete when the student receives a passing grade, or completed_fail if they use all attempts without passing
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@asd.com |
| teacher1 | Teacher | 1 | teacher1@asd.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And I log in as "admin"
And I set the following administration settings values:
| Enable completion tracking | 1 |
And I expand "Grades" node
And I follow "Grade item settings"
And I set the field "Advanced grade item options" to "hiddenuntil"
And I press "Save changes"
And I log out
Scenario: student1 uses up both attempts without passing
When I log in as "teacher1"
And I follow "Course 1"
And I turn editing mode on
And I click on "Edit settings" "link" in the "Administration" "block"
And I set the following fields to these values:
| Enable completion tracking | Yes |
And I press "Save changes"
And I add a "Quiz" to section "1" and I fill the form with:
| Name | Test quiz name |
| Description | Test quiz description |
| Completion tracking | Show activity as complete when conditions are met |
| Attempts allowed | 2 |
| Require passing grade | 1 |
| Or all available attempts completed | 1 |
And I add a "True/False" question to the "Test quiz name" quiz with:
| Question name | First question |
| Question text | Answer the first question |
| General feedback | Thank you, this is the general feedback |
| Correct answer | True |
| Feedback for the response 'True'. | So you think it is true |
| Feedback for the response 'False'. | So you think it is false |
And I follow "Course 1"
And I follow "Grades"
And I follow "Simple view"
And I follow "Edit quiz Test quiz name"
Then I should see "Edit grade item"
And I set the field "gradepass" to "5"
And I press "Save changes"
And I should see "Simple view"
Then I log out
And I log in as "student1"
And I follow "Course 1"
And "//img[contains(@alt, 'Not completed: Test quiz name')]" "xpath_element" should exist in the "li.modtype_quiz" "css_element"
And I follow "Test quiz name"
And I press "Attempt quiz now"
And I should see "Question 1"
And I should see "Answer the first question"
And I set the field "False" to "1"
And I press "Next"
And I should see "Answer saved"
And I press "Submit all and finish"
And I follow "C1"
And "//img[contains(@alt, 'Not completed: Test quiz name')]" "xpath_element" should exist in the "li.modtype_quiz" "css_element"
And I follow "Test quiz name"
And I press "Re-attempt quiz"
Then I should see "Question 1"
And I should see "Answer the first question"
And I set the field "False" to "1"
And I press "Next"
And I should see "Answer saved"
And I press "Submit all and finish"
And I follow "C1"
And "//img[contains(@alt, 'Completed: Test quiz name')]" "xpath_element" should exist in the "li.modtype_quiz" "css_element"
And I log out
And I log in as "teacher1"
And I follow "Course 1"
And I follow "Activity completion"
Then "//img[contains(@title,'Test quiz name') and @alt='Completed']" "xpath_element" should exist in the "Student 1" "table_row"

View File

@ -0,0 +1,87 @@
@mod @mod_quiz
Feature: Set a quiz to be marked complete when the student passes
In order to ensure a student has learned the material before being marked complete
As a teacher
I need to set a quiz to complete when the student recieves a passing grade
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@asd.com |
| teacher1 | Teacher | 1 | teacher1@asd.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And I log in as "admin"
And I set the following administration settings values:
| Enable completion tracking | 1 |
And I expand "Grades" node
And I follow "Grade item settings"
And I set the field "Advanced grade item options" to "hiddenuntil"
And I press "Save changes"
And I log out
Scenario: student1 passes on the first try
When I log in as "teacher1"
And I follow "Course 1"
And I turn editing mode on
And I click on "Edit settings" "link" in the "Administration" "block"
And I set the following fields to these values:
| Enable completion tracking | Yes |
And I press "Save changes"
And I add a "Quiz" to section "1" and I fill the form with:
| Name | Test quiz name |
| Description | Test quiz description |
| Completion tracking | Show activity as complete when conditions are met |
| Attempts allowed | 4 |
| Require passing grade | 1 |
And I add a "True/False" question to the "Test quiz name" quiz with:
| Question name | First question |
| Question text | Answer the first question |
| General feedback | Thank you, this is the general feedback |
| Correct answer | True |
| Feedback for the response 'True'. | So you think it is true |
| Feedback for the response 'False'. | So you think it is false |
And I follow "Course 1"
And I follow "Grades"
And I select "Simple view" from "jump"
And I press "Go"
And I follow "Edit quiz Test quiz name"
Then I should see "Edit grade item"
And I set the field "gradepass" to "5"
And I press "Save changes"
Then I should see "Simple view"
And I log out
And I log in as "student1"
And I follow "Course 1"
And "//img[contains(@alt, 'Not completed: Test quiz name')]" "xpath_element" should exist in the "li.modtype_quiz" "css_element"
And I follow "Test quiz name"
And I press "Attempt quiz now"
Then I should see "Question 1"
And I should see "Answer the first question"
And I set the field "False" to "1"
And I press "Next"
And I should see "Answer saved"
And I press "Submit all and finish"
And I follow "C1"
And "//img[contains(@alt, 'Not completed: Test quiz name')]" "xpath_element" should exist in the "li.modtype_quiz" "css_element"
And I follow "Test quiz name"
And I press "Re-attempt quiz"
Then I should see "Question 1"
And I should see "Answer the first question"
And I set the field "True" to "1"
And I press "Next"
And I should see "Answer saved"
And I press "Submit all and finish"
And I follow "C1"
And "//img[contains(@alt, 'Completed: Test quiz name')]" "xpath_element" should exist in the "li.modtype_quiz" "css_element"
And I log out
And I log in as "teacher1"
And I follow "Course 1"
And I follow "Activity completion"
Then "//img[contains(@title,'Test quiz name') and @alt='Completed']" "xpath_element" should exist in the "Student 1" "table_row"

View File

@ -24,7 +24,7 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2014051200; // The current module version (Date: YYYYMMDDXX).
$plugin->version = 2014052801; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2014050800; // Requires this Moodle version.
$plugin->component = 'mod_quiz'; // Full name of the plugin (used for diagnostics).
$plugin->cron = 60;