public function save_question_options($fromform) { global $DB; $context = $fromform->context; parent::save_question_options($fromform); $options = $DB->get_record('qtype_stack_options', array('questionid' => $fromform->id)); if (!$options) { $options = new stdClass(); $options->questionid = $fromform->id; $options->questionvariables = ''; $options->specificfeedback = ''; $options->prtcorrect = ''; $options->prtpartiallycorrect = ''; $options->prtincorrect = ''; $options->id = $DB->insert_record('qtype_stack_options', $options); } $options->questionvariables = $fromform->questionvariables; $options->specificfeedback = $this->import_or_save_files($fromform->specificfeedback, $context, 'qtype_stack', 'specificfeedback', $fromform->id); $options->specificfeedbackformat = $fromform->specificfeedback['format']; $options->questionnote = $fromform->questionnote; $options->questionsimplify = $fromform->questionsimplify; $options->assumepositive = $fromform->assumepositive; $options->prtcorrect = $this->import_or_save_files($fromform->prtcorrect, $context, 'qtype_stack', 'prtcorrect', $fromform->id); $options->prtcorrectformat = $fromform->prtcorrect['format']; $options->prtpartiallycorrect = $this->import_or_save_files($fromform->prtpartiallycorrect, $context, 'qtype_stack', 'prtpartiallycorrect', $fromform->id); $options->prtpartiallycorrectformat = $fromform->prtpartiallycorrect['format']; $options->prtincorrect = $this->import_or_save_files($fromform->prtincorrect, $context, 'qtype_stack', 'prtincorrect', $fromform->id); $options->prtincorrectformat = $fromform->prtincorrect['format']; $options->multiplicationsign = $fromform->multiplicationsign; $options->sqrtsign = $fromform->sqrtsign; $options->complexno = $fromform->complexno; $options->inversetrig = $fromform->inversetrig; $options->matrixparens = $fromform->matrixparens; $options->variantsselectionseed = $fromform->variantsselectionseed; $DB->update_record('qtype_stack_options', $options); $inputnames = stack_utils::extract_placeholders($fromform->questiontext, 'input'); $inputs = $DB->get_records('qtype_stack_inputs', array('questionid' => $fromform->id), '', 'name, id, questionid'); $questionhasinputs = false; foreach ($inputnames as $inputname) { if (array_key_exists($inputname, $inputs)) { $input = $inputs[$inputname]; unset($inputs[$inputname]); } else { $input = new stdClass(); $input->questionid = $fromform->id; $input->name = $inputname; $input->options = ''; $input->id = $DB->insert_record('qtype_stack_inputs', $input); } $input->type = $fromform->{$inputname . 'type'}; $input->tans = $fromform->{$inputname . 'modelans'}; $input->boxsize = $fromform->{$inputname . 'boxsize'}; $input->strictsyntax = $fromform->{$inputname . 'strictsyntax'}; $input->insertstars = $fromform->{$inputname . 'insertstars'}; $input->syntaxhint = $fromform->{$inputname . 'syntaxhint'}; $input->forbidwords = $fromform->{$inputname . 'forbidwords'}; $input->allowwords = $fromform->{$inputname . 'allowwords'}; $input->forbidfloat = $fromform->{$inputname . 'forbidfloat'}; $input->requirelowestterms = $fromform->{$inputname . 'requirelowestterms'}; $input->checkanswertype = $fromform->{$inputname . 'checkanswertype'}; $input->mustverify = $fromform->{$inputname . 'mustverify'}; $input->showvalidation = $fromform->{$inputname . 'showvalidation'}; $input->options = $fromform->{$inputname . 'options'}; $questionhasinputs = true; $DB->update_record('qtype_stack_inputs', $input); } if ($inputs) { list($test, $params) = $DB->get_in_or_equal(array_keys($inputs)); $params[] = $fromform->id; $DB->delete_records_select('qtype_stack_inputs', 'name ' . $test . ' AND questionid = ?', $params); } if (!$questionhasinputs) { // A question with no inputs is an information item. $DB->set_field('question', 'length', 0, array('id' => $fromform->id)); } $prtnames = stack_utils::extract_placeholders($fromform->questiontext . $options->specificfeedback, 'feedback'); $prts = $DB->get_records('qtype_stack_prts', array('questionid' => $fromform->id), '', 'name, id, questionid'); foreach ($prtnames as $prtname) { if (array_key_exists($prtname, $prts)) { $prt = $prts[$prtname]; unset($prts[$prtname]); } else { $prt = new stdClass(); $prt->questionid = $fromform->id; $prt->name = $prtname; $prt->feedbackvariables = ''; $prt->firstnodename = 0; $prt->id = $DB->insert_record('qtype_stack_prts', $prt); } // Find the root node of the PRT. // Otherwise, if an existing question is being edited, and this is an // existing PRT, base things on the existing question definition. $graph = new stack_abstract_graph(); foreach ($fromform->{$prtname . 'answertest'} as $nodename => $notused) { $truenextnode = $fromform->{$prtname . 'truenextnode'}[$nodename]; $falsenextnode = $fromform->{$prtname . 'falsenextnode'}[$nodename]; if ($truenextnode == -1) { $left = null; } else { $left = $truenextnode + 1; } if ($falsenextnode == -1) { $right = null; } else { $right = $falsenextnode + 1; } $graph->add_node($nodename + 1, $left, $right); } $graph->layout(); $roots = $graph->get_roots(); if (count($roots) != 1 || $graph->get_broken_cycles()) { throw new coding_exception('The PRT ' . $prtname . ' is malformed.'); } reset($roots); $firstnode = key($roots) - 1; $prt->value = $fromform->{$prtname . 'value'}; $prt->autosimplify = $fromform->{$prtname . 'autosimplify'}; $prt->feedbackvariables = $fromform->{$prtname . 'feedbackvariables'}; $prt->firstnodename = $firstnode; $DB->update_record('qtype_stack_prts', $prt); $nodes = $DB->get_records('qtype_stack_prt_nodes', array('questionid' => $fromform->id, 'prtname' => $prtname), '', 'nodename, id, questionid, prtname'); foreach ($fromform->{$prtname . 'answertest'} as $nodename => $notused) { if (array_key_exists($nodename, $nodes)) { $node = $nodes[$nodename]; unset($nodes[$nodename]); } else { $node = new stdClass(); $node->questionid = $fromform->id; $node->prtname = $prtname; $node->nodename = $nodename; $node->truefeedback = ''; $node->falsefeedback = ''; $node->id = $DB->insert_record('qtype_stack_prt_nodes', $node); } $node->answertest = $fromform->{$prtname . 'answertest'}[$nodename]; $node->sans = $fromform->{$prtname . 'sans'}[$nodename]; $node->tans = $fromform->{$prtname . 'tans'}[$nodename]; $node->testoptions = $fromform->{$prtname . 'testoptions'}[$nodename]; $node->quiet = $fromform->{$prtname . 'quiet'}[$nodename]; $node->truescoremode = $fromform->{$prtname . 'truescoremode'}[$nodename]; $node->truescore = $fromform->{$prtname . 'truescore'}[$nodename]; $node->truepenalty = $fromform->{$prtname . 'truepenalty'}[$nodename]; $node->truenextnode = $fromform->{$prtname . 'truenextnode'}[$nodename]; $node->trueanswernote = $fromform->{$prtname . 'trueanswernote'}[$nodename]; $node->truefeedback = $this->import_or_save_files($fromform->{$prtname . 'truefeedback'}[$nodename], $context, 'qtype_stack', 'prtnodetruefeedback', $node->id); $node->truefeedbackformat = $fromform->{$prtname . 'truefeedback'}[$nodename]['format']; $node->falsescoremode = $fromform->{$prtname . 'falsescoremode'}[$nodename]; $node->falsescore = $fromform->{$prtname . 'falsescore'}[$nodename]; $node->falsepenalty = $fromform->{$prtname . 'falsepenalty'}[$nodename]; $node->falsenextnode = $fromform->{$prtname . 'falsenextnode'}[$nodename]; $node->falseanswernote = $fromform->{$prtname . 'falseanswernote'}[$nodename]; $node->falsefeedback = $this->import_or_save_files($fromform->{$prtname . 'falsefeedback'}[$nodename], $context, 'qtype_stack', 'prtnodefalsefeedback', $node->id); $node->falsefeedbackformat = $fromform->{$prtname . 'falsefeedback'}[$nodename]['format']; if ('' === $node->truepenalty) { $node->truepenalty = null; } if ('' === $node->falsepenalty) { $node->falsepenalty = null; } $DB->update_record('qtype_stack_prt_nodes', $node); } if ($nodes) { list($test, $params) = $DB->get_in_or_equal(array_keys($nodes)); $params[] = $fromform->id; $params[] = $prt->name; $DB->delete_records_select('qtype_stack_prt_nodes', 'nodename ' . $test . ' AND questionid = ? AND prtname = ?', $params); } } if ($prts) { list($test, $params) = $DB->get_in_or_equal(array_keys($prts)); $params[] = $fromform->id; $DB->delete_records_select('qtype_stack_prt_nodes', 'prtname ' . $test . ' AND questionid = ?', $params); $DB->delete_records_select('qtype_stack_prts', 'name ' . $test . ' AND questionid = ?', $params); } $this->save_hints($fromform); if (isset($fromform->deployedseeds)) { $DB->delete_records('qtype_stack_deployed_seeds', array('questionid' => $fromform->id)); foreach ($fromform->deployedseeds as $deployedseed) { $record = new stdClass(); $record->questionid = $fromform->id; $record->seed = $deployedseed; $DB->insert_record('qtype_stack_deployed_seeds', $record, false); } } // This is a bit of a hack. If doing 'Make a copy' when saving the // editing form, then detect that here, and try to copy the question // tests from the original question. if (!isset($fromform->testcases) && !empty($fromform->makecopy)) { $oldquestionid = optional_param('id', 0, PARAM_INT); if ($oldquestionid) { $fromform->testcases = $this->load_question_tests($oldquestionid); } } if (isset($fromform->testcases)) { // If the data includes the defintion of the question tests that there // should be (i.e. when doing import) then replace the existing set // of tests with the new one. $this->save_question_tests($fromform->id, $fromform->testcases); } // Irrespective of what else has happened, ensure there is no garbage // in the database, for example if we delete a PRT, remove the expected // values for that PRT while leaving the rest of the testcases alone. list($nametest, $params) = $DB->get_in_or_equal($inputnames, SQL_PARAMS_NAMED, 'input', false, null); $params['questionid'] = $fromform->id; $DB->delete_records_select('qtype_stack_qtest_inputs', 'questionid = :questionid AND inputname ' . $nametest, $params); list($nametest, $params) = $DB->get_in_or_equal($prtnames, SQL_PARAMS_NAMED, 'prt', false, null); $params['questionid'] = $fromform->id; $DB->delete_records_select('qtype_stack_qtest_expected', 'questionid = :questionid AND prtname ' . $nametest, $params); }
/** * This graph has 2 distinct nodes. We verify that they are both detected as roots. */ public function test_two_roots() { $graph = new stack_abstract_graph(); $graph->add_node(1, null, null, '=1', '=0'); $graph->add_node(2, null, null, '=1', '=0'); $graph->layout(); $n = $graph->get(1); $this->assertEquals(1, $n->depth); $this->assertEquals(2, $n->x); $n = $graph->get(2); $this->assertEquals(1, $n->depth); $this->assertEquals(0, $n->x); $this->assertEmpty($graph->get_broken_cycles()); $this->assertSame(array(1, 2), array_keys($graph->get_roots())); }
/** * When restoring old data, that does not have the essay options information * in the XML, supply defaults. */ protected function after_execute_question() { global $DB; $prtswithoutfirstnode = $DB->get_records('qtype_stack_prts', array('firstnodename' => -1), '', 'id, questionid, name'); foreach ($prtswithoutfirstnode as $prt) { $nodes = $DB->get_records('qtype_stack_prt_nodes', array('questionid' => $prt->questionid, 'prtname' => $prt->name), '', 'nodename, truenextnode, falsenextnode'); // Find the root node of the PRT. // Otherwise, if an existing question is being edited, and this is an // existing PRT, base things on the existing question definition. $graph = new stack_abstract_graph(); foreach ($nodes as $node) { if ($node->truenextnode == -1) { $left = null; } else { $left = $node->truenextnode + 1; } if ($node->falsenextnode == -1) { $right = null; } else { $right = $node->falsenextnode + 1; } $graph->add_node($node->nodename + 1, $left, $right); } $graph->layout(); $roots = $graph->get_roots(); if (count($roots) != 1 || $graph->get_broken_cycles()) { $questions = $DB->get_records('question', array('id' => $prt->questionid), '', 'name'); $qnames = array(); foreach ($questions as $q) { $qnames[] = $q->name; } if (count($roots) != 1) { $err = 'abnormal root count: ' . count($roots) . '(<>1)'; } else { $err = 'broken cycles: ' . implode('.', $graph->get_broken_cycles()); } throw new coding_exception('The PRT named "' . $prt->name . '" is malformed in question id ' . $prt->questionid . ', question named "' . implode(', ', $qnames) . '". Error reported: ' . $err); } reset($roots); $firstnode = key($roots) - 1; $DB->set_field('qtype_stack_prts', 'firstnodename', $firstnode, array('id' => $prt->id)); } }