. /** * 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; } }