--- loncom/interface/lonnavmaps.pm 2002/10/01 07:20:31 1.58 +++ loncom/interface/lonnavmaps.pm 2002/10/01 19:15:22 1.59 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Navigate Maps Handler # -# $Id: lonnavmaps.pm,v 1.58 2002/10/01 07:20:31 albertel Exp $ +# $Id: lonnavmaps.pm,v 1.59 2002/10/01 19:15:22 bowersj2 Exp $ # # Copyright Michigan State University Board of Trustees # @@ -848,16 +848,38 @@ sub new_handle { # Defines a status->color mapping, null string means don't color my %colormap = ( $res->NETWORK_FAILURE => '', - $res->CORRECT => '#BBFFBB', + $res->CORRECT => '', $res->EXCUSED => '#BBBBFF', - $res->PAST_DUE_ANSWER_LATER => '#FFAA00', - $res->PAST_DUE_NO_ANSWER => '#FFAA00', - $res->ANSWER_OPEN => '#FF00AA', + $res->PAST_DUE_ANSWER_LATER => '', + $res->PAST_DUE_NO_ANSWER => '', + $res->ANSWER_OPEN => '#CCFFCC', $res->OPEN_LATER => '', - $res->TRIES_LEFT => '#FFFF00', - $res->INCORRECT => '#FFAA00', - $res->OPEN => '#FFFF88', + $res->TRIES_LEFT => '', + $res->INCORRECT => '', + $res->OPEN => '', $res->NOTHING_SET => '' ); + # And a special case in the nav map; what to do when the assignment + # is not yet done and due in less then 24 hours + my $hurryUpColor = "#FFCCCC"; + + my %statusIconMap = + ( $res->NETWORK_FAILURE => '', + $res->NOTHING_SET => '', + $res->CORRECT => 'navmap.correct.gif', + $res->EXCUSED => 'navmap.correct.gif', + $res->PAST_DUE_NO_ANSWER => 'navmap.wrong.gif', + $res->PAST_DUE_ANSWER_LATER => 'navmap.wrong.gif', + $res->ANSWER_OPEN => 'navmap.wrong.gif', + $res->OPEN_LATER => '', + $res->TRIES_LEFT => 'navmap.open.gif', + $res->INCORRECT => 'navmap.wrong.gif', + $res->OPEN => 'navmap.open.gif' ); + + my %condenseStatuses = + ( $res->NETWORK_FAILURE => 1, + $res->NOTHING_SET => 1, + $res->CORRECT => 1, + $res->OPEN => 1 ); my %filterHash; # Figure out what we're not displaying @@ -868,16 +890,18 @@ sub new_handle { } # Begin the HTML table - # two columns: the title/icon on the left, the info on the right + # four cols: resource + indent, chat+feedback, icon, text string $r->print('' ."\n"); 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 = "        "; + my $indentString = ""; my $isNewBranch = 0; + my $now = time(); + my $in24Hours = $now + 24 * 60 * 60; while ($curRes != $mapIterator->END_NAV_MAP) { if ($curRes == $mapIterator->BEGIN_MAP() || @@ -894,17 +918,60 @@ sub new_handle { if (ref($curRes) && $curRes->src()) { + # Step one: Decide which parts to show my @parts; if ($curRes->is_problem()) { + # Is it multipart? @parts = @{$curRes->parts()}; - if (scalar(@parts) > 1) { shift(@parts); } + + my $multipart = scalar(@parts) > 1; + + if ($multipart) { + # If it's multipart, see if part 0 is "open" + # if it is, display all parts, if it isn't, + # just display first + if (!$curRes->opendate("0")) { + @parts = ("0"); # just display the zero-th part + } else { + # Otherwise, only display part 0 if we want to + # attach feedback or email information to it + if (!$curRes->hasDiscussion() && !$curRes->getFeedback()) { + shift @parts; + } else { + # If there's discussion or feedback, that counts + # as a difference, so display the parts. + + # Now, we decide whether to condense the + # parts due to similarity + my $status = $curRes->status($parts[0]); + my $allSame = 1; + for (my $i = 1; $i < scalar(@parts); $i++) { + if ($curRes->status($parts[$i]) != $status){ + $allSame = 0; + } + } + + # $allSame is true if all the statuses were + # the same. Now, if they are all the same and + # match one of the statuses to condense, then + # only display part 0. + if ($allSame && defined($condenseStatuses{$status})){ + @parts = ("0"); + } + } + } + } + } else { @parts[0] = "0"; # this is to get past foreach loop below # you can consider a non-problem resource as a resource # with only one part without loss } + # Step two: Show the parts foreach my $part (@parts) { + + my $deltalevel = 0; # for inserting the branch icon # For each thing we're displaying... @@ -923,8 +990,9 @@ sub new_handle { # If this is a new branch, label it so # (temporary, this should be an icon w/ alt text) if ($isNewBranch) { - $newBranchText = "NB -> "; + $newBranchText = ""; $isNewBranch = 0; + $deltalevel = 1; } # links to open and close the folders @@ -960,10 +1028,11 @@ sub new_handle { } } + # FIRST ROW: The resource indentation, branch icon, and name $r->print(" ". - ""); + + my $discussionHTML = ""; my $feedbackHTML = ""; + + # SECOND ROW: Is there text or feedback? + if ($curRes->hasDiscussion()) { + $discussionHTML = $linkopen . + '' . + $linkclose; + } + + if ($curRes->getFeedback()) { + my $feedback = $curRes->getFeedback(); + foreach (split(/\,/, $feedback)) { + if ($_) { + $feedbackHTML .= ' ' + . ''; + } + } + } + + $r->print(""); + + # THIRD ROW: Problem status icon + if ($curRes->is_problem()) { + my $icon = $statusIconMap{$curRes->status()}; + if ($icon) { + $r->print("\n"); + } else { + $r->print("\n"); + } + } else { # not problem, no icon + $r->print("\n"); + } + + # FOURTH ROW: Text description + $r->print("\n"); } } $curRes = $mapIterator->next(); @@ -986,6 +1094,8 @@ sub new_handle { $r->print("
\n"); # print indentation - for (my $i = 0; $i < $indentLevel; $i++) { + for (my $i = 0; $i < $indentLevel - $deltalevel; $i++) { $r->print($indentString); } @@ -972,13 +1041,52 @@ sub new_handle { if ($curRes->is_problem() && $part != "0") { $partLabel = " (Part $part)"; } - $r->print(" $title$partLabel\n"); + $r->print(" $title$partLabel$discussionHTML$feedbackHTML$linkopen$linkclose\n"); if ($curRes->kind() eq "res" and $curRes->is_problem() ) { $r->print (getDescription($curRes, $part)); } + + $r->print("
"); + $navmap->untieHashes(); + return OK; } @@ -1054,10 +1164,14 @@ sub getDescription { return "Opens: " . timeToHumanString($res->opendate($part)); } if ($status == $res->OPEN) { - return "Due: $status " . timeToHumanString($res->duedate($part)); + if ($res->duedate()) { + return "Due: $status " . timeToHumanString($res->duedate($part)); + } else { + return "Open, no due date"; + } } if ($status == $res->PAST_DUE_ANSWER_LATER) { - return "Answer: " . timeToHumanString($res->answerdate($part)); + return "Answer opens: " . timeToHumanString($res->answerdate($part)); } if ($status == $res->PAST_DUE_NO_ANSWER) { return "Was Due: " . timeToHumanString($res->duedate($part)); @@ -1065,6 +1179,20 @@ sub getDescription { if ($status == $res->ANSWER_OPEN) { return "Answer available"; } + if ($status == $res->EXCUSED) { + return "Excused by instructor"; + } + if ($status == $res->TRIES_LEFT) { + my $tries = $res->tries(); + my $maxtries = $res->maxtries(); + my $triesString = "($tries of $maxtries tries used)"; + if ($res->duedate()) { + return "Due: $status " . timeToHumanString($res->duedate($part)) . + " $triesString"; + } else { + return "No due date $triesString"; + } + } } # I want to change this into something more human-friendly. For @@ -1288,6 +1416,29 @@ sub DESTROY { $self->untieHashes(); } +# Private function: Does the given resource (as a symb string) have +# current discussion? Returns 0 if chat/mail data not extracted. +sub hasDiscussion { + my $self = shift; + my $symb = shift; + if (!defined($self->{DISCUSSION_TIME})) { return 0; } + + return $self->{DISCUSSION_TIME}->{$symb} > + $self->{LAST_CHECK}; +} + +# Private function: Does the given resource (as a symb string) have +# current feedback? Returns the string in the feedback hash, which +# will be false if it does not exist. +sub getFeedback { + my $self = shift; + my $symb = shift; + + if (!defined($self->{FEEDBACK})) { return ""; } + + return $self->{FEEDBACK}->{$symb}; +} + =pod =item * B(id): Based on the ID of the resource (1.1, 3.2, etc.), get a resource object for that resource. This method, or other methods that use it (as in the resource object) is the only proper way to obtain a resource object. @@ -2020,6 +2171,30 @@ Misc. functions for the resource. =over 4 +=item * B: Returns a false value if there has been discussion since the user last logged in, true if there has. Always returns false if the discussion data was not extracted when the nav map was constructed. + +=item * B: Gets the feedback for the resource and returns the raw feedback string for the resource, or the null string if there is no feedback or the email data was not extracted when the nav map was constructed. Usually used like this: + + for (split(/\,/, $res->getFeedback())) { + my $link = &Apache::lonnet::escape($_); + ... + +and use the link as appropriate. + +=cut + +sub hasDiscussion { + my $self = shift; + return $self->{NAV_MAP}->hasDiscussion($self->symb()); +} + +sub getFeedback { + my $self = shift; + return $self->{NAV_MAP}->getFeedback($self->symb()); +} + +=pod + =item * B(): Returns a list reference containing sorted strings corresponding to each part of the problem. To count the number of parts, use the list in a scalar context, and subtract one if greater then two. (One part problems have a part 0. Multi-parts have a part 0, plus a part for each part. You may or may not wish to filter out part 0.) =item * B(): Returns the number of parts of the problem a student can answer. Thus, for single part problems, returns 1. For multipart, it returns the number of parts in the problem, not including psuedo-part 0. @@ -2087,7 +2262,7 @@ Idiomatic usage of these two methods wou =over 4 -=item * B($part): ($part defaults to 0). A convenience function that returns a symbolic constant telling you about the date status of the part, with the following possible results: +=item * B($part): ($part defaults to 0). A convenience function that returns a symbolic constant telling you about the date status of the part. You should still check to see if there is a due date at all, if you care about that. The possible return values are: =back @@ -2099,6 +2274,7 @@ 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 and there is no answer opening date set. @@ -2146,10 +2322,10 @@ sub getDateStatus { # 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; }; + 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; } @@ -2222,7 +2398,7 @@ 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 * 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. For single part, this is the same as "OPEN_LATER".) =item * CORRECT: For any reason at all, the part is considered correct. @@ -2280,7 +2456,7 @@ sub status { # by 4 matrix (date status). if ($dateStatus == PAST_DUE_ANSWER_LATER || - $dateStatus == PAST_DUE_NO_ANSWER) { + $dateStatus == PAST_DUE_NO_ANSWER ) { return $dateStatus; }