MDL-10383 - groups/groupings refactoring and improvements - basic UI for groupings...
[moodle.git] / mod / hotpot / report.php
1 <?PHP  // $Id$
3 // This script uses installed report plugins to print quiz reports
5     require_once("../../config.php");
6     require_once("lib.php");
8     $id = optional_param('id', 0, PARAM_INT); // Course Module ID, or
9     $hp = optional_param('hp', 0, PARAM_INT); // hotpot ID
11     if ($id) {
12         if (! $cm = get_coursemodule_from_id('hotpot', $id)) {
13             error("Course Module ID was incorrect");
14         }
15         if (! $course = get_record("course", "id", $cm->course)) {
16             error("Course is misconfigured");
17         }    
18         if (! $hotpot = get_record("hotpot", "id", $cm->instance)) {
19             error("Course module is incorrect");
20         }
22     } else {
23         if (! $hotpot = get_record("hotpot", "id", $hp)) {
24             error("Course module is incorrect");
25         }
26         if (! $course = get_record("course", "id", $hotpot->course)) {
27             error("Course is misconfigured");
28         }
29         if (! $cm = get_coursemodule_from_instance("hotpot", $hotpot->id, $course->id)) {
30             error("Course Module ID was incorrect");
31         }
32     }
34     // get the roles context for this course
35     $sitecontext = get_context_instance(CONTEXT_SYSTEM, SITEID);
36     $modulecontext = get_context_instance(CONTEXT_MODULE, $cm->id);
38     // set homeurl of couse (for error messages)
39     $course_homeurl = "$CFG->wwwroot/course/view.php?id=$course->id";
41     require_login($course->id);
43     // get report mode
44     if (has_capability('mod/hotpot:viewreport',$modulecontext)) {
45         $mode = optional_param('mode', 'overview', PARAM_ALPHA);
46     } else {
47         // ordinary students have no choice
48         $mode = 'overview';
49     }
51     // assemble array of form data
52     $formdata = array(
53         'mode' => $mode,
54         'reportusers'      => has_capability('mod/hotpot:viewreport',$modulecontext) ? optional_param('reportusers', get_user_preferences('hotpot_reportusers', 'allusers'), PARAM_ALPHANUM) : 'this',
55         'reportattempts'   => optional_param('reportattempts', get_user_preferences('hotpot_reportattempts', 'all'), PARAM_ALPHA),
56         'reportformat'     => optional_param('reportformat', 'htm', PARAM_ALPHA),
57         'reportshowlegend' => optional_param('reportshowlegend', get_user_preferences('hotpot_reportshowlegend', '0'), PARAM_INT),
58         'reportencoding'   => optional_param('reportencoding', get_user_preferences('hotpot_reportencoding', ''), PARAM_ALPHANUM),
59         'reportwrapdata'   => optional_param('reportwrapdata', get_user_preferences('hotpot_reportwrapdata', '1'), PARAM_INT),
60     );
62     foreach ($formdata as $name=>$value) {
63         set_user_preference("hotpot_$name", $value);
64     }
66 /// Start the report
68     add_to_log($course->id, "hotpot", "report", "report.php?id=$cm->id&mode=$mode", "$hotpot->id", "$cm->id");
70     // print page header. if required
71     if ($formdata['reportformat']=='htm') {
72         hotpot_print_report_heading($course, $cm, $hotpot, $mode);
73         if (has_capability('mod/hotpot:viewreport',$modulecontext)) {
74             hotpot_print_report_selector($course, $hotpot, $formdata);
75         }
76     }
78     // delete selected attempts, if any
79     if (has_capability('mod/hotpot:deleteattempt',$modulecontext)) {
80         $del = optional_param('del', '', PARAM_ALPHA);
81         hotpot_delete_selected_attempts($hotpot, $del);
82     }
84     // check for groups
85     if (preg_match('/^group(\d*)$/', $formdata['reportusers'], $matches)) {
86         $formdata['reportusers'] = 'group';
87         $formdata['reportgroupid'] = 0;
88         // validate groupid
89         if ($groups = groups_get_groups_names($course->id)) {
90             if (isset($groups[$matches[1]])) {
91                 $formdata['reportgroupid'] = $matches[1];
92             }
93         }
94     }
96     $user_ids = '';
97     $users = array();
99     switch ($formdata['reportusers']) {
101         case 'allusers':
102             // anyone who has ever attempted this hotpot
103             if ($records = get_records_select('hotpot_attempts', "hotpot=$hotpot->id", '', 'id,userid')) {
104                 foreach ($records as $record) {
105                     $users[$record->userid] = 0; // "0" means user is NOT currently allowed to attempt this HotPot
106                 }
107                 unset($records);
108             }
109             break;
111         case 'group':
112             // group members
113             if ($members = groups_get_members($formdata['reportgroupid'])) {
114                 foreach ($members as $memberid=>$unused) {
115                     $users[$memberid] = 1; // "1" signifies currently recognized participant
116                 }
117             }
118             break;
120         case 'allparticipants':
121             // anyone currently allowed to attempt this HotPot
122             if ($records = get_users_by_capability($modulecontext, 'mod/hotpot:attempt', 'u.id,u.id', 'u.id')) {
123                 foreach ($records as $record) {
124                     $users[$record->id] = 1; // "1" means user is allowed to do this HotPot
125                 }
126                 unset($records);
127             }
128             break;
130         case 'existingstudents':
131             // anyone currently allowed to attempt this HotPot who is not a teacher
132             $teachers = get_users_by_capability($modulecontext, 'mod/hotpot:viewreport', 'u.id,u.id', 'u.id');
133             if ($records = get_users_by_capability($modulecontext, 'mod/hotpot:attempt', 'u.id,u.id', 'u.id')) {
134                 foreach ($records as $record) {
135                     if (empty($teachers[$record->id])) {
136                         $users[$record->id] = 1;
137                     }
138                 }
139             }
140             break;
142         case 'this': // current user only
143             $user_ids = $USER->id;
144             break;
146         default: // specific user selected by teacher
147             if (is_numeric($formdata['reportusers'])) {
148                 $user_ids = $formdata['reportusers'];
149             }
150     }
151     if (empty($user_ids) && count($users)) {
152         ksort($users);
153         $user_ids = join(',', array_keys($users));
154     }
155     if (empty($user_ids)) {
156         print_heading(get_string('nousersyet'));
157         exit;
158     }
160     // database table and selection conditions
161     $table = "{$CFG->prefix}hotpot_attempts a";
162     $select = "a.hotpot=$hotpot->id AND a.userid IN ($user_ids)";
163     if ($mode!='overview') {
164         $select .= ' AND a.status<>'.HOTPOT_STATUS_INPROGRESS;
165     }
167     // confine attempts if necessary
168     switch ($formdata['reportattempts']) {
169         case 'best':
170             $function = 'MAX';
171             $fieldnames = array('score', 'id', 'clickreportid');
172             $defaultvalue = 0;
173             break;
174         case 'first':
175             $function = 'MIN';
176             $fieldnames = array('timefinish', 'id', 'clickreportid');
177             $default_value = time();
178             break;
179         case 'last':
180             $function = 'MAX';
181             $fieldnames = array('timefinish', 'id', 'clickreportid');
182             $defaultvalue = time();
183             break;
184         default: // 'all' and any others
185             $function = '';
186             $fieldnames = array();
187             $defaultvalue = '';
188             break;
189     }
190     if (empty($function) || empty($fieldnames)) {
191         // do nothing (i.e. get ALL attempts)
192     } else {
193         $groupby = 'userid';
194         $records = hotpot_get_records_groupby($function, $fieldnames, $table, $select, $groupby);
196         $ids = array();
197         foreach ($records as $record) {
198             $ids[] = $record->clickreportid;
199         }
200         $select = "a.clickreportid IN (".join(',', $ids).")";
201     }
203     // pick out last attempt in each clickreport series
204     $cr_attempts = hotpot_get_records_groupby('MAX', array('timefinish', 'id'), $table, $select, 'clickreportid');
206     $fields = 'a.*, u.firstname, u.lastname, u.picture';
207     if ($mode=='click') {
208         $fields .= ', u.idnumber';
209     } else { 
210         // overview, simple and detailed reports 
211         // get last attempt record in clickreport series
212         $ids = array();
213         foreach ($cr_attempts as $cr_attempt) {
214             $ids[] = $cr_attempt->id;
215         }
216         if (empty($ids)) {
217             $select = "";
218         } else {
219             $ids = array_unique($ids);
220             sort($ids);
221             $select = "a.id IN (".join(',', $ids).")";
222         }
223     }
225     $attempts = array();
227     if ($select) {
228         // add user information to SQL query
229         $select .= ' AND a.userid = u.id';
230         $table .= ", {$CFG->prefix}user u";
231         $order = "u.lastname, a.attempt, a.timefinish";
232         // get the attempts (at last!)
233         $attempts = get_records_sql("SELECT $fields FROM $table WHERE $select ORDER BY $order");
234     }
236     // stop now if no attempts were found
237     if (empty($attempts)) {
238         print_heading(get_string('noattemptstoshow','quiz'));
239         exit;
240     }
242     // get the questions
243     if (!$questions = get_records_select('hotpot_questions', "hotpot='$hotpot->id'")) {
244         $questions = array();
245     }
247     // get grades
248     $grades = hotpot_get_grades($hotpot, $user_ids);
250     // get list of attempts by user and set reference to last attempt in clickreport series
251     $users = array();
252     foreach ($attempts as $id=>$attempt) {
254         $userid = $attempt->userid;
256         if (!isset($users[$userid])) {
257             $users[$userid]->grade = isset($grades[$userid]) ? $grades[$userid] : '&nbsp;';
258             $users[$userid]->attempts = array();
259         }
261         $users[$userid]->attempts[] = &$attempts[$id];
263         if ($mode=='click') {
264             // shortcut to clickreportid (=the id of the FIRST attempt in this clickreport series)
265             $clickreportid = $attempt->clickreportid;
266             if (isset($cr_attempts[$clickreportid])) {
267                 // store id and finish time of LAST attempt in this clickreport series
268                 $attempts[$id]->cr_lastclick = $cr_attempts[$clickreportid]->id;
269                 $attempts[$id]->cr_timefinish = $cr_attempts[$clickreportid]->timefinish;
270             }
271         }
272     }
274     if ($mode!='overview') {
276         // initialise details of responses to questions in these attempts
277         foreach ($attempts as $a=>$attempt) {
278             $attempts[$a]->responses = array();
279         }
280         foreach ($questions as $q=>$question) {
281             $questions[$q]->attempts = array();
282         }
284         // get reponses to these attempts
285         $attempt_ids = join(',',array_keys($attempts));
286         if (!$responses = get_records_sql("SELECT * FROM {$CFG->prefix}hotpot_responses WHERE attempt IN ($attempt_ids)")) {
287             $responses = array();
288         }
290         // ids of questions used in these responses
291         $questionids = array();
293         foreach ($responses as $response) {
294             // shortcuts to the attempt and question ids
295             $a = $response->attempt;
296             $q = $response->question;
298             // check the attempt and question objects exist
299             // (if they don't exist, something is very wrong!)
300             if (isset($attempts[$a]) || isset($questions[$q])) {
302                 // add the response for this attempt
303                 $attempts[$a]->responses[$q] = $response;
305                 // add a reference from the question to the attempt which includes this question
306                 $questions[$q]->attempts[] = &$attempts[$a];
308                 // flag this id as being used
309                 $questionids[$q] = true;
310             }
311         }
313         // remove unused questions
314         $questionids = array_keys($questionids);
315         foreach ($questions as $id=>$question) {
316             if (!in_array($id, $questionids)) {
317                 unset($questions[$id]);
318             }
319         }
320     }
322 /// Open the selected hotpot report and display it
324     if (! is_readable("report/$mode/report.php")) {
325         error("Report not known (".clean_text($mode).")", $course_homeurl);
326     }
328     include("report/default.php");  // Parent class
329     include("report/$mode/report.php");
331     $report = new hotpot_report();
333     if (! $report->display($hotpot, $cm, $course, $users, $attempts, $questions, $formdata)) {
334         error("Error occurred during report processing!", $course_homeurl);
335     }
337     if ($formdata['reportformat']=='htm') {
338         print_footer($course);
339     }
341 //////////////////////////////////////////////
342 /// functions to delete attempts and responses
344 function hotpot_grade_heading($hotpot, $formdata) {
346     global $HOTPOT_GRADEMETHOD;
347     $grademethod = $HOTPOT_GRADEMETHOD[$hotpot->grademethod];
349     if ($hotpot->grade!=100) {
350         $grademethod = "$hotpot->grade x $grademethod/100";
351     }
352     if ($formdata['reportformat']=='htm') {
353         $grademethod = '<font size="1">'.$grademethod.'</font>';
354     }
355     $nl = $formdata['reportformat']=='htm' ? '<br />' : "\n";
356     return get_string('grade')."$nl($grademethod)";
358 function hotpot_delete_selected_attempts(&$hotpot, $del) {
360     $select = '';
361     switch ($del) {
362         case 'all' :
363             $select = "hotpot='$hotpot->id'";
364             break;
365         case 'abandoned':
366             $select = "hotpot='$hotpot->id' AND status=".HOTPOT_STATUS_ABANDONED;
367             break;
368         case 'selection':
369             $ids = (array)data_submitted();
370             unset($ids['del']);
371             unset($ids['id']);
372             if (!empty($ids)) {
373                 $select = "hotpot='$hotpot->id' AND clickreportid IN (".implode(',', $ids).")";
374             }
375             break;
376     }
378     // delete attempts using $select, if it is set
379     if ($select) {
381         $table = 'hotpot_attempts';
382         if ($attempts = get_records_select($table, $select)) {
384             hotpot_delete_and_notify($table, $select, get_string('attempts', 'quiz'));
386             $select = 'attempt IN ('.implode(',', array_keys($attempts)).')';
387             hotpot_delete_and_notify('hotpot_details', $select, get_string('rawdetails', 'hotpot'));
388             hotpot_delete_and_notify('hotpot_responses', $select, get_string('answer', 'quiz'));
389         }
390     }
394 //////////////////////////////////////////////
395 /// functions to print the report headings and 
396 /// report selector menus
398 function hotpot_print_report_heading(&$course, &$cm, &$hotpot, &$mode) {
399     $strmodulenameplural = get_string("modulenameplural", "hotpot");
400     $strmodulename  = get_string("modulename", "hotpot");
402     $title = format_string($course->shortname) . ": $hotpot->name";
403     $heading = $course->fullname;
404     
405     $navlinks = array();
406     $navlinks[] = array('name' => $strmodulenameplural, 'link' => 'index.php?id='.$course->id, 'type' => 'activity');
407     $navlinks[] = array('name' => $hotpot->name, 'link' => "view.php?id=$cm->id", 'type' => 'activityinstance');
410     $modulecontext = get_context_instance(CONTEXT_MODULE, $cm->id);
411     if (has_capability('mod/hotpot:viewreport',$modulecontext)) {
412         if ($mode=='overview' || $mode=='simplestat' || $mode=='fullstat') {
413             $module = "quiz";
414         } else {
415             $module = "hotpot";
416         }
418         $navlinks[] = array('name' => get_string("report$mode", $module), 'link' => '', 'type' => 'title');
419         
421     } else {
423         $navlinks[] = array('name' => get_string("report", "quiz"), 'link' => '', 'type' => 'title');
424     }
426     $button = update_module_button($cm->id, $course->id, $strmodulename);
427     $navigation = build_navigation($navlinks);
428     print_header($title, $heading, $navigation, "", "", true, $button, navmenu($course, $cm));
430     print_heading($hotpot->name);
432 function hotpot_print_report_selector(&$course, &$hotpot, &$formdata) {
434     global $CFG;
436     $reports = hotpot_get_report_names('overview,simplestat,fullstat');
438     print '<form method="post" action="'."$CFG->wwwroot/mod/hotpot/report.php?hp=$hotpot->id".'">';
439     print '<table cellpadding="2" align="center">';
441     $menus = array();
443     $menus['mode'] = array();
444     foreach ($reports as $name) {
445         if ($name=='overview' || $name=='simplestat' || $name=='fullstat') {
446             $module = "quiz";   // standard reports
447         } else if ($name=='click' && empty($hotpot->clickreporting)) {
448             $module =  "";      // clickreporting is disabled
449         } else {
450             $module = "hotpot"; // custom reports
451         }
452         if ($module) {
453             $menus['mode'][$name] = get_string("report$name", $module);
454         }
455     }
457     $menus['reportusers'] = array(
458         'allusers' => get_string('allusers', 'hotpot'),
459         'allparticipants' => get_string('allparticipants')
460     );
462     // groups
463     if ($groups = groups_get_groups_names($course->id)) { //TODO:check.
464         foreach ($groups as $gid => $gname) {
465             $menus['reportusers']["group$gid"] = get_string('group').': '.$gname;
466         }
467     }
469     // get users who have ever atetmpted this HotPot
470     $users = get_records_sql("
471         SELECT 
472             u.id, u.firstname, u.lastname
473         FROM 
474             {$CFG->prefix}user u,
475             {$CFG->prefix}hotpot_attempts ha
476         WHERE
477             u.id = ha.userid AND ha.hotpot=$hotpot->id
478         ORDER BY
479             u.lastname
480     ");
482     // get context
483     $cm = get_coursemodule_from_instance('hotpot', $hotpot->id);
484     $modulecontext = get_context_instance(CONTEXT_MODULE, $cm->id);
486     // get teachers enrolled students
487     $teachers = get_users_by_capability($modulecontext, 'mod/hotpot:viewreport', 'u.id,u.firstname,u.lastname', 'u.lastname');
488     $students = get_users_by_capability($modulecontext, 'mod/hotpot:attempt', 'u.id,u.firstname,u.lastname', 'u.lastname');
490     // current students
491     if (!empty($students)) {
492         $firsttime = true;
493         foreach ($students as $user) {
494             if (isset($users[$user->id])) {
495                 if ($firsttime) {
496                     $firsttime = false; // so we only do this once
497                     $menus['reportusers']['existingstudents'] = get_string('existingstudents');
498                     $menus['reportusers'][] = '------';
499                 }
500                 $menus['reportusers']["$user->id"] = fullname($user);
501                 unset($users[$user->id]);
502             }
503         }
504     }
505     // others (former students, teachers, admins, course creators)
506     if (!empty($users)) {
507         $firsttime = true;
508         foreach ($users as $user) {
509             if ($firsttime) {
510                 $firsttime = false; // so we only do this once
511                 $menus['reportusers'][] = '======';
512             }
513             $menus['reportusers']["$user->id"] = fullname($user);
514         }
515     }
517     $menus['reportattempts'] = array(
518         'all' => get_string('attemptsall', 'hotpot'),
519         'best' => get_string('attemptsbest', 'hotpot'),
520         'first' => get_string('attemptsfirst', 'hotpot'),
521         'last' => get_string('attemptslast', 'hotpot')
522     );
524     print '<tr><td>';
525     helpbutton('reportcontent', get_string('reportcontent', 'hotpot'), 'hotpot');
526     print '</td><th align="right" scope="col">'.get_string('reportcontent', 'hotpot').':</th><td colspan="7">';
527     foreach ($menus as $name => $options) {
528         $value = $formdata[$name];
529         print choose_from_menu($options, $name, $value, "", "", 0, true);
530     };
531     print '<input type="submit" value="'.get_string('reportbutton', 'hotpot').'" /></td></tr>';
533     $menus = array();
535     $menus['reportformat'] = array();
536     $menus['reportformat']['htm'] = get_string('reportformathtml', 'hotpot');
537     if (file_exists("$CFG->libdir/excel") || file_exists("$CFG->libdir/excellib.class.php")) {
538         $menus['reportformat']['xls'] = get_string('reportformatexcel', 'hotpot');
539     }
540     $menus['reportformat']['txt'] = get_string('reportformattext', 'hotpot');
542     if (trim($CFG->hotpot_excelencodings)) {
543         $menus['reportencoding'] = array(get_string('none')=>'');
545         $encodings = explode(',', $CFG->hotpot_excelencodings);
546         foreach ($encodings as $encoding) {
548             $encoding = trim($encoding);
549             if ($encoding) {
550                 $menus['reportencoding'][$encoding] = $encoding;
551             }
552         }
553     }
554     $menus['reportwrapdata'] = array(
555         '1' => get_string('yes'),
556         '0'  => get_string('no'),
557     );
558     $menus['reportshowlegend'] = array(
559         '1' => get_string('yes'),
560         '0'  => get_string('no'),
561     );
563     print '<tr><td>';
564     helpbutton('reportformat', get_string('reportformat', 'hotpot'), 'hotpot');
565     print '</td>';
566     foreach ($menus as $name => $options) {
567         $value = $formdata[$name];
568         print '<th align="right" scope="col">'.get_string($name, 'hotpot').':</th><td>'.choose_from_menu($options, $name, $value, "", "", 0, true).'</td>';
569     }
570     print '</tr>';
572     print '</table>';
574     print '<hr size="1" noshade="noshade" />';
575     print '</form>'."\n";
577 function hotpot_get_report_names($names='') {
578     // $names : optional list showing required order reports names
580     $reports = array();
582     // convert $names to an array, if necessary (usually is)
583     if (!is_array($names)) {
584         $names = explode(',', $names);
585     }
587     $plugins = get_list_of_plugins('mod/hotpot/report');
588     foreach($names as $name) {
589         if (is_numeric($i = array_search($name, $plugins))) {
590             $reports[] = $name;
591             unset($plugins[$i]);
592         }
593     }
595     // append remaining plugins
596     $reports = array_merge($reports, $plugins);
598     return $reports;
600 function hotpot_get_report_users($course, $formdata) {
601     $users = array();
603     /// Check to see if groups are being used in this module
604     $groupmode = groupmode($course, $cm); //TODO: there is no $cm defined!
605     $currentgroup = setup_and_print_groups($course, $groupmode, "report.php?id=$cm->id&mode=simple");
607     $sort = "u.lastname ASC";
609     switch ($formdata['reportusers']) {
610         case 'students':
611             if ($currentgroup) {
612                 $users = get_group_students($currentgroup, $sort);
613             } else {
614                 $users = get_course_students($course->id, $sort);
615             }
616             break;
617         case 'all':
618             if ($currentgroup) {
619                 $users = get_group_users($currentgroup, $sort);
620             } else {
621                 $users = get_course_users($course->id, $sort);
622             }
623             break;
624     }
626     return $users;
628 function hotpot_get_records_groupby($function, $fieldnames, $table, $select, $groupby) {
629     // $function is an SQL aggregate function (MAX or MIN)
631     $fields = sql_concat_join("'_'", $fieldnames);
632     $fields = "$groupby, $function($fields) AS joinedvalues";
634     if ($fields) {
635         $records = get_records_sql("SELECT $fields FROM $table WHERE $select GROUP BY $groupby");
636     }
638     if (empty($fields) || empty($records)) {
639         $records = array();
640     }
642     $fieldcount = count($fieldnames);
644     foreach ($records as $id=>$record) {
645         if (empty($record->joinedvalues)) {
646             unset($records[$id]);
647         } else {
648             $values = explode('_', $record->joinedvalues);
650             for ($i=0; $i<$fieldcount; $i++) {
651                 $fieldname = $fieldnames[$i];
652                 $records[$id]->$fieldname = $values[$i];
653             }
654         }
655         unset($record->joinedvalues);
656     }
658     return $records;
660 ?>