2 // This file is part of Moodle - http://moodle.org/
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.
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/>.
18 * This defines the states a question can be in.
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();
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
{
41 * Specific question_state instances.
43 public static $notstarted;
44 public static $unprocessed;
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;
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];
74 * Get all the states in an array.
76 * @return question_state[] of question_state objects.
78 public static function get_all() {
80 $us = new ReflectionClass('question_state');
81 foreach ($us->getStaticProperties() as $name => $notused) {
82 $states[] = self
::$
$name;
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) {
95 foreach (self
::get_all() as $state) {
96 if ($state->get_summary_state() == $summarystate) {
100 if (empty($states)) {
101 throw new coding_exception('unknown summary state ' . $summarystate);
107 * @return string convert this state to a string.
109 public function __toString() {
110 return substr(get_class($this), 15);
114 * @param string $name a state name.
115 * @return question_state the state with that name.
117 public static function get($name) {
122 * Is this state one of the ones that mean the question attempt is in progress?
123 * That is, started, but no finished.
126 public function is_active() {
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.
135 public function is_finished() {
140 * Is this state one of the ones that mean the question attempt has been graded?
143 public function is_graded() {
148 * Is this state one of the ones that mean the question attempt has been graded?
151 public function is_correct() {
156 * Is this state one of the ones that mean the question attempt has been graded?
159 public function is_partially_correct() {
164 * Is this state one of the ones that mean the question attempt has been graded?
167 public function is_incorrect() {
172 * Is this state one of the ones that mean the question attempt has been graded?
175 public function is_gave_up() {
180 * Is this state one of the ones that mean the question attempt has had a manual comment added?
183 public function is_commented() {
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.
192 public function get_summary_state() {
193 if (!$this->is_finished()) {
195 } else if ($this == self
::$needsgrading) {
196 return 'needsgrading';
197 } else if ($this->is_commented()) {
198 return 'manuallygraded';
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.
209 * @param number $fraction the grade, on the fraction scale.
210 * @return question_state one of the state constants.
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;
218 return self
::$gradedpartial;
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.
227 * @param number $fraction the grade, on the fraction scale.
228 * @return int one of the state constants.
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;
238 return self
::$mangrpartial;
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.
248 public function corresponding_commented_state($fraction) {
249 throw new coding_exception('Unexpected question state.');
253 * Return an appropriate CSS class name ''/'correct'/'partiallycorrect'/'incorrect',
257 public function get_feedback_class() {
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.
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.
272 public abstract function get_state_class($showcorrectness);
275 * The result of doing get_string on the result of {@link get_state_class()}.
277 * @param bool $showcorrectness Whether right/partial/wrong states should
279 * @return string a string from the lang pack that can be used in the UI.
281 public function default_string($showcorrectness) {
282 return get_string($this->get_state_class($showcorrectness), 'question');
288 * Specific question_state subclasses.
290 * @copyright 2009 The Open University
291 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
293 class question_state_notstarted
extends question_state
{
294 public function is_finished() {
297 public function get_state_class($showcorrectness) {
298 throw new coding_exception('Unexpected question state.');
301 class question_state_unprocessed
extends question_state
{
302 public function is_finished() {
305 public function get_state_class($showcorrectness) {
306 throw new coding_exception('Unexpected question state.');
309 class question_state_todo
extends question_state
{
310 public function is_active() {
313 public function is_finished() {
316 public function get_state_class($showcorrectness) {
317 return 'notyetanswered';
320 class question_state_invalid
extends question_state
{
321 public function is_active() {
324 public function is_finished() {
327 public function get_state_class($showcorrectness) {
328 return 'invalidanswer';
331 class question_state_complete
extends question_state
{
332 public function is_active() {
335 public function is_finished() {
338 public function get_state_class($showcorrectness) {
339 return 'answersaved';
342 class question_state_needsgrading
extends question_state
{
343 public function get_state_class($showcorrectness) {
344 if ($showcorrectness) {
345 return 'requiresgrading';
350 public function corresponding_commented_state($fraction) {
351 return self
::manually_graded_state_for_fraction($fraction);
354 class question_state_finished
extends question_state
{
355 public function get_state_class($showcorrectness) {
358 public function corresponding_commented_state($fraction) {
359 return self
::$manfinished;
362 class question_state_gaveup
extends question_state
{
363 public function is_gave_up() {
366 public function get_feedback_class() {
369 public function get_state_class($showcorrectness) {
370 return 'notanswered';
372 public function corresponding_commented_state($fraction) {
373 if (is_null($fraction)) {
374 return self
::$mangaveup;
376 return self
::manually_graded_state_for_fraction($fraction);
380 abstract class question_state_graded
extends question_state
{
381 public function is_graded() {
384 public function get_state_class($showcorrectness) {
385 if ($showcorrectness) {
386 return $this->get_feedback_class();
391 public function corresponding_commented_state($fraction) {
392 return self
::manually_graded_state_for_fraction($fraction);
395 class question_state_gradedwrong
extends question_state_graded
{
396 public function is_incorrect() {
399 public function get_feedback_class() {
403 class question_state_gradedpartial
extends question_state_graded
{
404 public function is_graded() {
407 public function is_partially_correct() {
410 public function get_feedback_class() {
411 return 'partiallycorrect';
414 class question_state_gradedright
extends question_state_graded
{
415 public function is_graded() {
418 public function is_correct() {
421 public function get_feedback_class() {
425 class question_state_manfinished
extends question_state_finished
{
426 public function is_commented() {
430 class question_state_mangaveup
extends question_state_gaveup
{
431 public function is_commented() {
435 abstract class question_state_manuallygraded
extends question_state_graded
{
436 public function is_commented() {
440 class question_state_mangrwrong
extends question_state_manuallygraded
{
441 public function is_incorrect() {
444 public function get_feedback_class() {
448 class question_state_mangrpartial
extends question_state_manuallygraded
{
449 public function is_partially_correct() {
452 public function get_feedback_class() {
453 return 'partiallycorrect';
456 class question_state_mangrright
extends question_state_manuallygraded
{
457 public function is_correct() {
460 public function get_feedback_class() {
465 question_state
::init();