--- loncom/interface/statistics/lonproblemstatistics.pm 2004/07/22 17:33:29 1.92 +++ loncom/interface/statistics/lonproblemstatistics.pm 2013/12/30 14:05:21 1.122.2.2 @@ -1,6 +1,6 @@ # The LearningOnline Network with CAPA # -# $Id: lonproblemstatistics.pm,v 1.92 2004/07/22 17:33:29 matthew Exp $ +# $Id: lonproblemstatistics.pm,v 1.122.2.2 2013/12/30 14:05:21 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -50,7 +50,7 @@ Excel files, and plots. package Apache::lonproblemstatistics; use strict; -use Apache::lonnet(); +use Apache::lonnet; use Apache::loncommon(); use Apache::lonhtmlcommon; use Apache::loncoursedata; @@ -60,6 +60,8 @@ 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 @@ -71,6 +73,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. +## # # ## @@ -82,7 +92,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 @@ -121,11 +131,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', @@ -231,6 +241,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', @@ -238,6 +259,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', @@ -250,7 +282,7 @@ my @Fields = ( sortable => 'yes', graphable => 'yes', long_title => 'Degree of Difficulty'. - '[ 1 - ((#YES+#yes) / Tries) ]', + ' ~[ 1 - ((#YES+#yes) / Tries) ~]', selectable => 'yes', defaultselected => 'yes', }, @@ -309,6 +341,40 @@ my @Fields = ( # 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 = ( @@ -317,7 +383,7 @@ my @SeqFields = ( align => 'left', color => '#FFFFE6', special => 'no', - sortable => 'no', + sortable => 'no', selectable => 'yes', defaultselected => 'no', }, @@ -452,7 +518,7 @@ my @SeqFields = ( long_title => 'KR-21 reliability statistic', selectable => 'yes', defaultselected => 'no', - }, + }, ); my %SelectedFields; @@ -460,35 +526,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) @@ -503,7 +569,7 @@ 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'}}++; @@ -519,7 +585,7 @@ sub parse_field_selection { } sub field_selection_input { - my $Str = ''."\n"; $Str .= ''."\n"; foreach my $field (@Fields) { next if ($field->{'selectable'} ne 'yes'); @@ -552,48 +618,43 @@ sub CreateInterface { &parse_field_selection(); # my $Str = ''; - $Str .= &Apache::lonhtmlcommon::breadcrumbs - (undef,'Overall Problem Statistics','Statistics_Overall_Key'); - $Str .= ''."\n"; - $Str .= ''; - $Str .= ''; - $Str .= ''; - $Str .= ''; - $Str .= ''; - $Str .= ''; - $Str .= ''."\n"; + $Str .= '

'; + $Str .= &Apache::loncommon::start_data_table(); + $Str .= &Apache::loncommon::start_data_table_header_row(); + $Str .= '

'; + $Str .= ''; + $Str .= ''; + $Str .= ''; + $Str .= ''; + $Str .= ''; + $Str .= ''; + $Str .= &Apache::loncommon::end_data_table_header_row(); # - $Str .= ''."\n"; - $Str .= '
'.&mt('Sections').''.&mt('Enrollment Status').''.&mt('Sequences and Folders').''.&mt('Statistics').''. - &Apache::lonstathelpers::limit_by_time_form().'
'.&mt('Sections').''.&mt('Groups').''.&mt('Access Status').''.&mt('Sequences and Folders').''.&mt('Statistics').''.&mt('Plot Graph').''.&mt('Time Period').'
'."\n"; + $Str .= &Apache::loncommon::start_data_table_row(); + $Str .= ''."\n"; $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5); - $Str .= ''; + $Str .= ''; + $Str .= &Apache::lonstatistics::GroupSelect('Group','multiple',5); + $Str .= ''; $Str .= &Apache::lonhtmlcommon::StatusOptions(undef,undef,5); - $Str .= ''; + $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 .= ''.&field_selection_input(); - $Str .= '
'."\n"; - # - $Str .= '

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

