--- loncom/interface/statistics/lonproblemanalysis.pm 2004/02/19 20:17:01 1.71 +++ loncom/interface/statistics/lonproblemanalysis.pm 2004/03/12 20:29:48 1.78 @@ -1,6 +1,6 @@ # The LearningOnline Network with CAPA # -# $Id: lonproblemanalysis.pm,v 1.71 2004/02/19 20:17:01 matthew Exp $ +# $Id: lonproblemanalysis.pm,v 1.78 2004/03/12 20:29:48 matthew Exp $ # # Copyright Michigan State University Board of Trustees # @@ -77,7 +77,6 @@ sub BuildProblemAnalysisPage { # &Apache::lonstatistics::PrepareClasslist(); # - $r->print('

'.&mt('Detailed Problem Analysis').'

'); $r->print(&CreateInterface()); # my @Students = @Apache::lonstatistics::Students; @@ -162,11 +161,11 @@ sub BuildProblemAnalysisPage { \@Students); } elsif ($current_problem->{'resptype'} eq 'numerical') { # if (exists($ENV{'form.ExcelOutput'})) { - &Apache::lonstudentsubmissions::prepare_excel_output - ($r,$current_problem,$ProblemData,\@Students); +# &Apache::lonstudentsubmissions::prepare_excel_output +# ($r,$current_problem,$ProblemData,\@Students); # } else { -# &NumericalResponseAnalysis($r,$current_problem, -# $ProblemData,\@Students); + &NumericalResponseAnalysis($r,$current_problem, + $ProblemData,\@Students); # } } else { $r->print('

This analysis is not supported

'); @@ -193,7 +192,6 @@ sub BuildProblemAnalysisPage { ######################################################### sub NumericalResponseAnalysis { my ($r,$problem,$ProblemData,$Students) = @_; - $r->print('

This analysis is not yet supported

'); my ($resource,$respid) = ($problem->{'resource'}, $problem->{'respid'}); my $analysis_html; @@ -208,40 +206,110 @@ sub NumericalResponseAnalysis { $r->print($analysis_html); return; } + # + # This next call causes all the waiting around that people complain about my ($max,$min) = &GetStudentAnswers($r,$problem,$Students); - $r->print('Maximum = '.$max.' Minimum = '.$min); - my $max_students = 0; + # + # Collate the data my %Data; foreach my $student (@$Students) { my $answer = $student->{'answer'}; $Data{$answer}++; - if ($max_students < $Data{$answer}) { - $max_students = $Data{$answer}; - } - } - foreach (5,10,20,25,50,75,100,150,200,250,500,1000,1500,2000,2500,5000) { - if ($max_students < $_) { - $max_students = $_; - last; - } } my @Labels = sort {$a <=> $b } keys(%Data); - $r->print('number of labels = '.scalar(@Labels)); my @PlotData = @Data{@Labels}; - $r->print('number of PlotData = '.scalar(@PlotData)); - my $graph = - &Apache::loncommon::DrawXYGraph('Correct Answer Distribution', - 'Correct Answer', - 'Number of students', - $max_students, - undef, - \@Labels, - [\@PlotData], - (xskip=>10)); - $r->print($graph); + # + my $width = 500; + my $height = 100; + my $plot = &one_dimensional_plot($r,500,100,scalar(@$Students), + \@Labels,\@PlotData); + $r->print($plot); return; } +sub one_dimensional_plot { + my ($r,$width,$height,$N,$Labels,$Data)=@_; + # + # Compute data -> image scaling factors + my $min = $Labels->[0]; + my $max = $Labels->[-1]; + my $h_scale = ($width-10)/($max-$min); + # + my $max_y = 0; + foreach (@$Data) { + $max_y = $_ if ($max_y < $_); + } + my $ticscale = 5; + if ($max_y * $ticscale > $height/2) { + $ticscale = int($height/2/$max_y); + $ticscale = 1 if ($ticscale < 1); + } + # + # Create the plot + my $plot = + qq{}; + for (my $idx=0;$idx[$idx] - $min); + my $top = $height/2-$Data->[$idx]*$ticscale; + my $bottom = $height/2+$Data->[$idx]*$ticscale; + $plot .= + &line($xloc,$top,$xloc,$bottom,'888888',1); + } + # + # Put the scale on last to ensure it is on top of the data. + if ($min < 0 && $max > 0) { + my $circle_x = 5+$h_scale*abs($min); # '0' in data coordinates + my $r = 4; + $plot .= &line(5,$height/2,$circle_x-$r,$height/2,'000000',1); + $plot .= &circle($circle_x,$height/2,$r+1,'000000'); + $plot .= &line($circle_x+$r,$height/2,$width-5,$height/2,'000000',1); + } else { + $plot .= &line(5,$height/2,$width-5,$height/2,'000000',1); + } + $plot .= ''; + my $plotresult = &Apache::lonxml::xmlparse($r,'web',$plot); + + my $title = 'Distribution of correct answers'; + my $result = ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''. + '
'. + ''.$title.' (N='.$N.')'. + ''. + '
'.$min.''.$plotresult.''.$max.'
'. + 'Maximum Number of Coinciding Values: '.$max_y. + '
'; + return $result; +} + +## +## Helper subroutines for . +## These should probably go somewhere more suitable soon. +sub line { + my ($x1,$y1,$x2,$y2,$color,$thickness) = @_; + return qq{$/}; +} + +sub rectangle { + my ($x1,$y1,$x2,$y2,$color,$thickness,$filled) = @_; + return qq{}; +} + +sub arc { + my ($x,$y,$width,$height,$start,$end,$color,$thickness,$filled)=@_; + return qq{}; +} + +sub circle { + my ($x,$y,$radius,$color,$thickness,$filled)=@_; + return &arc($x,$y,$radius,$radius,0,360,$color,$thickness,$filled); +} + sub GetStudentAnswers { my ($r,$problem,$Students) = @_; my %Answers; @@ -542,7 +610,8 @@ sub OR_tries_analysis { my $mintries = 1; my $maxtries = $ENV{'form.NumPlots'}; my ($table,$Foils,$Concepts) = &build_foil_index($ORdata); - if ((@$Concepts < 2) && ($ENV{'form.AnalyzeAs'} ne 'Foils')) { + if (! defined($Concepts) || + ((@$Concepts < 2) && ($ENV{'form.AnalyzeAs'} ne 'Foils'))) { $table = '

