version 1.59, 2003/09/29 16:20:18
|
version 1.72, 2004/03/23 20:08:58
|
Line 51 package Apache::lonproblemstatistics;
|
Line 51 package Apache::lonproblemstatistics;
|
|
|
use strict; |
use strict; |
use Apache::lonnet(); |
use Apache::lonnet(); |
|
use Apache::loncommon(); |
use Apache::lonhtmlcommon; |
use Apache::lonhtmlcommon; |
use Apache::loncoursedata; |
use Apache::loncoursedata; |
use Apache::lonstatistics; |
use Apache::lonstatistics; |
use Apache::lonlocal; |
use Apache::lonlocal; |
use Spreadsheet::WriteExcel; |
use Spreadsheet::WriteExcel; |
|
use Apache::lonstathelpers(); |
|
use Time::HiRes; |
## |
## |
## Localization notes: |
## Localization notes: |
## |
## |
Line 146 my @Fields = (
|
Line 148 my @Fields = (
|
title => '#YES', |
title => '#YES', |
align => 'right', |
align => 'right', |
color => '#FFDDDD', |
color => '#FFDDDD', |
format => '%d', |
format => '%4.1f',# format => '%d', |
sortable => 'yes', |
sortable => 'yes', |
graphable => 'yes', |
graphable => 'yes', |
long_title => 'Number of Students able to Solve' }, |
long_title => 'Number of Students able to Solve' }, |
Line 154 my @Fields = (
|
Line 156 my @Fields = (
|
title => '#yes', |
title => '#yes', |
align => 'right', |
align => 'right', |
color => '#FFDDDD', |
color => '#FFDDDD', |
format => '%d', |
format => '%4.1f',# format => '%d', |
sortable => 'yes', |
sortable => 'yes', |
graphable => 'yes', |
graphable => 'yes', |
long_title => 'Number of Students given Override' }, |
long_title => 'Number of Students given Override' }, |
Line 166 my @Fields = (
|
Line 168 my @Fields = (
|
sortable => 'yes', |
sortable => 'yes', |
graphable => 'yes', |
graphable => 'yes', |
long_title => 'Percent of students whose final answer is wrong' }, |
long_title => 'Percent of students whose final answer is wrong' }, |
|
{ name => 'deg_of_disc', |
|
title => 'Deg of Disc', |
|
align => 'right', |
|
color => '#FFFFE6', |
|
format => '%4.2f', |
|
sortable => 'yes', |
|
graphable => 'yes', |
|
long_title => 'Degree of Discrimination' }, |
|
|
); |
); |
|
|
############################################### |
############################################### |
Line 184 select sections, maps, and output.
|
Line 195 select sections, maps, and output.
|
############################################### |
############################################### |
my @OutputOptions = |
my @OutputOptions = |
( |
( |
{ name => 'problem statistics grouped by sequence', |
{ name => 'grouped by sequence', |
value => 'HTML problem statistics grouped', |
value => 'HTML problem statistics grouped', |
description => 'Output statistics for the problem parts.', |
description => 'Output statistics for the problem parts.', |
mode => 'html', |
mode => 'html', |
show => 'grouped', |
show => 'grouped', |
}, |
}, |
{ name => 'problem statistics ungrouped', |
{ name => 'ungrouped', |
value => 'HTML problem statistics ungrouped', |
value => 'HTML problem statistics ungrouped', |
description => 'Output statistics for the problem parts.', |
description => 'Output statistics for the problem parts.', |
mode => 'html', |
mode => 'html', |
show => 'ungrouped', |
show => 'ungrouped', |
}, |
}, |
{ name => 'problem statistics, Excel', |
{ name => 'Excel', |
value => 'Excel problem statistics', |
value => 'Excel problem statistics', |
description => 'Output statistics for the problem parts '. |
description => 'Output statistics for the problem parts '. |
'in an Excel workbook', |
'in an Excel workbook', |
Line 207 my @OutputOptions =
|
Line 218 my @OutputOptions =
|
|
|
sub CreateInterface { |
sub CreateInterface { |
my $Str = ''; |
my $Str = ''; |
|
$Str .= &Apache::lonhtmlcommon::breadcrumbs |
|
(undef,'Overall Problem Statistics','Statistics_Overall_Key'); |
$Str .= '<table cellspacing="5">'."\n"; |
$Str .= '<table cellspacing="5">'."\n"; |
$Str .= '<tr>'; |
$Str .= '<tr>'; |
$Str .= '<td align="center"><b>'.&mt('Sections').'</b></td>'; |
$Str .= '<td align="center"><b>'.&mt('Sections').'</b></td>'; |
$Str .= '<td align="center"><b>'.&mt('Enrollment Status').'</b></td>'; |
$Str .= '<td align="center"><b>'.&mt('Enrollment Status').'</b></td>'; |
$Str .= '<td align="center"><b>'.&mt('Sequences and Folders').'</b></td>'; |
$Str .= '<td align="center"><b>'.&mt('Sequences and Folders').'</b></td>'; |
$Str .= '<td align="center"><b>'.&mt('Output').'</b></td>'; |
$Str .= '<td align="center"><b>'.&mt('Output').'</b></td>'; |
|
$Str .= '<td rowspan="2">'. |
|
&Apache::lonstathelpers::limit_by_time_form().'</td>'; |
$Str .= '</tr>'."\n"; |
$Str .= '</tr>'."\n"; |
# |
# |
$Str .= '<tr><td align="center">'."\n"; |
$Str .= '<tr><td align="center">'."\n"; |
Line 265 Main interface to problem statistics.
|
Line 280 Main interface to problem statistics.
|
sub BuildProblemStatisticsPage { |
sub BuildProblemStatisticsPage { |
my ($r,$c)=@_; |
my ($r,$c)=@_; |
# |
# |
|
my %Saveable_Parameters = ('Status' => 'scalar', |
|
'statsoutputmode' => 'scalar', |
|
'Section' => 'array', |
|
'StudentData' => 'array', |
|
'Maps' => 'array'); |
|
&Apache::loncommon::store_course_settings('statistics', |
|
\%Saveable_Parameters); |
|
&Apache::loncommon::restore_course_settings('statistics', |
|
\%Saveable_Parameters); |
|
# |
|
&Apache::lonstatistics::PrepareClasslist(); |
|
# |
|
&Apache::loncoursedata::populate_weight_table(); |
|
# |
my ($interface,$output_mode,$show) = &CreateInterface(); |
my ($interface,$output_mode,$show) = &CreateInterface(); |
$r->print($interface); |
$r->print($interface); |
$r->print('<input type="hidden" name="statsfirstcall" value="no" />'); |
$r->print('<input type="hidden" name="statsfirstcall" value="no" />'); |
Line 282 sub BuildProblemStatisticsPage {
|
Line 311 sub BuildProblemStatisticsPage {
|
$r->print("<h2>". |
$r->print("<h2>". |
$ENV{'course.'.$ENV{'request.course.id'}.'.description'}. |
$ENV{'course.'.$ENV{'request.course.id'}.'.description'}. |
"</h2>\n"); |
"</h2>\n"); |
$r->print("<h3>".localtime(time)."</h3>"); |
my ($starttime,$endtime) = &Apache::lonstathelpers::get_time_limits(); |
|
if (defined($starttime) || defined($endtime)) { |
|
# Inform the user what the time limits on the data are. |
|
$r->print('<h3>'.&mt('Statistics on submissions from [_1] to [_2]', |
|
&Apache::lonlocal::locallocaltime($starttime), |
|
&Apache::lonlocal::locallocaltime($endtime)). |
|
'</h3>'); |
|
} |
|
$r->print("<h3>".&mt('Compiled on [_1]', |
|
&Apache::lonlocal::locallocaltime(time))."</h3>"); |
$r->rflush(); |
$r->rflush(); |
if ($show eq 'grouped') { |
if ($show eq 'grouped') { |
&output_html_grouped_by_sequence($r); |
&output_html_grouped_by_sequence($r); |
Line 488 sub output_excel {
|
Line 526 sub output_excel {
|
$ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'. |
$ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'. |
time.'_'.rand(1000000000).'.xls'; |
time.'_'.rand(1000000000).'.xls'; |
# |
# |
|
my ($starttime,$endtime) = &Apache::lonstathelpers::get_time_limits(); |
|
# |
my $excel_workbook = undef; |
my $excel_workbook = undef; |
my $excel_sheet = undef; |
my $excel_sheet = undef; |
# |
# |
Line 517 sub output_excel {
|
Line 557 sub output_excel {
|
if (length($sheetname) > 31) { |
if (length($sheetname) > 31) { |
$sheetname = substr($sheetname,0,31); |
$sheetname = substr($sheetname,0,31); |
} |
} |
$excel_sheet = $excel_workbook->addworksheet($sheetname); |
$excel_sheet = $excel_workbook->addworksheet( |
|
&Apache::loncommon::clean_excel_name($sheetname) |
|
); |
# |
# |
# Put the course description in the header |
# Put the course description in the header |
$excel_sheet->write($rows_output,$cols_output++, |
$excel_sheet->write($rows_output,$cols_output++, |
Line 544 sub output_excel {
|
Line 586 sub output_excel {
|
$excel_sheet->write($rows_output,$cols_output++,$sectionstring); |
$excel_sheet->write($rows_output,$cols_output++,$sectionstring); |
$cols_output += scalar(@Sections); |
$cols_output += scalar(@Sections); |
# |
# |
|
# Time restrictions |
|
my $time_string; |
|
if (defined($starttime)) { |
|
# call localtime but not lonlocal:locallocaltime because excel probably |
|
# cannot handle localized text. Probably. |
|
$time_string .= 'Data collected from '.localtime($time_string); |
|
if (defined($endtime)) { |
|
$time_string .= ' to '.localtime($endtime); |
|
} |
|
$time_string .= '.'; |
|
} elsif (defined($endtime)) { |
|
# See note above about lonlocal:locallocaltime |
|
$time_string .= 'Data collected before '.localtime($endtime).'.'; |
|
} |
|
|
|
# |
# Put the date in there too |
# Put the date in there too |
$excel_sheet->write($rows_output,$cols_output++, |
$excel_sheet->write($rows_output,$cols_output++, |
'Compiled on '.localtime(time)); |
'Compiled on '.localtime(time)); |
Line 751 sub plot_statistics {
|
Line 809 sub plot_statistics {
|
} |
} |
} |
} |
|
|
$r->print("<p>".&DrawGraph(\@Data,$title,'Problem Number',$yaxis, |
$r->print("<p>".&Apache::loncommon::DrawBarGraph($title, |
$Max)."</p>\n"); |
'Problem Number', |
|
$yaxis, |
|
$Max, |
|
undef, # colors |
|
undef, # labels |
|
\@Data)."</p>\n"); |
# |
# |
# Print out the data |
# Print out the data |
$ENV{'form.sortby'} = 'Contents'; |
$ENV{'form.sortby'} = 'Contents'; |
Line 760 sub plot_statistics {
|
Line 823 sub plot_statistics {
|
return; |
return; |
} |
} |
|
|
############################################### |
######################################################## |
############################################### |
######################################################## |
|
|
=pod |
=pod |
|
|
=item &DrawGraph() |
=item &get_statistics() |
|
|
=cut |
Wrapper routine from the call to loncoursedata::get_problem_statistics. |
|
Calls lonstathelpers::get_time_limits() to limit the data set by time. |
|
|
############################################### |
Inputs: $sequence, $resource, $part, $problem_num |
############################################### |
|
sub DrawGraph { |
Returns: Hash reference with statistics data from |
my ($values,$title,$xaxis,$yaxis,$Max)=@_; |
loncoursedata::get_problem_statistics. |
$title = '' if (! defined($title)); |
|
$xaxis = '' if (! defined($xaxis)); |
=cut |
$yaxis = '' if (! defined($yaxis)); |
|
$title = &Apache::lonnet::escape($title); |
|
$xaxis = &Apache::lonnet::escape($xaxis); |
|
$yaxis = &Apache::lonnet::escape($yaxis); |
|
# |
|
my $sendValues = join(',', @$values); |
|
my $sendCount = scalar(@$values); |
|
$Max =1 if ($Max < 1); |
|
if ( int($Max) < $Max ) { |
|
$Max++; |
|
$Max = int($Max); |
|
} |
|
my @GData = ($title,$xaxis,$yaxis,$Max,$sendCount,$sendValues); |
|
return '<IMG src="/cgi-bin/graph.png?'. |
|
(join('&', @GData)).'" border="1" />'; |
|
} |
|
|
|
|
######################################################## |
|
######################################################## |
sub get_statistics { |
sub get_statistics { |
my ($sequence,$resource,$part,$problem_num) = @_; |
my ($sequence,$resource,$part,$problem_num) = @_; |
# |
# |
|
my ($starttime,$endtime) = &Apache::lonstathelpers::get_time_limits(); |
my $symb = $resource->{'symb'}; |
my $symb = $resource->{'symb'}; |
my $courseid = $ENV{'request.course.id'}; |
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 |
my $data = &Apache::loncoursedata::get_problem_statistics |
($students,$symb,$part,$courseid); |
(\@Apache::lonstatistics::SelectedSections, |
|
$Apache::lonstatistics::enrollment_status, |
|
$symb,$part,$courseid,$starttime,$endtime); |
$data->{'part'} = $part; |
$data->{'part'} = $part; |
$data->{'problem_num'} = $problem_num; |
$data->{'problem_num'} = $problem_num; |
$data->{'container'} = $sequence->{'title'}; |
$data->{'container'} = $sequence->{'title'}; |
Line 811 sub get_statistics {
|
Line 860 sub get_statistics {
|
$data->{'title.link'} = $resource->{'src'}.'?symb='. |
$data->{'title.link'} = $resource->{'src'}.'?symb='. |
&Apache::lonnet::escape($resource->{'symb'}); |
&Apache::lonnet::escape($resource->{'symb'}); |
# |
# |
|
$data->{'deg_of_disc'} = &compute_discrimination_factor($resource,$part,$sequence); |
return $data; |
return $data; |
} |
} |
|
|
|
|
|
############################################### |
|
############################################### |
|
|
|
=pod |
|
|
|
=item &compute_discrimination_factor() |
|
|
|
Inputs: $Resource, $Sequence |
|
|
|
Returns: integer between -1 and 1 |
|
|
|
=cut |
|
|
|
############################################### |
|
############################################### |
|
sub compute_discrimination_factor { |
|
my ($resource,$part,$sequence) = @_; |
|
my @Resources; |
|
foreach my $res (@{$sequence->{'contents'}}) { |
|
next if ($res->{'symb'} eq $resource->{'symb'}); |
|
push (@Resources,$res->{'symb'}); |
|
} |
|
# |
|
# rank |
|
my $ranking = |
|
&Apache::loncoursedata::rank_students_by_scores_on_resources |
|
(\@Resources, |
|
\@Apache::lonstatistics::SelectedSections, |
|
$Apache::lonstatistics::enrollment_status,undef); |
|
# |
|
# compute their percent scores on the problems in the sequence, |
|
my $number_to_grab = int(scalar(@{$ranking})/4); |
|
my $num_students = scalar(@{$ranking}); |
|
my @BottomSet = map { $_->[&Apache::loncoursedata::RNK_student()]; |
|
} @{$ranking}[0..$number_to_grab]; |
|
my @TopSet = |
|
map { |
|
$_->[&Apache::loncoursedata::RNK_student()]; |
|
} @{$ranking}[($num_students-$number_to_grab)..($num_students-1)]; |
|
my ($bottom_sum,$bottom_max) = |
|
&Apache::loncoursedata::get_sum_of_scores($resource,$part,\@BottomSet); |
|
my ($top_sum,$top_max) = |
|
&Apache::loncoursedata::get_sum_of_scores($resource,$part,\@TopSet); |
|
my $deg_of_disc; |
|
if ($top_max == 0 || $bottom_max==0) { |
|
$deg_of_disc = 'nan'; |
|
} else { |
|
$deg_of_disc = ($top_sum/$top_max) - ($bottom_sum/$bottom_max); |
|
} |
|
#&Apache::lonnet::logthis(' '.$top_sum.'/'.$top_max. |
|
# ' - '.$bottom_sum.'/'.$bottom_max); |
|
return $deg_of_disc; |
|
} |
|
|
############################################### |
############################################### |
############################################### |
############################################### |
|
|