Annotation of loncom/interface/statistics/lonstudentassessment.pm, revision 1.30

1.1       stredwic    1: # The LearningOnline Network with CAPA
                      2: #
1.30    ! matthew     3: # $Id: lonstudentassessment.pm,v 1.29 2003/02/25 20:47:47 matthew Exp $
1.1       stredwic    4: #
                      5: # Copyright Michigan State University Board of Trustees
                      6: #
                      7: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
                      8: # LON-CAPA is free software; you can redistribute it and/or modify
                      9: # it under the terms of the GNU General Public License as published by
                     10: # the Free Software Foundation; either version 2 of the License, or
                     11: # (at your option) any later version.
                     12: #
                     13: # LON-CAPA is distributed in the hope that it will be useful,
                     14: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     15: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     16: # GNU General Public License for more details.
                     17: #
                     18: # You should have received a copy of the GNU General Public License
                     19: # along with LON-CAPA; if not, write to the Free Software
                     20: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
                     21: #
                     22: # /home/httpd/html/adm/gpl.txt
                     23: #
                     24: # http://www.lon-capa.org/
                     25: #
                     26: # (Navigate problems for statistical reports
1.28      matthew    27: #
                     28: #######################################################
                     29: #######################################################
                     30: 
                     31: =pod
                     32: 
                     33: =head1 NAME
                     34: 
                     35: lonstudentassessment
                     36: 
                     37: =head1 SYNOPSIS
                     38: 
                     39: Presents assessment data about a student or a group of students.
                     40: 
                     41: =head1 Subroutines
                     42: 
                     43: =over 4 
                     44: 
                     45: =cut
                     46: 
                     47: #######################################################
                     48: #######################################################
1.1       stredwic   49: 
1.21      minaeibi   50: package Apache::lonstudentassessment;
1.1       stredwic   51: 
                     52: use strict;
1.28      matthew    53: use Apache::lonstatistics;
1.1       stredwic   54: use Apache::lonhtmlcommon;
                     55: use Apache::loncoursedata;
1.28      matthew    56: use Apache::lonnet; # for logging porpoises
1.1       stredwic   57: use GDBM_File;
                     58: 
1.30    ! matthew    59: my $Statistics;
        !            60: 
1.28      matthew    61: #######################################################
                     62: #######################################################
                     63: 
                     64: =pod
                     65: 
                     66: =item &BuildStudentAssessmentPage()
                     67: 
                     68: Inputs: 
                     69: 
                     70: =over 4
                     71: 
                     72: =item $cacheDB The name of the cache file used to store student data
                     73: 
                     74: =item $students Array ref containing the name(s) of the students 
                     75: selected for display
                     76: 
                     77: =item $courseID The ID of the course
                     78: 
                     79: =item $formName The name of the html form - 'Statistics'
                     80: 
                     81: =item $headings Array ref of headings to show
                     82: 
                     83: =item $spacing A string of spaces
1.4       stredwic   84: 
1.28      matthew    85: =item $studentInformation Array ref of possible headings for student info
                     86: ('fullname','section',...)
                     87: 
                     88: =item $r Apache Request
                     89: 
                     90: =item $c Apache Connection 
                     91: 
                     92: =back
                     93: 
                     94: =cut
                     95: 
                     96: #######################################################
                     97: #######################################################
1.1       stredwic   98: sub BuildStudentAssessmentPage {
1.30    ! matthew    99:     my ($r,$c)=@_;
        !           100:     undef($Statistics);
        !           101:     
        !           102:     #
        !           103:     $r->print(&CreateInterface());
1.7       stredwic  104:     $r->rflush();
1.30    ! matthew   105:     #
        !           106:     $r->print(&CreateTableHeadings());
        !           107:     if($c->aborted()) {  return ; }
        !           108:     
1.24      minaeibi  109:     my $Count = 0;
1.2       stredwic  110:     $r->print('<pre>'."\n");
1.30    ! matthew   111:     foreach my $student (@Apache::lonstatistics::Students) {
        !           112:         if($c->aborted()) { return ; }
        !           113:         $r->print(&ChartOutputStudent($student));
        !           114:         # output it
1.8       stredwic  115: 
1.30    ! matthew   116:         $Count++;
        !           117:         if($Count % 5 == 0) {
        !           118:             $r->print("</pre>\n<pre>");
1.1       stredwic  119:         }
1.30    ! matthew   120: 
        !           121:         $r->rflush();
1.1       stredwic  122:     }
1.26      minaeibi  123:     $r->print('</pre>'."\n"); 
1.30    ! matthew   124:     my $Str;
1.2       stredwic  125:     return;
                    126: }
