Moodle release 3.8.4
[moodle.git] / mod / lesson / report.php
blob4fd027a8a4a2858e5a5911130e614c82d311c1ce
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
18 /**
19 * Displays the lesson statistics.
21 * @package mod_lesson
22 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or late
24 **/
26 require_once('../../config.php');
27 require_once($CFG->dirroot.'/mod/lesson/locallib.php');
29 $id = required_param('id', PARAM_INT); // Course Module ID
30 $pageid = optional_param('pageid', null, PARAM_INT); // Lesson Page ID
31 $action = optional_param('action', 'reportoverview', PARAM_ALPHA); // action to take
32 $nothingtodisplay = false;
34 $cm = get_coursemodule_from_id('lesson', $id, 0, false, MUST_EXIST);
35 $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
36 $lesson = new lesson($DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST));
38 require_login($course, false, $cm);
40 $currentgroup = groups_get_activity_group($cm, true);
42 $context = context_module::instance($cm->id);
43 require_capability('mod/lesson:viewreports', $context);
45 $url = new moodle_url('/mod/lesson/report.php', array('id'=>$id));
46 $url->param('action', $action);
47 if ($pageid !== null) {
48 $url->param('pageid', $pageid);
50 $PAGE->set_url($url);
51 if ($action == 'reportoverview') {
52 $PAGE->navbar->add(get_string('reports', 'lesson'));
53 $PAGE->navbar->add(get_string('overview', 'lesson'));
56 $lessonoutput = $PAGE->get_renderer('mod_lesson');
58 if ($action === 'delete') {
59 /// Process any form data before fetching attempts, grades and times
60 if (has_capability('mod/lesson:edit', $context) and $form = data_submitted() and confirm_sesskey()) {
61 /// Cycle through array of userids with nested arrays of tries
62 if (!empty($form->attempts)) {
63 foreach ($form->attempts as $userid => $tries) {
64 // Modifier IS VERY IMPORTANT! What does it do?
65 // Well, it is for when you delete multiple attempts for the same user.
66 // If you delete try 1 and 3 for a user, then after deleting try 1, try 3 then
67 // becomes try 2 (because try 1 is gone and all tries after try 1 get decremented).
68 // So, the modifier makes sure that the submitted try refers to the current try in the
69 // database - hope this all makes sense :)
70 $modifier = 0;
72 foreach ($tries as $try => $junk) {
73 $try -= $modifier;
75 /// Clean up the timer table by removing using the order - this is silly, it should be linked to specific attempt (skodak)
76 $timers = $lesson->get_user_timers($userid, 'starttime', 'id', $try, 1);
77 if ($timers) {
78 $timer = reset($timers);
79 $DB->delete_records('lesson_timer', array('id' => $timer->id));
82 $params = array ("userid" => $userid, "lessonid" => $lesson->id);
83 // Remove the grade from the grades tables - this is silly, it should be linked to specific attempt (skodak).
84 $grades = $DB->get_records_sql("SELECT id FROM {lesson_grades}
85 WHERE userid = :userid AND lessonid = :lessonid
86 ORDER BY completed", $params, $try, 1);
88 if ($grades) {
89 $grade = reset($grades);
90 $DB->delete_records('lesson_grades', array('id' => $grade->id));
93 /// Remove attempts and update the retry number
94 $DB->delete_records('lesson_attempts', array('userid' => $userid, 'lessonid' => $lesson->id, 'retry' => $try));
95 $DB->execute("UPDATE {lesson_attempts} SET retry = retry - 1 WHERE userid = ? AND lessonid = ? AND retry > ?", array($userid, $lesson->id, $try));
97 /// Remove seen branches and update the retry number
98 $DB->delete_records('lesson_branch', array('userid' => $userid, 'lessonid' => $lesson->id, 'retry' => $try));
99 $DB->execute("UPDATE {lesson_branch} SET retry = retry - 1 WHERE userid = ? AND lessonid = ? AND retry > ?", array($userid, $lesson->id, $try));
101 /// update central gradebook
102 lesson_update_grades($lesson, $userid);
104 $modifier++;
109 redirect(new moodle_url($PAGE->url, array('action'=>'reportoverview')));
111 } else if ($action === 'reportoverview') {
112 /**************************************************************************
113 this action is for default view and overview view
114 **************************************************************************/
116 // Get the table and data for build statistics.
117 list($table, $data) = lesson_get_overview_report_table_and_data($lesson, $currentgroup);
119 if ($table === false) {
120 echo $lessonoutput->header($lesson, $cm, $action, false, null, get_string('nolessonattempts', 'lesson'));
121 if (!empty($currentgroup)) {
122 $groupname = groups_get_group_name($currentgroup);
123 echo $OUTPUT->notification(get_string('nolessonattemptsgroup', 'lesson', $groupname));
124 } else {
125 echo $OUTPUT->notification(get_string('nolessonattempts', 'lesson'));
127 groups_print_activity_menu($cm, $url);
128 echo $OUTPUT->footer();
129 exit();
132 echo $lessonoutput->header($lesson, $cm, $action, false, null, get_string('overview', 'lesson'));
133 groups_print_activity_menu($cm, $url);
135 $course_context = context_course::instance($course->id);
136 if (has_capability('gradereport/grader:view', $course_context) && has_capability('moodle/grade:viewall', $course_context)) {
137 $seeallgradeslink = new moodle_url('/grade/report/grader/index.php', array('id'=>$course->id));
138 $seeallgradeslink = html_writer::link($seeallgradeslink, get_string('seeallcoursegrades', 'grades'));
139 echo $OUTPUT->box($seeallgradeslink, 'allcoursegrades');
142 // The attempts table.
143 $attemptstable = html_writer::table($table);
145 // The HTML that we will be displaying which includes the attempts table and bulk actions menu, if necessary.
146 $attemptshtml = $attemptstable;
148 // Show bulk actions when user has capability to edit the lesson.
149 if (has_capability('mod/lesson:edit', $context)) {
150 $reporturl = new moodle_url('/mod/lesson/report.php');
151 $formid = 'mod-lesson-report-form';
153 // Sesskey hidden input.
154 $formcontents = html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()]);
156 // CMID hidden input.
157 $formcontents .= html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'id', 'value' => $cm->id]);
159 // Attempts table.
160 $formcontents .= $attemptstable;
162 // Bulk actions menu.
163 $attemptsactions = [
164 'delete' => get_string('deleteselected')
166 $bulkactions = new single_select($reporturl, 'action', $attemptsactions, '', ['' => 'choosedots'], $formid);
167 $bulkactions->set_label(get_string('withselectedattempts', 'lesson'));
168 $bulkactions->disabled = true;
169 $bulkactions->attributes = [
170 'data-action' => 'toggle',
171 'data-togglegroup' => 'lesson-attempts',
172 'data-toggle' => 'action',
174 $bulkactionshtml = $OUTPUT->render($bulkactions);
175 $formcontents .= $OUTPUT->box($bulkactionshtml, 'center');
177 // Build the attempts form.
178 $formattributes = [
179 'id' => $formid,
180 'method' => 'post',
182 $attemptshtml = html_writer::tag('form', $formcontents, $formattributes);
185 // Show the attempts HTML.
186 echo $attemptshtml;
188 // Calculate the Statistics.
189 if ($data->avetime == null) {
190 $data->avetime = get_string("notcompleted", "lesson");
191 } else {
192 $data->avetime = format_float($data->avetime / $data->numofattempts, 0);
193 $data->avetime = format_time($data->avetime);
195 if ($data->hightime == null) {
196 $data->hightime = get_string("notcompleted", "lesson");
197 } else {
198 $data->hightime = format_time($data->hightime);
200 if ($data->lowtime == null) {
201 $data->lowtime = get_string("notcompleted", "lesson");
202 } else {
203 $data->lowtime = format_time($data->lowtime);
206 if ($data->lessonscored) {
207 if ($data->numofattempts == 0) {
208 $data->avescore = get_string("notcompleted", "lesson");
209 } else {
210 $data->avescore = format_float($data->avescore, 2) . '%';
212 if ($data->highscore === null) {
213 $data->highscore = get_string("notcompleted", "lesson");
214 } else {
215 $data->highscore .= '%';
217 if ($data->lowscore === null) {
218 $data->lowscore = get_string("notcompleted", "lesson");
219 } else {
220 $data->lowscore .= '%';
223 // Display the full stats for the lesson.
224 echo $OUTPUT->heading(get_string('lessonstats', 'lesson'), 3);
225 $stattable = new html_table();
226 $stattable->head = array(get_string('averagescore', 'lesson'), get_string('averagetime', 'lesson'),
227 get_string('highscore', 'lesson'), get_string('lowscore', 'lesson'),
228 get_string('hightime', 'lesson'), get_string('lowtime', 'lesson'));
229 $stattable->align = array('center', 'center', 'center', 'center', 'center', 'center');
230 $stattable->wrap = array('nowrap', 'nowrap', 'nowrap', 'nowrap', 'nowrap', 'nowrap');
231 $stattable->attributes['class'] = 'standardtable generaltable';
232 $stattable->data[] = array($data->avescore, $data->avetime, $data->highscore, $data->lowscore, $data->hightime, $data->lowtime);
234 } else {
235 // Display simple stats for the lesson.
236 echo $OUTPUT->heading(get_string('lessonstats', 'lesson'), 3);
237 $stattable = new html_table();
238 $stattable->head = array(get_string('averagetime', 'lesson'), get_string('hightime', 'lesson'),
239 get_string('lowtime', 'lesson'));
240 $stattable->align = array('center', 'center', 'center');
241 $stattable->wrap = array('nowrap', 'nowrap', 'nowrap');
242 $stattable->attributes['class'] = 'standardtable generaltable';
243 $stattable->data[] = array($data->avetime, $data->hightime, $data->lowtime);
246 echo html_writer::table($stattable);
247 } else if ($action === 'reportdetail') {
248 /**************************************************************************
249 this action is for a student detailed view and for the general detailed view
251 General flow of this section of the code
252 1. Generate a object which holds values for the statistics for each question/answer
253 2. Cycle through all the pages to create a object. Foreach page, see if the student actually answered
254 the page. Then process the page appropriatly. Display all info about the question,
255 Highlight correct answers, show how the user answered the question, and display statistics
256 about each page
257 3. Print out info about the try (if needed)
258 4. Print out the object which contains all the try info
260 **************************************************************************/
261 echo $lessonoutput->header($lesson, $cm, $action, false, null, get_string('detailedstats', 'lesson'));
262 groups_print_activity_menu($cm, $url);
264 $course_context = context_course::instance($course->id);
265 if (has_capability('gradereport/grader:view', $course_context) && has_capability('moodle/grade:viewall', $course_context)) {
266 $seeallgradeslink = new moodle_url('/grade/report/grader/index.php', array('id'=>$course->id));
267 $seeallgradeslink = html_writer::link($seeallgradeslink, get_string('seeallcoursegrades', 'grades'));
268 echo $OUTPUT->box($seeallgradeslink, 'allcoursegrades');
271 $formattextdefoptions = new stdClass;
272 $formattextdefoptions->para = false; //I'll use it widely in this page
273 $formattextdefoptions->overflowdiv = true;
275 $userid = optional_param('userid', null, PARAM_INT); // if empty, then will display the general detailed view
276 $try = optional_param('try', null, PARAM_INT);
278 list($answerpages, $userstats) = lesson_get_user_detailed_report_data($lesson, $userid, $try);
280 /// actually start printing something
281 $table = new html_table();
282 $table->wrap = array();
283 $table->width = "60%";
284 if (!empty($userid)) {
285 // if looking at a students try, print out some basic stats at the top
287 // print out users name
288 //$headingobject->lastname = $students[$userid]->lastname;
289 //$headingobject->firstname = $students[$userid]->firstname;
290 //$headingobject->attempt = $try + 1;
291 //print_heading(get_string("studentattemptlesson", "lesson", $headingobject));
292 echo $OUTPUT->heading(get_string('attempt', 'lesson', $try+1), 3);
294 $table->head = array();
295 $table->align = array('right', 'left');
296 $table->attributes['class'] = 'generaltable';
298 if (empty($userstats->gradeinfo)) {
299 $table->align = array("center");
301 $table->data[] = array(get_string("notcompleted", "lesson"));
302 } else {
303 $user = $DB->get_record('user', array('id' => $userid));
305 $gradeinfo = lesson_grade($lesson, $try, $user->id);
307 $table->data[] = array(get_string('name').':', $OUTPUT->user_picture($user, array('courseid'=>$course->id)).fullname($user, true));
308 $table->data[] = array(get_string("timetaken", "lesson").":", format_time($userstats->timetotake));
309 $table->data[] = array(get_string("completed", "lesson").":", userdate($userstats->completed));
310 $table->data[] = array(get_string('rawgrade', 'lesson').':', $userstats->gradeinfo->earned.'/'.$userstats->gradeinfo->total);
311 $table->data[] = array(get_string("grade", "lesson").":", $userstats->grade."%");
313 echo html_writer::table($table);
315 // Don't want this class for later tables
316 $table->attributes['class'] = '';
319 foreach ($answerpages as $page) {
320 $table->align = array('left', 'left');
321 $table->size = array('70%', null);
322 $table->attributes['class'] = 'generaltable';
323 unset($table->data);
324 if ($page->grayout) { // set the color of text
325 $fontstart = html_writer::start_tag('span', array('class' => 'dimmed_text'));
326 $fontend = html_writer::end_tag('span');
327 $fontstart2 = $fontstart;
328 $fontend2 = $fontend;
329 } else {
330 $fontstart = '';
331 $fontend = '';
332 $fontstart2 = '';
333 $fontend2 = '';
336 $table->head = array($fontstart2.$page->qtype.": ".format_string($page->title).$fontend2, $fontstart2.get_string("classstats", "lesson").$fontend2);
337 $table->data[] = array($fontstart.get_string("question", "lesson").": <br />".$fontend.$fontstart2.$page->contents.$fontend2, " ");
338 $table->data[] = array($fontstart.get_string("answer", "lesson").":".$fontend, ' ');
339 // apply the font to each answer
340 if (!empty($page->answerdata) && !empty($page->answerdata->answers)) {
341 foreach ($page->answerdata->answers as $answer){
342 $modified = array();
343 foreach ($answer as $single) {
344 // need to apply a font to each one
345 $modified[] = $fontstart2.$single.$fontend2;
347 $table->data[] = $modified;
349 if (isset($page->answerdata->response)) {
350 $table->data[] = array($fontstart.get_string("response", "lesson").": <br />".$fontend
351 .$fontstart2.$page->answerdata->response.$fontend2, " ");
353 $table->data[] = array($page->answerdata->score, " ");
354 } else {
355 $table->data[] = array(get_string('didnotanswerquestion', 'lesson'), " ");
357 echo html_writer::start_tag('div', array('class' => 'no-overflow'));
358 echo html_writer::table($table);
359 echo html_writer::end_tag('div');
361 } else {
362 print_error('unknowaction');
365 /// Finish the page
366 echo $OUTPUT->footer();