[LON-CAPA-cvs] cvs: loncom / loncapa_apache.conf /interface loncoursedata.pm lonhtmlcommon.pm lonstatistics.pm mydesk.tab doc/loncapafiles loncapafiles.lpml

stredwic lon-capa-cvs@mail.lon-capa.org
Mon, 22 Jul 2002 20:35:05 -0000


This is a MIME encoded message

--stredwic1027370105
Content-Type: text/plain

stredwic		Mon Jul 22 16:35:05 2002 EDT

  Added files:                 
    /loncom/interface	lonhtmlcommon.pm 

  Modified files:              
    /doc/loncapafiles	loncapafiles.lpml 
    /loncom	loncapa_apache.conf 
    /loncom/interface	loncoursedata.pm lonstatistics.pm mydesk.tab 
  Log:
  Lonchart is now part of lonstatistics.  When the CHRT button is pressed on
  the remote control it will take you to the student assessment page.  In
  the next commit or two, the student assessment page and lonchart will 
  gradually merge, or I will make chart its own report.
  
  I added another perl module, lonhtmlcommon which will contain a series of
  functions that create controls and data layouts for various different
  pieces of information.  This is just a fledgling module.
  
  The biggest feature of this commit is that the downloading of student course
  data has changed slightly.  Now, the classlist will contain all students,
  but will only display the appropriate ones.  Different reports require 
  different amounts of data, so some may only download student course data
  when selected and some will download it all at once.  The downloading 
  will first grab the time stamp on the course.db file for the student and
  compare it will the cached timestamp for that file.  It will only do a
  dump if the data is not yet cached or the timestamp is newer than the one
  stored.  Only when the data is processed is the timestamp update, not
  when downloaded.  The Download/Process duo of function work hand in hand.
  Calling both will be typical even if there was nothing actually downloaded.
  
  The is now a new form, which is a class list with different information. 
  This list also contain the timestamp for each student, when their course
  data was downloaded last.  Having this page is both informational, and
  hopefully will mean that it is not necessary for all the other pages, at least
  as defaults.
  
  Each report now has a common header.  There is currently some white space,
  but it can be filled.  The header has a refresh button, update all student
  data button, a selection of forms and a selection of student status.  The
  update all student data button will download all students (active and expired)
  for the given course.  This allows someone to come to the entry page(
  class list) update all the students while they do stuff and come back.  
  The two selection boxes are special in that they will update the 
  document when changed.  The onchange attribute was tested on mac,
  linux and pc, for the different browsers.  The only noticed difference
  was netscape 4.7 executes the javascript if it you select the same thing, 
  where all others do not.  Also, the same browser causes the first 
  button in the form to be pressed when the document.form.submit()
  javascript is executed for the onchange of the selection box.
  
  There was other cosmetic changes, but were minor.
  
  
--stredwic1027370105
Content-Type: text/plain
Content-Disposition: attachment; filename="stredwic-20020722163505.txt"

Index: doc/loncapafiles/loncapafiles.lpml
diff -u doc/loncapafiles/loncapafiles.lpml:1.142 doc/loncapafiles/loncapafiles.lpml:1.143
--- doc/loncapafiles/loncapafiles.lpml:1.142	Sat Jul 20 13:36:36 2002
+++ doc/loncapafiles/loncapafiles.lpml	Mon Jul 22 16:35:05 2002
@@ -3,7 +3,7 @@
 <!-- loncapafiles.lpml -->
 <!-- Scott Harrison -->
 
-<!-- $Id: loncapafiles.lpml,v 1.142 2002/07/20 17:36:36 harris41 Exp $ -->
+<!-- $Id: loncapafiles.lpml,v 1.143 2002/07/22 20:35:05 stredwic Exp $ -->
 
 <!--
 
@@ -1738,6 +1738,14 @@
 <categoryname>handler</categoryname>
 <description>
 Produces simple LectureOnline-like student assessment performance chart
+</description>
+</file>
+<file>
+<source>loncom/interface/lonhtmlcommon.pm</source>
+<target dist='default'>home/httpd/lib/perl/Apache/lonhtmlcommon.pm</target>
+<categoryname>handler</categoryname>
+<description>
+Contains a set of functions that generate html controls and data layouts
 </description>
 </file>
 <file>
Index: loncom/loncapa_apache.conf
diff -u loncom/loncapa_apache.conf:1.8 loncom/loncapa_apache.conf:1.9
--- loncom/loncapa_apache.conf:1.8	Fri Jul  5 14:41:26 2002
+++ loncom/loncapa_apache.conf	Mon Jul 22 16:35:05 2002
@@ -1,7 +1,7 @@
 ##
 ## loncapa_apache.conf -- Apache HTTP LON-CAPA configuration file
 ##
-## $Id: loncapa_apache.conf,v 1.8 2002/07/05 18:41:26 harris41 Exp $
+## $Id: loncapa_apache.conf,v 1.9 2002/07/22 20:35:05 stredwic Exp $
 ##
 ## 1/11/2002 - Scott Harrison
 ## 2/19/2002 - Scott Harrison
@@ -349,15 +349,6 @@
 PerlAccessHandler       Apache::lonacc
 SetHandler perl-script
 PerlHandler Apache::lonspreadsheet
-ErrorDocument     403 /adm/login
-ErrorDocument     406 /adm/roles
-ErrorDocument	  500 /adm/errorhandler
-</Location>
-
-<Location /adm/chart>
-PerlAccessHandler       Apache::lonacc
-SetHandler perl-script
-PerlHandler Apache::lonchart
 ErrorDocument     403 /adm/login
 ErrorDocument     406 /adm/roles
 ErrorDocument	  500 /adm/errorhandler
Index: loncom/interface/loncoursedata.pm
diff -u loncom/interface/loncoursedata.pm:1.2 loncom/interface/loncoursedata.pm:1.3
--- loncom/interface/loncoursedata.pm:1.2	Wed Jul 17 08:38:25 2002
+++ loncom/interface/loncoursedata.pm	Mon Jul 22 16:35:05 2002
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # (Publication Handler
 #
-# $Id: loncoursedata.pm,v 1.2 2002/07/17 12:38:25 stredwic Exp $
+# $Id: loncoursedata.pm,v 1.3 2002/07/22 20:35:05 stredwic Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -70,7 +70,7 @@
 
 =pod
 
-=item &DownloadNamePIDSection()
+=item &DownloadClasslist()
 
 Collects lastname, generation, middlename, firstname, PID, and section for each
 student from their environment database.  The list of students is built from
@@ -101,12 +101,23 @@
 
 =cut
 
