/**
  * Create the CAS context in which we will evaluate this PRT. This contains
  * all the question variables, student responses, feedback variables, and all
  * the sans, tans and atoptions expressions from all the nodes.
  *
  * @param stack_cas_session $questionvars the question varaibles.
  * @param stack_options $options
  * @param array $answers name => value the student response.
  * @param int $seed the random number seed.
  * @return stack_cas_session initialised with all the expressions this PRT will need.
  */
 protected function create_cas_context_for_evaluation($questionvars, $options, $answers, $seed)
 {
     // Start with the question variables (note that order matters here).
     $cascontext = clone $questionvars;
     // Set the value of simp from this point onwards.
     // If the question has simp:true, but the prt simp:false, then this needs to be done here.
     if ($this->simplify) {
         $simp = 'true';
     } else {
         $simp = 'false';
     }
     $cs = new stack_cas_casstring($simp);
     $cs->set_key('simp');
     $answervars = array($cs);
     // Add the student's responses, but only those needed by this prt.
     // Some irrelevant but invalid answers might break the CAS connection.
     foreach ($this->get_required_variables(array_keys($answers)) as $name) {
         if (array_key_exists($name . '_val', $answers)) {
             $cs = new stack_cas_casstring($answers[$name . '_val']);
         } else {
             $cs = new stack_cas_casstring($answers[$name]);
         }
         // Validating as teacher at this stage removes the problem of "allowWords" which
         // we don't have access to.  This effectively allows any words here.  But the
         // student's answer has already been through validation.
         $cs->get_valid('t');
         // Setting the key must come after validation.
         $cs->set_key($name);
         $answervars[] = $cs;
     }
     $cascontext->add_vars($answervars);
     // Add the feedback variables.
     $cascontext->merge_session($this->feedbackvariables);
     // Add all the expressions from all the nodes.
     // Note this approach does not allow for effective guard clauses in the PRT.
     // All the inputs to answer tests are evaluated at the start.
     foreach ($this->nodes as $key => $node) {
         $cascontext->add_vars($node->get_context_variables($key));
     }
     $cascontext->instantiate();
     return $cascontext;
 }
Пример #2
0
 /**
  * Once we know the random seed, we can initialise all the other parts of the question.
  */
 public function initialise_question_from_seed()
 {
     // Build up the question session out of all the bits that need to go into it.
     // 1. question variables.
     $questionvars = new stack_cas_keyval($this->questionvariables, $this->options, $this->seed, 't');
     $session = $questionvars->get_session();
     // 2. correct answer for all inputs.
     $response = array();
     foreach ($this->inputs as $name => $input) {
         $cs = new stack_cas_casstring($input->get_teacher_answer());
         $cs->get_valid('t');
         $cs->set_key($name);
         $response[$name] = $cs;
     }
     $session->add_vars($response);
     $sessionlength = count($session->get_session());
     // 3. CAS bits inside the question text.
     $questiontext = $this->prepare_cas_text($this->questiontext, $session);
     // 4. CAS bits inside the specific feedback.
     $feedbacktext = $this->prepare_cas_text($this->specificfeedback, $session);
     // 5. CAS bits inside the question note.
     $notetext = $this->prepare_cas_text($this->questionnote, $session);
     // 6. The standard PRT feedback.
     $prtcorrect = $this->prepare_cas_text($this->prtcorrect, $session);
     $prtpartiallycorrect = $this->prepare_cas_text($this->prtpartiallycorrect, $session);
     $prtincorrect = $this->prepare_cas_text($this->prtincorrect, $session);
     // Now instantiate the session.
     $session->instantiate();
     if ($session->get_errors()) {
         // We throw an exception here because any problems with the CAS code
         // up to this point should have been caught during validation when
         // the question was edited or deployed.
         throw new stack_exception('qtype_stack_question : CAS error when instantiating the session: ' . $session->get_errors($this->user_can_edit()));
     }
     // Finally, store only those values really needed for later.
     $this->questiontextinstantiated = $questiontext->get_display_castext();
     $this->specificfeedbackinstantiated = $feedbacktext->get_display_castext();
     $this->questionnoteinstantiated = $notetext->get_display_castext();
     $this->prtcorrectinstantiated = $prtcorrect->get_display_castext();
     $this->prtpartiallycorrectinstantiated = $prtpartiallycorrect->get_display_castext();
     $this->prtincorrectinstantiated = $prtincorrect->get_display_castext();
     $session->prune_session($sessionlength);
     $this->session = $session;
     // Allow inputs to update themselves based on the model answers.
     $this->adapt_inputs();
 }
