Diff for /loncom/interface/loncoursedata.pm between versions 1.1 and 1.15

version 1.1, 2002/07/09 15:43:49 version 1.15, 2002/08/14 13:16:31
Line 51  package Apache::loncoursedata; Line 51  package Apache::loncoursedata;
 use strict;  use strict;
 use Apache::Constants qw(:common :http);  use Apache::Constants qw(:common :http);
 use Apache::lonnet();  use Apache::lonnet();
   use Apache::lonhtmlcommon;
 use HTML::TokeParser;  use HTML::TokeParser;
 use GDBM_File;  use GDBM_File;
   
Line 70  here in the future. Line 71  here in the future.
   
 =pod  =pod
   
 =item &DownloadNamePIDSection()  =item &DownloadClasslist()
   
 Collects lastname, generation, middlename, firstname, PID, and section for each  Collects lastname, generation, middlename, firstname, PID, and section for each
 student from their environment database.  The list of students is built from  student from their environment database.  The list of students is built from
Line 101  middlename, and PID : Key is $name.'stud Line 102  middlename, and PID : Key is $name.'stud
   
 =cut  =cut
   
 sub DownloadStudentNamePIDSection {  sub DownloadClasslist {
     my ($courseID, $c)=@_;      my ($courseID, $lastDownloadTime, $c)=@_;
     my ($courseDomain,$courseNumber)=split(/\_/,$courseID);      my ($courseDomain,$courseNumber)=split(/\_/,$courseID);
       my %classlist;
   
     my %classlist=&Apache::lonnet::dump('classlist',$courseDomain,      my $modifiedTime = &GetFileTimestamp($courseDomain, $courseNumber,
                                         $courseNumber);                                       'classlist.db', 
                                        $Apache::lonnet::perlvar{'lonUsersDir'});
   
       if($lastDownloadTime ne 'Not downloaded' &&
          $lastDownloadTime >= $modifiedTime && $modifiedTime >= 0) {
           $classlist{'lastDownloadTime'}=time;
           $classlist{'UpToDate'} = 'true';
           return \%classlist;
       }
   
       %classlist=&Apache::lonnet::dump('classlist',$courseDomain, $courseNumber);
     my ($checkForError)=keys (%classlist);      my ($checkForError)=keys (%classlist);
     if($checkForError =~ /^(con_lost|error|no_such_host)/i) {      if($checkForError =~ /^(con_lost|error|no_such_host)/i) {
         return \%classlist;          return \%classlist;
Line 135  sub DownloadStudentNamePIDSection { Line 147  sub DownloadStudentNamePIDSection {
   
         #Section          #Section
         my %section=&Apache::lonnet::dump('roles',$studentDomain,$studentName);          my %section=&Apache::lonnet::dump('roles',$studentDomain,$studentName);
         $classlist{$name.':section'}=\%section;          $classlist{$name.':sections'}=\%section;
     }      }
   
       $classlist{'UpToDate'} = 'false';
       $classlist{'lastDownloadTime'}=time;
   
     return \%classlist;      return \%classlist;
 }  }
   
 =pod  =pod
   
 =item &DownloadStudentCourseInformation()  =item &DownloadCourseInformation()
   
 Dump of all the course information for a single student.  There is no  Dump of all the course information for a single student.  There is no
 pruning of data, it is all stored in a hash and returned.  pruning of data, it is all stored in a hash and returned.  It also
   checks the timestamp of the students course database file and only downloads
   if it has been modified since the last download.
   
 =over 4  =over 4
   
Line 165  database. Line 182  database.
   
 =cut  =cut
   
 sub DownloadStudentCourseInformation {  sub DownloadCourseInformation {
     my ($name,$courseID)=@_;      my ($namedata,$courseID,$lastDownloadTime,$WhatIWant)=@_;
     my ($studentName,$studentDomain) = split(/\:/,$name);      my %courseData;
       my ($name,$domain) = split(/\:/,$namedata);
     # Download student course data  
     my %courseData=&Apache::lonnet::dump($courseID,$studentDomain,      my $modifiedTime = &GetFileTimestamp($domain, $name,
  $studentName);                                        $courseID.'.db', 
     return \%courseData;                                        $Apache::lonnet::perlvar{'lonUsersDir'});
   
       if($lastDownloadTime >= $modifiedTime && $modifiedTime >= 0) {
           $courseData{$namedata.':lastDownloadTime'}=time;
           $courseData{$namedata.':UpToDate'} = 'true';
           return \%courseData;
       }
   
       # Download course data
       if(!defined($WhatIWant)) {
           $WhatIWant = '.';
       }
       %courseData=&Apache::lonnet::dump($courseID, $domain, $name, $WhatIWant);
       $courseData{'UpToDate'} = 'false';
       $courseData{'lastDownloadTime'}=time;
   
       my %newData;
       foreach (keys(%courseData)) {
           $newData{$namedata.':'.$_} = $courseData{$_};
       }
   
       return \%newData;
 }  }
   
 # ----- END DOWNLOAD INFORMATION ---------------------------------------  # ----- END DOWNLOAD INFORMATION ---------------------------------------
Line 227  sub ProcessTopResourceMap { Line 265  sub ProcessTopResourceMap {
             if($c->aborted()) {              if($c->aborted()) {
                 return;                  return;
             }              }
     if(tie(%hash,'GDBM_File',"$fn.db",&GDBM_READER,0640)) {      if(tie(%hash,'GDBM_File',"$fn.db",&GDBM_READER(),0640)) {
  last;   last;
     }      }
     $tieTries++;      $tieTries++;
Line 250  sub ProcessTopResourceMap { Line 288  sub ProcessTopResourceMap {
     $currentSequence=-1;      $currentSequence=-1;
     my $topLevelSequenceNumber = $currentSequence;      my $topLevelSequenceNumber = $currentSequence;
   
       my %sequenceRecord;
     while(1) {      while(1) {
         if($c->aborted()) {          if($c->aborted()) {
             last;              last;
         }          }
  # HANDLE NEW SEQUENCE!   # HANDLE NEW SEQUENCE!
  #if page || sequence   #if page || sequence
  if(defined($hash{'map_pc_'.$hash{'src_'.$currentResourceID}})) {   if(defined($hash{'map_pc_'.$hash{'src_'.$currentResourceID}}) &&
              !defined($sequenceRecord{$currentResourceID})) {
               $sequenceRecord{$currentResourceID}++;
     push(@sequences, $currentSequence);      push(@sequences, $currentSequence);
     push(@currentResource, $currentResourceID);      push(@currentResource, $currentResourceID);
     push(@finishResource, $lastResourceID);      push(@finishResource, $lastResourceID);
Line 286  sub ProcessTopResourceMap { Line 327  sub ProcessTopResourceMap {
     last;      last;
  }   }
     }      }
               next;
  }   }
   
  # Handle gradable resources: exams, problems, etc   # Handle gradable resources: exams, problems, etc
Line 294  sub ProcessTopResourceMap { Line 336  sub ProcessTopResourceMap {
         my $partB=$2;          my $partB=$2;
  if($hash{'src_'.$currentResourceID}=~   if($hash{'src_'.$currentResourceID}=~
    /\.(problem|exam|quiz|assess|survey|form)$/ &&     /\.(problem|exam|quiz|assess|survey|form)$/ &&
    $partA eq $currentSequence) {     $partA eq $currentSequence && 
              !defined($sequenceRecord{$currentSequence.':'.
                                       $currentResourceID})) {
               $sequenceRecord{$currentSequence.':'.$currentResourceID}++;
     my $Problem = &Apache::lonnet::symbclean(      my $Problem = &Apache::lonnet::symbclean(
   &Apache::lonnet::declutter($hash{'map_id_'.$partA}).    &Apache::lonnet::declutter($hash{'map_id_'.$partA}).
   '___'.$partB.'___'.    '___'.$partB.'___'.
Line 309  sub ProcessTopResourceMap { Line 354  sub ProcessTopResourceMap {
     ':'.$currentResourceID;      ':'.$currentResourceID;
     }      }
   
             # Get Parts for problem  
     my $meta=$hash{'src_'.$currentResourceID};      my $meta=$hash{'src_'.$currentResourceID};
     foreach (split(/\,/,&Apache::lonnet::metadata($meta,'keys'))) {  #            $cache->{$currentResourceID.':title'}=
  if($_=~/^stores\_(\d+)\_tries$/) {  #                &Apache::lonnet::metdata($meta,'title');
     my $Part=&Apache::lonnet::metadata($meta,$_.'.part');              $cache->{$currentResourceID.':title'}=
                     if(!defined($cache->{$currentSequence.':'.                  $hash{'title_'.$currentResourceID};
                                           $currentResourceID.':parts'})) {              $cache->{$currentResourceID.':source'}=
                         $cache->{$currentSequence.':'.$currentResourceID.                  $hash{'src_'.$currentResourceID};
                                    ':parts'}=$Part;  
                     } else {              # Get Parts for problem
                         $cache->{$currentSequence.':'.$currentResourceID.              my %beenHere;
                                    ':parts'}.=':'.$Part;              foreach (split(/\,/,&Apache::lonnet::metadata($meta,'packages'))) {
                   if(/^\w+response_\d+.*/) {
                       my (undef, $partId, $responseId) = split(/_/,$_);
                       if($beenHere{'p:'.$partId} ==  0) {
                           $beenHere{'p:'.$partId}++;
                           if(!defined($cache->{$currentSequence.':'.
                                               $currentResourceID.':parts'})) {
                               $cache->{$currentSequence.':'.$currentResourceID.
                                        ':parts'}=$partId;
                           } else {
                               $cache->{$currentSequence.':'.$currentResourceID.
                                        ':parts'}.=':'.$partId;
                           }
                     }                      }
  }                      if($beenHere{'r:'.$partId.':'.$responseId} == 0) {
     }                          $beenHere{'r:'.$partId.':'.$responseId}++;
  }                          if(!defined($cache->{$currentSequence.':'.
                                                $currentResourceID.':'.$partId.
                                                ':responseIDs'})) {
                               $cache->{$currentSequence.':'.$currentResourceID.
                                        ':'.$partId.':responseIDs'}=$responseId;
                           } else {
                               $cache->{$currentSequence.':'.$currentResourceID.
                                        ':'.$partId.':responseIDs'}.=':'.
                                                                     $responseId;
                           }
                       }
                       if(/^optionresponse/ && 
                          $beenHere{'o:'.$partId.':'.$currentResourceID} == 0) {
                           $beenHere{'o:'.$partId.$currentResourceID}++;
                           if(defined($cache->{'OptionResponses'})) {
                               $cache->{'OptionResponses'}.= ':::'.
                                   $currentResourceID.':'.
                                   $partId.':'.$responseId;
                           } else {
                               $cache->{'OptionResponses'}= $currentResourceID.
                                   ':'.$partId.':'.$responseId;
                           }
                       }
                   }
               }
           }
   
  # if resource == finish resource, then it is the end of a sequence/page   # if resource == finish resource, then it is the end of a sequence/page
  if($currentResourceID eq $lastResourceID) {   if($currentResourceID eq $lastResourceID) {
Line 336  sub ProcessTopResourceMap { Line 417  sub ProcessTopResourceMap {
  # Capture sequence information here   # Capture sequence information here
  $cache->{$currentSequence.':title'}=   $cache->{$currentSequence.':title'}=
     $hash{'title_'.$currentResourceID};      $hash{'title_'.$currentResourceID};
                   $cache->{$currentSequence.':source'}=
                       $hash{'src_'.$currentResourceID};
   
                 my $totalProblems=0;                  my $totalProblems=0;
                 foreach my $currentProblem (split(/\:/,                  foreach my $currentProblem (split(/\:/,
Line 371  sub ProcessTopResourceMap { Line 454  sub ProcessTopResourceMap {
     if($currentSequence eq $topLevelSequenceNumber) {      if($currentSequence eq $topLevelSequenceNumber) {
  last;   last;
     }      }
  }          }
   
  # MOVE!!!   # MOVE!!!
  # move to next resource   # move to next resource
  unless(defined($hash{'to_'.$currentResourceID})) {   unless(defined($hash{'to_'.$currentResourceID})) {
     # big problem, need to handle.  Next is probably wrong      # big problem, need to handle.  Next is probably wrong
               my $errorMessage = 'Big problem in ';
               $errorMessage .= 'loncoursedata::ProcessTopLevelMap.';
               $errorMessage .= '  bighash to_$currentResourceID not defined!';
               &Apache::lonnet::logthis($errorMessage);
     last;      last;
  }   }
  my @nextResources=();   my @nextResources=();
  foreach (split(/\,/,$hash{'to_'.$currentResourceID})) {   foreach (split(/\,/,$hash{'to_'.$currentResourceID})) {
     push(@nextResources, $hash{'goesto_'.$_});              if(!defined($sequenceRecord{$currentSequence.':'.
                                           $hash{'goesto_'.$_}})) {
                   push(@nextResources, $hash{'goesto_'.$_});
               }
  }   }
  push(@currentResource, @nextResources);   push(@currentResource, @nextResources);
  # Set the next resource to be processed   # Set the next resource to be processed
Line 399  sub ProcessTopResourceMap { Line 489  sub ProcessTopResourceMap {
   
 =pod  =pod
   
 =item &ProcessSection()  =item &ProcessClasslist()
   
 Determine the section number for a student for the class.  A student can have   
 multiple sections for the same class.  The correct one is chosen.  
   
 =over 4  
   
 Input: $sectionData, $courseid, $ActiveFlag  
   
 $sectionData:  A pointer to a hash containing all section data for this   
 student for the class  
   
 $courseid:  The course ID.  
   
 $ActiveFlag:  The student's active status (Active/Expired)  Taking the class list dumped from &DownloadClasslist(), all the 
   students and their non-class information is processed using the 
 Output: $oldsection, $cursection, or -1  &ProcessStudentInformation() function.  A date stamp is also recorded for
   when the data was processed.
 $oldsection and $cursection and sections number that will be displayed in the   
 chart.  
   
 -1 is returned if an error occurs.  
   
 =back  
   
 =cut  
   
 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';  
 }  
   
 =pod  
   
 =item &ProcessNamePIDSection()  
   
 Takes data downloaded for a student and breaks it up into managable pieces and   Takes data downloaded for a student and breaks it up into managable pieces and 
 stored in cache data.  The username, domain, class related date, PID,   stored in cache data.  The username, domain, class related date, PID, 
 full name, and section are all processed here.  full name, and section are all processed here.
   
 =over 4  
   
 Input: $cache, $studentInformation, $section, $date, $name, $courseID  
   
 $cache:  A hash pointer to store the data  
   
 $studentInformation:  Student information is what was requested in   
 &DownloadPrerequistedData().  See that function for what data is requested.  
   
 $section: A hash pointer to class section related information.  
   
 $date:  A composite of the start and end date for this class for this  
 student.  Format:  end:start  
   
 $name:  the username:domain information  
   
 $courseID: The course ID  
   
 Output: None  
   
 *NOTE:  There is no return value, but if an error occurs a key is added to   
 the cache data with the value being the error message.  The key is   
 username:domain:error.  It will only exist if an error occurs.  
   
 =back  
   
 =cut  
   
 sub ProcessStudentNamePIDSection {  
     my ($cache,$studentInformation,$section,$date,$name,$courseID,$status)=@_;  
     my ($studentName,$studentDomain) = split(/\:/,$name);  
   
     $cache->{$name.':username'}=$studentName;  
     $cache->{$name.':domain'}=$studentDomain;  
     $cache->{$name.':date'}=$date;  
   
     my ($checkForError)=keys(%$studentInformation);  
     if($checkForError =~ /^(con_lost|error|no_such_host)/i) {  
  $cache->{$name.':error'}=  
     'Could not download student environment data.';  
  $cache->{$name.':fullname'}='';  
  $cache->{$name.':id'}='';  
     } else {  
  $cache->{$name.':fullname'}=&ProcessFullName(  
                                           $studentInformation->{'lastname'},  
           $studentInformation->{'generation'},  
           $studentInformation->{'firstname'},  
                                           $studentInformation->{'middlename'});  
  $cache->{$name.':id'}=$studentInformation->{'id'};  
     }  
   
     # Get student's section number  
     my $sec=&ProcessSection($section, $courseID, $status);  
     if($sec != -1) {  
  $cache->{$name.':section'}=$sec;  
     } else {  
  $cache->{$name.':section'}='';  
     }  
   
     return;  
 }  
   
 =pod  
   
 =item &ProcessClassList()  
   
 Taking the class list dumped from &DownloadPrerequisiteData(), all the   
 students and their non-class information is processed using the   
 &ProcessStudentInformation() function.  A date stamp is also recorded for  
 when the data was processed.  
   
 =over 4  =over 4
   
Line 566  Input: $cache, $classlist, $courseID, $C Line 508  Input: $cache, $classlist, $courseID, $C
 $cache: A hash pointer to store the data  $cache: A hash pointer to store the data
   
 $classlist:  The hash of data collected about a student from   $classlist:  The hash of data collected about a student from 
 &DownloadPrerequisteData().  The hash contains a list of students, a pointer   &DownloadClasslist().  The hash contains a list of students, a pointer 
 to a hash of student information for each student, and each student's section   to a hash of student information for each student, and each student's section 
 number.  number.
   
Line 586  be considered in an arbitrary order. Line 528  be considered in an arbitrary order.
   
 =cut  =cut
   
 sub ProcessClassList {  sub ProcessClasslist {
     my ($cache,$classlist,$courseID,$status,$c)=@_;      my ($cache,$classlist,$courseID,$c)=@_;
     my @names=();      my @names=();
   
       $cache->{'ClasslistTimeStamp'}=$classlist->{'lastDownloadTime'};
       if($classlist->{'UpToDate'} eq 'true') {
           return split(/:::/,$cache->{'NamesOfStudents'});;
       }
   
     foreach my $name (keys(%$classlist)) {      foreach my $name (keys(%$classlist)) {
         if($name =~ /\:section/ || $name =~ /\:studentInformation/ ||          if($name =~ /\:section/ || $name =~ /\:studentInformation/ ||
            $name eq '') {             $name eq '' || $name eq 'UpToDate' || $name eq 'lastDownloadTime') {
             next;              next;
         }          }
         if($c->aborted()) {          if($c->aborted()) {
             last;              return ();
         }          }
         push(@names,$name);          push(@names,$name);
         &ProcessStudentNamePIDSection($cache,          my $studentInformation = $classlist->{$name.':studentInformation'},
                                      $classlist->{$name.':studentInformation'},          my $sectionData = $classlist->{$name.':sections'},
                                      $classlist->{$name.':section'},          my $date = $classlist->{$name},
                                      $classlist->{$name},          my ($studentName,$studentDomain) = split(/\:/,$name);
                                      $name,$courseID,$status);  
           $cache->{$name.':username'}=$studentName;
           $cache->{$name.':domain'}=$studentDomain;
           # Initialize timestamp for student
           if(!defined($cache->{$name.':lastDownloadTime'})) {
               $cache->{$name.':lastDownloadTime'}='Not downloaded';
               $cache->{$name.':updateTime'}=' Not updated';
           }
   
           my ($checkForError)=keys(%$studentInformation);
           if($checkForError =~ /^(con_lost|error|no_such_host)/i) {
               $cache->{$name.':error'}=
                   'Could not download student environment data.';
               $cache->{$name.':fullname'}='';
               $cache->{$name.':id'}='';
           } else {
               $cache->{$name.':fullname'}=&ProcessFullName(
                                             $studentInformation->{'lastname'},
                                             $studentInformation->{'generation'},
                                             $studentInformation->{'firstname'},
                                             $studentInformation->{'middlename'});
               $cache->{$name.':id'}=$studentInformation->{'id'};
           }
   
           my ($end, $start)=split(':',$date);
           $courseID=~s/\_/\//g;
           $courseID=~s/^(\w)/\/$1/;
   
           my $sec='';
           foreach my $key (keys (%$sectionData)) {
               my $value = $sectionData->{$key};
               if ($key=~/^$courseID(?:\/)*(\w+)*\_st$/) {
                   my $tempsection=$1;
                   if($key eq $courseID.'_st') {
                       $tempsection='';
                   }
                   my ($dummy,$roleend,$rolestart)=split(/\_/,$value);
                   if($roleend eq $end && $rolestart eq $start) {
                       $sec = $tempsection;
                       last;
                   }
               }
           }
   
           my $status='Expired';
           if(((!$end) || time < $end) && ((!$start) || (time > $start))) {
               $status='Active';
           }
           $cache->{$name.':Status'}=$status;
           $cache->{$name.':section'}=$sec;
   
           if($sec eq '' || !defined($sec) || $sec eq ' ') {
               $sec = 'none';
           }
           if(defined($cache->{'sectionList'})) {
               if($cache->{'sectionList'} !~ /(^$sec:|^$sec$|:$sec$|:$sec:)/) {
                   $cache->{'sectionList'} .= ':'.$sec;
               }
           } else {
               $cache->{'sectionList'} = $sec;
           }
     }      }
   
     # Time of download      $cache->{'ClasslistTimestamp'}=time;
     $cache->{'time'}=localtime();      $cache->{'NamesOfStudents'}=join(':::',@names);
   
     return @names;      return @names;
 }  }
Line 617  sub ProcessClassList { Line 624  sub ProcessClassList {
 =item &ProcessStudentData()  =item &ProcessStudentData()
   
 Takes the course data downloaded for a student in   Takes the course data downloaded for a student in 
 &DownloadStudentCourseInformation() and breaks it up into key value pairs  &DownloadCourseInformation() and breaks it up into key value pairs
 to be stored in the cached data.  The keys are comprised of the   to be stored in the cached data.  The keys are comprised of the 
 $username:$domain:$keyFromCourseDatabase.  The student username:domain is  $username:$domain:$keyFromCourseDatabase.  The student username:domain is
 stored away signifying that the student's information has been downloaded and   stored away signifying that the student's information has been downloaded and 
Line 639  Output: None Line 646  Output: None
 *NOTE:  There is no output, but an error message is stored away in the cache   *NOTE:  There is no output, but an error message is stored away in the cache 
 data.  This is checked in &FormatStudentData().  The key username:domain:error   data.  This is checked in &FormatStudentData().  The key username:domain:error 
 will only exist if an error occured.  The error is an error from   will only exist if an error occured.  The error is an error from 
 &DownloadStudentCourseInformation().  &DownloadCourseInformation().
   
 =back  =back
   
Line 648  will only exist if an error occured.  Th Line 655  will only exist if an error occured.  Th
 sub ProcessStudentData {  sub ProcessStudentData {
     my ($cache,$courseData,$name)=@_;      my ($cache,$courseData,$name)=@_;
   
     my ($checkForError) = keys(%$courseData);      if(!&CheckDateStampError($courseData, $cache, $name)) {
     if($checkForError =~ /^(con_lost|error|no_such_host)/i) {          return;
         $cache->{$name.':error'}='Could not download course data.';      }
     } else {  
         foreach my $key (keys (%$courseData)) {      foreach (keys %$courseData) {
             $cache->{$name.':'.$key}=$courseData->{$key};          $cache->{$_}=$courseData->{$_};
         }      }
         if(defined($cache->{'NamesOfStudents'})) {  
             $cache->{'NamesOfStudents'}.=':::'.$name;      return;
         } else {  }
             $cache->{'NamesOfStudents'}=$name;  
   sub ExtractStudentData {
       my ($input, $output, $data, $name)=@_;
   
       if(!&CheckDateStampError($input, $data, $name)) {
           return;
       }
   
       my ($username,$domain)=split(':',$name);
   
       my $Version;
       my $problemsCorrect = 0;
       my $totalProblems   = 0;
       my $problemsSolved  = 0;
       my $numberOfParts   = 0;
       my $totalAwarded    = 0;
       foreach my $sequence (split(':', $data->{'orderedSequences'})) {
           foreach my $problemID (split(':', $data->{$sequence.':problems'})) {
               my $problem = $data->{$problemID.':problem'};
               my $LatestVersion = $input->{$name.':version:'.$problem};
   
               # Output dashes for all the parts of this problem if there
               # is no version information about the current problem.
               if(!$LatestVersion) {
                   foreach my $part (split(/\:/,$data->{$sequence.':'.
                                                         $problemID.
                                                         ':parts'})) {
                       $output->{$name.':'.$problemID.':'.$part.':tries'} = 0;
                       $output->{$name.':'.$problemID.':'.$part.':awarded'} = 0;
                       $output->{$name.':'.$problemID.':'.$part.':code'} = ' ';
                       $totalProblems++;
                   }
                   $output->{$name.':'.$problemID.':NoVersion'} = 'true';
                   next;
               }
   
               my %partData=undef;
               # Initialize part data, display skips correctly
               # Skip refers to when a student made no submissions on that
               # part/problem.
               foreach my $part (split(/\:/,$data->{$sequence.':'.
                                                    $problemID.
                                                    ':parts'})) {
                   $partData{$part.':tries'}=0;
                   $partData{$part.':code'}=' ';
                   $partData{$part.':awarded'}=0;
                   $partData{$part.':timestamp'}=0;
                   foreach my $response (split(':', $data->{$sequence.':'.
                                                            $problemID.':'.
                                                            $part.':responseIDs'})) {
                       $partData{$part.':'.$response.':submission'}='';
                   }
               }
   
               # Looping through all the versions of each part, starting with the
               # oldest version.  Basically, it gets the most recent 
               # set of grade data for each part.
               my @submissions = ();
       for(my $Version=1; $Version<=$LatestVersion; $Version++) {
                   foreach my $part (split(/\:/,$data->{$sequence.':'.
                                                        $problemID.
                                                        ':parts'})) {
   
                       if(!defined($input->{"$name:$Version:$problem".
                                            ":resource.$part.solved"})) {
                           # No grade for this submission, so skip
                           next;
                       }
   
                       my $tries=0;
                       my $code=' ';
                       my $awarded=0;
   
                       $tries = $input->{$name.':'.$Version.':'.$problem.
                                         ':resource.'.$part.'.tries'};
                       $awarded = $input->{$name.':'.$Version.':'.$problem.
                                           ':resource.'.$part.'.awarded'};
   
                       $partData{$part.':awarded'}=($awarded) ? $awarded : 0;
                       $partData{$part.':tries'}=($tries) ? $tries : 0;
   
                       $partData{$part.':timestamp'}=$input->{$name.':'.$Version.':'.
                                                              $problem.
                                                              ':timestamp'};
                       if(!$input->{$name.':'.$Version.':'.$problem.':resource.'.$part.
                                    '.previous'}) {
                           foreach my $response (split(':',
                                                      $data->{$sequence.':'.
                                                              $problemID.':'.
                                                              $part.':responseIDs'})) {
                               @submissions=($input->{$name.':'.$Version.':'.
                                                      $problem.
                                                      ':resource.'.$part.'.'.
                                                      $response.'.submission'},
                                             @submissions);
                           }
                       }
   
                       my $val = $input->{$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;
                   }
               }
   
               foreach my $part (split(/\:/,$data->{$sequence.':'.$problemID.
                                                    ':parts'})) {
                   $output->{$name.':'.$problemID.':'.$part.':wrong'} = 
                       $partData{$part.':tries'};
   
                   if($partData{$part.':code'} eq '*') {
                       $output->{$name.':'.$problemID.':'.$part.':wrong'}--;
                       $problemsCorrect++;
                   } elsif($partData{$part.':code'} eq '+') {
                       $output->{$name.':'.$problemID.':'.$part.':wrong'}--;
                       $problemsCorrect++;
                   }
   
                   $output->{$name.':'.$problemID.':'.$part.':tries'} = 
                       $partData{$part.':tries'};
                   $output->{$name.':'.$problemID.':'.$part.':code'} =
                       $partData{$part.':code'};
                   $output->{$name.':'.$problemID.':'.$part.':awarded'} =
                       $partData{$part.':awarded'};
                   $totalAwarded += $partData{$part.':awarded'};
                   $output->{$name.':'.$problemID.':'.$part.':timestamp'} =
                       $partData{$part.':timestamp'};
                   foreach my $response (split(':', $data->{$sequence.':'.
                                                            $problemID.':'.
                                                            $part.':responseIDs'})) {
                       $output->{$name.':'.$problemID.':'.$part.':'.$response.
                                 ':submission'}=join(':::',@submissions);
                   }
   
                   if($partData{$part.':code'} ne 'x') {
                       $totalProblems++;
                   }
               }
         }          }
   
           $output->{$name.':'.$sequence.':problemsCorrect'} = $problemsCorrect;
           $problemsSolved += $problemsCorrect;
    $problemsCorrect=0;
     }      }
   
       $output->{$name.':problemsSolved'} = $problemsSolved;
       $output->{$name.':totalProblems'} = $totalProblems;
       $output->{$name.':totalAwarded'} = $totalAwarded;
   
     return;      return;
 }  }
   
   sub LoadDiscussion {
       my ($courseID)=@_;
       my %Discuss=();
       my %contrib=&Apache::lonnet::dump(
                   $courseID,
                   $ENV{'course.'.$courseID.'.domain'},
                   $ENV{'course.'.$courseID.'.num'});
     
       #my %contrib=&DownloadCourseInformation($name, $courseID, 0);
   
       foreach my $temp(keys %contrib) {
    if ($temp=~/^version/) {
       my $ver=$contrib{$temp};
       my ($dummy,$prb)=split(':',$temp);
       for (my $idx=1; $idx<=$ver; $idx++ ) {
    my $name=$contrib{"$idx:$prb:sendername"};
    $Discuss{"$name:$prb"}=$idx;
       }
    }
       }       
   
       return \%Discuss;
   }
   
 # ----- END PROCESSING FUNCTIONS ---------------------------------------  # ----- END PROCESSING FUNCTIONS ---------------------------------------
   
 =pod  =pod
Line 678  jobs. Line 860  jobs.
   
 # ----- HELPER FUNCTIONS -----------------------------------------------  # ----- HELPER FUNCTIONS -----------------------------------------------
   
   sub CheckDateStampError {
       my ($courseData, $cache, $name)=@_;
       if($courseData->{$name.':UpToDate'} eq 'true') {
           $cache->{$name.':lastDownloadTime'} = 
               $courseData->{$name.':lastDownloadTime'};
           if($courseData->{$name.':lastDownloadTime'} eq 'Not downloaded') {
               $cache->{$name.':updateTime'} = ' Not updated';
           } else {
               $cache->{$name.':updateTime'}=
                   localtime($courseData->{$name.':lastDownloadTime'});
           }
           return 0;
       }
   
       $cache->{$name.':lastDownloadTime'}=$courseData->{$name.':lastDownloadTime'};
       if($courseData->{$name.':lastDownloadTime'} eq 'Not downloaded') {
           $cache->{$name.':updateTime'} = ' Not updated';
       } else {
           $cache->{$name.':updateTime'}=
               localtime($courseData->{$name.':lastDownloadTime'});
       }
   
       if(defined($courseData->{$name.':error'})) {
           $cache->{$name.':error'}=$courseData->{$name.':error'};
           return 0;
       }
   
       return 1;
   }
   
 =pod  =pod
   
 =item &ProcessFullName()  =item &ProcessFullName()
Line 773  sub TestCacheData { Line 985  sub TestCacheData {
     while($tieTries < $totalDelay) {      while($tieTries < $totalDelay) {
         my $result=0;          my $result=0;
         if($isCached) {          if($isCached) {
             $result=tie(%testData,'GDBM_File',$ChartDB,&GDBM_READER,0640);              $result=tie(%testData,'GDBM_File',$ChartDB,&GDBM_READER(),0640);
         } else {          } else {
             $result=tie(%testData,'GDBM_File',$ChartDB,&GDBM_NEWDB,0640);              $result=tie(%testData,'GDBM_File',$ChartDB,&GDBM_NEWDB(),0640);
         }          }
         if($result) {          if($result) {
             last;              last;
Line 792  sub TestCacheData { Line 1004  sub TestCacheData {
     return $isCached;      return $isCached;
 }  }
   
   sub DownloadStudentCourseData {
       my ($students,$checkDate,$cacheDB,$extract,$status,$courseID,$r,$c)=@_;
   
       my $title = 'LON-CAPA Statistics';
       my $heading = 'Download and Process Course Data';
       my $studentCount = scalar(@$students);
       my %cache;
   
       my $WhatIWant;
       $WhatIWant = '(^version:(\w|\/|\.|-)+?$|';
       $WhatIWant .= '^\d+:(\w|\/|\.|-)+?:(resource\.\d+\.';
       $WhatIWant .= '(solved|tries|previous|awarded|(\d+\.submission))\s*$';
       $WhatIWant .= '|timestamp)';
       $WhatIWant .= ')';
   
       if($status eq 'true') {
           &Apache::lonhtmlcommon::Create_PrgWin($r, $title, $heading);
       }
       my $count=1;
       foreach (@$students) {
           if($c->aborted()) { return 'Aborted'; }
   
           if($status eq 'true') {
               my $displayString = $count.'/'.$studentCount.': '.$_;
               &Apache::lonhtmlcommon::Update_PrgWin($displayString, $r);
           }
   
           my $downloadTime='Not downloaded';
           if($checkDate eq 'true'  && 
              tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
               $downloadTime = $cache{$_.':lastDownloadTime'};
               untie(%cache);
           }
   
           if($c->aborted()) { return 'Aborted'; }
   
           if($downloadTime eq 'Not downloaded') {
               my $courseData = 
                   &DownloadCourseInformation($_, $courseID, $downloadTime, 
                                              $WhatIWant);
               if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_WRCREAT(),0640)) {
                   foreach my $key (keys(%$courseData)) {
                       if($key =~ /^(con_lost|error|no_such_host)/i) {
                           $courseData->{$_.':error'} = 'No course data for '.$_;
                           last;
                       }
                   }
                   if($extract eq 'true') {
                       &ExtractStudentData($courseData, \%cache, \%cache, $_);
                   } else {
                       &ProcessStudentData(\%cache, $courseData, $_);
                   }
                   untie(%cache);
               } else {
                   next;
               }
           }
           $count++;
       }
       if($status eq 'true') { &Apache::lonhtmlcommon::Close_PrgWin($r); }
   
       return 'OK';
   }
   
   sub DownloadStudentCourseDataSeparate {
       my ($students,$checkDate,$cacheDB,$extract,$status,$courseID,$r,$c)=@_;
       my $residualFile = '/home/httpd/perl/tmp/'.$courseID.'DownloadFile.db';
       my $title = 'LON-CAPA Statistics';
       my $heading = 'Download Course Data';
   
       my $WhatIWant;
       $WhatIWant = '(^version:(\w|\/|\.|-)+?$|';
       $WhatIWant .= '^\d+:(\w|\/|\.|-)+?:(resource\.\d+\.';
       $WhatIWant .= '(solved|tries|previous|awarded|(\d+\.submission))\s*$';
       $WhatIWant .= '|timestamp)';
       $WhatIWant .= ')';
   
       &CheckForResidualDownload($courseID, $cacheDB, $students, $c);
   
       my %cache;
       my %downloadData;
       unless(tie(%downloadData,'GDBM_File',$residualFile,&GDBM_NEWDB(),0640)) {
           return 'Failed to tie temporary download hash.';
       }
   
       my $studentCount = scalar(@$students);
       if($status eq 'true') {
           &Apache::lonhtmlcommon::Create_PrgWin($r, $title, $heading);
       }
       my $count=1;
       foreach (@$students) {
           if($c->aborted()) {
               untie(%downloadData);
               return 'Aborted';
           }
   
           if($status eq 'true') {
               my $displayString = $count.'/'.$studentCount.': '.$_;
               &Apache::lonhtmlcommon::Update_PrgWin($displayString, $r);
           }
   
           my $downloadTime='Not downloaded';
           if($checkDate eq 'true'  && 
              tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
               $downloadTime = $cache{$_.':lastDownloadTime'};
               untie(%cache);
           }
   
           if($c->aborted()) {
               untie(%downloadData);
               return 'Aborted';
           }
   
           if($downloadTime eq 'Not downloaded') {
               my $error = 0;
               my $courseData = 
                   &DownloadCourseInformation($_, $courseID, $downloadTime,
                                              $WhatIWant);
               foreach my $key (keys(%$courseData)) {
                   $downloadData{$key} = $courseData->{$key};
                   if($key =~ /^(con_lost|error|no_such_host)/i) {
                       $error = 1;
                       last;
                   }
               }
               if($error) {
                   foreach my $deleteKey (keys(%$courseData)) {
                       delete $downloadData{$deleteKey};
                   }
                   $downloadData{$_.':error'} = 'No course data for '.$_;
               }
           }
           $count++;
       }
       if($status eq 'true') { &Apache::lonhtmlcommon::Close_PrgWin($r); }
   
       return &CheckForResidualDownload($cacheDB, 'true', 'true', 
                                        $courseID, $r, $c);
   }
   
   sub CheckForResidualDownload {
       my ($cacheDB,$extract,$status,$courseID,$r,$c)=@_;
   
       my $residualFile = '/home/httpd/perl/tmp/'.$courseID.'DownloadFile.db';
       if(!-e $residualFile) {
           return;
       }
   
       my %downloadData;
       my %cache;
       unless(tie(%downloadData,'GDBM_File',$residualFile,&GDBM_READER(),0640) &&
              tie(%cache,'GDBM_File',$cacheDB,&GDBM_WRCREAT(),0640)) {
           return;
       }
   
       my @dataKeys=keys(%downloadData);
       my @students=();
       my %checkStudent;
       foreach(@dataKeys) {
           my @temp = split(':', $_);
           my $student = $temp[0].':'.$temp[1];
           if(!defined($checkStudent{$student})) {
               $checkStudent{$student}++;
               push(@students, $student);
           }
       }
   
       my $heading = 'Process Course Data';
       my $title = 'LON-CAPA Statistics';
       my $studentCount = scalar(@students);
       if($status eq 'true') {
           &Apache::lonhtmlcommon::Create_PrgWin($r, $title, $heading);
       }
   
       my $count=1;
       foreach my $name (@students) {
           last if($c->aborted());
   
           if($status eq 'true') {
               my $displayString = $count.'/'.$studentCount.': '.$_;
               &Apache::lonhtmlcommon::Update_PrgWin($displayString, $r);
           }
   
           if($extract eq 'true') {
               &ExtractStudentData(\%downloadData, \%cache, \%cache, $name);
           } else {
               &ProcessStudentData(\%cache, \%downloadData, $name);
           }
           foreach (@dataKeys) {
               if(/^$name/) {
                   delete $downloadData{$_};
               }
           }
           $count++;
       }
   
       if($status eq 'true') { &Apache::lonhtmlcommon::Close_PrgWin($r); }
   
       untie(%cache);
       untie(%downloadData);
   
       if(!$c->aborted()) {
           my @files = ($residualFile);
           unlink(@files);
       }
   
       return 'OK';
   }
   
   sub GetFileTimestamp {
       my ($studentDomain,$studentName,$filename,$root)=@_;
       $studentDomain=~s/\W//g;
       $studentName=~s/\W//g;
       my $subdir=$studentName.'__';
       $subdir =~ s/(.)(.)(.).*/$1\/$2\/$3/;
       my $proname="$studentDomain/$subdir/$studentName";
       $proname .= '/'.$filename;
       my @dir = &Apache::lonnet::dirlist($proname, $studentDomain, $studentName,
                                          $root);
       my $fileStat = $dir[0];
       my @stats = split('&', $fileStat);
       if($stats[0] ne 'empty' && $stats[0] ne 'no_such_dir') {
           return $stats[9];
       } else {
           return -1;
       }
   }
   
 # ----- END HELPER FUNCTIONS --------------------------------------------  # ----- END HELPER FUNCTIONS --------------------------------------------
   
 1;  1;

Removed from v.1.1  
changed lines
  Added in v.1.15


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