-sub DownloadStudentNamePIDSection {
-    my ($courseID, $c)=@_;
+sub DownloadClasslist {
+    my ($courseID, $lastDownloadTime, $c)=@_;
     my ($courseDomain,$courseNumber)=split(/\_/,$courseID);
+    my %classlist;
 
-    my %classlist=&Apache::lonnet::dump('classlist',$courseDomain,
-                                        $courseNumber);
+    my $modifiedTime = &GetFileTimestamp($courseDomain, $courseNumber,
+                                     'classlist.db', 
+                                     $Apache::lonnet::perlvar{'lonUsersDir'});
+
+    if($lastDownloadTime ne 'Not downloaded' &&
+       $lastDownloadTime >= $modifiedTime && $modifiedTime >= 0) {
+        $classlist{'lastDownloadTime'}=time;
+        $classlist{'UpToDate'} = 'true';
+        return \%classlist;
+    }
+
+    %classlist=&Apache::lonnet::dump('classlist',$courseDomain, $courseNumber);
     my ($checkForError)=keys (%classlist);
     if($checkForError =~ /^(con_lost|error|no_such_host)/i) {
         return \%classlist;
@@ -135,9 +146,12 @@
 
         #Section
         my %section=&Apache::lonnet::dump('roles',$studentDomain,$studentName);
-        $classlist{$name.':section'}=\%section;
+        $classlist{$name.':sections'}=\%section;
     }
 
+    $classlist{'UpToDate'} = 'false';
+    $classlist{'lastDownloadTime'}=time;
+
     return \%classlist;
 }
 
@@ -146,7 +160,9 @@
 =item &DownloadStudentCourseInformation()
 
 Dump of all the course information for a single student.  There is no
-pruning of data, it is all stored in a hash and returned.
+pruning of data, it is all stored in a hash and returned.  It also
+checks the timestamp of the students course database file and only downloads
+if it has been modified since the last download.
 
 =over 4
 
@@ -166,12 +182,23 @@
 =cut
 
 sub DownloadStudentCourseInformation {
-    my ($name,$courseID)=@_;
+    my ($name,$courseID,$lastDownloadTime)=@_;
+    my %courseData;
     my ($studentName,$studentDomain) = split(/\:/,$name);
 
+    my $modifiedTime = &GetFileTimestamp($studentDomain, $studentName,
+                                      $courseID.'.db', 
+                                      $Apache::lonnet::perlvar{'lonUsersDir'});
+    if($lastDownloadTime >= $modifiedTime) {
+        $courseData{'lastDownloadTime'}=time;
+        $courseData{'UpToDate'} = 'true';
+        return \%courseData;
+    }
+
     # Download student course data
-    my %courseData=&Apache::lonnet::dump($courseID,$studentDomain,
-					 $studentName);
+    %courseData=&Apache::lonnet::dump($courseID, $studentDomain, $studentName);
+    $courseData{'UpToDate'} = 'false';
+    $courseData{'lastDownloadTime'}=time;
     return \%courseData;
 }
 
@@ -447,7 +474,6 @@
 
 =back
 
-=cut
 
 sub ProcessSection {
     my ($sectionData,$courseid,$ActiveFlag)=@_;
@@ -455,7 +481,7 @@
     $courseid=~s/^(\w)/\/$1/;
 
     my $cursection='-1';
-    my $oldsection='-1';
+    my $oldend='-1';
     my $status='Expired';
     my $section='';
     foreach my $key (keys (%$sectionData)) {
@@ -485,103 +511,33 @@
                 last;
             }
             if($notactive == 1) {
-                $oldsection=$section;
+                if($end > $oldend) {
+                    $cursection=$section;
+                    $oldend = $end;
+                }
             }
 	}
     }
-    if($status eq $ActiveFlag) {
-        if($cursection eq '-1') {
-            return $oldsection;
-        }
-        return $cursection;
-    }
-    if($ActiveFlag eq 'Any') {
-        if($cursection eq '-1') {
-            return $oldsection;
-        }
-        return $cursection;
-    }
-    return '-1';
-}
-
-=pod
-
-=item &ProcessNamePIDSection()
-
-Takes data downloaded for a student and breaks it up into managable pieces and 
-stored in cache data.  The username, domain, class related date, PID, 
-full name, and section are all processed here.
-
-=over 4
-
-Input: $cache, $studentInformation, $section, $date, $name, $courseID
 
-$cache:  A hash pointer to store the data
-
-$studentInformation:  Student information is what was requested in 
-&DownloadPrerequistedData().  See that function for what data is requested.
-
-$section: A hash pointer to class section related information.
-
-$date:  A composite of the start and end date for this class for this
-student.  Format:  end:start
-
-$name:  the username:domain information
-
-$courseID: The course ID
-
-Output: None
-
-*NOTE:  There is no return value, but if an error occurs a key is added to 
-the cache data with the value being the error message.  The key is 
-username:domain:error.  It will only exist if an error occurs.
-
-=back
+    return ($cursections, $status);
+}
 
 =cut
 
-sub ProcessStudentNamePIDSection {
-    my ($cache,$studentInformation,$section,$date,$name,$courseID,$status)=@_;
-    my ($studentName,$studentDomain) = split(/\:/,$name);
-
-    $cache->{$name.':username'}=$studentName;
-    $cache->{$name.':domain'}=$studentDomain;
-    $cache->{$name.':date'}=$date;
-
-    my ($checkForError)=keys(%$studentInformation);
-    if($checkForError =~ /^(con_lost|error|no_such_host)/i) {
-	$cache->{$name.':error'}=
-	    'Could not download student environment data.';
-	$cache->{$name.':fullname'}='';
-	$cache->{$name.':id'}='';
-    } else {
-	$cache->{$name.':fullname'}=&ProcessFullName(
-                                          $studentInformation->{'lastname'},
-				          $studentInformation->{'generation'},
-				          $studentInformation->{'firstname'},
-                                          $studentInformation->{'middlename'});
-	$cache->{$name.':id'}=$studentInformation->{'id'};
-    }
-
-    my $sec=&ProcessSection($section, $courseID, $status);
-    if($sec != -1) {
-        $cache->{$name.':section'}=$sec;
-    } else {
-        $cache->{$name.':section'}='';
-    }
-
-    return;
-}
-
 =pod
 
-=item &ProcessClassList()
+=item &ProcessClasslist()
 
-Taking the class list dumped from &DownloadPrerequisiteData(), all the 
+Taking the class list dumped from &DownloadClasslist(), all the 
 students and their non-class information is processed using the 
 &ProcessStudentInformation() function.  A date stamp is also recorded for
 when the data was processed.
 
+Takes data downloaded for a student and breaks it up into managable pieces and 
+stored in cache data.  The username, domain, class related date, PID, 
+full name, and section are all processed here.
+
+
 =over 4
 
 Input: $cache, $classlist, $courseID, $ChartDB, $c
@@ -589,7 +545,7 @@
 $cache: A hash pointer to store the data
 
 $classlist:  The hash of data collected about a student from 
-&DownloadPrerequisteData().  The hash contains a list of students, a pointer 
+&DownloadClasslist().  The hash contains a list of students, a pointer 
 to a hash of student information for each student, and each student's section 
 number.
 
@@ -609,28 +565,80 @@
 
 =cut
 
