diff --git a/admin/tool/lp/classes/api.php b/admin/tool/lp/classes/api.php index d1b6048bc42..f0ae9f2f166 100644 --- a/admin/tool/lp/classes/api.php +++ b/admin/tool/lp/classes/api.php @@ -1989,41 +1989,66 @@ class api { /** * Count all the competencies in a learning plan template. * - * @param int $templateid The id of the template to check. + * @param template|int $templateorid The template or its ID. * @return int */ - public static function count_competencies_in_template($templateid) { + public static function count_competencies_in_template($templateorid) { static::require_enabled(); // First we do a permissions check. - $template = new template($templateid); - $context = $template->get_context(); + $template = $templateorid; + if (!is_object($template)) { + $template = new template($template); + } if (!$template->can_read()) { throw new required_capability_exception($template->get_context(), 'tool/lp:templateread', 'nopermissions', ''); } // OK - all set. - return template_competency::count_competencies($templateid); + return template_competency::count_competencies($template->get_id()); + } + + /** + * Count all the competencies in a learning plan template with no linked courses. + * + * @param template|int $templateorid The template or its ID. + * @return int + */ + public static function count_competencies_in_template_with_no_courses($templateorid) { + // First we do a permissions check. + $template = $templateorid; + if (!is_object($template)) { + $template = new template($template); + } + + if (!$template->can_read()) { + throw new required_capability_exception($template->get_context(), 'tool/lp:templateread', 'nopermissions', ''); + } + + // OK - all set. + return template_competency::count_competencies_with_no_courses($template->get_id()); } /** * List all the competencies in a template. * - * @param int $templateid The id of the template to check. + * @param template|int $templateorid The template or its ID. * @return array of competencies */ - public static function list_competencies_in_template($templateid) { + public static function list_competencies_in_template($templateorid) { static::require_enabled(); // First we do a permissions check. - $template = new template($templateid); - $context = $template->get_context(); + $template = $templateorid; + if (!is_object($template)) { + $template = new template($template); + } if (!$template->can_read()) { throw new required_capability_exception($template->get_context(), 'tool/lp:templateread', 'nopermissions', ''); } // OK - all set. - return template_competency::list_competencies($templateid); + return template_competency::list_competencies($template->get_id()); } /** @@ -3516,7 +3541,6 @@ class api { // OK - all set. return $template->has_plans(); - } /** @@ -4680,6 +4704,101 @@ class api { return $result; } + /** + * Count the plans in the template, filtered by status. + * + * Requires tool/lp:templateread capability at the system context. + * + * @param mixed $templateorid The id or the template. + * @param int $status One of the plan status constants (or 0 for all plans). + * @return int + */ + public static function count_plans_for_template($templateorid, $status = 0) { + $template = $templateorid; + if (!is_object($template)) { + $template = new template($template); + } + + // First we do a permissions check. + if (!$template->can_read()) { + throw new required_capability_exception($template->get_context(), 'tool/lp:templateread', 'nopermissions', ''); + } + + return plan::count_records_for_template($template->get_id(), $status); + } + + /** + * Count the user-completency-plans in the template, optionally filtered by proficiency. + * + * Requires tool/lp:templateread capability at the system context. + * + * @param mixed $templateorid The id or the template. + * @param mixed $proficiency If true, filter by proficiency, if false filter by not proficient, if null - no filter. + * @return int + */ + public static function count_user_competency_plans_for_template($templateorid, $proficiency = null) { + $template = $templateorid; + if (!is_object($template)) { + $template = new template($template); + } + + // First we do a permissions check. + if (!$template->can_read()) { + throw new required_capability_exception($template->get_context(), 'tool/lp:templateread', 'nopermissions', ''); + } + + return user_competency_plan::count_records_for_template($template->get_id(), $proficiency); + } + + /** + * List the plans in the template, filtered by status. + * + * Requires tool/lp:templateread capability at the system context. + * + * @param mixed $templateorid The id or the template. + * @param int $status One of the plan status constants (or 0 for all plans). + * @param int $skip The number of records to skip + * @param int $limit The max number of records to return + * @return plan[] + */ + public static function list_plans_for_template($templateorid, $status = 0, $skip = 0, $limit = 100) { + $template = $templateorid; + if (!is_object($template)) { + $template = new template($template); + } + + // First we do a permissions check. + if (!$template->can_read()) { + throw new required_capability_exception($template->get_context(), 'tool/lp:templateread', 'nopermissions', ''); + } + + return plan::get_records_for_template($template->get_id(), $status, $skip, $limit); + } + + /** + * Get the most often not completed competency for this template. + * + * Requires tool/lp:templateread capability at the system context. + * + * @param mixed $templateorid The id or the template. + * @param int $skip The number of records to skip + * @param int $limit The max number of records to return + * @return competency[] + */ + public static function get_least_proficient_competencies_for_template($templateorid, $skip = 0, $limit = 100) { + $template = $templateorid; + if (!is_object($template)) { + $template = new template($template); + } + + // First we do a permissions check. + if (!$template->can_read()) { + throw new required_capability_exception($template->get_context(), 'tool/lp:templateread', 'nopermissions', ''); + } + + return user_competency_plan::get_least_proficient_competencies_for_template($template->get_id(), $skip, $limit); + } + /** * Template event viewed. * diff --git a/admin/tool/lp/classes/external.php b/admin/tool/lp/classes/external.php index b88739c4311..6a8a5c8e0fb 100644 --- a/admin/tool/lp/classes/external.php +++ b/admin/tool/lp/classes/external.php @@ -45,6 +45,7 @@ use grade_scale; use tool_lp\external\competency_framework_exporter; use tool_lp\external\competency_summary_exporter; use tool_lp\external\cohort_summary_exporter; +use tool_lp\external\template_statistics_exporter; use tool_lp\external\user_summary_exporter; use tool_lp\external\user_competency_exporter; use tool_lp\external\user_competency_plan_exporter; @@ -3203,6 +3204,7 @@ class external extends external_api { competency_summary_exporter::get_read_structure() ), 'manageurl' => new external_value(PARAM_LOCALURL, 'Url to the manage competencies page.'), + 'statistics' => template_statistics_exporter::get_read_structure() )); } diff --git a/admin/tool/lp/classes/external/template_statistics_exporter.php b/admin/tool/lp/classes/external/template_statistics_exporter.php new file mode 100644 index 00000000000..9fdd3f11b45 --- /dev/null +++ b/admin/tool/lp/classes/external/template_statistics_exporter.php @@ -0,0 +1,125 @@ +. + +/** + * Class for exporting a template statistics summary. + * + * @package tool_lp + * @copyright 2016 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace tool_lp\external; +defined('MOODLE_INTERNAL') || die(); + +use renderer_base; +use moodle_url; + +/** + * Class for exporting a cohort summary from an stdClass. + * + * @copyright 2015 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class template_statistics_exporter extends exporter { + + public static function define_properties() { + return array( + 'competencycount' => array( + 'type' => PARAM_INT, + ), + 'unlinkedcompetencycount' => array( + 'type' => PARAM_INT, + ), + 'plancount' => array( + 'type' => PARAM_INT, + ), + 'completedplancount' => array( + 'type' => PARAM_INT, + ), + 'usercompetencyplancount' => array( + 'type' => PARAM_INT, + ), + 'proficientusercompetencyplancount' => array( + 'type' => PARAM_INT, + ) + ); + } + + public static function define_other_properties() { + return array( + 'linkedcompetencypercentage' => array( + 'type' => PARAM_FLOAT + ), + 'linkedcompetencycount' => array( + 'type' => PARAM_INT + ), + 'completedplanpercentage' => array( + 'type' => PARAM_FLOAT + ), + 'proficientusercompetencyplanpercentage' => array( + 'type' => PARAM_FLOAT + ), + 'leastproficient' => array( + 'type' => competency_exporter::read_properties_definition(), + 'multiple' => true + ), + 'leastproficientcount' => array( + 'type' => PARAM_INT + ), + ); + } + + protected function get_other_values(renderer_base $output) { + $linkedcompetencycount = $this->data->competencycount - $this->data->unlinkedcompetencycount; + if ($linkedcompetencycount < 0) { + // Should never happen. + $linkedcompetencycount = 0; + } + $linkedcompetencypercentage = 0; + if ($this->data->competencycount > 0) { + $linkedcompetencypercentage = format_float(((float) $linkedcompetencycount / + (float) $this->data->competencycount) * 100.0); + } + $completedplanpercentage = 0; + if ($this->data->plancount > 0) { + $completedplanpercentage = format_float(((float) $this->data->completedplancount / + (float) $this->data->plancount) * 100.0); + } + $proficientusercompetencyplanpercentage = 0; + if ($this->data->usercompetencyplancount > 0) { + $proficientusercompetencyplanpercentage = format_float(((float) $this->data->proficientusercompetencyplancount / + (float) $this->data->usercompetencyplancount) * 100.0); + } + $competencies = array(); + $contextcache = array(); + foreach ($this->data->leastproficientcompetencies as $competency) { + if (!isset($contextcache[$competency->get_competencyframeworkid()])) { + $contextcache[$competency->get_competencyframeworkid()] = $competency->get_context(); + } + $context = $contextcache[$competency->get_competencyframeworkid()]; + $exporter = new competency_exporter($competency, array('context' => $context)); + $competencies[] = $exporter->export($output); + } + return array( + 'linkedcompetencycount' => $linkedcompetencycount, + 'linkedcompetencypercentage' => $linkedcompetencypercentage, + 'completedplanpercentage' => $completedplanpercentage, + 'proficientusercompetencyplanpercentage' => $proficientusercompetencyplanpercentage, + 'leastproficient' => $competencies, + 'leastproficientcount' => count($competencies) + ); + } +} diff --git a/admin/tool/lp/classes/output/template_competencies_page.php b/admin/tool/lp/classes/output/template_competencies_page.php index 37aecbcdef5..cc7c0123e11 100644 --- a/admin/tool/lp/classes/output/template_competencies_page.php +++ b/admin/tool/lp/classes/output/template_competencies_page.php @@ -33,6 +33,8 @@ use context_system; use moodle_url; use tool_lp\api; use tool_lp\external\competency_summary_exporter; +use tool_lp\template_statistics; +use tool_lp\external\template_statistics_exporter; /** * Class containing data for learning plan template competencies page @@ -60,6 +62,9 @@ class template_competencies_page implements renderable, templatable { /** @var context $pagecontext The page context. */ protected $pagecontext = null; + /** @var template_statistics $templatestatistics The generated summary statistics for this template. */ + protected $templatestatistics = null; + /** * Construct this renderable. * @@ -68,6 +73,7 @@ class template_competencies_page implements renderable, templatable { public function __construct($templateid, context $pagecontext) { $this->pagecontext = $pagecontext; $this->templateid = $templateid; + $this->templatestatistics = new template_statistics($templateid); $this->competencies = api::list_competencies_in_template($templateid); $this->canmanagecompetencyframeworks = has_capability('tool/lp:competencymanage', $this->pagecontext); $this->canmanagetemplatecompetencies = has_capability('tool/lp:templatemanage', $this->pagecontext); @@ -115,6 +121,9 @@ class template_competencies_page implements renderable, templatable { $data->canmanagecompetencyframeworks = $this->canmanagecompetencyframeworks; $data->canmanagetemplatecompetencies = $this->canmanagetemplatecompetencies; $data->manageurl = $this->manageurl->out(true); + $exporter = new template_statistics_exporter($this->templatestatistics); + $data->statistics = $exporter->export($output); + $data->showcompetencylinks = true; return $data; } diff --git a/admin/tool/lp/classes/plan.php b/admin/tool/lp/classes/plan.php index 8b6817cf8a9..63ca1a0eb2b 100644 --- a/admin/tool/lp/classes/plan.php +++ b/admin/tool/lp/classes/plan.php @@ -671,4 +671,36 @@ class plan extends persistent { public static function has_records_for_template($templateid) { return self::record_exists_select('templateid = ?', array($templateid)); } + + /** + * Count the number of plans for a template, optionally filtering by status. + * + * @param int $templateid The template ID + * @param int $status The plan status. 0 means all statuses. + * @return int + */ + public static function count_records_for_template($templateid, $status) { + $filters = array('templateid' => $templateid); + if ($status > 0) { + $filters['status'] = $status; + } + return self::count_records($filters); + } + + /** + * Get the plans for a template, optionally filtering by status. + * + * @param int $templateid The template ID + * @param int $status The plan status. 0 means all statuses. + * @param int $skip The number of plans to skip + * @param int $limit The max number of plans to return + * @return int + */ + public static function get_records_for_template($templateid, $status = 0, $skip = 0, $limit = 100) { + $filters = array('templateid' => $templateid); + if ($status > 0) { + $filters['status'] = $status; + } + return self::get_records($filters, $skip, $limit); + } } diff --git a/admin/tool/lp/classes/template_competency.php b/admin/tool/lp/classes/template_competency.php index f3810f6851f..5d603c0a6e5 100644 --- a/admin/tool/lp/classes/template_competency.php +++ b/admin/tool/lp/classes/template_competency.php @@ -138,6 +138,29 @@ class template_competency extends persistent { return $results; } + /** + * Count the competencies in a template with no links to courses. + * + * @param int $templateid The template id + * @return int + */ + public static function count_competencies_with_no_courses($templateid) { + global $DB; + + $sql = 'SELECT COUNT(comp.id) + FROM {' . self::TABLE . '} tplcomp + JOIN {' . competency::TABLE . '} comp + ON tplcomp.competencyid = comp.id + LEFT JOIN {' . course_competency::TABLE . '} crscomp + ON crscomp.competencyid = comp.id + WHERE tplcomp.templateid = ? AND crscomp.id IS NULL'; + $params = array($templateid); + + $results = $DB->count_records_sql($sql, $params); + + return $results; + } + /** * Get a single competency from the template (only if it is really in the template). * diff --git a/admin/tool/lp/classes/template_statistics.php b/admin/tool/lp/classes/template_statistics.php new file mode 100644 index 00000000000..616f35cbde4 --- /dev/null +++ b/admin/tool/lp/classes/template_statistics.php @@ -0,0 +1,76 @@ +. + +/** + * Template statistics class + * + * @package tool_lp + * @copyright 2016 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace tool_lp; +defined('MOODLE_INTERNAL') || die(); + +/** + * Template statistics class. + * + * @package tool_lp + * @copyright 2016 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class template_statistics { + + /** @var $competencycount The number of competencies in the template */ + public $competencycount = 0; + + /** @var $unlinkedcompetencycount The number of unlinked competencies in the template */ + public $unlinkedcompetencycount = 0; + + /** @var $plancount The number of plans for the template */ + public $plancount = 0; + + /** @var $completedplancount The number of completed plans for the template */ + public $completedplancount = 0; + + /** @var $usercompetencyplancount The number of competencies in completed plans for the template */ + public $usercompetencyplancount = 0; + + /** @var $proficientusercompetencyplancount The number of proficient competencies in completed plans for the template */ + public $proficientusercompetencyplancount = 0; + + /** @var $leastproficientcompetencies The competencies in this template that were proficient the least times */ + public $leastproficientcompetencies = array(); + + /** + * Return the custom definition of the properties of this model. + * + * @param int $template The template we want to generate statistics for. + */ + public function __construct($templateid) { + $template = new template($templateid); + $this->competencycount = api::count_competencies_in_template($template); + $this->unlinkedcompetencycount = api::count_competencies_in_template_with_no_courses($template); + + $this->plancount = api::count_plans_for_template($template, 0); + $this->completedplancount = api::count_plans_for_template($template, plan::STATUS_COMPLETE); + + $this->usercompetencyplancount = api::count_user_competency_plans_for_template($template, false); + $this->proficientusercompetencyplancount = api::count_user_competency_plans_for_template($template, true); + + $this->leastproficientcompetencies = api::get_least_proficient_competencies_for_template($template, 0, 3); + } +} diff --git a/admin/tool/lp/classes/user_competency_plan.php b/admin/tool/lp/classes/user_competency_plan.php index 92ded732729..e001232e86d 100644 --- a/admin/tool/lp/classes/user_competency_plan.php +++ b/admin/tool/lp/classes/user_competency_plan.php @@ -280,4 +280,64 @@ class user_competency_plan extends persistent { return self::record_exists_select("competencyid $insql", $params); } + /** + * Count the number of records matching a specific template, optionally filtered by proficient values. + * + * @param int $templateid + * @param mixed $proficiency - If true - filter by proficiency, if false filter by not proficient, if null - do not filter. + * @return int + */ + public static function count_records_for_template($templateid, $proficiency=null) { + global $DB; + + $params = array('templateid' => $templateid); + $sql = 'SELECT ' . " COUNT('x') " . + 'FROM {' . self::TABLE . '} ucp + JOIN {' . plan::TABLE . '} p + ON ucp.planid = p.id + WHERE p.templateid = :templateid'; + if ($proficiency === true) { + $sql .= ' AND ucp.proficiency = :proficiency'; + $params['proficiency'] = true; + } else if ($proficiency === false) { + $sql .= ' AND (ucp.proficiency = :proficiency OR ucp.proficiency IS NULL)'; + $params['proficiency'] = false; + } + + return $DB->count_records_sql($sql, $params); + } + + /** + * Get the list of competencies that were completed the least times (in completed plans) from a template. + * + * @param int $templateid + * @param int $skip The number of competencies to skip + * @param int $limit The max number of competencies to return + * @return competency[] + */ + public static function get_least_proficient_competencies_for_template($templateid, $skip = 0, $limit = 0) { + global $DB; + + $fields = competency::get_sql_fields('c'); + $params = array('templateid' => $templateid, 'notproficient' => false); + $sql = 'SELECT ' . $fields . ', COUNT(c.id) AS timesnotproficient ' . + ' FROM {' . self::TABLE . '} ucp + JOIN {' . plan::TABLE . '} p + ON ucp.planid = p.id + JOIN {' . competency::TABLE . '} c + ON ucp.competencyid = c.id + WHERE p.templateid = :templateid + AND (ucp.proficiency = :notproficient OR ucp.proficiency IS NULL) + GROUP BY c.id + ORDER BY timesnotproficient DESC'; + + $results = $DB->get_records_sql($sql, $params, $skip, $limit); + + $comps = array(); + foreach ($results as $r) { + $c = competency::extract_record($r); + $comps[] = new competency(0, $c); + } + return $comps; + } } diff --git a/admin/tool/lp/lang/en/tool_lp.php b/admin/tool/lp/lang/en/tool_lp.php index ae9b3fc92a5..5f0e0590fac 100644 --- a/admin/tool/lp/lang/en/tool_lp.php +++ b/admin/tool/lp/lang/en/tool_lp.php @@ -38,6 +38,7 @@ $string['allchildrenarecomplete'] = 'All children are complete'; $string['aplanswerecreated'] = '{$a} plans were created.'; $string['aplanswerecreatedmoremayrequiresync'] = '{$a} plans were created, more will be created during the next synchronization.'; $string['assigncohorts'] = 'Assign cohorts'; +$string['averageproficiencyrate'] = 'The average proficiency rate for completed plans based on this template is {$a} %'; $string['cancelreviewrequest'] = 'Cancel review request'; $string['cannotaddrules'] = 'This competency cannot be configured.'; $string['cannotcreateuserplanswhentemplateduedateispassed'] = 'New user plans can not be created: this template\'s due date has, or is about to, expire.'; @@ -47,6 +48,7 @@ $string['cohortssyncedtotemplate'] = 'Cohorts synced to this template'; $string['competencies'] = 'Competencies'; $string['competenciesarenotenabled'] = 'The competencies module is not enabled.'; $string['competenciesforframework'] = 'Competencies for {$a}'; +$string['competenciesmostoftennotproficient'] = 'Competencies most often not proficient in completed plans'; $string['competenciessettings'] = 'Competencies settings'; $string['competencycannotbedeleted'] = 'The competency \'{$a}\' can not be deleted'; $string['competencycreated'] = 'Competency created'; @@ -418,3 +420,5 @@ $string['viewdetails'] = 'View details'; $string['visible'] = 'Visible'; $string['visible_help'] = 'A competency framework can be hidden from teachers. This could be useful if a framework is still in the process of being developed.'; $string['when'] = 'When'; +$string['xcompetencieslinkedoutofy'] = '{$a->x} out of {$a->y} competencies linked to courses'; +$string['xplanscompletedoutofy'] = '{$a->x} out of {$a->y} plans completed for this template'; diff --git a/admin/tool/lp/styles.css b/admin/tool/lp/styles.css index 6e6f6ebde38..6530232968b 100644 --- a/admin/tool/lp/styles.css +++ b/admin/tool/lp/styles.css @@ -4,7 +4,17 @@ .path-admin-tool-lp [data-region="competencylinktree"] ul li { list-style-type: none; } - +.path-admin-tool-lp [data-region="templatestatistics"] .progresstext { + display: inline-block; + height: 40px; + vertical-align: top; +} +.path-admin-tool-lp [data-region="templatestatistics"] .progress { + width: 10em; + display: inline-block; + margin-left: 2em; + margin-right: 2em; +} .path-admin-tool-lp [data-region="managecompetencies"] ul[data-enhance="tree"], .path-admin-tool-lp [data-region="plans"] ul[data-enhance="tree"], .path-admin-tool-lp [data-region="competencylinktree"] ul[data-enhance="linktree"], diff --git a/admin/tool/lp/templates/template_competencies_page.mustache b/admin/tool/lp/templates/template_competencies_page.mustache index f6807dcd233..c974328963d 100644 --- a/admin/tool/lp/templates/template_competencies_page.mustache +++ b/admin/tool/lp/templates/template_competencies_page.mustache @@ -18,6 +18,21 @@ Template competencies template. }}
+ {{#statistics}} + {{> tool_lp/template_statistics }} + {{/statistics}} +
+ {{#canmanagecompetencyframeworks}} + + {{/canmanagecompetencyframeworks}} + {{#canmanagetemplatecompetencies}} +

+ +

+ {{/canmanagetemplatecompetencies}} +
@@ -31,6 +46,7 @@
{{/canmanagetemplatecompetencies}} {{#competency}} + {{> tool_lp/competency_summary }} {{/competency}} {{#str}}linkedcourseslist, tool_lp{{/str}} @@ -55,16 +71,6 @@

{{/competencies}}
-
-
- {{#canmanagetemplatecompetencies}} - - {{/canmanagetemplatecompetencies}} -
- {{#canmanagecompetencyframeworks}} -

{{#str}}managecompetenciesandframeworks, tool_lp{{/str}}

- {{/canmanagecompetencyframeworks}} -
{{#js}} require(['tool_lp/competencies'], function(mod) { diff --git a/admin/tool/lp/templates/template_statistics.mustache b/admin/tool/lp/templates/template_statistics.mustache new file mode 100644 index 00000000000..edef1c4f1a7 --- /dev/null +++ b/admin/tool/lp/templates/template_statistics.mustache @@ -0,0 +1,102 @@ +{{! + 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 . +}} +{{! + @template tool_lp/template_statistics + + IDs required for JS: + * none + + Classes required for JS: + * none + + Data attributes required for JS: + * none + + Context variables required for this template: + + See admin/tool/lp/classes/external/template_statistics_exporter + + Example context (json): + { + linkedcompetencycount: 4, + competencycount: 8, + linkedcompetencypercentage: 50, + plancount: 40, + completedplancount: 30, + completedplanpercentage: 75, + } + +}} +{{! + Template statistics template. +}} +{{#competencycount}} +
+
+
+ {{#str}}xcompetencieslinkedoutofy, tool_lp, { "x": "{{linkedcompetencycount}}", "y": "{{competencycount}}" } {{/str}} +
+
+
+ {{linkedcompetencypercentage}} % +
+
+
+ {{#plancount}} +
+
+ {{#str}}xplanscompletedoutofy, tool_lp, { "x": "{{completedplancount}}", "y": "{{plancount}}" } {{/str}} +
+
+
+ {{completedplanpercentage}} % +
+
+
+ {{/plancount}} + {{#usercompetencyplancount}} +
+
+ {{#str}}averageproficiencyrate, tool_lp, {{proficientusercompetencyplanpercentage}} {{/str}} +
+
+
+ {{proficientusercompetencyplanpercentage}} % +
+
+
+ {{/usercompetencyplancount}} + {{#leastproficientcount}} +
+
+

{{#str}}competenciesmostoftennotproficient, tool_lp{{/str}}

+
+
+ {{#leastproficient}} + {{#showcompetencylinks}} + + {{/showcompetencylinks}} +

{{shortname}} {{idnumber}}

+ {{#showcompetencylinks}} +
+ {{/showcompetencylinks}} + {{/leastproficient}} +
+
+ {{/leastproficientcount}} +
+{{/competencycount}} diff --git a/admin/tool/lp/tests/api_test.php b/admin/tool/lp/tests/api_test.php index 6ce128f1c9b..7091dd7a987 100644 --- a/admin/tool/lp/tests/api_test.php +++ b/admin/tool/lp/tests/api_test.php @@ -4028,4 +4028,144 @@ class tool_lp_api_testcase extends advanced_testcase { $this->assertEquals(null, $uc2->get_grade()); $this->assertEquals(null, $uc2->get_proficiency()); } + + /** + * Test template statistics api functions. + */ + public function test_template_statistics() { + $this->resetAfterTest(true); + $dg = $this->getDataGenerator(); + $lpg = $dg->get_plugin_generator('tool_lp'); + $this->setAdminUser(); + + $u1 = $dg->create_user(); + $u2 = $dg->create_user(); + $u3 = $dg->create_user(); + $u4 = $dg->create_user(); + $c1 = $dg->create_course(); + $framework = $lpg->create_framework(); + + // Create 6 competencies. + $comp1 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id())); + $comp2 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id())); + $comp3 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id())); + $comp4 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id())); + $comp5 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id())); + $comp6 = $lpg->create_competency(array('competencyframeworkid' => $framework->get_id())); + + // Link 5 out of 6 to a course. + $lpg->create_course_competency(array('competencyid' => $comp1->get_id(), 'courseid' => $c1->id)); + $lpg->create_course_competency(array('competencyid' => $comp2->get_id(), 'courseid' => $c1->id)); + $lpg->create_course_competency(array('competencyid' => $comp3->get_id(), 'courseid' => $c1->id)); + $lpg->create_course_competency(array('competencyid' => $comp4->get_id(), 'courseid' => $c1->id)); + $lpg->create_course_competency(array('competencyid' => $comp5->get_id(), 'courseid' => $c1->id)); + + // Put all 6 in a template. + $tpl = $this->getDataGenerator()->get_plugin_generator('tool_lp')->create_template(); + $tplc1 = $lpg->create_template_competency(array('templateid' => $tpl->get_id(), 'competencyid' => $comp1->get_id())); + $tplc2 = $lpg->create_template_competency(array('templateid' => $tpl->get_id(), 'competencyid' => $comp2->get_id())); + $tplc3 = $lpg->create_template_competency(array('templateid' => $tpl->get_id(), 'competencyid' => $comp3->get_id())); + $tplc4 = $lpg->create_template_competency(array('templateid' => $tpl->get_id(), 'competencyid' => $comp4->get_id())); + $tplc5 = $lpg->create_template_competency(array('templateid' => $tpl->get_id(), 'competencyid' => $comp5->get_id())); + $tplc6 = $lpg->create_template_competency(array('templateid' => $tpl->get_id(), 'competencyid' => $comp6->get_id())); + + // Create some plans from the template. + $p1 = $lpg->create_plan(array('templateid' => $tpl->get_id(), 'userid' => $u1->id)); + $p2 = $lpg->create_plan(array('templateid' => $tpl->get_id(), 'userid' => $u2->id)); + $p3 = $lpg->create_plan(array('templateid' => $tpl->get_id(), 'userid' => $u3->id)); + $p4 = $lpg->create_plan(array('templateid' => $tpl->get_id(), 'userid' => $u4->id)); + + // Rate some competencies. + // User 1. + $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $comp1->get_id(), + 'proficiency' => true, 'grade' => 1 )); + $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $comp2->get_id(), + 'proficiency' => true, 'grade' => 1 )); + $lpg->create_user_competency(array('userid' => $u1->id, 'competencyid' => $comp3->get_id(), + 'proficiency' => true, 'grade' => 1 )); + // User 2. + $lpg->create_user_competency(array('userid' => $u2->id, 'competencyid' => $comp1->get_id(), + 'proficiency' => false, 'grade' => 1 )); + $lpg->create_user_competency(array('userid' => $u2->id, 'competencyid' => $comp2->get_id(), + 'proficiency' => false, 'grade' => 1 )); + $lpg->create_user_competency(array('userid' => $u2->id, 'competencyid' => $comp3->get_id(), + 'proficiency' => false, 'grade' => 1 )); + // User 3. + $lpg->create_user_competency(array('userid' => $u3->id, 'competencyid' => $comp2->get_id(), + 'proficiency' => false, 'grade' => 1 )); + $lpg->create_user_competency(array('userid' => $u3->id, 'competencyid' => $comp3->get_id(), + 'proficiency' => true, 'grade' => 1 )); + $lpg->create_user_competency(array('userid' => $u3->id, 'competencyid' => $comp4->get_id(), + 'proficiency' => false, 'grade' => 1 )); + $lpg->create_user_competency(array('userid' => $u3->id, 'competencyid' => $comp5->get_id(), + 'proficiency' => true, 'grade' => 1 )); + // User 4. + $lpg->create_user_competency(array('userid' => $u4->id, 'competencyid' => $comp3->get_id(), + 'proficiency' => true, 'grade' => 1 )); + $lpg->create_user_competency(array('userid' => $u4->id, 'competencyid' => $comp5->get_id(), + 'proficiency' => true, 'grade' => 1 )); + + // Complete 3 out of 4 plans. + api::complete_plan($p1->get_id()); + api::complete_plan($p2->get_id()); + api::complete_plan($p3->get_id()); + + // OK we have enough data - lets call some API functions and check for expected results. + + $result = api::count_competencies_in_template_with_no_courses($tpl->get_id()); + $this->assertEquals(1, $result); + + $result = api::count_plans_for_template($tpl->get_id()); + $this->assertEquals(4, $result); + + $result = api::count_plans_for_template($tpl->get_id(), plan::STATUS_COMPLETE); + $this->assertEquals(3, $result); + + // This counts the records of competencies in completed plans for all users with a plan from this template. + $result = api::count_user_competency_plans_for_template($tpl->get_id()); + // There should be 3 plans * 6 competencies. + $this->assertEquals(18, $result); + + // This counts the records of proficient competencies in completed plans for all users with a plan from this template. + $result = api::count_user_competency_plans_for_template($tpl->get_id(), true); + // There should be 5. + $this->assertEquals(5, $result); + + // This counts the records of not proficient competencies in completed plans for all users with a plan from this template. + $result = api::count_user_competency_plans_for_template($tpl->get_id(), false); + // There should be 13. + $this->assertEquals(13, $result); + + // This lists the plans based on this template, optionally filtered by status. + $result = api::list_plans_for_template($tpl->get_id()); + + $this->assertEquals(4, count($result)); + foreach ($result as $one) { + $this->assertInstanceOf('\tool_lp\plan', $one); + } + // This lists the plans based on this template, optionally filtered by status. + $result = api::list_plans_for_template($tpl->get_id(), plan::STATUS_COMPLETE); + + $this->assertEquals(3, count($result)); + foreach ($result as $one) { + $this->assertInstanceOf('\tool_lp\plan', $one); + $this->assertEquals(plan::STATUS_COMPLETE, $one->get_status()); + } + + $result = api::get_least_proficient_competencies_for_template($tpl->get_id(), 0, 2); + + // Our times completed counts should look like this: + // comp1 - 1 + // comp2 - 1 + // comp3 - 2 + // comp4 - 0 + // comp5 - 1 + // comp6 - 0 + $this->assertEquals(2, count($result)); + $leastarray = array($comp4->get_id(), $comp6->get_id()); + foreach ($result as $one) { + $this->assertInstanceOf('\tool_lp\competency', $one); + $this->assertContains($one->get_id(), $leastarray); + } + } }