Diff for /loncom/interface/loncoursedata.pm between versions 1.22 and 1.49

version 1.22, 2002/08/28 21:50:27 version 1.49, 2003/02/20 14:28:25
Line 89  Input: $courseID, $lastDownloadTime, $c Line 89  Input: $courseID, $lastDownloadTime, $c
 $courseID:  The id of the course  $courseID:  The id of the course
   
 $lastDownloadTime:  This is the date stamp for when this information was  $lastDownloadTime:  This is the date stamp for when this information was
 last gathered.  If it is set to 'Not downloaded', it will gather the data  last gathered.  If it is set to Not downloaded, it will gather the data
 again, though it currently does not remove the old data.  again, though it currently does not remove the old data.
   
 $c: The connection class that can determine if the browser has aborted.  It  $c: The connection class that can determine if the browser has aborted.  It
Line 103  Output: \%classlist Line 103  Output: \%classlist
 -A list of student name:domain (as keys) (known below as $name)  -A list of student name:domain (as keys) (known below as $name)
   
 -A hash pointer for each student containing lastname, generation, firstname,  -A hash pointer for each student containing lastname, generation, firstname,
 middlename, and PID : Key is $name.'studentInformation'  middlename, and PID : Key is $name.studentInformation
   
 -A hash pointer to each students section data : Key is $name.section  -A hash pointer to each students section data : Key is $name.section
   