-sub ProcessClassList {
-    my ($cache,$classlist,$courseID,$status,$c)=@_;
+sub ProcessClasslist {
+    my ($cache,$classlist,$courseID,$c)=@_;
     my @names=();
 
+    $cache->{'ClasslistTimeStamp'}=$classlist->{'lastDownloadTime'};
+    if($classlist->{'UpToDate'} eq 'true') {
+        return split(/:::/,$cache->{'NamesOfStudents'});;
+    }
+
     foreach my $name (keys(%$classlist)) {
         if($name =~ /\:section/ || $name =~ /\:studentInformation/ ||
-           $name eq '') {
+           $name eq '' || $name eq 'UpToDate' || $name eq 'lastDownloadTime') {
             next;
         }
         if($c->aborted()) {
-            last;
+            return ();
         }
         push(@names,$name);
-        &ProcessStudentNamePIDSection($cache,
-                                     $classlist->{$name.':studentInformation'},
-                                     $classlist->{$name.':section'},
-                                     $classlist->{$name},
-                                     $name,$courseID,$status);
+        my $studentInformation = $classlist->{$name.':studentInformation'},
+        my $sectionData = $classlist->{$name.':sections'},
+        my $date = $classlist->{$name},
+        my ($studentName,$studentDomain) = split(/\:/,$name);
+
+        $cache->{$name.':username'}=$studentName;
+        $cache->{$name.':domain'}=$studentDomain;
+        if(!defined($cache->{$name.':lastDownloadTime'})) {
+            $cache->{$name.':lastDownloadTime'}='Not downloaded';
+        }
+
+        my ($checkForError)=keys(%$studentInformation);
+        if($checkForError =~ /^(con_lost|error|no_such_host)/i) {
+            $cache->{$name.':error'}=
+                'Could not download student environment data.';
+            $cache->{$name.':fullname'}='';
+            $cache->{$name.':id'}='';
+        } else {
+            $cache->{$name.':fullname'}=&ProcessFullName(
+                                          $studentInformation->{'lastname'},
+                                          $studentInformation->{'generation'},
+                                          $studentInformation->{'firstname'},
+                                          $studentInformation->{'middlename'});
+            $cache->{$name.':id'}=$studentInformation->{'id'};
+        }
+
+        my ($end, $start)=split(':',$date);
+        $courseID=~s/\_/\//g;
+        $courseID=~s/^(\w)/\/$1/;
+
+        my $sec='';
+        foreach my $key (keys (%$sectionData)) {
+            my $value = $sectionData->{$key};
+            if ($key=~/^$courseID(?:\/)*(\w+)*\_st$/) {
+                my $tempsection=$1;
+                if($key eq $courseID.'_st') {
+                    $tempsection='';
+                }
+                my ($dummy,$roleend,$rolestart)=split(/\_/,$value);
+                if($roleend eq $end && $rolestart eq $start) {
+                    $sec = $tempsection;
+                    last;
+                }
+            }
+        }
+
+        my $status='Expired';
+        if(((!$end) || time < $end) && ((!$start) || (time > $start))) {
+            $status='Active';
+        }
+        $cache->{$name.':Status'}=$status;
+        $cache->{$name.':section'}=$sec;
     }
 
-    # Time of download
-    $cache->{'time'}=localtime();
+    $cache->{'ClasslistTimestamp'}=time;
+    $cache->{'NamesOfStudents'}=join(':::',@names);
 
     return @names;
 }
@@ -671,20 +679,25 @@
 sub ProcessStudentData {
     my ($cache,$courseData,$name)=@_;
 
-    my ($checkForError) = keys(%$courseData);
-    if($checkForError =~ /^(con_lost|error|no_such_host)/i) {
-        $cache->{$name.':error'}='Could not download course data.';
-    } else {
-        foreach my $key (keys (%$courseData)) {
-            $cache->{$name.':'.$key}=$courseData->{$key};
-        }
-        if(defined($cache->{'NamesOfStudents'})) {
-            $cache->{'NamesOfStudents'}.=':::'.$name;
-        } else {
-            $cache->{'NamesOfStudents'}=$name;
+    if($courseData->{'UpToDate'} eq 'true') {
+        $cache->{$name.':lastDownloadTime'}=$courseData->{'lastDownloadTime'};
+        return;
+    }
+
+    my @courseKeys = keys(%$courseData);
+
+    foreach (@courseKeys) {
+        if(/^(con_lost|error|no_such_host)/i) {
+            $cache->{$name.':error'}='Could not download course data.';
+            return;
         }
     }
 
+    $cache->{$name.':lastDownloadTime'}=$courseData->{'lastDownloadTime'};
+    foreach (@courseKeys) {
+        $cache->{$name.':'.$_}=$courseData->{$_};
+    }
+
     return;
 }
 
@@ -815,34 +828,24 @@
     return $isCached;
 }
 
-#sub CheckStatus {
-#    my ($name, $data, $status)=@_;
-
-#    if($status eq 'Any') {
-#        my $section = ' ';
-#        foreach (split(':',$data->{$name.':Sections'})) {
-#            if($data->{$name.':'.$_.'Status'} eq 'Active') {
-#                return $_;
-#            }
-#            $section = $_;
-#        }
-#        return $_;
-#    }
-
-#    foreach (split(':',$data->{$name.':Sections'})) {
-#        if($data->{$name.':'.$_.'Status'} eq $status) {
-#            return $_;
-#        }
-#    }
-
-#    foreach (split(':',$data->{$name.':Sections'})) {
-#        if($data->{$name.':'.$_.'Status'} eq 'Any') {
-#            return $_;
-#        }
-#    }
-
-#    return 'not found';
-#}
+sub GetFileTimestamp {
+    my ($studentDomain,$studentName,$filename,$root)=@_;
+    $studentDomain=~s/\W//g;
+    $studentName=~s/\W//g;
+    my $subdir=$studentName.'__';
+    $subdir =~ s/(.)(.)(.).*/$1\/$2\/$3/;
+    my $proname="$studentDomain/$subdir/$studentName";
+    $proname .= '/'.$filename;
+    my @dir = &Apache::lonnet::dirlist($proname, $studentDomain, $studentName,
+                                       $root);
+    my $fileStat = $dir[0];
+    my @stats = split('&', $fileStat);
+    if(@stats) {
+        return $stats[9];
+    } else {
+        return -1;
+    }
+}
 
 # ----- END HELPER FUNCTIONS --------------------------------------------
 
Index: loncom/interface/lonstatistics.pm
diff -u loncom/interface/lonstatistics.pm:1.28 loncom/interface/lonstatistics.pm:1.29
--- loncom/interface/lonstatistics.pm:1.28	Fri Jul 19 14:17:34 2002
+++ loncom/interface/lonstatistics.pm	Mon Jul 22 16:35:05 2002
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # (Publication Handler
 #
-# $Id: lonstatistics.pm,v 1.28 2002/07/19 18:17:34 minaeibi Exp $
+# $Id: lonstatistics.pm,v 1.29 2002/07/22 20:35:05 stredwic Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -42,6 +42,9 @@
 use Apache::lonnet();
 use Apache::lonhomework;
 use Apache::loncommon;
+use Apache::loncoursedata;
+use Apache::lonhtmlcommon;
+use Apache::lonchart;
 use HTML::TokeParser;
 use GDBM_File;
 
@@ -1114,6 +1117,7 @@
     }
     $GraphDat{$RealIdx}=$DoD.':'.$Wrng;
 }
