Diff for /loncom/interface/statistics/lonproblemstatistics.pm between versions 1.4 and 1.109

version 1.4, 2002/07/27 20:48:36 version 1.109, 2006/05/30 12:46:50
Line 1 Line 1
 # The LearningOnline Network with CAPA  # The LearningOnline Network with CAPA
 # (Publication Handler  
 #  #
 # $Id$  # $Id$
 #  #
Line 26 Line 25
 # http://www.lon-capa.org/  # http://www.lon-capa.org/
 #  #
 # (Navigate problems for statistical reports  # (Navigate problems for statistical reports
 # YEAR=2001  
 # 5/5,7/9,7/25/1,8/11,9/13,9/26,10/5,10/9,10/22,10/26 Behrouz Minaei  
 # 11/1,11/4,11/16,12/14,12/16,12/18,12/20,12/31 Behrouz Minaei  
 # YEAR=2002  
 # 1/22,2/1,2/6,2/25,3/2,3/6,3/17,3/21,3/22,3/26,4/7,5/6 Behrouz Minaei  
 # 5/12,5/14,5/15,5/19,5/26,7/16,7/25  Behrouz Minaei  
 #  #
 ###  ###############################################
   ###############################################
   
 package Apache::lonproblemstatistics;   =pod
   
   =head1 NAME
   
   lonproblemstatistics
   
   =head1 SYNOPSIS
   
   Routines to present problem statistics to instructors via tables,
   Excel files, and plots.
   
   =over 4
   
   =cut
   
   ###############################################
   ###############################################
   
   package Apache::lonproblemstatistics;
   
 use strict;  use strict;
 use Apache::lonnet();  use Apache::lonnet;
   use Apache::loncommon();
 use Apache::lonhtmlcommon;  use Apache::lonhtmlcommon;
 use Apache::loncoursedata;  use Apache::loncoursedata;
 use GDBM_File;  use Apache::lonstatistics;
   use LONCAPA::lonmetadata();
   use Apache::lonlocal;
   use Spreadsheet::WriteExcel;
   use Apache::lonstathelpers();
   use Time::HiRes;
   use lib '/home/httpd/lib/perl/';
   use LONCAPA;
    
   
   my @StatsArray;
   my %SeqStat;    # keys are symbs, values are hash refs
   
 sub BuildProblemStatisticsPage {  ##
     my ($cacheDB, $students, $courseID, $c, $r,%color)=@_;  ## Localization notes:
     my %cache;  ##
     #my %DoDiff;  ## in @Fields[0]->{'long_title'} is placed in Excel files and is used as the
     unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {  ## header for plots created with Graph.pm, both of which more than likely do
         return '<html><body>Unable to tie database.</body></html>';  ## not support localization.
   ##
   #
   #
   ##
   ## Description of Field attributes
   ##
   ## Attribute     Required   Value       Meaning or Use
   ##
   ## name            yes      any scalar  Used to uniquely identify field
   ## title           yes      any scalar  This is what the user sees to identify
   ##                                      the field.  Passed through &mt().
   ## long_title      yes      any scalar  Used as graph heading and in excel
   ##                                      output.  NOT translated
   ## align           no    (left|right|center)  HTML cell contents alignment
   ## color           yes      html color  HTML cell background color
   ##                                      used to visually group statistics
   ## special         no          (link)   Indicates a link, target is name.link
   ##                                      Currently set in &get_statistics()
   ## graphable       no      (yes|no)     Can a bar graph of the field be 
   ##                                      produced?
   ## sortable        no      (yes|no)     Should a sort link be put in the
   ##                                      column header?
   ## selectable      yes     (yes|no)     Can the column be removed from the
   ##                                      statistics display?
   ## selected        yes     (yes|no)     Is the column selected by default?
   ##
   ## format          no      sprintf format string
   ##
   ## excel_format    no      excel format type 
   ##                               (see &Apache::loncommon::define_excel_formats
   my @Fields = (
              { name => 'problem_num',
                title => 'P#',
                align => 'right',
                color => '#FFFFE6',
                selectable => 'no',
                defaultselected => 'yes',
              },
              { name   => 'container',
                title  => 'Sequence or Folder',
                align  => 'left',
                color  => '#FFFFE6',
                sortable => 'yes',
                selectable => 'no',
                defaultselected => 'yes',
              },
              { name   => 'title',
                title  => 'Title',
                align  => 'left',
                color  => '#FFFFE6',
                special  => 'link',
                sortable => 'yes', 
                selectable => 'no',
                defaultselected => 'yes',
              },
              { name   => 'part', 
                title  => 'Part',
                align  => 'left',
                color  => '#FFFFE6',
                selectable => 'no',
                defaultselected => 'yes',
              },
              { name   => 'num_students',
                title  => '#Stdnts',
                align  => 'right',
                color  => '#EEFFCC',
                format => '%d',
                sortable  => 'yes',
                graphable => 'yes',
                long_title => 'Number of Students Attempting Problem',
                selectable => 'yes',
                defaultselected => 'yes',
              },
              { name   => 'tries',
                title  => 'Tries',
                align  => 'right',
                color  => '#EEFFCC',
                format => '%d',
                sortable  => 'yes',
                graphable => 'yes',
                long_title => 'Total Number of Tries',
                selectable => 'yes',
                defaultselected => 'yes',
              },
              { name   => 'max_tries',
                title  => 'Max Tries',
                align  => 'right',
                color  => '#DDFFFF',
                format => '%d',
                sortable  => 'yes',
                graphable => 'yes',
                long_title => 'Maximum Number of Tries',
                selectable => 'yes',
                defaultselected => 'yes',
              },
              { name   => 'min_tries',
                title  => 'Min Tries',
                align  => 'right',
                color  => '#DDFFFF',
                format => '%d',
                sortable  => 'yes',
                graphable => 'yes',
                long_title => 'Minumum Number of Tries',
                selectable => 'yes',
                defaultselected => 'yes',
              },
              { name   => 'mean_tries',
                title  => 'Mean Tries',
                align  => 'right',
                color  => '#DDFFFF',
                format => '%5.2f',
                sortable  => 'yes',
                graphable => 'yes',
                long_title => 'Average Number of Tries',
                selectable => 'yes',
                defaultselected => 'yes',
              },
              { name   => 'std_tries',
                title  => 'S.D. tries',
                align  => 'right',
                color  => '#DDFFFF',
                format => '%5.2f',
                sortable  => 'yes',
                graphable => 'yes',
                long_title => 'Standard Deviation of Number of Tries',
                selectable => 'yes',
                defaultselected => 'yes',
              },
              { name   => 'skew_tries',
                title  => 'Skew Tries',
                align  => 'right',
                color  => '#DDFFFF',
                format => '%5.2f',
                sortable  => 'yes',
                graphable => 'yes',
                long_title => 'Skew of Number of Tries',
                selectable => 'yes',
                defaultselected => 'no',
              },
              { name   => 'num_solved',
                title  => '#YES',
                align  => 'right',
                color  => '#FFDDDD',
                format => '%4.1f',#             format => '%d',
                sortable  => 'yes',
                graphable => 'yes',
                long_title => 'Number of Students able to Solve',
                selectable => 'yes',
                defaultselected => 'yes',
              },
              { name   => 'num_override',
                title  => '#yes',
                align  => 'right',
                color  => '#FFDDDD',
                format => '%4.1f',#             format => '%d',
                sortable  => 'yes',
                graphable => 'yes',
                long_title => 'Number of Students given Override',
                selectable => 'yes',
                defaultselected => 'yes',
              },
              { name   => 'tries_per_correct',
                title  => 'tries/correct',
                align  => 'right',
                color  => '#FFDDDD',
                format => '%4.1f',
                sortable  => 'yes',
                graphable => 'yes',
                long_title => 'Tries per Correct Answer',
                selectable => 'yes',
                defaultselected => 'yes',
              },
              { name   => 'num_wrong',
                title  => '#Wrng',
                align  => 'right',
                color  => '#FFDDDD',
                format => '%4.1f',
                sortable  => 'yes',
                graphable => 'yes',
                long_title => 'Number of students whose final answer is wrong',
                selectable => 'yes',
                defaultselected => 'yes',
              },
              { name   => 'per_wrong',
                title  => '%Wrng',
                align  => 'right',
                color  => '#FFDDDD',
                format => '%4.1f',
                sortable  => 'yes',
                graphable => 'yes',
                long_title => 'Percent of students whose final answer is wrong',
                selectable => 'yes',
                defaultselected => 'yes',
              },
              { name   => 'deg_of_diff',
                title  => 'DoDiff',
                align  => 'right',
                color  => '#FFFFE6',
                format => '%5.2f',
                sortable  => 'yes',
                graphable => 'yes',
                long_title => 'Degree of Difficulty'.
                              '[ 1 - ((#YES+#yes) / Tries) ]',
                selectable => 'yes',
                defaultselected => 'yes',
              },
              { name   => 'deg_of_disc',
                title  => 'DoDisc',
                align  => 'right',
                color  => '#FFFFE6',
                format => '%4.2f',
                sortable  => 'yes',
                graphable => 'yes',
                long_title => 'Degree of Discrimination',
                selectable => 'yes',
                defaultselected => 'yes',
              },
   ##   duedate included for research purposes.  Commented out most of the time.
   #           { name => 'duedate',
   #             title => 'Due Date',
   #             align => 'left',
   #             color => '#FFFFFF',
   #             sortable => 'yes',
   #             graphable => 'no',
   #             long_title => 'Due date of resource for instructor',
   #             selectable => 'no',
   #             defaultselected => 'yes',
   #            },
   ##   opendate included for research purposes.  Commented out most of the time.
   #           { name => 'opendate',
   #             title => 'Open Date',
   #             align => 'left',
   #             color => '#FFFFFF',
   #             sortable => 'yes',
   #             graphable => 'no',
   #             long_title => 'date resource became answerable',
   #             selectable => 'no',
   #             defaultselected => 'yes',
   #            },
   ##   symb included for research purposes.  Commented out most of the time.
   #           { name => 'symb',
   #             title => 'Symb',
   #             align => 'left',
   #             color => '#FFFFFF',
   #             sortable => 'yes',
   #             graphable => 'no',
   #             long_title => 'Unique LON-CAPA identifier for problem',
   #             selectable => 'no',
   #             defaultselected => 'yes',
   #            },
   ##   resptypes included for research purposes.  Commented out most of the time.
   #           { name => 'resptypes',
   #             title => 'Response Types',
   #             align => 'left',
   #             color => '#FFFFFF',
   #             sortable => 'no',
   #             graphable => 'no',
   #             long_title => 'Response Types used in this problem',
   #             selectable => 'no',
   #             defaultselected => 'yes',
   #            },
   ##   maxtries included for research purposes.  Commented out most of the time.
   #           { name => 'maxtries',
   #             title => 'Maxtries',
   #             align => 'left',
   #             color => '#FFFFFF',
   #             sortable => 'no',
   #             graphable => 'no',
   #             long_title => 'Maximum number of tries',
   #             selectable => 'no',
   #             defaultselected => 'yes',
   #            },
   ##   hinttries included for research purposes.  Commented out most of the time.
   #           { name => 'hinttries',
   #             title => 'hinttries',
   #             align => 'left',
   #             color => '#FFFFFF',
   #             sortable => 'no',
   #             graphable => 'no',
   #             long_title => 'Number of tries before a hint appears',
   #             selectable => 'no',
   #             defaultselected => 'yes',
   #            },
   #
   ##   problem weight for instructor
              { name => 'weight',
                title => 'weight',
                align => 'right',
                color => '#FFFFFF',
                sortable => 'no',
                graphable => 'no',
                long_title => 'Problem weight (for instructor)',
                selectable => 'yes',
                defaultselected => 'yes',
               },
   );
   
   my @SeqFields = (
              { name   => 'title',
                title  => 'Sequence',
                align  => 'left',
                color  => '#FFFFE6',
                special  => 'no',
                sortable => 'no', 
                selectable => 'yes',
                defaultselected => 'no',
              },
              { name   => 'items',
                title  => '#Items',
                align  => 'right',
                color  => '#FFFFE6',
                format => '%4d',
                sortable  => 'no',
                graphable => 'no',
                long_title => 'Number of Items in Sequence',
                selectable => 'yes',
                defaultselected => 'no',
              },
              { name   => 'scoremean',
                title  => 'Score Mean',
                align  => 'right',
                color  => '#FFFFE6',
                format => '%4.2f',
                sortable  => 'no',
                graphable => 'no',
                long_title => 'Mean Sequence Score',
                selectable => 'yes',
                defaultselected => 'no',
              },
              { name   => 'scorestd',
                title  => 'Score STD',
                align  => 'right',
                color  => '#FFFFE6',
                format => '%4.2f',
                sortable  => 'no',
                graphable => 'no',
                long_title => 'Standard Deviation of Sequence Scores',
                selectable => 'yes',
                defaultselected => 'no',
              },
              { name   => 'scoremax',
                title  => 'Score Max',
                align  => 'right',
                color  => '#FFFFE6',
                format => '%4.2f',
                sortable  => 'no',
                graphable => 'no',
                long_title => 'Maximum Sequence Score',
                selectable => 'yes',
                defaultselected => 'no',
              },
              { name   => 'scoremin',
                title  => 'Score Min',
                align  => 'right',
                color  => '#FFFFE6',
                format => '%4.2f',
                sortable  => 'no',
                graphable => 'no',
                long_title => 'Minumum Sequence Score',
                selectable => 'yes',
                defaultselected => 'no',
              },
              { name   => 'scorecount',
                title  => 'Score N',
                align  => 'right',
                color  => '#FFFFE6',
                format => '%4d',
                sortable  => 'no',
                graphable => 'no',
                long_title => 'Number of Students in score computations',
                selectable => 'yes',
                defaultselected => 'no',
              },
              { name   => 'countmean',
                title  => 'Count Mean',
                align  => 'right',
                color  => '#FFFFFF',
                format => '%4.2f',
                sortable  => 'no',
                graphable => 'no',
                long_title => 'Mean Sequence Score',
                selectable => 'yes',
                defaultselected => 'no',
              },
              { name   => 'countstd',
                title  => 'Count STD',
                align  => 'right',
                color  => '#FFFFFF',
                format => '%4.2f',
                sortable  => 'no',
                graphable => 'no',
                long_title => 'Standard Deviation of Sequence Scores',
                selectable => 'yes',
                defaultselected => 'no',
              },
              { name   => 'countmax',
                title  => 'Count Max',
                align  => 'right',
                color  => '#FFFFFF',
                format => '%4.2f',
                sortable  => 'no',
                graphable => 'no',
                long_title => 'Maximum Number of Correct Problems',
                selectable => 'yes',
                defaultselected => 'no',
              },
              { name   => 'countmin',
                title  => 'Count Min',
                align  => 'right',
                color  => '#FFFFFF',
                format => '%4.2f',
                sortable  => 'no',
                graphable => 'no',
                long_title => 'Minumum Number of Correct Problems',
                selectable => 'yes',
                defaultselected => 'no',
              },
              { name   => 'count',
                title  => 'Count N',
                align  => 'right',
                color  => '#FFFFFF',
                format => '%4d',
                sortable  => 'no',
                graphable => 'no',
                long_title => 'Number of Students in score computations',
                selectable => 'yes',
                defaultselected => 'no',
              },
              { name   => 'KR-21',
                title  => 'KR-21',
                align  => 'right',
                color  => '#FFAAAA',
                format => '%4.2f',
                sortable  => 'no',
                graphable => 'no',
                long_title => 'KR-21 reliability statistic',
                selectable => 'yes',
                defaultselected => 'no',
              },           
   );
   
   my %SelectedFields;
   
   sub parse_field_selection {
       #
       # Pull out the defaults
       if (! defined($env{'form.fieldselections'})) {
           $env{'form.fieldselections'} = [];
           foreach my $field (@Fields) {
               next if ($field->{'selectable'} ne 'yes');
               if ($field->{'defaultselected'} eq 'yes') {
                   push(@{$env{'form.fieldselections'}},$field->{'name'});
               }
           }
     }      }
       #
     my $Ptr = '';      # Make sure the data we are plotting is there
     $Ptr .= '<table border="0"><tbody>';      my %NeededFields;
     $Ptr .= '<tr><td align="right"><b>Select Map</b></td>'."\n";      if (exists($env{'form.plot'}) && $env{'form.plot'} ne '' &&
     $Ptr .= '<td align="left">';          $env{'form.plot'} ne 'none') {
     $Ptr .= &Apache::lonhtmlcommon::MapOptions(\%cache, 'ProblemStatistics');          if ($env{'form.plot'} eq 'degrees') {
     $Ptr .= '</td></tr>'."\n";              $NeededFields{'deg_of_diff'}++;
     $Ptr .= &AscendOrderOptions($cache{'Ascend'});              $NeededFields{'deg_of_disc'}++;
     $Ptr .= &ProblemStatisticsButtons($cache{'DisplayFormat'});          } elsif ($env{'form.plot'} eq 'tries statistics') {
     $Ptr .= '</table>';              $NeededFields{'mean_tries'}++;
               $NeededFields{'std_tries'}++;
     $Ptr .= &ProblemStatisticsLegend();              $NeededFields{'problem_num'}++;
           } else {
     untie(%cache);              $NeededFields{$env{'form.plot'}}++;
     foreach (@$students) {  
         my $courseData =   
             &Apache::loncoursedata::DownloadCourseInformation($_, $courseID);  
         last if ($c->aborted());  
         if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_WRCREAT,0640)) {  
             &Apache::loncoursedata::ProcessStudentData(\%cache,   
                                                        $courseData, $_);  
             untie(%cache);  
         }          }
     }      }
     if($c->aborted()) { return $Ptr; }      #
       # This should not happen, but in case it does...
     unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER,0640)) {      if (ref($env{'form.fieldselections'}) ne 'ARRAY') {
         return '<html><body>Unable to tie database.</body></html>';          $env{'form.fieldselections'} = [$env{'form.fieldselections'}];
       }
       #
       # Set the field data and the selected fields (for easier checking)
       undef(%SelectedFields);
       foreach my $field (@Fields) {
           if ($field->{'selectable'} ne 'yes') {
               $field->{'selected'} = 'yes';
           } else {
               $field->{'selected'} = 'no';
           }
           if (exists($NeededFields{$field->{'name'}})) {
               $field->{'selected'} = 'yes';
               $SelectedFields{$field->{'name'}}++;
           }
           foreach my $selection (@{$env{'form.fieldselections'}}) {
               if ($selection eq $field->{'name'} || $selection eq 'all') {
                   $field->{'selected'} = 'yes';
                   $SelectedFields{$field->{'name'}}++;
               }
           }
       }
       #
       # Always show all the sequence statistics (for now)
       foreach my $field (@SeqFields) {
           $field->{'selected'} = 'yes';
     }      }
     my $discriminantFactor;      return;
     my @list=();  }
     my %Discuss=&Apache::loncoursedata::LoadDiscussion($courseID);  
     my $index=0;  sub field_selection_input {
     foreach (@$students) {      my $Str = '<select name="fieldselections" multiple size="5">'."\n";
  $index++;      $Str .= '<option value="all">all</option>'."\n";
  #&Apache::lonstatistics::Update_PrgWin(++$index,$r);      foreach my $field (@Fields) {
         $discriminantFactor = &ExtractStudentData(\%cache, $_, \@list,          next if ($field->{'selectable'} ne 'yes');
   \%Discuss, $r);          $Str .= '    <option value="'.$field->{'name'}.'" ';
           if ($field->{'selected'} eq 'yes') {
               $Str .= 'selected ';
           }
           $Str .= '>'.$field->{'title'}.'</option>'."\n";
     }      }
     #&Apache::lonstatistics::Close_PrgWin($r);      $Str .= "</select>\n";
   }
   
   ###############################################
   ###############################################
   
   =pod 
   
     my ($upper, $lower) = &Discriminant($discriminantFactor);  =item &CreateInterface()
     my %Header = (0,"Homework Sets Order",1,"#Stdnts",2,"Tries",3,"Mod",  
                   4,"Mean",5,"#YES",6,"#yes",7,"%Wrng",8,"DoDiff",  
                   9,"S.D.",10,"Skew.",11,"D.F.1st",12,"D.F.2nd", 13, "Disc.");  
   
     my %GraphDat= &BuildStatisticsTable(\%cache, $discriminantFactor, \@list,   Create the main intereface for the statistics page.  Allows the user to
                                         \%Header, $students, $r, \%color);  select sections, maps, and output.
   
     untie(%cache);  =cut
   
     return $Ptr;  ###############################################
   ###############################################
   sub CreateInterface {
       my ($r) = @_;
       #
       &parse_field_selection();
       #
       my $Str = '';
       $Str .= &Apache::lonhtmlcommon::breadcrumbs('Overall Problem Statistics',
    'Statistics_Overall_Key');
       $Str .= '<table cellspacing="5">'."\n";
       $Str .= '<tr>';
       $Str .= '<td align="center"><b>'.&mt('Sections').'</b></td>';
       $Str .= '<td align="center"><b>'.&mt('Groups').'</b></td>';
       $Str .= '<td align="center"><b>'.&mt('Enrollment Status').'</b></td>';
       $Str .= '<td align="center"><b>'.&mt('Sequences and Folders').'</b></td>';
       $Str .= '<td align="center"><b>'.&mt('Statistics').'</b></td>';
       $Str .= '<td rowspan="2">'.
           &Apache::lonstathelpers::limit_by_time_form().'</td>';
       $Str .= '</tr>'."\n";
       #
       $Str .= '<tr><td align="center">'."\n";
       $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5);
       $Str .= '</td><td align="center">';
       $Str .= &Apache::lonstatistics::GroupSelect('Group','multiple',5);
       $Str .= '</td><td align="center">';
       $Str .= &Apache::lonhtmlcommon::StatusOptions(undef,undef,5);
       $Str .= '</td><td align="center">';
       #
       $Str .= &Apache::lonstatistics::map_select('Maps','multiple,all',5);
       $Str .= '</td><td>'.&field_selection_input();
       $Str .= '</td></tr>'."\n";
       $Str .= '</table>'."\n";
       #
       $Str .= '<p>'.&mt('Status: [_1]',
                            '<input type="text" '.
                            'name="stats_status" size="60" value="" />'
                            ).
                            '</nobr></p>';
       #
       $Str .= '<input type="submit" name="GenerateStatistics" value="'.
           &mt('Generate Statistics').'" />';
       $Str .= '&nbsp;'x5;
       $Str .= 'Plot '.&plot_dropdown().('&nbsp;'x10);
       #
       return $Str;
 }  }
   
   ###############################################
   ###############################################
   
   =pod 
   
   =item &BuildProblemStatisticsPage()
   
 #---- Problem Statistics Web Page ---------------------------------------  Main interface to problem statistics.
   
   =cut
   
 sub NumericSort {            ###############################################
     $a <=> $b;  ###############################################
   my $navmap;
   my @sequences;
   
   sub clean_up {
       undef($navmap);
       undef(@sequences);
 }  }
   
   sub BuildProblemStatisticsPage {
       my ($r,$c)=@_;
       undef($navmap);
       undef(@sequences);
       #
       my %Saveable_Parameters = ('Status' => 'scalar',
                                  'statsoutputmode' => 'scalar',
                                  'Section' => 'array',
                                  'Groups' => 'array',
                                  'StudentData' => 'array',
                                  'Maps' => 'array',
                                  'fieldselections'=> 'array');
       &Apache::loncommon::store_course_settings('statistics',
                                                 \%Saveable_Parameters);
       &Apache::loncommon::restore_course_settings('statistics',
                                                   \%Saveable_Parameters);
       #
       &Apache::lonstatistics::PrepareClasslist();
       #
       # Clear the package variables
       undef(@StatsArray);
       undef(%SeqStat);
       #
       # Finally let the user know we are here
       my $interface = &CreateInterface($r);
       $r->print($interface);
       $r->print('<input type="hidden" name="sortby" value="'.$env{'form.sortby'}.
                 '" />');
       #
       my @CacheButtonHTML = 
           &Apache::lonstathelpers::manage_caches($r,'Statistics','stats_status');
       my $Str;
       foreach my $html (@CacheButtonHTML) {
           $Str.=$html.('&nbsp;'x5);
       }
       #
       $r->print($Str);
       if (! exists($env{'form.firstrun'})) {
           $r->print('<h3>'.
                     &mt('Press "Generate Statistics" when you are ready.').
                     '</h3><p>'.
                     &mt('It may take some time to update the student data '.
                         'for the first analysis.  Future analysis this session '.
                         ' will not have this delay.').
                     '</p>');
    &clean_up();
           return;
       }
       $r->rflush();
       #
       # This probably does not need to be done each time we are called, but
       # it does not slow things down noticably.
       &Apache::loncoursedata::populate_weight_table();
       #
       ($navmap,@sequences) = 
           &Apache::lonstatistics::selected_sequences_with_assessments();
       if (! ref($navmap)) {
           $r->print('<h1>'.&mt('A course-wide error occured.').'</h1>'.
                     '<h3>'.$navmap.'</h3>');
    &clean_up();
           return;
       }
       if (exists($env{'form.Excel'})) {
           $r->print('<h4>'.
                     &Apache::lonstatistics::section_and_enrollment_description().
                     '</h4>');
           &Excel_output($r);
       } else { 
           $r->print('<input type="submit" name="Excel" value="'.
                     &mt('Produce Excel Output').'" />'.'&nbsp;'x5);
           $r->rflush();
           $r->print('<h4>'.
                     &Apache::lonstatistics::section_and_enrollment_description().
                     '</h4>');
           my $count = 0;
           foreach my $seq (@sequences) {
               my @resources = 
                   &Apache::lonstathelpers::get_resources($navmap,$seq);
               $count += scalar(@resources);
           }
           if ($count > 10) {
               $r->print('<h2>'.
                         &mt('Compiling statistics for [_1] problems',$count).
                         '</h2>');
               if ($count > 30) {
                   $r->print('<h3>'.&mt('This will take some time.').'</h3>');
               }
               $r->rflush();
           }
           #
           my $sortby = $env{'form.sortby'};
           $sortby = 'container' if (! defined($sortby) || $sortby =~ /^\s*$/);
           my $plot = $env{'form.plot'};
           if ($plot eq '' || $plot eq 'none') {
               undef($plot);
           }
           if ($sortby eq 'container' && ! defined($plot)) {
               &output_sequence_statistics($r);
               &output_html_by_sequence($r);
           } else {
               if (defined($plot)) {
                   &make_plot($r,$plot);
               }
               &output_html_stats($r);
               &output_sequence_statistics($r);
           }
       }
       &clean_up();
       return;
   }
   
 sub CreateProblemStatisticsTableHeading {  sub output_sequence_statistics {
     my ($displayFormat,$sequenceSource,$sequenceTitle,$headings,$r)=@_;      my ($r) = @_;
     if($displayFormat eq 'Display CSV Format') {      my $c=$r->connection();
         $r->print('<br>"'.$sequenceTitle.'","');      $r->print('<h2>'.&mt('Sequence Statistics').
         $r->print($sequenceSource.'"');        &Apache::loncommon::help_open_topic('Statistics_Sequence').
  return;        '</h2>');
       $r->print('<table border="0"><tr><td bgcolor="#777777">'."\n".
                 '<table border="0" cellpadding="3">'."\n".
                 '<tr bgcolor="#FFFFE6">');
       $r->print(&sequence_html_header());
       foreach my $seq (@sequences) { 
           last if ($c->aborted);
           &compute_sequence_statistics($seq);
           $r->print(&sequence_html_output($seq));
     }      }
       $r->print('</table>');
       $r->print('</table>');
       $r->rflush();
       return;
   }
   
     $r->print('<br><a href="'.$sequenceSource.  
               '" target="_blank">'.$sequenceTitle.'</a>');  
   
     my $Result = "\n".'<table border=2><tr><th>P#</th>'."\n";  ##########################################################
     for(my $nIndex=0; $nIndex < (scalar (keys %$headings)); $nIndex++) {   ##########################################################
  $Result .= '<th>'.'<input type="submit" name="';  ##
         $Result .= 'ProblemStatisticsHeading" value="';  ## HTML output routines
         $Result .= $headings->{$nIndex}.'" />'.'</th>'."\n";  ##
   ##########################################################
   ##########################################################
   sub output_html_by_sequence {
       my ($r) = @_;
       my $c = $r->connection();
       $r->print(&html_preamble());
       #
       foreach my $seq (@sequences) {
           last if ($c->aborted);
           $r->print("<h3>".$seq->compTitle."</h3>".
                     '<table border="0"><tr><td bgcolor="#777777">'."\n".
                     '<table border="0" cellpadding="3">'."\n".
                     '<tr bgcolor="#FFFFE6">'.
                     &statistics_table_header('no container')."</tr>\n");
           my @Data = &compute_statistics_on_sequence($seq);
           foreach my $data (@Data) {
               $r->print('<tr>'.&statistics_html_table_data($data,
                                                            'no container').
                         "</tr>\n");
           }
           $r->print('</table>'."\n".'</table>'."\n");
           $r->rflush();
     }      }
     $Result .= "\n".'</tr>'."\n";          return;
     $r->print($Result);  
     $r->rflush();  
 }  }
   
 sub CloseTable {  sub output_html_stats {
     my ($cache,$r)=@_;      my ($r)=@_;
     if($cache->{'DisplayFormat'} eq 'Display CSV Format') {      &compute_all_statistics($r);
  return;      $r->print(&html_preamble());
     }          &sort_data($env{'form.sortby'});
     $r->print("\n".'</table>'."\n");      #
     $r->rflush();      my $count=0;
       foreach my $data (@StatsArray) {
           if ($count++ % 50 == 0) {
               $r->print("</table>\n</table>\n");
               $r->print('<table border="0"><tr><td bgcolor="#777777">'."\n".
                         '<table border="0" cellpadding="3">'."\n".
                         '<tr bgcolor="#FFFFE6">'.
                         '<tr bgcolor="#FFFFE6">'.
                         &statistics_table_header().
                         "</tr>\n");
           }
           $r->print('<tr>'.&statistics_html_table_data($data)."</tr>\n");
       }
       $r->print("</table>\n</table>\n");
       return;
   }
   
   sub html_preamble {
       my $Str='';
       $Str .= "<h2>".
           $env{'course.'.$env{'request.course.id'}.'.description'}.
           "</h2>\n";
       my ($starttime,$endtime) = &Apache::lonstathelpers::get_time_limits();
       if (defined($starttime) || defined($endtime)) {
           # Inform the user what the time limits on the data are.
           $Str .= '<h3>'.&mt('Statistics on submissions from [_1] to [_2]',
                              &Apache::lonlocal::locallocaltime($starttime),
                              &Apache::lonlocal::locallocaltime($endtime)
                              ).'</h3>';
       }
       $Str .= "<h3>".&mt('Compiled on [_1]',
                          &Apache::lonlocal::locallocaltime(time))."</h3>";
       return $Str;
 }  }
   
   
    ###############################################
 # ------ Dump the Student's DB file and handling the data for statistics table   ###############################################
 sub ExtractStudentData {  ##
     my ($cache,$name,$list,$Discuss,$r)=@_;  ## Misc HTML output routines
     my %discriminantFactor;  ##
   ###############################################
     my $totalTries = 0;  ###############################################
     my $totalAwarded = 0;  sub statistics_html_table_data {
     my $tempProblemOrder=0;      my ($data,$options) = @_;
     my $spent=0;      my $row = '';
     my $spent_yes=0;      foreach my $field (@Fields) {
     my $TotDiscuss=0;          next if ($options =~ /no $field->{'name'}/);
     my $TotalOpend = 0;          next if ($field->{'selected'} ne 'yes');
     my $ProbSolved = 0;          $row .= '<td bgcolor="'.$field->{'color'}.'"';
     my $ProbTot = 0;          if (exists($field->{'align'})) {
     my $TotFirst = 0;              $row .= ' align="'.$field->{'align'}.'"';
     my $TimeTot = 0;  
     my $Discussed=0;  
   
 $Apache::lonxml::debug=1;  
 #&Apache::lonhomework::showhash(%$cache);  
 $Apache::lonxml::debug=0;  
   
     foreach my $sequence (split(':', $cache->{'orderedSequences'})) {  
 #        if($cache->{'ProblemStatisticsMap'} ne 'All Maps'  &&  
 #           $cache->{'ProblemStatisticsMap'} ne $cache->{$sequence.':title'}) {  
 #    $r->print("<br>$name ---   $sequence");  
 #    $r->print("<br>".$cache->{$sequence.':title'});  
 #            next;  
 #        }  
   
         my $Dis = '';  
         foreach my $problemID (split(':', $cache->{$sequence.':problems'})) {  
             my $problem = $cache->{$problemID.':problem'};  
     $r->print("<br>$problemID === $problem");  
             my $LatestVersion = $cache->{$name.':version:'.$problem};  
     # Output dashes for all the parts of this problem if there  
             # is no version information about the current problem.  
             #if(!$LatestVersion) {  
             #    foreach my $part (split(/\:/,$cache->{$sequence.':'.  
             #                                          $problemID.  
             #                                          ':parts'})) {  
             #        $codes    .= "-,";  
             #        $attempts .= "0,";   
             #    }  
             #    next;  
             #}  
   
             my %partData=undef;  
             # Initialize part data, display skips correctly  
             # Skip refers to when a student made no submissions on that  
             # part/problem.  
             foreach my $part (split(/\:/,$cache->{$sequence.':'.  
                                                   $problemID.  
                                                   ':parts'})) {  
                 $partData{$part.':tries'}=0;  
                 $partData{$part.':code'}='-';  
             }              }
           $row .= '>';
           if (exists($field->{'special'}) && $field->{'special'} eq 'link') {
               $row .= '<a href="'.$data->{$field->{'name'}.'.link'}.'">';
           }
           if (exists($field->{'format'}) && $data->{$field->{'name'}} !~ /[A-Z]/i) {
               $row .= sprintf($field->{'format'},$data->{$field->{'name'}});
           } else {
               $row .= $data->{$field->{'name'}};
           }
           if (exists($field->{'special'}) && $field->{'special'} eq 'link') {
               $row.= '</a>';
           }
           $row .= '</td>';
       }
       return $row;
   }
   
             # Looping through all the versions of each part, starting with the  sub statistics_table_header {
             # oldest version.  Basically, it gets the most recent       my ($options) = @_;
             # set of grade data for each part.      my $header_row;
     for(my $Version=1; $Version<=$LatestVersion; $Version++) {      foreach my $field (@Fields) {
  foreach my $part (split(/\:/,$cache->{$sequence.':'.          next if ($options =~ /no $field->{'name'}/);
                                                       $problemID.          next if ($field->{'selected'} ne 'yes');
                                                       ':parts'})) {          $header_row .= '<th>';
           if (exists($field->{'sortable'}) && $field->{'sortable'} eq 'yes') {
                     if(!defined($cache->{$name.":$Version:$problem".              $header_row .= '<a href="javascript:'.
                                                ":resource.$part.solved"})) {                  'document.Statistics.sortby.value='."'".$field->{'name'}."'".
                         # No grade for this submission, so skip                      ';document.Statistics.submit();">';
                         next;          }
                     }          $header_row .= &mt($field->{'title'});
           if ($options =~ /sortable/) {
               $header_row.= '</a>';
           }
           if ($options !~ /no plots/        && 
               exists($field->{'graphable'}) && 
               $field->{'graphable'} eq 'yes') {
               $header_row.=' (';
               $header_row .= '<a href="javascript:'.
                   "document.Statistics.plot.value='$field->{'name'}'".
                       ';document.Statistics.submit();">';
               $header_row .= &mt('plot').'</a>)';
           }
           $header_row .= '</th>';
       }
       return $header_row;
   }
   
                     my $tries=0;  sub sequence_html_header {
                     my $time=0;      my $Str .= '<tr>';
                     my $awarded=0;      foreach my $field (@SeqFields) {
     $Discussed=0;  #        next if ($field->{'selected'} ne 'yes');
                     my $code='U';          $Str .= '<th bgcolor="'.$field->{'color'}.'"';
           $Str .= '>'.$field->{'title'}.'</th>';
                     $awarded = $cache->{$name.      }
                                         "$Version:$problem:resource.".      $Str .= '</tr>';
                                         "$part.awarded"};      return $Str;
                     $partData{$part.':awarded'} = ($awarded) ? $awarded : 0;  }
                     $totalAwarded += $awarded;  
   
                     $tries = $cache->{$name.":$Version:$problem".  
                                       ":resource.$part.tries"};  
                     $partData{$part.':tries'} = ($tries) ? $tries : 0;  
                     $partData{$part.':wrong'} = $partData{$part.':tries'};  
                     $totalTries += $tries;  
   
                     my $val = $cache->{$name.":$Version:$problem".  
                                        ":resource.$part.solved"};  
                     if    ($val eq 'correct_by_student')   {$code = 'C';}   
                     elsif ($val eq 'correct_by_override')  {$code = 'O';}  
                     elsif ($val eq 'incorrect_attempted')  {$code = 'I';}   
                     elsif ($val eq 'incorrect_by_override'){$code = 'I';}  
                     elsif ($val eq 'excused')              {$code = 'x';}  
                     $partData{$part.':code'}=$code;  
                     if($partData{$part.':wrong'} ne 0 &&   
                        ($code eq 'C' || $code eq 'O')) {  
                         $partData{$part.':wrong'}--;  
                     }  
                 }  
             }  
   
             # Loop through all the parts for the current problem in the   
             # correct order and prepare the output  
             foreach (split(/\:/,$cache->{$sequence.':'.$problemID.  
                                          ':parts'})) {  
     #$r->print("<br>".$cache->{$sequence.':title'});  
   
                 my $Yes = 0;  
                 if($partData{$_.':code'} eq 'C' ||   
                    $partData{$_.':code'} eq 'O') {  
                     $Yes=1;  
                 }  
                 my $ptr = $tempProblemOrder.'&'.$problemID;  
   
                 if($_ > 1) {  sub sequence_html_output {
                     $ptr .= "*(part $_)";      my ($seq) = @_;
                     $Dis .= '&';      my $data = $SeqStat{$seq->symb};
                 }      my $row = '<tr>';
       foreach my $field (@SeqFields) {
           next if ($field->{'selected'} ne 'yes');
           $row .= '<td bgcolor="'.$field->{'color'}.'"';
           if (exists($field->{'align'})) {
               $row .= ' align="'.$field->{'align'}.'"';
           }
           $row .= '>';
           if (exists($field->{'format'})) {
               $row .= sprintf($field->{'format'},$data->{$field->{'name'}});
           } else {
               $row .= $data->{$field->{'name'}};
           }
           $row .= '</td>';
       }
       $row .= '</tr>'."\n";
       return $row;
   }
   
  my ($pr_no,$dod)=split('&',$ptr);  ####################################################
 # my $DoDiff=$DoDiff->{$dod};  ####################################################
 #               $r->print('<br>'.$name.'---'.$ptr.'==='.$DoDiff);  ##
   ##    Plotting Routines
                 my $Fac = ($partData{$_.':Tries'}) ?   ##
                     ($partData{$_.':awarded'}/$partData{$_.':tries'}) : 0;  ####################################################
                 my $DisF;  ####################################################
                 if($Fac > 0 &&  $Fac < 1) {   sub make_plot {
                     $DisF = sprintf( "%.4f", $Fac );      my ($r,$plot) = @_;
                 } else {      &compute_all_statistics($r);
                     $DisF = $Fac;      &sort_data($env{'form.sortby'});
                 }      if ($plot eq 'degrees') {
           &degrees_plot($r);
       } elsif ($plot eq 'tries statistics') {
           &tries_data_plot($r);
       } else {
           &make_single_stat_plot($r,$plot);
       }
       return;
   }
   
                 if ($Discuss->{"$name:$problem"}) {  sub make_single_stat_plot {
     $TotDiscuss++;      my ($r,$datafield) = @_;
                     $Discussed=1;      #
                 }      my $title; my $yaxis;
                 my $time = $cache->{"$name:$LatestVersion:$problem:timestamp"};      foreach my $field (@Fields) {
                 $Dis .= $tempProblemOrder.'='.$DisF.'+'.$Yes;          next if ($field->{'name'} ne $datafield);
                 $ptr .= '&'.$partData{$_.'.tries'}.          $title = $field->{'long_title'};
                         '&'.$partData{$_.'.wrongs'}.          $yaxis = $field->{'title'};
                         '&'.$partData{$_.'.code'};          last;
                 push (@$list, $ptr."&$Discussed");      }
                 $r->print('<br>'.$name.'---'.$ptr);      if ($title eq '' || $yaxis eq '') {
           # datafield is something we do not know enough about to plot
 #### if ($DoDiff>0.85) {          $r->print('<h3>'.
                     &mt('Unable to plot the requested statistic.').
                     '</h3>');
           return;
       }
       #
       # Build up the data sets to plot
       my @Labels; 
       my @Data;
       my $max = 1;
       foreach my $data (@StatsArray) {
           push(@Labels,$data->{'problem_num'});
           push(@Data,$data->{$datafield});
           if ($data->{$datafield}>$max) {
               $max = $data->{$datafield};
           }
       }
       foreach (1,2,3,4,5,10,15,20,25,40,50,75,100,150,200,250,300,500,600,750,
                1000,1500,2000,2500,3000,3500,4000,5000,7500,10000,15000,20000) {
           if ($max <= $_) {
               $max = $_;
               last;
           }
       }
       if ($max > 20000) {
           $max = 10000*(int($max/10000)+1);
       }
       #
       $r->print("<p>".&Apache::loncommon::DrawBarGraph($title,
                                                        'Problem Number',
                                                        $yaxis,
                                                        $max,
                                                        undef, # colors
                                                        \@Labels,
                                                        \@Data)."</p>\n");
       return;
   }
   
   sub degrees_plot {
       my ($r)=@_;
       my $count = scalar(@StatsArray);
       my $width = 50 + 10*$count;
       $width = 300 if ($width < 300);
       my $height = 300;
       my $plot = '';
       my $ymax = 0;
       my $ymin = 0;
       my @Disc; my @Diff; my @Labels;    
       foreach my $data (@StatsArray) {
           push(@Labels,$data->{'problem_num'});
           my $disc = $data->{'deg_of_disc'};
           my $diff = $data->{'deg_of_diff'};
           push(@Disc,$disc);
           push(@Diff,$diff);
           #
           $ymin = $disc if ($ymin > $disc);
           $ymin = $diff if ($ymin > $diff);
           $ymax = $disc if ($ymax < $disc);
           $ymax = $diff if ($ymax < $diff);
       }
       #
       # Make sure we show relevant information.
       if ($ymin < 0) {
           if (abs($ymin) < 0.05) {
               $ymin = 0;
           } else {
               $ymin = -1;
           }
       }
       if ($ymax > 0) {
           if (abs($ymax) < 0.05) {
               $ymax = 0;
           } else {
               $ymax = 1;
           }
       }
       #
       my $xmax = $Labels[-1];
       if ($xmax > 50) {
           if ($xmax % 10 != 0) {
               $xmax = 10 * (int($xmax/10)+1);
           }
       } else {
           if ($xmax % 5 != 0) {
               $xmax = 5 * (int($xmax/5)+1);
           }
       }
       #
       my $discdata .= '<data>'.join(',',@Labels).'</data>'.$/.
                       '<data>'.join(',',@Disc).'</data>'.$/;
       #
       my $diffdata .= '<data>'.join(',',@Labels).'</data>'.$/.
                       '<data>'.join(',',@Diff).'</data>'.$/;
       #
       my $title = 'Degree of Discrimination\nand Degree of Difficulty';
       if ($xmax > 50) {
           $title = 'Degree of Discrimination and Degree of Difficulty';
       }
       #
       $plot=<<"END";
   <gnuplot 
       texfont="10"
       fgcolor="x000000"
       plottype="Cartesian"
       font="large"
       grid="on"
       align="center"
       border="on"
       transparent="on"
       alttag="Degree of Discrimination and Degree of Difficulty Plot"
       samples="100"
       bgcolor="xffffff"
       height="$height"
       width="$width">
       <key 
           pos="top right"
           title=""
           box="off" />
       <title>$title</title>
       <axis xmin="0" ymin="$ymin" xmax="$xmax" ymax="$ymax" color="x000000" />
       <xlabel>Problem Number</xlabel>
       <curve 
           linestyle="linespoints" 
           name="DoDisc" 
           pointtype="0" 
           color="x000000">
           $discdata
       </curve>
       <curve 
           linestyle="linespoints" 
           name="DoDiff" 
           pointtype="0" 
           color="xFF0000">
           $diffdata
       </curve>
   </gnuplot>
   END
       my $plotresult = 
           '<p>'.&Apache::lonxml::xmlparse($r,'web',$plot).'</p>'.$/;
       $r->print($plotresult);
       return;
   }
   
                 $TimeTot += $time;  sub tries_data_plot {
       my ($r)=@_;
       my $count = scalar(@StatsArray);
       my $width = 50 + 10*$count;
       $width = 300 if ($width < 300);
       my $height = 300;
       my $plot = '';
       my @STD;  my @Mean; my @Max; my @Min;
       my @Labels;
       my $ymax = 5;
       foreach my $data (@StatsArray) {
           my $max = $data->{'mean_tries'} + $data->{'std_tries'};
           $ymax = $max if ($ymax < $max);
           $ymax = $max if ($ymax < $max);
           push(@Labels,$data->{'problem_num'});
           push(@STD,$data->{'std_tries'});
           push(@Mean,$data->{'mean_tries'});
       }
       #
       # Make sure we show relevant information.
       my $xmax = $Labels[-1];
       if ($xmax > 50) {
           if ($xmax % 10 != 0) {
               $xmax = 10 * (int($xmax/10)+1);
           }
       } else {
           if ($xmax % 5 != 0) {
               $xmax = 5 * (int($xmax/5)+1);
           }
       }
       $ymax = int($ymax)+1+2;
       #
       my $std_data .= '<data>'.join(',',@Labels).'</data>'.$/.
                       '<data>'.join(',',@Mean).'</data>'.$/;
       #
       my $std_error_data .= '<data>'.join(',',@Labels).'</data>'.$/.
                             '<data>'.join(',',@Mean).'</data>'.$/.
                             '<data>'.join(',',@STD).'</data>'.$/;
       #
       my $title = 'Mean and S.D. of Tries';
       if ($xmax > 25) {
           $title = 'Mean and Standard Deviation of Tries';
       }
       #
       $plot=<<"END";
   <gnuplot 
       texfont="10"
       fgcolor="x000000"
       plottype="Cartesian"
       font="large"
       grid="on"
       align="center"
       border="on"
       transparent="on"
       alttag="Mean and S.D of Tries Plot"
       samples="100"
       bgcolor="xffffff"
       height="$height"
       width="$width">
       <title>$title</title>
       <axis xmin="0" ymin="0" xmax="$xmax" ymax="$ymax" color="x000000" />
       <xlabel>Problem Number</xlabel>
       <ylabel>Number of Tries</ylabel>
       <curve 
           linestyle="yerrorbars"
           name="S.D. Tries" 
           pointtype="1" 
           color="x666666">
           $std_error_data
       </curve>
       <curve 
           linestyle="points"
           name="Mean Tries" 
           pointtype="1" 
           color="xCC4444">
           $std_data
       </curve>
   </gnuplot>
   END
       my $plotresult = 
           '<p>'.&Apache::lonxml::xmlparse($r,'web',$plot).'</p>'.$/;
       $r->print($plotresult);
       return;
   }
   
                 if ($Yes==1 && $partData{$_.'.Tries'}==1) {  sub plot_dropdown {
     $TotFirst++;      my $current = '';
                 }      #
 # my $Acts= $Activity->{$name.':'.$problem};      if (defined($env{'form.plot'})) {
 # if ($Acts) {          $current = $env{'form.plot'};
 #    my $Pt=&ProcAct( $Acts, $time );      }
     #my ($spe,$beg) = split(/\+/,$Pt);      #
 #                    my $spe= $Pt;      my @Additional_Plots = (
 #    if ($Yes==1) {$spent_yes += $spe;}                              { graphable=>'yes',
 #    $spent += $spe;                                name => 'degrees',
     #$Beg += $beg;                                title => 'Difficulty Indexes' },
 #                   $r->print('<br>'.$name.'---'.$problem.'---'.$spe);                              { graphable=>'yes',
 # }                                name => 'tries statistics',
  $TotalOpend++;                                title => 'Tries Statistics' });
  $ProbTot++;      #
       my $Str= "\n".'<select name="plot" size="1">';
       $Str .= '<option name="none"></option>'."\n";
       $Str .= '<option name="none2">none</option>'."\n";
       foreach my $field (@Additional_Plots,@Fields) {
           if (! exists($field->{'graphable'}) ||
               $field->{'graphable'} ne 'yes') {
               next;
           }
           $Str .= '<option value="'.$field->{'name'}.'"';
           if ($field->{'name'} eq $current) {
               $Str .= ' selected ';
           }
           $Str.= '>'.&mt($field->{'title'}).'</option>'."\n";
       }
       $Str .= '</select>'."\n";
       return $Str;
   }
   
                 $tempProblemOrder++;  ###############################################
   ###############################################
   ##
   ## Excel output routines
   ##
   ###############################################
   ###############################################
   sub Excel_output {
       my ($r) = @_;
       $r->print('<h2>'.&mt('Preparing Excel Spreadsheet').'</h2>');
       ##
       ## Compute the statistics
       &compute_all_statistics($r);
       my $c = $r->connection;
       return if ($c->aborted());
       #
       my ($starttime,$endtime) = &Apache::lonstathelpers::get_time_limits();
       ##
       ## Create the excel workbook
       my ($excel_workbook,$filename,$format) =
           &Apache::loncommon::create_workbook($r);
       return if (! defined($excel_workbook));
       #
       # Add a worksheet
       my $sheetname = $env{'course.'.$env{'request.course.id'}.'.description'};
       if (length($sheetname) > 31) {
           $sheetname = substr($sheetname,0,31);
       }
       my $excel_sheet = $excel_workbook->addworksheet(
           &Apache::loncommon::clean_excel_name($sheetname));
       ##
       ## Begin creating excel sheet
       ##
       my ($rows_output,$cols_output) = (0,0);
       #
       # Put the course description in the header
       $excel_sheet->write($rows_output,$cols_output++,
                      $env{'course.'.$env{'request.course.id'}.'.description'},
                           $format->{'h1'});
       $cols_output += 3;
       #
       # Put a description of the sections listed
       my $sectionstring = '';
       $excel_sheet->write($rows_output,$cols_output++,
                           &Apache::lonstatistics::section_and_enrollment_description('plaintext'),
                           $format->{'h3'});
       $cols_output += scalar(&Apache::lonstatistics::get_selected_sections());
       $cols_output += scalar(&Apache::lonstatistics::get_selected_groups());
       #
       # Time restrictions
       my $time_string;
       if (defined($starttime)) {
           # call localtime but not lonlocal:locallocaltime because excel probably
           # cannot handle localized text.  Probably.
           $time_string .= 'Data collected from '.localtime($time_string);
           if (defined($endtime)) {
               $time_string .= ' to '.localtime($endtime);
           }
           $time_string .= '.';
       } elsif (defined($endtime)) {
           # See note above about lonlocal:locallocaltime
           $time_string .= 'Data collected before '.localtime($endtime).'.';
       }
       if (defined($time_string)) {
           $excel_sheet->write($rows_output,$cols_output++,$time_string);
           $cols_output+= 5;
       }
       #
       # Put the date in there too
       $excel_sheet->write($rows_output,$cols_output++,
                           'Compiled on '.localtime(time));
       #
       $rows_output++; 
       $cols_output=0;
       ##
       ## Sequence Statistics
       ## 
       &write_headers($excel_sheet,$format,\$rows_output,\$cols_output,
                      \@SeqFields);
       foreach my $seq (@sequences) {
           my $data = $SeqStat{$seq->symb};
           $cols_output=0;
           foreach my $field (@SeqFields) {
               next if ($field->{'selected'} ne 'yes');
               my $fieldformat = undef;
               if (exists($field->{'excel_format'})) {
                   $fieldformat = $format->{$field->{'excel_format'}};
               }
               $excel_sheet->write($rows_output,$cols_output++,
                                   $data->{$field->{'name'}},$fieldformat);
           }
           $rows_output++;
           $cols_output=0;
       }
       ##
       ## Resource Statistics
       ##
       $rows_output++;
       $cols_output=0;
       &write_headers($excel_sheet,$format,\$rows_output,\$cols_output,
                      \@Fields);
       #
       foreach my $data (@StatsArray) {
           $cols_output=0;
           foreach my $field (@Fields) {
               next if ($field->{'selected'} ne 'yes');
               next if ($field->{'name'} eq 'problem_num');
               my $fieldformat = undef;
               if (exists($field->{'excel_format'})) {
                   $fieldformat = $format->{$field->{'excel_format'}};
             }              }
               $excel_sheet->write($rows_output,$cols_output++,
                                   $data->{$field->{'name'}},$fieldformat);
         }          }
  my $pstr;          $rows_output++;
         if($totalTries) {          $cols_output=0;
     my $DisFac = ($totalAwarded/$totalTries);  
     my $DisFactor = sprintf( "%.4f", $DisFac );  
             my $TS = sprintf( "%.2f", $spent );  
             my $TS_yes = sprintf( "%.2f", $spent_yes );  
    # $DiscFac{$DisFactor}=$Dis;  
     $pstr=$DisFactor.':'.$name.':'.$ProbTot.':'.$TotalOpend.':'.  
                   $totalTries.':'.$ProbSolved.':'.$TotFirst.':'.  
                   $TS_yes.':'.$TS.':'.$TotDiscuss;  
     $discriminantFactor{$pstr}=$Dis;  
  }  
     }      }
       #
       $excel_workbook->close();
       #
       # Tell the user where to get their excel file
       $r->print('<br />'.
                 '<a href="'.$filename.'">'.
                 &mt('Your Excel Spreadsheet').'</a>'."\n");
       $r->rflush();
       return;
   }
   
     return (\%discriminantFactor);  ##
   ## &write_headers
   ##
   sub write_headers {
       my ($excel_sheet,$format,$rows_output,$cols_output,$Fields) = @_;
       ##
       ## First the long titles
       foreach my $field (@{$Fields}) {
           next if ($field->{'name'} eq 'problem_num');
           next if ($field->{'selected'} ne 'yes');
           if (exists($field->{'long_title'})) {
               $excel_sheet->write($$rows_output,${$cols_output},
                                   $field->{'long_title'},
                                   $format->{'bold'});
           } else {
               $excel_sheet->write($$rows_output,${$cols_output},'');
           }
           ${$cols_output}+= 1;
       }
       ${$cols_output} =0;
       ${$rows_output}+=1;
       ##
       ## Then the short titles
       foreach my $field (@{$Fields}) {
           next if ($field->{'selected'} ne 'yes');
           next if ($field->{'name'} eq 'problem_num');
           # Use english for excel as I am not sure how well excel handles 
           # other character sets....
           $excel_sheet->write($$rows_output,$$cols_output,
                               $field->{'title'},
                               $format->{'bold'});
           $$cols_output+=1;
       }
       ${$cols_output} =0;
       ${$rows_output}+=1;
       return;
 }  }
   
   ##################################################
   ##################################################
   ##
   ## Statistics Gathering and Manipulation Routines
   ##
   ##################################################
   ##################################################
   sub compute_statistics_on_sequence {
       my ($seq) = @_;
       my @Data;
       foreach my $res (&Apache::lonstathelpers::get_resources($navmap,$seq)) {
           foreach my $part (@{$res->parts}) {
               next if ($res->is_survey($part));
               #
               # This is where all the work happens
               my $data = &get_statistics($seq,$res,$part,scalar(@StatsArray)+1);
               push (@Data,$data);
               push (@StatsArray,$data);
           }
       }
       return @Data;
   }
   
 =pod  sub compute_all_statistics {
 sub MySort {                my ($r) = @_;
     if ( $Pos > 0 ) {      if (@StatsArray > 0) {
  if ($ENV{'form.order'} eq 'Descending') {$b <=> $a;}          # Assume we have already computed the statistics
  else { $a <=> $b; }          return;
     }      }
     else {      my $c = $r->connection;
  if ($ENV{'form.order'} eq 'Descending') {$b cmp $a;}      foreach my $seq (@sequences) {
  else { $a cmp $b; }          last if ($c->aborted);
           &compute_sequence_statistics($seq);
           &compute_statistics_on_sequence($seq);
     }      }
 }  }
 =cut  
   
   sub sort_data {
       my ($sortkey) = @_;
       return if (! @StatsArray);
       #
       # Sort the data
       my $sortby = undef;
       foreach my $field (@Fields) {
           if ($sortkey eq $field->{'name'}) {
               $sortby = $field->{'name'};
           }
       }
       if (! defined($sortby) || $sortby eq '' || $sortby eq 'problem_num') {
           $sortby = 'container';
       }
       if ($sortby ne 'container') {
           # $sortby is already defined, so we can charge ahead
           if ($sortby =~ /^(title|part)$/i) {
               # Alpha comparison
               @StatsArray = sort {
                   lc($a->{$sortby}) cmp lc($b->{$sortby}) ||
                   lc($a->{'title'}) cmp lc($b->{'title'}) ||
                   lc($a->{'part'}) cmp lc($b->{'part'});
               } @StatsArray;
           } else {
               # Numerical comparison
               @StatsArray = sort {
                   my $retvalue = 0;
                   if ($b->{$sortby} eq 'nan') {
                       if ($a->{$sortby} ne 'nan') {
                           $retvalue = -1;
                       } else {
                           $retvalue = 0;
                       }
                   }
                   if ($a->{$sortby} eq 'nan') {
                       if ($b->{$sortby} ne 'nan') {
                           $retvalue = 1;
                       }
                   }
                   if ($retvalue eq '0') {
                       $retvalue = $b->{$sortby} <=> $a->{$sortby}     ||
                               lc($a->{'title'}) <=> lc($b->{'title'}) ||
                               lc($a->{'part'})  <=> lc($b->{'part'});
                   }
                   $retvalue;
               } @StatsArray;
           }
       }
       #
       # Renumber the data set
       my $count;
       foreach my $data (@StatsArray) {
           $data->{'problem_num'} = ++$count;
       }
       return;
   }
   
 sub BuildStatisticsTable {  ########################################################
     my ($cache,$discriminantFactor,$list,$headings,$students,$r,%color)=@_;  ########################################################
   
 #6666666  
 #    my $file="/home/httpd/perl/tmp/183d.txt";  
 #    open(OUT, ">$file");  
 #6666666  
 ##     &Apache::lonstatistics::Create_PrgWin($r);  
 ##777777  
 ##    my (%Activity) = &LoadActivityLog();  
 ##    $r->print('<script>popwin.document.popremain.remaining.value="'.  
 ##              'Loading Discussion...";</script>');  
 ##    my ($doDiffFile) = &LoadDoDiffFile();  
   
     my $p_count = 0;  
     my $nIndex = 0;  
     my $dummy;  
     my $p_val;  
     my $ResId;  
     my %GraphDat;  
     my $NoElements = scalar @$list;  
   
   
     foreach my $sequence (split(':', $cache->{'orderedSequences'})) {  
   
  &CreateProblemStatisticsTableHeading($cache->{'DisplayFormat'},  
                                              $cache->{$sequence.':source'},   
                                              $cache->{$sequence.':title'},  
                                              $headings,$r);  
   
         foreach my $problemID (split(':', $cache->{$sequence.':problems'})) {  
             my $problem = $cache->{$problemID.':problem'};  
     $r->print("<br>$problem");  
   
  }  
     }    
 }  
 =pod  =pod
 ##777777  
 ##    $Str .= &Classify($discriminantFactor, $students);  
   
   =item &get_statistics()
   
  my ($Hid,$pr)=split(/\:/,$mapsort{$_});  Wrapper routine from the call to loncoursedata::get_problem_statistics.  
  my @lpr=split(/\&/,$pr);  Calls lonstathelpers::get_time_limits() to limit the data set by time
  for (my $i=1; $i<=$#lpr; $i++) {  and &compute_discrimination_factor
     my %storestats=();  
     my ($PrOrd,$Prob,$Tries,$Wrongs,$Code,$Disc)=split(/\&/,$list->[$nIndex]);  
     my $Temp = $Prob;  
     my $MxTries = 0;  
     my $TotalTries = 0;  
     my $YES = 0;  
     my $Incorrect = 0;  
     my $Override = 0;  
     my $StdNo = 0;  
     my $DiscNo=0;  
     my @StdLst;  
     while ( $PrOrd == $lpr[$i] )   
     {  
  $nIndex++;  
  $StdNo++;  
  $StdLst[ $StdNo ] = $Tries;  
  $TotalTries += $Tries;  
  if ( $MxTries < $Tries ) { $MxTries = $Tries; }   
  if ( $Code eq 'C' ){ $YES++; }  
  elsif( $Code eq 'I' ) { $Incorrect++; }  
  elsif( $Code eq 'O' ) { $Override++; }  
  elsif( $Code eq 'U' ) { $StdNo--; }  
  ($PrOrd,$Prob,$Tries,$Wrongs,$Code,$Disc)=split(/\&/,$list->[$nIndex]);  
     }  
   
     $p_count++;  
     my $Dummy;  
     ($ResId,$Dummy)=split(/\*/,$Temp);  
   
     $Temp = '<a href="'.$cache->{'src_'.$ResId}.  
                 '" target="_blank">'.$cache->{'title_'.$ResId}.$Dummy.'</a>';  
   
     my $res = &Apache::lonnet::declutter($cache->{'src_'.$ResId});  
     my $urlres=$res;  
   
     $ResId=~/(\d+)\.(\d+)/;  
     my $Map = &Apache::lonnet::declutter( $cache->{'map_id_'.$1} );  
     $urlres=$Map;  
    
     $res = '<a href="'.$cache->{'src_'.$ResId}.'">'.$res.'</a>';  
     #$Map = '<a href="'.$Map.'">'.$res.'</a>';  
   
 #------------------------ Compute the Average of Tries about one problem  Inputs: $sequence, $resource, $part, $problem_num
     my $Average = ($StdNo) ? $TotalTries/$StdNo : 0;  
   Returns: Hash reference with statistics data from 
   loncoursedata::get_problem_statistics.
   
     $storestats{$ENV{'request.course.id'}.'___'.$urlres.'___timestamp'}=time;         
     $storestats{$ENV{'request.course.id'}.'___'.$urlres.'___stdno'}=$StdNo;  
     $storestats{$ENV{'request.course.id'}.'___'.$urlres.'___avetries'}=$Average;  
      
 #-------------------------------- Compute percentage of Wrong tries  
     my $Wrong = ( $StdNo ) ? 100 * ( $Incorrect / $StdNo ) : 0;  
   
 #-------------------------------- Compute Standard Deviation  
     my $StdDev = 0;   
     if ( $StdNo > 1 ) {  
  for ( my $n = 0; $n < $StdNo; $n++ ) {  
     my $Dif = $StdLst[ $n ]-$Average;  
     $StdDev += $Dif*$Dif;  
  }   
  $StdDev /= ( $StdNo - 1 );  
  $StdDev = sqrt( $StdDev );  
     }  
   
 #-------------------------------- Compute Degree of Difficulty  
     my $DoDiff = 0;  
     if( $TotalTries > 0 ) {  
  $DoDiff = 1 - ( ( $YES + $Override ) / $TotalTries );  
 #    $DoDiff =  ($TotalTries)/($YES + $Override+ 0.1);      
     }  
          
     $storestats{$ENV{'request.course.id'}.'___'.$urlres.'___difficulty'}=$DoDiff;  
   
 #-------------------------------- Compute the Skewness  
     my $Skewness = 0;  
     my $Sum = 0;   
     if ( $StdNo > 0 && $StdDev > 0 ) {  
  for ( my $n = 0; $n < $StdNo; $n++ ) {  
     my $Dif = $StdLst[ $n ]-$Average;  
     $Skewness += $Dif*$Dif*$Dif;  
  }   
  $Skewness /= $StdNo;  
  $Skewness /= $StdDev*$StdDev*$StdDev;  
     }  
   
 #--------------------- Compute the Discrimination Factors  
             my ($Up1,$Up2)=split(/\:/,':');#jason$DisUp->{$lpr[$i]});  
     my ($Lw1,$Lw2)=split(/\:/,':');#jason$DisLow->{$lpr[$i]});  
     my $Dis1 = $Up1 - $Lw1;  
     my $Dis2 = $Up2 - $Lw2;  
     my $_D1 = sprintf("%.2f", $Dis1);  
     my $_D2 = sprintf("%.2f", $Dis2);  
   
 #-----------------  Some restition in presenting the float numbers  
     my $Avg = sprintf( "%.2f", $Average );  
     my $Wrng = sprintf( "%.1f", $Wrong );  
     my $SD = sprintf( "%.1f", $StdDev );  
     my $DoD = sprintf( "%.2f", $DoDiff );  
     my $Sk = sprintf( "%.1f", $Skewness );  
     my $join = $lpr[$i].'&'.$Temp.'&'.$StdNo.'&'.  
                        $TotalTries.'&'.$MxTries.'&'.$Avg.'&'.  
                        $YES.'&'.$Override.'&'.$Wrng.'&'.$DoD.'&'.  
        $SD.'&'.$Sk.'&'.$_D1.'&'.$_D2.'&'.  
                        $DiscNo.'&'.$Prob;  
 ##8888    $CachData{($p_count-1)}=$join;  
   
 #6666666  
 #    $r->print('<br>'.$out.'&'.$DoD);  
 #            print (OUT $out.'@'.$DoD.'&');  
 #6666666  
   
     $urlres=~/^(\w+)\/(\w+)/;  
     if ($StdNo) {   
  &Apache::lonnet::put('resevaldata',\%storestats,$1,$2);   
     }  
 #-------------------------------- Row of statistical table  
             &TableRow($cache,$join,$i,($p_count-1),$r,\%color,\%GraphDat);  
  }  
  &CloseTable($cache,$r);  
     }  
     &Close_PrgWin();  
 #666666  
 #    close( OUT );  
 #666666  
 }  
 =cut  =cut
   
 =pod  ########################################################
 sub Cache_Statistics {  ########################################################
     my ($cache,$color)=@_;  sub get_statistics {
     my @list = ();      my ($sequence,$resource,$part,$problem_num) = @_;
     my $Useful;      #
     my $UnUseful;      my ($starttime,$endtime) = &Apache::lonstathelpers::get_time_limits();
 #    $r->print('<input type="hidden" name="show" value="excel" />'."\n");       my $symb = $resource->symb;
     my %myHeader = reverse( %Header );      my $courseid = $env{'request.course.id'};
     $Pos = $myHeader{$ENV{'form.sort'}};      #
     if ($Pos > 0) {$Pos++;}      my $data = &Apache::loncoursedata::get_problem_statistics
     my $p_count = 0;                          ([&Apache::lonstatistics::get_selected_sections()],
     foreach my $key( keys %CachData) {                            [&Apache::lonstatistics::get_selected_groups()],
  my @Temp=split(/\&/,$CachData{$key});                           $Apache::lonstatistics::enrollment_status,
  if ( $Pos == 0 ) {                           $symb,$part,$courseid,$starttime,$endtime);
     ($UnUseful,$Useful)=split(/\>/,$Temp[$Pos]);      $data->{'symb'}        = $symb;
  }      $data->{'part'}        = $part;
  else {      $data->{'problem_num'} = $problem_num;
     $Useful = $Temp[$Pos];      $data->{'container'}   = $sequence->compTitle;
  }         $data->{'title'}       = $resource->compTitle;
  $list[$p_count]=$Useful.'@'.$CachData{$key};      $data->{'title.link'}  = $resource->src.'?symb='.
         $p_count++;          &escape($resource->symb);
     }      #
       if ($SelectedFields{'deg_of_disc'}) {
     @list = sort MySort (@list);          $data->{'deg_of_disc'} = 
               &compute_discrimination_factor($resource,$part,$sequence);
     my $nIndex=0;      }
       #
     if ( $Pos == 0 ) {      # Store in metadata if computations were done for all students
  foreach (sort keys %mapsort) {      if ($data->{'num_students'} > 1) {
     my ($Hid,$pr)=split(/\:/,$mapsort{$_});          my @Sections = &Apache::lonstatistics::get_selected_sections();
     &CreateProblemStatisticsTableHeading($cache,1,$Hid);          my $sections = '"'.join(' ',@Sections).'"';
     my @lpr=split(/\&/,$pr);          $sections =~ s/&+/_/g;  # Ensure no special characters
     for (my $i=1; $i<=$#lpr; $i++) {          $data->{'sections'}=$sections;
  my($Pre, $Post) = split(/\@/,$list[$nIndex]);           $data->{'course'} = $env{'request.course.id'};
  #$r->print('<br>'.$Pre.'---'.$Post);          my $urlres=(&Apache::lonnet::decode_symb($resource->symb))[2];
  &TableRow($cache,$Post,$i,$nIndex,\%color,\%GraphDat);          $data->{'urlres'}=$urlres;
  $nIndex++;          my %storestats = 
     }              &LONCAPA::lonmetadata::dynamic_metadata_storage($data);
     &CloseTable($cache);          my ($dom,$user) = $urlres=~/^(\w+)\/(\w+)/; 
  }          &Apache::lonnet::put('nohist_resevaldata',\%storestats,$dom,$user);
     }      }
     else {      #
  &CreateProblemStatisticsTableHeading($cache,0);      $data->{'tries_per_correct'} = $data->{'tries'} / 
  for ( my $nIndex = 0; $nIndex < $p_count; $nIndex++ ) {          ($data->{'num_solved'}+0.1);
     my($Pre, $Post) = split(/\@/,$list[$nIndex]);       #
     &TableRow($cache,$Post,$nIndex,$nIndex,\%color,\%GraphDat);      # Get the due date for research purposes (commented out most of the time)
  }   #    $data->{'duedate'} = 
  &CloseTable($cache);  #        &Apache::lonnet::EXT('resource.'.$part.'.duedate',$symb);
     }  #    $data->{'opendate'} = 
 }  #        &Apache::lonnet::EXT('resource.'.$part.'.opendate',$symb);
 =cut   #    $data->{'maxtries'} = 
   #        &Apache::lonnet::EXT('resource.'.$part.'.maxtries',$symb);
 sub TableRow {  #    $data->{'hinttries'} =
     my ($cache,$Str,$Idx,$RealIdx,$r,$color,$GraphDat)=@_;  #        &Apache::lonnet::EXT('resource.'.$part.'.hinttries',$symb);
     my($PrOrd,$Temp,$StdNo,$TotalTries,$MxTries,$Avg,$YES,$Override,      $data->{'weight'} =
        $Wrng,$DoD,$SD,$Sk,$_D1,$_D2,$DiscNo,$Prob)=split(/\&/,$Str);          &Apache::lonnet::EXT('resource.'.$part.'.weight',$symb);
     if ($ENV{'form.showcsv'}) {  #    $data->{'resptypes'} = join(',',@{$resource->{'partdata'}->{$part}->{'ResponseTypes'}});
         my ($ResId,$Dummy)=split(/\*/,$Prob);      return $data;
         my $Ptr =  "\n".'<br>'.  
                "\n".'"'.($RealIdx+1).'",'.  
                "\n".'"'.$cache->{'title_'.$ResId}.$Dummy.'",'.  
                "\n".'"'.$cache->{'src_'.$ResId}.'",'.  
                "\n".'"'.$StdNo.'",'.  
                "\n".'"'.$TotalTries.'",'.  
                "\n".'"'.$MxTries.'",'.  
                "\n".'"'.$Avg.'",'.  
                "\n".'"'.$YES.'",'.  
                "\n".'"'.$Override.'",'.  
                "\n".'"'.$Wrng.'",'.  
                "\n".'"'.$DoD.'",'.  
                "\n".'"'.$SD.'",'.  
                "\n".'"'.$Sk.'",'.  
                "\n".'"'.$_D1.'",'.  
        "\n".'"'.$_D2.'"'.  
        "\n".'"'.$DiscNo.'"';  
   
         $r->print("\n".$Ptr);  
     }  
     else{  
         my $Ptr =  "\n".'<tr>'.  
                "\n".'<td>'.($RealIdx+1).'</td>'.  
           #     "\n".'<td>'.$PrOrd.$Temp.'</td>'.  
                "\n".'<td>'.$Temp.'</td>'.  
                "\n".'<td bgcolor='.$color->{"yellow"}.'> '.$StdNo.'</td>'.  
                "\n".'<td bgcolor='.$color->{"yellow"}.'>'.$TotalTries.'</td>'.  
                "\n".'<td bgcolor='.$color->{"yellow"}.'>'.$MxTries.'</td>'.  
                "\n".'<td bgcolor='.$color->{"gb"}.'>'.$Avg.'</td>'.  
                "\n".'<td bgcolor='.$color->{"gb"}.'> '.$YES.'</td>'.  
                "\n".'<td bgcolor='.$color->{"gb"}.'> '.$Override.'</td>'.  
                "\n".'<td bgcolor='.$color->{"red"}.'> '.$Wrng.'</td>'.  
                "\n".'<td bgcolor='.$color->{"red"}.'> '.$DoD.'</td>'.  
                "\n".'<td bgcolor='.$color->{"green"}.'> '.$SD.'</td>'.  
                "\n".'<td bgcolor='.$color->{"green"}.'> '.$Sk.'</td>'.  
                "\n".'<td bgcolor='.$color->{"purple"}.'> '.$_D1.'</td>'.  
        "\n".'<td bgcolor='.$color->{"purple"}.'> '.$_D2.'</td>'.  
                "\n".'<td bgcolor='.$color->{"yellow"}.'> '.$DiscNo.'</td>';  
         $r->print("\n".$Ptr.'</tr>' );  
     }  
     $GraphDat->{$RealIdx}=$DoD.':'.$Wrng;  
 }  
   
 sub StatusOptions {  
     my ($cache)=@_;  
   
     my $Status = $cache->{'Status'};  
     my $OpSel1 = '';  
     my $OpSel2 = '';  
     my $OpSel3 = '';  
   
     if($Status eq 'Any')         { $OpSel3 = ' selected'; }  
     elsif($Status eq 'Expired' ) { $OpSel2 = ' selected'; }  
     else                         { $OpSel1 = ' selected'; }  
   
     my $Ptr = '';  
     $Ptr .= '<tr><td align="right"><b>Student Status:</b></td>'."\n";  
     $Ptr .= '<td align="left"><select name="Status">';  
     $Ptr .= '<option'.$OpSel1.'>Active</option>'."\n";  
     $Ptr .= '<option'.$OpSel2.'>Expired</option>'."\n";  
     $Ptr .= '<option'.$OpSel3.'>Any</option>'."\n";  
     $Ptr .= '</select></td></tr>'."\n";  
   
     return $Ptr;  
 }  }
   
 sub AscendOrderOptions {  ###############################################
     my ($order)=@_;  ###############################################
   
   =pod
   
   =item &compute_discrimination_factor()
   
   Inputs: $Resource, $Sequence
   
     my $OpSel1 = '';  Returns: integer between -1 and 1
     my $OpSel2 = '';  
   
     if($order eq 'Ascending') {  =cut
         $OpSel1 = ' selected';  
   ###############################################
   ###############################################
   sub compute_discrimination_factor {
       my ($resource,$part,$seq) = @_;
       my $symb = $resource->symb;
       my @Resources;
       foreach my $res (&Apache::lonstathelpers::get_resources($navmap,$seq)){
           next if ($res->symb eq $symb);
           push (@Resources,$res->symb);
       }
       #
       # rank
       my ($starttime,$endtime) = &Apache::lonstathelpers::get_time_limits();
       my $ranking = 
           &Apache::loncoursedata::rank_students_by_scores_on_resources
           (\@Resources,
            [&Apache::lonstatistics::get_selected_sections()],
            [&Apache::lonstatistics::get_selected_groups()],
            $Apache::lonstatistics::enrollment_status,undef,
            $starttime,$endtime, $symb);
       #
       # compute their percent scores on the problems in the sequence,
       my $number_to_grab = int(scalar(@{$ranking})/4);
       my $num_students = scalar(@{$ranking});
       my @BottomSet = map { $_->[&Apache::loncoursedata::RNK_student()]; 
                         } @{$ranking}[0..$number_to_grab];
       my @TopSet    = 
           map { 
               $_->[&Apache::loncoursedata::RNK_student()]; 
             } @{$ranking}[-$number_to_grab..0];
       if (! @BottomSet || (@BottomSet == 1 && $BottomSet[0] eq '') ||
           ! @TopSet    || (@TopSet    == 1 && $TopSet[0]    eq '')) {
           return 'nan';
       }
       my ($bottom_sum,$bottom_max) = 
           &Apache::loncoursedata::get_sum_of_scores($symb,$part,\@BottomSet,
                                                     undef,$starttime,$endtime);
       my ($top_sum,$top_max) = 
           &Apache::loncoursedata::get_sum_of_scores($symb,$part,\@TopSet,
                                                     undef,$starttime,$endtime);
       my $deg_of_disc;
       if ($top_max == 0 || $bottom_max==0) {
           $deg_of_disc = 'nan';
     } else {      } else {
         $OpSel2 = ' selected';          $deg_of_disc = ($top_sum/$top_max) - ($bottom_sum/$bottom_max);
     }      }
       #&Apache::lonnet::logthis('    '.$top_sum.'/'.$top_max.
       #                         ' - '.$bottom_sum.'/'.$bottom_max);
       return $deg_of_disc;
   }
   
     my $Ptr = '';  ###############################################
     $Ptr .= '<tr><td align="right"><b>Sorting Type:</b></td>'."\n";  ###############################################
     $Ptr .= '<td align="left"><select name="Ascend">'."\n";  ##
     $Ptr .= '<option'.$OpSel1.'>Ascending</option>'."\n".  ## Compute KR-21
     '<option'.$OpSel2.'>Descending</option>'."\n";  ##
     $Ptr .= '</select></td></tr>'."\n";  ## To compute KR-21, you need the following information:
   ##
     return $Ptr;  ## K=the number of items in your test
 }  ## M=the mean score on the test
   ## s=the standard deviation of the scores on your test 
 sub ProblemStatisticsButtons {  ##
     my ($displayFormat)=@_;  ## then:
   ## 
     my $Ptr = '<tr><td></td><td align="left">';  ## KR-21 rk= [K/(K-1)] * [1- (M*(K-M))/(K*s^2))]
     $Ptr .= '<input type=submit name="ProblemStatisticsRecalculate" ';  ##
     $Ptr .= 'value="Recalculate Statistics"/>'."\n";  ###############################################
     $Ptr .= '&nbsp;&nbsp;&nbsp;';  ###############################################
     $Ptr .= '<input type="submit" name="DoDiffGraph" ';  sub compute_sequence_statistics {
     $Ptr .= 'value="DoDiff Graph" />'."\n";      my ($seq) = @_;
     $Ptr .= '&nbsp;&nbsp;&nbsp;';      my $symb = $seq->symb;
     $Ptr .= '<input type="submit" name="PercentWrongGraph" ';      my @Resources;
     $Ptr .= 'value="%Wrong Graph" />'."\n";      my $part_count;
     $Ptr .= '&nbsp;&nbsp;&nbsp;';      foreach my $res (&Apache::lonstathelpers::get_resources($navmap,$seq)) {
     $Ptr .= '<input type="submit" name="DisplayCSVFormat" ';          push (@Resources,$res->symb);
     if($displayFormat eq 'Display CSV Format') {          $part_count += scalar(@{$res->parts});
         $Ptr .= 'value="Display CSV Format" />'."\n";      }
       my ($starttime,$endtime) = &Apache::lonstathelpers::get_time_limits();
       #
       # First compute statistics based on student scores
       my ($smin,$smax,$sMean,$sSTD,$scount,$sMAX) = 
           &Apache::loncoursedata::score_stats
                       ([&Apache::lonstatistics::get_selected_sections()],
                        [&Apache::lonstatistics::get_selected_groups()],
                        $Apache::lonstatistics::enrollment_status,
                        \@Resources,$starttime,$endtime,undef);
       $SeqStat{$symb}->{'title'}  = $seq->compTitle;
       $SeqStat{$symb}->{'scoremax'}  = $smax;
       $SeqStat{$symb}->{'scoremin'}  = $smin;
       $SeqStat{$symb}->{'scoremean'} = $sMean;
       $SeqStat{$symb}->{'scorestd'}  = $sSTD;
       $SeqStat{$symb}->{'scorecount'} = $scount;
       $SeqStat{$symb}->{'max_possible'} = $sMAX;
       #
       # Compute statistics based on the number of correct problems
       # 'correct' is taken to mean 
       my ($cmin,$cmax,$cMean,$cSTD,$ccount)=
           &Apache::loncoursedata::count_stats
           ([&Apache::lonstatistics::get_selected_sections()],
            [&Apache::lonstatistics::get_selected_groups()],
            $Apache::lonstatistics::enrollment_status,
            \@Resources,$starttime,$endtime,undef);
       my $K = $part_count;
       my $kr_21;
       if ($K > 1 && $cSTD > 0) {
           $kr_21 =  ($K/($K-1)) * (1 - $cMean*($K-$cMean)/($K*$cSTD**2));
     } else {      } else {
         $Ptr .= 'value="Display Table Format" />'."\n";          $kr_21 = 'nan';
     }      }
     $Ptr .= '</td></tr>';      $SeqStat{$symb}->{'countmax'} = $cmax;
       $SeqStat{$symb}->{'countmin'} = $cmin;
     return $Ptr;      $SeqStat{$symb}->{'countstd'} = $cSTD;
       $SeqStat{$symb}->{'countmean'} = $cMean;
       $SeqStat{$symb}->{'count'} = $ccount;
       $SeqStat{$symb}->{'items'} = $K;
       $SeqStat{$symb}->{'KR-21'}=$kr_21;
       return;
 }  }
   
 sub ProblemStatisticsLegend {  
     my $Ptr = '';  
     $Ptr = '<table border="0">';  
     $Ptr .= '<tr><td>';  
     $Ptr .= '<b>#Stdnts</b>:</td>';  
     $Ptr .= '<td>Total Number of Students opened the problem.';  
     $Ptr .= '</td></tr><tr><td>';  
     $Ptr .= '<b>Tries</b>:</td>';  
     $Ptr .= '<td>Total Number of Tries for solving the problem.';  
     $Ptr .= '</td></tr><tr><td>';  
     $Ptr .= '<b>Mod</b>:</td>';  
     $Ptr .= '<td>Maximunm Number of Tries for solving the problem.';  
     $Ptr .= '</td></tr><tr><td>';  
     $Ptr .= '<b>Mean</b>:</td>';  
     $Ptr .= '<td>Average Number of the tries. [ Tries / #Stdnts ]';  
     $Ptr .= '</td></tr><tr><td>';  
     $Ptr .= '<b>#YES</b>:</td>';  
     $Ptr .= '<td>Number of students solved the problem correctly.';  
     $Ptr .= '</td></tr><tr><td>';  
     $Ptr .= '<b>#yes</b>:</td>';  
     $Ptr .= '<td>Number of students solved the problem by override.';  
     $Ptr .= '</td></tr><tr><td>';  
     $Ptr .= '<b>%Wrng</b>:</td>';  
     $Ptr .= '<td>Percentage of students tried to solve the problem ';  
     $Ptr .= 'but still incorrect. [ 100*((#Stdnts-(#YES+#yes))/#Stdnts) ]';  
     $Ptr .= '</td></tr><tr><td>';  
     $Ptr .= '<b>DoDiff</b>:</td>';  
     $Ptr .= '<td>Degree of Difficulty of the problem.  ';  
     $Ptr .= '[ 1 - ((#YES+#yes) / Tries) ]';  
     $Ptr .= '</td></tr><tr><td>';  
     $Ptr .= '<b>S.D.</b>:</td>';  
     $Ptr .= '<td>Standard Deviation of the tries.  ';  
     $Ptr .= '[ sqrt(sum((Xi - Mean)^2)) / (#Stdnts-1) ';  
     $Ptr .= 'where Xi denotes every student\'s tries ]';  
     $Ptr .= '</td></tr><tr><td>';  
     $Ptr .= '<b>Skew.</b>:</td>';  
     $Ptr .= '<td>Skewness of the students tries.';  
     $Ptr .= '[(sqrt( sum((Xi - Mean)^3) / #Stdnts)) / (S.D.^3)]';  
     $Ptr .= '</td></tr><tr><td>';  
     $Ptr .= '<b>Dis.F.</b>:</td>';  
     $Ptr .= '<td>Discrimination Factor: A Standard for evaluating the ';  
     $Ptr .= 'problem according to a Criterion<br>';  
     $Ptr .= '<b>[Applied Criterion in %27 Upper Students - ';  
     $Ptr .= 'Applied the same Criterion in %27 Lower Students]</b><br>';  
     $Ptr .= '<b>1st Criterion</b> for Sorting the Students: ';  
     $Ptr .= '<b>Sum of Partial Credit Awarded / Total Number of Tries</b><br>';  
     $Ptr .= '<b>2nd Criterion</b> for Sorting the Students: ';  
     $Ptr .= '<b>Total number of Correct Answers / Total Number of Tries</b>';  
     $Ptr .= '</td></tr>';  
     $Ptr .= '<tr><td><b>Disc.</b></td>';  
     $Ptr .= '<td>Number of Students had at least one discussion.';  
     $Ptr .= '</td></tr></table>';  
   
     return $Ptr;  
 }  
   
 #------- Processing upperlist and lowerlist according to each problem  
 sub ProcessDiscriminant {  
     my ($List) = @_;  
     my @sortedList = sort (@$List);  
     my $Count = scalar @sortedList;  
     my $Problem;  
     my @Dis;  
     my $Slvd=0;  
     my $tmp;  
     my $Sum1=0;  
     my $Sum2=0;  
     my $nIndex=0;  
     my $nStudent=0;  
     my %Proc=undef;  
     while ($nIndex<$Count) {  
  ($Problem,$tmp)=split(/\=/,$sortedList[$nIndex]);  
  @Dis=split(/\+/,$tmp);  
  my $Temp = $Problem;  
  do {  
     $nIndex++;  
     $nStudent++;  
     $Sum1 += $Dis[0];  
     $Sum2 += $Dis[1];  
     ($Problem,$tmp)=split(/\=/,$sortedList[$nIndex]);  
     @Dis=split(/\+/,$tmp);  
  } while ( $Problem eq $Temp && $nIndex < $Count );  
 # $Proc{$Temp}=($Sum1/$nStudent).':'.$nStudent;  
  $Proc{$Temp}=($Sum1/$nStudent).':'.($Sum2/$nStudent);  
 #       $r->print("$nIndex) $Temp --> ($nStudent) $Proc{$Temp} <br>");  
  $Sum1=0;  
  $Sum2=0;  
  $nStudent=0;  
     }  
   
     return %Proc;  
 }  
   
 #------- Creating Discimination factor     
 sub Discriminant {  
     my ($discriminantFactor)=@_;  
     my @discriminantKeys=keys(%$discriminantFactor);  
     my $Count = scalar @discriminantKeys;  
   
     my $UpCnt = int(0.27*$Count);  
     my $low=0;  
     my $up=$Count-$UpCnt;  
     my @UpList=();  
     my @LowList=();  
   
     $Count=0;  
     foreach my $key (sort(@discriminantKeys)) {   
  $Count++;      
  if($low < $UpCnt || $Count > $up) {  
             $low++;  
             my $str=$discriminantFactor->{$key};  
             foreach(split(/\:/,$str)){  
                 if($_) {  
                     if($low<$UpCnt) { push(@LowList,$_); }  
                     else            { push(@UpList,$_);  }  
                 }  
             }  
         }  
     }  
     my %DisUp =  &ProcessDiscriminant(\@UpList);  
     my %DisLow = &ProcessDiscriminant(\@LowList);  
   
     return (\%DisUp, \%DisLow);  
 }  
   
      =pod 
   
 #---- END Problem Statistics Web Page ----------------------------------------  =item ProblemStatisticsLegend
   
 #---- Problem Statistics Graph Web Page --------------------------------------  =over 4
   
 # ------------------------------------------- Prepare data for Graphical chart  =item #Stdnts
   Total number of students attempted the problem.
   
 sub BuildDiffGraph {  =item Tries
     my ($r)=@_;  Total number of tries for solving the problem.
   
     my $graphData = &GetGraphData('DiffGraph', $r);  =item Max Tries
     return '<IMG src="/cgi-bin/graph.gif?'.$graphData.'" />';  Largest number of tries for solving the problem by a student.
 }  
   
 sub BuildWrongGraph {  =item Mean
     my ($r)=@_;  Average number of tries. [ Tries / #Stdnts ]
   
     my $graphData = &GetGraphData('WrongGraph', $r);  =item #YES
     return '<IMG src="/cgi-bin/graph.gif?'.$graphData.'" />';  Number of students solved the problem correctly.
 }  
   
   =item #yes
   Number of students solved the problem by override.
   
 sub GetGraphData {  =item %Wrong
     my ($ylab,$r,%GraphDat)=@_;  Percentage of students who tried to solve the problem 
     my $Col;  but is still incorrect. [ 100*((#Stdnts-(#YES+#yes))/#Stdnts) ]
     my $data='';  
     my $count = 0;  =item DoDiff
     my $Max = 0;  Degree of Difficulty of the problem.  
     my $cid=$ENV{'request.course.id'};  [ 1 - ((#YES+#yes) / Tries) ]
     my $GraphDB = "/home/httpd/perl/tmp/$ENV{'user.name'}".  
                   "_$ENV{'user.domain'}_$cid\_graph.db";  =item S.D.
     foreach (keys %GraphDat) {delete $GraphDat{$_};}  Standard Deviation of the tries.  
     if (-e "$GraphDB") {  [ sqrt(sum((Xi - Mean)^2)) / (#Stdnts-1) 
  if (tie(%GraphDat,'GDBM_File',"$GraphDB",&GDBM_READER,0640)) {  where Xi denotes every student\'s tries ]
     if ( $ylab eq 'DoDiff Graph' ) {  
  $ylab = 'Degree-of-Difficulty';  =item Skew.
  $Col = 0;  Skewness of the students tries.
     }  [(sqrt( sum((Xi - Mean)^3) / #Stdnts)) / (S.D.^3)]
     else {  
  $ylab = 'Wrong-Percentage';  =item Dis.F.
  $Col = 1;  Discrimination Factor: A Standard for evaluating the 
     }  problem according to a Criterion<br>
     foreach (sort NumericSort keys %GraphDat) {   
  my @Temp=split(/\:/,$GraphDat{$_});  =item [Criterion to group students into %27 Upper Students - 
                 my $inf = $Temp[$Col];   and %27 Lower Students]
  if ( $Max < $inf ) {$Max = $inf;}  1st Criterion for Sorting the Students: 
  $data .= $inf.',';  Sum of Partial Credit Awarded / Total Number of Tries
  $count++;  2nd Criterion for Sorting the Students: 
     }  Total number of Correct Answers / Total Number of Tries
     if ( $Max > 1 ) {   
  $Max += (10 - $Max % 10);  =item Disc.
  $Max = int($Max);  Number of Students had at least one discussion.
     }  
     else { $Max = 1; }  =back
             untie(%GraphDat);  
     my $Course = $ENV{'course.'.$cid.'.description'};  =cut
     $Course =~ s/\ /"_"/eg;  
     my $GData=$Course.'&'.'Problems'.'&'.$ylab.'&'.  
       $Max.'&'.$count.'&'.$data;  
  }  
  else {  
     $r->print("Unable to tie hash to db file");  
  }  
     }  
 }  
   
   ############################################################
   ############################################################
   
 1;  1;
 __END__  __END__

Removed from v.1.4  
changed lines
  Added in v.1.109


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>
500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.