.
/**
* Question type class for the calculated multiple-choice question type.
*
* @package qtype
* @subpackage calculatedmulti
* @copyright 2009 Pierre Pichet
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
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.
*
* @copyright 2009 Pierre Pichet
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qtype_calculatedmulti extends qtype_calculated {
public function save_question_options($question) {
global $CFG, $DB;
$context = $question->context;
// Make it impossible to save bad formulas anywhere.
$this->validate_question_data($question);
// Calculated options.
$update = true;
$options = $DB->get_record('question_calculated_options',
array('question' => $question->id));
if (!$options) {
$options = new stdClass();
$options->question = $question->id;
$options->correctfeedback = '';
$options->partiallycorrectfeedback = '';
$options->incorrectfeedback = '';
$options->id = $DB->insert_record('question_calculated_options', $options);
}
$options->synchronize = $question->synchronize;
$options->single = $question->single;
$options->answernumbering = $question->answernumbering;
$options->shuffleanswers = $question->shuffleanswers;
$options = $this->save_combined_feedback_helper($options, $question, $context, true);
$DB->update_record('question_calculated_options', $options);
// Get old versions of the objects.
if (!$oldanswers = $DB->get_records('question_answers',
array('question' => $question->id), 'id ASC')) {
$oldanswers = array();
}
if (!$oldoptions = $DB->get_records('question_calculated',
array('question' => $question->id), 'answer ASC')) {
$oldoptions = array();
}
// Insert all the new answers.
foreach ($question->answer as $key => $answerdata) {
if (is_array($answerdata)) {
$answerdata = $answerdata['text'];
}
if (trim($answerdata) == '') {
continue;
}
// Update an existing answer if possible.
$answer = array_shift($oldanswers);
if (!$answer) {
$answer = new stdClass();
$answer->question = $question->id;
$answer->answer = '';
$answer->feedback = '';
$answer->id = $DB->insert_record('question_answers', $answer);
}
if (is_array($answerdata)) {
// Doing an import.
$answer->answer = $this->import_or_save_files($answerdata,
$context, 'question', 'answer', $answer->id);
$answer->answerformat = $answerdata['format'];
} else {
// Saving the form.
$answer->answer = $answerdata;
$answer->answerformat = FORMAT_HTML;
}
$answer->fraction = $question->fraction[$key];
$answer->feedback = $this->import_or_save_files($question->feedback[$key],
$context, 'question', 'answerfeedback', $answer->id);
$answer->feedbackformat = $question->feedback[$key]['format'];
$DB->update_record("question_answers", $answer);
// Set up the options object.
if (!$options = array_shift($oldoptions)) {
$options = new stdClass();
}
$options->question = $question->id;
$options->answer = $answer->id;
$options->tolerance = trim($question->tolerance[$key]);
$options->tolerancetype = trim($question->tolerancetype[$key]);
$options->correctanswerlength = trim($question->correctanswerlength[$key]);
$options->correctanswerformat = trim($question->correctanswerformat[$key]);
// Save options.
if (isset($options->id)) {
// Reusing existing record.
$DB->update_record('question_calculated', $options);
} else {
// New options.
$DB->insert_record('question_calculated', $options);
}
}
// Delete old answer records.
if (!empty($oldanswers)) {
foreach ($oldanswers as $oa) {
$DB->delete_records('question_answers', array('id' => $oa->id));
}
}
if (!empty($oldoptions)) {
foreach ($oldoptions as $oo) {
$DB->delete_records('question_calculated', array('id' => $oo->id));
}
}
$this->save_hints($question, true);
if (isset($question->import_process) && $question->import_process) {
$this->import_datasets($question);
}
// Report any problems.
if (!empty($result->notice)) {
return $result;
}
return true;
}
protected function validate_answer($answer) {
$error = qtype_calculated_find_formula_errors_in_text($answer);
if ($error) {
throw new coding_exception($error);
}
}
protected function validate_question_data($question) {
parent::validate_question_data($question);
$this->validate_text($question->correctfeedback['text']);
$this->validate_text($question->partiallycorrectfeedback['text']);
$this->validate_text($question->incorrectfeedback['text']);
}
protected function make_question_instance($questiondata) {
question_bank::load_question_definition_classes($this->name());
if ($questiondata->options->single) {
$class = 'qtype_calculatedmulti_single_question';
} else {
$class = 'qtype_calculatedmulti_multi_question';
}
return new $class();
}
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 {
$question->layout = qtype_multichoice_single_question::LAYOUT_VERTICAL;
}
$question->synchronised = $questiondata->options->synchronize;
$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;
}
$question->datasetloader = new qtype_calculated_dataset_loader($questiondata->id);
}
public function comment_header($question) {
$strheader = '';
$delimiter = '';
$answers = $question->options->answers;
foreach ($answers as $key => $answer) {
$ans = shorten_text($answer->answer, 17, true);
$strheader .= $delimiter.$ans;
$delimiter = '
';
}
return $strheader;
}
public function comment_on_datasetitems($qtypeobj, $questionid, $questiontext,
$answers, $data, $number) {
global $DB;
$comment = new stdClass();
$comment->stranswers = array();
$comment->outsidelimit = false;
$comment->answers = array();
$answers = fullclone($answers);
$errors = '';
$delimiter = ': ';
foreach ($answers as $key => $answer) {
$anssubstituted = $this->substitute_variables($answer->answer, $data);
// Evaluate the equations i.e {=5+4).
$anstext = '';
$anstextremaining = $anssubstituted;
while (preg_match('~\{=([^[:space:]}]*)}~', $anstextremaining, $regs1)) {
$anstextsplits = explode($regs1[0], $anstextremaining, 2);
$anstext =$anstext.$anstextsplits[0];
$anstextremaining = $anstextsplits[1];
if (empty($regs1[1])) {
$str = '';
} else {
if ($formulaerrors = qtype_calculated_find_formula_errors($regs1[1])) {
$str=$formulaerrors;
} else {
eval('$str = '.$regs1[1].';');
}
}
$anstext = $anstext.$str;
}
$anstext .= $anstextremaining;
$comment->stranswers[$key] = $anssubstituted.'
'.$anstext;
}
return fullclone($comment);
}
public function get_virtual_qtype() {
return question_bank::get_qtype('multichoice');
}
public function get_possible_responses($questiondata) {
if ($questiondata->options->single) {
$responses = array();
foreach ($questiondata->options->answers as $aid => $answer) {
$responses[$aid] = new question_possible_response($answer->answer,
$answer->fraction);
}
$responses[null] = question_possible_response::no_response();
return array($questiondata->id => $responses);
} else {
$parts = array();
foreach ($questiondata->options->answers as $aid => $answer) {
$parts[$aid] = array($aid =>
new question_possible_response($answer->answer, $answer->fraction));
}
return $parts;
}
}
public function move_files($questionid, $oldcontextid, $newcontextid) {
$fs = get_file_storage();
parent::move_files($questionid, $oldcontextid, $newcontextid);
$this->move_files_in_answers($questionid, $oldcontextid, $newcontextid, true);
$this->move_files_in_hints($questionid, $oldcontextid, $newcontextid);
$fs->move_area_files_to_new_context($oldcontextid,
$newcontextid, 'qtype_calculatedmulti', 'correctfeedback', $questionid);
$fs->move_area_files_to_new_context($oldcontextid,
$newcontextid, 'qtype_calculatedmulti', 'partiallycorrectfeedback', $questionid);
$fs->move_area_files_to_new_context($oldcontextid,
$newcontextid, 'qtype_calculatedmulti', 'incorrectfeedback', $questionid);
}
protected function delete_files($questionid, $contextid) {
$fs = get_file_storage();
parent::delete_files($questionid, $contextid);
$this->delete_files_in_answers($questionid, $contextid, true);
$this->delete_files_in_hints($questionid, $contextid);
$fs->delete_area_files($contextid, 'qtype_calculatedmulti',
'correctfeedback', $questionid);
$fs->delete_area_files($contextid, 'qtype_calculatedmulti',
'partiallycorrectfeedback', $questionid);
$fs->delete_area_files($contextid, 'qtype_calculatedmulti',
'incorrectfeedback', $questionid);
}
}