--- loncom/interface/Attic/lonspreadsheet.pm	2002/04/18 20:21:38	1.87
+++ loncom/interface/Attic/lonspreadsheet.pm	2002/09/27 18:43:10	1.100.4.1
@@ -1,5 +1,5 @@
 #
-# $Id: lonspreadsheet.pm,v 1.87 2002/04/18 20:21:38 matthew Exp $
+# $Id: lonspreadsheet.pm,v 1.100.4.1 2002/09/27 18:43:10 matthew Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -71,7 +71,7 @@ use Apache::lonnet;
 use Apache::Constants qw(:common :http);
 use GDBM_File;
 use HTML::TokeParser;
-
+use Apache::lonhtmlcommon;
 #
 # Caches for previously calculated spreadsheets
 #
@@ -106,6 +106,14 @@ my %courseopt;
 my %useropt;
 my %parmhash;
 
+#
+# Some hashes for stats on timing and performance
+#
+
+my %starttimes;
+my %usedtimes;
+my %numbertimes;
+
 # Stuff that only the screen handler can know
 
 my $includedir;
@@ -122,8 +130,7 @@ sub initsheet {
     $safeeval->permit("sort");
     $safeeval->deny(":base_io");
     $safehole->wrap(\&Apache::lonnet::EXT,$safeeval,'&EXT');
-    $safehole->wrap(\&send_msg,     $safeeval,"&send_msg");
-    $safehole->wrap(\&send_crit_msg,$safeeval,"&send_crit_msg");
+    $safeeval->share('$@');
     my $code=<<'ENDDEFS';
 # ---------------------------------------------------- Inside of the safe space
 
@@ -167,6 +174,10 @@ $cfn='';
 
 $usymb='';
 
+# error messages
+
+$errormsg='';
+
 sub mask {
     my ($lower,$upper)=@_;
 
@@ -742,7 +753,33 @@ sub expandnamed {
 	    return 0;
         }
     } 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;
     }
 }
 
@@ -754,6 +791,8 @@ sub sett {
     } else {
         $pattern='[A-Z]';
     }
