--- loncom/interface/statistics/lonproblemstatistics.pm 2003/03/26 16:26:35 1.43 +++ 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.43 2003/03/26 16:26:35 matthew Exp $ +# $Id: lonproblemstatistics.pm,v 1.124 2013/12/30 13:51:37 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -26,367 +26,1503 @@ # # (Navigate problems for statistical reports # -### +############################################### +############################################### -package Apache::lonproblemstatistics; +=pod -use strict; -use Apache::lonnet(); -use Apache::lonhtmlcommon; -use Apache::loncoursedata; -use Apache::lonstatistics; +=head1 NAME -####################################################### -####################################################### +lonproblemstatistics -sub CreateInterface { - my $Str = ''; - $Str .= '
Sections | '; - $Str .= 'Sequences and Folders | '; - $Str .= 'Output | '; - $Str .= '
'."\n"; - $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',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 .= ' | '."\n"; - $Str .= &CreateAndParseOutputSelector(); - $Str .= ' |
'; + $Str .= &Apache::loncommon::start_data_table(); + $Str .= &Apache::loncommon::start_data_table_header_row(); + $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.'). + '
'); + &clean_up(); return; } + $r->rflush(); # - &Gather_Student_Data($r); - # - # - if ($output_mode eq 'html') { - $r->print("'. + &Apache::lonstatistics::section_and_enrollment_description(). + '
'); + &Excel_output($r); + } else { + $r->print(''.' 'x5); $r->rflush(); - if ($show eq 'grouped') { - &output_html_grouped_by_sequence($r); - } elsif ($show eq 'ungrouped') { - &output_html_ungrouped($r); + $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 [quant,_1,problem]',$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'}; + if ($plot eq '' || $plot eq 'none') { + undef($plot); + } + if ($sortby eq 'container' && ! defined($plot)) { + &output_sequence_statistics($r); + &output_html_by_sequence($r); + } else { + if (defined($plot)) { + &make_plot($r,$plot); + } + &output_html_stats($r); + &output_sequence_statistics($r); } - } else { - $r->print("'."\n");
- $r->print('
|
".&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 .= ''. + &mt('Unable to plot the requested statistic.'). + '
'); + return; + } # - my $sortby = undef; - foreach (@Header) { - if ($ENV{'form.sortby'} eq $_) { - $sortby = $_; + # Build up the data sets to plot + my @Labels; + my @Data; + my $max = 1; + foreach my $data (@StatsArray) { + push(@Labels,$data->{'problem_num'}); + push(@Data,$data->{$datafield}); + if ($data->{$datafield}>$max) { + $max = $data->{$datafield}; } } - if (! defined($sortby) || $sortby eq '') { - $sortby = 'Container'; + foreach (1,2,3,4,5,10,15,20,25,40,50,75,100,150,200,250,300,500,600,750, + 1000,1500,2000,2500,3000,3500,4000,5000,7500,10000,15000,20000) { + if ($max <= $_) { + $max = $_; + last; + } } - # #FFFFE6 #EEFFCC #DDFFFF FFDDDD #DDFFDD #FFDDFF - my @Sequences = &Apache::lonstatistics::Sequences_with_Assess(); - if (@Sequences > 1) { - unshift(@Header,"Container"); - $show_container = 1; + if ($max > 20000) { + $max = 10000*(int($max/10000)+1); } # - $r->print(''."\n");
- $r->rflush();
+ $r->print(" ".&Apache::loncommon::DrawBarGraph($title, + &mt('Problem Number'), + $yaxis, + $max, + undef, # colors + \@Labels, + \@Data)." \n"); + return; +} + +sub degrees_plot { + my ($r)=@_; + my $count = scalar(@StatsArray); + my $width = 50 + 10*$count; + $width = 300 if ($width < 300); + my $height = 300; + my $plot = ''; + my $ymax = 0; + my $ymin = 0; + my @Disc; my @Diff; my @Labels; + foreach my $data (@StatsArray) { + push(@Labels,$data->{'problem_num'}); + my $disc = $data->{'deg_of_disc'}; + my $diff = $data->{'deg_of_diff'}; + push(@Disc,$disc); + push(@Diff,$diff); + # + $ymin = $disc if ($ymin > $disc); + $ymin = $diff if ($ymin > $diff); + $ymax = $disc if ($ymax < $disc); + $ymax = $diff if ($ymax < $diff); + } + # + # Make sure we show relevant information. + if ($ymin < 0) { + if (abs($ymin) < 0.05) { + $ymin = 0; + } else { + $ymin = -1; + } + } + if ($ymax > 0) { + if (abs($ymax) < 0.05) { + $ymax = 0; + } else { + $ymax = 1; + } + } # - # Compile the data - my @Statsarray; - foreach my $sequence (@Sequences) { - next if ($sequence->{'num_assess'}<1); - foreach my $resource (@{$sequence->{'contents'}}) { - next if ($resource->{'type'} ne 'assessment'); - foreach my $part (@{$resource->{'parts'}}) { - my ($num,$tries,$mod,$mean,$Solved,$solved,$DegOfDiff,$STD, - $SKEW) = &Apache::loncoursedata::get_problem_statistics - (undef,$resource->{'symb'},$part, - $ENV{'request.course.id'}); - # - $show_part = 1 if ($part ne '0'); - $part = ' ' if ($part == 0); - # - my $wrongpercent = 0; - if (defined($num) && $num > 0) { - $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10; - } - push (@Statsarray, - { 'sequence' => $sequence, - 'resource' => $resource, - 'Title' => $resource->{'title'}, - 'Part' => $part, - '#Stdnts' => $num, - 'Tries' => $tries, - 'Mod' => $mod, - 'Mean' => $mean, - '#YES' => $Solved, - '#yes' => $solved, - '%Wrng' => $wrongpercent, - 'DoDiff' => $DegOfDiff, - 'S.D.' => $STD, - 'Skew' => $SKEW, - }); + my $xmax = $Labels[-1]; + if ($xmax > 50) { + if ($xmax % 10 != 0) { + $xmax = 10 * (int($xmax/10)+1); + } + } else { + if ($xmax % 5 != 0) { + $xmax = 5 * (int($xmax/5)+1); + } + } + # + my $discdata .= ''.join(',',@Labels).''.$/. + ''.join(',',@Disc).''.$/; + # + 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; +} + +sub tries_data_plot { + my ($r)=@_; + my $count = scalar(@StatsArray); + my $width = 50 + 10*$count; + $width = 300 if ($width < 300); + my $height = 300; + my $plot = ''; + my @STD; my @Mean; my @Max; my @Min; + my @Labels; + my $ymax = 5; + foreach my $data (@StatsArray) { + my $max = $data->{'mean_tries'} + $data->{'std_tries'}; + $ymax = $max if ($ymax < $max); + $ymax = $max if ($ymax < $max); + push(@Labels,$data->{'problem_num'}); + push(@STD,$data->{'std_tries'}); + push(@Mean,$data->{'mean_tries'}); + } + # + # Make sure we show relevant information. + my $xmax = $Labels[-1]; + if ($xmax > 50) { + if ($xmax % 10 != 0) { + $xmax = 10 * (int($xmax/10)+1); + } + } else { + if ($xmax % 5 != 0) { + $xmax = 5 * (int($xmax/5)+1); + } + } + $ymax = int($ymax)+1+2; + # + my $std_data .= ''.join(',',@Labels).''.$/. + ''.join(',',@Mean).''.$/; + # + my $std_error_data .= ''.join(',',@Labels).''.$/. + ''.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; +} + +sub plot_dropdown { + my $current = ''; + my $title; + # + if (defined($env{'form.plot'})) { + $current = $env{'form.plot'}; + } + # + my @Additional_Plots = ( + { graphable=>'yes', + name => 'degrees', + title => 'Difficulty Indexes' }, + { graphable=>'yes', + name => 'tries statistics', + title => 'Tries Statistics' }); + # + my $Str= "\n".''."\n"; + return $Str; +} + +############################################### +############################################### +## +## Excel output routines +## +############################################### +############################################### +sub Excel_output { + my ($r) = @_; + $r->print(''.&mt('Preparing Excel Spreadsheet').''); + ## + ## 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('
|