Annotation of loncom/interface/lonchart.pm, revision 1.47

1.1       www         1: # The LearningOnline Network with CAPA
1.25      minaeibi    2: # (Publication Handler
                      3: #
1.47    ! stredwic    4: # $Id: lonchart.pm,v 1.46 2002/06/28 21:12:46 stredwic Exp $
1.25      minaeibi    5: #
                      6: # Copyright Michigan State University Board of Trustees
                      7: #
                      8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
                      9: #
                     10: # LON-CAPA is free software; you can redistribute it and/or modify
                     11: # it under the terms of the GNU General Public License as published by
                     12: # the Free Software Foundation; either version 2 of the License, or
                     13: # (at your option) any later version.
                     14: #
                     15: # LON-CAPA is distributed in the hope that it will be useful,
                     16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     18: # GNU General Public License for more details.
                     19: #
                     20: # You should have received a copy of the GNU General Public License
                     21: # along with LON-CAPA; if not, write to the Free Software
                     22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
                     23: #
                     24: # /home/httpd/html/adm/gpl.txt
                     25: #
                     26: # http://www.lon-capa.org/
                     27: #
1.1       www        28: # Homework Performance Chart
                     29: #
                     30: # (Navigate Maps Handler
                     31: #
                     32: # (Page Handler
                     33: #
                     34: # (TeX Content Handler
1.27      minaeibi   35: # YEAR=2000
1.1       www        36: # 05/29/00,05/30 Gerd Kortemeyer)
                     37: # 08/30,08/31,09/06,09/14,09/15,09/16,09/19,09/20,09/21,09/23,
                     38: # 10/02,10/10,10/14,10/16,10/18,10/19,10/31,11/6,11/14,11/16 Gerd Kortemeyer)
1.27      minaeibi   39: # YEAR=2001
1.14      minaeibi   40: # 3/1/1,6/1,17/1,29/1,30/1,31/1 Gerd Kortemeyer)
1.5       minaeibi   41: # 7/10/01 Behrouz Minaei
1.6       www        42: # 9/8 Gerd Kortemeyer
1.27      minaeibi   43: # 10/1, 10/19, 11/17, 11/22, 11/24, 11/28 12/18 Behrouz Minaei
                     44: # YEAR=2002
1.33      minaeibi   45: # 2/1, 2/6, 2/19, 2/28 Behrouz Minaei
1.26      minaeibi   46: #
                     47: ###
1.1       www        48: 
                     49: package Apache::lonchart;
                     50: 
                     51: use strict;
                     52: use Apache::Constants qw(:common :http);
                     53: use Apache::lonnet();
1.28      albertel   54: use Apache::loncommon();
1.1       www        55: use HTML::TokeParser;
                     56: use GDBM_File;
                     57: 
