public function test_immediatecbm_feedback_multichoice_right() { // Create a true-false question with correct answer true. $mc = test_question_maker::make_a_multichoice_single_question(); $this->start_attempt_at_question($mc, 'immediatecbm'); $rightindex = $this->get_mc_right_answer_index($mc); $wrongindex = ($rightindex + 1) % 3; // Check the initial state. $this->check_current_state(question_state::$todo); $this->check_current_mark(null); $this->check_current_output($this->get_contains_question_text_expectation($mc), $this->get_contains_mc_radio_expectation(0, true, false), $this->get_contains_mc_radio_expectation(1, true, false), $this->get_contains_mc_radio_expectation(2, true, false), $this->get_contains_submit_button_expectation(true), $this->get_does_not_contain_feedback_expectation()); $this->assertEquals('A [' . question_cbm::get_short_string(question_cbm::HIGH) . ']', $this->quba->get_right_answer_summary($this->slot)); $this->assertRegExp('/' . preg_quote($mc->questiontext, '/') . '/', $this->quba->get_question_summary($this->slot)); $this->assertNull($this->quba->get_response_summary($this->slot)); // Save the wrong answer. $this->process_submission(array('answer' => $wrongindex, '-certainty' => 1)); // Verify. $this->check_current_state(question_state::$todo); $this->check_current_mark(null); $this->check_current_output($this->get_contains_mc_radio_expectation($wrongindex, true, true), $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, true, false), $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, true, false), $this->get_contains_submit_button_expectation(true), $this->get_does_not_contain_correctness_expectation(), $this->get_does_not_contain_feedback_expectation()); // Submit the right answer. $this->process_submission(array('answer' => $rightindex, '-certainty' => 2, '-submit' => 1)); // Verify. $this->check_current_state(question_state::$gradedright); $this->check_current_mark(2); $this->check_current_output($this->get_contains_mc_radio_expectation($rightindex, false, true), $this->get_contains_mc_radio_expectation(($rightindex + 1) % 3, false, false), $this->get_contains_mc_radio_expectation(($rightindex + 1) % 3, false, false), $this->get_contains_correct_expectation()); $this->assertEquals('A [' . question_cbm::get_short_string(2) . ']', $this->quba->get_response_summary($this->slot)); $numsteps = $this->get_step_count(); // Finish the attempt - should not need to add a new state. $this->quba->finish_all_questions(); // Verify. $this->assertEquals($numsteps, $this->get_step_count()); $this->check_current_state(question_state::$gradedright); $this->check_current_mark(2); $this->check_current_output($this->get_contains_mc_radio_expectation($rightindex, false, true), $this->get_contains_mc_radio_expectation(($rightindex + 1) % 3, false, false), $this->get_contains_mc_radio_expectation(($rightindex + 1) % 3, false, false), $this->get_contains_correct_expectation()); // Process a manual comment. $this->manual_grade('Not good enough!', 0.5, FORMAT_HTML); // Verify. $this->check_current_state(question_state::$mangrpartial); $this->check_current_mark(0.5); $this->check_current_output($this->get_contains_partcorrect_expectation(), new question_pattern_expectation('/' . preg_quote('Not good enough!', '/') . '/')); // Now change the correct answer to the question, and regrade. $mc->answers[13]->fraction = -0.33333333; $mc->answers[15]->fraction = 1; $this->quba->regrade_all_questions(); // Verify. $this->check_current_state(question_state::$mangrpartial); $this->check_current_mark(0.5); $this->check_current_output($this->get_contains_partcorrect_expectation()); $autogradedstep = $this->get_step($this->get_step_count() - 2); $this->assertEquals($autogradedstep->get_fraction(), -2, '', 1.0E-7); }
public function summarise_usage(question_usage_by_activity $quba, question_display_options $options) { $summarydata = parent::summarise_usage($quba, $options); if ($options->marks < question_display_options::MARK_AND_MAX) { return $summarydata; } // Prepare accumulators to hold the data we are about to collect. $notansweredcount = 0; $notansweredweight = 0; $attemptcount = array(question_cbm::HIGH => 0, question_cbm::MED => 0, question_cbm::LOW => 0); $totalweight = array(question_cbm::HIGH => 0, question_cbm::MED => 0, question_cbm::LOW => 0); $totalrawscore = array(question_cbm::HIGH => 0, question_cbm::MED => 0, question_cbm::LOW => 0); $totalcbmscore = array(question_cbm::HIGH => 0, question_cbm::MED => 0, question_cbm::LOW => 0); // Loop through the data, and add it to the accumulators. foreach ($quba->get_attempt_iterator() as $qa) { if (strpos($qa->get_behaviour_name(), 'cbm') === false || $qa->get_max_mark() < 5.0E-7) { continue; } $gradedstep = $qa->get_last_step_with_behaviour_var('_rawfraction'); if (!$gradedstep->has_behaviour_var('_rawfraction')) { $notansweredcount += 1; $notansweredweight += $qa->get_max_mark(); continue; } $certainty = $qa->get_last_behaviour_var('certainty'); if (is_null($certainty) || $certainty == -1) { // Certainty -1 has never been used in standard Moodle, but is // used in Tony-Gardiner Medwin's patches to mean 'No idea' which // we intend to implement: MDL-42077. In the mean time, avoid // errors for people who have used TGM's patches. $certainty = question_cbm::default_certainty(); } $attemptcount[$certainty] += 1; $totalweight[$certainty] += $qa->get_max_mark(); $totalrawscore[$certainty] += $qa->get_max_mark() * $gradedstep->get_behaviour_var('_rawfraction'); $totalcbmscore[$certainty] += $qa->get_mark(); } // Hence compute some statistics. $totalquestions = $notansweredcount + array_sum($attemptcount); $grandtotalweight = $notansweredweight + array_sum($totalweight); $accuracy = array_sum($totalrawscore) / $grandtotalweight; $averagecbm = array_sum($totalcbmscore) / $grandtotalweight; $cbmbonus = $this->calculate_bonus($averagecbm, $accuracy); $accuracyandbonus = $accuracy + $cbmbonus; // Now we can start generating some of the summary: overall values. $summarydata['qbehaviour_cbm_entire_quiz_heading'] = array('title' => '', 'content' => html_writer::tag('h3', get_string('forentirequiz', 'qbehaviour_deferredcbm', $totalquestions), array('class' => 'qbehaviour_deferredcbm_summary_heading'))); $summarydata['qbehaviour_cbm_entire_quiz_cbm_average'] = array('title' => get_string('averagecbmmark', 'qbehaviour_deferredcbm'), 'content' => format_float($averagecbm, $options->markdp)); $summarydata['qbehaviour_cbm_entire_quiz_accuracy'] = array('title' => get_string('accuracy', 'qbehaviour_deferredcbm'), 'content' => $this->format_probability($accuracy, 1)); $summarydata['qbehaviour_cbm_entire_quiz_cbm_bonus'] = array('title' => get_string('cbmbonus', 'qbehaviour_deferredcbm'), 'content' => $this->format_probability($cbmbonus, 1)); $summarydata['qbehaviour_cbm_entire_quiz_accuracy_and_bonus'] = array('title' => get_string('accuracyandbonus', 'qbehaviour_deferredcbm'), 'content' => $this->format_probability($accuracyandbonus, 1)); if ($notansweredcount && array_sum($attemptcount) > 0) { $totalquestions = array_sum($attemptcount); $grandtotalweight = array_sum($totalweight); $accuracy = array_sum($totalrawscore) / $grandtotalweight; $averagecbm = array_sum($totalcbmscore) / $grandtotalweight; $cbmbonus = $this->calculate_bonus($averagecbm, $accuracy); $accuracyandbonus = $accuracy + $cbmbonus; $summarydata['qbehaviour_cbm_answered_quiz_heading'] = array('title' => '', 'content' => html_writer::tag('h3', get_string('foransweredquestions', 'qbehaviour_deferredcbm', $totalquestions), array('class' => 'qbehaviour_deferredcbm_summary_heading'))); $summarydata['qbehaviour_cbm_answered_quiz_cbm_average'] = array('title' => get_string('averagecbmmark', 'qbehaviour_deferredcbm'), 'content' => format_float($averagecbm, $options->markdp)); $summarydata['qbehaviour_cbm_answered_quiz_accuracy'] = array('title' => get_string('accuracy', 'qbehaviour_deferredcbm'), 'content' => $this->format_probability($accuracy, 1)); $summarydata['qbehaviour_cbm_answered_quiz_cbm_bonus'] = array('title' => get_string('cbmbonus', 'qbehaviour_deferredcbm'), 'content' => $this->format_probability($cbmbonus, 1)); $summarydata['qbehaviour_cbm_answered_quiz_accuracy_and_bonus'] = array('title' => get_string('accuracyandbonus', 'qbehaviour_deferredcbm'), 'content' => $this->format_probability($accuracyandbonus, 1)); } // Now per-certainty level values. $summarydata['qbehaviour_cbm_judgement_heading'] = array('title' => '', 'content' => html_writer::tag('h3', get_string('breakdownbycertainty', 'qbehaviour_deferredcbm'), array('class' => 'qbehaviour_deferredcbm_summary_heading'))); foreach ($attemptcount as $certainty => $count) { $key = 'qbehaviour_cbm_judgement' . $certainty; $title = question_cbm::get_short_string($certainty); if ($count == 0) { $summarydata[$key] = array('title' => $title, 'content' => get_string('noquestions', 'qbehaviour_deferredcbm')); continue; } $lowerlimit = question_cbm::optimal_probablility_low($certainty); $upperlimit = question_cbm::optimal_probablility_high($certainty); $fraction = $totalrawscore[$certainty] / $totalweight[$certainty]; $a = new stdClass(); $a->responses = $count; $a->idealrangelow = $this->format_probability($lowerlimit); $a->idealrangehigh = $this->format_probability($upperlimit); $a->fraction = html_writer::tag('span', $this->format_probability($fraction), array('class' => 'qbehaviour_deferredcbm_actual_percentage')); if ($fraction < $lowerlimit - 5.0E-7) { if (pow($fraction - $lowerlimit, 2) * $count > 0.5) { // Rough indicator of significance: t > 1.5 or 1.8. $judgement = 'overconfident'; } else { $judgement = 'slightlyoverconfident'; } } else { if ($fraction > $upperlimit + 5.0E-7) { if (pow($fraction - $upperlimit, 2) * $count > 0.5) { $judgement = 'underconfident'; } else { $judgement = 'slightlyunderconfident'; } } else { $judgement = 'judgementok'; } } $a->judgement = html_writer::tag('span', get_string($judgement, 'qbehaviour_deferredcbm'), array('class' => 'qbehaviour_deferredcbm_' . $judgement)); $summarydata[$key] = array('title' => $title, 'content' => get_string('judgementsummary', 'qbehaviour_deferredcbm', $a)); } return $summarydata; }
public function test_deferredcbm_resume_multichoice_single() { // Create a multiple-choice question. $mc = test_question_maker::make_a_multichoice_single_question(); // Attempt it getting it wrong. $this->start_attempt_at_question($mc, 'deferredcbm', 1); $rightindex = $this->get_mc_right_answer_index($mc); $wrongindex = ($rightindex + 1) % 3; $this->process_submission(array('answer' => $wrongindex, '-certainty' => 2)); $this->quba->finish_all_questions(); // Verify. $this->check_current_state(question_state::$gradedwrong); $this->check_current_mark(-2); $this->check_current_output($this->get_contains_mc_radio_expectation($wrongindex, false, true), $this->get_contains_cbm_radio_expectation(2, false, true), $this->get_contains_incorrect_expectation()); $this->assertEquals('A [' . question_cbm::get_short_string(question_cbm::HIGH) . ']', $this->quba->get_right_answer_summary($this->slot)); $this->assertRegExp('/' . preg_quote($mc->questiontext, '/') . '/', $this->quba->get_question_summary($this->slot)); $this->assertRegExp('/(B|C) \\[' . preg_quote(question_cbm::get_short_string(question_cbm::MED), '/') . '\\]/', $this->quba->get_response_summary($this->slot)); // Save the old attempt. $oldqa = $this->quba->get_question_attempt($this->slot); // Reinitialise. $this->setUp(); $this->quba->set_preferred_behaviour('deferredcbm'); $this->slot = $this->quba->add_question($mc, 1); $this->quba->start_question_based_on($this->slot, $oldqa); // Verify. $this->check_current_state(question_state::$complete); $this->check_output_contains_lang_string('notchanged', 'question'); $this->check_current_mark(null); $this->check_current_output($this->get_contains_mc_radio_expectation($wrongindex, true, true), $this->get_contains_cbm_radio_expectation(2, true, true), $this->get_does_not_contain_feedback_expectation(), $this->get_does_not_contain_correctness_expectation()); $this->assertEquals('A [' . question_cbm::get_short_string(question_cbm::HIGH) . ']', $this->quba->get_right_answer_summary($this->slot)); $this->assertRegExp('/' . preg_quote($mc->questiontext, '/') . '/', $this->quba->get_question_summary($this->slot)); $this->assertNull($this->quba->get_response_summary($this->slot)); // Now get it right. $this->process_submission(array('answer' => $rightindex, '-certainty' => 3)); $this->quba->finish_all_questions(); // Verify. $this->check_current_state(question_state::$gradedright); $this->check_current_mark(3); $this->check_current_output($this->get_contains_mc_radio_expectation($rightindex, false, true), $this->get_contains_cbm_radio_expectation(question_cbm::HIGH, false, true), $this->get_contains_correct_expectation()); $this->assertRegExp('/(A) \\[' . preg_quote(question_cbm::get_short_string(question_cbm::HIGH), '/') . '\\]/', $this->quba->get_response_summary($this->slot)); }