MDL-69823 qtype: Support core question types
authorJuan Leyva <juanleyvadelgado@gmail.com>
Wed, 30 Sep 2020 09:27:29 +0000 (11:27 +0200)
committerJuan Leyva <juanleyvadelgado@gmail.com>
Tue, 10 Nov 2020 18:16:24 +0000 (19:16 +0100)
18 files changed:
question/type/calculated/tests/question_test.php
question/type/essay/question.php
question/type/essay/tests/question_test.php
question/type/gapselect/questionbase.php
question/type/gapselect/tests/question_test.php
question/type/match/question.php
question/type/match/tests/question_test.php
question/type/multianswer/question.php
question/type/multianswer/tests/question_test.php
question/type/multichoice/question.php
question/type/multichoice/tests/question_multi_test.php
question/type/numerical/question.php
question/type/numerical/questiontype.php
question/type/numerical/tests/question_test.php
question/type/shortanswer/question.php
question/type/shortanswer/tests/question_test.php
question/type/truefalse/question.php
question/type/truefalse/tests/question_test.php

index 924f62e..28a9d15 100644 (file)
@@ -178,4 +178,23 @@ class qtype_calculated_question_test extends advanced_testcase {
         $this->assertEquals('category' . $question->category,
                 $question->get_variants_selection_seed());
     }
+
+    /**
+     * test_get_question_definition_for_external_rendering
+     */
+    public function test_get_question_definition_for_external_rendering() {
+        $this->resetAfterTest();
+
+        $question = test_question_maker::make_question('calculated');
+        $question->start_attempt(new question_attempt_step(), 1);
+        $qa = test_question_maker::get_a_qa($question);
+        $displayoptions = new question_display_options();
+
+        $options = $question->get_question_definition_for_external_rendering($qa, $displayoptions);
+        $this->assertNotEmpty($options);
+        $this->assertEquals(0, $options['unitgradingtype']);
+        $this->assertEquals(0, $options['unitpenalty']);
+        $this->assertEquals(qtype_numerical::UNITNONE, $options['unitdisplay']);
+        $this->assertEmpty($options['unitsleft']);
+    }
 }
index b74cca2..8f1f03a 100644 (file)
@@ -185,4 +185,31 @@ class qtype_essay_question extends question_with_responses {
                     $filearea, $args, $forcedownload);
         }
     }
+
+    /**
+     * Return the question settings that define this question as structured data.
+     *
+     * @param question_attempt $qa the current attempt for which we are exporting the settings.
+     * @param question_display_options $options the question display options which say which aspects of the question
+     * should be visible.
+     * @return mixed structure representing the question settings. In web services, this will be JSON-encoded.
+     */
+    public function get_question_definition_for_external_rendering(question_attempt $qa, question_display_options $options) {
+        // This is a partial implementation, returning only the most relevant question settings for now,
+        // ideally, we should return as much as settings as possible (depending on the state and display options).
+
+        $settings = [
+            'responseformat' => $this->responseformat,
+            'responserequired' => $this->responserequired,
+            'responsefieldlines' => $this->responsefieldlines,
+            'attachments' => $this->attachments,
+            'attachmentsrequired' => $this->attachmentsrequired,
+            'maxbytes' => $this->maxbytes,
+            'filetypeslist' => $this->filetypeslist,
+            'responsetemplate' => $this->responsetemplate,
+            'responsetemplateformat' => $this->responsetemplateformat,
+        ];
+
+        return $settings;
+    }
 }
index 2ac7e16..9467ecb 100644 (file)
@@ -239,4 +239,27 @@ class qtype_essay_question_test extends advanced_testcase {
 
     }
 
+    /**
+     * test_get_question_definition_for_external_rendering
+     */
+    public function test_get_question_definition_for_external_rendering() {
+        $this->resetAfterTest();
+
+        $essay = test_question_maker::make_an_essay_question();
+        $essay->start_attempt(new question_attempt_step(), 1);
+        $qa = test_question_maker::get_a_qa($essay);
+        $displayoptions = new question_display_options();
+
+        $options = $essay->get_question_definition_for_external_rendering($qa, $displayoptions);
+        $this->assertNotEmpty($options);
+        $this->assertEquals('editor', $options['responseformat']);
+        $this->assertEquals(1, $options['responserequired']);
+        $this->assertEquals(15, $options['responsefieldlines']);
+        $this->assertEquals(0, $options['attachments']);
+        $this->assertEquals(0, $options['attachmentsrequired']);
+        $this->assertNull($options['maxbytes']);
+        $this->assertNull($options['filetypeslist']);
+        $this->assertEquals('', $options['responsetemplate']);
+        $this->assertEquals(FORMAT_MOODLE, $options['responsetemplateformat']);
+    }
 }
