--- loncom/interface/lonnavmaps.pm 2002/11/12 18:24:38 1.102 +++ loncom/interface/lonnavmaps.pm 2002/11/15 19:32:09 1.109 @@ -2,7 +2,7 @@ # The LearningOnline Network with CAPA # Navigate Maps Handler # -# $Id: lonnavmaps.pm,v 1.102 2002/11/12 18:24:38 bowersj2 Exp $ +# $Id: lonnavmaps.pm,v 1.109 2002/11/15 19:32:09 bowersj2 Exp $ # # Copyright Michigan State University Board of Trustees # @@ -37,15 +37,13 @@ # 3/1/1,6/1,17/1,29/1,30/1,2/8,9/21,9/24,9/25 Gerd Kortemeyer # YEAR=2002 # 1/1 Gerd Kortemeyer -# +# Oct-Nov Jeremy Bowers package Apache::lonnavmaps; use strict; use Apache::Constants qw(:common :http); -use Apache::lonnet(); use Apache::loncommon(); -use GDBM_File; use POSIX qw (floor strftime); sub handler { @@ -73,7 +71,7 @@ sub handler { &Apache::loncommon::no_cache($r); $r->send_http_header; - # Create the nav map the nav map + # Create the nav map my $navmap = Apache::lonnavmaps::navmap->new( $ENV{"request.course.fn"}.".db", $ENV{"request.course.fn"}."_parms.db", 1, 1); @@ -437,11 +435,7 @@ sub handler { 'symb='.&Apache::lonnet::escape($curRes->symb()). '"'; - my $title = $curRes->title(); - if (!$title) { - $title = $curRes->src(); - $title = substr ($title, rindex($title, "/") + 1); - } + my $title = $curRes->compTitle(); my $partLabel = ""; my $newBranchText = ""; @@ -1289,6 +1283,11 @@ sub min { if ($a < $b) { return $a; } else { return $b; } } +# In the CVS repository, documentation of this algorithm is included +# in /doc/lonnavdocs, as a PDF and .tex source. Markers like **1** +# will reference the same location in the text as the part of the +# algorithm is running through. + sub new { # magic invocation to create a class instance my $proto = shift; @@ -1331,6 +1330,8 @@ sub new { my $maxDepth = 0; # tracks max depth + # **1** + foreach my $pass (@iterations) { my $direction = $pass->[0]; my $valName = $pass->[1]; @@ -1355,13 +1356,13 @@ sub new { my $resultingVal = $curRes->{DATA}->{$valName}; my $nextResources = $curRes->$nextResourceMethod(); my $resourceCount = scalar(@{$nextResources}); - - if ($resourceCount == 1) { + + if ($resourceCount == 1) { # **3** my $current = $nextResources->[0]->{DATA}->{$valName} || 999999999; $nextResources->[0]->{DATA}->{$valName} = min($resultingVal, $current); } - if ($resourceCount > 1) { + if ($resourceCount > 1) { # **4** foreach my $res (@{$nextResources}) { my $current = $res->{DATA}->{$valName} || 999999999; $res->{DATA}->{$valName} = min($current, $resultingVal + 1); @@ -1369,7 +1370,7 @@ sub new { } } - # Assign the final val + # Assign the final val (**2**) if (ref($curRes) && $direction == BACKWARD()) { my $finalDepth = min($curRes->{DATA}->{TOP_DOWN_VAL}, $curRes->{DATA}->{BOT_UP_VAL}); @@ -1391,7 +1392,7 @@ sub new { push @{$self->{STACK}}, []; } - # Prime the recursion w/ the first resource + # Prime the recursion w/ the first resource **5** push @{$self->{STACK}->[0]}, $self->{FIRST_RESOURCE}; $self->{ALREADY_SEEN}->{$self->{FIRST_RESOURCE}->{ID}} = 1; @@ -1440,8 +1441,8 @@ sub next { my $newDepth; my $here; while ( $i >= 0 && !$found ) { - if ( scalar(@{$self->{STACK}->[$i]}) > 0 ) { - $here = $self->{HERE} = shift @{$self->{STACK}->[$i]}; + if ( scalar(@{$self->{STACK}->[$i]}) > 0 ) { # **6** + $here = pop @{$self->{STACK}->[$i]}; # **7** $found = 1; $newDepth = $i; } @@ -1459,6 +1460,18 @@ sub next { } } + # If this is not a resource, it must be an END_BRANCH marker we want + # to return directly. + if (!ref($here)) { # **8** + if ($here == END_BRANCH()) { # paranoia, in case of later extension + $self->{CURRENT_DEPTH}--; + return $here; + } + } + + # Otherwise, it is a resource and it's safe to store in $self->{HERE} + $self->{HERE} = $here; + # Get to the right level if ( $self->{CURRENT_DEPTH} > $newDepth ) { push @{$self->{STACK}->[$newDepth]}, $here; @@ -1478,14 +1491,29 @@ sub next { # So we need to look at all the resources we can get to from here, # categorize them if we haven't seen them, remember if we have a new my $nextUnfiltered = $here->getNext(); - + my $maxDepthAdded = -1; + for (@$nextUnfiltered) { if (!defined($self->{ALREADY_SEEN}->{$_->{ID}})) { - push @{$self->{STACK}->[$_->{DATA}->{DISPLAY_DEPTH}]}, $_; + my $depth = $_->{DATA}->{DISPLAY_DEPTH}; + push @{$self->{STACK}->[$depth]}, $_; $self->{ALREADY_SEEN}->{$_->{ID}} = 1; + if ($maxDepthAdded < $depth) { $maxDepthAdded = $depth; } } } - + + # Is this the end of a branch? If so, all of the resources examined above + # led to lower levels then the one we are currently at, so we push a END_BRANCH + # marker onto the stack so we don't forget. + # Example: For the usual A(BC)(DE)F case, when the iterator goes down the + # BC branch and gets to C, it will see F as the only next resource, but it's + # one level lower. Thus, this is the end of the branch, since there are no + # more resources added to this level or above. + my $isEndOfBranch = $maxDepthAdded < $self->{CURRENT_DEPTH}; + if ($isEndOfBranch) { # **9** + push @{$self->{STACK}->[$self->{CURRENT_DEPTH}]}, END_BRANCH(); + } + # That ends the main iterator logic. Now, do we want to recurse # down this map (if this resource is a map)? if ($self->{HERE}->is_map() && @@ -1757,6 +1785,8 @@ These are methods that help you retrieve =over 4 +=item * B: Returns a "composite title", that is equal to $res->title() if the resource has a title, and is otherwise the last part of the URL (e.g., "problem.problem"). + =item * B: Returns true if the resource is external. =item * B: Returns the "goesto" value from the compiled nav map. (It is likely you want to use B instead.) @@ -1806,7 +1836,15 @@ sub symb { } sub title { my $self=shift; return $self->navHash("title_", 1); } sub to { my $self=shift; return $self->navHash("to_", 1); } - +sub compTitle { + my $self = shift; + my $title = $self->title(); + if (!$title) { + $title = $self->src(); + $title = substr($title, rindex($title, '/') + 1); + } + return $title; +} =pod B @@ -1966,6 +2004,7 @@ sub answerdate { } return $self->parmval("answerdate", $part); } +sub awarded { my $self = shift; return $self->queryRestoreHash('awarded', shift); } sub duedate { (my $self, my $part) = @_; return $self->parmval("duedate", $part); @@ -1990,24 +2029,18 @@ sub tol { (my $self, my $part) = @_; return $self->parmval("tol", $part); } -sub tries { - my $self = shift; - my $part = shift; - $part = '0' if (!defined($part)); - - # Make sure return hash is loaded, should error check - $self->getReturnHash(); - - my $tries = $self->{RETURN_HASH}->{'resource.'.$part.'.tries'}; - if (!defined($tries)) {return '0';} +sub tries { + my $self = shift; + my $tries = $self->queryRestoreHash('tries', shift); + if (!defined($tries)) { return '0';} return $tries; } sub type { (my $self, my $part) = @_; return $self->parmval("type", $part); } -sub weight { - (my $self, my $part) = @_; +sub weight { + my $self = shift; my $part = shift; return $self->parmval("weight", $part); } @@ -2269,14 +2302,9 @@ sub ATTEMPTED { return 16; } sub getCompletionStatus { my $self = shift; - my $part = shift; - $part = "0" if (!defined($part)); return $self->NETWORK_FAILURE if ($self->{NAV_MAP}->{NETWORK_FAILURE}); - # Make sure return hash exists - $self->getReturnHash(); - - my $status = $self->{RETURN_HASH}->{'resource.'.$part.'.solved'}; + my $status = $self->queryRestoreHash('solved', shift); # Left as seperate if statements in case we ever do more with this if ($status eq 'correct_by_student') {return $self->CORRECT;} @@ -2288,6 +2316,18 @@ sub getCompletionStatus { return $self->NOT_ATTEMPTED; } +sub queryRestoreHash { + my $self = shift; + my $hashentry = shift; + my $part = shift; + $part = "0" if (!defined($part)); + return $self->NETWORK_FAILURE if ($self->{NAV_MAP}->{NETWORK_FAILURE}); + + $self->getReturnHash(); + + return $self->{RETURN_HASH}->{'resource.'.$part.'.'.$hashentry}; +} + =pod B @@ -2433,7 +2473,7 @@ sub getPrevious { # Don't remember it if the student doesn't have browse priviledges # future note: this may properly belong in the client of the resource my $browsePriv = &Apache::lonnet::allowed('bre', $self->src); - if ($browsePriv ne '2' && $browsePriv ne 'F') { + if (!($browsePriv ne '2' && $browsePriv ne 'F')) { push @branches, $prev; } }