'; + $Str .= &Apache::lonstatistics::map_select('Maps','multiple,all',5); + $Str .= ''; + $Str .= &field_selection_input(); + $Str .= ''; + $Str .= &plot_dropdown(); + $Str .= ''."\n"; + $Str .= ''; + $Str .= &Apache::lonstathelpers::limit_by_time_form(); + $Str .= ''."\n"; + $Str .= &Apache::loncommon::end_data_table_row(); + $Str .= &Apache::loncommon::end_data_table(); # + $Str .= '

'; $Str .= ''; - $Str .= ' 'x5; - $Str .= 'Plot '.&plot_dropdown().(' 'x10); + $Str .= (' 'x10); # return $Str; } @@ -611,12 +672,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'); @@ -632,12 +704,15 @@ sub BuildProblemStatisticsPage { undef(%SeqStat); # # Finally let the user know we are here + $r->print(&Apache::lonhtmlcommon::breadcrumbs('Overall Problem Statistics', + 'Statistics_Overall_Key')); + my $interface = &CreateInterface($r); $r->print($interface); - $r->print(''); # - my @CacheButtonHTML = + my @CacheButtonHTML = &Apache::lonstathelpers::manage_caches($r,'Statistics','stats_status'); my $Str; foreach my $html (@CacheButtonHTML) { @@ -645,14 +720,16 @@ sub BuildProblemStatisticsPage { } # $r->print($Str); - if (! exists($ENV{'form.firstrun'})) { - $r->print('

'. + if (! exists($env{'form.firstrun'})) { + $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; } $r->rflush(); @@ -661,29 +738,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('
'.&mt('A course-wide error occurred.').'
'. + '

'.$navmap.'

'); + &clean_up(); + 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 (&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 [_1] problems',$count). - '

'); + $r->print('

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

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

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

'); + $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); } @@ -698,6 +791,7 @@ sub BuildProblemStatisticsPage { &output_sequence_statistics($r); } } + &clean_up(); return; } @@ -707,18 +801,15 @@ sub output_sequence_statistics { $r->print('

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

'); - $r->print('
'."\n". - ''."\n". - ''); + $r->print(&Apache::loncommon::start_data_table().&Apache::loncommon::start_data_table_header_row()); $r->print(&sequence_html_header()); - foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) { + $r->print(&Apache::loncommon::end_data_table_header_row()); + foreach my $seq (@sequences) { last if ($c->aborted); - next if ($seq->{'num_assess'} < 1); &compute_sequence_statistics($seq); $r->print(&sequence_html_output($seq)); } - $r->print('
'); - $r->print('
'); + $r->print(&Apache::loncommon::end_data_table()); $r->rflush(); return; } @@ -734,23 +825,22 @@ sub output_sequence_statistics { sub output_html_by_sequence { my ($r) = @_; my $c = $r->connection(); - $r->print(&html_preamble()); + $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'}."

". - '
'."\n". - ''."\n". - ''. - &statistics_table_header('no container')."\n"); + $r->print("

".$seq->compTitle."

". + &Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_header_row(). + &statistics_table_header('no container'). + &Apache::loncommon::end_data_table_header_row()."\n"); my @Data = &compute_statistics_on_sequence($seq); foreach my $data (@Data) { - $r->print(''.&statistics_html_table_data($data, - 'no container'). - "\n"); + $r->print(&Apache::loncommon::start_data_table_row(). + &statistics_html_table_data($data,'no container'). + &Apache::loncommon::end_data_table_row()."\n"); } - $r->print('
'."\n".'
'."\n"); + $r->print(&Apache::loncommon::end_data_table()."\n"); $r->rflush(); } return; @@ -760,29 +850,29 @@ sub output_html_stats { my ($r)=@_; &compute_all_statistics($r); $r->print(&html_preamble()); - &sort_data($ENV{'form.sortby'}); + &sort_data($env{'form.sortby'}); # my $count=0; foreach my $data (@StatsArray) { if ($count++ % 50 == 0) { - $r->print("\n\n"); - $r->print('
'."\n". - ''."\n". - ''. - ''. - &statistics_table_header(). - "\n"); - } - $r->print(''.&statistics_html_table_data($data)."\n"); + $r->print(&Apache::loncommon::end_data_table()); + $r->print(&Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_row(). + &statistics_table_header(). + &Apache::loncommon::end_data_table_row()); + } + $r->print(&Apache::loncommon::start_data_table_row(). + &statistics_html_table_data($data). + &Apache::loncommon::end_data_table_row()); } - $r->print("
\n
\n"); + $r->print(&Apache::loncommon::end_data_table_row()); return; } sub html_preamble { my $Str=''; $Str .= "

