--- loncom/interface/lonnavmaps.pm 2003/04/18 13:51:46 1.176 +++ loncom/interface/lonnavmaps.pm 2003/05/12 18:22:38 1.185 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Navigate Maps Handler # -# $Id: lonnavmaps.pm,v 1.176 2003/04/18 13:51:46 www Exp $ +# $Id: lonnavmaps.pm,v 1.185 2003/05/12 18:22:38 bowersj2 Exp $ # # Copyright Michigan State University Board of Trustees # @@ -71,7 +71,8 @@ my %statusIconMap = $resObj->TRIES_LEFT => 'navmap.open.gif', $resObj->INCORRECT => 'navmap.wrong.gif', $resObj->OPEN => 'navmap.open.gif', - $resObj->ATTEMPTED => 'navmap.open.gif' ); + $resObj->ATTEMPTED => 'navmap.open.gif', + $resObj->ANSWER_SUBMITTED => '' ); my %iconAltTags = ( 'navmap.correct.gif' => 'Correct', @@ -169,30 +170,80 @@ sub real_handler { return OK; } - # See if there's only one map in the top-level... if so, - # automatically display it - my $iterator = $navmap->getIterator(undef, undef, undef, 0); - my $depth = 1; - $iterator->next(); - my $curRes = $iterator->next(); - my $sequenceCount = 0; - my $sequenceId; - while ($depth > 0) { - if ($curRes == $iterator->BEGIN_MAP()) { $depth++; } - if ($curRes == $iterator->END_MAP()) { $depth--; } - - if (ref($curRes) && $curRes->is_sequence()) { - $sequenceCount++; - $sequenceId = $curRes->map_pc(); + # See if there's only one map in the top-level, if we don't + # already have a filter... if so, automatically display it + if ($ENV{QUERY_STRING} !~ /filter/) { + my $iterator = $navmap->getIterator(undef, undef, undef, 0); + my $depth = 1; + $iterator->next(); + my $curRes = $iterator->next(); + my $sequenceCount = 0; + my $sequenceId; + while ($depth > 0) { + if ($curRes == $iterator->BEGIN_MAP()) { $depth++; } + if ($curRes == $iterator->END_MAP()) { $depth--; } + + if (ref($curRes) && $curRes->is_sequence()) { + $sequenceCount++; + $sequenceId = $curRes->map_pc(); + } + + $curRes = $iterator->next(); + } + + if ($sequenceCount == 1) { + # The automatic iterator creation in the render call + # will pick this up. We know the condition because + # the defined($ENV{'form.filter'}) also ensures this + # is a fresh call. + $ENV{'form.filter'} = "$sequenceId"; } - - $curRes = $iterator->next(); } - if ($sequenceCount == 1) { - # The automatic iterator creation in the render call - # will pick this up. - $ENV{'form.filter'} = "$sequenceId"; + # Check to see if the student is jumping to next open, do-able problem + if ($ENV{QUERY_STRING} eq 'jumpToFirstHomework') { + # Find the next homework problem that they can do. + my $iterator = $navmap->getIterator(undef, undef, undef, 1); + my $depth = 1; + $iterator->next(); + my $curRes = $iterator->next(); + my $foundDoableProblem = 0; + my $problemRes; + + while ($depth > 0 && !$foundDoableProblem) { + if ($curRes == $iterator->BEGIN_MAP()) { $depth++; } + if ($curRes == $iterator->END_MAP()) { $depth--; } + + if (ref($curRes) && $curRes->is_problem()) { + my $status = $curRes->status(); + if (($status == $curRes->OPEN || + $status == $curRes->TRIES_LEFT()) && + $curRes->getCompletionStatus() != $curRes->ATTEMPTED()) { + $problemRes = $curRes; + $foundDoableProblem = 1; + + # Pop open all previous maps + my $stack = $iterator->getStack(); + pop @$stack; # last resource in the stack is the problem + # itself, which we don't need in the map stack + my @mapPcs = map {$_->map_pc()} @$stack; + $ENV{'form.filter'} = join(',', @mapPcs); + + # Mark as both "here" and "jump" + $ENV{'form.postsymb'} = $curRes->symb(); + } + } + } continue { + $curRes = $iterator->next(); + } + + # If we found no problems, print a note to that effect. + if (!$foundDoableProblem) { + $r->print("All homework assignments have been completed.

