Diff for /loncom/interface/lonnavmaps.pm between versions 1.221 and 1.225

version 1.221, 2003/08/07 17:26:44 version 1.225, 2003/09/08 22:44:36
Line 61  my $resObj = "Apache::lonnavmaps::resour Line 61  my $resObj = "Apache::lonnavmaps::resour
 # Keep these mappings in sync with lonquickgrades, which uses the colors  # Keep these mappings in sync with lonquickgrades, which uses the colors
 # instead of the icons.  # instead of the icons.
 my %statusIconMap =   my %statusIconMap = 
     ( $resObj->NETWORK_FAILURE    => '',      (
       $resObj->NOTHING_SET        => '',       $resObj->CLOSED       => '',
       $resObj->CORRECT            => 'navmap.correct.gif',       $resObj->OPEN         => 'navmap.open.gif',
       $resObj->EXCUSED            => 'navmap.correct.gif',       $resObj->CORRECT      => 'navmap.correct.gif',
       $resObj->PAST_DUE_NO_ANSWER => 'navmap.wrong.gif',       $resObj->INCORRECT    => 'navmap.wrong.gif',
       $resObj->PAST_DUE_ANSWER_LATER => 'navmap.wrong.gif',       $resObj->ATTEMPTED    => 'navmap.ellipsis.gif',
       $resObj->ANSWER_OPEN        => 'navmap.wrong.gif',       $resObj->ERROR        => ''
       $resObj->OPEN_LATER         => '',       );
       $resObj->TRIES_LEFT         => 'navmap.open.gif',  
       $resObj->INCORRECT          => 'navmap.wrong.gif',  
       $resObj->OPEN               => 'navmap.open.gif',  
       $resObj->ATTEMPTED          => 'navmap.ellipsis.gif',  
       $resObj->ANSWER_SUBMITTED   => 'navmap.ellipsis.gif' );  
   
 my %iconAltTags =   my %iconAltTags = 
     ( 'navmap.correct.gif' => 'Correct',      ( 'navmap.correct.gif' => 'Correct',
Line 168  sub real_handler { Line 163  sub real_handler {
   
     # See if there's only one map in the top-level, if we don't      # See if there's only one map in the top-level, if we don't
     # already have a filter... if so, automatically display it      # already have a filter... if so, automatically display it
       # (older code; should use retrieveResources)
     if ($ENV{QUERY_STRING} !~ /filter/) {      if ($ENV{QUERY_STRING} !~ /filter/) {
         my $iterator = $navmap->getIterator(undef, undef, undef, 0);          my $iterator = $navmap->getIterator(undef, undef, undef, 0);
         my $depth = 1;          my $curRes;
         $iterator->next();  
         my $curRes = $iterator->next();  
         my $sequenceCount = 0;          my $sequenceCount = 0;
         my $sequenceId;          my $sequenceId;
         while ($depth > 0) {          while ($curRes = $iterator->next()) {
             if ($curRes == $iterator->BEGIN_MAP()) { $depth++; }  
             if ($curRes == $iterator->END_MAP()) { $depth--; }  
               
             if (ref($curRes) && $curRes->is_sequence()) {              if (ref($curRes) && $curRes->is_sequence()) {
                 $sequenceCount++;                  $sequenceCount++;
                 $sequenceId = $curRes->map_pc();                  $sequenceId = $curRes->map_pc();
             }              }
               
             $curRes = $iterator->next();  
         }          }
                   
         if ($sequenceCount == 1) {          if ($sequenceCount == 1) {
Line 202  sub real_handler { Line 191  sub real_handler {
         $jumpToFirstHomework = 1;          $jumpToFirstHomework = 1;
         # Find the next homework problem that they can do.          # Find the next homework problem that they can do.
         my $iterator = $navmap->getIterator(undef, undef, undef, 1);          my $iterator = $navmap->getIterator(undef, undef, undef, 1);
         my $depth = 1;          my $curRes;
         $iterator->next();  
         my $curRes = $iterator->next();  
         my $foundDoableProblem = 0;          my $foundDoableProblem = 0;
         my $problemRes;          my $problemRes;
                   
         while ($depth > 0 && !$foundDoableProblem) {          while (($curRes = $iterator->next()) && !$foundDoableProblem) {
             if ($curRes == $iterator->BEGIN_MAP()) { $depth++; }  
             if ($curRes == $iterator->END_MAP()) { $depth--; }  
   
             if (ref($curRes) && $curRes->is_problem()) {              if (ref($curRes) && $curRes->is_problem()) {
                 my $status = $curRes->status();                  my $status = $curRes->status();
                 if ($curRes->completable()) {                  if ($curRes->completable()) {
Line 229  sub real_handler { Line 213  sub real_handler {
                     $ENV{'form.postsymb'} = $curRes->symb();                      $ENV{'form.postsymb'} = $curRes->symb();
                 }                  }
             }              }
         } continue {  
             $curRes = $iterator->next();  
         }          }
   
         # If we found no problems, print a note to that effect.          # If we found no problems, print a note to that effect.
Line 673  can't close or open folders when this is Line 655  can't close or open folders when this is
   
 =back  =back
   
 =item B<Apache::lonnavmaps::communication_status>:  =item * B<Apache::lonnavmaps::communication_status>:
   
 Whether there is discussion on the resource, email for the user, or  Whether there is discussion on the resource, email for the user, or
 (lumped in here) perl errors in the execution of the problem. This is  (lumped in here) perl errors in the execution of the problem. This is
 the second column in the main nav map.  the second column in the main nav map.
   
 =item B<Apache::lonnavmaps::quick_status>:  =item * B<Apache::lonnavmaps::quick_status>:
   
 An icon for the status of a problem, with five possible states:  An icon for the status of a problem, with five possible states:
 Correct, incorrect, open, awaiting grading (for a problem where the  Correct, incorrect, open, awaiting grading (for a problem where the
Line 687  computer's grade is suppressed, or the c Line 669  computer's grade is suppressed, or the c
 essay problem), or none (not open yet, not a problem). The  essay problem), or none (not open yet, not a problem). The
 third column of the standard navmap.  third column of the standard navmap.
   
 =item B<Apache::lonnavmaps::long_status>:  =item * B<Apache::lonnavmaps::long_status>:
   
 A text readout of the details of the current status of the problem,  A text readout of the details of the current status of the problem,
 such as "Due in 22 hours". The fourth column of the standard navmap.  such as "Due in 22 hours". The fourth column of the standard navmap.
   
   =item * B<Apache::lonnavmaps::part_status_summary>:
   
   A text readout summarizing the status of the problem. If it is a
   single part problem, will display "Correct", "Incorrect", 
   "Not yet open", "Open", "Attempted", or "Error". If there are
   multiple parts, this will output a string that in HTML will show a
   status of how many parts are in each status, in color coding, trying
   to match the colors of the icons within reason.
   
   Note this only makes sense if you are I<not> showing parts. If 
   C<showParts> is true (see below), this column will not output
   anything. 
   
 =back  =back
   
 If you add any others please be sure to document them here.  If you add any others please be sure to document them here.
Line 859  sub resource { return 0; } Line 854  sub resource { return 0; }
 sub communication_status { return 1; }  sub communication_status { return 1; }
 sub quick_status { return 2; }  sub quick_status { return 2; }
 sub long_status { return 3; }  sub long_status { return 3; }
   sub part_status_summary { return 4; }
 # Data for render_resource  
   
 sub render_resource {  sub render_resource {
     my ($resource, $part, $params) = @_;      my ($resource, $part, $params) = @_;
Line 1049  sub render_quick_status { Line 1043  sub render_quick_status {
   
     if ($resource->is_problem() &&      if ($resource->is_problem() &&
         !$firstDisplayed) {          !$firstDisplayed) {
         my $icon = $statusIconMap{$resource->status($part)};  
           my $icon = $statusIconMap{$resource->simpleStatus($part)};
         my $alt = $iconAltTags{$icon};          my $alt = $iconAltTags{$icon};
         if ($icon) {          if ($icon) {
             $result .= "<td width='30' valign='center' width='50' align='right'>$linkopen<img width='25' height='25' src='/adm/lonIcons/$icon' border='0' alt='$alt' />$linkclose</td>\n";              $result .= "<td width='30' valign='center' width='50' align='right'>$linkopen<img width='25' height='25' src='/adm/lonIcons/$icon' border='0' alt='$alt' />$linkclose</td>\n";
Line 1098  sub render_long_status { Line 1093  sub render_long_status {
     return $result;      return $result;
 }  }
   
   my %statusColors = 
       (
        $resObj->CLOSED => '#000000',
        $resObj->OPEN   => '#000000',
        $resObj->CORRECT => '#000000',
        $resObj->INCORRECT => '#000000',
        $resObj->ATTEMPTED => '#000000',
        $resObj->ERROR => '#000000'
        );
   my %statusStrings = 
       (
        $resObj->CLOSED => 'Not yet open',
        $resObj->OPEN   => 'Open',
        $resObj->CORRECT => 'Correct',
        $resObj->INCORRECT => 'Incorrect',
        $resObj->ATTEMPTED => 'Attempted',
        $resObj->ERROR => 'Network Error'
        );
   my @statuses = ($resObj->CORRECT, $resObj->ATTEMPTED, $resObj->INCORRECT, $resObj->OPEN, $resObj->CLOSED, $resObj->ERROR);
   
   use Data::Dumper;
   sub render_parts_summary_status {
       my ($resource, $part, $params) = @_;
       if (!$resource->is_problem()) { return '<td></td>'; }
       if ($params->{showParts}) { 
    return '<td></td>';
       }
   
       my $td = "<td align='right'>\n";
       my $endtd = "</td>\n";
   
       # If there is a single part, just show the simple status
       if ($resource->singlepart()) {
    my $status = $resource->simpleStatus('0');
    return $td . "<font color='" . $statusColors{$status} . "'>"
       . $statusStrings{$status} . "</font>" . $endtd;
       }
   
       # Now we can be sure the $part doesn't really matter.
       my $statusCount = $resource->simpleStatusCount();
       my @counts;
       foreach my $status(@statuses) {
    # decouple display order from the simpleStatusCount order
    my $slot = Apache::lonnavmaps::resource::statusToSlot($status);
    if ($statusCount->[$slot]) {
       push @counts, "<font color='" . $statusColors{$status} .
    "'>" . $statusCount->[$slot] . ' '
    . $statusStrings{$status} . "</font>";
    }
       }
   
       return $td . join (', ', @counts) . $endtd;
   }
   
 my @preparedColumns = (\&render_resource, \&render_communication_status,  my @preparedColumns = (\&render_resource, \&render_communication_status,
                        \&render_quick_status, \&render_long_status);                         \&render_quick_status, \&render_long_status,
          \&render_parts_summary_status);
   
 sub setDefault {  sub setDefault {
     my ($val, $default) = @_;      my ($val, $default) = @_;
Line 1185  sub render { Line 1235  sub render {
   
         # Step three: Ensure the folders are open          # Step three: Ensure the folders are open
         my $mapIterator = $navmap->getIterator(undef, undef, undef, 1);          my $mapIterator = $navmap->getIterator(undef, undef, undef, 1);
         my $depth = 1;          my $curRes;
         $mapIterator->next(); # discard the first BEGIN_MAP  
         my $curRes = $mapIterator->next();  
         my $found = 0;          my $found = 0;
                   
         # We only need to do this if we need to open the maps to show the          # We only need to do this if we need to open the maps to show the
         # current position. This will change the counter so we can't count          # current position. This will change the counter so we can't count
         # for the jump marker with this loop.          # for the jump marker with this loop.
         while ($depth > 0 && !$found) {          while (($curRes = $mapIterator->next()) && !$found) {
             if ($curRes == $mapIterator->BEGIN_MAP()) { $depth++; }  
             if ($curRes == $mapIterator->END_MAP()) { $depth--; }  
               
             if (ref($curRes) && $curRes->symb() eq $here) {              if (ref($curRes) && $curRes->symb() eq $here) {
                 my $mapStack = $mapIterator->getStack();                  my $mapStack = $mapIterator->getStack();
                                   
Line 1210  sub render { Line 1255  sub render {
                 }                  }
                 $found = 1;                  $found = 1;
             }              }
               
             $curRes = $mapIterator->next();  
         }                      }            
     }              }        
   
Line 1249  sub render { Line 1292  sub render {
     # Note this does not take filtering or hidden into account... need      # Note this does not take filtering or hidden into account... need
     # to be fixed?      # to be fixed?
     my $mapIterator = $navmap->getIterator(undef, undef, $filterHash, 0);      my $mapIterator = $navmap->getIterator(undef, undef, $filterHash, 0);
     my $depth = 1;      my $curRes;
     $mapIterator->next();  
     my $curRes = $mapIterator->next();  
     my $foundJump = 0;      my $foundJump = 0;
     my $counter = 0;      my $counter = 0;
           
     while ($depth > 0 && !$foundJump) {      while (($curRes = $mapIterator->next()) && !$foundJump) {
         if ($curRes == $mapIterator->BEGIN_MAP()) { $depth++; }  
         if ($curRes == $mapIterator->END_MAP()) { $depth--; }  
         if (ref($curRes)) { $counter++; }          if (ref($curRes)) { $counter++; }
                   
         if (ref($curRes) && $jump eq $curRes->symb()) {          if (ref($curRes) && $jump eq $curRes->symb()) {
Line 1268  sub render { Line 1307  sub render {
             $args->{'currentJumpIndex'} = $counter;              $args->{'currentJumpIndex'} = $counter;
             $foundJump = 1;              $foundJump = 1;
         }          }
           
         $curRes = $mapIterator->next();  
     }      }
   
     my $showParts = setDefault($args->{'showParts'}, 1);      my $showParts = setDefault($args->{'showParts'}, 1);
Line 1347  sub render { Line 1384  sub render {
                                                          $it->{FIRST_RESOURCE},                                                           $it->{FIRST_RESOURCE},
                                                          $it->{FINISH_RESOURCE},                                                           $it->{FINISH_RESOURCE},
                                                          {}, undef, 1);                                                           {}, undef, 1);
         $depth = 0;          my $depth = 0;
         $dfsit->next();          $dfsit->next();
         my $curRes = $dfsit->next();          my $curRes = $dfsit->next();
         while ($depth > -1) {          while ($depth > -1) {
Line 1379  sub render { Line 1416  sub render {
   
     my $displayedJumpMarker = 0;      my $displayedJumpMarker = 0;
     # Set up iteration.      # Set up iteration.
     $depth = 1;  
     $it->next(); # discard initial BEGIN_MAP  
     $curRes = $it->next();  
     my $now = time();      my $now = time();
     my $in24Hours = $now + 24 * 60 * 60;      my $in24Hours = $now + 24 * 60 * 60;
     my $rownum = 0;      my $rownum = 0;
Line 1389  sub render { Line 1423  sub render {
     # export "here" marker information      # export "here" marker information
     $args->{'here'} = $here;      $args->{'here'} = $here;
   
     while ($depth > 0) {      $args->{'indentLevel'} = -1; # first BEGIN_MAP takes this to 0
         if ($curRes == $it->BEGIN_MAP()) { $depth++; }      while ($curRes = $it->next()) {
         if ($curRes == $it->END_MAP()) { $depth--; }  
   
         # Maintain indentation level.          # Maintain indentation level.
         if ($curRes == $it->BEGIN_MAP() ||          if ($curRes == $it->BEGIN_MAP() ||
             $curRes == $it->BEGIN_BRANCH() ) {              $curRes == $it->BEGIN_BRANCH() ) {
Line 1545  sub render { Line 1577  sub render {
             $r->rflush();              $r->rflush();
         }          }
     } continue {      } continue {
         $curRes = $it->next();  
   
  if ($r) {   if ($r) {
     # If we have the connection, make sure the user is still connected      # If we have the connection, make sure the user is still connected
     my $c = $r->connection;      my $c = $r->connection;
Line 2184  sub retrieveResources { Line 2214  sub retrieveResources {
     my @resources = ();      my @resources = ();
   
     # Run down the iterator and collect the resources.      # Run down the iterator and collect the resources.
     my $depth = 1;      my $curRes;
     $it->next();  
     my $curRes = $it->next();      while ($curRes = $it->next()) {
   
     while ($depth > 0) {  
         if ($curRes == $it->BEGIN_MAP()) {  
             $depth++;  
         }  
         if ($curRes == $it->END_MAP()) {  
             $depth--;  
         }  
           
         if (ref($curRes)) {          if (ref($curRes)) {
             if (!&$filterFunc($curRes)) {              if (!&$filterFunc($curRes)) {
                 next;                  next;
Line 2208  sub retrieveResources { Line 2229  sub retrieveResources {
             }              }
         }          }
   
     } continue {  
         $curRes = $it->next();  
     }      }
   
     return @resources;      return @resources;
Line 2285  new branch. The possible tokens are: Line 2304  new branch. The possible tokens are:
   
 =over 4  =over 4
   
   =item * B<END_ITERATOR>:
   
   The iterator has returned all that it's going to. Further calls to the
   iterator will just produce more of these. This is a "false" value, and
   is the only false value the iterator which will be returned, so it can
   be used as a loop sentinel.
   
 =item * B<BEGIN_MAP>:  =item * B<BEGIN_MAP>:
   
 A new map is being recursed into. This is returned I<after> the map  A new map is being recursed into. This is returned I<after> the map
Line 2315  consisting entirely of empty resources e Line 2341  consisting entirely of empty resources e
 ending resource, will cause a lot of BRANCH_STARTs and BRANCH_ENDs,  ending resource, will cause a lot of BRANCH_STARTs and BRANCH_ENDs,
 but only one resource will be returned.  but only one resource will be returned.
   
   =head2 Normal Usage
   
   Normal usage of the iterator object is to do the following:
   
    my $it = $navmap->getIterator([your params here]);
    my $curRes;
    while ($curRes = $it->next()) {
      [your logic here]
    }
   
   Note that inside of the loop, it's frequently useful to check if
   "$curRes" is a reference or not with the reference function; only
   resource objects will be references, and any non-references will 
   be the tokens described above.
   
   Also note there is some old code floating around that trys to track
   the depth of the iterator to see when it's done; do not copy that 
   code. It is difficult to get right and harder to understand then
   this. They should be migrated to this new style.
   
 =back  =back
   
 =cut  =cut
   
 # Here are the tokens for the iterator:  # Here are the tokens for the iterator:
   
   sub END_ITERATOR { return 0; }
 sub BEGIN_MAP { return 1; }    # begining of a new map  sub BEGIN_MAP { return 1; }    # begining of a new map
 sub END_MAP { return 2; }      # end of the map  sub END_MAP { return 2; }      # end of the map
 sub BEGIN_BRANCH { return 3; } # beginning of a branch  sub BEGIN_BRANCH { return 3; } # beginning of a branch
Line 2406  sub new { Line 2453  sub new {
           
         # prime the recursion          # prime the recursion
         $self->{$firstResourceName}->{DATA}->{$valName} = 0;          $self->{$firstResourceName}->{DATA}->{$valName} = 0;
         my $depth = 0;   $iterator->next();
         $iterator->next();  
         my $curRes = $iterator->next();          my $curRes = $iterator->next();
         while ($depth > -1) {   my $depth = 1;
             if ($curRes == $iterator->BEGIN_MAP()) { $depth++; }          while ($depth > 0) {
             if ($curRes == $iterator->END_MAP()) { $depth--; }      if ($curRes == $iterator->BEGIN_MAP()) { $depth++; }
               if ($curRes == $iterator->END_MAP()) { $depth--; }
   
             if (ref($curRes)) {              if (ref($curRes)) {
                 # If there's only one resource, this will save it                  # If there's only one resource, this will save it
                 # we have to filter empty resources from consideration here,                  # we have to filter empty resources from consideration here,
Line 2446  sub new { Line 2493  sub new {
                 $curRes->{DATA}->{DISPLAY_DEPTH} = $finalDepth;                  $curRes->{DATA}->{DISPLAY_DEPTH} = $finalDepth;
                 if ($finalDepth > $maxDepth) {$maxDepth = $finalDepth;}                  if ($finalDepth > $maxDepth) {$maxDepth = $finalDepth;}
             }              }
         } continue {  
             $curRes = $iterator->next();      $curRes = $iterator->next();
         }          }
     }      }
   
Line 2468  sub new { Line 2515  sub new {
     $self->{MAX_DEPTH} = $maxDepth;      $self->{MAX_DEPTH} = $maxDepth;
     $self->{STACK} = [];      $self->{STACK} = [];
     $self->{RECURSIVE_ITERATOR_FLAG} = 0;      $self->{RECURSIVE_ITERATOR_FLAG} = 0;
       $self->{FINISHED} = 0; # When true, the iterator has finished
   
     for (my $i = 0; $i <= $self->{MAX_DEPTH}; $i++) {      for (my $i = 0; $i <= $self->{MAX_DEPTH}; $i++) {
         push @{$self->{STACK}}, [];          push @{$self->{STACK}}, [];
Line 2485  sub new { Line 2533  sub new {
 sub next {  sub next {
     my $self = shift;      my $self = shift;
   
       if ($self->{FINISHED}) {
    return END_ITERATOR();
       }
   
     # If we want to return the top-level map object, and haven't yet,      # If we want to return the top-level map object, and haven't yet,
     # do so.      # do so.
     if ($self->{RETURN_0} && !$self->{HAVE_RETURNED_0}) {      if ($self->{RETURN_0} && !$self->{HAVE_RETURNED_0}) {
Line 2544  sub next { Line 2596  sub next {
             $self->{CURRENT_DEPTH}--;              $self->{CURRENT_DEPTH}--;
             return END_BRANCH();              return END_BRANCH();
         } else {          } else {
       $self->{FINISHED} = 1;
             return END_MAP();              return END_MAP();
         }          }
     }      }
Line 3040  sub symb { Line 3093  sub symb {
     my $self=shift;      my $self=shift;
     (my $first, my $second) = $self->{ID} =~ /(\d+).(\d+)/;      (my $first, my $second) = $self->{ID} =~ /(\d+).(\d+)/;
     my $symbSrc = &Apache::lonnet::declutter($self->src());      my $symbSrc = &Apache::lonnet::declutter($self->src());
     return &Apache::lonnet::declutter(      my $symb = &Apache::lonnet::declutter($self->navHash('map_id_'.$first)) 
          $self->navHash('map_id_'.$first))   
         . '___' . $second . '___' . $symbSrc;          . '___' . $second . '___' . $symbSrc;
       return &Apache::lonnet::symbclean($symb);
 }  }
 sub title {   sub title { 
     my $self=shift;       my $self=shift; 
Line 3478  sub multipart { Line 3531  sub multipart {
     return $self->countParts() > 1;      return $self->countParts() > 1;
 }  }
   
   sub singlepart {
       my $self = shift;
       return $self->countParts() == 1;
   }
   
 sub responseType {  sub responseType {
     my $self = shift;      my $self = shift;
     my $part = shift;      my $part = shift;
Line 3881  sub status { Line 3939  sub status {
     # dimension and 5 entries on the other, which we want to colorize,      # dimension and 5 entries on the other, which we want to colorize,
     # plus network failure and "no date data at all".      # plus network failure and "no date data at all".
   
       #if ($self->{RESOURCE_ERROR}) { return NETWORK_FAILURE; }
     if ($completionStatus == NETWORK_FAILURE) { return NETWORK_FAILURE; }      if ($completionStatus == NETWORK_FAILURE) { return NETWORK_FAILURE; }
   
     my $suppressFeedback = lc($self->parmval("problemstatus", $part)) eq 'no';      my $suppressFeedback = lc($self->parmval("problemstatus", $part)) eq 'no';
Line 3927  sub status { Line 3986  sub status {
     if ($completionStatus == INCORRECT || $completionStatus == INCORRECT_BY_OVERRIDE) {      if ($completionStatus == INCORRECT || $completionStatus == INCORRECT_BY_OVERRIDE) {
         # and there are TRIES LEFT:          # and there are TRIES LEFT:
         if ($self->tries($part) < $self->maxtries($part) || !$self->maxtries($part)) {          if ($self->tries($part) < $self->maxtries($part) || !$self->maxtries($part)) {
             return TRIES_LEFT;              return $suppressFeedback ? ANSWER_SUBMITTED : TRIES_LEFT;
         }          }
         return $suppressFeedback ? ANSWER_SUBMITTED : INCORRECT; # otherwise, return orange; student can't fix this          return $suppressFeedback ? ANSWER_SUBMITTED : INCORRECT; # otherwise, return orange; student can't fix this
     }      }
Line 3936  sub status { Line 3995  sub status {
     return OPEN;       return OPEN; 
 }  }
   
   sub CLOSED { return 23; }
   sub ERROR { return 24; }
   
   =pod
   
   B<Simple Status>
   
   Convenience method B<simpleStatus> provides a "simple status" for the resource.
   "Simple status" corresponds to "which icon is shown on the
   Navmaps". There are six "simple" statuses:
   
   =over 4
   
   =item * B<CLOSED>: The problem is currently closed. (No icon shown.)
   
   =item * B<OPEN>: The problem is open and unattempted.
   
   =item * B<CORRECT>: The problem is correct for any reason.
   
   =item * B<INCORRECT>: The problem is incorrect and can still be
   completed successfully.
   
   =item * B<ATTEMPTED>: The problem has been attempted, but the student
   does not know if they are correct. (The ellipsis icon.)
   
   =item * B<ERROR>: There is an error retrieving information about this
   problem.
   
   =back
   
   =cut
   
   # This hash maps the composite status to this simple status, and
   # can be used directly, if you like
   my %compositeToSimple = 
       (
         NETWORK_FAILURE()       => ERROR,
         NOTHING_SET()           => CLOSED,
         CORRECT()               => CORRECT,
         EXCUSED()               => CORRECT,
         PAST_DUE_NO_ANSWER()    => INCORRECT,
         PAST_DUE_ANSWER_LATER() => INCORRECT,
         ANSWER_OPEN()           => INCORRECT,
         OPEN_LATER()            => CLOSED,
         TRIES_LEFT()            => OPEN,
         INCORRECT()             => INCORRECT,
         OPEN()                  => OPEN,
         ATTEMPTED()             => ATTEMPTED,
         ANSWER_SUBMITTED()      => ATTEMPTED
        );
   
   sub simpleStatus {
       my $self = shift;
       my $part = shift;
       my $status = $self->status($part);
       return $compositeToSimple{$status};
   }
   
   =pod
   
   B<simpleStatusCount> will return an array reference containing, in
   this order, the number of OPEN, CLOSED, CORRECT, INCORRECT, ATTEMPTED,
   and ERROR parts the given problem has.
   
   =cut
       
   # This maps the status to the slot we want to increment
   my %statusToSlotMap = 
       (
        OPEN()      => 0,
        CLOSED()    => 1,
        CORRECT()   => 2,
        INCORRECT() => 3,
        ATTEMPTED() => 4,
        ERROR()     => 5
        );
   
   sub statusToSlot { return $statusToSlotMap{shift()}; }
   
   sub simpleStatusCount {
       my $self = shift;
   
       my @counts = (0, 0, 0, 0, 0, 0, 0);
       foreach my $part (@{$self->parts()}) {
    $counts[$statusToSlotMap{$self->simpleStatus($part)}]++;
       }
   
       return \@counts;
   }
   
 =pod  =pod
   
 B<Completable>  B<Completable>

Removed from v.1.221  
changed lines
  Added in v.1.225


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