--- loncom/interface/statistics/lonproblemstatistics.pm 2004/03/26 22:04:22 1.73 +++ loncom/interface/statistics/lonproblemstatistics.pm 2005/03/27 05:08:03 1.103 @@ -1,6 +1,6 @@ # The LearningOnline Network with CAPA # -# $Id: lonproblemstatistics.pm,v 1.73 2004/03/26 22:04:22 matthew Exp $ +# $Id: lonproblemstatistics.pm,v 1.103 2005/03/27 05:08:03 matthew Exp $ # # Copyright Michigan State University Board of Trustees # @@ -55,12 +55,14 @@ 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; my @StatsArray; +my %SeqStat; # keys are symbs, values are hash refs ## ## Localization notes: @@ -69,27 +71,67 @@ my @StatsArray; ## 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' }, + color => '#FFFFE6', + selectable => 'no', + defaultselected => 'yes', + }, { name => 'container', title => 'Sequence or Folder', align => 'left', color => '#FFFFE6', - sortable => 'yes' }, + sortable => 'yes', + selectable => 'no', + defaultselected => 'yes', + }, { name => 'title', title => 'Title', align => 'left', color => '#FFFFE6', special => 'link', - sortable => 'yes', }, + 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', @@ -97,7 +139,10 @@ my @Fields = ( format => '%d', sortable => 'yes', graphable => 'yes', - long_title => 'Number of Students Attempting Problem' }, + long_title => 'Number of Students Attempting Problem', + selectable => 'yes', + defaultselected => 'yes', + }, { name => 'tries', title => 'Tries', align => 'right', @@ -105,7 +150,10 @@ my @Fields = ( format => '%d', sortable => 'yes', graphable => 'yes', - long_title => 'Total Number of Tries' }, + long_title => 'Total Number of Tries', + selectable => 'yes', + defaultselected => 'yes', + }, { name => 'max_tries', title => 'Max Tries', align => 'right', @@ -113,7 +161,10 @@ my @Fields = ( format => '%d', sortable => 'yes', graphable => 'yes', - long_title => 'Maximum Number of Tries' }, + long_title => 'Maximum Number of Tries', + selectable => 'yes', + defaultselected => 'yes', + }, { name => 'min_tries', title => 'Min Tries', align => 'right', @@ -121,7 +172,10 @@ my @Fields = ( format => '%d', sortable => 'yes', graphable => 'yes', - long_title => 'Minumum Number of Tries' }, + long_title => 'Minumum Number of Tries', + selectable => 'yes', + defaultselected => 'yes', + }, { name => 'mean_tries', title => 'Mean Tries', align => 'right', @@ -129,7 +183,10 @@ my @Fields = ( format => '%5.2f', sortable => 'yes', graphable => 'yes', - long_title => 'Average Number of Tries' }, + long_title => 'Average Number of Tries', + selectable => 'yes', + defaultselected => 'yes', + }, { name => 'std_tries', title => 'S.D. tries', align => 'right', @@ -137,7 +194,10 @@ my @Fields = ( format => '%5.2f', sortable => 'yes', graphable => 'yes', - long_title => 'Standard Deviation of Number of Tries' }, + long_title => 'Standard Deviation of Number of Tries', + selectable => 'yes', + defaultselected => 'yes', + }, { name => 'skew_tries', title => 'Skew Tries', align => 'right', @@ -145,7 +205,10 @@ my @Fields = ( format => '%5.2f', sortable => 'yes', graphable => 'yes', - long_title => 'Skew of Number of Tries' }, + long_title => 'Skew of Number of Tries', + selectable => 'yes', + defaultselected => 'no', + }, { name => 'num_solved', title => '#YES', align => 'right', @@ -153,7 +216,10 @@ my @Fields = ( format => '%4.1f',# format => '%d', sortable => 'yes', graphable => 'yes', - long_title => 'Number of Students able to Solve' }, + long_title => 'Number of Students able to Solve', + selectable => 'yes', + defaultselected => 'yes', + }, { name => 'num_override', title => '#yes', align => 'right', @@ -161,7 +227,21 @@ my @Fields = ( format => '%4.1f',# format => '%d', sortable => 'yes', graphable => 'yes', - long_title => 'Number of Students given Override' }, + 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', @@ -169,7 +249,21 @@ my @Fields = ( format => '%4.1f', sortable => 'yes', graphable => 'yes', - long_title => 'Percent of students whose final answer is wrong' }, + 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', @@ -178,7 +272,10 @@ my @Fields = ( sortable => 'yes', graphable => 'yes', long_title => 'Degree of Difficulty'. - '[ 1 - ((#YES+#yes) / Tries) ]'}, + '[ 1 - ((#YES+#yes) / Tries) ]', + selectable => 'yes', + defaultselected => 'yes', + }, { name => 'deg_of_disc', title => 'DoDisc', align => 'right', @@ -186,9 +283,311 @@ my @Fields = ( format => '%4.2f', sortable => 'yes', graphable => 'yes', - long_title => 'Degree of Discrimination' }, + 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'}); + } + } + } + # + # 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 { + $NeededFields{$ENV{'form.plot'}}++; + } + } + # + # This should not happen, but in case it does... + if (ref($ENV{'form.fieldselections'}) ne 'ARRAY') { + $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'; + } + return; +} + +sub field_selection_input { + my $Str = '\n"; +} + ############################################### ############################################### @@ -204,6 +603,10 @@ select sections, maps, and output. ############################################### ############################################### sub CreateInterface { + my ($r) = @_; + # + &parse_field_selection(); + # my $Str = ''; $Str .= &Apache::lonhtmlcommon::breadcrumbs (undef,'Overall Problem Statistics','Statistics_Overall_Key'); @@ -212,6 +615,7 @@ sub CreateInterface { $Str .= ''.&mt('Sections').''; $Str .= ''.&mt('Enrollment Status').''; $Str .= ''.&mt('Sequences and Folders').''; + $Str .= ''.&mt('Statistics').''; $Str .= ''. &Apache::lonstathelpers::limit_by_time_form().''; $Str .= ''."\n"; @@ -222,31 +626,22 @@ sub CreateInterface { $Str .= &Apache::lonhtmlcommon::StatusOptions(undef,undef,5); $Str .= ''; # - my $only_seq_with_assessments = sub { - my $s=shift; - if ($s->{'num_assess'} < 1) { - return 0; - } else { - return 1; - } - }; - $Str .= &Apache::lonstatistics::MapSelect('Maps','multiple,all',5, - $only_seq_with_assessments); + $Str .= &Apache::lonstatistics::map_select('Maps','multiple,all',5); + $Str .= ''.&field_selection_input(); $Str .= ''."\n"; $Str .= ''."\n"; + # + $Str .= '

