--- loncom/interface/statistics/lonstudentassessment.pm 2004/03/10 18:50:38 1.98 +++ loncom/interface/statistics/lonstudentassessment.pm 2005/02/25 19:48:01 1.114 @@ -1,6 +1,6 @@ # The LearningOnline Network with CAPA # -# $Id: lonstudentassessment.pm,v 1.98 2004/03/10 18:50:38 matthew Exp $ +# $Id: lonstudentassessment.pm,v 1.114 2005/02/25 19:48:01 matthew Exp $ # # Copyright Michigan State University Board of Trustees # @@ -56,6 +56,7 @@ use Apache::loncommon(); use Apache::loncoursedata; use Apache::lonnet; # for logging porpoises use Apache::lonlocal; +use Time::HiRes; use Spreadsheet::WriteExcel; use Spreadsheet::WriteExcel::Utility(); @@ -153,6 +154,8 @@ sub BuildStudentAssessmentPage { # $single_student_mode = 0; $single_student_mode = 1 if ($ENV{'form.SelectedStudent'}); + &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, + ['selectstudent']); if ($ENV{'form.selectstudent'}) { &Apache::lonstatistics::DisplayClasslist($r); return; @@ -174,6 +177,7 @@ sub BuildStudentAssessmentPage { my $initialize = \&html_initialize; my $output_student = \&html_outputstudent; my $finish = \&html_finish; + &Apache::lonnet::logthis('got here! 1'); # if ($output_mode eq 'excel') { $initialize = \&excel_initialize; @@ -185,6 +189,7 @@ sub BuildStudentAssessmentPage { $finish = \&csv_finish; } # + &Apache::lonnet::logthis('got here! 2'); if($c->aborted()) { return ; } # # Determine which students we want to look at @@ -209,6 +214,7 @@ sub BuildStudentAssessmentPage { # # Call the initialize routine selected above $initialize->($r); + &Apache::lonnet::logthis('got here! 3'); foreach my $student (@Students) { if($c->aborted()) { $finish->($r); @@ -325,21 +331,12 @@ sub CreateInterface { $Str .= ''."\n"; $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5); $Str .= ''; - my $only_seq_with_assessments = sub { - my $s=shift; - if ($s->{'num_assess'} < 1) { - return 0; - } else { - return 1; - } - }; $Str .= &Apache::lonstatistics::StudentDataSelect('StudentData','multiple', 5,undef); $Str .= ''."\n"; $Str .= &Apache::lonhtmlcommon::StatusOptions(undef,undef,5); $Str .= ''."\n"; - $Str .= &Apache::lonstatistics::MapSelect('Maps','multiple,all',5, - $only_seq_with_assessments); + $Str .= &Apache::lonstatistics::map_select('Maps','multiple,all',5); $Str .= ''."\n"; $Str .= &CreateAndParseOutputSelector(); $Str .= ''."\n"; @@ -355,6 +352,9 @@ sub CreateInterface { $Str .= ''; $Str .= ' 'x5; + $Str .= + &mt('Status[_1]', + ''); $Str .= '
'; return $Str; } @@ -474,6 +474,7 @@ my @OutputDataOptions = sequence_sum => 1, sequence_max => 1, grand_total => 1, + grand_maximum => 1, summary_table => 1, maximum_row => 1, shortdesc => 'Total Score and Maximum Possible for each '. @@ -491,6 +492,7 @@ my @OutputDataOptions = sequence_sum => 1, sequence_max => 1, grand_total => 1, + grand_maximum => 1, summary_table => 1, maximum_row => 1, shortdesc => 'Score on each Problem Part', @@ -507,10 +509,12 @@ my @OutputDataOptions = sequence_sum => 0, sequence_max => 0, grand_total => 0, + grand_maximum => 0, summary_table => 0, maximum_row => 0, shortdesc => 'Number of Tries before success on each Problem Part', longdesc =>'The number of tries before success on each problem part.', + non_html_notes => 'negative values indicate an incorrect problem', }, { name =>'Parts Correct', base =>'tries', @@ -522,6 +526,7 @@ my @OutputDataOptions = sequence_sum => 1, sequence_max => 1, grand_total => 1, + grand_maximum => 1, summary_table => 1, maximum_row => 0, shortdesc => 'Number of Problem Parts completed successfully.', @@ -601,6 +606,11 @@ Return a line of the chart for a student my $nodata_count; # The number of students for which there is no data my %prog_state; # progress state used by loncommon PrgWin routines + my $total_sum_width; + + my %width; # Holds sequence width information + my @sequences; + my $navmap; # Have to keep this around since weakref is a bit zealous sub html_initialize { my ($r) = @_; @@ -609,10 +619,14 @@ sub html_initialize { $count = 0; $nodata_count = 0; undef(%prog_state); + undef(%width); + undef($navmap); + undef(@sequences); + &Apache::lonnet::logthis('called html_initialize'); # $r->print("

