Quiz Item Analysis of Multianswers Question Types

From MoodleDocs
Jump to: navigation, search

This page described a code proposal to access the embedded questions data in multianswers questiion types so that they can be displayed in the Item analysis report. See http://moodle.org/mod/forum/discuss.php?d=86598#387154

The code is experimental and need more testing and is not optimized.

Contents

All question types to be analyzed contain at least one embedded question

If we set this as a general framework, the code will be valid for all question types.

We need a question type function to retrieve the embedded questions

There is actually no question type function to know the number the embedded questions in a question. The default function number_of_embedded_questions() returns 1;

This is not the same as  actual_number_of_questions().

We need a convention of how to store the responses from the different embedded questions

The same convention should be used in the $session states and in the get_responses function. The actual responses convention used an array to contains the different answers and put in [''] element the actual response. an example for a numerical question with the tolerance limit

stdClass Object
(
   [id] => 2312
   [responses] => Array
       (
           [6837] => stdClass Object
               (
                   [answer] => 1822 (1817..1827)
                   [credit] => 1
               )
       )
)

In a multianswers qtype the different answers responses are stored in an array as.

stdClass Object
(
   [id] => 2815
   [responses] => Array
       (
            [1] => stdClass Object
               (
                   [id] => 2816
                   [responses] => Array
                       (
                           [7300] => stdClass Object
                               (
                                   [answer] => Wrong answer
                                   [credit] => 0
                               )
                           [7302] => stdClass Object
                               (
                                   [answer] => Correct answer
                                   [credit] => 1
                               )
                           [7305] => stdClass Object
                               (
                                   [answer] => Answer that gives half the credit
                                   [credit] => 0.5
                               )
                       )
               )
           [2] => stdClass Object
               (
                   [id] => 2817
                   [responses] => Array
                       (
                           [7303] => stdClass Object
                               (
                                   [answer] => Wrong answer
                                   [credit] => 0
                               )
                           [7304] => stdClass Object
                               (
                                   [answer] => Correct answer
                                   [credit] => 1
                               )
                       )
               )
           [3] => stdClass Object
               (
                   [id] => 2818
                   [responses] => Array
                       (
                           [7307] => stdClass Object
                               (
                                   [answer] => 65 (64.9..65.1)
                                   [credit] => 1
                               )
                       )
               )
       )
)

So the one question question types responses is tranformed in a similar structure.

The upper example becomes

stdClass Object
(
   [id] => 2312
   [responses] => Array
       (
           [0] => stdClass Object
               (
                   [id] => 2312
                   [responses] => Array
                       (
                           [6837] => stdClass Object
                               (
                                   [answer] => 1822 (1817..1827)
                                   [credit] => 1
                               )
                       )
               )
       )
)

The new array elements the defined the responses of the embedded questions and are put in the questions array as subquestions

                 foreach ($q->responses as $subquestion => $qresponses ){ 
                   foreach ($qresponses->responses as $answer => $r) {
                       $r->count = 0;
                       $questions[$qid]['subquestion'][$subquestion]['responses'][$answer] = $r->answer;
                       $questions[$qid]['subquestion'][$subquestion]['rcounts'][$answer] = 0;
                       $questions[$qid]['subquestion'][$subquestion]['credits'][$answer] = $r->credit;
                       $statsrow[$qid]['subquestion'][$subquestion] = 0;
                   }

For a 1 embedded question question type there is just one subquestion with a $subquestion value of 0.

Check response of each subquestion instead of each question

  foreach ($responses as $subquestion => $resp ){ 
      if ($resp) {
          if ($key = array_search($resp, $questions[$qid]['subquestion'][$subquestion]['responses'])) {                            
               $questions[$qid]['subquestion'][$subquestion]['rcounts'][$key]++;
           } else {
                $test = new stdClass;
                $test->responses =  $QTYPES[$quizquestions[$i]->qtype]->get_correct_responses($quizquestions[$i], $states[$i]);
                $test->resp = $resp ;
                $test->keyresp = $subquestion ;
                $test->question_qid = &$questions[$qid] ;
                if ($key = $QTYPES[$quizquestions[$i]->qtype]->check_response($quizquestions[$i], $states[$i], $test)) 

The check_response of a multianswer question type will use the $subquestion to check the $test->resp agains the correct $states response as the $subquestion values where obtained from the multianswer question. The only requirement is that the multianswer use the same convention in get_all_responses that it uses to create the $states data. For one embeded question types the $subquestion ( 0) is ignored as there is no array in the $states data. The result is stored using the same subquestion convention

{
    $questions[$qid]['subquestion'][$subquestion]['rcounts'][$key]++;
 } else {
                               $questions[$qid]['subquestion'][$subquestion]['responses'][] = $resp;
                               $questions[$qid]['subquestion'][$subquestion]['rcounts'][] = 1;
                               $questions[$qid]['subquestion'][$subquestion]['credits'][] = 0;
                           }
                       }
                   }
               }

The multianswer should divide the question text in each embedded questions

We need a new questiontype function that will format the question text and return it in an array of the emmbedded questions text ordered as the responses. The default for 1 embedded question is an unique array element.

$qtext1 = $QTYPES[$question->qtype]->print_question_formulation($question, $format_options, $quiz->course) ;

that is used as

$qtext = $qquestion = $qname."\n".$qtext1[0]."\n";

for the first line of a question display. A typical output for a multianswers

Array
(
   [0] => This question consists of some text with an answer embedded right here 
   [1] => .Then you will have to deal with this short answer 
   [2] =>  and finally we have a floating point number age 
)

The table is built also on the subquestion structure

    foreach ($q['subquestion']as $key => $qr ){
           foreach ($qr['responses'] as $aid=>$resp){
               $response = new stdClass;                
               if ($qr['credits'][$aid] <= 0) {
                   $qclass = 'uncorrect';
               } elseif ($qr['credits'][$aid] == 1) {
                   $qclass = 'correct';
               } else {
                   $qclass = 'partialcorrect';
               }
               $response->credit = '('.format_float($qr['credits'][$aid],2).') ';
               $response->text = ''.format_text($resp, FORMAT_MOODLE, $format_options, $quiz->course).' ';
               $count = $qr['rcounts'][$aid].'/'.$q['count'];
               $response->rcount = $count;
               $response->rpercent =  '('.format_float($qr['rcounts'][$aid]/$q['count']*100,0).'%)';
               $responses[$subindex][] = $response;
           }
           $subindex++;
       }
           $facility = format_float($q['facility']*100,0)."%";
           $qsd = format_float($q['qsd'],3);
           $di = format_float($q['disc_index'],2);
           $dc = format_float($q['disc_coeff'],2);
           $response = array_shift($responses[0]);
           $table->add_data(array($qnumber."\n
".$qicon."\n ".$qreview, $qquestion, $response->text, $response->credit, $response->rcount, $response->rpercent, $facility, $qsd, $di, $dc)); foreach($responses[0] as $response) { $table->add_data(array(, , $response->text, $response->credit, $response->rcount, $response->rpercent, , , , )); } if($subindex >1) { for($i = 1 ; $i < $subindex ; $i++){ $response = array_shift($responses[$i]); $table->add_data(array(, $qtext1[$i], $response->text, $response->credit, $response->rcount, $response->rpercent, , , , )); foreach($responses[$i] as $response) { $table->add_data(array(, , $response->text, $response->credit, $response->rcount, $response->rpercent, , , , )); } } } }

A simple example

Item analysis example.jpg

Personal tools
User docs (English)