Differences Between: [Versions 311 and 402] [Versions 400 and 402] [Versions 401 and 402]
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 namespace mod_h5pactivity\external; 18 19 use mod_h5pactivity\local\manager; 20 use mod_h5pactivity\local\attempt; 21 use mod_h5pactivity\local\report; 22 use mod_h5pactivity\local\report\attempts as report_attempts; 23 use core_external\external_api; 24 use core_external\external_function_parameters; 25 use core_external\external_multiple_structure; 26 use core_external\external_single_structure; 27 use core_external\external_value; 28 use core_external\external_warnings; 29 use moodle_exception; 30 use context_module; 31 use stdClass; 32 33 /** 34 * This is the external method to return the information needed to list all enrolled user attempts. 35 * 36 * @package mod_h5pactivity 37 * @since Moodle 3.11 38 * @copyright 2020 Ilya Tregubov <ilya@moodle.com> 39 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 40 */ 41 class get_user_attempts extends external_api { 42 43 /** 44 * Webservice parameters. 45 * 46 * @return external_function_parameters 47 */ 48 public static function execute_parameters(): external_function_parameters { 49 return new external_function_parameters( 50 [ 51 'h5pactivityid' => new external_value(PARAM_INT, 'h5p activity instance id'), 52 'sortorder' => new external_value(PARAM_TEXT, 53 'sort by either user id, firstname or lastname (with optional asc/desc)', VALUE_DEFAULT, 'id ASC'), 54 'page' => new external_value(PARAM_INT, 'current page', VALUE_DEFAULT, -1), 55 'perpage' => new external_value(PARAM_INT, 'items per page', VALUE_DEFAULT, 0), 56 'firstinitial' => new external_value(PARAM_TEXT, 'Users whose first name ' . 57 'starts with $firstinitial', VALUE_DEFAULT, ''), 58 'lastinitial' => new external_value(PARAM_TEXT, 'Users whose last name ' . 59 'starts with $lastinitial', VALUE_DEFAULT, ''), 60 ] 61 ); 62 } 63 64 /** 65 * Return user attempts information in a h5p activity. 66 * 67 * @throws moodle_exception if the user cannot see the report 68 * @param int $h5pactivityid The h5p activity id 69 * @param int $sortorder The sort order 70 * @param int $page page number 71 * @param int $perpage items per page 72 * @param int $firstinitial Users whose first name starts with $firstinitial 73 * @param int $lastinitial Users whose last name starts with $lastinitial 74 * @return stdClass report data 75 */ 76 public static function execute(int $h5pactivityid, $sortorder = 'id ASC', ?int $page = 0, 77 ?int $perpage = 0, $firstinitial = '', $lastinitial = ''): stdClass { 78 [ 79 'h5pactivityid' => $h5pactivityid, 80 'sortorder' => $sortorder, 81 'page' => $page, 82 'perpage' => $perpage, 83 'firstinitial' => $firstinitial, 84 'lastinitial' => $lastinitial, 85 ] = external_api::validate_parameters(self::execute_parameters(), [ 86 'h5pactivityid' => $h5pactivityid, 87 'sortorder' => $sortorder, 88 'page' => $page, 89 'perpage' => $perpage, 90 'firstinitial' => $firstinitial, 91 'lastinitial' => $lastinitial, 92 ]); 93 94 $warnings = []; 95 96 [$course, $cm] = get_course_and_cm_from_instance($h5pactivityid, 'h5pactivity'); 97 98 $context = context_module::instance($cm->id); 99 self::validate_context($context); 100 101 $manager = manager::create_from_coursemodule($cm); 102 $instance = $manager->get_instance(); 103 if (!$manager->can_view_all_attempts()) { 104 throw new moodle_exception('nopermissiontoviewattempts', 'error', '', null, 105 'h5pactivity:reviewattempts required view attempts of all enrolled users.'); 106 } 107 108 // Ensure sortorder parameter is safe to use. Fallback to default value of the parameter itself. 109 $sortorderparts = explode(' ', $sortorder, 2); 110 $sortorder = get_safe_orderby([ 111 'id' => 'u.id', 112 'firstname' => 'u.firstname', 113 'lastname' => 'u.lastname', 114 'default' => 'u.id', 115 ], $sortorderparts[0], $sortorderparts[1] ?? ''); 116 117 $users = self::get_active_users($manager, 'u.id, u.firstname, u.lastname', 118 $sortorder, $page * $perpage, $perpage); 119 120 $usersattempts = []; 121 122 foreach ($users as $user) { 123 124 if ($firstinitial) { 125 if (strpos($user->firstname, $firstinitial) === false) { 126 continue; 127 } 128 } 129 130 if ($lastinitial) { 131 if (strpos($user->lastname, $lastinitial) === false) { 132 continue; 133 } 134 } 135 136 $report = $manager->get_report($user->id); 137 if ($report && $report instanceof report_attempts) { 138 $usersattempts[] = self::export_user_attempts($report, $user->id); 139 } else { 140 $warnings[] = [ 141 'item' => 'user', 142 'itemid' => $user->id, 143 'warningcode' => '1', 144 'message' => "Cannot access user attempts", 145 ]; 146 } 147 } 148 149 $result = (object)[ 150 'activityid' => $instance->id, 151 'usersattempts' => $usersattempts, 152 'warnings' => $warnings, 153 ]; 154 155 return $result; 156 } 157 158 /** 159 * Generate the active users list 160 * 161 * @param manager $manager the h5pactivity manager 162 * @param string $userfields the user fields to get 163 * @param string $sortorder the SQL sortorder 164 * @param int $limitfrom SQL limit from 165 * @param int $limitnum SQL limit num 166 */ 167 private static function get_active_users( 168 manager $manager, 169 string $userfields = 'u.*', 170 string $sortorder = '', 171 int $limitfrom = 0, 172 int $limitnum = 0 173 ): array { 174 175 global $DB; 176 177 $capjoin = $manager->get_active_users_join(true); 178 179 // Final SQL. 180 $sql = "SELECT DISTINCT {$userfields} 181 FROM {user} u {$capjoin->joins} 182 WHERE {$capjoin->wheres} 183 {$sortorder}"; 184 185 return $DB->get_records_sql($sql, $capjoin->params, $limitfrom, $limitnum); 186 } 187 188 /** 189 * Export attempts data for a specific user. 190 * 191 * @param report $report the report attempts object 192 * @param int $userid the user id 193 * @return stdClass 194 */ 195 private static function export_user_attempts(report $report, int $userid): stdClass { 196 $scored = $report->get_scored(); 197 $attempts = $report->get_attempts(); 198 199 $result = (object)[ 200 'userid' => $userid, 201 'attempts' => [], 202 ]; 203 204 foreach ($attempts as $attempt) { 205 $result->attempts[] = self::export_attempt($attempt); 206 } 207 208 if (!empty($scored)) { 209 $result->scored = (object)[ 210 'title' => $scored->title, 211 'grademethod' => $scored->grademethod, 212 'attempts' => [self::export_attempt($scored->attempt)], 213 ]; 214 } 215 216 return $result; 217 } 218 219 /** 220 * Return a data object from an attempt. 221 * 222 * @param attempt $attempt the attempt object 223 * @return stdClass a WS compatible version of the attempt 224 */ 225 private static function export_attempt(attempt $attempt): stdClass { 226 $result = (object)[ 227 'id' => $attempt->get_id(), 228 'h5pactivityid' => $attempt->get_h5pactivityid(), 229 'userid' => $attempt->get_userid(), 230 'timecreated' => $attempt->get_timecreated(), 231 'timemodified' => $attempt->get_timemodified(), 232 'attempt' => $attempt->get_attempt(), 233 'rawscore' => $attempt->get_rawscore(), 234 'maxscore' => $attempt->get_maxscore(), 235 'duration' => $attempt->get_duration(), 236 'scaled' => $attempt->get_scaled(), 237 ]; 238 if ($attempt->get_completion() !== null) { 239 $result->completion = $attempt->get_completion(); 240 } 241 if ($attempt->get_success() !== null) { 242 $result->success = $attempt->get_success(); 243 } 244 return $result; 245 } 246 247 /** 248 * Describes the get_h5pactivity_access_information return value. 249 * 250 * @return external_single_structure 251 */ 252 public static function execute_returns(): external_single_structure { 253 return new external_single_structure([ 254 'activityid' => new external_value(PARAM_INT, 'Activity course module ID'), 255 'usersattempts' => new external_multiple_structure( 256 self::get_user_attempts_returns(), 'The complete users attempts list' 257 ), 258 'warnings' => new external_warnings(), 259 ], 'Activity attempts data'); 260 } 261 262 /** 263 * Describes the get_h5pactivity_access_information return value. 264 * 265 * @return external_single_structure 266 */ 267 private static function get_user_attempts_returns(): external_single_structure { 268 $structure = [ 269 'userid' => new external_value(PARAM_INT, 'The user id'), 270 'attempts' => new external_multiple_structure(self::get_user_attempt_returns(), 'The complete attempts list'), 271 'scored' => new external_single_structure([ 272 'title' => new external_value(PARAM_NOTAGS, 'Scored attempts title'), 273 'grademethod' => new external_value(PARAM_NOTAGS, 'Grading method'), 274 'attempts' => new external_multiple_structure(self::get_user_attempt_returns(), 'List of the grading attempts'), 275 ], 'Attempts used to grade the activity', VALUE_OPTIONAL), 276 ]; 277 return new external_single_structure($structure); 278 } 279 280 /** 281 * Return the external structure of an attempt. 282 * 283 * @return external_single_structure 284 */ 285 private static function get_user_attempt_returns(): external_single_structure { 286 $result = new external_single_structure([ 287 'id' => new external_value(PARAM_INT, 'ID of the context'), 288 'h5pactivityid' => new external_value(PARAM_INT, 'ID of the H5P activity'), 289 'userid' => new external_value(PARAM_INT, 'ID of the user'), 290 'timecreated' => new external_value(PARAM_INT, 'Attempt creation'), 291 'timemodified' => new external_value(PARAM_INT, 'Attempt modified'), 292 'attempt' => new external_value(PARAM_INT, 'Attempt number'), 293 'rawscore' => new external_value(PARAM_INT, 'Attempt score value'), 294 'maxscore' => new external_value(PARAM_INT, 'Attempt max score'), 295 'duration' => new external_value(PARAM_INT, 'Attempt duration in seconds'), 296 'completion' => new external_value(PARAM_INT, 'Attempt completion', VALUE_OPTIONAL), 297 'success' => new external_value(PARAM_INT, 'Attempt success', VALUE_OPTIONAL), 298 'scaled' => new external_value(PARAM_FLOAT, 'Attempt scaled'), 299 ]); 300 return $result; 301 } 302 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body