mirror of
https://github.com/moodle/moodle.git
synced 2025-01-17 13:38:32 +01:00
MDL-71196 completion: final removal of get_completion_state support.
This commit is contained in:
parent
a1d5d1b2f7
commit
334b6c75a4
@ -152,17 +152,6 @@ class cm_completion_details {
|
||||
|
||||
$details = $this->sort_completion_details($details);
|
||||
}
|
||||
} else {
|
||||
if (function_exists($this->cminfo->modname . '_get_completion_state')) {
|
||||
// If the plugin does not have the custom completion implementation but implements the get_completion_state() callback,
|
||||
// fallback to displaying the overall completion state of the activity.
|
||||
$details = [
|
||||
'plugincompletionstate' => (object)[
|
||||
'status' => $this->get_overall_completion(),
|
||||
'description' => get_string('completeactivity', 'completion')
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $details;
|
||||
|
@ -5,6 +5,8 @@ information provided here is intended especially for developers.
|
||||
* A trait class, core_completion/form/form_trait has been added to reuse code for adding and validation completion settings to any
|
||||
form.
|
||||
* New method is_manual() has been added to `core_completion/cm_completion_details`
|
||||
* Support for deprecated `[modname]_get_completion_state` callbacks has been removed, custom completion rules must be implemented
|
||||
by appropriate `mod_[modname]\completion\custom_completion` class instead
|
||||
|
||||
=== 4.0 ===
|
||||
* New method mark_course_completions_activity_criteria() has been added to mark course completions instantly. It is
|
||||
|
@ -720,19 +720,6 @@ class completion_info {
|
||||
/** @var activity_custom_completion $cmcompletion */
|
||||
$cmcompletion = new $cmcompletionclass($cminfo, $userid, $completionstate);
|
||||
$response = $cmcompletion->get_overall_completion_state() != COMPLETION_INCOMPLETE;
|
||||
} else {
|
||||
// Fallback to the get_completion_state callback.
|
||||
$cmcompletionclass = "mod_{$cminfo->modname}\\completion\\custom_completion";
|
||||
$function = $cminfo->modname . '_get_completion_state';
|
||||
if (!function_exists($function)) {
|
||||
$this->internal_systemerror("Module {$cminfo->modname} claims to support FEATURE_COMPLETION_HAS_RULES " .
|
||||
"but does not implement the custom completion class $cmcompletionclass which extends " .
|
||||
"\core_completion\activity_custom_completion.");
|
||||
}
|
||||
debugging("*_get_completion_state() callback functions such as $function have been deprecated and should no " .
|
||||
"longer be used. Please implement the custom completion class $cmcompletionclass which extends " .
|
||||
"\core_completion\activity_custom_completion.", DEBUG_DEVELOPER);
|
||||
$response = $function($this->course, $cm, $userid, COMPLETION_AND, $completionstate);
|
||||
}
|
||||
|
||||
if (!$response) {
|
||||
|
@ -23,39 +23,11 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Obtains the automatic completion state for this module based on any conditions
|
||||
* in assign settings.
|
||||
*
|
||||
* @deprecated since Moodle 3.11
|
||||
* @todo MDL-71196 Final deprecation in Moodle 4.3
|
||||
* @see \mod_assign\completion\custom_completion
|
||||
* @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, $type if conditions not set.
|
||||
*/
|
||||
function assign_get_completion_state($course, $cm, $userid, $type) {
|
||||
global $CFG;
|
||||
|
||||
// No need to call debugging here. Deprecation debugging notice already being called in \completion_info::internal_get_state().
|
||||
|
||||
require_once($CFG->dirroot . '/mod/assign/locallib.php');
|
||||
|
||||
$assign = new assign(null, $cm, $course);
|
||||
|
||||
// If completion option is enabled, evaluate it and return true/false.
|
||||
if ($assign->get_instance()->completionsubmit) {
|
||||
if ($assign->get_instance()->teamsubmission) {
|
||||
$submission = $assign->get_group_submission($userid, 0, false);
|
||||
} else {
|
||||
$submission = $assign->get_user_submission($userid, false);
|
||||
}
|
||||
return $submission && $submission->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED;
|
||||
} else {
|
||||
// Completion option is not enabled so just return $type.
|
||||
return $type;
|
||||
}
|
||||
function assign_get_completion_state() {
|
||||
$completionclass = \mod_assign\completion\custom_completion::class;
|
||||
throw new coding_exception(__FUNCTION__ . "() has been removed, please use the '{$completionclass}' class instead");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -176,38 +176,6 @@ class lib_test extends \advanced_testcase {
|
||||
$this->assertMatchesRegularExpression('/50.5/', $result->info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that assign_get_completion_state reflects the correct status at each point.
|
||||
*/
|
||||
public function test_assign_get_completion_state() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
|
||||
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
|
||||
$assign = $this->create_instance($course, [
|
||||
'submissiondrafts' => 0,
|
||||
'completionsubmit' => 1
|
||||
]);
|
||||
|
||||
$this->setUser($student);
|
||||
$result = assign_get_completion_state($course, $assign->get_course_module(), $student->id, false);
|
||||
$this->assertFalse($result);
|
||||
|
||||
$this->add_submission($student, $assign);
|
||||
$result = assign_get_completion_state($course, $assign->get_course_module(), $student->id, false);
|
||||
$this->assertFalse($result);
|
||||
|
||||
$this->submit_for_grading($student, $assign);
|
||||
$result = assign_get_completion_state($course, $assign->get_course_module(), $student->id, false);
|
||||
$this->assertTrue($result);
|
||||
|
||||
$this->mark_submission($teacher, $assign, $student, 50.0);
|
||||
$result = assign_get_completion_state($course, $assign->get_course_module(), $student->id, false);
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for mod_assign_refresh_events.
|
||||
*/
|
||||
|
@ -23,33 +23,9 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Obtains the automatic completion state for this choice based on any conditions
|
||||
* in forum settings.
|
||||
*
|
||||
* @deprecated since Moodle 3.11
|
||||
* @todo MDL-71196 Final deprecation in Moodle 4.3
|
||||
* @see \mod_choice\completion\custom_completion
|
||||
* @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, $type if conditions not set.
|
||||
*/
|
||||
function choice_get_completion_state($course, $cm, $userid, $type) {
|
||||
global $DB;
|
||||
|
||||
// No need to call debugging here. Deprecation debugging notice already being called in \completion_info::internal_get_state().
|
||||
|
||||
// Get choice details.
|
||||
$choice = $DB->get_record('choice', array('id' => $cm->instance), '*',
|
||||
MUST_EXIST);
|
||||
|
||||
// If completion option is enabled, evaluate it and return true/false.
|
||||
if ($choice->completionsubmit) {
|
||||
return $DB->record_exists('choice_answers', array(
|
||||
'choiceid' => $choice->id, 'userid' => $userid));
|
||||
} else {
|
||||
// Completion option is not enabled so just return $type.
|
||||
return $type;
|
||||
}
|
||||
function choice_get_completion_state() {
|
||||
$completionclass = \mod_choice\completion\custom_completion::class;
|
||||
throw new coding_exception(__FUNCTION__ . "() has been removed, please use the '{$completionclass}' class instead");
|
||||
}
|
||||
|
@ -23,43 +23,11 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Obtains the automatic completion state for this database item based on any conditions
|
||||
* on its settings. The call for this is in completion lib where the modulename is appended
|
||||
* to the function name. This is why there are unused parameters.
|
||||
*
|
||||
* @deprecated since Moodle 3.11
|
||||
* @todo MDL-71196 Final deprecation in Moodle 4.3
|
||||
* @see \mod_data\completion\custom_completion
|
||||
* @since Moodle 3.3
|
||||
* @param stdClass $course Course
|
||||
* @param cm_info|stdClass $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, $type if conditions not set.
|
||||
*/
|
||||
function data_get_completion_state($course, $cm, $userid, $type) {
|
||||
global $DB, $PAGE;
|
||||
|
||||
// No need to call debugging here. Deprecation debugging notice already being called in \completion_info::internal_get_state().
|
||||
|
||||
$result = $type; // Default return value
|
||||
// Get data details.
|
||||
if (isset($PAGE->cm->id) && $PAGE->cm->id == $cm->id) {
|
||||
$data = $PAGE->activityrecord;
|
||||
} else {
|
||||
$data = $DB->get_record('data', array('id' => $cm->instance), '*', MUST_EXIST);
|
||||
}
|
||||
// If completion option is enabled, evaluate it and return true/false.
|
||||
if ($data->completionentries) {
|
||||
$numentries = data_numentries($data, $userid);
|
||||
// Check the number of entries required against the number of entries already made.
|
||||
if ($numentries >= $data->completionentries) {
|
||||
$result = true;
|
||||
} else {
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
function data_get_completion_state() {
|
||||
$completionclass = \mod_data\completion\custom_completion::class;
|
||||
throw new coding_exception(__FUNCTION__ . "() has been removed, please use the '{$completionclass}' class instead");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,32 +23,9 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Obtains the automatic completion state for this feedback based on the condition
|
||||
* in feedback settings.
|
||||
*
|
||||
* @deprecated since Moodle 3.11
|
||||
* @todo MDL-71196 Final deprecation in Moodle 4.3
|
||||
* @see \mod_feedback\completion\custom_completion
|
||||
* @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, $type if conditions not set.
|
||||
*/
|
||||
function feedback_get_completion_state($course, $cm, $userid, $type) {
|
||||
global $DB;
|
||||
|
||||
// No need to call debugging here. Deprecation debugging notice already being called in \completion_info::internal_get_state().
|
||||
|
||||
// Get feedback details.
|
||||
$feedback = $DB->get_record('feedback', array('id' => $cm->instance), '*', MUST_EXIST);
|
||||
|
||||
// If completion option is enabled, evaluate it and return true/false.
|
||||
if ($feedback->completionsubmit) {
|
||||
$params = array('userid' => $userid, 'feedback' => $feedback->id);
|
||||
return $DB->record_exists('feedback_completed', $params);
|
||||
} else {
|
||||
// Completion option is not enabled so just return $type.
|
||||
return $type;
|
||||
}
|
||||
function feedback_get_completion_state() {
|
||||
$completionclass = \mod_feedback\completion\custom_completion::class;
|
||||
throw new coding_exception(__FUNCTION__ . "() has been removed, please use the '{$completionclass}' class instead");
|
||||
}
|
||||
|
@ -410,71 +410,11 @@ function forum_get_user_grades() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the automatic completion state for this forum based on any conditions
|
||||
* in forum settings.
|
||||
*
|
||||
* @deprecated since Moodle 3.11
|
||||
* @todo MDL-71196 Final deprecation in Moodle 4.3
|
||||
* @see \mod_forum\completion\custom_completion
|
||||
* @global object
|
||||
* @global object
|
||||
* @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 forum_get_completion_state($course, $cm, $userid, $type) {
|
||||
global $DB;
|
||||
|
||||
// No need to call debugging here. Deprecation debugging notice already being called in \completion_info::internal_get_state().
|
||||
|
||||
// Get forum details.
|
||||
if (!($forum = $DB->get_record('forum', array('id' => $cm->instance)))) {
|
||||
throw new Exception("Can't find forum {$cm->instance}");
|
||||
}
|
||||
|
||||
$result = $type; // Default return value.
|
||||
|
||||
$postcountparams = array('userid' => $userid, 'forumid' => $forum->id);
|
||||
$postcountsql = "
|
||||
SELECT
|
||||
COUNT(1)
|
||||
FROM
|
||||
{forum_posts} fp
|
||||
INNER JOIN {forum_discussions} fd ON fp.discussion=fd.id
|
||||
WHERE
|
||||
fp.userid=:userid AND fd.forum=:forumid";
|
||||
|
||||
if ($forum->completiondiscussions) {
|
||||
$value = $forum->completiondiscussions <=
|
||||
$DB->count_records('forum_discussions', array('forum' => $forum->id, 'userid' => $userid));
|
||||
if ($type == COMPLETION_AND) {
|
||||
$result = $result && $value;
|
||||
} else {
|
||||
$result = $result || $value;
|
||||
}
|
||||
}
|
||||
if ($forum->completionreplies) {
|
||||
$value = $forum->completionreplies <=
|
||||
$DB->get_field_sql($postcountsql . ' AND fp.parent<>0', $postcountparams);
|
||||
if ($type == COMPLETION_AND) {
|
||||
$result = $result && $value;
|
||||
} else {
|
||||
$result = $result || $value;
|
||||
}
|
||||
}
|
||||
if ($forum->completionposts) {
|
||||
$value = $forum->completionposts <= $DB->get_field_sql($postcountsql, $postcountparams);
|
||||
if ($type == COMPLETION_AND) {
|
||||
$result = $result && $value;
|
||||
} else {
|
||||
$result = $result || $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
function forum_get_completion_state() {
|
||||
$completionclass = \mod_forum\completion\custom_completion::class;
|
||||
throw new coding_exception(__FUNCTION__ . "() has been removed, please use the '{$completionclass}' class instead");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,39 +23,9 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Obtains the automatic completion state for this glossary based on any conditions
|
||||
* in glossary settings.
|
||||
*
|
||||
* @deprecated since Moodle 3.11
|
||||
* @todo MDL-71196 Final deprecation in Moodle 4.3
|
||||
* @see \mod_glossary\completion\custom_completion
|
||||
* @param stdClass $course Course
|
||||
* @param cm_info|stdClass $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 glossary_get_completion_state($course, $cm, $userid, $type) {
|
||||
global $DB;
|
||||
|
||||
// No need to call debugging here. Deprecation debugging notice already being called in \completion_info::internal_get_state().
|
||||
|
||||
// Get glossary details.
|
||||
if (!($glossary = $DB->get_record('glossary', array('id' => $cm->instance)))) {
|
||||
throw new Exception("Can't find glossary {$cm->instance}");
|
||||
}
|
||||
|
||||
$result = $type; // Default return value.
|
||||
|
||||
if ($glossary->completionentries) {
|
||||
$value = $glossary->completionentries <=
|
||||
$DB->count_records('glossary_entries', array('glossaryid' => $glossary->id, 'userid' => $userid, 'approved' => 1));
|
||||
if ($type == COMPLETION_AND) {
|
||||
$result = $result && $value;
|
||||
} else {
|
||||
$result = $result || $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
function glossary_get_completion_state() {
|
||||
$completionclass = \mod_glossary\completion\custom_completion::class;
|
||||
throw new coding_exception(__FUNCTION__ . "() has been removed, please use the '{$completionclass}' class instead");
|
||||
}
|
||||
|
@ -23,51 +23,9 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Obtains the automatic completion state for this lesson based on any conditions
|
||||
* in lesson settings.
|
||||
*
|
||||
* @deprecated since Moodle 3.11
|
||||
* @todo MDL-71196 Final deprecation in Moodle 4.3
|
||||
* @see \mod_lesson\completion\custom_completion
|
||||
* @param stdClass $course Course
|
||||
* @param cm_info|stdClass $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, $type if conditions not set.
|
||||
*/
|
||||
function lesson_get_completion_state($course, $cm, $userid, $type) {
|
||||
global $DB;
|
||||
|
||||
// No need to call debugging here. Deprecation debugging notice already being called in \completion_info::internal_get_state().
|
||||
|
||||
// Get lesson details.
|
||||
$lesson = $DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST);
|
||||
|
||||
$result = $type; // Default return value.
|
||||
// If completion option is enabled, evaluate it and return true/false.
|
||||
if ($lesson->completionendreached) {
|
||||
$value = $DB->record_exists('lesson_timer', array('lessonid' => $lesson->id, 'userid' => $userid, 'completed' => 1));
|
||||
if ($type == COMPLETION_AND) {
|
||||
$result = $result && $value;
|
||||
} else {
|
||||
$result = $result || $value;
|
||||
}
|
||||
}
|
||||
if ($lesson->completiontimespent != 0) {
|
||||
$duration = $DB->get_field_sql(
|
||||
"SELECT SUM(lessontime - starttime)
|
||||
FROM {lesson_timer}
|
||||
WHERE lessonid = :lessonid
|
||||
AND userid = :userid",
|
||||
array('userid' => $userid, 'lessonid' => $lesson->id));
|
||||
if (!$duration) {
|
||||
$duration = 0;
|
||||
}
|
||||
if ($type == COMPLETION_AND) {
|
||||
$result = $result && ($lesson->completiontimespent < $duration);
|
||||
} else {
|
||||
$result = $result || ($lesson->completiontimespent < $duration);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
function lesson_get_completion_state() {
|
||||
$completionclass = \mod_lesson\completion\custom_completion::class;
|
||||
throw new coding_exception(__FUNCTION__ . "() has been removed, please use the '{$completionclass}' class instead");
|
||||
}
|
||||
|
@ -27,114 +27,11 @@ use mod_quiz\quiz_settings;
|
||||
use mod_quiz\task\update_overdue_attempts;
|
||||
|
||||
/**
|
||||
* Internal function used in quiz_get_completion_state. Check passing grade (or no attempts left) requirement for completion.
|
||||
*
|
||||
* @deprecated since Moodle 3.11
|
||||
* @todo MDL-71196 Final deprecation in Moodle 4.3
|
||||
* @see \mod_quiz\completion\custom_completion
|
||||
* @param stdClass $course
|
||||
* @param cm_info|stdClass $cm
|
||||
* @param int $userid
|
||||
* @param stdClass $quiz
|
||||
* @return bool True if the passing grade (or no attempts left) requirement is disabled or met.
|
||||
* @throws coding_exception
|
||||
*/
|
||||
function quiz_completion_check_passing_grade_or_all_attempts($course, $cm, $userid, $quiz) {
|
||||
global $CFG;
|
||||
|
||||
debugging('quiz_completion_check_passing_grade_or_all_attempts has been deprecated.', DEBUG_DEVELOPER);
|
||||
|
||||
if (!$cm->completionpassgrade) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for passing grade.
|
||||
require_once($CFG->libdir . '/gradelib.php');
|
||||
$item = grade_item::fetch(['courseid' => $course->id, 'itemtype' => 'mod',
|
||||
'itemmodule' => 'quiz', 'iteminstance' => $cm->instance, 'outcomeid' => null]);
|
||||
if ($item) {
|
||||
$grades = grade_grade::fetch_users_grades($item, [$userid], false);
|
||||
if (!empty($grades[$userid]) && $grades[$userid]->is_passed($item)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If a passing grade is required and exhausting all available attempts is not accepted for completion,
|
||||
// then this quiz is not complete.
|
||||
if (!$quiz->completionattemptsexhausted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if all attempts are used up.
|
||||
$attempts = quiz_get_user_attempts($quiz->id, $userid, 'finished', true);
|
||||
if (!$attempts) {
|
||||
return false;
|
||||
}
|
||||
$lastfinishedattempt = end($attempts);
|
||||
$context = context_module::instance($cm->id);
|
||||
$quizobj = quiz_settings::create($quiz->id, $userid);
|
||||
$accessmanager = new access_manager($quizobj, time(),
|
||||
has_capability('mod/quiz:ignoretimelimits', $context, $userid, false));
|
||||
|
||||
return $accessmanager->is_finished(count($attempts), $lastfinishedattempt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function used in quiz_get_completion_state. Check minimum attempts requirement for completion.
|
||||
*
|
||||
* @deprecated since Moodle 3.11
|
||||
* @todo MDL-71196 Final deprecation in Moodle 4.3
|
||||
* @see \mod_quiz\completion\custom_completion
|
||||
* @param int $userid
|
||||
* @param stdClass $quiz
|
||||
* @return bool True if minimum attempts requirement is disabled or met.
|
||||
*/
|
||||
function quiz_completion_check_min_attempts($userid, $quiz) {
|
||||
|
||||
debugging('quiz_completion_check_min_attempts has been deprecated.', DEBUG_DEVELOPER);
|
||||
|
||||
if (empty($quiz->completionminattempts)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the user has done enough attempts.
|
||||
$attempts = quiz_get_user_attempts($quiz->id, $userid, 'finished', true);
|
||||
return $quiz->completionminattempts <= count($attempts);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @deprecated since Moodle 3.11
|
||||
* @todo MDL-71196 Final deprecation in Moodle 4.3
|
||||
* @see \mod_quiz\completion\custom_completion
|
||||
* @param stdClass $course Course
|
||||
* @param cm_info|stdClass $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;
|
||||
|
||||
// No need to call debugging here. Deprecation debugging notice already being called in \completion_info::internal_get_state().
|
||||
|
||||
$quiz = $DB->get_record('quiz', ['id' => $cm->instance], '*', MUST_EXIST);
|
||||
if (!$quiz->completionattemptsexhausted && !$cm->completionpassgrade && !$quiz->completionminattempts) {
|
||||
return $type;
|
||||
}
|
||||
|
||||
if (!quiz_completion_check_passing_grade_or_all_attempts($course, $cm, $userid, $quiz)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!quiz_completion_check_min_attempts($userid, $quiz)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
function quiz_get_completion_state() {
|
||||
$completionclass = \mod_quiz\completion\custom_completion::class;
|
||||
throw new coding_exception(__FUNCTION__ . "() has been removed, please use the '{$completionclass}' class instead");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -134,299 +134,6 @@ class lib_test extends \advanced_testcase {
|
||||
$this->assertEquals(0, $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup function for all test_quiz_get_completion_state_* tests.
|
||||
*
|
||||
* @param array $completionoptions ['nbstudents'] => int, ['qtype'] => string, ['quizoptions'] => array
|
||||
* @throws dml_exception
|
||||
* @return array [$course, $students, $quiz, $cm]
|
||||
*/
|
||||
private function setup_quiz_for_testing_completion(array $completionoptions) {
|
||||
global $CFG, $DB;
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
// Enable completion before creating modules, otherwise the completion data is not written in DB.
|
||||
$CFG->enablecompletion = true;
|
||||
|
||||
// Create a course and students.
|
||||
$studentrole = $DB->get_record('role', ['shortname' => 'student']);
|
||||
$course = $this->getDataGenerator()->create_course(['enablecompletion' => true]);
|
||||
$students = [];
|
||||
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));
|
||||
}
|
||||
|
||||
// Make a quiz.
|
||||
$quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
|
||||
$data = array_merge([
|
||||
'course' => $course->id,
|
||||
'grade' => 100.0,
|
||||
'questionsperpage' => 0,
|
||||
'sumgrades' => 1,
|
||||
'completion' => COMPLETION_TRACKING_AUTOMATIC
|
||||
], $completionoptions['quizoptions']);
|
||||
$quiz = $quizgenerator->create_instance($data);
|
||||
$cm = get_coursemodule_from_id('quiz', $quiz->cmid);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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 [
|
||||
$course,
|
||||
$students,
|
||||
$quiz,
|
||||
$cm
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for all test_quiz_get_completion_state_* tests.
|
||||
* Starts an attempt, processes responses and finishes the attempt.
|
||||
*
|
||||
* @param $attemptoptions ['quiz'] => object, ['student'] => object, ['tosubmit'] => array, ['attemptnumber'] => int
|
||||
*/
|
||||
private function do_attempt_quiz($attemptoptions) {
|
||||
$quizobj = quiz_settings::create($attemptoptions['quiz']->id);
|
||||
|
||||
// Start the passing attempt.
|
||||
$quba = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
|
||||
$quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
|
||||
|
||||
$timenow = time();
|
||||
$attempt = quiz_create_attempt($quizobj, $attemptoptions['attemptnumber'], false, $timenow, false,
|
||||
$attemptoptions['student']->id);
|
||||
quiz_start_new_attempt($quizobj, $quba, $attempt, $attemptoptions['attemptnumber'], $timenow);
|
||||
quiz_attempt_save_started($quizobj, $quba, $attempt);
|
||||
|
||||
// Process responses from the student.
|
||||
$attemptobj = quiz_attempt::create($attempt->id);
|
||||
$attemptobj->process_submitted_actions($timenow, false, $attemptoptions['tosubmit']);
|
||||
|
||||
// Finish the attempt.
|
||||
$attemptobj = quiz_attempt::create($attempt->id);
|
||||
$this->assertTrue($attemptobj->has_response_to_at_least_one_graded_question());
|
||||
$attemptobj->process_finish($timenow, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test checking the completion state of a quiz.
|
||||
* The quiz requires a passing grade to be completed.
|
||||
*/
|
||||
public function test_quiz_get_completion_state_completionpass() {
|
||||
|
||||
list($course, $students, $quiz, $cm) = $this->setup_quiz_for_testing_completion([
|
||||
'nbstudents' => 2,
|
||||
'qtype' => 'numerical',
|
||||
'quizoptions' => [
|
||||
'completionusegrade' => 1,
|
||||
'completionpassgrade' => 1
|
||||
]
|
||||
]);
|
||||
|
||||
list($passstudent, $failstudent) = $students;
|
||||
|
||||
// Do a passing attempt.
|
||||
$this->do_attempt_quiz([
|
||||
'quiz' => $quiz,
|
||||
'student' => $passstudent,
|
||||
'attemptnumber' => 1,
|
||||
'tosubmit' => [1 => ['answer' => '3.14']]
|
||||
]);
|
||||
|
||||
// Check the results.
|
||||
$this->assertTrue(quiz_get_completion_state($course, $cm, $passstudent->id, 'return'));
|
||||
|
||||
// Do a failing attempt.
|
||||
$this->do_attempt_quiz([
|
||||
'quiz' => $quiz,
|
||||
'student' => $failstudent,
|
||||
'attemptnumber' => 1,
|
||||
'tosubmit' => [1 => ['answer' => '0']]
|
||||
]);
|
||||
|
||||
// Check the results.
|
||||
$this->assertFalse(quiz_get_completion_state($course, $cm, $failstudent->id, 'return'));
|
||||
|
||||
$this->assertDebuggingCalledCount(3, [
|
||||
'quiz_completion_check_passing_grade_or_all_attempts has been deprecated.',
|
||||
'quiz_completion_check_min_attempts has been deprecated.',
|
||||
'quiz_completion_check_passing_grade_or_all_attempts has been deprecated.',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test checking the completion state of a quiz.
|
||||
* To be completed, this quiz requires either a passing grade or for all attempts to be used up.
|
||||
*/
|
||||
public function test_quiz_get_completion_state_completionexhausted() {
|
||||
|
||||
list($course, $students, $quiz, $cm) = $this->setup_quiz_for_testing_completion([
|
||||
'nbstudents' => 2,
|
||||
'qtype' => 'numerical',
|
||||
'quizoptions' => [
|
||||
'attempts' => 2,
|
||||
'completionusegrade' => 1,
|
||||
'completionpassgrade' => 1,
|
||||
'completionattemptsexhausted' => 1
|
||||
]
|
||||
]);
|
||||
|
||||
list($passstudent, $exhauststudent) = $students;
|
||||
|
||||
// Start a passing attempt.
|
||||
$this->do_attempt_quiz([
|
||||
'quiz' => $quiz,
|
||||
'student' => $passstudent,
|
||||
'attemptnumber' => 1,
|
||||
'tosubmit' => [1 => ['answer' => '3.14']]
|
||||
]);
|
||||
|
||||
// Check the results. Quiz is completed by $passstudent because of passing grade.
|
||||
$this->assertTrue(quiz_get_completion_state($course, $cm, $passstudent->id, 'return'));
|
||||
|
||||
// Do a failing attempt.
|
||||
$this->do_attempt_quiz([
|
||||
'quiz' => $quiz,
|
||||
'student' => $exhauststudent,
|
||||
'attemptnumber' => 1,
|
||||
'tosubmit' => [1 => ['answer' => '0']]
|
||||
]);
|
||||
|
||||
// Check the results. Quiz is not completed by $exhauststudent yet because of failing grade and of remaining attempts.
|
||||
$this->assertFalse(quiz_get_completion_state($course, $cm, $exhauststudent->id, 'return'));
|
||||
|
||||
// Do a second failing attempt.
|
||||
$this->do_attempt_quiz([
|
||||
'quiz' => $quiz,
|
||||
'student' => $exhauststudent,
|
||||
'attemptnumber' => 2,
|
||||
'tosubmit' => [1 => ['answer' => '0']]
|
||||
]);
|
||||
|
||||
// Check the results. Quiz is completed by $exhauststudent because there are no remaining attempts.
|
||||
$this->assertTrue(quiz_get_completion_state($course, $cm, $exhauststudent->id, 'return'));
|
||||
|
||||
$this->assertDebuggingCalledCount(5, [
|
||||
'quiz_completion_check_passing_grade_or_all_attempts has been deprecated.',
|
||||
'quiz_completion_check_min_attempts has been deprecated.',
|
||||
'quiz_completion_check_passing_grade_or_all_attempts has been deprecated.',
|
||||
'quiz_completion_check_passing_grade_or_all_attempts has been deprecated.',
|
||||
'quiz_completion_check_min_attempts has been deprecated.',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test checking the completion state of a quiz.
|
||||
* To be completed, this quiz requires a minimum number of attempts.
|
||||
*/
|
||||
public function test_quiz_get_completion_state_completionminattempts() {
|
||||
|
||||
list($course, $students, $quiz, $cm) = $this->setup_quiz_for_testing_completion([
|
||||
'nbstudents' => 1,
|
||||
'qtype' => 'essay',
|
||||
'quizoptions' => [
|
||||
'completionminattemptsenabled' => 1,
|
||||
'completionminattempts' => 2
|
||||
]
|
||||
]);
|
||||
|
||||
list($student) = $students;
|
||||
|
||||
// Do a first attempt.
|
||||
$this->do_attempt_quiz([
|
||||
'quiz' => $quiz,
|
||||
'student' => $student,
|
||||
'attemptnumber' => 1,
|
||||
'tosubmit' => [1 => ['answer' => 'Lorem ipsum.', 'answerformat' => '1']]
|
||||
]);
|
||||
|
||||
// Check the results. Quiz is not completed yet because only one attempt was done.
|
||||
$this->assertFalse(quiz_get_completion_state($course, $cm, $student->id, 'return'));
|
||||
|
||||
// Do a second attempt.
|
||||
$this->do_attempt_quiz([
|
||||
'quiz' => $quiz,
|
||||
'student' => $student,
|
||||
'attemptnumber' => 2,
|
||||
'tosubmit' => [1 => ['answer' => 'Lorem ipsum.', 'answerformat' => '1']]
|
||||
]);
|
||||
|
||||
// Check the results. Quiz is completed by $student because two attempts were done.
|
||||
$this->assertTrue(quiz_get_completion_state($course, $cm, $student->id, 'return'));
|
||||
|
||||
$this->assertDebuggingCalledCount(4, [
|
||||
'quiz_completion_check_passing_grade_or_all_attempts has been deprecated.',
|
||||
'quiz_completion_check_min_attempts has been deprecated.',
|
||||
'quiz_completion_check_passing_grade_or_all_attempts has been deprecated.',
|
||||
'quiz_completion_check_min_attempts has been deprecated.',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test checking the completion state of a quiz.
|
||||
* To be completed, this quiz requires a minimum number of attempts AND a passing grade.
|
||||
* This is somewhat of an edge case as it is hard to imagine a scenario in which these precise settings are useful.
|
||||
* Nevertheless, this test makes sure these settings interact as intended.
|
||||
*/
|
||||
public function test_quiz_get_completion_state_completionminattempts_pass() {
|
||||
|
||||
list($course, $students, $quiz, $cm) = $this->setup_quiz_for_testing_completion([
|
||||
'nbstudents' => 1,
|
||||
'qtype' => 'numerical',
|
||||
'quizoptions' => [
|
||||
'attempts' => 2,
|
||||
'completionusegrade' => 1,
|
||||
'completionpassgrade' => 1,
|
||||
'completionminattemptsenabled' => 1,
|
||||
'completionminattempts' => 2
|
||||
]
|
||||
]);
|
||||
|
||||
list($student) = $students;
|
||||
|
||||
// Start a first attempt.
|
||||
$this->do_attempt_quiz([
|
||||
'quiz' => $quiz,
|
||||
'student' => $student,
|
||||
'attemptnumber' => 1,
|
||||
'tosubmit' => [1 => ['answer' => '3.14']]
|
||||
]);
|
||||
|
||||
// Check the results. Even though one requirement is met (passing grade) quiz is not completed yet because only
|
||||
// one attempt was done.
|
||||
$this->assertFalse(quiz_get_completion_state($course, $cm, $student->id, 'return'));
|
||||
|
||||
// Start a second attempt.
|
||||
$this->do_attempt_quiz([
|
||||
'quiz' => $quiz,
|
||||
'student' => $student,
|
||||
'attemptnumber' => 2,
|
||||
'tosubmit' => [1 => ['answer' => '42']]
|
||||
]);
|
||||
|
||||
// Check the results. Quiz is completed by $student because two attempts were done AND a passing grade was obtained.
|
||||
$this->assertTrue(quiz_get_completion_state($course, $cm, $student->id, 'return'));
|
||||
|
||||
$this->assertDebuggingCalledCount(4, [
|
||||
'quiz_completion_check_passing_grade_or_all_attempts has been deprecated.',
|
||||
'quiz_completion_check_min_attempts has been deprecated.',
|
||||
'quiz_completion_check_passing_grade_or_all_attempts has been deprecated.',
|
||||
'quiz_completion_check_min_attempts has been deprecated.',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_quiz_get_user_attempts() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
@ -23,119 +23,9 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Obtains the automatic completion state for this scorm based on any conditions
|
||||
* in scorm settings.
|
||||
*
|
||||
* @deprecated since Moodle 3.11
|
||||
* @todo MDL-71196 Final deprecation in Moodle 4.3
|
||||
* @see \mod_scorm\completion\custom_completion
|
||||
* @param stdClass $course Course
|
||||
* @param cm_info|stdClass $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 scorm_get_completion_state($course, $cm, $userid, $type) {
|
||||
global $DB;
|
||||
|
||||
// No need to call debugging here. Deprecation debugging notice already being called in \completion_info::internal_get_state().
|
||||
|
||||
$result = $type;
|
||||
|
||||
// Get scorm.
|
||||
if (!$scorm = $DB->get_record('scorm', array('id' => $cm->instance))) {
|
||||
throw new \moodle_exception('cannotfindscorm');
|
||||
}
|
||||
// Only check for existence of tracks and return false if completionstatusrequired or completionscorerequired
|
||||
// this means that if only view is required we don't end up with a false state.
|
||||
if ($scorm->completionstatusrequired !== null || $scorm->completionscorerequired !== null) {
|
||||
// Get user's tracks data.
|
||||
$tracks = $DB->get_records_sql(
|
||||
"
|
||||
SELECT
|
||||
v.id,
|
||||
v.scoid,
|
||||
e.element,
|
||||
v.value
|
||||
FROM
|
||||
{scorm_scoes_value} v
|
||||
JOIN {scorm_attempt} a on a.id = v.attemptid
|
||||
JOIN {scorm_element} e on e.id = v.elementid
|
||||
WHERE
|
||||
a.scormid = ?
|
||||
AND a.userid = ?
|
||||
AND e.element IN
|
||||
(
|
||||
'cmi.core.lesson_status',
|
||||
'cmi.completion_status',
|
||||
'cmi.success_status',
|
||||
'cmi.core.score.raw',
|
||||
'cmi.score.raw'
|
||||
)
|
||||
",
|
||||
array($scorm->id, $userid)
|
||||
);
|
||||
|
||||
if (!$tracks) {
|
||||
return completion_info::aggregate_completion_states($type, $result, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for status.
|
||||
if ($scorm->completionstatusrequired !== null) {
|
||||
|
||||
// Get status.
|
||||
$statuses = array_flip(scorm_status_options());
|
||||
$nstatus = 0;
|
||||
// Check any track for these values.
|
||||
$scostatus = array();
|
||||
foreach ($tracks as $track) {
|
||||
if (!in_array($track->element, array('cmi.core.lesson_status', 'cmi.completion_status', 'cmi.success_status'))) {
|
||||
continue;
|
||||
}
|
||||
if (array_key_exists($track->value, $statuses)) {
|
||||
$scostatus[$track->scoid] = true;
|
||||
$nstatus |= $statuses[$track->value];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($scorm->completionstatusallscos)) {
|
||||
// Iterate over all scos and make sure each has a lesson_status.
|
||||
$scos = $DB->get_records('scorm_scoes', array('scorm' => $scorm->id, 'scormtype' => 'sco'));
|
||||
foreach ($scos as $sco) {
|
||||
if (empty($scostatus[$sco->id])) {
|
||||
return completion_info::aggregate_completion_states($type, $result, false);
|
||||
}
|
||||
}
|
||||
return completion_info::aggregate_completion_states($type, $result, true);
|
||||
} else if ($scorm->completionstatusrequired & $nstatus) {
|
||||
return completion_info::aggregate_completion_states($type, $result, true);
|
||||
} else {
|
||||
return completion_info::aggregate_completion_states($type, $result, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for score.
|
||||
if ($scorm->completionscorerequired !== null) {
|
||||
$maxscore = -1;
|
||||
|
||||
foreach ($tracks as $track) {
|
||||
if (!in_array($track->element, array('cmi.core.score.raw', 'cmi.score.raw'))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strlen($track->value) && floatval($track->value) >= $maxscore) {
|
||||
$maxscore = floatval($track->value);
|
||||
}
|
||||
}
|
||||
|
||||
if ($scorm->completionscorerequired <= $maxscore) {
|
||||
return completion_info::aggregate_completion_states($type, $result, true);
|
||||
} else {
|
||||
return completion_info::aggregate_completion_states($type, $result, false);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
function scorm_get_completion_state() {
|
||||
$completionclass = \mod_scorm\completion\custom_completion::class;
|
||||
throw new coding_exception(__FUNCTION__ . "() has been removed, please use the '{$completionclass}' class instead");
|
||||
}
|
||||
|
@ -23,32 +23,9 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Obtains the automatic completion state for this survey based on the condition
|
||||
* in feedback settings.
|
||||
*
|
||||
* @deprecated since Moodle 3.11
|
||||
* @todo MDL-71196 Final deprecation in Moodle 4.3
|
||||
* @see \mod_survey\completion\custom_completion
|
||||
* @param stdClass $course Course
|
||||
* @param cm_info|stdClass $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, $type if conditions not set.
|
||||
*/
|
||||
function survey_get_completion_state($course, $cm, $userid, $type) {
|
||||
global $DB;
|
||||
|
||||
// No need to call debugging here. Deprecation debugging notice already being called in \completion_info::internal_get_state().
|
||||
|
||||
// Get survey details.
|
||||
$survey = $DB->get_record('survey', array('id' => $cm->instance), '*', MUST_EXIST);
|
||||
|
||||
// If completion option is enabled, evaluate it and return true/false.
|
||||
if ($survey->completionsubmit) {
|
||||
$params = array('userid' => $userid, 'survey' => $survey->id);
|
||||
return $DB->record_exists('survey_answers', $params);
|
||||
} else {
|
||||
// Completion option is not enabled so just return $type.
|
||||
return $type;
|
||||
}
|
||||
function survey_get_completion_state() {
|
||||
$completionclass = \mod_survey\completion\custom_completion::class;
|
||||
throw new coding_exception(__FUNCTION__ . "() has been removed, please use the '{$completionclass}' class instead");
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user