MDL-17366 adding the calculatedsimple questiontype

This commit is contained in:
pichetp 2009-05-29 22:42:22 +00:00
parent b925fe0a00
commit 83a15d025c
4 changed files with 1164 additions and 0 deletions

View file

@ -0,0 +1,15 @@
<?php
$string['addingcalculatedsimple'] = 'Adding a Simple Calculated question';
$string['calculatedsimple'] = 'Calculated Simple ';
$string['calculatedsimplesummary'] = 'A simpler version of calculated questions which are like numerical questions but with the numbers used selected randomly from a set when the quiz is taken.';
$string['converttocalculated'] = 'Save as a new regular calculated question';
$string['editingcalculatedsimple'] = 'Editing a Simple Calculated question';
$string['findwildcards']='Find the wild cards {x..} present in the correct answer formulas';
$string['generatenewitemsset'] = 'Generate a new set of ';
$string['mustbenumeric'] = 'You must enter a number here.';
$string['mustnotbenumeric'] = 'This can\'t be a number.';
$string['wildcarditems']= 'Items generated';
$string['wildcardrole']= ' The wild cards <strong>{x..}</strong> will be substituted by a numerical value from the generated values';
$string['wildcardparam']= 'Wild cards parameters used to generate the items ';
$string['willconverttocalculated']= 'If set, the <strong>Save as new question</strong> will save as a new calculated question';
?>

View file

