From 307f045f0718da95d00faf38a630d4f3169f12e5 Mon Sep 17 00:00:00 2001 From: tjhunt Date: Thu, 24 Aug 2006 10:22:43 +0000 Subject: [PATCH] MDL-5969 Let multiple choice questions have feedback that does not depend on the chosen answer In passing, I fixed MDL-6297 Export of multichoice questions in Moodle XML format doesn't save shuffle option too. --- lang/en_utf8/qtype_multichoice.php | 25 ++++++++ question/format/xml/format.php | 30 ++++++++- question/restorelib.php | 1 + question/type/multichoice/db/mysql.php | 11 +++- question/type/multichoice/db/mysql.sql | 3 + question/type/multichoice/db/postgres7.php | 11 +++- question/type/multichoice/db/postgres7.sql | 5 +- question/type/multichoice/display.html | 5 ++ question/type/multichoice/editquestion.html | 46 ++++++++++--- question/type/multichoice/editquestion.php | 5 +- question/type/multichoice/questiontype.php | 71 ++++++++++++++++----- question/type/multichoice/version.php | 2 +- theme/standard/styles_color.css | 5 +- theme/standard/styles_layout.css | 2 +- 14 files changed, 184 insertions(+), 38 deletions(-) create mode 100644 lang/en_utf8/qtype_multichoice.php diff --git a/lang/en_utf8/qtype_multichoice.php b/lang/en_utf8/qtype_multichoice.php new file mode 100644 index 0000000000..69b5e10991 --- /dev/null +++ b/lang/en_utf8/qtype_multichoice.php @@ -0,0 +1,25 @@ +Instead, they add up to $a%%
Do you want to go back and fix this question?'; +$string['fractionsnomax'] = 'One of the answers should be 100%%, so that it is
possible to get a full grade for this question.
Do you want to go back and fix this question?'; +$string['feedback'] = 'Feedback'; +$string['notenoughanswers'] = 'This type of question requires at least $a answers'; +$string['overallcorrectfeedback'] = 'Feedback for any correct answer'; +$string['overallpartiallycorrectfeedback'] = 'Feedback for any partially correct answer'; +$string['overallincorrectfeedback'] = 'Feedback for any incorrect answer'; +$string['shuffleanswers'] = 'Shuffle answers'; +$string['singleanswer'] = 'Choose one answer.'; + +?> diff --git a/question/format/xml/format.php b/question/format/xml/format.php index f11198e01c..d1583dbb17 100755 --- a/question/format/xml/format.php +++ b/question/format/xml/format.php @@ -82,6 +82,21 @@ class qformat_xml extends qformat_default { return addslashes(trim( $data )); } + /** + * Process text from an element in the XML that may or not be there. + * @param string $subelement the name of the element which is either present or missing. + * @param array $question a bit of xml tree, this method looks for $question['#'][$subelement][0]['#']['text']. + * @return string If $subelement is present, return the content of the text tag inside it. + * Otherwise returns an empty string. + */ + function import_optional_text($subelement, $question) { + if (array_key_exists($subelement, $question['#'])) { + return $this->import_text($question['#'][$subelement][0]['#']['text']); + } else { + return ''; + } + } + /** * import parts of question common to all types * @param array question question array from xml tree @@ -149,7 +164,16 @@ class qformat_xml extends qformat_default { $qo->qtype = MULTICHOICE; $single = $question['#']['single'][0]['#']; $qo->single = $this->trans_single( $single ); - + if (array_key_exists('shuffleanswers', $question['#'])) { + $shuffleanswers = $question['#']['shuffleanswers'][0]['#']; + } else { + $shuffleanswers = 'false'; + } + $qo->$shuffleanswers = $this->trans_single($shuffleanswers); + $qo->correctfeedback = $this->import_optional_text('correctfeedback', $question); + $qo->partiallycorrectfeedback = $this->import_optional_text('partiallycorrectfeedback', $question); + $qo->incorrectfeedback = $this->import_optional_text('incorrectfeedback', $question); + // run through the answers $answers = $question['#']['answer']; $a_count = 0; @@ -671,6 +695,10 @@ class qformat_xml extends qformat_default { break; case MULTICHOICE: $expout .= " ".$this->get_single($question->options->single)."\n"; + $expout .= " ".$this->get_single($question->options->shuffleanswers)."\n"; + $expout .= " ".$this->writetext($question->options->correctfeedback, 3)."\n"; + $expout .= " ".$this->writetext($question->options->partiallycorrectfeedback, 3)."\n"; + $expout .= " ".$this->writetext($question->options->incorrectfeedback, 3)."\n"; foreach($question->options->answers as $answer) { $percent = $answer->fraction * 100; $expout .= " \n"; diff --git a/question/restorelib.php b/question/restorelib.php index 25630ccfce..b46a3c9f03 100644 --- a/question/restorelib.php +++ b/question/restorelib.php @@ -614,6 +614,7 @@ $state->grade = backup_todb($res_info['#']['GRADE']['0']['#']); $state->raw_grade = backup_todb($res_info['#']['RAW_GRADE']['0']['#']); $state->penalty = backup_todb($res_info['#']['PENALTY']['0']['#']); + $state->oldid = $oldid; // So it is available to restore_recode_answer. //We have to recode the question field $question = backup_getid($restore->backup_unique_code,"question",$state->question); diff --git a/question/type/multichoice/db/mysql.php b/question/type/multichoice/db/mysql.php index c95cd21f73..d8fa976b72 100644 --- a/question/type/multichoice/db/mysql.php +++ b/question/type/multichoice/db/mysql.php @@ -4,8 +4,15 @@ function qtype_multichoice_upgrade($oldversion=0) { global $CFG; - - return true; + $success = true; + + if ($success && $oldversion < 2006081900) { + $success = $success && table_column('question_multichoice', '', 'correctfeedback', 'text', '', '', ''); + $success = $success && table_column('question_multichoice', '', 'partiallycorrectfeedback', 'text', '', '', ''); + $success = $success && table_column('question_multichoice', '', 'incorrectfeedback', 'text', '', '', ''); + } + + return $success; } ?> diff --git a/question/type/multichoice/db/mysql.sql b/question/type/multichoice/db/mysql.sql index e729bb199f..33016d3cfb 100644 --- a/question/type/multichoice/db/mysql.sql +++ b/question/type/multichoice/db/mysql.sql @@ -11,6 +11,9 @@ CREATE TABLE prefix_question_multichoice ( answers varchar(255) NOT NULL default '', single tinyint(4) NOT NULL default '0', shuffleanswers tinyint(4) NOT NULL default '1', + correctfeedback text NOT NULL default '', + partiallycorrectfeedback text NOT NULL default '', + incorrectfeedback text NOT NULL default '', PRIMARY KEY (id), KEY question (question) ) TYPE=MyISAM COMMENT='Options for multiple choice questions'; diff --git a/question/type/multichoice/db/postgres7.php b/question/type/multichoice/db/postgres7.php index b811bfe8cb..2c11f2cffe 100644 --- a/question/type/multichoice/db/postgres7.php +++ b/question/type/multichoice/db/postgres7.php @@ -4,8 +4,15 @@ function qtype_multichoice_upgrade($oldversion=0) { global $CFG; - - return true; + $success = true; + + if ($success && $oldversion < 2006081900) { + $success = $success && table_column('question_multichoice', '', 'correctfeedback', 'text', '', '', ''); + $success = $success && table_column('question_multichoice', '', 'partiallycorrectfeedback', 'text', '', '', ''); + $success = $success && table_column('question_multichoice', '', 'incorrectfeedback', 'text', '', '', ''); + } + + return $success; } ?> diff --git a/question/type/multichoice/db/postgres7.sql b/question/type/multichoice/db/postgres7.sql index 036278b9e8..44a425afb2 100644 --- a/question/type/multichoice/db/postgres7.sql +++ b/question/type/multichoice/db/postgres7.sql @@ -11,7 +11,10 @@ CREATE TABLE prefix_question_multichoice ( layout integer NOT NULL default '0', answers varchar(255) NOT NULL default '', single integer NOT NULL default '0', - shuffleanswers integer NOT NULL default '1' + shuffleanswers integer NOT NULL default '1', + correctfeedback text NOT NULL default '', + partiallycorrectfeedback text NOT NULL default '', + incorrectfeedback text NOT NULL default '' ); CREATE INDEX prefix_question_multichoice_question_idx ON prefix_question_multichoice (question); diff --git a/question/type/multichoice/display.html b/question/type/multichoice/display.html index ef553d29f6..83960bc132 100644 --- a/question/type/multichoice/display.html +++ b/question/type/multichoice/display.html @@ -28,5 +28,10 @@ + + + print_question_submit_buttons($question, $state, $cmoptions, $options); ?> diff --git a/question/type/multichoice/editquestion.html b/question/type/multichoice/editquestion.html index 472fe062d3..05d62a131c 100644 --- a/question/type/multichoice/editquestion.html +++ b/question/type/multichoice/editquestion.html @@ -2,28 +2,28 @@ $QTYPES[$question->qtype]->print_question_form_start($question, array(), $course, $usehtmleditor); ?> - : + : single", ""); unset($menu); ?> - : + : shuffleanswers", ""); - helpbutton("multichoiceshuffle", get_string("shuffleanswers","quiz"), "quiz"); + helpbutton("multichoiceshuffle", get_string("shuffleanswers","qtype_multichoice"), "quiz"); ?> - : - + : + - : + : - "/>   +    - : + : @@ -61,7 +61,33 @@ for ($i=1; $i<=count($answers); $i++) { + + : + + + + + + + : + + + + + + : + + + + + + +   + + +qtype]->print_replacement_options($question, $course, $contextquiz); $QTYPES[$question->qtype]->print_question_form_end($question); ?> diff --git a/question/type/multichoice/editquestion.php b/question/type/multichoice/editquestion.php index 2f55609deb..dc77af1b5b 100644 --- a/question/type/multichoice/editquestion.php +++ b/question/type/multichoice/editquestion.php @@ -5,6 +5,9 @@ } else { $options->single = 1; $options->shuffleanswers = 1; + $options->correctfeedback = ''; + $options->partiallycorrectfeedback = ''; + $options->incorrectfeedback = ''; } if (!empty($options->answers)) { $answersraw = get_records_list("question_answers", "id", $options->answers); @@ -29,7 +32,7 @@ $yesnooptions[0] = get_string("no"); $yesnooptions[1] = get_string("yes"); - print_heading_with_help(get_string("editingmultichoice", "quiz"), "multichoice", "quiz"); + print_heading_with_help(get_string("editingmultichoice", "qtype_multichoice"), "multichoice", "quiz"); require("$CFG->dirroot/question/type/multichoice/editquestion.html"); ?> diff --git a/question/type/multichoice/questiontype.php b/question/type/multichoice/questiontype.php index 44ccadff9e..63404e7801 100644 --- a/question/type/multichoice/questiontype.php +++ b/question/type/multichoice/questiontype.php @@ -35,7 +35,7 @@ class question_multichoice_qtype extends default_questiontype { } function save_question_options($question) { - + $result = new stdClass; if (!$oldanswers = get_records("question_answers", "question", $question->id, "id ASC")) { $oldanswers = array(); @@ -50,7 +50,7 @@ class question_multichoice_qtype extends default_questiontype { } $answercount += count($oldanswers); if ($answercount < 2) { // check there are at lest 2 answers for multiple choice - $result->notice = get_string("notenoughanswers", "quiz", "2"); + $result->notice = get_string("notenoughanswers", "qtype_multichoice", "2"); return $result; } @@ -93,20 +93,26 @@ class question_multichoice_qtype extends default_questiontype { } } - if ($options = get_record("question_multichoice", "question", $question->id)) { - $options->answers = implode(",",$answers); - $options->single = $question->single; - $options->shuffleanswers = $question->shuffleanswers; + $update = true; + $options = get_record("question_multichoice", "question", $question->id); + if (!$options) { + $update = false; + $options = new stdClass; + $options->question = $question->id; + + } + $options->answers = implode(",",$answers); + $options->single = $question->single; + $options->shuffleanswers = $question->shuffleanswers; + $options->correctfeedback = trim($question->correctfeedback); + $options->partiallycorrectfeedback = trim($question->partiallycorrectfeedback); + $options->incorrectfeedback = trim($question->incorrectfeedback); + if ($update) { if (!update_record("question_multichoice", $options)) { $result->error = "Could not update quiz multichoice options! (id=$options->id)"; return $result; } } else { - unset($options); - $options->question = $question->id; - $options->answers = implode(",",$answers); - $options->single = $question->single; - $options->shuffleanswers = $question->shuffleanswers; if (!insert_record("question_multichoice", $options)) { $result->error = "Could not insert quiz multichoice options!"; return $result; @@ -124,14 +130,14 @@ class question_multichoice_qtype extends default_questiontype { if ($options->single) { if ($maxfraction != 1) { $maxfraction = $maxfraction * 100; - $result->noticeyesno = get_string("fractionsnomax", "quiz", $maxfraction); + $result->noticeyesno = get_string("fractionsnomax", "qtype_multichoice", $maxfraction); return $result; } } else { $totalfraction = round($totalfraction,2); if ($totalfraction != 1) { $totalfraction = $totalfraction * 100; - $result->noticeyesno = get_string("fractionsaddwrong", "quiz", $totalfraction); + $result->noticeyesno = get_string("fractionsaddwrong", "qtype_multichoice", $totalfraction); return $result; } } @@ -291,6 +297,7 @@ class question_multichoice_qtype extends default_questiontype { ? 'checked="checked"' : ''; } + $a = new stdClass; $a->id = $question->name_prefix . $aid; // Print the control @@ -314,6 +321,21 @@ class question_multichoice_qtype extends default_questiontype { $anss[] = clone($a); } + + $feedback = ''; + if ($options->feedback) { + if ($state->raw_grade >= $question->maxgrade/1.01) { + $feedback = $question->options->correctfeedback; + } else if ($state->raw_grade > 0) { + $feedback = $question->options->partiallycorrectfeedback; + } else { + $feedback = $question->options->incorrectfeedback; + } + $feedback = format_text($feedback, + $question->questiontextformat, + $formatoptions, $cmoptions->course); + } + include("$CFG->dirroot/question/type/multichoice/display.html"); } @@ -392,6 +414,9 @@ class question_multichoice_qtype extends default_questiontype { fwrite ($bf,full_tag("ANSWERS",$level+1,false,$multichoice->answers)); fwrite ($bf,full_tag("SINGLE",$level+1,false,$multichoice->single)); fwrite ($bf,full_tag("SHUFFLEANSWERS",$level+1,false,$multichoice->shuffleanswers)); + fwrite ($bf,full_tag("CORRECTFEEDBACK",$level+1,false,$multichoice->correctfeedback)); + fwrite ($bf,full_tag("PARTIALLYCORRECTFEEDBACK",$level+1,false,$multichoice->partiallycorrectfeedback)); + fwrite ($bf,full_tag("INCORRECTFEEDBACK",$level+1,false,$multichoice->incorrectfeedback)); $status = fwrite ($bf,end_tag("MULTICHOICE",$level,true)); } @@ -420,11 +445,27 @@ class question_multichoice_qtype extends default_questiontype { $mul_info = $multichoices[$i]; //Now, build the question_multichoice record structure + $multichoice = new stdClass; $multichoice->question = $new_question_id; $multichoice->layout = backup_todb($mul_info['#']['LAYOUT']['0']['#']); $multichoice->answers = backup_todb($mul_info['#']['ANSWERS']['0']['#']); $multichoice->single = backup_todb($mul_info['#']['SINGLE']['0']['#']); $multichoice->shuffleanswers = backup_todb($mul_info['#']['SHUFFLEANSWERS']['0']['#']); + if (array_key_exists("CORRECTFEEDBACK", $mul_info['#'])) { + $multichoice->correctfeedback = backup_todb($mul_info['#']['CORRECTFEEDBACK']['0']['#']); + } else { + $multichoice->correctfeedback = ''; + } + if (array_key_exists("PARTIALLYCORRECTFEEDBACK", $mul_info['#'])) { + $multichoice->partiallycorrectfeedback = backup_todb($mul_info['#']['PARTIALLYCORRECTFEEDBACK']['0']['#']); + } else { + $multichoice->partiallycorrectfeedback = ''; + } + if (array_key_exists("INCORRECTFEEDBACK", $mul_info['#'])) { + $multichoice->incorrectfeedback = backup_todb($mul_info['#']['INCORRECTFEEDBACK']['0']['#']); + } else { + $multichoice->incorrectfeedback = ''; + } //We have to recode the answers field (a list of answers id) //Extracts answer id from sequence @@ -490,7 +531,7 @@ class question_multichoice_qtype extends default_questiontype { if ($answer) { $order[$key] = $answer->new_id; } else { - echo 'Could not recode multichoice answer id '.$oldansid.' for state '.$oldid.'
'; + echo 'Could not recode multichoice answer id '.$oldansid.' for state '.$state->oldid.'
'; } } } @@ -500,7 +541,7 @@ class question_multichoice_qtype extends default_questiontype { if ($answer) { $responses[$key] = $answer->new_id; } else { - echo 'Could not recode multichoice response answer id '.$oldansid.' for state '.$oldid.'
'; + echo 'Could not recode multichoice response answer id '.$oldansid.' for state '.$state->oldid.'
'; } } } diff --git a/question/type/multichoice/version.php b/question/type/multichoice/version.php index 2d2a8d9fa7..e6e6337095 100644 --- a/question/type/multichoice/version.php +++ b/question/type/multichoice/version.php @@ -1,6 +1,6 @@ version = 2006032200; +$plugin->version = 2006081900; $plugin->requires = 2006032200; ?> diff --git a/theme/standard/styles_color.css b/theme/standard/styles_color.css index 9fa32e9afc..fb1c68bcda 100644 --- a/theme/standard/styles_color.css +++ b/theme/standard/styles_color.css @@ -615,10 +615,7 @@ table.message_search_results td { .truefalse .answer { background-color: #EEE; } -.calculated .feedback, -.numerical .feedback, -.shortanswer .feedback, -.truefalse .feedback { +.que .feedback { border-color: #DDD; } .que.multianswer .incorrect { diff --git a/theme/standard/styles_layout.css b/theme/standard/styles_layout.css index 7f8763f99e..68138961f2 100644 --- a/theme/standard/styles_layout.css +++ b/theme/standard/styles_layout.css @@ -1208,7 +1208,7 @@ body#message-messages { padding: 0 0 0.3em 0.3em; border: 1px solid; } -.multichoice .feedback { +.multichoice td.feedback { width: auto; vertical-align: top; padding-top: 0.3em; -- 2.39.5