Diff for /loncom/interface/statistics/lonstudentassessment.pm between versions 1.30 and 1.32

version 1.30, 2003/02/28 21:19:00 version 1.32, 2003/03/03 22:54:05
Line 54  use Apache::lonstatistics; Line 54  use Apache::lonstatistics;
 use Apache::lonhtmlcommon;  use Apache::lonhtmlcommon;
 use Apache::loncoursedata;  use Apache::loncoursedata;
 use Apache::lonnet; # for logging porpoises  use Apache::lonnet; # for logging porpoises
 use GDBM_File;  use Spreadsheet::WriteExcel;
   
   #######################################################
   #######################################################
   =pod
   
   =item Package Variables
   
   =over 4
   
   =item $Statistics Hash ref to store student data.  Indexed by symb,
         contains hashes with keys 'score' and 'max'.
   
   =cut
   
   #######################################################
   #######################################################
   
 my $Statistics;  my $Statistics;
   
Line 63  my $Statistics; Line 79  my $Statistics;
   
 =pod  =pod
   
 =item &BuildStudentAssessmentPage()  =item $show_links 'yes' or 'no' for linking to student performance data
   
 Inputs:   =item $output_mode 'html', 'excel', or 'csv' for output mode
   
 =over 4  =item $show 'all', 'totals', or 'scores' determines how much data is output
   
 =item $cacheDB The name of the cache file used to store student data  =cut
   
 =item $students Array ref containing the name(s) of the students   #######################################################
 selected for display  #######################################################
   my $show_links;
   my $output_mode;
   my $show;
   
   #######################################################
   #######################################################
   # End of package variable declarations
   
 =item $courseID The ID of the course  =pod
   
 =item $formName The name of the html form - 'Statistics'  =back
   
 =item $headings Array ref of headings to show  =cut
   
 =item $spacing A string of spaces  #######################################################
   #######################################################
   
 =item $studentInformation Array ref of possible headings for student info  =pod
 ('fullname','section',...)  
   =item &BuildStudentAssessmentPage()
   
   Inputs: 
   
   =over 4
   
 =item $r Apache Request  =item $r Apache Request
   
Line 98  selected for display Line 127  selected for display
 sub BuildStudentAssessmentPage {  sub BuildStudentAssessmentPage {
     my ($r,$c)=@_;      my ($r,$c)=@_;
     undef($Statistics);      undef($Statistics);
       
     #      #
       # Print out the HTML headers for the interface
       #    This also parses the output mode selector
       #    This step must always be done.
     $r->print(&CreateInterface());      $r->print(&CreateInterface());
       $r->print('<input type="hidden" name="notfirstrun" value="true" />');
     $r->rflush();      $r->rflush();
       if (! exists($ENV{'form.notfirstrun'})) {
           $r->print(<<ENDMSG);
   <p>
   <font size="+1">
   Please make your selections in the boxes above and hit 
   the button marked &quot;Update&nbsp;Display&quot;.
   </font>
   </p>
   ENDMSG
           return;
       }
       #
       #
       my $initialize     = \&html_initialize;
       my $output_student = \&html_outputstudent;
       my $finish         = \&html_finish;
       #
       if ($output_mode eq 'excel') {
           $initialize     = \&excel_initialize;
           $output_student = \&excel_outputstudent;
           $finish         = \&excel_finish;
       } elsif ($output_mode eq 'csv') {
           $initialize     = \&csv_initialize;
           $output_student = \&csv_outputstudent;
           $finish         = \&csv_finish;
       }
     #      #
     $r->print(&CreateTableHeadings());  
     if($c->aborted()) {  return ; }      if($c->aborted()) {  return ; }
           #
     my $Count = 0;      # Call the initialize routine selected above
     $r->print('<pre>'."\n");      $initialize->($r);
     foreach my $student (@Apache::lonstatistics::Students) {      foreach my $student (@Apache::lonstatistics::Students) {
         if($c->aborted()) { return ; }          if($c->aborted()) { 
         $r->print(&ChartOutputStudent($student));              $finish->($r);
         # output it              return ; 
   
         $Count++;  
         if($Count % 5 == 0) {  
             $r->print("</pre>\n<pre>");  
         }          }
           # Call the output_student routine selected above
         $r->rflush();          $output_student->($r,$student);
     }      }
     $r->print('</pre>'."\n");       # Call the "finish" routine selected above
     my $Str;      $finish->($r);
       #
     return;      return;
 }  }
   
 #######################################################  #######################################################
 #######################################################  #######################################################
   
   sub get_student_fields_to_show {
       my @to_show = @Apache::lonstatistics::SelectedStudentData;
       foreach (@to_show) {
           if ($_ eq 'all') {
               @to_show = @Apache::lonstatistics::StudentDataOrder;
               last;
           }
       }
       return @to_show;
   }
   
   sub get_sequences_to_show {
       my @Sequences;
       foreach my $map_symb (@Apache::lonstatistics::SelectedMaps) {
           foreach my $sequence (@Apache::lonstatistics::Sequences) {
               next if ($sequence->{'symb'} ne $map_symb && $map_symb ne 'all');
               next if ($sequence->{'num_assess'} < 1);
               push (@Sequences,$sequence);
           }
       }
       return @Sequences;
   }
   
   
   #######################################################
   #######################################################
   
 =pod  =pod
   
 =item &CreateInterface()  =item &CreateInterface()
