File:  [LON-CAPA] / loncom / interface / statistics / lonproblemstatistics.pm
Revision 1.49: download - view: text, annotated - select for diffs
Tue Apr 1 17:00:24 2003 UTC (21 years, 3 months ago) by matthew
Branches: MAIN
CVS tags: HEAD
Modified to accept a hash ref from loncoursedata::get_problem_statistics.
Fields output is now configured in one place instead of 3.  The @Fields array
holds the description of each field, its format, and much more.
Removed the output options 'Degree of Difficulty Plot' and 'Percent Wrong
Plot' (see below).
Plots are now available from the 'ungrouped statistics' page.
&statistics_html_table_data now loops through @Fields to determine its behavior.
Added &statistics_table_header to output the proper fields and links for the
HTML statistics tables.
&get_statistics (the wrapper for loncoursedata::get_problem_statistics) now
requires the sequence, resource, part, and problem number as inputs in order
to add extra data to the returned hash reference.

# The LearningOnline Network with CAPA
#
# $Id: lonproblemstatistics.pm,v 1.49 2003/04/01 17:00:24 matthew Exp $
#
# Copyright Michigan State University Board of Trustees
#
# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
#
# LON-CAPA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# LON-CAPA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LON-CAPA; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# /home/httpd/html/adm/gpl.txt
#
# http://www.lon-capa.org/
#
# (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;

use strict;
use Apache::lonnet();
use Apache::lonhtmlcommon;
use Apache::loncoursedata;
use Apache::lonstatistics;
use Spreadsheet::WriteExcel;

my @Fields = (
           { name => 'problem_num',
             title => 'P#',
             align => 'right',
             color => '#FFFFE6' },
           { name   => 'container',
             title  => 'Container',
             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' },
           { 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 Wrong' },
);

###############################################
###############################################

=pod 

=item &CreateInterface()

Create the main intereface for the statistics page.  Allows the user to
select sections, maps, and output.

=cut

###############################################
###############################################
sub CreateInterface {
    my $Str = '';
    $Str .= '<table cellspacing="5">'."\n";
    $Str .= '<tr>';
    $Str .= '<td align="center"><b>Sections</b></td>';
    $Str .= '<td align="center"><b>Sequences and Folders</b></td>';
    $Str .= '<td align="center"><b>Output</b></td>';
    $Str .= '</tr>'."\n";
    #
    $Str .= '<tr><td align="center">'."\n";
    $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5);
    $Str .= '</td><td align="center">';
    #
    my $only_seq_with_assessments = sub { 
        my $s=shift;
        if ($s->{'num_assess'} < 1) { 
            return 0;
        } else { 
            return 1;
        }
    };
    $Str .= &Apache::lonstatistics::MapSelect('Maps','multiple,all',5,
                                              $only_seq_with_assessments);
    $Str .= '</td><td>'."\n";
    $Str .= &CreateAndParseOutputSelector();
    $Str .= '</td></tr>'."\n";
    $Str .= '</table>'."\n";
    return $Str;
}

#######################################################
#######################################################

=pod

=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

#######################################################
#######################################################
my $output_mode;
my $show;

my @OutputOptions = 
    (
     { name  => 'problem statistics grouped by sequence',
       value => 'HTML problem statistics grouped',
       description => 'Output statistics for the problem parts.',
       mode => 'html',
       show => 'grouped',
     },
     { name  => 'problem statistics ungrouped',
       value => 'HTML problem statistics ungrouped',
       description => 'Output statistics for the problem parts.',
       mode => 'html',
       show => 'ungrouped',
     },
     { name  => 'problem statistics, Excel',
       value => 'Excel problem statistics',
       description => 'Output statistics for the problem parts '.
           'in an Excel workbook',
       mode => 'excel',
       show => 'all',
     },
     );

sub OutputDescriptions {
    my $Str = '';
    $Str .= "<h2>Output Modes</h2>\n";
    $Str .= "<dl>\n";
    foreach my $outputmode (@OutputOptions) {
	$Str .="    <dt>".$outputmode->{'name'}."</dt>\n";
	$Str .="        <dd>".$outputmode->{'description'}."</dd>\n";
    }
    $Str .= "</dl>\n";
    return $Str;
}

sub CreateAndParseOutputSelector {
    my $Str = '';
    my $elementname = 'statsoutputmode';
    #
    # Format for output options is 'mode, restrictions';
    my $selected = 'HTML problem statistics grouped';
    if (exists($ENV{'form.'.$elementname})) {
        if (ref($ENV{'form.'.$elementname} eq 'ARRAY')) {
            $selected = $ENV{'form.'.$elementname}->[0];
        } else {
            $selected = $ENV{'form.'.$elementname};
        }
    }
    #
    # Set package variables describing output mode
    $output_mode = 'html';
    $show        = 'all';
    foreach my $option (@OutputOptions) {
        next if ($option->{'value'} ne $selected);
        $output_mode = $option->{'mode'};
        $show        = $option->{'show'};
    }
    #
    # Build the form element
    $Str = qq/<select size="5" name="$elementname">/;
    foreach my $option (@OutputOptions) {
        if (exists($option->{'special'}) && 
            $option->{'special'} =~ /do not show/) {
            next;
        }
        $Str .= "\n".'    <option value="'.$option->{'value'}.'"';
        $Str .= " selected " if ($option->{'value'} eq $selected);
        $Str .= ">".$option->{'name'}."<\/option>";
    }
    $Str .= "\n</select>";
    return $Str;
}

###############################################
###############################################

=pod 

=item &Gather_Student_Data()

Ensures all student data is up to date.

=cut

###############################################
###############################################
sub Gather_Student_Data {
    my ($r) = @_;
    my $c = $r->connection();
    #
    my @Sequences = &Apache::lonstatistics::Sequences_with_Assess();
    #
    my @Students = @Apache::lonstatistics::Students;
    #
    # Open the progress window
    my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin
        ($r,'Statistics Compilation Status',
         'Statistics Compilation Progress', scalar(@Students));
    #
    while (my $student = shift @Students) {
        return if ($c->aborted());
        my ($status,undef) = &Apache::loncoursedata::ensure_current_data
            ($student->{'username'},$student->{'domain'},
             $ENV{'request.course.id'});
        &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
                                                 'last student');
    }
    &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
    $r->rflush();
}

