Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
   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   * Upgrade library code for the multichoice question type.
  19   *
  20   * @package    qtype
  21   * @subpackage multichoice
  22   * @copyright  2010 The Open University
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  
  30  /**
  31   * Class for converting attempt data for multichoice questions when upgrading
  32   * attempts to the new question engine.
  33   *
  34   * This class is used by the code in question/engine/upgrade/upgradelib.php.
  35   *
  36   * @copyright  2010 The Open University
  37   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  38   */
  39  class qtype_multichoice_qe2_attempt_updater extends question_qtype_attempt_updater {
  40      protected $order;
  41  
  42      public function is_blank_answer($state) {
  43          // Blank multichoice answers are not empty strings, they rather end in a colon.
  44          return empty($state->answer) || substr($state->answer, -1) == ':';
  45      }
  46  
  47      public function right_answer() {
  48          if ($this->question->options->single) {
  49              foreach ($this->question->options->answers as $ans) {
  50                  if ($ans->fraction > 0.999) {
  51                      return $this->to_text($ans->answer);
  52                  }
  53              }
  54  
  55          } else {
  56              $rightbits = array();
  57              foreach ($this->question->options->answers as $ans) {
  58                  if ($ans->fraction >= 0.000001) {
  59                      $rightbits[] = $this->to_text($ans->answer);
  60                  }
  61              }
  62              return implode('; ', $rightbits);
  63          }
  64      }
  65  
  66      protected function explode_answer($answer) {
  67          if (strpos($answer, ':') !== false) {
  68              list($order, $responses) = explode(':', $answer);
  69              return $responses;
  70          } else {
  71              // Sometimes, a bug means that a state is missing the <order>: bit,
  72              // We need to deal with that.
  73              $this->logger->log_assumption("Dealing with missing order information
  74                      in attempt at multiple choice question {$this->question->id}");
  75              return $answer;
  76          }
  77      }
  78  
  79      public function response_summary($state) {
  80          $responses = $this->explode_answer($state->answer);
  81          if ($this->question->options->single) {
  82              if (is_numeric($responses)) {
  83                  if (array_key_exists($responses, $this->question->options->answers)) {
  84                      return $this->to_text($this->question->options->answers[$responses]->answer);
  85                  } else {
  86                      $this->logger->log_assumption("Dealing with a place where the
  87                              student selected a choice that was later deleted for
  88                              multiple choice question {$this->question->id}");
  89                      return '[CHOICE THAT WAS LATER DELETED]';
  90                  }
  91              } else {
  92                  return null;
  93              }
  94  
  95          } else {
  96              if (!empty($responses)) {
  97                  $responses = explode(',', $responses);
  98                  $bits = array();
  99                  foreach ($responses as $response) {
 100                      if (array_key_exists($response, $this->question->options->answers)) {
 101                          $bits[] = $this->to_text(
 102                                  $this->question->options->answers[$response]->answer);
 103                      } else {
 104                          $this->logger->log_assumption("Dealing with a place where the
 105                                  student selected a choice that was later deleted for
 106                                  multiple choice question {$this->question->id}");
 107                          $bits[] = '[CHOICE THAT WAS LATER DELETED]';
 108                      }
 109                  }
 110                  return implode('; ', $bits);
 111              } else {
 112                  return null;
 113              }
 114          }
 115      }
 116  
 117      public function was_answered($state) {
 118          $responses = $this->explode_answer($state->answer);
 119          if ($this->question->options->single) {
 120              return is_numeric($responses);
 121          } else {
 122              return !empty($responses);
 123          }
 124      }
 125  
 126      public function set_first_step_data_elements($state, &$data) {
 127          if (!$state->answer) {
 128              return;
 129          }
 130          list($order, $responses) = explode(':', $state->answer);
 131          $data['_order'] = $order;
 132          $this->order = explode(',', $order);
 133      }
 134  
 135      public function supply_missing_first_step_data(&$data) {
 136          $data['_order'] = implode(',', array_keys($this->question->options->answers));
 137      }
 138  
 139      public function set_data_elements_for_step($state, &$data) {
 140          $responses = $this->explode_answer($state->answer);
 141          if ($this->question->options->single) {
 142              if (is_numeric($responses)) {
 143                  $flippedorder = array_combine(array_values($this->order), array_keys($this->order));
 144                  if (array_key_exists($responses, $flippedorder)) {
 145                      $data['answer'] = $flippedorder[$responses];
 146                  } else {
 147                      $data['answer'] = '-1';
 148                  }
 149              }
 150  
 151          } else {
 152              $responses = explode(',', $responses);
 153              foreach ($this->order as $key => $ansid) {
 154                  if (in_array($ansid, $responses)) {
 155                      $data['choice' . $key] = 1;
 156                  } else {
 157                      $data['choice' . $key] = 0;
 158                  }
 159              }
 160          }
 161      }
 162  }