See Release Notes
Long Term Support Release
Differences Between: [Versions 401 and 402] [Versions 401 and 403]
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 declare(strict_types=1); 18 19 namespace core_course\reportbuilder\local\entities; 20 21 use core_reportbuilder\local\entities\base; 22 use core_course\reportbuilder\local\formatters\completion as completion_formatter; 23 use core_reportbuilder\local\filters\boolean_select; 24 use core_reportbuilder\local\filters\date; 25 use core_reportbuilder\local\helpers\format; 26 use core_reportbuilder\local\report\column; 27 use core_reportbuilder\local\report\filter; 28 use lang_string; 29 use stdClass; 30 31 /** 32 * Course completion entity implementation 33 * 34 * @package core_course 35 * @copyright 2022 David Matamoros <davidmc@moodle.com> 36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 */ 38 class completion extends base { 39 40 /** 41 * Database tables that this entity uses and their default aliases 42 * 43 * @return array 44 */ 45 protected function get_default_table_aliases(): array { 46 return [ 47 'course_completion' => 'ccomp', 48 'course' => 'c', 49 'grade_grades' => 'gg', 50 'grade_items' => 'gi', 51 'user' => 'u', 52 ]; 53 } 54 55 /** 56 * The default title for this entity in the list of columns/conditions/filters in the report builder 57 * 58 * @return lang_string 59 */ 60 protected function get_default_entity_title(): lang_string { 61 return new lang_string('coursecompletion', 'completion'); 62 } 63 64 /** 65 * Initialise the entity 66 * 67 * @return base 68 */ 69 public function initialise(): base { 70 foreach ($this->get_all_columns() as $column) { 71 $this->add_column($column); 72 } 73 74 // All the filters defined by the entity can also be used as conditions. 75 foreach ($this->get_all_filters() as $filter) { 76 $this 77 ->add_filter($filter) 78 ->add_condition($filter); 79 } 80 81 return $this; 82 } 83 84 /** 85 * Returns list of all available columns 86 * 87 * @return column[] 88 */ 89 protected function get_all_columns(): array { 90 $coursecompletion = $this->get_table_alias('course_completion'); 91 $course = $this->get_table_alias('course'); 92 $grade = $this->get_table_alias('grade_grades'); 93 $gradeitem = $this->get_table_alias('grade_items'); 94 $user = $this->get_table_alias('user'); 95 96 // Completed column. 97 $columns[] = (new column( 98 'completed', 99 new lang_string('completed', 'completion'), 100 $this->get_entity_name() 101 )) 102 ->add_joins($this->get_joins()) 103 ->set_type(column::TYPE_BOOLEAN) 104 ->add_field("CASE WHEN {$coursecompletion}.timecompleted > 0 THEN 1 ELSE 0 END", 'completed') 105 ->add_field("{$user}.id", 'userid') 106 ->set_is_sortable(true) 107 ->add_callback(static function(bool $value, stdClass $row): string { 108 if (!$row->userid) { 109 return ''; 110 } 111 return format::boolean_as_text($value); 112 }); 113 114 // Progress percentage column. 115 $columns[] = (new column( 116 'progresspercent', 117 new lang_string('progress', 'completion'), 118 $this->get_entity_name() 119 )) 120 ->add_joins($this->get_joins()) 121 ->set_type(column::TYPE_TEXT) 122 ->add_field("{$course}.id", 'courseid') 123 ->add_field("{$user}.id", 'userid') 124 ->set_is_sortable(false) 125 ->add_callback([completion_formatter::class, 'completion_progress']); 126 127 // Time enrolled. 128 $columns[] = (new column( 129 'timeenrolled', 130 new lang_string('timeenrolled', 'enrol'), 131 $this->get_entity_name() 132 )) 133 ->add_joins($this->get_joins()) 134 ->set_type(column::TYPE_TIMESTAMP) 135 ->add_field("{$coursecompletion}.timeenrolled") 136 ->set_is_sortable(true) 137 ->add_callback([format::class, 'userdate']); 138 139 // Time started. 140 $columns[] = (new column( 141 'timestarted', 142 new lang_string('timestarted', 'enrol'), 143 $this->get_entity_name() 144 )) 145 ->add_joins($this->get_joins()) 146 ->set_type(column::TYPE_TIMESTAMP) 147 ->add_field("{$coursecompletion}.timestarted") 148 ->set_is_sortable(true) 149 ->add_callback([format::class, 'userdate']); 150 151 // Time completed. 152 $columns[] = (new column( 153 'timecompleted', 154 new lang_string('timecompleted', 'completion'), 155 $this->get_entity_name() 156 )) 157 ->add_joins($this->get_joins()) 158 ->set_type(column::TYPE_TIMESTAMP) 159 ->add_field("{$coursecompletion}.timecompleted") 160 ->set_is_sortable(true) 161 ->add_callback([format::class, 'userdate']); 162 163 // Time reaggregated. 164 $columns[] = (new column( 165 'reaggregate', 166 new lang_string('timereaggregated', 'enrol'), 167 $this->get_entity_name() 168 )) 169 ->add_joins($this->get_joins()) 170 ->set_type(column::TYPE_TIMESTAMP) 171 ->add_field("{$coursecompletion}.reaggregate") 172 ->set_is_sortable(true) 173 ->add_callback([format::class, 'userdate']); 174 175 // Days taking course (days since course start date until completion or until current date if not completed). 176 $currenttime = time(); 177 $columns[] = (new column( 178 'dayscourse', 179 new lang_string('daystakingcourse', 'course'), 180 $this->get_entity_name() 181 )) 182 ->add_joins($this->get_joins()) 183 ->set_type(column::TYPE_INTEGER) 184 ->add_field("( 185 CASE 186 WHEN {$coursecompletion}.timecompleted > 0 THEN 187 {$coursecompletion}.timecompleted 188 ELSE 189 {$currenttime} 190 END - {$course}.startdate) / " . DAYSECS, 'dayscourse') 191 ->add_field("{$user}.id", 'userid') 192 ->set_is_sortable(true) 193 ->add_callback([completion_formatter::class, 'get_days']); 194 195 // Days since last completion (days since last enrolment date until completion or until current date if not completed). 196 $columns[] = (new column( 197 'daysuntilcompletion', 198 new lang_string('daysuntilcompletion', 'completion'), 199 $this->get_entity_name() 200 )) 201 ->add_joins($this->get_joins()) 202 ->set_type(column::TYPE_INTEGER) 203 ->add_field("( 204 CASE 205 WHEN {$coursecompletion}.timecompleted > 0 THEN 206 {$coursecompletion}.timecompleted 207 ELSE 208 {$currenttime} 209 END - {$coursecompletion}.timeenrolled) / " . DAYSECS, 'daysuntilcompletion') 210 ->add_field("{$user}.id", 'userid') 211 ->set_is_sortable(true) 212 ->add_callback([completion_formatter::class, 'get_days']); 213 214 // Student course grade. 215 $columns[] = (new column( 216 'grade', 217 new lang_string('gradenoun'), 218 $this->get_entity_name() 219 )) 220 ->add_joins($this->get_joins()) 221 ->add_join(" 222 LEFT JOIN {grade_items} {$gradeitem} 223 ON ({$gradeitem}.itemtype = 'course' AND {$course}.id = {$gradeitem}.courseid) 224 ") 225 ->add_join(" 226 LEFT JOIN {grade_grades} {$grade} 227 ON ({$user}.id = {$grade}.userid AND {$gradeitem}.id = {$grade}.itemid) 228 ") 229 ->set_type(column::TYPE_FLOAT) 230 ->add_fields("{$grade}.finalgrade") 231 ->set_is_sortable(true) 232 ->add_callback(function(?float $value): string { 233 if ($value === null) { 234 return ''; 235 } 236 return format_float($value, 2); 237 }); 238 239 return $columns; 240 } 241 242 /** 243 * Return list of all available filters 244 * 245 * @return filter[] 246 */ 247 protected function get_all_filters(): array { 248 $coursecompletion = $this->get_table_alias('course_completion'); 249 250 // Completed status filter. 251 $filters[] = (new filter( 252 boolean_select::class, 253 'completed', 254 new lang_string('completed', 'completion'), 255 $this->get_entity_name(), 256 "CASE WHEN {$coursecompletion}.timecompleted > 0 THEN 1 ELSE 0 END" 257 )) 258 ->add_joins($this->get_joins()); 259 260 // Time completed filter. 261 $filters[] = (new filter( 262 date::class, 263 'timecompleted', 264 new lang_string('timecompleted', 'completion'), 265 $this->get_entity_name(), 266 "{$coursecompletion}.timecompleted" 267 )) 268 ->add_joins($this->get_joins()) 269 ->set_limited_operators([ 270 date::DATE_ANY, 271 date::DATE_NOT_EMPTY, 272 date::DATE_EMPTY, 273 date::DATE_RANGE, 274 date::DATE_LAST, 275 date::DATE_CURRENT, 276 ]); 277 278 return $filters; 279 } 280 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body