+
 sub StatusOptions {
     my ($cache)=@_;
 
@@ -1236,8 +1240,8 @@
     $Ptr .= '<b>Sum of Partial Credit Awarded / Total Number of Tries</b><br>';
     $Ptr .= '<b>2nd Criterion</b> for Sorting the Students: ';
     $Ptr .= '<b>Total number of Correct Answers / Total Number of Tries</b>';
-    $Ptr .= '</td>';
-    $Ptr .= '<td><b>Disc.</b></td>';
+    $Ptr .= '</td></tr>';
+    $Ptr .= '<tr><td><b>Disc.</b></td>';
     $Ptr .= '<td>Number of Students had at least one discussion.';
     $Ptr .= '</td></tr></table>';
 
@@ -1348,61 +1352,6 @@
 
 #---- Student Assessment Web Page --------------------------------------------
 
-sub MapOptions {
-    my ($cache, $page)=@_;
-    my $Ptr = '<tr>';
-    $Ptr .= '<td align="right"><b>Select Map</b></td>'."\n";
-    $Ptr .= '<td align="left"><select name="Maps">'."\n";
-
-    my $selected = 0;
-    foreach my $sequence (split(':',$cache->{'orderedSequences'})) {
-	$Ptr .= '<option';
-        if($cache->{$page.'Map'} eq $cache->{$sequence.':title'}) {
-            $Ptr .= ' selected';
-            $selected = 1;
-        }
-	$Ptr .= '>'.$cache->{$sequence.':title'}.'</option>'."\n";	     
-    }
-    $Ptr .= '<option';
-    if(!$selected) {
-        $Ptr .= ' selected';
-    }
-    $Ptr .= '>All Maps</option>'."\n";
-
-    $Ptr .= '</select></td></tr>'."\n";
-
-    return $Ptr;
-}
-
-sub StudentOptions {
-    my ($students, $selectedName)=@_;
-
-    my $Ptr ='<tr>';
-    $Ptr .= '<td align="right"><b>Select Student</b></td>'."\n";
-    $Ptr .= '<td align="left"><select name="Students">'."\n";
-
-    my $selected=0;
-    foreach (@$students) {
-        my ($name) = split(':',$_);
-	$Ptr .= '<option';
-	if($selectedName eq $name) {
-            $Ptr .= ' selected';
-            $selected = 1;
-        }
-        $Ptr .= '>'.$name.'</option>'."\n";	     
-    }
-
-    $Ptr .= '<option';
-    if(!$selected) {
-        $Ptr .= ' selected';
-    }
-    $Ptr .= '>No Student Selected</option>'."\n";
-
-    $Ptr .= '</select></td></tr>'."\n";
-
-    return $Ptr;
-}
-
 # ------ Create different Student Report 
 sub StudentReport {
     my ($cache, $name)=@_;
@@ -1514,47 +1463,6 @@
 
 #---- Menu Web Page ----------------------------------------------------------
 
-sub Title {
-    my ($downloadTime)=@_;
-
-    my $Ptr = '';
-
-    $Ptr .= '<html><head><title>LON-CAPA Statistics</title></head>'."\n";
-    $Ptr .= '<body bgcolor="#FFFFFF">'."\n";
-    $Ptr .= '<script>window.focus(); window.width=500;window.height=500;';
-    $Ptr .= '</script>'."\n";
-    $Ptr .= '<img align=right src=/adm/lonIcons/lonlogos.gif>'."\n";
-    $Ptr .= '<h1> Course : "';
-    $Ptr .= $ENV{'course.'.$ENV{'request.course.id'}.'.description'};
-    $Ptr .= '"</h1>'."\n";
-    $Ptr .= '<h3>'.$downloadTime.'</h3>';
-
-    return $Ptr;
-}
-
-sub CreateMenuForm {
-    my ($cache)=@_;
-    my $Ptr = '<table border="0">';
-    $Ptr .= '<tr><td><input type="submit" name="ProblemStatistics" ';
-    $Ptr .= 'value="Problem Stats"/>';
-    $Ptr .= '</td></tr>'."\n";
-
-    if(defined($cache->{'OptionResponses'})) {
-        $Ptr .= '<tr><td><input type="submit" name="ProblemAnalysis" ';
-        $Ptr .= 'value="Problem Analysis"/>';
-        $Ptr .= '</td></tr>'."\n";
-    }
-
-    $Ptr .= '<tr><td><input type="submit" name="StudentAssessment" ';
-    $Ptr .= 'value="Student Assessment"/>';
-    $Ptr .= '</td></tr>'."\n";
-    #$Ptr .= '<input type="submit" name="ActivityLog" value="Activity Log"/>';
-
-    $Ptr .= '</table>'."\n";
-
-    return $Ptr;
-}
-
 #---- END Menu Web Page ------------------------------------------------------
 
 #---- HELPER FUNCTIONS -------------------------------------------------------
@@ -1572,68 +1480,80 @@
 }
 
 sub ProcessFormData{
-    my ($cacheDB, $isCached)=@_;
-    my %cache;
+    my ($cache)=@_;
 
-    if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_WRCREAT,0640)) {
-        # Select page to display
-        if(defined($ENV{'form.ProblemStatistics'}) ||
-           defined($ENV{'form.ProblemStatisticsRecalculate'}) || 
-           defined($ENV{'form.DisplayCSVFormat'})) {
-            $cache{'GoToPage'} = 'ProblemStatistics';
-            &CheckFormElement(\%cache, 'DisplayCSVFormat',
-                              'DisplayFormat', 'Display Table Format');
-            &CheckFormElement(\%cache, 'Ascend','ProblemStatisticsAscend',
-                              'Ascending');
-            &CheckFormElement(\%cache, 'Maps', 'ProblemStatisticsMap', 
-                              'All Maps');
-        } elsif(defined($ENV{'form.ProblemAnalysis'})) {
-            $cache{'GoToPage'} = 'ProblemAnalysis';
-            &CheckFormElement(\%cache, 'Interval', 'Interval', '1');
-        } elsif(defined($ENV{'form.StudentAssessment'}) ||
-                defined($ENV{'form.CreateStudentAssessment'}) ||
-                defined($ENV{'form.NextStudent'}) ||
-                defined($ENV{'form.PreviousStudent'})) {
-            $cache{'GoToPage'} = 'StudentAssessment';
-            if(defined($ENV{'form.NextStudent'})) {
-                $cache{'StudentAssessmentMove'} = 'next';
-            } elsif(defined($ENV{'form.PreviousStudent'})) {
-                $cache{'StudentAssessmentMove'} = 'previous';
-            } else {
-                $cache{'StudentAssessmentMove'} = 'selected';
-            }
-            &CheckFormElement(\%cache, 'Maps', 'StudentAssessmentMap', 
-                              'All Maps');
+    $cache->{'reportKey'} = 'false';
 
-            &CheckFormElement(\%cache, 'Students', 'StudentAssessmentStudent', 
-                              'No Student Selected');
-        } elsif(defined($ENV{'form.DoDiffGraph'})) {
-            $cache{'GoToPage'} = 'DoDiffGraph';
-        } elsif(defined($ENV{'form.PercentWrongGraph'})) {
-            $cache{'GoToPage'} = 'PercentWrongGraph';
-        } elsif(defined($ENV{'form.ActivityLog'})) {
-            $cache{'GoToPage'} = 'ActivityLog';
-        } else {
-            $cache{'GoToPage'} = 'Menu';
+    &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
+                                            ['sort','download']);
+    &CheckFormElement($cache, 'Status', 'Status', 'Active');
+    &CheckFormElement($cache, 'postdata', 'reportSelected', 'Class list');
+    &CheckFormElement($cache, 'reportSelected', 'reportSelected', 
+                      'Class list');
+    &CheckFormElement($cache, 'DownloadAll', 'DownloadAll', 'false');
+    &CheckFormElement($cache, 'sort', 'sort', 'fullname');
+    &CheckFormElement($cache, 'download', 'download', 'false');
+
+    if(defined($ENV{'form.CreateStudentAssessment'}) ||
+       defined($ENV{'form.NextStudent'}) ||
+       defined($ENV{'form.PreviousStudent'})) {
+        $cache->{'reportSelected'} = 'Student Assessment';
+    }
+    if(defined($ENV{'form.NextStudent'})) {
+        $cache->{'StudentAssessmentMove'} = 'next';
+    } elsif(defined($ENV{'form.PreviousStudent'})) {
+        $cache->{'StudentAssessmentMove'} = 'previous';
+    } else {
+        $cache->{'StudentAssessmentMove'} = 'selected';
+    }
+    &CheckFormElement($cache, 'StudentAssessmentMap', 'StudentAssessmentMap', 
+                      'All Maps');
+    &CheckFormElement($cache, 'StudentAssessmentStudent', 
+                      'StudentAssessmentStudent', 'No Student Selected');
+
+    foreach (keys(%ENV)) {
+        if(/form\.Analyze:::/) {
+#            $cache->{'reportSelected'} = 'Analyze';
+#            $cache->{'reportKey'} = 'Problem Analysis';
+            my ($uri, $title, $part, $problem);
+            (undef, $uri, $title, $part, $problem)=split(':::', $_);
+            $cache->{'AnalyzeURI'}     = $uri;
+            $cache->{'AnalyzeTitle'}   = $title;
+            $cache->{'AnalyzePart'}    = $part;
+            $cache->{'AnalyzeProblem'} = $problem;
+            
+            &CheckFormElement($cache, 'Interval', 'Interval', '1');
         }
+    }
 
-        &CheckFormElement(\%cache, 'Status', 'Status', 'Active');
-
-        foreach (keys(%ENV)) {
-            if(/form\.Analyze:::/) {
-                $cache{'GoToPage'} = 'Analyze';
-                my ($uri, $title, $part, $problem);
-                (undef, $uri, $title, $part, $problem)=split(':::', $_);
-                $cache{'AnalyzeURI'}     = $uri;
-                $cache{'AnalyzeTitle'}   = $title;
-                $cache{'AnalyzePart'}    = $part;
-                $cache{'AnalyzeProblem'} = $problem;
+    return;
 
-                &CheckFormElement(\%cache, 'Interval', 'Interval', '1');
-            }
-        }
+    # Select page to display
+    if(defined($ENV{'form.ProblemStatistics'}) ||
+       defined($ENV{'form.ProblemStatisticsRecalculate'}) || 
+       defined($ENV{'form.DisplayCSVFormat'})) {
+        $cache->{'GoToPage'} = 'ProblemStatistics';
+        &CheckFormElement($cache, 'DisplayCSVFormat',
+                          'DisplayFormat', 'Display Table Format');
+        &CheckFormElement($cache, 'Ascend','ProblemStatisticsAscend',
+                          'Ascending');
+        &CheckFormElement($cache, 'Maps', 'ProblemStatisticsMap', 
+                          'All Maps');
+    } elsif(defined($ENV{'form.ProblemAnalysis'})) {
+        $cache->{'GoToPage'} = 'ProblemAnalysis';
+        &CheckFormElement($cache, 'Interval', 'Interval', '1');
+    } elsif(defined($ENV{'form.DoDiffGraph'})) {
+        $cache->{'GoToPage'} = 'DoDiffGraph';
+    } elsif(defined($ENV{'form.PercentWrongGraph'})) {
+        $cache->{'GoToPage'} = 'PercentWrongGraph';
+    } elsif(defined($ENV{'form.ActivityLog'})) {
+        $cache->{'GoToPage'} = 'ActivityLog';
+    } else {
+        $cache->{'GoToPage'} = 'Menu';
     }
 
+    &CheckFormElement($cache, 'Status', 'Status', 'Active');
+
     return;
 }
 
