Diff for /loncom/interface/statistics/lonstudentassessment.pm between versions 1.1 and 1.31

version 1.1, 2002/07/24 14:52:32 version 1.31, 2003/03/03 19:28:29
Line 1 Line 1
 # The LearningOnline Network with CAPA  # The LearningOnline Network with CAPA
 # (Publication Handler  
 #  #
 # $Id$  # $Id$
 #  #
 # Copyright Michigan State University Board of Trustees  # Copyright Michigan State University Board of Trustees
 #  #
 # This file is part of the LearningOnline Network with CAPA (LON-CAPA).  # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
 #  
 # LON-CAPA is free software; you can redistribute it and/or modify  # LON-CAPA is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by  # it under the terms of the GNU General Public License as published by
 # the Free Software Foundation; either version 2 of the License, or  # the Free Software Foundation; either version 2 of the License, or
Line 26 Line 24
 # http://www.lon-capa.org/  # http://www.lon-capa.org/
 #  #
 # (Navigate problems for statistical reports  # (Navigate problems for statistical reports
 # YEAR=2001  
 # 5/5,7/9,7/25/1,8/11,9/13,9/26,10/5,10/9,10/22,10/26 Behrouz Minaei  
 # 11/1,11/4,11/16,12/14,12/16,12/18,12/20,12/31 Behrouz Minaei  
 # YEAR=2002  
 # 1/22,2/1,2/6,2/25,3/2,3/6,3/17,3/21,3/22,3/26,4/7,5/6 Behrouz Minaei  
 # 5/12,5/14,5/15,5/19,5/26,7/16  Behrouz Minaei  
 #  #
 ###  #######################################################
   #######################################################
   
   =pod
   
   =head1 NAME
   
   lonstudentassessment
   
   =head1 SYNOPSIS
   
   Presents assessment data about a student or a group of students.
   
 package Apache::lonstudentassessment;   =head1 Subroutines
   
   =over 4 
   
   =cut
   
   #######################################################
   #######################################################
   
   package Apache::lonstudentassessment;
   
 use strict;  use strict;
   use Apache::lonstatistics;
 use Apache::lonhtmlcommon;  use Apache::lonhtmlcommon;
 use Apache::loncoursedata;  use Apache::loncoursedata;
 use GDBM_File;  use Apache::lonnet; # for logging porpoises
   use Spreadsheet::WriteExcel;
   
 sub BuildStudentAssessmentPage {  #######################################################
     my ($cacheDB, $students, $courseID, $c)=@_;  #######################################################
   =pod
   
     my %cache;  =item Package Variables
   
     my $Ptr = '';  =over 4
     $Ptr .= '<table border="0"><tbody>';  
   
     unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {  =item $Statistics Hash ref to store student data.  Indexed by symb,
         return '<html><body>Unable to tie database.</body></html>';        contains hashes with keys 'score' and 'max'.
     }  
   =cut
     my $selectedName = $cache{'StudentAssessmentStudent'};  
     for(my $index=0;   #######################################################
         ($selectedName ne 'All Students') && ($index<(scalar @$students));   #######################################################
         $index++) {  
         my $fullname = $cache{$students->[$index].':fullname'};  my $Statistics;
         if($fullname eq $selectedName) {  
             if($cache{'StudentAssessmentMove'} eq 'next') {  #######################################################
                 if($index == ((scalar @$students) - 1)) {  #######################################################
                     $selectedName = $students->[0];  
                 } else {  =pod
                     $selectedName = $students->[$index+1];  
                 }  =item $show_links 'yes' or 'no' for linking to student performance data
             } elsif($cache{'StudentAssessmentMove'} eq 'previous') {  
                 if($index == 0) {  =item $output_mode 'html', 'excel', or 'csv' for output mode
                     $selectedName = $students->[-1];  
                 } else {  =item $show 'all' or 'totals' determines how much data is output
                     $selectedName = $students->[$index-1];  
                 }  =cut
             } else {  
                 $selectedName = $students->[$index];  #######################################################
             }  #######################################################
   my $show_links;
   my $output_mode;
   my $show;
   
   #######################################################
   #######################################################
   # End of package variable declarations
   
   =pod
   
   =back
   
   =cut
   
   #######################################################
   #######################################################
   
   =pod
   
   =item &BuildStudentAssessmentPage()
   
   Inputs: 
   
   =over 4
   
   =item $r Apache Request
   
   =item $c Apache Connection 
   
   =back
   
   =cut
   
   #######################################################
   #######################################################
   sub BuildStudentAssessmentPage {
       my ($r,$c)=@_;
       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('<input type="hidden" name="notfirstrun" value="true" />');
       $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;
       }
       #
       if($c->aborted()) {  return ; }
       #
       # Call the initialize routine selected above
       $initialize->($r);
       foreach my $student (@Apache::lonstatistics::Students) {
           if($c->aborted()) { 
               $finish->($r);
               return ; 
           }
           # Call the output_student routine selected above
           $output_student->($r,$student);
       }
       # Call the "finish" routine selected above
       $finish->($r);
       #
       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;              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;
   }
   
   
     $Ptr .= '<tr><td align="right"><b>Select Map</b></td>'."\n";  #######################################################
     $Ptr .= '<td align="left">';  #######################################################
     $Ptr .= &Apache::lonhtmlcommon::MapOptions(\%cache, 'StudentAssessment');  
     $Ptr .= '</td></tr>'."\n";  
     $Ptr .= '<tr><td align="right"><b>Select Student</b></td>'."\n";  
     $Ptr .= '<td align="left">'."\n";  
     $Ptr .= &Apache::lonhtmlcommon::StudentOptions(\%cache, $students,   
                                                    $selectedName,   
                                                    'StudentAssessment');  
     $Ptr .= '</td></tr>'."\n";  
     untie(%cache);  
   
     $Ptr .= '<tr><td></td><td align="left">';  
     $Ptr .= '<input type="submit" name="CreateStudentAssessment" ';  
     $Ptr .= 'value="Create Student Report" />';  
     $Ptr .= '&nbsp&nbsp&nbsp';  
     $Ptr .= '<input type="submit" name="PreviousStudent" ';  
     $Ptr .= 'value="Previous Student" />';  
     $Ptr .= '&nbsp&nbsp&nbsp';  
     $Ptr .= '<input type="submit" name="NextStudent" ';  
     $Ptr .= 'value="Next Student" />';  
     $Ptr .= '&nbsp&nbsp&nbsp';  
     $Ptr .= '</td></tr></tbody></table>'."\n";  
   
     if($selectedName eq 'No Student Selected') {  
  $Ptr .= '<h3><font color=blue>WARNING: ';  
         $Ptr .= 'Please select a student</font></h3>';  
         return $Ptr;  
     }  
   
     my $selected=0;  
     foreach (@$students) {  
         next if ($_ ne $selectedName &&   
                  $selectedName ne 'All Students');  
         $selected = 1;  
         my $courseData =   
             &Apache::loncoursedata::DownloadCourseInformation($_, $courseID);  
         last if ($c->aborted());  
         if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_WRCREAT,0640)) {  
             &Apache::loncoursedata::ProcessStudentData(\%cache,   
                                                        $courseData, $_);  
             if(!$c->aborted()) { $Ptr .= &StudentReport(\%cache, $_); }  
             untie(%cache);  
         }  
     }  
     if($selected == 0) {  
  $Ptr .= '<h3><font color=blue>WARNING: ';  
         $Ptr .= 'Please select a student</font></h3>';  
     }  
   
     return $Ptr;  
 }  
   
 #---- Student Assessment Web Page --------------------------------------------  
   
 # ------ Create different Student Report   
 sub StudentReport {  
     my ($cache, $name)=@_;  
   
   =pod
   
   =item &CreateInterface()
   
   Called by &BuildStudentAssessmentPage to create the top part of the
   page which displays the chart.
   
   Inputs: None
   
   Returns:  A string containing the HTML for the headers and top table for 
   the chart page.
   
   =cut
   
   #######################################################
   #######################################################
   sub CreateInterface {
     my $Str = '';      my $Str = '';
     if($cache->{$name.':error'} =~ /course/) {  #    $Str .= &CreateLegend();
         my ($username)=split(':',$name);      $Str .= '<table cellspacing="5">'."\n";
         $Str .= '<b><font color="blue">No course data for student </font>';      $Str .= '<tr>';
         $Str .= '<font color="red">'.$username.'.</font></b><br>';      $Str .= '<td align="center"><b>Sections</b></td>';
         return $Str;      $Str .= '<td align="center"><b>Student Data</b></td>';
     }      $Str .= '<td align="center"><b>Sequences and Folders</b></td>';
       $Str .= '<td align="center"><b>Output Format</b></td>';
     $Str .= "<table border=2><tr><th> \# </th><th> Set Title </th>";      $Str .= '</tr>'."\n";
     $Str .= '<th> Results </th><th> Tries </th></tr>'."\n";      #
       $Str .= '<tr><td align="center">'."\n";
     my $codes;      $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5);
     my $attempts;      $Str .= '</td><td align="center">';
     foreach my $sequence (split(':', $cache->{'orderedSequences'})) {      my $only_seq_with_assessments = sub { 
         if($cache->{'StudentAssessmentMap'} ne 'All Maps'  &&          my $s=shift;
            $cache->{'StudentAssessmentMap'} ne $cache->{$sequence.':title'}) {          if ($s->{'num_assess'} < 1) { 
             next;              return 0;
         }          } else { 
               return 1;
         $Str .= '<tr><td>'.$sequence.'</td>';          }
         $Str .= '<td>'.$cache->{$sequence.':title'}.'</td>';      };
       $Str .= &Apache::lonstatistics::StudentDataSelect('StudentData','multiple',
         $codes = '';                                                        5,undef);
         $attempts = '';      $Str .= '</td><td>'."\n";
         foreach my $problemID (split(':', $cache->{$sequence.':problems'})) {      $Str .= &Apache::lonstatistics::MapSelect('Maps','multiple,all',5,
             my $problem = $cache->{$problemID.':problem'};                                                $only_seq_with_assessments);
             my $LatestVersion = $cache->{$name.':version:'.$problem};      $Str .= '</td><td>'."\n";
       $Str .= &CreateAndParseOutputSelector();
             # Output dashes for all the parts of this problem if there      $Str .= '</td></tr>'."\n";
             # is no version information about the current problem.      $Str .= '</table>'."\n";
             if(!$LatestVersion) {      return $Str;
                 foreach my $part (split(/\:/,$cache->{$sequence.':'.  }
                                                       $problemID.  
                                                       ':parts'})) {  
     $codes    .= "-,";  
                     $attempts .= "0,";   
                 }  
                 next;  
             }  
   
             my %partData=undef;  #######################################################
             # Initialize part data, display skips correctly  #######################################################
             # Skip refers to when a student made no submissions on that  
             # part/problem.  
             foreach my $part (split(/\:/,$cache->{$sequence.':'.  
                                                   $problemID.  
                                                   ':parts'})) {  
                 $partData{$part.':tries'}=0;  
                 $partData{$part.':code'}='-';  
             }  
   
             # Looping through all the versions of each part, starting with the  =pod
             # oldest version.  Basically, it gets the most recent   
             # set of grade data for each part.  =item &CreateAndParseOutputSelector()
     for(my $Version=1; $Version<=$LatestVersion; $Version++) {  
                 foreach my $part (split(/\:/,$cache->{$sequence.':'.  =cut
                                                       $problemID.  
                                                       ':parts'})) {  #######################################################
   #######################################################
                     if(!defined($cache->{$name.":$Version:$problem".  sub CreateAndParseOutputSelector {
                                                ":resource.$part.solved"})) {      my $Str = '';
                         # No grade for this submission, so skip      my $elementname = 'outputmode';
                         next;      #
                     }      # Format for output options is 'mode, restrictions';
       my @Options = ('html, with links','html, without links',
                      'html, totals only','excel, totals only',
                      'csv, totals only','csv, everything');
       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 only') {
           $show = 'totals';
       } else {
           $show = 'everything';
       }
       #
       # Build the form element
       $Str = qq/<select size="5" name="$elementname">/;
       foreach my $option (@Options) {
           $Str .= qq/\n    <option value="$option"/;
           $Str .= " selected " if ($option eq $selected);
           $Str .= ">$option<\/option>";
       }
       $Str .= "\n</select>";
       return $Str;
   }
   
   #######################################################
   #######################################################
   
   =pod
   
   =head2 HTML output routines
   
   =item &html_initialize($r)
   
   Create labels for the columns of student data to show.
   
   =item &html_outputstudent($r,$student)
   
   Return a line of the chart for a student.
   
                     my $tries=0;  =item &html_finish($r)
                     my $code='U';  
   
                     $tries = $cache->{$name.":$Version:$problem".  =cut
                                       ":resource.$part.tries"};  
                     $partData{$part.':tries'}=($tries) ? $tries : 0;  #######################################################
   #######################################################
                     my $val = $cache->{$name.":$Version:$problem".  {
                                        ":resource.$part.solved"};      my $padding;
                     if    ($val eq 'correct_by_student')   {$code = 'Y';}       my $count;
                     elsif ($val eq 'correct_by_override')  {$code = 'y';}  
                     elsif ($val eq 'incorrect_attempted')  {$code = 'N';}   sub html_initialize {
                     elsif ($val eq 'incorrect_by_override'){$code = 'N';}      my ($r) = @_;
                     elsif ($val eq 'excused')              {$code = 'x';}      #
                     $partData{$part.':code'}=$code;      $padding = ' 'x3;
       $count = 1;
       #
       my $Str = "<pre>\n";
       # First, the @StudentData fields need to be listed
       my @to_show = &get_student_fields_to_show();
       foreach my $field (@to_show) {
           my $title=$Apache::lonstatistics::StudentData{$field}->{'title'};
           my $base =$Apache::lonstatistics::StudentData{$field}->{'base_width'};
           my $width=$Apache::lonstatistics::StudentData{$field}->{'width'};
           $Str .= $title.' 'x($width-$base).$padding;
       }
       # Now the selected sequences need to be listed
       foreach my $sequence (&get_sequences_to_show) {
           my $title = $sequence->{'title'};
           my $base  = $sequence->{'base_width'};
           my $width = $sequence->{'width'};
           $Str .= $title.' 'x($width-$base).$padding;
       }
       $Str .= "total (of shown problems)</pre>\n";
       $Str .= "<pre>";
       $r->print($Str);
       $r->rflush();
       return;
   }
   
   sub html_outputstudent {
       my ($r,$student) = @_;
       my $Str = '';
       # First, the @StudentData fields need to be listed
       my @to_show = &get_student_fields_to_show();
       foreach my $field (@to_show) {
           my $title=$student->{$field};
           my $base = length($title);
           my $width=$Apache::lonstatistics::StudentData{$field}->{'width'};
           $Str .= $title.' 'x($width-$base).$padding;
       }
       # Get ALL the students 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;
       }
       if (scalar(@tmp) < 1) {
           $Str .= '<font color="blue">No Course Data</font>'."\n";
           $r->print($Str);
           $r->rflush();
           return;
       }
       #
       # By sequence build up the data
       my $studentstats;
       my $PerformanceStr = '';
       foreach my $seq (&get_sequences_to_show) {
           my ($performance,$score,$seq_max) =
               &StudentPerformanceOnSequence($student,\%StudentsData,
                                             $seq,$show_links);
           my $ratio = $score.'/'.$seq_max;
           #
           if ($show eq 'totals') {
               $performance = ' 'x(length($seq_max)-length($score)).$ratio;
               $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.
       my ($score,$max) = (0,0);
       while (my ($symb,$seq_stats) = each (%{$studentstats})) {
           $Statistics->{$symb}->{'score'} += $seq_stats->{'score'};
           $Statistics->{$symb}->{'max'}   += $seq_stats->{'max'};
           $score += $seq_stats->{'score'};
           $max   += $seq_stats->{'max'};
       }
       $Str .= ' '.' 'x(length($max)-length($score)).$score.'/'.$max;
       $Str .= " \n";
       $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
   
   =head2 EXCEL subroutines
   
   =item &excel_initialize($r)
   
   =item &excel_outputstudent($r,$student)
   
   =item &excel_finish($r)
   
   =cut
   
   #######################################################
   #######################################################
   {
   
   my $excel_sheet;
   
   sub excel_initialize {
       my ($r) = @_;
       #
       $r->print("<h1>Not implemented yet</h1>");
       return;
       my $filename = '/prtspool/'.
           $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'.
               time.'_'.rand(1000000000).'.xls';
       $excel_sheet = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
       if (! defined($excel_sheet)) {
           $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 0;
       }
       #
       # 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_sheet->set_tempdir($Apache::lonnet::tmpdir);
       #
       # Determine the name to give the worksheet
   #    $excel_sheet->addworksheet();
   
       return;
   }
   
   sub excel_outputstudent {
       my ($r,$student) = @_;
   }
   
   sub excel_finish {
       my ($r) = @_;
   }
   
   }
   #######################################################
   #######################################################
   
   =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) = @_;
   }
   
   }
   
   #######################################################
   #######################################################
   
   =pod
   
   =item &StudentPerformanceOnSequence()
   
   Inputs:
   
   =over 4
   
   =item $student
   
   =item $studentdata Hash ref to all student data
   
   =item $seq Hash ref, the sequence we are working on
   
   =item $links if defined we will output links to each resource.
   
   =back
   
   =cut
   
   #######################################################
   #######################################################
   sub StudentPerformanceOnSequence {
       my ($student,$studentdata,$seq,$links,$totalonly) = @_;
       $totalonly = 0 if (! defined($totalonly));
       $links = 'no' if (! defined($links));
       my $Str = '';
       my ($sum,$max) = (0,0);
       foreach my $resource (@{$seq->{'contents'}}) {
           next if ($resource->{'type'} ne 'assessment');
           my $resource_data = $studentdata->{$resource->{'symb'}};
           my $value = '';
           foreach my $partnum (@{$resource->{'parts'}}) {
               $max++;
               my $symbol = ' '; # default to space
               #
               if (exists($resource_data->{'resource.'.$partnum.'.solved'})) {
                   my $status = $resource_data->{'resource.'.$partnum.'.solved'};
                   if ($status eq 'correct_by_override') {
                       $symbol = '+';
                       $sum++;
                   } elsif ($status eq 'incorrect_by_override') {
                       $symbol = '-';
                   } elsif ($status eq 'ungraded_attempted') {
                       $symbol = '#';
                   } elsif ($status eq 'incorrect_attempted')  {
                       $symbol = '.';
                   } elsif ($status eq 'excused') {
                       $symbol = 'x';
                       $max--;
                   } elsif ($status eq 'correct_by_student' &&
                       exists($resource_data->{'resource.'.$partnum.'.tries'})){
                       my $num = $resource_data->{'resource.'.$partnum.'.tries'};
                       if ($num > 9) {
                           $symbol = '*';
                       } elsif ($num > 0) {
                           $symbol = $num;
                       } else {
                           $symbol = ' ';
                       }
                       $sum++;
                   } else {
                       $symbol = ' ';
                   }
               } else {
                   # Unsolved.  Did they try?
                   if (exists($resource_data->{'resource.'.$partnum.'.tries'})){
                       $symbol = '.';
                   } else {
                       $symbol = ' ';
                 }                  }
             }              }
               #
             # Loop through all the parts for the current problem in the               if ($links eq 'yes' && $symbol ne ' ') {
             # correct order and prepare the output                  $symbol = '<a href="/adm/grades'.
             foreach (split(/\:/,$cache->{$sequence.':'.$problemID.                      '?symb='.&Apache::lonnet::escape($resource->{'symb'}).
                                          ':parts'})) {                          '&student='.$student->{'username'}.
                 $codes    .= $partData{$_.':code'}.',';                              '&domain='.$student->{'domain'}.
                 $attempts .= $partData{$_.':tries'}.',';                                   '&command=submission">'.$symbol.'</a>';
             }              }
               $value .= $symbol;
         }          }
         $codes    =~ s/,$//;          $Str .= $value;
         $attempts =~ s/,$//;      }
         $Str .= '<td>'.$codes.'</td>';      return ($Str,$sum,$max);
         $Str .= '<td>'.$attempts.'</td>';  }
         $Str .= '</tr>'."\n";  
   #######################################################
   #######################################################
   sub StudentAverageTotal {
       my ($cache, $students, $sequenceKeys)=@_;
       my $Str = "\n<b>Summary Tables:</b>\n";
       my %Correct = ();
       my $ProblemsSolved = 0;
       my $TotalProblems = 0;
       my $StudentCount = 0;
   
       foreach my $name (@$students) {
           $StudentCount++;
           foreach my $sequence (@$sequenceKeys) {
               $Correct{$sequence} +=
          $cache->{$name.':'.$sequence.':problemsCorrect'};
           }
    $ProblemsSolved += $cache->{$name.':problemsSolved'};
           $TotalProblems += $cache->{$name.':totalProblems'};
       }
       if ($StudentCount) { 
           $ProblemsSolved = sprintf( "%.2f", 
                                $ProblemsSolved/$StudentCount);
           $TotalProblems /= $StudentCount;
       } else {
           $ProblemsSolved = 0;
           $TotalProblems  = 0;
       }
   
       $Str .= '<table border=2 cellspacing="1">'."\n";
       $Str .= '<tr><td><b>Students Count</b></td><td><b>'.
               $StudentCount.'</b></td></tr>'."\n";
       $Str .= '<tr><td><b>Total Problems</b></td><td><b>'.
               $TotalProblems.'</b></td></tr>'."\n";
       $Str .= '<tr><td><b>Average Correct</b></td><td><b>'.
               $ProblemsSolved.'</b></td></tr>'."\n";
       $Str .= '</table>'."\n";
   
       $Str .= '<table border=2 cellspacing="1">'."\n";
       $Str .= '<tr><th>Title</th><th>Total Problems</th>'.
               '<th>Average Correct</th></tr>'."\n";
       foreach my $S(@$sequenceKeys) {
           my $title=$cache->{$S.':title'};
    #$Str .= $cache->{$S.':problems'};
    #my @problems=split(':', $cache->{$S.':problems'});
    #my $pCount=scalar @problems;
    my $pCount=MaxSeqPr($cache,@$students[0],$S);
           my $crr;
    if ($StudentCount) {
               $crr=sprintf( "%.2f", $Correct{$S}/$StudentCount );
           } else {
               $crr="0.00";
           }
           $Str .= '<tr><td>'.$title.
                   '</td><td align=center>'.$pCount.
                   '</td><td align=center>'.$crr.
                   '</td></tr>'."\n";
     }      }
   
     $Str .= '</table>'."\n";      $Str .= '</table>'."\n";
Line 241  sub StudentReport { Line 702  sub StudentReport {
     return $Str;      return $Str;
 }  }
   
 #---- END Student Assessment Web Page ----------------------------------------  #######################################################
   #######################################################
   
   =pod
   
   =item &CreateLegend()
   
   This function returns a formatted string containing the legend for the
   chart.  The legend describes the symbols used to represent grades for
   problems.
   
   =cut
   
   #######################################################
   #######################################################
   sub CreateLegend {
       my $Str = "<p><pre>".
                 "   1  correct by student in 1 try\n".
                 "   7  correct by student in 7 tries\n".
                 "   *  correct by student in more than 9 tries\n".
         "   +  correct by hand grading or override\n".
                 "   -  incorrect by override\n".
         "   .  incorrect attempted\n".
         "   #  ungraded attempted\n".
                 "      not attempted (blank field)\n".
         "   x  excused".
                 "</pre><p>";
       return $Str;
   }
   
   #######################################################
   #######################################################
   
   =pod 
   
   =back
   
   =cut
   
   #######################################################
   #######################################################
   
 1;  1;
   
 __END__  __END__

Removed from v.1.1  
changed lines
  Added in v.1.31


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