# 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('
'."\n");
    foreach my $student (@Apache::lonstatistics::Students) {
        if($c->aborted()) { return ; }
        $r->print(&ChartOutputStudent($student));
        # output it

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

        $r->rflush();
    }
    $r->print('
'."\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 .= ''."\n"; $Str .= ''; $Str .= ''; $Str .= ''; $Str .= ''; $Str .= ''."\n"; # $Str .= ''."\n"; $Str .= '
SectionsStudent DataSequences and Folders
'."\n"; $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5); $Str .= ''; 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 .= ''."\n"; $Str .= &Apache::lonstatistics::MapSelect('Maps','multiple,all',5, $only_seq_with_assessments); $Str .= '
'."\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 = '
';
    # 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 .= "
\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 .= 'No Course Data'."\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 = ''.$symbol.''; } $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 = "\nSummary Tables:\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 .= ''."\n"; $Str .= ''."\n"; $Str .= ''."\n"; $Str .= ''."\n"; $Str .= '
Students Count'. $StudentCount.'
Total Problems'. $TotalProblems.'
Average Correct'. $ProblemsSolved.'
'."\n"; $Str .= ''."\n"; $Str .= ''. ''."\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 .= ''."\n"; } $Str .= '
TitleTotal ProblemsAverage Correct
'.$title. ''.$pCount. ''.$crr. '
'."\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 = "

".
              "   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".
              "

"; return $Str; } ####################################################### ####################################################### =pod =back =cut ####################################################### ####################################################### 1; __END__