@@ -1663,51 +1583,24 @@
 =cut
 
 sub SortStudents {
-    my ($students,$cache)=@_;
+    my ($cache)=@_;
 
+    my @students = split(':::',$cache->{'NamesOfStudents'});
     my @sorted1Students=();
-    foreach (@$students) {
-        push(@sorted1Students, $_);
-    }
-#        my ($end,$start)=split(/\:/,$cache->{$_.':date'});
-#        my $active=1;
-#        my $now=time;
-#        my $Status=$cache->{'form.Status'};
-#        $Status = ($Status) ? $Status : 'Active';
-#        if((($end) && $now > $end) && (($Status eq 'Active'))) { 
-#            $active=0; 
-#        }
-#        if(($Status eq 'Expired') && ($end == 0 || $now < $end)) {
-#            $active=0;
-#        }
-#        if($active) {
-#            push(@sorted1Students, $_);
-#        }
-#    }
-
-    my $Pos = $cache->{'form.ChartSort'};
-    my %sortData;
-    if($Pos eq 'Last Name') {
-	for(my $index=0; $index<scalar @sorted1Students; $index++) {
-	    $sortData{$cache->{$sorted1Students[$index].':fullname'}}=
-		$sorted1Students[$index];
-	}
-    } elsif($Pos eq 'Section') {
-	for(my $index=0; $index<scalar @sorted1Students; $index++) {
-	    $sortData{$cache->{$sorted1Students[$index].':section'}.
-		      $sorted1Students[$index]}=$sorted1Students[$index];
-	}
-    } else {
-	# Sort by user name
-	for(my $index=0; $index<scalar @sorted1Students; $index++) {
-	    $sortData{$sorted1Students[$index]}=$sorted1Students[$index];
-	}
+    foreach (@students) {
+        if($cache->{'Status'} eq 'Any' || 
+           $cache->{$_.':Status'} eq $cache->{'Status'}) {
+            push(@sorted1Students, $_);
+        }
     }
 
-    my @order = ();
-    foreach my $key (sort(keys(%sortData))) {
-	push (@order,$sortData{$key});
+    my $sortBy = '';
+    if(defined($cache->{'sort'})) {
+        $sortBy = ':'.$cache->{'sort'};
     }
+    my @order = sort { $cache->{$a.$sortBy} cmp $cache->{$b.$sortBy} ||
+                       $cache->{$a.':fullname'} cmp $cache->{$b.':fullname'} } 
+                @sorted1Students;
 
     return \@order;
 }
@@ -1716,79 +1609,106 @@
     my ($c, $cacheDB)=@_;
 
     # Test for access to the cache data
-    my $isCached=0;
     my $courseID=$ENV{'request.course.id'};
     my $isRecalculate=0;
-    if(defined($ENV{'form.ProblemStatisticsRecalculate'}) ||
-       defined($ENV{'form.ChartRecalculate'})) {
+    if(defined($ENV{'form.Recalculate'})) {
         $isRecalculate=1;
     }
 
-    $isRecalculate=1;
-
-    $isCached=&Apache::loncoursedata::TestCacheData($cacheDB, $isRecalculate);
+    my $isCached = &Apache::loncoursedata::TestCacheData($cacheDB, 
+                                                         $isRecalculate);
     if($isCached < 0) {
         return "Unable to tie hash to db file.";
     }
-    &ProcessFormData($cacheDB, $isCached);
 
     # Download class list information if not using cached data
     my %cache;
-    my @students=();
-    if(!$isCached) {
-        unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_WRCREAT,0640)) {
-            return "Unable to tie hash to db file.";
-        }
+    unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_WRCREAT,0640)) {
+        return "Unable to tie hash to db file.";
+    }
 
+    if(!$isCached) {
         my $processTopResourceMapReturn=
             &Apache::loncoursedata::ProcessTopResourceMap(\%cache, $c);
         if($processTopResourceMapReturn ne 'OK') {
             untie(%cache);
             return $processTopResourceMapReturn;
         }
+    }
 
-        if($c->aborted()) {
-            untie(%cache);
-            return 'aborted'; 
-        }
+    if($c->aborted()) {
+        untie(%cache);
+        return 'aborted'; 
+    }
 
-        my $classlist=&Apache::loncoursedata::DownloadStudentNamePIDSection(
-                                                                   $courseID, 
-                                                                   $c);
-        my ($checkForError)=keys(%$classlist);
-        if($checkForError =~ /^(con_lost|error|no_such_host)/i ||
-           defined($classlist->{'error'})) {
+    my $classlist=&Apache::loncoursedata::DownloadClasslist($courseID,
+                                                $cache{'ClasslistTimestamp'},
+                                                $c);
+    foreach (keys(%$classlist)) {
+        if(/^(con_lost|error|no_such_host)/i) {
             untie(%cache);
             return "Error getting student data.";
         }
+    }
 
-        if($c->aborted()) {
-            untie(%cache);
-            return 'aborted'; 
-        }
+    if($c->aborted()) {
+        untie(%cache);
+        return 'aborted'; 
+    }
 
-        # Active is a temporary solution, remember to change
-        @students=&Apache::loncoursedata::ProcessClassList(\%cache, 
-                                                           $classlist, 
-                                                           $courseID, 
-                                                           'Active', $c);
+    # Active is a temporary solution, remember to change
+    Apache::loncoursedata::ProcessClasslist(\%cache,$classlist,$courseID,$c);
+    if($c->aborted()) {
+        untie(%cache);
+        return 'aborted'; 
+    }
 
-        if($c->aborted()) {
-            untie(%cache);
-            return 'aborted'; 
-        }
+    &ProcessFormData(\%cache);
+    my $students = &SortStudents(\%cache);
 
-        untie(%cache);
-    } else {
-        if(!$c->aborted() && tie(%cache,'GDBM_File',$cacheDB,
-                                 &GDBM_READER,0640)) {
-            @students=split(/:::/,$cache{'NamesOfStudents'});
+    if($cache{'download'} ne 'false') {
+        my $who = $cache{'download'};
+        my $courseData = 
+            &Apache::loncoursedata::DownloadStudentCourseInformation(
+                                             $who, $courseID, 
+                                             $cache{$who.':lastDownloadTime'});
+        &Apache::loncoursedata::ProcessStudentData(\%cache, $courseData, $who);
+        $cache{'download'} = 'false';
+    } elsif($cache{'DownloadAll'} ne 'false') {
+        my @allStudents;
+        if($cache{'DownloadAll'} eq 'sorted') {
+            @allStudents = @$students;
         } else {
-            return 'aborted';
+            @allStudents = split(':::', $cache{'NamesOfStudents'});
         }
+        foreach (@allStudents) {
+            my $courseData = 
+                &Apache::loncoursedata::DownloadStudentCourseInformation(
+                                             $_, $courseID, 
+                                             $cache{$_.':lastDownloadTime'});
+            &Apache::loncoursedata::ProcessStudentData(\%cache, $courseData, 
+                                                       $_);
+            if($c->aborted()) {
+                untie(%cache);
+                return 'aborted'; 
+            }
+        }
+        $cache{'DownloadAll'} = 'false';
     }
 
-    return ('OK', $isCached, \@students);
+    if($c->aborted()) {
+        untie(%cache);
+        return 'aborted'; 
+    }
+
+    if($c->aborted()) {
+        untie(%cache);
+        return 'aborted'; 
+    }
+
+    untie(%cache);
+
+    return ('OK', $students);
 }
 
 # Create progress
@@ -1856,7 +1776,7 @@
 #---- END HELPER FUNCTIONS ---------------------------------------------------
 
 sub BuildProblemStatisticsPage {
-    my ($cacheDB, $students)=@_;
+    my ($cacheDB, $students, $courseID, $c)=@_;
 
     my %cache;
     unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {
@@ -1865,36 +1785,48 @@
     }
 
     my $Ptr = '';
-    $Ptr .= '<form name="ProblemStatisticsPage" method="post" ';
-    $Ptr .= 'action="/adm/statistics">'."\n";
     $Ptr .= '<table border="0"><tbody>';
-    $Ptr .= '<tr><td></td><td align="left"><input type="submit" name="Menu" ';
-    $Ptr .= 'value="Return to Menu" /></td></tr>'."\n";
+    $Ptr .= '<tr><td align="right"><b>Select Map</b></td>'."\n";
+    $Ptr .= '<td align="left">';
+    $Ptr .= &Apache::lonhtmlcommon::MapOptions(\%cache, 'ProblemStatistics');
+    $Ptr .= '</td></tr>'."\n";
     $r->print($Ptr);
-
-    $r->print(&MapOptions(\%cache, 'ProblemStatistics'));
-    $r->print(&StatusOptions());
     $r->print(&AscendOrderOptions());
     $r->print(&ProblemStatisticsButtons(\%cache));
     $r->print('</table>');
 
     $r->print(&ProblemStatisticsLegend());
 
-#    my $discriminantFactor;
-#    my @list=();
-#    foreach (@$students) {
-#        ($discriminantFactor, $list) = &ExtractStudentData(\%cache, $_,
-#                                                           \@list);
-#    }
+    untie(%cache);
+    foreach (@$students) {
+        my $courseData = 
+            &Apache::loncoursedata::DownloadStudentCourseInformation($_, 
+                                                                    $courseID);
+        last if ($c->aborted());
+        if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_WRCREAT,0640)) {
+            &Apache::loncoursedata::ProcessStudentData(\%cache, 
+                                                       $courseData, $_);
+            untie(%cache);
+        }
+    }
+    if($c->aborted()) { return; }
 
