dirroot/mod/quiz/lib.php"); //require_once($CFG->libdir.'/questionlib.php'); require_once("{$CFG->dirroot}/question/editlib.php"); /// CONSTANTS /////////////////////////////////////////////////////////////////// /**#@+ * Options determining how the grades from individual attempts are combined to give * the overall grade for a user */ define("QUIZ_GRADEHIGHEST", "1"); define("QUIZ_GRADEAVERAGE", "2"); define("QUIZ_ATTEMPTFIRST", "3"); define("QUIZ_ATTEMPTLAST", "4"); $QUIZ_GRADE_METHOD = array ( QUIZ_GRADEHIGHEST => get_string("gradehighest", "quiz"), QUIZ_GRADEAVERAGE => get_string("gradeaverage", "quiz"), QUIZ_ATTEMPTFIRST => get_string("attemptfirst", "quiz"), QUIZ_ATTEMPTLAST => get_string("attemptlast", "quiz")); /**#@-*/ /// FUNCTIONS RELATED TO ATTEMPTS ///////////////////////////////////////// /** * Creates an object to represent a new attempt at a quiz * * Creates an attempt object to represent an attempt at the quiz by the current * user starting at the current time. The ->id field is not set. The object is * NOT written to the database. * @return object The newly created attempt object. * @param object $quiz The quiz to create an attempt for. * @param integer $attemptnumber The sequence number for the attempt. */ function quiz_create_attempt($quiz, $attemptnumber) { global $USER, $CFG; if (!$attemptnumber > 1 or !$quiz->attemptonlast or !$attempt = get_record('quiz_attempts', 'quiz', $quiz->id, 'userid', $USER->id, 'attempt', $attemptnumber-1)) { // we are not building on last attempt so create a new attempt $attempt->quiz = $quiz->id; $attempt->userid = $USER->id; $attempt->preview = 0; if ($quiz->shufflequestions) { $attempt->layout = quiz_repaginate($quiz->questions, $quiz->questionsperpage, true); } else { $attempt->layout = $quiz->questions; } } $timenow = time(); $attempt->attempt = $attemptnumber; $attempt->sumgrades = 0.0; $attempt->timestart = $timenow; $attempt->timefinish = 0; $attempt->timemodified = $timenow; $attempt->uniqueid = question_new_attempt_uniqueid(); return $attempt; } function quiz_get_user_attempt_unfinished($quizid, $userid) { // Returns an object containing an unfinished attempt (if there is one) return get_record("quiz_attempts", "quiz", $quizid, "userid", $userid, "timefinish", 0); } function quiz_get_user_attempts($quizid, $userid) { // Returns a list of all attempts by a user return get_records_select("quiz_attempts", "quiz = '$quizid' AND userid = '$userid' AND timefinish > 0", "attempt ASC"); } /// FUNCTIONS TO DO WITH QUIZ LAYOUT AND PAGES //////////////////////////////// /** * Returns a comma separated list of question ids for the current page * * @return string Comma separated list of question ids * @param string $layout The string representing the quiz layout. Each page is represented as a * comma separated list of question ids and 0 indicating page breaks. * So 5,2,0,3,0 means questions 5 and 2 on page 1 and question 3 on page 2 * @param integer $page The number of the current page. */ function quiz_questions_on_page($layout, $page) { $pages = explode(',0', $layout); return trim($pages[$page], ','); } /** * Returns a comma separated list of question ids for the quiz * * @return string Comma separated list of question ids * @param string $layout The string representing the quiz layout. Each page is represented as a * comma separated list of question ids and 0 indicating page breaks. * So 5,2,0,3,0 means questions 5 and 2 on page 1 and question 3 on page 2 */ function quiz_questions_in_quiz($layout) { return str_replace(',0', '', $layout); } /** * Returns the number of pages in the quiz layout * * @return integer Comma separated list of question ids * @param string $layout The string representing the quiz layout. */ function quiz_number_of_pages($layout) { return substr_count($layout, ',0'); } /** * Returns the first question number for the current quiz page * * @return integer The number of the first question * @param string $quizlayout The string representing the layout for the whole quiz * @param string $pagelayout The string representing the layout for the current page */ function quiz_first_questionnumber($quizlayout, $pagelayout) { // this works by finding all the questions from the quizlayout that // come before the current page and then adding up their lengths. global $CFG; $start = strpos($quizlayout, ','.$pagelayout.',')-2; if ($start > 0) { $prevlist = substr($quizlayout, 0, $start); return get_field_sql("SELECT sum(length)+1 FROM {$CFG->prefix}question WHERE id IN ($prevlist)"); } else { return 1; } } /** * Re-paginates the quiz layout * * @return string The new layout string * @param string $layout The string representing the quiz layout. * @param integer $perpage The number of questions per page * @param boolean $shuffle Should the questions be reordered randomly? */ function quiz_repaginate($layout, $perpage, $shuffle=false) { $layout = str_replace(',0', '', $layout); // remove existing page breaks $questions = explode(',', $layout); if ($shuffle) { srand((float)microtime() * 1000000); // for php < 4.2 shuffle($questions); } $i = 1; $layout = ''; foreach ($questions as $question) { if ($perpage and $i > $perpage) { $layout .= '0,'; $i = 1; } $layout .= $question.','; $i++; } return $layout.'0'; } /** * Print navigation panel for quiz attempt and review pages * * @param integer $page The number of the current page (counting from 0). * @param integer $pages The total number of pages. */ function quiz_print_navigation_panel($page, $pages) { //$page++; echo '
'; } /// FUNCTIONS TO DO WITH QUIZ GRADES ////////////////////////////////////////// /** * Creates an array of maximum grades for a quiz * * The grades are extracted from the quiz_question_instances table. * @return array Array of grades indexed by question id * These are the maximum possible grades that * students can achieve for each of the questions * @param integer $quiz The quiz object */ function quiz_get_all_question_grades($quiz) { global $CFG; $questionlist = quiz_questions_in_quiz($quiz->questions); if (empty($questionlist)) { return array(); } $instances = get_records_sql("SELECT question,grade,id FROM {$CFG->prefix}quiz_question_instances WHERE quiz = '$quiz->id'" . (is_null($questionlist) ? '' : "AND question IN ($questionlist)")); $list = explode(",", $questionlist); $grades = array(); foreach ($list as $qid) { if (isset($instances[$qid])) { $grades[$qid] = $instances[$qid]->grade; } else { $grades[$qid] = 1; } } return $grades; } function quiz_get_best_grade($quiz, $userid) { /// Get the best current grade for a particular user in a quiz if (!$grade = get_record('quiz_grades', 'quiz', $quiz->id, 'userid', $userid)) { return NULL; } return (round($grade->grade,$quiz->decimalpoints)); } /** * Save the overall grade for a user at a quiz in the quiz_grades table * * @return boolean Indicates success or failure. * @param object $quiz The quiz for which the best grade is to be calculated * and then saved. * @param integer $userid The id of the user to save the best grade for. Can be * null in which case the current user is assumed. */ function quiz_save_best_grade($quiz, $userid=null) { global $USER; // Assume the current user if $userid is null if (is_null($userid)) { $userid = $USER->id; } // Get all the attempts made by the user if (!$attempts = quiz_get_user_attempts($quiz->id, $userid)) { notify('Could not find any user attempts'); return false; } // Calculate the best grade $bestgrade = quiz_calculate_best_grade($quiz, $attempts); $bestgrade = $quiz->sumgrades ? (($bestgrade / $quiz->sumgrades) * $quiz->grade) : 0; $bestgrade = round($bestgrade, $quiz->decimalpoints); // Save the best grade in the database if ($grade = get_record('quiz_grades', 'quiz', $quiz->id, 'userid', $userid)) { $grade->grade = $bestgrade; $grade->timemodified = time(); if (!update_record('quiz_grades', $grade)) { notify('Could not update best grade'); return false; } } else { $grade->quiz = $quiz->id; $grade->userid = $userid; $grade->grade = $bestgrade; $grade->timemodified = time(); if (!insert_record('quiz_grades', $grade)) { notify('Could not insert new best grade'); return false; } } return true; } /** * Calculate the overall grade for a quiz given a number of attempts by a particular user. * * @return float The overall grade * @param object $quiz The quiz for which the best grade is to be calculated * @param array $attempts An array of all the attempts of the user at the quiz */ function quiz_calculate_best_grade($quiz, $attempts) { switch ($quiz->grademethod) { case QUIZ_ATTEMPTFIRST: foreach ($attempts as $attempt) { return $attempt->sumgrades; } break; case QUIZ_ATTEMPTLAST: foreach ($attempts as $attempt) { $final = $attempt->sumgrades; } return $final; case QUIZ_GRADEAVERAGE: $sum = 0; $count = 0; foreach ($attempts as $attempt) { $sum += $attempt->sumgrades; $count++; } return (float)$sum/$count; default: case QUIZ_GRADEHIGHEST: $max = 0; foreach ($attempts as $attempt) { if ($attempt->sumgrades > $max) { $max = $attempt->sumgrades; } } return $max; } } /** * Return the attempt with the best grade for a quiz * * Which attempt is the best depends on $quiz->grademethod. If the grade * method is GRADEAVERAGE then this function simply returns the last attempt. * @return object The attempt with the best grade * @param object $quiz The quiz for which the best grade is to be calculated * @param array $attempts An array of all the attempts of the user at the quiz */ function quiz_calculate_best_attempt($quiz, $attempts) { switch ($quiz->grademethod) { case QUIZ_ATTEMPTFIRST: foreach ($attempts as $attempt) { return $attempt; } break; case QUIZ_GRADEAVERAGE: // need to do something with it :-) case QUIZ_ATTEMPTLAST: foreach ($attempts as $attempt) { $final = $attempt; } return $final; default: case QUIZ_GRADEHIGHEST: $max = -1; foreach ($attempts as $attempt) { if ($attempt->sumgrades > $max) { $max = $attempt->sumgrades; $maxattempt = $attempt; } } return $maxattempt; } } /// OTHER QUIZ FUNCTIONS //////////////////////////////////////////////////// /** * Print a box with quiz start and due dates * * @param object $quiz */ function quiz_view_dates($quiz) { if (!$quiz->timeopen && !$quiz->timeclose) { return; } print_simple_box_start('center', '', '', '', 'generalbox', 'dates'); echo ''.get_string("quizopen", "quiz").': | '; echo ''.userdate($quiz->timeopen).' |
'.get_string("quizclose", "quiz").': | '; echo ''.userdate($quiz->timeclose).' |