index a64e67c..0a93eb4 100644 (file)
@@ -331,4 +331,21 @@ abstract class qtype_gapselect_question_base extends question_graded_automatical
                     $args, $forcedownload);
         }
     }
+
+    /**
+     * Return the question settings that define this question as structured data.
+     *
+     * @param question_attempt $qa the current attempt for which we are exporting the settings.
+     * @param question_display_options $options the question display options which say which aspects of the question
+     * should be visible.
+     * @return mixed structure representing the question settings. In web services, this will be JSON-encoded.
+     */
+    public function get_question_definition_for_external_rendering(question_attempt $qa, question_display_options $options) {
+        // This is a partial implementation, returning only the most relevant question settings for now,
+        // ideally, we should return as much as settings as possible (depending on the state and display options).
+
+        return [
+            'shufflechoices' => $this->shufflechoices,
+        ];
+    }
 }
index 4bfdfd6..4e4663a 100644 (file)
@@ -246,4 +246,17 @@ class qtype_gapselect_question_test extends basic_testcase {
                     3 => new question_classified_response(2, 'assiduous', 0),
                 ), $gapselect->classify_response(array('p1' => '0', 'p2' => '1', 'p3' => '2')));
     }
+
+    /**
+     * test_get_question_definition_for_external_rendering
+     */
+    public function test_get_question_definition_for_external_rendering() {
+        $question = test_question_maker::make_question('gapselect', 'maths');
+        $question->start_attempt(new question_attempt_step(), 1);
+        $qa = test_question_maker::get_a_qa($question);
+        $displayoptions = new question_display_options();
+
+        $options = $question->get_question_definition_for_external_rendering($qa, $displayoptions);
+        $this->assertEquals(1, $options['shufflechoices']);
+    }
 }
index 6aba21d..15996ce 100644 (file)
@@ -354,4 +354,21 @@ class qtype_match_question extends question_graded_automatically_with_countback
                     $args, $forcedownload);
         }
     }
+
+    /**
+     * Return the question settings that define this question as structured data.
+     *
+     * @param question_attempt $qa the current attempt for which we are exporting the settings.
+     * @param question_display_options $options the question display options which say which aspects of the question
+     * should be visible.
+     * @return mixed structure representing the question settings. In web services, this will be JSON-encoded.
+     */
+    public function get_question_definition_for_external_rendering(question_attempt $qa, question_display_options $options) {
+        // This is a partial implementation, returning only the most relevant question settings for now,
+        // ideally, we should return as much as settings as possible (depending on the state and display options).
+
+        return [
+            'shufflestems' => $this->shufflestems,
+        ];
+    }
 }
index 67ab401..d516fa3 100644 (file)
@@ -233,4 +233,16 @@ class qtype_match_question_test extends advanced_testcase {
         $this->assertEquals(array(4, 4), $m->get_num_parts_right($postdata));
     }
 
+    /**
+     * test_get_question_definition_for_external_rendering
+     */
+    public function test_get_question_definition_for_external_rendering() {
+        $question = test_question_maker::make_question('match');
+        $question->start_attempt(new question_attempt_step(), 1);
+        $qa = test_question_maker::get_a_qa($question);
+        $displayoptions = new question_display_options();
+
+        $options = $question->get_question_definition_for_external_rendering($qa, $displayoptions);
+        $this->assertEquals(1, $options['shufflestems']);
+    }
 }
index 9b6707d..de55224 100644 (file)
@@ -355,4 +355,19 @@ class qtype_multianswer_question extends question_graded_automatically_with_coun
                     $args, $forcedownload);
         }
     }
+
+    /**
+     * Return the question settings that define this question as structured data.
+     *
+     * @param question_attempt $qa the current attempt for which we are exporting the settings.
+     * @param question_display_options $options the question display options which say which aspects of the question
+     * should be visible.
+     * @return mixed structure representing the question settings. In web services, this will be JSON-encoded.
+     */
+    public function get_question_definition_for_external_rendering(question_attempt $qa, question_display_options $options) {
+        // Empty implementation for now in order to avoid debugging in core questions (generated in the parent class),
+        // ideally, we should return as much as settings as possible (depending on the state and display options).
+
+        return null;
+    }
 }
