--- loncom/interface/lonnavmaps.pm 2003/04/25 18:54:36 1.183
+++ loncom/interface/lonnavmaps.pm 2003/05/14 18:33:28 1.188
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Navigate Maps Handler
#
-# $Id: lonnavmaps.pm,v 1.183 2003/04/25 18:54:36 bowersj2 Exp $
+# $Id: lonnavmaps.pm,v 1.188 2003/05/14 18:33:28 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',
@@ -319,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
@@ -2544,6 +2594,12 @@ You will probably never need to instanti
Apache::lonnavmaps::navmap, and use the "start" method to obtain the
starting resource.
+Resource objects respect the parameter_hiddenparts, which suppresses
+various parts according to the wishes of the map author. As of this
+writing, there is no way to override this parameter, and suppressed
+parts will never be returned, nor will their response types or ids be
+stored.
+
=head2 Public Members
resource objects have a hash called DATA ($resourceRef->{DATA}) that
@@ -2753,7 +2809,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 +2986,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 +3103,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 +3141,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;
@@ -3086,6 +3175,7 @@ sub extractParts {
if (!$metadata) {
$self->{RESOURCE_ERROR} = 1;
$self->{PARTS} = [];
+ $self->{PART_TYPE} = {};
return;
}
foreach (split(/\,/,$metadata)) {
@@ -3107,6 +3197,43 @@ 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);
+ if (!defined($responseIdHash{$partIdSoFar})) {
+ $responseIdHash{$partIdSoFar} = [];
+ }
+ push @{$responseIdHash{$partIdSoFar}}, $responseId;
+ $responseTypeHash{$partIdSoFar} = $responseType;
+ last;
+ }
+ }
+ }
+ }
+
+ $self->{RESPONSE_IDS} = \%responseIdHash;
+ $self->{RESPONSE_TYPES} = \%responseTypeHash;
}
return;
@@ -3310,7 +3437,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 +3455,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 +3517,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 +3541,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 +3587,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