1.46      stredwic   58: my $jr; 
1.44      stredwic   59: # ----- FORMAT PRINT DATA ----------------------------------------------
1.1       www        60: 
1.44      stredwic   61: sub FormatStudentInformation {
                     62:     my ($cache,$name,$studentInformation,$spacePadding)=@_;
                     63:     my $Str='<pre>';
                     64: 
                     65:     foreach (@$studentInformation) {
                     66: 	my $data=$cache->{$name.':'.$_};
                     67: 	$Str .= $data;
                     68: 
                     69: 	my @dataLength=split(//,$data);
                     70: 	my $length=scalar @dataLength;
                     71: 	$Str .= (' 'x($cache->{$_.'Length'}-$length));
                     72: 	$Str .= $spacePadding;
                     73:     }
                     74: 
                     75:     return $Str;
                     76: }
                     77: 
                     78: sub FormatStudentData {
                     79:     my ($name,$coid,$studentInformation,$spacePadding,$ChartDB)=@_;
1.43      stredwic   80:     my ($sname,$sdom) = split(/\:/,$name);
                     81:     my $Str;
1.44      stredwic   82:     my %CacheData;
1.43      stredwic   83: 
1.44      stredwic   84:     unless(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_READER,0640)) {
                     85:         return '';
                     86:     }
1.43      stredwic   87:     # Handle Student information ------------------------------------------
1.44      stredwic   88:     # Handle user data
                     89:     $Str=&FormatStudentInformation(\%CacheData, $name, $studentInformation, 
                     90:                                    $spacePadding);
                     91: 
1.43      stredwic   92:     # Handle errors
1.44      stredwic   93:     if($CacheData{$name.':error'} =~ /environment/) {
                     94:         untie(%CacheData);
                     95:         $Str .= '</pre>';
                     96:         return $Str;
                     97: #	my $errorMessage = $CacheData{$name.':error'};
1.43      stredwic   98: #	return '<td>'.$sname.'</td><td>'.$sdom.
                     99: #	    '</td><td><font color="#000088">'.$errorMessage.'</font></td>';
1.44      stredwic  100:     }
1.43      stredwic  101: 
1.44      stredwic  102:     if($CacheData{$name.':error'} =~ /course/) {
                    103:         untie(%CacheData);
                    104:         $Str .= '</pre>';
1.40      stredwic  105: 	return $Str;
1.43      stredwic  106: #	my $errorMessage = 'May have no course data or '.
1.44      stredwic  107: #	                   $CacheData{$name.':error'};
1.43      stredwic  108: #	return '<td>'.$sname.'</td><td>'.$sdom.
                    109: #	    '</td><td><font color="#000088">'.$errorMessage.'</font></td>';
1.40      stredwic  110:     }
                    111: 
1.43      stredwic  112:     # Handle problem data ------------------------------------------------
1.44      stredwic  113:     my $Version;
                    114:     my $problemsCorrect = 0;
                    115:     my $totalProblems   = 0;
                    116:     my $problemsSolved  = 0;
                    117:     my $numberOfParts   = 0;
                    118:     foreach my $sequence (split(/\:/,$CacheData{'orderedSequences'})) {
                    119: 	my $characterCount=0;
                    120: 	foreach my $problemID (split(/\:/,$CacheData{$sequence.':problems'})) {
                    121: 	    my $problem = $CacheData{$problemID.':problem'};
                    122: 	    my $LatestVersion = $CacheData{$name.":version:$problem"};
                    123: 
                    124:             if(!$LatestVersion) {
                    125:                 foreach my $part (split(/\:/,$CacheData{$sequence.':'.
                    126:                                                         $problemID.
                    127:                                                         ':parts'})) {
                    128:                     $Str .= ' ';
                    129:                     $totalProblems++;
                    130:                     $characterCount++;
                    131:                 }
                    132:                 next;
                    133:             }
                    134: 
                    135:             my %partData=undef;
                    136:             #initialize data, displays skips correctly
                    137:             foreach my $part (split(/\:/,$CacheData{$sequence.':'.
                    138:                                                     $problemID.
                    139:                                                     ':parts'})) {
                    140:                 $partData{$part.':tries'}=0;
                    141:                 $partData{$part.':code'}=' ';
                    142:             }
                    143: 	    for(my $Version=1; $Version<=$LatestVersion; $Version++) {
                    144:                 foreach my $part (split(/\:/,$CacheData{$sequence.':'.
                    145:                                                         $problemID.
                    146:                                                         ':parts'})) {
                    147: 
                    148:                     if(!defined($CacheData{$name.":$Version:$problem".
                    149:                                                ":resource.$part.solved"})) {
                    150:                         next;
                    151:                     }
                    152: 
                    153:                     my $tries=0;
                    154:                     my $code=' ';
                    155: 
                    156:                     $tries = $CacheData{$name.":$Version:$problem".
                    157:                                         ":resource.$part.tries"};
                    158:                     $partData{$part.':tries'}=($tries) ? $tries : 0;
                    159: 
                    160:                     my $val = $CacheData{$name.":$Version:$problem".
                    161:                                          ":resource.$part.solved"};
                    162:                     if    ($val eq 'correct_by_student')   {$code = '*';} 
                    163:                     elsif ($val eq 'correct_by_override')  {$code = '+';}
                    164:                     elsif ($val eq 'incorrect_attempted')  {$code = '.';} 
                    165:                     elsif ($val eq 'incorrect_by_override'){$code = '-';}
                    166:                     elsif ($val eq 'excused')              {$code = 'x';}
                    167:                     elsif ($val eq 'ungraded_attempted')   {$code = '#';}
                    168:                     else                                   {$code = ' ';}
                    169:                     $partData{$part.':code'}=$code;
                    170:                 }
                    171:             }
                    172: 
                    173:             $Str.='<a href="/adm/grades?symb='.
                    174:                 &Apache::lonnet::escape($problem).
                    175:                 '&student='.$sname.'&domain='.$sdom.'&command=submission">'; 
                    176:             foreach(split(/\:/,$CacheData{$sequence.':'.$problemID.
                    177:                                           ':parts'})) {
                    178:                 if($partData{$_.':code'} eq '*') {
                    179:                     $problemsCorrect++;
                    180:                     if (($partData{$_.':tries'}<10) &&
                    181:                         ($partData{$_.':tries'} ne '')) {
                    182:                         $partData{$_.':code'}=$partData{$_.':tries'};
                    183:                     }
                    184:                 } elsif($partData{$_.':code'} eq '+') {
                    185:                     $problemsCorrect++;
                    186:                 }
                    187: 
                    188:                 $Str .= $partData{$_.':code'};
                    189:                 $characterCount++;
                    190: 
                    191:                 if($partData{$_.':code'} ne 'x') {
                    192:                     $totalProblems++;
                    193:                 }
                    194:             }
                    195:             $Str.='</a>';
                    196:         }
                    197: 
                    198:         my $spacesNeeded=$CacheData{$sequence.':columnWidth'}-$characterCount;
                    199:         $spacesNeeded -= 3;
                    200:         $Str .= (' 'x$spacesNeeded);
                    201: 
                    202: 	my $outputProblemsCorrect = sprintf( "%3d", $problemsCorrect );
                    203: 	$Str .= '<font color="#007700">'.$outputProblemsCorrect.'</font>';
                    204: 	$problemsSolved += $problemsCorrect;
                    205: 	$problemsCorrect=0;
                    206: 
                    207:         $Str .= $spacePadding;
                    208:     }
1.11      minaeibi  209: 
1.44      stredwic  210:     $Str .= '<font color="#000088">'.$problemsSolved.
                    211: 	    ' / '.$totalProblems.'</font></pre>';
1.39      stredwic  212: 
1.44      stredwic  213:     untie(%CacheData);
                    214:     return $Str;
                    215: }
