The calculated question wizard has been restructured/rewritten. A new question is now only saved when the first dataset item is added. And hopefully the code is somewhat clearer and easier to understand. Additionally it all works through the (slightly adapted) standard question.php mechanisms and doesn't hack around it.

This commit is contained in:
mindforge
2005-06-11 13:16:57 +00:00
parent eceda4634f
commit 3ed1ef582f
8 changed files with 736 additions and 644 deletions
@@ -1,7 +1,5 @@
<form name="theform" method="post" action="question.php">
<center>
<input type="hidden" name="nextwizardpage" value="<?php p($nextwizardpage)?>" />
<?php foreach ($calculatedmessages as $message) {formerr("$message<br/>");} ?>
<table cellpadding="5">
<tr valign="top">
<td align="right"><b><?php print_string("category", "quiz") ?>:</b></td>
@@ -77,7 +75,7 @@
<tr valign="top">
<td align="right"><b><?php print_string("correctanswerformula", "quiz") ?>:</b></td>
<td>
<input align="left" type="text" id="formula0" name="answer[]" size="20" value="<?php p($answers[0]->answer) ?>" alt="<?php print_string("correctanswerformula", "quiz") ?>" />&nbsp;&nbsp;
<input align="left" type="text" id="formula0" name="answers[]" size="20" value="<?php p($answers[0]->answer) ?>" alt="<?php print_string("correctanswerformula", "quiz") ?>" />&nbsp;&nbsp;
<input type="hidden" name="fraction[]" value="1.0" />
</td>
</tr>
@@ -153,10 +151,10 @@ for ($i=1; $i<count($units); $i++) {
} /// END for
?>
</table>
<input type="hidden" name="sesskey" value="<?php echo $USER->sesskey; ?>" />
<input type="hidden" name="id" value="<?php p($question->id) ?>" />
<input type="hidden" name="qtype" value="<?php p($question->qtype) ?>" />
<input type="hidden" name="wizardpage" value="question" />
<input type="submit" onclick="return determineMinAndMax();" value="<?php print_string("savechanges") ?>" />
</center>
</form>
@@ -2,163 +2,12 @@
// Get a handle to the question type we are dealing with here
$qtypeobj = $QUIZ_QTYPES[CALCULATED];
if (isset($form->editdatasets) && $form->editdatasets) {
require("$CFG->dirroot/mod/quiz/questiontypes/datasetdependent/datasetitems.php");
exit();
}
$calculatedmessages = array();
if ($form) {
// Verify the quality of the question properties
if (empty($question->name)) {
$calculatedmessages[] = get_string('missingname', 'quiz');
}
if (empty($question->questiontext)) {
$calculatedmessages[] = get_string('missingquestiontext', 'quiz');
}
// Formula stuff (verify some of them)
$answers[0]->answer = trim(array_shift($form->answer))
and false===($formulaerrors =
quiz_qtype_calculated_find_formula_errors($answers[0]->answer))
or $answers[0]->answer
and $calculatedmessages[] = $formulaerrors
or $calculatedmesages[] = get_string('missingformula', 'quiz');
$answers[0]->tolerance = array_shift($form->tolerance)
or $answers[0]->tolerance = 0.0;
is_numeric($answers[0]->tolerance)
or $calculatedmessages[] = get_string('tolerancemustbenumeric', 'quiz');
$answers[0]->feedback = array_shift($form->feedback);
// Let's trust the drop down menus.
$answers[0]->tolerancetype = array_shift($form->tolerancetype);
$answers[0]->fraction = array_shift($form->fraction);
$answers[0]->correctanswerlength = array_shift($form->correctanswerlength);
$answers[0]->correctanswerformat = array_shift($form->correctanswerformat);
2 == $answers[0]->correctanswerformat
and 0 == $answers[0]->correctanswerlength
and $calculatedmessages[]=get_string('zerosignificantfiguresnotallowed','quiz');
// Fill with remaining answers, in case calculated.html
// supports multiple formulas.
$i = 1;
foreach ($form->answer as $key => $answer) {
if (trim($answer)) {
$answers[$i]->answer = trim($answer);
$answers[$i]->tolerance = $form->tolerance[$key]
or $answers[$i]->tolerance = 0.0;
$answers[$i]->tolerancetype = $form->tolerancetype[$key];
$answers[$i]->correctanswerlength =
$form->correctanswerlength[$key];
$answers[$i]->correctanswerformat =
$form->correctanswerformat[$key];
$answers[$i]->fraction = $form->fraction[$key];
$answers[$i]->feedback = $form->feedback[$key];
// Check for errors:
false === ($formulaerrors =
quiz_qtype_calculated_find_formula_errors($answer))
or $calculatedmessages[] = $formulaerrors;
is_numeric($answers[$i]->tolerance)
or $calculatedmessages[] = get_string('tolerancemustbenumeric',
'quiz');
// Increase answer count
++$i;
}
}
// Finally the units:
// Start with the default units...
$units[0]->unit = array_shift($form->unit);
array_shift($form->multiplier); // In case it is not 1.0
$units[0]->multiplier = 1.0; // Must!
// Accept other units if they have legal multipliers
$i = 1;
foreach ($form->multiplier as $key => $multiplier) {
if ($multiplier && is_numeric($multiplier)) {
$units[$i]->multiplier = $multiplier;
$units[$i]->unit = $form->unit[$key];
++$i;
}
}
if (empty($calculatedmessages)) {
/*First page in the question wizard (calculated.html) passed all right!*/
if (!empty($form->dataset)) {
/* Second page in the question wizard has also passed all right */
/***** Save question! ... ****/
$subtypeoptions->answers = $answers;
$subtypeoptions->units = $units;
$question = $qtypeobj->save_question
($question, $form, $course, $subtypeoptions);
/***** ... and continue to the dataset item editing: *****/
require("$CFG->dirroot/mod/quiz/questiontypes/datasetdependent/datasetitems.php");
exit();
}
/***** Now continue by preparing for the second page ******
***** in the question wizard: "questiondatasets.html" ******/
$datasetmessage = '';
/*** Answer information is not to be shown ***/
$hiddeninputnames= array();
$hiddeninputvalues= array();
foreach ($answers as $answer) {
$hiddeninputnames[] = 'answer[]';
$hiddeninputvalues[] = $answer->answer;
$hiddeninputnames[] = 'fraction[]';
$hiddeninputvalues[] = $answer->fraction;
$hiddeninputnames[] = 'feedback[]';
$hiddeninputvalues[] = $answer->feedback;
$hiddeninputnames[] = 'tolerance[]';
$hiddeninputvalues[] = $answer->tolerance;
$hiddeninputnames[] = 'tolerancetype[]';
$hiddeninputvalues[] = $answer->tolerancetype;
$hiddeninputnames[] = 'correctanswerlength[]';
$hiddeninputvalues[] = $answer->correctanswerlength;
$hiddeninputnames[] = 'correctanswerformat[]';
$hiddeninputvalues[] = $answer->correctanswerformat;
}
foreach ($units as $unit) {
$hiddeninputnames[] = 'unit[]';
$hiddeninputvalues[] = $unit->unit;
$hiddeninputnames[] = 'multiplier[]';
$hiddeninputvalues[] = $unit->multiplier;
}
/*** Determine possible and mandatory datasets... ***/
$possibledatasets = $qtypeobj->find_dataset_names($question->questiontext);
$mandatorydatasets = array();
foreach ($answers as $answer) {
$mandatorydatasets += $qtypeobj
->find_dataset_names($answer->answer);
}
$datasets = $qtypeobj->construct_dataset_menus(
$question, $mandatorydatasets, $possibledatasets);
/*** Print the page ***/
print_heading_with_help(get_string("choosedatasetproperties", "quiz"), "questiondatasets", "quiz");
require("$CFG->dirroot/mod/quiz/questiontypes/datasetdependent/questiondatasets.html");
exit();
}
} else {
if (empty($form)) {
/*********************************************************/
/***** First page in question wizard - calculated.html! **/
/*********************************************************/
// Inside this block everything works as for non-wizardquestions
// The layout of the editing page will only support
// one formula alternative for calculated questions.
@@ -167,7 +16,7 @@ if ($form) {
// does not have any limit.
$answers= array();
for ($i=0; $i<6; $i++) {
/*** Make answer slots with default values ***/
// Make answer slots with default values
$answers[$i]->answer = "";
$answers[$i]->feedback = "";
$answers[$i]->fraction = "1.0";
@@ -176,16 +25,16 @@ if ($form) {
$answers[$i]->correctanswerlength = "2"; // Defaults to two ...
$answers[$i]->correctanswerformat = "1"; // ... decimals
}
if (!empty($question->id)) {
$QUIZ_QTYPES[$question->qtype]->get_question_options($question);
$answersraw = $question->options->answers;
if (!empty($answersraw)) {
/*** Overwrite the default valued answer slots ***
*** with correct values from database ***/
$i=0;
foreach ($answersraw as $answer) {
$answers[$i] = $answer;
$i++;
if (!empty($question->options->answers)) {
// Overwrite the default valued answer slots
// with correct values from database
$answersraw = array_values($question->options->answers);
$n = count($answersraw);
for ($i = 0; $i < $n; $i++) {
$answers[$i] = $answersraw[$i];
}
}
}
@@ -233,8 +82,12 @@ if ($form) {
}
}
}
print_heading_with_help(get_string("editingcalculated", "quiz"), "calculated", "quiz");
require("$CFG->dirroot/mod/quiz/questiontypes/calculated/calculated.html");
} else { // $form is not empty
/*********************************************************/
/***** Any subsequent page of the question wizard **/
/*********************************************************/
$qtypeobj->print_next_wizard_page($question, $form, $course);
}
print_heading_with_help(get_string("editingcalculated", "quiz"), "calculated", "quiz");
require("$CFG->dirroot/mod/quiz/questiontypes/calculated/calculated.html");
?>
@@ -32,10 +32,6 @@ class quiz_calculated_qtype extends quiz_dataset_dependent_questiontype {
$options->correctanswerlength = 2;
$options->correctanswerformat = 1; // decimals
}
$question->options->tolerance = $options->tolerance;
$question->options->tolerancetype = $options->tolerancetype;
$question->options->correctanswerlength = $options->correctanswerlength;
$question->options->correctanswerformat = $options->correctanswerformat;
// For historic reasons we also need these fields in the answer objects.
// This should eventually be removed and related code changed to use
@@ -49,35 +45,48 @@ class quiz_calculated_qtype extends quiz_dataset_dependent_questiontype {
$answer->correctanswerformat = $options->correctanswerformat;
}
$this->get_numerical_units($question);
$virtualqtype = $this->get_virtual_qtype();
$virtualqtype->get_numerical_units($question);
return true;
}
function get_numerical_units(&$question) {
$virtualqtype = $this->get_virtual_qtype();
return $virtualqtype->get_numerical_units($question);
}
function save_question_options($question, $options) {
function save_question_options($question) {
//$options = $question->subtypeoptions;
// Get old answers:
$oldanswers = array();
if ($this->get_question_options($question)) {
$oldanswers = $question->options->answers;
global $CFG;
if (!$oldanswers = get_records_sql(
"SELECT a.*, c.tolerance, c.tolerancetype,
c.correctanswerlength, c.id calcid
FROM {$CFG->prefix}quiz_answers a,
{$CFG->prefix}quiz_calculated c
WHERE c.question = $question->id AND a.id = c.answer")) {
$oldanswers = array();
}
// Update with new answers
$answerrec->question = $calcrec->question = $question->id;
foreach ($options->answers as $newanswer) {
$answerrec->answer = $newanswer->answer;
$answerrec->fraction = $newanswer->fraction;
$answerrec->feedback = $newanswer->feedback;
$calcrec->tolerance = $newanswer->tolerance;
$calcrec->tolerancetype = $newanswer->tolerancetype;
$calcrec->correctanswerlength = $newanswer->correctanswerlength;
$calcrec->correctanswerformat = $newanswer->correctanswerformat;
$n = count($question->answers);
for ($i = 0; $i < $n; $i++) {
$answerrec->answer = $question->answers[$i];
$answerrec->fraction = isset($question->fraction[$i])
? $question->fraction[$i] : 1.0;
$answerrec->feedback = isset($question->feedback[$i])
? $question->feedback[$i] : '';
$calcrec->tolerance = isset($question->tolerance[$i])
? $question->tolerance[$i]
: $question->tolerance[0];
$calcrec->tolerancetype = isset($question->tolerancetype[$i])
? $question->tolerancetype[$i]
: $question->tolerancetype[0];
$calcrec->correctanswerlength = isset($question->correctanswerlength[$i])
? $question->correctanswerlength[$i]
: $question->correctanswerlength[0];
$calcrec->correctanswerformat = isset($question->correctanswerformat[$i])
? $question->correctanswerformat[$i]
: $question->correctanswerformat[0];
if ($oldanswer = array_shift($oldanswers)) {
// Reuse old record:
// Reuse old records:
$calcrec->answer = $answerrec->id = $oldanswer->id;
$calcrec->id = $oldanswer->calcid;
if (!update_record('quiz_answers', $answerrec)) {
@@ -94,15 +103,15 @@ class quiz_calculated_qtype extends quiz_dataset_dependent_questiontype {
unset($answerrec->id);
unset($calcrec->id);
if (!($calcrec->answer = insert_record('quiz_answers',
$answerrec))) {
error("Unable to insert answer for calculated question $question->name");
$answerrec))) {
error("Unable to insert answer for calculated question $question->id");
} else {
// notify("Answer inserted successfully for calculated question $question->name");
// notify("Answer inserted successfully for calculated question $question->id");
}
if (!insert_record('quiz_calculated', $calcrec)) {
error("Unable to insert options calculared question $question->name");
error("Unable to insert options calculared question $question->id");
} else {
// notify("Options inserted successfully for calculated question $question->name");
// notify("Options inserted successfully for calculated question $question->id");
}
}
}
@@ -110,23 +119,84 @@ class quiz_calculated_qtype extends quiz_dataset_dependent_questiontype {
// Delete excessive records:
foreach ($oldanswers as $oldanswer) {
if (!delete_records('quiz_answers', 'id', $oldanswer->id)) {
error("Unable to delete old answers for calculated question $question->name");
error("Unable to delete old answers for calculated question $question->id");
} else {
// notify("Old answers deleted successfully for calculated question $question->name");
// notify("Old answers deleted successfully for calculated question $question->id");
}
if (!delete_records('quiz_calculated', 'id', $oldanswer->calcid)) {
error("Unable to delete old options for calculated question $question->name");
error("Unable to delete old options for calculated question $question->id");
} else {
// notify("Old options deleted successfully for calculated question $question->name");
// notify("Old options deleted successfully for calculated question $question->id");
}
}
// Save units
$virtualqtype = $this->get_virtual_qtype();
$virtualqtype->save_numerical_units($question);
return true;
}
function create_runtime_question($question, $form) {
$question = parent::create_runtime_question($question, $form);
$question->options->answers = array();
foreach ($form->answers as $key => $answer) {
$a->answer = trim($form->answer[$key]);
$a->tolerance = $form->tolerance[$key];
$a->tolerancetype = $form->tolerancetype[$key];
$a->correctanswerlength = $form->correctanswerlength[$key];
$a->correctanswerformat = $form->correctanswerformat[$key];
$question->options->answers[] = clone($a);
}
return $question;
}
function validate_form($form) {
switch($form->wizardpage) {
case 'question':
$calculatedmessages = array();
if (empty($form->name)) {
$calculatedmessages[] = get_string('missingname', 'quiz');
}
if (empty($form->questiontext)) {
$calculatedmessages[] = get_string('missingquestiontext', 'quiz');
}
// Verify formulas
foreach ($form->answers as $key => $answer) {
if ('' === trim($answer)) {
$calculatedmessages[] =
get_string('missingformula', 'quiz');
}
if ($formulaerrors =
quiz_qtype_calculated_find_formula_errors($answer)) {
$calculatedmessages[] = $formulaerrors;
}
if (! isset($form->tolerance[$key])) {
$form->tolerance[$key] = 0.0;
}
if (! is_numeric($form->tolerance[$key])) {
$calculatedmessages[] =
get_string('tolerancemustbenumeric', 'quiz');
}
}
if (!empty($calculatedmessages)) {
$errorstring = "The following errors were found:<br />";
foreach ($calculatedmessages as $msg) {
$errorstring .= $msg . '<br />';
}
error($errorstring);
}
break;
default:
return parent::validate_form($form);
break;
}
return true;
}
function print_question_formulation_and_controls(&$question, &$state, $quiz,
$options) {
// Substitute variables in questiontext before giving the data to the
@@ -137,7 +207,6 @@ class quiz_calculated_qtype extends quiz_dataset_dependent_questiontype {
$answer = &$question->options->answers[$key]; // for PHP 4.x
$answer->answer = $this->substitute_variables($answer->answer,
$state->options->dataset);
// apply_unit
}
$question->questiontext = parent::substitute_variables(
$question->questiontext, $state->options->dataset);
@@ -154,7 +223,7 @@ class quiz_calculated_qtype extends quiz_dataset_dependent_questiontype {
}
return parent::grade_responses($question, $state, $quiz);
}
// ULPGC ecastro
function check_response(&$question, &$state) {
// Forward the checking to the virtual qtype
@@ -170,7 +239,7 @@ class quiz_calculated_qtype extends quiz_dataset_dependent_questiontype {
// ULPGC ecastro
function get_actual_response(&$question, &$state) {
// Substitute variables in questiontext before giving the data to the
// virtual type
// virtual type
$virtualqtype = $this->get_virtual_qtype();
$unit = $virtualqtype->get_default_numerical_unit($question);
foreach ($question->options->answers as $key => $answer) {
@@ -183,21 +252,17 @@ class quiz_calculated_qtype extends quiz_dataset_dependent_questiontype {
$question->questiontext, $state->options->dataset);
$responses = $virtualqtype->get_all_responses($question, $state);
$response = reset($responses->responses);
$correct = $response->answer.' : ';
$correct = $response->answer.' : ';
$responses = $virtualqtype->get_actual_response($question, $state);
foreach ($responses as $key=>$response){
$responses[$key] = $correct.$response;
}
return $responses;
return $responses;
}
function create_virtual_qtype() {
global $CFG;
require_once("$CFG->dirroot/mod/quiz/questiontypes/numerical/questiontype.php");
@@ -212,13 +277,14 @@ class quiz_calculated_qtype extends quiz_dataset_dependent_questiontype {
function custom_generator_tools($datasetdef) {
if (ereg('^(uniform|loguniform):([^:]*):([^:]*):([0-9]*)$',
$datasetdef->options, $regs)) {
$defid = "$datasetdef->type-$datasetdef->category-$datasetdef->name";
for ($i = 0 ; $i<10 ; ++$i) {
$lengthoptions[$i] = get_string(($regs[1] == 'uniform'
? 'decimals'
: 'significantfigures'), 'quiz', $i);
}
return '<input type="submit" onClick="'
. "document.addform.regenerateddefid.value='$datasetdef->id'; return true;"
. "document.addform.regenerateddefid.value='$defid'; return true;"
.'" value="'. get_string('generatevalue', 'quiz') . '"/><br/>'
. '<input type="text" size="3" name="calcmin[]" '
. " value=\"$regs[2]\"/> &amp; <input name=\"calcmax[]\" "
@@ -266,9 +332,9 @@ class quiz_calculated_qtype extends quiz_dataset_dependent_questiontype {
}
// Look for empty options, on which we set default values
foreach ($datasetdefs as $def) {
foreach ($datasetdefs as $defid => $def) {
if (empty($def->options)) {
$datasetdefs[$def->id]->options = 'uniform:1.0:10.0:1';
$datasetdefs[$defid]->options = 'uniform:1.0:10.0:1';
}
}
return $datasetdefs;
@@ -346,60 +412,60 @@ class quiz_calculated_qtype extends quiz_dataset_dependent_questiontype {
}
function comment_header($question) {
$this->get_question_options($question);
$answers = $question->options->answers;
//$this->get_question_options($question);
global $SESSION;
$strheader = '';
$delimiter = '';
if (empty($question->id)) {
$answers = $SESSION->datasetdependent->questionform->answers;
} else {
$answers = $question->options->answers;
}
foreach ($answers as $answer) {
$strheader .= $delimiter.$answer->answer;
if (is_string($answer)) {
$strheader .= $delimiter.$answer;
} else {
$strheader .= $delimiter.$answer->answer;
}
$delimiter = ',';
}
return $strheader;
}
function comment_on_datasetitems($question, $data, $number) {
/// Find a default unit:
if ($unit = get_record('quiz_numerical_units',
if (!empty($question->id) && $unit = get_record('quiz_numerical_units',
'question', $question->id, 'multiplier', 1.0)) {
$unit = $unit->unit;
} else {
$unit = '';
}
// Get answers
// the next line is hacked to get rid of the PHP notice until this gets fixed properly
$answers = (isset($question->options->answers)) ? $question->options->answers : null;
$answers = $question->options->answers;
$stranswers = get_string('answer', 'quiz');
$strmin = get_string('min', 'quiz');
$strmax = get_string('max', 'quiz');
$errors = '';
$delimiter = ': ';
$state = new stdClass;
$state->responses = array();
$state->options = new stdClass;
$virtualqtype = $this->get_virtual_qtype();
if ($answers) {
foreach ($answers as $answer) {
$calculated = quiz_qtype_calculated_calculate_answer(
$answer->answer, $data, $answer->tolerance,
$answer->tolerancetype, $answer->correctanswerlength,
$answer->correctanswerformat, $unit);
$state->responses[''] = $calculated->answer;
$virtualqtype->get_tolerance_interval($question, $state);
$calculated->min = $state->options->min;
$calculated->max = $state->options->max;
if ($calculated->min === '') {
// This should mean that something is wrong
$errors .= " -$calculated->answer";
$stranswers .= $delimiter;
} else {
$stranswers .= $delimiter.$calculated->answer;
}
$strmin .= $delimiter.$calculated->min;
$strmax .= $delimiter.$calculated->max;
$delimiter = ', ';
foreach ($answers as $answer) {
$calculated = quiz_qtype_calculated_calculate_answer(
$answer->answer, $data, $answer->tolerance,
$answer->tolerancetype, $answer->correctanswerlength,
$answer->correctanswerformat, $unit);
$calculated->tolerance = $answer->tolerance;
$calculated->tolerancetype = $answer->tolerancetype;
$calculated->correctanswerlength = $answer->correctanswerlength;
$calculated->correctanswerformat = $answer->correctanswerformat;
$virtualqtype->get_tolerance_interval($calculated);
if ($calculated->min === '') {
// This should mean that something is wrong
$errors .= " -$calculated->answer";
$stranswers .= $delimiter;
} else {
$stranswers .= $delimiter.$calculated->answer;
$strmin .= $delimiter.$calculated->min;
$strmax .= $delimiter.$calculated->max;
}
}
return "$stranswers<br/>$strmin<br/>$strmax<br/>$errors";
@@ -411,11 +477,11 @@ class quiz_calculated_qtype extends quiz_dataset_dependent_questiontype {
'3' => get_string('geometric', 'quiz'));
}
function dataset_options($question, $name, $renameabledatasets=false) {
function dataset_options($form, $name, $renameabledatasets=false) {
// Takes datasets from the parent implementation but
// filters options that are currently not accepted by calculated
// It also determines a default selection...
list($options, $selected) = parent::dataset_options($question, $name);
list($options, $selected) = parent::dataset_options($form, $name);
foreach ($options as $key => $whatever) {
if (!ereg('^'.LITERAL.'-', $key) && $key != '0') {
unset($options[$key]);
@@ -427,13 +493,13 @@ class quiz_calculated_qtype extends quiz_dataset_dependent_questiontype {
return array($options, $selected);
}
function construct_dataset_menus($question, $mandatorydatasets,
function construct_dataset_menus($form, $mandatorydatasets,
$optionaldatasets) {
$datasetmenus = array();
foreach ($mandatorydatasets as $datasetname) {
if (!isset($datasetmenus[$datasetname])) {
list($options, $selected) =
$this->dataset_options($question, $datasetname);
$this->dataset_options($form, $datasetname);
unset($options['0']); // Mandatory...
$datasetmenus[$datasetname] = choose_from_menu ($options,
'dataset[]', $selected, '', '', "0", true);
@@ -442,7 +508,7 @@ class quiz_calculated_qtype extends quiz_dataset_dependent_questiontype {
foreach ($optionaldatasets as $datasetname) {
if (!isset($datasetmenus[$datasetname])) {
list($options, $selected) =
$this->dataset_options($question, $datasetname);
$this->dataset_options($form, $datasetname);
$datasetmenus[$datasetname] = choose_from_menu ($options,
'dataset[]', $selected, '', '', "0", true);
}
@@ -451,35 +517,33 @@ class quiz_calculated_qtype extends quiz_dataset_dependent_questiontype {
}
function get_correct_responses(&$question, &$state) {
foreach ($question->options->answers as $key => $answer) {
if (((float) $answer->fraction) === 1.0) {
$virtualqtype = $this->get_virtual_qtype();
$unit = $virtualqtype->get_default_numerical_unit($question);
$virtualqtype = $this->get_virtual_qtype();
$unit = $virtualqtype->get_default_numerical_unit($question);
foreach ($question->options->answers as $answer) {
if (((int) $answer->fraction) === 1) {
$answernumerical = quiz_qtype_calculated_calculate_answer(
$answer->answer, $state->options->dataset,
$answer->tolerance, $answer->tolerancetype,
$answer->correctanswerlength,
$answer->correctanswerformat, $unit);
$answer->answer, $state->options->dataset, $answer->tolerance,
$answer->tolerancetype, $answer->correctanswerlength,
$answer->correctanswerformat, $unit->unit);
return array('' => $answernumerical->answer);
}
}
return null;
}
function substitute_variables($str, $dataset) {
$formula = parent::substitute_variables($str, $dataset);
if ($error = quiz_qtype_calculated_find_formula_errors($formula)) {
return $error;
}
/// Calculate the correct answer
eval('$str = '.$formula.';');
if (empty($formula)) {
$str = '';
} else {
eval('$str = '.$formula.';');
}
return $str;
}
}
//// END OF CLASS ////
@@ -674,4 +738,10 @@ function quiz_qtype_calculated_find_formula_errors($formula) {
}
}
function dump($obj) {
echo "<pre>\n";
var_dump($obj);
echo "</pre><br />\n";
}
?>
@@ -18,7 +18,7 @@ class quiz_dataset_dependent_questiontype extends quiz_default_questiontype {
return 'datasetdependent';
}
function save_question_options($question, $options) {
function save_question_options($question) {
// Default does nothing...
return true;
}
@@ -33,14 +33,13 @@ class quiz_dataset_dependent_questiontype extends quiz_default_questiontype {
WHERE b.question = $question->id
AND a.id = b.datasetdefinition")) {
error("Couldn't get the specified dataset for a calculated " .
"question! (question: {$question->id}, " .
"datasetnumber: {$datasetnumber})");
"question! (question: {$question->id}");
}
// Choose a random dataset
$state->options->datasetnumber = rand(1, $maxnumber);
$state->options->datasetitem = rand(1, $maxnumber);
$state->options->dataset =
$this->pick_question_dataset($question,$state->options->datasetnumber);
$this->pick_question_dataset($question,$state->options->datasetitem);
$state->responses = array('' => '');
return true;
}
@@ -51,22 +50,22 @@ class quiz_dataset_dependent_questiontype extends quiz_default_questiontype {
notify ("Wrongly formatted raw response answer " .
"{$state->responses['']}! Could not restore session for " .
" question #{$question->id}.");
$state->options->datasetnumber = 1;
$state->options->datasetitem = 1;
$state->options->dataset = array();
$state->responses = array('' => '');
return false;
}
// Restore the chosen dataset
$state->options->datasetnumber = $regs[1];
$state->options->datasetitem = $regs[1];
$state->options->dataset =
$this->pick_question_dataset($question,$state->options->datasetnumber);
$this->pick_question_dataset($question,$state->options->datasetitem);
$state->responses = array('' => $regs[2]);
return true;
}
function save_session_and_responses(&$question, &$state) {
$responses = 'dataset'.$state->options->datasetnumber.'-'.
$responses = 'dataset'.$state->options->datasetitem.'-'.
$state->responses[''];
// Set the legacy answer field
@@ -93,17 +92,6 @@ class quiz_dataset_dependent_questiontype extends quiz_default_questiontype {
$state, $quiz, $options);
}
function get_correct_responses(&$question, &$state) {
foreach ($question->options->answers as $answer) {
$answer->answer = $this->substitute_variables(
$answer->answer, $state->options->dataset);
if (((float) $answer->fraction) === 1.0) {
return array('' => $answer->answer);
}
}
return null;
}
function grade_responses(&$question, &$state, $quiz) {
// Forward the grading to the virtual qtype
foreach ($question->options->answers as $answer) {
@@ -113,7 +101,7 @@ class quiz_dataset_dependent_questiontype extends quiz_default_questiontype {
$virtualqtype = $this->get_virtual_qtype();
return $virtualqtype->grade_responses($question, $state, $quiz) ;
}
// ULPGC ecastro
function check_response(&$question, &$state) {
// Forward the checking to the virtual qtype
@@ -125,9 +113,6 @@ class quiz_dataset_dependent_questiontype extends quiz_default_questiontype {
return $virtualqtype->check_response($question, $state) ;
}
function substitute_variables($str, $dataset) {
foreach ($dataset as $name => $value) {
$str = str_replace('{'.$name.'}', $value, $str);
@@ -159,139 +144,311 @@ class quiz_dataset_dependent_questiontype extends quiz_default_questiontype {
return isset($form->backtoquiz);
}
function save_question($question, $form, $course, $subtypeoptions=false) {
// For dataset dependent questions a wizard is used for editing
// questions. Therefore calls from question.php are ignored.
// Instead questions are saved when this method is called by
// editquestion.php
// This gets called by editquestion.php after the standard question is saved
function print_next_wizard_page(&$question, &$form, $course) {
global $CFG, $USER, $SESSION;
if ($subtypeoptions) {
// Let's save the question
// We need to save the question first (in order to get the id)
// We then save the dataset definitions and finally we
// save the subtype options...
// Catch invalid navigation & reloads
if (empty($question->id) && empty($SESSION->datasetdependent)) {
redirect('edit.php', 'The page you are loading has expired.', 3);
}
// Save question
if (!empty($question->id)) { // Question already exists
$question->version++; // Update version number of question
if (!update_record("quiz_questions", $question)) {
error("Could not update question!");
}
} else { // Question is a new one
// Set the unique code (not to be changed)
$question->stamp = make_unique_id_code();
$question->version = 1;
if (!$question->id=insert_record("quiz_questions", $question)) {
error("Could not insert new question!");
}
}
// Save datasets
global $CFG;
$datasetdefinitions = get_records_sql( // Indexed by name...
"SELECT a.name, a.id, a.type, a.category
FROM {$CFG->prefix}quiz_dataset_definitions a,
{$CFG->prefix}quiz_question_datasets b
WHERE a.id = b.datasetdefinition
AND b.question = $question->id");
foreach ($form->dataset as $dataset) {
if (!$dataset) {
continue; // The no dataset case...
}
list($type, $category, $name) = explode('-', $dataset, 3);
if (isset($datasetdefinitions[$name])
and $datasetdefinitions[$name]->type == $type
and $datasetdefinitions[$name]->category == $category) {
// Keep this dataset as it already fulfills our dreams
// by preventing it from being deleted
unset($datasetdefinitions[$name]);
continue;
}
// We need to create a new datasetdefinition
unset ($datasetdef);
$datasetdef->type = $type;
$datasetdef->name = $name;
$datasetdef->category = $category;
if (!$datasetdef->id = insert_record(
'quiz_dataset_definitions', $datasetdef)) {
error("Unable to create dataset $name");
}
if ($category) {
// We need to look for already existing
// datasets in the category.
// By first creating the datasetdefinition above we
// can manage to automatically take care of
// some possible realtime concurrence
while ($olderdatasetdef = get_record_select(
'quiz_dataset_definitions',
" type = '$type' AND name = '$name'
AND category = '$category'
AND id < $datasetdef->id ")) {
// Use older dataset instead:
delete_records('quiz_dataset_definitions',
'id', $datasetdef->id);
$datasetdef = $olderdatasetdef;
}
}
// Create relation to this dataset:
unset($questiondataset);
$questiondataset->question = $question->id;
$questiondataset->datasetdefinition = $datasetdef->id;
if (!insert_record('quiz_question_datasets',
$questiondataset)) {
error("Unable to create relation to dataset $name");
}
}
// Remove local obsolete datasets as well as relations
// to datasets in other categories:
if (!empty($datasetdefinitions)) {
foreach ($datasetdefinitions as $def) {
delete_records('quiz_question_datasets',
'question', $question->id,
'datasetdefinition', $def->id);
if ($def->category == 0) { // Question local dataset
delete_records('quiz_dataset_definitions', 'id', $def->id);
delete_records('quiz_dataset_items',
'definition', $def->id);
}
}
}
// Save subtype options
$this->save_question_options($question, $subtypeoptions);
return $question;
} else if (empty($form->editdatasets)) {
// Parse for common question entries and
// continue with editquestion.php by returning the question
$question->name = $form->name;
$question->questiontext = $form->questiontext;
$question->questiontextformat = $form->questiontextformat;
if (empty($form->image)) {
$question->image = "";
} else {
$question->image = $form->image;
}
if (isset($form->defaultgrade)) {
$question->defaultgrade = $form->defaultgrade;
}
return $question;
} else {
return $question;
// See where we're coming from
switch($form->wizardpage) {
case 'question':
require("$CFG->dirroot/mod/quiz/questiontypes/datasetdependent/datasetdefinitions.php");
break;
case 'datasetdefinitions':
case 'datasetitems':
require("$CFG->dirroot/mod/quiz/questiontypes/datasetdependent/datasetitems.php");
break;
default:
error('Incorrect or no wizard page specified!');
break;
}
}
function save_question($question, &$form, $course) {
// For dataset dependent questions a wizard is used for editing
// questions. Therefore saving the question is delayed until
// we're through with the whole wizard.
global $SESSION;
// $this->validate_form($form);
// We need to return a valid question object (incl. id) if we're in the
// edit case. If it is a new question we don't necessarily need to
// return a valid question object
// See where we're coming from
switch($form->wizardpage) {
case 'question': // coming from the first page, creating the second
if (empty($form->id)) {
$SESSION->datasetdependent = new stdClass;
$SESSION->datasetdependent->questionform = $form;
} else {
$question = parent::save_question($question, $form, $course);
}
break;
case 'datasetdefinitions':
if (empty($form->id)) {
if (isset($SESSION->datasetdependent->questionform)) {
$SESSION->datasetdependent->definitionform = $form;
} else {
// Something went wrong, go back to the first page
redirect("question.php?category={$question->category}" .
"&qtype={$question->qtype}");
}
} else {
$this->save_dataset_definitions($form);
}
break;
case 'datasetitems':
if (empty($form->id) && isset($form->addbutton)) {
$question = parent::save_question($question,
$SESSION->datasetdependent->questionform, $course);
$SESSION->datasetdependent->definitionform->id = $form->id = $question->id;
$this->save_dataset_definitions($SESSION->datasetdependent->definitionform);
unset($SESSION->datasetdependent);
}
break;
default:
error('Incorrect or no wizard page specified!');
break;
}
return $question;
}
function get_dataset_definitions($form) {
$datasetdefs = array();
if (!empty($form->id)) {
global $CFG;
$sql = "SELECT i.*
FROM {$CFG->prefix}quiz_question_datasets d,
{$CFG->prefix}quiz_dataset_definitions i
WHERE d.question = '$form->id'
AND d.datasetdefinition = i.id
";
if ($records = get_records_sql($sql)) {
foreach ($records as $r) {
$datasetdefs["$r->type-$r->category-$r->name"] = $r;
}
}
}
$datasets = empty($form->dataset) ? $form->definition : $form->dataset;
foreach ($datasets as $dataset) {
if (!$dataset) {
continue; // The no dataset case...
}
if (!isset($datasetdefs[$dataset])) {
list($type, $category, $name) = explode('-', $dataset, 3);
$datasetdef = new stdClass;
$datasetdef->type = $type;
$datasetdef->name = $name;
$datasetdef->category = $category;
$datasetdef->itemcount = 0;
$datasetdef->options = '';
$datasetdefs[$dataset] = clone($datasetdef);
}
}
return $datasetdefs;
}
function save_dataset_definitions($form) {
// Save datasets
$datasetdefinitions = $this->get_dataset_definitions($form);
$tmpdatasets = array_flip($form->dataset);
$defids = array_keys($datasetdefinitions);
foreach ($defids as $defid) {
$datasetdef = &$datasetdefinitions[$defid];
if (isset($datasetdef->id)) {
// This dataset is not used any more, delete it
if (!isset($tmpdatasets[$defid])) {
delete_records('quiz_question_datasets',
'question', $form->id,
'datasetdefinition', $datasetdef->id);
if ($datasetdef->category == 0) { // Question local dataset
delete_records('quiz_dataset_definitions',
'id', $datasetdef->id);
delete_records('quiz_dataset_items',
'definition', $datasetdef->id);
}
}
// This has already been saved or just got deleted
unset($datasetdefinitions[$defid]);
continue;
}
if (!$datasetdef->id = insert_record(
'quiz_dataset_definitions', $datasetdef)) {
error("Unable to create dataset $defid");
}
if (0 != $datasetdef->category) {
// We need to look for already existing
// datasets in the category.
// By first creating the datasetdefinition above we
// can manage to automatically take care of
// some possible realtime concurrence
if ($olderdatasetdefs = get_records_select(
'quiz_dataset_definitions',
"type = '$datasetdef->type'
AND name = '$datasetdef->name'
AND category = '$datasetdef->category'
AND id < $datasetdef->id
ORDER BY id DESC")) {
while ($olderdatasetdef = array_shift($olderdatasetdefs)) {
delete_records('quiz_dataset_definitions',
'id', $datasetdef->id);
$datasetdef = $olderdatasetdef;
}
}
}
// Create relation to this dataset:
$questiondataset = new stdClass;
$questiondataset->question = $form->id;
$questiondataset->datasetdefinition = $datasetdef->id;
if (!insert_record('quiz_question_datasets',
$questiondataset)) {
error("Unable to create relation to dataset $name");
}
unset($datasetdefinitions[$defid]);
}
// Remove local obsolete datasets as well as relations
// to datasets in other categories:
if (!empty($datasetdefinitions)) {
foreach ($datasetdefinitions as $def) {
delete_records('quiz_question_datasets',
'question', $form->id,
'datasetdefinition', $def->id);
if ($def->category == 0) { // Question local dataset
delete_records('quiz_dataset_definitions', 'id', $def->id);
delete_records('quiz_dataset_items',
'definition', $def->id);
}
}
}
}
/*
function save_question($question, &$form, $course) {
// For dataset dependent questions a wizard is used for editing
// questions. Therefore saving the question is delayed until
// we're through with the whole wizard.
global $SESSION;
$this->validate_form($form);
// See where we're coming from
switch($form->wizardpage) {
case 'question':
unset($SESSION->datasetdependent); // delete any remaining data
// from previous wizards
if (empty($form->id)) {
$SESSION->datasetdependent->question = $form;
$question = $this->create_runtime_question($question, $form);
} else {
$question = parent::save_question($question, $form, $course);
}
break;
case 'datasetdefinitions':
$SESSION->datasetdependent->datasetdefinitions = $form;
if (empty($form->id)) {
$question = $this->create_runtime_question($question, $SESSION->datasetdependent->question);
} else {
$this->save_dataset_definitions($form);
$this->get_question_options($question);
//unset($SESSION->datasetdependent->datasetdefinitions);
}
//$this->get_question_options($question);
break;
case 'datasets':
if (!empty($form->addbutton) && isset($SESSION->datasetdependent->question)) {
echo "saving";
$question = parent::save_question($question, $SESSION->datasetdependent->question, $course);
$SESSION->datasetdependent->datasetdefinitions->id = $question->id;
$this->save_dataset_definitions($SESSION->datasetdependent->datasetdefinitions);
//$this->get_dataset_definitions($question);
unset($SESSION->datasetdependent);
}
dump($question);
if (empty($question->id)) {
$question = $this->create_runtime_question($question, $SESSION->datasetdependent->question);
} else {
$this->get_question_options($question);
}
break;
default:
error('Incorrect or no wizard page specified!');
break;
}
return $question;
}
*/
/*
function create_runtime_question($question, $form) {
$question->name = trim($form->name);
$question->questiontext = trim($form->questiontext);
$question->questiontextformat = $form->questiontextformat;
$question->parent = isset($form->parent)? $form->parent : 0;
$question->length = $this->actual_number_of_questions($question);
$question->penalty = isset($form->penalty) ? $form->penalty : 0;
if (empty($form->image)) {
$question->image = "";
} else {
$question->image = $form->image;
}
if (empty($question->name)) {
$question->name = strip_tags($question->questiontext);
if (empty($question->name)) {
$question->name = '-';
}
}
if ($question->penalty > 1 or $question->penalty < 0) {
$question->errors['penalty'] = get_string('invalidpenalty', 'quiz');
}
if (isset($form->defaultgrade)) {
$question->defaultgrade = $form->defaultgrade;
}
return $question;
}
*/
/*
function validate_form($form) {
switch($form->wizardpage) {
case 'question':
error('Validation for the wizardpage "question" needs to be ' .
'defined in the inheriting questiontype.');
case 'datasetdefinitions':
// Nothing to validate
$SESSION->datasetdependent->datasetdefinitions = $form;
break;
case 'datasets':
break;
default:
error('Incorrect or no wizard page specified!');
break;
}
return true;
}
*/
/// Dataset functionality
function pick_question_dataset($question, $datasetnumber) {
function pick_question_dataset($question, $datasetitem) {
// Select a dataset in the following format:
// An array indexed by the variable names (d.name) pointing to the value
// to be substituted
@@ -304,10 +461,10 @@ class quiz_dataset_dependent_questiontype extends quiz_default_questiontype {
WHERE q.question = $question->id
AND q.datasetdefinition = d.id
AND d.id = i.definition
AND i.number = $datasetnumber")) {
AND i.number = $datasetitem")) {
error("Couldn't get the specified dataset for a dataset dependent " .
"question! (question: {$question->id}, " .
"datasetnumber: {$datasetnumber})");
"datasetitem: {$datasetitem})");
}
array_walk($dataset, create_function('&$val', '$val = $val->value;'));
return $dataset;
@@ -362,7 +519,7 @@ class quiz_dataset_dependent_questiontype extends quiz_default_questiontype {
return $datasetdefs;
}
function dataset_options($question, $name) {
function dataset_options($form, $name) {
// First options - it is not a dataset...
$options['0'] = get_string('nodataset', 'quiz');
@@ -374,7 +531,7 @@ class quiz_dataset_dependent_questiontype extends quiz_default_questiontype {
FROM {$CFG->prefix}quiz_dataset_definitions a,
{$CFG->prefix}quiz_question_datasets b
WHERE a.id = b.datasetdefinition
AND b.question = '$question->id'
AND b.question = '$form->id'
AND a.name = '$name'")
or $currentdatasetdef->type = '0';
foreach (array( LITERAL, FILE, LINK) as $type) {
@@ -393,10 +550,10 @@ class quiz_dataset_dependent_questiontype extends quiz_default_questiontype {
FROM {$CFG->prefix}quiz_dataset_definitions a,
{$CFG->prefix}quiz_question_datasets b
WHERE a.id = b.datasetdefinition
AND a.category = '$question->category'
AND a.category = '$form->category'
AND a.name = '$name'");
foreach(array( LITERAL, FILE, LINK) as $type) {
$key = "$type-$question->category-$name";
$key = "$type-$form->category-$name";
if (isset($categorydatasetdefs[$type])
and $categorydef = $categorydatasetdefs[$type]) {
if ($currentdatasetdef->type == $type
@@ -0,0 +1,18 @@
<?php
// Now continue by preparing for the second page
// in the question wizard: "questiondatasets.html"
// Determine possible and mandatory datasets...
$possibledatasets = $this->find_dataset_names($form->questiontext);
$mandatorydatasets = array();
foreach ($form->answers as $answer) {
$mandatorydatasets += $this
->find_dataset_names($answer);
}
$datasets = $this->construct_dataset_menus(
$form, $mandatorydatasets, $possibledatasets);
// Print the page
print_heading_with_help(get_string("choosedatasetproperties", "quiz"), "questiondatasets", "quiz");
require("$CFG->dirroot/mod/quiz/questiontypes/datasetdependent/questiondatasets.html");
?>
@@ -1,9 +1,7 @@
<?php // $Id$
// Allows a teacher to create, edit and delete datasets
/// Print headings
// Set up strings
$strdatasetnumber = get_string("datasetnumber", "quiz");
$strnumberinfo = get_string("categoryinfo", "quiz");
$strquestions = get_string("questions", "quiz");
@@ -20,81 +18,78 @@
$strreuseifpossible = get_string('reuseifpossible', 'quiz');
$strforceregeneration = get_string('forceregeneration', 'quiz');
// Get datasetdefinitions:
$datasetdefs = get_records_sql(
"SELECT a.* FROM {$CFG->prefix}quiz_dataset_definitions a,
{$CFG->prefix}quiz_question_datasets b
WHERE a.id = b.datasetdefinition
AND b.question = $question->id");
if (empty($datasetdefs)) {
redirect('edit.php');
if (empty($question->id)) {
$datasetdefs = $this->get_dataset_definitions(
$SESSION->datasetdependent->definitionform);
} else {
if (empty($question->options)) {
$this->get_question_options($question);
}
$datasetdefs = $this->get_dataset_definitions($form);
}
foreach($datasetdefs as $datasetdef) {
if (!isset($maxnumber) || $datasetdef->itemcount < $maxnumber) {
// Handle generator options...
$olddatasetdefs = $datasetdefs;
$datasetdefs = $this->update_dataset_options($olddatasetdefs, $form);
$maxnumber = -1;
$datasets = empty($form->dataset) ? $form->definition : $form->dataset;
foreach ($datasetdefs as $defid => $datasetdef) {
if (isset($datasetdef->id)
&& $datasetdef->options != $olddatasetdefs[$defid]->options) {
// Save the new value for options
update_record('quiz_dataset_definitions', $datasetdef);
}
// Get maxnumber
if ($maxnumber == -1 || $datasetdef->itemcount < $maxnumber) {
$maxnumber = $datasetdef->itemcount;
}
}
}
/// Print heading
print_heading_with_help($streditdatasets, 'questiondatasets', "quiz");
/// If data submitted, then process and store.
if ($form = data_submitted()) {
if (isset($form->addbutton) && $form->addbutton &&
$maxnumber + 1 == $form->numbertoadd) { // This twisted condition should effectively stop resubmits caused by reloads
$addeditem->number = $form->numbertoadd;
foreach ($form->definition as $key => $itemdef) {
$addeditem->definition = $itemdef;
$addeditem->value = $form->value[$key];
if ($form->itemid[$key]) {
// Reuse an previously used record
$addeditem->id = $form->itemid[$key];
if (!update_record('quiz_dataset_items', $addeditem)) {
error("Error: Unable to update dataset item");
}
} else {
unset($addeditem->id);
if (!insert_record('quiz_dataset_items', $addeditem)) {
error("Error: Unable to insert dataset item");
}
// Handle adding and removing of dataset items
// This twisted condition should effectively stop resubmits caused by reloads
if (isset($form->addbutton) && $maxnumber + 1 == $form->numbertoadd) {
$addeditem->number = $form->numbertoadd;
foreach ($form->definition as $key => $defid) {
$addeditem->definition = $datasetdefs[$defid]->id;
$addeditem->value = $form->value[$key];
if ($form->itemid[$key]) {
// Reuse an previously used record
$addeditem->id = $form->itemid[$key];
if (!update_record('quiz_dataset_items', $addeditem)) {
error("Error: Unable to update dataset item");
}
if ($datasetdefs[$itemdef]->itemcount <= $maxnumber) {
$datasetdefs[$itemdef]->itemcount = $maxnumber+1;
if (!update_record('quiz_dataset_definitions',
$datasetdefs[$itemdef])) {
error("Error: Unable to update itemcount");
}
} else {
unset($addeditem->id);
if (!insert_record('quiz_dataset_items', $addeditem)) {
error("Error: Unable to insert dataset item");
}
}
// else Success:
$maxnumber = $addeditem->number;
} else if (isset($form->deletebutton) && $form->deletebutton
and $maxnumber == $form->numbertodelete)
{
// Simply decrease itemcount where == $maxnumber
foreach ($datasetdefs as $datasetdef) {
if ($datasetdef->itemcount == $maxnumber) {
$datasetdef->itemcount--;
if (!update_record('quiz_dataset_definitions',
$datasetdef)) {
error("Error: Unable to update itemcount");
}
foreach ($datasetdefs as $key => $newdef) {
if (isset($newdef->id) && $newdef->itemcount <= $maxnumber) {
$newdef->itemcount = $maxnumber+1;
// Save the new value for options
update_record('quiz_dataset_definitions', $newdef);
}
}
--$maxnumber;
}
// Handle generator options...
$olddatasetdefs = $datasetdefs;
$datasetdefs = $qtypeobj->update_dataset_options($olddatasetdefs, $form);
foreach ($datasetdefs as $key => $newdef) {
if ($newdef->options != $olddatasetdefs[$key]->options) {
// Save the new value for options
update_record('quiz_dataset_definitions', $newdef);
}
}
// else Success:
$maxnumber = $addeditem->number;
} else if (isset($form->deletebutton)
&& $maxnumber == $form->numbertodelete) {
// Simply decrease itemcount where == $maxnumber
foreach ($datasetdefs as $datasetdef) {
if ($datasetdef->itemcount == $maxnumber) {
$datasetdef->itemcount--;
if (!update_record('quiz_dataset_definitions',
$datasetdef)) {
error("Error: Unable to update itemcount");
}
}
}
--$maxnumber;
}
make_upload_directory("$course->id"); // Just in case
@@ -107,19 +102,121 @@
$coursefiles[$coursefile] = $coursefile;
}
// Get question header if any
$strquestionheader = $qtypeobj->comment_header($question);
// Get the data set definition and items:
foreach ($datasetdefs as $key => $datasetdef) {
$datasetdefs[$key]->items = get_records_sql( // Use number as key!!
// Generate a new dataset item (or reuse an old one)
foreach ($datasetdefs as $defid => $datasetdef) {
if (isset($datasetdef->id)) {
$datasetdefs[$defid]->items = get_records_sql( // Use number as key!!
" SELECT number, definition, id, value
FROM {$CFG->prefix}quiz_dataset_items
WHERE definition = $datasetdef->id ");
}
if (isset($datasetdefs[$defid]->items[$maxnumber + 1]) && (
empty($form->forceregeneration) &&
isset($form->regenerateddefid) &&
$form->regenerateddefid != $defid ||
isset($form->deletebutton))) {
// Reuse existing datasets
} else {
$datasetitem = new stdClass;
$datasetitem->id =
isset($datasetdefs[$defid]->items[$maxnumber + 1]->id)
? $datasetdefs[$defid]->items[$maxnumber + 1]->id : '';
$datasetitem->number = $maxnumber + 1;
if ($this->supports_dataset_item_generation()) {
if (!empty($form->addbutton) || // If we added an item
!isset($form->value) || // If we don't have a value
$form->regenerateddefid == $defid) { // If we explicitly ask for regeneration
$datasetitem->value =
$this->generate_dataset_item($datasetdef->options);
} else {
$definition = array_flip($form->definition);
$datasetitem->value = $form->value[$definition[$defid]];
}
} else {
$datasetitem->value = '';
}
$datasetdefs[$defid]->items[$maxnumber + 1] = clone($datasetitem);
}
}
// Get question header if any
$strquestionheader = $this->comment_header($question);
// Set up the table with the controls for creating new dataset items
$table->head = $addtable->head = array($straction, $strdatasetnumber);
$addtable->align = array("center", "center");
$forceregeneration = '';
if ($this->supports_dataset_item_generation()) {
$force = $reuse = '';
empty($form->forceregeneration)
? $reuse = ' checked="checked" '
: $force = ' checked="checked" ';
$forceregeneration = '<br/><input type="radio" name="forceregeneration" '
. $reuse . ' value="0"/>' . $strreuseifpossible
. '<br/><input type="radio" name="forceregeneration" value="1" '
. $force . ' />' . $strforceregeneration;
}
$addline[] = '<input type="hidden" name="numbertoadd" value="'.
($maxnumber+1).'"/><input type="submit" name="addbutton" '.
'value="'.$stradd.'"/>'.$forceregeneration;
$addline[] = $maxnumber+1;
foreach ($datasetdefs as $defid => $datasetdef) {
if ($datasetdef->name) {
$table->head[] = $datasetdef->name;
$addtable->head[] = $datasetdef->name
. ($this->supports_dataset_item_generation()
? '<br/>' . $this->custom_generator_tools($datasetdef)
: '');
//$table->align[] = "center";
if (isset($datasetdef->items[$maxnumber + 1]->id)) {
$itemid = $datasetdef->items[$maxnumber + 1]->id;
} else {
$itemid = '';
}
$addline[] =
'<input type="hidden" name="itemid[]" value="'.$itemid.'"/>'
. "<input type=\"hidden\" name=\"definition[]\" value=\"$defid\"/>"
. ( 2 != $datasetdef->type
? '<input type="text" size="20" name="value[]" value="'
. $datasetdef->items[$maxnumber + 1]->value
. '"/>'
: choose_from_menu($coursefiles, 'value[]',
$datasetdef->items[$maxnumber + 1]->value,
'', '', '', true));
$data[$datasetdef->name] = $datasetdef->items[$maxnumber + 1]->value;
}
}
if ($strquestionheader) {
$table->head[] = $strquestionheader;
$addtable->head[] = $strquestionheader;
if (empty($question->id) &&
isset($SESSION->datasetdependent->questionform)) {
$tmp = &$SESSION->datasetdependent->questionform;
$answers = array();
foreach ($tmp->answers as $key => $val) {
$answers[$key]->answer = $tmp->answers[$key];
$answers[$key]->tolerance = $tmp->tolerance[$key];
$answers[$key]->tolerancetype = $tmp->tolerancetype[$key];
$answers[$key]->correctanswerlength = $tmp->correctanswerlength[$key];
$answers[$key]->correctanswerformat = $tmp->correctanswerformat[$key];
}
$question->options->answers = $answers;
}
$addline[] = $this->comment_on_datasetitems($question, $data, $maxnumber + 1);
}
// Set up the table showing existing datasets
$table->data = array();
$table->align = $addtable->align;
for ($number = $maxnumber ; $number > 0 ; --$number) {
$columns = array();
if ($maxnumber == $number) {
@@ -130,147 +227,57 @@
$columns[] = '';
}
$columns[] = $number;
foreach ($datasetdefs as $datasetdef) {
foreach ($datasetdefs as $defid => $datasetdef) {
$columns[] =
'<input type="hidden" name="itemid[]" value="'. $datasetdef->items[$number]->id .'"/>'
. "<input type=\"hidden\" name=\"number[]\" value=\"$number\"/>
<input type=\"hidden\" name=\"definition[]\" value=\"$datasetdef->id\"/>"
. // Set $data:
($data[$datasetdef->name] = $datasetdef->items[$number]->value) ;
'<input type="hidden" name="itemid[]" value="'. $datasetdef->items[$number]->id .'"/>'
. "<input type=\"hidden\" name=\"number[]\" value=\"$number\"/>
<input type=\"hidden\" name=\"definition[]\" value=\"$defid\"/>"
. // Set $data:
($data[$datasetdef->name] = $datasetdef->items[$number]->value) ;
}
if ($strquestionheader) {
$columns[] = $qtypeobj->comment_on_datasetitems($question, $data, $number);
$columns[] = $this->comment_on_datasetitems($question, $data, $number);
}
$table->data[] = $columns;
}
$table->head = array($straction, $strdatasetnumber);
$table->align = array("center", "center");
$addtable->head = $table->head;
if ($qtypeobj->supports_dataset_item_generation()) {
if (isset($form->forceregeneration) && $form->forceregeneration) {
$force = ' checked="checked" ';
$reuse = '';
} else {
$force = '';
$reuse = ' checked="checked" ';
}
$forceregeneration = '<br/><input type="radio" name="forceregeneration" '
. $reuse . ' value="0"/>' . $strreuseifpossible
. '<br/><input type="radio" name="forceregeneration" value="1" '
. $force . ' />' . $strforceregeneration;
} else {
$forceregeneration = '';
}
$addline = array('<input type="hidden" name="numbertoadd" value="'
. ($maxnumber+1)
. "\"/><input type=\"submit\" name=\"addbutton\" value=\"$stradd\"/>"
. $forceregeneration
, $maxnumber+1);
foreach ($datasetdefs as $datasetdef) {
if ($datasetdef->name) {
$table->head[] = $datasetdef->name;
$addtable->head[] = $datasetdef->name
. ($qtypeobj->supports_dataset_item_generation()
? '<br/>' . $qtypeobj->custom_generator_tools($datasetdef)
: '');
$table->align[] = "center";
// THE if-statement IS FOR BUT ONE THING
// - to determine an item value for the input field
// - this is tried in a number of different way...
if (isset($form->regenerateddefid) && $form->regenerateddefid) {
// Regeneration clicked...
if ($form->regenerateddefid == $datasetdef->id) {
//...for this item...
$itemvalue = $qtypeobj
->generate_dataset_item($datasetdef->options);
} else {
// ...but not for this, keep unchanged!
foreach ($form->definition as $key => $itemdef) {
if ($datasetdef->id == $itemdef) {
$itemvalue = $form->value[$key];
break;
}
}
}
} else if (isset($form->forceregeneration)
&& $form->forceregeneration) {
// Can only mean a an "Add operation with forced regeneration:
$itemvalue = $qtypeobj->generate_dataset_item($datasetdef->options);
/// Print heading
} else if (isset($datasetdef->items[$maxnumber + 1])) {
// Looks like we do have an old value to use here:
$itemvalue = $datasetdef->items[$maxnumber + 1]->value;
print_heading_with_help($streditdatasets, 'questiondatasets', "quiz");
} else {
// We're getting getting desperate -
// is there any chance to determine a value somehow
// Let's just try anything now...
$qtypeobj->supports_dataset_item_generation() and '' !== (
// Generation could work if the options are alright:
$itemvalue = $qtypeobj->generate_dataset_item($datasetdef->options))
or ereg('(.*)'.($maxnumber).'(.*)',
$datasetdef->items[$maxnumber]->value, $valueregs)
// Looks like this trivial generator does it:
and $itemvalue = $valueregs[1].($maxnumber+1).$valueregs[2]
or // Let's just pick the dataset number, better than nothing:
$itemvalue = $maxnumber + 1;
}
if (isset($datasetdef->items[$maxnumber + 1]->id)) {
$itemid = $datasetdef->items[$maxnumber + 1]->id;
} else {
$itemid = '';
}
$addline[] =
'<input type="hidden" name="itemid[]" value="'.$itemid.'"/>'
. "<input type=\"hidden\" name=\"definition[]\" value=\"$datasetdef->id\"/>"
. ( 2 != $datasetdef->type
? '<input type="text" size="20" name="value[]" value="'
. $itemvalue
. '"/>'
: choose_from_menu($coursefiles, 'value[]',
$itemvalue,
'', '', '', true));
$data[$datasetdef->name] = $itemvalue;
}
}
if ($strquestionheader) {
$table->head[] = $strquestionheader;
$addtable->head[] = $strquestionheader;
$table->align[] = "center";
$addline[] = $qtypeobj->comment_on_datasetitems($question, $data, $maxnumber + 1);
}
// Print form for adding one more dataset
$addtable->align = $table->align;
// Print the new-dataset table
$addtable->data = array($addline);
echo "<form name=\"addform\" method=\"post\" action=\"question.php\">
<input type=\"hidden\" name=\"sesskey\" value=\"".sesskey()."\"/>
<input type=\"hidden\" name=\"regenerateddefid\" value=\"0\"/>
<input type=\"hidden\" name=\"id\" value=\"$question->id\"/>
<input type=\"hidden\" name=\"editdatasets\" value=\"1\"/>";
<input type=\"hidden\" name=\"category\" value=\"$question->category\"/>
<input type=\"hidden\" name=\"qtype\" value=\"$question->qtype\"/>
<input type=\"hidden\" name=\"sesskey\" value=\"".sesskey()."\"/>
<input type=\"hidden\" name=\"wizardpage\" value=\"datasetitems\"/>";
print_table($addtable);
echo '</form>';
// Print form with current datasets
if ($table->data) {
// <input type=\"hidden\" name=\"editdatasets\" value=\"1\"/>
// Print the existing-datasets table
if (!empty($table->data)) {
echo "<form method=\"post\" action=\"question.php\">
<input type=\"hidden\" name=\"sesskey\" value=\"".sesskey()."\"/>
<input type=\"hidden\" name=\"id\" value=\"$question->id\"/>
<input type=\"hidden\" name=\"editdatasets\" value=\"1\"/>";
<input type=\"hidden\" name=\"category\" value=\"$question->category\"/>
<input type=\"hidden\" name=\"qtype\" value=\"$question->qtype\"/>
<input type=\"hidden\" name=\"sesskey\" value=\"".sesskey()."\"/>
<input type=\"hidden\" name=\"wizardpage\" value=\"datasetitems\"/>";
print_table($table);
echo '</form>';
}
echo "<center><br /><br /><form method=\"get\" action=\"edit.php\"><input type=\"hidden\" name=\"question\" value=\"$question->id\"/><input type=\"submit\" name=\"backtoquiz\" value=\"$strbacktoquiz\"></form></center>\n";
print_footer();
echo "<center><br /><br /><form method=\"post\" action=\"question.php\">
<input type=\"hidden\" name=\"id\" value=\"$question->id\"/>
<input type=\"hidden\" name=\"category\" value=\"$question->category\"/>
<input type=\"hidden\" name=\"qtype\" value=\"$question->qtype\"/>
<input type=\"hidden\" name=\"sesskey\" value=\"".sesskey()."\"/>
<input type=\"hidden\" name=\"wizardpage\" value=\"datasetitems\"/>
<input type=\"submit\" name=\"backtoquiz\" value=\"$strbacktoquiz\">
</form></center>\n";
?>
@@ -1,6 +1,5 @@
<form name="theform" method="post" action="question.php">
<center>
<input type="hidden" name="nextwizardpage" value="calculated.html"/>
<table cellpadding="5">
<?php if (!empty($datasets)) { ?>
<tr valign="top">
@@ -9,7 +8,7 @@
<td><b> - <?php print_string("substitutedby", "quiz") ?></b></td>
<td align="left"><?php echo array_shift($datasets) ?></td>
</tr>
<?php foreach ($datasets as $name => $menu) { ?>
<?php foreach ($datasets as $name => $menu) { ?>
<tr valign="top">
<td/>
<td align="right">{<?php p($name) ?>}</td>
@@ -24,27 +23,12 @@
</td>
</tr>
<?php } ?>
<tr valign="top">
<td/>
<td/>
<td/>
<td>
<input type="hidden" name="sesskey" value="<?php echo $USER->sesskey; ?>" />
<input type="hidden" name="name" value="<?php p($question->name) ?>" />
<input type="hidden" name="questiontext" value="<?php p($question->questiontext) ?>" />
<input type="hidden" name="questiontextformat" value="<?php p($question->questiontextformat) ?>" />
<input type="hidden" name="image" value="<?php p($question->image) ?>" />
<?php if (!empty($hiddeninputnames)) foreach ($hiddeninputnames as $key => $hiddeninputname) { ?>
<input type="hidden" name="<?php p($hiddeninputname) ?>"
value="<?php p($hiddeninputvalues[$key]) ?>" />
<?php } ?>
</td>
</tr>
</table>
<input type="hidden" name="id" value="<?php p($question->id) ?>" />
<input type="hidden" name="qtype" value="<?php p($question->qtype) ?>" />
<input type="hidden" name="category" value="<?php p($question->category) ?>" />
<input type="hidden" name="sesskey" value="<?php p(sesskey()) ?>" />
<input type="hidden" name="wizardpage" value="datasetdefinitions" />
<input type="submit" value="<?php print_string("savechanges") ?>" />
</center>
</form>
@@ -70,17 +70,22 @@ class quiz_numerical_qtype extends quiz_shortanswer_qtype {
}
function get_default_numerical_unit(&$question) {
$unit = new stdClass;
$unit->unit = '';
$unit->multiplier = 1.0;
if (!isset($question->options->units[0])) {
return '';
// do nothing
} else if (1.0 === (float)$question->options->units[0]->multiplier) {
return $question->options->units[0];
$unit->unit = $question->options->units[0]->unit;
} else {
foreach ($question->options->units as $unit) {
foreach ($question->options->units as $u) {
if (1.0 === (float)$unit->multiplier) {
return $unit;
$unit->unit = $u->unit;
break;
}
}
}
return $unit;
}
function save_question_options($question) {
@@ -229,7 +234,7 @@ class quiz_numerical_qtype extends quiz_shortanswer_qtype {
return ($response == $answer->answer);
}
}
// ULPGC ecastro
function check_response(&$question, &$state){
$answers = &$question->options->answers;
@@ -325,7 +330,7 @@ class quiz_numerical_qtype extends quiz_shortanswer_qtype {
$unit = $this->get_default_numerical_unit($question);
if (is_array($question->options->answers)) {
foreach ($question->options->answers as $aid=>$answer) {
unset ($r);
unset ($r);
$r->answer = $answer->answer;
$r->credit = $answer->fraction;
$this->get_tolerance_interval($answer);
@@ -344,7 +349,7 @@ class quiz_numerical_qtype extends quiz_shortanswer_qtype {
}
$result->id = $question->id;
$result->responses = $answers;
return $result;
return $result;
}
function get_tolerance_interval(&$answer) {