-#    my ($upper, $lower) = &Discriminant($discriminantFactor);
-#    my %Header = (0,"Homework Sets Order",1,"#Stdnts",2,"Tries",3,"Mod",
-#                  4,"Mean",5,"#YES",6,"#yes",7,"%Wrng",8,"DoDiff",
-#                  9,"S.D.",10,"Skew.",11,"D.F.1st",12,"D.F.2nd", 13, "Disc.");
-#    &BuildStatisticsTable(\%cache, $discriminantFactor, \@list, \%Header, 
-#                           $students);
+    unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {
+        $r->print('<html><body>Unable to tie database.</body></html>');
+        return;
+    }
+    my $discriminantFactor;
+    my @list=();
+    foreach (@$students) {
+        $discriminantFactor = &ExtractStudentData(\%cache, $_, \@list);
+    }
 
-    $r->print('</form>');
+    my ($upper, $lower) = &Discriminant($discriminantFactor);
+    my %Header = (0,"Homework Sets Order",1,"#Stdnts",2,"Tries",3,"Mod",
+                  4,"Mean",5,"#YES",6,"#yes",7,"%Wrng",8,"DoDiff",
+                  9,"S.D.",10,"Skew.",11,"D.F.1st",12,"D.F.2nd", 13, "Disc.");
+    &BuildStatisticsTable(\%cache, $discriminantFactor, \@list, \%Header, 
+                           $students);
 
     untie(%cache);
 
@@ -1944,13 +1876,8 @@
         return;
     }
 
-    $r->print('<form name="ProblemAnalysisPage" method="post" ');
-    $r->print('action="/adm/statistics">'."\n");
-    $r->print('<tr><td></td><td align="left"><input type="submit" ');
-    $r->print('name="Menu" value="Return to Menu" /></td></tr>'."\n");
     $r->print(&IntervalOptions());
     $r->print(&OptionResponseTable(\%cache));
-    $r->print('</form>'."\n");
 
     untie(%cache);
 
@@ -1958,16 +1885,12 @@
 }
 
 sub BuildStudentAssessmentPage {
-    my ($cacheDB, $students, $courseID)=@_;
+    my ($cacheDB, $students, $courseID, $c)=@_;
 
     my %cache;
 
     my $Ptr = '';
-    $Ptr .= '<form name="StudentAssessmentPage" method="post" ';
-    $Ptr .= 'action="/adm/statistics">'."\n";
     $Ptr .= '<table border="0"><tbody>';
-    $Ptr .= '<tr><td></td><td align="left"><input type="submit" name="Menu" ';
-    $Ptr .= 'value="Return to Menu" /></td></tr>'."\n";
     $r->print($Ptr);
 
     unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {
@@ -1976,31 +1899,43 @@
     }
 
     my $selectedName = $cache{'StudentAssessmentStudent'};
-    for(my $index=0; $index<(scalar @$students); $index++) {
-        my ($username) = split(':', $students->[$index]);
-        if($username eq $selectedName) {
+    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) = split(':',$students->[0]);
+                    $selectedName = $students->[0];
                 } else {
-                    ($selectedName) = split(':',$students->[$index+1]);
+                    $selectedName = $students->[$index+1];
                 }
             } elsif($cache{'StudentAssessmentMove'} eq 'previous') {
                 if($index == 0) {
-                    ($selectedName) = split(':',
-                                           $students->[(scalar @$students)-1]);
+                    $selectedName = $students->[-1];
                 } else {
-                    ($selectedName)=split(':',$students->[$index-1]);
+                    $selectedName = $students->[$index-1];
                 }
+            } else {
+                $selectedName = $students->[$index];
             }
             last;
         }
     }
 
-    $r->print(&MapOptions(\%cache, 'StudentAssessment'));
-    $r->print(&StudentOptions($students, $selectedName));
+    $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 .= '<tr><td></td><td align="left">';
     $Ptr .= '<input type="submit" name="CreateStudentAssessment" ';
     $Ptr .= 'value="Create Student Report" />';
     $Ptr .= '&nbsp&nbsp&nbsp';
@@ -2010,156 +1945,211 @@
     $Ptr .= '<input type="submit" name="NextStudent" ';
     $Ptr .= 'value="Next Student" />';
     $Ptr .= '&nbsp&nbsp&nbsp';
-    $Ptr .= '</td></tr></tbody></table></form>'."\n";
+    $Ptr .= '</td></tr></tbody></table>'."\n";
     $r->print($Ptr);
 
-    untie(%cache);
-
     if($selectedName eq 'No Student Selected') {
 	$r->print('<h3><font color=blue>WARNING: ');
         $r->print('Please select a student</font></h3>');
         return;
     }
 
-    my $name = '';
+    my $selected=0;
     foreach (@$students) {
-        my ($currentName) = split(':',$_);
-        if($currentName eq $selectedName) {
-            $name = $_;
-            last;
+        next if ($_ ne $selectedName && 
+                 $selectedName ne 'All Students');
+        $selected = 1;
+        my $courseData = 
+            &Apache::loncoursedata::DownloadStudentCourseInformation($_, 
+                                                                    $courseID);
+        last if ($c->aborted());
+        if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_WRCREAT,0640)) {
+            &Apache::loncoursedata::ProcessStudentData(\%cache, 
+                                                       $courseData, $_);
+            if(!$c->aborted()) { $r->print(&StudentReport(\%cache, $_)); }
+            untie(%cache);
         }
     }
-    if($name eq '') {
-        $r->print('<h3><font color=blue>ERROR: Unknown student selected.');
-        $r->print('</font></h3>');
+    if($selected == 0) {
+	$r->print('<h3><font color=blue>WARNING: ');
+        $r->print('Please select a student</font></h3>');
         return;
     }
 
-    my $courseData = 
-        &Apache::loncoursedata::DownloadStudentCourseInformation($name, 
-                                                                 $courseID);
-    if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_WRCREAT,0640)) {
-        &Apache::loncoursedata::ProcessStudentData(\%cache, 
-                                                   $courseData, $name);
-        untie(%cache);
-    }
-
-    unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {
-        $Ptr .= 'Could not tie database.';
-        return $Ptr;
-    }
-    $r->print(&StudentReport(\%cache, $name));
-    untie(%cache);
-
     return;
 }
 
