Diff for /loncom/interface/Attic/lonspreadsheet.pm between versions 1.78 and 1.102

version 1.78, 2002/01/14 16:32:38 version 1.102, 2002/08/28 19:50:29
Line 1 Line 1
   #
   # $Id$
   #
   # Copyright Michigan State University Board of Trustees
   #
   # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
   #
   # LON-CAPA is free software; you can redistribute it and/or modify
   # it under the terms of the GNU General Public License as published by
   # the Free Software Foundation; either version 2 of the License, or
   # (at your option) any later version.
   #
   # LON-CAPA is distributed in the hope that it will be useful,
   # but WITHOUT ANY WARRANTY; without even the implied warranty of
   # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   # GNU General Public License for more details.
   #
   # You should have received a copy of the GNU General Public License
   # along with LON-CAPA; if not, write to the Free Software
   # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   #
   # /home/httpd/html/adm/gpl.txt
   #
   # http://www.lon-capa.org/
   #
 # The LearningOnline Network with CAPA  # The LearningOnline Network with CAPA
 # Spreadsheet/Grades Display Handler  # Spreadsheet/Grades Display Handler
 #  #
Line 9 Line 34
 # 07/09,07/14,07/21,09/01,09/10,9/11,9/12,9/13,9/14,9/17,  # 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  # 10/16,10/17,10/20,11/05,11/28,12/27 Gerd Kortemeyer
 # 01/14/02 Matthew  # 01/14/02 Matthew
   # 02/04/02 Matthew
   
   # POD required stuff:
   
   =head1 NAME
   
   lonspreadsheet
   
   =head1 SYNOPSIS
   
   Spreadsheet interface to internal LON-CAPA data
   
   =head1 DESCRIPTION
   
   Lonspreadsheet provides course coordinators the ability to manage their
   students grades online.  The students are able to view their own grades, but
   not the grades of their peers.  The spreadsheet is highly customizable,
   offering the ability to use Perl code to manipulate data, as well as many
   built-in functions.
   
   
   =head2 Functions available to user of lonspreadsheet
   
   =over 4
   
   =cut
   
 package Apache::lonspreadsheet;  package Apache::lonspreadsheet;
                           
Line 20  use Apache::lonnet; Line 71  use Apache::lonnet;
 use Apache::Constants qw(:common :http);  use Apache::Constants qw(:common :http);
 use GDBM_File;  use GDBM_File;
 use HTML::TokeParser;  use HTML::TokeParser;
   use Apache::lonhtmlcommon;
 #  #
 # Caches for previously calculated spreadsheets  # Caches for previously calculated spreadsheets
 #  #
Line 55  my %courseopt; Line 106  my %courseopt;
 my %useropt;  my %useropt;
 my %parmhash;  my %parmhash;
   
   #
   # Some hashes for stats on timing and performance
   #
   
   my %starttimes;
   my %usedtimes;
   my %numbertimes;
   
 # Stuff that only the screen handler can know  # Stuff that only the screen handler can know
   
 my $includedir;  my $includedir;