'.&mt('Status: [_1]', + '' + ). + '

'; + # $Str .= ''; $Str .= ' 'x5; $Str .= 'Plot '.&plot_dropdown().(' 'x10); - $Str .= ''; - $Str .= ' 'x5; - $Str .= ''; - $Str .= ' 'x5; - $Str .= ''; - $Str .= ' 'x5; + # return $Str; } @@ -263,14 +658,20 @@ Main interface to problem statistics. ############################################### ############################################### +my $navmap; +my @sequences; + sub BuildProblemStatisticsPage { my ($r,$c)=@_; + undef($navmap); + undef(@sequences); # my %Saveable_Parameters = ('Status' => 'scalar', 'statsoutputmode' => 'scalar', 'Section' => 'array', 'StudentData' => 'array', - 'Maps' => 'array'); + 'Maps' => 'array', + 'fieldselections'=> 'array'); &Apache::loncommon::store_course_settings('statistics', \%Saveable_Parameters); &Apache::loncommon::restore_course_settings('statistics', @@ -280,15 +681,23 @@ sub BuildProblemStatisticsPage { # # Clear the package variables undef(@StatsArray); + undef(%SeqStat); # # Finally let the user know we are here - my $interface = &CreateInterface(); + my $interface = &CreateInterface($r); $r->print($interface); $r->print(''); # - if (! exists($ENV{'form.statsfirstcall'})) { - $r->print(''); + my @CacheButtonHTML = + &Apache::lonstathelpers::manage_caches($r,'Statistics','stats_status'); + my $Str; + foreach my $html (@CacheButtonHTML) { + $Str.=$html.(' 'x5); + } + # + $r->print($Str); + if (! exists($ENV{'form.firstrun'})) { $r->print('

'. &mt('Press "Generate Statistics" when you are ready.'). '

'. @@ -297,39 +706,90 @@ sub BuildProblemStatisticsPage { ' will not have this delay.'). '

'); return; - } elsif ($ENV{'form.statsfirstcall'} eq 'yes' || - exists($ENV{'form.UpdateCache'}) || - exists($ENV{'form.ClearCache'}) ) { - $r->print(''); - &Apache::lonstatistics::Gather_Student_Data($r); - } else { - $r->print(''); } $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('

'.&mt('A course-wide error occured.').'

'. + '

'.$navmap.'

'); + return; + } if (exists($ENV{'form.Excel'})) { + $r->print('

'. + &Apache::lonstatistics::section_and_enrollment_description(). + '

'); &Excel_output($r); - } else { + } else { + $r->print(''.' 'x5); + $r->rflush(); + $r->print('

'. + &Apache::lonstatistics::section_and_enrollment_description(). + '

'); + my $count = 0; + foreach my $seq (@sequences) { + my @resources = + &Apache::lonstathelpers::get_resources($navmap,$seq); + $count += scalar(@resources); + } + if ($count > 10) { + $r->print('

'. + &mt('Compiling statistics for [_1] problems',$count). + '

'); + if ($count > 30) { + $r->print('

'.&mt('This will take some time.').'

