subanswer1 =subquestion2 -> subanswer2 =subquestion3 -> subanswer3 } Match the following countries with their corresponding capitals. { =Canada -> Ottawa =Italy -> Rome =Japan -> Tokyo =India -> New Delhi } // ===Numerical=== When was Ulysses S. Grant born? {#1822} What is the value of pi (to 3 decimal places)? {#3.1415:0.0005}. What is the value of pi (to 3 decimal places)? {#3.141..3.142}. When was Ulysses S. Grant born? {# =1822:0 =%50%1822:2} // OPTIONS // ===Line Comments=== // Subheading: Numerical questions below What's 2 plus 2? {#4} // ===Question Name=== ::Kanji Origins::Japanese characters originally came from what country? {=China} ::Thanksgiving Date::The American holiday of Thanksgiving is celebrated on the {~second ~third =fourth} Thursday of November. // ===Feedback=== What's the answer to this multiple-choice question?{ ~wrong answer#feedback comment on the wrong answer ~another wrong answer#feedback comment on this wrong answer =right answer#Very good!} Who's buried in Grant's tomb?{ =no one#excellent answer! =nobody#excellent answer!} // ===Specify text format=== [markdown]Who's buried in **Grant's tomb**?{ =no one#excellent answer! =nobody#excellent answer!} // ===Percentage Answer Weights=== Grant is buried in Grant's tomb.{FALSE#No one is buried in Grant's tomb.} Difficult question.{~wrong answer ~%50%half credit answer =full credit answer} ::Jesus' hometown::Jesus Christ was from { ~Jerusalem#This was an important city, but the wrong answer. ~%25%Bethlehem#He was born here, but not raised here. ~%50%Galilee#You need to be more specific. =Nazareth#Yes! That's right!}. ::Jesus' hometown:: Jesus Christ was from { =Nazareth#Yes! That's right! =%75%Nazereth#Right, but misspelled. =%25%Bethlehem#He was born here, but not raised here.} // ===Multiple Answers=== What two people are entombed in Grant's tomb? { ~No one ~%50%Grant ~%50%Grant's wife ~Grant's father } What two people are entombed in Grant's tomb? { ~%-50%No one ~%50%Grant ~%50%Grant's wife ~%-50%Grant's father } // ===Special Characters=== Which answer equals 5? { ~ \= 2 + 2 = \= 2 + 3 ~ \= 2 + 4 } ::GIFT Control Characters:: Which of the following is NOT a control character for the GIFT import format? { ~ \~ # \~ is a control character. ~ \= # \= is a control character. ~ \# # \# is a control character. ~ \{ # \{ is a control character. ~ \} # \} is a control character. = \\ # Correct! \\ (backslash) is not a control character. BUT, it is used to escape the control characters. So, to specify a literal backslash, you must escape it with a backslash (as shown in this example). } //-----------------------------------------// // Examples from gift/format.php. //-----------------------------------------// Who's buried in Grant's tomb?{~Grant ~Jefferson =no one} Grant is {~buried =entombed ~living} in Grant's tomb. Grant is buried in Grant's tomb.{FALSE} Who's buried in Grant's tomb?{=no one =nobody} When was Ulysses S. Grant born?{#1822:5} Match the following countries with their corresponding capitals.{=Canada->Ottawa =Italy->Rome =Japan->Tokyo} //-----------------------------------------// // More complicated examples. //-----------------------------------------// ::Grant's Tomb::Grant is { ~buried#No one is buried there. =entombed#Right answer! ~living#We hope not! } in Grant's tomb. Difficult multiple choice question.{ ~wrong answer #comment on wrong answer ~%50%half credit answer #comment on answer =full credit answer #well done!} ::Jesus' hometown (Short answer ex.):: Jesus Christ was from { =Nazareth#Yes! That's right! =%75%Nazereth#Right, but misspelled. =%25%Bethlehem#He was born here, but not raised here. }. //this comment will be ignored by the filter ::Numerical example:: When was Ulysses S. Grant born? {# =1822:0 #Correct! 100% credit =%50%1822:2 #He was born in 1822. You get 50% credit for being close. }"; class GIFT { public $text; public $questions; public function __construct($input) { $this->text = $input; $this->questions = $this->parseQuiz($input); } public function fixscape($str) { return strtr($str,array( '&126;' => '~', '&61;' => '=', '&35;' => '#', '&123;' => '{', '&125;' => '}', '&92;' => '\\', '[code]' => '
', '[br]' => '', )); } public function parseQuiz($input) { $escape = array( '\\'.'~' => '&126;', '\\'.'=' => '&61;', '\\'.'#' => '&35;', '\\'.'{' => '&123;', '\\'.'}' => '&125;', '\\'.'\\' => '&92;', ); $unescape = array_flip($escape); //echo "
', '[/code]' => '
"; //Break up each question chunk $input = explode(" ",$input); $result = array(); //Go over each question chunk foreach ($input as $line) { //Break it up line by line $line = explode(" ",trim($line)); //Kill comment lines $kill = ""; if (count($line) > 0) foreach ($line as $l) { if (substr($l,0,2) != "//") $kill .= " ".trim($l); } if (trim($kill)=="") continue; $line = $kill; //Escape all special characters $line = strtr($line, $escape); //Create the new question array $question = array(); //Find format if ($line[0] == "[") { $line = explode("]",$line,2); $format = substr($line[0],1); $format = trim($format); $format = strtolower($format); if (!in_array($format,array('moodle','html','markdown','plain'))) { $question["type"] = "error"; $question["reason"] = 'Unknown format'; $question["raw"] = $kill; $result[] = $question; continue; } $question["format"] = $format; $line = $line[1]; } //Find the name if ($line[0].$line[1] == "::") { $line = explode("::",$line); //If there's an error if (count($line) != 3) { $question["type"] = "error"; $question["reason"] = 'Improperly formed name.'; $question["raw"] = $kill; $result[] = $question; continue; } $question["name"] = $line[1]; $line = $line[2]; } //Find the question block. $line = explode("{",$line,2); //If there is no question block, it's a comment. if (count($line) == 1) { $question["type"] = 'comment'; $question["comment"] = $line[0]; $result[] = $question; continue; } $before = trim($line[0]); $line = explode("}",$line[1]); //If there's an error if (count($line) > 2) { $question["type"] = "error"; $question["reason"] = 'Improperly formed question block.'; $question["raw"] = $kill; $result[] = $question; continue; } $choices = trim($line[0]); $after = trim($line[1]); $question["before"] = $before;//strtr($before,$unescape); $question["choices"] = $choices; $question["after"] = $after;///strtr($after,$unescape); $type = "unknown"; //Determine question type //Essay/short answer if(trim($choices) == "") { $type = 'essay'; } //Numerical elseif ($choices[0] == "#") { $type = "numerical"; $choices = trim(substr($choices,1)); //If there are multiple correct or partial answers if ($choices[0] == "=") { $choices = explode("=",substr($choices,1)); } else $choices = array($choices); foreach ($choices as $c) { $c = trim($c); $credit = 100; //If there's partial credit if ($c[0] == "%") { $c = explode("%",substr($c,1),2); $credit = $c[0]; $c = $c[1]; } //Snap off any comments $comment = ""; $c = explode("#",$c,2); if (count($c) > 1) { $comment = trim($c[1]); $comment = $comment;//strtr($comment, $unescape); } $c = $c[0]; //Check for ranges //Plus/minus $plusminus = ""; $c = explode(":",$c,2); if (count($c) > 1) { $plusminus = trim($c[1]); } $c = $c[0]; //From/to $fromto = ""; $c = explode("..",$c,2); if (count($c) > 1) { $fromto = trim($c[1]); } $c = $c[0]; //Store it $question["answers"][] = array( 'value' => $c, 'credit' => $credit, 'plusminus' => $plusminus, 'fromto' => $fromto, 'comment' => $comment, ); } } //True/false elseif ( in_array($choices,array("T","F","TRUE","FALSE")) || substr($choices,0,2) == "T#" || substr($choices,0,2) == "F#" || substr($choices,0,5) == "TRUE#" || substr($choices,0,6) == "FALSE#" ) { $type = "truefalse"; //Snap off any comments $comment = ""; $choices = explode("#",$choices,2); if (count($choices) > 1) { $comment = trim($choices[1]); $comment = $comment;//strtr($comment, $unescape); } $choices = $choices[0]; $question["answer"] = $choices[0]; $question["comment"] = $comment; } //Different choices... elseif ($choices[0] == "~" || $choices[0] == "=") { $tildes = substr_count($choices,"~"); $equals = substr_count($choices,"="); $darts = substr_count($choices,"->"); //Multiple choice, one answer if ($equals == 1 && $tildes >= 1) { $type = "multichoice"; } //Multiple choice, multi-select elseif ($equals == 0 && $tildes >= 1) { $type = "multianswer"; } //Fill in the blank elseif ($equals >= 1 && $tildes == 0 && $darts == 0) { $type = "fillblank"; } //Matching elseif ($equals >= 1 && $tildes == 0 && $darts==$equals) { $type = "matching"; $choices = explode("=",substr($choices,1)); foreach ($choices as $c) { $c = trim($c); //Break apart the darts $comment = ""; $c = explode("->",$c,2); $c[0] = strtr($c[0]);//trim(strtr($c[0],$unescape)); $c[1] = strtr($c[1]);//trim(strtr($c[1],$unescape)); //Store it $question["answers"][] = array( 'value' => $c[0], 'match' => $c[1] ); } } //If it's not matching, chunk it if (in_array($type,array("multichoice","multianswer","fillblank"))) { $choices = preg_split( "/(~|=)/", $choices, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); for ($i=0; $i1) { $comment = trim($c[1]); $comment = $comment;//strtr($comment, $unescape); } $c = $c[0]; //Store it $question["answers"][] = array( 'value' => $c, 'credit' => $credit, 'comment' => $comment, ); } } } $question["type"] = $type; //Unescape everything $question["before"] = $this->fixscape($question["before"]); $question["after"] = $this->fixscape($question["after"]); if (isset($question["answers"])) if (count($question["answers"]) > 0) { foreach ($question["answers"] as $a => $r) { $question["answers"][$a]['value'] = $this->fixscape($question["answers"][$a]['value']); if (isset($question["answers"][$a]['comment'])) $question["answers"][$a]['comment'] = $this->fixscape($question["answers"][$a]['comment']); } } //Add it $result[] = $question; } return $result; } public function printQuiz() { $q = $this->questions; foreach ($q as $num => $r) { //echo " "; //print_r($r); //echo ""; //Multiple choice if ($r["type"] == "multichoice" || $r["type"] == "multianswer") { echo ''; echo ($num+1).') '; //Standard list if (strlen($r["before"]) > 0 && strlen($r["after"]) == 0) { echo $r["before"]; } //Fill in the blank else { echo $r["before"].' __________ '.$r["after"]; } echo '
'; echo ''; //Print out the answers foreach ($r["answers"] as $an => $answer) { echo '
'; if ($r["type"] == "multichoice") echo ''; else echo ''; echo ' '; echo $answer["value"]; echo ''; } echo ''; } //True/false elseif ($r["type"] == "truefalse") { echo ''; echo ($num+1).') '; echo $r["before"]; echo '
'; echo ''; echo '
'; echo ' True'; echo ''; echo ''; echo ' False'; echo ''; echo ''; } //Numerical or fill in the blank elseif ($r["type"] == "numerical" || $r["type"] == "fillblank") { echo ''; echo ($num+1).') '; //Standard list if (strlen($r["before"]) > 0 && strlen($r["after"]) == 0) { echo $r["before"]; } //Fill in the blank else { echo $r["before"].' __________ '.$r["after"]; } echo '
'; echo ''; echo '
'; echo ''; echo ''; echo ''; } //Essay elseif ($r["type"] == "essay") { echo ''; echo ($num+1).') '; echo $r["before"]; echo '
'; echo '
'; echo ''; echo ''; echo ''; } //Matching elseif ($r["type"] == "matching") { echo ''; echo ($num+1).') '; echo $r["before"]; //Gather answers $answers = array(); foreach ($r["answers"] as $k => $a) { $answers[] = array("key" => $k, "match" => $a["match"]); } shuffle($answers); echo '
'; foreach ($r["answers"] as $k => $a) { echo '
'; echo $a["value"]; echo ' '; echo ''; echo ''; } echo ''; } echo "\n\n"; } } //Grade a test and diplay it, graded //"return" return graded test in html //"print" print results and return stats //"stats" return stats public function gradeQuiz($a, $action = "print") { $q = $this->questions; $correct = 0; $ungradable = 0; $e = ""; ob_start(); foreach ($q as $num => $r) { if (!isset($a[$num])) continue; //echo ""; //print_r($r); //echo ""; $score = 0; //Multiple choice if ($r["type"] == "multichoice" || $r["type"] == "multianswer") { echo ''; echo ($num+1).') '; //Standard list if (strlen($r["before"]) > 0 && strlen($r["after"]) == 0) { echo $r["before"]; } //Fill in the blank else { echo $r["before"].' __________ '.$r["after"]; } echo '
'; echo ''; //Print out the answers foreach ($r["answers"] as $an => $answer) { echo '
'; if ($r["type"] == "multichoice") { $style = 'color:#000;'; $comment = ""; //If it's correct if ($answer["credit"] == "100") { //If they chose it if ($an == $a[$num]) { $style = 'color:#0C0; font-weight:bold;'; $score += $answer["credit"]; $comment = $answer["comment"]; } //If they didn't else { $style = 'color:#000; font-weight:bold; text-decoration:underline;'; } } //It's incorrect or partial credit else { //If they chose it if ($an == $a[$num]) { $style = 'color:#C00; text-decoration:line-through'; $score += $answer["credit"]; $comment = $answer["comment"]; } //If they didn't else { $style = 'color:#000;'; } } echo ''; echo $answer["value"]; echo ''; if ($comment != "") echo ' ('.$comment.')'; } else {//multichoice $style = 'color:#000;'; $comment = ""; //If it's correct if ($answer["credit"] > 0) { //If they chose it if (in_array($an,$a[$num])) { $style = 'color:#0C0; font-weight:bold;'; $score += $answer["credit"]; $comment = $answer["comment"]; } //If they didn't else { $style = 'color:#000; font-weight:bold; text-decoration:underline;'; } } //It's incorrect or partial credit else { //If they chose it if (in_array($an,$a[$num])) { $style = 'color:#C00; text-decoration:line-through'; $score += $answer["credit"]; $comment = $answer["comment"]; } //If they didn't else { $style = 'color:#000;'; } } echo ''; echo $answer["value"]; echo ''; if ($comment != "") echo ' ('.$comment.')'; } echo ''; } echo ''; } //True/false elseif ($r["type"] == "truefalse") { echo ''; echo ($num+1).') '; echo $r["before"]; echo '
'; echo ''; echo '
'; $style = "color:#C00; text-decoration:line-through;"; //If correct if ($r["answer"] == $a[$num]) { $style = "color:#0C0; font-weight:bold;"; $score = 100; } //If wrong else { $score = 0; } echo ''.($r["answer"]=="T"?"True":"False").''; if ($comment != "") echo ' ('.$comment.')'; echo ''; echo ''; } //Fill in the blank elseif ($r["type"] == "fillblank") { echo ''; echo ($num+1).') '; //Standard list if (strlen($r["before"]) > 0 && strlen($r["after"]) == 0) { echo $r["before"]; } //Fill in the blank else { echo $r["before"].' __________ '.$r["after"]; } echo '
'; echo ''; echo '
'; $good_answers = array(); $style = "color:#C00; text-decoration:line-through;"; //Check answers foreach ($r["answers"] as $an => $answer) { if (strtolower($a[$num]) == strtolower($answer["value"])) { $style = "color:#0C0; font-weight:bold;"; $score = 100; } else { $good_answers[] = ''.$answer["value"].''; } } echo ''.htmlspecialchars($a[$num]).''; if ($score != 100) { echo ''; echo ''; } //Numerical elseif ($r["type"] == "numerical") { echo '
'.implode(", ",$good_answers); } echo ''; echo ($num+1).') '; //Standard list if (strlen($r["before"]) > 0 && strlen($r["after"]) == 0) { echo $r["before"]; } //Fill in the blank else { echo $r["before"].' __________ '.$r["after"]; } echo '
'; echo ''; echo '
'; $style = "color:#C00; text-decoration:line-through;"; //Iterate over each possibility foreach ($r["answers"] as $cor => $subanswer) { //Calculate answer $rightAnswer = floatVal($subanswer["value"]); $minAnswer = $rightAnswer; $maxAnswer = $rightAnswer; if ($subanswer["plusminus"] != "") { $minAnswer -= floatVal($subanswer["plusminus"]); $maxAnswer += floatVal($subanswer["plusminus"]); } elseif ($subanswer["fromto"] != "") { $maxAnswer += floatVal($subanswer["fromto"]); } //If it's correct if ( ($a[$num] >= $minAnswer) & ($a[$num] <= $maxAnswer) ) { $score = $subanswer["credit"]; $comment = $comment = $r["answers"][$cor]["comment"]; break; } } if ($score == 100) { $style = "color:#0C0; font-weight:bold;"; } echo ''.$a[$num].''; if ($comment != "") echo ' ('.$comment.')'; if ($score == 0) { echo ''; echo ''; } //Essay elseif ($r["type"] == "essay") { $ungradable++; echo '
'; echo ''; echo $r["answers"][0]["value"]; if ($r["answers"][0]["plusminus"]) echo ' +/- '.$r["answers"][0]["plusminus"]; if ($r["answers"][0]["fromto"]) echo ' to '.$r["answers"][0]["fromto"]; echo ''; } echo ''; echo ($num+1).') '; echo $r["before"]. " (Essays are graded separately.)"; echo '
'; echo '
'; if (strlen(trim($a[$num])) > 0) echo htmlspecialchars($a[$num]); else echo '[No entry.]'; echo ''; echo ''; } //Matching elseif ($r["type"] == "matching") { echo ''; echo ($num+1).') '; echo $r["before"]; $numRight = 0; echo '
'; foreach ($r["answers"] as $k => $ma) { echo '
'; echo $ma["value"]; echo ' '; echo '-->'; //Correct! if ($a[$num][$k] == $k) { echo ' '; echo $ma["match"]; echo ''; $numRight++; } else { echo ' '; echo $r["answers"][$a[$num][$k]]["match"]; echo ''; echo ' '; echo $ma["match"]; echo ''; } echo ''; } echo ''; $score = $numRight/count($r["answers"]) * 100; } echo "\n\n"; $correct += $score/100; } $results = array( "score" => $correct, "outof" => count($q), "pending" => $ungradable, "percentage" => count($q) == 0 ? 0 : $correct/count($q), "final" => ($ungradable > 0)?true:false ); //"return" return graded test in html //"print" print results and return stats //"stats" return stats if ($action == "return") { return ob_get_clean(); } elseif ($action == "stats") { ob_end_clean(); return $results; } ob_end_flush(); return $results; } }//end class /* $GIFT = new GIFT($input); if (isset($_REQUEST["GIFTquestion"])) { $answers = serialize($_REQUEST["GIFTquestion"]); $answers = unserialize($answers); print_r($GIFT->grade($answers)); echo "Responses:". serialize($_REQUEST["GIFTquestion"]).'
'; } else { echo ''; }*/ ?>