--- loncom/interface/Attic/lonspreadsheet.pm 2001/01/02 20:19:34 1.29 +++ loncom/interface/Attic/lonspreadsheet.pm 2001/03/10 16:46:52 1.41 @@ -3,10 +3,11 @@ # # 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 Gerd Kortemeyer +# 01/01/01,02/01,03/01,19/01,20/01,22/01, +# 03/05,03/08,03/10 Gerd Kortemeyer package Apache::lonspreadsheet; - + use strict; use Safe; use Safe::Hole; @@ -17,10 +18,22 @@ use GDBM_File; use HTML::TokeParser; # +# Cache for stores of an individual user +# + +my $cachedassess; +my %cachedstores; + +# # These cache hashes need to be independent of user, resource and course # (user and course can/should be in the keys) # -use vars qw(%spreadsheets %courserdatas %userrdatas %defaultsheets); + +my %spreadsheets; +my %courserdatas; +my %userrdatas; +my %defaultsheets; +my %updatedata; # # These global hashes are dependent on user, course and resource, @@ -39,7 +52,7 @@ my $tmpdir; # ===================================== Implements an instance of a spreadsheet sub initsheet { - my $safeeval = new Safe; + my $safeeval = new Safe(shift); my $safehole = new Safe::Hole; $safeeval->permit("entereval"); $safeeval->permit(":base_math"); @@ -56,11 +69,11 @@ sub initsheet { # c: preloaded constants (A-column) # rl: row label -%v=(); -%t=(); -%f=(); -%c=(); -%rl=(); +undef %v; +undef %t; +undef %f; +undef %c; +undef %rl; $maxrow=0; $sheettype=''; @@ -304,6 +317,7 @@ 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; } } } keys %f; @@ -320,12 +334,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; } } } 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; } sub calc { @@ -423,22 +439,22 @@ ENDDEFS # ------------------------------------------------ Add or change formula values sub setformulas { - my ($safeeval,@f)=@_; - $safeeval->reval('%f='."('".join("','",@f)."');"); + my ($safeeval,%f)=@_; + %{$safeeval->varglob('f')}=%f; } # ------------------------------------------------ Add or change formula values sub setconstants { - my ($safeeval,@c)=@_; - $safeeval->reval('%c='."('".join("','",@c)."');"); + my ($safeeval,%c)=@_; + %{$safeeval->varglob('c')}=%c; } # ------------------------------------------------ Add or change formula values sub setrowlabels { - my ($safeeval,@rl)=@_; - $safeeval->reval('%rl='."('".join("','",@rl)."');"); + my ($safeeval,%rl)=@_; + %{$safeeval->varglob('rl')}=%rl; } # ------------------------------------------------------- Calculate spreadsheet @@ -459,7 +475,7 @@ sub getvalues { sub getformulas { my $safeeval=shift; - return $safeeval->reval('%f'); + return %{$safeeval->varglob('f')}; } # -------------------------------------------------------------------- Get type @@ -774,13 +790,15 @@ sub readsheet { sub makenewsheet { my ($uname,$udom,$stype,$usymb)=@_; - my $safeeval=initsheet(); + my $safeeval=initsheet($stype); $safeeval->reval( '$uname="'.$uname. '";$udom="'.$udom. '";$uhome="'.&Apache::lonnet::homeserver($uname,$udom). '";$sheettype="'.$stype. '";$usymb="'.$usymb. + '";$csec="'.&Apache::lonnet::usection($udom,$uname, + $ENV{'request.course.id'}). '";$cid="'.$ENV{'request.course.id'}. '";$cfn="'.$ENV{'request.course.fn'}. '";$cnum="'.$ENV{'course.'.$ENV{'request.course.id'}.'.num'}. @@ -815,7 +833,7 @@ sub writesheet { $sheetdata,$chome); if ($reply eq 'ok') { $reply=&Apache::lonnet::reply('put:'.$cdom.':'.$cnum.':'. - &stype.'_spreadsheets:'. + $stype.'_spreadsheets:'. &Apache::lonnet::escape($fn).'='.$ENV{'user.name'}, $chome); if ($reply eq 'ok') { @@ -1054,6 +1072,9 @@ sub updateclasssheet { sub updatestudentassesssheet { my $safeeval=shift; my %bighash; + my $stype=&gettype($safeeval); + my %current=(); + unless ($updatedata{$ENV{'request.course.fn'}.'_'.$stype}) { # -------------------------------------------------------------------- Tie hash if (tie(%bighash,'GDBM_File',$ENV{'request.course.fn'}.'.db', &GDBM_READER,0640)) { @@ -1062,8 +1083,6 @@ sub updatestudentassesssheet { my %allkeys=(); my %allassess=(); - my $stype=&gettype($safeeval); - map { if ($_=~/^src\_(\d+)\.(\d+)$/) { my $mapid=$1; @@ -1075,8 +1094,8 @@ sub updatestudentassesssheet { &Apache::lonnet::declutter($bighash{'map_id_'.$mapid}). '___'.$resid.'___'. &Apache::lonnet::declutter($srcf); - $allassess{$symb}=$bighash{'title_'.$id}; - + $allassess{$symb}= + ''.$bighash{'title_'.$id}.''; if ($stype eq 'assesscalc') { map { if (($_=~/^stores\_(.*)/) || ($_=~/^parameter\_(.*)/)) { @@ -1084,9 +1103,10 @@ sub updatestudentassesssheet { 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')); @@ -1100,17 +1120,26 @@ sub updatestudentassesssheet { # %allkeys has a list of storage and parameter displays by unikey # %allassess has a list of all resource displays by symb # -# -------------------- Find discrepancies between the course row table and this -# - my %f=&getformulas($safeeval); - my $changed=0; - my %current=(); if ($stype eq 'assesscalc') { %current=%allkeys; } elsif ($stype eq 'studentcalc') { %current=%allassess; } + $updatedata{$ENV{'request.course.fn'}.'_'.$stype}= + join('___;___',%current); + } else { + return 'Could not access course data'; + } +# ------------------------------------------------------ Get current from cache + } else { + %current=split(/\_\_\_\;\_\_\_/, + $updatedata{$ENV{'request.course.fn'}.'_'.$stype}); + } +# -------------------- Find discrepancies between the course row table and this +# + my %f=&getformulas($safeeval); + my $changed=0; my $maxrow=0; my %existing=(); @@ -1136,15 +1165,14 @@ sub updatestudentassesssheet { $f{'A'.$maxrow}=$_; } } keys %current; - + if ($changed) { &setformulas($safeeval,%f); } &setmaxrow($safeeval,$maxrow); &setrowlabels($safeeval,%current); - - } else { - return 'Could not access course data'; - } + + undef %current; + undef %existing; } # ------------------------------------------------ Load data for one assessment @@ -1153,66 +1181,116 @@ sub loadstudent { my $safeeval=shift; my %c=(); my %f=&getformulas($safeeval); + $cachedassess=&getuname($safeeval).':'.&getudom($safeeval); + %cachedstores=(); + { + my $reply=&Apache::lonnet::reply('dump:'.&getudom($safeeval).':'. + &getuname($safeeval).':'. + &getcid($safeeval), + &getuhome($safeeval)); + unless ($reply=~/^error\:/) { + map { + my ($name,$value)=split(/\=/,$_); + $cachedstores{&Apache::lonnet::unescape($name)}= + &Apache::lonnet::unescape($value); + } split(/\&/,$reply); + } + } + my @assessdata=(); map { if ($_=~/^A(\d+)/) { my $row=$1; unless ($f{$_}=~/^\!/) { - # my @assessdata=split(/\_\_\_\;\_\_\_/, - # &Apache::lonnet::ssi( - # '/adm/assesscalc',('utarget' => 'export', - # 'uname' => $uname, - # 'udom' => $udom, -# 'usymb' => $f{$_}))); + @assessdata=&exportsheet(&getuname($safeeval), + &getudom($safeeval), + 'assesscalc',$f{$_}); my $index=0; -# map { -# if ($assessdata[$index]) { -# $c{$_.$row}=$assessdata[$index]; -# unless ($_ eq 'A') { -# $f{$_.$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'); + map { + if ($assessdata[$index]) { + 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); &setconstants($safeeval,%c); } # --------------------------------------------------- Load data for one student -sub loadclass { - my $safeeval=shift; +sub loadcourse { + my ($safeeval,$r)=@_; my %c=(); my %f=&getformulas($safeeval); + my $total=0; + map { + 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

