MDL-37250 mod_lesson: All actual attempts on the lesson are displayed.
[moodle.git] / mod / lesson / report.php
CommitLineData
86342d63 1<?php
2e796a16 2
0a4abb73
SH
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.
14//
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/>.
17
8d1b4ee7 18/**
19 * Displays the lesson statistics.
20 *
9b24f68b 21 * @package mod_lesson
cc3dbaaa
PS
22 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or late
8d1b4ee7 24 **/
37eeba77 25
0a4abb73
SH
26require_once('../../config.php');
27require_once($CFG->dirroot.'/mod/lesson/locallib.php');
0a3c1f79 28require_once($CFG->dirroot.'/mod/lesson/pagetypes/branchtable.php'); // Needed for constant.
0a4abb73
SH
29
30$id = required_param('id', PARAM_INT); // Course Module ID
1b586516 31$pageid = optional_param('pageid', null, PARAM_INT); // Lesson Page ID
2e796a16 32$action = optional_param('action', 'reportoverview', PARAM_ALPHA); // action to take
0a4abb73
SH
33$nothingtodisplay = false;
34
d85c8399 35$cm = get_coursemodule_from_id('lesson', $id, 0, false, MUST_EXIST);
74df2951 36$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
b3daa926
PS
37$lesson = new lesson($DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST));
38
0a4abb73 39require_login($course, false, $cm);
37eeba77 40
fdc790fc
GF
41$currentgroup = groups_get_activity_group($cm, true);
42
5918e371 43$context = context_module::instance($cm->id);
b69d512a 44require_capability('mod/lesson:viewreports', $context);
37eeba77 45
2e796a16 46$url = new moodle_url('/mod/lesson/report.php', array('id'=>$id));
02ff22e9 47$url->param('action', $action);
1b586516 48if ($pageid !== null) {
0a4abb73
SH
49 $url->param('pageid', $pageid);
50}
51$PAGE->set_url($url);
c165ce9f
RW
52if ($action == 'reportoverview') {
53 $PAGE->navbar->add(get_string('reports', 'lesson'));
54 $PAGE->navbar->add(get_string('overview', 'lesson'));
55}
56
649cf95d 57$lessonoutput = $PAGE->get_renderer('mod_lesson');
37eeba77 58
19735816 59if ($action === 'delete') {
2e796a16 60 /// Process any form data before fetching attempts, grades and times
19735816 61 if (has_capability('mod/lesson:edit', $context) and $form = data_submitted() and confirm_sesskey()) {
2e796a16 62 /// Cycle through array of userids with nested arrays of tries
19735816
PS
63 if (!empty($form->attempts)) {
64 foreach ($form->attempts as $userid => $tries) {
65 // Modifier IS VERY IMPORTANT! What does it do?
66 // Well, it is for when you delete multiple attempts for the same user.
67 // If you delete try 1 and 3 for a user, then after deleting try 1, try 3 then
68 // becomes try 2 (because try 1 is gone and all tries after try 1 get decremented).
69 // So, the modifier makes sure that the submitted try refers to the current try in the
2e796a16 70 // database - hope this all makes sense :)
19735816
PS
71 $modifier = 0;
72
73 foreach ($tries as $try => $junk) {
74 $try -= $modifier;
75
2e796a16 76 /// Clean up the timer table by removing using the order - this is silly, it should be linked to specific attempt (skodak)
19735816
PS
77 $params = array ("userid" => $userid, "lessonid" => $lesson->id);
78 $timers = $DB->get_records_sql("SELECT id FROM {lesson_timer}
79 WHERE userid = :userid AND lessonid = :lessonid
80 ORDER BY starttime", $params, $try, 1);
81 if ($timers) {
82 $timer = reset($timers);
83 $DB->delete_records('lesson_timer', array('id' => $timer->id));
84 }
85
bb685cf3 86 // Remove the grade from the grades tables - this is silly, it should be linked to specific attempt (skodak).
19735816
PS
87 $grades = $DB->get_records_sql("SELECT id FROM {lesson_grades}
88 WHERE userid = :userid AND lessonid = :lessonid
89 ORDER BY completed", $params, $try, 1);
90
91 if ($grades) {
92 $grade = reset($grades);
93 $DB->delete_records('lesson_grades', array('id' => $grade->id));
19735816
PS
94 }
95
2e796a16 96 /// Remove attempts and update the retry number
19735816 97 $DB->delete_records('lesson_attempts', array('userid' => $userid, 'lessonid' => $lesson->id, 'retry' => $try));
2e796a16 98 $DB->execute("UPDATE {lesson_attempts} SET retry = retry - 1 WHERE userid = ? AND lessonid = ? AND retry > ?", array($userid, $lesson->id, $try));
19735816 99
2e796a16 100 /// Remove seen branches and update the retry number
19735816 101 $DB->delete_records('lesson_branch', array('userid' => $userid, 'lessonid' => $lesson->id, 'retry' => $try));
2e796a16 102 $DB->execute("UPDATE {lesson_branch} SET retry = retry - 1 WHERE userid = ? AND lessonid = ? AND retry > ?", array($userid, $lesson->id, $try));
19735816 103
2e796a16 104 /// update central gradebook
19735816
PS
105 lesson_update_grades($lesson, $userid);
106
107 $modifier++;
108 }
109 }
110 }
111 }
2e796a16 112 redirect(new moodle_url($PAGE->url, array('action'=>'reportoverview')));
19735816
PS
113
114} else if ($action === 'reportoverview') {
115 /**************************************************************************
116 this action is for default view and overview view
117 **************************************************************************/
2835eee6 118
0a3c1f79
SB
119 // Count the number of branch and question pages in this lesson.
120 $branchcount = $DB->count_records('lesson_pages', array('lessonid' => $lesson->id, 'qtype' => LESSON_PAGE_BRANCHTABLE));
121 $questioncount = ($DB->count_records('lesson_pages', array('lessonid' => $lesson->id)) - $branchcount);
122
2835eee6 123 // Only load students if there attempts for this lesson.
0a3c1f79
SB
124 $attempts = $DB->record_exists('lesson_attempts', array('lessonid' => $lesson->id));
125 $branches = $DB->record_exists('lesson_branch', array('lessonid' => $lesson->id));
681f8f09
AG
126 $timer = $DB->record_exists('lesson_timer', array('lessonid' => $lesson->id));
127 if ($attempts or $branches or $timer) {
2835eee6
RT
128 list($esql, $params) = get_enrolled_sql($context, '', $currentgroup, true);
129 list($sort, $sortparams) = users_order_by_sql('u');
130
131 $params['lessonid'] = $lesson->id;
132 $ufields = user_picture::fields('u');
133 $sql = "SELECT DISTINCT $ufields
0a3c1f79 134 FROM {user} u
681f8f09
AG
135 JOIN (
136 SELECT userid, lessonid FROM {lesson_attempts} a1
137 UNION
138 SELECT userid, lessonid FROM {lesson_branch} b1
139 UNION
140 SELECT userid, lessonid FROM {lesson_timer} c1
141 ) a ON u.id = a.userid
0a3c1f79
SB
142 JOIN ($esql) ue ON ue.id = a.userid
143 WHERE a.lessonid = :lessonid
144 ORDER BY $sort";
2835eee6
RT
145
146 $students = $DB->get_recordset_sql($sql, $params);
147 if (!$students->valid()) {
0a3c1f79 148 $students->close();
2835eee6
RT
149 $nothingtodisplay = true;
150 }
151 } else {
152 $nothingtodisplay = true;
153 }
154
155 if ($nothingtodisplay) {
156 echo $lessonoutput->header($lesson, $cm, $action, false, null, get_string('nolessonattempts', 'lesson'));
157 if (!empty($currentgroup)) {
158 $groupname = groups_get_group_name($currentgroup);
159 echo $OUTPUT->notification(get_string('nolessonattemptsgroup', 'lesson', $groupname));
160 } else {
161 echo $OUTPUT->notification(get_string('nolessonattempts', 'lesson'));
162 }
163 groups_print_activity_menu($cm, $url);
164 echo $OUTPUT->footer();
165 exit();
166 }
167
2835eee6
RT
168 if (! $grades = $DB->get_records('lesson_grades', array('lessonid' => $lesson->id), 'completed')) {
169 $grades = array();
170 }
171
172 if (! $times = $DB->get_records('lesson_timer', array('lessonid' => $lesson->id), 'starttime')) {
173 $times = array();
174 }
175
d42bc7dc 176 echo $lessonoutput->header($lesson, $cm, $action, false, null, get_string('overview', 'lesson'));
fdc790fc 177 groups_print_activity_menu($cm, $url);
19735816 178
2e796a16
DP
179 $course_context = context_course::instance($course->id);
180 if (has_capability('gradereport/grader:view', $course_context) && has_capability('moodle/grade:viewall', $course_context)) {
181 $seeallgradeslink = new moodle_url('/grade/report/grader/index.php', array('id'=>$course->id));
19735816
PS
182 $seeallgradeslink = html_writer::link($seeallgradeslink, get_string('seeallcoursegrades', 'grades'));
183 echo $OUTPUT->box($seeallgradeslink, 'allcoursegrades');
184 }
185
974053b9 186 // Build an array for output.
0a4abb73
SH
187 $studentdata = array();
188
974053b9 189 $attempts = $DB->get_recordset('lesson_attempts', array('lessonid' => $lesson->id), 'timeseen');
2e796a16
DP
190 foreach ($attempts as $attempt) {
191 // if the user is not in the array or if the retry number is not in the sub array, add the data for that try.
974053b9 192 if (empty($studentdata[$attempt->userid]) || empty($studentdata[$attempt->userid][$attempt->retry])) {
2e796a16
DP
193 // restore/setup defaults
194 $n = 0;
195 $timestart = 0;
196 $timeend = 0;
197 $usergrade = null;
0a3c1f79 198 $eol = false;
2e796a16
DP
199
200 // search for the grade record for this try. if not there, the nulls defined above will be used.
201 foreach($grades as $grade) {
202 // check to see if the grade matches the correct user
203 if ($grade->userid == $attempt->userid) {
204 // see if n is = to the retry
205 if ($n == $attempt->retry) {
206 // get grade info
207 $usergrade = round($grade->grade, 2); // round it here so we only have to do it once
208 break;
209 }
210 $n++; // if not equal, then increment n
211 }
212 }
213 $n = 0;
214 // search for the time record for this try. if not there, the nulls defined above will be used.
215 foreach($times as $time) {
216 // check to see if the grade matches the correct user
217 if ($time->userid == $attempt->userid) {
218 // see if n is = to the retry
219 if ($n == $attempt->retry) {
220 // get grade info
221 $timeend = $time->lessontime;
222 $timestart = $time->starttime;
0a3c1f79 223 $eol = $time->completed;
2e796a16
DP
224 break;
225 }
226 $n++; // if not equal, then increment n
227 }
228 }
0a4abb73 229
2e796a16
DP
230 // build up the array.
231 // this array represents each student and all of their tries at the lesson
232 $studentdata[$attempt->userid][$attempt->retry] = array( "timestart" => $timestart,
233 "timeend" => $timeend,
234 "grade" => $usergrade,
0a3c1f79 235 "end" => $eol,
2e796a16
DP
236 "try" => $attempt->retry,
237 "userid" => $attempt->userid);
ec81373f 238 }
0a4abb73 239 }
2e796a16 240 $attempts->close();
974053b9
DM
241
242 $branches = $DB->get_recordset('lesson_branch', array('lessonid' => $lesson->id), 'timeseen');
0a3c1f79
SB
243 foreach ($branches as $branch) {
244 // If the user is not in the array or if the retry number is not in the sub array, add the data for that try.
974053b9 245 if (empty($studentdata[$branch->userid]) || empty($studentdata[$branch->userid][$branch->retry])) {
0a3c1f79
SB
246 // Restore/setup defaults.
247 $n = 0;
248 $timestart = 0;
249 $timeend = 0;
250 $usergrade = null;
251 $eol = false;
252 // Search for the time record for this try. if not there, the nulls defined above will be used.
253 foreach ($times as $time) {
254 // Check to see if the grade matches the correct user.
255 if ($time->userid == $branch->userid) {
256 // See if n is = to the retry.
257 if ($n == $branch->retry) {
258 // Get grade info.
259 $timeend = $time->lessontime;
260 $timestart = $time->starttime;
261 $eol = $time->completed;
262 break;
263 }
264 $n++; // If not equal, then increment n.
265 }
266 }
267
268 // Build up the array.
269 // This array represents each student and all of their tries at the lesson.
270 $studentdata[$branch->userid][$branch->retry] = array( "timestart" => $timestart,
271 "timeend" => $timeend,
272 "grade" => $usergrade,
273 "end" => $eol,
274 "try" => $branch->retry,
275 "userid" => $branch->userid);
276 }
277 }
278 $branches->close();
279
681f8f09
AG
280 // Need the same thing for timed entries that were not completed.
281 foreach ($times as $time) {
282 $endoflesson = $time->completed;
283 // If the time start is the same with another record then we shouldn't be adding another item to this array.
284 if (isset($studentdata[$time->userid])) {
285 $foundmatch = false;
286 $n = 0;
287 foreach ($studentdata[$time->userid] as $key => $value) {
288 if ($value['timestart'] == $time->starttime) {
289 // Don't add this to the array.
290 $foundmatch = true;
291 break;
292 }
293 }
294 $n = count($studentdata[$time->userid]) + 1;
295 if (!$foundmatch) {
296 // Add a record.
297 $studentdata[$time->userid][] = array(
298 "timestart" => $time->starttime,
299 "timeend" => $time->lessontime,
300 "grade" => null,
301 "end" => $endoflesson,
302 "try" => $n,
303 "userid" => $time->userid
304 );
305 }
306 } else {
307 $studentdata[$time->userid][] = array(
308 "timestart" => $time->starttime,
309 "timeend" => $time->lessontime,
310 "grade" => null,
311 "end" => $endoflesson,
312 "try" => 0,
313 "userid" => $time->userid
314 );
315 }
316 }
0a3c1f79
SB
317 // Determine if lesson should have a score.
318 if ($branchcount > 0 AND $questioncount == 0) {
319 // This lesson only contains content pages and is not graded.
320 $lessonscored = false;
321 } else {
322 // This lesson is graded.
323 $lessonscored = true;
324 }
2e796a16 325 // set all the stats variables
0a4abb73
SH
326 $numofattempts = 0;
327 $avescore = 0;
328 $avetime = 0;
1b586516
RT
329 $highscore = null;
330 $lowscore = null;
331 $hightime = null;
332 $lowtime = null;
0a4abb73
SH
333
334 $table = new html_table();
335
0a3c1f79
SB
336 // Set up the table object.
337 if ($lessonscored) {
338 $table->head = array(get_string('name'), get_string('attempts', 'lesson'), get_string('highscore', 'lesson'));
339 } else {
340 $table->head = array(get_string('name'), get_string('attempts', 'lesson'));
341 }
16be8974
DM
342 $table->align = array('center', 'left', 'left');
343 $table->wrap = array('nowrap', 'nowrap', 'nowrap');
344 $table->attributes['class'] = 'standardtable generaltable';
345 $table->size = array(null, '70%', null);
0a4abb73 346
2e796a16
DP
347 // print out the $studentdata array
348 // going through each student that has attempted the lesson, so, each student should have something to be displayed
349 foreach ($students as $student) {
350 // check to see if the student has attempts to print out
351 if (array_key_exists($student->id, $studentdata)) {
352 // set/reset some variables
353 $attempts = array();
354 // gather the data for each user attempt
355 $bestgrade = 0;
356 $bestgradefound = false;
357 // $tries holds all the tries/retries a student has done
358 $tries = $studentdata[$student->id];
359 $studentname = fullname($student, true);
360 foreach ($tries as $try) {
361 // start to build up the checkbox and link
362 if (has_capability('mod/lesson:edit', $context)) {
363 $temp = '<input type="checkbox" id="attempts" name="attempts['.$try['userid'].']['.$try['try'].']" /> ';
364 } else {
365 $temp = '';
366 }
0a4abb73 367
2e796a16
DP
368 $temp .= "<a href=\"report.php?id=$cm->id&amp;action=reportdetail&amp;userid=".$try['userid']
369 .'&amp;try='.$try['try'].'" class="lesson-attempt-link">';
370 if ($try["grade"] !== null) { // if null then not done yet
371 // this is what the link does when the user has completed the try
372 $timetotake = $try["timeend"] - $try["timestart"];
4ac4a9cc 373
2e796a16
DP
374 $temp .= $try["grade"]."%";
375 $bestgradefound = true;
376 if ($try["grade"] > $bestgrade) {
377 $bestgrade = $try["grade"];
378 }
0a4abb73
SH
379 $temp .= "&nbsp;".userdate($try["timestart"]);
380 $temp .= ",&nbsp;(".format_time($timetotake).")</a>";
2e796a16 381 } else {
0a3c1f79
SB
382 if ($try["end"]) {
383 // User finished the lesson but has no grade. (Happens when there are only content pages).
384 $temp .= "&nbsp;".userdate($try["timestart"]);
385 $timetotake = $try["timeend"] - $try["timestart"];
386 $temp .= ",&nbsp;(".format_time($timetotake).")</a>";
387 } else {
388 // This is what the link does/looks like when the user has not completed the attempt.
389 $temp .= get_string("notcompleted", "lesson");
390 if ($try['timestart'] !== 0) {
391 // Teacher previews do not track time spent.
392 $temp .= "&nbsp;".userdate($try["timestart"]);
393 }
394 $temp .= "</a>";
395 $timetotake = null;
396 }
0a4abb73 397 }
2e796a16
DP
398 // build up the attempts array
399 $attempts[] = $temp;
400
0a3c1f79
SB
401 // Run these lines for the stats only if the user finnished the lesson.
402 if ($try["end"]) {
403 // User has completed the lesson.
2e796a16 404 $numofattempts++;
2e796a16 405 $avetime += $timetotake;
2e796a16
DP
406 if ($timetotake > $hightime || $hightime == null) {
407 $hightime = $timetotake;
408 }
409 if ($timetotake < $lowtime || $lowtime == null) {
410 $lowtime = $timetotake;
411 }
0a3c1f79
SB
412 if ($try["grade"] !== null) {
413 // The lesson was scored.
414 $avescore += $try["grade"];
415 if ($try["grade"] > $highscore || $highscore === null) {
416 $highscore = $try["grade"];
417 }
418 if ($try["grade"] < $lowscore || $lowscore === null) {
419 $lowscore = $try["grade"];
420 }
421
422 }
ec81373f 423 }
ec81373f 424 }
2e796a16
DP
425 // get line breaks in after each attempt
426 $attempts = implode("<br />\n", $attempts);
0a3c1f79
SB
427
428 if ($lessonscored) {
429 // Add the grade if the lesson is graded.
430 $bestgrade = $bestgrade."%";
431 $table->data[] = array($studentname, $attempts, $bestgrade);
432 } else {
433 // This lesson does not have a grade.
434 $table->data[] = array($studentname, $attempts);
435 }
ec81373f 436 }
0a4abb73 437 }
2e796a16 438 $students->close();
0a3c1f79 439 // Print it all out!
0a4abb73
SH
440 if (has_capability('mod/lesson:edit', $context)) {
441 echo "<form id=\"theform\" method=\"post\" action=\"report.php\">\n
442 <input type=\"hidden\" name=\"sesskey\" value=\"".sesskey()."\" />\n
443 <input type=\"hidden\" name=\"id\" value=\"$cm->id\" />\n";
444 }
16be8974 445 echo html_writer::table($table);
0a4abb73
SH
446 if (has_capability('mod/lesson:edit', $context)) {
447 $checklinks = '<a href="javascript: checkall();">'.get_string('selectall').'</a> / ';
edc28287 448 $checklinks .= '<a href="javascript: checknone();">'.get_string('deselectall').'</a>';
68ca508f 449 $checklinks .= html_writer::label('action', 'menuaction', false, array('class' => 'accesshide'));
2e796a16 450 $checklinks .= html_writer::select(array('delete' => get_string('deleteselected')), 'action', 0, array(''=>'choosedots'), array('id'=>'actionid', 'class' => 'autosubmit'));
7266bd3e
ARN
451 $PAGE->requires->yui_module('moodle-core-formautosubmit',
452 'M.core.init_formautosubmit',
453 array(array('selectid' => 'actionid', 'nothing' => false))
454 );
edc28287 455 echo $OUTPUT->box($checklinks, 'center');
0a4abb73
SH
456 echo '</form>';
457 }
86342d63 458
0a3c1f79 459 // Calculate the Statistics.
1b586516 460 if ($avetime == null) {
0a4abb73
SH
461 $avetime = get_string("notcompleted", "lesson");
462 } else {
2e796a16 463 $avetime = format_float($avetime/$numofattempts, 0);
0a4abb73
SH
464 $avetime = format_time($avetime);
465 }
1b586516 466 if ($hightime == null) {
0a4abb73
SH
467 $hightime = get_string("notcompleted", "lesson");
468 } else {
469 $hightime = format_time($hightime);
470 }
1b586516 471 if ($lowtime == null) {
0a4abb73
SH
472 $lowtime = get_string("notcompleted", "lesson");
473 } else {
474 $lowtime = format_time($lowtime);
475 }
ec81373f 476
0a3c1f79
SB
477 if ($lessonscored) {
478 if ($numofattempts == 0) {
479 $avescore = get_string("notcompleted", "lesson");
480 } else {
481 $avescore = format_float($avescore / $numofattempts, 2) . '%';
482 }
483 if ($highscore === null) {
484 $highscore = get_string("notcompleted", "lesson");
485 } else {
486 $highscore .= '%';
487 }
488 if ($lowscore === null) {
489 $lowscore = get_string("notcompleted", "lesson");
490 } else {
491 $lowscore .= '%';
492 }
493
494 // Display the full stats for the lesson.
495 echo $OUTPUT->heading(get_string('lessonstats', 'lesson'), 3);
496 $stattable = new html_table();
497 $stattable->head = array(get_string('averagescore', 'lesson'), get_string('averagetime', 'lesson'),
498 get_string('highscore', 'lesson'), get_string('lowscore', 'lesson'),
499 get_string('hightime', 'lesson'), get_string('lowtime', 'lesson'));
500 $stattable->align = array('center', 'center', 'center', 'center', 'center', 'center');
501 $stattable->wrap = array('nowrap', 'nowrap', 'nowrap', 'nowrap', 'nowrap', 'nowrap');
502 $stattable->attributes['class'] = 'standardtable generaltable';
503 $stattable->data[] = array($avescore, $avetime, $highscore, $lowscore, $hightime, $lowtime);
504
505 } else {
506 // Display simple stats for the lesson.
507 echo $OUTPUT->heading(get_string('lessonstats', 'lesson'), 3);
508 $stattable = new html_table();
509 $stattable->head = array(get_string('averagetime', 'lesson'), get_string('hightime', 'lesson'),
510 get_string('lowtime', 'lesson'));
511 $stattable->align = array('center', 'center', 'center');
512 $stattable->wrap = array('nowrap', 'nowrap', 'nowrap');
513 $stattable->attributes['class'] = 'standardtable generaltable';
514 $stattable->data[] = array($avetime, $hightime, $lowtime);
8b8d7fcd 515 }
0a4abb73 516
16be8974 517 echo html_writer::table($stattable);
19735816 518} else if ($action === 'reportdetail') {
ec81373f 519 /**************************************************************************
520 this action is for a student detailed view and for the general detailed view
521
0a4abb73
SH
522 General flow of this section of the code
523 1. Generate a object which holds values for the statistics for each question/answer
524 2. Cycle through all the pages to create a object. Foreach page, see if the student actually answered
525 the page. Then process the page appropriatly. Display all info about the question,
526 Highlight correct answers, show how the user answered the question, and display statistics
527 about each page
528 3. Print out info about the try (if needed)
529 4. Print out the object which contains all the try info
530
2e796a16 531**************************************************************************/
d43d3418 532 echo $lessonoutput->header($lesson, $cm, $action, false, null, get_string('detailedstats', 'lesson'));
fdc790fc 533 groups_print_activity_menu($cm, $url);
19735816 534
2e796a16
DP
535 $course_context = context_course::instance($course->id);
536 if (has_capability('gradereport/grader:view', $course_context) && has_capability('moodle/grade:viewall', $course_context)) {
537 $seeallgradeslink = new moodle_url('/grade/report/grader/index.php', array('id'=>$course->id));
19735816
PS
538 $seeallgradeslink = html_writer::link($seeallgradeslink, get_string('seeallcoursegrades', 'grades'));
539 echo $OUTPUT->box($seeallgradeslink, 'allcoursegrades');
540 }
541
0a4abb73 542 $formattextdefoptions = new stdClass;
2e796a16 543 $formattextdefoptions->para = false; //I'll use it widely in this page
367a75fa 544 $formattextdefoptions->overflowdiv = true;
0a4abb73 545
2e796a16 546 $userid = optional_param('userid', null, PARAM_INT); // if empty, then will display the general detailed view
1b586516 547 $try = optional_param('try', null, PARAM_INT);
0a4abb73 548
e0e1a83e
JMV
549 if (!empty($userid)) {
550 // Apply overrides.
551 $lesson->update_effective_access($userid);
552 }
553
0a4abb73
SH
554 $lessonpages = $lesson->load_all_pages();
555 foreach ($lessonpages as $lessonpage) {
556 if ($lessonpage->prevpageid == 0) {
557 $pageid = $lessonpage->id;
ec81373f 558 }
0a4abb73 559 }
f6e200cb 560
2e796a16 561 // now gather the stats into an object
0a4abb73
SH
562 $firstpageid = $pageid;
563 $pagestats = array();
2e796a16 564 while ($pageid != 0) { // EOL
0a4abb73
SH
565 $page = $lessonpages[$pageid];
566 $params = array ("lessonid" => $lesson->id, "pageid" => $page->id);
2e796a16
DP
567 if ($allanswers = $DB->get_records_select("lesson_attempts", "lessonid = :lessonid AND pageid = :pageid", $params, "timeseen")) {
568 // get them ready for processing
0a4abb73
SH
569 $orderedanswers = array();
570 foreach ($allanswers as $singleanswer) {
2e796a16 571 // ordering them like this, will help to find the single attempt record that we want to keep.
0a4abb73
SH
572 $orderedanswers[$singleanswer->userid][$singleanswer->retry][] = $singleanswer;
573 }
2e796a16 574 // this is foreach user and for each try for that user, keep one attempt record
0a4abb73 575 foreach ($orderedanswers as $orderedanswer) {
2e796a16 576 foreach($orderedanswer as $tries) {
0a4abb73 577 $page->stats($pagestats, $tries);
ec81373f 578 }
ec81373f 579 }
2e796a16
DP
580 } else {
581 // no one answered yet...
ec81373f 582 }
2e796a16 583 //unset($orderedanswers); initialized above now
0a4abb73
SH
584 $pageid = $page->nextpageid;
585 }
ec81373f 586
0a4abb73
SH
587 $manager = lesson_page_type_manager::get($lesson);
588 $qtypes = $manager->get_page_type_strings();
589
590 $answerpages = array();
591 $answerpage = "";
592 $pageid = $firstpageid;
2e796a16
DP
593 // cycle through all the pages
594 // foreach page, add to the $answerpages[] array all the data that is needed
595 // from the question, the users attempt, and the statistics
0a4abb73 596 // grayout pages that the user did not answer and Branch, end of branch, cluster
2e796a16
DP
597 // and end of cluster pages
598 while ($pageid != 0) { // EOL
0a4abb73
SH
599 $page = $lessonpages[$pageid];
600 $answerpage = new stdClass;
2e796a16 601 $data ='';
d42bc7dc 602
0a4abb73 603 $answerdata = new stdClass;
2a03a824 604 // Set some defaults for the answer data.
1b586516
RT
605 $answerdata->score = null;
606 $answerdata->response = null;
2a03a824 607 $answerdata->responseformat = FORMAT_PLAIN;
0a4abb73
SH
608
609 $answerpage->title = format_string($page->title);
610
611 $options = new stdClass;
612 $options->noclean = true;
367a75fa 613 $options->overflowdiv = true;
59f1c9f5 614 $options->context = $context;
01c37ef1 615 $answerpage->contents = format_text($page->contents, $page->contentsformat, $options);
0a4abb73
SH
616
617 $answerpage->qtype = $qtypes[$page->qtype].$page->option_description_string();
618 $answerpage->grayout = $page->grayout;
d85c8399 619 $answerpage->context = $context;
0a4abb73
SH
620
621 if (empty($userid)) {
2e796a16 622 // there is no userid, so set these vars and display stats.
0a4abb73 623 $answerpage->grayout = 0;
1b586516 624 $useranswer = null;
2e796a16
DP
625 } elseif ($useranswers = $DB->get_records("lesson_attempts",array("lessonid"=>$lesson->id, "userid"=>$userid, "retry"=>$try,"pageid"=>$page->id), "timeseen")) {
626 // get the user's answer for this page
627 // need to find the right one
0a4abb73
SH
628 $i = 0;
629 foreach ($useranswers as $userattempt) {
630 $useranswer = $userattempt;
631 $i++;
632 if ($lesson->maxattempts == $i) {
2e796a16 633 break; // reached maxattempts, break out
0a4abb73 634 }
ec81373f 635 }
0a4abb73 636 } else {
2e796a16 637 // user did not answer this page, gray it out and set some nulls
0a4abb73 638 $answerpage->grayout = 1;
1b586516 639 $useranswer = null;
0a4abb73
SH
640 }
641 $i = 0;
642 $n = 0;
643 $answerpages[] = $page->report_answers(clone($answerpage), clone($answerdata), $useranswer, $pagestats, $i, $n);
644 $pageid = $page->nextpageid;
645 }
ec81373f 646
2e796a16 647 /// actually start printing something
0a4abb73
SH
648 $table = new html_table();
649 $table->wrap = array();
650 $table->width = "60%";
651 if (!empty($userid)) {
2e796a16
DP
652 // if looking at a students try, print out some basic stats at the top
653
654 // print out users name
655 //$headingobject->lastname = $students[$userid]->lastname;
656 //$headingobject->firstname = $students[$userid]->firstname;
657 //$headingobject->attempt = $try + 1;
658 //print_heading(get_string("studentattemptlesson", "lesson", $headingobject));
659 echo $OUTPUT->heading(get_string('attempt', 'lesson', $try+1), 3);
0a4abb73
SH
660
661 $table->head = array();
16be8974
DM
662 $table->align = array('right', 'left');
663 $table->attributes['class'] = 'compacttable generaltable';
0a4abb73 664
2e796a16
DP
665 $params = array("lessonid"=>$lesson->id, "userid"=>$userid);
666 if (!$grades = $DB->get_records_select("lesson_grades", "lessonid = :lessonid and userid = :userid", $params, "completed", "*", $try, 1)) {
0a4abb73
SH
667 $grade = -1;
668 $completed = -1;
669 } else {
670 $grade = current($grades);
671 $completed = $grade->completed;
672 $grade = round($grade->grade, 2);
673 }
2e796a16 674 if (!$times = $DB->get_records_select("lesson_timer", "lessonid = :lessonid and userid = :userid", $params, "starttime", "*", $try, 1)) {
0a4abb73
SH
675 $timetotake = -1;
676 } else {
677 $timetotake = current($times);
678 $timetotake = $timetotake->lessontime - $timetotake->starttime;
ec81373f 679 }
680
0a4abb73
SH
681 if ($timetotake == -1 || $completed == -1 || $grade == -1) {
682 $table->align = array("center");
ec81373f 683
0a4abb73
SH
684 $table->data[] = array(get_string("notcompleted", "lesson"));
685 } else {
53339437 686 $user = $DB->get_record('user', array('id' => $userid));
ec81373f 687
0a4abb73 688 $gradeinfo = lesson_grade($lesson, $try, $user->id);
86342d63 689
2e796a16 690 $table->data[] = array(get_string('name').':', $OUTPUT->user_picture($user, array('courseid'=>$course->id)).fullname($user, true));
0a4abb73
SH
691 $table->data[] = array(get_string("timetaken", "lesson").":", format_time($timetotake));
692 $table->data[] = array(get_string("completed", "lesson").":", userdate($completed));
693 $table->data[] = array(get_string('rawgrade', 'lesson').':', $gradeinfo->earned.'/'.$gradeinfo->total);
694 $table->data[] = array(get_string("grade", "lesson").":", $grade."%");
695 }
16be8974 696 echo html_writer::table($table);
86342d63 697
2e796a16 698 // Don't want this class for later tables
16be8974 699 $table->attributes['class'] = '';
0a4abb73 700 }
86342d63 701
ec81373f 702
16be8974
DM
703 $table->align = array('left', 'left');
704 $table->size = array('70%', null);
705 $table->attributes['class'] = 'compacttable generaltable';
ec81373f 706
0a4abb73
SH
707 foreach ($answerpages as $page) {
708 unset($table->data);
2e796a16 709 if ($page->grayout) { // set the color of text
0a4abb73
SH
710 $fontstart = "<span class=\"dimmed\">";
711 $fontend = "</font>";
712 $fontstart2 = $fontstart;
713 $fontend2 = $fontend;
714 } else {
715 $fontstart = "";
716 $fontend = "";
717 $fontstart2 = "";
718 $fontend2 = "";
719 }
ec81373f 720
2e796a16
DP
721 $table->head = array($fontstart2.$page->qtype.": ".format_string($page->title).$fontend2, $fontstart2.get_string("classstats", "lesson").$fontend2);
722 $table->data[] = array($fontstart.get_string("question", "lesson").": <br />".$fontend.$fontstart2.$page->contents.$fontend2, " ");
0a4abb73 723 $table->data[] = array($fontstart.get_string("answer", "lesson").":".$fontend, ' ');
2e796a16 724 // apply the font to each answer
5951d333 725 if (!empty($page->answerdata)) {
2e796a16 726 foreach ($page->answerdata->answers as $answer){
ec81373f 727 $modified = array();
728 foreach ($answer as $single) {
2e796a16 729 // need to apply a font to each one
ec81373f 730 $modified[] = $fontstart2.$single.$fontend2;
731 }
732 $table->data[] = $modified;
733 }
5951d333 734 if (isset($page->answerdata->response)) {
0abc18cf
JMV
735 $table->data[] = array($fontstart.get_string("response", "lesson").": <br />".$fontend
736 .$fontstart2.$page->answerdata->response.$fontend2, " ");
ec81373f 737 }
738 $table->data[] = array($page->answerdata->score, " ");
0a4abb73 739 } else {
5951d333 740 $table->data[] = array(get_string('didnotanswerquestion', 'lesson'), " ");
ec81373f 741 }
3266d14b 742 echo html_writer::start_tag('div', array('class' => 'no-overflow'));
16be8974 743 echo html_writer::table($table);
3266d14b 744 echo html_writer::end_tag('div');
ec81373f 745 }
0a4abb73
SH
746} else {
747 print_error('unknowaction');
748}
37eeba77 749
2e796a16 750/// Finish the page
0a4abb73 751echo $OUTPUT->footer();