From 5712bf7b91964e6dbbd2afdead94bb3dd8c0030a Mon Sep 17 00:00:00 2001 From: Jean-Michel Vedrine Date: Fri, 4 Jan 2013 14:24:55 +0100 Subject: Ability to use Mathjax for TeX rendering --- questiontype.php | 393 +++++++++++++++++++++++++++---------------------------- 1 file changed, 196 insertions(+), 197 deletions(-) (limited to 'questiontype.php') diff --git a/questiontype.php b/questiontype.php index dfd9f3b..0d816e3 100644 --- a/questiontype.php +++ b/questiontype.php @@ -17,9 +17,8 @@ /** * Question type class for the algebra question type. * - * @package qtype - * @subpackage algebra - * @author Roger Moore + * @package qtype_algebra + * @author Roger Moore * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -45,8 +44,8 @@ class qtype_algebra extends question_type { * @return an array with the table name (first) and then the column names (apart from id and questionid) */ public function extra_question_fields() { - return array('question_algebra', - 'compareby', // Name of comparison algorithm to use + return array('qtype_algebra', + 'compareby', // Name of comparison algorithm to use 'nchecks', // Number of evaluate checks to make when comparing by evaluation 'tolerance', // Max. fractional difference allowed for evaluation checks 'allowedfuncs', // Comma separated list of functions allowed in responses @@ -55,11 +54,11 @@ class qtype_algebra extends question_type { ); } - public function questionid_column_name() { + public function questionid_column_name() { return 'questionid'; } - public function move_files($questionid, $oldcontextid, $newcontextid) { + public function move_files($questionid, $oldcontextid, $newcontextid) { parent::move_files($questionid, $oldcontextid, $newcontextid); $this->move_files_in_answers($questionid, $oldcontextid, $newcontextid); } @@ -68,45 +67,45 @@ class qtype_algebra extends question_type { parent::delete_files($questionid, $contextid); $this->delete_files_in_answers($questionid, $contextid); } - - public function delete_question($questionid, $contextid) { + + public function delete_question($questionid, $contextid) { global $DB; - $DB->delete_records('question_algebra', array('questionid' => $questionid)); - $DB->delete_records('question_algebra_variables', array('question' => $questionid)); + $DB->delete_records('qtype_algebra', array('questionid' => $questionid)); + $DB->delete_records('qtype_algebra_variables', array('question' => $questionid)); parent::delete_question($questionid, $contextid); } - /** - * Saves the questions variables to the database - * - * This is called by {@link save_question_options()} to save the variables of the question to - * the database from the data in the submitted form. The method returns an array of the variables - * IDs written to the database or, in the event of an error, throws an exception. - * - * @param object $question This holds the information from the editing form, - * it is not a standard question object. - * @return array of variable object IDs - */ + /** + * Saves the questions variables to the database + * + * This is called by {@link save_question_options()} to save the variables of the question to + * the database from the data in the submitted form. The method returns an array of the variables + * IDs written to the database or, in the event of an error, throws an exception. + * + * @param object $question This holds the information from the editing form, + * it is not a standard question object. + * @return array of variable object IDs + */ public function save_question_variables($question) { - global $DB; - // Create the results class + global $DB; + // Create the results class $result = new stdClass; - // Get all the old answers from the database as an array - if (!$oldvars = $DB->get_records('question_algebra_variables', array('question' => $question->id), 'id ASC')) { + // Get all the old answers from the database as an array + if (!$oldvars = $DB->get_records('qtype_algebra_variables', array('question' => $question->id), 'id ASC')) { $oldvars = array(); } - // Create an array of the variable IDs for the question + // Create an array of the variable IDs for the question $variables = array(); - + // Loop over all the answers in the question form and write them to the database foreach ($question->variable as $key => $varname) { - // Check to see that there is a variable and skip any which are empty + // Check to see that there is a variable and skip any which are empty if ($varname == '') { continue; } - - // Get the old variable from the array and overwrite what is required, if there + + // Get the old variable from the array and overwrite what is required, if there // is no old variable then we skip to the 'else' clause if ($oldvar = array_shift($oldvars)) { // Existing variable, so reuse it $var = $oldvar; @@ -114,7 +113,7 @@ class qtype_algebra extends question_type { $var->min = trim($question->varmin[$key]); $var->max = trim($question->varmax[$key]); // Update the record in the database to denote this change. - if (!$DB->update_record('question_algebra_variables', $var)) { + if (!$DB->update_record('qtype_algebra_variables', $var)) { throw new Exception("Could not update algebra question variable (id=$var->id)"); } } @@ -126,7 +125,7 @@ class qtype_algebra extends question_type { $var->min = trim($question->varmin[$key]); $var->max = trim($question->varmax[$key]); // Insert a new record into the database table - if (!$var->id = $DB->insert_record('question_algebra_variables', $var)) { + if (!$var->id = $DB->insert_record('qtype_algebra_variables', $var)) { throw new Exception("Could not insert algebra question variable '$varname'!"); } } @@ -134,44 +133,44 @@ class qtype_algebra extends question_type { $variables[] = $var->id; } // end loop over variables - // Delete any left over old variables records. + // Delete any left over old variables records. foreach ($oldvars as $oldvar) { - $DB->delete_records('question_algebra_variables', array('id' => $oldvar->id)); - } - // Finally we are all done so return the result! - return $variables; - } - - /** - * Saves the questions answers to the database - * - * This is called by {@link save_question_options()} to save the answers to the question to - * the database from the data in the submitted form. This method should probably be in the - * questin base class rather than in the algebra subclass since the code is common to multiple - * question types and originally comes from the shortanswer question type. The method returns + $DB->delete_records('qtype_algebra_variables', array('id' => $oldvar->id)); + } + // Finally we are all done so return the result! + return $variables; + } + + /** + * Saves the questions answers to the database + * + * This is called by {@link save_question_options()} to save the answers to the question to + * the database from the data in the submitted form. This method should probably be in the + * questin base class rather than in the algebra subclass since the code is common to multiple + * question types and originally comes from the shortanswer question type. The method returns * a list of the answer ID written to the database or throws an exception if an error is detected. - * - * @param object $question This holds the information from the editing form, - * it is not a standard question object. - * @return array of answer IDs which were written to the database - */ + * + * @param object $question This holds the information from the editing form, + * it is not a standard question object. + * @return array of answer IDs which were written to the database + */ public function save_question_answers($question) { - global $CFG, $DB; - - $context = $question->context; - // Create the results class + global $CFG, $DB; + + $context = $question->context; + // Create the results class $result = new stdClass; - - // Get all the old answers from the database as an array + + // Get all the old answers from the database as an array if (!$oldanswers = $DB->get_records('question_answers', array('question' => $question->id), 'id ASC')) { //'question', $question->'id ASC')) { $oldanswers = array(); } - // Create an array of the answer IDs for the question + // Create an array of the answer IDs for the question $answers = array(); - // Set the maximum answer fraction to be -1. We will check this at the end of our - // loop over the questions and if it is not 100% (=1.0) then we will flag an error + // Set the maximum answer fraction to be -1. We will check this at the end of our + // loop over the questions and if it is not 100% (=1.0) then we will flag an error $maxfraction = -1; - + // Loop over all the answers in the question form and write them to the database foreach ($question->answer as $key => $answerdata) { // Check for, and ignore, completely blank answer from the form. @@ -179,16 +178,16 @@ class qtype_algebra extends question_type { html_is_blank($question->feedback[$key]['text'])) { continue; } - // Update an existing answer if possible. + // Update an existing answer if possible. $answer = array_shift($oldanswers); if (!$answer) { $answer = new stdClass(); $answer->question = $question->id; $answer->answer = ''; $answer->feedback = ''; - $answer->feedbackformat = FORMAT_HTML; - if (!$answer->id = $DB->insert_record('question_answers', $answer)) { - throw new Exception("Could not create new algebra question answer"); + $answer->feedbackformat = FORMAT_HTML; + if (!$answer->id = $DB->insert_record('question_answers', $answer)) { + throw new Exception("Could not create new algebra question answer"); } } @@ -196,53 +195,53 @@ class qtype_algebra extends question_type { $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']; - if (!$DB->update_record('question_answers', $answer)) { + $answer->feedbackformat = $question->feedback[$key]['format']; + if (!$DB->update_record('question_answers', $answer)) { throw new Exception("Could not update algebra question answer (id=$answer->id)"); } - + $answers[] = $answer->id; if ($question->fraction[$key] > $maxfraction) { $maxfraction = $question->fraction[$key]; } } // end loop over answers - - // Perform sanity check on the maximum fractional grade which should be 100% + + // Perform sanity check on the maximum fractional grade which should be 100% if ($maxfraction != 1) { $maxfraction = $maxfraction * 100; throw new Exception(get_string('fractionsnomax', 'quiz', $maxfraction)); } - - // Delete any left over old answer records. + + // Delete any left over old answer records. $fs = get_file_storage(); foreach ($oldanswers as $oldanswer) { $fs->delete_area_files($context->id, 'question', 'answerfeedback', $oldanswer->id); $DB->delete_records('question_answers', array('id' => $oldanswer->id)); } - // Finally we are all done so return the result! - return $answers; - } - + // Finally we are all done so return the result! + return $answers; + } + /** - * Saves question-type specific options - * - * This is called by {@link save_question()} to save the question-type specific data from a - * submitted form. This method takes the form data and formats into the correct format for - * writing to the database. It then calls the parent method to actually write the data. - * - * @param object $question This holds the information from the editing form, - * it is not a standard question object. - * @return object $result->error or $result->noticeyesno or $result->notice - */ + * Saves question-type specific options + * + * This is called by {@link save_question()} to save the question-type specific data from a + * submitted form. This method takes the form data and formats into the correct format for + * writing to the database. It then calls the parent method to actually write the data. + * + * @param object $question This holds the information from the editing form, + * it is not a standard question object. + * @return object $result->error or $result->noticeyesno or $result->notice + */ public function save_question_options($question) { - // Start a try block to catch any exceptions generated when we attempt to parse and + // Start a try block to catch any exceptions generated when we attempt to parse and // then add the answers and variables to the database try { // First write out all the variables associated with the question - $variables=$this->save_question_variables($question); - + $variables=$this->save_question_variables($question); + // Loop over all the answers in the question form and parse them to generate // a parser string. This ensures a constant formatting is stored in the database foreach ($question->answer as &$answer) { @@ -262,7 +261,7 @@ class qtype_algebra extends question_type { $result->error=$e->getMessage(); return $result; } - + // Process the allowed functions field. This code just sets up the variable, it is saved // in the parent class' save_question_options method called at the end of this method // Look for the 'all' option. If we find it then set the string to an empty value @@ -275,61 +274,61 @@ class qtype_algebra extends question_type { // keys of the array $question->allowedfuncs=implode(',',array_keys($question->allowedfuncs)); } - - // Call the parent method to write the extensions fields to the database. This either returns null - // or an error object so if we get anything then return it otherwise return our existing + + // Call the parent method to write the extensions fields to the database. This either returns null + // or an error object so if we get anything then return it otherwise return our existing $parentresult = parent::save_question_options($question); if ($parentresult !== null) { // Parent function returns null if all is OK return $parentresult; } - // Otherwise just return true - this mimics the shortanswer return format - else { - return true; - } - } - + // Otherwise just return true - this mimics the shortanswer return format + else { + return true; + } + } + /** - * Loads the question type specific options for the question. - * - * This function loads the compare algorithm type, disallowed strings and variables - * into the class from the database table in which they are stored. It first uses the - * parent class method to get the database information. - * - * @param object $question The question object for the question. This object - * should be updated to include the question type - * specific information (it is passed by reference). - * @return bool Indicates success or failure. - */ + * Loads the question type specific options for the question. + * + * This function loads the compare algorithm type, disallowed strings and variables + * into the class from the database table in which they are stored. It first uses the + * parent class method to get the database information. + * + * @param object $question The question object for the question. This object + * should be updated to include the question type + * specific information (it is passed by reference). + * @return bool Indicates success or failure. + */ public function get_question_options($question) { - // Get the information from the database table. If this fails then immediately bail. + // Get the information from the database table. If this fails then immediately bail. // Note unlike the save_question_options base class method this method DOES get the question's // answers along with any answer extensions global $DB; - if(!parent::get_question_options($question)) { - return false; - } - // Check that we have answers and if not then bail since this question type requires answers - if(count($question->options->answers)==0) { - notify('Failed to load question answers from the table question_answers for questionid ' . - $question->id); - return false; - } + if(!parent::get_question_options($question)) { + return false; + } + // Check that we have answers and if not then bail since this question type requires answers + if(count($question->options->answers)==0) { + notify('Failed to load question answers from the table question_answers for questionid ' . + $question->id); + return false; + } // Now get the variables from the database as well - $question->options->variables = $DB->get_records('question_algebra_variables', array('question' => $question->id)); - - - - //, 'id ASC'); - // Check that we have variables and if not then bail since this question type requires variables - - if(count($question->options->variables)==0) { - notify('Failed to load question variables from the table question_algebra_variables '. + $question->options->variables = $DB->get_records('qtype_algebra_variables', array('question' => $question->id)); + + + + //, 'id ASC'); + // Check that we have variables and if not then bail since this question type requires variables + + if(count($question->options->variables)==0) { + notify('Failed to load question variables from the table qtype_algebra_variables '. "for questionid $question->id"); - return false; - } - - + return false; + } + + // Check to see if there are any allowed functions if($question->options->allowedfuncs!='') { // Extract the allowed functions as an array @@ -339,15 +338,15 @@ class qtype_algebra extends question_type { else { $question->options->allowedfuncs=array(); } - + // Everything worked so return true return true; - } + } /** * Imports the question from Moodle XML format. * - * This method is called by the format class when importing an algebra question from the + * This method is called by the format class when importing an algebra question from the * Moodle XML format. * * @param $data structure containing the XML data @@ -356,35 +355,35 @@ class qtype_algebra extends question_type { * @param $extra extra information (not required for importing this question in this format) * @return text string containing the question data in XML format */ - public function import_from_xml($data, $question, qformat_xml $format, $extra=null) { - if (!array_key_exists('@', $data)) { + public function import_from_xml($data, $question, qformat_xml $format, $extra=null) { + if (!array_key_exists('@', $data)) { return false; } if (!array_key_exists('type', $data['@'])) { return false; } if ($data['@']['type'] == 'algebra') { - // Import the common question headers - $qo = $format->import_headers($data); - // Set the question type - $qo->qtype='algebra'; - - $qo->compareby = $format->getpath($data, array('#','compareby',0,'#'),'eval'); - $qo->tolerance = $format->getpath($data, array('#','tolerance',0,'#'),'0'); - $qo->nchecks = $format->getpath($data, array('#','nchecks',0,'#'),'10'); - $qo->disallow = $format->getpath($data, array('#','disallow',0,'#','text',0,'#'),'',true); - $allowedfuncs = $format->getpath($data, array('#','allowedfuncs',0,'#'), ''); - if($allowedfuncs=='') { - $qo->allowedfuncs=array('all' => 1); - } - // Need to separate the allowed functions into an array of strings and then - // flip the values of this array into the keys because this is what the + // Import the common question headers + $qo = $format->import_headers($data); + // Set the question type + $qo->qtype='algebra'; + + $qo->compareby = $format->getpath($data, array('#','compareby',0,'#'),'eval'); + $qo->tolerance = $format->getpath($data, array('#','tolerance',0,'#'),'0'); + $qo->nchecks = $format->getpath($data, array('#','nchecks',0,'#'),'10'); + $qo->disallow = $format->getpath($data, array('#','disallow',0,'#','text',0,'#'),'',true); + $allowedfuncs = $format->getpath($data, array('#','allowedfuncs',0,'#'), ''); + if($allowedfuncs=='') { + $qo->allowedfuncs=array('all' => 1); + } + // Need to separate the allowed functions into an array of strings and then + // flip the values of this array into the keys because this is what the // save options method requires else { $qo->allowedfuncs=array_flip(explode(',',$allowedfuncs)); } $qo->answerprefix = $format->getpath($data, array('#','answerprefix',0,'#','text',0,'#'),'',true); - + // Import all the answers $answers = $data['#']['answer']; $a_count = 0; @@ -397,9 +396,9 @@ class qtype_algebra extends question_type { $qo->feedback[$a_count] = $ans->feedback; ++$a_count; } - + // Import all the variables - $vars = $data['#']['variable']; + $vars = $data['#']['variable']; $v_count = 0; // Loop over each answer block found in the XML foreach($vars as $var) { @@ -409,14 +408,14 @@ class qtype_algebra extends question_type { ++$v_count; } - $format->import_hints($qo, $data); + $format->import_hints($qo, $data); return $qo; - } - return false; + } + return false; } - - + + /** * Exports the question to Moodle XML format. * @@ -428,7 +427,7 @@ class qtype_algebra extends question_type { * @param $extra extra information (not required for exporting this question in this format) * @return text string containing the question data in XML format */ - public function export_to_xml($question, qformat_xml $format, $extra=null) { + public function export_to_xml($question, qformat_xml $format, $extra=null) { $expout=''; // Create a text string of the allowed functions from the array $allowedfuncs=implode(',',$question->options->allowedfuncs); @@ -441,7 +440,7 @@ class qtype_algebra extends question_type { $expout .= " ".$format->writetext($question->options->answerprefix,1,true). "\n"; // Write out all the answers - $expout .= $format->write_answers($question->options->answers); + $expout .= $format->write_answers($question->options->answers); // Loop over all the variables for the question and write out all their details foreach ($question->options->variables as $var) { $expout .= "name}\">\n"; @@ -451,12 +450,12 @@ class qtype_algebra extends question_type { } return $expout; } - + // Gets all the question responses public function get_all_responses(&$question, &$state) { $result = new stdClass; $answers = array(); - // Loop over all the answers + // Loop over all the answers if (is_array($question->options->answers)) { foreach ($question->options->answers as $aid=>$answer) { $r = new stdClass; @@ -470,17 +469,17 @@ class qtype_algebra extends question_type { return $result; } - /** - * Parses the given expression with the parser if required. - * - * This method will check to see if the argument it is given is already a parsed - * expression and if not will attempt to parse it. - * + /** + * Parses the given expression with the parser if required. + * + * This method will check to see if the argument it is given is already a parsed + * expression and if not will attempt to parse it. + * * @param $expr expression which will be parsed * @param $question question containing the expression or null if none - * @return top term of the parse tree or a string if an exception is thrown - */ - function parse_expression($expr) { + * @return top term of the parse tree or a string if an exception is thrown + */ + function parse_expression($expr) { // Check to see if this is already a parsed expression if(is_a($expr, 'qtype_algebra_parser_term')) { // It is a parsed expression so simply return it @@ -491,40 +490,40 @@ class qtype_algebra extends question_type { if(isset($expr->responses[''])) { $expr=$expr->responses['']; } - // Create an empty array of variable names for the parser (no variable checking here as it is done in the form validation - // TODO see in case of import + // Create an empty array of variable names for the parser (no variable checking here as it is done in the form validation + // TODO see in case of import $varnames=array(); - // We now assume that we have a string to parse. Create a parser instance to + // We now assume that we have a string to parse. Create a parser instance to // to this and return the parser expression at the top of the parse tree - $p=new qtype_algebra_parser; + $p=new qtype_algebra_parser; // Perform the actual parsing inside a try-catch block so that any exceptions // can be caught and converted into errors - try { - return $p->parse($expr,$varnames); - } catch(Exception $e) { - // If the expression cannot be parsed then return a null term. This will + try { + return $p->parse($expr,$varnames); + } catch(Exception $e) { + // If the expression cannot be parsed then return a null term. This will // make Moodle treat the answer as wrong. // TODO: Would be nice to have support for 'invalid answer' in the quiz // engine since an unparseable response is usually caused by a silly typo - return new qtype_algebra_parser_nullterm; - } - } - - protected function initialise_question_instance(question_definition $question, $questiondata) { + return new qtype_algebra_parser_nullterm; + } + } + + protected function initialise_question_instance(question_definition $question, $questiondata) { parent::initialise_question_instance($question, $questiondata); - $question->variables = array(); + $question->variables = array(); if (!empty($questiondata->options->variables)) { - foreach ($questiondata->options->variables as $v) { - $question->variables[$v->id] = new question_variable($v->id, $v->name, $v->min, $v->max); + foreach ($questiondata->options->variables as $v) { + $question->variables[$v->id] = new question_variable($v->id, $v->name, $v->min, $v->max); } } - $question->compareby = $questiondata->options->compareby; - $question->nchecks = $questiondata->options->nchecks; - $question->tolerance = $questiondata->options->tolerance; - $question->allowedfuncs = $questiondata->options->allowedfuncs; - $question->disallow = $questiondata->options->disallow; - $question->answerprefix = $questiondata->options->answerprefix; + $question->compareby = $questiondata->options->compareby; + $question->nchecks = $questiondata->options->nchecks; + $question->tolerance = $questiondata->options->tolerance; + $question->allowedfuncs = $questiondata->options->allowedfuncs; + $question->disallow = $questiondata->options->disallow; + $question->answerprefix = $questiondata->options->answerprefix; $this->initialise_question_answers($question, $questiondata); } -- cgit v1.2.3