Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/question/type/multichoice/question.php
Go to the documentation of this file.
00001 <?php
00002 // This file is part of Moodle - http://moodle.org/
00003 //
00004 // Moodle is free software: you can redistribute it and/or modify
00005 // it under the terms of the GNU General Public License as published by
00006 // the Free Software Foundation, either version 3 of the License, or
00007 // (at your option) any later version.
00008 //
00009 // Moodle is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 // GNU General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU General Public License
00015 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
00016 
00027 defined('MOODLE_INTERNAL') || die();
00028 
00029 
00037 abstract class qtype_multichoice_base extends question_graded_automatically {
00038     const LAYOUT_DROPDOWN = 0;
00039     const LAYOUT_VERTICAL = 1;
00040     const LAYOUT_HORIZONTAL = 2;
00041 
00042     public $answers;
00043 
00044     public $shuffleanswers;
00045     public $answernumbering;
00046     public $layout = self::LAYOUT_VERTICAL;
00047 
00048     public $correctfeedback;
00049     public $correctfeedbackformat;
00050     public $partiallycorrectfeedback;
00051     public $partiallycorrectfeedbackformat;
00052     public $incorrectfeedback;
00053     public $incorrectfeedbackformat;
00054 
00055     protected $order = null;
00056 
00057     public function start_attempt(question_attempt_step $step, $variant) {
00058         $this->order = array_keys($this->answers);
00059         if ($this->shuffleanswers) {
00060             shuffle($this->order);
00061         }
00062         $step->set_qt_var('_order', implode(',', $this->order));
00063     }
00064 
00065     public function apply_attempt_state(question_attempt_step $step) {
00066         $this->order = explode(',', $step->get_qt_var('_order'));
00067     }
00068 
00069     public function get_question_summary() {
00070         $question = $this->html_to_text($this->questiontext, $this->questiontextformat);
00071         $choices = array();
00072         foreach ($this->order as $ansid) {
00073             $choices[] = $this->html_to_text($this->answers[$ansid]->answer,
00074                     $this->answers[$ansid]->answerformat);
00075         }
00076         return $question . ': ' . implode('; ', $choices);
00077     }
00078 
00079     public function get_order(question_attempt $qa) {
00080         $this->init_order($qa);
00081         return $this->order;
00082     }
00083 
00084     protected function init_order(question_attempt $qa) {
00085         if (is_null($this->order)) {
00086             $this->order = explode(',', $qa->get_step(0)->get_qt_var('_order'));
00087         }
00088     }
00089 
00090     public abstract function get_response(question_attempt $qa);
00091 
00092     public abstract function is_choice_selected($response, $value);
00093 
00094     public function check_file_access($qa, $options, $component, $filearea, $args, $forcedownload) {
00095         if ($component == 'question' && in_array($filearea,
00096                 array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback'))) {
00097             return $this->check_combined_feedback_file_access($qa, $options, $filearea);
00098 
00099         } else if ($component == 'question' && $filearea == 'answer') {
00100             $answerid = reset($args); // itemid is answer id.
00101             return  in_array($answerid, $this->order);
00102 
00103         } else if ($component == 'question' && $filearea == 'answerfeedback') {
00104             $answerid = reset($args); // itemid is answer id.
00105             $response = $this->get_response($qa);
00106             $isselected = false;
00107             foreach ($this->order as $value => $ansid) {
00108                 if ($ansid == $answerid) {
00109                     $isselected = $this->is_choice_selected($response, $value);
00110                     break;
00111                 }
00112             }
00113             // $options->suppresschoicefeedback is a hack specific to the
00114             // oumultiresponse question type. It would be good to refactor to
00115             // avoid refering to it here.
00116             return $options->feedback && empty($options->suppresschoicefeedback) &&
00117                     $isselected;
00118 
00119         } else if ($component == 'question' && $filearea == 'hint') {
00120             return $this->check_hint_file_access($qa, $options, $args);
00121 
00122         } else {
00123             return parent::check_file_access($qa, $options, $component, $filearea,
00124                     $args, $forcedownload);
00125         }
00126     }
00127 
00128     public function make_html_inline($html) {
00129         $html = preg_replace('~\s*<p>\s*~', '', $html);
00130         $html = preg_replace('~\s*</p>\s*~', '<br />', $html);
00131         $html = preg_replace('~<br />$~', '', $html);
00132         return $html;
00133     }
00134 }
00135 
00136 
00143 class qtype_multichoice_single_question extends qtype_multichoice_base {
00144     public function get_renderer(moodle_page $page) {
00145         return $page->get_renderer('qtype_multichoice', 'single');
00146     }
00147 
00148     public function get_min_fraction() {
00149         $minfraction = 0;
00150         foreach ($this->answers as $ans) {
00151             $minfraction = min($minfraction, $ans->fraction);
00152         }
00153         return $minfraction;
00154     }
00155 
00162     public function get_expected_data() {
00163         return array('answer' => PARAM_INT);
00164     }
00165 
00166     public function summarise_response(array $response) {
00167         if (!array_key_exists('answer', $response) ||
00168                 !array_key_exists($response['answer'], $this->order)) {
00169             return null;
00170         }
00171         $ansid = $this->order[$response['answer']];
00172         return $this->html_to_text($this->answers[$ansid]->answer,
00173                 $this->answers[$ansid]->answerformat);
00174     }
00175 
00176     public function classify_response(array $response) {
00177         if (!array_key_exists('answer', $response) ||
00178                 !array_key_exists($response['answer'], $this->order)) {
00179             return array($this->id => question_classified_response::no_response());
00180         }
00181         $choiceid = $this->order[$response['answer']];
00182         $ans = $this->answers[$choiceid];
00183         return array($this->id => new question_classified_response($choiceid,
00184                 $this->html_to_text($ans->answer, $ans->answerformat), $ans->fraction));
00185     }
00186 
00187     public function get_correct_response() {
00188         foreach ($this->order as $key => $answerid) {
00189             if (question_state::graded_state_for_fraction(
00190                     $this->answers[$answerid]->fraction)->is_correct()) {
00191                 return array('answer' => $key);
00192             }
00193         }
00194         return array();
00195     }
00196 
00197     public function is_same_response(array $prevresponse, array $newresponse) {
00198         return question_utils::arrays_same_at_key($prevresponse, $newresponse, 'answer');
00199     }
00200 
00201     public function is_complete_response(array $response) {
00202         return array_key_exists('answer', $response);
00203     }
00204 
00205     public function is_gradable_response(array $response) {
00206         return $this->is_complete_response($response);
00207     }
00208 
00209     public function grade_response(array $response) {
00210         if (array_key_exists('answer', $response) &&
00211                 array_key_exists($response['answer'], $this->order)) {
00212             $fraction = $this->answers[$this->order[$response['answer']]]->fraction;
00213         } else {
00214             $fraction = 0;
00215         }
00216         return array($fraction, question_state::graded_state_for_fraction($fraction));
00217     }
00218 
00219     public function get_validation_error(array $response) {
00220         if ($this->is_gradable_response($response)) {
00221             return '';
00222         }
00223         return get_string('pleaseselectananswer', 'qtype_multichoice');
00224     }
00225 
00226     public function get_response(question_attempt $qa) {
00227         return $qa->get_last_qt_var('answer', -1);
00228     }
00229 
00230     public function is_choice_selected($response, $value) {
00231         return (string) $response === (string) $value;
00232     }
00233 }
00234 
00235 
00242 class qtype_multichoice_multi_question extends qtype_multichoice_base {
00243     public function get_renderer(moodle_page $page) {
00244         return $page->get_renderer('qtype_multichoice', 'multi');
00245     }
00246 
00247     public function get_min_fraction() {
00248         return 0;
00249     }
00250 
00251     public function clear_wrong_from_response(array $response) {
00252         foreach ($this->order as $key => $ans) {
00253             if (array_key_exists($this->field($key), $response) &&
00254                     question_state::graded_state_for_fraction(
00255                     $this->answers[$ans]->fraction)->is_incorrect()) {
00256                 $response[$this->field($key)] = 0;
00257             }
00258         }
00259         return $response;
00260     }
00261 
00262     public function get_num_parts_right(array $response) {
00263         $numright = 0;
00264         foreach ($this->order as $key => $ans) {
00265             $fieldname = $this->field($key);
00266             if (!array_key_exists($fieldname, $response) || !$response[$fieldname]) {
00267                 continue;
00268             }
00269 
00270             if (!question_state::graded_state_for_fraction(
00271                     $this->answers[$ans]->fraction)->is_incorrect()) {
00272                 $numright += 1;
00273             }
00274         }
00275         return array($numright, count($this->order));
00276     }
00277 
00282     protected function field($key) {
00283         return 'choice' . $key;
00284     }
00285 
00286     public function get_expected_data() {
00287         $expected = array();
00288         foreach ($this->order as $key => $notused) {
00289             $expected[$this->field($key)] = PARAM_BOOL;
00290         }
00291         return $expected;
00292     }
00293 
00294     public function summarise_response(array $response) {
00295         $selectedchoices = array();
00296         foreach ($this->order as $key => $ans) {
00297             $fieldname = $this->field($key);
00298             if (array_key_exists($fieldname, $response) && $response[$fieldname]) {
00299                 $selectedchoices[] = $this->html_to_text($this->answers[$ans]->answer,
00300                         $this->answers[$ans]->answerformat);
00301             }
00302         }
00303         if (empty($selectedchoices)) {
00304             return null;
00305         }
00306         return implode('; ', $selectedchoices);
00307     }
00308 
00309     public function classify_response(array $response) {
00310         $selectedchoices = array();
00311         foreach ($this->order as $key => $ansid) {
00312             $fieldname = $this->field($key);
00313             if (array_key_exists($fieldname, $response) && $response[$fieldname]) {
00314                 $selectedchoices[$ansid] = 1;
00315             }
00316         }
00317         $choices = array();
00318         foreach ($this->answers as $ansid => $ans) {
00319             if (isset($selectedchoices[$ansid])) {
00320                 $choices[$ansid] = new question_classified_response($ansid,
00321                         $this->html_to_text($ans->answer, $ans->answerformat), $ans->fraction);
00322             }
00323         }
00324         return $choices;
00325     }
00326 
00327     public function get_correct_response() {
00328         $response = array();
00329         foreach ($this->order as $key => $ans) {
00330             if (!question_state::graded_state_for_fraction(
00331                     $this->answers[$ans]->fraction)->is_incorrect()) {
00332                 $response[$this->field($key)] = 1;
00333             }
00334         }
00335         return $response;
00336     }
00337 
00338     public function is_same_response(array $prevresponse, array $newresponse) {
00339         foreach ($this->order as $key => $notused) {
00340             $fieldname = $this->field($key);
00341             if (!question_utils::arrays_same_at_key($prevresponse, $newresponse, $fieldname)) {
00342                 return false;
00343             }
00344         }
00345         return true;
00346     }
00347 
00348     public function is_complete_response(array $response) {
00349         foreach ($this->order as $key => $notused) {
00350             if (!empty($response[$this->field($key)])) {
00351                 return true;
00352             }
00353         }
00354         return false;
00355     }
00356 
00357     public function is_gradable_response(array $response) {
00358         return $this->is_complete_response($response);
00359     }
00360 
00366     public function get_num_selected_choices(array $response) {
00367         $numselected = 0;
00368         foreach ($response as $key => $value) {
00369             if (!empty($value)) {
00370                 $numselected += 1;
00371             }
00372         }
00373         return $numselected;
00374     }
00375 
00379     public function get_num_correct_choices() {
00380         $numcorrect = 0;
00381         foreach ($this->answers as $ans) {
00382             if (!question_state::graded_state_for_fraction($ans->fraction)->is_incorrect()) {
00383                 $numcorrect += 1;
00384             }
00385         }
00386         return $numcorrect;
00387     }
00388 
00389     public function grade_response(array $response) {
00390         $fraction = 0;
00391         foreach ($this->order as $key => $ansid) {
00392             if (!empty($response[$this->field($key)])) {
00393                 $fraction += $this->answers[$ansid]->fraction;
00394             }
00395         }
00396         $fraction = min(max(0, $fraction), 1.0);
00397         return array($fraction, question_state::graded_state_for_fraction($fraction));
00398     }
00399 
00400     public function get_validation_error(array $response) {
00401         if ($this->is_gradable_response($response)) {
00402             return '';
00403         }
00404         return get_string('pleaseselectatleastoneanswer', 'qtype_multichoice');
00405     }
00406 
00412     protected function disable_hint_settings_when_too_many_selected(
00413             question_hint_with_parts $hint) {
00414         $hint->clearwrong = false;
00415     }
00416 
00417     public function get_hint($hintnumber, question_attempt $qa) {
00418         $hint = parent::get_hint($hintnumber, $qa);
00419         if (is_null($hint)) {
00420             return $hint;
00421         }
00422 
00423         if ($this->get_num_selected_choices($qa->get_last_qt_data()) >
00424                 $this->get_num_correct_choices()) {
00425             $hint = clone($hint);
00426             $this->disable_hint_settings_when_too_many_selected($hint);
00427         }
00428         return $hint;
00429     }
00430 
00431     public function get_response(question_attempt $qa) {
00432         return $qa->get_last_qt_data();
00433     }
00434 
00435     public function is_choice_selected($response, $value) {
00436         return !empty($response['choice' . $value]);
00437     }
00438 }
 All Data Structures Namespaces Files Functions Variables Enumerations