". - $ENV{'course.'.$ENV{'request.course.id'}.'.description'}. + $env{'course.'.$env{'request.course.id'}.'.description'}. "

\n"; my ($starttime,$endtime) = &Apache::lonstathelpers::get_time_limits(); if (defined($starttime) || defined($endtime)) { @@ -792,8 +882,8 @@ sub html_preamble { &Apache::lonlocal::locallocaltime($endtime) ).''; } - $Str .= "

".&mt('Compiled on [_1]', - &Apache::lonlocal::locallocaltime(time))."

"; + $Str .= "

".&mt('Compiled on [_1]', + &Apache::lonlocal::locallocaltime(time))."

"; return $Str; } @@ -811,7 +901,7 @@ sub statistics_html_table_data { foreach my $field (@Fields) { next if ($options =~ /no $field->{'name'}/); next if ($field->{'selected'} ne 'yes'); - $row .= '{'color'}.'"'; if (exists($field->{'align'})) { $row .= ' align="'.$field->{'align'}.'"'; } @@ -876,7 +966,7 @@ sub sequence_html_header { sub sequence_html_output { my ($seq) = @_; - my $data = $SeqStat{$seq->{'symb'}}; + my $data = $SeqStat{$seq->symb}; my $row = ''; foreach my $field (@SeqFields) { next if ($field->{'selected'} ne 'yes'); @@ -906,7 +996,7 @@ sub sequence_html_output { sub make_plot { my ($r,$plot) = @_; &compute_all_statistics($r); - &sort_data($ENV{'form.sortby'}); + &sort_data($env{'form.sortby'}); if ($plot eq 'degrees') { °rees_plot($r); } elsif ($plot eq 'tries statistics') { @@ -923,20 +1013,20 @@ sub make_single_stat_plot { my $title; my $yaxis; foreach my $field (@Fields) { next if ($field->{'name'} ne $datafield); - $title = $field->{'long_title'}; - $yaxis = $field->{'title'}; + $title = &mt($field->{'long_title'}); + $yaxis = &mt($field->{'title'}); last; } if ($title eq '' || $yaxis eq '') { # datafield is something we do not know enough about to plot - $r->print('

'. + $r->print('

'. &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) { @@ -958,7 +1048,7 @@ sub make_single_stat_plot { } # $r->print("

".&Apache::loncommon::DrawBarGraph($title, - 'Problem Number', + &mt('Problem Number'), $yaxis, $max, undef, # colors @@ -976,7 +1066,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'}; @@ -1023,10 +1113,14 @@ sub degrees_plot { my $diffdata .= ''.join(',',@Labels).''.$/. ''.join(',',@Diff).''.$/; # - my $title = 'Degree of Discrimination\nand Degree of Difficulty'; + my $title = &mt('Degree of Discrimination[_1]and Degree of Difficulty','\n'); if ($xmax > 50) { - $title = 'Degree of Discrimination and Degree of Difficulty'; + $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"; $title - Problem Number + $lt{'xlabel'} END - my $plotresult = + my $plotresult = '

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

'.$/; $r->print($plotresult); return; @@ -1111,11 +1205,16 @@ sub tries_data_plot { ''.join(',',@Mean).''.$/. ''.join(',',@STD).''.$/; # - my $title = 'Mean and S.D. of Tries'; - if ($xmax > 25) { - $title = 'Mean and Standard Deviation of Tries'; + 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"; $title - Problem Number - Number of Tries + $lt{'xlabel'} + $lt{'ylabel'} END - my $plotresult = + my $plotresult = '

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

