on-demand release 4.5dev+
[moodle.git] / question / engine / states.php
blob6afc0eef1b24523b81b09faddd1d7e53cc5bc2dc
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.
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/>.
17 /**
18 * This defines the states a question can be in.
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
27 defined('MOODLE_INTERNAL') || die();
30 /**
31 * An enumeration representing the states a question can be in after a
32 * {@link question_attempt_step}.
34 * There are also some useful methods for testing and manipulating states.
36 * @copyright 2009 The Open University
37 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39 abstract class question_state {
40 /**#@+
41 * Specific question_state instances.
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 /**#@+-*/
61 protected function __construct() {
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];
73 /**
74 * Get all the states in an array.
76 * @return question_state[] of question_state objects.
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;
84 return $states;
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.
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;
100 if (empty($states)) {
101 throw new coding_exception('unknown summary state ' . $summarystate);
103 return $states;
107 * @return string convert this state to a string.
109 public function __toString() {
110 return substr(get_class($this), 15);
114 * Get the instance of this class for a given state name.
116 * @param string $name a state name.
117 * @return question_state|null the state with that name. (Null only in an exceptional case.)
119 public static function get(string $name): ?question_state {
120 // In the past, there was a bug where null states got stored
121 // in the database as an empty string, which was wrong because
122 // the state column should be NOT NULL.
123 // That is no longer possible, but we need to avoid exceptions
124 // for people with old bad data in their database.
125 if ($name === '') {
126 debugging('Attempt to create a state from an empty string. ' .
127 'This is probably a sign of bad data in your database. See MDL-80127.');
128 return null;
131 return self::$$name;
135 * Is this state one of the ones that mean the question attempt is in progress?
136 * That is, started, but no finished.
137 * @return bool
139 public function is_active() {
140 return false;
144 * Is this state one of the ones that mean the question attempt is finished?
145 * That is, no further interaction possible, apart from manual grading.
146 * @return bool
148 public function is_finished() {
149 return true;
153 * Is this state one of the ones that mean the question attempt has been graded?
154 * @return bool
156 public function is_graded() {
157 return false;
161 * Is this state one of the ones that mean the question attempt has been graded?
162 * @return bool
164 public function is_correct() {
165 return false;
169 * Is this state one of the ones that mean the question attempt has been graded?
170 * @return bool
172 public function is_partially_correct() {
173 return false;
177 * Is this state one of the ones that mean the question attempt has been graded?
178 * @return bool
180 public function is_incorrect() {
181 return false;
185 * Is this state one of the ones that mean the question attempt has been graded?
186 * @return bool
188 public function is_gave_up() {
189 return false;
193 * Is this state one of the ones that mean the question attempt has had a manual comment added?
194 * @return bool
196 public function is_commented() {
197 return false;
201 * Each state can be categorised into one of four categories:
202 * inprogress, needsgrading, manuallygraded or autograded.
203 * @return string which category this state falls into.
205 public function get_summary_state() {
206 if (!$this->is_finished()) {
207 return 'inprogress';
208 } else if ($this == self::$needsgrading) {
209 return 'needsgrading';
210 } else if ($this->is_commented()) {
211 return 'manuallygraded';
212 } else {
213 return 'autograded';
218 * Return the appropriate graded state based on a fraction. That is 0 or less
219 * is $graded_incorrect, 1 is $graded_correct, otherwise it is $graded_partcorrect.
220 * Appropriate allowance is made for rounding float values.
222 * @param number $fraction the grade, on the fraction scale.
223 * @return question_state one of the state constants.
225 public static function graded_state_for_fraction($fraction) {
226 if ($fraction < 0.000001) {
227 return self::$gradedwrong;
228 } else if ($fraction > 0.999999) {
229 return self::$gradedright;
230 } else {
231 return self::$gradedpartial;
236 * Return the appropriate manually graded state based on a fraction. That is 0 or less
237 * is $manually_graded_incorrect, 1 is $manually_graded_correct, otherwise it is
238 * $manually_graded_partcorrect. Appropriate allowance is made for rounding float values.
240 * @param number $fraction the grade, on the fraction scale.
241 * @return int one of the state constants.
243 public static function manually_graded_state_for_fraction($fraction) {
244 if (is_null($fraction)) {
245 return self::$needsgrading;
246 } else if ($fraction < 0.000001) {
247 return self::$mangrwrong;
248 } else if ($fraction > 0.999999) {
249 return self::$mangrright;
250 } else {
251 return self::$mangrpartial;
256 * Compute an appropriate state to move to after a manual comment has been
257 * added to this state.
258 * @param number $fraction the manual grade (if any) on the fraction scale.
259 * @return int the new state.
261 public function corresponding_commented_state($fraction) {
262 throw new coding_exception('Unexpected question state.');
266 * Return an appropriate CSS class name ''/'correct'/'partiallycorrect'/'incorrect',
267 * for a state.
268 * @return string
270 public function get_feedback_class() {
271 return '';
275 * Return the name of an appropriate string to look up in the question
276 * language pack for a state. This is used, for example, by
277 * {@link question_behaviour::get_state_string()}. However, behaviours
278 * sometimes change this default string for soemthing more specific.
280 * @param bool $showcorrectness Whether right/partial/wrong states should
281 * be distinguised, or just treated as 'complete'.
282 * @return string the name of a string that can be looked up in the 'question'
283 * lang pack, or used as a CSS class name, etc.
285 abstract public function get_state_class($showcorrectness);
288 * The result of doing get_string on the result of {@link get_state_class()}.
290 * @param bool $showcorrectness Whether right/partial/wrong states should
291 * be distinguised.
292 * @return string a string from the lang pack that can be used in the UI.
294 public function default_string($showcorrectness) {
295 return get_string($this->get_state_class($showcorrectness), 'question');
300 /**#@+
301 * Specific question_state subclasses.
303 * @copyright 2009 The Open University
304 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
306 class question_state_notstarted extends question_state {
307 public function is_finished() {
308 return false;
310 public function get_state_class($showcorrectness) {
311 throw new coding_exception('Unexpected question state.');
314 class question_state_unprocessed extends question_state {
315 public function is_finished() {
316 return false;
318 public function get_state_class($showcorrectness) {
319 throw new coding_exception('Unexpected question state.');
322 class question_state_todo extends question_state {
323 public function is_active() {
324 return true;
326 public function is_finished() {
327 return false;
329 public function get_state_class($showcorrectness) {
330 return 'notyetanswered';
333 class question_state_invalid extends question_state {
334 public function is_active() {
335 return true;
337 public function is_finished() {
338 return false;
340 public function get_state_class($showcorrectness) {
341 return 'invalidanswer';
344 class question_state_complete extends question_state {
345 public function is_active() {
346 return true;
348 public function is_finished() {
349 return false;
351 public function get_state_class($showcorrectness) {
352 return 'answersaved';
355 class question_state_needsgrading extends question_state {
356 public function get_state_class($showcorrectness) {
357 if ($showcorrectness) {
358 return 'requiresgrading';
359 } else {
360 return 'complete';
363 public function corresponding_commented_state($fraction) {
364 return self::manually_graded_state_for_fraction($fraction);
367 class question_state_finished extends question_state {
368 public function get_state_class($showcorrectness) {
369 return 'complete';
371 public function corresponding_commented_state($fraction) {
372 return self::$manfinished;
375 class question_state_gaveup extends question_state {
376 public function is_gave_up() {
377 return true;
379 public function get_feedback_class() {
380 return 'incorrect';
382 public function get_state_class($showcorrectness) {
383 return 'notanswered';
385 public function corresponding_commented_state($fraction) {
386 if (is_null($fraction)) {
387 return self::$mangaveup;
388 } else {
389 return self::manually_graded_state_for_fraction($fraction);
393 abstract class question_state_graded extends question_state {
394 public function is_graded() {
395 return true;
397 public function get_state_class($showcorrectness) {
398 if ($showcorrectness) {
399 return $this->get_feedback_class();
400 } else {
401 return 'complete';
404 public function corresponding_commented_state($fraction) {
405 return self::manually_graded_state_for_fraction($fraction);
408 class question_state_gradedwrong extends question_state_graded {
409 public function is_incorrect() {
410 return true;
412 public function get_feedback_class() {
413 return 'incorrect';
416 class question_state_gradedpartial extends question_state_graded {
417 public function is_graded() {
418 return true;
420 public function is_partially_correct() {
421 return true;
423 public function get_feedback_class() {
424 return 'partiallycorrect';
427 class question_state_gradedright extends question_state_graded {
428 public function is_graded() {
429 return true;
431 public function is_correct() {
432 return true;
434 public function get_feedback_class() {
435 return 'correct';
438 class question_state_manfinished extends question_state_finished {
439 public function is_commented() {
440 return true;
443 class question_state_mangaveup extends question_state_gaveup {
444 public function is_commented() {
445 return true;
448 abstract class question_state_manuallygraded extends question_state_graded {
449 public function is_commented() {
450 return true;
453 class question_state_mangrwrong extends question_state_manuallygraded {
454 public function is_incorrect() {
455 return false;
457 public function get_feedback_class() {
458 return 'incorrect';
461 class question_state_mangrpartial extends question_state_manuallygraded {
462 public function is_partially_correct() {
463 return true;
465 public function get_feedback_class() {
466 return 'partiallycorrect';
469 class question_state_mangrright extends question_state_manuallygraded {
470 public function is_correct() {
471 return true;
473 public function get_feedback_class() {
474 return 'correct';
477 /**#@-*/
478 question_state::init();