This commit is contained in:
Sara Arjona
2024-08-22 16:16:15 +02:00
6 changed files with 1259 additions and 0 deletions
@@ -0,0 +1,182 @@
<?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/>.
declare(strict_types=1);
namespace core_competency\reportbuilder\datasource;
use core\reportbuilder\local\entities\context;
use core_cohort\reportbuilder\local\entities\cohort;
use core_competency\reportbuilder\local\entities\{competency, framework, usercompetency};
use core_reportbuilder\datasource;
use core_reportbuilder\local\entities\user;
use core_reportbuilder\local\filters\boolean_select;
use core_reportbuilder\local\helpers\database;
/**
* Competencies datasource
*
* @package core_competency
* @copyright 2024 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class competencies extends datasource {
/**
* Return user friendly name of the report source
*
* @return string
*/
public static function get_name(): string {
return get_string('competencies', 'core_competency');
}
/**
* Initialise report
*/
protected function initialise(): void {
$frameworkentity = new framework();
$frameworkalias = $frameworkentity->get_table_alias('competency_framework');
$contextentity = new context();
$contextalias = $contextentity->get_table_alias('context');
$this->set_main_table('competency_framework', $frameworkalias);
// Join context entity (unconditionally, as table also used by both framework/competency entities).
$this->add_join("LEFT JOIN {context} {$contextalias} ON {$contextalias}.id = {$frameworkalias}.contextid");
$this->add_entity($contextentity);
$this->add_entity($frameworkentity
->set_table_join_alias('context', $contextalias)
);
// Join competency entity.
$competencyentity = new competency();
$competencyalias = $competencyentity->get_table_alias('competency');
$this->add_entity($competencyentity
->set_table_join_alias('context', $contextalias)
->add_join("LEFT JOIN {competency} {$competencyalias}
ON {$competencyalias}.competencyframeworkid = {$frameworkalias}.id")
);
// Join user competency entity.
$usercompetencyentity = new usercompetency();
$usercompetencyalias = $usercompetencyentity->get_table_alias('competency_usercomp');
$this->add_entity($usercompetencyentity
->add_joins($competencyentity->get_joins())
->add_join("LEFT JOIN {competency_usercomp} {$usercompetencyalias}
ON {$usercompetencyalias}.competencyid = {$competencyalias}.id")
);
// Join user entity.
$userentity = new user();
$useralias = $userentity->get_table_alias('user');
$this->add_entity($userentity
->add_joins($usercompetencyentity->get_joins())
->add_join("LEFT JOIN {user} {$useralias} ON {$useralias}.id = {$usercompetencyalias}.userid")
);
// Join cohort entity.
$cohortentity = new cohort();
$cohortalias = $cohortentity->get_table_alias('cohort');
$cohortmemberalias = database::generate_alias();
$this->add_entity($cohortentity
->add_joins($userentity->get_joins())
->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_entities([
$contextentity->get_entity_name(),
$frameworkentity->get_entity_name(),
$competencyentity->get_entity_name(),
$usercompetencyentity->get_entity_name(),
$userentity->get_entity_name(),
]);
$this->add_all_from_entity(
$cohortentity->get_entity_name(),
['name', 'idnumber', 'description', 'customfield*'],
['cohortselect', 'name', 'idnumber', 'customfield*'],
['cohortselect', 'name', 'idnumber', 'customfield*'],
);
}
/**
* Return the columns that will be added to the report upon creation
*
* @return string[]
*/
public function get_default_columns(): array {
return [
'framework:name',
'competency:name',
'user:fullname',
'usercompetency:proficient',
];
}
/**
* Return the column sorting that will be added to the report upon creation
*
* @return int[]
*/
public function get_default_column_sorting(): array {
return [
'framework:name' => SORT_ASC,
'competency:name' => SORT_ASC,
'user:fullname' => SORT_ASC,
];
}
/**
* Return the filters that will be added to the report upon creation
*
* @return string[]
*/
public function get_default_filters(): array {
return [
'competency:name',
'user:fullname',
];
}
/**
* Return the conditions that will be added to the report upon creation
*
* @return string[]
*/
public function get_default_conditions(): array {
return [
'framework:visible',
];
}
/**
* Return the condition values that will be added to the report upon creation
*
* @return array
*/
public function get_default_condition_values(): array {
return [
'framework:visible_operator' => boolean_select::CHECKED,
];
}
}
@@ -0,0 +1,238 @@
<?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/>.
declare(strict_types=1);
namespace core_competency\reportbuilder\local\entities;
use core\{context, context_helper};
use core\lang_string;
use core_reportbuilder\local\entities\base;
use core_reportbuilder\local\filters\{date, text};
use core_reportbuilder\local\helpers\{database, format};
use core_reportbuilder\local\report\{column, filter};
use stdClass;
/**
* Competency entity
*
* @package core_competency
* @copyright 2024 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class competency extends base {
/**
* Database tables that this entity uses
*
* @return string[]
*/
protected function get_default_tables(): array {
return [
'competency',
'context',
];
}
/**
* The default title for this entity
*
* @return lang_string
*/
protected function get_default_entity_title(): lang_string {
return new lang_string('competency', 'core_competency');
}
/**
* Initialise the entity
*
* @return base
*/
public function initialise(): base {
$columns = $this->get_all_columns();
foreach ($columns as $column) {
$this->add_column($column);
}
// All the filters defined by the entity can also be used as conditions.
$filters = $this->get_all_filters();
foreach ($filters as $filter) {
$this
->add_filter($filter)
->add_condition($filter);
}
return $this;
}
/**
* Returns list of all available columns
*
* @return column[]
*/
protected function get_all_columns(): array {
global $DB;
$contextalias = $this->get_table_alias('context');
$competencyalias = $this->get_table_alias('competency');
// Name.
$columns[] = (new column(
'name',
new lang_string('name'),
$this->get_entity_name(),
))
->add_joins($this->get_joins())
->add_field("{$competencyalias}.shortname")
->set_is_sortable(true);
// Description.
$descriptionfieldsql = "{$competencyalias}.description";
if ($DB->get_dbfamily() === 'oracle') {
$descriptionfieldsql = $DB->sql_order_by_text($descriptionfieldsql, 1024);
}
$columns[] = (new column(
'description',
new lang_string('description'),
$this->get_entity_name(),
))
->add_joins($this->get_joins())
->add_joins($this->get_context_joins())
->set_type(column::TYPE_LONGTEXT)
->add_field($descriptionfieldsql, 'description')
->add_field("{$competencyalias}.descriptionformat")
->add_fields(context_helper::get_preload_record_columns_sql($contextalias))
->add_callback(static function(?string $description, stdClass $competency): string {
if ($description === null) {
return '';
}
context_helper::preload_from_record(clone $competency);
$context = context::instance_by_id($competency->ctxid);
return format_text($description, $competency->descriptionformat, ['context' => $context->id]);
});
// ID number.
$columns[] = (new column(
'idnumber',
new lang_string('idnumber'),
$this->get_entity_name(),
))
->add_joins($this->get_joins())
->add_field("{$competencyalias}.idnumber")
->set_is_sortable(true);
// Time created.
$columns[] = (new column(
'timecreated',
new lang_string('timecreated', 'core_reportbuilder'),
$this->get_entity_name(),
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TIMESTAMP)
->add_field("{$competencyalias}.timecreated")
->set_is_sortable(true)
->add_callback([format::class, 'userdate']);
// Time modified.
$columns[] = (new column(
'timemodified',
new lang_string('timemodified', 'core_reportbuilder'),
$this->get_entity_name(),
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TIMESTAMP)
->add_field("{$competencyalias}.timemodified")
->set_is_sortable(true)
->add_callback([format::class, 'userdate']);
return $columns;
}
/**
* Return list of all available filters
*
* @return filter[]
*/
protected function get_all_filters(): array {
$competencyalias = $this->get_table_alias('competency');
// Name.
$filters[] = (new filter(
text::class,
'name',
new lang_string('name'),
$this->get_entity_name(),
"{$competencyalias}.shortname",
))
->add_joins($this->get_joins());
// ID number.
$filters[] = (new filter(
text::class,
'idnumber',
new lang_string('idnumber'),
$this->get_entity_name(),
"{$competencyalias}.idnumber",
))
->add_joins($this->get_joins());
// Time created.
$filters[] = (new filter(
date::class,
'timecreated',
new lang_string('timecreated', 'core_reportbuilder'),
$this->get_entity_name(),
"{$competencyalias}.timecreated",
))
->add_joins($this->get_joins());
// Time modified.
$filters[] = (new filter(
date::class,
'timemodified',
new lang_string('timemodified', 'core_reportbuilder'),
$this->get_entity_name(),
"{$competencyalias}.timemodified",
))
->add_joins($this->get_joins());
return $filters;
}
/**
* Return context joins
*
* @return string[]
*/
private function get_context_joins(): array {
// If the context table is already joined, we don't need to do that again.
if ($this->has_table_join_alias('context')) {
return [];
}
$frameworkalias = database::generate_alias();
$competencyalias = $this->get_table_alias('competency');
$contextalias = $this->get_table_alias('context');
return [
"LEFT JOIN {competency_framework} {$frameworkalias} ON {$frameworkalias}.id = {$competencyalias}.competencyframeworkid",
"LEFT JOIN {context} {$contextalias} ON {$contextalias}.id = {$frameworkalias}.contextid",
];
}
}
@@ -0,0 +1,280 @@
<?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/>.
declare(strict_types=1);
namespace core_competency\reportbuilder\local\entities;
use core\{context, context_helper};
use core\lang_string;
use core_reportbuilder\local\entities\base;
use core_reportbuilder\local\filters\{boolean_select, date, select, text};
use core_reportbuilder\local\helpers\format;
use core_reportbuilder\local\report\{column, filter};
use stdClass;
/**
* Competency framework entity
*
* @package core_competency
* @copyright 2024 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class framework extends base {
/**
* Database tables that this entity uses
*
* @return string[]
*/
protected function get_default_tables(): array {
return [
'competency_framework',
'context',
];
}
/**
* The default title for this entity
*
* @return lang_string
*/
protected function get_default_entity_title(): lang_string {
return new lang_string('competencyframework', 'core_competency');
}
/**
* Initialise the entity
*
* @return base
*/
public function initialise(): base {
$columns = $this->get_all_columns();
foreach ($columns as $column) {
$this->add_column($column);
}
// All the filters defined by the entity can also be used as conditions.
$filters = $this->get_all_filters();
foreach ($filters as $filter) {
$this
->add_filter($filter)
->add_condition($filter);
}
return $this;
}
/**
* Returns list of all available columns
*
* @return column[]
*/
protected function get_all_columns(): array {
global $DB;
$frameworkalias = $this->get_table_alias('competency_framework');
$contextalias = $this->get_table_alias('context');
// Name.
$columns[] = (new column(
'name',
new lang_string('name'),
$this->get_entity_name(),
))
->add_joins($this->get_joins())
->add_field("{$frameworkalias}.shortname")
->set_is_sortable(true);
// Description.
$descriptionfieldsql = "{$frameworkalias}.description";
if ($DB->get_dbfamily() === 'oracle') {
$descriptionfieldsql = $DB->sql_order_by_text($descriptionfieldsql, 1024);
}
$columns[] = (new column(
'description',
new lang_string('description'),
$this->get_entity_name(),
))
->add_joins($this->get_joins())
->add_join($this->get_context_join())
->set_type(column::TYPE_LONGTEXT)
->add_field($descriptionfieldsql, 'description')
->add_field("{$frameworkalias}.descriptionformat")
->add_fields(context_helper::get_preload_record_columns_sql($contextalias))
->add_callback(static function(?string $description, stdClass $framework): string {
if ($description === null) {
return '';
}
context_helper::preload_from_record(clone $framework);
$context = context::instance_by_id($framework->ctxid);
return format_text($description, $framework->descriptionformat, ['context' => $context->id]);
});
// ID number.
$columns[] = (new column(
'idnumber',
new lang_string('idnumber'),
$this->get_entity_name(),
))
->add_joins($this->get_joins())
->add_field("{$frameworkalias}.idnumber")
->set_is_sortable(true);
// Scale.
$columns[] = (new column(
'scale',
new lang_string('scale'),
$this->get_entity_name(),
))
->add_joins($this->get_joins())
->add_field("{$frameworkalias}.scaleid")
->add_callback(static function(?string $scaleid): string {
$scales = get_scales_menu();
return (string) ($scales[(int) $scaleid] ?? $scaleid);
});
// Visible.
$columns[] = (new column(
'visible',
new lang_string('visible'),
$this->get_entity_name(),
))
->add_joins($this->get_joins())
->set_type(column::TYPE_BOOLEAN)
->add_field("{$frameworkalias}.visible")
->set_is_sortable(true)
->add_callback([format::class, 'boolean_as_text']);
// Time created.
$columns[] = (new column(
'timecreated',
new lang_string('timecreated', 'core_reportbuilder'),
$this->get_entity_name(),
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TIMESTAMP)
->add_field("{$frameworkalias}.timecreated")
->set_is_sortable(true)
->add_callback([format::class, 'userdate']);
// Time modified.
$columns[] = (new column(
'timemodified',
new lang_string('timemodified', 'core_reportbuilder'),
$this->get_entity_name(),
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TIMESTAMP)
->add_field("{$frameworkalias}.timemodified")
->set_is_sortable(true)
->add_callback([format::class, 'userdate']);
return $columns;
}
/**
* Return list of all available filters
*
* @return filter[]
*/
protected function get_all_filters(): array {
$frameworkalias = $this->get_table_alias('competency_framework');
// Name.
$filters[] = (new filter(
text::class,
'name',
new lang_string('name'),
$this->get_entity_name(),
"{$frameworkalias}.shortname",
))
->add_joins($this->get_joins());
// ID number.
$filters[] = (new filter(
text::class,
'idnumber',
new lang_string('idnumber'),
$this->get_entity_name(),
"{$frameworkalias}.idnumber",
))
->add_joins($this->get_joins());
// Scale.
$filters[] = (new filter(
select::class,
'scale',
new lang_string('scale'),
$this->get_entity_name(),
"{$frameworkalias}.scaleid",
))
->add_joins($this->get_joins())
->set_options_callback('get_scales_menu');
// Visible.
$filters[] = (new filter(
boolean_select::class,
'visible',
new lang_string('visible'),
$this->get_entity_name(),
"{$frameworkalias}.visible",
))
->add_joins($this->get_joins());
// Time created.
$filters[] = (new filter(
date::class,
'timecreated',
new lang_string('timecreated', 'core_reportbuilder'),
$this->get_entity_name(),
"{$frameworkalias}.timecreated",
))
->add_joins($this->get_joins());
// Time modified.
$filters[] = (new filter(
date::class,
'timemodified',
new lang_string('timemodified', 'core_reportbuilder'),
$this->get_entity_name(),
"{$frameworkalias}.timemodified",
))
->add_joins($this->get_joins());
return $filters;
}
/**
* Return context join
*
* @return string
*/
private function get_context_join(): string {
// If the context table is already joined, we don't need to do that again.
if ($this->has_table_join_alias('context')) {
return '';
}
$frameworkalias = $this->get_table_alias('competency_framework');
$contextalias = $this->get_table_alias('context');
return "LEFT JOIN {context} {$contextalias} ON {$contextalias}.id = {$frameworkalias}.contextid";
}
}
@@ -0,0 +1,171 @@
<?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/>.
declare(strict_types=1);
namespace core_competency\reportbuilder\local\entities;
use core\lang_string;
use core_competency\{competency, user_competency};
use core_reportbuilder\local\entities\base;
use core_reportbuilder\local\filters\{boolean_select, select};
use core_reportbuilder\local\helpers\format;
use core_reportbuilder\local\report\{column, filter};
use stdClass;
/**
* User competency entity
*
* @package core_competency
* @copyright 2024 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class usercompetency extends base {
/**
* Database tables that this entity uses
*
* @return string[]
*/
protected function get_default_tables(): array {
return [
'competency_usercomp',
];
}
/**
* The default title for this entity
*
* @return lang_string
*/
protected function get_default_entity_title(): lang_string {
return new lang_string('usercompetency', 'core_competency');
}
/**
* Initialise the entity
*
* @return base
*/
public function initialise(): base {
$columns = $this->get_all_columns();
foreach ($columns as $column) {
$this->add_column($column);
}
// All the filters defined by the entity can also be used as conditions.
$filters = $this->get_all_filters();
foreach ($filters as $filter) {
$this
->add_filter($filter)
->add_condition($filter);
}
return $this;
}
/**
* Returns list of all available columns
*
* @return column[]
*/
protected function get_all_columns(): array {
$usercompetencyalias = $this->get_table_alias('competency_usercomp');
// Status.
$columns[] = (new column(
'status',
new lang_string('status'),
$this->get_entity_name(),
))
->add_joins($this->get_joins())
->set_type(column::TYPE_TEXT)
->add_field("{$usercompetencyalias}.status")
->set_is_sortable(true)
->add_callback(static function(?string $status): string {
if ($status === null) {
return '';
}
return (string) user_competency::get_status_name((int) $status);
});
// Proficient.
$columns[] = (new column(
'proficient',
new lang_string('proficient', 'core_competency'),
$this->get_entity_name(),
))
->add_joins($this->get_joins())
->set_type(column::TYPE_BOOLEAN)
->add_field("{$usercompetencyalias}.proficiency")
->set_is_sortable(true)
->add_callback([format::class, 'boolean_as_text']);
// Rating.
$columns[] = (new column(
'rating',
new lang_string('rating', 'core_competency'),
$this->get_entity_name(),
))
->add_joins($this->get_joins())
->add_fields("{$usercompetencyalias}.grade, {$usercompetencyalias}.competencyid")
->add_callback(static function(?string $grade, stdClass $row): string {
if ($grade === null) {
return '';
}
$competency = new competency($row->competencyid);
$scale = $competency->get_scale()->scale_items;
return (string) ($scale[(int) $grade - 1] ?? $grade);
});
return $columns;
}
/**
* Return list of all available filters
*
* @return filter[]
*/
protected function get_all_filters(): array {
$usercompetencyalias = $this->get_table_alias('competency_usercomp');
// Status.
$filters[] = (new filter(
select::class,
'status',
new lang_string('status'),
$this->get_entity_name(),
"{$usercompetencyalias}.status",
))
->add_joins($this->get_joins())
->set_options_callback([user_competency::class, 'get_status_list']);
// Proficient.
$filters[] = (new filter(
boolean_select::class,
'proficient',
new lang_string('proficient', 'core_competency'),
$this->get_entity_name(),
"{$usercompetencyalias}.proficiency",
))
->add_joins($this->get_joins());
return $filters;
}
}
@@ -0,0 +1,383 @@
<?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/>.
declare(strict_types=1);
namespace core_competency\reportbuilder\datasource;
use core_competency_generator;
use core_competency\user_competency;
use core_reportbuilder_generator;
use core_reportbuilder_testcase;
use core_reportbuilder\local\filters\{boolean_select, date, select, text};
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once("{$CFG->dirroot}/reportbuilder/tests/helpers.php");
/**
* Unit tests for comptencies datasource
*
* @package core_competency
* @covers \core_competency\reportbuilder\datasource\competencies
* @copyright 2024 Paul Holden <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
final class competencies_test extends core_reportbuilder_testcase {
/**
* Test default datasource
*/
public function test_datasource_default(): void {
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
/** @var core_competency_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_competency');
// First framework has two competencies.
$framework = $generator->create_framework(['shortname' => 'Zoology']);
$generator->create_competency(['competencyframeworkid' => $framework->get('id'), 'shortname' => 'Zebras']);
$competency = $generator->create_competency([
'competencyframeworkid' => $framework->get('id'),
'shortname' => 'Aardvarks',
]);
// Framework two has no competencies.
$generator->create_framework(['shortname' => 'Algebra']);
// Assign user to second competency.
$generator->create_user_competency([
'competencyid' => $competency->get('id'),
'userid' => $user->id,
'proficiency' => true,
'grade' => 1,
]);
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
$report = $generator->create_report(['name' => 'Competencies', 'source' => competencies::class, 'default' => 1]);
$content = $this->get_custom_report_content($report->get('id'));
// Default columns are framework name, competency name, user fullname, proficient. Sorted by first three.
$this->assertEquals([
['Algebra', '', '', ''],
['Zoology', 'Aardvarks', fullname($user), 'Yes'],
['Zoology', 'Zebras', '', ''],
], array_map('array_values', $content));
}
/**
* Test datasource columns that aren't added by default
*/
public function test_datasource_non_default_columns(): void {
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
$cohort = $this->getDataGenerator()->create_cohort(['name' => 'My cohort']);
cohort_add_member($cohort->id, $user->id);
$scale = $this->getDataGenerator()->create_scale(['name' => 'My scale', 'scale' => 'A,B,C,D']);
/** @var core_competency_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_competency');
$framework = $generator->create_framework(['description' => 'So cool', 'idnumber' => 'FRM101', 'scaleid' => $scale->id]);
$competency = $generator->create_competency(['competencyframeworkid' => $framework->get('id'), 'idnumber' => 'COM101']);
$generator->create_user_competency([
'competencyid' => $competency->get('id'),
'userid' => $user->id,
'proficiency' => true,
'grade' => 3,
]);
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
$report = $generator->create_report(['name' => 'Competencies', 'source' => competencies::class, 'default' => 0]);
// Framework.
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'framework:description']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'framework:idnumber']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'framework:scale']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'framework:visible']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'framework:timecreated']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'framework:timemodified']);
// Competency.
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'competency:idnumber']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'competency:timecreated']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'competency:timemodified']);
// User competency.
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'usercompetency:status']);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'usercompetency:rating']);
// Cohort.
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'cohort:name']);
$content = $this->get_custom_report_content($report->get('id'));
$this->assertCount(1, $content);
[
$frameworkdescription,
$frameworkidnumber,
$frameworkscale,
$frameworkvisible,
$frameworktimecreated,
$frameworktimemodified,
$competencyidnumber,
$competencytimecreated,
$competencytimemodified,
$usercompetencystatus,
$usercompetencyrating,
$cohortname,
] = array_values($content[0]);
$this->assertEquals('So cool', $frameworkdescription);
$this->assertEquals('FRM101', $frameworkidnumber);
$this->assertEquals('My scale', $frameworkscale);
$this->assertEquals('Yes', $frameworkvisible);
$this->assertNotEmpty($frameworktimecreated);
$this->assertNotEmpty($frameworktimemodified);
$this->assertEquals('COM101', $competencyidnumber);
$this->assertNotEmpty($competencytimecreated);
$this->assertNotEmpty($competencytimemodified);
$this->assertEquals('Idle', $usercompetencystatus);
$this->assertEquals('C', $usercompetencyrating);
$this->assertEquals('My cohort', $cohortname);
}
/**
* Data provider for {@see test_datasource_filters}
*
* @return array[]
*/
public static function datasource_filters_provider(): array {
return [
// Framework.
'Framework name' => ['framework:name', [
'framework:name_operator' => text::IS_EQUAL_TO,
'framework:name_value' => 'How much I care',
], true],
'Framework name (no match)' => ['framework:name', [
'framework:name_operator' => text::IS_EQUAL_TO,
'framework:name_value' => 'Something else',
], false],
'Framework idnumber' => ['framework:idnumber', [
'framework:idnumber_operator' => text::IS_EQUAL_TO,
'framework:idnumber_value' => 'FRM101',
], true],
'Framework idnumber (no match)' => ['framework:idnumber', [
'framework:idnumber_operator' => text::IS_EQUAL_TO,
'framework:idnumber_value' => 'FRM102',
], false],
'Framework scale' => ['framework:scale', [
'framework:scale_operator' => select::EQUAL_TO,
'framework:scale_value' => '<SCALEID>',
], true],
'Framework scale (no match)' => ['framework:scale', [
'framework:scale_operator' => select::EQUAL_TO,
'framework:scale_value' => -1,
], false],
'Framework visible' => ['framework:visible', [
'framework:visible_operator' => boolean_select::CHECKED,
], true],
'Framework visible (no match)' => ['framework:visible', [
'framework:visible_operator' => boolean_select::NOT_CHECKED,
], false],
'Framework time created' => ['framework:timecreated', [
'framework:timecreated_operator' => date::DATE_RANGE,
'framework:timecreated_from' => 1622502000,
], true],
'Framework time created (no match)' => ['framework:timecreated', [
'framework:timecreated_operator' => date::DATE_RANGE,
'framework:timecreated_to' => 1622502000,
], false],
'Framework time modified' => ['framework:timemodified', [
'framework:timemodified_operator' => date::DATE_RANGE,
'framework:timemodified_from' => 1622502000,
], true],
'Framework time modified (no match)' => ['framework:timemodified', [
'framework:timemodified_operator' => date::DATE_RANGE,
'framework:timemodified_to' => 1622502000,
], false],
// Competency.
'Competency name' => ['competency:name', [
'competency:name_operator' => text::IS_EQUAL_TO,
'competency:name_value' => 'My framework',
], true],
'Competency name (no match)' => ['competency:name', [
'competency:name_operator' => text::IS_EQUAL_TO,
'competency:name_value' => 'Something else',
], false],
'Competency idnumber' => ['competency:idnumber', [
'competency:idnumber_operator' => text::IS_EQUAL_TO,
'competency:idnumber_value' => 'COM101',
], true],
'Competency idnumber (no match)' => ['competency:idnumber', [
'competency:idnumber_operator' => text::IS_EQUAL_TO,
'competency:idnumber_value' => 'COM102',
], false],
'Competency time created' => ['competency:timecreated', [
'competency:timecreated_operator' => date::DATE_RANGE,
'competency:timecreated_from' => 1622502000,
], true],
'Competency time created (no match)' => ['competency:timecreated', [
'competency:timecreated_operator' => date::DATE_RANGE,
'competency:timecreated_to' => 1622502000,
], false],
'Competency time modified' => ['competency:timemodified', [
'competency:timemodified_operator' => date::DATE_RANGE,
'competency:timemodified_from' => 1622502000,
], true],
'Competency time modified (no match)' => ['competency:timemodified', [
'competency:timemodified_operator' => date::DATE_RANGE,
'competency:timemodified_to' => 1622502000,
], false],
// User competency.
'User competency status' => ['usercompetency:status', [
'usercompetency:status_operator' => SELECT::EQUAL_TO,
'usercompetency:status_value' => user_competency::STATUS_IDLE,
], true],
'User competency status (no match)' => ['usercompetency:status', [
'usercompetency:status_operator' => SELECT::EQUAL_TO,
'usercompetency:status_value' => user_competency::STATUS_WAITING_FOR_REVIEW,
], false],
'User competency proficient' => ['usercompetency:proficient', [
'usercompetency:proficient_operator' => boolean_select::CHECKED,
], true],
'User competency proficient (no match)' => ['usercompetency:proficient', [
'usercompetency:proficient_operator' => boolean_select::NOT_CHECKED,
], false],
// User.
'User username' => ['user:username', [
'user:username_operator' => text::IS_EQUAL_TO,
'user:username_value' => 'testuser',
], true],
'User username (no match)' => ['user:username', [
'user:username_operator' => text::IS_NOT_EQUAL_TO,
'user:username_value' => 'testuser',
], false],
// Cohort.
'Cohort name' => ['cohort:name', [
'cohort:name_operator' => text::IS_EQUAL_TO,
'cohort:name_value' => 'My cohort',
], true],
'Cohort name (no match)' => ['cohort:name', [
'cohort:name_operator' => text::IS_EQUAL_TO,
'cohort:name_value' => 'Another cohort',
], false],
];
}
/**
* Test datasource filters
*
* @param string $filtername
* @param array $filtervalues
* @param bool $expectmatch
*
* @dataProvider datasource_filters_provider
*/
public function test_datasource_filters(
string $filtername,
array $filtervalues,
bool $expectmatch
): void {
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user(['username' => 'testuser']);
$cohort = $this->getDataGenerator()->create_cohort(['name' => 'My cohort']);
cohort_add_member($cohort->id, $user->id);
$scale = $this->getDataGenerator()->create_scale([]);
/** @var core_competency_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_competency');
$framework = $generator->create_framework([
'shortname' => 'How much I care',
'description' => 'Sometimes I feel my heart will overflow',
'idnumber' => 'FRM101',
'scaleid' => $scale->id,
]);
$competency = $generator->create_competency([
'competencyframeworkid' => $framework->get('id'),
'shortname' => 'My framework',
'idnumber' => 'COM101',
]);
$generator->create_user_competency([
'competencyid' => $competency->get('id'),
'userid' => $user->id,
'proficiency' => true,
'grade' => 3,
]);
/** @var core_reportbuilder_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
// Create report containing single column, and given filter.
$report = $generator->create_report(['name' => 'Competencies', 'source' => competencies::class, 'default' => 0]);
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'framework:idnumber']);
// Add filter, set it's values.
$generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => $filtername]);
$content = $this->get_custom_report_content(
reportid: $report->get('id'),
filtervalues: str_replace('<SCALEID>', $scale->id, $filtervalues),
);
if ($expectmatch) {
$this->assertCount(1, $content);
$this->assertEquals('FRM101', reset($content[0]));
} else {
$this->assertEmpty($content);
}
}
/**
* Stress test datasource
*
* In order to execute this test PHPUNIT_LONGTEST should be defined as true in phpunit.xml or directly in config.php
*/
public function test_stress_datasource(): void {
if (!PHPUNIT_LONGTEST) {
$this->markTestSkipped('PHPUNIT_LONGTEST is not defined');
}
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
/** @var core_competency_generator $generator */
$generator = $this->getDataGenerator()->get_plugin_generator('core_competency');
$framework = $generator->create_framework();
$competency = $generator->create_competency(['competencyframeworkid' => $framework->get('id')]);
$generator->create_user_competency(['competencyid' => $competency->get('id'), 'userid' => $user->id]);
$this->datasource_stress_test_columns(competencies::class);
$this->datasource_stress_test_columns_aggregation(competencies::class);
$this->datasource_stress_test_conditions(competencies::class, 'framework:idnumber');
}
}
+5
View File
@@ -28,6 +28,8 @@ $string['allchildrenarecomplete'] = 'All children are complete';
$string['competencies'] = 'Competencies';
$string['competenciesarenotenabled'] = 'Competencies are not enabled.';
$string['competenciessettings'] = 'Competencies settings';
$string['competency'] = 'Competency';
$string['competencyframework'] = 'Competency framework';
$string['completeplanstask'] = 'Complete learning plans which are due';
$string['coursecompetencyoutcome_complete'] = 'Complete the competency';
$string['coursecompetencyoutcome_evidence'] = 'Attach evidence';
@@ -162,8 +164,10 @@ $string['privacy:metadata:usermodified'] = 'The user who created or modified the
$string['privacy:path:plans'] = 'Learning plans';
$string['privacy:path:relatedtome'] = 'Related to me';
$string['privacy:path:userevidence'] = 'Evidence of prior learning';
$string['proficient'] = 'Proficient';
$string['pushcourseratingstouserplans'] = 'Push course ratings to individual learning plans';
$string['pushcourseratingstouserplans_desc'] = 'Default value for the course setting to update individual learning plans when course competencies are rated.';
$string['rating'] = 'Rating';
$string['syncplanscohorts'] = 'Sync plans from learning plan template cohorts';
$string['taxonomy_behaviour'] = 'Behaviour';
$string['taxonomy_competency'] = 'Competency';
@@ -196,6 +200,7 @@ $string['usercommentedonaplanhtml'] = '<p>{$a->fullname} commented on the learni
<p>See: <a href="{$a->url}">{$a->urlname}</a>.</p>';
$string['usercommentedonaplansmall'] = '{$a->fullname} commented on the learning plan "{$a->plan}".';
$string['usercommentedonaplansubject'] = '{$a} commented on a learning plan.';
$string['usercompetency'] = 'User competency';
$string['usercompetencystatus_idle'] = 'Idle';
$string['usercompetencystatus_inreview'] = 'In review';
$string['usercompetencystatus_waitingforreview'] = 'Waiting for review';