Diff for /loncom/interface/Attic/lonspreadsheet.pm between versions 1.83 and 1.112

version 1.83, 2002/04/10 15:30:13 version 1.112, 2002/09/27 20:40:19
Line 26 Line 26
 # 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,  
 # 12/08,12/09,12/11,12/12,12/15,12/16,12/18,12/19,12/30,  
 # 01/01/01,02/01,03/01,19/01,20/01,22/01,  
 # 03/05,03/08,03/10,03/12,03/13,03/15,03/17,  
 # 03/19,03/20,03/21,03/27,04/05,04/09,  
 # 07/09,07/14,07/21,09/01,09/10,9/11,9/12,9/13,9/14,9/17,  
 # 10/16,10/17,10/20,11/05,11/28,12/27 Gerd Kortemeyer  
 # 01/14/02 Matthew  
 # 02/04/02 Matthew  
   
 # POD required stuff:  # POD required stuff:
   
 =head1 NAME  =head1 NAME
Line 54  not the grades of their peers.  The spre Line 44  not the grades of their peers.  The spre
 offering the ability to use Perl code to manipulate data, as well as many  offering the ability to use Perl code to manipulate data, as well as many
 built-in functions.  built-in functions.
   
   
 =head2 Functions available to user of lonspreadsheet  =head2 Functions available to user of lonspreadsheet
   
 =over 4  =over 4
Line 71  use Apache::lonnet; Line 60  use Apache::lonnet;
 use Apache::Constants qw(:common :http);  use Apache::Constants qw(:common :http);
 use GDBM_File;  use GDBM_File;
 use HTML::TokeParser;  use HTML::TokeParser;
   use Apache::lonhtmlcommon;
 #  #
 # Caches for previously calculated spreadsheets  # Caches for previously calculated spreadsheets
 #  #
Line 106  my %courseopt; Line 95  my %courseopt;
 my %useropt;  my %useropt;
 my %parmhash;  my %parmhash;
   
   #
   # Some hashes for stats on timing and performance
   #
   
   my %starttimes;
   my %usedtimes;
   my %numbertimes;
   
 # Stuff that only the screen handler can know  # Stuff that only the screen handler can know
   
 my $includedir;  my $includedir;
