--- loncom/interface/spreadsheet/assesscalc.pm	2003/05/22 20:23:56	1.4
+++ loncom/interface/spreadsheet/assesscalc.pm	2003/11/17 19:55:41	1.29
@@ -1,5 +1,5 @@
 #
-# $Id: assesscalc.pm,v 1.4 2003/05/22 20:23:56 matthew Exp $
+# $Id: assesscalc.pm,v 1.29 2003/11/17 19:55:41 matthew Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -44,14 +44,18 @@ assesscalc
 package Apache::assesscalc;
 
 use strict;
+use warnings FATAL=>'all';
+no warnings 'uninitialized';
 use Apache::Constants qw(:common :http);
 use Apache::lonnet;
 use Apache::loncommon;
 use Apache::Spreadsheet;
+use Apache::loncoursedata();
 use HTML::Entities();
 use Spreadsheet::WriteExcel;
 use GDBM_File;
 use Time::HiRes;
+use Apache::lonlocal;
 
 @Apache::assesscalc::ISA = ('Apache::Spreadsheet');
 
@@ -88,6 +92,7 @@ use Time::HiRes;
 ########################################################
 
 my %Exportrows;
+my %newExportrows;
 
 my $current_name;
 my $current_domain;
@@ -97,6 +102,7 @@ my %parmhash;
 my %nice_parameter_name;
 
 my %useropt;
+my %userdata;
 my %courseopt;
 
 ########################################################
@@ -108,21 +114,45 @@ my %courseopt;
 
 =item &clear_package()
 
-Reset all package variables.  
+Reset all package variables and clean up caches.
 
 =cut
 
 ########################################################
 ########################################################
 sub clear_package {
+    if (defined($current_name) &&
+        defined($current_domain) &&
+        defined($current_course) &&
+        $current_course eq $ENV{'request.course.id'} &&
+        %newExportrows) {
+        &save_cached_export_rows($current_name,$current_domain);
+    }
     undef(%Exportrows);
+    undef(%newExportrows);
     undef($current_name);
     undef($current_domain);
     undef($current_course);
     undef(%useropt);
+    undef(%userdata);
     undef(%courseopt);
 }
 
+sub save_cached_export_rows {
+    my ($sname,$sdomain) = @_;
+    my $start = Time::HiRes::time;
+    my $result = &Apache::lonnet::put
+        ('nohist_calculatedsheets_'.$ENV{'request.course.id'},
+         $newExportrows{$sname.':'.$sdomain},
+         $sdomain,$sname);
+    delete($newExportrows{$sname.':'.$sdomain});
+}
+
+sub initialize {
+    &clear_package();
+    &Apache::loncoursedata::clear_internal_caches();
+}
+
 ########################################################
 ########################################################
 
@@ -136,14 +166,20 @@ sub clear_package {
 ########################################################
 sub initialize_package {
     my ($sname,$sdomain) = @_;
-    $current_course = $ENV{'request.course.id'};
     $current_name   = $sname;
     $current_domain = $sdomain;
-    undef(%courseopt);
+    undef(%useropt);
+    undef(%userdata);
+    if ($current_course ne $ENV{'request.course.id'}) {
+        $current_course = $ENV{'request.course.id'};
+        undef(%courseopt);
+    }
     &load_cached_export_rows();
     &load_parameter_caches();
+    &Apache::loncoursedata::clear_internal_caches();
 }
 
+
 ########################################################
 ########################################################
 
@@ -161,11 +197,9 @@ sub load_parameter_caches {
     #
     # Course Parameters Cache
     if (! %courseopt) {
-        &Apache::lonnet::logthis("loading course options");
         $current_course = $ENV{'request.course.id'};
         undef(%courseopt);
         if (! defined($current_name) || ! defined($current_domain)) {
-            &Apache::lonnet::logthis('bad call to setup_parameter_caches');
             return;
         }
         my $dom = $ENV{'course.'.$ENV{'request.course.id'}.'.domain'};
@@ -185,7 +219,14 @@ sub load_parameter_caches {
             }
             $useropt{$userprefix.$name}=$value;
         }
+        $useropt{'loadtime'} = time;
     }
+    if (! %userdata) {
+        %userdata = &Apache::loncoursedata::get_current_state($current_name,
+                                                              $current_domain);
+        $userdata{'loadtime'} = time;
+    }
+    return;
 }
 
 ########################################################
