40
40
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
42
42
abstract class question_behaviour {
44
* Certain behaviours are definitive of a way that questions can
45
* behave when attempted. For example deferredfeedback model, interactive
46
* model, etc. These are the options that should be listed in the
47
* user-interface. These models should define the class constant
48
* IS_ARCHETYPAL as true. Other models are more implementation details, for
49
* example the informationitem model, or a special subclass like
50
* interactive_adapted_for_my_qtype. These models should IS_ARCHETYPAL as
54
const IS_ARCHETYPAL = false;
56
44
/** @var question_attempt the question attempt we are managing. */
58
47
/** @var question_definition shortcut to $qa->get_question(). */
59
48
protected $question;
86
75
* @param question_definition $question the question.
88
public function is_compatible_question(question_definition $question) {
89
$requiredclass = $this->required_question_definition_type();
90
return $this->question instanceof $requiredclass;
94
* Most behaviours can only work with {@link question_definition}s
95
* of a particular subtype, or that implement a particular interface.
96
* This method lets the behaviour document that. The type of
97
* question passed to the constructor is then checked against this type.
99
* @deprecated since 2.2. Please use/override {@link is_compatible_question()} instead.
101
* @return string class/interface name.
103
protected function required_question_definition_type() {
104
return 'question_definition';
77
public abstract function is_compatible_question(question_definition $question);
108
80
* @return string the name of this behaviour. For example the name of
116
* 'Override' this method if there are some display options that do not make
117
* sense 'during the attempt'.
118
* @return array of {@link question_display_options} field names, that are
119
* not relevant to this behaviour before a 'finish' action.
121
public static function get_unused_display_options() {
126
88
* Cause the question to be renderered. This gets the appropriate behaviour
127
89
* renderer using {@link get_renderer()}, and adjusts the display
128
90
* options using {@link adjust_display_options()} and then calls
199
161
* What is the minimum fraction that can be scored for this question.
200
162
* Normally this will be based on $this->question->get_min_fraction(),
201
* but may be modified in some way by the model.
163
* but may be modified in some way by the behaviour.
203
165
* @return number the minimum fraction when this question is attempted under
206
168
public function get_min_fraction() {
211
* Adjust a random guess score for a question using this model. You have to
212
* do this without knowing details of the specific question, or which usage
214
* @param number $fraction the random guess score from the question type.
215
* @return number the adjusted fraction.
173
* Return the maximum possible fraction that can be scored for this question.
174
* Normally this will be based on $this->question->get_max_fraction(),
175
* but may be modified in some way by the behaviour.
177
* @return number the maximum fraction when this question is attempted under
217
public static function adjust_random_guess_score($fraction) {
180
public function get_max_fraction() {
181
return $this->question->get_max_fraction();
321
* @return array subpartid => object with fields
322
* ->responseclassid matches one of the values returned from
323
* quetion_type::get_possible_responses.
324
* ->response the actual response the student gave to this part, as a string.
325
* ->fraction the credit awarded for this subpart, may be null.
326
* returns an empty array if no analysis is possible.
284
* @return question_possible_response[] where keys are subpartid or an empty array if no classification is possible.
328
286
public function classify_response() {
329
287
return $this->question->classify_response($this->qa->get_last_qt_data());
486
444
$pendingstep->get_behaviour_var('maxmark');
487
445
if ($pendingstep->get_behaviour_var('mark') === '') {
488
446
$fraction = null;
489
} else if ($fraction > 1 || $fraction < $this->qa->get_min_fraction()) {
447
} else if ($fraction > $this->qa->get_max_fraction() || $fraction < $this->qa->get_min_fraction()) {
490
448
throw new coding_exception('Score out of range when processing ' .
491
449
'a manual grading action.', 'Question ' . $this->question->id .
492
450
', slot ' . $this->qa->get_slot() . ', fraction ' . $fraction);
503
* Validate that the manual grade submitted for a particular question is in range.
504
* @param int $qubaid the question_usage id.
505
* @param int $slot the slot number within the usage.
506
* @return bool whether the submitted data is in range.
508
public static function is_manual_grade_in_range($qubaid, $slot) {
509
$prefix = 'q' . $qubaid . ':' . $slot . '_';
510
$mark = question_utils::optional_param_mark($prefix . '-mark');
511
$maxmark = optional_param($prefix . '-maxmark', null, PARAM_FLOAT);
512
$minfraction = optional_param($prefix . ':minfraction', null, PARAM_FLOAT);
513
return is_null($mark) || ($mark >= $minfraction * $maxmark && $mark <= $maxmark);
517
461
* @param $comment the comment text to format. If omitted,
518
462
* $this->qa->get_manual_comment() is used.
519
463
* @param $commentformat the format of the comment, one of the FORMAT_... constants.
699
643
/** @var array list of all the certainty levels. */
700
644
public static $certainties = array(self::LOW, self::MED, self::HIGH);
702
/**#@+ @var array coefficients used to adjust the fraction based on certainty.. */
703
protected static $factor = array(
704
self::LOW => 0.333333333333333,
705
self::MED => 1.333333333333333,
646
/**#@+ @var array coefficients used to adjust the fraction based on certainty. */
647
protected static $rightscore = array(
708
protected static $offset = array(
710
self::MED => -0.666666666666667,
652
protected static $wrongscore = array(
659
/**#@+ @var array upper and lower limits of the optimal window. */
660
protected static $lowlimit = array(
662
self::MED => 0.666666666666667,
665
protected static $highlimit = array(
666
self::LOW => 0.666666666666667,
724
* Given a fraction, and a certainly, compute the adjusted fraction.
681
* Given a fraction, and a certainty, compute the adjusted fraction.
725
682
* @param number $fraction the raw fraction for this question.
726
* @param int $certainty one of the certainly level constants.
727
* @return number the adjusted fraction taking the certainly into account.
683
* @param int $certainty one of the certainty level constants.
684
* @return number the adjusted fraction taking the certainty into account.
729
686
public static function adjust_fraction($fraction, $certainty) {
730
return self::$offset[$certainty] + self::$factor[$certainty] * $fraction;
687
if ($certainty == -1) {
688
// Certainty -1 has never been used in standard Moodle, but is
689
// used in Tony-Gardiner Medwin's patches to mean 'No idea' which
690
// we intend to implement: MDL-42077. In the mean time, avoid
691
// errors for people who have used TGM's patches.
694
if ($fraction <= 0.00000005) {
695
return self::$wrongscore[$certainty];
697
return self::$rightscore[$certainty] * $fraction;
734
702
* @param int $certainty one of the LOW/MED/HIGH constants.
735
* @return string a textual desciption of this certainly.
703
* @return string a textual description of this certainty.
737
705
public static function get_string($certainty) {
738
706
return get_string('certainty' . $certainty, 'qbehaviour_deferredcbm');
710
* @param int $certainty one of the LOW/MED/HIGH constants.
711
* @return string a short textual description of this certainty.
713
public static function get_short_string($certainty) {
714
return get_string('certaintyshort' . $certainty, 'qbehaviour_deferredcbm');
718
* Add information about certainty to a response summary.
719
* @param string $summary the response summary.
720
* @param int $certainty the level of certainty to add.
721
* @return string the summary with information about the certainty added.
741
723
public static function summary_with_certainty($summary, $certainty) {
742
724
if (is_null($certainty)) {
745
return $summary . ' [' . self::get_string($certainty) . ']';
727
return $summary . ' [' . self::get_short_string($certainty) . ']';
731
* @param int $certainty one of the LOW/MED/HIGH constants.
732
* @return float the lower limit of the optimal probability range for this certainty.
734
public static function optimal_probablility_low($certainty) {
735
return self::$lowlimit[$certainty];
739
* @param int $certainty one of the LOW/MED/HIGH constants.
740
* @return float the upper limit of the optimal probability range for this certainty.
742
public static function optimal_probablility_high($certainty) {
743
return self::$highlimit[$certainty];