###############################################
###############################################

=pod 

=item &BuildProblemStatisticsPage()

Main interface to problem statistics.

=cut

###############################################
###############################################
sub BuildProblemStatisticsPage {
    my ($r,$c)=@_;
    #
    $output_mode = 'html';
    $show = 'grouped';
    #
    $r->print(&CreateInterface());
    $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="plot" value="" />');
    if (! exists($ENV{'form.statsfirstcall'})) {
        $r->print(<<ENDMSG);
<p>
<font size="+1">
Please make your selections in the boxes above and hit 
the button marked &quot;Update&nbsp;Display&quot;.
</font>
</p>
ENDMSG
        return;
    }
    #
    &Gather_Student_Data($r);
    #
    #
    if ($output_mode eq 'html') {
        $r->print("<h2>".
                  $ENV{'course.'.$ENV{'request.course.id'}.'.description'}.
                  "</h2>\n");
        $r->print("<h3>".localtime(time)."</h3>");
        $r->rflush();
        if ($show eq 'grouped') {
            &output_html_grouped_by_sequence($r);
        } elsif ($show eq 'ungrouped') {
            &output_html_ungrouped($r);
        }
    } elsif ($output_mode eq 'excel') {
        $r->print("<h2>Preparing Excel Spreadsheet</h2>");
        &output_excel($r);
    } else {
        $r->print("<h1>Not implemented</h1>");
    }
    return;
}

###############################################
###############################################