'+ + '
'+ + '
'+ + ''); + popwin.document.close; + +ENDPOP + $r->rflush(); map { if ($_=~/^A(\d+)/) { my $row=$1; unless ($f{$_}=~/^\!/) { - my ($tname,$tdom)=split(/\:/,$_); -# my @assessdata=split(/\_\_\_\;\_\_\_/, -# &Apache::lonnet::ssi( -# '/adm/studentcalc',('utarget' => 'export', -# 'uname' => $tname, -# 'udom' => $tdom))); + my @studentdata=&exportsheet(split(/\:/,$f{$_}), + 'studentcalc'); + undef %userrdatas; + $now++; + $r->print(''); + $r->rflush(); + my $index=0; -# map { -# if ($assessdata[$index]) { -# $c{$_.$row}=$assessdata[$index]; -# unless ($_ eq 'A') { -# $f{$_.$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'); + map { + if ($studentdata[$index]) { + 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->rflush(); } # ------------------------------------------------ Load data for one assessment @@ -1233,11 +1311,31 @@ sub loadassessment { unless ($namespace=$cid) { return ''; } # ----------------------------------------------------------- Get stored values + + my %returnhash=(); + + if ($cachedassess eq $uname.':'.$udom) { +# +# get data out of the dumped stores +# + + my $version=$cachedstores{'version:'.$symb}; + my $scope; + for ($scope=1;$scope<=$version;$scope++) { + map { + $returnhash{$_}=$cachedstores{$scope.':'.$symb.':'.$_}; + } split(/\:/,$cachedstores{$scope.':keys:'.$symb}); + } + + } else { +# +# restore individual +# + my $answer=&Apache::lonnet::reply( "restore:$udom:$uname:". &Apache::lonnet::escape($namespace).":". &Apache::lonnet::escape($symb),$uhome); - my %returnhash=(); map { my ($name,$value)=split(/\=/,$_); $returnhash{&Apache::lonnet::unescape($name)}= @@ -1249,18 +1347,19 @@ sub loadassessment { $returnhash{$_}=$returnhash{$version.':'.$_}; } split(/\:/,$returnhash{$version.':keys'}); } + } # ----------------------------- returnhash now has all stores for this resource # ---------------------------- initialize coursedata and userdata for this user - %courseopt=(); - %useropt=(); + undef %courseopt; + undef %useropt; my $userprefix=$uname.'_'.$udom.'_'; unless ($uhome eq 'no_host') { # -------------------------------------------------------------- Get coursedata unless - ((time-$courserdatas{$cid.'.last_cache'})<120) { + ((time-$courserdatas{$cid.'.last_cache'})<240) { my $reply=&Apache::lonnet::reply('dump:'.$cdom.':'.$cnum. ':resourcedata',$chome); if ($reply!~/^error\:/) { @@ -1272,10 +1371,10 @@ sub loadassessment { my ($name,$value)=split(/\=/,$_); $courseopt{$userprefix.&Apache::lonnet::unescape($name)}= &Apache::lonnet::unescape($value); - } split(/\&/,$courserdatas{$ENV{'request.course.id'}}); + } split(/\&/,$courserdatas{$cid}); # --------------------------------------------------- Get userdata (if present) unless - ((time-$userrdatas{$uname.'___'.$udom.'.last_cache'})<120) { + ((time-$userrdatas{$uname.'___'.$udom.'.last_cache'})<240) { my $reply= &Apache::lonnet::reply('dump:'.$udom.':'.$uname.':resourcedata',$uhome); if ($reply!~/^error\:/) { @@ -1289,7 +1388,6 @@ sub loadassessment { &Apache::lonnet::unescape($value); } split(/\&/,$userrdatas{$uname.'___'.$udom}); } - # ----------------- now courseopt, useropt initialized for this user and course # (used by parmval) @@ -1302,12 +1400,16 @@ sub loadassessment { if ($_=~/^A/) { unless ($f{$_}=~/^\!/) { if ($f{$_}=~/^parameter/) { - $c{$_}=&parmval($f{$_},$safeeval); + my $val=&parmval($f{$_},$safeeval); + $c{$_}=$val; + $c{$f{$_}}=$val; } else { my $key=$f{$_}; + my $ckey=$key; $key=~s/^stores\_/resource\./; $key=~s/\_/\./; $c{$_}=$returnhash{$key}; + $c{$ckey}=$returnhash{$key}; } } } @@ -1361,11 +1463,11 @@ sub updatesheet { # Import the data for rows # -sub loadrows() { - my $safeeval=shift; +sub loadrows { + my ($safeeval,$r)=@_; my $stype=&gettype($safeeval); if ($stype eq 'classcalc') { - &loadcourse($safeeval); + &loadcourse($safeeval,$r); } elsif ($stype eq 'studentcalc') { &loadstudent($safeeval); } else { @@ -1379,6 +1481,7 @@ sub loadrows() { # sub exportsheet { + my ($uname,$udom,$stype,$usymb,$fn)=@_; my $thissheet=&makenewsheet($uname,$udom,$stype,$usymb); &readsheet($thissheet,$fn); @@ -1484,107 +1587,93 @@ ENDSCRIPT my $asheet=&makenewsheet($aname,$adom,$1,$ENV{'form.usymb'}); -# if ($ENV{'form.unewfield'}) { -# $r->print('

Modified Workcopy

'); -# $ENV{'form.unewformula'}=~s/\'/\"/g; -# $r->print('

New formula: '.$ENV{'form.unewfield'}.'='. -# $ENV{'form.unewformula'}.'

'); -# &setfilename($sheetone,$ENV{'form.ufn'}); -# &tmpread($sheetone,$r->dir_config('lonDaemons').'/tmp/', -# $ENV{'form.usymb'}, -# $ENV{'form.unewfield'},$ENV{'form.unewformula'}); -# } elsif ($ENV{'form.saveas'}) { -# &setfilename($sheetone,$ENV{'form.ufn'}); -# &tmpread($sheetone,, -# $ENV{'form.usymb'}); -# } else { -# unless ($ENV{'form.ufn'}) { -# }# -# -# if (&gettype($sheetone) eq 'classcalc') { -# ---------------------------------- For course view: get courselist and update -# &updatestudentrows($sheetone); -# } else { -# ----------------- For assessment and student: See if all import rows uptodate -# $csec=&Apache::lonnet::usection($udom,$uname,$ENV{'request.course.id'}); -# if ($csec eq '-1') { -# $r->print('

'. -# "User '$uname' at domain '$udom' not a student in this course

"); -# } -# &updaterows($sheetone); -# untie(%parmhash); -# } else { -# $r->print('

'. -# 'Could not initialize import fields (not in a course)

'); -# } -# } +# ------------------------ If a new formula had been entered, go from work copy + + if ($ENV{'form.unewfield'}) { + $r->print('

Modified Workcopy

'); + $ENV{'form.unewformula'}=~s/\'/\"/g; + $r->print('

New formula: '.$ENV{'form.unewfield'}.'='. + $ENV{'form.unewformula'}.'

'); + &setfilename($asheet,$ENV{'form.ufn'}); + &tmpread($asheet, + $ENV{'form.unewfield'},$ENV{'form.unewformula'}); + + } elsif ($ENV{'form.saveas'}) { + &setfilename($asheet,$ENV{'form.ufn'}); + &tmpread($asheet); + } else { + &readsheet($asheet,$ENV{'form.ufn'}); + } + +# -------------------------------------------------- Print out user information + + unless (&gettype($asheet) eq 'classcalc') { + $r->print('

User: '.&getuname($asheet). + '
Domain: '.&getudom($asheet)); + if (&getcsec($asheet) eq '-1') { + $r->print('

'. + 'Not a student in this course

'); + } else { + $r->print('
Section/Group: '.&getcsec($asheet)); + } + } + +# ---------------------------------------------------------------- Course title + + $r->print('

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

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

Saving spreadsheet: '.$reply.'

'); -# } -# if ($ENV{'form.makedefufn'}) { -# my $reply=&Apache::lonnet::reply('put:'. -# $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}.':'. -# $ENV{'course.'.$ENV{'request.course.id'}.'.num'}. -# ':environment:spreadsheet_default_'. -# &gettype($sheetone).'='. -# &Apache::lonnet::escape($fname), -# $ENV{'course.'.$ENV{'request.course.id'}.'.home'}); -# unless ($reroute) { -# $r->print('

Making default spreadsheet: '.$reply.'

'); -# } -# } -# } -# } + + 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 -# -# &tmpwrite($sheetone,$r->dir_config('lonDaemons').'/tmp/', - # $ENV{'form.usymb'}); -# -# ----------------------------------------------------- Print user, course, etc -# unless ($reroute) { -# if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) { -# my $fname=$ENV{'form.ufn'}; -# $fname=~s/\_[^\_]+$//; -# if ($fname eq 'default') { $fname='course_default'; } -# $r->print(''. -# ' (make default: )

'); -# } -# $r->print(&hiddenfield('ufn',$ENV{'form.ufn'})); -# unless (&gettype($sheetone) eq 'classcalc') { -# $r->print('
User: '.$uname.'
Domain: '.$udom); -# } -# $r->print('

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

'); -# if ($csec) { -# $r->print('

Group/Section: '.$csec.'

'); -# } -# } -# -------------------------------------------------------- Import and calculate -# -# if (&gettype($sheetone) eq 'assesscalc') { -# &rowaassess($sheetone,$ENV{'form.usymb'}); -# } elsif (&gettype($sheetone) eq 'studentcalc') { -# &rowazstudent($sheetone); -# } else { -# &rowazclass($sheetone); -# } + $r->print('Current sheet: '.&getfilename($asheet).'

'); + + &tmpwrite($asheet); + +# ----------------------------------------------------------------- Save dialog + + + if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) { + my $fname=$ENV{'form.ufn'}; + $fname=~s/\_[^\_]+$//; + if ($fname eq 'default') { $fname='course_default'; } + $r->print(''. + ' (make default: )

'); + } + + $r->print(&hiddenfield('ufn',&getfilename($asheet))); + + +# ----------------------------------------------------- Update sheet, load rows + + $r->print("Loaded sheet, updating rows ...
\n"); + $r->rflush(); - &readsheet($asheet,''); &updatesheet($asheet); - &loadrows($asheet); + $r->print("Updated rows, loading row data ...
\n"); + $r->rflush(); + + &loadrows($asheet,$r); + + $r->print("Loaded row data, calculating sheet ...
\n"); + $r->rflush(); my $calcoutput=&calcsheet($asheet); $r->print('

'.$calcoutput.'

'); @@ -1592,7 +1681,6 @@ ENDSCRIPT &outsheet($r,$asheet); $r->print(''); - # ------------------------------------------------------------------------ Done } else { # ----------------------------- Not in a course, or not allowed to modify parms