Diff for /loncom/interface/statistics/lonstudentassessment.pm between versions 1.48 and 1.90

version 1.48, 2003/05/21 14:43:01 version 1.90, 2004/02/12 21:15:47
Line 52  package Apache::lonstudentassessment; Line 52  package Apache::lonstudentassessment;
 use strict;  use strict;
 use Apache::lonstatistics;  use Apache::lonstatistics;
 use Apache::lonhtmlcommon;  use Apache::lonhtmlcommon;
   use Apache::loncommon();
 use Apache::loncoursedata;  use Apache::loncoursedata;
 use Apache::lonnet; # for logging porpoises  use Apache::lonnet; # for logging porpoises
   use Apache::lonlocal;
 use Spreadsheet::WriteExcel;  use Spreadsheet::WriteExcel;
   use Spreadsheet::WriteExcel::Utility();
   
 #######################################################  #######################################################
 #######################################################  #######################################################
Line 85  my $Statistics; Line 88  my $Statistics;
   
 =item $show 'all', 'totals', or 'scores' determines how much data is output  =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  =cut
   
 #######################################################  #######################################################
 #######################################################  #######################################################
 my $show_links;  my $show_links;
 my $output_mode;  my $output_mode;
 my $show;  my $chosen_output;
   my $single_student_mode;
   
 #######################################################  #######################################################
 #######################################################  #######################################################