=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 {
    my ($r) = @_;
    my $problem_num = 0;
    #$r->print(&ProblemStatisticsLegend());
    foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) {
        next if ($sequence->{'num_assess'}<1);
        $r->print("<h3>".$sequence->{'title'}."</h3>");
        $r->print('<table border="0"><tr><td bgcolor="#777777">'."\n");
        $r->print('<table border="0" cellpadding="3">'."\n");
        $r->print('<tr bgcolor="#FFFFE6">');
        my $Str = &statistics_table_header('no container no plots');
        $r->print('<tr bgcolor="#FFFFE6">'.$Str."</tr>\n");
        foreach my $resource (@{$sequence->{'contents'}}) {
            next if ($resource->{'type'} ne 'assessment');
            foreach my $part (@{$resource->{'parts'}}) {
                $problem_num++;
                my $data = &get_statistics($sequence,$resource,$part,
                                           $problem_num);
                my $option = '';
                $r->print('<tr>'.&statistics_html_table_data($data,
                                                             'no container').
                          "</tr>\n");
            }
        }
        $r->print("</table>\n");
        $r->print("</td></tr></table>\n");
        $r->rflush();
    }
    #
    return;
}

###############################################
###############################################

=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 {
    my ($r,$option) = @_;
    #
    if (exists($ENV{'form.plot'}) && $ENV{'form.plot'} ne '') {
        &plot_statistics($r,$ENV{'form.plot'});
    }
    #
    my $problem_num = 0;
    my $show_container = 0;
    my $show_part = 0;
    #$r->print(&ProblemStatisticsLegend());
    my $sortby = undef;
    foreach my $field (@Fields) {
        if ($ENV{'form.sortby'} eq $field->{'name'}) {
            $sortby = $field->{'name'};
        }
    }
    if (! defined($sortby) || $sortby eq '' || $sortby eq 'problem_num') {
        $sortby = 'container';
    }
    # If there is more than one sequence, list their titles
    my @Sequences = &Apache::lonstatistics::Sequences_with_Assess();
    if (@Sequences < 1) {
        $option .= ' no container';
    }
    #
    # Compile the data
    my @Statsarray;
    foreach my $sequence (@Sequences) {
        next if ($sequence->{'num_assess'}<1);
        foreach my $resource (@{$sequence->{'contents'}}) {
            next if ($resource->{'type'} ne 'assessment');
            foreach my $part (@{$resource->{'parts'}}) {
                $problem_num++;
                my $data = &get_statistics($sequence,$resource,$part,
                                           $problem_num);
                $show_part = 1 if ($part ne '0');
                #
                push (@Statsarray,$data);
            }
        }
    }
    #
    # Sort the data
    my @OutputOrder;
    if ($sortby eq 'container') {
        @OutputOrder = @Statsarray;
    } else {
        # $sortby is already defined, so we can charge ahead
        if ($sortby =~ /^(title|part)$/i) {
            # Alpha comparison
            @OutputOrder = sort {
                lc($a->{$sortby}) cmp lc($b->{$sortby}) ||
                    lc($a->{'title'}) cmp lc($b->{'title'}) ||
                        lc($a->{'part'}) cmp lc($b->{'part'});
            } @Statsarray;
        } else {
            # Numerical comparison
            @OutputOrder = sort {
                my $retvalue = 0;
                if ($b->{$sortby} eq 'nan') {
                    if ($a->{$sortby} ne 'nan') {
                        $retvalue = -1;
                    } else {
                        $retvalue = 0;
                    }
                }
                if ($a->{$sortby} eq 'nan') {
                    if ($b->{$sortby} ne 'nan') {
                        $retvalue = 1;
                    }
                }
                if ($retvalue eq '0') {
                    $retvalue = $b->{$sortby} <=> $a->{$sortby} ||
                                lc($a->{'title'}) <=> lc($b->{'title'}) ||
                                lc($a->{'part'})  <=> lc($b->{'part'});
                }
                $retvalue;
            } @Statsarray;
        }
    }
    $option .= 'no part' if (! $show_part);
    my $num_output = 0;
    #
    # output the headers
    $r->print('<table border="0"><tr><td bgcolor="#777777">'."\n");
    $r->print('<table border="0" cellpadding="3">'."\n");
    my $Str = &statistics_table_header($option.' sortable');
    $r->print('<tr bgcolor="#FFFFE6">'.$Str."</tr>\n");
    #
    foreach my $rowdata (@OutputOrder) {
        $num_output++;
        if ($num_output % 25 == 0) {
            $r->print("</table>\n</td></tr></table>\n");
            #
            $r->print('<table border="0"><tr><td bgcolor="#777777">'."\n");
            $r->print('<table border="0" cellpadding="3">'."\n");
            my $Str = &statistics_table_header($option.' sortable');
            $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("</td></tr></table>\n");
    $r->rflush();
    #
    return;
}

###############################################
###############################################

=pod 

=item &output_excel()

Presents the statistical data in an Excel 95 compatable spreadsheet file.

=cut

###############################################
###############################################
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
    foreach my $field (@Fields) {
        next if ($field->{'name'} eq 'problem_num');
        $excel_sheet->write($rows_output,$cols_output++,$field->{'title'});
    }
    $rows_output++;
    #
    # Write the data
    my $problem_num=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'}}) {
                $cols_output=0;
                $problem_num++;
                my $data = &get_statistics($sequence,$resource,$part,
                                           $problem_num);
                #
                if (!defined($part) || $part eq '') {
                    $part = ' ';
                }
                foreach my $field (@Fields) {
                    next if ($field->{'name'} eq 'problem_num');
                    $excel_sheet->write($rows_output,$cols_output++,
                                        $data->{$field->{'name'}});
                }
                $rows_output++;
            }
        }
    }
    #
    # Write the excel file
    $excel_workbook->close();
    # Tell the user where to get their excel file
    $r->print('<br />'.
              '<a href="'.$filename.'">Your Excel spreadsheet.</a>'."\n");
    $r->rflush();
    return;
}

