예제 #1
0
 /**
  * Output a graph as SVG.
  * @param stack_abstract_graph $g the graph to display.
  * @param string $id an id to add to the SVG node in the HTML.
  */
 public static function render(stack_abstract_graph $g, $id)
 {
     $renderer = new self($g);
     list($minx, $maxx) = $g->x_range();
     $width = ceil((2 + $maxx - $minx) * self::SCALE / 2);
     $height = ceil((0.3 + $g->max_depth()) * self::SCALE);
     $output = '';
     $output .= html_writer::start_tag('svg', array('id' => $id, 'class' => 'stack_abstract_graph', 'width' => $width . 'px', 'height' => $height . 'px', 'version' => '1.1', 'xmlns' => 'http://www.w3.org/2000/svg'));
     $output .= $renderer->to_svg();
     $output .= html_writer::end_tag('svg');
     return $output;
 }
 /**
  * Get a stack_abstract_graph represemtatopm of a PRT.
  * @return stack_abstract_graph.
  */
 protected function get_prt_graph($prt)
 {
     $graph = new stack_abstract_graph();
     foreach ($prt->get_nodes_summary() as $nodekey => $summary) {
         if ($summary->truenextnode == -1) {
             $left = null;
         } else {
             $left = $summary->truenextnode + 1;
         }
         if ($summary->falsenextnode == -1) {
             $right = null;
         } else {
             $right = $summary->falsenextnode + 1;
         }
         $graph->add_node($nodekey + 1, $left, $right, $summary->truescoremode . round($summary->truescore, 2), $summary->falsescoremode . round($summary->falsescore, 2));
     }
     $graph->layout();
     return $graph;
 }
 /**
  * Get a list of the PRT notes that should be present for a given PRT.
  * @param string $prtname the name of a PRT.
  * @return array list of nodes that should be present in the form definitino for this PRT.
  */
 protected function get_prt_graph($prtname)
 {
     if (array_key_exists($prtname, $this->prtgraph)) {
         return $this->prtgraph[$prtname];
     }
     // If the form has been submitted and is being redisplayed, and this is
     // an existing PRT, base things on the submitted data.
     $submitted = optional_param_array($prtname . 'truenextnode', null, PARAM_RAW);
     if ($submitted) {
         $truescoremode = optional_param_array($prtname . 'truescoremode', null, PARAM_RAW);
         $truescore = optional_param_array($prtname . 'truescore', null, PARAM_RAW);
         $falsenextnode = optional_param_array($prtname . 'falsenextnode', null, PARAM_RAW);
         $falsescoremode = optional_param_array($prtname . 'falsescoremode', null, PARAM_RAW);
         $falsescore = optional_param_array($prtname . 'falsescore', null, PARAM_RAW);
         $graph = new stack_abstract_graph();
         $deletednode = null;
         $lastkey = -1;
         foreach ($submitted as $key => $truenextnode) {
             if (optional_param($prtname . 'nodedelete' . $key, false, PARAM_BOOL)) {
                 // Slightly odd to register the button here, especially since
                 // now this node has been deleted, this button will not exist,
                 // but anyway this works, and in necessary to stop the form
                 // from being submitted.
                 $this->_form->registerNoSubmitButton($prtname . 'nodedelete' . $key);
                 // For deleted nodes, we add them to the tree anyway, and
                 // then remove them again below. We have to do it that way
                 // because we also need to delete links that point to the
                 // deleted node.
                 $deletednode = $key;
             }
             if ($truenextnode == -1 || !array_key_exists($truenextnode, $submitted)) {
                 $left = null;
             } else {
                 $left = $truenextnode + 1;
             }
             if ($falsenextnode[$key] == -1 || !array_key_exists($falsenextnode[$key], $submitted)) {
                 $right = null;
             } else {
                 $right = $falsenextnode[$key] + 1;
             }
             $graph->add_node($key + 1, $left, $right, $truescoremode[$key] . round($truescore[$key], 2), $falsescoremode[$key] . round($falsescore[$key], 2), '#fgroup_id_' . $prtname . 'node_' . $key);
             $lastkey = max($lastkey, $key);
         }
         if (optional_param($prtname . 'nodeadd', false, PARAM_BOOL)) {
             $graph->add_node($lastkey + 2, null, null, '+0', '-0', '#fgroup_id_' . $prtname . 'node_' . $lastkey + 1);
         }
         if (!is_null($deletednode)) {
             $graph->remove_node($deletednode + 1);
         }
         $graph->layout();
         $this->prtgraph[$prtname] = $graph;
         return $graph;
     }
     // Otherwise, if an existing question is being edited, and this is an
     // existing PRT, base things on the existing question definition.
     if (!empty($this->question->prts[$prtname]->nodes)) {
         $graph = new stack_abstract_graph();
         foreach ($this->question->prts[$prtname]->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, $node->truescoremode . round($node->truescore, 2), $node->falsescoremode . round($node->falsescore, 2), '#fgroup_id_' . $prtname . 'node_' . $node->nodename);
         }
         $graph->layout();
         $this->prtgraph[$prtname] = $graph;
         return $graph;
     }
     // Otherwise, it is a new PRT. Just one node.
     $graph = new stack_abstract_graph();
     $graph->add_node('1', null, null, '=1', '=0', '#fgroup_id_' . $prtname . 'node_0');
     $graph->layout();
     $this->prtgraph[$prtname] = $graph;
     return $graph;
 }
 /**
  * This graph has a link to a non-existent node. We verify that throws an exception.
  */
 public function test_get_suggested_node_names()
 {
     $graph = new stack_abstract_graph();
     $graph->add_node(1, 2, 3);
     $graph->add_node(2, 7, null);
     $graph->add_node(3, 2, 4);
     $graph->add_node(4, 5, 6);
     $graph->add_node(5, null, 9);
     $graph->add_node(6, null, null);
     $graph->add_node(7, null, null);
     $graph->add_node(9, null, null);
     $graph->layout();
     $newnames = $graph->get_suggested_node_names();
     $this->assertEquals(array(1 => 1, 3 => 2, 2 => 3, 7 => 4, 4 => 5, 5 => 6, 9 => 7, 6 => 8), $newnames);
 }
