Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
integration
prechecker
Commits
1e678271
Commit
1e678271
authored
Oct 26, 2017
by
Jake Dallimore
Browse files
Merge branch 'MDL-60434_master' of
git://github.com/dmonllao/moodle
parents
7dfc9ea6
3a89d0b5
Changes
7
Hide whitespace changes
Inline
Side-by-side
analytics/classes/analysable.php
View file @
1e678271
...
...
@@ -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
...
...
analytics/classes/course.php
View file @
1e678271
...
...
@@ -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.
*
* L
oads
course students and teachers.
* L
azy load of
course
data,
students and teachers.
*
* @param int|stdClass $course Course id
* @return void
...
...
@@ -130,35 +140,19 @@ 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
);
$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'
);
if
(
!
$studentroles
=
$cache
->
get
(
'student'
))
{
$studentroles
=
array_keys
(
get_archetype_roles
(
'student'
));
$cache
->
set
(
'student'
,
$studentroles
);
}
$this
->
studentids
=
$this
->
get_user_ids
(
$studentroles
);
if
(
!
$teacherroles
=
$cache
->
get
(
'teacher'
))
{
$teacherroles
=
array_keys
(
get_archetype_roles
(
'editingteacher'
)
+
get_archetype_roles
(
'teacher'
));
$cache
->
set
(
'teacher'
,
$teacherroles
);
}
$this
->
teacherids
=
$this
->
get_user_ids
(
$teacherroles
);
}
/**
* Returns an analytics course instance.
*
* @param int|stdClass $course Course id
* 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
)
{
...
...
@@ -168,31 +162,59 @@ class course implements \core_analytics\analysable {
$courseid
=
$course
->
id
;
}
if
(
!
empty
(
self
::
$instances
[
$courseid
])
)
{
return
self
::
$instance
s
[
$courseid
]
;
if
(
self
::
$cachedid
===
$courseid
)
{
return
self
::
$
cached
instance
;
}
$instance
=
new
\
core_analytics\course
(
$course
);
self
::
$instances
[
$courseid
]
=
$instance
;
return
self
::
$instances
[
$courseid
];
$cachedinstance
=
new
\
core_analytics\course
(
$course
);
self
::
$cachedinstance
=
$cachedinstance
;
self
::
$cachedid
=
(
int
)
$courseid
;
return
self
::
$cachedinstance
;
}
/**
*
Clears all statically cached instances.
*
get_id
*
* @return
void
* @return
int
*/
public
static
function
reset_caches
()
{
self
::
$instances
=
array
()
;
public
function
get_id
()
{
return
$this
->
course
->
id
;
}
/**
*
get_id
*
Loads the analytics course object.
*
* @return
int
* @return
null
*/
public
function
get_id
()
{
return
$this
->
course
->
id
;
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
);
}
$this
->
studentids
=
$this
->
get_user_ids
(
$studentroles
);
if
(
!
$teacherroles
=
$cache
->
get
(
'teacher'
))
{
$teacherroles
=
array_keys
(
get_archetype_roles
(
'editingteacher'
)
+
get_archetype_roles
(
'teacher'
));
$cache
->
set
(
'teacher'
,
$teacherroles
);
}
$this
->
teacherids
=
$this
->
get_user_ids
(
$teacherroles
);
}
/**
...
...
@@ -201,7 +223,7 @@ class course implements \core_analytics\analysable {
* @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
->
student
ids
as
$studentid
)
{
foreach
(
$this
->
get_
student
s
()
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
->
student
ids
);
$studentenrolments
=
enrol_get_course_users
(
$this
->
get_id
(),
$this
->
get_
student
s
()
);
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
tru
e
;
return
$this
->
cours
e
;
}
/**
...
...
@@ -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
->
course
context
,
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
->
student
ids
))
{
if
(
empty
(
$this
->
get_
student
s
()
))
{
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
->
student
ids
,
SQL_PARAMS_NAMED
);
list
(
$studentssql
,
$studentsparams
)
=
$DB
->
get_in_or_equal
(
$this
->
get_
student
s
()
,
SQL_PARAMS_NAMED
);
$filterselect
=
$prefix
.
'courseid = :courseid AND '
.
$prefix
.
'userid '
.
$studentssql
;
$filterparams
=
array
(
'courseid'
=>
$this
->
course
->
id
)
+
$studentsparams
;
...
...
analytics/classes/local/analyser/by_course.php
View file @
1e678271
...
...
@@ -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
;
}
...
...
analytics/tests/course_test.php
View file @
1e678271
...
...
@@ -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
());
}
/**
...
...
analytics/tests/prediction_test.php
View file @
1e678271
...
...
@@ -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'
);
...
...
course/tests/indicators_test.php
View file @
1e678271
...
...
@@ -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.
*
...
...
lib/phpunit/classes/util.php
View file @
1e678271
...
...
@@ -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
))
{
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment