Diff for /loncom/interface/Attic/lonspreadsheet.pm between versions 1.28 and 1.81

version 1.28, 2001/01/02 16:03:14 version 1.81, 2002/04/08 18:28:03
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 Gerd Kortemeyer  # 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,
   # 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
   
 package Apache::lonspreadsheet;  # 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;
               
 use strict;  use strict;
 use Safe;  use Safe;
 use Safe::Hole;  use Safe::Hole;
Line 17  use GDBM_File; Line 73  use GDBM_File;
 use HTML::TokeParser;  use HTML::TokeParser;
   
 #  #
   # Caches for previously calculated spreadsheets
   #
   
   my %oldsheets;
   my %loadedcaches;
   my %expiredates;
   
   #
   # Cache for stores of an individual user
   #
   
   my $cachedassess;
   my %cachedstores;
   
   #
 # These cache hashes need to be independent of user, resource and course  # These cache hashes need to be independent of user, resource and course
 # (user and course can/should be in the keys)  # (user and course can/should be in the keys)
 #  #
 use vars qw(%spreadsheets %courserdatas %userrdatas %defaultsheets);  
   my %spreadsheets;
   my %courserdatas;
   my %userrdatas;
   my %defaultsheets;
   my %updatedata;
   
 #  #
 # These global hashes are dependent on user, course and resource,   # These global hashes are dependent on user, course and resource, 
Line 39  my $tmpdir; Line 115  my $tmpdir;
 # ===================================== Implements an instance of a spreadsheet  # ===================================== Implements an instance of a spreadsheet
   
 sub initsheet {  sub initsheet {
     my $safeeval = new Safe;      my $safeeval = new Safe(shift);
     my $safehole = new Safe::Hole;      my $safehole = new Safe::Hole;
     $safeeval->permit("entereval");      $safeeval->permit("entereval");
     $safeeval->permit(":base_math");      $safeeval->permit(":base_math");
Line 55  sub initsheet { Line 131  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)
   
 %v=();   undef %v; 
 %t=();  undef %t;
 %f=();  undef %f;
 %c=();  undef %c;
 %rl=();  undef %rl;
   undef @os;
   
 $maxrow=0;  $maxrow=0;
 $sheettype='';  $sheettype='';
Line 81  $chome=''; Line 159  $chome='';
 $cnum='';  $cnum='';
 $cdom='';  $cdom='';
 $cid='';  $cid='';
   $cfn='';
   
 # symb  # symb
   
Line 115  sub mask { Line 194  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 162  sub mask { Line 241  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(%v);
    @Keys = $v{@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(%v);
    @Values =$v{@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 = $v{$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(%v);
    @Keys = $v{@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(%v);
    @Values =$v{@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];
   }
   
 sub NUM {  sub NUM {
     my $mask=mask(@_);      my $mask=mask(@_);
     my $num=0;      my $num= $#{@{grep(/$mask/,keys(%v))}}+1;
     map {  
         $num++;  
     } grep /$mask/,keys %v;  
     return $num;         return $num;   
 }  }
   
Line 175  sub BIN { Line 518  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(%v)) {
         if (($v{$_}>=$low) && ($v{$_}<=$high)) {          if (($v{$_}>=$low) && ($v{$_}<=$high)) {
             $num++;              $num++;
         }          }
     } grep /$mask/,keys %v;      }
     return $num;         return $num;   
 }  }
   
Line 187  sub BIN { Line 530  sub BIN {
 sub SUM {  sub SUM {
     my $mask=mask(@_);      my $mask=mask(@_);
     my $sum=0;      my $sum=0;
     map {      foreach (grep /$mask/,keys(%v)) {
         $sum+=$v{$_};          $sum+=$v{$_};
     } grep /$mask/,keys %v;      }
     return $sum;         return $sum;   
 }  }
   
 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(%v)) {
         $sum+=$v{$_};          $sum+=$v{$_};
         $num++;          $num++;
     } grep /$mask/,keys %v;      }
     if ($num) {      if ($num) {
        return $sum/$num;         return $sum/$num;
     } else {      } else {
Line 210  sub MEAN { Line 553  sub MEAN {
 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(%v)) {
         $sum+=$v{$_};          $sum+=$v{$_};
         $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(%v)) {
         $sum+=($v{$_}-$mean)**2;          $sum+=($v{$_}-$mean)**2;
     } grep /$mask/,keys %v;      }
     return sqrt($sum/($num-1));          return sqrt($sum/($num-1));    
 }  }
   
 sub PROD {  sub PROD {
     my $mask=mask(@_);      my $mask=mask(@_);
     my $prod=1;      my $prod=1;
     map {      foreach (grep /$mask/,keys(%v)) {
         $prod*=$v{$_};          $prod*=$v{$_};
     } grep /$mask/,keys %v;      }
     return $prod;         return $prod;   
 }  }
   
 sub MAX {  sub MAX {
     my $mask=mask(@_);      my $mask=mask(@_);
     my $max='-';      my $max='-';
     map {      foreach (grep /$mask/,keys(%v)) {
         unless ($max) { $max=$v{$_}; }          unless ($max) { $max=$v{$_}; }
         if (($v{$_}>$max) || ($max eq '-')) { $max=$v{$_}; }          if (($v{$_}>$max) || ($max eq '-')) { $max=$v{$_}; }
     } grep /$mask/,keys %v;      } 
     return $max;         return $max;   
 }  }
   
 sub MIN {  sub MIN {
     my $mask=mask(@_);      my $mask=mask(@_);
     my $min='-';      my $min='-';
     map {      foreach (grep /$mask/,keys(%v)) {
         unless ($max) { $max=$v{$_}; }          unless ($max) { $max=$v{$_}; }
         if (($v{$_}<$min) || ($min eq '-')) { $min=$v{$_}; }          if (($v{$_}<$min) || ($min eq '-')) { $min=$v{$_}; }
     } grep /$mask/,keys %v;      }
     return $min;         return $min;   
 }  }
   
Line 256  sub SUMMAX { Line 599  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(%v)) {
  $inside[$#inside+1]=$v{$_};   $inside[$#inside+1]=$v{$_};
     } 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 271  sub SUMMIN { Line 614  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(%v)) {
  $inside[$#inside+1]=$v{$_};   $inside[$#inside+1]=$v{$_};
     } 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 282  sub SUMMIN { Line 625  sub SUMMIN {
     return $sum;         return $sum;   
 }  }
   
   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 {
           return '$c{\''.$expression.'\'}';
       }
   }
   
 sub sett {  sub sett {
     %t=();      %t=();
     my $pattern='';      my $pattern='';
Line 290  sub sett { Line 669  sub sett {
     } else {      } else {
         $pattern='[A-Z]';          $pattern='[A-Z]';
     }      }
     map {      foreach (keys(%f)) {
  if ($_=~/template\_(\w)/) {   if ($_=~/template\_(\w)/) {
   my $col=$1;    my $col=$1;
           unless ($col=~/^$pattern/) {            unless ($col=~/^$pattern/) {
             map {      foreach (keys(%f)) {
       if ($_=~/A(\d+)/) {        if ($_=~/A(\d+)/) {
  my $trow=$1;   my $trow=$1;
                 if ($trow) {                  if ($trow) {
Line 303  sub sett { Line 682  sub sett {
                     $t{$lb}=~s/\#/$trow/g;                      $t{$lb}=~s/\#/$trow/g;
                     $t{$lb}=~s/\.\.+/\,/g;                      $t{$lb}=~s/\.\.+/\,/g;
                     $t{$lb}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$v\{\'$2\'\}/g;                      $t{$lb}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$v\{\'$2\'\}/g;
                       $t{$lb}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.&expandnamed($2)/ge;
                 }                  }
       }        }
             } keys %f;      }
   }    }
       }        }
     } keys %f;      }
     map {      foreach (keys(%f)) {
  if (($f{$_}) && ($_!~/template\_/)) {   if (($f{$_}) && ($_!~/template\_/)) {
             if ($_=~/^$pattern/) {              my $matches=($_=~/^$pattern(\d+)/);
               if  (($matches) && ($1)) {
         unless ($f{$_}=~/^\!/) {          unless ($f{$_}=~/^\!/) {
     $t{$_}=$c{$_};      $t{$_}=$c{$_};
                 }                  }
Line 319  sub sett { Line 700  sub sett {
        $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\$v\{\'$2\'\}/g;
                  $t{$_}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.&expandnamed($2)/ge;
             }              }
         }          }
     } keys %f;      }
     $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\$v\{\'$2\'\}/g;
       $t{'A0'}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.&expandnamed($2)/ge;
 }  }
   
 sub calc {  sub calc {
Line 334  sub calc { Line 717  sub calc {
     my $depth=0;      my $depth=0;
     while ($notfinished) {      while ($notfinished) {
  $notfinished=0;   $notfinished=0;
         map {          foreach (keys(%t)) {
             my $old=$v{$_};              my $old=$v{$_};
             $v{$_}=eval($t{$_});              $v{$_}=eval($t{$_});
     if ($@) {      if ($@) {
Line 342  sub calc { Line 725  sub calc {
                 return $@;                  return $@;
             }              }
     if ($v{$_} ne $old) { $notfinished=1; }      if ($v{$_} ne $old) { $notfinished=1; }
         } keys %t;          }
         $depth++;          $depth++;
         if ($depth>100) {          if ($depth>100) {
     %v=();      %v=();
Line 355  sub calc { Line 738  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;          $cols[$#cols+1]="'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;
 }  }
   
Line 370  sub outrowassess { Line 753  sub outrowassess {
     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});
          $cols[0]=$rl{$usy}.'<br>'.
                   '<select name="sel_'.$n.'" onChange="changesheet('.$n.
                   ')"><option name="default">Default</option>';
          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};          $cols[$#cols+1]="'$_$n','$fm'".'___eq___'.$v{$_.$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 393  sub outrow { Line 787  sub outrow {
     } 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};          $cols[$#cols+1]="'$_$n','$fm'".'___eq___'.$v{$_.$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',
        'N','O','P','Q','R','S','T','U','V','W','X','Y','Z') {
  $exportarray[$#exportarray+1]=$v{$_.'0'};   $exportarray[$#exportarray+1]=$v{$_.'0'};
     } ('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 @exportarray;      return @exportarray;
 }  }
   
Line 422  ENDDEFS Line 816  ENDDEFS
 # ------------------------------------------------ Add or change formula values  # ------------------------------------------------ Add or change formula values
   
 sub setformulas {  sub setformulas {
     my ($safeeval,@f)=@_;      my ($safeeval,%f)=@_;
     $safeeval->reval('%f='."('".join("','",@f)."');");      %{$safeeval->varglob('f')}=%f;
 }  }
   
 # ------------------------------------------------ Add or change formula values  # ------------------------------------------------ Add or change formula values
   
 sub setconstants {  sub setconstants {
     my ($safeeval,@c)=@_;      my ($safeeval,%c)=@_;
     $safeeval->reval('%c='."('".join("','",@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,%rl)=@_;
     $safeeval->reval('%rl='."('".join("','",@rl)."');");      %{$safeeval->varglob('rl')}=%rl;
 }  }
   
 # ------------------------------------------------------- Calculate spreadsheet  # ------------------------------------------------------- Calculate spreadsheet
Line 458  sub getvalues { Line 859  sub getvalues {
   
 sub getformulas {  sub getformulas {
     my $safeeval=shift;      my $safeeval=shift;
     return $safeeval->reval('%f');      return %{$safeeval->varglob('f')};
 }  }
   
 # -------------------------------------------------------------------- Get type  # -------------------------------------------------------------------- Get type
Line 495  sub getfilename { Line 896  sub getfilename {
     my $safeeval=shift;      my $safeeval=shift;
     return $safeeval->reval('$filename');      return $safeeval->reval('$filename');
 }  }
   
 # --------------------------------------------------------------- Get course ID  # --------------------------------------------------------------- Get course ID
   
 sub getcid {  sub getcid {
Line 502  sub getcid { Line 904  sub getcid {
     return $safeeval->reval('$cid');      return $safeeval->reval('$cid');
 }  }
   
   # --------------------------------------------------------- Get course filename
   
   sub getcfn {
       my $safeeval=shift;
       return $safeeval->reval('$cfn');
   }
   
 # ----------------------------------------------------------- Get course number  # ----------------------------------------------------------- Get course number
   
 sub getcnum {  sub getcnum {
Line 565  sub exportdata { Line 974  sub exportdata {
     return $safeeval->reval('&exportrowa()');      return $safeeval->reval('&exportrowa()');
 }  }
   
   
 # ========================================================== End of Spreadsheet  # ========================================================== End of Spreadsheet
 # =============================================================================  # =============================================================================
   
Line 577  sub rown { Line 987  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;
     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 ($n eq '-') { $proc='&templaterow'; $n=-1; $dataflag=1; }
     map {      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 627  sub rown { Line 1033  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 649  sub outsheet { Line 1062  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.
Line 657  sub outsheet { Line 1072  sub outsheet {
                   '<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',
        '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') {
         $showf++;          $showf++;
         if ($showf<=$maxred) {           if ($showf<=$maxred) { 
            $tabledata.='<td bgcolor="#FFDDDD">';              $tabledata.='<td bgcolor="#FFDDDD">'; 
Line 665  sub outsheet { Line 1083  sub outsheet {
            $tabledata.='<td>';             $tabledata.='<td>';
         }          }
         $tabledata.="<b><font size=+1>$_</font></b></td>";          $tabledata.="<b><font size=+1>$_</font></b></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',      $tabledata.='</tr>'.&rown($safeeval,'-').&rown($safeeval,0);
        'a','b','c','d','e','f','g','h','i','j','k','l','m',     } else { $tabledata='<pre>'; }
        'n','o','p','q','r','s','t','u','v','w','x','y','z');  
     $tabledata.='</tr>';      $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;
       }
       @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>');
           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') {
              $r->print('<td>'.$_.'</td>');
           }
           $r->print('</tr>');
          }
          $n++;
          $r->print($thisrow);
         }
       }
       $r->print($ENV{'form.showcsv'}?'</pre>':'</table>');
   }
   
   #
   # ----------------------------------------------- Read list of available sheets
   # 
   sub othersheets {
       my ($safeeval,$stype)=@_;
       #
       my $cnum=&getcnum($safeeval);
       my $cdom=&getcdom($safeeval);
       my $chome=&getchome($safeeval);
       #
       my @alternatives=();
       my %results=&Apache::lonnet::dump($stype.'_spreadsheets',$cdom,$cnum);
       my ($tmp) = keys(%results);
       unless ($tmp =~ /^(con_lost|error|no_such_host)/i) {
           @alternatives = sort (keys(%results));
     }      }
     $r->print('</table>');      return @alternatives; 
 }  }
   
 #  #
Line 723  sub readsheet { Line 1192  sub readsheet {
  my $sheetxml='';   my $sheetxml='';
        {         {
          my $fh;           my $fh;
          if ($fh=Apache::File->new($includedir.           my $dfn=$fn;
                          '/default.'.&gettype($safeeval))) {           $dfn=~s/\_/\./g;
            if ($fh=Apache::File->new($includedir.'/'.$dfn)) {
                $sheetxml=join('',<$fh>);                 $sheetxml=join('',<$fh>);
           }   } else {
                $sheetxml='<field row="0" col="A">"Error"</field>';
    }
        }         }
         my $parser=HTML::TokeParser->new(\$sheetxml);          my $parser=HTML::TokeParser->new(\$sheetxml);
         my $token;          my $token;
Line 744  sub readsheet { Line 1216  sub readsheet {
         }          }
       } else {        } else {
           my $sheet='';            my $sheet='';
           my $reply=&Apache::lonnet::reply('dump:'.$cdom.':'.$cnum.':'.$fn,            my %tmphash = &Apache::lonnet::dump($fn,$cdom,$cnum);
                                          $chome);            my ($tmp) = keys(%tmphash);
           unless ($reply=~/^error\:/) {            unless ($tmp =~ /^(con_lost|error|no_such_host)/i) {
              $sheet=$reply;                foreach (keys(%tmphash)) {
   }                    $f{$_}=$tmphash{$_};
           map {                }
              my ($name,$value)=split(/\=/,$_);            }
              $f{&Apache::lonnet::unescape($name)}=        }
         &Apache::lonnet::unescape($value);  
           } split(/\&/,$sheet);  
        }  
 # --------------------------------------------------------------- Cache and set  # --------------------------------------------------------------- Cache and set
        $spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}=join('___;___',%f);           $spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}=join('___;___',%f);  
        &setformulas($safeeval,%f);         &setformulas($safeeval,%f);
Line 765  sub readsheet { Line 1234  sub readsheet {
   
 sub makenewsheet {  sub makenewsheet {
     my ($uname,$udom,$stype,$usymb)=@_;      my ($uname,$udom,$stype,$usymb)=@_;
     my $safeeval=initsheet();      my $safeeval=initsheet($stype);
     $safeeval->reval(      $safeeval->reval(
        '$uname='.$uname.         '$uname="'.$uname.
       ';$udom='.$udom.        '";$udom="'.$udom.
       ';$sheettype='.$stype.        '";$uhome="'.&Apache::lonnet::homeserver($uname,$udom).
       ';$usymb='.$usymb.        '";$sheettype="'.$stype.
       ';$cid='.$ENV{'request.course.id'}.        '";$usymb="'.$usymb.
       ';$cnum='.$ENV{'course.'.$ENV{'request.course.id'}.'.num'}.        '";$csec="'.&Apache::lonnet::usection($udom,$uname,
       ';$cdom='.$ENV{'course.'.$ENV{'request.course.id'}.'.domain'}.                                              $ENV{'request.course.id'}).
       ';$chome='.$ENV{'course.'.$ENV{'request.course.id'}.'.home'}.';');        '";$cid="'.$ENV{'request.course.id'}.
         '";$cfn="'.$ENV{'request.course.fn'}.
         '";$cnum="'.$ENV{'course.'.$ENV{'request.course.id'}.'.num'}.
         '";$cdom="'.$ENV{'course.'.$ENV{'request.course.id'}.'.domain'}.
         '";$chome="'.$ENV{'course.'.$ENV{'request.course.id'}.'.home'}.'";');
     return $safeeval;      return $safeeval;
 }  }
   
Line 795  sub writesheet { Line 1268  sub writesheet {
     $spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}=join('___;___',%f);          $spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}=join('___;___',%f);    
 # ----------------------------------------------------------------- Write sheet  # ----------------------------------------------------------------- Write sheet
     my $sheetdata='';      my $sheetdata='';
     map {      foreach (keys(%f)) {
        unless ($f{$_} eq 'import') {
        $sheetdata.=&Apache::lonnet::escape($_).'='.         $sheetdata.=&Apache::lonnet::escape($_).'='.
    &Apache::lonnet::escape($f{$_}).'&';     &Apache::lonnet::escape($f{$_}).'&';
     } keys %f;       }
       }
     $sheetdata=~s/\&$//;      $sheetdata=~s/\&$//;
     my $reply=&Apache::lonnet::reply('put:'.$cdom.':'.$cnum.':'.$fn.':'.      my $reply=&Apache::lonnet::reply('put:'.$cdom.':'.$cnum.':'.$fn.':'.
               $sheetdata,$chome);                $sheetdata,$chome);
     if ($reply eq 'ok') {      if ($reply eq 'ok') {
           $reply=&Apache::lonnet::reply('put:'.$cdom.':'.$cnum.':'.            $reply=&Apache::lonnet::reply('put:'.$cdom.':'.$cnum.':'.
               &stype.'_spreadsheets:'.                $stype.'_spreadsheets:'.
               &Apache::lonnet::escape($fn).'='.$ENV{'user.name'},                &Apache::lonnet::escape($fn).'='.$ENV{'user.name'}.'@'.
                                                  $ENV{'user.domain'},
               $chome);                $chome);
           if ($reply eq 'ok') {            if ($reply eq 'ok') {
               if ($makedef) {                 if ($makedef) { 
Line 863  sub tmpread { Line 1339  sub tmpread {
             $fo{$name}=$value;              $fo{$name}=$value;
         }          }
     }      }
     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};
           }
       } else {
          if ($nfield) { $fo{$nfield}=$nform; }
       }
     &setformulas($safeeval,%fo);      &setformulas($safeeval,%fo);
 }  }
   
 # ================================================================== Parameters  # ================================================================== Parameters
 # -------------------------------------------- Figure out a cascading parameter  # -------------------------------------------- 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 {  sub parmval {
     my ($what,$safeeval)=@_;      my ($what,$safeeval)=@_;
Line 886  sub parmval { Line 1374  sub parmval {
 # ----------------------------------------------------- Cascading lookup scheme  # ----------------------------------------------------- Cascading lookup scheme
        my $rwhat=$what;         my $rwhat=$what;
        $what=~s/^parameter\_//;         $what=~s/^parameter\_//;
        $what=~s/\_/\./;         $what=~s/\_([^\_]+)$/\.$1/;
   
        my $symbparm=$symb.'.'.$what;         my $symbparm=$symb.'.'.$what;
        my $mapparm=$mapname.'___(all).'.$what;         my $mapparm=$mapname.'___(all).'.$what;
        my $usercourseprefix=$cid.'_'.$uname.'_'.$udom;         my $usercourseprefix=$uname.'_'.$udom.'_'.$cid;
   
        my $seclevel=         my $seclevel=
             $usercourseprefix.'.['.              $usercourseprefix.'.['.
Line 966  sub updateclasssheet { Line 1454  sub updateclasssheet {
     my %currentlist=();      my %currentlist=();
     my $now=time;      my $now=time;
     unless ($classlst=~/^error\:/) {      unless ($classlst=~/^error\:/) {
         map {          foreach (split(/\&/,$classlst)) {
             my ($name,$value)=split(/\=/,$_);              my ($name,$value)=split(/\=/,$_);
             my ($end,$start)=split(/\:/,&Apache::lonnet::unescape($value));              my ($end,$start)=split(/\:/,&Apache::lonnet::unescape($value));
             my $active=1;              my $active=1;
Line 977  sub updateclasssheet { Line 1465  sub updateclasssheet {
                 my ($sname,$sdom)=split(/\:/,$name);                  my ($sname,$sdom)=split(/\:/,$name);
                 my $ssec=&Apache::lonnet::usection($sdom,$sname,$cid);                  my $ssec=&Apache::lonnet::usection($sdom,$sname,$cid);
                 if ($ssec==-1) {                  if ($ssec==-1) {
      unless ($ENV{'form.showcsv'}) {
                     $rowlabel='<font color=red>Data not available: '.$name.                      $rowlabel='<font color=red>Data not available: '.$name.
       '</font>';        '</font>';
      } else {
          $rowlabel='ERROR","'.$name.
                                    '","Data not available","","","';
                      }
                 } else {                  } else {
                     my %reply=&Apache::lonnet::idrget($sdom,$sname);                      my %reply=&Apache::lonnet::idrget($sdom,$sname);
                     my $reply=&Apache::lonnet::reply('get:'.$sdom.':'.$sname.                      my $reply=&Apache::lonnet::reply('get:'.$sdom.':'.$sname.
       ':environment:firstname&middlename&lastname&generation',        ':environment:firstname&middlename&lastname&generation',
                       &Apache::lonnet::homeserver($sname,$sdom));                        &Apache::lonnet::homeserver($sname,$sdom));
                     $rowlabel=$ssec.'&nbsp;'.$reply{$sname}.'<br>';     unless ($ENV{'form.showcsv'}) {
                     map {                      $rowlabel='<a href="/adm/studentcalc?uname='.$sname.
                                 '&udom='.$sdom.'">'.
                                 $ssec.'&nbsp;'.$reply{$sname}.'<br>';
                       foreach ( split(/\&/,$reply)) {
                         $rowlabel.=&Apache::lonnet::unescape($_).' ';                          $rowlabel.=&Apache::lonnet::unescape($_).' ';
                     } split(/\&/,$reply);                      }
                       $rowlabel.='</a>';
      } else {
       $rowlabel=$ssec.'","'.$reply{$sname}.'"';
                       my $ncount=0;
                       foreach (split(/\&/,$reply)) {
                           $rowlabel.=',"'.&Apache::lonnet::unescape($_).'"';
                           $ncount++;
                       }
                       unless ($ncount==4) { $rowlabel.=',""'; }
                       $rowlabel=~s/\"$//;
      }
                 }                  }
  $currentlist{&Apache::lonnet::unescape($name)}=$rowlabel;   $currentlist{&Apache::lonnet::unescape($name)}=$rowlabel;
             }              }
         } split(/\&/,$classlst);          } # end of foreach (split(/\&/,$classlst))
 #  #
 # -------------------- Find discrepancies between the course row table and this  # -------------------- Find discrepancies between the course row table and this
 #  #
Line 1002  sub updateclasssheet { Line 1509  sub updateclasssheet {
         my %existing=();          my %existing=();
   
 # ----------------------------------------------------------- Now obsolete rows  # ----------------------------------------------------------- Now obsolete rows
  map {   foreach (keys(%f)) {
     if ($_=~/^A(\d+)/) {      if ($_=~/^A(\d+)/) {
                 $maxrow=($1>$maxrow)?$1:$maxrow;                  $maxrow=($1>$maxrow)?$1:$maxrow;
                 $existing{$f{$_}}=1;                  $existing{$f{$_}}=1;
Line 1011  sub updateclasssheet { Line 1518  sub updateclasssheet {
                    $changed=1;                     $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); }          if ($changed) { &setformulas($safeeval,%f); }
   
Line 1038  sub updateclasssheet { Line 1545  sub updateclasssheet {
 sub updatestudentassesssheet {  sub updatestudentassesssheet {
     my $safeeval=shift;      my $safeeval=shift;
     my %bighash;      my %bighash;
       my $stype=&gettype($safeeval);
       my %current=();
       unless ($updatedata{$ENV{'request.course.fn'}.'_'.$stype}) {
 # -------------------------------------------------------------------- Tie hash  # -------------------------------------------------------------------- Tie hash
       if (tie(%bighash,'GDBM_File',$ENV{'request.course.fn'}.'.db',        if (tie(%bighash,'GDBM_File',$ENV{'request.course.fn'}.'.db',
                        &GDBM_READER,0640)) {                         &GDBM_READER,0640)) {
 # --------------------------------------------------------- Get all assessments  # --------------------------------------------------------- Get all assessments
   
  my %allkeys=();   my %allkeys=('timestamp' => 
         my %allassess=();                       'Timestamp of Last Transaction<br>timestamp',
                        'subnumber' =>
                        'Number of Submissions<br>subnumber',
                        'tutornumber' =>
                        'Number of Tutor Responses<br>tutornumber',
                        'totalpoints' =>
                        'Total Points Granted<br>totalpoints');
   
           my $adduserstr='';
           if ((&getuname($safeeval) ne $ENV{'user.name'}) ||
               (&getudom($safeeval) ne $ENV{'user.domain'})) {
               $adduserstr='&uname='.&getuname($safeeval).
    '&udom='.&getudom($safeeval);
           }
   
         my $stype=&gettype($safeeval);          my %allassess=('_feedback' =>
                 '<a href="/adm/assesscalc?usymb=_feedback'.$adduserstr.
                          '">Feedback</a>',
                          '_evaluation' =>
                 '<a href="/adm/assesscalc?usymb=_evaluation'.$adduserstr.
                          '">Evaluation</a>',
                          '_tutoring' =>
                 '<a href="/adm/assesscalc?usymb=_tutoring'.$adduserstr.
                          '">Tutoring</a>',
                          '_discussion' =>
                 '<a href="/adm/assesscalc?usymb=_discussion'.$adduserstr.
                          '">Discussion</a>'
           );
   
         map {          foreach (keys(%bighash)) {
     if ($_=~/^src\_(\d+)\.(\d+)$/) {      if ($_=~/^src\_(\d+)\.(\d+)$/) {
        my $mapid=$1;         my $mapid=$1;
                my $resid=$2;                 my $resid=$2;
Line 1059  sub updatestudentassesssheet { Line 1594  sub updatestudentassesssheet {
                      &Apache::lonnet::declutter($bighash{'map_id_'.$mapid}).                       &Apache::lonnet::declutter($bighash{'map_id_'.$mapid}).
     '___'.$resid.'___'.      '___'.$resid.'___'.
     &Apache::lonnet::declutter($srcf);      &Apache::lonnet::declutter($srcf);
  $allassess{$symb}=$bighash{'title_'.$id};   $allassess{$symb}=
               '<a href="/adm/assesscalc?usymb='.$symb.$adduserstr.'">'.
                        $bighash{'title_'.$id}.'</a>';
                  if ($stype eq 'assesscalc') {                   if ($stype eq 'assesscalc') {
                    map {       foreach (split(/\,/,
       &Apache::lonnet::metadata($srcf,'keys'))) {
                        if (($_=~/^stores\_(.*)/) || ($_=~/^parameter\_(.*)/)) {                         if (($_=~/^stores\_(.*)/) || ($_=~/^parameter\_(.*)/)) {
   my $key=$_;    my $key=$_;
                           my $display=                            my $display=
       &Apache::lonnet::metadata($srcf,$key.'.display');        &Apache::lonnet::metadata($srcf,$key.'.display');
                           unless ($display) {                            unless ($display) {
                               $display=                                $display.=
          &Apache::lonnet::metadata($srcf,$key.'.name');           &Apache::lonnet::metadata($srcf,$key.'.name');
                           }                            }
                             $display.='<br>'.$key;
                           $allkeys{$key}=$display;                            $allkeys{$key}=$display;
        }         }
                    } split(/\,/,&Apache::lonnet::metadata($srcf,'keys'));                     } # end of foreach
          }           }
       }        }
    }     }
         } keys %bighash;          } # 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
 #  #
 # -------------------- Find discrepancies between the course row table and this  
 #  
         my %f=&getformulas($safeeval);  
         my $changed=0;  
   
         my %current=();  
         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}=
       join('___;___',%current);
       } else {
           return 'Could not access course data';
       }
   # ------------------------------------------------------ Get current from cache
       } else {
           %current=split(/\_\_\_\;\_\_\_/,
          $updatedata{$ENV{'request.course.fn'}.'_'.$stype});
       }
   # -------------------- Find discrepancies between the course row table and this
   #
           my %f=&getformulas($safeeval);
           my $changed=0;
   
         my $maxrow=0;          my $maxrow=0;
         my %existing=();          my %existing=();
   
 # ----------------------------------------------------------- Now obsolete rows  # ----------------------------------------------------------- Now obsolete rows
  map {   foreach (keys(%f)) {
     if ($_=~/^A(\d+)/) {      if ($_=~/^A(\d+)/) {
                 $maxrow=($1>$maxrow)?$1:$maxrow;                  $maxrow=($1>$maxrow)?$1:$maxrow;
                 $existing{$f{$_}}=1;                  my ($usy,$ufn)=split(/\_\_\&\&\&\_\_/,$f{$_});
  unless ((defined($current{$f{$_}})) || (!$1)) {                  $existing{$usy}=1;
    unless ((defined($current{$usy})) || (!$1)) {
    $f{$_}='!!! Obsolete';     $f{$_}='!!! Obsolete';
                    $changed=1;                     $changed=1;
           } elsif ($ufn) {
       $current{$usy}
                          =~s/assesscalc\?usymb\=/assesscalc\?ufn\=$ufn\&usymb\=/;
                 }                  }
             }              }
         } keys %f;          }
   
 # -------------------------------------------------------- New and unknown keys  # -------------------------------------------------------- New and unknown keys
             
         map {          foreach (keys(%current)) {
             unless ($existing{$_}) {              unless ($existing{$_}) {
  $changed=1;   $changed=1;
                 $maxrow++;                  $maxrow++;
                 $f{'A'.$maxrow}=$_;                  $f{'A'.$maxrow}=$_;
             }              }
         } keys %current;                  }
            
         if ($changed) { &setformulas($safeeval,%f); }          if ($changed) { &setformulas($safeeval,%f); }
   
         &setmaxrow($safeeval,$maxrow);          &setmaxrow($safeeval,$maxrow);
         &setrowlabels($safeeval,%current);          &setrowlabels($safeeval,%current);
    
     } else {          undef %current;
         return 'Could not access course data';          undef %existing;
     }  
 }  }
   
 # ------------------------------------------------ Load data for one assessment  # ------------------------------------------------ Load data for one assessment
   
 sub rowazstudent {  sub loadstudent {
     my $safeeval=shift;      my $safeeval=shift;
     my %c=();      my %c=();
     my %f=&getformulas($safeeval);      my %f=&getformulas($safeeval);
     map {      $cachedassess=&getuname($safeeval).':'.&getudom($safeeval);
       %cachedstores=();
       {
         my $reply=&Apache::lonnet::reply('dump:'.&getudom($safeeval).':'.
                                                  &getuname($safeeval).':'.
                                                  &getcid($safeeval),
                                                  &getuhome($safeeval));
         unless ($reply=~/^error\:/) {
    foreach ( split(/\&/,$reply)) {
               my ($name,$value)=split(/\=/,$_);
               $cachedstores{&Apache::lonnet::unescape($name)}=
                     &Apache::lonnet::unescape($value);
    }
         }
       }
       my @assessdata=();
       foreach (keys(%f)) {
  if ($_=~/^A(\d+)/) {   if ($_=~/^A(\d+)/) {
    my $row=$1;     my $row=$1;
            unless ($f{$_}=~/^\!/) {             unless (($f{$_}=~/^\!/) || ($row==0)) {
               my @assessdata=split(/\_\_\_\;\_\_\_/,        my ($usy,$ufn)=split(/\_\_\&\&\&\_\_/,$f{$_});
                              &Apache::lonnet::ssi(        @assessdata=&exportsheet(&getuname($safeeval),
                        '/adm/assesscalc',('utarget' => 'export',                                         &getudom($safeeval),
                                           'uname'   => $uname,                                         'assesscalc',$usy,$ufn);
                                           'udom'    => $udom,  
                   'usymb'   => $f{$_})));  
               my $index=0;                my $index=0;
               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') {
                   if ($assessdata[$index]) {                    if ($assessdata[$index]) {
      $c{$_.$row}=$assessdata[$index];       my $col=$_;
                      unless ($_ eq 'A') {        if ($assessdata[$index]=~/\D/) {
  $f{$_.$row}='import';                           $c{$col.$row}="'".$assessdata[$index]."'";
         } else {
            $c{$col.$row}=$assessdata[$index];
        }
                        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;      }
       $cachedassess='';
       undef %cachedstores;
     &setformulas($safeeval,%f);      &setformulas($safeeval,%f);
     &setconstants($safeeval,%c);      &setconstants($safeeval,%c);
 }  }
   
 # --------------------------------------------------- Load data for one student  # --------------------------------------------------- Load data for one student
   
 sub rowazclass {  sub loadcourse {
     my $safeeval=shift;      my ($safeeval,$r)=@_;
     my %c=();      my %c=();
     my %f=&getformulas($safeeval);      my %f=&getformulas($safeeval);
     map {      my $total=0;
       foreach (keys(%f)) {
    if ($_=~/^A(\d+)/) {
       unless ($f{$_}=~/^\!/) { $total++; }
           }
       }
       my $now=0;
       my $since=time;
       $r->print(<<ENDPOP);
   <script>
       popwin=open('','popwin','width=400,height=100');
       popwin.document.writeln('<html><body bgcolor="#FFFFFF">'+
         '<h3>Spreadsheet Calculation Progress</h3>'+
         '<form name=popremain>'+
         '<input type=text size=35 name=remaining value=Starting></form>'+
         '</body></html>');
       popwin.document.close();
   </script>
   ENDPOP
       $r->rflush();
       foreach (keys(%f)) {
  if ($_=~/^A(\d+)/) {   if ($_=~/^A(\d+)/) {
    my $row=$1;     my $row=$1;
            unless ($f{$_}=~/^\!/) {             unless (($f{$_}=~/^\!/)  || ($row==0)) {
       my ($tname,$tdom)=split(/\:/,$_);        my @studentdata=&exportsheet(split(/\:/,$f{$_}),
               my @assessdata=split(/\_\_\_\;\_\_\_/,                                             'studentcalc');
                              &Apache::lonnet::ssi(                undef %userrdatas;
                       '/adm/studentcalc',('utarget' => 'export',                $now++;
                                           'uname'   => $tname,                $r->print('<script>popwin.document.popremain.remaining.value="'.
                                           'udom'    => $tdom)));                    $now.'/'.$total.': '.int((time-$since)/$now*($total-$now)).
                           ' secs remaining";</script>');
                 $r->rflush(); 
   
               my $index=0;                my $index=0;
               map {               foreach ('A','B','C','D','E','F','G','H','I','J','K','L','M',
                   if ($assessdata[$index]) {                'N','O','P','Q','R','S','T','U','V','W','X','Y','Z') {
      $c{$_.$row}=$assessdata[$index];                    if ($studentdata[$index]) {
                      unless ($_ eq 'A') {        my $col=$_;
  $f{$_.$row}='import';       if ($studentdata[$index]=~/\D/) {
                            $c{$col.$row}="'".$studentdata[$index]."'";
         } else {
            $c{$col.$row}=$studentdata[$index];
        }
                        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->rflush(); 
 }  }
   
 # ------------------------------------------------ Load data for one assessment  # ------------------------------------------------ Load data for one assessment
   
 sub rowaassess {  sub loadassessment {
     my ($safeeval,$symb)=@_;      my $safeeval=shift;
     my $uhome=&Apache::lonnet::homeserver($uname,$udom);  
       my $uhome=&getuhome($safeeval);
       my $uname=&getuname($safeeval);
       my $udom=&getudom($safeeval);
       my $symb=&getusymb($safeeval);
       my $cid=&getcid($safeeval);
       my $cnum=&getcnum($safeeval);
       my $cdom=&getcdom($safeeval);
       my $chome=&getchome($safeeval);
   
     my $namespace;      my $namespace;
     unless ($namespace=$ENV{'request.course.id'}) { return ''; }      unless ($namespace=$cid) { return ''; }
   
 # ----------------------------------------------------------- Get stored values  # ----------------------------------------------------------- Get stored values
   
      my %returnhash=();
   
      if ($cachedassess eq $uname.':'.$udom) {
   #
   # get data out of the dumped stores
   # 
   
          my $version=$cachedstores{'version:'.$symb};
          my $scope;
          for ($scope=1;$scope<=$version;$scope++) {
              foreach (split(/\:/,$cachedstores{$scope.':keys:'.$symb})) {
                  $returnhash{$_}=$cachedstores{$scope.':'.$symb.':'.$_};
              } 
          }
   
      } else {
   #
   # restore individual
   #
   
     my $answer=&Apache::lonnet::reply(      my $answer=&Apache::lonnet::reply(
        "restore:$udom:$uname:".         "restore:$udom:$uname:".
        &Apache::lonnet::escape($namespace).":".         &Apache::lonnet::escape($namespace).":".
        &Apache::lonnet::escape($symb),$uhome);         &Apache::lonnet::escape($symb),$uhome);
     my %returnhash=();      foreach (split(/\&/,$answer)) {
     map {  
  my ($name,$value)=split(/\=/,$_);   my ($name,$value)=split(/\=/,$_);
         $returnhash{&Apache::lonnet::unescape($name)}=          $returnhash{&Apache::lonnet::unescape($name)}=
                     &Apache::lonnet::unescape($value);                      &Apache::lonnet::unescape($value);
     } split(/\&/,$answer);      }
     my $version;      my $version;
     for ($version=1;$version<=$returnhash{'version'};$version++) {      for ($version=1;$version<=$returnhash{'version'};$version++) {
        map {         foreach (split(/\:/,$returnhash{$version.':keys'})) {
           $returnhash{$_}=$returnhash{$version.':'.$_};            $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;
   
       foreach (@oldkeys) {
           my $name=$_;
           my $value=$returnhash{$_};
           delete $returnhash{$_};
           $name=~s/\_/\./g;
           $returnhash{$name}=$value;
       }
   
 # ---------------------------- initialize coursedata and userdata for this user  # ---------------------------- initialize coursedata and userdata for this user
     %courseopt=();      undef %courseopt;
     %useropt=();      undef %useropt;
     my $uhome=&Apache::lonnet::homeserver($uname,$udom);  
       my $userprefix=$uname.'_'.$udom.'_';
   
     unless ($uhome eq 'no_host') {       unless ($uhome eq 'no_host') { 
 # -------------------------------------------------------------- Get coursedata  # -------------------------------------------------------------- Get coursedata
       unless        unless
         ((time-$courserdatas{$ENV{'request.course.id'}.'.last_cache'})<120) {          ((time-$courserdatas{$cid.'.last_cache'})<240) {
          my $reply=&Apache::lonnet::reply('dump:'.           my $reply=&Apache::lonnet::reply('dump:'.$cdom.':'.$cnum.
               $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}.':'.                ':resourcedata',$chome);
               $ENV{'course.'.$ENV{'request.course.id'}.'.num'}.':resourcedata',  
               $ENV{'course.'.$ENV{'request.course.id'}.'.home'});  
          if ($reply!~/^error\:/) {           if ($reply!~/^error\:/) {
             $courserdatas{$ENV{'request.course.id'}}=$reply;              $courserdatas{$cid}=$reply;
             $courserdatas{$ENV{'request.course.id'}.'.last_cache'}=time;              $courserdatas{$cid.'.last_cache'}=time;
          }           }
       }        }
       map {        foreach (split(/\&/,$courserdatas{$cid})) {
          my ($name,$value)=split(/\=/,$_);           my ($name,$value)=split(/\=/,$_);
          $courseopt{&Apache::lonnet::unescape($name)}=           $courseopt{$userprefix.&Apache::lonnet::unescape($name)}=
                     &Apache::lonnet::unescape($value);                        &Apache::lonnet::unescape($value);  
       } split(/\&/,$courserdatas{$ENV{'request.course.id'}});        }
 # --------------------------------------------------- Get userdata (if present)  # --------------------------------------------------- Get userdata (if present)
       unless        unless
         ((time-$userrdatas{$uname.'___'.$udom.'.last_cache'})<120) {          ((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\:/) {
Line 1258  sub rowaassess { Line 1901  sub rowaassess {
      $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{&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 (used parmval)  # ----------------- now courseopt, useropt initialized for this user and course
   # (used by parmval)
   
     my %c=();  #
   # Load keys for this assessment only
   #
       my %thisassess=();
       my ($symap,$syid,$srcf)=split(/\_\_\_/,$symb);
       
       foreach (split(/\,/,&Apache::lonnet::metadata($srcf,'keys'))) {
           $thisassess{$_}=1;
       } 
   #
   # Load parameters
   #
      my %c=();
   
      if (tie(%parmhash,'GDBM_File',
              &getcfn($safeeval).'_parms.db',&GDBM_READER,0640)) {
     my %f=&getformulas($safeeval);      my %f=&getformulas($safeeval);
     map {      foreach (keys(%f))  {
  if ($_=~/^A/) {   if ($_=~/^A/) {
             unless ($f{$_}=~/^\!/) {              unless ($f{$_}=~/^\!/) {
         if ($f{$_}=~/^parameter/) {          if ($f{$_}=~/^parameter/) {
           $c{$_}=&parmval($f{$_},$safeeval);   if ($thisassess{$f{$_}}) {
                     my $val=&parmval($f{$_},$safeeval);
                     $c{$_}=$val;
                     $c{$f{$_}}=$val;
           }
        } else {         } else {
   my $key=$f{$_};    my $key=$f{$_};
                     my $ckey=$key;
                   $key=~s/^stores\_/resource\./;                    $key=~s/^stores\_/resource\./;
                   $key=~s/\_/\./;                    $key=~s/\_/\./g;
            $c{$_}=$returnhash{$key};             $c{$_}=$returnhash{$key};
                     $c{$ckey}=$returnhash{$key};
        }         }
    }     }
         }          }
     } keys %f;      }
       untie(%parmhash);
     &setconstants($safeeval,%c);     }
      &setconstants($safeeval,%c);
 }  }
   
 # --------------------------------------------------------- Various form fields  # --------------------------------------------------------- Various form fields
Line 1302  sub hiddenfield { Line 1968  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 1330  sub updatesheet { Line 1996  sub updatesheet {
 # Import the data for rows  # Import the data for rows
 #  #
   
 sub loadrows() {  sub loadrows {
     my $safeeval=shift;      my ($safeeval,$r)=@_;
     my $stype=&gettype($safeeval);      my $stype=&gettype($safeeval);
     if ($stype eq 'classcalc') {      if ($stype eq 'classcalc') {
  &loadcourse($thissheet);   &loadcourse($safeeval,$r);
     } elsif ($stype eq 'studentcalc') {      } elsif ($stype eq 'studentcalc') {
         &loadstudent($thissheet);          &loadstudent($safeeval);
     } else {      } else {
         &loadassessment($thissheet);          &loadassessment($safeeval);
     }      }
 }  }
   
   # ======================================================= 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 $thissheet=($uname,$udom,$stype,$usymb);   my @exportarr=();
   
    if (($usymb=~/^\_(\w+)/) && (!$fn)) {
       $fn='default_'.$1;
    }
   
   #
   # Check if cached
   #
   
    my $key=$uname.':'.$udom.':'.$stype.':'.$usymb;
    my $found='';
   
    if ($oldsheets{$key}) {
        foreach (split(/\_\_\_\&\_\_\_/,$oldsheets{$key})) {
            my ($name,$value)=split(/\_\_\_\=\_\_\_/,$_);
            if ($name eq $fn) {
        $found=$value;
            }
        }
    }
   
    unless ($found) {
        &cachedssheets($uname,$udom,&Apache::lonnet::homeserver($uname,$udom));
        if ($oldsheets{$key}) {
    foreach (split(/\_\_\_\&\_\_\_/,$oldsheets{$key})) {
               my ($name,$value)=split(/\_\_\_\=\_\_\_/,$_);
               if ($name eq $fn) {
           $found=$value;
               }
           } 
        }
    }
   #
   # 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);      &readsheet($thissheet,$fn);
     &updatesheet($thissheet);      &updatesheet($thissheet);
     &loadrows($thissheet);      &loadrows($thissheet);
     &calcsheet($thissheet);      &calcsheet($thissheet); 
     return &exportdata($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;
 }  }
   # ============================================================ Expiration Dates
   #
   # Load previously cached student spreadsheets for this course
   #
   
   sub expirationdates {
       undef %expiredates;
       my $cid=$ENV{'request.course.id'};
       my $reply=&Apache::lonnet::reply('dump:'.
        $ENV{'course.'.$cid.'.domain'}.':'.
                                        $ENV{'course.'.$cid.'.num'}.
        ':nohist_expirationdates',
                                        $ENV{'course.'.$cid.'.home'});
       unless ($reply=~/^error\:/) {
    foreach (split(/\&/,$reply)) {
               my ($name,$value)=split(/\=/,$_);
               $expiredates{&Apache::lonnet::unescape($name)}
                           =&Apache::lonnet::unescape($value);
           }
       }
   }
   
   # ===================================================== Calculated sheets cache
   #
   # Load previously cached student spreadsheets for this course
   #
   
   sub cachedcsheets {
       my $cid=$ENV{'request.course.id'};
       my $reply=&Apache::lonnet::reply('dump:'.
        $ENV{'course.'.$cid.'.domain'}.':'.
                                        $ENV{'course.'.$cid.'.num'}.
        ':nohist_calculatedsheets',
                                        $ENV{'course.'.$cid.'.home'});
       unless ($reply=~/^error\:/) {
    foreach ( split(/\&/,$reply)) {
               my ($name,$value)=split(/\=/,$_);
               $oldsheets{&Apache::lonnet::unescape($name)}
                         =&Apache::lonnet::unescape($value);
           }
       }
   }
   
   # ===================================================== Calculated sheets cache
   #
   # Load previously cached assessment spreadsheets for this student
   #
   
   sub cachedssheets {
     my ($sname,$sdom,$shome)=@_;
     unless (($loadedcaches{$sname.'_'.$sdom}) || ($shome eq 'no_host')) {
       my $cid=$ENV{'request.course.id'};
       my $reply=&Apache::lonnet::reply('dump:'.$sdom.':'.$sname.
                ':nohist_calculatedsheets_'.
                                         $ENV{'request.course.id'},
                                        $shome);
       unless ($reply=~/^error\:/) {
    foreach ( split(/\&/,$reply)) {
               my ($name,$value)=split(/\=/,$_);
               $oldsheets{&Apache::lonnet::unescape($name)}
                         =&Apache::lonnet::unescape($value);
           }
       }
       $loadedcaches{$sname.'_'.$sdom}=1;
     }
   }
   
   # ===================================================== Calculated sheets cache
   #
   # Load previously cached assessment spreadsheets for this student
   #
   
 # ================================================================ Main handler  # ================================================================ Main handler
 #  #
Line 1375  sub handler { Line 2255  sub handler {
   
 # ---------------------------------------------------- Global directory configs  # ---------------------------------------------------- Global directory configs
   
 $includedir=r->dir_config('lonIncludes');  $includedir=$r->dir_config('lonIncludes');
 $tmpdir=$r->dir_config('lonDaemons').'/tmp/';  $tmpdir=$r->dir_config('lonDaemons').'/tmp/';
   
 # ----------------------------------------------------- Needs to be in a course  # ----------------------------------------------------- Needs to be in a course
   
   if (($ENV{'request.course.fn'}) ||     if ($ENV{'request.course.fn'}) { 
       ($ENV{'request.state'} eq 'construct')) {   
   
 # --------------------------- Get query string for limited number of parameters  # --------------------------- Get query string for limited number of parameters
   
     map {      foreach (split(/&/,$ENV{'QUERY_STRING'})) {
        my ($name, $value) = split(/=/,$_);         my ($name, $value) = split(/=/,$_);
        $value =~ tr/+/ /;         $value =~ tr/+/ /;
        $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;         $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
Line 1395  $tmpdir=$r->dir_config('lonDaemons').'/t Line 2274  $tmpdir=$r->dir_config('lonDaemons').'/t
               $ENV{'form.'.$name}=$value;                $ENV{'form.'.$name}=$value;
    }     }
        }         }
     } (split(/&/,$ENV{'QUERY_STRING'}));      }
   
       if (($ENV{'form.usymb'}=~/^\_(\w+)/) && (!$ENV{'form.ufn'})) {
    $ENV{'form.ufn'}='default_'.$1;
       }
   
   # -------------------------------------- Interactive loading of specific sheet?
       if (($ENV{'form.load'}) && ($ENV{'form.loadthissheet'} ne 'Default')) {
    $ENV{'form.ufn'}=$ENV{'form.loadthissheet'};
       }
 # ------------------------------------------- Nothing there? Must be login user  # ------------------------------------------- Nothing there? Must be login user
   
       my $aname;
       my $adom;
   
     unless ($ENV{'form.uname'}) {      unless ($ENV{'form.uname'}) {
  $uname=$ENV{'user.name'};   $aname=$ENV{'user.name'};
         $udom=$ENV{'user.domain'};          $adom=$ENV{'user.domain'};
     } else {      } else {
         $uname=$ENV{'form.uname'};          $aname=$ENV{'form.uname'};
         $udom=$ENV{'form.udom'};          $adom=$ENV{'form.udom'};
     }      }
 # ----------------------------------------------------------- Change of target?  
   
     my $reroute=($ENV{'form.utarget'} eq 'export');  
   
 # ------------------------------------------------------------------- Open page  # ------------------------------------------------------------------- Open page
   
Line 1418  $tmpdir=$r->dir_config('lonDaemons').'/t Line 2306  $tmpdir=$r->dir_config('lonDaemons').'/t
   
 # --------------------------------------------------------------- Screen output  # --------------------------------------------------------------- Screen output
   
   unless ($reroute) {  
     $r->print('<html><head><title>LON-CAPA Spreadsheet</title>');      $r->print('<html><head><title>LON-CAPA Spreadsheet</title>');
     $r->print(<<ENDSCRIPT);      $r->print(<<ENDSCRIPT);
 <script language="JavaScript">  <script language="JavaScript">
Line 1432  $tmpdir=$r->dir_config('lonDaemons').'/t Line 2319  $tmpdir=$r->dir_config('lonDaemons').'/t
         }          }
     }      }
   
       function changesheet(cn) {
    document.sheet.unewfield.value=cn;
           document.sheet.unewformula.value='changesheet';
           document.sheet.submit();
       }
   
 </script>  </script>
 ENDSCRIPT  ENDSCRIPT
     $r->print('</head><body bgcolor="#FFFFFF">'.      $r->print('</head><body bgcolor="#FFFFFF">'.
Line 1443  ENDSCRIPT Line 2336  ENDSCRIPT
        &hiddenfield('usymb',$ENV{'form.usymb'}).         &hiddenfield('usymb',$ENV{'form.usymb'}).
        &hiddenfield('unewfield','').         &hiddenfield('unewfield','').
        &hiddenfield('unewformula',''));         &hiddenfield('unewformula',''));
   }  
   # ---------------------- Make sure that this gets out, even if user hits "stop"
   
     $r->rflush();      $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  # ---------------------------------------- Read new sheet or modified worksheet
   
     my $sheetone=initsheet();  
     $r->uri=~/\/(\w+)$/;      $r->uri=~/\/(\w+)$/;
     &settype($sheetone,$1);  
       my $asheet=&makenewsheet($aname,$adom,$1,$ENV{'form.usymb'});
   
   # ------------------------ If a new formula had been entered, go from work copy
   
     if ($ENV{'form.unewfield'}) {      if ($ENV{'form.unewfield'}) {
         $r->print('<h2>Modified Workcopy</h2>');          $r->print('<h2>Modified Workcopy</h2>');
         $ENV{'form.unewformula'}=~s/\'/\"/g;          $ENV{'form.unewformula'}=~s/\'/\"/g;
         $r->print('<p>New formula: '.$ENV{'form.unewfield'}.'='.          $r->print('<p>New formula: '.$ENV{'form.unewfield'}.'='.
                   $ENV{'form.unewformula'}.'<p>');                    $ENV{'form.unewformula'}.'<p>');
         &setfilename($sheetone,$ENV{'form.ufn'});          &setfilename($asheet,$ENV{'form.ufn'});
  &tmpread($sheetone,$r->dir_config('lonDaemons').'/tmp/',   &tmpread($asheet,
                  $ENV{'form.usymb'},  
                  $ENV{'form.unewfield'},$ENV{'form.unewformula'});                   $ENV{'form.unewfield'},$ENV{'form.unewformula'});
     } elsif ($ENV{'form.saveas'}) {  
         &setfilename($sheetone,$ENV{'form.ufn'});       } elsif ($ENV{'form.saveas'}) {
  &tmpread($sheetone,,          &setfilename($asheet,$ENV{'form.ufn'});
                  $ENV{'form.usymb'});   &tmpread($asheet);
     } else {      } else {
         unless ($ENV{'form.ufn'}) {          &readsheet($asheet,$ENV{'form.ufn'});
     }      }
   
   if (&gettype($sheetone) eq 'classcalc') {  # -------------------------------------------------- Print out user information
 # ---------------------------------- For course view: get courselist and update  
        &updatestudentrows($sheetone);  
   } else {  
 # ----------------- For assessment and student: See if all import rows uptodate  
   
     if (tie(%parmhash,'GDBM_File',      unless (&gettype($asheet) eq 'classcalc') {
        $ENV{'request.course.fn'}.'_parms.db',&GDBM_READER,0640)) {          $r->print('<p><b>User:</b> '.&getuname($asheet).
        $csec=&Apache::lonnet::usection($udom,$uname,$ENV{'request.course.id'});                    '<br><b>Domain:</b> '.&getudom($asheet));
        if ($csec eq '-1') {          if (&getcsec($asheet) eq '-1') {
           $r->print('<h3><font color=red>'.             $r->print('<h3><font color=red>'.
    "User '$uname' at domain '$udom' not a student in this course</font></h3>");                       'Not a student in this course</font></h3>');
        }          } else {
        &updaterows($sheetone);             $r->print('<br><b>Section/Group:</b> '.&getcsec($asheet));
        untie(%parmhash);          }
    } else {          if ($ENV{'form.usymb'}) {
        $r->print('<h3><font color=red>'.             $r->print('<br><b>Assessment:</b> <tt>'.$ENV{'form.usymb'}.'</tt>');
    'Could not initialize import fields (not in a course)</font></h3>');  
    }  
  }  
 # ---------------------------------------------------- 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($sheetone);  
             &setfilename($sheetone,$fname);  
             $ENV{'form.ufn'}=$fname;  
             my $reply=&writesheet($sheetone);  
             unless ($reroute) {  
  $r->print('<p>Saving spreadsheet: '.$reply.'<p>');  
             }  
             if ($ENV{'form.makedefufn'}) {  
                 my $reply=&Apache::lonnet::reply('put:'.  
                      $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}.':'.  
                      $ENV{'course.'.$ENV{'request.course.id'}.'.num'}.  
                      ':environment:spreadsheet_default_'.  
                      &gettype($sheetone).'='.  
                      &Apache::lonnet::escape($fname),  
                      $ENV{'course.'.$ENV{'request.course.id'}.'.home'});  
                unless ($reroute) {  
            $r->print('<p>Making default spreadsheet: '.$reply.'<p>');  
                }  
             }  
         }          }
     }      }
 # ------------------------------------------------ Write the modified worksheet  
   
    &tmpwrite($sheetone,$r->dir_config('lonDaemons').'/tmp/',  # ---------------------------------------------------------------- Course title
               $ENV{'form.usymb'});  
       $r->print('<h1>'.
               $ENV{'course.'.$ENV{'request.course.id'}.'.description'}.
                '</h1><h3>'.localtime().'</h3>');
   
   # ---------------------------------------------------- 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
   
   
 # ----------------------------------------------------- Print user, course, etc  
    unless ($reroute) {  
     if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) {      if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) {
         my $fname=$ENV{'form.ufn'};          my $fname=$ENV{'form.ufn'};
         $fname=~s/\_[^\_]+$//;          $fname=~s/\_[^\_]+$//;
Line 1529  ENDSCRIPT Line 2442  ENDSCRIPT
               '<input type=text size=20 name=newfn value="'.$fname.                '<input type=text size=20 name=newfn value="'.$fname.
               '"> (make default: <input type=checkbox name="makedefufn">)<p>');                '"> (make default: <input type=checkbox name="makedefufn">)<p>');
     }      }
     $r->print(&hiddenfield('ufn',$ENV{'form.ufn'}));  
     unless (&gettype($sheetone) eq 'classcalc') {      $r->print(&hiddenfield('ufn',&getfilename($asheet)));
         $r->print('<br><b>User:</b> '.$uname.'<br><b>Domain:</b> '.$udom);  
   # ----------------------------------------------------------------- Load dialog
       if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) {
    $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'));
           }
     }      }
     $r->print('<h1>'.  
             $ENV{'course.'.$ENV{'request.course.id'}.'.description'}.'</h1>');  # --------------------------------------------------------------- Cached sheets
     if ($csec) {  
        $r->print('<h3>Group/Section: '.$csec.'</h3>');      &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));
     }      }
    }  
 # -------------------------------------------------------- Import and calculate  
   
     if (&gettype($sheetone) eq 'assesscalc') {  # ----------------------------------------------------- Update sheet, load rows
  &rowaassess($sheetone,$ENV{'form.usymb'});  
     } elsif  (&gettype($sheetone) eq 'studentcalc') {      $r->print("Loaded sheet(s), updating rows ...<br>\n");
  &rowazstudent($sheetone);      $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>');
   
      &tmpwrite($asheet);
   
       if (&gettype($asheet) eq 'studentcalc') {
    $r->print('<br>Show rows with empty A column: ');
     } else {      } else {
         &rowazclass($sheetone);          $r->print('<br>Show empty rows: ');
       } 
   
       $r->print(&hiddenfield('userselhidden','true').
                '<input type=checkbox name=showall onClick="submit()"');
   
       if ($ENV{'form.showall'}) { 
          $r->print(' checked'); 
       } else {
    unless ($ENV{'form.userselhidden'}) {
              unless 
    ($ENV{'course.'.$ENV{'request.course.id'}.'.hideemptyrows'} eq 'yes') {
             $r->print(' checked');
             $ENV{'form.showall'}=1;
              }
          }
     }      }
     my $calcoutput=&calcsheet($sheetone);      $r->print('>');
     unless ($reroute) {      if (&gettype($asheet) eq 'classcalc') {
        $r->print('<h3><font color=red>'.$calcoutput.'</h3></font>');         $r->print(
      ' Output CSV format: <input type=checkbox name=showcsv onClick="submit()"');
          if ($ENV{'form.showcsv'}) { $r->print(' checked'); }
          $r->print('>');
     }      }
   # ------------------------------------------------------------- Print out sheet
   
 # ------------------------------------------------------- Print or export sheet      &outsheet($r,$asheet);
    unless ($reroute) {     
     &outsheet($r,$sheetone);  
   
     $r->print('</form></body></html>');      $r->print('</form></body></html>');
   } else {  
      $r->print(&exportrow($sheetone));  
   }  
 # ------------------------------------------------------------------------ Done  # ------------------------------------------------------------------------ Done
   } else {    } else {
 # ----------------------------- Not in a course, or not allowed to modify parms  # ----------------------------- Not in a course, or not allowed to modify parms
Line 1570  ENDSCRIPT Line 2561  ENDSCRIPT
   }    }
     return OK;      return OK;
   
     $bombnomatterwhat='yes';  
 }  }
   
 1;  1;
Line 1578  __END__ Line 2568  __END__
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   

Removed from v.1.28  
changed lines
  Added in v.1.81


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