MDL-34306 gift question format: allow import of general feedback
authorTim Hunt <T.J.Hunt@open.ac.uk>
Thu, 12 Jul 2012 18:11:06 +0000 (19:11 +0100)
committerTim Hunt <T.J.Hunt@open.ac.uk>
Sat, 25 Aug 2012 13:27:40 +0000 (14:27 +0100)
This change introduces #### as a separator for general feedback. You need
to add ####General feedback goes here as the last thing inside the {...}.
For example

// question: 123  name: Shortanswer
::Shortanswer::Which is the best animal?{
    =Frog#Good!
    =%50%Cat#What is it with Moodlers and cats?
    =%0%*#Completely wrong
    ####Here is some general feedback!
}

Note that this change is not entirely backwards compatible. It will break
any existing GIFT file where the character sequence #### us used between the
{} as part of the question. This seems highly unlikely.

question/format/gift/format.php
question/format/gift/tests/fixtures/questions.gift.txt
question/format/gift/tests/giftformat_test.php

index 06bcebc..9373b06 100644 (file)
@@ -213,42 +213,56 @@ class qformat_gift extends qformat_default {
             $question->name = false;
         }
 
-
-        // FIND ANSWER section
-        // no answer means its a description
+        // Find the answer section.
         $answerstart = strpos($text, '{');
         $answerfinish = strpos($text, '}');
 
         $description = false;
-        if (($answerstart === false) and ($answerfinish === false)) {
+        if ($answerstart === false && $answerfinish === false) {
+            // No answer means it's a description.
             $description = true;
             $answertext = '';
             $answerlength = 0;
-        } else if (!(($answerstart !== false) and ($answerfinish !== false))) {
+
+        } else if ($answerstart === false || $answerfinish === false) {
             $this->error(get_string('braceerror', 'qformat_gift'), $text);
             return false;
+
         } else {
             $answerlength = $answerfinish - $answerstart;
             $answertext = trim(substr($text, $answerstart + 1, $answerlength - 1));
         }
 
-        // Format QUESTION TEXT without answer, inserting "_____" as necessary
+        // Format the question text, without answer, inserting "_____" as necessary.
         if ($description) {
             $questiontext = $text;
         } else if (substr($text, -1) == "}") {
-            // no blank line if answers follow question, outside of closing punctuation
-            $questiontext = substr_replace($text, "", $answerstart, $answerlength+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);
+            // Inserts blank line for missing word format.
+            $questiontext = substr_replace($text, "_____", $answerstart, $answerlength + 1);
         }
 
-        // Get questiontext format from questiontext
+        // Look to see if there is any general feedback.
+        $gfseparator = strrpos($answertext, '####');
+        if ($gfseparator === false) {
+            $generalfeedback = '';
+        } else {
+            $generalfeedback = substr($answertext, $gfseparator + 4);
+            $answertext = trim(substr($answertext, 0, $gfseparator));
+        }
+
+        // Get questiontext format from questiontext.
         $text = $this->parse_text_with_format($questiontext);
         $question->questiontextformat = $text['format'];
-        $question->generalfeedbackformat = $text['format'];
         $question->questiontext = $text['text'];
 
+        // Get generalfeedback format from questiontext.
+        $text = $this->parse_text_with_format($generalfeedback, $question->questiontextformat);
+        $question->generalfeedback = $text['text'];
+        $question->generalfeedbackformat = $text['format'];
+
         // set question name if not already set
         if ($question->name === false) {
             $question->name = $question->questiontext;
@@ -609,6 +623,27 @@ class qformat_gift extends qformat_default {
         return $output;
     }
 
+    /**
+     * Outputs the general feedback for the question, if any. This needs to be the
+     * last thing before the }.
+     * @param object $question the question data.
+     * @param string $indent to put before the general feedback. Defaults to a tab.
+     *      If this is not blank, a newline is added after the line.
+     */
+    public function write_general_feedback($question, $indent = "\t") {
+        $generalfeedback = $this->write_questiontext($question->generalfeedback,
+                $question->generalfeedbackformat, $question->questiontextformat);
+
+        if ($generalfeedback) {
+            $generalfeedback = '####' . $generalfeedback;
+            if ($indent) {
+                $generalfeedback = $indent . $generalfeedback . "\n";
+            }
+        }
+
+        return $generalfeedback;
+    }
+
     public function writequestion($question) {
         global $OUTPUT;
 
@@ -631,7 +666,9 @@ class qformat_gift extends qformat_default {
         case ESSAY:
             $expout .= $this->write_name($question->name);
             $expout .= $this->write_questiontext($question->questiontext, $question->questiontextformat);
-            $expout .= "{}\n";
+            $expout .= "{";
+            $expout .= $this->write_general_feedback($question, '');
+            $expout .= "}\n";
             break;
 
         case TRUEFALSE:
@@ -662,6 +699,7 @@ class qformat_gift extends qformat_default {
             if ($rightfeedback) {
                 $expout .= '#' . $rightfeedback;
             }
+            $expout .= $this->write_general_feedback($question, '');
             $expout .= "}\n";
             break;
 
@@ -686,6 +724,7 @@ class qformat_gift extends qformat_default {
                 }
                 $expout .= "\n";
             }
+            $expout .= $this->write_general_feedback($question);
             $expout .= "}\n";
             break;
 
@@ -699,6 +738,7 @@ class qformat_gift extends qformat_default {
                         '#' . $this->write_questiontext($answer->feedback,
                             $answer->feedbackformat, $question->questiontextformat) . "\n";
             }
+            $expout .= $this->write_general_feedback($question);
             $expout .= "}\n";
             break;
 
@@ -717,6 +757,7 @@ class qformat_gift extends qformat_default {
                             $answer->feedbackformat, $question->questiontextformat) . "\n";
                 }
             }
+            $expout .= $this->write_general_feedback($question);
             $expout .= "}\n";
             break;
 
@@ -729,6 +770,7 @@ class qformat_gift extends qformat_default {
                         $subquestion->questiontextformat, $question->questiontextformat) .
                         ' -> ' . $this->repchar($subquestion->answertext) . "\n";
             }
+            $expout .= $this->write_general_feedback($question);
             $expout .= "}\n";
             break;
 
index e7ad9a6..c434dcd 100644 (file)
     =%0%*#Completely wrong
 }
 
