version 1.220, 2003/08/07 14:29:43
|
version 1.251, 2004/03/08 17:31:37
|
Line 25
|
Line 25
|
# |
# |
# http://www.lon-capa.org/ |
# 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; |
package Apache::lonnavmaps; |
|
|
Line 46 use strict;
|
Line 33 use strict;
|
use Apache::Constants qw(:common :http); |
use Apache::Constants qw(:common :http); |
use Apache::loncommon(); |
use Apache::loncommon(); |
use Apache::lonmenu(); |
use Apache::lonmenu(); |
|
use Apache::lonlocal; |
use POSIX qw (floor strftime); |
use POSIX qw (floor strftime); |
use Data::Dumper; # for debugging, not always used |
use Data::Dumper; # for debugging, not always used |
|
|
Line 61 my $resObj = "Apache::lonnavmaps::resour
|
Line 49 my $resObj = "Apache::lonnavmaps::resour
|
# Keep these mappings in sync with lonquickgrades, which uses the colors |
# Keep these mappings in sync with lonquickgrades, which uses the colors |
# instead of the icons. |
# instead of the icons. |
my %statusIconMap = |
my %statusIconMap = |
( $resObj->NETWORK_FAILURE => '', |
( |
$resObj->NOTHING_SET => '', |
$resObj->CLOSED => '', |
$resObj->CORRECT => 'navmap.correct.gif', |
$resObj->OPEN => 'navmap.open.gif', |
$resObj->EXCUSED => 'navmap.correct.gif', |
$resObj->CORRECT => 'navmap.correct.gif', |
$resObj->PAST_DUE_NO_ANSWER => 'navmap.wrong.gif', |
$resObj->INCORRECT => 'navmap.wrong.gif', |
$resObj->PAST_DUE_ANSWER_LATER => 'navmap.wrong.gif', |
$resObj->ATTEMPTED => 'navmap.ellipsis.gif', |
$resObj->ANSWER_OPEN => 'navmap.wrong.gif', |
$resObj->ERROR => '' |
$resObj->OPEN_LATER => '', |
); |
$resObj->TRIES_LEFT => 'navmap.open.gif', |
|
$resObj->INCORRECT => 'navmap.wrong.gif', |
|
$resObj->OPEN => 'navmap.open.gif', |
|
$resObj->ATTEMPTED => 'navmap.ellipsis.gif', |
|
$resObj->ANSWER_SUBMITTED => 'navmap.ellipsis.gif' ); |
|
|
|
my %iconAltTags = |
my %iconAltTags = |
( 'navmap.correct.gif' => 'Correct', |
( 'navmap.correct.gif' => 'Correct', |
Line 111 sub real_handler {
|
Line 94 sub real_handler {
|
# Handle header-only request |
# Handle header-only request |
if ($r->header_only) { |
if ($r->header_only) { |
if ($ENV{'browser.mathml'}) { |
if ($ENV{'browser.mathml'}) { |
$r->content_type('text/xml'); |
&Apache::loncommon::content_type($r,'text/xml'); |
} else { |
} else { |
$r->content_type('text/html'); |
&Apache::loncommon::content_type($r,'text/html'); |
} |
} |
$r->send_http_header; |
$r->send_http_header; |
return OK; |
return OK; |
Line 121 sub real_handler {
|
Line 104 sub real_handler {
|
|
|
# Send header, don't cache this page |
# Send header, don't cache this page |
if ($ENV{'browser.mathml'}) { |
if ($ENV{'browser.mathml'}) { |
$r->content_type('text/xml'); |
&Apache::loncommon::content_type($r,'text/xml'); |
} else { |
} else { |
$r->content_type('text/html'); |
&Apache::loncommon::content_type($r,'text/html'); |
} |
} |
&Apache::loncommon::no_cache($r); |
&Apache::loncommon::no_cache($r); |
$r->send_http_header; |
$r->send_http_header; |
|
|
# Create the nav map |
# Create the nav map |
my $navmap = Apache::lonnavmaps::navmap->new(1, 1); |
my $navmap = Apache::lonnavmaps::navmap->new(); |
|
|
|
|
if (!defined($navmap)) { |
if (!defined($navmap)) { |
my $requrl = $r->uri; |
my $requrl = $r->uri; |
Line 139 sub real_handler {
|
Line 121 sub real_handler {
|
} |
} |
|
|
$r->print("<html><head>\n"); |
$r->print("<html><head>\n"); |
$r->print("<title>Navigate Course Contents</title>"); |
$r->print("<title>".&mt('Navigate Course Contents')."</title>"); |
# ------------------------------------------------------------ Get query string |
# ------------------------------------------------------------ Get query string |
&Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['register']); |
&Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['register']); |
|
|
Line 159 sub real_handler {
|
Line 141 sub real_handler {
|
|
|
$r->rflush(); |
$r->rflush(); |
|
|
# Now that we've displayed some stuff to the user, init the navmap |
|
$navmap->init(); |
|
|
|
$r->rflush(); |
|
|
|
# Check that it's defined |
# Check that it's defined |
if (!($navmap->courseMapDefined())) { |
if (!($navmap->courseMapDefined())) { |
$r->print('<font size="+2" color="red">Coursemap undefined.</font>' . |
$r->print('<font size="+2" color="red">Coursemap undefined.</font>' . |
Line 173 sub real_handler {
|
Line 150 sub real_handler {
|
|
|
# See if there's only one map in the top-level, if we don't |
# See if there's only one map in the top-level, if we don't |
# already have a filter... if so, automatically display it |
# already have a filter... if so, automatically display it |
|
# (older code; should use retrieveResources) |
if ($ENV{QUERY_STRING} !~ /filter/) { |
if ($ENV{QUERY_STRING} !~ /filter/) { |
my $iterator = $navmap->getIterator(undef, undef, undef, 0); |
my $iterator = $navmap->getIterator(undef, undef, undef, 0); |
my $depth = 1; |
my $curRes; |
$iterator->next(); |
|
my $curRes = $iterator->next(); |
|
my $sequenceCount = 0; |
my $sequenceCount = 0; |
my $sequenceId; |
my $sequenceId; |
while ($depth > 0) { |
while ($curRes = $iterator->next()) { |
if ($curRes == $iterator->BEGIN_MAP()) { $depth++; } |
|
if ($curRes == $iterator->END_MAP()) { $depth--; } |
|
|
|
if (ref($curRes) && $curRes->is_sequence()) { |
if (ref($curRes) && $curRes->is_sequence()) { |
$sequenceCount++; |
$sequenceCount++; |
$sequenceId = $curRes->map_pc(); |
$sequenceId = $curRes->map_pc(); |
} |
} |
|
|
$curRes = $iterator->next(); |
|
} |
} |
|
|
if ($sequenceCount == 1) { |
if ($sequenceCount == 1) { |
Line 207 sub real_handler {
|
Line 178 sub real_handler {
|
$jumpToFirstHomework = 1; |
$jumpToFirstHomework = 1; |
# Find the next homework problem that they can do. |
# Find the next homework problem that they can do. |
my $iterator = $navmap->getIterator(undef, undef, undef, 1); |
my $iterator = $navmap->getIterator(undef, undef, undef, 1); |
my $depth = 1; |
my $curRes; |
$iterator->next(); |
|
my $curRes = $iterator->next(); |
|
my $foundDoableProblem = 0; |
my $foundDoableProblem = 0; |
my $problemRes; |
my $problemRes; |
|
|
while ($depth > 0 && !$foundDoableProblem) { |
while (($curRes = $iterator->next()) && !$foundDoableProblem) { |
if ($curRes == $iterator->BEGIN_MAP()) { $depth++; } |
|
if ($curRes == $iterator->END_MAP()) { $depth--; } |
|
|
|
if (ref($curRes) && $curRes->is_problem()) { |
if (ref($curRes) && $curRes->is_problem()) { |
my $status = $curRes->status(); |
my $status = $curRes->status(); |
if ($curRes->completable()) { |
if ($curRes->completable()) { |
Line 234 sub real_handler {
|
Line 200 sub real_handler {
|
$ENV{'form.postsymb'} = $curRes->symb(); |
$ENV{'form.postsymb'} = $curRes->symb(); |
} |
} |
} |
} |
} continue { |
|
$curRes = $iterator->next(); |
|
} |
} |
|
|
# If we found no problems, print a note to that effect. |
# If we found no problems, print a note to that effect. |
Line 244 sub real_handler {
|
Line 208 sub real_handler {
|
} |
} |
} else { |
} else { |
$r->print("<a href='navmaps?jumpToFirstHomework'>" . |
$r->print("<a href='navmaps?jumpToFirstHomework'>" . |
"Go To My First Homework Problem</a> "); |
&mt("Go To My First Homework Problem")."</a> "); |
} |
} |
|
|
my $suppressEmptySequences = 0; |
my $suppressEmptySequences = 0; |
Line 259 sub real_handler {
|
Line 223 sub real_handler {
|
$filterFunc = sub { my $res = shift; |
$filterFunc = sub { my $res = shift; |
return $res->completable() || $res->is_map(); |
return $res->completable() || $res->is_map(); |
}; |
}; |
$r->print("<p><font size='+2'>Uncompleted Homework</font></p>"); |
$r->print("<p><font size='+2'>".&mt("Uncompleted Homework")."</font></p>"); |
$ENV{'form.filter'} = ''; |
$ENV{'form.filter'} = ''; |
$ENV{'form.condition'} = 1; |
$ENV{'form.condition'} = 1; |
$resource_no_folder_link = 1; |
$resource_no_folder_link = 1; |
} else { |
} else { |
$r->print("<a href='navmaps?showOnlyHomework'>" . |
$r->print("<a href='navmaps?showOnlyHomework'>" . |
"Show Only Uncompleted Homework</a> "); |
&mt("Show Only Uncompleted Homework")."</a> "); |
} |
} |
|
|
# renderer call |
# renderer call |
Line 284 sub real_handler {
|
Line 248 sub real_handler {
|
# user knows there was no error. |
# user knows there was no error. |
if ($renderArgs->{'counter'} == 0) { |
if ($renderArgs->{'counter'} == 0) { |
if ($showOnlyHomework) { |
if ($showOnlyHomework) { |
$r->print("<p><font size='+1'>All homework is currently completed.</font></p>"); |
$r->print("<p><font size='+1'>".&mt("All homework is currently completed").".</font></p>"); |
} else { # both jumpToFirstHomework and normal use the same: course must be empty |
} else { # both jumpToFirstHomework and normal use the same: course must be empty |
$r->print("<p><font size='+1'>This course is empty.</font></p>"); |
$r->print("<p><font size='+1'>This course is empty.</font></p>"); |
} |
} |
Line 326 sub getLinkForResource {
|
Line 290 sub getLinkForResource {
|
|
|
# Check to see if there are any pages in the stack |
# Check to see if there are any pages in the stack |
foreach $res (@$stack) { |
foreach $res (@$stack) { |
if (defined($res) && $res->is_page()) { |
if (defined($res)) { |
return $res->src(); |
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)); |
|
} |
} |
} |
} |
} |
|
|
Line 342 sub getLinkForResource {
|
Line 314 sub getLinkForResource {
|
return $res->src(); |
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", |
# 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. |
# 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. |
# It's basically a big switch statement on the status of the resource. |
|
|
Line 354 sub getDescription {
|
Line 326 sub getDescription {
|
my $status = $res->status($part); |
my $status = $res->status($part); |
|
|
if ($status == $res->NETWORK_FAILURE) { |
if ($status == $res->NETWORK_FAILURE) { |
return "Having technical difficulties; please check status later"; |
return &mt("Having technical difficulties; please check status later"); |
} |
} |
if ($status == $res->NOTHING_SET) { |
if ($status == $res->NOTHING_SET) { |
return "Not currently assigned."; |
return &mt("Not currently assigned."); |
} |
} |
if ($status == $res->OPEN_LATER) { |
if ($status == $res->OPEN_LATER) { |
return "Open " . timeToHumanString($res->opendate($part)); |
return "Open " . timeToHumanString($res->opendate($part)); |
} |
} |
if ($status == $res->OPEN) { |
if ($status == $res->OPEN) { |
if ($res->duedate($part)) { |
if ($res->duedate($part)) { |
return "Due " . timeToHumanString($res->duedate($part)); |
return &mt("Due")." " .timeToHumanString($res->duedate($part)); |
} else { |
} else { |
return "Open, no due date"; |
return &mt("Open, no due date"); |
} |
} |
} |
} |
if ($status == $res->PAST_DUE_ANSWER_LATER) { |
if ($status == $res->PAST_DUE_ANSWER_LATER) { |
return "Answer open " . timeToHumanString($res->answerdate($part)); |
return &mt("Answer open")." " . timeToHumanString($res->answerdate($part)); |
} |
} |
if ($status == $res->PAST_DUE_NO_ANSWER) { |
if ($status == $res->PAST_DUE_NO_ANSWER) { |
return "Was due " . timeToHumanString($res->duedate($part)); |
return &mt("Was due")." " . timeToHumanString($res->duedate($part)); |
} |
} |
if ($status == $res->ANSWER_OPEN) { |
if ($status == $res->ANSWER_OPEN) { |
return "Answer available"; |
return &mt("Answer available"); |
} |
} |
if ($status == $res->EXCUSED) { |
if ($status == $res->EXCUSED) { |
return "Excused by instructor"; |
return &mt("Excused by instructor"); |
} |
} |
if ($status == $res->ATTEMPTED) { |
if ($status == $res->ATTEMPTED) { |
return "Answer submitted, not yet graded."; |
return &mt("Answer submitted, not yet graded"); |
} |
} |
if ($status == $res->TRIES_LEFT) { |
if ($status == $res->TRIES_LEFT) { |
my $tries = $res->tries($part); |
my $tries = $res->tries($part); |
Line 394 sub getDescription {
|
Line 366 sub getDescription {
|
$triesString = "<b>$triesString</b>"; |
$triesString = "<b>$triesString</b>"; |
} |
} |
} |
} |
if ($res->duedate()) { |
if ($res->duedate($part)) { |
return "Due " . timeToHumanString($res->duedate($part)) . |
return &mt("Due")." " . timeToHumanString($res->duedate($part)) . |
" $triesString"; |
" $triesString"; |
} else { |
} else { |
return "No due date $triesString"; |
return &mt("No due date")." $triesString"; |
} |
} |
} |
} |
if ($status == $res->ANSWER_SUBMITTED) { |
if ($status == $res->ANSWER_SUBMITTED) { |
return 'Answer submitted'; |
return &mt('Answer submitted'); |
} |
} |
} |
} |
|
|
Line 416 sub dueInLessThen24Hours {
|
Line 388 sub dueInLessThen24Hours {
|
|
|
return ($status == $res->OPEN() || |
return ($status == $res->OPEN() || |
$status == $res->TRIES_LEFT()) && |
$status == $res->TRIES_LEFT()) && |
$res->duedate() && $res->duedate() < time()+(24*60*60) && |
$res->duedate($part) && $res->duedate($part) < time()+(24*60*60) && |
$res->duedate() > time(); |
$res->duedate($part) > time(); |
} |
} |
|
|
# Convenience function, so others can use it: Is there only one try remaining for the |
# Convenience function, so others can use it: Is there only one try remaining for the |
Line 429 sub lastTry {
|
Line 401 sub lastTry {
|
my $tries = $res->tries($part); |
my $tries = $res->tries($part); |
my $maxtries = $res->maxtries($part); |
my $maxtries = $res->maxtries($part); |
return $tries && $maxtries && $maxtries > 1 && |
return $tries && $maxtries && $maxtries > 1 && |
$maxtries - $tries == 1 && $res->duedate() && |
$maxtries - $tries == 1 && $res->duedate($part) && |
$res->duedate() > time(); |
$res->duedate($part) > time(); |
} |
} |
|
|
# This puts a human-readable name on the ENV variable. |
# This puts a human-readable name on the ENV variable. |
Line 452 sub timeToHumanString {
|
Line 424 sub timeToHumanString {
|
my ($time) = @_; |
my ($time) = @_; |
# zero, '0' and blank are bad times |
# zero, '0' and blank are bad times |
if (!$time) { |
if (!$time) { |
return 'never'; |
return &mt('never'); |
} |
} |
|
unless (&Apache::lonlocal::current_language()=~/^en/) { |
|
return &Apache::lonlocal::locallocaltime($time); |
|
} |
my $now = time(); |
my $now = time(); |
|
|
my @time = localtime($time); |
my @time = localtime($time); |
Line 522 sub timeToHumanString {
|
Line 496 sub timeToHumanString {
|
# HH:MM |
# HH:MM |
if ( $delta < $day * 5 ) { |
if ( $delta < $day * 5 ) { |
my $timeStr = strftime("%A, %b %e at %I:%M %P", localtime($time)); |
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/; |
$timeStr =~ s/12:00 pm/noon/; |
return ($inPast ? "last " : "next ") . |
return ($inPast ? "last " : "next ") . |
$timeStr; |
$timeStr; |
Line 532 sub timeToHumanString {
|
Line 506 sub timeToHumanString {
|
if ( $time[5] == $now[5]) { |
if ( $time[5] == $now[5]) { |
# Return on Month Day, HH:MM meridian |
# Return on Month Day, HH:MM meridian |
my $timeStr = strftime("on %A, %b %e at %I:%M %P", localtime($time)); |
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/; |
$timeStr =~ s/12:00 pm/noon/; |
return $timeStr; |
return $timeStr; |
} |
} |
|
|
# Not this year, so show the year |
# Not this year, so show the year |
my $timeStr = strftime("on %A, %b %e %G at %I:%M %P", localtime($time)); |
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/; |
$timeStr =~ s/12:00 pm/noon/; |
return $timeStr; |
return $timeStr; |
} |
} |
Line 678 can't close or open folders when this is
|
Line 652 can't close or open folders when this is
|
|
|
=back |
=back |
|
|
=item B<Apache::lonnavmaps::communication_status>: |
=item * B<Apache::lonnavmaps::communication_status>: |
|
|
Whether there is discussion on the resource, email for the user, or |
Whether there is discussion on the resource, email for the user, or |
(lumped in here) perl errors in the execution of the problem. This is |
(lumped in here) perl errors in the execution of the problem. This is |
the second column in the main nav map. |
the second column in the main nav map. |
|
|
=item B<Apache::lonnavmaps::quick_status>: |
=item * B<Apache::lonnavmaps::quick_status>: |
|
|
An icon for the status of a problem, with five possible states: |
An icon for the status of a problem, with five possible states: |
Correct, incorrect, open, awaiting grading (for a problem where the |
Correct, incorrect, open, awaiting grading (for a problem where the |
Line 692 computer's grade is suppressed, or the c
|
Line 666 computer's grade is suppressed, or the c
|
essay problem), or none (not open yet, not a problem). The |
essay problem), or none (not open yet, not a problem). The |
third column of the standard navmap. |
third column of the standard navmap. |
|
|
=item B<Apache::lonnavmaps::long_status>: |
=item * B<Apache::lonnavmaps::long_status>: |
|
|
A text readout of the details of the current status of the problem, |
A text readout of the details of the current status of the problem, |
such as "Due in 22 hours". The fourth column of the standard navmap. |
such as "Due in 22 hours". The fourth column of the standard navmap. |
|
|
|
=item * B<Apache::lonnavmaps::part_status_summary>: |
|
|
|
A text readout summarizing the status of the problem. If it is a |
|
single part problem, will display "Correct", "Incorrect", |
|
"Not yet open", "Open", "Attempted", or "Error". If there are |
|
multiple parts, this will output a string that in HTML will show a |
|
status of how many parts are in each status, in color coding, trying |
|
to match the colors of the icons within reason. |
|
|
|
Note this only makes sense if you are I<not> showing parts. If |
|
C<showParts> is true (see below), this column will not output |
|
anything. |
|
|
=back |
=back |
|
|
If you add any others please be sure to document them here. |
If you add any others please be sure to document them here. |
Line 864 sub resource { return 0; }
|
Line 851 sub resource { return 0; }
|
sub communication_status { return 1; } |
sub communication_status { return 1; } |
sub quick_status { return 2; } |
sub quick_status { return 2; } |
sub long_status { return 3; } |
sub long_status { return 3; } |
|
sub part_status_summary { return 4; } |
# Data for render_resource |
|
|
|
sub render_resource { |
sub render_resource { |
my ($resource, $part, $params) = @_; |
my ($resource, $part, $params) = @_; |
Line 904 sub render_resource {
|
Line 890 sub render_resource {
|
$icon = $params->{'indentString'}; |
$icon = $params->{'indentString'}; |
} |
} |
} else { |
} else { |
my $curfext= (split (/\./,$resource->src))[-1]; |
$icon = "<img src='".&Apache::loncommon::icon($resource->src). |
my $embstyle = &Apache::loncommon::fileembstyle($curfext); |
"' alt='' border='0' />"; |
# The unless conditional that follows is a bit of overkill |
|
if (!(!defined($embstyle) || $embstyle eq 'unk' || $embstyle eq 'hdn')) { |
|
$icon = "<img src='/adm/lonIcons/$curfext.gif' alt='' border='0' />"; |
|
} |
|
} |
} |
|
|
# Display the correct map icon to open or shut map |
# Display the correct map icon to open or shut map |
Line 979 sub render_resource {
|
Line 961 sub render_resource {
|
|
|
if ($resource->is_problem() && $part ne '0' && |
if ($resource->is_problem() && $part ne '0' && |
!$params->{'condensed'}) { |
!$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 = ""; |
$title = ""; |
} |
} |
|
|
Line 1054 sub render_quick_status {
|
Line 1040 sub render_quick_status {
|
|
|
if ($resource->is_problem() && |
if ($resource->is_problem() && |
!$firstDisplayed) { |
!$firstDisplayed) { |
my $icon = $statusIconMap{$resource->status($part)}; |
|
|
my $icon = $statusIconMap{$resource->simpleStatus($part)}; |
my $alt = $iconAltTags{$icon}; |
my $alt = $iconAltTags{$icon}; |
if ($icon) { |
if ($icon) { |
$result .= "<td width='30' valign='center' width='50' align='right'>$linkopen<img width='25' height='25' src='/adm/lonIcons/$icon' border='0' alt='$alt' />$linkclose</td>\n"; |
$result .= "<td width='30' valign='center' width='50' align='right'>$linkopen<img width='25' height='25' src='/adm/lonIcons/$icon' border='0' alt='$alt' />$linkclose</td>\n"; |
Line 1103 sub render_long_status {
|
Line 1090 sub render_long_status {
|
return $result; |
return $result; |
} |
} |
|
|
|
# Colors obtained by taking the icons, matching the colors, and |
|
# possibly reducing the Value (HSV) of the color, if it's too bright |
|
# for text, generally by one third or so. |
|
my %statusColors = |
|
( |
|
$resObj->CLOSED => '#000000', |
|
$resObj->OPEN => '#998b13', |
|
$resObj->CORRECT => '#26933f', |
|
$resObj->INCORRECT => '#c48207', |
|
$resObj->ATTEMPTED => '#a87510', |
|
$resObj->ERROR => '#000000' |
|
); |
|
my %statusStrings = |
|
( |
|
$resObj->CLOSED => 'Not yet open', |
|
$resObj->OPEN => 'Open', |
|
$resObj->CORRECT => 'Correct', |
|
$resObj->INCORRECT => 'Incorrect', |
|
$resObj->ATTEMPTED => 'Attempted', |
|
$resObj->ERROR => 'Network Error' |
|
); |
|
my @statuses = ($resObj->CORRECT, $resObj->ATTEMPTED, $resObj->INCORRECT, $resObj->OPEN, $resObj->CLOSED, $resObj->ERROR); |
|
|
|
use Data::Dumper; |
|
sub render_parts_summary_status { |
|
my ($resource, $part, $params) = @_; |
|
if (!$resource->is_problem()) { return '<td></td>'; } |
|
if ($params->{showParts}) { |
|
return '<td></td>'; |
|
} |
|
|
|
my $td = "<td align='right'>\n"; |
|
my $endtd = "</td>\n"; |
|
|
|
# If there is a single part, just show the simple status |
|
if ($resource->singlepart()) { |
|
my $status = $resource->simpleStatus('0'); |
|
return $td . "<font color='" . $statusColors{$status} . "'>" |
|
. $statusStrings{$status} . "</font>" . $endtd; |
|
} |
|
|
|
# Now we can be sure the $part doesn't really matter. |
|
my $statusCount = $resource->simpleStatusCount(); |
|
my @counts; |
|
foreach my $status(@statuses) { |
|
# decouple display order from the simpleStatusCount order |
|
my $slot = Apache::lonnavmaps::resource::statusToSlot($status); |
|
if ($statusCount->[$slot]) { |
|
push @counts, "<font color='" . $statusColors{$status} . |
|
"'>" . $statusCount->[$slot] . ' ' |
|
. $statusStrings{$status} . "</font>"; |
|
} |
|
} |
|
|
|
return $td . $resource->countParts() . ' parts: ' . join (', ', @counts) . $endtd; |
|
} |
|
|
my @preparedColumns = (\&render_resource, \&render_communication_status, |
my @preparedColumns = (\&render_resource, \&render_communication_status, |
\&render_quick_status, \&render_long_status); |
\&render_quick_status, \&render_long_status, |
|
\&render_parts_summary_status); |
|
|
sub setDefault { |
sub setDefault { |
my ($val, $default) = @_; |
my ($val, $default) = @_; |
Line 1170 sub render {
|
Line 1215 sub render {
|
if (!$ENV{'form.folderManip'} && !defined($args->{'iterator'})) { |
if (!$ENV{'form.folderManip'} && !defined($args->{'iterator'})) { |
# Step 1: Check to see if we have a navmap |
# Step 1: Check to see if we have a navmap |
if (!defined($navmap)) { |
if (!defined($navmap)) { |
$navmap = Apache::lonnavmaps::navmap->new(1, 1); |
$navmap = Apache::lonnavmaps::navmap->new(); |
$mustCloseNavMap = 1; |
$mustCloseNavMap = 1; |
} |
} |
$navmap->init(); |
|
|
|
# Step two: Locate what kind of here marker is necessary |
# Step two: Locate what kind of here marker is necessary |
# Determine where the "here" marker is and where the screen jumps to. |
# Determine where the "here" marker is and where the screen jumps to. |
Line 1191 sub render {
|
Line 1235 sub render {
|
|
|
# Step three: Ensure the folders are open |
# Step three: Ensure the folders are open |
my $mapIterator = $navmap->getIterator(undef, undef, undef, 1); |
my $mapIterator = $navmap->getIterator(undef, undef, undef, 1); |
my $depth = 1; |
my $curRes; |
$mapIterator->next(); # discard the first BEGIN_MAP |
|
my $curRes = $mapIterator->next(); |
|
my $found = 0; |
my $found = 0; |
|
|
# We only need to do this if we need to open the maps to show the |
# We only need to do this if we need to open the maps to show the |
# current position. This will change the counter so we can't count |
# current position. This will change the counter so we can't count |
# for the jump marker with this loop. |
# for the jump marker with this loop. |
while ($depth > 0 && !$found) { |
while (($curRes = $mapIterator->next()) && !$found) { |
if ($curRes == $mapIterator->BEGIN_MAP()) { $depth++; } |
|
if ($curRes == $mapIterator->END_MAP()) { $depth--; } |
|
|
|
if (ref($curRes) && $curRes->symb() eq $here) { |
if (ref($curRes) && $curRes->symb() eq $here) { |
my $mapStack = $mapIterator->getStack(); |
my $mapStack = $mapIterator->getStack(); |
|
|
Line 1216 sub render {
|
Line 1255 sub render {
|
} |
} |
$found = 1; |
$found = 1; |
} |
} |
|
|
$curRes = $mapIterator->next(); |
|
} |
} |
} |
} |
|
|
Line 1234 sub render {
|
Line 1271 sub render {
|
|
|
# Step 1: Check to see if we have a navmap |
# Step 1: Check to see if we have a navmap |
if (!defined($navmap)) { |
if (!defined($navmap)) { |
$navmap = Apache::lonnavmaps::navmap->new(1, 1); |
$navmap = Apache::lonnavmaps::navmap->new(); |
$mustCloseNavMap = 1; |
$mustCloseNavMap = 1; |
} |
} |
# Paranoia: Make sure it's ready |
|
$navmap->init(); |
|
|
|
# See if we're being passed a specific map |
# See if we're being passed a specific map |
if ($args->{'iterator_map'}) { |
if ($args->{'iterator_map'}) { |
Line 1257 sub render {
|
Line 1292 sub render {
|
# Note this does not take filtering or hidden into account... need |
# Note this does not take filtering or hidden into account... need |
# to be fixed? |
# to be fixed? |
my $mapIterator = $navmap->getIterator(undef, undef, $filterHash, 0); |
my $mapIterator = $navmap->getIterator(undef, undef, $filterHash, 0); |
my $depth = 1; |
my $curRes; |
$mapIterator->next(); |
|
my $curRes = $mapIterator->next(); |
|
my $foundJump = 0; |
my $foundJump = 0; |
my $counter = 0; |
my $counter = 0; |
|
|
while ($depth > 0 && !$foundJump) { |
while (($curRes = $mapIterator->next()) && !$foundJump) { |
if ($curRes == $mapIterator->BEGIN_MAP()) { $depth++; } |
|
if ($curRes == $mapIterator->END_MAP()) { $depth--; } |
|
if (ref($curRes)) { $counter++; } |
if (ref($curRes)) { $counter++; } |
|
|
if (ref($curRes) && $jump eq $curRes->symb()) { |
if (ref($curRes) && $jump eq $curRes->symb()) { |
Line 1276 sub render {
|
Line 1307 sub render {
|
$args->{'currentJumpIndex'} = $counter; |
$args->{'currentJumpIndex'} = $counter; |
$foundJump = 1; |
$foundJump = 1; |
} |
} |
|
|
$curRes = $mapIterator->next(); |
|
} |
} |
|
|
my $showParts = setDefault($args->{'showParts'}, 1); |
my $showParts = setDefault($args->{'showParts'}, 1); |
Line 1296 sub render {
|
Line 1325 sub render {
|
$result.='<tr><td align="right" valign="bottom">Key: </td>'; |
$result.='<tr><td align="right" valign="bottom">Key: </td>'; |
if ($navmap->{LAST_CHECK}) { |
if ($navmap->{LAST_CHECK}) { |
$result .= |
$result .= |
'<img src="/adm/lonMisc/chat.gif"> New discussion since '. |
'<img src="/adm/lonMisc/chat.gif"> '.&mt('New discussion since').' '. |
strftime("%A, %b %e at %I:%M %P", localtime($navmap->{LAST_CHECK})). |
strftime("%A, %b %e at %I:%M %P", localtime($navmap->{LAST_CHECK})). |
'</td><td align="center" valign="bottom"> '. |
'</td><td align="center" valign="bottom"> '. |
'<img src="/adm/lonMisc/feedback.gif"> New message (click to open)<p>'. |
'<img src="/adm/lonMisc/feedback.gif"> '.&mt('New message (click to open)').'<p>'. |
'</td>'; |
'</td>'; |
} else { |
} else { |
$result .= '<td align="center" valign="bottom"> '. |
$result .= '<td align="center" valign="bottom"> '. |
'<img src="/adm/lonMisc/chat.gif"> Discussions</td><td align="center" valign="bottom">'. |
'<img src="/adm/lonMisc/chat.gif"> '.&mt('Discussions').'</td><td align="center" valign="bottom">'. |
' <img src="/adm/lonMisc/feedback.gif"> New message (click to open)'. |
' <img src="/adm/lonMisc/feedback.gif"> '.&mt('New message (click to open)'). |
'</td>'; |
'</td>'; |
} |
} |
|
|
Line 1315 sub render {
|
Line 1344 sub render {
|
if ($condition) { |
if ($condition) { |
$result.="<a href=\"navmaps?condition=0&filter=&$queryString" . |
$result.="<a href=\"navmaps?condition=0&filter=&$queryString" . |
"&here=" . Apache::lonnet::escape($here) . |
"&here=" . Apache::lonnet::escape($here) . |
"\">Close All Folders</a>"; |
"\">".&mt('Close All Folders')."</a>"; |
} else { |
} else { |
$result.="<a href=\"navmaps?condition=1&filter=&$queryString" . |
$result.="<a href=\"navmaps?condition=1&filter=&$queryString" . |
"&here=" . Apache::lonnet::escape($here) . |
"&here=" . Apache::lonnet::escape($here) . |
"\">Open All Folders</a>"; |
"\">".&mt('Open All Folders')."</a>"; |
} |
} |
$result .= "<br /><br />\n"; |
$result .= "<br /><br />\n"; |
} |
} |
Line 1355 sub render {
|
Line 1384 sub render {
|
$it->{FIRST_RESOURCE}, |
$it->{FIRST_RESOURCE}, |
$it->{FINISH_RESOURCE}, |
$it->{FINISH_RESOURCE}, |
{}, undef, 1); |
{}, undef, 1); |
$depth = 0; |
my $depth = 0; |
$dfsit->next(); |
$dfsit->next(); |
my $curRes = $dfsit->next(); |
my $curRes = $dfsit->next(); |
while ($depth > -1) { |
while ($depth > -1) { |
Line 1387 sub render {
|
Line 1416 sub render {
|
|
|
my $displayedJumpMarker = 0; |
my $displayedJumpMarker = 0; |
# Set up iteration. |
# Set up iteration. |
$depth = 1; |
|
$it->next(); # discard initial BEGIN_MAP |
|
$curRes = $it->next(); |
|
my $now = time(); |
my $now = time(); |
my $in24Hours = $now + 24 * 60 * 60; |
my $in24Hours = $now + 24 * 60 * 60; |
my $rownum = 0; |
my $rownum = 0; |
Line 1397 sub render {
|
Line 1423 sub render {
|
# export "here" marker information |
# export "here" marker information |
$args->{'here'} = $here; |
$args->{'here'} = $here; |
|
|
while ($depth > 0) { |
$args->{'indentLevel'} = -1; # first BEGIN_MAP takes this to 0 |
if ($curRes == $it->BEGIN_MAP()) { $depth++; } |
while ($curRes = $it->next()) { |
if ($curRes == $it->END_MAP()) { $depth--; } |
|
|
|
# Maintain indentation level. |
# Maintain indentation level. |
if ($curRes == $it->BEGIN_MAP() || |
if ($curRes == $it->BEGIN_MAP() || |
$curRes == $it->BEGIN_BRANCH() ) { |
$curRes == $it->BEGIN_BRANCH() ) { |
Line 1516 sub render {
|
Line 1540 sub render {
|
my $filter = $it->{FILTER}; |
my $filter = $it->{FILTER}; |
my $stack = $it->getStack(); |
my $stack = $it->getStack(); |
my $src = getLinkForResource($stack); |
my $src = getLinkForResource($stack); |
|
my $anchor=''; |
|
if ($src=~s/(\#.*$)//) { |
|
$anchor=$1; |
|
} |
my $srcHasQuestion = $src =~ /\?/; |
my $srcHasQuestion = $src =~ /\?/; |
$args->{"resourceLink"} = $src. |
$args->{"resourceLink"} = $src. |
($srcHasQuestion?'&':'?') . |
($srcHasQuestion?'&':'?') . |
'symb=' . &Apache::lonnet::escape($curRes->symb()); |
'symb=' . &Apache::lonnet::escape($curRes->symb()). |
|
$anchor; |
|
|
# Now, display each column. |
# Now, display each column. |
foreach my $col (@$cols) { |
foreach my $col (@$cols) { |
Line 1553 sub render {
|
Line 1581 sub render {
|
$r->rflush(); |
$r->rflush(); |
} |
} |
} continue { |
} continue { |
$curRes = $it->next(); |
|
|
|
if ($r) { |
if ($r) { |
# If we have the connection, make sure the user is still connected |
# If we have the connection, make sure the user is still connected |
my $c = $r->connection; |
my $c = $r->connection; |
if ($c->aborted()) { |
if ($c->aborted()) { |
Apache::lonnet::logthis("navmaps aborted"); |
|
# Who cares what we do, nobody will see it anyhow. |
# Who cares what we do, nobody will see it anyhow. |
return ''; |
return ''; |
} |
} |
Line 1574 sub render {
|
Line 1599 sub render {
|
# it's quite likely this might fix other browsers, too, and |
# it's quite likely this might fix other browsers, too, and |
# certainly won't hurt anything. |
# certainly won't hurt anything. |
if ($displayedJumpMarker) { |
if ($displayedJumpMarker) { |
$result .= "<script>setTimeout(\"location += '#curloc';\", 0)</script>\n"; |
$result .= " |
|
<script> |
|
if (location.href.indexOf('#curloc')==-1) { |
|
setTimeout(\"location += '#curloc';\", 0) |
|
} |
|
</script>"; |
} |
} |
|
|
$result .= "</table>"; |
$result .= "</table>"; |
Line 1640 To create a navmap object, use the follo
|
Line 1670 To create a navmap object, use the follo
|
|
|
=over 4 |
=over 4 |
|
|
=item * B<Apache::lonnavmaps::navmap-E<gt>new>( |
=item * B<Apache::lonnavmaps::navmap-E<gt>new>(): |
genCourseAndUserOptions, genMailDiscussStatus, getUserData): |
|
|
|
Creates a new navmap object. genCourseAndUserOptions is a flag saying whether |
Creates a new navmap object. Returns the navmap object if this is |
the course options and user options hash should be generated. This is |
successful, or B<undef> if not. |
for when you are using the parameters of the resources that require |
|
them; see documentation in resource object |
|
documentation. genMailDiscussStatus causes the nav map to retreive |
|
information about the email and discussion status of |
|
resources. Returns the navmap object if this is successful, or |
|
B<undef> if not. You must check for undef; errors will occur when you |
|
try to use the other methods otherwise. getUserData, if true, will |
|
retreive the user's performance data for various problems. |
|
|
|
=back |
=back |
|
|
Once you have the $navmap object, call ->init() on it when you are ready |
|
to use it. This allows you to check if the course map is defined (see |
|
B<courseMapDefined> below) before engaging in potentially expensive |
|
initialization routines for the genCourseAndUserOptions and |
|
genMailDiscussStatus option. |
|
|
|
When you are done with the $navmap object, you I<must> call |
When you are done with the $navmap object, you I<must> call |
$navmap->untieHashes(), or you'll prevent the current user from using that |
$navmap->untieHashes(), or you'll prevent the current user from using that |
course until the web server is restarted. (!) |
course until the web server is restarted. (!) |
Line 1685 sub new {
|
Line 1700 sub new {
|
my $class = ref($proto) || $proto; |
my $class = ref($proto) || $proto; |
my $self = {}; |
my $self = {}; |
|
|
$self->{GENERATE_COURSE_USER_OPT} = shift; |
|
$self->{GENERATE_EMAIL_DISCUSS_STATUS} = shift; |
|
$self->{GET_USER_DATA} = shift; |
|
|
|
# Resource cache stores navmap resources as we reference them. We generate |
# Resource cache stores navmap resources as we reference them. We generate |
# them on-demand so we don't pay for creating resources unless we use them. |
# them on-demand so we don't pay for creating resources unless we use them. |
$self->{RESOURCE_CACHE} = {}; |
$self->{RESOURCE_CACHE} = {}; |
Line 1716 sub new {
|
Line 1727 sub new {
|
|
|
$self->{NAV_HASH} = \%navmaphash; |
$self->{NAV_HASH} = \%navmaphash; |
$self->{PARM_HASH} = \%parmhash; |
$self->{PARM_HASH} = \%parmhash; |
$self->{INITED} = 0; |
$self->{PARM_CACHE} = {}; |
|
|
bless($self); |
bless($self); |
|
|
return $self; |
return $self; |
} |
} |
|
|
sub init { |
sub generate_course_user_opt { |
my $self = shift; |
my $self = shift; |
if ($self->{INITED}) { return; } |
if ($self->{COURSE_USER_OPT_GENERATED}) { return; } |
|
|
# If the course opt hash and the user opt hash should be generated, |
my $uname=$ENV{'user.name'}; |
# generate them |
my $udom=$ENV{'user.domain'}; |
if ($self->{GENERATE_COURSE_USER_OPT}) { |
my $uhome=$ENV{'user.home'}; |
my $uname=$ENV{'user.name'}; |
my $cid=$ENV{'request.course.id'}; |
my $udom=$ENV{'user.domain'}; |
my $chome=$ENV{'course.'.$cid.'.home'}; |
my $uhome=$ENV{'user.home'}; |
my ($cdom,$cnum)=split(/\_/,$cid); |
my $cid=$ENV{'request.course.id'}; |
|
my $chome=$ENV{'course.'.$cid.'.home'}; |
my $userprefix=$uname.'_'.$udom.'_'; |
my ($cdom,$cnum)=split(/\_/,$cid); |
|
|
my %courserdatas; my %useropt; my %courseopt; my %userrdatas; |
my $userprefix=$uname.'_'.$udom.'_'; |
unless ($uhome eq 'no_host') { |
|
|
my %courserdatas; my %useropt; my %courseopt; my %userrdatas; |
|
unless ($uhome eq 'no_host') { |
|
# ------------------------------------------------- Get coursedata (if present) |
# ------------------------------------------------- Get coursedata (if present) |
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); |
# Check for network failure |
# Check for network failure |
if ( $reply =~ /no.such.host/i || $reply =~ /con_lost/i) { |
if ( $reply =~ /no.such.host/i || $reply =~ /con_lost/i) { |
$self->{NETWORK_FAILURE} = 1; |
$self->{NETWORK_FAILURE} = 1; |
} elsif ($reply!~/^error\:/) { |
} elsif ($reply!~/^error\:/) { |
$courserdatas{$cid}=$reply; |
$courserdatas{$cid}=$reply; |
$courserdatas{$cid.'.last_cache'}=time; |
$courserdatas{$cid.'.last_cache'}=time; |
} |
} |
} |
} |
foreach (split(/\&/,$courserdatas{$cid})) { |
foreach (split(/\&/,$courserdatas{$cid})) { |
my ($name,$value)=split(/\=/,$_); |
my ($name,$value)=split(/\=/,$_); |
$courseopt{$userprefix.&Apache::lonnet::unescape($name)}= |
$courseopt{$userprefix.&Apache::lonnet::unescape($name)}= |
&Apache::lonnet::unescape($value); |
&Apache::lonnet::unescape($value); |
} |
} |
# --------------------------------------------------- Get userdata (if present) |
# --------------------------------------------------- Get userdata (if present) |
unless ((time-$userrdatas{$uname.'___'.$udom.'.last_cache'})<240) { |
unless ((time-$userrdatas{$uname.'___'.$udom.'.last_cache'})<240) { |
my $reply=&Apache::lonnet::reply('dump:'.$udom.':'.$uname.':resourcedata',$uhome); |
my $reply=&Apache::lonnet::reply('dump:'.$udom.':'.$uname.':resourcedata',$uhome); |
if ($reply!~/^error\:/) { |
if ($reply!~/^error\:/) { |
$userrdatas{$uname.'___'.$udom}=$reply; |
$userrdatas{$uname.'___'.$udom}=$reply; |
$userrdatas{$uname.'___'.$udom.'.last_cache'}=time; |
$userrdatas{$uname.'___'.$udom.'.last_cache'}=time; |
} |
} |
# check to see if network failed |
# check to see if network failed |
elsif ( $reply=~/no.such.host/i || $reply=~/con.*lost/i ) |
elsif ( $reply=~/no.such.host/i || $reply=~/con.*lost/i ) |
{ |
{ |
$self->{NETWORK_FAILURE} = 1; |
$self->{NETWORK_FAILURE} = 1; |
} |
} |
} |
} |
foreach (split(/\&/,$userrdatas{$uname.'___'.$udom})) { |
foreach (split(/\&/,$userrdatas{$uname.'___'.$udom})) { |
my ($name,$value)=split(/\=/,$_); |
my ($name,$value)=split(/\=/,$_); |
$useropt{$userprefix.&Apache::lonnet::unescape($name)}= |
$useropt{$userprefix.&Apache::lonnet::unescape($name)}= |
&Apache::lonnet::unescape($value); |
&Apache::lonnet::unescape($value); |
} |
} |
$self->{COURSE_OPT} = \%courseopt; |
$self->{COURSE_OPT} = \%courseopt; |
$self->{USER_OPT} = \%useropt; |
$self->{USER_OPT} = \%useropt; |
} |
|
} |
|
|
|
if ($self->{GENERATE_EMAIL_DISCUSS_STATUS}) { |
|
my $cid=$ENV{'request.course.id'}; |
|
my ($cdom,$cnum)=split(/\_/,$cid); |
|
|
|
my %emailstatus = &Apache::lonnet::dump('email_status'); |
|
my $logoutTime = $emailstatus{'logout'}; |
|
my $courseLeaveTime = $emailstatus{'logout_'.$ENV{'request.course.id'}}; |
|
$self->{LAST_CHECK} = (($courseLeaveTime > $logoutTime) ? |
|
$courseLeaveTime : $logoutTime); |
|
my %discussiontime = &Apache::lonnet::dump('discussiontimes', |
|
$cdom, $cnum); |
|
my %feedback=(); |
|
my %error=(); |
|
my $keys = &Apache::lonnet::reply('keys:'. |
|
$ENV{'user.domain'}.':'. |
|
$ENV{'user.name'}.':nohist_email', |
|
$ENV{'user.home'}); |
|
|
|
foreach my $msgid (split(/\&/, $keys)) { |
|
$msgid=&Apache::lonnet::unescape($msgid); |
|
my $plain=&Apache::lonnet::unescape(&Apache::lonnet::unescape($msgid)); |
|
if ($plain=~/(Error|Feedback) \[([^\]]+)\]/) { |
|
my ($what,$url)=($1,$2); |
|
my %status= |
|
&Apache::lonnet::get('email_status',[$msgid]); |
|
if ($status{$msgid}=~/^error\:/) { |
|
$status{$msgid}=''; |
|
} |
|
|
|
if (($status{$msgid} eq 'new') || |
|
(!$status{$msgid})) { |
|
if ($what eq 'Error') { |
|
$error{$url}.=','.$msgid; |
|
} else { |
|
$feedback{$url}.=','.$msgid; |
|
} |
|
} |
|
} |
|
} |
|
|
|
$self->{FEEDBACK} = \%feedback; |
|
$self->{ERROR_MSG} = \%error; # what is this? JB |
|
$self->{DISCUSSION_TIME} = \%discussiontime; |
|
$self->{EMAIL_STATUS} = \%emailstatus; |
|
|
|
} |
} |
|
|
if ($self->{GET_USER_DATA}) { |
$self->{COURSE_USER_OPT_GENERATED} = 1; |
# Retreive performance data on problems |
|
my %student_data = Apache::lonnet::currentdump($ENV{'request.course.id'}, |
return; |
$ENV{'user.domain'}, |
} |
$ENV{'user.name'}); |
|
$self->{STUDENT_DATA} = \%student_data; |
sub generate_email_discuss_status { |
|
my $self = shift; |
|
if ($self->{EMAIL_DISCUSS_GENERATED}) { return; } |
|
|
|
my $cid=$ENV{'request.course.id'}; |
|
my ($cdom,$cnum)=split(/\_/,$cid); |
|
|
|
my %emailstatus = &Apache::lonnet::dump('email_status'); |
|
my $logoutTime = $emailstatus{'logout'}; |
|
my $courseLeaveTime = $emailstatus{'logout_'.$ENV{'request.course.id'}}; |
|
$self->{LAST_CHECK} = (($courseLeaveTime > $logoutTime) ? |
|
$courseLeaveTime : $logoutTime); |
|
my %discussiontime = &Apache::lonnet::dump('discussiontimes', |
|
$cdom, $cnum); |
|
my %feedback=(); |
|
my %error=(); |
|
my $keys = &Apache::lonnet::reply('keys:'. |
|
$ENV{'user.domain'}.':'. |
|
$ENV{'user.name'}.':nohist_email', |
|
$ENV{'user.home'}); |
|
|
|
foreach my $msgid (split(/\&/, $keys)) { |
|
$msgid=&Apache::lonnet::unescape($msgid); |
|
my $plain=&Apache::lonnet::unescape(&Apache::lonnet::unescape($msgid)); |
|
if ($plain=~/(Error|Feedback) \[([^\]]+)\]/) { |
|
my ($what,$url)=($1,$2); |
|
my %status= |
|
&Apache::lonnet::get('email_status',[$msgid]); |
|
if ($status{$msgid}=~/^error\:/) { |
|
$status{$msgid}=''; |
|
} |
|
|
|
if (($status{$msgid} eq 'new') || |
|
(!$status{$msgid})) { |
|
if ($what eq 'Error') { |
|
$error{$url}.=','.$msgid; |
|
} else { |
|
$feedback{$url}.=','.$msgid; |
|
} |
|
} |
|
} |
} |
} |
|
|
|
$self->{FEEDBACK} = \%feedback; |
|
$self->{ERROR_MSG} = \%error; # what is this? JB |
|
$self->{DISCUSSION_TIME} = \%discussiontime; |
|
$self->{EMAIL_STATUS} = \%emailstatus; |
|
|
|
$self->{EMAIL_DISCUSS_GENERATED} = 1; |
|
} |
|
|
$self->{PARM_CACHE} = {}; |
sub get_user_data { |
$self->{INITED} = 1; |
my $self = shift; |
|
if ($self->{RETRIEVED_USER_DATA}) { return; } |
|
|
|
# Retrieve performance data on problems |
|
my %student_data = Apache::lonnet::currentdump($ENV{'request.course.id'}, |
|
$ENV{'user.domain'}, |
|
$ENV{'user.name'}); |
|
$self->{STUDENT_DATA} = \%student_data; |
|
|
|
$self->{RETRIEVED_USER_DATA} = 1; |
} |
} |
|
|
# Internal function: Takes a key to look up in the nav hash and implements internal |
# Internal function: Takes a key to look up in the nav hash and implements internal |
Line 1885 sub untieHashes {
|
Line 1902 sub untieHashes {
|
sub hasDiscussion { |
sub hasDiscussion { |
my $self = shift; |
my $self = shift; |
my $symb = shift; |
my $symb = shift; |
|
|
|
$self->generate_email_discuss_status(); |
|
|
if (!defined($self->{DISCUSSION_TIME})) { return 0; } |
if (!defined($self->{DISCUSSION_TIME})) { return 0; } |
|
|
#return defined($self->{DISCUSSION_TIME}->{$symb}); |
#return defined($self->{DISCUSSION_TIME}->{$symb}); |
Line 1899 sub getFeedback {
|
Line 1919 sub getFeedback {
|
my $self = shift; |
my $self = shift; |
my $symb = shift; |
my $symb = shift; |
|
|
|
$self->generate_email_discuss_status(); |
|
|
if (!defined($self->{FEEDBACK})) { return ""; } |
if (!defined($self->{FEEDBACK})) { return ""; } |
|
|
return $self->{FEEDBACK}->{$symb}; |
return $self->{FEEDBACK}->{$symb}; |
Line 1908 sub getFeedback {
|
Line 1930 sub getFeedback {
|
sub getErrors { |
sub getErrors { |
my $self = shift; |
my $self = shift; |
my $src = shift; |
my $src = shift; |
|
|
|
$self->generate_email_discuss_status(); |
|
|
if (!defined($self->{ERROR_MSG})) { return ""; } |
if (!defined($self->{ERROR_MSG})) { return ""; } |
return $self->{ERROR_MSG}->{$src}; |
return $self->{ERROR_MSG}->{$src}; |
} |
} |
Line 1956 sub getById {
|
Line 1980 sub getById {
|
sub getBySymb { |
sub getBySymb { |
my $self = shift; |
my $self = shift; |
my $symb = shift; |
my $symb = shift; |
my ($mapUrl, $id, $filename) = split (/___/, $symb); |
my ($mapUrl, $id, $filename) = &Apache::lonnet::decode_symb($symb); |
my $map = $self->getResourceByUrl($mapUrl); |
my $map = $self->getResourceByUrl($mapUrl); |
return $self->getById($map->map_pc() . '.' . $id); |
return $self->getById($map->map_pc() . '.' . $id); |
} |
} |
Line 2021 sub parmval_real {
|
Line 2045 sub parmval_real {
|
my $self = shift; |
my $self = shift; |
my ($what,$symb,$recurse) = @_; |
my ($what,$symb,$recurse) = @_; |
|
|
|
# Make sure the {USER_OPT} and {COURSE_OPT} hashes are populated |
|
$self->generate_course_user_opt(); |
|
|
my $cid=$ENV{'request.course.id'}; |
my $cid=$ENV{'request.course.id'}; |
my $csec=$ENV{'request.course.sec'}; |
my $csec=$ENV{'request.course.sec'}; |
my $uname=$ENV{'user.name'}; |
my $uname=$ENV{'user.name'}; |
Line 2029 sub parmval_real {
|
Line 2056 sub parmval_real {
|
unless ($symb) { return ''; } |
unless ($symb) { return ''; } |
my $result=''; |
my $result=''; |
|
|
my ($mapname,$id,$fn)=split(/\_\_\_/,$symb); |
my ($mapname,$id,$fn)=&Apache::lonnet::decode_symb($symb); |
|
|
# ----------------------------------------------------- Cascading lookup scheme |
# ----------------------------------------------------- Cascading lookup scheme |
my $rwhat=$what; |
my $rwhat=$what; |
Line 2079 sub parmval_real {
|
Line 2106 sub parmval_real {
|
|
|
# ----------------------------------------------------- fourth , check default |
# ----------------------------------------------------- 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} |
if (defined($default)) { return $default} |
|
|
# --------------------------------------------------- fifth , cascade up parts |
# --------------------------------------------------- fifth , cascade up parts |
Line 2195 sub retrieveResources {
|
Line 2226 sub retrieveResources {
|
my @resources = (); |
my @resources = (); |
|
|
# Run down the iterator and collect the resources. |
# Run down the iterator and collect the resources. |
my $depth = 1; |
my $curRes; |
$it->next(); |
|
my $curRes = $it->next(); |
while ($curRes = $it->next()) { |
|
|
while ($depth > 0) { |
|
if ($curRes == $it->BEGIN_MAP()) { |
|
$depth++; |
|
} |
|
if ($curRes == $it->END_MAP()) { |
|
$depth--; |
|
} |
|
|
|
if (ref($curRes)) { |
if (ref($curRes)) { |
if (!&$filterFunc($curRes)) { |
if (!&$filterFunc($curRes)) { |
next; |
next; |
Line 2219 sub retrieveResources {
|
Line 2241 sub retrieveResources {
|
} |
} |
} |
} |
|
|
} continue { |
|
$curRes = $it->next(); |
|
} |
} |
|
|
return @resources; |
return @resources; |
Line 2296 new branch. The possible tokens are:
|
Line 2316 new branch. The possible tokens are:
|
|
|
=over 4 |
=over 4 |
|
|
|
=item * B<END_ITERATOR>: |
|
|
|
The iterator has returned all that it's going to. Further calls to the |
|
iterator will just produce more of these. This is a "false" value, and |
|
is the only false value the iterator which will be returned, so it can |
|
be used as a loop sentinel. |
|
|
=item * B<BEGIN_MAP>: |
=item * B<BEGIN_MAP>: |
|
|
A new map is being recursed into. This is returned I<after> the map |
A new map is being recursed into. This is returned I<after> the map |
Line 2328 but only one resource will be returned.
|
Line 2355 but only one resource will be returned.
|
|
|
=back |
=back |
|
|
|
=head2 Normal Usage |
|
|
|
Normal usage of the iterator object is to do the following: |
|
|
|
my $it = $navmap->getIterator([your params here]); |
|
my $curRes; |
|
while ($curRes = $it->next()) { |
|
[your logic here] |
|
} |
|
|
|
Note that inside of the loop, it's frequently useful to check if |
|
"$curRes" is a reference or not with the reference function; only |
|
resource objects will be references, and any non-references will |
|
be the tokens described above. |
|
|
|
Also note there is some old code floating around that trys to track |
|
the depth of the iterator to see when it's done; do not copy that |
|
code. It is difficult to get right and harder to understand then |
|
this. They should be migrated to this new style. |
|
|
=cut |
=cut |
|
|
# Here are the tokens for the iterator: |
# Here are the tokens for the iterator: |
|
|
|
sub END_ITERATOR { return 0; } |
sub BEGIN_MAP { return 1; } # begining of a new map |
sub BEGIN_MAP { return 1; } # begining of a new map |
sub END_MAP { return 2; } # end of the map |
sub END_MAP { return 2; } # end of the map |
sub BEGIN_BRANCH { return 3; } # beginning of a branch |
sub BEGIN_BRANCH { return 3; } # beginning of a branch |
Line 2417 sub new {
|
Line 2465 sub new {
|
|
|
# prime the recursion |
# prime the recursion |
$self->{$firstResourceName}->{DATA}->{$valName} = 0; |
$self->{$firstResourceName}->{DATA}->{$valName} = 0; |
my $depth = 0; |
$iterator->next(); |
$iterator->next(); |
|
my $curRes = $iterator->next(); |
my $curRes = $iterator->next(); |
while ($depth > -1) { |
my $depth = 1; |
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)) { |
if (ref($curRes)) { |
# If there's only one resource, this will save it |
# If there's only one resource, this will save it |
# we have to filter empty resources from consideration here, |
# we have to filter empty resources from consideration here, |
Line 2457 sub new {
|
Line 2505 sub new {
|
$curRes->{DATA}->{DISPLAY_DEPTH} = $finalDepth; |
$curRes->{DATA}->{DISPLAY_DEPTH} = $finalDepth; |
if ($finalDepth > $maxDepth) {$maxDepth = $finalDepth;} |
if ($finalDepth > $maxDepth) {$maxDepth = $finalDepth;} |
} |
} |
} continue { |
|
$curRes = $iterator->next(); |
$curRes = $iterator->next(); |
} |
} |
} |
} |
|
|
Line 2479 sub new {
|
Line 2527 sub new {
|
$self->{MAX_DEPTH} = $maxDepth; |
$self->{MAX_DEPTH} = $maxDepth; |
$self->{STACK} = []; |
$self->{STACK} = []; |
$self->{RECURSIVE_ITERATOR_FLAG} = 0; |
$self->{RECURSIVE_ITERATOR_FLAG} = 0; |
|
$self->{FINISHED} = 0; # When true, the iterator has finished |
|
|
for (my $i = 0; $i <= $self->{MAX_DEPTH}; $i++) { |
for (my $i = 0; $i <= $self->{MAX_DEPTH}; $i++) { |
push @{$self->{STACK}}, []; |
push @{$self->{STACK}}, []; |
Line 2496 sub new {
|
Line 2545 sub new {
|
sub next { |
sub next { |
my $self = shift; |
my $self = shift; |
|
|
|
if ($self->{FINISHED}) { |
|
return END_ITERATOR(); |
|
} |
|
|
# If we want to return the top-level map object, and haven't yet, |
# If we want to return the top-level map object, and haven't yet, |
# do so. |
# do so. |
if ($self->{RETURN_0} && !$self->{HAVE_RETURNED_0}) { |
if ($self->{RETURN_0} && !$self->{HAVE_RETURNED_0}) { |
Line 2555 sub next {
|
Line 2608 sub next {
|
$self->{CURRENT_DEPTH}--; |
$self->{CURRENT_DEPTH}--; |
return END_BRANCH(); |
return END_BRANCH(); |
} else { |
} else { |
|
$self->{FINISHED} = 1; |
return END_MAP(); |
return END_MAP(); |
} |
} |
} |
} |
Line 2688 package Apache::lonnavmaps::DFSiterator;
|
Line 2742 package Apache::lonnavmaps::DFSiterator;
|
# useful for pre-processing of some kind, and is in fact used by the main |
# useful for pre-processing of some kind, and is in fact used by the main |
# iterator that way, but that's about it. |
# iterator that way, but that's about it. |
# One could imagine merging this into the init routine of the main iterator, |
# 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 |
# use might be found for it. - Jeremy |
|
|
# Unlike the main iterator, this DOES return all resources, even blank ones. |
# Unlike the main iterator, this DOES return all resources, even blank ones. |
Line 3051 sub symb {
|
Line 3105 sub symb {
|
my $self=shift; |
my $self=shift; |
(my $first, my $second) = $self->{ID} =~ /(\d+).(\d+)/; |
(my $first, my $second) = $self->{ID} =~ /(\d+).(\d+)/; |
my $symbSrc = &Apache::lonnet::declutter($self->src()); |
my $symbSrc = &Apache::lonnet::declutter($self->src()); |
return &Apache::lonnet::declutter( |
my $symb = &Apache::lonnet::declutter($self->navHash('map_id_'.$first)) |
$self->navHash('map_id_'.$first)) |
|
. '___' . $second . '___' . $symbSrc; |
. '___' . $second . '___' . $symbSrc; |
|
return &Apache::lonnet::symbclean($symb); |
} |
} |
sub title { |
sub title { |
my $self=shift; |
my $self=shift; |
Line 3121 sub is_page {
|
Line 3175 sub is_page {
|
sub is_problem { |
sub is_problem { |
my $self=shift; |
my $self=shift; |
my $src = $self->src(); |
my $src = $self->src(); |
return ($src =~ /problem$/); |
return ($src =~ /\.(problem|exam|quiz|assess|survey|form|library)$/); |
} |
} |
sub is_sequence { |
sub is_sequence { |
my $self=shift; |
my $self=shift; |
Line 3299 sub answerdate {
|
Line 3353 sub answerdate {
|
} |
} |
sub awarded { |
sub awarded { |
my $self = shift; my $part = shift; |
my $self = shift; my $part = shift; |
|
$self->{NAV_MAP}->get_user_data(); |
if (!defined($part)) { $part = '0'; } |
if (!defined($part)) { $part = '0'; } |
return $self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$part.'.awarded'}; |
return $self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$part.'.awarded'}; |
} |
} |
Line 3320 sub opendate {
|
Line 3375 sub opendate {
|
} |
} |
sub problemstatus { |
sub problemstatus { |
(my $self, my $part) = @_; |
(my $self, my $part) = @_; |
return $self->parmval("problemstatus", $part); |
return lc $self->parmval("problemstatus", $part); |
} |
} |
sub sig { |
sub sig { |
(my $self, my $part) = @_; |
(my $self, my $part) = @_; |
Line 3488 sub multipart {
|
Line 3543 sub multipart {
|
return $self->countParts() > 1; |
return $self->countParts() > 1; |
} |
} |
|
|
|
sub singlepart { |
|
my $self = shift; |
|
return $self->countParts() == 1; |
|
} |
|
|
sub responseType { |
sub responseType { |
my $self = shift; |
my $self = shift; |
my $part = shift; |
my $part = shift; |
|
|
$self->extractParts(); |
$self->extractParts(); |
return $self->{RESPONSE_TYPES}->{$part}; |
if (defined($self->{RESPONSE_TYPES}->{$part})) { |
|
return @{$self->{RESPONSE_TYPES}->{$part}}; |
|
} else { |
|
return undef; |
|
} |
} |
} |
|
|
sub responseIds { |
sub responseIds { |
Line 3501 sub responseIds {
|
Line 3565 sub responseIds {
|
my $part = shift; |
my $part = shift; |
|
|
$self->extractParts(); |
$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 |
# Private function: Extracts the parts information, both part names and |
Line 3518 sub extractParts {
|
Line 3586 sub extractParts {
|
|
|
# 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 $partorder = &Apache::lonnet::metadata($self->src(), 'partorder'); |
my $metadata = &Apache::lonnet::metadata($self->src(), 'packages'); |
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())) { |
if ($partorder) { |
$parts{$part} = 1; |
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 %responseIdHash; |
my %responseTypeHash; |
my %responseTypeHash; |
Line 3555 sub extractParts {
|
Line 3635 sub extractParts {
|
} |
} |
|
|
# Now, the unfortunate thing about this is that parts, part name, and |
# 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. |
# name and response id can themselves have underscores in them. |
# So we have to use our knowlege of part names to figure out |
# 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 |
# where the part names begin and end, and even then, it is possible |
Line 3567 sub extractParts {
|
Line 3647 sub extractParts {
|
my $partIdSoFar = ''; |
my $partIdSoFar = ''; |
my @partChunks = split /_/, $partStuff; |
my @partChunks = split /_/, $partStuff; |
my $i = 0; |
my $i = 0; |
|
|
for ($i = 0; $i < scalar(@partChunks); $i++) { |
for ($i = 0; $i < scalar(@partChunks); $i++) { |
if ($partIdSoFar) { $partIdSoFar .= '_'; } |
if ($partIdSoFar) { $partIdSoFar .= '_'; } |
$partIdSoFar .= $partChunks[$i]; |
$partIdSoFar .= $partChunks[$i]; |
Line 3575 sub extractParts {
|
Line 3654 sub extractParts {
|
my @otherChunks = @partChunks[$i+1..$#partChunks]; |
my @otherChunks = @partChunks[$i+1..$#partChunks]; |
my $responseId = join('_', @otherChunks); |
my $responseId = join('_', @otherChunks); |
push @{$responseIdHash{$partIdSoFar}}, $responseId; |
push @{$responseIdHash{$partIdSoFar}}, $responseId; |
$responseTypeHash{$partIdSoFar} = $responseType; |
push @{$responseTypeHash{$partIdSoFar}}, $responseType; |
last; |
|
} |
} |
} |
} |
} |
} |
} |
} |
|
|
$self->{RESPONSE_IDS} = \%responseIdHash; |
$self->{RESPONSE_IDS} = \%responseIdHash; |
$self->{RESPONSE_TYPES} = \%responseTypeHash; |
$self->{RESPONSE_TYPES} = \%responseTypeHash; |
} |
} |
Line 3773 sub getCompletionStatus {
|
Line 3850 sub getCompletionStatus {
|
|
|
my $status = $self->queryRestoreHash('solved', shift); |
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_student') {return $self->CORRECT;} |
if ($status eq 'correct_by_override') {return $self->CORRECT_BY_OVERRIDE; } |
if ($status eq 'correct_by_override') {return $self->CORRECT_BY_OVERRIDE; } |
if ($status eq 'incorrect_attempted') {return $self->INCORRECT; } |
if ($status eq 'incorrect_attempted') {return $self->INCORRECT; } |
Line 3891 sub status {
|
Line 3968 sub status {
|
# dimension and 5 entries on the other, which we want to colorize, |
# dimension and 5 entries on the other, which we want to colorize, |
# plus network failure and "no date data at all". |
# plus network failure and "no date data at all". |
|
|
|
#if ($self->{RESOURCE_ERROR}) { return NETWORK_FAILURE; } |
if ($completionStatus == NETWORK_FAILURE) { 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: |
# There are a few whole rows we can dispose of: |
if ($completionStatus == CORRECT || |
if ($completionStatus == CORRECT || |
Line 3937 sub status {
|
Line 4020 sub status {
|
if ($completionStatus == INCORRECT || $completionStatus == INCORRECT_BY_OVERRIDE) { |
if ($completionStatus == INCORRECT || $completionStatus == INCORRECT_BY_OVERRIDE) { |
# and there are TRIES LEFT: |
# and there are TRIES LEFT: |
if ($self->tries($part) < $self->maxtries($part) || !$self->maxtries($part)) { |
if ($self->tries($part) < $self->maxtries($part) || !$self->maxtries($part)) { |
return TRIES_LEFT; |
return $suppressFeedback ? ANSWER_SUBMITTED : TRIES_LEFT; |
} |
} |
return $suppressFeedback ? ANSWER_SUBMITTED : INCORRECT; # otherwise, return orange; student can't fix this |
return $suppressFeedback ? ANSWER_SUBMITTED : INCORRECT; # otherwise, return orange; student can't fix this |
} |
} |
Line 3946 sub status {
|
Line 4029 sub status {
|
return OPEN; |
return OPEN; |
} |
} |
|
|
|
sub CLOSED { return 23; } |
|
sub ERROR { return 24; } |
|
|
|
=pod |
|
|
|
B<Simple Status> |
|
|
|
Convenience method B<simpleStatus> provides a "simple status" for the resource. |
|
"Simple status" corresponds to "which icon is shown on the |
|
Navmaps". There are six "simple" statuses: |
|
|
|
=over 4 |
|
|
|
=item * B<CLOSED>: The problem is currently closed. (No icon shown.) |
|
|
|
=item * B<OPEN>: The problem is open and unattempted. |
|
|
|
=item * B<CORRECT>: The problem is correct for any reason. |
|
|
|
=item * B<INCORRECT>: The problem is incorrect and can still be |
|
completed successfully. |
|
|
|
=item * B<ATTEMPTED>: The problem has been attempted, but the student |
|
does not know if they are correct. (The ellipsis icon.) |
|
|
|
=item * B<ERROR>: There is an error retrieving information about this |
|
problem. |
|
|
|
=back |
|
|
|
=cut |
|
|
|
# This hash maps the composite status to this simple status, and |
|
# can be used directly, if you like |
|
my %compositeToSimple = |
|
( |
|
NETWORK_FAILURE() => ERROR, |
|
NOTHING_SET() => CLOSED, |
|
CORRECT() => CORRECT, |
|
EXCUSED() => CORRECT, |
|
PAST_DUE_NO_ANSWER() => INCORRECT, |
|
PAST_DUE_ANSWER_LATER() => INCORRECT, |
|
ANSWER_OPEN() => INCORRECT, |
|
OPEN_LATER() => CLOSED, |
|
TRIES_LEFT() => OPEN, |
|
INCORRECT() => INCORRECT, |
|
OPEN() => OPEN, |
|
ATTEMPTED() => ATTEMPTED, |
|
ANSWER_SUBMITTED() => ATTEMPTED |
|
); |
|
|
|
sub simpleStatus { |
|
my $self = shift; |
|
my $part = shift; |
|
my $status = $self->status($part); |
|
return $compositeToSimple{$status}; |
|
} |
|
|
|
=pod |
|
|
|
B<simpleStatusCount> will return an array reference containing, in |
|
this order, the number of OPEN, CLOSED, CORRECT, INCORRECT, ATTEMPTED, |
|
and ERROR parts the given problem has. |
|
|
|
=cut |
|
|
|
# This maps the status to the slot we want to increment |
|
my %statusToSlotMap = |
|
( |
|
OPEN() => 0, |
|
CLOSED() => 1, |
|
CORRECT() => 2, |
|
INCORRECT() => 3, |
|
ATTEMPTED() => 4, |
|
ERROR() => 5 |
|
); |
|
|
|
sub statusToSlot { return $statusToSlotMap{shift()}; } |
|
|
|
sub simpleStatusCount { |
|
my $self = shift; |
|
|
|
my @counts = (0, 0, 0, 0, 0, 0, 0); |
|
foreach my $part (@{$self->parts()}) { |
|
$counts[$statusToSlotMap{$self->simpleStatus($part)}]++; |
|
} |
|
|
|
return \@counts; |
|
} |
|
|
=pod |
=pod |
|
|
B<Completable> |
B<Completable> |