[LON-CAPA-cvs] cvs: loncom /interface lonspreadsheet.pm
matthew
lon-capa-cvs@mail.lon-capa.org
Fri, 08 Nov 2002 15:28:03 -0000
This is a MIME encoded message
--matthew1036769283
Content-Type: text/plain
matthew Fri Nov 8 10:28:03 2002 EDT
Modified files:
/loncom/interface lonspreadsheet.pm
Log:
Many minor changes. This is a check-in before I start reworking the internal
cache mechanisms.
&updatestudentassesssheet in to three parts -
&get_student_rowlabels, &get_assess_rowlabels, and what remains of
&updatestudentassesssheet. May undo this change in the future....
Changed a few foreach (keys %hash) to while (.. each (%hash)).
Some extra logging is currently done, to be removed.
Comments added to &handler.
Changed (probably broke) the caching....
--matthew1036769283
Content-Type: text/plain
Content-Disposition: attachment; filename="matthew-20021108102803.txt"
Index: loncom/interface/lonspreadsheet.pm
diff -u loncom/interface/lonspreadsheet.pm:1.135 loncom/interface/lonspreadsheet.pm:1.136
--- loncom/interface/lonspreadsheet.pm:1.135 Thu Nov 7 10:37:02 2002
+++ loncom/interface/lonspreadsheet.pm Fri Nov 8 10:28:03 2002
@@ -1,5 +1,5 @@
#
-# $Id: lonspreadsheet.pm,v 1.135 2002/11/07 15:37:02 matthew Exp $
+# $Id: lonspreadsheet.pm,v 1.136 2002/11/08 15:28:03 matthew Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -64,6 +64,7 @@
use Apache::loncoursedata;
use Apache::File();
use Spreadsheet::WriteExcel;
+
#
# Caches for coursewide information
#
@@ -93,7 +94,7 @@
my %courserdatas;
my %userrdatas;
my %defaultsheets;
-my %updatedata;
+my %rowlabel_cache;
#
# These global hashes are dependent on user, course and resource,
@@ -1107,6 +1108,22 @@
return @exportarray;
}
+
+
+sub update_student_sheet{
+ my $sheet = shift;
+ # Load in the studentcalc sheet
+ &readsheet($sheet,'default_studentcalc');
+ # Determine the structure (contained assessments, etc) of the sheet
+ &updatesheet($sheet);
+ # Load in the cached sheets for this student
+ &cachedssheets($sheet);
+ # Load in the (possibly cached) data from the assessment sheets
+ &loadstudent($sheet);
+ # Compute the sheet
+ &calcsheet($sheet);
+}
+
# ========================================================== End of Spreadsheet
# =============================================================================
#
@@ -1275,6 +1292,7 @@
my $rows_output=0;
foreach my $rownum (@Rows) {
my ($rowlabel,@rowdata) = &get_row($sheet,$rownum);
+ next if ($rowlabel =~ /^\s*$/);
#
my $defaultbg='#E0FF';
#
@@ -1369,6 +1387,7 @@
my $rows_output=0;
foreach my $rownum (@Rows) {
my ($rowlabel,@rowdata) = &get_row($sheet,$rownum);
+ next if ($rowlabel =~ /^\s*$/);
push (@Values,&format_csv_rowlabel($rowlabel));
foreach my $cell (@rowdata) {
push (@Values,'"'.$cell->{'value'}.'"');
@@ -1404,6 +1423,7 @@
############################################
sub outsheet_recursive_excel {
my ($sheet,$r) = @_;
+ my $c = $r->connection;
return undef if ($sheet->{'sheettype'} ne 'classcalc');
my ($workbook,$filename) = &create_excel_spreadsheet($sheet,$r);
return undef if (! defined($workbook));
@@ -1414,8 +1434,21 @@
# Figure out who the students are
my %f=&getformulas($sheet);
my $count = 0;
- $r->print("<br />\n");
+ $r->print(<<END);
+<p>
+Compiling Excel Workbook with a worksheet for each student.
+</p><p>
+This operation may take longer than a complete recalculation of the
+spreadsheet.
+</p><p>
+To abort this operation, hit the stop button on your browser.
+</p><p>
+A link to the spreadsheet will be available at the end of this process.
+</p>
+<p>
+END
$r->rflush();
+ my $starttime = time;
foreach (keys(%f)) {
next if ($_!~/^A(\d+)/ || $1 == 0 || ($f{$_}=~/^[!~-]/));
$count++;
@@ -1424,32 +1457,37 @@
# Create a new spreadsheet
my $studentsheet = &makenewsheet($sname,$sdom,'studentcalc',undef);
# Read in the spreadsheet definition
- &readsheet($studentsheet,'default_studentcalc');
- # Determine the structure (contained assessments, etc) of the sheet
- &updatesheet($studentsheet);
- # Load in the (possibly cached) data from the assessment sheets
- &loadrows($studentsheet);
- # Compute the sheet
- &calcsheet($studentsheet);
- &Apache::lonnet::logthis("Sheet value for A0 = ".$sheet->{'values'}->{'A0'});
+ &update_student_sheet($studentsheet);
# Stuff the sheet into excel
&export_sheet_as_excel($studentsheet,$student_excel_worksheet);
+ my $totaltime = int((time - $starttime) / $count * $sheet->{'maxrow'});
+ my $timeleft = int((time - $starttime) / $count * ($sheet->{'maxrow'} - $count));
if ($count % 5 == 0) {
- $r->print($count.' students completed<br />');
+ $r->print($count.' students completed.'.
+ ' Time remaining: '.$timeleft.' sec. '.
+ ' Estimated total time: '.$totaltime." sec <br />\n");
$r->rflush();
}
+ if(defined($c) && ($c->aborted())) {
+ last;
+ }
}
#
- $r->print('All students spreadsheets completed<br />');
- $r->rflush();
- #
- # &export_sheet_as_excel fills $worksheet with the data from $sheet
- &export_sheet_as_excel($sheet,$main_worksheet);
- #
- $workbook->close();
- # Okay, the spreadsheet is taken care of, so give the user a link.
- $r->print('<br /><br />'.
- '<a href="'.$filename.'">Your Excel spreadsheet.</a>'."\n");
+ if(! $c->aborted() ) {
+ $r->print('All students spreadsheets completed!<br />');
+ $r->rflush();
+ #
+ # &export_sheet_as_excel fills $worksheet with the data from $sheet
+ &export_sheet_as_excel($sheet,$main_worksheet);
+ #
+ $workbook->close();
+ # Okay, the spreadsheet is taken care of, so give the user a link.
+ $r->print('<br /><br />'.
+ '<a href="'.$filename.'">Your Excel spreadsheet.</a>'."\n");
+ } else {
+ $workbook->close(); # Not sure how necessary this is.
+ #unlink('/home/httpd'.$filename); # No need to keep this around?
+ }
return 1;
}
@@ -1514,6 +1552,7 @@
my $rows_output=0;
foreach my $rownum (@Rows) {
my ($rowlabel,@rowdata) = &get_row($sheet,$rownum);
+ next if ($rowlabel =~ /^\s*$/);
my $cols_output = 0;
my $label = &format_excel_rowlabel($rowlabel);
$worksheet->write($rows_output,$cols_output++,$label);
@@ -1975,7 +2014,6 @@
my $chome =$sheet->{'chome'};
#
%Section = ();
-
#
# Read class list and row labels
my $classlist = &Apache::loncoursedata::get_classlist();
@@ -2034,29 +2072,80 @@
}
# ----------------------------------- Update rows for student and assess sheets
-sub updatestudentassesssheet {
+sub get_student_rowlabels {
my ($sheet) = @_;
#
- my %bighash;
+ my %course_db;
#
my $stype = $sheet->{'sheettype'};
my $uname = $sheet->{'uname'};
my $udom = $sheet->{'udom'};
+ #
$sheet->{'rowlabel'} = {};
- my $identifier =$sheet->{'coursefilename'}.'_'.$stype.'_'.$uname.'_'.$udom;
- if ($updatedata{$identifier}) {
- %{$sheet->{'rowlabel'}}=split(/___;___/,$updatedata{$identifier});
+ #
+ my $identifier =$sheet->{'coursefilename'}.'_'.$stype;
+ if ($rowlabel_cache{$identifier}) {
+ %{$sheet->{'rowlabel'}}=split(/___;___/,$rowlabel_cache{$identifier});
+ } else {
+ # Get the data and store it in the cache
+ # Tie hash
+ tie(%course_db,'GDBM_File',$sheet->{'coursefilename'}.'.db',
+ &GDBM_READER(),0640);
+ if (! tied(%course_db)) {
+ return 'Could not access course data';
+ }
+ #
+ my %assesslist;
+ foreach ('Feedback','Evaluation','Tutoring','Discussion') {
+ my $symb = '_'.lc($_);
+ $assesslist{$symb} = join(':',('symb',$symb,$uname,$udom,$_));
+ }
+ #
+ while (my ($key,$srcf) = each(%course_db)) {
+ next if ($key !~ /^src_(\d+)\.(\d+)$/);
+ my $mapid = $1;
+ my $resid = $2;
+ my $id = $mapid.'.'.$resid;
+ if ($srcf=~/\.(problem|exam|quiz|assess|survey|form)$/) {
+ my $symb=
+ &Apache::lonnet::declutter($course_db{'map_id_'.$mapid}).
+ '___'.$resid.'___'.&Apache::lonnet::declutter($srcf);
+ $assesslist{$symb}='symb:'.&Apache::lonnet::escape($symb).':'
+ .$uname.':'.$udom.':'.$course_db{'title_'.$id};
+ }
+ }
+ untie(%course_db);
+ # Store away the data
+ $sheet->{'rowlabel'} = \%assesslist;
+ $rowlabel_cache{$identifier}=join('___;___',%{$sheet->{'rowlabel'}});
+ }
+
+}
+
+sub get_assess_rowlabels {
+ my ($sheet) = @_;
+ #
+ my %course_db;
+ #
+ my $stype = $sheet->{'sheettype'};
+ my $uname = $sheet->{'uname'};
+ my $udom = $sheet->{'udom'};
+ my $usymb = $sheet->{'usymb'};
+ #
+ $sheet->{'rowlabel'} = {};
+ my $identifier =$sheet->{'coursefilename'}.'_'.$stype.'_'.$usymb;
+ #
+ if ($rowlabel_cache{$identifier}) {
+ %{$sheet->{'rowlabel'}}=split(/___;___/,$rowlabel_cache{$identifier});
} else {
+ # Get the data and store it in the cache
# Tie hash
- tie(%bighash,'GDBM_File',$sheet->{'coursefilename'}.'.db',
+ tie(%course_db,'GDBM_File',$sheet->{'coursefilename'}.'.db',
&GDBM_READER(),0640);
- if (! tied(%bighash)) {
+ if (! tied(%course_db)) {
return 'Could not access course data';
}
- # Get all assessments
#
- # parameter_labels is used in the assessment sheets to provide labels
- # for the parameters.
my %parameter_labels=
('timestamp' =>
'parameter:Timestamp of Last Transaction<br>timestamp',
@@ -2066,29 +2155,16 @@
'parameter:Number of Tutor Responses<br>tutornumber',
'totalpoints' =>
'parameter:Total Points Granted<br>totalpoints');
- #
- # assesslist holds the descriptions of all assessments
- my %assesslist;
- foreach ('Feedback','Evaluation','Tutoring','Discussion') {
- my $symb = '_'.lc($_);
- $assesslist{$symb} = join(':',('symb',$symb,$uname,$udom,$_));
- }
- while (($_,undef) = each(%bighash)) {
- next if ($_!~/^src\_(\d+)\.(\d+)$/);
- my $mapid=$1;
- my $resid=$2;
- my $id=$mapid.'.'.$resid;
- my $srcf=$bighash{$_};
+ while (my ($key,$srcf) = each(%course_db)) {
+ next if ($key !~ /^src_(\d+)\.(\d+)$/);
+ my $mapid = $1;
+ my $resid = $2;
+ my $id = $mapid.'.'.$resid;
if ($srcf=~/\.(problem|exam|quiz|assess|survey|form)$/) {
- my $symb=
- &Apache::lonnet::declutter($bighash{'map_id_'.$mapid}).
- '___'.$resid.'___'.&Apache::lonnet::declutter($srcf);
- $assesslist{$symb}='symb:'.&Apache::lonnet::escape($symb).':'
- .$uname.':'.$udom.':'.$bighash{'title_'.$id};
- next if ($stype ne 'assesscalc');
- foreach my $key (split(/\,/,
- &Apache::lonnet::metadata($srcf,'keys')
- )) {
+ # Loop through the metadata for this key
+ my @Metadata = split(/,/,
+ &Apache::lonnet::metadata($srcf,'keys'));
+ foreach my $key (@Metadata) {
next if ($key !~ /^(stores|parameter)_/);
my $display=
&Apache::lonnet::metadata($srcf,$key.'.display');
@@ -2100,46 +2176,43 @@
$parameter_labels{$key}='parameter:'.$display;
} # end of foreach
}
- } # end of foreach (keys(%bighash))
- untie(%bighash);
- #
- # %parameter_labels has a list of storage and parameter displays by
- # unikey
- # %assesslist has a list of all resource, by symb
- #
- if ($stype eq 'assesscalc') {
- $sheet->{'rowlabel'} = \%parameter_labels;
- } elsif ($stype eq 'studentcalc') {
- $sheet->{'rowlabel'} = \%assesslist;
- }
- $updatedata{$sheet->{'coursefilename'}.'_'.$stype.'_'
- .$uname.'_'.$udom}=
- join('___;___',%{$sheet->{'rowlabel'}});
- # Get current from cache
+ }
+ untie(%course_db);
+ # Store away the results
+ $sheet->{'rowlabel'} = \%parameter_labels;
+ $rowlabel_cache{$identifier}=join('___;___',%{$sheet->{'rowlabel'}});
}
- # Find discrepancies between the course row table and this
- #
+
+}
+
+sub updatestudentassesssheet {
+ my $sheet = shift;
+ if ($sheet->{'sheettype'} eq 'studentcalc') {
+ &get_student_rowlabels($sheet);
+ } else {
+ &get_assess_rowlabels($sheet);
+ }
+ # Determine if any of the information has changed
my %f=&getformulas($sheet);
my $changed=0;
$sheet->{'maxrow'} = 0;
my %existing=();
# Now obsolete rows
- foreach (keys(%f)) {
- next if ($_!~/^A(\d+)/);
- if ($1 > $sheet->{'maxrow'}) {
- $sheet->{'maxrow'} = $1;
- }
- my ($usy,$ufn)=split(/__&&&\__/,$f{$_});
+ while (my ($cell, $formula) = each (%f)) {
+ next if ($cell !~ /^A(\d+)/);
+ $sheet->{'maxrow'} = $1 if ($1 > $sheet->{'maxrow'});
+ my ($usy,$ufn)=split(/__&&&\__/,$formula);
$existing{$usy}=1;
unless ((exists($sheet->{'rowlabel'}->{$usy}) &&
(defined($sheet->{'rowlabel'}->{$usy})) || (!$1) ||
- ($f{$_}=~/^(~~~|---)/))){
+ ($formula =~ /^(~~~|---)/) )) {
$f{$_}='!!! Obsolete';
$changed=1;
} elsif ($ufn) {
+ # I do not think this works any more
$sheet->{'rowlabel'}->{$usy}
- =~s/assesscalc\?usymb\=/assesscalc\?ufn\=$ufn\&usymb\=/;
+ =~s/assesscalc\?usymb\=/assesscalc\?ufn\=$ufn&\usymb\=/;
}
}
# New and unknown keys
@@ -2154,8 +2227,6 @@
$sheet->{'f'} = \%f;
&setformulas($sheet);
}
- #
- undef %existing;
}
# ------------------------------------------------ Load data for one assessment
@@ -2176,18 +2247,18 @@
undef @tmp;
#
my @assessdata=();
- foreach (keys(%f)) {
- next if ($_!~/^A(\d+)/);
+ while (my ($cell,$value) = each (%f)) {
+ next if ($cell !~ /^A(\d+)/);
my $row=$1;
- next if (($f{$_}=~/^[\!\~\-]/) || ($row==0));
- my ($usy,$ufn)=split(/__&&&\__/,$f{$_});
+ next if (($value =~ /^[!~-]/) || ($row==0));
+ my ($usy,$ufn)=split(/__&&&\__/,$value);
@assessdata=&exportsheet($sheet,$sheet->{'uname'},
$sheet->{'udom'},
'assesscalc',$usy,$ufn);
my $index=0;
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]) {
+ if (defined($assessdata[$index])) {
my $col=$_;
if ($assessdata[$index]=~/\D/) {
$c{$col.$row}="'".$assessdata[$index]."'";
@@ -2426,8 +2497,7 @@
sub updatesheet {
my ($sheet)=@_;
- my $stype=$sheet->{'sheettype'};
- if ($stype eq 'classcalc') {
+ if ($sheet->{'sheettype'} eq 'classcalc') {
return &updateclasssheet($sheet);
} else {
return &updatestudentassesssheet($sheet);
@@ -2503,6 +2573,7 @@
my $key=$uname.':'.$udom.':'.$stype.':'.$usymb;
my $found='';
if ($oldsheets{$key}) {
+ &Apache::lonnet::logthis("got cached $stype for $uname");
foreach (split(/___&\___/,$oldsheets{$key})) {
my ($name,$value)=split(/___=___/,$_);
if ($name eq $fn) {
@@ -2513,6 +2584,7 @@
unless ($found) {
&cachedssheets($sheet,$uname,$udom);
if ($oldsheets{$key}) {
+ &Apache::lonnet::logthis("got cached $stype for $uname");
foreach (split(/___&\___/,$oldsheets{$key})) {
my ($name,$value)=split(/___=___/,$_);
if ($name eq $fn) {
@@ -2602,7 +2674,7 @@
#
# Load previously cached student spreadsheets for this course
#
-sub expirationdates {
+sub load_spreadsheet_expirationdates {
undef %expiredates;
my $cid=$ENV{'request.course.id'};
my @tmp = &Apache::lonnet::dump('nohist_expirationdates',
@@ -2640,18 +2712,22 @@
my ($sheet,$uname,$udom) = @_;
$uname = $uname || $sheet->{'uname'};
$udom = $udom || $sheet->{'udom'};
- if (! $loadedcaches{$sheet->{'uname'}.'_'.$sheet->{'udom'}}) {
+ if (! $loadedcaches{$uname.'_'.$udom}) {
my @tmp = &Apache::lonnet::dump('nohist_calculatedsheets',
$sheet->{'udom'},
$sheet->{'uname'});
if ($tmp[0] !~ /^error/) {
- my %StupidTempHash = @tmp;
- while (my ($key,$value) = each %StupidTempHash) {
+ my %TempHash = @tmp;
+ my $count = 0;
+ while (my ($key,$value) = each %TempHash) {
$oldsheets{$key} = $value;
+ $count++;
}
+ &Apache::lonnet::logthis('saved '.$count.' cached sheets for '.$uname);
$loadedcaches{$sheet->{'uname'}.'_'.$sheet->{'udom'}}=1;
}
}
+
}
# ===================================================== Calculated sheets cache
@@ -2677,6 +2753,9 @@
lc($ENV{'form.output'}) eq 'recursive excel')) {
$ENV{'form.output'} = 'HTML';
}
+ #
+ # Overload checking
+ #
# Check this server
my $loaderror=&Apache::lonnet::overloaderror($r);
if ($loaderror) { return $loaderror; }
@@ -2684,15 +2763,22 @@
$loaderror= &Apache::lonnet::overloaderror($r,
$ENV{'course.'.$ENV{'request.course.id'}.'.home'});
if ($loaderror) { return $loaderror; }
-
+ #
+ # HTML Header
+ #
if ($r->header_only) {
$r->content_type('text/html');
$r->send_http_header;
return OK;
}
+ #
# Global directory configs
+ #
$includedir = $r->dir_config('lonIncludes');
$tmpdir = $r->dir_config('lonDaemons').'/tmp/';
+ #
+ # Roles Checking
+ #
# Needs to be in a course
if (! $ENV{'request.course.fn'}) {
# Not in a course, or not allowed to modify parms
@@ -2700,17 +2786,27 @@
$r->uri.":opa:0:0:Cannot modify spreadsheet";
return HTTP_NOT_ACCEPTABLE;
}
+ #
# Get query string for limited number of parameters
+ #
&Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
['uname','udom','usymb','ufn']);
+ #
+ # Deal with restricted student permissions
+ #
if ($ENV{'request.role'} =~ /^st\./) {
delete $ENV{'form.unewfield'} if (exists($ENV{'form.unewfield'}));
delete $ENV{'form.unewformula'} if (exists($ENV{'form.unewformula'}));
}
+ #
+ # Clean up symb and spreadsheet filename
+ #
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'};
}
@@ -2726,12 +2822,15 @@
$adom=$ENV{'form.udom'};
}
#
- # Open page
+ # Open page, try to prevent browser cache.
+ #
$r->content_type('text/html');
$r->header_out('Cache-control','no-cache');
$r->header_out('Pragma','no-cache');
$r->send_http_header;
- # Screen output
+ #
+ # Header....
+ #
$r->print('<html><head><title>LON-CAPA Spreadsheet</title>');
if ($ENV{'request.role'} !~ /^st\./) {
$r->print(<<ENDSCRIPT);
@@ -2771,13 +2870,14 @@
$r->rflush();
#
# Full recalc?
+ #
if ($ENV{'form.forcerecalc'}) {
$r->print('<h4>Completely Recalculating Sheet ...</h4>');
undef %spreadsheets;
undef %courserdatas;
undef %userrdatas;
undef %defaultsheets;
- undef %updatedata;
+ undef %rowlabel_cache;
}
# Read new sheet or modified worksheet
my ($sheet)=&makenewsheet($aname,$adom,$sheettype,$ENV{'form.usymb'});
@@ -2861,10 +2961,18 @@
&othersheets($sheet,'assesscalc'));
}
}
- # Cached sheets
- &expirationdates();
- undef %oldsheets;
- undef %loadedcaches;
+ #
+ # Set up caching mechanisms
+ #
+ &load_spreadsheet_expirationdates();
+ # Clear out old caches if we have not seen this class before.
+ if (exists($oldsheets{'course'}) &&
+ $oldsheets{'course'} ne $sheet->{'cid'}) {
+ undef %oldsheets;
+ undef %loadedcaches;
+ }
+ $oldsheets{'course'} = $sheet->{'cid'};
+ #
if ($sheet->{'sheettype'} eq 'classcalc') {
$r->print("Loading previously calculated student sheets ...\n");
$r->rflush();
--matthew1036769283--