Diff for /loncom/interface/Attic/lonspreadsheet.pm between versions 1.46 and 1.104

version 1.46, 2001/03/17 20:43:57 version 1.104, 2002/08/30 19:47:47
Line 1 Line 1
   #
   # $Id$
   #
   # Copyright Michigan State University Board of Trustees
   #
   # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
   #
   # LON-CAPA is free software; you can redistribute it and/or modify
   # it under the terms of the GNU General Public License as published by
   # the Free Software Foundation; either version 2 of the License, or
   # (at your option) any later version.
   #
   # LON-CAPA is distributed in the hope that it will be useful,
   # but WITHOUT ANY WARRANTY; without even the implied warranty of
   # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   # GNU General Public License for more details.
   #
   # You should have received a copy of the GNU General Public License
   # along with LON-CAPA; if not, write to the Free Software
   # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   #
   # /home/httpd/html/adm/gpl.txt
   #
   # http://www.lon-capa.org/
   #
 # The LearningOnline Network with CAPA  # The LearningOnline Network with CAPA
 # Spreadsheet/Grades Display Handler  # Spreadsheet/Grades Display Handler
 #  #
 # 11/11,11/15,11/27,12/04,12/05,12/06,12/07,  # 11/11,11/15,11/27,12/04,12/05,12/06,12/07,
 # 12/08,12/09,12/11,12/12,12/15,12/16,12/18,12/19,12/30,  # 12/08,12/09,12/11,12/12,12/15,12/16,12/18,12/19,12/30,
 # 01/01/01,02/01,03/01,19/01,20/01,22/01,  # 01/01/01,02/01,03/01,19/01,20/01,22/01,
 # 03/05,03/08,03/10,03/12,03/13,03/15,03/17 Gerd Kortemeyer  # 03/05,03/08,03/10,03/12,03/13,03/15,03/17,
   # 03/19,03/20,03/21,03/27,04/05,04/09,
   # 07/09,07/14,07/21,09/01,09/10,9/11,9/12,9/13,9/14,9/17,
   # 10/16,10/17,10/20,11/05,11/28,12/27 Gerd Kortemeyer
   # 01/14/02 Matthew
   # 02/04/02 Matthew
   
   # POD required stuff:
   
   =head1 NAME
   
   lonspreadsheet
   
   =head1 SYNOPSIS
   
   Spreadsheet interface to internal LON-CAPA data
   
   =head1 DESCRIPTION
   
   Lonspreadsheet provides course coordinators the ability to manage their
   students grades online.  The students are able to view their own grades, but
   not the grades of their peers.  The spreadsheet is highly customizable,
   offering the ability to use Perl code to manipulate data, as well as many
   built-in functions.
   
   
   =head2 Functions available to user of lonspreadsheet
   
   =over 4
   
   =cut
   
 package Apache::lonspreadsheet;  package Apache::lonspreadsheet;
                           
Line 16  use Apache::lonnet; Line 71  use Apache::lonnet;
 use Apache::Constants qw(:common :http);  use Apache::Constants qw(:common :http);
 use GDBM_File;  use GDBM_File;
 use HTML::TokeParser;  use HTML::TokeParser;
   use Apache::lonhtmlcommon;
 #  #
 # Caches for previously calculated spreadsheets  # Caches for previously calculated spreadsheets
 #  #
   
 my %oldsheets;  my %oldsheets;
 my %loadedcaches;  my %loadedcaches;
   my %expiredates;
   
 #  #
 # Cache for stores of an individual user  # Cache for stores of an individual user
Line 50  my %courseopt; Line 106  my %courseopt;
 my %useropt;  my %useropt;
 my %parmhash;  my %parmhash;
   
   #
   # Some hashes for stats on timing and performance
   #
   
   my %starttimes;
   my %usedtimes;
   my %numbertimes;
   
 # Stuff that only the screen handler can know  # Stuff that only the screen handler can know
   
 my $includedir;  my $includedir;
