');
- $r->print(&OptionResponseProblemSelector());
+ $r->print(&Apache::lonstathelpers::ProblemSelector
+ ($problem_types));
}
}
@@ -136,357 +181,1156 @@ sub BuildProblemAnalysisPage {
#########################################################
#########################################################
##
-## Misc interface routines use by analysis code
+## Numerical Response Routines
##
#########################################################
#########################################################
-sub build_foil_index {
- my ($ORdata) = @_;
- my %Foildata = %{$ORdata->{'Foils'}};
- my @Foils = sort(keys(%Foildata));
- my %Concepts;
- foreach my $foilid (@Foils) {
- push(@{$Concepts{$Foildata{$foilid}->{'Concept'}}},
- $foilid);
- }
- undef(@Foils);
- # Having gathered the concept information in a hash, we now translate it
- # into an array because we need to be consistent about order.
- # Also put the foils in order, too.
- my $sortfunction = sub {
- my %Numbers = (one => 1,
- two => 2,
- three => 3,
- four => 4,
- five => 5,
- six => 6,
- seven => 7,
- eight => 8,
- nine => 9,
- ten => 10,);
- my $a1 = $a;
- my $b1 = $b;
- if (exists($Numbers{$a})) {
- $a1 = $Numbers{$a};
- }
- if (exists($Numbers{$b})) {
- $b1 = $Numbers{$b};
+sub NumericalResponseAnalysis {
+ my ($r,$problem,$problem_data,$Students) = @_;
+ my $c = $r->connection();
+ my ($resource,$partid,$respid) = ($problem->{'resource'},
+ $problem->{'part'},
+ $problem->{'respid'});
+ #
+ if (scalar(@{$resource->{'parts'}})>1) {
+ if (@{$resource->{'partdata'}->{$partid}->{'ResponseIds'}}>1) {
+ $r->print('
'.
+ &mt('The analysis you have selected is '.
+ 'not supported at this time').
+ '
');
+ }
+ }
+ }
+}
+
+#########################################################
+#
+# Option Response: Tries Analysis
+#
+#########################################################
+sub OR_tries_analysis {
+ my ($r,$PerformanceData,$ORdata) = @_;
+ my $mintries = 1;
+ my $maxtries = $ENV{'form.NumPlots'};
+ my ($table,$Foils,$Concepts) = &build_foil_index($ORdata);
+ my %response_data = &OR_analyze_by_tries($r,$PerformanceData,
+ $mintries,$maxtries);
+ my $analysis = '';
+ #
+ # Compute the data necessary to make the plots
+ my @foil_plot;
+ my @concept_data;
+ for (my $j=0;$j<=scalar(@$Concepts);$j++) {
+ my $concept = $Concepts->[$j];
+ foreach my $foilid (@{$concept->{'foils'}}) {
+ for (my $try=$mintries;$try<=$maxtries;$try++) {
+ # concept analysis data
+ $concept_data[$j]->{'_correct'} +=
+ $response_data{$foilid}->[$try]->{'_correct'};
+ $concept_data[$j]->{'_total'} +=
+ $response_data{$foilid}->[$try]->{'_total'};
#
- # Gather the per-attempt data
- my ($correct,$incorrect,$total);
- foreach my $foil (@{$concept->{'foils'}}) {
- $correct += $ResponseData{$foil}->[$i]->{'correct'};
- $incorrect += $ResponseData{$foil}->[$i]->{'incorrect'};
- $total += $ResponseData{$foil}->[$i]->{'total'};
+ # foil analysis data
+ if ($response_data{$foilid}->[$try]->{'_total'} == 0) {
+ push(@{$foil_plot[$try]->{'_correct'}},0);
+ } else {
+ push(@{$foil_plot[$try]->{'_correct'}},
+ 100*$response_data{$foilid}->[$try]->{'_correct'}/
+ $response_data{$foilid}->[$try]->{'_total'});
}
- push (@{$PlotData[$i]->{'correct'}}, $correct);
- push (@{$PlotData[$i]->{'incorrect'}},$incorrect);
- push (@{$PlotData[$i]->{'total'}}, $total);
- my $percent;
+ foreach my $option (@{$ORdata->{'_Options'}}) {
+ push(@{$foil_plot[$try]->{'_total'}},
+ $response_data{$foilid}->[$try]->{'_total'});
+ if ($response_data{$foilid}->[$try]->{'_total'} == 0) {
+ push (@{$foil_plot[$try]->{$option}},0);
+ } else {
+ if ($response_data{$foilid}->[$try]->{'_total'} ==
+ $response_data{$foilid}->[$try]->{'_correct'}) {
+ push(@{$foil_plot[$try]->{$option}},0);
+ } else {
+ push (@{$foil_plot[$try]->{$option}},
+ 100 *
+ $response_data{$foilid}->[$try]->{$option} /
+ ($response_data{$foilid}->[$try]->{'_total'}
+ -
+ $response_data{$foilid}->[$try]->{'_correct'}
+ ));
+ }
+ }
+ } # End of foreach my $option
+ }
+ } # End of foreach my $foilid
+ } # End of concept loops
+ #
+ # Build a table for the plots
+ my $analysis_html = "
\n";
+ my $optionkey = &build_option_index($ORdata);
+ my $num_concepts = 1;
+ if (defined($Concepts)) { $num_concepts = scalar(@$Concepts); }
+ #
+ for (my $try=$mintries;$try<=$maxtries;$try++) {
+ my $concept_graph='';
+ if ($num_concepts > 1) {
+ #
+ # Create concept plot
+ my @concept_plot_data;
+ for (my $j=0;$j<=$#concept_data;$j++) {
+ my $total = $concept_data[$j]->{'_total'};
if ($total == 0) {
- $percent = 0;
+ $concept_plot_data[$j] = 0;
} else {
- $percent = $correct/$total;
+ $concept_plot_data[$j] = 100 *
+ sprintf('%0.3f',
+ $concept_data[$j]->{'_correct'} / $total);
}
- push (@{$PlotData[$i]->{'good'}},100*$percent);
- push (@{$PlotData[$i]->{'bad'}},100*(1-$percent));
}
+ #
+ my $title;
+ my $count = $response_data{'_total'}->[$try];
+ $title = 'Attempt '.$try.' (N='.$count.')';
+ $concept_graph = &Apache::loncommon::DrawBarGraph
+ ($title,'Concept Number','Percent Correct',
+ 100,$plotcolors,undef,\@concept_plot_data);
}
- }
- #
- # Build a table for the plots
- $table .= "
\n";
- my @Plots;
- for (my $i=$mintries;$i<=$maxtries;$i++) {
- my $minstu = $PlotData[$i]->{'total'}->[0];
- my $maxstu = $PlotData[$i]->{'total'}->[0];
- foreach my $count (@{$PlotData[$i]->{'total'}}) {
- if ($minstu > $count) {
- $minstu = $count;
- }
- if ($maxstu < $count) {
- $maxstu = $count;
- }
- }
- $maxstu = 0 if (! $maxstu);
- $minstu = 0 if (! $minstu);
- my $title;
- if ($maxstu == $minstu) {
- $title = 'Attempt '.$i.', '.$maxstu.' students';
- } else {
- $title = 'Attempt '.$i.', '.$minstu.'-'.$maxstu.' students';
+ #
+ # Create Foil Plots
+ my $count = $response_data{'_total'}->[$try];
+ my $title = 'Attempt '.$try.' (N='.$count.')';
+ my @Datasets;
+ foreach my $option ('_correct',@{$ORdata->{'_Options'}}) {
+ next if (! exists($foil_plot[$try]->{$option}));
+ push(@Datasets,$foil_plot[$try]->{$option});
}
- my $graphlink = &Apache::loncommon::DrawGraph($title,
- $xlabel,
- 'Percent Correct',
- 100,
- $PlotData[$i]->{'good'},
- $PlotData[$i]->{'bad'});
- push(@Plots,$graphlink);
- }
- #
- # Should this be something the user can set? Too many dialogs!
- my $plots_per_row = 2;
- while (my $plotlink = shift(@Plots)) {
- $table .= '
'.$plotlink.'
';
- for (my $i=1;$i<$plots_per_row;$i++) {
- if ($plotlink = shift(@Plots)) {
- $table .= '
'.$plotlink.'
';
- } else {
- $table .= '
';
+ #
+ # 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;
+ }
}
}
- $table .= "
\n";
+ #
+ # 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 $correct_graph = &Apache::loncommon::DrawBarGraph
+ ($title,'Foil Number','Percent Correct',
+ 100,$plotcolors,\@Labels,$Datasets[0]);
+
+ #
+ #
+ next if (! defined($Datasets[0]));
+ for (my $i=0; $i< scalar(@{$Datasets[0]});$i++) {
+ $Datasets[0]->[$i]=0;
+ }
+ $count = $response_data{'_total'}->[$try] -
+ $response_data{'_correct'}->[$try];
+ $title = 'Attempt '.$try.' (N='.$count.')';
+ my $incorrect_graph = &Apache::loncommon::DrawBarGraph
+ ($title,'Foil Number','% Option Chosen Incorrectly',
+ 100,$plotcolors,\@Labels,@Datasets);
+ $analysis_html.=
+ '
'.
+ '
'.$concept_graph.'
'.
+ '
'.$correct_graph.'
'.
+ '
'.$incorrect_graph.'
'.
+ '
'.$optionkey.'
'.
+ '
'.$/;
}
- $table .= "
\n";
- return ($table);
+ $analysis_html .= "
\n";
+ $table .= $analysis_html;
+ return $table;
}
-sub analyze_option_data_by_tries {
- my ($PerformanceData,$mintries,$maxtries) = @_;
+sub OR_analyze_by_tries {
+ my ($r,$PerformanceData,$mintries,$maxtries) = @_;
my %Trydata;
$mintries = 1 if (! defined($mintries) || $mintries < 1);
$maxtries = $mintries if (! defined($maxtries) || $maxtries < $mintries);
foreach my $row (@$PerformanceData) {
next if (! defined($row));
- my ($grading,$submission,$time,$tries) = @$row;
- my @Foilgrades = split('&',$grading);
- my @Foilsubs = split('&',$submission);
- for (my $numtries = 1; $numtries <= $maxtries; $numtries++) {
- if ($tries == $numtries) {
- foreach my $foilgrade (@Foilgrades) {
- my ($foilid,$correct) = split('=',$foilgrade);
- if ($correct) {
- $Trydata{$foilid}->[$numtries]->{'correct'}++;
- } else {
- $Trydata{$foilid}->[$numtries]->{'incorrect'}++;
- }
- }
+ my $tries = &get_tries_from_row($row);
+ my %Row = &Process_OR_Row($row);
+ next if (! %Row);
+ while (my ($foilid,$href) = each(%Row)) {
+ if (! ref($href)) {
+ $Trydata{$foilid}->[$tries] += $href;
+ next;
+ }
+ while (my ($option,$value) = each(%$href)) {
+ $Trydata{$foilid}->[$tries]->{$option}+=$value;
}
- }
- }
- foreach my $foilid (keys(%Trydata)) {
- foreach my $tryhash (@{$Trydata{$foilid}}) {
- next if ((! exists($tryhash->{'correct'}) &&
- ! exists($tryhash->{'incorrect'})) ||
- ($tryhash->{'correct'} < 1 &&
- $tryhash->{'incorrect'} < 1));
- $tryhash->{'total'} = $tryhash->{'correct'} +
- $tryhash->{'incorrect'};
}
}
return %Trydata;
}
#########################################################
+#
+# Option Response: Time Analysis
+#
#########################################################
-##
-## Time Analysis
-##
-#########################################################
-#########################################################
-sub time_analysis {
- my ($PerformanceData,$ORdata) = @_;
- my $num_plots = $ENV{'form.NumPlots'};
+sub OR_time_analysis {
+ my ($performance_data,$ORdata) = @_;
my ($table,$Foils,$Concepts) = &build_foil_index($ORdata);
- my $num_data = scalar(@$PerformanceData)-1;
+ my $foilkey = &build_option_index($ORdata);
+ my $num_concepts = 1;
+ if (defined($Concepts)) { $num_concepts = scalar(@$Concepts); }
+ #
+ if ($num_concepts < 2) {
+ $table = '
'.
+ &mt('Not enough data for concept analysis. '.
+ 'Performing Foil Analysis').
+ '
'.$table;
+ }
+ #
+ my $num_plots = $ENV{'form.NumPlots'};
+ my $num_data = scalar(@$performance_data)-1;
my $percent = sprintf('%2f',100/$num_plots);
+ #
+ $table .= "
\n";
for (my $i=0;$i<$num_plots;$i++) {
+ ##
my $starttime = &Apache::lonhtmlcommon::get_date_from_form
('startdate_'.$i);
my $endtime = &Apache::lonhtmlcommon::get_date_from_form
('enddate_'.$i);
- my ($begin_index,$end_index,$plottitle,$plothtml,$data);
if (! defined($starttime) || ! defined($endtime)) {
- $begin_index = $i*int($num_data/$num_plots);
- $end_index = ($i+1)*int($num_data/$num_plots);
- my $lownum = sprintf('%2.1f',$i*$percent);
- $lownum =~ s/(\.0)$//;
- my $highnum = sprintf('%2.1f',($i+1)*$percent);
- $highnum =~ s/(\.0)$//;
- $plottitle = $lownum.'% to '.$highnum.'% of submissions';
- } else {
- my $j;
- while (++$j < scalar(@$PerformanceData)) {
- last if ($PerformanceData->[$j]->[2] > $starttime);
- }
- $begin_index = $j;
- while (++$j < scalar(@$PerformanceData)) {
- last if ($PerformanceData->[$j]->[2] > $endtime);
- }
- $end_index = $j;
- $plottitle = 'Tries plot '.($i+1);
- }
- ($plothtml,$starttime,$endtime,$data) =
- &analyze_option_data_by_time($PerformanceData,
- $begin_index,$end_index,
- $plottitle,
- @$Foils);
+ my $sec_in_day = 86400;
+ my $last_sub_time = &get_time_from_row($performance_data->[-1]);
+ my ($sday,$smon,$syear);
+ (undef,undef,undef,$sday,$smon,$syear) =
+ localtime($last_sub_time - $sec_in_day*$i);
+ $starttime = &Time::Local::timelocal(0,0,0,$sday,$smon,$syear);
+ $endtime = $starttime + $sec_in_day;
+ if ($i == ($num_plots -1 )) {
+ $starttime = &get_time_from_row($performance_data->[0]);
+ }
+ }
+ $table .= '
'.
+ &mt('Data from [_1] to [_2]',
+ &Apache::lonlocal::locallocaltime($starttime),
+ &Apache::lonlocal::locallocaltime($endtime)).'
'.$/;
my $startdateform = &Apache::lonhtmlcommon::date_setter
('Statistics','startdate_'.$i,$starttime);
my $enddateform = &Apache::lonhtmlcommon::date_setter
('Statistics','enddate_'.$i,$endtime);
- $plothtml.= " \n".
- "Start Time: "." ".$startdateform." \n".
- "End Time : "." ".$enddateform." \n";
- $table.=$plothtml;
+ #
+ my $begin_index;
+ my $end_index;
+ my $j;
+ while (++$j < scalar(@$performance_data)) {
+ last if (&get_time_from_row($performance_data->[$j])
+ > $starttime);
+ }
+ $begin_index = $j;
+ while (++$j < scalar(@$performance_data)) {
+ last if (&get_time_from_row($performance_data->[$j]) > $endtime);
+ }
+ $end_index = $j;
+ ##
+ my $interval = {
+ begin_index => $begin_index,
+ end_index => $end_index,
+ startdateform => $startdateform,
+ enddateform => $enddateform,
+ };
+ my $concept_correct_plot = '';
+ if ($num_concepts > 1) {
+ $concept_correct_plot =
+ &OR_Concept_Time_Analysis($performance_data,
+ $ORdata,$Concepts,
+ $interval);
+ }
+ my ($foil_correct_plot,$foil_incorrect_plot) =
+ &OR_Foil_Time_Analysis($performance_data,$ORdata,
+ $Foils,$Concepts,$interval);
+ $table .= '