--- loncom/interface/spreadsheet/Spreadsheet.pm 2005/05/12 22:42:57 1.44 +++ loncom/interface/spreadsheet/Spreadsheet.pm 2005/05/17 20:17:03 1.51 @@ -1,5 +1,5 @@ # -# $Id: Spreadsheet.pm,v 1.44 2005/05/12 22:42:57 albertel Exp $ +# $Id: Spreadsheet.pm,v 1.51 2005/05/17 20:17:03 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -106,13 +106,13 @@ sub new { cid => $env{'request.course.id'}, cnum => $env{'course.'.$env{'request.course.id'}.'.num'}, cdom => $env{'course.'.$env{'request.course.id'}.'.domain'}, - chome => $env{'course.'.$env{'request.course.id'}.'.home'}, coursedesc => $env{'course.'.$env{'request.course.id'}.'.description'}, coursefilename => $env{'request.course.fn'}, # # Flags temporary => 0, # true if this sheet has been modified but not saved - new_rows => 0, # true if this sheet has new rows + new_rows => 0, # true if this sheet has new rows + loaded => 0, # true if the formulas have been loaded # # blackout is used to determine if any data needs to be hidden from the # student. @@ -126,18 +126,9 @@ sub new { othersheets => [], }; # - $self->{'uhome'} = &Apache::lonnet::homeserver($name,$domain); - # bless($self,$class); - # - # Load in the spreadsheet definition $self->filename($filename); - if (exists($env{'form.workcopy'}) && - $self->{'type'} eq $env{'form.workcopy'}) { - $self->load_tmp(); - } else { - $self->load(); - } + # return $self; } @@ -330,6 +321,10 @@ sub initialize_safe_space { $safehole->wrap(\&mask,$safeeval,'&mask'); $safehole->wrap(\&Apache::lonnet::logthis,$safeeval,'&logthis'); $safeeval->share('$@'); + # Holds the (computed, final) values for the sheet + # This is only written to by &calc, the spreadsheet computation routine. + # It is read by many functions + $safeeval->share('%sheet_values'); my $code=<<'ENDDEFS'; # ---------------------------------------------------- Inside of the safe space # @@ -339,12 +334,9 @@ sub initialize_safe_space { # c: preloaded constants (A-column) # rl: row label # os: other spreadsheets (for student spreadsheet only) -undef %sheet_values; # Holds the (computed, final) values for the sheet - # This is only written to by &calc, the spreadsheet computation routine. - # It is read by many functions -undef %t; # Holds the values of the spreadsheet temporarily. Set in &sett, - # which does the translation of strings like C5 into the value in C5. - # Used in &calc - %t holds the values that are actually eval'd. +undef %t; # Holds the forumlas of the spreadsheet to be computed. Set in + # &sett, which does the translation of strings like C5 into the value + # in C5. Used in &calc - %t holds the values that are actually eval'd. undef %f; # Holds the formulas for each cell. This is the users # (spreadsheet authors) data for each cell. undef %c; # Holds the constants for a sheet. In the assessment @@ -365,12 +357,10 @@ $filename = ''; # # user data $name = ''; -$uhome = ''; $domain = ''; # # course data $csec = ''; -$chome= ''; $cnum = ''; $cdom = ''; $cid = ''; @@ -670,34 +660,65 @@ sub MAXPARM { return $max; } +#------------------------------------------------------- + +=pod + +=item &get_values($lower,$upper) + +Inputs: $lower and $upper, cell names ("X12" or "a150") or globs ("X*"). + +Returns: an array ref of the values of the cells that exist in the + speced range + +=cut + +#------------------------------------------------------- sub get_values { my ($lower,$upper)=@_; - my $mask=&mask(@_); + $upper = $lower if (! defined($upper)); my @values; - foreach (grep eval("/$mask/"),keys(%sheet_values)) { - push(@values,$sheet_values{$_}); - } - return \@values; - if (0) { - # perhaps creating a list of possible cells and looking if they exist - # would be faster somtimes? - &logthis("mask is ".$mask); + my ($la,$ld) = ($lower=~/([A-z]|\*)(\d+|\*)/); + my ($ua,$ud) = ($upper=~/([A-z]|\*)(\d+|\*)/); + my ($alpha,$num); + if ($ld ne '*' && $ud ne '*') { my @alpha; if (($la eq '*') || ($ua eq '*')) { @alpha=('A'..'z'); } else { - if ($la gt $ua) { - my $tmp = $ua; - $ua = $la; - $la = $ua; + if ($la gt $ua) { ($la,$ua)=($ua,$la); } + if ((lc($la) ne $la) && (lc($ua) eq $ua)) { + @alpha=($la..'Z','a'..$ua); + } else { + @alpha=($la..$ua); + } + } + my @num=($ld..$ud); + foreach my $a (@alpha) { + foreach my $n (@num) { + if (exists($sheet_values{$a.$n})) { + push(@values,$sheet_values{$a.$n}); + } } - $alpha=($la..$ua); } + return \@values; + } else { + $num = '(\d+)'; } + if (($la eq '*') || ($ua eq '*')) { + $alpha='[A-z]'; + } else { + if ($la gt $ua) { ($la,$ua)=($ua,$la); } + $alpha=qq/[$la-$ua]/; + } + my $expression = '^'.$alpha.$num.'$'; + foreach (grep /$expression/,keys(%sheet_values)) { + push(@values,$sheet_values{$_}); + } + return \@values; } sub calc { - %sheet_values = %t; my $notfinished = 1; my $lastcalc = ''; my $depth = 0; @@ -738,7 +759,7 @@ ENDDEFS # itself my $initstring = ''; foreach (qw/name domain type symb cid csec coursefilename - cnum cdom chome uhome/) { + cnum cdom/) { $initstring.= qq{\$$_="$self->{$_}";}; } $initstring.=qq{\$usection="$usection";}; @@ -758,92 +779,6 @@ ENDDEFS ###################################################### - -###################################################### - -=pod - -=item &mask($lower,$upper) - -Inputs: $lower and $upper, cell names ("X12" or "a150") or globs ("X*"). - -Returns: Regular expression matching spreadsheet cells that are within -the rectangle defined by $lower and $upper. Due to the nature of the -regular expression this result must be used inside an eval(). - -=cut - -###################################################### -{ - -my %memoizer; - -sub mask { - my ($lower,$upper)=@_; - my $key = $lower.'_'.$upper; - if (exists($memoizer{$key})) { - return $memoizer{$key}; - } - $upper = $lower if (! defined($upper)); - # - my ($la,$ld) = ($lower=~/([A-z]|\*)(\d+|\*)/); - my ($ua,$ud) = ($upper=~/([A-z]|\*)(\d+|\*)/); - # - my $alpha=''; - my $num=''; - # - # Do not put parenthases around $alpha. - # $num depends on the value in $1. - if (($la eq '*') || ($ua eq '*')) { - $alpha='[A-z]'; - } else { - if ($la gt $ua) { - my $tmp = $ua; - $ua = $la; - $la = $ua; - } - $alpha=qq/[$la-$ua]/; - } - if ($ld ne '*' && $ud ne '*') { - # Make sure $ld <= $ud - if ($ld > $ud) { - my $tmp = $ud; - $ud = $ld; - $ld = $tmp; - } - # Here we make a regular expression using some advanced regexp - # abilities. - # (\d+) will match the digits of the cell name and dump them in - # to $1 - # (?(?{ ... code ...} pattern_if_true | pattern_if_false)) will - # choose pattern_if_true if { ... code ... } is true and - # pattern_if_false if { ... code ... } is false. - # In this case, pattern_if_true is empty. pattern_if_false is - # 'donotmatch' and will not match our cells because none of - # them end with donotmatch. - # Unfortunately, the use of this type of regular expression - # requires that each match be wrapped in an eval(). Search for - # $mask in this module for examples - $num = '(\d+)(?(?{$1>= '.$ld.' && $1<='.$ud.'})|donotmatch)'; - } else { - $num = '(\d+)'; - } - my $expression = '^'.$alpha.$num.'$'; - $memoizer{$key} = $expression; - return $expression; -} - -# -# Debugging routine -sub dump_memoized_values { - while (my ($key,$value) = each(%memoizer)) { - &Apache::lonnet::logthis('memoizer: '.$key.' = '.$value); - } - return; -} - -} - ## ## sub add_hash_to_safe {} # spreadsheet, would like to destroy ## @@ -923,6 +858,7 @@ sub expandnamed { sub sett { my $self = shift; my %t=(); + undef(%Apache::Spreadsheet::sheet_values); # # Deal with the template row foreach my $col ($self->template_cells()) { @@ -955,8 +891,8 @@ sub sett { } elsif ( $col =~ /^[A-Z]$/ ) { if ($formula !~ /^\!/ && exists($self->{'constants'}->{$cell}) && $self->{'constants'}->{$cell} ne '') { - my $data = $self->{'constants'}->{$cell}; - $t{$cell} = $data; + $Apache::Spreadsheet::sheet_values{$cell}= + eval($self->{'constants'}->{$cell}); } } else { # $row > 1 and $col =~ /[a-z] $t{$cell}=$formula; @@ -975,11 +911,11 @@ sub sett { sub sync_safe_space { my $self = shift; # Inside the safe space 'formulas' has a diabolical alter-ego named 'f'. - %{$self->{'safe'}->varglob('f')}=%{$self->{'formulas'}}; + #%{$self->{'safe'}->varglob('f')}=%{$self->{'formulas'}}; # 'constants' leads a peaceful hidden life of 'c'. %{$self->{'safe'}->varglob('c')}=%{$self->{'constants'}}; # 'othersheets' hides as 'os', a disguise few can penetrate. - @{$self->{'safe'}->varglob('os')}=@{$self->{'othersheets'}}; + #@{$self->{'safe'}->varglob('os')}=@{$self->{'othersheets'}}; } ## @@ -1034,6 +970,16 @@ sub formulas { $self->{'template_cells'} = []; return; } else { + if (!$self->{'loaded'}) { + $self->{'loaded'}=1; + # Load in the spreadsheet definition + if (exists($env{'form.workcopy'}) && + $self->{'type'} eq $env{'form.workcopy'}) { + $self->load_tmp(); + } else { + $self->load(); + } + } return %{$self->{'formulas'}}; } } @@ -1661,7 +1607,6 @@ sub load { my $stype = $self->{'type'}; my $cnum = $self->{'cnum'}; my $cdom = $self->{'cdom'}; - my $chome = $self->{'chome'}; # my $filename = $self->filename(); my $cachekey = join('_',($cnum,$cdom,$stype,$filename)); @@ -1670,7 +1615,11 @@ sub load { my ($formulas); if (exists($spreadsheets{$cachekey})) { $formulas = $spreadsheets{$cachekey}->{'formulas'}; - } else { + $self->formulas($formulas); + $self->{'row_source'}=$spreadsheets{$cachekey}->{'row_source'}; + $self->{'row_numbers'}=$spreadsheets{$cachekey}->{'row_numbers'}; + $self->{'maxrow'}=$spreadsheets{$cachekey}->{'maxrow'}; + } else { # Not cached, need to read if (! defined($filename)) { $formulas = $self->load_system_default_sheet(); @@ -1706,10 +1655,13 @@ sub load { $filename=$self->filename(); # filename may have changed $cachekey = join('_',($cnum,$cdom,$stype,$filename)); %{$spreadsheets{$cachekey}->{'formulas'}} = %{$formulas}; + $self->formulas($formulas); + $self->set_row_sources(); + $self->set_row_numbers(); + %{$spreadsheets{$cachekey}->{'row_source'}} = %{$self->{'row_source'}}; + %{$spreadsheets{$cachekey}->{'row_numbers'}} = %{$self->{'row_numbers'}}; + $spreadsheets{$cachekey}->{'maxrow'} = $self->{'maxrow'}; } - $self->formulas($formulas); - $self->set_row_sources(); - $self->set_row_numbers(); } sub set_row_sources { @@ -1758,7 +1710,6 @@ sub save { my $stype = $self->{'type'}; my $cnum = $self->{'cnum'}; my $cdom = $self->{'cdom'}; - my $chome = $self->{'chome'}; my $filename = $self->{'filename'}; my $cachekey = join('_',($cnum,$cdom,$stype,$filename)); # Cache new sheet