Skip to content

Commit

Permalink
Merge branch 'MDL-58136-32' of git://github.com/damyon/moodle into MO…
Browse files Browse the repository at this point in the history
…ODLE_32_STABLE
  • Loading branch information
snake committed Jun 13, 2017
2 parents cb1c0ae + 587b996 commit 1faba07
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 8 deletions.
26 changes: 23 additions & 3 deletions completion/completion_completion.php
Expand Up @@ -74,7 +74,16 @@ class completion_completion extends data_object {
* @return data_object instance of data_object or false if none found.
*/
public static function fetch($params) {
return self::fetch_helper('course_completions', __CLASS__, $params);
$cache = cache::make('core', 'coursecompletion');

$key = $params['userid'] . '_' . $params['course'];
if ($hit = $cache->get($key)) {
return $hit['value'];
}

$tocache = self::fetch_helper('course_completions', __CLASS__, $params);
$cache->set($key, ['value' => $tocache]);
return $tocache;
}

/**
Expand Down Expand Up @@ -179,9 +188,10 @@ private function _save() {
$this->timeenrolled = 0;
}

$result = false;
// Save record
if ($this->id) {
return $this->update();
$result = $this->update();
} else {
// Make sure reaggregate field is not null
if (!$this->reaggregate) {
Expand All @@ -193,7 +203,17 @@ private function _save() {
$this->timestarted = 0;
}

return $this->insert();
$result = $this->insert();
}

if ($result) {
// Update the cached record.
$cache = cache::make('core', 'coursecompletion');
$data = $this->get_record_data();
$key = $data->userid . '_' . $data->course;
$cache->set($key, ['value' => $data]);
}

return $result;
}
}
44 changes: 44 additions & 0 deletions course/lib.php
Expand Up @@ -55,6 +55,10 @@
define('MOD_CLASS_ACTIVITY', 0);
define('MOD_CLASS_RESOURCE', 1);

define('COURSE_TIMELINE_PAST', 'past');
define('COURSE_TIMELINE_INPROGRESS', 'inprogress');
define('COURSE_TIMELINE_FUTURE', 'future');

function make_log_url($module, $url) {
switch ($module) {
case 'course':
Expand Down Expand Up @@ -3920,6 +3924,46 @@ function course_check_updates($course, $tocheck, $filter = array()) {
return array($instances, $warnings);
}

/**
* This function classifies a course as past, in progress or future.
*
* This function may incur a DB hit to calculate course completion.
* @param stdClass $course Course record
* @param stdClass $user User record (optional - defaults to $USER).
* @param completion_info $completioninfo Completion record for the user (optional - will be fetched if required).
* @return string (one of COURSE_TIMELINE_FUTURE, COURSE_TIMELINE_INPROGRESS or COURSE_TIMELINE_PAST)
*/
function course_classify_for_timeline($course, $user = null, $completioninfo = null) {
global $USER;

if ($user == null) {
$user = $USER;
}

$today = time();
// End date past.
if (!empty($course->enddate) && $course->enddate < $today) {
return COURSE_TIMELINE_PAST;
}

if ($completioninfo == null) {
$completioninfo = new completion_info($course);
}

// Course was completed.
if ($completioninfo->is_enabled() && $completioninfo->is_course_complete($user->id)) {
return COURSE_TIMELINE_PAST;
}

// Start date not reached.
if (!empty($course->startdate) && $course->startdate > $today) {
return COURSE_TIMELINE_FUTURE;
}

// Everything else is in progress.
return COURSE_TIMELINE_INPROGRESS;
}

/**
* Check module updates since a given time.
* This function checks for updates in the module config, file areas, completion, grades, comments and ratings.
Expand Down
48 changes: 48 additions & 0 deletions course/tests/courselib_test.php
Expand Up @@ -3689,4 +3689,52 @@ public function test_async_section_deletion_hook_not_implemented() {
}
$this->assertEquals(2, $count);
}

public function test_classify_course_for_timeline() {
global $DB, $CFG;

require_once($CFG->dirroot.'/completion/criteria/completion_criteria_self.php');

set_config('enablecompletion', COMPLETION_ENABLED);

$this->resetAfterTest(true);
$this->setAdminUser();

// Create courses for testing.
$generator = $this->getDataGenerator();
$future = time() + 3600;
$past = time() - 3600;
$futurecourse = $generator->create_course(['startdate' => $future]);
$pastcourse = $generator->create_course(['startdate' => $past - 60, 'enddate' => $past]);
$completedcourse = $generator->create_course(['enablecompletion' => COMPLETION_ENABLED]);
$inprogresscourse = $generator->create_course();

// Set completion rules.
$criteriadata = new stdClass();
$criteriadata->id = $completedcourse->id;

// Self completion.
$criteriadata->criteria_self = COMPLETION_CRITERIA_TYPE_SELF;
$class = 'completion_criteria_self';
$criterion = new $class();
$criterion->update_config($criteriadata);

$user = $this->getDataGenerator()->create_user();
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
$this->getDataGenerator()->enrol_user($user->id, $futurecourse->id, $studentrole->id);
$this->getDataGenerator()->enrol_user($user->id, $pastcourse->id, $studentrole->id);
$this->getDataGenerator()->enrol_user($user->id, $completedcourse->id, $studentrole->id);
$this->getDataGenerator()->enrol_user($user->id, $inprogresscourse->id, $studentrole->id);

$this->setUser($user);
core_completion_external::mark_course_self_completed($completedcourse->id);
$ccompletion = new completion_completion(array('course' => $completedcourse->id, 'userid' => $user->id));
$ccompletion->mark_complete();

// Aggregate the completions.
$this->assertEquals(COURSE_TIMELINE_PAST, course_classify_for_timeline($pastcourse));
$this->assertEquals(COURSE_TIMELINE_FUTURE, course_classify_for_timeline($futurecourse));
$this->assertEquals(COURSE_TIMELINE_PAST, course_classify_for_timeline($completedcourse));
$this->assertEquals(COURSE_TIMELINE_INPROGRESS, course_classify_for_timeline($inprogresscourse));
}
}
3 changes: 2 additions & 1 deletion lang/en/cache.php
Expand Up @@ -41,8 +41,9 @@
$string['cachedef_config'] = 'Config settings';
$string['cachedef_coursecat'] = 'Course categories lists for particular user';
$string['cachedef_coursecatrecords'] = 'Course categories records';
$string['cachedef_coursecontacts'] = 'List of course contacts';
$string['cachedef_coursecattree'] = 'Course categories tree';
$string['cachedef_coursecompletion'] = 'Course completion status';
$string['cachedef_coursecontacts'] = 'List of course contacts';
$string['cachedef_coursemodinfo'] = 'Accumulated information about modules and sections for each course';
$string['cachedef_completion'] = 'Activity completion status';
$string['cachedef_databasemeta'] = 'Database meta information';
Expand Down
2 changes: 2 additions & 0 deletions lib/completionlib.php
Expand Up @@ -769,6 +769,7 @@ public function delete_course_completion_data() {

// Difficult to find affected users, just purge all completion cache.
cache::make('core', 'completion')->purge();
cache::make('core', 'coursecompletion')->purge();
}

/**
Expand Down Expand Up @@ -820,6 +821,7 @@ public function delete_all_state($cm) {

// Difficult to find affected users, just purge all completion cache.
cache::make('core', 'completion')->purge();
cache::make('core', 'coursecompletion')->purge();
}

/**
Expand Down
10 changes: 10 additions & 0 deletions lib/db/caches.php
Expand Up @@ -229,6 +229,16 @@
'staticaccelerationsize' => 2, // Should be current course and site course.
),

// Used to cache course completion status.
'coursecompletion' => array(
'mode' => cache_store::MODE_APPLICATION,
'simplekeys' => true,
'simpledata' => true,
'ttl' => 3600,
'staticacceleration' => true,
'staticaccelerationsize' => 30, // Will be users list of current courses in nav.
),

// A simple cache that stores whether a user can expand a course in the navigation.
// The key is the course ID and the value will either be 1 or 0 (cast to bool).
// The cache isn't always up to date, it should only ever be used to save a costly call to
Expand Down
15 changes: 12 additions & 3 deletions lib/navigationlib.php
Expand Up @@ -2567,7 +2567,16 @@ public function add_course(stdClass $course, $forcegeneric = false, $coursetype
}

$coursenode = $parent->add($coursename, $url, self::TYPE_COURSE, $shortname, $course->id);
$coursenode->showinflatnavigation = $coursetype == self::COURSE_MY;

// Do some calculation to see if the course is past, current or future.
if ($coursetype == self::COURSE_MY) {
$classify = course_classify_for_timeline($course);

if ($classify == COURSE_TIMELINE_INPROGRESS) {
$coursenode->showinflatnavigation = true;
}
}

$coursenode->hidden = (!$course->visible);
$coursenode->title(format_string($course->fullname, true, array('context' => $coursecontext, 'escape' => false)));
if ($canexpandcourse) {
Expand Down Expand Up @@ -2890,7 +2899,7 @@ protected function load_courses_enrolled() {
}
// Append the chosen sortorder.
$sortorder = $sortorder . ',' . $CFG->navsortmycoursessort . ' ASC';
$courses = enrol_get_my_courses(null, $sortorder);
$courses = enrol_get_my_courses('*', $sortorder);
if (count($courses) && $this->show_my_categories()) {
// Generate an array containing unique values of all the courses' categories.
$categoryids = array();
Expand Down Expand Up @@ -3140,7 +3149,7 @@ protected function load_category($categoryid, $nodetype = self::TYPE_CATEGORY) {
// If category is shown in MyHome then only show enrolled courses and hide empty subcategories,
// else show all courses.
if ($nodetype === self::TYPE_MY_CATEGORY) {
$courses = enrol_get_my_courses();
$courses = enrol_get_my_courses('*');
$categoryids = array();

// Only search for categories if basecategory was found.
Expand Down
2 changes: 1 addition & 1 deletion version.php
Expand Up @@ -29,7 +29,7 @@

defined('MOODLE_INTERNAL') || die();

$version = 2016120503.07; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2016120503.08; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.

Expand Down

0 comments on commit 1faba07

Please sign in to comment.