-sub BuildMenu {
-    my ($cacheDB)=@_;
+sub BuildClasslist {
+    my ($cacheDB,$students,$studentInformation,$headings,$spacePadding)=@_;
 
     my %cache;
     unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {
-        $r->print('<html><body>Unable to tie database.</body></html>');
-        return;
+        return '<html><body>Unable to tie database.</body></html>';
     }
 
-    $r->print('<form name="Menu" method="post" action="/adm/statistics" >');
-    $r->print(&CreateMenuForm(\%cache));
-    $r->print('</form>'."\n");
+    my $Str='';
+    $Str .= '<table border="0"><tr><td bgcolor="#777777">'."\n";
+    $Str .= '<table border="0" cellpadding="3"><tr bgcolor="#e6ffff">'."\n";
+
+    my $displayString = '<td align="left"><a href="/adm/statistics?';
+    $displayString .= 'sort=LINKDATA">DISPLAYDATA&nbsp</a></td>'."\n";
+    $Str .= &Apache::lonhtmlcommon::CreateStudentInformationHeadings(\%cache,
+                                                           $studentInformation,
+                                                           $headings,
+                                                           $displayString);
+    $Str .= '<td align="left">';
+    $Str .= '<a href="/adm/statistics?sort=lastDownloadTime">';
+    $Str .= 'Last Updated&nbsp&nbsp</a></td>'."\n";
+    $Str .= '</tr>'."\n";
+    my $alternate=0;
+    foreach (@$students) {
+        my ($username, $domain) = split(':', $_);
+        if($alternate) {
+            $Str .= '<tr bgcolor="#ffffe6"><td>';
+        } else {
+            $Str .= '<tr bgcolor="#ffffc6"><td>';
+        }
+        $alternate = ($alternate + 1) % 2;
+        foreach my $data (@$studentInformation) {
+            if($data eq 'fullname') {
+                $Str .= '<a href="/adm/statistics?reportSelected=';
+                $Str .= &Apache::lonnet::escape('Student Assessment').'">';
+            }
 
-    untie(%cache);
+            $Str .= $cache{$_.':'.$data}.'&nbsp';
 
-    return;
-}
+            if($data eq 'fullname') {
+                $Str .= '</a>';
+            }
 
-# ================================================================ Main Handler
+            $Str .= '</td><td>';
+        }
 
-sub handler {
-    $r=shift;
-    &initial();
+        $Str .= '<a href="/adm/statistics?download='.$_.'">';
+        my $downloadTime = $cache{$_.':lastDownloadTime'};
+        if($downloadTime ne 'Not downloaded') {
+            $downloadTime = localtime($downloadTime);
+        }
+        $Str .= $downloadTime;
 
-    unless(&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'})) {
-        $ENV{'user.error.msg'}=
-        $r->uri.":vgr:0:0:Cannot view grades for complete course";
-        return HTTP_NOT_ACCEPTABLE; 
+        $Str .= '&nbsp</a></td></tr>'."\n";
     }
 
-    # Set document type for header only
-    if($r->header_only) {
-        if ($ENV{'browser.mathml'}) {
-            $r->content_type('text/xml');
-        } else {
-            $r->content_type('text/html');
-        }
-        &Apache::loncommon::no_cache($r);
-        $r->send_http_header;
-        return OK;
-    }
+    $Str .= '</table></td></tr></table>'."\n";
 
-    unless($ENV{'request.course.fn'}) {
-	my $requrl=$r->uri;
-        $ENV{'user.error.msg'}="$requrl:bre:0:0:Course not initialized";
-        return HTTP_NOT_ACCEPTABLE; 
-    }
+    untie(%cache);
 
-    $r->content_type('text/html');
-    $r->send_http_header;
+    return $Str;
+}
+
+sub BuildStatistics {
+    my ($r)=@_;
+
+    my $c = $r->connection;
+    my @studentInformation=('fullname','section','id','domain','username');
+    my @headings=('Full Name', 'Section', 'PID', 'Domain', 'User Name');
+    my $spacePadding = '   ';
+    my %reports = ('classlist'          => 'Class list',
+                   'problem_statistics' => 'Problem Statistics',
+                   'student_assessment' => 'Student Assessment',
+                   'reportSelected'     => 'Class list');
 
     my %cache;
     my $courseID=$ENV{'request.course.id'};
     my $cacheDB = "/home/httpd/perl/tmp/$ENV{'user.name'}".
                   "_$ENV{'user.domain'}_$courseID\_statistics.db";
-    my $c = $r->connection;
 
-    my ($returnValue, $isCached, $students) = &PrepareData($c, $cacheDB);
+    &setbgcolor(0);
+    my ($returnValue, $students) = &PrepareData($c, $cacheDB);
     if($returnValue ne 'OK') {
         $r->print('<html><body>'.$returnValue."\n".'</body></html>');
         return OK;
     }
 
-    my $downloadTime=0;
     my $GoToPage;
     if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {
-        $students = &SortStudents($students, \%cache);
-        if(defined($cache{'time'})) {
-            $downloadTime=$cache{'time'};
-        } else {
-            $downloadTime=localtime();
-        }
-        if(!defined($cache{'GoToPage'})) {
-            $GoToPage = 'Menu';
-        } else {
-            $GoToPage = $cache{'GoToPage'};
+        $GoToPage = $cache{'reportSelected'};
+        $reports{'reportSelected'} = $cache{'reportSelected'};
+#        if(defined($cache{'reportKey'}) && $cache{'reportKey'} ne 'false') {
+#            $reports{$cache{'reportKey'}} = $cache{'reportSelected'};
+#        }
+
+        if(defined($cache{'OptionResponses'})) {
+            $reports{'problem_analysis'} = 'Problem Analysis';
         }
+
+        $r->print(&Apache::lonhtmlcommon::Title('LON-CAPA Statistics'));
+        $r->print('<form name="Statistics" ');
+        $r->print('method="post" action="/adm/statistics">');
+        $r->print(&Apache::lonhtmlcommon::CreateStatisticsMainMenu(
+                                                             $cache{'Status'}, 
+                                                             \%reports));
         untie(%cache);
     } else {
         $r->print('<html><body>Unable to tie database.</body></html>');
         return OK;
     }
 
-    &setbgcolor(0);
-    $r->print(&Title());
-
-    if($GoToPage eq 'ActivityLog') {
+    if($GoToPage eq 'Activity Log') {
         &Activity();
-    } elsif($GoToPage eq 'ProblemStatistics') {
-        &BuildProblemStatisticsPage($cacheDB, $students);
-    } elsif($GoToPage eq 'ProblemAnalysis') {
+    } elsif($GoToPage eq 'Problem Statistics') {
+        &BuildProblemStatisticsPage($cacheDB, $students, $courseID, $c);
+    } elsif($GoToPage eq 'Problem Analysis') {
         &BuildProblemAnalysisPage($cacheDB);
-    } elsif($GoToPage eq 'StudentAssessment') {
-        &BuildStudentAssessmentPage($cacheDB, $students, $courseID);
+    } elsif($GoToPage eq 'Student Assessment') {
+        &BuildStudentAssessmentPage($cacheDB, $students, $courseID, $c);
     } elsif($GoToPage eq 'Analyze') {
         &BuildAnalyzePage($cacheDB, $students, $courseID);
     } elsif($GoToPage eq 'DoDiffGraph') {
         &BuildDiffGraph($courseID);
     } elsif($GoToPage eq 'PercentWrongGraph') {
         &BuildWrongGraph($courseID);
-    } else {
-        &BuildMenu($cacheDB);
+    } elsif($GoToPage eq 'Class list') {
+        $r->print(&BuildClasslist($cacheDB, $students, \@studentInformation,
+                                  \@headings, $spacePadding));
     }
 
+    $r->print('</form>'."\n");
     $r->print("\n".'</body>'."\n".'</html>');
     $r->rflush();
+
+    return OK;
+}
+
+# ================================================================ Main Handler
+
+sub handler {
+    $r=shift;
+    &initial();
+
+    unless(&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'})) {
+        $ENV{'user.error.msg'}=
+        $r->uri.":vgr:0:0:Cannot view grades for complete course";
+        return HTTP_NOT_ACCEPTABLE; 
+    }
+
+    # Set document type for header only
+    if($r->header_only) {
+        if ($ENV{'browser.mathml'}) {
+            $r->content_type('text/xml');
+        } else {
+            $r->content_type('text/html');
+        }
+        &Apache::loncommon::no_cache($r);
+        $r->send_http_header;
+        return OK;
+    }
+
+    unless($ENV{'request.course.fn'}) {
+	my $requrl=$r->uri;
+        $ENV{'user.error.msg'}="$requrl:bre:0:0:Course not initialized";
+        return HTTP_NOT_ACCEPTABLE; 
+    }
+
+    $r->content_type('text/html');
+    $r->send_http_header;
+
+    &BuildStatistics($r);
 
     return OK;
 }
Index: loncom/interface/mydesk.tab
diff -u loncom/interface/mydesk.tab:1.18 loncom/interface/mydesk.tab:1.19
--- loncom/interface/mydesk.tab:1.18	Wed Apr 17 09:17:10 2002
+++ loncom/interface/mydesk.tab	Mon Jul 22 16:35:05 2002
@@ -22,7 +22,7 @@
 4:1:pbre:$crs:grds.gif:my:grades:go('/adm/studentcalc');
 4:1:pvgr:$crs:sprs.gif:course:grades:go('/adm/classcalc');
 4:2:clear
-4:2:pvgr:$crs:chrt.gif:course:chart:go('/adm/chart');
+4:2:pvgr:$crs:chrt.gif:course:chart:gopost('/adm/statistics','Student Assessment');
 4:3:clear
 4:3:pvgr:$crs:stat.gif:course:stats:go('/adm/statistics');
 5:1:clear

Index: loncom/interface/lonhtmlcommon.pm
+++ loncom/interface/lonhtmlcommon.pm
package Apache::lonhtmlcommon;

use strict;

sub MapOptions {
    my ($data, $page)=@_;
    my $Str = '';
    $Str .= '<select name="';
    $Str .= (($page)?$page:'').'Map">'."\n";

    my $selected = 0;
    foreach my $sequence (split(':',$data->{'orderedSequences'})) {
	$Str .= '<option';
        if($data->{$page.'Map'} eq $data->{$sequence.':title'}) {
            $Str .= ' selected';
            $selected = 1;
        }
	$Str .= '>'.$data->{$sequence.':title'}.'</option>'."\n";	     
    }
    $Str .= '<option';
    if(!$selected) {
        $Str .= ' selected';
    }
    $Str .= '>All Maps</option>'."\n";

    $Str .= '</select>'."\n";

    return $Str;
}

sub StudentOptions {
    my ($cache, $students, $selectedName, $page)=@_;

    my $Str = '';
    $Str = '<select name="'.(($page)?$page:'').'Student">'."\n";

    my $selected=0;
    $Str .= '<option';
    if($selectedName eq 'All Students') {
        $Str .= ' selected';
        $selected = 1;
    }
    $Str .= '>All Students</option>'."\n";

    foreach (@$students) {
	$Str .= '<option';
	if($selectedName eq $_) {
            $Str .= ' selected';
            $selected = 1;
        }
        $Str .= '>';
        $Str .= $cache->{$_.':fullname'};
        $Str .= '</option>'."\n";	     
    }

    $Str .= '<option';
    if(!$selected) {
        $Str .= ' selected';
    }
    $Str .= '>No Student Selected</option>'."\n";

    $Str .= '</select>'."\n";

    return $Str;
}

sub StatusOptions {
    my ($status, $formName)=@_;

    my $OpSel1 = '';
    my $OpSel2 = '';
    my $OpSel3 = '';

    if($status eq 'Any')         { $OpSel3 = ' selected'; }
    elsif($status eq 'Expired' ) { $OpSel2 = ' selected'; }
    else                         { $OpSel1 = ' selected'; }

    my $Str = '';
    $Str .= '<select name="Status"';
    if(defined($formName) && $formName ne '') {
        $Str .= ' onchange="document.'.$formName.'.submit()"';
    }
    $Str .= '>'."\n";
    $Str .= '<option'.$OpSel1.'>Active</option>'."\n";
    $Str .= '<option'.$OpSel2.'>Expired</option>'."\n";
    $Str .= '<option'.$OpSel3.'>Any</option>'."\n";
    $Str .= '</select>'."\n";
}

sub Title {
    my ($pageName)=@_;

    my $Str = '';

    $Str .= '<html><head><title>'.$pageName.'</title></head>'."\n";
    $Str .= '<body bgcolor="#FFFFFF">'."\n";
    $Str .= '<script>window.focus(); window.width=500;window.height=500;';
    $Str .= '</script>'."\n";
    $Str .= '<table width="100%"><tr><td valign="top">';
    $Str .= '<h1> Course: ';
    $Str .= $ENV{'course.'.$ENV{'request.course.id'}.'.description'};
    $Str .= '</h1></td><td align="right">'."\n";
    $Str .= '<img align="right" src=/adm/lonIcons/lonlogos.gif>';
    $Str .= '</td></tr></table>'."\n";
#    $Str .= '<h3>Current Time: '.localtime(time).'</h3><br><br><br>'."\n";

    return $Str;
}

sub CreateStatisticsMainMenu {
    my ($status, $reports)=@_;

    my $Str = '';

    $Str .= '<table border="0"><tbody><tr>'."\n";
    $Str .= '<td></td><td></td>'."\n";
    $Str .= '<td align="center"><b>Analysis Reports:</b></td>'."\n";
    $Str .= '<td align="center"><b>Student Status:</b></td></tr>'."\n";
    $Str .= '<tr>'."\n";
    $Str .= '<td align="center"><input type="submit" name="Refresh" ';
    $Str .= 'value="Refresh" /></td>'."\n";
    $Str .= '<td align="center"><input type="submit" name="DownloadAll" ';
    $Str .= 'value="Update All Student Data" /></td>'."\n";
    $Str .= '<td align="center">';
    $Str .= '<select name="reportSelected" onchange="document.';
    $Str .= 'Statistics.submit()">'."\n";

    foreach (sort(keys(%$reports))) {
        next if($_ eq 'reportSelected');
        $Str .= '<option name="'.$_.'"';
        if($reports->{'reportSelected'} eq $reports->{$_}) {
            $Str .= ' selected=""';
        }
        $Str .= '>'.$reports->{$_}.'</option>'."\n";
    }
    $Str .= '</select></td>'."\n";

    $Str .= '<td align="center">';
    $Str .= &StatusOptions($status, 'Statistics');
    $Str .= '</td>'."\n";

    $Str .= '</tr></tbody></table>'."\n";
    $Str .= '<hr>'."\n";

    return $Str;
}

=pod

=item &CreateTableHeadings()

This function generates the column headings for the chart.

=over 4

Inputs: $CacheData, $studentInformation, $headings, $spacePadding

$CacheData: pointer to a hash tied to the cached data database

$studentInformation: a pointer to an array containing the names of the data 
held in a column and is used as part of a key into $CacheData

$headings: The names of the headings for the student information

$spacePadding: The spaces to go between columns

Output: $Str

$Str: A formatted string of the table column headings.

=back

=cut

sub CreateStudentInformationHeadings {
    my ($data,$studentInformation,$headings,$displayString)=@_;
    my $Str='';

    for(my $index=0; $index<(scalar @$headings); $index++) {
#        if(!&ShouldShowColumn($data, 'ChartHeading'.$index)) {
#            next;
#        }
 	my $data=$headings->[$index];
        my $linkdata=$studentInformation->[$index];
        my $tempString = $displayString;
        $tempString =~ s/LINKDATA/$linkdata/;
        $tempString =~ s/DISPLAYDATA/$data/;
        $Str .= $tempString;
    }

    return $Str;
}

=pod

=item &FormatStudentInformation()

This function produces a formatted string of the student's information:
username, domain, section, full name, and PID.

=over 4

Input: $cache, $name, $studentInformation, $spacePadding

$cache: This is a pointer to a hash that is tied to the cached data

$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.

$spacePadding: Extra spaces that represent the space between columns

Output: $Str

$Str: Formatted string.

=back

=cut

sub FormatStudentInformation {
    my ($cache,$name,$studentInformation,$spacePadding)=@_;
    my $Str='';
    my $data;

    for(my $index=0; $index<(scalar @$studentInformation); $index++) {
        if(!&ShouldShowColumn($cache, 'ChartHeading'.$index)) {
            next;
        }
        $data=$cache->{$name.':'.$studentInformation->[$index]};
	$Str .= $data;

	my @dataLength=split(//,$data);
	my $length=scalar @dataLength;
	$Str .= (' 'x($cache->{$studentInformation->[$index].'Length'}-
                      $length));
	$Str .= $spacePadding;
    }

    return $Str;
}

1;
__END__

--stredwic1027370105--