File:  [LON-CAPA] / loncom / interface / statistics / loncorrectproblemplot.pm
Revision 1.9: download - view: text, annotated - select for diffs
Sun Mar 7 20:53:41 2004 UTC (20 years, 4 months ago) by matthew
Branches: MAIN
CVS tags: HEAD
loncoursedata.pm: &get_student_scores modified to limit by time.
loncorrectproblemplot.pm: Added time limits, corrected "bin"ing bug which
    caused the last data set to be missed, plot title is now the sequence
    being analyzed or "Multiple Sequences", added "N", the number of students,
    to the title.  Added the maximum possible number of correct problems to
    the x-axis label.

# The LearningOnline Network with CAPA
#
# $Id: loncorrectproblemplot.pm,v 1.9 2004/03/07 20:53:41 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/
#

package Apache::loncorrectproblemplot;

use strict;
use Apache::lonnet();
use Apache::loncommon();
use Apache::lonhtmlcommon();
use Apache::loncoursedata();
use Apache::lonstatistics;
use Apache::lonstathelpers;
use Apache::lonlocal;

my @SubmitButtons = (
                     { name => 'CreatePlot',
                       text => 'Create Plot' },
                     { name => 'ClearCache',
                       text => 'Clear Caches' },
                     { name => 'updatecaches',
                       text => 'Update Student Data' },
                     );

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

=pod

=item &BuildCorrectProblemsPage

Entry point from lonstatistics to the correct problems plot page.

=cut

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

sub BuildCorrectProblemsPage {
    my ($r,$c)=@_;
    #
    my %Saveable_Parameters = ('Status' => 'scalar',
                               'Section' => 'array');
    &Apache::loncommon::store_course_settings('correct_problems_plot',
                                              \%Saveable_Parameters);
    &Apache::loncommon::restore_course_settings('correct_problems_plot',
                                                \%Saveable_Parameters);
    #
    &Apache::lonstatistics::PrepareClasslist();    
    #
    $r->print(&CreateInterface());
    #
    my @Students = @Apache::lonstatistics::Students;
    #
    if (@Students < 1) {
        $r->print('<h2>'.
                  &mt('There are no students in the sections selected').
                  '</h2>');
    }
    #
    &Apache::loncoursedata::clear_internal_caches();
    if (exists($ENV{'form.ClearCache'}) || 
        exists($ENV{'form.updatecaches'}) ||
        (exists($ENV{'form.firstanalysis'}) &&
         $ENV{'form.firstanalysis'} ne 'no')) {
        &Apache::lonstatistics::Gather_Full_Student_Data($r);
    }
    if (! exists($ENV{'form.firstanalysis'})) {
        $r->print('<input type="hidden" name="firstanalysis" value="yes" />');
    } else {
        $r->print('<input type="hidden" name="firstanalysis" value="no" />');
    }
    foreach my $button (@SubmitButtons) {
        $r->print('<input type="submit" name="'.$button->{'name'}.'" '.
                  'value="'.&mt($button->{'text'}).'" />');
        $r->print('&nbsp;'x5);
    }
    $r->rflush();
    #
    # Determine which problem symbs we are to sum over
    if (exists($ENV{'form.CreatePlot'})) {
        my @ProblemSymbs;
        my $total_parts = 0;
        my $title = '';
        if ($Apache::lonstatistics::SelectedMaps[0] ne 'all') {
            foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()){
                if ($title eq '') {
                    $title = $seq->{'title'};
                } else {
                    $title = 'Multiple Sequences';
                }
                foreach my $res (@{$seq->{'contents'}}) {
                    next if ($res->{'type'} ne 'assessment');
                    foreach my $part (@{$res->{'parts'}}) {
                        $total_parts++;
                        push(@ProblemSymbs,{symb=>$res->{'symb'},
                                            part=>$part});
                    }
                }
            }
        }
        my ($starttime,$endtime) = &Apache::lonstathelpers::get_time_limits();
        my $score_data = &Apache::loncoursedata::get_student_scores
            (\@Apache::lonstatistics::SelectedSections,
             \@ProblemSymbs,
             $Apache::lonstatistics::enrollment_status,undef,
             $starttime,$endtime);
        $r->print(&AnalyzeScoreData($score_data,$title,$total_parts));
    }
    return;
}

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

=pod

=item & AnalyzeScoreData($score_data)

Analyze the result of &Apache::loncoursedata::get_student_scores() and
return html with a plot of the data and a table of the values and bins.

=cut

