[LON-CAPA-cvs] cvs: loncom /interface/statistics lonproblemanalysis.pm

matthew lon-capa-cvs@mail.lon-capa.org
Fri, 24 Oct 2003 13:38:06 -0000


This is a MIME encoded message

--matthew1067002686
Content-Type: text/plain

matthew		Fri Oct 24 09:38:06 2003 EDT

  Modified files:              
    /loncom/interface/statistics	lonproblemanalysis.pm 
  Log:
  Reorganization of code, some subroutines renamed.  Goal is to make the
  implementation of new types of problem analysis easier.
  
  
--matthew1067002686
Content-Type: text/plain
Content-Disposition: attachment; filename="matthew-20031024093806.txt"

Index: loncom/interface/statistics/lonproblemanalysis.pm
diff -u loncom/interface/statistics/lonproblemanalysis.pm:1.45 loncom/interface/statistics/lonproblemanalysis.pm:1.46
--- loncom/interface/statistics/lonproblemanalysis.pm:1.45	Tue Oct 21 17:31:35 2003
+++ loncom/interface/statistics/lonproblemanalysis.pm	Fri Oct 24 09:38:06 2003
@@ -1,6 +1,6 @@
 # The LearningOnline Network with CAPA
 #
-# $Id: lonproblemanalysis.pm,v 1.45 2003/10/21 21:31:35 matthew Exp $
+# $Id: lonproblemanalysis.pm,v 1.46 2003/10/24 13:38:06 matthew Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -43,6 +43,34 @@
                   '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66',
                   ]; 
 
