From ab1631ecdc41070b61006e777d9881be82d6083c Mon Sep 17 00:00:00 2001 From: mkassaei Date: Wed, 20 Dec 2023 12:42:01 +0000 Subject: [PATCH] Matrix: Reimplement get_possible_responses() for stats #746912 --- classes/privacy/provider.php | 2 +- question.php | 87 ++++++++++++++++++ questiontype.php | 34 +++---- tests/helper.php | 90 ++++++++----------- tests/question_multiple_test.php | 116 ++++++++++++++++++++++++ tests/question_single_test.php | 146 +++++++++++++++++++++++++++++++ tests/questiontype_test.php | 85 ++++++++++++++++++ 7 files changed, 484 insertions(+), 76 deletions(-) diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php index 339f0f9..2b491db 100644 --- a/classes/privacy/provider.php +++ b/classes/privacy/provider.php @@ -46,7 +46,7 @@ class provider implements * @param collection $collection The initialised collection to add items to. * @return collection A listing of user data stored through this system. */ - public static function get_metadata(collection $collection) : collection { + public static function get_metadata(collection $collection): collection { $collection->add_user_preference('qtype_oumatrix_defaultmark', 'privacy:preference:defaultmark'); $collection->add_user_preference('qtype_oumatrix_penalty', 'privacy:preference:penalty'); $collection->add_user_preference('qtype_oumatrix_inputtype', 'privacy:preference:inputtype'); diff --git a/question.php b/question.php index cd13208..3cd6f96 100644 --- a/question.php +++ b/question.php @@ -89,6 +89,7 @@ public function apply_attempt_state(question_attempt_step $step) { /** * Returns the roworder of the question being displayed. + * * @param question_attempt $qa * @return array|null */ @@ -269,6 +270,50 @@ public function is_complete_response(array $response): bool { return true; } + public function classify_response(array $response): array { + $selectedchoices = []; + foreach ($this->rows as $rownumber => $row) { + $fieldname = $this->field($rownumber - 1); + if (array_key_exists($fieldname, $response) && $response[$fieldname]) { + foreach ($this->columns as $colnumber => $col) { + if ($response[$fieldname] === $colnumber && + in_array($response[$fieldname], array_keys($row->correctanswers))) { + $fraction = 1; + } else { + $fraction = 0; + } + $selectedchoices[$rownumber][$colnumber] = + new question_classified_response ($colnumber, $row->name . ': ' . $col->name, $fraction); + } + } else { + $selectedchoices[$rownumber] = question_classified_response::no_response(); + } + } + return $selectedchoices; + } + + public function prepare_simulated_post_data($simulatedresponse): array { + $postdata = []; + $subquestions = array_keys($simulatedresponse); + $answers = array_values($simulatedresponse); + + foreach ($this->roworder as $key => $rowid) { + $row = $this->rows[$rowid]; + if ($row->name !== $subquestions[$key]) { + continue; + } + if ($key === ($row->number - 1) && $row->name === $subquestions[$key]) { + foreach ($this->columns as $colid => $column) { + if ($column->name !== $answers[$key]) { + continue; + } + $postdata[$this->field($key)] = $column->number; + } + } + } + return $postdata; + } + public function grade_response(array $response): array { // Retrieve the number of right responses and the total number of responses. [$numrightparts, $total] = $this->get_num_parts_right($response); @@ -388,6 +433,48 @@ public function is_complete_response(array $response): bool { return true; } + public function classify_response(array $response) { + $selectedchoices = []; + foreach ($this->rows as $rownumber => $row) { + foreach ($this->columns as $colnumber => $col) { + $fieldname = $this->field($rownumber - 1, $colnumber); + if (array_key_exists($fieldname, $response) && $response[$fieldname] && + in_array($colnumber, array_keys($row->correctanswers))) { + $fraction = 1 / count($row->correctanswers); + } else { + $fraction = 0; + } + $selectedchoices[$rownumber][$colnumber] = + new question_classified_response ($colnumber, $row->name . ': ' . $col->name, $fraction); + } + } + return $selectedchoices; + } + + public function prepare_simulated_post_data($simulatedresponse): array { + $postdata = []; + $subquestions = array_keys($simulatedresponse); + $answers = array_values($simulatedresponse); + foreach ($this->roworder as $key => $rowid) { + $row = $this->rows[$rowid]; + $rowanswers = $answers[$key]; + if ($key === ($row->number - 1) && $row->name === $subquestions[$key]) { + foreach ($this->columns as $colid => $column) { + // Set the field to '0' initially. + $postdata[$this->field($key, $column->number)] = '0'; + foreach ($rowanswers as $colnumber => $colname) { + if ($row->name === $subquestions[$key] && + $column->number === $colnumber && $column->name === $colname) { + // Set the field to '1' if it has been ticked.. + $postdata[$this->field($key, $column->number)] = '1'; + } + } + } + } + } + return $postdata; + } + public function grade_response(array $response): array { // Retrieve the number of right responses and the total number of responses. if ($this->grademethod == 'allnone') { diff --git a/questiontype.php b/questiontype.php index 0af0711..4ea9068 100644 --- a/questiontype.php +++ b/questiontype.php @@ -316,30 +316,22 @@ public function get_num_correct_choices(stdClass $questiondata): int { } public function get_possible_responses($questiondata) { - if ($questiondata->options->single) { - $responses = []; - - // TODO: Sort out this funtion to work with rows and columns, etc. - foreach ($questiondata->options->answers as $aid => $answer) { - $responses[$aid] = new question_possible_response( - question_utils::to_plain_text($answer->answer, $answer->answerformat), - $answer->fraction); + $q = $this->make_question($questiondata); + $subqs = []; + $responses = []; + foreach ($q->rows as $rownumber => $row) { + foreach ($q->columns as $colnumber => $col) { + if (in_array($colnumber, array_keys($row->correctanswers))) { + $fraction = 1; + } else { + $fraction = 0; + } + $responses[$colnumber] = new question_possible_response($row->name . ': ' . $col->name, $fraction); } - $responses[null] = question_possible_response::no_response(); - return [$questiondata->id => $responses]; - } else { - $parts = []; - - foreach ($questiondata->options->answers as $aid => $answer) { - $parts[$aid] = [ - $aid => new question_possible_response(question_utils::to_plain_text( - $answer->answer, $answer->answerformat), $answer->fraction), - ]; - } - - return $parts; + $subqs[$rownumber] = $responses; } + return $subqs; } public function import_from_xml($data, $question, qformat_xml $format, $extra = null) { diff --git a/tests/helper.php b/tests/helper.php index 0d13274..b55e504 100644 --- a/tests/helper.php +++ b/tests/helper.php @@ -72,54 +72,46 @@ public function get_oumatrix_question_data_animals_single(): stdClass { $qdata->options->shownumcorrect = 1; $qdata->columns = [ - 11 => (object) [ - 'id' => 11, + 1 => (object) [ 'number' => 1, 'name' => 'Insects', ], - 12 => (object) [ - 'id' => 12, + 2 => (object) [ 'number' => 2, 'name' => 'Fish', ], - 13 => (object) [ - 'id' => 13, + 3 => (object) [ 'number' => 3, 'name' => 'Birds', ], - 14 => (object) [ - 'id' => 13, + 4 => (object) [ 'number' => 4, 'name' => 'Mammals', ], ]; $qdata->rows = [ - 11 => (object) [ - 'id' => 11, + 1 => (object) [ 'number' => 1, 'name' => 'Bee', 'correctanswers' => ['1' => 'a1'], 'feedback' => 'Flies and Bees are insects.', 'feedbackformat' => FORMAT_HTML, ], - 12 => (object) [ - 'id' => 12, + 2 => (object) [ 'number' => 2, 'name' => 'Salmon', 'correctanswers' => ['2' => 'a2'], 'feedback' => 'Cod, Salmon and Trout are fish.', 'feedbackformat' => FORMAT_HTML, ], - 13 => (object) [ - 'id' => 13, + 3 => (object) [ 'number' => 3, 'name' => 'Seagull', 'correctanswers' => ['3' => 'a3'], 'feedback' => 'Gulls and Owls are birds.', 'feedbackformat' => FORMAT_HTML, ], - 14 => (object) [ - 'id' => 14, + 4 => (object) [ 'number' => 4, 'name' => 'Dog', 'correctanswers' => ['4' => 'a4'], @@ -266,61 +258,51 @@ public function get_oumatrix_question_data_food_multiple(): stdClass { $qdata->options->shownumcorrect = 1; $qdata->columns = [ - 21 => (object) [ - 'id' => 21, + 1 => (object) [ 'number' => 1, 'name' => 'Chicken breast', ], - 22 => (object) [ - 'id' => 22, + 2 => (object) [ 'number' => 2, 'name' => 'Carrot', ], - 23 => (object) [ - 'id' => 23, + 3 => (object) [ 'number' => 3, 'name' => 'Salmon fillet', ], - 24 => (object) [ - 'id' => 24, + 4 => (object) [ 'number' => 4, 'name' => 'Asparagus', ], - 25 => (object) [ - 'id' => 25, + 5 => (object) [ 'number' => 5, 'name' => 'Olive oil', ], - 26 => (object) [ - 'id' => 26, + 6 => (object) [ 'number' => 6, 'name' => 'Steak', ], - 27 => (object) [ - 'id' => 27, + 7 => (object) [ 'number' => 7, 'name' => 'Potato', ], ]; $qdata->rows = [ - 21 => (object) [ - 'id' => 21, + 1 => (object) [ 'number' => 1, 'name' => 'Proteins', 'correctanswers' => [1, 3, 6], 'feedback' => 'Chicken, fish and red meat containing proteins.', 'feedbackformat' => FORMAT_HTML, ], - 22 => (object) [ - 'id' => 22, + 2 => (object) [ 'number' => 2, 'name' => 'Vegetables', 'correctanswers' => [2, 4, 7], 'feedback' => 'Carrot, Asparagus, Potato are vegetables.', 'feedbackformat' => FORMAT_HTML, ], - 23 => (object) [ - 'id' => 23, + 3 => (object) [ 'number' => 3, 'name' => 'Fats', 'correctanswers' => [5], @@ -481,20 +463,20 @@ public function make_oumatrix_question_animals_single(): qtype_oumatrix_single { $question->shownumcorrect = 1; $question->columns = [ - 11 => new column($question->id, 1, 'Insects', 11), - 12 => new column($question->id, 2, 'Fish', 12), - 13 => new column($question->id, 3, 'Birds', 13), - 14 => new column($question->id, 4, 'Mammals', 14), + 1 => new column($question->id, 1, 'Insects'), + 2 => new column($question->id, 2, 'Fish'), + 3 => new column($question->id, 3, 'Birds'), + 4 => new column($question->id, 4, 'Mammals'), ]; $question->rows = [ - 11 => new row(11, $question->id, 1, 'Bee', [1 => '1'], + 1 => new row(1, $question->id, 1, 'Bee', [1 => '1'], 'Flies and Bees are insects.', FORMAT_HTML), - 12 => new row(12, $question->id, 2, 'Salmon', [2 => '1'], + 2 => new row(2, $question->id, 2, 'Salmon', [2 => '1'], 'Cod, Salmon and Trout are fish.', FORMAT_HTML), - 13 => new row(13, $question->id, 3, 'Seagull', [3 => '1'], + 3 => new row(3, $question->id, 3, 'Seagull', [3 => '1'], 'Gulls and Owls are birds.', FORMAT_HTML), - 14 => new row(14, $question->id, 4, 'Dog', [4 => '1'], + 4 => new row(4, $question->id, 4, 'Dog', [4 => '1'], 'Cows, Dogs and Horses are mammals.', FORMAT_HTML), ]; @@ -538,21 +520,21 @@ public function make_oumatrix_question_food_multiple(): qtype_oumatrix_multiple $question->shownumcorrect = 1; $question->columns = [ - 21 => new column($question->id, 1, 'Chicken breast', 21), - 22 => new column($question->id, 2, 'Carrot', 22), - 23 => new column($question->id, 3, 'Salmon fillet', 23), - 24 => new column($question->id, 4, 'Asparagus', 24), - 25 => new column($question->id, 5, 'Olive oil', 25), - 26 => new column($question->id, 6, 'Steak', 26), - 27 => new column($question->id, 7, 'Potato', 27), + 1 => new column($question->id, 1, 'Chicken breast'), + 2 => new column($question->id, 2, 'Carrot'), + 3 => new column($question->id, 3, 'Salmon fillet'), + 4 => new column($question->id, 4, 'Asparagus'), + 5 => new column($question->id, 5, 'Olive oil'), + 6 => new column($question->id, 6, 'Steak'), + 7 => new column($question->id, 7, 'Potato'), ]; $question->rows = [ - 21 => new row(21, $question->id, 1, 'Proteins', [1 => '1', 3 => '1', 6 => '1'], + 1 => new row(21, $question->id, 1, 'Proteins', [1 => '1', 3 => '1', 6 => '1'], 'Chicken, fish and red meat containing proteins.', FORMAT_HTML), - 22 => new row(22, $question->id, 2, 'Vegetables', [2 => '1', 4 => '1', 7 => '1'], + 2 => new row(22, $question->id, 2, 'Vegetables', [2 => '1', 4 => '1', 7 => '1'], 'Carrot, Asparagus, Potato are vegetables.', FORMAT_HTML), - 23 => new row(23, $question->id, 3, 'Fats', [5 => '1'], + 3 => new row(23, $question->id, 3, 'Fats', [5 => '1'], 'Olive oil contains fat.', FORMAT_HTML), ]; diff --git a/tests/question_multiple_test.php b/tests/question_multiple_test.php index dab1c8d..bba1172 100644 --- a/tests/question_multiple_test.php +++ b/tests/question_multiple_test.php @@ -18,6 +18,7 @@ use question_attempt_step; use question_state; +use question_classified_response; defined('MOODLE_INTERNAL') || die(); @@ -100,6 +101,121 @@ public function test_is_gradable_response(): void { $this->assertTrue($question->is_gradable_response($response), $question->is_complete_response($response)); } + public function test_classify_response_multiple(): void { + $this->resetAfterTest(); + $question = \test_question_maker::make_question('oumatrix', 'food_multiple'); + $question->shuffleanswers = 0; + $question->start_attempt(new question_attempt_step(), 1); + + $response = $question->prepare_simulated_post_data([ + 'Proteins' => [1 => 'Chicken breast', 3 => 'Salmon fillet', 6 => 'Steak'], + 'Vegetables' => [2 => 'Carrot', 4 => 'Asparagus', 7 => 'Potato'], + 'Fats' => [5 => 'Olive oil']]); + $expected = [ + 1 => [ + 1 => new question_classified_response(1, 'Proteins: Chicken breast', 1 / 3), + 2 => new question_classified_response(2, 'Proteins: Carrot', 0), + 3 => new question_classified_response(3, 'Proteins: Salmon fillet', 1 / 3), + 4 => new question_classified_response(4, 'Proteins: Asparagus', 0), + 5 => new question_classified_response(5, 'Proteins: Olive oil', 0), + 6 => new question_classified_response(6, 'Proteins: Steak', 1 / 3), + 7 => new question_classified_response(7, 'Proteins: Potato', 0), + ], + 2 => [ + 1 => new question_classified_response(1, 'Vegetables: Chicken breast', 0), + 2 => new question_classified_response(2, 'Vegetables: Carrot', 1 / 3), + 3 => new question_classified_response(3, 'Vegetables: Salmon fillet', 0), + 4 => new question_classified_response(4, 'Vegetables: Asparagus', 1 / 3), + 5 => new question_classified_response(5, 'Vegetables: Olive oil', 0), + 6 => new question_classified_response(6, 'Vegetables: Steak', 0), + 7 => new question_classified_response(7, 'Vegetables: Potato', 1 / 3), + ], + 3 => [ + 1 => new question_classified_response(1, 'Fats: Chicken breast', 0), + 2 => new question_classified_response(2, 'Fats: Carrot', 0), + 3 => new question_classified_response(3, 'Fats: Salmon fillet', 0), + 4 => new question_classified_response(4, 'Fats: Asparagus', 0), + 5 => new question_classified_response(5, 'Fats: Olive oil', 1), + 6 => new question_classified_response(6, 'Fats: Steak', 0), + 7 => new question_classified_response(7, 'Fats: Potato', 0), + ], + ]; + $this->assertEquals($expected, $question->classify_response($response)); + + $response = $question->prepare_simulated_post_data([ + 'Proteins' => [1 => 'Chicken breast', 3 => 'Salmon fillet'], + 'Vegetables' => [2 => 'Carrot', 7 => 'Potato'], + 'Fats' => [5 => 'Olive oil']]); + $expected = [ + 1 => [ + 1 => new question_classified_response(1, 'Proteins: Chicken breast', 1 / 3), + 2 => new question_classified_response(2, 'Proteins: Carrot', 0), + 3 => new question_classified_response(3, 'Proteins: Salmon fillet', 1 / 3), + 4 => new question_classified_response(4, 'Proteins: Asparagus', 0), + 5 => new question_classified_response(5, 'Proteins: Olive oil', 0), + 6 => new question_classified_response(6, 'Proteins: Steak', 0), + 7 => new question_classified_response(7, 'Proteins: Potato', 0), + ], + 2 => [ + 1 => new question_classified_response(1, 'Vegetables: Chicken breast', 0), + 2 => new question_classified_response(2, 'Vegetables: Carrot', 1 / 3), + 3 => new question_classified_response(3, 'Vegetables: Salmon fillet', 0), + 4 => new question_classified_response(4, 'Vegetables: Asparagus', 0), + 5 => new question_classified_response(5, 'Vegetables: Olive oil', 0), + 6 => new question_classified_response(6, 'Vegetables: Steak', 0), + 7 => new question_classified_response(7, 'Vegetables: Potato', 1 / 3), + ], + 3 => [ + 1 => new question_classified_response(1, 'Fats: Chicken breast', 0), + 2 => new question_classified_response(2, 'Fats: Carrot', 0), + 3 => new question_classified_response(3, 'Fats: Salmon fillet', 0), + 4 => new question_classified_response(4, 'Fats: Asparagus', 0), + 5 => new question_classified_response(5, 'Fats: Olive oil', 1), + 6 => new question_classified_response(6, 'Fats: Steak', 0), + 7 => new question_classified_response(7, 'Fats: Potato', 0), + ], + ]; + $this->assertEquals($expected, $question->classify_response($response)); + } + + public function test_prepare_simulated_post_data_multiple(): void { + $this->resetAfterTest(); + $question = \test_question_maker::make_question('oumatrix', 'food_multiple'); + $question->shuffleanswers = 0; + $question->start_attempt(new question_attempt_step(), 1); + + $response = ['Proteins' => [1 => 'Chicken breast', 3 => 'Salmon fillet', 6 => 'Steak'], + 'Vegetables' => [2 => 'Carrot', 4 => 'Asparagus', 7 => 'Potato'], 'Fats' => [5 => 'Olive oil']]; + + $expected = [ + 'rowanswers0_1' => '1', + 'rowanswers0_2' => '0', + 'rowanswers0_3' => '1', + 'rowanswers0_4' => '0', + 'rowanswers0_5' => '0', + 'rowanswers0_6' => '1', + 'rowanswers0_7' => '0', + + 'rowanswers1_1' => '0', + 'rowanswers1_2' => '1', + 'rowanswers1_3' => '0', + 'rowanswers1_4' => '1', + 'rowanswers1_5' => '0', + 'rowanswers1_6' => '0', + 'rowanswers1_7' => '1', + + 'rowanswers2_1' => '0', + 'rowanswers2_2' => '0', + 'rowanswers2_3' => '0', + 'rowanswers2_4' => '0', + 'rowanswers2_5' => '1', + 'rowanswers2_6' => '0', + 'rowanswers2_7' => '0', + ]; + $this->assertEquals($expected, $question->prepare_simulated_post_data($response)); + } + + public function test_is_same_response(): void { $question = \test_question_maker::make_question('oumatrix', 'food_multiple'); $question->start_attempt(new question_attempt_step(), 1); diff --git a/tests/question_single_test.php b/tests/question_single_test.php index 1b8325d..74629eb 100644 --- a/tests/question_single_test.php +++ b/tests/question_single_test.php @@ -17,6 +17,7 @@ namespace qtype_oumatrix; use question_attempt_step; use question_state; +use question_classified_response; defined('MOODLE_INTERNAL') || die(); @@ -60,6 +61,151 @@ public function test_is_gradable_response(): void { $this->assertEquals($question->is_gradable_response($response), $question->is_complete_response($response)); } + public function test_classify_response_single(): void { + $this->resetAfterTest(); + $question = \test_question_maker::make_question('oumatrix', 'animals_single'); + $question->shuffleanswers = 0; + $question->start_attempt(new question_attempt_step(), 1); + + // All sub-questions are answered correctly. + $response = $question->prepare_simulated_post_data( + ['Bee' => 'Insects', 'Salmon' => 'Fish', 'Seagull' => 'Birds', 'Dog' => 'Mammals']); + $expected = [ + 1 => [ + 1 => new question_classified_response(1, 'Bee: Insects', 1), + 2 => new question_classified_response(2, 'Bee: Fish', 0), + 3 => new question_classified_response(3, 'Bee: Birds', 0), + 4 => new question_classified_response(4, 'Bee: Mammals', 0), + ], + 2 => [ + 1 => new question_classified_response(1, 'Salmon: Insects', 0), + 2 => new question_classified_response(2, 'Salmon: Fish', 1), + 3 => new question_classified_response(3, 'Salmon: Birds', 0), + 4 => new question_classified_response(4, 'Salmon: Mammals', 0), + ], + 3 => [ + 1 => new question_classified_response(1, 'Seagull: Insects', 0), + 2 => new question_classified_response(2, 'Seagull: Fish', 0), + 3 => new question_classified_response(3, 'Seagull: Birds', 1), + 4 => new question_classified_response(4, 'Seagull: Mammals', 0), + ], + 4 => [ + 1 => new question_classified_response(1, 'Dog: Insects', 0), + 2 => new question_classified_response(2, 'Dog: Fish', 0), + 3 => new question_classified_response(3, 'Dog: Birds', 0), + 4 => new question_classified_response(4, 'Dog: Mammals', 1), + ], + ]; + $this->assertEquals($expected, $question->classify_response($response)); + + // Three sub-questions are answered correctly and one incorrectly. + $response = $question->prepare_simulated_post_data( + ['Bee' => 'Insects', 'Salmon' => 'Birds', 'Seagull' => 'Birds', 'Dog' => 'Mammals']); + $expected = [ + 1 => [ + 1 => new question_classified_response(1, 'Bee: Insects', 1), + 2 => new question_classified_response(2, 'Bee: Fish', 0), + 3 => new question_classified_response(3, 'Bee: Birds', 0), + 4 => new question_classified_response(4, 'Bee: Mammals', 0), + ], + 2 => [ + 1 => new question_classified_response(1, 'Salmon: Insects', 0), + 2 => new question_classified_response(2, 'Salmon: Fish', 0), + 3 => new question_classified_response(3, 'Salmon: Birds', 0), + 4 => new question_classified_response(4, 'Salmon: Mammals', 0), + ], + 3 => [ + 1 => new question_classified_response(1, 'Seagull: Insects', 0), + 2 => new question_classified_response(2, 'Seagull: Fish', 0), + 3 => new question_classified_response(3, 'Seagull: Birds', 1), + 4 => new question_classified_response(4, 'Seagull: Mammals', 0), + ], + 4 => [ + 1 => new question_classified_response(1, 'Dog: Insects', 0), + 2 => new question_classified_response(2, 'Dog: Fish', 0), + 3 => new question_classified_response(3, 'Dog: Birds', 0), + 4 => new question_classified_response(4, 'Dog: Mammals', 1), + ], + ]; + $this->assertEquals($expected, $question->classify_response($response)); + + // Two sub-questions are answered correctly and two incorrectly. + $response = $question->prepare_simulated_post_data( + ['Bee' => 'Insects', 'Salmon' => 'Birds', 'Seagull' => 'Birds', 'Dog' => 'Insects']); + $expected = [ + 1 => [ + 1 => new question_classified_response(1, 'Bee: Insects', 1), + 2 => new question_classified_response(2, 'Bee: Fish', 0), + 3 => new question_classified_response(3, 'Bee: Birds', 0), + 4 => new question_classified_response(4, 'Bee: Mammals', 0), + ], + 2 => [ + 1 => new question_classified_response(1, 'Salmon: Insects', 0), + 2 => new question_classified_response(2, 'Salmon: Fish', 0), + 3 => new question_classified_response(3, 'Salmon: Birds', 0), + 4 => new question_classified_response(4, 'Salmon: Mammals', 0), + ], + 3 => [ + 1 => new question_classified_response(1, 'Seagull: Insects', 0), + 2 => new question_classified_response(2, 'Seagull: Fish', 0), + 3 => new question_classified_response(3, 'Seagull: Birds', 1), + 4 => new question_classified_response(4, 'Seagull: Mammals', 0), + ], + 4 => [ + 1 => new question_classified_response(1, 'Dog: Insects', 0), + 2 => new question_classified_response(2, 'Dog: Fish', 0), + 3 => new question_classified_response(3, 'Dog: Birds', 0), + 4 => new question_classified_response(4, 'Dog: Mammals', 0), + ], + ]; + $this->assertEquals($expected, $question->classify_response($response)); + + // Two sub-questions are answered correctly, one incorrectly, and the second sub-question is not answered. + $response = $question->prepare_simulated_post_data( + ['Bee' => 'Insects', 'Salmon' => '', 'Seagull' => 'Birds', 'Dog' => 'Insects']); + $expected = [ + 1 => [ + 1 => new question_classified_response(1, 'Bee: Insects', 1), + 2 => new question_classified_response(2, 'Bee: Fish', 0), + 3 => new question_classified_response(3, 'Bee: Birds', 0), + 4 => new question_classified_response(4, 'Bee: Mammals', 0), + ], + 2 => question_classified_response::no_response(), + 3 => [ + 1 => new question_classified_response(1, 'Seagull: Insects', 0), + 2 => new question_classified_response(2, 'Seagull: Fish', 0), + 3 => new question_classified_response(3, 'Seagull: Birds', 1), + 4 => new question_classified_response(4, 'Seagull: Mammals', 0), + ], + 4 => [ + 1 => new question_classified_response(1, 'Dog: Insects', 0), + 2 => new question_classified_response(2, 'Dog: Fish', 0), + 3 => new question_classified_response(3, 'Dog: Birds', 0), + 4 => new question_classified_response(4, 'Dog: Mammals', 0), + ], + ]; + $this->assertEquals($expected, $question->classify_response($response)); + } + + public function test_prepare_simulated_post_data_single(): void { + $this->resetAfterTest(); + $question = \test_question_maker::make_question('oumatrix', 'animals_single'); + $question->shuffleanswers = 0; + $question->start_attempt(new question_attempt_step(), 1); + + $response = ['Bee' => 'Insects', 'Salmon' => 'Fish', 'Seagull' => 'Birds', 'Dog' => 'Mammals']; + $expected = ['rowanswers0' => 1, 'rowanswers1' => 2, 'rowanswers2' => 3, 'rowanswers3' => 4]; + $this->assertEquals($expected, $question->prepare_simulated_post_data($response)); + + $response = ['Bee' => 'Insects', 'Salmon' => 'Birds', 'Seagull' => 'Birds', 'Dog' => 'Mammals']; + $expected = ['rowanswers0' => 1, 'rowanswers1' => 3, 'rowanswers2' => 3, 'rowanswers3' => 4]; + $this->assertEquals($expected, $question->prepare_simulated_post_data($response)); + + $response = ['Bee' => 'Insects', 'Salmon' => 'Birds', 'Seagull' => 'Birds', 'Dog' => 'Insects']; + $expected = ['rowanswers0' => 1, 'rowanswers1' => 3, 'rowanswers2' => 3, 'rowanswers3' => 1]; + $this->assertEquals($expected, $question->prepare_simulated_post_data($response)); + } + public function test_is_same_response(): void { $question = \test_question_maker::make_question('oumatrix'); $question->start_attempt(new question_attempt_step(), 1); diff --git a/tests/questiontype_test.php b/tests/questiontype_test.php index 1634e89..678ace4 100644 --- a/tests/questiontype_test.php +++ b/tests/questiontype_test.php @@ -19,10 +19,13 @@ use qtype_oumatrix; use qtype_oumatrix_edit_form; use qtype_oumatrix_test_helper; +use question_bank; +use question_possible_response; defined('MOODLE_INTERNAL') || die(); global $CFG; + require_once($CFG->dirroot . '/question/engine/tests/helpers.php'); require_once($CFG->dirroot . '/question/type/oumatrix/tests/helper.php'); require_once($CFG->dirroot . '/question/type/oumatrix/questiontype.php'); @@ -70,6 +73,88 @@ public function test_get_random_guess_score_broken_question(): void { $this->assertNull($this->qtype->get_random_guess_score($q)); } + public function test_get_possible_responses_single(): void { + $this->resetAfterTest(); + $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); + $category = $generator->create_question_category([]); + $createdquestion = $generator->create_question('oumatrix', 'animals_single', + ['category' => $category->id, 'name' => 'Test question']); + $q = question_bank::load_question_data($createdquestion->id); + $expected = [ + 1 => [ + 1 => new question_possible_response('Bee: Insects', 1), + 2 => new question_possible_response('Bee: Fish', 0), + 3 => new question_possible_response('Bee: Birds', 0), + 4 => new question_possible_response('Bee: Mammals', 0), + null => question_possible_response::no_response(), + ], + 2 => [ + 1 => new question_possible_response('Salmon: Insects', 0), + 2 => new question_possible_response('Salmon: Fish', 1), + 3 => new question_possible_response('Salmon: Birds', 0), + 4 => new question_possible_response('Salmon: Mammals', 0), + null => question_possible_response::no_response(), + ], + 3 => [ + 1 => new question_possible_response('Seagull: Insects', 0), + 2 => new question_possible_response('Seagull: Fish', 0), + 3 => new question_possible_response('Seagull: Birds', 1), + 4 => new question_possible_response('Seagull: Mammals', 0), + null => question_possible_response::no_response(), + ], + 4 => [ + 1 => new question_possible_response('Dog: Insects', 0), + 2 => new question_possible_response('Dog: Fish', 0), + 3 => new question_possible_response('Dog: Birds', 0), + 4 => new question_possible_response('Dog: Mammals', 1), + null => question_possible_response::no_response(), + ], + ]; + $this->assertEquals($expected, $this->qtype->get_possible_responses($q)); + } + + public function test_get_possible_responses_multiple(): void { + $this->resetAfterTest(); + $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); + $category = $generator->create_question_category([]); + $createdquestion = $generator->create_question('oumatrix', 'food_multiple', + ['category' => $category->id, 'name' => 'Test question']); + $q = question_bank::load_question_data($createdquestion->id); + $expected = [ + 1 => [ + 1 => new question_possible_response('Proteins: Chicken breast', 1), + 2 => new question_possible_response('Proteins: Carrot', 0), + 3 => new question_possible_response('Proteins: Salmon fillet', 1), + 4 => new question_possible_response('Proteins: Asparagus', 0), + 5 => new question_possible_response('Proteins: Olive oil', 0), + 6 => new question_possible_response('Proteins: Steak', 1), + 7 => new question_possible_response('Proteins: Potato', 0), + null => question_possible_response::no_response(), + ], + 2 => [ + 1 => new question_possible_response('Vegetables: Chicken breast', 0), + 2 => new question_possible_response('Vegetables: Carrot', 1), + 3 => new question_possible_response('Vegetables: Salmon fillet', 0), + 4 => new question_possible_response('Vegetables: Asparagus', 1), + 5 => new question_possible_response('Vegetables: Olive oil', 0), + 6 => new question_possible_response('Vegetables: Steak', 0), + 7 => new question_possible_response('Vegetables: Potato', 1), + null => question_possible_response::no_response(), + ], + 3 => [ + 1 => new question_possible_response('Fats: Chicken breast', 0), + 2 => new question_possible_response('Fats: Carrot', 0), + 3 => new question_possible_response('Fats: Salmon fillet', 0), + 4 => new question_possible_response('Fats: Asparagus', 0), + 5 => new question_possible_response('Fats: Olive oil', 1), + 6 => new question_possible_response('Fats: Steak', 0), + 7 => new question_possible_response('Fats: Potato', 0), + null => question_possible_response::no_response(), + ], + ]; + $this->assertEquals($expected, $this->qtype->get_possible_responses($q)); + } + public function get_save_question_which() { return [['animals_single'], ['oumatrix_multiple']]; }