moodle/mod/quiz/lib.php
2002-10-13 13:51:56 +00:00

494 lines
17 KiB
PHP

<?PHP // $Id$
/// Library of function for module quiz
/// CONSTANTS ///////////////////////////////////////////////////////////////////
define("GRADEHIGHEST", "1");
define("GRADEAVERAGE", "2");
define("ATTEMPTFIRST", "3");
define("ATTEMPTLAST", "4");
$QUIZ_GRADE_METHOD = array ( GRADEHIGHEST => get_string("gradehighest", "quiz"),
GRADEAVERAGE => get_string("gradeaverage", "quiz"),
ATTEMPTFIRST => get_string("attemptfirst", "quiz"),
ATTEMPTLAST => get_string("attemptlast", "quiz"));
define("SHORTANSWER", "1");
define("TRUEFALSE", "2");
define("MULTICHOICE", "3");
$QUIZ_QUESTION_TYPE = array ( SHORTANSWER => get_string("shortanswer", "quiz"),
TRUEFALSE => get_string("truefalse", "quiz"),
MULTICHOICE => get_string("multichoice", "quiz"));
/// FUNCTIONS ///////////////////////////////////////////////////////////////////
function quiz_add_instance($quiz) {
/// Given an object containing all the necessary data,
/// (defined by the form in mod.html) this function
/// will create a new instance and return the id number
/// of the new instance.
$quiz->timemodified = time();
# May have to add extra stuff in here #
return insert_record("quiz", $quiz);
}
function quiz_update_instance($quiz) {
/// Given an object containing all the necessary data,
/// (defined by the form in mod.html) this function
/// will update an existing instance with new data.
$quiz->timemodified = time();
$quiz->id = $quiz->instance;
# May have to add extra stuff in here #
return update_record("quiz", $quiz);
}
function quiz_delete_instance($id) {
/// Given an ID of an instance of this module,
/// this function will permanently delete the instance
/// and any data that depends on it.
if (! $quiz = get_record("quiz", "id", "$id")) {
return false;
}
$result = true;
# Delete any dependent records here #
if (! delete_records("quiz", "id", "$quiz->id")) {
$result = false;
}
return $result;
}
function quiz_user_outline($course, $user, $mod, $quiz) {
/// Return a small object with summary information about what a
/// user has done with a given particular instance of this module
/// Used for user activity reports.
/// $return->time = the time they did it
/// $return->info = a short text description
return $return;
}
function quiz_user_complete($course, $user, $mod, $quiz) {
/// Print a detailed representation of what a user has done with
/// a given particular instance of this module, for user activity reports.
return true;
}
function quiz_print_recent_activity(&$logs, $isteacher=false) {
/// Given a list of logs, assumed to be those since the last login
/// this function prints a short list of changes related to this module
/// If isteacher is true then perhaps additional information is printed.
/// This function is called from course/lib.php: print_recent_activity()
global $CFG, $COURSE_TEACHER_COLOR;
return $content; // True if anything was printed, otherwise false
}
function quiz_cron () {
/// Function to be run periodically according to the moodle cron
/// This function searches for things that need to be done, such
/// as sending out mail, toggling flags etc ...
global $CFG;
return true;
}
//////////////////////////////////////////////////////////////////////////////////////
/// Any other quiz functions go here. Each of them must have a name that
/// starts with quiz_
function quiz_print_question($number, $questionid, $grade, $courseid) {
/// Prints a quiz question, any format
if (!$question = get_record("quiz_questions", "id", $questionid)) {
notify("Error: Question not found!");
}
$stranswer = get_string("answer", "quiz");
$strmarks = get_string("marks", "quiz");
echo "<TABLE WIDTH=100% CELLSPACING=10><TR><TD NOWRAP WIDTH=100 VALIGN=top>";
echo "<P ALIGN=CENTER><B>$number</B><BR><FONT SIZE=1>$grade $strmarks</FONT></P>";
print_spacer(1,100);
echo "</TD><TD VALIGN=TOP>";
switch ($question->type) {
case SHORTANSWER:
if (!$options = get_record("quiz_shortanswer", "question", $question->id)) {
notify("Error: Missing question options!");
}
echo "<P>$question->question</P>";
if ($question->image) {
print_file_picture($question->image, $courseid, 200);
}
echo "<P ALIGN=RIGHT>$stranswer: <INPUT TYPE=TEXT NAME=q$question->id SIZE=20></P>";
break;
case TRUEFALSE:
if (!$options = get_record("quiz_truefalse", "question", $question->id)) {
notify("Error: Missing question options!");
}
if (!$true = get_record("quiz_answers", "id", $options->true)) {
notify("Error: Missing question answers!");
}
if (!$false = get_record("quiz_answers", "id", $options->false)) {
notify("Error: Missing question answers!");
}
if (!$true->answer) {
$true->answer = get_string("true", "quiz");
}
if (!$false->answer) {
$false->answer = get_string("false", "quiz");
}
echo "<P>$question->question</P>";
if ($question->image) {
print_file_picture($question->image, $courseid, 200);
}
echo "<P ALIGN=RIGHT>$stranswer:&nbsp;&nbsp;";
echo "<INPUT TYPE=RADIO NAME=\"q$question->id\" VALUE=\"$true->id\">$true->answer";
echo "&nbsp;&nbsp;&nbsp;";
echo "<INPUT TYPE=RADIO NAME=\"q$question->id\" VALUE=\"$false->id\">$false->answer</P>";
break;
case MULTICHOICE:
if (!$options = get_record("quiz_multichoice", "question", $question->id)) {
notify("Error: Missing question options!");
}
if (!$answers = get_records_list("quiz_answers", "id", $options->answers)) {
notify("Error: Missing question answers!");
}
echo "<P>$question->question</P>";
if ($question->image) {
print_file_picture($question->image, $courseid, 200);
}
echo "<TABLE ALIGN=right>";
echo "<TR><TD valign=top>$stranswer:&nbsp;&nbsp;</TD><TD>";
echo "<TABLE ALIGN=right>";
$answerids = explode(",", $options->answers);
foreach ($answerids as $key => $answerid) {
$answer = $answers[$answerid];
$qnum = $key + 1;
echo "<TR><TD valign=top>";
if ($options->single) {
echo "<INPUT TYPE=RADIO NAME=q$question->id VALUE=\"$answer->id\">";
} else {
echo "<INPUT TYPE=CHECKBOX NAME=q$question->id"."a$answer->id VALUE=\"$answer->id\">";
}
echo "</TD>";
echo "<TD valign=top>$qnum. $answer->answer</TD>";
echo "</TR>";
}
echo "</TABLE>";
echo "</TABLE>";
break;
default:
notify("Error: Unknown question type!");
}
echo "</TD></TR></TABLE>";
}
function quiz_print_quiz_questions($quiz, $results=NULL) {
// Prints a whole quiz on one page.
if (!$quiz->questions) {
error("No questions have been defined!", "view.php?id=$cm->id");
}
$questions = explode(",", $quiz->questions);
if (!$grades = get_records_list("quiz_question_grades", "question", $quiz->questions, "", "question,grade")) {
error("No grades were found for these questions!");
}
echo "<FORM METHOD=POST ACTION=attempt.php>";
echo "<INPUT TYPE=hidden NAME=q VALUE=\"$quiz->id\">";
foreach ($questions as $key => $questionid) {
print_simple_box_start("CENTER", "90%");
quiz_print_question($key+1, $questionid, $grades[$questionid]->grade, $course->id);
print_simple_box_end();
echo "<BR>";
}
echo "<CENTER><INPUT TYPE=submit VALUE=\"".get_string("savemyanswers", "quiz")."\"></CENTER>";
echo "</FORM>";
}
function quiz_get_user_attempts($quizid, $userid) {
// Returns a list of all attempts by a user
return get_records_sql("SELECT * FROM quiz_attempts WHERE quiz = '$quizid' and user = '$userid' ORDER by attempt ASC");
}
function quiz_get_best_grade($quizid, $userid) {
/// Get the best current grade for a particular user in a quiz
if (!$grade = get_record_sql("SELECT * FROM quiz_grades WHERE quiz = '$quizid' and user = '$userid'")) {
return 0;
}
return $grade->grade;
}
function quiz_get_grade_records($quiz) {
/// Gets all info required to display the table of quiz results
/// for report.php
return get_records_sql("SELECT qg.*, u.firstname, u.lastname, u.picture
FROM quiz_grades qg, user u
WHERE qg.quiz = '$quiz->id'
AND qg.user = u.id");
}
function quiz_save_best_grade($quiz, $user) {
/// Calculates the best grade out of all attempts at a quiz for a user,
/// and then saves that grade in the quiz_grades table.
if (!$attempts = quiz_get_user_attempts($quiz->id, $user->id)) {
return false;
}
$bestgrade = quiz_calculate_best_grade($quiz, $attempts);
$bestgrade = (($bestgrade / $quiz->sumgrades) * $quiz->grade);
if ($grade = get_record_sql("SELECT * FROM quiz_grades WHERE quiz='$quiz->id' AND user='$user->id'")) {
$grade->grade = $bestgrade;
$grade->timemodified = time();
if (!update_record("quiz_grades", $grade)) {
return false;
}
} else {
$grade->quiz = $quiz->id;
$grade->user = $user->id;
$grade->grade = $bestgrade;
$grade->timemodified = time();
if (!insert_record("quiz_grades", $grade)) {
return false;
}
}
return true;
}
function quiz_get_answer($question) {
// Given a question, returns the correct answers and grades
switch ($question->type) {
case SHORTANSWER; // Could be multiple answers
return get_records_sql("SELECT a.*, sa.case, g.grade
FROM quiz_shortanswer sa, quiz_answers a, quiz_question_grades g
WHERE sa.question = '$question->id'
AND sa.question = a.question
AND sa.question = g.question");
break;
case TRUEFALSE; // Should be always two answers
return get_records_sql("SELECT a.*, g.grade
FROM quiz_answers a, quiz_question_grades g
WHERE a.question = '$question->id'
AND a.question = g.question");
break;
case MULTICHOICE; // Should be multiple answers
return get_records_sql("SELECT a.*, mc.single, g.grade
FROM quiz_multichoice mc, quiz_answers a, quiz_question_grades g
WHERE mc.question = '$question->id'
AND mc.question = a.question
AND mc.question = g.question");
break;
default:
return false;
}
}
function quiz_calculate_best_grade($quiz, $attempts) {
/// Calculate the best grade for a quiz given a number of attempts by a particular user.
switch ($quiz->grademethod) {
case ATTEMPTFIRST:
foreach ($attempts as $attempt) {
return $attempt->sumgrades;
}
break;
case ATTEMPTLAST:
foreach ($attempts as $attempt) {
$final = $attempt->sumgrades;
}
return $final;
case GRADEAVERAGE:
$sum = 0;
$count = 0;
foreach ($attempts as $attempt) {
$sum += $attempt->sumgrades;
$count++;
}
return (float)$sum/$count;
default:
case GRADEHIGHEST:
$max = 0;
foreach ($attempts as $attempt) {
if ($attempt->sumgrades > $max) {
$max = $attempt->sumgrades;
}
}
return $max;
}
}
function quiz_save_attempt($quiz, $questions, $result, $attemptnum) {
/// Given a quiz, a list of attempted questions and a total grade
/// this function saves EVERYTHING so it can be reconstructed later
/// if necessary.
global $USER;
// First let's save the attempt record itself
$attempt->quiz = $quiz->id;
$attempt->user = $USER->id;
$attempt->attempt = $attemptnum;
$attempt->sumgrades = $result->sumgrades;
$attempt->timemodified = time();
if (!$attempt->id = insert_record("quiz_attempts", $attempt)) {
return false;
}
// Now let's save all the questions for this attempt
foreach ($questions as $question) {
$response->attempt = $attempt->id;
$response->question = $question->id;
$response->grade = $result->grades[$question->id];
if ($question->answer) {
$response->answer = implode(",",$question->answer);
} else {
$response->answer = "";
}
if (!insert_record("quiz_responses", $response)) {
return false;
}
}
return true;
}
function quiz_grade_attempt_results($quiz, $questions) {
/// Given a list of questions (including answers for each one)
/// this function does all the hard work of calculating the
/// grades for each question, as well as a total grade for
/// for the whole quiz. It returns everything in a structure
/// that looks like:
/// $result->sumgrades (sum of all grades for all questions)
/// $result->percentage (Percentage of grades that were correct)
/// $result->grade (final grade result for the whole quiz)
/// $result->grades[] (array of grades, indexed by question id)
/// $result->feedback[] (array of feedback arrays, indexed by question id)
if (!$questions) {
error("No questions!");
}
$result->sumgrades = 0;
foreach ($questions as $question) {
if (!$answers = quiz_get_answer($question)) {
error("No answer defined for question id $question->id!");
}
$grade = 0; // default
$feedback = array ();
switch ($question->type) {
case SHORTANSWER:
if ($question->answer) {
$question->answer = $question->answer[0];
} else {
$question->answer = NULL;
}
foreach($answers as $answer) { // There might be multiple right answers
$feedback[$answer->id] = $answer->feedback;
if (!$answer->case) { // Don't compare case
$answer->answer = strtolower($answer->answer);
$question->answer = strtolower($question->answer);
}
if ($question->answer == $answer->answer) {
$grade = (float)$answer->fraction * $answer->grade;
}
}
break;
case TRUEFALSE:
if ($question->answer) {
$question->answer = $question->answer[0];
} else {
$question->answer = NULL;
}
foreach($answers as $answer) { // There should be two answers (true and false)
$feedback[$answer->id] = $answer->feedback;
if ($question->answer == $answer->id) {
$grade = (float)$answer->fraction * $answer->grade;
}
}
break;
case MULTICHOICE:
foreach($answers as $answer) { // There will be multiple answers, perhaps more than one is right
$feedback[$answer->id] = $answer->feedback;
if ($question->answer) {
foreach ($question->answer as $questionanswer) {
if ($questionanswer == $answer->id) {
if ($answer->single) {
$grade = (float)$answer->fraction * $answer->grade;
continue;
} else {
$grade += (float)$answer->fraction * $answer->grade;
}
}
}
}
}
break;
}
if ($grade < 0.0) { // No negative grades
$grade = 0.0;
}
$result->grades[$question->id] = $grade;
$result->sumgrades += $grade;
$result->feedback[$question->id] = $feedback;
}
$result->percentage = ($result->sumgrades / $quiz->sumgrades);
$result->grade = $result->percentage * $quiz->grade;
return $result;
}
?>