-// true/false
+// true/false, with general feedback
 ::Q1:: 42 is the Absolute Answer to everything.{
-FALSE#42 is the Ultimate Answer.#You gave the right answer.}";
+FALSE#42 is the Ultimate Answer.#You gave the right answer.
+####This is, of course, a Hitchiker's Guide to the Galaxy reference.}";
 
 // name 0-11
 ::2-08 TSL::TSL is blablabla.{T}
index e06a123..54fcdb3 100644 (file)
@@ -673,6 +673,62 @@ class qformat_gift_test extends question_testcase {
         $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
     }
 
+    public function test_import_shortanswer_with_general_feedback() {
+        $gift = "
+// question: 666  name: Shortanswer
+::Shortanswer::Which is the best animal?{
+    =Frog#Good!
+    =%50%Cat#What is it with Moodlers and cats?
+    =%0%*#Completely wrong
+    ####[html]Here is some general feedback!
+}";
+        $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift));
+
+        $importer = new qformat_gift();
+        $q = $importer->readquestion($lines);
+
+        $expectedq = (object) array(
+            'name' => 'Shortanswer',
+            'questiontext' => "Which is the best animal?",
+            'questiontextformat' => FORMAT_MOODLE,
+            'generalfeedback' => 'Here is some general feedback!',
+            'generalfeedbackformat' => FORMAT_HTML,
+            'qtype' => 'shortanswer',
+            'defaultmark' => 1,
+            'penalty' => 0.3333333,
+            'length' => 1,
+            'answer' => array(
+                'Frog',
+                'Cat',
+                '*',
+            ),
+            'fraction' => array(1, 0.5, 0),
+            'feedback' => array(
+                0 => array(
+                    'text' => 'Good!',
+                    'format' => FORMAT_MOODLE,
+                    'files' => array(),
+                ),
+                1 => array(
+                    'text' => "What is it with Moodlers and cats?",
+                    'format' => FORMAT_MOODLE,
+                    'files' => array(),
+                ),
+                2 => array(
+                    'text' => "Completely wrong",
+                    'format' => FORMAT_MOODLE,
+                    'files' => array(),
+                ),
+            ),
+        );
+
+        // Repeated test for better failure messages.
+        $this->assertEquals($expectedq->answer, $q->answer);
+        $this->assertEquals($expectedq->fraction, $q->fraction);
+        $this->assertEquals($expectedq->feedback, $q->feedback);
+        $this->assert(new question_check_specified_fields_expectation($expectedq), $q);
+    }
+
     public function test_export_shortanswer() {
         $qdata = (object) array(
             'id' => 666 ,
@@ -728,6 +784,67 @@ class qformat_gift_test extends question_testcase {
 \t=%0%*#Completely wrong
 }
 
+";
+
+        $this->assert_same_gift($expectedgift, $gift);
+    }
+
+    public function test_export_shortanswer_with_general_feedback() {
+        $qdata = (object) array(
+            'id' => 666 ,
+            'name' => 'Shortanswer',
+            'questiontext' => "Which is the best animal?",
+            'questiontextformat' => FORMAT_MOODLE,
+            'generalfeedback' => 'Here is some general feedback!',
+            'generalfeedbackformat' => FORMAT_HTML,
+            'defaultmark' => 1,
+            'penalty' => 1,
+            'length' => 1,
+            'qtype' => 'shortanswer',
+            'options' => (object) array(
+                'id' => 123,
+                'question' => 666,
+                'usecase' => 1,
+                'answers' => array(
+                    1 => (object) array(
+                        'id' => 1,
+                        'answer' => 'Frog',
+                        'answerformat' => 0,
+                        'fraction' => 1,
+                        'feedback' => 'Good!',
+                        'feedbackformat' => FORMAT_MOODLE,
+                    ),
+                    2 => (object) array(
+                        'id' => 2,
+                        'answer' => 'Cat',
+                        'answerformat' => 0,
+                        'fraction' => 0.5,
+                        'feedback' => "What is it with Moodlers and cats?",
+                        'feedbackformat' => FORMAT_MOODLE,
+                    ),
+                    3 => (object) array(
+                        'id' => 3,
+                        'answer' => '*',
+                        'answerformat' => 0,
+                        'fraction' => 0,
+                        'feedback' => "Completely wrong",
+                        'feedbackformat' => FORMAT_MOODLE,
+                    ),
+                ),
+            ),
+        );
+
+        $exporter = new qformat_gift();
+        $gift = $exporter->writequestion($qdata);
+
+        $expectedgift = "// question: 666  name: Shortanswer
+::Shortanswer::Which is the best animal?{
+\t=%100%Frog#Good!
+\t=%50%Cat#What is it with Moodlers and cats?
+\t=%0%*#Completely wrong
+\t####[html]Here is some general feedback!
+}
+
 ";
 
         $this->assert_same_gift($expectedgift, $gift);