--- loncom/interface/statistics/lonstudentassessment.pm 2003/03/03 22:54:05 1.32
+++ loncom/interface/statistics/lonstudentassessment.pm 2004/03/10 18:41:37 1.97
@@ -1,6 +1,6 @@
# The LearningOnline Network with CAPA
#
-# $Id: lonstudentassessment.pm,v 1.32 2003/03/03 22:54:05 matthew Exp $
+# $Id: lonstudentassessment.pm,v 1.97 2004/03/10 18:41:37 matthew Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -52,9 +52,12 @@ package Apache::lonstudentassessment;
use strict;
use Apache::lonstatistics;
use Apache::lonhtmlcommon;
+use Apache::loncommon();
use Apache::loncoursedata;
use Apache::lonnet; # for logging porpoises
+use Apache::lonlocal;
use Spreadsheet::WriteExcel;
+use Spreadsheet::WriteExcel::Utility();
#######################################################
#######################################################
@@ -85,13 +88,17 @@ my $Statistics;
=item $show 'all', 'totals', or 'scores' determines how much data is output
+=item $single_student_mode evaluates to true if we are showing only one
+student.
+
=cut
#######################################################
#######################################################
my $show_links;
my $output_mode;
-my $show;
+my $chosen_output;
+my $single_student_mode;
#######################################################
#######################################################
@@ -126,27 +133,44 @@ Inputs:
#######################################################
sub BuildStudentAssessmentPage {
my ($r,$c)=@_;
+ #
undef($Statistics);
+ undef($show_links);
+ undef($output_mode);
+ undef($chosen_output);
+ undef($single_student_mode);
+ #
+ my %Saveable_Parameters = ('Status' => 'scalar',
+ 'chartoutputmode' => 'scalar',
+ 'chartoutputdata' => 'scalar',
+ 'Section' => 'array',
+ 'StudentData' => 'array',
+ 'Maps' => 'array');
+ &Apache::loncommon::store_course_settings('chart',\%Saveable_Parameters);
+ &Apache::loncommon::restore_course_settings('chart',\%Saveable_Parameters);
+ #
+ &Apache::lonstatistics::PrepareClasslist();
+ #
+ $single_student_mode = 0;
+ $single_student_mode = 1 if ($ENV{'form.SelectedStudent'});
+ if ($ENV{'form.selectstudent'}) {
+ &Apache::lonstatistics::DisplayClasslist($r);
+ return;
+ }
#
# Print out the HTML headers for the interface
# This also parses the output mode selector
- # This step must always be done.
+ # This step must *always* be done.
$r->print(&CreateInterface());
$r->print('');
+ $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
+ #
+ if (! exists($ENV{'form.notfirstrun'}) && ! $single_student_mode) {
return;
}
#
- #
my $initialize = \&html_initialize;
my $output_student = \&html_outputstudent;
my $finish = \&html_finish;
@@ -163,9 +187,29 @@ ENDMSG
#
if($c->aborted()) { return ; }
#
+ # Determine which students we want to look at
+ my @Students;
+ if ($single_student_mode) {
+ @Students = (&Apache::lonstatistics::current_student());
+ $r->print(&next_and_previous_buttons());
+ $r->rflush();
+ } else {
+ @Students = @Apache::lonstatistics::Students;
+ }
+ #
+ # Perform generic initialization tasks
+ # Since we use lonnet::EXT to retrieve problem weights,
+ # to ensure current data we must clear the caches out.
+ # This makes sure that parameter changes at the student level
+ # are immediately reflected in the chart.
+ &Apache::lonnet::clear_EXT_cache_status();
+ #
+ # Clean out loncoursedata's package data, just to be safe.
+ &Apache::loncoursedata::clear_internal_caches();
+ #
# Call the initialize routine selected above
$initialize->($r);
- foreach my $student (@Apache::lonstatistics::Students) {
+ foreach my $student (@Students) {
if($c->aborted()) {
$finish->($r);
return ;
@@ -181,6 +225,54 @@ ENDMSG
#######################################################
#######################################################
+sub next_and_previous_buttons {
+ my $Str = '';
+ $Str .= '';
+ #
+ # Build the previous student link
+ my $previous = &Apache::lonstatistics::previous_student();
+ my $previousbutton = '';
+ if (defined($previous)) {
+ my $sname = $previous->{'username'}.':'.$previous->{'domain'};
+ $previousbutton .= '';
+ } else {
+ $previousbutton .= '';
+ }
+ #
+ # Build the next student link
+ my $next = &Apache::lonstatistics::next_student();
+ my $nextbutton = '';
+ if (defined($next)) {
+ my $sname = $next->{'username'}.':'.$next->{'domain'};
+ $nextbutton .= '';
+ } else {
+ $nextbutton .= '';
+ }
+ #
+ # Build the 'all students' button
+ my $all = '';
+ $all .= '';
+ $Str .= $previousbutton.(' 'x5).$all.(' 'x5).$nextbutton;
+ return $Str;
+}
+
+#######################################################
+#######################################################
sub get_student_fields_to_show {
my @to_show = @Apache::lonstatistics::SelectedStudentData;
@@ -193,19 +285,6 @@ sub get_student_fields_to_show {
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;
-}
-
-
#######################################################
#######################################################
@@ -227,13 +306,20 @@ the chart page.
#######################################################
sub CreateInterface {
my $Str = '';
+ $Str .= &Apache::lonhtmlcommon::breadcrumbs(undef,'Chart');
# $Str .= &CreateLegend();
$Str .= '
\n";
+ return $Str;
+}
+
+sub CreateAndParseOutputDataSelector {
+ my $Str = '';
+ my $elementname = 'chartoutputdata';
+ #
+ my $selected = 'scores';
+ if (exists($ENV{'form.'.$elementname})) {
+ if (ref($ENV{'form.'.$elementname} eq 'ARRAY')) {
+ $selected = $ENV{'form.'.$elementname}->[0];
+ } else {
+ $selected = $ENV{'form.'.$elementname};
+ }
+ }
+ #
+ $chosen_output = $OutputDataOptions[0];
+ foreach my $option (@OutputDataOptions) {
+ if ($option->{'value'} eq $selected) {
+ $chosen_output = $option;
+ }
+ }
+ #
+ # Build the form element
+ $Str = qq/";
+ return $Str;
+
+}
+
#######################################################
#######################################################
@@ -374,12 +599,23 @@ Return a line of the chart for a student
my $padding;
my $count;
+ my $nodata_count; # The number of students for which there is no data
+ my %prog_state; # progress state used by loncommon PrgWin routines
+
sub html_initialize {
my ($r) = @_;
#
$padding = ' 'x3;
- $count = 1;
+ $count = 0;
+ $nodata_count = 0;
+ undef(%prog_state);
#
+ $r->print("
\n";
# First, the @StudentData fields need to be listed
my @to_show = &get_student_fields_to_show();
@@ -389,14 +625,44 @@ sub html_initialize {
my $width=$Apache::lonstatistics::StudentData{$field}->{'width'};
$Str .= $title.' 'x($width-$base).$padding;
}
- # Now the selected sequences need to be listed
- 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;
+ #
+ # Compute the column widths and output the sequence titles
+ foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()){
+ #
+ # Comptue column widths
+ $sequence->{'width_sum'} = 0;
+ if ($chosen_output->{'sequence_sum'}) {
+ # Use 3 digits for the sum
+ $sequence->{'width_sum'} = 3;
+ }
+ if ($chosen_output->{'sequence_max'}) {
+ if ($sequence->{'width_sum'}>0) {
+ # One digit for the '/'
+ $sequence->{'width_sum'} +=1;
+ }
+ # Use 3 digits for the total
+ $sequence->{'width_sum'}+=3;
+ }
+ #
+ if ($chosen_output->{'every_problem'}) {
+ # one problem per digit
+ $sequence->{'width_problem'} = $sequence->{'num_assess_parts'};
+ } else {
+ $sequence->{'width_problem'} = 0;
+ }
+ $sequence->{'width_total'} = $sequence->{'width_problem'} +
+ $sequence->{'width_sum'};
+ if ($sequence->{'width_total'} < length(&HTML::Entities::decode($sequence->{'title'}))) {
+ $sequence->{'width_total'} = length(&HTML::Entities::decode($sequence->{'title'}));
+ }
+ #
+ # Output the sequence titles
+ $Str .=
+ $sequence->{'title'}.' 'x($sequence->{'width_total'}-
+ length($sequence->{'title'})
+ ).$padding;
}
- $Str .= "total (of shown problems)
\n";
+ $Str .= "total\n";
$Str .= "
";
$r->print($Str);
$r->rflush();
@@ -406,6 +672,10 @@ sub html_initialize {
sub html_outputstudent {
my ($r,$student) = @_;
my $Str = '';
+ #
+ if($count++ % 5 == 0 && $count > 0) {
+ $r->print("
");
+ }
# First, the @StudentData fields need to be listed
my @to_show = &get_student_fields_to_show();
foreach my $field (@to_show) {
@@ -423,6 +693,7 @@ sub html_outputstudent {
%StudentsData = @tmp;
}
if (scalar(@tmp) < 1) {
+ $nodata_count++;
$Str .= 'No Course Data'."\n";
$r->print($Str);
$r->rflush();
@@ -432,24 +703,36 @@ sub html_outputstudent {
# By sequence build up the data
my $studentstats;
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;
+ foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {
+ my ($performance,$performance_length,$score,$seq_max,$rawdata);
+ if ($chosen_output->{'tries'}) {
+ ($performance,$performance_length,$score,$seq_max,$rawdata) =
+ &StudentTriesOnSequence($student,\%StudentsData,
+ $seq,$show_links);
+ } else {
+ ($performance,$performance_length,$score,$seq_max,$rawdata) =
+ &StudentPerformanceOnSequence($student,\%StudentsData,
+ $seq,$show_links);
+ }
+ my $ratio='';
+ if ($chosen_output->{'sequence_sum'} && $score ne ' ') {
+ $ratio .= sprintf("%3d",$score);
+ } else {
+ $ratio .= ' 'x3;
+ }
+ if ($chosen_output->{'sequence_max'}) {
+ if ($chosen_output->{'sequence_sum'}) {
+ $ratio .= '/';
+ }
+ $ratio .= sprintf("%3d",$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;
+ if (! $chosen_output->{'every_problem'}) {
+ $performance = '';
+ $performance_length=0;
}
+ $performance .= ' 'x($seq->{'width_total'}-$performance_length-$seq->{'width_sum'}).
+ $ratio;
#
$Str .= $performance.$padding;
#
@@ -458,21 +741,25 @@ sub html_outputstudent {
}
#
# Total it up and store the statistics info.
- my ($score,$max) = (0,0);
+ my ($score,$max);
while (my ($symb,$seq_stats) = each (%{$studentstats})) {
$Statistics->{$symb}->{'score'} += $seq_stats->{'score'};
- $Statistics->{$symb}->{'max'} += $seq_stats->{'max'};
- $score += $seq_stats->{'score'};
+ if ($Statistics->{$symb}->{'max'} < $seq_stats->{'max'}) {
+ $Statistics->{$symb}->{'max'} = $seq_stats->{'max'};
+ }
+ if ($seq_stats->{'score'} ne ' ') {
+ $score += $seq_stats->{'score'};
+ $Statistics->{$symb}->{'num_students'}++;
+ }
$max += $seq_stats->{'max'};
}
+ if (! defined($score)) {
+ $score = ' 'x3;
+ }
$Str .= ' '.' 'x(length($max)-length($score)).$score.'/'.$max;
$Str .= " \n";
- $r->print($Str);
#
- $count++;
- if($count % 5 == 0) {
- $r->print("
");
- }
+ $r->print($Str);
#
$r->rflush();
return;
@@ -480,11 +767,86 @@ sub html_outputstudent {
sub html_finish {
my ($r) = @_;
+ #
+ # Check for suppressed output and close the progress window if so
$r->print("
\n";
+ return $Str;
+}
+
}
#######################################################
@@ -513,12 +875,69 @@ my $filename;
my $rows_output;
my $cols_output;
-my $num_students;
-my $start_time;
+my %prog_state; # progress window state
+my $request_aborted;
+
+my $total_formula;
sub excel_initialize {
my ($r) = @_;
#
+ undef ($excel_sheet);
+ undef ($excel_workbook);
+ undef ($filename);
+ undef ($rows_output);
+ undef ($cols_output);
+ undef (%prog_state);
+ undef ($request_aborted);
+ undef ($total_formula);
+ #
+ my $total_columns = scalar(&get_student_fields_to_show());
+ my $num_students = scalar(@Apache::lonstatistics::Students);
+ #
+ foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {
+ if ($chosen_output->{'every_problem'}) {
+ $total_columns += $seq->{'num_assess_parts'};
+ }
+ # Add 2 because we need a 'sequence_sum' and 'total' column for each
+ $total_columns += 2;
+ }
+ if ($chosen_output->{'base'} eq 'tries' && $total_columns > 255) {
+ $r->print(<Unable to Complete Request
+
+LON-CAPA is unable to produce your Excel spreadsheet because your selections
+will result in more than 255 columns. Excel allows only 255 columns in a
+spreadsheet.
+
+You may consider reducing the number of Sequences or Folders you
+have selected.
+
+LON-CAPA can produce CSV files of this data or Excel files of the
+summary data (Parts Correct or Parts Correct & Totals).
+
+LON-CAPA is unable to produce your Excel spreadsheet because your selections
+will result in more than 255 columns. Excel allows only 255 columns in a
+spreadsheet.
+
+You may consider reducing the number of Sequences or Folders you
+have selected.
+
+LON-CAPA can produce CSV files of this data or Excel files of the
+Scores Summary data.
+
+END
+ $request_aborted = 1;
+ }
+ return if ($request_aborted);
+ #
$filename = '/prtspool/'.
$ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'.
time.'_'.rand(1000000000).'.xls';
@@ -529,8 +948,23 @@ sub excel_initialize {
$rows_output = 0;
$cols_output = 0;
#
- $num_students = 0;
- $start_time = time;
+ # Determine rows
+ my $header_row = $rows_output++;
+ my $description_row = $rows_output++;
+ $rows_output++; # blank row
+ my $summary_header_row;
+ if ($chosen_output->{'summary_table'}) {
+ $summary_header_row = $rows_output++;
+ $rows_output+= scalar(&Apache::lonstatistics::Sequences_with_Assess());
+ $rows_output++;
+ }
+ my $sequence_name_row = $rows_output++;
+ my $resource_name_row = $rows_output++;
+ my $maximum_data_row = $rows_output++;
+ if (! $chosen_output->{'maximum_row'}) {
+ $rows_output--;
+ }
+ my $first_data_row = $rows_output++;
#
# Create sheet
$excel_workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
@@ -550,47 +984,298 @@ sub excel_initialize {
# File::Temp is used to determine the temporary directory.
$excel_workbook->set_tempdir($Apache::lonnet::tmpdir);
#
+ my $format = &Apache::loncommon::define_excel_formats($excel_workbook);
+ #
# Add a worksheet
- $excel_sheet = $excel_workbook->addworksheet
- ($ENV{'course.'.$ENV{'request.course.id'}.'.description'});
+ my $sheetname = $ENV{'course.'.$ENV{'request.course.id'}.'.description'};
+ $sheetname = &Apache::loncommon::clean_excel_name($sheetname);
+ $excel_sheet = $excel_workbook->addworksheet($sheetname);
+ #
+ # Put the course description in the header
+ $excel_sheet->write($header_row,$cols_output++,
+ $ENV{'course.'.$ENV{'request.course.id'}.'.description'},
+ $format->{'h1'});
+ $cols_output += 3;
+ #
+ # Put a description of the sections listed
+ my $sectionstring = '';
+ my @Sections = @Apache::lonstatistics::SelectedSections;
+ if (scalar(@Sections) > 1) {
+ if (scalar(@Sections) > 2) {
+ my $last = pop(@Sections);
+ $sectionstring = "Sections ".join(', ',@Sections).', and '.$last;
+ } else {
+ $sectionstring = "Sections ".join(' and ',@Sections);
+ }
+ } else {
+ if ($Sections[0] eq 'all') {
+ $sectionstring = "All sections";
+ } else {
+ $sectionstring = "Section ".$Sections[0];
+ }
+ }
+ $excel_sheet->write($header_row,$cols_output++,$sectionstring,
+ $format->{'h3'});
+ $cols_output += scalar(@Sections);
+ #
+ # Put the date in there too
+ $excel_sheet->write($header_row,$cols_output++,
+ 'Compiled on '.localtime(time),$format->{'h3'});
+ #
+ $cols_output = 0;
+ $excel_sheet->write($description_row,$cols_output++,
+ $chosen_output->{'shortdesc'},
+ $format->{'h3'});
+ ##############################################
+ # Output headings for the raw data
+ ##############################################
#
# Add the student headers
+ $cols_output = 0;
foreach my $field (&get_student_fields_to_show()) {
- $excel_sheet->write(1,$cols_output++,$field);
+ $excel_sheet->write($resource_name_row,$cols_output++,$field,
+ $format->{'bold'});
}
#
- # 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;
+ # Add the remaining column headers
+ my $total_formula_string = '=0';
+ foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {
+ $excel_sheet->write($sequence_name_row,,
+ $cols_output,$seq->{'title'},$format->{'bold'});
+ # Determine starting cell
+ $seq->{'Excel:startcell'}=
+ &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell
+ ($first_data_row,$cols_output);
+ $seq->{'Excel:startcol'}=$cols_output;
+ my $count = 0;
+ if ($chosen_output->{'every_problem'}) {
+ # Put the names of the problems and parts into the sheet
+ foreach my $res (@{$seq->{'contents'}}) {
+ if ($res->{'type'} ne 'assessment' ||
+ ! exists($res->{'parts'}) ||
+ ref($res->{'parts'}) ne 'ARRAY' ||
+ scalar(@{$res->{'parts'}}) < 1) {
+ next;
+ }
+ if (scalar(@{$res->{'parts'}}) > 1) {
+ foreach my $part (@{$res->{'parts'}}) {
+ $excel_sheet->write($resource_name_row,
+ $cols_output++,
+ $res->{'title'}.' part '.$part,
+ $format->{'bold'});
+ }
+ } else {
+ $excel_sheet->write($resource_name_row,
+ $cols_output++,
+ $res->{'title'},$format->{'bold'});
+ }
+ $count++;
+ }
+ }
+ # Determine ending cell
+ if ($count <= 1) {
+ $seq->{'Excel:endcell'} = $seq->{'Excel:startcell'};
+ $seq->{'Excel:endcol'} = $seq->{'Excel:startcol'};
} else {
- $cols_output++;
+ $seq->{'Excel:endcell'} =
+ &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell
+ ($first_data_row,$cols_output-1);
+ $seq->{'Excel:endcol'} = $cols_output-1;
+ }
+ # Create the formula for summing up this sequence
+ if (! exists($seq->{'Excel:endcell'}) ||
+ ! defined($seq->{'Excel:endcell'})) {
+ $seq->{'Excel:endcell'} = $seq->{'Excel:startcell'};
+ }
+ $seq->{'Excel:sum'}= $excel_sheet->store_formula
+ ('=SUM('.$seq->{'Excel:startcell'}.
+ ':'.$seq->{'Excel:endcell'}.')');
+ # Determine cell the score is held in
+ $seq->{'Excel:scorecell'} =
+ &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell
+ ($first_data_row,$cols_output);
+ $seq->{'Excel:scorecol'}=$cols_output;
+ if ($chosen_output->{'base'} eq 'parts correct total') {
+ $excel_sheet->write($resource_name_row,$cols_output++,
+ 'parts correct',
+ $format->{'bold'});
+ } elsif ($chosen_output->{'sequence_sum'}) {
+ if ($chosen_output->{'correct'}) {
+ # Only reporting the number correct, so do not call it score
+ $excel_sheet->write($resource_name_row,$cols_output++,
+ 'sum',
+ $format->{'bold'});
+ } else {
+ $excel_sheet->write($resource_name_row,$cols_output++,
+ 'score',
+ $format->{'bold'});
+ }
+ }
+ #
+ $total_formula_string.='+'.
+ &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell
+ ($first_data_row,$cols_output-1);
+ if ($chosen_output->{'sequence_max'}) {
+ $excel_sheet->write($resource_name_row,$cols_output++,
+ 'maximum',
+ $format->{'bold'});
}
}
- #
- # Bookkeeping
- if ($show eq 'totals') {
- $rows_output = 2;
- } else {
- $rows_output = 1;
+ if ($chosen_output->{'grand_total'}) {
+ $excel_sheet->write($resource_name_row,$cols_output++,'Total',
+ $format->{'bold'});
+ }
+ $total_formula = $excel_sheet->store_formula($total_formula_string);
+ ##############################################
+ # Output a row for MAX, if appropriate
+ ##############################################
+ if ($chosen_output->{'maximum_row'}) {
+ $cols_output = 0;
+ foreach my $field (&get_student_fields_to_show()) {
+ if ($field eq 'username' || $field eq 'fullname' ||
+ $field eq 'id') {
+ $excel_sheet->write($maximum_data_row,$cols_output++,'Maximum',
+ $format->{'bold'});
+ } else {
+ $excel_sheet->write($maximum_data_row,$cols_output++,'');
+ }
+ }
+ #
+ # Add the maximums for each sequence or assessment
+ my %total_cell_translation;
+ foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {
+ $cols_output=$seq->{'Excel:startcol'};
+ $total_cell_translation{$seq->{'Excel:scorecell'}} =
+ &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell
+ ($maximum_data_row,$seq->{'Excel:scorecol'});
+ my $weight;
+ my $max = 0;
+ foreach my $resource (@{$seq->{'contents'}}) {
+ next if ($resource->{'type'} ne 'assessment');
+ foreach my $part (@{$resource->{'parts'}}) {
+ $weight = 1;
+ if ($chosen_output->{'scores'}) {
+ $weight = &Apache::lonnet::EXT
+ ('resource.'.$part.'.weight',$resource->{'symb'},
+ undef,undef,undef);
+ if (!defined($weight) || ($weight eq '')) {
+ $weight=1;
+ }
+ }
+ if ($chosen_output->{'scores'} &&
+ $chosen_output->{'every_problem'}) {
+ $excel_sheet->write($maximum_data_row,$cols_output++,
+ $weight);
+ }
+ $max += $weight;
+ }
+ }
+ #
+ if ($chosen_output->{'sequence_sum'} &&
+ $chosen_output->{'every_problem'}) {
+ my %replaceCells;
+ $replaceCells{$seq->{'Excel:startcell'}} =
+ &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell
+ ($maximum_data_row,$seq->{'Excel:startcol'});
+ $replaceCells{$seq->{'Excel:endcell'}} =
+ &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell
+ ($maximum_data_row,$seq->{'Excel:endcol'});
+ $excel_sheet->repeat_formula($maximum_data_row,$cols_output++,
+ $seq->{'Excel:sum'},undef,
+ %replaceCells);
+ } elsif ($chosen_output->{'sequence_sum'}) {
+ $excel_sheet->write($maximum_data_row,$cols_output++,$max);
+ }
+ if ($chosen_output->{'sequence_max'}) {
+ $excel_sheet->write($maximum_data_row,$cols_output++,$max);
+ }
+ #
+ }
+ if ($chosen_output->{'grand_total'}) {
+ $excel_sheet->repeat_formula($maximum_data_row,$cols_output++,
+ $total_formula,undef,
+ %total_cell_translation);
+ }
+ } # End of MAXIMUM row output if ($chosen_output->{'maximum_row'}) {
+ $rows_output = $first_data_row;
+ ##############################################
+ # Output summary table, which actually is above the sequence name row.
+ ##############################################
+ if ($chosen_output->{'summary_table'}) {
+ $cols_output = 0;
+ $excel_sheet->write($summary_header_row,$cols_output++,
+ 'Summary Table',$format->{'bold'});
+ if ($chosen_output->{'maximum_row'}) {
+ $excel_sheet->write($summary_header_row,$cols_output++,
+ 'Maximum',$format->{'bold'});
+ }
+ $excel_sheet->write($summary_header_row,$cols_output++,
+ 'Average',$format->{'bold'});
+ $excel_sheet->write($summary_header_row,$cols_output++,
+ 'Median',$format->{'bold'});
+ $excel_sheet->write($summary_header_row,$cols_output++,
+ 'Std Dev',$format->{'bold'});
+ my $row = $summary_header_row+1;
+ foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {
+ $cols_output = 0;
+ $excel_sheet->write($row,$cols_output++,
+ $seq->{'title'},
+ $format->{'bold'});
+ if ($chosen_output->{'maximum_row'}) {
+ $excel_sheet->write
+ ($row,$cols_output++,
+ '='.
+ &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell
+ ($maximum_data_row,$seq->{'Excel:scorecol'})
+ );
+ }
+ my $range =
+ &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell
+ ($first_data_row,$seq->{'Excel:scorecol'}).
+ ':'.
+ &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell
+ ($first_data_row+$num_students-1,$seq->{'Excel:scorecol'});
+ $excel_sheet->write($row,$cols_output++,
+ '=AVERAGE('.$range.')');
+ $excel_sheet->write($row,$cols_output++,
+ '=MEDIAN('.$range.')');
+ $excel_sheet->write($row,$cols_output++,
+ '=STDEV('.$range.')');
+ $row++;
+ }
}
+ ##############################################
+ # Take care of non-excel initialization
+ ##############################################
#
# Let the user know what we are doing
my $studentcount = scalar(@Apache::lonstatistics::Students);
- $r->print("
Compiling Excel spreadsheet for ".
- $studentcount.' student');
- $r->print('s') if ($studentcount > 1);
- $r->print("
\n");
+ if ($ENV{'form.SelectedStudent'}) {
+ $studentcount = '1';
+ }
+ if ($studentcount > 1) {
+ $r->print('
'.&mt('Compiling Excel spreadsheet for [_1] students',
+ $studentcount)."
\n");
+ } else {
+ $r->print('
'.
+ &mt('Compiling Excel spreadsheet for 1 student').
+ "
\n");
+ }
$r->rflush();
#
+ # Initialize progress window
+ %prog_state=&Apache::lonhtmlcommon::Create_PrgWin
+ ($r,'Excel File Compilation Status',
+ 'Excel File Compilation Progress', $studentcount);
+ #
+ &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,
+ 'Processing first student');
return;
}
sub excel_outputstudent {
my ($r,$student) = @_;
+ return if ($request_aborted);
return if (! defined($excel_sheet));
$cols_output=0;
#
@@ -611,51 +1296,93 @@ sub excel_outputstudent {
}
#
# Write out sequence scores and totals data
- foreach my $seq (&get_sequences_to_show) {
- my ($performance,$score,$seq_max) =
- &StudentPerformanceOnSequence($student,\%StudentsData,
- $seq,'no');
- if ($show eq 'totals' || $show eq 'scores') {
- $excel_sheet->write($rows_output,$cols_output++,$score);
+ my %total_cell_translation;
+ foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {
+ $cols_output = $seq->{'Excel:startcol'};
+ # Keep track of cells to translate in total cell
+ $total_cell_translation{$seq->{'Excel:scorecell'}} =
+ &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell
+ ($rows_output,$seq->{'Excel:scorecol'});
+ #
+ my ($performance,$performance_length,$score,$seq_max,$rawdata);
+ if ($chosen_output->{'tries'} || $chosen_output->{'correct'}){
+ ($performance,$performance_length,$score,$seq_max,$rawdata) =
+ &StudentTriesOnSequence($student,\%StudentsData,
+ $seq,'no');
+ } else {
+ ($performance,$performance_length,$score,$seq_max,$rawdata) =
+ &StudentPerformanceOnSequence($student,\%StudentsData,
+ $seq,'no');
+ }
+ if ($chosen_output->{'every_problem'}) {
+ if ($chosen_output->{'correct'}) {
+ # only indiciate if each item is correct or not
+ foreach my $value (@$rawdata) {
+ # nonzero means correct
+ $value = 1 if ($value > 0);
+ $excel_sheet->write($rows_output,$cols_output++,$value);
+ }
+ } else {
+ foreach my $value (@$rawdata) {
+ if ($score eq ' ' || !defined($value)) {
+ $cols_output++;
+ } else {
+ $excel_sheet->write($rows_output,$cols_output++,
+ $value);
+ }
+ }
+ }
}
- if ($show eq 'totals') {
+ if ($chosen_output->{'sequence_sum'} &&
+ $chosen_output->{'every_problem'}) {
+ # Write a formula for the sum of this sequence
+ my %replaceCells;
+ $replaceCells{$seq->{'Excel:startcell'}} =
+ &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell
+ ($rows_output,$seq->{'Excel:startcol'});
+ $replaceCells{$seq->{'Excel:endcell'}} =
+ &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell
+ ($rows_output,$seq->{'Excel:endcol'});
+ # The undef is for the format
+ if (scalar(keys(%replaceCells)) == 1) {
+ $excel_sheet->repeat_formula($rows_output,$cols_output++,
+ $seq->{'Excel:sum'},undef,
+ %replaceCells,%replaceCells);
+ } else {
+ $excel_sheet->repeat_formula($rows_output,$cols_output++,
+ $seq->{'Excel:sum'},undef,
+ %replaceCells);
+ }
+ } elsif ($chosen_output->{'sequence_sum'}) {
+ if ($score eq ' ') {
+ $cols_output++;
+ } else {
+ $excel_sheet->write($rows_output,$cols_output++,$score);
+ }
+ }
+ if ($chosen_output->{'sequence_max'}) {
$excel_sheet->write($rows_output,$cols_output++,$seq_max);
}
}
#
+ if ($chosen_output->{'grand_total'}) {
+ $excel_sheet->repeat_formula($rows_output,$cols_output++,
+ $total_formula,undef,
+ %total_cell_translation);
+ }
+ #
# Bookkeeping
$rows_output++;
$cols_output=0;
#
- # Time estimate
- $num_students++;
- if ($num_students % 10 == 0) {
- my $time_est = (time - $start_time)/$num_students *
- (scalar(@Apache::lonstatistics::Students)-$num_students);
- $time_est = int($time_est);
- if (int ($time_est/60) > 0) {
- my $min = int($time_est/60);
- my $sec = $time_est % 60;
- $time_est = $min.' minutes';
- if ($sec > 1) {
- $time_est.= ', '.$sec.' seconds';
- } elsif ($sec > 0) {
- $time_est.= ', '.$sec.' second';
- }
- } else {
- $time_est .= ' seconds';
- }
- $r->print($num_students.' out of '.
- (scalar(@Apache::lonstatistics::Students)).
- " students processed. ".
- $time_est." remain. \n");
- $r->rflush();
- }
+ # Update the progress window
+ &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,'last student');
return;
}
sub excel_finish {
my ($r) = @_;
+ return if ($request_aborted);
return if (! defined($excel_sheet));
#
# Write the excel file
@@ -664,14 +1391,12 @@ sub excel_finish {
#
return if($c->aborted());
#
+ # Close the progress window
+ &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
+ #
# Tell the user where to get their excel file
- $r->print('