--- loncom/interface/statistics/lonproblemanalysis.pm 2004/03/11 19:34:10 1.75 +++ loncom/interface/statistics/lonproblemanalysis.pm 2004/03/12 21:06:32 1.79 @@ -1,6 +1,6 @@ # The LearningOnline Network with CAPA # -# $Id: lonproblemanalysis.pm,v 1.75 2004/03/11 19:34:10 matthew Exp $ +# $Id: lonproblemanalysis.pm,v 1.79 2004/03/12 21:06:32 matthew Exp $ # # Copyright Michigan State University Board of Trustees # @@ -99,7 +99,7 @@ sub BuildProblemAnalysisPage { } $r->rflush(); # - my $problem_types = '(option|radiobutton)'; + my $problem_types = '(option|radiobutton|numerical)'; if (exists($ENV{'form.problemchoice'}) && ! exists($ENV{'form.SelectAnother'})) { foreach my $button (@SubmitButtons) { @@ -161,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

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

This analysis is not yet supported

'); + my $c = $r->connection(); my ($resource,$respid) = ($problem->{'resource'}, $problem->{'respid'}); my $analysis_html; @@ -207,42 +207,114 @@ 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; + return if ($c->aborted()); + # + # 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 $c = $r->connection(); my %Answers; my ($resource,$partid,$respid) = ($problem->{'resource'}, $problem->{'part'}, @@ -254,6 +326,7 @@ sub GetStudentAnswers { $r->print("\n"); $r->rflush(); foreach my $student (@$Students) { + last if ($c->aborted()); my $sname = $student->{'username'}; my $sdom = $student->{'domain'}; my $answer = &Apache::lonstathelpers::analyze_problem_as_student @@ -262,6 +335,7 @@ sub GetStudentAnswers { &mt('last student')); $student->{'answer'} = $answer; } + return if ($c->aborted()); $r->print("
\n"); $r->rflush(); # close progress window @@ -541,7 +615,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'). @@ -570,56 +645,77 @@ sub OR_Tries_Foil_Analysis { my @PlotData; foreach my $concept (@$Concepts) { foreach my $foilid (@{$concept->{'foils'}}) { - for (my $i=$mintries;$i<=$maxtries;$i++) { - if ($ResponseData{$foilid}->[$i]->{'_total'} == 0) { - push(@{$PlotData[$i]->{'_correct'}},0); + for (my $try=$mintries;$try<=$maxtries;$try++) { + if ($ResponseData{$foilid}->[$try]->{'_total'} == 0) { + push(@{$PlotData[$try]->{'_correct'}},0); } else { - push(@{$PlotData[$i]->{'_correct'}}, - 100*$ResponseData{$foilid}->[$i]->{'_correct'}/ - $ResponseData{$foilid}->[$i]->{'_total'}); + push(@{$PlotData[$try]->{'_correct'}}, + 100*$ResponseData{$foilid}->[$try]->{'_correct'}/ + $ResponseData{$foilid}->[$try]->{'_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); + push(@{$PlotData[$try]->{'_total'}}, + $ResponseData{$foilid}->[$try]->{'_total'}); + if ($ResponseData{$foilid}->[$try]->{'_total'} == 0) { + push (@{$PlotData[$try]->{$option}},0); } else { - if ($ResponseData{$foilid}->[$i]->{'_total'} == - $ResponseData{$foilid}->[$i]->{'_correct'}) { - push(@{$PlotData[$i]->{$option}},0); + if ($ResponseData{$foilid}->[$try]->{'_total'} == + $ResponseData{$foilid}->[$try]->{'_correct'}) { + push(@{$PlotData[$try]->{$option}},0); } else { - push (@{$PlotData[$i]->{$option}}, - 100 * $ResponseData{$foilid}->[$i]->{$option} / - ($ResponseData{$foilid}->[$i]->{'_total'} - - $ResponseData{$foilid}->[$i]->{'_correct'})); + 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 $optionkey = &build_option_index($ORdata); for (my $try=$mintries;$try<=$maxtries;$try++) { my $count = $ResponseData{'_total'}->[$try]; - if ($count == 0) { - $count = 'no submissions'; - } elsif ($count == 1) { - $count = '1 submission'; - } else { - $count = $count.' submissions'; - } - my $title = 'Attempt '.$try.', '.$count; + my $title = 'Submission '.$try.' (N='.$count.')'; my @Datasets; foreach my $option ('_correct',@{$ORdata->{'_Options'}}) { 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.= ''; # @@ -630,17 +726,10 @@ sub OR_Tries_Foil_Analysis { } $count = $ResponseData{'_total'}->[$try] - $ResponseData{'_correct'}->[$try]; - if ($count == 0) { - $count = 'no submissions'; - } elsif ($count == 1) { - $count = '1 submission'; - } else { - $count = $count.' submissions'; - } - $title = 'Attempt '.$try.', '.$count; + $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"; }
'.$correctgraph.''.$incorrectgraph.''.$optionkey."