MDL-79863 qtype_ordering: qtype_ordering add new grading type: LONGEST_CONTIGUOUS_SUBSET

This commit is contained in:
Gordon Bateson 2016-01-23 12:31:09 +09:00 committed by Mathew May
parent 59d7a77da8
commit bb140c20cf
6 changed files with 135 additions and 86 deletions

View File

@ -23,9 +23,11 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/** Prevent direct access to this script */
defined('MOODLE_INTERNAL') || die();
/** Include required files */
require_once($CFG->dirroot.'/question/type/ordering/question.php');
/**
* Ordering editing form definition
@ -42,13 +44,6 @@ class qtype_ordering_edit_form extends question_edit_form {
const NUM_ANS_MIN = 3;
const NUM_ANS_ADD = 3;
const ABSOLUTE_POSITION = 0;
const RELATIVE_NEXT_EXCLUDE_LAST = 1;
const RELATIVE_NEXT_INCLUDE_LAST = 2;
const RELATIVE_ONE_PREVIOUS_AND_NEXT = 3;
const RELATIVE_ALL_PREVIOUS_AND_NEXT = 4;
const LONGEST_ORDERED_SUBSET = 5;
// this functionality is currently disabled
// because it is not fully functional
protected $use_editor_for_answers = true;
@ -73,10 +68,7 @@ class qtype_ordering_edit_form extends question_edit_form {
// layouttype
$name = 'layouttype';
$label = get_string($name, $plugin);
$options = array(
0 => get_string('vertical', $plugin),
1 => get_string('horizontal', $plugin)
);
$options = qtype_ordering_question::get_layout_types();
$mform->addElement('select', $name, $label, $options);
$mform->addHelpButton($name, $name, $plugin);
$mform->setDefault($name, 0);
@ -84,11 +76,7 @@ class qtype_ordering_edit_form extends question_edit_form {
// selecttype
$name = 'selecttype';
$label = get_string($name, $plugin);
$options = array(
0 => get_string('selectall', $plugin),
1 => get_string('selectrandom', $plugin),
2 => get_string('selectcontiguous', $plugin)
);
$options = qtype_ordering_question::get_select_types();
$mform->addElement('select', $name, $label, $options);
$mform->addHelpButton($name, $name, $plugin);
$mform->setDefault($name, 0);
@ -108,14 +96,7 @@ class qtype_ordering_edit_form extends question_edit_form {
// gradingtype
$name = 'gradingtype';
$label = get_string($name, $plugin);
$options = array(
self::ABSOLUTE_POSITION => get_string('absoluteposition', $plugin),
self::RELATIVE_NEXT_EXCLUDE_LAST => get_string('relativenextexcludelast', $plugin),
self::RELATIVE_NEXT_INCLUDE_LAST => get_string('relativenextincludelast', $plugin),
self::RELATIVE_ONE_PREVIOUS_AND_NEXT => get_string('relativeonepreviousandnext', $plugin),
self::RELATIVE_ALL_PREVIOUS_AND_NEXT => get_string('relativeallpreviousandnext', $plugin),
self::LONGEST_ORDERED_SUBSET => get_string('longestorderedsubset', $plugin)
);
$options = qtype_ordering_question::get_grading_types();
$mform->addElement('select', $name, $label, $options);
$mform->addHelpButton($name, $name, $plugin);
$mform->setDefault($name, 0);
@ -315,14 +296,14 @@ class qtype_ordering_edit_form extends question_edit_form {
if (isset($question->options->layouttype)) {
$question->layouttype = $question->options->layouttype;
} else {
$question->layouttype = 0;
$question->layouttype = qtype_ordering_question::LAYOUT_VERTICAL;
}
// selecttype
if (isset($question->options->selecttype)) {
$question->selecttype = $question->options->selecttype;
} else {
$question->selecttype = 0;
$question->selecttype = qtype_ordering_question::SELECT_ALL;
}
// selectcount
@ -336,7 +317,7 @@ class qtype_ordering_edit_form extends question_edit_form {
if (isset($question->options->gradingtype)) {
$question->gradingtype = $question->options->gradingtype;
} else {
$question->gradingtype = 0;
$question->gradingtype = qtype_ordering_question::GRADING_ABSOLUTE_POSITION;
}
return $question;

View File

@ -41,10 +41,14 @@ $string['gradingtype_help'] = 'Choose the type of grading calculation.
: An item is considered correct if it is preceded by all the same items as it is in the correct answer, and it is followed by all the same items as it is in the correct answer. The order of the previous items does not matter, and nor does the order of the following items. Thus, if ***n*** items are displayed to the student, the number of marks available for each item is ***(n - 1)***, and the highest mark availabe for the question is ***n x (n - 1)***, which is the same as ***( - n)***.
**Longest ordered subset**
: The grade is the number of items in the longest ordered subset of items. The highest possible grade is the same as the number of items displayed. A subset must have at least two items. Subsets do not need to start at the first item (but they can) and they do not need to be contiguous (but they can be). Where there are multiple subsets of equal length, items in the subset that is found first, when searching from left to right, will be displayed as correct. Other items will be marked as incorrect.';
: The grade is the number of items in the longest ordered subset of items. The highest possible grade is the same as the number of items displayed. A subset must have at least two items. Subsets do not need to start at the first item (but they can) and they do not need to be contiguous (but they can be). Where there are multiple subsets of equal length, items in the subset that is found first, when searching from left to right, will be displayed as correct. Other items will be marked as incorrect.
**Longest contiguous subset**
: The grade is the number of items in the longest contiguous subset of items. The highest possible grade is the same as the number of items displayed. A subset must have at least two items. Subsets do not need to start at the first item (but they can) and they MUST BE CONTIGUOUS. Where there are multiple subsets of equal length, items in the subset that is found first, when searching from left to right, will be displayed as correct. Other items will be marked as incorrect.';
$string['horizontal'] = 'Horizontal';
$string['layouttype_help'] = 'Choose whether to display the items vertically or horizontally.';
$string['layouttype'] = 'Layout of items';
$string['longestcontiguoussubset'] = 'Longest contiguous subset';
$string['longestorderedsubset'] = 'Longest ordered subset';
$string['noresponsedetails'] = 'Sorry, no details of the response to this question are available.';
$string['noscore'] = 'No score';

View File

@ -23,6 +23,7 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/** Prevent direct access to this script */
defined('MOODLE_INTERNAL') || die();
/**
@ -33,6 +34,21 @@ defined('MOODLE_INTERNAL') || die();
*/
class qtype_ordering_question extends question_graded_automatically {
const SELECT_ALL = 0;
const SELECT_RANDOM = 1;
const SELECT_CONTIGUOUS = 2;
const LAYOUT_VERTICAL = 0;
const LAYOUT_HORIZONTAL = 1;
const GRADING_ABSOLUTE_POSITION = 0;
const GRADING_RELATIVE_NEXT_EXCLUDE_LAST = 1;
const GRADING_RELATIVE_NEXT_INCLUDE_LAST = 2;
const GRADING_RELATIVE_ONE_PREVIOUS_AND_NEXT = 3;
const GRADING_RELATIVE_ALL_PREVIOUS_AND_NEXT = 4;
const GRADING_LONGEST_ORDERED_SUBSET = 5;
const GRADING_LONGEST_CONTIGUOUS_SUBSET = 6;
/** fields from "qtype_ordering_options" */
public $correctfeedback;
public $correctfeedbackformat;
@ -156,7 +172,7 @@ class qtype_ordering_question extends question_graded_automatically {
$options = $this->get_ordering_options();
switch ($options->gradingtype) {
case 0: // ABSOLUTE_POSITION
case self::GRADING_ABSOLUTE_POSITION:
$correctresponse = $this->correctresponse;
$currentresponse = $this->currentresponse;
foreach ($correctresponse as $position => $answerid) {
@ -169,8 +185,8 @@ class qtype_ordering_question extends question_graded_automatically {
}
break;
case 1: // RELATIVE_NEXT_EXCLUDE_LAST
case 2: // RELATIVE_NEXT_INCLUDE_LAST
case self::GRADING_RELATIVE_NEXT_EXCLUDE_LAST:
case self::GRADING_RELATIVE_NEXT_INCLUDE_LAST:
$currentresponse = $this->get_next_answerids($this->currentresponse, ($options->gradingtype==2));
$correctresponse = $this->get_next_answerids($this->correctresponse, ($options->gradingtype==2));
foreach ($correctresponse as $thisanswerid => $nextanswerid) {
@ -183,8 +199,8 @@ class qtype_ordering_question extends question_graded_automatically {
}
break;
case 3: // RELATIVE_ONE_PREVIOUS_AND_NEXT
case 4: // RELATIVE_ALL_PREVIOUS_AND_NEXT
case self::GRADING_RELATIVE_ONE_PREVIOUS_AND_NEXT:
case self::GRADING_RELATIVE_ALL_PREVIOUS_AND_NEXT:
$currentresponse = $this->get_previous_and_next_answerids($this->currentresponse, ($options->gradingtype==4));
$correctresponse = $this->get_previous_and_next_answerids($this->correctresponse, ($options->gradingtype==4));
foreach ($correctresponse as $thisanswerid => $answerids) {
@ -201,8 +217,9 @@ class qtype_ordering_question extends question_graded_automatically {
}
break;
case 5: // LONGEST_ORDERED_SUBSET
$subset = $this->get_ordered_subset();
case self::GRADING_LONGEST_ORDERED_SUBSET:
case self::GRADING_LONGEST_CONTIGUOUS_SUBSET:
$subset = $this->get_ordered_subset($options->gradingtype==5);
$countcorrect = count($subset);
$countanswers = count($this->currentresponse);
break;
@ -349,10 +366,10 @@ class qtype_ordering_question extends question_graded_automatically {
return $prevnextanswerids;
}
public function get_ordered_subset() {
public function get_ordered_subset($contiguous) {
$positions = $this->get_ordered_positions($this->correctresponse,
$this->currentresponse);
$subsets = $this->get_ordered_subsets($positions, count($positions));
$subsets = $this->get_ordered_subsets($positions, $contiguous, count($positions));
$countcorrect = 1; // i.e. ignore single item subsets
$subsetcorrect = array();
foreach ($subsets as $subset) {
@ -373,7 +390,7 @@ class qtype_ordering_question extends question_graded_automatically {
return $positions;
}
public function get_ordered_subsets($positions, $i_max, $i_min=0, $previous=-1) {
public function get_ordered_subsets($positions, $contiguous, $i_max, $i_min=0, $previous=-1) {
// $subsets is the collection of all subsets within $positions
$subsets = array();
@ -394,18 +411,27 @@ class qtype_ordering_question extends question_graded_automatically {
if ($current > ($previous + 1)) {
// fetch all the subsets in the tail of $positions,
// and prepend $subset-so-far onto each tail subset
$tailsets = $this->get_ordered_subsets($positions, $i_max, $i+1, $previous);
$tailsets = $this->get_ordered_subsets($positions, $contiguous, $i_max, $i+1, $previous);
foreach ($tailsets as $tailset) {
$subsets[] = array_merge($subset, $tailset);
if ($contiguous) {
// add this tail subset
$subsets[] = $tailset;
} else {
// prepend $subset-so-far to each tail subset
$subsets[] = array_merge($subset, $tailset);
}
}
}
// add $i to the main subset
$subset[] = $i;
// decide if we want to add this $i(tem) to the main $subset
if ($contiguous==false || $previous < 0 || $current==($previous + 1)) {
// update the $previous value
$previous = $current;
// add $i to the main subset
$subset[] = $i;
// update the $previous value
$previous = $current;
}
}
if (count($subset)) {
// put the main $subset first
@ -413,4 +439,47 @@ class qtype_ordering_question extends question_graded_automatically {
}
return $subsets;
}
static public function get_types($types, $type) {
if ($type===null) {
return $types; // return all $types
}
if (array_key_exists($type, $types)) {
return $types[$type]; // one $type
}
return $type; // shouldn't happen !!
}
static public function get_select_types($type=null) {
$plugin = 'qtype_ordering';
$types = array(
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);
}
static public function get_layout_types($type=null) {
$plugin = 'qtype_ordering';
$types = array(
self::LAYOUT_VERTICAL => get_string('vertical', $plugin),
self::LAYOUT_HORIZONTAL => get_string('horizontal', $plugin)
);
return self::get_types($types, $type);
}
static public function get_grading_types($type=null) {
$plugin = 'qtype_ordering';
$types = array(
self::GRADING_ABSOLUTE_POSITION => get_string('absoluteposition', $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);
}
}

View File

@ -26,9 +26,9 @@
defined('MOODLE_INTERNAL') || die();
if (class_exists('question_type')) {
$register_question_type = false;
$register_ordering_questiontype = false;
} else {
$register_question_type = true; // Moodle 2.0
$register_ordering_questiontype = true; // Moodle 2.0
require_once($CFG->dirroot.'/question/type/ordering/legacy/questiontypebase.php');
}
@ -671,8 +671,9 @@ class qtype_ordering extends question_type {
}
}
if ($register_question_type) {
if ($register_ordering_questiontype) {
class question_ordering_qtype extends qtype_ordering {
}
question_register_questiontype(new question_ordering_qtype());
}
unset($register_ordering_questiontype);

View File

@ -24,6 +24,7 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/** Prevent direct access to this script */
defined('MOODLE_INTERNAL') || die();
if (! class_exists('qtype_with_combined_feedback_renderer')) { // Moodle 2.0
@ -64,9 +65,9 @@ class qtype_ordering_renderer extends qtype_with_combined_feedback_renderer {
$ablock_id = 'id_ablock_'.$question->id;
switch ($question->options->layouttype) {
case 0 : $axis = 'y'; break; // vertical
case 1 : $axis = ''; break; // horizontal
default: $axis = ''; // unknown
case qtype_ordering_question::LAYOUT_VERTICAL : $axis = 'y'; break;
case qtype_ordering_question::LAYOUT_HORIZONTAL : $axis = ''; break;
default: $axis = '';
}
$result = '';
@ -178,14 +179,8 @@ class qtype_ordering_renderer extends qtype_with_combined_feedback_renderer {
$question = $qa->get_question();
// fetch grading type
switch ($question->options->gradingtype) {
case 0: $gradingtype = get_string('absoluteposition', $plugin); break;
case 1: $gradingtype = get_string('relativenextexcludelast', $plugin); break;
case 2: $gradingtype = get_string('relativenextincludelast', $plugin); break;
case 3: $gradingtype = get_string('relativeonepreviousandnext', $plugin); break;
case 4: $gradingtype = get_string('relativeallpreviousandnext', $plugin); break;
case 5: $gradingtype = get_string('longestorderedsubset', $plugin); break;
}
$gradingtype = $question->options->gradingtype;
$gradingtype = qtype_ordering_question::get_grading_types($gradingtype);
// format grading type, e.g. Grading type: Relative to next item, excluding last item
if ($gradingtype) {
@ -276,35 +271,32 @@ class qtype_ordering_renderer extends qtype_with_combined_feedback_renderer {
/////////////////////////////////////
protected function get_response_info($question) {
switch ($question->options->gradingtype) {
case 0: // ABSOLUTE
$gradingtype = $question->options->gradingtype;
switch ($gradingtype) {
case qtype_ordering_question::GRADING_ABSOLUTE_POSITION:
$this->correctinfo = $question->correctresponse;
$this->currentinfo = $question->currentresponse;
break;
case 1: // RELATIVE_NEXT_EXCLUDE_LAST
$this->correctinfo = $question->get_next_answerids($question->correctresponse, false);
$this->currentinfo = $question->get_next_answerids($question->currentresponse, false);
break;
case 2: // RELATIVE_NEXT_INCLUDE_LAST
$this->correctinfo = $question->get_next_answerids($question->correctresponse, true);
$this->currentinfo = $question->get_next_answerids($question->currentresponse, true);
case qtype_ordering_question::GRADING_RELATIVE_NEXT_EXCLUDE_LAST:
case qtype_ordering_question::GRADING_RELATIVE_NEXT_INCLUDE_LAST:
$this->correctinfo = $question->get_next_answerids($question->correctresponse, $gradingtype==2);
$this->currentinfo = $question->get_next_answerids($question->currentresponse, $gradingtype==2);
break;
case 3: // RELATIVE_ONE_PREVIOUS_AND_NEXT
$this->correctinfo = $question->get_previous_and_next_answerids($question->correctresponse, false);
$this->currentinfo = $question->get_previous_and_next_answerids($question->currentresponse, false);
case qtype_ordering_question::GRADING_RELATIVE_ONE_PREVIOUS_AND_NEXT:
case qtype_ordering_question::GRADING_RELATIVE_ALL_PREVIOUS_AND_NEXT:
$this->correctinfo = $question->get_previous_and_next_answerids($question->correctresponse, $gradingtype==4);
$this->currentinfo = $question->get_previous_and_next_answerids($question->currentresponse, $gradingtype==4);
break;
case 4: // RELATIVE_ALL_PREVIOUS_AND_NEXT
$this->correctinfo = $question->get_previous_and_next_answerids($question->correctresponse, true);
$this->currentinfo = $question->get_previous_and_next_answerids($question->currentresponse, true);
break;
case 5: // LONGEST_ORDERED_SUBSET
case qtype_ordering_question::GRADING_LONGEST_ORDERED_SUBSET:
case qtype_ordering_question::GRADING_LONGEST_CONTIGUOUS_SUBSET:
$this->correctinfo = $question->correctresponse;
$this->currentinfo = $question->currentresponse;
$subset = $question->get_ordered_subset();
$subset = $question->get_ordered_subset($gradingtype==6);
foreach ($this->currentinfo as $position => $answerid) {
if (array_search($position, $subset)===false) {
$this->currentinfo[$position] = 0;
@ -336,7 +328,7 @@ class qtype_ordering_renderer extends qtype_with_combined_feedback_renderer {
switch ($question->options->gradingtype) {
case 0: // ABSOLUTE
case qtype_ordering_question::GRADING_ABSOLUTE_POSITION:
if (isset($correctinfo[$position])) {
if ($correctinfo[$position]==$answerid) {
$score = 1;
@ -345,8 +337,8 @@ class qtype_ordering_renderer extends qtype_with_combined_feedback_renderer {
}
break;
case 1; // RELATIVE_NEXT_EXCLUDE_LAST
case 2; // RELATIVE_NEXT_INCLUDE_LAST
case qtype_ordering_question::GRADING_RELATIVE_NEXT_EXCLUDE_LAST:
case qtype_ordering_question::GRADING_RELATIVE_NEXT_INCLUDE_LAST:
if (isset($correctinfo[$answerid])) {
if (isset($currentinfo[$answerid]) && $currentinfo[$answerid]==$correctinfo[$answerid]) {
$score = 1;
@ -355,8 +347,8 @@ class qtype_ordering_renderer extends qtype_with_combined_feedback_renderer {
}
break;
case 3; // RELATIVE_ONE_PREVIOUS_AND_NEXT
case 4; // RELATIVE_ALL_PREVIOUS_AND_NEXT
case qtype_ordering_question::GRADING_RELATIVE_ONE_PREVIOUS_AND_NEXT:
case qtype_ordering_question::GRADING_RELATIVE_ALL_PREVIOUS_AND_NEXT:
if (isset($correctinfo[$answerid])) {
$maxscore = 0;
$prev = $correctinfo[$answerid]->prev;
@ -370,7 +362,8 @@ class qtype_ordering_renderer extends qtype_with_combined_feedback_renderer {
}
break;
case 5: // LONGEST_ORDERED_SUBSET
case qtype_ordering_question::GRADING_LONGEST_ORDERED_SUBSET:
case qtype_ordering_question::GRADING_LONGEST_CONTIGUOUS_SUBSET:
if (isset($correctinfo[$position])) {
if (isset($currentinfo[$position])) {
$score = $currentinfo[$position];
@ -383,6 +376,7 @@ class qtype_ordering_renderer extends qtype_with_combined_feedback_renderer {
if ($maxscore===null) {
// an unscored item is either an illegal item
// or last item of RELATIVE_NEXT_EXCLUDE_LAST
// or an item from an unrecognized grading type
$class = 'unscored';
} else {
if ($maxscore==0) {

View File

@ -31,5 +31,5 @@ $plugin->cron = 0;
$plugin->component = 'qtype_ordering';
$plugin->maturity = MATURITY_STABLE;
$plugin->requires = 2010112400; // Moodle 2.0
$plugin->version = 2016011139;
$plugin->release = '2016-01-11 (39)';
$plugin->version = 2016012340;
$plugin->release = '2016-01-23 (40)';