1.30    ! matthew   127: 
1.28      matthew   128: #######################################################
                    129: #######################################################
                    130: 
                    131: =pod
1.2       stredwic  132: 
1.28      matthew   133: =item &CreateInterface()
1.21      minaeibi  134: 
1.28      matthew   135: Called by &BuildStudentAssessmentPage to create the top part of the
                    136: page which displays the chart.
                    137: 
1.30    ! matthew   138: Inputs: None
1.28      matthew   139: 
                    140: Returns:  A string containing the HTML for the headers and top table for 
                    141: the chart page.
                    142: 
                    143: =cut
                    144: 
                    145: #######################################################
                    146: #######################################################
1.2       stredwic  147: sub CreateInterface {
1.4       stredwic  148:     my $Str = '';
1.30    ! matthew   149: #    $Str .= &CreateLegend();
        !           150:     $Str .= '<table cellspacing="5">'."\n";
        !           151:     $Str .= '<tr>';
        !           152:     $Str .= '<td align="center"><b>Sections</b></td>';
        !           153:     $Str .= '<td align="center"><b>Student Data</b></td>';
        !           154:     $Str .= '<td align="center"><b>Sequences and Folders</b></td>';
        !           155:     $Str .= '</tr>'."\n";
        !           156:     #
1.4       stredwic  157:     $Str .= '<tr><td align="center">'."\n";
1.29      matthew   158:     $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5);
1.4       stredwic  159:     $Str .= '</td><td align="center">';
1.30    ! matthew   160:     my $only_seq_with_assessments = sub { 
        !           161:         my $s=shift;
        !           162:         if ($s->{'num_assess'} < 1) { 
        !           163:             return 0;
        !           164:         } else { 
        !           165:             return 1;
        !           166:         }
        !           167:     };
        !           168:     $Str .= &Apache::lonstatistics::StudentDataSelect('StudentData','multiple',
        !           169:                                                       5,undef);
1.4       stredwic  170:     $Str .= '</td><td>'."\n";
1.30    ! matthew   171:     $Str .= &Apache::lonstatistics::MapSelect('Maps','multiple,all',5,
        !           172:                                               $only_seq_with_assessments);
        !           173:     $Str .= '</td></tr>'."\n";
        !           174:     $Str .= '</table>'."\n";
