version 1.175, 2003/04/11 20:13:25
|
version 1.186, 2003/05/12 19:22:39
|
Line 71 my %statusIconMap =
|
Line 71 my %statusIconMap =
|
$resObj->TRIES_LEFT => 'navmap.open.gif', |
$resObj->TRIES_LEFT => 'navmap.open.gif', |
$resObj->INCORRECT => 'navmap.wrong.gif', |
$resObj->INCORRECT => 'navmap.wrong.gif', |
$resObj->OPEN => 'navmap.open.gif', |
$resObj->OPEN => 'navmap.open.gif', |
$resObj->ATTEMPTED => 'navmap.open.gif' ); |
$resObj->ATTEMPTED => 'navmap.open.gif', |
|
$resObj->ANSWER_SUBMITTED => '' ); |
|
|
my %iconAltTags = |
my %iconAltTags = |
( 'navmap.correct.gif' => 'Correct', |
( 'navmap.correct.gif' => 'Correct', |
Line 169 sub real_handler {
|
Line 170 sub real_handler {
|
return OK; |
return OK; |
} |
} |
|
|
# See if there's only one map in the top-level... if so, |
# See if there's only one map in the top-level, if we don't |
# automatically display it |
# already have a filter... if so, automatically display it |
my $iterator = $navmap->getIterator(undef, undef, undef, 0); |
if ($ENV{QUERY_STRING} !~ /filter/) { |
my $depth = 1; |
my $iterator = $navmap->getIterator(undef, undef, undef, 0); |
$iterator->next(); |
my $depth = 1; |
my $curRes = $iterator->next(); |
$iterator->next(); |
my $sequenceCount = 0; |
my $curRes = $iterator->next(); |
my $sequenceId; |
my $sequenceCount = 0; |
while ($depth > 0) { |
my $sequenceId; |
if ($curRes == $iterator->BEGIN_MAP()) { $depth++; } |
while ($depth > 0) { |
if ($curRes == $iterator->END_MAP()) { $depth--; } |
if ($curRes == $iterator->BEGIN_MAP()) { $depth++; } |
|
if ($curRes == $iterator->END_MAP()) { $depth--; } |
if (ref($curRes) && $curRes->is_sequence()) { |
|
$sequenceCount++; |
if (ref($curRes) && $curRes->is_sequence()) { |
$sequenceId = $curRes->map_pc(); |
$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) { |
# Check to see if the student is jumping to next open, do-able problem |
# The automatic iterator creation in the render call |
if ($ENV{QUERY_STRING} eq 'jumpToFirstHomework') { |
# will pick this up. |
# Find the next homework problem that they can do. |
$ENV{'form.filter'} = "$sequenceId"; |
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("<font size='+2'>All homework assignments have been completed.</font><br /><br />"); |
|
} |
|
} else { |
|
$r->print("<a href='navmaps?jumpToFirstHomework'>" . |
|
"Go To My First Homework Problem</a><br />"); |
} |
} |
|
|
# renderer call |
# renderer call |
Line 267 sub getDescription {
|
Line 318 sub getDescription {
|
my $part = shift; |
my $part = shift; |
my $status = $res->status($part); |
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) { |
if ($status == $res->NOTHING_SET) { |
return "Not currently assigned."; |
return "Not currently assigned."; |
} |
} |
Line 313 sub getDescription {
|
Line 366 sub getDescription {
|
return "No due date $triesString"; |
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 |
# Convenience function, so others can use it: Is the problem due in less then |
Line 343 sub lastTry {
|
Line 399 sub lastTry {
|
} |
} |
|
|
# This puts a human-readable name on the ENV variable. |
# 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 { |
sub advancedUser { |
return $ENV{'user.adv'}; |
return $ENV{'request.role.adv'}; |
} |
} |
|
|
|
|
Line 1473 sub init {
|
Line 1524 sub init {
|
unless ((time-$courserdatas{$cid.'.last_cache'})<240) { |
unless ((time-$courserdatas{$cid.'.last_cache'})<240) { |
my $reply=&Apache::lonnet::reply('dump:'.$cdom.':'.$cnum. |
my $reply=&Apache::lonnet::reply('dump:'.$cdom.':'.$cnum. |
':resourcedata',$chome); |
':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}=$reply; |
$courserdatas{$cid.'.last_cache'}=time; |
$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})) { |
foreach (split(/\&/,$courserdatas{$cid})) { |
my ($name,$value)=split(/\=/,$_); |
my ($name,$value)=split(/\=/,$_); |
Line 2690 sub to { my $self=shift; return $self->n
|
Line 2739 sub to { my $self=shift; return $self->n
|
sub compTitle { |
sub compTitle { |
my $self = shift; |
my $self = shift; |
my $title = $self->title(); |
my $title = $self->title(); |
|
$title=~s/\&colon\;/\:/gs; |
if (!$title) { |
if (!$title) { |
$title = $self->src(); |
$title = $self->src(); |
$title = substr($title, rindex($title, '/') + 1); |
$title = substr($title, rindex($title, '/') + 1); |
Line 2753 sub is_sequence {
|
Line 2803 sub is_sequence {
|
sub parmval { |
sub parmval { |
my $self = shift; |
my $self = shift; |
my $what = 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()); |
return $self->{NAV_MAP}->parmval($part.'.'.$what, $self->symb()); |
} |
} |
|
|
Line 2927 sub opendate {
|
Line 2980 sub opendate {
|
} |
} |
return $self->parmval("opendate"); |
return $self->parmval("opendate"); |
} |
} |
|
sub problemstatus { |
|
(my $self, my $part) = @_; |
|
return $self->parmval("problemstatus", $part); |
|
} |
sub sig { |
sub sig { |
(my $self, my $part) = @_; |
(my $self, my $part) = @_; |
return $self->parmval("sig", $part); |
return $self->parmval("sig", $part); |
Line 3069 sub countParts {
|
Line 3126 sub countParts {
|
return scalar(@{$parts}) + $delta; |
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 { |
sub extractParts { |
my $self = shift; |
my $self = shift; |
|
|
Line 3078 sub extractParts {
|
Line 3144 sub extractParts {
|
|
|
$self->{PARTS} = []; |
$self->{PARTS} = []; |
|
|
|
my %parts; |
|
|
# Retrieve part count, if this is a problem |
# Retrieve part count, if this is a problem |
if ($self->is_problem()) { |
if ($self->is_problem()) { |
my $metadata = &Apache::lonnet::metadata($self->src(), 'packages'); |
my $metadata = &Apache::lonnet::metadata($self->src(), 'packages'); |
if (!$metadata) { |
if (!$metadata) { |
$self->{RESOURCE_ERROR} = 1; |
$self->{RESOURCE_ERROR} = 1; |
$self->{PARTS} = []; |
$self->{PARTS} = []; |
|
$self->{PART_TYPE} = {}; |
return; |
return; |
} |
} |
foreach (split(/\,/,$metadata)) { |
foreach (split(/\,/,$metadata)) { |
if ($_ =~ /^part_(.*)$/) { |
if ($_ =~ /^part_(.*)$/) { |
my $part = $1; |
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. |
# 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; |
$self->{PARTS} = \@sortedParts; |
} |
} |
|
|
Line 3302 sub queryRestoreHash {
|
Line 3377 sub queryRestoreHash {
|
my $self = shift; |
my $self = shift; |
my $hashentry = shift; |
my $hashentry = shift; |
my $part = 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}); |
return $self->NETWORK_FAILURE if ($self->{NAV_MAP}->{NETWORK_FAILURE}); |
|
|
$self->getReturnHash(); |
$self->getReturnHash(); |
Line 3320 combine the two status tidbits into one
|
Line 3395 combine the two status tidbits into one
|
represent the status of the resource as a whole. The precise logic is |
represent the status of the resource as a whole. The precise logic is |
documented in the comments of the status method. The following results |
documented in the comments of the status method. The following results |
may be returned, all available as methods on the resource object |
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 |
=over 4 |
|
|
Line 3379 The item is open and not yet tried.
|
Line 3457 The item is open and not yet tried.
|
|
|
The problem has been attempted. |
The problem has been attempted. |
|
|
|
=item * B<ANSWER_SUBMITTED>: |
|
|
|
An answer has been submitted, but the student should not see it. |
|
|
=back |
=back |
|
|
=cut |
=cut |
|
|
sub TRIES_LEFT { return 10; } |
sub TRIES_LEFT { return 20; } |
|
sub ANSWER_SUBMITTED { return 21; } |
|
|
sub status { |
sub status { |
my $self = shift; |
my $self = shift; |
Line 3398 sub status {
|
Line 3481 sub status {
|
|
|
if ($completionStatus == NETWORK_FAILURE) { return NETWORK_FAILURE; } |
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: |
# There are a few whole rows we can dispose of: |
if ($completionStatus == CORRECT || |
if ($completionStatus == CORRECT || |
$completionStatus == CORRECT_BY_OVERRIDE ) { |
$completionStatus == CORRECT_BY_OVERRIDE ) { |
return CORRECT; |
return $suppressFeedback? ANSWER_SUBMITTED : CORRECT; |
} |
} |
|
|
if ($completionStatus == ATTEMPTED) { |
if ($completionStatus == ATTEMPTED) { |
Line 3442 sub status {
|
Line 3527 sub status {
|
if ($self->tries($part) < $self->maxtries($part) || !$self->maxtries($part)) { |
if ($self->tries($part) < $self->maxtries($part) || !$self->maxtries($part)) { |
return TRIES_LEFT; |
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 |
# Otherwise, it's untried and open |