[system] / trunk / wwmoodle / wwquestion / questiontype.php Repository:
ViewVC logotype

View of /trunk/wwmoodle/wwquestion/questiontype.php

Parent Directory Parent Directory | Revision Log Revision Log


Revision 5156 - (download) (as text) (annotate)
Fri Jul 13 06:57:44 2007 UTC (12 years, 7 months ago) by mleventi
File size: 16660 byte(s)
Added support for dropdown fields in webwork questions

    1 <?php
    2 
    3 require_once("$CFG->libdir/soap/nusoap.php");
    4 
    5 require_once("htmlparser.php");
    6 
    7 //Path to the WSDL file on the Webwork Server
    8 define('PROBLEMSERVER_WSDL','http://128.151.231.20/problemserver_wsdl/');
    9 
   10 //Display Mode
   11 define('PROBLEMSERVER_DISPLAYMODE','images');
   12 
   13 
   14 /**
   15  * The question type class for the webwork question type.
   16  *
   17  * @copyright &copy; 2007 Matthew Leventi
   18  * @author mleventi@gmail.com
   19  * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
   20  * @package webwork_qtype
   21  *
   22 **/
   23 
   24 /**
   25  * The webwork question class
   26  *
   27  * Allows webwork questions to be used in Moodle through a new question type.
   28  */
   29 class webwork_qtype extends default_questiontype {
   30 
   31     function name() {
   32         return 'webwork';
   33     }
   34 
   35     /**
   36      * @desc Retrieves the seed and decoded code out of the question_webwork table.
   37      * @return boolean to indicate success of failure.
   38      */
   39     function get_question_options(&$question) {
   40         // TODO code to retrieve the extra data you stored in the database into
   41         // $question->options.
   42         if (!$record = get_record('question_webwork', 'question', $question->id)) {
   43             notify('Error: Missing question options!');
   44             return false;
   45         }
   46         $question->seed = $record->seed;
   47         $question->code = base64_decode($record->code);
   48         return true;
   49     }
   50 
   51     /**
   52      * @desc Saves the webwork question code and default seed setting into question_webwork
   53      * @return boolean to indicate success of failure.
   54      */
   55     function save_question_options($question) {
   56         // TODO code to save the extra data to your database tables from the
   57         // $question object, which has all the post data from editquestion.html
   58         // Save question options in question_webwork table
   59         if ($record = get_record("question_webwork", "question", $question->id)) {
   60             // No need to do anything, since the answer IDs won't have changed
   61             // But we'll do it anyway, just for robustness
   62             $record->code = base64_encode(stripslashes($question->code));
   63             $record->seed = $question->seed;
   64             if (!update_record("question_webwork", $record)) {
   65                 $result->error = "Could not update quiz webwork options! (id=$record->id)";
   66                 return $result;
   67             }
   68         } else {
   69             unset($record);
   70             $record->question    = $question->id;
   71             $record->code = base64_encode(stripslashes($question->code));
   72             $record->seed = $question->seed;
   73             if (!insert_record("question_webwork", $record)) {
   74                 $result->error = "Could not insert quiz webwork options!";
   75                 return $result;
   76             }
   77         }
   78         return true;
   79     }
   80 
   81     /**
   82      * @desc Deletes question from the question_webwork table
   83      * @param integer $questionid The question being deleted
   84      * @return boolean to indicate success of failure.
   85      */
   86     function delete_question($questionid) {
   87         delete_records("question_webwork", "question", $questionid);
   88         return true;
   89     }
   90 
   91     /**
   92     * @desc Creates an empty response before student answers a question. This contains the possibly randomized seed for that particular student. Sticky seeds are created here.
   93     */
   94     function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) {
   95         //if question seed is 0, we want a random seed for the student
   96         if($question->seed == "0") {
   97             srand(time());
   98             $random = rand(0,1000);
   99             $state->responses['seed'] = $random;
  100         } else {
  101             $state->responses['seed'] = $question->seed;
  102         }
  103         return true;
  104     }
  105 
  106     /**
  107     * @desc Decodes and unserializes a students response into the response array carried by state
  108     */
  109     function restore_session_and_responses(&$question, &$state) {
  110         $serializedresponse = $state->responses[''];
  111         $serializedresponse = base64_decode($serializedresponse);
  112         $responses = unserialize($serializedresponse);
  113         $state->responses = $responses;
  114         return true;
  115     }
  116 
  117     /**
  118     * @desc Serialize, encodes and inserts a students response into the question_states table.
  119     */
  120     function save_session_and_responses(&$question, &$state) {
  121         // TODO package up the students response from the $state->responses
  122         // array into a string and save it in the question_states.answer field.
  123         $responses = $state->responses;
  124         $serialized = serialize($responses);
  125         $serialized = base64_encode($serialized);
  126         return set_field('question_states', 'answer', $serialized, 'id', $state->id);
  127     }
  128 
  129     /**
  130     * @desc Prints the question. Calls the Webwork Server for appropriate HTML output and image paths.
  131     */
  132     function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
  133         global $CFG;
  134         $readonly = empty($options->readonly) ? '' : 'disabled="disabled"';
  135 
  136         //Formulate question image and text
  137         $questiontext = $this->format_text($question->questiontext,
  138                 $question->questiontextformat, $cmoptions);
  139         $image = get_question_image($question, $cmoptions->course);
  140 
  141         //FIXME the problem code comes into this function unencoded, need encoding for transport
  142         $code = base64_encode($question->code);
  143 
  144         //Get previous answers to send to the server
  145         $answerarray = array();
  146         foreach($state->responses as $key => $value) {
  147             array_push($answerarray, array('field' => $key, 'answer'=> $value));
  148         }
  149 
  150         //echo "PRINT" . $state->responses['seed'];
  151         //echo "PRINT" . var_dump($state);
  152         $params = array('request' => array(
  153             'id' => '5',
  154             'code' => $code,
  155             'seed' => $state->responses['seed'],
  156             'answers' => $answerarray,
  157             'displayMode' => PROBLEMSERVER_DISPLAYMODE
  158         ));
  159         $client = new problemserver_client();
  160         $response = $client->handler('renderProblem',$params);
  161         $unparsedhtml = base64_decode($response['body_text']);
  162         $problemhtml = "";
  163         //new array keyed by field
  164         $fieldhash = array();
  165         //put the answer response from the server into state
  166         $answerfields = $response['answers'];
  167         foreach($answerfields as $answerobj) {
  168             $state->responses[$answerobj['field']] = $answerobj['answer'];
  169             $fieldhash[$answerobj['field']] = $answerobj;
  170         }
  171 
  172         $parser = new HtmlParser($unparsedhtml);
  173         $currentselect = "";
  174         while($parser->parse()) {
  175             //change some attributes of html tags for moodle compliance
  176             if ($parser->iNodeType == NODE_TYPE_ELEMENT) {
  177                 $nodename = $parser->iNodeName;
  178                 $name = $parser->iNodeAttributes['name'];
  179                 //handle generic change of node's attribute name
  180                 if(($nodename == "INPUT") || ($nodename == "SELECT") || ($nodename == "TEXTAREA")) {
  181                     $parser->iNodeAttributes['name'] = 'resp' . $question->id . '_' . $name;
  182                     if(($state->event == QUESTION_EVENTGRADE) && (isset($fieldhash[$name]))) {
  183                         $parser->iNodeAttributes['class'] = $parser->iNodeAttributes['class'] . question_get_feedback_class($fieldhash[$name]['score']);
  184                     }
  185                 }
  186                 //handle specific change
  187                 if($nodename == "INPUT") {
  188                     //put submitted value into field
  189                     if(isset($fieldhash[$name])) {
  190                         $parser->iNodeAttributes['value'] = $fieldhash[$name]['answer'];
  191                     }
  192                 } else if($nodename == "SELECT") {
  193                     $currentselect = $name;
  194                 } else if($nodename == "OPTION") {
  195                     if($parser->iNodeAttributes['value'] == $fieldhash[$currentselect]['answer'])
  196                         $parser->iNodeAttributes['selected'] = '1';
  197                 } else if($nodename == "TEXTAREA") {
  198                 }
  199             }
  200             $problemhtml .= $parser->printTag();
  201         }
  202 
  203         //for the seed form field
  204         $qid = $question->id;
  205         $seed = $state->responses['seed'];
  206 
  207         include("$CFG->dirroot/question/type/webwork/display.html");
  208     }
  209 
  210     /**
  211     * @desc Assigns a grade for a student response. Currently a percentage right/total questions. Calls the Webwork Server to evaluate answers
  212     */
  213     function grade_responses(&$question, &$state, $cmoptions) {
  214         // TODO assign a grade to the response in state.
  215         //get code
  216         //echo "GRADE";
  217         //var_dump($state);
  218         $code = base64_encode($question->code);
  219         //get answers
  220         $answerarray = array();
  221         foreach($state->responses as $key => $value) {
  222             array_push($answerarray, array('field' => $key, 'answer'=> $value));
  223         }
  224 
  225         $params = array('request' => array(
  226             'id' => '5',
  227             'code' => $code,
  228             'seed' => $state->responses['seed'],
  229             'answers' => $answerarray,
  230             'displayMode' => PROBLEMSERVER_DISPLAYMODE
  231         ));
  232         $client = new problemserver_client();
  233         //var_dump($params);
  234 
  235         $response = $client->handler('renderProblem',$params);
  236 
  237         $answers = $response['answers'];
  238         $state->raw_grade = 0;
  239         $total = 0;
  240         $num = 0;
  241         foreach($answers as $answer) {
  242             $total += $answer['score'];
  243             $num++;
  244         }
  245         if($num != 0) {
  246             $state->raw_grade = $total / $num;
  247         }
  248 
  249         // Apply the penalty for this attempt
  250         $state->penalty = $question->penalty * $question->maxgrade;
  251 
  252         // mark the state as graded
  253         $state->event = ($state->event ==  QUESTION_EVENTCLOSE) ? QUESTION_EVENTCLOSEANDGRADE : QUESTION_EVENTGRADE;
  254         return true;
  255         //var_dump($state);
  256     }
  257 
  258     /**
  259     * @desc Comparison of two student responses for the same question. Checks based on seed equality, and response equality.
  260     * Perhaps we could add check on evaluated answer (depends on whether the server is called before this function)
  261     */
  262     function compare_responses($question, $state, $teststate) {
  263         if(sizeof($state->responses) != sizeof($teststate->responses)) {
  264             return false;
  265         }
  266         //check values are equal
  267         foreach($state->responses as $key => $value) {
  268             if($value != $teststate->responses[$key]) {
  269                 return false;
  270             }
  271         }
  272         return true;
  273     }
  274 
  275     /**
  276     * @desc Gets the correct answers from the server for the seed in state. Places them into the state->responses array.
  277     */
  278     function get_correct_responses(&$question, &$state) {
  279 
  280         $code = base64_encode($question->code);
  281         //var_dump($state);
  282         $params = array('request' => array(
  283             'id' => '5',
  284             'code' => $code,
  285             'seed' => $state->responses['seed'],
  286             'answers' => array(array()),
  287             'displayMode' => PROBLEMSERVER_DISPLAYMODE
  288         ));
  289         $client = new problemserver_client();
  290         //var_dump($params);
  291         $response = $client->handler('renderProblem',$params);
  292 
  293         $answers = $response['answers'];
  294         $ret = array();
  295         foreach ($answers as $answer) {
  296             $ret[$answer['field']] = $answer['correct'];
  297         }
  298         //push the seed onto the answer array, keep track of what seed these are for.
  299         $ret['seed'] = $state->responses['seed'];
  300         return $ret;
  301     }
  302 
  303     /**
  304     * @desc Prints a short 40 character limited version of all the answers for a question.
  305     */
  306     function get_actual_response($question, $state) {
  307         // TODO
  308         $temp = '';
  309         $i = 1;
  310         foreach($state->responses as $key => $value) {
  311             if($key != 'seed') {
  312                 $temp .= "$i) " . $value . " ";
  313                 $i++;
  314             }
  315         }
  316         $lmax = 40;
  317         $responses[] = (strlen($temp) > $lmax) ? substr($temp, 0, $lmax).'...' : $temp;
  318         return $responses;
  319     }
  320 
  321     /**
  322      * Backup the data in the question
  323      *
  324      * This is used in question/backuplib.php
  325      */
  326     function backup($bf,$preferences,$question,$level=6) {
  327 
  328         $status = true;
  329 
  330         $webworks = get_records('question_webwork', 'question', $question, 'id ASC');
  331         //If there are webworks
  332         if ($webworks) {
  333             //Iterate over each webwork
  334             foreach ($webworks as $webwork) {
  335                 $status = fwrite ($bf,start_tag("WEBWORK",$level,true));
  336                 //Print webwork contents
  337                 fwrite ($bf,full_tag("CODE",$level+1,false,$webwork->code));
  338                 fwrite ($bf,full_tag("SEED",$level+1,false,$webwork->seed));
  339                 $status = fwrite ($bf,end_tag("WEBWORK",$level,true));
  340             }
  341             //Now print question_webwork
  342             $status = question_backup_answers($bf,$preferences,$question);
  343         }
  344         return $status;
  345     }
  346 
  347     /**
  348      * Restores the data in the question
  349      *
  350      * This is used in question/restorelib.php
  351      */
  352      function restore($old_question_id,$new_question_id,$info,$restore) {
  353 
  354         $status = true;
  355 
  356         //Get the shortanswers array
  357         $webworks = $info['#']['WEBWORK'];
  358 
  359         //Iterate over shortanswers
  360         for($i = 0; $i < sizeof($webworks); $i++) {
  361             $webwork_info = $webworks[$i];
  362 
  363             //Now, build the question_shortanswer record structure
  364             $webwork = new stdClass;
  365             $webwork->question = $new_question_id;
  366             $webwork->code = backup_todb($webwork_info['#']['CODE']['0']['#']);
  367             $webwork->seed = backup_todb($webwork_info['#']['SEED']['0']['#']);
  368 
  369             //The structure is equal to the db, so insert the question_shortanswer
  370             $newid = insert_record("question_webwork",$webwork);
  371 
  372             //Do some output
  373             if (($i+1) % 50 == 0) {
  374                 if (!defined('RESTORE_SILENTLY')) {
  375                     echo ".";
  376                     if (($i+1) % 1000 == 0) {
  377                         echo "<br />";
  378                     }
  379                 }
  380                 backup_flush(300);
  381             }
  382 
  383             if (!$newid) {
  384                 $status = false;
  385             }
  386         }
  387         return $status;
  388     }
  389 }
  390 
  391 /**
  392 * @desc Singleton class that contains function for communication to the server.
  393 */
  394 class problemserver_client {
  395         var $client;
  396         /**
  397          * @desc Constructs a singleton problemserver_client.
  398          */
  399         function problemserver_client() {
  400             // static associative array containing the real objects, key is classname
  401             static $instances=array();
  402             // get classname
  403             $class = get_class($this);
  404             if (!array_key_exists($class, $instances)) {
  405                 // does not yet exist, save in array
  406                 $this->client = new soap_client(PROBLEMSERVER_WSDL,'wsdl');
  407                 $err = $this->client->getError();
  408                 if ($err) {
  409                     print_error($err . " " . get_string('error_client_construction','qtype_webwork'));
  410                 }
  411                 $instances[$class] = $this;
  412             }
  413             foreach (get_class_vars($class) as $var => $value) {
  414                 $this->$var =& $instances[$class]->$var;
  415             }
  416         }
  417 
  418         /**
  419          *@desc Calls a SOAP function and passes (authenkey,course) automatically in the parameter list.
  420          *@param string $functioncall The function to call
  421          *@param array $params The parameters to the function.
  422          *@return Result of the soap function.
  423          */
  424         function handler($functioncall,$params=array()) {
  425             if(!is_array($params)) {
  426                 $params = array($params);
  427             }
  428             $result = $this->client->call($functioncall,$params);
  429             //$result = call_user_func_array(array(&$this->client,$functioncall),$params);
  430             if($err = $this->client->getError()) {
  431                 //print_error(get_string("rpc_fault","wwassignment') . " " . $functioncall. " ". $err);
  432                 print_error($err . " " . get_string('error_client_call','qtype_webwork'));
  433             }
  434             return $result;
  435         }
  436 }
  437 
  438 
  439 
  440 // Register this question type with the system.
  441 question_register_questiontype(new webwork_qtype());
  442 ?>

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9