--- loncom/interface/statistics/lonproblemstatistics.pm 2004/04/01 21:14:32 1.80 +++ loncom/interface/statistics/lonproblemstatistics.pm 2013/12/30 13:51:37 1.124 @@ -1,6 +1,6 @@ # The LearningOnline Network with CAPA # -# $Id: lonproblemstatistics.pm,v 1.80 2004/04/01 21:14:32 matthew Exp $ +# $Id: lonproblemstatistics.pm,v 1.124 2013/12/30 13:51:37 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -50,15 +50,19 @@ Excel files, and plots. package Apache::lonproblemstatistics; use strict; -use Apache::lonnet(); +use Apache::lonnet; use Apache::loncommon(); +use Apache::lonquickgrades(); 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 @@ -70,6 +74,14 @@ my %SeqStat; # keys are symbs, values ## header for plots created with Graph.pm, both of which more than likely do ## not support localization. ## +## Additional Notes: +## Localization can be done and is done before passing the phrases +## to the output. +## This might conflict with special characters, e.g. German Umlaute or +## chinese characters. Do not use such characters in this case. +## If this failed, consider that the sequence and folder names +## are also passed to the output and would fail the same way. +## # # ## @@ -81,7 +93,7 @@ my %SeqStat; # keys are symbs, values ## 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 +## output. Passed through &mt(). ## align no (left|right|center) HTML cell contents alignment ## color yes html color HTML cell background color ## used to visually group statistics @@ -95,6 +107,10 @@ my %SeqStat; # keys are symbs, values ## 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#', @@ -116,11 +132,11 @@ my @Fields = ( align => 'left', color => '#FFFFE6', special => 'link', - sortable => 'yes', + sortable => 'yes', selectable => 'no', defaultselected => 'yes', }, - { name => 'part', + { name => 'part', title => 'Part', align => 'left', color => '#FFFFE6', @@ -226,6 +242,17 @@ my @Fields = ( 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', @@ -233,6 +260,17 @@ my @Fields = ( 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', @@ -245,7 +283,7 @@ my @Fields = ( sortable => 'yes', graphable => 'yes', long_title => 'Degree of Difficulty'. - '[ 1 - ((#YES+#yes) / Tries) ]', + ' ~[ 1 - ((#YES+#yes) / Tries) ~]', selectable => 'yes', defaultselected => 'yes', }, @@ -258,8 +296,86 @@ my @Fields = ( graphable => 'yes', long_title => 'Degree of Discrimination', selectable => 'yes', - defaultselected => 'no', + 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 = ( @@ -268,7 +384,7 @@ my @SeqFields = ( align => 'left', color => '#FFFFE6', special => 'no', - sortable => 'no', + sortable => 'no', selectable => 'yes', defaultselected => 'no', }, @@ -403,7 +519,7 @@ my @SeqFields = ( long_title => 'KR-21 reliability statistic', selectable => 'yes', defaultselected => 'no', - }, + }, ); my %SelectedFields; @@ -411,35 +527,35 @@ my %SelectedFields; sub parse_field_selection { # # Pull out the defaults - if (! defined($ENV{'form.fieldselections'})) { - $ENV{'form.fieldselections'} = []; + 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'}); + 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') { + 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') { + } elsif ($env{'form.plot'} eq 'tries statistics') { $NeededFields{'mean_tries'}++; $NeededFields{'std_tries'}++; $NeededFields{'problem_num'}++; } else { - $NeededFields{$ENV{'form.plot'}}++; + $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'}]; + if (ref($env{'form.fieldselections'}) ne 'ARRAY') { + $env{'form.fieldselections'} = [$env{'form.fieldselections'}]; } # # Set the field data and the selected fields (for easier checking) @@ -454,18 +570,23 @@ sub parse_field_selection { $field->{'selected'} = 'yes'; $SelectedFields{$field->{'name'}}++; } - foreach my $selection (@{$ENV{'form.fieldselections'}}) { + 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 = '
'; $Str .= ''; - $Str .= ' 'x5; - $Str .= 'Plot '.&plot_dropdown().(' 'x10); - $Str .= ''; - $Str .= ' 'x5; - $Str .= ''; - $Str .= ' 'x5; - $Str .= ''; - $Str .= ' 'x5; + $Str .= (' 'x10); + # return $Str; } @@ -557,12 +673,23 @@ Main interface to problem statistics. ############################################### ############################################### +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'); @@ -578,28 +705,34 @@ sub BuildProblemStatisticsPage { undef(%SeqStat); # # Finally let the user know we are here - my $interface = &CreateInterface(); + $r->print(&Apache::lonhtmlcommon::breadcrumbs('Overall Problem Statistics', + 'Statistics_Overall_Key')); + &Apache::lonquickgrades::startGradeScreen($r,'statistics'); + + my $interface = &CreateInterface($r); $r->print($interface); - $r->print(''); # - if (! exists($ENV{'form.statsfirstcall'})) { - $r->print(''); - $r->print(''. &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.'). + 'for the first analysis. Future analysis this session '. + 'will not have this delay.'). '
'); + &clean_up(); 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(); # @@ -607,26 +740,45 @@ sub BuildProblemStatisticsPage { # it does not slow things down noticably. &Apache::loncoursedata::populate_weight_table(); # - if (exists($ENV{'form.Excel'})) { + ($navmap,@sequences) = + &Apache::lonstatistics::selected_sequences_with_assessments(); + if (! ref($navmap)) { + $r->print(''. + &Apache::lonstatistics::section_and_enrollment_description(). + '
'); &Excel_output($r); } else { + $r->print(''.' 'x5); + $r->rflush(); + $r->print(''. + &Apache::lonstatistics::section_and_enrollment_description(). + '
'); my $count = 0; - foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) { - $count += $seq->{'num_assess_parts'}; + foreach my $seq (@sequences) { + my @resources = + &Apache::lonstathelpers::get_resources($navmap,$seq); + $count += scalar(@resources); } if ($count > 10) { - $r->print(''. + &mt('Compiling statistics for [quant,_1,problem]',$count). + '
'); if ($count > 30) { - $r->print(''.&mt('This will take some time.').'
'); } $r->rflush(); } # - my $sortby = $ENV{'form.sortby'}; + my $sortby = $env{'form.sortby'}; $sortby = 'container' if (! defined($sortby) || $sortby =~ /^\s*$/); - my $plot = $ENV{'form.plot'}; + my $plot = $env{'form.plot'}; if ($plot eq '' || $plot eq 'none') { undef($plot); } @@ -641,25 +793,25 @@ sub BuildProblemStatisticsPage { &output_sequence_statistics($r); } } + &clean_up(); return; } sub output_sequence_statistics { my ($r) = @_; my $c=$r->connection(); - $r->print(''."\n".
- ' |
'."\n".
- ' |
'."\n".
- ' |
".&mt('Compiled on [_1]', + &Apache::lonlocal::locallocaltime(time))."
"; return $Str; } @@ -752,7 +903,7 @@ sub statistics_html_table_data { foreach my $field (@Fields) { next if ($options =~ /no $field->{'name'}/); next if ($field->{'selected'} ne 'yes'); - $row .= ''. &mt('Unable to plot the requested statistic.'). - ''); + '
'); return; } # # Build up the data sets to plot - my @Labels; + my @Labels; my @Data; my $max = 1; foreach my $data (@StatsArray) { @@ -905,7 +1050,7 @@ sub make_single_stat_plot { } # $r->print("".&Apache::loncommon::DrawBarGraph($title,
- 'Problem Number',
+ &mt('Problem Number'),
$yaxis,
$max,
undef, # colors
@@ -923,7 +1068,7 @@ sub degrees_plot {
my $plot = '';
my $ymax = 0;
my $ymin = 0;
- my @Disc; my @Diff; my @Labels;
+ my @Disc; my @Diff; my @Labels;
foreach my $data (@StatsArray) {
push(@Labels,$data->{'problem_num'});
my $disc = $data->{'deg_of_disc'};
@@ -970,6 +1115,15 @@ sub degrees_plot {
my $diffdata .= ''.join(',',@Labels).''.$/.
''.join(',',@Diff).''.$/;
#
+ my $title = &mt('Degree of Discrimination[_1]and Degree of Difficulty','\n');
+ if ($xmax > 50) {
+ $title = &mt('Degree of Discrimination and Degree of Difficulty');
+ }
+ my %lt = &Apache::lonlocal::texthash(
+ 'alttag' => 'Degree of Discrimination and Degree of Difficulty Plot',
+ 'xlabel' => 'Problem Number',
+ );
+ #
$plot=<<"END";
'.&Apache::lonxml::xmlparse($r,'web',$plot).'
'.$/; $r->print($plotresult); return; @@ -1053,6 +1207,16 @@ sub tries_data_plot { ''.join(',',@Mean).''.$/. ''.join(',',@STD).''.$/; # + my $title = &mt('Mean and S.D. of Tries'); + if ($xmax > 30) { + $title = &mt('Mean and Standard Deviation of Tries'); + } + # + my %lt = &Apache::lonlocal::texthash( + 'alttag' => 'Mean and S.D of Tries Plot', + 'xlabel' => 'Problem Number', + 'ylabel' => 'Number of Tries', + ); $plot=<<"END";'.&Apache::lonxml::xmlparse($r,'web',$plot).'
'.$/; $r->print($plotresult); return; @@ -1095,32 +1260,35 @@ END sub plot_dropdown { my $current = ''; + my $title; # - if (defined($ENV{'form.plot'})) { - $current = $ENV{'form.plot'}; + if (defined($env{'form.plot'})) { + $current = $env{'form.plot'}; } # my @Additional_Plots = ( { graphable=>'yes', name => 'degrees', - title => 'DoDisc and DoDiff' }, + title => 'Difficulty Indexes' }, { graphable=>'yes', name => 'tries statistics', - title => 'Mean and S.D. of Tries' }); + title => 'Tries Statistics' }); # my $Str= "\n".'