@ -0,0 +1,715 @@
<?php // $Id$
/**
* Defines the editing form for the calculated simplequestion type.
*
* @copyright &copy; 2007 Jamie Pratt
* @author Jamie Pratt me@jamiep.org
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package questionbank
* @subpackage questiontypes
*/
/**
* calculatedsimple editing form definition.
*/
class question_edit_calculatedsimple_form extends question_edit_form {
/**
* Handle to the question type for this question.
*
* @var question_calculatedsimple_qtype
*/
var $qtypeobj;
var $wildcarddisplay ;
// public $qtypeobj;
// $questiondisplay will contain the question from the form
var $questiondisplay ;
public $datasetdefs;
public $reload = false ;
public $maxnumber = -1;
public $regenerate = true;
public $noofitems;
public $outsidelimit = false ;
public $commentanswer = array();
public $answer = array();
public $nonemptyanswer = array();
function question_edit_calculatedsimple_form(&$submiturl, &$question, &$category, &$contexts, $formeditable = true){
global $QTYPES, $SESSION, $CFG, $DB;
// echo "<p> QTYPES <pre>";print_r($QTYPES);echo"</pre></p>";
// echo "<p> session <pre>";print_r($SESSION);echo"</pre></p>";
// echo "<p> question <pre>";print_r($question);echo"</pre></p>";
$this->regenerate = true;
$this->question = $question;
$this->qtypeobj =& $QTYPES[$this->question->qtype];
//get the dataset definitions for this question
//coming here everytime even when using a NoSubmitButton
//so this will only set the values to the actual question database content which is not what we want
//so this should be removed from here
// get priority to paramdatasets
// question->id == 0 so no stored datasets
// else get datasets
if ( "1" == optional_param('reload','', PARAM_INT )) {
// echo "<p> optional reload exist </p>";
$this->reload = true ;
}else {
$this->reload = false ;
}
if(!$this->reload ){ // use database data as this is first pass
if ( !empty($question->id)) {
if (empty($question->options)) {
$this->get_question_options($question);
}
$this->datasetdefs = $this->qtypeobj->get_dataset_definitions($question->id, array());
if(!empty($this->datasetdefs)){
foreach ($this->datasetdefs as $datasetdef) {
// Get maxnumber
if ($this->maxnumber == -1 || $datasetdef->itemcount < $this->maxnumber) {
$this->maxnumber = $datasetdef->itemcount;
}
}
foreach ($this->datasetdefs as $defid => $datasetdef) {
if (isset($datasetdef->id)) {
$this->datasetdefs[$defid]->items = $DB->get_records_sql( // Use number as key!!
" SELECT itemnumber, definition, id, value
FROM {question_dataset_items}
WHERE definition = ? ", array($datasetdef->id));
}
}
}
$i = 0 ;
foreach($this->question->options->answers as $answer){
$this->answer[$i] = $answer ;
$i++;
}
$this->nonemptyanswer = $this->answer ;
}
$datasettoremove = false;
$newdatasetvalues = false ;
$newdataset = false ;
}else {
// handle reload to get values from the form-elements
// answers, datasetdefs and data_items
// verify for the specific dataset values as the other parameters
// unints, feeedback etc are handled elsewhere
// handle request buttons :
// 'analyzequestion' (Identify the wild cards {x..} present in answers)
// 'addbutton' (create new set of datatitems)
// 'updatedatasets' is handled automatically on each reload
// The analyzequestion is done every time on reload
// to detect any new wild cards so that the current display reflects
// the mandatory (i.e. in answers) datasets
// to implement : don't do any changes if the question is used in a quiz.
// If new datadef, new properties should erase items.
$dummyform = new stdClass();
$mandatorydatasets = array();
if ( $dummyform->answer =optional_param('answer')) { // there is always at least one answer...
$fraction = optional_param('fraction') ;
$feedback = optional_param('feedback') ;
$tolerance = optional_param('tolerance') ;
$tolerancetype = optional_param('tolerancetype') ;
$correctanswerlength = optional_param('correctanswerlength') ;
$correctanswerformat = optional_param('correctanswerformat') ;
foreach( $dummyform->answer as $key => $answer ) {
if(trim($answer) != ''){ // just look for non-empty
$this->answer[$key]=new stdClass();
$this->answer[$key]->answer = $answer;
$this->answer[$key]->fraction = $fraction[$key];
$this->answer[$key]->feedback = $feedback[$key];
$this->answer[$key]->tolerance = $tolerance[$key];
$this->answer[$key]->tolerancetype = $tolerancetype[$key];
$this->answer[$key]->correctanswerlength = $correctanswerlength[$key];
$this->answer[$key]->correctanswerformat = $correctanswerformat[$key];
$this->nonemptyanswer[]= $this->answer[$key];
$mandatorydatasets +=$this->qtypeobj->find_dataset_names($answer);
}
}
}
// echo "<p>this->answer <pre>";print_r($this->answer);echo"</pre></p>";
// echo "<p>this->answer <pre>";print_r($this->nonemptyanswer);echo"</pre></p>";
$this->datasetdefs = array();
// rebuild datasetdefs from old values
$olddef = optional_param('datasetdef');
$oldoptions = optional_param('defoptions');
$calcmin = optional_param('calcmin') ;
$calclength = optional_param('calclength') ;
$calcmax = optional_param('calcmax') ;
/* echo "<p> calcmin <pre>";print_r(optional_param('calcmin'));echo"</pre></p>";
echo "<p> calcmax <pre>";print_r(optional_param('calcmax'));echo"</pre></p>";
echo "<p> calclength <pre>";print_r(optional_param('calclength'));echo"</pre></p>";*/
$newdatasetvalues = false ;
// echo "<p> olddef <pre>";print_r(optional_param('datasetdef'));echo"</pre></p>";
for($key = 1 ; $key <= sizeof($olddef) ; $key++) {
$def = $olddef[$key] ;
$this->datasetdefs[$def]= new stdClass ;
$this->datasetdefs[$def]->type = 1;
$this->datasetdefs[$def]->category = 0;
// $this->datasets[$key]->name = $datasetname;
$this->datasetdefs[$def]->options = $oldoptions[$key] ;
$this->datasetdefs[$def]->calcmin = $calcmin[$key] ;
$this->datasetdefs[$def]->calcmax = $calcmax[$key] ;
$this->datasetdefs[$def]->calclength = $calclength[$key] ;
//then compare with new values
if (ereg('^(uniform|loguniform):([^:]*):([^:]*):([0-9]*)$', $this->datasetdefs[$def]->options, $regs)) {
if( $this->datasetdefs[$def]->calcmin != $regs[2]||
$this->datasetdefs[$def]->calcmax != $regs[3] ||
$this->datasetdefs[$def]->calclength != $regs[4]){
$newdatasetvalues = true ;
// echo "<p> new datasetdefs values ".$regs[2]."xx". $regs[3]."xx".$regs[4]."</p>";
}
}
$this->datasetdefs[$def]->options="uniform:".$this->datasetdefs[$def]->calcmin.":".$this->datasetdefs[$def]->calcmax.":".$this->datasetdefs[$def]->calclength;
}
// echo "<p>this datasetdefs <pre>";print_r($this->datasetdefs);echo"</pre></p>";
// detect new datasets
$newdataset = false ;
foreach ($mandatorydatasets as $datasetname) {
if (!isset($this->datasetdefs["1-0-$datasetname"])) {
// list($options, $selected) =
// $this->dataset_options($form, $datasetname);
$key = "1-0-$datasetname";
$this->datasetdefs[$key]=new stdClass ;//"1-0-$datasetname";
$this->datasetdefs[$key]->type = 1;
$this->datasetdefs[$key]->category = 0;
$this->datasetdefs[$key]->name = $datasetname;
$this->datasetdefs[$key]->options = "uniform:1.0:10.0:1";
$newdataset = true ;
// echo "<p> new datasetdefs $datasetname </p>";
// $form->dataset[$key]=$selected ;
//$key++;
}else {
$this->datasetdefs["1-0-$datasetname"]->name = $datasetname ;
}
}
// remove obsolete datasets
$datasettoremove = false;
foreach ($this->datasetdefs as $defkey => $datasetdef){
if(!isset($datasetdef->name )){
// echo "<p> dataset $defkey to remove </p>";
$datasettoremove = true;
unset($this->datasetdefs[$defkey]);
}
}
} // handle reload
// create items if $newdataset and noofitems > 0 and !$newdatasetvalues
// eliminate any items if $newdatasetvalues
// eliminate any items if $datasettoremove, $newdataset, $newdatasetvalues
if ($datasettoremove ||$newdataset ||$newdatasetvalues ) {
foreach ($this->datasetdefs as $defkey => $datasetdef){
$datasetdef->itemcount = 0;
unset($datasetdef->items);
}
}
$maxnumber = -1 ;
if ( "" !=optional_param('addbutton')){
$maxnumber = optional_param('selectadd') ;
foreach ($this->datasetdefs as $defid => $datasetdef) {
$datasetdef->itemcount = $maxnumber;
unset($datasetdef->items);
for ($numberadded =1 ; $numberadded <= $maxnumber; $numberadded++){
$datasetitem = new stdClass;
// $datasetitem->definition = $datasetdef->id ;
$datasetitem->itemnumber = $numberadded;
$datasetitem->id = 0;
$datasetitem->value = $this->qtypeobj->generate_dataset_item($datasetdef->options);
$this->datasetdefs[$defid]->items[$numberadded]=$datasetitem ;
/* if (!$DB->insert_record('question_dataset_items', $datasetitem)) {
print_error('cannotinsert', 'question');
}*/
}//for number added
}// datasetsdefs end
$this->maxnumber = $maxnumber ;
// echo"<p> using create new items $this->maxnumber </p>";
}else {
// Handle reload dataset items
// echo"<p> using optional to build </p>";
if ( "" !=optional_param('definition')&& !($datasettoremove ||$newdataset ||$newdatasetvalues )){
$i = 1;
$fromformdefinition = optional_param('definition');
$fromformnumber = optional_param('number');
$fromformitemid = optional_param('itemid');
ksort($fromformdefinition);
// echo "<p> fromformdefinition <pre>";print_r($fromformdefinition);echo"</pre></p>";
// echo "<p> fromformnumber <pre>";print_r($fromformnumber);echo"</pre></p>";
foreach($fromformdefinition as $key => $defid) {
$addeditem = new stdClass();
$addeditem->id = $fromformitemid[$i] ;
$addeditem->value = $fromformnumber[$i];
$addeditem->itemnumber = ceil($i / count($this->datasetdefs));
$this->datasetdefs[$defid]->items[$addeditem->itemnumber]=$addeditem ;
$this->datasetdefs[$defid]->itemcount = $i ;
$i++;
}
}
if (isset($addeditem->itemnumber) && $this->maxnumber < $addeditem->itemnumber){
$this->maxnumber = $addeditem->itemnumber;
if(!empty($this->datasetdefs)){
foreach ($this->datasetdefs as $datasetdef) {
$datasetdef->itemcount = $this->maxnumber ;
}
}
}
}
// echo "<p>line 443 datasetdefs <pre>";print_r($this->datasetdefs);echo"</pre></p>";
//$key = 0 ;
// if ( "" !=optional_param('answer')) echo "<p> optional answer exist </p>";
// if ( "" !=optional_param('answer['.$key.']','', PARAM_NOTAGS)) echo "<p> optional $key exist </p>";
// if ( "" !=optional_param('noanswer','', PARAM_INT )) echo "<p> optional noanswer exist </p>";
parent::question_edit_form($submiturl, $question, $category, $contexts, $formeditable);
}
function get_per_answer_fields(&$mform, $label, $gradeoptions, &$repeatedoptions, &$answersoption) {
$repeated = parent::get_per_answer_fields(&$mform, $label, $gradeoptions, $repeatedoptions, $answersoption);
$mform->setType('answer', PARAM_NOTAGS);
$addrepeated = array();
$addrepeated[] =& $mform->createElement('text', 'tolerance', get_string('tolerance', 'qtype_calculated'));
$repeatedoptions['tolerance']['type'] = PARAM_NUMBER;
$repeatedoptions['tolerance']['default'] = 0.01;
$addrepeated[] =& $mform->createElement('select', 'tolerancetype', get_string('tolerancetype', 'quiz'), $this->qtypeobj->tolerance_types());
$addrepeated[] =& $mform->createElement('select', 'correctanswerlength', get_string('correctanswershows', 'qtype_calculated'), range(0, 9));
$repeatedoptions['correctanswerlength']['default'] = 2;
$answerlengthformats = array('1' => get_string('decimalformat', 'quiz'), '2' => get_string('significantfiguresformat', 'quiz'));
$addrepeated[] =& $mform->createElement('select', 'correctanswerformat', get_string('correctanswershowsformat', 'qtype_calculated'), $answerlengthformats);
array_splice($repeated, 3, 0, $addrepeated);
$repeated[1]->setLabel(get_string('correctanswerformula', 'quiz').'=');
return $repeated;
}
/**
* Add question-type specific form fields.
*
* @param MoodleQuickForm $mform the form being built.
*/
function definition_inner(&$mform) {
global $QTYPES;
$this->qtypeobj =& $QTYPES[$this->qtype()];
$strquestionlabel = $this->qtypeobj->comment_header($this->nonemptyanswer);
$label = get_string("sharedwildcards", "qtype_datasetdependent");
$mform->addElement('hidden', 'initialcategory', 1);
$mform->addElement('hidden', 'reload', 1);
// $html2 = $this->qtypeobj->print_dataset_definitions_category($this->question);
// $mform->insertElementBefore($mform->createElement('static','listcategory',$label,$html2),'name');
$addfieldsname='updatecategory';
$addstring=get_string("updatecategory", "qtype_calculated");
$mform->registerNoSubmitButton($addfieldsname);
// $mform->insertElementBefore( $mform->createElement('submit', $addfieldsname, $addstring),'listcategory');
$creategrades = get_grade_options();
$this->add_per_answer_fields($mform, get_string('answerhdr', 'qtype_calculated', '{no}'),
$creategrades->gradeoptions, 1, 1);
$repeated = array();
$repeated[] =& $mform->createElement('header', 'unithdr', get_string('unithdr', 'qtype_numerical', '{no}'));
$repeated[] =& $mform->createElement('text', 'unit', get_string('unit', 'quiz'));
$mform->setType('unit', PARAM_NOTAGS);
$repeated[] =& $mform->createElement('text', 'multiplier', get_string('multiplier', 'quiz'));
$mform->setType('multiplier', PARAM_NUMBER);
if (isset($this->question->options)){
$countunits = count($this->question->options->units);
} else {
$countunits = 0;
}
if ($this->question->formoptions->repeatelements){
$repeatsatstart = $countunits + 1;
} else {
$repeatsatstart = $countunits;
}
$this->repeat_elements($repeated, $repeatsatstart, array(), 'nounits', 'addunits', 2, get_string('addmoreunitblanks', 'qtype_calculated', '{no}'));
if ($mform->elementExists('multiplier[0]')){
$firstunit =& $mform->getElement('multiplier[0]');
$firstunit->freeze();
$firstunit->setValue('1.0');
$firstunit->setPersistantFreeze(true);
}
//hidden elements
// $mform->addElement('hidden', 'wizard', 'datasetdefinitions');
// $mform->setType('wizard', PARAM_ALPHA);
// $mform->addElement('header', '', '');
$label = "<div class='mdl-align'></div><div class='mdl-align'>".get_string('wildcardrole', 'qtype_calculatedsimple')."</div>";
$mform->addElement('html', "<div class='mdl-align'>&nbsp;</div>");
$mform->addElement('html', $label);// explaining the role of datasets so other strings can be shortened
$mform->addElement('submit', 'analyzequestion', get_string('findwildcards','qtype_calculatedsimple'));
$mform->registerNoSubmitButton('analyzequestion');
$mform->closeHeaderBefore('analyzequestion');
if ( "" != optional_param('analyzequestion','', PARAM_RAW)) {
$this->wizarddisplay = true;
// echo "<p> session answer <pre>";print_r($SESSION);echo"</pre></p>";
// echo "<p> session answer <pre>";print_r($SESSION->calculated->questionform->answers);echo"</pre></p>";
}else {
$this->wizwarddisplay = false;
}
if ($this->maxnumber != -1){
$this->noofitems = $this->maxnumber;
} else {
$this->noofitems = 0;
}
if(!empty($this->datasetdefs)){
$key = 0;
$mform->addElement('header', 'additemhdr', get_string('wildcardparam', 'qtype_calculatedsimple'));
$idx = 1;
if(!empty($this->datasetdefs)){
$j = (($this->noofitems) * count($this->datasetdefs))+1;//
foreach ($this->datasetdefs as $defkey => $datasetdef){
$mform->addElement('static', "na[$j]", get_string('param', 'qtype_datasetdependent', $datasetdef->name));
$this->qtypeobj->custom_generator_tools_part($mform, $idx, $j);
$mform->addElement('hidden', "datasetdef[$idx]");
$mform->setType("datasetdef[$idx]", PARAM_RAW);
$idx++;
$mform->addElement('static', "divider[$j]", '', '<hr />');
$j++;
}
}
$addoptions = Array();
$addoptions['1']='1';
for ($i=10; $i<=100 ; $i+=10){
$addoptions["$i"]="$i";
}
$mform->closeHeaderBefore('additemhdr');
$addgrp = array();
$addgrp[] =& $mform->createElement('submit', 'addbutton', get_string('generatenewitemsset', 'qtype_calculatedsimple'));
$addgrp[] =& $mform->createElement('select', "selectadd", get_string('additem', 'qtype_datasetdependent'), $addoptions);
$addgrp[] = & $mform->createElement('static',"stat","Items",get_string('item(s)', 'qtype_datasetdependent'));
$mform->addGroup($addgrp, 'addgrp', '', ' ', false);
$mform->registerNoSubmitButton('addbutton');
$mform->closeHeaderBefore('addgrp');
$mform->addElement('static', "divideradd", '', '');
if ($this->noofitems == 0) {
$mform->addElement('static','warningnoitems','','<span class="error">'.get_string('youmustaddatleastoneitem', 'qtype_datasetdependent').'</span>');
$mform->closeHeaderBefore('warningnoitems');
}else {
$mform->addElement('header', 'additemhdr1', get_string('wildcarditems', 'qtype_calculatedsimple'));
$mform->closeHeaderBefore('additemhdr1');
// $mform->addElement('header', '', get_string('itemno', 'qtype_datasetdependent', ""));
// $mform->addElement('submit', 'updatedatasets', get_string('updatedatasetparam', 'qtype_datasetdependent'));
// $mform->registerNoSubmitButton('updatedatasets');
// $mform->setAdvanced("updatedatasets",true);
//------------------------------------------------------------------------------------------------------------------------------
$j = $this->noofitems * count($this->datasetdefs);
for ($i = $this->noofitems; $i >= 1 ; $i--){
foreach ($this->datasetdefs as $defkey => $datasetdef){
$mform->addElement('text', "number[$j]", get_string('param', 'qtype_datasetdependent', $datasetdef->name));
$mform->setType("number[$j]", PARAM_NUMBER);
$mform->setAdvanced("number[$j]",true);
$mform->addElement('hidden', "itemid[$j]");
$mform->setType("itemid[$j]", PARAM_INT);
$mform->addElement('hidden', "definition[$j]");
$mform->setType("definition[$j]", PARAM_NOTAGS);
$j--;
}
if (!empty( $strquestionlabel)){
$repeated[] =& $mform->addElement('static', "answercomment[$i]", "<b>".get_string('itemno', 'qtype_datasetdependent', $i)."</b>&nbsp;&nbsp;".$strquestionlabel);
}
$mform->addElement('static', "divider1[$j]", '', '<hr />');
}
}
// if ($this->outsidelimit){
// $mform->addElement('static','outsidelimit','','');
// }
}else {
$mform->addElement('static','warningnowildcards','','<span class="error">'.get_string('atleastonewildcard', 'qtype_calculatedsimple').'</span>');
$mform->closeHeaderBefore('warningnowildcards');
}
//------------------------------------------------------------------------------------------------------------------------------
//non standard name for button element needed so not using add_action_buttons
//hidden elements
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'courseid');
$mform->setType('courseid', PARAM_INT);
$mform->setDefault('courseid', 0);
$mform->addElement('hidden', 'cmid');
$mform->setType('cmid', PARAM_INT);
$mform->setDefault('cmid', 0);
if (!empty($this->question->id)){
if ($this->question->formoptions->cansaveasnew){
$mform->addElement('header', 'additemhdr', get_string('converttocalculated', 'qtype_calculatedsimple'));
$mform->closeHeaderBefore('additemhdr');
$mform->addElement('checkbox', 'convert','' ,get_string('willconverttocalculated', 'qtype_calculatedsimple'));
$mform->setDefault('convert', 0);
}
}
// $mform->addElement('hidden', 'wizard', 'edit_calculatedsimple');
// $mform->setType('wizard', PARAM_ALPHA);
/*
$mform->addElement('hidden', 'returnurl');
$mform->setType('returnurl', PARAM_LOCALURL);
$mform->setDefault('returnurl', 0);
*/
}
function set_data($question) {
$answer = $this->answer;
$default_values = array();
if (count($answer)) {
$key = 0;
foreach ($answer as $answer){
$default_values['answer['.$key.']'] = $answer->answer;
$default_values['fraction['.$key.']'] = $answer->fraction;
$default_values['tolerance['.$key.']'] = $answer->tolerance;
$default_values['tolerancetype['.$key.']'] = $answer->tolerancetype;
$default_values['correctanswerlength['.$key.']'] = $answer->correctanswerlength;
$default_values['correctanswerformat['.$key.']'] = $answer->correctanswerformat;
$default_values['feedback['.$key.']'] = $answer->feedback;
$key++;
}
}
if (isset($question->options)){
$units = array_values($question->options->units);
// make sure the default unit is at index 0
usort($units, create_function('$a, $b',
'if (1.0 === (float)$a->multiplier) { return -1; } else '.
'if (1.0 === (float)$b->multiplier) { return 1; } else { return 0; }'));
if (count($units)) {
$key = 0;
foreach ($units as $unit){
$default_values['unit['.$key.']'] = $unit->unit;
$default_values['multiplier['.$key.']'] = $unit->multiplier;
$key++;
}
}
}
$key = 0 ;
// echo "<p> mandatorydatasets $key xx".optional_param("answer[0]",'', PARAM_NOTAGS)."YY".$this->_form->getElementValue('answer['.$key.']')."xx<p>";
$formdata = array();
$fromform = new stdClass();
//fill out all data sets and also the fields for the next item to add.
if(!empty($this->datasetdefs)){
$j = $this->noofitems * count($this->datasetdefs);
for ($itemnumber = $this->noofitems; $itemnumber >= 1; $itemnumber--){
$data = array();
foreach ($this->datasetdefs as $defid => $datasetdef){
if (isset($datasetdef->items[$itemnumber])){
$formdata["number[$j]"] = $datasetdef->items[$itemnumber]->value;
$formdata["definition[$j]"] = $defid;
$formdata["itemid[$j]"] = $datasetdef->items[$itemnumber]->id;
$data[$datasetdef->name] = $datasetdef->items[$itemnumber]->value;
}
$j--;
}
// echo "<p>answers avant comment <pre>";print_r($answer);echo"</pre></p>";
// echo "<p>data avant comment <pre>";print_r($data);echo"</pre></p>";
if($this->noofitems != 0 ) {
if(!isset($question->id)) $question->id = 0 ;
$comment = $this->qtypeobj->comment_on_datasetitems($question->id,$this->nonemptyanswer, $data, $itemnumber);//$this->
if ($comment->outsidelimit) {
$this->outsidelimit=$comment->outsidelimit ;
}
$totalcomment='';
// echo "<p> comment <pre>";print_r($comment);echo"</pre></p>";
foreach ($this->nonemptyanswer as $key => $answer) {
$totalcomment .= $comment->stranswers[$key].'<br/>';
}
$formdata['answercomment['.$itemnumber.']'] = $totalcomment ;
}
}
// $formdata['reload'] = '1';
// $formdata['nextpageparam[forceregeneration]'] = $this->regenerate;
$formdata['selectdelete'] = '1';
$formdata['selectadd'] = '1';
$j = $this->noofitems * count($this->datasetdefs)+1;
$data = array(); // data for comment_on_datasetitems later
$idx =1 ;
foreach ($this->datasetdefs as $defid => $datasetdef){
$formdata["datasetdef[$idx]"] = $defid;
$idx++;
}
$formdata = $this->qtypeobj->custom_generator_set_data($this->datasetdefs, $formdata);
}
$question = (object)((array)$question + $default_values+$formdata );
// echo "<p> question data <pre>";print_r($question);echo"</pre></p>";
parent::set_data($question);
}
function qtype() {
return 'calculatedsimple';
}
function validation($data, $files) {
$errors = parent::validation($data, $files);
//verifying for errors in {=...} in question text;
$qtext = "";
$qtextremaining = $data['questiontext'] ;
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']);
foreach ($possibledatasets as $name => $value) {
$qtextremaining = str_replace('{'.$name.'}', '1', $qtextremaining);
}
// echo "numericalquestion qtextremaining <pre>";print_r($possibledatasets);
while (ereg('\{=([^[:space:]}]*)}', $qtextremaining, $regs1)) {
$qtextsplits = explode($regs1[0], $qtextremaining, 2);
$qtext =$qtext.$qtextsplits[0];
$qtextremaining = $qtextsplits[1];
if (!empty($regs1[1]) && $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])) {
if(!isset($errors['questiontext'])){
$errors['questiontext'] = $formulaerrors.':'.$regs1[1] ;
}else {
$errors['questiontext'] .= '<br/>'.$formulaerrors.':'.$regs1[1];
}
}
}
$answers = $data['answer'];
$answercount = 0;
$maxgrade = false;
$possibledatasets = $this->qtypeobj->find_dataset_names($data['questiontext']);
$mandatorydatasets = array();
foreach ($answers as $key => $answer){
$mandatorydatasets += $this->qtypeobj->find_dataset_names($answer);
}
if ( count($mandatorydatasets )==0){
// $errors['questiontext']=get_string('atleastonewildcard', 'qtype_datasetdependent');
foreach ($answers as $key => $answer){
$errors['answer['.$key.']'] = get_string('atleastonewildcard', 'qtype_datasetdependent');
}
}
foreach ($answers as $key => $answer){
//check no of choices
// the * for everykind of answer not actually implemented
$trimmedanswer = trim($answer);
if (($trimmedanswer!='')||$answercount==0){
$eqerror = qtype_calculated_find_formula_errors($trimmedanswer);
if (FALSE !== $eqerror){
$errors['answer['.$key.']'] = $eqerror;
}
}
if ($trimmedanswer!=''){
if ('2' == $data['correctanswerformat'][$key]
&& '0' == $data['correctanswerlength'][$key]) {
$errors['correctanswerlength['.$key.']'] = get_string('zerosignificantfiguresnotallowed','quiz');
}
if (!is_numeric($data['tolerance'][$key])){
$errors['tolerance['.$key.']'] = get_string('mustbenumeric', 'qtype_calculated');
}
if ($data['fraction'][$key] == 1) {
$maxgrade = true;
}
$answercount++;
}
//check grades
//TODO how should grade checking work here??
/*if ($answer != '') {
if ($data['fraction'][$key] > 0) {
$totalfraction += $data['fraction'][$key];
}
if ($data['fraction'][$key] > $maxfraction) {
$maxfraction = $data['fraction'][$key];
}
}*/
}
//grade checking :
/// Perform sanity checks on fractional grades
/*if ( ) {
if ($maxfraction != 1) {
$maxfraction = $maxfraction * 100;
$errors['fraction[0]'] = get_string('errfractionsnomax', 'qtype_multichoice', $maxfraction);
}
} else {
$totalfraction = round($totalfraction,2);
if ($totalfraction != 1) {
$totalfraction = $totalfraction * 100;
$errors['fraction[0]'] = get_string('errfractionsaddwrong', 'qtype_multichoice', $totalfraction);
}
}*/
$units = $data['unit'];
if (count($units)) {
foreach ($units as $key => $unit){
if (is_numeric($unit)){
$errors['unit['.$key.']'] = get_string('mustnotbenumeric', 'qtype_calculated');
}
$trimmedunit = trim($unit);
$trimmedmultiplier = trim($data['multiplier'][$key]);
if (!empty($trimmedunit)){
if (empty($trimmedmultiplier)){
$errors['multiplier['.$key.']'] = get_string('youmustenteramultiplierhere', 'qtype_calculated');
}
if (!is_numeric($trimmedmultiplier)){
$errors['multiplier['.$key.']'] = get_string('mustbenumeric', 'qtype_calculated');
}
}
}
}
if ($answercount==0){
$errors['answer[0]'] = get_string('atleastoneanswer', 'qtype_calculated');
}
if ($maxgrade == false) {
$errors['fraction[0]'] = get_string('fractionsnomax', 'question');
}
if (isset($data['backtoquiz']) && ($this->noofitems==0) ){
$errors['warning'] = get_string('warning', 'mnet');
}
if ($this->outsidelimit){
// if(!isset($errors['warning'])) $errors['warning']=' ';
$errors['outsidelimits'] = get_string('oneanswertrueansweroutsidelimits','qtype_calculated');
}
$numbers = $data['number'];
foreach ($numbers as $key => $number){
if(! is_numeric($number)){
if (stristr($number,',')){
$errors['number['.$key.']'] = get_string('The , cannot be used, use . as in 0.013 or 1.3e-2', 'qtype_datasetdependent');
}else {
$errors['number['.$key.']'] = get_string('This is not a valid number', 'qtype_datasetdependent');
}
}else if( stristr($number,'x')){
$errors['number['.$key.']'] = get_string('Hexadecimal format (i.e. 0X12d) is not allowed', 'qtype_datasetdependent');
} else if( is_nan($number)){
$errors['number['.$key.']'] = get_string('is a NAN number', 'qtype_datasetdependent');
}
}
if ( $this->noofitems==0 ){
$errors['warning'] = get_string('warning', 'mnet');
}
return $errors;
}
}
?>

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 B

