--- loncom/interface/lonnavmaps.pm 2003/09/17 21:29:44 1.230 +++ loncom/interface/lonnavmaps.pm 2004/03/22 22:17:40 1.255 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Navigate Maps Handler # -# $Id: lonnavmaps.pm,v 1.230 2003/09/17 21:29:44 www Exp $ +# $Id: lonnavmaps.pm,v 1.255 2004/03/22 22:17:40 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -25,20 +25,7 @@ # # http://www.lon-capa.org/ # -# (Page Handler -# -# (TeX Content Handler -# -# 05/29/00,05/30 Gerd Kortemeyer) -# 08/30,08/31,09/06,09/14,09/15,09/16,09/19,09/20,09/21,09/23, -# 10/02,10/10,10/14,10/16,10/18,10/19,10/31,11/6,11/14,11/16 Gerd Kortemeyer) -# -# 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 -# YEAR=2003 -# Jeremy Bowers ... lots of days +### package Apache::lonnavmaps; @@ -107,9 +94,9 @@ sub real_handler { # Handle header-only request if ($r->header_only) { if ($ENV{'browser.mathml'}) { - $r->content_type('text/xml'); + &Apache::loncommon::content_type($r,'text/xml'); } else { - $r->content_type('text/html'); + &Apache::loncommon::content_type($r,'text/html'); } $r->send_http_header; return OK; @@ -117,9 +104,9 @@ sub real_handler { # Send header, don't cache this page if ($ENV{'browser.mathml'}) { - $r->content_type('text/xml'); + &Apache::loncommon::content_type($r,'text/xml'); } else { - $r->content_type('text/html'); + &Apache::loncommon::content_type($r,'text/html'); } &Apache::loncommon::no_cache($r); $r->send_http_header; @@ -127,7 +114,6 @@ sub real_handler { # Create the nav map my $navmap = Apache::lonnavmaps::navmap->new(); - if (!defined($navmap)) { my $requrl = $r->uri; $ENV{'user.error.msg'} = "$requrl:bre:0:0:Course not initialized"; @@ -304,8 +290,16 @@ sub getLinkForResource { # Check to see if there are any pages in the stack foreach $res (@$stack) { - if (defined($res) && $res->is_page()) { - return $res->src(); + if (defined($res)) { + if ($res->is_page()) { + return $res->src(); + } + # in case folder was skipped over as "only sequence" + my ($map,$id,$src)=&Apache::lonnet::decode_symb($res->symb()); + if ($map=~/\.page$/) { + return &Apache::lonnet::clutter($map).'#'. + &Apache::lonnet::escape(&Apache::lonnet::declutter($src)); + } } } @@ -320,9 +314,9 @@ sub getLinkForResource { return $res->src(); } -# Convenience function: This seperates the logic of how to create +# Convenience function: This separates 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 +# etc.) into a separate 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. @@ -372,7 +366,7 @@ sub getDescription { $triesString = "$triesString"; } } - if ($res->duedate()) { + if ($res->duedate($part)) { return &mt("Due")." " . timeToHumanString($res->duedate($part)) . " $triesString"; } else { @@ -387,15 +381,15 @@ sub getDescription { # Convenience function, so others can use it: Is the problem due in less then # 24 hours, and still can be done? -sub dueInLessThen24Hours { +sub dueInLessThan24Hours { my $res = shift; my $part = shift; my $status = $res->status($part); return ($status == $res->OPEN() || $status == $res->TRIES_LEFT()) && - $res->duedate() && $res->duedate() < time()+(24*60*60) && - $res->duedate() > time(); + $res->duedate($part) && $res->duedate($part) < time()+(24*60*60) && + $res->duedate($part) > time(); } # Convenience function, so others can use it: Is there only one try remaining for the @@ -407,8 +401,8 @@ sub lastTry { my $tries = $res->tries($part); my $maxtries = $res->maxtries($part); return $tries && $maxtries && $maxtries > 1 && - $maxtries - $tries == 1 && $res->duedate() && - $res->duedate() > time(); + $maxtries - $tries == 1 && $res->duedate($part) && + $res->duedate($part) > time(); } # This puts a human-readable name on the ENV variable. @@ -432,8 +426,8 @@ sub timeToHumanString { if (!$time) { return &mt('never'); } - unless (&Apache::loncommon::current_language()=~/^en/) { - return localtime($time); + unless (&Apache::lonlocal::current_language()=~/^en/) { + return &Apache::lonlocal::locallocaltime($time); } my $now = time(); @@ -502,7 +496,7 @@ sub timeToHumanString { # HH:MM if ( $delta < $day * 5 ) { my $timeStr = strftime("%A, %b %e at %I:%M %P", localtime($time)); - $timeStr =~ s/12:00 am/midnight/; + $timeStr =~ s/12:00 am/00:00/; $timeStr =~ s/12:00 pm/noon/; return ($inPast ? "last " : "next ") . $timeStr; @@ -512,14 +506,14 @@ sub timeToHumanString { if ( $time[5] == $now[5]) { # Return on Month Day, HH:MM meridian my $timeStr = strftime("on %A, %b %e at %I:%M %P", localtime($time)); - $timeStr =~ s/12:00 am/midnight/; + $timeStr =~ s/12:00 am/00:00/; $timeStr =~ s/12:00 pm/noon/; return $timeStr; } # Not this year, so show the year my $timeStr = strftime("on %A, %b %e %G at %I:%M %P", localtime($time)); - $timeStr =~ s/12:00 am/midnight/; + $timeStr =~ s/12:00 am/00:00/; $timeStr =~ s/12:00 pm/noon/; return $timeStr; } @@ -896,12 +890,8 @@ sub render_resource { $icon = $params->{'indentString'}; } } else { - my $curfext= (split (/\./,$resource->src))[-1]; - my $embstyle = &Apache::loncommon::fileembstyle($curfext); - # The unless conditional that follows is a bit of overkill - if (!(!defined($embstyle) || $embstyle eq 'unk' || $embstyle eq 'hdn')) { - $icon = ""; - } + $icon = ""; } # Display the correct map icon to open or shut map @@ -971,7 +961,11 @@ sub render_resource { if ($resource->is_problem() && $part ne '0' && !$params->{'condensed'}) { - $partLabel = " (Part $part)"; + my $displaypart=&Apache::lonnet::EXT('resource.'.$part.'.display', + $resource->symb()); + unless ($displaypart) { $displaypart=$part; } + $partLabel = " (Part: $displaypart)"; + $link.='#'.&Apache::lonnet::escape($part); $title = ""; } @@ -1017,8 +1011,11 @@ sub render_communication_status { if ($resource->getErrors()) { my $errors = $resource->getErrors(); + my $errorcount = 0; foreach (split(/,/, $errors)) { + last if ($errorcount>=10); # Only output 10 bombs maximum if ($_) { + $errorcount++; $errorHTML .= ' ' . 'is_problem()) { $color = $colormap{$resource->status}; - if (dueInLessThen24Hours($resource, $part) || + if (dueInLessThan24Hours($resource, $part) || lastTry($resource, $part)) { $color = $hurryUpColor; } @@ -1132,7 +1129,7 @@ sub render_parts_summary_status { # If there is a single part, just show the simple status if ($resource->singlepart()) { - my $status = $resource->simpleStatus('0'); + my $status = $resource->simpleStatus(${$resource->parts}[0]); return $td . "" . $statusStrings{$status} . "" . $endtd; } @@ -1546,11 +1543,15 @@ sub render { my $filter = $it->{FILTER}; my $stack = $it->getStack(); my $src = getLinkForResource($stack); - + my $anchor=''; + if ($src=~s/(\#.*$)//) { + $anchor=$1; + } my $srcHasQuestion = $src =~ /\?/; $args->{"resourceLink"} = $src. ($srcHasQuestion?'&':'?') . - 'symb=' . &Apache::lonnet::escape($curRes->symb()); + 'symb=' . &Apache::lonnet::escape($curRes->symb()). + $anchor; # Now, display each column. foreach my $col (@$cols) { @@ -1587,7 +1588,6 @@ sub render { # If we have the connection, make sure the user is still connected my $c = $r->connection; if ($c->aborted()) { - Apache::lonnet::logthis("navmaps aborted"); # Who cares what we do, nobody will see it anyhow. return ''; } @@ -1602,7 +1602,12 @@ sub render { # it's quite likely this might fix other browsers, too, and # certainly won't hurt anything. if ($displayedJumpMarker) { - $result .= "\n"; + $result .= " +"; } $result .= ""; @@ -2104,7 +2109,11 @@ sub parmval_real { # ----------------------------------------------------- fourth , check default - my $default=&Apache::lonnet::metadata($fn,$rwhat.'.default'); + my $meta_rwhat=$rwhat; + $meta_rwhat=~s/\./_/g; + my $default=&Apache::lonnet::metadata($fn,$meta_rwhat); + if (defined($default)) { return $default} + $default=&Apache::lonnet::metadata($fn,'parameter_'.$meta_rwhat); if (defined($default)) { return $default} # --------------------------------------------------- fifth , cascade up parts @@ -2347,6 +2356,8 @@ 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. +=back + =head2 Normal Usage Normal usage of the iterator object is to do the following: @@ -2367,8 +2378,6 @@ the depth of the iterator to see when it 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: @@ -2505,7 +2514,7 @@ sub new { } # Check: Was this only one resource, a map? - if ($resourceCount == 1 && $resource->is_map() && !$self->{FORCE_TOP}) { + if ($resourceCount == 1 && $resource->is_sequence() && !$self->{FORCE_TOP}) { my $firstResource = $resource->map_start(); my $finishResource = $resource->map_finish(); return @@ -2736,7 +2745,7 @@ package Apache::lonnavmaps::DFSiterator; # useful for pre-processing of some kind, and is in fact used by the main # iterator that way, but that's about it. # One could imagine merging this into the init routine of the main iterator, -# but this might as well be left seperate, since it is possible some other +# but this might as well be left separate, since it is possible some other # use might be found for it. - Jeremy # Unlike the main iterator, this DOES return all resources, even blank ones. @@ -3169,7 +3178,7 @@ sub is_page { sub is_problem { my $self=shift; my $src = $self->src(); - return ($src =~ /problem$/); + return ($src =~ /\.(problem|exam|quiz|assess|survey|form|library)$/); } sub is_sequence { my $self=shift; @@ -3369,7 +3378,7 @@ sub opendate { } sub problemstatus { (my $self, my $part) = @_; - return $self->parmval("problemstatus", $part); + return lc $self->parmval("problemstatus", $part); } sub sig { (my $self, my $part) = @_; @@ -3547,7 +3556,11 @@ sub responseType { my $part = shift; $self->extractParts(); - return $self->{RESPONSE_TYPES}->{$part}; + if (defined($self->{RESPONSE_TYPES}->{$part})) { + return @{$self->{RESPONSE_TYPES}->{$part}}; + } else { + return undef; + } } sub responseIds { @@ -3555,7 +3568,11 @@ sub responseIds { my $part = shift; $self->extractParts(); - return $self->{RESPONSE_IDS}->{$part}; + if (defined($self->{RESPONSE_IDS}->{$part})) { + return @{$self->{RESPONSE_IDS}->{$part}}; + } else { + return undef; + } } # Private function: Extracts the parts information, both part names and @@ -3572,32 +3589,44 @@ sub extractParts { # Retrieve part count, if this is a problem if ($self->is_problem()) { + my $partorder = &Apache::lonnet::metadata($self->src(), 'partorder'); my $metadata = &Apache::lonnet::metadata($self->src(), 'packages'); - if (!$metadata) { - $self->{RESOURCE_ERROR} = 1; - $self->{PARTS} = []; - $self->{PART_TYPE} = {}; - return; - } - foreach (split(/\,/,$metadata)) { - if ($_ =~ /^part_(.*)$/) { - my $part = $1; - # This floods the logs if it blows up - if (defined($parts{$part})) { - Apache::lonnet::logthis("$part multiply defined in metadata for " . $self->symb()); - } - # check to see if part is turned off. - - if (!Apache::loncommon::check_if_partid_hidden($part, $self->symb())) { - $parts{$part} = 1; - } - } + if ($partorder) { + my @parts; + for my $part (split (/,/,$partorder)) { + if (!Apache::loncommon::check_if_partid_hidden($part, $self->symb())) { + push @parts, $part; + $parts{$part} = 1; + } + } + $self->{PARTS} = \@parts; + } else { + if (!$metadata) { + $self->{RESOURCE_ERROR} = 1; + $self->{PARTS} = []; + $self->{PART_TYPE} = {}; + return; + } + foreach (split(/\,/,$metadata)) { + if ($_ =~ /^part_(.*)$/) { + my $part = $1; + # This floods the logs if it blows up + if (defined($parts{$part})) { + &Apache::lonnet::logthis("$part multiply defined in metadata for " . $self->symb()); + } + + # check to see if part is turned off. + + if (!Apache::loncommon::check_if_partid_hidden($part, $self->symb())) { + $parts{$part} = 1; + } + } + } + my @sortedParts = sort keys %parts; + $self->{PARTS} = \@sortedParts; } - - my @sortedParts = sort keys %parts; - $self->{PARTS} = \@sortedParts; my %responseIdHash; my %responseTypeHash; @@ -3609,7 +3638,7 @@ sub extractParts { } # Now, the unfortunate thing about this is that parts, part name, and - # response if are delimited by underscores, but both the part + # response id are delimited by underscores, but both the part # name and response id can themselves have underscores in them. # So we have to use our knowlege of part names to figure out # where the part names begin and end, and even then, it is possible @@ -3621,7 +3650,6 @@ sub extractParts { my $partIdSoFar = ''; my @partChunks = split /_/, $partStuff; my $i = 0; - for ($i = 0; $i < scalar(@partChunks); $i++) { if ($partIdSoFar) { $partIdSoFar .= '_'; } $partIdSoFar .= $partChunks[$i]; @@ -3629,13 +3657,11 @@ sub extractParts { my @otherChunks = @partChunks[$i+1..$#partChunks]; my $responseId = join('_', @otherChunks); push @{$responseIdHash{$partIdSoFar}}, $responseId; - $responseTypeHash{$partIdSoFar} = $responseType; - last; + push @{$responseTypeHash{$partIdSoFar}}, $responseType; } } } } - $self->{RESPONSE_IDS} = \%responseIdHash; $self->{RESPONSE_TYPES} = \%responseTypeHash; } @@ -3827,7 +3853,7 @@ sub getCompletionStatus { my $status = $self->queryRestoreHash('solved', shift); - # Left as seperate if statements in case we ever do more with this + # Left as separate if statements in case we ever do more with this if ($status eq 'correct_by_student') {return $self->CORRECT;} if ($status eq 'correct_by_override') {return $self->CORRECT_BY_OVERRIDE; } if ($status eq 'incorrect_attempted') {return $self->INCORRECT; } @@ -3948,7 +3974,12 @@ sub status { #if ($self->{RESOURCE_ERROR}) { return NETWORK_FAILURE; } if ($completionStatus == NETWORK_FAILURE) { return NETWORK_FAILURE; } - my $suppressFeedback = lc($self->parmval("problemstatus", $part)) eq 'no'; + my $suppressFeedback = $self->problemstatus($part) eq 'no'; + # If there's an answer date and we're past it, don't + # suppress the feedback; student should know + if ($self->answerdate($part) && $self->answerdate($part) < time()) { + $suppressFeedback = 0; + } # There are a few whole rows we can dispose of: if ($completionStatus == CORRECT ||