".$ENV{'course.'.$ENV{'request.course.id'}.'.description'}. "  ".localtime(time)."

"); - + # if ($chosen_output->{'base'} !~ /^final table/) { $r->print("

".$chosen_output->{'shortdesc'}."

"); } @@ -627,42 +641,72 @@ sub html_initialize { } # # Compute the column widths and output the sequence titles - foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()){ - # - # Comptue column widths - $sequence->{'width_sum'} = 0; + my $total_count; + ($navmap,@sequences) = + &Apache::lonstatistics::selected_sequences_with_assessments(); + if (! ref($navmap)) { + # Unable to get data, so bail out + $r->print("

". + &mt('Unable to retrieve course information.'). + '

'); + } + # + # Compute sequence widths + my $starttime = Time::HiRes::time; + foreach my $seq (@sequences) { + my $symb = $seq->symb; + my $title = $seq->compTitle; + $width{$symb}->{'width_sum'} = 0; + # Compute width of sum if ($chosen_output->{'sequence_sum'}) { + if ($chosen_output->{'every_problem'}) { + # Use 1 digit for a space + $width{$symb}->{'width_sum'} += 1; + } + $total_count += $width{$symb}->{'num_assess_parts'}; # Use 3 digits for the sum - $sequence->{'width_sum'} = 3; + $width{$symb}->{'width_sum'} += 3; } + # Compute width of maximum if ($chosen_output->{'sequence_max'}) { - if ($sequence->{'width_sum'}>0) { + if ($width{$symb}->{'width_sum'}>0) { # One digit for the '/' - $sequence->{'width_sum'} +=1; + $width{$symb}->{'width_sum'} +=1; } # Use 3 digits for the total - $sequence->{'width_sum'}+=3; + $width{$symb}->{'width_sum'}+=3; } # if ($chosen_output->{'every_problem'}) { # one problem per digit - $sequence->{'width_problem'} = $sequence->{'num_assess_parts'}; + $width{$symb}->{'width_parts'}=0; + $starttime = Time::HiRes::time; + my @resources = + $navmap->retrieveResources($seq,sub { shift->is_problem(); }, + 0,0,0); + $starttime = Time::HiRes::time; + foreach my $res (@resources) { + my @parts = @{$res->parts}; + $width{$symb}->{'width_parts'} += scalar(@parts); + } + &Apache::lonnet::logthis('2elapsed:'.(Time::HiRes::time-$starttime)); $width{$symb}->{'width_problem'} += + $width{$symb}->{'width_parts'}; } else { - $sequence->{'width_problem'} = 0; + $width{$symb}->{'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'})); + $width{$symb}->{'width_total'} = $width{$symb}->{'width_problem'} + + $width{$symb}->{'width_sum'}; + if ($width{$symb}->{'width_total'} < length(&HTML::Entities::decode($title))) { + $width{$symb}->{'width_total'} = length(&HTML::Entities::decode($title)); } # # Output the sequence titles - $Str .= - $sequence->{'title'}.' 'x($sequence->{'width_total'}- - length($sequence->{'title'}) - ).$padding; + $Str .= $title.(' 'x($width{$symb}->{'width_total'}- + length($title) + )).$padding; } - $Str .= "total\n"; + $total_sum_width = length($total_count)+1; + $Str .= " total\n"; $Str .= "
";
     $r->print($Str);
     $r->rflush();
