--- loncom/interface/statistics/lonstudentassessment.pm 2003/02/28 21:19:00 1.30
+++ loncom/interface/statistics/lonstudentassessment.pm 2003/03/04 14:21:36 1.33
@@ -1,6 +1,6 @@
# The LearningOnline Network with CAPA
#
-# $Id: lonstudentassessment.pm,v 1.30 2003/02/28 21:19:00 matthew Exp $
+# $Id: lonstudentassessment.pm,v 1.33 2003/03/04 14:21:36 matthew Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -54,7 +54,23 @@ use Apache::lonstatistics;
use Apache::lonhtmlcommon;
use Apache::loncoursedata;
use Apache::lonnet; # for logging porpoises
-use GDBM_File;
+use Spreadsheet::WriteExcel;
+
+#######################################################
+#######################################################
+=pod
+
+=item Package Variables
+
+=over 4
+
+=item $Statistics Hash ref to store student data. Indexed by symb,
+ contains hashes with keys 'score' and 'max'.
+
+=cut
+
+#######################################################
+#######################################################
my $Statistics;
@@ -63,27 +79,40 @@ my $Statistics;
=pod
-=item &BuildStudentAssessmentPage()
+=item $show_links 'yes' or 'no' for linking to student performance data
-Inputs:
+=item $output_mode 'html', 'excel', or 'csv' for output mode
-=over 4
+=item $show 'all', 'totals', or 'scores' determines how much data is output
+
+=cut
+
+#######################################################
+#######################################################
+my $show_links;
+my $output_mode;
+my $show;
+
+#######################################################
+#######################################################
+# End of package variable declarations
-=item $cacheDB The name of the cache file used to store student data
+=pod
-=item $students Array ref containing the name(s) of the students
-selected for display
+=back
-=item $courseID The ID of the course
+=cut
+
+#######################################################
+#######################################################
-=item $formName The name of the html form - 'Statistics'
+=pod
-=item $headings Array ref of headings to show
+=item &BuildStudentAssessmentPage()
-=item $spacing A string of spaces
+Inputs:
-=item $studentInformation Array ref of possible headings for student info
-('fullname','section',...)
+=over 4
=item $r Apache Request
@@ -98,36 +127,89 @@ selected for display
sub BuildStudentAssessmentPage {
my ($r,$c)=@_;
undef($Statistics);
-
#
+ # Print out the HTML headers for the interface
+ # This also parses the output mode selector
+ # This step must always be done.
$r->print(&CreateInterface());
+ $r->print('');
$r->rflush();
+ if (! exists($ENV{'form.notfirstrun'})) {
+ $r->print(<
'."\n"); + # + # Call the initialize routine selected above + $initialize->($r); foreach my $student (@Apache::lonstatistics::Students) { - if($c->aborted()) { return ; } - $r->print(&ChartOutputStudent($student)); - # output it - - $Count++; - if($Count % 5 == 0) { - $r->print("\n
"); + if($c->aborted()) { + $finish->($r); + return ; } - - $r->rflush(); + # Call the output_student routine selected above + $output_student->($r,$student); } - $r->print(''."\n"); - my $Str; + # Call the "finish" routine selected above + $finish->($r); + # return; } ####################################################### ####################################################### +sub get_student_fields_to_show { + my @to_show = @Apache::lonstatistics::SelectedStudentData; + foreach (@to_show) { + if ($_ eq 'all') { + @to_show = @Apache::lonstatistics::StudentDataOrder; + last; + } + } + return @to_show; +} + +sub get_sequences_to_show { + my @Sequences; + foreach my $map_symb (@Apache::lonstatistics::SelectedMaps) { + foreach my $sequence (@Apache::lonstatistics::Sequences) { + next if ($sequence->{'symb'} ne $map_symb && $map_symb ne 'all'); + next if ($sequence->{'num_assess'} < 1); + push (@Sequences,$sequence); + } + } + return @Sequences; +} + + +####################################################### +####################################################### + =pod =item &CreateInterface() @@ -152,6 +234,7 @@ sub CreateInterface { $Str .= '
'; + my $Str = "\n"; + $Str .= "\n"; # First, the @StudentData fields need to be listed - my @to_show = @Apache::lonstatistics::SelectedStudentData; - foreach (@to_show) { - if ($_ eq 'all') { - @to_show = @Apache::lonstatistics::StudentDataOrder; - last; - } - } + my @to_show = &get_student_fields_to_show(); foreach my $field (@to_show) { my $title=$Apache::lonstatistics::StudentData{$field}->{'title'}; my $base =$Apache::lonstatistics::StudentData{$field}->{'base_width'}; @@ -249,48 +420,27 @@ sub CreateTableHeadings { $Str .= $title.' 'x($width-$base).$padding; } # Now the selected sequences need to be listed - foreach my $map_symb (@Apache::lonstatistics::SelectedMaps) { - foreach my $sequence (@Apache::lonstatistics::Sequences) { - next if ($sequence->{'symb'} ne $map_symb && $map_symb ne 'all'); - next if ($sequence->{'num_assess'} < 1); - my $title = $sequence->{'title'}; - my $base = $sequence->{'base_width'}; - my $width = $sequence->{'width'}; - $Str .= $title.' 'x($width-$base).$padding; - } + foreach my $sequence (&get_sequences_to_show) { + my $title = $sequence->{'title'}; + my $base = $sequence->{'base_width'}; + my $width = $sequence->{'width'}; + $Str .= $title.' 'x($width-$base).$padding; } - $Str .= 'total'; - $Str .= "\n"; - return $Str; + $Str .= "total (of shown problems)
"; + $r->print($Str); + $r->rflush(); + return; } -####################################################### -####################################################### - -=pod - -=item &ChartOutputStudent($student) - -Return a line of the chart for a student. - -=cut - -####################################################### -####################################################### -sub ChartOutputStudent { - my $student = shift; +sub html_outputstudent { + my ($r,$student) = @_; my $Str = ''; # First, the @StudentData fields need to be listed - my @to_show = @Apache::lonstatistics::SelectedStudentData; - foreach (@to_show) { - if ($_ eq 'all') { - @to_show = @Apache::lonstatistics::StudentDataOrder; - last; - } - } + my @to_show = &get_student_fields_to_show(); foreach my $field (@to_show) { my $title=$student->{$field}; - my $base =scalar(my @Tmp = split(//,$title)); + my $base = length($title); my $width=$Apache::lonstatistics::StudentData{$field}->{'width'}; $Str .= $title.' 'x($width-$base).$padding; } @@ -304,22 +454,37 @@ sub ChartOutputStudent { } if (scalar(@tmp) < 1) { $Str .= 'No Course Data'."\n"; - return $Str; + $r->print($Str); + $r->rflush(); + return; } # # By sequence build up the data my $studentstats; - foreach my $map_symb (@Apache::lonstatistics::SelectedMaps) { - foreach my $seq (@Apache::lonstatistics::Sequences) { - next if ($map_symb ne $seq->{'symb'} && $map_symb ne 'all'); - next if ($seq->{'num_assess'} < 1); - my ($performance,$score,$seq_max) = - &StudentPerformanceOnSequence($student,\%StudentsData, - $seq,'linkify'); - $Str .= $performance.$padding; - $studentstats->{$seq->{'symb'}}->{'score'}= $score; - $studentstats->{$seq->{'symb'}}->{'max'} = $seq_max; + my $PerformanceStr = ''; + foreach my $seq (&get_sequences_to_show) { + my ($performance,$score,$seq_max) = + &StudentPerformanceOnSequence($student,\%StudentsData, + $seq,$show_links); + my $ratio = $score.'/'.$seq_max; + # + if ($show eq 'totals') { + $performance = ' 'x(length($seq_max)-length($score)).$ratio; + $performance .= ' 'x($seq->{'width'}-length($performance)); + } elsif ($show eq 'scores') { + $performance = $score; + $performance .= ' 'x($seq->{'width'}-length($performance)); + } else { + # Pad with extra spaces + $performance .= ' 'x($seq->{'width'}-$seq_max- + length($ratio) + ).$ratio; } + # + $Str .= $performance.$padding; + # + $studentstats->{$seq->{'symb'}}->{'score'}= $score; + $studentstats->{$seq->{'symb'}}->{'max'} = $seq_max; } # # Total it up and store the statistics info. @@ -330,24 +495,253 @@ sub ChartOutputStudent { $score += $seq_stats->{'score'}; $max += $seq_stats->{'max'}; } - my $scorelength = scalar(my @tmp1 = split(//,$score)); - my $maxlength = scalar(my @tmp2 = split(//,$max)); - $Str .= ' '.' 'x($maxlength-$scorelength).$score.'/'.$max; + $Str .= ' '.' 'x(length($max)-length($score)).$score.'/'.$max; $Str .= " \n"; - return $Str; + $r->print($Str); + # + $count++; + if($count % 5 == 0) { + $r->print("
"); + } + # + $r->rflush(); + return; } +sub html_finish { + my ($r) = @_; + $r->print("\n"); + $r->rflush(); + return; +} + +} + ####################################################### ####################################################### =pod -=back +=head2 EXCEL subroutines + +=item &excel_initialize($r) + +=item &excel_outputstudent($r,$student) + +=item &excel_finish($r) + +=cut + +####################################################### +####################################################### +{ + +my $excel_sheet; +my $excel_workbook; + +my $filename; +my $rows_output; +my $cols_output; + +my $num_students; +my $start_time; + +sub excel_initialize { + my ($r) = @_; + # + $filename = '/prtspool/'. + $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'. + time.'_'.rand(1000000000).'.xls'; + # + $excel_workbook = undef; + $excel_sheet = undef; + # + $rows_output = 0; + $cols_output = 0; + # + $num_students = 0; + $start_time = time; + # + # Create sheet + $excel_workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename); + # + # Check for errors + if (! defined($excel_workbook)) { + $r->log_error("Error creating excel spreadsheet $filename: $!"); + $r->print("Problems creating new Excel file. ". + "This error has been logged. ". + "Please alert your LON-CAPA administrator"); + return ; + } + # + # The excel spreadsheet stores temporary data in files, then put them + # together. If needed we should be able to disable this (memory only). + # The temporary directory must be specified before calling 'addworksheet'. + # File::Temp is used to determine the temporary directory. + $excel_workbook->set_tempdir($Apache::lonnet::tmpdir); + # + # Add a worksheet + my $sheetname = $ENV{'course.'.$ENV{'request.course.id'}.'.description'}; + if (length($sheetname) > 31) { + $sheetname = substr($sheetname,0,31); + } + $excel_sheet = $excel_workbook->addworksheet($sheetname); + # + # Add the student headers + foreach my $field (&get_student_fields_to_show()) { + $excel_sheet->write(1,$cols_output++,$field); + } + # + # Add the Sequence Headers + foreach my $seq (&get_sequences_to_show) { + $excel_sheet->write(0,$cols_output,$seq->{'title'}); + if ($show eq 'totals') { + $excel_sheet->write(1,$cols_output,'score'); + $excel_sheet->write(1,$cols_output+1,'maximum'); + $cols_output += 2; + } else { + $cols_output++; + } + } + # + # Bookkeeping + if ($show eq 'totals') { + $rows_output = 2; + } else { + $rows_output = 1; + } + # + # Let the user know what we are doing + my $studentcount = scalar(@Apache::lonstatistics::Students); + $r->print("