--- loncom/interface/lonnavmaps.pm 2003/08/07 17:26:44 1.221 +++ loncom/interface/lonnavmaps.pm 2003/09/08 19:53:09 1.224 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Navigate Maps Handler # -# $Id: lonnavmaps.pm,v 1.221 2003/08/07 17:26:44 bowersj2 Exp $ +# $Id: lonnavmaps.pm,v 1.224 2003/09/08 19:53:09 bowersj2 Exp $ # # Copyright Michigan State University Board of Trustees # @@ -61,19 +61,14 @@ my $resObj = "Apache::lonnavmaps::resour # Keep these mappings in sync with lonquickgrades, which uses the colors # instead of the icons. my %statusIconMap = - ( $resObj->NETWORK_FAILURE => '', - $resObj->NOTHING_SET => '', - $resObj->CORRECT => 'navmap.correct.gif', - $resObj->EXCUSED => 'navmap.correct.gif', - $resObj->PAST_DUE_NO_ANSWER => 'navmap.wrong.gif', - $resObj->PAST_DUE_ANSWER_LATER => 'navmap.wrong.gif', - $resObj->ANSWER_OPEN => 'navmap.wrong.gif', - $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' ); + ( + $resObj->CLOSED => '', + $resObj->OPEN => 'navmap.open.gif', + $resObj->CORRECT => 'navmap.correct.gif', + $resObj->INCORRECT => 'navmap.wrong.gif', + $resObj->ATTEMPTED => 'navmap.ellipsis.gif', + $resObj->ERROR => '' + ); my %iconAltTags = ( 'navmap.correct.gif' => 'Correct', @@ -168,23 +163,17 @@ sub real_handler { # See if there's only one map in the top-level, if we don't # already have a filter... if so, automatically display it + # (older code; should use retrieveResources) if ($ENV{QUERY_STRING} !~ /filter/) { my $iterator = $navmap->getIterator(undef, undef, undef, 0); - my $depth = 1; - $iterator->next(); - my $curRes = $iterator->next(); + my $curRes; my $sequenceCount = 0; my $sequenceId; - while ($depth > 0) { - if ($curRes == $iterator->BEGIN_MAP()) { $depth++; } - if ($curRes == $iterator->END_MAP()) { $depth--; } - + while ($curRes = $iterator->next()) { if (ref($curRes) && $curRes->is_sequence()) { $sequenceCount++; $sequenceId = $curRes->map_pc(); } - - $curRes = $iterator->next(); } if ($sequenceCount == 1) { @@ -202,16 +191,11 @@ sub real_handler { $jumpToFirstHomework = 1; # Find the next homework problem that they can do. my $iterator = $navmap->getIterator(undef, undef, undef, 1); - my $depth = 1; - $iterator->next(); - my $curRes = $iterator->next(); + my $curRes; my $foundDoableProblem = 0; my $problemRes; - while ($depth > 0 && !$foundDoableProblem) { - if ($curRes == $iterator->BEGIN_MAP()) { $depth++; } - if ($curRes == $iterator->END_MAP()) { $depth--; } - + while (($curRes = $iterator->next()) && !$foundDoableProblem) { if (ref($curRes) && $curRes->is_problem()) { my $status = $curRes->status(); if ($curRes->completable()) { @@ -229,8 +213,6 @@ sub real_handler { $ENV{'form.postsymb'} = $curRes->symb(); } } - } continue { - $curRes = $iterator->next(); } # If we found no problems, print a note to that effect. @@ -1049,7 +1031,8 @@ sub render_quick_status { if ($resource->is_problem() && !$firstDisplayed) { - my $icon = $statusIconMap{$resource->status($part)}; + + my $icon = $statusIconMap{$resource->simpleStatus($part)}; my $alt = $iconAltTags{$icon}; if ($icon) { $result .= "$linkopen$alt$linkclose\n"; @@ -1185,18 +1168,13 @@ sub render { # Step three: Ensure the folders are open my $mapIterator = $navmap->getIterator(undef, undef, undef, 1); - my $depth = 1; - $mapIterator->next(); # discard the first BEGIN_MAP - my $curRes = $mapIterator->next(); + my $curRes; my $found = 0; # 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 # for the jump marker with this loop. - while ($depth > 0 && !$found) { - if ($curRes == $mapIterator->BEGIN_MAP()) { $depth++; } - if ($curRes == $mapIterator->END_MAP()) { $depth--; } - + while (($curRes = $mapIterator->next()) && !$found) { if (ref($curRes) && $curRes->symb() eq $here) { my $mapStack = $mapIterator->getStack(); @@ -1210,8 +1188,6 @@ sub render { } $found = 1; } - - $curRes = $mapIterator->next(); } } @@ -1249,15 +1225,11 @@ sub render { # Note this does not take filtering or hidden into account... need # to be fixed? my $mapIterator = $navmap->getIterator(undef, undef, $filterHash, 0); - my $depth = 1; - $mapIterator->next(); - my $curRes = $mapIterator->next(); + my $curRes; my $foundJump = 0; my $counter = 0; - while ($depth > 0 && !$foundJump) { - if ($curRes == $mapIterator->BEGIN_MAP()) { $depth++; } - if ($curRes == $mapIterator->END_MAP()) { $depth--; } + while (($curRes = $mapIterator->next()) && !$foundJump) { if (ref($curRes)) { $counter++; } if (ref($curRes) && $jump eq $curRes->symb()) { @@ -1268,8 +1240,6 @@ sub render { $args->{'currentJumpIndex'} = $counter; $foundJump = 1; } - - $curRes = $mapIterator->next(); } my $showParts = setDefault($args->{'showParts'}, 1); @@ -1347,7 +1317,7 @@ sub render { $it->{FIRST_RESOURCE}, $it->{FINISH_RESOURCE}, {}, undef, 1); - $depth = 0; + my $depth = 0; $dfsit->next(); my $curRes = $dfsit->next(); while ($depth > -1) { @@ -1379,9 +1349,6 @@ sub render { my $displayedJumpMarker = 0; # Set up iteration. - $depth = 1; - $it->next(); # discard initial BEGIN_MAP - $curRes = $it->next(); my $now = time(); my $in24Hours = $now + 24 * 60 * 60; my $rownum = 0; @@ -1389,10 +1356,8 @@ sub render { # export "here" marker information $args->{'here'} = $here; - while ($depth > 0) { - if ($curRes == $it->BEGIN_MAP()) { $depth++; } - if ($curRes == $it->END_MAP()) { $depth--; } - + $args->{'indentLevel'} = -1; # first BEGIN_MAP takes this to 0 + while ($curRes = $it->next()) { # Maintain indentation level. if ($curRes == $it->BEGIN_MAP() || $curRes == $it->BEGIN_BRANCH() ) { @@ -1545,8 +1510,6 @@ sub render { $r->rflush(); } } continue { - $curRes = $it->next(); - if ($r) { # If we have the connection, make sure the user is still connected my $c = $r->connection; @@ -2184,18 +2147,9 @@ sub retrieveResources { my @resources = (); # Run down the iterator and collect the resources. - my $depth = 1; - $it->next(); - my $curRes = $it->next(); - - while ($depth > 0) { - if ($curRes == $it->BEGIN_MAP()) { - $depth++; - } - if ($curRes == $it->END_MAP()) { - $depth--; - } - + my $curRes; + + while ($curRes = $it->next()) { if (ref($curRes)) { if (!&$filterFunc($curRes)) { next; @@ -2208,8 +2162,6 @@ sub retrieveResources { } } - } continue { - $curRes = $it->next(); } return @resources; @@ -2285,6 +2237,13 @@ new branch. The possible tokens are: =over 4 +=item * B: + +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: A new map is being recursed into. This is returned I the map @@ -2315,12 +2274,33 @@ consisting entirely of empty resources e ending resource, will cause a lot of BRANCH_STARTs and BRANCH_ENDs, 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 =cut # Here are the tokens for the iterator: +sub END_ITERATOR { return 0; } sub BEGIN_MAP { return 1; } # begining of a new map sub END_MAP { return 2; } # end of the map sub BEGIN_BRANCH { return 3; } # beginning of a branch @@ -2406,13 +2386,13 @@ sub new { # prime the recursion $self->{$firstResourceName}->{DATA}->{$valName} = 0; - my $depth = 0; - $iterator->next(); + $iterator->next(); my $curRes = $iterator->next(); - while ($depth > -1) { - if ($curRes == $iterator->BEGIN_MAP()) { $depth++; } - if ($curRes == $iterator->END_MAP()) { $depth--; } - + my $depth = 1; + while ($depth > 0) { + if ($curRes == $iterator->BEGIN_MAP()) { $depth++; } + if ($curRes == $iterator->END_MAP()) { $depth--; } + if (ref($curRes)) { # If there's only one resource, this will save it # we have to filter empty resources from consideration here, @@ -2446,8 +2426,8 @@ sub new { $curRes->{DATA}->{DISPLAY_DEPTH} = $finalDepth; if ($finalDepth > $maxDepth) {$maxDepth = $finalDepth;} } - } continue { - $curRes = $iterator->next(); + + $curRes = $iterator->next(); } } @@ -2468,6 +2448,7 @@ sub new { $self->{MAX_DEPTH} = $maxDepth; $self->{STACK} = []; $self->{RECURSIVE_ITERATOR_FLAG} = 0; + $self->{FINISHED} = 0; # When true, the iterator has finished for (my $i = 0; $i <= $self->{MAX_DEPTH}; $i++) { push @{$self->{STACK}}, []; @@ -2485,6 +2466,10 @@ sub new { sub next { my $self = shift; + if ($self->{FINISHED}) { + return END_ITERATOR(); + } + # If we want to return the top-level map object, and haven't yet, # do so. if ($self->{RETURN_0} && !$self->{HAVE_RETURNED_0}) { @@ -2544,6 +2529,7 @@ sub next { $self->{CURRENT_DEPTH}--; return END_BRANCH(); } else { + $self->{FINISHED} = 1; return END_MAP(); } } @@ -3040,9 +3026,9 @@ sub symb { my $self=shift; (my $first, my $second) = $self->{ID} =~ /(\d+).(\d+)/; my $symbSrc = &Apache::lonnet::declutter($self->src()); - return &Apache::lonnet::declutter( - $self->navHash('map_id_'.$first)) + my $symb = &Apache::lonnet::declutter($self->navHash('map_id_'.$first)) . '___' . $second . '___' . $symbSrc; + return &Apache::lonnet::symbclean($symb); } sub title { my $self=shift; @@ -3881,6 +3867,7 @@ sub status { # dimension and 5 entries on the other, which we want to colorize, # plus network failure and "no date data at all". + #if ($self->{RESOURCE_ERROR}) { return NETWORK_FAILURE; } if ($completionStatus == NETWORK_FAILURE) { return NETWORK_FAILURE; } my $suppressFeedback = lc($self->parmval("problemstatus", $part)) eq 'no'; @@ -3927,7 +3914,7 @@ sub status { if ($completionStatus == INCORRECT || $completionStatus == INCORRECT_BY_OVERRIDE) { # and there are TRIES LEFT: 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 } @@ -3936,6 +3923,64 @@ sub status { return OPEN; } +sub CLOSED { return 23; } +sub ERROR { return 24; } + +=pod + +B + +Convenience method B 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: The problem is currently closed. (No icon shown.) + +=item * B: The problem is open and unattempted. + +=item * B: The problem is correct for any reason. + +=item * B: The problem is incorrect and can still be +completed successfully. + +=item * B: The problem has been attempted, but the student +does not know if they are correct. (The ellipsis icon.) + +=item * B: 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