Rev 1 | AutorÃa | Comparar con el anterior | Ultima modificación | Ver Log |
<?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/>./*** Represents an ordering question.** @package qtype_ordering* @copyright 2013 Gordon Bateson (gordon.bateson@gmail.com)* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/class qtype_ordering_question extends question_graded_automatically {/** Select all answers */const SELECT_ALL = 0;/** Select random set of answers */const SELECT_RANDOM = 1;/** Select contiguous subset of answers */const SELECT_CONTIGUOUS = 2;/** Show answers in vertical list */const LAYOUT_VERTICAL = 0;/** Show answers in one horizontal line */const LAYOUT_HORIZONTAL = 1;/** The minimum number of items to create a subset */const MIN_SUBSET_ITEMS = 2;/** Default value for numberingstyle */const NUMBERING_STYLE_DEFAULT = 'none';/** @var int Zero grade on any error */const GRADING_ALL_OR_NOTHING = -1;/** @var int Counts items, placed into right absolute place */const GRADING_ABSOLUTE_POSITION = 0;/** @var int Every sequential pair in right order is graded (last pair is excluded) */const GRADING_RELATIVE_NEXT_EXCLUDE_LAST = 1;/** @var int Every sequential pair in right order is graded (last pair is included) */const GRADING_RELATIVE_NEXT_INCLUDE_LAST = 2;/** @var int Single answers that are placed before and after each answer is graded if in right order*/const GRADING_RELATIVE_ONE_PREVIOUS_AND_NEXT = 3;/** @var int All answers that are placed before and after each answer is graded if in right order*/const GRADING_RELATIVE_ALL_PREVIOUS_AND_NEXT = 4;/** @var int Only longest ordered subset is graded */const GRADING_LONGEST_ORDERED_SUBSET = 5;/** @var int Only longest ordered and contiguous subset is graded */const GRADING_LONGEST_CONTIGUOUS_SUBSET = 6;/** @var int Items are graded relative to their position in the correct answer */const GRADING_RELATIVE_TO_CORRECT = 7;/** @var int {@see LAYOUT_VERTICAL} or {@see LAYOUT_HORIZONTAL}. */public $layouttype;/** @var int {@see SELECT_ALL}, {@see SELECT_RANDOM} or {@see SELECT_CONTIGUOUS}. */public $selecttype;/** @var int if {@see $selecttype} is not SELECT_ALL, then the number to select. */public $selectcount;/** @var int Which grading strategy to use. One of the GRADING_... constants. */public $gradingtype;/** @var bool Should details of the grading calculation be shown to students. */public $showgrading;/** @var string How to number the items. A key from the array returned by {@see get_numbering_styles()}. */public $numberingstyle;// Fields from "qtype_ordering_options" table./** @var string */public $correctfeedback;/** @var int */public $correctfeedbackformat;/** @var string */public $incorrectfeedback;/** @var int */public $incorrectfeedbackformat;/** @var string */public $partiallycorrectfeedback;/** @var int */public $partiallycorrectfeedbackformat;/** @var array Records from "question_answers" table */public $answers;/** @var array of answerids in correct order */public $correctresponse;/** @var array contatining current order of answerids */public $currentresponse;/** @var array of scored for every item */protected $itemscores = [];public function start_attempt(question_attempt_step $step, $variant) {$countanswers = count($this->answers);// Sanitize "selecttype".$selecttype = $this->selecttype;$selecttype = max(0, $selecttype);$selecttype = min(2, $selecttype);// Sanitize "selectcount".$selectcount = $this->selectcount;$selectcount = max(self::MIN_SUBSET_ITEMS, $selectcount);$selectcount = min($countanswers, $selectcount);// Ensure consistency between "selecttype" and "selectcount".switch (true) {case ($selecttype == self::SELECT_ALL):$selectcount = $countanswers;break;case ($selectcount == $countanswers):$selecttype = self::SELECT_ALL;break;}// Extract answer ids.switch ($selecttype) {case self::SELECT_ALL:$answerids = array_keys($this->answers);break;case self::SELECT_RANDOM:$answerids = array_rand($this->answers, $selectcount);break;case self::SELECT_CONTIGUOUS:$answerids = array_keys($this->answers);$offset = mt_rand(0, $countanswers - $selectcount);$answerids = array_slice($answerids, $offset, $selectcount);break;}$this->correctresponse = $answerids;$step->set_qt_var('_correctresponse', implode(',', $this->correctresponse));shuffle($answerids);$this->currentresponse = $answerids;$step->set_qt_var('_currentresponse', implode(',', $this->currentresponse));}public function apply_attempt_state(question_attempt_step $step) {$this->currentresponse = array_filter(explode(',', $step->get_qt_var('_currentresponse')));$this->correctresponse = array_filter(explode(',', $step->get_qt_var('_correctresponse')));}public function validate_can_regrade_with_other_version(question_definition $otherversion): ?string {$basemessage = parent::validate_can_regrade_with_other_version($otherversion);if ($basemessage) {return $basemessage;}if (count($this->answers) != count($otherversion->answers)) {return get_string('regradeissuenumitemschanged', 'qtype_ordering');}return null;}public function update_attempt_state_data_for_new_version(question_attempt_step $oldstep, question_definition $oldquestion) {parent::update_attempt_state_data_for_new_version($oldstep, $oldquestion);$mapping = array_combine(array_keys($oldquestion->answers), array_keys($this->answers));$oldorder = explode(',', $oldstep->get_qt_var('_currentresponse'));$neworder = [];foreach ($oldorder as $oldid) {$neworder[] = $mapping[$oldid] ?? $oldid;}$oldcorrect = explode(',', $oldstep->get_qt_var('_correctresponse'));$newcorrect = [];foreach ($oldcorrect as $oldid) {$newcorrect[] = $mapping[$oldid] ?? $oldid;}return ['_currentresponse' => implode(',', $neworder),'_correctresponse' => implode(',', $newcorrect),];}public function get_expected_data() {$name = $this->get_response_fieldname();return [$name => PARAM_TEXT];}public function get_correct_response() {$correctresponse = $this->correctresponse;foreach ($correctresponse as $position => $answerid) {$answer = $this->answers[$answerid];$correctresponse[$position] = $answer->md5key;}$name = $this->get_response_fieldname();return [$name => implode(',', $correctresponse)];}public function summarise_response(array $response) {$name = $this->get_response_fieldname();$items = [];if (array_key_exists($name, $response)) {$items = explode(',', $response[$name]);}$answerids = [];foreach ($this->answers as $answer) {$answerids[$answer->md5key] = $answer->id;}foreach ($items as $i => $item) {if (array_key_exists($item, $answerids)) {$item = $this->answers[$answerids[$item]];$item = $this->html_to_text($item->answer, $item->answerformat);$item = shorten_text($item, 10, true); // Force truncate at 10 chars.$items[$i] = $item;} else {$items[$i] = ''; // Shouldn't happen!}}return implode('; ', array_filter($items));}public function classify_response(array $response) {$this->update_current_response($response);$fraction = 1 / count($this->correctresponse);$classifiedresponse = [];foreach ($this->correctresponse as $position => $answerid) {if (in_array($answerid, $this->currentresponse)) {$currentposition = array_search($answerid, $this->currentresponse);}$answer = $this->answers[$answerid];$subqid = question_utils::to_plain_text($answer->answer, $answer->answerformat);// Truncate responses longer than 100 bytes because they cannot be stored in the database.// CAUTION: This will mess up answers which are not unique within the first 100 chars!$maxbytes = 100;if (strlen($subqid) > $maxbytes) {// If the truncation point is in the middle of a multi-byte unicode char,// we remove the incomplete part with a preg_match() that is unicode aware.$subqid = substr($subqid, 0, $maxbytes);if (preg_match('/^(.|\n)*/u', '', $subqid, $match)) {$subqid = $match[0];}}$classifiedresponse[$subqid] = new question_classified_response($currentposition + 1,get_string('positionx', 'qtype_ordering', $currentposition + 1),($currentposition == $position) * $fraction);}return $classifiedresponse;}public function is_complete_response(array $response) {return true;}public function is_gradable_response(array $response) {return true;}public function get_validation_error(array $response) {return '';}public function is_same_response(array $old, array $new) {$name = $this->get_response_fieldname();return (isset($old[$name]) && isset($new[$name]) && $old[$name] == $new[$name]);}public function grade_response(array $response) {$this->update_current_response($response);$countcorrect = 0;$countanswers = 0;$gradingtype = $this->gradingtype;switch ($gradingtype) {case self::GRADING_ALL_OR_NOTHING:case self::GRADING_ABSOLUTE_POSITION:$correctresponse = $this->correctresponse;$currentresponse = $this->currentresponse;foreach ($correctresponse as $position => $answerid) {if (array_key_exists($position, $currentresponse)) {if ($currentresponse[$position] == $answerid) {$countcorrect++;}}$countanswers++;}if ($gradingtype == self::GRADING_ALL_OR_NOTHING && $countcorrect < $countanswers) {$countcorrect = 0;}break;case self::GRADING_RELATIVE_NEXT_EXCLUDE_LAST:case self::GRADING_RELATIVE_NEXT_INCLUDE_LAST:$lastitem = ($gradingtype == self::GRADING_RELATIVE_NEXT_INCLUDE_LAST);$currentresponse = $this->get_next_answerids($this->currentresponse, $lastitem);$correctresponse = $this->get_next_answerids($this->correctresponse, $lastitem);foreach ($correctresponse as $thisanswerid => $nextanswerid) {if (array_key_exists($thisanswerid, $currentresponse)) {if ($currentresponse[$thisanswerid] == $nextanswerid) {$countcorrect++;}}$countanswers++;}break;case self::GRADING_RELATIVE_ONE_PREVIOUS_AND_NEXT:case self::GRADING_RELATIVE_ALL_PREVIOUS_AND_NEXT:$all = ($gradingtype == self::GRADING_RELATIVE_ALL_PREVIOUS_AND_NEXT);$currentresponse = $this->get_previous_and_next_answerids($this->currentresponse, $all);$correctresponse = $this->get_previous_and_next_answerids($this->correctresponse, $all);foreach ($correctresponse as $thisanswerid => $answerids) {if (array_key_exists($thisanswerid, $currentresponse)) {$prev = $currentresponse[$thisanswerid]->prev;$prev = array_intersect($prev, $answerids->prev);$countcorrect += count($prev);$next = $currentresponse[$thisanswerid]->next;$next = array_intersect($next, $answerids->next);$countcorrect += count($next);}$countanswers += count($answerids->prev);$countanswers += count($answerids->next);}break;case self::GRADING_LONGEST_ORDERED_SUBSET:case self::GRADING_LONGEST_CONTIGUOUS_SUBSET:$contiguous = ($gradingtype == self::GRADING_LONGEST_CONTIGUOUS_SUBSET);$subset = $this->get_ordered_subset($contiguous);$countcorrect = count($subset);$countanswers = count($this->currentresponse);break;case self::GRADING_RELATIVE_TO_CORRECT:$correctresponse = $this->correctresponse;$currentresponse = $this->currentresponse;$count = (count($correctresponse) - 1);foreach ($correctresponse as $position => $answerid) {if (in_array($answerid, $currentresponse)) {$currentposition = array_search($answerid, $currentresponse);$currentscore = ($count - abs($position - $currentposition));if ($currentscore > 0) {$countcorrect += $currentscore;}}$countanswers += $count;}break;}if ($countanswers == 0) {$fraction = 0;} else {$fraction = ($countcorrect / $countanswers);}return [$fraction,question_state::graded_state_for_fraction($fraction),];}public function check_file_access($qa, $options, $component, $filearea, $args, $forcedownload) {if ($component == 'question') {if ($filearea == 'answer') {$answerid = reset($args); // Value of "itemid" is answer id.return array_key_exists($answerid, $this->answers);}if (in_array($filearea, ['correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback'])) {return $this->check_combined_feedback_file_access($qa, $options, $filearea, $args);}if ($filearea == 'hint') {return $this->check_hint_file_access($qa, $options, $args);}}return parent::check_file_access($qa, $options, $component, $filearea, $args, $forcedownload);}protected function check_combined_feedback_file_access($qa, $options, $filearea, $args = null) {$state = $qa->get_state();if (! $state->is_finished()) {$response = $qa->get_last_qt_data();if (! $this->is_gradable_response($response)) {return false;}list($fraction, $state) = $this->grade_response($response);}if ($state->get_feedback_class().'feedback' == $filearea) {return ($this->id == reset($args));} else {return false;}}// Custom methods./*** Returns response mform field name** @return string*/public function get_response_fieldname(): string {return 'response_' . $this->id;}/*** Unpack the students' response into an array which updates the question currentresponse.** @param array $response Form data*/public function update_current_response(array $response) {$name = $this->get_response_fieldname();if (array_key_exists($name, $response)) {$ids = explode(',', $response[$name]);foreach ($ids as $i => $id) {foreach ($this->answers as $answer) {if ($id == $answer->md5key) {$ids[$i] = $answer->id;break;}}}// Note: TH mentions that this is a bit of a hack.$this->currentresponse = $ids;}}/*** Returns layoutclass** @return string*/public function get_ordering_layoutclass(): string {switch ($this->layouttype) {case self::LAYOUT_VERTICAL:return 'vertical';case self::LAYOUT_HORIZONTAL:return 'horizontal';default:return ''; // Shouldn't happen!}}/*** Returns array of next answers** @param array $answerids array of answers id* @param bool $lastitem Include last item?* @return array of id of next answer*/public function get_next_answerids(array $answerids, bool $lastitem = false): array {$nextanswerids = [];$imax = count($answerids);$imax--;if ($lastitem) {$nextanswerid = 0;} else {$nextanswerid = $answerids[$imax];$imax--;}for ($i = $imax; $i >= 0; $i--) {$thisanswerid = $answerids[$i];$nextanswerids[$thisanswerid] = $nextanswerid;$nextanswerid = $thisanswerid;}return $nextanswerids;}/*** Returns prev and next answers array** @param array $answerids array of answers id* @param bool $all include all answers* @return array of array('prev' => previd, 'next' => nextid)*/public function get_previous_and_next_answerids(array $answerids, bool $all = false): array {$prevnextanswerids = [];$next = $answerids;$prev = [];while ($answerid = array_shift($next)) {if ($all) {$prevnextanswerids[$answerid] = (object) ['prev' => $prev,'next' => $next,];} else {$prevnextanswerids[$answerid] = (object) ['prev' => [empty($prev) ? 0 : $prev[0]],'next' => [empty($next) ? 0 : $next[0]],];}array_unshift($prev, $answerid);}return $prevnextanswerids;}/*** Search for best ordered subset** @param bool $contiguous A flag indicating whether only contiguous values should be considered for inclusion in the subset.* @return array*/public function get_ordered_subset(bool $contiguous): array {$positions = $this->get_ordered_positions($this->correctresponse, $this->currentresponse);$subsets = $this->get_ordered_subsets($positions, $contiguous);// The best subset (longest and leftmost).$bestsubset = [];// The length of the best subset// initializing this to 1 means// we ignore single item subsets.$bestcount = 1;foreach ($subsets as $subset) {$count = count($subset);if ($count > $bestcount) {$bestcount = $count;$bestsubset = $subset;}}return $bestsubset;}/*** Get array of right answer positions for current response** @param array $correctresponse* @param array $currentresponse* @return array*/public function get_ordered_positions(array $correctresponse, array $currentresponse): array {$positions = [];foreach ($currentresponse as $answerid) {$positions[] = array_search($answerid, $correctresponse);}return $positions;}/*** Get all ordered subsets in the positions array** @param array $positions maps an item's current position to its correct position* @param bool $contiguous TRUE if searching only for contiguous subsets; otherwise FALSE* @return array of ordered subsets from within the $positions array*/public function get_ordered_subsets(array $positions, bool $contiguous): array {// Var $subsets is the collection of all subsets within $positions.$subsets = [];// Loop through the values at each position.foreach ($positions as $p => $value) {// Is $value a "new" value that cannot be added to any $subsets found so far?$isnew = true;// An array of new and saved subsets to be added to $subsets.$new = [];// Append the current value to any subsets to which it belongs// i.e. any subset whose end value is less than the current value.foreach ($subsets as $s => $subset) {// Get value at end of $subset.$end = $positions[end($subset)];switch (true) {case ($value == ($end + 1)):// For a contiguous value, we simply append $p to the subset.$isnew = false;$subsets[$s][] = $p;break;case $contiguous:// If the $contiguous flag is set, we ignore non-contiguous values.break;case ($value > $end):// For a non-contiguous value, we save the subset so far,// because a value between $end and $value may be found later,// and then append $p to the subset.$isnew = false;$new[] = $subset;$subsets[$s][] = $p;break;}}// If this is a "new" value, add it as a new subset.if ($isnew) {$new[] = [$p];}// Append any "new" subsets that were found during this iteration.if (count($new)) {$subsets = array_merge($subsets, $new);}}return $subsets;}/*** Helper function for get_select_types, get_layout_types, get_grading_types** @param array $types* @param int $type* @return array|string array if $type is not specified and single string if $type is specified* @throws coding_exception* @codeCoverageIgnore*/public static function get_types(array $types, $type): array|string {if ($type === null) {return $types; // Return all $types.}if (array_key_exists($type, $types)) {return $types[$type]; // One $type.}throw new coding_exception('Invalid type: ' . $type);}/*** Returns available values and descriptions for field "selecttype"** @param int|null $type* @return array|string array if $type is not specified and single string if $type is specified* @codeCoverageIgnore*/public static function get_select_types(?int $type = null): array|string {$plugin = 'qtype_ordering';$types = [self::SELECT_ALL => get_string('selectall', $plugin),self::SELECT_RANDOM => get_string('selectrandom', $plugin),self::SELECT_CONTIGUOUS => get_string('selectcontiguous', $plugin),];return self::get_types($types, $type);}/*** Returns available values and descriptions for field "layouttype"** @param int|null $type* @return array|string array if $type is not specified and single string if $type is specified* @codeCoverageIgnore*/public static function get_layout_types(?int $type = null): array|string {$plugin = 'qtype_ordering';$types = [self::LAYOUT_VERTICAL => get_string('vertical', $plugin),self::LAYOUT_HORIZONTAL => get_string('horizontal', $plugin),];return self::get_types($types, $type);}/*** Returns available values and descriptions for field "gradingtype"** @param int|null $type* @return array|string array if $type is not specified and single string if $type is specified* @codeCoverageIgnore*/public static function get_grading_types(?int $type = null): array|string {$plugin = 'qtype_ordering';$types = [self::GRADING_ALL_OR_NOTHING => get_string('allornothing', $plugin),self::GRADING_ABSOLUTE_POSITION => get_string('absoluteposition', $plugin),self::GRADING_RELATIVE_TO_CORRECT => get_string('relativetocorrect', $plugin),self::GRADING_RELATIVE_NEXT_EXCLUDE_LAST => get_string('relativenextexcludelast', $plugin),self::GRADING_RELATIVE_NEXT_INCLUDE_LAST => get_string('relativenextincludelast', $plugin),self::GRADING_RELATIVE_ONE_PREVIOUS_AND_NEXT => get_string('relativeonepreviousandnext', $plugin),self::GRADING_RELATIVE_ALL_PREVIOUS_AND_NEXT => get_string('relativeallpreviousandnext', $plugin),self::GRADING_LONGEST_ORDERED_SUBSET => get_string('longestorderedsubset', $plugin),self::GRADING_LONGEST_CONTIGUOUS_SUBSET => get_string('longestcontiguoussubset', $plugin),];return self::get_types($types, $type);}/*** Get the numbering styles supported.** For each style, there should be a corresponding lang string 'numberingstylexxx' in the qtype_ordering language file,* a case in the switch statement in number_in_style, and it should be listed in the definition of this column in install.xml.** @param string|null $style The specific numbering style to retrieve.* @return array|string Numbering style(s).* The keys are style identifiers, and the values are the corresponding language strings.* @codeCoverageIgnore*/public static function get_numbering_styles(?string $style = null): array|string {$plugin = 'qtype_ordering';$styles = ['none' => get_string('numberingstylenone', $plugin),'abc' => get_string('numberingstyleabc', $plugin),'ABCD' => get_string('numberingstyleABCD', $plugin),'123' => get_string('numberingstyle123', $plugin),'iii' => get_string('numberingstyleiii', $plugin),'IIII' => get_string('numberingstyleIIII', $plugin),];return self::get_types($styles, $style);}/*** Return the number of subparts of this response that are correct|partial|incorrect.** @param array $response A response.* @return array Array of three elements: the number of correct subparts,* the number of partial correct subparts and the number of incorrect subparts.*/public function get_num_parts_right(array $response): array {$this->update_current_response($response);$gradingtype = $this->gradingtype;$numright = 0;$numpartial = 0;$numincorrect = 0;list($correctresponse, $currentresponse) = $this->get_response_depend_on_grading_type($gradingtype);foreach ($this->currentresponse as $position => $answerid) {[$fraction, $score, $maxscore] =$this->get_fraction_maxscore_score_of_item($position, $answerid, $correctresponse, $currentresponse);if (is_null($fraction)) {continue;}if ($fraction > 0.999999) {$numright++;} else if ($fraction < 0.000001) {$numincorrect++;} else {$numpartial++;}}return [$numright, $numpartial, $numincorrect];}/*** Returns the grade for one item, base on the fraction scale.** @param int $position The position of the current response.* @param int $answerid The answerid of the current response.* @param array $correctresponse The correct response list base on grading type.* @param array $currentresponse The current response list base on grading type.* @return array.*/protected function get_fraction_maxscore_score_of_item(int $position,int $answerid,array $correctresponse,array $currentresponse): array {$gradingtype = $this->gradingtype;$score = 0;$maxscore = null;switch ($gradingtype) {case self::GRADING_ALL_OR_NOTHING:case self::GRADING_ABSOLUTE_POSITION:if (isset($correctresponse[$position])) {if ($correctresponse[$position] == $answerid) {$score = 1;}$maxscore = 1;}break;case self::GRADING_RELATIVE_NEXT_EXCLUDE_LAST:case self::GRADING_RELATIVE_NEXT_INCLUDE_LAST:if (isset($correctresponse[$answerid])) {if (isset($currentresponse[$answerid]) && $currentresponse[$answerid] == $correctresponse[$answerid]) {$score = 1;}$maxscore = 1;}break;case self::GRADING_RELATIVE_ONE_PREVIOUS_AND_NEXT:case self::GRADING_RELATIVE_ALL_PREVIOUS_AND_NEXT:if (isset($correctresponse[$answerid])) {$maxscore = 0;$prev = $correctresponse[$answerid]->prev;$maxscore += count($prev);$prev = array_intersect($prev, $currentresponse[$answerid]->prev);$score += count($prev);$next = $correctresponse[$answerid]->next;$maxscore += count($next);$next = array_intersect($next, $currentresponse[$answerid]->next);$score += count($next);}break;case self::GRADING_LONGEST_ORDERED_SUBSET:case self::GRADING_LONGEST_CONTIGUOUS_SUBSET:if (isset($correctresponse[$position])) {if (isset($currentresponse[$position])) {$score = $currentresponse[$position];}$maxscore = 1;}break;case self::GRADING_RELATIVE_TO_CORRECT:if (isset($correctresponse[$position])) {$maxscore = (count($correctresponse) - 1);$answerid = $currentresponse[$position];$correctposition = array_search($answerid, $correctresponse);$score = ($maxscore - abs($correctposition - $position));if ($score < 0) {$score = 0;}}break;}$fraction = $maxscore ? $score / $maxscore : $maxscore;return [$fraction, $score, $maxscore];}/*** Get correcresponse and currentinfo depending on grading type.** @param string $gradingtype The kind of grading.* @return array Correctresponse and currentresponsescore in one array.*/protected function get_response_depend_on_grading_type(string $gradingtype): array {$correctresponse = [];$currentresponse = [];switch ($gradingtype) {case self::GRADING_ALL_OR_NOTHING:case self::GRADING_ABSOLUTE_POSITION:case self::GRADING_RELATIVE_TO_CORRECT:$correctresponse = $this->correctresponse;$currentresponse = $this->currentresponse;break;case self::GRADING_RELATIVE_NEXT_EXCLUDE_LAST:case self::GRADING_RELATIVE_NEXT_INCLUDE_LAST:$lastitem = ($gradingtype == self::GRADING_RELATIVE_NEXT_INCLUDE_LAST);$correctresponse = $this->get_next_answerids($this->correctresponse, $lastitem);$currentresponse = $this->get_next_answerids($this->currentresponse, $lastitem);break;case self::GRADING_RELATIVE_ONE_PREVIOUS_AND_NEXT:case self::GRADING_RELATIVE_ALL_PREVIOUS_AND_NEXT:$all = ($gradingtype == self::GRADING_RELATIVE_ALL_PREVIOUS_AND_NEXT);$correctresponse = $this->get_previous_and_next_answerids($this->correctresponse, $all);$currentresponse = $this->get_previous_and_next_answerids($this->currentresponse, $all);break;case self::GRADING_LONGEST_ORDERED_SUBSET:case self::GRADING_LONGEST_CONTIGUOUS_SUBSET:$correctresponse = $this->correctresponse;$currentresponse = $this->currentresponse;$contiguous = ($gradingtype == self::GRADING_LONGEST_CONTIGUOUS_SUBSET);$subset = $this->get_ordered_subset($contiguous);foreach ($currentresponse as $position => $answerid) {if (array_search($position, $subset) === false) {$currentresponse[$position] = 0;} else {$currentresponse[$position] = 1;}}break;}return [$correctresponse, $currentresponse];}/*** Returns score for one item depending on correctness and question settings.** @param question_definition $question question definition object* @param int $position The position of the current response.* @param int $answerid The answerid of the current response.* @return array (score, maxscore, fraction, percent, class)*/public function get_ordering_item_score(question_definition $question, int $position, int $answerid): array {if (!isset($this->itemscores[$position])) {[$correctresponse, $currentresponse] = $this->get_response_depend_on_grading_type($this->gradingtype);$percent = 0; // 100 * $fraction.[$fraction, $score, $maxscore] =$this->get_fraction_maxscore_score_of_item($position, $answerid, $correctresponse, $currentresponse);if ($maxscore === null) {// An unscored item is either an illegal item// or last item of RELATIVE_NEXT_EXCLUDE_LAST// or an item in an incorrect ALL_OR_NOTHING// or an item from an unrecognized grading type.$class = 'unscored';} else {if ($maxscore > 0) {$percent = round(100 * $fraction, 0);}$class = match (true) {$fraction > 0.999999 => 'correct',$fraction < 0.000001 => 'incorrect',$fraction >= 0.66 => 'partial66',$fraction >= 0.33 => 'partial33',default => 'partial00',};}$itemscores = ['score' => $score,'maxscore' => $maxscore,'fraction' => $fraction,'percent' => $percent,'class' => $class,];$this->itemscores[$position] = $itemscores;}return $this->itemscores[$position];}}