$graph3->add_node(3, 4, 4, '+0.1', '-0.1');
$graph3->add_node(4, null, null, '+0.1', '-0.1');
$graph4 = new stack_abstract_graph();
$graph4->add_node(1, 3, 2, '=1', '=0');
$graph4->add_node(2, 4, 4, '+0.1', '-0.1');
$graph4->add_node(3, 4, 4, '+0.1', '-0.1');
$graph4->add_node(4, null, null, '+0.1', '-0.1');
$graph5 = new stack_abstract_graph();
$graph5->add_node(1, 7, 2, '=1', '=0');
$graph5->add_node(2, 7, 3, '+0.1', '-0.1');
$graph5->add_node(3, 7, 4, '+0.1', '-0.1');
$graph5->add_node(4, 7, 5, '+0.1', '-0.1');
$graph5->add_node(5, 7, 6, '+0.1', '-0.1');
$graph5->add_node(6, 7, 7, '+0.1', '-0.1');
$graph5->add_node(7, null, null, '+0.1', '-0.1');
$graph6 = new stack_abstract_graph();
$graph6->add_node(7, 10, 8, '=1', '=0');
$graph6->add_node(8, null, 9, '+0.1', '-0.1');
$graph6->add_node(9, 10, 10, '+0.1', '-0.1');
$graph6->add_node(10, null, null, '+0.1', '-0.1');
$examples = array($broken, $broken2, $tree, $tree2, $tree3, $graph, $graph2, $graph3, $graph4, $graph5, $graph6);
foreach ($examples as $example) {
    $example->layout();
}
echo $OUTPUT->header();
echo $OUTPUT->heading('Example graphs');
$i = 0;
foreach ($examples as $example) {
    echo stack_abstract_graph_svg_renderer::render($example, 'example' . $i++);
}
echo $OUTPUT->footer();
예제 #6
0
 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);
 }
예제 #7
0
foreach ($trees as $qname => $tree) {
    $key = json_encode($tree);
    $uniquetrees[$key] = $tree;
    $qnamesused[$key][] = $qname;
    if (array_key_exists($key, $frequency)) {
        $frequency[$key] += 1;
    } else {
        $frequency[$key] = 1;
    }
}
arsort($frequency);
echo $OUTPUT->header();
$i = 0;
foreach ($frequency as $key => $count) {
    $uniquetree = $uniquetrees[$key];
    $tree = new stack_abstract_graph();
    foreach ($uniquetree as $node => $branches) {
        list($left, $right) = $branches;
        if ($left == -1) {
            $left = null;
        } else {
            $left += 1;
        }
        if ($right == -1) {
            $right = null;
        } else {
            $right += 1;
        }
        $tree->add_node($node + 1, $left, $right);
    }
    reset($uniquetree);
 /**
  * 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));
     }
 }