--- loncom/interface/statistics/lonstudentassessment.pm	2002/07/24 14:52:32	1.1
+++ loncom/interface/statistics/lonstudentassessment.pm	2002/10/23 21:04:26	1.16
@@ -1,12 +1,11 @@
 # The LearningOnline Network with CAPA
 # (Publication Handler
 #
-# $Id: lonstudentassessment.pm,v 1.1 2002/07/24 14:52:32 stredwic Exp $
+# $Id: lonstudentassessment.pm,v 1.16 2002/10/23 21:04:26 minaeibi Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
 # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
-#
 # 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
 # the Free Software Foundation; either version 2 of the License, or
@@ -42,205 +41,576 @@ use Apache::lonhtmlcommon;
 use Apache::loncoursedata;
 use GDBM_File;
 
-sub BuildStudentAssessmentPage {
-    my ($cacheDB, $students, $courseID, $c)=@_;
+#my $jr;
 
+sub BuildStudentAssessmentPage {
+    my ($cacheDB,$students,$courseID,$formName,$headings,$spacing,
+        $studentInformation,$r,$c)=@_;
+#    $jr = $r;
     my %cache;
-
-    my $Ptr = '';
-    $Ptr .= '<table border="0"><tbody>';
-
-    unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {
-        return '<html><body>Unable to tie database.</body></html>';
+    unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
+        $r->print('<html><body>Unable to tie database.</body></html>');
+        return;
     }
 
-    my $selectedName = $cache{'StudentAssessmentStudent'};
-    for(my $index=0; 
-        ($selectedName ne 'All Students') && ($index<(scalar @$students)); 
-        $index++) {
-        my $fullname = $cache{$students->[$index].':fullname'};
-        if($fullname eq $selectedName) {
-            if($cache{'StudentAssessmentMove'} eq 'next') {
-                if($index == ((scalar @$students) - 1)) {
-                    $selectedName = $students->[0];
-                } else {
-                    $selectedName = $students->[$index+1];
-                }
-            } elsif($cache{'StudentAssessmentMove'} eq 'previous') {
-                if($index == 0) {
-                    $selectedName = $students->[-1];
-                } else {
-                    $selectedName = $students->[$index-1];
+    # 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 {
-                $selectedName = $students->[$index];
+                if($value eq $_) {
+                    $found = 1;
+                    last;
+                }
             }
-            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));
+    $r->rflush();
 
-    $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";
-
+    my $Str = '';
     if($selectedName eq 'No Student Selected') {
-	$Ptr .= '<h3><font color=blue>WARNING: ';
-        $Ptr .= 'Please select a student</font></h3>';
-        return $Ptr;
+	$Str .= '<h3><font color=blue>WARNING: ';
+        $Str .= 'Please select a student</font></h3>';
+        $r->print($Str);
+        return;
     }
 
+    $r->print(&CreateTableHeadings(\%cache, $spacing, $infoKeys, $infoHeadings,
+                                   $sequenceKeys, $sequenceHeadings));
+    untie(%cache);
+    if($c->aborted()) {  return $Str; }
+
     my $selected=0;
+    $r->print('<pre>'."\n");
     foreach (@$students) {
+        if($c->aborted()) { return $Str; }
         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, $_); }
+
+        my @who = ($_);
+        next if(&Apache::loncoursedata::DownloadStudentCourseData(\@who, 'true', 
+                                                             $cacheDB, 'true', 
+                                                             'false', $courseID,
+                                                             $r, $c) ne 'OK');
+        next if($c->aborted());
+
+        if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
+            my @before=();
+            my @after=();
+            my @updateColumn=();
+            my $foundUpdate = 0;
+            foreach(@$infoKeys) {
+                if(/updateTime/) {
+                    $foundUpdate=1;
+                    push(@updateColumn, $_);
+                    next;
+                }
+                if($foundUpdate) {
+                    push(@after, $_);
+                } else {
+                    push(@before, $_);
+                }
+            }
+            my $displayString = 'DISPLAYDATA'.$spacing;
+            $r->print(&Apache::lonhtmlcommon::FormatStudentInformation(
+                                                         \%cache, $_,
+                                                         \@before,
+                                                         $displayString,
+                                                         'preformatted'));
+
+            if($foundUpdate) {
+                $displayString = '';
+                $displayString .= '<a href="/adm/statistics?reportSelected=';
+                $displayString .= &Apache::lonnet::escape('Student Assessment');
+                $displayString .= '&download='.$_.'">';
+                $displayString .= 'DISPLAYDATA</a>'.$spacing;
+                $r->print(&Apache::lonhtmlcommon::FormatStudentInformation(
+                                                                   \%cache, $_,
+                                                                   \@updateColumn,
+                                                                   $displayString,
+                                                                   'preformatted'));
+            }
+
+            $displayString = 'DISPLAYDATA'.$spacing;
+            $r->print(&Apache::lonhtmlcommon::FormatStudentInformation(
+                                                         \%cache, $_,
+                                                         \@after,
+                                                         $displayString,
+                                                         'preformatted'));
+            $r->print(&StudentReport(\%cache, $_, $spacing, $sequenceKeys));
+            $r->print("\n");
+            $r->rflush();
             untie(%cache);
         }
     }