###############################################
###############################################

=pod 

=item &statistics_html_table_data()

Help function used to format the rows for HTML table output.

=cut

###############################################
###############################################
sub statistics_html_table_data {
    my ($data,$options) = @_;
    my $row = '';
    foreach my $field (@Fields) {
        next if ($options =~ /no $field->{'name'}/);
        $row .= '<td bgcolor="'.$field->{'color'}.'"';
        if (exists($field->{'align'})) {
            $row .= ' align="'.$field->{'align'}.'"';
            }
        $row .= '>';
        if (exists($field->{'special'}) && $field->{'special'} eq 'link') {
            $row .= '<a href="'.$data->{$field->{'name'}.'.link'}.
                ' target="_blank">';
        }
        if (exists($field->{'format'})) {
            $row .= sprintf($field->{'format'},$data->{$field->{'name'}});
        } else {
            $row .= $data->{$field->{'name'}};
        }
        if (exists($field->{'special'}) && $field->{'special'} eq 'link') {
            $row.= '</a>';
        }
        $row .= '</td>';
    }
    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 {
    my ($r,$datafield) = @_;
    my @Data;
    #
    #
    my $sortfield = undef;
    my $title = undef;
    foreach my $field (@Fields) {
        if ($datafield eq $field->{'name'} &&
            exists($field->{'graphable'}) && $field->{'graphable'} eq 'yes') {
            $sortfield = $field->{'name'};
            $title = $field->{'long_title'};
        }
    }
    return if (! defined($sortfield) || $sortfield eq '');
    &Apache::lonnet::logthis('data field = '.$datafield);
    #
    my $Max = 0;
    my $problem_num = 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 $problem_number++;
                my $data = &get_statistics($sequence,$resource,$part,
                                           $problem_num);
                my $value = $data->{$sortfield};
                $Max = $value if ($Max < $value);
                push (@Data,$value);
            }
        }
    }
    #
    # Print out plot request
    my $yaxis = '';
    if ($sortfield eq 'per_wrong') {
        $yaxis = 'Percent';
    }
    #
    # Determine appropriate value for $Max
    if ($sortfield eq 'deg_of_diff') {
        if ($Max > 0.5) {
            $Max = 1;
        } elsif ($Max > 0.2) {
            $Max = 0.5;
        } elsif ($Max > 0.1) {
            $Max = 0.2;
        }
    } elsif ($sortfield eq 'per_wrong') {
        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;
        }
    }
    
    $r->print("<p>".&DrawGraph(\@Data,$title,'Problem Number',$yaxis,
                               $Max)."</p>\n");
    #
    # Print out the data
    $ENV{'form.sortby'} = 'Contents';
