diff --git a/completion/classes/progress.php b/completion/classes/progress.php new file mode 100644 index 00000000000..415b28263f8 --- /dev/null +++ b/completion/classes/progress.php @@ -0,0 +1,82 @@ +. + +/** + * Contains class used to return completion progress information. + * + * @package core_completion + * @copyright 2017 Mark Nelson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_completion; + +defined('MOODLE_INTERNAL') || die(); + +/** + * Class used to return completion progress information. + * + * @package core_completion + * @copyright 2017 Mark Nelson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class progress { + + /** + * Returns the course percentage completed by a certain user, returns null if no completion data is available. + * + * @param \stdClass $course Moodle course object + * @param int $userid The id of the user, 0 for the current user + * @return null|float The percentage, or null if completion is not supported in the course, + * or there are no activities that support completion. + */ + public static function get_course_progress_percentage($course, $userid = 0) { + global $USER; + + // Make sure we continue with a valid userid. + if (empty($userid)) { + $userid = $USER->id; + } + + $completion = new \completion_info($course); + + // First, let's make sure completion is enabled. + if (!$completion->is_enabled()) { + return null; + } + + // Before we check how many modules have been completed see if the course has. + if ($completion->is_course_complete($userid)) { + return 100; + } + + // Get the number of modules that support completion. + $modules = $completion->get_activities(); + $count = count($modules); + if (!$count) { + return null; + } + + // Get the number of modules that have been completed. + $completed = 0; + foreach ($modules as $module) { + $data = $completion->get_data($module, false, $userid); + $completed += $data->completionstate == COMPLETION_INCOMPLETE ? 0 : 1; + } + + return ($completed / $count) * 100; + } +} diff --git a/completion/tests/progress_test.php b/completion/tests/progress_test.php new file mode 100644 index 00000000000..b2e5b97e56f --- /dev/null +++ b/completion/tests/progress_test.php @@ -0,0 +1,154 @@ +. + +/** + * Test completion progress API. + * + * @package core_completion + * @category test + * @copyright 2017 Mark Nelson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * Test completion progress API. + * + * @package core_completion + * @category test + * @copyright 2017 Mark Nelson + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class core_completion_progress_testcase extends advanced_testcase { + + /** + * Test setup. + */ + public function setUp() { + global $CFG; + + $CFG->enablecompletion = true; + $this->resetAfterTest(); + } + + /** + * Tests that the course progress percentage is returned correctly when we have only activity completion. + */ + public function test_course_progress_percentage_with_just_activities() { + global $DB; + + // Add a course that supports completion. + $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); + + // Enrol a user in the course. + $user = $this->getDataGenerator()->create_user(); + $studentrole = $DB->get_record('role', array('shortname' => 'student')); + $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id); + + // Add four activities that use completion. + $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id), + array('completion' => 1)); + $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id), + array('completion' => 1)); + $this->getDataGenerator()->create_module('forum', array('course' => $course->id), + array('completion' => 1)); + $this->getDataGenerator()->create_module('forum', array('course' => $course->id), + array('completion' => 1)); + + // Add an activity that does *not* use completion. + $this->getDataGenerator()->create_module('assign', array('course' => $course->id)); + + // Mark two of them as completed for a user. + $cmassign = get_coursemodule_from_id('assign', $assign->cmid); + $cmdata = get_coursemodule_from_id('data', $data->cmid); + $completion = new completion_info($course); + $completion->update_state($cmassign, COMPLETION_COMPLETE, $user->id); + $completion->update_state($cmdata, COMPLETION_COMPLETE, $user->id); + + // Check we have received valid data. + // Note - only 4 out of the 5 activities support completion, and the user has completed 2 of those. + $this->assertEquals('50', \core_completion\progress::get_course_progress_percentage($course, $user->id)); + } + + /** + * Tests that the course progress percentage is returned correctly when we have a course and activity completion. + */ + public function test_course_progress_percentage_with_activities_and_course() { + global $DB; + + // Add a course that supports completion. + $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); + + // Enrol a user in the course. + $user = $this->getDataGenerator()->create_user(); + $studentrole = $DB->get_record('role', array('shortname' => 'student')); + $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id); + + // Add four activities that use completion. + $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id), + array('completion' => 1)); + $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id), + array('completion' => 1)); + $this->getDataGenerator()->create_module('forum', array('course' => $course->id), + array('completion' => 1)); + $this->getDataGenerator()->create_module('forum', array('course' => $course->id), + array('completion' => 1)); + + // Add an activity that does *not* use completion. + $this->getDataGenerator()->create_module('assign', array('course' => $course->id)); + + // Mark two of them as completed for a user. + $cmassign = get_coursemodule_from_id('assign', $assign->cmid); + $cmdata = get_coursemodule_from_id('data', $data->cmid); + $completion = new completion_info($course); + $completion->update_state($cmassign, COMPLETION_COMPLETE, $user->id); + $completion->update_state($cmdata, COMPLETION_COMPLETE, $user->id); + + // Now, mark the course as completed. + $ccompletion = new completion_completion(array('course' => $course->id, 'userid' => $user->id)); + $ccompletion->mark_complete(); + + // Check we have received valid data. + // The course completion takes priority, so should return 100. + $this->assertEquals('100', \core_completion\progress::get_course_progress_percentage($course, $user->id)); + } + + /** + * Tests that the course progress returns null when the course does not support it. + */ + public function test_course_progress_course_not_using_completion() { + // Create a course that does not use completion. + $course = $this->getDataGenerator()->create_course(); + + // Check that the result was null. + $this->assertNull(\core_completion\progress::get_course_progress_percentage($course)); + } + + /** + * Tests that the course progress returns null when there are no activities that support it. + */ + public function test_course_progress_no_activities_using_completion() { + // Create a course that does support completion. + $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); + + // Add an activity that does *not* support completion. + $this->getDataGenerator()->create_module('assign', array('course' => $course->id)); + + // Check that the result was null. + $this->assertNull(\core_completion\progress::get_course_progress_percentage($course)); + } +}