Пример #3
0
 /**
  * Create the actual response data. The response data in the test case may
  * include expressions in terms of the question variables.
  * @param qtype_stack_question $question the question - with $question->session initialised.
  * @return array the respones to send to $quba->process_action.
  */
 public static function compute_response(qtype_stack_question $question, $inputs)
 {
     // If the question has simp:false, then the local options should reflect this.
     // In this case, test constructors (question authors) will need to explicitly simplify their test case constructions.
     $localoptions = clone $question->options;
     // Start with the question variables (note that order matters here).
     $cascontext = new stack_cas_session(null, $localoptions, $question->seed);
     $question->add_question_vars_to_session($cascontext);
     // Turn off simplification - we *always* need test cases to be unsimplified, even if the question option is true.
     $vars = array();
     $cs = new stack_cas_casstring('false');
     $cs->set_key('simp');
     $vars[] = $cs;
     // Now add the expressions we want evaluated.
     foreach ($inputs as $name => $value) {
         if ('' !== $value) {
             $cs = new stack_cas_casstring($value);
             if ($cs->get_valid('t')) {
                 $cs->set_key('testresponse_' . $name);
                 $vars[] = $cs;
             }
         }
     }
     $cascontext->add_vars($vars);
     $cascontext->instantiate();
     $response = array();
     foreach ($inputs as $name => $notused) {
         $computedinput = $cascontext->get_value_key('testresponse_' . $name);
         // In the case we start with an invalid input, and hence don't send it to the CAS
         // We want the response to constitute the raw invalid input.
         // This permits invalid expressions in the inputs, and to compute with valid expressions.
         if ('' == $computedinput) {
             $computedinput = $inputs[$name];
         }
         if (array_key_exists($name, $question->inputs)) {
             $response = array_merge($response, $question->inputs[$name]->maxima_to_response_array($computedinput));
         }
     }
     return $response;
 }
 /**
  * Get the context variables that this node uses, so that they can be
  * pre-evaluated prior to transversing the tree.
  * @param string $key used to make the variable names unique to this node.
  * @return array of stack_cas_casstring
  */
 public function get_context_variables($key)
 {
     $variables = array();
     $this->sans->set_key('PRSANS' . $key);
     $variables[] = $this->sans;
     $this->tans->set_key('PRTANS' . $key);
     $variables[] = $this->tans;
     if ($this->process_atoptions()) {
         $atopts = new stack_cas_casstring($this->atoptions);
         $atopts->get_valid('t', false, 0);
         $atopts->set_key('PRATOPT' . $key);
         $variables[] = $atopts;
     }
     return $variables;
 }
Пример #5
0
 /**
  * Extract the CAS commands from the string
  *
  * @access public
  * @return bool false if no commands to extract, true if succeeds.
  */
 private function extract_cas_commands()
 {
     // First check contains @s.
     $count = preg_match_all('~(?<!@)@(?!@)~', $this->trimmedcastext, $notused);
     if ($count == 0) {
         // Nothing to do.
         return null;
     } else {
         // Extract the CAS commands.
         $temp = stack_utils::all_substring_between($this->trimmedcastext, '@', '@', true);
         // Create array of commands matching with their labels.
         $i = 0;
         $valid = true;
         $errors = '';
         $cmdarray = array();
         $labels = array();
         $sessionkeys = array();
         if (is_a($this->session, 'stack_cas_session')) {
             $sessionkeys = $this->session->get_all_keys();
         }
         foreach ($temp as $cmd) {
             // Trim of surrounding white space and CAS commands.
             $cmd = stack_utils::trim_commands($cmd);
             $cs = new stack_cas_casstring($cmd);
             $cs->get_valid($this->security, $this->syntax, $this->insertstars);
             do {
                 // ... make sure names are not already in use.
                 $key = 'caschat' . $i;
                 $i++;
             } while (in_array($key, $sessionkeys));
             $sesionkeys[] = $key;
             $labels[] = $key;
             $cs->set_key($key, true);
             $cmdarray[] = $cs;
             $valid = $valid && $cs->get_valid();
             $errors .= $cs->get_errors();
         }
         if (!$valid) {
             $this->valid = false;
             $this->errors .= stack_string('stackCas_invalidCommand') . '</br>' . $errors;
         }
         if (!empty($cmdarray)) {
             $newsession = $this->session;
             if (null === $newsession) {
                 $newsession = new stack_cas_session($cmdarray, null, $this->seed);
             } else {
                 $newsession->add_vars($cmdarray);
             }
             $this->session = $newsession;
             // Now replace the commannds with their labels in the text.
             $this->trimmedcastext = stack_utils::replace_between($this->trimmedcastext, '@', '@', $labels, true);
         }
     }
 }