--- loncom/interface/lonnavmaps.pm 2003/04/24 18:18:38 1.181 +++ loncom/interface/lonnavmaps.pm 2003/05/14 18:01:16 1.187 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Navigate Maps Handler # -# $Id: lonnavmaps.pm,v 1.181 2003/04/24 18:18:38 bowersj2 Exp $ +# $Id: lonnavmaps.pm,v 1.187 2003/05/14 18:01:16 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', @@ -199,6 +200,52 @@ sub real_handler { } } + # 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 my $render = render({ 'cols' => [0,1,2,3], 'url' => '/adm/navmaps', @@ -271,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."; } @@ -317,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 @@ -1472,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(/\=/,$_); @@ -2753,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()); } @@ -2927,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); @@ -3040,6 +3097,15 @@ number of parts in the problem, not incl B may return an array with fewer parts in it then countParts might lead you to believe. +=item * B($part): + +Returns the response type of the part, without the word "response" on the +end. Example return values: 'string', 'essay', 'numeric', etc. + +=item * B($part): + +Retreives the response ID for the given part, which may be an empty string. + =back =cut @@ -3069,7 +3135,24 @@ sub countParts { return scalar(@{$parts}) + $delta; } -# Private function: Extracts the parts information and saves it +sub responseType { + my $self = shift; + my $part = shift; + + $self->extractParts(); + return $self->{RESPONSE_TYPE}->{$part}; +} + +sub responseId { + my $self = shift; + my $part = shift; + + $self->extractParts(); + return $self->{RESPONSE_IDS}->{$part}; +} + +# Private function: Extracts the parts information, both part names and +# part types, and saves it. sub extractParts { my $self = shift; @@ -3083,18 +3166,20 @@ sub extractParts { # Retrieve part count, if this is a problem if ($self->is_problem()) { my $metadata = &Apache::lonnet::metadata($self->src(), 'packages'); + print $metadata; 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 (defined($parts{$part})) { - # Apache::lonnet::logthis("$part multiply defined in metadata for " . $self->symb()); - # } + # 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. @@ -3107,6 +3192,40 @@ sub extractParts { my @sortedParts = sort keys %parts; $self->{PARTS} = \@sortedParts; + + my %responseIdHash; + my %responseTypeHash; + + # Now, the unfortunate thing about this is that parts, part name, and + # response if 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 + # to construct ambiguous situations. + foreach (split /,/, $metadata) { + if ($_ =~ /^([a-zA-Z]+)response_(.*)/) { + my $responseType = $1; + my $partStuff = $2; + my $partIdSoFar = ''; + my @partChunks = split /_/, $partStuff; + my $i = 0; + + for ($i = 0; $i < scalar(@partChunks); $i++) { + if ($partIdSoFar) { $partIdSoFar .= '_'; } + $partIdSoFar .= $partChunks[$i]; + if ($parts{$partIdSoFar}) { + my @otherChunks = @partChunks[$i+1..$#partChunks]; + my $responseId = join('_', @otherChunks); + $responseIdHash{$partIdSoFar} = $responseId; + $responseTypeHash{$partIdSoFar} = $responseType; + last; + } + } + } + } + + $self->{RESPONSE_IDS} = \%responseIdHash; + $self->{RESPONSE_TYPES} = \%responseTypeHash; } return; @@ -3310,7 +3429,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(); @@ -3328,7 +3447,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 @@ -3387,11 +3509,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; @@ -3406,10 +3533,12 @@ sub status { if ($completionStatus == NETWORK_FAILURE) { return NETWORK_FAILURE; } + my $suppressFeedback = lc($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) { @@ -3450,7 +3579,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 500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.