+my @SubmitButtons = ({ name => 'ProblemAnalyis',
+                       text => 'Analyze Problem Again' },
+                     { name => 'ClearCache',
+                       text => 'Clear Caches' },
+                     { name => 'updatecaches',
+                       text => 'Update Student Data' },
+                     { name => 'SelectAnother',
+                       text => 'Choose a different resource' },
+                     { name => 'ExcelOutput',
+                       text => 'Produce Excel Output' });
+
+sub render_resource {
+    my ($resource) = @_;
+    ##
+    ## Render the problem
+    my $base;
+    ($base,undef) = ($resource->{'src'} =~ m|(.*/)[^/]*$|);
+    $base = "http://".$ENV{'SERVER_NAME'}.$base;
+    my $rendered_problem = 
+        &Apache::lonnet::ssi_body($resource->{'src'});
+    $rendered_problem =~ s/<\s*form\s*/<nop /g;
+    $rendered_problem =~ s|(<\s*/form\s*>)|<\/nop>|g;
+    return '<table bgcolor="ffffff"><tr><td>'.
+        '<base href="'.$base.'" />'.
+        $rendered_problem.
+        '</td></tr></table>';
+}
+
 sub BuildProblemAnalysisPage {
     my ($r,$c)=@_;
     $r->print('<h2>'.&mt('Option Response Problem Analysis').'</h2>');
@@ -50,6 +78,11 @@
     #
     my @Students = @Apache::lonstatistics::Students;
     #
+    if (@Students < 1) {
+        $r->print('<h2>There are no students in the sections selected</h2>');
+    }
+    #
+    &Apache::loncoursedata::clear_internal_caches();
     if (exists($ENV{'form.ClearCache'}) || 
         exists($ENV{'form.updatecaches'}) ||
         (exists($ENV{'form.firstanalysis'}) &&
@@ -62,94 +95,34 @@
         $r->print('<input type="hidden" name="firstanalysis" value="no" />');
     }
     $r->rflush();
+    #
     if (exists($ENV{'form.problemchoice'}) && 
         ! exists($ENV{'form.SelectAnother'})) {
-        $r->print('<input type="submit" name="ProblemAnalysis" value="'.
-                  &mt('Analyze Problem Again').'" />');
-        $r->print('&nbsp;'x5);
-        $r->print('<input type="submit" name="ClearCache" value="'.
-                  &mt('Clear Caches').'" />');
-        $r->print('&nbsp;'x5);
-        $r->print('<input type="submit" name="updatecaches" value="'.
-                  &mt('Update Student Data').'" />');
-        $r->print('&nbsp;'x5);
+        foreach my $button (@SubmitButtons) {
+            $r->print('<input type="submit" name="'.$button->{'name'}.'" '.
+                      'value="'.&mt($button->{'text'}).'" />');
+            $r->print('&nbsp;'x5);
+        }
         $r->print('<input type="hidden" name="problemchoice" value="'.
                   $ENV{'form.problemchoice'}.'" />');
-        $r->print('<input type="submit" name="SelectAnother" value="'.
-                  &mt('Choose a different resource').'" />');
-        $r->print('&nbsp;'x5);
-        $r->print('<input type="submit" name="ExcelOutput" value="'.
-                  &mt('Produce Excel Data Sheet').'" />');
-        $r->print('&nbsp;'x5);
         #
         $r->print('<hr />');
         #
         my ($symb,$part,$resid) = &get_problem_symb(
-                     &Apache::lonnet::unescape($ENV{'form.problemchoice'})
-                                           );
+                  &Apache::lonnet::unescape($ENV{'form.problemchoice'}));
         $r->rflush();
         #
         my $resource = &get_resource_from_symb($symb);
-        if (defined($resource)) {
+        if (! defined($resource)) {
+            $r->print('resource is undefined');
+        } else {
             $r->print('<h1>'.$resource->{'title'}.'</h1>');
             $r->print('<h3>'.$resource->{'src'}.'</h3>');
             $r->rflush();
             my %Data = &get_problem_data($resource->{'src'});
-            my $PerformanceData = 
-                &Apache::loncoursedata::get_optionresponse_data
-                                           (\@Students,$symb,$resid);
-            my $ORdata = $Data{$part.'.'.$resid};
-            if (exists($ENV{'form.ExcelOutput'}) && 
-                defined($PerformanceData)) {
-                my $result = &prepare_excel_sheet($r,$resource,
-                                                  $PerformanceData,$ORdata);
-                $r->print($result);
-                $r->rflush();
-            }
-            ##
-            ## Render the problem
-            my $base;
-            ($base,undef) = ($resource->{'src'} =~ m|(.*/)[^/]*$|);
-            $base = "http://".$ENV{'SERVER_NAME'}.$base;
-            my $rendered_problem = 
-                &Apache::lonnet::ssi_body($resource->{'src'});
-            $rendered_problem =~ s/<\s*form\s*/<nop /g;
-            $rendered_problem =~ s|(<\s*/form\s*>)|<\/nop>|g;
-            $r->print('<table bgcolor="ffffff"><tr><td>'.
-                      '<base href="'.$base.'" />'.
-                      $rendered_problem.
-                      '</td></tr></table>');
-            $r->rflush();
-            if (! exists($ENV{'form.ExcelOutput'})) {
-                ##
-                ## Analyze the problem
-                if (defined($PerformanceData) && 
-                    ref($PerformanceData) eq 'ARRAY') {
-                    if ($ENV{'form.AnalyzeOver'} eq 'Tries') {
-                        my $analysis_html = &tries_analysis($r,
-                                                            $PerformanceData,
-                                                            $ORdata);
-                        $r->print($analysis_html);
-                        $r->rflush();
-                    } elsif ($ENV{'form.AnalyzeOver'} eq 'Time') {
-                        my $analysis_html = &time_analysis($PerformanceData,
-                                                           $ORdata);
-                        $r->print($analysis_html);
-                        $r->rflush();
-                    } else {
-                        $r->print('<h2>'.
-                                  &mt('The analysis you have selected is '.
-                                      'not supported at this time').
-                                  '</h2>');
-                    }
-                } else {
-                    $r->print('<h2>'.
-                         &mt('There is no student data for this problem.').
-                              '</h2>');
-                }
-            }
-        } else {
-            $r->print('resource is undefined');
+            my $ProblemData = $Data{$part.'.'.$resid};
+            &OptionResponseAnalysis($r,$resource,$resid,$ProblemData,
+                                    \@Students);
         }
         $r->print('<hr />');
     } else {
@@ -157,154 +130,63 @@
                   &mt('Analyze Problem').'" />');
         $r->print('&nbsp;'x5);
         $r->print('<h3>'.&mt('Please select a problem to analyze').'</h3>');
-        $r->print(&OptionResponseProblemSelector());
+        $r->print(&ProblemSelector());
     }
 }
 
 #########################################################
 #########################################################
 ##
