mirror of
https://github.com/moodle/moodle.git
synced 2025-03-14 12:40:01 +01:00
Merge branch 'MDL-60434_master' of git://github.com/dmonllao/moodle
This commit is contained in:
commit
1e67827131
@ -29,6 +29,10 @@ defined('MOODLE_INTERNAL') || die();
|
||||
/**
|
||||
* Any element analysers can analyse.
|
||||
*
|
||||
* Analysers get_analysers method return all analysable elements in the site;
|
||||
* it is important that analysable elements implement lazy loading to avoid
|
||||
* big memory footprints. See \core_analytics\course example.
|
||||
*
|
||||
* @package core_analytics
|
||||
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
|
@ -40,9 +40,19 @@ require_once($CFG->dirroot . '/lib/enrollib.php');
|
||||
class course implements \core_analytics\analysable {
|
||||
|
||||
/**
|
||||
* @var \core_analytics\course[] $instances
|
||||
* @var bool Has this course data been already loaded.
|
||||
*/
|
||||
protected static $instances = array();
|
||||
protected $loaded = false;
|
||||
|
||||
/**
|
||||
* @var int $cachedid self::$cachedinstance analysable id.
|
||||
*/
|
||||
protected static $cachedid = 0;
|
||||
|
||||
/**
|
||||
* @var \core_analytics\course $cachedinstance
|
||||
*/
|
||||
protected static $cachedinstance = null;
|
||||
|
||||
/**
|
||||
* Course object
|
||||
@ -122,7 +132,7 @@ class course implements \core_analytics\analysable {
|
||||
* Use self::instance() instead to get cached copies of the course. Instances obtained
|
||||
* through this constructor will not be cached.
|
||||
*
|
||||
* Loads course students and teachers.
|
||||
* Lazy load of course data, students and teachers.
|
||||
*
|
||||
* @param int|stdClass $course Course id
|
||||
* @return void
|
||||
@ -130,18 +140,70 @@ class course implements \core_analytics\analysable {
|
||||
public function __construct($course) {
|
||||
|
||||
if (is_scalar($course)) {
|
||||
$this->course = get_course($course);
|
||||
$this->course = new \stdClass();
|
||||
$this->course->id = $course;
|
||||
} else {
|
||||
$this->course = $course;
|
||||
}
|
||||
}
|
||||
|
||||
$this->coursecontext = \context_course::instance($this->course->id);
|
||||
/**
|
||||
* Returns an analytics course instance.
|
||||
*
|
||||
* Lazy load of course data, students and teachers.
|
||||
*
|
||||
* @param int|stdClass $course Course object or course id
|
||||
* @return \core_analytics\course
|
||||
*/
|
||||
public static function instance($course) {
|
||||
|
||||
$courseid = $course;
|
||||
if (!is_scalar($courseid)) {
|
||||
$courseid = $course->id;
|
||||
}
|
||||
|
||||
if (self::$cachedid === $courseid) {
|
||||
return self::$cachedinstance;
|
||||
}
|
||||
|
||||
$cachedinstance = new \core_analytics\course($course);
|
||||
self::$cachedinstance = $cachedinstance;
|
||||
self::$cachedid = (int)$courseid;
|
||||
return self::$cachedinstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_id
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_id() {
|
||||
return $this->course->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the analytics course object.
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
protected function load() {
|
||||
|
||||
// The instance constructor could be already loaded with the full course object. Using shortname
|
||||
// because it is a required course field.
|
||||
if (empty($this->course->shortname)) {
|
||||
$this->course = get_course($this->course->id);
|
||||
}
|
||||
|
||||
$this->coursecontext = $this->get_context();
|
||||
|
||||
$this->now = time();
|
||||
|
||||
// Get the course users, including users assigned to student and teacher roles at an higher context.
|
||||
$cache = \cache::make_from_params(\cache_store::MODE_REQUEST, 'core_analytics', 'rolearchetypes');
|
||||
|
||||
// Flag the instance as loaded.
|
||||
$this->loaded = true;
|
||||
|
||||
if (!$studentroles = $cache->get('student')) {
|
||||
$studentroles = array_keys(get_archetype_roles('student'));
|
||||
$cache->set('student', $studentroles);
|
||||
@ -155,53 +217,13 @@ class course implements \core_analytics\analysable {
|
||||
$this->teacherids = $this->get_user_ids($teacherroles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an analytics course instance.
|
||||
*
|
||||
* @param int|stdClass $course Course id
|
||||
* @return \core_analytics\course
|
||||
*/
|
||||
public static function instance($course) {
|
||||
|
||||
$courseid = $course;
|
||||
if (!is_scalar($courseid)) {
|
||||
$courseid = $course->id;
|
||||
}
|
||||
|
||||
if (!empty(self::$instances[$courseid])) {
|
||||
return self::$instances[$courseid];
|
||||
}
|
||||
|
||||
$instance = new \core_analytics\course($course);
|
||||
self::$instances[$courseid] = $instance;
|
||||
return self::$instances[$courseid];
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all statically cached instances.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function reset_caches() {
|
||||
self::$instances = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* get_id
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_id() {
|
||||
return $this->course->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* The course short name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_name() {
|
||||
return format_string($this->course->shortname, true, array('context' => $this->get_context()));
|
||||
return format_string($this->get_course_data()->shortname, true, array('context' => $this->get_context()));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -228,8 +250,8 @@ class course implements \core_analytics\analysable {
|
||||
}
|
||||
|
||||
// The field always exist but may have no valid if the course is created through a sync process.
|
||||
if (!empty($this->course->startdate)) {
|
||||
$this->starttime = (int)$this->course->startdate;
|
||||
if (!empty($this->get_course_data()->startdate)) {
|
||||
$this->starttime = (int)$this->get_course_data()->startdate;
|
||||
} else {
|
||||
$this->starttime = 0;
|
||||
}
|
||||
@ -256,7 +278,7 @@ class course implements \core_analytics\analysable {
|
||||
|
||||
// We first try to find current course student logs.
|
||||
$firstlogs = array();
|
||||
foreach ($this->studentids as $studentid) {
|
||||
foreach ($this->get_students() as $studentid) {
|
||||
// Grrr, we are limited by logging API, we could do this easily with a
|
||||
// select min(timecreated) from xx where courseid = yy group by userid.
|
||||
|
||||
@ -278,7 +300,7 @@ class course implements \core_analytics\analysable {
|
||||
sort($firstlogs);
|
||||
$firstlogsmedian = $this->median($firstlogs);
|
||||
|
||||
$studentenrolments = enrol_get_course_users($this->get_id(), $this->studentids);
|
||||
$studentenrolments = enrol_get_course_users($this->get_id(), $this->get_students());
|
||||
if (empty($studentenrolments)) {
|
||||
return 0;
|
||||
}
|
||||
@ -306,8 +328,8 @@ class course implements \core_analytics\analysable {
|
||||
}
|
||||
|
||||
// The enddate field is only available from Moodle 3.2 (MDL-22078).
|
||||
if (!empty($this->course->enddate)) {
|
||||
$this->endtime = (int)$this->course->enddate;
|
||||
if (!empty($this->get_course_data()->enddate)) {
|
||||
$this->endtime = (int)$this->get_course_data()->enddate;
|
||||
return $this->endtime;
|
||||
}
|
||||
|
||||
@ -362,21 +384,12 @@ class course implements \core_analytics\analysable {
|
||||
* @return \stdClass
|
||||
*/
|
||||
public function get_course_data() {
|
||||
return $this->course;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the course valid to extract indicators from it?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_valid() {
|
||||
|
||||
if (!$this->was_started() || !$this->is_finished()) {
|
||||
return false;
|
||||
if (!$this->loaded) {
|
||||
$this->load();
|
||||
}
|
||||
|
||||
return true;
|
||||
return $this->course;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -427,7 +440,7 @@ class course implements \core_analytics\analysable {
|
||||
public function get_user_ids($roleids) {
|
||||
|
||||
// We need to index by ra.id as a user may have more than 1 $roles role.
|
||||
$records = get_role_users($roleids, $this->coursecontext, true, 'ra.id, u.id AS userid, r.id AS roleid', 'ra.id ASC');
|
||||
$records = get_role_users($roleids, $this->get_context(), true, 'ra.id, u.id AS userid, r.id AS roleid', 'ra.id ASC');
|
||||
|
||||
// If a user have more than 1 $roles role array_combine will discard the duplicate.
|
||||
$callable = array($this, 'filter_user_id');
|
||||
@ -441,6 +454,11 @@ class course implements \core_analytics\analysable {
|
||||
* @return stdClass[]
|
||||
*/
|
||||
public function get_students() {
|
||||
|
||||
if (!$this->loaded) {
|
||||
$this->load();
|
||||
}
|
||||
|
||||
return $this->studentids;
|
||||
}
|
||||
|
||||
@ -453,7 +471,7 @@ class course implements \core_analytics\analysable {
|
||||
global $DB;
|
||||
|
||||
// No logs if no students.
|
||||
if (empty($this->studentids)) {
|
||||
if (empty($this->get_students())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -606,7 +624,7 @@ class course implements \core_analytics\analysable {
|
||||
|
||||
// When the course is using format weeks we use the week's end date.
|
||||
$format = course_get_format($activity->get_modinfo()->get_course());
|
||||
if ($this->course->format === 'weeks') {
|
||||
if ($this->get_course_data()->format === 'weeks') {
|
||||
$dates = $format->get_section_dates($section);
|
||||
|
||||
// We need to consider the +2 hours added by get_section_dates.
|
||||
@ -628,7 +646,7 @@ class course implements \core_analytics\analysable {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!course_format_uses_sections($this->course->format)) {
|
||||
if (!course_format_uses_sections($this->get_course_data()->format)) {
|
||||
// If it does not use sections and there are no availability conditions to access it it is available
|
||||
// and we can not magically classify it into any other time range than this one.
|
||||
return true;
|
||||
@ -731,7 +749,7 @@ class course implements \core_analytics\analysable {
|
||||
}
|
||||
|
||||
// Check the amount of student logs in the 4 previous weeks.
|
||||
list($studentssql, $studentsparams) = $DB->get_in_or_equal($this->studentids, SQL_PARAMS_NAMED);
|
||||
list($studentssql, $studentsparams) = $DB->get_in_or_equal($this->get_students(), SQL_PARAMS_NAMED);
|
||||
$filterselect = $prefix . 'courseid = :courseid AND ' . $prefix . 'userid ' . $studentssql;
|
||||
$filterparams = array('courseid' => $this->course->id) + $studentsparams;
|
||||
|
||||
|
@ -47,14 +47,14 @@ abstract class by_course extends base {
|
||||
$courses = $this->options['filter'];
|
||||
} else {
|
||||
// Iterate through all potentially valid courses.
|
||||
$courses = get_courses('all', 'c.sortorder ASC');
|
||||
$courses = get_courses('all', 'c.sortorder ASC', 'c.id');
|
||||
}
|
||||
unset($courses[SITEID]);
|
||||
|
||||
$analysables = array();
|
||||
foreach ($courses as $course) {
|
||||
// Skip the frontpage course.
|
||||
$analysable = \core_analytics\course::instance($course);
|
||||
$analysable = \core_analytics\course::instance($course->id);
|
||||
$analysables[$analysable->get_id()] = $analysable;
|
||||
}
|
||||
|
||||
|
@ -86,7 +86,6 @@ class core_analytics_course_testcase extends advanced_testcase {
|
||||
$courseman = new \core_analytics\course($this->course->id);
|
||||
$this->assertFalse($courseman->was_started());
|
||||
$this->assertFalse($courseman->is_finished());
|
||||
$this->assertFalse($courseman->is_valid());
|
||||
|
||||
// Nothing should change when assigning as teacher.
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
@ -94,7 +93,8 @@ class core_analytics_course_testcase extends advanced_testcase {
|
||||
$this->getDataGenerator()->enrol_user($user->id, $this->course->id, $this->teacherroleid);
|
||||
}
|
||||
$courseman = new \core_analytics\course($this->course->id);
|
||||
$this->assertFalse($courseman->is_valid());
|
||||
$this->assertFalse($courseman->was_started());
|
||||
$this->assertFalse($courseman->is_finished());
|
||||
|
||||
// More students now.
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
@ -102,7 +102,8 @@ class core_analytics_course_testcase extends advanced_testcase {
|
||||
$this->getDataGenerator()->enrol_user($user->id, $this->course->id, $this->studentroleid);
|
||||
}
|
||||
$courseman = new \core_analytics\course($this->course->id);
|
||||
$this->assertFalse($courseman->is_valid());
|
||||
$this->assertFalse($courseman->was_started());
|
||||
$this->assertFalse($courseman->is_finished());
|
||||
|
||||
// Valid start date unknown end date.
|
||||
$this->course->startdate = gmmktime('0', '0', '0', 10, 24, 2015);
|
||||
@ -110,7 +111,6 @@ class core_analytics_course_testcase extends advanced_testcase {
|
||||
$courseman = new \core_analytics\course($this->course->id);
|
||||
$this->assertTrue($courseman->was_started());
|
||||
$this->assertFalse($courseman->is_finished());
|
||||
$this->assertFalse($courseman->is_valid());
|
||||
|
||||
// Valid start and end date.
|
||||
$this->course->enddate = gmmktime('0', '0', '0', 8, 27, 2016);
|
||||
@ -118,7 +118,6 @@ class core_analytics_course_testcase extends advanced_testcase {
|
||||
$courseman = new \core_analytics\course($this->course->id);
|
||||
$this->assertTrue($courseman->was_started());
|
||||
$this->assertTrue($courseman->is_finished());
|
||||
$this->assertTrue($courseman->is_valid());
|
||||
|
||||
// Valid start and ongoing course.
|
||||
$this->course->enddate = gmmktime('0', '0', '0', 8, 27, 2286);
|
||||
@ -126,7 +125,6 @@ class core_analytics_course_testcase extends advanced_testcase {
|
||||
$courseman = new \core_analytics\course($this->course->id);
|
||||
$this->assertTrue($courseman->was_started());
|
||||
$this->assertFalse($courseman->is_finished());
|
||||
$this->assertFalse($courseman->is_valid());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once(__DIR__ . '/fixtures/test_indicator_max.php');
|
||||
require_once(__DIR__ . '/fixtures/test_indicator_min.php');
|
||||
require_once(__DIR__ . '/fixtures/test_indicator_fullname.php');
|
||||
|
@ -25,9 +25,10 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once(__DIR__ . '/../../lib/completionlib.php');
|
||||
require_once(__DIR__ . '/../../completion/criteria/completion_criteria_self.php');
|
||||
|
||||
|
||||
/**
|
||||
* Unit tests for core_course indicators.
|
||||
*
|
||||
|
@ -220,7 +220,6 @@ class phpunit_util extends testing_util {
|
||||
if (class_exists('core_media_manager', false)) {
|
||||
core_media_manager::reset_caches();
|
||||
}
|
||||
\core_analytics\course::reset_caches();
|
||||
|
||||
// Reset static unit test options.
|
||||
if (class_exists('\availability_date\condition', false)) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user