index 9390ad2..487cb57 100644 (file)
@@ -238,4 +238,19 @@ class qtype_multianswer_question_test extends advanced_testcase {
         $finalgrade = $question->compute_final_grade($responses, 1);
         $this->assertEquals(1 / 3 * (1 - 3 * 0.2) + 2 / 3 * (1 - 2 * 0.2), $finalgrade);
     }
+
+    /**
+     * test_get_question_definition_for_external_rendering
+     */
+    public function test_get_question_definition_for_external_rendering() {
+        $this->resetAfterTest();
+
+        $question = test_question_maker::make_question('multianswer');
+        $question->start_attempt(new question_attempt_step(), 1);
+        $qa = test_question_maker::get_a_qa($question);
+        $displayoptions = new question_display_options();
+
+        $options = $question->get_question_definition_for_external_rendering($qa, $displayoptions);
+        $this->assertNull($options);
+    }
 }
index 624c909..ec9916c 100644 (file)
@@ -147,6 +147,26 @@ abstract class qtype_multichoice_base extends question_graded_automatically {
                     $args, $forcedownload);
         }
     }
+
+    /**
+     * Return the question settings that define this question as structured data.
+     *
+     * @param question_attempt $qa the current attempt for which we are exporting the settings.
+     * @param question_display_options $options the question display options which say which aspects of the question
+     * should be visible.
+     * @return mixed structure representing the question settings. In web services, this will be JSON-encoded.
+     */
+    public function get_question_definition_for_external_rendering(question_attempt $qa, question_display_options $options) {
+        // This is a partial implementation, returning only the most relevant question settings for now,
+        // ideally, we should return as much as settings as possible (depending on the state and display options).
+
+        return [
+            'shuffleanswers' => $this->shuffleanswers,
+            'answernumbering' => $this->answernumbering,
+            'showstandardinstruction' => $this->showstandardinstruction,
+            'layout' => $this->layout,
+        ];
+    }
 }
 
 
index d062024..f5e532c 100644 (file)
@@ -175,4 +175,19 @@ class qtype_multichoice_multi_question_test extends advanced_testcase {
         }
     }
 
+    /**
+     * test_get_question_definition_for_external_rendering
+     */
+    public function test_get_question_definition_for_external_rendering() {
+        $question = test_question_maker::make_a_multichoice_multi_question();
+        $question->start_attempt(new question_attempt_step(), 1);
+        $qa = test_question_maker::get_a_qa($question);
+        $displayoptions = new question_display_options();
+
+        $options = $question->get_question_definition_for_external_rendering($qa, $displayoptions);
+        $this->assertEquals(1, $options['shuffleanswers']);
+        $this->assertEquals('abc', $options['answernumbering']);
+        $this->assertEquals(0, $options['showstandardinstruction']);
+        $this->assertEquals(1, $options['shuffleanswers']);
+    }
 }
index 79733d4..7137ed8 100644 (file)
@@ -44,7 +44,8 @@ class qtype_numerical_question extends question_graded_automatically {
     public $unitgradingtype;
     /** @var number the penalty for a missing or unrecognised unit. */
     public $unitpenalty;
-
+    /** @var boolean whether the units come before or after the number */
+    public $unitsleft;
     /** @var qtype_numerical_answer_processor */
     public $ap;
 
@@ -318,6 +319,26 @@ class qtype_numerical_question extends question_graded_automatically {
                     $args, $forcedownload);
         }
     }
+
+    /**
+     * Return the question settings that define this question as structured data.
+     *
+     * @param question_attempt $qa the current attempt for which we are exporting the settings.
+     * @param question_display_options $options the question display options which say which aspects of the question
+     * should be visible.
+     * @return mixed structure representing the question settings. In web services, this will be JSON-encoded.
+     */
+    public function get_question_definition_for_external_rendering(question_attempt $qa, question_display_options $options) {
+        // This is a partial implementation, returning only the most relevant question settings for now,
+        // ideally, we should return as much as settings as possible (depending on the state and display options).
+
+        return [
+            'unitgradingtype' => $this->unitgradingtype,
+            'unitpenalty' => $this->unitpenalty,
+            'unitdisplay' => $this->unitdisplay,
+            'unitsleft' => $this->unitsleft,
+        ];
+    }
 }
 
 
