This commit is contained in:
Sara Arjona 2024-02-14 09:30:09 +01:00
commit ce7f3eef04
No known key found for this signature in database
7 changed files with 428 additions and 41 deletions

View File

@ -23,6 +23,7 @@ use core_reportbuilder\datasource;
use core_reportbuilder\local\entities\{course, user};
use core_reportbuilder\local\helpers\database;
use core_badges\reportbuilder\local\entities\{badge, badge_issued};
use core_cohort\reportbuilder\local\entities\cohort;
use core_tag\reportbuilder\local\entities\tag;
/**
@ -88,17 +89,23 @@ class users extends datasource {
->add_join("LEFT JOIN {course} {$coursealias} ON {$coursealias}.id =
CASE WHEN {$badgealias}.id IS NULL THEN 0 ELSE COALESCE({$badgealias}.courseid, 1) END"));
// Join the cohort entity.
$cohortentity = new cohort();
$cohortalias = $cohortentity->get_table_alias('cohort');
$cohortmemberalias = database::generate_alias();
$this->add_entity($cohortentity->add_joins([
"LEFT JOIN {cohort_members} {$cohortmemberalias} ON {$cohortmemberalias}.userid = {$useralias}.id",
"LEFT JOIN {cohort} {$cohortalias} ON {$cohortalias}.id = {$cohortmemberalias}.cohortid",
]));
// Add report elements from each of the entities we added to the report.
$this->add_all_from_entity($userentity->get_entity_name());
$this->add_all_from_entity($badgeissuedentity->get_entity_name());
$this->add_all_from_entity($badgeentity->get_entity_name());
// Add specific tag entity elements.
$this->add_columns_from_entity($tagentity->get_entity_name(), ['name', 'namewithlink']);
$this->add_filter($tagentity->get_filter('name'));
$this->add_condition($tagentity->get_condition('name'));
$this->add_all_from_entity($tagentity->get_entity_name(), ['name', 'namewithlink'], ['name'], ['name']);
$this->add_all_from_entity($courseentity->get_entity_name());
$this->add_all_from_entity($cohortentity->get_entity_name(), ['name', 'idnumber', 'description', 'customfield*'],
['name', 'idnumber', 'customfield*'], ['name', 'idnumber', 'customfield*']);
}
/**

View File

@ -92,6 +92,9 @@ class users_test extends core_reportbuilder_testcase {
$course = $this->getDataGenerator()->create_course();
$user = $this->getDataGenerator()->create_and_enrol($course, 'student', ['firstname' => 'Zoe', 'lastname' => 'Zebra']);
$cohort = $this->getDataGenerator()->create_cohort(['name' => 'My cohort']);
cohort_add_member($cohort->id, $user->id);
/** @var core_badges_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_badges');
@ -137,14 +140,17 @@ class users_test extends core_reportbuilder_testcase {
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:fullname']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'cohort:name']);
$content = $this->get_custom_report_content($report->get('id'));
$this->assertCount(3, $content);
// Admin user, no badge issued.
[$fullname, $badgename, $criteria, $image, $language, $version, $status, $expiry, $tag, $expires, $visible, $coursename]
= array_values($content[0]);
[$fullname, $badgename, $namewithlink, $criteria, $image, $language, $version, $status, $expiry, $tag, $expires,
$visible, $coursename, $cohortname] = array_values($content[0]);
$this->assertEquals('Admin User', $fullname);
$this->assertEmpty($badgename);
$this->assertEmpty($namewithlink);
$this->assertEmpty($criteria);
$this->assertEmpty($image);
$this->assertEmpty($language);
@ -155,13 +161,14 @@ class users_test extends core_reportbuilder_testcase {
$this->assertEmpty($expires);
$this->assertEmpty($visible);
$this->assertEmpty($coursename);
$this->assertEmpty($cohortname);
$expectedbadgesitelink = \html_writer::link(new \moodle_url('/badges/overview.php',
['id' => $badgesite->id]), ($badgesite->name));
// User issued site badge.
[$fullname, $badgename, $namewithlink, $criteria, $image, $language, $version, $status, $expiry, $tag, $expires,
$visible, $coursename] = array_values($content[1]);
$visible, $coursename, $cohortname] = array_values($content[1]);
$this->assertEquals(fullname($user), $fullname);
$this->assertEquals($badgesite->name, $badgename);
$this->assertEquals($expectedbadgesitelink, $namewithlink);
@ -175,13 +182,14 @@ class users_test extends core_reportbuilder_testcase {
$this->assertNotEmpty($expires);
$this->assertEquals('Yes', $visible);
$this->assertEquals('PHPUnit test site', $coursename);
$this->assertEquals($cohort->name, $cohortname);
$expectedbadgecourselink = \html_writer::link(new \moodle_url('/badges/overview.php',
['id' => $badgecourse->id]), ($badgecourse->name));
// User issued course badge.
[$fullname, $badgename, $namewithlink, $criteria, $image, $language, $version, $status, $expiry, $tag, $expires,
$visible, $coursename] = array_values($content[2]);
$visible, $coursename, $cohortname] = array_values($content[2]);
$this->assertEquals(fullname($user), $fullname);
$this->assertEquals($badgecourse->name, $badgename);
$this->assertEquals($expectedbadgecourselink, $namewithlink);
@ -195,6 +203,7 @@ class users_test extends core_reportbuilder_testcase {
$this->assertEmpty($expires);
$this->assertEquals('Yes', $visible);
$this->assertEquals($course->fullname, $coursename);
$this->assertEquals($cohort->name, $cohortname);
}
/**
@ -282,6 +291,16 @@ class users_test extends core_reportbuilder_testcase {
'course:fullname_operator' => text::IS_EQUAL_TO,
'course:fullname_value' => 'Course 2',
], false],
// Cohort.
'Filter cohort name' => ['cohort:name', [
'cohort:name_operator' => text::IS_EQUAL_TO,
'cohort:name_value' => 'My cohort',
], true],
'Filter cohort name (no match)' => ['cohort:name', [
'cohort:name_operator' => text::IS_EQUAL_TO,
'cohort:name_value' => 'Not my cohort',
], false],
];
}
@ -300,6 +319,9 @@ class users_test extends core_reportbuilder_testcase {
$course = $this->getDataGenerator()->create_course(['fullname' => 'Course 1']);
$user = $this->getDataGenerator()->create_and_enrol($course, 'student', ['firstname' => 'Zoe', 'lastname' => 'Zebra']);
$cohort = $this->getDataGenerator()->create_cohort(['name' => 'My cohort']);
cohort_add_member($cohort->id, $user->id);
/** @var core_badges_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_badges');
$badge = $generator->create_badge([

View File

@ -57,9 +57,11 @@ abstract class datasource extends base {
/**
* Add columns from the given entity name to be available to use in a custom report
*
* Wildcard matching is supported with '*' in both $include and $exclude, e.g. ['customfield*']
*
* @param string $entityname
* @param array $include Include only these columns, if omitted then include all
* @param array $exclude Exclude these columns, if omitted then exclude none
* @param string[] $include Include only these columns, if omitted then include all
* @param string[] $exclude Exclude these columns, if omitted then exclude none
* @throws coding_exception If both $include and $exclude are non-empty
*/
final protected function add_columns_from_entity(string $entityname, array $include = [], array $exclude = []): void {
@ -70,13 +72,13 @@ abstract class datasource extends base {
$entity = $this->get_entity($entityname);
// Retrieve filtered columns from entity, respecting given $include/$exclude parameters.
$columns = array_filter($entity->get_columns(), static function(column $column) use ($include, $exclude): bool {
$columns = array_filter($entity->get_columns(), function(column $column) use ($include, $exclude): bool {
if (!empty($include)) {
return in_array($column->get_name(), $include);
return $this->report_element_search($column->get_name(), $include);
}
if (!empty($exclude)) {
return !in_array($column->get_name(), $exclude);
return !$this->report_element_search($column->get_name(), $exclude);
}
return true;
@ -188,9 +190,11 @@ abstract class datasource extends base {
/**
* Add filters from the given entity name to be available to use in a custom report
*
* Wildcard matching is supported with '*' in both $include and $exclude, e.g. ['customfield*']
*
* @param string $entityname
* @param array $include Include only these filters, if omitted then include all
* @param array $exclude Exclude these filters, if omitted then exclude none
* @param string[] $include Include only these filters, if omitted then include all
* @param string[] $exclude Exclude these filters, if omitted then exclude none
* @throws coding_exception If both $include and $exclude are non-empty
*/
final protected function add_filters_from_entity(string $entityname, array $include = [], array $exclude = []): void {
@ -201,13 +205,13 @@ abstract class datasource extends base {
$entity = $this->get_entity($entityname);
// Retrieve filtered filters from entity, respecting given $include/$exclude parameters.
$filters = array_filter($entity->get_filters(), static function(filter $filter) use ($include, $exclude): bool {
$filters = array_filter($entity->get_filters(), function(filter $filter) use ($include, $exclude): bool {
if (!empty($include)) {
return in_array($filter->get_name(), $include);
return $this->report_element_search($filter->get_name(), $include);
}
if (!empty($exclude)) {
return !in_array($filter->get_name(), $exclude);
return !$this->report_element_search($filter->get_name(), $exclude);
}
return true;
@ -278,9 +282,11 @@ abstract class datasource extends base {
/**
* Add conditions from the given entity name to be available to use in a custom report
*
* Wildcard matching is supported with '*' in both $include and $exclude, e.g. ['customfield*']
*
* @param string $entityname
* @param array $include Include only these conditions, if omitted then include all
* @param array $exclude Exclude these conditions, if omitted then exclude none
* @param string[] $include Include only these conditions, if omitted then include all
* @param string[] $exclude Exclude these conditions, if omitted then exclude none
* @throws coding_exception If both $include and $exclude are non-empty
*/
final protected function add_conditions_from_entity(string $entityname, array $include = [], array $exclude = []): void {
@ -291,13 +297,13 @@ abstract class datasource extends base {
$entity = $this->get_entity($entityname);
// Retrieve filtered conditions from entity, respecting given $include/$exclude parameters.
$conditions = array_filter($entity->get_conditions(), static function(filter $condition) use ($include, $exclude): bool {
$conditions = array_filter($entity->get_conditions(), function(filter $condition) use ($include, $exclude): bool {
if (!empty($include)) {
return in_array($condition->get_name(), $include);
return $this->report_element_search($condition->get_name(), $include);
}
if (!empty($exclude)) {
return !in_array($condition->get_name(), $exclude);
return !$this->report_element_search($condition->get_name(), $exclude);
}
return true;
@ -384,11 +390,19 @@ abstract class datasource extends base {
* Adds all columns/filters/conditions from the given entity to the report at once
*
* @param string $entityname
* @param string[] $limitcolumns Include only these columns
* @param string[] $limitfilters Include only these filters
* @param string[] $limitconditions Include only these conditions
*/
final protected function add_all_from_entity(string $entityname): void {
$this->add_columns_from_entity($entityname);
$this->add_filters_from_entity($entityname);
$this->add_conditions_from_entity($entityname);
final protected function add_all_from_entity(
string $entityname,
array $limitcolumns = [],
array $limitfilters = [],
array $limitconditions = [],
): void {
$this->add_columns_from_entity($entityname, $limitcolumns);
$this->add_filters_from_entity($entityname, $limitfilters);
$this->add_conditions_from_entity($entityname, $limitconditions);
}
/**
@ -408,4 +422,28 @@ abstract class datasource extends base {
final public static function report_elements_modified(int $reportid): void {
self::$elementsmodified[$reportid] = microtime(true);
}
/**
* Search for given element within list of search items, supporting '*' wildcards
*
* @param string $element
* @param string[] $search
* @return bool
*/
private function report_element_search(string $element, array $search): bool {
foreach ($search as $item) {
// Simple matching.
if ($element === $item) {
return true;
}
// Wildcard matching.
if (strpos($item, '*') !== false) {
$pattern = '/^' . str_replace('\*', '.*', preg_quote($item)) . '$/';
return (bool) preg_match($pattern, $element);
}
}
return false;
}
}

View File

@ -0,0 +1,291 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Unit tests for base datasource
*
* @package core_reportbuilder
* @copyright 2023 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types=1);
namespace core_reportbuilder;
use advanced_testcase;
use core_reportbuilder_generator;
use core_reportbuilder\local\entities\user;
use core_reportbuilder\local\report\{column, filter};
use lang_string;
use ReflectionClass;
defined('MOODLE_INTERNAL') || die();
/**
* Unit tests for base datasource
*
* @package core_reportbuilder
* @coversDefaultClass \core_reportbuilder\datasource
* @copyright 2023 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class datasource_test extends advanced_testcase {
/**
* Data provider for {@see test_add_columns_from_entity}
*
* @return array[]
*/
public static function add_columns_from_entity_provider(): array {
return [
'All column' => [
[],
[],
29,
],
'Include columns (picture, fullname, fullnamewithlink, fullnamewithpicture, fullnamewithpicturelink)' => [
['picture', 'fullname*'],
[],
5,
],
'Exclude columns (picture, fullname, fullnamewithlink, fullnamewithpicture, fullnamewithpicturelink)' => [
[],
['picture', 'fullname*'],
24,
],
];
}
/**
* Test adding columns from entity
*
* @param string[] $include
* @param string[] $exclude
* @param int $expectedcount
*
* @covers ::add_columns_from_entity
*
* @dataProvider add_columns_from_entity_provider
*/
public function test_add_columns_from_entity(
array $include,
array $exclude,
int $expectedcount,
): void {
$instance = $this->get_datasource_test_source();
$method = (new ReflectionClass($instance))->getMethod('add_columns_from_entity');
$method->setAccessible(true);
$method->invoke($instance, 'user', $include, $exclude);
// Get all our user entity columns.
$columns = array_filter(
$instance->get_columns(),
fn(string $columnname) => strpos($columnname, 'user:') === 0,
ARRAY_FILTER_USE_KEY,
);
$this->assertCount($expectedcount, $columns);
}
/**
* Data provider for {@see test_add_filters_from_entity}
*
* @return array[]
*/
public static function add_filters_from_entity_provider(): array {
return [
'All filters' => [
[],
[],
27,
],
'Include filters (department, phone1, phone2)' => [
['department', 'phone*'],
[],
3,
],
'Exclude filters (department, phone1, phone2)' => [
[],
['department', 'phone*'],
24,
],
];
}
/**
* Test adding filters from entity
*
* @param string[] $include
* @param string[] $exclude
* @param int $expectedcount
*
* @covers ::add_filters_from_entity
*
* @dataProvider add_filters_from_entity_provider
*/
public function test_add_filters_from_entity(
array $include,
array $exclude,
int $expectedcount,
): void {
$instance = $this->get_datasource_test_source();
$method = (new ReflectionClass($instance))->getMethod('add_filters_from_entity');
$method->setAccessible(true);
$method->invoke($instance, 'user', $include, $exclude);
// Get all our user entity filters.
$filters = array_filter(
$instance->get_filters(),
fn(string $filtername) => strpos($filtername, 'user:') === 0,
ARRAY_FILTER_USE_KEY,
);
$this->assertCount($expectedcount, $filters);
}
/**
* Data provider for {@see test_add_conditions_from_entity}
*
* @return array[]
*/
public static function add_conditions_from_entity_provider(): array {
return [
'All conditions' => [
[],
[],
27,
],
'Include conditions (department, phone1, phone2)' => [
['department', 'phone*'],
[],
3,
],
'Exclude conditions (department, phone1, phone2)' => [
[],
['department', 'phone*'],
24,
],
];
}
/**
* Test adding conditions from entity
*
* @param string[] $include
* @param string[] $exclude
* @param int $expectedcount
*
* @covers ::add_conditions_from_entity
*
* @dataProvider add_conditions_from_entity_provider
*/
public function test_add_conditions_from_entity(
array $include,
array $exclude,
int $expectedcount,
): void {
$instance = $this->get_datasource_test_source();
$method = (new ReflectionClass($instance))->getMethod('add_conditions_from_entity');
$method->setAccessible(true);
$method->invoke($instance, 'user', $include, $exclude);
// Get all our user entity conditions.
$conditions = array_filter(
$instance->get_conditions(),
fn(string $conditionname) => strpos($conditionname, 'user:') === 0,
ARRAY_FILTER_USE_KEY,
);
$this->assertCount($expectedcount, $conditions);
}
/**
* Test adding all from entity
*
* @covers ::add_all_from_entity
*/
public function test_add_all_from_entity(): void {
$instance = $this->get_datasource_test_source();
$method = (new ReflectionClass($instance))->getMethod('add_all_from_entity');
$method->setAccessible(true);
$method->invoke($instance, 'user', ['username'], ['firstname'], ['lastname']);
// Assert the column we added (plus one we didn't).
$this->assertInstanceOf(column::class, $instance->get_column('user:username'));
$this->assertNull($instance->get_column('user:email'));
// Assert the filter we added (plus one we didn't).
$this->assertInstanceOf(filter::class, $instance->get_filter('user:firstname'));
$this->assertNull($instance->get_filter('user:email'));
// Assert the condition we added (plus one we didn't).
$this->assertInstanceOf(filter::class, $instance->get_condition('user:lastname'));
$this->assertNull($instance->get_condition('user:email'));
}
/**
* Create and return our test datasource instance
*
* @return datasource_test_source
*/
protected function get_datasource_test_source(): datasource_test_source {
$this->resetAfterTest();
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
$report = $generator->create_report(['name' => 'Test', 'source' => datasource_test_source::class, 'default' => 0]);
/** @var datasource_test_source $instance */
$instance = manager::get_report_from_persistent($report);
return $instance;
}
}
/**
* Simple implementation of the base datasource
*/
class datasource_test_source extends datasource {
protected function initialise(): void {
$this->set_main_table('user', 'u');
$this->annotate_entity('dummy', new lang_string('yes'));
$this->add_column(new column('test', null, 'dummy'));
// This is the entity from which we'll add our report elements.
$this->add_entity(new user());
}
public static function get_name(): string {
return self::class;
}
public function get_default_columns(): array {
return [];
}
public function get_default_filters(): array {
return [];
}
public function get_default_conditions(): array {
return [];
}
}

View File

@ -9,6 +9,9 @@ Information provided here is intended especially for developers.
* New method `get_table_aliases` in base entity class, for retrieving all table aliases in a single call
* The database helper `generate_alias[es]` and `generate_param_name[s]` methods now accept an optional `$suffix` argument for
appending additional string to the generated value
* The base datasource `add_all_from_entity` method accepts additional parameters to limit which columns, filters and conditions
are added. The `add_[columns|filters|conditions]_from_entity` class methods also now support wildcard matching in both `$include`
and `$exclude` parameters
* New report filter types:
- `filesize` for reports containing filesize data

View File

@ -19,6 +19,7 @@ declare(strict_types=1);
namespace core_user\reportbuilder\datasource;
use lang_string;
use core_cohort\reportbuilder\local\entities\cohort;
use core_reportbuilder\datasource;
use core_reportbuilder\local\entities\user;
use core_reportbuilder\local\filters\boolean_select;
@ -50,17 +51,16 @@ class users extends datasource {
global $CFG;
$userentity = new user();
$usertablealias = $userentity->get_table_alias('user');
$useralias = $userentity->get_table_alias('user');
$this->set_main_table('user', $usertablealias);
$this->set_main_table('user', $useralias);
$this->add_entity($userentity);
$userparamguest = database::generate_param_name();
$this->add_base_condition_sql("{$usertablealias}.id != :{$userparamguest} AND {$usertablealias}.deleted = 0", [
$this->add_base_condition_sql("{$useralias}.id != :{$userparamguest} AND {$useralias}.deleted = 0", [
$userparamguest => $CFG->siteguest,
]);
$this->add_entity($userentity);
// Join the tag entity.
$tagentity = (new tag())
->set_table_alias('tag', $userentity->get_table_alias('tag'))
@ -68,13 +68,20 @@ class users extends datasource {
$this->add_entity($tagentity
->add_joins($userentity->get_tag_joins()));
// Join the cohort entity.
$cohortentity = new cohort();
$cohortalias = $cohortentity->get_table_alias('cohort');
$cohortmemberalias = database::generate_alias();
$this->add_entity($cohortentity->add_joins([
"LEFT JOIN {cohort_members} {$cohortmemberalias} ON {$cohortmemberalias}.userid = {$useralias}.id",
"LEFT JOIN {cohort} {$cohortalias} ON {$cohortalias}.id = {$cohortmemberalias}.cohortid",
]));
// Add all columns/filters/conditions from entities to be available in custom reports.
$this->add_all_from_entity($userentity->get_entity_name());
// Add specific tag entity elements.
$this->add_columns_from_entity($tagentity->get_entity_name(), ['name', 'namewithlink']);
$this->add_filter($tagentity->get_filter('name'));
$this->add_condition($tagentity->get_condition('name'));
$this->add_all_from_entity($tagentity->get_entity_name(), ['name', 'namewithlink'], ['name'], ['name']);
$this->add_all_from_entity($cohortentity->get_entity_name(), ['name', 'idnumber', 'description', 'customfield*'],
['name', 'idnumber', 'customfield*'], ['name', 'idnumber', 'customfield*']);
}
/**
@ -123,7 +130,7 @@ class users extends datasource {
/**
* Return the default sorting that will be added to the report once it is created
*
* @return array|int[]
* @return int[]
*/
public function get_default_column_sorting(): array {
return [

View File

@ -80,6 +80,9 @@ class users_test extends core_reportbuilder_testcase {
'interests' => ['Horses'],
]);
$cohort = $this->getDataGenerator()->create_cohort(['name' => 'My cohort']);
cohort_add_member($cohort->id, $user->id);
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
$report = $generator->create_report(['name' => 'Users', 'source' => users::class, 'default' => 0]);
@ -116,6 +119,9 @@ class users_test extends core_reportbuilder_testcase {
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'tag:name']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'tag:namewithlink']);
// Cohort.
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'cohort:name']);
$content = $this->get_custom_report_content($report->get('id'));
$this->assertCount(2, $content);
@ -155,6 +161,7 @@ class users_test extends core_reportbuilder_testcase {
$this->assertEquals('0.0.0.0', $userrow[24]);
$this->assertEquals('Horses', $userrow[25]);
$this->assertStringContainsString('Horses', $userrow[26]);
$this->assertEquals($cohort->name, $userrow[27]);
}
/**
@ -257,7 +264,6 @@ class users_test extends core_reportbuilder_testcase {
'user:address_operator' => text::IS_EQUAL_TO,
'user:address_value' => 'Small Farm',
], false],
'Filter city' => ['user:city', [
'user:city_operator' => text::IS_EQUAL_TO,
'user:city_value' => 'Barcelona',
@ -376,6 +382,16 @@ class users_test extends core_reportbuilder_testcase {
'Filter tag name not empty' => ['tag:name', [
'tag:name_operator' => tags::NOT_EMPTY,
], true],
// Cohort.
'Filter cohort name' => ['cohort:name', [
'cohort:name_operator' => text::IS_EQUAL_TO,
'cohort:name_value' => 'My cohort',
], true],
'Filter cohort name (no match)' => ['cohort:name', [
'cohort:name_operator' => text::IS_EQUAL_TO,
'cohort:name_value' => 'Not my cohort',
], false],
];
}
@ -414,6 +430,9 @@ class users_test extends core_reportbuilder_testcase {
'lastip' => '0.0.0.0',
]);
$cohort = $this->getDataGenerator()->create_cohort(['name' => 'My cohort']);
cohort_add_member($cohort->id, $user->id);
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');