--- loncom/interface/spreadsheet/assesscalc.pm	2003/09/22 21:03:25	1.24
+++ loncom/interface/spreadsheet/assesscalc.pm	2007/01/23 01:36:26	1.58
@@ -1,5 +1,5 @@
 #
-# $Id: assesscalc.pm,v 1.24 2003/09/22 21:03:25 matthew Exp $
+# $Id: assesscalc.pm,v 1.58 2007/01/23 01:36:26 albertel Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -50,10 +50,12 @@ 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');
 
@@ -102,6 +104,7 @@ my %nice_parameter_name;
 my %useropt;
 my %userdata;
 my %courseopt;
+my $navmap;
 
 ########################################################
 ########################################################
@@ -122,7 +125,7 @@ sub clear_package {
     if (defined($current_name) &&
         defined($current_domain) &&
         defined($current_course) &&
-        $current_course eq $ENV{'request.course.id'} &&
+        $current_course eq $env{'request.course.id'} &&
         %newExportrows) {
         &save_cached_export_rows($current_name,$current_domain);
     }
@@ -134,20 +137,29 @@ sub clear_package {
     undef(%useropt);
     undef(%userdata);
     undef(%courseopt);
+    undef($navmap);
 }
 
 sub save_cached_export_rows {
     my ($sname,$sdomain) = @_;
-    my $start = Time::HiRes::time;
     my $result = &Apache::lonnet::put
-        ('nohist_calculatedsheets_'.$ENV{'request.course.id'},
+        ('nohist_calculatedsheets_'.$env{'request.course.id'},
          $newExportrows{$sname.':'.$sdomain},
          $sdomain,$sname);
     delete($newExportrows{$sname.':'.$sdomain});
 }
 
 sub initialize {
+    my ($in_navmap) = @_;
     &clear_package();
+    $navmap = $in_navmap;
+    if (! defined($navmap)) {
+        $navmap = Apache::lonnavmaps::navmap->new();
+    }
+    if (!defined($navmap)) {
+        &Apache::lonnet::logthis('assesscalc:Can not open Coursemap');
+    }
+    &Apache::loncoursedata::clear_internal_caches();
 }
 
 ########################################################
@@ -162,17 +174,19 @@ sub initialize {
 ########################################################
 ########################################################
 sub initialize_package {
-    my ($sname,$sdomain) = @_;
+    my ($sname,$sdomain,$in_navmap,$calling_sheet) = @_;
     $current_name   = $sname;
     $current_domain = $sdomain;
+    $navmap = $in_navmap;
     undef(%useropt);
     undef(%userdata);
-    if ($current_course ne $ENV{'request.course.id'}) {
-        $current_course = $ENV{'request.course.id'};
+    if ($current_course ne $env{'request.course.id'}) {
+        $current_course = $env{'request.course.id'};
         undef(%courseopt);
     }
     &load_cached_export_rows();
-    &load_parameter_caches();
+    &load_parameter_caches($calling_sheet);
+    &Apache::loncoursedata::clear_internal_caches();
 }
 
 
@@ -188,18 +202,19 @@ sub initialize_package {
 ########################################################
 ########################################################
 sub load_parameter_caches {
+    my ($calling_sheet) = @_;
     my $userprefix = $current_name.':'.$current_domain.'_';
     $userprefix =~ s/:/_/g;
     #
     # Course Parameters Cache
     if (! %courseopt) {
-        $current_course = $ENV{'request.course.id'};
+        $current_course = $env{'request.course.id'};
         undef(%courseopt);
         if (! defined($current_name) || ! defined($current_domain)) {
             return;
         }
-        my $dom = $ENV{'course.'.$ENV{'request.course.id'}.'.domain'};
-        my $id  = $ENV{'course.'.$ENV{'request.course.id'}.'.num'};
+        my $dom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+        my $id  = $env{'course.'.$env{'request.course.id'}.'.num'};
         my %Tmp = &Apache::lonnet::dump('resourcedata',$dom,$id);
         while (my ($name,$value) = each(%Tmp)) {
             $courseopt{$name}=$value;
@@ -220,6 +235,10 @@ sub load_parameter_caches {
     if (! %userdata) {
         %userdata = &Apache::loncoursedata::get_current_state($current_name,
                                                               $current_domain);
+	my ($tmp) = %userdata;
+	if ($tmp =~ /^error:(.*)/) {
+	    $calling_sheet->set_calcerror($1);
+	}
         $userdata{'loadtime'} = time;
     }
     return;
@@ -242,8 +261,8 @@ sub ensure_current_caches {
     ## Check for a modified parameters
     ##
     if (! defined($current_course) || 
-        $current_course ne $ENV{'request.course.id'} ) {
-        $current_course = $ENV{'request.course.id'};
+        $current_course ne $env{'request.course.id'} ) {
+        $current_course = $env{'request.course.id'};
         undef(%courseopt); 
         undef(%useropt);
         undef(%userdata);
@@ -258,7 +277,7 @@ sub ensure_current_caches {
         undef(%useropt);
         undef(%userdata);
     }
-    &load_parameter_caches();
+    &load_parameter_caches($self);
 }
 
 ##################################################
@@ -276,7 +295,8 @@ Returns: The value of a parameter, or ''
 
 This function cascades through the possible levels searching for a value for
 a parameter.  The levels are checked in the following order:
-user, course (at section level and course level), map, and lonnet::metadata.
+user, course (at group, section level and course level), map, and 
+lonnet::metadata.
 This function uses %parmhash, which must be tied prior to calling it.
 This function also requires %courseopt and %useropt to be initialized for
 this user and course.
@@ -287,16 +307,20 @@ this user and course.
 ##################################################
 sub parmval {
     my $self = shift;
-    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));
-    $symb  = $self->{'symb'}    if (! defined($symb));
+    my ($what,$symb,$uname,$udom,$csec,$recurse,$mapname,$id,$fn,$groups)=@_;
+    $uname  = $self->{'name'}     if (! defined($uname));
+    $udom   = $self->{'domain'}   if (! defined($udom));
+    $csec   = $self->{'section'}  if (! defined($csec));
+    $groups = $self->{'groups'}   if (! defined($groups)); 
+    $symb   = $self->{'symb'}     if (! defined($symb));
     #
     my $result='';
     #
     # This should be a 
-    my ($mapname,$id,$fn)=&Apache::lonnet::decode_symb($symb);
+    if (!defined($mapname) || !defined($id) || !defined($fn)) {
+	($mapname,$id,$fn)=&Apache::lonnet::decode_symb($symb);
+	$mapname = &Apache::lonnet::deversion($mapname);
+    }
     # Cascading lookup scheme
     my $rwhat=$what;
     $what =~ s/^parameter\_//;
@@ -318,12 +342,23 @@ sub parmval {
     my $ucourselevel  = $usercourseprefix.'.'.$what;
     my $ucourselevelr = $usercourseprefix.'.'.$symbparm;
     my $ucourselevelm = $usercourseprefix.'.'.$mapparm;
-   # check user
+    # check user
     if (defined($uname)) {
         return $useropt{$ucourselevelr} if (defined($useropt{$ucourselevelr}));
         return $useropt{$ucourselevelm} if (defined($useropt{$ucourselevelm}));
         return $useropt{$ucourselevel}  if (defined($useropt{$ucourselevel}));
     }
+    # check groups
+    if (defined($groups) && ref($groups) eq 'ARRAY') {
+        foreach my $group (@{$groups}) {
+            foreach my $level ($symbparm,$mapparm,$what) {
+                my $item = $courseprefix.'.['.$group.'].'.$level;
+                if (defined($courseopt{$item})) {
+                    return $courseopt{$item};
+                }
+            }
+        }
+    }
     # check section
     if (defined($csec)) {
         return $courseopt{$seclevelr} if (defined($courseopt{$seclevelr}));
@@ -333,18 +368,19 @@ sub parmval {
     #
     # check course
     return $courseopt{$courselevelr} if (defined($courseopt{$courselevelr}));
-    return $courseopt{$courselevelm} if (defined($courseopt{$courselevelm}));
-    return $courseopt{$courselevel}  if (defined($courseopt{$courselevel}));
     # check map parms
     my $thisparm = $parmhash{$symbparm};
     return $thisparm if (defined($thisparm));
     # check default
     $thisparm = &Apache::lonnet::metadata($fn,$rwhat.'.default');
     return $thisparm if (defined($thisparm));
-    #
+    # check more course
+    return $courseopt{$courselevelm} if (defined($courseopt{$courselevelm}));
+    return $courseopt{$courselevel}  if (defined($courseopt{$courselevel}));
+
     # Cascade Up
     my $space=$what;
-    $space=~s/\.\w+$//;
+    $space=~s/\.[^._]+$//;
     if ($space ne '0') {
 	my @parts=split(/_/,$space);
 	my $id=pop(@parts);
@@ -352,7 +388,8 @@ 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,1);
+	my $partgeneral=$self->parmval($newwhat,$symb,$uname,$udom,$csec,1,
+				       $mapname,$id,$fn,$groups);
 	if (defined($partgeneral)) { return $partgeneral; }
     }
     if ($recurse) { return undef; }
@@ -364,7 +401,7 @@ sub parmval {
 
 sub get_html_title {
     my $self = shift;
-    my ($assess_title,$name,$time) = $self->get_title();
+    my ($assess_title,$name,$time) = $self->get_full_title();
     my $title = '<h1>'.$assess_title.'</h1>'.
         '<h2>'.$name.', '.
         &Apache::loncommon::aboutmewrapper
@@ -376,26 +413,27 @@ sub get_html_title {
 
 sub get_title {
     my $self = shift;
-    my @title = ();
     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/^_//;
-        push(@title,$assess_title);
+        return $assess_title;
     } else {
-        push(@title,&Apache::lonnet::gettitle($self->{'symb'}));
+        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'},
-                                                      $self->{'domain'});
-    my $name = 
-        join(' ',@userenv{'firstname','middlename','lastname','generation'});
-    $name =~ s/\s+$//;
+    my $name = &Apache::loncommon::plainname($self->{'name'},
+					     $self->{'domain'});
     push (@title,$name);
-    push (@title,scalar(localtime(time)));
+    push (@title,&Apache::lonlocal::locallocaltime(time));
     return @title;
 }
 
@@ -404,13 +442,17 @@ 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
     ###################################
@@ -418,13 +460,17 @@ sub outsheet_html {
     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>
+  <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;
@@ -466,6 +512,11 @@ END
     #
     my $num_output = 0;
     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);
 	}
@@ -483,8 +534,8 @@ sub assess_html_row {
     my @rowdata = $self->get_row($row);
     my $num_cols_output = 0;
     my $row_html;
-    if (exists($nice_parameter_name{$parameter_name})) {
-        my $name = $nice_parameter_name{$parameter_name};
+    my $name=$self->get_parm_name($parameter_name);
+    if ($name ne '') {
         $name =~ s/ /\&nbsp;/g;
         $row_html .= '<td>'.$name.'<br />'.$parameter_name.'</td>';
     } else {
@@ -496,7 +547,7 @@ sub assess_html_row {
             $row_html .= &Apache::Spreadsheet::html_uneditable_cell($cell,
                                                                     '#FFDDDD');
         } else {
-            $row_html .= '<td bgcolor="#EOFFDD">';
+            $row_html .= '<td bgcolor="#E0FFDD">';
             $row_html .= &Apache::Spreadsheet::html_editable_cell($cell,
                                                                   '#E0FFDD',1);
         }
@@ -510,19 +561,16 @@ sub csv_rows {
     # writes the meat of the spreadsheet to an excel worksheet.  Called
     # by Spreadsheet::outsheet_excel;
     my $self = shift;
-    my ($filehandle) = @_;
+    my ($connection,$filehandle) = @_;
     #
     # Write a header row
     $self->csv_output_row($filehandle,undef,
-                          ('Parameter','Description','Value'));
+                          (&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};
-        }
+        my $description = $self->get_parm_name($parameter_name);
         $self->csv_output_row($filehandle,$rownum,
                               $parameter_name,$description);
     }
@@ -533,22 +581,20 @@ 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) = @_;
+    my ($connection,$worksheet,$cols_output,$rows_output,$format) = @_;
+    return if (! ref($worksheet));
     #
     # Write a header row
     $cols_output = 0;
     foreach my $value ('Parameter','Description','Value') {
-        $worksheet->write($rows_output,$cols_output++,$value);
+        $worksheet->write($rows_output,$cols_output++,$value,$format->{'h4'});
     }
-    $rows_output++;    
+    $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};
-        }
+        my $description = $self->get_parm_name($parameter_name);
         $self->excel_output_row($worksheet,$rownum,$rows_output++,
                                 $parameter_name,$description);
     }
@@ -558,10 +604,10 @@ sub excel_rows {
 ##
 ## Routines to support assesscalc::compute
 ##
-sub get_parm_names {
+sub get_parm {
     my $self = shift;
     my @Mandatory_parameters = @_;
-    my %parameters_and_names;
+    my %parameters;
     #
     my ($symap,$syid,$srcf) = &Apache::lonnet::decode_symb($self->{'symb'});
     my @Metadata = split(/\,/,&Apache::lonnet::metadata($srcf,'keys'));
@@ -570,14 +616,20 @@ sub get_parm_names {
         my $cleaned_name = $parm;
         $cleaned_name =~ s/^resource\./stores_/;
         $cleaned_name =~ s/\./_/g;
-        my $display = &Apache::lonnet::metadata($srcf,
-                                                $cleaned_name.'.display');
-        if (! $display) {
-            $display .= &Apache::lonnet::metadata($srcf,$cleaned_name.'.name');
-        }
-        $parameters_and_names{$cleaned_name}=$display;
+        $parameters{$cleaned_name}=1;
     }
-    return (%parameters_and_names);
+    return (keys(%parameters));
+}
+
+sub get_parm_name {
+    my $self = shift;
+    my $parm = shift;
+    my ($symap,$syid,$srcf) = &Apache::lonnet::decode_symb($self->{'symb'});
+    my $display = &Apache::lonnet::metadata($srcf,$parm.'.display');
+    if (! $display) {
+	$display .= &Apache::lonnet::metadata($srcf,$parm.'.name');
+    }
+    return $display;
 }
 
 sub get_parameter_values {
@@ -597,8 +649,13 @@ sub get_parameter_values {
     my $filename = $self->{'coursefilename'}.'_parms.db';
     if (tie(%parmhash,'GDBM_File',
             $self->{'coursefilename'}.'_parms.db',&GDBM_READER(),0640)) {
+	my ($mapname,$id,$fn)=&Apache::lonnet::decode_symb($self->{'symb'});
+	$mapname = &Apache::lonnet::deversion($mapname);
         foreach my $parmname (@Parameters) {
-            my $value = $self->parmval($parmname);
+            my $value = $self->parmval($parmname,$self->{'symb'},
+				       $self->{'name'},$self->{'domain'},
+				       $self->{'section'},undef,
+				       $mapname,$id,$fn,$self->{'groups'});
             $parameters{$parmname} =$value;
         }
         untie(%parmhash);
@@ -629,11 +686,59 @@ sub get_problem_state {
     return %student_parameters;
 }
 
+sub determine_parts {
+    my $self = shift;
+    my $check_hidden = 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)) { next; }
+        if (!$check_hidden) { $Parts{$part}++; next; }
+        if (!&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 ($start,@pieces)=split('_',$parameter);
+    if ( $start !~ m/^(resource|stores|parameter)$/) { return 0; }
+    while (@pieces) {
+        pop(@pieces);
+        my $testpart=join('_',@pieces);
+	if (exists($self->{'Parts'}->{$testpart}) &&
+	    $self->{'Parts'}->{$testpart} ) {
+	    return 1;
+	}
+    }
+    return 0;
+}
+
 sub compute {
     my $self = shift;
     my ($r) = @_;
-    my $connection = $r->connection();
-    if ($connection->aborted()) { $self->cleanup(); return; }
     $self->initialize_safe_space();
     #########################################
     #########################################
@@ -654,18 +759,11 @@ sub compute {
     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;
-    }
+    my @parameters=$self->get_parm(@Mandatory_parameters);
     #
     # 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; }
+    %parameters = $self->get_parameter_values(@parameters);
     #
     # Clean out unnecessary parameters
     foreach (keys(%parameters)) {
@@ -673,11 +771,14 @@ sub compute {
     }
     #
     # Get the students performance data
+    $self->determine_parts(($parameters{'parameter_0_hiddenparts'} ne ''));
     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
+	next if (! $self->parameter_part_is_valid($parm));
+	$parameters{$parm} = $value;
     }
     #
     # Set up the formulas and parameter values
@@ -685,7 +786,7 @@ sub compute {
     my %c;
     #
     # Check for blackout requirements
-    if ((!exists($ENV{'request.role.adv'}) || !$ENV{'request.role.adv'})) {
+    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$/);
@@ -699,26 +800,31 @@ sub compute {
             }
         }
     }
-    if ($connection->aborted()) { $self->cleanup(); return; }
     #
     # Move the parameters into the spreadsheet
-    if ($connection->aborted()) { $self->cleanup(); return; }
     while (my ($parm,$value) = each(%parameters)) {
         my $cell = 'A'.$self->get_row_number_from_key($parm);
         $f{$cell} = $parm;
-        $value = '"'.$value.'"' if ($value =~/[^0-9.]/);
+        if ($parm =~ /_submission$/ && $value =~ /(\{|\})/) {
+            $value = 'witheld';
+        }
+        $value = 'q{'.$value.'}' if ($value =~/([^\d\.]|\.\.)/);
         $c{$parm} = $value;
     }
+    foreach my $cell (grep(/^A/,keys(%f))) {
+        # Clean out any bad formulas
+	next if (exists($c{$f{$cell}}));
+	next if ($cell eq 'A0');
+	delete($f{$cell});
+    }
     $self->formulas(\%f);
     $self->constants(\%c);
-    if ($connection->aborted()) { $self->cleanup(); return; }
     $self->calcsheet();
     #
     # Store export row in cache
     my @exportarray = $self->exportrow();
     $self->deal_with_export_row(@exportarray);
     $self->save() if ($self->need_to_save());
-    if ($connection->aborted()) { $self->cleanup(); return; }
     return;
 }
 
@@ -728,6 +834,7 @@ sub compute {
 sub sett {
     my $self = shift;
     my %t=();
+    undef(%Apache::Spreadsheet::sheet_values);
     #
     # Deal with the template row by copying the template formulas into each
     # row.
@@ -745,7 +852,7 @@ sub sett {
             # Replace 'A0' with the value from 'A0'
             $t{$cell}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$sheet_values\{\'$2\'\}/g;
             # Replace parameters
-            $t{$cell}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.$self->expandnamed($2)/ge;
+            $t{$cell}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.$self->expandnamed($2)/sge;
         }
     }
     #
@@ -753,14 +860,18 @@ sub sett {
     while (my ($cell,$formula) = each(%{$self->{'formulas'}})) {
 	next if ($cell =~ /template_/);
         if ($cell =~ /^A/ && $cell ne 'A0') {
-            if ($formula !~ /^\!/) {
-                $t{$cell}=$self->{'constants'}->{$formula};
+	    if ($formula !~ /^\!/ 
+		&& exists($self->{'constants'}->{$formula}) 
+		&& $self->{'constants'}->{$formula} ne ''
+		) {
+		$Apache::Spreadsheet::sheet_values{$cell}=
+		    eval($self->{'constants'}->{$formula});
             }
         } else {
             $t{$cell}=$formula;
             $t{$cell}=~s/\.\.+/\,/g;
             $t{$cell}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$sheet_values\{\'$2\'\}/g;
-            $t{$cell}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.$self->expandnamed($2)/ge;
+            $t{$cell}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.$self->expandnamed($2)/sge;
         }
     }
     # Put %t into the safe space
@@ -789,15 +900,19 @@ These rows are saved in the students dir
 sub load_cached_export_rows {
     undef(%Exportrows);
     my @tmp = &Apache::lonnet::dump('nohist_calculatedsheets_'.
-                                    $ENV{'request.course.id'},
+                                    $env{'request.course.id'},
                                     $current_domain,$current_name,undef);
     if ($tmp[0]!~/^error/) {
         my %tmp = @tmp;
-        my $default_filename =  $ENV{'course.'.$ENV{'request.course.id'}.
+        my $default_filename =  $env{'course.'.$env{'request.course.id'}.
                                          '.spreadsheet_default_assesscalc'};
         # We only got one key, so we will access it directly.
         while (my ($key,$sheetdata) = each(%tmp)) {
             my ($sname,$sdom,$sheettype,$symb) = split(':',$key);
+            if (! defined($sname) || $sname eq '' ||
+                ! defined($sdom)  || $sdom eq '' ) {
+                next;
+            }
             if ($symb =~ /\.time$/) {
                 $symb =~ s/\.time$//;
                 $Exportrows{$symb}->{'time'} = $sheetdata;
@@ -829,9 +944,8 @@ spreadsheet only if necessary.
 sub export_data {
     my $self = shift;
     my ($r) = @_;
-    my $connection = $r->connection();
     my $symb = $self->{'symb'};
-    if (! exists($ENV{'request.role.adv'}) || ! $ENV{'request.role.adv'} ||
+    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'}}) ||
@@ -840,14 +954,20 @@ sub export_data {
         ) {
         $self->compute($r);
     }
-    if ($connection->aborted()) { $self->cleanup(); return; }
-    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]));
+    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;
 }
@@ -870,6 +990,11 @@ sub save_export_data {
     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;