--- loncom/interface/statistics/lonstudentassessment.pm	2004/02/12 14:47:55	1.88
+++ 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.88 2004/02/12 14:47:55 matthew Exp $
+# $Id: lonstudentassessment.pm,v 1.97 2004/03/10 18:41:37 matthew Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -306,6 +306,7 @@ the chart page.
 #######################################################
 sub CreateInterface {
     my $Str = '';
+    $Str .= &Apache::lonhtmlcommon::breadcrumbs(undef,'Chart');
 #    $Str .= &CreateLegend();
     $Str .= '<table cellspacing="5">'."\n";
     $Str .= '<tr>';
@@ -401,7 +402,7 @@ my @OutputOptions =
    },
      { name  => 'CSV',
        value => 'csv',
-       description => 'Output a comma seperated values file suitable for '.
+       description => 'Output a comma separated values file suitable for '.
            'import into a spreadsheet program.  Using this method as opposed '.
            'to Excel output allows you to organize your data before importing'.
            ' it into a spreadsheet program.',
@@ -473,6 +474,8 @@ my @OutputDataOptions =
        sequence_sum => 1,
        sequence_max => 1,
        grand_total => 1,
+       summary_table => 1,
+       maximum_row => 1,
        shortdesc => 'Total Score and Maximum Possible for each '.
            'Sequence or Folder',
        longdesc => 'The score of each student as well as the '.
@@ -488,6 +491,8 @@ my @OutputDataOptions =
        sequence_sum => 1,
        sequence_max => 1,
        grand_total => 1,
+       summary_table => 1,
+       maximum_row => 1,
        shortdesc => 'Score on each Problem Part',
        longdesc =>'The students score on each problem part, computed as'.
            'the part weight * part awarded',
@@ -502,6 +507,8 @@ my @OutputDataOptions =
        sequence_sum => 0,
        sequence_max => 0,
        grand_total => 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.',
        },
@@ -515,6 +522,8 @@ my @OutputDataOptions =
        sequence_sum => 1,
        sequence_max => 1,
        grand_total => 1,
+       summary_table => 1,
+       maximum_row => 0,
        shortdesc => 'Number of Problem Parts completed successfully.',
        longdesc => 'The Number of Problem Parts completed successfully and '.
            'the maximum possible for each student',
@@ -616,12 +625,42 @@ sub html_initialize {
         my $width=$Apache::lonstatistics::StudentData{$field}->{'width'};
         $Str .= $title.' 'x($width-$base).$padding;
     }
