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

version 1.4, 2002/07/26 19:49:26 version 1.30, 2003/02/28 21:19:00
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  
 #  #
 ###  #######################################################
   #######################################################
   
 package Apache::lonstudentassessment;   =pod
   
 use strict;  =head1 NAME
 use Apache::lonhtmlcommon;  
 use Apache::loncoursedata;  
 use GDBM_File;  
   
 #my $jr;  lonstudentassessment
   
 sub BuildStudentAssessmentPage {  =head1 SYNOPSIS
     my ($cacheDB,$students,$courseID,$formName,$headings,$spacing,  
         $studentInformation,$r,$c)=@_;  
 #    $jr = $r;  
     my %cache;  
     unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {  
         $r->print('<html><body>Unable to tie database.</body></html>');  
         return;  
     }  
   
     # Remove students who don't have the proper section.  
     my @sectionsSelected = split(':',$cache{'sectionsSelected'});  
     for(my $studentIndex=((scalar @$students)-1); $studentIndex>=0;  
         $studentIndex--) {  
         my $value = $cache{$students->[$studentIndex].':section'};  
         my $found = 0;  
         foreach (@sectionsSelected) {  
             if($_ eq 'none') {  
                 if($value eq '' || !defined($value) || $value eq ' ') {  
                     $found = 1;  
                     last;  
                 }  
             } else {  
                 if($value eq $_) {  
                     $found = 1;  
                     last;  
                 }  
             }  
         }  
         if($found == 0) {  
             splice(@$students, $studentIndex, 1);  
         }  
     }  
     my ($infoHeadings, $infoKeys, $sequenceHeadings, $sequenceKeys,  
         $doNotShow) =   
         &ShouldShowColumns(\%cache, $headings, $studentInformation);  
   
     my $selectedName = &FindSelectedStudent(\%cache,   
                                             $cache{'StudentAssessmentStudent'},  
                                             $students);  
     $r->print(&CreateInterface(\%cache, $selectedName, $students, $formName,  
                                $doNotShow));  
   
     my $Str = '';  Presents assessment data about a student or a group of students.
     if($selectedName eq 'No Student Selected') {  
  $Str .= '<h3><font color=blue>WARNING: ';  
         $Str .= 'Please select a student</font></h3>';  
         $r->print($Str);  
         return;  
     }  
   
     $r->print(&CreateTableHeadings(\%cache, $spacing, $infoKeys, $infoHeadings,  =head1 Subroutines
                                    $sequenceKeys, $sequenceHeadings));  
     untie(%cache);  
   
     my $selected=0;  =over 4 
     $r->print('<pre>'."\n");  
     foreach (@$students) {  
         next if ($_ ne $selectedName &&   
                  $selectedName ne 'All Students');  
         $selected = 1;  
         my $courseData;   
         if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {  
             if($cache{$_.':lastDownloadTime'} eq 'Not downloaded') {  
                 untie(%cache);  
                 $courseData =   
                     &Apache::loncoursedata::DownloadCourseInformation($_,   
                                                                     $courseID);  
                 if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_WRCREAT,0640)) {  
                     &Apache::loncoursedata::ProcessStudentData(\%cache,   
                                                              $courseData, $_);  
                     untie(%cache);  
                 } else {  
                     last if($c->aborted());  
                     next;  
                 }  
             } else {  
                 untie(%cache);  
             }  
         } else {  
             last if($c->aborted());  
             next;  
         }  
   
         last if ($c->aborted());  
   
         if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {  
             my $displayString = 'DISPLAYDATA'.$spacing;  
             $r->print(&Apache::lonhtmlcommon::FormatStudentInformation(  
                                                          \%cache, $_,  
                                                          $infoKeys,  
                                                          $displayString,  
                                                          'preformatted'));  
             $r->print(&StudentReport(\%cache, $_, $spacing, $sequenceKeys));  
             $r->print("\n");  
             untie(%cache);  
         }  
     }  
     $r->print('</pre>'."\n");  
     if($selected == 0) {  
  $Str .= '<h3><font color=blue>WARNING: ';  
         $Str .= 'Please select a student</font></h3>';  
         $r->print($Str);  
     }  
   
     return;  =cut
 }  
   
 #---- Student Assessment Web Page --------------------------------------------  
   
 sub CreateInterface {  
     my($cache,$selectedName,$students,$formName,$doNotShow)=@_;  
   
     my $Str = '';  #######################################################
     $Str .= &CreateLegend();  #######################################################
     $Str .= '<table><tr><td>'."\n";  
     $Str .= '<input type="submit" name="PreviousStudent" ';  
     $Str .= 'value="Previous Student" />'."\n";  
     $Str .= '&nbsp&nbsp&nbsp'."\n";  
     $Str .= &Apache::lonhtmlcommon::StudentOptions($cache, $students,   
                                                    $selectedName,   
                                                    'StudentAssessment',   
                                                    $formName);  
     $Str .= "\n".'&nbsp&nbsp&nbsp'."\n";  
     $Str .= '<input type="submit" name="NextStudent" ';  
     $Str .= 'value="Next Student" />'."\n";  
     $Str .= '</td></tr></table>'."\n";  
     $Str .= '<table cellspacing="5"><tr>'."\n";  
     $Str .= '<td align="center"><b>Select Sections</b>'."\n";  
     $Str .= '</td>'."\n";  
     $Str .= '<td align="center"><b>Select column to view:</b></td>'."\n";  
     $Str .= '<td></td></tr>'."\n";  
   
     $Str .= '<tr><td align="center">'."\n";  package Apache::lonstudentassessment;
     my @sections = split(':',$cache->{'sectionList'});  
     my @selectedSections = split(':',$cache->{'sectionsSelected'});  
     $Str .= &Apache::lonhtmlcommon::MultipleSectionSelect(\@sections,  
                                                           \@selectedSections,  
                                                           'Statistics');  
     $Str .= '</td><td align="center">';  
     $Str .= &CreateColumnSelectionBox($doNotShow);  
     $Str .= '</td><td>'."\n";  
     $Str .= '<input type="submit" name="DefaultColumns" ';  
     $Str .= 'value="Default Column Display" />'."\n";  
     $Str .= '</td></tr></table>'."\n";  
   
     return $Str;  use strict;
 }  use Apache::lonstatistics;
   use Apache::lonhtmlcommon;
   use Apache::loncoursedata;
   use Apache::lonnet; # for logging porpoises
   use GDBM_File;
   
 sub CreateTableHeadings {  my $Statistics;
     my($cache,$spacing,$infoKeys,$infoHeadings,$sequenceKeys,  
        $sequenceHeadings)=@_;  
   
     my $Str = '';  #######################################################
     $Str .= '<table border="0" cellpadding="0" cellspacing="0">'."\n";  #######################################################
   
     $Str .= '<tr>'."\n";  =pod
     $Str .= &CreateColumnSelectors($infoHeadings, $sequenceHeadings,  
                                    $sequenceKeys);  
     $Str .= '<td></td></tr>'."\n";  
   
     $Str .= '<tr>'."\n";  
     my $displayString = '<td align="left"><pre><a href="/adm/statistics?';  
     $displayString .= 'sort=LINKDATA">DISPLAYDATA</a>FORMATTING';  
     $displayString .= $spacing.'</pre></td>'."\n";  
     $Str .= &Apache::lonhtmlcommon::CreateHeadings($cache,   
                                                    $infoKeys,  
                                                    $infoHeadings,  
                                                    $displayString,  
                                                    'preformatted');  
   
     $displayString  = '<td align="left"><pre>DISPLAYDATA'.$spacing;  
     $displayString .= '</pre></td>'."\n";  
     $Str .= &Apache::lonhtmlcommon::CreateHeadings($cache,  
                                                    $sequenceKeys,  
                                                    $sequenceHeadings,  
                                                    $displayString,  
                                                    'preformatted');  
   
     $Str .= '<td><pre>Total Solved/Total Problems</pre></td>';  =item &BuildStudentAssessmentPage()
     $Str .= '</tr></table>'."\n";  
   
     return $Str;  Inputs: 
 }  
   
 =pod  =over 4
   
 =item &FormatStudentData()  =item $cacheDB The name of the cache file used to store student data
   
 First, FormatStudentInformation is called and prefixes the course information.  =item $students Array ref containing the name(s) of the students 
 This function produces a formatted string of the student's course information.  selected for display
 Each column of data represents all the problems for a given sequence.  For  
 valid grade data, a link is created for that problem to a submission record  
 for that problem.  
   
 =over 4  =item $courseID The ID of the course
   
 Input: $name, $studentInformation, $ChartDB  =item $formName The name of the html form - 'Statistics'
   
 $name: The name and domain of the current student in name:domain format  =item $headings Array ref of headings to show
   
 $studentInformation: A pointer to an array holding the names used to   =item $spacing A string of spaces
 remove data from the hash.  They represent   
 the name of the data to be removed.  
   
 $ChartDB: The name of the cached data database which will be tied to that   =item $studentInformation Array ref of possible headings for student info
 database.  ('fullname','section',...)
   
 Output: $Str  =item $r Apache Request
   
 $Str: Formatted string that is an entire row of the chart.  It is a   =item $c Apache Connection 
 concatenation of student information and student course information.  
   
 =back  =back
   
 =cut  =cut
   
 sub StudentReport {  #######################################################
     my ($cache,$name,$spacing,$showSequences)=@_;  #######################################################
     my ($username,$domain)=split(':',$name);  sub BuildStudentAssessmentPage {
       my ($r,$c)=@_;
       undef($Statistics);
       
       #
       $r->print(&CreateInterface());
       $r->rflush();
       #
       $r->print(&CreateTableHeadings());
       if($c->aborted()) {  return ; }
       
       my $Count = 0;
       $r->print('<pre>'."\n");
       foreach my $student (@Apache::lonstatistics::Students) {
           if($c->aborted()) { return ; }
           $r->print(&ChartOutputStudent($student));
           # output it
   
           $Count++;
           if($Count % 5 == 0) {
               $r->print("</pre>\n<pre>");
           }
   
     my $Str = '';          $r->rflush();
     if($cache->{$name.':error'} =~ /course/) {  
         $Str .= '<b><font color="blue">No course data for student </font>';  
         $Str .= '<font color="red">'.$username.'.</font></b><br>';  
         return $Str;  
     }      }
       $r->print('</pre>'."\n"); 
       my $Str;
       return;
   }
   
     my $Version;  #######################################################
     my $problemsCorrect = 0;  #######################################################
     my $totalProblems   = 0;  
     my $problemsSolved  = 0;  
     my $numberOfParts   = 0;  
 #    foreach my $sequence (split(':', $cache->{'orderedSequences'})) {  
     foreach my $sequence (@$showSequences) {  
         my $characterCount=0;  
         foreach my $problemID (split(':', $cache->{$sequence.':problems'})) {  
             my $problem = $cache->{$problemID.':problem'};  
             my $LatestVersion = $cache->{$name.':version:'.$problem};  
   
             # Output dashes for all the parts of this problem if there  
             # is no version information about the current problem.  
             if(!$LatestVersion) {  
                 foreach my $part (split(/\:/,$cache->{$sequence.':'.  
                                                       $problemID.  
                                                       ':parts'})) {  
                     $Str .= ' ';  
                     $totalProblems++;  
                     $characterCount++;  
                 }  
                 next;  
             }  
   
             my %partData=undef;  =pod
             # 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  =item &CreateInterface()
             # oldest version.  Basically, it gets the most recent   
             # set of grade data for each part.  
     for(my $Version=1; $Version<=$LatestVersion; $Version++) {  
                 foreach my $part (split(/\:/,$cache->{$sequence.':'.  
                                                       $problemID.  
                                                       ':parts'})) {  
   
                     if(!defined($cache->{$name.":$Version:$problem".  
                                                ":resource.$part.solved"})) {  
                         # No grade for this submission, so skip  
                         next;  
                     }  
   
                     my $tries=0;  Called by &BuildStudentAssessmentPage to create the top part of the
                     my $code=' ';  page which displays the chart.
   
                     $tries = $cache->{$name.':'.$Version.':'.$problem.  Inputs: None
                                       ':resource.'.$part.'.tries'};  
                     $partData{$part.':tries'}=($tries) ? $tries : 0;  
   
                     my $val = $cache->{$name.':'.$Version.':'.$problem.  
                                        ':resource.'.$part.'.solved'};  
                     if    ($val eq 'correct_by_student')   {$code = '*';}   
                     elsif ($val eq 'correct_by_override')  {$code = '+';}  
                     elsif ($val eq 'incorrect_attempted')  {$code = '.';}   
                     elsif ($val eq 'incorrect_by_override'){$code = '-';}  
                     elsif ($val eq 'excused')              {$code = 'x';}  
                     elsif ($val eq 'ungraded_attempted')   {$code = '#';}  
                     else                                   {$code = ' ';}  
                     $partData{$part.':code'}=$code;  
                 }  
             }  
   
             # All grades (except for versionless parts) are displayed as links  Returns:  A string containing the HTML for the headers and top table for 
             # to their submission record.  Loop through all the parts for the  the chart page.
             # current problem in the correct order and prepare the output links  
             $Str .= '<a href="/adm/grades?symb=';  
             $Str .= &Apache::lonnet::escape($problem);  
             $Str .= '&student='.$username.'&domain='.$domain;  
             $Str .= '&command=submission">';   
             foreach(split(/\:/,$cache->{$sequence.':'.$problemID.  
                                         ':parts'})) {  
                 if($partData{$_.':code'} eq '*') {  
                     $problemsCorrect++;  
                     if (($partData{$_.':tries'}<10) &&  
                         ($partData{$_.':tries'} ne '')) {  
                         $partData{$_.':code'}=$partData{$_.':tries'};  
                     }  
                 } elsif($partData{$_.':code'} eq '+') {  
                     $problemsCorrect++;  
                 }  
   
                 $Str .= $partData{$_.':code'};  =cut
                 $characterCount++;  
   
                 if($partData{$_.':code'} ne 'x') {  #######################################################
                     $totalProblems++;  #######################################################
                 }  sub CreateInterface {
             }      my $Str = '';
             $Str.='</a>';  #    $Str .= &CreateLegend();
       $Str .= '<table cellspacing="5">'."\n";
       $Str .= '<tr>';
       $Str .= '<td align="center"><b>Sections</b></td>';
       $Str .= '<td align="center"><b>Student Data</b></td>';
       $Str .= '<td align="center"><b>Sequences and Folders</b></td>';
       $Str .= '</tr>'."\n";
       #
       $Str .= '<tr><td align="center">'."\n";
       $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5);
       $Str .= '</td><td align="center">';
       my $only_seq_with_assessments = sub { 
           my $s=shift;
           if ($s->{'num_assess'} < 1) { 
               return 0;
           } else { 
               return 1;
         }          }
       };
         # Output the number of correct answers for the current sequence.      $Str .= &Apache::lonstatistics::StudentDataSelect('StudentData','multiple',
         # This part takes up 6 character slots, but is formated right                                                         5,undef);
         # justified.      $Str .= '</td><td>'."\n";
         my $spacesNeeded=$cache->{$sequence.':columnWidth'}-$characterCount;      $Str .= &Apache::lonstatistics::MapSelect('Maps','multiple,all',5,
         $spacesNeeded -= 3;                                                $only_seq_with_assessments);
         $Str .= (' 'x$spacesNeeded);      $Str .= '</td></tr>'."\n";
       $Str .= '</table>'."\n";
  my $outputProblemsCorrect = sprintf( "%3d", $problemsCorrect );  
  $Str .= '<font color="#007700">'.$outputProblemsCorrect.'</font>';  
  $problemsSolved += $problemsCorrect;  
  $problemsCorrect=0;  
   
         $Str .= $spacing;  
     }  
   
     # Output the total correct problems over the total number of problems.  
     # I don't like this type of formatting, but it is a solution.  Need  
     # a way to dynamically determine the space requirements.  
     my $outputProblemsSolved = sprintf( "%4d", $problemsSolved );  
     my $outputTotalProblems  = sprintf( "%4d", $totalProblems );  
     $Str .= '<font color="#000088">'.$outputProblemsSolved.  
     ' / '.$outputTotalProblems.'</font>';  
   
     return $Str;      return $Str;
 }  }
   
   
   #######################################################
   #######################################################
   
 =pod  =pod
   
 =item &CreateLegend()  =item Table Output Routines
   
 This function returns a formatted string containing the legend for the  =over 4
 chart.  The legend describes the symbols used to represent grades for  
 problems.  
   
 =cut  =cut
   
 sub CreateLegend {  #######################################################
     my $Str = "<p><pre>".  #######################################################
               "1..9: correct by student in 1..9 tries\n".  {
               "   *: correct by student in more than 9 tries\n".      my $padding;
       "   +: correct by override\n".  
               "   -: incorrect by override\n".  #######################################################
       "   .: incorrect attempted\n".  #######################################################
       "   #: ungraded attempted\n".  
               "    : not attempted\n".  
       "   x: excused".  
               "</pre><p>";   
     return $Str;  
 }  
   
 =pod  =pod
   
 =item &CreateColumnSelectionBox()  =item &CreateTableHeadings()
   
   Create HTML for the columns of student data to show.
   Called by &BuildStudentAssessmentPage().  Calls
   &Apache::lonhtmlcommon::CreateHeadings().
   
 If there are columns not being displayed then this selection box is created  Inputs:
 with a list of those columns.  When selections are made and the page   
 refreshed, the columns will be removed from this box and the column is  
 put back in the chart.  If there is no columns to select, no row is added  
 to the interface table.  
   
 =over 4  =over 4
 Input: $CacheData, $headings  
   
   =item $cache The ubiquitous cache
   
   =item $spacing A string of spaces
   
 $CacheData: A pointer to a hash tied to the cached data  =item $infoKeys Array ref to names of keys to display from the cache 
   which describe students
   
 $headings:  An array of the names of the columns for the student information.    =item $infoHeadings Array ref to headings of columns for student info
 They are used for displaying which columns are missing.  
   
 Output: $notThere  =item $sequenceKeys Array ref of names of keys to use to retrieve sequence
   data from the cache
   
 $notThere: The string contains one row of a table.  The first column has the   =item $sequenceHeadings Array ref of names of sequences used for output.
 name of the selection box.  The second contains the selection box   
 which has a size of four.  
   
 =back  =back
   
   Returns: A string containing the HTML of the table headings.
   
 =cut  =cut
   
 sub CreateColumnSelectionBox {  #######################################################
     my ($doNotShow)=@_;  #######################################################
   sub CreateTableHeadings {
       #
       $padding = ' 'x3;
       #
       my $Str = '<pre>';
       # First, the @StudentData fields need to be listed
       my @to_show = @Apache::lonstatistics::SelectedStudentData;
       foreach (@to_show) {
           if ($_ eq 'all') {
               @to_show = @Apache::lonstatistics::StudentDataOrder;
               last;
           }
       }
       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 $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);
               my $title = $sequence->{'title'};
               my $base  = $sequence->{'base_width'};
               my $width = $sequence->{'width'};
               $Str .= $title.' 'x($width-$base).$padding;
           }
       }
       $Str .= 'total';
       $Str .= "</pre>\n";
       return $Str;
   }
   
     my $notThere = '';  #######################################################
     $notThere .= '<select name="ReselectColumns" size="4" ';  #######################################################
     $notThere .= 'multiple="true">'."\n";  
   
     for(my $index=0; $index<$doNotShow->{'count'}; $index++) {  =pod
         my $name = $doNotShow->{$index.':name'};  
         $notThere .= '<option value="';  
         $notThere .= $doNotShow->{$index.':id'}.'">';  
         $notThere .= $name.'</option>'."\n";  
     }  
   
     $notThere .= '</select>';  =item &ChartOutputStudent($student)
   
     return $notThere;  Return a line of the chart for a student.
 }  
   
 =pod  =cut
   
 =item &CreateColumnSelectors()  #######################################################
   #######################################################
   sub ChartOutputStudent {
       my $student = shift;
       my $Str = '';
       # First, the @StudentData fields need to be listed
       my @to_show = @Apache::lonstatistics::SelectedStudentData;
       foreach (@to_show) {
           if ($_ eq 'all') {
               @to_show = @Apache::lonstatistics::StudentDataOrder;
               last;
           }
       }
       foreach my $field (@to_show) {
           my $title=$student->{$field};
           my $base =scalar(my @Tmp = split(//,$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";
           return $Str;
       }
       #
       # By sequence build up the data
       my $studentstats;
       foreach my $map_symb (@Apache::lonstatistics::SelectedMaps) {
           foreach my $seq (@Apache::lonstatistics::Sequences) {
               next if ($map_symb ne $seq->{'symb'} && $map_symb ne 'all');
               next if ($seq->{'num_assess'} < 1);
               my ($performance,$score,$seq_max) =
                   &StudentPerformanceOnSequence($student,\%StudentsData,
                                                 $seq,'linkify');
               $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'};
       }
       my $scorelength = scalar(my @tmp1 = split(//,$score));
       my $maxlength   = scalar(my @tmp2 = split(//,$max));
       $Str .= ' '.' 'x($maxlength-$scorelength).$score.'/'.$max;
       $Str .= " \n";
       return $Str;
   }    
   
 This function generates the checkboxes above the column headings.  The   #######################################################
 column will be removed if the checkbox is unchecked.  #######################################################
   
 =over 4  =pod
   
 Input: $CacheData, $headings  =back
   
 $CacheData: A pointer to a hash tied to the cached data  =cut
   
 $headings:  An array of the names of the columns for the student   #######################################################
 information.  They are used to know what are the student information columns  #######################################################
   
 Output: $present  }
   
 $present: The string contains the first row of a table.  Each column contains  #######################################################
 a checkbox which is left justified.  Currently left justification is used  #######################################################
 for consistency of location over the column in which it presides.  
   
 =back  =pod
   
 =cut  =item &StudentPerformanceOnSequence()
   
 sub CreateColumnSelectors {  Inputs:
     my ($infoHeadings, $sequenceHeadings, $sequenceKeys)=@_;  
   
     my $present = '';  =over 4
     for(my $index=0; $index<(scalar @$infoHeadings); $index++) {  
         $present .= '<td align="left">';  
         $present .= '<input type="checkbox" checked="on" ';  
         $present .= 'name="HeadingColumn'.$infoHeadings->[$index].'" />';  
         $present .= '</td>'."\n";  
     }  
   
     for(my $index=0; $index<(scalar @$sequenceHeadings); $index++) {  =item $student
         $present .= '<td align="left">';  
         $present .= '<input type="checkbox" checked="on" ';  
         $present .= 'name="SequenceColumn'.$sequenceKeys->[$index].'" />';  
         $present .= '</td>'."\n";  
     }  
   
     return $present;  =item $studentdata Hash ref to all student data
 }  
   
 #---- END Student Assessment Web Page ----------------------------------------  =item $seq Hash ref, the sequence we are working on
   
 #---- Student Assessment Worker Functions ------------------------------------  =item $links if defined we will output links to each resource.
   
 sub FindSelectedStudent {  =back
     my($cache, $selectedName, $students)=@_;  
   
     if($selectedName eq 'All Students' ||   =cut
        $selectedName eq 'No Student Selected') {  
         return $selectedName;  
     }  
   
     for(my $index=0; $index<(scalar @$students); $index++) {  #######################################################
         my $fullname = $cache->{$students->[$index].':fullname'};  #######################################################
         if($fullname eq $selectedName) {  sub StudentPerformanceOnSequence {
             if($cache->{'StudentAssessmentMove'} eq 'next') {      my ($student,$studentdata,$seq,$links) = @_;
                 if($index == ((scalar @$students) - 1)) {      my $Str = '';
                     $selectedName = $students->[0];      my $output_width = 0;
                     return $selectedName;      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 {                  } else {
                     $selectedName = $students->[$index+1];                      $symbol = ' ';
                     return $selectedName;  
                 }                  }
             } elsif($cache->{'StudentAssessmentMove'} eq 'previous') {              } else {
                 if($index == 0) {                  # Unsolved.  Did they try?
                     $selectedName = $students->[-1];                  if (exists($resource_data->{'resource.'.$partnum.'.tries'})){
                     return $selectedName;                      $symbol = '.';
                 } else {                  } else {
                     $selectedName = $students->[$index-1];                      $symbol = ' ';
                     return $selectedName;  
                 }                  }
             } else {  
                 $selectedName = $students->[$index];  
                 return $selectedName;  
             }              }
             last;              #
               $output_width++;
               if (defined($links) && $symbol ne ' ') {
                   $symbol = '<a href="/adm/grades'.
                       '?symb='.&Apache::lonnet::escape($resource->{'symb'}).
                           '&student='.$student->{'username'}.
                               '&domain='.$student->{'domain'}.
                                   '&command=submission">'.$symbol.'</a>';
               }
               $value .= $symbol;
         }          }
           $Str .= $value;
     }      }
       # Put on the totals
     return 'No Student Selected';      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);
 }  }
   
 =pod  #######################################################
   #######################################################
 =item &ShouldShowColumn()  sub StudentAverageTotal {
       my ($cache, $students, $sequenceKeys)=@_;
 Determine if a specified column should be shown on the chart.      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";
       }
   
 =over 4      $Str .= '</table>'."\n";
   
 Input: $cache, $test      return $Str;
   }
   
 $cache: A pointer to the hash tied to the cached data  #######################################################
   #######################################################
   
 $test: The form name of the column (heading.$headingIndex) or   =pod
 (sequence.$sequenceIndex)  
   
 Output: 0 (false), 1 (true)  =item &CreateLegend()
   
 =back  This function returns a formatted string containing the legend for the
   chart.  The legend describes the symbols used to represent grades for
   problems.
   
 =cut  =cut
   
 sub ShouldShowColumns {  #######################################################
     my ($cache,$headings,$cacheKey)=@_;  #######################################################
   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;
   }
   
     my @infoKeys=();  #######################################################
     my @infoHeadings=();  #######################################################
   
     my @sequenceKeys=();  =pod 
     my @sequenceHeadings=();  
   
     my %doNotShow;  =back
   
     my $index;  
     my $count = 0;  
     my $check = '';  
     for($index=0; $index < scalar @$headings; $index++) {  
         $check = 'HeadingColumn'.$headings->[$index];  
         if($cache->{'HeadingsFound'} =~ /$check/) {  
             push(@infoHeadings, $headings->[$index]);  
             push(@infoKeys, $cacheKey->[$index]);  
         } else {  
             $doNotShow{$count.':name'} = $headings->[$index];  
             $doNotShow{$count.':id'} = 'HeadingColumn'.$headings->[$index];  
             $count++;  
         }  
     }  
   
     foreach my $sequence (split(/\:/,$cache->{'orderedSequences'})) {  
         $check = 'SequenceColumn'.$sequence;  
         if($cache->{'SequencesFound'} eq 'All Sequences' ||   
            $cache->{'SequencesFound'} =~ /$check/) {  
             push(@sequenceHeadings, $cache->{$sequence.':title'});  
             push(@sequenceKeys, $sequence);  
         } else {  
             $doNotShow{$count.':name'} = $cache->{$sequence.':title'};  
             $doNotShow{$count.':id'} = 'SequenceColumn'.$sequence;  
             $count++;  
         }  
     }  
   
     $doNotShow{'count'} = $count;  
   
     return (\@infoHeadings, \@infoKeys, \@sequenceHeadings,   =cut
             \@sequenceKeys, \%doNotShow);  
 }  
   
 #---- END Student Assessment Worker Functions --------------------------------  #######################################################
   #######################################################
   
 1;  1;
   
 __END__  __END__

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


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