+
+
+    $r->print(&StudentReport(\%cache, "AvgTotal", $spacing, $sequenceKeys));
+    $r->print("\n");
+    $r->rflush();
+    untie(%cache);
+
+
+    $r->print('</pre>'."\n");
     if($selected == 0) {
-	$Ptr .= '<h3><font color=blue>WARNING: ';
-        $Ptr .= 'Please select a student</font></h3>';
+	$Str .= '<h3><font color=blue>WARNING: ';
+        $Str .= 'Please select a student</font></h3>';
+        $r->print($Str);
     }
 
-    return $Ptr;
+    return;
 }
 
 #---- Student Assessment Web Page --------------------------------------------
 
-# ------ Create different Student Report 
+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";
+    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;
+}
+
+sub CreateTableHeadings {
+    my($cache,$spacing,$infoKeys,$infoHeadings,$sequenceKeys,
+       $sequenceHeadings)=@_;
+
+    my $Str = '';
+    $Str .= '<table border="0" cellpadding="0" cellspacing="0">'."\n";
+
+    $Str .= '<tr>'."\n";
+    $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>DISPLAYDATAFORMATTING'.$spacing;
+    $displayString .= '</pre></td>'."\n";
+    $Str .= &Apache::lonhtmlcommon::CreateHeadings($cache,
+                                                   $sequenceKeys,
+                                                   $sequenceHeadings,
+                                                   $displayString,
+                                                   'preformatted');
+
+    $Str .= '<td><pre>Total Solved/Total Problems</pre></td>';
+    $Str .= '</tr></table>'."\n";
+
+    return $Str;
+}
+
+=pod
+
+=item &FormatStudentData()
+
+First, FormatStudentInformation is called and prefixes the course information.
+This function produces a formatted string of the student's course information.
+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
+
+Input: $name, $studentInformation, $ChartDB
+
+$name: The name and domain of the current student in name:domain format
+
+$studentInformation: A pointer to an array holding the names used to 
+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 
+database.
+
+Output: $Str
+
+$Str: Formatted string that is an entire row of the chart.  It is a 
+concatenation of student information and student course information.
+
+=back
+
+=cut
+
 sub StudentReport {
-    my ($cache, $name)=@_;
+    my ($cache,$name,$spacing,$showSequences)=@_;
+    my ($username,$domain)=split(':',$name);
 
     my $Str = '';
+    if(defined($cache->{$name.':error'})) {
+        return $Str;
+    }
     if($cache->{$name.':error'} =~ /course/) {
-        my ($username)=split(':',$name);
         $Str .= '<b><font color="blue">No course data for student </font>';
         $Str .= '<font color="red">'.$username.'.</font></b><br>';
         return $Str;
     }
 
-    $Str .= "<table border=2><tr><th> \# </th><th> Set Title </th>";
-    $Str .= '<th> Results </th><th> Tries </th></tr>'."\n";
+    my $hasVersion = 'false';
+    my $hasFinalData = 'false';
+    foreach my $sequence (@$showSequences) {
+        my $hasData = 'false';
+        my $characterCount=0;
+        foreach my $problemID (split(':', $cache->{$sequence.':problems'})) {
+            my $problem = $cache->{$problemID.':problem'};
+            # All grades (except for versionless parts) are displayed as links
+            # to their submission record.  Loop through all the parts for the
+            # current problem in the correct order and prepare the output links
+            foreach(split(/\:/,$cache->{$sequence.':'.$problemID.
+                                        ':parts'})) {
+                if($cache->{$name.':'.$problemID.':NoVersion'} eq 'true' ||
+                   $cache->{$name.':'.$problemID.':'.$_.':code'} eq ' ' ||
+                   $cache->{$name.':'.$problemID.':'.$_.':code'} eq '') {
+                    $Str .= ' ';
+                    $characterCount++;
+                    next;
+                }
+                $hasVersion = 'true';
+                $hasData = 'true';
+                $Str .= '<a href="/adm/grades?symb=';
+                $Str .= &Apache::lonnet::escape($problem);
+                $Str .= '&student='.$username.'&domain='.$domain;
+                $Str .= '&command=submission">'; 
+                my $code = $cache->{$name.':'.$problemID.':'.$_.':code'};
+                my $tries = $cache->{$name.':'.$problemID.':'.$_.':tries'};
+                if($code eq '*' && $tries < 10 && $tries ne '') {
+                    $code = $tries;
+                }
+                $Str .= $code;
+                $Str .= '</a>';
+                $characterCount++;
+            }
+        }
 
-    my $codes;
-    my $attempts;
-    foreach my $sequence (split(':', $cache->{'orderedSequences'})) {
-        if($cache->{'StudentAssessmentMap'} ne 'All Maps'  &&
-           $cache->{'StudentAssessmentMap'} ne $cache->{$sequence.':title'}) {
-            next;
+        # Output the number of correct answers for the current sequence.
+        # This part takes up 6 character slots, but is formated right 
+        # justified.
+        my $spacesNeeded=$cache->{$sequence.':columnWidth'}-$characterCount;
+        $spacesNeeded -= 3;
+        $Str .= (' 'x$spacesNeeded);
+
+#        my $outputProblemsCorrect = sprintf("%3d", $cache->{$name.':'.$sequence.
+#							    ':problemsCorrect'});
+
+	my $outputProblemsCorrect = sprintf("%2d/%2d", $cache->{$name.':'.$sequence.
+                                            ':problemsCorrect'}, 
+                                            $characterCount);
+        if($hasData eq 'true') {
+            $Str .= '<font color="#007700">'.$outputProblemsCorrect.'</font>';
+            $hasFinalData = 'true';
+        } else {
+            $Str .= '<font color="#007700">     </font>';
         }
+        $Str .= $spacing;
+    }
 
-        $Str .= '<tr><td>'.$sequence.'</td>';
-        $Str .= '<td>'.$cache->{$sequence.':title'}.'</td>';
+    # 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", $cache->{$name.':problemsSolved'});
+    my $outputTotalProblems  = sprintf("%4d", $cache->{$name.':totalProblems'});
+    if($hasFinalData eq 'true') {
+        $Str .= '<font color="#000088">'.$outputProblemsSolved.
+	    ' / '.$outputTotalProblems.'</font>';
+    } else {
+        $Str .= '<font color="#000088">           </font>';
+    }
 
-        $codes = '';
-        $attempts = '';
-        foreach my $problemID (split(':', $cache->{$sequence.':problems'})) {
-            my $problem = $cache->{$problemID.':problem'};
-            my $LatestVersion = $cache->{$name.':version:'.$problem};
+    if($hasVersion eq 'false') {
+        $Str = '<b><font color="blue">No course data.</font></b>';
+    }
 
-            # 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'})) {
-		    $codes    .= "-,";
-                    $attempts .= "0,"; 
-                }
-                next;
-            }
+    return $Str;
+}
 
-            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'}='-';
-            }
+=pod
+
+=item &CreateLegend()
 