Line 122  sub initsheet { Line 119  sub initsheet {
     $safeeval->permit("sort");      $safeeval->permit("sort");
     $safeeval->deny(":base_io");      $safeeval->deny(":base_io");
     $safehole->wrap(\&Apache::lonnet::EXT,$safeeval,'&EXT');      $safehole->wrap(\&Apache::lonnet::EXT,$safeeval,'&EXT');
       $safeeval->share('$@');
     my $code=<<'ENDDEFS';      my $code=<<'ENDDEFS';
 # ---------------------------------------------------- Inside of the safe space  # ---------------------------------------------------- Inside of the safe space
   
Line 133  sub initsheet { Line 131  sub initsheet {
 # rl: row label  # rl: row label
 # os: other spreadsheets (for student spreadsheet only)  # os: other spreadsheets (for student spreadsheet only)
   
 undef %v;   undef %sheet_values; 
 undef %t;  undef %t;
 undef %f;  undef %f;
 undef %c;  undef %c;
 undef %rl;  undef %rowlabel;
 undef @os;  undef @os;
   
 $maxrow=0;  $maxrow = 0;
 $sheettype='';  $sheettype = '';
   
 # filename/reference of the sheet  # filename/reference of the sheet
   $filename = '';
 $filename='';  
   
 # user data  # user data
 $uname='';  $uname = '';
 $uhome='';  $uhome = '';
 $udom='';  $udom  = '';
   
 # course data  # course data
   
 $csec='';  $csec = '';
 $chome='';  $chome= '';
 $cnum='';  $cnum = '';
 $cdom='';  $cdom = '';
 $cid='';  $cid  = '';
 $cfn='';  $coursefilename  = '';
   
 # symb  # symb
   
 $usymb='';  $usymb = '';
   
   # error messages
   $errormsg = '';
   
 sub mask {  sub mask {
     my ($lower,$upper)=@_;      my ($lower,$upper)=@_;
Line 372  sub CDLHASH { Line 372  sub CDLHASH {
     if ($key =~ /[A-z](\-[A-z])?\d+(\-\d+)?/) {      if ($key =~ /[A-z](\-[A-z])?\d+(\-\d+)?/) {
  my $keymask = &mask($key);   my $keymask = &mask($key);
  # Assume the keys are addresses   # Assume the keys are addresses
  my @Temp = grep /$keymask/,keys(%v);   my @Temp = grep /$keymask/,keys(%sheet_values);
  @Keys = $v{@Temp};   @Keys = $sheet_values{@Temp};
     } else {      } else {
  $Keys[0]= $key;   $Keys[0]= $key;
     }      }
Line 384  sub CDLHASH { Line 384  sub CDLHASH {
     @Keys = @Temp;      @Keys = @Temp;
     if ($value =~ /[A-z](\-[A-z])?\d+(\-\d+)?/) {      if ($value =~ /[A-z](\-[A-z])?\d+(\-\d+)?/) {
  my $valmask = &mask($value);   my $valmask = &mask($value);
  my @Temp = grep /$valmask/,keys(%v);   my @Temp = grep /$valmask/,keys(%sheet_values);
  @Values =$v{@Temp};   @Values =$sheet_values{@Temp};
     } else {      } else {
  $Values[0]= $value;   $Values[0]= $value;
     }      }
Line 419  sub GETHASH { Line 419  sub GETHASH {
  $index = 0;   $index = 0;
     }      }
     if ($key =~ /^[A-z]\d+$/) {      if ($key =~ /^[A-z]\d+$/) {
  $key = $v{$key};   $key = $sheet_values{$key};
     }      }
     return $hashes{$name}->{$key}->[$index];      return $hashes{$name}->{$key}->[$index];
 }  }
Line 476  sub HASH { Line 476  sub HASH {
     if ($key =~ /[A-z](\-[A-z])?\d+(\-\d+)?/) {      if ($key =~ /[A-z](\-[A-z])?\d+(\-\d+)?/) {
  my $keymask = &mask($key);   my $keymask = &mask($key);
  # Assume the keys are addresses   # Assume the keys are addresses
  my @Temp = grep /$keymask/,keys(%v);   my @Temp = grep /$keymask/,keys(%sheet_values);
  @Keys = $v{@Temp};   @Keys = $sheet_values{@Temp};
     } else {      } else {
  $Keys[0]= $key;   $Keys[0]= $key;
     }      }
Line 489  sub HASH { Line 489  sub HASH {
     # Check to see if we have multiple $value(s)       # Check to see if we have multiple $value(s) 
     if ($value =~ /[A-z](\-[A-z])?\d+(\-\d+)?/) {      if ($value =~ /[A-z](\-[A-z])?\d+(\-\d+)?/) {
  my $valmask = &mask($value);   my $valmask = &mask($value);
  my @Temp = grep /$valmask/,keys(%v);   my @Temp = grep /$valmask/,keys(%sheet_values);
  @Values =$v{@Temp};   @Values =$sheet_values{@Temp};
     } else {      } else {
  $Values[0]= $value;   $Values[0]= $value;
     }      }
Line 508  sub HASH { Line 508  sub HASH {
     return $Values[-1];      return $Values[-1];
 }  }
   
   #-------------------------------------------------------
   
   =item NUM(range)
   
   returns the number of items in the range.
   
   =cut
   
   #-------------------------------------------------------
 sub NUM {  sub NUM {
     my $mask=mask(@_);      my $mask=mask(@_);
     my $num= $#{@{grep(/$mask/,keys(%v))}}+1;      my $num= $#{@{grep(/$mask/,keys(%sheet_values))}}+1;
     return $num;         return $num;   
 }  }
   
Line 518  sub BIN { Line 527  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;
     foreach (grep /$mask/,keys(%v)) {      foreach (grep /$mask/,keys(%sheet_values)) {
         if (($v{$_}>=$low) && ($v{$_}<=$high)) {          if (($sheet_values{$_}>=$low) && ($sheet_values{$_}<=$high)) {
             $num++;              $num++;
         }          }
     }      }
Line 527  sub BIN { Line 536  sub BIN {
 }  }
   
   
   #-------------------------------------------------------
   
   =item SUM(range)
   
   returns the sum of items in the range.
   
   =cut
   
   #-------------------------------------------------------
 sub SUM {  sub SUM {
     my $mask=mask(@_);      my $mask=mask(@_);
     my $sum=0;      my $sum=0;
     foreach (grep /$mask/,keys(%v)) {      foreach (grep /$mask/,keys(%sheet_values)) {
         $sum+=$v{$_};          $sum+=$sheet_values{$_};
     }      }
     return $sum;         return $sum;   
 }  }
   
   #-------------------------------------------------------
   
   =item MEAN(range)
   
   compute the average of the items in the range.
   
   =cut
   
   #-------------------------------------------------------
 sub MEAN {  sub MEAN {
     my $mask=mask(@_);      my $mask=mask(@_);
     my $sum=0; my $num=0;      my $sum=0; my $num=0;
     foreach (grep /$mask/,keys(%v)) {      foreach (grep /$mask/,keys(%sheet_values)) {
         $sum+=$v{$_};          $sum+=$sheet_values{$_};
         $num++;          $num++;
     }      }
     if ($num) {      if ($num) {
Line 550  sub MEAN { Line 577  sub MEAN {
     }         }   
 }  }
   
   #-------------------------------------------------------
   
   =item STDDEV(range)
   
   compute the standard deviation of the items in the range.
   
   =cut
   
   #-------------------------------------------------------
 sub STDDEV {  sub STDDEV {
     my $mask=mask(@_);      my $mask=mask(@_);
     my $sum=0; my $num=0;      my $sum=0; my $num=0;
     foreach (grep /$mask/,keys(%v)) {      foreach (grep /$mask/,keys(%sheet_values)) {
         $sum+=$v{$_};          $sum+=$sheet_values{$_};
         $num++;          $num++;
     }      }
     unless ($num>1) { return undef; }      unless ($num>1) { return undef; }
     my $mean=$sum/$num;      my $mean=$sum/$num;
     $sum=0;      $sum=0;
     foreach (grep /$mask/,keys(%v)) {      foreach (grep /$mask/,keys(%sheet_values)) {
         $sum+=($v{$_}-$mean)**2;          $sum+=($sheet_values{$_}-$mean)**2;
     }      }
     return sqrt($sum/($num-1));          return sqrt($sum/($num-1));    
 }  }
   
   #-------------------------------------------------------
   
   =item PROD(range)
   
   compute the product of the items in the range.
   
   =cut
   
   #-------------------------------------------------------
 sub PROD {  sub PROD {
     my $mask=mask(@_);      my $mask=mask(@_);
     my $prod=1;      my $prod=1;
     foreach (grep /$mask/,keys(%v)) {      foreach (grep /$mask/,keys(%sheet_values)) {
         $prod*=$v{$_};          $prod*=$sheet_values{$_};
     }      }
     return $prod;         return $prod;   
 }  }
   
   #-------------------------------------------------------
   
   =item MAX(range)
   
   compute the maximum of the items in the range.
   
   =cut
   
   #-------------------------------------------------------
 sub MAX {  sub MAX {
     my $mask=mask(@_);      my $mask=mask(@_);
     my $max='-';      my $max='-';
     foreach (grep /$mask/,keys(%v)) {      foreach (grep /$mask/,keys(%sheet_values)) {
         unless ($max) { $max=$v{$_}; }          unless ($max) { $max=$sheet_values{$_}; }
         if (($v{$_}>$max) || ($max eq '-')) { $max=$v{$_}; }          if (($sheet_values{$_}>$max) || ($max eq '-')) { $max=$sheet_values{$_}; }
     }       } 
     return $max;         return $max;   
 }  }
   
   #-------------------------------------------------------
   
   =item MIN(range)
   
   compute the minimum of the items in the range.
   
   =cut
   
   #-------------------------------------------------------
 sub MIN {  sub MIN {
     my $mask=mask(@_);      my $mask=mask(@_);
     my $min='-';      my $min='-';
     foreach (grep /$mask/,keys(%v)) {      foreach (grep /$mask/,keys(%sheet_values)) {
         unless ($max) { $max=$v{$_}; }          unless ($max) { $max=$sheet_values{$_}; }
         if (($v{$_}<$min) || ($min eq '-')) { $min=$v{$_}; }          if (($sheet_values{$_}<$min) || ($min eq '-')) { 
               $min=$sheet_values{$_}; 
           }
     }      }
     return $min;         return $min;   
 }  }
   
   #-------------------------------------------------------
   
   =item SUMMAX(num,lower,upper)
   
   compute the sum of the largest 'num' items in the range from
   'lower' to 'upper'
   
   =cut
   
   #-------------------------------------------------------
 sub SUMMAX {  sub SUMMAX {
     my ($num,$lower,$upper)=@_;      my ($num,$lower,$upper)=@_;
     my $mask=mask($lower,$upper);      my $mask=mask($lower,$upper);
     my @inside=();      my @inside=();
     foreach (grep /$mask/,keys(%v)) {      foreach (grep /$mask/,keys(%sheet_values)) {
  $inside[$#inside+1]=$v{$_};   push (@inside,$sheet_values{$_});
     }      }
     @inside=sort(@inside);      @inside=sort(@inside);
     my $sum=0; my $i;      my $sum=0; my $i;
Line 610  sub SUMMAX { Line 685  sub SUMMAX {
     return $sum;         return $sum;   
 }  }
   
   #-------------------------------------------------------
   
   =item SUMMIN(num,lower,upper)
   
   compute the sum of the smallest 'num' items in the range from
   'lower' to 'upper'
   
   =cut
   
   #-------------------------------------------------------
 sub SUMMIN {  sub SUMMIN {
     my ($num,$lower,$upper)=@_;      my ($num,$lower,$upper)=@_;
     my $mask=mask($lower,$upper);      my $mask=mask($lower,$upper);
     my @inside=();      my @inside=();
     foreach (grep /$mask/,keys(%v)) {      foreach (grep /$mask/,keys(%sheet_values)) {
  $inside[$#inside+1]=$v{$_};   $inside[$#inside+1]=$sheet_values{$_};
     }      }
     @inside=sort(@inside);      @inside=sort(@inside);
     my $sum=0; my $i;      my $sum=0; my $i;
Line 625  sub SUMMIN { Line 710  sub SUMMIN {
     return $sum;         return $sum;   
 }  }
   
   #-------------------------------------------------------
   
   =item MINPARM(parametername)
   
   Returns the minimum value of the parameters matching the parametername.
   parametername should be a string such as 'duedate'.
   
   =cut
   
   #-------------------------------------------------------
   sub MINPARM {
       my ($expression) = @_;
       my $min = undef;
       study($expression);
       foreach $parameter (keys(%c)) {
           next if ($parameter !~ /$expression/);
           if ((! defined($min)) || ($min > $c{$parameter})) {
               $min = $c{$parameter} 
           }
       }
       return $min;
   }
   
   #-------------------------------------------------------
   
   =item MAXPARM(parametername)
   
   Returns the maximum value of the parameters matching the input parameter name.
   parametername should be a string such as 'duedate'.
   
   =cut
   
   #-------------------------------------------------------
   sub MAXPARM {
       my ($expression) = @_;
       my $max = undef;
       study($expression);
       foreach $parameter (keys(%c)) {
           next if ($parameter !~ /$expression/);
           if ((! defined($min)) || ($max < $c{$parameter})) {
               $max = $c{$parameter} 
           }
       }
       return $max;
   }
   
   #--------------------------------------------------------
 sub expandnamed {  sub expandnamed {
     my $expression=shift;      my $expression=shift;
     if ($expression=~/^\&/) {      if ($expression=~/^\&/) {
Line 657  sub expandnamed { Line 789  sub expandnamed {
     return 0;      return 0;
         }          }
     } else {      } else {
         return '$c{\''.$expression.'\'}';          # it is not a function, so it is a parameter name
           # We should do the following:
           #    1. Take the list of parameter names
           #    2. look through the list for ones that match the parameter we want
           #    3. If there are no collisions, return the one that matches
           #    4. If there is a collision, return 'bad parameter name error'
           my $returnvalue = '';
           my @matches = ();
           $#matches = -1;
           study $expression;
           foreach $parameter (keys(%c)) {
               push @matches,$parameter if ($parameter =~ /$expression/);
           }
           if ($#matches == 0) {
               $returnvalue = '$c{\''.$matches[0].'\'}';
           } elsif ($#matches > 0) {
               # more than one match.  Look for a concise one
               $returnvalue =  "'non-unique parameter name : $expression'";
               foreach (@matches) {
                   if (/^$expression$/) {
                       $returnvalue = '$c{\''.$_.'\'}';
                   }
               }
           } else {
               $returnvalue =  "'bad parameter name : $expression'";
           }
           return $returnvalue;
     }      }
 }  }
   
Line 669  sub sett { Line 827  sub sett {
     } else {      } else {
         $pattern='[A-Z]';          $pattern='[A-Z]';
     }      }
       # Deal with the template row
     foreach (keys(%f)) {      foreach (keys(%f)) {
  if ($_=~/template\_(\w)/) {   next if ($_!~/template\_(\w)/);
   my $col=$1;          my $col=$1;
           unless ($col=~/^$pattern/) {          next if ($col=~/^$pattern/);
     foreach (keys(%f)) {          foreach (keys(%f)) {
       if ($_=~/A(\d+)/) {              next if ($_!~/A(\d+)/);
  my $trow=$1;              my $trow=$1;
                 if ($trow) {              next if (! $trow);
     my $lb=$col.$trow;              # Get the name of this cell
                     $t{$lb}=$f{'template_'.$col};              my $lb=$col.$trow;
                     $t{$lb}=~s/\#/$trow/g;              # Grab the template declaration
                     $t{$lb}=~s/\.\.+/\,/g;              $t{$lb}=$f{'template_'.$col};
                     $t{$lb}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$v\{\'$2\'\}/g;              # Replace '#' with the row number
                     $t{$lb}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.&expandnamed($2)/ge;              $t{$lb}=~s/\#/$trow/g;
                 }              # Replace '....' with ','
       }              $t{$lb}=~s/\.\.+/\,/g;
     }              # Replace 'A0' with the value from 'A0'
   }              $t{$lb}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$sheet_values\{\'$2\'\}/g;
       }              # Replace parameters
               $t{$lb}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.&expandnamed($2)/ge;
           }
     }      }
       # Deal with the normal cells
     foreach (keys(%f)) {      foreach (keys(%f)) {
  if (($f{$_}) && ($_!~/template\_/)) {   if (exists($f{$_}) && ($_!~/template\_/)) {
             my $matches=($_=~/^$pattern(\d+)/);              my $matches=($_=~/^$pattern(\d+)/);
             if  (($matches) && ($1)) {              if  (($matches) && ($1)) {
         unless ($f{$_}=~/^\!/) {          unless ($f{$_}=~/^\!/) {
Line 699  sub sett { Line 861  sub sett {
             } else {              } else {
        $t{$_}=$f{$_};         $t{$_}=$f{$_};
                $t{$_}=~s/\.\.+/\,/g;                 $t{$_}=~s/\.\.+/\,/g;
                $t{$_}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$v\{\'$2\'\}/g;                 $t{$_}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$sheet_values\{\'$2\'\}/g;
                $t{$_}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.&expandnamed($2)/ge;                 $t{$_}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.&expandnamed($2)/ge;
             }              }
         }          }
     }      }
       # For inserted lines, [B-Z] is also valid
       unless ($sheettype eq 'assesscalc') {
          foreach (keys(%f)) {
      if ($_=~/[B-Z](\d+)/) {
          if ($f{'A'.$1}=~/^[\~\-]/) {
              $t{$_}=$f{$_};
                     $t{$_}=~s/\.\.+/\,/g;
                     $t{$_}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$sheet_values\{\'$2\'\}/g;
                     $t{$_}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.&expandnamed($2)/ge;
                  }
              }
          }
       }
       # For some reason 'A0' gets special treatment...  This seems superfluous
       # but I imagine it is here for a reason.
     $t{'A0'}=$f{'A0'};      $t{'A0'}=$f{'A0'};
     $t{'A0'}=~s/\.\.+/\,/g;      $t{'A0'}=~s/\.\.+/\,/g;
     $t{'A0'}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$v\{\'$2\'\}/g;      $t{'A0'}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$sheet_values\{\'$2\'\}/g;
     $t{'A0'}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.&expandnamed($2)/ge;      $t{'A0'}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.&expandnamed($2)/ge;
 }  }
   
 sub calc {  sub calc {
     %v=();      undef %sheet_values;
     &sett();      &sett();
     my $notfinished=1;      my $notfinished=1;
       my $lastcalc='';
     my $depth=0;      my $depth=0;
     while ($notfinished) {      while ($notfinished) {
  $notfinished=0;   $notfinished=0;
         foreach (keys(%t)) {          foreach (keys(%t)) {
             my $old=$v{$_};              my $old=$sheet_values{$_};
             $v{$_}=eval($t{$_});              $sheet_values{$_}=eval $t{$_};
     if ($@) {      if ($@) {
  %v=();   undef %sheet_values;
                 return $@;                  return $_.': '.$@;
             }              }
     if ($v{$_} ne $old) { $notfinished=1; }      if ($sheet_values{$_} ne $old) { $notfinished=1; $lastcalc=$_; }
         }          }
         $depth++;          $depth++;
         if ($depth>100) {          if ($depth>100) {
     %v=();      undef %sheet_values;
             return 'Maximum calculation depth exceeded';              return $lastcalc.': Maximum calculation depth exceeded';
         }          }
     }      }
     return '';      return '';
Line 744  sub templaterow { Line 922  sub templaterow {
      'n','o','p','q','r','s','t','u','v','w','x','y','z') {       'n','o','p','q','r','s','t','u','v','w','x','y','z') {
         my $fm=$f{'template_'.$_};          my $fm=$f{'template_'.$_};
         $fm=~s/[\'\"]/\&\#34;/g;          $fm=~s/[\'\"]/\&\#34;/g;
         $cols[$#cols+1]="'template_$_','$fm'".'___eq___'.$fm;          push(@cols,"'template_$_','$fm'".'___eq___'.$fm);
     }      }
     return @cols;      return @cols;
 }  }
   
   #
   # This is actually used for the student spreadsheet, not the assessment sheet
   # Do not be fooled by the name!
   #
 sub outrowassess {  sub outrowassess {
       # $n is the current row number
     my $n=shift;      my $n=shift;
     my @cols=();      my @cols=();
     if ($n) {      if ($n) {
        my ($usy,$ufn)=split(/\_\_\&\&\&\_\_/,$f{'A'.$n});          my ($usy,$ufn)=split(/__&&&\__/,$f{'A'.$n});
        $cols[0]=$rl{$usy}.'<br>'.          if ($rowlabel{$usy}) {
                 '<select name="sel_'.$n.'" onChange="changesheet('.$n.              $cols[0]=$rowlabel{$usy}.'<br>'.
                 ')"><option name="default">Default</option>';                  '<select name="sel_'.$n.'" onChange="changesheet('.$n.')">'.
        foreach (@os) {                      '<option name="default">Default</option>';
            $cols[0].='<option name="'.$_.'"';          } else { 
               $cols[0]=''; 
           }
           foreach (@os) {
               $cols[0].='<option name="'.$_.'"';
             if ($ufn eq $_) {              if ($ufn eq $_) {
                $cols[0].=' selected';                  $cols[0].=' selected';
             }              }
             $cols[0].='>'.$_.'</option>';              $cols[0].='>'.$_.'</option>';
        }          }
        $cols[0].='</select>';          $cols[0].='</select>';
     } else {      } else {
        $cols[0]='<b><font size=+1>Export</font></b>';          $cols[0]='<b><font size=+1>Export</font></b>';
     }      }
     foreach ('A','B','C','D','E','F','G','H','I','J','K','L','M',      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',       'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
Line 774  sub outrowassess { Line 961  sub outrowassess {
      'n','o','p','q','r','s','t','u','v','w','x','y','z') {       '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;
         push(@cols,"'$_$n','$fm'".'___eq___'.$v{$_.$n});          push(@cols,"'$_$n','$fm'".'___eq___'.$sheet_values{$_.$n});
     }      }
     return @cols;      return @cols;
 }  }
Line 783  sub outrow { Line 970  sub outrow {
     my $n=shift;      my $n=shift;
     my @cols=();      my @cols=();
     if ($n) {      if ($n) {
        $cols[0]=$rl{$f{'A'.$n}};         $cols[0]=$rowlabel{$f{'A'.$n}};
     } else {      } else {
        $cols[0]='<b><font size=+1>Export</font></b>';         $cols[0]='<b><font size=+1>Export</font></b>';
     }      }
Line 793  sub outrow { Line 980  sub outrow {
      'n','o','p','q','r','s','t','u','v','w','x','y','z') {       'n','o','p','q','r','s','t','u','v','w','x','y','z') {
         my $fm=$f{$_.$n};          my $fm=$f{$_.$n};
         $fm=~s/[\'\"]/\&\#34;/g;          $fm=~s/[\'\"]/\&\#34;/g;
         $cols[$#cols+1]="'$_$n','$fm'".'___eq___'.$v{$_.$n};          push(@cols,"'$_$n','$fm'".'___eq___'.$sheet_values{$_.$n});
     }      }
     return @cols;      return @cols;
 }  }
Line 802  sub exportrowa { Line 989  sub exportrowa {
     my @exportarray=();      my @exportarray=();
     foreach ('A','B','C','D','E','F','G','H','I','J','K','L','M',      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') {       'N','O','P','Q','R','S','T','U','V','W','X','Y','Z') {
  $exportarray[$#exportarray+1]=$v{$_.'0'};   push(@exportarray,$sheet_values{$_.'0'});
     }       } 
     return @exportarray;      return @exportarray;
 }  }
Line 837  sub setothersheets { Line 1024  sub setothersheets {
 # ------------------------------------------------ Add or change formula values  # ------------------------------------------------ Add or change formula values
   
 sub setrowlabels {  sub setrowlabels {
     my ($safeeval,%rl)=@_;      my ($safeeval,%rowlabel)=@_;
     %{$safeeval->varglob('rl')}=%rl;      %{$safeeval->varglob('rowlabel')}=%rowlabel;
 }  }
   
 # ------------------------------------------------------- Calculate spreadsheet  # ------------------------------------------------------- Calculate spreadsheet
   
 sub calcsheet {  sub calcsheet {
     my $safeeval=shift;      my $safeeval=shift;
     $safeeval->reval('&calc();');      return $safeeval->reval('&calc();');
 }  }
   
 # ------------------------------------------------------------------ Get values  # ------------------------------------------------------------------ Get values
   
 sub getvalues {  sub getvalues {
     my $safeeval=shift;      my $safeeval=shift;
     return $safeeval->reval('%v');      return $safeeval->reval('%sheet_values');
 }  }
   
 # ---------------------------------------------------------------- Get formulas  # ---------------------------------------------------------------- Get formulas
Line 862  sub getformulas { Line 1049  sub getformulas {
     return %{$safeeval->varglob('f')};      return %{$safeeval->varglob('f')};
 }  }
   
   # ----------------------------------------------------- Get value of $f{'A'.$n}
   
   sub getfa {
       my ($safeeval,$n)=@_;
       return $safeeval->reval('$f{"A'.$n.'"}');
   }
   
 # -------------------------------------------------------------------- Get type  # -------------------------------------------------------------------- Get type
   
 sub gettype {  sub gettype {
Line 906  sub getcid { Line 1100  sub getcid {
   
 # --------------------------------------------------------- Get course filename  # --------------------------------------------------------- Get course filename
   
 sub getcfn {  sub getcoursefilename {
     my $safeeval=shift;      my $safeeval=shift;
     return $safeeval->reval('$cfn');      return $safeeval->reval('$coursefilename');
 }  }
   
 # ----------------------------------------------------------- Get course number  # ----------------------------------------------------------- Get course number
Line 989  sub rown { Line 1183  sub rown {
     my $rowdata='';      my $rowdata='';
     my $dataflag=0;      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';
     }      }
     unless ($ENV{'form.showcsv'}) {      unless ($ENV{'form.showcsv'}) {
        $rowdata.="\n<tr><td><b><font size=+1>$n</font></b></td>";          $rowdata.="\n<tr><td><b><font size=+1>$n</font></b></td>";
     } else {      } else {
        $rowdata.="\n".'"'.$n.'"';          $rowdata.="\n".'"'.$n.'"';
     }      }
     my $showf=0;      my $showf=0;
     my $proc;      my $proc;
     my $maxred;      my $maxred=1;
     my $sheettype=&gettype($safeeval);      my $sheettype=&gettype($safeeval);
     if ($sheettype eq 'studentcalc') {      if ($sheettype eq 'studentcalc') {
         $proc='&outrowassess';          $proc='&outrowassess';
Line 1013  sub rown { Line 1207  sub rown {
     } else {      } else {
         $maxred=26;          $maxred=26;
     }      }
     if ($n eq '-') { $proc='&templaterow'; $n=-1; $dataflag=1; }      if (&getfa($safeeval,$n)=~/^[\~\-]/) { $maxred=1; }
       if ($n eq '-') { 
           $proc='&templaterow'; 
           $n=-1; 
           $dataflag=1; 
       }
     foreach ($safeeval->reval($proc.'('.$n.')')) {      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')) &&          if ((($vl ne '') || ($vl eq '0')) &&
            (($showf==1) || ($sheettype ne 'studentcalc'))) { $dataflag=1; }              (($showf==1) || ($sheettype ne 'studentcalc'))) { $dataflag=1; }
        if ($showf==0) { $vl=$_; }          if ($showf==0) { $vl=$_; }
       unless ($ENV{'form.showcsv'}) {          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))) {
    if ($vl eq '') {                  if ($vl eq '') {
        $vl='<font size=+2 color='.$bgcolor.'>&#35;</font>';                      $vl='<font size=+2 color='.$bgcolor.'>&#35;</font>';
            }                  }
            $rowdata.=                  $rowdata.='<td bgcolor='.$bgcolor.'>';
        '<td bgcolor='.$bgcolor.'><a href="javascript:celledit('.$fm.');">'.$vl.                  if ($ENV{'request.role'} =~ /^st\./) {
        '</a></td>';                      $rowdata.=$vl;
        } else {                  } else {
            $rowdata.='<td bgcolor='.$bgcolor.'>&nbsp;'.$vl.'&nbsp;</td>';                      $rowdata.='<a href="javascript:celledit('.$fm.');">'.
        }                          $vl.'</a>';
       } else {                  }
   $rowdata.=',"'.$vl.'"';                  $rowdata.='</td>';
       }              } else {
        $showf++;                  $rowdata.='<td bgcolor='.$bgcolor.'>&nbsp;'.$vl.'&nbsp;</td>';
               }
           } else {
               $rowdata.=',"'.$vl.'"';
           }
           $showf++;
     }  # End of foreach($safeval...)      }  # End of foreach($safeval...)
     if ($ENV{'form.showall'} || ($dataflag)) {      if ($ENV{'form.showall'} || ($dataflag)) {
        return $rowdata.($ENV{'form.showcsv'}?'':'</tr>');          return $rowdata.($ENV{'form.showcsv'}?'':'</tr>');
     } else {      } else {
        return '';          return '';
     }      }
 }  }
   
 # ------------------------------------------------------------- Print out sheet  # ------------------------------------------------------------- Print out sheet
   
 sub outsheet {  sub outsheet {
     my ($r,$safeeval)=@_;      my ($r,$safeeval,$sheetdata)=@_;
     my $maxred;      my $maxred = 26;    # The maximum number of cells to show as 
     my $realm;                          # red (uneditable) 
     if (&gettype($safeeval) eq 'assesscalc') {                          # To make student sheets uneditable could we 
                           # set $maxred = 52?
                           #
       my $realm='Course'; # 'assessment', 'user', or 'course' sheet
       if ($sheetdata->{'sheettype'} eq 'assesscalc') {
         $maxred=1;          $maxred=1;
         $realm='Assessment';          $realm='Assessment';
     } elsif (&gettype($safeeval) eq 'studentcalc') {      } elsif ($sheetdata->{'sheettype'} eq 'studentcalc') {
         $maxred=26;          $maxred=26;
         $realm='User';          $realm='User';
     } else {  
         $maxred=26;  
         $realm='Course';  
     }      }
     my $maxyellow=52-$maxred;      #
       # Column label
     my $tabledata;      my $tabledata;
     unless ($ENV{'form.showcsv'}) {      if ($ENV{'form.showcsv'}) {
        $tabledata=          $tabledata='<pre>';
         '<table border=2><tr><th colspan=2 rowspan=2><font size=+2>'.      } else { 
                   $realm.'</font></th>'.          $tabledata='<table border=2><tr><th colspan=2 rowspan=2>'.
               '<font size=+2>'.$realm.'</font></th>'.
                   '<td bgcolor=#FFDDDD colspan='.$maxred.                    '<td bgcolor=#FFDDDD colspan='.$maxred.
                   '><b><font size=+1>Import</font></b></td>'.                    '><b><font size=+1>Import</font></b></td>'.
                   '<td colspan='.$maxyellow.                    '<td colspan='.(52-$maxred).
   '><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;
     foreach ('A','B','C','D','E','F','G','H','I','J','K','L','M',          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',                   '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',                   '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') {                   '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">'; 
         } else {              } else {
            $tabledata.='<td>';                  $tabledata.='<td>';
               }
               $tabledata.="<b><font size=+1>$_</font></b></td>";
         }          }
         $tabledata.="<b><font size=+1>$_</font></b></td>";          $tabledata.='</tr>'.&rown($safeeval,'-').&rown($safeeval,0);
     }      }
     $tabledata.='</tr>'.&rown($safeeval,'-').&rown($safeeval,0);  
    } else { $tabledata='<pre>'; }  
   
     $r->print($tabledata);      $r->print($tabledata);
       #
       # Prepare to output rows
     my $row;      my $row;
     my $maxrow=&getmaxrow($safeeval);      my $maxrow=&getmaxrow($safeeval);
       #
     my @sortby=();      my @sortby=();
     my @sortidx=();      my @sortidx=();
     for ($row=1;$row<=$maxrow;$row++) {      for ($row=1;$row<=$maxrow;$row++) {
        $sortby[$row-1]=$safeeval->reval('$f{"A'.$row.'"}');          push (@sortby, $safeeval->reval('$f{"A'.$row.'"}'));
        $sortidx[$row-1]=$row-1;          push (@sortidx, $row-1);
     }      }
     @sortidx=sort { $sortby[$a] cmp $sortby[$b]; } @sortidx;      @sortidx=sort { lc($sortby[$a]) cmp lc($sortby[$b]); } @sortidx;
       #
         my $what='Student';      # Determine the type of child spreadsheets
         if (&gettype($safeeval) eq 'assesscalc') {      my $what='Student';
     $what='Item';      if ($sheetdata->{'sheettype'} eq 'assesscalc') {
  } elsif (&gettype($safeeval) eq 'studentcalc') {          $what='Item';
             $what='Assessment';      } elsif ($sheetdata->{'sheettype'} eq 'studentcalc') {
         }          $what='Assessment';
       }
       #
       # Loop through the rows and output them one at a time
     my $n=0;      my $n=0;
     for ($row=0;$row<$maxrow;$row++) {      for ($row=0;$row<$maxrow;$row++) {
      my $thisrow=&rown($safeeval,$sortidx[$row]+1);          my $thisrow=&rown($safeeval,$sortidx[$row]+1);
      if ($thisrow) {          if ($thisrow) {
        if (($n/25==int($n/25)) && (!$ENV{'form.showcsv'})) {              if (($n/25==int($n/25)) && (!$ENV{'form.showcsv'})) {
  $r->print("</table>\n<br>\n");                  $r->print("</table>\n<br>\n");
         $r->rflush();                  $r->rflush();
         $r->print('<table border=2><tr><td>&nbsp;<td>'.$what.'</td>');                  $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',                  $r->print('<td>'.
  'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',                            join('</td><td>',
  'a','b','c','d','e','f','g','h','i','j','k','l','m',                                 (split(//,'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
  'n','o','p','q','r','s','t','u','v','w','x','y','z') {                                        'abcdefghijklmnopqrstuvwxyz'))).
            $r->print('<td>'.$_.'</td>');                            "</td></tr>\n");
               }
               $n++;
               $r->print($thisrow);
         }          }
         $r->print('</tr>');  
        }  
        $n++;  
        $r->print($thisrow);  
       }  
     }      }
     $r->print($ENV{'form.showcsv'}?'</pre>':'</table>');      $r->print($ENV{'form.showcsv'}?'</pre>':'</table>');
 }  }
Line 1136  sub outsheet { Line 1343  sub outsheet {
 sub othersheets {  sub othersheets {
     my ($safeeval,$stype)=@_;      my ($safeeval,$stype)=@_;
     #      #
     my $cnum=&getcnum($safeeval);      my $cnum  = &getcnum($safeeval);
     my $cdom=&getcdom($safeeval);      my $cdom  = &getcdom($safeeval);
     my $chome=&getchome($safeeval);      my $chome = &getchome($safeeval);
     #      #
     my @alternatives=();      my @alternatives=();
     my %results=&Apache::lonnet::dump($stype.'_spreadsheets',$cdom,$cnum);      my %results=&Apache::lonnet::dump($stype.'_spreadsheets',$cdom,$cnum);
Line 1181  sub parse_sheet { Line 1388  sub parse_sheet {
 #  #
 # -------------------------------------- Read spreadsheet formulas for a course  # -------------------------------------- Read spreadsheet formulas for a course
 #  #
   
 sub readsheet {  sub readsheet {
   my ($safeeval,$fn)=@_;      my ($safeeval,$sheetdata,$fn)=@_;
   my $stype=&gettype($safeeval);      #
   my $cnum=&getcnum($safeeval);      my $stype = $sheetdata->{'sheettype'};
   my $cdom=&getcdom($safeeval);      my $cnum  = $sheetdata->{'cnum'};
   my $chome=&getchome($safeeval);      my $cdom  = $sheetdata->{'cdom'};
       my $chome = $sheetdata->{'chome'};
   if (! defined($fn)) {      #
       # There is no filename. Look for defaults in course and global, cache      if (! defined($fn)) {
       unless ($fn=$defaultsheets{$cnum.'_'.$cdom.'_'.$stype}) {          # There is no filename. Look for defaults in course and global, cache
           my %tmphash = &Apache::lonnet::get('environment',          unless ($fn=$defaultsheets{$cnum.'_'.$cdom.'_'.$stype}) {
                                              ['spreadsheet_default_'.$stype],              my %tmphash = &Apache::lonnet::get('environment',
                                              $cdom,$cnum);                                                 ['spreadsheet_default_'.$stype],
           my ($tmp) = keys(%tmphash);                                                 $cdom,$cnum);
           if ($tmp =~ /^(con_lost|error|no_such_host)/i) {              my ($tmp) = keys(%tmphash);
               $fn = 'default_'.$stype;              if ($tmp =~ /^(con_lost|error|no_such_host)/i) {
           } else {                  $fn = 'default_'.$stype;
               $fn = $tmphash{'spreadsheet_default_'.$stype};              } else {
           }                   $fn = $tmphash{'spreadsheet_default_'.$stype};
           $defaultsheets{$cnum.'_'.$cdom.'_'.$stype}=$fn;               } 
       }              unless (($fn) && ($fn!~/^error\:/)) {
   }                  $fn='default_'.$stype;
               }
 # ---------------------------------------------------------- fn now has a value              $defaultsheets{$cnum.'_'.$cdom.'_'.$stype}=$fn; 
           }
   &setfilename($safeeval,$fn);      }
       # $fn now has a value
 # ------------------------------------------------------ see if sheet is cached      &setfilename($safeeval,$fn);
   my $fstring='';      # see if sheet is cached
   if ($fstring=$spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}) {      my $fstring='';
       &setformulas($safeeval,split(/\_\_\_\;\_\_\_/,$fstring));      if ($fstring=$spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}) {
   } else {          &setformulas($safeeval,split(/\_\_\_\;\_\_\_/,$fstring));
       } else {
 # ---------------------------------------------------- Not cached, need to read          # Not cached, need to read
           my %f=();
      my %f=();          if ($fn=~/^default\_/) {
               my $sheetxml='';
      if ($fn=~/^default\_/) {              my $fh;
          my $sheetxml='';              my $dfn=$fn;
          my $fh;              $dfn=~s/\_/\./g;
          my $dfn=$fn;              if ($fh=Apache::File->new($includedir.'/'.$dfn)) {
          $dfn=~s/\_/\./g;                  $sheetxml=join('',<$fh>);
          if ($fh=Apache::File->new($includedir.'/'.$dfn)) {              } else {
              $sheetxml=join('',<$fh>);                  $sheetxml='<field row="0" col="A">"Error"</field>';
          } else {              }
              $sheetxml='<field row="0" col="A">"Error"</field>';              %f=%{&parse_sheet(\$sheetxml)};
          }          } elsif($fn=~/\/*\.spreadsheet$/) {
          %f=%{&parse_sheet(\$sheetxml)};              my $sheetxml=&Apache::lonnet::getfile
      } elsif($fn=~/\/*\.spreadsheet$/) {                  (&Apache::lonnet::filelocation('',$fn));
          my $sheetxml=&Apache::lonnet::getfile              if ($sheetxml == -1) {
              (&Apache::lonnet::filelocation('',$fn));                  $sheetxml='<field row="0" col="A">"Error loading spreadsheet '
          print "<pre>$sheetxml</pre>";                      .$fn.'"</field>';
          if ($sheetxml == -1) {              }
              $sheetxml='<field row="0" col="A">"Error loading spreadsheet '              %f=%{&parse_sheet(\$sheetxml)};
                  .$fn.'"</field>';          } else {
          }              my $sheet='';
          %f=%{&parse_sheet(\$sheetxml)};              my %tmphash = &Apache::lonnet::dump($fn,$cdom,$cnum);
          print "<pre>";              my ($tmp) = keys(%tmphash);
          foreach (sort( keys(%f))) {              unless ($tmp =~ /^(con_lost|error|no_such_host)/i) {
              print "$_ = $f{$_}\n";                  foreach (keys(%tmphash)) {
          }                      $f{$_}=$tmphash{$_};
          print "</pre>";                  }
      } else {              }
          my $sheet='';          }
          my %tmphash = &Apache::lonnet::dump($fn,$cdom,$cnum);          # Cache and set
          my ($tmp) = keys(%tmphash);          $spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}=join('___;___',%f);  
          unless ($tmp =~ /^(con_lost|error|no_such_host)/i) {          &setformulas($safeeval,%f);
              foreach (keys(%tmphash)) {  
                  $f{$_}=$tmphash{$_};  
              }  
          }  
      }  
 # --------------------------------------------------------------- Cache and set  
        $spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}=join('___;___',%f);    
        &setformulas($safeeval,%f);  
     }      }
 }  }
   
 # -------------------------------------------------------- Make new spreadsheet  # -------------------------------------------------------- Make new spreadsheet
   
 sub makenewsheet {  sub makenewsheet {
     my ($uname,$udom,$stype,$usymb)=@_;      my ($uname,$udom,$stype,$usymb)=@_;
       my %sheetdata=();
       $sheetdata{'uname'} = $uname;
       $sheetdata{'udom'}  = $udom;
       $sheetdata{'sheettype'} = $stype;
       $sheetdata{'usymb'} = $usymb;
       $sheetdata{'cid'}   = $ENV{'request.course.id'};
       $sheetdata{'csec'}  = &Apache::lonnet::usection
                                  ($udom,$uname,$ENV{'request.course.id'});
       $sheetdata{'coursefilename'}   = $ENV{'request.course.fn'};
       $sheetdata{'cnum'}  = $ENV{'course.'.$ENV{'request.course.id'}.'.num'};
       $sheetdata{'cdom'}  = $ENV{'course.'.$ENV{'request.course.id'}.'.domain'};
       $sheetdata{'chome'} = $ENV{'course.'.$ENV{'request.course.id'}.'.home'};
       $sheetdata{'uhome'} = &Apache::lonnet::homeserver($uname,$udom);
       
     my $safeeval=initsheet($stype);      my $safeeval=initsheet($stype);
     $safeeval->reval(      my $initstring = '';
        '$uname="'.$uname.      foreach (keys(%sheetdata)) {
       '";$udom="'.$udom.          $initstring.= qq{\$$_="$sheetdata{$_}";};
       '";$uhome="'.&Apache::lonnet::homeserver($uname,$udom).      }
       '";$sheettype="'.$stype.      $safeeval->reval($initstring);
       '";$usymb="'.$usymb.      return $safeeval,\%sheetdata;
       '";$csec="'.&Apache::lonnet::usection($udom,$uname,  
                                             $ENV{'request.course.id'}).  
       '";$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;  
 }  }
   
 # ------------------------------------------------------------ Save spreadsheet  # ------------------------------------------------------------ Save spreadsheet
   
 sub writesheet {  sub writesheet {
   my ($safeeval,$makedef)=@_;      my ($safeeval,$makedef)=@_;
   my $cid=&getcid($safeeval);      my $cid=&getcid($safeeval);
   if (&Apache::lonnet::allowed('opa',$cid)) {      if (&Apache::lonnet::allowed('opa',$cid)) {
     my %f=&getformulas($safeeval);          my %f=&getformulas($safeeval);
     my $stype=&gettype($safeeval);          my $stype=&gettype($safeeval);
     my $cnum=&getcnum($safeeval);          my $cnum=&getcnum($safeeval);
     my $cdom=&getcdom($safeeval);          my $cdom=&getcdom($safeeval);
     my $chome=&getchome($safeeval);          my $chome=&getchome($safeeval);
     my $fn=&getfilename($safeeval);          my $fn=&getfilename($safeeval);
           # Cache new sheet
 # ------------------------------------------------------------- Cache new sheet          $spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}=join('___;___',%f);
     $spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}=join('___;___',%f);              # Write sheet
 # ----------------------------------------------------------------- Write sheet          my $sheetdata='';
     my $sheetdata='';          foreach (keys(%f)) {
     foreach (keys(%f)) {              unless ($f{$_} eq 'import') {
      unless ($f{$_} eq 'import') {                  $sheetdata.=&Apache::lonnet::escape($_).'='.
        $sheetdata.=&Apache::lonnet::escape($_).'='.                      &Apache::lonnet::escape($f{$_}).'&';
    &Apache::lonnet::escape($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).
               &Apache::lonnet::escape($fn).'='.$ENV{'user.name'}.'@'.                                            '='.$ENV{'user.name'}.'@'.
                                                $ENV{'user.domain'},                                            $ENV{'user.domain'},
               $chome);                                            $chome);
           if ($reply eq 'ok') {              if ($reply eq 'ok') {
               if ($makedef) {                   if ($makedef) { 
                 return &Apache::lonnet::reply('put:'.$cdom.':'.$cnum.                      return &Apache::lonnet::reply('put:'.$cdom.':'.$cnum.
                                 ':environment:spreadsheet_default_'.$stype.'='.                                                    ':environment:'.
                                 &Apache::lonnet::escape($fn),                                                    'spreadsheet_default_'.
                                 $chome);                                                    $stype.'='.
       } else {                                                    &Apache::lonnet::escape($fn),
   return $reply;                                                    $chome);
          }                  } 
    } else {                  return $reply;
        return $reply;              } 
            }              return $reply;
       } else {          } 
   return $reply;          return $reply;
       }      }
   }      return 'unauthorized';
   return 'unauthorized';  
 }  }
   
 # ----------------------------------------------- Make a temp copy of the sheet  # ----------------------------------------------- Make a temp copy of the sheet
 # "Modified workcopy" - interactive only  # "Modified workcopy" - interactive only
 #  #
   
 sub tmpwrite {  sub tmpwrite {
     my $safeeval=shift;      my $safeeval=shift;
     my $fn=$ENV{'user.name'}.'_'.      my $fn=$ENV{'user.name'}.'_'.
            $ENV{'user.domain'}.'_spreadsheet_'.&getusymb($safeeval).'_'.          $ENV{'user.domain'}.'_spreadsheet_'.&getusymb($safeeval).'_'.
            &getfilename($safeeval);             &getfilename($safeeval);
     $fn=~s/\W/\_/g;      $fn=~s/\W/\_/g;
     $fn=$tmpdir.$fn.'.tmp';      $fn=$tmpdir.$fn.'.tmp';
Line 1350  sub tmpwrite { Line 1550  sub tmpwrite {
 }  }
   
 # ---------------------------------------------------------- Read the temp copy  # ---------------------------------------------------------- Read the temp copy
   
 sub tmpread {  sub tmpread {
     my ($safeeval,$nfield,$nform)=@_;      my ($safeeval,$nfield,$nform)=@_;
     my $fn=$ENV{'user.name'}.'_'.      my $fn=$ENV{'user.name'}.'_'.
Line 1360  sub tmpread { Line 1559  sub tmpread {
     $fn=$tmpdir.$fn.'.tmp';      $fn=$tmpdir.$fn.'.tmp';
     my $fh;      my $fh;
     my %fo=();      my %fo=();
       my $countrows=0;
     if ($fh=Apache::File->new($fn)) {      if ($fh=Apache::File->new($fn)) {
         my $name;          my $name;
         while ($name=<$fh>) {          while ($name=<$fh>) {
Line 1367  sub tmpread { Line 1567  sub tmpread {
             my $value=<$fh>;              my $value=<$fh>;
             chomp($value);              chomp($value);
             $fo{$name}=$value;              $fo{$name}=$value;
               if ($name=~/^A(\d+)$/) {
    if ($1>$countrows) {
       $countrows=$1;
                   }
               }
         }          }
     }      }
     if ($nform eq 'changesheet') {      if ($nform eq 'changesheet') {
Line 1374  sub tmpread { Line 1579  sub tmpread {
         unless ($ENV{'form.sel_'.$nfield} eq 'Default') {          unless ($ENV{'form.sel_'.$nfield} eq 'Default') {
     $fo{'A'.$nfield}.='__&&&__'.$ENV{'form.sel_'.$nfield};      $fo{'A'.$nfield}.='__&&&__'.$ENV{'form.sel_'.$nfield};
         }          }
       } elsif ($nfield eq 'insertrow') {
           $countrows++;
           my $newrow=substr('000000'.$countrows,-7);
           if ($nform eq 'top') {
       $fo{'A'.$countrows}='--- '.$newrow;
           } else {
               $fo{'A'.$countrows}='~~~ '.$newrow;
           }
     } else {      } else {
        if ($nfield) { $fo{$nfield}=$nform; }         if ($nfield) { $fo{$nfield}=$nform; }
     }      }
     &setformulas($safeeval,%fo);      &setformulas($safeeval,%fo);
 }  }
   
 # ================================================================== Parameters  ##################################################
 # -------------------------------------------- Figure out a cascading parameter  ##################################################
 #  
 # For this function to work  
 #  
 # * parmhash needs to be tied  
 # * courseopt and useropt need to be initialized for this user and course  
 #  
   
 sub parmval {  
     my ($what,$safeeval)=@_;  
     my $cid=&getcid($safeeval);  
     my $csec=&getcsec($safeeval);  
     my $uname=&getuname($safeeval);  
     my $udom=&getudom($safeeval);  
     my $symb=&getusymb($safeeval);  
   
     unless ($symb) { return ''; }  
     my $result='';  
   
     my ($mapname,$id,$fn)=split(/\_\_\_/,$symb);  
 # ----------------------------------------------------- Cascading lookup scheme  
        my $rwhat=$what;  
        $what=~s/^parameter\_//;  
        $what=~s/\_([^\_]+)$/\.$1/;  
   
        my $symbparm=$symb.'.'.$what;  
        my $mapparm=$mapname.'___(all).'.$what;  
        my $usercourseprefix=$uname.'_'.$udom.'_'.$cid;  
   
        my $seclevel=  
             $usercourseprefix.'.['.  
  $csec.'].'.$what;  
        my $seclevelr=  
             $usercourseprefix.'.['.  
  $csec.'].'.$symbparm;  
        my $seclevelm=  
             $usercourseprefix.'.['.  
  $csec.'].'.$mapparm;  
   
        my $courselevel=  
             $usercourseprefix.'.'.$what;  
        my $courselevelr=  
             $usercourseprefix.'.'.$symbparm;  
        my $courselevelm=  
             $usercourseprefix.'.'.$mapparm;  
   
 # ---------------------------------------------------------- fourth, check user  
         
       if ($uname) {   
   
        if ($useropt{$courselevelr}) { return $useropt{$courselevelr}; }  
   
        if ($useropt{$courselevelm}) { return $useropt{$courselevelm}; }  
   
        if ($useropt{$courselevel}) { return $useropt{$courselevel}; }  
   
       }  =pod
   
 # --------------------------------------------------------- third, check course  =item &parmval()
        
        if ($csec) {  
    
         if ($courseopt{$seclevelr}) { return $courseopt{$seclevelr}; }  
   
         if ($courseopt{$seclevelm}) { return $courseopt{$seclevelm}; }    Determine the value of a parameter.
   
         if ($courseopt{$seclevel}) { return $courseopt{$seclevel}; }  Inputs: $what, the parameter needed, $safeeval, the safe space
     
       }  
   
        if ($courseopt{$courselevelr}) { return $courseopt{$courselevelr}; }  Returns: The value of a parameter, or '' if none.
   
        if ($courseopt{$courselevelm}) { return $courseopt{$courselevelm}; }  This function cascades through the possible levels searching for a value for
   a parameter.  The levels are checked in the following order:
   user, course (at section level and course level), map, and lonnet::metadata.
   This function uses %parmhash, which must be tied prior to calling it.
   This function also requires %courseopt and %useropt to be initialized for
   this user and course.
   
        if ($courseopt{$courselevel}) { return $courseopt{$courselevel}; }  =cut
   
 # ----------------------------------------------------- second, check map parms  
   
        my $thisparm=$parmhash{$symbparm};  
        if ($thisparm) { return $thisparm; }  
   
 # -------------------------------------------------------- first, check default  
   
        return &Apache::lonnet::metadata($fn,$rwhat.'.default');  ##################################################
           ##################################################
   sub parmval {
       my ($what,$safeeval,$sheetdata)=@_;
       my $symb  = $sheetdata->{'usymb'};
       unless ($symb) { return ''; }
       #
       my $cid   = $sheetdata->{'cid'};
       my $csec  = $sheetdata->{'csec'};
       my $uname = $sheetdata->{'uname'};
       my $udom  = $sheetdata->{'udom'};
       my $result='';
       #
       my ($mapname,$id,$fn)=split(/\_\_\_/,$symb);
       # Cascading lookup scheme
       my $rwhat=$what;
       $what =~ s/^parameter\_//;
       $what =~ s/\_([^\_]+)$/\.$1/;
       #
       my $symbparm = $symb.'.'.$what;
       my $mapparm  = $mapname.'___(all).'.$what;
       my $usercourseprefix = $uname.'_'.$udom.'_'.$cid;
       #
       my $seclevel  = $usercourseprefix.'.['.$csec.'].'.$what;
       my $seclevelr = $usercourseprefix.'.['.$csec.'].'.$symbparm;
       my $seclevelm = $usercourseprefix.'.['.$csec.'].'.$mapparm;
       #
       my $courselevel  = $usercourseprefix.'.'.$what;
       my $courselevelr = $usercourseprefix.'.'.$symbparm;
       my $courselevelm = $usercourseprefix.'.'.$mapparm;
       # fourth, check user
       if ($uname) { 
           return $useropt{$courselevelr} if ($useropt{$courselevelr});
           return $useropt{$courselevelm} if ($useropt{$courselevelm});
           return $useropt{$courselevel}  if ($useropt{$courselevel});
       }
       # third, check course
       if ($csec) {
           return $courseopt{$seclevelr} if ($courseopt{$seclevelr});
           return $courseopt{$seclevelm} if ($courseopt{$seclevelm});
           return $courseopt{$seclevel}  if ($courseopt{$seclevel});
       }
       #
       return $courseopt{$courselevelr} if ($courseopt{$courselevelr});
       return $courseopt{$courselevelm} if ($courseopt{$courselevelm});
       return $courseopt{$courselevel}  if ($courseopt{$courselevel});
       # second, check map parms
       my $thisparm = $parmhash{$symbparm};
       return $thisparm if ($thisparm);
       # first, check default
       return &Apache::lonnet::metadata($fn,$rwhat.'.default');
 }  }
   
 # ---------------------------------------------- Update rows for course listing  # ---------------------------------------------- Update rows for course listing
   
 sub updateclasssheet {  sub updateclasssheet {
     my $safeeval=shift;      my ($safeeval,$sheetdata) = @_;
     my $cnum=&getcnum($safeeval);      my $cnum  =$sheetdata->{'cnum'};
     my $cdom=&getcdom($safeeval);      my $cdom  =$sheetdata->{'cdom'};
     my $cid=&getcid($safeeval);      my $cid   =$sheetdata->{'cid'};
     my $chome=&getchome($safeeval);      my $chome =$sheetdata->{'chome'};
       #
 # ---------------------------------------------- Read class list and row labels      # Read class list and row labels
       my %classlist;
     my $classlst=&Apache::lonnet::reply      my @tmp = &Apache::lonnet::dump('classlist',$cdom,$cnum);
                                  ('dump:'.$cdom.':'.$cnum.':classlist',$chome);      if ($tmp[0] !~ /^error/) {
           %classlist = @tmp;
       } else {
           return 'Could not access course data';
       }
       undef @tmp;
       #
     my %currentlist=();      my %currentlist=();
     my $now=time;      my $now=time;
     unless ($classlst=~/^error\:/) {      foreach my $student (keys(%classlist)) {
         foreach (split(/\&/,$classlst)) {          my ($end,$start)=split(/\:/,$classlist{$student});
             my ($name,$value)=split(/\=/,$_);          my $active=1;
             my ($end,$start)=split(/\:/,&Apache::lonnet::unescape($value));          $active = 0 if (($end) && ($now>$end));
             my $active=1;          $active = 1 if ($ENV{'form.Status'} eq 'Any');
             if (($end) && ($now>$end)) { $active=0; }          $active = !$active if ($ENV{'form.Status'} eq 'Expired');
             if ($active) {          if ($active) {
                 my $rowlabel='';              my $rowlabel='';
                 $name=&Apache::lonnet::unescape($name);              my ($studentName,$studentDomain)=split(/\:/,$student);
                 my ($sname,$sdom)=split(/\:/,$name);              my $studentSection=&Apache::lonnet::usection($studentDomain,
                 my $ssec=&Apache::lonnet::usection($sdom,$sname,$cid);                                                           $studentName,$cid);
                 if ($ssec==-1) {  #            if ($studentSection==-1) {
    unless ($ENV{'form.showcsv'}) {  #                unless ($ENV{'form.showcsv'}) {
                     $rowlabel='<font color=red>Data not available: '.$name.  #                    $rowlabel='<font color=red>Data not available: '.
       '</font>';  #                        $studentName.'</font>';
    } else {  #                } else {
        $rowlabel='ERROR","'.$name.  #                    $rowlabel='ERROR","'.$studentName.
                                  '","Data not available","","","';  #                        '","Data not available","","","';
                    }  #                }
                 } else {  #            } else {
                     my %reply=&Apache::lonnet::idrget($sdom,$sname);                  my %reply=&Apache::lonnet::idrget($studentDomain,$studentName);
                     my $reply=&Apache::lonnet::reply('get:'.$sdom.':'.$sname.                  my %studentInformation=&Apache::lonnet::get
       ':environment:firstname&middlename&lastname&generation',                      ('environment',
                       &Apache::lonnet::homeserver($sname,$sdom));                       ['lastname','generation','firstname','middlename','id'],
    unless ($ENV{'form.showcsv'}) {                       $studentDomain,$studentName);
                     $rowlabel='<a href="/adm/studentcalc?uname='.$sname.                  if (! $ENV{'form.showcsv'}) {
                               '&udom='.$sdom.'">'.                      $rowlabel='<a href="/adm/studentcalc?uname='.$studentName.
                               $ssec.'&nbsp;'.$reply{$sname}.'<br>';                          '&udom='.$studentDomain.'">'.
                     foreach ( split(/\&/,$reply)) {                              $studentSection.'&nbsp;';
                         $rowlabel.=&Apache::lonnet::unescape($_).' ';                      foreach ('id','firstname','middlename',
                                'lastname','generation'){
                           $rowlabel.=$studentInformation{$_}."&nbsp;";
                     }                      }
                     $rowlabel.='</a>';                      $rowlabel.='</a>';
    } else {                  } else {
     $rowlabel=$ssec.'","'.$reply{$sname}.'"';                      $rowlabel= '"'.join('","',
                     my $ncount=0;                                          ($studentSection,
                     foreach (split(/\&/,$reply)) {                                           $studentInformation{'id'},
                         $rowlabel.=',"'.&Apache::lonnet::unescape($_).'"';                                           $studentInformation{'firstname'},
                         $ncount++;                                           $studentInformation{'middlename'},
                     }                                           $studentInformation{'lastname'},
                     unless ($ncount==4) { $rowlabel.=',""'; }                                           $studentInformation{'generation'})
                     $rowlabel=~s/\"$//;                                          ).'"';
    }  
                 }  
  $currentlist{&Apache::lonnet::unescape($name)}=$rowlabel;  
             }  
         } # end of foreach (split(/\&/,$classlst))  
 #  
 # -------------------- Find discrepancies between the course row table and this  
 #  
         my %f=&getformulas($safeeval);  
         my $changed=0;  
   
         my $maxrow=0;  
         my %existing=();  
   
 # ----------------------------------------------------------- Now obsolete rows  
  foreach (keys(%f)) {  
     if ($_=~/^A(\d+)/) {  
                 $maxrow=($1>$maxrow)?$1:$maxrow;  
                 $existing{$f{$_}}=1;  
  unless ((defined($currentlist{$f{$_}})) || (!$1)) {  
    $f{$_}='!!! Obsolete';  
                    $changed=1;  
                 }                  }
    #           }
               $currentlist{$student}=$rowlabel;
           } # end of if ($active)
       } # end of foreach my $student (keys(%classlist))
       #
       # Find discrepancies between the course row table and this
       #
       my %f=&getformulas($safeeval);
       my $changed=0;
       #
       my $maxrow=0;
       my %existing=();
       #
       # Now obsolete rows
       foreach (keys(%f)) {
           if ($_=~/^A(\d+)/) {
               $maxrow=($1>$maxrow)?$1:$maxrow;
               $existing{$f{$_}}=1;
               unless ((defined($currentlist{$f{$_}})) || (!$1) ||
                       ($f{$_}=~/^(\~\~\~|\-\-\-)/)) {
                   $f{$_}='!!! Obsolete';
                   $changed=1;
             }              }
         }          }
       }
 # -------------------------------------------------------- New and unknown keys      #
            # New and unknown keys
         foreach (sort keys(%currentlist)) {      foreach (sort keys(%currentlist)) {
             unless ($existing{$_}) {          unless ($existing{$_}) {
  $changed=1;              $changed=1;
                 $maxrow++;              $maxrow++;
                 $f{'A'.$maxrow}=$_;              $f{'A'.$maxrow}=$_;
             }  
         }          }
        
         if ($changed) { &setformulas($safeeval,%f); }  
   
         &setmaxrow($safeeval,$maxrow);  
         &setrowlabels($safeeval,%currentlist);  
   
     } else {  
         return 'Could not access course data';  
     }      }
       if ($changed) { &setformulas($safeeval,%f); }
       #
       &setmaxrow($safeeval,$maxrow);
       &setrowlabels($safeeval,%currentlist);
 }  }
   
 # ----------------------------------- Update rows for student and assess sheets  # ----------------------------------- Update rows for student and assess sheets
   
 sub updatestudentassesssheet {  sub updatestudentassesssheet {
     my $safeeval=shift;      my ($safeeval,$sheetdata) = @_;
     my %bighash;      my %bighash;
     my $stype=&gettype($safeeval);      my $stype=$sheetdata->{'sheettype'};
       my $uname=$sheetdata->{'uname'};
       my $udom =$sheetdata->{'udom'};
     my %current=();      my %current=();
     unless ($updatedata{$ENV{'request.course.fn'}.'_'.$stype}) {      if  ($updatedata
 # -------------------------------------------------------------------- Tie hash           {$ENV{'request.course.fn'}.'_'.$stype.'_'.$uname.'_'.$udom}) {
       if (tie(%bighash,'GDBM_File',$ENV{'request.course.fn'}.'.db',          %current=split(/\_\_\_\;\_\_\_/,
                        &GDBM_READER,0640)) {                         $updatedata{$ENV{'request.course.fn'}.
 # --------------------------------------------------------- Get all assessments                                         '_'.$stype.'_'.$uname.'_'.$udom});
       } else {
  my %allkeys=('timestamp' =>           # Tie hash
           tie(%bighash,'GDBM_File',$ENV{'request.course.fn'}.'.db',
               &GDBM_READER(),0640);
           if (! tied(%bighash)) {
               return 'Could not access course data';
           }
           # Get all assessments
           my %allkeys=('timestamp' => 
                      'Timestamp of Last Transaction<br>timestamp',                       'Timestamp of Last Transaction<br>timestamp',
                      'subnumber' =>                       'subnumber' =>
                      'Number of Submissions<br>subnumber',                       'Number of Submissions<br>subnumber',
Line 1591  sub updatestudentassesssheet { Line 1801  sub updatestudentassesssheet {
                      'Number of Tutor Responses<br>tutornumber',                       'Number of Tutor Responses<br>tutornumber',
                      'totalpoints' =>                       'totalpoints' =>
                      'Total Points Granted<br>totalpoints');                       'Total Points Granted<br>totalpoints');
   
         my $adduserstr='';          my $adduserstr='';
         if ((&getuname($safeeval) ne $ENV{'user.name'}) ||          if (($uname ne $ENV{'user.name'}) || ($udom ne $ENV{'user.domain'})){
             (&getudom($safeeval) ne $ENV{'user.domain'})) {              $adduserstr='&uname='.$uname.'&udom='.$udom;
             $adduserstr='&uname='.&getuname($safeeval).          }
  '&udom='.&getudom($safeeval);          my %allassess =
         }              ('_feedback' =>'<a href="/adm/assesscalc?usymb=_feedback'.
                $adduserstr.'">Feedback</a>',
         my %allassess=('_feedback' =>               '_evaluation' =>'<a href="/adm/assesscalc?usymb=_evaluation'.
               '<a href="/adm/assesscalc?usymb=_feedback'.$adduserstr.               $adduserstr.'">Evaluation</a>',
                        '">Feedback</a>',               '_tutoring' =>'<a href="/adm/assesscalc?usymb=_tutoring'.
                        '_evaluation' =>               $adduserstr.'">Tutoring</a>',
               '<a href="/adm/assesscalc?usymb=_evaluation'.$adduserstr.               '_discussion' =>'<a href="/adm/assesscalc?usymb=_discussion'.
                        '">Evaluation</a>',               $adduserstr.'">Discussion</a>'
                        '_tutoring' =>               );
               '<a href="/adm/assesscalc?usymb=_tutoring'.$adduserstr.          while (($_,undef) = each(%bighash)) {
                        '">Tutoring</a>',              next if ($_!~/^src\_(\d+)\.(\d+)$/);
                        '_discussion' =>              my $mapid=$1;
               '<a href="/adm/assesscalc?usymb=_discussion'.$adduserstr.              my $resid=$2;
                        '">Discussion</a>'              my $id=$mapid.'.'.$resid;
         );              my $srcf=$bighash{$_};
               if ($srcf=~/\.(problem|exam|quiz|assess|survey|form)$/) {
         foreach (keys(%bighash)) {                  my $symb=
     if ($_=~/^src\_(\d+)\.(\d+)$/) {                      &Apache::lonnet::declutter($bighash{'map_id_'.$mapid}).
        my $mapid=$1;                          '___'.$resid.'___'.&Apache::lonnet::declutter($srcf);
                my $resid=$2;                  $allassess{$symb}=
                my $id=$mapid.'.'.$resid;                      '<a href="/adm/assesscalc?usymb='.$symb.$adduserstr.'">'.
                my $srcf=$bighash{$_};                          $bighash{'title_'.$id}.'</a>';
                if ($srcf=~/\.(problem|exam|quiz|assess|survey|form)$/) {                  next if ($stype ne 'assesscalc');
                  my $symb=                  foreach my $key (split(/\,/,
                      &Apache::lonnet::declutter($bighash{'map_id_'.$mapid}).                                         &Apache::lonnet::metadata($srcf,'keys')
     '___'.$resid.'___'.                                         )) {
     &Apache::lonnet::declutter($srcf);                      next if ($key !~ /^(stores|parameter)_/);
  $allassess{$symb}=                      my $display=
             '<a href="/adm/assesscalc?usymb='.$symb.$adduserstr.'">'.                          &Apache::lonnet::metadata($srcf,$key.'.display');
                      $bighash{'title_'.$id}.'</a>';                      unless ($display) {
                  if ($stype eq 'assesscalc') {                          $display.=
      foreach (split(/\,/,                              &Apache::lonnet::metadata($srcf,$key.'.name');
     &Apache::lonnet::metadata($srcf,'keys'))) {                      }
                        if (($_=~/^stores\_(.*)/) || ($_=~/^parameter\_(.*)/)) {                      $display.='<br>'.$key;
   my $key=$_;                      $allkeys{$key}=$display;
                           my $display=                  } # end of foreach
       &Apache::lonnet::metadata($srcf,$key.'.display');              }
                           unless ($display) {  
                               $display.=  
          &Apache::lonnet::metadata($srcf,$key.'.name');  
                           }  
                           $display.='<br>'.$key;  
                           $allkeys{$key}=$display;  
        }  
                    } # end of foreach  
          }  
       }  
    }  
         } # end of foreach (keys(%bighash))          } # end of foreach (keys(%bighash))
         untie(%bighash);          untie(%bighash);
               #
 #          # %allkeys has a list of storage and parameter displays by unikey
 # %allkeys has a list of storage and parameter displays by unikey          # %allassess has a list of all resource displays by symb
 # %allassess has a list of all resource displays by symb          #
 #  
   
         if ($stype eq 'assesscalc') {          if ($stype eq 'assesscalc') {
     %current=%allkeys;              %current=%allkeys;
         } elsif ($stype eq 'studentcalc') {          } elsif ($stype eq 'studentcalc') {
             %current=%allassess;              %current=%allassess;
         }          }
         $updatedata{$ENV{'request.course.fn'}.'_'.$stype}=          $updatedata{$ENV{'request.course.fn'}.'_'.$stype.'_'.$uname.'_'.$udom}=
     join('___;___',%current);              join('___;___',%current);
     } else {          # Get current from cache
         return 'Could not access course data';  
     }      }
 # ------------------------------------------------------ Get current from cache      # Find discrepancies between the course row table and this
     } else {      #
         %current=split(/\_\_\_\;\_\_\_/,      my %f=&getformulas($safeeval);
        $updatedata{$ENV{'request.course.fn'}.'_'.$stype});      my $changed=0;
     }  
 # -------------------- Find discrepancies between the course row table and this  
 #  
         my %f=&getformulas($safeeval);  
         my $changed=0;  
   
         my $maxrow=0;  
         my %existing=();  
   
 # ----------------------------------------------------------- Now obsolete rows  
  foreach (keys(%f)) {  
     if ($_=~/^A(\d+)/) {  
                 $maxrow=($1>$maxrow)?$1:$maxrow;  
                 my ($usy,$ufn)=split(/\_\_\&\&\&\_\_/,$f{$_});  
                 $existing{$usy}=1;  
  unless ((defined($current{$usy})) || (!$1)) {  
    $f{$_}='!!! Obsolete';  
                    $changed=1;  
         } elsif ($ufn) {  
     $current{$usy}  
                        =~s/assesscalc\?usymb\=/assesscalc\?ufn\=$ufn\&usymb\=/;  
                 }  
             }  
         }  
   
 # -------------------------------------------------------- New and unknown keys  
        
         foreach (keys(%current)) {  
             unless ($existing{$_}) {  
  $changed=1;  
                 $maxrow++;  
                 $f{'A'.$maxrow}=$_;  
             }  
         }  
           
         if ($changed) { &setformulas($safeeval,%f); }      my $maxrow=0;
       my %existing=();
         &setmaxrow($safeeval,$maxrow);      # Now obsolete rows
         &setrowlabels($safeeval,%current);      foreach (keys(%f)) {
            next if ($_!~/^A(\d+)/);
         undef %current;          $maxrow=($1>$maxrow)?$1:$maxrow;
         undef %existing;          my ($usy,$ufn)=split(/\_\_\&\&\&\_\_/,$f{$_});
           $existing{$usy}=1;
           unless ((defined($current{$usy})) || (!$1) ||
                   ($f{$_}=~/^(\~\~\~|\-\-\-)/)){
               $f{$_}='!!! Obsolete';
               $changed=1;
           } elsif ($ufn) {
               $current{$usy}
               =~s/assesscalc\?usymb\=/assesscalc\?ufn\=$ufn\&usymb\=/;
           }
       }
       # New and unknown keys
       foreach (keys(%current)) {
           unless ($existing{$_}) {
               $changed=1;
               $maxrow++;
               $f{'A'.$maxrow}=$_;
           }
       }
       if ($changed) { &setformulas($safeeval,%f); }
       &setmaxrow($safeeval,$maxrow);
       &setrowlabels($safeeval,%current);
       #
       undef %current;
       undef %existing;
 }  }
   
 # ------------------------------------------------ Load data for one assessment  # ------------------------------------------------ Load data for one assessment
   
 sub loadstudent {  sub loadstudent {
     my $safeeval=shift;      my ($safeeval,$sheetdata)=@_;
     my %c=();      my %c=();
     my %f=&getformulas($safeeval);      my %f=&getformulas($safeeval);
     $cachedassess=&getuname($safeeval).':'.&getudom($safeeval);      $cachedassess=$sheetdata->{'uname'}.':'.$sheetdata->{'udom'};
     %cachedstores=();      # Get ALL the student preformance data
     {      my @tmp = &Apache::lonnet::dump($sheetdata->{'cid'},
       my $reply=&Apache::lonnet::reply('dump:'.&getudom($safeeval).':'.                                      $sheetdata->{'udom'},
                                                &getuname($safeeval).':'.                                      $sheetdata->{'uname'},
                                                &getcid($safeeval),                                      undef);
                                                &getuhome($safeeval));      if ($tmp[0] !~ /^error:/) {
       unless ($reply=~/^error\:/) {          %cachedstores = @tmp;
  foreach ( split(/\&/,$reply)) {  
             my ($name,$value)=split(/\=/,$_);  
             $cachedstores{&Apache::lonnet::unescape($name)}=  
                   &Apache::lonnet::unescape($value);  
  }  
       }  
     }      }
       undef @tmp;
       # 
     my @assessdata=();      my @assessdata=();
     foreach (keys(%f)) {      foreach (keys(%f)) {
  if ($_=~/^A(\d+)/) {   next if ($_!~/^A(\d+)/);
    my $row=$1;          my $row=$1;
            unless (($f{$_}=~/^\!/) || ($row==0)) {          next if (($f{$_}=~/^[\!\~\-]/) || ($row==0));
       my ($usy,$ufn)=split(/\_\_\&\&\&\_\_/,$f{$_});          my ($usy,$ufn)=split(/__&&&\__/,$f{$_});
       @assessdata=&exportsheet(&getuname($safeeval),          @assessdata=&exportsheet($sheetdata->{'uname'},
                                        &getudom($safeeval),                                   $sheetdata->{'udom'},
                                        'assesscalc',$usy,$ufn);                                   'assesscalc',$usy,$ufn);
               my $index=0;          my $index=0;
               foreach ('A','B','C','D','E','F','G','H','I','J','K','L','M',          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') {                   'N','O','P','Q','R','S','T','U','V','W','X','Y','Z') {
                   if ($assessdata[$index]) {              if ($assessdata[$index]) {
      my $col=$_;                  my $col=$_;
      if ($assessdata[$index]=~/\D/) {                  if ($assessdata[$index]=~/\D/) {
                          $c{$col.$row}="'".$assessdata[$index]."'";                      $c{$col.$row}="'".$assessdata[$index]."'";
       } else {                  } else {
          $c{$col.$row}=$assessdata[$index];                      $c{$col.$row}=$assessdata[$index];
      }                  }
                      unless ($col eq 'A') {                   unless ($col eq 'A') { 
  $f{$col.$row}='import';                      $f{$col.$row}='import';
                      }                  }
   }              }
                   $index++;              $index++;
               }  
    }  
         }          }
     }      }
     $cachedassess='';      $cachedassess='';
Line 1767  sub loadstudent { Line 1946  sub loadstudent {
 }  }
   
 # --------------------------------------------------- Load data for one student  # --------------------------------------------------- Load data for one student
   #
 sub loadcourse {  sub loadcourse {
     my ($safeeval,$r)=@_;      my ($safeeval,$sheetdata,$r)=@_;
     my %c=();      my %c=();
     my %f=&getformulas($safeeval);      my %f=&getformulas($safeeval);
     my $total=0;      my $total=0;
     foreach (keys(%f)) {      foreach (keys(%f)) {
  if ($_=~/^A(\d+)/) {   if ($_=~/^A(\d+)/) {
     unless ($f{$_}=~/^\!/) { $total++; }      unless ($f{$_}=~/^[\!\~\-]/) { $total++; }
         }          }
     }      }
     my $now=0;      my $now=0;
Line 1793  sub loadcourse { Line 1972  sub loadcourse {
 ENDPOP  ENDPOP
     $r->rflush();      $r->rflush();
     foreach (keys(%f)) {      foreach (keys(%f)) {
  if ($_=~/^A(\d+)/) {   next if ($_!~/^A(\d+)/);
    my $row=$1;          my $row=$1;
            unless (($f{$_}=~/^\!/)  || ($row==0)) {          next if (($f{$_}=~/^[\!\~\-]/)  || ($row==0));
       my @studentdata=&exportsheet(split(/\:/,$f{$_}),          my @studentdata=&exportsheet(split(/\:/,$f{$_}),
                                            'studentcalc');                                       'studentcalc');
               undef %userrdatas;          undef %userrdatas;
               $now++;          $now++;
               $r->print('<script>popwin.document.popremain.remaining.value="'.          $r->print('<script>popwin.document.popremain.remaining.value="'.
                   $now.'/'.$total.': '.int((time-$since)/$now*($total-$now)).                    $now.'/'.$total.': '.int((time-$since)/$now*($total-$now)).
                         ' secs remaining";</script>');                    ' secs remaining";</script>');
               $r->rflush();           $r->rflush(); 
           #
               my $index=0;          my $index=0;
              foreach ('A','B','C','D','E','F','G','H','I','J','K','L','M',          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') {                   'N','O','P','Q','R','S','T','U','V','W','X','Y','Z') {
                   if ($studentdata[$index]) {              if ($studentdata[$index]) {
      my $col=$_;                  my $col=$_;
      if ($studentdata[$index]=~/\D/) {                  if ($studentdata[$index]=~/\D/) {
                          $c{$col.$row}="'".$studentdata[$index]."'";                      $c{$col.$row}="'".$studentdata[$index]."'";
       } else {                  } else {
          $c{$col.$row}=$studentdata[$index];                      $c{$col.$row}=$studentdata[$index];
      }                  }
                      unless ($col eq 'A') {                   unless ($col eq 'A') { 
  $f{$col.$row}='import';                      $f{$col.$row}='import';
                      }                  }
   }                  $index++;
                   $index++;              }
               }  
    }  
         }          }
     }      }
     &setformulas($safeeval,%f);      &setformulas($safeeval,%f);
Line 1831  ENDPOP Line 2008  ENDPOP
 }  }
   
 # ------------------------------------------------ Load data for one assessment  # ------------------------------------------------ Load data for one assessment
   #
 sub loadassessment {  sub loadassessment {
     my $safeeval=shift;      my ($safeeval,$sheetdata)=@_;
   
     my $uhome=&getuhome($safeeval);      my $uhome = $sheetdata->{'uhome'};
     my $uname=&getuname($safeeval);      my $uname = $sheetdata->{'uname'};
     my $udom=&getudom($safeeval);      my $udom  = $sheetdata->{'udom'};
     my $symb=&getusymb($safeeval);      my $symb  = $sheetdata->{'usymb'};
     my $cid=&getcid($safeeval);      my $cid   = $sheetdata->{'cid'};
     my $cnum=&getcnum($safeeval);      my $cnum  = $sheetdata->{'cnum'};
     my $cdom=&getcdom($safeeval);      my $cdom  = $sheetdata->{'cdom'};
     my $chome=&getchome($safeeval);      my $chome = $sheetdata->{'chome'};
   
     my $namespace;      my $namespace;
     unless ($namespace=$cid) { return ''; }      unless ($namespace=$cid) { return ''; }
       # Get stored values
 # ----------------------------------------------------------- Get stored values      my %returnhash=();
       if ($cachedassess eq $uname.':'.$udom) {
    my %returnhash=();          #
           # get data out of the dumped stores
    if ($cachedassess eq $uname.':'.$udom) {          # 
 #          my $version=$cachedstores{'version:'.$symb};
 # get data out of the dumped stores          my $scope;
 #           for ($scope=1;$scope<=$version;$scope++) {
               foreach (split(/\:/,$cachedstores{$scope.':keys:'.$symb})) {
        my $version=$cachedstores{'version:'.$symb};                  $returnhash{$_}=$cachedstores{$scope.':'.$symb.':'.$_};
        my $scope;              } 
        for ($scope=1;$scope<=$version;$scope++) {          }
            foreach (split(/\:/,$cachedstores{$scope.':keys:'.$symb})) {      } else {
                $returnhash{$_}=$cachedstores{$scope.':'.$symb.':'.$_};          #
            }           # restore individual
        }          #
           %returnhash = &Apache::lonnet::restore($symb,$namespace,$udom,$uname);
    } else {          for (my $version=1;$version<=$returnhash{'version'};$version++) {
 #              foreach (split(/\:/,$returnhash{$version.':keys'})) {
 # restore individual                  $returnhash{$_}=$returnhash{$version.':'.$_};
 #              } 
           }
     my $answer=&Apache::lonnet::reply(  
        "restore:$udom:$uname:".  
        &Apache::lonnet::escape($namespace).":".  
        &Apache::lonnet::escape($symb),$uhome);  
     foreach (split(/\&/,$answer)) {  
  my ($name,$value)=split(/\=/,$_);  
         $returnhash{&Apache::lonnet::unescape($name)}=  
                     &Apache::lonnet::unescape($value);  
     }  
     my $version;  
     for ($version=1;$version<=$returnhash{'version'};$version++) {  
        foreach (split(/\:/,$returnhash{$version.':keys'})) {  
           $returnhash{$_}=$returnhash{$version.':'.$_};  
        }   
     }      }
    }      #
 # ----------------------------- 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
 # --------- convert all "_" to "." to be able to use libraries, multiparts, etc      #
       # This is dumb.  It is also necessary :(
     my @oldkeys=keys %returnhash;      my @oldkeys=keys %returnhash;
       #
     foreach (@oldkeys) {      foreach (@oldkeys) {
         my $name=$_;          my $name=$_;
         my $value=$returnhash{$_};          my $value=$returnhash{$_};
Line 1898  sub loadassessment { Line 2061  sub loadassessment {
         $name=~s/\_/\./g;          $name=~s/\_/\./g;
         $returnhash{$name}=$value;          $returnhash{$name}=$value;
     }      }
       # initialize coursedata and userdata for this user
 # ---------------------------- initialize coursedata and userdata for this user  
     undef %courseopt;      undef %courseopt;
     undef %useropt;      undef %useropt;
   
     my $userprefix=$uname.'_'.$udom.'_';      my $userprefix=$uname.'_'.$udom.'_';
       
     unless ($uhome eq 'no_host') {       unless ($uhome eq 'no_host') { 
 # -------------------------------------------------------------- Get coursedata          # Get coursedata
       unless          unless ((time-$courserdatas{$cid.'.last_cache'})<240) {
         ((time-$courserdatas{$cid.'.last_cache'})<240) {              my $reply=&Apache::lonnet::reply('dump:'.$cdom.':'.$cnum.
          my $reply=&Apache::lonnet::reply('dump:'.$cdom.':'.$cnum.                                               ':resourcedata',$chome);
               ':resourcedata',$chome);              if ($reply!~/^error\:/) {
          if ($reply!~/^error\:/) {                  $courserdatas{$cid}=$reply;
             $courserdatas{$cid}=$reply;                  $courserdatas{$cid.'.last_cache'}=time;
             $courserdatas{$cid.'.last_cache'}=time;              }
          }          }
       }          foreach (split(/\&/,$courserdatas{$cid})) {
       foreach (split(/\&/,$courserdatas{$cid})) {              my ($name,$value)=split(/\=/,$_);
          my ($name,$value)=split(/\=/,$_);              $courseopt{$userprefix.&Apache::lonnet::unescape($name)}=
          $courseopt{$userprefix.&Apache::lonnet::unescape($name)}=                  &Apache::lonnet::unescape($value);  
                     &Apache::lonnet::unescape($value);            }
       }          # Get userdata (if present)
 # --------------------------------------------------- Get userdata (if present)          unless ((time-$userrdatas{$uname.'___'.$udom.'.last_cache'})<240) {
       unless              my $reply=
         ((time-$userrdatas{$uname.'___'.$udom.'.last_cache'})<240) {                  &Apache::lonnet::reply('dump:'.$udom.':'.$uname.':resourcedata',$uhome);
          my $reply=              if ($reply!~/^error\:/) {
        &Apache::lonnet::reply('dump:'.$udom.':'.$uname.':resourcedata',$uhome);                  $userrdatas{$uname.'___'.$udom}=$reply;
          if ($reply!~/^error\:/) {                  $userrdatas{$uname.'___'.$udom.'.last_cache'}=time;
      $userrdatas{$uname.'___'.$udom}=$reply;              }
      $userrdatas{$uname.'___'.$udom.'.last_cache'}=time;          }
          }          foreach (split(/\&/,$userrdatas{$uname.'___'.$udom})) {
       }              my ($name,$value)=split(/\=/,$_);
       foreach (split(/\&/,$userrdatas{$uname.'___'.$udom})) {              $useropt{$userprefix.&Apache::lonnet::unescape($name)}=
          my ($name,$value)=split(/\=/,$_);                  &Apache::lonnet::unescape($value);
          $useropt{$userprefix.&Apache::lonnet::unescape($name)}=          }
           &Apache::lonnet::unescape($value);  
       }  
     }      }
 # ----------------- now courseopt, useropt initialized for this user and course      # now courseopt, useropt initialized for this user and course
 # (used by parmval)      # (used by parmval)
       #
 #      # Load keys for this assessment only
 # Load keys for this assessment only      #
 #  
     my %thisassess=();      my %thisassess=();
     my ($symap,$syid,$srcf)=split(/\_\_\_/,$symb);      my ($symap,$syid,$srcf)=split(/\_\_\_/,$symb);
       
     foreach (split(/\,/,&Apache::lonnet::metadata($srcf,'keys'))) {      foreach (split(/\,/,&Apache::lonnet::metadata($srcf,'keys'))) {
         $thisassess{$_}=1;          $thisassess{$_}=1;
     }       } 
 #      #
 # Load parameters      # Load parameters
 #      #
    my %c=();      my %c=();
       if (tie(%parmhash,'GDBM_File',
    if (tie(%parmhash,'GDBM_File',              &getcoursefilename($safeeval).'_parms.db',&GDBM_READER(),0640)) {
            &getcfn($safeeval).'_parms.db',&GDBM_READER,0640)) {          my %f=&getformulas($safeeval);
     my %f=&getformulas($safeeval);          foreach (keys(%f))  {
     foreach (keys(%f))  {              next if ($_!~/^A/);
  if ($_=~/^A/) {              next if  ($f{$_}=~/^[\!\~\-]/);
             unless ($f{$_}=~/^\!/) {              if ($f{$_}=~/^parameter/) {
         if ($f{$_}=~/^parameter/) {                  if ($thisassess{$f{$_}}) {
  if ($thisassess{$f{$_}}) {                      my $val=&parmval($f{$_},$safeeval,$sheetdata);
                   my $val=&parmval($f{$_},$safeeval);                      $c{$_}=$val;
                   $c{$_}=$val;                      $c{$f{$_}}=$val;
                   $c{$f{$_}}=$val;                  }
         }              } else {
        } else {                  my $key=$f{$_};
   my $key=$f{$_};                  my $ckey=$key;
                   my $ckey=$key;                  $key=~s/^stores\_/resource\./;
                   $key=~s/^stores\_/resource\./;                  $key=~s/\_/\./g;
                   $key=~s/\_/\./g;                  $c{$_}=$returnhash{$key};
            $c{$_}=$returnhash{$key};                  $c{$ckey}=$returnhash{$key};
                   $c{$ckey}=$returnhash{$key};              }
        }  
    }  
         }          }
           untie(%parmhash);
     }      }
     untie(%parmhash);      &setconstants($safeeval,%c);
    }  
    &setconstants($safeeval,%c);  
 }  }
   
 # --------------------------------------------------------- Various form fields  # --------------------------------------------------------- Various form fields
Line 1987  sub loadassessment { Line 2142  sub loadassessment {
 sub textfield {  sub textfield {
     my ($title,$name,$value)=@_;      my ($title,$name,$value)=@_;
     return "\n<p><b>$title:</b><br>".      return "\n<p><b>$title:</b><br>".
            '<input type=text name="'.$name.'" size=80 value="'.$value.'">';          '<input type=text name="'.$name.'" size=80 value="'.$value.'">';
 }  }
   
 sub hiddenfield {  sub hiddenfield {
Line 2012  sub selectbox { Line 2167  sub selectbox {
 #  #
   
 sub updatesheet {  sub updatesheet {
     my $safeeval=shift;      my ($safeeval,$sheetdata)=@_;
     my $stype=&gettype($safeeval);      my $stype=$sheetdata->{'sheettype'};
     if ($stype eq 'classcalc') {      if ($stype eq 'classcalc') {
  return &updateclasssheet($safeeval);   return &updateclasssheet($safeeval,$sheetdata);
     } else {      } else {
         return &updatestudentassesssheet($safeeval);          return &updatestudentassesssheet($safeeval,$sheetdata);
     }      }
 }  }
   
Line 2027  sub updatesheet { Line 2182  sub updatesheet {
 #  #
   
 sub loadrows {  sub loadrows {
     my ($safeeval,$r)=@_;      my ($safeeval,$sheetdata,$r)=@_;
     my $stype=&gettype($safeeval);      my $stype=$sheetdata->{'sheettype'};
     if ($stype eq 'classcalc') {      if ($stype eq 'classcalc') {
  &loadcourse($safeeval,$r);   &loadcourse($safeeval,$sheetdata,$r);
     } elsif ($stype eq 'studentcalc') {      } elsif ($stype eq 'studentcalc') {
         &loadstudent($safeeval);          &loadstudent($safeeval,$sheetdata);
     } else {      } else {
         &loadassessment($safeeval);          &loadassessment($safeeval,$sheetdata);
     }      }
 }  }
   
Line 2044  sub checkthis { Line 2199  sub checkthis {
     my ($keyname,$time)=@_;      my ($keyname,$time)=@_;
     return ($time<$expiredates{$keyname});      return ($time<$expiredates{$keyname});
 }  }
   
 sub forcedrecalc {  sub forcedrecalc {
     my ($uname,$udom,$stype,$usymb)=@_;      my ($uname,$udom,$stype,$usymb)=@_;
     my $key=$uname.':'.$udom.':'.$stype.':'.$usymb;      my $key=$uname.':'.$udom.':'.$stype.':'.$usymb;
Line 2075  sub forcedrecalc { Line 2231  sub forcedrecalc {
 #  #
   
 sub exportsheet {  sub exportsheet {
  my ($uname,$udom,$stype,$usymb,$fn)=@_;      my ($uname,$udom,$stype,$usymb,$fn)=@_;
  my @exportarr=();      my @exportarr=();
       if (($usymb=~/^\_(\w+)/) && (!$fn)) {
  if (($usymb=~/^\_(\w+)/) && (!$fn)) {          $fn='default_'.$1;
     $fn='default_'.$1;      }
  }      #
       # Check if cached
 #      #
 # Check if cached      my $key=$uname.':'.$udom.':'.$stype.':'.$usymb;
 #      my $found='';
       if ($oldsheets{$key}) {
  my $key=$uname.':'.$udom.':'.$stype.':'.$usymb;          foreach (split(/\_\_\_\&\_\_\_/,$oldsheets{$key})) {
  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(/\_\_\_\=\_\_\_/,$_);              my ($name,$value)=split(/\_\_\_\=\_\_\_/,$_);
             if ($name eq $fn) {              if ($name eq $fn) {
         $found=$value;                  $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);  
     &updatesheet($thissheet);  
     &loadrows($thissheet);  
     &calcsheet($thissheet);   
     @exportarr=&exportdata($thissheet);  
 #  
 # Store now  
 #  
     my $cid=$ENV{'request.course.id'};   
     my $current='';  
     if ($stype eq 'studentcalc') {  
        $current=&Apache::lonnet::reply('get:'.  
                                      $ENV{'course.'.$cid.'.domain'}.':'.  
                                      $ENV{'course.'.$cid.'.num'}.  
      ':nohist_calculatedsheets:'.  
                                      &Apache::lonnet::escape($key),  
                                      $ENV{'course.'.$cid.'.home'});  
     } else {  
        $current=&Apache::lonnet::reply('get:'.  
                                      &getudom($thissheet).':'.  
                                      &getuname($thissheet).  
      ':nohist_calculatedsheets_'.  
                                      $ENV{'request.course.id'}.':'.  
                                      &Apache::lonnet::escape($key),  
                                      &getuhome($thissheet));  
   
     }      }
     my %currentlystored=();      unless ($found) {
     unless ($current=~/^error\:/) {          &cachedssheets($uname,$udom,&Apache::lonnet::homeserver($uname,$udom));
        foreach (split(/\_\_\_\&\_\_\_/,&Apache::lonnet::unescape($current))) {          if ($oldsheets{$key}) {
            my ($name,$value)=split(/\_\_\_\=\_\_\_/,$_);              foreach (split(/\_\_\_\&\_\_\_/,$oldsheets{$key})) {
            $currentlystored{$name}=$value;                  my ($name,$value)=split(/\_\_\_\=\_\_\_/,$_);
        }                  if ($name eq $fn) {
                       $found=$value;
                   }
               } 
           }
     }      }
     $currentlystored{$fn}=join('___;___',@exportarr);      #
       # Check if still valid
     my $newstore='';      #
     foreach (keys(%currentlystored)) {      if ($found) {
         if ($newstore) { $newstore.='___&___'; }          if (&forcedrecalc($uname,$udom,$stype,$usymb)) {
         $newstore.=$_.'___=___'.$currentlystored{$_};              $found='';
           }
       }
       if ($found) {
           #
           # Return what was cached
           #
           @exportarr=split(/\_\_\_\;\_\_\_/,$found);
       } else {
           #
           # Not cached
           #        
           my ($thissheet,$sheetdata)=&makenewsheet($uname,$udom,$stype,$usymb);
           &readsheet($thissheet,$sheetdata,$fn);
           &updatesheet($thissheet,$sheetdata);
           &loadrows($thissheet,$sheetdata);
           &calcsheet($thissheet,$sheetdata); 
           @exportarr=&exportdata($thissheet,$sheetdata);
           #
           # 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:'.$sheetdata->{'udom'}.':'.
                                               $sheetdata->{'uname'}.
                                               ':nohist_calculatedsheets_'.
                                               $ENV{'request.course.id'}.':'.
                                               &Apache::lonnet::escape($key),
                                               $sheetdata->{'uhome'});
           }
           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:'.
                                      $sheetdata->{'udom'}.':'.
                                      $sheetdata->{'uname'}.
                                      ':nohist_calculatedsheets_'.
                                      $ENV{'request.course.id'}.':'.
                                      &Apache::lonnet::escape($key).'='.
                                      &Apache::lonnet::escape($newstore).'&'.
                                      &Apache::lonnet::escape($key).'.time='.$now,
                                      $sheetdata->{'uhome'});
           }
     }      }
     my $now=time;      return @exportarr;
     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  # ============================================================ Expiration Dates
 #  #
 # Load previously cached student spreadsheets for this course  # Load previously cached student spreadsheets for this course
 #  #
   
 sub expirationdates {  sub expirationdates {
     undef %expiredates;      undef %expiredates;
     my $cid=$ENV{'request.course.id'};      my $cid=$ENV{'request.course.id'};
Line 2272  sub cachedssheets { Line 2418  sub cachedssheets {
 # Interactive call to screen  # Interactive call to screen
 #  #
 #  #
   
   
 sub handler {  sub handler {
     my $r=shift;      my $r=shift;
   
     if ($r->header_only) {      my $loaderror=&Apache::lonnet::overloaderror($r);
       $r->content_type('text/html');      if ($loaderror) { return $loaderror; }
       $r->send_http_header;      $loaderror= &Apache::lonnet::overloaderror($r,
       return OK;                        $ENV{'course.'.$ENV{'request.course.id'}.'.home'});
     }      if ($loaderror) { return $loaderror; } 
   
 # ---------------------------------------------------- Global directory configs  
   
 $includedir=$r->dir_config('lonIncludes');  
 $tmpdir=$r->dir_config('lonDaemons').'/tmp/';  
   
 # ----------------------------------------------------- Needs to be in a course  
   
   if ($ENV{'request.course.fn'}) {   
   
 # --------------------------- Get query string for limited number of parameters      if ($r->header_only) {
           $r->content_type('text/html');
     foreach (split(/&/,$ENV{'QUERY_STRING'})) {          $r->send_http_header;
        my ($name, $value) = split(/=/,$_);          return OK;
        $value =~ tr/+/ /;      }
        $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;      # Global directory configs
        if (($name eq 'uname') || ($name eq 'udom') ||       $includedir = $r->dir_config('lonIncludes');
            ($name eq 'usymb') || ($name eq 'ufn')) {      $tmpdir = $r->dir_config('lonDaemons').'/tmp/';
            unless ($ENV{'form.'.$name}) {      # Needs to be in a course
               $ENV{'form.'.$name}=$value;      if (! $ENV{'request.course.fn'}) { 
    }          # Not in a course, or not allowed to modify parms
        }          $ENV{'user.error.msg'}=
               $r->uri.":opa:0:0:Cannot modify spreadsheet";
           return HTTP_NOT_ACCEPTABLE; 
       }
       # Get query string for limited number of parameters
       &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
                                               ['uname','udom','usymb','ufn']);
       if ($ENV{'request.role'} =~ /^st\./) {
           delete $ENV{'form.unewfield'}   if (exists($ENV{'form.unewfield'}));
           delete $ENV{'form.unewformula'} if (exists($ENV{'form.unewformula'}));
     }      }
   
     if (($ENV{'form.usymb'}=~/^\_(\w+)/) && (!$ENV{'form.ufn'})) {      if (($ENV{'form.usymb'}=~/^\_(\w+)/) && (!$ENV{'form.ufn'})) {
  $ENV{'form.ufn'}='default_'.$1;          $ENV{'form.ufn'}='default_'.$1;
     }      }
       # Interactive loading of specific sheet?
 # -------------------------------------- Interactive loading of specific sheet?  
     if (($ENV{'form.load'}) && ($ENV{'form.loadthissheet'} ne 'Default')) {      if (($ENV{'form.load'}) && ($ENV{'form.loadthissheet'} ne 'Default')) {
  $ENV{'form.ufn'}=$ENV{'form.loadthissheet'};          $ENV{'form.ufn'}=$ENV{'form.loadthissheet'};
     }      }
 # ------------------------------------------- Nothing there? Must be login user      #
       # Determine the user name and domain for the sheet.
     my $aname;      my $aname;
     my $adom;      my $adom;
   
     unless ($ENV{'form.uname'}) {      unless ($ENV{'form.uname'}) {
  $aname=$ENV{'user.name'};          $aname=$ENV{'user.name'};
         $adom=$ENV{'user.domain'};          $adom=$ENV{'user.domain'};
     } else {      } else {
         $aname=$ENV{'form.uname'};          $aname=$ENV{'form.uname'};
         $adom=$ENV{'form.udom'};          $adom=$ENV{'form.udom'};
     }      }
       #
 # ------------------------------------------------------------------- Open page      # Open page
   
     $r->content_type('text/html');      $r->content_type('text/html');
     $r->header_out('Cache-control','no-cache');      $r->header_out('Cache-control','no-cache');
     $r->header_out('Pragma','no-cache');      $r->header_out('Pragma','no-cache');
     $r->send_http_header;      $r->send_http_header;
       # Screen output
 # --------------------------------------------------------------- Screen output  
   
     $r->print('<html><head><title>LON-CAPA Spreadsheet</title>');      $r->print('<html><head><title>LON-CAPA Spreadsheet</title>');
     $r->print(<<ENDSCRIPT);      if ($ENV{'request.role'} !~ /^st\./) {
           $r->print(<<ENDSCRIPT);
 <script language="JavaScript">  <script language="JavaScript">
   
     function celledit(cn,cf) {      function celledit(cn,cf) {
         var cnf=prompt(cn,cf);          var cnf=prompt(cn,cf);
  if (cnf!=null) {          if (cnf!=null) {
     document.sheet.unewfield.value=cn;              document.sheet.unewfield.value=cn;
             document.sheet.unewformula.value=cnf;              document.sheet.unewformula.value=cnf;
             document.sheet.submit();              document.sheet.submit();
         }          }
Line 2355  $tmpdir=$r->dir_config('lonDaemons').'/t Line 2494  $tmpdir=$r->dir_config('lonDaemons').'/t
         document.sheet.submit();          document.sheet.submit();
     }      }
   
       function insertrow(cn) {
    document.sheet.unewfield.value='insertrow';
           document.sheet.unewformula.value=cn;
           document.sheet.submit();
       }
   
 </script>  </script>
 ENDSCRIPT  ENDSCRIPT
     $r->print('</head><body bgcolor="#FFFFFF">'.      }
        '<img align=right src=/adm/lonIcons/lonlogos.gif>'.      $r->print('</head>'.&Apache::loncommon::bodytag('Grades Spreadsheet').
        '<h1>LON-CAPA Spreadsheet</h1>'.                '<form action="'.$r->uri.'" name=sheet method=post>');
        '<form action="'.$r->uri.'" name=sheet method=post>'.      $r->print(&hiddenfield('uname',$ENV{'form.uname'}).
        &hiddenfield('uname',$ENV{'form.uname'}).                &hiddenfield('udom',$ENV{'form.udom'}).
        &hiddenfield('udom',$ENV{'form.udom'}).                &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?      # Full recalc?
   
   
     if ($ENV{'form.forcerecalc'}) {      if ($ENV{'form.forcerecalc'}) {
  $r->print('<h4>Completely Recalculating Sheet ...</h4>');          $r->print('<h4>Completely Recalculating Sheet ...</h4>');
         undef %spreadsheets;          undef %spreadsheets;
         undef %courserdatas;          undef %courserdatas;
         undef %userrdatas;          undef %userrdatas;
         undef %defaultsheets;          undef %defaultsheets;
         undef %updatedata;          undef %updatedata;
    }      }
        # Read new sheet or modified worksheet
 # ---------------------------------------- Read new sheet or modified worksheet  
   
     $r->uri=~/\/(\w+)$/;      $r->uri=~/\/(\w+)$/;
       my ($asheet,$asheetdata)=&makenewsheet($aname,$adom,$1,$ENV{'form.usymb'});
     my $asheet=&makenewsheet($aname,$adom,$1,$ENV{'form.usymb'});      #
       # If a new formula had been entered, go from work copy
 # ------------------------ 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($asheet,$ENV{'form.ufn'});          &setfilename($asheet,$ENV{'form.ufn'});
  &tmpread($asheet,          &tmpread($asheet,$ENV{'form.unewfield'},$ENV{'form.unewformula'});
                  $ENV{'form.unewfield'},$ENV{'form.unewformula'});      } elsif ($ENV{'form.saveas'}) {
   
      } elsif ($ENV{'form.saveas'}) {  
         &setfilename($asheet,$ENV{'form.ufn'});          &setfilename($asheet,$ENV{'form.ufn'});
  &tmpread($asheet);          &tmpread($asheet);
     } else {      } else {
         &readsheet($asheet,$ENV{'form.ufn'});          &readsheet($asheet,$asheetdata,$ENV{'form.ufn'});
     }      }
       # Print out user information
 # -------------------------------------------------- Print out user information      unless ($asheetdata->{'sheettype'} eq 'classcalc') {
           $r->print('<p><b>User:</b> '.$asheetdata->{'uname'}.
     unless (&gettype($asheet) eq 'classcalc') {                    '<br><b>Domain:</b> '.$asheetdata->{'udom'});
         $r->print('<p><b>User:</b> '.&getuname($asheet).  #        if (&getcsec($asheet) eq '-1') {
                   '<br><b>Domain:</b> '.&getudom($asheet));  #            $r->print('<h3><font color=red>'.
         if (&getcsec($asheet) eq '-1') {  #                      'Not a student in this course</font></h3>');
            $r->print('<h3><font color=red>'.  #        } else {
                      'Not a student in this course</font></h3>');              $r->print('<br><b>Section/Group:</b> '.$asheetdata->{'csec'});
         } else {  #        }
            $r->print('<br><b>Section/Group:</b> '.&getcsec($asheet));  
         }  
         if ($ENV{'form.usymb'}) {          if ($ENV{'form.usymb'}) {
            $r->print('<br><b>Assessment:</b> <tt>'.$ENV{'form.usymb'}.'</tt>');              $r->print('<br><b>Assessment:</b> <tt>'.
                         $ENV{'form.usymb'}.'</tt>');
         }          }
     }      }
       #
 # ---------------------------------------------------------------- Course title      # Check user permissions
       if (($asheetdata->{'sheettype'} eq 'classcalc'       ) || 
     $r->print('<h1>'.          ($asheetdata->{'uname'}     ne $ENV{'user.name'} ) ||
             $ENV{'course.'.$ENV{'request.course.id'}.'.description'}.          ($asheetdata->{'udom'}      ne $ENV{'user.domain'})) {
              '</h1><h3>'.localtime().'</h3>');          unless (&Apache::lonnet::allowed('vgr',$asheetdata->{'cid'})) {
               $r->print('<h1>Access Permission Denied</h1>'.
 # ---------------------------------------------------- See if user can see this                        '</form></body></html>');
   
     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;              return OK;
         }          }
     }      }
       # Additional options
 # ---------------------------------------------------------- Additional options      $r->print('<br />'.
                 '<input type="submit" name="forcerecalc" '.
     $r->print(                'value="Completely Recalculate Sheet"><p>');
  '<input type=submit name=forcerecalc value="Completely Recalculate Sheet"><p>'      if ($asheetdata->{'sheettype'} eq 'assesscalc') {
  );          $r->print('<p><font size=+2>'.
     if (&gettype($asheet) eq 'assesscalc') {                    '<a href="/adm/studentcalc?'.
        $r->print ('<p><font size=+2><a href="/adm/studentcalc?uname='.                    'uname='.$asheetdata->{'uname'}.
                                                &getuname($asheet).                    '&udom='.$asheetdata->{'udom'}.'">'.
                                                '&udom='.&getudom($asheet).                    'Level up: Student Sheet</a></font><p>');
                   '">Level up: Student Sheet</a></font><p>');      }
     }      if (($asheetdata->{'sheettype'} eq 'studentcalc') && 
               (&Apache::lonnet::allowed('vgr',$asheetdata->{'cid'}))) {
     if ((&gettype($asheet) eq 'studentcalc') &&           $r->print ('<p><font size=+2><a href="/adm/classcalc">'.
         (&Apache::lonnet::allowed('vgr',&getcid($asheet)))) {  
        $r->print (  
                    '<p><font size=+2><a href="/adm/classcalc">'.  
                    'Level up: Course Sheet</a></font><p>');                     'Level up: Course Sheet</a></font><p>');
     }      }
           # Save dialog
   
 # ----------------------------------------------------------------- Save dialog  
   
   
     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/\_[^\_]+$//;
         if ($fname eq 'default') { $fname='course_default'; }          if ($fname eq 'default') { $fname='course_default'; }
         $r->print('<input type=submit name=saveas value="Save as ...">'.          $r->print('<input type=submit name=saveas value="Save as ...">'.
               '<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',&getfilename($asheet)));      $r->print(&hiddenfield('ufn',&getfilename($asheet)));
       # Load dialog
 # ----------------------------------------------------------------- Load dialog  
     if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) {      if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) {
  $r->print('<p><input type=submit name=load value="Load ...">'.          $r->print('<p><input type=submit name=load value="Load ...">'.
                   '<select name="loadthissheet">'.                    '<select name="loadthissheet">'.
                   '<option name="default">Default</option>');                    '<option name="default">Default</option>');
         foreach (&othersheets($asheet,&gettype($asheet))) {          foreach (&othersheets($asheet,$asheetdata->{'sheettype'})) {
     $r->print('<option name="'.$_.'"');              $r->print('<option name="'.$_.'"');
             if ($ENV{'form.ufn'} eq $_) {              if ($ENV{'form.ufn'} eq $_) {
                $r->print(' selected');                  $r->print(' selected');
             }              }
             $r->print('>'.$_.'</option>');              $r->print('>'.$_.'</option>');
         }           } 
         $r->print('</select><p>');          $r->print('</select><p>');
         if (&gettype($asheet) eq 'studentcalc') {          if ($asheetdata->{'sheettype'} eq 'studentcalc') {
     &setothersheets($asheet,&othersheets($asheet,'assesscalc'));              &setothersheets($asheet,&othersheets($asheet,'assesscalc'));
         }          }
     }      }
       # Cached sheets
 # --------------------------------------------------------------- Cached sheets  
   
     &expirationdates();      &expirationdates();
   
     undef %oldsheets;      undef %oldsheets;
     undef %loadedcaches;      undef %loadedcaches;
       if ($asheetdata->{'sheettype'} eq 'classcalc') {
     if (&gettype($asheet) eq 'classcalc') {          $r->print("Loading previously calculated student sheets ...\n");
         $r->print("Loading previously calculated student sheets ...<br>\n");  
         $r->rflush();          $r->rflush();
         &cachedcsheets();          &cachedcsheets();
     } elsif (&gettype($asheet) eq 'studentcalc') {      } elsif ($asheetdata->{'sheettype'} eq 'studentcalc') {
         $r->print("Loading previously calculated assessment sheets ...<br>\n");          $r->print("Loading previously calculated assessment sheets ...\n");
         $r->rflush();          $r->rflush();
         &cachedssheets(&getuname($asheet),&getudom($asheet),          &cachedssheets($asheetdata->{'uname'},$asheetdata->{'udom'},
                        &getuhome($asheet));                         $asheetdata->{'uhome'});
     }      }
       # Update sheet, load rows
 # ----------------------------------------------------- Update sheet, load rows  
   
     $r->print("Loaded sheet(s), updating rows ...<br>\n");      $r->print("Loaded sheet(s), updating rows ...<br>\n");
     $r->rflush();      $r->rflush();
       #
     &updatesheet($asheet);      &updatesheet($asheet,$asheetdata);
       $r->print("Updated rows, loading row data ...\n");
     $r->print("Updated rows, loading row data ...<br>\n");  
     $r->rflush();      $r->rflush();
       #
     &loadrows($asheet,$r);      &loadrows($asheet,$asheetdata,$r);
   
     $r->print("Loaded row data, calculating sheet ...<br>\n");      $r->print("Loaded row data, calculating sheet ...<br>\n");
     $r->rflush();      $r->rflush();
       #
     my $calcoutput=&calcsheet($asheet);      my $calcoutput=&calcsheet($asheet);
     $r->print('<h3><font color=red>'.$calcoutput.'</h3></font>');      $r->print('<h3><font color=red>'.$calcoutput.'</h3></font>');
       # See if something to save
 # ---------------------------------------------------- See if something to save  
   
     if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) {      if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) {
         my $fname='';          my $fname='';
  if ($ENV{'form.saveas'} && ($fname=$ENV{'form.newfn'})) {          if ($ENV{'form.saveas'} && ($fname=$ENV{'form.newfn'})) {
             $fname=~s/\W/\_/g;              $fname=~s/\W/\_/g;
             if ($fname eq 'default') { $fname='course_default'; }              if ($fname eq 'default') { $fname='course_default'; }
             $fname.='_'.&gettype($asheet);              $fname.='_'.$asheetdata->{'sheettype'};
             &setfilename($asheet,$fname);              &setfilename($asheet,$fname);
             $ENV{'form.ufn'}=$fname;              $ENV{'form.ufn'}=$fname;
     $r->print('<p>Saving spreadsheet: '.              $r->print('<p>Saving spreadsheet: '.
                          &writesheet($asheet,$ENV{'form.makedefufn'}).'<p>');                        &writesheet($asheet,$ENV{'form.makedefufn'}).'<p>');
  }          }
     }      }
       #
 # ------------------------------------------------ Write the modified worksheet      #Write the modified worksheet
       $r->print('<b>Current sheet:</b> '.&getfilename($asheet).'<p>');
    $r->print('<b>Current sheet:</b> '.&getfilename($asheet).'<p>');      &tmpwrite($asheet);
       if ($asheetdata->{'sheettype'} eq 'studentcalc') {
    &tmpwrite($asheet);          $r->print('<br>Show rows with empty A column: ');
   
     if (&gettype($asheet) eq 'studentcalc') {  
  $r->print('<br>Show rows with empty A column: ');  
     } else {      } else {
         $r->print('<br>Show empty rows: ');          $r->print('<br>Show empty rows: ');
     }       } 
       #
     $r->print(&hiddenfield('userselhidden','true').      $r->print(&hiddenfield('userselhidden','true').
              '<input type=checkbox name=showall onClick="submit()"');                '<input type="checkbox" name="showall" onClick="submit()"');
       #
     if ($ENV{'form.showall'}) {       if ($ENV{'form.showall'}) { 
        $r->print(' checked');           $r->print(' checked'); 
     } else {      } else {
  unless ($ENV{'form.userselhidden'}) {          unless ($ENV{'form.userselhidden'}) {
            unless               unless 
  ($ENV{'course.'.$ENV{'request.course.id'}.'.hideemptyrows'} eq 'yes') {                  ($ENV{'course.'.$ENV{'request.course.id'}.'.hideemptyrows'} eq 'yes') {
           $r->print(' checked');                      $r->print(' checked');
           $ENV{'form.showall'}=1;                      $ENV{'form.showall'}=1;
            }                  }
        }          }
     }      }
     $r->print('>');      $r->print('>');
     if (&gettype($asheet) eq 'classcalc') {      #
        $r->print(      # CSV format checkbox (classcalc sheets only)
    ' Output CSV format: <input type=checkbox name=showcsv onClick="submit()"');      if ($asheetdata->{'sheettype'} eq 'classcalc') {
        if ($ENV{'form.showcsv'}) { $r->print(' checked'); }          $r->print(' Output CSV format: <input type="checkbox" '.
        $r->print('>');                    'name="showcsv" onClick="submit()"');
           if ($ENV{'form.showcsv'}) { $r->print(' checked'); }
           $r->print('>');
     }      }
 # ------------------------------------------------------------- Print out sheet      #
       # Buttons to insert rows
     &outsheet($r,$asheet);      $r->print('&nbsp;Student Status: '.
                 &Apache::lonhtmlcommon::StatusOptions
                 ($ENV{'form.Status'},'sheet'));
       $r->print(<<ENDINSERTBUTTONS);
   <br>
   <input type='button' onClick='insertrow("top");' 
   value='Insert Row Top'>
   <input type='button' onClick='insertrow("bottom");' 
   value='Insert Row Bottom'><br>
   ENDINSERTBUTTONS
       # Print out sheet
       &outsheet($r,$asheet,$asheetdata);
     $r->print('</form></body></html>');      $r->print('</form></body></html>');
       #  Done
 # ------------------------------------------------------------------------ Done  
   } else {  
 # ----------------------------- Not in a course, or not allowed to modify parms  
       $ENV{'user.error.msg'}=  
         $r->uri.":opa:0:0:Cannot modify spreadsheet";  
       return HTTP_NOT_ACCEPTABLE;   
   }  
     return OK;      return OK;
   
 }  }
   
 1;  1;

Removed from v.1.83  
changed lines
  Added in v.1.112


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