Line 152  sub CreateInterface { Line 233  sub CreateInterface {
     $Str .= '<td align="center"><b>Sections</b></td>';      $Str .= '<td align="center"><b>Sections</b></td>';
     $Str .= '<td align="center"><b>Student Data</b></td>';      $Str .= '<td align="center"><b>Student Data</b></td>';
     $Str .= '<td align="center"><b>Sequences and Folders</b></td>';      $Str .= '<td align="center"><b>Sequences and Folders</b></td>';
       $Str .= '<td align="center"><b>Output Format</b></td>';
     $Str .= '</tr>'."\n";      $Str .= '</tr>'."\n";
     #      #
     $Str .= '<tr><td align="center">'."\n";      $Str .= '<tr><td align="center">'."\n";
Line 170  sub CreateInterface { Line 252  sub CreateInterface {
     $Str .= '</td><td>'."\n";      $Str .= '</td><td>'."\n";
     $Str .= &Apache::lonstatistics::MapSelect('Maps','multiple,all',5,      $Str .= &Apache::lonstatistics::MapSelect('Maps','multiple,all',5,
                                               $only_seq_with_assessments);                                                $only_seq_with_assessments);
       $Str .= '</td><td>'."\n";
       $Str .= &CreateAndParseOutputSelector();
     $Str .= '</td></tr>'."\n";      $Str .= '</td></tr>'."\n";
     $Str .= '</table>'."\n";      $Str .= '</table>'."\n";
     return $Str;      return $Str;
 }  }
   
   
 #######################################################  #######################################################
 #######################################################  #######################################################
   
 =pod  =pod
   
 =item Table Output Routines  =item &CreateAndParseOutputSelector()
   
 =over 4  
   
 =cut  =cut
   
 #######################################################  #######################################################
 #######################################################  #######################################################
 {  my @OutputOptions = 
     my $padding;      ({ name  => 'HTML, with links',
          value => 'html, with links',
          description => ''},
        { name  => 'HTML, without links',
          value => 'html, without links',
          description => ''},
        { name  => 'HTML, totals',
          value => 'html, totals',
          description => ''},
        { name  => 'HTML, scores only',
          value => 'html, scores only',
          description => ''},
        { name  => 'Excel, totals',
          value => 'excel, totals',
          description => ''},
        { name  => 'Excel, scores only',
          value => 'excel, scores only',
          description => ''},
        { name  => 'CSV, totals',
          value => 'csv, totals',
          description => ''},
        { name  => 'CSV, scores only',
          value => 'csv, scores only',
          description => ''},
        { name  => 'CSV, everything',
          value => 'csv, everything',
          description => ''}
        );
   
   sub CreateAndParseOutputSelector {
       my $Str = '';
       my $elementname = 'outputmode';
       #
       # Format for output options is 'mode, restrictions';
       my $selected = 'html, with links';
       if (exists($ENV{'form.'.$elementname})) {
           if (ref($ENV{'form.'.$elementname} eq 'ARRAY')) {
               $selected = $ENV{'form.'.$elementname}->[0];
           } else {
               $selected = $ENV{'form.'.$elementname};
           }
       }
       #
       # Set package variables describing output mode
       $show_links  = 'no';
       $output_mode = 'html';
       $show        = 'all';
       my ($mode,$restriction) = split(',',$selected);
       $restriction =~ s/^\s*//;
       if ($mode =~ /^(html|excel|csv)$/) {
           $output_mode = $mode;
       } else {
           $output_mode = 'html';
       }
       if ($restriction eq 'with links') {
           $show_links = 'yes';
       } else {
           $show_links = 'no';
       }
       if ($restriction eq 'totals') {
           $show = 'totals';
       } elsif ($restriction eq 'scores only') {
           $show = 'scores';
       } else {
           $show = 'everything';
       }
       #
       # Build the form element
       $Str = qq/<select size="5" name="$elementname">/;
       foreach my $option (@OutputOptions) {
           $Str .= "\n".'    <option value="'.$option->{'value'}.'"';
           $Str .= " selected " if ($option->{'value'} eq $selected);
           $Str .= ">".$option->{'name'}."<\/option>";
       }
       $Str .= "\n</select>";
       return $Str;
   }
   
 #######################################################  #######################################################
 #######################################################  #######################################################
   
 =pod  =pod
   
 =item &CreateTableHeadings()  =head2 HTML output routines
   
 Create HTML for the columns of student data to show.  
 Called by &BuildStudentAssessmentPage().  Calls  
 &Apache::lonhtmlcommon::CreateHeadings().  
   
 Inputs:  
   
 =over 4  
   
 =item $cache The ubiquitous cache  
   
 =item $spacing A string of spaces  
   
 =item $infoKeys Array ref to names of keys to display from the cache   
 which describe students  
   
 =item $infoHeadings Array ref to headings of columns for student info  =item &html_initialize($r)
   
 =item $sequenceKeys Array ref of names of keys to use to retrieve sequence  Create labels for the columns of student data to show.
 data from the cache  
   
 =item $sequenceHeadings Array ref of names of sequences used for output.  =item &html_outputstudent($r,$student)
   
 =back  Return a line of the chart for a student.
   
 Returns: A string containing the HTML of the table headings.  =item &html_finish($r)
   
 =cut  =cut
   
 #######################################################  #######################################################
 #######################################################  #######################################################
 sub CreateTableHeadings {  {
       my $padding;
       my $count;
   
   sub html_initialize {
       my ($r) = @_;
     #      #
     $padding = ' 'x3;      $padding = ' 'x3;
       $count = 1;
     #      #
     my $Str = '<pre>';      my $Str = "<pre>\n";
     # First, the @StudentData fields need to be listed      # First, the @StudentData fields need to be listed
     my @to_show = @Apache::lonstatistics::SelectedStudentData;      my @to_show = &get_student_fields_to_show();
     foreach (@to_show) {  
         if ($_ eq 'all') {  
             @to_show = @Apache::lonstatistics::StudentDataOrder;  
             last;  
         }  
     }  
     foreach my $field (@to_show) {      foreach my $field (@to_show) {
         my $title=$Apache::lonstatistics::StudentData{$field}->{'title'};          my $title=$Apache::lonstatistics::StudentData{$field}->{'title'};
         my $base =$Apache::lonstatistics::StudentData{$field}->{'base_width'};          my $base =$Apache::lonstatistics::StudentData{$field}->{'base_width'};
Line 249  sub CreateTableHeadings { Line 390  sub CreateTableHeadings {
         $Str .= $title.' 'x($width-$base).$padding;          $Str .= $title.' 'x($width-$base).$padding;
     }      }
     # Now the selected sequences need to be listed      # Now the selected sequences need to be listed
     foreach my $map_symb (@Apache::lonstatistics::SelectedMaps) {      foreach my $sequence (&get_sequences_to_show) {
         foreach my $sequence (@Apache::lonstatistics::Sequences) {          my $title = $sequence->{'title'};
             next if ($sequence->{'symb'} ne $map_symb && $map_symb ne 'all');          my $base  = $sequence->{'base_width'};
             next if ($sequence->{'num_assess'} < 1);          my $width = $sequence->{'width'};
             my $title = $sequence->{'title'};          $Str .= $title.' 'x($width-$base).$padding;
             my $base  = $sequence->{'base_width'};  
             my $width = $sequence->{'width'};  
             $Str .= $title.' 'x($width-$base).$padding;  
         }  
     }      }
     $Str .= 'total';      $Str .= "total (of shown problems)</pre>\n";
     $Str .= "</pre>\n";      $Str .= "<pre>";
     return $Str;      $r->print($Str);
       $r->rflush();
       return;
 }  }
   
 #######################################################  sub html_outputstudent {
 #######################################################      my ($r,$student) = @_;
   
 =pod  
   
 =item &ChartOutputStudent($student)  
   
 Return a line of the chart for a student.  
   
 =cut  
   
 #######################################################  
 #######################################################  
 sub ChartOutputStudent {  
     my $student = shift;  
     my $Str = '';      my $Str = '';
     # First, the @StudentData fields need to be listed      # First, the @StudentData fields need to be listed
     my @to_show = @Apache::lonstatistics::SelectedStudentData;      my @to_show = &get_student_fields_to_show();
     foreach (@to_show) {  
         if ($_ eq 'all') {  
             @to_show = @Apache::lonstatistics::StudentDataOrder;  
             last;  
         }  
     }  
     foreach my $field (@to_show) {      foreach my $field (@to_show) {
         my $title=$student->{$field};          my $title=$student->{$field};
         my $base =scalar(my @Tmp = split(//,$title));          my $base = length($title);
         my $width=$Apache::lonstatistics::StudentData{$field}->{'width'};          my $width=$Apache::lonstatistics::StudentData{$field}->{'width'};
         $Str .= $title.' 'x($width-$base).$padding;          $Str .= $title.' 'x($width-$base).$padding;
     }      }
Line 304  sub ChartOutputStudent { Line 424  sub ChartOutputStudent {
     }      }
     if (scalar(@tmp) < 1) {      if (scalar(@tmp) < 1) {
         $Str .= '<font color="blue">No Course Data</font>'."\n";          $Str .= '<font color="blue">No Course Data</font>'."\n";
         return $Str;          $r->print($Str);
           $r->rflush();
           return;
     }      }
     #      #
     # By sequence build up the data      # By sequence build up the data
     my $studentstats;      my $studentstats;
     foreach my $map_symb (@Apache::lonstatistics::SelectedMaps) {      my $PerformanceStr = '';
         foreach my $seq (@Apache::lonstatistics::Sequences) {      foreach my $seq (&get_sequences_to_show) {
             next if ($map_symb ne $seq->{'symb'} && $map_symb ne 'all');          my ($performance,$score,$seq_max) =
             next if ($seq->{'num_assess'} < 1);              &StudentPerformanceOnSequence($student,\%StudentsData,
             my ($performance,$score,$seq_max) =                                            $seq,$show_links);
                 &StudentPerformanceOnSequence($student,\%StudentsData,          my $ratio = $score.'/'.$seq_max;
                                               $seq,'linkify');          #
             $Str .= $performance.$padding;          if ($show eq 'totals') {
             $studentstats->{$seq->{'symb'}}->{'score'}= $score;              $performance = ' 'x(length($seq_max)-length($score)).$ratio;
             $studentstats->{$seq->{'symb'}}->{'max'}  = $seq_max;              $performance .= ' 'x($seq->{'width'}-length($performance));
           } elsif ($show eq 'scores') {
               $performance = $score;
               $performance .= ' 'x($seq->{'width'}-length($performance));
           } else {
               # Pad with extra spaces
               $performance .= ' 'x($seq->{'width'}-$seq_max-
                                    length($ratio)
                                    ).$ratio;
         }          }
           #
           $Str .= $performance.$padding;
           #
           $studentstats->{$seq->{'symb'}}->{'score'}= $score;
           $studentstats->{$seq->{'symb'}}->{'max'}  = $seq_max;
     }      }
     #      #
     # Total it up and store the statistics info.      # Total it up and store the statistics info.
Line 330  sub ChartOutputStudent { Line 465  sub ChartOutputStudent {
         $score += $seq_stats->{'score'};          $score += $seq_stats->{'score'};
         $max   += $seq_stats->{'max'};          $max   += $seq_stats->{'max'};
     }      }
     my $scorelength = scalar(my @tmp1 = split(//,$score));      $Str .= ' '.' 'x(length($max)-length($score)).$score.'/'.$max;
     my $maxlength   = scalar(my @tmp2 = split(//,$max));  
     $Str .= ' '.' 'x($maxlength-$scorelength).$score.'/'.$max;  
     $Str .= " \n";      $Str .= " \n";
     return $Str;      $r->print($Str);
       #
       $count++;
       if($count % 5 == 0) {
           $r->print("</pre><pre>");
       }
       #
       $r->rflush();
       return;
 }      }    
   
   sub html_finish {
       my ($r) = @_;
       $r->print("</pre>\n"); 
       $r->rflush();
       return;
   }
   
   }
   
 #######################################################  #######################################################
 #######################################################  #######################################################
   
 =pod  =pod
   
 =back  =head2 EXCEL subroutines
   
   =item &excel_initialize($r)
   
   =item &excel_outputstudent($r,$student)
   
   =item &excel_finish($r)
   
 =cut  =cut
   
 #######################################################  #######################################################
 #######################################################  #######################################################
   {
   
   my $excel_sheet;
   my $excel_workbook;
   
   my $filename;
   my $rows_output;
   my $cols_output;
   
   my $num_students;
   my $start_time;
   
   sub excel_initialize {
       my ($r) = @_;
       #
       $filename = '/prtspool/'.
           $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'.
               time.'_'.rand(1000000000).'.xls';
       #
       $excel_workbook = undef;
       $excel_sheet = undef;
       #
       $rows_output = 0;
       $cols_output = 0;
       #
       $num_students = 0;
       $start_time   = time;
       #
       # Create sheet
       $excel_workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
       #
       # Check for errors
       if (! defined($excel_workbook)) {
           $r->log_error("Error creating excel spreadsheet $filename: $!");
           $r->print("Problems creating new Excel file.  ".
                     "This error has been logged.  ".
                     "Please alert your LON-CAPA administrator");
           return ;
       }
       #
       # The excel spreadsheet stores temporary data in files, then put them
       # together.  If needed we should be able to disable this (memory only).
       # The temporary directory must be specified before calling 'addworksheet'.
       # File::Temp is used to determine the temporary directory.
       $excel_workbook->set_tempdir($Apache::lonnet::tmpdir);
       #
       # Add a worksheet
       $excel_sheet = $excel_workbook->addworksheet
           ($ENV{'course.'.$ENV{'request.course.id'}.'.description'});
       #
       # Add the student headers
       foreach my $field (&get_student_fields_to_show()) {
           $excel_sheet->write(1,$cols_output++,$field);
       }
       #
       # Add the Sequence Headers
       foreach my $seq (&get_sequences_to_show) {
           $excel_sheet->write(0,$cols_output,$seq->{'title'});
           if ($show eq 'totals') {
               $excel_sheet->write(1,$cols_output,'score');
               $excel_sheet->write(1,$cols_output+1,'maximum');
               $cols_output += 2;
           } else {
               $cols_output++;
           }
       }
       #
       # Bookkeeping
       if ($show eq 'totals') {
           $rows_output = 2;
       } else {
           $rows_output = 1;
       }
       #
       # Let the user know what we are doing
       my $studentcount = scalar(@Apache::lonstatistics::Students); 
       $r->print("<h1>Compiling Excel spreadsheet for ".
                 $studentcount.' student');
       $r->print('s') if ($studentcount > 1);
       $r->print("</h1>\n");
       $r->rflush();
       #
       return;
   }
   
   sub excel_outputstudent {
       my ($r,$student) = @_;
       return if (! defined($excel_sheet));
       $cols_output=0;
       #
       # Write out student data
       my @to_show = &get_student_fields_to_show();
       foreach my $field (@to_show) {
           $excel_sheet->write($rows_output,$cols_output++,$student->{$field});
       }
       #
       # Get student assessment data
       my %StudentsData;
       my @tmp = &Apache::loncoursedata::get_current_state($student->{'username'},
                                                           $student->{'domain'},
                                                           undef,
                                                      $ENV{'request.course.id'});
       if ((scalar @tmp > 0) && ($tmp[0] !~ /^error:/)) {
           %StudentsData = @tmp;
       }
       #
       # Write out sequence scores and totals data
       foreach my $seq (&get_sequences_to_show) {
           my ($performance,$score,$seq_max) =
               &StudentPerformanceOnSequence($student,\%StudentsData,
                                             $seq,'no');
           if ($show eq 'totals' || $show eq 'scores') {
               $excel_sheet->write($rows_output,$cols_output++,$score);
           }
           if ($show eq 'totals') {
               $excel_sheet->write($rows_output,$cols_output++,$seq_max);
           }
       }
       #
       # Bookkeeping
       $rows_output++; 
       $cols_output=0;
       #
       # Time estimate
       $num_students++;
       if ($num_students % 10 == 0) {
           my $time_est = (time - $start_time)/$num_students * 
               (scalar(@Apache::lonstatistics::Students)-$num_students);
           $time_est = int($time_est);
           if (int ($time_est/60) > 0) {
               my $min = int($time_est/60);
               my $sec = $time_est % 60;
               $time_est = $min.' minutes';
               if ($sec > 1) {
                   $time_est.= ', '.$sec.' seconds';
               } elsif ($sec > 0) {
                   $time_est.= ', '.$sec.' second';
               }
           } else {
               $time_est .= ' seconds';
           }
           $r->print($num_students.' out of '.
                     (scalar(@Apache::lonstatistics::Students)).
                     " students processed.  ".
                     $time_est." remain.  <br />\n");
           $r->rflush();
       }
       return;
   }
   
   sub excel_finish {
       my ($r) = @_;
       return if (! defined($excel_sheet));
       #
       # Write the excel file
       $excel_workbook->close();
       my $c = $r->connection();
       #
       return if($c->aborted());
       #
       # Tell the user where to get their excel file
       $r->print('<br /><br />'.
                 '<a href="'.$filename.'">Your Excel spreadsheet.</a>'."\n");
       my $total_time = time - $start_time;
       if (int ($total_time / 60) > 0) {
           $total_time = int($total_time/60).' minutes, '.($total_time % 60);
       }
       $r->print('<br />'.$total_time.' seconds total');
       $r->rflush();
       return;
   }
   
   }
   #######################################################
   #######################################################
   
   =pod
   
   =head2 CSV output routines
   
   =item &csv_initialize($r)
   
   =item &csv_outputstudent($r,$student)
   
   =item &csv_finish($r)
   
   =cut
   
   #######################################################
   #######################################################
   {
   
   sub csv_initialize{
       my ($r) = @_;
       $r->print("<h1>Not implemented yet</h1>");
       return;
   }
   
   sub csv_outputstudent {
       my ($r,$student) = @_;
   }
   
   sub csv_finish {
       my ($r) = @_;
   }
   
 }  }
   
Line 378  Inputs: Line 739  Inputs:
 #######################################################  #######################################################
 sub StudentPerformanceOnSequence {  sub StudentPerformanceOnSequence {
     my ($student,$studentdata,$seq,$links) = @_;      my ($student,$studentdata,$seq,$links) = @_;
       $links = 'no' if (! defined($links));
     my $Str = '';      my $Str = '';
     my $output_width = 0;  
     my ($sum,$max) = (0,0);      my ($sum,$max) = (0,0);
     foreach my $resource (@{$seq->{'contents'}}) {      foreach my $resource (@{$seq->{'contents'}}) {
         next if ($resource->{'type'} ne 'assessment');          next if ($resource->{'type'} ne 'assessment');
Line 426  sub StudentPerformanceOnSequence { Line 787  sub StudentPerformanceOnSequence {
                 }                  }
             }              }
             #              #
             $output_width++;              if ($links eq 'yes' && $symbol ne ' ') {
             if (defined($links) && $symbol ne ' ') {  
                 $symbol = '<a href="/adm/grades'.                  $symbol = '<a href="/adm/grades'.
                     '?symb='.&Apache::lonnet::escape($resource->{'symb'}).                      '?symb='.&Apache::lonnet::escape($resource->{'symb'}).
                         '&student='.$student->{'username'}.                          '&student='.$student->{'username'}.
Line 438  sub StudentPerformanceOnSequence { Line 798  sub StudentPerformanceOnSequence {
         }          }
         $Str .= $value;          $Str .= $value;
     }      }
     # Put on the totals  
     my $ratio = $sum.'/'.$max;  
     my $ratio_length = scalar(my @tmp1 = split(//,$ratio));  
     # Pad with extra spaces  
     my $width = $seq->{'width'};  
     $Str .= ' 'x($width-$output_width-$ratio_length).$ratio;  
     #  
     return ($Str,$sum,$max);      return ($Str,$sum,$max);
 }  }
   

Removed from v.1.30  
changed lines
  Added in v.1.32


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>