File:  [LON-CAPA] / loncom / interface / statistics / lonstudentassessment.pm
Revision 1.30: download - view: text, annotated - select for diffs
Fri Feb 28 21:19:00 2003 UTC (21 years, 2 months ago) by matthew
Branches: MAIN
CVS tags: HEAD
I did *NOT* rewrite the subroutines &CreateLegend and &StudentAverageTotal.
&StudentAverageTotal is not called at this time and will be rewritten.

Removed:
    &ShouldShowColumns
    &FindSelectedStudent
    &CreateColumnSelectors
    &CreateColumnSelectionBox
    &MaxSeqPr
    &StudentReport

Added:
    &ChartOutputStudent
    &StudentPerformanceOnSequence

Rewrote:
    &CreateTableHeadings
    &CreateInterface
    &BuildStudentAssessmentPage

Now use &Apache::loncommon::get_current_state() to get the chart data (see
    &ChartOutputStudent()).  Note this uses the symb-less call since we are
    most likely to process all the students data.

# The LearningOnline Network with CAPA
#
# $Id: lonstudentassessment.pm,v 1.30 2003/02/28 21:19:00 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

lonstudentassessment

=head1 SYNOPSIS

Presents assessment data about a student or a group of students.

=head1 Subroutines

=over 4 

=cut

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

package Apache::lonstudentassessment;

use strict;
use Apache::lonstatistics;
use Apache::lonhtmlcommon;
use Apache::loncoursedata;
use Apache::lonnet; # for logging porpoises
use GDBM_File;

my $Statistics;

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

=pod

=item &BuildStudentAssessmentPage()

Inputs: 

=over 4

=item $cacheDB The name of the cache file used to store student data

=item $students Array ref containing the name(s) of the students 
selected for display

=item $courseID The ID of the course

=item $formName The name of the html form - 'Statistics'

=item $headings Array ref of headings to show

=item $spacing A string of spaces

=item $studentInformation Array ref of possible headings for student info
('fullname','section',...)

=item $r Apache Request

=item $c Apache Connection 

=back

=cut

#######################################################
#######################################################
sub BuildStudentAssessmentPage {
    my ($r,$c)=@_;
    undef($Statistics);
    
    #
    $r->print(&CreateInterface());
    $r->rflush();
    #
    $r->print(&CreateTableHeadings());
    if($c->aborted()) {  return ; }
    
    my $Count = 0;
    $r->print('<pre>'."\n");
    foreach my $student (@Apache::lonstatistics::Students) {
        if($c->aborted()) { return ; }
        $r->print(&ChartOutputStudent($student));
        # output it

        $Count++;
        if($Count % 5 == 0) {
            $r->print("</pre>\n<pre>");
        }

        $r->rflush();
    }
    $r->print('</pre>'."\n"); 
    my $Str;
    return;
}

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

=pod

=item &CreateInterface()

Called by &BuildStudentAssessmentPage to create the top part of the
page which displays the chart.

Inputs: None

Returns:  A string containing the HTML for the headers and top table for 
the chart page.

=cut

#######################################################
#######################################################
sub CreateInterface {
    my $Str = '';
#    $Str .= &CreateLegend();
    $Str .= '<table cellspacing="5">'."\n";
    $Str .= '<tr>';
    $Str .= '<td align="center"><b>Sections</b></td>';
    $Str .= '<td align="center"><b>Student Data</b></td>';
    $Str .= '<td align="center"><b>Sequences and Folders</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::StudentDataSelect('StudentData','multiple',
                                                      5,undef);
    $Str .= '</td><td>'."\n";
    $Str .= &Apache::lonstatistics::MapSelect('Maps','multiple,all',5,
                                              $only_seq_with_assessments);
    $Str .= '</td></tr>'."\n";
    $Str .= '</table>'."\n";
    return $Str;
}


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

=pod

=item Table Output Routines

=over 4

=cut