@@ -199,18 +240,27 @@ sub load_parameter_caches {
 
 ########################################################
 ########################################################
-sub ensure_current_parameter_caches {
+sub ensure_current_caches {
     my $self = shift;
+    ##
+    ## Check for a modified parameters
+    ##
     if (! defined($current_course) || 
         $current_course ne $ENV{'request.course.id'} ) {
         $current_course = $ENV{'request.course.id'};
         undef(%courseopt); 
+        undef(%useropt);
+        undef(%userdata);
     }
+    ##
+    ## Check for new user
+    ##
     if (! defined($current_name)   || $current_name ne $self->{'name'} ||
         ! defined($current_domain) || $current_domain ne $self->{'domain'}) {
         $current_domain = $self->{'domain'};
         $current_name   = $self->{'name'};
         undef(%useropt);
+        undef(%userdata);
     }
     &load_parameter_caches();
 }
@@ -241,7 +291,7 @@ this user and course.
 ##################################################
 sub parmval {
     my $self = shift;
-    my ($what,$symb,$uname,$udom,$csec)=@_;
+    my ($what,$symb,$uname,$udom,$csec,$recurse)=@_;
     $uname = $self->{'name'}    if (! defined($uname));
     $udom  = $self->{'domain'}  if (! defined($udom));
     $csec  = $self->{'section'} if (! defined($csec));
@@ -250,7 +300,7 @@ sub parmval {
     my $result='';
     #
     # This should be a 
-    my ($mapname,$id,$fn)=split(/___/,$symb);
+    my ($mapname,$id,$fn)=&Apache::lonnet::decode_symb($symb);
     # Cascading lookup scheme
     my $rwhat=$what;
     $what =~ s/^parameter\_//;
@@ -306,26 +356,45 @@ sub parmval {
 	if ($part eq '') { $part='0'; }
 	my $newwhat=$rwhat;
 	$newwhat=~s/\Q$space\E/$part/;
-	my $partgeneral=$self->parmval($newwhat,$symb,$uname,$udom,$csec);
+	my $partgeneral=$self->parmval($newwhat,$symb,$uname,$udom,$csec,1);
 	if (defined($partgeneral)) { return $partgeneral; }
     }
+    if ($recurse) { return undef; }
+    my $pack_def=&Apache::lonnet::packages_tab_default($fn,'resource.'.$what);
+    if (defined($pack_def)) { return $pack_def; }
     #nothing defined
     return '';
 }
 
+sub get_html_title {
+    my $self = shift;
+    my ($assess_title,$name,$time) = $self->get_full_title();
+    my $title = '<h1>'.$assess_title.'</h1>'.
+        '<h2>'.$name.', '.
+        &Apache::loncommon::aboutmewrapper
+                         ($self->{'name'}.'@'.$self->{'domain'},
+                          $self->{'name'},$self->{'domain'});
+    $title .= '<h3>'.$time.'</h3>';
+    return $title;
+}
+
 sub get_title {
     my $self = shift;
-    my $title;
-    if (($self->{'usymb'} eq '_feedback') ||
-        ($self->{'usymb'} eq '_evaluation') ||
-        ($self->{'usymb'} eq '_discussion') ||
-        ($self->{'usymb'} eq '_tutoring')) {
-        $title = $self->{'usymb'};
-        $title =~ s/^_//;
-        $title = '<h1>'.ucfirst($title)."</h1>\n";
+    if (($self->{'symb'} eq '_feedback') ||
+        ($self->{'symb'} eq '_evaluation') ||
+        ($self->{'symb'} eq '_discussion') ||
+        ($self->{'symb'} eq '_tutoring')) {
+        my $assess_title = ucfirst($self->{'symb'});
+        $assess_title =~ s/^_//;
+        return $assess_title;
     } else {
-        $title = '<h1>'.&Apache::lonnet::gettitle($self->{'symb'})."</h1>\n";
+        return &Apache::lonnet::gettitle($self->{'symb'});
     }
+}
+
+sub get_full_title {
+    my $self = shift;
+    my @title = ($self->get_title());
     # Look up the users identifying information
     # Get the users information
     my %userenv = &Apache::loncoursedata::GetUserName($self->{'name'},
@@ -333,13 +402,9 @@ sub get_title {
     my $name = 
         join(' ',@userenv{'firstname','middlename','lastname','generation'});
     $name =~ s/\s+$//;
-    $title .= '<h2>'.$name.', '.
-        &Apache::loncommon::aboutmewrapper($self->{'name'}.'@'.$self->{'domain'},
-                                           $self->{'name'},$self->{'domain'}).
-                                           "</h2>\n";
-    $title .= '<h3>'.localtime(time).'</h3>';
-    #
-    return $title;
+    push (@title,$name);
+    push (@title,&Apache::lonlocal::locallocaltime(time));
+    return @title;
 }
 
 sub parent_link {
@@ -347,31 +412,41 @@ sub parent_link {
     my $link .= '<p><a href="/adm/studentcalc?'.
         'sname='.$self->{'name'}.
             '&sdomain='.$self->{'domain'}.'">'.
-                'Student level sheet</a></p>'."\n";
+                &mt('Student level sheet').'</a></p>'."\n";
     return $link;
 }
 
 sub outsheet_html {
     my $self = shift;
     my ($r) = @_;
+    ####################################
+    # Report any calculation errors    #
+    ####################################
+    $r->print($self->html_report_error());
     ###################################
     # Determine table structure
     ###################################
+    my $importcolor = '#FFFFFF';
+    my $exportcolor = '#FFFFAA';
     my $num_uneditable = 1;
     my $num_left = 52-$num_uneditable;
+    my %lt=&Apache::lonlocal::texthash(
+				       'as' => 'Assessment',
+				       'ca' => 'Calculations',
+				       );
     my $tableheader =<<"END";
 <table border="2">
 <tr>
-  <th colspan="2" rowspan="2"><font size="+2">Assessment</font></th>
-  <td bgcolor="#FFDDDD" colspan="$num_uneditable">&nbsp;</td>
+  <th colspan="2" rowspan="2"><font size="+2">$lt{'as'}</font></th>
+  <td bgcolor="$importcolor" colspan="$num_uneditable">&nbsp;</td>
   <td colspan="$num_left">
-      <b><font size="+1">Calculations</font></b></td>
+      <b><font size="+1">$lt{'ca'}</font></b></td>
 </tr><tr>
 END
     my $label_num = 0;
     foreach (split(//,'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')){
         if ($label_num<$num_uneditable) { 
-            $tableheader .= '<td bgcolor="#FFDDDD">';
+            $tableheader .= '<td bgcolor="'.$importcolor.'">';
         } else {
             $tableheader .= '<td>';
         }
@@ -384,11 +459,12 @@ END
     #
     # Print out template row
     $r->print('<tr><td>Template</td><td>&nbsp;</td>'.
-	      $self->html_template_row($num_uneditable)."</tr>\n");
+	      $self->html_template_row($num_uneditable,$importcolor).
+              "</tr>\n");
     #
     # Print out summary/export row
     $r->print('<tr><td>Export</td><td>0</td>'.
-	      $self->html_export_row()."</tr>\n");
+	      $self->html_export_row($exportcolor)."</tr>\n");
     #
     # Prepare to output rows
     $tableheader =<<"END";
@@ -397,7 +473,7 @@ END
 END
     foreach (split(//,'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')){
 	if ($label_num<$num_uneditable) { 
-            $tableheader.='<th bgcolor="#FFDDDD">';
+            $tableheader.='<th bgcolor="'.$importcolor.'">';
         } else {
             $tableheader.='<th>';
         }
@@ -405,12 +481,17 @@ END
     }
     #
     my $num_output = 0;
-    foreach my $rownum ($self->rows()) {
+    foreach my $rownum (sort {$a <=> $b} ($self->rows())) {
+        if (! $self->parameter_part_is_valid(
+                                             $self->{'formulas'}->{'A'.$rownum}
+                                             )) {
+            next;
+        }
 	if ($num_output++ % 50 == 0) {
 	    $r->print("</table>\n".$tableheader);
 	}
 	$r->print('<tr><td>'.$rownum.'</td>'.
-                  $self->assess_html_row($num_uneditable,$rownum)."</tr>\n");
+                  $self->assess_html_row($rownum,$importcolor)."</tr>\n");
     }
     $r->print("</table>\n");
     return;
@@ -418,8 +499,7 @@ END
 
 sub assess_html_row {
     my $self = shift();
-    my ($num_uneditable,$row) = @_;
-    my $requester_is_student = ($ENV{'request.role'} =~ /^st\./);
+    my ($row,$importcolor) = @_;
     my $parameter_name = $self->{'formulas'}->{'A'.$row};
     my @rowdata = $self->get_row($row);
     my $num_cols_output = 0;
@@ -432,49 +512,81 @@ sub assess_html_row {
         $row_html .= '<td>'.$parameter_name.'</td>';
     }
     foreach my $cell (@rowdata) {
-	if ($requester_is_student || 
-	    $num_cols_output++ < $num_uneditable) {
-	    $row_html .= '<td bgcolor="#FFDDDD">';
-	    $row_html .= &Apache::Spreadsheet::html_uneditable_cell($cell,'#FFDDDD');
-	} else {
-	    $row_html .= '<td bgcolor="#EOFFDD">';
-	    $row_html .= &Apache::Spreadsheet::html_editable_cell($cell,'#E0FFDD');
-	}
+        if ($num_cols_output < 1) {
+            $row_html .= '<td bgcolor="'.$importcolor.'">';
+            $row_html .= &Apache::Spreadsheet::html_uneditable_cell($cell,
+                                                                    '#FFDDDD');
+        } else {
+            $row_html .= '<td bgcolor="#EOFFDD">';
+            $row_html .= &Apache::Spreadsheet::html_editable_cell($cell,
+                                                                  '#E0FFDD',1);
+        }
 	$row_html .= '</td>';
+        $num_cols_output++;
     }
     return $row_html;
 }
 
-sub outsheet_csv {
-    my $self = shift;
-    my ($r)=@_;
-}
-
-sub outsheet_excel {
-    my $self = shift;
-    my ($r)=@_;
+sub csv_rows {
+    # writes the meat of the spreadsheet to an excel worksheet.  Called
+    # by Spreadsheet::outsheet_excel;
+    my $self = shift;
+    my ($filehandle) = @_;
+    #
+    # Write a header row
+    $self->csv_output_row($filehandle,undef,
+                          (&mt('Parameter'),&mt('Description'),&mt('Value')));
+    #
+    # Write each row
+    foreach my $rownum (sort {$a <=> $b} ($self->rows())) {
+        my $parameter_name = $self->{'formulas'}->{'A'.$rownum};
+        my $description = '';
+        if (exists($nice_parameter_name{$parameter_name})) {
+            $description = $nice_parameter_name{$parameter_name};
+        }
+        $self->csv_output_row($filehandle,$rownum,
+                              $parameter_name,$description);
+    }
+    return;
 }
 
-sub display {
-    my $self = shift;
-    my ($r) = @_;
-    $self->compute();
-    $self->outsheet_html($r);
+sub excel_rows {
+    # writes the meat of the spreadsheet to an excel worksheet.  Called
+    # by Spreadsheet::outsheet_excel;
+    my $self = shift;
+    my ($worksheet,$cols_output,$rows_output) = @_;
+    #
+    # Write a header row
+    $cols_output = 0;
+    foreach my $value ('Parameter','Description','Value') {
+        $worksheet->write($rows_output,$cols_output++,&mt($value));
+    }
+    $rows_output++;    
+    #
+    # Write each row
+    foreach my $rownum (sort {$a <=> $b} ($self->rows())) {
+        my $parameter_name = $self->{'formulas'}->{'A'.$rownum};
+        my $description = '';
+        if (exists($nice_parameter_name{$parameter_name})) {
+            $description = $nice_parameter_name{$parameter_name};
+        }
+        $self->excel_output_row($worksheet,$rownum,$rows_output++,
+                                $parameter_name,$description);
+    }
+    return;
 }
 
-sub compute {
+##
+## Routines to support assesscalc::compute
+##
+sub get_parm_names {
     my $self = shift;
-    $self->logthis('computing');
-    $self->initialize_safe_space();
+    my @Mandatory_parameters = @_;
+    my %parameters_and_names;
     #
-    # Definitions
-    undef(%nice_parameter_name);
-    my %parameters;   # holds underscored parameters by name
-    #
-    # Get the metadata fields and determine their proper names
-    my ($symap,$syid,$srcf)=split(/___/,$self->{'symb'});
+    my ($symap,$syid,$srcf) = &Apache::lonnet::decode_symb($self->{'symb'});
     my @Metadata = split(/\,/,&Apache::lonnet::metadata($srcf,'keys'));
-    foreach my $parm (@Metadata) {
+    foreach my $parm (@Mandatory_parameters,@Metadata) {
         next if ($parm !~ /^(resource\.|stores|parameter)_/);
         my $cleaned_name = $parm;
         $cleaned_name =~ s/^resource\./stores_/;
@@ -484,16 +596,29 @@ sub compute {
         if (! $display) {
             $display .= &Apache::lonnet::metadata($srcf,$cleaned_name.'.name');
         }
-        $parameters{$cleaned_name}++;
-        $nice_parameter_name{$cleaned_name} = $display;
+        $parameters_and_names{$cleaned_name}=$display;
+    }
+    return (%parameters_and_names);
+}
+
+sub get_parameter_values {
+    my $self = shift();
+    my @Parameters;
+    my ($parameters) = @_;
+    if (!ref($parameters)) {
+        @Parameters = @_;
+    } elsif (ref($parameters) eq 'ARRAY') {
+        @Parameters = @$parameters;
+    } elsif (ref($parameters) eq 'HASH') {
+        @Parameters = keys(%$parameters);
     }
     #
-    # Get the values of the metadata fields
-    $self->ensure_current_parameter_caches();
+    my %parameters;
+    #
     my $filename = $self->{'coursefilename'}.'_parms.db';
     if (tie(%parmhash,'GDBM_File',
             $self->{'coursefilename'}.'_parms.db',&GDBM_READER(),0640)) {
-        foreach my $parmname (keys(%parameters)) {
+        foreach my $parmname (@Parameters) {
             my $value = $self->parmval($parmname);
             $parameters{$parmname} =$value;
         }
@@ -501,6 +626,113 @@ sub compute {
     } else {
         $self->logthis('unable to tie '.$filename);
     }
+    return %parameters;
+}
+
+sub deal_with_export_row {
+    my $self = shift();
+    my @exportarray = @_;
+    $Exportrows{$self->{'symb'}}->{'time'} = time;
+    $Exportrows{$self->{'symb'}}->{$self->{'filename'}} = \@exportarray;
+    #
+    # Save the export data
+    $self->save_export_data();
+    return;
+}
+
+sub get_problem_state {
+    my $self = shift;
+    my %student_parameters;
+    if (exists($userdata{$self->{'symb'}}) && 
+        ref($userdata{$self->{'symb'}}) eq 'HASH') {
+        %student_parameters = %{$userdata{$self->{'symb'}}};
+    }
+    return %student_parameters;
+}
+
+sub determine_parts {
+    my $self = shift;
+    if (exists($self->{'Parts'}) && ref($self->{'Parts'}) eq 'HASH') {
+        return;
+    }
+    my (undef,undef,$url) = &Apache::lonnet::decode_symb($self->{'symb'});
+    my $src = &Apache::lonnet::clutter($url);
+    return if (! defined($src));
+    my %Parts;
+    my $metadata = &Apache::lonnet::metadata($src,'packages');
+    foreach (split(',',$metadata)) {
+        my ($part) = (/^part_(.*)$/);
+        if (defined($part) && 
+            ! &Apache::loncommon::check_if_partid_hidden
+                ($part,$self->{'symb'},$self->{'name'},$self->{'domain'})
+            ) {
+            $Parts{$part}++;
+        }
+    }
+    # Make sure part 0 is defined.
+    $Parts{'0'}++;
+    $self->{'Parts'} = \%Parts;
+    return;
+}
+
+sub parameter_part_is_valid {
+    my $self = shift;
+    my ($parameter) = @_;
+    return 1 if ($parameter eq 'timestamp');
+    if (! defined($self->{'Parts'}) || 
+        ! ref ($self->{'Parts'})    ||
+        ref($self->{'Parts'}) ne 'HASH') {
+        return 1;
+    }
+    #
+    my (undef,$part) = 
+        ($parameter =~ m/^(resource|stores|parameter)_([^_]+)_.*/);
+    if (exists($self->{'Parts'})          && 
+        exists($self->{'Parts'}->{$part}) &&
+        $self->{'Parts'}->{$part} ) {
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+sub compute {
+    my $self = shift;
+    my ($r) = @_;
+    my $connection = $r->connection();
+    if ($connection->aborted()) { $self->cleanup(); return; }
+    $self->initialize_safe_space();
+    #########################################
+    #########################################
+    ###                                   ###
+    ###  Retrieve the problem parameters  ###
+    ###                                   ###
+    #########################################
+    #########################################
+    my @Mandatory_parameters = ("stores_0_solved",
+                                "stores_0_awarddetail",
+                                "stores_0_awarded",
+                                "timestamp",
+                                "stores_0_tries",
+                                "stores_0_award");
+    #
+    # Definitions
+    undef(%nice_parameter_name);
+    my %parameters;   # holds underscored parameters by name
+    #
+    # Get the metadata fields and determine their proper names
+    my %nice_parm_names = $self->get_parm_names(@Mandatory_parameters);
+    while (my($cleaned_name,$display) = each(%nice_parm_names)) {
+        $parameters{$cleaned_name}++;
+        $nice_parameter_name{$cleaned_name} = $display;
+    }
+    #
+    # Get the values of the metadata fields
+    if ($connection->aborted()) { $self->cleanup(); return; }
+    $self->ensure_current_caches();
+    if ($connection->aborted()) { $self->cleanup(); return; }
+    %parameters = $self->get_parameter_values(keys(%parameters));
+    if ($connection->aborted()) { $self->cleanup(); return; }
     #
     # Clean out unnecessary parameters
     foreach (keys(%parameters)) {
@@ -508,40 +740,59 @@ sub compute {
     }
     #
     # Get the students performance data
-    my %student_parameters = 
-        &Apache::loncoursedata::get_current_state($self->{'name'},
-                                                  $self->{'domain'},
-                                                  $self->{'symb'},
-                                                  $self->{'cid'});
+    my %student_parameters = $self->get_problem_state();
     while (my ($parm,$value) = each(%student_parameters)) {
         $parm =~ s/^resource\./stores_/;
         $parm =~ s/\./_/g;
         $parameters{$parm} = $value;
     }
     #
+    # Clean out any bad parameters
+    $self->determine_parts();
+    foreach my $param (keys(%parameters)) {
+        if (! $self->parameter_part_is_valid($param)) {
+            delete ($parameters{$param});
+        }
+    }
+    #
     # Set up the formulas and parameter values
     my %f=$self->formulas();
     my %c;
     #
+    # Check for blackout requirements
+    if ((!exists($ENV{'request.role.adv'}) || !$ENV{'request.role.adv'})) {
+        while (my ($parm,$value) = each(%parameters)) {
+            last if ($self->blackout());
+            next if ($parm !~ /^(parameter_.*)_problemstatus$/);
+            if ($parameters{$1.'_answerdate'} ne '' &&
+                $parameters{$1.'_answerdate'} < time) {
+                next;
+            }
+            if (lc($value) eq 'no') {
+                # We must blackout this sheet
+                $self->blackout(1);
+            }
+        }
+    }
+    if ($connection->aborted()) { $self->cleanup(); return; }
+    #
+    # Move the parameters into the spreadsheet
     while (my ($parm,$value) = each(%parameters)) {
         my $cell = 'A'.$self->get_row_number_from_key($parm);
         $f{$cell} = $parm;
-        $c{$parm} = '';
-        if ($value ne '') {
-            $c{$parm} = '"'.$value.'"';
-        }
+        $value = '"'.$value.'"' if ($value =~/[^0-9.]/);
+        $c{$parm} = $value;
     }
-    $self->formulas(%f);
-    $self->constants(%c);
+    $self->formulas(\%f);
+    $self->constants(\%c);
+    if ($connection->aborted()) { $self->cleanup(); return; }
     $self->calcsheet();
     #
     # Store export row in cache
     my @exportarray = $self->exportrow();
-    $Exportrows{$self->{'symb'}}->{'time'} = time;
-    $Exportrows{$self->{'symb'}}->{$self->{'filename'}} = \@exportarray;
-    #
-    # Save the export data
-    $self->save_export_data();
+    $self->deal_with_export_row(@exportarray);
+    $self->save() if ($self->need_to_save());
+    if ($connection->aborted()) { $self->cleanup(); return; }
     return;
 }
 
@@ -610,7 +861,7 @@ These rows are saved in the students dir
 ########################################################
 ########################################################
 sub load_cached_export_rows {
-    %Exportrows = undef;
+    undef(%Exportrows);
     my @tmp = &Apache::lonnet::dump('nohist_calculatedsheets_'.
                                     $ENV{'request.course.id'},
                                     $current_domain,$current_name,undef);
@@ -651,20 +902,33 @@ spreadsheet only if necessary.
 #############################################
 sub export_data {
     my $self = shift;
+    my ($r) = @_;
+    my $connection = $r->connection();
     my $symb = $self->{'symb'};
-    if (! exists($Exportrows{$symb}) || ! defined($Exportrows{$symb})  ||
+    if (! exists($ENV{'request.role.adv'}) || ! $ENV{'request.role.adv'} ||
+        ! exists($Exportrows{$symb}) || ! defined($Exportrows{$symb})  ||
         ! $self->check_expiration_time($Exportrows{$symb}->{'time'}) ||
         ! exists($Exportrows{$symb}->{$self->{'filename'}}) ||
-        ! defined($Exportrows{$symb}->{$self->{'filename'}})) {
-        $self->compute();
-    }
-    my @Data = @{$Exportrows{$symb}->{$self->{'filename'}}};
-    if ($Data[0] =~ /^(.*)___=___/) {
-        $self->{'sheetname'} = $1;
-        $Data[0] =~ s/^(.*)___=___//;
-    }
-    for (my $i=0;$i<$#Data;$i++) {
-        $Data[$i]="'".$Data[$i]."'" if ($Data[$i]=~/\D/ && defined($Data[$i]));
+        ! defined($Exportrows{$symb}->{$self->{'filename'}}) ||
+        ! ref($Exportrows{$symb}->{$self->{'filename'}}) 
+        ) {
+        $self->compute($r);
+    }
+    if ($connection->aborted()) { $self->cleanup(); return; }
+    my @Data;
+    if ($self->badcalc()) {
+        @Data = ();
+    } else {
+        @Data = @{$Exportrows{$symb}->{$self->{'filename'}}};
+        if ($Data[0] =~ /^(.*)___=___/) {
+            $self->{'sheetname'} = $1;
+            $Data[0] =~ s/^(.*)___=___//;
+        }
+        for (my $i=0;$i<$#Data;$i++) {
+            if ($Data[$i]=~/\D/ && defined($Data[$i])) {
+                $Data[$i]="'".$Data[$i]."'";
+            }
+        }
     }
     return @Data;
 }
@@ -684,23 +948,26 @@ Writes the export data for this spreadsh
 #############################################
 sub save_export_data {
     my $self = shift;
+    return if ($self->temporary());
     my $student = $self->{'name'}.':'.$self->{'domain'};
     my $symb    = $self->{'symb'};
+    if ($self->badcalc()){
+        # do not save data away when calculations have not been done properly.
+        delete($Exportrows{$symb});
+        return;
+    }
     if (! exists($Exportrows{$symb}) || 
         ! exists($Exportrows{$symb}->{$self->{'filename'}})) {
         return;
     }
     my $key = join(':',($self->{'name'},$self->{'domain'},'assesscalc',$symb));
     my $timekey = $key.'.time';
-    my $newstore= join('___;___',@{$Exportrows{$symb}->{$self->{'filename'}}});
+    my $newstore= join('___;___',
+                       map {s/[^[:print:]]//g;$_;} # strip out unprintable
+                                @{$Exportrows{$symb}->{$self->{'filename'}}});
     $newstore = $self->{'filename'}.'___=___'.$newstore;
-    my $result = &Apache::lonnet::put
-        ('nohist_calculatedsheets_'.$ENV{'request.course.id'},
-         { $key     => $newstore,
-           $timekey => $Exportrows{$symb}->{'time'} },
-         $self->{'domain'},
-         $self->{'name'});
-
+    $newExportrows{$student}->{$key} = $newstore;
+    $newExportrows{$student}->{$timekey} = $Exportrows{$symb}->{'time'};
     return;
 }