MDL-66958 gradingform_rubric: Only save on form change
[moodle.git] / grade / grading / form / rubric / classes / grades / grader / gradingpanel / external / fetch.php
CommitLineData
eb9df053
MM
1<?php
2// This file is part of Moodle - http://moodle.org/
3//
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.
8//
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.
13//
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/>.
16
17/**
18 * Web services relating to fetching of a rubric for the grading panel.
19 *
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
23 */
24
25declare(strict_types = 1);
26
27namespace gradingform_rubric\grades\grader\gradingpanel\external;
28
47919bbe
MM
29global $CFG;
30
eb9df053
MM
31use coding_exception;
32use context;
33use core_grades\component_gradeitem as gradeitem;
34use core_grades\component_gradeitems;
35use external_api;
36use external_function_parameters;
37use external_multiple_structure;
38use external_single_structure;
39use external_value;
40use external_warnings;
41use stdClass;
42use moodle_exception;
47919bbe 43require_once($CFG->dirroot.'/grade/grading/form/rubric/lib.php');
eb9df053
MM
44
45/**
46 * Web services relating to fetching of a rubric for the grading panel.
47 *
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
51 */
52class fetch extends external_api {
53
54 /**
55 * Describes the parameters for fetching the grading panel for a simple grade.
56 *
57 * @return external_function_parameters
58 * @since Moodle 3.8
59 */
60 public static function execute_parameters(): external_function_parameters {
61 return new external_function_parameters ([
62 'component' => new external_value(
63 PARAM_ALPHANUMEXT,
64 'The name of the component',
65 VALUE_REQUIRED
66 ),
67 'contextid' => new external_value(
68 PARAM_INT,
69 'The ID of the context being graded',
70 VALUE_REQUIRED
71 ),
72 'itemname' => new external_value(
73 PARAM_ALPHANUM,
74 'The grade item itemname being graded',
75 VALUE_REQUIRED
76 ),
77 'gradeduserid' => new external_value(
78 PARAM_INT,
79 'The ID of the user show',
80 VALUE_REQUIRED
81 ),
82 ]);
83 }
84
85 /**
86 * Fetch the data required to build a grading panel for a simple grade.
87 *
88 * @param string $component
89 * @param int $contextid
90 * @param string $itemname
91 * @param int $gradeduserid
92 * @return array
93 * @since Moodle 3.8
94 */
95 public static function execute(string $component, int $contextid, string $itemname, int $gradeduserid): array {
eb9df053
MM
96 [
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,
106 ]);
107
108 // Validate the context.
109 $context = context::instance_by_id($contextid);
110 self::validate_context($context);
111
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");
115 }
116
117 // Fetch the gradeitem instance.
118 $gradeitem = gradeitem::instance($component, $context, $itemname);
119
47919bbe 120 if (RUBRIC !== $gradeitem->get_advanced_grading_method()) {
eb9df053
MM
121 throw new moodle_exception(
122 "The {$itemname} item in {$component}/{$contextid} is not configured for advanced grading with a rubric"
123 );
124 }
125
126 // Fetch the actual data.
127 $gradeduser = \core_user::get_user($gradeduserid);
128
129 return self::get_fetch_data($gradeitem, $gradeduser);
130 }
131
132 /**
47919bbe 133 * Get the data to be fetched and create the structure ready for Mustache.
eb9df053
MM
134 *
135 * @param gradeitem $gradeitem
136 * @param stdClass $gradeduser
137 * @return array
138 */
139 public static function get_fetch_data(gradeitem $gradeitem, stdClass $gradeduser): array {
140 global $USER;
141
47919bbe 142 // Set up all the controllers etc that we'll be needing.
eb9df053
MM
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;
150
151 $teacherdescription = self::get_formatted_text(
152 $context,
153 $definitionid,
154 'description',
155 $definition->description,
156 (int) $definition->descriptionformat
157 );
158
159 $criterion = [];
160 if ($definition->rubric_criteria) {
47919bbe 161 // Iterate over the defined criterion in the rubric and map out what we need to render each item.
9769ba08 162 $criterion = array_map(function($criterion) use ($definitionid, $fillings, $context, $hasgrade) {
47919bbe 163 // The general structure we'll be returning, we still need to get the remark (if any) and the levels associated.
eb9df053
MM
164 $result = [
165 'id' => $criterion['id'],
166 'description' => self::get_formatted_text(
167 $context,
168 $definitionid,
169 'description',
170 $criterion['description'],
171 (int) $criterion['descriptionformat']
172 ),
173 ];
174
47919bbe 175 // Do we have an existing grade filling? if so lets get the remark associated to this criteria.
eb9df053
MM
176 $filling = [];
177 if (array_key_exists($criterion['id'], $fillings['criteria'])) {
178 $filling = $fillings['criteria'][$criterion['id']];
179 $result['remark'] = self::get_formatted_text($context,
180 $definitionid,
181 'remark',
182 $filling['remark'],
183 (int) $filling['remarkformat']
184 );
185 }
186
47919bbe 187 // Lets build the levels within a criteria and figure out what needs to go where.
eb9df053 188 $result['levels'] = array_map(function($level) use ($criterion, $filling, $context, $definitionid) {
47919bbe 189 // The bulk of what'll be returned can be defined easily we'll add to this further down.
eb9df053
MM
190 $result = [
191 'id' => $level['id'],
192 'criterionid' => $criterion['id'],
193 'score' => $level['score'],
194 'definition' => self::get_formatted_text(
195 $context,
196 $definitionid,
197 'definition',
198 $level['definition'],
199 (int) $level['definitionformat']
200 ),
201 'checked' => null,
202 ];
203
47919bbe 204 // Consult the grade filling to see if a level has been selected and if it is the current level.
eb9df053
MM
205 if (array_key_exists('levelid', $filling) && $filling['levelid'] == $level['id']) {
206 $result['checked'] = true;
207 }
208
209 return $result;
210 }, $criterion['levels']);
211
47919bbe
MM
212 $nulllevel = [
213 'id' => null,
214 'criterionid' => $criterion['id'],
215 'score' => '-',
9769ba08
MM
216 'definition' => get_string('notset', 'gradingform_rubric'),
217 'checked' => !$hasgrade,
47919bbe
MM
218 ];
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;
222 }
223
224 array_unshift($result['levels'], $nulllevel);
225
eb9df053
MM
226 return $result;
227 }, $definition->rubric_criteria);
228 }
229
230 return [
231 'templatename' => 'gradingform_rubric/grades/grader/gradingpanel',
232 'grade' => [
233 'instanceid' => $instance->get_id(),
234 'criteria' => $criterion,
235 'rubricmode' => 'evaluate editable',
236 'teacherdescription' => $teacherdescription,
237 'canedit' => false,
238 'timecreated' => $grade->timecreated,
239 'timemodified' => $grade->timemodified,
240 ],
241 'warnings' => [],
242 ];
243 }
244
245 /**
246 * Describes the data returned from the external function.
247 *
248 * @return external_single_structure
249 * @since Moodle 3.8
250 */
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'),
47919bbe 266 'score' => new external_value(PARAM_RAW, 'What this level is worth'),
eb9df053
MM
267 'definition' => new external_value(PARAM_RAW, 'Definition of the level'),
268 'checked' => new external_value(PARAM_BOOL, 'Selected flag'),
269 ])),
270 ])
271 ),
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'),
274 ]),
275 'warnings' => new external_warnings(),
276 ]);
277 }
278
279 /**
280 * Get a formatted version of the remark/description/etc.
281 *
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
287 * @return string
288 */
289 protected static function get_formatted_text(context $context, int $definitionid, string $filearea, string $text, int $format): string {
290 $formatoptions = [
291 'noclean' => false,
292 'trusted' => false,
293 'filter' => true,
294 ];
295 [$newtext, ] = external_format_text($text, $format, $context, 'grading', $filearea, $definitionid, $formatoptions);
296 return $newtext;
297 }
298}