c60f421d82652bd6e94e9cbe7095a277b8c06fa0
[moodle.git] / grade / grading / form / rubric / classes / grades / grader / gradingpanel / external / fetch.php
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/>.
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  */
25 declare(strict_types = 1);
27 namespace gradingform_rubric\grades\grader\gradingpanel\external;
29 global $CFG;
31 use coding_exception;
32 use context;
33 use core_grades\component_gradeitem as gradeitem;
34 use core_grades\component_gradeitems;
35 use external_api;
36 use external_function_parameters;
37 use external_multiple_structure;
38 use external_single_structure;
39 use external_value;
40 use external_warnings;
41 use stdClass;
42 use moodle_exception;
43 require_once($CFG->dirroot.'/grade/grading/form/rubric/lib.php');
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  */
52 class fetch extends external_api {
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     }
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 {
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         ]);
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");
115         }
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"
123             );
124         }
126         // Fetch the actual data.
127         $gradeduser = \core_user::get_user($gradeduserid);
129         return self::get_fetch_data($gradeitem, $gradeduser);
130     }
132     /**
133      * Get the data to be fetched and create the structure ready for Mustache.
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;
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(
152             $context,
153             $definitionid,
154             'description',
155             $definition->description,
156             (int) $definition->descriptionformat
157         );
159         $criterion = [];
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) {
163                 // The general structure we'll be returning, we still need to get the remark (if any) and the levels associated.
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                 ];
175                 // Do we have an existing grade filling? if so lets get the remark associated to this criteria.
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                 }
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.
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                     ];
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;
207                     }
209                     return $result;
210                 }, $criterion['levels']);
212                 $nulllevel = [
213                     'id' => null,
214                     'criterionid' => $criterion['id'],
215                     'score' => '-',
216                     'definition' => 'Not set',
217                     'checked' => null,
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                 }
224                 array_unshift($result['levels'], $nulllevel);
226                 return $result;
227             }, $definition->rubric_criteria);
228         }
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     }
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'),
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'),
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     }
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     }