#    &output_html_ungrouped($r);
    return;
}

###############################################
###############################################

=pod 

=item &DrawGraph()

=cut

###############################################
###############################################
sub DrawGraph {
    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);
    $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 {
    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'};
    #
    return $data;
}

###############################################
###############################################

=pod 

=item &ProblemStatisticsLegend()

=cut

###############################################
###############################################
sub ProblemStatisticsLegend {
    my $Ptr = '';
    $Ptr = '<table border="0">';
    $Ptr .= '<tr><td>';
    $Ptr .= '<b>#Stdnts</b></td>';
    $Ptr .= '<td>Total number of students attempted the problem.';
    $Ptr .= '</td></tr><tr><td>';
    $Ptr .= '<b>Tries</b></td>';
    $Ptr .= '<td>Total number of tries for solving the problem.';
    $Ptr .= '</td></tr><tr><td>';
    $Ptr .= '<b>Max Tries</b></td>';
    $Ptr .= '<td>Largest number of tries for solving the problem by a student.';
    $Ptr .= '</td></tr><tr><td>';
    $Ptr .= '<b>Mean</b></td>';
    $Ptr .= '<td>Average number of tries. [ Tries / #Stdnts ]';
    $Ptr .= '</td></tr><tr><td>';
    $Ptr .= '<b>#YES</b></td>';
    $Ptr .= '<td>Number of students solved the problem correctly.';
    $Ptr .= '</td></tr><tr><td>';
    $Ptr .= '<b>#yes</b></td>';
    $Ptr .= '<td>Number of students solved the problem by override.';
    $Ptr .= '</td></tr><tr><td>';
    $Ptr .= '<b>%Wrong</b></td>';
    $Ptr .= '<td>Percentage of students who tried to solve the problem ';
    $Ptr .= 'but is still incorrect. [ 100*((#Stdnts-(#YES+#yes))/#Stdnts) ]';
    $Ptr .= '</td></tr><tr><td>';
    $Ptr .= '<b>DoDiff</b></td>';
    $Ptr .= '<td>Degree of Difficulty of the problem.  ';
    $Ptr .= '[ 1 - ((#YES+#yes) / Tries) ]';
    $Ptr .= '</td></tr><tr><td>';
    $Ptr .= '<b>S.D.</b></td>';
    $Ptr .= '<td>Standard Deviation of the tries.  ';
    $Ptr .= '[ sqrt(sum((Xi - Mean)^2)) / (#Stdnts-1) ';
    $Ptr .= 'where Xi denotes every student\'s tries ]';
    $Ptr .= '</td></tr><tr><td>';
    $Ptr .= '<b>Skew.</b></td>';
    $Ptr .= '<td>Skewness of the students tries.';
    $Ptr .= '[(sqrt( sum((Xi - Mean)^3) / #Stdnts)) / (S.D.^3)]';
    $Ptr .= '</td></tr><tr><td>';
    $Ptr .= '<b>Dis.F.</b></td>';
    $Ptr .= '<td>Discrimination Factor: A Standard for evaluating the ';
    $Ptr .= 'problem according to a Criterion<br>';
    $Ptr .= '<b>[Criterion to group students into %27 Upper Students - ';
    $Ptr .= 'and %27 Lower Students]</b><br>';
    $Ptr .= '<b>1st Criterion</b> for Sorting the Students: ';
    $Ptr .= '<b>Sum of Partial Credit Awarded / Total Number of Tries</b><br>';
    $Ptr .= '<b>2nd Criterion</b> for Sorting the Students: ';
    $Ptr .= '<b>Total number of Correct Answers / Total Number of Tries</b>';
    $Ptr .= '</td></tr>';
    $Ptr .= '<tr><td><b>Disc.</b></td>';
    $Ptr .= '<td>Number of Students had at least one discussion.';
    $Ptr .= '</td></tr></table>';
    return $Ptr;
}

#---- END Problem Statistics Web Page ----------------------------------------

1;
__END__

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>