Merge commit 'catalyst/MOODLE_19_STABLE' into mdl19-linuxchix
[moodle-linuxchix.git] / mod / hotpot / report.php
blob0d2362815849d6cc7f451a0799457492dd230eb9
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");
15 if (! $course = get_record("course", "id", $cm->course)) {
16 error("Course is misconfigured");
18 if (! $hotpot = get_record("hotpot", "id", $cm->instance)) {
19 error("Course module is incorrect");
22 } else {
23 if (! $hotpot = get_record("hotpot", "id", $hp)) {
24 error("Course module is incorrect");
26 if (! $course = get_record("course", "id", $hotpot->course)) {
27 error("Course is misconfigured");
29 if (! $cm = get_coursemodule_from_instance("hotpot", $hotpot->id, $course->id)) {
30 error("Course Module ID was incorrect");
34 // get the roles context for this course
35 $sitecontext = get_context_instance(CONTEXT_SYSTEM);
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);
43 // check user can access this hotpot activity
44 if (!hotpot_is_visible($cm)) {
45 print_error("activityiscurrentlyhidden");
48 // get report mode
49 if (has_capability('mod/hotpot:viewreport',$modulecontext)) {
50 $mode = optional_param('mode', 'overview', PARAM_ALPHA);
51 } else {
52 // ordinary students have no choice
53 $mode = 'overview';
56 // assemble array of form data
57 $formdata = array(
58 'mode' => $mode,
59 'reportusers' => has_capability('mod/hotpot:viewreport',$modulecontext) ? optional_param('reportusers', get_user_preferences('hotpot_reportusers', 'allusers'), PARAM_ALPHANUM) : 'this',
60 'reportattempts' => optional_param('reportattempts', get_user_preferences('hotpot_reportattempts', 'all'), PARAM_ALPHA),
61 'reportformat' => optional_param('reportformat', 'htm', PARAM_ALPHA),
62 'reportshowlegend' => optional_param('reportshowlegend', get_user_preferences('hotpot_reportshowlegend', '0'), PARAM_INT),
63 'reportencoding' => optional_param('reportencoding', get_user_preferences('hotpot_reportencoding', ''), PARAM_ALPHANUM),
64 'reportwrapdata' => optional_param('reportwrapdata', get_user_preferences('hotpot_reportwrapdata', '1'), PARAM_INT),
67 foreach ($formdata as $name=>$value) {
68 set_user_preference("hotpot_$name", $value);
71 /// Start the report
73 add_to_log($course->id, "hotpot", "report", "report.php?id=$cm->id&mode=$mode", "$hotpot->id", "$cm->id");
75 // print page header. if required
76 if ($formdata['reportformat']=='htm') {
77 hotpot_print_report_heading($course, $cm, $hotpot, $mode);
78 if (has_capability('mod/hotpot:viewreport',$modulecontext)) {
79 hotpot_print_report_selector($course, $hotpot, $formdata);
83 // delete selected attempts, if any
84 if (has_capability('mod/hotpot:deleteattempt',$modulecontext)) {
85 $del = optional_param('del', '', PARAM_ALPHA);
86 hotpot_delete_selected_attempts($hotpot, $del);
89 // check for groups
90 if (preg_match('/^group(\d*)$/', $formdata['reportusers'], $matches)) {
91 $formdata['reportusers'] = 'group';
92 $formdata['reportgroupid'] = 0;
93 // validate groupid
94 if ($groups = groups_get_all_groups($course->id)) {
95 if (isset($groups[$matches[1]])) {
96 $formdata['reportgroupid'] = $matches[1];
101 $user_ids = '';
102 $users = array();
104 switch ($formdata['reportusers']) {
106 case 'allusers':
107 // anyone who has ever attempted this hotpot
108 if ($records = get_records_select('hotpot_attempts', "hotpot=$hotpot->id", '', 'id,userid')) {
109 foreach ($records as $record) {
110 $users[$record->userid] = 0; // "0" means user is NOT currently allowed to attempt this HotPot
112 unset($records);
114 break;
116 case 'group':
117 // group members
118 if ($members = groups_get_members($formdata['reportgroupid'])) {
119 foreach ($members as $memberid=>$unused) {
120 $users[$memberid] = 1; // "1" signifies currently recognized participant
123 break;
125 case 'allparticipants':
126 // anyone currently allowed to attempt this HotPot
127 if ($records = get_users_by_capability($modulecontext, 'mod/hotpot:attempt', 'u.id,u.id', 'u.id')) {
128 foreach ($records as $record) {
129 $users[$record->id] = 1; // "1" means user is allowed to do this HotPot
131 unset($records);
133 break;
135 case 'existingstudents':
136 // anyone currently allowed to attempt this HotPot who is not a teacher
137 $teachers = get_users_by_capability($modulecontext, 'mod/hotpot:viewreport', 'u.id,u.id', 'u.id');
138 if ($records = get_users_by_capability($modulecontext, 'mod/hotpot:attempt', 'u.id,u.id', 'u.id')) {
139 foreach ($records as $record) {
140 if (empty($teachers[$record->id])) {
141 $users[$record->id] = 1;
145 break;
147 case 'this': // current user only
148 $user_ids = $USER->id;
149 break;
151 default: // specific user selected by teacher
152 if (is_numeric($formdata['reportusers'])) {
153 $user_ids = $formdata['reportusers'];
156 if (empty($user_ids) && count($users)) {
157 ksort($users);
158 $user_ids = join(',', array_keys($users));
160 if (empty($user_ids)) {
161 print_heading(get_string('nousersyet'));
162 exit;
165 // database table and selection conditions
166 $table = "{$CFG->prefix}hotpot_attempts a";
167 $select = "a.hotpot=$hotpot->id AND a.userid IN ($user_ids)";
168 if ($mode!='overview') {
169 $select .= ' AND a.status<>'.HOTPOT_STATUS_INPROGRESS;
172 // confine attempts if necessary
173 switch ($formdata['reportattempts']) {
174 case 'best':
175 $function = 'MAX';
176 $fieldnames = array('score', 'id', 'clickreportid');
177 $defaultvalue = 0;
178 break;
179 case 'first':
180 $function = 'MIN';
181 $fieldnames = array('timefinish', 'id', 'clickreportid');
182 $default_value = time();
183 break;
184 case 'last':
185 $function = 'MAX';
186 $fieldnames = array('timefinish', 'id', 'clickreportid');
187 $defaultvalue = time();
188 break;
189 default: // 'all' and any others
190 $function = '';
191 $fieldnames = array();
192 $defaultvalue = '';
193 break;
195 if (empty($function) || empty($fieldnames)) {
196 // do nothing (i.e. get ALL attempts)
197 } else {
198 $groupby = 'userid';
199 $records = hotpot_get_records_groupby($function, $fieldnames, $table, $select, $groupby);
201 $select = '';
202 if ($records) {
203 $ids = array();
204 foreach ($records as $record) {
205 $ids[] = $record->clickreportid;
207 if (count($ids)) {
208 $select = "a.clickreportid IN (".join(',', $ids).")";
213 // pick out last attempt in each clickreport series
214 if ($select) {
215 $cr_attempts = hotpot_get_records_groupby('MAX', array('timefinish', 'id'), $table, $select, 'clickreportid');
216 } else {
217 $cr_attempts = array();
220 $fields = 'a.*, u.firstname, u.lastname, u.picture';
221 if ($mode=='click') {
222 $fields .= ', u.idnumber';
223 } else {
224 // overview, simple and detailed reports
225 // get last attempt record in clickreport series
226 $ids = array();
227 foreach ($cr_attempts as $cr_attempt) {
228 $ids[] = $cr_attempt->id;
230 if (empty($ids)) {
231 $select = "";
232 } else {
233 $ids = array_unique($ids);
234 sort($ids);
235 $select = "a.id IN (".join(',', $ids).")";
239 $attempts = array();
241 if ($select) {
242 // add user information to SQL query
243 $select .= ' AND a.userid = u.id';
244 $table .= ", {$CFG->prefix}user u";
245 $order = "u.lastname, a.attempt, a.timefinish";
246 // get the attempts (at last!)
247 $attempts = get_records_sql("SELECT $fields FROM $table WHERE $select ORDER BY $order");
250 // stop now if no attempts were found
251 if (empty($attempts)) {
252 print_heading(get_string('noattemptstoshow','quiz'));
253 exit;
256 // get the questions
257 if (!$questions = get_records_select('hotpot_questions', "hotpot='$hotpot->id'")) {
258 $questions = array();
261 // get grades
262 $grades = hotpot_get_grades($hotpot, $user_ids);
264 // get list of attempts by user and set reference to last attempt in clickreport series
265 $users = array();
266 foreach ($attempts as $id=>$attempt) {
268 $userid = $attempt->userid;
270 if (!isset($users[$userid])) {
271 $users[$userid]->grade = isset($grades[$userid]) ? $grades[$userid] : '&nbsp;';
272 $users[$userid]->attempts = array();
275 $users[$userid]->attempts[] = &$attempts[$id];
277 if ($mode=='click') {
278 // shortcut to clickreportid (=the id of the FIRST attempt in this clickreport series)
279 $clickreportid = $attempt->clickreportid;
280 if (isset($cr_attempts[$clickreportid])) {
281 // store id and finish time of LAST attempt in this clickreport series
282 $attempts[$id]->cr_lastclick = $cr_attempts[$clickreportid]->id;
283 $attempts[$id]->cr_timefinish = $cr_attempts[$clickreportid]->timefinish;
288 if ($mode!='overview') {
290 // initialise details of responses to questions in these attempts
291 foreach ($attempts as $a=>$attempt) {
292 $attempts[$a]->responses = array();
294 foreach ($questions as $q=>$question) {
295 $questions[$q]->attempts = array();
298 // get reponses to these attempts
299 $attempt_ids = join(',',array_keys($attempts));
300 if (!$responses = get_records_sql("SELECT * FROM {$CFG->prefix}hotpot_responses WHERE attempt IN ($attempt_ids)")) {
301 $responses = array();
304 // ids of questions used in these responses
305 $questionids = array();
307 foreach ($responses as $response) {
308 // shortcuts to the attempt and question ids
309 $a = $response->attempt;
310 $q = $response->question;
312 // check the attempt and question objects exist
313 // (if they don't exist, something is very wrong!)
314 if (isset($attempts[$a]) || isset($questions[$q])) {
316 // add the response for this attempt
317 $attempts[$a]->responses[$q] = $response;
319 // add a reference from the question to the attempt which includes this question
320 $questions[$q]->attempts[] = &$attempts[$a];
322 // flag this id as being used
323 $questionids[$q] = true;
327 // remove unused questions
328 $questionids = array_keys($questionids);
329 foreach ($questions as $id=>$question) {
330 if (!in_array($id, $questionids)) {
331 unset($questions[$id]);
336 /// Open the selected hotpot report and display it
338 if (! is_readable("report/$mode/report.php")) {
339 error("Report not known (".clean_text($mode).")", $course_homeurl);
342 include("report/default.php"); // Parent class
343 include("report/$mode/report.php");
345 $report = new hotpot_report();
347 if (! $report->display($hotpot, $cm, $course, $users, $attempts, $questions, $formdata)) {
348 error("Error occurred during report processing!", $course_homeurl);
351 if ($formdata['reportformat']=='htm') {
352 print_footer($course);
355 //////////////////////////////////////////////
356 /// functions to delete attempts and responses
358 function hotpot_grade_heading($hotpot, $formdata) {
360 global $HOTPOT_GRADEMETHOD;
361 $grademethod = $HOTPOT_GRADEMETHOD[$hotpot->grademethod];
363 if ($hotpot->grade!=100) {
364 $grademethod = "$hotpot->grade x $grademethod/100";
366 if ($formdata['reportformat']=='htm') {
367 $grademethod = '<font size="1">'.$grademethod.'</font>';
369 $nl = $formdata['reportformat']=='htm' ? '<br />' : "\n";
370 return get_string('grade')."$nl($grademethod)";
372 function hotpot_delete_selected_attempts(&$hotpot, $del) {
374 $select = '';
375 switch ($del) {
376 case 'all' :
377 $select = "hotpot='$hotpot->id'";
378 break;
379 case 'abandoned':
380 $select = "hotpot='$hotpot->id' AND status=".HOTPOT_STATUS_ABANDONED;
381 break;
382 case 'selection':
383 $ids = array();
384 $data = (array)data_submitted();
385 foreach ($data as $name => $value) {
386 if (preg_match('/^box\d+$/', $name)) {
387 $ids[] = intval($value);
390 if (count($ids)) {
391 $select = "hotpot='$hotpot->id' AND clickreportid IN (".implode(',', $ids).")";
393 break;
396 // delete attempts using $select, if it is set
397 if ($select) {
399 $table = 'hotpot_attempts';
400 if ($attempts = get_records_select($table, $select)) {
402 hotpot_delete_and_notify($table, $select, get_string('attempts', 'quiz'));
404 $select = 'attempt IN ('.implode(',', array_keys($attempts)).')';
405 hotpot_delete_and_notify('hotpot_details', $select, get_string('rawdetails', 'hotpot'));
406 hotpot_delete_and_notify('hotpot_responses', $select, get_string('answer', 'quiz'));
408 // update grades for all users for this hotpot
409 hotpot_update_grades($hotpot);
415 //////////////////////////////////////////////
416 /// functions to print the report headings and
417 /// report selector menus
419 function hotpot_print_report_heading(&$course, &$cm, &$hotpot, &$mode) {
420 global $CFG;
421 $strmodulenameplural = get_string("modulenameplural", "hotpot");
422 $strmodulename = get_string("modulename", "hotpot");
424 $title = format_string($course->shortname) . ": $hotpot->name";
425 $heading = $course->fullname;
427 $modulecontext = get_context_instance(CONTEXT_MODULE, $cm->id);
428 if (has_capability('mod/hotpot:viewreport',$modulecontext)) {
429 if ($mode=='overview' || $mode=='simplestat' || $mode=='fullstat') {
430 $module = "quiz";
431 } else {
432 $module = "hotpot";
435 $navigation = build_navigation(get_string("report$mode", $module), $cm);
436 } else {
437 $navigation = build_navigation(get_string("report", "quiz"), $cm);
440 $button = update_module_button($cm->id, $course->id, $strmodulename);
441 print_header($title, $heading, $navigation, "", "", true, $button, navmenu($course, $cm));
442 $course_context = get_context_instance(CONTEXT_COURSE, $course->id);
443 if (has_capability('gradereport/grader:view', $course_context) && has_capability('moodle/grade:viewall', $course_context)) {
444 echo '<div class="allcoursegrades"><a href="' . $CFG->wwwroot . '/grade/report/grader/index.php?id=' . $course->id . '">'
445 . get_string('seeallcoursegrades', 'grades') . '</a></div>';
447 print_heading($hotpot->name);
449 function hotpot_print_report_selector(&$course, &$hotpot, &$formdata) {
451 global $CFG;
453 $reports = hotpot_get_report_names('overview,simplestat,fullstat');
455 print '<form method="post" action="'."$CFG->wwwroot/mod/hotpot/report.php?hp=$hotpot->id".'">';
456 print '<table cellpadding="2" align="center">';
458 $menus = array();
460 $menus['mode'] = array();
461 foreach ($reports as $name) {
462 if ($name=='overview' || $name=='simplestat' || $name=='fullstat') {
463 $module = "quiz"; // standard reports
464 } else if ($name=='click' && empty($hotpot->clickreporting)) {
465 $module = ""; // clickreporting is disabled
466 } else {
467 $module = "hotpot"; // custom reports
469 if ($module) {
470 $menus['mode'][$name] = get_string("report$name", $module);
474 $menus['reportusers'] = array(
475 'allusers' => get_string('allusers', 'hotpot'),
476 'allparticipants' => get_string('allparticipants')
479 // groups
480 if ($groups = groups_get_all_groups($course->id)) {
481 foreach ($groups as $gid => $group) {
482 $menus['reportusers']["group$gid"] = get_string('group').': '.format_string($group->name);
486 // get users who have ever atetmpted this HotPot
487 $users = get_records_sql("
488 SELECT
489 u.id, u.firstname, u.lastname
490 FROM
491 {$CFG->prefix}user u,
492 {$CFG->prefix}hotpot_attempts ha
493 WHERE
494 u.id = ha.userid AND ha.hotpot=$hotpot->id
495 ORDER BY
496 u.lastname
499 // get context
500 $cm = get_coursemodule_from_instance('hotpot', $hotpot->id);
501 $modulecontext = get_context_instance(CONTEXT_MODULE, $cm->id);
503 // get teachers enrolled students
504 $teachers = get_users_by_capability($modulecontext, 'mod/hotpot:viewreport', 'u.id,u.firstname,u.lastname', 'u.lastname');
505 $students = get_users_by_capability($modulecontext, 'mod/hotpot:attempt', 'u.id,u.firstname,u.lastname', 'u.lastname');
507 // current students
508 if (!empty($students)) {
509 $firsttime = true;
510 foreach ($students as $user) {
511 if (isset($users[$user->id])) {
512 if ($firsttime) {
513 $firsttime = false; // so we only do this once
514 $menus['reportusers']['existingstudents'] = get_string('existingstudents');
515 $menus['reportusers'][] = '------';
517 $menus['reportusers']["$user->id"] = fullname($user);
518 unset($users[$user->id]);
522 // others (former students, teachers, admins, course creators)
523 if (!empty($users)) {
524 $firsttime = true;
525 foreach ($users as $user) {
526 if ($firsttime) {
527 $firsttime = false; // so we only do this once
528 $menus['reportusers'][] = '======';
530 $menus['reportusers']["$user->id"] = fullname($user);
534 $menus['reportattempts'] = array(
535 'all' => get_string('attemptsall', 'hotpot'),
536 'best' => get_string('attemptsbest', 'hotpot'),
537 'first' => get_string('attemptsfirst', 'hotpot'),
538 'last' => get_string('attemptslast', 'hotpot')
541 print '<tr><td>';
542 helpbutton('reportcontent', get_string('reportcontent', 'hotpot'), 'hotpot');
543 print '</td><th align="right" scope="col">'.get_string('reportcontent', 'hotpot').':</th><td colspan="7">';
544 foreach ($menus as $name => $options) {
545 $value = $formdata[$name];
546 print choose_from_menu($options, $name, $value, "", "", 0, true);
548 print '<input type="submit" value="'.get_string('reportbutton', 'hotpot').'" /></td></tr>';
550 $menus = array();
552 $menus['reportformat'] = array();
553 $menus['reportformat']['htm'] = get_string('reportformathtml', 'hotpot');
554 if (file_exists("$CFG->libdir/excel") || file_exists("$CFG->libdir/excellib.class.php")) {
555 $menus['reportformat']['xls'] = get_string('reportformatexcel', 'hotpot');
557 $menus['reportformat']['txt'] = get_string('reportformattext', 'hotpot');
559 if (trim($CFG->hotpot_excelencodings)) {
560 $menus['reportencoding'] = array(get_string('none')=>'');
562 $encodings = explode(',', $CFG->hotpot_excelencodings);
563 foreach ($encodings as $encoding) {
565 $encoding = trim($encoding);
566 if ($encoding) {
567 $menus['reportencoding'][$encoding] = $encoding;
571 $menus['reportwrapdata'] = array(
572 '1' => get_string('yes'),
573 '0' => get_string('no'),
575 $menus['reportshowlegend'] = array(
576 '1' => get_string('yes'),
577 '0' => get_string('no'),
580 print '<tr><td>';
581 helpbutton('reportformat', get_string('reportformat', 'hotpot'), 'hotpot');
582 print '</td>';
583 foreach ($menus as $name => $options) {
584 $value = $formdata[$name];
585 print '<th align="right" scope="col">'.get_string($name, 'hotpot').':</th><td>'.choose_from_menu($options, $name, $value, "", "", 0, true).'</td>';
587 print '</tr>';
589 print '</table>';
591 print '<hr size="1" noshade="noshade" />';
592 print '</form>'."\n";
594 function hotpot_get_report_names($names='') {
595 // $names : optional list showing required order reports names
597 $reports = array();
599 // convert $names to an array, if necessary (usually is)
600 if (!is_array($names)) {
601 $names = explode(',', $names);
604 $plugins = get_list_of_plugins('mod/hotpot/report');
605 foreach($names as $name) {
606 if (is_numeric($i = array_search($name, $plugins))) {
607 $reports[] = $name;
608 unset($plugins[$i]);
612 // append remaining plugins
613 $reports = array_merge($reports, $plugins);
615 return $reports;
617 function hotpot_get_report_users($course, $formdata) {
618 $users = array();
620 /// Check to see if groups are being used in this module
621 $groupmode = groupmode($course, $cm); //TODO: there is no $cm defined!
622 $currentgroup = setup_and_print_groups($course, $groupmode, "report.php?id=$cm->id&mode=simple");
624 $sort = "u.lastname ASC";
626 switch ($formdata['reportusers']) {
627 case 'students':
628 if ($currentgroup) {
629 $users = get_group_students($currentgroup, $sort);
630 } else {
631 $users = get_course_students($course->id, $sort);
633 break;
634 case 'all':
635 if ($currentgroup) {
636 $users = get_group_users($currentgroup, $sort);
637 } else {
638 $users = get_course_users($course->id, $sort);
640 break;
643 return $users;
645 function hotpot_get_records_groupby($function, $fieldnames, $table, $select, $groupby) {
646 // $function is an SQL aggregate function (MAX or MIN)
648 $fields = sql_concat_join("'_'", $fieldnames);
649 $fields = "$groupby, $function($fields) AS joinedvalues";
651 if ($fields) {
652 $records = get_records_sql("SELECT $fields FROM $table WHERE $select GROUP BY $groupby");
655 if (empty($fields) || empty($records)) {
656 $records = array();
659 $fieldcount = count($fieldnames);
661 foreach ($records as $id=>$record) {
662 if (empty($record->joinedvalues)) {
663 unset($records[$id]);
664 } else {
665 $values = explode('_', $record->joinedvalues);
667 for ($i=0; $i<$fieldcount; $i++) {
668 $fieldname = $fieldnames[$i];
669 $records[$id]->$fieldname = $values[$i];
672 unset($record->joinedvalues);
675 return $records;