adding some strings
[moodle-linuxchix.git] / mod / hotpot / report / click / report.php
blobf3428e6f0597d33f0f9840818711b1b858699bb6
1 <?php // $Id$
2 /// Overview report just displays a big table of all the attempts
3 class hotpot_report extends hotpot_default_report {
4 function display(&$hotpot, &$cm, &$course, &$users, &$attempts, &$questions, &$options) {
5 global $CFG;
6 // create the tables
7 $tables = array();
8 $this->create_clickreport_table($hotpot, $cm, $course, $users, $attempts, $questions, $options, $tables);
9 // print the tables
10 $this->print_report($course, $hotpot, $tables, $options);
11 return true;
13 function create_clickreport_table(&$hotpot, &$cm, &$course, &$users, &$attempts, &$questions, &$options, &$tables) {
14 global $CFG;
15 $is_html = ($options['reportformat']=='htm');
16 // time and date format strings // date format strings
17 $strftimetime = '%H:%M:%S';
18 $strftimedate = get_string('strftimedate');
19 // get the current time and max execution time
20 $start_report_time = microtime();
21 $max_execution_time = ini_get('max_execution_time');
22 $correct = get_string('reportcorrectsymbol', 'hotpot');
23 $wrong = get_string('reportwrongsymbol', 'hotpot');
24 $nottried = get_string('reportnottriedsymbol', 'hotpot');
25 // shortcuts for font tags
26 $blank = $is_html ? '&nbsp;' : "";
27 // store question count
28 $questioncount = count($questions);
29 // array to map columns onto question ids ($col => $id)
30 $questionids = array_keys($questions);
31 // store exercise type
32 $exercisetype = $this->get_exercisetype($questions, $questionids, $blank);
33 // initialize details ('events' must go last)
34 $details = array('checks', 'status', 'answers', 'changes', 'hints', 'clues', 'events');
35 // initialize $table
36 unset($table);
37 $table->border = 1;
38 $table->width = '100%';
39 // initialize legend, if necessary
40 if (!empty($options['reportshowlegend'])) {
41 $table->legend = array();
43 // start $table headings
44 $this->set_head($options, $table, 'exercise');
45 $this->set_head($options, $table, 'user');
46 $this->set_head($options, $table, 'attempt');
47 $this->set_head($options, $table, 'click');
48 // store clicktype column number
49 $clicktype_col = count($table->head)-1;
50 // finish $table headings
51 $this->set_head($options, $table, 'details', $exercisetype, $details, $questioncount);
52 $this->set_head($options, $table, 'totals', $exercisetype);
53 // set align and wrap
54 $this->set_align_and_wrap($table);
55 // is link to review allowed?
56 $allow_review = ($is_html && (has_capability('mod/hotpot:viewreport',get_context_instance(CONTEXT_COURSE, $course->id)) || $hotpot->review));
57 // initialize array of data values
58 $this->data = array();
59 // set exercise data values
60 $this->set_data_exercise($cm, $course, $hotpot, $questions, $questionids, $questioncount, $blank);
61 // add details of users' responses
62 foreach ($users as $user) {
63 $this->set_data_user($options, $course, $user);
64 unset($clickreportid);
65 foreach ($user->attempts as $attempt) {
66 // initialize totals for
67 $click = array(
68 'qnumber' => array(),
69 'correct' => array(),
70 'wrong' => array(),
71 'answers' => array(),
72 'hints' => array(),
73 'clues' => array(),
74 'changes' => array(),
75 'checks' => array(),
76 'events' => array(),
77 'score' => array(),
78 'weighting' => array()
80 $clicktypes = array();
81 // is the start of a new attempt?
82 // (clicks in the same attempt have the same clickreportid)
83 if (!isset($clickreportid) || $clickreportid != $attempt->clickreportid) {
84 $clickcount = 1;
85 $clickreportid = $attempt->clickreportid;
86 // initialize totals for all clicks in this attempt
87 $clicks = $click; // $click has just been initialized
88 $this->set_data_attempt($attempt, $strftimedate, $strftimetime, $blank);
90 $cells = array();
91 $this->set_data($cells, 'exercise');
92 $this->set_data($cells, 'user');
93 $this->set_data($cells, 'attempt');
94 // get responses to questions in this attempt
95 foreach ($attempt->responses as $response) {
96 // set $q(uestion number)
97 $q = array_search($response->question, $questionids);
98 $click['qnumber'][$q] = true;
99 // was this question answered correctly?
100 if ($answer = hotpot_strings($response->correct)) {
101 // mark the question as correctly answered
102 if (empty($clicks['correct'][$q])) {
103 $click['correct'][$q] = true;
104 $clicks['correct'][$q] = true;
106 // unset 'wrong' flags, if necessary
107 if (isset($click['wrong'][$q])) {
108 unset($click['wrong'][$q]);
110 if (isset($clicks['wrong'][$q])) {
111 unset($clicks['wrong'][$q]);
113 // otherwise, was the question answered wrongly?
114 } else if ($answer = hotpot_strings($response->wrong)) {
115 // mark the question as wrongly answered
116 $click['wrong'][$q] = true;
117 $clicks['wrong'][$q] = true;
118 } else { // not correct or wrong (curious?!)
119 unset($answer);
121 if (!empty($click['correct'][$q]) || !empty($click['wrong'][$q])) {
122 $click['score'][$q] = $response->score;
123 $clicks['score'][$q] = $response->score;
124 $weighting = isset($response->weighting) ? $response->weighting : 100;
125 $click['weighting'][$q] = $weighting;
126 $clicks['weighting'][$q] =$weighting;
128 foreach($details as $detail) {
129 switch ($detail) {
130 case 'answers':
131 if (isset($answer) && is_string($answer) && !empty($answer)) {
132 $click[$detail][$q] = $answer;
134 break;
135 case 'hints':
136 case 'clues':
137 case 'checks':
138 if (isset($response->$detail) && is_numeric($response->$detail) && $response->$detail>0) {
139 if (!isset($click[$detail][$q]) || $click[$detail][$q] < $response->$detail) {
140 $click[$detail][$q] = $response->$detail;
143 break;
145 } // end foreach $detail
146 } // end foreach $response
147 $click['types'] = array();
148 $this->data['details'] = array();
149 foreach($details as $detail) {
150 for ($q=0; $q<$questioncount; $q++) {
151 switch ($detail) {
152 case 'status':
153 if (isset($clicks['correct'][$q])) {
154 $this->data['details'][] = $correct;
155 } else if (isset($clicks['wrong'][$q])) {
156 $this->data['details'][] = $wrong;
157 } else if (isset($click['qnumber'][$q])) {
158 $this->data['details'][] = $nottried;
159 } else { // this question did not appear in this attempt
160 $this->data['details'][] = $blank;
162 break;
163 case 'answers':
164 case 'hints':
165 case 'clues':
166 case 'checks':
167 if (!isset($clicks[$detail][$q])) {
168 if (!isset($click[$detail][$q])) {
169 $this->data['details'][] = $blank;
170 } else {
171 $clicks[$detail][$q] = $click[$detail][$q];
172 if ($detail=='answers') {
173 $this->set_legend($table, $q, $click[$detail][$q], $questions[$questionids[$q]]);
175 $this->data['details'][] = $click[$detail][$q];
176 $this->update_event_count($click, $detail, $q);
178 } else {
179 if (!isset($click[$detail][$q])) {
180 $this->data['details'][] = $blank;
181 } else {
182 $difference = '';
183 if ($detail=='answers') {
184 if ($click[$detail][$q] != $clicks[$detail][$q]) {
185 $pattern = '/^'.preg_quote($clicks[$detail][$q], '/').',/';
186 $difference = preg_replace($pattern, '', $click[$detail][$q], 1);
188 } else { // hints, clues, checks
189 if ($click[$detail][$q] > $clicks[$detail][$q]) {
190 $difference = $click[$detail][$q] - $clicks[$detail][$q];
193 if ($difference) {
194 $clicks[$detail][$q] = $click[$detail][$q];
195 $click[$detail][$q] = $difference;
196 if ($detail=='answers') {
197 $this->set_legend($table, $q, $difference, $questions[$questionids[$q]]);
199 $this->data['details'][] = $difference;
200 $this->update_event_count($click, $detail, $q);
201 } else {
202 unset($click[$detail][$q]);
203 $this->data['details'][] = $blank;
207 break;
208 case 'changes':
209 case 'events':
210 if (empty($click[$detail][$q])) {
211 $this->data['details'][] = $blank;
212 } else {
213 $this->data['details'][] = $click[$detail][$q];
215 break;
216 default:
217 // do nothing
218 break;
219 } // end switch
220 } // for $q
221 } // foreach $detail
222 // set data cell values for
223 $this->set_data_click(
224 $allow_review ? '<a href="review.php?hp='.$hotpot->id.'&attempt='.$attempt->id.'">'.$clickcount.'</a>' : $clickcount,
225 trim(userdate($attempt->timefinish, $strftimetime)),
226 $exercisetype,
227 $click
229 $this->set_data($cells, 'click');
230 $this->set_data($cells, 'details');
231 $this->set_data_totals($click, $clicks, $questioncount, $blank, $attempt);
232 $this->set_data($cells, 'totals');
233 $table->data[] = $cells;
234 $clickcount++;
235 } // end foreach $attempt
236 // insert 'tabledivider' between users
237 $table->data[] = 'hr';
238 } // end foreach $user
239 // remove final 'hr' from data rows
240 array_pop($table->data);
241 if ($is_html && $CFG->hotpot_showtimes) {
242 $count = count($users);
243 $duration = sprintf("%0.3f", microtime_diff($start_report_time, microtime()));
244 print "$count users processed in $duration seconds (".sprintf("%0.3f", $duration/$count).' secs/user)<hr size="1" noshade="noshade" />'."\n";
246 $tables[] = &$table;
247 $this->create_legend_table($tables, $table);
248 } // end function
249 function get_exercisetype(&$questions, &$questionids, &$blank) {
250 if (empty($questions)) {
251 $type = $blank;
252 } else {
253 switch ($questions[$questionids[0]]->type) {
254 case HOTPOT_JCB:
255 $type = "JCB";
256 break;
257 case HOTPOT_JCLOZE :
258 $type = "JCloze";
259 break;
260 case HOTPOT_JCROSS :
261 $type = "JCross";
262 break;
263 case HOTPOT_JMATCH :
264 $type = "JMatch";
265 break;
266 case HOTPOT_JMIX :
267 $type = "JMix";
268 break;
269 case HOTPOT_JQUIZ :
270 $type = "JQuiz";
271 break;
272 case HOTPOT_TEXTOYS_RHUBARB :
273 $type = "Rhubarb";
274 break;
275 case HOTPOT_TEXTOYS_SEQUITUR :
276 $type = "Sequitur";
277 break;
278 default:
279 $type = $blank;
282 return $type;
284 function set_head(&$options, &$table, $zone, $exercisetype='', $details=array(), $questioncount=0) {
285 if (empty($table->head)) {
286 $table->head = array();
288 switch ($zone) {
289 case 'exercise':
290 array_push($table->head,
291 get_string('reportcoursename', 'hotpot'),
292 get_string('reportsectionnumber', 'hotpot'),
293 get_string('reportexercisenumber', 'hotpot'),
294 get_string('reportexercisename', 'hotpot'),
295 get_string('reportexercisetype', 'hotpot'),
296 get_string('reportnumberofquestions', 'hotpot')
298 break;
299 case 'user':
300 array_push($table->head,
301 get_string('reportstudentid', 'hotpot'),
302 get_string('reportlogindate', 'hotpot'),
303 get_string('reportlogintime', 'hotpot'),
304 get_string('reportlogofftime', 'hotpot')
306 break;
307 case 'attempt':
308 array_push($table->head,
309 get_string('reportattemptnumber', 'hotpot'),
310 get_string('reportattemptstart', 'hotpot'),
311 get_string('reportattemptfinish', 'hotpot')
313 break;
314 case 'click':
315 array_push($table->head,
316 get_string('reportclicknumber', 'hotpot'),
317 get_string('reportclicktime', 'hotpot'),
318 get_string('reportclicktype', 'hotpot')
320 break;
321 case 'details':
322 foreach($details as $detail) {
323 if ($exercisetype=='JQuiz' && $detail=='clues') {
324 $detail = 'showanswer';
326 $detail = get_string("report$detail", 'hotpot');
327 for ($i=0; $i<$questioncount; $i++) {
328 $str = get_string('questionshort', 'hotpot', $i+1);
329 if ($i==0 || $options['reportformat']!='htm') {
330 $str = "$detail $str";
332 $table->head[] = $str;
335 break;
336 case 'totals':
337 $reportpercentscore =get_string('reportpercentscore', 'hotpot');
338 if (!function_exists('clean_getstring_data')) { // Moodle 1.4 (and less)
339 $reportpercentscore = str_replace('%', '%%', $reportpercentscore);
341 array_push($table->head,
342 get_string('reportthisclick', 'hotpot', get_string('reportquestionstried', 'hotpot')),
343 get_string('reportsofar', 'hotpot', get_string('reportquestionstried', 'hotpot')),
344 get_string('reportthisclick', 'hotpot', get_string('reportright', 'hotpot')),
345 get_string('reportthisclick', 'hotpot', get_string('reportwrong', 'hotpot')),
346 get_string('reportthisclick', 'hotpot', get_string('reportnottried', 'hotpot')),
347 get_string('reportsofar', 'hotpot', get_string('reportright', 'hotpot')),
348 get_string('reportsofar', 'hotpot', get_string('reportwrong', 'hotpot')),
349 get_string('reportsofar', 'hotpot', get_string('reportnottried', 'hotpot')),
350 get_string('reportthisclick', 'hotpot', get_string('reportanswers', 'hotpot')),
351 get_string('reportthisclick', 'hotpot', get_string('reporthints', 'hotpot')),
352 get_string('reportthisclick', 'hotpot', get_string($exercisetype=='JQuiz' ? 'reportshowanswer' : 'reportclues', 'hotpot')),
353 get_string('reportthisclick', 'hotpot', get_string('reportevents', 'hotpot')),
354 get_string('reportsofar', 'hotpot', get_string('reporthints', 'hotpot')),
355 get_string('reportsofar', 'hotpot', get_string($exercisetype=='JQuiz' ? 'reportshowanswer' : 'reportclues', 'hotpot')),
356 get_string('reportthisclick', 'hotpot', get_string('reportrawscore', 'hotpot')),
357 get_string('reportthisclick', 'hotpot', get_string('reportmaxscore', 'hotpot')),
358 get_string('reportthisclick', 'hotpot', $reportpercentscore),
359 get_string('reportsofar', 'hotpot', get_string('reportrawscore', 'hotpot')),
360 get_string('reportsofar', 'hotpot', get_string('reportmaxscore', 'hotpot')),
361 get_string('reportsofar', 'hotpot', $reportpercentscore),
362 get_string('reporthotpotscore', 'hotpot')
364 break;
365 } // end switch
367 function set_align_and_wrap(&$table) {
368 $count = count($table->head);
369 for ($i=0; $i<$count; $i++) {
370 if ($i==0 || $i==1 || $i==2 || $i==4 || $i==5 || $i>=7) {
371 // numeric (and short text) columns
372 $table->align[] = 'center';
373 $table->wrap[] = '';
374 } else {
375 // text columns
376 $table->align[] = 'left';
377 $table->wrap[] = 'nowrap';
381 function set_data_exercise(&$cm, &$course, &$hotpot, &$questions, &$questionids, &$questioncount, &$blank) {
382 // get exercise details (course name, section number, activity number, quiztype and question count)
383 $record = get_record("course_sections", "id", $cm->section);
384 $this->data['exercise'] = array(
385 'course' => $course->shortname,
386 'section' => empty($record) ? $blank : $record->section+1,
387 'number' => empty($record) ? $blank : array_search($cm->id, explode(',', $record->sequence))+1,
388 'name' => $hotpot->name,
389 'type' => $this->get_exercisetype($questions, $questionids, $blank),
390 'questioncount' => $questioncount
393 function set_data_user(&$options, &$course, &$user) {
394 global $CFG;
395 // shortcut to first attempt record (which also hold user info)
396 $attempt = &$user->attempts[0];
397 $idnumber = $attempt->idnumber;
398 if (empty($idnumber)) {
399 $idnumber = fullname($attempt);
401 if ($options['reportformat']=='htm') {
402 $idnumber = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$attempt->userid.'&course='.$course->id.'">'.$idnumber.'</a>';
404 $this->data['user'] = array(
405 'idnumber' => $idnumber,
408 function set_data_attempt(&$attempt, &$strftimedate, &$strftimetime, &$blank) {
409 global $CFG;
410 $records = get_records_sql_menu("
411 SELECT userid, MAX(time) AS logintime
412 FROM {$CFG->prefix}log
413 WHERE userid=$attempt->userid AND action='login' AND time<$attempt->timestart
414 GROUP BY userid
416 if (empty($records)) {
417 $logindate = $blank;
418 $logintime = $blank;
419 } else {
420 $logintime = $records[$attempt->userid];
421 $logindate = trim(userdate($logintime, $strftimedate));
422 $logintime = trim(userdate($logintime, $strftimetime));
424 $records = get_records_sql_menu("
425 SELECT userid, MIN(time) AS logouttime
426 FROM {$CFG->prefix}log
427 WHERE userid=$attempt->userid AND action='logout' AND time>$attempt->cr_timefinish
428 GROUP BY userid
430 if (empty($records)) {
431 $logouttime = $blank;
432 } else {
433 $logouttime = $records[$attempt->userid];
434 $logouttime = trim(userdate($logouttime, $strftimetime));
436 $this->data['attempt'] = array(
437 'logindate' => $logindate,
438 'logintime' => $logintime,
439 'logouttime' => $logouttime,
440 'number' => $attempt->attempt,
441 'start' => trim(userdate($attempt->timestart, $strftimetime)),
442 'finish' => trim(userdate($attempt->cr_timefinish, $strftimetime)),
445 function set_data_click($number, $time, $exercisetype, $click) {
446 $types = array();
447 foreach (array_keys($click['types']) as $type) {
448 if ($exercisetype=='JQuiz' && $type=='clues') {
449 $type = 'showanswer';
450 } else {
451 // remove final 's'
452 $type = substr($type, 0, strlen($type)-1);
454 // $types[] = get_string($type, 'hotpot');
455 $types[] = $type;
457 $this->data['click'] = array(
458 'number' => $number,
459 'time' => $time,
460 'type' => empty($types) ? '??' : implode(',', $types)
463 function set_data_totals(&$click, &$clicks, &$questioncount, &$blank, &$attempt) {
464 $count= array(
465 'click' => array(
466 'correct' => count($click['correct']),
467 'wrong' => count($click['wrong']),
468 'answers' => count($click['answers']),
469 'hints' => array_sum($click['hints']),
470 'clues' => array_sum($click['clues']),
471 'events' => array_sum($click['events']),
472 'score' => array_sum($click['score']),
473 'maxscore' => array_sum($click['weighting']),
475 'clicks' => array(
476 'correct' => count($clicks['correct']),
477 'wrong' => count($clicks['wrong']),
478 'answers' => count($clicks['answers']),
479 'hints' => array_sum($clicks['hints']),
480 'clues' => array_sum($clicks['clues']),
481 'score' => array_sum($clicks['score']),
482 'maxscore' => array_sum($clicks['weighting']),
485 foreach ($count as $period=>$values) {
486 $count[$period]['nottried'] = $questioncount - ($values['correct'] + $values['wrong']);
487 $count[$period]['percent'] = empty($values['maxscore']) ? $blank : round(100 * $values['score'] / $values['maxscore'], 0);
488 // blank out zero click values
489 if ($period=='click') {
490 foreach ($values as $detail=>$value) {
491 if ($detail=='answers' || $detail=='hints' || $detail=='clues' || $detail=='events') {
492 if (empty($value)) {
493 $count[$period][$detail] = $blank;
499 $this->data['totals'] = array(
500 $count['click']['answers'], // "q's tried"
501 $count['clicks']['answers'], // "q's tried so far"
502 $count['click']['correct'], // "right"
503 $count['click']['wrong'], // "wrong"
504 $count['click']['nottried'], // "not tried"
505 $count['clicks']['correct'], // "right so far"
506 $count['clicks']['wrong'], // "wrong so far"
507 $count['clicks']['nottried'], // "not tried so far"
508 $count['click']['answers'], // "answers",
509 $count['click']['hints'], // "hints",
510 $count['click']['clues'], // "clues",
511 $count['click']['events'], // "answers",
512 $count['clicks']['hints'], // "hints so far",
513 $count['clicks']['clues'], // "clues so far",
514 $count['click']['score'], // 'raw score',
515 $count['click']['maxscore'], // 'max score',
516 $count['click']['percent'], // '% score'
517 $count['clicks']['score'], // 'raw score,
518 $count['clicks']['maxscore'], // 'max score,
519 $count['clicks']['percent'], // '% score
520 $attempt->score // 'hotpot score'
523 function update_event_count(&$click, $detail, $q) {
524 if ($detail=='checks' || $detail=='hints' || $detail=='clues') {
525 $click['types'][$detail] = true;
527 if ($detail=='answers' || $detail=='hints' || $detail=='clues') {
528 $click['events'][$q] = isset($click['events'][$q]) ? $click['events'][$q]+1 : 1;
530 if ($detail=='answers') {
531 $click['changes'][$q] = isset($click['changes'][$q]) ? $click['changes'][$q]+1 : 1;
534 function set_data(&$cells, $zone) {
535 foreach ($this->data[$zone] as $name=>$value) {
536 $cells[] = $value;
539 } // end class