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

matthew lon-capa-cvs@mail.lon-capa.org
Wed, 15 Oct 2003 21:30:51 -0000


This is a MIME encoded message

--matthew1066253451
Content-Type: text/plain

matthew		Wed Oct 15 17:30:51 2003 EDT

  Modified files:              
    /loncom/interface/statistics	lonproblemanalysis.pm 
  Log:
  Monstrous rewrite to, on foil analysis, output graphs showing how the
  students got the foil incorrect.  As a side effect there will no longer
  be any big green bars on the top of the bar chart in concept analysis
  mode.  The case of one concept is handled more cleanly.
  Only one column of graphs in foil analysis mode as the key needs to be
  repeated.
  Skip 'MISSING_ANSWER' graded submissions because they will really screw it
  all up.
  The major functionality to include is implemented, with the exception of
  Excel output of the basic data.
  Assumes no options = '_total' or '_incorrect'.
  Known issues:
  The 'submission' and 'submissiongrading' parameter thingys do not appear to
  allow leading/trailing spaces, so these are zealously removed.  May be an 
  erronous assumption.
  A warning is thrown: "Possible attempt to put comments in qw() list".  I need
  to turn off warnings around the "offending" line of code.
  HTML::Entities::decode does not decode %20 into ' ' so I have to do it myself.
  The foil analysis plots can look pretty vomitous, especially for the
  Atwood problem in the PHY183 Spring 2003 course.  
  The colors selected may not be useful to color blind people.
  
  
--matthew1066253451
Content-Type: text/plain
Content-Disposition: attachment; filename="matthew-20031015173051.txt"

Index: loncom/interface/statistics/lonproblemanalysis.pm
diff -u loncom/interface/statistics/lonproblemanalysis.pm:1.38 loncom/interface/statistics/lonproblemanalysis.pm:1.39
--- loncom/interface/statistics/lonproblemanalysis.pm:1.38	Tue Oct 14 18:02:49 2003
+++ loncom/interface/statistics/lonproblemanalysis.pm	Wed Oct 15 17:30:51 2003
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 #
 
-# $Id: lonproblemanalysis.pm,v 1.38 2003/10/14 22:02:49 matthew Exp $
+# $Id: lonproblemanalysis.pm,v 1.39 2003/10/15 21:30:51 matthew Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -37,6 +37,13 @@
 use Apache::lonlocal;
 use HTML::Entities();
 
+my $plotcolors = [qw/
+                  #33ff00 
+                  #0033cc #990000 #aaaa66 #663399 #ff9933
+                  #66ccff #ff9999 #cccc33 #660000 #33cc66
+                  /]; 
+               #[qw/lgreen dgreen dred/];
+
 sub BuildProblemAnalysisPage {
     my ($r,$c)=@_;
     $r->print('<h2>'.&mt('Option Response Problem Analysis').'</h2>');
@@ -54,6 +61,7 @@
     } else {
         $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="'.
@@ -167,53 +175,80 @@
                        ten   => 10,);
         my $a1 = $a; 
         my $b1 = $b;
