2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
18 * Web services relating to fetching of a rubric for the grading panel.
20 * @package gradingform_rubric
21 * @copyright 2019 Mathew May <mathew.solutions>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 declare(strict_types = 1);
27 namespace gradingform_rubric\grades\grader\gradingpanel\external;
33 use core_grades\component_gradeitem as gradeitem;
34 use core_grades\component_gradeitems;
36 use external_function_parameters;
37 use external_multiple_structure;
38 use external_single_structure;
40 use external_warnings;
43 require_once($CFG->dirroot.'/grade/grading/form/rubric/lib.php');
46 * Web services relating to fetching of a rubric for the grading panel.
48 * @package gradingform_rubric
49 * @copyright 2019 Mathew May <mathew.solutions>
50 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
52 class fetch extends external_api {
55 * Describes the parameters for fetching the grading panel for a simple grade.
57 * @return external_function_parameters
60 public static function execute_parameters(): external_function_parameters {
61 return new external_function_parameters ([
62 'component' => new external_value(
64 'The name of the component',
67 'contextid' => new external_value(
69 'The ID of the context being graded',
72 'itemname' => new external_value(
74 'The grade item itemname being graded',
77 'gradeduserid' => new external_value(
79 'The ID of the user show',
86 * Fetch the data required to build a grading panel for a simple grade.
88 * @param string $component
89 * @param int $contextid
90 * @param string $itemname
91 * @param int $gradeduserid
95 public static function execute(string $component, int $contextid, string $itemname, int $gradeduserid): array {
97 'component' => $component,
98 'contextid' => $contextid,
99 'itemname' => $itemname,
100 'gradeduserid' => $gradeduserid,
101 ] = self::validate_parameters(self::execute_parameters(), [
102 'component' => $component,
103 'contextid' => $contextid,
104 'itemname' => $itemname,
105 'gradeduserid' => $gradeduserid,
108 // Validate the context.
109 $context = context::instance_by_id($contextid);
110 self::validate_context($context);
112 // Validate that the supplied itemname is a gradable item.
113 if (!component_gradeitems::is_valid_itemname($component, $itemname)) {
114 throw new coding_exception("The '{$itemname}' item is not valid for the '{$component}' component");
117 // Fetch the gradeitem instance.
118 $gradeitem = gradeitem::instance($component, $context, $itemname);
120 if (RUBRIC !== $gradeitem->get_advanced_grading_method()) {
121 throw new moodle_exception(
122 "The {$itemname} item in {$component}/{$contextid} is not configured for advanced grading with a rubric"
126 // Fetch the actual data.
127 $gradeduser = \core_user::get_user($gradeduserid);
129 return self::get_fetch_data($gradeitem, $gradeduser);
133 * Get the data to be fetched and create the structure ready for Mustache.
135 * @param gradeitem $gradeitem
136 * @param stdClass $gradeduser
139 public static function get_fetch_data(gradeitem $gradeitem, stdClass $gradeduser): array {
142 // Set up all the controllers etc that we'll be needing.
143 $grade = $gradeitem->get_grade_for_user($gradeduser, $USER);
144 $instance = $gradeitem->get_advanced_grading_instance($USER, $grade);
145 $controller = $instance->get_controller();
146 $definition = $controller->get_definition();
147 $fillings = $instance->get_rubric_filling();
148 $context = $controller->get_context();
149 $definitionid = (int) $definition->id;
151 $teacherdescription = self::get_formatted_text(
155 $definition->description,
156 (int) $definition->descriptionformat
160 if ($definition->rubric_criteria) {
161 // Iterate over the defined criterion in the rubric and map out what we need to render each item.
162 $criterion = array_map(function($criterion) use ($definitionid, $fillings, $context, $hasgrade) {
163 // The general structure we'll be returning, we still need to get the remark (if any) and the levels associated.
165 'id' => $criterion['id'],
166 'description' => self::get_formatted_text(
170 $criterion['description'],
171 (int) $criterion['descriptionformat']
175 // Do we have an existing grade filling? if so lets get the remark associated to this criteria.
177 if (array_key_exists($criterion['id'], $fillings['criteria'])) {
178 $filling = $fillings['criteria'][$criterion['id']];
179 $result['remark'] = self::get_formatted_text($context,
183 (int) $filling['remarkformat']
187 // Lets build the levels within a criteria and figure out what needs to go where.
188 $result['levels'] = array_map(function($level) use ($criterion, $filling, $context, $definitionid) {
189 // The bulk of what'll be returned can be defined easily we'll add to this further down.
191 'id' => $level['id'],
192 'criterionid' => $criterion['id'],
193 'score' => $level['score'],
194 'definition' => self::get_formatted_text(
198 $level['definition'],
199 (int) $level['definitionformat']
204 // Consult the grade filling to see if a level has been selected and if it is the current level.
205 if (array_key_exists('levelid', $filling) && $filling['levelid'] == $level['id']) {
206 $result['checked'] = true;
210 }, $criterion['levels']);
214 'criterionid' => $criterion['id'],
216 'definition' => get_string('notset', 'gradingform_rubric'),
217 'checked' => !$hasgrade,
219 // Consult the grade filling to see if a level has been selected and if it is the current level.
220 if (array_key_exists('levelid', $filling) && $filling['levelid'] == 0) {
221 $nulllevel['checked'] = true;
224 array_unshift($result['levels'], $nulllevel);
227 }, $definition->rubric_criteria);
231 'templatename' => 'gradingform_rubric/grades/grader/gradingpanel',
233 'instanceid' => $instance->get_id(),
234 'criteria' => $criterion,
235 'rubricmode' => 'evaluate editable',
236 'teacherdescription' => $teacherdescription,
238 'timecreated' => $grade->timecreated,
239 'timemodified' => $grade->timemodified,
246 * Describes the data returned from the external function.
248 * @return external_single_structure
251 public static function execute_returns(): external_single_structure {
252 return new external_single_structure([
253 'templatename' => new external_value(PARAM_SAFEPATH, 'The template to use when rendering this data'),
254 'grade' => new external_single_structure([
255 'instanceid' => new external_value(PARAM_INT, 'The id of the current grading instance'),
256 'rubricmode' => new external_value(PARAM_RAW, 'The mode i.e. evaluate editable'),
257 'canedit' => new external_value(PARAM_BOOL, 'Can the user edit this'),
258 'criteria' => new external_multiple_structure(
259 new external_single_structure([
260 'id' => new external_value(PARAM_INT, 'ID of the Criteria'),
261 'description' => new external_value(PARAM_RAW, 'Description of the Criteria'),
262 'remark' => new external_value(PARAM_RAW, 'Any remarks for this criterion for the user being assessed', VALUE_OPTIONAL),
263 'levels' => new external_multiple_structure(new external_single_structure([
264 'id' => new external_value(PARAM_INT, 'ID of level'),
265 'criterionid' => new external_value(PARAM_INT, 'ID of the criterion this matches to'),
266 'score' => new external_value(PARAM_RAW, 'What this level is worth'),
267 'definition' => new external_value(PARAM_RAW, 'Definition of the level'),
268 'checked' => new external_value(PARAM_BOOL, 'Selected flag'),
272 'timecreated' => new external_value(PARAM_INT, 'The time that the grade was created'),
273 'timemodified' => new external_value(PARAM_INT, 'The time that the grade was last updated'),
275 'warnings' => new external_warnings(),
280 * Get a formatted version of the remark/description/etc.
282 * @param context $context
283 * @param int $definitionid
284 * @param string $filearea The file area of the field
285 * @param string $text The text to be formatted
286 * @param int $format The input format of the string
289 protected static function get_formatted_text(context $context, int $definitionid, string $filearea, string $text, int $format): string {
295 [$newtext, ] = external_format_text($text, $format, $context, 'grading', $filearea, $definitionid, $formatoptions);