+
+# Deal with the template row
     foreach (keys(%f)) {
 	if ($_=~/template\_(\w)/) {
 	  my $col=$1;
@@ -762,11 +801,17 @@ sub sett {
 	      if ($_=~/A(\d+)/) {
 		my $trow=$1;
                 if ($trow) {
+                    # Get the name of this cell
 		    my $lb=$col.$trow;
+                    # Grab the template declaration
                     $t{$lb}=$f{'template_'.$col};
+                    # Replace '#' with the row number
                     $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\$v\{\'$2\'\}/g;
+                    # Replace parameters
                     $t{$lb}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.&expandnamed($2)/ge;
                 }
 	      }
@@ -774,6 +819,8 @@ sub sett {
 	  }
       }
     }
+
+# Deal with the normal cells
     foreach (keys(%f)) {
 	if (($f{$_}) && ($_!~/template\_/)) {
             my $matches=($_=~/^$pattern(\d+)/);
@@ -789,6 +836,23 @@ 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'}=~s/\.\.+/\,/g;
     $t{'A0'}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$v\{\'$2\'\}/g;
@@ -796,25 +860,26 @@ sub sett {
 }
 
 sub calc {
-    %v=();
+    undef %v;
     &sett();
     my $notfinished=1;
+    my $lastcalc='';
     my $depth=0;
     while ($notfinished) {
 	$notfinished=0;
         foreach (keys(%t)) {
             my $old=$v{$_};
-            $v{$_}=eval($t{$_});
+            $v{$_}=eval $t{$_};
 	    if ($@) {
-		%v=();
-                return $@;
+		undef %v;
+                return $_.': '.$@;
             }
-	    if ($v{$_} ne $old) { $notfinished=1; }
+	    if ($v{$_} ne $old) { $notfinished=1; $lastcalc=$_; }
         }
         $depth++;
         if ($depth>100) {
-	    %v=();
-            return 'Maximum calculation depth exceeded';
+	    undef %v;
+            return $lastcalc.': Maximum calculation depth exceeded';
         }
     }
     return '';
@@ -839,9 +904,11 @@ sub outrowassess {
     my @cols=();
     if ($n) {
        my ($usy,$ufn)=split(/\_\_\&\&\&\_\_/,$f{'A'.$n});
+      if ($rl{$usy}) {
        $cols[0]=$rl{$usy}.'<br>'.
                 '<select name="sel_'.$n.'" onChange="changesheet('.$n.
                 ')"><option name="default">Default</option>';
+      } else { $cols[0]=''; }
        foreach (@os) {
            $cols[0].='<option name="'.$_.'"';
             if ($ufn eq $_) {
@@ -930,7 +997,7 @@ sub setrowlabels {
 
 sub calcsheet {
     my $safeeval=shift;
-    $safeeval->reval('&calc();');
+    return $safeeval->reval('&calc();');
 }
 
 # ------------------------------------------------------------------ Get values
@@ -947,6 +1014,13 @@ sub getformulas {
     return %{$safeeval->varglob('f')};
 }
 
+# ----------------------------------------------------- Get value of $f{'A'.$n}
+
+sub getfa {
+    my ($safeeval,$n)=@_;
+    return $safeeval->reval('$f{"A'.$n.'"}');
+}
+
 # -------------------------------------------------------------------- Get type
 
 sub gettype {
@@ -1085,7 +1159,7 @@ sub rown {
     }
     my $showf=0;
     my $proc;
-    my $maxred;
+    my $maxred=1;
     my $sheettype=&gettype($safeeval);
     if ($sheettype eq 'studentcalc') {
         $proc='&outrowassess';
@@ -1098,6 +1172,7 @@ sub rown {
     } else {
         $maxred=26;
     }
+    if (&getfa($safeeval,$n)=~/^[\~\-]/) { $maxred=1; }
     if ($n eq '-') { $proc='&templaterow'; $n=-1; $dataflag=1; }
     foreach ($safeeval->reval($proc.'('.$n.')')) {
        my $bgcolor=$defaultbg.((($showf-1)/5==int(($showf-1)/5))?'99':'DD');
@@ -1112,9 +1187,14 @@ sub rown {
 	   if ($vl eq '') {
 	       $vl='<font size=+2 color='.$bgcolor.'>&#35;</font>';
            }
-           $rowdata.=
-       '<td bgcolor='.$bgcolor.'><a href="javascript:celledit('.$fm.');">'.$vl.
-	       '</a></td>';
+           $rowdata.='<td bgcolor='.$bgcolor.'>';
+           if ($ENV{'request.role'} =~ /^st\./) {
+               $rowdata.=$vl;
+           } else {
+               $rowdata.='<a href="javascript:celledit('.$fm.');">'.
+                   $vl.'</a>';
+           }
+           $rowdata.='</td>';
        } else {
            $rowdata.='<td bgcolor='.$bgcolor.'>&nbsp;'.$vl.'&nbsp;</td>';
        }
@@ -1286,6 +1366,9 @@ sub readsheet {
           } else {
               $fn = $tmphash{'spreadsheet_default_'.$stype};
           } 
+          unless (($fn) && ($fn!~/^error\:/)) {
+ 	     $fn='default_'.$stype;
+          }
           $defaultsheets{$cnum.'_'.$cdom.'_'.$stype}=$fn; 
       }
   }
@@ -1439,6 +1522,7 @@ sub tmpread {
     $fn=$tmpdir.$fn.'.tmp';
     my $fh;
     my %fo=();
+    my $countrows=0;
     if ($fh=Apache::File->new($fn)) {
         my $name;
         while ($name=<$fh>) {
@@ -1446,6 +1530,11 @@ sub tmpread {
             my $value=<$fh>;
             chomp($value);
             $fo{$name}=$value;
+            if ($name=~/^A(\d+)$/) {
+		if ($1>$countrows) {
+		    $countrows=$1;
+                }
+            }
         }
     }
     if ($nform eq 'changesheet') {
@@ -1453,6 +1542,14 @@ sub tmpread {
         unless ($ENV{'form.sel_'.$nfield} eq 'Default') {
 	    $fo{'A'.$nfield}.='__&&&__'.$ENV{'form.sel_'.$nfield};
         }
+    } elsif ($nfield eq 'insertrow') {
+        $countrows++;
+        my $newrow=substr('000000'.$countrows,-7);
+        if ($nform eq 'top') {
+	    $fo{'A'.$countrows}='--- '.$newrow;
+        } else {
+            $fo{'A'.$countrows}='~~~ '.$newrow;
+        }
     } else {
        if ($nfield) { $fo{$nfield}=$nform; }
     }
@@ -1568,20 +1665,22 @@ sub updateclasssheet {
             my ($end,$start)=split(/\:/,&Apache::lonnet::unescape($value));
             my $active=1;
             if (($end) && ($now>$end)) { $active=0; }
+            $active = 1 if ($ENV{'form.Status'} eq 'Any');
+            $active = !$active if ($ENV{'form.Status'} eq 'Expired');
             if ($active) {
                 my $rowlabel='';
                 $name=&Apache::lonnet::unescape($name);
                 my ($sname,$sdom)=split(/\:/,$name);
                 my $ssec=&Apache::lonnet::usection($sdom,$sname,$cid);
-                if ($ssec==-1) {
-		   unless ($ENV{'form.showcsv'}) {
-                    $rowlabel='<font color=red>Data not available: '.$name.
-			      '</font>';
-		   } else {
-		       $rowlabel='ERROR","'.$name.
-                                 '","Data not available","","","';
-                   }
-                } else {
+#                if ($ssec==-1) {
+#		   unless ($ENV{'form.showcsv'}) {
+#                    $rowlabel='<font color=red>Data not available: '.$name.
+#			      '</font>';
+#		   } else {
+#		       $rowlabel='ERROR","'.$name.
+#                                 '","Data not available","","","';
+#                   }
+#                } else {
                     my %reply=&Apache::lonnet::idrget($sdom,$sname);
                     my $reply=&Apache::lonnet::reply('get:'.$sdom.':'.$sname.
 		      ':environment:firstname&middlename&lastname&generation',
@@ -1604,7 +1703,7 @@ sub updateclasssheet {
                     unless ($ncount==4) { $rowlabel.=',""'; }
                     $rowlabel=~s/\"$//;
 		   }
-                }
+#                }
 		$currentlist{&Apache::lonnet::unescape($name)}=$rowlabel;
             }
         } # end of foreach (split(/\&/,$classlst))
@@ -1622,7 +1721,8 @@ sub updateclasssheet {
 	    if ($_=~/^A(\d+)/) {
                 $maxrow=($1>$maxrow)?$1:$maxrow;
                 $existing{$f{$_}}=1;
-		unless ((defined($currentlist{$f{$_}})) || (!$1)) {
+		unless ((defined($currentlist{$f{$_}})) || (!$1) ||
+                        ($f{$_}=~/^(\~\~\~|\-\-\-)/)) {
 		   $f{$_}='!!! Obsolete';
                    $changed=1;
                 }
@@ -1655,11 +1755,14 @@ sub updatestudentassesssheet {
     my $safeeval=shift;
     my %bighash;
     my $stype=&gettype($safeeval);
+    my $uname=&getuname($safeeval);
+    my $udom =&getudom($safeeval);
     my %current=();
-    unless ($updatedata{$ENV{'request.course.fn'}.'_'.$stype}) {
+    unless ($updatedata{
+        $ENV{'request.course.fn'}.'_'.$stype.'_'.$uname.'_'.$udom}) {
 # -------------------------------------------------------------------- Tie hash
       if (tie(%bighash,'GDBM_File',$ENV{'request.course.fn'}.'.db',
-                       &GDBM_READER,0640)) {
+                       &GDBM_READER(),0640)) {
 # --------------------------------------------------------- Get all assessments
 
 	my %allkeys=('timestamp' => 
@@ -1737,7 +1840,7 @@ sub updatestudentassesssheet {
         } elsif ($stype eq 'studentcalc') {
             %current=%allassess;
         }
-        $updatedata{$ENV{'request.course.fn'}.'_'.$stype}=
+        $updatedata{$ENV{'request.course.fn'}.'_'.$stype.'_'.$uname.'_'.$udom}=
 	    join('___;___',%current);
     } else {
         return 'Could not access course data';
@@ -1745,7 +1848,7 @@ sub updatestudentassesssheet {
 # ------------------------------------------------------ Get current from cache
     } else {
         %current=split(/\_\_\_\;\_\_\_/,
-		       $updatedata{$ENV{'request.course.fn'}.'_'.$stype});
+	     $updatedata{$ENV{'request.course.fn'}.'_'.$stype.'_'.$uname.'_'.$udom});
     }
 # -------------------- Find discrepancies between the course row table and this
 #
@@ -1761,8 +1864,9 @@ sub updatestudentassesssheet {
                 $maxrow=($1>$maxrow)?$1:$maxrow;
                 my ($usy,$ufn)=split(/\_\_\&\&\&\_\_/,$f{$_});
                 $existing{$usy}=1;
-		unless ((defined($current{$usy})) || (!$1)) {
-		   $f{$_}='!!! Obsolete';
+		unless ((defined($current{$usy})) || (!$1) ||
+                        ($f{$_}=~/^(\~\~\~|\-\-\-)/)){
+ 		   $f{$_}='!!! Obsolete';
                    $changed=1;
 	        } elsif ($ufn) {
 		    $current{$usy}
@@ -1815,7 +1919,7 @@ sub loadstudent {
     foreach (keys(%f)) {
 	if ($_=~/^A(\d+)/) {
 	   my $row=$1;
-           unless (($f{$_}=~/^\!/) || ($row==0)) {
+           unless (($f{$_}=~/^[\!\~\-]/) || ($row==0)) {
 	      my ($usy,$ufn)=split(/\_\_\&\&\&\_\_/,$f{$_});
 	      @assessdata=&exportsheet(&getuname($safeeval),
                                        &getudom($safeeval),
@@ -1854,7 +1958,7 @@ sub loadcourse {
     my $total=0;
     foreach (keys(%f)) {
 	if ($_=~/^A(\d+)/) {
-	    unless ($f{$_}=~/^\!/) { $total++; }
+	    unless ($f{$_}=~/^[\!\~\-]/) { $total++; }
         }
     }
     my $now=0;
@@ -1874,7 +1978,7 @@ ENDPOP
     foreach (keys(%f)) {
 	if ($_=~/^A(\d+)/) {
 	   my $row=$1;
-           unless (($f{$_}=~/^\!/)  || ($row==0)) {
+           unless (($f{$_}=~/^[\!\~\-]/)  || ($row==0)) {
 	      my @studentdata=&exportsheet(split(/\:/,$f{$_}),
                                            'studentcalc');
               undef %userrdatas;
@@ -2034,11 +2138,11 @@ sub loadassessment {
    my %c=();
 
    if (tie(%parmhash,'GDBM_File',
-           &getcfn($safeeval).'_parms.db',&GDBM_READER,0640)) {
+           &getcfn($safeeval).'_parms.db',&GDBM_READER(),0640)) {
     my %f=&getformulas($safeeval);
     foreach (keys(%f))  {
 	if ($_=~/^A/) {
-            unless ($f{$_}=~/^\!/) {
+            unless ($f{$_}=~/^[\!\~\-]/) {
   	       if ($f{$_}=~/^parameter/) {
 		if ($thisassess{$f{$_}}) {
                   my $val=&parmval($f{$_},$safeeval);
@@ -2362,6 +2466,11 @@ sub handler {
       return OK;
     }
 
+    if ($ENV{'request.role'} =~ /^st\./) {
+        delete $ENV{'form.unewfield'}   if (exists($ENV{'form.unewfield'}));
+        delete $ENV{'form.unewformula'} if (exists($ENV{'form.unewformula'}));
+    }
+
 # ---------------------------------------------------- Global directory configs
 
 $includedir=$r->dir_config('lonIncludes');
@@ -2373,17 +2482,8 @@ $tmpdir=$r->dir_config('lonDaemons').'/t
 
 # --------------------------- Get query string for limited number of parameters
 
-    foreach (split(/&/,$ENV{'QUERY_STRING'})) {
-       my ($name, $value) = split(/=/,$_);
-       $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;
-	   }
-       }
-    }
+    &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
+                                            ['uname','udom','usymb','ufn']);
 
     if (($ENV{'form.usymb'}=~/^\_(\w+)/) && (!$ENV{'form.ufn'})) {
 	$ENV{'form.ufn'}='default_'.$1;
@@ -2416,7 +2516,8 @@ $tmpdir=$r->dir_config('lonDaemons').'/t
 # --------------------------------------------------------------- Screen output
 
     $r->print('<html><head><title>LON-CAPA Spreadsheet</title>');
-    $r->print(<<ENDSCRIPT);
+    if ($ENV{'request.role'} !~ /^st\./) {
+        $r->print(<<ENDSCRIPT);
 <script language="JavaScript">
 
     function celledit(cn,cf) {
@@ -2434,8 +2535,15 @@ $tmpdir=$r->dir_config('lonDaemons').'/t
         document.sheet.submit();
     }
 
+    function insertrow(cn) {
+	document.sheet.unewfield.value='insertrow';
+        document.sheet.unewformula.value=cn;
+        document.sheet.submit();
+    }
+
 </script>
 ENDSCRIPT
+    }
     $r->print('</head><body bgcolor="#FFFFFF">'.
        '<img align=right src=/adm/lonIcons/lonlogos.gif>'.
        '<h1>LON-CAPA Spreadsheet</h1>'.
@@ -2491,12 +2599,12 @@ ENDSCRIPT
     unless (&gettype($asheet) eq 'classcalc') {
         $r->print('<p><b>User:</b> '.&getuname($asheet).
                   '<br><b>Domain:</b> '.&getudom($asheet));
-        if (&getcsec($asheet) eq '-1') {
-           $r->print('<h3><font color=red>'.
-                     'Not a student in this course</font></h3>');
-        } else {
+#        if (&getcsec($asheet) eq '-1') {
+#           $r->print('<h3><font color=red>'.
+#                     'Not a student in this course</font></h3>');
+#        } else {
            $r->print('<br><b>Section/Group:</b> '.&getcsec($asheet));
-        }
+#        }
         if ($ENV{'form.usymb'}) {
            $r->print('<br><b>Assessment:</b> <tt>'.$ENV{'form.usymb'}.'</tt>');
         }
@@ -2657,6 +2765,20 @@ ENDSCRIPT
        if ($ENV{'form.showcsv'}) { $r->print(' checked'); }
        $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
 
     &outsheet($r,$asheet);