1.43      stredwic  216: 
1.44      stredwic  217: sub CreateTableHeadings {
                    218:     my ($CacheData,$studentInformation,$headings,$spacePadding)=@_;
                    219:     my $Str='<pre>';
1.43      stredwic  220: 
1.44      stredwic  221:     for(my $index=0; $index<(scalar @$headings); $index++) {
                    222: 	my $data=$$headings[$index];
                    223: 	$Str .= $data;
                    224: 
                    225: 	my @dataLength=split(//,$data);
                    226: 	my $length=scalar @dataLength;
                    227: 	$Str .= (' 'x($CacheData->{$$studentInformation[$index].'Length'}-
                    228:                       $length));
                    229: 	$Str .= $spacePadding;
                    230:     }
                    231: 
                    232:     foreach my $sequence (split(/\:/,$CacheData->{'orderedSequences'})) {
                    233: 	$Str .= $CacheData->{$sequence.':title'};
                    234: 	my @titleLength=split(//,$CacheData->{$sequence.':title'});
                    235: 	my $leftover=$CacheData->{$sequence.':columnWidth'}-
                    236:                      (scalar @titleLength);
                    237: 	$Str .= (' 'x$leftover);
                    238: 	$Str .= $spacePadding;
1.1       www       239:     }
1.39      stredwic  240: 
1.44      stredwic  241:     $Str .= 'Total Solved/Total Problems';
                    242:     $Str .= '</pre>';
1.11      minaeibi  243: 
1.43      stredwic  244:     return $Str;
                    245: }
                    246: 
1.46      stredwic  247: sub CreateColumnSelectors {
                    248:     my ($CacheData,$studentInformation,$headings,$spacePadding)=@_;
                    249:     my $Str='';
                    250: 
                    251:     $Str .= '<form name="stat" method="post" action="/adm/chart" >'."\n";
                    252:     $Str .= '<input type="submit" name="sort" value="Refresh Chart"/>';
                    253:     $Str .= '</form>'."\n";
                    254:     return $Str;
                    255: 
                    256:     for(my $index=0; $index<(scalar @$headings); $index++) {
                    257: 	my $data=$$headings[$index];
                    258: 	$Str .= $data;
                    259: 
                    260: 	my @dataLength=split(//,$data);
                    261: 	my $length=scalar @dataLength;
                    262: 	$Str .= (' 'x($CacheData->{$$studentInformation[$index].'Length'}-
                    263:                       $length));
                    264: 	$Str .= $spacePadding;
                    265:     }
                    266: 
                    267:     foreach my $sequence (split(/\:/,$CacheData->{'orderedSequences'})) {
                    268: 	$Str .= $CacheData->{$sequence.':title'};
                    269: 	my @titleLength=split(//,$CacheData->{$sequence.':title'});
                    270: 	my $leftover=$CacheData->{$sequence.':columnWidth'}-
                    271:                      (scalar @titleLength);
                    272: 	$Str .= (' 'x$leftover);
                    273: 	$Str .= $spacePadding;
                    274:     }
                    275: 
                    276:     return $Str;
                    277: }
                    278: 
1.43      stredwic  279: sub CreateForm {
                    280:     my $OpSel1='';
                    281:     my $OpSel2='';
                    282:     my $OpSel3='';
                    283:     my $Status = $ENV{'form.status'};
                    284:     if ( $Status eq 'Any' ) { $OpSel3='selected'; }
                    285:     elsif ($Status eq 'Expired' ) { $OpSel2 = 'selected'; }
                    286:     else { $OpSel1 = 'selected'; }
                    287: 
1.44      stredwic  288:     my $Ptr = '<form name="stat" method="post" action="/adm/chart" >'."\n";
1.43      stredwic  289:     $Ptr .= '<b> Sort by: &nbsp; </b>'."\n";
                    290:     $Ptr .= '&nbsp;&nbsp;&nbsp;';
1.44      stredwic  291:     $Ptr .= '<input type="submit" name="sort" value="User Name" />'."\n";
1.43      stredwic  292:     $Ptr .= '&nbsp;&nbsp;&nbsp;';
1.44      stredwic  293:     $Ptr .= '<input type="submit" name="sort" value="Last Name" />'."\n";
1.43      stredwic  294:     $Ptr .= '&nbsp;&nbsp;&nbsp;';
1.44      stredwic  295:     $Ptr .= '<input type="submit" name="sort" value="Section"/>'."\n";
1.43      stredwic  296:     $Ptr .= '<br><br>';
                    297:     $Ptr .= '<b> Student Status: &nbsp; </b>'."\n".
                    298:             '<select name="status">'. 
                    299:             '<option '.$OpSel1.' >Active</option>'."\n".
                    300:             '<option '.$OpSel2.' >Expired</option>'."\n".
                    301: 	    '<option '.$OpSel3.' >Any</option> </select> '."\n";
1.45      stredwic  302:     $Ptr .= '<br><br>';
                    303:     $Ptr .= '<input type="submit" name="sort" value="Recalculate Chart"/>';
                    304:     $Ptr .= "\n";
1.43      stredwic  305:     $Ptr .= '&nbsp;&nbsp;&nbsp;';
1.45      stredwic  306:     $Ptr .= '<input type="submit" name="sort" value="Refresh Chart"/>';
1.44      stredwic  307:     $Ptr .= "\n";
1.43      stredwic  308:     $Ptr .= '</form>'."\n";
1.44      stredwic  309: 
                    310:     return $Ptr;
                    311: }
                    312: 
                    313: sub CreateLegend {
                    314:     my $Str = '<h1>'.$ENV{'course.'.$ENV{'request.course.id'}.'.description'}.
                    315:               '</h1><h3>'.localtime().
                    316:               "</h3><p><pre>1..9: correct by student in 1..9 tries\n".
                    317:               "   *: correct by student in more than 9 tries\n".
                    318: 	      "   +: correct by override\n".
                    319:               "   -: incorrect by override\n".
                    320: 	      "   .: incorrect attempted\n".
                    321: 	      "   #: ungraded attempted\n".
                    322:               "    : not attempted\n".
                    323: 	      "   x: excused</pre><p>"; 
                    324:     return $Str;
                    325: }
                    326: 
                    327: sub StartDocument {
                    328:     my $Str = '';
                    329:     $Str .= '<html>';
                    330:     $Str .= '<head><title>';
                    331:     $Str .= 'LON-CAPA Assessment Chart</title></head>';
                    332:     $Str .= '<body bgcolor="#FFFFFF">';
                    333:     $Str .= '<script>window.focus();</script>';
                    334:     $Str .= '<img align=right src=/adm/lonIcons/lonlogos.gif>';
                    335:     $Str .= '<h1>Assessment Chart</h1>';
                    336: 
                    337:     return $Str;
                    338: }
                    339: 
                    340: # ----- END FORMAT PRINT DATA ------------------------------------------
                    341: 
                    342: # ----- DOWNLOAD INFORMATION -------------------------------------------
                    343: 
                    344: sub DownloadPrerequisiteData {
                    345:     my ($courseID, $c)=@_;
                    346:     my ($courseDomain,$courseNumber)=split(/\_/,$courseID);
                    347: 
                    348:     my %classlist=&Apache::lonnet::dump('classlist',$courseDomain,
                    349:                                         $courseNumber);
                    350:     my ($checkForError)=keys (%classlist);
                    351:     if($checkForError =~ /^(con_lost|error|no_such_host)/i) {
                    352:         return \%classlist;
                    353:     }
                    354: 
                    355:     foreach my $name (keys(%classlist)) {
                    356:         if($c->aborted()) {
                    357:             $classlist{'error'}='aborted';
                    358:             return \%classlist;
                    359:         }
                    360: 
                    361:         my ($studentName,$studentDomain) = split(/\:/,$name);
                    362:         # Download student environment data, specifically the full name and id.
                    363:         my %studentInformation=&Apache::lonnet::get('environment',
                    364:                                                     ['lastname','generation',
                    365:                                                      'firstname','middlename',
                    366:                                                      'id'],
                    367:                                                     $studentDomain,
                    368:                                                     $studentName);
                    369:         $classlist{$name.':studentInformation'}=\%studentInformation;
                    370: 
                    371:         if($c->aborted()) {
                    372:             $classlist{'error'}='aborted';
                    373:             return \%classlist;
                    374:         }
                    375: 
                    376:         #Section
                    377:         my %section=&Apache::lonnet::dump('roles',$studentDomain,$studentName);
                    378:         $classlist{$name.':section'}=\%section;
                    379:     }
                    380: 
                    381:     return \%classlist;
1.1       www       382: }
                    383: 
1.44      stredwic  384: sub DownloadStudentCourseInformation {
                    385:     my ($name,$courseID)=@_;
                    386:     my ($studentName,$studentDomain) = split(/\:/,$name);
                    387: 
                    388:     # Download student course data
                    389:     my %courseData=&Apache::lonnet::dump($courseID,$studentDomain,
                    390: 					 $studentName);
                    391:     return \%courseData;
                    392: }
                    393: 
                    394: # ----- END DOWNLOAD INFORMATION ---------------------------------------
                    395: 
                    396: # ----- END PROCESSING FUNCTIONS ---------------------------------------
                    397: 
                    398: sub ProcessTopResourceMap {
                    399:     my ($ChartDB,$c)=@_;
                    400:     my %hash;
                    401:     my $fn=$ENV{'request.course.fn'};
                    402:     if(-e "$fn.db") {
                    403: 	my $tieTries=0;
                    404: 	while($tieTries < 3) {
                    405: 	    if(tie(%hash,'GDBM_File',"$fn.db",&GDBM_READER,0640)) {
                    406: 		last;
                    407: 	    }
                    408: 	    $tieTries++;
                    409: 	    sleep 1;
1.43      stredwic  410: 	}
1.44      stredwic  411: 	if($tieTries >= 3) {
                    412:             return 'Coursemap undefined.';
                    413:         }
                    414:     } else {
                    415:         return 'Can not open Coursemap.';
1.43      stredwic  416:     }
                    417: 
1.44      stredwic  418:     my %CacheData;
                    419:     unless(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) {
                    420:         untie(%hash);
                    421: 	return 'Could not tie cache hash.';
                    422:     }
                    423: 
                    424:     my (@sequences, @currentResource, @finishResource);
                    425:     my ($currentSequence, $currentResourceID, $lastResourceID);
                    426: 
                    427:     $currentResourceID=$hash{'ids_/res/'.$ENV{'request.course.uri'}};
1.46      stredwic  428:     push(@currentResource, $currentResourceID);
1.44      stredwic  429:     $lastResourceID=-1;
                    430:     $currentSequence=-1;
                    431:     my $topLevelSequenceNumber = $currentSequence;
                    432: 
                    433:     while(1) {
                    434:         if($c->aborted()) {
                    435:             last;
                    436:         }
                    437: 	# HANDLE NEW SEQUENCE!
                    438: 	#if page || sequence
                    439: 	if(defined($hash{'map_pc_'.$hash{'src_'.$currentResourceID}})) {
                    440: 	    push(@sequences, $currentSequence);
                    441: 	    push(@currentResource, $currentResourceID);
                    442: 	    push(@finishResource, $lastResourceID);
                    443: 
                    444: 	    $currentSequence=$hash{'map_pc_'.$hash{'src_'.$currentResourceID}};
                    445: 	    $lastResourceID=$hash{'map_finish_'.
                    446: 				  $hash{'src_'.$currentResourceID}};
                    447: 	    $currentResourceID=$hash{'map_start_'.
                    448: 				     $hash{'src_'.$currentResourceID}};
                    449: 
                    450: 	    if(!($currentResourceID) || !($lastResourceID)) {
                    451: 		$currentSequence=pop(@sequences);
                    452: 		$currentResourceID=pop(@currentResource);
                    453: 		$lastResourceID=pop(@finishResource);
                    454: 		if($currentSequence eq $topLevelSequenceNumber) {
                    455: 		    last;
                    456: 		}
                    457: 	    }
                    458: 	}
                    459: 
                    460: 	# Handle gradable resources: exams, problems, etc
                    461: 	$currentResourceID=~/(\d+)\.(\d+)/;
                    462:         my $partA=$1;
                    463:         my $partB=$2;
                    464: 	if($hash{'src_'.$currentResourceID}=~
                    465: 	   /\.(problem|exam|quiz|assess|survey|form)$/ &&
                    466: 	   $partA eq $currentSequence) {
                    467: 	    my $Problem = &Apache::lonnet::symbclean(
                    468: 			  &Apache::lonnet::declutter($hash{'map_id_'.$partA}).
                    469: 			  '___'.$partB.'___'.
                    470: 			  &Apache::lonnet::declutter($hash{'src_'.
                    471: 							 $currentResourceID}));
                    472: 
                    473: 	    $CacheData{$currentResourceID.':problem'}=$Problem;
                    474: 	    if(!defined($CacheData{$currentSequence.':problems'})) {
                    475: 		$CacheData{$currentSequence.':problems'}=$currentResourceID;
                    476: 	    } else {
                    477: 		$CacheData{$currentSequence.':problems'}.=
                    478: 		    ':'.$currentResourceID;
                    479: 	    }
                    480: 
                    481:             #Get Parts for problem
                    482: 	    my $meta=$hash{'src_'.$currentResourceID};
                    483: 	    foreach (split(/\,/,&Apache::lonnet::metadata($meta,'keys'))) {
                    484: 		if($_=~/^stores\_(\d+)\_tries$/) {
                    485: 		    my $Part=&Apache::lonnet::metadata($meta,$_.'.part');
                    486:                     if(!defined($CacheData{$currentSequence.':'.
                    487:                                           $currentResourceID.':parts'})) {
                    488:                         $CacheData{$currentSequence.':'.$currentResourceID.
                    489:                                    ':parts'}=$Part;
                    490:                     } else {
                    491:                         $CacheData{$currentSequence.':'.$currentResourceID.
                    492:                                    ':parts'}.=':'.$Part;
                    493:                     }
                    494: 		}
                    495: 	    }
                    496: 	}
                    497: 
                    498: 	#if resource == finish resource
                    499: 	if($currentResourceID eq $lastResourceID) {
                    500: 	    #pop off last resource of sequence
                    501: 	    $currentResourceID=pop(@currentResource);
                    502: 	    $lastResourceID=pop(@finishResource);
                    503: 
                    504: 	    if(defined($CacheData{$currentSequence.':problems'})) {
                    505: 		# Capture sequence information here
                    506: 		if(!defined($CacheData{'orderedSequences'})) {
                    507: 		    $CacheData{'orderedSequences'}=$currentSequence;
                    508: 		} else {
                    509: 		    $CacheData{'orderedSequences'}.=':'.$currentSequence;
                    510: 		}
                    511: 
                    512: 		$CacheData{$currentSequence.':title'}=
                    513: 		    $hash{'title_'.$currentResourceID};
                    514: 
                    515:                 my $totalProblems=0;
1.47    ! stredwic  516:                 foreach my $currentProblem (split(/\:/,
        !           517:                                                $CacheData{$currentSequence.
1.44      stredwic  518:                                                ':problems'})) {
1.47    ! stredwic  519:                     foreach (split(/\:/,$CacheData{$currentSequence.':'.
        !           520:                                                    $currentProblem.
        !           521:                                                    ':parts'})) {
1.44      stredwic  522:                         $totalProblems++;
                    523:                     }
                    524:                 }
                    525: 		my @titleLength=split(//,$CacheData{$currentSequence.
                    526:                                                     ':title'});
                    527:                 # $extra is 3 for problems correct and 3 for space
                    528:                 # between problems correct and problem output
                    529:                 my $extra = 6;
                    530: 		if(($totalProblems + $extra) > (scalar @titleLength)) {
                    531: 		    $CacheData{$currentSequence.':columnWidth'}=
                    532:                         $totalProblems + $extra;
                    533: 		} else {
                    534: 		    $CacheData{$currentSequence.':columnWidth'}=
                    535:                         (scalar @titleLength);
                    536: 		}
                    537: 	    }
                    538: 
                    539: 	    $currentSequence=pop(@sequences);
                    540: 	    if($currentSequence eq $topLevelSequenceNumber) {
                    541: 		last;
                    542: 	    }
                    543: 	#else
                    544: 	}
1.43      stredwic  545: 
1.44      stredwic  546: 	# MOVE!!!
                    547: 	#move to next resource
                    548: 	unless(defined($hash{'to_'.$currentResourceID})) {
                    549: 	    # big problem, need to handle.  Next is probably wrong
                    550: 	    last;
                    551: 	}
                    552: 	my @nextResources=();
                    553: 	foreach (split(/\,/,$hash{'to_'.$currentResourceID})) {
                    554: 	    push(@nextResources, $hash{'goesto_'.$_});
                    555: 	}
                    556: 	push(@currentResource, @nextResources);
1.46      stredwic  557: 	# Set the next resource to be processed
                    558: 	$currentResourceID=pop(@currentResource);
1.44      stredwic  559:     }
1.5       minaeibi  560: 
1.44      stredwic  561:     unless (untie(%hash)) {
                    562:         &Apache::lonnet::logthis("<font color=blue>WARNING: ".
                    563:                                  "Could not untie coursemap $fn (browse)".
                    564:                                  ".</font>"); 
                    565:     }
1.1       www       566: 
1.44      stredwic  567:     unless (untie(%CacheData)) {
                    568:         &Apache::lonnet::logthis("<font color=blue>WARNING: ".
                    569:                                  "Could not untie Cache Hash (browse)".
                    570:                                  ".</font>"); 
1.1       www       571:     }
1.44      stredwic  572: 
                    573:     return 'OK';
1.1       www       574: }
1.33      minaeibi  575: 
1.44      stredwic  576: sub ProcessSection {
                    577:     my ($sectionData, $courseid,$ActiveFlag)=@_;
1.33      minaeibi  578:     $courseid=~s/\_/\//g;
                    579:     $courseid=~s/^(\w)/\/$1/;
1.39      stredwic  580: 
1.41      albertel  581:     my $cursection='-1';
                    582:     my $oldsection='-1';
                    583:     my $status='Expired';
1.44      stredwic  584:     my $section='';
                    585:     foreach my $key (keys (%$sectionData)) {
                    586: 	my $value = $sectionData->{$key};
1.33      minaeibi  587:         if ($key=~/^$courseid(?:\/)*(\w+)*\_st$/) {
1.44      stredwic  588: 	    $section=$1;
                    589: 	    if($key eq $courseid.'_st') {
                    590: 		$section='';
                    591: 	    }
1.39      stredwic  592: 	    my ($dummy,$end,$start)=split(/\_/,$value);
1.41      albertel  593: 	    my $now=time;
                    594: 	    my $notactive=0;
1.43      stredwic  595: 	    if ($start) {
                    596: 		if($now<$start) {
                    597: 		    $notactive=1;
                    598: 		}
                    599: 	    }
                    600: 	    if($end) {
                    601: 		if ($now>$end) {
                    602: 		    $notactive=1;
                    603: 		}
                    604: 	    }
                    605: 	    if($notactive == 0) {
                    606: 		$status='Active';
                    607: 		$cursection=$section;
1.44      stredwic  608: 		last;
1.43      stredwic  609: 	    }
                    610: 	    if($notactive == 1) {
                    611: 		$oldsection=$section;
                    612: 	    }
                    613: 	}
                    614:     }
                    615:     if($status eq $ActiveFlag) {
                    616: 	if($cursection eq '-1') {
                    617: 	    return $oldsection;
                    618: 	}
                    619: 	return $cursection;
                    620:     }
                    621:     if($ActiveFlag eq 'Any') {
                    622: 	if($cursection eq '-1') {
                    623: 	    return $oldsection;
                    624: 	}
                    625: 	return $cursection;
1.41      albertel  626:     }
1.36      minaeibi  627:     return '-1';
1.33      minaeibi  628: }
                    629: 
1.44      stredwic  630: sub ProcessStudentInformation {
                    631:     my ($CacheData,$studentInformation,$section,$date,$name,$courseID,$c)=@_;
                    632:     my ($studentName,$studentDomain) = split(/\:/,$name);
                    633: 
                    634:     $CacheData->{$name.':username'}=$studentName;
                    635:     $CacheData->{$name.':domain'}=$studentDomain;
                    636:     $CacheData->{$name.':date'}=$date;
                    637: 
                    638:     my ($checkForError)=keys(%$studentInformation);
                    639:     if($checkForError =~ /^(con_lost|error|no_such_host)/i) {
                    640: 	$CacheData->{$name.':error'}=
                    641: 	    'Could not download student environment data.';
                    642: 	$CacheData->{$name.':fullname'}='';
                    643: 	$CacheData->{$name.':id'}='';
                    644:     } else {
                    645: 	$CacheData->{$name.':fullname'}=&ProcessFullName(
                    646:                                           $studentInformation->{'lastname'},
                    647: 				          $studentInformation->{'generation'},
                    648: 				          $studentInformation->{'firstname'},
                    649:                                           $studentInformation->{'middlename'});
                    650: 	$CacheData->{$name.':id'}=$studentInformation->{'id'};
                    651:     }
                    652: 
                    653:     # Get student's section number
                    654:     my $sec=&ProcessSection($section, $courseID, $ENV{'form.status'});
                    655:     if($sec != -1) {
                    656: 	$CacheData->{$name.':section'}=$sec;
                    657:     } else {
                    658: 	$CacheData->{$name.':section'}='';
                    659:     }
                    660: 
                    661:     return 0;
                    662: }
                    663: 
                    664: sub ProcessClassList {
                    665:     my ($classlist,$courseID,$ChartDB,$c)=@_;
                    666:     my @names=();
                    667: 
                    668:     my %CacheData;
                    669:     if(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) {
                    670:         foreach my $name (keys(%$classlist)) {
                    671:             if($name =~ /\:section/ || $name =~ /\:studentInformation/) {
                    672:                 next;
                    673:             }
                    674:             if($c->aborted()) {
                    675:                 last;
                    676:             }
                    677:             push(@names,$name);
                    678:             &ProcessStudentInformation(
                    679:                                     \%CacheData,
                    680:                                     $classlist->{$name.':studentInformation'},
                    681:                                     $classlist->{$name.':section'},
                    682:                                     $classlist->{$name},
                    683:                                     $name,$courseID,$c);
                    684:         }
                    685: 
                    686:         $CacheData{'NamesOfStudents'}=join(":::",@names);
                    687: #        $CacheData{'NamesOfStudents'}=&Apache::lonnet::arrayref2str(\@names);
                    688: 	untie(%CacheData);
                    689:     }
                    690: 
                    691:     return @names;
                    692: }
                    693: 
                    694: # ----- END PROCESSING FUNCTIONS ---------------------------------------
                    695: 
                    696: # ----- HELPER FUNCTIONS -----------------------------------------------
                    697: 
                    698: sub SpaceColumns {
                    699:     my ($students,$studentInformation,$headings,$ChartDB)=@_;
                    700: 
                    701:     my %CacheData;
                    702:     if(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) {
                    703:         # Initialize Lengths
                    704:         for(my $index=0; $index<(scalar @$headings); $index++) {
                    705: 	    my @titleLength=split(//,$$headings[$index]);
                    706: 	    $CacheData{$$studentInformation[$index].'Length'}=
                    707:                 scalar @titleLength;
                    708: 	}
                    709: 
                    710:         foreach my $name (@$students) {
                    711:             foreach (@$studentInformation) {
                    712: 		my @dataLength=split(//,$CacheData{$name.':'.$_});
                    713: 		my $length=scalar @dataLength;
                    714: 		if($length > $CacheData{$_.'Length'}) {
                    715: 		    $CacheData{$_.'Length'}=$length;
                    716: 		}
                    717:             }
                    718:         }
                    719:         untie(%CacheData);
                    720:     }
                    721: 
                    722:     return;
                    723: }
                    724: 
1.43      stredwic  725: sub ProcessFullName {
1.44      stredwic  726:     my ($lastname, $generation, $firstname, $middlename)=@_;
1.43      stredwic  727:     my $Str = '';
                    728: 
1.44      stredwic  729:     if($lastname ne '') {
                    730: 	$Str .= $lastname.' ';
                    731: 	if($generation ne '') {
                    732: 	    $Str .= $generation;
1.43      stredwic  733: 	} else {
                    734: 	    chop($Str);
                    735: 	}
                    736: 	$Str .= ', ';
1.44      stredwic  737: 	if($firstname ne '') {
                    738: 	    $Str .= $firstname.' ';
1.43      stredwic  739: 	}
1.44      stredwic  740: 	if($middlename ne '') {
                    741: 	    $Str .= $middlename;
1.40      stredwic  742: 	} else {
1.43      stredwic  743: 	    chop($Str);
1.44      stredwic  744: 	    if($firstname eq '') {
1.43      stredwic  745: 		chop($Str);
1.31      minaeibi  746: 	    }
1.30      minaeibi  747: 	}
1.43      stredwic  748:     } else {
1.44      stredwic  749: 	if($firstname ne '') {
                    750: 	    $Str .= $firstname.' ';
1.43      stredwic  751: 	}
1.44      stredwic  752: 	if($middlename ne '') {
                    753: 	    $Str .= $middlename.' ';
1.43      stredwic  754: 	}
1.44      stredwic  755: 	if($generation ne '') {
                    756: 	    $Str .= $generation;
1.43      stredwic  757: 	} else {
                    758: 	    chop($Str);
                    759: 	}
                    760:     }
                    761: 
                    762:     return $Str;
                    763: }
1.30      minaeibi  764: 
1.44      stredwic  765: sub SortStudents {
                    766:     my ($CacheData)=@_;
                    767:     my @students = split(/:::/,$CacheData->{'NamesOfStudents'});
                    768: #    my @students=&Apache::lonnet::str2array($CacheData->{'NamesOfStudents'});
                    769: 
                    770:     my @sorted1Students=();
                    771:     foreach (@students) {
                    772:         my ($end,$start)=split(/\:/,$CacheData->{$_.':date'});
                    773:         my $active=1;
                    774:         my $now=time;
                    775:         my $Status=$ENV{'form.status'};
                    776:         $Status = ($Status) ? $Status : 'Active';
                    777:         if((($end) && $now > $end) && (($Status eq 'Active'))) { 
                    778:             $active=0; 
                    779:         }
                    780:         if(($Status eq 'Expired') && ($end == 0 || $now < $end)) {
                    781:             $active=0;
                    782:         }
                    783:         if($active) {
                    784:             push(@sorted1Students, $_);
                    785:         }
1.43      stredwic  786:     }
1.1       www       787: 
1.43      stredwic  788:     my $Pos = $ENV{'form.sort'};
                    789:     my %sortData;
                    790:     if($Pos eq 'Last Name') {
1.44      stredwic  791: 	for(my $index=0; $index<scalar @sorted1Students; $index++) {
                    792: 	    $sortData{$CacheData->{$sorted1Students[$index].':fullname'}}=
                    793: 		$sorted1Students[$index];
1.43      stredwic  794: 	}
                    795:     } elsif($Pos eq 'Section') {
1.44      stredwic  796: 	for(my $index=0; $index<scalar @sorted1Students; $index++) {
                    797: 	    $sortData{$CacheData->{$sorted1Students[$index].':section'}.
                    798: 		      $sorted1Students[$index]}=$sorted1Students[$index];
1.43      stredwic  799: 	}
                    800:     } else {
                    801: 	# Sort by user name
1.44      stredwic  802: 	for(my $index=0; $index<scalar @sorted1Students; $index++) {
                    803: 	    $sortData{$sorted1Students[$index]}=$sorted1Students[$index];
1.43      stredwic  804: 	}
                    805:     }
                    806: 
                    807:     my @order = ();
                    808:     foreach my $key (sort keys(%sortData)) {
                    809: 	push (@order,$sortData{$key});
                    810:     }
1.33      minaeibi  811: 
1.43      stredwic  812:     return @order;
1.30      minaeibi  813: }
1.1       www       814: 
1.44      stredwic  815: sub TestCacheData {
                    816:     my ($ChartDB)=@_;
                    817:     my $isCached=-1;
                    818:     my %testData;
                    819:     my $tieTries=0;
1.43      stredwic  820: 
1.44      stredwic  821:     if ((-e "$ChartDB") && ($ENV{'form.sort'} ne 'Recalculate Chart')) {
                    822: 	$isCached = 1;
                    823:     } else {
                    824: 	$isCached = 0;
1.43      stredwic  825:     }
                    826: 
1.44      stredwic  827:     while($tieTries < 3) {
                    828:         my $result=0;
                    829:         if($isCached) {
                    830:             $result=tie(%testData,'GDBM_File',$ChartDB,&GDBM_READER,0640);
                    831:         } else {
                    832:             $result=tie(%testData,'GDBM_File',$ChartDB,&GDBM_NEWDB,0640);
                    833:         }
                    834:         if($result) {
                    835:             last;
                    836:         }
                    837:         $tieTries++;
                    838:         sleep 1;
                    839:     }
                    840:     if($tieTries >= 3) {
                    841:         return -1;
1.43      stredwic  842:     }
                    843: 
1.44      stredwic  844:     untie(%testData);
1.30      minaeibi  845: 
1.44      stredwic  846:     return $isCached;
1.43      stredwic  847: }
1.30      minaeibi  848: 
1.44      stredwic  849: sub ExtractStudentData {
                    850:     my ($courseData, $name, $ChartDB)=@_;
                    851: 
                    852:     my %CacheData;
                    853:     if(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) {
                    854:         my ($checkForError) = keys(%$courseData);
                    855:         if($checkForError =~ /^(con_lost|error|no_such_host)/i) {
                    856:             $CacheData{$name.':error'}='Could not download course data.';
                    857:         } else {
                    858:             foreach my $key (keys (%$courseData)) {
                    859:                 $CacheData{$name.':'.$key}=$courseData->{$key};
                    860:             }
                    861:         }
                    862:         untie(%CacheData);
1.30      minaeibi  863:     }
1.1       www       864: 
1.44      stredwic  865:     return;
                    866: }
                    867: 
                    868: # ----- END HELPER FUNCTIONS --------------------------------------------
                    869: 
                    870: sub BuildChart {
                    871:     my ($r)=@_;
                    872:     my $c = $r->connection;
1.1       www       873: 
1.44      stredwic  874:     # Start the lonchart document
                    875:     $r->content_type('text/html');
                    876:     $r->send_http_header;
                    877:     $r->print(&StartDocument());
                    878:     $r->rflush();
1.43      stredwic  879: 
1.44      stredwic  880:     # Test for access to the CacheData
                    881:     my $isCached=0;
1.43      stredwic  882:     my $cid=$ENV{'request.course.id'};
                    883:     my $ChartDB = "/home/httpd/perl/tmp/$ENV{'user.name'}".
                    884:                   "_$ENV{'user.domain'}_$cid\_chart.db";
1.44      stredwic  885: 
                    886:     $isCached=&TestCacheData($ChartDB);
                    887:     if($isCached < 0) {
                    888:         $r->print("Unable to tie hash to db file");
                    889:         $r->rflush();
                    890:         return;
                    891:     }
                    892: 
                    893:     # Download class list information if not using cached data
                    894:     my @students=();
                    895:     my @studentInformation=('username','domain','section','id','fullname');
                    896:     my @headings=('User Name','Domain','Section','PID','Full Name');
                    897:     my $spacePadding='   ';
                    898:     if(!$isCached) {
                    899:         my $processTopResourceMapReturn=&ProcessTopResourceMap($ChartDB,$c);
                    900:         if($processTopResourceMapReturn ne 'OK') {
                    901:             $r->print($processTopResourceMapReturn);
                    902:             return;
                    903:         }
                    904:         if($c->aborted()) { return; }
                    905:         my $classlist=&DownloadPrerequisiteData($cid, $c);
                    906:         my ($checkForError)=keys(%$classlist);
                    907:         if($checkForError =~ /^(con_lost|error|no_such_host)/i ||
                    908:            defined($classlist->{'error'})) {
                    909:             return;
                    910:         }
                    911:         if($c->aborted()) { return; }
                    912:         @students=&ProcessClassList($classlist,$cid,$ChartDB,$c);
                    913:         if($c->aborted()) { return; }
                    914:         &SpaceColumns(\@students,\@studentInformation,\@headings,
                    915:                       $ChartDB);
                    916:         if($c->aborted()) { return; }
                    917:     }
                    918: 
                    919:     # Sort students and print out table desciptive data
                    920:     my %CacheData;
                    921:     if(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_READER,0640)) {
                    922:         if(!$c->aborted()) { @students=&SortStudents(\%CacheData); }
                    923: 	if(!$c->aborted()) { $r->print(&CreateLegend()); }
                    924: 	if(!$c->aborted()) { $r->print(&CreateForm()); }
                    925: 	if(!$c->aborted()) { $r->print('<h3>'.(scalar @students).
                    926:                                        ' students</h3>'); }
                    927: 	if(!$c->aborted()) { $r->rflush(); }
1.46      stredwic  928: #	if(!$c->aborted()) { $r->print(&CreateColumnSelectors(
                    929: #                                                         \%CacheData,
                    930: #                                                         \@studentInformation, 
                    931: #							 \@headings, 
                    932: #							 $spacePadding)); }
1.44      stredwic  933: 	if(!$c->aborted()) { $r->print(&CreateTableHeadings(
                    934:                                                          \%CacheData,
                    935:                                                          \@studentInformation, 
                    936: 							 \@headings, 
                    937: 							 $spacePadding)); }
                    938: 	untie(%CacheData);
1.43      stredwic  939:     } else {
1.44      stredwic  940: 	$r->print("Init2: Unable to tie hash to db file");
                    941: 	return;
1.43      stredwic  942:     }
                    943: 
                    944:     my @updateStudentList = ();
1.44      stredwic  945:     my $courseData;
                    946:     foreach (@students) {
                    947:         if($c->aborted()) {
                    948:             if(!$isCached && 
                    949:                tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) {
                    950:                 $CacheData{'NamesOfStudents'}=join(":::", @updateStudentList);
                    951: #		    $CacheData{'NamesOfStudents'}=
                    952: #		            &Apache::lonnet::arrayref2str(\@updateStudentList);
                    953:                 untie(%CacheData);
                    954:             }
                    955:             last;
                    956:         }
                    957: 
                    958:         if(!$isCached) {
                    959:             $courseData=&DownloadStudentCourseInformation($_, $cid);
                    960:             if($c->aborted()) { next; }
                    961:             push(@updateStudentList, $_);
                    962:             &ExtractStudentData($courseData, $_, $ChartDB);
                    963:         }
                    964:         $r->print(&FormatStudentData($_, $cid, \@studentInformation,
                    965:                                      $spacePadding, $ChartDB));
                    966:         $r->rflush();
1.43      stredwic  967:     }
                    968: 
1.44      stredwic  969:     $r->print('</body></html>');
1.30      minaeibi  970:     $r->rflush();
1.1       www       971: 
1.43      stredwic  972:     return;
1.30      minaeibi  973: }
1.1       www       974: 
1.30      minaeibi  975: # ================================================================ Main Handler
1.1       www       976: 
1.30      minaeibi  977: sub handler {
1.44      stredwic  978:     my $r=shift;
1.46      stredwic  979:     $jr=$r;
1.44      stredwic  980:     unless(&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'})) {
1.30      minaeibi  981: 	$ENV{'user.error.msg'}=
1.1       www       982:         $r->uri.":vgr:0:0:Cannot view grades for complete course";
1.30      minaeibi  983: 	return HTTP_NOT_ACCEPTABLE; 
                    984:     }
1.44      stredwic  985: 
                    986:     # Set document type for header only
                    987:     if ($r->header_only) {
                    988:         if($ENV{'browser.mathml'}) {
                    989:             $r->content_type('text/xml');
                    990:         } else {
                    991:             $r->content_type('text/html');
                    992:         }
                    993:         &Apache::loncommon::no_cache($r);
                    994:         $r->send_http_header;
                    995:         return OK;
                    996:     }
                    997:     
                    998:     unless($ENV{'request.course.fn'}) {
                    999:         my $requrl=$r->uri;
                   1000:         $ENV{'user.error.msg'}="$requrl:bre:0:0:Course not initialized";
                   1001:         return HTTP_NOT_ACCEPTABLE; 
                   1002:     }
                   1003: 
                   1004:     &BuildChart($r);
                   1005: 
                   1006:     return OK;
1.1       www      1007: }
                   1008: 1;
                   1009: __END__

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