-    # Now the selected sequences need to be listed
+    #
+    # Compute the column widths and output the sequence titles
     foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()){
-        my $title = $sequence->{'title'};
-        my $base  = $sequence->{'base_width'};
-        my $width = $sequence->{'width'};
-        $Str .= $title.' 'x($width-$base).$padding;
+        #
+        # 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</pre>\n";
     $Str .= "<pre>";
@@ -675,18 +714,26 @@ sub html_outputstudent {
                 &StudentPerformanceOnSequence($student,\%StudentsData,
                                               $seq,$show_links);
         }
-        my $ratio = sprintf("%3d",$score).'/'.sprintf("%3d",$seq_max);
-        #
-        if ($chosen_output->{'sequence_sum'}) {
-            $performance  = $ratio;
-            $performance .= ' 'x($seq->{'width'}-length($ratio));
+        my $ratio='';
+        if ($chosen_output->{'sequence_sum'} && $score ne ' ') {
+            $ratio .= sprintf("%3d",$score);
         } else {
-            # Pad with extra spaces
-            $performance .= ' 'x($seq->{'width'}-$performance_length-
-                                 length($ratio)
-                                 ).$ratio;
+            $ratio .= ' 'x3;
+        }
+        if ($chosen_output->{'sequence_max'}) {
+            if ($chosen_output->{'sequence_sum'}) {
+                $ratio .= '/';
+            }
+            $ratio .= sprintf("%3d",$seq_max);
         }
         #
+        if (! $chosen_output->{'every_problem'}) {
+            $performance = '';
+	    $performance_length=0;
+        }
+        $performance .= ' 'x($seq->{'width_total'}-$performance_length-$seq->{'width_sum'}).
+            $ratio;
+        #
         $Str .= $performance.$padding;
         #
         $studentstats->{$seq->{'symb'}}->{'score'}= $score;
@@ -694,15 +741,21 @@ 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'};
         if ($Statistics->{$symb}->{'max'} < $seq_stats->{'max'}) {
             $Statistics->{$symb}->{'max'} = $seq_stats->{'max'};
         }
-        $score += $seq_stats->{'score'};
+        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";
     #
@@ -717,10 +770,12 @@ sub html_finish {
     #
     # Check for suppressed output and close the progress window if so
     $r->print("</pre>\n"); 
-    if ($single_student_mode) {
-        $r->print(&SingleStudentTotal());
-    } else {
-        $r->print(&StudentAverageTotal());
+    if ($chosen_output->{'summary_table'}) {
+        if ($single_student_mode) {
+            $r->print(&SingleStudentTotal());
+        } else {
+            $r->print(&StudentAverageTotal());
+        }
     }
     $r->rflush();
     return;
@@ -728,37 +783,42 @@ sub html_finish {
 
 sub StudentAverageTotal {
     my $Str = "<h3>Summary Tables</h3>\n";
-    my $num_students = scalar(@Apache::lonstatistics::Students);
-    my $total_ave = 0;
-    my $total_max = 0;
+#    my $max_students;
+#    my $total_ave = 0;
+#    my $total_max = 0;
     $Str .= '<table border=2 cellspacing="1">'."\n";
     $Str .= "<tr><th>Title</th><th>Average</th><th>Maximum</th></tr>\n";
     foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {
         my $ave;
-        if ($num_students > $nodata_count) {
-            $ave = int(100*($Statistics->{$seq->{'symb'}}->{'score'}/
-                            ($num_students-$nodata_count)))/100;
+        my $num_students = $Statistics->{$seq->{'symb'}}->{'num_students'};
+#        if ($num_students > $max_students) {
+#            $max_students = $num_students;
+#        }
+        if ($num_students > 0) {
+            $ave = int(100*
+                       ($Statistics->{$seq->{'symb'}}->{'score'}/$num_students)
+                       )/100;
         } else {
             $ave = 0;
         }
-        $total_ave += $ave;
+#        $total_ave += $ave;
         my $max = $Statistics->{$seq->{'symb'}}->{'max'};
-        $total_max += $max;
+#        $total_max += $max;
         $ave = sprintf("%.2f",$ave);
         $Str .= '<tr><td>'.$seq->{'title'}.'</td>'.
             '<td align="right">'.$ave.'&nbsp;</td>'.
             '<td align="right">'.$max.'&nbsp;'.'</td></tr>'."\n";
     }
-    $total_ave = sprintf('%.2f',$total_ave); # only two digit
-    $Str .= "</table>\n";
-    $Str .= '<table border=2 cellspacing="1">'."\n";
-    $Str .= '<tr><th>Number of Students</th><th>Average</th>'.
-        "<th>Maximum</th></tr>\n";
-    $Str .= '<tr>'.
-        '<td align="right">'.($num_students-$nodata_count).'</td>'.
-        '<td align="right">'.$total_ave.'&nbsp;'.'</td>'.
-        '<td align="right">'.$total_max.'&nbsp;'.'</td>';
+#    $total_ave = sprintf('%.2f',$total_ave); # only two digit
     $Str .= "</table>\n";
+#    $Str .= '<table border=2 cellspacing="1">'."\n";
+#    $Str .= '<tr><th>Number of Students</th><th>Average</th>'.
+#        "<th>Maximum</th></tr>\n";
+#    $Str .= '<tr>'.
+#        '<td align="right">'.$max_students.'</td>'.
+#        '<td align="right">'.$total_ave.'&nbsp;'.'</td>'.
+#        '<td align="right">'.$total_max.'&nbsp;'.'</td>';
+#    $Str .= "</table>\n";
     return $Str;
 }
 
@@ -833,7 +893,8 @@ sub excel_initialize {
     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'};
@@ -887,6 +948,24 @@ END
     $rows_output = 0;
     $cols_output = 0;
     #
+    # 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);
     #
@@ -905,25 +984,15 @@ END
     # 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
     my $sheetname = $ENV{'course.'.$ENV{'request.course.id'}.'.description'};
     $sheetname = &Apache::loncommon::clean_excel_name($sheetname);
     $excel_sheet = $excel_workbook->addworksheet($sheetname);
     #
-    # Define some potentially useful formats
-    my $format;
-    $format->{'header'} = $excel_workbook->add_format(bold      => 1, 
-                                                      bottom    => 1,
-                                                      align     => 'center');
-    $format->{'bold'} = $excel_workbook->add_format(bold=>1);
-    $format->{'h1'}   = $excel_workbook->add_format(bold=>1, size=>18);
-    $format->{'h2'}   = $excel_workbook->add_format(bold=>1, size=>16);
-    $format->{'h3'}   = $excel_workbook->add_format(bold=>1, size=>14);
-    $format->{'date'} = $excel_workbook->add_format(num_format=>
-                                                    'mmm d yyyy hh:mm AM/PM');
-    #
     # Put the course description in the header
-    $excel_sheet->write($rows_output,$cols_output++,
+    $excel_sheet->write($header_row,$cols_output++,
                    $ENV{'course.'.$ENV{'request.course.id'}.'.description'},
                         $format->{'h1'});
     $cols_output += 3;
@@ -945,24 +1014,21 @@ END
             $sectionstring = "Section ".$Sections[0];
         }
     }
-    $excel_sheet->write($rows_output,$cols_output++,$sectionstring,
+    $excel_sheet->write($header_row,$cols_output++,$sectionstring,
                         $format->{'h3'});
     $cols_output += scalar(@Sections);
     #
     # Put the date in there too
-    $excel_sheet->write($rows_output++,$cols_output++,
+    $excel_sheet->write($header_row,$cols_output++,
                         'Compiled on '.localtime(time),$format->{'h3'});
     #
     $cols_output = 0;
-    $excel_sheet->write($rows_output++,$cols_output++,
+    $excel_sheet->write($description_row,$cols_output++,
                         $chosen_output->{'shortdesc'},
                         $format->{'h3'});
-    #
-    # Figure out the rows we need
-    my $sequence_name_row = $rows_output+1;
-    my $resource_name_row = $sequence_name_row+1;
-    my $maximum_data_row = $sequence_name_row+2;
-    my $first_data_row = $sequence_name_row+3;
+    ##############################################
+    # Output headings for the raw data
+    ##############################################
     #
     # Add the student headers
     $cols_output = 0;
@@ -975,11 +1041,11 @@ END
     my $total_formula_string = '=0';
     foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {
         $excel_sheet->write($sequence_name_row,,
-                            $cols_output,$seq->{'title'},$format->{'h3'});
+                            $cols_output,$seq->{'title'},$format->{'bold'});
         # Determine starting cell
         $seq->{'Excel:startcell'}=
             &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell
-            ($maximum_data_row,$cols_output);
+            ($first_data_row,$cols_output);
         $seq->{'Excel:startcol'}=$cols_output;
         my $count = 0;
         if ($chosen_output->{'every_problem'}) {
@@ -1013,7 +1079,7 @@ END
         } else {
             $seq->{'Excel:endcell'} = 
                 &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell
-                ($maximum_data_row,$cols_output-1);
+                ($first_data_row,$cols_output-1);
             $seq->{'Excel:endcol'} = $cols_output-1;
         }
         # Create the formula for summing up this sequence
@@ -1027,7 +1093,7 @@ END
         # Determine cell the score is held in
         $seq->{'Excel:scorecell'} = 
             &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell
-            ($maximum_data_row,$cols_output);
+            ($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++,
@@ -1048,7 +1114,7 @@ END
         #
         $total_formula_string.='+'.
             &Spreadsheet::WriteExcel::Utility::xl_rowcol_to_cell
-            ($maximum_data_row,$cols_output-1);
+            ($first_data_row,$cols_output-1);
         if ($chosen_output->{'sequence_max'}) {
             $excel_sheet->write($resource_name_row,$cols_output++,
                                 'maximum',
@@ -1060,9 +1126,10 @@ END
                             $format->{'bold'});
     }
     $total_formula = $excel_sheet->store_formula($total_formula_string);
-    #
+    ##############################################
     # Output a row for MAX, if appropriate
-    if ($chosen_output->{'scores'}) {
+    ##############################################
+    if ($chosen_output->{'maximum_row'}) {
         $cols_output = 0;
         foreach my $field (&get_student_fields_to_show()) {
             if ($field eq 'username' || $field eq 'fullname' || 
@@ -1129,8 +1196,57 @@ END
                                          $total_formula,undef,
                                          %total_cell_translation);
         }
-    } # End of MAXIMUM row output  if ($chosen_output->{'scores'}) {
+    } # 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); 
@@ -1208,7 +1324,12 @@ sub excel_outputstudent {
                 }
             } else {
                 foreach my $value (@$rawdata) {
-                    $excel_sheet->write($rows_output,$cols_output++,$value);
+                    if ($score eq ' ' || !defined($value)) {
+                        $cols_output++;
+                    } else {                        
+                        $excel_sheet->write($rows_output,$cols_output++,
+                                            $value);
+                    }
                 }
             }
         }
@@ -1233,7 +1354,11 @@ sub excel_outputstudent {
                                              %replaceCells);
             }
         } elsif ($chosen_output->{'sequence_sum'}) {
-            $excel_sheet->write($rows_output,$cols_output++,$score);
+            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);
@@ -1483,7 +1608,7 @@ sub csv_finish {
     #
     # Tell the user where to get their csv file
     $r->print('<br />'.
-              '<a href="'.$filename.'">Your csv file.</a>'."\n");
+              '<a href="'.$filename.'">'.&mt('Your csv file.').'</a>'."\n");
     $r->rflush();
     return;
     
@@ -1524,6 +1649,7 @@ sub StudentTriesOnSequence {
     my $performance_length = 0;
     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'}};
@@ -1548,6 +1674,7 @@ sub StudentTriesOnSequence {
             my $tries = 0;
             if(exists($resource_data->{'resource.'.$partnum.'.tries'})) {
                 $tries = $resource_data->{'resource.'.$partnum.'.tries'};
+                $hasdata =1;
             }
             #
             if ($awarded > 0) {
@@ -1610,6 +1737,9 @@ sub StudentTriesOnSequence {
     if ($seq->{'randompick'}) {
         $max = $seq->{'randompick'};
     }
+    if (! $hasdata && $sum == 0) {
+        $sum = ' ';
+    }
     return ($Str,$performance_length,$sum,$max,\@TriesData);
 }
 
@@ -1647,6 +1777,7 @@ sub StudentPerformanceOnSequence {
     my $symbol;
     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'}};
@@ -1665,13 +1796,17 @@ sub StudentPerformanceOnSequence {
             $performance_length++; # one character per part
             $symbol = ' '; # default to space
             #
-            my $awarded = 0;
+            my $awarded;
             if (exists($resource_data->{'resource.'.$part.'.awarded'})) {
                 $awarded = $resource_data->{'resource.'.$part.'.awarded'};
                 $awarded = 0 if (! $awarded);
+                $hasdata = 1;
             }
             #
             $partscore = $weight*$awarded;
+            if (! defined($awarded)) {
+                $partscore = undef;
+            }
             $score += $partscore;
             $symbol = $partscore; 
             if (abs($symbol - sprintf("%.0f",$symbol)) < 0.001) {
@@ -1686,10 +1821,12 @@ sub StudentPerformanceOnSequence {
                     $symbol = 'x';
                     $max -= $weight; # Do not count 'excused' problems.
                 }
+                $hasdata = 1;
             } else {
                 # Unsolved.  Did they try?
                 if (exists($resource_data->{'resource.'.$part.'.tries'})){
                     $symbol = '.';
+                    $hasdata = 1;
                 } else {
                     $symbol = ' ';
                 }
@@ -1710,6 +1847,9 @@ sub StudentPerformanceOnSequence {
             $Str .= $symbol;
         }
     }
+    if (! $hasdata && $score == 0) {
+        $score = ' ';
+    }
     return ($Str,$performance_length,$score,$max,\@ScoreData);
 }