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, 4 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.

    1: # The LearningOnline Network with CAPA
    2: #
    3: # $Id: lonstudentassessment.pm,v 1.30 2003/02/28 21:19:00 matthew Exp $
    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
   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: #######################################################
   49: 
   50: package Apache::lonstudentassessment;
   51: 
   52: use strict;
   53: use Apache::lonstatistics;
   54: use Apache::lonhtmlcommon;
   55: use Apache::loncoursedata;
   56: use Apache::lonnet; # for logging porpoises
   57: use GDBM_File;
   58: 
   59: my $Statistics;
   60: 
   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
   84: 
   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: #######################################################
   98: sub BuildStudentAssessmentPage {
   99:     my ($r,$c)=@_;
  100:     undef($Statistics);
  101:     
  102:     #
  103:     $r->print(&CreateInterface());
  104:     $r->rflush();
  105:     #
  106:     $r->print(&CreateTableHeadings());
  107:     if($c->aborted()) {  return ; }
  108:     
  109:     my $Count = 0;
  110:     $r->print('<pre>'."\n");
  111:     foreach my $student (@Apache::lonstatistics::Students) {
  112:         if($c->aborted()) { return ; }
  113:         $r->print(&ChartOutputStudent($student));
  114:         # output it
  115: 
  116:         $Count++;
  117:         if($Count % 5 == 0) {
  118:             $r->print("</pre>\n<pre>");
  119:         }
  120: 
  121:         $r->rflush();
  122:     }
  123:     $r->print('</pre>'."\n"); 
  124:     my $Str;
  125:     return;
  126: }
  127: 
  128: #######################################################
  129: #######################################################
  130: 
  131: =pod
  132: 
  133: =item &CreateInterface()
  134: 
  135: Called by &BuildStudentAssessmentPage to create the top part of the
  136: page which displays the chart.
  137: 
  138: Inputs: None
  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: #######################################################
  147: sub CreateInterface {
  148:     my $Str = '';
  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:     #
  157:     $Str .= '<tr><td align="center">'."\n";
  158:     $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5);
  159:     $Str .= '</td><td align="center">';
  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);
  170:     $Str .= '</td><td>'."\n";
  171:     $Str .= &Apache::lonstatistics::MapSelect('Maps','multiple,all',5,
  172:                                               $only_seq_with_assessments);
  173:     $Str .= '</td></tr>'."\n";
  174:     $Str .= '</table>'."\n";
  175:     return $Str;
  176: }
  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: 
  195: #######################################################
  196: #######################################################
  197: 
  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: #######################################################
  232: sub CreateTableHeadings {
  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)
  273: 
  274: Return a line of the chart for a student.
  275: 
  276: =cut
  277: 
  278: #######################################################
  279: #######################################################
  280: sub ChartOutputStudent {
  281:     my $student = shift;
  282:     my $Str = '';
  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: }    
  339: 
  340: #######################################################
  341: #######################################################
  342: 
  343: =pod
  344: 
  345: =back
  346: 
  347: =cut
  348: 
  349: #######################################################
  350: #######################################################
  351: 
  352: }
  353: 
  354: #######################################################
  355: #######################################################
  356: 
  357: =pod
  358: 
  359: =item &StudentPerformanceOnSequence()
  360: 
  361: Inputs:
  362: 
  363: =over 4
  364: 
  365: =item $student
  366: 
  367: =item $studentdata Hash ref to all student data
  368: 
  369: =item $seq Hash ref, the sequence we are working on
  370: 
  371: =item $links if defined we will output links to each resource.
  372: 
  373: =back
  374: 
  375: =cut
  376: 
  377: #######################################################
  378: #######################################################
  379: sub StudentPerformanceOnSequence {
  380:     my ($student,$studentdata,$seq,$links) = @_;
  381:     my $Str = '';
  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 = ' ';
  419:                 }
  420:             } else {
  421:                 # Unsolved.  Did they try?
  422:                 if (exists($resource_data->{'resource.'.$partnum.'.tries'})){
  423:                     $symbol = '.';
  424:                 } else {
  425:                     $symbol = ' ';
  426:                 }
  427:             }
  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;
  438:         }
  439:         $Str .= $value;
  440:     }
  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);
  449: }
  450: 
  451: #######################################################
  452: #######################################################
  453: sub StudentAverageTotal {
  454:     my ($cache, $students, $sequenceKeys)=@_;
  455:     my $Str = "\n<b>Summary Tables:</b>\n";
  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) {
  464:             $Correct{$sequence} +=
  465: 	       $cache->{$name.':'.$sequence.':problemsCorrect'};
  466:         }
  467: 	$ProblemsSolved += $cache->{$name.':problemsSolved'};
  468:         $TotalProblems += $cache->{$name.':totalProblems'};
  469:     }
  470:     if ($StudentCount) { 
  471:         $ProblemsSolved = sprintf( "%.2f", 
  472:                              $ProblemsSolved/$StudentCount);
  473:         $TotalProblems /= $StudentCount;
  474:     } else {
  475:         $ProblemsSolved = 0;
  476:         $TotalProblems  = 0;
  477:     }
  478: 
  479:     $Str .= '<table border=2 cellspacing="1">'."\n";
  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: 
  488:     $Str .= '<table border=2 cellspacing="1">'."\n";
  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'};
  494: 	#my @problems=split(':', $cache->{$S.':problems'});
  495: 	#my $pCount=scalar @problems;
  496: 	my $pCount=MaxSeqPr($cache,@$students[0],$S);
  497:         my $crr;
  498: 	if ($StudentCount) {
  499:             $crr=sprintf( "%.2f", $Correct{$S}/$StudentCount );
  500:         } else {
  501:             $crr="0.00";
  502:         }
  503:         $Str .= '<tr><td>'.$title.
  504:                 '</td><td align=center>'.$pCount.
  505:                 '</td><td align=center>'.$crr.
  506:                 '</td></tr>'."\n";
  507:     }
  508: 
  509:     $Str .= '</table>'."\n";
  510: 
  511:     return $Str;
  512: }
  513: 
  514: #######################################################
  515: #######################################################
  516: 
  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: 
  527: #######################################################
  528: #######################################################
  529: sub CreateLegend {
  530:     my $Str = "<p><pre>".
  531:               "   1  correct by student in 1 try\n".
  532:               "   7  correct by student in 7 tries\n".
  533:               "   *  correct by student in more than 9 tries\n".
  534: 	      "   +  correct by hand grading or override\n".
  535:               "   -  incorrect by override\n".
  536: 	      "   .  incorrect attempted\n".
  537: 	      "   #  ungraded attempted\n".
  538:               "      not attempted (blank field)\n".
  539: 	      "   x  excused".
  540:               "</pre><p>";
  541:     return $Str;
  542: }
  543: 
  544: #######################################################
  545: #######################################################
  546: 
  547: =pod 
  548: 
  549: =back
  550: 
  551: =cut
  552: 
  553: #######################################################
  554: #######################################################
  555: 
  556: 1;
  557: 
  558: __END__

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