libdir.'/tablelib.php'); optional_variable($id); // Course Module ID, or optional_variable($q); // quiz ID if ($id) { if (! $cm = get_record("course_modules", "id", $id)) { error("Course Module ID was incorrect"); } if (! $course = get_record("course", "id", $cm->course)) { error("Course is misconfigured"); } if (! $quiz = get_record("quiz", "id", $cm->instance)) { error("Course module is incorrect"); } } else { if (! $quiz = get_record("quiz", "id", $q)) { error("Course module is incorrect"); } if (! $course = get_record("course", "id", $quiz->course)) { error("Course is misconfigured"); } if (! $cm = get_coursemodule_from_instance("quiz", $quiz->id, $course->id)) { error("Course Module ID was incorrect"); } } require_login($course->id, false); if (!isteacher($course->id)) { error("You are not allowed to use this script"); } // if no questions have been set up yet redirect to edit.php if (!$quiz->questions and isteacheredit($course->id)) { redirect('edit.php?quizid='.$quiz->id); } // Upgrade any attempts that have not yet been upgraded to the // Moodle 1.5 model (they will not yet have the timestamp set) if ($attempts = get_records_sql("SELECT a.*". " FROM {$CFG->prefix}quiz_attempts a, {$CFG->prefix}quiz_states s". " WHERE a.quiz = '$quiz->id' AND s.attempt = a.id AND s.timestamp = 0")) { foreach ($attempts as $attempt) { quiz_upgrade_states($attempt); } } add_to_log($course->id, "quiz", "attempts", "attempts.php?id=$cm->id", "$quiz->id", "$cm->id"); /// Define some strings $strquizzes = get_string("modulenameplural", "quiz"); $strquiz = get_string("modulename", "quiz"); $strreallydel = addslashes(get_string('deleteattemptcheck','quiz')); $strnoattempts = get_string('noattempts','quiz'); $strtimeformat = get_string('strftimedatetime'); $strreviewquestion = get_string('reviewresponse', 'quiz'); /// Print the page header print_header_simple(format_string($quiz->name), "", "id\">$strquizzes -> ".format_string($quiz->name), "", "", true, update_module_button($cm->id, $course->id, $strquiz), navmenu($course, $cm)); /// Print the tabs $currenttab = 'attempts'; include('tabs.php'); /// 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)) { delete_records('quiz_attempts', 'id', $attemptid); delete_records('quiz_states', 'attempt', $attemptid); // 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 $currentgroup = setup_and_print_groups($course, $groupmode, "attempts.php?id=$cm->id&mode=overview"); } else { $currentgroup = false; } /// Get all students if ($currentgroup) { $users = get_group_students($currentgroup); } else { $users = get_course_students($course->id); } if(empty($users)) { print_heading($strnoattempts); return true; } /// Set table options if(!isset($SESSION->quiz_overview_table)) { $SESSION->quiz_overview_table = array('noattempts' => false, 'detailedmarks' => false); } 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; } } /// Print display options echo '
'; echo '
'; echo '

'.get_string('displayoptions', 'quiz').': '; echo ''; echo ''; echo ''; echo ' '; echo ' '; echo ''; echo '

'; echo '
'; echo '
'; /// 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) { $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}quiz_questions 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]); } } } /// 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/attempts.php?id='.$cm->id); $table->sortable(true); $table->collapsible(true); $table->initialbars(count($users)>20); $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(); // Construct the SQL $select = 'SELECT '.$db->Concat('u.id', '\'#\'', $db->IfNull('qa.attempt', '0')).' AS uniqueid, 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 '; $from = 'FROM mdl_user u LEFT JOIN mdl_quiz_attempts qa ON (u.id = qa.userid AND qa.quiz = '.$quiz->id.') '; $where = 'WHERE u.id IN ('.implode(',', array_keys($users)).') '; // Add extra limits if we 're not interested in students without attempts if(!$noattempts) { $where .= 'AND '.$db->IfNull('qa.attempt', '0').' != 0 '; } // 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 $total = count_records_sql('SELECT COUNT(DISTINCT('.$db->Concat('u.id', '\'#\'', $db->IfNull('qa.attempt', '0')).')) '.$from.$where); // 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 mdl_quiz_newest_states qns ON qns.attemptid = qa.id '. 'LEFT JOIN mdl_quiz_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); } $table->pagesize(10, $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 $attempts = get_records_sql($select.$from.$where.$sort.$limit); /// Build table rows if(!empty($attempts)) { foreach ($attempts as $attempt) { $picture = print_user_picture($attempt->userid, $course->id, $attempt->picture, false, true); $row = array( '', $picture, ''.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) { $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('quiz_newest_states', 'newgraded', 'attemptid', $attempt->attempt, 'questionid', $questionid)) { $grade = round(get_field('quiz_states', 'grade', 'id', $gradedstateid), $quiz->decimalpoints); } else { // This is an old-style attempt $grade = round(get_field('quiz_states', 'grade', 'attempt', $attempt->attempt, 'question', $questionid), $quiz->decimalpoints); } $row[] = link_to_popup_window ('/mod/quiz/reviewquestion.php?state='.$gradedstateid.'&number='.$questions[$questionid]->number, 'reviewquestion', $grade, 450, 650, $strreviewquestion, 'none', true); } } } $table->add_data($row); } /// Start form echo '
'; echo '
'; echo ''; /// Print table $table->print_html(); /// Print "Select all" etc. echo ''; echo '
'; echo ''.get_string('selectall', 'quiz').' / '; echo ''.get_string('selectnone', 'quiz').' '; echo '  '; $options = array('delete' => get_string('delete')); $menu = str_replace(' 0) submitFormById(\'attemptsform\');', '', true)); echo $menu; echo ''; echo ''; echo '
'; /// Close form echo '
'; } else { $table->print_html(); } /// Print footer print_footer($course); ?>