mirror of
https://github.com/moodle/moodle.git
synced 2025-08-02 07:39:54 +02:00
MDL-64739 core_analytics: Contexts param for get_analysables_iterator
This commit is contained in:
parent
aaff6692a1
commit
c22fb4bd4b
6 changed files with 73 additions and 40 deletions
|
@ -131,9 +131,10 @@ abstract class base {
|
|||
* to ease to implementation of get_analysables_iterator: get_iterator_sql and order_sql.
|
||||
*
|
||||
* @param string|null $action 'prediction', 'training' or null if no specific action needed.
|
||||
* @param \context[] $contexts Only analysables that depend on the provided contexts. All analysables in the system if empty.
|
||||
* @return \Iterator
|
||||
*/
|
||||
public function get_analysables_iterator(?string $action = null) {
|
||||
public function get_analysables_iterator(?string $action = null, array $contexts = []) {
|
||||
|
||||
debugging('Please overwrite get_analysables_iterator with your own implementation, we only keep this default
|
||||
implementation for backwards compatibility purposes with get_analysables(). note that $action param will
|
||||
|
@ -431,9 +432,12 @@ abstract class base {
|
|||
* @param int $contextlevel The context level of the analysable
|
||||
* @param string|null $action
|
||||
* @param string|null $tablealias The table alias
|
||||
* @param \context[] $contexts Only analysables that depend on the provided contexts. All analysables if empty.
|
||||
* @return array [0] => sql and [1] => params array
|
||||
*/
|
||||
protected function get_iterator_sql(string $tablename, int $contextlevel, ?string $action = null, ?string $tablealias = null) {
|
||||
protected function get_iterator_sql(string $tablename, int $contextlevel, ?string $action = null, ?string $tablealias = null,
|
||||
array $contexts = []) {
|
||||
global $DB;
|
||||
|
||||
if (!$tablealias) {
|
||||
$tablealias = 'analysable';
|
||||
|
@ -452,13 +456,30 @@ abstract class base {
|
|||
$params = $params + ['action' => $action];
|
||||
}
|
||||
|
||||
// Adding the 1 = 1 just to have the WHERE part so that all further conditions added by callers can be
|
||||
// appended to $sql with and ' AND'.
|
||||
$sql = 'SELECT ' . $select . '
|
||||
FROM {' . $tablename . '} ' . $tablealias . '
|
||||
' . $usedanalysablesjoin . '
|
||||
JOIN {context} ctx ON (ctx.contextlevel = :contextlevel AND ctx.instanceid = ' . $tablealias . '.id)
|
||||
WHERE 1 = 1';
|
||||
JOIN {context} ctx ON (ctx.contextlevel = :contextlevel AND ctx.instanceid = ' . $tablealias . '.id) ';
|
||||
|
||||
if (!$contexts) {
|
||||
// Adding the 1 = 1 just to have the WHERE part so that all further conditions
|
||||
// added by callers can be appended to $sql with and ' AND'.
|
||||
$sql .= 'WHERE 1 = 1';
|
||||
} else {
|
||||
|
||||
$contextsqls = [];
|
||||
foreach ($contexts as $context) {
|
||||
$paramkey1 = 'paramctxlike' . $context->id;
|
||||
$paramkey2 = 'paramctxeq' . $context->id;
|
||||
$contextsqls[] = $DB->sql_like('ctx.path', ':' . $paramkey1);
|
||||
$contextsqls[] = 'ctx.path = :' . $paramkey2;
|
||||
|
||||
// This includes the context itself.
|
||||
$params[$paramkey1] = $context->path . '/%';
|
||||
$params[$paramkey2] = $context->path;
|
||||
}
|
||||
$sql .= 'WHERE (' . implode(' OR ', $contextsqls) . ')';
|
||||
}
|
||||
|
||||
return [$sql, $params];
|
||||
}
|
||||
|
|
|
@ -39,24 +39,13 @@ abstract class by_course extends base {
|
|||
* Return the list of courses to analyse.
|
||||
*
|
||||
* @param string|null $action 'prediction', 'training' or null if no specific action needed.
|
||||
* @param \context[] $contexts Only analysables that depend on the provided contexts. All analysables in the system if empty.
|
||||
* @return \Iterator
|
||||
*/
|
||||
public function get_analysables_iterator(?string $action = null) {
|
||||
public function get_analysables_iterator(?string $action = null, array $contexts = []) {
|
||||
global $DB;
|
||||
|
||||
list($sql, $params) = $this->get_iterator_sql('course', CONTEXT_COURSE, $action, 'c');
|
||||
|
||||
// This will be updated to filter by context as part of MDL-64739.
|
||||
if (!empty($this->options['filter'])) {
|
||||
$courses = array();
|
||||
foreach ($this->options['filter'] as $courseid) {
|
||||
$courses[$courseid] = intval($courseid);
|
||||
}
|
||||
|
||||
list($coursesql, $courseparams) = $DB->get_in_or_equal($courses, SQL_PARAMS_NAMED);
|
||||
$sql .= " AND c.id $coursesql";
|
||||
$params = $params + $courseparams;
|
||||
}
|
||||
list($sql, $params) = $this->get_iterator_sql('course', CONTEXT_COURSE, $action, 'c', $contexts);
|
||||
|
||||
$ordersql = $this->order_sql('sortorder', 'ASC', 'c');
|
||||
|
||||
|
@ -76,4 +65,4 @@ abstract class by_course extends base {
|
|||
return \core_analytics\course::instance($record, $context);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,9 +39,10 @@ abstract class sitewide extends base {
|
|||
* Return the list of analysables to analyse.
|
||||
*
|
||||
* @param string|null $action 'prediction', 'training' or null if no specific action needed.
|
||||
* @param \context[] $contexts Ignored here.
|
||||
* @return \Iterator
|
||||
*/
|
||||
public function get_analysables_iterator(?string $action = null) {
|
||||
public function get_analysables_iterator(?string $action = null, array $contexts = []) {
|
||||
// We can safely ignore $action as we have 1 single analysable element in this analyser.
|
||||
return new \ArrayIterator([new \core_analytics\site()]);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@ information provided here is intended especially for developers.
|
|||
* Predictions flagged as "Not useful" in models whose targets use analysers that provide multiple samples
|
||||
per analysable (e.g. students at risk or no teaching) have been updated to "Incorrectly flagged".
|
||||
* \core_analytics\predictor::delete_output_dir has a new 2nd parameter, $uniquemodelid.
|
||||
* Analyser's get_analysables_iterator and get_iterator_sql have a new $contexts parameter to limit the returned analysables to
|
||||
the ones that depend on the provided contexts.
|
||||
|
||||
=== 3.7 ===
|
||||
|
||||
|
|
|
@ -39,14 +39,15 @@ class users extends \core_analytics\local\analyser\base {
|
|||
* The site users are the analysable elements returned by this analyser.
|
||||
*
|
||||
* @param string|null $action 'prediction', 'training' or null if no specific action needed.
|
||||
* @param \context[] $contexts Only analysables that depend on the provided contexts. All analysables in the system if empty.
|
||||
* @return \Iterator
|
||||
*/
|
||||
public function get_analysables_iterator(?string $action = null) {
|
||||
public function get_analysables_iterator(?string $action = null, array $contexts = []) {
|
||||
global $DB, $CFG;
|
||||
|
||||
$siteadmins = explode(',', $CFG->siteadmins);
|
||||
|
||||
list($sql, $params) = $this->get_iterator_sql('user', CONTEXT_USER, $action, 'u');
|
||||
list($sql, $params) = $this->get_iterator_sql('user', CONTEXT_USER, $action, 'u', $contexts);
|
||||
|
||||
$sql .= " AND u.deleted = :deleted AND u.confirmed = :confirmed AND u.suspended = :suspended";
|
||||
$params = $params + ['deleted' => 0, 'confirmed' => 1, 'suspended' => 0];
|
||||
|
|
|
@ -48,16 +48,16 @@ class core_analytics_analysers_testcase extends advanced_testcase {
|
|||
public function test_courses_analyser() {
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
$course1 = $this->getDataGenerator()->create_course();
|
||||
$coursecontext = \context_course::instance($course1->id);
|
||||
|
||||
$target = new test_target_shortname();
|
||||
$analyser = new \core\analytics\analyser\courses(1, $target, [], [], []);
|
||||
$analysable = new \core_analytics\course($course);
|
||||
$analysable = new \core_analytics\course($course1);
|
||||
|
||||
$this->assertInstanceOf('\core_analytics\course', $analyser->get_sample_analysable($course->id));
|
||||
$this->assertInstanceOf('\core_analytics\course', $analyser->get_sample_analysable($course1->id));
|
||||
|
||||
$this->assertInstanceOf('\context_course', $analyser->sample_access_context($course->id));
|
||||
$this->assertInstanceOf('\context_course', $analyser->sample_access_context($course1->id));
|
||||
|
||||
// Just 1 sample per course.
|
||||
$class = new ReflectionClass('\core\analytics\analyser\courses');
|
||||
|
@ -66,8 +66,8 @@ class core_analytics_analysers_testcase extends advanced_testcase {
|
|||
list($sampleids, $samplesdata) = $method->invoke($analyser, $analysable);
|
||||
$this->assertCount(1, $sampleids);
|
||||
$sampleid = reset($sampleids);
|
||||
$this->assertEquals($course->id, $sampleid);
|
||||
$this->assertEquals($course->fullname, $samplesdata[$sampleid]['course']->fullname);
|
||||
$this->assertEquals($course1->id, $sampleid);
|
||||
$this->assertEquals($course1->fullname, $samplesdata[$sampleid]['course']->fullname);
|
||||
$this->assertEquals($coursecontext, $samplesdata[$sampleid]['context']);
|
||||
|
||||
// To compare it later.
|
||||
|
@ -75,6 +75,16 @@ class core_analytics_analysers_testcase extends advanced_testcase {
|
|||
list($sampleids, $samplesdata) = $analyser->get_samples(array($sampleid));
|
||||
$this->assertEquals($prevsampledata['context'], $samplesdata[$sampleid]['context']);
|
||||
$this->assertEquals($prevsampledata['course']->shortname, $samplesdata[$sampleid]['course']->shortname);
|
||||
|
||||
// Context restriction.
|
||||
$category1 = $this->getDataGenerator()->create_category();
|
||||
$category1context = \context_coursecat::instance($category1->id);
|
||||
$category2 = $this->getDataGenerator()->create_category();
|
||||
$category2context = \context_coursecat::instance($category2->id);
|
||||
$course2 = $this->getDataGenerator()->create_course(['category' => $category1->id]);
|
||||
$course3 = $this->getDataGenerator()->create_course(['category' => $category2->id]);
|
||||
$this->assertCount(2, $analyser->get_analysables_iterator(false, [$category1context, $category2context]));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -130,24 +140,24 @@ class core_analytics_analysers_testcase extends advanced_testcase {
|
|||
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
$course1 = $this->getDataGenerator()->create_course();
|
||||
$course1context = \context_course::instance($course1->id);
|
||||
|
||||
$user1 = $this->getDataGenerator()->create_user();
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
$user3 = $this->getDataGenerator()->create_user();
|
||||
|
||||
// Checking that suspended users are also included.
|
||||
$this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student');
|
||||
$this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student', 'manual', 0, 0, ENROL_USER_SUSPENDED);
|
||||
$this->getDataGenerator()->enrol_user($user3->id, $course->id, 'editingteacher');
|
||||
$enrol = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'manual'));
|
||||
$this->getDataGenerator()->enrol_user($user1->id, $course1->id, 'student');
|
||||
$this->getDataGenerator()->enrol_user($user2->id, $course1->id, 'student', 'manual', 0, 0, ENROL_USER_SUSPENDED);
|
||||
$this->getDataGenerator()->enrol_user($user3->id, $course1->id, 'editingteacher');
|
||||
$enrol = $DB->get_record('enrol', array('courseid' => $course1->id, 'enrol' => 'manual'));
|
||||
$ue1 = $DB->get_record('user_enrolments', array('userid' => $user1->id, 'enrolid' => $enrol->id));
|
||||
$ue2 = $DB->get_record('user_enrolments', array('userid' => $user2->id, 'enrolid' => $enrol->id));
|
||||
|
||||
$target = new test_target_shortname();
|
||||
$analyser = new \core\analytics\analyser\student_enrolments(1, $target, [], [], []);
|
||||
$analysable = new \core_analytics\course($course);
|
||||
$analysable = new \core_analytics\course($course1);
|
||||
|
||||
$this->assertInstanceOf('\core_analytics\course', $analyser->get_sample_analysable($ue1->id));
|
||||
$this->assertInstanceOf('\context_course', $analyser->sample_access_context($ue1->id));
|
||||
|
@ -165,8 +175,8 @@ class core_analytics_analysers_testcase extends advanced_testcase {
|
|||
// Shouldn't matter which one we select.
|
||||
$sampleid = $ue1->id;
|
||||
$this->assertEquals($ue1, $samplesdata[$sampleid]['user_enrolments']);
|
||||
$this->assertEquals($course->fullname, $samplesdata[$sampleid]['course']->fullname);
|
||||
$this->assertEquals($coursecontext, $samplesdata[$sampleid]['context']);
|
||||
$this->assertEquals($course1->fullname, $samplesdata[$sampleid]['course']->fullname);
|
||||
$this->assertEquals($course1context, $samplesdata[$sampleid]['context']);
|
||||
$this->assertEquals($user1->firstname, $samplesdata[$sampleid]['user']->firstname);
|
||||
|
||||
// To compare it later.
|
||||
|
@ -176,6 +186,15 @@ class core_analytics_analysers_testcase extends advanced_testcase {
|
|||
$this->assertEquals($prevsampledata['context'], $samplesdata[$sampleid]['context']);
|
||||
$this->assertEquals($prevsampledata['course']->shortname, $samplesdata[$sampleid]['course']->shortname);
|
||||
$this->assertEquals($prevsampledata['user']->firstname, $samplesdata[$sampleid]['user']->firstname);
|
||||
|
||||
// Context restriction.
|
||||
$category1 = $this->getDataGenerator()->create_category();
|
||||
$category1context = \context_coursecat::instance($category1->id);
|
||||
$category2 = $this->getDataGenerator()->create_category();
|
||||
$category2context = \context_coursecat::instance($category2->id);
|
||||
$course2 = $this->getDataGenerator()->create_course(['category' => $category1->id]);
|
||||
$course3 = $this->getDataGenerator()->create_course(['category' => $category2->id]);
|
||||
$this->assertCount(2, $analyser->get_analysables_iterator(false, [$category1context, $category2context]));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue