Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: c74677ca95
Fetching contributors…

Cannot retrieve contributors at this time

709 lines (642 sloc) 29.85 kb
<?php // $Id$
///////////////////////////////////////////////////////////////
/// ABSTRACT SUPERCLASS FOR QUSTION TYPES THAT USE DATASETS ///
///////////////////////////////////////////////////////////////
require_once($CFG->libdir.'/filelib.php');
define("LITERAL", "1");
define("FILE", "2");
define("LINK", "3");
class question_dataset_dependent_questiontype extends default_questiontype {
var $virtualqtype = false;
function name() {
return 'datasetdependent';
}
function save_question_options($question) {
// Default does nothing...
return true;
}
function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) {
// Find out how many datasets are available
global $CFG;
if(!$maxnumber = (int)get_field_sql(
"SELECT MIN(a.itemcount)
FROM {$CFG->prefix}question_dataset_definitions a,
{$CFG->prefix}question_datasets b
WHERE b.question = $question->id
AND a.id = b.datasetdefinition")) {
error("Couldn't get the specified dataset for a calculated " .
"question! (question: {$question->id}");
}
// Choose a random dataset
$state->options->datasetitem = rand(1, $maxnumber);
$state->options->dataset =
$this->pick_question_dataset($question,$state->options->datasetitem);
$state->responses = array('' => '');
return true;
}
function restore_session_and_responses(&$question, &$state) {
if (!ereg('^dataset([0-9]+)[^-]*-(.*)$',
$state->responses[''], $regs)) {
notify ("Wrongly formatted raw response answer " .
"{$state->responses['']}! Could not restore session for " .
" question #{$question->id}.");
$state->options->datasetitem = 1;
$state->options->dataset = array();
$state->responses = array('' => '');
return false;
}
// Restore the chosen dataset
$state->options->datasetitem = $regs[1];
$state->options->dataset =
$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->datasetitem.'-'.
$state->responses[''];
// Set the legacy answer field
if (!set_field('question_states', 'answer', $responses, 'id',
$state->id)) {
return false;
}
return true;
}
function substitute_variables($str, $dataset) {
foreach ($dataset as $name => $value) {
$str = str_replace('{'.$name.'}', $value, $str);
}
return $str;
}
function uses_quizfile($question, $relativefilepath) {
// Check whether the specified file is available by any
// dataset item on this question...
global $CFG;
if (get_record_sql(" SELECT *
FROM {$CFG->prefix}question_dataset_items i,
{$CFG->prefix}question_dataset_definitions d,
{$CFG->prefix}question_datasets q
WHERE i.value = '$relativefilepath'
AND d.id = i.definition AND d.type = 2
AND d.id = q.datasetdefinition
AND q.question = $question->id ")) {
return true;
} else {
// Make the check of the parent:
return parent::uses_quizfile($question, $relativefilepath);
}
}
function finished_edit_wizard(&$form) {
return isset($form->backtoquiz);
}
// This gets called by editquestion.php after the standard question is saved
function print_next_wizard_page(&$question, &$form, $course) {
global $CFG, $USER, $SESSION;
// Catch invalid navigation & reloads
if (empty($question->id) && empty($SESSION->datasetdependent)) {
redirect('edit.php', 'The page you are loading has expired.', 3);
}
// 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':
require("$CFG->dirroot/question/type/datasetdependent/datasetitems.php");
break;
default:
error('Incorrect or no wizard page specified!');
break;
}
}
/**
* 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
// 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 '' :
case 'question': // coming from the first page, creating the second
if (empty($form->id)) { // for a new question $form->id is empty
$question = parent::save_question($question, $form, $course);
//prepare the datasets using default $questionfromid
$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
$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
$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':
$this->save_dataset_items($question, $form);
break;
default:
error('Incorrect or no wizard page specified!');
break;
}
return $question;
}
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($questionid)) {
global $CFG;
$sql = "SELECT i.*
FROM {$CFG->prefix}question_datasets d,
{$CFG->prefix}question_dataset_definitions i
WHERE d.question = '$questionid'
AND d.datasetdefinition = i.id
";
if ($records = get_records_sql($sql)) {
foreach ($records as $r) {
$datasetdefs["$r->type-$r->category-$r->name"] = $r;
}
}
}
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 = 'uniform:1.0:10.0:1';
$datasetdefs[$dataset] = clone($datasetdef);
}
}
return $datasetdefs;
}
function save_dataset_definitions($form) {
// Save datasets
$datasetdefinitions = $this->get_dataset_definitions($form->id, $form->dataset);
$tmpdatasets = array_flip($form->dataset);
$defids = array_keys($datasetdefinitions);
foreach ($defids as $defid) {
$datasetdef = &$datasetdefinitions[$defid];
if (isset($datasetdef->id)) {
if (!isset($tmpdatasets[$defid])) {
// This dataset is not used any more, delete it
delete_records('question_datasets',
'question', $form->id,
'datasetdefinition', $datasetdef->id);
if ($datasetdef->category == 0) { // Question local dataset
delete_records('question_dataset_definitions',
'id', $datasetdef->id);
delete_records('question_dataset_items',
'definition', $datasetdef->id);
}
}
// This has already been saved or just got deleted
unset($datasetdefinitions[$defid]);
continue;
}
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);
}
}
}
}
/** 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 itemnumber, 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->itemnumber 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);
}
}
}
}
/*
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;
}
*/
/// Dataset functionality
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
global $CFG;
if (!$dataset = get_records_sql(
"SELECT d.name, i.value
FROM {$CFG->prefix}question_dataset_definitions d,
{$CFG->prefix}question_dataset_items i,
{$CFG->prefix}question_datasets q
WHERE q.question = $question->id
AND q.datasetdefinition = d.id
AND d.id = i.definition
AND i.itemnumber = $datasetitem")) {
error("Couldn't get the specified dataset for a dataset dependent " .
"question! (question: {$question->id}, " .
"datasetitem: {$datasetitem})");
}
array_walk($dataset, create_function('&$val', '$val = $val->value;'));
return $dataset;
}
function create_virtual_qtype() {
error("No vitrual question type for question type ".$this->name());
}
function get_virtual_qtype() {
if (!$this->virtualqtype) {
$this->virtualqtype = $this->create_virtual_qtype();
}
return $this->virtualqtype;
}
function comment_header($question) {
// Used by datasetitems.php
// Default returns nothing and thus takes away the column
return '';
}
function comment_on_datasetitems($question, $data, $number) {
// Used by datasetitems.php
// Default returns nothing
return '';
}
function supports_dataset_item_generation() {
// Used by datasetitems.php
// Default does not support any item generation
return false;
}
function custom_generator_tools($datasetdef) {
// Used by datasetitems.php
// If there is no generation support,
// there cannot possibly be any custom tools either
return '';
}
function generate_dataset_item($options) {
// Used by datasetitems.php
// By default nothing is generated
return '';
}
function update_dataset_options($datasetdefs, $form) {
// Used by datasetitems.php
// Returns the updated datasets
// By default the dataset options cannot be updated
return $datasetdefs;
}
function dataset_options($form, $name,$prefix='',$langfile='quiz') {
// First options - it is not a dataset...
$options['0'] = get_string($prefix.'nodataset', $langfile);
// Construct question local options
global $CFG;
$currentdatasetdef = get_record_sql(
"SELECT a.*
FROM {$CFG->prefix}question_dataset_definitions a,
{$CFG->prefix}question_datasets b
WHERE a.id = b.datasetdefinition
AND b.question = '$form->id'
AND a.name = '$name'")
or $currentdatasetdef->type = '0';
foreach (array( LITERAL, FILE, LINK) as $type) {
$key = "$type-0-$name";
if ($currentdatasetdef->type == $type
and $currentdatasetdef->category == 0) {
$options[$key] = get_string($prefix."keptlocal$type", $langfile);
} else {
$options[$key] = get_string($prefix."newlocal$type", $langfile);
}
}
// Construct question category options
$categorydatasetdefs = get_records_sql(
"SELECT a.type, a.id
FROM {$CFG->prefix}question_dataset_definitions a,
{$CFG->prefix}question_datasets b
WHERE a.id = b.datasetdefinition
AND a.category = '$form->category'
AND a.name = '$name'");
foreach(array( LITERAL, FILE, LINK) as $type) {
$key = "$type-$form->category-$name";
if (isset($categorydatasetdefs[$type])
and $categorydef = $categorydatasetdefs[$type]) {
if ($currentdatasetdef->type == $type
and $currentdatasetdef->id == $categorydef->id) {
$options[$key] = get_string($prefix."keptcategory$type", $langfile);
} else {
$options[$key] = get_string($prefix."existingcategory$type", $langfile);
}
} else {
$options[$key] = get_string($prefix."newcategory$type", $langfile);
}
}
// All done!
return array($options, $currentdatasetdef->type
? "$currentdatasetdef->type-$currentdatasetdef->category-$name"
: '');
}
function find_dataset_names($text) {
/// Returns the possible dataset names found in the text as an array
/// The array has the dataset name for both key and value
$datasetnames = array();
while (ereg('\\{([[:alpha:]][^>} <{"\']*)\\}', $text, $regs)) {
$datasetnames[$regs[1]] = $regs[1];
$text = str_replace($regs[0], '', $text);
}
return $datasetnames;
}
function create_virtual_nameprefix($nameprefix, $datasetinput) {
if (!ereg('([0-9]+)' . $this->name() . '$', $nameprefix, $regs)) {
error("Wrongly formatted nameprefix $nameprefix");
}
$virtualqtype = $this->get_virtual_qtype();
return $nameprefix . $regs[1] . $virtualqtype->name();
}
}
//// END OF CLASS ////
?>
Jump to Line
Something went wrong with that request. Please try again.