# The LearningOnline Network with CAPA # (Publication Handler # # $Id: lonchart.pm,v 1.51 2002/07/02 21:34:40 stredwic Exp $ # # Copyright Michigan State University Board of Trustees # # This file is part of the LearningOnline Network with CAPA (LON-CAPA). # # LON-CAPA is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # LON-CAPA is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with LON-CAPA; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # /home/httpd/html/adm/gpl.txt # # http://www.lon-capa.org/ # # Homework Performance Chart # # (Navigate Maps Handler # # (Page Handler # # (TeX Content Handler # YEAR=2000 # 05/29/00,05/30 Gerd Kortemeyer) # 08/30,08/31,09/06,09/14,09/15,09/16,09/19,09/20,09/21,09/23, # 10/02,10/10,10/14,10/16,10/18,10/19,10/31,11/6,11/14,11/16 Gerd Kortemeyer) # YEAR=2001 # 3/1/1,6/1,17/1,29/1,30/1,31/1 Gerd Kortemeyer) # 7/10/01 Behrouz Minaei # 9/8 Gerd Kortemeyer # 10/1, 10/19, 11/17, 11/22, 11/24, 11/28 12/18 Behrouz Minaei # YEAR=2002 # 2/1, 2/6, 2/19, 2/28 Behrouz Minaei # ### =pod =cut package Apache::lonchart; use strict; use Apache::Constants qw(:common :http); use Apache::lonnet(); use Apache::loncommon(); use HTML::TokeParser; use GDBM_File; #my $jr; # ----- FORMAT PRINT DATA ---------------------------------------------- sub FormatStudentInformation { my ($cache,$name,$studentInformation,$spacePadding)=@_; my $Str=''; for(my $index=0; $index<(scalar @$studentInformation); $index++) { if(!&ShouldShowColumn($cache, 'heading'.$index)) { next; } my $data=$cache->{$name.':'.$studentInformation->[$index]}; $Str .= $data; my @dataLength=split(//,$data); my $length=scalar @dataLength; $Str .= (' 'x($cache->{$studentInformation->[$index].'Length'}- $length)); $Str .= $spacePadding; } return $Str; } sub FormatStudentData { my ($name,$coid,$studentInformation,$spacePadding,$ChartDB)=@_; my ($sname,$sdom) = split(/\:/,$name); my $Str; my %CacheData; unless(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_READER,0640)) { return ''; } # Handle Student information ------------------------------------------ # Handle user data $Str=&FormatStudentInformation(\%CacheData, $name, $studentInformation, $spacePadding); # Handle errors if($CacheData{$name.':error'} =~ /environment/) { $Str .= '
'; untie(%CacheData); return $Str; } if($CacheData{$name.':error'} =~ /course/) { $Str .= '
'; untie(%CacheData); return $Str; } # Handle problem data ------------------------------------------------ my $Version; my $problemsCorrect = 0; my $totalProblems = 0; my $problemsSolved = 0; my $numberOfParts = 0; foreach my $sequence (split(/\:/,$CacheData{'orderedSequences'})) { if(!&ShouldShowColumn(\%CacheData, 'sequence'.$sequence)) { next; } my $characterCount=0; foreach my $problemID (split(/\:/,$CacheData{$sequence.':problems'})) { my $problem = $CacheData{$problemID.':problem'}; my $LatestVersion = $CacheData{$name.":version:$problem"}; if(!$LatestVersion) { foreach my $part (split(/\:/,$CacheData{$sequence.':'. $problemID. ':parts'})) { $Str .= ' '; $totalProblems++; $characterCount++; } next; } my %partData=undef; #initialize data, displays skips correctly foreach my $part (split(/\:/,$CacheData{$sequence.':'. $problemID. ':parts'})) { $partData{$part.':tries'}=0; $partData{$part.':code'}=' '; } for(my $Version=1; $Version<=$LatestVersion; $Version++) { foreach my $part (split(/\:/,$CacheData{$sequence.':'. $problemID. ':parts'})) { if(!defined($CacheData{$name.":$Version:$problem". ":resource.$part.solved"})) { next; } my $tries=0; my $code=' '; $tries = $CacheData{$name.":$Version:$problem". ":resource.$part.tries"}; $partData{$part.':tries'}=($tries) ? $tries : 0; my $val = $CacheData{$name.":$Version:$problem". ":resource.$part.solved"}; if ($val eq 'correct_by_student') {$code = '*';} elsif ($val eq 'correct_by_override') {$code = '+';} elsif ($val eq 'incorrect_attempted') {$code = '.';} elsif ($val eq 'incorrect_by_override'){$code = '-';} elsif ($val eq 'excused') {$code = 'x';} elsif ($val eq 'ungraded_attempted') {$code = '#';} else {$code = ' ';} $partData{$part.':code'}=$code; } } $Str.=''; foreach(split(/\:/,$CacheData{$sequence.':'.$problemID. ':parts'})) { if($partData{$_.':code'} eq '*') { $problemsCorrect++; if (($partData{$_.':tries'}<10) && ($partData{$_.':tries'} ne '')) { $partData{$_.':code'}=$partData{$_.':tries'}; } } elsif($partData{$_.':code'} eq '+') { $problemsCorrect++; } $Str .= $partData{$_.':code'}; $characterCount++; if($partData{$_.':code'} ne 'x') { $totalProblems++; } } $Str.=''; } my $spacesNeeded=$CacheData{$sequence.':columnWidth'}-$characterCount; $spacesNeeded -= 3; $Str .= (' 'x$spacesNeeded); my $outputProblemsCorrect = sprintf( "%3d", $problemsCorrect ); $Str .= ''.$outputProblemsCorrect.''; $problemsSolved += $problemsCorrect; $problemsCorrect=0; $Str .= $spacePadding; } my $outputProblemsSolved = sprintf( "%4d", $problemsSolved ); my $outputTotalProblems = sprintf( "%4d", $totalProblems ); $Str .= ''.$outputProblemsSolved. ' / '.$outputTotalProblems.'
'; untie(%CacheData); return $Str; } sub CreateTableHeadings { my ($CacheData,$studentInformation,$headings,$spacePadding)=@_; my $Str='
';

    for(my $index=0; $index<(scalar @$headings); $index++) {
        if(!&ShouldShowColumn($CacheData, 'heading'.$index)) {
            next;
        }

	my $data=$$headings[$index];
	$Str .= $data;

	my @dataLength=split(//,$data);
	my $length=scalar @dataLength;
	$Str .= (' 'x($CacheData->{$$studentInformation[$index].'Length'}-
                      $length));
	$Str .= $spacePadding;
    }

    foreach my $sequence (split(/\:/,$CacheData->{'orderedSequences'})) {
        if(!&ShouldShowColumn($CacheData, 'sequence'.$sequence)) {
            next;
        }

        my $name = $CacheData->{$sequence.':title'};
	$Str .= $name;
	my @titleLength=split(//,$CacheData->{$sequence.':title'});
	my $leftover=$CacheData->{$sequence.':columnWidth'}-
                     (scalar @titleLength);
	$Str .= (' 'x$leftover);
	$Str .= $spacePadding;
    }

    $Str .= 'Total Solved/Total Problems';
    $Str .= '
'; return $Str; } sub CreateColumnSelectionBox { my ($CacheData,$studentInformation,$headings,$spacePadding)=@_; my $missing=0; my $notThere='Select column to view:'; my $name; $notThere .= ''; $notThere .= ''; } else { $notThere=''; } return $notThere.''; } sub CreateColumnSelectors { my ($CacheData,$studentInformation,$headings,$spacePadding)=@_; my $found=0; my ($name, $length, $position); my $present='
';
    for(my $index=0; $index<(scalar @$headings); $index++) {
        if(!&ShouldShowColumn($CacheData, 'heading'.$index)) {
            next;
        }
        $name = $headings->[$index];
        $length=$CacheData->{$$studentInformation[$index].'Length'};
        $position=int($length/2);
	$present .= (' 'x($position));
        $present .= '';
        $position+=2;
	$present .= (' 'x($length-$position));
	$present .= $spacePadding;
        $found++;
    }

    foreach my $sequence (split(/\:/,$CacheData->{'orderedSequences'})) {
        if(!&ShouldShowColumn($CacheData, 'sequence'.$sequence)) {
            next;
        }
        $name = $CacheData->{$sequence.':title'};
        $length=$CacheData->{$sequence.':columnWidth'};
        $position=int($length/2);
	$present .= (' 'x($position));
        $present .= '';
        $position+=2;
	$present .= (' 'x($length-$position));
	$present .= $spacePadding;
        $found++;
    }

    if($found) {
        $present .= '
'; $present = $present; } else { $present = ''; } return $present.''."\n";; } sub CreateForm { my ($CacheData)=@_; my $OpSel1=''; my $OpSel2=''; my $OpSel3=''; my $Status = $CacheData->{'form.status'}; if ( $Status eq 'Any' ) { $OpSel3='selected'; } elsif ($Status eq 'Expired' ) { $OpSel2 = 'selected'; } else { $OpSel1 = 'selected'; } my $Ptr .= '
'."\n"; $Ptr .= ''; $Ptr .= ''; return $Ptr; } sub CreateLegend { my $Str = "

".
              "1..9: correct by student in 1..9 tries\n".
              "   *: correct by student in more than 9 tries\n".
	      "   +: correct by override\n".
              "   -: incorrect by override\n".
	      "   .: incorrect attempted\n".
	      "   #: ungraded attempted\n".
              "    : not attempted\n".
	      "   x: excused".
              "

"; return $Str; } sub StartDocument { my $Str = ''; $Str .= ''; $Str .= ''; $Str .= 'LON-CAPA Assessment Chart'; $Str .= ''; $Str .= ''; $Str .= ''; $Str .= '

Assessment Chart'.(' 'x8).localtime();
    $Str .= '

'; $Str .= '

'.$ENV{'course.'.$ENV{'request.course.id'}.'.description'}; $Str .= '

'; return $Str; } # ----- END FORMAT PRINT DATA ------------------------------------------ # ----- DOWNLOAD INFORMATION ------------------------------------------- sub DownloadPrerequisiteData { my ($courseID, $c)=@_; my ($courseDomain,$courseNumber)=split(/\_/,$courseID); my %classlist=&Apache::lonnet::dump('classlist',$courseDomain, $courseNumber); my ($checkForError)=keys (%classlist); if($checkForError =~ /^(con_lost|error|no_such_host)/i) { return \%classlist; } foreach my $name (keys(%classlist)) { if($c->aborted()) { $classlist{'error'}='aborted'; return \%classlist; } my ($studentName,$studentDomain) = split(/\:/,$name); # Download student environment data, specifically the full name and id. my %studentInformation=&Apache::lonnet::get('environment', ['lastname','generation', 'firstname','middlename', 'id'], $studentDomain, $studentName); $classlist{$name.':studentInformation'}=\%studentInformation; if($c->aborted()) { $classlist{'error'}='aborted'; return \%classlist; } #Section my %section=&Apache::lonnet::dump('roles',$studentDomain,$studentName); $classlist{$name.':section'}=\%section; } return \%classlist; } sub DownloadStudentCourseInformation { my ($name,$courseID)=@_; my ($studentName,$studentDomain) = split(/\:/,$name); # Download student course data my %courseData=&Apache::lonnet::dump($courseID,$studentDomain, $studentName); return \%courseData; } # ----- END DOWNLOAD INFORMATION --------------------------------------- # ----- END PROCESSING FUNCTIONS --------------------------------------- sub ProcessTopResourceMap { my ($ChartDB,$c)=@_; my %hash; my $fn=$ENV{'request.course.fn'}; if(-e "$fn.db") { my $tieTries=0; while($tieTries < 3) { if(tie(%hash,'GDBM_File',"$fn.db",&GDBM_READER,0640)) { last; } $tieTries++; sleep 1; } if($tieTries >= 3) { return 'Coursemap undefined.'; } } else { return 'Can not open Coursemap.'; } my %CacheData; unless(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) { untie(%hash); return 'Could not tie cache hash.'; } my (@sequences, @currentResource, @finishResource); my ($currentSequence, $currentResourceID, $lastResourceID); $currentResourceID=$hash{'ids_/res/'.$ENV{'request.course.uri'}}; push(@currentResource, $currentResourceID); $lastResourceID=-1; $currentSequence=-1; my $topLevelSequenceNumber = $currentSequence; while(1) { if($c->aborted()) { last; } # HANDLE NEW SEQUENCE! #if page || sequence if(defined($hash{'map_pc_'.$hash{'src_'.$currentResourceID}})) { push(@sequences, $currentSequence); push(@currentResource, $currentResourceID); push(@finishResource, $lastResourceID); $currentSequence=$hash{'map_pc_'.$hash{'src_'.$currentResourceID}}; # Mark sequence as containing problems. If it doesn't, then # it will be removed when processing for this sequence is # complete. This allows the problems in a sequence # to be outputed before problems in the subsequences if(!defined($CacheData{'orderedSequences'})) { $CacheData{'orderedSequences'}=$currentSequence; } else { $CacheData{'orderedSequences'}.=':'.$currentSequence; } $lastResourceID=$hash{'map_finish_'. $hash{'src_'.$currentResourceID}}; $currentResourceID=$hash{'map_start_'. $hash{'src_'.$currentResourceID}}; if(!($currentResourceID) || !($lastResourceID)) { $currentSequence=pop(@sequences); $currentResourceID=pop(@currentResource); $lastResourceID=pop(@finishResource); if($currentSequence eq $topLevelSequenceNumber) { last; } } } # Handle gradable resources: exams, problems, etc $currentResourceID=~/(\d+)\.(\d+)/; my $partA=$1; my $partB=$2; if($hash{'src_'.$currentResourceID}=~ /\.(problem|exam|quiz|assess|survey|form)$/ && $partA eq $currentSequence) { my $Problem = &Apache::lonnet::symbclean( &Apache::lonnet::declutter($hash{'map_id_'.$partA}). '___'.$partB.'___'. &Apache::lonnet::declutter($hash{'src_'. $currentResourceID})); $CacheData{$currentResourceID.':problem'}=$Problem; if(!defined($CacheData{$currentSequence.':problems'})) { $CacheData{$currentSequence.':problems'}=$currentResourceID; } else { $CacheData{$currentSequence.':problems'}.= ':'.$currentResourceID; } #Get Parts for problem my $meta=$hash{'src_'.$currentResourceID}; foreach (split(/\,/,&Apache::lonnet::metadata($meta,'keys'))) { if($_=~/^stores\_(\d+)\_tries$/) { my $Part=&Apache::lonnet::metadata($meta,$_.'.part'); if(!defined($CacheData{$currentSequence.':'. $currentResourceID.':parts'})) { $CacheData{$currentSequence.':'.$currentResourceID. ':parts'}=$Part; } else { $CacheData{$currentSequence.':'.$currentResourceID. ':parts'}.=':'.$Part; } } } } #if resource == finish resource if($currentResourceID eq $lastResourceID) { #pop off last resource of sequence $currentResourceID=pop(@currentResource); $lastResourceID=pop(@finishResource); if(defined($CacheData{$currentSequence.':problems'})) { # Capture sequence information here $CacheData{$currentSequence.':title'}= $hash{'title_'.$currentResourceID}; my $totalProblems=0; foreach my $currentProblem (split(/\:/, $CacheData{$currentSequence. ':problems'})) { foreach (split(/\:/,$CacheData{$currentSequence.':'. $currentProblem. ':parts'})) { $totalProblems++; } } my @titleLength=split(//,$CacheData{$currentSequence. ':title'}); # $extra is 3 for problems correct and 3 for space # between problems correct and problem output my $extra = 6; if(($totalProblems + $extra) > (scalar @titleLength)) { $CacheData{$currentSequence.':columnWidth'}= $totalProblems + $extra; } else { $CacheData{$currentSequence.':columnWidth'}= (scalar @titleLength); } } else { $CacheData{'orderedSequences'}=~s/$currentSequence//; $CacheData{'orderedSequences'}=~s/::/:/g; $CacheData{'orderedSequences'}=~s/^:|:$//g; } $currentSequence=pop(@sequences); if($currentSequence eq $topLevelSequenceNumber) { last; } } # MOVE!!! #move to next resource unless(defined($hash{'to_'.$currentResourceID})) { # big problem, need to handle. Next is probably wrong last; } my @nextResources=(); foreach (split(/\,/,$hash{'to_'.$currentResourceID})) { push(@nextResources, $hash{'goesto_'.$_}); } push(@currentResource, @nextResources); # Set the next resource to be processed $currentResourceID=pop(@currentResource); } unless (untie(%hash)) { &Apache::lonnet::logthis("WARNING: ". "Could not untie coursemap $fn (browse)". "."); } unless (untie(%CacheData)) { &Apache::lonnet::logthis("WARNING: ". "Could not untie Cache Hash (browse)". "."); } return 'OK'; } sub ProcessSection { my ($sectionData, $courseid,$ActiveFlag)=@_; $courseid=~s/\_/\//g; $courseid=~s/^(\w)/\/$1/; my $cursection='-1'; my $oldsection='-1'; my $status='Expired'; my $section=''; foreach my $key (keys (%$sectionData)) { my $value = $sectionData->{$key}; if ($key=~/^$courseid(?:\/)*(\w+)*\_st$/) { $section=$1; if($key eq $courseid.'_st') { $section=''; } my ($dummy,$end,$start)=split(/\_/,$value); my $now=time; my $notactive=0; if ($start) { if($now<$start) { $notactive=1; } } if($end) { if ($now>$end) { $notactive=1; } } if($notactive == 0) { $status='Active'; $cursection=$section; last; } if($notactive == 1) { $oldsection=$section; } } } if($status eq $ActiveFlag) { if($cursection eq '-1') { return $oldsection; } return $cursection; } if($ActiveFlag eq 'Any') { if($cursection eq '-1') { return $oldsection; } return $cursection; } return '-1'; } sub ProcessStudentInformation { my ($CacheData,$studentInformation,$section,$date,$name,$courseID,$c)=@_; my ($studentName,$studentDomain) = split(/\:/,$name); $CacheData->{$name.':username'}=$studentName; $CacheData->{$name.':domain'}=$studentDomain; $CacheData->{$name.':date'}=$date; my ($checkForError)=keys(%$studentInformation); if($checkForError =~ /^(con_lost|error|no_such_host)/i) { $CacheData->{$name.':error'}= 'Could not download student environment data.'; $CacheData->{$name.':fullname'}=''; $CacheData->{$name.':id'}=''; } else { $CacheData->{$name.':fullname'}=&ProcessFullName( $studentInformation->{'lastname'}, $studentInformation->{'generation'}, $studentInformation->{'firstname'}, $studentInformation->{'middlename'}); $CacheData->{$name.':id'}=$studentInformation->{'id'}; } # Get student's section number my $sec=&ProcessSection($section, $courseID, $CacheData->{'form.status'}); if($sec != -1) { $CacheData->{$name.':section'}=$sec; } else { $CacheData->{$name.':section'}=''; } return 0; } sub ProcessClassList { my ($classlist,$courseID,$ChartDB,$c)=@_; my @names=(); my %CacheData; if(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) { foreach my $name (keys(%$classlist)) { if($name =~ /\:section/ || $name =~ /\:studentInformation/ || $name eq '') { next; } if($c->aborted()) { last; } push(@names,$name); &ProcessStudentInformation( \%CacheData, $classlist->{$name.':studentInformation'}, $classlist->{$name.':section'}, $classlist->{$name}, $name,$courseID,$c); } untie(%CacheData); } return @names; } # ----- END PROCESSING FUNCTIONS --------------------------------------- # ----- HELPER FUNCTIONS ----------------------------------------------- sub SpaceColumns { my ($students,$studentInformation,$headings,$ChartDB)=@_; my %CacheData; if(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) { # Initialize Lengths for(my $index=0; $index<(scalar @$headings); $index++) { my @titleLength=split(//,$$headings[$index]); $CacheData{$$studentInformation[$index].'Length'}= scalar @titleLength; } foreach my $name (@$students) { foreach (@$studentInformation) { my @dataLength=split(//,$CacheData{$name.':'.$_}); my $length=scalar @dataLength; if($length > $CacheData{$_.'Length'}) { $CacheData{$_.'Length'}=$length; } } } untie(%CacheData); } return; } sub ProcessFullName { my ($lastname, $generation, $firstname, $middlename)=@_; my $Str = ''; if($lastname ne '') { $Str .= $lastname.' '; if($generation ne '') { $Str .= $generation; } else { chop($Str); } $Str .= ', '; if($firstname ne '') { $Str .= $firstname.' '; } if($middlename ne '') { $Str .= $middlename; } else { chop($Str); if($firstname eq '') { chop($Str); } } } else { if($firstname ne '') { $Str .= $firstname.' '; } if($middlename ne '') { $Str .= $middlename.' '; } if($generation ne '') { $Str .= $generation; } else { chop($Str); } } return $Str; } sub SortStudents { my ($students,$CacheData)=@_; my @sorted1Students=(); foreach (@$students) { my ($end,$start)=split(/\:/,$CacheData->{$_.':date'}); my $active=1; my $now=time; my $Status=$CacheData->{'form.status'}; $Status = ($Status) ? $Status : 'Active'; if((($end) && $now > $end) && (($Status eq 'Active'))) { $active=0; } if(($Status eq 'Expired') && ($end == 0 || $now < $end)) { $active=0; } if($active) { push(@sorted1Students, $_); } } my $Pos = $CacheData->{'form.sort'}; my %sortData; if($Pos eq 'Last Name') { for(my $index=0; $index{$sorted1Students[$index].':fullname'}}= $sorted1Students[$index]; } } elsif($Pos eq 'Section') { for(my $index=0; $index{$sorted1Students[$index].':section'}. $sorted1Students[$index]}=$sorted1Students[$index]; } } else { # Sort by user name for(my $index=0; $index= 10) { return -1; } untie(%testData); return $isCached; } sub ExtractStudentData { my ($courseData, $name, $ChartDB)=@_; my %CacheData; if(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) { my ($checkForError) = keys(%$courseData); if($checkForError =~ /^(con_lost|error|no_such_host)/i) { $CacheData{$name.':error'}='Could not download course data.'; } else { foreach my $key (keys (%$courseData)) { $CacheData{$name.':'.$key}=$courseData->{$key}; } if(defined($CacheData{'NamesOfStudents'})) { $CacheData{'NamesOfStudents'}.=':::'.$name; } else { $CacheData{'NamesOfStudents'}=$name; } } untie(%CacheData); } return; } sub ShouldShowColumn { my ($cache,$test)=@_; if($cache->{'form.reset'} eq 'true') { return 1; } my $headings=$cache->{'form.headings'}; my $sequences=$cache->{'form.sequences'}; if($headings eq 'ALLHEADINGS' || $sequences eq 'ALLSEQUENCES' || $headings=~/$test/ || $sequences=~/$test/) { return 1; } # my $reselected=$cache->{'form.reselect'}; # if($reselected=~/$test/) { # return 1; # } return 0; } sub ProcessFormData { my ($ChartDB)=@_; my %CacheData; if(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) { if(defined($ENV{'form.sort'})) { $CacheData{'form.sort'}=$ENV{'form.sort'}; } elsif(!defined($CacheData{'form.sort'})) { $CacheData{'form.sort'}='username'; } # Ignore $ENV{'form.refresh'} # Ignore $ENV{'form.recalculate'} if(defined($ENV{'form.status'})) { $CacheData{'form.status'}=$ENV{'form.status'}; } elsif(!defined($CacheData{'form.status'})) { $CacheData{'form.status'}='Active'; } my @headings=(); my @sequences=(); my $found=0; foreach (keys(%ENV)) { if(/form\.heading/) { $found++; push(@headings, $_); } elsif(/form\.sequence/) { $found++; push(@sequences, $_); } elsif(/form\./) { $found++; } } if($found) { $CacheData{'form.headings'}=join(":::",@headings); $CacheData{'form.sequences'}=join(":::",@sequences); } if(defined($ENV{'form.reselect'})) { my @reselected = (ref($ENV{'form.reselect'}) ? @{$ENV{'form.reselect'}} : ($ENV{'form.reselect'})); foreach (@reselected) { if(/heading/) { $CacheData{'form.headings'}.=":::".$_; } elsif(/sequence/) { $CacheData{'form.sequences'}.=":::".$_; } } } if(defined($ENV{'form.reset'})) { $CacheData{'form.reset'}='true'; $CacheData{'form.status'}='Active'; $CacheData{'form.sort'}='username'; $CacheData{'form.headings'}='ALLHEADINGS'; $CacheData{'form.sequences'}='ALLSEQUENCES'; } else { $CacheData{'form.reset'}='false'; } untie(%CacheData); } return; } # ----- END HELPER FUNCTIONS -------------------------------------------- sub BuildChart { my ($r)=@_; my $c = $r->connection; # Start the lonchart document $r->content_type('text/html'); $r->send_http_header; $r->print(&StartDocument()); $r->rflush(); # Test for access to the CacheData my $isCached=0; my $cid=$ENV{'request.course.id'}; my $ChartDB = "/home/httpd/perl/tmp/$ENV{'user.name'}". "_$ENV{'user.domain'}_$cid\_chart.db"; $isCached=&TestCacheData($ChartDB); if($isCached < 0) { $r->print("Unable to tie hash to db file"); $r->rflush(); return; } &ProcessFormData($ChartDB); # Download class list information if not using cached data my %CacheData; my @students=(); my @studentInformation=('username','domain','section','id','fullname'); my @headings=('User Name','Domain','Section','PID','Full Name'); my $spacePadding=' '; if(!$isCached) { my $processTopResourceMapReturn=&ProcessTopResourceMap($ChartDB,$c); if($processTopResourceMapReturn ne 'OK') { $r->print($processTopResourceMapReturn); return; } if($c->aborted()) { return; } my $classlist=&DownloadPrerequisiteData($cid, $c); my ($checkForError)=keys(%$classlist); if($checkForError =~ /^(con_lost|error|no_such_host)/i || defined($classlist->{'error'})) { return; } if($c->aborted()) { return; } @students=&ProcessClassList($classlist,$cid,$ChartDB,$c); if($c->aborted()) { return; } &SpaceColumns(\@students,\@studentInformation,\@headings, $ChartDB); if($c->aborted()) { return; } } else { if(!$c->aborted() && tie(%CacheData,'GDBM_File',$ChartDB, &GDBM_READER,0640)) { @students=split(/:::/,$CacheData{'NamesOfStudents'}); } } # Sort students and print out table desciptive data if(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_READER,0640)) { if(!$c->aborted()) { @students=&SortStudents(\@students,\%CacheData); } if(!$c->aborted()) { $r->print('

'.(scalar @students). ' students

'); } if(!$c->aborted()) { $r->rflush(); } if(!$c->aborted()) { $r->print(&CreateLegend()); } if(!$c->aborted()) { $r->print(&CreateForm(\%CacheData)); } if(!$c->aborted()) { $r->print(&CreateColumnSelectionBox( \%CacheData, \@studentInformation, \@headings, $spacePadding)); } if(!$c->aborted()) { $r->print(&CreateColumnSelectors( \%CacheData, \@studentInformation, \@headings, $spacePadding)); } if(!$c->aborted()) { $r->print(&CreateTableHeadings( \%CacheData, \@studentInformation, \@headings, $spacePadding)); } if(!$c->aborted()) { $r->rflush(); } untie(%CacheData); } else { $r->print("Init2: Unable to tie hash to db file"); return; } my @updateStudentList = (); my $courseData; $r->print('
');
    foreach (@students) {
        if($c->aborted()) {
            last;
        }

        if(!$isCached) {
            $courseData=&DownloadStudentCourseInformation($_, $cid);
            if($c->aborted()) { last; }
            push(@updateStudentList, $_);
            &ExtractStudentData($courseData, $_, $ChartDB);
        }
        $r->print(&FormatStudentData($_, $cid, \@studentInformation,
                                     $spacePadding, $ChartDB));
        $r->rflush();
    }

    if(!$isCached && tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) {
        $CacheData{'NamesOfStudents'}=join(":::", @updateStudentList);
#		    $CacheData{'NamesOfStudents'}=
#		            &Apache::lonnet::arrayref2str(\@updateStudentList);
        untie(%CacheData);
    }

    $r->print('
'); $r->rflush(); return; } # ================================================================ Main Handler sub handler { my $r=shift; # $jr=$r; unless(&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'})) { $ENV{'user.error.msg'}= $r->uri.":vgr:0:0:Cannot view grades for complete course"; return HTTP_NOT_ACCEPTABLE; } # Set document type for header only if ($r->header_only) { if($ENV{'browser.mathml'}) { $r->content_type('text/xml'); } else { $r->content_type('text/html'); } &Apache::loncommon::no_cache($r); $r->send_http_header; return OK; } unless($ENV{'request.course.fn'}) { my $requrl=$r->uri; $ENV{'user.error.msg'}="$requrl:bre:0:0:Course not initialized"; return HTTP_NOT_ACCEPTABLE; } &BuildChart($r); return OK; } 1; __END__
'; $Ptr .= ''; $Ptr .= ''. ' '."\n"; $Ptr .= '