MDL-77328 qtype_calculated: Refactor for PHP 8.2 compatibility

This commit addresses compatibility issues in the qtype_calculated
question type. The following problems were identified and resolved:

- qtype_calculated was reusing the qtype_numerical_answer class and
  setting two additional properties ($correctanswerlength and
  $correctanswerformat) that were not declared. To resolve this, a new
  class, qtype_calculated_answer, was defined and utilised.
- During grading in qtype_numerical, there were references to an
  undeclared property ($unitisright) used to track calculation details.
  This issue was fixed using a 'replace temp with query' refactoring.

These changes ensure the qtype_calculated question type is compatible
with PHP 8.2 and addresses the identified issues.
This commit is contained in:
Shamim Rezaie
2023-07-13 21:03:23 +10:00
parent 2b23c8cee2
commit 9f00818d00
5 changed files with 93 additions and 21 deletions
@@ -0,0 +1,38 @@
<?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/>.
declare(strict_types=1);
namespace qtype_calculated;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/question/type/numerical/question.php');
/**
* Class to represent a calculated question answer.
*
* @package qtype_calculated
* @copyright 2023 Shamim Rezaie <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qtype_calculated_answer extends \qtype_numerical_answer {
/** @var int The length of the correct answer. */
public $correctanswerlength;
/** @var int The format of the correct answer. */
public $correctanswerformat;
}
+18 -2
View File
@@ -332,11 +332,27 @@ class qtype_calculated extends question_type {
}
}
/**
* Initializes calculated answers for a given question.
*
* @param question_definition $question The question definition object.
* @param stdClass $questiondata The question data object.
*/
protected function initialise_calculated_answers(question_definition $question, stdClass $questiondata) {
$question->answers = array();
if (empty($questiondata->options->answers)) {
return;
}
foreach ($questiondata->options->answers as $a) {
$question->answers[$a->id] = new \qtype_calculated\qtype_calculated_answer($a->id, $a->answer,
$a->fraction, $a->feedback, $a->feedbackformat, $a->tolerance);
}
}
protected function initialise_question_instance(question_definition $question, $questiondata) {
parent::initialise_question_instance($question, $questiondata);
$this->initialise_calculated_answers($question, $questiondata);
question_bank::get_qtype('numerical')->initialise_numerical_answers(
$question, $questiondata);
foreach ($questiondata->options->answers as $a) {
$question->answers[$a->id]->tolerancetype = $a->tolerancetype;
$question->answers[$a->id]->correctanswerlength = $a->correctanswerlength;
+10 -12
View File
@@ -56,12 +56,11 @@ class qtype_calculated_test_helper extends question_test_helper {
$q->questiontext = 'What is {a} + {b}?';
$q->generalfeedback = 'Generalfeedback: {={a} + {b}} is the right answer.';
$q->answers = array(
13 => new qtype_numerical_answer(13, '{a} + {b}', 1.0, 'Very good.', FORMAT_HTML, 0),
14 => new qtype_numerical_answer(14, '{a} - {b}', 0.0, 'Add. not subtract!.',
FORMAT_HTML, 0),
17 => new qtype_numerical_answer(17, '*', 0.0, 'Completely wrong.', FORMAT_HTML, 0),
);
$q->answers = [
13 => new \qtype_calculated\qtype_calculated_answer(13, '{a} + {b}', 1.0, 'Very good.', FORMAT_HTML, 0),
14 => new \qtype_calculated\qtype_calculated_answer(14, '{a} - {b}', 0.0, 'Add. not subtract!.', FORMAT_HTML, 0),
17 => new \qtype_calculated\qtype_calculated_answer(17, '*', 0.0, 'Completely wrong.', FORMAT_HTML, 0),
];
foreach ($q->answers as $answer) {
$answer->correctanswerlength = 2;
$answer->correctanswerformat = 1;
@@ -106,12 +105,11 @@ class qtype_calculated_test_helper extends question_test_helper {
$qdata->options->unitsleft = 0;
$qdata->options->synchronize = 0;
$qdata->options->answers = array(
13 => new qtype_numerical_answer(13, '{a} + {b}', 1.0, 'Very good.', FORMAT_HTML, 0.001),
14 => new qtype_numerical_answer(14, '{a} - {b}', 0.0, 'Add. not subtract!.',
FORMAT_HTML, 0.001),
17 => new qtype_numerical_answer(17, '*', 0.0, 'Completely wrong.', FORMAT_HTML, 0),
);
$qdata->options->answers = [
13 => new \qtype_calculated\qtype_calculated_answer(13, '{a} + {b}', 1.0, 'Very good.', FORMAT_HTML, 0.001),
14 => new \qtype_calculated\qtype_calculated_answer(14, '{a} - {b}', 0.0, 'Add. not subtract!.', FORMAT_HTML, 0.001),
17 => new \qtype_calculated\qtype_calculated_answer(17, '*', 0.0, 'Completely wrong.', FORMAT_HTML, 0),
];
foreach ($qdata->options->answers as $answer) {
$answer->correctanswerlength = 2;
$answer->correctanswerformat = 1;
+25 -6
View File
@@ -207,10 +207,8 @@ class qtype_numerical_question extends question_graded_automatically {
}
foreach ($this->answers as $answer) {
if ($answer->within_tolerance($scaledvalue)) {
$answer->unitisright = !is_null($multiplier);
return $answer;
} else if ($answer->within_tolerance($value)) {
$answer->unitisright = false;
return $answer;
}
}
@@ -218,6 +216,23 @@ class qtype_numerical_question extends question_graded_automatically {
return null;
}
/**
* Checks if the provided $multiplier is appropriate for the unit of the given $value,
* ensuring that multiplying $value by the $multiplier yields the expected $answer.
*
* @param qtype_numerical_answer $answer The expected result when multiplying $value by the appropriate $multiplier.
* @param float $value The provided value
* @param float|null $multiplier The multiplier value for the unit of $value.
* @return bool Returns true if the $multiplier is correct for the unit of $value, false otherwise.
*/
public function is_unit_right(qtype_numerical_answer $answer, float $value, ?float $multiplier): bool {
if (is_null($multiplier)) {
return false;
}
return $answer->within_tolerance($multiplier * $value);
}
public function get_correct_answer() {
foreach ($this->answers as $answer) {
$state = question_state::graded_state_for_fraction($answer->fraction);
@@ -256,12 +271,14 @@ class qtype_numerical_question extends question_graded_automatically {
list($value, $unit, $multiplier) = $this->ap->apply_units(
$response['answer'], $selectedunit);
/** @var qtype_numerical_answer $answer */
$answer = $this->get_matching_answer($value, $multiplier);
if (!$answer) {
return array(0, question_state::$gradedwrong);
}
$fraction = $this->apply_unit_penalty($answer->fraction, $answer->unitisright);
$unitisright = $this->is_unit_right($answer, $value, $multiplier);
$fraction = $this->apply_unit_penalty($answer->fraction, $unitisright);
return array($fraction, question_state::graded_state_for_fraction($fraction));
}
@@ -276,6 +293,7 @@ class qtype_numerical_question extends question_graded_automatically {
$selectedunit = null;
}
list($value, $unit, $multiplier) = $this->ap->apply_units($response['answer'], $selectedunit);
/** @var qtype_numerical_answer $ans */
$ans = $this->get_matching_answer($value, $multiplier);
$resp = $response['answer'];
@@ -291,9 +309,10 @@ class qtype_numerical_question extends question_graded_automatically {
return array($this->id => new question_classified_response(0, $resp, 0));
}
return array($this->id => new question_classified_response($ans->id,
$resp,
$this->apply_unit_penalty($ans->fraction, $ans->unitisright)));
$unitisright = $this->is_unit_right($ans, $value, $multiplier);
return [
$this->id => new question_classified_response($ans->id, $resp, $this->apply_unit_penalty($ans->fraction, $unitisright))
];
}
public function check_file_access($qa, $options, $component, $filearea, $args,
+2 -1
View File
@@ -62,7 +62,8 @@ class qtype_numerical_renderer extends qtype_renderer {
$currentanswer, $selectedunit);
$answer = $question->get_matching_answer($value, $multiplier);
if ($answer) {
$fraction = $question->apply_unit_penalty($answer->fraction, $answer->unitisright);
$unitisright = $question->is_unit_right($answer, $value, $multiplier);
$fraction = $question->apply_unit_penalty($answer->fraction, $unitisright);
} else {
$fraction = 0;
}