class qformat_gift in Quiz 6.6
Same name and namespace in other branches
- 6.5 includes/moodle/question/format/gift/format.php \qformat_gift
@package questionbank @subpackage importexport
Hierarchy
- class \qformat_default
- class \qformat_gift
Expanded class hierarchy of qformat_gift
1 string reference to 'qformat_gift'
- qformat_gift::writequestion in includes/
moodle/ question/ format/ gift/ format.php - convert a single question object into text output in the given format. This must be overriden
File
- includes/
moodle/ question/ format/ gift/ format.php, line 38
View source
class qformat_gift extends qformat_default {
function provide_import() {
return true;
}
function provide_export() {
return true;
}
function answerweightparser(&$answer) {
$answer = substr($answer, 1);
// removes initial %
$end_position = strpos($answer, "%");
$answer_weight = substr($answer, 0, $end_position);
// gets weight as integer
$answer_weight = $answer_weight / 100;
// converts to percent
$answer = substr($answer, $end_position + 1);
// removes comment from answer
return $answer_weight;
}
function commentparser(&$answer) {
if (strpos($answer, "#") > 0) {
$hashpos = strpos($answer, "#");
$comment = substr($answer, $hashpos + 1);
$comment = addslashes(trim($this
->escapedchar_post($comment)));
$answer = substr($answer, 0, $hashpos);
}
else {
$comment = " ";
}
return $comment;
}
function split_truefalse_comment($comment) {
// splits up comment around # marks
// returns an array of true/false feedback
$bits = explode('#', $comment);
$feedback = array(
'wrong' => $bits[0],
);
if (count($bits) >= 2) {
$feedback['right'] = $bits[1];
}
else {
$feedback['right'] = '';
}
return $feedback;
}
function escapedchar_pre($string) {
//Replaces escaped control characters with a placeholder BEFORE processing
$escapedcharacters = array(
"\\:",
"\\#",
"\\=",
"\\{",
"\\}",
"\\~",
"\\n",
);
//dlnsk
$placeholders = array(
"&&058;",
"&&035;",
"&&061;",
"&&123;",
"&&125;",
"&&126;",
"&&010",
);
//dlnsk
$string = str_replace("\\\\", "&&092;", $string);
$string = str_replace($escapedcharacters, $placeholders, $string);
$string = str_replace("&&092;", "\\", $string);
return $string;
}
function escapedchar_post($string) {
//Replaces placeholders with corresponding character AFTER processing is done
$placeholders = array(
"&&058;",
"&&035;",
"&&061;",
"&&123;",
"&&125;",
"&&126;",
"&&010",
);
//dlnsk
$characters = array(
":",
"#",
"=",
"{",
"}",
"~",
"\n",
);
//dlnsk
$string = str_replace($placeholders, $characters, $string);
return $string;
}
function check_answer_count($min, $answers, $text) {
$countanswers = count($answers);
if ($countanswers < $min) {
$importminerror = get_string('importminerror', 'quiz');
$this
->error($importminerror, $text);
return false;
}
return true;
}
function readquestion($lines) {
// Given an array of lines known to define a question in this format, this function
// converts it into a question object suitable for processing and insertion into Moodle.
$question = $this
->defaultquestion();
$comment = NULL;
// define replaced by simple assignment, stop redefine notices
$gift_answerweight_regex = "^%\\-*([0-9]{1,2})\\.?([0-9]*)%";
// REMOVED COMMENTED LINES and IMPLODE
foreach ($lines as $key => $line) {
$line = trim($line);
if (substr($line, 0, 2) == "//") {
$lines[$key] = " ";
}
}
$text = trim(implode(" ", $lines));
if ($text == "") {
return false;
}
// Substitute escaped control characters with placeholders
$text = $this
->escapedchar_pre($text);
// Look for category modifier
if (ereg('^\\$CATEGORY:', $text)) {
// $newcategory = $matches[1];
$newcategory = trim(substr($text, 10));
// build fake question to contain category
$question->qtype = 'category';
$question->category = $newcategory;
return $question;
}
// QUESTION NAME parser
if (substr($text, 0, 2) == "::") {
$text = substr($text, 2);
$namefinish = strpos($text, "::");
if ($namefinish === false) {
$question->name = false;
// name will be assigned after processing question text below
}
else {
$questionname = substr($text, 0, $namefinish);
$question->name = addslashes(trim($this
->escapedchar_post($questionname)));
$text = trim(substr($text, $namefinish + 2));
// Remove name from text
}
}
else {
$question->name = false;
}
// FIND ANSWER section
// no answer means its a description
$answerstart = strpos($text, "{");
$answerfinish = strpos($text, "}");
$description = false;
if ($answerstart === false and $answerfinish === false) {
$description = true;
$answertext = '';
$answerlength = 0;
}
elseif (!($answerstart !== false and $answerfinish !== false)) {
$this
->error(get_string('braceerror', 'quiz'), $text);
return false;
}
else {
$answerlength = $answerfinish - $answerstart;
$answertext = trim(substr($text, $answerstart + 1, $answerlength - 1));
}
// Format QUESTION TEXT without answer, inserting "_____" as necessary
if ($description) {
$questiontext = $text;
}
elseif (substr($text, -1) == "}") {
// no blank line if answers follow question, outside of closing punctuation
$questiontext = substr_replace($text, "", $answerstart, $answerlength + 1);
}
else {
// inserts blank line for missing word format
$questiontext = substr_replace($text, "_____", $answerstart, $answerlength + 1);
}
// get questiontext format from questiontext
$oldquestiontext = $questiontext;
$questiontextformat = 0;
if (substr($questiontext, 0, 1) == '[') {
$questiontext = substr($questiontext, 1);
$rh_brace = strpos($questiontext, ']');
$qtformat = substr($questiontext, 0, $rh_brace);
$questiontext = substr($questiontext, $rh_brace + 1);
if (!($questiontextformat = text_format_name($qtformat))) {
$questiontext = $oldquestiontext;
}
}
$question->questiontextformat = $questiontextformat;
$question->questiontext = addslashes(trim($this
->escapedchar_post($questiontext)));
// set question name if not already set
if ($question->name === false) {
$question->name = $question->questiontext;
}
// ensure name is not longer than 250 characters
//$question->name = shorten_text( $question->name, 200 );
$question->name = substr($question->name, 0, 200);
$question->name = strip_tags(substr($question->name, 0, 250));
// determine QUESTION TYPE
$question->qtype = NULL;
// give plugins first try
// plugins must promise not to intercept standard qtypes
// MDL-12346, this could be called from lesson mod which has its own base class =(
if (method_exists($this, 'try_importing_using_qtypes') && ($try_question = $this
->try_importing_using_qtypes($lines, $question, $answertext))) {
return $try_question;
}
if ($description) {
$question->qtype = DESCRIPTION;
}
elseif ($answertext == '') {
$question->qtype = ESSAY;
}
elseif ($answertext[0] == "#") {
$question->qtype = NUMERICAL;
}
elseif (strpos($answertext, "~") !== false) {
// only Multiplechoice questions contain tilde ~
$question->qtype = MULTICHOICE;
}
elseif (strpos($answertext, "=") !== false && strpos($answertext, "->") !== false) {
// only Matching contains both = and ->
$question->qtype = MATCH;
}
else {
// either TRUEFALSE or SHORTANSWER
// TRUEFALSE question check
$truefalse_check = $answertext;
if (strpos($answertext, "#") > 0) {
// strip comments to check for TrueFalse question
$truefalse_check = trim(substr($answertext, 0, strpos($answertext, "#")));
}
$valid_tf_answers = array(
"T",
"TRUE",
"F",
"FALSE",
);
if (in_array($truefalse_check, $valid_tf_answers)) {
$question->qtype = TRUEFALSE;
}
else {
// Must be SHORTANSWER
$question->qtype = SHORTANSWER;
}
}
if (!isset($question->qtype)) {
$giftqtypenotset = get_string('giftqtypenotset', 'quiz');
$this
->error($giftqtypenotset, $text);
return false;
}
switch ($question->qtype) {
case DESCRIPTION:
$question->defaultgrade = 0;
$question->length = 0;
return $question;
break;
case ESSAY:
$question->feedback = '';
$question->fraction = 0;
return $question;
break;
case MULTICHOICE:
if (strpos($answertext, "=") === false) {
$question->single = 0;
// multiple answers are enabled if no single answer is 100% correct
}
else {
$question->single = 1;
// only one answer allowed (the default)
}
$answertext = str_replace("=", "~=", $answertext);
$answers = explode("~", $answertext);
if (isset($answers[0])) {
$answers[0] = trim($answers[0]);
}
if (empty($answers[0])) {
array_shift($answers);
}
$countanswers = count($answers);
if (!$this
->check_answer_count(2, $answers, $text)) {
return false;
break;
}
foreach ($answers as $key => $answer) {
$answer = trim($answer);
// determine answer weight
if ($answer[0] == "=") {
$answer_weight = 1;
$answer = substr($answer, 1);
}
elseif (ereg($gift_answerweight_regex, $answer)) {
// check for properly formatted answer weight
$answer_weight = $this
->answerweightparser($answer);
}
else {
//default, i.e., wrong anwer
$answer_weight = 0;
}
$question->fraction[$key] = $answer_weight;
$question->feedback[$key] = $this
->commentparser($answer);
// commentparser also removes comment from $answer
$question->answer[$key] = addslashes($this
->escapedchar_post($answer));
$question->correctfeedback = '';
$question->partiallycorrectfeedback = '';
$question->incorrectfeedback = '';
}
// end foreach answer
//$question->defaultgrade = 1;
//$question->image = ""; // No images with this format
return $question;
break;
case MATCH:
$answers = explode("=", $answertext);
if (isset($answers[0])) {
$answers[0] = trim($answers[0]);
}
if (empty($answers[0])) {
array_shift($answers);
}
if (!$this
->check_answer_count(2, $answers, $text)) {
return false;
break;
}
foreach ($answers as $key => $answer) {
$answer = trim($answer);
if (strpos($answer, "->") === false) {
$giftmatchingformat = get_string('giftmatchingformat', 'quiz');
$this
->error($giftmatchingformat, $answer);
return false;
break 2;
}
$marker = strpos($answer, "->");
$question->subquestions[$key] = addslashes(trim($this
->escapedchar_post(substr($answer, 0, $marker))));
$question->subanswers[$key] = addslashes(trim($this
->escapedchar_post(substr($answer, $marker + 2))));
}
// end foreach answer
return $question;
break;
case TRUEFALSE:
$answer = $answertext;
$comment = $this
->commentparser($answer);
// commentparser also removes comment from $answer
$feedback = $this
->split_truefalse_comment($comment);
if ($answer == "T" or $answer == "TRUE") {
$question->answer = 1;
$question->feedbacktrue = $feedback['right'];
$question->feedbackfalse = $feedback['wrong'];
}
else {
$question->answer = 0;
$question->feedbackfalse = $feedback['right'];
$question->feedbacktrue = $feedback['wrong'];
}
$question->penalty = 1;
$question->correctanswer = $question->answer;
return $question;
break;
case SHORTANSWER:
// SHORTANSWER Question
$answers = explode("=", $answertext);
if (isset($answers[0])) {
$answers[0] = trim($answers[0]);
}
if (empty($answers[0])) {
array_shift($answers);
}
if (!$this
->check_answer_count(1, $answers, $text)) {
return false;
break;
}
foreach ($answers as $key => $answer) {
$answer = trim($answer);
// Answer Weight
if (ereg($gift_answerweight_regex, $answer)) {
// check for properly formatted answer weight
$answer_weight = $this
->answerweightparser($answer);
}
else {
//default, i.e., full-credit anwer
$answer_weight = 1;
}
$question->fraction[$key] = $answer_weight;
$question->feedback[$key] = $this
->commentparser($answer);
//commentparser also removes comment from $answer
$question->answer[$key] = addslashes($this
->escapedchar_post($answer));
}
// end foreach
//$question->usecase = 0; // Ignore case
//$question->defaultgrade = 1;
//$question->image = ""; // No images with this format
return $question;
break;
case NUMERICAL:
// Note similarities to ShortAnswer
$answertext = substr($answertext, 1);
// remove leading "#"
// If there is feedback for a wrong answer, store it for now.
if (($pos = strpos($answertext, '~')) !== false) {
$wrongfeedback = substr($answertext, $pos);
$answertext = substr($answertext, 0, $pos);
}
else {
$wrongfeedback = '';
}
$answers = explode("=", $answertext);
if (isset($answers[0])) {
$answers[0] = trim($answers[0]);
}
if (empty($answers[0])) {
array_shift($answers);
}
if (count($answers) == 0) {
// invalid question
$giftnonumericalanswers = get_string('giftnonumericalanswers', 'quiz');
$this
->error($giftnonumericalanswers, $text);
return false;
break;
}
foreach ($answers as $key => $answer) {
$answer = trim($answer);
// Answer weight
if (ereg($gift_answerweight_regex, $answer)) {
// check for properly formatted answer weight
$answer_weight = $this
->answerweightparser($answer);
}
else {
//default, i.e., full-credit anwer
$answer_weight = 1;
}
$question->fraction[$key] = $answer_weight;
$question->feedback[$key] = $this
->commentparser($answer);
//commentparser also removes comment from $answer
//Calculate Answer and Min/Max values
if (strpos($answer, "..") > 0) {
// optional [min]..[max] format
$marker = strpos($answer, "..");
$max = trim(substr($answer, $marker + 2));
$min = trim(substr($answer, 0, $marker));
$ans = ($max + $min) / 2;
$tol = $max - $ans;
}
elseif (strpos($answer, ":") > 0) {
// standard [answer]:[errormargin] format
$marker = strpos($answer, ":");
$tol = trim(substr($answer, $marker + 1));
$ans = trim(substr($answer, 0, $marker));
}
else {
// only one valid answer (zero errormargin)
$tol = 0;
$ans = trim($answer);
}
if (!(is_numeric($ans) || ($ans = '*')) || !is_numeric($tol)) {
$errornotnumbers = get_string('errornotnumbers');
$this
->error($errornotnumbers, $text);
return false;
break;
}
// store results
$question->answer[$key] = $ans;
$question->tolerance[$key] = $tol;
}
// end foreach
if ($wrongfeedback) {
$key += 1;
$question->fraction[$key] = 0;
$question->feedback[$key] = $this
->commentparser($wrongfeedback);
$question->answer[$key] = '';
$question->tolerance[$key] = '';
}
return $question;
break;
default:
$giftnovalidquestion = get_string('giftnovalidquestion', 'quiz');
$this
->error($giftnovalidquestion, $text);
return false;
break;
}
// end switch ($question->qtype)
}
// end function readquestion($lines)
function repchar($text, $format = 0) {
// escapes 'reserved' characters # = ~ { ) : and removes new lines
// also pushes text through format routine
$reserved = array(
'#',
'=',
'~',
'{',
'}',
':',
"\n",
"\r",
);
$escaped = array(
'\\#',
'\\=',
'\\~',
'\\{',
'\\}',
'\\:',
'\\n',
'',
);
//dlnsk
$newtext = str_replace($reserved, $escaped, $text);
$format = 0;
// turn this off for now
if ($format) {
$newtext = format_text($format);
}
return $newtext;
}
function writequestion($question) {
// turns question into string
// question reflects database fields for general question and specific to type
global $QTYPES;
// initial string;
$expout = "";
// add comment
$expout .= "// question: {$question->id} name: {$question->name} \n";
// get question text format
$textformat = $question->questiontextformat;
$tfname = "";
if ($textformat != FORMAT_MOODLE) {
$tfname = text_format_name((int) $textformat);
$tfname = "[{$tfname}]";
}
// output depends on question type
switch ($question->qtype) {
case 'category':
// not a real question, used to insert category switch
$expout .= "\$CATEGORY: {$question->category}\n";
break;
case DESCRIPTION:
$expout .= '::' . $this
->repchar($question->name) . '::';
$expout .= $tfname;
$expout .= $this
->repchar($question->questiontext, $textformat);
break;
case ESSAY:
$expout .= '::' . $this
->repchar($question->name) . '::';
$expout .= $tfname;
$expout .= $this
->repchar($question->questiontext, $textformat);
$expout .= "{}\n";
break;
case TRUEFALSE:
$trueanswer = $question->options->answers[$question->options->trueanswer];
$falseanswer = $question->options->answers[$question->options->falseanswer];
if ($trueanswer->fraction == 1) {
$answertext = 'TRUE';
$right_feedback = $trueanswer->feedback;
$wrong_feedback = $falseanswer->feedback;
}
else {
$answertext = 'FALSE';
$right_feedback = $falseanswer->feedback;
$wrong_feedback = $trueanswer->feedback;
}
$wrong_feedback = $this
->repchar($wrong_feedback);
$right_feedback = $this
->repchar($right_feedback);
$expout .= "::" . $this
->repchar($question->name) . "::" . $tfname . $this
->repchar($question->questiontext, $textformat) . "{" . $this
->repchar($answertext);
if ($wrong_feedback) {
$expout .= "#" . $wrong_feedback;
}
else {
if ($right_feedback) {
$expout .= "#";
}
}
if ($right_feedback) {
$expout .= "#" . $right_feedback;
}
$expout .= "}\n";
break;
case MULTICHOICE:
$expout .= "::" . $this
->repchar($question->name) . "::" . $tfname . $this
->repchar($question->questiontext, $textformat) . "{\n";
foreach ($question->options->answers as $answer) {
if ($answer->fraction == 1) {
$answertext = '=';
}
elseif ($answer->fraction == 0) {
$answertext = '~';
}
else {
$export_weight = $answer->fraction * 100;
$answertext = "~%{$export_weight}%";
}
$expout .= "\t" . $answertext . $this
->repchar($answer->answer);
if ($answer->feedback != "") {
$expout .= "#" . $this
->repchar($answer->feedback);
}
$expout .= "\n";
}
$expout .= "}\n";
break;
case SHORTANSWER:
$expout .= "::" . $this
->repchar($question->name) . "::" . $tfname . $this
->repchar($question->questiontext, $textformat) . "{\n";
foreach ($question->options->answers as $answer) {
$weight = 100 * $answer->fraction;
$expout .= "\t=%" . $weight . "%" . $this
->repchar($answer->answer) . "#" . $this
->repchar($answer->feedback) . "\n";
}
$expout .= "}\n";
break;
case NUMERICAL:
$expout .= "::" . $this
->repchar($question->name) . "::" . $tfname . $this
->repchar($question->questiontext, $textformat) . "{#\n";
foreach ($question->options->answers as $answer) {
if ($answer->answer != '') {
$percentage = '';
if ($answer->fraction < 1) {
$pval = $answer->fraction * 100;
$percentage = "%{$pval}%";
}
$expout .= "\t={$percentage}" . $answer->answer . ":" . (double) $answer->tolerance . "#" . $this
->repchar($answer->feedback) . "\n";
}
else {
$expout .= "\t~#" . $this
->repchar($answer->feedback) . "\n";
}
}
$expout .= "}\n";
break;
case MATCH:
$expout .= "::" . $this
->repchar($question->name) . "::" . $tfname . $this
->repchar($question->questiontext, $textformat) . "{\n";
foreach ($question->options->subquestions as $subquestion) {
$expout .= "\t=" . $this
->repchar($subquestion->questiontext) . " -> " . $this
->repchar($subquestion->answertext) . "\n";
}
$expout .= "}\n";
break;
default:
// check for plugins
if ($out = $this
->try_exporting_using_qtypes($question->qtype, $question)) {
$expout .= $out;
}
else {
$expout .= "// {$question->qtype} is not supported by the GIFT format\n";
$menuname = $QTYPES[$question->qtype]
->menu_name();
notify(get_string('nohandler', 'qformat_gift', $menuname));
}
}
// add empty line to delimit questions
$expout .= "\n";
return $expout;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
qformat_default:: |
property | |||
qformat_default:: |
property | |||
qformat_default:: |
property | |||
qformat_default:: |
property | |||
qformat_default:: |
property | |||
qformat_default:: |
property | |||
qformat_default:: |
property | |||
qformat_default:: |
property | |||
qformat_default:: |
property | |||
qformat_default:: |
property | |||
qformat_default:: |
property | |||
qformat_default:: |
property | |||
qformat_default:: |
property | |||
qformat_default:: |
property | |||
qformat_default:: |
property | |||
qformat_default:: |
property | |||
qformat_default:: |
function | Count all non-category questions in the questions array. | ||
qformat_default:: |
function | find and/or create the category described by a delimited list e.g. $course$/tom/dick/harry or tom/dick/harry | ||
qformat_default:: |
function | return an "empty" question Somewhere to specify question parameters that are not handled by import but are required db fields. This should not be overridden. | ||
qformat_default:: |
function | Handle parsing error | ||
qformat_default:: |
function | Do an post-processing that may be required | ||
qformat_default:: |
function | Do any pre-processing that may be required | 1 | |
qformat_default:: |
function | Do the export For most types this should not need to be overrided | 1 | |
qformat_default:: |
function | Return the files extension appropriate for this type override if you don't want .txt | 3 | |
qformat_default:: |
function | where question specifies a moodle (text) format this performs the conversion. | ||
qformat_default:: |
function | get the category as a path (e.g., tom/dick/harry) | ||
qformat_default:: |
function | Import an image file encoded in base64 format | ||
qformat_default:: |
function | Override if any post-processing is required | 2 | |
qformat_default:: |
function | Perform any required pre-processing | 2 | |
qformat_default:: |
function | Process the file This method should not normally be overidden | 1 | |
qformat_default:: |
function | Enable any processing to be done on the content just prior to the file being saved default is to do nothing | 2 | |
qformat_default:: |
function | get directory into which export is going | ||
qformat_default:: |
function | Return complete file within an array, one item per line | 1 | |
qformat_default:: |
function | Parses an array of lines into an array of questions, where each item is a question object as defined by readquestion(). Questions are defined as anything between blank lines. | 9 | |
qformat_default:: |
function | set the category | ||
qformat_default:: |
function | set catfromfile | ||
qformat_default:: |
function | set cattofile | ||
qformat_default:: |
function | set contextfromfile | ||
qformat_default:: |
function | set an array of contexts. | ||
qformat_default:: |
function | set contexttofile | ||
qformat_default:: |
function | set the course class variable | ||
qformat_default:: |
function | set the filename | ||
qformat_default:: |
function | set matchgrades | ||
qformat_default:: |
function | Set the specific questions to export. Should not include questions with parents (sub questions of cloze question type). Only used for question export. | ||
qformat_default:: |
function | set the "real" filename (this is what the user typed, regardless of wha happened next) | ||
qformat_default:: |
function | set stoponerror | ||
qformat_default:: |
function | |||
qformat_default:: |
function | Provide export functionality for plugin questiontypes Do not override | ||
qformat_default:: |
function | Import for questiontype plugins Do not override. | ||
qformat_gift:: |
function | |||
qformat_gift:: |
function | |||
qformat_gift:: |
function | |||
qformat_gift:: |
function | |||
qformat_gift:: |
function | |||
qformat_gift:: |
function |
Overrides qformat_default:: |
||
qformat_gift:: |
function |
Overrides qformat_default:: |
||
qformat_gift:: |
function |
Given the data known to define a question in
this format, this function converts it into a question
object suitable for processing and insertion into Moodle. Overrides qformat_default:: |
||
qformat_gift:: |
function | |||
qformat_gift:: |
function | |||
qformat_gift:: |
function |
convert a single question object into text output in the given
format.
This must be overriden Overrides qformat_default:: |