1.4       stredwic  175:     return $Str;
1.1       stredwic  176: }
1.30    ! matthew   177: 
        !           178: 
        !           179: #######################################################
        !           180: #######################################################
        !           181: 
        !           182: =pod
        !           183: 
        !           184: =item Table Output Routines
        !           185: 
        !           186: =over 4
        !           187: 
        !           188: =cut
        !           189: 
        !           190: #######################################################
        !           191: #######################################################
        !           192: {
        !           193:     my $padding;
        !           194: 
1.28      matthew   195: #######################################################
                    196: #######################################################
1.1       stredwic  197: 
1.28      matthew   198: =pod
                    199: 
                    200: =item &CreateTableHeadings()
                    201: 
                    202: Create HTML for the columns of student data to show.
                    203: Called by &BuildStudentAssessmentPage().  Calls
                    204: &Apache::lonhtmlcommon::CreateHeadings().
                    205: 
                    206: Inputs:
                    207: 
                    208: =over 4
                    209: 
                    210: =item $cache The ubiquitous cache
                    211: 
                    212: =item $spacing A string of spaces
                    213: 
                    214: =item $infoKeys Array ref to names of keys to display from the cache 
                    215: which describe students
                    216: 
                    217: =item $infoHeadings Array ref to headings of columns for student info
                    218: 
                    219: =item $sequenceKeys Array ref of names of keys to use to retrieve sequence
                    220: data from the cache
                    221: 
                    222: =item $sequenceHeadings Array ref of names of sequences used for output.
                    223: 
                    224: =back
                    225: 
                    226: Returns: A string containing the HTML of the table headings.
                    227: 
                    228: =cut
                    229: 
                    230: #######################################################
                    231: #######################################################
1.2       stredwic  232: sub CreateTableHeadings {
1.30    ! matthew   233:     #
        !           234:     $padding = ' 'x3;
        !           235:     #
        !           236:     my $Str = '<pre>';
        !           237:     # First, the @StudentData fields need to be listed
        !           238:     my @to_show = @Apache::lonstatistics::SelectedStudentData;
        !           239:     foreach (@to_show) {
        !           240:         if ($_ eq 'all') {
        !           241:             @to_show = @Apache::lonstatistics::StudentDataOrder;
        !           242:             last;
        !           243:         }
        !           244:     }
        !           245:     foreach my $field (@to_show) {
        !           246:         my $title=$Apache::lonstatistics::StudentData{$field}->{'title'};
        !           247:         my $base =$Apache::lonstatistics::StudentData{$field}->{'base_width'};
        !           248:         my $width=$Apache::lonstatistics::StudentData{$field}->{'width'};
        !           249:         $Str .= $title.' 'x($width-$base).$padding;
        !           250:     }
        !           251:     # Now the selected sequences need to be listed
        !           252:     foreach my $map_symb (@Apache::lonstatistics::SelectedMaps) {
        !           253:         foreach my $sequence (@Apache::lonstatistics::Sequences) {
        !           254:             next if ($sequence->{'symb'} ne $map_symb && $map_symb ne 'all');
        !           255:             next if ($sequence->{'num_assess'} < 1);
        !           256:             my $title = $sequence->{'title'};
        !           257:             my $base  = $sequence->{'base_width'};
        !           258:             my $width = $sequence->{'width'};
        !           259:             $Str .= $title.' 'x($width-$base).$padding;
        !           260:         }
        !           261:     }
        !           262:     $Str .= 'total';
        !           263:     $Str .= "</pre>\n";
        !           264:     return $Str;
        !           265: }
        !           266: 
        !           267: #######################################################
        !           268: #######################################################
        !           269: 
        !           270: =pod
        !           271: 
        !           272: =item &ChartOutputStudent($student)
1.2       stredwic  273: 
1.30    ! matthew   274: Return a line of the chart for a student.
        !           275: 
        !           276: =cut
        !           277: 
        !           278: #######################################################
        !           279: #######################################################
        !           280: sub ChartOutputStudent {
        !           281:     my $student = shift;
1.2       stredwic  282:     my $Str = '';
1.30    ! matthew   283:     # First, the @StudentData fields need to be listed
        !           284:     my @to_show = @Apache::lonstatistics::SelectedStudentData;
        !           285:     foreach (@to_show) {
        !           286:         if ($_ eq 'all') {
        !           287:             @to_show = @Apache::lonstatistics::StudentDataOrder;
        !           288:             last;
        !           289:         }
        !           290:     }
        !           291:     foreach my $field (@to_show) {
        !           292:         my $title=$student->{$field};
        !           293:         my $base =scalar(my @Tmp = split(//,$title));
        !           294:         my $width=$Apache::lonstatistics::StudentData{$field}->{'width'};
        !           295:         $Str .= $title.' 'x($width-$base).$padding;
        !           296:     }
        !           297:     # Get ALL the students data
        !           298:     my %StudentsData;
        !           299:     my @tmp = &Apache::loncoursedata::get_current_state
        !           300:         ($student->{'username'},$student->{'domain'},undef,
        !           301:          $ENV{'request.course.id'});
        !           302:     if ((scalar @tmp > 0) && ($tmp[0] !~ /^error:/)) {
        !           303:         %StudentsData = @tmp;
        !           304:     }
        !           305:     if (scalar(@tmp) < 1) {
        !           306:         $Str .= '<font color="blue">No Course Data</font>'."\n";
        !           307:         return $Str;
        !           308:     }
        !           309:     #
        !           310:     # By sequence build up the data
        !           311:     my $studentstats;
        !           312:     foreach my $map_symb (@Apache::lonstatistics::SelectedMaps) {
        !           313:         foreach my $seq (@Apache::lonstatistics::Sequences) {
        !           314:             next if ($map_symb ne $seq->{'symb'} && $map_symb ne 'all');
        !           315:             next if ($seq->{'num_assess'} < 1);
        !           316:             my ($performance,$score,$seq_max) =
        !           317:                 &StudentPerformanceOnSequence($student,\%StudentsData,
        !           318:                                               $seq,'linkify');
        !           319:             $Str .= $performance.$padding;
        !           320:             $studentstats->{$seq->{'symb'}}->{'score'}= $score;
        !           321:             $studentstats->{$seq->{'symb'}}->{'max'}  = $seq_max;
        !           322:         }
        !           323:     }
        !           324:     #
        !           325:     # Total it up and store the statistics info.
        !           326:     my ($score,$max) = (0,0);
        !           327:     while (my ($symb,$seq_stats) = each (%{$studentstats})) {
        !           328:         $Statistics->{$symb}->{'score'} += $seq_stats->{'score'};
        !           329:         $Statistics->{$symb}->{'max'}   += $seq_stats->{'max'};
        !           330:         $score += $seq_stats->{'score'};
        !           331:         $max   += $seq_stats->{'max'};
        !           332:     }
        !           333:     my $scorelength = scalar(my @tmp1 = split(//,$score));
        !           334:     my $maxlength   = scalar(my @tmp2 = split(//,$max));
        !           335:     $Str .= ' '.' 'x($maxlength-$scorelength).$score.'/'.$max;
        !           336:     $Str .= " \n";
        !           337:     return $Str;
        !           338: }    
1.2       stredwic  339: 
1.30    ! matthew   340: #######################################################
        !           341: #######################################################
        !           342: 
        !           343: =pod
        !           344: 
        !           345: =back
        !           346: 
        !           347: =cut
        !           348: 
        !           349: #######################################################
        !           350: #######################################################
1.2       stredwic  351: 
                    352: }
                    353: 
1.28      matthew   354: #######################################################
                    355: #######################################################
                    356: 
1.2       stredwic  357: =pod
                    358: 
1.30    ! matthew   359: =item &StudentPerformanceOnSequence()
1.2       stredwic  360: 
1.30    ! matthew   361: Inputs:
1.2       stredwic  362: 
                    363: =over 4
                    364: 
1.30    ! matthew   365: =item $student
1.28      matthew   366: 
1.30    ! matthew   367: =item $studentdata Hash ref to all student data
1.2       stredwic  368: 
1.30    ! matthew   369: =item $seq Hash ref, the sequence we are working on
1.2       stredwic  370: 
1.30    ! matthew   371: =item $links if defined we will output links to each resource.
1.2       stredwic  372: 
1.28      matthew   373: =back
1.2       stredwic  374: 
                    375: =cut
1.1       stredwic  376: 
1.28      matthew   377: #######################################################
                    378: #######################################################
1.30    ! matthew   379: sub StudentPerformanceOnSequence {
        !           380:     my ($student,$studentdata,$seq,$links) = @_;
1.1       stredwic  381:     my $Str = '';
1.30    ! matthew   382:     my $output_width = 0;
        !           383:     my ($sum,$max) = (0,0);
        !           384:     foreach my $resource (@{$seq->{'contents'}}) {
        !           385:         next if ($resource->{'type'} ne 'assessment');
        !           386:         my $resource_data = $studentdata->{$resource->{'symb'}};
        !           387:         my $value = '';
        !           388:         foreach my $partnum (@{$resource->{'parts'}}) {
        !           389:             $max++;
        !           390:             my $symbol = ' '; # default to space
        !           391:             #
        !           392:             if (exists($resource_data->{'resource.'.$partnum.'.solved'})) {
        !           393:                 my $status = $resource_data->{'resource.'.$partnum.'.solved'};
        !           394:                 if ($status eq 'correct_by_override') {
        !           395:                     $symbol = '+';
        !           396:                     $sum++;
        !           397:                 } elsif ($status eq 'incorrect_by_override') {
        !           398:                     $symbol = '-';
        !           399:                 } elsif ($status eq 'ungraded_attempted') {
        !           400:                     $symbol = '#';
        !           401:                 } elsif ($status eq 'incorrect_attempted')  {
        !           402:                     $symbol = '.';
        !           403:                 } elsif ($status eq 'excused') {
        !           404:                     $symbol = 'x';
        !           405:                     $max--;
        !           406:                 } elsif ($status eq 'correct_by_student' &&
        !           407:                     exists($resource_data->{'resource.'.$partnum.'.tries'})){
        !           408:                     my $num = $resource_data->{'resource.'.$partnum.'.tries'};
        !           409:                     if ($num > 9) {
        !           410:                         $symbol = '*';
        !           411:                     } elsif ($num > 0) {
        !           412:                         $symbol = $num;
        !           413:                     } else {
        !           414:                         $symbol = ' ';
        !           415:                     }
        !           416:                     $sum++;
        !           417:                 } else {
        !           418:                     $symbol = ' ';
1.2       stredwic  419:                 }
1.30    ! matthew   420:             } else {
        !           421:                 # Unsolved.  Did they try?
        !           422:                 if (exists($resource_data->{'resource.'.$partnum.'.tries'})){
        !           423:                     $symbol = '.';
        !           424:                 } else {
        !           425:                     $symbol = ' ';
1.18      matthew   426:                 }
1.2       stredwic  427:             }
1.30    ! matthew   428:             #
        !           429:             $output_width++;
        !           430:             if (defined($links) && $symbol ne ' ') {
        !           431:                 $symbol = '<a href="/adm/grades'.
        !           432:                     '?symb='.&Apache::lonnet::escape($resource->{'symb'}).
        !           433:                         '&student='.$student->{'username'}.
        !           434:                             '&domain='.$student->{'domain'}.
        !           435:                                 '&command=submission">'.$symbol.'</a>';
        !           436:             }
        !           437:             $value .= $symbol;
1.2       stredwic  438:         }
1.30    ! matthew   439:         $Str .= $value;
1.17      minaeibi  440:     }
1.30    ! matthew   441:     # Put on the totals
        !           442:     my $ratio = $sum.'/'.$max;
        !           443:     my $ratio_length = scalar(my @tmp1 = split(//,$ratio));
        !           444:     # Pad with extra spaces
        !           445:     my $width = $seq->{'width'};
        !           446:     $Str .= ' 'x($width-$output_width-$ratio_length).$ratio;
        !           447:     #
        !           448:     return ($Str,$sum,$max);
1.17      minaeibi  449: }
                    450: 
1.28      matthew   451: #######################################################
                    452: #######################################################
1.17      minaeibi  453: sub StudentAverageTotal {
1.21      minaeibi  454:     my ($cache, $students, $sequenceKeys)=@_;
1.23      minaeibi  455:     my $Str = "\n<b>Summary Tables:</b>\n";
1.21      minaeibi  456:     my %Correct = ();
                    457:     my $ProblemsSolved = 0;
                    458:     my $TotalProblems = 0;
                    459:     my $StudentCount = 0;
                    460: 
                    461:     foreach my $name (@$students) {
                    462:         $StudentCount++;
                    463:         foreach my $sequence (@$sequenceKeys) {
1.23      minaeibi  464:             $Correct{$sequence} +=
                    465: 	       $cache->{$name.':'.$sequence.':problemsCorrect'};
1.17      minaeibi  466:         }
1.21      minaeibi  467: 	$ProblemsSolved += $cache->{$name.':problemsSolved'};
                    468:         $TotalProblems += $cache->{$name.':totalProblems'};
1.1       stredwic  469:     }
1.25      matthew   470:     if ($StudentCount) { 
1.27      minaeibi  471:         $ProblemsSolved = sprintf( "%.2f", 
                    472:                              $ProblemsSolved/$StudentCount);
1.25      matthew   473:         $TotalProblems /= $StudentCount;
                    474:     } else {
                    475:         $ProblemsSolved = 0;
                    476:         $TotalProblems  = 0;
                    477:     }
1.27      minaeibi  478: 
1.24      minaeibi  479:     $Str .= '<table border=2 cellspacing="1">'."\n";
1.23      minaeibi  480:     $Str .= '<tr><td><b>Students Count</b></td><td><b>'.
                    481:             $StudentCount.'</b></td></tr>'."\n";
                    482:     $Str .= '<tr><td><b>Total Problems</b></td><td><b>'.
                    483:             $TotalProblems.'</b></td></tr>'."\n";
                    484:     $Str .= '<tr><td><b>Average Correct</b></td><td><b>'.
                    485:             $ProblemsSolved.'</b></td></tr>'."\n";
                    486:     $Str .= '</table>'."\n";
                    487: 
1.24      minaeibi  488:     $Str .= '<table border=2 cellspacing="1">'."\n";
1.23      minaeibi  489:     $Str .= '<tr><th>Title</th><th>Total Problems</th>'.
                    490:             '<th>Average Correct</th></tr>'."\n";
                    491:     foreach my $S(@$sequenceKeys) {
                    492:         my $title=$cache->{$S.':title'};
                    493: 	#$Str .= $cache->{$S.':problems'};
1.24      minaeibi  494: 	#my @problems=split(':', $cache->{$S.':problems'});
1.23      minaeibi  495: 	#my $pCount=scalar @problems;
                    496: 	my $pCount=MaxSeqPr($cache,@$students[0],$S);
1.25      matthew   497:         my $crr;
                    498: 	if ($StudentCount) {
                    499:             $crr=sprintf( "%.2f", $Correct{$S}/$StudentCount );
                    500:         } else {
                    501:             $crr="0.00";
                    502:         }
1.23      minaeibi  503:         $Str .= '<tr><td>'.$title.
                    504:                 '</td><td align=center>'.$pCount.
                    505:                 '</td><td align=center>'.$crr.
                    506:                 '</td></tr>'."\n";
1.10      stredwic  507:     }
1.1       stredwic  508: 
1.23      minaeibi  509:     $Str .= '</table>'."\n";
                    510: 
1.1       stredwic  511:     return $Str;
                    512: }
1.23      minaeibi  513: 
1.28      matthew   514: #######################################################
                    515: #######################################################
1.23      minaeibi  516: 
1.2       stredwic  517: =pod
                    518: 
                    519: =item &CreateLegend()
                    520: 
                    521: This function returns a formatted string containing the legend for the
                    522: chart.  The legend describes the symbols used to represent grades for
                    523: problems.
                    524: 
                    525: =cut
                    526: 
1.28      matthew   527: #######################################################
                    528: #######################################################
1.2       stredwic  529: sub CreateLegend {
                    530:     my $Str = "<p><pre>".
1.13      minaeibi  531:               "   1  correct by student in 1 try\n".
                    532:               "   7  correct by student in 7 tries\n".
1.12      minaeibi  533:               "   *  correct by student in more than 9 tries\n".
1.20      minaeibi  534: 	      "   +  correct by hand grading or override\n".
1.12      minaeibi  535:               "   -  incorrect by override\n".
                    536: 	      "   .  incorrect attempted\n".
                    537: 	      "   #  ungraded attempted\n".
1.13      minaeibi  538:               "      not attempted (blank field)\n".
1.12      minaeibi  539: 	      "   x  excused".
1.17      minaeibi  540:               "</pre><p>";
1.2       stredwic  541:     return $Str;
                    542: }
                    543: 
1.28      matthew   544: #######################################################
                    545: #######################################################
                    546: 
1.30    ! matthew   547: =pod 
1.2       stredwic  548: 
                    549: =back
                    550: 
                    551: =cut
                    552: 
1.28      matthew   553: #######################################################
                    554: #######################################################
1.2       stredwic  555: 
1.28      matthew   556: 1;
1.2       stredwic  557: 
1.1       stredwic  558: __END__

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