#########################################################
#########################################################
sub AnalyzeScoreData {
    my ($score_data,$title,$total_parts) = @_;
    #
    # Basic check first
    if (@$score_data < 1) {
        return '<h2>There is no data to plot</h2>';
    }
    #
    # Determine which bins to use
    my $lowest  = $score_data->[0]->[0];
    $lowest = 0;
    my $highest = $score_data->[-1]->[0];
    &Apache::lonnet::logthis('highest = '.$highest);
    my $binsize = 1;
    if ($highest > 50) { $binsize = 2; }
    if ($highest > 100) { $binsize = 5; }
    if ($highest > 200) { $binsize = 10; }
    if ($highest > 500) { $binsize = 20; }
    if ($highest > 1000) { $binsize = 50; }
    if ($highest > 2000) { $binsize = 100; }
    #
    # Get the data into the bins (destroying $score_data in the process)
    my @Bins = &bin_data($score_data,$binsize,$lowest,$highest);
    my @Xdata; my @Ydata; my $max;
    my $Str = '<table border="1">'."\n".'<tr><th>Range</th><th>Count</th></tr>'."\n";
    my $sum = 0;
    while (my $bin = shift(@Bins)) {
        push (@Xdata,$bin->{'start'});
        push (@Ydata,$bin->{'count'});
        $sum += $bin->{'count'};
        if ($bin->{'count'} > $max) {
            $max = $bin->{'count'};
        }
        $Str.= '<tr><td>'.$bin->{'start'}.' - '.$bin->{'end'}.'</td>'.
            '<td>'.$bin->{'count'}.'</td></tr>'."\n";
    }
    # scale max to an integer.
    $max = 5*(int($max/5)+1);
    $Str .= "</table><br />\n";
    $title = &HTML::Entities::decode($title);
    $Str = "<br />\n".&Apache::loncommon::DrawBarGraph($title.' N = '.$sum,
                                 'Num Correct Problems (max:'.$total_parts.')',
                                                       'Number of students',
                                                       $max,
                                                       undef, # colors
                                                       \@Xdata,
                                                       \@Ydata).
                                                           "\n<br />\n".$Str;
    return $Str;                                               
}


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

=pod

=item &bin_data($data,$binsize,$startbin,$endbin)

Note: This routine destroys the array of data passed to it.

Inputs: $data: array reference - the contents of @$data must
        be arrays with x and y data.  $data = [ [x1,y1],[x2,y2],...];
        $binsize: Width of bins to put data in
        $startbin: the starting bin.
        $endbin: the ending bin.
Returns: Array of Bins.  Each bin is a hash reference with the following
         keys: 'start', 'count', and 'end'.

=cut

#########################################################
#########################################################
sub bin_data {
    my ($data,$binsize,$startbin,$endbin)=@_;
    return () if (! defined($data) || ref($data) ne 'ARRAY');
    #
    # Determine bin geometry
    my $binstart = $startbin;
    my $binend = $startbin+$binsize;
    # put the data into bins
    my @Bins;
    my $count=0;
    my $idx=0;
    while ($idx < scalar(@$data) && ($endbin-$binend + $binsize)>0) {
        my $dataset = $data->[$idx++];
        my ($x,$y) = @{$dataset};
        while ($x > ($binend-.001)) {
            # store the old data
            push (@Bins,{ start => $binstart,
                          count => $count,
                          end   => $binend });
            # start counting again
            $binstart += $binsize;
            $binend += $binsize;
            $count = 0;
        }
        $count+=$y;
    }
    if ($count > 0) {
        push (@Bins,{ start => $binstart,
                      count => $count,
                      end   => $binend });
    }
    return @Bins;
}

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

=pod

=item &CreateInterface

Inputs: none.

Returns: HTML for the correct problems plot interface.

=cut

#########################################################
#########################################################
sub CreateInterface {
    ##
    ## Environment variable initialization
    my $Str;
    $Str .= &Apache::lonhtmlcommon::breadcrumbs
        (undef,'Correct Problems Plot');
    $Str .= '<table cellspacing="5">'."\n";
    $Str .= '<tr>';
    $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('Sequences and Folders').'</b></td>';
    $Str .= '<td rowspan="2">'.
        &Apache::lonstathelpers::limit_by_time_form().'</td>';
    $Str .= '</tr>'."\n";
    ##
    ## 
    $Str .= '<tr><td align="center">'."\n";
    $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5);
    $Str .= '</td>';
    #
    $Str .= '<td align="center">';
    $Str .= &Apache::lonhtmlcommon::StatusOptions(undef,undef,5);
    $Str .= '</td><td>'."\n";
    #
    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>';
    #
    ##
    $Str .= '</tr>'."\n";
    $Str .= '</table>'."\n";
    return $Str;
}

1;

__END__

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