'."\n";
- return $Str;
-}
+=head1 SYNOPSIS
-#######################################################
-#######################################################
+Routines to present problem statistics to instructors via tables,
+Excel files, and plots.
-=pod
-
-=item &CreateAndParseOutputSelector()
+=over 4
=cut
-#######################################################
-#######################################################
-my $output_mode;
-my $show;
-
-my @OutputOptions =
- (
- { name => 'problem statistics grouped by sequence',
- value => 'HTML problem statistics grouped',
- description => 'Output statistics for the problem parts.',
- mode => 'html',
- show => 'grouped',
- },
- { name => 'problem statistics ungrouped',
- value => 'HTML problem statistics ungrouped',
- description => 'Output statistics for the problem parts.',
- mode => 'html',
- show => 'ungrouped',
- },
- { name => 'problem statistics, Excel',
- value => 'Excel problem statistics',
- description => 'Output statistics for the problem parts '.
- 'in an Excel workbook',
- mode => 'excel',
- show => 'all',
- },
- { name => 'Degree of Difficulty Plot',
- value => 'plot deg diff',
- description => 'Generate a plot of the degree of difficulty of each '.
- 'problem part.',
- mode => 'plot',
- show => 'deg of diff',
- },
- { name => 'Percent Wrong Plot',
- value => 'plot per wrong',
- description => 'Generate a plot showing the percent of students who '.
- 'were unable to complete each problem part',
- mode => 'plot',
- show => 'per wrong',
- },
- );
+###############################################
+###############################################
+
+package Apache::lonproblemstatistics;
-sub OutputDescriptions {
- my $Str = '';
- $Str .= "
Output Modes
\n";
- $Str .= "
\n";
- foreach my $outputmode (@OutputOptions) {
- $Str .="
".$outputmode->{'name'}."
\n";
- $Str .="
".$outputmode->{'description'}."
\n";
+use strict;
+use Apache::lonnet;
+use Apache::loncommon();
+use Apache::lonhtmlcommon;
+use Apache::loncoursedata;
+use Apache::lonstatistics;
+use LONCAPA::lonmetadata();
+use Apache::lonlocal;
+use Spreadsheet::WriteExcel;
+use Apache::lonstathelpers();
+use Time::HiRes;
+use LONCAPA;
+
+
+my @StatsArray;
+my %SeqStat; # keys are symbs, values are hash refs
+
+##
+## Localization notes:
+##
+## in @Fields[0]->{'long_title'} is placed in Excel files and is used as the
+## header for plots created with Graph.pm, both of which more than likely do
+## 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'});
+ }
+ }
}
- $Str .= "
\n";
- return $Str;
-}
-
-sub CreateAndParseOutputSelector {
- my $Str = '';
- my $elementname = 'outputmode';
#
- # Format for output options is 'mode, restrictions';
- my $selected = 'html, with links';
- if (exists($ENV{'form.'.$elementname})) {
- if (ref($ENV{'form.'.$elementname} eq 'ARRAY')) {
- $selected = $ENV{'form.'.$elementname}->[0];
+ # Make sure the data we are plotting is there
+ my %NeededFields;
+ if (exists($env{'form.plot'}) && $env{'form.plot'} ne '' &&
+ $env{'form.plot'} ne 'none') {
+ if ($env{'form.plot'} eq 'degrees') {
+ $NeededFields{'deg_of_diff'}++;
+ $NeededFields{'deg_of_disc'}++;
+ } elsif ($env{'form.plot'} eq 'tries statistics') {
+ $NeededFields{'mean_tries'}++;
+ $NeededFields{'std_tries'}++;
+ $NeededFields{'problem_num'}++;
} else {
- $selected = $ENV{'form.'.$elementname};
+ $NeededFields{$env{'form.plot'}}++;
}
}
#
- # Set package variables describing output mode
- $output_mode = 'html';
- $show = 'all';
- foreach my $option (@OutputOptions) {
- next if ($option->{'value'} ne $selected);
- $output_mode = $option->{'mode'};
- $show = $option->{'show'};
+ # This should not happen, but in case it does...
+ if (ref($env{'form.fieldselections'}) ne 'ARRAY') {
+ $env{'form.fieldselections'} = [$env{'form.fieldselections'}];
}
#
- # Build the form element
- $Str = qq/";
- return $Str;
+ #
+ # Always show all the sequence statistics (for now)
+ foreach my $field (@SeqFields) {
+ $field->{'selected'} = 'yes';
+ }
+ return;
+}
+
+sub field_selection_input {
+ my $Str = '\n";
}
###############################################
###############################################
+=pod
+
+=item &CreateInterface()
+
+Create the main intereface for the statistics page. Allows the user to
+select sections, maps, and output.
+
+=cut
+
###############################################
###############################################
-sub Gather_Student_Data {
+sub CreateInterface {
my ($r) = @_;
- my $c = $r->connection();
#
- my @Sequences = &Apache::lonstatistics::Sequences_with_Assess();
+ &parse_field_selection();
#
- my @Students = @Apache::lonstatistics::Students;
+ my $Str = '';
+ $Str .= &Apache::lonhtmlcommon::breadcrumbs('Overall Problem Statistics',
+ 'Statistics_Overall_Key');
+ $Str .= '
'.
+ &mt('Press "Generate Statistics" when you are ready.').
+ '
'.
+ &mt('It may take some time to update the student data '.
+ 'for the first analysis. Future analysis this session '.
+ 'will not have this delay.').
+ '
");
+ # 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('
\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 .= '
'.&mt('Statistics on submissions from [_1] to [_2]',
+ &Apache::lonlocal::locallocaltime($starttime),
+ &Apache::lonlocal::locallocaltime($endtime)
+ ).'
';
+ }
+ $Str .= "
".&mt('Compiled on [_1]',
+ &Apache::lonlocal::locallocaltime(time))."
";
+ return $Str;
+}
+
###############################################
###############################################
-
+##
+## Misc HTML output routines
+##
###############################################
###############################################
-sub output_html_ungrouped {
- my ($r) = @_;
+sub statistics_html_table_data {
+ my ($data,$options) = @_;
+ my $row = '';
+ foreach my $field (@Fields) {
+ next if ($options =~ /no $field->{'name'}/);
+ next if ($field->{'selected'} ne 'yes');
+ $row .= '
');
+ ##
+ ## 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);
}
+ $rows_output++;
+ $cols_output=0;
}
#
- # Table Headers
- $r->print('
'."\n");
- my $Str = '';
- foreach (@Header) {
- next if ($_ eq 'Part' && !$show_part);
- # Do not allow sorting on some fields
- if ($_ eq $sortby || /^(Part)$/) {
- $Str .= '
'.$_.'
';
+ $excel_workbook->close();
+ #
+ # Tell the user where to get their excel file
+ $r->print(' '.
+ ''.
+ &mt('Your Excel Spreadsheet').''."\n");
+ $r->rflush();
+ return;
+}
+
+##
+## &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 {
- $Str .= '
';
+ $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);
}
}
- $r->print('
'.$Str."
\n");
+ return @Data;
+}
+
+sub compute_all_statistics {
+ my ($r) = @_;
+ if (@StatsArray > 0) {
+ # Assume we have already computed the statistics
+ return;
+ }
+ my $c = $r->connection;
+ foreach my $seq (@sequences) {
+ last if ($c->aborted);
+ &compute_sequence_statistics($seq);
+ &compute_statistics_on_sequence($seq);
+ }
+}
+
+sub sort_data {
+ my ($sortkey) = @_;
+ return if (! @StatsArray);
#
# Sort the data
- my @OutputOrder;
- if ($sortby eq 'Container') {
- @OutputOrder = @Statsarray;
- } else {
+ 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
- @OutputOrder = sort {
+ @StatsArray = sort {
lc($a->{$sortby}) cmp lc($b->{$sortby}) ||
- lc($a->{'Title'}) cmp lc($b->{'Title'}) ||
- lc($a->{'Part'}) cmp lc($b->{'Part'});
- } @Statsarray;
+ lc($a->{'title'}) cmp lc($b->{'title'}) ||
+ lc($a->{'part'}) cmp lc($b->{'part'});
+ } @StatsArray;
} else {
# Numerical comparison
- @OutputOrder = sort {
+ @StatsArray = sort {
my $retvalue = 0;
if ($b->{$sortby} eq 'nan') {
if ($a->{$sortby} ne 'nan') {
@@ -401,394 +1521,296 @@ sub output_html_ungrouped {
}
}
if ($retvalue eq '0') {
- $retvalue = $b->{$sortby} <=> $a->{$sortby} ||
- lc($a->{'Title'}) <=> lc($b->{'Title'}) ||
- lc($a->{'Part'}) <=> lc($b->{'Part'});
+ $retvalue = $b->{$sortby} <=> $a->{$sortby} ||
+ lc($a->{'title'}) <=> lc($b->{'title'}) ||
+ lc($a->{'part'}) <=> lc($b->{'part'});
}
$retvalue;
- } @Statsarray;
+ } @StatsArray;
}
}
- foreach my $row (@OutputOrder) {
- $r->print('