--- loncom/interface/statistics/lonproblemstatistics.pm 2003/03/26 16:26:35 1.43
+++ loncom/interface/statistics/lonproblemstatistics.pm 2003/03/26 21:55:18 1.46
@@ -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.46 2003/03/26 21:55:18 matthew Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -35,6 +35,7 @@ use Apache::lonnet();
use Apache::lonhtmlcommon;
use Apache::loncoursedata;
use Apache::lonstatistics;
+use Spreadsheet::WriteExcel;
#######################################################
#######################################################
@@ -229,14 +230,29 @@ sub BuildProblemStatisticsPage {
} elsif ($show eq 'ungrouped') {
&output_html_ungrouped($r);
}
+ } elsif ($output_mode eq 'excel') {
+ $r->print("
Preparing Excel Spreadsheet
");
+ &output_excel($r);
+ } elsif ($output_mode eq 'plot') {
+ if ($show eq 'deg of diff') {
+ &plot_statistics($r,'DoDiff');
+ } elsif ($show eq 'per wrong') {
+ &plot_statistics($r,'%Wrng');
+ }
} else {
$r->print("Not implemented
");
}
return;
}
+###############################################
+###############################################
+
+###############################################
+###############################################
sub output_html_grouped_by_sequence {
my ($r) = @_;
+ my $problem_num = 0;
#$r->print(&ProblemStatisticsLegend());
my @Header = ("Title","Part","#Stdnts","Tries","Mod",
"Mean","#YES","#yes","%Wrng","DoDiff",
@@ -253,6 +269,7 @@ sub output_html_grouped_by_sequence {
foreach my $resource (@{$sequence->{'contents'}}) {
next if ($resource->{'type'} ne 'assessment');
foreach my $part (@{$resource->{'parts'}}) {
+ $problem_num++;
my ($num,$tries,$mod,$mean,$Solved,$solved,$DegOfDiff,$STD,
$SKEW) = &Apache::loncoursedata::get_problem_statistics
(undef,$resource->{'symb'},$part,
@@ -265,10 +282,12 @@ sub output_html_grouped_by_sequence {
if (defined($num) && $num > 0) {
$wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
}
+ my $option = '';
+ $option .= 'no part' if (! $show_part);
$r->print(''.&statistics_html_table_data
($resource,$part,$num,$tries,$mod,$mean,$Solved,
$solved,$wrongpercent,$DegOfDiff,$STD,$SKEW,
- $show_part).
+ $option).
"
\n");
}
}
@@ -280,15 +299,15 @@ sub output_html_grouped_by_sequence {
return;
}
-
###############################################
###############################################
###############################################
###############################################
sub output_html_ungrouped {
- my ($r) = @_;
+ my ($r,$option) = @_;
#
+ my $problem_num = 0;
my $show_container = 0;
my $show_part = 0;
#$r->print(&ProblemStatisticsLegend());
@@ -305,12 +324,18 @@ sub output_html_ungrouped {
if (! defined($sortby) || $sortby eq '') {
$sortby = 'Container';
}
- # #FFFFE6 #EEFFCC #DDFFFF FFDDDD #DDFFDD #FFDDFF
+ # If there is more than one sequence, list their titles
my @Sequences = &Apache::lonstatistics::Sequences_with_Assess();
if (@Sequences > 1) {
unshift(@Header,"Container");
$show_container = 1;
}
+ #
+ # If the option for showing the problem number is needed, push that
+ # on the list too
+ if (defined($option) && $option =~ /show probnum/) {
+ unshift(@Header,"P#");
+ }
#
$r->print(''."\n");
$r->rflush();
@@ -322,6 +347,7 @@ sub output_html_ungrouped {
foreach my $resource (@{$sequence->{'contents'}}) {
next if ($resource->{'type'} ne 'assessment');
foreach my $part (@{$resource->{'parts'}}) {
+ $problem_num++;
my ($num,$tries,$mod,$mean,$Solved,$solved,$DegOfDiff,$STD,
$SKEW) = &Apache::loncoursedata::get_problem_statistics
(undef,$resource->{'symb'},$part,
@@ -349,6 +375,7 @@ sub output_html_ungrouped {
'DoDiff' => $DegOfDiff,
'S.D.' => $STD,
'Skew' => $SKEW,
+ 'problem_num' => $problem_num,
});
}
}
@@ -360,7 +387,7 @@ sub output_html_ungrouped {
foreach (@Header) {
next if ($_ eq 'Part' && !$show_part);
# Do not allow sorting on some fields
- if ($_ eq $sortby || /^(Part)$/) {
+ if ($_ eq $sortby || /^(Part|P\#)$/) {
$Str .= ' | '.$_.' | ';
} else {
$Str .= ''.
@@ -409,13 +436,23 @@ sub output_html_ungrouped {
} @Statsarray;
}
}
+ $option .= ',no part' if (! $show_part);
foreach my $row (@OutputOrder) {
$r->print(' |
---|
');
+ if (defined($option) && $option =~ /show probnum/) {
+ $r->print(''.$row->{'problem_num'}.' | ');
+ }
if ($show_container) {
$r->print(''
.$row->{'sequence'}->{'title'}.' | ');
}
- $r->print(&stats_row_from_hash($row,$show_part));
+ $r->print(&statistics_html_table_data
+ ($row->{'resource'},$row->{'Part'},$row->{'#Stdnts'},
+ $row->{'Tries'},$row->{'Mod'},$row->{'Mean'},
+ $row->{'#YES'},$row->{'#yes'},$row->{"\%Wrng"},
+ $row->{'DoDiff'},$row->{'S.D.'},$row->{'Skew'},
+ $option));
+
$r->print("
\n");
}
$r->print("
\n");
@@ -425,15 +462,126 @@ sub output_html_ungrouped {
return;
}
-sub stats_row_from_hash {
- my ($data,$show_part) = @_;
- return &statistics_html_table_data($data->{'resource'},$data->{'Part'},
- $data->{'#Stdnts'}, $data->{'Tries'},
- $data->{'Mod'}, $data->{'Mean'},
- $data->{'#YES'}, $data->{'#yes'},
- $data->{"\%Wrng"}, $data->{'DoDiff'},
- $data->{'S.D.'}, $data->{'Skew'},
- $show_part);
+
+###############################################
+###############################################
+
+###############################################
+###############################################
+sub output_excel {
+ my ($r) = @_;
+ my $filename = '/prtspool/'.
+ $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'.
+ time.'_'.rand(1000000000).'.xls';
+ #
+ my $excel_workbook = undef;
+ my $excel_sheet = undef;
+ #
+ my $rows_output = 0;
+ my $cols_output = 0;
+ #
+ # Create sheet
+ $excel_workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
+ #
+ # Check for errors
+ if (! defined($excel_workbook)) {
+ $r->log_error("Error creating excel spreadsheet $filename: $!");
+ $r->print("Problems creating new Excel file. ".
+ "This error has been logged. ".
+ "Please alert your LON-CAPA administrator");
+ return ;
+ }
+ #
+ # The excel spreadsheet stores temporary data in files, then put them
+ # together. If needed we should be able to disable this (memory only).
+ # The temporary directory must be specified before calling 'addworksheet'.
+ # File::Temp is used to determine the temporary directory.
+ $excel_workbook->set_tempdir($Apache::lonnet::tmpdir);
+ #
+ # Add a worksheet
+ my $sheetname = $ENV{'course.'.$ENV{'request.course.id'}.'.description'};
+ if (length($sheetname) > 31) {
+ $sheetname = substr($sheetname,0,31);
+ }
+ $excel_sheet = $excel_workbook->addworksheet($sheetname);
+ #
+ # Put the course description in the header
+ $excel_sheet->write($rows_output,$cols_output++,
+ $ENV{'course.'.$ENV{'request.course.id'}.'.description'});
+ $cols_output += 3;
+ #
+ # Put a description of the sections listed
+ my $sectionstring = '';
+ my @Sections = @Apache::lonstatistics::SelectedSections;
+ if (scalar(@Sections) > 1) {
+ if (scalar(@Sections) > 2) {
+ my $last = pop(@Sections);
+ $sectionstring = "Sections ".join(', ',@Sections).', and '.$last;
+ } else {
+ $sectionstring = "Sections ".join(' and ',@Sections);
+ }
+ } else {
+ if ($Sections[0] eq 'all') {
+ $sectionstring = "All sections";
+ } else {
+ $sectionstring = "Section ".$Sections[0];
+ }
+ }
+ $excel_sheet->write($rows_output,$cols_output++,$sectionstring);
+ $cols_output += scalar(@Sections);
+ #
+ # Put the date in there too
+ $excel_sheet->write($rows_output,$cols_output++,
+ 'Compiled on '.localtime(time));
+ #
+ $rows_output++;
+ $cols_output=0;
+ #
+ # Add the headers
+ my @Header = ("Container","Title","Part","#Stdnts","Tries","Mod",
+ "Mean","#YES","#yes","%Wrng","DoDiff",
+ "S.D.","Skew.");#,"D.F.1st","D.F.2nd");
+ foreach (@Header) {
+ $excel_sheet->write($rows_output,$cols_output++,$_);
+ }
+ $rows_output++;
+ #
+ # Write the data
+ foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) {
+ next if ($sequence->{'num_assess'}<1);
+ foreach my $resource (@{$sequence->{'contents'}}) {
+ next if ($resource->{'type'} ne 'assessment');
+ foreach my $part (@{$resource->{'parts'}}) {
+ $cols_output=0;
+ my ($num,$tries,$mod,$mean,$Solved,$solved,$DegOfDiff,$STD,
+ $SKEW) = &Apache::loncoursedata::get_problem_statistics
+ (undef,$resource->{'symb'},$part,
+ $ENV{'request.course.id'});
+ #
+ if (!defined($part) || $part eq '') {
+ $part = ' ';
+ }
+ my $wrongpercent = 0;
+ if (defined($num) && $num > 0) {
+ $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
+ }
+ foreach ($sequence->{'title'},$resource->{'title'},$part,
+ $num,$tries,$mod,$mean,$Solved,$solved,$wrongpercent,
+ $DegOfDiff,$STD,$SKEW) {
+ $excel_sheet->write($rows_output,$cols_output++,$_);
+ }
+ $rows_output++;
+ }
+ }
+ }
+ #
+ # Write the excel file
+ $excel_workbook->close();
+ # Tell the user where to get their excel file
+ $r->print('
'.
+ 'Your Excel spreadsheet.'."\n");
+ $r->rflush();
+ return;
}
###############################################
@@ -443,13 +591,13 @@ sub stats_row_from_hash {
###############################################
sub statistics_html_table_data {
my ($resource,$part,$num,$tries,$mod,$mean,$Solved,$solved,$wrongpercent,
- $DegOfDiff,$STD,$SKEW,$show_part) = @_;
+ $DegOfDiff,$STD,$SKEW,$options) = @_;
my $row = '';
$row .= ''.
''.
$resource->{'title'}.''.
' | ';
- $row .= ''.$part.' | ' if ($show_part);
+ $row .= ''.$part.' | ' if ($options !~ /no part/);
foreach ($num,$tries) {
$row .= ''.$_.' | ';
}
@@ -471,71 +619,111 @@ sub statistics_html_table_data {
return $row;
}
-
###############################################
###############################################
-
-sub BuildGraphicChart {
- my ($graph,$cacheDB,$courseDescription,$students,$courseID,$r,$c)=@_;
- my %cache;
- my $max;
- my $title = '';
- if($graph eq 'DoDiffGraph') {
- $title = 'Degree-of-Difficulty';
- } else {
- $title = 'Wrong-Percentage';
- }
- my $currentSequence = -1;
- my $sortProblems = 'Sort Within Sequence';
- my ($result, $orderedProblems) =
- &InitializeProblemStatistics($cacheDB, $students, $courseID, $c, $r);
- if($result ne 'OK') {
- return;
+sub plot_statistics {
+ my ($r,$datafield) = @_;
+ my @Data;
+ #
+ my %Fields = ('#Stdnts'=> 0,
+ 'Tries' => 1,
+ 'Mod' => 2,
+ 'Mean' => 3,
+ '#YES' => 4,
+ '#yes' => 5,
+ '%Wrng' => 9,
+ 'DoDiff' => 6,
+ 'S.D.' => 7,
+ 'Skew' => 8,);
+ #
+ my $field = '%Wrng';
+ foreach (keys(%Fields)) {
+ $field = $_ if ($datafield eq $_);
}
- my @values = ();
- unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
- return 'Unable to tie database.7';
- }
- foreach(@$orderedProblems) {
- my ($sequence,$problem,$part)=split(':', $_);
- if($cache{'StatisticsMaps'} ne 'All Maps' &&
- $cache{'StatisticsMaps'} ne $cache{$sequence.':title'}) {
- next;
- }
- if( $currentSequence == -1 ||
- ($sortProblems eq 'Sort Within Sequence' &&
- $currentSequence != $sequence)) {
- if($currentSequence != -1) {
- &DrawGraph(\@values,$courseDescription,$title,$max,$r);
- }
- if($sortProblems eq 'Sort Within Sequence') {
- $r->print('
'.$cache{$sequence.':title'}.''."\n");
+ my $fieldindex = $Fields{$field};
+ #
+ my $Max = 0;
+ foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) {
+ next if ($sequence->{'num_assess'}<1);
+ foreach my $resource (@{$sequence->{'contents'}}) {
+ next if ($resource->{'type'} ne 'assessment');
+ foreach my $part (@{$resource->{'parts'}}) {
+ my @Results = &Apache::loncoursedata::get_problem_statistics
+ (undef,$resource->{'symb'},$part,
+ $ENV{'request.course.id'});
+ my ($num,$Solved,$solved) = @Results[0,4,5];
+ my $wrongpercent = 0;
+ if (defined($num) && $num > 0) {
+ $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
+ }
+ push (@Results,$wrongpercent);
+ my $data = $Results[$fieldindex];
+ $data = 0 if ($data eq 'nan');
+ $Max = $data if ($Max<$data);
+ push (@Data,$data);
}
- $currentSequence = $sequence;
- @values = ();
- $max=0;
- }
- my $data = 0;
- if($graph eq 'DoDiffGraph') {
- $data = sprintf("%.2f", $cache{$_.':degreeOfDifficulty'}),
- } else {
- $data = sprintf("%.1f", $cache{$_.':percentWrong'}),
}
- if($max < $data) {
- $max = $data;
+ }
+ #
+ # Print out plot request
+ my $title = 'Percent Wrong';
+ if ($field eq 'DoDiff') {
+ $title = 'Degree of Difficulty';
+ }
+ my $yaxis = 'Percent';
+ if ($field eq 'DoDiff') {
+ $yaxis = '';
+ } elsif ($field ne '%Wrng') {
+ $yaxis = '';
+ }
+ #
+ # Determine appropriate value for $Max
+ if ($field eq 'DoDiff') {
+ if ($Max > 0.5) {
+ $Max = 1;
+ } elsif ($Max > 0.2) {
+ $Max = 0.5;
+ } elsif ($Max > 0.1) {
+ $Max = 0.2;
+ }
+ } elsif ($field eq '%Wrng') {
+ if ($Max > 50) {
+ $Max = 100;
+ } elsif ($Max > 25) {
+ $Max = 50;
+ } elsif ($Max > 20) {
+ $Max = 25;
+ } elsif ($Max > 10) {
+ $Max = 20;
+ } elsif ($Max > 5) {
+ $Max = 10;
+ } else {
+ $Max = 5;
}
- push(@values, $data);
}
- untie(%cache);
- &DrawGraph(\@values,$courseDescription,$title,$max,$r);
+
+ $r->print("".&DrawGraph(\@Data,$title,'Problem Number',$yaxis,
+ $Max)."
\n");
+ #
+ # Print out the data
+ $ENV{'form.sortby'} = 'Contents';
+ &output_html_ungrouped($r,'show probnum');
return;
}
+###############################################
+###############################################
+
+###############################################
+###############################################
sub DrawGraph {
- my ($values,$courseDescription,$title,$Max,$r)=@_;
+ my ($values,$title,$xaxis,$yaxis,$Max)=@_;
+ $title = '' if (! defined($title));
+ $xaxis = '' if (! defined($xaxis));
+ $yaxis = '' if (! defined($yaxis));
+ #
my $sendValues = join(',', @$values);
my $sendCount = scalar(@$values);
- $r->print("
The Maximum Value is: $Max");
if ( $Max > 1 ) {
if ($Max % 10) {
if ( int($Max) < $Max ) {
@@ -543,196 +731,19 @@ sub DrawGraph {
$Max = int($Max);
}
}
- #(10 - $Max % 10);
- } else { $Max = 1; }
- my @GData = ('','Problem_number',$title,$Max,$sendCount,$sendValues);
-# $r->print(''."\n");
- $r->print('
'."\n");
- $r->print('');
-# $r->print('