mod-lesson MDL-25632 Fixed undefined var notice for responseformat in the lesson...
[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
22  * @subpackage lesson
23  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
24  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or late
25  **/
27 require_once('../../config.php');
28 require_once($CFG->dirroot.'/mod/lesson/locallib.php');
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 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
42 require_capability('mod/lesson:manage', $context);
44 $params = array("lessonid" => $lesson->id);
45 if (!empty($cm->groupingid)) {
46     $params["groupid"] = $cm->groupingid;
47     $sql = "SELECT DISTINCT u.id, u.*
48                 FROM {lesson_attempts} a
49                     INNER JOIN {user} u ON u.id = a.userid
50                     INNER JOIN {groups_members} gm ON gm.userid = u.id
51                     INNER JOIN {groupings_groups} gg ON gm.groupid = :groupid
52                 WHERE a.lessonid = :lessonid
53                 ORDER BY u.lastname";
54 } else {
55     $sql = "SELECT DISTINCT u.id, u.*
56             FROM {user} u,
57                  {lesson_attempts} a
58             WHERE a.lessonid = :lessonid and
59                   u.id = a.userid
60             ORDER BY u.lastname";
61 }
63 if (! $students = $DB->get_records_sql($sql, $params)) {
64     $nothingtodisplay = true;
65 }
67 $url = new moodle_url('/mod/lesson/report.php', array('id'=>$id));
68 if ($action !== 'reportoverview') {
69     $url->param('action', $action);
70 }
71 if ($pageid !== NULL) {
72     $url->param('pageid', $pageid);
73 }
74 $PAGE->set_url($url);
75 if ($action == 'reportoverview') {
76     $PAGE->navbar->add(get_string('reports', 'lesson'));
77     $PAGE->navbar->add(get_string('overview', 'lesson'));
78 }
80 $lessonoutput = $PAGE->get_renderer('mod_lesson');
82 if (! $attempts = $DB->get_records('lesson_attempts', array('lessonid' => $lesson->id), 'timeseen')) {
83     $nothingtodisplay = true;
84 }
86 if (! $grades = $DB->get_records('lesson_grades', array('lessonid' => $lesson->id), 'completed')) {
87     $grades = array();
88 }
90 if (! $times = $DB->get_records('lesson_timer', array('lessonid' => $lesson->id), 'starttime')) {
91     $times = array();
92 }
94 if ($nothingtodisplay) {
95     echo $lessonoutput->header($lesson, $cm, $action);
96     echo $OUTPUT->notification(get_string('nolessonattempts', 'lesson'));
97     echo $OUTPUT->footer();
98     exit();
99 }
101 if ($action === 'delete') {
102     /// Process any form data before fetching attempts, grades and times
103     if (has_capability('mod/lesson:edit', $context) and $form = data_submitted() and confirm_sesskey()) {
104     /// Cycle through array of userids with nested arrays of tries
105         if (!empty($form->attempts)) {
106             foreach ($form->attempts as $userid => $tries) {
107                 // Modifier IS VERY IMPORTANT!  What does it do?
108                 //      Well, it is for when you delete multiple attempts for the same user.
109                 //      If you delete try 1 and 3 for a user, then after deleting try 1, try 3 then
110                 //      becomes try 2 (because try 1 is gone and all tries after try 1 get decremented).
111                 //      So, the modifier makes sure that the submitted try refers to the current try in the
112                 //      database - hope this all makes sense :)
113                 $modifier = 0;
115                 foreach ($tries as $try => $junk) {
116                     $try -= $modifier;
118                 /// Clean up the timer table by removing using the order - this is silly, it should be linked to specific attempt (skodak)
119                     $params = array ("userid" => $userid, "lessonid" => $lesson->id);
120                     $timers = $DB->get_records_sql("SELECT id FROM {lesson_timer}
121                                                      WHERE userid = :userid AND lessonid = :lessonid
122                                                   ORDER BY starttime", $params, $try, 1);
123                     if ($timers) {
124                         $timer = reset($timers);
125                         $DB->delete_records('lesson_timer', array('id' => $timer->id));
126                     }
128                 /// Remove the grade from the grades and high_scores tables - this is silly, it should be linked to specific attempt (skodak)
129                     $grades = $DB->get_records_sql("SELECT id FROM {lesson_grades}
130                                                      WHERE userid = :userid AND lessonid = :lessonid
131                                                   ORDER BY completed", $params, $try, 1);
133                     if ($grades) {
134                         $grade = reset($grades);
135                         $DB->delete_records('lesson_grades', array('id' => $grade->id));
136                         $DB->delete_records('lesson_high_scores', array('gradeid' => $grade->id, 'lessonid' => $lesson->id, 'userid' => $userid));
137                     }
139                 /// Remove attempts and update the retry number
140                     $DB->delete_records('lesson_attempts', array('userid' => $userid, 'lessonid' => $lesson->id, 'retry' => $try));
141                     $DB->execute("UPDATE {lesson_attempts} SET retry = retry - 1 WHERE userid = ? AND lessonid = ? AND retry > ?", array($userid, $lesson->id, $try));
143                 /// Remove seen branches and update the retry number
144                     $DB->delete_records('lesson_branch', array('userid' => $userid, 'lessonid' => $lesson->id, 'retry' => $try));
145                     $DB->execute("UPDATE {lesson_branch} SET retry = retry - 1 WHERE userid = ? AND lessonid = ? AND retry > ?", array($userid, $lesson->id, $try));
147                 /// update central gradebook
148                     lesson_update_grades($lesson, $userid);
150                     $modifier++;
151                 }
152             }
153         }
154     }
155     redirect(new moodle_url($PAGE->url, array('action'=>'reportoverview')));
157 } else if ($action === 'reportoverview') {
158     /**************************************************************************
159     this action is for default view and overview view
160     **************************************************************************/
161     echo $lessonoutput->header($lesson, $cm, $action);
163     $course_context = get_context_instance(CONTEXT_COURSE, $course->id);
164     if (has_capability('gradereport/grader:view', $course_context) && has_capability('moodle/grade:viewall', $course_context)) {
165         $seeallgradeslink = new moodle_url('/grade/report/grader/index.php', array('id'=>$course->id));
166         $seeallgradeslink = html_writer::link($seeallgradeslink, get_string('seeallcoursegrades', 'grades'));
167         echo $OUTPUT->box($seeallgradeslink, 'allcoursegrades');
168     }
170     $studentdata = array();
172     // build an array for output
173     foreach ($attempts as $attempt) {
174         // 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.
175         if (!array_key_exists($attempt->userid, $studentdata) || !array_key_exists($attempt->retry, $studentdata[$attempt->userid])) {
176             // restore/setup defaults
177             $n = 0;
178             $timestart = 0;
179             $timeend = 0;
180             $usergrade = NULL;
182             // search for the grade record for this try. if not there, the nulls defined above will be used.
183             foreach($grades as $grade) {
184                 // check to see if the grade matches the correct user
185                 if ($grade->userid == $attempt->userid) {
186                     // see if n is = to the retry
187                     if ($n == $attempt->retry) {
188                         // get grade info
189                         $usergrade = round($grade->grade, 2); // round it here so we only have to do it once
190                         break;
191                     }
192                     $n++; // if not equal, then increment n
193                 }
194             }
195             $n = 0;
196             // search for the time record for this try. if not there, the nulls defined above will be used.
197             foreach($times as $time) {
198                 // check to see if the grade matches the correct user
199                 if ($time->userid == $attempt->userid) {
200                     // see if n is = to the retry
201                     if ($n == $attempt->retry) {
202                         // get grade info
203                         $timeend = $time->lessontime;
204                         $timestart = $time->starttime;
205                         break;
206                     }
207                     $n++; // if not equal, then increment n
208                 }
209             }
211             // build up the array.
212             // this array represents each student and all of their tries at the lesson
213             $studentdata[$attempt->userid][$attempt->retry] = array( "timestart" => $timestart,
214                                                                     "timeend" => $timeend,
215                                                                     "grade" => $usergrade,
216                                                                     "try" => $attempt->retry,
217                                                                     "userid" => $attempt->userid);
218         }
219     }
220     // set all the stats variables
221     $numofattempts = 0;
222     $avescore      = 0;
223     $avetime       = 0;
224     $highscore     = NULL;
225     $lowscore      = NULL;
226     $hightime      = NULL;
227     $lowtime       = NULL;
229     $table = new html_table();
231     // set up the table object
232     $table->head = array(get_string('name'), get_string('attempts', 'lesson'), get_string('highscore', 'lesson'));
233     $table->align = array('center', 'left', 'left');
234     $table->wrap = array('nowrap', 'nowrap', 'nowrap');
235     $table->attributes['class'] = 'standardtable generaltable';
236     $table->size = array(null, '70%', null);
238     // print out the $studentdata array
239     // going through each student that has attempted the lesson, so, each student should have something to be displayed
240     foreach ($students as $student) {
241         // check to see if the student has attempts to print out
242         if (array_key_exists($student->id, $studentdata)) {
243             // set/reset some variables
244             $attempts = array();
245             // gather the data for each user attempt
246             $bestgrade = 0;
247             $bestgradefound = false;
248             // $tries holds all the tries/retries a student has done
249             $tries = $studentdata[$student->id];
250             $studentname = "{$student->lastname},&nbsp;$student->firstname";
251             foreach ($tries as $try) {
252             // start to build up the checkbox and link
253                 if (has_capability('mod/lesson:edit', $context)) {
254                     $temp = '<input type="checkbox" id="attempts" name="attempts['.$try['userid'].']['.$try['try'].']" /> ';
255                 } else {
256                     $temp = '';
257                 }
259                 $temp .= "<a href=\"report.php?id=$cm->id&amp;action=reportdetail&amp;userid=".$try['userid'].'&amp;try='.$try['try'].'">';
260                 if ($try["grade"] !== NULL) { // if NULL then not done yet
261                     // this is what the link does when the user has completed the try
262                     $timetotake = $try["timeend"] - $try["timestart"];
264                     $temp .= $try["grade"]."%";
265                     $bestgradefound = true;
266                     if ($try["grade"] > $bestgrade) {
267                         $bestgrade = $try["grade"];
268                     }
269                     $temp .= "&nbsp;".userdate($try["timestart"]);
270                     $temp .= ",&nbsp;(".format_time($timetotake).")</a>";
271                 } else {
272                     // this is what the link does/looks like when the user has not completed the try
273                     $temp .= get_string("notcompleted", "lesson");
274                     $temp .= "&nbsp;".userdate($try["timestart"])."</a>";
275                     $timetotake = NULL;
276                 }
277                 // build up the attempts array
278                 $attempts[] = $temp;
280                 // run these lines for the stats only if the user finnished the lesson
281                 if ($try["grade"] !== NULL) {
282                     $numofattempts++;
283                     $avescore += $try["grade"];
284                     $avetime += $timetotake;
285                     if ($try["grade"] > $highscore || $highscore == NULL) {
286                         $highscore = $try["grade"];
287                     }
288                     if ($try["grade"] < $lowscore || $lowscore == NULL) {
289                         $lowscore = $try["grade"];
290                     }
291                     if ($timetotake > $hightime || $hightime == NULL) {
292                         $hightime = $timetotake;
293                     }
294                     if ($timetotake < $lowtime || $lowtime == NULL) {
295                         $lowtime = $timetotake;
296                     }
297                 }
298             }
299             // get line breaks in after each attempt
300             $attempts = implode("<br />\n", $attempts);
301             // add it to the table data[] object
302             $table->data[] = array($studentname, $attempts, $bestgrade."%");
303         }
304     }
305     // print it all out !
306     if (has_capability('mod/lesson:edit', $context)) {
307         echo  "<form id=\"theform\" method=\"post\" action=\"report.php\">\n
308                <input type=\"hidden\" name=\"sesskey\" value=\"".sesskey()."\" />\n
309                <input type=\"hidden\" name=\"id\" value=\"$cm->id\" />\n";
310     }
311     echo html_writer::table($table);
312     if (has_capability('mod/lesson:edit', $context)) {
313         $checklinks  = '<a href="javascript: checkall();">'.get_string('selectall').'</a> / ';
314         $checklinks .= '<a href="javascript: checknone();">'.get_string('deselectall').'</a>';
315         $checklinks .= html_writer::select(array('delete' => get_string('deleteselected')), 'action', 0, array(''=>'choosedots'), array('id'=>'actionid'));
316         $PAGE->requires->js_init_call('M.util.init_select_autosubmit', array('theform', 'actionid', ''));
317         echo $OUTPUT->box($checklinks, 'center');
318         echo '</form>';
319     }
321     // some stat calculations
322     if ($numofattempts == 0) {
323         $avescore = get_string("notcompleted", "lesson");
324     } else {
325         $avescore = format_float($avescore/$numofattempts, 2);
326     }
327     if ($avetime == NULL) {
328         $avetime = get_string("notcompleted", "lesson");
329     } else {
330         $avetime = format_float($avetime/$numofattempts, 0);
331         $avetime = format_time($avetime);
332     }
333     if ($hightime == NULL) {
334         $hightime = get_string("notcompleted", "lesson");
335     } else {
336         $hightime = format_time($hightime);
337     }
338     if ($lowtime == NULL) {
339         $lowtime = get_string("notcompleted", "lesson");
340     } else {
341         $lowtime = format_time($lowtime);
342     }
343     if ($highscore == NULL) {
344         $highscore = get_string("notcompleted", "lesson");
345     }
346     if ($lowscore == NULL) {
347         $lowscore = get_string("notcompleted", "lesson");
348     }
350     // output the stats
351     echo $OUTPUT->heading(get_string('lessonstats', 'lesson'));
352     $stattable = new html_table();
353     $stattable->head = array(get_string('averagescore', 'lesson'), get_string('averagetime', 'lesson'),
354                             get_string('highscore', 'lesson'), get_string('lowscore', 'lesson'),
355                             get_string('hightime', 'lesson'), get_string('lowtime', 'lesson'));
356     $stattable->align = array('center', 'center', 'center', 'center', 'center', 'center');
357     $stattable->wrap = array('nowrap', 'nowrap', 'nowrap', 'nowrap', 'nowrap', 'nowrap');
358     $stattable->attributes['class'] = 'standardtable generaltable';
359     $stattable->data[] = array($avescore.'%', $avetime, $highscore.'%', $lowscore.'%', $hightime, $lowtime);
361     echo html_writer::table($stattable);
362 } else if ($action === 'reportdetail') {
363     /**************************************************************************
364     this action is for a student detailed view and for the general detailed view
366     General flow of this section of the code
367     1.  Generate a object which holds values for the statistics for each question/answer
368     2.  Cycle through all the pages to create a object.  Foreach page, see if the student actually answered
369         the page.  Then process the page appropriatly.  Display all info about the question,
370         Highlight correct answers, show how the user answered the question, and display statistics
371         about each page
372     3.  Print out info about the try (if needed)
373     4.  Print out the object which contains all the try info
375 **************************************************************************/
376     echo $lessonoutput->header($lesson, $cm, $action);
378     $course_context = get_context_instance(CONTEXT_COURSE, $course->id);
379     if (has_capability('gradereport/grader:view', $course_context) && has_capability('moodle/grade:viewall', $course_context)) {
380         $seeallgradeslink = new moodle_url('/grade/report/grader/index.php', array('id'=>$course->id));
381         $seeallgradeslink = html_writer::link($seeallgradeslink, get_string('seeallcoursegrades', 'grades'));
382         echo $OUTPUT->box($seeallgradeslink, 'allcoursegrades');
383     }
385     $formattextdefoptions = new stdClass;
386     $formattextdefoptions->para = false;  //I'll use it widely in this page
387     $formattextdefoptions->overflowdiv = true;
389     $userid = optional_param('userid', NULL, PARAM_INT); // if empty, then will display the general detailed view
390     $try    = optional_param('try', NULL, PARAM_INT);
392     $lessonpages = $lesson->load_all_pages();
393     foreach ($lessonpages as $lessonpage) {
394         if ($lessonpage->prevpageid == 0) {
395             $pageid = $lessonpage->id;
396         }
397     }
399     // now gather the stats into an object
400     $firstpageid = $pageid;
401     $pagestats = array();
402     while ($pageid != 0) { // EOL
403         $page = $lessonpages[$pageid];
404         $params = array ("lessonid" => $lesson->id, "pageid" => $page->id);
405         if ($allanswers = $DB->get_records_select("lesson_attempts", "lessonid = :lessonid AND pageid = :pageid", $params, "timeseen")) {
406             // get them ready for processing
407             $orderedanswers = array();
408             foreach ($allanswers as $singleanswer) {
409                 // ordering them like this, will help to find the single attempt record that we want to keep.
410                 $orderedanswers[$singleanswer->userid][$singleanswer->retry][] = $singleanswer;
411             }
412             // this is foreach user and for each try for that user, keep one attempt record
413             foreach ($orderedanswers as $orderedanswer) {
414                 foreach($orderedanswer as $tries) {
415                     $page->stats($pagestats, $tries);
416                 }
417             }
418         } else {
419             // no one answered yet...
420         }
421         //unset($orderedanswers);  initialized above now
422         $pageid = $page->nextpageid;
423     }
425     $manager = lesson_page_type_manager::get($lesson);
426     $qtypes = $manager->get_page_type_strings();
428     $answerpages = array();
429     $answerpage = "";
430     $pageid = $firstpageid;
431     // cycle through all the pages
432     //  foreach page, add to the $answerpages[] array all the data that is needed
433     //  from the question, the users attempt, and the statistics
434     // grayout pages that the user did not answer and Branch, end of branch, cluster
435     // and end of cluster pages
436     while ($pageid != 0) { // EOL
437         $page = $lessonpages[$pageid];
438         $answerpage = new stdClass;
439         $data ='';
440         
441         $answerdata = new stdClass;
442         // Set some defaults for the answer data.
443         $answerdata->score = NULL;
444         $answerdata->response = NULL;
445         $answerdata->responseformat = FORMAT_PLAIN;
447         $answerpage->title = format_string($page->title);
449         $options = new stdClass;
450         $options->noclean = true;
451         $options->overflowdiv = true;
452         $answerpage->contents = format_text($page->contents, $page->contentsformat, $options);
454         $answerpage->qtype = $qtypes[$page->qtype].$page->option_description_string();
455         $answerpage->grayout = $page->grayout;
457         if (empty($userid)) {
458             // there is no userid, so set these vars and display stats.
459             $answerpage->grayout = 0;
460             $useranswer = NULL;    
461         } elseif ($useranswers = $DB->get_records("lesson_attempts",array("lessonid"=>$lesson->id, "userid"=>$userid, "retry"=>$try,"pageid"=>$page->id), "timeseen")) {
462             // get the user's answer for this page
463             // need to find the right one
464             $i = 0;
465             foreach ($useranswers as $userattempt) {
466                 $useranswer = $userattempt;
467                 $i++;
468                 if ($lesson->maxattempts == $i) {
469                     break; // reached maxattempts, break out
470                 }
471             }
472         } else {
473             // user did not answer this page, gray it out and set some nulls
474             $answerpage->grayout = 1;
475             $useranswer = NULL;
476         }
477         $i = 0;
478         $n = 0;
479         $answerpages[] = $page->report_answers(clone($answerpage), clone($answerdata), $useranswer, $pagestats, $i, $n);
480         $pageid = $page->nextpageid;
481     }
483     /// actually start printing something
484     $table = new html_table();
485     $table->wrap = array();
486     $table->width = "60%";
487     if (!empty($userid)) {
488         // if looking at a students try, print out some basic stats at the top
490             // print out users name
491             //$headingobject->lastname = $students[$userid]->lastname;
492             //$headingobject->firstname = $students[$userid]->firstname;
493             //$headingobject->attempt = $try + 1;
494             //print_heading(get_string("studentattemptlesson", "lesson", $headingobject));
495         echo $OUTPUT->heading(get_string('attempt', 'lesson', $try+1));
497         $table->head = array();
498         $table->align = array('right', 'left');
499         $table->attributes['class'] = 'compacttable generaltable';
501         $params = array("lessonid"=>$lesson->id, "userid"=>$userid);
502         if (!$grades = $DB->get_records_select("lesson_grades", "lessonid = :lessonid and userid = :userid", $params, "completed", "*", $try, 1)) {
503             $grade = -1;
504             $completed = -1;
505         } else {
506             $grade = current($grades);
507             $completed = $grade->completed;
508             $grade = round($grade->grade, 2);
509         }
510         if (!$times = $DB->get_records_select("lesson_timer", "lessonid = :lessonid and userid = :userid", $params, "starttime", "*", $try, 1)) {
511             $timetotake = -1;
512         } else {
513             $timetotake = current($times);
514             $timetotake = $timetotake->lessontime - $timetotake->starttime;
515         }
517         if ($timetotake == -1 || $completed == -1 || $grade == -1) {
518             $table->align = array("center");
520             $table->data[] = array(get_string("notcompleted", "lesson"));
521         } else {
522             $user = $students[$userid];
524             $gradeinfo = lesson_grade($lesson, $try, $user->id);
526             $table->data[] = array(get_string('name').':', $OUTPUT->user_picture($user, array('courseid'=>$course->id)).fullname($user, true));
527             $table->data[] = array(get_string("timetaken", "lesson").":", format_time($timetotake));
528             $table->data[] = array(get_string("completed", "lesson").":", userdate($completed));
529             $table->data[] = array(get_string('rawgrade', 'lesson').':', $gradeinfo->earned.'/'.$gradeinfo->total);
530             $table->data[] = array(get_string("grade", "lesson").":", $grade."%");
531         }
532         echo html_writer::table($table);
534         // Don't want this class for later tables
535         $table->attributes['class'] = '';
536     }
539     $table->align = array('left', 'left');
540     $table->size = array('70%', null);
541     $table->attributes['class'] = 'compacttable generaltable';
543     foreach ($answerpages as $page) {
544         unset($table->data);
545         if ($page->grayout) { // set the color of text
546             $fontstart = "<span class=\"dimmed\">";
547             $fontend = "</font>";
548             $fontstart2 = $fontstart;
549             $fontend2 = $fontend;
550         } else {
551             $fontstart = "";
552             $fontend = "";
553             $fontstart2 = "";
554             $fontend2 = "";
555         }
557         $table->head = array($fontstart2.$page->qtype.": ".format_string($page->title).$fontend2, $fontstart2.get_string("classstats", "lesson").$fontend2);
558         $table->data[] = array($fontstart.get_string("question", "lesson").": <br />".$fontend.$fontstart2.$page->contents.$fontend2, " ");
559         $table->data[] = array($fontstart.get_string("answer", "lesson").":".$fontend, ' ');
560         // apply the font to each answer
561         if (!empty($page->answerdata) && isset($page->answerdata->response)) {
562             foreach ($page->answerdata->answers as $answer){
563                 $modified = array();
564                 foreach ($answer as $single) {
565                     // need to apply a font to each one
566                     $modified[] = $fontstart2.$single.$fontend2;
567                 }
568                 $table->data[] = $modified;
569             }
570             if ($page->answerdata->response != NULL) {
571                 $table->data[] = array($fontstart.get_string("response", "lesson").": <br />".$fontend.$fontstart2.format_text($page->answerdata->response,$page->answerdata->responseformat,$formattextdefoptions).$fontend2, " ");
572             }
573             $table->data[] = array($page->answerdata->score, " ");
574         } else {
575             $table->data[] = array(0, " ");
576         }
577         echo html_writer::table($table);
578     }
579 } else {
580     print_error('unknowaction');
583 /// Finish the page
584 echo $OUTPUT->footer();