index 6e2a89a..25754a9 100644 (file)
@@ -359,6 +359,7 @@ class qtype_numerical extends question_type {
         $question->unitdisplay = $questiondata->options->showunits;
         $question->unitgradingtype = $questiondata->options->unitgradingtype;
         $question->unitpenalty = $questiondata->options->unitpenalty;
+        $question->unitsleft = $questiondata->options->unitsleft;
         $question->ap = $this->make_answer_processor($questiondata->options->units,
                 $questiondata->options->unitsleft);
     }
index 73a6960..33eaaf5 100644 (file)
@@ -306,4 +306,23 @@ class qtype_numerical_question_test extends advanced_testcase {
                 new question_classified_response(null, '$abc', 0.0)),
                 $num->classify_response(array('answer' => '$abc')));
     }
+
+    /**
+     * test_get_question_definition_for_external_rendering
+     */
+    public function test_get_question_definition_for_external_rendering() {
+        $this->resetAfterTest();
+
+        $question = test_question_maker::make_question('numerical', 'unit');
+        $question->start_attempt(new question_attempt_step(), 1);
+        $qa = test_question_maker::get_a_qa($question);
+        $displayoptions = new question_display_options();
+
+        $options = $question->get_question_definition_for_external_rendering($qa, $displayoptions);
+        $this->assertNotEmpty($options);
+        $this->assertEquals(1, $options['unitgradingtype']);
+        $this->assertEquals(0.5, $options['unitpenalty']);
+        $this->assertEquals(qtype_numerical::UNITSELECT, $options['unitdisplay']);
+        $this->assertEmpty($options['unitsleft']);
+    }
 }
index a9de53b..b8923aa 100644 (file)
@@ -184,4 +184,17 @@ class qtype_shortanswer_question extends question_graded_by_strategy
                     $args, $forcedownload);
         }
     }
+
+    /**
+     * Return the question settings that define this question as structured data.
+     *
+     * @param question_attempt $qa the current attempt for which we are exporting the settings.
+     * @param question_display_options $options the question display options which say which aspects of the question
+     * should be visible.
+     * @return mixed structure representing the question settings. In web services, this will be JSON-encoded.
+     */
+    public function get_question_definition_for_external_rendering(question_attempt $qa, question_display_options $options) {
+        // No need to return anything, external clients do not need additional information for rendering this question type.
+        return null;
+    }
 }
index c8c05cf..89f4d38 100644 (file)
@@ -226,4 +226,19 @@ class qtype_shortanswer_question_test extends advanced_testcase {
                 question_classified_response::no_response()),
                 $sa->classify_response(array('answer' => '')));
     }
+
+    /**
+     * test_get_question_definition_for_external_rendering
+     */
+    public function test_get_question_definition_for_external_rendering() {
+        $this->resetAfterTest();
+
+        $question = test_question_maker::make_question('shortanswer');
+        $question->start_attempt(new question_attempt_step(), 1);
+        $qa = test_question_maker::get_a_qa($question);
+        $displayoptions = new question_display_options();
+
+        $options = $question->get_question_definition_for_external_rendering($qa, $displayoptions);
+        $this->assertNull($options);
+    }
 }
index 05d1d69..320781b 100644 (file)
@@ -123,4 +123,17 @@ class qtype_truefalse_question extends question_graded_automatically {
                     $args, $forcedownload);
         }
     }
+
+    /**
+     * Return the question settings that define this question as structured data.
+     *
+     * @param question_attempt $qa the current attempt for which we are exporting the settings.
+     * @param question_display_options $options the question display options which say which aspects of the question
+     * should be visible.
+     * @return mixed structure representing the question settings. In web services, this will be JSON-encoded.
+     */
+    public function get_question_definition_for_external_rendering(question_attempt $qa, question_display_options $options) {
+        // No need to return anything, external clients do not need additional information for rendering this question type.
+        return null;
+    }
 }
index 4d68b4b..fb53054 100644 (file)
@@ -107,4 +107,19 @@ class qtype_truefalse_question_test extends advanced_testcase {
                 $tf->id => question_classified_response::no_response()),
                 $tf->classify_response(array()));
     }
+
+    /**
+     * test_get_question_definition_for_external_rendering
+     */
+    public function test_get_question_definition_for_external_rendering() {
+        $this->resetAfterTest();
+
+        $question = test_question_maker::make_question('truefalse', 'true');
+        $question->start_attempt(new question_attempt_step(), 1);
+        $qa = test_question_maker::get_a_qa($question);
+        $displayoptions = new question_display_options();
+
+        $options = $question->get_question_definition_for_external_rendering($qa, $displayoptions);
+        $this->assertNull($options);
+    }
 }