Line 126  Inputs: Line 133  Inputs:
 #######################################################  #######################################################
 sub BuildStudentAssessmentPage {  sub BuildStudentAssessmentPage {
     my ($r,$c)=@_;      my ($r,$c)=@_;
       #
     undef($Statistics);      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      # Print out the HTML headers for the interface
     #    This also parses the output mode selector      #    This also parses the output mode selector
     #    This step must always be done.      #    This step must *always* be done.
     $r->print(&CreateInterface());      $r->print(&CreateInterface());
     $r->print('<input type="hidden" name="notfirstrun" value="true" />');      $r->print('<input type="hidden" name="notfirstrun" value="true" />');
       $r->print('<input type="hidden" name="sort" value="'.
                 $ENV{'form.sort'}.'" />');
     $r->rflush();      $r->rflush();
     if (! exists($ENV{'form.notfirstrun'})) {      #
         $r->print(<<ENDMSG);      if (! exists($ENV{'form.notfirstrun'}) && ! $single_student_mode) {
 <p>  
 <font size="+2">  
 Please make your selections in the boxes above and hit   
 the button marked &quot;Update&nbsp;Display&quot;.  
 </font>  
 </p>  
 ENDMSG  
 #        $r->print(&OutputDescriptions());  
         return;          return;
     }      }
     #      #
     #  
     my $initialize     = \&html_initialize;      my $initialize     = \&html_initialize;
     my $output_student = \&html_outputstudent;      my $output_student = \&html_outputstudent;
     my $finish         = \&html_finish;      my $finish         = \&html_finish;
Line 156  ENDMSG Line 179  ENDMSG
         $initialize     = \&excel_initialize;          $initialize     = \&excel_initialize;
         $output_student = \&excel_outputstudent;          $output_student = \&excel_outputstudent;
         $finish         = \&excel_finish;          $finish         = \&excel_finish;
 #    } elsif ($output_mode eq 'multi-sheet excel') {  
 #        $initialize     = \&multi_sheet_excel_initialize;  
 #        $output_student = \&multi_sheet_excel_outputstudent;  
 #        $finish         = \&multi_sheet_excel_finish;  
     } elsif ($output_mode eq 'csv') {      } elsif ($output_mode eq 'csv') {
         $initialize     = \&csv_initialize;          $initialize     = \&csv_initialize;
         $output_student = \&csv_outputstudent;          $output_student = \&csv_outputstudent;
Line 168  ENDMSG Line 187  ENDMSG
     #      #
     if($c->aborted()) {  return ; }      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      # Call the initialize routine selected above
     $initialize->($r);      $initialize->($r);
     foreach my $student (@Apache::lonstatistics::Students) {      foreach my $student (@Students) {
         if($c->aborted()) {           if($c->aborted()) { 
             $finish->($r);              $finish->($r);
             return ;               return ; 
Line 186  ENDMSG Line 225  ENDMSG
   
 #######################################################  #######################################################
 #######################################################  #######################################################
   sub next_and_previous_buttons {
       my $Str = '';
       $Str .= '<input type="hidden" name="SelectedStudent" value="'.
           $ENV{'form.SelectedStudent'}.'" />';
       #
       # Build the previous student link
       my $previous = &Apache::lonstatistics::previous_student();
       my $previousbutton = '';
       if (defined($previous)) {
           my $sname = $previous->{'username'}.':'.$previous->{'domain'};
           $previousbutton .= '<input type="button" value="'.
               'Previous Student ('.
               $previous->{'username'}.'@'.$previous->{'domain'}.')'.
               '" onclick="document.Statistics.SelectedStudent.value='.
               "'".$sname."'".';'.
               'document.Statistics.submit();" />';
       } else {
           $previousbutton .= '<input type="button" value="'.
               'Previous student (none)'.'" />';
       }
       #
       # Build the next student link
       my $next = &Apache::lonstatistics::next_student();
       my $nextbutton = '';
       if (defined($next)) {
           my $sname = $next->{'username'}.':'.$next->{'domain'};
           $nextbutton .= '<input type="button" value="'.
               'Next Student ('.
               $next->{'username'}.'@'.$next->{'domain'}.')'.
               '" onclick="document.Statistics.SelectedStudent.value='.
               "'".$sname."'".';'.
               'document.Statistics.submit();" />';
       } else {
           $nextbutton .= '<input type="button" value="'.
               'Next student (none)'.'" />';
       }
       #
       # Build the 'all students' button
       my $all = '';
       $all .= '<input type="button" value="All Students" '.
               '" onclick="document.Statistics.SelectedStudent.value='.
               "''".';'.'document.Statistics.submit();" />';
       $Str .= $previousbutton.('&nbsp;'x5).$all.('&nbsp;'x5).$nextbutton;
       return $Str;
   }
   
   #######################################################
   #######################################################
   
 sub get_student_fields_to_show {  sub get_student_fields_to_show {
     my @to_show = @Apache::lonstatistics::SelectedStudentData;      my @to_show = @Apache::lonstatistics::SelectedStudentData;
Line 222  sub CreateInterface { Line 309  sub CreateInterface {
 #    $Str .= &CreateLegend();  #    $Str .= &CreateLegend();
     $Str .= '<table cellspacing="5">'."\n";      $Str .= '<table cellspacing="5">'."\n";
     $Str .= '<tr>';      $Str .= '<tr>';
     $Str .= '<td align="center"><b>Sections</b></td>';      $Str .= '<td align="center"><b>'.&mt('Sections').'</b></td>';
     $Str .= '<td align="center"><b>Student Data</b></td>';      $Str .= '<td align="center"><b>'.&mt('Student Data</b>').'</td>';
     $Str .= '<td align="center"><b>Enrollment Status</b></td>';      $Str .= '<td align="center"><b>'.&mt('Enrollment Status').'</b></td>';
     $Str .= '<td align="center"><b>Sequences and Folders</b></td>';      $Str .= '<td align="center"><b>'.&mt('Sequences and Folders').'</b></td>';
     $Str .= '<td align="center"><b>Output Format</b></td>';      $Str .= '<td align="center"><b>'.&mt('Output Format').'</b>'.
           &Apache::loncommon::help_open_topic("Chart_Output_Formats").
           '</td>';
       $Str .= '<td align="center"><b>'.&mt('Output Data').'</b>'.
           &Apache::loncommon::help_open_topic("Chart_Output_Data").
           '</td>';
     $Str .= '</tr>'."\n";      $Str .= '</tr>'."\n";
     #      #
     $Str .= '<tr><td align="center">'."\n";      $Str .= '<tr><td align="center">'."\n";
Line 249  sub CreateInterface { Line 341  sub CreateInterface {
                                               $only_seq_with_assessments);                                                $only_seq_with_assessments);
     $Str .= '</td><td>'."\n";      $Str .= '</td><td>'."\n";
     $Str .= &CreateAndParseOutputSelector();      $Str .= &CreateAndParseOutputSelector();
       $Str .= '</td><td>'."\n";
       $Str .= &CreateAndParseOutputDataSelector();
     $Str .= '</td></tr>'."\n";      $Str .= '</td></tr>'."\n";
     $Str .= '</table>'."\n";      $Str .= '</table>'."\n";
       $Str .= '<input type="submit" name="Generate Chart" value="'.
           &mt('Generate Chart').'" />';
       $Str .= '&nbsp;'x5;
       $Str .= '<input type="submit" name="selectstudent" value="'.
           &mt('Select One Student').'" />';
       $Str .= '&nbsp;'x5;
       $Str .= '<input type="submit" name="ClearCache" value="'.
           &mt('Clear Caches').'" />';
       $Str .= '&nbsp;'x5;
       $Str .= '<br />';
     return $Str;      return $Str;
 }  }
   
Line 271  my @OutputOptions = Line 375  my @OutputOptions =
        description => 'Output HTML with each symbol linked to the problem '.         description => 'Output HTML with each symbol linked to the problem '.
    'which generated it.',     'which generated it.',
        mode => 'html',         mode => 'html',
        show => 'all',  
        show_links => 'yes',         show_links => 'yes',
        },         },
      { name  => 'HTML, with all links',       { name  => 'HTML, with all links',
Line 280  my @OutputOptions = Line 383  my @OutputOptions =
    'which generated it.  '.     'which generated it.  '.
            'This includes links for unattempted problems.',             'This includes links for unattempted problems.',
        mode => 'html',         mode => 'html',
        show => 'all',  
        show_links => 'all',         show_links => 'all',
        },         },
      { name  => 'HTML, without links',       { name  => 'HTML, without links',
Line 289  my @OutputOptions = Line 391  my @OutputOptions =
    ' web page is greatly reduced.  If your browser crashes on the '.     ' web page is greatly reduced.  If your browser crashes on the '.
    'full display, try this.',     'full display, try this.',
        mode => 'html',         mode => 'html',
        show => 'all',  
        show_links => 'no',         show_links => 'no',
            },             },
      { name  => 'HTML, scores only',       { name  => 'Excel',
        value => 'html, scores only',         value => 'excel',
        description => 'Output HTML, only showing the total number of correct'.         description => 'Output an Excel file (compatable with Excel 95).',
    ' problems (or problem parts) and not the maximum possible for '.  
    'each student',  
        mode => 'html',  
        show => 'scores',  
        show_links => 'no',  
            },  
      { name  => 'HTML, totals',  
        value => 'html, totals',  
        description => 'Output HTML, but only the summary statistics for each'.  
    ' sequence selected for each student.',  
        mode => 'html',  
        show => 'totals',  
        show_links => 'no',  
        },  
      { name  => 'HTML, summary table only',  
        value => 'html summary table only',  
        description => 'Output HTML, but only the final summary table for '.  
            'all students across all sequences.',  
        mode => 'html',  
        show => 'final table',  
        show_links => 'no',  
        },  
      { name  => 'Excel, scores only',  
        value => 'excel, scores only',  
        description => 'Output an Excel file (compatable with Excel 95), '.  
    'with a single column for each sequence showing the students '.  
    'score.',  
        mode => 'excel',  
        show => 'scores',  
        show_links => 'no',  
            },  
      { name  => 'Excel, totals',  
        value => 'excel, totals',  
        description => 'Output an Excel file (compatable with Excel 95), '.  
    'with two columns for each sequence, the students score on the '.  
    'sequence and the students maximum possible on the sequence',  
        mode => 'excel',         mode => 'excel',
        show => 'totals',  
        show_links => 'no',         show_links => 'no',
            },     },
 #     { name  => 'multi-sheet Excel',       { name  => 'CSV',
 #       value => 'multi-sheet excel',         value => 'csv',
 #       description => 'Output an Excel file (compatable with Excel 95), '.         description => 'Output a comma seperated values file suitable for '.
 #   'with a seperate worksheet for each sequence you have selected '.             'import into a spreadsheet program.  Using this method as opposed '.
 #           'the data for each problem part '.             'to Excel output allows you to organize your data before importing'.
 #           '(number of tries, status, points awarded) will be listed.',             ' it into a spreadsheet program.',
 #       mode => 'multi-sheet excel',  
 #       show => 'totals',  
 #       show_links => 'no',  
 #           },  
 #     { name  => 'multi-sheet Excel, by section',  
 #       value => 'multi-sheet excel, by section',  
 #       description => 'Output an Excel file (compatable with Excel 95), '.  
 #   'with a seperate worksheet for each sequence you have selected '.  
 #           'the data for each problem part '.  
 #           '(number of tries, status, points awarded) will be listed.  '.  
 #           'There will be one Excel workbook for each section selected.',  
 #       mode => 'multi-sheet excel',  
 #       show => 'by section',  
 #       show_links => 'no',  
 #           },  
      { name  => 'CSV, everything',  
        value => 'csv, everything',  
        description => '',  
        mode => 'csv',         mode => 'csv',
        show => 'all',  
        show_links => 'no',  
            },  
      { name  => 'CSV, scores only',  
        value => 'csv, scores only',  
        description => '',  
        mode => 'csv',  
        show => 'scores',  
        show_links => 'no',  
            },  
      { name  => 'CSV, totals',  
        value => 'csv, totals',  
        description => '',  
        mode => 'csv',  
        show => 'totals',  
        show_links => 'no',         show_links => 'no',
            },             },
      );       );
   
 sub OutputDescriptions {  sub OutputDescriptions {
     my $Str = '';      my $Str = '';
     $Str .= "<h2>Output Modes</h2>\n";      $Str .= "<h2>Output Formats</h2>\n";
     $Str .= "<dl>\n";      $Str .= "<dl>\n";
     foreach my $outputmode (@OutputOptions) {      foreach my $outputmode (@OutputOptions) {
  $Str .="    <dt>".$outputmode->{'name'}."</dt>\n";   $Str .="    <dt>".$outputmode->{'name'}."</dt>\n";
Line 394  sub OutputDescriptions { Line 425  sub OutputDescriptions {
 sub CreateAndParseOutputSelector {  sub CreateAndParseOutputSelector {
     my $Str = '';      my $Str = '';
     my $elementname = 'chartoutputmode';      my $elementname = 'chartoutputmode';
       &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
                                               [$elementname]);
     #      #
     # Format for output options is 'mode, restrictions';      # Format for output options is 'mode, restrictions';
     my $selected = 'html, with links';      my $selected = 'html, without links';
     if (exists($ENV{'form.'.$elementname})) {      if (exists($ENV{'form.'.$elementname})) {
         if (ref($ENV{'form.'.$elementname} eq 'ARRAY')) {          if (ref($ENV{'form.'.$elementname} eq 'ARRAY')) {
             $selected = $ENV{'form.'.$elementname}->[0];              $selected = $ENV{'form.'.$elementname}->[0];
Line 408  sub CreateAndParseOutputSelector { Line 441  sub CreateAndParseOutputSelector {
     # Set package variables describing output mode      # Set package variables describing output mode
     $show_links  = 'no';      $show_links  = 'no';
     $output_mode = 'html';      $output_mode = 'html';
     $show        = 'all';  
     foreach my $option (@OutputOptions) {      foreach my $option (@OutputOptions) {
         next if ($option->{'value'} ne $selected);          next if ($option->{'value'} ne $selected);
         $output_mode = $option->{'mode'};          $output_mode = $option->{'mode'};
         $show        = $option->{'show'};  
         $show_links  = $option->{'show_links'};          $show_links  = $option->{'show_links'};
     }      }
   
Line 422  sub CreateAndParseOutputSelector { Line 453  sub CreateAndParseOutputSelector {
     foreach my $option (@OutputOptions) {      foreach my $option (@OutputOptions) {
         $Str .= "\n".'    <option value="'.$option->{'value'}.'"';          $Str .= "\n".'    <option value="'.$option->{'value'}.'"';
         $Str .= " selected " if ($option->{'value'} eq $selected);          $Str .= " selected " if ($option->{'value'} eq $selected);
         $Str .= ">".$option->{'name'}."<\/option>";          $Str .= ">".&mt($option->{'name'})."<\/option>";
       }
       $Str .= "\n</select>";
       return $Str;
   }
   
   ##
   ## Data selector stuff
   ##
   my @OutputDataOptions =
       (
        { name  => 'Scores Summary',
          base  => 'scores',
          value => 'sum and total',
          scores => 1,
          tries  => 0,
          every_problem => 0,
          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 '.
              ' maximum possible on each Sequence or Folder.',
          },
        { name  => 'Scores Per Problem',
          base  => 'scores',
          value => 'scores',
          scores => 1,
          tries  => 0,
          correct => 0,
          every_problem => 1,
          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',
          },
        { name  =>'Tries',
          base  =>'tries',
          value => 'tries',
          scores => 0,
          tries  => 1,
          correct => 0,
          every_problem => 1,
          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.',
          },
        { name  =>'Parts Correct',
          base  =>'tries',
          value => 'parts correct total',
          scores => 0,
          tries  => 0,
          correct => 1,
          every_problem => 1,
          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',
          },
        );
   
   sub HTMLifyOutputDataDescriptions {
       my $Str = '';
       $Str .= "<h2>Output Data</h2>\n";
       $Str .= "<dl>\n";
       foreach my $option (@OutputDataOptions) {
           $Str .= '    <dt>'.$option->{'name'}.'</dt>';
           $Str .= '<dd>'.$option->{'longdesc'}.'</dd>'."\n";
       }
       $Str .= "</dl>\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/<select size="5" name="$elementname">/;
       foreach my $option (@OutputDataOptions) {
           $Str .= "\n".'    <option value="'.$option->{'value'}.'"';
           $Str .= " selected " if ($option->{'value'} eq $chosen_output->{'value'});
           $Str .= ">".&mt($option->{'name'})."<\/option>";
     }      }
     $Str .= "\n</select>";      $Str .= "\n</select>";
     return $Str;      return $Str;
   
 }  }
   
 #######################################################  #######################################################
Line 462  sub html_initialize { Line 607  sub html_initialize {
     $padding = ' 'x3;      $padding = ' 'x3;
     $count = 0;      $count = 0;
     $nodata_count = 0;      $nodata_count = 0;
       undef(%prog_state);
     #      #
     $r->print("<h3>".$ENV{'course.'.$ENV{'request.course.id'}.'.description'}.      $r->print("<h3>".$ENV{'course.'.$ENV{'request.course.id'}.'.description'}.
               "&nbsp;&nbsp;".localtime(time)."</h3>");                "&nbsp;&nbsp;".localtime(time)."</h3>");
   
     #      if ($chosen_output->{'base'} !~ /^final table/) {
     # Set up progress window for 'final table' display only          $r->print("<h3>".$chosen_output->{'shortdesc'}."</h3>");        
     if ($show eq 'final table') {  
         my $studentcount = scalar(@Apache::lonstatistics::Students);   
         %prog_state=&Apache::lonhtmlcommon::Create_PrgWin  
             ($r,'Summary Table Status',  
              'Summary Table Compilation Progress', $studentcount);  
     }      }
     my $Str = "<pre>\n";      my $Str = "<pre>\n";
     # First, the @StudentData fields need to be listed      # First, the @StudentData fields need to be listed
Line 483  sub html_initialize { Line 624  sub html_initialize {
         my $width=$Apache::lonstatistics::StudentData{$field}->{'width'};          my $width=$Apache::lonstatistics::StudentData{$field}->{'width'};
         $Str .= $title.' 'x($width-$base).$padding;          $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()){      foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()){
         my $title = $sequence->{'title'};          #
         my $base  = $sequence->{'base_width'};          # Comptue column widths
         my $width = $sequence->{'width'};          $sequence->{'width_sum'} = 0;
         $Str .= $title.' 'x($width-$base).$padding;          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($sequence->{'title'})) {
               $sequence->{'width_total'} = length($sequence->{'title'});
               $sequence->{'width_problem'} = 
                   $sequence->{'width_total'} - $sequence->{'width_sum'};
           }
           #
           # Output the sequence titles
           $Str .= 
               $sequence->{'title'}.' 'x($sequence->{'width_total'}-
                                         length($sequence->{'title'})
                                         ).$padding;
     }      }
     $Str .= "total (of shown problems)</pre>\n";      $Str .= "total</pre>\n";
     $Str .= "<pre>";      $Str .= "<pre>";
     #  
     # Check for suppression of output  
     if ($show eq 'final table') {  
         $Str = '';  
     }  
     $r->print($Str);      $r->print($Str);
     $r->rflush();      $r->rflush();
     return;      return;
Line 527  sub html_outputstudent { Line 694  sub html_outputstudent {
     }      }
     if (scalar(@tmp) < 1) {      if (scalar(@tmp) < 1) {
         $nodata_count++;          $nodata_count++;
         return if ($show eq 'final table');  
         $Str .= '<font color="blue">No Course Data</font>'."\n";          $Str .= '<font color="blue">No Course Data</font>'."\n";
         $r->print($Str);          $r->print($Str);
         $r->rflush();          $r->rflush();
Line 538  sub html_outputstudent { Line 704  sub html_outputstudent {
     my $studentstats;      my $studentstats;
     my $PerformanceStr = '';      my $PerformanceStr = '';
     foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {      foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {
         my ($performance,$score,$seq_max) =          my ($performance,$performance_length,$score,$seq_max,$rawdata);
             &StudentPerformanceOnSequence($student,\%StudentsData,          if ($chosen_output->{'tries'}) {
                                           $seq,$show_links);              ($performance,$performance_length,$score,$seq_max,$rawdata) =
         my $ratio = $score.'/'.$seq_max;                  &StudentTriesOnSequence($student,\%StudentsData,
         #                                          $seq,$show_links);
         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 {          } else {
             # Pad with extra spaces              ($performance,$performance_length,$score,$seq_max,$rawdata) =
             $performance .= ' 'x($seq->{'width'}-$seq_max-                  &StudentPerformanceOnSequence($student,\%StudentsData,
                                  length($ratio)                                                $seq,$show_links);
                                  ).$ratio;          }
           my $ratio='';
           if ($chosen_output->{'sequence_sum'}) {
               $ratio .= sprintf("%3d",$score);
           }
           if ($chosen_output->{'sequence_max'}) {
               if ($chosen_output->{'sequence_sum'}) {
                   $ratio .= '/';
               }
               $ratio .= sprintf("%3d",$seq_max);
           }
           #
           if (! $chosen_output->{'every_problem'}) {
               $performance = '';
         }          }
           $performance .= ' 'x($seq->{'width_problem'}-$performance_length).
               $ratio;
         #          #
         $Str .= $performance.$padding;          $Str .= $performance.$padding;
         #          #
Line 566  sub html_outputstudent { Line 741  sub html_outputstudent {
     my ($score,$max) = (0,0);      my ($score,$max) = (0,0);
     while (my ($symb,$seq_stats) = each (%{$studentstats})) {      while (my ($symb,$seq_stats) = each (%{$studentstats})) {
         $Statistics->{$symb}->{'score'} += $seq_stats->{'score'};          $Statistics->{$symb}->{'score'} += $seq_stats->{'score'};
         $Statistics->{$symb}->{'max'}   += $seq_stats->{'max'};          if ($Statistics->{$symb}->{'max'} < $seq_stats->{'max'}) {
               $Statistics->{$symb}->{'max'} = $seq_stats->{'max'};
           }
         $score += $seq_stats->{'score'};          $score += $seq_stats->{'score'};
         $max   += $seq_stats->{'max'};          $max   += $seq_stats->{'max'};
     }      }
     $Str .= ' '.' 'x(length($max)-length($score)).$score.'/'.$max;      $Str .= ' '.' 'x(length($max)-length($score)).$score.'/'.$max;
     $Str .= " \n";      $Str .= " \n";
     #      #
     # Check for suppressed output and update the progress window if so...  
     if ($show eq 'final table') {  
         $Str = '';  
         &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,  
                                                  'last student');  
     }  
     #  
     $r->print($Str);      $r->print($Str);
     #      #
     $r->rflush();      $r->rflush();
Line 590  sub html_finish { Line 760  sub html_finish {
     my ($r) = @_;      my ($r) = @_;
     #      #
     # Check for suppressed output and close the progress window if so      # Check for suppressed output and close the progress window if so
     if ($show eq 'final table') {      $r->print("</pre>\n"); 
         &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);      if ($chosen_output->{'summary_table'}) {
     } else {          if ($single_student_mode) {
         $r->print("</pre>\n");               $r->print(&SingleStudentTotal());
           } else {
               $r->print(&StudentAverageTotal());
           }
     }      }
     $r->print(&StudentAverageTotal());  
     $r->rflush();      $r->rflush();
     return;      return;
 }  }
Line 616  sub StudentAverageTotal { Line 788  sub StudentAverageTotal {
             $ave = 0;              $ave = 0;
         }          }
         $total_ave += $ave;          $total_ave += $ave;
         my $max = $seq->{'num_assess_parts'};          my $max = $Statistics->{$seq->{'symb'}}->{'max'};
         $total_max += $max;          $total_max += $max;
         if ($ave == 0) {          $ave = sprintf("%.2f",$ave);
             $ave = "0.00";  
         }  
         $ave .= '&nbsp;';  
         $max .= '&nbsp;&nbsp;&nbsp;';  
         $Str .= '<tr><td>'.$seq->{'title'}.'</td>'.          $Str .= '<tr><td>'.$seq->{'title'}.'</td>'.
             '<td align="right">'.$ave.'</td>'.              '<td align="right">'.$ave.'&nbsp;</td>'.
                 '<td align="right">'.$max.'</td></tr>'."\n";              '<td align="right">'.$max.'&nbsp;'.'</td></tr>'."\n";
     }      }
     $total_ave = int(100*$total_ave)/100; # only two digit      $total_ave = sprintf('%.2f',$total_ave); # only two digit
     $Str .= "</table>\n";      $Str .= "</table>\n";
     $Str .= '<table border=2 cellspacing="1">'."\n";      $Str .= '<table border=2 cellspacing="1">'."\n";
     $Str .= '<tr><th>Number of Students</th><th>Average</th>'.      $Str .= '<tr><th>Number of Students</th><th>Average</th>'.
         "<th>Maximum</th></tr>\n";          "<th>Maximum</th></tr>\n";
     $Str .= '<tr><td>'.($num_students-$nodata_count).'</td>'.      $Str .= '<tr>'.
         '<td>'.$total_ave.'</td><td>'.$total_max.'</td>';          '<td align="right">'.($num_students-$nodata_count).'</td>'.
           '<td align="right">'.$total_ave.'&nbsp;'.'</td>'.
           '<td align="right">'.$total_max.'&nbsp;'.'</td>';
     $Str .= "</table>\n";      $Str .= "</table>\n";
     return $Str;      return $Str;
 }  }
   
 }  sub SingleStudentTotal {
       my $student = &Apache::lonstatistics::current_student();
 #######################################################      my $Str = "<h3>Summary table for ".$student->{'fullname'}." ".
 #######################################################          $student->{'username'}.'@'.$student->{'domain'}."</h3>\n";
       $Str .= '<table border=2 cellspacing="1">'."\n";
 =pod      $Str .= 
           "<tr><th>Sequence or Folder</th><th>Score</th><th>Maximum</th></tr>\n";
 =head2 Multi-Sheet EXCEL subroutines      my $total = 0;
       my $total_max = 0;
 =item &multi_sheet_excel_initialize($r)  
   
 =item &multi_sheet_excel_outputstudent($r,$student)  
   
 =item &multi_sheet_excel_finish($r)  
   
 =cut  
   
 #######################################################  
 #######################################################  
 {  
   
 sub multi_sheet_excel_initialize {  
     my ($r)=@_;  
     $r->print("<h1>Not yet implemented</h1>");  
     #   
     # Estimate the size of the file.  We would like to have < 5 megs of data.  
     my $max_size = 5000000;  
     my $num_students  = scalar(@Apache::lonstatistics::Students);  
     my $num_sequences = 0;  
     my $num_data_per_part  = 2; # 'status' and 'numtries'  
     my $fields_per_student = scalar(&get_student_fields_to_show());  
     my $bytes_per_field    = 20; # Back of the envelope calculation  
     foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {      foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {
         $num_sequences++ if ($seq->{'num_assess'} > 0);          my $value = $Statistics->{$seq->{'symb'}}->{'score'};
         $fields_per_student += $num_data_per_part * $seq->{'num_assess_parts'};          my $max = $Statistics->{$seq->{'symb'}}->{'max'};
     }          $Str .= '<tr><td>'.$seq->{'title'}.'</td>'.
     my $size_estimate = $fields_per_student*$num_students*$bytes_per_field;              '<td align="right">'.$value.'</td>'.
     #                  '<td align="right">'.$max.'</td></tr>'."\n";
     # Compute number of workbooks          $total += $value;
     my $num_workbooks = 1;          $total_max +=$max;
     if ($size_estimate > $max_size) { # try to stay under 5 megs  
         $num_workbooks += int($size_estimate / $max_size);  
     }  
     if ($show eq 'by section') {  
         if (@Apache::lonstatistics::SelectedSections > 1 &&   
             $Apache::lonstatistics::SelectedSections[0] ne 'all') {  
             $num_workbooks = scalar(@Apache::lonstatistics::SelectedSections);  
         } else {  
             # @Apache::lonstatistics::Sections contains 'all' as well.  
             $num_workbooks = scalar(@Apache::lonstatistics::Sections) - 1;  
         }  
     }      }
           $Str .= '<tr><td><b>Total</b></td>'.
     $r->print("Maximum allowed size: ".$max_size." bytes<br />");          '<td align="right">'.$total.'</td>'.
     $r->print("Number of students: ".$num_students."<br />");          '<td align="right">'.$total_max."</td></tr>\n";
     $r->print("Number of fields per student: ".$fields_per_student."<br />");      $Str .= "</table>\n";
     $r->print("Total number of fields: ".($fields_per_student*$num_students).      return $Str;
               "<br />");  
     $r->print("Bytes per field: ".$bytes_per_field." (estimated)"."<br />");  
     $r->print("Estimated size: ".$size_estimate." bytes<br />");  
     $r->print("Number of workbooks: ".$num_workbooks."<br />");  
     $r->rflush();  
     return;  
 }  
   
 sub multi_sheet_excel_outputstudent {  
     my ($r,$student) = @_;  
 }  }
   
 sub multi_sheet_excel_finish {  
     my ($r) = @_;  
 }  }
   
 }  
 #######################################################  #######################################################
 #######################################################  #######################################################
   
Line 739  my $rows_output; Line 862  my $rows_output;
 my $cols_output;  my $cols_output;
   
 my %prog_state; # progress window state  my %prog_state; # progress window state
   my $request_aborted;
   
   my $total_formula;
   
 sub excel_initialize {  sub excel_initialize {
     my ($r) = @_;      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(<<END);
   <h2>Unable to Complete Request</h2>
   <p>
   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.
   </p><p>
   You may consider reducing the number of <b>Sequences or Folders</b> you
   have selected.  
   </p><p>
   LON-CAPA can produce <b>CSV</b> files of this data or Excel files of the
   summary data (<b>Parts Correct</b> or <b>Parts Correct & Totals</b>).
   </p>
   END
          $request_aborted = 1;
       }
       if ($chosen_output->{'base'} eq 'scores' && $total_columns > 255) {
           $r->print(<<END);
   <h2>Unable to Complete Request</h2>
   <p>
   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.
   </p><p>
   You may consider reducing the number of <b>Sequences or Folders</b> you
   have selected.  
   </p><p>
   LON-CAPA can produce <b>CSV</b> files of this data or Excel files of the
   <b>Scores Summary</b> data.
   </p>
   END
          $request_aborted = 1;
       }
       return if ($request_aborted);
       #
     $filename = '/prtspool/'.      $filename = '/prtspool/'.
         $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'.          $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'.
             time.'_'.rand(1000000000).'.xls';              time.'_'.rand(1000000000).'.xls';
Line 753  sub excel_initialize { Line 934  sub excel_initialize {
     $rows_output = 0;      $rows_output = 0;
     $cols_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      # Create sheet
     $excel_workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);      $excel_workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
     #      #
Line 773  sub excel_initialize { Line 972  sub excel_initialize {
     #      #
     # Add a worksheet      # Add a worksheet
     my $sheetname = $ENV{'course.'.$ENV{'request.course.id'}.'.description'};      my $sheetname = $ENV{'course.'.$ENV{'request.course.id'}.'.description'};
     if (length($sheetname) > 31) {      $sheetname = &Apache::loncommon::clean_excel_name($sheetname);
         $sheetname = substr($sheetname,0,31);  
     }  
     $excel_sheet = $excel_workbook->addworksheet($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      # 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'});                     $ENV{'course.'.$ENV{'request.course.id'}.'.description'},
                           $format->{'h1'});
     $cols_output += 3;      $cols_output += 3;
     #      #
     # Put a description of the sections listed      # Put a description of the sections listed
Line 800  sub excel_initialize { Line 1010  sub excel_initialize {
             $sectionstring = "Section ".$Sections[0];              $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);      $cols_output += scalar(@Sections);
     #      #
     # Put the date in there too      # Put the date in there too
     $excel_sheet->write($rows_output,$cols_output++,      $excel_sheet->write($header_row,$cols_output++,
                         'Compiled on '.localtime(time));                          'Compiled on '.localtime(time),$format->{'h3'});
     #      #
     $rows_output++;      $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      # Add the student headers
     $cols_output = 0;      $cols_output = 0;
     foreach my $field (&get_student_fields_to_show()) {      foreach my $field (&get_student_fields_to_show()) {
         $excel_sheet->write($rows_output,$cols_output++,$field);          $excel_sheet->write($resource_name_row,$cols_output++,$field,
                               $format->{'bold'});
     }      }
     #      #
     # Add the Sequence Headers      # Add the remaining column headers
       my $total_formula_string = '=0';
     foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {      foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {
         $excel_sheet->write($rows_output,$cols_output,$seq->{'title'});          $excel_sheet->write($sequence_name_row,,
         if ($show eq 'totals') {                              $cols_output,$seq->{'title'},$format->{'bold'});
             $excel_sheet->write($rows_output+1,$cols_output,'score');          # Determine starting cell
             $excel_sheet->write($rows_output+1,$cols_output+1,'maximum');          $seq->{'Excel:startcell'}=
             $cols_output += 2;              &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 {          } 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'});
         }          }
     }      }
     #      if ($chosen_output->{'grand_total'}) {
     # Bookkeeping          $excel_sheet->write($resource_name_row,$cols_output++,'Total',
     if ($show eq 'totals') {                              $format->{'bold'});
         $rows_output += 2;      }
     } else {      $total_formula = $excel_sheet->store_formula($total_formula_string);
         $rows_output += 1;      ##############################################
     }      # Output a row for MAX, if appropriate
     #      ##############################################
     # Output a row for MAX      if ($chosen_output->{'maximum_row'}) {
     if ($show ne 'totals') {  
         $cols_output = 0;          $cols_output = 0;
         foreach my $field (&get_student_fields_to_show()) {          foreach my $field (&get_student_fields_to_show()) {
             if ($field eq 'username' || $field eq 'fullname' ||               if ($field eq 'username' || $field eq 'fullname' || 
                 $field eq 'id') {                  $field eq 'id') {
                 $excel_sheet->write($rows_output,$cols_output++,'Maximum');                  $excel_sheet->write($maximum_data_row,$cols_output++,'Maximum',
                                       $format->{'bold'});
             } else {              } else {
                 $excel_sheet->write($rows_output,$cols_output++,'');                  $excel_sheet->write($maximum_data_row,$cols_output++,'');
             }              }
         }          }
         #          #
         # Add the Sequence Headers          # Add the maximums for each sequence or assessment
           my %total_cell_translation;
         foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {          foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {
             $excel_sheet->write($rows_output,$cols_output++,              $cols_output=$seq->{'Excel:startcol'};
                                 $seq->{'num_assess_parts'});              $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++;
         }          }
         $rows_output++;  
     }      }
       ##############################################
       #   Take care of non-excel initialization
       ##############################################
     #      #
     # Let the user know what we are doing      # Let the user know what we are doing
     my $studentcount = scalar(@Apache::lonstatistics::Students);       my $studentcount = scalar(@Apache::lonstatistics::Students); 
     $r->print("<h1>Compiling Excel spreadsheet for ".      if ($ENV{'form.SelectedStudent'}) {
               $studentcount.' student');          $studentcount = '1';
     $r->print('s') if ($studentcount > 1);      }
     $r->print("</h1>\n");      if ($studentcount > 1) {
           $r->print('<h1>'.&mt('Compiling Excel spreadsheet for [_1] students',
                                $studentcount)."</h1>\n");
       } else {
           $r->print('<h1>'.
                     &mt('Compiling Excel spreadsheet for 1 student').
                     "</h1>\n");
       }
     $r->rflush();      $r->rflush();
     #      #
     # Initialize progress window      # Initialize progress window
Line 867  sub excel_initialize { Line 1264  sub excel_initialize {
         ($r,'Excel File Compilation Status',          ($r,'Excel File Compilation Status',
          'Excel File Compilation Progress', $studentcount);           'Excel File Compilation Progress', $studentcount);
     #      #
       &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,
                                             'Processing first student');
     return;      return;
 }  }
   
 sub excel_outputstudent {  sub excel_outputstudent {
     my ($r,$student) = @_;      my ($r,$student) = @_;
       return if ($request_aborted);
     return if (! defined($excel_sheet));      return if (! defined($excel_sheet));
     $cols_output=0;      $cols_output=0;
     #      #
Line 892  sub excel_outputstudent { Line 1292  sub excel_outputstudent {
     }      }
     #      #
     # Write out sequence scores and totals data      # Write out sequence scores and totals data
       my %total_cell_translation;
     foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {      foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {
         my ($performance,$score,$seq_max) =          $cols_output = $seq->{'Excel:startcol'};
             &StudentPerformanceOnSequence($student,\%StudentsData,          # Keep track of cells to translate in total cell
                                           $seq,'no');          $total_cell_translation{$seq->{'Excel:scorecell'}} = 
         if ($show eq 'totals' || $show eq 'scores') {              &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) {
                       $excel_sheet->write($rows_output,$cols_output++,$value);
                   }
               }
           }
           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'}) {
             $excel_sheet->write($rows_output,$cols_output++,$score);              $excel_sheet->write($rows_output,$cols_output++,$score);
         }          }
         if ($show eq 'totals') {          if ($chosen_output->{'sequence_max'}) {
             $excel_sheet->write($rows_output,$cols_output++,$seq_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      # Bookkeeping
     $rows_output++;       $rows_output++; 
     $cols_output=0;      $cols_output=0;
Line 915  sub excel_outputstudent { Line 1369  sub excel_outputstudent {
   
 sub excel_finish {  sub excel_finish {
     my ($r) = @_;      my ($r) = @_;
       return if ($request_aborted);
     return if (! defined($excel_sheet));      return if (! defined($excel_sheet));
     #      #
     # Write the excel file      # Write the excel file
Line 955  sub excel_finish { Line 1410  sub excel_finish {
   
 my $outputfile;  my $outputfile;
 my $filename;  my $filename;
   my $request_aborted;
 my %prog_state; # progress window state  my %prog_state; # progress window state
   
 sub csv_initialize{  sub csv_initialize{
     my ($r) = @_;      my ($r) = @_;
     #       # 
     # Clean up      # Clean up
     $filename = undef;      undef($outputfile);
     $outputfile = undef;      undef($filename);
       undef($request_aborted);
     undef(%prog_state);      undef(%prog_state);
     #      #
       # Deal with unimplemented requests
       $request_aborted = undef;
       if ($chosen_output->{'base'} =~ /final table/) {
           $r->print(<<END);
   <h2>Unable to Complete Request</h2>
   <p>
   The <b>Summary Table (Scores)</b> option is not available for non-HTML output.
   </p>
   END
          $request_aborted = 1;
       }
       return if ($request_aborted);
       #
       # Initialize progress window
       my $studentcount = scalar(@Apache::lonstatistics::Students);
       %prog_state=&Apache::lonhtmlcommon::Create_PrgWin
           ($r,'CSV File Compilation Status',
            'CSV File Compilation Progress', $studentcount);
       #
     # Open a file      # Open a file
     $filename = '/prtspool/'.      $filename = '/prtspool/'.
         $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'.          $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'.
Line 983  sub csv_initialize{ Line 1458  sub csv_initialize{
     print $outputfile '"'.&Apache::loncommon::csv_translate($description).'",'.      print $outputfile '"'.&Apache::loncommon::csv_translate($description).'",'.
         '"'.&Apache::loncommon::csv_translate(scalar(localtime(time))).'"'.          '"'.&Apache::loncommon::csv_translate(scalar(localtime(time))).'"'.
             "\n";              "\n";
   
     #      #
     # Print out the headings      # Print out the headings
     my $Str = '';      my $sequence_row = '';
     my $Str2 = undef;      my $resource_row = undef;
     foreach my $field (&get_student_fields_to_show()) {      foreach my $field (&get_student_fields_to_show()) {
         if ($show eq 'scores') {          $sequence_row .='"",';
             $Str .= '"'.&Apache::loncommon::csv_translate($field).'",';          $resource_row .= '"'.&Apache::loncommon::csv_translate($field).'",';
         } elsif ($show eq 'totals') {  
             $Str .= '"",'; # first row empty on the student fields  
             $Str2 .= '"'.&Apache::loncommon::csv_translate($field).'",';  
         } elsif ($show eq 'all') {  
             $Str .= '"'.&Apache::loncommon::csv_translate($field).'",';  
         }  
     }      }
     foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {      foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {
         if ($show eq 'scores') {          $sequence_row .= '"'.
             $Str .= '"'.&Apache::loncommon::csv_translate($seq->{'title'}).              &Apache::loncommon::csv_translate($seq->{'title'}).'",';
                 '",';          my $count = 0;
         } elsif ($show eq 'totals') {          if ($chosen_output->{'every_problem'}) {
             $Str  .= '"'.&Apache::loncommon::csv_translate($seq->{'title'}).              foreach my $res (@{$seq->{'contents'}}) {
                 '","",';                  if ($res->{'type'} ne 'assessment'  || 
             $Str2 .= '"score","total possible",';                      ! exists($res->{'parts'})       ||
         } elsif ($show eq 'all') {                      ref($res->{'parts'}) ne 'ARRAY' ||
             $Str  .= '"'.&Apache::loncommon::csv_translate($seq->{'title'}).                      scalar(@{$res->{'parts'}}) < 1) {
                 '",';                      next;
             $Str .= '"",'x($seq->{'num_assess_parts'}-1);                  }
             $Str .= '"score","total possible",';                  foreach my $part (@{$res->{'parts'}}) {
                       $resource_row .= '"'.
                           &Apache::loncommon::csv_translate($res->{'title'}.
                                                             ', Part '.$part
                                                             ).'",';
                       $count++;
                   }
               }
           }
           $sequence_row.='"",'x$count;
           if ($chosen_output->{'sequence_sum'}) {
               if($chosen_output->{'correct'}) {
                   $resource_row .= '"sum",';
               } else {
                   $resource_row .= '"score",';
               }
           }
           if ($chosen_output->{'sequence_max'}) {
               $sequence_row.= '"",';
               $resource_row .= '"maximum possible",';
         }          }
     }      }
     chop($Str);      if ($chosen_output->{'grand_total'}) {
     $Str .= "\n";          $sequence_row.= '"",';
     print $outputfile $Str;          $resource_row.= '"Total",';
     if (defined($Str2)) {      } 
         chop($Str2);      chomp($sequence_row);
         $Str2 .= "\n";      chomp($resource_row);
         print $outputfile $Str2;      print $outputfile $sequence_row."\n";
     }      print $outputfile $resource_row."\n";
     #  
     # Initialize progress window  
     my $studentcount = scalar(@Apache::lonstatistics::Students);  
     %prog_state=&Apache::lonhtmlcommon::Create_PrgWin  
         ($r,'CSV File Compilation Status',  
          'CSV File Compilation Progress', $studentcount);  
     return;      return;
 }  }
   
 sub csv_outputstudent {  sub csv_outputstudent {
     my ($r,$student) = @_;      my ($r,$student) = @_;
       return if ($request_aborted);
     return if (! defined($outputfile));      return if (! defined($outputfile));
     my $Str = '';      my $Str = '';
     #      #
Line 1052  sub csv_outputstudent { Line 1534  sub csv_outputstudent {
     }      }
     #      #
     # Output performance data      # Output performance data
       my $total = 0;
     foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {      foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {
         my ($performance,$score,$seq_max) =          my ($performance,$performance_length,$score,$seq_max,$rawdata);
             &StudentPerformanceOnSequence($student,\%StudentsData,          if ($chosen_output->{'tries'}){
                                           $seq,'no');              ($performance,$performance_length,$score,$seq_max,$rawdata) =
         if ($show eq 'scores') {                  &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'}) {
                   $score = 0;
                   # Deal with number of parts correct data
                   $Str .= '"'.join('","',( map { if ($_>0) { 
                                                      $score += 1;
                                                      1; 
                                                  } else { 
                                                      0; 
                                                  }
                                                } @$rawdata)).'",';
               } else {
                   $Str .= '"'.join('","',(@$rawdata)).'",';
               }
           }
           if ($chosen_output->{'sequence_sum'}) {
             $Str .= '"'.$score.'",';              $Str .= '"'.$score.'",';
         } elsif ($show eq 'totals') {          } 
             $Str  .= '"'.$score.'","'.$seq_max.'",';          if ($chosen_output->{'sequence_max'}) {
         } elsif ($show eq 'all') {              $Str .= '"'.$seq_max.'",';
             $Str .= '"'.join('","',(split(//,$performance),$score,$seq_max)).  
                 '",';  
         }          }
           $total+=$score;
       }
       if ($chosen_output->{'grand_total'}) {
           $Str .= '"'.$total.'",';
     }      }
     chop($Str);      chop($Str);
     $Str .= "\n";      $Str .= "\n";
Line 1076  sub csv_outputstudent { Line 1583  sub csv_outputstudent {
   
 sub csv_finish {  sub csv_finish {
     my ($r) = @_;      my ($r) = @_;
       return if ($request_aborted);
     return if (! defined($outputfile));      return if (! defined($outputfile));
     close($outputfile);      close($outputfile);
     #      #
Line 1100  sub csv_finish { Line 1608  sub csv_finish {
   
 =pod  =pod
   
 =item &StudentPerformanceOnSequence()  =item &StudentTriesOnSequence()
   
 Inputs:  Inputs:
   
Line 1120  Inputs: Line 1628  Inputs:
   
 #######################################################  #######################################################
 #######################################################  #######################################################
 sub StudentPerformanceOnSequence {  sub StudentTriesOnSequence {
     my ($student,$studentdata,$seq,$links) = @_;      my ($student,$studentdata,$seq,$links) = @_;
     $links = 'no' if (! defined($links));      $links = 'no' if (! defined($links));
     my $Str = '';      my $Str = '';
     my ($sum,$max) = (0,0);      my ($sum,$max) = (0,0);
       my $performance_length = 0;
       my @TriesData = ();
       my $tries;
     foreach my $resource (@{$seq->{'contents'}}) {      foreach my $resource (@{$seq->{'contents'}}) {
         next if ($resource->{'type'} ne 'assessment');          next if ($resource->{'type'} ne 'assessment');
         my $resource_data = $studentdata->{$resource->{'symb'}};          my $resource_data = $studentdata->{$resource->{'symb'}};
         my $value = '';          my $value = '';
         foreach my $partnum (@{$resource->{'parts'}}) {          foreach my $partnum (@{$resource->{'parts'}}) {
               $tries = undef;
             $max++;              $max++;
               $performance_length++;
             my $symbol = ' '; # default to space              my $symbol = ' '; # default to space
             #              #
               my $awarded = 0;
               if (exists($resource_data->{'resource.'.$partnum.'.awarded'})) {
                   $awarded = $resource_data->{'resource.'.$partnum.'.awarded'};
                   $awarded = 0 if (! $awarded);
               }
               #
               my $status = '';
             if (exists($resource_data->{'resource.'.$partnum.'.solved'})) {              if (exists($resource_data->{'resource.'.$partnum.'.solved'})) {
                 my $status = $resource_data->{'resource.'.$partnum.'.solved'};                  $status = $resource_data->{'resource.'.$partnum.'.solved'};
                 if ($status eq 'correct_by_override') {              }
                     $symbol = '+';              #
                     $sum++;              my $tries = 0;
                 } elsif ($status eq 'incorrect_by_override') {              if(exists($resource_data->{'resource.'.$partnum.'.tries'})) {
                     $symbol = '-';                  $tries = $resource_data->{'resource.'.$partnum.'.tries'};
                 } elsif ($status eq 'ungraded_attempted') {              }
                     $symbol = '#';              #
                 } elsif ($status eq 'incorrect_attempted')  {              if ($awarded > 0) {
                     $symbol = '.';                  # The student has gotten the problem correct to some degree
                 } elsif ($status eq 'excused') {                  if ($status eq 'excused') {
                     $symbol = 'x';                      $symbol = 'x';
                     $max--;                      $max--;
                 } elsif ($status eq 'correct_by_student' &&                  } elsif ($status eq 'correct_by_override') {
                     exists($resource_data->{'resource.'.$partnum.'.tries'})){                      $symbol = '+';
                     my $num = $resource_data->{'resource.'.$partnum.'.tries'};                      $sum++;
                     if ($num > 9) {                  } elsif ($tries > 0) {
                       if ($tries > 9) {
                         $symbol = '*';                          $symbol = '*';
                     } elsif ($num > 0) {  
                         $symbol = $num;  
                     } else {                      } else {
                         $symbol = ' ';                          $symbol = $tries;
                     }                      }
                     $sum++;                      $sum++;
                 } elsif (exists($resource_data->{'resource.'.  
                                                      $partnum.'.tries'})){  
                     $symbol = '.';  
                 } else {                  } else {
                     $symbol = ' ';                      $symbol = '+';
                       $sum++;
                 }                  }
             } else {              } else {
                 # Unsolved.  Did they try?                  # The student has the problem incorrect or it is ungraded
                 if (exists($resource_data->{'resource.'.$partnum.'.tries'})){                  if ($status eq 'excused') {
                       $symbol = 'x';
                       $max--;
                   } elsif ($status eq 'incorrect_by_override') {
                       $symbol = '-';
                   } elsif ($status eq 'ungraded_attempted') {
                       $symbol = '#';
                   } elsif ($status eq 'incorrect_attempted' ||
                            $tries > 0)  {
                     $symbol = '.';                      $symbol = '.';
                 } else {                  } else {
                     $symbol = ' ';                      # Problem is wrong and has not been attempted.
                       $symbol=' ';
                 }                  }
             }              }
             #              #
               if (! defined($tries)) {
                   $tries = $symbol;
               }
               push (@TriesData,$tries);
               #
             if ( ($links eq 'yes' && $symbol ne ' ') ||              if ( ($links eq 'yes' && $symbol ne ' ') ||
                  ($links eq 'all')) {                   ($links eq 'all')) {
                   if (length($symbol) > 1) {
                       &Apache::lonnet::logthis('length of symbol "'.$symbol.'" > 1');
                   }
                 $symbol = '<a href="/adm/grades'.                  $symbol = '<a href="/adm/grades'.
                     '?symb='.&Apache::lonnet::escape($resource->{'symb'}).                      '?symb='.&Apache::lonnet::escape($resource->{'symb'}).
                         '&student='.$student->{'username'}.                          '&student='.$student->{'username'}.
                             '&domain='.$student->{'domain'}.                              '&userdom='.$student->{'domain'}.
                                 '&command=submission">'.$symbol.'</a>';                                  '&command=submission">'.$symbol.'</a>';
             }              }
             $value .= $symbol;              $value .= $symbol;
         }          }
         $Str .= $value;          $Str .= $value;
     }      }
     return ($Str,$sum,$max);      if ($seq->{'randompick'}) {
           $max = $seq->{'randompick'};
       }
       return ($Str,$performance_length,$sum,$max,\@TriesData);
   }
   
   #######################################################
   #######################################################
   
   =pod
   
   =item &StudentPerformanceOnSequence()
   
   Inputs:
   
   =over 4
   
   =item $student
   
   =item $studentdata Hash ref to all student data
   
   =item $seq Hash ref, the sequence we are working on
   
   =item $links if defined we will output links to each resource.
   
   =back
   
   =cut
   
   #######################################################
   #######################################################
   sub StudentPerformanceOnSequence {
       my ($student,$studentdata,$seq,$links) = @_;
       $links = 'no' if (! defined($links));
       my $Str = ''; # final result string
       my ($score,$max) = (0,0);
       my $performance_length = 0;
       my $symbol;
       my @ScoreData = ();
       my $partscore;
       foreach my $resource (@{$seq->{'contents'}}) {
           next if ($resource->{'type'} ne 'assessment');
           my $resource_data = $studentdata->{$resource->{'symb'}};
           foreach my $part (@{$resource->{'parts'}}) {
               $partscore = undef;
               my $weight = &Apache::lonnet::EXT('resource.'.$part.'.weight',
                                                 $resource->{'symb'},
                                                 $student->{'domain'},
                                                 $student->{'username'},
                                                 $student->{'section'});
               if (!defined($weight) || ($weight eq '')) { 
                   $weight=1;
               }
               #
               $max += $weight; # see the 'excused' branch below...
               $performance_length++; # one character per part
               $symbol = ' '; # default to space
               #
               my $awarded = 0;
               if (exists($resource_data->{'resource.'.$part.'.awarded'})) {
                   $awarded = $resource_data->{'resource.'.$part.'.awarded'};
                   $awarded = 0 if (! $awarded);
               }
               #
               $partscore = $weight*$awarded;
               $score += $partscore;
               $symbol = $partscore; 
               if (abs($symbol - sprintf("%.0f",$symbol)) < 0.001) {
                   $symbol = sprintf("%.0f",$symbol);
               }
               if (length($symbol) > 1) {
                   $symbol = '*';
               }
               if (exists($resource_data->{'resource.'.$part.'.solved'})) {
                   my $status = $resource_data->{'resource.'.$part.'.solved'};
                   if ($status eq 'excused') {
                       $symbol = 'x';
                       $max -= $weight; # Do not count 'excused' problems.
                   }
               } else {
                   # Unsolved.  Did they try?
                   if (exists($resource_data->{'resource.'.$part.'.tries'})){
                       $symbol = '.';
                   } else {
                       $symbol = ' ';
                   }
               }
               #
               if (! defined($partscore)) {
                   $partscore = $symbol;
               }
               push (@ScoreData,$partscore);
               #
               if ( ($links eq 'yes' && $symbol ne ' ') || ($links eq 'all')) {
                   $symbol = '<a href="/adm/grades'.
                       '?symb='.&Apache::lonnet::escape($resource->{'symb'}).
                       '&student='.$student->{'username'}.
                       '&userdom='.$student->{'domain'}.
                       '&command=submission">'.$symbol.'</a>';
               }
               $Str .= $symbol;
           }
       }
       return ($Str,$performance_length,$score,$max,\@ScoreData);
 }  }
   
 #######################################################  #######################################################

Removed from v.1.48  
changed lines
  Added in v.1.90


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>