-            # Looping through all the versions of each part, starting with the
-            # 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;
-                    my $code='U';
-
-                    $tries = $cache->{$name.":$Version:$problem".
-                                      ":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 = 'Y';} 
-                    elsif ($val eq 'correct_by_override')  {$code = 'y';}
-                    elsif ($val eq 'incorrect_attempted')  {$code = 'N';} 
-                    elsif ($val eq 'incorrect_by_override'){$code = 'N';}
-                    elsif ($val eq 'excused')              {$code = 'x';}
-                    $partData{$part.':code'}=$code;
+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 override\n".
+              "   -  incorrect by override\n".
+	      "   .  incorrect attempted\n".
+	      "   #  ungraded attempted\n".
+              "      not attempted (blank field)\n".
+	      "   x  excused".
+              "</pre><p>"; 
+    return $Str;
+}
+
+=pod
+
+=item &CreateColumnSelectionBox()
+
+If there are columns not being displayed then this selection box is created
+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
+Input: $CacheData, $headings
+
+
+$CacheData: A pointer to a hash tied to the cached data
+
+$headings:  An array of the names of the columns for the student information.  
+They are used for displaying which columns are missing.
+
+Output: $notThere
+
+$notThere: The string contains one row of a table.  The first column has the 
+name of the selection box.  The second contains the selection box 
+which has a size of four.
+
+=back
+
+=cut
+
+sub CreateColumnSelectionBox {
+    my ($doNotShow)=@_;
+
+    my $notThere = '';
+    $notThere .= '<select name="ReselectColumns" size="4" ';
+    $notThere .= 'multiple="true">'."\n";
+
+    for(my $index=0; $index<$doNotShow->{'count'}; $index++) {
+        my $name = $doNotShow->{$index.':name'};
+        $notThere .= '<option value="';
+        $notThere .= $doNotShow->{$index.':id'}.'">';
+        $notThere .= $name.'</option>'."\n";
+    }
+
+    $notThere .= '</select>';
+
+    return $notThere;
+}
+
+=pod
+
+=item &CreateColumnSelectors()
+
+This function generates the checkboxes above the column headings.  The 
+column will be removed if the checkbox is unchecked.
+
+=over 4
+
+Input: $CacheData, $headings
+
+$CacheData: A pointer to a hash tied to the cached data
+
+$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
+
+=cut
+
+sub CreateColumnSelectors {
+    my ($infoHeadings, $sequenceHeadings, $sequenceKeys)=@_;
+
+    my $present = '';
+    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++) {
+        $present .= '<td align="left">';
+        $present .= '<input type="checkbox" checked="on" ';
+        $present .= 'name="SequenceColumn'.$sequenceKeys->[$index].'" />';
+        $present .= '</td>'."\n";
+    }
+
+    return $present;
+}
+
+#---- END Student Assessment Web Page ----------------------------------------
+
+#---- Student Assessment Worker Functions ------------------------------------
+
+sub FindSelectedStudent {
+    my($cache, $selectedName, $students)=@_;
+
+    if($selectedName eq 'All Students' || 
+       $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) {
+            if($cache->{'StudentAssessmentMove'} eq 'next') {
+                if($index == ((scalar @$students) - 1)) {
+                    $selectedName = $students->[0];
+                    return $selectedName;
+                } else {
+                    $selectedName = $students->[$index+1];
+                    return $selectedName;
                 }
+            } elsif($cache->{'StudentAssessmentMove'} eq 'previous') {
+                if($index == 0) {
+                    $selectedName = $students->[-1];
+                    return $selectedName;
+                } else {
+                    $selectedName = $students->[$index-1];
+                    return $selectedName;
+                }
+            } else {
+                $selectedName = $students->[$index];
+                return $selectedName;
             }
+            last;
+        }
+    }
 
