moodle_local_treestudyplan/classes/coursecompetencyinfo.php
2023-11-23 07:44:04 +01:00

303 lines
11 KiB
PHP

<?php
// This file is part of the Studyplan plugin for Moodle
//
// 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 <https://www.gnu.org/licenses/>.
/**
* Class to collect course completion info for a given course
* @package local_treestudyplan
* @copyright 2023 P.M. Kuipers
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace local_treestudyplan;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/externallib.php');
require_once($CFG->libdir.'/gradelib.php');
require_once($CFG->dirroot.'/course/lib.php');
use core_competency\course_competency;
use core_competency\competency;
use core_competency\api as c_api;
use stdClass;
/**
* Class to collect course competencty info for a given course
*/
class coursecompetencyinfo {
/** @var \stdClass */
private $course;
/** @var \course_modinfo */
private $modinfo;
/** @var studyitem */
private $studyitem;
/**
* Course id of relevant course
*/
public function id() {
return $this->course->id;
}
/**
* Construct new object for a given course
* @param \stdClass $course Course database record
*/
public function __construct($course, $studyitem) {
global $DB;
$this->course = $course;
$this->studyitem = $studyitem;
$this->completion = new \completion_info($this->course);
$this->modinfo = get_fast_modinfo($this->course);
}
/**
* Generic competency info structure for individual competency stats
* @param $recurse True if child competencies may be included
*/
public static function competencyinfo_structure($recurse=true) : \external_description {
$struct = [
"id" => new \external_value(PARAM_INT, 'competency id'),
"shortname" => new \external_value(PARAM_RAW, 'competency short name'),
"idnumber" => new \external_value(PARAM_TEXT, 'competency ID number'),
"description" => new \external_value(PARAM_RAW, 'competency description'),
"path" => new \external_multiple_structure(new \external_single_structure([
"id" => new \external_value(PARAM_INT),
"shortname" => new \external_value(PARAM_RAW),
"idnumber" => new \external_value(PARAM_TEXT),
]), 'competency path'),
"grade" => new \external_value(PARAM_TEXT, 'competency grade', VALUE_OPTIONAL),
"coursegrade" => new \external_value(PARAM_TEXT, 'course competency grade', VALUE_OPTIONAL),
"proficient" => new \external_value(PARAM_BOOL, 'competency proficiency',VALUE_OPTIONAL),
"courseproficient" => new \external_value(PARAM_BOOL, 'course competency proficiency',VALUE_OPTIONAL),
"nproficient" => new \external_value(PARAM_INT, 'number of students with proficiency',VALUE_OPTIONAL),
"ncourseproficient" => new \external_value(PARAM_INT, 'number of students with course proficiency',VALUE_OPTIONAL),
"count" => new \external_value(PARAM_INT, 'number of students in stats',VALUE_OPTIONAL),
];
if($recurse) {
$struct["children"] = new \external_multiple_structure(self::competencyinfo_structure(false),'child competencies',VALUE_OPTIONAL);
}
return new \external_single_structure($struct, 'course completion info');
}
/**
* Webservice structure for editor info
* @param int $value Webservice requirement constant
*/
public static function editor_structure($value = VALUE_REQUIRED) : \external_description {
return new \external_single_structure([
"competencies" => new \external_multiple_structure(self::competencyinfo_structure(), 'competencies'),
"fproficient" => new \external_value(PARAM_FLOAT, 'fraction of completion for total course proficiency ',VALUE_OPTIONAL),
"fcourseproficient" => new \external_value(PARAM_FLOAT, 'fraction of completion for total course proficienct',VALUE_OPTIONAL),
], 'course completion info', $value);
}
/**
* Webservice structure for userinfo
* @param int $value Webservice requirement constant
*/
public static function user_structure($value = VALUE_REQUIRED) : \external_description {
return new \external_single_structure([
"progress" => new \external_value(PARAM_INT, 'number completed competencies'),
"competencies" => new \external_multiple_structure(self::competencyinfo_structure(), 'competencies'),
"count" => new \external_value(PARAM_INT, 'number of competencies',VALUE_OPTIONAL),
], 'course completion info', $value);
}
/**
* Create basic competency information model from competency
* @param Object $competency
*/
private function competencyinfo_model($competency) : array {
$path = [];
foreach ($competency->get_ancestors() as $c) {
$competencypath[] = $c->get('shortname');
$path[] = [
'id' => $c->get('id'),
'shortname' => $c->get('shortname'),
'idnumber' => $c->get('idnumber'),
];
}
$path[] = [
'id' => $competency->get('id'),
'shortname' => $competency->get('shortname'),
'idnumber' => $competency->get('idnumber'),
];
$model = [
'id' => $competency->get('id'),
'shortname' => $competency->get('shortname'),
'idnumber' => $competency->get('idnumber'),
'description' => $competency->get('description'),
'path' => $path,
];
return $model;
}
/**
* Webservice model for editor info
* @param int[] $studentlist List of user id's to use for checking issueing progress within a study plan
* @return array Webservice data model
*/
public function editor_model(array $studentlist = null) {
$coursecompetencies = $this->course_competencies();
// Next create the data model, and check user proficiency for each competency.
$count = 0;
$nproficient = 0;
$ncourseproficient = 0;
foreach($coursecompetencies as $c) {
$stats = $this->proficiency_stats($c,$studentlist);
$count += $stats->count;
$nproficient += $stats->nproficient;
$ncourseproficient += $stats->ncourseproficient;
$ci = $this->competencyinfo_model($c);
// Copy proficiency stats to model.
foreach ((array)$stats as $key => $value) {
$ci[$key] = $value;
}
// get one level of children
$dids = competency::get_descendants_ids($c);
if(count($dids) > 0) {
$children = [];
foreach($dids as $did) {
$cc = new competency($did);
$cci = $this->competencyinfo_model($cc);
$children[] = $cci;
}
$ci["children"] = $children;
}
$cis[] = $ci;
}
$info = [
"competencies" => $cis,
"fproficient" => (float)($nproficient)/(float)($count),
"fcourseproficient" => (float)($ncourseproficient)/(float)($count),
];
return $info;
}
/**
* Webservice model for user course completion info
* @param int $userid ID of user to check specific info for
* @return array Webservice data model
*/
public function user_model($userid) {
$competencies = $this->course_competencies();
$progress = 0;
$cis = [];
foreach ($competencies as $c) {
$ci = $this->competencyinfo_model($c,$userid);
// Add user info if $userid is set.
$p = $this->proficiency($c,$userid);
// Copy proficiency info to model.
foreach ((array)$p as $key => $value) {
$ci[$key] = $value;
}
if ($p->proficient || $p->courseproficient) {
$progress += 1;
}
// get one level of children
$dids = competency::get_descendants_ids($c);
if(count($dids) > 0) {
$children = [];
foreach($dids as $did) {
$cc = new competency($did);
$cci = $this->competencyinfo_model($cc);
$cp = $this->proficiency($cc,$userid);
// Copy proficiency info to model.
foreach ((array)$cp as $key => $value) {
$cci[$key] = $value;
}
$children[] = $cci;
}
$ci["children"] = $children;
}
$cis[] = $ci;
}
$info = [
'progress' => $progress,
"count" => count($competencies),
"competencies" => $cis,
];
return $info;
}
/**
* Get the course's competencies with user status
* @return array of Competencies Webservice data model
*/
public function course_competencies() {
$list = [];
// First retrieve all the competencies associates with this course.
$coursecompetencies = c_api::list_course_competencies($this->course->id);
// Next create the data model, and check user proficiency for each competency.
foreach($coursecompetencies as $ccinfo) {
$list[] = $ccinfo['competency'];
}
return $list;
}
protected function proficiency_stats($competency,$studentlist) {
$r = new \stdClass();
$r->count = 0;
$r->nproficient = 0;
$r->ncourseproficient = 0;
foreach ($studentlist as $sid) {
$p = $this->proficiency($competency,$sid);
$r->count += 1;
$r->nproficient += ($p->proficient)?1:0;
$r->ncourseproficient += ($p->courseproficient)?1:0;
}
return $r;
}
/**
* Retrieve course proficiency and overall proficiency for a competency and user
*
* @param \core_competency\competency $competency
* @param int $userid
*
* @return stdClass
*
*/
public function proficiency($competency, $userid) {
$scale = $competency->get_scale();
$competencyid = $competency->get('id');
$uc = c_api::get_user_competency($userid, $competencyid);
$ucc = c_api::get_user_competency_in_course($this->course->id,$userid,$competencyid);
$r = new \stdClass();
$r->proficient = $uc->get('proficiency');
$r->courseproficient = $ucc->get('proficiency');
$r->grade = $scale->get_nearest_item($uc->get('grade'));
$r->coursegrade = $scale->get_nearest_item($ucc->get('grade'));
return $r;
}
}