--- loncom/interface/statistics/lonstudentassessment.pm 2003/02/28 21:19:00 1.30
+++ loncom/interface/statistics/lonstudentassessment.pm 2003/03/03 19:28:29 1.31
@@ -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.31 2003/03/03 19:28:29 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' or 'totals' determines how much data is output
-=item $cacheDB The name of the cache file used to store student data
+=cut
-=item $students Array ref containing the name(s) of the students
-selected for display
+#######################################################
+#######################################################
+my $show_links;
+my $output_mode;
+my $show;
-=item $courseID The ID of the course
+#######################################################
+#######################################################
+# End of package variable declarations
-=item $formName The name of the html form - 'Statistics'
+=pod
-=item $headings Array ref of headings to show
+=back
-=item $spacing A string of spaces
+=cut
-=item $studentInformation Array ref of possible headings for student info
-('fullname','section',...)
+#######################################################
+#######################################################
+
+=pod
+
+=item &BuildStudentAssessmentPage()
+
+Inputs:
+
+=over 4
=item $r Apache Request
@@ -98,36 +127,88 @@ 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(<
+
+Please make your selections in the boxes above and hit
+the button marked "Update Display".
+
+
+ENDMSG
+ return;
+ }
+ #
+ #
+ my $initialize = \&html_initialize;
+ my $output_student = \&html_outputstudent;
+ my $finish = \&html_finish;
+ #
+ if ($output_mode eq 'excel') {
+ $initialize = \&excel_initialize;
+ $output_student = \&excel_outputstudent;
+ $finish = \&excel_finish;
+ } elsif ($output_mode eq 'csv') {
+ $initialize = \&csv_initialize;
+ $output_student = \&csv_outputstudent;
+ $finish = \&csv_finish;
+ }
#
- $r->print(&CreateTableHeadings());
if($c->aborted()) { return ; }
-
- my $Count = 0;
- $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 +233,7 @@ sub CreateInterface {
$Str .= 'Sections ';
$Str .= 'Student Data ';
$Str .= 'Sequences and Folders ';
+ $Str .= 'Output Format ';
$Str .= ''."\n";
#
$Str .= ''."\n";
@@ -170,78 +252,108 @@ sub CreateInterface {
$Str .= ' '."\n";
$Str .= &Apache::lonstatistics::MapSelect('Maps','multiple,all',5,
$only_seq_with_assessments);
+ $Str .= ' '."\n";
+ $Str .= &CreateAndParseOutputSelector();
$Str .= ' '."\n";
$Str .= ''."\n";
return $Str;
}
-
#######################################################
#######################################################
=pod
-=item Table Output Routines
-
-=over 4
+=item &CreateAndParseOutputSelector()
=cut
#######################################################
#######################################################
-{
- my $padding;
+sub CreateAndParseOutputSelector {
+ my $Str = '';
+ my $elementname = 'outputmode';
+ #
+ # Format for output options is 'mode, restrictions';
+ my @Options = ('html, with links','html, without links',
+ 'html, totals only','excel, totals only',
+ 'csv, totals only','csv, everything');
+ my $selected = 'html, with links';
+ if (exists($ENV{'form.'.$elementname})) {
+ if (ref($ENV{'form.'.$elementname} eq 'ARRAY')) {
+ $selected = $ENV{'form.'.$elementname}->[0];
+ } else {
+ $selected = $ENV{'form.'.$elementname};
+ }
+ }
+ #
+ # Set package variables describing output mode
+ $show_links = 'no';
+ $output_mode = 'html';
+ $show = 'all';
+ my ($mode,$restriction) = split(',',$selected);
+ $restriction =~ s/^\s*//;
+ if ($mode =~ /^(html|excel|csv)$/) {
+ $output_mode = $mode;
+ } else {
+ $output_mode = 'html';
+ }
+ if ($restriction eq 'with links') {
+ $show_links = 'yes';
+ } else {
+ $show_links = 'no';
+ }
+ if ($restriction eq 'totals only') {
+ $show = 'totals';
+ } else {
+ $show = 'everything';
+ }
+ #
+ # Build the form element
+ $Str = qq//;
+ foreach my $option (@Options) {
+ $Str .= qq/\n ';
+ my $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 +361,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)\n";
+ $Str .= "";
+ $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 +395,34 @@ 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));
+ } 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 +433,119 @@ 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;
+
+sub excel_initialize {
+ my ($r) = @_;
+ #
+ $r->print("Not implemented yet ");
+ return;
+ my $filename = '/prtspool/'.
+ $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'.
+ time.'_'.rand(1000000000).'.xls';
+ $excel_sheet = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
+ if (! defined($excel_sheet)) {
+ $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 0;
+ }
+ #
+ # 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_sheet->set_tempdir($Apache::lonnet::tmpdir);
+ #
+ # Determine the name to give the worksheet
+# $excel_sheet->addworksheet();
+
+ return;
+}
+
+sub excel_outputstudent {
+ my ($r,$student) = @_;
+}
+
+sub excel_finish {
+ my ($r) = @_;
+}
+
+}
+#######################################################
+#######################################################
+
+=pod
+
+=head2 CSV output routines
+
+=item &csv_initialize($r)
+
+=item &csv_outputstudent($r,$student)
+
+=item &csv_finish($r)
=cut
#######################################################
#######################################################
+{
+
+sub csv_initialize{
+ my ($r) = @_;
+ $r->print("Not implemented yet ");
+ return;
+}
+
+sub csv_outputstudent {
+ my ($r,$student) = @_;
+}
+
+sub csv_finish {
+ my ($r) = @_;
+}
}
@@ -377,9 +575,10 @@ Inputs:
#######################################################
#######################################################
sub StudentPerformanceOnSequence {
- my ($student,$studentdata,$seq,$links) = @_;
+ my ($student,$studentdata,$seq,$links,$totalonly) = @_;
+ $totalonly = 0 if (! defined($totalonly));
+ $links = 'no' if (! defined($links));
my $Str = '';
- my $output_width = 0;
my ($sum,$max) = (0,0);
foreach my $resource (@{$seq->{'contents'}}) {
next if ($resource->{'type'} ne 'assessment');
@@ -426,8 +625,7 @@ sub StudentPerformanceOnSequence {
}
}
#
- $output_width++;
- if (defined($links) && $symbol ne ' ') {
+ if ($links eq 'yes' && $symbol ne ' ') {
$symbol = '