-            # Loop through all the parts for the current problem in the 
-            # correct order and prepare the output
-            foreach (split(/\:/,$cache->{$sequence.':'.$problemID.
-                                         ':parts'})) {
-                $codes    .= $partData{$_.':code'}.',';
-                $attempts .= $partData{$_.':tries'}.','; 
-            }
+    return 'No Student Selected';
+}
+
+=pod
+
+=item &ShouldShowColumn()
+
+Determine if a specified column should be shown on the chart.
+
+=over 4
+
+Input: $cache, $test
+
+$cache: A pointer to the hash tied to the cached data
+
+$test: The form name of the column (heading.$headingIndex) or 
+(sequence.$sequenceIndex)
+
+Output: 0 (false), 1 (true)
+
+=back
+
+=cut
+
+sub ShouldShowColumns {
+    my ($cache,$headings,$cacheKey)=@_;
+
+    my @infoKeys=();
+    my @infoHeadings=();
+
+    my @sequenceKeys=();
+    my @sequenceHeadings=();
+
+    my %doNotShow;
+
+    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++;
         }
-        $codes    =~ s/,$//;
-        $attempts =~ s/,$//;
-        $Str .= '<td>'.$codes.'</td>';
-        $Str .= '<td>'.$attempts.'</td>';
-        $Str .= '</tr>'."\n";
     }
 
-    $Str .= '</table>'."\n";
+    $doNotShow{'count'} = $count;
 
-    return $Str;
+    return (\@infoHeadings, \@infoKeys, \@sequenceHeadings, 
+            \@sequenceKeys, \%doNotShow);
 }
 
-#---- END Student Assessment Web Page ----------------------------------------
+#---- END Student Assessment Worker Functions --------------------------------
+
 1;
 __END__