Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 * This defines the states a question can be in.
 *
 * @package    moodlecore
 * @subpackage questionengine
 * @copyright  2010 The Open University
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */


defined('MOODLE_INTERNAL') || die();


/**
 * An enumeration representing the states a question can be in after a
 * {@link question_attempt_step}.
 *
 * There are also some useful methods for testing and manipulating states.
 *
 * @copyright  2009 The Open University
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
abstract class question_state {
    /**#@+
     * Specific question_state instances.
     */
    public static $notstarted;
    public static $unprocessed;
    public static $todo;
    public static $invalid;
    public static $complete;
    public static $needsgrading;
    public static $finished;
    public static $gaveup;
    public static $gradedwrong;
    public static $gradedpartial;
    public static $gradedright;
    public static $manfinished;
    public static $mangaveup;
    public static $mangrwrong;
    public static $mangrpartial;
    public static $mangrright;
    /**#@+-*/

    protected function __construct() {
    }

    public static function init() {
        $us = new ReflectionClass('question_state');
        foreach ($us->getStaticProperties() as $name => $notused) {
            $class = 'question_state_' . $name;
            $states[$name] = new $class();
            self::$$name = $states[$name];
        }
    }

    /**
     * Get all the states in an array.
     *
     * @return question_state[] of question_state objects.
     */
    public static function get_all() {
        $states = array();
        $us = new ReflectionClass('question_state');
        foreach ($us->getStaticProperties() as $name => $notused) {
            $states[] = self::$$name;
        }
        return $states;
    }

    /**
     * Get all the states in an array.
     * @param string $summarystate one of the four summary states
     * inprogress, needsgrading, manuallygraded or autograded.
     * @return array of the corresponding states.
     */
    public static function get_all_for_summary_state($summarystate) {
        $states = array();
        foreach (self::get_all() as $state) {
            if ($state->get_summary_state() == $summarystate) {
                $states[] = $state;
            }
        }
        if (empty($states)) {
            throw new coding_exception('unknown summary state ' . $summarystate);
        }
        return $states;
    }

    /**
     * @return string convert this state to a string.
     */
    public function __toString() {
        return substr(get_class($this), 15);
    }

    /**
> * Get the instance of this class for a given state name. * @param string $name a state name. > *
< * @return question_state the state with that name.
> * @return question_state|null the state with that name. (Null only in an exceptional case.)
*/
< public static function get($name) {
> public static function get(string $name): ?question_state { > // In the past, there was a bug where null states got stored > // in the database as an empty string, which was wrong because > // the state column should be NOT NULL. > // That is no longer possible, but we need to avoid exceptions > // for people with old bad data in their database. > if ($name === '') { > debugging('Attempt to create a state from an empty string. ' . > 'This is probably a sign of bad data in your database. See MDL-80127.'); > return null; > } >
return self::$$name; } /** * Is this state one of the ones that mean the question attempt is in progress? * That is, started, but no finished. * @return bool */ public function is_active() { return false; } /** * Is this state one of the ones that mean the question attempt is finished? * That is, no further interaction possible, apart from manual grading. * @return bool */ public function is_finished() { return true; } /** * Is this state one of the ones that mean the question attempt has been graded? * @return bool */ public function is_graded() { return false; } /** * Is this state one of the ones that mean the question attempt has been graded? * @return bool */ public function is_correct() { return false; } /** * Is this state one of the ones that mean the question attempt has been graded? * @return bool */ public function is_partially_correct() { return false; } /** * Is this state one of the ones that mean the question attempt has been graded? * @return bool */ public function is_incorrect() { return false; } /** * Is this state one of the ones that mean the question attempt has been graded? * @return bool */ public function is_gave_up() { return false; } /** * Is this state one of the ones that mean the question attempt has had a manual comment added? * @return bool */ public function is_commented() { return false; } /** * Each state can be categorised into one of four categories: * inprogress, needsgrading, manuallygraded or autograded. * @return string which category this state falls into. */ public function get_summary_state() { if (!$this->is_finished()) { return 'inprogress'; } else if ($this == self::$needsgrading) { return 'needsgrading'; } else if ($this->is_commented()) { return 'manuallygraded'; } else { return 'autograded'; } } /** * Return the appropriate graded state based on a fraction. That is 0 or less * is $graded_incorrect, 1 is $graded_correct, otherwise it is $graded_partcorrect. * Appropriate allowance is made for rounding float values. * * @param number $fraction the grade, on the fraction scale. * @return question_state one of the state constants. */ public static function graded_state_for_fraction($fraction) { if ($fraction < 0.000001) { return self::$gradedwrong; } else if ($fraction > 0.999999) { return self::$gradedright; } else { return self::$gradedpartial; } } /** * Return the appropriate manually graded state based on a fraction. That is 0 or less * is $manually_graded_incorrect, 1 is $manually_graded_correct, otherwise it is * $manually_graded_partcorrect. Appropriate allowance is made for rounding float values. * * @param number $fraction the grade, on the fraction scale. * @return int one of the state constants. */ public static function manually_graded_state_for_fraction($fraction) { if (is_null($fraction)) { return self::$needsgrading; } else if ($fraction < 0.000001) { return self::$mangrwrong; } else if ($fraction > 0.999999) { return self::$mangrright; } else { return self::$mangrpartial; } } /** * Compute an appropriate state to move to after a manual comment has been * added to this state. * @param number $fraction the manual grade (if any) on the fraction scale. * @return int the new state. */ public function corresponding_commented_state($fraction) { throw new coding_exception('Unexpected question state.'); } /** * Return an appropriate CSS class name ''/'correct'/'partiallycorrect'/'incorrect', * for a state. * @return string */ public function get_feedback_class() { return ''; } /** * Return the name of an appropriate string to look up in the question * language pack for a state. This is used, for example, by * {@link question_behaviour::get_state_string()}. However, behaviours * sometimes change this default string for soemthing more specific. * * @param bool $showcorrectness Whether right/partial/wrong states should * be distinguised, or just treated as 'complete'. * @return string the name of a string that can be looked up in the 'question' * lang pack, or used as a CSS class name, etc. */ public abstract function get_state_class($showcorrectness); /** * The result of doing get_string on the result of {@link get_state_class()}. * * @param bool $showcorrectness Whether right/partial/wrong states should * be distinguised. * @return string a string from the lang pack that can be used in the UI. */ public function default_string($showcorrectness) { return get_string($this->get_state_class($showcorrectness), 'question'); } } /**#@+ * Specific question_state subclasses. * * @copyright 2009 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class question_state_notstarted extends question_state { public function is_finished() { return false; } public function get_state_class($showcorrectness) { throw new coding_exception('Unexpected question state.'); } } class question_state_unprocessed extends question_state { public function is_finished() { return false; } public function get_state_class($showcorrectness) { throw new coding_exception('Unexpected question state.'); } } class question_state_todo extends question_state { public function is_active() { return true; } public function is_finished() { return false; } public function get_state_class($showcorrectness) { return 'notyetanswered'; } } class question_state_invalid extends question_state { public function is_active() { return true; } public function is_finished() { return false; } public function get_state_class($showcorrectness) { return 'invalidanswer'; } } class question_state_complete extends question_state { public function is_active() { return true; } public function is_finished() { return false; } public function get_state_class($showcorrectness) { return 'answersaved'; } } class question_state_needsgrading extends question_state { public function get_state_class($showcorrectness) { if ($showcorrectness) { return 'requiresgrading'; } else { return 'complete'; } } public function corresponding_commented_state($fraction) { return self::manually_graded_state_for_fraction($fraction); } } class question_state_finished extends question_state { public function get_state_class($showcorrectness) { return 'complete'; } public function corresponding_commented_state($fraction) { return self::$manfinished; } } class question_state_gaveup extends question_state { public function is_gave_up() { return true; } public function get_feedback_class() { return 'incorrect'; } public function get_state_class($showcorrectness) { return 'notanswered'; } public function corresponding_commented_state($fraction) { if (is_null($fraction)) { return self::$mangaveup; } else { return self::manually_graded_state_for_fraction($fraction); } } } abstract class question_state_graded extends question_state { public function is_graded() { return true; } public function get_state_class($showcorrectness) { if ($showcorrectness) { return $this->get_feedback_class(); } else { return 'complete'; } } public function corresponding_commented_state($fraction) { return self::manually_graded_state_for_fraction($fraction); } } class question_state_gradedwrong extends question_state_graded { public function is_incorrect() { return true; } public function get_feedback_class() { return 'incorrect'; } } class question_state_gradedpartial extends question_state_graded { public function is_graded() { return true; } public function is_partially_correct() { return true; } public function get_feedback_class() { return 'partiallycorrect'; } } class question_state_gradedright extends question_state_graded { public function is_graded() { return true; } public function is_correct() { return true; } public function get_feedback_class() { return 'correct'; } } class question_state_manfinished extends question_state_finished { public function is_commented() { return true; } } class question_state_mangaveup extends question_state_gaveup { public function is_commented() { return true; } } abstract class question_state_manuallygraded extends question_state_graded { public function is_commented() { return true; } } class question_state_mangrwrong extends question_state_manuallygraded { public function is_incorrect() { return false; } public function get_feedback_class() { return 'incorrect'; } } class question_state_mangrpartial extends question_state_manuallygraded { public function is_partially_correct() { return true; } public function get_feedback_class() { return 'partiallycorrect'; } } class question_state_mangrright extends question_state_manuallygraded { public function is_correct() { return true; } public function get_feedback_class() { return 'correct'; } } /**#@-*/ question_state::init();