--- loncom/interface/Attic/lonspreadsheet.pm 2001/03/05 21:26:29 1.39 +++ loncom/interface/Attic/lonspreadsheet.pm 2002/04/08 18:28:03 1.81 @@ -1,10 +1,65 @@ +# +# $Id: lonspreadsheet.pm,v 1.81 2002/04/08 18:28:03 matthew Exp $ +# +# 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 # 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 Gerd Kortemeyer +# 03/05,03/08,03/10,03/12,03/13,03/15,03/17, +# 03/19,03/20,03/21,03/27,04/05,04/09, +# 07/09,07/14,07/21,09/01,09/10,9/11,9/12,9/13,9/14,9/17, +# 10/16,10/17,10/20,11/05,11/28,12/27 Gerd Kortemeyer +# 01/14/02 Matthew +# 02/04/02 Matthew + +# POD required stuff: + +=head1 NAME + +lonspreadsheet + +=head1 SYNOPSIS + +Spreadsheet interface to internal LON-CAPA data + +=head1 DESCRIPTION + +Lonspreadsheet provides course coordinators the ability to manage their +students grades online. The students are able to view their own grades, but +not the grades of their peers. The spreadsheet is highly customizable, +offering the ability to use Perl code to manipulate data, as well as many +built-in functions. + + +=head2 Functions available to user of lonspreadsheet + +=over 4 + +=cut package Apache::lonspreadsheet; @@ -18,6 +73,14 @@ use GDBM_File; use HTML::TokeParser; # +# Caches for previously calculated spreadsheets +# + +my %oldsheets; +my %loadedcaches; +my %expiredates; + +# # Cache for stores of an individual user # @@ -48,25 +111,6 @@ my %parmhash; my $includedir; my $tmpdir; - -sub mdeb { - my $msg=shift; - my $mem=''; - { - my $mfh=Apache::File->new('/proc/'.$$.'/status'); - my $line; - while ($line=<$mfh>) { - my ($name,$value)=split(/\:/,$line); - if ($name eq 'VmSize') { $mem=$value; } - } - $mfh->close(); - } - print $msg.$mem."
\n"; -} - - - - # ============================================================================= # ===================================== Implements an instance of a spreadsheet @@ -87,12 +131,14 @@ sub initsheet { # v: output values # c: preloaded constants (A-column) # rl: row label +# os: other spreadsheets (for student spreadsheet only) undef %v; undef %t; undef %f; undef %c; undef %rl; +undef @os; $maxrow=0; $sheettype=''; @@ -148,16 +194,16 @@ sub mask { } else { if (length($ld)!=length($ud)) { $num.='('; - map { + foreach ($ld=~m/\d/g) { $num.='['.$_.'-9]'; - } ($ld=~m/\d/g); + } if (length($ud)-length($ld)>1) { $num.='|\d{'.(length($ld)+1).','.(length($ud)-1).'}'; } $num.='|'; - map { + foreach ($ud=~m/\d/g) { $num.='[0-'.$_.']'; - } ($ud=~m/\d/g); + } $num.=')'; } else { my @lda=($ld=~m/\d/g); @@ -195,12 +241,276 @@ sub mask { return '^'.$alpha.$num."\$"; } +#------------------------------------------------------- + +=item UWCALC(hashname,modules,units,date) + +returns the proportion of the module +weights not previously completed by the student. + +=over 4 + +=item hashname + +name of the hash the module dates have been inserted into + +=item modules + +reference to a cell which contains a comma deliminated list of modules +covered by the assignment. + +=item units + +reference to a cell which contains a comma deliminated list of module +weights with respect to the assignment + +=item date + +reference to a cell which contains the date the assignment was completed. + +=back + +=cut + +#------------------------------------------------------- +sub UWCALC { + my ($hashname,$modules,$units,$date) = @_; + my @Modules = split(/,/,$modules); + my @Units = split(/,/,$units); + my $total_weight; + foreach (@Units) { + $total_weight += $_; + } + my $usum=0; + for (my $i=0; $i<=$#Modules; $i++) { + if (&HASH($hashname,$Modules[$i]) eq $date) { + $usum += $Units[$i]; + } + } + return $usum/$total_weight; +} + +#------------------------------------------------------- + +=item CDLSUM(list) + +returns the sum of the elements in a cell which contains +a Comma Deliminate List of numerical values. +'list' is a reference to a cell which contains a comma deliminated list. + +=cut + +#------------------------------------------------------- +sub CDLSUM { + my ($list)=@_; + my $sum; + foreach (split/,/,$list) { + $sum += $_; + } + return $sum; +} + +#------------------------------------------------------- + +=item CDLITEM(list,index) + +returns the item at 'index' in a Comma Deliminated List. + +=over 4 + +=item list + +reference to a cell which contains a comma deliminated list. + +=item index + +the Perl index of the item requested (first element in list has +an index of 0) + +=back + +=cut + +#------------------------------------------------------- +sub CDLITEM { + my ($list,$index)=@_; + my @Temp = split/,/,$list; + return $Temp[$index]; +} + +#------------------------------------------------------- + +=item CDLHASH(name,key,value) + +loads a comma deliminated list of keys into +the hash 'name', all with a value of 'value'. + +=over 4 + +=item name + +name of the hash. + +=item key + +(a pointer to) a comma deliminated list of keys. + +=item value + +a single value to be entered for each key. + +=back + +=cut + +#------------------------------------------------------- +sub CDLHASH { + my ($name,$key,$value)=@_; + my @Keys; + my @Values; + # Check to see if we have multiple $key values + if ($key =~ /[A-z](\-[A-z])?\d+(\-\d+)?/) { + my $keymask = &mask($key); + # Assume the keys are addresses + my @Temp = grep /$keymask/,keys(%v); + @Keys = $v{@Temp}; + } else { + $Keys[0]= $key; + } + my @Temp; + foreach $key (@Keys) { + @Temp = (@Temp, split/,/,$key); + } + @Keys = @Temp; + if ($value =~ /[A-z](\-[A-z])?\d+(\-\d+)?/) { + my $valmask = &mask($value); + my @Temp = grep /$valmask/,keys(%v); + @Values =$v{@Temp}; + } else { + $Values[0]= $value; + } + $value = $Values[0]; + # Add values to hash + for (my $i = 0; $i<=$#Keys; $i++) { + my $key = $Keys[$i]; + if (! exists ($hashes{$name}->{$key})) { + $hashes{$name}->{$key}->[0]=$value; + } else { + my @Temp = sort(@{$hashes{$name}->{$key}},$value); + $hashes{$name}->{$key} = \@Temp; + } + } + return "hash '$name' updated"; +} + +#------------------------------------------------------- + +=item GETHASH(name,key,index) + +returns the element in hash 'name' +reference by the key 'key', at index 'index' in the values list. + +=cut + +#------------------------------------------------------- +sub GETHASH { + my ($name,$key,$index)=@_; + if (! defined($index)) { + $index = 0; + } + if ($key =~ /^[A-z]\d+$/) { + $key = $v{$key}; + } + return $hashes{$name}->{$key}->[$index]; +} + +#------------------------------------------------------- + +=item CLEARHASH(name) + +clears all the values from the hash 'name' + +=item CLEARHASH(name,key) + +clears all the values from the hash 'name' associated with the given key. + +=cut + +#------------------------------------------------------- +sub CLEARHASH { + my ($name,$key)=@_; + if (defined($key)) { + if (exists($hashes{$name}->{$key})) { + $hashes{$name}->{$key}=undef; + return "hash '$name' key '$key' cleared"; + } + } else { + if (exists($hashes{$name})) { + $hashes{$name}=undef; + return "hash '$name' cleared"; + } + } + return "Error in clearing hash"; +} + +#------------------------------------------------------- + +=item HASH(name,key,value) + +loads values into an internal hash. If a key +already has a value associated with it, the values are sorted numerically. + +=item HASH(name,key) + +returns the 0th value in the hash 'name' associated with 'key'. + +=cut + +#------------------------------------------------------- +sub HASH { + my ($name,$key,$value)=@_; + my @Keys; + undef @Keys; + my @Values; + # Check to see if we have multiple $key values + if ($key =~ /[A-z](\-[A-z])?\d+(\-\d+)?/) { + my $keymask = &mask($key); + # Assume the keys are addresses + my @Temp = grep /$keymask/,keys(%v); + @Keys = $v{@Temp}; + } else { + $Keys[0]= $key; + } + # If $value is empty, return the first value associated + # with the first key. + if (! $value) { + return $hashes{$name}->{$Keys[0]}->[0]; + } + # Check to see if we have multiple $value(s) + if ($value =~ /[A-z](\-[A-z])?\d+(\-\d+)?/) { + my $valmask = &mask($value); + my @Temp = grep /$valmask/,keys(%v); + @Values =$v{@Temp}; + } else { + $Values[0]= $value; + } + # Add values to hash + for (my $i = 0; $i<=$#Keys; $i++) { + my $key = $Keys[$i]; + my $value = ($i<=$#Values ? $Values[$i] : $Values[0]); + if (! exists ($hashes{$name}->{$key})) { + $hashes{$name}->{$key}->[0]=$value; + } else { + my @Temp = sort(@{$hashes{$name}->{$key}},$value); + $hashes{$name}->{$key} = \@Temp; + } + } + return $Values[-1]; +} + sub NUM { my $mask=mask(@_); - my $num=0; - map { - $num++; - } grep /$mask/,keys %v; + my $num= $#{@{grep(/$mask/,keys(%v))}}+1; return $num; } @@ -208,11 +518,11 @@ sub BIN { my ($low,$high,$lower,$upper)=@_; my $mask=mask($lower,$upper); my $num=0; - map { + foreach (grep /$mask/,keys(%v)) { if (($v{$_}>=$low) && ($v{$_}<=$high)) { $num++; } - } grep /$mask/,keys %v; + } return $num; } @@ -220,19 +530,19 @@ sub BIN { sub SUM { my $mask=mask(@_); my $sum=0; - map { + foreach (grep /$mask/,keys(%v)) { $sum+=$v{$_}; - } grep /$mask/,keys %v; + } return $sum; } sub MEAN { my $mask=mask(@_); my $sum=0; my $num=0; - map { + foreach (grep /$mask/,keys(%v)) { $sum+=$v{$_}; $num++; - } grep /$mask/,keys %v; + } if ($num) { return $sum/$num; } else { @@ -243,45 +553,45 @@ sub MEAN { sub STDDEV { my $mask=mask(@_); my $sum=0; my $num=0; - map { + foreach (grep /$mask/,keys(%v)) { $sum+=$v{$_}; $num++; - } grep /$mask/,keys %v; + } unless ($num>1) { return undef; } my $mean=$sum/$num; $sum=0; - map { + foreach (grep /$mask/,keys(%v)) { $sum+=($v{$_}-$mean)**2; - } grep /$mask/,keys %v; + } return sqrt($sum/($num-1)); } sub PROD { my $mask=mask(@_); my $prod=1; - map { + foreach (grep /$mask/,keys(%v)) { $prod*=$v{$_}; - } grep /$mask/,keys %v; + } return $prod; } sub MAX { my $mask=mask(@_); my $max='-'; - map { + foreach (grep /$mask/,keys(%v)) { unless ($max) { $max=$v{$_}; } if (($v{$_}>$max) || ($max eq '-')) { $max=$v{$_}; } - } grep /$mask/,keys %v; + } return $max; } sub MIN { my $mask=mask(@_); my $min='-'; - map { + foreach (grep /$mask/,keys(%v)) { unless ($max) { $max=$v{$_}; } if (($v{$_}<$min) || ($min eq '-')) { $min=$v{$_}; } - } grep /$mask/,keys %v; + } return $min; } @@ -289,9 +599,9 @@ sub SUMMAX { my ($num,$lower,$upper)=@_; my $mask=mask($lower,$upper); my @inside=(); - map { + foreach (grep /$mask/,keys(%v)) { $inside[$#inside+1]=$v{$_}; - } grep /$mask/,keys %v; + } @inside=sort(@inside); my $sum=0; my $i; for ($i=$#inside;(($i>$#inside-$num) && ($i>=0));$i--) { @@ -304,9 +614,9 @@ sub SUMMIN { my ($num,$lower,$upper)=@_; my $mask=mask($lower,$upper); my @inside=(); - map { + foreach (grep /$mask/,keys(%v)) { $inside[$#inside+1]=$v{$_}; - } grep /$mask/,keys %v; + } @inside=sort(@inside); my $sum=0; my $i; for ($i=0;(($i<$num) && ($i<=$#inside));$i++) { @@ -315,6 +625,42 @@ sub SUMMIN { return $sum; } +sub expandnamed { + my $expression=shift; + if ($expression=~/^\&/) { + my ($func,$var,$formula)=($expression=~/^\&(\w+)\(([^\;]+)\;(.*)\)/); + my @vars=split(/\W+/,$formula); + my %values=(); + undef %values; + foreach ( @vars ) { + my $varname=$_; + if ($varname=~/\D/) { + $formula=~s/$varname/'$c{\''.$varname.'\'}'/ge; + $varname=~s/$var/\(\\w\+\)/g; + foreach (keys(%c)) { + if ($_=~/$varname/) { + $values{$1}=1; + } + } + } + } + if ($func eq 'EXPANDSUM') { + my $result=''; + foreach (keys(%values)) { + my $thissum=$formula; + $thissum=~s/$var/$_/g; + $result.=$thissum.'+'; + } + $result=~s/\+$//; + return $result; + } else { + return 0; + } + } else { + return '$c{\''.$expression.'\'}'; + } +} + sub sett { %t=(); my $pattern=''; @@ -323,11 +669,11 @@ sub sett { } else { $pattern='[A-Z]'; } - map { + foreach (keys(%f)) { if ($_=~/template\_(\w)/) { my $col=$1; unless ($col=~/^$pattern/) { - map { + foreach (keys(%f)) { if ($_=~/A(\d+)/) { my $trow=$1; if ($trow) { @@ -336,16 +682,17 @@ sub sett { $t{$lb}=~s/\#/$trow/g; $t{$lb}=~s/\.\.+/\,/g; $t{$lb}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$v\{\'$2\'\}/g; - $t{$lb}=~s/(^|[^\"\'])\[([\w\.]+)\]/$1\$c\{\'$2\'\}/g; + $t{$lb}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.&expandnamed($2)/ge; } } - } keys %f; + } } } - } keys %f; - map { + } + foreach (keys(%f)) { if (($f{$_}) && ($_!~/template\_/)) { - if ($_=~/^$pattern/) { + my $matches=($_=~/^$pattern(\d+)/); + if (($matches) && ($1)) { unless ($f{$_}=~/^\!/) { $t{$_}=$c{$_}; } @@ -353,14 +700,14 @@ sub sett { $t{$_}=$f{$_}; $t{$_}=~s/\.\.+/\,/g; $t{$_}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$v\{\'$2\'\}/g; - $t{$_}=~s/(^|[^\"\'])\[([\w\.]+)\]/$1\$c\{\'$2\'\}/g; + $t{$_}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.&expandnamed($2)/ge; } } - } keys %f; + } $t{'A0'}=$f{'A0'}; $t{'A0'}=~s/\.\.+/\,/g; $t{'A0'}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$v\{\'$2\'\}/g; - $t{'A0'}=~s/(^|[^\"\'])\[([\w\.]+)\]/$1\$c\{\'$2\'\}/g; + $t{'A0'}=~s/(^|[^\"\'])\[([^\]]+)\]/$1.&expandnamed($2)/ge; } sub calc { @@ -370,7 +717,7 @@ sub calc { my $depth=0; while ($notfinished) { $notfinished=0; - map { + foreach (keys(%t)) { my $old=$v{$_}; $v{$_}=eval($t{$_}); if ($@) { @@ -378,7 +725,7 @@ sub calc { return $@; } if ($v{$_} ne $old) { $notfinished=1; } - } keys %t; + } $depth++; if ($depth>100) { %v=(); @@ -391,14 +738,14 @@ sub calc { sub templaterow { my @cols=(); $cols[0]='Template'; - map { + foreach ('A','B','C','D','E','F','G','H','I','J','K','L','M', + 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', + 'a','b','c','d','e','f','g','h','i','j','k','l','m', + 'n','o','p','q','r','s','t','u','v','w','x','y','z') { my $fm=$f{'template_'.$_}; $fm=~s/[\'\"]/\&\#34;/g; $cols[$#cols+1]="'template_$_','$fm'".'___eq___'.$fm; - } ('A','B','C','D','E','F','G','H','I','J','K','L','M', - 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', - 'a','b','c','d','e','f','g','h','i','j','k','l','m', - 'n','o','p','q','r','s','t','u','v','w','x','y','z'); + } return @cols; } @@ -406,18 +753,29 @@ sub outrowassess { my $n=shift; my @cols=(); if ($n) { - $cols[0]=$rl{$f{'A'.$n}}; + my ($usy,$ufn)=split(/\_\_\&\&\&\_\_/,$f{'A'.$n}); + $cols[0]=$rl{$usy}.'
'. + ''; } else { $cols[0]='Export'; } - map { + foreach ('A','B','C','D','E','F','G','H','I','J','K','L','M', + 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', + 'a','b','c','d','e','f','g','h','i','j','k','l','m', + 'n','o','p','q','r','s','t','u','v','w','x','y','z') { my $fm=$f{$_.$n}; $fm=~s/[\'\"]/\&\#34;/g; $cols[$#cols+1]="'$_$n','$fm'".'___eq___'.$v{$_.$n}; - } ('A','B','C','D','E','F','G','H','I','J','K','L','M', - 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', - 'a','b','c','d','e','f','g','h','i','j','k','l','m', - 'n','o','p','q','r','s','t','u','v','w','x','y','z'); + } return @cols; } @@ -429,23 +787,23 @@ sub outrow { } else { $cols[0]='Export'; } - map { + foreach ('A','B','C','D','E','F','G','H','I','J','K','L','M', + 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', + 'a','b','c','d','e','f','g','h','i','j','k','l','m', + 'n','o','p','q','r','s','t','u','v','w','x','y','z') { my $fm=$f{$_.$n}; $fm=~s/[\'\"]/\&\#34;/g; $cols[$#cols+1]="'$_$n','$fm'".'___eq___'.$v{$_.$n}; - } ('A','B','C','D','E','F','G','H','I','J','K','L','M', - 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', - 'a','b','c','d','e','f','g','h','i','j','k','l','m', - 'n','o','p','q','r','s','t','u','v','w','x','y','z'); + } return @cols; } sub exportrowa { my @exportarray=(); - map { + foreach ('A','B','C','D','E','F','G','H','I','J','K','L','M', + 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z') { $exportarray[$#exportarray+1]=$v{$_.'0'}; - } ('A','B','C','D','E','F','G','H','I','J','K','L','M', - 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z'); + } return @exportarray; } @@ -469,6 +827,13 @@ sub setconstants { %{$safeeval->varglob('c')}=%c; } +# --------------------------------------------- Set names of other spreadsheets + +sub setothersheets { + my ($safeeval,@os)=@_; + @{$safeeval->varglob('os')}=@os; +} + # ------------------------------------------------ Add or change formula values sub setrowlabels { @@ -609,6 +974,7 @@ sub exportdata { return $safeeval->reval('&exportrowa()'); } + # ========================================================== End of Spreadsheet # ============================================================================= @@ -621,44 +987,40 @@ sub rown { my ($safeeval,$n)=@_; my $defaultbg; my $rowdata=''; + my $dataflag=0; unless ($n eq '-') { $defaultbg=((($n-1)/5)==int(($n-1)/5))?'#E0E0':'#FFFF'; } else { $defaultbg='#E0FF'; } - if ((($n-1)/25)==int(($n-1)/25)) { - my $what='Student'; - if (&gettype($safeeval) eq 'assesscalc') { - $what='Item'; - } elsif (&gettype($safeeval) eq 'studentcalc') { - $what='Assessment'; - } - $rowdata.="\n
". - ''; - map { - $rowdata.=''; - } ('A','B','C','D','E','F','G','H','I','J','K','L','M', - 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', - 'a','b','c','d','e','f','g','h','i','j','k','l','m', - 'n','o','p','q','r','s','t','u','v','w','x','y','z'); - $rowdata.=''; + unless ($ENV{'form.showcsv'}) { + $rowdata.="\n"; + } else { + $rowdata.="\n".'"'.$n.'"'; } - $rowdata.="\n"; my $showf=0; my $proc; my $maxred; - if (&gettype($safeeval) eq 'assesscalc') { + my $sheettype=&gettype($safeeval); + if ($sheettype eq 'studentcalc') { $proc='&outrowassess'; - $maxred=1; + $maxred=26; } else { $proc='&outrow'; + } + if ($sheettype eq 'assesscalc') { + $maxred=1; + } else { $maxred=26; } - if ($n eq '-') { $proc='&templaterow'; $n=-1; } - map { + if ($n eq '-') { $proc='&templaterow'; $n=-1; $dataflag=1; } + foreach ($safeeval->reval($proc.'('.$n.')')) { my $bgcolor=$defaultbg.((($showf-1)/5==int(($showf-1)/5))?'99':'DD'); my ($fm,$vl)=split(/\_\_\_eq\_\_\_/,$_); + if ((($vl ne '') || ($vl eq '0')) && + (($showf==1) || ($sheettype ne 'studentcalc'))) { $dataflag=1; } if ($showf==0) { $vl=$_; } + unless ($ENV{'form.showcsv'}) { if ($showf<=$maxred) { $bgcolor='#FFDDDD'; } if (($n==0) && ($showf<=26)) { $bgcolor='#CCCCFF'; } if (($showf>$maxred) || ((!$n) && ($showf>0))) { @@ -671,9 +1033,16 @@ sub rown { } else { $rowdata.=''; } + } else { + $rowdata.=',"'.$vl.'"'; + } $showf++; - } $safeeval->reval($proc.'('.$n.')'); - return $rowdata.''; + } # End of foreach($safeval...) + if ($ENV{'form.showall'} || ($dataflag)) { + return $rowdata.($ENV{'form.showcsv'}?'':''); + } else { + return ''; + } } # ------------------------------------------------------------- Print out sheet @@ -693,7 +1062,9 @@ sub outsheet { $realm='Course'; } my $maxyellow=52-$maxred; - my $tabledata= + my $tabledata; + unless ($ENV{'form.showcsv'}) { + $tabledata= '
 '.$what.''.$_.'