#######################################################
#######################################################
{
    my $padding;

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

=pod

=item &CreateTableHeadings()

Create HTML for the columns of student data to show.
Called by &BuildStudentAssessmentPage().  Calls
&Apache::lonhtmlcommon::CreateHeadings().

Inputs:

=over 4

=item $cache The ubiquitous cache

=item $spacing A string of spaces

=item $infoKeys Array ref to names of keys to display from the cache 
which describe students

=item $infoHeadings Array ref to headings of columns for student info

=item $sequenceKeys Array ref of names of keys to use to retrieve sequence
data from the cache

=item $sequenceHeadings Array ref of names of sequences used for output.

=back

Returns: A string containing the HTML of the table headings.

=cut

#######################################################
#######################################################
sub CreateTableHeadings {
    #
    $padding = ' 'x3;
    #
    my $Str = '<pre>';
    # First, the @StudentData fields need to be listed
    my @to_show = @Apache::lonstatistics::SelectedStudentData;
    foreach (@to_show) {
        if ($_ eq 'all') {
            @to_show = @Apache::lonstatistics::StudentDataOrder;
            last;
        }
    }
    foreach my $field (@to_show) {
        my $title=$Apache::lonstatistics::StudentData{$field}->{'title'};
        my $base =$Apache::lonstatistics::StudentData{$field}->{'base_width'};
        my $width=$Apache::lonstatistics::StudentData{$field}->{'width'};
        $Str .= $title.' 'x($width-$base).$padding;
    }
    # Now the selected sequences need to be listed
    foreach my $map_symb (@Apache::lonstatistics::SelectedMaps) {
        foreach my $sequence (@Apache::lonstatistics::Sequences) {
            next if ($sequence->{'symb'} ne $map_symb && $map_symb ne 'all');
            next if ($sequence->{'num_assess'} < 1);
            my $title = $sequence->{'title'};
            my $base  = $sequence->{'base_width'};
            my $width = $sequence->{'width'};
            $Str .= $title.' 'x($width-$base).$padding;
        }
    }
    $Str .= 'total';
    $Str .= "</pre>\n";
    return $Str;
}

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

=pod

=item &ChartOutputStudent($student)

Return a line of the chart for a student.

=cut

#######################################################
#######################################################
sub ChartOutputStudent {
    my $student = shift;
    my $Str = '';
    # First, the @StudentData fields need to be listed
    my @to_show = @Apache::lonstatistics::SelectedStudentData;
    foreach (@to_show) {
        if ($_ eq 'all') {
            @to_show = @Apache::lonstatistics::StudentDataOrder;
            last;
        }
    }
    foreach my $field (@to_show) {
        my $title=$student->{$field};
        my $base =scalar(my @Tmp = split(//,$title));
        my $width=$Apache::lonstatistics::StudentData{$field}->{'width'};
        $Str .= $title.' 'x($width-$base).$padding;
    }
    # Get ALL the students data
    my %StudentsData;
    my @tmp = &Apache::loncoursedata::get_current_state
        ($student->{'username'},$student->{'domain'},undef,
         $ENV{'request.course.id'});
    if ((scalar @tmp > 0) && ($tmp[0] !~ /^error:/)) {
        %StudentsData = @tmp;
    }
    if (scalar(@tmp) < 1) {
        $Str .= '<font color="blue">No Course Data</font>'."\n";
        return $Str;
    }
    #
    # By sequence build up the data
    my $studentstats;
    foreach my $map_symb (@Apache::lonstatistics::SelectedMaps) {
        foreach my $seq (@Apache::lonstatistics::Sequences) {
            next if ($map_symb ne $seq->{'symb'} && $map_symb ne 'all');
            next if ($seq->{'num_assess'} < 1);
            my ($performance,$score,$seq_max) =
                &StudentPerformanceOnSequence($student,\%StudentsData,
                                              $seq,'linkify');
            $Str .= $performance.$padding;
            $studentstats->{$seq->{'symb'}}->{'score'}= $score;
            $studentstats->{$seq->{'symb'}}->{'max'}  = $seq_max;
        }
    }
    #
    # Total it up and store the statistics info.
    my ($score,$max) = (0,0);
    while (my ($symb,$seq_stats) = each (%{$studentstats})) {
        $Statistics->{$symb}->{'score'} += $seq_stats->{'score'};
        $Statistics->{$symb}->{'max'}   += $seq_stats->{'max'};
        $score += $seq_stats->{'score'};
        $max   += $seq_stats->{'max'};
    }
    my $scorelength = scalar(my @tmp1 = split(//,$score));
    my $maxlength   = scalar(my @tmp2 = split(//,$max));
    $Str .= ' '.' 'x($maxlength-$scorelength).$score.'/'.$max;
    $Str .= " \n";
    return $Str;
}    

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

=pod

=back

=cut

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

}

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

=pod

=item &StudentPerformanceOnSequence()

Inputs:

=over 4

=item $student

=item $studentdata Hash ref to all student data

=item $seq Hash ref, the sequence we are working on

=item $links if defined we will output links to each resource.

=back

=cut

#######################################################
#######################################################
sub StudentPerformanceOnSequence {
    my ($student,$studentdata,$seq,$links) = @_;
    my $Str = '';
    my $output_width = 0;
    my ($sum,$max) = (0,0);
    foreach my $resource (@{$seq->{'contents'}}) {
        next if ($resource->{'type'} ne 'assessment');
        my $resource_data = $studentdata->{$resource->{'symb'}};
        my $value = '';
        foreach my $partnum (@{$resource->{'parts'}}) {
            $max++;
            my $symbol = ' '; # default to space
            #
            if (exists($resource_data->{'resource.'.$partnum.'.solved'})) {
                my $status = $resource_data->{'resource.'.$partnum.'.solved'};
                if ($status eq 'correct_by_override') {
                    $symbol = '+';
                    $sum++;
                } elsif ($status eq 'incorrect_by_override') {
                    $symbol = '-';
                } elsif ($status eq 'ungraded_attempted') {
                    $symbol = '#';
                } elsif ($status eq 'incorrect_attempted')  {
                    $symbol = '.';
                } elsif ($status eq 'excused') {
                    $symbol = 'x';
                    $max--;
                } elsif ($status eq 'correct_by_student' &&
                    exists($resource_data->{'resource.'.$partnum.'.tries'})){
                    my $num = $resource_data->{'resource.'.$partnum.'.tries'};
                    if ($num > 9) {
                        $symbol = '*';
                    } elsif ($num > 0) {
                        $symbol = $num;
                    } else {
                        $symbol = ' ';
                    }
                    $sum++;
                } else {
                    $symbol = ' ';
                }
            } else {
                # Unsolved.  Did they try?
                if (exists($resource_data->{'resource.'.$partnum.'.tries'})){
                    $symbol = '.';
                } else {
                    $symbol = ' ';
                }
            }
            #
            $output_width++;
            if (defined($links) && $symbol ne ' ') {
                $symbol = '<a href="/adm/grades'.
                    '?symb='.&Apache::lonnet::escape($resource->{'symb'}).
                        '&student='.$student->{'username'}.
                            '&domain='.$student->{'domain'}.
                                '&command=submission">'.$symbol.'</a>';
            }
            $value .= $symbol;
        }
        $Str .= $value;
    }
    # Put on the totals
    my $ratio = $sum.'/'.$max;
    my $ratio_length = scalar(my @tmp1 = split(//,$ratio));
    # Pad with extra spaces
    my $width = $seq->{'width'};
    $Str .= ' 'x($width-$output_width-$ratio_length).$ratio;
    #
    return ($Str,$sum,$max);
}

#######################################################
#######################################################
sub StudentAverageTotal {
    my ($cache, $students, $sequenceKeys)=@_;
    my $Str = "\n<b>Summary Tables:</b>\n";
    my %Correct = ();
    my $ProblemsSolved = 0;
    my $TotalProblems = 0;
    my $StudentCount = 0;

    foreach my $name (@$students) {
        $StudentCount++;
        foreach my $sequence (@$sequenceKeys) {
            $Correct{$sequence} +=
	       $cache->{$name.':'.$sequence.':problemsCorrect'};
        }
	$ProblemsSolved += $cache->{$name.':problemsSolved'};
        $TotalProblems += $cache->{$name.':totalProblems'};
    }
    if ($StudentCount) { 
        $ProblemsSolved = sprintf( "%.2f", 
                             $ProblemsSolved/$StudentCount);
        $TotalProblems /= $StudentCount;
    } else {
        $ProblemsSolved = 0;
        $TotalProblems  = 0;
    }

    $Str .= '<table border=2 cellspacing="1">'."\n";
    $Str .= '<tr><td><b>Students Count</b></td><td><b>'.
            $StudentCount.'</b></td></tr>'."\n";
    $Str .= '<tr><td><b>Total Problems</b></td><td><b>'.
            $TotalProblems.'</b></td></tr>'."\n";
    $Str .= '<tr><td><b>Average Correct</b></td><td><b>'.
            $ProblemsSolved.'</b></td></tr>'."\n";
    $Str .= '</table>'."\n";

    $Str .= '<table border=2 cellspacing="1">'."\n";
    $Str .= '<tr><th>Title</th><th>Total Problems</th>'.
            '<th>Average Correct</th></tr>'."\n";
    foreach my $S(@$sequenceKeys) {
        my $title=$cache->{$S.':title'};
	#$Str .= $cache->{$S.':problems'};
	#my @problems=split(':', $cache->{$S.':problems'});
	#my $pCount=scalar @problems;
	my $pCount=MaxSeqPr($cache,@$students[0],$S);
        my $crr;
	if ($StudentCount) {
            $crr=sprintf( "%.2f", $Correct{$S}/$StudentCount );
        } else {
            $crr="0.00";
        }
        $Str .= '<tr><td>'.$title.
                '</td><td align=center>'.$pCount.
                '</td><td align=center>'.$crr.
                '</td></tr>'."\n";
    }

    $Str .= '</table>'."\n";

    return $Str;
}

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

=pod

=item &CreateLegend()

This function returns a formatted string containing the legend for the
chart.  The legend describes the symbols used to represent grades for
problems.

=cut

#######################################################
#######################################################
sub CreateLegend {
    my $Str = "<p><pre>".
              "   1  correct by student in 1 try\n".
              "   7  correct by student in 7 tries\n".
              "   *  correct by student in more than 9 tries\n".
	      "   +  correct by hand grading or override\n".
              "   -  incorrect by override\n".
	      "   .  incorrect attempted\n".
	      "   #  ungraded attempted\n".
              "      not attempted (blank field)\n".
	      "   x  excused".
              "</pre><p>";
    return $Str;
}

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

=pod 

=back

=cut

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

1;

__END__

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