--- loncom/interface/Attic/lonspreadsheet.pm 2002/11/07 15:37:02 1.135 +++ loncom/interface/Attic/lonspreadsheet.pm 2002/11/08 15:28:03 1.136 @@ -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::lonhtmlcommon; use Apache::loncoursedata; use Apache::File(); use Spreadsheet::WriteExcel; + # # Caches for coursewide information # @@ -93,7 +94,7 @@ my %spreadsheets; 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 @@ sub exportdata { 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 @@ END 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 @@ sub outsheet_csv { 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_csv { ############################################ 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 @@ sub outsheet_recursive_excel { # Figure out who the students are my %f=&getformulas($sheet); my $count = 0; - $r->print("
\n"); + $r->print(< +Compiling Excel Workbook with a worksheet for each student. +

+This operation may take longer than a complete recalculation of the +spreadsheet. +

+To abort this operation, hit the stop button on your browser. +

+A link to the spreadsheet will be available at the end of this process. +

+

+END $r->rflush(); + my $starttime = time; foreach (keys(%f)) { next if ($_!~/^A(\d+)/ || $1 == 0 || ($f{$_}=~/^[!~-]/)); $count++; @@ -1424,32 +1457,37 @@ sub outsheet_recursive_excel { # 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
'); + $r->print($count.' students completed.'. + ' Time remaining: '.$timeleft.' sec. '. + ' Estimated total time: '.$totaltime." sec
\n"); $r->rflush(); } + if(defined($c) && ($c->aborted())) { + last; + } } # - $r->print('All students spreadsheets completed
'); - $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('

'. - 'Your Excel spreadsheet.'."\n"); + if(! $c->aborted() ) { + $r->print('All students spreadsheets completed!
'); + $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('

'. + 'Your Excel spreadsheet.'."\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 @@ sub export_sheet_as_excel { 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 @@ sub updateclasssheet { my $chome =$sheet->{'chome'}; # %Section = (); - # # Read class list and row labels my $classlist = &Apache::loncoursedata::get_classlist(); @@ -2034,29 +2072,80 @@ sub updateclasssheet { } # ----------------------------------- 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; + 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.'_'.$uname.'_'.$udom; - if ($updatedata{$identifier}) { - %{$sheet->{'rowlabel'}}=split(/___;___/,$updatedata{$identifier}); + 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
timestamp', @@ -2066,29 +2155,16 @@ sub updatestudentassesssheet { 'parameter:Number of Tutor Responses
tutornumber', 'totalpoints' => 'parameter:Total Points Granted
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 @@ sub updatestudentassesssheet { $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 @@ sub updatestudentassesssheet { $sheet->{'f'} = \%f; &setformulas($sheet); } - # - undef %existing; } # ------------------------------------------------ Load data for one assessment @@ -2176,18 +2247,18 @@ sub loadstudent{ 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 selectbox { 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 @@ sub exportsheet { 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 @@ sub exportsheet { 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 @@ sub exportsheet { # # 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 @@ sub cachedssheets { 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 @@ sub handler { 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 @@ sub handler { $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 @@ sub handler { $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 @@ sub handler { $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('LON-CAPA Spreadsheet'); if ($ENV{'request.role'} !~ /^st\./) { $r->print(<rflush(); # # Full recalc? + # if ($ENV{'form.forcerecalc'}) { $r->print('

Completely Recalculating Sheet ...

'); 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 @@ ENDSCRIPT &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();