[ Index ]

PHP Cross Reference of moodle-2.8

title

Body

[close]

/question/type/match/ -> question.php (source)

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Matching question definition class.
  19   *
  20   * @package   qtype_match
  21   * @copyright 2009 The Open University
  22   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  
  29  /**
  30   * Represents a matching question.
  31   *
  32   * @copyright 2009 The Open University
  33   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  34   */
  35  class qtype_match_question extends question_graded_automatically_with_countback {
  36      /** @var boolean Whether the question stems should be shuffled. */
  37      public $shufflestems;
  38  
  39      public $correctfeedback;
  40      public $correctfeedbackformat;
  41      public $partiallycorrectfeedback;
  42      public $partiallycorrectfeedbackformat;
  43      public $incorrectfeedback;
  44      public $incorrectfeedbackformat;
  45  
  46      /** @var array of question stems. */
  47      public $stems;
  48      /** @var array of choices that can be matched to each stem. */
  49      public $choices;
  50      /** @var array index of the right choice for each stem. */
  51      public $right;
  52  
  53      /** @var array shuffled stem indexes. */
  54      protected $stemorder;
  55      /** @var array shuffled choice indexes. */
  56      protected $choiceorder;
  57  
  58      public function start_attempt(question_attempt_step $step, $variant) {
  59          $this->stemorder = array_keys($this->stems);
  60          if ($this->shufflestems) {
  61              shuffle($this->stemorder);
  62          }
  63          $step->set_qt_var('_stemorder', implode(',', $this->stemorder));
  64  
  65          $choiceorder = array_keys($this->choices);
  66          shuffle($choiceorder);
  67          $step->set_qt_var('_choiceorder', implode(',', $choiceorder));
  68          $this->set_choiceorder($choiceorder);
  69      }
  70  
  71      public function apply_attempt_state(question_attempt_step $step) {
  72          $this->stemorder = explode(',', $step->get_qt_var('_stemorder'));
  73          $this->set_choiceorder(explode(',', $step->get_qt_var('_choiceorder')));
  74      }
  75  
  76      /**
  77       * Helper method used by both {@link start_attempt()} and
  78       * {@link apply_attempt_state()}.
  79       * @param array $choiceorder the choices, in order.
  80       */
  81      protected function set_choiceorder($choiceorder) {
  82          $this->choiceorder = array();
  83          foreach ($choiceorder as $key => $value) {
  84              $this->choiceorder[$key + 1] = $value;
  85          }
  86      }
  87  
  88      public function get_question_summary() {
  89          $question = $this->html_to_text($this->questiontext, $this->questiontextformat);
  90          $stems = array();
  91          foreach ($this->stemorder as $stemid) {
  92              $stems[] = $this->html_to_text($this->stems[$stemid], $this->stemformat[$stemid]);
  93          }
  94          $choices = array();
  95          foreach ($this->choiceorder as $choiceid) {
  96              $choices[] = $this->choices[$choiceid];
  97          }
  98          return $question . ' {' . implode('; ', $stems) . '} -> {' .
  99                  implode('; ', $choices) . '}';
 100      }
 101  
 102      public function summarise_response(array $response) {
 103          $matches = array();
 104          foreach ($this->stemorder as $key => $stemid) {
 105              if (array_key_exists($this->field($key), $response) && $response[$this->field($key)]) {
 106                  $matches[] = $this->html_to_text($this->stems[$stemid],
 107                          $this->stemformat[$stemid]) . ' -> ' .
 108                          $this->choices[$this->choiceorder[$response[$this->field($key)]]];
 109              }
 110          }
 111          if (empty($matches)) {
 112              return null;
 113          }
 114          return implode('; ', $matches);
 115      }
 116  
 117      public function classify_response(array $response) {
 118          $selectedchoices = array();
 119          foreach ($this->stemorder as $key => $stemid) {
 120              if (array_key_exists($this->field($key), $response) && $response[$this->field($key)]) {
 121                  $selectedchoices[$stemid] = $this->choiceorder[$response[$this->field($key)]];
 122              } else {
 123                  $selectedchoices[$stemid] = 0;
 124              }
 125          }
 126  
 127          $parts = array();
 128          foreach ($this->stems as $stemid => $stem) {
 129              if (empty($selectedchoices[$stemid])) {
 130                  $parts[$stemid] = question_classified_response::no_response();
 131                  continue;
 132              }
 133              $choice = $this->choices[$selectedchoices[$stemid]];
 134              $parts[$stemid] = new question_classified_response(
 135                      $selectedchoices[$stemid], $choice,
 136                      ($selectedchoices[$stemid] == $this->right[$stemid]) / count($this->stems));
 137          }
 138          return $parts;
 139      }
 140  
 141      public function clear_wrong_from_response(array $response) {
 142          foreach ($this->stemorder as $key => $stemid) {
 143              if (!array_key_exists($this->field($key), $response) ||
 144                      $response[$this->field($key)] != $this->get_right_choice_for($stemid)) {
 145                  $response[$this->field($key)] = 0;
 146              }
 147          }
 148          return $response;
 149      }
 150  
 151      public function get_num_parts_right(array $response) {
 152          $numright = 0;
 153          foreach ($this->stemorder as $key => $stemid) {
 154              $fieldname = $this->field($key);
 155              if (!array_key_exists($fieldname, $response)) {
 156                  continue;
 157              }
 158  
 159              $choice = $response[$fieldname];
 160              if ($choice && $this->choiceorder[$choice] == $this->right[$stemid]) {
 161                  $numright += 1;
 162              }
 163          }
 164          return array($numright, count($this->stemorder));
 165      }
 166  
 167      /**
 168       * @param int $key stem number
 169       * @return string the question-type variable name.
 170       */
 171      protected function field($key) {
 172          return 'sub' . $key;
 173      }
 174  
 175      public function get_expected_data() {
 176          $vars = array();
 177          foreach ($this->stemorder as $key => $notused) {
 178              $vars[$this->field($key)] = PARAM_INT;
 179          }
 180          return $vars;
 181      }
 182  
 183      public function get_correct_response() {
 184          $response = array();
 185          foreach ($this->stemorder as $key => $stemid) {
 186              $response[$this->field($key)] = $this->get_right_choice_for($stemid);
 187          }
 188          return $response;
 189      }
 190  
 191      public function prepare_simulated_post_data($simulatedresponse) {
 192          $postdata = array();
 193          $stemtostemids = array_flip(clean_param_array($this->stems, PARAM_NOTAGS));
 194          $choicetochoiceno = array_flip($this->choices);
 195          $choicenotochoiceselectvalue = array_flip($this->choiceorder);
 196          foreach ($simulatedresponse as $stem => $choice) {
 197              $choice = clean_param($choice, PARAM_NOTAGS);
 198              $stemid = $stemtostemids[$stem];
 199              $shuffledstemno = array_search($stemid, $this->stemorder);
 200              if (empty($choice)) {
 201                  $choiceselectvalue = 0;
 202              } else if ($choicetochoiceno[$choice]) {
 203                  $choiceselectvalue = $choicenotochoiceselectvalue[$choicetochoiceno[$choice]];
 204              } else {
 205                  throw new coding_exception("Unknown choice {$choice} in matching question - {$this->name}.");
 206              }
 207              $postdata[$this->field($shuffledstemno)] = $choiceselectvalue;
 208          }
 209          return $postdata;
 210      }
 211  
 212      public function get_student_response_values_for_simulation($postdata) {
 213          $simulatedresponse = array();
 214          foreach ($this->stemorder as $shuffledstemno => $stemid) {
 215              if (!empty($postdata[$this->field($shuffledstemno)])) {
 216                  $choiceselectvalue = $postdata[$this->field($shuffledstemno)];
 217                  $choiceno = $this->choiceorder[$choiceselectvalue];
 218                  $choice = clean_param($this->choices[$choiceno], PARAM_NOTAGS);
 219                  $stem = clean_param($this->stems[$stemid], PARAM_NOTAGS);
 220                  $simulatedresponse[$stem] = $choice;
 221              }
 222          }
 223          ksort($simulatedresponse);
 224          return $simulatedresponse;
 225      }
 226  
 227      public function get_right_choice_for($stemid) {
 228          foreach ($this->choiceorder as $choicekey => $choiceid) {
 229              if ($this->right[$stemid] == $choiceid) {
 230                  return $choicekey;
 231              }
 232          }
 233      }
 234  
 235      public function is_complete_response(array $response) {
 236          $complete = true;
 237          foreach ($this->stemorder as $key => $stemid) {
 238              $complete = $complete && !empty($response[$this->field($key)]);
 239          }
 240          return $complete;
 241      }
 242  
 243      public function is_gradable_response(array $response) {
 244          foreach ($this->stemorder as $key => $stemid) {
 245              if (!empty($response[$this->field($key)])) {
 246                  return true;
 247              }
 248          }
 249          return false;
 250      }
 251  
 252      public function get_validation_error(array $response) {
 253          if ($this->is_complete_response($response)) {
 254              return '';
 255          }
 256          return get_string('pleaseananswerallparts', 'qtype_match');
 257      }
 258  
 259      public function is_same_response(array $prevresponse, array $newresponse) {
 260          foreach ($this->stemorder as $key => $notused) {
 261              $fieldname = $this->field($key);
 262              if (!question_utils::arrays_same_at_key_integer(
 263                      $prevresponse, $newresponse, $fieldname)) {
 264                  return false;
 265              }
 266          }
 267          return true;
 268      }
 269  
 270      public function grade_response(array $response) {
 271          list($right, $total) = $this->get_num_parts_right($response);
 272          $fraction = $right / $total;
 273          return array($fraction, question_state::graded_state_for_fraction($fraction));
 274      }
 275  
 276      public function compute_final_grade($responses, $totaltries) {
 277          $totalstemscore = 0;
 278          foreach ($this->stemorder as $key => $stemid) {
 279              $fieldname = $this->field($key);
 280  
 281              $lastwrongindex = -1;
 282              $finallyright = false;
 283              foreach ($responses as $i => $response) {
 284                  if (!array_key_exists($fieldname, $response) || !$response[$fieldname] ||
 285                          $this->choiceorder[$response[$fieldname]] != $this->right[$stemid]) {
 286                      $lastwrongindex = $i;
 287                      $finallyright = false;
 288                  } else {
 289                      $finallyright = true;
 290                  }
 291              }
 292  
 293              if ($finallyright) {
 294                  $totalstemscore += max(0, 1 - ($lastwrongindex + 1) * $this->penalty);
 295              }
 296          }
 297  
 298          return $totalstemscore / count($this->stemorder);
 299      }
 300  
 301      public function get_stem_order() {
 302          return $this->stemorder;
 303      }
 304  
 305      public function get_choice_order() {
 306          return $this->choiceorder;
 307      }
 308  
 309      public function check_file_access($qa, $options, $component, $filearea, $args, $forcedownload) {
 310          if ($component == 'qtype_match' && $filearea == 'subquestion') {
 311              $subqid = reset($args); // Itemid is sub question id.
 312              return array_key_exists($subqid, $this->stems);
 313  
 314          } else if ($component == 'question' && in_array($filearea,
 315                  array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback'))) {
 316              return $this->check_combined_feedback_file_access($qa, $options, $filearea);
 317  
 318          } else if ($component == 'question' && $filearea == 'hint') {
 319              return $this->check_hint_file_access($qa, $options, $args);
 320  
 321          } else {
 322              return parent::check_file_access($qa, $options, $component, $filearea,
 323                      $args, $forcedownload);
 324          }
 325      }
 326  }


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1