MDL-27412 Upgrade the calculatedmulti question type to the new question engine.
This commit is contained in:
@@ -36,6 +36,7 @@
|
||||
<FIELD NAME="incorrectfeedback" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" COMMENT="Feedback shown for any incorrect response." PREVIOUS="partiallycorrectfeedbackformat" NEXT="incorrectfeedbackformat"/>
|
||||
<FIELD NAME="incorrectfeedbackformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" PREVIOUS="incorrectfeedback" NEXT="answernumbering"/>
|
||||
<FIELD NAME="answernumbering" TYPE="char" LENGTH="10" NOTNULL="true" DEFAULT="abc" SEQUENCE="false" COMMENT="Indicates how and whether the choices should be numbered." PREVIOUS="incorrectfeedbackformat"/>
|
||||
<FIELD NAME="shownumcorrect" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="false" DEFAULT="0" SEQUENCE="false" COMMENT="If true, then when the user gets a multiple-response question partially correct, tell them how many choices they got correct alongside the feedback." PREVIOUS="answernumbering"/>
|
||||
</FIELDS>
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="question"/>
|
||||
|
||||
@@ -208,6 +208,25 @@ function xmldb_qtype_calculated_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, 2010020801, 'qtype', 'calculated');
|
||||
}
|
||||
|
||||
// Add new shownumcorrect field. If this is true, then when the user gets a
|
||||
// multiple-response question partially correct, tell them how many choices
|
||||
// they got correct alongside the feedback.
|
||||
if ($oldversion < 2011051900) {
|
||||
|
||||
// Define field shownumcorrect to be added to question_multichoice
|
||||
$table = new xmldb_table('question_calculated_options');
|
||||
$field = new xmldb_field('shownumcorrect', XMLDB_TYPE_INTEGER, '2', null,
|
||||
XMLDB_NOTNULL, null, '0', 'answernumbering');
|
||||
|
||||
// Launch add field shownumcorrect
|
||||
if (!$dbman->field_exists($table, $field)) {
|
||||
$dbman->add_field($table, $field);
|
||||
}
|
||||
|
||||
// multichoice savepoint reached
|
||||
upgrade_plugin_savepoint(true, 2011051900, 'qtype', 'calculated');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
*
|
||||
* @package qtype
|
||||
* @subpackage calculated
|
||||
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
@@ -32,34 +32,94 @@ require_once($CFG->dirroot . '/question/type/numerical/question.php');
|
||||
/**
|
||||
* Represents a calculated question.
|
||||
*
|
||||
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class qtype_calculated_question extends qtype_numerical_question {
|
||||
class qtype_calculated_question extends qtype_numerical_question
|
||||
implements qtype_calculated_question_with_expressions {
|
||||
/** @var qtype_calculated_dataset_loader helper for loading the dataset. */
|
||||
public $datasetloader;
|
||||
/** @var qtype_calculated_variable_substituter stores the dataset we are using. */
|
||||
public $vs;
|
||||
|
||||
public function start_attempt(question_attempt_step $step) {
|
||||
$maxnumber = $this->datasetloader->get_number_of_items();
|
||||
$setnumber = rand(1, $maxnumber);
|
||||
// TODO implement the $synchronizecalculated bit from create_session_and_responses.
|
||||
|
||||
$this->vs = new qtype_calculated_variable_substituter(
|
||||
$this->datasetloader->get_values($setnumber),
|
||||
get_string('decsep', 'langconfig'));
|
||||
$this->calculate_all_expressions();
|
||||
|
||||
$step->set_qt_var('_dataset', $setnumber);
|
||||
foreach ($this->vs->get_values() as $name => $value) {
|
||||
$step->set_qt_var('_var_' . $name, $value);
|
||||
}
|
||||
|
||||
qtype_calculated_question_helper::start_attempt($this, $step);
|
||||
parent::start_attempt($step);
|
||||
}
|
||||
|
||||
public function apply_attempt_state(question_attempt_step $step) {
|
||||
qtype_calculated_question_helper::apply_attempt_state($this, $step);
|
||||
parent::apply_attempt_state($step);
|
||||
}
|
||||
|
||||
public function calculate_all_expressions() {
|
||||
$this->questiontext = $this->vs->replace_expressions_in_text($this->questiontext);
|
||||
$this->generalfeedback = $this->vs->replace_expressions_in_text($this->generalfeedback);
|
||||
|
||||
foreach ($this->answers as $ans) {
|
||||
if ($ans->answer && $ans->answer !== '*') {
|
||||
$ans->answer = $this->vs->calculate($ans->answer,
|
||||
$ans->correctanswerlength, $ans->correctanswerformat);
|
||||
}
|
||||
$ans->feedback = $this->vs->replace_expressions_in_text($ans->feedback,
|
||||
$ans->correctanswerlength, $ans->correctanswerformat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This interface defines the method that a quetsion type must implement if it
|
||||
* is to work with {@link qtype_calculated_question_helper}.
|
||||
*
|
||||
* As well as this method, the class that implements this interface must have
|
||||
* fields
|
||||
* public $datasetloader; // of type qtype_calculated_dataset_loader
|
||||
* public $vs; // of type qtype_calculated_variable_substituter
|
||||
*
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
interface qtype_calculated_question_with_expressions {
|
||||
/**
|
||||
* Replace all the expression in the question definition with the values
|
||||
* computed from the selected dataset by calling $this->vs->calculate() and
|
||||
* $this->vs->replace_expressions_in_text() on the parts of the question
|
||||
* that require it.
|
||||
*/
|
||||
public function calculate_all_expressions();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper class for questions that use datasets. Works with the interface
|
||||
* {@link qtype_calculated_question_with_expressions} and the class
|
||||
* {@link qtype_calculated_dataset_loader} to set up the value of each variable
|
||||
* in start_attempt, and restore that in apply_attempt_state.
|
||||
*
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class qtype_calculated_question_helper {
|
||||
public static function start_attempt(
|
||||
qtype_calculated_question_with_expressions $question, question_attempt_step $step) {
|
||||
$maxnumber = $question->datasetloader->get_number_of_items();
|
||||
$setnumber = rand(1, $maxnumber);
|
||||
// TODO implement the $synchronizecalculated bit from create_session_and_responses.
|
||||
|
||||
$question->vs = new qtype_calculated_variable_substituter(
|
||||
$question->datasetloader->get_values($setnumber),
|
||||
get_string('decsep', 'langconfig'));
|
||||
$question->calculate_all_expressions();
|
||||
|
||||
$step->set_qt_var('_dataset', $setnumber);
|
||||
foreach ($question->vs->get_values() as $name => $value) {
|
||||
$step->set_qt_var('_var_' . $name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
public static function apply_attempt_state(
|
||||
qtype_calculated_question_with_expressions $question, question_attempt_step $step) {
|
||||
$values = array();
|
||||
foreach ($step->get_qt_data() as $name => $value) {
|
||||
if (substr($name, 0, 5) === '_var_') {
|
||||
@@ -67,27 +127,9 @@ class qtype_calculated_question extends qtype_numerical_question {
|
||||
}
|
||||
}
|
||||
|
||||
$this->vs = new qtype_calculated_variable_substituter(
|
||||
$question->vs = new qtype_calculated_variable_substituter(
|
||||
$values, get_string('decsep', 'langconfig'));
|
||||
$this->calculate_all_expressions();
|
||||
|
||||
parent::apply_attempt_state($step);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace all the expression in the question definition with the values
|
||||
* computed from the selected dataset.
|
||||
*/
|
||||
protected function calculate_all_expressions() {
|
||||
$this->questiontext = $this->vs->replace_expressions_in_text($this->questiontext);
|
||||
$this->generalfeedback = $this->vs->replace_expressions_in_text($this->generalfeedback);
|
||||
|
||||
foreach ($this->answers as $ans) {
|
||||
if ($ans->answer && $ans->answer !== '*') {
|
||||
$ans->answer = $this->vs->calculate($ans->answer);
|
||||
}
|
||||
$ans->feedback = $this->vs->replace_expressions_in_text($ans->feedback);
|
||||
}
|
||||
$question->calculate_all_expressions();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,7 +275,17 @@ class qtype_calculated_variable_substituter {
|
||||
* Display a float properly formatted with a certain number of decimal places.
|
||||
* @param $x
|
||||
*/
|
||||
public function format_float($x) {
|
||||
public function format_float($x, $length = null, $format = null) {
|
||||
if (!is_null($format) && !is_null($format)) {
|
||||
if ($format == 1) {
|
||||
// Decimal places.
|
||||
$x = sprintf('%.' . $length . 'F', $x);
|
||||
} else if ($format == 1) {
|
||||
// Significant figures.
|
||||
$x = sprintf('%.' . $length . 'g', $x);
|
||||
$x = str_replace(',', '.', $x);
|
||||
}
|
||||
}
|
||||
return str_replace('.', $this->decimalpoint, $x);
|
||||
}
|
||||
|
||||
@@ -298,11 +350,12 @@ class qtype_calculated_variable_substituter {
|
||||
* @param string $text the text to process.
|
||||
* @return string the text with values substituted.
|
||||
*/
|
||||
public function replace_expressions_in_text($text) {
|
||||
public function replace_expressions_in_text($text, $length = null, $format = null) {
|
||||
$vs = $this; // Can't see to use $this in a PHP closure.
|
||||
$text = preg_replace_callback('~\{=([^{}]*(?:\{[^{}]+}[^{}]*)*)}~', function ($matches) use ($vs) {
|
||||
return $vs->format_float($vs->calculate($matches[1]));
|
||||
}, $text);
|
||||
$text = preg_replace_callback('~\{=([^{}]*(?:\{[^{}]+}[^{}]*)*)}~',
|
||||
function ($matches) use ($vs, $format, $length) {
|
||||
return $vs->format_float($vs->calculate($matches[1]), $length, $format);
|
||||
}, $text);
|
||||
return $this->substitute_values_pretty($text);
|
||||
}
|
||||
|
||||
|
||||
@@ -343,6 +343,8 @@ class qtype_calculated extends question_type {
|
||||
$question, $questiondata);
|
||||
foreach ($questiondata->options->answers as $a) {
|
||||
$question->answers[$a->id]->tolerancetype = $a->tolerancetype;
|
||||
$question->answers[$a->id]->correctanswerlength = $a->correctanswerlength;
|
||||
$question->answers[$a->id]->correctanswerformat = $a->correctanswerformat;
|
||||
}
|
||||
|
||||
$question->unitdisplay = $questiondata->options->showunits;
|
||||
@@ -771,12 +773,6 @@ class qtype_calculated extends question_type {
|
||||
|
||||
}
|
||||
|
||||
public function create_virtual_qtype() {
|
||||
global $CFG;
|
||||
require_once("$CFG->dirroot/question/type/numerical/questiontype.php");
|
||||
return new question_numerical_qtype();
|
||||
}
|
||||
|
||||
public function supports_dataset_item_generation() {
|
||||
// Calcualted support generation of randomly distributed number data
|
||||
return true;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
*
|
||||
* @package qtype
|
||||
* @subpackage calculated
|
||||
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
|
||||
@@ -41,8 +41,7 @@ class qtype_calculated_test_helper extends question_test_helper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a calculated question with correct ansewer 3.14, and various incorrect
|
||||
* answers with different feedback.
|
||||
* Makes a calculated question about summing two numbers.
|
||||
* @return qtype_calculated_question
|
||||
*/
|
||||
public function make_calculated_question_sum() {
|
||||
@@ -52,11 +51,17 @@ class qtype_calculated_test_helper extends question_test_helper {
|
||||
$q->name = 'Simple sum';
|
||||
$q->questiontext = 'What is {a} + {b}?';
|
||||
$q->generalfeedback = 'Generalfeedback: {={a} + {b}} is the right answer.';
|
||||
|
||||
$q->answers = array(
|
||||
13 => new qtype_numerical_answer(13, '{a} + {b}', 1.0, 'Very good.', FORMAT_HTML, 0),
|
||||
14 => new qtype_numerical_answer(14, '{a} - {b}', 0.0, 'Add. not subtract!.', FORMAT_HTML, 0),
|
||||
17 => new qtype_numerical_answer(17, '*', 0.0, 'Completely wrong.', FORMAT_HTML, 0),
|
||||
);
|
||||
foreach ($q->answers as $answer) {
|
||||
$answer->correctanswerlength = 2;
|
||||
$answer->correctanswerformat = 1;
|
||||
}
|
||||
|
||||
$q->qtype = question_bank::get_qtype('calculated');
|
||||
$q->unitdisplay = qtype_numerical::UNITNONE;
|
||||
$q->unitgradingtype = 0;
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
display: inline;
|
||||
}
|
||||
.que.calculated .answer input {
|
||||
width: 99%;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
@@ -25,5 +25,5 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2010090501;
|
||||
$plugin->requires = 2010090501;
|
||||
$plugin->version = 2011051900;
|
||||
$plugin->requires = 2011051212;
|
||||
|
||||
@@ -43,11 +43,11 @@ class qtype_calculatedmulti_edit_form extends question_edit_form {
|
||||
public $questiondisplay;
|
||||
public $initialname = '';
|
||||
public $reload = false;
|
||||
|
||||
public function __construct($submiturl, $question, $category,
|
||||
$contexts, $formeditable = true) {
|
||||
global $SESSION, $CFG, $DB;
|
||||
$this->question = $question;
|
||||
$this->qtypeobj = $QTYPES[$this->question->qtype];
|
||||
$this->qtypeobj = question_bank::get_qtype('calculatedmulti');
|
||||
if (1 == optional_param('reload', '', PARAM_INT)) {
|
||||
$this->reload = true;
|
||||
} else {
|
||||
@@ -105,13 +105,7 @@ class qtype_calculatedmulti_edit_form extends question_edit_form {
|
||||
return $repeated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add question-type specific form fields.
|
||||
*
|
||||
* @param MoodleQuickForm $mform the form being built.
|
||||
*/
|
||||
protected function definition_inner($mform) {
|
||||
$this->qtypeobj = $QTYPES[$this->qtype()];
|
||||
|
||||
$label = get_string('sharedwildcards', 'qtype_calculated');
|
||||
$mform->addElement('hidden', 'initialcategory', 1);
|
||||
@@ -148,14 +142,9 @@ class qtype_calculatedmulti_edit_form extends question_edit_form {
|
||||
$mform->addHelpButton('shuffleanswers', 'shuffleanswers', 'qtype_multichoice');
|
||||
$mform->setDefault('shuffleanswers', 1);
|
||||
|
||||
$numberingoptions = $QTYPES['multichoice']->get_numbering_styles();
|
||||
$menu = array();
|
||||
foreach ($numberingoptions as $numberingoption) {
|
||||
$menu[$numberingoption] = get_string(
|
||||
'answernumbering' . $numberingoption, 'qtype_multichoice');
|
||||
}
|
||||
$numberingoptions = question_bank::get_qtype('multichoice')->get_numbering_styles();
|
||||
$mform->addElement('select', 'answernumbering',
|
||||
get_string('answernumbering', 'qtype_multichoice'), $menu);
|
||||
get_string('answernumbering', 'qtype_multichoice'), $numberingoptions);
|
||||
$mform->setDefault('answernumbering', 'abc');
|
||||
|
||||
$creategrades = get_grade_options();
|
||||
@@ -301,10 +290,6 @@ class qtype_calculatedmulti_edit_form extends question_edit_form {
|
||||
return $question;
|
||||
}
|
||||
|
||||
public function qtype() {
|
||||
return 'calculatedmulti';
|
||||
}
|
||||
|
||||
public function validation($data, $files) {
|
||||
$errors = parent::validation($data, $files);
|
||||
|
||||
@@ -431,4 +416,8 @@ class qtype_calculatedmulti_edit_form extends question_edit_form {
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
|
||||
public function qtype() {
|
||||
return 'calculatedmulti';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Calculated multiple-choice question definition class.
|
||||
*
|
||||
* @package qtype
|
||||
* @subpackage calculatedmulti
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/question/type/multichoice/question.php');
|
||||
require_once($CFG->dirroot . '/question/type/calculated/question.php');
|
||||
|
||||
|
||||
/**
|
||||
* Represents a calculated multiple-choice multiple-response question.
|
||||
*
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class qtype_calculatedmulti_single_question extends qtype_multichoice_single_question
|
||||
implements qtype_calculated_question_with_expressions {
|
||||
/** @var qtype_calculated_dataset_loader helper for loading the dataset. */
|
||||
public $datasetloader;
|
||||
|
||||
/** @var qtype_calculated_variable_substituter stores the dataset we are using. */
|
||||
public $vs;
|
||||
|
||||
public function start_attempt(question_attempt_step $step) {
|
||||
qtype_calculated_question_helper::start_attempt($this, $step);
|
||||
parent::start_attempt($step);
|
||||
}
|
||||
|
||||
public function apply_attempt_state(question_attempt_step $step) {
|
||||
qtype_calculated_question_helper::apply_attempt_state($this, $step);
|
||||
parent::apply_attempt_state($step);
|
||||
}
|
||||
|
||||
public function calculate_all_expressions() {
|
||||
qtype_calculatedmulti_calculate_helper::calculate_all_expressions($this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents a calculated multiple-choice multiple-response question.
|
||||
*
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class qtype_calculatedmulti_multi_question extends qtype_multichoice_multi_question
|
||||
implements qtype_calculated_question_with_expressions {
|
||||
/** @var qtype_calculated_dataset_loader helper for loading the dataset. */
|
||||
public $datasetloader;
|
||||
|
||||
/** @var qtype_calculated_variable_substituter stores the dataset we are using. */
|
||||
public $vs;
|
||||
|
||||
public function start_attempt(question_attempt_step $step) {
|
||||
qtype_calculated_question_helper::start_attempt($this, $step);
|
||||
parent::start_attempt($step);
|
||||
}
|
||||
|
||||
public function apply_attempt_state(question_attempt_step $step) {
|
||||
qtype_calculated_question_helper::apply_attempt_state($this, $step);
|
||||
parent::apply_attempt_state($step);
|
||||
}
|
||||
|
||||
public function calculate_all_expressions() {
|
||||
qtype_calculatedmulti_calculate_helper::calculate_all_expressions($this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper to abstract common code between qtype_calculatedmulti_single_question
|
||||
* and qtype_calculatedmulti_multi_question.
|
||||
*
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class qtype_calculatedmulti_calculate_helper {
|
||||
/**
|
||||
* Calculate all the exressions in a qtype_calculatedmulti_single_question
|
||||
* or qtype_calculatedmulti_multi_question.
|
||||
* @param unknown_type $question
|
||||
*/
|
||||
public static function calculate_all_expressions(
|
||||
qtype_calculated_question_with_expressions $question) {
|
||||
$question->questiontext = $question->vs->replace_expressions_in_text(
|
||||
$question->questiontext);
|
||||
$question->generalfeedback = $question->vs->replace_expressions_in_text(
|
||||
$question->generalfeedback);
|
||||
|
||||
foreach ($question->answers as $ans) {
|
||||
if ($ans->answer && $ans->answer !== '*') {
|
||||
$ans->answer = $question->vs->replace_expressions_in_text($ans->answer,
|
||||
$ans->correctanswerlength, $ans->correctanswerformat);
|
||||
}
|
||||
$ans->feedback = $question->vs->replace_expressions_in_text($ans->feedback,
|
||||
$ans->correctanswerlength, $ans->correctanswerformat);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,9 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/question/type/multichoice/questiontype.php');
|
||||
require_once($CFG->dirroot . '/question/type/calculated/questiontype.php');
|
||||
|
||||
|
||||
/**
|
||||
* The calculated multiple-choice question type.
|
||||
@@ -37,7 +40,6 @@ class qtype_calculatedmulti extends qtype_calculated {
|
||||
|
||||
// Used by the function custom_generator_tools:
|
||||
public $calcgenerateidhasbeenadded = false;
|
||||
public $virtualqtype = false;
|
||||
|
||||
public function requires_qtypes() {
|
||||
return array('calculated', 'multichoice');
|
||||
@@ -100,13 +102,6 @@ class qtype_calculatedmulti extends qtype_calculated {
|
||||
$oldoptions = array();
|
||||
}
|
||||
|
||||
// Save the units.
|
||||
$virtualqtype = $this->get_virtual_qtype($question);
|
||||
if (isset($result->error)) {
|
||||
return $result;
|
||||
} else {
|
||||
$units = &$result->units;
|
||||
}
|
||||
// Insert all the new answers
|
||||
if (isset($question->answer) && !isset($question->answers)) {
|
||||
$question->answers = $question->answer;
|
||||
@@ -195,108 +190,36 @@ class qtype_calculatedmulti extends qtype_calculated {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) {
|
||||
// Find out how many datasets are available
|
||||
global $CFG, $DB, $OUTPUT;
|
||||
$maxnumber = (int)$DB->get_field_sql(
|
||||
"SELECT MIN(a.itemcount)
|
||||
FROM {question_dataset_definitions} a, {question_datasets} b
|
||||
WHERE b.question = ? AND a.id = b.datasetdefinition", array($question->id));
|
||||
if (!$maxnumber) {
|
||||
print_error('cannotgetdsforquestion', 'question', '', $question->id);
|
||||
}
|
||||
$sql = "SELECT i.*
|
||||
FROM {question_datasets} d, {question_dataset_definitions} i
|
||||
WHERE d.question = ? AND d.datasetdefinition = i.id AND i.category != 0";
|
||||
if (!$question->options->synchronize || !$records = $DB->get_records_sql($sql,
|
||||
array($question->id))) {
|
||||
$synchronize_calculated = false;
|
||||
protected function make_question_instance($questiondata) {
|
||||
question_bank::load_question_definition_classes($this->name());
|
||||
if ($questiondata->options->single) {
|
||||
$class = 'qtype_calculatedmulti_single_question';
|
||||
} else {
|
||||
// i.e records is true so test coherence
|
||||
$coherence = true;
|
||||
$a = new stdClass();
|
||||
$a->qid = $question->id;
|
||||
$a->qcat = $question->category;
|
||||
foreach ($records as $def) {
|
||||
if ($def->category != $question->category) {
|
||||
$a->name = $def->name;
|
||||
$a->sharedcat = $def->category;
|
||||
$coherence = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$coherence) {
|
||||
echo $OUTPUT->notification(
|
||||
get_string('nocoherencequestionsdatyasetcategory', 'qtype_calculated', $a));
|
||||
}
|
||||
|
||||
$synchronize_calculated = true;
|
||||
$class = 'qtype_calculatedmulti_multi_question';
|
||||
}
|
||||
return new $class();
|
||||
}
|
||||
|
||||
// Choose a random dataset
|
||||
// maxnumber sould not be breater than 100
|
||||
if ($maxnumber > qtype_calculated::MAX_DATASET_ITEMS) {
|
||||
$maxnumber = qtype_calculated::MAX_DATASET_ITEMS;
|
||||
}
|
||||
if ($synchronize_calculated === false) {
|
||||
$state->options->datasetitem = rand(1, $maxnumber);
|
||||
protected function initialise_question_instance(question_definition $question, $questiondata) {
|
||||
question_type::initialise_question_instance($question, $questiondata);
|
||||
|
||||
$question->shuffleanswers = $questiondata->options->shuffleanswers;
|
||||
$question->answernumbering = $questiondata->options->answernumbering;
|
||||
if (!empty($questiondata->options->layout)) {
|
||||
$question->layout = $questiondata->options->layout;
|
||||
} else {
|
||||
$state->options->datasetitem =
|
||||
intval($maxnumber * substr($attempt->timestart, -2) /100);
|
||||
if ($state->options->datasetitem < 1) {
|
||||
$state->options->datasetitem =1;
|
||||
} else if ($state->options->datasetitem > $maxnumber) {
|
||||
$state->options->datasetitem = $maxnumber;
|
||||
}
|
||||
|
||||
};
|
||||
$state->options->dataset =
|
||||
$this->pick_question_dataset($question, $state->options->datasetitem);
|
||||
// create an array of answerids ??? why so complicated ???
|
||||
$answerids = array_values(array_map(create_function('$val',
|
||||
'return $val->id;'), $question->options->answers));
|
||||
// Shuffle the answers if required
|
||||
if (!empty($cmoptions->shuffleanswers) and !empty($question->options->shuffleanswers)) {
|
||||
$answerids = swapshuffle($answerids);
|
||||
$question->layout = qtype_multichoice_single_question::LAYOUT_VERTICAL;
|
||||
}
|
||||
$state->options->order = $answerids;
|
||||
// Create empty responses
|
||||
if ($question->options->single) {
|
||||
$state->responses = array('' => '');
|
||||
} else {
|
||||
$state->responses = array();
|
||||
|
||||
$this->initialise_combined_feedback($question, $questiondata, true);
|
||||
$this->initialise_question_answers($question, $questiondata);
|
||||
|
||||
foreach ($questiondata->options->answers as $a) {
|
||||
$question->answers[$a->id]->correctanswerlength = $a->correctanswerlength;
|
||||
$question->answers[$a->id]->correctanswerformat = $a->correctanswerformat;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function save_session_and_responses(&$question, &$state) {
|
||||
global $DB;
|
||||
$responses = 'dataset'.$state->options->datasetitem.'-';
|
||||
$responses .= implode(',', $state->options->order) . ':';
|
||||
$responses .= implode(',', $state->responses);
|
||||
|
||||
// Set the legacy answer field
|
||||
$DB->set_field('question_states', 'answer', $responses, array('id'=> $state->id));
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_default_numerical_unit($question, $virtualqtype) {
|
||||
$unit = '';
|
||||
return $unit;
|
||||
}
|
||||
|
||||
public function grade_responses(&$question, &$state, $cmoptions) {
|
||||
// Forward the grading to the virtual qtype
|
||||
// We modify the question to look like a multichoice question
|
||||
// for grading nothing to do
|
||||
$virtualqtype = $this->get_virtual_qtype($question);
|
||||
return $virtualqtype->grade_responses($question, $state, $cmoptions);
|
||||
}
|
||||
|
||||
public function create_virtual_qtype() {
|
||||
global $CFG;
|
||||
require_once("$CFG->dirroot/question/type/multichoice/questiontype.php");
|
||||
return new question_multichoice_qtype();
|
||||
$question->datasetloader = new qtype_calculated_dataset_loader($questiondata->id);
|
||||
}
|
||||
|
||||
public function comment_header($question) {
|
||||
@@ -325,8 +248,6 @@ class qtype_calculatedmulti extends qtype_calculated {
|
||||
$comment->answers = array();
|
||||
|
||||
$answers = fullclone($answers);
|
||||
$strmin = get_string('min');
|
||||
$strmax = get_string('max');
|
||||
$errors = '';
|
||||
$delimiter = ': ';
|
||||
foreach ($answers as $key => $answer) {
|
||||
@@ -355,62 +276,8 @@ class qtype_calculatedmulti extends qtype_calculated {
|
||||
return fullclone($comment);
|
||||
}
|
||||
|
||||
public function get_correct_responses1(&$question, &$state) {
|
||||
$virtualqtype = $this->get_vir(al_qtype($question));
|
||||
|
||||
return $virtualqtype->get_correct_responses($question, $state);
|
||||
}
|
||||
|
||||
public function get_virtual_qtype() {
|
||||
$this->virtualqtype = $QTYPES['multichoice'];
|
||||
return $this->virtualqtype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs all the code required to set up and save an essay question for testing purposes.
|
||||
* Alternate DB table prefix may be used to facilitate data deletion.
|
||||
*/
|
||||
public function generate_test($name, $courseid = null) {
|
||||
global $DB;
|
||||
list($form, $question) = parent::generate_test($name, $courseid);
|
||||
$form->feedback = 1;
|
||||
$form->multiplier = array(1, 1);
|
||||
$form->shuffleanswers = 1;
|
||||
$form->noanswers = 1;
|
||||
$form->qtype ='calculatedmulti';
|
||||
$question->qtype ='calculatedmulti';
|
||||
$form->answers = array('{a} + {b}');
|
||||
$form->fraction = array(1);
|
||||
$form->tolerance = array(0.01);
|
||||
$form->tolerancetype = array(1);
|
||||
$form->correctanswerlength = array(2);
|
||||
$form->correctanswerformat = array(1);
|
||||
$form->questiontext = "What is {a} + {b}?";
|
||||
|
||||
if ($courseid) {
|
||||
$course = $DB->get_record('course', array('id'=> $courseid));
|
||||
}
|
||||
|
||||
$new_question = $this->save_question($question, $form);
|
||||
|
||||
$dataset_form = new stdClass();
|
||||
$dataset_form->nextpageparam['forceregeneration']= 1;
|
||||
$dataset_form->calcmin = array(1 => 1.0, 2 => 1.0);
|
||||
$dataset_form->calcmax = array(1 => 10.0, 2 => 10.0);
|
||||
$dataset_form->calclength = array(1 => 1, 2 => 1);
|
||||
$dataset_form->number = array(1 => 5.4, 2 => 4.9);
|
||||
$dataset_form->itemid = array(1 => '', 2 => '');
|
||||
$dataset_form->calcdistribution = array(1 => 'uniform', 2 => 'uniform');
|
||||
$dataset_form->definition = array(1 => "1-0-a", 2 => "1-0-b");
|
||||
$dataset_form->nextpageparam = array('forceregeneration' => false);
|
||||
$dataset_form->addbutton = 1;
|
||||
$dataset_form->selectadd = 1;
|
||||
$dataset_form->courseid = $courseid;
|
||||
$dataset_form->cmid = 0;
|
||||
$dataset_form->id = $new_question->id;
|
||||
$this->save_dataset_items($new_question, $dataset_form);
|
||||
|
||||
return $new_question;
|
||||
return question_bank::get_qtype('multichoice');
|
||||
}
|
||||
|
||||
public function move_files($questionid, $oldcontextid, $newcontextid) {
|
||||
@@ -432,6 +299,7 @@ class qtype_calculatedmulti extends qtype_calculated {
|
||||
|
||||
parent::delete_files($questionid, $contextid);
|
||||
$this->delete_files_in_answers($questionid, $contextid, true);
|
||||
|
||||
$fs->delete_area_files($contextid, 'qtype_calculatedmulti',
|
||||
'correctfeedback', $questionid);
|
||||
$fs->delete_area_files($contextid, 'qtype_calculatedmulti',
|
||||
@@ -439,37 +307,4 @@ class qtype_calculatedmulti extends qtype_calculated {
|
||||
$fs->delete_area_files($contextid, 'qtype_calculatedmulti',
|
||||
'incorrectfeedback', $questionid);
|
||||
}
|
||||
|
||||
public function check_file_access($question, $state, $options, $contextid, $component,
|
||||
$filearea, $args) {
|
||||
$itemid = reset($args);
|
||||
|
||||
if (empty($question->maxgrade)) {
|
||||
$question->maxgrade = $question->defaultgrade;
|
||||
}
|
||||
|
||||
if (in_array($filearea, array('correctfeedback', 'partiallycorrectfeedback',
|
||||
'incorrectfeedback'))) {
|
||||
$result = $options->feedback && ($itemid == $question->id);
|
||||
if (!$result) {
|
||||
return false;
|
||||
}
|
||||
if ($state->raw_grade >= $question->maxgrade/1.01) {
|
||||
$feedbacktype = 'correctfeedback';
|
||||
} else if ($state->raw_grade > 0) {
|
||||
$feedbacktype = 'partiallycorrectfeedback';
|
||||
} else {
|
||||
$feedbacktype = 'incorrectfeedback';
|
||||
}
|
||||
if ($feedbacktype != $filearea) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else if ($component == 'question' && $filearea == 'answerfeedback') {
|
||||
return $options->feedback && array_key_exists($itemid, $question->options->answers);
|
||||
} else {
|
||||
return parent::check_file_access($question, $state, $options, $contextid, $component,
|
||||
$filearea, $args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Calculated multiple-choice question renderer class.
|
||||
*
|
||||
* @package qtype
|
||||
* @subpackage calculatedmulti
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/question/type/multichoice/renderer.php');
|
||||
|
||||
|
||||
/**
|
||||
* Generates the output for calculated multiple-choice questions.
|
||||
*
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class qtype_calculatedmulti_renderer extends qtype_numerical_renderer {
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Test helpers for the calculated question type.
|
||||
*
|
||||
* @package qtype
|
||||
* @subpackage calculatedmulti
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/question/type/calculated/question.php');
|
||||
|
||||
|
||||
/**
|
||||
* Test helper class for the calculated multiple-choice question type.
|
||||
*
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class qtype_calculatedmulti_test_helper extends question_test_helper {
|
||||
public function get_test_questions() {
|
||||
return array('sum');
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a calculated multiple-choice question about summing two numbers.
|
||||
* @return qtype_calculatedmulti_question
|
||||
*/
|
||||
public function make_calculatedmulti_question_sum() {
|
||||
// TODO
|
||||
question_bank::load_question_definition_classes('calculated');
|
||||
$q = new qtype_calculatedmulti_question();
|
||||
test_question_maker::initialise_a_question($q);
|
||||
$q->name = 'Simple sum';
|
||||
$q->questiontext = 'What is {a} + {b}?';
|
||||
$q->generalfeedback = 'Generalfeedback: {={a} + {b}} is the right answer.';
|
||||
$q->answers = array(
|
||||
13 => new qtype_numerical_answer(13, '{a} + {b}', 1.0, 'Very good.', FORMAT_HTML, 0),
|
||||
14 => new qtype_numerical_answer(14, '{a} - {b}', 0.0, 'Add. not subtract!.', FORMAT_HTML, 0),
|
||||
17 => new qtype_numerical_answer(17, '*', 0.0, 'Completely wrong.', FORMAT_HTML, 0),
|
||||
);
|
||||
$q->qtype = question_bank::get_qtype('calculated');
|
||||
$q->unitdisplay = qtype_numerical::UNITNONE;
|
||||
$q->unitgradingtype = 0;
|
||||
$q->unitpenalty = 0;
|
||||
$q->ap = new qtype_numerical_answer_processor(array());
|
||||
|
||||
$q->datasetloader = new qtype_calculated_test_dataset_loader(0, array(
|
||||
array('a' => 1, 'b' => 5),
|
||||
array('a' => 3, 'b' => 4),
|
||||
));
|
||||
|
||||
return $q;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test implementation of {@link qtype_calculated_dataset_loader}. Gets the values
|
||||
* from an array passed to the constructor, rather than querying the database.
|
||||
*
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class qtype_calculated_test_dataset_loader extends qtype_calculated_dataset_loader{
|
||||
protected $valuesets;
|
||||
|
||||
public function __construct($questionid, array $valuesets) {
|
||||
parent::__construct($questionid);
|
||||
$this->valuesets = $valuesets;
|
||||
}
|
||||
|
||||
public function get_number_of_items() {
|
||||
return count($this->valuesets);
|
||||
}
|
||||
|
||||
public function load_values($itemnumber) {
|
||||
return $this->valuesets[$itemnumber - 1];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?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/>.
|
||||
|
||||
|
||||
/**
|
||||
* Unit tests for the calculated multiple-choice question definition class.
|
||||
*
|
||||
* @package qtype
|
||||
* @subpackage calculatedmulti
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
|
||||
require_once($CFG->dirroot . '/question/engine/simpletest/helpers.php');
|
||||
|
||||
|
||||
/**
|
||||
* Unit tests for qtype_calculatedmulti_definition.
|
||||
*
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class qtype_calculatedmulti_question_test extends UnitTestCase {
|
||||
public function test_is_complete_response() {
|
||||
$question = test_question_maker::make_question('calculated');
|
||||
|
||||
$this->assertFalse($question->is_complete_response(array()));
|
||||
$this->assertTrue($question->is_complete_response(array('answer' => '0')));
|
||||
$this->assertTrue($question->is_complete_response(array('answer' => 0)));
|
||||
$this->assertFalse($question->is_complete_response(array('answer' => 'test')));
|
||||
}
|
||||
|
||||
public function test_is_gradable_response() {
|
||||
$question = test_question_maker::make_question('calculated');
|
||||
|
||||
$this->assertFalse($question->is_gradable_response(array()));
|
||||
$this->assertTrue($question->is_gradable_response(array('answer' => '0')));
|
||||
$this->assertTrue($question->is_gradable_response(array('answer' => 0)));
|
||||
$this->assertTrue($question->is_gradable_response(array('answer' => 'test')));
|
||||
}
|
||||
|
||||
public function test_grading() {
|
||||
$question = test_question_maker::make_question('calculated');
|
||||
$question->start_attempt(new question_attempt_step());
|
||||
$values = $question->vs->get_values();
|
||||
|
||||
$this->assertEqual(array(0, question_state::$gradedwrong),
|
||||
$question->grade_response(array('answer' => $values['a'] - $values['b'])));
|
||||
$this->assertEqual(array(1, question_state::$gradedright),
|
||||
$question->grade_response(array('answer' => $values['a'] + $values['b'])));
|
||||
}
|
||||
|
||||
public function test_get_correct_response() {
|
||||
$question = test_question_maker::make_question('calculated');
|
||||
$question->start_attempt(new question_attempt_step());
|
||||
$values = $question->vs->get_values();
|
||||
|
||||
$this->assertEqual(array('answer' => $values['a'] + $values['b']),
|
||||
$question->get_correct_response());
|
||||
}
|
||||
|
||||
public function test_get_question_summary() {
|
||||
$question = test_question_maker::make_question('calculated');
|
||||
$question->start_attempt(new question_attempt_step());
|
||||
$values = $question->vs->get_values();
|
||||
|
||||
$qsummary = $question->get_question_summary();
|
||||
$this->assertEqual('What is ' . $values['a'] . ' + ' . $values['b'] . '?', $qsummary);
|
||||
}
|
||||
|
||||
public function test_summarise_response() {
|
||||
$question = test_question_maker::make_question('calculated');
|
||||
$question->start_attempt(new question_attempt_step());
|
||||
$values = $question->vs->get_values();
|
||||
|
||||
$this->assertEqual('3.1', $question->summarise_response(array('answer' => '3.1')));
|
||||
}
|
||||
|
||||
public function test_classify_response() {
|
||||
$question = test_question_maker::make_question('calculated');
|
||||
$question->start_attempt(new question_attempt_step());
|
||||
$values = $question->vs->get_values();
|
||||
|
||||
$this->assertEqual(array(
|
||||
new question_classified_response(13, $values['a'] + $values['b'], 1.0)),
|
||||
$question->classify_response(array('answer' => $values['a'] + $values['b'])));
|
||||
$this->assertEqual(array(
|
||||
new question_classified_response(14, $values['a'] - $values['b'], 0.0)),
|
||||
$question->classify_response(array('answer' => $values['a'] - $values['b'])));
|
||||
$this->assertEqual(array(
|
||||
new question_classified_response(17, 7 * $values['a'], 0.0)),
|
||||
$question->classify_response(array('answer' => 7 * $values['a'])));
|
||||
$this->assertEqual(array(
|
||||
question_classified_response::no_response()),
|
||||
$question->classify_response(array('answer' => '')));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* This file contains overall tests of calculated multiple-choice questions.
|
||||
*
|
||||
* @package qtype
|
||||
* @subpackage calculatedmulti
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot . '/question/engine/simpletest/helpers.php');
|
||||
|
||||
|
||||
/**
|
||||
* Unit tests for the calculated multiple-choice question type.
|
||||
*
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class qtype_calculatedmulti_walkthrough_test extends qbehaviour_walkthrough_test_base {
|
||||
public function test_interactive() {
|
||||
|
||||
// Create a gapselect question.
|
||||
$q = test_question_maker::make_question('calculated');
|
||||
$q->hints = array(
|
||||
new question_hint(1, 'This is the first hint.', FORMAT_HTML),
|
||||
new question_hint(2, 'This is the second hint.', FORMAT_HTML),
|
||||
);
|
||||
$this->start_attempt_at_question($q, 'interactive', 3);
|
||||
$values = $q->vs->get_values();
|
||||
|
||||
// Check the initial state.
|
||||
$this->check_current_state(question_state::$todo);
|
||||
$this->check_current_mark(null);
|
||||
$this->check_current_output(
|
||||
$this->get_contains_marked_out_of_summary(),
|
||||
$this->get_contains_submit_button_expectation(true),
|
||||
$this->get_does_not_contain_feedback_expectation(),
|
||||
$this->get_does_not_contain_validation_error_expectation(),
|
||||
$this->get_does_not_contain_try_again_button_expectation(),
|
||||
$this->get_no_hint_visible_expectation());
|
||||
|
||||
// Submit blank.
|
||||
$this->process_submission(array('-submit' => 1, 'answer' => ''));
|
||||
|
||||
// Verify.
|
||||
$this->check_current_state(question_state::$invalid);
|
||||
$this->check_current_mark(null);
|
||||
$this->check_current_output(
|
||||
$this->get_contains_marked_out_of_summary(),
|
||||
$this->get_contains_submit_button_expectation(true),
|
||||
$this->get_does_not_contain_feedback_expectation(),
|
||||
$this->get_contains_validation_error_expectation(),
|
||||
$this->get_does_not_contain_try_again_button_expectation(),
|
||||
$this->get_no_hint_visible_expectation());
|
||||
|
||||
// Sumit something that does not look like a number.
|
||||
$this->process_submission(array('-submit' => 1, 'answer' => 'newt'));
|
||||
|
||||
// Verify.
|
||||
$this->check_current_state(question_state::$invalid);
|
||||
$this->check_current_mark(null);
|
||||
$this->check_current_output(
|
||||
$this->get_contains_marked_out_of_summary(),
|
||||
$this->get_contains_submit_button_expectation(true),
|
||||
$this->get_does_not_contain_feedback_expectation(),
|
||||
$this->get_contains_validation_error_expectation(),
|
||||
new PatternExpectation('/' .
|
||||
preg_quote(get_string('invalidnumber', 'qtype_numerical') . '/')),
|
||||
$this->get_does_not_contain_try_again_button_expectation(),
|
||||
$this->get_no_hint_visible_expectation());
|
||||
|
||||
// Now get it right.
|
||||
$this->process_submission(array('-submit' => 1, 'answer' => $values['a'] + $values['b']));
|
||||
|
||||
// Verify.
|
||||
$this->check_current_state(question_state::$gradedright);
|
||||
$this->check_current_mark(3);
|
||||
$this->check_current_output(
|
||||
$this->get_contains_mark_summary(3),
|
||||
$this->get_contains_submit_button_expectation(false),
|
||||
$this->get_contains_correct_expectation(),
|
||||
$this->get_does_not_contain_validation_error_expectation(),
|
||||
$this->get_no_hint_visible_expectation());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
.que.calculatedmulti .answer .specificfeedback {
|
||||
display: inline;
|
||||
padding: 0 0.7em;
|
||||
background: #FFF3BF;
|
||||
}
|
||||
.que.calculatedmulti .answer .specificfeedback * {
|
||||
display: inline;
|
||||
background: #FFF3BF;
|
||||
}
|
||||
.que.calculatedmulti .answer div.r0,
|
||||
.que.calculatedmulti .answer div.r1 {
|
||||
padding: 0.3em;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Version information for the calculated multiple-choice question type.
|
||||
*
|
||||
* @package qtype
|
||||
* @subpackage calculatedmulti
|
||||
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2011051900;
|
||||
$plugin->requires = 2011051212;
|
||||
@@ -70,7 +70,6 @@ class qtype_calculatedsimple_edit_form extends qtype_calculated_edit_form {
|
||||
public $formdata = array();
|
||||
|
||||
public function __construct($submiturl, $question, $category, $contexts, $formeditable = true) {
|
||||
global $SESSION, $CFG, $DB;
|
||||
$this->regenerate = true;
|
||||
$this->question = $question;
|
||||
|
||||
|
||||
@@ -41,9 +41,8 @@ class qtype_calculatedsimple_test_helper extends question_test_helper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a calculated question with correct ansewer 3.14, and various incorrect
|
||||
* answers with different feedback.
|
||||
* @return qtype_calculated_question
|
||||
* Makes a simple calculated question about summing two numbers.
|
||||
* @return qtype_calculatedsimple_question
|
||||
*/
|
||||
public function make_calculatedsimple_question_sum() {
|
||||
question_bank::load_question_definition_classes('calculatedsimple');
|
||||
@@ -52,11 +51,17 @@ class qtype_calculatedsimple_test_helper extends question_test_helper {
|
||||
$q->name = 'Simple sum';
|
||||
$q->questiontext = 'What is {a} + {b}?';
|
||||
$q->generalfeedback = 'Generalfeedback: {={a} + {b}} is the right answer.';
|
||||
|
||||
$q->answers = array(
|
||||
13 => new qtype_numerical_answer(13, '{a} + {b}', 1.0, 'Very good.', FORMAT_HTML, 0),
|
||||
14 => new qtype_numerical_answer(14, '{a} - {b}', 0.0, 'Add. not subtract!.', FORMAT_HTML, 0),
|
||||
17 => new qtype_numerical_answer(17, '*', 0.0, 'Completely wrong.', FORMAT_HTML, 0),
|
||||
);
|
||||
foreach ($q->answers as $answer) {
|
||||
$answer->correctanswerlength = 2;
|
||||
$answer->correctanswerformat = 1;
|
||||
}
|
||||
|
||||
$q->qtype = question_bank::get_qtype('calculated');
|
||||
$q->unitdisplay = qtype_numerical::UNITNONE;
|
||||
$q->unitgradingtype = 0;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
.que.calculatedsimple .answer {
|
||||
padding: 0.3em;
|
||||
width: auto;
|
||||
display: inline;
|
||||
}
|
||||
.que.calculatedsimple .answer input {
|
||||
width: 30%;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Version information for the simple calculated question type.
|
||||
*
|
||||
* @package qtype
|
||||
* @subpackage calculatedsimple
|
||||
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2011051900;
|
||||
$plugin->requires = 2011051212;
|
||||
@@ -252,6 +252,7 @@ class qtype_multichoice extends question_type {
|
||||
|
||||
parent::delete_files($questionid, $contextid);
|
||||
$this->delete_files_in_answers($questionid, $contextid, true);
|
||||
|
||||
$fs->delete_area_files($contextid,
|
||||
'qtype_multichoice', 'correctfeedback', $questionid);
|
||||
$fs->delete_area_files($contextid,
|
||||
|
||||
Reference in New Issue
Block a user