version 1.46, 2003/03/26 21:55:18
|
version 1.55.2.1, 2003/10/28 20:08:23
|
Line 26
|
Line 26
|
# |
# |
# (Navigate problems for statistical reports |
# (Navigate problems for statistical reports |
# |
# |
### |
############################################### |
|
############################################### |
|
|
|
=pod |
|
|
|
=head1 NAME |
|
|
|
lonproblemstatistics |
|
|
|
=head1 SYNOPSIS |
|
|
|
Routines to present problem statistics to instructors via tables, |
|
Excel files, and plots. |
|
|
|
=over 4 |
|
|
|
=cut |
|
|
|
############################################### |
|
############################################### |
|
|
package Apache::lonproblemstatistics; |
package Apache::lonproblemstatistics; |
|
|
Line 37 use Apache::loncoursedata;
|
Line 56 use Apache::loncoursedata;
|
use Apache::lonstatistics; |
use Apache::lonstatistics; |
use Spreadsheet::WriteExcel; |
use Spreadsheet::WriteExcel; |
|
|
####################################################### |
my @Fields = ( |
####################################################### |
{ name => 'problem_num', |
|
title => 'P#', |
|
align => 'right', |
|
color => '#FFFFE6' }, |
|
{ name => 'container', |
|
title => 'Sequence or Folder', |
|
align => 'left', |
|
color => '#FFFFE6', |
|
sortable => 'yes' }, |
|
{ name => 'title', |
|
title => 'Title', |
|
align => 'left', |
|
color => '#FFFFE6', |
|
special => 'link', |
|
sortable => 'yes', }, |
|
{ name => 'part', |
|
title => 'Part', |
|
align => 'left', |
|
color => '#FFFFE6', |
|
}, |
|
{ name => 'num_students', |
|
title => '#Stdnts', |
|
align => 'right', |
|
color => '#EEFFCC', |
|
format => '%d', |
|
sortable => 'yes', |
|
graphable => 'yes', |
|
long_title => 'Number of Students Attempting Problem' }, |
|
{ name => 'tries', |
|
title => 'Tries', |
|
align => 'right', |
|
color => '#EEFFCC', |
|
format => '%d', |
|
sortable => 'yes', |
|
graphable => 'yes', |
|
long_title => 'Total Number of Tries' }, |
|
{ name => 'max_tries', |
|
title => 'Max Tries', |
|
align => 'right', |
|
color => '#DDFFFF', |
|
format => '%d', |
|
sortable => 'yes', |
|
graphable => 'yes', |
|
long_title => 'Maximum Number of Tries' }, |
|
{ name => 'mean_tries', |
|
title => 'Mean Tries', |
|
align => 'right', |
|
color => '#DDFFFF', |
|
format => '%5.2f', |
|
sortable => 'yes', |
|
graphable => 'yes', |
|
long_title => 'Average Number of Tries' }, |
|
{ name => 'std_tries', |
|
title => 'S.D. tries', |
|
align => 'right', |
|
color => '#DDFFFF', |
|
format => '%5.2f', |
|
sortable => 'yes', |
|
graphable => 'yes', |
|
long_title => 'Standard Deviation of Number of Tries' }, |
|
{ name => 'skew_tries', |
|
title => 'Skew Tries', |
|
align => 'right', |
|
color => '#DDFFFF', |
|
format => '%5.2f', |
|
sortable => 'yes', |
|
graphable => 'yes', |
|
long_title => 'Skew of Number of Tries' }, |
|
{ name => 'deg_of_diff', |
|
title => 'DoDiff', |
|
align => 'right', |
|
color => '#DDFFFF', |
|
format => '%5.2f', |
|
sortable => 'yes', |
|
graphable => 'yes', |
|
long_title => 'Degree of Difficulty'. |
|
'[ 1 - ((#YES+#yes) / Tries) ]'}, |
|
{ name => 'num_solved', |
|
title => '#YES', |
|
align => 'right', |
|
color => '#FFDDDD', |
|
format => '%d', |
|
sortable => 'yes', |
|
graphable => 'yes', |
|
long_title => 'Number of Students able to Solve' }, |
|
{ name => 'num_override', |
|
title => '#yes', |
|
align => 'right', |
|
color => '#FFDDDD', |
|
format => '%d', |
|
sortable => 'yes', |
|
graphable => 'yes', |
|
long_title => 'Number of Students given Override' }, |
|
{ name => 'per_wrong', |
|
title => '%Wrng', |
|
align => 'right', |
|
color => '#FFFFE6', |
|
format => '%4.1f', |
|
sortable => 'yes', |
|
graphable => 'yes', |
|
long_title => 'Percent of students whose final answer is wrong' }, |
|
); |
|
|
|
############################################### |
|
############################################### |
|
|
|
=pod |
|
|
|
=item &CreateInterface() |
|
|
|
Create the main intereface for the statistics page. Allows the user to |
|
select sections, maps, and output. |
|
|
|
=cut |
|
|
|
############################################### |
|
############################################### |
sub CreateInterface { |
sub CreateInterface { |
my $Str = ''; |
my $Str = ''; |
$Str .= '<table cellspacing="5">'."\n"; |
$Str .= '<table cellspacing="5">'."\n"; |
$Str .= '<tr>'; |
$Str .= '<tr>'; |
$Str .= '<td align="center"><b>Sections</b></td>'; |
$Str .= '<td align="center"><b>Sections</b></td>'; |
|
$Str .= '<td align="center"><b>Enrollment Status</b></td>'; |
$Str .= '<td align="center"><b>Sequences and Folders</b></td>'; |
$Str .= '<td align="center"><b>Sequences and Folders</b></td>'; |
$Str .= '<td align="center"><b>Output</b></td>'; |
$Str .= '<td align="center"><b>Output</b></td>'; |
$Str .= '</tr>'."\n"; |
$Str .= '</tr>'."\n"; |
Line 52 sub CreateInterface {
|
Line 187 sub CreateInterface {
|
$Str .= '<tr><td align="center">'."\n"; |
$Str .= '<tr><td align="center">'."\n"; |
$Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5); |
$Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5); |
$Str .= '</td><td align="center">'; |
$Str .= '</td><td align="center">'; |
|
$Str .= &Apache::lonhtmlcommon::StatusOptions(undef,undef,5); |
|
$Str .= '</td><td align="center">'; |
# |
# |
my $only_seq_with_assessments = sub { |
my $only_seq_with_assessments = sub { |
my $s=shift; |
my $s=shift; |
Line 67 sub CreateInterface {
|
Line 204 sub CreateInterface {
|
$Str .= &CreateAndParseOutputSelector(); |
$Str .= &CreateAndParseOutputSelector(); |
$Str .= '</td></tr>'."\n"; |
$Str .= '</td></tr>'."\n"; |
$Str .= '</table>'."\n"; |
$Str .= '</table>'."\n"; |
|
$Str .= '<input type="submit" value="Generate Statistics" />'; |
|
$Str .= ' 'x5; |
|
$Str .= '<input type="submit" name="ClearCache" value="Clear Caches" />'; |
|
$Str .= ' 'x5; |
return $Str; |
return $Str; |
} |
} |
|
|
Line 77 sub CreateInterface {
|
Line 218 sub CreateInterface {
|
|
|
=item &CreateAndParseOutputSelector() |
=item &CreateAndParseOutputSelector() |
|
|
|
Construct a selection list of options for output and parse output selections. |
|
The current output selected is indicated by the values of the two package |
|
variables $output_mode and $show. @OutputOptions holds the descriptions of |
|
the output options and the values for $output_mode and $show. |
|
|
|
Based on code from lonstudentassessment.pm. |
|
|
=cut |
=cut |
|
|
####################################################### |
####################################################### |
Line 105 my @OutputOptions =
|
Line 253 my @OutputOptions =
|
mode => 'excel', |
mode => 'excel', |
show => 'all', |
show => 'all', |
}, |
}, |
{ name => 'Degree of Difficulty Plot', |
|
value => 'plot deg diff', |
|
description => 'Generate a plot of the degree of difficulty of each '. |
|
'problem part.', |
|
mode => 'plot', |
|
show => 'deg of diff', |
|
}, |
|
{ name => 'Percent Wrong Plot', |
|
value => 'plot per wrong', |
|
description => 'Generate a plot showing the percent of students who '. |
|
'were unable to complete each problem part', |
|
mode => 'plot', |
|
show => 'per wrong', |
|
}, |
|
); |
); |
|
|
sub OutputDescriptions { |
sub OutputDescriptions { |
Line 135 sub OutputDescriptions {
|
Line 269 sub OutputDescriptions {
|
|
|
sub CreateAndParseOutputSelector { |
sub CreateAndParseOutputSelector { |
my $Str = ''; |
my $Str = ''; |
my $elementname = 'outputmode'; |
my $elementname = 'statsoutputmode'; |
# |
# |
# Format for output options is 'mode, restrictions'; |
# Format for output options is 'mode, restrictions'; |
my $selected = 'html, with links'; |
my $selected = 'HTML problem statistics grouped'; |
if (exists($ENV{'form.'.$elementname})) { |
if (exists($ENV{'form.'.$elementname})) { |
if (ref($ENV{'form.'.$elementname} eq 'ARRAY')) { |
if (ref($ENV{'form.'.$elementname} eq 'ARRAY')) { |
$selected = $ENV{'form.'.$elementname}->[0]; |
$selected = $ENV{'form.'.$elementname}->[0]; |
Line 159 sub CreateAndParseOutputSelector {
|
Line 293 sub CreateAndParseOutputSelector {
|
# Build the form element |
# Build the form element |
$Str = qq/<select size="5" name="$elementname">/; |
$Str = qq/<select size="5" name="$elementname">/; |
foreach my $option (@OutputOptions) { |
foreach my $option (@OutputOptions) { |
|
if (exists($option->{'special'}) && |
|
$option->{'special'} =~ /do not show/) { |
|
next; |
|
} |
$Str .= "\n".' <option value="'.$option->{'value'}.'"'; |
$Str .= "\n".' <option value="'.$option->{'value'}.'"'; |
$Str .= " selected " if ($option->{'value'} eq $selected); |
$Str .= " selected " if ($option->{'value'} eq $selected); |
$Str .= ">".$option->{'name'}."<\/option>"; |
$Str .= ">".$option->{'name'}."<\/option>"; |
Line 170 sub CreateAndParseOutputSelector {
|
Line 308 sub CreateAndParseOutputSelector {
|
############################################### |
############################################### |
############################################### |
############################################### |
|
|
|
=pod |
|
|
|
=item &Gather_Student_Data() |
|
|
|
Ensures all student data is up to date. |
|
|
|
=cut |
|
|
############################################### |
############################################### |
############################################### |
############################################### |
sub Gather_Student_Data { |
sub Gather_Student_Data { |
Line 200 sub Gather_Student_Data {
|
Line 346 sub Gather_Student_Data {
|
############################################### |
############################################### |
############################################### |
############################################### |
|
|
|
=pod |
|
|
|
=item &BuildProblemStatisticsPage() |
|
|
|
Main interface to problem statistics. |
|
|
|
=cut |
|
|
############################################### |
############################################### |
############################################### |
############################################### |
sub BuildProblemStatisticsPage { |
sub BuildProblemStatisticsPage { |
Line 212 sub BuildProblemStatisticsPage {
|
Line 366 sub BuildProblemStatisticsPage {
|
$r->print('<input type="hidden" name="statsfirstcall" value="no" />'); |
$r->print('<input type="hidden" name="statsfirstcall" value="no" />'); |
$r->print('<input type="hidden" name="sortby" value="'.$ENV{'form.sortby'}. |
$r->print('<input type="hidden" name="sortby" value="'.$ENV{'form.sortby'}. |
'" />'); |
'" />'); |
|
$r->print('<input type="hidden" name="plot" value="" />'); |
if (! exists($ENV{'form.statsfirstcall'})) { |
if (! exists($ENV{'form.statsfirstcall'})) { |
return; |
return; |
} |
} |
Line 233 sub BuildProblemStatisticsPage {
|
Line 388 sub BuildProblemStatisticsPage {
|
} elsif ($output_mode eq 'excel') { |
} elsif ($output_mode eq 'excel') { |
$r->print("<h2>Preparing Excel Spreadsheet</h2>"); |
$r->print("<h2>Preparing Excel Spreadsheet</h2>"); |
&output_excel($r); |
&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 { |
} else { |
$r->print("<h1>Not implemented</h1>"); |
$r->print("<h1>Not implemented</h1>"); |
} |
} |
Line 248 sub BuildProblemStatisticsPage {
|
Line 397 sub BuildProblemStatisticsPage {
|
############################################### |
############################################### |
############################################### |
############################################### |
|
|
|
=pod |
|
|
|
=item &output_html_grouped_by_sequence() |
|
|
|
Presents the statistics data as an html table organized by the order |
|
the assessments appear in the course. |
|
|
|
=cut |
|
|
############################################### |
############################################### |
############################################### |
############################################### |
sub output_html_grouped_by_sequence { |
sub output_html_grouped_by_sequence { |
my ($r) = @_; |
my ($r) = @_; |
my $problem_num = 0; |
my $problem_num = 0; |
#$r->print(&ProblemStatisticsLegend()); |
#$r->print(&ProblemStatisticsLegend()); |
my @Header = ("Title","Part","#Stdnts","Tries","Mod", |
|
"Mean","#YES","#yes","%Wrng","DoDiff", |
|
"S.D.","Skew.");#,"D.F.1st","D.F.2nd"); |
|
# #FFFFE6 #EEFFCC #DDFFFF FFDDDD #DDFFDD #FFDDFF |
|
foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) { |
foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) { |
my $show_part = 0; |
|
next if ($sequence->{'num_assess'}<1); |
next if ($sequence->{'num_assess'}<1); |
$r->print("<h3>".$sequence->{'title'}."</h3>"); |
$r->print("<h3>".$sequence->{'title'}."</h3>"); |
$r->print('<table border="0"><tr><td bgcolor="#777777">'."\n"); |
$r->print('<table border="0"><tr><td bgcolor="#777777">'."\n"); |
$r->print('<table border="0" cellpadding="3">'."\n"); |
$r->print('<table border="0" cellpadding="3">'."\n"); |
$r->print('<tr bgcolor="#FFFFE6"><th>'. |
$r->print('<tr bgcolor="#FFFFE6">'); |
join("</th><th>",@Header)."</th></tr>\n"); |
my $Str = &statistics_table_header('no container no plots'); |
|
$r->print('<tr bgcolor="#FFFFE6">'.$Str."</tr>\n"); |
foreach my $resource (@{$sequence->{'contents'}}) { |
foreach my $resource (@{$sequence->{'contents'}}) { |
next if ($resource->{'type'} ne 'assessment'); |
next if ($resource->{'type'} ne 'assessment'); |
foreach my $part (@{$resource->{'parts'}}) { |
foreach my $part (@{$resource->{'parts'}}) { |
$problem_num++; |
$problem_num++; |
my ($num,$tries,$mod,$mean,$Solved,$solved,$DegOfDiff,$STD, |
my $data = &get_statistics($sequence,$resource,$part, |
$SKEW) = &Apache::loncoursedata::get_problem_statistics |
$problem_num); |
(undef,$resource->{'symb'},$part, |
|
$ENV{'request.course.id'}); |
|
# |
|
$show_part = 1 if ($part ne '0'); |
|
$part = ' ' if ($part == 0); |
|
# |
|
my $wrongpercent = 0; |
|
if (defined($num) && $num > 0) { |
|
$wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10; |
|
} |
|
my $option = ''; |
my $option = ''; |
$option .= 'no part' if (! $show_part); |
$r->print('<tr>'.&statistics_html_table_data($data, |
$r->print('<tr>'.&statistics_html_table_data |
'no container'). |
($resource,$part,$num,$tries,$mod,$mean,$Solved, |
|
$solved,$wrongpercent,$DegOfDiff,$STD,$SKEW, |
|
$option). |
|
"</tr>\n"); |
"</tr>\n"); |
} |
} |
} |
} |
Line 302 sub output_html_grouped_by_sequence {
|
Line 443 sub output_html_grouped_by_sequence {
|
############################################### |
############################################### |
############################################### |
############################################### |
|
|
|
=pod |
|
|
|
=item &output_html_ungrouped() |
|
|
|
Presents the statistics data in a single html table which can be sorted by |
|
different columns. |
|
|
|
=cut |
|
|
############################################### |
############################################### |
############################################### |
############################################### |
sub output_html_ungrouped { |
sub output_html_ungrouped { |
my ($r,$option) = @_; |
my ($r,$option) = @_; |
# |
# |
|
if (exists($ENV{'form.plot'}) && $ENV{'form.plot'} ne '') { |
|
&plot_statistics($r,$ENV{'form.plot'}); |
|
} |
|
# |
my $problem_num = 0; |
my $problem_num = 0; |
my $show_container = 0; |
my $show_container = 0; |
my $show_part = 0; |
my $show_part = 0; |
#$r->print(&ProblemStatisticsLegend()); |
#$r->print(&ProblemStatisticsLegend()); |
my @Header = ("Title","Part","#Stdnts","Tries","Mod", |
|
"Mean","#YES","#yes","%Wrng","DoDiff", |
|
"S.D.","Skew");#,"D.F.1st","D.F.2nd"); |
|
# |
|
my $sortby = undef; |
my $sortby = undef; |
foreach (@Header) { |
foreach my $field (@Fields) { |
if ($ENV{'form.sortby'} eq $_) { |
if ($ENV{'form.sortby'} eq $field->{'name'}) { |
$sortby = $_; |
$sortby = $field->{'name'}; |
} |
} |
} |
} |
if (! defined($sortby) || $sortby eq '') { |
if (! defined($sortby) || $sortby eq '' || $sortby eq 'problem_num') { |
$sortby = 'Container'; |
$sortby = 'container'; |
} |
} |
# If there is more than one sequence, list their titles |
# If there is more than one sequence, list their titles |
my @Sequences = &Apache::lonstatistics::Sequences_with_Assess(); |
my @Sequences = &Apache::lonstatistics::Sequences_with_Assess(); |
if (@Sequences > 1) { |
if (@Sequences < 1) { |
unshift(@Header,"Container"); |
$option .= ' no 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('<table border="0"><tr><td bgcolor="#777777">'."\n"); |
|
$r->rflush(); |
|
# |
|
# Compile the data |
# Compile the data |
my @Statsarray; |
my @Statsarray; |
foreach my $sequence (@Sequences) { |
foreach my $sequence (@Sequences) { |
Line 348 sub output_html_ungrouped {
|
Line 488 sub output_html_ungrouped {
|
next if ($resource->{'type'} ne 'assessment'); |
next if ($resource->{'type'} ne 'assessment'); |
foreach my $part (@{$resource->{'parts'}}) { |
foreach my $part (@{$resource->{'parts'}}) { |
$problem_num++; |
$problem_num++; |
my ($num,$tries,$mod,$mean,$Solved,$solved,$DegOfDiff,$STD, |
my $data = &get_statistics($sequence,$resource,$part, |
$SKEW) = &Apache::loncoursedata::get_problem_statistics |
$problem_num); |
(undef,$resource->{'symb'},$part, |
|
$ENV{'request.course.id'}); |
|
# |
|
$show_part = 1 if ($part ne '0'); |
$show_part = 1 if ($part ne '0'); |
$part = ' ' if ($part == 0); |
|
# |
# |
my $wrongpercent = 0; |
push (@Statsarray,$data); |
if (defined($num) && $num > 0) { |
|
$wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10; |
|
} |
|
push (@Statsarray, |
|
{ 'sequence' => $sequence, |
|
'resource' => $resource, |
|
'Title' => $resource->{'title'}, |
|
'Part' => $part, |
|
'#Stdnts' => $num, |
|
'Tries' => $tries, |
|
'Mod' => $mod, |
|
'Mean' => $mean, |
|
'#YES' => $Solved, |
|
'#yes' => $solved, |
|
'%Wrng' => $wrongpercent, |
|
'DoDiff' => $DegOfDiff, |
|
'S.D.' => $STD, |
|
'Skew' => $SKEW, |
|
'problem_num' => $problem_num, |
|
}); |
|
} |
} |
} |
} |
} |
} |
# |
# |
# Table Headers |
|
$r->print('<table border="0" cellpadding="3">'."\n"); |
|
my $Str = ''; |
|
foreach (@Header) { |
|
next if ($_ eq 'Part' && !$show_part); |
|
# Do not allow sorting on some fields |
|
if ($_ eq $sortby || /^(Part|P\#)$/) { |
|
$Str .= '<th>'.$_.'</th>'; |
|
} else { |
|
$Str .= '<th>'. |
|
'<a href="javascript:document.Statistics.sortby.value='."'$_'". |
|
';document.Statistics.submit();">'. |
|
$_.'</a></th>'; |
|
} |
|
} |
|
$r->print('<tr bgcolor="#FFFFE6">'.$Str."</tr>\n"); |
|
# |
|
# Sort the data |
# Sort the data |
my @OutputOrder; |
my @OutputOrder; |
if ($sortby eq 'Container') { |
if ($sortby eq 'container') { |
@OutputOrder = @Statsarray; |
@OutputOrder = @Statsarray; |
} else { |
} else { |
# $sortby is already defined, so we can charge ahead |
# $sortby is already defined, so we can charge ahead |
Line 408 sub output_html_ungrouped {
|
Line 507 sub output_html_ungrouped {
|
# Alpha comparison |
# Alpha comparison |
@OutputOrder = sort { |
@OutputOrder = sort { |
lc($a->{$sortby}) cmp lc($b->{$sortby}) || |
lc($a->{$sortby}) cmp lc($b->{$sortby}) || |
lc($a->{'Title'}) cmp lc($b->{'Title'}) || |
lc($a->{'title'}) cmp lc($b->{'title'}) || |
lc($a->{'Part'}) cmp lc($b->{'Part'}); |
lc($a->{'part'}) cmp lc($b->{'part'}); |
} @Statsarray; |
} @Statsarray; |
} else { |
} else { |
# Numerical comparison |
# Numerical comparison |
Line 429 sub output_html_ungrouped {
|
Line 528 sub output_html_ungrouped {
|
} |
} |
if ($retvalue eq '0') { |
if ($retvalue eq '0') { |
$retvalue = $b->{$sortby} <=> $a->{$sortby} || |
$retvalue = $b->{$sortby} <=> $a->{$sortby} || |
lc($a->{'Title'}) <=> lc($b->{'Title'}) || |
lc($a->{'title'}) <=> lc($b->{'title'}) || |
lc($a->{'Part'}) <=> lc($b->{'Part'}); |
lc($a->{'part'}) <=> lc($b->{'part'}); |
} |
} |
$retvalue; |
$retvalue; |
} @Statsarray; |
} @Statsarray; |
} |
} |
} |
} |
$option .= ',no part' if (! $show_part); |
$option .= 'no part' if (! $show_part); |
foreach my $row (@OutputOrder) { |
my $num_output = 0; |
$r->print('<tr>'); |
# |
if (defined($option) && $option =~ /show probnum/) { |
# output the headers |
$r->print('<td bgcolor="#FFFFE6">'.$row->{'problem_num'}.'</td>'); |
$r->print('<table border="0"><tr><td bgcolor="#777777">'."\n"); |
} |
$r->print('<table border="0" cellpadding="3">'."\n"); |
if ($show_container) { |
my $Str = &statistics_table_header($option.' sortable'); |
$r->print('<td bgcolor="#FFFFE6">' |
$r->print('<tr bgcolor="#FFFFE6">'.$Str."</tr>\n"); |
.$row->{'sequence'}->{'title'}.'</td>'); |
# |
} |
foreach my $rowdata (@OutputOrder) { |
$r->print(&statistics_html_table_data |
$num_output++; |
($row->{'resource'},$row->{'Part'},$row->{'#Stdnts'}, |
if ($num_output % 25 == 0) { |
$row->{'Tries'},$row->{'Mod'},$row->{'Mean'}, |
$r->print("</table>\n</td></tr></table>\n"); |
$row->{'#YES'},$row->{'#yes'},$row->{"\%Wrng"}, |
# |
$row->{'DoDiff'},$row->{'S.D.'},$row->{'Skew'}, |
$r->print('<table border="0"><tr><td bgcolor="#777777">'."\n"); |
$option)); |
$r->print('<table border="0" cellpadding="3">'."\n"); |
|
my $Str = &statistics_table_header($option.' sortable'); |
$r->print("</tr>\n"); |
$r->print('<tr bgcolor="#FFFFE6">'.$Str."</tr>\n"); |
|
$r->rflush(); |
|
} |
|
$r->print('<tr>'.&statistics_html_table_data($rowdata,$option). |
|
"</tr>\n"); |
} |
} |
$r->print("</table>\n"); |
$r->print("</table>\n"); |
$r->print("</td></tr></table>\n"); |
$r->print("</td></tr></table>\n"); |
Line 462 sub output_html_ungrouped {
|
Line 565 sub output_html_ungrouped {
|
return; |
return; |
} |
} |
|
|
|
|
############################################### |
############################################### |
############################################### |
############################################### |
|
|
|
=pod |
|
|
|
=item &output_excel() |
|
|
|
Presents the statistical data in an Excel 95 compatable spreadsheet file. |
|
|
|
=cut |
|
|
############################################### |
############################################### |
############################################### |
############################################### |
sub output_excel { |
sub output_excel { |
Line 537 sub output_excel {
|
Line 647 sub output_excel {
|
$rows_output++; |
$rows_output++; |
$cols_output=0; |
$cols_output=0; |
# |
# |
# Add the headers |
# Long Headersheaders |
my @Header = ("Container","Title","Part","#Stdnts","Tries","Mod", |
foreach my $field (@Fields) { |
"Mean","#YES","#yes","%Wrng","DoDiff", |
next if ($field->{'name'} eq 'problem_num'); |
"S.D.","Skew.");#,"D.F.1st","D.F.2nd"); |
if (exists($field->{'long_title'})) { |
foreach (@Header) { |
$excel_sheet->write($rows_output,$cols_output++, |
$excel_sheet->write($rows_output,$cols_output++,$_); |
$field->{'long_title'}); |
|
} else { |
|
$excel_sheet->write($rows_output,$cols_output++,''); |
|
} |
|
} |
|
$rows_output++; |
|
$cols_output=0; |
|
# Brief headers |
|
foreach my $field (@Fields) { |
|
next if ($field->{'name'} eq 'problem_num'); |
|
$excel_sheet->write($rows_output,$cols_output++,$field->{'title'}); |
} |
} |
$rows_output++; |
$rows_output++; |
# |
# |
# Write the data |
# Write the data |
|
my $problem_num=0; |
foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) { |
foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) { |
next if ($sequence->{'num_assess'}<1); |
next if ($sequence->{'num_assess'}<1); |
foreach my $resource (@{$sequence->{'contents'}}) { |
foreach my $resource (@{$sequence->{'contents'}}) { |
next if ($resource->{'type'} ne 'assessment'); |
next if ($resource->{'type'} ne 'assessment'); |
foreach my $part (@{$resource->{'parts'}}) { |
foreach my $part (@{$resource->{'parts'}}) { |
$cols_output=0; |
$cols_output=0; |
my ($num,$tries,$mod,$mean,$Solved,$solved,$DegOfDiff,$STD, |
$problem_num++; |
$SKEW) = &Apache::loncoursedata::get_problem_statistics |
my $data = &get_statistics($sequence,$resource,$part, |
(undef,$resource->{'symb'},$part, |
$problem_num); |
$ENV{'request.course.id'}); |
|
# |
# |
if (!defined($part) || $part eq '') { |
if (!defined($part) || $part eq '') { |
$part = ' '; |
$part = ' '; |
} |
} |
my $wrongpercent = 0; |
foreach my $field (@Fields) { |
if (defined($num) && $num > 0) { |
next if ($field->{'name'} eq 'problem_num'); |
$wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10; |
$excel_sheet->write($rows_output,$cols_output++, |
} |
$data->{$field->{'name'}}); |
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++; |
$rows_output++; |
} |
} |
Line 587 sub output_excel {
|
Line 703 sub output_excel {
|
############################################### |
############################################### |
############################################### |
############################################### |
|
|
|
=pod |
|
|
|
=item &statistics_html_table_data() |
|
|
|
Help function used to format the rows for HTML table output. |
|
|
|
=cut |
|
|
############################################### |
############################################### |
############################################### |
############################################### |
sub statistics_html_table_data { |
sub statistics_html_table_data { |
my ($resource,$part,$num,$tries,$mod,$mean,$Solved,$solved,$wrongpercent, |
my ($data,$options) = @_; |
$DegOfDiff,$STD,$SKEW,$options) = @_; |
|
my $row = ''; |
my $row = ''; |
$row .= '<td bgcolor="#FFFFE6">'. |
foreach my $field (@Fields) { |
'<a href="'.$resource->{'src'}.'" target="_blank" >'. |
next if ($options =~ /no $field->{'name'}/); |
$resource->{'title'}.'</a>'. |
$row .= '<td bgcolor="'.$field->{'color'}.'"'; |
'</td>'; |
if (exists($field->{'align'})) { |
$row .= '<td bgcolor="#FFFFE6">'.$part.'</td>' if ($options !~ /no part/); |
$row .= ' align="'.$field->{'align'}.'"'; |
foreach ($num,$tries) { |
} |
$row .= '<td bgcolor="#EEFFCC" align="right">'.$_.'</td>'; |
$row .= '>'; |
} |
if (exists($field->{'special'}) && $field->{'special'} eq 'link') { |
foreach ($mod,$mean) { |
$row .= '<a href="'.$data->{$field->{'name'}.'.link'}.'">'; |
$row .= '<td bgcolor="#DDFFFF" align="right">'. |
} |
sprintf("%5.2f",$_).'</td>'; |
if (exists($field->{'format'})) { |
} |
$row .= sprintf($field->{'format'},$data->{$field->{'name'}}); |
foreach ($Solved,$solved) { |
} else { |
$row .= '<td bgcolor="#DDFFFF" align="right">'.$_.'</td>'; |
$row .= $data->{$field->{'name'}}; |
} |
} |
foreach ($wrongpercent) { |
if (exists($field->{'special'}) && $field->{'special'} eq 'link') { |
$row .= '<td bgcolor="#DDFFFF" align="right">'. |
$row.= '</a>'; |
sprintf("%5.1f",$_).'</td>'; |
} |
} |
$row .= '</td>'; |
foreach ($DegOfDiff,$STD,$SKEW) { |
|
$row .= '<td bgcolor="#FFDDDD" align="right">'. |
|
sprintf("%5.2f",$_).'</td>'; |
|
} |
} |
return $row; |
return $row; |
} |
} |
|
|
|
sub statistics_table_header { |
|
my ($options) = @_; |
|
my $header_row; |
|
foreach my $field (@Fields) { |
|
next if ($options =~ /no $field->{'name'}/); |
|
$header_row .= '<th>'; |
|
if ($options =~ /sortable/ && |
|
exists($field->{'sortable'}) && $field->{'sortable'} eq 'yes') { |
|
$header_row .= '<a href="javascript:'. |
|
'document.Statistics.sortby.value='."'".$field->{'name'}."'". |
|
';document.Statistics.submit();">'; |
|
} |
|
$header_row .= $field->{'title'}; |
|
if ($options =~ /sortable/) { |
|
$header_row.= '</a>'; |
|
} |
|
if ($options !~ /no plots/ && |
|
exists($field->{'graphable'}) && |
|
$field->{'graphable'} eq 'yes') { |
|
$header_row.=' ('; |
|
$header_row .= '<a href="javascript:'. |
|
"document.Statistics.plot.value='$field->{'name'}'". |
|
';document.Statistics.submit();">'; |
|
$header_row .= 'plot</a>)'; |
|
} |
|
$header_row .= '</th>'; |
|
} |
|
return $header_row; |
|
} |
|
|
|
############################################### |
|
############################################### |
|
|
|
=pod |
|
|
|
=item &plot_statistics() |
|
|
|
=cut |
|
|
############################################### |
############################################### |
############################################### |
############################################### |
sub plot_statistics { |
sub plot_statistics { |
my ($r,$datafield) = @_; |
my ($r,$datafield) = @_; |
my @Data; |
my @Data; |
# |
# |
my %Fields = ('#Stdnts'=> 0, |
# |
'Tries' => 1, |
my $sortfield = undef; |
'Mod' => 2, |
my $title = undef; |
'Mean' => 3, |
foreach my $field (@Fields) { |
'#YES' => 4, |
if ($datafield eq $field->{'name'} && |
'#yes' => 5, |
exists($field->{'graphable'}) && $field->{'graphable'} eq 'yes') { |
'%Wrng' => 9, |
$sortfield = $field->{'name'}; |
'DoDiff' => 6, |
$title = $field->{'long_title'}; |
'S.D.' => 7, |
} |
'Skew' => 8,); |
|
# |
|
my $field = '%Wrng'; |
|
foreach (keys(%Fields)) { |
|
$field = $_ if ($datafield eq $_); |
|
} |
} |
my $fieldindex = $Fields{$field}; |
return if (! defined($sortfield) || $sortfield eq ''); |
# |
# |
my $Max = 0; |
my $Max = 0; |
|
my $problem_num = 0; |
foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) { |
foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) { |
next if ($sequence->{'num_assess'}<1); |
next if ($sequence->{'num_assess'}<1); |
foreach my $resource (@{$sequence->{'contents'}}) { |
foreach my $resource (@{$sequence->{'contents'}}) { |
next if ($resource->{'type'} ne 'assessment'); |
next if ($resource->{'type'} ne 'assessment'); |
foreach my $part (@{$resource->{'parts'}}) { |
foreach my $part (@{$resource->{'parts'}}) { |
my @Results = &Apache::loncoursedata::get_problem_statistics |
my $problem_number++; |
(undef,$resource->{'symb'},$part, |
my $data = &get_statistics($sequence,$resource,$part, |
$ENV{'request.course.id'}); |
$problem_num); |
my ($num,$Solved,$solved) = @Results[0,4,5]; |
my $value = $data->{$sortfield}; |
my $wrongpercent = 0; |
$Max = $value if ($Max < $value); |
if (defined($num) && $num > 0) { |
push (@Data,$value); |
$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); |
|
} |
} |
} |
} |
} |
} |
# |
# |
# Print out plot request |
# Print out plot request |
my $title = 'Percent Wrong'; |
my $yaxis = ''; |
if ($field eq 'DoDiff') { |
if ($sortfield eq 'per_wrong') { |
$title = 'Degree of Difficulty'; |
$yaxis = 'Percent'; |
} |
|
my $yaxis = 'Percent'; |
|
if ($field eq 'DoDiff') { |
|
$yaxis = ''; |
|
} elsif ($field ne '%Wrng') { |
|
$yaxis = ''; |
|
} |
} |
# |
# |
# Determine appropriate value for $Max |
# Determine appropriate value for $Max |
if ($field eq 'DoDiff') { |
if ($sortfield eq 'deg_of_diff') { |
if ($Max > 0.5) { |
if ($Max > 0.5) { |
$Max = 1; |
$Max = 1; |
} elsif ($Max > 0.2) { |
} elsif ($Max > 0.2) { |
Line 686 sub plot_statistics {
|
Line 828 sub plot_statistics {
|
} elsif ($Max > 0.1) { |
} elsif ($Max > 0.1) { |
$Max = 0.2; |
$Max = 0.2; |
} |
} |
} elsif ($field eq '%Wrng') { |
} elsif ($sortfield eq 'per_wrong') { |
if ($Max > 50) { |
if ($Max > 50) { |
$Max = 100; |
$Max = 100; |
} elsif ($Max > 25) { |
} elsif ($Max > 25) { |
Line 707 sub plot_statistics {
|
Line 849 sub plot_statistics {
|
# |
# |
# Print out the data |
# Print out the data |
$ENV{'form.sortby'} = 'Contents'; |
$ENV{'form.sortby'} = 'Contents'; |
&output_html_ungrouped($r,'show probnum'); |
# &output_html_ungrouped($r); |
return; |
return; |
} |
} |
|
|
############################################### |
############################################### |
############################################### |
############################################### |
|
|
|
=pod |
|
|
|
=item &DrawGraph() |
|
|
|
=cut |
|
|
############################################### |
############################################### |
############################################### |
############################################### |
sub DrawGraph { |
sub DrawGraph { |
my ($values,$title,$xaxis,$yaxis,$Max)=@_; |
my ($values,$title,$xaxis,$yaxis,$Max)=@_; |
$title = '' if (! defined($title)); |
$title = '' if (! defined($title)); |
$xaxis = '' if (! defined($xaxis)); |
$xaxis = '' if (! defined($xaxis)); |
$yaxis = '' if (! defined($yaxis)); |
$yaxis = '' if (! defined($yaxis)); |
|
$title = &Apache::lonnet::escape($title); |
|
$xaxis = &Apache::lonnet::escape($xaxis); |
|
$yaxis = &Apache::lonnet::escape($yaxis); |
# |
# |
my $sendValues = join(',', @$values); |
my $sendValues = join(',', @$values); |
my $sendCount = scalar(@$values); |
my $sendCount = scalar(@$values); |
if ( $Max > 1 ) { |
$Max =1 if ($Max < 1); |
if ($Max % 10) { |
if ( int($Max) < $Max ) { |
if ( int($Max) < $Max ) { |
$Max++; |
$Max++; |
$Max = int($Max); |
$Max = int($Max); |
|
} |
|
} |
|
} else { |
|
$Max = 1; |
|
} |
} |
my @GData = ($title,$xaxis,$yaxis,$Max,$sendCount,$sendValues); |
my @GData = ($title,$xaxis,$yaxis,$Max,$sendCount,$sendValues); |
return '<IMG src="/cgi-bin/graph.png?'. |
return '<IMG src="/cgi-bin/graph.png?'. |
(join('&', @GData)).'" border="1" />'; |
(join('&', @GData)).'" border="1" />'; |
} |
} |
|
|
|
sub get_statistics { |
|
my ($sequence,$resource,$part,$problem_num) = @_; |
|
# |
|
my $symb = $resource->{'symb'}; |
|
my $courseid = $ENV{'request.course.id'}; |
|
# |
|
my $students = \@Apache::lonstatistics::Students; |
|
if ($Apache::lonstatistics::SelectedSections[0] eq 'all') { |
|
$students = undef; |
|
} |
|
my $data = &Apache::loncoursedata::get_problem_statistics |
|
($students,$symb,$part,$courseid); |
|
$data->{'part'} = $part; |
|
$data->{'problem_num'} = $problem_num; |
|
$data->{'container'} = $sequence->{'title'}; |
|
$data->{'title'} = $resource->{'title'}; |
|
$data->{'title.link'} = $resource->{'src'}.'?symb='. |
|
&Apache::lonnet::escape($resource->{'symb'}); |
|
# |
|
return $data; |
|
} |
|
|
############################################### |
############################################### |
############################################### |
############################################### |
|
|
|
=pod |
|
|
|
=item &ProblemStatisticsLegend() |
|
|
|
=cut |
|
|
############################################### |
############################################### |
############################################### |
############################################### |
sub ProblemStatisticsLegend { |
sub ProblemStatisticsLegend { |
Line 754 sub ProblemStatisticsLegend {
|
Line 928 sub ProblemStatisticsLegend {
|
$Ptr .= '<b>Tries</b></td>'; |
$Ptr .= '<b>Tries</b></td>'; |
$Ptr .= '<td>Total number of tries for solving the problem.'; |
$Ptr .= '<td>Total number of tries for solving the problem.'; |
$Ptr .= '</td></tr><tr><td>'; |
$Ptr .= '</td></tr><tr><td>'; |
$Ptr .= '<b>Mod</b></td>'; |
$Ptr .= '<b>Max Tries</b></td>'; |
$Ptr .= '<td>Largest number of tries for solving the problem by a student.'; |
$Ptr .= '<td>Largest number of tries for solving the problem by a student.'; |
$Ptr .= '</td></tr><tr><td>'; |
$Ptr .= '</td></tr><tr><td>'; |
$Ptr .= '<b>Mean</b></td>'; |
$Ptr .= '<b>Mean</b></td>'; |