Line 66  sub initsheet { Line 130  sub initsheet {
     $safeeval->permit("sort");      $safeeval->permit("sort");
     $safeeval->deny(":base_io");      $safeeval->deny(":base_io");
     $safehole->wrap(\&Apache::lonnet::EXT,$safeeval,'&EXT');      $safehole->wrap(\&Apache::lonnet::EXT,$safeeval,'&EXT');
       $safeeval->share('$@');
     my $code=<<'ENDDEFS';      my $code=<<'ENDDEFS';
 # ---------------------------------------------------- Inside of the safe space  # ---------------------------------------------------- Inside of the safe space
   
Line 75  sub initsheet { Line 140  sub initsheet {
 # v: output values  # v: output values
 # c: preloaded constants (A-column)  # c: preloaded constants (A-column)
 # rl: row label  # rl: row label
   # os: other spreadsheets (for student spreadsheet only)
   
 undef %v;   undef %sheet_values; 
 undef %t;  undef %t;
 undef %f;  undef %f;
 undef %c;  undef %c;
 undef %rl;  undef %rowlabel;
   undef @os;
   
 $maxrow=0;  $maxrow = 0;
 $sheettype='';  $sheettype = '';
   
 # filename/reference of the sheet  # filename/reference of the sheet
   $filename = '';
 $filename='';  
   
 # user data  # user data
 $uname='';  $uname = '';
 $uhome='';  $uhome = '';
 $udom='';  $udom  = '';
   
 # course data  # course data
   
 $csec='';  $csec = '';
 $chome='';  $chome= '';
 $cnum='';  $cnum = '';
 $cdom='';  $cdom = '';
 $cid='';  $cid  = '';
 $cfn='';  $cfn  = '';
   
 # symb  # symb
   
 $usymb='';  $usymb = '';
   
   # error messages
   $errormsg = '';
   
 sub mask {  sub mask {
     my ($lower,$upper)=@_;      my ($lower,$upper)=@_;
Line 136  sub mask { Line 205  sub mask {
     } else {      } else {
         if (length($ld)!=length($ud)) {          if (length($ld)!=length($ud)) {
            $num.='(';             $num.='(';
    map {     foreach ($ld=~m/\d/g) {
               $num.='['.$_.'-9]';                $num.='['.$_.'-9]';
            } ($ld=~m/\d/g);     }
            if (length($ud)-length($ld)>1) {             if (length($ud)-length($ld)>1) {
               $num.='|\d{'.(length($ld)+1).','.(length($ud)-1).'}';                $num.='|\d{'.(length($ld)+1).','.(length($ud)-1).'}';
    }     }
            $num.='|';             $num.='|';
            map {             foreach ($ud=~m/\d/g) {
                $num.='[0-'.$_.']';                 $num.='[0-'.$_.']';
            } ($ud=~m/\d/g);             }
            $num.=')';             $num.=')';
        } else {         } else {
            my @lda=($ld=~m/\d/g);             my @lda=($ld=~m/\d/g);
Line 183  sub mask { Line 252  sub mask {
     return '^'.$alpha.$num."\$";      return '^'.$alpha.$num."\$";
 }  }
   
   #-------------------------------------------------------
   
   =item UWCALC(hashname,modules,units,date) 
   
   returns the proportion of the module 
   weights not previously completed by the student.
   
   =over 4
   
   =item hashname 
   
   name of the hash the module dates have been inserted into
   
   =item modules 
   
   reference to a cell which contains a comma deliminated list of modules 
   covered by the assignment.
   
   =item units 
   
   reference to a cell which contains a comma deliminated list of module 
   weights with respect to the assignment
   
   =item date 
   
   reference to a cell which contains the date the assignment was completed.
   
   =back 
   
   =cut
   
   #-------------------------------------------------------
   sub UWCALC {
       my ($hashname,$modules,$units,$date) = @_;
       my @Modules = split(/,/,$modules);
       my @Units   = split(/,/,$units);
       my $total_weight;
       foreach (@Units) {
    $total_weight += $_;
       }
       my $usum=0;
       for (my $i=0; $i<=$#Modules; $i++) {
    if (&HASH($hashname,$Modules[$i]) eq $date) {
       $usum += $Units[$i];
    }
       }
       return $usum/$total_weight;
   }
   
   #-------------------------------------------------------
   
   =item CDLSUM(list) 
   
   returns the sum of the elements in a cell which contains
   a Comma Deliminate List of numerical values.
   'list' is a reference to a cell which contains a comma deliminated list.
   
   =cut
   
   #-------------------------------------------------------
   sub CDLSUM {
       my ($list)=@_;
       my $sum;
       foreach (split/,/,$list) {
    $sum += $_;
       }
       return $sum;
   }
   
   #-------------------------------------------------------
   
   =item CDLITEM(list,index) 
   
   returns the item at 'index' in a Comma Deliminated List.
   
   =over 4
   
   =item list
   
   reference to a cell which contains a comma deliminated list.
   
   =item index 
   
   the Perl index of the item requested (first element in list has
   an index of 0) 
   
   =back
   
   =cut
   
   #-------------------------------------------------------
   sub CDLITEM {
       my ($list,$index)=@_;
       my @Temp = split/,/,$list;
       return $Temp[$index];
   }
   
   #-------------------------------------------------------
   
   =item CDLHASH(name,key,value) 
   
   loads a comma deliminated list of keys into
   the hash 'name', all with a value of 'value'.
   
   =over 4
   
   =item name  
   
   name of the hash.
   
   =item key
   
   (a pointer to) a comma deliminated list of keys.
   
   =item value
   
   a single value to be entered for each key.
   
   =back
   
   =cut
   
   #-------------------------------------------------------
   sub CDLHASH {
       my ($name,$key,$value)=@_;
       my @Keys;
       my @Values;
       # Check to see if we have multiple $key values
       if ($key =~ /[A-z](\-[A-z])?\d+(\-\d+)?/) {
    my $keymask = &mask($key);
    # Assume the keys are addresses
    my @Temp = grep /$keymask/,keys(%sheet_values);
    @Keys = $sheet_values{@Temp};
       } else {
    $Keys[0]= $key;
       }
       my @Temp;
       foreach $key (@Keys) {
    @Temp = (@Temp, split/,/,$key);
       }
       @Keys = @Temp;
       if ($value =~ /[A-z](\-[A-z])?\d+(\-\d+)?/) {
    my $valmask = &mask($value);
    my @Temp = grep /$valmask/,keys(%sheet_values);
    @Values =$sheet_values{@Temp};
       } else {
    $Values[0]= $value;
       }
       $value = $Values[0];
       # Add values to hash
       for (my $i = 0; $i<=$#Keys; $i++) {
    my $key   = $Keys[$i];
    if (! exists ($hashes{$name}->{$key})) {
       $hashes{$name}->{$key}->[0]=$value;
    } else {
       my @Temp = sort(@{$hashes{$name}->{$key}},$value);
       $hashes{$name}->{$key} = \@Temp;
    }
       }
       return "hash '$name' updated";
   }
   
   #-------------------------------------------------------
   
   =item GETHASH(name,key,index) 
   
   returns the element in hash 'name' 
   reference by the key 'key', at index 'index' in the values list.
   
   =cut
   
   #-------------------------------------------------------
   sub GETHASH {
       my ($name,$key,$index)=@_;
       if (! defined($index)) {
    $index = 0;
       }
       if ($key =~ /^[A-z]\d+$/) {
    $key = $sheet_values{$key};
       }
       return $hashes{$name}->{$key}->[$index];
   }
   
   #-------------------------------------------------------
   
   =item CLEARHASH(name) 
   
   clears all the values from the hash 'name'
   
   =item CLEARHASH(name,key) 
   
   clears all the values from the hash 'name' associated with the given key.
   
   =cut
   
   #-------------------------------------------------------
   sub CLEARHASH {
       my ($name,$key)=@_;
       if (defined($key)) {
    if (exists($hashes{$name}->{$key})) {
       $hashes{$name}->{$key}=undef;
       return "hash '$name' key '$key' cleared";
    }
       } else {
    if (exists($hashes{$name})) {
       $hashes{$name}=undef;
       return "hash '$name' cleared";
    }
       }
       return "Error in clearing hash";
   }
   
   #-------------------------------------------------------
   
   =item HASH(name,key,value) 
   
   loads values into an internal hash.  If a key 
   already has a value associated with it, the values are sorted numerically.  
   
   =item HASH(name,key) 
   
   returns the 0th value in the hash 'name' associated with 'key'.
   
   =cut
   
   #-------------------------------------------------------
   sub HASH {
       my ($name,$key,$value)=@_;
       my @Keys;
       undef @Keys;
       my @Values;
       # Check to see if we have multiple $key values
       if ($key =~ /[A-z](\-[A-z])?\d+(\-\d+)?/) {
    my $keymask = &mask($key);
    # Assume the keys are addresses
    my @Temp = grep /$keymask/,keys(%sheet_values);
    @Keys = $sheet_values{@Temp};
       } else {
    $Keys[0]= $key;
       }
       # If $value is empty, return the first value associated 
       # with the first key.
       if (! $value) {
    return $hashes{$name}->{$Keys[0]}->[0];
       }
       # Check to see if we have multiple $value(s) 
       if ($value =~ /[A-z](\-[A-z])?\d+(\-\d+)?/) {
    my $valmask = &mask($value);
    my @Temp = grep /$valmask/,keys(%sheet_values);
    @Values =$sheet_values{@Temp};
       } else {
    $Values[0]= $value;
       }
       # Add values to hash
       for (my $i = 0; $i<=$#Keys; $i++) {
    my $key   = $Keys[$i];
    my $value = ($i<=$#Values ? $Values[$i] : $Values[0]);
    if (! exists ($hashes{$name}->{$key})) {
       $hashes{$name}->{$key}->[0]=$value;
    } else {
       my @Temp = sort(@{$hashes{$name}->{$key}},$value);
       $hashes{$name}->{$key} = \@Temp;
    }
       }
       return $Values[-1];
   }
   
   #-------------------------------------------------------
   
   =item NUM(range)
   
   returns the number of items in the range.
   
   =cut
   
   #-------------------------------------------------------
 sub NUM {  sub NUM {
     my $mask=mask(@_);      my $mask=mask(@_);
     my $num=0;      my $num= $#{@{grep(/$mask/,keys(%sheet_values))}}+1;
     map {  
         $num++;  
     } grep /$mask/,keys %v;  
     return $num;         return $num;   
 }  }
   
Line 196  sub BIN { Line 538  sub BIN {
     my ($low,$high,$lower,$upper)=@_;      my ($low,$high,$lower,$upper)=@_;
     my $mask=mask($lower,$upper);      my $mask=mask($lower,$upper);
     my $num=0;      my $num=0;
     map {      foreach (grep /$mask/,keys(%sheet_values)) {
         if (($v{$_}>=$low) && ($v{$_}<=$high)) {          if (($sheet_values{$_}>=$low) && ($sheet_values{$_}<=$high)) {
             $num++;              $num++;
         }          }
     } grep /$mask/,keys %v;      }
     return $num;         return $num;   
 }  }
   
   
   #-------------------------------------------------------
   
   =item SUM(range)
   
   returns the sum of items in the range.
   
   =cut
   
   #-------------------------------------------------------
 sub SUM {  sub SUM {
     my $mask=mask(@_);      my $mask=mask(@_);
     my $sum=0;      my $sum=0;
     map {      foreach (grep /$mask/,keys(%sheet_values)) {
         $sum+=$v{$_};          $sum+=$sheet_values{$_};
     } grep /$mask/,keys %v;      }
     return $sum;         return $sum;   
 }  }
   
   #-------------------------------------------------------
   
   =item MEAN(range)
   
   compute the average of the items in the range.
   
   =cut
   
   #-------------------------------------------------------
 sub MEAN {  sub MEAN {
     my $mask=mask(@_);      my $mask=mask(@_);
     my $sum=0; my $num=0;      my $sum=0; my $num=0;
     map {      foreach (grep /$mask/,keys(%sheet_values)) {
         $sum+=$v{$_};          $sum+=$sheet_values{$_};
         $num++;          $num++;
     } grep /$mask/,keys %v;      }
     if ($num) {      if ($num) {
        return $sum/$num;         return $sum/$num;
     } else {      } else {
Line 228  sub MEAN { Line 588  sub MEAN {
     }         }   
 }  }
   
   #-------------------------------------------------------
   
   =item STDDEV(range)
   
   compute the standard deviation of the items in the range.
   
   =cut
   
   #-------------------------------------------------------
 sub STDDEV {  sub STDDEV {
     my $mask=mask(@_);      my $mask=mask(@_);
     my $sum=0; my $num=0;      my $sum=0; my $num=0;
     map {      foreach (grep /$mask/,keys(%sheet_values)) {
         $sum+=$v{$_};          $sum+=$sheet_values{$_};
         $num++;          $num++;
     } grep /$mask/,keys %v;      }
     unless ($num>1) { return undef; }      unless ($num>1) { return undef; }
     my $mean=$sum/$num;      my $mean=$sum/$num;
     $sum=0;      $sum=0;
     map {      foreach (grep /$mask/,keys(%sheet_values)) {
         $sum+=($v{$_}-$mean)**2;          $sum+=($sheet_values{$_}-$mean)**2;
     } grep /$mask/,keys %v;      }
     return sqrt($sum/($num-1));          return sqrt($sum/($num-1));    
 }  }
   
   #-------------------------------------------------------
   
   =item PROD(range)
   
   compute the product of the items in the range.
   
   =cut
   
   #-------------------------------------------------------
 sub PROD {  sub PROD {
     my $mask=mask(@_);      my $mask=mask(@_);
     my $prod=1;      my $prod=1;
     map {      foreach (grep /$mask/,keys(%sheet_values)) {
         $prod*=$v{$_};          $prod*=$sheet_values{$_};
     } grep /$mask/,keys %v;      }
     return $prod;         return $prod;   
 }  }
   
   #-------------------------------------------------------
   
   =item MAX(range)
   
   compute the maximum of the items in the range.
   
   =cut
   
   #-------------------------------------------------------
 sub MAX {  sub MAX {
     my $mask=mask(@_);      my $mask=mask(@_);
     my $max='-';      my $max='-';
     map {      foreach (grep /$mask/,keys(%sheet_values)) {
         unless ($max) { $max=$v{$_}; }          unless ($max) { $max=$sheet_values{$_}; }
         if (($v{$_}>$max) || ($max eq '-')) { $max=$v{$_}; }          if (($sheet_values{$_}>$max) || ($max eq '-')) { $max=$sheet_values{$_}; }
     } grep /$mask/,keys %v;      } 
     return $max;         return $max;   
 }  }
   
   #-------------------------------------------------------
   
   =item MIN(range)
   
   compute the minimum of the items in the range.
   
   =cut
   
   #-------------------------------------------------------
 sub MIN {  sub MIN {
     my $mask=mask(@_);      my $mask=mask(@_);
     my $min='-';      my $min='-';
     map {      foreach (grep /$mask/,keys(%sheet_values)) {
         unless ($max) { $max=$v{$_}; }          unless ($max) { $max=$sheet_values{$_}; }
         if (($v{$_}<$min) || ($min eq '-')) { $min=$v{$_}; }          if (($sheet_values{$_}<$min) || ($min eq '-')) { 
     } grep /$mask/,keys %v;              $min=$sheet_values{$_}; 
           }
       }
     return $min;         return $min;   
 }  }
   
   #-------------------------------------------------------
   
   =item SUMMAX(num,lower,upper)
   
   compute the sum of the largest 'num' items in the range from
   'lower' to 'upper'
   
   =cut
   
   #-------------------------------------------------------
 sub SUMMAX {  sub SUMMAX {
     my ($num,$lower,$upper)=@_;      my ($num,$lower,$upper)=@_;
     my $mask=mask($lower,$upper);      my $mask=mask($lower,$upper);
     my @inside=();      my @inside=();
     map {      foreach (grep /$mask/,keys(%sheet_values)) {
  $inside[$#inside+1]=$v{$_};   push (@inside,$sheet_values{$_});
     } grep /$mask/,keys %v;      }
     @inside=sort(@inside);      @inside=sort(@inside);
     my $sum=0; my $i;      my $sum=0; my $i;
     for ($i=$#inside;(($i>$#inside-$num) && ($i>=0));$i--) {       for ($i=$#inside;(($i>$#inside-$num) && ($i>=0));$i--) { 
Line 288  sub SUMMAX { Line 696  sub SUMMAX {
     return $sum;         return $sum;   
 }  }
   
   #-------------------------------------------------------
   
   =item SUMMIN(num,lower,upper)
   
   compute the sum of the smallest 'num' items in the range from
   'lower' to 'upper'
   
   =cut
   
   #-------------------------------------------------------
 sub SUMMIN {  sub SUMMIN {
     my ($num,$lower,$upper)=@_;      my ($num,$lower,$upper)=@_;
     my $mask=mask($lower,$upper);      my $mask=mask($lower,$upper);
     my @inside=();      my @inside=();
     map {      foreach (grep /$mask/,keys(%sheet_values)) {
  $inside[$#inside+1]=$v{$_};   $inside[$#inside+1]=$sheet_values{$_};
     } grep /$mask/,keys %v;      }
     @inside=sort(@inside);      @inside=sort(@inside);
     my $sum=0; my $i;      my $sum=0; my $i;
     for ($i=0;(($i<$num) && ($i<=$#inside));$i++) {       for ($i=0;(($i<$num) && ($i<=$#inside));$i++) { 
Line 303  sub SUMMIN { Line 721  sub SUMMIN {
     return $sum;         return $sum;   
 }  }
   
   #-------------------------------------------------------
   
   =item MINPARM(parametername)
   
   Returns the minimum value of the parameters matching the parametername.
   parametername should be a string such as 'duedate'.
   
   =cut
   
   #-------------------------------------------------------
   sub MINPARM {
       my ($expression) = @_;
       my $min = undef;
       study($expression);
       foreach $parameter (keys(%c)) {
           next if ($parameter !~ /$expression/);
           if ((! defined($min)) || ($min > $c{$parameter})) {
               $min = $c{$parameter} 
           }
       }
       return $min;
   }
   
   #-------------------------------------------------------
   
   =item MAXPARM(parametername)
   
   Returns the maximum value of the parameters matching the input parameter name.
   parametername should be a string such as 'duedate'.
   
   =cut
   
   #-------------------------------------------------------
   sub MAXPARM {
       my ($expression) = @_;
       my $max = undef;
       study($expression);
       foreach $parameter (keys(%c)) {
           next if ($parameter !~ /$expression/);
           if ((! defined($min)) || ($max < $c{$parameter})) {
               $max = $c{$parameter} 
           }
       }
       return $max;
   }
   
   #--------------------------------------------------------
   sub expandnamed {
       my $expression=shift;
       if ($expression=~/^\&/) {
    my ($func,$var,$formula)=($expression=~/^\&(\w+)\(([^\;]+)\;(.*)\)/);
    my @vars=split(/\W+/,$formula);
           my %values=();
           undef %values;
    foreach ( @vars ) {
               my $varname=$_;
               if ($varname=~/\D/) {
                  $formula=~s/$varname/'$c{\''.$varname.'\'}'/ge;
                  $varname=~s/$var/\(\\w\+\)/g;
          foreach (keys(%c)) {
     if ($_=~/$varname/) {
         $values{$1}=1;
                     }
                  }
       }
           }
           if ($func eq 'EXPANDSUM') {
               my $result='';
       foreach (keys(%values)) {
                   my $thissum=$formula;
                   $thissum=~s/$var/$_/g;
                   $result.=$thissum.'+';
               } 
               $result=~s/\+$//;
               return $result;
           } else {
       return 0;
           }
       } else {
           # it is not a function, so it is a parameter name
           # We should do the following:
           #    1. Take the list of parameter names
           #    2. look through the list for ones that match the parameter we want
           #    3. If there are no collisions, return the one that matches
           #    4. If there is a collision, return 'bad parameter name error'
           my $returnvalue = '';
           my @matches = ();
           $#matches = -1;
           study $expression;
           foreach $parameter (keys(%c)) {
               push @matches,$parameter if ($parameter =~ /$expression/);
           }
           if ($#matches == 0) {
               $returnvalue = '$c{\''.$matches[0].'\'}';
           } elsif ($#matches > 0) {
               # more than one match.  Look for a concise one
               $returnvalue =  "'non-unique parameter name : $expression'";
               foreach (@matches) {
                   if (/^$expression$/) {
                       $returnvalue = '$c{\''.$_.'\'}';
                   }
               }
           } else {
               $returnvalue =  "'bad parameter name : $expression'";
           }
           return $returnvalue;
       }
   }
   
 sub sett {  sub sett {
     %t=();      %t=();
     my $pattern='';      my $pattern='';
Line 311  sub sett { Line 838  sub sett {
     } else {      } else {
         $pattern='[A-Z]';          $pattern='[A-Z]';
     }      }
     map {      # Deal with the template row
  if ($_=~/template\_(\w)/) {      foreach (keys(%f)) {
   my $col=$1;   next if ($_!~/template\_(\w)/);
           unless ($col=~/^$pattern/) {          my $col=$1;
             map {          next if ($col=~/^$pattern/);
       if ($_=~/A(\d+)/) {          foreach (keys(%f)) {
  my $trow=$1;              next if ($_!~/A(\d+)/);
                 if ($trow) {              my $trow=$1;
     my $lb=$col.$trow;              next if (! $trow);
                     $t{$lb}=$f{'template_'.$col};              # Get the name of this cell
                     $t{$lb}=~s/\#/$trow/g;              my $lb=$col.$trow;
                     $t{$lb}=~s/\.\.+/\,/g;              # Grab the template declaration
                     $t{$lb}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$v\{\'$2\'\}/g;              $t{$lb}=$f{'template_'.$col};
                     $t{$lb}=~s/(^|[^\"\'])\[(\w+)\]/$1\$c\{\'$2\'\}/g;              # Replace '#' with the row number
                 }              $t{$lb}=~s/\#/$trow/g;
       }              # Replace '....' with ','
             } keys %f;              $t{$lb}=~s/\.\.+/\,/g;
   }              # Replace 'A0' with the value from 'A0'
       }              $t{$lb}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$sheet_values\{\'$2\'\}/g;
     } keys %f;              # Replace parameters
     map {              $t{$lb}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.&expandnamed($2)/ge;
           }
       }
       # Deal with the normal cells
       foreach (keys(%f)) {
  if (($f{$_}) && ($_!~/template\_/)) {   if (($f{$_}) && ($_!~/template\_/)) {
             my $matches=($_=~/^$pattern(\d+)/);              my $matches=($_=~/^$pattern(\d+)/);
             if  (($matches) && ($1)) {              if  (($matches) && ($1)) {
Line 341  sub sett { Line 872  sub sett {
             } else {              } else {
        $t{$_}=$f{$_};         $t{$_}=$f{$_};
                $t{$_}=~s/\.\.+/\,/g;                 $t{$_}=~s/\.\.+/\,/g;
                $t{$_}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$v\{\'$2\'\}/g;                 $t{$_}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$sheet_values\{\'$2\'\}/g;
                $t{$_}=~s/(^|[^\"\'])\[([\w\.]+)\]/$1\$c\{\'$2\'\}/g;                 $t{$_}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.&expandnamed($2)/ge;
             }              }
         }          }
     } keys %f;      }
       # For inserted lines, [B-Z] is also valid
       unless ($sheettype eq 'assesscalc') {
          foreach (keys(%f)) {
      if ($_=~/[B-Z](\d+)/) {
          if ($f{'A'.$1}=~/^[\~\-]/) {
              $t{$_}=$f{$_};
                     $t{$_}=~s/\.\.+/\,/g;
                     $t{$_}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$sheet_values\{\'$2\'\}/g;
                     $t{$_}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.&expandnamed($2)/ge;
                  }
              }
          }
       }
       # For some reason 'A0' gets special treatment...  This seems superfluous
       # but I imagine it is here for a reason.
     $t{'A0'}=$f{'A0'};      $t{'A0'}=$f{'A0'};
     $t{'A0'}=~s/\.\.+/\,/g;      $t{'A0'}=~s/\.\.+/\,/g;
     $t{'A0'}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$v\{\'$2\'\}/g;      $t{'A0'}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$sheet_values\{\'$2\'\}/g;
     $t{'A0'}=~s/(^|[^\"\'])\[([\w\.]+)\]/$1\$c\{\'$2\'\}/g;      $t{'A0'}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.&expandnamed($2)/ge;
 }  }
   
 sub calc {  sub calc {
     %v=();      undef %sheet_values;
     &sett();      &sett();
     my $notfinished=1;      my $notfinished=1;
       my $lastcalc='';
     my $depth=0;      my $depth=0;
     while ($notfinished) {      while ($notfinished) {
  $notfinished=0;   $notfinished=0;
         map {          foreach (keys(%t)) {
             my $old=$v{$_};              my $old=$sheet_values{$_};
             $v{$_}=eval($t{$_});              $sheet_values{$_}=eval $t{$_};
     if ($@) {      if ($@) {
  %v=();   undef %sheet_values;
                 return $@;                  return $_.': '.$@;
             }              }
     if ($v{$_} ne $old) { $notfinished=1; }      if ($sheet_values{$_} ne $old) { $notfinished=1; $lastcalc=$_; }
         } keys %t;          }
         $depth++;          $depth++;
         if ($depth>100) {          if ($depth>100) {
     %v=();      undef %sheet_values;
             return 'Maximum calculation depth exceeded';              return $lastcalc.': Maximum calculation depth exceeded';
         }          }
     }      }
     return '';      return '';
Line 380  sub calc { Line 927  sub calc {
 sub templaterow {  sub templaterow {
     my @cols=();      my @cols=();
     $cols[0]='<b><font size=+1>Template</font></b>';      $cols[0]='<b><font size=+1>Template</font></b>';
     map {      foreach ('A','B','C','D','E','F','G','H','I','J','K','L','M',
        'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
        'a','b','c','d','e','f','g','h','i','j','k','l','m',
        'n','o','p','q','r','s','t','u','v','w','x','y','z') {
         my $fm=$f{'template_'.$_};          my $fm=$f{'template_'.$_};
         $fm=~s/[\'\"]/\&\#34;/g;          $fm=~s/[\'\"]/\&\#34;/g;
         $cols[$#cols+1]="'template_$_','$fm'".'___eq___'.$fm;          push(@cols,"'template_$_','$fm'".'___eq___'.$fm);
     } ('A','B','C','D','E','F','G','H','I','J','K','L','M',      }
        'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',  
        'a','b','c','d','e','f','g','h','i','j','k','l','m',  
        'n','o','p','q','r','s','t','u','v','w','x','y','z');  
     return @cols;      return @cols;
 }  }
   
   #
   # This is actually used for the student spreadsheet, not the assessment sheet
   # Do not be fooled by the name!
   #
 sub outrowassess {  sub outrowassess {
       # $n is the current row number
     my $n=shift;      my $n=shift;
     my @cols=();      my @cols=();
     if ($n) {      if ($n) {
        $cols[0]=$rl{$f{'A'.$n}};          my ($usy,$ufn)=split(/__&&&\__/,$f{'A'.$n});
           if ($rowlabel{$usy}) {
               $cols[0]=$rowlabel{$usy}.'<br>'.
                   '<select name="sel_'.$n.'" onChange="changesheet('.$n.')">'.
                       '<option name="default">Default</option>';
           } else { 
               $cols[0]=''; 
           }
           foreach (@os) {
               $cols[0].='<option name="'.$_.'"';
               if ($ufn eq $_) {
                   $cols[0].=' selected';
               }
               $cols[0].='>'.$_.'</option>';
           }
           $cols[0].='</select>';
     } else {      } else {
        $cols[0]='<b><font size=+1>Export</font></b>';          $cols[0]='<b><font size=+1>Export</font></b>';
     }      }
     map {      foreach ('A','B','C','D','E','F','G','H','I','J','K','L','M',
        'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
        'a','b','c','d','e','f','g','h','i','j','k','l','m',
        'n','o','p','q','r','s','t','u','v','w','x','y','z') {
         my $fm=$f{$_.$n};          my $fm=$f{$_.$n};
         $fm=~s/[\'\"]/\&\#34;/g;          $fm=~s/[\'\"]/\&\#34;/g;
         $cols[$#cols+1]="'$_$n','$fm'".'___eq___'.$v{$_.$n};          push(@cols,"'$_$n','$fm'".'___eq___'.$sheet_values{$_.$n});
     } ('A','B','C','D','E','F','G','H','I','J','K','L','M',      }
        'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',  
        'a','b','c','d','e','f','g','h','i','j','k','l','m',  
        'n','o','p','q','r','s','t','u','v','w','x','y','z');  
     return @cols;      return @cols;
 }  }
   
Line 414  sub outrow { Line 981  sub outrow {
     my $n=shift;      my $n=shift;
     my @cols=();      my @cols=();
     if ($n) {      if ($n) {
        $cols[0]=$rl{$f{'A'.$n}};         $cols[0]=$rowlabel{$f{'A'.$n}};
     } else {      } else {
        $cols[0]='<b><font size=+1>Export</font></b>';         $cols[0]='<b><font size=+1>Export</font></b>';
     }      }
     map {      foreach ('A','B','C','D','E','F','G','H','I','J','K','L','M',
        'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
        'a','b','c','d','e','f','g','h','i','j','k','l','m',
        'n','o','p','q','r','s','t','u','v','w','x','y','z') {
         my $fm=$f{$_.$n};          my $fm=$f{$_.$n};
         $fm=~s/[\'\"]/\&\#34;/g;          $fm=~s/[\'\"]/\&\#34;/g;
         $cols[$#cols+1]="'$_$n','$fm'".'___eq___'.$v{$_.$n};          push(@cols,"'$_$n','$fm'".'___eq___'.$sheet_values{$_.$n});
     } ('A','B','C','D','E','F','G','H','I','J','K','L','M',      }
        'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',  
        'a','b','c','d','e','f','g','h','i','j','k','l','m',  
        'n','o','p','q','r','s','t','u','v','w','x','y','z');  
     return @cols;      return @cols;
 }  }
   
 sub exportrowa {  sub exportrowa {
     my @exportarray=();      my @exportarray=();
     map {      foreach ('A','B','C','D','E','F','G','H','I','J','K','L','M',
  $exportarray[$#exportarray+1]=$v{$_.'0'};       'N','O','P','Q','R','S','T','U','V','W','X','Y','Z') {
     } ('A','B','C','D','E','F','G','H','I','J','K','L','M',   push(@exportarray,$sheet_values{$_.'0'});
        'N','O','P','Q','R','S','T','U','V','W','X','Y','Z');      } 
     return @exportarray;      return @exportarray;
 }  }
   
Line 458  sub setconstants { Line 1025  sub setconstants {
     %{$safeeval->varglob('c')}=%c;      %{$safeeval->varglob('c')}=%c;
 }  }
   
   # --------------------------------------------- Set names of other spreadsheets
   
   sub setothersheets {
       my ($safeeval,@os)=@_;
       @{$safeeval->varglob('os')}=@os;
   }
   
 # ------------------------------------------------ Add or change formula values  # ------------------------------------------------ Add or change formula values
   
 sub setrowlabels {  sub setrowlabels {
     my ($safeeval,%rl)=@_;      my ($safeeval,%rowlabel)=@_;
     %{$safeeval->varglob('rl')}=%rl;      %{$safeeval->varglob('rowlabel')}=%rowlabel;
 }  }
   
 # ------------------------------------------------------- Calculate spreadsheet  # ------------------------------------------------------- Calculate spreadsheet
   
 sub calcsheet {  sub calcsheet {
     my $safeeval=shift;      my $safeeval=shift;
     $safeeval->reval('&calc();');      return $safeeval->reval('&calc();');
 }  }
   
 # ------------------------------------------------------------------ Get values  # ------------------------------------------------------------------ Get values
   
 sub getvalues {  sub getvalues {
     my $safeeval=shift;      my $safeeval=shift;
     return $safeeval->reval('%v');      return $safeeval->reval('%sheet_values');
 }  }
   
 # ---------------------------------------------------------------- Get formulas  # ---------------------------------------------------------------- Get formulas
Line 486  sub getformulas { Line 1060  sub getformulas {
     return %{$safeeval->varglob('f')};      return %{$safeeval->varglob('f')};
 }  }
   
   # ----------------------------------------------------- Get value of $f{'A'.$n}
   
   sub getfa {
       my ($safeeval,$n)=@_;
       return $safeeval->reval('$f{"A'.$n.'"}');
   }
   
 # -------------------------------------------------------------------- Get type  # -------------------------------------------------------------------- Get type
   
 sub gettype {  sub gettype {
Line 598  sub exportdata { Line 1179  sub exportdata {
     return $safeeval->reval('&exportrowa()');      return $safeeval->reval('&exportrowa()');
 }  }
   
   
 # ========================================================== End of Spreadsheet  # ========================================================== End of Spreadsheet
 # =============================================================================  # =============================================================================
   
Line 610  sub rown { Line 1192  sub rown {
     my ($safeeval,$n)=@_;      my ($safeeval,$n)=@_;
     my $defaultbg;      my $defaultbg;
     my $rowdata='';      my $rowdata='';
       my $dataflag=0;
     unless ($n eq '-') {      unless ($n eq '-') {
        $defaultbg=((($n-1)/5)==int(($n-1)/5))?'#E0E0':'#FFFF';         $defaultbg=((($n-1)/5)==int(($n-1)/5))?'#E0E0':'#FFFF';
     } else {      } else {
        $defaultbg='#E0FF';         $defaultbg='#E0FF';
     }      }
     if ((($n-1)/25)==int(($n-1)/25)) {      unless ($ENV{'form.showcsv'}) {
         my $what='Student';         $rowdata.="\n<tr><td><b><font size=+1>$n</font></b></td>";
         if (&gettype($safeeval) eq 'assesscalc') {      } else {
     $what='Item';         $rowdata.="\n".'"'.$n.'"';
  } elsif (&gettype($safeeval) eq 'studentcalc') {  
             $what='Assessment';  
         }  
  $rowdata.="</table>\n<br><table border=2>".  
         '<tr><td>&nbsp;<td>'.$what.'</td>';  
         map {  
            $rowdata.='<td>'.$_.'</td>';  
         } ('A','B','C','D','E','F','G','H','I','J','K','L','M',  
            'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',  
            'a','b','c','d','e','f','g','h','i','j','k','l','m',  
            'n','o','p','q','r','s','t','u','v','w','x','y','z');  
         $rowdata.='</tr>';  
     }      }
     $rowdata.="\n<tr><td><b><font size=+1>$n</font></b></td>";  
     my $showf=0;      my $showf=0;
     my $proc;      my $proc;
     my $maxred;      my $maxred=1;
     if (&gettype($safeeval) eq 'assesscalc') {      my $sheettype=&gettype($safeeval);
       if ($sheettype eq 'studentcalc') {
         $proc='&outrowassess';          $proc='&outrowassess';
         $maxred=1;          $maxred=26;
     } else {      } else {
         $proc='&outrow';          $proc='&outrow';
       }
       if ($sheettype eq 'assesscalc') {
           $maxred=1;
       } else {
         $maxred=26;          $maxred=26;
     }      }
     if ($n eq '-') { $proc='&templaterow'; $n=-1; }      if (&getfa($safeeval,$n)=~/^[\~\-]/) { $maxred=1; }
     map {      if ($n eq '-') { 
           $proc='&templaterow'; 
           $n=-1; 
           $dataflag=1; 
       }
       foreach ($safeeval->reval($proc.'('.$n.')')) {
        my $bgcolor=$defaultbg.((($showf-1)/5==int(($showf-1)/5))?'99':'DD');         my $bgcolor=$defaultbg.((($showf-1)/5==int(($showf-1)/5))?'99':'DD');
        my ($fm,$vl)=split(/\_\_\_eq\_\_\_/,$_);         my ($fm,$vl)=split(/\_\_\_eq\_\_\_/,$_);
          if ((($vl ne '') || ($vl eq '0')) &&
              (($showf==1) || ($sheettype ne 'studentcalc'))) { $dataflag=1; }
        if ($showf==0) { $vl=$_; }         if ($showf==0) { $vl=$_; }
         unless ($ENV{'form.showcsv'}) {
        if ($showf<=$maxred) { $bgcolor='#FFDDDD'; }         if ($showf<=$maxred) { $bgcolor='#FFDDDD'; }
        if (($n==0) && ($showf<=26)) { $bgcolor='#CCCCFF'; }          if (($n==0) && ($showf<=26)) { $bgcolor='#CCCCFF'; } 
        if (($showf>$maxred) || ((!$n) && ($showf>0))) {         if (($showf>$maxred) || ((!$n) && ($showf>0))) {
Line 660  sub rown { Line 1243  sub rown {
        } else {         } else {
            $rowdata.='<td bgcolor='.$bgcolor.'>&nbsp;'.$vl.'&nbsp;</td>';             $rowdata.='<td bgcolor='.$bgcolor.'>&nbsp;'.$vl.'&nbsp;</td>';
        }         }
         } else {
     $rowdata.=',"'.$vl.'"';
         }
        $showf++;         $showf++;
     } $safeeval->reval($proc.'('.$n.')');      }  # End of foreach($safeval...)
     return $rowdata.'</tr>';      if ($ENV{'form.showall'} || ($dataflag)) {
          return $rowdata.($ENV{'form.showcsv'}?'':'</tr>');
       } else {
          return '';
       }
 }  }
   
 # ------------------------------------------------------------- Print out sheet  # ------------------------------------------------------------- Print out sheet
Line 682  sub outsheet { Line 1272  sub outsheet {
         $realm='Course';          $realm='Course';
     }      }
     my $maxyellow=52-$maxred;      my $maxyellow=52-$maxred;
     my $tabledata=      my $tabledata;
       unless ($ENV{'form.showcsv'}) {
          $tabledata=
         '<table border=2><tr><th colspan=2 rowspan=2><font size=+2>'.          '<table border=2><tr><th colspan=2 rowspan=2><font size=+2>'.
                   $realm.'</font></th>'.                    $realm.'</font></th>'.
                   '<td bgcolor=#FFDDDD colspan='.$maxred.                    '<td bgcolor=#FFDDDD colspan='.$maxred.
                   '><b><font size=+1>Import</font></b></td>'.                    '><b><font size=+1>Import</font></b></td>'.
                   '<td colspan='.$maxyellow.                    '<td colspan='.$maxyellow.
   '><b><font size=+1>Calculations</font></b></td></tr><tr>';    '><b><font size=+1>Calculations</font></b></td></tr><tr>';
     my $showf=0;         my $showf=0;
     map {         foreach ('A','B','C','D','E','F','G','H','I','J','K','L','M',
         $showf++;                  'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
         if ($showf<=$maxred) {                   'a','b','c','d','e','f','g','h','i','j','k','l','m',
            $tabledata.='<td bgcolor="#FFDDDD">';                   'n','o','p','q','r','s','t','u','v','w','x','y','z') {
         } else {             $showf++;
            $tabledata.='<td>';             if ($showf<=$maxred) { 
         }                 $tabledata.='<td bgcolor="#FFDDDD">'; 
         $tabledata.="<b><font size=+1>$_</font></b></td>";             } else {
     } ('A','B','C','D','E','F','G','H','I','J','K','L','M',                 $tabledata.='<td>';
        'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',             }
        'a','b','c','d','e','f','g','h','i','j','k','l','m',             $tabledata.="<b><font size=+1>$_</font></b></td>";
        'n','o','p','q','r','s','t','u','v','w','x','y','z');         }
     $tabledata.='</tr>';         $tabledata.='</tr>'.&rown($safeeval,'-').&rown($safeeval,0);
      } else { $tabledata='<pre>'; }
   
       $r->print($tabledata);
   
     my $row;      my $row;
     my $maxrow=&getmaxrow($safeeval);      my $maxrow=&getmaxrow($safeeval);
     $tabledata.=&rown($safeeval,'-');  
     $r->print($tabledata);      my @sortby=();
     for ($row=0;$row<=$maxrow;$row++) {      my @sortidx=();
         $r->print(&rown($safeeval,$row));      for ($row=1;$row<=$maxrow;$row++) {
          $sortby[$row-1]=$safeeval->reval('$f{"A'.$row.'"}');
          $sortidx[$row-1]=$row-1;
     }      }
     $r->print('</table>');      @sortidx=sort { $sortby[$a] cmp $sortby[$b]; } @sortidx;
           my $what='Student';
           if (&gettype($safeeval) eq 'assesscalc') {
       $what='Item';
    } elsif (&gettype($safeeval) eq 'studentcalc') {
               $what='Assessment';
           }
   
       my $n=0;
       for ($row=0;$row<$maxrow;$row++) {
           my $thisrow=&rown($safeeval,$sortidx[$row]+1);
           if ($thisrow) {
               if (($n/25==int($n/25)) && (!$ENV{'form.showcsv'})) {
                   $r->print("</table>\n<br>\n");
                   $r->rflush();
                   $r->print('<table border=2><tr><td>&nbsp;<td>'.$what.'</td>');
                   $r->print('<td>'.join('</td><td>',
                                         (split(//,'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
                                               'abcdefghijklmnopqrstuvwxyz'))).
                             "</td></tr>\n");
               }
               $n++;
               $r->print($thisrow);
           }
       }
       $r->print($ENV{'form.showcsv'}?'</pre>':'</table>');
 }  }
   
 #  #
 # -------------------------------------- Read spreadsheet formulas for a course  # ----------------------------------------------- Read list of available sheets
 #  # 
   sub othersheets {
 sub readsheet {      my ($safeeval,$stype)=@_;
   my ($safeeval,$fn)=@_;      #
   my $stype=&gettype($safeeval);      my $cnum  = &getcnum($safeeval);
   my $cnum=&getcnum($safeeval);      my $cdom  = &getcdom($safeeval);
   my $cdom=&getcdom($safeeval);      my $chome = &getchome($safeeval);
   my $chome=&getchome($safeeval);      #
       my @alternatives=();
 # --------- There is no filename. Look for defaults in course and global, cache      my %results=&Apache::lonnet::dump($stype.'_spreadsheets',$cdom,$cnum);
       my ($tmp) = keys(%results);
   unless($fn) {      unless ($tmp =~ /^(con_lost|error|no_such_host)/i) {
       unless ($fn=$defaultsheets{$cnum.'_'.$cdom.'_'.$stype}) {          @alternatives = sort (keys(%results));
          $fn=&Apache::lonnet::reply('get:'.$cdom.':'.$cnum.      }
                                     ':environment:spreadsheet_default_'.$stype,      return @alternatives; 
                                     $chome);  }
          unless (($fn) && ($fn!~/^error\:/)) {  
      $fn='default_'.$stype;  
          }  
          $defaultsheets{$cnum.'_'.$cdom.'_'.$stype}=$fn;   
       }  
   }  
   
 # ---------------------------------------------------------- fn now has a value  
   
   &setfilename($safeeval,$fn);  
   
 # ------------------------------------------------------ see if sheet is cached  
   my $fstring='';  
   if ($fstring=$spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}) {  
       &setformulas($safeeval,split(/\_\_\_\;\_\_\_/,$fstring));  
   } else {  
   
 # ---------------------------------------------------- Not cached, need to read  #
   # -------------------------------------- Parse a spreadsheet
   # 
   sub parse_sheet {
       # $sheetxml is a scalar reference or a scalar
       my ($sheetxml) = @_;
       if (! ref($sheetxml)) {
           my $tmp = $sheetxml;
           $sheetxml = \$tmp;
       }
       my %f;
       my $parser=HTML::TokeParser->new($sheetxml);
       my $token;
       while ($token=$parser->get_token) {
           if ($token->[0] eq 'S') {
               if ($token->[1] eq 'field') {
                   $f{$token->[2]->{'col'}.$token->[2]->{'row'}}=
                       $parser->get_text('/field');
               }
               if ($token->[1] eq 'template') {
                   $f{'template_'.$token->[2]->{'col'}}=
                       $parser->get_text('/template');
               }
           }
       }
       return \%f;
   }
   
      my %f=();  #
   # -------------------------------------- Read spreadsheet formulas for a course
   #
   
      if ($fn=~/^default\_/) {  sub readsheet {
  my $sheetxml='';      my ($safeeval,$fn)=@_;
        {      my $stype = &gettype($safeeval);
          my $fh;      my $cnum  = &getcnum($safeeval);
          if ($fh=Apache::File->new($includedir.      my $cdom  = &getcdom($safeeval);
                          '/default.'.&gettype($safeeval))) {      my $chome = &getchome($safeeval);
                $sheetxml=join('',<$fh>);  
           }      if (! defined($fn)) {
        }          # There is no filename. Look for defaults in course and global, cache
         my $parser=HTML::TokeParser->new(\$sheetxml);          unless ($fn=$defaultsheets{$cnum.'_'.$cdom.'_'.$stype}) {
         my $token;              my %tmphash = &Apache::lonnet::get('environment',
         while ($token=$parser->get_token) {                                                 ['spreadsheet_default_'.$stype],
           if ($token->[0] eq 'S') {                                                 $cdom,$cnum);
       if ($token->[1] eq 'field') {              my ($tmp) = keys(%tmphash);
   $f{$token->[2]->{'col'}.$token->[2]->{'row'}}=              if ($tmp =~ /^(con_lost|error|no_such_host)/i) {
       $parser->get_text('/field');                  $fn = 'default_'.$stype;
       }              } else {
              if ($token->[1] eq 'template') {                  $fn = $tmphash{'spreadsheet_default_'.$stype};
                  $f{'template_'.$token->[2]->{'col'}}=              } 
                      $parser->get_text('/template');              unless (($fn) && ($fn!~/^error\:/)) {
              }                  $fn='default_'.$stype;
           }              }
               $defaultsheets{$cnum.'_'.$cdom.'_'.$stype}=$fn; 
         }          }
       } else {      }
           my $sheet='';      # $fn now has a value
           my $reply=&Apache::lonnet::reply('dump:'.$cdom.':'.$cnum.':'.$fn,      &setfilename($safeeval,$fn);
                                          $chome);      # see if sheet is cached
           unless ($reply=~/^error\:/) {      my $fstring='';
              $sheet=$reply;      if ($fstring=$spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}) {
   }          &setformulas($safeeval,split(/\_\_\_\;\_\_\_/,$fstring));
           map {      } else {
              my ($name,$value)=split(/\=/,$_);          # Not cached, need to read
              $f{&Apache::lonnet::unescape($name)}=          my %f=();
         &Apache::lonnet::unescape($value);          if ($fn=~/^default\_/) {
           } split(/\&/,$sheet);              my $sheetxml='';
        }              my $fh;
 # --------------------------------------------------------------- Cache and set              my $dfn=$fn;
        $spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}=join('___;___',%f);                $dfn=~s/\_/\./g;
        &setformulas($safeeval,%f);              if ($fh=Apache::File->new($includedir.'/'.$dfn)) {
                   $sheetxml=join('',<$fh>);
               } else {
                   $sheetxml='<field row="0" col="A">"Error"</field>';
               }
               %f=%{&parse_sheet(\$sheetxml)};
           } elsif($fn=~/\/*\.spreadsheet$/) {
               my $sheetxml=&Apache::lonnet::getfile
                   (&Apache::lonnet::filelocation('',$fn));
               if ($sheetxml == -1) {
                   $sheetxml='<field row="0" col="A">"Error loading spreadsheet '
                       .$fn.'"</field>';
               }
               %f=%{&parse_sheet(\$sheetxml)};
           } else {
               my $sheet='';
               my %tmphash = &Apache::lonnet::dump($fn,$cdom,$cnum);
               my ($tmp) = keys(%tmphash);
               unless ($tmp =~ /^(con_lost|error|no_such_host)/i) {
                   foreach (keys(%tmphash)) {
                       $f{$_}=$tmphash{$_};
                   }
               }
           }
           # Cache and set
           $spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}=join('___;___',%f);  
           &setformulas($safeeval,%f);
     }      }
 }  }
   
Line 818  sub makenewsheet { Line 1481  sub makenewsheet {
 # ------------------------------------------------------------ Save spreadsheet  # ------------------------------------------------------------ Save spreadsheet
   
 sub writesheet {  sub writesheet {
   my ($safeeval,$makedef)=@_;      my ($safeeval,$makedef)=@_;
   my $cid=&getcid($safeeval);      my $cid=&getcid($safeeval);
   if (&Apache::lonnet::allowed('opa',$cid)) {      if (&Apache::lonnet::allowed('opa',$cid)) {
     my %f=&getformulas($safeeval);          my %f=&getformulas($safeeval);
     my $stype=&gettype($safeeval);          my $stype=&gettype($safeeval);
     my $cnum=&getcnum($safeeval);          my $cnum=&getcnum($safeeval);
     my $cdom=&getcdom($safeeval);          my $cdom=&getcdom($safeeval);
     my $chome=&getchome($safeeval);          my $chome=&getchome($safeeval);
     my $fn=&getfilename($safeeval);          my $fn=&getfilename($safeeval);
           # Cache new sheet
 # ------------------------------------------------------------- Cache new sheet          $spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}=join('___;___',%f);
     $spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}=join('___;___',%f);              # Write sheet
 # ----------------------------------------------------------------- Write sheet          my $sheetdata='';
     my $sheetdata='';          foreach (keys(%f)) {
     map {              unless ($f{$_} eq 'import') {
        $sheetdata.=&Apache::lonnet::escape($_).'='.                  $sheetdata.=&Apache::lonnet::escape($_).'='.
    &Apache::lonnet::escape($f{$_}).'&';                      &Apache::lonnet::escape($f{$_}).'&';
     } keys %f;              }
     $sheetdata=~s/\&$//;          }
     my $reply=&Apache::lonnet::reply('put:'.$cdom.':'.$cnum.':'.$fn.':'.          $sheetdata=~s/\&$//;
               $sheetdata,$chome);          my $reply=&Apache::lonnet::reply('put:'.$cdom.':'.$cnum.':'.$fn.':'.
     if ($reply eq 'ok') {                                           $sheetdata,$chome);
           $reply=&Apache::lonnet::reply('put:'.$cdom.':'.$cnum.':'.          if ($reply eq 'ok') {
               $stype.'_spreadsheets:'.              $reply=&Apache::lonnet::reply('put:'.$cdom.':'.$cnum.':'.
               &Apache::lonnet::escape($fn).'='.$ENV{'user.name'},                                            $stype.'_spreadsheets:'.
               $chome);                                            &Apache::lonnet::escape($fn).
           if ($reply eq 'ok') {                                            '='.$ENV{'user.name'}.'@'.
               if ($makedef) {                                             $ENV{'user.domain'},
                 return &Apache::lonnet::reply('put:'.$cdom.':'.$cnum.                                            $chome);
                                 ':environment:spreadsheet_default_'.$stype.'='.              if ($reply eq 'ok') {
                                 &Apache::lonnet::escape($fn),                  if ($makedef) { 
                                 $chome);                      return &Apache::lonnet::reply('put:'.$cdom.':'.$cnum.
       } else {                                                    ':environment:'.
   return $reply;                                                    'spreadsheet_default_'.
          }                                                    $stype.'='.
    } else {                                                    &Apache::lonnet::escape($fn),
        return $reply;                                                    $chome);
            }                  } 
       } else {                  return $reply;
   return $reply;              } 
       }              return $reply;
   }          } 
   return 'unauthorized';          return $reply;
       }
       return 'unauthorized';
 }  }
   
 # ----------------------------------------------- Make a temp copy of the sheet  # ----------------------------------------------- Make a temp copy of the sheet
Line 870  sub writesheet { Line 1535  sub writesheet {
 sub tmpwrite {  sub tmpwrite {
     my $safeeval=shift;      my $safeeval=shift;
     my $fn=$ENV{'user.name'}.'_'.      my $fn=$ENV{'user.name'}.'_'.
            $ENV{'user.domain'}.'_spreadsheet_'.&getusymb($safeeval).'_'.          $ENV{'user.domain'}.'_spreadsheet_'.&getusymb($safeeval).'_'.
            &getfilename($safeeval);             &getfilename($safeeval);
     $fn=~s/\W/\_/g;      $fn=~s/\W/\_/g;
     $fn=$tmpdir.$fn.'.tmp';      $fn=$tmpdir.$fn.'.tmp';
Line 891  sub tmpread { Line 1556  sub tmpread {
     $fn=$tmpdir.$fn.'.tmp';      $fn=$tmpdir.$fn.'.tmp';
     my $fh;      my $fh;
     my %fo=();      my %fo=();
       my $countrows=0;
     if ($fh=Apache::File->new($fn)) {      if ($fh=Apache::File->new($fn)) {
         my $name;          my $name;
         while ($name=<$fh>) {          while ($name=<$fh>) {
Line 898  sub tmpread { Line 1564  sub tmpread {
             my $value=<$fh>;              my $value=<$fh>;
             chomp($value);              chomp($value);
             $fo{$name}=$value;              $fo{$name}=$value;
               if ($name=~/^A(\d+)$/) {
    if ($1>$countrows) {
       $countrows=$1;
                   }
               }
         }          }
     }      }
     if ($nfield) { $fo{$nfield}=$nform; }      if ($nform eq 'changesheet') {
           $fo{'A'.$nfield}=(split(/\_\_\&\&\&\_\_/,$fo{'A'.$nfield}))[0];
           unless ($ENV{'form.sel_'.$nfield} eq 'Default') {
       $fo{'A'.$nfield}.='__&&&__'.$ENV{'form.sel_'.$nfield};
           }
       } elsif ($nfield eq 'insertrow') {
           $countrows++;
           my $newrow=substr('000000'.$countrows,-7);
           if ($nform eq 'top') {
       $fo{'A'.$countrows}='--- '.$newrow;
           } else {
               $fo{'A'.$countrows}='~~~ '.$newrow;
           }
       } else {
          if ($nfield) { $fo{$nfield}=$nform; }
       }
     &setformulas($safeeval,%fo);      &setformulas($safeeval,%fo);
 }  }
   
 # ================================================================== Parameters  ##################################################
 # -------------------------------------------- Figure out a cascading parameter  ##################################################
 #  
 # For this function to work  
 #  
 # * parmhash needs to be tied  
 # * courseopt and useropt need to be initialized for this user and course  
 #  
   
 sub parmval {  =pod
     my ($what,$safeeval)=@_;  
     my $cid=&getcid($safeeval);  
     my $csec=&getcsec($safeeval);  
     my $uname=&getuname($safeeval);  
     my $udom=&getudom($safeeval);  
     my $symb=&getusymb($safeeval);  
   
     unless ($symb) { return ''; }  =item &parmval()
     my $result='';  
   
     my ($mapname,$id,$fn)=split(/\_\_\_/,$symb);  Determine the value of a parameter.
 # ----------------------------------------------------- Cascading lookup scheme  
        my $rwhat=$what;  
        $what=~s/^parameter\_//;  
        $what=~s/\_/\./;  
   
        my $symbparm=$symb.'.'.$what;  
        my $mapparm=$mapname.'___(all).'.$what;  
        my $usercourseprefix=$uname.'_'.$udom.'_'.$cid;  
   
        my $seclevel=  
             $usercourseprefix.'.['.  
  $csec.'].'.$what;  
        my $seclevelr=  
             $usercourseprefix.'.['.  
  $csec.'].'.$symbparm;  
        my $seclevelm=  
             $usercourseprefix.'.['.  
  $csec.'].'.$mapparm;  
   
        my $courselevel=  
             $usercourseprefix.'.'.$what;  
        my $courselevelr=  
             $usercourseprefix.'.'.$symbparm;  
        my $courselevelm=  
             $usercourseprefix.'.'.$mapparm;  
   
 # ---------------------------------------------------------- fourth, check user  
         
       if ($uname) {   
   
        if ($useropt{$courselevelr}) { return $useropt{$courselevelr}; }  
   
        if ($useropt{$courselevelm}) { return $useropt{$courselevelm}; }  
   
        if ($useropt{$courselevel}) { return $useropt{$courselevel}; }  
   
       }  
   
 # --------------------------------------------------------- third, check course  
        
        if ($csec) {  
    
         if ($courseopt{$seclevelr}) { return $courseopt{$seclevelr}; }  
   
         if ($courseopt{$seclevelm}) { return $courseopt{$seclevelm}; }    
   
         if ($courseopt{$seclevel}) { return $courseopt{$seclevel}; }  
     
       }  
   
        if ($courseopt{$courselevelr}) { return $courseopt{$courselevelr}; }  Inputs: $what, the parameter needed, $safeeval, the safe space
   
        if ($courseopt{$courselevelm}) { return $courseopt{$courselevelm}; }  Returns: The value of a parameter, or '' if none.
   
        if ($courseopt{$courselevel}) { return $courseopt{$courselevel}; }  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.
   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.
   
 # ----------------------------------------------------- second, check map parms  =cut
   
        my $thisparm=$parmhash{$symbparm};  ##################################################
        if ($thisparm) { return $thisparm; }  ##################################################
   sub parmval {
 # -------------------------------------------------------- first, check default      my ($what,$safeeval)=@_;
       my $symb  = &getusymb($safeeval);
        return &Apache::lonnet::metadata($fn,$rwhat.'.default');      unless ($symb) { return ''; }
               #
       my $cid   = &getcid($safeeval);
       my $csec  = &getcsec($safeeval);
       my $uname = &getuname($safeeval);
       my $udom  = &getudom($safeeval);
       my $result='';
       #
       my ($mapname,$id,$fn)=split(/\_\_\_/,$symb);
       # Cascading lookup scheme
       my $rwhat=$what;
       $what =~ s/^parameter\_//;
       $what =~ s/\_([^\_]+)$/\.$1/;
       #
       my $symbparm = $symb.'.'.$what;
       my $mapparm  = $mapname.'___(all).'.$what;
       my $usercourseprefix = $uname.'_'.$udom.'_'.$cid;
       #
       my $seclevel  = $usercourseprefix.'.['.$csec.'].'.$what;
       my $seclevelr = $usercourseprefix.'.['.$csec.'].'.$symbparm;
       my $seclevelm = $usercourseprefix.'.['.$csec.'].'.$mapparm;
       #
       my $courselevel  = $usercourseprefix.'.'.$what;
       my $courselevelr = $usercourseprefix.'.'.$symbparm;
       my $courselevelm = $usercourseprefix.'.'.$mapparm;
       # fourth, check user
       if ($uname) { 
           return $useropt{$courselevelr} if ($useropt{$courselevelr});
           return $useropt{$courselevelm} if ($useropt{$courselevelm});
           return $useropt{$courselevel} if ($useropt{$courselevel});
       }
       # third, check course
       if ($csec) {
           return $courseopt{$seclevelr} if ($courseopt{$seclevelr});
           return $courseopt{$seclevelm} if ($courseopt{$seclevelm});
           return $courseopt{$seclevel} if ($courseopt{$seclevel});
       }
       #
       return $courseopt{$courselevelr} if ($courseopt{$courselevelr});
       return $courseopt{$courselevelm} if ($courseopt{$courselevelm});
       return $courseopt{$courselevel} if ($courseopt{$courselevel});
       # second, check map parms
       my $thisparm = $parmhash{$symbparm};
       return $thisparm if ($thisparm);
       # first, check default
       return &Apache::lonnet::metadata($fn,$rwhat.'.default');
 }  }
   
 # ---------------------------------------------- Update rows for course listing  # ---------------------------------------------- Update rows for course listing
   
 sub updateclasssheet {  sub updateclasssheet {
     my $safeeval=shift;      my $safeeval=shift;
     my $cnum=&getcnum($safeeval);      my $cnum=&getcnum($safeeval);
     my $cdom=&getcdom($safeeval);      my $cdom=&getcdom($safeeval);
     my $cid=&getcid($safeeval);      my $cid=&getcid($safeeval);
     my $chome=&getchome($safeeval);      my $chome=&getchome($safeeval);
       #
 # ---------------------------------------------- Read class list and row labels      # Read class list and row labels
       my %classlist;
     my $classlst=&Apache::lonnet::reply      my @tmp = &Apache::lonnet::dump('classlist',$cdom,$cnum);
                                  ('dump:'.$cdom.':'.$cnum.':classlist',$chome);      if ($tmp[0] !~ /^error/) {
           %classlist = @tmp;
       } else {
           return 'Could not access course data';
       }
       undef @tmp;
       #
     my %currentlist=();      my %currentlist=();
     my $now=time;      my $now=time;
     unless ($classlst=~/^error\:/) {      foreach my $student (keys(%classlist)) {
         map {          my ($end,$start)=split(/\:/,$classlist{$student});
             my ($name,$value)=split(/\=/,$_);          my $active=1;
             my ($end,$start)=split(/\:/,&Apache::lonnet::unescape($value));          $active = 0 if (($end) && ($now>$end));
             my $active=1;          $active = 1 if ($ENV{'form.Status'} eq 'Any');
             if (($end) && ($now>$end)) { $active=0; }          $active = !$active if ($ENV{'form.Status'} eq 'Expired');
             if ($active) {          if ($active) {
                 my $rowlabel='';              my $rowlabel='';
                 $name=&Apache::lonnet::unescape($name);              my ($studentName,$studentDomain)=split(/\:/,$student);
                 my ($sname,$sdom)=split(/\:/,$name);              my $studentSection=&Apache::lonnet::usection($studentDomain,
                 my $ssec=&Apache::lonnet::usection($sdom,$sname,$cid);                                                           $studentName,$cid);
                 if ($ssec==-1) {              if ($studentSection==-1) {
                     $rowlabel='<font color=red>Data not available: '.$name.                  unless ($ENV{'form.showcsv'}) {
       '</font>';                      $rowlabel='<font color=red>Data not available: '.
                           $studentName.'</font>';
                 } else {                  } else {
                     my %reply=&Apache::lonnet::idrget($sdom,$sname);                      $rowlabel='ERROR","'.$studentName.
                     my $reply=&Apache::lonnet::reply('get:'.$sdom.':'.$sname.                          '","Data not available","","","';
       ':environment:firstname&middlename&lastname&generation',                  }
                       &Apache::lonnet::homeserver($sname,$sdom));              } else {
                     $rowlabel='<a href="/adm/studentcalc?uname='.$sname.                  my %reply=&Apache::lonnet::idrget($studentDomain,$studentName);
                               '&udom='.$sdom.'">'.                  my %studentInformation=&Apache::lonnet::get
                               $ssec.'&nbsp;'.$reply{$sname}.'<br>';                      ('environment',
                     map {                       ['lastname','generation','firstname','middlename','id'],
                         $rowlabel.=&Apache::lonnet::unescape($_).' ';                       $studentDomain,$studentName);
                     } split(/\&/,$reply);                  if (! $ENV{'form.showcsv'}) {
                       $rowlabel='<a href="/adm/studentcalc?uname='.$studentName.
                           '&udom='.$studentDomain.'">'.
                               $studentSection.'&nbsp;';
                       foreach ('id','firstname','middlename',
                                'lastname','generation'){
                           $rowlabel.=$studentInformation{$_}."&nbsp;";
                       }
                     $rowlabel.='</a>';                      $rowlabel.='</a>';
                   } else {
                       $rowlabel= '"'.join('","',
                                           ($studentSection,
                                            $studentInformation{'id'},
                                            $studentInformation{'firstname'},
                                            $studentInformation{'middlename'},
                                            $studentInformation{'lastname'},
                                            $studentInformation{'generation'})
                                           ).'"';
                 }                  }
  $currentlist{&Apache::lonnet::unescape($name)}=$rowlabel;  
             }              }
         } split(/\&/,$classlst);              $currentlist{$student}=$rowlabel;
 #          } # end of if ($active)
 # -------------------- Find discrepancies between the course row table and this      } # end of foreach my $student (keys(%classlist))
 #      #
         my %f=&getformulas($safeeval);      # Find discrepancies between the course row table and this
         my $changed=0;      #
       my %f=&getformulas($safeeval);
         my $maxrow=0;      my $changed=0;
         my %existing=();      #
       my $maxrow=0;
 # ----------------------------------------------------------- Now obsolete rows      my %existing=();
  map {      #
     if ($_=~/^A(\d+)/) {      # Now obsolete rows
                 $maxrow=($1>$maxrow)?$1:$maxrow;      foreach (keys(%f)) {
                 $existing{$f{$_}}=1;          if ($_=~/^A(\d+)/) {
  unless ((defined($currentlist{$f{$_}})) || (!$1)) {              $maxrow=($1>$maxrow)?$1:$maxrow;
    $f{$_}='!!! Obsolete';              $existing{$f{$_}}=1;
                    $changed=1;              unless ((defined($currentlist{$f{$_}})) || (!$1) ||
                 }                      ($f{$_}=~/^(\~\~\~|\-\-\-)/)) {
                   $f{$_}='!!! Obsolete';
                   $changed=1;
             }              }
         } keys %f;          }
       }
 # -------------------------------------------------------- New and unknown keys      #
            # New and unknown keys
         map {      foreach (sort keys(%currentlist)) {
             unless ($existing{$_}) {          unless ($existing{$_}) {
  $changed=1;              $changed=1;
                 $maxrow++;              $maxrow++;
                 $f{'A'.$maxrow}=$_;              $f{'A'.$maxrow}=$_;
             }          }
         } sort keys %currentlist;          
        
         if ($changed) { &setformulas($safeeval,%f); }  
   
         &setmaxrow($safeeval,$maxrow);  
         &setrowlabels($safeeval,%currentlist);  
   
     } else {  
         return 'Could not access course data';  
     }      }
       if ($changed) { &setformulas($safeeval,%f); }
       #
       &setmaxrow($safeeval,$maxrow);
       &setrowlabels($safeeval,%currentlist);
 }  }
   
 # ----------------------------------- Update rows for student and assess sheets  # ----------------------------------- Update rows for student and assess sheets
   
 sub updatestudentassesssheet {  sub updatestudentassesssheet {
     my $safeeval=shift;      my $safeeval=shift;
     my %bighash;      my %bighash;
     my $stype=&gettype($safeeval);      my $stype=&gettype($safeeval);
     my %current=();      my %current=();
     unless ($updatedata{$ENV{'request.course.fn'}.'_'.$stype}) {      if  ($updatedata{$ENV{'request.course.fn'}.'_'.$stype}) {
 # -------------------------------------------------------------------- Tie hash          %current=split(/\_\_\_\;\_\_\_/,
       if (tie(%bighash,'GDBM_File',$ENV{'request.course.fn'}.'.db',         $updatedata{$ENV{'request.course.fn'}.'_'.$stype});
                        &GDBM_READER,0640)) {      } else {
 # --------------------------------------------------------- Get all assessments          # Tie hash
           tie(%bighash,'GDBM_File',$ENV{'request.course.fn'}.'.db',
  my %allkeys=();              &GDBM_READER(),0640);
         my %allassess=();          if (! tied(%bighash)) {
               return 'Could not access course data';
         map {          }
     if ($_=~/^src\_(\d+)\.(\d+)$/) {          # Get all assessments
        my $mapid=$1;          my %allkeys=('timestamp' => 
                my $resid=$2;                       'Timestamp of Last Transaction<br>timestamp',
                my $id=$mapid.'.'.$resid;                       'subnumber' =>
                my $srcf=$bighash{$_};                       'Number of Submissions<br>subnumber',
                if ($srcf=~/\.(problem|exam|quiz|assess|survey|form)$/) {                       'tutornumber' =>
                  my $symb=                       'Number of Tutor Responses<br>tutornumber',
                      &Apache::lonnet::declutter($bighash{'map_id_'.$mapid}).                       'totalpoints' =>
     '___'.$resid.'___'.                       'Total Points Granted<br>totalpoints');
     &Apache::lonnet::declutter($srcf);          my $adduserstr='';
  $allassess{$symb}=          if ((&getuname($safeeval) ne $ENV{'user.name'}) ||
     '<a href="/adm/assesscalc?usymb='.$symb.'">'.$bighash{'title_'.$id}.'</a>';              (&getudom($safeeval) ne $ENV{'user.domain'})) {
                  if ($stype eq 'assesscalc') {              $adduserstr='&uname='.&getuname($safeeval).
                    map {                  '&udom='.&getudom($safeeval);
                        if (($_=~/^stores\_(.*)/) || ($_=~/^parameter\_(.*)/)) {          }
   my $key=$_;          my %allassess =
                           my $display=              ('_feedback' =>'<a href="/adm/assesscalc?usymb=_feedback'.
       &Apache::lonnet::metadata($srcf,$key.'.display');               $adduserstr.'">Feedback</a>',
                           unless ($display) {               '_evaluation' =>'<a href="/adm/assesscalc?usymb=_evaluation'.
                               $display.=               $adduserstr.'">Evaluation</a>',
          &Apache::lonnet::metadata($srcf,$key.'.name');               '_tutoring' =>'<a href="/adm/assesscalc?usymb=_tutoring'.
                           }               $adduserstr.'">Tutoring</a>',
                           $display.='<br>'.$key;               '_discussion' =>'<a href="/adm/assesscalc?usymb=_discussion'.
                           $allkeys{$key}=$display;               $adduserstr.'">Discussion</a>'
        }               );
                    } split(/\,/,&Apache::lonnet::metadata($srcf,'keys'));          foreach (keys(%bighash)) {
          }              next if ($_!~/^src\_(\d+)\.(\d+)$/);
       }              my $mapid=$1;
    }              my $resid=$2;
         } keys %bighash;              my $id=$mapid.'.'.$resid;
               my $srcf=$bighash{$_};
               if ($srcf=~/\.(problem|exam|quiz|assess|survey|form)$/) {
                   my $symb=
                       &Apache::lonnet::declutter($bighash{'map_id_'.$mapid}).
                           '___'.$resid.'___'.&Apache::lonnet::declutter($srcf);
                   $allassess{$symb}=
                       '<a href="/adm/assesscalc?usymb='.$symb.$adduserstr.'">'.
                           $bighash{'title_'.$id}.'</a>';
                   next if ($stype ne 'assesscalc');
                   foreach my $key (split(/\,/,
                                          &Apache::lonnet::metadata($srcf,'keys')
                                          )) {
                       next if ($key !~ /^(stores|parameter)_/);
                       my $display=
                           &Apache::lonnet::metadata($srcf,$key.'.display');
                       unless ($display) {
                           $display.=
                               &Apache::lonnet::metadata($srcf,$key.'.name');
                       }
                       $display.='<br>'.$key;
                       $allkeys{$key}=$display;
                   } # end of foreach
               }
           } # end of foreach (keys(%bighash))
         untie(%bighash);          untie(%bighash);
               #
 #          # %allkeys has a list of storage and parameter displays by unikey
 # %allkeys has a list of storage and parameter displays by unikey          # %allassess has a list of all resource displays by symb
 # %allassess has a list of all resource displays by symb          #
 #  
   
         if ($stype eq 'assesscalc') {          if ($stype eq 'assesscalc') {
     %current=%allkeys;              %current=%allkeys;
         } elsif ($stype eq 'studentcalc') {          } elsif ($stype eq 'studentcalc') {
             %current=%allassess;              %current=%allassess;
         }          }
         $updatedata{$ENV{'request.course.fn'}.'_'.$stype}=          $updatedata{$ENV{'request.course.fn'}.'_'.$stype}=
     join('___;___',%current);              join('___;___',%current);
     } else {          # Get current from cache
         return 'Could not access course data';  
     }      }
 # ------------------------------------------------------ Get current from cache      # Find discrepancies between the course row table and this
     } else {      #
         %current=split(/\_\_\_\;\_\_\_/,      my %f=&getformulas($safeeval);
        $updatedata{$ENV{'request.course.fn'}.'_'.$stype});      my $changed=0;
     }  
 # -------------------- Find discrepancies between the course row table and this  
 #  
         my %f=&getformulas($safeeval);  
         my $changed=0;  
   
         my $maxrow=0;  
         my %existing=();  
   
 # ----------------------------------------------------------- Now obsolete rows  
  map {  
     if ($_=~/^A(\d+)/) {  
                 $maxrow=($1>$maxrow)?$1:$maxrow;  
                 $existing{$f{$_}}=1;  
  unless ((defined($current{$f{$_}})) || (!$1)) {  
    $f{$_}='!!! Obsolete';  
                    $changed=1;  
                 }  
             }  
         } keys %f;  
   
 # -------------------------------------------------------- New and unknown keys  
        
         map {  
             unless ($existing{$_}) {  
  $changed=1;  
                 $maxrow++;  
                 $f{'A'.$maxrow}=$_;  
             }  
         } keys %current;          
           
         if ($changed) { &setformulas($safeeval,%f); }      my $maxrow=0;
       my %existing=();
         &setmaxrow($safeeval,$maxrow);      # Now obsolete rows
         &setrowlabels($safeeval,%current);      foreach (keys(%f)) {
            next if ($_!~/^A(\d+)/);
         undef %current;          $maxrow=($1>$maxrow)?$1:$maxrow;
         undef %existing;          my ($usy,$ufn)=split(/\_\_\&\&\&\_\_/,$f{$_});
           $existing{$usy}=1;
           unless ((defined($current{$usy})) || (!$1) ||
                   ($f{$_}=~/^(\~\~\~|\-\-\-)/)){
               $f{$_}='!!! Obsolete';
               $changed=1;
           } elsif ($ufn) {
               $current{$usy}
               =~s/assesscalc\?usymb\=/assesscalc\?ufn\=$ufn\&usymb\=/;
           }
       }
       # New and unknown keys
       foreach (keys(%current)) {
           unless ($existing{$_}) {
               $changed=1;
               $maxrow++;
               $f{'A'.$maxrow}=$_;
           }
       }
       if ($changed) { &setformulas($safeeval,%f); }
       &setmaxrow($safeeval,$maxrow);
       &setrowlabels($safeeval,%current);
       #
       undef %current;
       undef %existing;
 }  }
   
 # ------------------------------------------------ Load data for one assessment  # ------------------------------------------------ Load data for one assessment
Line 1193  sub loadstudent { Line 1898  sub loadstudent {
     my %c=();      my %c=();
     my %f=&getformulas($safeeval);      my %f=&getformulas($safeeval);
     $cachedassess=&getuname($safeeval).':'.&getudom($safeeval);      $cachedassess=&getuname($safeeval).':'.&getudom($safeeval);
     %cachedstores=();      # Get ALL the student preformance data
     {      my @tmp = &Apache::lonnet::dump(&getcid($safeeval),
       my $reply=&Apache::lonnet::reply('dump:'.&getudom($safeeval).':'.                                      &getudom($safeeval),
                                                &getuname($safeeval).':'.                                      &getuname($safeeval),
                                                &getcid($safeeval),                                      undef);
                                                &getuhome($safeeval));      if ($tmp[0] !~ /^error:/) {
       unless ($reply=~/^error\:/) {          %cachedstores = @tmp;
          map {  
             my ($name,$value)=split(/\=/,$_);  
             $cachedstores{&Apache::lonnet::unescape($name)}=  
                   &Apache::lonnet::unescape($value);  
          } split(/\&/,$reply);  
       }  
     }      }
       undef @tmp;
       # 
     my @assessdata=();      my @assessdata=();
     map {      foreach (keys(%f)) {
  if ($_=~/^A(\d+)/) {   next if ($_!~/^A(\d+)/);
    my $row=$1;          my $row=$1;
            unless (($f{$_}=~/^\!/) || ($row==0)) {          next if (($f{$_}=~/^[\!\~\-]/) || ($row==0));
       @assessdata=&exportsheet(&getuname($safeeval),          my ($usy,$ufn)=split(/__&&&\__/,$f{$_});
                                        &getudom($safeeval),          @assessdata=&exportsheet(&getuname($safeeval),
                                        'assesscalc',$f{$_});                                   &getudom($safeeval),
               my $index=0;                                   'assesscalc',$usy,$ufn);
               map {          my $index=0;
                   if ($assessdata[$index]) {          foreach ('A','B','C','D','E','F','G','H','I','J','K','L','M',
      my $col=$_;                   'N','O','P','Q','R','S','T','U','V','W','X','Y','Z') {
      if ($assessdata[$index]=~/\D/) {              if ($assessdata[$index]) {
                          $c{$col.$row}="'".$assessdata[$index]."'";                  my $col=$_;
       } else {                  if ($assessdata[$index]=~/\D/) {
          $c{$col.$row}=$assessdata[$index];                      $c{$col.$row}="'".$assessdata[$index]."'";
      }                  } else {
                      unless ($col eq 'A') {                       $c{$col.$row}=$assessdata[$index];
  $f{$col.$row}='import';                  }
                      }                  unless ($col eq 'A') { 
   }                      $f{$col.$row}='import';
                   $index++;                  }
               } ('A','B','C','D','E','F','G','H','I','J','K','L','M',              }
                  'N','O','P','Q','R','S','T','U','V','W','X','Y','Z');              $index++;
    }  
         }          }
     } keys %f;      }
     $cachedassess='';      $cachedassess='';
     undef %cachedstores;      undef %cachedstores;
     &setformulas($safeeval,%f);      &setformulas($safeeval,%f);
Line 1247  sub loadcourse { Line 1947  sub loadcourse {
     my %c=();      my %c=();
     my %f=&getformulas($safeeval);      my %f=&getformulas($safeeval);
     my $total=0;      my $total=0;
     map {      foreach (keys(%f)) {
  if ($_=~/^A(\d+)/) {   if ($_=~/^A(\d+)/) {
     unless ($f{$_}=~/^\!/) { $total++; }      unless ($f{$_}=~/^[\!\~\-]/) { $total++; }
         }          }
     } keys %f;      }
     my $now=0;      my $now=0;
     my $since=time;      my $since=time;
     $r->print(<<ENDPOP);      $r->print(<<ENDPOP);
 <script>  <script>
     popwin=open('','popwin','width=400,height=100');      popwin=open('','popwin','width=400,height=100');
     popwin.document.writeln('<html><body bgcolor="#FFFFFF">'+      popwin.document.writeln('<html><body bgcolor="#FFFFFF">'+
       '<h1>Spreadsheet Calculation Progress</h1>'+        '<h3>Spreadsheet Calculation Progress</h3>'+
       '<form name=popremain>'+        '<form name=popremain>'+
       '<input type=text size=35 name=remaining value=Starting></form>'+        '<input type=text size=35 name=remaining value=Starting></form>'+
       '</body></html>');        '</body></html>');
Line 1266  sub loadcourse { Line 1966  sub loadcourse {
 </script>  </script>
 ENDPOP  ENDPOP
     $r->rflush();      $r->rflush();
     map {      foreach (keys(%f)) {
  if ($_=~/^A(\d+)/) {   next if ($_!~/^A(\d+)/);
    my $row=$1;          my $row=$1;
            unless (($f{$_}=~/^\!/)  || ($row==0)) {          next if (($f{$_}=~/^[\!\~\-]/)  || ($row==0));
       my @studentdata=&exportsheet(split(/\:/,$f{$_}),          my @studentdata=&exportsheet(split(/\:/,$f{$_}),
                                            'studentcalc');                                       'studentcalc');
               undef %userrdatas;          undef %userrdatas;
               $now++;          $now++;
               $r->print('<script>popwin.document.popremain.remaining.value="'.          $r->print('<script>popwin.document.popremain.remaining.value="'.
                   $now.'/'.$total.': '.int((time-$since)/$now*($total-$now)).                    $now.'/'.$total.': '.int((time-$since)/$now*($total-$now)).
                         ' secs remaining";</script>');                    ' secs remaining";</script>');
               $r->rflush();           $r->rflush(); 
           #
               my $index=0;          my $index=0;
               map {          foreach ('A','B','C','D','E','F','G','H','I','J','K','L','M',
                   if ($studentdata[$index]) {                   'N','O','P','Q','R','S','T','U','V','W','X','Y','Z') {
      my $col=$_;              if ($studentdata[$index]) {
      if ($studentdata[$index]=~/\D/) {                  my $col=$_;
                          $c{$col.$row}="'".$studentdata[$index]."'";                  if ($studentdata[$index]=~/\D/) {
       } else {                      $c{$col.$row}="'".$studentdata[$index]."'";
          $c{$col.$row}=$studentdata[$index];                  } else {
      }                      $c{$col.$row}=$studentdata[$index];
                      unless ($col eq 'A') {                   }
  $f{$col.$row}='import';                  unless ($col eq 'A') { 
                      }                      $f{$col.$row}='import';
   }                  }
                   $index++;                  $index++;
               } ('A','B','C','D','E','F','G','H','I','J','K','L','M',              }
                  'N','O','P','Q','R','S','T','U','V','W','X','Y','Z');  
    }  
         }          }
     } keys %f;      }
     &setformulas($safeeval,%f);      &setformulas($safeeval,%f);
     &setconstants($safeeval,%c);      &setconstants($safeeval,%c);
     $r->print('<script>popwin.close()</script>');      $r->print('<script>popwin.close()</script>');
Line 1309  ENDPOP Line 2007  ENDPOP
 sub loadassessment {  sub loadassessment {
     my $safeeval=shift;      my $safeeval=shift;
   
     my $uhome=&getuhome($safeeval);      my $uhome = &getuhome($safeeval);
     my $uname=&getuname($safeeval);      my $uname = &getuname($safeeval);
     my $udom=&getudom($safeeval);      my $udom  = &getudom($safeeval);
     my $symb=&getusymb($safeeval);      my $symb  = &getusymb($safeeval);
     my $cid=&getcid($safeeval);      my $cid   = &getcid($safeeval);
     my $cnum=&getcnum($safeeval);      my $cnum  = &getcnum($safeeval);
     my $cdom=&getcdom($safeeval);      my $cdom  = &getcdom($safeeval);
     my $chome=&getchome($safeeval);      my $chome = &getchome($safeeval);
   
     my $namespace;      my $namespace;
     unless ($namespace=$cid) { return ''; }      unless ($namespace=$cid) { return ''; }
       # Get stored values
 # ----------------------------------------------------------- Get stored values      my %returnhash=();
       if ($cachedassess eq $uname.':'.$udom) {
    my %returnhash=();          #
           # get data out of the dumped stores
    if ($cachedassess eq $uname.':'.$udom) {          # 
 #          my $version=$cachedstores{'version:'.$symb};
 # get data out of the dumped stores          my $scope;
 #           for ($scope=1;$scope<=$version;$scope++) {
               foreach (split(/\:/,$cachedstores{$scope.':keys:'.$symb})) {
        my $version=$cachedstores{'version:'.$symb};                  $returnhash{$_}=$cachedstores{$scope.':'.$symb.':'.$_};
        my $scope;              } 
        for ($scope=1;$scope<=$version;$scope++) {          }
            map {      } else {
                $returnhash{$_}=$cachedstores{$scope.':'.$symb.':'.$_};          #
            } split(/\:/,$cachedstores{$scope.':keys:'.$symb});           # restore individual
        }          #
           my $answer=&Apache::lonnet::reply(
    } else {                                            "restore:$udom:$uname:".
 #                                            &Apache::lonnet::escape($namespace).":".
 # restore individual                                            &Apache::lonnet::escape($symb),$uhome);
 #          foreach (split(/\&/,$answer)) {
               my ($name,$value)=split(/\=/,$_);
     my $answer=&Apache::lonnet::reply(              $returnhash{&Apache::lonnet::unescape($name)}=
        "restore:$udom:$uname:".                  &Apache::lonnet::unescape($value);
        &Apache::lonnet::escape($namespace).":".          }
        &Apache::lonnet::escape($symb),$uhome);          my $version;
     map {          for ($version=1;$version<=$returnhash{'version'};$version++) {
  my ($name,$value)=split(/\=/,$_);              foreach (split(/\:/,$returnhash{$version.':keys'})) {
         $returnhash{&Apache::lonnet::unescape($name)}=                  $returnhash{$_}=$returnhash{$version.':'.$_};
                     &Apache::lonnet::unescape($value);              } 
     } split(/\&/,$answer);          }
     my $version;  
     for ($version=1;$version<=$returnhash{'version'};$version++) {  
        map {  
           $returnhash{$_}=$returnhash{$version.':'.$_};  
        } split(/\:/,$returnhash{$version.':keys'});  
     }      }
    }      # returnhash now has all stores for this resource
 # ----------------------------- returnhash now has all stores for this resource      # convert all "_" to "." to be able to use libraries, multiparts, etc
       my @oldkeys=keys %returnhash;
 # ---------------------------- initialize coursedata and userdata for this user  
       foreach (@oldkeys) {
           my $name=$_;
           my $value=$returnhash{$_};
           delete $returnhash{$_};
           $name=~s/\_/\./g;
           $returnhash{$name}=$value;
       }
       # initialize coursedata and userdata for this user
     undef %courseopt;      undef %courseopt;
     undef %useropt;      undef %useropt;
   
     my $userprefix=$uname.'_'.$udom.'_';      my $userprefix=$uname.'_'.$udom.'_';
       
     unless ($uhome eq 'no_host') {       unless ($uhome eq 'no_host') { 
 # -------------------------------------------------------------- Get coursedata          # Get coursedata
       unless          unless
         ((time-$courserdatas{$cid.'.last_cache'})<240) {              ((time-$courserdatas{$cid.'.last_cache'})<240) {
          my $reply=&Apache::lonnet::reply('dump:'.$cdom.':'.$cnum.                  my $reply=&Apache::lonnet::reply('dump:'.$cdom.':'.$cnum.
               ':resourcedata',$chome);                                                   ':resourcedata',$chome);
          if ($reply!~/^error\:/) {                  if ($reply!~/^error\:/) {
             $courserdatas{$cid}=$reply;                      $courserdatas{$cid}=$reply;
             $courserdatas{$cid.'.last_cache'}=time;                      $courserdatas{$cid.'.last_cache'}=time;
          }                  }
       }              }
       map {          foreach (split(/\&/,$courserdatas{$cid})) {
          my ($name,$value)=split(/\=/,$_);              my ($name,$value)=split(/\=/,$_);
          $courseopt{$userprefix.&Apache::lonnet::unescape($name)}=              $courseopt{$userprefix.&Apache::lonnet::unescape($name)}=
                     &Apache::lonnet::unescape($value);                    &Apache::lonnet::unescape($value);  
       } split(/\&/,$courserdatas{$cid});          }
 # --------------------------------------------------- Get userdata (if present)          # Get userdata (if present)
       unless          unless
         ((time-$userrdatas{$uname.'___'.$udom.'.last_cache'})<240) {              ((time-$userrdatas{$uname.'___'.$udom.'.last_cache'})<240) {
          my $reply=                  my $reply=
        &Apache::lonnet::reply('dump:'.$udom.':'.$uname.':resourcedata',$uhome);                      &Apache::lonnet::reply('dump:'.$udom.':'.$uname.':resourcedata',$uhome);
          if ($reply!~/^error\:/) {                  if ($reply!~/^error\:/) {
      $userrdatas{$uname.'___'.$udom}=$reply;                      $userrdatas{$uname.'___'.$udom}=$reply;
      $userrdatas{$uname.'___'.$udom.'.last_cache'}=time;                      $userrdatas{$uname.'___'.$udom.'.last_cache'}=time;
          }                  }
       }              }
       map {          foreach (split(/\&/,$userrdatas{$uname.'___'.$udom})) {
          my ($name,$value)=split(/\=/,$_);              my ($name,$value)=split(/\=/,$_);
          $useropt{$userprefix.&Apache::lonnet::unescape($name)}=              $useropt{$userprefix.&Apache::lonnet::unescape($name)}=
           &Apache::lonnet::unescape($value);                  &Apache::lonnet::unescape($value);
       } split(/\&/,$userrdatas{$uname.'___'.$udom});          }
     }      }
 # ----------------- now courseopt, useropt initialized for this user and course      # now courseopt, useropt initialized for this user and course
 # (used by parmval)      # (used by parmval)
       #
    my %c=();      # Load keys for this assessment only
       #
    if (tie(%parmhash,'GDBM_File',      my %thisassess=();
            &getcfn($safeeval).'_parms.db',&GDBM_READER,0640)) {      my ($symap,$syid,$srcf)=split(/\_\_\_/,$symb);
     my %f=&getformulas($safeeval);      foreach (split(/\,/,&Apache::lonnet::metadata($srcf,'keys'))) {
     map {          $thisassess{$_}=1;
  if ($_=~/^A/) {      } 
             unless ($f{$_}=~/^\!/) {      #
         if ($f{$_}=~/^parameter/) {      # Load parameters
                   my $val=&parmval($f{$_},$safeeval);      #
                   $c{$_}=$val;      my %c=();
                   $c{$f{$_}}=$val;      if (tie(%parmhash,'GDBM_File',
        } else {              &getcfn($safeeval).'_parms.db',&GDBM_READER(),0640)) {
   my $key=$f{$_};          my %f=&getformulas($safeeval);
                   my $ckey=$key;          foreach (keys(%f))  {
                   $key=~s/^stores\_/resource\./;              next if ($_!~/^A/);
                   $key=~s/\_/\./;              next if  ($f{$_}=~/^[\!\~\-]/);
            $c{$_}=$returnhash{$key};              if ($f{$_}=~/^parameter/) {
                   $c{$ckey}=$returnhash{$key};                  if ($thisassess{$f{$_}}) {
        }                      my $val=&parmval($f{$_},$safeeval);
    }                      $c{$_}=$val;
                       $c{$f{$_}}=$val;
                   }
               } else {
                   my $key=$f{$_};
                   my $ckey=$key;
                   $key=~s/^stores\_/resource\./;
                   $key=~s/\_/\./g;
                   $c{$_}=$returnhash{$key};
                   $c{$ckey}=$returnhash{$key};
               }
         }          }
     } keys %f;          untie(%parmhash);
     untie(%parmhash);      }
    }      &setconstants($safeeval,%c);
    &setconstants($safeeval,%c);  
 }  }
   
 # --------------------------------------------------------- Various form fields  # --------------------------------------------------------- Various form fields
Line 1435  sub loadassessment { Line 2145  sub loadassessment {
 sub textfield {  sub textfield {
     my ($title,$name,$value)=@_;      my ($title,$name,$value)=@_;
     return "\n<p><b>$title:</b><br>".      return "\n<p><b>$title:</b><br>".
            '<input type=text name="'.$name.'" size=80 value="'.$value.'">';          '<input type=text name="'.$name.'" size=80 value="'.$value.'">';
 }  }
   
 sub hiddenfield {  sub hiddenfield {
Line 1446  sub hiddenfield { Line 2156  sub hiddenfield {
 sub selectbox {  sub selectbox {
     my ($title,$name,$value,%options)=@_;      my ($title,$name,$value,%options)=@_;
     my $selout="\n<p><b>$title:</b><br>".'<select name="'.$name.'">';      my $selout="\n<p><b>$title:</b><br>".'<select name="'.$name.'">';
     map {      foreach (sort keys(%options)) {
         $selout.='<option value="'.$_.'"';          $selout.='<option value="'.$_.'"';
         if ($_ eq $value) { $selout.=' selected'; }          if ($_ eq $value) { $selout.=' selected'; }
         $selout.='>'.$options{$_}.'</option>';          $selout.='>'.$options{$_}.'</option>';
     } sort keys %options;      }
     return $selout.'</select>';      return $selout.'</select>';
 }  }
   
Line 1486  sub loadrows { Line 2196  sub loadrows {
     }      }
 }  }
   
   # ======================================================= Forced recalculation?
   
   sub checkthis {
       my ($keyname,$time)=@_;
       return ($time<$expiredates{$keyname});
   }
   
   sub forcedrecalc {
       my ($uname,$udom,$stype,$usymb)=@_;
       my $key=$uname.':'.$udom.':'.$stype.':'.$usymb;
       my $time=$oldsheets{$key.'.time'};
       if ($ENV{'form.forcerecalc'}) { return 1; }
       unless ($time) { return 1; }
       if ($stype eq 'assesscalc') {
           my $map=(split(/\_\_\_/,$usymb))[0];
           if (&checkthis('::assesscalc:',$time) ||
               &checkthis('::assesscalc:'.$map,$time) ||
               &checkthis('::assesscalc:'.$usymb,$time) ||
               &checkthis($uname.':'.$udom.':assesscalc:',$time) ||
               &checkthis($uname.':'.$udom.':assesscalc:'.$map,$time) ||
               &checkthis($uname.':'.$udom.':assesscalc:'.$usymb,$time)) {
               return 1;
           } 
       } else {
           if (&checkthis('::studentcalc:',$time) || 
               &checkthis($uname.':'.$udom.':studentcalc:',$time)) {
       return 1;
           }
       }
       return 0; 
   }
   
 # ============================================================== Export handler  # ============================================================== Export handler
 #  #
 # Non-interactive call from with program  # Non-interactive call from with program
 #  #
   
 sub exportsheet {  sub exportsheet {
  my ($uname,$udom,$stype,$usymb,$fn)=@_;      my ($uname,$udom,$stype,$usymb,$fn)=@_;
  my @exportarr=();      my @exportarr=();
 #      if (($usymb=~/^\_(\w+)/) && (!$fn)) {
 # Check if cached          $fn='default_'.$1;
 #      }
       #
  my $key=$uname.':'.$udom.':'.$stype.':'.$usymb;      # Check if cached
  my $found='';      #
       my $key=$uname.':'.$udom.':'.$stype.':'.$usymb;
  if ($oldsheets{$key}) {      my $found='';
      map {      if ($oldsheets{$key}) {
          my ($name,$value)=split(/\_\_\_\=\_\_\_/,$_);          foreach (split(/\_\_\_\&\_\_\_/,$oldsheets{$key})) {
          if ($name eq $fn) {  
      $found=$value;  
          }  
      } split(/\_\_\_\&\_\_\_/,$oldsheets{$key});  
  }  
   
  unless ($found) {  
      &cachedssheets($uname,$udom,&Apache::lonnet::homeserver($uname,$udom));  
      if ($oldsheets{$key}) {  
         map {  
             my ($name,$value)=split(/\_\_\_\=\_\_\_/,$_);              my ($name,$value)=split(/\_\_\_\=\_\_\_/,$_);
             if ($name eq $fn) {              if ($name eq $fn) {
         $found=$value;                  $found=$value;
             }              }
         } split(/\_\_\_\&\_\_\_/,$oldsheets{$key});          }
      }      }
  }      unless ($found) {
           &cachedssheets($uname,$udom,&Apache::lonnet::homeserver($uname,$udom));
  if ($found) {          if ($oldsheets{$key}) {
 #              foreach (split(/\_\_\_\&\_\_\_/,$oldsheets{$key})) {
 # Return what was cached                  my ($name,$value)=split(/\_\_\_\=\_\_\_/,$_);
 #                  if ($name eq $fn) {
      @exportarr=split(/\_\_\_\;\_\_\_/,$found);                      $found=$value;
                   }
  } else {              } 
 #          }
 # Not cached      }
 #              #
       # Check if still valid
       #
       if ($found) {
           if (&forcedrecalc($uname,$udom,$stype,$usymb)) {
               $found='';
           }
       }
       if ($found) {
           #
           # Return what was cached
           #
           @exportarr=split(/\_\_\_\;\_\_\_/,$found);
       } else {
           #
           # Not cached
           #        
           my $thissheet=&makenewsheet($uname,$udom,$stype,$usymb);
           &readsheet($thissheet,$fn);
           &updatesheet($thissheet);
           &loadrows($thissheet);
           &calcsheet($thissheet); 
           @exportarr=&exportdata($thissheet);
           #
           # Store now
           #
           my $cid=$ENV{'request.course.id'}; 
           my $current='';
           if ($stype eq 'studentcalc') {
               $current=&Apache::lonnet::reply('get:'.
                                               $ENV{'course.'.$cid.'.domain'}.':'.
                                               $ENV{'course.'.$cid.'.num'}.
                                               ':nohist_calculatedsheets:'.
                                               &Apache::lonnet::escape($key),
                                               $ENV{'course.'.$cid.'.home'});
           } else {
               $current=&Apache::lonnet::reply('get:'.
                                               &getudom($thissheet).':'.
                                               &getuname($thissheet).
                                               ':nohist_calculatedsheets_'.
                                               $ENV{'request.course.id'}.':'.
                                               &Apache::lonnet::escape($key),
                                               &getuhome($thissheet));
           }
           my %currentlystored=();
           unless ($current=~/^error\:/) {
               foreach (split(/___&\___/,&Apache::lonnet::unescape($current))) {
                   my ($name,$value)=split(/___=___/,$_);
                   $currentlystored{$name}=$value;
               }
           }
           $currentlystored{$fn}=join('___;___',@exportarr);
           #
           my $newstore='';
           foreach (keys(%currentlystored)) {
               if ($newstore) { $newstore.='___&___'; }
               $newstore.=$_.'___=___'.$currentlystored{$_};
           }
           my $now=time;
           if ($stype eq 'studentcalc') {
               &Apache::lonnet::reply('put:'.
                                      $ENV{'course.'.$cid.'.domain'}.':'.
                                      $ENV{'course.'.$cid.'.num'}.
                                      ':nohist_calculatedsheets:'.
                                      &Apache::lonnet::escape($key).'='.
                                      &Apache::lonnet::escape($newstore).'&'.
                                      &Apache::lonnet::escape($key).'.time='.$now,
                                      $ENV{'course.'.$cid.'.home'});
           } else {
               &Apache::lonnet::reply('put:'.
                                      &getudom($thissheet).':'.
                                      &getuname($thissheet).
                                      ':nohist_calculatedsheets_'.
                                      $ENV{'request.course.id'}.':'.
                                      &Apache::lonnet::escape($key).'='.
                                      &Apache::lonnet::escape($newstore).'&'.
                                      &Apache::lonnet::escape($key).'.time='.$now,
                                      &getuhome($thissheet));
           }
       }
       return @exportarr;
   }
   
     my $thissheet=&makenewsheet($uname,$udom,$stype,$usymb);  # ============================================================ Expiration Dates
     &readsheet($thissheet,$fn);  
     &updatesheet($thissheet);  
     &loadrows($thissheet);  
     &calcsheet($thissheet);   
     @exportarr=&exportdata($thissheet);  
 #  #
 # Store now  # Load previously cached student spreadsheets for this course
 #  #
     my $cid=$ENV{'request.course.id'};   sub expirationdates {
     my $current=();      undef %expiredates;
     if ($stype eq 'studentcalc') {      my $cid=$ENV{'request.course.id'};
        $current=&Apache::lonnet::reply('get:'.      my $reply=&Apache::lonnet::reply('dump:'.
                                      $ENV{'course.'.$cid.'.domain'}.':'.       $ENV{'course.'.$cid.'.domain'}.':'.
                                      $ENV{'course.'.$cid.'.num'}.                                       $ENV{'course.'.$cid.'.num'}.
      ':nohist_calculatedsheets:'.       ':nohist_expirationdates',
                                      &Apache::lonnet::escape($key),  
                                      $ENV{'course.'.$cid.'.home'});                                       $ENV{'course.'.$cid.'.home'});
     } else {      unless ($reply=~/^error\:/) {
        $current=&Apache::lonnet::reply('get:'.   foreach (split(/\&/,$reply)) {
                                      &getudom($thissheet).':'.              my ($name,$value)=split(/\=/,$_);
                                      &getuname($thissheet).              $expiredates{&Apache::lonnet::unescape($name)}
      ':nohist_calculatedsheets_'.                          =&Apache::lonnet::unescape($value);
                                      $ENV{'request.course.id'}.':'.          }
                                      &Apache::lonnet::escape($key),      }
                                      &getuhome($thissheet));  
   
     }  
     my %currentlystored=();  
     unless ($current=~/^error\:/) {  
        map {  
            my ($name,$value)=split(/\_\_\_\=\_\_\_/,$_);  
            $currentlystored{$name}=$value;  
        } split(/\_\_\_\&\_\_\_/,&Apache::lonnet::unescape($current));  
     }  
     $currentlystored{$fn}=join('___;___',@exportarr);  
   
     my $newstore='';  
     map {  
         if ($newstore) { $newstore.='___&___'; }  
         $newstore.=$_.'___=___'.$currentlystored{$_};  
     } keys %currentlystored;  
     if ($stype eq 'studentcalc') {  
        &Apache::lonnet::reply('put:'.  
                          $ENV{'course.'.$cid.'.domain'}.':'.  
                          $ENV{'course.'.$cid.'.num'}.  
  ':nohist_calculatedsheets:'.  
                          &Apache::lonnet::escape($key).'='.  
  &Apache::lonnet::escape($newstore),  
                          $ENV{'course.'.$cid.'.home'});  
    } else {  
        &Apache::lonnet::reply('put:'.  
                          &getudom($thissheet).':'.  
                          &getuname($thissheet).  
  ':nohist_calculatedsheets_'.  
                          $ENV{'request.course.id'}.':'.  
                          &Apache::lonnet::escape($key).'='.  
  &Apache::lonnet::escape($newstore),  
                          &getuhome($thissheet));  
    }  
  }  
  return @exportarr;  
 }  }
   
 # ===================================================== Calculated sheets cache  # ===================================================== Calculated sheets cache
Line 1610  sub cachedcsheets { Line 2380  sub cachedcsheets {
      ':nohist_calculatedsheets',       ':nohist_calculatedsheets',
                                      $ENV{'course.'.$cid.'.home'});                                       $ENV{'course.'.$cid.'.home'});
     unless ($reply=~/^error\:/) {      unless ($reply=~/^error\:/) {
  map {   foreach ( split(/\&/,$reply)) {
             my ($name,$value)=split(/\=/,$_);              my ($name,$value)=split(/\=/,$_);
             $oldsheets{&Apache::lonnet::unescape($name)}              $oldsheets{&Apache::lonnet::unescape($name)}
                       =&Apache::lonnet::unescape($value);                        =&Apache::lonnet::unescape($value);
         } split(/\&/,$reply);          }
     }      }
 }  }
   
Line 1632  sub cachedssheets { Line 2402  sub cachedssheets {
                                       $ENV{'request.course.id'},                                        $ENV{'request.course.id'},
                                      $shome);                                       $shome);
     unless ($reply=~/^error\:/) {      unless ($reply=~/^error\:/) {
  map {   foreach ( split(/\&/,$reply)) {
             my ($name,$value)=split(/\=/,$_);              my ($name,$value)=split(/\=/,$_);
             $oldsheets{&Apache::lonnet::unescape($name)}              $oldsheets{&Apache::lonnet::unescape($name)}
                       =&Apache::lonnet::unescape($value);                        =&Apache::lonnet::unescape($value);
         } split(/\&/,$reply);          }
     }      }
     $loadedcaches{$sname.'_'.$sdom}=1;      $loadedcaches{$sname.'_'.$sdom}=1;
   }    }
Line 1656  sub cachedssheets { Line 2426  sub cachedssheets {
   
 sub handler {  sub handler {
     my $r=shift;      my $r=shift;
   
     if ($r->header_only) {      if ($r->header_only) {
       $r->content_type('text/html');          $r->content_type('text/html');
       $r->send_http_header;          $r->send_http_header;
       return OK;          return OK;
     }      }
       # Global directory configs
 # ---------------------------------------------------- Global directory configs      $includedir=$r->dir_config('lonIncludes');
       $tmpdir=$r->dir_config('lonDaemons').'/tmp/';
 $includedir=$r->dir_config('lonIncludes');      # Needs to be in a course
 $tmpdir=$r->dir_config('lonDaemons').'/tmp/';      if ($ENV{'request.course.fn'}) { 
           # Get query string for limited number of parameters
 # ----------------------------------------------------- Needs to be in a course          &Apache::loncommon::get_unprocessed_cgi
               ($ENV{'QUERY_STRING'},['uname','udom','usymb','ufn']);
   if ($ENV{'request.course.fn'}) {           if (($ENV{'form.usymb'}=~/^\_(\w+)/) && (!$ENV{'form.ufn'})) {
               $ENV{'form.ufn'}='default_'.$1;
 # --------------------------- Get query string for limited number of parameters          }
           # Interactive loading of specific sheet?
     map {          if (($ENV{'form.load'}) && ($ENV{'form.loadthissheet'} ne 'Default')) {
        my ($name, $value) = split(/=/,$_);              $ENV{'form.ufn'}=$ENV{'form.loadthissheet'};
        $value =~ tr/+/ /;          }
        $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;          # Nothing there? Must be login user
        if (($name eq 'uname') || ($name eq 'udom') ||           my $aname;
            ($name eq 'usymb') || ($name eq 'ufn')) {          my $adom;
            unless ($ENV{'form.'.$name}) {  
               $ENV{'form.'.$name}=$value;          unless ($ENV{'form.uname'}) {
    }              $aname=$ENV{'user.name'};
        }              $adom=$ENV{'user.domain'};
     } (split(/&/,$ENV{'QUERY_STRING'}));          } else {
               $aname=$ENV{'form.uname'};
 # ------------------------------------------- Nothing there? Must be login user              $adom=$ENV{'form.udom'};
           }
     my $aname;          # Open page
     my $adom;          $r->content_type('text/html');
           $r->header_out('Cache-control','no-cache');
     unless ($ENV{'form.uname'}) {          $r->header_out('Pragma','no-cache');
  $aname=$ENV{'user.name'};          $r->send_http_header;
         $adom=$ENV{'user.domain'};          # Screen output
     } else {          $r->print('<html><head><title>LON-CAPA Spreadsheet</title>');
         $aname=$ENV{'form.uname'};          $r->print(<<ENDSCRIPT);
         $adom=$ENV{'form.udom'};  
     }  
   
 # ------------------------------------------------------------------- Open page  
   
     $r->content_type('text/html');  
     $r->header_out('Cache-control','no-cache');  
     $r->header_out('Pragma','no-cache');  
     $r->send_http_header;  
   
 # --------------------------------------------------------------- Screen output  
   
     $r->print('<html><head><title>LON-CAPA Spreadsheet</title>');  
     $r->print(<<ENDSCRIPT);  
 <script language="JavaScript">  <script language="JavaScript">
   
     function celledit(cn,cf) {      function celledit(cn,cf) {
         var cnf=prompt(cn,cf);          var cnf=prompt(cn,cf);
  if (cnf!=null) {          if (cnf!=null) {
     document.sheet.unewfield.value=cn;              document.sheet.unewfield.value=cn;
             document.sheet.unewformula.value=cnf;              document.sheet.unewformula.value=cnf;
             document.sheet.submit();              document.sheet.submit();
         }          }
     }      }
   
 </script>      function changesheet(cn) {
 ENDSCRIPT   document.sheet.unewfield.value=cn;
     $r->print('</head><body bgcolor="#FFFFFF">'.          document.sheet.unewformula.value='changesheet';
        '<img align=right src=/adm/lonIcons/lonlogos.gif>'.          document.sheet.submit();
        '<h1>LON-CAPA Spreadsheet</h1>'.  
        '<form action="'.$r->uri.'" name=sheet method=post>'.  
        &hiddenfield('uname',$ENV{'form.uname'}).  
        &hiddenfield('udom',$ENV{'form.udom'}).  
        &hiddenfield('usymb',$ENV{'form.usymb'}).  
        &hiddenfield('unewfield','').  
        &hiddenfield('unewformula',''));  
   
 # ---------------------- Make sure that this gets out, even if user hits "stop"  
   
     $r->rflush();  
   
 # ---------------------------------------- Read new sheet or modified worksheet  
   
     $r->uri=~/\/(\w+)$/;  
   
     my $asheet=&makenewsheet($aname,$adom,$1,$ENV{'form.usymb'});  
   
 # ------------------------ If a new formula had been entered, go from work copy  
   
     if ($ENV{'form.unewfield'}) {  
         $r->print('<h2>Modified Workcopy</h2>');  
         $ENV{'form.unewformula'}=~s/\'/\"/g;  
         $r->print('<p>New formula: '.$ENV{'form.unewfield'}.'='.  
                   $ENV{'form.unewformula'}.'<p>');  
         &setfilename($asheet,$ENV{'form.ufn'});  
  &tmpread($asheet,  
                  $ENV{'form.unewfield'},$ENV{'form.unewformula'});  
   
      } elsif ($ENV{'form.saveas'}) {  
         &setfilename($asheet,$ENV{'form.ufn'});  
  &tmpread($asheet);  
     } else {  
         &readsheet($asheet,$ENV{'form.ufn'});  
     }      }
   
 # -------------------------------------------------- Print out user information      function insertrow(cn) {
    document.sheet.unewfield.value='insertrow';
           document.sheet.unewformula.value=cn;
           document.sheet.submit();
       }
   
     unless (&gettype($asheet) eq 'classcalc') {  </script>
         $r->print('<p><b>User:</b> '.&getuname($asheet).  ENDSCRIPT
                   '<br><b>Domain:</b> '.&getudom($asheet));          $r->print('</head>'.&Apache::loncommon::bodytag('Grades Spreadsheet').
         if (&getcsec($asheet) eq '-1') {                    '<form action="'.$r->uri.'" name=sheet method=post>'.
            $r->print('<h3><font color=red>'.                    &hiddenfield('uname',$ENV{'form.uname'}).
                      'Not a student in this course</font></h3>');                    &hiddenfield('udom',$ENV{'form.udom'}).
                     &hiddenfield('usymb',$ENV{'form.usymb'}).
                     &hiddenfield('unewfield','').
                     &hiddenfield('unewformula',''));
           # Send this out right away
           $r->rflush();
           # Full recalc?
           if ($ENV{'form.forcerecalc'}) {
               $r->print('<h4>Completely Recalculating Sheet ...</h4>');
               undef %spreadsheets;
               undef %courserdatas;
               undef %userrdatas;
               undef %defaultsheets;
               undef %updatedata;
           }
           # Read new sheet or modified worksheet
           $r->uri=~/\/(\w+)$/;
           my $asheet=&makenewsheet($aname,$adom,$1,$ENV{'form.usymb'});
           # If a new formula had been entered, go from work copy
           if ($ENV{'form.unewfield'}) {
               $r->print('<h2>Modified Workcopy</h2>');
               $ENV{'form.unewformula'}=~s/\'/\"/g;
               $r->print('<p>New formula: '.$ENV{'form.unewfield'}.'='.
                         $ENV{'form.unewformula'}.'<p>');
               &setfilename($asheet,$ENV{'form.ufn'});
               &tmpread($asheet,
                        $ENV{'form.unewfield'},$ENV{'form.unewformula'});
               
           } elsif ($ENV{'form.saveas'}) {
               &setfilename($asheet,$ENV{'form.ufn'});
               &tmpread($asheet);
         } else {          } else {
            $r->print('<br><b>Section/Group:</b> '.&getcsec($asheet));              &readsheet($asheet,$ENV{'form.ufn'});
         }          }
     }          # Print out user information
           unless (&gettype($asheet) eq 'classcalc') {
 # ---------------------------------------------------------------- Course title              $r->print('<p><b>User:</b> '.&getuname($asheet).
                         '<br><b>Domain:</b> '.&getudom($asheet));
     $r->print('<h1>'.              if (&getcsec($asheet) eq '-1') {
             $ENV{'course.'.$ENV{'request.course.id'}.'.description'}.'</h1>');                  $r->print('<h3><font color=red>'.
                             'Not a student in this course</font></h3>');
               } else {
 # ---------------------------------------------------- See if something to save                  $r->print('<br><b>Section/Group:</b> '.&getcsec($asheet));
               }
     if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) {              if ($ENV{'form.usymb'}) {
         my $fname='';                  $r->print('<br><b>Assessment:</b> <tt>'.
  if ($ENV{'form.saveas'} && ($fname=$ENV{'form.newfn'})) {                            $ENV{'form.usymb'}.'</tt>');
             $fname=~s/\W/\_/g;              }
           }
           # See if user can see this
           if ((&gettype($asheet) eq 'classcalc') || 
               (&getuname($asheet) ne $ENV{'user.name'}) ||
               (&getudom($asheet) ne $ENV{'user.domain'})) {
               unless (&Apache::lonnet::allowed('vgr',&getcid($asheet))) {
                   $r->print('<h1>Access Permission Denied</h1>'.
                             '</form></body></html>');
                   return OK;
               }
           }
           # Additional options
           $r->print(
    '<input type=submit name=forcerecalc value="Completely Recalculate Sheet"><p>'
                     );
           if (&gettype($asheet) eq 'assesscalc') {
               $r->print ('<p><font size=+2><a href="/adm/studentcalc?uname='.
                          &getuname($asheet).'&udom='.&getudom($asheet).'">'.
                          'Level up: Student Sheet</a></font><p>');
           }
           if ((&gettype($asheet) eq 'studentcalc') && 
               (&Apache::lonnet::allowed('vgr',&getcid($asheet)))) {
               $r->print (
                          '<p><font size=+2><a href="/adm/classcalc">'.
                          'Level up: Course Sheet</a></font><p>');
           }
           # Save dialog
           if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) {
               my $fname=$ENV{'form.ufn'};
               $fname=~s/\_[^\_]+$//;
             if ($fname eq 'default') { $fname='course_default'; }              if ($fname eq 'default') { $fname='course_default'; }
             $fname.='_'.&gettype($asheet);              $r->print
             &setfilename($asheet,$fname);                  ('<input type=submit name=saveas value="Save as ...">'.
             $ENV{'form.ufn'}=$fname;                   '<input type=text size=20 name=newfn value="'.$fname.'">'.
     $r->print('<p>Saving spreadsheet: '.                   '(make default: <input type=checkbox name="makedefufn">)<p>');
                          &writesheet($asheet,$ENV{'form.makedefufn'}).'<p>');          }
  }          $r->print(&hiddenfield('ufn',&getfilename($asheet)));
     }          # Load dialog
           if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) {
 # ------------------------------------------------ Write the modified worksheet              $r->print('<p><input type=submit name=load value="Load ...">'.
                         '<select name="loadthissheet">'.
                         '<option name="default">Default</option>');
               foreach (&othersheets($asheet,&gettype($asheet))) {
                   $r->print('<option name="'.$_.'"');
                   if ($ENV{'form.ufn'} eq $_) {
                       $r->print(' selected');
                   }
                   $r->print('>'.$_.'</option>');
               } 
               $r->print('</select><p>');
               if (&gettype($asheet) eq 'studentcalc') {
                   &setothersheets($asheet,&othersheets($asheet,'assesscalc'));
               }
           }
           # Cached sheets
           &expirationdates();
           undef %oldsheets;
           undef %loadedcaches;
           if (&gettype($asheet) eq 'classcalc') {
               $r->print
                   ("Loading previously calculated student sheets ...<br>\n");
               $r->rflush();
               &cachedcsheets();
           } elsif (&gettype($asheet) eq 'studentcalc') {
               $r->print
                   ("Loading previously calculated assessment sheets ...<br>\n");
               $r->rflush();
               &cachedssheets(&getuname($asheet),&getudom($asheet),
                              &getuhome($asheet));
           }
           # Update sheet, load rows
           $r->print("Loaded sheet(s), updating rows ...<br>\n");
           $r->rflush();
           #
           &updatesheet($asheet);
           $r->print("Updated rows, loading row data ...<br>\n");
           $r->rflush();
           #
           &loadrows($asheet,$r);
           $r->print("Loaded row data, calculating sheet ...<br>\n");
           $r->rflush();
           #
           my $calcoutput=&calcsheet($asheet);
           $r->print('<h3><font color=red>'.$calcoutput.'</h3></font>');
           # See if something to save
           if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) {
               my $fname='';
               if ($ENV{'form.saveas'} && ($fname=$ENV{'form.newfn'})) {
                   $fname=~s/\W/\_/g;
                   if ($fname eq 'default') { $fname='course_default'; }
                   $fname.='_'.&gettype($asheet);
                   &setfilename($asheet,$fname);
                   $ENV{'form.ufn'}=$fname;
                   $r->print('<p>Saving spreadsheet: '.
                             &writesheet($asheet,$ENV{'form.makedefufn'}).'<p>');
               }
           }
           #Write the modified worksheet
   
    $r->print('<b>Current sheet:</b> '.&getfilename($asheet).'<p>');     $r->print('<b>Current sheet:</b> '.&getfilename($asheet).'<p>');
   
    &tmpwrite($asheet);     &tmpwrite($asheet);
   
 # ----------------------------------------------------------------- Save dialog      if (&gettype($asheet) eq 'studentcalc') {
    $r->print('<br>Show rows with empty A column: ');
       } else {
           $r->print('<br>Show empty rows: ');
       } 
   
       $r->print(&hiddenfield('userselhidden','true').
                '<input type=checkbox name=showall onClick="submit()"');
   
     if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) {      if ($ENV{'form.showall'}) { 
         my $fname=$ENV{'form.ufn'};         $r->print(' checked'); 
         $fname=~s/\_[^\_]+$//;      } else {
         if ($fname eq 'default') { $fname='course_default'; }   unless ($ENV{'form.userselhidden'}) {
         $r->print('<input type=submit name=saveas value="Save as ...">'.             unless 
               '<input type=text size=20 name=newfn value="'.$fname.   ($ENV{'course.'.$ENV{'request.course.id'}.'.hideemptyrows'} eq 'yes') {
               '"> (make default: <input type=checkbox name="makedefufn">)<p>');            $r->print(' checked');
             $ENV{'form.showall'}=1;
              }
          }
     }      }
       $r->print('>');
     $r->print(&hiddenfield('ufn',&getfilename($asheet)));  
   
 # --------------------------------------------------------------- Cached sheets  
   
     undef %oldsheets;  
     undef %loadedcaches;  
   
     if (&gettype($asheet) eq 'classcalc') {      if (&gettype($asheet) eq 'classcalc') {
         $r->print("Loading previously calculated student sheets ...<br>\n");         $r->print(
         $r->rflush();     ' Output CSV format: <input type=checkbox name=showcsv onClick="submit()"');
         &cachedcsheets();         if ($ENV{'form.showcsv'}) { $r->print(' checked'); }
     } elsif (&gettype($asheet) eq 'studentcalc') {         $r->print('>');
         $r->print("Loading previously calculated assessment sheets ...<br>\n");      }
         $r->rflush();  
         &cachedssheets(&getuname($asheet),&getudom($asheet),  # ------------------------------------------------------------------ Insertrows
                        &getuhome($asheet));      $r->print('&nbsp;Student Status: '.
     }                &Apache::lonhtmlcommon::StatusOptions
                 ($ENV{'form.Status'},'sheet'));
   
      $r->print(<<ENDINSERTBUTTONS);
   <br>
   <input type='button' onClick='insertrow("top");' 
   value='Insert Row Top'>
   <input type='button' onClick='insertrow("bottom");' 
   value='Insert Row Bottom'><br>
   ENDINSERTBUTTONS
   
 # ----------------------------------------------------- Update sheet, load rows  # ------------------------------------------------------------- Print out sheet
   
     $r->print("Loaded sheet(s), updating rows ...<br>\n");  
     $r->rflush();  
   
     &updatesheet($asheet);  
   
     $r->print("Updated rows, loading row data ...<br>\n");  
     $r->rflush();  
   
     &loadrows($asheet,$r);  
   
     $r->print("Loaded row data, calculating sheet ...<br>\n");  
     $r->rflush();  
   
     my $calcoutput=&calcsheet($asheet);  
     $r->print('<h3><font color=red>'.$calcoutput.'</h3></font>');  
   
     &outsheet($r,$asheet);      &outsheet($r,$asheet);
     $r->print('</form></body></html>');      $r->print('</form></body></html>');
Line 1865  ENDSCRIPT Line 2704  ENDSCRIPT
   
 1;  1;
 __END__  __END__
   
   
   
   

Removed from v.1.46  
changed lines
  Added in v.1.104


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