-        if (exists($Numbers{$a})) {
-            $a1 = $Numbers{$a};
+        if (exists($Numbers{lc($a)})) {
+            $a1 = $Numbers{lc($a)};
         }
-        if (exists($Numbers{$b})) {
-            $b1 = $Numbers{$b};
+        if (exists($Numbers{lc($b)})) {
+            $b1 = $Numbers{lc($b)};
         }
         $a1 cmp $b1;
     };
     my @Concepts;
     foreach my $concept (sort $sortfunction (keys(%Concepts))) {
-        push(@Concepts,{name => $concept,
+        push(@Concepts,{ name => $concept,
                         foils => [@{$Concepts{$concept}}]});
         push(@Foils,(@{$Concepts{$concept}}));
     }
     #
     # Build up the table of row labels.
     my $table = '<table border="1" >'."\n";
-    $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";
+    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);
-        $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";
-        foreach my $foilid (@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></td>'.
-                '<td></td>'.
-                '<td>'.$foilindex.'</td>'.
-                '<td>'.$Foildata{$foilid}->{'name'}.'</td>'.
-                '<td>'.$Foildata{$foilid}->{'text'}.'</td>'.
-                '<td>'.$Foildata{$foilid}->{'value'}.'</td>'.
+                '<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++;
         }
@@ -221,9 +256,28 @@
         $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 ('correct',@{$ORdata->{'Options'}}) {
+        push (@Rows,
+              '<tr>'.
+              '<td bgcolor="'.$plotcolors->[$optionindex++].'">'.
+              ('&nbsp;'x4).'</td>'.
+              '<td>'.$option.'</td>'.
+              "</tr>\n");
+    }
+    $table .= join('',reverse(@Rows));
+    $table .= "</table>\n";
+}
+
 #########################################################
 #########################################################
 ##
@@ -235,67 +289,81 @@
     my ($PerformanceData,$ORdata) = @_;
     my $mintries = 1;
     my $maxtries = $ENV{'form.NumPlots'};
+    my ($table,$Foils,$Concepts) = &build_foil_index($ORdata);
+    if ((@$Concepts < 2) && ($ENV{'form.AnalyzeAs'} ne 'Foils')) {
+        $table = '<h3>'.
+            &mt('Not enough data for concept analysis.  '.
+                'Performing Foil Analysis').
+            '</h3>'.$table;
+        $ENV{'form.AnalyzeAs'} = 'Foils';
+    }
     my %ResponseData = &analyze_option_data_by_tries($PerformanceData,
                                                      $mintries,$maxtries);
-    my ($table,$Foils,$Concepts) = &build_foil_index($ORdata);
     #
     # Compute the data neccessary to make the plots
-    my @PlotData;
-    my $xlabel;
+    my @PlotData;   # Array which holds the data for each plot
+                    # @{$PlotData[$try]->{'datasetname'}} holds the data for
+                    # try $try with respect to 'datasetname'.  The array is
+                    # filled either with per-foil or per-concept data.
+    my ($extrakey,$xlabel,$ylabel);
     if ($ENV{'form.AnalyzeAs'} eq 'Foils') {
+        $extrakey = &build_option_index($ORdata);
         $xlabel = 'Foil Number';
+        $ylabel = 'Option Chosen';
         foreach my $foilid (@$Foils) {
             for (my $i=$mintries;$i<=$maxtries;$i++) {
-                #
-                # Gather the per-attempt data
-                my $percent;
-                if ($ResponseData{$foilid}->[$i]->{'total'} == 0) {
-                    $percent = 0;
-                } else {
-                    $percent = $ResponseData{$foilid}->[$i]->{'correct'} /
-                        $ResponseData{$foilid}->[$i]->{'total'};
+                foreach my $option ('_correct',@{$ORdata->{'Options'}}) {
+                    push(@{$PlotData[$i]->{'_total'}},
+                         $ResponseData{$foilid}->[$i]->{'_total'});
+                    if ($ResponseData{$foilid}->[$i]->{'_total'} == 0) {
+                        push (@{$PlotData[$i]->{$option}},0);
+                    } else {
+                        push (@{$PlotData[$i]->{$option}},
+                              100 * $ResponseData{$foilid}->[$i]->{$option} / 
+                                    $ResponseData{$foilid}->[$i]->{'_total'});
+                    }
                 }
-                push (@{$PlotData[$i]->{'total'}},
-                                 $ResponseData{$foilid}->[$i]->{'total'});
-                push (@{$PlotData[$i]->{'good'}},100 * $percent);
-                push (@{$PlotData[$i]->{'bad'}}, 100 *(1-$percent));
             }
         }
     } else {
         # Concept analysis
+        #
+        # Note: we do not bother with characterizing the students incorrect
+        # answers at the concept level because an incorrect answer for one foil
+        # may be a correct answer for another foil.
+        $extrakey = '';
         $xlabel = 'Concept Number';
-        foreach my $concept (@$Concepts) {
+        $ylabel = 'Percent Correct';
+        my %ConceptData;
+        foreach my $concept (@{$Concepts}) {
             for (my $i=$mintries;$i<=$maxtries;$i++) {
                 #
                 # Gather the per-attempt data
-                my ($correct,$incorrect,$total);
-                foreach my $foil (@{$concept->{'foils'}}) {
-                    $correct   += $ResponseData{$foil}->[$i]->{'correct'};
-                    $incorrect += $ResponseData{$foil}->[$i]->{'incorrect'};
-                    $total     += $ResponseData{$foil}->[$i]->{'total'};
+                my $cdata = $ConceptData{$concept}->[$i];
+                foreach my $foilid (@{$concept->{'foils'}}) {
+                    $cdata->{'_correct'} += 
+                        $ResponseData{$foilid}->[$i]->{'_correct'};
+                    $cdata->{'_total'}   += 
+                        $ResponseData{$foilid}->[$i]->{'_total'};
                 }
-                push (@{$PlotData[$i]->{'correct'}},  $correct);
-                push (@{$PlotData[$i]->{'incorrect'}},$incorrect);
-                push (@{$PlotData[$i]->{'total'}},    $total);
-                my $percent;
-                if ($total == 0) {
-                    $percent = 0;
+                push (@{$PlotData[$i]->{'_total'}},$cdata->{'_total'});
+                if ($cdata->{'_total'} == 0) {
+                    push (@{$PlotData[$i]->{'_correct'}},0);
                 } else {
-                    $percent = $correct/$total;
+                    push (@{$PlotData[$i]->{'_correct'}},
+                          100*$cdata->{'_correct'}/$cdata->{'_total'});
                 }
-                push (@{$PlotData[$i]->{'good'}},100*$percent);
-                push (@{$PlotData[$i]->{'bad'}},100*(1-$percent));
             }
         }
-    }
+    } # End of work to fill @PlotData
     # 
     # Build a table for the plots
     $table .= "<table>\n";
     my @Plots;
     for (my $i=$mintries;$i<=$maxtries;$i++) {
-        my $minstu = $PlotData[$i]->{'total'}->[0];
-        my $maxstu = $PlotData[$i]->{'total'}->[0];
-        foreach my $count (@{$PlotData[$i]->{'total'}}) {
+        my $minstu = $PlotData[$i]->{'_total'}->[0];
+        my $maxstu = $PlotData[$i]->{'_total'}->[0];
+        foreach my $count (@{$PlotData[$i]->{'_total'}}) {
             if ($minstu > $count) {
                 $minstu = $count;
             }
@@ -303,35 +371,31 @@
                 $maxstu = $count;
             }
         }
-        $maxstu = 0 if (! $maxstu);
-        $minstu = 0 if (! $minstu);
+        $maxstu = 0 if (! defined($maxstu));
+        $minstu = 0 if (! defined($minstu));
         my $title;
         if ($maxstu == $minstu) {
             $title = 'Attempt '.$i.', '.$maxstu.' students';
         } else {
             $title = 'Attempt '.$i.', '.$minstu.'-'.$maxstu.' students';
         }
+        my @Datasets;
+        foreach my $option ('_correct',@{$ORdata->{'Options'}}) {
+            next if (! exists($PlotData[$i]->{$option}));
+            push(@Datasets,$PlotData[$i]->{$option});
+        }
         my $graphlink = &Apache::loncommon::DrawGraph($title,
                                                       $xlabel,
-                                                      'Percent Correct',
+                                                      $ylabel,
                                                       100,
-                                                      $PlotData[$i]->{'good'},
-                                                      $PlotData[$i]->{'bad'});
+                                                      $plotcolors,
+                                                      @Datasets);
         push(@Plots,$graphlink);
     }
     #
     # Should this be something the user can set?  Too many dialogs!
-    my $plots_per_row = 2;
     while (my $plotlink = shift(@Plots)) {
-        $table .= '<tr><td>'.$plotlink.'</td>';
-        for (my $i=1;$i<$plots_per_row;$i++) {
-            if ($plotlink = shift(@Plots)) {
-                $table .= '<td>'.$plotlink.'</td>';
-            } else {
-                $table .= '<td></td>';
-            }
-        }
-        $table .= "</tr>\n";
+        $table .= '<tr><td>'.$plotlink.'</td><td>'.$extrakey."</td></tr>\n";
     }
     $table .= "</table>\n";
     return ($table);
@@ -345,31 +409,26 @@
     foreach my $row (@$PerformanceData) {
         next if (! defined($row));
         my ($grading,$submission,$time,$tries) = @$row;
+        next if ($grading eq 'MISSING_ANSWER');
         my @Foilgrades = split('&',$grading);
         my @Foilsubs   = split('&',$submission);
         for (my $numtries = 1; $numtries <= $maxtries; $numtries++) {
             if ($tries == $numtries) {
-                foreach my $foilgrade (@Foilgrades) {
-                    my ($foilid,$correct) = split('=',$foilgrade);
+                for (my $i=0;$i<=$#Foilgrades;$i++) {
+                    my ($foilid,$correct)  = split('=',$Foilgrades[$i]);
+                    my (undef,$submission) = split('=',$Foilsubs[$i]);
+                    $submission = &HTML::Entities::decode($submission);
+                    $submission =~ s/\%20/ /g;
                     if ($correct) {
-                        $Trydata{$foilid}->[$numtries]->{'correct'}++;
+                        $Trydata{$foilid}->[$numtries]->{'_correct'}++;
                     } else {
-                        $Trydata{$foilid}->[$numtries]->{'incorrect'}++;
+                        $Trydata{$foilid}->[$numtries]->{$submission}++;
                     }                        
+                    $Trydata{$foilid}->[$numtries]->{'_total'}++;
                 }
             }
         }
     }
-    foreach my $foilid (keys(%Trydata)) {
-        foreach my $tryhash (@{$Trydata{$foilid}}) {
-            next if ((! exists($tryhash->{'correct'}) && 
-                      ! exists($tryhash->{'incorrect'})) ||
-                     ($tryhash->{'correct'} < 1 &&
-                      $tryhash->{'incorrect'} < 1));
-            $tryhash->{'total'} = $tryhash->{'correct'} + 
-                $tryhash->{'incorrect'};
-        }
-    }
     return %Trydata;
 }
 
@@ -386,6 +445,10 @@
     my ($table,$Foils,$Concepts) = &build_foil_index($ORdata);
     my $num_data = scalar(@$PerformanceData)-1;
     my $percent = sprintf('%2f',100/$num_plots);
+    my $extratable = '';
+    if ($ENV{'form.AnalyzeAs'} eq 'Foils') {
+        $extratable = &build_option_index($ORdata);
+    }
     $table .= "<table>\n";
     for (my $i=0;$i<$num_plots;$i++) {
         my $starttime = &Apache::lonhtmlcommon::get_date_from_form
@@ -416,8 +479,8 @@
         ($plothtml,$starttime,$endtime,$data) = 
             &analyze_option_data_by_time($PerformanceData,
                                          $begin_index,$end_index,
-                                         $plottitle,
-                                         @$Concepts);
+                                         $plottitle,$Foils,
+                                         $Concepts,$ORdata);
         my $startdateform = &Apache::lonhtmlcommon::date_setter
             ('Statistics','startdate_'.$i,$starttime);
         my $enddateform = &Apache::lonhtmlcommon::date_setter
@@ -427,7 +490,7 @@
             "<b>End Time</b>&nbsp;&nbsp;: "."&nbsp;".$enddateform."<br />".
             '<b>Plot Title</b>&nbsp;&nbsp;:'.("&nbsp;"x3).
             '<input type="text" size="30" name="plottitle_'.$i.'" value="'.
-                  &HTML::Entities::encode($plottitle).'" /><br />'.
+                  &HTML::Entities::encode($plottitle).'" /><br />'.$extratable.
             "</td></tr>\n";
     }
     $table .="</table>\n";
@@ -435,7 +498,8 @@
 }
 
 sub analyze_option_data_by_time {
-    my ($PerformanceData,$begin_index,$end_index,$description,@Concepts) = @_;
+    my ($PerformanceData,$begin_index,
+        $end_index,$description,$Foils,$Concepts,$ORdata) = @_;
     my %TimeData;
     #
     # Get the start and end times for this segment of the plot
@@ -447,74 +511,69 @@
         my $row = $PerformanceData->[$i];
         next if (! defined($row));
         my ($grading,$submission,$time,$tries) = @$row;
+        next if ($grading eq 'MISSING_ANSWER');
         my @Foilgrades = split('&',$grading);
         my @Foilsubs   = split('&',$submission);
-        foreach my $foilgrade (@Foilgrades) {
-            my ($foilid,$correct) = split('=',$foilgrade);
+        for (my $j=0;$j<=$#Foilgrades;$j++) {
+            my ($foilid,$correct)  = split('=',$Foilgrades[$j]);
+            my (undef,$submission) = split('=',$Foilsubs[$j]);
             if ($correct) {
-                $TimeData{$foilid}->{'correct'}++;
+                $TimeData{$foilid}->{'_correct'}++;
             } else {
-                $TimeData{$foilid}->{'incorrect'}++;
+                $submission = &HTML::Entities::decode($submission);
+                $submission =~ s/\%20/ /g;
+                $TimeData{$foilid}->{$submission}++;
             }
+            $TimeData{$foilid}->{'_total'}++;
         }
     }
     #
     # Compute the total and percent correct
-    my @Plotdata1;
-    my @Plotdata2;
-    foreach my $concept (@Concepts) {
-        my ($correct,$incorrect,$total);
-        foreach my $foilid (@{$concept->{'foils'}}) {
-            if (! exists($TimeData{$foilid}->{'correct'})) {
-                $TimeData{$foilid}->{'correct'} = 0;
-            }
-            if (! exists($TimeData{$foilid}->{'incorrect'})) {
-                $incorrect = 0;
-                $TimeData{$foilid}->{'incorrect'} = 0;
-            }
-            $correct   += $TimeData{$foilid}->{'correct'};
-            $incorrect += $TimeData{$foilid}->{'incorrect'};
-            $total     += $TimeData{$foilid}->{'correct'}+
-                $TimeData{$foilid}->{'incorrect'};
-            $TimeData{$foilid}->{'total'} = $TimeData{$foilid}->{'correct'} +
-                $TimeData{$foilid}->{'incorrect'};
-            my $percent;
-            if ($TimeData{$foilid}->{'total'} == 0) {
-                $percent = 0;
-            } else {
-                $percent = $TimeData{$foilid}->{'correct'} / 
-                    $TimeData{$foilid}->{'total'};
-            }
-            $TimeData{$foilid}->{'percent_corr'} = 100 * $percent;
-            if ($ENV{'form.AnalyzeAs'} eq 'Foils') {
-                push (@Plotdata1,    $TimeData{$foilid}->{'percent_corr'});
-                push (@Plotdata2,100-$TimeData{$foilid}->{'percent_corr'});
+    my @Plotdata;
+    my ($xlabel,$ylabel);
+    if ($ENV{'form.AnalyzeAs'} eq 'Foils') {
+        $xlabel = 'Foil Number';
+        $ylabel = 'Option Chosen';
+        foreach my $foil (@$Foils) {
+            my $total = $TimeData{$foil}->{'_total'};
+            my $optionidx = 0;
+            foreach my $option ('_correct',@{$ORdata->{'Options'}}) {
+                if ($total > 0) {
+                    push(@{$Plotdata[$optionidx]},
+                         100 * $TimeData{$foil}->{$option} / $total);
+                } else {
+                    push(@{$Plotdata[$optionidx]},0);
+                }
+            } continue {
+                $optionidx++;
             }
         }
-        if ($ENV{'form.AnalyzeAs'} ne 'Foils') {
-            if ($total == 0) {
-                push (@Plotdata1,0);
-                push (@Plotdata2,100);
+    } else {
+        $xlabel = 'Concept Number';
+        $ylabel = 'Percent Correct';
+        foreach my $concept (@$Concepts) {
+            my $correct;
+            my $total;
+            foreach my $foil (@{$concept->{'foils'}}) {
+                $correct+=$TimeData{$foil}->{'_correct'};
+                $total  +=$TimeData{$foil}->{'_total'};
+            }
+            if ($total > 0) {
+                push(@{$Plotdata[0]},100 * $correct / $total);
             } else {
-                push (@Plotdata1,100 *   $correct / $total);
-                push (@Plotdata2,100 * (1-$correct / $total));
+                push(@{$Plotdata[0]},0);
             }
         }
     }
     #
     # Create the plot
-    my $xlabel;
-    if ($ENV{'form.AnalyzeAs'} eq 'Foils') {
-        $xlabel = 'Foil Number';
-    } else {
-        $xlabel = 'Concept Number';
-    }
     my $graphlink = &Apache::loncommon::DrawGraph
         ($description,#'Time Interval Analysis',
          $xlabel,
-         'Percent Correct / Incorrect',
+         $ylabel,
          100,
-         \@Plotdata1,\@Plotdata2);
+         $plotcolors,
+         @Plotdata);
     #
     return ($graphlink,$starttime,$endtime,\%TimeData);
 }
@@ -690,6 +749,11 @@
     return undef;
 }
 
+##
+## get problem data and put it into a useful data structure.
+## note: we must force each foil and option to not begin or end with
+##       spaces as they are stored without such data.
+##
 sub get_problem_data {
     my ($url) = @_;
     my $Answ=&Apache::lonnet::ssi($url,('grade_target' => 'analyze'));
@@ -703,25 +767,30 @@
             $key =~ s/^$part\.//;
             if (ref($value) eq 'ARRAY') {
                 if ($key eq 'options') {
+                    for(my $i=0;$i<scalar(@$value);$i++) {
+                        $value->[$i]=~ s/(\s*$|^\s*)//g;
+                    }
                     $Partdata{$part}->{'Options'}=$value;
                 } elsif ($key eq 'concepts') {
                     $Partdata{$part}->{'Concepts'}=$value;
                 } elsif ($key =~ /^concept\.(.*)$/) {
                     my $concept = $1;
                     foreach my $foil (@$value) {
+                        $foil =~ s/(\s*$|^\s*)//g;
                         $Partdata{$part}->{'Foils'}->{$foil}->{'Concept'}=
                                                                       $concept;
                     }
                 }
             } else {
-                $value =~ s/^\s*//g;
-                $value =~ s/\s*$//g;
+                $value =~ s/(\s*$|^\s*)//g;
                 if ($key=~ /^foil\.text\.(.*)$/) {
                     my $foil = $1;
+                    $foil =~ s/(\s*$|^\s*)//g;
                     $Partdata{$part}->{'Foils'}->{$foil}->{'name'}=$foil;
                     $Partdata{$part}->{'Foils'}->{$foil}->{'text'}=$value;
                 } elsif ($key =~ /^foil\.value\.(.*)$/) {
                     my $foil = $1;
+                    $foil =~ s/(\s*$|^\s*)//g;
                     $Partdata{$part}->{'Foils'}->{$foil}->{'value'}=$value;
                 }
             }

--matthew1066253451--