-##      Misc interface routines use by analysis code
+##      Option Response Routines
 ##
 #########################################################
 #########################################################
-sub build_foil_index {
-    my ($ORdata) = @_;
-    return if (! exists($ORdata->{'Foils'}));
-    my %Foildata = %{$ORdata->{'Foils'}};
-    my @Foils = sort(keys(%Foildata));
-    my %Concepts;
-    foreach my $foilid (@Foils) {
-        push(@{$Concepts{$Foildata{$foilid}->{'Concept'}}},
-             $foilid);
-    }
-    undef(@Foils);
-    # Having gathered the concept information in a hash, we now translate it
-    # into an array because we need to be consistent about order.
-    # Also put the foils in order, too.
-    my $sortfunction = sub {
-        my %Numbers = (one   => 1,
-                       two   => 2,
-                       three => 3,
-                       four  => 4,
-                       five  => 5,
-                       six   => 6,
-                       seven => 7,
-                       eight => 8,
-                       nine  => 9,
-                       ten   => 10,);
-        my $a1 = lc($a); 
-        my $b1 = lc($b);
-        if (exists($Numbers{$a})) {
-            $a1 = $Numbers{$a};
-        }
-        if (exists($Numbers{$b})) {
-            $b1 = $Numbers{$b};
-        }
-        $a1 cmp $b1;
-    };
-    my @Concepts;
-    foreach my $concept (sort $sortfunction (keys(%Concepts))) {
-        push(@Concepts,{ name => $concept,
-                        foils => [@{$Concepts{$concept}}]});
-        push(@Foils,(@{$Concepts{$concept}}));
-    }
-    #
-    # Build up the table of row labels.
-    my $table = '<table border="1" >'."\n";
-    if (@Concepts > 1) {
-        $table .= '<tr>'.
-            '<th>'.&mt('Concept Number').'</th>'.
-            '<th>'.&mt('Concept').'</th>'.
-            '<th>'.&mt('Foil Number').'</th>'.
-            '<th>'.&mt('Foil Name').'</th>'.
-            '<th>'.&mt('Foil Text').'</th>'.
-            '<th>'.&mt('Correct Value').'</th>'.
-            "</tr>\n";
-    } else {
-        $table .= '<tr>'.
-            '<th>'.&mt('Foil Number').'</th>'.
-            '<th>'.&mt('Foil Name').'</th>'.
-            '<th>'.&mt('Foil Text').'</th>'.
-            '<th>'.&mt('Correct Value').'</th>'.
-            "</tr>\n";
-    }        
-    my $conceptindex = 1;
-    my $foilindex = 1;
-    foreach my $concept (@Concepts) {
-        my @FoilsInConcept = @{$concept->{'foils'}};
-        my $firstfoil = shift(@FoilsInConcept);
-        if (@Concepts > 1) {
-            $table .= '<tr>'.
-                '<td>'.$conceptindex.'</td>'.
-                '<td>'.$concept->{'name'}.'</td>'.
-                '<td>'.$foilindex++.'</td>'.
-                '<td>'.$Foildata{$firstfoil}->{'name'}.'</td>'.
-                '<td>'.$Foildata{$firstfoil}->{'text'}.'</td>'.
-                '<td>'.$Foildata{$firstfoil}->{'value'}.'</td>'.
-                "</tr>\n";
+sub OptionResponseAnalysis {
+    my ($r,$resource,$resid,$ProblemData,$Students) = @_;
+    &Apache::lonnet::logthis('resid = '.$resid);
+    my $PerformanceData = 
+        &Apache::loncoursedata::get_optionresponse_data
+        ($Students,$resource->{'symb'},$resid);
+    if (! defined($PerformanceData) || 
+        ref($PerformanceData) ne 'ARRAY' ) {
+        $r->print('<h2>'.
+                  &mt('There is no student data for this problem.').
+                  '</h2>');
+    }  else {
+        $r->print(&render_resource($resource));
+        $r->rflush();
+        if (exists($ENV{'form.ExcelOutput'})) {
+            my $result = &prepare_optionresponse_excel_sheet($r,$resource,
+                                                             $PerformanceData,
+                                                             $ProblemData);
+            $r->print($result);
+            $r->rflush();
         } else {
-            $table .= '<tr>'.
-                '<td>'.$foilindex++.'</td>'.
-                '<td>'.$Foildata{$firstfoil}->{'name'}.'</td>'.
-                '<td>'.$Foildata{$firstfoil}->{'text'}.'</td>'.
-                '<td>'.$Foildata{$firstfoil}->{'value'}.'</td>'.
-                "</tr>\n";
-        }
-        foreach my $foilid (@FoilsInConcept) {
-            if (@Concepts > 1) {
-                $table .= '<tr>'.
-                    '<td></td>'.
-                    '<td></td>'.
-                    '<td>'.$foilindex.'</td>'.
-                    '<td>'.$Foildata{$foilid}->{'name'}.'</td>'.
-                    '<td>'.$Foildata{$foilid}->{'text'}.'</td>'.
-                    '<td>'.$Foildata{$foilid}->{'value'}.'</td>'.
-                    "</tr>\n";
+            if ($ENV{'form.AnalyzeOver'} eq 'Tries') {
+                my $analysis_html = &tries_analysis($r,
+                                                    $PerformanceData,
+                                                    $ProblemData);
+                $r->print($analysis_html);
+                $r->rflush();
+            } elsif ($ENV{'form.AnalyzeOver'} eq 'Time') {
+                my $analysis_html = &time_analysis($PerformanceData,
+                                                   $ProblemData);
+                $r->print($analysis_html);
+                $r->rflush();
             } else {
-                $table .= '<tr>'.
-                    '<td>'.$foilindex.'</td>'.
-                    '<td>'.$Foildata{$foilid}->{'name'}.'</td>'.
-                    '<td>'.$Foildata{$foilid}->{'text'}.'</td>'.
-                    '<td>'.$Foildata{$foilid}->{'value'}.'</td>'.
-                    "</tr>\n";
-            }                
-        } continue {
-            $foilindex++;
+                $r->print('<h2>'.
+                          &mt('The analysis you have selected is '.
+                              'not supported at this time').
+                          '</h2>');
+            }
         }
-    } continue {
-        $conceptindex++;
-    }
-    $table .= "</table>\n";
-    #
-    # Build option index with color stuff
-    return ($table,\@Foils,\@Concepts);
-}
-
-sub build_option_index {
-    my ($ORdata)= @_;
-    my $table = "<table>\n";
-    my $optionindex = 0;
-    my @Rows;
-    foreach my $option (&mt('correct option chosen'),@{$ORdata->{'Options'}}) {
-        push (@Rows,
-              '<tr>'.
-              '<td bgcolor="'.$plotcolors->[$optionindex++].'">'.
-              ('&nbsp;'x4).'</td>'.
-              '<td>'.$option.'</td>'.
-              "</tr>\n");
     }
-    shift(@Rows); # Throw away 'correct option chosen' color
-    $table .= join('',reverse(@Rows));
-    $table .= "</table>\n";
 }
 
 #########################################################
-#########################################################
-##
-##         Tries Analysis
-##
-#########################################################
+#
+#       Option Response:  Tries Analysis
+#
 #########################################################
 sub tries_analysis {
     my ($r,$PerformanceData,$ORdata) = @_;
@@ -385,7 +267,7 @@
             next if (! exists($PlotData[$i]->{$option}));
             push(@Datasets,$PlotData[$i]->{$option});
         }
-        my $correctgraph = &Apache::loncommon::DrawGraph
+        my $correctgraph = &Apache::loncommon::DrawBarGraph
             ($title,'Foil Number','Percent Correct',
              100,$plotcolors,$Datasets[0]);
         $analysis_html.= '<tr><td>'.$correctgraph.'</td>';
@@ -403,7 +285,7 @@
             $count = $count.' submissions';
         }
         $title = 'Attempt '.$i.', '.$count;
-        my $incorrectgraph = &Apache::loncommon::DrawGraph
+        my $incorrectgraph = &Apache::loncommon::DrawBarGraph
             ($title,'Foil Number','% Option Chosen Incorrectly',
              100,$plotcolors,@Datasets);
         $analysis_html.= '<td>'.$incorrectgraph.'</td>';
@@ -470,7 +352,7 @@
             $count = $count.' submissions';
         }
         $title = 'Attempt '.$i.', '.$count;
-        my $graphlink = &Apache::loncommon::DrawGraph
+        my $graphlink = &Apache::loncommon::DrawBarGraph
             ($title,'Concept Number','Percent Correct',
              100,$plotcolors,$PlotData[$i]->{'_correct'});
         $analysis_html.= '<tr><td>'.$graphlink."</td></tr>\n";
@@ -486,8 +368,8 @@
     $maxtries = $mintries if (! defined($maxtries) || $maxtries < $mintries);
     foreach my $row (@$PerformanceData) {
         next if (! defined($row));
-        my $tries = &get_tries_from_row($row);
-        my %Row   = &Process_Row($row);
+        my $tries = &get_tries_from_OR_row($row);
+        my %Row   = &Process_OR_Row($row);
         next if (! %Row);
         while (my ($foilid,$href) = each(%Row)) {
             if (! ref($href)) { 
@@ -503,11 +385,9 @@
 }
 
 #########################################################
-#########################################################
-##
-##                 Time Analysis
-##
-#########################################################
+#
+#     Option Response: Time Analysis
+#
 #########################################################
 sub time_analysis {
     my ($PerformanceData,$ORdata) = @_;
@@ -532,14 +412,14 @@
             ('enddate_'.$i);
         if (! defined($starttime) || ! defined($endtime)) {
             my $sec_in_day = 86400;
-            my $last_sub_time = &get_time_from_row($PerformanceData->[-1]);
+            my $last_sub_time = &get_time_from_OR_row($PerformanceData->[-1]);
             my ($sday,$smon,$syear);
             (undef,undef,undef,$sday,$smon,$syear) = 
                 localtime($last_sub_time - $sec_in_day*$i);
             $starttime = &Time::Local::timelocal(0,0,0,$sday,$smon,$syear);
             $endtime = $starttime + $sec_in_day;
             if ($i == ($num_plots -1 )) {
-                $starttime = &get_time_from_row($PerformanceData->[0]);
+                $starttime = &get_time_from_OR_row($PerformanceData->[0]);
             }
         }
         my $startdateform = &Apache::lonhtmlcommon::date_setter
@@ -551,11 +431,12 @@
         my $end_index;
         my $j;
         while (++$j < scalar(@$PerformanceData)) {
-            last if (&get_time_from_row($PerformanceData->[$j]) > $starttime);
+            last if (&get_time_from_OR_row($PerformanceData->[$j]) 
+                                                              > $starttime);
         }
         $begin_index = $j;
         while (++$j < scalar(@$PerformanceData)) {
-            last if (&get_time_from_row($PerformanceData->[$j]) > $endtime);
+            last if (&get_time_from_OR_row($PerformanceData->[$j]) > $endtime);
         }
         $end_index = $j;
         ##
@@ -589,7 +470,7 @@
     for (my $j=$begin_index;$j<=$end_index;$j++) {
         my $row = $PerformanceData->[$j];
         next if (! defined($row));
-        my %Row = &Process_Row($row);
+        my %Row = &Process_OR_Row($row);
         while (my ($foilid,$href) = each(%Row)) {
             if (! ref($href)) {
                 $TimeData{$foilid} += $href;
@@ -633,12 +514,12 @@
     } else {
         $title = $count.' submissions';
     }
-    my $correctplot = &Apache::loncommon::DrawGraph($title,
-                                                    'Foil Number',
-                                                    'Percent Correct',
-                                                    100,
-                                                    $plotcolors,
-                                                    $Plotdata[0]);
+    my $correctplot = &Apache::loncommon::DrawBarGraph($title,
+                                                       'Foil Number',
+                                                       'Percent Correct',
+                                                       100,
+                                                       $plotcolors,
+                                                       $Plotdata[0]);
     for (my $j=0; $j< scalar(@{$Plotdata[0]});$j++) {
         $Plotdata[0]->[$j]=0;
     }
@@ -650,7 +531,7 @@
     } else {
         $title = $count.' submissions';
     }
-    my $incorrectplot = &Apache::loncommon::DrawGraph($title,
+    my $incorrectplot = &Apache::loncommon::DrawBarGraph($title,
                                                  'Foil Number',
                                                  'Incorrect Option Choice',
                                                  100,
@@ -685,7 +566,7 @@
     for (my $j=$begin_index;$j<=$end_index;$j++) {
         my $row = $PerformanceData->[$j];
         next if (! defined($row));
-        my %Row = &Process_Row($row);
+        my %Row = &Process_OR_Row($row);
         while (my ($foilid,$href) = each(%Row)) {
             if (! ref($href)) {
                 $TimeData{$foilid} += $href;
@@ -714,7 +595,7 @@
     #
     # Create the plot
     my $title = ($end_index - $begin_index).' submissions';
-    my $correctplot = &Apache::loncommon::DrawGraph($title,
+    my $correctplot = &Apache::loncommon::DrawBarGraph($title,
                                                     'Concept Number',
                                                     'Percent Correct',
                                                     100,
@@ -740,7 +621,7 @@
 ##
 #########################################################
 #########################################################
-sub prepare_excel_sheet {
+sub prepare_optionresponse_excel_sheet {
     my ($r,$resource,$PerformanceData,$ORdata) = @_;
     my $response = '';
     my (undef,$Foils,$Concepts) = &build_foil_index($ORdata);
@@ -1130,10 +1011,141 @@
         return $adjust;
 }
 
+sub build_foil_index {
+    my ($ORdata) = @_;
+    return if (! exists($ORdata->{'Foils'}));
+    my %Foildata = %{$ORdata->{'Foils'}};
+    my @Foils = sort(keys(%Foildata));
+    my %Concepts;
+    foreach my $foilid (@Foils) {
+        push(@{$Concepts{$Foildata{$foilid}->{'Concept'}}},
+             $foilid);
+    }
+    undef(@Foils);
+    # Having gathered the concept information in a hash, we now translate it
+    # into an array because we need to be consistent about order.
+    # Also put the foils in order, too.
+    my $sortfunction = sub {
+        my %Numbers = (one   => 1,
+                       two   => 2,
+                       three => 3,
+                       four  => 4,
+                       five  => 5,
+                       six   => 6,
+                       seven => 7,
+                       eight => 8,
+                       nine  => 9,
+                       ten   => 10,);
+        my $a1 = lc($a); 
+        my $b1 = lc($b);
+        if (exists($Numbers{$a})) {
+            $a1 = $Numbers{$a};
+        }
+        if (exists($Numbers{$b})) {
+            $b1 = $Numbers{$b};
+        }
+        $a1 cmp $b1;
+    };
+    my @Concepts;
+    foreach my $concept (sort $sortfunction (keys(%Concepts))) {
+        push(@Concepts,{ name => $concept,
+                        foils => [@{$Concepts{$concept}}]});
+        push(@Foils,(@{$Concepts{$concept}}));
+    }
+    #
+    # Build up the table of row labels.
+    my $table = '<table border="1" >'."\n";
+    if (@Concepts > 1) {
+        $table .= '<tr>'.
+            '<th>'.&mt('Concept Number').'</th>'.
+            '<th>'.&mt('Concept').'</th>'.
+            '<th>'.&mt('Foil Number').'</th>'.
+            '<th>'.&mt('Foil Name').'</th>'.
+            '<th>'.&mt('Foil Text').'</th>'.
+            '<th>'.&mt('Correct Value').'</th>'.
+            "</tr>\n";
+    } else {
+        $table .= '<tr>'.
+            '<th>'.&mt('Foil Number').'</th>'.
+            '<th>'.&mt('Foil Name').'</th>'.
+            '<th>'.&mt('Foil Text').'</th>'.
+            '<th>'.&mt('Correct Value').'</th>'.
+            "</tr>\n";
+    }        
+    my $conceptindex = 1;
+    my $foilindex = 1;
+    foreach my $concept (@Concepts) {
+        my @FoilsInConcept = @{$concept->{'foils'}};
+        my $firstfoil = shift(@FoilsInConcept);
+        if (@Concepts > 1) {
+            $table .= '<tr>'.
+                '<td>'.$conceptindex.'</td>'.
+                '<td>'.$concept->{'name'}.'</td>'.
+                '<td>'.$foilindex++.'</td>'.
+                '<td>'.$Foildata{$firstfoil}->{'name'}.'</td>'.
+                '<td>'.$Foildata{$firstfoil}->{'text'}.'</td>'.
+                '<td>'.$Foildata{$firstfoil}->{'value'}.'</td>'.
+                "</tr>\n";
+        } else {
+            $table .= '<tr>'.
+                '<td>'.$foilindex++.'</td>'.
+                '<td>'.$Foildata{$firstfoil}->{'name'}.'</td>'.
+                '<td>'.$Foildata{$firstfoil}->{'text'}.'</td>'.
+                '<td>'.$Foildata{$firstfoil}->{'value'}.'</td>'.
+                "</tr>\n";
+        }
+        foreach my $foilid (@FoilsInConcept) {
+            if (@Concepts > 1) {
+                $table .= '<tr>'.
+                    '<td></td>'.
+                    '<td></td>'.
+                    '<td>'.$foilindex.'</td>'.
+                    '<td>'.$Foildata{$foilid}->{'name'}.'</td>'.
+                    '<td>'.$Foildata{$foilid}->{'text'}.'</td>'.
+                    '<td>'.$Foildata{$foilid}->{'value'}.'</td>'.
+                    "</tr>\n";
+            } else {
+                $table .= '<tr>'.
+                    '<td>'.$foilindex.'</td>'.
+                    '<td>'.$Foildata{$foilid}->{'name'}.'</td>'.
+                    '<td>'.$Foildata{$foilid}->{'text'}.'</td>'.
+                    '<td>'.$Foildata{$foilid}->{'value'}.'</td>'.
+                    "</tr>\n";
+            }                
+        } continue {
+            $foilindex++;
+        }
+    } continue {
+        $conceptindex++;
+    }
+    $table .= "</table>\n";
+    #
+    # Build option index with color stuff
+    return ($table,\@Foils,\@Concepts);
+}
+
+sub build_option_index {
+    my ($ORdata)= @_;
+    my $table = "<table>\n";
+    my $optionindex = 0;
+    my @Rows;
+    foreach my $option (&mt('correct option chosen'),@{$ORdata->{'Options'}}) {
+        push (@Rows,
+              '<tr>'.
+              '<td bgcolor="'.$plotcolors->[$optionindex++].'">'.
+              ('&nbsp;'x4).'</td>'.
+              '<td>'.$option.'</td>'.
+              "</tr>\n");
+    }
+    shift(@Rows); # Throw away 'correct option chosen' color
+    $table .= join('',reverse(@Rows));
+    $table .= "</table>\n";
+}
+
 #########################################################
 #########################################################
 ##
-##             Interface 
+##   Generic Interface Routines
 ##
 #########################################################
 #########################################################
@@ -1232,7 +1244,7 @@
     return $Str;
 }
 
-sub OptionResponseProblemSelector {
+sub ProblemSelector {
     my $Str;
     $Str = "\n<table>\n";
     foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {
@@ -1301,7 +1313,7 @@
     return undef;
 }
 
-sub get_time_from_row {
+sub get_time_from_OR_row {
     my ($row) = @_;
     if (ref($row)) {
         return $row->[4];
@@ -1309,7 +1321,7 @@
     return undef;
 }
 
-sub get_tries_from_row {
+sub get_tries_from_OR_row {
     my ($row) = @_;
     if (ref($row)) {
         return $row->[5];
@@ -1317,7 +1329,7 @@
     return undef;
 }
 
-sub Process_Row {
+sub Process_OR_Row {
     my ($row) = @_;
     my %RowData;
     my ($student_id,$award,$grading,$submission,$time,$tries) = @$row;

--matthew1067002686--