--- loncom/interface/lonnavmaps.pm 2002/09/24 01:18:50 1.51
+++ loncom/interface/lonnavmaps.pm 2002/09/24 20:01:05 1.54
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Navigate Maps Handler
#
-# $Id: lonnavmaps.pm,v 1.51 2002/09/24 01:18:50 bowersj2 Exp $
+# $Id: lonnavmaps.pm,v 1.54 2002/09/24 20:01:05 bowersj2 Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -833,15 +833,17 @@ sub new_handle {
# Defines a status->color mapping, null string means don't color
my %colormap =
- ( $res->NETWORK_FAILURE => '',
- $res->CORRECT => '#BBFFBB',
- $res->EXCUSED => '#BBBBFF',
- $res->PAST_DUE => '#FFAA00',
- $res->ANSWER_OPEN => '#FF00AA',
- $res->OPEN_LATER => '',
- $res->TRIES_LEFT => '#FFFF00',
- $res->INCORRECT => '#FFAA00',
- $res->OPEN => '#FFFF88' );
+ ( $res->NETWORK_FAILURE => '',
+ $res->CORRECT => '#BBFFBB',
+ $res->EXCUSED => '#BBBBFF',
+ $res->PAST_DUE_ANSWER_LATER => '#FFAA00',
+ $res->PAST_DUE_NO_ANSWER => '#FFAA00',
+ $res->ANSWER_OPEN => '#FF00AA',
+ $res->OPEN_LATER => '',
+ $res->TRIES_LEFT => '#FFFF00',
+ $res->INCORRECT => '#FFAA00',
+ $res->OPEN => '#FFFF88',
+ $res->NOTHING_SET => '' );
if (!defined($navmap)) {
my $requrl = $r->uri;
@@ -863,6 +865,7 @@ sub new_handle {
my $mapIterator = $navmap->getIterator(undef, undef, \%filterHash);
my $curRes = $mapIterator->next();
+ undef $res; # so we don't accidentally use it later
my $indentLevel = -1;
my $indentString = " ";
@@ -870,19 +873,15 @@ sub new_handle {
while ($curRes != $mapIterator->END_NAV_MAP) {
if ($curRes == $mapIterator->BEGIN_MAP() ||
- $curRes == $mapIterator->BEGIN_BRANCH()) {
+ $curRes == $mapIterator->BEGIN_BRANCH()) {
$indentLevel++;
}
if ($curRes == $mapIterator->END_MAP() ||
- $curRes == $mapIterator->END_BRANCH()) {
+ $curRes == $mapIterator->END_BRANCH()) {
$indentLevel--;
}
-
- if ($curRes == $mapIterator->BEGIN_BRANCH()) {
- $r->print("Begin branch
");
- }
- if ($curRes == $mapIterator->END_BRANCH()) {
- $r->print("End branch
");
+ if ($curRes == $mapIterator->BEGIN_BRANCH()) {
+ $isNewBranch = 1;
}
if (ref($curRes) && $curRes->src()) {
@@ -911,6 +910,14 @@ sub new_handle {
'"';
my $title = $curRes->title();
my $partLabel = "";
+ my $newBranchText = "";
+
+ # If this is a new branch, label it so
+ # (temporary, this should be an icon w/ alt text)
+ if ($isNewBranch) {
+ $newBranchText = "NB -> ";
+ $isNewBranch = 0;
+ }
# links to open and close the folders
my $linkopen = "";
@@ -927,7 +934,7 @@ sub new_handle {
my $nowOpen = !defined($filterHash{$mapId});
$icon = $nowOpen ?
"folder_opened.gif" : "folder_closed.gif";
- $linkopen = "print($indentString);
}
- $r->print(" ${linkopen}
print(" ${newBranchText}${linkopen}
${linkclose}\n");
if ($curRes->is_problem() && $part != "0") { $partLabel = " (Part $part)"; }
@@ -962,7 +969,7 @@ sub new_handle {
if ($curRes->kind() eq "res" and
$curRes->is_problem() ) {
- $r->print (" Due: " . localtime($curRes->duedate()));
+ $r->print (getDescription($curRes, $part));
}
}
}
@@ -1020,6 +1027,45 @@ sub getLinkForResource {
return $res->src();
}
+# Convenience function: This seperates the logic of how to create
+# the problem text strings ("Due: DATE", "Open: DATE", "Not yet assigned",
+# etc.) into a seperate function. It takes a resource object as the
+# first parameter, and the part number of the resource as the second.
+# It's basically a big switch statement on the status of the resource.
+
+sub getDescription {
+ my $res = shift;
+ my $part = shift;
+ my $status = $res->getDateStatus();
+
+ if ($status == $res->NETWORK_FAILURE) { return ""; }
+ if ($status == $res->NOTHING_SET) {
+ return "Not currently assigned.";
+ }
+ if ($status == $res->OPEN_LATER) {
+ return "Opens: " . timeToHumanString($res->opendate($part));
+ }
+ if ($status == $res->OPEN) {
+ return "Due: $status " . timeToHumanString($res->duedate($part));
+ }
+ if ($status == $res->PAST_DUE_ANSWER_LATER) {
+ return "Answer: " . timeToHumanString($res->answerdate($part));
+ }
+ if ($status == $res->PAST_DUE_NO_ANSWER) {
+ return "Was Due: " . timeToHumanString($res->duedate($part));
+ }
+ if ($status == $res->ANSWER_OPEN) {
+ return "Answer available";
+ }
+}
+
+# I want to change this into something more human-friendly. For
+# now, this is a simple call to localtime. The final function
+# probably belongs in loncommon.
+sub timeToHumanString {
+ return localtime(shift);
+}
+
1;
package Apache::lonnavmaps::navmap;
@@ -1460,7 +1506,7 @@ sub next {
if (scalar(@{$self->{BRANCH_STACK}}) == 0) {
if ($self->{BRANCH_DEPTH} > 0) {
$self->{FORCE_NEXT} = $self->END_MAP();
- $self->{BRANCH_DEPTH}--;
+ $self->{BRANCH_DEPTH}--;
return $self->END_BRANCH();
} else {
return $self->END_MAP();
@@ -1479,13 +1525,32 @@ sub next {
# to start a new one. (We know because we already checked to see
# if the stack was empty.)
if ( scalar (@{$self->{BRANCH_STACK}}) < $self->{BRANCH_STACK_SIZE}) {
- $self->{BRANCH_STACK_SIZE} = scalar(@{$self->{BRANCH_STACK}});
- $self->{BRANCH_DEPTH}++;
- return $self->BEGIN_BRANCH();
+ $self->{BRANCH_STACK_SIZE} = scalar(@{$self->{BRANCH_STACK}});
+ $self->{BRANCH_DEPTH}++;
+ return $self->BEGIN_BRANCH();
}
+ # Remember the size for comparision next time.
+ $self->{BRANCH_STACK_SIZE} = scalar(@{$self->{BRANCH_STACK}});
+
+ # If the next resource we mean to return is going to need
+ # a lower branch level, terminate branches until we get
+ # there.
+
# Get the next resource in the branch
$self->{HERE} = pop @{$self->{BRANCH_STACK}};
+
+ # Are we at the right depth? If not, close a branch and return
+ # the current resource onto the branch stack
+ if (defined($self->{HERE}->{DATA}->{ITERATOR_DEPTH})
+ && $self->{HERE}->{DATA}->{ITERATOR_DEPTH} <
+ $self->{BRANCH_DEPTH} ) {
+ $self->{BRANCH_DEPTH}--;
+ # return it so we can pick it up eventually
+ push @{$self->{BRANCH_STACK}}, $self->{HERE};
+ return $self->END_BRANCH();
+ }
+
# We always return it after this point and never before
# (proof: look at just the return statements), so we
# remember that we've seen this.
@@ -1493,23 +1558,23 @@ sub next {
# Are we at the utter end? If so, return the END_NAV_MAP marker.
if ($self->{HERE} == $self->{NAV_MAP}->finishResource() ) {
- $self->{FORCE_NEXT} = $self->END_NAV_MAP;
+ $self->{FORCE_NEXT} = $self->END_NAV_MAP;
return $self->{HERE};
}
- # Remember the size for comparision next time.
- $self->{BRANCH_STACK_SIZE} = scalar(@{$self->{BRANCH_STACK}});
-
# Get the next possible resources
my $nextUnfiltered = $self->{HERE}->getNext();
my $next = [];
# filter the next possibilities to remove things we've
- # already seen
+ # already seen. Also, remember what branch depth they should
+ # be displayed at, since there's no other reliable way to tell.
foreach (@$nextUnfiltered) {
- if (!defined($self->{ALREADY_SEEN}->{$_->{ID}})) {
- push @$next, $_;
- }
+ if (!defined($self->{ALREADY_SEEN}->{$_->{ID}})) {
+ push @$next, $_;
+ $_->{DATA}->{ITERATOR_DEPTH} =
+ $self->{BRANCH_DEPTH} + 1;
+ }
}
# Handle branch cases:
@@ -1518,24 +1583,24 @@ sub next {
# 2+ things next: have some branches
my $nextCount = scalar(@$next);
if ($nextCount == 0) {
- # Return this and on the next run, close the branch up if we're
- # in a branch
- if ($self->{BRANCH_DEPTH} > 0 ) {
- $self->{FORCE_NEXT} = $self->END_BRANCH();
- $self->{BRANCH_DEPTH}--;
- }
+ # Return this and on the next run, close the branch up if we're
+ # in a branch
+ if ($self->{BRANCH_DEPTH} > 0 ) {
+ $self->{FORCE_NEXT} = $self->END_BRANCH();
+ $self->{BRANCH_DEPTH}--;
+ }
return $self->{HERE};
}
while (@$next) {
# copy the next possibilities over to the branch stack
# in the right order
- push @{$self->{BRANCH_STACK}}, shift @$next;
+ push @{$self->{BRANCH_STACK}}, shift @$next;
}
if ($nextCount >= 2) {
- $self->{FORCE_NEXT} = $self->BEGIN_BRANCH();
- $self->{BRANCH_DEPTH}++;
+ $self->{FORCE_NEXT} = $self->BEGIN_BRANCH();
+ $self->{BRANCH_DEPTH}++;
return $self->{HERE};
}
@@ -1552,8 +1617,8 @@ sub next {
$self->{RECURSIVE_ITERATOR} =
Apache::lonnavmaps::iterator->new ($self->{NAV_MAP}, $firstResource,
$finishResource, $self->{FILTER}, $self->{ALREADY_SEEN});
- # prime the new iterator with the first resource
- #push @{$self->{RECURSIVE_ITERATOR}->{BRANCH_STACK}}, $firstResource;
+ # prime the new iterator with the first resource
+ #push @{$self->{RECURSIVE_ITERATOR}->{BRANCH_STACK}}, $firstResource;
}
return $self->{HERE};
@@ -1962,7 +2027,9 @@ B
=item * B: Open and not yet due.
-=item * B: The due date has passed, but the answer date has not yet arrived.
+=item * B: The due date has passed, but the answer date has not yet arrived.
+
+=item * B: The due date has passed and there is no answer opening date set.
=item * B: The answer date is here.
@@ -1973,28 +2040,45 @@ B
=cut
# Apparently the compiler optimizes these into constants automatically
-sub OPEN_LATER { return 0; }
-sub OPEN { return 1; }
-sub PAST_DUE { return 2; }
-sub ANSWER_OPEN { return 3; }
-sub NETWORK_FAILURE { return 100; }
+sub OPEN_LATER { return 0; }
+sub OPEN { return 1; }
+sub PAST_DUE_NO_ANSWER { return 2; }
+sub PAST_DUE_ANSWER_LATER { return 3; }
+sub ANSWER_OPEN { return 4; }
+sub NOTHING_SET { return 5; }
+sub NETWORK_FAILURE { return 100; }
+
+# getDateStatus gets the date status for a given problem part.
+# Because answer date, due date, and open date are fully independent
+# (i.e., it is perfectly possible to *only* have an answer date),
+# we have to completely cover the 3x3 maxtrix of (answer, due, open) x
+# (past, future, none given). This function handles this with a decision
+# tree. Read the comments to follow the decision tree.
sub getDateStatus {
my $self = shift;
my $part = shift;
$part = "0" if (!defined($part));
+
+ # Always return network failure if there was one.
return $self->NETWORK_FAILURE if ($self->{NAV_MAP}->{NETWORK_FAILURE});
my $now = time();
- my $o = $now - $self->opendate($part);
- my $d = $now - $self->duedate($part);
- my $a = $now - $self->answerdate($part);
-
- if ($o < 0) {return $self->OPEN_LATER};
- if ($d < 0) {return $self->OPEN};
- if ($a < 0) {return $self->PAST_DUE};
- return $self->ANSWER_OPEN;
+ my $open = $self->opendate($part);
+ my $due = $self->duedate($part);
+ my $answer = $self->answerdate($part);
+
+ if (!$open && !$due && !$answer) {
+ # no data on the problem at all
+ # should this be the same as "open later"? think multipart.
+ return $self->NOTHING_SET;
+ }
+ if (!$open || $now < $open) {return $self->OPEN_LATER};
+ if (!$due || $now < $due) {return $self->OPEN};
+ if ($answer && $now < $answer) {return $self->PAST_DUE_ANSWER_LATER};
+ if ($answer) { return $self->ANSWER_OPEN; };
+ return PAST_DUE_NO_ANSWER;
}
=pod
@@ -2029,12 +2113,12 @@ B
=cut
-sub NOT_ATTEMPTED { return 0; }
-sub INCORRECT { return 1; }
-sub INCORRECT_BY_OVERRIDE { return 2; }
-sub CORRECT { return 3; }
-sub CORRECT_BY_OVERRIDE { return 4; }
-sub EXCUSED { return 5; }
+sub NOT_ATTEMPTED { return 10; }
+sub INCORRECT { return 11; }
+sub INCORRECT_BY_OVERRIDE { return 12; }
+sub CORRECT { return 13; }
+sub CORRECT_BY_OVERRIDE { return 14; }
+sub EXCUSED { return 15; }
sub getCompletionStatus {
my $self = shift;
@@ -2066,11 +2150,15 @@ Along with directly returning the date o
=item * NETWORK_FAILURE: The network has failed and the information is not available.
+=item * NOTHING_SET: No dates have been set for this problem (part) at all. (Because only certain parts of a multi-part problem may be assigned, this can not be collapsed into "open later", as we don't know a given part will EVER be opened.)
+
=item * CORRECT: For any reason at all, the part is considered correct.
=item * EXCUSED: For any reason at all, the problem is excused.
-=item * PAST_DUE: The problem is past due, and not considered correct.
+=item * PAST_DUE_NO_ANSWER: The problem is past due, not considered correct, and no answer date is set.
+
+=item * PAST_DUE_ANSWER_LATER: The problem is past due, not considered correct, and an answer date in the future is set.
=item * ANSWER_OPEN: The problem is past due, not correct, and the answer is now available.
@@ -2097,56 +2185,55 @@ sub status {
# What we have is a two-dimensional matrix with 4 entries on one
# dimension and 5 entries on the other, which we want to colorize,
- # plus network failure.
+ # plus network failure and "no date data at all".
- # Don't colorize on network failure.
- if ($completionStatus == NETWORK_FAILURE()) { return $self->NETWORK_FAILURE(); }
+ if ($completionStatus == NETWORK_FAILURE) { return NETWORK_FAILURE; }
# There are a few whole rows we can dispose of:
- # If the problem is CORRECT, color it green no matter what
- if ($completionStatus == CORRECT() ||
- $completionStatus == CORRECT_BY_OVERRIDE() ) {
- return $self->CORRECT(); # Return a nice green.
+ if ($completionStatus == CORRECT ||
+ $completionStatus == CORRECT_BY_OVERRIDE ) {
+ return CORRECT();
+ }
+
+ # If it's EXCUSED, then return that no matter what
+ if ($completionStatus == EXCUSED) {
+ return EXCUSED;
}
- # If it's EXCUSED, then return something no matter what
- if ($completionStatus == EXCUSED()) {
- return $self->EXCUSED(); # return a nice blue
+ if ($dateStatus == NOTHING_SET) {
+ return NOTHING_SET;
}
# Now we're down to a 3 (incorrect, incorrect_override, not_attempted)
# by 4 matrix (date status).
- # If it's Past Due and we didn't bail earlier because it's correct,
- # color it orange. (Red is sort inappropriate; too drastic a color
- # for something the student can't fix.
- if ($dateStatus == PAST_DUE()) {
- return $self->PAST_DUE(); # return orange
+ if ($dateStatus == PAST_DUE_ANSWER_LATER ||
+ $dateStatus == PAST_DUE_NO_ANSWER) {
+ return $dateStatus;
}
- if ($dateStatus == ANSWER_OPEN()) {
- return $self->ANSWER_OPEN();
+ if ($dateStatus == ANSWER_OPEN) {
+ return ANSWER_OPEN;
}
# Now: (incorrect, incorrect_override, not_attempted) x
# (open_later), (open)
- # If it's open later, then don't colorize
- if ($dateStatus == OPEN_LATER()) {
- return $self->OPEN_LATER();
+ if ($dateStatus == OPEN_LATER) {
+ return OPEN_LATER;
}
# If it's WRONG...
- if ($completionStatus == INCORRECT() || $completionStatus == INCORRECT_BY_OVERRIDE()) {
+ if ($completionStatus == INCORRECT || $completionStatus == INCORRECT_BY_OVERRIDE) {
# and there are TRIES LEFT:
if ($self->tries() < $self->maxtries()) {
- return $self->TRIES_LEFT(); # return red: The student can fix this
+ return TRIES_LEFT;
}
- return $self->INCORRECT(); # otherwise, return orange; student can't fix this
+ return INCORRECT; # otherwise, return orange; student can't fix this
}
# Otherwise, it's untried and open
- return $self->OPEN(); # Light yellow
+ return OPEN;
}
=pod