@@ -672,6 +716,7 @@ sub html_initialize {
 sub html_outputstudent {
     my ($r,$student) = @_;
     my $Str = '';
+    return if (! defined($navmap));
     #
     if($count++ % 5 == 0 && $count > 0) {
         $r->print("
");
@@ -680,6 +725,10 @@ sub html_outputstudent {
     my @to_show = &get_student_fields_to_show();
     foreach my $field (@to_show) {
         my $title=$student->{$field};
+        # Deal with 'comments' - how I love special cases
+        if ($field eq 'comments') {
+            $title = ''.&mt('Comments').'';
+        }
         my $base = length($title);
         my $width=$Apache::lonstatistics::StudentData{$field}->{'width'};
         $Str .= $title.' 'x($width-$base).$padding;
@@ -703,20 +752,24 @@ sub html_outputstudent {
     # By sequence build up the data
     my $studentstats;
     my $PerformanceStr = '';
-    foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {
+    foreach my $seq (@sequences) {
+        &Apache::lonnet::logthis('computing student data for '.$seq->compTitle);
         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);
+                &student_tries_on_sequence($student,\%StudentsData,
+                                           $navmap,$seq,$show_links);
         } else {
             ($performance,$performance_length,$score,$seq_max,$rawdata) =
-                &StudentPerformanceOnSequence($student,\%StudentsData,
-                                              $seq,$show_links);
+                &student_performance_on_sequence($student,\%StudentsData,
+                                                 $navmap,$seq,$show_links);
         }
         my $ratio='';
+        if ($chosen_output->{'every_problem'}) {
+            $ratio .= ' ';
+        }
         if ($chosen_output->{'sequence_sum'} && $score ne ' ') {
-            $ratio .= sprintf("%3d",$score);
+            $ratio .= sprintf("%3.0f",$score);
         } elsif($chosen_output->{'sequence_sum'}) {
             $ratio .= ' 'x3;
         }
@@ -724,20 +777,22 @@ sub html_outputstudent {
             if ($chosen_output->{'sequence_sum'}) {
                 $ratio .= '/';
             }
-            $ratio .= sprintf("%3d",$seq_max);
+            $ratio .= sprintf("%3.0f",$seq_max);
         }
         #
         if (! $chosen_output->{'every_problem'}) {
             $performance = '';
 	    $performance_length=0;
         }
-        $performance .= ' 'x($seq->{'width_total'}-$performance_length-$seq->{'width_sum'}).
+        $performance .= ' 'x($width{$seq->symb}->{'width_total'} -
+                             $performance_length -
+                             $width{$seq->symb}->{'width_sum'}).
             $ratio;
         #
         $Str .= $performance.$padding;
         #
-        $studentstats->{$seq->{'symb'}}->{'score'}= $score;
-        $studentstats->{$seq->{'symb'}}->{'max'}  = $seq_max;
+        $studentstats->{$seq->symb}->{'score'}= $score;
+        $studentstats->{$seq->symb}->{'max'}  = $seq_max;
     }
     #
     # Total it up and store the statistics info.
@@ -754,9 +809,11 @@ sub html_outputstudent {
         $max   += $seq_stats->{'max'};
     }
     if (! defined($score)) {
-        $score = ' 'x3;
+        $score = ' ' x $total_sum_width;
+    } else {
+        $score = sprintf("%.0f",$score);
     }
-    $Str .= ' '.' 'x(length($max)-length($score)).$score.'/'.$max;
+    $Str .= ' '.' 'x($total_sum_width-length($score)).$score.' / '.$max;
     $Str .= " \n";
     #
     $r->print($Str);
@@ -767,6 +824,7 @@ sub html_outputstudent {
 
 sub html_finish {
     my ($r) = @_;
+    return if (! defined($navmap));
     #
     # Check for suppressed output and close the progress window if so
     $r->print("
\n"); @@ -778,69 +836,67 @@ sub html_finish { } } $r->rflush(); + undef($navmap); return; } sub StudentAverageTotal { - my $Str = "

Summary Tables

\n"; -# my $max_students; -# my $total_ave = 0; -# my $total_max = 0; + my $Str = '

'.&mt('Summary Tables').'

'.$/; $Str .= ''."\n"; - $Str .= "\n"; - foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) { + $Str .= ''. + ''. + ''. + ''. + ''.$/; + foreach my $seq (@sequences) { + my $symb = $seq->symb; my $ave; - my $num_students = $Statistics->{$seq->{'symb'}}->{'num_students'}; -# if ($num_students > $max_students) { -# $max_students = $num_students; -# } + my $num_students = $Statistics->{$symb}->{'num_students'}; if ($num_students > 0) { $ave = int(100* - ($Statistics->{$seq->{'symb'}}->{'score'}/$num_students) + ($Statistics->{$symb}->{'score'}/$num_students) )/100; } else { $ave = 0; } -# $total_ave += $ave; - my $max = $Statistics->{$seq->{'symb'}}->{'max'}; -# $total_max += $max; + my $max = $Statistics->{$symb}->{'max'}; $ave = sprintf("%.2f",$ave); - $Str .= ''. + $Str .= ''. ''. ''."\n"; } -# $total_ave = sprintf('%.2f',$total_ave); # only two digit $Str .= "
TitleAverageMaximum
'.&mt('Title').''.&mt('Average').''.&mt('Maximum').'
'.$seq->{'title'}.'
'.$seq->compTitle.''.$ave.' '.$max.' '.'
\n"; -# $Str .= ''."\n"; -# $Str .= ''. -# "\n"; -# $Str .= ''. -# ''. -# ''. -# ''; -# $Str .= "
Number of StudentsAverageMaximum
'.$max_students.''.$total_ave.' '.''.$total_max.' '.'
\n"; return $Str; } sub SingleStudentTotal { my $student = &Apache::lonstatistics::current_student(); - my $Str = "

Summary table for ".$student->{'fullname'}." ". - $student->{'username'}.'@'.$student->{'domain'}."

\n"; + my $Str = '

'.&mt('Summary table for [_1] ([_2]@[_3])', + $student->{'fullname'}, + $student->{'username'},$student->{'domain'}).'

'; + $Str .= $/; $Str .= ''."\n"; $Str .= - "\n"; + ''. + ''; + if ($chosen_output->{'base'} eq 'tries') { + $Str .= ''; + } else { + $Str .= ''; + } + $Str .= ''."\n"; my $total = 0; my $total_max = 0; foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) { - my $value = $Statistics->{$seq->{'symb'}}->{'score'}; - my $max = $Statistics->{$seq->{'symb'}}->{'max'}; - $Str .= ''. + my $value = $Statistics->{$seq->symb}->{'score'}; + my $max = $Statistics->{$seq->symb}->{'max'}; + $Str .= ''. ''. ''."\n"; $total += $value; $total_max +=$max; } - $Str .= ''. + $Str .= ''. ''. '\n"; $Str .= "
Sequence or FolderScoreMaximum
'.&mt('Sequence or Folder').''.&mt('Parts Correct').''.&mt('Score').''.&mt('Maximum').'
'.$seq->{'title'}.'
'.&HTML::Entities::encode($seq->compTitle).''.$value.''.$max.'
Total
'.&mt('Total').''.$total.''.$total_max."
\n"; @@ -879,6 +935,7 @@ my %prog_state; # progress window state my $request_aborted; my $total_formula; +my $maximum_formula; sub excel_initialize { my ($r) = @_; @@ -891,6 +948,7 @@ sub excel_initialize { undef (%prog_state); undef ($request_aborted); undef ($total_formula); + undef ($maximum_formula); # my $total_columns = scalar(&get_student_fields_to_show()); my $num_students = scalar(@Apache::lonstatistics::Students); @@ -902,39 +960,18 @@ sub excel_initialize { # Add 2 because we need a 'sequence_sum' and 'total' column for each $total_columns += 2; } + my $too_many_cols_error_message = + '

'.&mt('Unable to Complete Request').'

'.$/. + '

'.&mt('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.').'

'.$/. + '

'.&mt('You may consider reducing the number of Sequences or Folders you have selected.').'

'.$/. + '

'.&mt('LON-CAPA can produce CSV files of this data or Excel files of the Scores Summary data.').'

'.$/; 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). -

-END - $request_aborted = 1; + $r->print($too_many_cols_error_message); + $request_aborted = 1; } if ($chosen_output->{'base'} eq 'scores' && $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 -Scores Summary data. -

-END - $request_aborted = 1; + $r->print($too_many_cols_error_message); + $request_aborted = 1; } return if ($request_aborted); # @@ -951,6 +988,7 @@ END # Determine rows my $header_row = $rows_output++; my $description_row = $rows_output++; + my $notes_row = $rows_output++; $rows_output++; # blank row my $summary_header_row; if ($chosen_output->{'summary_table'}) { @@ -1025,7 +1063,13 @@ END $cols_output = 0; $excel_sheet->write($description_row,$cols_output++, $chosen_output->{'shortdesc'}, - $format->{'h3'}); + $format->{'b'}); + # + $cols_output = 0; + $excel_sheet->write($notes_row,$cols_output++, + $chosen_output->{'non_html_notes'}, + $format->{'i'}); + ############################################## # Output headings for the raw data ############################################## @@ -1039,6 +1083,7 @@ END # # Add the remaining column headers my $total_formula_string = '=0'; + my $maximum_formula_string = '=0'; foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) { $excel_sheet->write($sequence_name_row,, $cols_output,$seq->{'title'},$format->{'bold'}); @@ -1063,13 +1108,14 @@ END $cols_output++, $res->{'title'}.' part '.$part, $format->{'bold'}); + $count++; } } else { $excel_sheet->write($resource_name_row, $cols_output++, $res->{'title'},$format->{'bold'}); + $count++; } - $count++; } } # Determine ending cell @@ -1116,16 +1162,30 @@ END &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++, + $excel_sheet->write($resource_name_row,$cols_output, 'maximum', $format->{'bold'}); + $seq->{'Excel:maxcell'} = + &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell + ($first_data_row,$cols_output); + $seq->{'Excel:maxcol'}=$cols_output; + $maximum_formula_string.='+'. + &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell + ($first_data_row,$cols_output); + $cols_output++; + } } if ($chosen_output->{'grand_total'}) { $excel_sheet->write($resource_name_row,$cols_output++,'Total', $format->{'bold'}); } + if ($chosen_output->{'grand_maximum'}) { + $excel_sheet->write($resource_name_row,$cols_output++,'Max. Total', + $format->{'bold'}); + } $total_formula = $excel_sheet->store_formula($total_formula_string); + $maximum_formula = $excel_sheet->store_formula($maximum_formula_string); ############################################## # Output a row for MAX, if appropriate ############################################## @@ -1143,11 +1203,15 @@ END # # Add the maximums for each sequence or assessment my %total_cell_translation; + my %maximum_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'}); + $maximum_cell_translation{$seq->{'Excel:maxcell'}} = + &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell + ($maximum_data_row,$seq->{'Excel:maxcol'}); my $weight; my $max = 0; foreach my $resource (@{$seq->{'contents'}}) { @@ -1173,16 +1237,15 @@ END # 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'}); + my %replaceCells= + ('^'.$seq->{'Excel:startcell'}.':'. + $seq->{'Excel:endcell'}.'$' => + &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell($maximum_data_row,$seq->{'Excel:startcol'}).':'. + &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); + %replaceCells); + } elsif ($chosen_output->{'sequence_sum'}) { $excel_sheet->write($maximum_data_row,$cols_output++,$max); } @@ -1196,6 +1259,11 @@ END $total_formula,undef, %total_cell_translation); } + if ($chosen_output->{'grand_maximum'}) { + $excel_sheet->repeat_formula($maximum_data_row,$cols_output++, + $maximum_formula,undef, + %maximum_cell_translation); + } } # End of MAXIMUM row output if ($chosen_output->{'maximum_row'}) { $rows_output = $first_data_row; ############################################## @@ -1266,7 +1334,8 @@ END # Initialize progress window %prog_state=&Apache::lonhtmlcommon::Create_PrgWin ($r,'Excel File Compilation Status', - 'Excel File Compilation Progress', $studentcount); + 'Excel File Compilation Progress', $studentcount, + 'inline',undef,'Statistics','stats_status'); # &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state, 'Processing first student'); @@ -1282,7 +1351,12 @@ sub excel_outputstudent { # Write out student data my @to_show = &get_student_fields_to_show(); foreach my $field (@to_show) { - $excel_sheet->write($rows_output,$cols_output++,$student->{$field}); + my $value = $student->{$field}; + if ($field eq 'comments') { + $value = &Apache::lonmsg::retrieve_instructor_comments + ($student->{'username'},$student->{'domain'}); + } + $excel_sheet->write($rows_output,$cols_output++,$value); } # # Get student assessment data @@ -1297,12 +1371,17 @@ sub excel_outputstudent { # # Write out sequence scores and totals data my %total_cell_translation; + my %maximum_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'}); + # and maximum cell + $maximum_cell_translation{$seq->{'Excel:maxcell'}} = + &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell + ($rows_output,$seq->{'Excel:maxcol'}); # my ($performance,$performance_length,$score,$seq_max,$rawdata); if ($chosen_output->{'tries'} || $chosen_output->{'correct'}){ @@ -1336,23 +1415,16 @@ sub excel_outputstudent { 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); - } + my %replaceCells= + ('^'.$seq->{'Excel:startcell'}.':'.$seq->{'Excel:endcell'}.'$' + => + &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell($rows_output,$seq->{'Excel:startcol'}).':'. + &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell($rows_output,$seq->{'Excel:endcol'}) + ); + # The undef is for the format + $excel_sheet->repeat_formula($rows_output,$cols_output++, + $seq->{'Excel:sum'},undef, + %replaceCells); } elsif ($chosen_output->{'sequence_sum'}) { if ($score eq ' ') { $cols_output++; @@ -1370,6 +1442,11 @@ sub excel_outputstudent { $total_formula,undef, %total_cell_translation); } + if ($chosen_output->{'grand_maximum'}) { + $excel_sheet->repeat_formula($rows_output,$cols_output++, + $maximum_formula,undef, + %maximum_cell_translation); + } # # Bookkeeping $rows_output++; @@ -1452,7 +1529,8 @@ END my $studentcount = scalar(@Apache::lonstatistics::Students); %prog_state=&Apache::lonhtmlcommon::Create_PrgWin ($r,'CSV File Compilation Status', - 'CSV File Compilation Progress', $studentcount); + 'CSV File Compilation Progress', $studentcount, + 'inline',undef,'Statistics','stats_status'); # # Open a file $filename = '/prtspool/'. @@ -1471,6 +1549,12 @@ END print $outputfile '"'.&Apache::loncommon::csv_translate($description).'",'. '"'.&Apache::loncommon::csv_translate(scalar(localtime(time))).'"'. "\n"; + foreach my $item ('shortdesc','non_html_notes') { + next if (! exists($chosen_output->{$item})); + print $outputfile + '"'.&Apache::loncommon::csv_translate($chosen_output->{$item}).'"'. + "\n"; + } # # Print out the headings my $sequence_row = ''; @@ -1517,6 +1601,10 @@ END $sequence_row.= '"",'; $resource_row.= '"Total",'; } + if ($chosen_output->{'grand_maximum'}) { + $sequence_row.= '"",'; + $resource_row.= '"Maximum",'; + } chomp($sequence_row); chomp($resource_row); print $outputfile $sequence_row."\n"; @@ -1533,7 +1621,12 @@ sub csv_outputstudent { # Output student fields my @to_show = &get_student_fields_to_show(); foreach my $field (@to_show) { - $Str .= '"'.&Apache::loncommon::csv_translate($student->{$field}).'",'; + my $value = $student->{$field}; + if ($field eq 'comments') { + $value = &Apache::lonmsg::retrieve_instructor_comments + ($student->{'username'},$student->{'domain'}); + } + $Str .= '"'.&Apache::loncommon::csv_translate($value).'",'; } # # Get student assessment data @@ -1548,6 +1641,7 @@ sub csv_outputstudent { # # Output performance data my $total = 0; + my $maximum = 0; foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) { my ($performance,$performance_length,$score,$seq_max,$rawdata); if ($chosen_output->{'tries'}){ @@ -1581,10 +1675,14 @@ sub csv_outputstudent { $Str .= '"'.$seq_max.'",'; } $total+=$score; + $maximum += $seq_max; } if ($chosen_output->{'grand_total'}) { $Str .= '"'.$total.'",'; } + if ($chosen_output->{'grand_maximum'}) { + $Str .= '"'.$maximum.'",'; + } chop($Str); $Str .= "\n"; print $outputfile $Str; @@ -1641,8 +1739,8 @@ Inputs: ####################################################### ####################################################### -sub StudentTriesOnSequence { - my ($student,$studentdata,$seq,$links) = @_; +sub student_tries_on_sequence { + my ($student,$studentdata,$navmap,$seq,$links) = @_; $links = 'no' if (! defined($links)); my $Str = ''; my ($sum,$max) = (0,0); @@ -1650,11 +1748,12 @@ sub StudentTriesOnSequence { my @TriesData = (); my $tries; my $hasdata = 0; # flag - true if the student has any data on the sequence - foreach my $resource (@{$seq->{'contents'}}) { - next if ($resource->{'type'} ne 'assessment'); - my $resource_data = $studentdata->{$resource->{'symb'}}; + my @resources = + $navmap->retrieveResources($seq,sub { shift->is_problem(); },0,0,0); + foreach my $resource (@resources) { + my $resource_data = $studentdata->{$resource->symb}; my $value = ''; - foreach my $partnum (@{$resource->{'parts'}}) { + foreach my $partnum (@{$resource->parts()}) { $tries = undef; $max++; $performance_length++; @@ -1715,9 +1814,15 @@ sub StudentTriesOnSequence { } # if (! defined($tries)) { - $tries = $symbol; + $tries = 0; + } + if ($status =~ /^(incorrect|ungraded)/) { + # Bug 3390: show '-' for tries on incorrect problems + # (csv & excel only) + push(@TriesData,-$tries); + } else { + push (@TriesData,$tries); } - push (@TriesData,$tries); # if ( ($links eq 'yes' && $symbol ne ' ') || ($links eq 'all')) { @@ -1725,7 +1830,7 @@ sub StudentTriesOnSequence { &Apache::lonnet::logthis('length of symbol "'.$symbol.'" > 1'); } $symbol = ''.$symbol.''; @@ -1734,8 +1839,8 @@ sub StudentTriesOnSequence { } $Str .= $value; } - if ($seq->{'randompick'}) { - $max = $seq->{'randompick'}; + if ($seq->randompick()) { + $max = $seq->randompick(); } if (! $hasdata && $sum == 0) { $sum = ' '; @@ -1768,8 +1873,8 @@ Inputs: ####################################################### ####################################################### -sub StudentPerformanceOnSequence { - my ($student,$studentdata,$seq,$links) = @_; +sub student_performance_on_sequence { + my ($student,$studentdata,$navmap,$seq,$links) = @_; $links = 'no' if (! defined($links)); my $Str = ''; # final result string my ($score,$max) = (0,0); @@ -1778,13 +1883,15 @@ sub StudentPerformanceOnSequence { my @ScoreData = (); my $partscore; my $hasdata = 0; # flag, 0 if there were no submissions on the sequence - foreach my $resource (@{$seq->{'contents'}}) { - next if ($resource->{'type'} ne 'assessment'); - my $resource_data = $studentdata->{$resource->{'symb'}}; - foreach my $part (@{$resource->{'parts'}}) { + my @resources = + $navmap->retrieveResources($seq,sub { shift->is_problem(); },0,0,0); + foreach my $resource (@resources) { + my $symb = $resource->symb; + my $resource_data = $studentdata->{$symb}; + foreach my $part (@{$resource->parts()}) { $partscore = undef; my $weight = &Apache::lonnet::EXT('resource.'.$part.'.weight', - $resource->{'symb'}, + $symb, $student->{'domain'}, $student->{'username'}, $student->{'section'}); @@ -1822,7 +1929,7 @@ sub StudentPerformanceOnSequence { $max -= $weight; # Do not count 'excused' problems. } $hasdata = 1; - } else { + } elsif (!exists($resource_data->{'resource.'.$part.'.awarded'})){ # Unsolved. Did they try? if (exists($resource_data->{'resource.'.$part.'.tries'})){ $symbol = '.'; @@ -1839,7 +1946,7 @@ sub StudentPerformanceOnSequence { # if ( ($links eq 'yes' && $symbol ne ' ') || ($links eq 'all')) { $symbol = ''.$symbol.'';