moodle_local_treestudyplan/classes/badgeinfo.php

321 lines
13 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/>.
/**
* Handle badge information
* @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');
use \core_badges\badge;
/**
* Handle badge information in the same style as the other classes
*/
class badgeinfo {
/**
* Holds database record
* @var \core_badges\badge
*/
private $badge;
/**
* Maps badge status to strings
* @var array
*/
private const STATUSINFO = [
BADGE_STATUS_INACTIVE => 'inactive',
BADGE_STATUS_ACTIVE => 'active',
BADGE_STATUS_INACTIVE_LOCKED => 'inactive',
BADGE_STATUS_ACTIVE_LOCKED => 'active',
BADGE_STATUS_ARCHIVED => 'archived',
];
/**
* Maps badge status to locked or not
* @var array
*/
private const LOCKEDINFO = [
BADGE_STATUS_INACTIVE => 0,
BADGE_STATUS_ACTIVE => 0,
BADGE_STATUS_INACTIVE_LOCKED => 1,
BADGE_STATUS_ACTIVE_LOCKED => 1,
BADGE_STATUS_ARCHIVED => 1, // We don't want to edit archived badges anyway.... .
];
/**
* Construct new badgeinfo object
* @param badge $badge Badge object to use
*/
public function __construct(badge $badge) {
$this->badge = $badge;
}
/**
* Return full name
* @return string
*/
public function name() {
return $this->badge->name;
}
/**
* Get the badge id from the badge name
* @param string $name Badge name
* @return int Badge id
*/
public static function id_from_name($name) {
global $DB;
return $DB->get_field("badge", "id", ['name' => $name]);
}
/**
* Check if a given badge exists
* @param int $id Badge id
* @return bool
*/
public static function exists($id) {
global $DB;
return is_numeric($id) && $DB->record_exists('badge', array('id' => $id));
}
/**
* 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([
"id" => new \external_value(PARAM_INT, 'id of badge'),
"infolink" => new \external_value(PARAM_TEXT, 'badge issue information link', VALUE_OPTIONAL),
"name" => new \external_value(PARAM_TEXT, 'badge name'),
"status" => new \external_value(PARAM_TEXT, 'badge status'),
"locked" => new \external_value(PARAM_TEXT, 'badge lock status'),
"criteria" => new \external_multiple_structure(
new \external_value(PARAM_RAW, 'criteria text'), 'badge criteria', VALUE_OPTIONAL),
"description" => new \external_value(PARAM_TEXT, 'badge description'),
"imageurl" => new \external_value(PARAM_TEXT, 'url of badge image'),
"studentcount" => new \external_value(PARAM_INT,
'number of studyplan students that can get this badge', VALUE_OPTIONAL),
"issuedcount" => new \external_value(PARAM_INT,
'number of studyplan students that have got this badge', VALUE_OPTIONAL),
], "Badge info", $value);
}
/**
* 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) {
if ($this->badge->type == BADGE_TYPE_SITE) {
$context = \context_system::instance();
} else {
$context = \context_course::instance($this->badge->courseid);
}
// If the user is viewing another user's badge and doesn't have the right capability return only part of the data.
$criteria = [];
foreach ($this->badge->get_criteria() as $bc) {
$criteria[] = $bc->get_title()." ".$bc->get_details();
}
$model = [
'id' => $this->badge->id,
'infolink' => (new \moodle_url('/badges/overview.php', ['id' => $this->badge->id]))->out(false),
'name' => $this->badge->name,
'status' => self::STATUSINFO[$this->badge->status],
'locked' => self::LOCKEDINFO[$this->badge->status],
'criteria' => $criteria,
'description' => $this->badge->description,
'imageurl' => \moodle_url::make_pluginfile_url($context->id,
'badges', 'badgeimage', $this->badge->id, '/', 'f1')->out(false),
];
// Add badge issue stats if a studentlist is attached to the request.
if (!empty($studentlist) && is_array($studentlist)) {
$model['studentcount'] = count($studentlist);
$model['issuedcount'] = $this->count_issued($studentlist);
}
return $model;
}
/**
* 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([
"id" => new \external_value(PARAM_INT, 'id of badge'),
"infolink" => new \external_value(PARAM_TEXT, 'badge issue information link', VALUE_OPTIONAL),
"name" => new \external_value(PARAM_TEXT, 'badge name'),
"completion" => new \external_multiple_structure(new \external_single_structure([
"types" => new \external_multiple_structure(new \external_single_structure([
'criteria' => new \external_multiple_structure(new \external_single_structure([
"title" => new \external_value(PARAM_TEXT, 'criterion title'),
"details" => new \external_value(PARAM_TEXT, 'criterion details'),
"completed" => new \external_value(PARAM_BOOL, 'criterion is completed or not'),
]),'specific criteria'),
"aggregation" => new \external_value(PARAM_TEXT, 'any|all'),
"count" => new \external_value(PARAM_INT, 'effective number of critera for type progress'),
"progress" => new \external_value(PARAM_INT, 'effective number of completed criteria for type progress'),
"fraction" => new \external_value(PARAM_FLOAT, 'fraction of completed type criteria as float'),
]), "criteria types"),
"aggregation" => new \external_value(PARAM_TEXT, 'any|all'),
"count" => new \external_value(PARAM_INT, 'total number of critera for progress'),
"progress" => new \external_value(PARAM_INT, 'number of completed criteria for progress'),
"fraction" => new \external_value(PARAM_FLOAT, 'fraction of completed criteria as float'),
]),'badge criteria', VALUE_OPTIONAL),
"description" => new \external_value(PARAM_TEXT, 'badge description'),
"imageurl" => new \external_value(PARAM_TEXT, 'url of badge image'),
"issued" => new \external_value(PARAM_BOOL, 'badge is issued'),
"dateissued" => new \external_value(PARAM_TEXT, 'date the badge was issued', VALUE_OPTIONAL),
"dateexpire" => new \external_value(PARAM_TEXT, 'date the badge will expire', VALUE_OPTIONAL),
"uniquehash" => new \external_value(PARAM_TEXT, 'badge issue hash', VALUE_OPTIONAL),
"issuedlink" => new \external_value(PARAM_TEXT, 'badge issue information link', VALUE_OPTIONAL),
], "Badge info", $value);
}
/**
* Webservice model for user info
* @param int $userid ID of user to check specific info for
* @return array Webservice data model
*/
public function user_model($userid) {
global $DB;
if ($this->badge->type == BADGE_TYPE_SITE) {
$context = \context_system::instance();
} else {
$context = \context_course::instance($this->badge->courseid);
}
$issued = $this->badge->is_issued($userid);
// If the user is viewing another user's badge and doesn't have the right capability return only part of the data.
$badge = [
'id' => $this->badge->id,
'name' => $this->badge->name,
'description' => $this->badge->description,
'imageurl' => \moodle_url::make_pluginfile_url($context->id,
'badges',
'badgeimage',
$this->badge->id, '/', 'f1'
)->out(false),
'completion' => $this->badge_completion_data($userid),
'issued' => $issued,
'infolink' => (new \moodle_url('/badges/overview.php', ['id' => $this->badge->id]))->out(false),
];
if ($issued) {
$issueinfo = $DB->get_record('badge_issued', array('badgeid' => $this->badge->id, 'userid' => $userid));
$badge['dateissued'] = date("Y-m-d", $issueinfo->dateissued);
if ($issueinfo->expiredate) {
$badge['dateexpire'] = date("Y-m-d", $issueinfo->dateexpire);
}
$badge['uniquehash'] = $issueinfo->uniquehash;
$badge['issuedlink'] = (new \moodle_url('/badges/badge.php', ['hash' => $issueinfo->uniquehash]))->out(false);
}
return $badge;
}
protected function badge_completion_data($userid) {
$count = 0;
$progress = 0;
$fraction = 0;
$badgeagg = $this->badge->get_aggregation_method();
$types = [];
foreach ($this->badge->criteria as $type => $c) {
$typeagg = $this->badge->get_aggregation_method($type);
$typecrit = [];
$typeprogress = 0;
$typecount = count($c);
foreach ($c as $bc) {
$iscompleted = $bc->review($userid);
$typecrit[] = [
"title" => $bc->get_title(),
"details" => $bc->get_details(),
"completed" => $iscompleted,
];
if ($iscompleted) {
$typeprogress++;
}
}
if ($typeagg == BADGE_CRITERIA_AGGREGATION_ANY) {
$typecount = 1;
$typeprogress = ($typeprogress > 0)?1:0;
}
if($badgeagg == BADGE_CRITERIA_AGGREGATION_ANY) {
/* If ANY completion overall, count only the criteria type with the highest completion percentage -.
Overwrite data if current type is more complete */
$typefraction = $typeprogress / $typecount;
if ($typefraction > $fraction) {
$fraction = $typefraction;
$count = $typecount;
$progress = $typeprogress;
}
} else {
/* If ALL completion overall, just add it up to the total */
$count += $typecount;
$progress += $typeprogress;
}
$typeinfo = [
'aggregation' => ($typeagg == BADGE_CRITERIA_AGGREGATION_ALL)?"all":"any",
'criteria' => $typecrit,
'count' => $typecount,
'progress' => $typeprogress,
"fraction" => $typefraction,
];
$types[] = $typeinfo;
}
$c = [
"types" => $types,
"aggregation" => ($typeagg == BADGE_CRITERIA_AGGREGATION_ALL)?"all":"any",
"count" => $count,
"progress" => $progress,
"fraction" => $fraction,
];
}
/**
* Count how many of the students in the array have this badge issued
* @param int[] $studentids List of user id's to check
* @return int
*/
public function count_issued(array $studentids) {
$issuecount = 0;
foreach ($studentids as $userid) {
if ($this->badge->is_issued($userid)) {
$issuecount++;
}
}
return $issuecount;
}
}