View file

@ -0,0 +1,434 @@
<?php // $Id$
/////////////////
// CALCULATED ///
/////////////////
/// QUESTION TYPE CLASS //////////////////
class question_calculatedsimple_qtype extends question_calculated_qtype {
// Used by the function custom_generator_tools:
var $calcgenerateidhasbeenadded = false;
public $virtualqtype = false;
function name() {
return 'calculatedsimple';
}
function save_question_options($question) {
//$options = $question->subtypeoptions;
// Get old answers:
global $CFG, $DB;
if (isset($question->answer) && !isset($question->answers)) {
$question->answers = $question->answer;
}
// 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();
}
// Save the units.
$virtualqtype = $this->get_virtual_qtype();
$result = $virtualqtype->save_numerical_units($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;
}
foreach ($question->answers as $key => $dataanswer) {
if ( trim($dataanswer) != '' ) {
$answer = new stdClass;
$answer->question = $question->id;
$answer->answer = trim($dataanswer);
$answer->fraction = $question->fraction[$key];
$answer->feedback = trim($question->feedback[$key]);
if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it
$answer->id = $oldanswer->id;
if (! $DB->update_record("question_answers", $answer)) {
$result->error = get_string('errorupdatinganswer','question',$answer->id);
return $result;
}
} else { // This is a completely new answer
if (! $answer->id = $DB->insert_record("question_answers", $answer)) {
$result->error = get_string('errorinsertinganswer','question');
return $result;
}
}
// 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
if (! $DB->update_record('question_calculated', $options)) {
$mess->name = $this->name();
$mess->id = $options->id ;
$result->error = get_string('errorupdatingoptions','question',$mess);
// $result->error = "Could not update question calculated options! (id=$options->id)";
return $result;
}
} else { // new options
if (! $DB->insert_record('question_calculated', $options)) {
$result->error = "Could not insert question calculated options!";
return $result;
}
}
}
}
// delete old answer records
if (!empty($oldanswers)) {
foreach($oldanswers as $oa) {
$DB->delete_records('question_answers', array('id' => $oa->id));
}
}
// delete old answer records
if (!empty($oldoptions)) {
foreach($oldoptions as $oo) {
$DB->delete_records('question_calculated', array('id' => $oo->id));
}
}
if( isset($question->import_process)&&$question->import_process){
$this->import_datasets($question);
}else {
//save datasets and datatitems from form i.e in question
// $datasetdefs = $this->get_dataset_definitions($question->id, array());
$question->dataset = $question->datasetdef ;
// $this->save_dataset_definitions($question);
// Save datasets
$datasetdefinitions = $this->get_dataset_definitions($question->id, $question->dataset);
$tmpdatasets = array_flip($question->dataset);
$defids = array_keys($datasetdefinitions);
$datasetdefs = array();
foreach ($defids as $defid) {
$datasetdef = &$datasetdefinitions[$defid];
if (isset($datasetdef->id)) {
if (!isset($tmpdatasets[$defid])) {
// This dataset is not used any more, delete it
$DB->delete_records('question_datasets', array('question' => $question->id, 'datasetdefinition' => $datasetdef->id));
// if ($datasetdef->category == 0) { // Question local dataset
$DB->delete_records('question_dataset_definitions', array('id' => $datasetdef->id));
$DB->delete_records('question_dataset_items', array('definition' => $datasetdef->id));
// }
}
// This has already been saved or just got deleted
unset($datasetdefinitions[$defid]);
continue;
}
if (!$datasetdef->id = $DB->insert_record('question_dataset_definitions', $datasetdef)) {
print_error("cannotcreatedataset", 'question', '', $defid);
}
$datasetdefs[]= clone($datasetdef);
$questiondataset = new stdClass;
$questiondataset->question = $question->id;
$questiondataset->datasetdefinition = $datasetdef->id;
if (!$DB->insert_record('question_datasets', $questiondataset)) {
print_error('cannotcreaterelation', 'question', '', $name);
}
unset($datasetdefinitions[$defid]);
}
// Remove local obsolete datasets as well as relations
// to datasets in other categories:
if (!empty($datasetdefinitions)) {
foreach ($datasetdefinitions as $def) {
$DB->delete_records('question_datasets', array('question' => $question->id, 'datasetdefinition' => $def->id));
if ($def->category == 0) { // Question local dataset
$DB->delete_records('question_dataset_definitions', array('id' => $def->id));
$DB->delete_records('question_dataset_items', array('definition' => $def->id));
}
}
}
$datasetdefs = $this->get_dataset_definitions($question->id, $question->dataset);
// echo "<p> datasetdefs save <pre>";print_r($datasetdefs);echo"</pre></p>";
// Handle adding and removing of dataset items
$i = 1;
ksort($question->definition);
foreach ($question->definition as $key => $defid) {
$addeditem = new stdClass();
$addeditem->definition = $datasetdefs[$defid]->id;
$addeditem->value = $question->number[$i];
$addeditem->itemnumber = ceil($i / count($datasetdefs));
if (empty($question->makecopy) && $question->itemid[$i]) {
// Reuse any previously used record
$addeditem->id = $question->itemid[$i];
if (!$DB->update_record('question_dataset_items', $addeditem)) {
print_error('cannotupdateitem', 'question');
}
} else {
if (!$DB->insert_record('question_dataset_items', $addeditem)) {
print_error('cannotinsert', 'question');
}
}
$i++;
}
if (isset($addeditem->itemnumber) && $maxnumber < $addeditem->itemnumber){
$maxnumber = $addeditem->itemnumber;
foreach ($datasetdefs as $key => $newdef) {
if (isset($newdef->id) && $newdef->itemcount <= $maxnumber) {
$newdef->itemcount = $maxnumber;
// Save the new value for options
$DB->update_record('question_dataset_definitions', $newdef);
}
}
}
}
// Report any problems.
//convert to calculated
if(!empty($question->makecopy) && !empty($question->convert) ){
// echo "<p> question save <pre>";print_r($question);echo"</pre></p>";
if (!$DB->set_field('question', 'qtype', 'calculated', array('id'=> $question->id))) {
print_error('cannotupdateitem', 'question');
}
}
if (!empty($result->notice)) {
return $result;
}
return true;
}
function finished_edit_wizard(&$form) {
return true ; //isset($form->backtoquiz);
}
/**
* this version save the available data at the different steps of the question editing process
* without using global $SESSION as storage between steps
* at the first step $wizardnow = 'question'
* when creating a new question
* when modifying a question
* when copying as a new question
* the general parameters and answers are saved using parent::save_question
* then the datasets are prepared and saved
* at the second step $wizardnow = 'datasetdefinitions'
* the datadefs final type are defined as private, category or not a datadef
* at the third step $wizardnow = 'datasetitems'
* the datadefs parameters and the data items are created or defined
*
* @param object question
* @param object $form
* @param int $course
* @param PARAM_ALPHA $wizardnow should be added as we are coming from question2.php
*/
function save_question($question, $form, $course) {
$question = default_questiontype::save_question($question, $form, $course);
return $question;
}
function response_summary($question, $state, $length=80, $formatting=true) {
// The actual response is the bit after the hyphen
return substr($state->answer, strpos($state->answer, '-')+1, $length);
}
function custom_generator_tools_part(&$mform, $idx, $j){
$minmaxgrp = array();
$minmaxgrp[] =& $mform->createElement('text', "calcmin[$idx]", get_string('calcmin', 'qtype_datasetdependent'));
$minmaxgrp[] =& $mform->createElement('text', "calcmax[$idx]", get_string('calcmax', 'qtype_datasetdependent'));
$mform->addGroup($minmaxgrp, 'minmaxgrp', get_string('minmax', 'qtype_datasetdependent'), ' - ', false);
$mform->setType("calcmin[$idx]", PARAM_NUMBER);
$mform->setType("calcmax[$idx]", PARAM_NUMBER);
$precisionoptions = range(0, 10);
$mform->addElement('select', "calclength[$idx]", get_string('calclength', 'qtype_datasetdependent'), $precisionoptions);
$distriboptions = array('uniform' => get_string('uniform', 'qtype_datasetdependent'), 'loguniform' => get_string('loguniform', 'qtype_datasetdependent'));
$mform->addElement('hidden', "calcdistribution[$idx]", 'uniform');
}
function comment_header($answers) {
//$this->get_question_options($question);
$strheader = "";
$delimiter = '';
// $answers = $question->options->answers;
// echo "<p>comments header answers <pre>";print_r($answers);echo"</pre></p>";
foreach ($answers as $key => $answer) {
/* if (is_string($answer)) {
$strheader .= $delimiter.$answer;
} else {*/
$strheader .= $delimiter.$answer->answer;
// }
$delimiter = '<br/><br/><br/>';
}
return $strheader;
}
function comment_on_datasetitems($questionid, $answers,$data, $number) {
global $DB;
$comment = new stdClass;
$comment->stranswers = array();
$comment->outsidelimit = false ;
$comment->answers = array();
/// Find a default unit:
if (!empty($questionid) && $unit = $DB->get_record('question_numerical_units', array('question'=> $questionid, 'multiplier' => 1.0))) {
$unit = $unit->unit;
} else {
$unit = '';
}
$answers = fullclone($answers);
$strmin = get_string('min', 'quiz');
$strmax = get_string('max', 'quiz');
$errors = '';
$delimiter = ': ';
$virtualqtype = $this->get_virtual_qtype();
foreach ($answers as $key => $answer) {
$formula = $this->substitute_variables($answer->answer,$data);
$formattedanswer = qtype_calculated_calculate_answer(
$answer->answer, $data, $answer->tolerance,
$answer->tolerancetype, $answer->correctanswerlength,
$answer->correctanswerformat, $unit);
if ( $formula === '*'){
$answer->min = ' ';
$formattedanswer->answer = $answer->answer ;
}else {
eval('$answer->answer = '.$formula.';') ;
$virtualqtype->get_tolerance_interval($answer);
}
if ($answer->min === '') {
// This should mean that something is wrong
$comment->stranswers[$key] = " $formattedanswer->answer".'<br/><br/>';
} else if ($formula === '*'){
$comment->stranswers[$key] = $formula.' = '.get_string('anyvalue','qtype_calculated').'<br/><br/>';
}else{
$comment->stranswers[$key]= $formula.' = '.$formattedanswer->answer.'' ;
$comment->stranswers[$key] .= "<br/>".$strmin. $delimiter.$answer->min.'---';
$comment->stranswers[$key] .= $strmax.$delimiter.$answer->max;
$comment->stranswers[$key] .='<br/>';
$correcttrue->correct = $formattedanswer->answer ;
$correcttrue->true = $answer->answer ;
if ($formattedanswer->answer < $answer->min || $formattedanswer->answer > $answer->max){
$comment->outsidelimit = true ;
$comment->answers[$key] = $key;
$comment->stranswers[$key] .=get_string('trueansweroutsidelimits','qtype_calculated',$correcttrue);//<span class="error">ERROR True answer '..' outside limits</span>';
}else {
$comment->stranswers[$key] .=get_string('trueanswerinsidelimits','qtype_calculated',$correcttrue);//' True answer :'.$calculated->trueanswer.' inside limits';
}
$comment->stranswers[$key] .='';
}
}
return fullclone($comment);
}
function tolerance_types() {
return array('1' => get_string('relative', 'quiz'),
'2' => get_string('nominal', 'quiz'),
// '3' => get_string('geometric', 'quiz')
);
}
function dataset_options($form, $name, $mandatory=true,$renameabledatasets=false) {
// Takes datasets from the parent implementation but
// filters options that are currently not accepted by calculated
// It also determines a default selection...
//$renameabledatasets not implemented anmywhere
list($options, $selected) = $this->dataset_options_from_database($form, $name,'','qtype_calculated');
// list($options, $selected) = $this->dataset_optionsa($form, $name);
foreach ($options as $key => $whatever) {
if (!ereg('^1-', $key) && $key != '0') {
unset($options[$key]);
}
}
if (!$selected) {
if ($mandatory){
$selected = "1-0-$name"; // Default
}else {
$selected = "0"; // Default
}
}
return array($options, $selected);
}
/**
* 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.
*/
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 ='calculatedsimple';
$question->qtype ='calculatedsimple';
$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, $course);
$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;
}
}
//// END OF CLASS ////
//////////////////////////////////////////////////////////////////////////
//// INITIATION - Without this line the question type is not in use... ///
//////////////////////////////////////////////////////////////////////////
question_register_questiontype(new question_calculatedsimple_qtype());
?>