Line 125  sub DownloadClasslist { Line 125  sub DownloadClasslist {
                                                          $Apache::lonnet::perlvar{'lonUsersDir'});                                                           $Apache::lonnet::perlvar{'lonUsersDir'});
   
     # Always download the information if lastDownloadTime is set to      # Always download the information if lastDownloadTime is set to
     # 'Not downloaded', otherwise it is only downloaded if the file      # Not downloaded, otherwise it is only downloaded if the file
     # has been updated and has a more recent date stamp      # has been updated and has a more recent date stamp
     if($lastDownloadTime ne 'Not downloaded' &&      if($lastDownloadTime ne 'Not downloaded' &&
        $lastDownloadTime >= $modifiedTime && $modifiedTime >= 0) {         $lastDownloadTime >= $modifiedTime && $modifiedTime >= 0) {
Line 139  sub DownloadClasslist { Line 139  sub DownloadClasslist {
     %classlist=&Apache::lonnet::dump('classlist',$courseDomain, $courseNumber);      %classlist=&Apache::lonnet::dump('classlist',$courseDomain, $courseNumber);
     foreach(keys (%classlist)) {      foreach(keys (%classlist)) {
         if(/^(con_lost|error|no_such_host)/i) {          if(/^(con_lost|error|no_such_host)/i) {
             return \%classlist;      return;
         }          }
     }      }
   
Line 195  $namedata: student name:domain Line 195  $namedata: student name:domain
 $courseID:  The id of the course  $courseID:  The id of the course
   
 $lastDownloadTime:  This is the date stamp for when this information was  $lastDownloadTime:  This is the date stamp for when this information was
 last gathered.  If it is set to 'Not downloaded', it will gather the data  last gathered.  If it is set to Not downloaded, it will gather the data
 again, though it currently does not remove the old data.  again, though it currently does not remove the old data.
   
 $WhatIWant:  Regular expression used to get selected data with dump  $WhatIWant:  Regular expression used to get selected data with dump
   
 Output: \%courseData  Output: \%courseData
   
 \%courseData:  A hash pointer to the raw data from the student's course  \%courseData:  A hash pointer to the raw data from the students course
 database.  database.
   
 =back  =back
Line 218  sub DownloadCourseInformation { Line 218  sub DownloadCourseInformation {
                                       $courseID.'.db',                                         $courseID.'.db', 
                                       $Apache::lonnet::perlvar{'lonUsersDir'});                                        $Apache::lonnet::perlvar{'lonUsersDir'});
   
     if($lastDownloadTime >= $modifiedTime && $modifiedTime >= 0) {      if($lastDownloadTime ne 'Not downloaded' && 
          $lastDownloadTime >= $modifiedTime && $modifiedTime >= 0) {
         # Data is not gathered so return UpToDate as true.  This          # Data is not gathered so return UpToDate as true.  This
         # will be interpreted in ProcessClasslist          # will be interpreted in ProcessClasslist
         $courseData{$namedata.':lastDownloadTime'}=time;          $courseData{$namedata.':lastDownloadTime'}=time;
Line 254  sub DownloadCourseInformation { Line 255  sub DownloadCourseInformation {
 These functions process all the data for all the students.  Also, they  These functions process all the data for all the students.  Also, they
 are the functions that access the cache database for writing the majority of  are the functions that access the cache database for writing the majority of
 the time.  The downloading and caching were separated to reduce problems   the time.  The downloading and caching were separated to reduce problems 
 with stopping downloading then can't tie hash to database later.  with stopping downloading then can not tie hash to database later.
   
 =cut  =cut
   
 # ----- PROCESSING FUNCTIONS ---------------------------------------  # ----- PROCESSING FUNCTIONS ---------------------------------------
   
   
   
   =pod
   
   =item &get_sequence_assessment_data()
   
   AT THIS TIME THE USE OF THIS FUNCTION IS *NOT* RECOMMENDED
   
   Use lonnavmaps to build a data structure describing the order and 
   assessment contents of each sequence in the current course.
   
   The returned structure is a hash reference. 
   
   { title  => 'title',
     symb   => 'symb',
     source => '/s/o/u/r/c/e',
     type  => (container|assessment),
     contents     => [ {},{},{},{} ], # only for container
     parts        => [11,13,15],      # only for assessment
     response_ids => [12,14,16]       # only for assessment
   }
   
   Also returned are array references to the symbs and assessments contained
   in the sequence.
   
   $hash->{'contents'} is a reference to an array of hashes of the same structure.
   
   =cut
   
   sub get_sequence_assessment_data {
       return undef;
       my $fn=$ENV{'request.course.fn'};
       &Apache::lonnet::logthis('filename = '.$fn);
       ##
       ## use navmaps
       my $navmap = Apache::lonnavmaps::navmap->new($fn.".db",$fn."_parms.db",
                                                    1,0);
       if (!defined($navmap)) {
           return 'Can not open Coursemap';
       }
       my $iterator = $navmap->getIterator(undef, undef, undef, 1);
       ##
       ## Prime the pump 
       ## 
       ## We are going to loop until we run out of sequences/pages to explore for
       ## resources.  This means we have to start out with something to look
       ## at.
       my $curRes = $iterator->next(); # BEGIN_MAP
       $curRes = $iterator->next(); # The sequence itself
       #
       my $title = $curRes->title();
       my $symb  = $curRes->symb();
       my $src   = $curRes->src();
       #
       my @Sequences; 
       my @Assessments;
       my @Nested_Sequences = ();   # Stack of sequences, keeps track of depth
       my $top = { title    => $title,
                   symb     => $symb,
                   type     => 'container',
                   num_assess => 0,
                   contents   => [], };
       push (@Sequences,$top);
       push (@Nested_Sequences, $top);
       #
       # We need to keep track of which sequences contain homework problems
       # 
       while (scalar(@Nested_Sequences)) {
           $curRes = $iterator->next();
           my $currentmap = $Nested_Sequences[-1]; # Last one on the stack
           if ($curRes == $iterator->BEGIN_MAP()) {
               # get the map itself, instead of BEGIN_MAP
               $curRes = $iterator->next(); 
               $title = $curRes->title();
               $symb  = $curRes->symb();
               $src   = $curRes->src();
               my $newmap = { title    => $title,
                              src      => $src,
                              symb     => $symb,
                              type     => 'container',
                              num_assess => 0,
                              contents   => [],
                          };
               push (@{$currentmap->{'contents'}},$newmap); # this is permanent
               push (@Sequences,$newmap);
               push (@Nested_Sequences, $newmap); # this is a stack
               next;
           }
           if ($curRes == $iterator->END_MAP()) {
               pop(@Nested_Sequences);
               next;
           }
           next if (! ref($curRes));
           next if (! $curRes->is_problem() && !$curRes->randomout);
           # Okay, from here on out we only deal with assessments
           $title = $curRes->title();
           $symb  = $curRes->symb();
           $src   = $curRes->src();
           my $parts = $curRes->parts();
           my $assessment = { title => $title,
                              src   => $src,
                              symb  => $symb,
                              type  => 'assessment',
                          };
           push(@Assessments,$assessment);
           push(@{$currentmap->{'contents'}},$assessment);
           $currentmap->{'num_assess'}++;
       }
       return ($top,\@Sequences,\@Assessments);
   }
   
 =pod  =pod
   
 =item &ProcessTopResourceMap()  =item &ProcessTopResourceMap()
Line 309  sub ProcessTopResourceMap { Line 421  sub ProcessTopResourceMap {
         return 'Can not open Coursemap.';          return 'Can not open Coursemap.';
     }      }
   
       my $oldkeys;
       delete $cache->{'OptionResponses'};
       if(defined($cache->{'ResourceKeys'})) {
           $oldkeys = $cache->{'ResourceKeys'};
           foreach (split(':::', $cache->{'ResourceKeys'})) {
               delete $cache->{$_};
           }
           delete $cache->{'ResourceKeys'};
       }
   
     # Initialize state machine.  Set information pointing to top level map.      # Initialize state machine.  Set information pointing to top level map.
     my (@sequences, @currentResource, @finishResource);      my (@sequences, @currentResource, @finishResource);
     my ($currentSequence, $currentResourceID, $lastResourceID);      my ($currentSequence, $currentResourceID, $lastResourceID);
   
     $currentResourceID=$hash{'ids_/res/'.$ENV{'request.course.uri'}};      $currentResourceID=$hash{'ids_'.
         &Apache::lonnet::clutter($ENV{'request.course.uri'})};
     push(@currentResource, $currentResourceID);      push(@currentResource, $currentResourceID);
     $lastResourceID=-1;      $lastResourceID=-1;
     $currentSequence=-1;      $currentSequence=-1;
     my $topLevelSequenceNumber = $currentSequence;      my $topLevelSequenceNumber = $currentSequence;
   
     my %sequenceRecord;      my %sequenceRecord;
       my %allkeys;
     while(1) {      while(1) {
         if($c->aborted()) {          if($c->aborted()) {
             last;              last;
Line 344  sub ProcessTopResourceMap { Line 468  sub ProcessTopResourceMap {
             } else {              } else {
                 $cache->{'orderedSequences'}.=':'.$currentSequence;                  $cache->{'orderedSequences'}.=':'.$currentSequence;
             }              }
               $allkeys{'orderedSequences'}++;
   
     $lastResourceID=$hash{'map_finish_'.      $lastResourceID=$hash{'map_finish_'.
   $hash{'src_'.$currentResourceID}};    $hash{'src_'.$currentResourceID}};
Line 378  sub ProcessTopResourceMap { Line 503  sub ProcessTopResourceMap {
  $currentResourceID}));   $currentResourceID}));
   
     $cache->{$currentResourceID.':problem'}=$Problem;      $cache->{$currentResourceID.':problem'}=$Problem;
               $allkeys{$currentResourceID.':problem'}++;
     if(!defined($cache->{$currentSequence.':problems'})) {      if(!defined($cache->{$currentSequence.':problems'})) {
  $cache->{$currentSequence.':problems'}=$currentResourceID;   $cache->{$currentSequence.':problems'}=$currentResourceID;
     } else {      } else {
  $cache->{$currentSequence.':problems'}.=   $cache->{$currentSequence.':problems'}.=
     ':'.$currentResourceID;      ':'.$currentResourceID;
     }      }
               $allkeys{$currentSequence.':problems'}++;
   
     my $meta=$hash{'src_'.$currentResourceID};      my $meta=$hash{'src_'.$currentResourceID};
 #            $cache->{$currentResourceID.':title'}=  #            $cache->{$currentResourceID.':title'}=
 #                &Apache::lonnet::metdata($meta,'title');  #                &Apache::lonnet::metdata($meta,'title');
             $cache->{$currentResourceID.':title'}=              $cache->{$currentResourceID.':title'}=
                 $hash{'title_'.$currentResourceID};                  $hash{'title_'.$currentResourceID};
               $allkeys{$currentResourceID.':title'}++;
             $cache->{$currentResourceID.':source'}=              $cache->{$currentResourceID.':source'}=
                 $hash{'src_'.$currentResourceID};                  $hash{'src_'.$currentResourceID};
               $allkeys{$currentResourceID.':source'}++;
   
             # Get Parts for problem              # Get Parts for problem
             my %beenHere;              my %beenHere;
Line 408  sub ProcessTopResourceMap { Line 537  sub ProcessTopResourceMap {
                             $cache->{$currentSequence.':'.$currentResourceID.                              $cache->{$currentSequence.':'.$currentResourceID.
                                      ':parts'}.=':'.$partId;                                       ':parts'}.=':'.$partId;
                         }                          }
                           $allkeys{$currentSequence.':'.$currentResourceID.
                                     ':parts'}++;
                     }                      }
                     if($beenHere{'r:'.$partId.':'.$responseId} == 0) {                      if($beenHere{'r:'.$partId.':'.$responseId} == 0) {
                         $beenHere{'r:'.$partId.':'.$responseId}++;                          $beenHere{'r:'.$partId.':'.$responseId}++;
Line 421  sub ProcessTopResourceMap { Line 552  sub ProcessTopResourceMap {
                                      ':'.$partId.':responseIDs'}.=':'.                                       ':'.$partId.':responseIDs'}.=':'.
                                                                   $responseId;                                                                    $responseId;
                         }                          }
                           $allkeys{$currentSequence.':'.$currentResourceID.':'.
                                        $partId.':responseIDs'}++;
                     }                      }
                     if(/^optionresponse/ &&                       if(/^optionresponse/ && 
                        $beenHere{'o:'.$partId.':'.$currentResourceID} == 0) {                         $beenHere{'o:'.$partId.':'.$currentResourceID} == 0) {
Line 434  sub ProcessTopResourceMap { Line 567  sub ProcessTopResourceMap {
                                 $currentResourceID.':'.                                  $currentResourceID.':'.
                                 $partId.':'.$responseId;                                  $partId.':'.$responseId;
                         }                          }
                           $allkeys{'OptionResponses'}++;
                     }                      }
                 }                  }
             }              }