"); + } + } else { + $r->print("" . + "Go To My First Homework Problem
"); } # renderer call @@ -267,7 +318,9 @@ sub getDescription { my $part = shift; my $status = $res->status($part); - if ($status == $res->NETWORK_FAILURE) { return ""; } + if ($status == $res->NETWORK_FAILURE) { + return "Having technical difficulties; please check status later"; + } if ($status == $res->NOTHING_SET) { return "Not currently assigned."; } @@ -313,6 +366,9 @@ sub getDescription { return "No due date $triesString"; } } + if ($status == $res->ANSWER_SUBMITTED) { + return 'Answer submitted'; + } } # Convenience function, so others can use it: Is the problem due in less then @@ -343,14 +399,9 @@ sub lastTry { } # This puts a human-readable name on the ENV variable. -# FIXME: This needs better logic: Who gets the advanced view of navmaps? -# As of 3-13-03, it's an open question. Guy doesn't want to check -# roles directly because it should be a check of capabilities for future -# role compatibity. There is no capability that matches this one for -# now, so this is done. (A hack for 1.0 might be to simply check roles -# anyhow.) + sub advancedUser { - return $ENV{'user.adv'}; + return $ENV{'request.role.adv'}; } @@ -1473,15 +1524,13 @@ sub init { unless ((time-$courserdatas{$cid.'.last_cache'})<240) { my $reply=&Apache::lonnet::reply('dump:'.$cdom.':'.$cnum. ':resourcedata',$chome); - if ($reply!~/^error\:/) { + # Check for network failure + if ( $reply =~ /no.such.host/i || $reply =~ /con_lost/i) { + $self->{NETWORK_FAILURE} = 1; + } elsif ($reply!~/^error\:/) { $courserdatas{$cid}=$reply; $courserdatas{$cid.'.last_cache'}=time; } - # check to see if network failed - elsif ( $reply=~/no.such.host/i || $reply=~/con.*lost/i ) - { - $self->{NETWORK_FAILURE} = 1; - } } foreach (split(/\&/,$courserdatas{$cid})) { my ($name,$value)=split(/\=/,$_); @@ -2754,7 +2803,10 @@ sub is_sequence { sub parmval { my $self = shift; my $what = shift; - my $part = shift || "0"; + my $part = shift; + if (!defined($part)) { + $part = '0'; + } return $self->{NAV_MAP}->parmval($part.'.'.$what, $self->symb()); } @@ -2928,6 +2980,10 @@ sub opendate { } return $self->parmval("opendate"); } +sub problemstatus { + (my $self, my $part) = @_; + return $self->parmval("problemstatus", $part); +} sub sig { (my $self, my $part) = @_; return $self->parmval("sig", $part); @@ -3070,7 +3126,16 @@ sub countParts { return scalar(@{$parts}) + $delta; } -# Private function: Extracts the parts information and saves it +sub partType { + my $self = shift; + my $part = shift; + + $self->extractParts(); + return $self->{PART_TYPE}->{$part}; +} + +# Private function: Extracts the parts information, both part names and +# part types, and saves it sub extractParts { my $self = shift; @@ -3079,26 +3144,35 @@ sub extractParts { $self->{PARTS} = []; + my %parts; + # Retrieve part count, if this is a problem if ($self->is_problem()) { 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())) { - push @{$self->{PARTS}}, $1; + + if (!Apache::loncommon::check_if_partid_hidden($part, $self->symb())) { + $parts{$part} = 1; } } } - my @sortedParts = sort @{$self->{PARTS}}; + my @sortedParts = sort keys %parts; $self->{PARTS} = \@sortedParts; } @@ -3303,7 +3377,7 @@ sub queryRestoreHash { my $self = shift; my $hashentry = shift; my $part = shift; - $part = "0" if (!defined($part)); + $part = "0" if (!defined($part) || $part eq ''); return $self->NETWORK_FAILURE if ($self->{NAV_MAP}->{NETWORK_FAILURE}); $self->getReturnHash(); @@ -3321,7 +3395,10 @@ combine the two status tidbits into one represent the status of the resource as a whole. The precise logic is documented in the comments of the status method. The following results may be returned, all available as methods on the resource object -($res->NETWORK_FAILURE): +($res->NETWORK_FAILURE): In addition to the return values that match +the date or completion status, this function can return "ANSWER_SUBMITTED" +if that problemstatus parameter value is set to No, suppressing the +incorrect/correct feedback. =over 4 @@ -3380,11 +3457,16 @@ The item is open and not yet tried. The problem has been attempted. +=item * B: + +An answer has been submitted, but the student should not see it. + =back =cut -sub TRIES_LEFT { return 10; } +sub TRIES_LEFT { return 20; } +sub ANSWER_SUBMITTED { return 21; } sub status { my $self = shift; @@ -3399,10 +3481,12 @@ sub status { if ($completionStatus == NETWORK_FAILURE) { return NETWORK_FAILURE; } + my $suppressFeedback = $self->parmval("problemstatus", $part) eq 'No'; + # There are a few whole rows we can dispose of: if ($completionStatus == CORRECT || $completionStatus == CORRECT_BY_OVERRIDE ) { - return CORRECT; + return $suppressFeedback? ANSWER_SUBMITTED : CORRECT; } if ($completionStatus == ATTEMPTED) { @@ -3443,7 +3527,7 @@ sub status { if ($self->tries($part) < $self->maxtries($part) || !$self->maxtries($part)) { return TRIES_LEFT; } - return INCORRECT; # otherwise, return orange; student can't fix this + return $suppressFeedback ? ANSWER_SUBMITTED : INCORRECT; # otherwise, return orange; student can't fix this } # Otherwise, it's untried and open