libdir.'/tablelib.php'); class quiz_report extends quiz_default_report { function display($quiz, $cm, $course) { /// This function just displays the report global $CFG, $SESSION, $db, $QTYPES; /// Define some strings $strreallydel = addslashes(get_string('deleteattemptcheck','quiz')); $strnoattempts = get_string('noattempts','quiz'); $strnoattemptsonly = get_string('shownoattemptsonly', 'quiz'); $strattemptsonly = get_string('attemptsonly','quiz'); $strbothattempts = get_string('bothattempts','quiz'); $strtimeformat = get_string('strftimedatetime'); $strreviewquestion = get_string('reviewresponse', 'quiz'); /// Only print headers if not asked to download data if (!$download = optional_param('download', NULL)) { $this->print_header_and_tabs($cm, $course, $quiz, $reportmode="overview"); } /// Deal with actions $action = optional_param('action', ''); switch($action) { case 'delete': /// Some attempts need to be deleted // the following needs to be improved to delete all associated data as well $attemptids = isset($_POST['attemptid']) ? $_POST['attemptid'] : array(); if(!is_array($attemptids) || empty($attemptids)) { break; } foreach($attemptids as $num => $attemptid) { if(empty($attemptid)) { unset($attemptids[$num]); } } foreach($attemptids as $attemptid) { if ($todelete = get_record('quiz_attempts', 'id', $attemptid)) { // TODO: use function from questionlib.php to delete attempt delete_records('quiz_attempts', 'id', $attemptid); delete_records('question_states', 'attempt', $todelete->uniqueid); delete_records('question_sessions', 'attemptid', $todelete->uniqueid); // Search quiz_attempts for other instances by this user. // If none, then delete record for this quiz, this user from quiz_grades // else recalculate best grade $userid = $todelete->userid; if (!record_exists('quiz_attempts', 'userid', $userid, 'quiz', $quiz->id)) { delete_records('quiz_grades', 'userid', $userid,'quiz', $quiz->id); } else { quiz_save_best_grade($quiz, $userid); } } } break; } /// Check to see if groups are being used in this quiz if ($groupmode = groupmode($course, $cm)) { // Groups are being used if (!$download) { $currentgroup = setup_and_print_groups($course, $groupmode, "report.php?id=$cm->id&mode=overview"); } else { if (isset($_GET['group'])) { $changegroup = $_GET['group']; /// 0 or higher } else { $changegroup = -1; /// This means no group change was specified } $currentgroup = get_and_set_current_group($course, $groupmode, $changegroup); } } else { $currentgroup = false; } /// Set table options if(!isset($SESSION->quiz_overview_table)) { $SESSION->quiz_overview_table = array('noattempts' => false, 'detailedmarks' => false, 'pagesize' => 10); } foreach($SESSION->quiz_overview_table as $option => $value) { $urlparam = optional_param($option, NULL); if($urlparam === NULL) { $$option = $value; } else { $$option = $SESSION->quiz_overview_table[$option] = $urlparam; } } /// Now check if asked download of data if ($download) { $filename = clean_filename("$course->shortname ".format_string($quiz->name,true)); $sort = ''; $limit = ''; } /// Define table columns $tablecolumns = array('checkbox', 'picture', 'fullname', 'timestart', 'duration'); $tableheaders = array(NULL, '', get_string('fullname'), get_string('startedon', 'quiz'), get_string('attemptduration', 'quiz')); if ($quiz->grade and $quiz->sumgrades) { $tablecolumns[] = 'sumgrades'; $tableheaders[] = get_string('grade', 'quiz').'/'.$quiz->grade; } if($detailedmarks) { // we want to display marks for all questions // Start by getting all questions $questionlist = quiz_questions_in_quiz($quiz->questions); $questionids = explode(',', $questionlist); $sql = "SELECT q.*, i.grade AS maxgrade, i.id AS instance". " FROM {$CFG->prefix}question q,". " {$CFG->prefix}quiz_question_instances i". " WHERE i.quiz = '$quiz->id' AND q.id = i.question". " AND q.id IN ($questionlist)"; if (!$questions = get_records_sql($sql)) { error('No questions found'); } $number = 1; foreach($questionids as $key => $id) { if ($questions[$id]->length) { // Only print questions of non-zero length $tablecolumns[] = '$'.$id; $tableheaders[] = '#'.$number; $questions[$id]->number = $number; $number += $questions[$id]->length; } else { // get rid of zero length questions unset($questions[$id]); unset($questionids[$key]); } } } if (!$download) { // Set up the table $table = new flexible_table('mod-quiz-report-overview-report'); $table->define_columns($tablecolumns); $table->define_headers($tableheaders); $table->define_baseurl($CFG->wwwroot.'/mod/quiz/report.php?mode=overview&id='.$cm->id); $table->sortable(true); $table->collapsible(true); $table->column_suppress('picture'); $table->column_suppress('fullname'); $table->column_class('picture', 'picture'); $table->set_attribute('cellspacing', '0'); $table->set_attribute('id', 'attempts'); $table->set_attribute('class', 'generaltable generalbox'); // Start working -- this is necessary as soon as the niceties are over $table->setup(); } elseif ($download =='Excel') { require_once("$CFG->libdir/excellib.class.php"); $filename .= ".xls"; /// Creating a workbook $workbook = new MoodleExcelWorkbook("-"); /// Sending HTTP headers $workbook->send($filename); /// Creating the first worksheet $sheettitle = get_string('reportresponses','quiz_responses'); $myxls =& $workbook->add_worksheet($sheettitle); /// format types $format =& $workbook->add_format(); $format->set_bold(0); $formatbc =& $workbook->add_format(); $formatbc->set_bold(1); $formatbc->set_align('center'); $formatb =& $workbook->add_format(); $formatb->set_bold(1); $formaty =& $workbook->add_format(); $formaty->set_bg_color('yellow'); $formatc =& $workbook->add_format(); $formatc->set_align('center'); $formatr =& $workbook->add_format(); $formatr->set_bold(1); $formatr->set_color('red'); $formatr->set_align('center'); $formatg =& $workbook->add_format(); $formatg->set_bold(1); $formatg->set_color('green'); $formatg->set_align('center'); // Here starts workshhet headers $headers = array(get_string('fullname'), get_string('startedon', 'quiz'), get_string('attemptduration', 'quiz')); if ($quiz->grade and $quiz->sumgrades) { $headers[] = get_string('grade', 'quiz').'/'.$quiz->grade; } if($detailedmarks) { foreach ($questionids as $key=>$id) { $headers[] = '#'.$questions[$id]->number; } } $colnum = 0; foreach ($headers as $item) { $myxls->write(0,$colnum,$item,$formatbc); $colnum++; } $rownum=1; } elseif ($download=='CSV') { $filename .= ".txt"; header("Content-Type: application/download\n"); header("Content-Disposition: attachment; filename=\"$filename\""); header("Expires: 0"); header("Cache-Control: must-revalidate,post-check=0,pre-check=0"); header("Pragma: public"); $headers = get_string('fullname')."\t".get_string('startedon', 'quiz')."\t".get_string('attemptduration', 'quiz'); if ($quiz->grade and $quiz->sumgrades) { $headers .= "\t".get_string('grade', 'quiz')."/".$quiz->grade; } if($detailedmarks) { foreach ($questions as $question) { $headers .= "\t#".$question->number; } } echo $headers." \n"; } // Construct the SQL $select = 'SELECT '.$db->Concat('u.id', '\'#\'', $db->IfNull('qa.attempt', '0')).' AS uniqueid, '. 'qa.uniqueid as attemptuniqueid, qa.id AS attempt, u.id AS userid, u.firstname, u.lastname, u.picture, '. 'qa.sumgrades, qa.timefinish, qa.timestart, qa.timefinish - qa.timestart AS duration '; if ($course->id != SITEID) { // this is too complicated, so just do it for each of the four cases. if (!empty($currentgroup) && empty($noattempts)) { // we want a particular group and we only want to see students WITH attempts. // So join on groups_members and do an inner join on attempts. $from = 'FROM '.$CFG->prefix.'user u JOIN '.$CFG->prefix.'user_students us ON us.userid = u.id JOIN '.$CFG->prefix.'groups_members gm ON u.id = gm.userid '. 'JOIN '.$CFG->prefix.'quiz_attempts qa ON u.id = qa.userid AND qa.quiz = '.$quiz->id; $where = ' WHERE us.course = '.$course->id.' AND gm.groupid = '.$currentgroup.' AND qa.preview = 0'; } else if (!empty($currentgroup) && !empty($noattempts)) { // We want a particular group and we want to do something funky with attempts // So join on groups_members and left join on attempts... $from = 'FROM '.$CFG->prefix.'user u JOIN '.$CFG->prefix.'user_students us ON us.userid = u.id JOIN '.$CFG->prefix.'groups_members gm ON u.id = gm.userid '. 'LEFT JOIN '.$CFG->prefix.'quiz_attempts qa ON u.id = qa.userid AND qa.quiz = '.$quiz->id; $where = ' WHERE us.course = '.$course->id.' AND gm.groupid = '.$currentgroup.' AND qa.preview = 0'; if ($noattempts == 1) { // noattempts = 1 means only no attempts, so make the left join ask for only records where the right is null (no attempts) $where .= ' AND qa.userid IS NULL'; // show ONLY no attempts; } // no else, the left join is not filtered, which means we get both back. } else if (empty($currentgroup) && empty($noattempts)) { // We don't care about group, and we only want to see students WITH attempts. // So just do a striaght inner join on attempts, don't worry about the groups_members table $from = 'FROM '.$CFG->prefix.'user u JOIN '.$CFG->prefix.'quiz_attempts qa ON u.id = qa.userid '; $where = ' WHERE qa.quiz = '.$quiz->id.' AND qa.preview = 0'; } else if (empty($currentgroup) && !empty($noattempts)) { // We don't care about group, and we to do something funky with attempts // So do a left join on attempts $from = 'FROM '.$CFG->prefix.'user u JOIN '.$CFG->prefix.'user_students us ON us.userid = u.id LEFT JOIN '.$CFG->prefix.'quiz_attempts qa ON u.id = qa.userid AND qa.quiz = '.$quiz->id; $where = ' WHERE us.course = '.$course->id; if ($noattempts == 1) { // noattempts = 1 means only no attempts, so make the left join ask for only records where the right is null (no attempts) $where .= ' AND qa.userid IS NULL'; // show ONLY no attempts; } // no else, the left join is not filtered, which means we get both back. } $countsql = 'SELECT COUNT(DISTINCT('.$db->Concat('u.id', '\'#\'', $db->IfNull('qa.attempt', '0')).')) '.$from.$where; } else { if (empty($noattempts)) { $from = 'FROM '.$CFG->prefix.'user u JOIN '.$CFG->prefix.'quiz_attempts qa ON u.id = qa.userid '; $where = ' WHERE qa.quiz = '.$quiz->id.' AND qa.preview = 0'; $countsql = 'SELECT COUNT(DISTINCT('.$db->Concat('u.id', '\'#\'', $db->IfNull('qa.attempt', '0')).')) '.$from.$where; } } if (!$download) { // Add extra limits due to initials bar if($table->get_sql_where()) { $where .= ' AND '.$table->get_sql_where(); } // Count the records NOW, before funky question grade sorting messes up $from if (!empty($countsql)) { $totalinitials = count_records_sql($countsql); if ($table->get_sql_where()) { $countsql .= ' AND '.$table->get_sql_where(); } $total = count_records_sql($countsql); } // Add extra limits due to sorting by question grade if($sort = $table->get_sql_sort()) { $sortparts = explode(',', $sort); $newsort = array(); $questionsort = false; foreach($sortparts as $sortpart) { $sortpart = trim($sortpart); if(substr($sortpart, 0, 1) == '$') { if(!$questionsort) { $qid = intval(substr($sortpart, 1)); $select .= ', grade '; $from .= 'LEFT JOIN '.$CFG->prefix.'question_sessions qns ON qns.attemptid = qa.attemptuniqueid '. 'LEFT JOIN '.$CFG->prefix.'question_states qs ON qs.id = qns.newgraded '; $where .= ' AND ('.sql_isnull('qns.questionid').' OR qns.questionid = '.$qid.')'; $newsort[] = 'grade '.(strpos($sortpart, 'ASC')? 'ASC' : 'DESC'); $questionsort = true; } } else { $newsort[] = $sortpart; } } // Reconstruct the sort string $sort = ' ORDER BY '.implode(', ', $newsort); } // Fix some wired sorting if (empty($sort)) { $sort = ' ORDER BY uniqueid'; } // Now it is time to page the data if (!isset($pagesize) || ((int)$pagesize < 1) ) { $pagesize = 10; } $table->pagesize($pagesize, $total); if($table->get_page_start() !== '' && $table->get_page_size() !== '') { $limit = ' '.sql_paging_limit($table->get_page_start(), $table->get_page_size()); } else { $limit = ''; } } /// Fetch the attempts if (!empty($from)) { // if we're in the site course and displaying no attempts, it makes no sense to do the query. $attempts = get_records_sql($select.$from.$where.$sort.$limit); } else { $attempts = array(); } /// Build table rows if (!$download) { $table->initialbars($totalinitials>20); } if(!empty($attempts) || !empty($noattempts)) { if ($attempts) { foreach ($attempts as $attempt) { $picture = print_user_picture($attempt->userid, $course->id, $attempt->picture, false, true); // uncomment the commented lines below if you are choosing to show unenrolled users and // have uncommented the corresponding lines earlier in this script //if (in_array($attempt->userid, $unenrolledusers)) { // $userlink = ''.fullname($attempt).''; //} //else { $userlink = ''.fullname($attempt).''; //} if (!$download) { $row = array( '', $picture, $userlink, empty($attempt->attempt) ? '-' : ''.userdate($attempt->timestart, $strtimeformat).'', empty($attempt->attempt) ? '-' : (empty($attempt->timefinish) ? get_string('unfinished', 'quiz') : format_time($attempt->duration)) ); } else { $row = array(fullname($attempt), empty($attempt->attempt) ? '-' : userdate($attempt->timestart, $strtimeformat), empty($attempt->attempt) ? '-' : (empty($attempt->timefinish) ? get_string('unfinished', 'quiz') : format_time($attempt->duration)) ); } if ($quiz->grade and $quiz->sumgrades) { if (!$download) { $row[] = $attempt->sumgrades === NULL ? '-' : ''.round($attempt->sumgrades / $quiz->sumgrades * $quiz->grade,$quiz->decimalpoints).''; } else { $row[] = $attempt->sumgrades === NULL ? '-' : round($attempt->sumgrades / $quiz->sumgrades * $quiz->grade,$quiz->decimalpoints); } } if($detailedmarks) { if(empty($attempt->attempt)) { foreach($questionids as $questionid) { $row[] = '-'; } } else { foreach($questionids as $questionid) { if ($gradedstateid = get_field('question_sessions', 'newgraded', 'attemptid', $attempt->attemptuniqueid, 'questionid', $questionid)) { $grade = round(get_field('question_states', 'grade', 'id', $gradedstateid), $quiz->decimalpoints); } else { $grade = '--'; } if (!$download) { $row[] = link_to_popup_window ('/mod/quiz/reviewquestion.php?state='.$gradedstateid.'&number='.$questions[$questionid]->number, 'reviewquestion', $grade, 450, 650, $strreviewquestion, 'none', true); } else { $row[] = $grade; } } } } if (!$download) { $table->add_data($row); } elseif ($download == 'Excel') { $colnum = 0; foreach($row as $item){ $myxls->write($rownum,$colnum,$item,$format); $colnum++; } $rownum++; } elseif ($download=='CSV') { $text = implode("\t", $row); echo $text." \n"; } } } if (!$download) { /// Start form echo '
'; $options["download"] = "Excel"; print_single_button("report.php", $options, get_string("downloadexcel")); echo " | \n"; echo ''; $options["download"] = "CSV"; print_single_button('report.php', $options, get_string("downloadtext")); echo " | \n"; echo ""; helpbutton("download", get_string("download","quiz"), "quiz"); echo " | \n"; echo '