Line 71  sub initsheet { Line 130  sub initsheet {
     $safeeval->permit("sort");      $safeeval->permit("sort");
     $safeeval->deny(":base_io");      $safeeval->deny(":base_io");
     $safehole->wrap(\&Apache::lonnet::EXT,$safeeval,'&EXT');      $safehole->wrap(\&Apache::lonnet::EXT,$safeeval,'&EXT');
       $safeeval->share('$@');
     my $code=<<'ENDDEFS';      my $code=<<'ENDDEFS';
 # ---------------------------------------------------- Inside of the safe space  # ---------------------------------------------------- Inside of the safe space
   
Line 86  undef %v; Line 146  undef %v;
 undef %t;  undef %t;
 undef %f;  undef %f;
 undef %c;  undef %c;
 undef %rl;  undef %rowlabel;
 undef @os;  undef @os;
   
 $maxrow=0;  $maxrow=0;
Line 114  $cfn=''; Line 174  $cfn='';
   
 $usymb='';  $usymb='';
   
   # error messages
   
   $errormsg='';
   
 sub mask {  sub mask {
     my ($lower,$upper)=@_;      my ($lower,$upper)=@_;
   
Line 190  sub mask { Line 254  sub mask {
     return '^'.$alpha.$num."\$";      return '^'.$alpha.$num."\$";
 }  }
   
   #-------------------------------------------------------
   
   =item UWCALC(hashname,modules,units,date) 
   
   returns the proportion of the module 
   weights not previously completed by the student.
   
   =over 4
   
   =item hashname 
   
   name of the hash the module dates have been inserted into
   
   =item modules 
   
   reference to a cell which contains a comma deliminated list of modules 
   covered by the assignment.
   
   =item units 
   
   reference to a cell which contains a comma deliminated list of module 
   weights with respect to the assignment
   
   =item date 
   
   reference to a cell which contains the date the assignment was completed.
   
   =back 
   
   =cut
   
   #-------------------------------------------------------
   sub UWCALC {
       my ($hashname,$modules,$units,$date) = @_;
       my @Modules = split(/,/,$modules);
       my @Units   = split(/,/,$units);
       my $total_weight;
       foreach (@Units) {
    $total_weight += $_;
       }
       my $usum=0;
       for (my $i=0; $i<=$#Modules; $i++) {
    if (&HASH($hashname,$Modules[$i]) eq $date) {
       $usum += $Units[$i];
    }
       }
       return $usum/$total_weight;
   }
   
   #-------------------------------------------------------
   
   =item CDLSUM(list) 
   
   returns the sum of the elements in a cell which contains
   a Comma Deliminate List of numerical values.
   'list' is a reference to a cell which contains a comma deliminated list.
   
   =cut
   
   #-------------------------------------------------------
   sub CDLSUM {
       my ($list)=@_;
       my $sum;
       foreach (split/,/,$list) {
    $sum += $_;
       }
       return $sum;
   }
   
   #-------------------------------------------------------
   
   =item CDLITEM(list,index) 
   
   returns the item at 'index' in a Comma Deliminated List.
   
   =over 4
   
   =item list
   
   reference to a cell which contains a comma deliminated list.
   
   =item index 
   
   the Perl index of the item requested (first element in list has
   an index of 0) 
   
   =back
   
   =cut
   
   #-------------------------------------------------------
   sub CDLITEM {
       my ($list,$index)=@_;
       my @Temp = split/,/,$list;
       return $Temp[$index];
   }
   
   #-------------------------------------------------------
   
   =item CDLHASH(name,key,value) 
   
   loads a comma deliminated list of keys into
   the hash 'name', all with a value of 'value'.
   
   =over 4
   
   =item name  
   
   name of the hash.
   
   =item key
   
   (a pointer to) a comma deliminated list of keys.
   
   =item value
   
   a single value to be entered for each key.
   
   =back
   
   =cut
   
   #-------------------------------------------------------
   sub CDLHASH {
       my ($name,$key,$value)=@_;
       my @Keys;
       my @Values;
       # Check to see if we have multiple $key values
       if ($key =~ /[A-z](\-[A-z])?\d+(\-\d+)?/) {
    my $keymask = &mask($key);
    # Assume the keys are addresses
    my @Temp = grep /$keymask/,keys(%v);
    @Keys = $v{@Temp};
       } else {
    $Keys[0]= $key;
       }
       my @Temp;
       foreach $key (@Keys) {
    @Temp = (@Temp, split/,/,$key);
       }
       @Keys = @Temp;
       if ($value =~ /[A-z](\-[A-z])?\d+(\-\d+)?/) {
    my $valmask = &mask($value);
    my @Temp = grep /$valmask/,keys(%v);
    @Values =$v{@Temp};
       } else {
    $Values[0]= $value;
       }
       $value = $Values[0];
       # Add values to hash
       for (my $i = 0; $i<=$#Keys; $i++) {
    my $key   = $Keys[$i];
    if (! exists ($hashes{$name}->{$key})) {
       $hashes{$name}->{$key}->[0]=$value;
    } else {
       my @Temp = sort(@{$hashes{$name}->{$key}},$value);
       $hashes{$name}->{$key} = \@Temp;
    }
       }
       return "hash '$name' updated";
   }
   
   #-------------------------------------------------------
   
   =item GETHASH(name,key,index) 
   
   returns the element in hash 'name' 
   reference by the key 'key', at index 'index' in the values list.
   
   =cut
   
   #-------------------------------------------------------
   sub GETHASH {
       my ($name,$key,$index)=@_;
       if (! defined($index)) {
    $index = 0;
       }
       if ($key =~ /^[A-z]\d+$/) {
    $key = $v{$key};
       }
       return $hashes{$name}->{$key}->[$index];
   }
   
   #-------------------------------------------------------
   
   =item CLEARHASH(name) 
   
   clears all the values from the hash 'name'
   
   =item CLEARHASH(name,key) 
   
   clears all the values from the hash 'name' associated with the given key.
   
   =cut
   
   #-------------------------------------------------------
   sub CLEARHASH {
       my ($name,$key)=@_;
       if (defined($key)) {
    if (exists($hashes{$name}->{$key})) {
       $hashes{$name}->{$key}=undef;
       return "hash '$name' key '$key' cleared";
    }
       } else {
    if (exists($hashes{$name})) {
       $hashes{$name}=undef;
       return "hash '$name' cleared";
    }
       }
       return "Error in clearing hash";
   }
   
   #-------------------------------------------------------
   
   =item HASH(name,key,value) 
   
   loads values into an internal hash.  If a key 
   already has a value associated with it, the values are sorted numerically.  
   
   =item HASH(name,key) 
   
   returns the 0th value in the hash 'name' associated with 'key'.
   
   =cut
   
   #-------------------------------------------------------
   sub HASH {
       my ($name,$key,$value)=@_;
       my @Keys;
       undef @Keys;
       my @Values;
       # Check to see if we have multiple $key values
       if ($key =~ /[A-z](\-[A-z])?\d+(\-\d+)?/) {
    my $keymask = &mask($key);
    # Assume the keys are addresses
    my @Temp = grep /$keymask/,keys(%v);
    @Keys = $v{@Temp};
       } else {
    $Keys[0]= $key;
       }
       # If $value is empty, return the first value associated 
       # with the first key.
       if (! $value) {
    return $hashes{$name}->{$Keys[0]}->[0];
       }
       # Check to see if we have multiple $value(s) 
       if ($value =~ /[A-z](\-[A-z])?\d+(\-\d+)?/) {
    my $valmask = &mask($value);
    my @Temp = grep /$valmask/,keys(%v);
    @Values =$v{@Temp};
       } else {
    $Values[0]= $value;
       }
       # Add values to hash
       for (my $i = 0; $i<=$#Keys; $i++) {
    my $key   = $Keys[$i];
    my $value = ($i<=$#Values ? $Values[$i] : $Values[0]);
    if (! exists ($hashes{$name}->{$key})) {
       $hashes{$name}->{$key}->[0]=$value;
    } else {
       my @Temp = sort(@{$hashes{$name}->{$key}},$value);
       $hashes{$name}->{$key} = \@Temp;
    }
       }
       return $Values[-1];
   }
   
   #-------------------------------------------------------
   
   =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(%v))}}+1;
Line 209  sub BIN { Line 549  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;
Line 218  sub SUM { Line 567  sub SUM {
     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;
Line 232  sub MEAN { Line 590  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;
Line 248  sub STDDEV { Line 615  sub STDDEV {
     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;
Line 257  sub PROD { Line 633  sub PROD {
     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='-';
Line 267  sub MAX { Line 652  sub MAX {
     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='-';
Line 277  sub MIN { Line 671  sub MIN {
     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(%v)) {
  $inside[$#inside+1]=$v{$_};   push (@inside,$v{$_});
     }      }
     @inside=sort(@inside);      @inside=sort(@inside);
     my $sum=0; my $i;      my $sum=0; my $i;
Line 292  sub SUMMAX { Line 696  sub SUMMAX {
     return $sum;         return $sum;   
 }  }
   
   #-------------------------------------------------------
   
   =item SUMMIN(num,lower,upper)
   
   compute the sum of the smallest 'num' items in the range from
   'lower' to 'upper'
   
   =cut
   
   #-------------------------------------------------------
 sub SUMMIN {  sub SUMMIN {
     my ($num,$lower,$upper)=@_;      my ($num,$lower,$upper)=@_;
     my $mask=mask($lower,$upper);      my $mask=mask($lower,$upper);
Line 339  sub expandnamed { Line 753  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 351  sub sett { Line 791  sub sett {
     } else {      } else {
         $pattern='[A-Z]';          $pattern='[A-Z]';
     }      }
   
   # Deal with the template row
     foreach (keys(%f)) {      foreach (keys(%f)) {
  if ($_=~/template\_(\w)/) {   if ($_=~/template\_(\w)/) {
   my $col=$1;    my $col=$1;
Line 359  sub sett { Line 801  sub sett {
       if ($_=~/A(\d+)/) {        if ($_=~/A(\d+)/) {
  my $trow=$1;   my $trow=$1;
                 if ($trow) {                  if ($trow) {
                       # Get the name of this cell
     my $lb=$col.$trow;      my $lb=$col.$trow;
                       # Grab the template declaration
                     $t{$lb}=$f{'template_'.$col};                      $t{$lb}=$f{'template_'.$col};
                       # Replace '#' with the row number
                     $t{$lb}=~s/\#/$trow/g;                      $t{$lb}=~s/\#/$trow/g;
                       # Replace '....' with ','
                     $t{$lb}=~s/\.\.+/\,/g;                      $t{$lb}=~s/\.\.+/\,/g;
                       # Replace 'A0' with the value from 'A0'
                     $t{$lb}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$v\{\'$2\'\}/g;                      $t{$lb}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$v\{\'$2\'\}/g;
                       # Replace parameters
                     $t{$lb}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.&expandnamed($2)/ge;                      $t{$lb}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.&expandnamed($2)/ge;
                 }                  }
       }        }
Line 371  sub sett { Line 819  sub sett {
   }    }
       }        }
     }      }
   
   # Deal with the normal cells
     foreach (keys(%f)) {      foreach (keys(%f)) {
  if (($f{$_}) && ($_!~/template\_/)) {   if (($f{$_}) && ($_!~/template\_/)) {
             my $matches=($_=~/^$pattern(\d+)/);              my $matches=($_=~/^$pattern(\d+)/);
Line 386  sub sett { Line 836  sub sett {
             }              }
         }          }
     }      }
   # 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\$v\{\'$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\$v\{\'$2\'\}/g;
Line 393  sub sett { Line 860  sub sett {
 }  }
   
 sub calc {  sub calc {
     %v=();      undef %v;
     &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=$v{$_};
             $v{$_}=eval($t{$_});              $v{$_}=eval $t{$_};
     if ($@) {      if ($@) {
  %v=();   undef %v;
                 return $@;                  return $_.': '.$@;
             }              }
     if ($v{$_} ne $old) { $notfinished=1; }      if ($v{$_} ne $old) { $notfinished=1; $lastcalc=$_; }
         }          }
         $depth++;          $depth++;
         if ($depth>100) {          if ($depth>100) {
     %v=();      undef %v;
             return 'Maximum calculation depth exceeded';              return $lastcalc.': Maximum calculation depth exceeded';
         }          }
     }      }
     return '';      return '';
Line 436  sub outrowassess { Line 904  sub outrowassess {
     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}) {
          $cols[0]=$rowlabel{$usy}.'<br>'.
                 '<select name="sel_'.$n.'" onChange="changesheet('.$n.                  '<select name="sel_'.$n.'" onChange="changesheet('.$n.
                 ')"><option name="default">Default</option>';                  ')"><option name="default">Default</option>';
         } else { $cols[0]=''; }
        foreach (@os) {         foreach (@os) {
            $cols[0].='<option name="'.$_.'"';             $cols[0].='<option name="'.$_.'"';
             if ($ufn eq $_) {              if ($ufn eq $_) {
Line 456  sub outrowassess { Line 926  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;
         $cols[$#cols+1]="'$_$n','$fm'".'___eq___'.$v{$_.$n};          push(@cols,"'$_$n','$fm'".'___eq___'.$v{$_.$n});
     }      }
     return @cols;      return @cols;
 }  }
Line 465  sub outrow { Line 935  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 519  sub setothersheets { Line 989  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
Line 544  sub getformulas { Line 1014  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 682  sub rown { Line 1159  sub rown {
     }      }
     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 695  sub rown { Line 1172  sub rown {
     } else {      } else {
         $maxred=26;          $maxred=26;
     }      }
       if (&getfa($safeeval,$n)=~/^[\~\-]/) { $maxred=1; }
     if ($n eq '-') { $proc='&templaterow'; $n=-1; $dataflag=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');
Line 753  sub outsheet { Line 1231  sub outsheet {
                   '><b><font size=+1>Import</font></b></td>'.                    '><b><font size=+1>Import</font></b></td>'.
                   '<td colspan='.$maxyellow.                    '<td colspan='.$maxyellow.
   '><b><font size=+1>Calculations</font></b></td></tr><tr>';    '><b><font size=+1>Calculations</font></b></td></tr><tr>';
     my $showf=0;         my $showf=0;
     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>'; }     } else { $tabledata='<pre>'; }
   
     $r->print($tabledata);      $r->print($tabledata);
Line 781  sub outsheet { Line 1259  sub outsheet {
        $sortidx[$row-1]=$row-1;         $sortidx[$row-1]=$row-1;
     }      }
     @sortidx=sort { $sortby[$a] cmp $sortby[$b]; } @sortidx;      @sortidx=sort { $sortby[$a] cmp $sortby[$b]; } @sortidx;
   
         my $what='Student';          my $what='Student';
         if (&gettype($safeeval) eq 'assesscalc') {          if (&gettype($safeeval) eq 'assesscalc') {
     $what='Item';      $what='Item';
Line 791  sub outsheet { Line 1268  sub outsheet {
   
     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>'.join('</td><td>',
  'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',                                        (split(//,'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
  'a','b','c','d','e','f','g','h','i','j','k','l','m',                                              'abcdefghijklmnopqrstuvwxyz'))).
  'n','o','p','q','r','s','t','u','v','w','x','y','z') {                            "</td></tr>\n");
            $r->print('<td>'.$_.'</td>');              }
               $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 815  sub outsheet { Line 1289  sub outsheet {
 #  #
 # ----------------------------------------------- Read list of available sheets  # ----------------------------------------------- Read list of available sheets
 #   # 
   
 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 $result=&Apache::lonnet::reply('dump:'.$cdom.':'.$cnum.':'.      my %results=&Apache::lonnet::dump($stype.'_spreadsheets',$cdom,$cnum);
                                       $stype.'_spreadsheets',$chome);      my ($tmp) = keys(%results);
     if ($result!~/^error\:/) {      unless ($tmp =~ /^(con_lost|error|no_such_host)/i) {
  foreach (split(/\&/,$result)) {          @alternatives = sort (keys(%results));
             $alternatives[$#alternatives+1]=      }
             &Apache::lonnet::unescape((split(/\=/,$_))[0]);  
         }  
     }   
     return @alternatives;       return @alternatives; 
 }  }
   
   
   #
   # -------------------------------------- Parse a spreadsheet
   # 
   sub parse_sheet {
       # $sheetxml is a scalar reference or a scalar
       my ($sheetxml) = @_;
       if (! ref($sheetxml)) {
           my $tmp = $sheetxml;
           $sheetxml = \$tmp;
       }
       my %f;
       my $parser=HTML::TokeParser->new($sheetxml);
       my $token;
       while ($token=$parser->get_token) {
           if ($token->[0] eq 'S') {
               if ($token->[1] eq 'field') {
                   $f{$token->[2]->{'col'}.$token->[2]->{'row'}}=
                       $parser->get_text('/field');
               }
               if ($token->[1] eq 'template') {
                   $f{'template_'.$token->[2]->{'col'}}=
                       $parser->get_text('/template');
               }
           }
       }
       return \%f;
   }
   
 #  #
 # -------------------------------------- Read spreadsheet formulas for a course  # -------------------------------------- Read spreadsheet formulas for a course
 #  #
Line 846  sub readsheet { Line 1345  sub readsheet {
   my $cdom=&getcdom($safeeval);    my $cdom=&getcdom($safeeval);
   my $chome=&getchome($safeeval);    my $chome=&getchome($safeeval);
   
 # --------- There is no filename. Look for defaults in course and global, cache    if (! defined($fn)) {
         # There is no filename. Look for defaults in course and global, cache
   unless($fn) {  
       unless ($fn=$defaultsheets{$cnum.'_'.$cdom.'_'.$stype}) {        unless ($fn=$defaultsheets{$cnum.'_'.$cdom.'_'.$stype}) {
          $fn=&Apache::lonnet::reply('get:'.$cdom.':'.$cnum.            my %tmphash = &Apache::lonnet::get('environment',
                                     ':environment:spreadsheet_default_'.$stype,                                               ['spreadsheet_default_'.$stype],
                                     $chome);                                               $cdom,$cnum);
          unless (($fn) && ($fn!~/^error\:/)) {            my ($tmp) = keys(%tmphash);
      $fn='default_'.$stype;            if ($tmp =~ /^(con_lost|error|no_such_host)/i) {
          }                $fn = 'default_'.$stype;
          $defaultsheets{$cnum.'_'.$cdom.'_'.$stype}=$fn;             } else {
                 $fn = $tmphash{'spreadsheet_default_'.$stype};
             } 
             unless (($fn) && ($fn!~/^error\:/)) {
         $fn='default_'.$stype;
             }
             $defaultsheets{$cnum.'_'.$cdom.'_'.$stype}=$fn; 
       }        }
   }    }
   
Line 875  sub readsheet { Line 1379  sub readsheet {
      my %f=();       my %f=();
   
      if ($fn=~/^default\_/) {       if ($fn=~/^default\_/) {
  my $sheetxml='';           my $sheetxml='';
        {  
          my $fh;           my $fh;
          my $dfn=$fn;           my $dfn=$fn;
          $dfn=~s/\_/\./g;           $dfn=~s/\_/\./g;
          if ($fh=Apache::File->new($includedir.'/'.$dfn)) {           if ($fh=Apache::File->new($includedir.'/'.$dfn)) {
                $sheetxml=join('',<$fh>);               $sheetxml=join('',<$fh>);
  } else {           } else {
              $sheetxml='<field row="0" col="A">"Error"</field>';               $sheetxml='<field row="0" col="A">"Error"</field>';
  }           }
        }           %f=%{&parse_sheet(\$sheetxml)};
         my $parser=HTML::TokeParser->new(\$sheetxml);       } elsif($fn=~/\/*\.spreadsheet$/) {
         my $token;           my $sheetxml=&Apache::lonnet::getfile
         while ($token=$parser->get_token) {               (&Apache::lonnet::filelocation('',$fn));
           if ($token->[0] eq 'S') {           if ($sheetxml == -1) {
       if ($token->[1] eq 'field') {               $sheetxml='<field row="0" col="A">"Error loading spreadsheet '
   $f{$token->[2]->{'col'}.$token->[2]->{'row'}}=                   .$fn.'"</field>';
       $parser->get_text('/field');           }
       }           %f=%{&parse_sheet(\$sheetxml)};
              if ($token->[1] eq 'template') {       } else {
                  $f{'template_'.$token->[2]->{'col'}}=           my $sheet='';
                      $parser->get_text('/template');           my %tmphash = &Apache::lonnet::dump($fn,$cdom,$cnum);
            my ($tmp) = keys(%tmphash);
            unless ($tmp =~ /^(con_lost|error|no_such_host)/i) {
                foreach (keys(%tmphash)) {
                    $f{$_}=$tmphash{$_};
              }               }
           }           }
         }       }
       } else {  
           my $sheet='';  
           my $reply=&Apache::lonnet::reply('dump:'.$cdom.':'.$cnum.':'.$fn,  
                                          $chome);  
           unless ($reply=~/^error\:/) {  
              $sheet=$reply;  
   }  
           foreach (split(/\&/,$sheet)) {  
              my ($name,$value)=split(/\=/,$_);  
              $f{&Apache::lonnet::unescape($name)}=  
         &Apache::lonnet::unescape($value);  
           }  
        }  
 # --------------------------------------------------------------- Cache and set  # --------------------------------------------------------------- Cache and set
        $spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}=join('___;___',%f);           $spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}=join('___;___',%f);  
        &setformulas($safeeval,%f);         &setformulas($safeeval,%f);
Line 1019  sub tmpread { Line 1513  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 1026  sub tmpread { Line 1521  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 1033  sub tmpread { Line 1533  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; }
     }      }
Line 1135  sub updateclasssheet { Line 1643  sub updateclasssheet {
     my $cdom=&getcdom($safeeval);      my $cdom=&getcdom($safeeval);
     my $cid=&getcid($safeeval);      my $cid=&getcid($safeeval);
     my $chome=&getchome($safeeval);      my $chome=&getchome($safeeval);
       #
 # ---------------------------------------------- Read class list and row labels      # Read class list and row labels
       my %classlist;
     my $classlst=&Apache::lonnet::reply      my @tmp = &Apache::lonnet::dump('classlist',$cdom,$cnum);
                                  ('dump:'.$cdom.':'.$cnum.':classlist',$chome);      if ($tmp[0] !~ /^error/) {
           %classlist = @tmp;
       } else {
           return 'Could not access course data';
       }
       undef @tmp;
       #
     my %currentlist=();      my %currentlist=();
     my $now=time;      my $now=time;
     unless ($classlst=~/^error\:/) {      foreach my $student (keys(%classlist)) {
         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 {  
        $rowlabel='ERROR","'.$name.  
                                  '","Data not available","","","';  
                    }  
                 } else {                  } else {
                     my %reply=&Apache::lonnet::idrget($sdom,$sname);                      $rowlabel='ERROR","'.$studentName.
                     my $reply=&Apache::lonnet::reply('get:'.$sdom.':'.$sname.                          '","Data not available","","","';
       ':environment:firstname&middlename&lastname&generation',                  }
                       &Apache::lonnet::homeserver($sname,$sdom));              } else {
    unless ($ENV{'form.showcsv'}) {                  my %reply=&Apache::lonnet::idrget($studentDomain,$studentName);
                     $rowlabel='<a href="/adm/studentcalc?uname='.$sname.                  my %studentInformation=&Apache::lonnet::get
                               '&udom='.$sdom.'">'.                      ('environment',
                               $ssec.'&nbsp;'.$reply{$sname}.'<br>';                       ['lastname','generation','firstname','middlename','id'],
                     foreach ( split(/\&/,$reply)) {                       $studentDomain,$studentName);
                         $rowlabel.=&Apache::lonnet::unescape($_).' ';                  if (! $ENV{'form.showcsv'}) {
                       $rowlabel='<a href="/adm/studentcalc?uname='.$studentName.
                           '&udom='.$studentDomain.'">'.
                               $studentSection.'&nbsp;';
                       foreach ('id','firstname','middlename',
                                'lastname','generation'){
                           $rowlabel.=$studentInformation{$_}."&nbsp;";
                     }                      }
                     $rowlabel.='</a>';                      $rowlabel.='</a>';
    } else {                  } 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))              $currentlist{$student}=$rowlabel;
 #          } # end of if ($active)
 # -------------------- Find discrepancies between the course row table and this      } # end of foreach my $student (keys(%classlist))
 #      #
         my %f=&getformulas($safeeval);      # Find discrepancies between the course row table and this
         my $changed=0;      #
       my %f=&getformulas($safeeval);
         my $maxrow=0;      my $changed=0;
         my %existing=();      #
       my $maxrow=0;
 # ----------------------------------------------------------- Now obsolete rows      my %existing=();
  foreach (keys(%f)) {      #
     if ($_=~/^A(\d+)/) {      # Now obsolete rows
                 $maxrow=($1>$maxrow)?$1:$maxrow;      foreach (keys(%f)) {
                 $existing{$f{$_}}=1;          if ($_=~/^A(\d+)/) {
  unless ((defined($currentlist{$f{$_}})) || (!$1)) {              $maxrow=($1>$maxrow)?$1:$maxrow;
    $f{$_}='!!! Obsolete';              $existing{$f{$_}}=1;
                    $changed=1;              unless ((defined($currentlist{$f{$_}})) || (!$1) ||
                 }                      ($f{$_}=~/^(\~\~\~|\-\-\-)/)) {
                   $f{$_}='!!! Obsolete';
                   $changed=1;
             }              }
         }          }
       }
 # -------------------------------------------------------- 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
Line 1239  sub updatestudentassesssheet { Line 1750  sub updatestudentassesssheet {
     unless ($updatedata{$ENV{'request.course.fn'}.'_'.$stype}) {      unless ($updatedata{$ENV{'request.course.fn'}.'_'.$stype}) {
 # -------------------------------------------------------------------- Tie hash  # -------------------------------------------------------------------- Tie hash
       if (tie(%bighash,'GDBM_File',$ENV{'request.course.fn'}.'.db',        if (tie(%bighash,'GDBM_File',$ENV{'request.course.fn'}.'.db',
                        &GDBM_READER,0640)) {                         &GDBM_READER(),0640)) {
 # --------------------------------------------------------- Get all assessments  # --------------------------------------------------------- Get all assessments
   
  my %allkeys=('timestamp' =>    my %allkeys=('timestamp' => 
Line 1341  sub updatestudentassesssheet { Line 1852  sub updatestudentassesssheet {
                 $maxrow=($1>$maxrow)?$1:$maxrow;                  $maxrow=($1>$maxrow)?$1:$maxrow;
                 my ($usy,$ufn)=split(/\_\_\&\&\&\_\_/,$f{$_});                  my ($usy,$ufn)=split(/\_\_\&\&\&\_\_/,$f{$_});
                 $existing{$usy}=1;                  $existing{$usy}=1;
  unless ((defined($current{$usy})) || (!$1)) {   unless ((defined($current{$usy})) || (!$1) ||
    $f{$_}='!!! Obsolete';                          ($f{$_}=~/^(\~\~\~|\-\-\-)/)){
       $f{$_}='!!! Obsolete';
                    $changed=1;                     $changed=1;
         } elsif ($ufn) {          } elsif ($ufn) {
     $current{$usy}      $current{$usy}
Line 1377  sub loadstudent { Line 1889  sub loadstudent {
     my %c=();      my %c=();
     my %f=&getformulas($safeeval);      my %f=&getformulas($safeeval);
     $cachedassess=&getuname($safeeval).':'.&getudom($safeeval);      $cachedassess=&getuname($safeeval).':'.&getudom($safeeval);
     %cachedstores=();      # Get ALL the student preformance data
     {      my @tmp = &Apache::lonnet::dump(&getcid($safeeval),
       my $reply=&Apache::lonnet::reply('dump:'.&getudom($safeeval).':'.                                      &getudom($safeeval),
                                                &getuname($safeeval).':'.                                      &getuname($safeeval),
                                                &getcid($safeeval),                                      undef);
                                                &getuhome($safeeval));      if ($tmp[0] !~ /^error:/) {
       unless ($reply=~/^error\:/) {          %cachedstores = @tmp;
  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+)/) {   if ($_=~/^A(\d+)/) {
    my $row=$1;     my $row=$1;
            unless (($f{$_}=~/^\!/) || ($row==0)) {             unless (($f{$_}=~/^[\!\~\-]/) || ($row==0)) {
       my ($usy,$ufn)=split(/\_\_\&\&\&\_\_/,$f{$_});        my ($usy,$ufn)=split(/__&&&\__/,$f{$_});
       @assessdata=&exportsheet(&getuname($safeeval),        @assessdata=&exportsheet(&getuname($safeeval),
                                        &getudom($safeeval),                                         &getudom($safeeval),
                                        'assesscalc',$usy,$ufn);                                         'assesscalc',$usy,$ufn);
Line 1434  sub loadcourse { Line 1942  sub loadcourse {
     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 1454  ENDPOP Line 1962  ENDPOP
     foreach (keys(%f)) {      foreach (keys(%f)) {
  if ($_=~/^A(\d+)/) {   if ($_=~/^A(\d+)/) {
    my $row=$1;     my $row=$1;
            unless (($f{$_}=~/^\!/)  || ($row==0)) {             unless (($f{$_}=~/^[\!\~\-]/)  || ($row==0)) {
       my @studentdata=&exportsheet(split(/\:/,$f{$_}),        my @studentdata=&exportsheet(split(/\:/,$f{$_}),
                                            'studentcalc');                                             'studentcalc');
               undef %userrdatas;                undef %userrdatas;
Line 1465  ENDPOP Line 1973  ENDPOP
               $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++;
               }                }
Line 1614  sub loadassessment { Line 2122  sub loadassessment {
    my %c=();     my %c=();
   
    if (tie(%parmhash,'GDBM_File',     if (tie(%parmhash,'GDBM_File',
            &getcfn($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))  {
  if ($_=~/^A/) {   if ($_=~/^A/) {
             unless ($f{$_}=~/^\!/) {              unless ($f{$_}=~/^[\!\~\-]/) {
         if ($f{$_}=~/^parameter/) {          if ($f{$_}=~/^parameter/) {
  if ($thisassess{$f{$_}}) {   if ($thisassess{$f{$_}}) {
                   my $val=&parmval($f{$_},$safeeval);                    my $val=&parmval($f{$_},$safeeval);
Line 1953  $tmpdir=$r->dir_config('lonDaemons').'/t Line 2461  $tmpdir=$r->dir_config('lonDaemons').'/t
   
 # --------------------------- Get query string for limited number of parameters  # --------------------------- Get query string for limited number of parameters
   
     foreach (split(/&/,$ENV{'QUERY_STRING'})) {      &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
        my ($name, $value) = split(/=/,$_);                                              ['uname','udom','usymb','ufn']);
        $value =~ tr/+/ /;  
        $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;  
        if (($name eq 'uname') || ($name eq 'udom') ||   
            ($name eq 'usymb') || ($name eq 'ufn')) {  
            unless ($ENV{'form.'.$name}) {  
               $ENV{'form.'.$name}=$value;  
    }  
        }  
     }  
   
     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;
Line 2001  $tmpdir=$r->dir_config('lonDaemons').'/t Line 2500  $tmpdir=$r->dir_config('lonDaemons').'/t
   
     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 2014  $tmpdir=$r->dir_config('lonDaemons').'/t Line 2513  $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">'.     $r->print('</head>'.&Apache::loncommon::bodytag('Grades Spreadsheet').
        '<img align=right src=/adm/lonIcons/lonlogos.gif>'.  
        '<h1>LON-CAPA Spreadsheet</h1>'.  
        '<form action="'.$r->uri.'" name=sheet method=post>'.         '<form action="'.$r->uri.'" name=sheet method=post>'.
        &hiddenfield('uname',$ENV{'form.uname'}).         &hiddenfield('uname',$ENV{'form.uname'}).
        &hiddenfield('udom',$ENV{'form.udom'}).         &hiddenfield('udom',$ENV{'form.udom'}).
Line 2082  ENDSCRIPT Line 2585  ENDSCRIPT
         }          }
     }      }
   
 # ---------------------------------------------------------------- Course title  
   
     $r->print('<h1>'.  
             $ENV{'course.'.$ENV{'request.course.id'}.'.description'}.  
              '</h1><h3>'.localtime().'</h3>');  
   
 # ---------------------------------------------------- See if user can see this  # ---------------------------------------------------- See if user can see this
   
     if ((&gettype($asheet) eq 'classcalc') ||       if ((&gettype($asheet) eq 'classcalc') || 
Line 2230  ENDSCRIPT Line 2727  ENDSCRIPT
        }         }
     }      }
     $r->print('>');      $r->print('>');
   
     if (&gettype($asheet) eq 'classcalc') {      if (&gettype($asheet) eq 'classcalc') {
        $r->print(         $r->print(
    ' Output CSV format: <input type=checkbox name=showcsv onClick="submit()"');     ' Output CSV format: <input type=checkbox name=showcsv onClick="submit()"');
        if ($ENV{'form.showcsv'}) { $r->print(' checked'); }         if ($ENV{'form.showcsv'}) { $r->print(' checked'); }
        $r->print('>');         $r->print('>');
     }      }
   
   # ------------------------------------------------------------------ Insertrows
       $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  # ------------------------------------------------------------- Print out sheet
   
     &outsheet($r,$asheet);      &outsheet($r,$asheet);

Removed from v.1.78  
changed lines
  Added in v.1.102


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