');
}
- 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};
+ #
+ my $analysis_html;
+ my $PerformanceData = &Apache::loncoursedata::get_response_data
+ (\@Apache::lonstatistics::SelectedSections,
+ $Apache::lonstatistics::enrollment_status,
+ $resource->{'symb'},$respid);
+ if (! defined($PerformanceData) ||
+ ref($PerformanceData) ne 'ARRAY' ) {
+ $analysis_html = '
'.
+ &mt('There is no submission data for this resource').
+ '
';
+ $r->print($analysis_html);
+ return;
+ }
+ #
+ # This next call causes all the waiting around that people complain about
+ &Apache::lonstathelpers::GetStudentAnswers($r,$problem,$Students,
+ 'Statistics',
+ 'stats_status');
+ return if ($c->aborted());
+ #
+ # Collate the data
+ my %Data;
+ foreach my $student (@$Students) {
+ my $answer = $student->{'answer'};
+ $Data{$answer}++;
+ }
+ my @Labels = sort {$a <=> $b } keys(%Data);
+ my @PlotData = @Data{@Labels};
+ #
+ 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];
+ if ($max == $min) {
+ $max =$min+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 text {
+ my ($x,$y,$color,$text,$font,$direction) = @_;
+ if (! defined($font) || $font !~ /^(tiny|small|medium|large|giant)$/) {
+ $font = 'medium';
+ }
+ if (! defined($direction) || $direction ne 'vertical') {
+ $direction = '';
+ }
+ return qq{$text};
+}
+
+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 build_student_data_worksheet {
+ my ($workbook,$format) = @_;
+ my $rows_output = 3;
+ my $cols_output = 0;
+ my $worksheet = $workbook->addworksheet('Student Data');
+ $worksheet->write($rows_output++,0,'Student Data',$format->{'h3'});
+ my @Headers = ('full name','username','domain','section',
+ "student\nnumber",'identifier');
+ $worksheet->write_row($rows_output++,0,\@Headers,$format->{'header'});
+ my @Students = @Apache::lonstatistics::Students;
+ my $studentrows = &Apache::loncoursedata::get_student_data(\@Students);
+ my %ids;
+ foreach my $row (@$studentrows) {
+ my ($mysqlid,$student) = @$row;
+ $ids{$student}=$mysqlid;
+ }
+ foreach my $student (@Students) {
+ my $name_domain = $student->{'username'}.':'.$student->{'domain'};
+ $worksheet->write_row($rows_output++,0,
+ [$student->{'fullname'},
+ $student->{'username'},$student->{'domain'},
+ $student->{'section'},$student->{'id'},
+ $ids{$name_domain}]);
+ }
+ return $worksheet;
+}
+
+#########################################################
+#########################################################
+##
+## Radio Response Routines
+##
+#########################################################
+#########################################################
+sub RadioResponseAnalysis {
+ my ($r,$problem,$problem_analysis,$students) = @_;
+ if ($ENV{'form.AnalyzeOver'} eq 'tries') {
+ &RR_tries_analysis($r,$problem,$problem_analysis,$students);
+ } elsif ($ENV{'form.AnalyzeOver'} eq 'time') {
+ &RR_static_time_analysis($r,$problem,$problem_analysis,$students);
+ } else {
+ $r->print('Bad request');
+ }
+ return;
+}
+
+sub RR_computed_tries_analysis {
+ my ($r,$problem,$problem_analysis) = @_;
+ my ($resource,$partid,$respid) = ($problem->{'resource'},
+ $problem->{'part'},
+ $problem->{'respid'});
+ $r->print('The tries answer you seek must be computed');
+ # Gather student data
+ # for each try
+ # loop through data, classifying it by
+ # correct foil -> selected foil
+ # if there is concept data
+ # make a concept correct plot
+ # for each correct foil
+ # make a plot of the data
+}
+
+sub RR_computed_time_analysis {
+ my ($r,$problem,$problem_analysis) = @_;
+ my ($resource,$partid,$respid) = ($problem->{'resource'},
+ $problem->{'part'},
+ $problem->{'respid'});
+ $r->print('The time answer you seek must be computed');
+ # Gather student data
+ # for time division
+ # limit to between start time & end time
+ # loop through data, classifying it by
+ # correct foil -> selected foil
+ # if there is concept data
+ # make a concept correct plot
+ # for each correct foil
+ # make a plot of the data
+}
+
+sub RR_tries_analysis {
+ my ($r,$problem,$problem_analysis,$students) = @_;
+ my ($resource,$partid,$respid) = ($problem->{'resource'},
+ $problem->{'part'},
+ $problem->{'respid'});
+ #
+ my $analysis_html;
+ my $foildata = $problem_analysis->{'_Foils'};
+ my ($table,$foils,$concepts) = &build_foil_index($problem_analysis);
+ $analysis_html .= $table;
+ # Gather student data
+ my $response_data = &Apache::loncoursedata::get_response_data
+ (\@Apache::lonstatistics::SelectedSections,
+ $Apache::lonstatistics::enrollment_status,
+ $resource->{'symb'},$respid);
+ my $correct; # either a hash reference or a scalar
+ if ($problem_analysis->{'answercomputed'} || scalar(@$concepts) > 1) {
+ # This takes a while for large classes...
+ &Apache::lonstathelpers::GetStudentAnswers($r,$problem,$students,
+ 'Statistics',
+ 'stats_status');
+ foreach my $student (@$students) {
+ my ($idx,@remainder) = split('&',$student->{'answer'});
+ my ($answer) = ($remainder[$idx]=~/^(.*)=([^=]*)$/);
+ $correct->{$student->{'username'}.':'.$student->{'domain'}}=
+ &Apache::lonnet::unescape($answer);
}
- if (exists($Numbers{$b})) {
- $b1 = $Numbers{$b};
+ } else {
+ foreach my $foil (keys(%$foildata)) {
+ if ($foildata->{$foil}->{'value'} eq 'true') {
+ $correct = $foildata->{$foil}->{'name'};
+ last;
+ }
}
- $a1 cmp $b1;
- };
- my @Concepts;
- foreach my $concept (sort $sortfunction (keys(%Concepts))) {
- push(@Concepts,{name => $concept,
- foils => [@{$Concepts{$concept}}]});
- push(@Foils,(@{$Concepts{$concept}}));
+ }
+ if (! defined($response_data) ||
+ ref($response_data) ne 'ARRAY' ) {
+ $analysis_html = '
'.
+ &mt('There is no submission data for this resource').
+ '
';
+ $r->print($analysis_html);
+ return;
}
#
- # Build up the table of row labels.
- my $table = '
'."\n";
- $table .= '
'.
- '
'.&mt('Concept Number').'
'.
- '
'.&mt('Concept').'
'.
- '
'.&mt('Foil Number').'
'.
- '
'.&mt('Foil Name').'
'.
- '
'.&mt('Foil Text').'
'.
- '
'.&mt('Correct Value').'
'.
- "
\n";
- my $conceptindex = 1;
- my $foilindex = 1;
- foreach my $concept (@Concepts) {
- my @FoilsInConcept = @{$concept->{'foils'}};
- my $firstfoil = shift(@FoilsInConcept);
- $table .= '
';
+ $r->print($analysis_html);
+}
+
+
+sub RR_concept_plot {
+ my ($concepts,$foil_data,$title) = @_;
+ #
+ my %correct_by_concept;
+ my %incorrect_by_concept;
+ my %true;
+ foreach my $concept (@$concepts) {
+ foreach my $foil (@{$concept->{'foils'}}) {
+ next if (! exists($foil_data->{$foil}));
+ foreach my $choice (keys(%{$foil_data->{$foil}})) {
+ if ($choice eq $foil) {
+ $correct_by_concept{$concept->{'name'}} +=
+ $foil_data->{$foil}->{$choice};
+ } else {
+ $incorrect_by_concept{$concept->{'name'}} +=
+ $foil_data->{$foil}->{$choice};
+
+ }
+ }
+ }
+ }
+ #
+ # need arrays for incorrect and correct because we want to use different
+ # colors for them
+ my @correct;
+ #
+ my $total =0;
+ for (my $i=0;$i[$i];
+ $correct[$i] = $correct_by_concept{$concept->{'name'}};
+ $total += $correct_by_concept{$concept->{'name'}}+
+ $incorrect_by_concept{$concept->{'name'}};
+ }
+ if ($total == 0) { return ''; };
+ for (my $i=0;$i<=$#correct;$i++) {
+ $correct[$i] = sprintf('%0f',$correct[$i]/$total*100);
+ }
+ my $xlabel = 'concept';
+ $title.= ' (N='.$total.')';
+ my $plot= &Apache::loncommon::DrawBarGraph($title,
+ $xlabel,
+ 'Percent Choosing',
+ 100,
+ ['#33ff00','#ff3300'],
+ undef,
+ \@correct);
+ return $plot;
+}
+
+
+sub RR_create_percent_selected_plot {
+ my ($foils,$foil_data,$title) = @_;
+ #
+ my %foil_selections;
+ my %true;
+ foreach my $foil (@$foils) {
+ # foil_data has format $foil_data->{true_foil}->{selected foil}
+ next if (! exists($foil_data->{$foil}));
+ $true{$foil}++;
+ while (my ($f,$count)= each(%{$foil_data->{$foil}})) {
+ $foil_selections{$f}+=$count;
+ }
+ }
+ #
+ # need arrays for incorrect and correct because we want to use different
+ # colors for them
+ my @correct;
+ my @incorrect;
+ #
+ my $total =0;
+ for (my $i=0;$i[$i];
+ if ($true{$foil}) {
+ $correct[$i] = $foil_selections{$foil};
+ $incorrect[$i] = 0;
+ } else {
+ $correct[$i] = 0;
+ $incorrect[$i] = $foil_selections{$foil};
+ }
+ $total+=$foil_selections{$foil};
+ }
+ if ($total == 0) { return ''; };
+ for (my $i=0;$i<=$#correct;$i++) {
+ $correct[$i] = sprintf('%0f',$correct[$i]/$total*100);
+ }
+ for (my $i=0;$i<=$#incorrect;$i++) {
+ $incorrect[$i] = sprintf('%0f',$incorrect[$i]/$total*100);
+ }
+ my $xlabel = 'foil chosen';
+ $title.= ' (N='.$total.')';
+ my $plot= &Apache::loncommon::DrawBarGraph($title,
+ $xlabel,
+ 'Percent Choosing',
+ 100,
+ ['#33ff00','#ff3300'],
+ undef,
+ \@correct,
+ \@incorrect);
+ return $plot;
+}
+
+
+sub RR_create_stacked_selection_plot {
+ my ($foils,$foil_data,$title)=@_;
+ #
+ my @correct_choice; # the green row
+ my @dataset; # array of array refs - multicolor rows.
+ my %filled;
+ my @labels;
+ my $count=-1;
+ my %column;
+ for (my $i=0;$i{$foils->[$i]}));
+ my $correct_foil = $foils->[$i];
+ push(@labels,$i+1);
+ $column{$correct_foil}= ++$count;
+ for (my $j=0;$j{$correct_foil}->{$foils->[$j]};
+ }
+ $dataset[$j]->[$column{$correct_foil}]=$value;
+ }
+ }
+ #
+ return '' if (! scalar(keys(%column)));
+ #
+ my $grand_total = 0;
+ my %count_per_foil;
+ while (my ($foil,$bar) = each (%column)) {
+ my $bar_total = 0;
+ for (my $j=0;$j[$bar];
+ }
+ next if ($bar_total == 0);
+ for (my $j=0;$j[$bar] =
+ sprintf('%2f',$dataset[$j]->[$bar]/$bar_total * 100);
+ }
+ $count_per_foil{$foil}=' (N='.$bar_total.')';
+ $grand_total += $bar_total;
+ }
+ if ($grand_total == 0) {
+ return ('',undef);
+ }
+ my @empty_row = ();
+ foreach (@{$dataset[0]}) {
+ push(@empty_row,0);
+ }
+ #
+ $title .= ' (N='.$grand_total.')';
+ my $graph = &Apache::loncommon::DrawBarGraph
+ ($title,'Correct Foil','foils chosen Incorrectly',
+ 100,$plotcolors,\@labels,\@empty_row,@dataset);
+ return ($graph,\%count_per_foil);
+}
+
+
+# if $correct is a hash ref, it is assumed to be indexed by student names.
+# the values are assumed to be hash refs with a key of 'answer'.
+sub RR_classify_response_data {
+ my ($full_row_data,$correct,$function) = @_;
+ my %submission_data;
+ foreach my $row (@$full_row_data) {
+ my %subm = &hashify_attempt($row);
+ if (ref($correct) eq 'HASH') {
+ $subm{'correct'} = $correct->{$subm{'student'}};
+ } else {
+ $subm{'correct'} = $correct;
+ }
+ $subm{'submission'} =~ s/=\d+\s*$//;
+ if (&$function(\%subm)) {
+ $submission_data{$subm{'correct'}}->{$subm{'submission'}}++;
+ }
+ }
+ return \%submission_data;
}
+sub RR_static_time_analysis {
+ my ($r,$problem,$problem_analysis) = @_;
+ my ($resource,$partid,$respid) = ($problem->{'resource'},
+ $problem->{'part'},
+ $problem->{'respid'});
+ $r->print('
The time answer you seek is static
');
+ my $analysis_html;
+ # Gather student data
+ my $response_data = &Apache::loncoursedata::get_response_data
+ (\@Apache::lonstatistics::SelectedSections,
+ $Apache::lonstatistics::enrollment_status,
+ $resource->{'symb'},$respid);
+ if (! defined($response_data) ||
+ ref($response_data) ne 'ARRAY' ) {
+ $analysis_html = '
'.
+ &mt('There is no submission data for this resource').
+ '
';
+ $r->print($analysis_html);
+ return;
+ }
+ # for time division
+
+ # limit to between start time & end time
+ # loop through data, classifying it by
+ # correct foil -> selected foil
+ # if there is concept data
+ # make a concept correct plot
+ # for each correct foil
+ # make a plot of the data
+
+}
+
+
+=pod
+
+
+ } elsif ($ENV{'form.AnalyzeOver'} eq 'tries') {
+ $analysis_html .= &RR_tries_Analysis($r,$problem->{'resource'},
+ $PerformanceData,$problem_data);
+ } elsif ($ENV{'form.AnalyzeOver'} eq 'time') {
+ $analysis_html .= &RR_time_Analysis($r,$problem->{'resource'},
+ $PerformanceData,$problem_data);
+ } else {
+ $analysis_html .= '
'.
+ &mt('The analysis you have selected is not supported at this time').
+ '
\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';
- }
- 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 .= '
';
+ #
+ # Create Foil Plots
+ my $data_count = $response_data{'_total'}->[$try];
+ my $correct = $response_data{'_correct'}->[$try];
+ my @Datasets;
+ foreach my $option ('_correct',@{$ORdata->{'_Options'}}) {
+ next if (! exists($foil_plot[$try]->{$option}));
+ push(@Datasets,$foil_plot[$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,'');
}
- $table .= "
\n";
+ #
+ my $correct_graph = &Apache::loncommon::DrawBarGraph
+ ('Correct Statements','Statement','% Answered Correct',
+ 100,$plotcolors,\@Labels,$Datasets[0]);
+
+ #
+ #
+ next if (! defined($Datasets[0]));
+ for (my $i=0; $i< scalar(@{$Datasets[0]});$i++) {
+ $Datasets[0]->[$i]=0;
+ }
+ my $count = $response_data{'_total'}->[$try] -
+ $response_data{'_correct'}->[$try];
+ my $incorrect_graph = &Apache::loncommon::DrawBarGraph
+ ('Incorrect Statements','Statement','% Chosen Incorrectly',
+ 100,$plotcolors,\@Labels,@Datasets);
+ $analysis_html.=
+ '
\n";
+ $table .= '';
+ #
return $table;
}
-sub analyze_option_data_by_time {
- my ($PerformanceData,$begin_index,$end_index,$description,@Concepts) = @_;
- my %TimeData;
- #
- # Get the start and end times for this segment of the plot
- my $starttime = $PerformanceData->[$begin_index]->[2];
- my $endtime = $PerformanceData->[$end_index ]->[2];
- #
- # Compute the number getting the foils correct or incorrects
- for (my $i=$begin_index;$i<=$end_index;$i++) {
- my $row = $PerformanceData->[$i];
- next if (! defined($row));
- my ($grading,$submission,$time,$tries) = @$row;
- my @Foilgrades = split('&',$grading);
- my @Foilsubs = split('&',$submission);
- foreach my $foilgrade (@Foilgrades) {
- my ($foilid,$correct) = split('=',$foilgrade);
- if ($correct) {
- $TimeData{$foilid}->{'correct'}++;
+sub OR_Foil_Time_Analysis {
+ my ($processed_time_data,$correct,$data_count,$student_count,
+ $ORdata,$Foils,$Concepts) = @_;
+ if ($data_count <= 0) {
+ return ('
'.&mt('There is no data to plot').'
','');
+ }
+ my $analysis_html;
+ my @plotdata;
+ my @labels;
+ foreach my $concept (@{$Concepts}) {
+ foreach my $foil (@{$concept->{'foils'}}) {
+ push(@labels,scalar(@labels)+1);
+ my $total = $processed_time_data->{$foil}->{'_total'};
+ if ($total == 0) {
+ push(@{$plotdata[0]},0);
} else {
- $TimeData{$foilid}->{'incorrect'}++;
+ push(@{$plotdata[0]},
+ 100 * $processed_time_data->{$foil}->{'_correct'} / $total);
+ }
+ my $total_incorrect = $total - $processed_time_data->{$foil}->{'_correct'};
+ for (my $i=0;$i{'_Options'}});$i++) {
+ my $option = $ORdata->{'_Options'}->[$i];
+ if ($total_incorrect == 0) {
+ push(@{$plotdata[$i+1]},0);
+ } else {
+ push(@{$plotdata[$i+1]},
+ 100 * $processed_time_data->{$foil}->{$option} / $total_incorrect);
+ }
}
}
+ # Put in a blank one
+ push(@labels,'');
+ push(@{$plotdata[0]},0);
+ for (my $i=0;$i{'_Options'}});$i++) {
+ push(@{$plotdata[$i+1]},0);
+ }
}
#
- # Compute the total and percent correct
- my @Plotdata1;
- my @Plotdata2;
- foreach my $concept (@Concepts) {
- my ($correct,$incorrect,$total);
- foreach my $foilid (@{$concept->{'foils'}}) {
- if (! exists($TimeData{$foilid}->{'correct'})) {
- $TimeData{$foilid}->{'correct'} = 0;
- }
- if (! exists($TimeData{$foilid}->{'incorrect'})) {
- $incorrect = 0;
- $TimeData{$foilid}->{'incorrect'} = 0;
- }
- $correct += $TimeData{$foilid}->{'correct'};
- $incorrect += $TimeData{$foilid}->{'incorrect'};
- $total += $TimeData{$foilid}->{'correct'}+
- $TimeData{$foilid}->{'incorrect'};
- $TimeData{$foilid}->{'total'} = $TimeData{$foilid}->{'correct'} +
- $TimeData{$foilid}->{'incorrect'};
- my $percent;
- if ($TimeData{$foilid}->{'total'} == 0) {
- $percent = 0;
- } else {
- $percent = $TimeData{$foilid}->{'correct'} /
- $TimeData{$foilid}->{'total'};
+ # Create the plot
+ my $correct_plot = &Apache::loncommon::DrawBarGraph('Correct Statements',
+ 'Statement Number',
+ 'Percent Correct',
+ 100,
+ $plotcolors,
+ undef,
+ $plotdata[0]);
+ for (my $j=0; $j< scalar(@{$plotdata[0]});$j++) {
+ $plotdata[0]->[$j]=0;
+ }
+ my $incorrect_plot =
+ &Apache::loncommon::DrawBarGraph('Incorrect Statements',
+ 'Statement Number',
+ 'Incorrect Option Choice',
+ 100,
+ $plotcolors,
+ undef,
+ @plotdata);
+ return ($correct_plot,$incorrect_plot);
+}
+
+sub OR_Concept_Time_Analysis {
+ my ($processed_time_data,$correct,$data_count,$student_count,
+ $ORdata,$Concepts) = @_;
+ return '' if ($data_count == 0);
+ #
+ # Put the data in plottable form
+ my @plotdata;
+ foreach my $concept (@$Concepts) {
+ my ($total,$correct);
+ foreach my $foil (@{$concept->{'foils'}}) {
+ $total += $processed_time_data->{$foil}->{'_total'};
+ $correct += $processed_time_data->{$foil}->{'_correct'};
+ }
+ if ($total == 0) {
+ push(@plotdata,0);
+ } else {
+ push(@plotdata,100 * $correct / $total);
+ }
+ }
+ #
+ # Create the plot
+ return &Apache::loncommon::DrawBarGraph('Correct Concepts',
+ 'Concept Number',
+ 'Percent Correct',
+ 100,
+ $plotcolors,
+ undef,
+ \@plotdata);
+}
+
+sub OR_time_process_data {
+ my ($performance_data,$begin_index,$end_index)=@_;
+ my %processed_time_data;
+ my %distinct_students;
+ my ($correct,$data_count);
+ if (($begin_index == $end_index) &&
+ ($end_index != scalar(@$performance_data)-1)) {
+ return undef;
+ }
+ # Be sure we include the last one if we are asked for it.
+ # That we have to correct here (and not when $end_index is
+ # given a value) should probably be considered a bug.
+ if ($end_index == scalar(@$performance_data)-1) {
+ $end_index++;
+ }
+ &Apache::lonnet::logthis(' '.$begin_index.':'.$end_index);
+ my $count;
+ for (my $i=$begin_index;$i<$end_index;$i++) {
+ my $attempt = $performance_data->[$i];
+ $count++;
+ next if (! defined($attempt));
+ my %attempt = &Process_OR_Row($attempt);
+ $data_count++;
+ $correct += $attempt{'_correct'};
+ $distinct_students{$attempt->[&Apache::loncoursedata::RD_student_id()]}++;
+ while (my ($foilid,$href) = each(%attempt)) {
+ if (! ref($href)) {
+ $processed_time_data{$foilid} += $href;
+ next;
}
- $TimeData{$foilid}->{'percent_corr'} = 100 * $percent;
- if ($ENV{'form.AnalyzeAs'} eq 'Foils') {
- push (@Plotdata1, $TimeData{$foilid}->{'percent_corr'});
- push (@Plotdata2,100-$TimeData{$foilid}->{'percent_corr'});
+ while (my ($option,$value) = each(%$href)) {
+ $processed_time_data{$foilid}->{$option}+=$value;
}
}
- if ($ENV{'form.AnalyzeAs'} ne 'Foils') {
- if ($total == 0) {
- push (@Plotdata1,0);
- push (@Plotdata2,100);
+ }
+ &Apache::lonnet::logthis('count = '.$count);
+ return (\%processed_time_data,$correct,$data_count,
+ scalar(keys(%distinct_students)));
+}
+
+#########################################################
+#########################################################
+##
+## Excel output
+##
+#########################################################
+#########################################################
+sub OR_excel_sheet {
+ my ($r,$resource,$performance_data,$ORdata) = @_;
+ my $response = '';
+ my (undef,$Foils,$Concepts) = &build_foil_index($ORdata);
+ #
+ # Create excel worksheet
+ my $filename = '/prtspool/'.
+ $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'.
+ time.'_'.rand(1000000000).'.xls';
+ my $workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
+ if (! defined($workbook)) {
+ $r->log_error("Error creating excel spreadsheet $filename: $!");
+ $r->print('
'.&mt("Unable to create new Excel file. ".
+ "This error has been logged. ".
+ "Please alert your LON-CAPA administrator").
+ '
');
+ return undef;
+ }
+ #
+ $workbook->set_tempdir('/home/httpd/perl/tmp');
+ my $format = &Apache::loncommon::define_excel_formats($workbook);
+ #
+ # Create and populate main worksheets
+ my $problem_data_sheet = $workbook->addworksheet('Problem Data');
+ my $student_data_sheet = &build_student_data_worksheet($workbook,$format);
+ my $response_data_sheet = $workbook->addworksheet('Response Data');
+ foreach my $sheet ($problem_data_sheet,$student_data_sheet,
+ $response_data_sheet) {
+ $sheet->write(0,0,$resource->{'title'},$format->{'h2'});
+ $sheet->write(1,0,$resource->{'src'},$format->{'h3'});
+ }
+ #
+ my $result;
+ $result = &OR_build_problem_data_worksheet($problem_data_sheet,$format,
+ $Concepts,$ORdata);
+ if ($result ne 'okay') {
+ # Do something useful
+ }
+ $result = &OR_build_response_data_worksheet($response_data_sheet,$format,
+ $performance_data,$Foils,
+ $ORdata);
+ if ($result ne 'okay') {
+ # Do something useful
+ }
+ $response_data_sheet->activate();
+ #
+ # Close the excel file
+ $workbook->close();
+ #
+ # Write a link to allow them to download it
+ $result .= '