diff --git a/question/type/calculated/datasetitems_form.php b/question/type/calculated/datasetitems_form.php index 4ef7c9656a8..47a0a6b80bb 100644 --- a/question/type/calculated/datasetitems_form.php +++ b/question/type/calculated/datasetitems_form.php @@ -128,7 +128,7 @@ class question_dataset_dependent_items_form extends moodleform { $key1++; } - $addremoveoptions = Array(); + $addremoveoptions = array(); $addremoveoptions['1']='1'; for ($i=10; $i<=100 ; $i+=10){ $addremoveoptions["$i"]="$i"; @@ -262,10 +262,14 @@ class question_dataset_dependent_items_form extends moodleform { } $j--; } + if($question->options->multichoice == 1 ){ + $comment = $this->qtypeobj->multichoice_comment_on_datasetitems($question->id,$answers, $data, $itemnumber); + }else { $comment = $this->qtypeobj->comment_on_datasetitems($question->id,$answers, $data, $itemnumber); if ($comment->outsidelimit) { $this->outsidelimit=$comment->outsidelimit ; } + } $totalcomment=''; foreach ($question->options->answers as $key => $answer) { $totalcomment .= $comment->stranswers[$key].'
'; @@ -313,10 +317,15 @@ class question_dataset_dependent_items_form extends moodleform { } //default answercomment will get ignored if answer element is not in the form. + if($question->options->multichoice == 1 ){ + $comment = $this->qtypeobj->multichoice_comment_on_datasetitems($question->id,$answers, $data, $itemnumber); + }else { + $comment = $this->qtypeobj->comment_on_datasetitems($question->id,$answers, $data, ($this->noofitems+1)); if ($comment->outsidelimit) { $this->outsidelimit=$comment->outsidelimit ; } + } $key1 = 1; foreach ($question->options->answers as $key => $answer) { $formdata['answercomment['.($this->noofitems+$key1).']'] = $comment->stranswers[$key]; diff --git a/question/type/calculated/db/install.xml b/question/type/calculated/db/install.xml index 84bca655d15..fb7cc17956c 100644 --- a/question/type/calculated/db/install.xml +++ b/question/type/calculated/db/install.xml @@ -1,5 +1,8 @@ - + @@ -23,11 +26,18 @@ - + + + + + + + + - +
diff --git a/question/type/calculated/db/upgrade.php b/question/type/calculated/db/upgrade.php index 69978b11456..72f548e75d9 100644 --- a/question/type/calculated/db/upgrade.php +++ b/question/type/calculated/db/upgrade.php @@ -33,7 +33,9 @@ function xmldb_qtype_calculated_upgrade($oldversion) { } upgrade_plugin_savepoint($result, 2008091700, 'qtype', 'calculated'); } + if ($result && $oldversion < 2009082000 ) { //New version in version.php +// this should be changed if merged to 1.9 // let if ($dbman->table_exists()) replace the normal $oldversion test // as in any case the question question_calculated_options should be created @@ -44,6 +46,14 @@ function xmldb_qtype_calculated_upgrade($oldversion) { $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); $table->add_field('question', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0'); $table->add_field('synchronize', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0'); + $table->add_field('multichoice', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0','synchronize'); + $table->add_field('single', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0','multichoice'); + $table->add_field('shuffleanswers', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0','single'); + $table->add_field('correctfeedback', XMLDB_TYPE_TEXT, 'small', null, null, null, null,'shuffleanswers'); + $table->add_field('partiallycorrectfeedback', XMLDB_TYPE_TEXT, 'small', null, null, null, null, 'correctfeedback'); + $table->add_field('incorrectfeedback', XMLDB_TYPE_TEXT, 'small', null, null, null, null, 'partiallycorrectfeedback'); + $table->add_field('answernumbering', XMLDB_TYPE_CHAR, '10', null, XMLDB_NOTNULL, null, 'abc', 'incorrectfeedback'); + /// Adding keys to table question_calculated_options $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); @@ -54,7 +64,73 @@ function xmldb_qtype_calculated_upgrade($oldversion) { // $dbman->create_table doesnt return a result, we just have to trust it $dbman->create_table($table); } + upgrade_plugin_savepoint($result, 2009092000, 'qtype', 'calculated'); + } + if ($result && $oldversion >= 2009082000 && $oldversion < 2009092000 ) { //New version in version.php + /// Define field multichoice to be added to question_calculated_options + $table = new xmldb_table('question_calculated_options'); + $field = new xmldb_field('multichoice', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0', 'synchronize'); + + /// Conditionally launch add field multichoice + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + /// Define field single to be added to question_calculated_options + $table = new xmldb_table('question_calculated_options'); + $field = new xmldb_field('single', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0', 'multichoice'); + + /// Conditionally launch add field single + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + /// Define field shuffleanswers to be added to question_calculated_options + $table = new xmldb_table('question_calculated_options'); + $field = new xmldb_field('shuffleanswers', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0', 'single'); + + /// Conditionally launch add field shuffleanswers + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + /// Define field correctfeedback to be added to question_calculated_options + $table = new xmldb_table('question_calculated_options'); + $field = new xmldb_field('correctfeedback', XMLDB_TYPE_TEXT, 'small', null, null, null, null, 'shuffleanswers'); + + /// Conditionally launch add field correctfeedback + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + /// Define field partiallycorrectfeedback to be added to question_calculated_options + $table = new xmldb_table('question_calculated_options'); + $field = new xmldb_field('partiallycorrectfeedback', XMLDB_TYPE_TEXT, 'small', null, null, null, null, 'correctfeedback'); + + /// Conditionally launch add field partiallycorrectfeedback + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + /// Define field incorrectfeedback to be added to question_calculated_options + $table = new xmldb_table('question_calculated_options'); + $field = new xmldb_field('incorrectfeedback', XMLDB_TYPE_TEXT, 'small', null, null, null, null, 'partiallycorrectfeedback'); + + /// Conditionally launch add field incorrectfeedback + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + /// Define field answernumbering to be added to question_calculated_options + $table = new xmldb_table('question_calculated_options'); + $field = new xmldb_field('answernumbering', XMLDB_TYPE_CHAR, '10', null, XMLDB_NOTNULL, null, 'abc', 'incorrectfeedback'); + + /// Conditionally launch add field answernumbering + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + upgrade_plugin_savepoint($result, 2009092000, 'qtype', 'calculated'); + + + } + /// calculated savepoint reached /// if ($result && $oldversion < YYYYMMDD00) { //New version in version.php /// $result = result of database_manager methods diff --git a/question/type/calculated/edit_calculated_form.php b/question/type/calculated/edit_calculated_form.php index b7fc93d1336..db9956109a9 100644 --- a/question/type/calculated/edit_calculated_form.php +++ b/question/type/calculated/edit_calculated_form.php @@ -20,15 +20,52 @@ class question_edit_calculated_form extends question_edit_form { */ var $qtypeobj; + /** + * Get the list of form elements to repeat, one for each answer. + * @param object $mform the form being built. + * @param $label the label to use for each option. + * @param $gradeoptions the possible grades for each answer. + * @param $repeatedoptions reference to array of repeated options to fill + * @param $answersoption reference to return the name of $question->options field holding an array of answers + * @return array of form fields. + */ + /* function get_per_answer_fields(&$mform, $label, $gradeoptions, &$repeatedoptions, &$answersoption) { + $repeated = array(); + $repeated[] =& $mform->createElement('header', 'answerhdr', $label); + $repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'), array('size' => 50)); + $repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions); + $repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'), + array('course' => $this->coursefilesid)); + $repeatedoptions['answer']['type'] = PARAM_RAW; + $repeatedoptions['fraction']['default'] = 0; + $answersoption = 'answers'; + return $repeated; + }*/ function get_per_answer_fields(&$mform, $label, $gradeoptions, &$repeatedoptions, &$answersoption) { - $repeated = parent::get_per_answer_fields($mform, $label, $gradeoptions, $repeatedoptions, $answersoption); + // $repeated = parent::get_per_answer_fields($mform, $label, $gradeoptions, $repeatedoptions, $answersoption); + $repeated = array(); + $repeated[] =& $mform->createElement('header', 'answerhdr', $label); + // if ($this->editasmultichoice == 1){ + $repeated[] =& $mform->createElement('text', 'answer', get_string('answer', 'quiz'), array('size' => 50)); + $repeated[] =& $mform->createElement('select', 'fraction', get_string('grade'), $gradeoptions); + $repeated[] =& $mform->createElement('htmleditor', 'feedback', get_string('feedback', 'quiz'), + array('course' => $this->coursefilesid)); + $repeatedoptions['answer']['type'] = PARAM_RAW; + $repeatedoptions['fraction']['default'] = 0; + $answersoption = 'answers'; + $mform->setType('answer', PARAM_NOTAGS); $addrepeated = array(); - $addrepeated[] =& $mform->createElement('text', 'tolerance', get_string('tolerance', 'qtype_calculated')); + if ($this->editasmultichoice == 1){ + $addrepeated[] =& $mform->createElement('hidden', 'tolerance'); + $addrepeated[] =& $mform->createElement('hidden', 'tolerancetype'); + }else { + $addrepeated[] =& $mform->createElement('text', 'tolerance', get_string('tolerance', 'qtype_calculated')); + $addrepeated[] =& $mform->createElement('select', 'tolerancetype', get_string('tolerancetype', 'quiz'), $this->qtypeobj->tolerance_types()); + } $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; @@ -36,7 +73,12 @@ class question_edit_calculated_form extends question_edit_form { $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').'='); + if ($this->editasmultichoice == 1){ + $repeated[1]->setLabel('...{={x}+..}...'); + }else { + $repeated[1]->setLabel(get_string('correctanswerformula', 'quiz').'='); + + } return $repeated; } @@ -49,6 +91,8 @@ class question_edit_calculated_form extends question_edit_form { function definition_inner(&$mform) { global $QTYPES; $this->qtypeobj =& $QTYPES[$this->qtype()]; + // echo code left for testing period + // echo "

question ".optional_param('multichoice', '', PARAM_RAW)." optional

";print_r($this->question);echo "

"; $label = get_string("sharedwildcards", "qtype_datasetdependent"); $mform->addElement('hidden', 'initialcategory', 1); $html2 = $this->qtypeobj->print_dataset_definitions_category($this->question); @@ -56,20 +100,93 @@ class question_edit_calculated_form extends question_edit_form { $addfieldsname='updatecategory'; $addstring=get_string("updatecategory", "qtype_calculated"); $mform->registerNoSubmitButton($addfieldsname); + $this->editasmultichoice = 0 ; + if ( isset($this->question->options->multichoice) && $this->question->options->multichoice == '1'){ + $this->editasmultichoice = 1 ; + }else { + if ( !isset($this->question->id ) && '' != optional_param('createoptionbutton', '', PARAM_RAW) && 1 == optional_param('multichoice', '', PARAM_RAW)){ + $this->editasmultichoice = 1 ; + } + if ( !isset($this->question->id )== 0 && '' != optional_param('createoptionbutton', '', PARAM_RAW) && 0 == optional_param('multichoice', '', PARAM_RAW)){ + $this->editasmultichoice = 0 ; + } + } + + /* if ( '' != optional_param('changetomultichoice', '', PARAM_RAW)){ + $this->editasmultichoice = 1 ; + } + if ( '' != optional_param('changetocalculated', '', PARAM_RAW)){ + $this->editasmultichoice = 0 ; + }*/ $mform->insertElementBefore( $mform->createElement('submit', $addfieldsname, $addstring),'listcategory'); + $mform->registerNoSubmitButton('createoptionbutton'); + + if(!isset($this->question->id ) ){ + $mform->addElement('header', 'choicehdr',get_string('Choosingcreationmode', 'qtype_calculated')); + $createoptions = Array(); + + $createoptions['0']=get_string('Regularcalculated', 'qtype_calculated'); + $createoptions['1']=get_string('Multiplechoicecalculated', 'qtype_calculated'); + $addgrp1 = array(); + $addgrp1[] =& $mform->createElement('submit', 'createoptionbutton', get_string('Createas', 'qtype_calculatedsimple')); + $addgrp1[] =& $mform->createElement('select', "multichoice",'' , $createoptions); + $mform->addGroup($addgrp1, 'addgrp1', '', ' ', false); + }else { + $mform->addElement('hidden', 'multichoice',$this->editasmultichoice); + } + + if ($this->editasmultichoice == 1){ + $mform->addElement('header', 'choicehdr',get_string('multichoicecalculatedquestion', 'qtype_calculated')); + $menu = array(get_string('answersingleno', 'qtype_multichoice'), get_string('answersingleyes', 'qtype_multichoice')); + $mform->addElement('select', 'single', get_string('answerhowmany', 'qtype_multichoice'), $menu); + $mform->setDefault('single', 1); + + $mform->addElement('advcheckbox', 'shuffleanswers', get_string('shuffleanswers', 'qtype_multichoice'), null, null, array(0,1)); + $mform->setHelpButton('shuffleanswers', array('multichoiceshuffle', get_string('shuffleanswers','qtype_multichoice'), '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'); + } + $mform->addElement('select', 'answernumbering', get_string('answernumbering', 'qtype_multichoice'), $menu); + $mform->setDefault('answernumbering', 'abc'); + }else { //editing as regular + $mform->addElement('header', 'choicehdr', get_string('regularcalculatedquestion', 'qtype_calculated')); + $mform->addElement('hidden','single', '1'); + $mform->addElement('hidden','shuffleanswers', '1'); + $mform->addElement('hidden','answernumbering', 'abc'); + } $creategrades = get_grade_options(); - $this->add_per_answer_fields($mform, get_string('answerhdr', 'qtype_calculated', '{no}'), + if ($this->editasmultichoice == 1){ + $this->add_per_answer_fields($mform, get_string('choiceno', 'qtype_multichoice', '{no}'), + $creategrades->gradeoptionsfull, max(5, QUESTION_NUMANS_START)); + }else{ + $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}')); + if ($this->editasmultichoice == 1){ + $nounits = optional_param('nounits', 1, PARAM_INT); + $mform->addElement('hidden', 'nounits', $nounits); + $mform->setConstants(array('nounits'=>$nounits)); + for ($i=0; $i< $nounits; $i++) { + $mform->addElement('hidden','unit'."[$i]", optional_param('unit'."[$i]", '', PARAM_NOTAGS)); + $mform->addElement('hidden', 'multiplier'."[$i]", optional_param('multiplier'."[$i]", '', PARAM_NUMBER)); + } - $repeated[] =& $mform->createElement('text', 'unit', get_string('unit', 'quiz')); + }else { + $repeated[] =& $mform->createElement('header', 'unithdr', get_string('unithdr', 'qtype_numerical', '{no}')); + $repeated[] =& $mform->createElement('text', 'unit', get_string('unit', 'quiz')); + $repeated[] =& $mform->createElement('text', 'multiplier', get_string('multiplier', '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)){ @@ -84,12 +201,28 @@ class question_edit_calculated_form extends question_edit_form { } $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); } + if ($this->editasmultichoice == 1){ + $mform->setType('addunits','hidden'); + $mform->addElement('header', 'overallfeedbackhdr', get_string('overallfeedback', 'qtype_multichoice')); + + foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) { + $mform->addElement('htmleditor', $feedbackname, get_string($feedbackname, 'qtype_multichoice'), + array('course' => $this->coursefilesid)); + $mform->setType($feedbackname, PARAM_RAW); + } + }else { + foreach (array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback') as $feedbackname) { + $mform->addElement('hidden', $feedbackname); + $mform->setType($feedbackname, PARAM_RAW); + } + } //hidden elements $mform->addElement('hidden', 'synchronize', ''); if (isset($this->question->options)&& isset($this->question->options->synchronize) ){ @@ -104,6 +237,9 @@ class question_edit_calculated_form extends question_edit_form { } function set_data($question) { + if (isset($this->editasmultichoice)){ + $default_values['multichoice']= 1 ; //$this->editasmultichoice ; + } if (isset($question->options)){ $answers = $question->options->answers; if (count($answers)) { @@ -119,20 +255,30 @@ class question_edit_calculated_form extends question_edit_form { $key++; } } - $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++; + if (isset($question->options->units)){ + $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++; + } } } } + if (isset($question->options->single)){ + $default_values['single'] = $question->options->single; + $default_values['answernumbering'] = $question->options->answernumbering; + $default_values['shuffleanswers'] = $question->options->shuffleanswers; + $default_values['correctfeedback'] = $question->options->correctfeedback; + $default_values['partiallycorrectfeedback'] = $question->options->partiallycorrectfeedback; + $default_values['incorrectfeedback'] = $question->options->incorrectfeedback; + } $default_values['submitbutton'] = get_string('nextpage', 'qtype_calculated'); $default_values['makecopy'] = get_string('makecopynextpage', 'qtype_calculated'); /* set the wild cards category display given that on loading the category element is @@ -166,6 +312,11 @@ class question_edit_calculated_form extends question_edit_form { } function validation($data, $files) { + // echo code left for testing period + + // echo "

question

";print_r($this->question);echo "

"; + // echo "

data

";print_r($data);echo "

"; + $errors = parent::validation($data, $files); //verifying for errors in {=...} in question text; $qtext = ""; @@ -201,6 +352,48 @@ class question_edit_calculated_form extends question_edit_form { $errors['answer['.$key.']'] = get_string('atleastonewildcard', 'qtype_datasetdependent'); } } + if ($data['multichoice']== 1 ){ + foreach ($answers as $key => $answer){ + $trimmedanswer = trim($answer); + if (($trimmedanswer!='')||$answercount==0){ + //verifying for errors in {=...} in answer text; + $qanswer = ""; + $qanswerremaining = $trimmedanswer ; + $possibledatasets = $this->qtypeobj->find_dataset_names($trimmedanswer); + foreach ($possibledatasets as $name => $value) { + $qanswerremaining = str_replace('{'.$name.'}', '1', $qanswerremaining); + } + // echo "numericalquestion qanswerremaining
";print_r($possibledatasets);
+                while  (preg_match('~\{=([^[:space:]}]*)}~', $qanswerremaining, $regs1)) {
+                    $qanswersplits = explode($regs1[0], $qanswerremaining, 2);
+                    $qanswer =$qanswer.$qanswersplits[0];
+                    $qanswerremaining = $qanswersplits[1];
+                    if (!empty($regs1[1]) && $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])) {
+                        if(!isset($errors['answer['.$key.']'])){
+                            $errors['answer['.$key.']'] = $formulaerrors.':'.$regs1[1] ;
+                        }else {
+                            $errors['answer['.$key.']'] .= '
'.$formulaerrors.':'.$regs1[1]; + } + } + } + } + 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++; + } + + } + }else{ foreach ($answers as $key => $answer){ //check no of choices // the * for everykind of answer not actually implemented @@ -237,6 +430,7 @@ class question_edit_calculated_form extends question_edit_form { } }*/ } + } //grade checking : /// Perform sanity checks on fractional grades /*if ( ) { diff --git a/question/type/calculated/questiontype.php b/question/type/calculated/questiontype.php index b962efe545b..479aff719c6 100644 --- a/question/type/calculated/questiontype.php +++ b/question/type/calculated/questiontype.php @@ -28,7 +28,15 @@ class question_calculated_qtype extends default_questiontype { function get_question_options(&$question) { // First get the datasets and default options - global $CFG, $DB, $OUTPUT; + global $CFG, $DB, $OUTPUT,$QTYPES; + if (!$question->options = $DB->get_record('question_calculated_options', array('question' => $question->id))) { + // echo $OUTPUT->notification('Error: Missing question options for calculated question'.$question->id.'!'); + // return false; + $question->options->synchronize = 0; + $question->options->multichoice = 0; + + } + if (!$question->options->answers = $DB->get_records_sql( "SELECT a.*, c.tolerance, c.tolerancetype, c.correctanswerlength, c.correctanswerformat " . "FROM {question_answers} a, " . @@ -39,9 +47,6 @@ class question_calculated_qtype extends default_questiontype { echo $OUTPUT->notification('Error: Missing question answer for calculated question ' . $question->id . '!'); return false; } - if (!$question->options->synchronize = $DB->get_field('question_calculated_options', 'synchronize', array('question' => $question->id)) ) { - $question->options->synchronize = 0; - } /* if(false === parent::get_question_options($question)) { @@ -71,8 +76,8 @@ class question_calculated_qtype extends default_questiontype { $answer->correctanswerformat = $options->correctanswerformat; }*/ - $virtualqtype = $this->get_virtual_qtype(); - $virtualqtype->get_numerical_units($question); + //$virtualqtype = $this->get_virtual_qtype( $question); + $QTYPES['numerical']->get_numerical_units($question); if( isset($question->export_process)&&$question->export_process){ $question->options->datasets = $this->get_datasets_for_export($question); @@ -142,6 +147,13 @@ class question_calculated_qtype extends default_questiontype { $options->question = $question->id; } $options->synchronize = $question->synchronize; + $options->multichoice = $question->multichoice; + $options->single = $question->single; + $options->answernumbering = $question->answernumbering; + $options->shuffleanswers = $question->shuffleanswers; + $options->correctfeedback = trim($question->correctfeedback); + $options->partiallycorrectfeedback = trim($question->partiallycorrectfeedback); + $options->incorrectfeedback = trim($question->incorrectfeedback); if ($update) { if (!$DB->update_record("question_calculated_options", $options)) { $result->error = "Could not update calculated question options! (id=$options->id)"; @@ -164,7 +176,7 @@ class question_calculated_qtype extends default_questiontype { } // Save the units. - $virtualqtype = $this->get_virtual_qtype(); + $virtualqtype = $this->get_virtual_qtype( $question); $result = $virtualqtype->save_numerical_units($question); if (isset($result->error)) { return $result; @@ -306,12 +318,17 @@ class question_calculated_qtype extends default_questiontype { $state->options->dataset = $this->pick_question_dataset($question,$state->options->datasetitem); $state->responses = array('' => $regs[2]); + if ( isset($question->options->multichoice) && $question->options->multichoice == '1'){ + $virtualqtype = $this->get_virtual_qtype( $question); + + return $virtualqtype->restore_session_and_responses($question, $state); + } return true; } function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) { // Find out how many datasets are available - global $CFG, $DB; + global $CFG, $DB, $QTYPES; if(!$maxnumber = (int)$DB->get_field_sql( "SELECT MIN(a.itemcount) FROM {question_dataset_definitions} a, @@ -348,14 +365,47 @@ class question_calculated_qtype extends default_questiontype { $state->options->dataset = $this->pick_question_dataset($question,$state->options->datasetitem); $state->responses = array('' => ''); + if ($question->options->multichoice == 1 ) { + // 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); + } + $state->options->order = $answerids; + // Create empty responses + if ($question->options->single) { + $state->responses = array('' => ''); + } else { + $state->responses = array(); + } + } return true; } function save_session_and_responses(&$question, &$state) { global $DB; - $responses = 'dataset'.$state->options->datasetitem.'-'. - $state->responses['']; - // Set the legacy answer field + $responses = 'dataset'.$state->options->datasetitem.'-' ; + if ( isset($question->options->multichoice) && $question->options->multichoice == '1'){ + + // Bundle the answer order and the responses into the legacy answer + // field. + // The serialized format for multiple choice quetsions + // is (optionally) a comma separated list of answer ids + // followed by a colon, followed by another comma separated + // list of answer ids, which are the radio/checkboxes that were + // ticked. + // E.g. 1,3,2,4:2,4 means that the answers were shown in the order + // 1, 3, 2 and then 4 and the answers 2 and 4 were checked. + $responses .= implode(',', $state->options->order) . ':'; + $responses .= implode(',', $state->responses); + }else { + // regular numeric type + $responses .= $state->responses['']; + } + + // Set the legacy answer field if (!$DB->set_field('question_states', 'answer', $responses, array('id'=> $state->id))) { return false; } @@ -621,30 +671,18 @@ class question_calculated_qtype extends default_questiontype { } break; case 'datasetdefinitions': - // calculated options - $update = true ; - $options = $DB->get_record("question_calculated_options", array("question" => $question->id)); - if (!$options) { - $update = false; - $options = new stdClass; - $options->question = $question->id; - } - if($form->synchronize == 1 ){ - $options->synchronize = $form->synchronize; - }else { - $options->synchronize = 0 ; - } - if ($update) { - if (!$DB->update_record("question_calculated_options", $options)) { - $result->error = "Could not update calculated question options! (id=$options->id)"; - return $result; - } - } else { - if (!$DB->insert_record("question_calculated_options", $options)) { - $result->error = "Could not insert calculated question options!"; - return $result; - } - } + // calculated options + // it cannot go here without having done the first page + // so the question_calculated_options should exist + // only need to update the synchronize field + if($form->synchronize == 1 ){ + $options_synchronize = 1 ; + }else { + $options_synchronize = 0 ; + } + if (!$DB->set_field('question_calculated_options', 'synchronize', $options_synchronize, array("question" => $question->id))) { + return false; + } $this->save_dataset_definitions($form); break; @@ -689,19 +727,49 @@ class question_calculated_qtype extends default_questiontype { function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) { // Substitute variables in questiontext before giving the data to the // virtual type for printing - $virtualqtype = $this->get_virtual_qtype(); - if($unit = $virtualqtype->get_default_numerical_unit($question)){ - $unit = $unit->unit; - } else { - $unit = ''; + $virtualqtype = $this->get_virtual_qtype( $question); + // why $unit as it is not use + if ($question->options->multichoice != 1 ) { + if($unit = $virtualqtype->get_default_numerical_unit($question)){ + $unit = $unit->unit; + } else { + $unit = ''; + } } + // We modify the question to look like a numerical question $numericalquestion = fullclone($question); + if ($question->options->multichoice == 1 ) { + foreach ($numericalquestion->options->answers as $key => $answer) { + $answer->answer = $this->substitute_variables($answer->answer, $state->options->dataset); + //evaluate the equations i.e {=5+4) + $qtext = ""; + $qtextremaining = $answer->answer ; + while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) { + $qtextsplits = explode($regs1[0], $qtextremaining, 2); + $qtext =$qtext.$qtextsplits[0]; + $qtextremaining = $qtextsplits[1]; + if (empty($regs1[1])) { + $str = ''; + } else { + if( $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])){ + $str=$formulaerrors ; + }else { + eval('$str = '.$regs1[1].';'); + } + } + $qtext = $qtext.$str ; + } + $answer->answer = $qtext.$qtextremaining ; ; + } + }else { + foreach ($numericalquestion->options->answers as $key => $answer) { $answer = fullclone($numericalquestion->options->answers[$key]); $numericalquestion->options->answers[$key]->answer = $this->substitute_variables_and_eval($answer->answer, $state->options->dataset); } + } $numericalquestion->questiontext = $this->substitute_variables( $numericalquestion->questiontext, $state->options->dataset); //evaluate the equations i.e {=5+4) @@ -723,6 +791,7 @@ class question_calculated_qtype extends default_questiontype { $qtext = $qtext.$str ; } $numericalquestion->questiontext = $qtext.$qtextremaining ; // end replace equations + $virtualqtype->print_question_formulation_and_controls($numericalquestion, $state, $cmoptions, $options); } function grade_responses(&$question, &$state, $cmoptions) { @@ -734,7 +803,7 @@ class question_calculated_qtype extends default_questiontype { $numericalquestion->options->answers[$key]->answer = $this->substitute_variables_and_eval($answer, $state->options->dataset); } - $virtualqtype = $this->get_virtual_qtype(); + $virtualqtype = $this->get_virtual_qtype( $question); return $virtualqtype->grade_responses($numericalquestion, $state, $cmoptions) ; } @@ -757,7 +826,7 @@ class question_calculated_qtype extends default_questiontype { $answer->answer = $this->substitute_variables_and_eval($answer->answer, $state->options->dataset); } - $virtualqtype = $this->get_virtual_qtype(); + $virtualqtype = $this->get_virtual_qtype( $question); return $virtualqtype->check_response($numericalquestion, $state) ; } @@ -765,7 +834,7 @@ class question_calculated_qtype extends default_questiontype { function get_actual_response(&$question, &$state) { // Substitute variables in questiontext before giving the data to the // virtual type - $virtualqtype = $this->get_virtual_qtype(); + $virtualqtype = $this->get_virtual_qtype( $question); $unit = $virtualqtype->get_default_numerical_unit($question); // We modify the question to look like a numerical question @@ -797,8 +866,13 @@ class question_calculated_qtype extends default_questiontype { function create_virtual_qtype() { global $CFG; - require_once("$CFG->dirroot/question/type/numerical/questiontype.php"); - return new question_numerical_qtype(); + /* if ($this->options->multichoice == 1 ) { + require_once("$CFG->dirroot/question/type/multichoice/questiontype.php"); + return new question_multichoice_qtype(); + }else { */ + require_once("$CFG->dirroot/question/type/numerical/questiontype.php"); + return new question_numerical_qtype(); + // } } function supports_dataset_item_generation() { @@ -1097,7 +1171,7 @@ class question_calculated_qtype extends default_questiontype { function comment_header($question) { //$this->get_question_options($question); - $strheader = array(); + $strheader = ''; $delimiter = ''; $answers = $question->options->answers; @@ -1108,13 +1182,17 @@ class question_calculated_qtype extends default_questiontype { } else { $strheader .= $delimiter.$answer->answer; } - $delimiter = '


'; + if($question->options->multichoice == 1 ){ + $delimiter = '
'; + }else{ + $delimiter = '


'; + } } return $strheader; } function comment_on_datasetitems($questionid, $answers,$data, $number) { - global $DB; + global $DB, $QTYPES; $comment = new stdClass; $comment->stranswers = array(); $comment->outsidelimit = false ; @@ -1131,7 +1209,7 @@ class question_calculated_qtype extends default_questiontype { $strmax = get_string('max', 'quiz'); $errors = ''; $delimiter = ': '; - $virtualqtype = $this->get_virtual_qtype(); + $virtualqtype = & $QTYPES['numerical'];// $this->get_virtual_qtype( $question); foreach ($answers as $key => $answer) { $formula = $this->substitute_variables($answer->answer,$data); $formattedanswer = qtype_calculated_calculate_answer( @@ -1169,6 +1247,84 @@ class question_calculated_qtype extends default_questiontype { } return fullclone($comment); } + function multichoice_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 = ': '; + foreach ($answers as $key => $answer) { + $answer->answer = $this->substitute_variables($answer->answer, $data); + //evaluate the equations i.e {=5+4) + $qtext = ""; + $qtextremaining = $answer->answer ; + while (preg_match('~\{=([^[:space:]}]*)}~', $qtextremaining, $regs1)) { + $qtextsplits = explode($regs1[0], $qtextremaining, 2); + $qtext =$qtext.$qtextsplits[0]; + $qtextremaining = $qtextsplits[1]; + if (empty($regs1[1])) { + $str = ''; + } else { + if( $formulaerrors = qtype_calculated_find_formula_errors($regs1[1])){ + $str=$formulaerrors ; + }else { + eval('$str = '.$regs1[1].';'); + } + } + $qtext = $qtext.$str ; + } + $answer->answer = $qtext.$qtextremaining ; ; + $comment->stranswers[$key]= $answer->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".'

'; + } else if ($formula === '*'){ + $comment->stranswers[$key] = $formula.' = '.get_string('anyvalue','qtype_calculated').'


'; + }else{ + $comment->stranswers[$key]= $formula.' = '.$formattedanswer->answer.'
' ; + $comment->stranswers[$key] .= $strmin. $delimiter.$answer->min.'---'; + $comment->stranswers[$key] .= $strmax.$delimiter.$answer->max; + $comment->stranswers[$key] .='
'; + $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);//ERROR True answer '..' outside limits'; + }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'), @@ -1222,12 +1378,12 @@ class question_calculated_qtype extends default_questiontype { } function print_question_grading_details(&$question, &$state, &$cmoptions, &$options) { - $virtualqtype = $this->get_virtual_qtype(); + $virtualqtype = $this->get_virtual_qtype( $question); $virtualqtype->print_question_grading_details($question, $state, $cmoptions, $options) ; } function get_correct_responses(&$question, &$state) { - $virtualqtype = $this->get_virtual_qtype(); + $virtualqtype = $this->get_virtual_qtype( $question); if($unit = $virtualqtype->get_default_numerical_unit($question)){ $unit = $unit->unit; } else { @@ -1266,6 +1422,14 @@ class question_calculated_qtype extends default_questiontype { } return $str; } + function evaluate_equations($str, $dataset){ + $formula = $this->substitute_variables($str, $dataset) ; + if ($error = qtype_calculated_find_formula_errors($formula)) { + return $error; + } + return $str; + } + function substitute_variables_and_eval($str, $dataset) { $formula = $this->substitute_variables($str, $dataset) ; @@ -1667,9 +1831,12 @@ class question_calculated_qtype extends default_questiontype { } return $text ; } - function get_virtual_qtype() { - if (!$this->virtualqtype) { - $this->virtualqtype = $this->create_virtual_qtype(); + function get_virtual_qtype($question) { + global $QTYPES; + if ( isset($question->options->multichoice) && $question->options->multichoice == '1'){ + $this->virtualqtype =& $QTYPES['multichoice']; + }else { + $this->virtualqtype =& $QTYPES['numerical']; } return $this->virtualqtype; } @@ -1712,6 +1879,13 @@ class question_calculated_qtype extends default_questiontype { $status = fwrite ($bf,start_tag("CALCULATED_OPTIONS",$level,true)); //Print calculated_option contents fwrite ($bf,full_tag("SYNCHRONIZE",$level+1,false,$calculated_option->synchronize)); + fwrite ($bf,full_tag("MULTIPLECHOICE",$level+1,false,$calculated_option->multiplechoice)); + fwrite ($bf,full_tag("SINGLE",$level+1,false,$calculated_option->single)); + fwrite ($bf,full_tag("SHUFFLEANSWERS",$level+1,false,$calculated_option->shuffleanswers)); + fwrite ($bf,full_tag("CORRECTFEEDBACK",$level+1,false,$calculated_option->correctfeedback)); + fwrite ($bf,full_tag("PARTIALLYCORRECTFEEDBACK",$level+1,false,$calculated_option->partiallycorrectfeedback)); + fwrite ($bf,full_tag("INCORRECTFEEDBACK",$level+1,false,$calculated_option->incorrectfeedback)); + fwrite ($bf,full_tag("ANSWERNUMBERING",$level+1,false,$calculated_option->answernumbering)); $status = fwrite ($bf,end_tag("CALCULATED_OPTIONS",$level,true)); } //Now print question_answers @@ -1776,7 +1950,7 @@ class question_calculated_qtype extends default_questiontype { $calculatedoptions = $info['#']['CALCULATED_OPTIONS']; //Iterate over calculated_options - for($i = 0; $i < sizeof($calculatedoptions); $i++) { + for($i = 0; $i < sizeof($calculatedoptions); $i++){ $cal_info = $calculatedoptions[$i]; //traverse_xmlize($cal_info); //Debug //print_object ($GLOBALS['traverse_array']); //Debug @@ -1785,7 +1959,13 @@ class question_calculated_qtype extends default_questiontype { //Now, build the question_calculated_options record structure $calculated_options->questionid = $new_question_id; $calculated_options->synchronize = backup_todb($cal_info['#']['SYNCHRONIZE']['0']['#']); - } + $calculated_options->multichoice = backup_todb($cal_info['#']['MULTICHOICe']['0']['#']); + $calculated_options->single = backup_todb($cal_info['#']['SINGLE']['0']['#']); + $calculated_options->shuffleanswers = isset($cal_info['#']['SHUFFLEANSWERS']['0']['#'])?backup_todb($mul_info['#']['SHUFFLEANSWERS']['0']['#']):''; + $calculated_options->correctfeedback = backup_todb($cal_info['#']['CORRECTFEEDBACK']['0']['#']); + $calculated_options->partiallycorrectfeedback = backup_todb($cal_info['#']['PARTIALLYCORRECTFEEDBACK']['0']['#']); + $calculated_options->incorrectfeedback = backup_todb($cal_info['#']['INCORRECTFEEDBACK']['0']['#']); + $calculated_options->answernumbering = backup_todb($cal_info['#']['ANSWERNUMBERING']['0']['#']); //The structure is equal to the db, so insert the question_calculated_options $newid = $DB->insert_record ("question_calculated_options",$calculated_options); @@ -1801,7 +1981,7 @@ class question_calculated_qtype extends default_questiontype { backup_flush(300); } } - + } //Now restore numerical_units $status = question_restore_numerical_units ($old_question_id,$new_question_id,$cal_info,$restore); diff --git a/question/type/calculated/version.php b/question/type/calculated/version.php index add73d0b3fa..c11728d2e66 100644 --- a/question/type/calculated/version.php +++ b/question/type/calculated/version.php @@ -1,5 +1,5 @@ version = 2009082000; +$plugin->version = 2009092000; $plugin->requires = 2007101000; ?>