moodle_local_treestudyplan/classes/courseservice.php

400 lines
No EOL
14 KiB
PHP

<?php
namespace local_treestudyplan;
require_once($CFG->libdir.'/externallib.php');
use \local_treestudyplan\courseinfo;
use \local_treestudyplan\associationservice;
use \local_treestudyplan\local\helpers\webservicehelper;
use \local_treestudyplan\completionscanner;
use \local_treestudyplan\gradingscanner;
class courseservice extends \external_api
{
const CAP_EDIT = "local/treestudyplan:editstudyplan";
const CAP_VIEW = "local/treestudyplan:viewuserreports";
/************************
* *
* list_courses *
* *
************************/
public static function map_categories_parameters()
{
return new \external_function_parameters( [
"root_id" => new \external_value(PARAM_INT, 'root category to use as base', VALUE_DEFAULT),
] );
}
public static function map_categories_returns()
{
return new \external_multiple_structure(static::map_category_structure(false));
}
protected static function map_category_structure($lazy=false,$value=VALUE_REQUIRED)
{
$s = [
"id" => new \external_value(PARAM_INT, 'course category id'),
"context_id" => new \external_value(PARAM_INT, 'course category context id'),
"category" => contextinfo::structure(VALUE_OPTIONAL),
"haschildren" => new \external_value(PARAM_BOOL, 'True if the category has child categories'),
"hascourses" => new \external_value(PARAM_BOOL, 'True if the category contains courses'),
"studyplancount" => new \external_value(PARAM_INT, 'number of linked studyplans',VALUE_OPTIONAL),
];
if(!$lazy > 0) {
$s["courses"] = new \external_multiple_structure( courseinfo::editor_structure() );
$s["children"] = new \external_multiple_structure( static::map_category_structure(true));
}
return new \external_single_structure($s,"CourseCat info",$value);
}
public static function map_categories($root_id = 0){
global $CFG, $DB;
$root = \core_course_category::get($root_id);
$context = $root->get_context();
// Make sure the user has access to the context for editing purposes
webservicehelper::require_capabilities(self::CAP_EDIT,$context);
// Determine top categories from provided context
if($root->id == 0){
// on the system level, determine the user's topmost allowed catecories
$user_top = \core_course_category::user_top();
if($user_top->id == 0){ // top category..
$children = $root->get_children(); // returns a list of çore_course_category, let it overwrite $children
} else {
$children = [$user_top];
}
} else if ($root->is_uservisible()){
$children = [$root];
}
foreach($children as $cat){
$list[] = static::map_category($cat,false);
}
return $list;
}
public static function get_category_parameters()
{
return new \external_function_parameters( [
"id" => new \external_value(PARAM_INT, 'id of category'),
] );
}
public static function get_category_returns()
{
return static::map_category_structure(false);
}
public static function get_category($id){
$cat = \core_course_category::get($id);
return static::map_category($cat);
}
protected static function map_category(\core_course_category $cat,$lazy=false){
global $DB;
$catcontext = $cat->get_context();
$ctx_info = new contextinfo($catcontext);
$children = $cat->get_children(); // only shows children visible to the current user
$courses = $cat->get_courses();
$model = [
"id" => $cat->id,
"context_id" => $catcontext->id,
"category" => $ctx_info->model(),
"haschildren" => !empty($children),
"hascourses" => !empty($courses),
];
if(!$lazy)
{
$model["courses"] = [];
foreach($courses as $course){
$courseinfo = new courseinfo($course->id);
$model["courses"][] = $courseinfo->editor_model();
}
$model["children"] = [];
foreach($children as $child){
$model["children"][] = static::map_category($child,true);
}
}
return $model;
}
public static function list_accessible_categories_parameters()
{
return new \external_function_parameters( [
"operation" => new \external_value(PARAM_TEXT, 'type of operation ["view"|"edit"]',VALUE_DEFAULT),]
);
}
public static function list_accessible_categories_returns()
{
return new \external_multiple_structure(static::map_category_structure(true));
}
public static function list_accessible_categories($operation="edit")
{
if($operation == "edit"){
$capability = self::CAP_EDIT;
} else { // $operation == "view" || default
$capability = self::CAP_VIEW;
}
$cats = static::categories_by_capability($capability);
$list = [];
/* @var $cat \core_course_category */
foreach($cats as $cat){
$list[] = static::map_category($cat,true);
}
return $list;
}
public static function categories_by_capability($capability,\core_course_category $parent=null){
// List the categories in which the user has a specific capability
$list = [];
// initialize parent if needed
if($parent == null){
$parent = \core_course_category::user_top();
if(has_capability($capability,$parent->get_context())){
$list[] = $parent;
}
}
$children = $parent->get_children();
foreach($children as $child){
// Check if we should add this category
if(has_capability($capability,$child->get_context())){
$list[] = $child;
// For optimization purposes, we include all its children now, since they will have inherited the permission
// #PREMATURE_OPTIMIZATION ???
$list = array_merge($list,self::recursive_child_categories($child));
} else {
if($child->get_children_count() > 0){
$list = array_merge($list,self::categories_by_capability($capability,$child));
}
}
}
return $list;
}
protected static function recursive_child_categories(\core_course_category $parent){
$list = [];
$children = $parent->get_children();
foreach($children as $child){
$list[] = $child;
if($child->get_children_count() > 0){
$list = array_merge($list,self::recursive_child_categories($child));
}
}
return $list;
}
public static function list_used_categories_parameters()
{
return new \external_function_parameters( [
"operation" => new \external_value(PARAM_TEXT, 'type of operation ["view"|"edit"]',VALUE_DEFAULT),
]);
}
public static function list_used_categories_returns()
{
return new \external_multiple_structure(static::map_category_structure(true));
}
public static function list_used_categories($operation='edit')
{
global $DB;
if($operation == "edit"){
$capability = self::CAP_EDIT;
} else { // $operation == "view" || default
$capability = self::CAP_VIEW;
}
$context_ids = [];
$rs = $DB->get_recordset_sql("SELECT DISTINCT context_id, COUNT(*) as num FROM {local_treestudyplan}
GROUP BY context_id");
foreach($rs as $r){
$context_ids[$r->context_id] = $r->num;
}
$rs->close();
// Now filter the categories that the user has acces to by the used context id's
// (That should filter out irrelevant stuff)
$cats = static::categories_by_capability($capability);
$list = [];
foreach($cats as $cat){
$count = 0;
$ctxid = $cat->get_context()->id;
if(array_key_exists($ctxid,$context_ids)){
$count = $context_ids[$ctxid];
}
$o = static::map_category($cat,true);
$o["studyplancount"] = $count;
$list[] = $o;
}
return $list;
}
public static function list_accessible_categories_with_usage($operation='edit'){
global $DB;
if($operation == "edit"){
$capability = self::CAP_EDIT;
} else { // $operation == "view" || default
$capability = self::CAP_VIEW;
}
// retrieve context ids used
$context_ids = [];
$rs = $DB->get_recordset_sql("SELECT DISTINCT context_id, COUNT(*) as num FROM {local_treestudyplan}
GROUP BY context_id");
foreach($rs as $r){
$context_ids[$r->context_id] = $r->num;
}
$rs->close();
// Now filter the categories that the user has acces to by the used context id's
// (That should filter out irrelevant stuff)
$cats = static::categories_by_capability($capability);
$list = [];
foreach($cats as $cat){
$count = 0;
$ctxid = $cat->get_context()->id;
if(array_key_exists($ctxid,$context_ids)){
$count = $context_ids[$ctxid];
}
$o = new \stdClass();
$o->cat = $cat;
$o->ctxid = $ctxid;
$o->count = $count;
$list[] = $o;
}
return $list;
}
/**************************************
*
* Progress scanners for teacherview
*
**************************************/
public static function scan_grade_progress_parameters()
{
return new \external_function_parameters( [
"gradeitemid" => new \external_value(PARAM_INT, 'Grade item ID to scan progress for',VALUE_DEFAULT),
"studyplanid" => new \external_value(PARAM_INT, 'Study plan id to check progress in',VALUE_DEFAULT),
]);
}
public static function scan_grade_progress_returns()
{
return gradingscanner::structure(VALUE_REQUIRED);
}
public static function scan_grade_progress($gradeitemid, $studyplanid)
{
global $DB;
// Verify access to the study plan
$o = studyplan::findById($studyplanid);
webservicehelper::require_capabilities(self::CAP_VIEW,$o->context());
// Retrieve grade item
$gi = \grade_item::fetch(["id" => $gradeitemid]);
// Validate course is linked to studyplan
$courseid = $gi->courseid;
if(!$o->course_linked($courseid)){
throw new \webservice_access_exception("Course {$courseid} linked to grade item {$gradeitemid} is not linked to studyplan {$o->id()}");
}
$scanner = new gradingscanner($gi);
return $scanner->model();
}
public static function scan_completion_progress_parameters()
{
return new \external_function_parameters( [
"criteriaid" => new \external_value(PARAM_INT, 'CriteriaID to scan progress for',VALUE_DEFAULT),
"studyplanid" => new \external_value(PARAM_INT, 'Study plan id to check progress in',VALUE_DEFAULT),
"courseid" => new \external_value(PARAM_INT, 'Course id of criteria',VALUE_DEFAULT),
]);
}
public static function scan_completion_progress_returns()
{
return completionscanner::structure(VALUE_REQUIRED);
}
public static function scan_completion_progress($criteriaid, $studyplanid,$courseid)
{
global $DB;
// Verify access to the study plan
$o = studyplan::findById($studyplanid);
webservicehelper::require_capabilities(self::CAP_VIEW,$o->context());
$crit = \completion_criteria::fetch(["id" => $criteriaid]);
if(!$o->course_linked($courseid)){
throw new \webservice_access_exception("Course {$courseid} linked to criteria {$criteriaid} is not linked to studyplan {$o->id()}");
}
$scanner = new completionscanner($crit,\get_course($courseid));
return $scanner->model();
}
public static function scan_badge_progress_parameters()
{
return new \external_function_parameters( [
"badgeid" => new \external_value(PARAM_INT, 'Badge to scan progress for',VALUE_DEFAULT),
"studyplanid" => new \external_value(PARAM_INT, 'Study plan id to limit progress search to (to determine which students to scan)',VALUE_DEFAULT),
]);
}
public static function scan_badge_progress_returns()
{
return new \external_single_structure([
"total" => new \external_value(PARAM_INT, 'Total number of students scanned'),
"issued" => new \external_value(PARAM_INT, 'Number of issued badges'),
]);
}
public static function scan_badge_progress($badgeid,$studyplanid)
{
global $DB;
// Check access to the study plan
$o = studyplan::findById($studyplanid);
webservicehelper::require_capabilities(self::CAP_VIEW,$o->context());
// Validate that badge is linked to studyplan
if(!$o->badge_linked($badgeid)){
throw new \webservice_access_exception("Badge {$badgeid} is not linked to studyplan {$o->id()}");
}
// Get badge info
$badge = new \core_badges\badge($badgeid);
$badgeinfo = new badgeinfo($badge);
// get the connected users
$students = associationservice::all_associated($studyplanid);
// Just get the user ids
$studentids = array_map(function ($a){ return $a["id"];},$students);
return [
"total" => count($studentids),
"issued" => $badgeinfo->count_issued($studentids),
];
}
}