'); + } + $r->rflush(); + } + # my $sortby = $ENV{'form.sortby'}; $sortby = 'container' if (! defined($sortby) || $sortby =~ /^\s*$/); my $plot = $ENV{'form.plot'}; - &Apache::lonnet::logthis('form.plot = '.$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)) { - &Apache::lonnet::logthis('calling plot routine'); &make_plot($r,$plot); } &output_html_stats($r); + &output_sequence_statistics($r); } } return; } +sub output_sequence_statistics { + my ($r) = @_; + my $c=$r->connection(); + $r->print('

'.&mt('Sequence Statistics'). + &Apache::loncommon::help_open_topic('Statistics_Sequence'). + '

'); + $r->print('
'."\n". + ''."\n". + ''); + $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('
'); + $r->print('
'); + $r->rflush(); + return; +} + + ########################################################## ########################################################## ## @@ -342,10 +802,9 @@ sub output_html_by_sequence { my $c = $r->connection(); $r->print(&html_preamble()); # - foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) { + foreach my $seq (@sequences) { last if ($c->aborted); - next if ($seq->{'num_assess'} < 1); - $r->print("

".$seq->{'title'}."

". + $r->print("

".$seq->compTitle."

". '
'."\n". ''."\n". ''. @@ -385,7 +844,6 @@ sub output_html_stats { return; } - sub html_preamble { my $Str=''; $Str .= "

". @@ -417,6 +875,7 @@ sub statistics_html_table_data { my $row = ''; foreach my $field (@Fields) { next if ($options =~ /no $field->{'name'}/); + next if ($field->{'selected'} ne 'yes'); $row .= '

'; + } + $Str .= ''; + return $Str; +} + + +sub sequence_html_output { + my ($seq) = @_; + my $data = $SeqStat{$seq->symb}; + my $row = ''; + foreach my $field (@SeqFields) { + next if ($field->{'selected'} ne 'yes'); + $row .= ''; + } + $row .= ''."\n"; + return $row; +} + #################################################### #################################################### ## @@ -480,6 +974,8 @@ sub make_plot { &sort_data($ENV{'form.sortby'}); if ($plot eq 'degrees') { °rees_plot($r); + } elsif ($plot eq 'tries statistics') { + &tries_data_plot($r); } else { &make_single_stat_plot($r,$plot); } @@ -592,6 +1088,11 @@ sub degrees_plot { my $diffdata .= ''.join(',',@Labels).''.$/. ''.join(',',@Diff).''.$/; # + my $title = 'Degree of Discrimination\nand Degree of Difficulty'; + if ($xmax > 50) { + $title = 'Degree of Discrimination and Degree of Difficulty'; + } + # $plot=<<"END"; - Degree of Discrmination and Degree of Difficulty + $title Problem Number {'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 .= ''.join(',',@Labels).''.$/. + ''.join(',',@Mean).''.$/; + # + my $std_error_data .= ''.join(',',@Labels).''.$/. + ''.join(',',@Mean).''.$/. + ''.join(',',@STD).''.$/; + # + my $title = 'Mean and S.D. of Tries'; + if ($xmax > 25) { + $title = 'Mean and Standard Deviation of Tries'; + } + # + $plot=<<"END"; + + $title + + Problem Number + Number of Tries + + $std_error_data + + + $std_data + + +END + my $plotresult = + '

'.&Apache::lonxml::xmlparse($r,'web',$plot).'

'.$/; + $r->print($plotresult); + return; +} + sub plot_dropdown { my $current = ''; # @@ -646,12 +1232,15 @@ sub plot_dropdown { my @Additional_Plots = ( { graphable=>'yes', name => 'degrees', - title => 'DoDisc and DoDiff' }); + title => 'Difficulty Indexes' }, + { graphable=>'yes', + name => 'tries statistics', + title => 'Tries Statistics' }); # my $Str= "\n".'
{'align'})) { $row .= ' align="'.$field->{'align'}.'"'; @@ -425,7 +884,7 @@ sub statistics_html_table_data { if (exists($field->{'special'}) && $field->{'special'} eq 'link') { $row .= ''; } - if (exists($field->{'format'})) { + if (exists($field->{'format'}) && $data->{$field->{'name'}} !~ /[A-Z]/i) { $row .= sprintf($field->{'format'},$data->{$field->{'name'}}); } else { $row .= $data->{$field->{'name'}}; @@ -443,6 +902,7 @@ sub statistics_table_header { my $header_row; foreach my $field (@Fields) { next if ($options =~ /no $field->{'name'}/); + next if ($field->{'selected'} ne 'yes'); $header_row .= ''; if (exists($field->{'sortable'}) && $field->{'sortable'} eq 'yes') { $header_row .= '{'color'}.'"'; + $Str .= '>'.$field->{'title'}.'
{'align'})) { + $row .= ' align="'.$field->{'align'}.'"'; + } + $row .= '>'; + if (exists($field->{'format'})) { + $row .= sprintf($field->{'format'},$data->{$field->{'name'}}); + } else { + $row .= $data->{$field->{'name'}}; + } + $row .= '