File:  [LON-CAPA] / loncom / interface / statistics / lonstudentassessment.pm
Revision 1.29: download - view: text, annotated - select for diffs
Tue Feb 25 20:47:47 2003 UTC (21 years, 4 months ago) by matthew
Branches: MAIN
CVS tags: HEAD
lonstatistics.pm:
   POD cleanups
   @SelectedSections is set when we read the classlist.
   Added variables $top_map, @Sequences, and @Assessments.  These are
      initialized by &PrepareCourseData($r).
   Added &PrepareCourseData($r) which is essentially a wrapper for
      &Apache::loncoursedata::get_sequence_assessment_data().
   Added &MapSelect() to output a <select> box for sequences.  Not tested.
   Added &SectionSelect(..) to output a <select> box for sections.  Tested.
   Added &DisplayClasslist($r) which displays a table of the current classlist
      that is sortable by each column.  Does not bother with 'update time' as
      this should not be an issue for the user.  Tested.
   Added call to &PrepareCourseData by the handler.
lonpercentage.pm, lonproblemanalysis.pm, lonproblemstatistics.pm,
lonstudentassessment.pm: modified to call &Apache::lonstatistics::SectionSelect
   instead of the method in lonhtmlcommon.pm.

    1: # The LearningOnline Network with CAPA
    2: #
    3: # $Id: lonstudentassessment.pm,v 1.29 2003/02/25 20:47:47 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: #######################################################
   60: #######################################################
   61: 
   62: =pod
   63: 
   64: =item &BuildStudentAssessmentPage()
   65: 
   66: Inputs: 
   67: 
   68: =over 4
   69: 
   70: =item $cacheDB The name of the cache file used to store student data
   71: 
   72: =item $students Array ref containing the name(s) of the students 
   73: selected for display
   74: 
   75: =item $courseID The ID of the course
   76: 
   77: =item $formName The name of the html form - 'Statistics'
   78: 
   79: =item $headings Array ref of headings to show
   80: 
   81: =item $spacing A string of spaces
   82: 
   83: =item $studentInformation Array ref of possible headings for student info
   84: ('fullname','section',...)
   85: 
   86: =item $r Apache Request
   87: 
   88: =item $c Apache Connection 
   89: 
   90: =back
   91: 
   92: =cut
   93: 
   94: #######################################################
   95: #######################################################
   96: sub BuildStudentAssessmentPage {
   97:     my ($cacheDB,$students,$courseID,$formName,$headings,$spacing,
   98:         $studentInformation,$r,$c)=@_;
   99:     my %cache;
  100:     unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
  101:         $r->print('<html><body>Unable to tie database.</body></html>');
  102:         return;
  103:     }
  104: 
  105:     # Remove students who don't have the proper section.
  106:     my @sectionsSelected = split(':',$cache{'sectionsSelected'});
  107:     for(my $studentIndex=((scalar @$students)-1); $studentIndex>=0;
  108:         $studentIndex--) {
  109:         my $value = $cache{$students->[$studentIndex].':section'};
  110:         my $found = 0;
  111:         foreach (@sectionsSelected) {
  112:             if($_ eq 'none') {
  113:                 if($value eq '' || !defined($value) || $value eq ' ') {
  114:                     $found = 1;
  115:                     last;
  116:                 }
  117:             } else {
  118:                 if($value eq $_) {
  119:                     $found = 1;
  120:                     last;
  121:                 }
  122:             }
  123:         }
  124:         if($found == 0) {
  125:             splice(@$students, $studentIndex, 1);
  126:         }
  127:     }
  128:     my ($infoHeadings, $infoKeys, $sequenceHeadings, $sequenceKeys,
  129:         $doNotShow) =
  130:         &ShouldShowColumns(\%cache, $headings, $studentInformation);
  131: 
  132:     my $selectedName = &FindSelectedStudent(\%cache,
  133:                                             $cache{'StudentAssessmentStudent'},
  134:                                             $students);
  135:     $r->print(&CreateInterface(\%cache, $selectedName, $students, $formName,
  136:                                $doNotShow));
  137:     $r->rflush();
  138: 
  139:     my $Str = '';
  140:     if($selectedName eq 'No Student Selected') {
  141: 	$Str .= '<h3><font color=blue>WARNING: ';
  142:         $Str .= 'Please select a student</font></h3>';
  143:         $r->print($Str);
  144:         return;
  145:     }
  146: 
  147:     $r->print(&CreateTableHeadings(\%cache, $spacing, $infoKeys, $infoHeadings,
  148:                                    $sequenceKeys, $sequenceHeadings));
  149:     untie(%cache);
  150:     if($c->aborted()) {  return $Str; }
  151: 
  152:     my $selected=0;
  153:     my $Count = 0;
  154:     $r->print('<pre>'."\n");
  155:     foreach (@$students) {
  156:         if($c->aborted()) { return $Str; }
  157:         next if ($_ ne $selectedName &&
  158:                  $selectedName ne 'All Students');
  159:         $selected = 1;
  160: 
  161:         my @who = ($_);
  162:         next if(&Apache::loncoursedata::DownloadStudentCourseData(\@who, 'true',
  163:                                                              $cacheDB, 'true',
  164:                                                              'false', $courseID,
  165:                                                              $r, $c) ne 'OK');
  166:         next if($c->aborted());
  167: 
  168:         if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
  169:             my @before=();
  170:             my @after=();
  171:             my @updateColumn=();
  172:             my $foundUpdate = 0;
  173:             foreach(@$infoKeys) {
  174:                 if(/updateTime/) {
  175:                     $foundUpdate=1;
  176:                     push(@updateColumn, $_);
  177:                     next;
  178:                 }
  179:                 if($foundUpdate) {
  180:                     push(@after, $_);
  181:                 } else {
  182:                     push(@before, $_);
  183:                 }
  184:             }
  185:             $Count++;
  186: 	    my $out = '';
  187: 	    $out .= sprintf("%3d) ", $Count);
  188:             if($Count % 2) {
  189:                 $out .= '<bgcolor="#FFFFFF">';
  190:             } else {
  191:                 $out .= '<bgcolor="#505050">';
  192:             }
  193: 	    my $displayString = $out.'DISPLAYDATA'.$spacing;
  194: 	    $r->print(&Apache::lonhtmlcommon::FormatStudentInformation(
  195:                                                          \%cache, $_,
  196:                                                          \@before,
  197:                                                          $displayString,
  198:                                                          'preformatted'));
  199: 
  200:             if($foundUpdate) {
  201:                 $displayString = '';
  202:                 $displayString .= '<a href="/adm/statistics?reportSelected=';
  203:                 $displayString .= &Apache::lonnet::escape('Student Assessment');
  204:                 $displayString .= '&download='.$_.'">';
  205:                 $displayString .= 'DISPLAYDATA</a>'.$spacing;
  206:                 $r->print(&Apache::lonhtmlcommon::FormatStudentInformation(
  207:                                                                    \%cache, $_,
  208:                                                                    \@updateColumn,
  209:                                                                    $displayString,
  210:                                                                    'preformatted'));
  211:             }
  212: 
  213:             $displayString = 'DISPLAYDATA'.$spacing;
  214:             $r->print(&Apache::lonhtmlcommon::FormatStudentInformation(
  215:                                                          \%cache, $_,
  216:                                                          \@after,
  217:                                                          $displayString,
  218:                                                          'preformatted'));
  219:             $r->print(&StudentReport(\%cache, $_, $spacing, $sequenceKeys));
  220:             $r->print("\n");
  221:             $r->rflush();
  222:             untie(%cache);
  223:         }
  224:     }
  225:     $r->print('</pre>'."\n"); 
  226:     if($selected == 0) {
  227: 	$Str .= '<h3><font color=blue>WARNING: ';
  228:         $Str .= 'No Students enrolled OR Please select a student</font></h3>';
  229:         $r->print($Str);
  230:     } else {
  231: 	if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
  232: 	    $r->print(&StudentAverageTotal(\%cache, $students, $sequenceKeys));
  233: 	    untie(%cache);
  234: 	}
  235:     }
  236:     return;
  237: }
  238: #######################################################
  239: #######################################################
  240: 
  241: =pod
  242: 
  243: =item &CreateInterface()
  244: 
  245: Called by &BuildStudentAssessmentPage to create the top part of the
  246: page which displays the chart.
  247: 
  248: Inputs:
  249: 
  250: =over 4
  251: 
  252: =item $cache The ubiquitous cache
  253: 
  254: =item $selectedName The name of the currently selected student, or
  255: 'All Students' or 'No Student Selected'.
  256: 
  257: =item $students Array ref containing the name(s) of the students selected
  258: for display.
  259: 
  260: =item $formName The name of the HTML form to use, 'Statistics'
  261: 
  262: =item $doNotShow Array ref containing the names of columns to not show
  263: 
  264: =back
  265: 
  266: Returns:  A string containing the HTML for the headers and top table for 
  267: the chart page.
  268: 
  269: =cut
  270: 
  271: #######################################################
  272: #######################################################
  273: sub CreateInterface {
  274:     my($cache,$selectedName,$students,$formName,$doNotShow)=@_;
  275: 
  276:     my $Str = '';
  277:     $Str .= &CreateLegend();
  278:     $Str .= '<table><tr><td>'."\n";
  279:     $Str .= '<input type="submit" name="PreviousStudent" ';
  280:     $Str .= 'value="Previous Student" />'."\n";
  281:     $Str .= '&nbsp&nbsp&nbsp'."\n";
  282:     $Str .= &Apache::lonhtmlcommon::StudentOptions($cache, $students,
  283:                                                    $selectedName,
  284:                                                    'StudentAssessment',
  285:                                                    $formName);
  286:     $Str .= "\n".'&nbsp&nbsp&nbsp'."\n";
  287:     $Str .= '<input type="submit" name="NextStudent" ';
  288:     $Str .= 'value="Next Student" />'."\n";
  289:     $Str .= '</td></tr></table>'."\n";
  290:     $Str .= '<table cellspacing="5"><tr>'."\n";
  291:     $Str .= '<td align="center"><b>Select Sections</b>'."\n";
  292:     $Str .= '</td>'."\n";
  293:     $Str .= '<td align="center"><b>Select column to view:</b></td>'."\n";
  294:     $Str .= '<td></td></tr>'."\n";
  295: 
  296:     $Str .= '<tr><td align="center">'."\n";
  297:     my @sections = split(':',$cache->{'sectionList'});
  298:     my @selectedSections = split(':',$cache->{'sectionsSelected'});
  299:     $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5);
  300:     $Str .= '</td><td align="center">';
  301:     $Str .= &CreateColumnSelectionBox($doNotShow);
  302:     $Str .= '</td><td>'."\n";
  303:     $Str .= '<input type="submit" name="DefaultColumns" ';
  304:     $Str .= 'value="Default Column Display" />'."\n";
  305:     $Str .= '</td><td>'."\n";
  306:     $Str .= '<input type="submit" name="displaymode" ';
  307:     if (! exists($ENV{'form.displaymode'}) ||
  308:         lc($ENV{'form.displaymode'}) eq 'display with links') {
  309:         $Str .= 'value="Display without links" />';
  310:         # Set the current value, in case it is undefined
  311:         $ENV{'form.displaymode'} = 'Display with links';
  312:     } else {
  313:         $Str .= 'value="Display with links" />';
  314:     }
  315:     $Str .= "\n";
  316:     $Str .= '</td></tr></table>'."\n";
  317: 
  318:     return $Str;
  319: }
  320: #######################################################
  321: #######################################################
  322: 
  323: =pod
  324: 
  325: =item &CreateTableHeadings()
  326: 
  327: Create HTML for the columns of student data to show.
  328: Called by &BuildStudentAssessmentPage().  Calls
  329: &Apache::lonhtmlcommon::CreateHeadings().
  330: 
  331: Inputs:
  332: 
  333: =over 4
  334: 
  335: =item $cache The ubiquitous cache
  336: 
  337: =item $spacing A string of spaces
  338: 
  339: =item $infoKeys Array ref to names of keys to display from the cache 
  340: which describe students
  341: 
  342: =item $infoHeadings Array ref to headings of columns for student info
  343: 
  344: =item $sequenceKeys Array ref of names of keys to use to retrieve sequence
  345: data from the cache
  346: 
  347: =item $sequenceHeadings Array ref of names of sequences used for output.
  348: 
  349: =back
  350: 
  351: Returns: A string containing the HTML of the table headings.
  352: 
  353: =cut
  354: 
  355: #######################################################
  356: #######################################################
  357: sub CreateTableHeadings {
  358:     my($cache,$spacing,$infoKeys,$infoHeadings,$sequenceKeys,
  359:        $sequenceHeadings)=@_;
  360: 
  361: #    my $Str = '&nbsp&nbsp&nbsp&nbsp&nbsp';
  362:     my $Str = '';
  363:     $Str .= '<table border="0" cellpadding="0" cellspacing="0">'."\n";
  364:     $Str .= '<tr>'."\n";
  365:     $Str .= '<td><pre>     </pre></td>'."\n";
  366:     $Str .= &CreateColumnSelectors($infoHeadings, $sequenceHeadings,
  367:                                    $sequenceKeys);
  368:     $Str .= '<td></td></tr>'."\n";
  369: 
  370:     $Str .= '<tr>'."\n";
  371:     my $displayString = '';
  372:     $displayString .= '<td><pre>     </pre></td>'."\n";
  373:     $displayString .= '<td align="left"><pre><a href="/adm/statistics?';
  374:     $displayString .= 'sort=LINKDATA">DISPLAYDATA</a>FORMATTING';
  375:     $displayString .= $spacing.'</pre></td>'."\n";
  376:     $Str .= &Apache::lonhtmlcommon::CreateHeadings($cache,
  377:                                                    $infoKeys,
  378:                                                    $infoHeadings,
  379:                                                    $displayString,
  380:                                                    'preformatted');
  381: 
  382:     $displayString  = '<td align="left"><pre>DISPLAYDATAFORMATTING'.$spacing;
  383:     $displayString .= '</pre></td>'."\n";
  384:     $Str .= &Apache::lonhtmlcommon::CreateHeadings($cache,
  385:                                                    $sequenceKeys,
  386:                                                    $sequenceHeadings,
  387:                                                    $displayString,
  388:                                                    'preformatted');
  389: 
  390:     $Str .= '<td><pre>Total Solved/Total Problems</pre></td>';
  391:     $Str .= '</tr></table>'."\n";
  392: 
  393:     return $Str;
  394: }
  395: 
  396: #######################################################
  397: #######################################################
  398: 
  399: =pod
  400: 
  401: =item &StudentReport()
  402: 
  403: This is the workhorse subroutine - it handles formatting and display of a
  404: students performance data.  It processes one row of the chart.
  405: 
  406: Input: 
  407: 
  408: =over 4
  409: 
  410: =item $cache The ubiquitous cache
  411: 
  412: =item $name The name and domain of the current student in name:domain format
  413: 
  414: =item $spacing A string containing spaces.
  415: 
  416: =item $showSequences Array ref containing the sequences to display
  417: 
  418: =back
  419: 
  420: Output: $Str
  421: 
  422: $Str: Formatted string that is an entire row of the chart.  It is a
  423: concatenation of student information and student course information.
  424: 
  425: =cut
  426: 
  427: #######################################################
  428: #######################################################
  429: sub StudentReport {
  430:     my ($cache,$name,$spacing,$showSequences)=@_;
  431:     my ($username,$domain)=split(':',$name);
  432: 
  433:     my $Str = '';
  434:     if(defined($cache->{$name.':error'})) {
  435:         return $Str;
  436:     }
  437:     if($cache->{$name.':error'} =~ /course/) {
  438:         $Str .= '<b><font color="blue">No course data for student </font>';
  439:         $Str .= '<font color="red">'.$username.'.</font></b><br>';
  440:         return $Str;
  441:     }
  442: 
  443:     my $hasVersion = 'false';
  444:     my $hasFinalData = 'false';
  445:     foreach my $sequence (@$showSequences) {
  446:         my $hasData = 'false';
  447:         my $characterCount=0;
  448:         foreach my $problemID (split(':', $cache->{$sequence.':problems'})) {
  449:             my $problem = $cache->{$problemID.':problem'};
  450:             # All grades (except for versionless parts) are displayed as links
  451:             # to their submission record.  Loop through all the parts for the
  452:             # current problem in the correct order and prepare the output links
  453:             foreach(split(/\:/,$cache->{$sequence.':'.$problemID.
  454:                                         ':parts'})) {
  455:                 if($cache->{$name.':'.$problemID.':NoVersion'} eq 'true' ||
  456:                    $cache->{$name.':'.$problemID.':'.$_.':code'} eq ' ' ||
  457:                    $cache->{$name.':'.$problemID.':'.$_.':code'} eq '') {
  458:                     $Str .= ' ';
  459:                     $characterCount++;
  460:                     next;
  461:                 }
  462:                 $hasVersion = 'true';
  463:                 $hasData = 'true';
  464:                 if (lc($ENV{'form.displaymode'}) ne 'display without links') {
  465:                     $Str .= '<a href="/adm/grades?symb=';
  466:                     $Str .= &Apache::lonnet::escape($problem);
  467:                     $Str .= '&student='.$username.'&domain='.$domain;
  468:                     $Str .= '&command=submission">';
  469:                 }
  470:                 my $code = $cache->{$name.':'.$problemID.':'.$_.':code'};
  471:                 my $tries = $cache->{$name.':'.$problemID.':'.$_.':tries'};
  472:                 if($code eq '*' && $tries < 10 && $tries ne '') {
  473:                     $code = $tries;
  474:                 }
  475:                 $Str .= $code;
  476:                 if (lc($ENV{'form.displaymode'}) ne 'display without links') {
  477:                     $Str .= '</a>';
  478:                 }
  479:                 $characterCount++;
  480:             }
  481:         }
  482: 
  483:         # Output the number of correct answers for the current sequence.
  484:         # This part takes up 6 character slots, but is formated right
  485:         # justified.
  486:         my $spacesNeeded=$cache->{$sequence.':columnWidth'}-$characterCount;
  487:         $spacesNeeded -= 3;
  488:         $Str .= (' 'x$spacesNeeded);
  489: 
  490: #        my $outputProblemsCorrect = sprintf("%3d", $cache->{$name.':'.$sequence.
  491: #							    ':problemsCorrect'});
  492: 
  493: 	my $outputProblemsCorrect = sprintf("%2d/%2d", $cache->{$name.':'.$sequence.
  494:                                             ':problemsCorrect'},
  495:                                             $characterCount);
  496:         if($hasData eq 'true') {
  497:             $Str .= '<font color="#007700">'.$outputProblemsCorrect.'</font>';
  498:             $hasFinalData = 'true';
  499:         } else {
  500:             $Str .= '<font color="#007700">     </font>';
  501:         }
  502:         $Str .= $spacing;
  503:     }
  504: 
  505:     # Output the total correct problems over the total number of problems.
  506:     # I don't like this type of formatting, but it is a solution.  Need
  507:     # a way to dynamically determine the space requirements.
  508:     my $outputProblemsSolved = sprintf("%4d", $cache->{$name.':problemsSolved'});
  509:     my $outputTotalProblems  = sprintf("%4d", $cache->{$name.':totalProblems'});
  510:     if($hasFinalData eq 'true') {
  511:         $Str .= '<font color="#000088">'.$outputProblemsSolved.
  512: 	    ' / '.$outputTotalProblems.'</font>';
  513:     } else {
  514:         $Str .= '<font color="#000088">           </font>';
  515:     }
  516: 
  517:     if($hasVersion eq 'false') {
  518:         $Str = '<b><font color="blue">No course data.</font></b>';
  519:     }
  520: 
  521:     return $Str;
  522: }
  523: 
  524: #######################################################
  525: #######################################################
  526: 
  527: #######################################################
  528: #######################################################
  529: sub StudentAverageTotal {
  530:     my ($cache, $students, $sequenceKeys)=@_;
  531:     my $Str = "\n<b>Summary Tables:</b>\n";
  532:     my %Correct = ();
  533:     my $ProblemsSolved = 0;
  534:     my $TotalProblems = 0;
  535:     my $StudentCount = 0;
  536: 
  537:     foreach my $name (@$students) {
  538:         $StudentCount++;
  539:         foreach my $sequence (@$sequenceKeys) {
  540:             $Correct{$sequence} +=
  541: 	       $cache->{$name.':'.$sequence.':problemsCorrect'};
  542:         }
  543: 	$ProblemsSolved += $cache->{$name.':problemsSolved'};
  544:         $TotalProblems += $cache->{$name.':totalProblems'};
  545:     }
  546:     if ($StudentCount) { 
  547:         $ProblemsSolved = sprintf( "%.2f", 
  548:                              $ProblemsSolved/$StudentCount);
  549:         $TotalProblems /= $StudentCount;
  550:     } else {
  551:         $ProblemsSolved = 0;
  552:         $TotalProblems  = 0;
  553:     }
  554: 
  555:     $Str .= '<table border=2 cellspacing="1">'."\n";
  556:     $Str .= '<tr><td><b>Students Count</b></td><td><b>'.
  557:             $StudentCount.'</b></td></tr>'."\n";
  558:     $Str .= '<tr><td><b>Total Problems</b></td><td><b>'.
  559:             $TotalProblems.'</b></td></tr>'."\n";
  560:     $Str .= '<tr><td><b>Average Correct</b></td><td><b>'.
  561:             $ProblemsSolved.'</b></td></tr>'."\n";
  562:     $Str .= '</table>'."\n";
  563: 
  564:     $Str .= '<table border=2 cellspacing="1">'."\n";
  565:     $Str .= '<tr><th>Title</th><th>Total Problems</th>'.
  566:             '<th>Average Correct</th></tr>'."\n";
  567:     foreach my $S(@$sequenceKeys) {
  568:         my $title=$cache->{$S.':title'};
  569: 	#$Str .= $cache->{$S.':problems'};
  570: 	#my @problems=split(':', $cache->{$S.':problems'});
  571: 	#my $pCount=scalar @problems;
  572: 	my $pCount=MaxSeqPr($cache,@$students[0],$S);
  573:         my $crr;
  574: 	if ($StudentCount) {
  575:             $crr=sprintf( "%.2f", $Correct{$S}/$StudentCount );
  576:         } else {
  577:             $crr="0.00";
  578:         }
  579:         $Str .= '<tr><td>'.$title.
  580:                 '</td><td align=center>'.$pCount.
  581:                 '</td><td align=center>'.$crr.
  582:                 '</td></tr>'."\n";
  583:     }
  584: 
  585:     $Str .= '</table>'."\n";
  586: 
  587:     return $Str;
  588: }
  589: 
  590: #######################################################
  591: #######################################################
  592: 
  593: #######################################################
  594: #######################################################
  595: sub MaxSeqPr {
  596:     my ($cache, $name, $sequence)=@_;
  597:     my $prCount=0;
  598:     foreach my $problemID (split(':', $cache->{$sequence.':problems'})) {
  599:         my $problem = $cache->{$problemID.':problem'};
  600:         foreach(split(/\:/,$cache->{$sequence.':'.$problemID.':parts'})) {
  601:             if($cache->{$name.':'.$problemID.':NoVersion'} eq 'true' ||
  602:                $cache->{$name.':'.$problemID.':'.$_.':code'} eq ' ' ||
  603:                $cache->{$name.':'.$problemID.':'.$_.':code'} eq '') {
  604:                  $prCount++;
  605:                  next;
  606:             }
  607:             $prCount++;
  608:         }
  609:     }
  610:     return $prCount;
  611: }
  612: 
  613: #######################################################
  614: #######################################################
  615: 
  616: =pod
  617: 
  618: =item &CreateLegend()
  619: 
  620: This function returns a formatted string containing the legend for the
  621: chart.  The legend describes the symbols used to represent grades for
  622: problems.
  623: 
  624: =cut
  625: 
  626: #######################################################
  627: #######################################################
  628: sub CreateLegend {
  629:     my $Str = "<p><pre>".
  630:               "   1  correct by student in 1 try\n".
  631:               "   7  correct by student in 7 tries\n".
  632:               "   *  correct by student in more than 9 tries\n".
  633: 	      "   +  correct by hand grading or override\n".
  634:               "   -  incorrect by override\n".
  635: 	      "   .  incorrect attempted\n".
  636: 	      "   #  ungraded attempted\n".
  637:               "      not attempted (blank field)\n".
  638: 	      "   x  excused".
  639:               "</pre><p>";
  640:     return $Str;
  641: }
  642: 
  643: #######################################################
  644: #######################################################
  645: 
  646: =pod
  647: 
  648: =item &CreateColumnSelectionBox()
  649: 
  650: If there are columns not being displayed then this selection box is created
  651: with a list of those columns.  When selections are made and the page
  652: refreshed, the columns will be removed from this box and the column is
  653: put back in the chart.  If there is no columns to select, no row is added
  654: to the interface table.
  655: 
  656: =over 4
  657: Input: $CacheData, $headings
  658: 
  659: $CacheData: A pointer to a hash tied to the cached data
  660: 
  661: $headings:  An array of the names of the columns for the student information.
  662: They are used for displaying which columns are missing.
  663: 
  664: Output: $notThere
  665: 
  666: $notThere: The string contains one row of a table.  The first column has the
  667: name of the selection box.  The second contains the selection box
  668: which has a size of four.
  669: 
  670: =back
  671: 
  672: =cut
  673: 
  674: #######################################################
  675: #######################################################
  676: sub CreateColumnSelectionBox {
  677:     my ($doNotShow)=@_;
  678: 
  679:     my $notThere = '';
  680:     $notThere .= '<select name="ReselectColumns" size="4" ';
  681:     $notThere .= 'multiple="true">'."\n";
  682: 
  683:     for(my $index=0; $index<$doNotShow->{'count'}; $index++) {
  684:         my $name = $doNotShow->{$index.':name'};
  685:         $notThere .= '<option value="';
  686:         $notThere .= $doNotShow->{$index.':id'}.'">';
  687:         $notThere .= $name.'</option>'."\n";
  688:     }
  689: 
  690:     $notThere .= '</select>';
  691: 
  692:     return $notThere;
  693: }
  694: 
  695: #######################################################
  696: #######################################################
  697: 
  698: =pod
  699: 
  700: =item &CreateColumnSelectors()
  701: 
  702: This function generates the checkboxes above the column headings.  The
  703: column will be removed if the checkbox is unchecked.
  704: 
  705: =over 4
  706: 
  707: Input: $CacheData, $headings
  708: 
  709: $CacheData: A pointer to a hash tied to the cached data
  710: 
  711: $headings:  An array of the names of the columns for the student 
  712: information.  They are used to know what are the student information columns
  713: 
  714: Output: $present
  715: 
  716: $present: The string contains the first row of a table.  Each column contains
  717: a checkbox which is left justified.  Currently left justification is used
  718: for consistency of location over the column in which it presides.
  719: 
  720: =back
  721: 
  722: =cut
  723: 
  724: #######################################################
  725: #######################################################
  726: sub CreateColumnSelectors {
  727:     my ($infoHeadings, $sequenceHeadings, $sequenceKeys)=@_;
  728: 
  729:     my $present = '';
  730:     for(my $index=0; $index<(scalar @$infoHeadings); $index++) {
  731:         $present .= '<td align="left">';
  732:         $present .= '<input type="checkbox" checked="on" ';
  733:         $present .= 'name="HeadingColumn'.$infoHeadings->[$index].'" />';
  734:         $present .= '</td>'."\n";
  735:     }
  736: 
  737:     for(my $index=0; $index<(scalar @$sequenceHeadings); $index++) {
  738:         $present .= '<td align="left">';
  739:         $present .= '<input type="checkbox" checked="on" ';
  740:         $present .= 'name="SequenceColumn'.$sequenceKeys->[$index].'" />';
  741:         $present .= '</td>'."\n";
  742:     }
  743: 
  744:     return $present;
  745: }
  746: 
  747: #######################################################
  748: #######################################################
  749: 
  750: =pod
  751: 
  752: =back
  753: 
  754: =head1 HELPER FUNCTIONS
  755: 
  756: =over 4
  757: 
  758: =cut 
  759: 
  760: #######################################################
  761: #######################################################
  762: sub FindSelectedStudent {
  763:     my($cache, $selectedName, $students)=@_;
  764: 
  765:     if($selectedName eq 'All Students' ||
  766:        $selectedName eq 'No Student Selected') {
  767:         return $selectedName;
  768:     }
  769: 
  770:     for(my $index=0; $index<(scalar @$students); $index++) {
  771:         my $fullname = $cache->{$students->[$index].':fullname'};
  772:         if($fullname eq $selectedName) {
  773:             if($cache->{'StudentAssessmentMove'} eq 'next') {
  774:                 if($index == ((scalar @$students) - 1)) {
  775:                     $selectedName = $students->[0];
  776:                     return $selectedName;
  777:                 } else {
  778:                     $selectedName = $students->[$index+1];
  779:                     return $selectedName;
  780:                 }
  781:             } elsif($cache->{'StudentAssessmentMove'} eq 'previous') {
  782:                 if($index == 0) {
  783:                     $selectedName = $students->[-1];
  784:                     return $selectedName;
  785:                 } else {
  786:                     $selectedName = $students->[$index-1];
  787:                     return $selectedName;
  788:                 }
  789:             } else {
  790:                 $selectedName = $students->[$index];
  791:                 return $selectedName;
  792:             }
  793:             last;
  794:         }
  795:     }
  796: 
  797:     return 'No Student Selected';
  798: }
  799: #######################################################
  800: #######################################################
  801: 
  802: =pod
  803: 
  804: =item &ShouldShowColumn()
  805: 
  806: Determine if a specified column should be shown on the chart.
  807: 
  808: =over 4
  809: 
  810: Input: $cache, $test
  811: 
  812: $cache: A pointer to the hash tied to the cached data
  813: 
  814: $test: The form name of the column (heading.$headingIndex) or 
  815: (sequence.$sequenceIndex)
  816: 
  817: =back
  818: 
  819: =cut
  820: 
  821: #######################################################
  822: #######################################################
  823: sub ShouldShowColumns {
  824:     my ($cache,$headings,$cacheKey)=@_;
  825: 
  826:     my @infoKeys=();
  827:     my @infoHeadings=();
  828: 
  829:     my @sequenceKeys=();
  830:     my @sequenceHeadings=();
  831: 
  832:     my %doNotShow;
  833: 
  834:     my $index;
  835:     my $count = 0;
  836:     my $check = '';
  837:     for($index=0; $index < scalar @$headings; $index++) {
  838:         $check = 'HeadingColumn'.$headings->[$index];
  839:         if($cache->{'HeadingsFound'} =~ /$check/) {
  840:             push(@infoHeadings, $headings->[$index]);
  841:             push(@infoKeys, $cacheKey->[$index]);
  842:         } else {
  843:             $doNotShow{$count.':name'} = $headings->[$index];
  844:             $doNotShow{$count.':id'} = 'HeadingColumn'.$headings->[$index];
  845:             $count++;
  846:         }
  847:     }
  848: 
  849:     foreach my $sequence (split(/\:/,$cache->{'orderedSequences'})) {
  850:         $check = 'SequenceColumn'.$sequence;
  851:         if($cache->{'SequencesFound'} eq 'All Sequences' ||
  852:            $cache->{'SequencesFound'} =~ /$check/) {
  853:             push(@sequenceHeadings, $cache->{$sequence.':title'});
  854:             push(@sequenceKeys, $sequence);
  855:         } else {
  856:             $doNotShow{$count.':name'} = $cache->{$sequence.':title'};
  857:             $doNotShow{$count.':id'} = 'SequenceColumn'.$sequence;
  858:             $count++;
  859:         }
  860:     }
  861: 
  862:     $doNotShow{'count'} = $count;
  863: 
  864:     return (\@infoHeadings, \@infoKeys, \@sequenceHeadings,
  865:             \@sequenceKeys, \%doNotShow);
  866: }
  867: 1;
  868: 
  869: #######################################################
  870: #######################################################
  871: 
  872: =pod 
  873: 
  874: =back
  875: 
  876: =cut
  877: 
  878: #######################################################
  879: #######################################################
  880: 
  881: __END__

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