MDL-37250 mod_lesson: All actual attempts on the lesson are displayed.
[moodle.git] / mod / lesson / report.php
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.
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/>.
18 /**
19  * Displays the lesson statistics.
20  *
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');
28 require_once($CFG->dirroot.'/mod/lesson/pagetypes/branchtable.php'); // Needed for constant.
30 $id     = required_param('id', PARAM_INT);    // Course Module ID
31 $pageid = optional_param('pageid', null, PARAM_INT);    // Lesson Page ID
32 $action = optional_param('action', 'reportoverview', PARAM_ALPHA);  // action to take
33 $nothingtodisplay = false;
35 $cm = get_coursemodule_from_id('lesson', $id, 0, false, MUST_EXIST);
36 $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
37 $lesson = new lesson($DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST));
39 require_login($course, false, $cm);
41 $currentgroup = groups_get_activity_group($cm, true);
43 $context = context_module::instance($cm->id);
44 require_capability('mod/lesson:viewreports', $context);
46 $url = new moodle_url('/mod/lesson/report.php', array('id'=>$id));
47 $url->param('action', $action);
48 if ($pageid !== null) {
49     $url->param('pageid', $pageid);
50 }
51 $PAGE->set_url($url);
52 if ($action == 'reportoverview') {
53     $PAGE->navbar->add(get_string('reports', 'lesson'));
54     $PAGE->navbar->add(get_string('overview', 'lesson'));
55 }
57 $lessonoutput = $PAGE->get_renderer('mod_lesson');
59 if ($action === 'delete') {
60     /// Process any form data before fetching attempts, grades and times
61     if (has_capability('mod/lesson:edit', $context) and $form = data_submitted() and confirm_sesskey()) {
62     /// Cycle through array of userids with nested arrays of tries
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
70                 //      database - hope this all makes sense :)
71                 $modifier = 0;
73                 foreach ($tries as $try => $junk) {
74                     $try -= $modifier;
76                 /// Clean up the timer table by removing using the order - this is silly, it should be linked to specific attempt (skodak)
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                     }
86                     // Remove the grade from the grades tables - this is silly, it should be linked to specific attempt (skodak).
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);
91                     if ($grades) {
92                         $grade = reset($grades);
93                         $DB->delete_records('lesson_grades', array('id' => $grade->id));
94                     }
96                 /// Remove attempts and update the retry number
97                     $DB->delete_records('lesson_attempts', array('userid' => $userid, 'lessonid' => $lesson->id, 'retry' => $try));
98                     $DB->execute("UPDATE {lesson_attempts} SET retry = retry - 1 WHERE userid = ? AND lessonid = ? AND retry > ?", array($userid, $lesson->id, $try));
100                 /// Remove seen branches and update the retry number
101                     $DB->delete_records('lesson_branch', array('userid' => $userid, 'lessonid' => $lesson->id, 'retry' => $try));
102                     $DB->execute("UPDATE {lesson_branch} SET retry = retry - 1 WHERE userid = ? AND lessonid = ? AND retry > ?", array($userid, $lesson->id, $try));
104                 /// update central gradebook
105                     lesson_update_grades($lesson, $userid);
107                     $modifier++;
108                 }
109             }
110         }
111     }
112     redirect(new moodle_url($PAGE->url, array('action'=>'reportoverview')));
114 } else if ($action === 'reportoverview') {
115     /**************************************************************************
116     this action is for default view and overview view
117     **************************************************************************/
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);
123     // Only load students if there attempts for this lesson.
124     $attempts = $DB->record_exists('lesson_attempts', array('lessonid' => $lesson->id));
125     $branches = $DB->record_exists('lesson_branch', array('lessonid' => $lesson->id));
126     $timer = $DB->record_exists('lesson_timer', array('lessonid' => $lesson->id));
127     if ($attempts or $branches or $timer) {
128         list($esql, $params) = get_enrolled_sql($context, '', $currentgroup, true);
129         list($sort, $sortparams) = users_order_by_sql('u');
131         $params['lessonid'] = $lesson->id;
132         $ufields = user_picture::fields('u');
133         $sql = "SELECT DISTINCT $ufields
134                 FROM {user} u
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
142                 JOIN ($esql) ue ON ue.id = a.userid
143                 WHERE a.lessonid = :lessonid
144                 ORDER BY $sort";
146         $students = $DB->get_recordset_sql($sql, $params);
147         if (!$students->valid()) {
148             $students->close();
149             $nothingtodisplay = true;
150         }
151     } else {
152         $nothingtodisplay = true;
153     }
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     }
168     if (! $grades = $DB->get_records('lesson_grades', array('lessonid' => $lesson->id), 'completed')) {
169         $grades = array();
170     }
172     if (! $times = $DB->get_records('lesson_timer', array('lessonid' => $lesson->id), 'starttime')) {
173         $times = array();
174     }
176     echo $lessonoutput->header($lesson, $cm, $action, false, null, get_string('overview', 'lesson'));
177     groups_print_activity_menu($cm, $url);
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));
182         $seeallgradeslink = html_writer::link($seeallgradeslink, get_string('seeallcoursegrades', 'grades'));
183         echo $OUTPUT->box($seeallgradeslink, 'allcoursegrades');
184     }
186     // Build an array for output.
187     $studentdata = array();
189     $attempts = $DB->get_recordset('lesson_attempts', array('lessonid' => $lesson->id), 'timeseen');
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.
192         if (empty($studentdata[$attempt->userid]) || empty($studentdata[$attempt->userid][$attempt->retry])) {
193             // restore/setup defaults
194             $n = 0;
195             $timestart = 0;
196             $timeend = 0;
197             $usergrade = null;
198             $eol = false;
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;
223                         $eol = $time->completed;
224                         break;
225                     }
226                     $n++; // if not equal, then increment n
227                 }
228             }
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,
235                                                                     "end" => $eol,
236                                                                     "try" => $attempt->retry,
237                                                                     "userid" => $attempt->userid);
238         }
239     }
240     $attempts->close();
242     $branches = $DB->get_recordset('lesson_branch', array('lessonid' => $lesson->id), 'timeseen');
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.
245         if (empty($studentdata[$branch->userid]) || empty($studentdata[$branch->userid][$branch->retry])) {
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             }
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();
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     }
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     }
325     // set all the stats variables
326     $numofattempts = 0;
327     $avescore      = 0;
328     $avetime       = 0;
329     $highscore     = null;
330     $lowscore      = null;
331     $hightime      = null;
332     $lowtime       = null;
334     $table = new html_table();
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     }
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);
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                 }
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"];
374                     $temp .= $try["grade"]."%";
375                     $bestgradefound = true;
376                     if ($try["grade"] > $bestgrade) {
377                         $bestgrade = $try["grade"];
378                     }
379                     $temp .= "&nbsp;".userdate($try["timestart"]);
380                     $temp .= ",&nbsp;(".format_time($timetotake).")</a>";
381                 } else {
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                     }
397                 }
398                 // build up the attempts array
399                 $attempts[] = $temp;
401                 // Run these lines for the stats only if the user finnished the lesson.
402                 if ($try["end"]) {
403                     // User has completed the lesson.
404                     $numofattempts++;
405                     $avetime += $timetotake;
406                     if ($timetotake > $hightime || $hightime == null) {
407                         $hightime = $timetotake;
408                     }
409                     if ($timetotake < $lowtime || $lowtime == null) {
410                         $lowtime = $timetotake;
411                     }
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                         }
422                     }
423                 }
424             }
425             // get line breaks in after each attempt
426             $attempts = implode("<br />\n", $attempts);
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             }
436         }
437     }
438     $students->close();
439     // Print it all out!
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     }
445     echo html_writer::table($table);
446     if (has_capability('mod/lesson:edit', $context)) {
447         $checklinks  = '<a href="javascript: checkall();">'.get_string('selectall').'</a> / ';
448         $checklinks .= '<a href="javascript: checknone();">'.get_string('deselectall').'</a>';
449         $checklinks .= html_writer::label('action', 'menuaction', false, array('class' => 'accesshide'));
450         $checklinks .= html_writer::select(array('delete' => get_string('deleteselected')), 'action', 0, array(''=>'choosedots'), array('id'=>'actionid', 'class' => 'autosubmit'));
451         $PAGE->requires->yui_module('moodle-core-formautosubmit',
452             'M.core.init_formautosubmit',
453             array(array('selectid' => 'actionid', 'nothing' => false))
454         );
455         echo $OUTPUT->box($checklinks, 'center');
456         echo '</form>';
457     }
459     // Calculate the Statistics.
460     if ($avetime == null) {
461         $avetime = get_string("notcompleted", "lesson");
462     } else {
463         $avetime = format_float($avetime/$numofattempts, 0);
464         $avetime = format_time($avetime);
465     }
466     if ($hightime == null) {
467         $hightime = get_string("notcompleted", "lesson");
468     } else {
469         $hightime = format_time($hightime);
470     }
471     if ($lowtime == null) {
472         $lowtime = get_string("notcompleted", "lesson");
473     } else {
474         $lowtime = format_time($lowtime);
475     }
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         }
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);
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);
515     }
517     echo html_writer::table($stattable);
518 } else if ($action === 'reportdetail') {
519     /**************************************************************************
520     this action is for a student detailed view and for the general detailed view
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
531 **************************************************************************/
532     echo $lessonoutput->header($lesson, $cm, $action, false, null, get_string('detailedstats', 'lesson'));
533     groups_print_activity_menu($cm, $url);
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));
538         $seeallgradeslink = html_writer::link($seeallgradeslink, get_string('seeallcoursegrades', 'grades'));
539         echo $OUTPUT->box($seeallgradeslink, 'allcoursegrades');
540     }
542     $formattextdefoptions = new stdClass;
543     $formattextdefoptions->para = false;  //I'll use it widely in this page
544     $formattextdefoptions->overflowdiv = true;
546     $userid = optional_param('userid', null, PARAM_INT); // if empty, then will display the general detailed view
547     $try    = optional_param('try', null, PARAM_INT);
549     if (!empty($userid)) {
550         // Apply overrides.
551         $lesson->update_effective_access($userid);
552     }
554     $lessonpages = $lesson->load_all_pages();
555     foreach ($lessonpages as $lessonpage) {
556         if ($lessonpage->prevpageid == 0) {
557             $pageid = $lessonpage->id;
558         }
559     }
561     // now gather the stats into an object
562     $firstpageid = $pageid;
563     $pagestats = array();
564     while ($pageid != 0) { // EOL
565         $page = $lessonpages[$pageid];
566         $params = array ("lessonid" => $lesson->id, "pageid" => $page->id);
567         if ($allanswers = $DB->get_records_select("lesson_attempts", "lessonid = :lessonid AND pageid = :pageid", $params, "timeseen")) {
568             // get them ready for processing
569             $orderedanswers = array();
570             foreach ($allanswers as $singleanswer) {
571                 // ordering them like this, will help to find the single attempt record that we want to keep.
572                 $orderedanswers[$singleanswer->userid][$singleanswer->retry][] = $singleanswer;
573             }
574             // this is foreach user and for each try for that user, keep one attempt record
575             foreach ($orderedanswers as $orderedanswer) {
576                 foreach($orderedanswer as $tries) {
577                     $page->stats($pagestats, $tries);
578                 }
579             }
580         } else {
581             // no one answered yet...
582         }
583         //unset($orderedanswers);  initialized above now
584         $pageid = $page->nextpageid;
585     }
587     $manager = lesson_page_type_manager::get($lesson);
588     $qtypes = $manager->get_page_type_strings();
590     $answerpages = array();
591     $answerpage = "";
592     $pageid = $firstpageid;
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
596     // grayout pages that the user did not answer and Branch, end of branch, cluster
597     // and end of cluster pages
598     while ($pageid != 0) { // EOL
599         $page = $lessonpages[$pageid];
600         $answerpage = new stdClass;
601         $data ='';
603         $answerdata = new stdClass;
604         // Set some defaults for the answer data.
605         $answerdata->score = null;
606         $answerdata->response = null;
607         $answerdata->responseformat = FORMAT_PLAIN;
609         $answerpage->title = format_string($page->title);
611         $options = new stdClass;
612         $options->noclean = true;
613         $options->overflowdiv = true;
614         $options->context = $context;
615         $answerpage->contents = format_text($page->contents, $page->contentsformat, $options);
617         $answerpage->qtype = $qtypes[$page->qtype].$page->option_description_string();
618         $answerpage->grayout = $page->grayout;
619         $answerpage->context = $context;
621         if (empty($userid)) {
622             // there is no userid, so set these vars and display stats.
623             $answerpage->grayout = 0;
624             $useranswer = null;
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
628             $i = 0;
629             foreach ($useranswers as $userattempt) {
630                 $useranswer = $userattempt;
631                 $i++;
632                 if ($lesson->maxattempts == $i) {
633                     break; // reached maxattempts, break out
634                 }
635             }
636         } else {
637             // user did not answer this page, gray it out and set some nulls
638             $answerpage->grayout = 1;
639             $useranswer = null;
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     }
647     /// actually start printing something
648     $table = new html_table();
649     $table->wrap = array();
650     $table->width = "60%";
651     if (!empty($userid)) {
652         // if looking at a students try, print out some basic stats at the top
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);
661         $table->head = array();
662         $table->align = array('right', 'left');
663         $table->attributes['class'] = 'compacttable generaltable';
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)) {
667             $grade = -1;
668             $completed = -1;
669         } else {
670             $grade = current($grades);
671             $completed = $grade->completed;
672             $grade = round($grade->grade, 2);
673         }
674         if (!$times = $DB->get_records_select("lesson_timer", "lessonid = :lessonid and userid = :userid", $params, "starttime", "*", $try, 1)) {
675             $timetotake = -1;
676         } else {
677             $timetotake = current($times);
678             $timetotake = $timetotake->lessontime - $timetotake->starttime;
679         }
681         if ($timetotake == -1 || $completed == -1 || $grade == -1) {
682             $table->align = array("center");
684             $table->data[] = array(get_string("notcompleted", "lesson"));
685         } else {
686             $user = $DB->get_record('user', array('id' => $userid));
688             $gradeinfo = lesson_grade($lesson, $try, $user->id);
690             $table->data[] = array(get_string('name').':', $OUTPUT->user_picture($user, array('courseid'=>$course->id)).fullname($user, true));
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         }
696         echo html_writer::table($table);
698         // Don't want this class for later tables
699         $table->attributes['class'] = '';
700     }
703     $table->align = array('left', 'left');
704     $table->size = array('70%', null);
705     $table->attributes['class'] = 'compacttable generaltable';
707     foreach ($answerpages as $page) {
708         unset($table->data);
709         if ($page->grayout) { // set the color of text
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         }
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, " ");
723         $table->data[] = array($fontstart.get_string("answer", "lesson").":".$fontend, ' ');
724         // apply the font to each answer
725         if (!empty($page->answerdata)) {
726             foreach ($page->answerdata->answers as $answer){
727                 $modified = array();
728                 foreach ($answer as $single) {
729                     // need to apply a font to each one
730                     $modified[] = $fontstart2.$single.$fontend2;
731                 }
732                 $table->data[] = $modified;
733             }
734             if (isset($page->answerdata->response)) {
735                 $table->data[] = array($fontstart.get_string("response", "lesson").": <br />".$fontend
736                         .$fontstart2.$page->answerdata->response.$fontend2, " ");
737             }
738             $table->data[] = array($page->answerdata->score, " ");
739         } else {
740             $table->data[] = array(get_string('didnotanswerquestion', 'lesson'), " ");
741         }
742         echo html_writer::start_tag('div', array('class' => 'no-overflow'));
743         echo html_writer::table($table);
744         echo html_writer::end_tag('div');
745     }
746 } else {
747     print_error('unknowaction');
750 /// Finish the page
751 echo $OUTPUT->footer();