[LON-CAPA-dev] Navmaps usage note

Jeremy Bowers lon-capa-dev@mail.lon-capa.org
Wed, 16 Jul 2003 15:52:59 -0500


Apache::lonnavmaps::navmaps has been used by most people like this:

-------

my $navmap = [construct navmap];

my $it = $navmap->getIterator(...);

my $depth = 1;
$it->next(); #discard MAP_BEGIN
my $curRes = $it->next();

while ($depth > 0) {
	if ($curRes == $it->BEGIN_MAP()) { $depth++; }
	if ($curRes == $it->END_MAP()) { $depth--; }

	[your code here]
} continue {
	$curRes = $it->next();
}

-------

However, I recommend that unless you intend to actually *do* something 
with BEGIN_BRANCH or END_BRANCH tokens, or really intend to do something 
special when BEGIN_MAP or END_MAP goes by, that you use something like 
this instead:

-------

my $navmap = [construct navmap];

my $mapOfInterest = [get a Resource object that is some map]
my $filter = sub { shift->is_problem() };

my @resources = $navmap->retrieveResource($mapOfInterest, $filter, 1);

for my $res (@resources) {
	[your logic here]
}

-------

It's safer because there's less bookkeeping you have to do yourself, and 
it's less typing.

retrieveResources returns an array populated with 
Apache::lonnavmaps::Resource objects from the course.

The first parameter is which map you want to get the resources from, 
either as a URL of the map, or as a Resource object. It defaults to the 
top-level map of the course.

The second is a filter function which takes in the current Resource 
object, and should return true if you want it in the list, or false if 
you don't want it.

The third is whether the function should recurse down any maps it may 
encounter in the given map.

So, for example

my @resources = $navmap->retrieveResources();

gets all the resources from the course, except the top-level map itself 
which is not included (but is easily added by hand to the array).

my @resources = $navmap->retrieveResources(undef, sub {
     shift->is_problem() }, 1);

will return a list of all problems in the course.

More documentation in the lonnavmaps.pm perldoc.

If you *do* care about the structure, particularly branching, you can't 
do this, because it throws branching information away. If you care about 
where maps begin and end, you can do this (which is now used in 
grades.pm in getSymbMap):

------

my @sequences = $navmap->retrieveResources(undef, sub { shift->is_map();
   }, 1);

for my $sequence ($navmap->getById('0.0'), @sequences) {
     my @resources = $navmap->retrieveResources($sequence);

     [logic here]
}

------

so you'll always know what map you're in. Depends on what you're doing.

Just a note because I think this is a lot easier in the %75 or so of the 
cases where it turns out you don't really care about a lot of the 
nitty-gritty details of how maps work. I will be adding something like 
this to the navmaps perldoc. Probably won't affect anyone before 1.0 
since we don't want to break working code without good reason. (I 
actually feel safer with getSymbMap the way it is now, or I wouldn't 
have changed it for 1.0; some of what it was doing made me a little 
nervous.)

I'd also appreciate it if people using the nav maps do this in the 
future if they don't really need full course structure information, 
because for 1.1 I hope to fix the iterator code to work correctly (you 
should *not* need to track $depth by hand, and it's the #1 source of 
bugs during development right now for people using navmaps; I for one 
keep writing "$depth++" for both of the 'ifs'), and the fewer people 
using it directly and the more people using retrieveResources, the less 
punishment I'll get having to re-write the client code when I make that 
change.