MDL-51147 qtype_match: stats should cope even if Q edited badly

The numbers become pretty meaningless, but we need to avoid fatal
errors.
This commit is contained in:
Tim Hunt 2015-08-20 19:12:02 +01:00
parent 1dabedeedf
commit 53eee12a7a
2 changed files with 60 additions and 7 deletions

View File

@ -134,25 +134,34 @@ class qtype_match_question extends question_graded_automatically_with_countback
}
public function classify_response(array $response) {
$selectedchoices = array();
$selectedchoicekeys = array();
foreach ($this->stemorder as $key => $stemid) {
if (array_key_exists($this->field($key), $response) && $response[$this->field($key)]) {
$selectedchoices[$stemid] = $this->choiceorder[$response[$this->field($key)]];
$selectedchoicekeys[$stemid] = $this->choiceorder[$response[$this->field($key)]];
} else {
$selectedchoices[$stemid] = 0;
$selectedchoicekeys[$stemid] = 0;
}
}
$parts = array();
foreach ($this->stems as $stemid => $stem) {
if (empty($selectedchoices[$stemid])) {
if ($this->right[$stemid] == 0 || !isset($selectedchoicekeys[$stemid])) {
// Choice for a deleted subquestion, ignore. (See apply_attempt_state.)
continue;
}
$selectedchoicekey = $selectedchoicekeys[$stemid];
if (empty($selectedchoicekey)) {
$parts[$stemid] = question_classified_response::no_response();
continue;
}
$choice = $this->choices[$selectedchoices[$stemid]];
$choice = $this->choices[$selectedchoicekey];
if ($choice == get_string('deletedchoice', 'qtype_match')) {
// Deleted choice, ignore. (See apply_attempt_state.)
continue;
}
$parts[$stemid] = new question_classified_response(
$selectedchoices[$stemid], $choice,
($selectedchoices[$stemid] == $this->right[$stemid]) / count($this->stems));
$selectedchoicekey, $choice,
($selectedchoicekey == $this->right[$stemid]) / count($this->stems));
}
return $parts;
}

View File

@ -181,6 +181,50 @@ class qtype_match_question_test extends advanced_testcase {
), $match->classify_response($response));
}
public function test_classify_response_choice_deleted_after_attempt() {
$match = test_question_maker::make_a_matching_question();
$firststep = new question_attempt_step();
$match->start_attempt($firststep, 1);
$response = $match->prepare_simulated_post_data(array(
'Dog' => 'Amphibian', 'Frog' => 'Insect', 'Toad' => '', 'Cat' => 'Mammal'));
$match = test_question_maker::make_a_matching_question();
unset($match->stems[4]);
unset($match->stemsformat[4]);
unset($match->right[4]);
$match->apply_attempt_state($firststep);
$this->assertEquals(array(
1 => new question_classified_response(2, 'Amphibian', 0),
2 => new question_classified_response(3, 'Insect', 0),
3 => question_classified_response::no_response(),
), $match->classify_response($response));
}
public function test_classify_response_choice_added_after_attempt() {
$match = test_question_maker::make_a_matching_question();
$firststep = new question_attempt_step();
$match->start_attempt($firststep, 1);
$response = $match->prepare_simulated_post_data(array(
'Dog' => 'Amphibian', 'Frog' => 'Insect', 'Toad' => '', 'Cat' => 'Mammal'));
$match = test_question_maker::make_a_matching_question();
$match->stems[5] = "Snake";
$match->stemsformat[5] = FORMAT_HTML;
$match->choices[5] = "Reptile";
$match->right[5] = 5;
$match->apply_attempt_state($firststep);
$this->assertEquals(array(
1 => new question_classified_response(2, 'Amphibian', 0),
2 => new question_classified_response(3, 'Insect', 0),
3 => question_classified_response::no_response(),
4 => new question_classified_response(1, 'Mammal', 0.20),
), $match->classify_response($response));
}
public function test_prepare_simulated_post_data() {
$m = test_question_maker::make_a_matching_question();
$m->start_attempt(new question_attempt_step(), 1);