'. &mt('Not enough data for concept analysis. '. 'Performing Foil Analysis'). @@ -553,7 +622,7 @@ sub OR_tries_analysis { $mintries,$maxtries); my $analysis = ''; if ($ENV{'form.AnalyzeAs'} eq 'Foils') { - $analysis = &OR_Tries_Foil_Analysis($mintries,$maxtries,$Foils, + $analysis = &OR_Tries_Foil_Analysis($mintries,$maxtries,$Concepts, \%ResponseData,$ORdata); } else { $analysis = &OR_Tries_Concept_Analysis($mintries,$maxtries, @@ -564,82 +633,100 @@ sub OR_tries_analysis { } sub OR_Tries_Foil_Analysis { - my ($mintries,$maxtries,$Foils,$respdat,$ORdata) = @_; + my ($mintries,$maxtries,$Concepts,$respdat,$ORdata) = @_; my %ResponseData = %$respdat; # # Compute the data neccessary to make the plots my @PlotData; - foreach my $foilid (@$Foils) { - for (my $i=$mintries;$i<=$maxtries;$i++) { - if ($ResponseData{$foilid}->[$i]->{'_total'} == 0) { - push(@{$PlotData[$i]->{'_correct'}},0); - } else { - push(@{$PlotData[$i]->{'_correct'}}, - 100*$ResponseData{$foilid}->[$i]->{'_correct'}/ - $ResponseData{$foilid}->[$i]->{'_total'}); - } - foreach my $option (@{$ORdata->{'_Options'}}) { - push(@{$PlotData[$i]->{'_total'}}, - $ResponseData{$foilid}->[$i]->{'_total'}); - if ($ResponseData{$foilid}->[$i]->{'_total'} == 0) { - push (@{$PlotData[$i]->{$option}},0); + foreach my $concept (@$Concepts) { + foreach my $foilid (@{$concept->{'foils'}}) { + for (my $try=$mintries;$try<=$maxtries;$try++) { + if ($ResponseData{$foilid}->[$try]->{'_total'} == 0) { + push(@{$PlotData[$try]->{'_correct'}},0); } else { - if ($ResponseData{$foilid}->[$i]->{'_total'} == - $ResponseData{$foilid}->[$i]->{'_correct'}) { - push(@{$PlotData[$i]->{$option}},0); + push(@{$PlotData[$try]->{'_correct'}}, + 100*$ResponseData{$foilid}->[$try]->{'_correct'}/ + $ResponseData{$foilid}->[$try]->{'_total'}); + } + foreach my $option (@{$ORdata->{'_Options'}}) { + push(@{$PlotData[$try]->{'_total'}}, + $ResponseData{$foilid}->[$try]->{'_total'}); + if ($ResponseData{$foilid}->[$try]->{'_total'} == 0) { + push (@{$PlotData[$try]->{$option}},0); } else { - push (@{$PlotData[$i]->{$option}}, - 100 * $ResponseData{$foilid}->[$i]->{$option} / - ($ResponseData{$foilid}->[$i]->{'_total'} - - $ResponseData{$foilid}->[$i]->{'_correct'})); + if ($ResponseData{$foilid}->[$try]->{'_total'} == + $ResponseData{$foilid}->[$try]->{'_correct'}) { + push(@{$PlotData[$try]->{$option}},0); + } else { + push (@{$PlotData[$try]->{$option}}, + 100 * + $ResponseData{$foilid}->[$try]->{$option} / + ($ResponseData{$foilid}->[$try]->{'_total'} + - + $ResponseData{$foilid}->[$try]->{'_correct'} + )); + } } - } + } # End of foreach my $option } - } - } + } # End of foreach my $foilid + } # End of foreach my $concept # # Build a table for the plots my $analysis_html = "\n"; - my $foilkey = &build_option_index($ORdata); - for (my $i=$mintries;$i<=$maxtries;$i++) { - my $count = $ResponseData{'_total'}->[$i]; - if ($count == 0) { - $count = 'no submissions'; - } elsif ($count == 1) { - $count = '1 submission'; - } else { - $count = $count.' submissions'; - } - my $title = 'Attempt '.$i.', '.$count; + my $optionkey = &build_option_index($ORdata); + for (my $try=$mintries;$try<=$maxtries;$try++) { + my $count = $ResponseData{'_total'}->[$try]; + my $title = 'Submission '.$try.' (N='.$count.')'; my @Datasets; foreach my $option ('_correct',@{$ORdata->{'_Options'}}) { - next if (! exists($PlotData[$i]->{$option})); - push(@Datasets,$PlotData[$i]->{$option}); + next if (! exists($PlotData[$try]->{$option})); + push(@Datasets,$PlotData[$try]->{$option}); + } + # + # Put a blank in the data set between concepts + for (my $set =0;$set<=$#Datasets;$set++) { + my @Data = @{$Datasets[$set]}; + my $idx = 0; + foreach my $concept (@{$Concepts}) { + foreach my $foilid (@{$concept->{'foils'}}) { + $Datasets[$set]->[$idx++]=shift(@Data); + } + if ($concept->{'name'} ne $Concepts->[-1]->{'name'}) { + $Datasets[$set]->[$idx++] = 0; + } + } } + # + # Set up the labels needed for the bar graph + my @Labels; + my $idx = 1; + foreach my $concept (@{$Concepts}) { + foreach my $foilid (@{$concept->{'foils'}}) { + push(@Labels,$idx++); + } + push(@Labels,''); + } + # my $correctgraph = &Apache::loncommon::DrawBarGraph ($title,'Foil Number','Percent Correct', - 100,$plotcolors,undef,$Datasets[0]); + 100,$plotcolors,\@Labels,$Datasets[0]); $analysis_html.= ''; - ## - ## + + # + # next if (! defined($Datasets[0])); for (my $i=0; $i< scalar(@{$Datasets[0]});$i++) { $Datasets[0]->[$i]=0; } - $count = $ResponseData{'_total'}->[$i]-$ResponseData{'_correct'}->[$i]; - if ($count == 0) { - $count = 'no submissions'; - } elsif ($count == 1) { - $count = '1 submission'; - } else { - $count = $count.' submissions'; - } - $title = 'Attempt '.$i.', '.$count; + $count = $ResponseData{'_total'}->[$try] - + $ResponseData{'_correct'}->[$try]; + $title = 'Submission '.$try.' (N='.$count.')'; my $incorrectgraph = &Apache::loncommon::DrawBarGraph ($title,'Foil Number','% Option Chosen Incorrectly', - 100,$plotcolors,undef,@Datasets); + 100,$plotcolors,\@Labels,@Datasets); $analysis_html.= ''; - $analysis_html.= '\n"; + $analysis_html.= '\n"; } $analysis_html .= "
'.$correctgraph.''.$incorrectgraph.''.$foilkey."
'.$optionkey."
\n"; return $analysis_html; @@ -1299,6 +1386,8 @@ sub CreateInterface { ## ## Build the menu my $Str = ''; + $Str .= &Apache::lonhtmlcommon::breadcrumbs + (undef,'Detailed Problem Analysis'); $Str .= ''."\n"; $Str .= ''; $Str .= '';
'.&mt('Sections').'