Adding various improvements (MDL-8565, MDL-9496, MDL10070 etc.)to calculated question

This commit is contained in:
pichetp
2007-06-11 07:44:29 +00:00
parent 719ef5f313
commit 009d136f72
5 changed files with 690 additions and 193 deletions
+57 -40
View File
@@ -80,24 +80,31 @@
<?php if (isset($err["penalty"])) formerr($err["penalty"]); ?>
</td>
</tr>
<?php
// Answers.
for ($i=1; $i<=count($answers); $i++) {
$answer = $answers[$i-1];
?>
<tr valign="top">
<td align="right"><b><?php print_string("correctanswerformula", "quiz") ?>:</b></td>
<td align="left">
<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" />
<input align="left" type="text" id="formula0" name="answers[]" size="20" value="<?php p($answer->answer) ?>" alt="<?php print_string("correctanswerformula", "quiz") ?>" />&nbsp;&nbsp;
<?php print_string("grade");
echo ":&nbsp;";
choose_from_menu($gradeoptions, "fraction[]", $answer->fraction,""); ?>
</td>
</tr>
<tr valign="top">
<td align="right"><b><?php print_string("tolerance", "quiz"); ?>:</b></td>
<td align="left">
<input align="left" type="text" id="tolerance0" name="tolerance[]" size="15" value="<?php p($answers[0]->tolerance) ?>" alt="<?php print_string("tolerance", "quiz"); ?>" />&plusmn;
<input align="left" type="text" id="tolerance0" name="tolerance[]" size="15" value="<?php p($answer->tolerance) ?>" alt="<?php print_string("tolerance", "quiz"); ?>" />&plusmn;
</td>
</tr>
<tr valign="top">
<td align="right"><b><?php print_string("tolerancetype", "quiz"); ?>:</b></td>
<td align="left">
<?php choose_from_menu($qtypeobj->tolerance_types(),
'tolerancetype[]', $answers[0]->tolerancetype, false); ?>
'tolerancetype[]', $answer->tolerancetype, false); ?>
</td>
</tr>
<tr valign="top">
@@ -108,17 +115,27 @@
'4' => ' 4 ', '5' => ' 5 ', '6' => ' 6 ',
'7' => ' 7 ', '8' => ' 8 ', '9' => ' 9 '),
'correctanswerlength[]',
$answers[0]->correctanswerlength, false); ?>
$answer->correctanswerlength, false); ?>
<?php choose_from_menu(array('1' => get_string('decimalformat', 'quiz'),
'2' => get_string('significantfiguresformat', 'quiz')),
'correctanswerformat[]',
$answers[0]->correctanswerformat, false); ?>
$answer->correctanswerformat, false); ?>
</td>
</tr>
<tr valign="top">
<td align="right"><b><?php print_string("feedback", "quiz") ?>:</b></td>
<td align="left">
<textarea name="feedback[]" rows="2" cols="50"><?php p($answers[0]->feedback) ?></textarea>
<textarea name="feedback[]" rows="2" cols="50"><?php p($answer->feedback) ?></textarea>
</td>
</tr>
<?php
}
// Units.
$addmoreanswerblanks=get_string('addmoreanswerblanks', 'qtype_calculated');
?>
<tr valign="top">
<td align="center" colspan="2">
<input name ='addanswers' type="submit" value="<?php p($addmoreanswerblanks) ?>" />
</td>
</tr>
<tr valign="top">
@@ -157,40 +174,40 @@ for ($i=1; $i<count($units); $i++) {
</tr>
<?php
} /// END for
$addmoreanswerblanks=get_string('addmoreunitblanks', 'qtype_calculated');
?>
<tr valign="top">
<td align="center" colspan="2">
<input name ='addunits' type="submit" value="<?php p($addmoreanswerblanks) ?>" />
</td>
</tr>
<tr valign="top">
<td colspan="2" align="center">
<input type="submit" value="<?php print_string('nextpage', 'qtype_calculated') ?>" />
<?php
if ($question->id) {
?>
<input type="submit" name="makecopy" value="<?php print_string('makecopynextpage', 'qtype_calculated')?>" />
<?php
}
?>
<input type="submit" name="cancel" value="<?php print_string("cancel") ?>" />
<input type="hidden" name="sesskey" value="<?php p($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="hidden" name="inpopup" value="<?php echo optional_param('inpopup', 0, PARAM_INT) ?>" />
<?php
// The following hidden field indicates that the versioning code should be turned on, i.e.,
// that old versions should be kept if necessary
?>
<input type="hidden" name="versioning" value="on" />
</td>
</tr>
</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></table>
</center>
</form>
<script language="javascript" type="text/javascript">
function determineMinAndMax() {
// This client-side script will determine the values for min and max
// based on the input for answer and acceptederror.
with(document.theform) {
if (formula0.value=='') {
alert('<?php print_string("missingformula","quiz") ?>');
return false;
/* It could perhaps be an idea to parse the formula here
* but as it is necessary at the server anyway, we can
* it leave like this for the moment. */
} else if (''!=defaultunit.value && !isNaN(defaultunit.value)) {
alert('<?php print_string("unitmustnotbenumeric","quiz") ?>');
return false;
} else if (isNaN(tolerance0.value)) {
alert('<?php print_string("tolerancemustbenumeric","quiz") ?>');
return false;
} else if ('2' == document.theform['correctanswerformat[]'].value
&& '0' == document.theform['correctanswerlength[]'].value) {
alert('<?php print_string("zerosignificantfiguresnotallowed","quiz") ?>');
return false;
} else {
return true;
}
}
}
</script>
+53 -8
View File
@@ -3,7 +3,7 @@
// Get a handle to the question type we are dealing with here
$qtypeobj = $QTYPES['calculated'];
if (empty($form)) {
if (empty($form)|| isset($form->addanswers) || isset($form->addunits)) {
/****************************************************************/
/***** First page in question wizard - editquestion.html.html! **/
/****************************************************************/
@@ -14,7 +14,7 @@ if (empty($form)) {
// However, the code behind supports up to six formulas
// and the database store and attempt/review framework
// does not have any limit.
$answers= array();
/* $answers= array();
for ($i=0; $i<6; $i++) {
// Make answer slots with default values
$answers[$i]->answer = "";
@@ -38,15 +38,52 @@ if (empty($form)) {
}
}
}
*/
$emptyanswer = new stdClass;
$emptyanswer->answer = '';
$emptyanswer->feedback = "";
$emptyanswer->fraction = "1.0";
$emptyanswer->tolerance = "0.01";
$emptyanswer->tolerancetype = "1";
$emptyanswer->correctanswerlength = "2"; // Defaults to two ...
$emptyanswer->correctanswerformat = "1"; // ... decimals
if (!empty($question->id) && isset($question->qtype) &&
$QTYPES[$question->qtype]->get_question_options($question)) {
$answers = array_values($question->options->answers);
/* $units = array_values($question->options->units);
usort($units, create_function('$a, $b', // make sure the default unit is at index 0
'if (1.0 === (float)$a->multiplier) { return -1; } else '.
'if (1.0 === (float)$b->multiplier) { return 1; } else { return 0; }'));
*/
} else {
$answers = array();
$answers[] = $emptyanswer;
// $units = array();
}
// Add blank answers to make the number up to QUESTION_NUMANS
// or one more than current, if there are already lots.
$emptyanswer = new stdClass;
$emptyanswer->answer = '';
$emptyanswer->feedback = "";
$emptyanswer->fraction = "1.0";
$emptyanswer->tolerance = "0.01";
$emptyanswer->tolerancetype = "1";
$emptyanswer->correctanswerlength = "2"; // Defaults to two ...
$emptyanswer->correctanswerformat = "1"; // ... decimals
// $i = count($answers);
// $limit = QUESTION_NUMANS;
// $limit = $limit <= $i ? $i+1 : $limit;
// for (; $i < $limit; $i++) {
$answers[] = $emptyanswer;
// }
// Units are handled the same way
// as for numerical questions
$units = array();
for ($i=0 ; $i<6 ; $i++) {
// Make unit slots, default as blank...
$units[$i]->multiplier = '';
$units[$i]->unit = '';
}
if (!empty($question->id) and $unitsraw = get_records(
'question_numerical_units', 'question', $question->id)) {
/// Find default unit and have it put in the zero slot
@@ -70,8 +107,9 @@ if (empty($form)) {
}
} else {
$units[0]->multiplier = 1.0;
$units[0]->unit= '';
}
// Strip trailing zeros from multipliers
foreach ($units as $i => $unit) {
if (ereg('^(.*\\..(.*[^0])?)0+$', $unit->multiplier, $regs1)) {
@@ -82,6 +120,13 @@ if (empty($form)) {
}
}
}
if (isset($form->addunits)){
$emptyunit = new stdClass;
$emptyunit->multiplier = '';
$emptyunit->unit = '';
$units[]=$emptyunit;
$units[]=$emptyunit;
}
print_heading_with_help(get_string("editingcalculated", "quiz"), "calculated", "quiz");
require("$CFG->dirroot/question/type/calculated/editquestion.html");
} else { // $form is not empty
+258 -10
View File
@@ -195,11 +195,136 @@ class question_calculated_qtype extends question_dataset_dependent_questiontype
delete_records('question_calculated', 'id', $oo->id);
}
}
// validating data but not on import
if( !isset($question->import_process)){
$mandatorydatasets = array();
foreach ($question->answers as $answer) {
$mandatorydatasets += $this->find_dataset_names($answer);
}
// do not proceed further if no mandatorydataset
if (count($mandatorydatasets) < 1) { // check there are at lest 2 answers for multiple choice
$result->notice = get_string('atleastonewildcard', 'qtype_datasetdependent');//get_string("notenoughanswers", "qtype_multichoice", "2");
return $result;
}
$calculatedmessages = array();
//evaluate the equations i.e {=5+4) eval(
$qtext = "";
$qtextremaining = $question->questiontext ;
$possibledatasets = $this->find_dataset_names($question->questiontext);
foreach ($possibledatasets as $name => $value) {
$qtextremaining = str_replace('{'.$name.'}', '1', $qtextremaining);
}
// echo "numericalquestion qtextremaining <pre>";print_r($possibledatasets);
// echo "test<pre>";print_r($numericalquestion);
while (ereg('\{=([^[:space:]}]*)}', $qtextremaining, $regs1)) {
$qtextsplits = explode($regs1[0], $qtextremaining, 2);
$qtext =$qtext.$qtextsplits[0];
$qtextremaining = $qtextsplits[1];
// echo "testsplit0".$qtextsplits[0]."<br/>";
// echo "test".$regs1[0]."<br/>";
// echo "qtext".$qtext."<br/>";
// echo "test<pre>";print_r($regs1);
if (empty($regs1[1])) {
$str = '';
} else {
// could put here an equation verification
if ($formulaerrors =
qtype_calculated_find_formula_errors($regs1[1])) {
$calculatedmessages[] = $formulaerrors;
$str = '';
}
}
}
// import from 1.8 to be tested
$answercount=0 ;
$maxgrade = false;
$mandatorydatasets = array();
foreach ($question->answers as $key => $answer){
$mandatorydatasets += $this->find_dataset_names($answer);
}
if ( count($mandatorydatasets )==0){
// $errors['questiontext']=get_string('atleastonewildcard', 'qtype_datasetdependent');
// foreach ($answers as $key => $answer){
$calculatedmessages[] = get_string('atleastonewildcard', 'qtype_datasetdependent');
// }
}
foreach ($question->answers as $key => $answer){
//check no of choices
// the * for everykind of answer not actually implemented
$trimmedanswer = trim($answer);
if (($trimmedanswer!='')||$answercount==0){
$eqerror = qtype_calculated_find_formula_errors($trimmedanswer);
if (FALSE !== $eqerror){
$calculatedmessages[] = $eqerror.":".$answer;
}
}
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++;
}
}
if ($answercount == 0) {
$calculatedmessages[]=get_string('atleastoneanswer', 'qtype_calculated');
}
/* $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 =
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);
}
*/
}
if( isset($question->import_process)&&$question->import_process){
$this->import_datasets($question);
}
if (!empty($calculatedmessages)) {
$errorstring = "The following errors were found:<br />";
foreach ($calculatedmessages as $msg) {
$errorstring .= $msg . '<br />';
}
$result->notice=$errorstring;
}
// Report any problems.
if (!empty($result->notice)) {
return $result;
@@ -261,7 +386,7 @@ class question_calculated_qtype extends question_dataset_dependent_questiontype
$datasetitem->number = $dataitem->itemnumber ;
$datasetitem->value = $dataitem->value ;
if (!insert_record('question_dataset_items', $datasetitem)) {
error("Unable to insert dataset item $item->itemnumber with $item->value for $datasetdef->name");
error("Unable to insert dataset item $datasetitem->number with $item->value for $datasetdef->name");
}
}
}
@@ -273,6 +398,7 @@ class question_calculated_qtype extends question_dataset_dependent_questiontype
$question->options->answers = array();
foreach ($form->answers as $key => $answer) {
$a->answer = trim($form->answer[$key]);
$a->fraction = $form->fraction[$key];//new
$a->tolerance = $form->tolerance[$key];
$a->tolerancetype = $form->tolerancetype[$key];
$a->correctanswerlength = $form->correctanswerlength[$key];
@@ -365,6 +491,25 @@ class question_calculated_qtype extends question_dataset_dependent_questiontype
}
$numericalquestion->questiontext = parent::substitute_variables(
$numericalquestion->questiontext, $state->options->dataset);
//evaluate the equations i.e {=5+4)
$qtext = "";
$qtextremaining = $numericalquestion->questiontext ;
while (ereg('\{=([^[: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 ;
}
$numericalquestion->questiontext = $qtext.$qtextremaining ; // end replace equations
$virtualqtype->print_question_formulation_and_controls($numericalquestion, $state, $cmoptions, $options);
}
@@ -590,7 +735,7 @@ class question_calculated_qtype extends question_dataset_dependent_questiontype
function comment_header($question) {
//$this->get_question_options($question);
global $SESSION;
$strheader = '';
$strheader = Array();
$delimiter = '';
if (empty($question->id)) {
$answers = $SESSION->datasetdependent->questionform->answers;
@@ -599,9 +744,9 @@ class question_calculated_qtype extends question_dataset_dependent_questiontype
}
foreach ($answers as $answer) {
if (is_string($answer)) {
$strheader .= $delimiter.$answer;
$strheader[] = $answer;
} else {
$strheader .= $delimiter.$answer->answer;
$strheader[]= $answer->answer;
}
$delimiter = ',';
}
@@ -624,6 +769,7 @@ class question_calculated_qtype extends question_dataset_dependent_questiontype
$errors = '';
$delimiter = ': ';
$virtualqtype = $this->get_virtual_qtype();
$column =Array();
foreach ($answers as $answer) {
$calculated = qtype_calculated_calculate_answer(
$answer->answer, $data, $answer->tolerance,
@@ -639,12 +785,13 @@ class question_calculated_qtype extends question_dataset_dependent_questiontype
$errors .= " -$calculated->answer";
$stranswers .= $delimiter;
} else {
$stranswers .= $delimiter.$calculated->answer;
$strmin .= $delimiter.$calculated->min;
$strmax .= $delimiter.$calculated->max;
// $stranswers .= $delimiter.$calculated->answer;
// $strmin .= $delimiter.$calculated->min;
// $strmax .= $delimiter.$calculated->max;
$column[]=$stranswers.$delimiter.$calculated->answer.'<br/>'.$strmin.$delimiter.$calculated->min.'<br/>'.$strmax.$delimiter.$calculated->max ;
}
}
return "$stranswers<br/>$strmin<br/>$strmax<br/>$errors";
return $column;
}
function tolerance_types() {
@@ -653,18 +800,25 @@ class question_calculated_qtype extends question_dataset_dependent_questiontype
'3' => get_string('geometric', 'quiz'));
}
function dataset_options($form, $name, $renameabledatasets=false) {
function dataset_options($form, $name, $mandatory=true,$renameabledatasets=false) {
// Takes datasets from the parent implementation but
// filters options that are currently not accepted by calculated
// It also determines a default selection...
list($options, $selected) = parent::dataset_options($form, $name);
//$renameabledatasets not implemented anmywhere
list($options, $selected) = parent::dataset_options($form, $name,'','qtype_calculated');
// list($options, $selected) = $this->dataset_optionsa($form, $name);
foreach ($options as $key => $whatever) {
if (!ereg('^'.LITERAL.'-', $key) && $key != '0') {
unset($options[$key]);
}
}
if (!$selected) {
if ($mandatory){
$selected = LITERAL . "-0-$name"; // Default
}else {
$selected = "0"; // Default
}
}
return array($options, $selected);
}
@@ -721,6 +875,100 @@ class question_calculated_qtype extends question_dataset_dependent_questiontype
return $str;
}
/**
* This function retrieve the item count of the available category shareable
* wild cards that is added as a comment displayed when a wild card with
* the same name is displayed in datasetdefinitions_form.php
*/
function get_dataset_definitions_category($form) {
global $CFG;
$datasetdefs = array();
$lnamemax = 30;
if (!empty($form->category)) {
$sql = "SELECT i.*,d.*
FROM {$CFG->prefix}question_datasets d,
{$CFG->prefix}question_dataset_definitions i
WHERE i.id = d.datasetdefinition
AND i.category = '$form->category'
;
";
if ($records = get_records_sql($sql)) {
foreach ($records as $r) {
if ( !isset ($datasetdefs["$r->name"])) $datasetdefs["$r->name"] = $r->itemcount;
}
}
}
return $datasetdefs ;
}
/**
* This function build a table showing the available category shareable
* wild cards, their name, their definition (Min, Max, Decimal) , the item count
* and the name of the question where they are used.
* This table is intended to be add before the question text to help the user use
* these wild cards
*/
function print_dataset_definitions_category($form) {
global $CFG;
$datasetdefs = array();
$lnamemax = 22;
$namestr =get_string('name', 'quiz');
$minstr=get_string('min', 'quiz');
$maxstr=get_string('max', 'quiz');
$rangeofvaluestr=get_string('minmax','qtype_datasetdependent');
$questionusingstr = get_string('usedinquestion','qtype_calculated');
$wildcardstr = get_string('wildcard', 'qtype_calculated');
$itemscountstr = get_string('itemscount','qtype_datasetdependent');
$text ='';
if (!empty($form->category)) {
$sql = "SELECT i.*,d.*
FROM {$CFG->prefix}question_datasets d,
{$CFG->prefix}question_dataset_definitions i
WHERE i.id = d.datasetdefinition
AND i.category = '$form->category';
" ;
if ($records = get_records_sql($sql)) {
foreach ($records as $r) {
$sql1 = "SELECT q.*
FROM {$CFG->prefix}question q
WHERE q.id = $r->question
";
if ( !isset ($datasetdefs["$r->type-$r->category-$r->name"])){
$datasetdefs["$r->type-$r->category-$r->name"]= $r;
}
if ($questionb = get_records_sql($sql1)) {
$datasetdefs["$r->type-$r->category-$r->name"]->questions[$r->question]->name =$questionb[$r->question]->name ;
}
}
}
}
if (!empty ($datasetdefs)){
$text ="<table width=\"100%\" border=\"1\"><tr><th style=\"white-space:nowrap;\" class=\"header\" scope=\"col\" >$namestr</th><th style=\"white-space:nowrap;\" class=\"header\" scope=\"col\">$rangeofvaluestr</th><th style=\"white-space:nowrap;\" class=\"header\" scope=\"col\">$itemscountstr</th><th style=\"white-space:nowrap;\" class=\"header\" scope=\"col\">$questionusingstr</th></tr>";
foreach ($datasetdefs as $datasetdef){
list($distribution, $min, $max,$dec) = explode(':', $datasetdef->options, 4);
$text .="<tr><td valign=\"top\" align=\"center\"> $datasetdef->name </td><td align=\"center\" valign=\"top\"> $min <strong>-</strong> $max </td><td align=\"right\" valign=\"top\">$datasetdef->itemcount&nbsp;&nbsp;</td><td align=\"left\">";
foreach ($datasetdef->questions as $qu) {
//limit the name length displayed
if (!empty($qu->name)) {
$qu->name = (strlen($qu->name) > $lnamemax) ?
substr($qu->name, 0, $lnamemax).'...' : $qu->name;
} else {
$qu->name = '';
}
$text .=" &nbsp;&nbsp; $qu->name <br/>";
}
$text .="</td></tr>";
}
$text .="</table>";
}else{
$text .=get_string('nosharedwildcard', 'qtype_calculated');
}
return $text ;
}
/// BACKUP FUNCTIONS ////////////////////////////
/*
+228 -117
View File
@@ -27,7 +27,7 @@ class question_dataset_dependent_questiontype extends default_questiontype {
// Find out how many datasets are available
global $CFG;
if(!$maxnumber = (int)get_field_sql(
"SELECT MAX(a.itemcount)
"SELECT MIN(a.itemcount)
FROM {$CFG->prefix}question_dataset_definitions a,
{$CFG->prefix}question_datasets b
WHERE b.question = $question->id
@@ -76,42 +76,6 @@ class question_dataset_dependent_questiontype extends default_questiontype {
return true;
}
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();
$unit = $virtualqtype->get_default_numerical_unit($question);
foreach ($question->options->answers as $answer) {
$answer->answer = $this->substitute_variables($answer->answer,
$state->options->dataset);
}
$question->questiontext = $this->substitute_variables(
$question->questiontext, $state->options->dataset);
$virtualqtype->print_question_formulation_and_controls($question,
$state, $cmoptions, $options);
}
function grade_responses(&$question, &$state, $cmoptions) {
// Forward the grading to the virtual qtype
foreach ($question->options->answers as $answer) {
$answer->answer = $this->substitute_variables($answer->answer,
$state->options->dataset);
}
$virtualqtype = $this->get_virtual_qtype();
return $virtualqtype->grade_responses($question, $state, $cmoptions) ;
}
// ULPGC ecastro
function check_response(&$question, &$state) {
// Forward the checking to the virtual qtype
foreach ($question->options->answers as $answer) {
$answer->answer = $this->substitute_variables($answer->answer,
$state->options->dataset);
}
$virtualqtype = $this->get_virtual_qtype();
return $virtualqtype->check_response($question, $state) ;
}
function substitute_variables($str, $dataset) {
foreach ($dataset as $name => $value) {
@@ -156,7 +120,11 @@ class question_dataset_dependent_questiontype extends default_questiontype {
// See where we're coming from
switch($form->wizardpage) {
case 'question':
if (!isset($form->addanswers)) {
require("$CFG->dirroot/question/type/datasetdependent/datasetdefinitions.php");
// require("$CFG->dirroot/question/type/datasetdependent/datasetdefinitions_form.php");
// $mform =& new question_dataset_dependent_definitions_form("question.php?wizardnow=datasetdefinitions", $question);
}
break;
case 'datasetdefinitions':
case 'datasetitems':
@@ -168,6 +136,80 @@ class question_dataset_dependent_questiontype extends default_questiontype {
}
}
/**
* This method prepare the $datasets in a format similar to dadatesetdefinitions_form.php
* so that they can be saved
* using the function save_dataset_definitions($form)
* when creating a new calculated question or
* whenediting an already existing calculated question
* or by function save_as_new_dataset_definitions($form, $initialid)
* when saving as new an already existing calculated question
*
* @param object $form
* @param int $questionfromid default = '0'
*/
function preparedatasets($form , $questionfromid='0'){
// the dataset names present in the edit_question_form and edit_calculated_form are retrieved
$possibledatasets = $this->find_dataset_names($form->questiontext);
$mandatorydatasets = array();
foreach ($form->answers as $answer) {
$mandatorydatasets += $this->find_dataset_names($answer);
}
// if there are identical datasetdefs already saved in the original question.
// either when editing a question or saving as new
// they are retrieved using $questionfromid
if ($questionfromid!='0'){
$form->id = $questionfromid ;
}
$datasets = array();
$key = 0 ;
// always prepare the mandatorydatasets present in the answers
// the $options are not used here
foreach ($mandatorydatasets as $datasetname) {
if (!isset($datasets[$datasetname])) {
list($options, $selected) =
$this->dataset_options($form, $datasetname);
$datasets[$datasetname]='';
$form->dataset[$key]=$selected ;
$key++;
}
}
// do not prepare possibledatasets when creating a question
// they will defined and stored with datasetdefinitions_form.php
// the $options are not used here
if ($questionfromid!='0'){
foreach ($possibledatasets as $datasetname) {
if (!isset($datasets[$datasetname])) {
list($options, $selected) =
$this->dataset_options($form, $datasetname,false);
$datasets[$datasetname]='';
$form->dataset[$key]=$selected ;
$key++;
}
}
}
return $datasets ;
}
/**
* this version save the available data at the different steps of the question editing process
* without using global $SESSION as storage between steps
* at the first step $wizardnow = 'question'
* when creating a new question
* when modifying a question
* when copying as a new question
* the general parameters and answers are saved using parent::save_question
* then the datasets are prepared and saved
* at the second step $wizardnow = 'datasetdefinitions'
* the datadefs final type are defined as private, category or not a datadef
* at the third step $wizardnow = 'datasetitems'
* the datadefs parameters and the data items are created or defined
*
* @param object question
* @param object $form
* @param int $course
* @param PARAM_ALPHA $wizardnow should be added as we are coming from question2.php
*/
function save_question($question, &$form, $course) {
// For dataset dependent questions a wizard is used for editing
// questions. Therefore saving the question is delayed until
@@ -181,35 +223,34 @@ class question_dataset_dependent_questiontype extends default_questiontype {
// See where we're coming from
switch($form->wizardpage) {
case '' :
case 'question': // coming from the first page, creating the second
if (empty($form->id)) {
$SESSION->datasetdependent = new stdClass;
$SESSION->datasetdependent->questionform = $form;
} else {
if (empty($form->id)) { // for a new question $form->id is empty
$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 {
//prepare the datasets using default $questionfromid
$form->datasets = $this->preparedatasets($form);
$form->id = $question->id;
$this->save_dataset_definitions($form);
} else if (!empty($form->makecopy)){
$questionfromid = $form->id ;
$question = parent::save_question($question, $form, $course);
//prepare the datasets
$form->datasets = $this->preparedatasets($form,$questionfromid);
$form->id = $question->id;
$this->save_as_new_dataset_definitions($form,$questionfromid );
} else {// editing a question
$question = parent::save_question($question, $form, $course);
//prepare the datasets
$form->datasets = $this->preparedatasets($form,$question->id);
$form->id = $question->id;
$this->save_dataset_definitions($form);
}
break;
case 'datasetdefinitions':
$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);
}
$this->save_dataset_items($question, $form);
break;
default:
error('Incorrect or no wizard page specified!');
@@ -218,14 +259,19 @@ class question_dataset_dependent_questiontype extends default_questiontype {
return $question;
}
function get_dataset_definitions($form) {
function save_dataset_items($question, $fromform){
//overridden in child classes
}
function get_dataset_definitions($questionid, $newdatasets) {
//get the existing datasets for this question
$datasetdefs = array();
if (!empty($form->id)) {
if (!empty($questionid)) {
global $CFG;
$sql = "SELECT i.*
FROM {$CFG->prefix}question_datasets d,
{$CFG->prefix}question_dataset_definitions i
WHERE d.question = '$form->id'
WHERE d.question = '$questionid'
AND d.datasetdefinition = i.id
";
if ($records = get_records_sql($sql)) {
@@ -235,20 +281,20 @@ class question_dataset_dependent_questiontype extends default_questiontype {
}
}
$datasets = empty($form->dataset) ? $form->definition : $form->dataset;
foreach ($datasets as $dataset) {
foreach ($newdatasets as $dataset) {
if (!$dataset) {
continue; // The no dataset case...
}
if (!isset($datasetdefs[$dataset])) {
//make new datasetdef
list($type, $category, $name) = explode('-', $dataset, 3);
$datasetdef = new stdClass;
$datasetdef->type = $type;
$datasetdef->name = $name;
$datasetdef->category = $category;
$datasetdef->itemcount = 0;
$datasetdef->options = '';
$datasetdef->options = 'uniform:1.0:10.0:1';
$datasetdefs[$dataset] = clone($datasetdef);
}
}
@@ -257,7 +303,7 @@ class question_dataset_dependent_questiontype extends default_questiontype {
function save_dataset_definitions($form) {
// Save datasets
$datasetdefinitions = $this->get_dataset_definitions($form);
$datasetdefinitions = $this->get_dataset_definitions($form->id, $form->dataset);
$tmpdatasets = array_flip($form->dataset);
$defids = array_keys($datasetdefinitions);
foreach ($defids as $defid) {
@@ -334,7 +380,122 @@ class question_dataset_dependent_questiontype extends default_questiontype {
}
}
}
/** This function create a copy of the datasets ( definition and dataitems)
* from the preceding question if they remain in the new question
* otherwise its create the datasets that have been added as in the
* save_dataset_definitions()
*/
function save_as_new_dataset_definitions($form, $initialid) {
global $CFG ;
// Get the datasets from the intial question
$datasetdefinitions = $this->get_dataset_definitions($initialid, $form->dataset);
// $tmpdatasets contains those of the new question
$tmpdatasets = array_flip($form->dataset);
$defids = array_keys($datasetdefinitions);// new datasets
foreach ($defids as $defid) {
$datasetdef = &$datasetdefinitions[$defid];
if (isset($datasetdef->id)) {
// This dataset exist in the initial question
if (!isset($tmpdatasets[$defid])) {
// do not exist in the new question so ignore
unset($datasetdefinitions[$defid]);
continue;
}
// create a copy but not for category one
if (0 == $datasetdef->category) {
$olddatasetid = $datasetdef->id ;
$olditemcount = $datasetdef->itemcount ;
$datasetdef->itemcount =0;
if (!$datasetdef->id = insert_record(
'question_dataset_definitions', $datasetdef)) {
error("Unable to create dataset $defid");
}
//copy the dataitems
$olditems = get_records_sql( // Use number as key!!
" SELECT number, value
FROM {$CFG->prefix}question_dataset_items
WHERE definition = $olddatasetid ");
if (count($olditems) > 0 ) {
$itemcount = 0;
foreach($olditems as $item ){
$item->definition = $datasetdef->id;
if (!insert_record('question_dataset_items', $item)) {
error("Unable to insert dataset item $item->number with $item->value for $datasetdef->name");
}
$itemcount++;
}
//update item count
$datasetdef->itemcount =$itemcount;
update_record('question_dataset_definitions', $datasetdef);
} // end of copy the dataitems
}// end of copy the datasetdef
// Create relation to the new question with this
// copy as new datasetdef from the initial question
$questiondataset = new stdClass;
$questiondataset->question = $form->id;
$questiondataset->datasetdefinition = $datasetdef->id;
if (!insert_record('question_datasets',
$questiondataset)) {
error("Unable to create relation to dataset $name");
}
unset($datasetdefinitions[$defid]);
continue;
}// end of datasetdefs from the initial question
// really new one code similar to save_dataset_definitions()
if (!$datasetdef->id = insert_record(
'question_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(
'question_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('question_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('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('question_datasets',
'question', $form->id,
'datasetdefinition', $def->id);
if ($def->category == 0) { // Question local dataset
delete_records('question_dataset_definitions', 'id', $def->id);
delete_records('question_dataset_items',
'definition', $def->id);
}
}
}
}
/*
@@ -395,58 +556,8 @@ class question_dataset_dependent_questiontype extends default_questiontype {
*/
/*
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, $datasetitem) {
// Select a dataset in the following format:
+94 -18
View File
@@ -1,6 +1,7 @@
<?php // $Id$
// Allows a teacher to create, edit and delete datasets
// max datasets = 100 items
$max100 = 100 ;
// Set up strings
$strdatasetnumber = get_string("datasetnumber", "quiz");
$strnumberinfo = get_string("categoryinfo", "quiz");
@@ -19,6 +20,8 @@
$strforceregeneration = get_string('forceregeneration', 'quiz');
$strdataitemneed = get_string('dataitemneed', 'quiz');
$strcondensedtable =get_string('switchtocondensedtable', 'qtype_datasetdependent');
$strnormaltable =get_string('switchtonormaltable', 'qtype_datasetdependent');
if (empty($question->id)) {
$datasetdefs = $this->get_dataset_definitions(
$SESSION->datasetdependent->definitionform);
@@ -26,7 +29,7 @@
if (empty($question->options)) {
$this->get_question_options($question);
}
$datasetdefs = $this->get_dataset_definitions($form);
$datasetdefs = $this->get_dataset_definitions($question->id, array());
}
// Handle generator options...
@@ -55,8 +58,21 @@
$addeditem->definition = $datasetdefs[$defid]->id;
$addeditem->value = $form->value[$key];
if ($form->itemid[$key]) {
// Reuse an previously used record
$addeditem->id = $form->itemid[$key];
// Reuse an previously used record
if (empty($form->forceregeneration) && (
isset($form->regenerateddefid) &&
$form->regenerateddefid != $defid ||
isset($form->deletebutton))) {
// echo "<p>Reuse an previously used record un".$addeditem->id."</p>";
} else {
if ($this->supports_dataset_item_generation()) {
$addeditem->value = $this->generate_dataset_item($datasetdefs[$defid]->options);
} else {
$addeditem->value = '';
}
}
//echo "<p>update le dataset_items".$addeditem->id."</p>";
if (!update_record('question_dataset_items', $addeditem)) {
error("Error: Unable to update dataset item");
}
@@ -66,31 +82,79 @@
error("Error: Unable to insert dataset item");
}
}
}
$maxnumber = $addeditem->number;
$numbertoadd =0;
if ($form->selectadd > 1 && $maxnumber < $max100 ) {
$numbertoadd =$form->selectadd-1 ;
if ( $max100 - $maxnumber < $numbertoadd ) {
$numbertoadd = $max100 - $maxnumber ;
}
//add the other items.
// tout d'abord le définit
// 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}question_dataset_items
WHERE definition = $datasetdef->id ORDER BY number");
}
// echo "<pre>"; print_r($datasetdefs[$defid]->items);
for ($numberadded =$maxnumber+1 ; $numberadded <= $maxnumber+$numbertoadd ; $numberadded++){
if (isset($datasetdefs[$defid]->items[$numberadded]) && (
empty($form->forceregeneration)
// && isset($form->regenerateddefid) &&
// $form->regenerateddefid != $defid || no
|| isset($form->deletebutton))) {
// echo "<p>Reuse an previously used record".$numberadded."id".$datasetdef->id."</p>";
} else {
$datasetitem = new stdClass;
$datasetitem->definition = $datasetdef->id ;
$datasetitem->number = $numberadded;
if ($this->supports_dataset_item_generation()) {
$datasetitem->value = $this->generate_dataset_item($datasetdef->options);
} else {
$datasetitem->value = '';
}
//pp echo "<pre>"; print_r( $datasetitem );
//insere le nouveau
if (!insert_record('question_dataset_items', $datasetitem)) {
error("Error: Unable to insert new dataset item");
}
$datasetdefs[$defid]->items[$numberadded] = clone($datasetitem);
}
}//for number added
}// datasetsdefs end
}
$maxnumber += $numbertoadd ;
foreach ($datasetdefs as $key => $newdef) {
if (isset($newdef->id) && $newdef->itemcount <= $maxnumber) {
$newdef->itemcount = $maxnumber+1;
$newdef->itemcount = $maxnumber;
// Save the new value for options
update_record('question_dataset_definitions', $newdef);
}
}
}
// else Success:
$maxnumber = $addeditem->number;
//} // if select >
} else if (isset($form->deletebutton)
&& $maxnumber == $form->numbertodelete) {
// Simply decrease itemcount where == $maxnumber
// selectedelete<p align="center"></p>
if(isset($form->selectdelete)) $newmaxnumber = $maxnumber-$form->selectdelete ;
else $newmaxnumber = $maxnumber-1 ;
if ($newmaxnumber < 0 ) $newmaxnumber = 0 ;
foreach ($datasetdefs as $datasetdef) {
if ($datasetdef->itemcount == $maxnumber) {
$datasetdef->itemcount--;
$datasetdef->itemcount= $newmaxnumber ; //pp--;
if (!update_record('question_dataset_definitions',
$datasetdef)) {
error("Error: Unable to update itemcount");
}
}
}
--$maxnumber;
$maxnumber= $newmaxnumber;
}
make_upload_directory("$course->id"); // Just in case
@@ -160,13 +224,17 @@
. '<br/><input type="radio" name="forceregeneration" value="1" '
. $force . ' />' . $strforceregeneration;
}
//pp limit
if ($maxnumber < $max100) {
$addline[] = '<input type="hidden" name="numbertoadd" value="'.
($maxnumber+1).'"/><input type="submit" name="addbutton" '.
'value="'.$stradd.'"/>'.$forceregeneration;
'value="'.$stradd.'"/>'.'<select name="selectadd"><option value="1" selected>1</option><option value="10">10</option><option value="20">20</option><option value="30">30</option><option value="40">40</option><option value="50">50</option><option value="100">100</option></select>' .$forceregeneration;
$addline[] = $maxnumber+1;
} else {
$addline[] ='<input type="hidden" name="numbertoadd" value="'.($maxnumber+1).'"/>';
$addline[] = "&gt;".$max100;
}
foreach ($datasetdefs as $defid => $datasetdef) {
if ($datasetdef->name) {
$table->head[] = $datasetdef->name;
@@ -197,8 +265,10 @@
}
if ($strquestionheader) {
$table->head[] = $strquestionheader;
$addtable->head[] = $strquestionheader;
foreach($strquestionheader as $header){
$table->head[] = $header;
$addtable->head[] = $header;
}
if (empty($question->id) &&
isset($SESSION->datasetdependent->questionform)) {
$tmp = &$SESSION->datasetdependent->questionform;
@@ -212,7 +282,10 @@
}
$question->options->answers = $answers;
}
$addline[] = $this->comment_on_datasetitems($question, $data, $maxnumber + 1);
$comment =$this->comment_on_datasetitems($question, $data, $maxnumber + 1);
foreach($comment as $comm){
$addline[] = $comm ;
}
}
// Set up the table showing existing datasets
@@ -223,7 +296,7 @@
if ($maxnumber == $number) {
$columns[] =
"<input type=\"hidden\" name=\"numbertodelete\" value=\"$number\"/>
<input type=\"submit\" name=\"deletebutton\" value=\"$strdelete\"/>";
<input type=\"submit\" name=\"deletebutton\" value=\"$strdelete\"/>".'<select name="selectdelete"><option value="1" selected>1</option><option value="10">10</option><option value="20">20</option><option value="30">30</option><option value="40">40</option><option value="50">50</option><option value="100">100</option></select>';
} else {
$columns[] = '';
}
@@ -237,7 +310,10 @@
($data[$datasetdef->name] = $datasetdef->items[$number]->value) ;
}
if ($strquestionheader) {
$columns[] = $this->comment_on_datasetitems($question, $data, $number);
$comment =$this->comment_on_datasetitems($question, $data, $number);
foreach($comment as $comm){
$columns[] = $comm ;
}
}
$table->data[] = $columns;
}