MDL-79863 qtype_ordering: Ensure qtype_ordering is compatible with PHP 8.2

Co-authored by: Mathew May <mathewm@hotmail.co.nz>
This commit is contained in:
Ilya Tregubov 2023-12-22 09:56:23 +08:00 committed by Mathew May
parent 63ed08c050
commit 272c8976fe
13 changed files with 74 additions and 88 deletions

View File

@ -72,7 +72,7 @@ class formulation_and_controls extends renderable_base {
if ($class = $question->get_ordering_layoutclass()) {
$data['layoutclass'] = $class;
}
if ($numberingstyle = $question->options->numberingstyle) {
if ($numberingstyle = $question->numberingstyle) {
$data['numberingstyle'] = $numberingstyle;
}

View File

@ -52,9 +52,9 @@ class specific_grade_detail_feedback extends renderable_base {
$plugin = 'qtype_ordering';
// Show grading details if they are required.
if ($question->options->showgrading) {
if ($question->showgrading) {
// Fetch grading type.
$gradingtype = $question->options->gradingtype;
$gradingtype = $question->gradingtype;
$gradingtype = qtype_ordering_question::get_grading_types($gradingtype);
// Format grading type, e.g. Grading type: Relative to next item, excluding last item.

View File

@ -5,11 +5,11 @@
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="questionid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="layouttype" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="selecttype" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="selectcount" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="gradingtype" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="showgrading" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="layouttype" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Layout type - horizontal or vertical"/>
<FIELD NAME="selecttype" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Select type - all, random etc."/>
<FIELD NAME="selectcount" TYPE="int" LENGTH="4" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The number to select."/>
<FIELD NAME="gradingtype" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Which grading strategy to use. One of the GRADING_... constants."/>
<FIELD NAME="showgrading" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Should details of the grading calculation be shown to students."/>
<FIELD NAME="numberingstyle" TYPE="char" LENGTH="10" NOTNULL="true" DEFAULT="none" SEQUENCE="false" COMMENT="Indicates whether and how choices should be numbered."/>
<FIELD NAME="correctfeedback" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="correctfeedbackformat" TYPE="int" LENGTH="2" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>

View File

@ -102,9 +102,6 @@ class qtype_ordering_question extends question_graded_automatically {
/** @var array Records from "question_answers" table */
public $answers;
/** @var stdClass Records from "qtype_ordering_options" table */
public $options;
/** @var array of answerids in correct order */
public $correctresponse;
@ -134,12 +131,12 @@ class qtype_ordering_question extends question_graded_automatically {
$countanswers = count($this->answers);
// Sanitize "selecttype".
$selecttype = $this->options->selecttype;
$selecttype = $this->selecttype;
$selecttype = max(0, $selecttype);
$selecttype = min(2, $selecttype);
// Sanitize "selectcount".
$selectcount = $this->options->selectcount;
$selectcount = $this->selectcount;
$selectcount = max(3, $selectcount);
$selectcount = min($countanswers, $selectcount);
@ -408,7 +405,7 @@ class qtype_ordering_question extends question_graded_automatically {
$countcorrect = 0;
$countanswers = 0;
$gradingtype = $this->options->gradingtype;
$gradingtype = $this->gradingtype;
switch ($gradingtype) {
case self::GRADING_ALL_OR_NOTHING:
@ -593,7 +590,7 @@ class qtype_ordering_question extends question_graded_automatically {
* @return string
*/
public function get_ordering_layoutclass(): string {
switch ($this->options->layouttype) {
switch ($this->layouttype) {
case self::LAYOUT_VERTICAL:
return 'vertical';
case self::LAYOUT_HORIZONTAL:
@ -873,7 +870,7 @@ class qtype_ordering_question extends question_graded_automatically {
*/
public function get_num_parts_right(array $response): array {
$this->update_current_response($response);
$gradingtype = $this->options->gradingtype;
$gradingtype = $this->gradingtype;
$numright = 0;
$numpartial = 0;
@ -914,7 +911,7 @@ class qtype_ordering_question extends question_graded_automatically {
array $correctresponse,
array $currentresponse
): array {
$gradingtype = $this->options->gradingtype;
$gradingtype = $this->gradingtype;
$score = 0;
$maxscore = null;
@ -1044,7 +1041,7 @@ class qtype_ordering_question extends question_graded_automatically {
if (!isset($this->itemscores[$position])) {
[$correctresponse, $currentresponse] = $this->get_response_depend_on_grading_type($question->options->gradingtype);
[$correctresponse, $currentresponse] = $this->get_response_depend_on_grading_type($this->gradingtype);
$percent = 0; // 100 * $fraction.
[$fraction, $score, $maxscore] =

View File

@ -77,9 +77,6 @@ class qtype_ordering extends question_type {
$question->answers[$answerid]->md5key = 'ordering_item_' . md5(($CFG->passwordsaltmain ?? '') . $answer->answer);
}
$question->options = clone($questiondata->options);
unset($question->options->answers);
$this->initialise_combined_feedback($question, $questiondata, true);
}

View File

@ -14,6 +14,12 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
use qtype_ordering\output\correct_response;
use qtype_ordering\output\feedback;
use qtype_ordering\output\formulation_and_controls;
use qtype_ordering\output\num_parts_correct;
use qtype_ordering\output\specific_grade_detail_feedback;
/**
* Ordering question renderer class.
*
@ -21,41 +27,24 @@
* @copyright 2013 Gordon Bateson (gordonbateson@gmail.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// Prevent direct access to this script.
/**
* Generates the output for ordering questions
*
* @copyright 2013 Gordon Bateson
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qtype_ordering_renderer extends qtype_with_combined_feedback_renderer {
/** @var array of answerids in correct order */
protected $correctinfo = null;
/** @var array of answerids in order of current answer*/
protected $currentinfo = null;
/** @var array of scored for every item */
protected $itemscores = array();
/** @var bool True if answer is 100% correct */
protected $allcorrect = null;
// Disable coverage report for most of this file as each method is tested separately and as a while via Behat.
// @codeCoverageIgnoreStart
/**
* Generate the display of the formulation part of the question. This is the
* area that contains the quetsion text, and the controls for students to
* area that contains the question text, and the controls for students to
* input their answers. Some question types also embed bits of feedback, for
* example ticks and crosses, in this area.
*
* @param question_attempt $qa the question attempt to display.
* @param question_display_options $options controls what should and should not be displayed.
* @return string HTML fragment.
* @throws moodle_exception
*/
public function formulation_and_controls(question_attempt $qa, question_display_options $options) {
$formulationandcontrols = new \qtype_ordering\output\formulation_and_controls($qa, $options);
public function formulation_and_controls(question_attempt $qa, question_display_options $options): string {
$formulationandcontrols = new formulation_and_controls($qa, $options);
return $this->output->render_from_template('qtype_ordering/formulation_and_controls',
$formulationandcontrols->export_for_template($this->output));
}
@ -65,13 +54,13 @@ class qtype_ordering_renderer extends qtype_with_combined_feedback_renderer {
* area that contains the various forms of feedback. This function generates
* the content of this area belonging to the question type.
*
* @codeCoverageIgnore This is tested by the feedback exporter.
* @param question_attempt $qa The question attempt to display.
* @param question_display_options $options Controls what should and should not be displayed.
* @return string HTML fragment.
* @throws moodle_exception
*/
public function feedback(question_attempt $qa, question_display_options $options) {
$feedback = new \qtype_ordering\output\feedback($qa, $options);
public function feedback(question_attempt $qa, question_display_options $options): string {
$feedback = new feedback($qa, $options);
return $this->output->render_from_template('qtype_ordering/feedback',
$feedback->export_for_template($this->output));
}
@ -81,9 +70,10 @@ class qtype_ordering_renderer extends qtype_with_combined_feedback_renderer {
*
* @param question_attempt $qa The question attempt to display.
* @return string Output grade detail of the response.
* @throws moodle_exception
*/
public function specific_grade_detail_feedback(question_attempt $qa): string {
$specificgradedetailfeedback = new \qtype_ordering\output\specific_grade_detail_feedback($qa);
$specificgradedetailfeedback = new specific_grade_detail_feedback($qa);
return $this->output->render_from_template('qtype_ordering/specific_grade_detail_feedback',
$specificgradedetailfeedback->export_for_template($this->output));
}
@ -92,11 +82,10 @@ class qtype_ordering_renderer extends qtype_with_combined_feedback_renderer {
* Generate the specific feedback. This is feedback that varies according to
* the response the student gave.
*
* @codeCoverageIgnore This is tested by the feedback exporter.
* @param question_attempt $qa The question attempt to display.
* @return string HTML fragment.
*/
public function specific_feedback(question_attempt $qa) {
public function specific_feedback(question_attempt $qa): string {
return $this->combined_feedback($qa);
}
@ -107,34 +96,38 @@ class qtype_ordering_renderer extends qtype_with_combined_feedback_renderer {
*
* @param question_attempt $qa the question attempt to display.
* @return string HTML fragment.
* @throws moodle_exception
*/
public function correct_response(question_attempt $qa): string {
$correctresponse = new \qtype_ordering\output\correct_response($qa);
$correctresponse = new correct_response($qa);
return $this->output->render_from_template('qtype_ordering/correct_response',
$correctresponse->export_for_template($this->output));
}
// Custom methods.
/**
* Generate a brief statement of how many sub-parts of this question the
* student got correct|partial|incorrect.
*
* @param question_attempt $qa The question attempt to display.
* @return string HTML fragment.
* @throws moodle_exception
*/
protected function num_parts_correct(question_attempt $qa) {
$numpartscorrect = new \qtype_ordering\output\num_parts_correct($qa);
protected function num_parts_correct(question_attempt $qa): string {
$numpartscorrect = new num_parts_correct($qa);
return $this->output->render_from_template('qtype_ordering/num_parts_correct',
$numpartscorrect->export_for_template($this->output));
}
// Below this point, is code that will be included in the report as it isn't reported in isolation.
// @codeCoverageIgnoreEnd
/**
* Return an appropriate icon (green tick, red cross, etc.) for a grade.
* Note: Strict typing the params here breaks code eval as the parent function is not strictly typed.
*
* @param float $fraction grade on a scale 0..1.
* @param bool $selected whether to show a big or small icon. (Deprecated)
* @param float $fraction The fraction of the maximum grade that was awarded.
* @param bool $selected Deprecated: size option.
* @return string html fragment.
*/
public function feedback_image($fraction, $selected = true): string {

View File

@ -62,14 +62,13 @@ class qtype_ordering_test_helper extends question_test_helper {
17 => $this->make_answer(17, 'Learning', FORMAT_HTML, 5, true),
18 => $this->make_answer(18, 'Environment', FORMAT_HTML, 6, true),
];
$q->options = new stdClass();
$q->options->layouttype = qtype_ordering_question::LAYOUT_HORIZONTAL;
$q->options->selecttype = qtype_ordering_question::SELECT_ALL;
$q->options->selectcount = 0;
$q->options->gradingtype = qtype_ordering_question::GRADING_RELATIVE_ALL_PREVIOUS_AND_NEXT;
$q->options->showgrading = true;
$q->options->numberingstyle = qtype_ordering_question::NUMBERING_STYLE_DEFAULT;
$q->options->shownumcorrect = 1;
$q->layouttype = qtype_ordering_question::LAYOUT_HORIZONTAL;
$q->selecttype = qtype_ordering_question::SELECT_ALL;
$q->selectcount = 0;
$q->gradingtype = qtype_ordering_question::GRADING_RELATIVE_ALL_PREVIOUS_AND_NEXT;
$q->showgrading = true;
$q->numberingstyle = qtype_ordering_question::NUMBERING_STYLE_DEFAULT;
$q->shownumcorrect = 1;
return $q;
}

View File

@ -50,8 +50,8 @@ class correct_response_test extends advanced_testcase {
$question = test_question_maker::make_question('ordering');
// Set the grading type and layout type options.
$question->options->gradingtype = qtype_ordering_question::GRADING_ABSOLUTE_POSITION;
$question->options->layouttype = $layouttype === 'horizontal' ? qtype_ordering_question::LAYOUT_HORIZONTAL :
$question->gradingtype = qtype_ordering_question::GRADING_ABSOLUTE_POSITION;
$question->layouttype = $layouttype === 'horizontal' ? qtype_ordering_question::LAYOUT_HORIZONTAL :
qtype_ordering_question::LAYOUT_VERTICAL;
// Create a question attempt.
$qa = new \testable_question_attempt($question, 0);

View File

@ -77,7 +77,7 @@ class feedback_test extends qbehaviour_walkthrough_test_base {
new question_hint_ordering(13, 'This is the first hint.', FORMAT_HTML, true, false, true),
new question_hint_ordering(14, 'This is the second hint.', FORMAT_HTML, false, false, false),
];
$question->options->layouttype = $testoptions['rot'] === 'horizontal' ? qtype_ordering_question::LAYOUT_HORIZONTAL :
$question->layouttype = $testoptions['rot'] === 'horizontal' ? qtype_ordering_question::LAYOUT_HORIZONTAL :
qtype_ordering_question::LAYOUT_VERTICAL;
// If we need to access the attempt midway through, we need a flow where we don't grade instantly.
@ -86,7 +86,7 @@ class feedback_test extends qbehaviour_walkthrough_test_base {
$step = new \question_attempt_step();
$qa->add_step($step);
$qa->set_behaviour($question->make_behaviour($qa, 'interactive'));
$question->options->gradingtype = $gradingtype;
$question->gradingtype = $gradingtype;
$question->start_attempt($step, 1);
// Process a response and check the expected result.
$keys = implode(',', array_keys($answeritems));

View File

@ -52,7 +52,7 @@ class formulation_and_controls_test extends advanced_testcase {
global $PAGE;
$question = test_question_maker::make_question('ordering');
$question->options->layouttype = $layouttype === 'horizontal' ? qtype_ordering_question::LAYOUT_HORIZONTAL :
$question->layouttype = $layouttype === 'horizontal' ? qtype_ordering_question::LAYOUT_HORIZONTAL :
qtype_ordering_question::LAYOUT_VERTICAL;
$qa = new \testable_question_attempt($question, 0);
$step = new \question_attempt_step();
@ -66,7 +66,7 @@ class formulation_and_controls_test extends advanced_testcase {
$options->rightanswer = question_display_options::VISIBLE;
$options->manualcomment = question_display_options::VISIBLE;
$options->history = question_display_options::VISIBLE;
$question->options->gradingtype = $gradingtype;
$question->gradingtype = $gradingtype;
$keys = implode(',', array_keys($answeritems));
$values = array_values($answeritems);

View File

@ -54,7 +54,7 @@ class num_parts_correct_test extends advanced_testcase {
$step = new \question_attempt_step();
$qa->add_step($step);
$question->start_attempt($step, 1);
$question->options->gradingtype = $gradingtype;
$question->gradingtype = $gradingtype;
$keys = implode(',', array_keys($answeritems));
$step->set_qt_var('_currentresponse', $keys);

View File

@ -53,10 +53,10 @@ class specific_grade_detail_feedback_test extends advanced_testcase {
$this->resetAfterTest();
$question = test_question_maker::make_question('ordering');
// Options need to be set before starting the attempt otherwise they are not passed along.
$question->options->layouttype = $layouttype === 'horizontal' ? qtype_ordering_question::LAYOUT_HORIZONTAL :
$question->layouttype = $layouttype === 'horizontal' ? qtype_ordering_question::LAYOUT_HORIZONTAL :
qtype_ordering_question::LAYOUT_VERTICAL;
$question->options->gradingtype = $gradingtype;
$question->options->selecttype = $selecttype;
$question->gradingtype = $gradingtype;
$question->selecttype = $selecttype;
$qa = new \testable_question_attempt($question, 0);
$step = new \question_attempt_step();
$qa->add_step($step);

View File

@ -61,7 +61,7 @@ class question_test extends \advanced_testcase {
/** @var qtype_ordering_question $question */
$question = test_question_maker::make_question('ordering');
// Zero grade on any error (no partial score at all, it is either 1 or 0).
$question->options->gradingtype = qtype_ordering_question::GRADING_ALL_OR_NOTHING;
$question->gradingtype = qtype_ordering_question::GRADING_ALL_OR_NOTHING;
$question->start_attempt(new question_attempt_pending_step(), 1);
$this->assertEquals(
@ -97,7 +97,7 @@ class question_test extends \advanced_testcase {
/** @var qtype_ordering_question $question */
$question = test_question_maker::make_question('ordering');
// Counts items, placed into right absolute place.
$question->options->gradingtype = qtype_ordering_question::GRADING_ABSOLUTE_POSITION;
$question->gradingtype = qtype_ordering_question::GRADING_ABSOLUTE_POSITION;
$question->start_attempt(new question_attempt_pending_step(), 1);
// Every item is in the correct position.
$this->assertEquals(
@ -163,7 +163,7 @@ class question_test extends \advanced_testcase {
/** @var qtype_ordering_question $question */
$question = test_question_maker::make_question('ordering');
// Every sequential pair in right order is graded (last pair is excluded).
$question->options->gradingtype = qtype_ordering_question::GRADING_RELATIVE_NEXT_EXCLUDE_LAST;
$question->gradingtype = qtype_ordering_question::GRADING_RELATIVE_NEXT_EXCLUDE_LAST;
$question->start_attempt(new question_attempt_pending_step(), 1);
// Every item is in the correct position.
@ -212,7 +212,7 @@ class question_test extends \advanced_testcase {
/** @var qtype_ordering_question $question */
$question = test_question_maker::make_question('ordering');
// Every sequential pair in right order is graded (last pair is included).
$question->options->gradingtype = qtype_ordering_question::GRADING_RELATIVE_NEXT_INCLUDE_LAST;
$question->gradingtype = qtype_ordering_question::GRADING_RELATIVE_NEXT_INCLUDE_LAST;
$question->start_attempt(new question_attempt_pending_step(), 1);
// Every item is in the correct position.
$this->assertEquals(
@ -250,7 +250,7 @@ class question_test extends \advanced_testcase {
/** @var qtype_ordering_question $question */
$question = test_question_maker::make_question('ordering');
// Single answers that are placed before and after each answer is graded if in right order.
$question->options->gradingtype = qtype_ordering_question::GRADING_RELATIVE_ONE_PREVIOUS_AND_NEXT;
$question->gradingtype = qtype_ordering_question::GRADING_RELATIVE_ONE_PREVIOUS_AND_NEXT;
$question->start_attempt(new question_attempt_pending_step(), 1);
// All items are in the correct position.
$this->assertEquals(
@ -297,7 +297,7 @@ class question_test extends \advanced_testcase {
/** @var qtype_ordering_question $question */
$question = test_question_maker::make_question('ordering');
// All answers that are placed before and after each answer is graded if in right order.
$question->options->gradingtype = qtype_ordering_question::GRADING_RELATIVE_ALL_PREVIOUS_AND_NEXT;
$question->gradingtype = qtype_ordering_question::GRADING_RELATIVE_ALL_PREVIOUS_AND_NEXT;
$question->start_attempt(new question_attempt_pending_step(), 1);
// All items are in the correct position.
$this->assertEquals(
@ -353,7 +353,7 @@ class question_test extends \advanced_testcase {
/** @var qtype_ordering_question $question */
$question = test_question_maker::make_question('ordering');
// Only longest ordered subset is graded.
$question->options->gradingtype = qtype_ordering_question::GRADING_LONGEST_ORDERED_SUBSET;
$question->gradingtype = qtype_ordering_question::GRADING_LONGEST_ORDERED_SUBSET;
$question->start_attempt(new question_attempt_pending_step(), 1);
// All items are in the correct position.
$this->assertEquals(
@ -400,7 +400,7 @@ class question_test extends \advanced_testcase {
/** @var qtype_ordering_question $question */
$question = test_question_maker::make_question('ordering');
// Only longest ordered and contiguous subset is graded.
$question->options->gradingtype = qtype_ordering_question::GRADING_LONGEST_CONTIGUOUS_SUBSET;
$question->gradingtype = qtype_ordering_question::GRADING_LONGEST_CONTIGUOUS_SUBSET;
$question->start_attempt(new question_attempt_pending_step(), 1);
// All items are in the correct position.
$this->assertEquals(
@ -447,7 +447,7 @@ class question_test extends \advanced_testcase {
/** @var qtype_ordering_question $question */
$question = test_question_maker::make_question('ordering');
// Items are graded relative to their position in the correct answer.
$question->options->gradingtype = qtype_ordering_question::GRADING_RELATIVE_TO_CORRECT;
$question->gradingtype = qtype_ordering_question::GRADING_RELATIVE_TO_CORRECT;
$question->start_attempt(new question_attempt_pending_step(), 1);
// All items are in the correct position.
$this->assertEquals(
@ -579,13 +579,13 @@ class question_test extends \advanced_testcase {
/** @var qtype_ordering_question $question */
$question = test_question_maker::make_question('ordering');
if ($question->options->layouttype === 0) {
if ($question->layouttype === 0) {
$this->assertEquals('vertical', $question->get_ordering_layoutclass());
} else if ($question->options->layouttype === 1) {
} else if ($question->layouttype === 1) {
$this->assertEquals('horizontal', $question->get_ordering_layoutclass());
}
// Confirm that if an invalid layouttype is set, an empty string is returned.
$question->options->layouttype = 3;
$question->layouttype = 3;
$error = $question->get_ordering_layoutclass();
$this->assertEquals('', $error);
}
@ -697,7 +697,7 @@ class question_test extends \advanced_testcase {
/** @var qtype_ordering_question $question */
$question = test_question_maker::make_question('ordering');
$question->options->gradingtype = qtype_ordering_question::GRADING_RELATIVE_TO_CORRECT;
$question->gradingtype = qtype_ordering_question::GRADING_RELATIVE_TO_CORRECT;
$question->start_attempt(new question_attempt_pending_step(), 1);
$response = qtype_ordering_test_helper::get_response(