Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403]

   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   * This defines the states a question can be in.
  19   *
  20   * @package    moodlecore
  21   * @subpackage questionengine
  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   * An enumeration representing the states a question can be in after a
  32   * {@link question_attempt_step}.
  33   *
  34   * There are also some useful methods for testing and manipulating states.
  35   *
  36   * @copyright  2009 The Open University
  37   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  38   */
  39  abstract class question_state {
  40      /**#@+
  41       * Specific question_state instances.
  42       */
  43      public static $notstarted;
  44      public static $unprocessed;
  45      public static $todo;
  46      public static $invalid;
  47      public static $complete;
  48      public static $needsgrading;
  49      public static $finished;
  50      public static $gaveup;
  51      public static $gradedwrong;
  52      public static $gradedpartial;
  53      public static $gradedright;
  54      public static $manfinished;
  55      public static $mangaveup;
  56      public static $mangrwrong;
  57      public static $mangrpartial;
  58      public static $mangrright;
  59      /**#@+-*/
  60  
  61      protected function __construct() {
  62      }
  63  
  64      public static function init() {
  65          $us = new ReflectionClass('question_state');
  66          foreach ($us->getStaticProperties() as $name => $notused) {
  67              $class = 'question_state_' . $name;
  68              $states[$name] = new $class();
  69              self::$$name = $states[$name];
  70          }
  71      }
  72  
  73      /**
  74       * Get all the states in an array.
  75       *
  76       * @return question_state[] of question_state objects.
  77       */
  78      public static function get_all() {
  79          $states = array();
  80          $us = new ReflectionClass('question_state');
  81          foreach ($us->getStaticProperties() as $name => $notused) {
  82              $states[] = self::$$name;
  83          }
  84          return $states;
  85      }
  86  
  87      /**
  88       * Get all the states in an array.
  89       * @param string $summarystate one of the four summary states
  90       * inprogress, needsgrading, manuallygraded or autograded.
  91       * @return array of the corresponding states.
  92       */
  93      public static function get_all_for_summary_state($summarystate) {
  94          $states = array();
  95          foreach (self::get_all() as $state) {
  96              if ($state->get_summary_state() == $summarystate) {
  97                  $states[] = $state;
  98              }
  99          }
 100          if (empty($states)) {
 101              throw new coding_exception('unknown summary state ' . $summarystate);
 102          }
 103          return $states;
 104      }
 105  
 106      /**
 107       * @return string convert this state to a string.
 108       */
 109      public function __toString() {
 110          return substr(get_class($this), 15);
 111      }
 112  
 113      /**
 114       * @param string $name a state name.
 115       * @return question_state the state with that name.
 116       */
 117      public static function get($name) {
 118          return self::$$name;
 119      }
 120  
 121      /**
 122       * Is this state one of the ones that mean the question attempt is in progress?
 123       * That is, started, but no finished.
 124       * @return bool
 125       */
 126      public function is_active() {
 127          return false;
 128      }
 129  
 130      /**
 131       * Is this state one of the ones that mean the question attempt is finished?
 132       * That is, no further interaction possible, apart from manual grading.
 133       * @return bool
 134       */
 135      public function is_finished() {
 136          return true;
 137      }
 138  
 139      /**
 140       * Is this state one of the ones that mean the question attempt has been graded?
 141       * @return bool
 142       */
 143      public function is_graded() {
 144          return false;
 145      }
 146  
 147      /**
 148       * Is this state one of the ones that mean the question attempt has been graded?
 149       * @return bool
 150       */
 151      public function is_correct() {
 152          return false;
 153      }
 154  
 155      /**
 156       * Is this state one of the ones that mean the question attempt has been graded?
 157       * @return bool
 158       */
 159      public function is_partially_correct() {
 160          return false;
 161      }
 162  
 163      /**
 164       * Is this state one of the ones that mean the question attempt has been graded?
 165       * @return bool
 166       */
 167      public function is_incorrect() {
 168          return false;
 169      }
 170  
 171      /**
 172       * Is this state one of the ones that mean the question attempt has been graded?
 173       * @return bool
 174       */
 175      public function is_gave_up() {
 176          return false;
 177      }
 178  
 179      /**
 180       * Is this state one of the ones that mean the question attempt has had a manual comment added?
 181       * @return bool
 182       */
 183      public function is_commented() {
 184          return false;
 185      }
 186  
 187      /**
 188       * Each state can be categorised into one of four categories:
 189       * inprogress, needsgrading, manuallygraded or autograded.
 190       * @return string which category this state falls into.
 191       */
 192      public function get_summary_state() {
 193          if (!$this->is_finished()) {
 194              return 'inprogress';
 195          } else if ($this == self::$needsgrading) {
 196              return 'needsgrading';
 197          } else if ($this->is_commented()) {
 198              return 'manuallygraded';
 199          } else {
 200              return 'autograded';
 201          }
 202      }
 203  
 204      /**
 205       * Return the appropriate graded state based on a fraction. That is 0 or less
 206       * is $graded_incorrect, 1 is $graded_correct, otherwise it is $graded_partcorrect.
 207       * Appropriate allowance is made for rounding float values.
 208       *
 209       * @param number $fraction the grade, on the fraction scale.
 210       * @return question_state one of the state constants.
 211       */
 212      public static function graded_state_for_fraction($fraction) {
 213          if ($fraction < 0.000001) {
 214              return self::$gradedwrong;
 215          } else if ($fraction > 0.999999) {
 216              return self::$gradedright;
 217          } else {
 218              return self::$gradedpartial;
 219          }
 220      }
 221  
 222      /**
 223       * Return the appropriate manually graded state based on a fraction. That is 0 or less
 224       * is $manually_graded_incorrect, 1 is $manually_graded_correct, otherwise it is
 225       * $manually_graded_partcorrect. Appropriate allowance is made for rounding float values.
 226       *
 227       * @param number $fraction the grade, on the fraction scale.
 228       * @return int one of the state constants.
 229       */
 230      public static function manually_graded_state_for_fraction($fraction) {
 231          if (is_null($fraction)) {
 232              return self::$needsgrading;
 233          } else if ($fraction < 0.000001) {
 234              return self::$mangrwrong;
 235          } else if ($fraction > 0.999999) {
 236              return self::$mangrright;
 237          } else {
 238              return self::$mangrpartial;
 239          }
 240      }
 241  
 242      /**
 243       * Compute an appropriate state to move to after a manual comment has been
 244       * added to this state.
 245       * @param number $fraction the manual grade (if any) on the fraction scale.
 246       * @return int the new state.
 247       */
 248      public function corresponding_commented_state($fraction) {
 249          throw new coding_exception('Unexpected question state.');
 250      }
 251  
 252      /**
 253       * Return an appropriate CSS class name ''/'correct'/'partiallycorrect'/'incorrect',
 254       * for a state.
 255       * @return string
 256       */
 257      public function get_feedback_class() {
 258          return '';
 259      }
 260  
 261      /**
 262       * Return the name of an appropriate string to look up in the question
 263       * language pack for a state. This is used, for example, by
 264       * {@link question_behaviour::get_state_string()}. However, behaviours
 265       * sometimes change this default string for soemthing more specific.
 266       *
 267       * @param bool $showcorrectness Whether right/partial/wrong states should
 268       * be distinguised, or just treated as 'complete'.
 269       * @return string the name of a string that can be looked up in the 'question'
 270       *      lang pack, or used as a CSS class name, etc.
 271       */
 272      public abstract function get_state_class($showcorrectness);
 273  
 274      /**
 275       * The result of doing get_string on the result of {@link get_state_class()}.
 276       *
 277       * @param bool $showcorrectness Whether right/partial/wrong states should
 278       * be distinguised.
 279       * @return string a string from the lang pack that can be used in the UI.
 280       */
 281      public function default_string($showcorrectness) {
 282          return get_string($this->get_state_class($showcorrectness), 'question');
 283      }
 284  }
 285  
 286  
 287  /**#@+
 288   * Specific question_state subclasses.
 289   *
 290   * @copyright  2009 The Open University
 291   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 292   */
 293  class question_state_notstarted extends question_state {
 294      public function is_finished() {
 295          return false;
 296      }
 297      public function get_state_class($showcorrectness) {
 298          throw new coding_exception('Unexpected question state.');
 299      }
 300  }
 301  class question_state_unprocessed extends question_state {
 302      public function is_finished() {
 303          return false;
 304      }
 305      public function get_state_class($showcorrectness) {
 306          throw new coding_exception('Unexpected question state.');
 307      }
 308  }
 309  class question_state_todo extends question_state {
 310      public function is_active() {
 311          return true;
 312      }
 313      public function is_finished() {
 314          return false;
 315      }
 316      public function get_state_class($showcorrectness) {
 317          return 'notyetanswered';
 318      }
 319  }
 320  class question_state_invalid extends question_state {
 321      public function is_active() {
 322          return true;
 323      }
 324      public function is_finished() {
 325          return false;
 326      }
 327      public function get_state_class($showcorrectness) {
 328          return 'invalidanswer';
 329      }
 330  }
 331  class question_state_complete extends question_state {
 332      public function is_active() {
 333          return true;
 334      }
 335      public function is_finished() {
 336          return false;
 337      }
 338      public function get_state_class($showcorrectness) {
 339          return 'answersaved';
 340      }
 341  }
 342  class question_state_needsgrading extends question_state {
 343      public function get_state_class($showcorrectness) {
 344          if ($showcorrectness) {
 345              return 'requiresgrading';
 346          } else {
 347              return 'complete';
 348          }
 349      }
 350      public function corresponding_commented_state($fraction) {
 351          return self::manually_graded_state_for_fraction($fraction);
 352      }
 353  }
 354  class question_state_finished extends question_state {
 355      public function get_state_class($showcorrectness) {
 356          return 'complete';
 357      }
 358      public function corresponding_commented_state($fraction) {
 359          return self::$manfinished;
 360      }
 361  }
 362  class question_state_gaveup extends question_state {
 363      public function is_gave_up() {
 364          return true;
 365      }
 366      public function get_feedback_class() {
 367          return 'incorrect';
 368      }
 369      public function get_state_class($showcorrectness) {
 370          return 'notanswered';
 371      }
 372      public function corresponding_commented_state($fraction) {
 373          if (is_null($fraction)) {
 374              return self::$mangaveup;
 375          } else {
 376              return self::manually_graded_state_for_fraction($fraction);
 377          }
 378      }
 379  }
 380  abstract class question_state_graded extends question_state {
 381      public function is_graded() {
 382          return true;
 383      }
 384      public function get_state_class($showcorrectness) {
 385          if ($showcorrectness) {
 386              return $this->get_feedback_class();
 387          } else {
 388              return 'complete';
 389          }
 390      }
 391      public function corresponding_commented_state($fraction) {
 392          return self::manually_graded_state_for_fraction($fraction);
 393      }
 394  }
 395  class question_state_gradedwrong extends question_state_graded {
 396      public function is_incorrect() {
 397          return true;
 398      }
 399      public function get_feedback_class() {
 400          return 'incorrect';
 401      }
 402  }
 403  class question_state_gradedpartial extends question_state_graded {
 404      public function is_graded() {
 405          return true;
 406      }
 407      public function is_partially_correct() {
 408          return true;
 409      }
 410      public function get_feedback_class() {
 411          return 'partiallycorrect';
 412      }
 413  }
 414  class question_state_gradedright extends question_state_graded {
 415      public function is_graded() {
 416          return true;
 417      }
 418      public function is_correct() {
 419          return true;
 420      }
 421      public function get_feedback_class() {
 422          return 'correct';
 423      }
 424  }
 425  class question_state_manfinished extends question_state_finished {
 426      public function is_commented() {
 427          return true;
 428      }
 429  }
 430  class question_state_mangaveup extends question_state_gaveup {
 431      public function is_commented() {
 432          return true;
 433      }
 434  }
 435  abstract class question_state_manuallygraded extends question_state_graded {
 436      public function is_commented() {
 437          return true;
 438      }
 439  }
 440  class question_state_mangrwrong extends question_state_manuallygraded {
 441      public function is_incorrect() {
 442          return false;
 443      }
 444      public function get_feedback_class() {
 445          return 'incorrect';
 446      }
 447  }
 448  class question_state_mangrpartial extends question_state_manuallygraded {
 449      public function is_partially_correct() {
 450          return true;
 451      }
 452      public function get_feedback_class() {
 453          return 'partiallycorrect';
 454      }
 455  }
 456  class question_state_mangrright extends question_state_manuallygraded {
 457      public function is_correct() {
 458          return true;
 459      }
 460      public function get_feedback_class() {
 461          return 'correct';
 462      }
 463  }
 464  /**#@-*/
 465  question_state::init();