$n
$n '.$vl.' 
'. ''; my $showf=0; - map { + foreach ('A','B','C','D','E','F','G','H','I','J','K','L','M', + 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', + 'a','b','c','d','e','f','g','h','i','j','k','l','m', + 'n','o','p','q','r','s','t','u','v','w','x','y','z') { $showf++; if ($showf<=$maxred) { $tabledata.='"; - } ('A','B','C','D','E','F','G','H','I','J','K','L','M', - 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', - 'a','b','c','d','e','f','g','h','i','j','k','l','m', - 'n','o','p','q','r','s','t','u','v','w','x','y','z'); - $tabledata.=''; + } + $tabledata.=''.&rown($safeeval,'-').&rown($safeeval,0); + } else { $tabledata='
'; }
+
+    $r->print($tabledata);
+
     my $row;
     my $maxrow=&getmaxrow($safeeval);
-    $tabledata.=&rown($safeeval,'-');
-    $r->print($tabledata);
-    for ($row=0;$row<=$maxrow;$row++) {
-        $r->print(&rown($safeeval,$row));
+
+    my @sortby=();
+    my @sortidx=();
+    for ($row=1;$row<=$maxrow;$row++) {
+       $sortby[$row-1]=$safeeval->reval('$f{"A'.$row.'"}');
+       $sortidx[$row-1]=$row-1;
     }
-    $r->print('
'. $realm.'Calculations
'; @@ -709,19 +1083,70 @@ sub outsheet { $tabledata.=''; } $tabledata.="$_
'); + @sortidx=sort { $sortby[$a] cmp $sortby[$b]; } @sortidx; + + my $what='Student'; + if (&gettype($safeeval) eq 'assesscalc') { + $what='Item'; + } elsif (&gettype($safeeval) eq 'studentcalc') { + $what='Assessment'; + } + + my $n=0; + for ($row=0;$row<$maxrow;$row++) { + my $thisrow=&rown($safeeval,$sortidx[$row]+1); + if ($thisrow) { + if (($n/25==int($n/25)) && (!$ENV{'form.showcsv'})) { + $r->print("\n
\n"); + $r->rflush(); + $r->print(''); + foreach ('A','B','C','D','E','F','G','H','I','J','K','L','M', + 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', + 'a','b','c','d','e','f','g','h','i','j','k','l','m', + 'n','o','p','q','r','s','t','u','v','w','x','y','z') { + $r->print(''); + } + $r->print(''); + } + $n++; + $r->print($thisrow); + } + } + $r->print($ENV{'form.showcsv'}?'':'
 '.$what.''.$_.'
'); +} + +# +# ----------------------------------------------- Read list of available sheets +# +sub othersheets { + my ($safeeval,$stype)=@_; + # + my $cnum=&getcnum($safeeval); + my $cdom=&getcdom($safeeval); + my $chome=&getchome($safeeval); + # + my @alternatives=(); + my %results=&Apache::lonnet::dump($stype.'_spreadsheets',$cdom,$cnum); + my ($tmp) = keys(%results); + unless ($tmp =~ /^(con_lost|error|no_such_host)/i) { + @alternatives = sort (keys(%results)); + } + return @alternatives; } # @@ -767,10 +1192,13 @@ sub readsheet { my $sheetxml=''; { my $fh; - if ($fh=Apache::File->new($includedir. - '/default.'.&gettype($safeeval))) { + my $dfn=$fn; + $dfn=~s/\_/\./g; + if ($fh=Apache::File->new($includedir.'/'.$dfn)) { $sheetxml=join('',<$fh>); - } + } else { + $sheetxml='"Error"'; + } } my $parser=HTML::TokeParser->new(\$sheetxml); my $token; @@ -788,17 +1216,14 @@ sub readsheet { } } else { my $sheet=''; - my $reply=&Apache::lonnet::reply('dump:'.$cdom.':'.$cnum.':'.$fn, - $chome); - unless ($reply=~/^error\:/) { - $sheet=$reply; - } - map { - my ($name,$value)=split(/\=/,$_); - $f{&Apache::lonnet::unescape($name)}= - &Apache::lonnet::unescape($value); - } split(/\&/,$sheet); - } + my %tmphash = &Apache::lonnet::dump($fn,$cdom,$cnum); + my ($tmp) = keys(%tmphash); + unless ($tmp =~ /^(con_lost|error|no_such_host)/i) { + foreach (keys(%tmphash)) { + $f{$_}=$tmphash{$_}; + } + } + } # --------------------------------------------------------------- Cache and set $spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}=join('___;___',%f); &setformulas($safeeval,%f); @@ -843,17 +1268,20 @@ sub writesheet { $spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}=join('___;___',%f); # ----------------------------------------------------------------- Write sheet my $sheetdata=''; - map { + foreach (keys(%f)) { + unless ($f{$_} eq 'import') { $sheetdata.=&Apache::lonnet::escape($_).'='. &Apache::lonnet::escape($f{$_}).'&'; - } keys %f; + } + } $sheetdata=~s/\&$//; my $reply=&Apache::lonnet::reply('put:'.$cdom.':'.$cnum.':'.$fn.':'. $sheetdata,$chome); if ($reply eq 'ok') { $reply=&Apache::lonnet::reply('put:'.$cdom.':'.$cnum.':'. $stype.'_spreadsheets:'. - &Apache::lonnet::escape($fn).'='.$ENV{'user.name'}, + &Apache::lonnet::escape($fn).'='.$ENV{'user.name'}.'@'. + $ENV{'user.domain'}, $chome); if ($reply eq 'ok') { if ($makedef) { @@ -911,7 +1339,14 @@ sub tmpread { $fo{$name}=$value; } } - if ($nfield) { $fo{$nfield}=$nform; } + if ($nform eq 'changesheet') { + $fo{'A'.$nfield}=(split(/\_\_\&\&\&\_\_/,$fo{'A'.$nfield}))[0]; + unless ($ENV{'form.sel_'.$nfield} eq 'Default') { + $fo{'A'.$nfield}.='__&&&__'.$ENV{'form.sel_'.$nfield}; + } + } else { + if ($nfield) { $fo{$nfield}=$nform; } + } &setformulas($safeeval,%fo); } @@ -939,7 +1374,7 @@ sub parmval { # ----------------------------------------------------- Cascading lookup scheme my $rwhat=$what; $what=~s/^parameter\_//; - $what=~s/\_/\./; + $what=~s/\_([^\_]+)$/\.$1/; my $symbparm=$symb.'.'.$what; my $mapparm=$mapname.'___(all).'.$what; @@ -1019,7 +1454,7 @@ sub updateclasssheet { my %currentlist=(); my $now=time; unless ($classlst=~/^error\:/) { - map { + foreach (split(/\&/,$classlst)) { my ($name,$value)=split(/\=/,$_); my ($end,$start)=split(/\:/,&Apache::lonnet::unescape($value)); my $active=1; @@ -1030,21 +1465,40 @@ sub updateclasssheet { my ($sname,$sdom)=split(/\:/,$name); my $ssec=&Apache::lonnet::usection($sdom,$sname,$cid); if ($ssec==-1) { + unless ($ENV{'form.showcsv'}) { $rowlabel='Data not available: '.$name. ''; + } 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', &Apache::lonnet::homeserver($sname,$sdom)); - $rowlabel=$ssec.' '.$reply{$sname}.'
'; - map { + unless ($ENV{'form.showcsv'}) { + $rowlabel=''. + $ssec.' '.$reply{$sname}.'
'; + foreach ( split(/\&/,$reply)) { $rowlabel.=&Apache::lonnet::unescape($_).' '; - } split(/\&/,$reply); + } + $rowlabel.='
'; + } else { + $rowlabel=$ssec.'","'.$reply{$sname}.'"'; + my $ncount=0; + foreach (split(/\&/,$reply)) { + $rowlabel.=',"'.&Apache::lonnet::unescape($_).'"'; + $ncount++; + } + unless ($ncount==4) { $rowlabel.=',""'; } + $rowlabel=~s/\"$//; + } } $currentlist{&Apache::lonnet::unescape($name)}=$rowlabel; } - } split(/\&/,$classlst); + } # end of foreach (split(/\&/,$classlst)) # # -------------------- Find discrepancies between the course row table and this # @@ -1055,7 +1509,7 @@ sub updateclasssheet { my %existing=(); # ----------------------------------------------------------- Now obsolete rows - map { + foreach (keys(%f)) { if ($_=~/^A(\d+)/) { $maxrow=($1>$maxrow)?$1:$maxrow; $existing{$f{$_}}=1; @@ -1064,17 +1518,17 @@ sub updateclasssheet { $changed=1; } } - } keys %f; + } # -------------------------------------------------------- New and unknown keys - map { + foreach (sort keys(%currentlist)) { unless ($existing{$_}) { $changed=1; $maxrow++; $f{'A'.$maxrow}=$_; } - } sort keys %currentlist; + } if ($changed) { &setformulas($safeeval,%f); } @@ -1099,10 +1553,37 @@ sub updatestudentassesssheet { &GDBM_READER,0640)) { # --------------------------------------------------------- Get all assessments - my %allkeys=(); - my %allassess=(); + my %allkeys=('timestamp' => + 'Timestamp of Last Transaction
timestamp', + 'subnumber' => + 'Number of Submissions
subnumber', + 'tutornumber' => + 'Number of Tutor Responses
tutornumber', + 'totalpoints' => + 'Total Points Granted
totalpoints'); + + my $adduserstr=''; + if ((&getuname($safeeval) ne $ENV{'user.name'}) || + (&getudom($safeeval) ne $ENV{'user.domain'})) { + $adduserstr='&uname='.&getuname($safeeval). + '&udom='.&getudom($safeeval); + } + + my %allassess=('_feedback' => + 'Feedback', + '_evaluation' => + 'Evaluation', + '_tutoring' => + 'Tutoring', + '_discussion' => + 'Discussion' + ); - map { + foreach (keys(%bighash)) { if ($_=~/^src\_(\d+)\.(\d+)$/) { my $mapid=$1; my $resid=$2; @@ -1114,24 +1595,27 @@ sub updatestudentassesssheet { '___'.$resid.'___'. &Apache::lonnet::declutter($srcf); $allassess{$symb}= - ''.$bighash{'title_'.$id}.''; + ''. + $bighash{'title_'.$id}.''; if ($stype eq 'assesscalc') { - map { + foreach (split(/\,/, + &Apache::lonnet::metadata($srcf,'keys'))) { if (($_=~/^stores\_(.*)/) || ($_=~/^parameter\_(.*)/)) { my $key=$_; my $display= &Apache::lonnet::metadata($srcf,$key.'.display'); unless ($display) { - $display= + $display.= &Apache::lonnet::metadata($srcf,$key.'.name'); } + $display.='
'.$key; $allkeys{$key}=$display; } - } split(/\,/,&Apache::lonnet::metadata($srcf,'keys')); + } # end of foreach } } } - } keys %bighash; + } # end of foreach (keys(%bighash)) untie(%bighash); # @@ -1163,26 +1647,30 @@ sub updatestudentassesssheet { my %existing=(); # ----------------------------------------------------------- Now obsolete rows - map { + foreach (keys(%f)) { if ($_=~/^A(\d+)/) { $maxrow=($1>$maxrow)?$1:$maxrow; - $existing{$f{$_}}=1; - unless ((defined($current{$f{$_}})) || (!$1)) { + 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\=/; } } - } keys %f; + } # -------------------------------------------------------- New and unknown keys - map { + foreach (keys(%current)) { unless ($existing{$_}) { $changed=1; $maxrow++; $f{'A'.$maxrow}=$_; } - } keys %current; + } if ($changed) { &setformulas($safeeval,%f); } @@ -1207,35 +1695,41 @@ sub loadstudent { &getcid($safeeval), &getuhome($safeeval)); unless ($reply=~/^error\:/) { - map { + foreach ( split(/\&/,$reply)) { my ($name,$value)=split(/\=/,$_); $cachedstores{&Apache::lonnet::unescape($name)}= &Apache::lonnet::unescape($value); - } split(/\&/,$reply); + } } } my @assessdata=(); - map { + foreach (keys(%f)) { if ($_=~/^A(\d+)/) { my $row=$1; - unless ($f{$_}=~/^\!/) { + unless (($f{$_}=~/^\!/) || ($row==0)) { + my ($usy,$ufn)=split(/\_\_\&\&\&\_\_/,$f{$_}); @assessdata=&exportsheet(&getuname($safeeval), &getudom($safeeval), - 'assesscalc',$f{$_}); + 'assesscalc',$usy,$ufn); my $index=0; - map { + foreach ('A','B','C','D','E','F','G','H','I','J','K','L','M', + 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z') { if ($assessdata[$index]) { - $c{$_.$row}=$assessdata[$index]; - unless ($_ eq 'A') { - $f{$_.$row}='import'; + my $col=$_; + if ($assessdata[$index]=~/\D/) { + $c{$col.$row}="'".$assessdata[$index]."'"; + } else { + $c{$col.$row}=$assessdata[$index]; + } + unless ($col eq 'A') { + $f{$col.$row}='import'; } } $index++; - } ('A','B','C','D','E','F','G','H','I','J','K','L','M', - 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z'); + } } } - } keys %f; + } $cachedassess=''; undef %cachedstores; &setformulas($safeeval,%f); @@ -1249,29 +1743,29 @@ sub loadcourse { my %c=(); my %f=&getformulas($safeeval); my $total=0; - map { + foreach (keys(%f)) { if ($_=~/^A(\d+)/) { unless ($f{$_}=~/^\!/) { $total++; } } - } keys %f; + } my $now=0; my $since=time; $r->print(< popwin=open('','popwin','width=400,height=100'); popwin.document.writeln(''+ - '

Spreadsheet Calculation Progress

'+ + '

Spreadsheet Calculation Progress

'+ '
'+ '
'+ ''); - popwin.document.close; + popwin.document.close(); ENDPOP $r->rflush(); - map { + foreach (keys(%f)) { if ($_=~/^A(\d+)/) { my $row=$1; - unless ($f{$_}=~/^\!/) { + unless (($f{$_}=~/^\!/) || ($row==0)) { my @studentdata=&exportsheet(split(/\:/,$f{$_}), 'studentcalc'); undef %userrdatas; @@ -1282,22 +1776,27 @@ ENDPOP $r->rflush(); my $index=0; - map { + foreach ('A','B','C','D','E','F','G','H','I','J','K','L','M', + 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z') { if ($studentdata[$index]) { - $c{$_.$row}=$studentdata[$index]; - unless ($_ eq 'A') { - $f{$_.$row}='import'; + my $col=$_; + if ($studentdata[$index]=~/\D/) { + $c{$col.$row}="'".$studentdata[$index]."'"; + } else { + $c{$col.$row}=$studentdata[$index]; + } + unless ($col eq 'A') { + $f{$col.$row}='import'; } } $index++; - } ('A','B','C','D','E','F','G','H','I','J','K','L','M', - 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z'); + } } } - } keys %f; + } &setformulas($safeeval,%f); &setconstants($safeeval,%c); - $r->print(''); + $r->print(''); $r->rflush(); } @@ -1330,9 +1829,9 @@ sub loadassessment { my $version=$cachedstores{'version:'.$symb}; my $scope; for ($scope=1;$scope<=$version;$scope++) { - map { + foreach (split(/\:/,$cachedstores{$scope.':keys:'.$symb})) { $returnhash{$_}=$cachedstores{$scope.':'.$symb.':'.$_}; - } split(/\:/,$cachedstores{$scope.':keys:'.$symb}); + } } } else { @@ -1344,20 +1843,32 @@ sub loadassessment { "restore:$udom:$uname:". &Apache::lonnet::escape($namespace).":". &Apache::lonnet::escape($symb),$uhome); - map { + foreach (split(/\&/,$answer)) { my ($name,$value)=split(/\=/,$_); $returnhash{&Apache::lonnet::unescape($name)}= &Apache::lonnet::unescape($value); - } split(/\&/,$answer); + } my $version; for ($version=1;$version<=$returnhash{'version'};$version++) { - map { + foreach (split(/\:/,$returnhash{$version.':keys'})) { $returnhash{$_}=$returnhash{$version.':'.$_}; - } split(/\:/,$returnhash{$version.':keys'}); + } } } # ----------------------------- returnhash now has all stores for this resource +# --------- convert all "_" to "." to be able to use libraries, multiparts, etc + + my @oldkeys=keys %returnhash; + + foreach (@oldkeys) { + my $name=$_; + my $value=$returnhash{$_}; + delete $returnhash{$_}; + $name=~s/\_/\./g; + $returnhash{$name}=$value; + } + # ---------------------------- initialize coursedata and userdata for this user undef %courseopt; undef %useropt; @@ -1375,11 +1886,11 @@ sub loadassessment { $courserdatas{$cid.'.last_cache'}=time; } } - map { + foreach (split(/\&/,$courserdatas{$cid})) { my ($name,$value)=split(/\=/,$_); $courseopt{$userprefix.&Apache::lonnet::unescape($name)}= &Apache::lonnet::unescape($value); - } split(/\&/,$courserdatas{$cid}); + } # --------------------------------------------------- Get userdata (if present) unless ((time-$userrdatas{$uname.'___'.$udom.'.last_cache'})<240) { @@ -1390,35 +1901,52 @@ sub loadassessment { $userrdatas{$uname.'___'.$udom.'.last_cache'}=time; } } - map { + foreach (split(/\&/,$userrdatas{$uname.'___'.$udom})) { my ($name,$value)=split(/\=/,$_); $useropt{$userprefix.&Apache::lonnet::unescape($name)}= &Apache::lonnet::unescape($value); - } split(/\&/,$userrdatas{$uname.'___'.$udom}); + } } # ----------------- now courseopt, useropt initialized for this user and course # (used by parmval) +# +# Load keys for this assessment only +# + my %thisassess=(); + my ($symap,$syid,$srcf)=split(/\_\_\_/,$symb); + + foreach (split(/\,/,&Apache::lonnet::metadata($srcf,'keys'))) { + $thisassess{$_}=1; + } +# +# Load parameters +# my %c=(); if (tie(%parmhash,'GDBM_File', &getcfn($safeeval).'_parms.db',&GDBM_READER,0640)) { my %f=&getformulas($safeeval); - map { + foreach (keys(%f)) { if ($_=~/^A/) { unless ($f{$_}=~/^\!/) { if ($f{$_}=~/^parameter/) { - $c{$_}=&parmval($f{$_},$safeeval); + if ($thisassess{$f{$_}}) { + my $val=&parmval($f{$_},$safeeval); + $c{$_}=$val; + $c{$f{$_}}=$val; + } } else { my $key=$f{$_}; + my $ckey=$key; $key=~s/^stores\_/resource\./; - $key=~s/\_/\./; + $key=~s/\_/\./g; $c{$_}=$returnhash{$key}; - $c{$key}=$returnhash{$key}; + $c{$ckey}=$returnhash{$key}; } } } - } keys %f; + } untie(%parmhash); } &setconstants($safeeval,%c); @@ -1440,11 +1968,11 @@ sub hiddenfield { sub selectbox { my ($title,$name,$value,%options)=@_; my $selout="\n

$title:
".''; } @@ -1480,22 +2008,235 @@ sub loadrows { } } +# ======================================================= Forced recalculation? + +sub checkthis { + my ($keyname,$time)=@_; + return ($time<$expiredates{$keyname}); +} +sub forcedrecalc { + my ($uname,$udom,$stype,$usymb)=@_; + my $key=$uname.':'.$udom.':'.$stype.':'.$usymb; + my $time=$oldsheets{$key.'.time'}; + if ($ENV{'form.forcerecalc'}) { return 1; } + unless ($time) { return 1; } + if ($stype eq 'assesscalc') { + my $map=(split(/\_\_\_/,$usymb))[0]; + if (&checkthis('::assesscalc:',$time) || + &checkthis('::assesscalc:'.$map,$time) || + &checkthis('::assesscalc:'.$usymb,$time) || + &checkthis($uname.':'.$udom.':assesscalc:',$time) || + &checkthis($uname.':'.$udom.':assesscalc:'.$map,$time) || + &checkthis($uname.':'.$udom.':assesscalc:'.$usymb,$time)) { + return 1; + } + } else { + if (&checkthis('::studentcalc:',$time) || + &checkthis($uname.':'.$udom.':studentcalc:',$time)) { + return 1; + } + } + return 0; +} + # ============================================================== Export handler # # Non-interactive call from with program # sub exportsheet { + my ($uname,$udom,$stype,$usymb,$fn)=@_; + my @exportarr=(); + + if (($usymb=~/^\_(\w+)/) && (!$fn)) { + $fn='default_'.$1; + } + +# +# Check if cached +# + + my $key=$uname.':'.$udom.':'.$stype.':'.$usymb; + my $found=''; + + if ($oldsheets{$key}) { + foreach (split(/\_\_\_\&\_\_\_/,$oldsheets{$key})) { + my ($name,$value)=split(/\_\_\_\=\_\_\_/,$_); + if ($name eq $fn) { + $found=$value; + } + } + } + + unless ($found) { + &cachedssheets($uname,$udom,&Apache::lonnet::homeserver($uname,$udom)); + if ($oldsheets{$key}) { + foreach (split(/\_\_\_\&\_\_\_/,$oldsheets{$key})) { + my ($name,$value)=split(/\_\_\_\=\_\_\_/,$_); + if ($name eq $fn) { + $found=$value; + } + } + } + } +# +# Check if still valid +# + if ($found) { + if (&forcedrecalc($uname,$udom,$stype,$usymb)) { + $found=''; + } + } - my ($uname,$udom,$stype,$usymb,$fn)=@_; + 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); - return &exportdata($thissheet); + &calcsheet($thissheet); + @exportarr=&exportdata($thissheet); +# +# Store now +# + my $cid=$ENV{'request.course.id'}; + my $current=''; + if ($stype eq 'studentcalc') { + $current=&Apache::lonnet::reply('get:'. + $ENV{'course.'.$cid.'.domain'}.':'. + $ENV{'course.'.$cid.'.num'}. + ':nohist_calculatedsheets:'. + &Apache::lonnet::escape($key), + $ENV{'course.'.$cid.'.home'}); + } else { + $current=&Apache::lonnet::reply('get:'. + &getudom($thissheet).':'. + &getuname($thissheet). + ':nohist_calculatedsheets_'. + $ENV{'request.course.id'}.':'. + &Apache::lonnet::escape($key), + &getuhome($thissheet)); + + } + my %currentlystored=(); + unless ($current=~/^error\:/) { + foreach (split(/\_\_\_\&\_\_\_/,&Apache::lonnet::unescape($current))) { + my ($name,$value)=split(/\_\_\_\=\_\_\_/,$_); + $currentlystored{$name}=$value; + } + } + $currentlystored{$fn}=join('___;___',@exportarr); + + my $newstore=''; + foreach (keys(%currentlystored)) { + if ($newstore) { $newstore.='___&___'; } + $newstore.=$_.'___=___'.$currentlystored{$_}; + } + my $now=time; + if ($stype eq 'studentcalc') { + &Apache::lonnet::reply('put:'. + $ENV{'course.'.$cid.'.domain'}.':'. + $ENV{'course.'.$cid.'.num'}. + ':nohist_calculatedsheets:'. + &Apache::lonnet::escape($key).'='. + &Apache::lonnet::escape($newstore).'&'. + &Apache::lonnet::escape($key).'.time='.$now, + $ENV{'course.'.$cid.'.home'}); + } else { + &Apache::lonnet::reply('put:'. + &getudom($thissheet).':'. + &getuname($thissheet). + ':nohist_calculatedsheets_'. + $ENV{'request.course.id'}.':'. + &Apache::lonnet::escape($key).'='. + &Apache::lonnet::escape($newstore).'&'. + &Apache::lonnet::escape($key).'.time='.$now, + &getuhome($thissheet)); + } + } + return @exportarr; +} +# ============================================================ Expiration Dates +# +# Load previously cached student spreadsheets for this course +# + +sub expirationdates { + undef %expiredates; + my $cid=$ENV{'request.course.id'}; + my $reply=&Apache::lonnet::reply('dump:'. + $ENV{'course.'.$cid.'.domain'}.':'. + $ENV{'course.'.$cid.'.num'}. + ':nohist_expirationdates', + $ENV{'course.'.$cid.'.home'}); + unless ($reply=~/^error\:/) { + foreach (split(/\&/,$reply)) { + my ($name,$value)=split(/\=/,$_); + $expiredates{&Apache::lonnet::unescape($name)} + =&Apache::lonnet::unescape($value); + } + } +} + +# ===================================================== Calculated sheets cache +# +# Load previously cached student spreadsheets for this course +# + +sub cachedcsheets { + my $cid=$ENV{'request.course.id'}; + my $reply=&Apache::lonnet::reply('dump:'. + $ENV{'course.'.$cid.'.domain'}.':'. + $ENV{'course.'.$cid.'.num'}. + ':nohist_calculatedsheets', + $ENV{'course.'.$cid.'.home'}); + unless ($reply=~/^error\:/) { + foreach ( split(/\&/,$reply)) { + my ($name,$value)=split(/\=/,$_); + $oldsheets{&Apache::lonnet::unescape($name)} + =&Apache::lonnet::unescape($value); + } + } } +# ===================================================== Calculated sheets cache +# +# Load previously cached assessment spreadsheets for this student +# + +sub cachedssheets { + my ($sname,$sdom,$shome)=@_; + unless (($loadedcaches{$sname.'_'.$sdom}) || ($shome eq 'no_host')) { + my $cid=$ENV{'request.course.id'}; + my $reply=&Apache::lonnet::reply('dump:'.$sdom.':'.$sname. + ':nohist_calculatedsheets_'. + $ENV{'request.course.id'}, + $shome); + unless ($reply=~/^error\:/) { + foreach ( split(/\&/,$reply)) { + my ($name,$value)=split(/\=/,$_); + $oldsheets{&Apache::lonnet::unescape($name)} + =&Apache::lonnet::unescape($value); + } + } + $loadedcaches{$sname.'_'.$sdom}=1; + } +} + +# ===================================================== Calculated sheets cache +# +# Load previously cached assessment spreadsheets for this student +# + # ================================================================ Main handler # # Interactive call to screen @@ -1523,7 +2264,7 @@ $tmpdir=$r->dir_config('lonDaemons').'/t # --------------------------- Get query string for limited number of parameters - map { + 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; @@ -1533,8 +2274,16 @@ $tmpdir=$r->dir_config('lonDaemons').'/t $ENV{'form.'.$name}=$value; } } - } (split(/&/,$ENV{'QUERY_STRING'})); + } + + if (($ENV{'form.usymb'}=~/^\_(\w+)/) && (!$ENV{'form.ufn'})) { + $ENV{'form.ufn'}='default_'.$1; + } +# -------------------------------------- Interactive loading of specific sheet? + if (($ENV{'form.load'}) && ($ENV{'form.loadthissheet'} ne 'Default')) { + $ENV{'form.ufn'}=$ENV{'form.loadthissheet'}; + } # ------------------------------------------- Nothing there? Must be login user my $aname; @@ -1570,6 +2319,12 @@ $tmpdir=$r->dir_config('lonDaemons').'/t } } + function changesheet(cn) { + document.sheet.unewfield.value=cn; + document.sheet.unewformula.value='changesheet'; + document.sheet.submit(); + } + ENDSCRIPT $r->print(''. @@ -1586,6 +2341,18 @@ ENDSCRIPT $r->rflush(); +# ---------------------------------------------------------------- Full recalc? + + + if ($ENV{'form.forcerecalc'}) { + $r->print('

Completely Recalculating Sheet ...

'); + undef %spreadsheets; + undef %courserdatas; + undef %userrdatas; + undef %defaultsheets; + undef %updatedata; + } + # ---------------------------------------- Read new sheet or modified worksheet $r->uri=~/\/(\w+)$/; @@ -1621,34 +2388,48 @@ ENDSCRIPT } else { $r->print('
Section/Group: '.&getcsec($asheet)); } + if ($ENV{'form.usymb'}) { + $r->print('
Assessment: '.$ENV{'form.usymb'}.''); + } } # ---------------------------------------------------------------- Course title $r->print('

'. - $ENV{'course.'.$ENV{'request.course.id'}.'.description'}.'

'); + $ENV{'course.'.$ENV{'request.course.id'}.'.description'}. + '

'.localtime().'

'); +# ---------------------------------------------------- See if user can see this -# ---------------------------------------------------- See if something to save - - if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) { - my $fname=''; - if ($ENV{'form.saveas'} && ($fname=$ENV{'form.newfn'})) { - $fname=~s/\W/\_/g; - if ($fname eq 'default') { $fname='course_default'; } - $fname.='_'.&gettype($asheet); - &setfilename($asheet,$fname); - $ENV{'form.ufn'}=$fname; - $r->print('

Saving spreadsheet: '. - &writesheet($asheet,$ENV{'form.makedefufn'}).'

'); - } + 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( + '

Access Permission Denied

'); + return OK; + } } -# ------------------------------------------------ Write the modified worksheet - - $r->print('Current sheet: '.&getfilename($asheet).'

'); +# ---------------------------------------------------------- Additional options - &tmpwrite($asheet); + $r->print( + '

' + ); + if (&gettype($asheet) eq 'assesscalc') { + $r->print ('

Level up: Student Sheet

'); + } + + if ((&gettype($asheet) eq 'studentcalc') && + (&Apache::lonnet::allowed('vgr',&getcid($asheet)))) { + $r->print ( + '

'. + 'Level up: Course Sheet

'); + } + # ----------------------------------------------------------------- Save dialog @@ -1664,10 +2445,45 @@ ENDSCRIPT $r->print(&hiddenfield('ufn',&getfilename($asheet))); +# ----------------------------------------------------------------- Load dialog + if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) { + $r->print('

'. + '

'); + if (&gettype($asheet) eq 'studentcalc') { + &setothersheets($asheet,&othersheets($asheet,'assesscalc')); + } + } + +# --------------------------------------------------------------- Cached sheets + + &expirationdates(); + + undef %oldsheets; + undef %loadedcaches; + + if (&gettype($asheet) eq 'classcalc') { + $r->print("Loading previously calculated student sheets ...
\n"); + $r->rflush(); + &cachedcsheets(); + } elsif (&gettype($asheet) eq 'studentcalc') { + $r->print("Loading previously calculated assessment sheets ...
\n"); + $r->rflush(); + &cachedssheets(&getuname($asheet),&getudom($asheet), + &getuhome($asheet)); + } # ----------------------------------------------------- Update sheet, load rows - $r->print("Loaded sheet, updating rows ...
\n"); + $r->print("Loaded sheet(s), updating rows ...
\n"); $r->rflush(); &updatesheet($asheet); @@ -1683,6 +2499,56 @@ ENDSCRIPT my $calcoutput=&calcsheet($asheet); $r->print('

'.$calcoutput.'

'); +# ---------------------------------------------------- See if something to save + + if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) { + my $fname=''; + if ($ENV{'form.saveas'} && ($fname=$ENV{'form.newfn'})) { + $fname=~s/\W/\_/g; + if ($fname eq 'default') { $fname='course_default'; } + $fname.='_'.&gettype($asheet); + &setfilename($asheet,$fname); + $ENV{'form.ufn'}=$fname; + $r->print('

Saving spreadsheet: '. + &writesheet($asheet,$ENV{'form.makedefufn'}).'

'); + } + } + +# ------------------------------------------------ Write the modified worksheet + + $r->print('Current sheet: '.&getfilename($asheet).'

'); + + &tmpwrite($asheet); + + if (&gettype($asheet) eq 'studentcalc') { + $r->print('
Show rows with empty A column: '); + } else { + $r->print('
Show empty rows: '); + } + + $r->print(&hiddenfield('userselhidden','true'). + 'print(' checked'); + } else { + unless ($ENV{'form.userselhidden'}) { + unless + ($ENV{'course.'.$ENV{'request.course.id'}.'.hideemptyrows'} eq 'yes') { + $r->print(' checked'); + $ENV{'form.showall'}=1; + } + } + } + $r->print('>'); + if (&gettype($asheet) eq 'classcalc') { + $r->print( + ' Output CSV format: print(' checked'); } + $r->print('>'); + } +# ------------------------------------------------------------- Print out sheet + &outsheet($r,$asheet); $r->print(''); @@ -1702,16 +2568,4 @@ __END__ - - - - - - - - - - - -