Line 449  sub ProcessTopResourceMap { Line 583  sub ProcessTopResourceMap {
  # Capture sequence information here   # Capture sequence information here
  $cache->{$currentSequence.':title'}=   $cache->{$currentSequence.':title'}=
     $hash{'title_'.$currentResourceID};      $hash{'title_'.$currentResourceID};
                   $allkeys{$currentSequence.':title'}++;
                 $cache->{$currentSequence.':source'}=                  $cache->{$currentSequence.':source'}=
                     $hash{'src_'.$currentResourceID};                      $hash{'src_'.$currentResourceID};
                   $allkeys{$currentSequence.':source'}++;
   
                 my $totalProblems=0;                  my $totalProblems=0;
                 foreach my $currentProblem (split(/\:/,                  foreach my $currentProblem (split(/\:/,
Line 464  sub ProcessTopResourceMap { Line 600  sub ProcessTopResourceMap {
                 }                  }
  my @titleLength=split(//,$cache->{$currentSequence.   my @titleLength=split(//,$cache->{$currentSequence.
                                                     ':title'});                                                      ':title'});
                 # $extra is 3 for problems correct and 3 for space                  # $extra is 5 for problems correct and 3 for space
                 # between problems correct and problem output                  # between problems correct and problem output
                 my $extra = 6;                  my $extra = 8;
  if(($totalProblems + $extra) > (scalar @titleLength)) {   if(($totalProblems + $extra) > (scalar @titleLength)) {
     $cache->{$currentSequence.':columnWidth'}=      $cache->{$currentSequence.':columnWidth'}=
                         $totalProblems + $extra;                          $totalProblems + $extra;
Line 474  sub ProcessTopResourceMap { Line 610  sub ProcessTopResourceMap {
     $cache->{$currentSequence.':columnWidth'}=      $cache->{$currentSequence.':columnWidth'}=
                         (scalar @titleLength);                          (scalar @titleLength);
  }   }
                   $allkeys{$currentSequence.':columnWidth'}++;
     } else {      } else {
                 # Remove sequence from list, if it contains no problems to                  # Remove sequence from list, if it contains no problems to
                 # display.                  # display.
Line 494  sub ProcessTopResourceMap { Line 631  sub ProcessTopResourceMap {
     # big problem, need to handle.  Next is probably wrong      # big problem, need to handle.  Next is probably wrong
             my $errorMessage = 'Big problem in ';              my $errorMessage = 'Big problem in ';
             $errorMessage .= 'loncoursedata::ProcessTopLevelMap.';              $errorMessage .= 'loncoursedata::ProcessTopLevelMap.';
             $errorMessage .= '  bighash to_$currentResourceID not defined!';              $errorMessage .= "  bighash to_$currentResourceID not defined!";
             &Apache::lonnet::logthis($errorMessage);              &Apache::lonnet::logthis($errorMessage);
     last;      if (!defined($currentResourceID)) {last;}
  }   }
  my @nextResources=();   my @nextResources=();
  foreach (split(/\,/,$hash{'to_'.$currentResourceID})) {   foreach (split(/\,/,$hash{'to_'.$currentResourceID})) {
Line 510  sub ProcessTopResourceMap { Line 647  sub ProcessTopResourceMap {
  $currentResourceID=pop(@currentResource);   $currentResourceID=pop(@currentResource);
     }      }
   
       my @theKeys = keys(%allkeys);
       my $newkeys = join(':::', @theKeys);
       $cache->{'ResourceKeys'} = join(':::', $newkeys);
       if($newkeys ne $oldkeys) {
           $cache->{'ResourceUpdated'} = 'true';
       } else {
           $cache->{'ResourceUpdated'} = 'false';
       }
   
     unless (untie(%hash)) {      unless (untie(%hash)) {
         &Apache::lonnet::logthis("<font color=blue>WARNING: ".          &Apache::lonnet::logthis("<font color=blue>WARNING: ".
                                  "Could not untie coursemap $fn (browse)".                                   "Could not untie coursemap $fn (browse)".
Line 540  $cache: A hash pointer to store the data Line 686  $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 
 &DownloadClasslist().  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 students section 
 number.  number.
   
 $courseID:  The course ID  $courseID:  The course ID
Line 553  browser Line 699  browser
 Output: @names  Output: @names
   
 @names:  An array of students whose information has been processed, and are to   @names:  An array of students whose information has been processed, and are to 
 be considered in an arbitrary order.  be considered in an arbitrary order.  The entries in @names are of the form
   username:domain.
   
   The values in $cache are as follows:
   
    *NOTE: for the following $name implies username:domain
    $name.':error'                  only defined if an error occured.  Value
                                    contains the error message
    $name.':lastDownloadTime'       unconverted time of the last update of a
                                    student\'s course data
    $name.'updateTime'              coverted time of the last update of a 
                                    student\'s course data
    $name.':username'               username of a student
    $name.':domain'                 domain of a student
    $name.':fullname'               full name of a student
    $name.':id'                     PID of a student
    $name.':Status'                 active/expired status of a student
    $name.':section'                section of a student
   
 =back  =back
   
Line 576  sub ProcessClasslist { Line 739  sub ProcessClasslist {
         if($c->aborted()) {          if($c->aborted()) {
             return ();              return ();
         }          }
         my $studentInformation = $classlist->{$name.':studentInformation'},          my $studentInformation = $classlist->{$name.':studentInformation'};
         my $sectionData = $classlist->{$name.':sections'},          my $date = $classlist->{$name};
         my $date = $classlist->{$name},  
         my ($studentName,$studentDomain) = split(/\:/,$name);          my ($studentName,$studentDomain) = split(/\:/,$name);
   
         $cache->{$name.':username'}=$studentName;          $cache->{$name.':username'}=$studentName;
Line 613  sub ProcessClasslist { Line 775  sub ProcessClasslist {
         $courseID=~s/^(\w)/\/$1/;          $courseID=~s/^(\w)/\/$1/;
   
         my $sec='';          my $sec='';
           my $sectionData = $classlist->{$name.':sections'};
         foreach my $key (keys (%$sectionData)) {          foreach my $key (keys (%$sectionData)) {
             my $value = $sectionData->{$key};              my $value = $sectionData->{$key};
             if ($key=~/^$courseID(?:\/)*(\w+)*\_st$/) {              if ($key=~/^$courseID(?:\/)*(\w+)*\_st$/) {
Line 620  sub ProcessClasslist { Line 783  sub ProcessClasslist {
                 if($key eq $courseID.'_st') {                  if($key eq $courseID.'_st') {
                     $tempsection='';                      $tempsection='';
                 }                  }
                 my ($dummy,$roleend,$rolestart)=split(/\_/,$value);                  my (undef,$roleend,$rolestart)=split(/\_/,$value);
                 if($roleend eq $end && $rolestart eq $start) {                  if($roleend eq $end && $rolestart eq $start) {
                     $sec = $tempsection;                      $sec = $tempsection;
                     last;                      last;
Line 661  Takes the course data downloaded for a s Line 824  Takes the course data downloaded for a s
 &DownloadCourseInformation() 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 students information has been downloaded and 
 can be reused from cached data.  can be reused from cached data.
   
 =over 4  =over 4
Line 693  sub ProcessStudentData { Line 856  sub ProcessStudentData {
         return;          return;
     }      }
   
       # This little delete thing, should not be here.  Move some other
       # time though.
       if(defined($cache->{$name.':keys'})) {
    foreach (split(':::', $cache->{$name.':keys'})) {
       delete $cache->{$name.':'.$_};
    }
           delete $cache->{$name.':keys'};
       }
   
       my %courseKeys;
     # user name:domain was prepended earlier in DownloadCourseInformation      # user name:domain was prepended earlier in DownloadCourseInformation
     foreach (keys %$courseData) {      foreach (keys %$courseData) {
    my $currentKey = $_;
    $currentKey =~ s/^$name//;
    $courseKeys{$currentKey}++;
         $cache->{$_}=$courseData->{$_};          $cache->{$_}=$courseData->{$_};
     }      }
   
       $cache->{$name.':keys'} = join(':::', keys(%courseKeys));
   
     return;      return;
 }  }
   
Line 753  sub ExtractStudentData { Line 931  sub ExtractStudentData {
         return;          return;
     }      }
   
       # This little delete thing, should not be here.  Move some other
       # time though.
       my %allkeys;
       if(defined($output->{$name.':keys'})) {
    foreach (split(':::', $output->{$name.':keys'})) {
       delete $output->{$name.':'.$_};
    }
           delete $output->{$name.':keys'};
       }
   
     my ($username,$domain)=split(':',$name);      my ($username,$domain)=split(':',$name);
   
     my $Version;      my $Version;
Line 768  sub ExtractStudentData { Line 956  sub ExtractStudentData {
   
             # Output dashes for all the parts of this problem if there              # Output dashes for all the parts of this problem if there
             # is no version information about the current problem.              # is no version information about the current problem.
               $output->{$name.':'.$problemID.':NoVersion'} = 'false';
               $allkeys{$name.':'.$problemID.':NoVersion'}++;
             if(!$LatestVersion) {              if(!$LatestVersion) {
                 foreach my $part (split(/\:/,$data->{$sequence.':'.                  foreach my $part (split(/\:/,$data->{$sequence.':'.
                                                       $problemID.                                                        $problemID.
Line 775  sub ExtractStudentData { Line 965  sub ExtractStudentData {
                     $output->{$name.':'.$problemID.':'.$part.':tries'} = 0;                      $output->{$name.':'.$problemID.':'.$part.':tries'} = 0;
                     $output->{$name.':'.$problemID.':'.$part.':awarded'} = 0;                      $output->{$name.':'.$problemID.':'.$part.':awarded'} = 0;
                     $output->{$name.':'.$problemID.':'.$part.':code'} = ' ';                      $output->{$name.':'.$problemID.':'.$part.':code'} = ' ';
       $allkeys{$name.':'.$problemID.':'.$part.':tries'}++;
       $allkeys{$name.':'.$problemID.':'.$part.':awarded'}++;
       $allkeys{$name.':'.$problemID.':'.$part.':code'}++;
                     $totalProblems++;                      $totalProblems++;
                 }                  }
                 $output->{$name.':'.$problemID.':NoVersion'} = 'true';                  $output->{$name.':'.$problemID.':NoVersion'} = 'true';
Line 860  sub ExtractStudentData { Line 1053  sub ExtractStudentData {
                                                  ':parts'})) {                                                   ':parts'})) {
                 $output->{$name.':'.$problemID.':'.$part.':wrong'} =                   $output->{$name.':'.$problemID.':'.$part.':wrong'} = 
                     $partData{$part.':tries'};                      $partData{$part.':tries'};
    $allkeys{$name.':'.$problemID.':'.$part.':wrong'}++;
   
                 if($partData{$part.':code'} eq '*') {                  if($partData{$part.':code'} eq '*') {
                     $output->{$name.':'.$problemID.':'.$part.':wrong'}--;                      $output->{$name.':'.$problemID.':'.$part.':wrong'}--;
Line 875  sub ExtractStudentData { Line 1069  sub ExtractStudentData {
                     $partData{$part.':code'};                      $partData{$part.':code'};
                 $output->{$name.':'.$problemID.':'.$part.':awarded'} =                  $output->{$name.':'.$problemID.':'.$part.':awarded'} =
                     $partData{$part.':awarded'};                      $partData{$part.':awarded'};
    $allkeys{$name.':'.$problemID.':'.$part.':tries'}++;
    $allkeys{$name.':'.$problemID.':'.$part.':code'}++;
    $allkeys{$name.':'.$problemID.':'.$part.':awarded'}++;
   
                 $totalAwarded += $partData{$part.':awarded'};                  $totalAwarded += $partData{$part.':awarded'};
                 $output->{$name.':'.$problemID.':'.$part.':timestamp'} =                  $output->{$name.':'.$problemID.':'.$part.':timestamp'} =
                     $partData{$part.':timestamp'};                      $partData{$part.':timestamp'};
    $allkeys{$name.':'.$problemID.':'.$part.':timestamp'}++;
   
                 foreach my $response (split(':', $data->{$sequence.':'.                  foreach my $response (split(':', $data->{$sequence.':'.
                                                          $problemID.':'.                                                           $problemID.':'.
                                                          $part.':responseIDs'})) {                                                           $part.':responseIDs'})) {
                     $output->{$name.':'.$problemID.':'.$part.':'.$response.                      $output->{$name.':'.$problemID.':'.$part.':'.$response.
                               ':submission'}=join(':::',@submissions);                                ':submission'}=join(':::',@submissions);
       $allkeys{$name.':'.$problemID.':'.$part.':'.$response.
        ':submission'}++;
                 }                  }
   
                 if($partData{$part.':code'} ne 'x') {                  if($partData{$part.':code'} ne 'x') {
Line 892  sub ExtractStudentData { Line 1094  sub ExtractStudentData {
         }          }
   
         $output->{$name.':'.$sequence.':problemsCorrect'} = $problemsCorrect;          $output->{$name.':'.$sequence.':problemsCorrect'} = $problemsCorrect;
    $allkeys{$name.':'.$sequence.':problemsCorrect'}++;
         $problemsSolved += $problemsCorrect;          $problemsSolved += $problemsCorrect;
  $problemsCorrect=0;   $problemsCorrect=0;
     }      }
Line 899  sub ExtractStudentData { Line 1102  sub ExtractStudentData {
     $output->{$name.':problemsSolved'} = $problemsSolved;      $output->{$name.':problemsSolved'} = $problemsSolved;
     $output->{$name.':totalProblems'} = $totalProblems;      $output->{$name.':totalProblems'} = $totalProblems;
     $output->{$name.':totalAwarded'} = $totalAwarded;      $output->{$name.':totalAwarded'} = $totalAwarded;
       $allkeys{$name.':problemsSolved'}++;
       $allkeys{$name.':totalProblems'}++;
       $allkeys{$name.':totalAwarded'}++;
   
       $output->{$name.':keys'} = join(':::', keys(%allkeys));
   
     return;      return;
 }  }
Line 989  sub ProcessFullName { Line 1197  sub ProcessFullName {
     my ($lastname, $generation, $firstname, $middlename)=@_;      my ($lastname, $generation, $firstname, $middlename)=@_;
     my $Str = '';      my $Str = '';
   
       # Strip whitespace preceeding & following name components.
       $lastname   =~ s/(\s+$|^\s+)//g;
       $generation =~ s/(\s+$|^\s+)//g;
       $firstname  =~ s/(\s+$|^\s+)//g;
       $middlename =~ s/(\s+$|^\s+)//g;
   
     if($lastname ne '') {      if($lastname ne '') {
  $Str .= $lastname.' ';   $Str .= $lastname;
  if($generation ne '') {   $Str .= ' '.$generation if ($generation ne '');
     $Str .= $generation;   $Str .= ',';
  } else {          $Str .= ' '.$firstname  if ($firstname ne '');
     chop($Str);          $Str .= ' '.$middlename if ($middlename ne '');
  }  
  $Str .= ', ';  
  if($firstname ne '') {  
     $Str .= $firstname.' ';  
  }  
  if($middlename ne '') {  
     $Str .= $middlename;  
  } else {  
     chop($Str);  
     if($firstname eq '') {  
  chop($Str);  
     }  
  }  
     } else {      } else {
  if($firstname ne '') {          $Str .= $firstname      if ($firstname ne '');
     $Str .= $firstname.' ';          $Str .= ' '.$middlename if ($middlename ne '');
  }          $Str .= ' '.$generation if ($generation ne '');
  if($middlename ne '') {  
     $Str .= $middlename.' ';  
  }  
  if($generation ne '') {  
     $Str .= $generation;  
  } else {  
     chop($Str);  
  }  
     }      }
   
     return $Str;      return $Str;
Line 1043  $ChartDB: The name of the cache database Line 1236  $ChartDB: The name of the cache database
   
 Output: -1, 0, 1  Output: -1, 0, 1
   
 -1: Couldn't tie database  -1: Could not tie database
  0: Use cached data   0: Use cached data
  1: New cache database created, use that.   1: New cache database created, use that.
   
Line 1095  sub DownloadStudentCourseData { Line 1288  sub DownloadStudentCourseData {
     my $title = 'LON-CAPA Statistics';      my $title = 'LON-CAPA Statistics';
     my $heading = 'Download and Process Course Data';      my $heading = 'Download and Process Course Data';
     my $studentCount = scalar(@$students);      my $studentCount = scalar(@$students);
     my %cache;  
   
   
     my $WhatIWant;      my $WhatIWant;
     $WhatIWant = '(^version:|';      $WhatIWant = '(^version:|';
     $WhatIWant .= '^\d+:.+?:(resource\.\d+\.';      $WhatIWant .= '^\d+:.+?:(resource\.\d+\.';
     $WhatIWant .= '(solved|tries|previous|awarded|(\d+\.submission))\s*$';      $WhatIWant .= '(solved|tries|previous|awarded|(\d+\.submission))\s*$';#'
     $WhatIWant .= '|timestamp)';      $WhatIWant .= '|timestamp)';
     $WhatIWant .= ')';      $WhatIWant .= ')';
 #    $WhatIWant = '.';  #    $WhatIWant = '.';
Line 1113  sub DownloadStudentCourseData { Line 1304  sub DownloadStudentCourseData {
     my $displayString;      my $displayString;
     my $count=0;      my $count=0;
     foreach (@$students) {      foreach (@$students) {
           my %cache;
   
         if($c->aborted()) { return 'Aborted'; }          if($c->aborted()) { return 'Aborted'; }
   
         if($status eq 'true') {          if($status eq 'true') {
Line 1122  sub DownloadStudentCourseData { Line 1315  sub DownloadStudentCourseData {
         }          }
   
         my $downloadTime='Not downloaded';          my $downloadTime='Not downloaded';
           my $needUpdate = 'false';
         if($checkDate eq 'true'  &&           if($checkDate eq 'true'  && 
            tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {             tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
             $downloadTime = $cache{$_.':lastDownloadTime'};              $downloadTime = $cache{$_.':lastDownloadTime'};
               $needUpdate = $cache{'ResourceUpdated'};
             untie(%cache);              untie(%cache);
         }          }
   
         if($c->aborted()) { return 'Aborted'; }          if($c->aborted()) { return 'Aborted'; }
   
         if($downloadTime eq 'Not downloaded') {          if($needUpdate eq 'true') {
             my $courseData =               $downloadTime = 'Not downloaded';
                 &DownloadCourseInformation($_, $courseID, $downloadTime,    }
                                            $WhatIWant);   my $courseData = 
             if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_WRCREAT(),0640)) {      &DownloadCourseInformation($_, $courseID, $downloadTime, 
                 foreach my $key (keys(%$courseData)) {         $WhatIWant);
                     if($key =~ /^(con_lost|error|no_such_host)/i) {   if(tie(%cache,'GDBM_File',$cacheDB,&GDBM_WRCREAT(),0640)) {
                         $courseData->{$_.':error'} = 'No course data for '.$_;      foreach my $key (keys(%$courseData)) {
                         last;   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 {      if($extract eq 'true') {
                     &ProcessStudentData(\%cache, $courseData, $_);   &ExtractStudentData($courseData, \%cache, \%cache, $_);
                 }      } else {
                 untie(%cache);   &ProcessStudentData(\%cache, $courseData, $_);
             } else {      }
                 next;      untie(%cache);
             }   } else {
         }      next;
    }
     }      }
     if($status eq 'true') { &Apache::lonhtmlcommon::Close_PrgWin($r); }      if($status eq 'true') { &Apache::lonhtmlcommon::Close_PrgWin($r); }
   
Line 1159  sub DownloadStudentCourseData { Line 1355  sub DownloadStudentCourseData {
   
 sub DownloadStudentCourseDataSeparate {  sub DownloadStudentCourseDataSeparate {
     my ($students,$checkDate,$cacheDB,$extract,$status,$courseID,$r,$c)=@_;      my ($students,$checkDate,$cacheDB,$extract,$status,$courseID,$r,$c)=@_;
     my $residualFile = '/home/httpd/perl/tmp/'.$courseID.'DownloadFile.db';      my $residualFile = $Apache::lonnet::tmpdir.$courseID.'DownloadFile.db';
     my $title = 'LON-CAPA Statistics';      my $title = 'LON-CAPA Statistics';
     my $heading = 'Download Course Data';      my $heading = 'Download Course Data';
   
     my $WhatIWant;      my $WhatIWant;
     $WhatIWant = '(^version:|';      $WhatIWant = '(^version:|';
     $WhatIWant .= '^\d+:.+?:(resource\.\d+\.';      $WhatIWant .= '^\d+:.+?:(resource\.\d+\.';
     $WhatIWant .= '(solved|tries|previous|awarded|(\d+\.submission))\s*$';      $WhatIWant .= '(solved|tries|previous|awarded|(\d+\.submission))\s*$';#'
     $WhatIWant .= '|timestamp)';      $WhatIWant .= '|timestamp)';
     $WhatIWant .= ')';      $WhatIWant .= ')';
   
     &CheckForResidualDownload($courseID, $cacheDB, $students, $c);      &CheckForResidualDownload($cacheDB, 'true', 'true', $courseID, $r, $c);
   
     my %cache;  
   
     my $studentCount = scalar(@$students);      my $studentCount = scalar(@$students);
     if($status eq 'true') {      if($status eq 'true') {
Line 1191  sub DownloadStudentCourseDataSeparate { Line 1385  sub DownloadStudentCourseDataSeparate {
             &Apache::lonhtmlcommon::Update_PrgWin($displayString, $r);              &Apache::lonhtmlcommon::Update_PrgWin($displayString, $r);
         }          }
   
           my %cache;
         my $downloadTime='Not downloaded';          my $downloadTime='Not downloaded';
           my $needUpdate = 'false';
         if($checkDate eq 'true'  &&           if($checkDate eq 'true'  && 
            tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {             tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
             $downloadTime = $cache{$_.':lastDownloadTime'};              $downloadTime = $cache{$_.':lastDownloadTime'};
               $needUpdate = $cache{'ResourceUpdated'};
             untie(%cache);              untie(%cache);
         }          }
   
Line 1202  sub DownloadStudentCourseDataSeparate { Line 1399  sub DownloadStudentCourseDataSeparate {
             return 'Aborted';              return 'Aborted';
         }          }
   
         if($downloadTime eq 'Not downloaded') {          if($needUpdate eq 'true') {
             my $error = 0;              $downloadTime = 'Not downloaded';
             my $courseData =    }
                 &DownloadCourseInformation($_, $courseID, $downloadTime,  
                                            $WhatIWant);          my $error = 0;
             my %downloadData;          my $courseData = 
             unless(tie(%downloadData,'GDBM_File',$residualFile,              &DownloadCourseInformation($_, $courseID, $downloadTime,
                        &GDBM_WRCREAT(),0640)) {                                         $WhatIWant);
                 return 'Failed to tie temporary download hash.';          my %downloadData;
             }          unless(tie(%downloadData,'GDBM_File',$residualFile,
             foreach my $key (keys(%$courseData)) {                     &GDBM_WRCREAT(),0640)) {
                 $downloadData{$key} = $courseData->{$key};              return 'Failed to tie temporary download hash.';
                 if($key =~ /^(con_lost|error|no_such_host)/i) {          }
                     $error = 1;          foreach my $key (keys(%$courseData)) {
                     last;              $downloadData{$key} = $courseData->{$key};
                 }              if($key =~ /^(con_lost|error|no_such_host)/i) {
                   $error = 1;
                   last;
             }              }
             if($error) {          }
                 foreach my $deleteKey (keys(%$courseData)) {          if($error) {
                     delete $downloadData{$deleteKey};              foreach my $deleteKey (keys(%$courseData)) {
                 }                  delete $downloadData{$deleteKey};
                 $downloadData{$_.':error'} = 'No course data for '.$_;  
             }              }
             untie(%downloadData);              $downloadData{$_.':error'} = 'No course data for '.$_;
         }          }
           untie(%downloadData);
     }      }
     if($status eq 'true') { &Apache::lonhtmlcommon::Close_PrgWin($r); }      if($status eq 'true') { &Apache::lonhtmlcommon::Close_PrgWin($r); }
   
Line 1237  sub DownloadStudentCourseDataSeparate { Line 1436  sub DownloadStudentCourseDataSeparate {
 sub CheckForResidualDownload {  sub CheckForResidualDownload {
     my ($cacheDB,$extract,$status,$courseID,$r,$c)=@_;      my ($cacheDB,$extract,$status,$courseID,$r,$c)=@_;
   
     my $residualFile = '/home/httpd/perl/tmp/'.$courseID.'DownloadFile.db';      my $residualFile = $Apache::lonnet::tmpdir.$courseID.'DownloadFile.db';
     if(!-e $residualFile) {      if(!-e $residualFile) {
         return 'OK';          return 'OK';
     }      }
Line 1301  sub CheckForResidualDownload { Line 1500  sub CheckForResidualDownload {
     return 'OK';      return 'OK';
 }  }
   
   
   ################################################
   ################################################
   
   =pod
   
   =item &make_into_hash($values);
   
   Returns a reference to a hash as described by $values.  $values is
   assumed to be the result of 
       join(':',map {&Apache::lonnet::escape($_)} %orighash;
   
   This is a helper function for get_current_state.
   
   =cut
   
   ################################################
   ################################################
   sub make_into_hash {
       my $values = shift;
       my %tmp = map { &Apache::lonnet::unescape($_); }
                                              split(':',$values);
       return \%tmp;
   }
   
   
   ################################################
   ################################################
   
   =pod
   
   =item &get_current_state($sname,$sdom,$symb,$courseid);
   
   Retrieve the current status of a students performance.  $sname and
   $sdom are the only required parameters.  If $symb is undef the results
   of an &Apache::lonnet::currentdump() will be returned.  
   If $courseid is undef it will be retrieved from the environment.
   
   The return structure is based on &Apache::lonnet::currentdump.  If
   $symb is unspecified, all the students data is returned in a hash of
   the form:
   ( 
     symb1 => { param1 => value1, param2 => value2 ... },
     symb2 => { param1 => value1, param2 => value2 ... },
   )
   
   If $symb is specified, a hash of 
   (
     param1 => value1, 
     param2 => value2,
   )
   is returned.
   
   If no data is found for $symb, or if the student has not performance data,
   an empty list is returned.
   
   =cut
   
   ################################################
   ################################################
   sub get_current_state {
       my ($sname,$sdom,$symb,$courseid,$forcedownload)=@_;
       return () if (! defined($sname) || ! defined($sdom));
       #
       $courseid = $ENV{'request.course.id'} if (! defined($courseid));
       #
       my $cachefilename = $Apache::lonnet::tmpdir.$ENV{'user.name'}.'_'.
                                                   $ENV{'user.domain'}.'_'.
                                                   $courseid.'_student_data.db';
       my %cache;
       #
       my %student_data; # return values go here
       #
       my $updatetime = 0;
       my $key = &Apache::lonnet::escape($sname).':'.
                 &Apache::lonnet::escape($sdom).':';
       # Open the cache file
       if (tie(%cache,'GDBM_File',$cachefilename,&GDBM_READER(),0640)) {
           if (exists($cache{$key.'time'})) {
               $updatetime = $cache{$key.'time'};
   #            &Apache::lonnet::logthis('got updatetime of '.$updatetime);
           }
           untie(%cache);
       }
       # timestamp/devalidation 
       my $modifiedtime = 1;
       # Take whatever steps are neccessary at this point to give $modifiedtime a
       # new value
       #
       if (($updatetime < $modifiedtime) || 
           (defined($forcedownload) && $forcedownload)) {
   #        &Apache::lonnet::logthis("loading data");
           # Get all the students current data
           my $time_of_retrieval = time;
           my @tmp = &Apache::lonnet::currentdump($courseid,$sdom,$sname);
           if ((scalar(@tmp) > 0) && ($tmp[0] =~ /^error:/)) {
               &Apache::lonnet::logthis('error getting data for '.
                                        $sname.':'.$sdom.' in course '.$courseid.
                                        ':'.$tmp[0]);
               return ();
           }
           %student_data = @tmp;
           #
           # Store away the data
           #
           # The cache structure is colon deliminated.  
           # $uname:$udom:time  => timestamp
           # $uname:$udom:$symb => $parm1:$val1:$parm2:$val2 ...
           #
           # BEWARE: The colons are NOT escaped so can search with escaped 
           #         keys instead of unescaping every key.
           #
           if (tie(%cache,'GDBM_File',$cachefilename,&GDBM_WRCREAT(),0640)) {
   #            &Apache::lonnet::logthis("writing data");
               while (my ($current_symb,$param_hash) = each(%student_data)) {
                   my @Parameters = %{$param_hash};
                   my $value = join(':',map { &Apache::lonnet::escape($_); } 
                                    @Parameters);
                   # Store away the values
                   $cache{$key.&Apache::lonnet::escape($current_symb)}=$value;
               }
               $cache{$key.'time'}=$time_of_retrieval;
               untie(%cache);
           }
       } else {
           &Apache::lonnet::logthis('retrieving cached data ');
           if (tie(%cache,'GDBM_File',$cachefilename,&GDBM_READER(),0640)) {
               if (defined($symb)) {
                   my  $searchkey = $key.&Apache::lonnet::escape($symb);
                   if (exists($cache{$searchkey})) {
                       $student_data{$symb} = &make_into_hash($cache{$searchkey});
                   }
               } else {
                   my $searchkey = '^'.$key.'(.*)$';#'
                   while (my ($testkey,$params)=each(%cache)) {
                       if ($testkey =~ /$searchkey/) { # \Q \E?  May be necc.
                           my $tmpsymb = $1;
                           next if ($tmpsymb =~ 'time');
   #                        &Apache::lonnet::logthis('found '.$tmpsymb.':');
                           $student_data{&Apache::lonnet::unescape($tmpsymb)} = 
                               &make_into_hash($params);
                       }
                   }
               }
               untie(%cache);
           }
       }
       if (! defined($symb)) {
   #        &Apache::lonnet::logthis("returning all data");
           return %student_data;
       } elsif (exists($student_data{$symb})) {
   #        &Apache::lonnet::logthis("returning data for symb=".$symb);
           return %{$student_data{$symb}};
       } else {
           return ();
       }
   }
   
   ################################################
   ################################################
   
   =pod
   
   =item &get_classlist();
   
   Retrieve the classist of a given class or of the current class.  Student
   information is returned from the classlist.db file and, if needed,
   from the students environment.
   
   Optional arguments are $cid, $cdom, and $cnum (course id, course domain,
   and course number, respectively).  Any omitted arguments will be taken 
   from the current environment ($ENV{'request.course.id'},
   $ENV{'course.'.$cid.'.domain'}, and $ENV{'course.'.$cid.'.num'}).
   
   Returns a reference to a hash which contains:
    keys    '$sname:$sdom'
    values  [$end,$start,$id,$section,$fullname]
   
   =cut
   
   ################################################
   ################################################
   
   sub get_classlist {
       my ($cid,$cdom,$cnum) = @_;
       $cid = $cid || $ENV{'request.course.id'};
       $cdom = $cdom || $ENV{'course.'.$cid.'.domain'};
       $cnum = $cnum || $ENV{'course.'.$cid.'.num'};
      my $now = time;
       #
       my %classlist=&Apache::lonnet::dump('classlist',$cdom,$cnum);
       while (my ($student,$info) = each(%classlist)) {
           return undef if ($student =~ /^(con_lost|error|no_such_host)/i);
           my ($sname,$sdom) = split(/:/,$student);
           my @Values = split(/:/,$info);
           my ($end,$start,$id,$section,$fullname);
           if (@Values > 2) {
               ($end,$start,$id,$section,$fullname) = @Values;
           } else { # We have to get the data ourselves
               ($end,$start) = @Values;
               $section = &Apache::lonnet::getsection($sdom,$sname,$cid);
               my %info=&Apache::lonnet::get('environment',
                                             ['firstname','middlename',
                                              'lastname','generation','id'],
                                             $sdom, $sname);
               my ($tmp) = keys(%info);
               if ($tmp =~/^(con_lost|error|no_such_host)/i) {
                   $fullname = 'not available';
                   $id = 'not available';
                   &Apache::lonnet::logthis('unable to retrieve environment '.
                                            'for '.$sname.':'.$sdom);
               } else {
                   $fullname = &ProcessFullName(@info{qw/lastname generation 
                                                          firstname middlename/});
                   $id = $info{'id'};
               }
               # Update the classlist with this students information
               if ($fullname ne 'not available') {
                   my $enrolldata = join(':',$end,$start,$id,$section,$fullname);
                   my $reply=&Apache::lonnet::cput('classlist',
                                                   {$student => $enrolldata},
                                                   $cdom,$cnum);
                   if ($reply !~ /^(ok|delayed)/) {
                       &Apache::lonnet::logthis('Unable to update classlist for '.
                                                'student '.$sname.':'.$sdom.
                                                ' error:'.$reply);
                   }
               }
           }
           my $status='Expired';
           if(((!$end) || $now < $end) && ((!$start) || ($now > $start))) {
               $status='Active';
           }
           $classlist{$student} = 
               [$sdom,$sname,$end,$start,$id,$section,$fullname,$status];
       }
       if (wantarray()) {
           return (\%classlist,['domain','username','end','start','id',
                                'section','fullname','status']);
       } else {
           return \%classlist;
       }
   }
   
 # ----- END HELPER FUNCTIONS --------------------------------------------  # ----- END HELPER FUNCTIONS --------------------------------------------
   
 1;  1;
 __END__  __END__
   
   

Removed from v.1.22  
changed lines
  Added in v.1.49


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