'.$/; $r->print($plotresult); return; @@ -1159,9 +1258,10 @@ 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 = ( @@ -1174,7 +1274,7 @@ sub plot_dropdown { # my $Str= "\n".''."\n"; return $Str; @@ -1205,40 +1307,21 @@ sub Excel_output { &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 $filename = '/prtspool/'. - $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'. - time.'_'.rand(1000000000).'.xls'; - my ($starttime,$endtime) = &Apache::lonstathelpers::get_time_limits(); - # - # Create sheet - my $excel_workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename); - # - # Check for errors - if (! defined($excel_workbook)) { - $r->log_error("Error creating excel spreadsheet $filename: $!"); - $r->print(&mt("Problems creating new Excel file. ". - "This error has been logged. ". - "Please alert your LON-CAPA administrator.")); - return 0; - } - # - # The excel spreadsheet stores temporary data in files, then put them - # together. If needed we should be able to disable this (memory only). - # The temporary directory must be specified before calling 'addworksheet'. - # File::Temp is used to determine the temporary directory. - $excel_workbook->set_tempdir($Apache::lonnet::tmpdir); + 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'}; + 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)); - # - my $format = &Apache::loncommon::define_excel_formats($excel_workbook); ## ## Begin creating excel sheet ## @@ -1246,17 +1329,17 @@ sub Excel_output { # # Put the course description in the header $excel_sheet->write($rows_output,$cols_output++, - $ENV{'course.'.$ENV{'request.course.id'}.'.description'}, + $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::lonstathelpers::sections_description - (@Apache::lonstatistics::SelectedSections), + &Apache::lonstatistics::section_and_enrollment_description('plaintext'), $format->{'h3'}); - $cols_output += scalar(@Apache::lonstatistics::SelectedSections); + $cols_output += scalar(&Apache::lonstatistics::get_selected_sections()); + $cols_output += scalar(&Apache::lonstatistics::get_selected_groups()); # # Time restrictions my $time_string; @@ -1281,16 +1364,15 @@ sub Excel_output { $excel_sheet->write($rows_output,$cols_output++, 'Compiled on '.localtime(time)); # - $rows_output++; + $rows_output++; $cols_output=0; ## ## Sequence Statistics - ## + ## &write_headers($excel_sheet,$format,\$rows_output,\$cols_output, \@SeqFields); - foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) { - next if ($seq->{'num_assess'} < 1); - my $data = $SeqStat{$seq->{'symb'}}; + foreach my $seq (@sequences) { + my $data = $SeqStat{$seq->symb}; $cols_output=0; foreach my $field (@SeqFields) { next if ($field->{'selected'} ne 'yes'); @@ -1364,7 +1446,7 @@ sub write_headers { 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 + # 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'}, @@ -1386,10 +1468,9 @@ sub write_headers { sub compute_statistics_on_sequence { my ($seq) = @_; my @Data; - foreach my $res (@{$seq->{'contents'}}) { - next if ($res->{'type'} ne 'assessment'); - foreach my $part (@{$res->{'parts'}}) { - next if ($res->{'partdata'}->{$part}->{'Survey'}); + foreach my $res (&Apache::lonstathelpers::get_resources($navmap,$seq)) { + foreach my $part (@{$res->parts}) { + next if (($res->is_survey($part)) || ($res->is_anonsurvey($part))) ; # # This is where all the work happens my $data = &get_statistics($seq,$res,$part,scalar(@StatsArray)+1); @@ -1407,9 +1488,8 @@ sub compute_all_statistics { return; } my $c = $r->connection; - foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) { + foreach my $seq (@sequences) { last if ($c->aborted); - next if ($seq->{'num_assess'} < 1); &compute_sequence_statistics($seq); &compute_statistics_on_sequence($seq); } @@ -1479,13 +1559,13 @@ sub sort_data { =item &get_statistics() -Wrapper routine from the call to loncoursedata::get_problem_statistics. +Wrapper routine from the call to loncoursedata::get_problem_statistics. Calls lonstathelpers::get_time_limits() to limit the data set by time and &compute_discrimination_factor Inputs: $sequence, $resource, $part, $problem_num -Returns: Hash reference with statistics data from +Returns: Hash reference with statistics data from loncoursedata::get_problem_statistics. =cut @@ -1496,46 +1576,55 @@ sub get_statistics { my ($sequence,$resource,$part,$problem_num) = @_; # my ($starttime,$endtime) = &Apache::lonstathelpers::get_time_limits(); - my $symb = $resource->{'symb'}; - my $courseid = $ENV{'request.course.id'}; + my $symb = $resource->symb; + my $courseid = $env{'request.course.id'}; # my $data = &Apache::loncoursedata::get_problem_statistics - (\@Apache::lonstatistics::SelectedSections, + ([&Apache::lonstatistics::get_selected_sections()], + [&Apache::lonstatistics::get_selected_groups()], $Apache::lonstatistics::enrollment_status, $symb,$part,$courseid,$starttime,$endtime); $data->{'symb'} = $symb; $data->{'part'} = $part; $data->{'problem_num'} = $problem_num; - $data->{'container'} = $sequence->{'title'}; - $data->{'title'} = $resource->{'title'}; - $data->{'title.link'} = $resource->{'src'}.'?symb='. - &Apache::lonnet::escape($resource->{'symb'}); + $data->{'container'} = $sequence->compTitle; + $data->{'title'} = $resource->compTitle; + $data->{'title.link'} = $resource->src.'?symb='. + &escape($resource->symb); # if ($SelectedFields{'deg_of_disc'}) { - $data->{'deg_of_disc'} = + $data->{'deg_of_disc'} = &compute_discrimination_factor($resource,$part,$sequence); } # # Store in metadata if computations were done for all students if ($data->{'num_students'} > 1) { - my @Sections = @Apache::lonstatistics::SelectedSections; + my @Sections = &Apache::lonstatistics::get_selected_sections(); my $sections = '"'.join(' ',@Sections).'"'; $sections =~ s/&+/_/g; # Ensure no special characters $data->{'sections'}=$sections; - $data->{'course'} = $ENV{'request.course.id'}; - my $urlres=(&Apache::lonnet::decode_symb($resource->{'symb'}))[2]; - $data->{'urlres'}=$urlres; - my %storestats = + $data->{'course'} = $env{'request.course.id'}; + my $urlres=(&Apache::lonnet::decode_symb($resource->symb))[2]; + my %storestats = &LONCAPA::lonmetadata::dynamic_metadata_storage($data); - my ($dom,$user) = $urlres=~/^(\w+)\/(\w+)/; + my ($dom,$user) = ($urlres=~m{^($LONCAPA::domain_re)/($LONCAPA::username_re)}); &Apache::lonnet::put('nohist_resevaldata',\%storestats,$dom,$user); } # + $data->{'tries_per_correct'} = $data->{'tries'} / + ($data->{'num_solved'}+0.1); + # # Get the due date for research purposes (commented out most of the time) -# $data->{'duedate'} = -# &Apache::lonnet::EXT('resource.'.$part.'.duedate',$symb); -# $data->{'opendate'} = -# &Apache::lonnet::EXT('resource.'.$part.'.opendate',$symb); +# my $duedate = &Apache::lonnet::EXT('resource.'.$part.'.duedate',$symb);; +# my $opendate = &Apache::lonnet::EXT('resource.'.$part.'.opendate',$symb); +# my $maxtries = &Apache::lonnet::EXT('resource.'.$part.'.maxtries',$symb); +# my $hinttries = &Apache::lonnet::EXT('resource.'.$part.'.hinttries',$symb); + my $weight = &Apache::lonnet::EXT('resource.'.$part.'.weight',$symb); + $data->{'weight'} = $weight; +# $data->{'duedate'} = $duedate; +# $data->{'opendate'} = $opendate; +# $data->{'maxtries'} = $maxtries; +# $data->{'hinttries'} = $hinttries; # $data->{'resptypes'} = join(',',@{$resource->{'partdata'}->{$part}->{'ResponseTypes'}}); return $data; } @@ -1556,40 +1645,42 @@ Returns: integer between -1 and 1 ############################################### ############################################### sub compute_discrimination_factor { - my ($resource,$part,$sequence) = @_; + my ($resource,$part,$seq) = @_; + my $symb = $resource->symb; my @Resources; - foreach my $res (@{$sequence->{'contents'}}) { - next if ($res->{'symb'} eq $resource->{'symb'}); - push (@Resources,$res->{'symb'}); + 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 = + my $ranking = &Apache::loncoursedata::rank_students_by_scores_on_resources (\@Resources, - \@Apache::lonstatistics::SelectedSections, + [&Apache::lonstatistics::get_selected_sections()], + [&Apache::lonstatistics::get_selected_groups()], $Apache::lonstatistics::enrollment_status,undef, - $starttime,$endtime); + $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()]; + my @BottomSet = map { $_->[&Apache::loncoursedata::RNK_student()]; } @{$ranking}[0..$number_to_grab]; - my @TopSet = - map { - $_->[&Apache::loncoursedata::RNK_student()]; - } @{$ranking}[($num_students-$number_to_grab)..($num_students-1)]; + 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($resource,$part,\@BottomSet, + 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($resource,$part,\@TopSet, + 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) { @@ -1611,31 +1702,33 @@ sub compute_discrimination_factor { ## ## 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 +## s=the standard deviation of the scores on your test ## ## then: -## +## ## KR-21 rk= [K/(K-1)] * [1- (M*(K-M))/(K*s^2))] ## ############################################### ############################################### sub compute_sequence_statistics { my ($seq) = @_; - my $symb = $seq->{'symb'}; + my $symb = $seq->symb; my @Resources; - foreach my $res (@{$seq->{'contents'}}) { - next if ($res->{'type'} ne 'assessment'); - push (@Resources,$res->{'symb'}); + my $part_count; + foreach my $res (&Apache::lonstathelpers::get_resources($navmap,$seq)) { + push (@Resources,$res->symb); + $part_count += scalar(@{$res->parts}); } my ($starttime,$endtime) = &Apache::lonstathelpers::get_time_limits(); # # First compute statistics based on student scores - my ($smin,$smax,$sMean,$sSTD,$scount,$sMAX) = + my ($smin,$smax,$sMean,$sSTD,$scount,$sMAX) = &Apache::loncoursedata::score_stats - (\@Apache::lonstatistics::SelectedSections, + ([&Apache::lonstatistics::get_selected_sections()], + [&Apache::lonstatistics::get_selected_groups()], $Apache::lonstatistics::enrollment_status, \@Resources,$starttime,$endtime,undef); - $SeqStat{$symb}->{'title'} = $seq->{'title'}; + $SeqStat{$symb}->{'title'} = $seq->compTitle; $SeqStat{$symb}->{'scoremax'} = $smax; $SeqStat{$symb}->{'scoremin'} = $smin; $SeqStat{$symb}->{'scoremean'} = $sMean; @@ -1644,13 +1737,14 @@ sub compute_sequence_statistics { $SeqStat{$symb}->{'max_possible'} = $sMAX; # # Compute statistics based on the number of correct problems - # 'correct' is taken to mean + # 'correct' is taken to mean my ($cmin,$cmax,$cMean,$cSTD,$ccount)= &Apache::loncoursedata::count_stats - (\@Apache::lonstatistics::SelectedSections, + ([&Apache::lonstatistics::get_selected_sections()], + [&Apache::lonstatistics::get_selected_groups()], $Apache::lonstatistics::enrollment_status, \@Resources,$starttime,$endtime,undef); - my $K = $seq->{'num_assess_parts'}; + my $K = $part_count; my $kr_21; if ($K > 1 && $cSTD > 0) { $kr_21 = ($K/($K-1)) * (1 - $cMean*($K-$cMean)/($K*$cSTD**2));