--- loncom/interface/Attic/lonspreadsheet.pm 2002/11/07 15:37:02 1.135 +++ loncom/interface/Attic/lonspreadsheet.pm 2002/11/21 19:26:34 1.147 @@ -1,5 +1,5 @@ # -# $Id: lonspreadsheet.pm,v 1.135 2002/11/07 15:37:02 matthew Exp $ +# $Id: lonspreadsheet.pm,v 1.147 2002/11/21 19:26:34 matthew Exp $ # # Copyright Michigan State University Board of Trustees # @@ -53,17 +53,18 @@ built-in functions. package Apache::lonspreadsheet; use strict; +use Apache::Constants qw(:common :http); +use Apache::lonnet; +use Apache::lonhtmlcommon; +use Apache::loncoursedata; +use Apache::File(); use Safe; use Safe::Hole; use Opcode; -use Apache::lonnet; -use Apache::Constants qw(:common :http); use GDBM_File; use HTML::TokeParser; -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, @@ -1085,6 +1086,44 @@ sub geterrorlog { return ${$sheet->{'safe'}->varglob('errorlog')}; } +sub gettitle { + my $sheet = shift; + if ($sheet->{'sheettype'} eq 'classcalc') { + return $sheet->{'coursedesc'}; + } elsif ($sheet->{'sheettype'} eq 'studentcalc') { + return 'Grades for '.$sheet->{'uname'}.'@'.$sheet->{'udom'}; + } elsif ($sheet->{'sheettype'} eq 'assesscalc') { + if (($sheet->{'usymb'} eq '_feedback') || + ($sheet->{'usymb'} eq '_evaluation') || + ($sheet->{'usymb'} eq '_discussion') || + ($sheet->{'usymb'} eq '_tutoring')) { + my $title = $sheet->{'usymb'}; + $title =~ s/^_//; + $title = ucfirst($title); + return $title; + } + return if (! defined($sheet->{'mapid'}) || + $sheet->{'mapid'} !~ /^\d+$/); + my $mapid = $sheet->{'mapid'}; + return if (! defined($sheet->{'resid'}) || + $sheet->{'resid'} !~ /^\d+$/); + my $resid = $sheet->{'resid'}; + my %course_db; + tie(%course_db,'GDBM_File',$sheet->{'coursefilename'}.'.db', + &GDBM_READER(),0640); + return if (! tied(%course_db)); + my $key = 'title_'.$mapid.'.'.$resid; + my $title = ''; + if (exists($course_db{$key})) { + $title = $course_db{$key}; + } else { + $title = $sheet->{'usymb'}; + } + untie (%course_db); + return $title; + } +} + # ----------------------------------------------------- Get value of $f{'A'.$n} sub getfa { my $sheet = shift; @@ -1107,6 +1146,22 @@ sub exportdata { return @exportarray; } + + +sub update_student_sheet{ + my ($sheet,$r,$c) = @_; + # 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,$r,$c); + # Compute the sheet + &calcsheet($sheet); +} + # ========================================================== End of Spreadsheet # ============================================================================= # @@ -1131,16 +1186,45 @@ sub get_row { ######################################################################## sub sort_indicies { my $sheet = shift; - # - # Sort the rows in some manner - # - my @sortby=(); my @sortidx=(); - for (my $row=1;$row<=$sheet->{'maxrow'};$row++) { - push (@sortby, $sheet->{'safe'}->reval('$f{"A'.$row.'"}')); - push (@sortidx, $row); + # + if ($sheet->{'sheettype'} eq 'classcalc') { + my @sortby=(undef); + # Skip row 0 + for (my $row=1;$row<=$sheet->{'maxrow'};$row++) { + my (undef,$sname,$sdom,$fullname,$section,$id) = + split(':',$sheet->{'rowlabel'}->{$sheet->{'f'}->{'A'.$row}}); + push (@sortby, lc($fullname)); + push (@sortidx, $row); + } + @sortidx = sort { $sortby[$a] cmp $sortby[$b]; } @sortidx; + } elsif ($sheet->{'sheettype'} eq 'studentcalc') { + my @sortby1=(undef); + my @sortby2=(undef); + # Skip row 0 + for (my $row=1;$row<=$sheet->{'maxrow'};$row++) { + my (undef,$symb,$uname,$udom,$mapid,$resid,$title) = + split(':',$sheet->{'rowlabel'}->{$sheet->{'f'}->{'A'.$row}}); + $symb = &Apache::lonnet::unescape($symb); + my ($sequence) = ($symb =~ /\/([^\/]*\.sequence)/); + if ($sequence eq '') { + $sequence = $symb; + } + push (@sortby1, $sequence); + push (@sortby2, $title); + push (@sortidx, $row); + } + @sortidx = sort { $sortby1[$a] cmp $sortby1[$b] || + $sortby2[$a] cmp $sortby2[$b] } @sortidx; + } else { + my @sortby=(undef); + # Skip row 0 + for (my $row=1;$row<=$sheet->{'maxrow'};$row++) { + push (@sortby, $sheet->{'safe'}->reval('$f{"A'.$row.'"}')); + push (@sortidx, $row); + } + @sortidx = sort { $sortby[$a] cmp $sortby[$b]; } @sortidx; } - @sortidx=sort { lc($sortby[$a]) cmp lc($sortby[$b]); } @sortidx; return @sortidx; } @@ -1173,6 +1257,7 @@ sub html_editable_cell { if ($value =~ /^\s*$/ ) { $value = '#'; } + $formula =~ s/\n/\\n/gs; $result .= ''.$value.''; return $result; @@ -1250,7 +1335,6 @@ END # Print out summary/export row #################################### my ($rowlabel,@rowdata) = &get_row($sheet,'0'); - my $rowcount = 0; $row_html = '
+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();
- foreach (keys(%f)) {
- next if ($_!~/^A(\d+)/ || $1 == 0 || ($f{$_}=~/^[!~-]/));
+ my $starttime = time;
+ foreach my $rownum (&sort_indicies($sheet)) {
$count++;
- my ($sname,$sdom) = split(':',$f{$_});
+ my ($sname,$sdom) = split(':',$f{'A'.$rownum});
my $student_excel_worksheet=$workbook->addworksheet($sname.'@'.$sdom);
# 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,$r,$c);
# 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;
}
@@ -1482,7 +1600,6 @@ sub create_excel_spreadsheet {
my $filename = '/prtspool/'.
$ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'.
time.'_'.rand(1000000000).'.xls';
- #&Apache::lonnet::logthis("spreadsheet:filename = ".$filename);
my $workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
if (! defined($workbook)) {
$r->log_error("Error creating excel spreadsheet $filename: $!");
@@ -1505,24 +1622,57 @@ sub create_excel_spreadsheet {
sub export_sheet_as_excel {
my $sheet = shift;
my $worksheet = shift;
+ #
+ my $rows_output = 0;
+ my $cols_output = 0;
+ ####################################
+ # Write an identifying row #
+ ####################################
+ my @Headerinfo = ($sheet->{'coursedesc'});
+ my $title = &gettitle($sheet);
+ $cols_output = 0;
+ if (defined($title)) {
+ $worksheet->write($rows_output++,$cols_output++,$title);
+ }
+ ####################################
+ # Write the summary/export row #
+ ####################################
+ my ($rowlabel,@rowdata) = &get_row($sheet,'0');
+ my $label = &format_excel_rowlabel($rowlabel);
+ $cols_output = 0;
+ $worksheet->write($rows_output,$cols_output++,$label);
+ foreach my $cell (@rowdata) {
+ $worksheet->write($rows_output,$cols_output++,$cell->{'value'});
+ }
+ $rows_output+= 2; # Skip a row, just for fun
####################################
# Prepare to output rows
####################################
my @Rows = &sort_indicies($sheet);
#
# Loop through the rows and output them one at a time
- my $rows_output=0;
foreach my $rownum (@Rows) {
my ($rowlabel,@rowdata) = &get_row($sheet,$rownum);
- my $cols_output = 0;
+ next if ($rowlabel =~ /^[\s]*$/);
+ $cols_output = 0;
my $label = &format_excel_rowlabel($rowlabel);
+ if ( ! $ENV{'form.showall'} &&
+ $sheet->{'sheettype'} =~ /^(studentcalc|classcalc)$/) {
+ my $row_is_empty = 1;
+ foreach my $cell (@rowdata) {
+ if ($cell->{'value'} !~ /^\s*$/) {
+ $row_is_empty = 0;
+ last;
+ }
+ }
+ next if ($row_is_empty);
+ }
$worksheet->write($rows_output,$cols_output++,$label);
if (ref($label)) {
$cols_output = (scalar(@$label));
}
foreach my $cell (@rowdata) {
- $worksheet->write($rows_output,$cols_output++,
- $cell->{'value'});
+ $worksheet->write($rows_output,$cols_output++,$cell->{'value'});
}
$rows_output++;
}
@@ -1543,7 +1693,7 @@ sub outsheet_xml {
## Outsheet - calls other outsheet_* functions
##
sub outsheet {
- my ($r,$sheet)=@_;
+ my ($sheet,$r)=@_;
if (! exists($ENV{'form.output'})) {
$ENV{'form.output'} = 'HTML';
}
@@ -1655,7 +1805,8 @@ sub readsheet {
if ($fh=Apache::File->new($includedir.'/'.$dfn)) {
$sheetxml=join('',<$fh>);
} else {
- $sheetxml='