mirror of
https://github.com/moodle/moodle.git
synced 2025-01-18 05:58:34 +01:00
MDL-79863 qtype_ordering: qtype/ordering remember ordering question state when moving between pages in a quiz
This commit is contained in:
parent
1cbc814afa
commit
dd5db2f3c0
@ -50,7 +50,7 @@ class backup_qtype_ordering_plugin extends backup_qtype_plugin {
|
||||
$this->add_question_question_answers($pluginwrapper);
|
||||
|
||||
// Now create the qtype own structures
|
||||
$fields = array('logical', 'studentsee',
|
||||
$fields = array('selecttype', 'selectcount',
|
||||
'correctfeedback', 'correctfeedbackformat',
|
||||
'incorrectfeedback', 'incorrectfeedbackformat',
|
||||
'partiallycorrectfeedback', 'partiallycorrectfeedbackformat');
|
||||
|
@ -4,12 +4,15 @@
|
||||
<TABLE NAME="question_ordering" COMMENT="Options for ordering questions">
|
||||
<FIELDS>
|
||||
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="question"/>
|
||||
<FIELD NAME="question" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="logical"/>
|
||||
<FIELD NAME="logical" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="question" NEXT="studentsee"/>
|
||||
<FIELD NAME="studentsee" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="logical" NEXT="correctfeedback"/>
|
||||
<FIELD NAME="correctfeedback" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" PREVIOUS="studentsee" NEXT="partiallycorrectfeedback"/>
|
||||
<FIELD NAME="partiallycorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" PREVIOUS="correctfeedback" NEXT="incorrectfeedback"/>
|
||||
<FIELD NAME="incorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" PREVIOUS="partiallycorrectfeedback"/>
|
||||
<FIELD NAME="question" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="selecttype"/>
|
||||
<FIELD NAME="selecttype" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="question" NEXT="selectcount"/>
|
||||
<FIELD NAME="selectcount" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="selecttype" NEXT="correctfeedback"/>
|
||||
<FIELD NAME="correctfeedback" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" PREVIOUS="selectcount" NEXT="correctfeedbackformat"/>
|
||||
<FIELD NAME="correctfeedbackformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="correctfeedback" NEXT="incorrectfeedback"/>
|
||||
<FIELD NAME="incorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" PREVIOUS="correctfeedbackformat" NEXT="incorrectfeedbackformat"/>
|
||||
<FIELD NAME="incorrectfeedbackformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="incorrectfeedback" NEXT="partiallycorrectfeedback"/>
|
||||
<FIELD NAME="partiallycorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" PREVIOUS="incorrectfeedbackformat" NEXT="partiallycorrectfeedbackformat"/>
|
||||
<FIELD NAME="partiallycorrectfeedbackformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="partiallycorrectfeedback"/>
|
||||
</FIELDS>
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="Primary key for question_ordering" NEXT="question"/>
|
||||
|
@ -61,15 +61,15 @@ function xmldb_qtype_ordering_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, $newversion, 'qtype', 'ordering');
|
||||
}
|
||||
|
||||
$newversion = 2015011408;
|
||||
$newversion = 2015011711;
|
||||
if ($oldversion < $newversion) {
|
||||
|
||||
// rename "ordering" table for Moodle >= 2.5
|
||||
$oldname = 'question_ordering';
|
||||
$newname = 'qtype_ordering_options';
|
||||
|
||||
$oldtable = new xmldb_table($oldname);
|
||||
if ($dbman->table_exists($oldtable)) {
|
||||
if ($dbman->table_exists($oldname)) {
|
||||
$oldtable = new xmldb_table($oldname);
|
||||
if ($dbman->table_exists($newname)) {
|
||||
$dbman->drop_table($oldtable);
|
||||
} else {
|
||||
@ -90,13 +90,18 @@ function xmldb_qtype_ordering_upgrade($oldversion) {
|
||||
}
|
||||
}
|
||||
|
||||
// add "feedbackformat" fields
|
||||
// rename "question" -> "questionid"
|
||||
// rename "logical" -> "selecttype"
|
||||
// rename "studentsee" -> "selectcount"
|
||||
// add "(xxx)feedbackformat" fields
|
||||
$table = new xmldb_table('qtype_ordering_options');
|
||||
$fields = array(
|
||||
'questionid' => new xmldb_field('question', XMLDB_TYPE_INTEGER, '11', null, XMLDB_NOTNULL, null, '0', 'id'),
|
||||
'correctfeedbackformat' => new xmldb_field('correctfeedbackformat', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0', 'correctfeedback'),
|
||||
'incorrectfeedbackformat' => new xmldb_field('incorrectfeedbackformat', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0', 'incorrectfeedback'),
|
||||
'partiallycorrectfeedbackformat' => new xmldb_field('partiallycorrectfeedbackformat', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0', 'partiallycorrectfeedback')
|
||||
'selecttype' => new xmldb_field('logical', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0', 'questionid'),
|
||||
'selectcount' => new xmldb_field('studentsee', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0', 'selecttype'),
|
||||
'correctfeedbackformat' => new xmldb_field('correctfeedbackformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'correctfeedback'),
|
||||
'incorrectfeedbackformat' => new xmldb_field('incorrectfeedbackformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'incorrectfeedback'),
|
||||
'partiallycorrectfeedbackformat' => new xmldb_field('partiallycorrectfeedbackformat', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'partiallycorrectfeedback')
|
||||
);
|
||||
foreach ($fields as $newname => $field) {
|
||||
$oldexists = $dbman->field_exists($table, $field);
|
||||
|
@ -36,8 +36,10 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*/
|
||||
class qtype_ordering_edit_form extends question_edit_form {
|
||||
|
||||
const NUM_ANS_START = 10;
|
||||
const NUM_ANS_ADD = 5;
|
||||
const NUM_ANS_ROWS = 2;
|
||||
const NUM_ANS_COLS = 60;
|
||||
const NUM_ANS_START = 6;
|
||||
const NUM_ANS_ADD = 2;
|
||||
|
||||
public function qtype() {
|
||||
return 'ordering';
|
||||
@ -50,40 +52,56 @@ class qtype_ordering_edit_form extends question_edit_form {
|
||||
*/
|
||||
public function definition_inner($mform) {
|
||||
|
||||
// logical
|
||||
$name = 'logical';
|
||||
$label = get_string('logicalpossibilities', 'qtype_ordering');
|
||||
// cache this plugins name
|
||||
$plugin = 'qtype_ordering';
|
||||
|
||||
// selecttype
|
||||
$name = 'selecttype';
|
||||
$label = get_string($name, $plugin);
|
||||
$options = array(
|
||||
0 => get_string('exactorder', 'qtype_ordering'), // = all ?
|
||||
1 => get_string('relativeorder', 'qtype_ordering'), // = random subset
|
||||
2 => get_string('contiguous', 'qtype_ordering') // = contiguous subset
|
||||
0 => get_string('selectall', $plugin),
|
||||
1 => get_string('selectrandom', $plugin),
|
||||
2 => get_string('selectcontiguous', $plugin)
|
||||
);
|
||||
$mform->addElement('select', $name, $label, $options);
|
||||
$mform->addHelpButton($name, $name, $plugin);
|
||||
$mform->setDefault($name, 0);
|
||||
|
||||
// studentsee
|
||||
$name = 'studentsee';
|
||||
$label = get_string('itemsforstudent', 'qtype_ordering');
|
||||
// selectcount
|
||||
$name = 'selectcount';
|
||||
$label = get_string($name, $plugin);
|
||||
$options = array(0 => get_string('all'));
|
||||
for ($i=3; $i <= 20; $i++) {
|
||||
$options[] = $i;
|
||||
$options[$i] = $i;
|
||||
}
|
||||
$mform->addElement('select', $name, $label, $options);
|
||||
$mform->setDefault('studentsee', 0);
|
||||
$mform->disabledIf($name, 'selecttype', 'eq', 0);
|
||||
$mform->addHelpButton($name, $name, $plugin);
|
||||
$mform->setDefault($name, 0);
|
||||
|
||||
// answers
|
||||
// answers (=items)
|
||||
$elements = array();
|
||||
$elements[] =& $mform->createElement('header', 'choicehdr', get_string('choiceno', 'qtype_ordering', '{no}'));
|
||||
$elements[] =& $mform->createElement('textarea', 'answer', get_string('answer', 'qtype_ordering'), 'rows="3" cols="50"');
|
||||
|
||||
$name = 'answerheader';
|
||||
$label = get_string($name, $plugin);
|
||||
$elements[] =& $mform->createElement('header', $name, $label);
|
||||
|
||||
$name = 'answer';
|
||||
$label = get_string($name, $plugin);
|
||||
$options = array('rows' => self::NUM_ANS_ROWS, 'cols' => self::NUM_ANS_COLS);
|
||||
$elements[] =& $mform->createElement('textarea', $name, $label, $options);
|
||||
|
||||
if (empty($this->question->options)){
|
||||
$count = 0;
|
||||
$start = 0;
|
||||
} else {
|
||||
$count = count($this->question->options->answers);
|
||||
$start = count($this->question->options->answers);
|
||||
}
|
||||
$start = max(self::NUM_ANS_START, $count + self::NUM_ANS_ADD);
|
||||
$options = array('fraction' => array('default' => 0));
|
||||
$buttontext = get_string('addmoreanswers', 'qtype_ordering');
|
||||
$this->repeat_elements($elements, $start, $options, 'noanswers', 'addanswers', self::NUM_ANS_ADD, $buttontext);
|
||||
if ($start < self::NUM_ANS_START) {
|
||||
$start = self::NUM_ANS_START;
|
||||
}
|
||||
$options = array('answerheader' => array('expanded' => true));
|
||||
$buttontext = get_string('addmoreanswers', $plugin, self::NUM_ANS_ADD);
|
||||
$this->repeat_elements($elements, $start, $options, 'countanswers', 'addanswers', self::NUM_ANS_ADD, $buttontext);
|
||||
|
||||
// feedback
|
||||
$this->add_ordering_feedback_fields();
|
||||
@ -113,18 +131,18 @@ class qtype_ordering_edit_form extends question_edit_form {
|
||||
|
||||
}
|
||||
|
||||
// logical
|
||||
if (isset($question->options->logical)) {
|
||||
$question->logical = $question->options->logical;
|
||||
// selecttype
|
||||
if (isset($question->options->selecttype)) {
|
||||
$question->selecttype = $question->options->selecttype;
|
||||
} else {
|
||||
$question->logical = 0;
|
||||
$question->selecttype = 0;
|
||||
}
|
||||
|
||||
// studentsee
|
||||
if (isset($question->options->studentsee)) {
|
||||
$question->studentsee = $question->options->studentsee;
|
||||
// selectcount
|
||||
if (isset($question->options->selectcount)) {
|
||||
$question->selectcount = $question->options->selectcount;
|
||||
} else {
|
||||
$question->studentsee = max(3, count($question->answer));
|
||||
$question->selectcount = max(3, count($question->answer));
|
||||
}
|
||||
|
||||
return $question;
|
||||
@ -132,6 +150,7 @@ class qtype_ordering_edit_form extends question_edit_form {
|
||||
|
||||
public function validation($data, $files) {
|
||||
$errors = array();
|
||||
$plugin = 'qtype_ordering';
|
||||
|
||||
$answercount = 0;
|
||||
foreach ($data['answer'] as $answer){
|
||||
@ -142,8 +161,8 @@ class qtype_ordering_edit_form extends question_edit_form {
|
||||
}
|
||||
|
||||
switch ($answercount) {
|
||||
case 0: $errors['answer[0]'] = get_string('notenoughanswers', 'qtype_ordering', 2);
|
||||
case 1: $errors['answer[1]'] = get_string('notenoughanswers', 'qtype_ordering', 2);
|
||||
case 0: $errors['answer[0]'] = get_string('notenoughanswers', $plugin, 2);
|
||||
case 1: $errors['answer[1]'] = get_string('notenoughanswers', $plugin, 2);
|
||||
}
|
||||
|
||||
return $errors;
|
||||
|
@ -15,35 +15,25 @@ $string['pluginnameadding'] = 'Adding an Ordering question';
|
||||
$string['pluginnameediting'] = 'Editing an Ordering question';
|
||||
$string['pluginnamesummary'] = 'Put jumbled items into a meaningful order.';
|
||||
|
||||
$string['editingordering'] = 'Editing ordering';
|
||||
$string['addingordering'] = 'Adding a Ordering';
|
||||
$string['orderingsummary'] = 'Ordering summary';
|
||||
|
||||
$string['ordering'] = 'Ordering';
|
||||
$string['exactorder'] = 'Exact order';
|
||||
$string['relativeorder'] = 'Relative order';
|
||||
$string['contiguous'] = 'Contiguous';
|
||||
|
||||
$string['logicalpossibilities'] = 'Logical possibilities';
|
||||
$string['addmoreanswers'] = 'Blank for {no} more answers';
|
||||
|
||||
$string['choiceno'] = 'Answer {$a}';
|
||||
$string['choices'] = 'Available answers';
|
||||
$string['answer'] = 'Answer';
|
||||
|
||||
$string['itemsforstudent'] = 'How many items the student will see';
|
||||
|
||||
$string['addmoreanswers'] = 'Add {$a} more items';
|
||||
$string['answer'] = 'Item text';
|
||||
$string['answerheader'] = 'Draggable item {no}';
|
||||
$string['correctorder'] = 'The correct order for these items is as follows:';
|
||||
$string['defaultquestionname'] = 'Drag the following items into the correct order.';
|
||||
$string['noresponsedetails'] = 'Sorry, no details of the response to this question are available.';
|
||||
$string['notenoughanswers'] = 'Ordering questions must have more than {$a} answers.';
|
||||
$string['selectall'] = 'Select all items';
|
||||
$string['selectcontiguous'] = 'Select a contiguous subset of items';
|
||||
$string['selectcount_help'] = 'The number of items that will be displayed when the question is appears in a quiz.';
|
||||
$string['selectcount'] = 'Size of subset';
|
||||
$string['selectrandom'] = 'Select a random subset of items';
|
||||
$string['selecttype_help'] = 'Choose whether to display all the items or a subset of the items.';
|
||||
$string['selecttype'] = 'Item selection type';
|
||||
|
||||
//$string['correctfeedback'] = 'Correct answer';
|
||||
//$string['incorrectfeedback'] = 'Incorrect answer';
|
||||
//$string['overallfeedback'] = 'Overall Feedback';
|
||||
//$string['partiallycorrectfeedback'] = 'Partially correct answer';
|
||||
$string['notenoughanswers'] = 'not enough answers';
|
||||
|
||||
$string['comment'] = 'Drag and drop the items into the correct order.';
|
||||
|
||||
// TODO add any other requred strings.
|
||||
$string['ordering_help'] = 'Ordering help';
|
||||
$string['defaultquestionname'] = 'Put the following events into the correct order.';
|
||||
// requred strings for Moodle 2.0
|
||||
$string['addingordering'] = $string['pluginnameadding'];
|
||||
$string['editingordering'] = $string['pluginnameediting'];
|
||||
$string['ordering_help'] = $string['pluginname_help'];
|
||||
$string['ordering_link'] = $string['pluginname_link'];
|
||||
$string['ordering'] = $string['pluginname'];
|
||||
$string['orderingsummary'] = $string['pluginnamesummary'];
|
||||
|
@ -27,7 +27,7 @@
|
||||
* @subpackage questiontypes
|
||||
*/
|
||||
|
||||
require_once($CFG->libdir . '/questionlib.php');
|
||||
require_once($CFG->libdir.'/questionlib.php');
|
||||
|
||||
/**
|
||||
* This is the base class for Moodle question types.
|
||||
@ -46,6 +46,7 @@ require_once($CFG->libdir . '/questionlib.php');
|
||||
* @subpackage questiontypes
|
||||
*/
|
||||
class question_type extends default_questiontype {
|
||||
|
||||
protected $fileoptions = array(
|
||||
'subdirs' => false,
|
||||
'maxfiles' => -1,
|
||||
@ -708,17 +709,12 @@ class question_type extends default_questiontype {
|
||||
*/
|
||||
function get_all_responses(&$question, &$state) {
|
||||
if (isset($question->options->answers) && is_array($question->options->answers)) {
|
||||
$answers = array();
|
||||
foreach ($question->options->answers as $aid=>$answer) {
|
||||
$r = new stdClass;
|
||||
$r->answer = $answer->answer;
|
||||
$r->credit = $answer->fraction;
|
||||
$answers[$aid] = $r;
|
||||
$responses = array();
|
||||
foreach ($question->options->answers as $id => $answer) {
|
||||
$responses[$id] = (object)array('answer' => $answer->answer,
|
||||
'credit' => $answer->fraction);
|
||||
}
|
||||
$result = new stdClass;
|
||||
$result->id = $question->id;
|
||||
$result->responses = $answers;
|
||||
return $result;
|
||||
return (object)array('id' => $question->id, 'responses' => $responses);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@ -1258,28 +1254,28 @@ class question_type extends default_questiontype {
|
||||
return; // shouldn't happen !!
|
||||
}
|
||||
|
||||
if ($question->options->studentsee==0) { // all items
|
||||
$question->options->studentsee = count($answers);
|
||||
if ($question->options->selectcount==0) { // all items
|
||||
$question->options->selectcount = count($answers);
|
||||
} else {
|
||||
// a nasty hack so that "studentsee" is the same
|
||||
// a nasty hack so that "selectcount" is the same
|
||||
// as what is displayed by edit_ordering_form.php
|
||||
$question->options->studentsee += 2;
|
||||
$question->options->selectcount += 2;
|
||||
}
|
||||
|
||||
switch ($question->options->logical) {
|
||||
switch ($question->options->selecttype) {
|
||||
|
||||
case 0: // all
|
||||
$answerids = array_keys($answers);
|
||||
break;
|
||||
|
||||
case 1: // random subset
|
||||
$answerids = array_rand($answers, $question->options->studentsee);
|
||||
$answerids = array_rand($answers, $question->options->selectcount);
|
||||
break;
|
||||
|
||||
case 2: // contiguous subset
|
||||
if (count($answers) > $question->options->studentsee) {
|
||||
$offset = mt_rand(0, count($answers) - $question->options->studentsee);
|
||||
$answers = array_slice($answers, $offset, $question->options->studentsee, true);
|
||||
if (count($answers) > $question->options->selectcount) {
|
||||
$offset = mt_rand(0, count($answers) - $question->options->selectcount);
|
||||
$answers = array_slice($answers, $offset, $question->options->selectcount, true);
|
||||
}
|
||||
$answerids = array_keys($answers);
|
||||
break;
|
||||
@ -1867,3 +1863,29 @@ class question_type extends default_questiontype {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class qtype_with_combined_feedback_renderer extends qtype_renderer {
|
||||
protected function combined_feedback(question_attempt $qa) {
|
||||
$question = $qa->get_question();
|
||||
|
||||
$state = $qa->get_state();
|
||||
|
||||
if (!$state->is_finished()) {
|
||||
$response = $qa->get_last_qt_data();
|
||||
if (!$qa->get_question()->is_gradable_response($response)) {
|
||||
return '';
|
||||
}
|
||||
list($notused, $state) = $qa->get_question()->grade_response($response);
|
||||
}
|
||||
|
||||
$feedback = '';
|
||||
$field = $state->get_feedback_class() . 'feedback';
|
||||
$format = $state->get_feedback_class() . 'feedbackformat';
|
||||
if ($question->$field) {
|
||||
$feedback .= $question->format_text($question->$field, $question->$format,
|
||||
$qa, 'question', $field, $question->id);
|
||||
}
|
||||
|
||||
return $feedback;
|
||||
}
|
||||
}
|
||||
|
@ -33,12 +33,78 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*/
|
||||
class qtype_ordering_question extends question_graded_automatically {
|
||||
|
||||
public $answers;
|
||||
public $options;
|
||||
public $rightanswer;
|
||||
/** fields from "qtype_ordering_options" */
|
||||
public $correctfeedback;
|
||||
public $correctfeedbackformat;
|
||||
public $incorrectfeedback;
|
||||
public $incorrectfeedbackformat;
|
||||
public $partiallycorrectfeedback;
|
||||
public $partiallycorrectfeedbackformat;
|
||||
|
||||
public function get_response_fieldname() {
|
||||
return 'response_'.$this->id;
|
||||
/** records from "question_answers" table */
|
||||
public $answers;
|
||||
|
||||
/** records from "qtype_ordering_options" table */
|
||||
public $options;
|
||||
|
||||
/** array of answerids in correct order */
|
||||
public $correctresponse;
|
||||
|
||||
/** array current order of answerids */
|
||||
public $currentresponse;
|
||||
|
||||
public function start_attempt(question_attempt_step $step, $variant) {
|
||||
$this->answers = $this->get_ordering_answers();
|
||||
$this->options = $this->get_ordering_options();
|
||||
|
||||
$countanswers = count($this->answers);
|
||||
|
||||
// sanitize "selecttype"
|
||||
$selecttype = $this->options->selecttype;
|
||||
$selecttype = max(0, $selecttype);
|
||||
$selecttype = min(2, $selecttype);
|
||||
|
||||
// sanitize "selectcount"
|
||||
$selectcount = $this->options->selectcount;
|
||||
$selectcount = max(3, $selectcount);
|
||||
$selectcount = min($countanswers, $selectcount);
|
||||
|
||||
// ensure consistency between "selecttype" and "selectcount"
|
||||
switch (true) {
|
||||
case ($selecttype==0): $selectcount = $countanswers; break;
|
||||
case ($selectcount==$countanswers): $selecttype = 0; break;
|
||||
}
|
||||
|
||||
// extract answer ids
|
||||
switch ($selecttype) {
|
||||
case 0: // all
|
||||
$answerids = array_keys($this->answers);
|
||||
break;
|
||||
|
||||
case 1: // random subset
|
||||
$answerids = array_rand($this->answers, $selectcount);
|
||||
break;
|
||||
|
||||
case 2: // contiguous subset
|
||||
$answerids = array_keys($this->answers);
|
||||
$offset = mt_rand(0, $countanswers - $selectcount);
|
||||
$answerids = array_slice($answerids, $offset, $selectcount, true);
|
||||
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->answers = $this->get_ordering_answers();
|
||||
$this->options = $this->get_ordering_options();
|
||||
$this->correctresponse = explode(',', $step->get_qt_var('_correctresponse'));
|
||||
$this->currentresponse = explode(',', $step->get_qt_var('_currentresponse'));
|
||||
}
|
||||
|
||||
public function format_questiontext($qa) {
|
||||
@ -52,24 +118,85 @@ class qtype_ordering_question extends question_graded_automatically {
|
||||
}
|
||||
|
||||
public function get_correct_response() {
|
||||
if ($this->rightanswer===null) {
|
||||
$this->rightanswer = $this->get_ordering_answers();
|
||||
$this->rightanswer = array_keys($this->rightanswer);
|
||||
$this->rightanswer = implode(',', $this->rightanswer);
|
||||
$correctresponse = $this->correctresponse;
|
||||
foreach ($correctresponse as $position => $answerid) {
|
||||
$answer = $this->answers[$answerid];
|
||||
$correctresponse[$position] = $answer->md5key;
|
||||
}
|
||||
return array('answer' => $this->rightanswer);
|
||||
$name = $this->get_response_fieldname();
|
||||
return array($name => implode(',', $correctresponse));
|
||||
}
|
||||
|
||||
public function summarise_response(array $response) {
|
||||
return '';
|
||||
}
|
||||
|
||||
public function classify_response(array $response) {
|
||||
if (array_key_exists('answer', $response)) {
|
||||
$responseclassid = ($response['answer'] ? 1 : 0);
|
||||
list($fraction) = $this->grade_response($response);
|
||||
return array($this->id => new question_classified_response($responseclassid, get_string('true', 'qtype_ordering'), $fraction));
|
||||
return array();
|
||||
}
|
||||
|
||||
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;
|
||||
$correctresponse = $this->correctresponse;
|
||||
$currentresponse = $this->currentresponse;
|
||||
foreach ($currentresponse as $position => $answerid) {
|
||||
if ($correctresponse[$position]==$answerid) {
|
||||
$countcorrect++;
|
||||
}
|
||||
$countanswers++;
|
||||
}
|
||||
if ($countanswers==0) {
|
||||
$fraction = 0;
|
||||
} else {
|
||||
return array($this->id => question_classified_response::no_response());
|
||||
$fraction = ($countcorrect / $countanswers);
|
||||
}
|
||||
return array($fraction, question_state::graded_state_for_fraction($fraction));
|
||||
}
|
||||
|
||||
public function check_file_access($qa, $options, $component, $filearea, $args, $forcedownload) {
|
||||
return parent::check_file_access($qa, $options, $component, $filearea, $args, $forcedownload);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// custom methods
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
public function get_response_fieldname() {
|
||||
return 'response_'.$this->id;
|
||||
}
|
||||
|
||||
public function update_current_response($response) {
|
||||
$name = $this->get_response_fieldname();
|
||||
if (isset($response[$name])) {
|
||||
$ids = explode(',', $response[$name]);
|
||||
foreach ($ids as $i => $id) {
|
||||
foreach ($this->answers as $answer) {
|
||||
if ($id==$answer->md5key) {
|
||||
$ids[$i] = $answer->id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->currentresponse = $ids;
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,17 +204,17 @@ class qtype_ordering_question extends question_graded_automatically {
|
||||
global $DB;
|
||||
if ($this->options===null) {
|
||||
$this->options = $DB->get_record('qtype_ordering_options', array('questionid' => $this->id));
|
||||
if ($this->options==false) {
|
||||
if (empty($this->options)) {
|
||||
$this->options = (object)array(
|
||||
'questionid' => $this->id,
|
||||
'logical' => 0, // require all answers
|
||||
'studentsee' => 0,
|
||||
'selecttype' => 0, // all answers
|
||||
'selectcount' => 0,
|
||||
'correctfeedback' => '',
|
||||
'correctfeedbackformat' => 0,
|
||||
'correctfeedbackformat' => FORMAT_MOODLE, // =0
|
||||
'incorrectfeedback' => '',
|
||||
'incorrectfeedbackformat' => 0,
|
||||
'incorrectfeedbackformat' => FORMAT_MOODLE, // =0
|
||||
'partiallycorrectfeedback' => '',
|
||||
'partiallycorrectfeedbackformat' => 0
|
||||
'partiallycorrectfeedbackformat' => FORMAT_MOODLE // =0
|
||||
);
|
||||
$this->options->id = $DB->insert_record('qtype_ordering', $options);
|
||||
}
|
||||
@ -114,135 +241,4 @@ class qtype_ordering_question extends question_graded_automatically {
|
||||
}
|
||||
return $this->answers;
|
||||
}
|
||||
|
||||
public function is_complete_response(array $response) {
|
||||
global $CFG, $DB, $SESSION;
|
||||
|
||||
$name = $this->get_response_fieldname();
|
||||
if (isset($response[$name])) {
|
||||
$responses = $response[$name];
|
||||
} else {
|
||||
$responses = optional_param($name, '', PARAM_TEXT);
|
||||
}
|
||||
$responses = preg_replace('[^a-zA-Z0-9,_-]', '', $responses);
|
||||
$responses = explode(',', $responses); // convert to array
|
||||
$responses = array_filter($responses); // remove blanks
|
||||
$responses = array_unique($responses); // remove duplicates
|
||||
|
||||
foreach ($responses as $i => $response) {
|
||||
if (substr($response, 0, 14)=='ordering_item_') {
|
||||
$responses[$i] = substr($response, 14);
|
||||
} else {
|
||||
unset($responses[$i]); // remove invalid response
|
||||
}
|
||||
}
|
||||
|
||||
$ordering = $this->get_ordering_options();
|
||||
$answers = $this->get_ordering_answers();
|
||||
|
||||
if ($ordering->logical==0) {
|
||||
$total = count($answers); // require all answers
|
||||
} else {
|
||||
$total = $ordering->studentsee + 2; // a subset of answers
|
||||
}
|
||||
|
||||
if (isset($CFG->passwordsaltmain)) {
|
||||
$salt = $CFG->passwordsaltmain;
|
||||
} else {
|
||||
$salt = ''; // complex_random_string()
|
||||
}
|
||||
|
||||
$validresponses = array();
|
||||
foreach ($answers as $answerid => $answer) {
|
||||
|
||||
$response = md5($salt.$answer->answer);
|
||||
$sortorder = intval($answer->fraction);
|
||||
|
||||
if (in_array($response, $responses)) {
|
||||
$answers[$answerid]->sortorder = $sortorder;
|
||||
$answers[$answerid]->response = $response;
|
||||
$validresponses[] = $response;
|
||||
} else {
|
||||
unset($answers[$answerid]); // this answer is not used
|
||||
}
|
||||
}
|
||||
|
||||
// convert $answers to sequentially numbered array
|
||||
$answers = array_values($answers);
|
||||
|
||||
// sort $answers by sortorder (not really necessary)
|
||||
usort($answers, array($this, 'usort_sortorder'));
|
||||
|
||||
// remove invalid responses
|
||||
foreach ($responses as $i => $response) {
|
||||
if (! in_array($response, $validresponses)) {
|
||||
unset($responses[$i]);
|
||||
}
|
||||
}
|
||||
unset($validresponses);
|
||||
|
||||
$correct = 0;
|
||||
foreach ($answers as $i => $answer) {
|
||||
if (isset($responses[$i]) && $answer->response==$responses[$i]) {
|
||||
$correct++;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
|
||||
if ($total==0) {
|
||||
$grade = 0;
|
||||
} else {
|
||||
$grade = round($correct / $total, 5);
|
||||
}
|
||||
|
||||
// we use $SESSION instead of accessing $_SESSION directly
|
||||
// $_SESSION['SESSION']->quiz_answer['q'.$this->id] = $grade;
|
||||
|
||||
if (empty($SESSION->quiz_answer)) {
|
||||
$SESSION->quiz_answer = array();
|
||||
}
|
||||
$SESSION->quiz_answer['q'.$this->id] = $grade;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function usort_sortorder($a, $b) {
|
||||
if ($a->sortorder < $b->sortorder) {
|
||||
return -1;
|
||||
}
|
||||
if ($a->sortorder > $b->sortorder) {
|
||||
return 1;
|
||||
}
|
||||
return 0; // equal values
|
||||
}
|
||||
|
||||
public function is_gradable_response(array $response) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_validation_error(array $response) {
|
||||
return '';
|
||||
}
|
||||
|
||||
public function is_same_response(array $prevresponse, array $newresponse) {
|
||||
|
||||
}
|
||||
|
||||
public function compute_final_grade($responses, $totaltries) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public function grade_response(array $response) {
|
||||
global $SESSION;
|
||||
if (empty($SESSION->quiz_answer['q'.$this->id])) {
|
||||
$fraction = 0;
|
||||
} else {
|
||||
$fraction = $SESSION->quiz_answer['q'.$this->id];
|
||||
}
|
||||
return array($fraction, question_state::graded_state_for_fraction($fraction));
|
||||
}
|
||||
|
||||
public function check_file_access($qa, $options, $component, $filearea, $args, $forcedownload) {
|
||||
return parent::check_file_access($qa, $options, $component, $filearea, $args, $forcedownload);
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ if (class_exists('question_type')) {
|
||||
$register_questiontype = false;
|
||||
} else {
|
||||
$register_questiontype = true; // Moodle 2.0
|
||||
require_once(dirname(__FILE__).'/legacy/20.php');
|
||||
require_once($CFG->dirroot.'/question/type/ordering/legacy/20.php');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,8 +70,7 @@ class qtype_ordering extends question_type {
|
||||
|
||||
protected function initialise_question_instance(question_definition $question, $questiondata) {
|
||||
parent::initialise_question_instance($question, $questiondata);
|
||||
$answers = array_keys($questiondata->options->answers);
|
||||
$question->rightanswer = implode(',', $answers);
|
||||
$this->initialise_ordering_feedback($question, $questiondata);
|
||||
}
|
||||
|
||||
public function save_question_options($question) {
|
||||
@ -119,6 +118,7 @@ class qtype_ordering extends question_type {
|
||||
return $result;
|
||||
}
|
||||
} else {
|
||||
unset($answer->id);
|
||||
if (! $answer->id = $DB->insert_record('question_answers', $answer)) {
|
||||
$result->error = get_string('cannotinsertrecord', 'error', 'question_answers');
|
||||
return $result;
|
||||
@ -129,8 +129,8 @@ class qtype_ordering extends question_type {
|
||||
// create $options for this ordering question
|
||||
$options = (object)array(
|
||||
'questionid' => $question->id,
|
||||
'logical' => $question->logical,
|
||||
'studentsee' => $question->studentsee
|
||||
'selecttype' => $question->selecttype,
|
||||
'selectcount' => $question->selectcount
|
||||
);
|
||||
$options = $this->save_ordering_feedback_helper($options, $question, $context, true);
|
||||
|
||||
@ -156,17 +156,53 @@ class qtype_ordering extends question_type {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function save_ordering_feedback_helper($options, $question, $context, $withparts = false) {
|
||||
protected function initialise_ordering_feedback($question, $questiondata, $shownumcorrect=false) {
|
||||
if (method_exists($this, 'initialise_combined_feedback')) {
|
||||
// Moodle >= 2.1
|
||||
$options = $this->initialise_combined_feedback($question, $questiondata, $shownumcorrect);
|
||||
} else {
|
||||
// Moodle 2.0
|
||||
$names = array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback');
|
||||
foreach ($names as $name) {
|
||||
$format = $name.'format';
|
||||
$question->$name = $questiondata->options->$name;
|
||||
$question->$format = $questiondata->options->$format;
|
||||
}
|
||||
if ($shownumcorrect) {
|
||||
$question->shownumcorrect = $questiondata->options->shownumcorrect;
|
||||
}
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function get_possible_responses($questiondata) {
|
||||
$responses = array();
|
||||
$question = $this->make_question($questiondata);
|
||||
foreach ($question->correctresponse as $position => $answerid) {
|
||||
$responses[] = $position.': '.$question->answers[$answerid]->answer;
|
||||
}
|
||||
$responses = array(
|
||||
0 => question_possible_response::no_response(),
|
||||
1 => implode(', ', $responses)
|
||||
);
|
||||
return ;
|
||||
}
|
||||
|
||||
protected function save_ordering_feedback_helper($options, $question, $context, $shownumcorrect=false) {
|
||||
if (method_exists($this, 'save_combined_feedback_helper')) {
|
||||
// Moodle >= 2.1
|
||||
$options = $this->save_combined_feedback_helper($options, $question, $context, $withparts);
|
||||
$options = $this->save_combined_feedback_helper($options, $question, $context, $shownumcorrect);
|
||||
} else {
|
||||
// Moodle 2.0
|
||||
$names = array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback');
|
||||
foreach ($names as $name) {
|
||||
$text = $question->$name;
|
||||
$format = $name.'format';
|
||||
$options->$name = $this->import_or_save_files($text, $context, 'qtype_ordering', $name, $question->id);
|
||||
$options->{$name.'format'} = $text['format'];
|
||||
$options->$format = $text['format'];
|
||||
}
|
||||
if ($shownumcorrect) {
|
||||
$options->shownumcorrect = (isset($question->shownumcorrect) && $question->shownumcorrect);
|
||||
}
|
||||
}
|
||||
return $options;
|
||||
@ -196,9 +232,6 @@ class qtype_ordering extends question_type {
|
||||
return true;
|
||||
}
|
||||
|
||||
// following seems to be unnecessary ...
|
||||
// initialise_question_instance(question_definition $question, $questiondata)
|
||||
|
||||
public function delete_question($questionid, $contextid) {
|
||||
global $DB;
|
||||
$DB->delete_records('qtype_ordering_options', array('questionid' => $questionid));
|
||||
@ -230,8 +263,8 @@ class qtype_ordering extends question_type {
|
||||
}
|
||||
|
||||
$questionname = trim($matches[1]);
|
||||
$numberofitems = trim($matches[2]);
|
||||
$extractiontype = trim($matches[3]);
|
||||
$selectcount = trim($matches[2]);
|
||||
$selecttype = trim($matches[3]);
|
||||
$lines = explode(PHP_EOL, $matches[4]);
|
||||
unset($matches);
|
||||
|
||||
@ -241,13 +274,13 @@ class qtype_ordering extends question_type {
|
||||
// fix empty or long question name
|
||||
$question->name = $this->fix_questionname($question->name, $questionname);
|
||||
|
||||
// set "studentsee" field from $numberofitems
|
||||
if (is_numeric($numberofitems) && $numberofitems > 2 && $numberofitems <= count($lines)) {
|
||||
$numberofitems = intval($numberofitems);
|
||||
// set "selectcount" field from $selectcount
|
||||
if (is_numeric($selectcount) && $selectcount > 2 && $selectcount <= count($lines)) {
|
||||
$selectcount = intval($selectcount);
|
||||
} else {
|
||||
$numberofitems = min(6, count($lines));
|
||||
$selectcount = min(6, count($lines));
|
||||
}
|
||||
$this->set_num_and_type($question, $numberofitems, $extractiontype);
|
||||
$this->set_count_and_type($question, $selectcount, $selecttype);
|
||||
|
||||
// remove blank items
|
||||
$lines = array_map('trim', $lines);
|
||||
@ -276,20 +309,23 @@ class qtype_ordering extends question_type {
|
||||
}
|
||||
|
||||
/**
|
||||
* extract_num_and_type
|
||||
* extract_count_and_type
|
||||
*
|
||||
* @param stdClass $question
|
||||
* @todo Finish documenting this function
|
||||
*/
|
||||
function extract_num_and_type($question) {
|
||||
switch ($question->options->logical) {
|
||||
case 0: $type = 'EXACT'; break; // all
|
||||
case 1: $type = 'REL'; break; // random subset
|
||||
case 2: $type = 'CONTIG'; break; // contiguous subset
|
||||
default: $type = ''; // shouldn't happen !!
|
||||
function extract_count_and_type($question) {
|
||||
switch ($question->options->selecttype) {
|
||||
case 0: $type = 'ALL'; break; // all items
|
||||
case 1: $type = 'RANDOM'; break; // random subset
|
||||
case 2: $type = 'CONTIGUOUS'; break; // contiguous subset
|
||||
default: $type = ''; // shouldn't happen !!
|
||||
}
|
||||
$num = $question->options->studentsee + 2;
|
||||
return array($num, $type);
|
||||
|
||||
// Note: this used to be (selectcount + 2)
|
||||
$count = $question->options->selectcount;
|
||||
|
||||
return array($count, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -301,9 +337,9 @@ class qtype_ordering extends question_type {
|
||||
* @todo Finish documenting this function
|
||||
*/
|
||||
function export_to_gift($question, $format, $extra=null) {
|
||||
list($num, $type) = $this->extract_num_and_type($question);
|
||||
list($count, $type) = $this->extract_count_and_type($question);
|
||||
|
||||
$expout = $question->questiontext.'{>'.$num.' '.$type.' '."\n";
|
||||
$expout = $question->questiontext.'{>'.$count.' '.$type.' '."\n";
|
||||
foreach ($question->options->answers as $answer) {
|
||||
$expout .= $answer->answer."\n";
|
||||
}
|
||||
@ -322,11 +358,11 @@ class qtype_ordering extends question_type {
|
||||
*/
|
||||
function export_to_xml($question, qformat_xml $format, $extra=null) {
|
||||
|
||||
list($num, $type) = $this->extract_num_and_type($question);
|
||||
list($count, $type) = $this->extract_count_and_type($question);
|
||||
|
||||
$output = '';
|
||||
$output .= " <logical>$type</logical>\n";
|
||||
$output .= " <studentsee>$num</studentsee>\n";
|
||||
$output .= " <selecttype>$type</selecttype>\n";
|
||||
$output .= " <selectcount>$count</selectcount>\n";
|
||||
|
||||
foreach($question->options->answers as $answer) {
|
||||
$output .= ' <answer fraction="'.$answer->fraction.'" '.$format->format($answer->answerformat).">\n";
|
||||
@ -367,10 +403,18 @@ class qtype_ordering extends question_type {
|
||||
// fix empty or long question name
|
||||
$newquestion->name = $this->fix_questionname($newquestion->name, $newquestion->questiontext);
|
||||
|
||||
// extra fields fields fields - "logical" and "studentsee"
|
||||
$numberofitems = $format->getpath($data, array('#', 'studentsee', 0, '#'), 6);
|
||||
$extractiontype = $format->getpath($data, array('#', 'logical', 0, '#'), 'RANDOM');
|
||||
$this->set_num_and_type($newquestion, $numberofitems, $extractiontype);
|
||||
// extra fields fields fields - "selecttype" and "selectcount"
|
||||
// (these fields used to be called "logical" and "studentsee")
|
||||
if (isset($data['#']['selecttype'])) {
|
||||
$selecttype = 'selecttype';
|
||||
$selectcount = 'selectcount';
|
||||
} else {
|
||||
$selecttype = 'logical';
|
||||
$selectcount = 'studentsee';
|
||||
}
|
||||
$selecttype = $format->getpath($data, array('#', $selecttype, 0, '#'), 'RANDOM');
|
||||
$selectcount = $format->getpath($data, array('#', $selectcount, 0, '#'), 6);
|
||||
$this->set_count_and_type($newquestion, $selectcount, $selecttype);
|
||||
|
||||
$newquestion->answer = array();
|
||||
$newquestion->answerformat = array();
|
||||
@ -421,38 +465,39 @@ class qtype_ordering extends question_type {
|
||||
}
|
||||
|
||||
/*
|
||||
* set_num_and_type
|
||||
* set_count_and_type
|
||||
*
|
||||
* @param object $question (passed by reference)
|
||||
* @param integer $num the number of items to display
|
||||
* @param integer $count the number of items to display
|
||||
* @param integer $type the extraction type
|
||||
* @param integer $default_type (optional, default=1)
|
||||
*/
|
||||
function set_num_and_type(&$question, $num, $type, $default_type=1) {
|
||||
function set_count_and_type(&$question, $count, $type, $default_type=1) {
|
||||
|
||||
// set "studentsee" from $num(ber of items)
|
||||
$question->studentsee = ($num - 2);
|
||||
// set "selectcount" from $count
|
||||
// this used to be ($count - 2)
|
||||
$question->selectcount = $count;
|
||||
|
||||
// set "logical" from (extraction) $type
|
||||
// set "selecttype" from $type
|
||||
switch ($type) {
|
||||
case 'ALL':
|
||||
case 'EXACT':
|
||||
$question->logical = 0;
|
||||
$question->selecttype = 0;
|
||||
break;
|
||||
|
||||
case 'RANDOM':
|
||||
case 'REL':
|
||||
$question->logical = 1;
|
||||
$question->selecttype = 1;
|
||||
break;
|
||||
|
||||
case 'CONTIGUOUS':
|
||||
case 'CONTIG':
|
||||
$question->logical = 2;
|
||||
$question->selecttype = 2;
|
||||
break;
|
||||
|
||||
// otherwise
|
||||
default:
|
||||
$question->logical = $default_type;
|
||||
$question->selecttype = $default_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,58 +26,35 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
if (! class_exists('qtype_with_combined_feedback_renderer')) {
|
||||
// Moodle 2.0
|
||||
require_once($CFG->dirroot.'/question/type/ordering/legacy/20.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_renderer {
|
||||
class qtype_ordering_renderer extends qtype_with_combined_feedback_renderer {
|
||||
|
||||
public function formulation_and_controls(question_attempt $qa, question_display_options $options) {
|
||||
global $CFG, $DB;
|
||||
|
||||
$question = $qa->get_question();
|
||||
$ordering = $question->get_ordering_options();
|
||||
$answers = $question->get_ordering_answers();
|
||||
$response = $qa->get_last_qt_data();
|
||||
$question->update_current_response($response);
|
||||
$currentresponse = $question->currentresponse;
|
||||
$correctresponse = $question->correctresponse;
|
||||
|
||||
if (empty($ordering) || empty($answers)) {
|
||||
return ''; // shouldn't happen !!
|
||||
}
|
||||
|
||||
if ($ordering->studentsee==0) { // all items
|
||||
$ordering->studentsee = count($answers);
|
||||
} else {
|
||||
// a nasty hack so that "studentsee" is the same
|
||||
// as what is displayed by edit_ordering_form.php
|
||||
$ordering->studentsee += 2;
|
||||
}
|
||||
|
||||
if ($options->correctness) {
|
||||
list($answerids, $correctorder) = $this->get_response($qa, $question, $answers);
|
||||
} else {
|
||||
$correctorder = array();
|
||||
switch ($ordering->logical) {
|
||||
case 0: // all
|
||||
$answerids = array_keys($answers);
|
||||
break;
|
||||
|
||||
case 1: // random subset
|
||||
$answerids = array_rand($answers, $ordering->studentsee);
|
||||
break;
|
||||
|
||||
case 2: // contiguous subset
|
||||
if (count($answers) > $ordering->studentsee) {
|
||||
$offset = mt_rand(0, count($answers) - $ordering->studentsee);
|
||||
$answers = array_slice($answers, $offset, $ordering->studentsee, true);
|
||||
}
|
||||
$answerids = array_keys($answers);
|
||||
break;
|
||||
}
|
||||
shuffle($answerids);
|
||||
}
|
||||
|
||||
$response_name = $qa->get_qt_field_name($question->get_response_fieldname());
|
||||
// generate fieldnames and ids
|
||||
// response_fieldname : fieldname: 1_response_319
|
||||
// response_name : q27:1_response_319
|
||||
// response_id : id_q27_1_response_319
|
||||
// sortable_id : id_sortable_q27_1_response_319
|
||||
$response_fieldname = $question->get_response_fieldname();
|
||||
$response_name = $qa->get_qt_field_name($response_fieldname);
|
||||
$response_id = 'id_'.preg_replace('/[^a-zA-Z0-9]+/', '_', $response_name);
|
||||
$sortable_id = 'id_sortable_'.$question->id;
|
||||
|
||||
@ -107,16 +84,16 @@ class qtype_ordering_renderer extends qtype_renderer {
|
||||
|
||||
$result .= html_writer::tag('div', $question->format_questiontext($qa), array('class' => 'qtext'));
|
||||
|
||||
if (count($answerids)) {
|
||||
if (count($currentresponse)) {
|
||||
$result .= html_writer::start_tag('div', array('class' => 'ablock'));
|
||||
$result .= html_writer::start_tag('div', array('class' => 'answer'));
|
||||
$result .= html_writer::start_tag('ul', array('class' => 'sortablelist', 'id' => $sortable_id));
|
||||
|
||||
// generate ordering items
|
||||
foreach ($answerids as $position => $answerid) {
|
||||
if (array_key_exists($answerid, $answers)) {
|
||||
foreach ($currentresponse as $position => $answerid) {
|
||||
if (array_key_exists($answerid, $question->answers)) {
|
||||
if ($options->correctness) {
|
||||
if ($correctorder[$position]==$answerid) {
|
||||
if ($correctresponse[$position]==$answerid) {
|
||||
$class = 'correctposition';
|
||||
$img = $this->feedback_image(1);
|
||||
} else {
|
||||
@ -130,10 +107,10 @@ class qtype_ordering_renderer extends qtype_renderer {
|
||||
}
|
||||
// the original "id" revealed the correct order of the answers
|
||||
// because $answer->fraction holds the correct order number
|
||||
// $id = 'ordering_item_'.$answerid.'_'.intval($answers[$answerid]->fraction);
|
||||
$params = array('class' => $class, 'id' => $answers[$answerid]->md5key);
|
||||
|
||||
$result .= html_writer::tag('li', $img.$answers[$answerid]->answer, $params);
|
||||
// $id = 'ordering_item_'.$answerid.'_'.intval($question->answers[$answerid]->fraction);
|
||||
$answer = $question->answers[$answerid];
|
||||
$params = array('class' => $class, 'id' => $answer->md5key);
|
||||
$result .= html_writer::tag('li', $img.$answer->answer, $params);
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,78 +128,40 @@ class qtype_ordering_renderer extends qtype_renderer {
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function specific_feedback(question_attempt $qa) {
|
||||
return $this->combined_feedback($qa);
|
||||
}
|
||||
|
||||
public function correct_response(question_attempt $qa) {
|
||||
global $DB;
|
||||
|
||||
$output = '';
|
||||
|
||||
$showcorrect = true;
|
||||
$showcorrect = false;
|
||||
if ($step = $qa->get_last_step()) {
|
||||
switch ($step->get_state()) {
|
||||
case 'gradedright':
|
||||
$showcorrect = false;
|
||||
$msg = get_string('correctfeedback', 'qtype_ordering');
|
||||
break;
|
||||
case 'gradedpartial':
|
||||
$fraction = round($step->get_fraction(), 2);
|
||||
$msg = get_string('partiallycorrectfeedback', 'qtype_ordering', $fraction);
|
||||
break;
|
||||
case 'gradedwrong':
|
||||
$msg = get_string('incorrectfeedback', 'qtype_ordering');
|
||||
break;
|
||||
default:
|
||||
$showcorrect = false;
|
||||
$msg = '';
|
||||
}
|
||||
if ($msg) {
|
||||
$output .= html_writer::tag('p', $msg);
|
||||
case 'gradedright' : $showcorrect = false; break;
|
||||
case 'gradedpartial': $showcorrect = true; break;
|
||||
case 'gradedwrong' : $showcorrect = true; break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($showcorrect) {
|
||||
$question = $qa->get_question();
|
||||
$answers = $question->get_ordering_answers();
|
||||
list($answerids, $correctorder) = $this->get_response($qa, $question, $answers);
|
||||
if (count($correctorder)) {
|
||||
if (empty($question->correctresponse)) {
|
||||
$output .= html_writer::tag('p', get_string('noresponsedetails', 'qtype_ordering'));
|
||||
} else {
|
||||
$output .= html_writer::tag('p', get_string('correctorder', 'qtype_ordering'));
|
||||
$output .= html_writer::start_tag('ol');
|
||||
foreach ($correctorder as $position => $answerid) {
|
||||
$output .= html_writer::tag('li', $answers[$answerid]->answer);
|
||||
$correctresponse = $question->correctresponse;
|
||||
foreach ($correctresponse as $position => $answerid) {
|
||||
$answer = $question->answers[$answerid];
|
||||
$output .= html_writer::tag('li', $answer->answer);
|
||||
}
|
||||
$output .= html_writer::end_tag('ol');
|
||||
} else {
|
||||
$output .= html_writer::tag('p', get_string('noresponsedetails', 'qtype_ordering'));
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function get_response($qa, $question, $answers) {
|
||||
$answerids = array();
|
||||
$correctorder = array();
|
||||
|
||||
$name = $question->get_response_fieldname();
|
||||
if ($step = $qa->get_last_step_with_qt_var($name)) {
|
||||
|
||||
$response = $step->get_qt_var($name); // "$md5key, ..."
|
||||
$response = explode(',', $response); // array($position => $md5key, ...)
|
||||
$response = array_flip($response); // array($md5key => $position, ...)
|
||||
|
||||
foreach ($answers as $answer) {
|
||||
if (array_key_exists($answer->md5key, $response)) {
|
||||
$position = $response[$answer->md5key];
|
||||
$sortorder = intval($answer->fraction);
|
||||
$answerids[$position] = $answer->id;
|
||||
$correctorder[$sortorder] = $answer->id;
|
||||
}
|
||||
}
|
||||
|
||||
ksort($answerids);
|
||||
ksort($correctorder);
|
||||
$correctorder = array_values($correctorder);
|
||||
}
|
||||
|
||||
return array($answerids, $correctorder);
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,6 @@ defined('MOODLE_INTERNAL') || die();
|
||||
$plugin->cron = 0;
|
||||
$plugin->component = 'qtype_ordering';
|
||||
$plugin->maturity = MATURITY_STABLE; // ALPHA=50, BETA=100, RC=150, STABLE=200
|
||||
$plugin->release = '2015-01-14 (10)';
|
||||
$plugin->version = 2015011410;
|
||||
$plugin->release = '2015-01-17 (11)';
|
||||
$plugin->version = 2015011711;
|
||||
$plugin->requires = 2010112400; // Moodle 2.0
|
||||
|
Loading…
x
Reference in New Issue
Block a user