version 1.394, 2006/12/20 23:25:13
|
version 1.450, 2010/08/12 16:03:24
|
Line 2
|
Line 2
|
# Navigate Maps Handler |
# Navigate Maps Handler |
# |
# |
# $Id$ |
# $Id$ |
|
|
# |
# |
# Copyright Michigan State University Board of Trustees |
# Copyright Michigan State University Board of Trustees |
# |
# |
Line 27
|
Line 28
|
# |
# |
### |
### |
|
|
package Apache::lonnavmaps; |
|
|
|
use strict; |
|
use GDBM_File; |
|
use Apache::loncommon(); |
|
use Apache::lonenc(); |
|
use Apache::lonlocal; |
|
use Apache::lonnet; |
|
use POSIX qw (floor strftime); |
|
use Data::Dumper; # for debugging, not always |
|
use Time::HiRes qw( gettimeofday tv_interval ); |
|
use lib '/home/httpd/lib/perl/'; |
|
use LONCAPA; |
|
|
|
# symbolic constants |
|
sub SYMB { return 1; } |
|
sub URL { return 2; } |
|
sub NOTHING { return 3; } |
|
|
|
# Some data |
|
|
|
my $resObj = "Apache::lonnavmaps::resource"; |
|
|
|
# Keep these mappings in sync with lonquickgrades, which uses the colors |
|
# instead of the icons. |
|
my %statusIconMap = |
|
( |
|
$resObj->CLOSED => '', |
|
$resObj->OPEN => 'navmap.open.gif', |
|
$resObj->CORRECT => 'navmap.correct.gif', |
|
$resObj->PARTIALLY_CORRECT => 'navmap.partial.gif', |
|
$resObj->INCORRECT => 'navmap.wrong.gif', |
|
$resObj->ATTEMPTED => 'navmap.ellipsis.gif', |
|
$resObj->ERROR => '' |
|
); |
|
|
|
my %iconAltTags = |
|
( 'navmap.correct.gif' => 'Correct', |
|
'navmap.wrong.gif' => 'Incorrect', |
|
'navmap.open.gif' => 'Open' ); |
|
|
|
# Defines a status->color mapping, null string means don't color |
|
my %colormap = |
|
( $resObj->NETWORK_FAILURE => '', |
|
$resObj->CORRECT => '', |
|
$resObj->EXCUSED => '#3333FF', |
|
$resObj->PAST_DUE_ANSWER_LATER => '', |
|
$resObj->PAST_DUE_NO_ANSWER => '', |
|
$resObj->ANSWER_OPEN => '#006600', |
|
$resObj->OPEN_LATER => '', |
|
$resObj->TRIES_LEFT => '', |
|
$resObj->INCORRECT => '', |
|
$resObj->OPEN => '', |
|
$resObj->NOTHING_SET => '', |
|
$resObj->ATTEMPTED => '', |
|
$resObj->ANSWER_SUBMITTED => '', |
|
$resObj->PARTIALLY_CORRECT => '#006600' |
|
); |
|
# And a special case in the nav map; what to do when the assignment |
|
# is not yet done and due in less then 24 hours |
|
my $hurryUpColor = "#FF0000"; |
|
|
|
sub close { |
|
if ($env{'environment.remotenavmap'} ne 'on') { return ''; } |
|
return(<<ENDCLOSE); |
|
<script type="text/javascript"> |
|
window.status='Accessing Nav Control'; |
|
menu=window.open("/adm/rat/empty.html","loncapanav", |
|
"height=600,width=400,scrollbars=1"); |
|
window.status='Closing Nav Control'; |
|
menu.close(); |
|
window.status='Done.'; |
|
</script> |
|
ENDCLOSE |
|
} |
|
|
|
sub update { |
|
if ($env{'environment.remotenavmap'} ne 'on') { return ''; } |
|
if (!$env{'request.course.id'}) { return ''; } |
|
if ($ENV{'REQUEST_URI'}=~m|^/adm/navmaps|) { return ''; } |
|
return(<<ENDUPDATE); |
|
<form name="navform"></form> |
|
<script type="text/javascript"> |
|
this.document.navform.action='/adm/navmaps#curloc'; |
|
this.document.navform.target='loncapanav'; |
|
this.document.navform.submit(); |
|
</script> |
|
ENDUPDATE |
|
} |
|
|
|
# Convenience functions: Returns a string that adds or subtracts |
|
# the second argument from the first hash, appropriate for the |
|
# query string that determines which folders to recurse on |
|
sub addToFilter { |
|
my $hashIn = shift; |
|
my $addition = shift; |
|
my %hash = %$hashIn; |
|
$hash{$addition} = 1; |
|
|
|
return join (",", keys(%hash)); |
|
} |
|
|
|
sub removeFromFilter { |
|
my $hashIn = shift; |
|
my $subtraction = shift; |
|
my %hash = %$hashIn; |
|
|
|
delete $hash{$subtraction}; |
|
return join(",", keys(%hash)); |
|
} |
|
|
|
# Convenience function: Given a stack returned from getStack on the iterator, |
|
# return the correct src() value. |
|
sub getLinkForResource { |
|
my $stack = shift; |
|
my $res; |
|
|
|
# Check to see if there are any pages in the stack |
|
foreach $res (@$stack) { |
|
if (defined($res)) { |
|
my $anchor; |
|
if ($res->is_page()) { |
|
foreach my $item (@$stack) { if (defined($item)) { $anchor = $item; } } |
|
$anchor=&escape($anchor->shown_symb()); |
|
return ($res->link(),$res->shown_symb(),$anchor); |
|
} |
|
# in case folder was skipped over as "only sequence" |
|
my ($map,$id,$src)=&Apache::lonnet::decode_symb($res->symb()); |
|
if ($map=~/\.page$/) { |
|
my $url=&Apache::lonnet::clutter($map); |
|
$anchor=&escape($src->shown_symb()); |
|
return ($url,$res->shown_symb(),$anchor); |
|
} |
|
} |
|
} |
|
|
|
# Failing that, return the src of the last resource that is defined |
|
# (when we first recurse on a map, it puts an undefined resource |
|
# on the bottom because $self->{HERE} isn't defined yet, and we |
|
# want the src for the map anyhow) |
|
foreach my $item (@$stack) { |
|
if (defined($item)) { $res = $item; } |
|
} |
|
|
|
return ($res->link(),$res->shown_symb()); |
|
} |
|
|
|
# Convenience function: This separates the logic of how to create |
|
# the problem text strings ("Due: DATE", "Open: DATE", "Not yet assigned", |
|
# etc.) into a separate function. It takes a resource object as the |
|
# 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. |
|
|
|
sub getDescription { |
|
my $res = shift; |
|
my $part = shift; |
|
my $status = $res->status($part); |
|
|
|
if ($status == $res->NETWORK_FAILURE) { |
|
return &mt("Having technical difficulties; please check status later"); |
|
} |
|
if ($status == $res->NOTHING_SET) { |
|
return &mt("Not currently assigned."); |
|
} |
|
if ($status == $res->OPEN_LATER) { |
|
return "Open " . timeToHumanString($res->opendate($part),'start'); |
|
} |
|
if ($status == $res->OPEN) { |
|
if ($res->duedate($part)) { |
|
return &mt("Due")." " .timeToHumanString($res->duedate($part),'end'); |
|
} else { |
|
return &mt("Open, no due date"); |
|
} |
|
} |
|
if ($status == $res->PAST_DUE_ANSWER_LATER) { |
|
return &mt("Answer open")." " . timeToHumanString($res->answerdate($part),'start'); |
|
} |
|
if ($status == $res->PAST_DUE_NO_ANSWER) { |
|
return &mt("Was due")." " . timeToHumanString($res->duedate($part),'end'); |
|
} |
|
if (($status == $res->ANSWER_OPEN || $status == $res->PARTIALLY_CORRECT) |
|
&& $res->handgrade($part) ne 'yes') { |
|
return &mt("Answer available"); |
|
} |
|
if ($status == $res->EXCUSED) { |
|
return &mt("Excused by instructor"); |
|
} |
|
if ($status == $res->ATTEMPTED) { |
|
return &mt("Answer submitted, not yet graded"); |
|
} |
|
if ($status == $res->TRIES_LEFT) { |
|
my $tries = $res->tries($part); |
|
my $maxtries = $res->maxtries($part); |
|
my $triesString = ""; |
|
if ($tries && $maxtries) { |
|
$triesString = "<font size=\"-1\"><i>($tries of $maxtries tries used)</i></font>"; |
|
if ($maxtries > 1 && $maxtries - $tries == 1) { |
|
$triesString = "<b>$triesString</b>"; |
|
} |
|
} |
|
if ($res->duedate($part)) { |
|
return &mt("Due")." " . timeToHumanString($res->duedate($part),'end') . |
|
" $triesString"; |
|
} else { |
|
return &mt("No due date")." $triesString"; |
|
} |
|
} |
|
if ($status == $res->ANSWER_SUBMITTED) { |
|
return &mt('Answer submitted'); |
|
} |
|
} |
|
|
|
# Convenience function, so others can use it: Is the problem due in less then |
|
# 24 hours, and still can be done? |
|
|
|
sub dueInLessThan24Hours { |
|
my $res = shift; |
|
my $part = shift; |
|
my $status = $res->status($part); |
|
|
|
return ($status == $res->OPEN() || |
|
$status == $res->TRIES_LEFT()) && |
|
$res->duedate($part) && $res->duedate($part) < time()+(24*60*60) && |
|
$res->duedate($part) > time(); |
|
} |
|
|
|
# Convenience function, so others can use it: Is there only one try remaining for the |
|
# part, with more then one try to begin with, not due yet and still can be done? |
|
sub lastTry { |
|
my $res = shift; |
|
my $part = shift; |
|
|
|
my $tries = $res->tries($part); |
|
my $maxtries = $res->maxtries($part); |
|
return $tries && $maxtries && $maxtries > 1 && |
|
$maxtries - $tries == 1 && $res->duedate($part) && |
|
$res->duedate($part) > time(); |
|
} |
|
|
|
# This puts a human-readable name on the env variable. |
|
|
|
sub advancedUser { |
|
return $env{'request.role.adv'}; |
|
} |
|
|
|
|
|
# timeToHumanString takes a time number and converts it to a |
|
# human-readable representation, meant to be used in the following |
|
# manner: |
|
# print "Due $timestring" |
|
# print "Open $timestring" |
|
# print "Answer available $timestring" |
|
# Very, very, very, VERY English-only... goodness help a localizer on |
|
# this func... |
|
|
|
|
|
sub timeToHumanString { |
|
my ($time,$type,$format) = @_; |
|
|
|
# zero, '0' and blank are bad times |
|
if (!$time) { |
|
return &mt('never'); |
|
} |
|
unless (&Apache::lonlocal::current_language()=~/^en/) { |
|
return &Apache::lonlocal::locallocaltime($time); |
|
} |
|
my $now = time(); |
|
|
|
my @time = localtime($time); |
|
my @now = localtime($now); |
|
|
|
# Positive = future |
|
my $delta = $time - $now; |
|
|
|
my $minute = 60; |
|
my $hour = 60 * $minute; |
|
my $day = 24 * $hour; |
|
my $week = 7 * $day; |
|
my $inPast = 0; |
|
|
|
# Logic in comments: |
|
# Is it now? (extremely unlikely) |
|
if ( $delta == 0 ) { |
|
return "this instant"; |
|
} |
|
|
|
if ($delta < 0) { |
|
$inPast = 1; |
|
$delta = -$delta; |
|
} |
|
|
|
if ( $delta > 0 ) { |
|
|
|
my $tense = $inPast ? " ago" : ""; |
|
my $prefix = $inPast ? "" : "in "; |
|
|
|
# Less then a minute |
|
if ( $delta < $minute ) { |
|
if ($delta == 1) { return "${prefix}1 second$tense"; } |
|
return "$prefix$delta seconds$tense"; |
|
} |
|
|
|
# Less then an hour |
|
if ( $delta < $hour ) { |
|
# If so, use minutes |
|
my $minutes = floor($delta / 60); |
|
if ($minutes == 1) { return "${prefix}1 minute$tense"; } |
|
return "$prefix$minutes minutes$tense"; |
|
} |
|
|
|
# Is it less then 24 hours away? If so, |
|
# display hours + minutes |
|
if ( $delta < $hour * 24) { |
|
my $hours = floor($delta / $hour); |
|
my $minutes = floor(($delta % $hour) / $minute); |
|
my $hourString = "$hours hours"; |
|
my $minuteString = ", $minutes minutes"; |
|
if ($hours == 1) { |
|
$hourString = "1 hour"; |
|
} |
|
if ($minutes == 1) { |
|
$minuteString = ", 1 minute"; |
|
} |
|
if ($minutes == 0) { |
|
$minuteString = ""; |
|
} |
|
return "$prefix$hourString$minuteString$tense"; |
|
} |
|
|
|
# If there's a caller supplied format, use it. |
|
|
|
if($format ne '') { |
|
my $timeStr = strftime($format, localtime($time)); |
|
return $timeStr.&Apache::lonlocal::gettimezone($time); |
|
} |
|
|
|
# Less then 5 days away, display day of the week and |
|
# HH:MM |
|
|
|
if ( $delta < $day * 5 ) { |
|
my $timeStr = strftime("%A, %b %e at %I:%M %P", localtime($time)); |
|
$timeStr =~ s/12:00 am/00:00/; |
|
$timeStr =~ s/12:00 pm/noon/; |
|
return ($inPast ? "last " : "this ") . |
|
$timeStr.&Apache::lonlocal::gettimezone($time); |
|
} |
|
|
|
my $conjunction='on'; |
|
if ($type eq 'start') { |
|
$conjunction='at'; |
|
} elsif ($type eq 'end') { |
|
$conjunction='by'; |
|
} |
|
# Is it this year? |
|
if ( $time[5] == $now[5]) { |
|
# Return on Month Day, HH:MM meridian |
|
my $timeStr = strftime("$conjunction %A, %b %e at %I:%M %P", localtime($time)); |
|
$timeStr =~ s/12:00 am/00:00/; |
|
$timeStr =~ s/12:00 pm/noon/; |
|
return $timeStr.&Apache::lonlocal::gettimezone($time); |
|
} |
|
|
|
# Not this year, so show the year |
|
my $timeStr = strftime("$conjunction %A, %b %e %Y at %I:%M %P", localtime($time)); |
|
$timeStr =~ s/12:00 am/00:00/; |
|
$timeStr =~ s/12:00 pm/noon/; |
|
return $timeStr.&Apache::lonlocal::gettimezone($time); |
|
} |
|
} |
|
|
|
|
|
=pod |
=pod |
|
|
=head1 NAME |
=head1 NAME |
|
|
Apache::lonnavmap - Subroutines to handle and render the navigation |
Apache::lonnavmaps - Subroutines to handle and render the navigation |
maps |
|
|
|
=head1 SYNOPSIS |
=head1 SYNOPSIS |
|
|
|
Handles navigational maps. |
|
|
The main handler generates the navigational listing for the course, |
The main handler generates the navigational listing for the course, |
the other objects export this information in a usable fashion for |
the other objects export this information in a usable fashion for |
other modules. |
other modules. |
|
|
|
|
|
This is part of the LearningOnline Network with CAPA project |
|
described at http://www.lon-capa.org. |
|
|
|
|
=head1 OVERVIEW |
=head1 OVERVIEW |
|
|
X<lonnavmaps, overview> When a user enters a course, LON-CAPA examines the |
X<lonnavmaps, overview> When a user enters a course, LON-CAPA examines the |
Line 428 to compute due to the amount of data tha
|
Line 64 to compute due to the amount of data tha
|
processed. |
processed. |
|
|
Apache::lonnavmaps provides an object model for manipulating this |
Apache::lonnavmaps provides an object model for manipulating this |
information in a higher-level fashion then directly manipulating |
information in a higher-level fashion than directly manipulating |
the hash. It also provides access to several auxilary functions |
the hash. It also provides access to several auxilary functions |
that aren't necessarily stored in the Big Hash, but are a per- |
that aren't necessarily stored in the Big Hash, but are a per- |
resource sort of value, like whether there is any feedback on |
resource sort of value, like whether there is any feedback on |
Line 474 Apache::lonnavmaps::render({}).
|
Line 110 Apache::lonnavmaps::render({}).
|
=head2 Overview of Columns |
=head2 Overview of Columns |
|
|
The renderer will build an HTML table for the navmap and return |
The renderer will build an HTML table for the navmap and return |
it. The table is consists of several columns, and a row for each |
it. The table consists of several columns, and a row for each |
resource (or possibly each part). You tell the renderer how many |
resource (or possibly each part). You tell the renderer how many |
columns to create and what to place in each column, optionally using |
columns to create and what to place in each column, optionally using |
one or more of the prepared columns, and the renderer will assemble |
one or more of the prepared columns, and the renderer will assemble |
Line 605 instruct the renderer to render only a p
|
Line 241 instruct the renderer to render only a p
|
the source of the map you want to process, like |
the source of the map you want to process, like |
'/res/103/jerf/navmap.course.sequence'. |
'/res/103/jerf/navmap.course.sequence'. |
|
|
|
=item * B<include_top_level_map>: default: false |
|
|
|
If you need to include the top level map (meaning the course) in the |
|
rendered output set this to true |
|
|
=item * B<navmap>: default: constructs one from %env |
=item * B<navmap>: default: constructs one from %env |
|
|
A reference to a navmap, used only if an iterator is not passed in. If |
A reference to a navmap, used only if an iterator is not passed in. If |
Line 725 navmaps screen) uses this to display the
|
Line 366 navmaps screen) uses this to display the
|
|
|
=cut |
=cut |
|
|
|
|
|
=head1 SUBROUTINES |
|
|
|
=over |
|
|
|
=item update() |
|
|
|
=item addToFilter() |
|
|
|
Convenience functions: Returns a string that adds or subtracts |
|
the second argument from the first hash, appropriate for the |
|
query string that determines which folders to recurse on |
|
|
|
=item removeFromFilter() |
|
|
|
=item getLinkForResource() |
|
|
|
Convenience function: Given a stack returned from getStack on the iterator, |
|
return the correct src() value. |
|
|
|
=item getDescription() |
|
|
|
Convenience function: This separates the logic of how to create |
|
the problem text strings ("Due: DATE", "Open: DATE", "Not yet assigned", |
|
etc.) into a separate function. It takes a resource object as the |
|
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. |
|
|
|
=item dueInLessThan24Hours() |
|
|
|
Convenience function, so others can use it: Is the problem due in less than 24 hours, and still can be done? |
|
|
|
=item lastTry() |
|
|
|
Convenience function, so others can use it: Is there only one try remaining for the |
|
part, with more than one try to begin with, not due yet and still can be done? |
|
|
|
=item advancedUser() |
|
|
|
This puts a human-readable name on the env variable. |
|
|
|
=item timeToHumanString() |
|
|
|
timeToHumanString takes a time number and converts it to a |
|
human-readable representation, meant to be used in the following |
|
manner: |
|
|
|
=over 4 |
|
|
|
=item * print "Due $timestring" |
|
|
|
=item * print "Open $timestring" |
|
|
|
=item * print "Answer available $timestring" |
|
|
|
=back |
|
|
|
Very, very, very, VERY English-only... goodness help a localizer on |
|
this func... |
|
|
|
=item resource() |
|
|
|
returns 0 |
|
|
|
=item communication_status() |
|
|
|
returns 1 |
|
|
|
=item quick_status() |
|
|
|
returns 2 |
|
|
|
=item long_status() |
|
|
|
returns 3 |
|
|
|
=item part_status_summary() |
|
|
|
returns 4 |
|
|
|
=item render_resource() |
|
|
|
=item render_communication_status() |
|
|
|
=item render_quick_status() |
|
|
|
=item render_long_status() |
|
|
|
=item render_parts_summary_status() |
|
|
|
=item setDefault() |
|
|
|
=item cmp_title() |
|
|
|
=item render() |
|
|
|
=item add_linkitem() |
|
|
|
=item show_linkitems() |
|
|
|
=back |
|
|
|
=cut |
|
|
|
package Apache::lonnavmaps; |
|
|
|
use strict; |
|
use GDBM_File; |
|
use Apache::loncommon(); |
|
use Apache::lonenc(); |
|
use Apache::lonlocal; |
|
use Apache::lonnet; |
|
|
|
use POSIX qw (floor strftime); |
|
use Time::HiRes qw( gettimeofday tv_interval ); |
|
use LONCAPA; |
|
use DateTime(); |
|
|
|
# symbolic constants |
|
sub SYMB { return 1; } |
|
sub URL { return 2; } |
|
sub NOTHING { return 3; } |
|
|
|
# Some data |
|
|
|
my $resObj = "Apache::lonnavmaps::resource"; |
|
|
|
# Keep these mappings in sync with lonquickgrades, which usesthe colors |
|
# instead of the icons. |
|
my %statusIconMap = |
|
( |
|
$resObj->CLOSED => '', |
|
$resObj->OPEN => 'navmap.open.gif', |
|
$resObj->CORRECT => 'navmap.correct.gif', |
|
$resObj->PARTIALLY_CORRECT => 'navmap.partial.gif', |
|
$resObj->INCORRECT => 'navmap.wrong.gif', |
|
$resObj->ATTEMPTED => 'navmap.ellipsis.gif', |
|
$resObj->ERROR => '' |
|
); |
|
|
|
my %iconAltTags = #texthash does not work here |
|
( 'navmap.correct.gif' => 'Correct', |
|
'navmap.wrong.gif' => 'Incorrect', |
|
'navmap.open.gif' => 'Open', |
|
'navmap.partial.gif' => 'Partially Correct', |
|
'navmap.ellipsis.gif' => 'Attempted', |
|
); |
|
|
|
# Defines a status->color mapping, null string means don't color |
|
my %colormap = |
|
( $resObj->NETWORK_FAILURE => '', |
|
$resObj->CORRECT => '', |
|
$resObj->EXCUSED => '#3333FF', |
|
$resObj->PAST_DUE_ANSWER_LATER => '', |
|
$resObj->PAST_DUE_NO_ANSWER => '', |
|
$resObj->ANSWER_OPEN => '#006600', |
|
$resObj->OPEN_LATER => '', |
|
$resObj->TRIES_LEFT => '', |
|
$resObj->INCORRECT => '', |
|
$resObj->OPEN => '', |
|
$resObj->NOTHING_SET => '', |
|
$resObj->ATTEMPTED => '', |
|
$resObj->CREDIT_ATTEMPTED => '', |
|
$resObj->ANSWER_SUBMITTED => '', |
|
$resObj->PARTIALLY_CORRECT => '#006600' |
|
); |
|
# And a special case in the nav map; what to do when the assignment |
|
# is not yet done and due in less than 24 hours |
|
my $hurryUpColor = "#FF0000"; |
|
|
|
my $future_slots_checked = 0; |
|
my $future_slots = 0; |
|
|
|
sub addToFilter { |
|
my $hashIn = shift; |
|
my $addition = shift; |
|
my %hash = %$hashIn; |
|
$hash{$addition} = 1; |
|
|
|
return join (",", keys(%hash)); |
|
} |
|
|
|
sub removeFromFilter { |
|
my $hashIn = shift; |
|
my $subtraction = shift; |
|
my %hash = %$hashIn; |
|
|
|
delete $hash{$subtraction}; |
|
return join(",", keys(%hash)); |
|
} |
|
|
|
sub getLinkForResource { |
|
my $stack = shift; |
|
my $res; |
|
|
|
# Check to see if there are any pages in the stack |
|
foreach $res (@$stack) { |
|
if (defined($res)) { |
|
my $anchor; |
|
if ($res->is_page()) { |
|
foreach my $item (@$stack) { if (defined($item)) { $anchor = $item; } } |
|
$anchor=&escape($anchor->shown_symb()); |
|
return ($res->link(),$res->shown_symb(),$anchor); |
|
} |
|
# in case folder was skipped over as "only sequence" |
|
my ($map,$id,$src)=&Apache::lonnet::decode_symb($res->symb()); |
|
if ($map=~/\.page$/) { |
|
my $url=&Apache::lonnet::clutter($map); |
|
$anchor=&escape($res->shown_symb()); |
|
return ($url,$res->shown_symb(),$anchor); |
|
} |
|
} |
|
} |
|
|
|
# Failing that, return the src of the last resource that is defined |
|
# (when we first recurse on a map, it puts an undefined resource |
|
# on the bottom because $self->{HERE} isn't defined yet, and we |
|
# want the src for the map anyhow) |
|
foreach my $item (@$stack) { |
|
if (defined($item)) { $res = $item; } |
|
} |
|
|
|
if ($res) { |
|
return ($res->link(),$res->shown_symb()); |
|
} |
|
return; |
|
} |
|
|
|
|
|
|
|
sub getDescription { |
|
my $res = shift; |
|
my $part = shift; |
|
my $status = $res->status($part); |
|
|
|
my $open = $res->opendate($part); |
|
my $due = $res->duedate($part); |
|
my $answer = $res->answerdate($part); |
|
|
|
if ($status == $res->NETWORK_FAILURE) { |
|
return &mt("Having technical difficulties; please check status later"); |
|
} |
|
if ($status == $res->NOTHING_SET) { |
|
return &mt("Not currently assigned."); |
|
} |
|
if ($status == $res->OPEN_LATER) { |
|
return &mt("Open ") .timeToHumanString($open,'start'); |
|
} |
|
if ($res->simpleStatus($part) == $res->OPEN) { |
|
unless (&Apache::lonnet::allowed('mgr',$env{'request.course.id'})) { |
|
my ($slot_status,$slot_time,$slot_name)=$res->check_for_slot($part); |
|
if ($slot_status == $res->UNKNOWN) { |
|
return &mt('Reservation status unknown'); |
|
} elsif ($slot_status == $res->RESERVED) { |
|
return &mt('Reserved - ends [_1]', |
|
timeToHumanString($slot_time,'end')); |
|
} elsif ($slot_status == $res->RESERVED_LOCATION) { |
|
return &mt('Reserved - specific location(s) - ends [_1]', |
|
timeToHumanString($slot_time,'end')); |
|
} elsif ($slot_status == $res->RESERVED_LATER) { |
|
return &mt('Reserved - next open [_1]', |
|
timeToHumanString($slot_time,'start')); |
|
} elsif ($slot_status == $res->RESERVABLE) { |
|
return &mt('Reservable ending [_1]', |
|
timeToHumanString($slot_time,'end')); |
|
} elsif ($slot_status == $res->RESERVABLE_LATER) { |
|
return &mt('Reservable starting [_1]', |
|
timeToHumanString($slot_time,'start')); |
|
} elsif ($slot_status == $res->NOT_IN_A_SLOT) { |
|
return &mt('Reserve a time/place to work'); |
|
} elsif ($slot_status == $res->NOTRESERVABLE) { |
|
return &mt('Reservation not available'); |
|
} elsif ($slot_status == $res->WAITING_FOR_GRADE) { |
|
return &mt('Submission in grading queue'); |
|
} |
|
} |
|
} |
|
if ($status == $res->OPEN) { |
|
if ($due) { |
|
if ($res->is_practice()) { |
|
return &mt("Closes ")." " .timeToHumanString($due,'start'); |
|
} else { |
|
return &mt("Due")." " .timeToHumanString($due,'end'); |
|
} |
|
} else { |
|
return &mt("Open, no due date"); |
|
} |
|
} |
|
if ($status == $res->PAST_DUE_ANSWER_LATER) { |
|
return &mt("Answer open")." " .timeToHumanString($answer,'start'); |
|
} |
|
if ($status == $res->PAST_DUE_NO_ANSWER) { |
|
if ($res->is_practice()) { |
|
return &mt("Closed")." " . timeToHumanString($due,'start'); |
|
} else { |
|
return &mt("Was due")." " . timeToHumanString($due,'end'); |
|
} |
|
} |
|
if (($status == $res->ANSWER_OPEN || $status == $res->PARTIALLY_CORRECT) |
|
&& $res->handgrade($part) ne 'yes') { |
|
return &mt("Answer available"); |
|
} |
|
if ($status == $res->EXCUSED) { |
|
return &mt("Excused by instructor"); |
|
} |
|
if ($status == $res->ATTEMPTED) { |
|
if ($res->is_anonsurvey($part) || $res->is_survey($part)) { |
|
return &mt("Survey submission recorded"); |
|
} else { |
|
return &mt("Answer submitted, not yet graded"); |
|
} |
|
} |
|
if ($status == $res->CREDIT_ATTEMPTED) { |
|
if ($res->is_anonsurvey($part) || $res->is_survey($part)) { |
|
return &mt("Credit for survey submission"); |
|
} |
|
} |
|
if ($status == $res->TRIES_LEFT) { |
|
my $tries = $res->tries($part); |
|
my $maxtries = $res->maxtries($part); |
|
my $triesString = ""; |
|
if ($tries && $maxtries) { |
|
$triesString = '<font size="-1"><i>('.&mt('[_1] of [quant,_2,try,tries] used',$tries,$maxtries).')</i></font>'; |
|
if ($maxtries > 1 && $maxtries - $tries == 1) { |
|
$triesString = "<b>$triesString</b>"; |
|
} |
|
} |
|
if ($due) { |
|
return &mt("Due")." " . timeToHumanString($due,'end') . |
|
" $triesString"; |
|
} else { |
|
return &mt("No due date")." $triesString"; |
|
} |
|
} |
|
if ($status == $res->ANSWER_SUBMITTED) { |
|
return &mt('Answer submitted'); |
|
} |
|
} |
|
|
|
|
|
sub dueInLessThan24Hours { |
|
my $res = shift; |
|
my $part = shift; |
|
my $status = $res->status($part); |
|
|
|
return ($status == $res->OPEN() || |
|
$status == $res->TRIES_LEFT()) && |
|
$res->duedate($part) && $res->duedate($part) < time()+(24*60*60) && |
|
$res->duedate($part) > time(); |
|
} |
|
|
|
|
|
sub lastTry { |
|
my $res = shift; |
|
my $part = shift; |
|
|
|
my $tries = $res->tries($part); |
|
my $maxtries = $res->maxtries($part); |
|
return $tries && $maxtries && $maxtries > 1 && |
|
$maxtries - $tries == 1 && $res->duedate($part) && |
|
$res->duedate($part) > time(); |
|
} |
|
|
|
|
|
sub advancedUser { |
|
return $env{'request.role.adv'}; |
|
} |
|
|
|
sub timeToHumanString { |
|
my ($time,$type,$format) = @_; |
|
|
|
# zero, '0' and blank are bad times |
|
if (!$time) { |
|
return &mt('never'); |
|
} |
|
unless (&Apache::lonlocal::current_language()=~/^en/) { |
|
return &Apache::lonlocal::locallocaltime($time); |
|
} |
|
my $now = time(); |
|
|
|
# Positive = future |
|
my $delta = $time - $now; |
|
|
|
my $minute = 60; |
|
my $hour = 60 * $minute; |
|
my $day = 24 * $hour; |
|
my $week = 7 * $day; |
|
my $inPast = 0; |
|
|
|
# Logic in comments: |
|
# Is it now? (extremely unlikely) |
|
if ( $delta == 0 ) { |
|
return "this instant"; |
|
} |
|
|
|
if ($delta < 0) { |
|
$inPast = 1; |
|
$delta = -$delta; |
|
} |
|
|
|
if ( $delta > 0 ) { |
|
|
|
my $tense = $inPast ? " ago" : ""; |
|
my $prefix = $inPast ? "" : "in "; |
|
|
|
# Less than a minute |
|
if ( $delta < $minute ) { |
|
if ($delta == 1) { return "${prefix}1 second$tense"; } |
|
return "$prefix$delta seconds$tense"; |
|
} |
|
|
|
# Less than an hour |
|
if ( $delta < $hour ) { |
|
# If so, use minutes |
|
my $minutes = floor($delta / 60); |
|
if ($minutes == 1) { return "${prefix}1 minute$tense"; } |
|
return "$prefix$minutes minutes$tense"; |
|
} |
|
|
|
# Is it less than 24 hours away? If so, |
|
# display hours + minutes |
|
if ( $delta < $hour * 24) { |
|
my $hours = floor($delta / $hour); |
|
my $minutes = floor(($delta % $hour) / $minute); |
|
my $hourString = "$hours hours"; |
|
my $minuteString = ", $minutes minutes"; |
|
if ($hours == 1) { |
|
$hourString = "1 hour"; |
|
} |
|
if ($minutes == 1) { |
|
$minuteString = ", 1 minute"; |
|
} |
|
if ($minutes == 0) { |
|
$minuteString = ""; |
|
} |
|
return "$prefix$hourString$minuteString$tense"; |
|
} |
|
|
|
my $dt = DateTime->from_epoch(epoch => $time) |
|
->set_time_zone(&Apache::lonlocal::gettimezone()); |
|
|
|
# If there's a caller supplied format, use it. |
|
|
|
if ($format ne '') { |
|
my $timeStr = $dt->strftime($format); |
|
return $timeStr.' ('.$dt->time_zone_short_name().')'; |
|
} |
|
|
|
# Less than 5 days away, display day of the week and |
|
# HH:MM |
|
|
|
if ( $delta < $day * 5 ) { |
|
my $timeStr = $dt->strftime("%A, %b %e at %I:%M %P (%Z)"); |
|
$timeStr =~ s/12:00 am/00:00/; |
|
$timeStr =~ s/12:00 pm/noon/; |
|
return ($inPast ? "last " : "this ") . |
|
$timeStr; |
|
} |
|
|
|
my $conjunction='on'; |
|
if ($type eq 'start') { |
|
$conjunction='at'; |
|
} elsif ($type eq 'end') { |
|
$conjunction='by'; |
|
} |
|
# Is it this year? |
|
my $dt_now = DateTime->from_epoch(epoch => $now) |
|
->set_time_zone(&Apache::lonlocal::gettimezone()); |
|
if ( $dt->year() == $dt_now->year()) { |
|
# Return on Month Day, HH:MM meridian |
|
my $timeStr = $dt->strftime("$conjunction %A, %b %e at %I:%M %P (%Z)"); |
|
$timeStr =~ s/12:00 am/00:00/; |
|
$timeStr =~ s/12:00 pm/noon/; |
|
return $timeStr; |
|
} |
|
|
|
# Not this year, so show the year |
|
my $timeStr = |
|
$dt->strftime("$conjunction %A, %b %e %Y at %I:%M %P (%Z)"); |
|
$timeStr =~ s/12:00 am/00:00/; |
|
$timeStr =~ s/12:00 pm/noon/; |
|
return $timeStr; |
|
} |
|
} |
|
|
|
|
sub resource { return 0; } |
sub resource { return 0; } |
sub communication_status { return 1; } |
sub communication_status { return 1; } |
sub quick_status { return 2; } |
sub quick_status { return 2; } |
Line 739 sub render_resource {
|
Line 866 sub render_resource {
|
my $link = $params->{"resourceLink"}; |
my $link = $params->{"resourceLink"}; |
|
|
# The URL part is not escaped at this point, but the symb is... |
# The URL part is not escaped at this point, but the symb is... |
# The stuff to the left of the ? must have ' replaced by \' since |
|
# it will be quoted with ' in the href. |
|
|
|
my ($left,$right) = split(/\?/, $link); |
|
$link = $left.'?'.$right; |
|
|
|
my $src = $resource->src(); |
my $src = $resource->src(); |
my $it = $params->{"iterator"}; |
my $it = $params->{"iterator"}; |
Line 756 sub render_resource {
|
Line 878 sub render_resource {
|
my $location=&Apache::loncommon::lonhttpdurl("/adm/lonIcons"); |
my $location=&Apache::loncommon::lonhttpdurl("/adm/lonIcons"); |
# If this is a new branch, label it so |
# If this is a new branch, label it so |
if ($params->{'isNewBranch'}) { |
if ($params->{'isNewBranch'}) { |
$newBranchText = "<img src='$location/branch.gif' border='0' alt='Branch' />"; |
$newBranchText = "<img src='$location/branch.gif' alt=".mt('Branch')." />"; |
} |
} |
|
|
# links to open and close the folder |
# links to open and close the folder |
|
|
|
my $whitespace = $location.'/whitespace_21.gif'; |
my $linkopen = "<a href=\"$link\">"; |
my $linkopen = "<img src='$whitespace' alt='' />"."<a href=\"$link\">"; |
|
|
|
|
my $linkclose = "</a>"; |
my $linkclose = "</a>"; |
|
|
# Default icon: unknown page |
# Default icon: unknown page |
my $icon = "<img src='$location/unknown.gif' alt='' border='0' alt=' ' ' />"; |
my $icon = "<img class=\"LC_contentImage\" src='$location/unknown.gif' alt='' />"; |
|
|
if ($resource->is_problem()) { |
if ($resource->is_problem()) { |
if ($part eq '0' || $params->{'condensed'}) { |
if ($part eq '0' || $params->{'condensed'}) { |
$icon = '<img src="'.$location.'/'; |
$icon = '<img class="LC_contentImage" src="'.$location.'/'; |
if ($resource->is_task()) { |
if ($resource->is_task()) { |
$icon .= 'task.gif" alt="'.&mt('Task'); |
$icon .= 'task.gif" alt="'.&mt('Task'); |
} else { |
} else { |
$icon .= 'problem.gif" alt="'.&mt('Problem'); |
$icon .= 'problem.gif" alt="'.&mt('Problem'); |
} |
} |
$icon .='" border="0" />'; |
$icon .='" />'; |
} else { |
} else { |
$icon = $params->{'indentString'}; |
$icon = $params->{'indentString'}; |
} |
} |
} else { |
} else { |
$icon = "<img src='".&Apache::loncommon::icon($resource->src)."' alt=' ' border='0' />"; |
$icon = "<img class=\"LC_contentImage\" src='".&Apache::loncommon::icon($resource->src)."' alt='' />"; |
} |
} |
|
|
# Display the correct map icon to open or shut map |
# Display the correct map icon to open or shut map |
Line 793 sub render_resource {
|
Line 913 sub render_resource {
|
if ($it->{CONDITION}) { |
if ($it->{CONDITION}) { |
$nowOpen = !$nowOpen; |
$nowOpen = !$nowOpen; |
} |
} |
|
|
my $folderType = $resource->is_sequence() ? 'folder' : 'page'; |
my $folderType = $resource->is_sequence() ? 'folder' : 'page'; |
my $title=$resource->title; |
my $title=$resource->title; |
$title=~s/\"/\"/g; |
$title=~s/\"/\&qout;/g; |
if (!$params->{'resource_no_folder_link'}) { |
if (!$params->{'resource_no_folder_link'}) { |
$icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') . '.gif'; |
$icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') . '.gif'; |
$icon = "<img src='$location/$icon' alt=\"". |
$icon = "<img src='$location/arrow." . ($nowOpen ? 'closed' : 'open') . ".gif' alt='' />" |
($nowOpen ? &mt('Open Folder') : &mt('Close Folder')).' '.$title."\" border='0' />"; |
."<img class=\"LC_contentImage\" src='$location/$icon' alt=\"" |
|
.($nowOpen ? &mt('Open Folder') : &mt('Close Folder')).' '.$title."\" />"; |
$linkopen = "<a href=\"" . $params->{'url'} . '?' . |
$linkopen = "<a href=\"" . $params->{'url'} . '?' . |
$params->{'queryString'} . '&filter='; |
$params->{'queryString'} . '&filter='; |
$linkopen .= ($nowOpen xor $it->{CONDITION}) ? |
$linkopen .= ($nowOpen xor $it->{CONDITION}) ? |
Line 816 sub render_resource {
|
Line 936 sub render_resource {
|
|
|
} else { |
} else { |
# Don't allow users to manipulate folder |
# Don't allow users to manipulate folder |
$icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') . |
$icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') . '.gif'; |
'.nomanip.gif'; |
$icon = "<img class=\"LC_space\" src='$whitespace' alt='' />"."<img class=\"LC_contentImage\" src='$location/$icon' alt=\"".($nowOpen ? &mt('Open Folder') : &mt('Close Folder')).' '.$title."\" />"; |
$icon = "<img src='$location/$icon' alt=\"". |
|
($nowOpen ? &mt('Open Folder') : &mt('Close Folder')).' '.$title."\" border='0' />"; |
|
|
|
$linkopen = ""; |
$linkopen = ""; |
$linkclose = ""; |
$linkclose = ""; |
Line 832 sub render_resource {
|
Line 950 sub render_resource {
|
if (!$resource->condval()) { |
if (!$resource->condval()) { |
$nonLinkedText .= ' <i>('.&mt('conditionally hidden').')</i> '; |
$nonLinkedText .= ' <i>('.&mt('conditionally hidden').')</i> '; |
} |
} |
|
if (($resource->is_practice()) && ($resource->is_raw_problem())) { |
# We're done preparing and finally ready to start the rendering |
$nonLinkedText .=' <font color="green"><b>'.&mt('not graded').'</b></font>'; |
my $result = "<td align='left' valign='middle'>"; |
} |
|
|
|
# We're done preparing and finally ready to start the rendering |
|
my $result = '<td class="LC_middle">'; |
|
my $newfolderType = $resource->is_sequence() ? 'folder' : 'page'; |
|
|
my $indentLevel = $params->{'indentLevel'}; |
my $indentLevel = $params->{'indentLevel'}; |
if ($newBranchText) { $indentLevel--; } |
if ($newBranchText) { $indentLevel--; } |
|
|
Line 845 sub render_resource {
|
Line 967 sub render_resource {
|
} |
} |
|
|
# Decide what to display |
# Decide what to display |
|
|
$result .= "$newBranchText$linkopen$icon$linkclose"; |
$result .= "$newBranchText$linkopen$icon$linkclose"; |
|
|
my $curMarkerBegin = ''; |
my $curMarkerBegin = ''; |
Line 854 sub render_resource {
|
Line 975 sub render_resource {
|
# Is this the current resource? |
# Is this the current resource? |
if (!$params->{'displayedHereMarker'} && |
if (!$params->{'displayedHereMarker'} && |
$resource->symb() eq $params->{'here'} ) { |
$resource->symb() eq $params->{'here'} ) { |
$curMarkerBegin = '<font color="red" size="+2">></font>'; |
$curMarkerBegin = '<em>'; |
$curMarkerEnd = '<font color="red" size="+2"><</font>'; |
$curMarkerEnd = '</em>'; |
$params->{'displayedHereMarker'} = 1; |
$params->{'displayedHereMarker'} = 1; |
} |
} |
|
|
if ($resource->is_problem() && $part ne '0' && |
if ($resource->is_problem() && $part ne '0' && |
Line 871 sub render_resource {
|
Line 992 sub render_resource {
|
$nonLinkedText .= ' ('.&mt('[_1] parts', $resource->countParts()).')'; |
$nonLinkedText .= ' ('.&mt('[_1] parts', $resource->countParts()).')'; |
} |
} |
|
|
my $target; |
|
if ($env{'environment.remotenavmap'} eq 'on') { |
|
$target=' target="loncapaclient" '; |
|
} |
|
if (!$params->{'resource_nolink'} && !$resource->is_sequence() && !$resource->is_empty_sequence) { |
if (!$params->{'resource_nolink'} && !$resource->is_sequence() && !$resource->is_empty_sequence) { |
$result .= " $curMarkerBegin<a $target href=\"$link\">$title$partLabel</a>$curMarkerEnd $nonLinkedText</td>"; |
$result .= "$curMarkerBegin<a href=\"$link\">$title$partLabel</a>$curMarkerEnd$nonLinkedText</td>"; |
} else { |
} else { |
$result .= " $curMarkerBegin$title$partLabel$curMarkerEnd $nonLinkedText</td>"; |
$result .= "$curMarkerBegin$linkopen$title$partLabel</a>$curMarkerEnd$nonLinkedText</td>"; |
} |
} |
|
|
return $result; |
return $result; |
Line 889 sub render_communication_status {
|
Line 1006 sub render_communication_status {
|
my $discussionHTML = ""; my $feedbackHTML = ""; my $errorHTML = ""; |
my $discussionHTML = ""; my $feedbackHTML = ""; my $errorHTML = ""; |
|
|
my $link = $params->{"resourceLink"}; |
my $link = $params->{"resourceLink"}; |
my $target; |
my $linkopen = "<a href=\"$link\">"; |
if ($env{'environment.remotenavmap'} eq 'on') { |
|
$target=' target="loncapaclient" '; |
|
} |
|
my $linkopen = "<a $target href=\"$link\">"; |
|
my $linkclose = "</a>"; |
my $linkclose = "</a>"; |
my $location=&Apache::loncommon::lonhttpdurl("/adm/lonMisc"); |
my $location=&Apache::loncommon::lonhttpdurl("/adm/lonMisc"); |
if ($resource->hasDiscussion()) { |
if ($resource->hasDiscussion()) { |
$discussionHTML = $linkopen . |
$discussionHTML = $linkopen . |
'<img alt="'.&mt('New Discussion').'" border="0" src="'.$location.'/chat.gif" />' . |
'<img alt="'.&mt('New Discussion').'" src="'.$location.'/chat.gif" title="'.&mt('New Discussion').'"/>' . |
$linkclose; |
$linkclose; |
} |
} |
|
|
Line 906 sub render_communication_status {
|
Line 1019 sub render_communication_status {
|
my $feedback = $resource->getFeedback(); |
my $feedback = $resource->getFeedback(); |
foreach my $msgid (split(/\,/, $feedback)) { |
foreach my $msgid (split(/\,/, $feedback)) { |
if ($msgid) { |
if ($msgid) { |
$feedbackHTML .= ' <a '.$target.' href="/adm/email?display=' |
$feedbackHTML .= ' <a href="/adm/email?display=' |
. &escape($msgid) . '">' |
. &escape($msgid) . '">' |
. '<img alt="'.&mt('New Email').'" src="'.$location.'/feedback.gif" ' |
. '<img alt="'.&mt('New E-mail').'" src="'.$location.'/feedback.gif" title="'.&mt('New E-mail').'"/></a>'; |
. 'border="0" /></a>'; |
|
} |
} |
} |
} |
} |
} |
Line 921 sub render_communication_status {
|
Line 1033 sub render_communication_status {
|
last if ($errorcount>=10); # Only output 10 bombs maximum |
last if ($errorcount>=10); # Only output 10 bombs maximum |
if ($msgid) { |
if ($msgid) { |
$errorcount++; |
$errorcount++; |
$errorHTML .= ' <a '.$target.' href="/adm/email?display=' |
$errorHTML .= ' <a href="/adm/email?display=' |
. &escape($msgid) . '">' |
. &escape($msgid) . '">' |
. '<img alt="'.&mt('New Error').'" src="'.$location.'/bomb.gif" ' |
. '<img alt="'.&mt('New Error').'" src="'.$location.'/bomb.gif" title="'.&mt('New Error').'"/></a>'; |
. 'border="0" /></a>'; |
|
} |
} |
} |
} |
} |
} |
Line 932 sub render_communication_status {
|
Line 1043 sub render_communication_status {
|
if ($params->{'multipart'} && $part != '0') { |
if ($params->{'multipart'} && $part != '0') { |
$discussionHTML = $feedbackHTML = $errorHTML = ''; |
$discussionHTML = $feedbackHTML = $errorHTML = ''; |
} |
} |
|
return "<td class=\"LC_middle\">$discussionHTML$feedbackHTML$errorHTML </td>"; |
return "<td width=\"75\" align=\"left\" valign=\"middle\">$discussionHTML$feedbackHTML$errorHTML </td>"; |
|
|
|
} |
} |
sub render_quick_status { |
sub render_quick_status { |
Line 943 sub render_quick_status {
|
Line 1053 sub render_quick_status {
|
$params->{'multipart'} && $part eq "0"; |
$params->{'multipart'} && $part eq "0"; |
|
|
my $link = $params->{"resourceLink"}; |
my $link = $params->{"resourceLink"}; |
my $target; |
my $linkopen = "<a href=\"$link\">"; |
if ($env{'environment.remotenavmap'} eq 'on') { |
|
$target=' target="loncapaclient" '; |
|
} |
|
my $linkopen = "<a $target href=\"$link\">"; |
|
my $linkclose = "</a>"; |
my $linkclose = "</a>"; |
|
|
|
$result .= '<td class="LC_middle">'; |
if ($resource->is_problem() && |
if ($resource->is_problem() && |
!$firstDisplayed) { |
!$firstDisplayed) { |
|
|
my $icon = $statusIconMap{$resource->simpleStatus($part)}; |
my $icon = $statusIconMap{$resource->simpleStatus($part)}; |
my $alt = $iconAltTags{$icon}; |
my $alt = $iconAltTags{$icon}; |
if ($icon) { |
if ($icon) { |
my $location= |
my $location= |
&Apache::loncommon::lonhttpdurl("/adm/lonIcons/$icon"); |
&Apache::loncommon::lonhttpdurl("/adm/lonIcons/$icon"); |
$result .= "<td valign='middle' width='50' align='right'>$linkopen<img width='25' height='25' src='$location' border='0' alt='$alt' />$linkclose</td>\n"; |
$result .= $linkopen.'<img src="'.$location.'" alt="'.&mt($alt).'" title="'.&mt($alt).'" />'.$linkclose; |
} else { |
} else { |
$result .= "<td width='30'> </td>\n"; |
$result .= " "; |
} |
} |
} else { # not problem, no icon |
} else { # not problem, no icon |
$result .= "<td width='30'> </td>\n"; |
$result .= " "; |
} |
} |
|
$result .= "</td>\n"; |
return $result; |
return $result; |
} |
} |
sub render_long_status { |
sub render_long_status { |
my ($resource, $part, $params) = @_; |
my ($resource, $part, $params) = @_; |
my $result = "<td align='right' valign='middle'>\n"; |
my $result = '<td class="LC_middle LC_right">'; |
my $firstDisplayed = !$params->{'condensed'} && |
my $firstDisplayed = !$params->{'condensed'} && |
$params->{'multipart'} && $part eq "0"; |
$params->{'multipart'} && $part eq "0"; |
|
|
my $color; |
my $color; |
if ($resource->is_problem()) { |
if ($resource->is_problem() || $resource->is_practice()) { |
$color = $colormap{$resource->status}; |
$color = $colormap{$resource->status}; |
|
|
if (dueInLessThan24Hours($resource, $part) || |
if (dueInLessThan24Hours($resource, $part) || |
Line 985 sub render_long_status {
|
Line 1091 sub render_long_status {
|
} |
} |
|
|
if ($resource->kind() eq "res" && |
if ($resource->kind() eq "res" && |
$resource->is_problem() && |
($resource->is_problem() || $resource->is_practice()) && |
!$firstDisplayed) { |
!$firstDisplayed) { |
if ($color) {$result .= "<font color=\"$color\"><b>"; } |
if ($color) {$result .= "<font color=\"$color\"><b>"; } |
$result .= getDescription($resource, $part); |
$result .= getDescription($resource, $part); |
if ($color) {$result .= "</b></font>"; } |
if ($color) {$result .= "</b></font>"; } |
} |
} |
if ($resource->is_map() && advancedUser() && $resource->randompick()) { |
if ($resource->is_map() && &advancedUser() && $resource->randompick()) { |
$result .= '(randomly select ' . $resource->randompick() .')'; |
$result .= &mt('(randomly select [_1])', $resource->randompick()); |
|
} |
|
if ($resource->is_map() && &advancedUser() && $resource->randomorder()) { |
|
$result .= &mt('(randomly ordered)'); |
} |
} |
|
|
# Debugging code |
# Debugging code |
Line 1027 my %statusStrings =
|
Line 1136 my %statusStrings =
|
); |
); |
my @statuses = ($resObj->CORRECT, $resObj->ATTEMPTED, $resObj->INCORRECT, $resObj->OPEN, $resObj->CLOSED, $resObj->ERROR); |
my @statuses = ($resObj->CORRECT, $resObj->ATTEMPTED, $resObj->INCORRECT, $resObj->OPEN, $resObj->CLOSED, $resObj->ERROR); |
|
|
use Data::Dumper; |
|
sub render_parts_summary_status { |
sub render_parts_summary_status { |
my ($resource, $part, $params) = @_; |
my ($resource, $part, $params) = @_; |
if (!$resource->is_problem() && !$resource->contains_problem) { return '<td></td>'; } |
if (!$resource->is_problem() && !$resource->contains_problem) { return '<td></td>'; } |
Line 1155 sub render {
|
Line 1263 sub render {
|
if (!defined($navmap)) { |
if (!defined($navmap)) { |
$navmap = Apache::lonnavmaps::navmap->new(); |
$navmap = Apache::lonnavmaps::navmap->new(); |
if (!defined($navmap)) { |
if (!defined($navmap)) { |
# no londer in course |
# no longer in course |
return '<span class="LC_error">'.&mt('No course selected').'</span><br /> |
return '<span class="LC_error">'.&mt('No course selected').'</span><br /> |
<a href="/adm/roles">'.&mt('Select a course').'</a><br />'; |
<a href="/adm/roles">'.&mt('Select a course').'</a><br />'; |
} |
} |
Line 1223 sub render {
|
Line 1331 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(); |
$navmap = Apache::lonnavmaps::navmap->new(); |
|
if (!defined($navmap)) { |
|
# no longer in course |
|
return '<span class="LC_error">'.&mt('No course selected').'</span><br /> |
|
<a href="/adm/roles">'.&mt('Select a course').'</a><br />'; |
|
} |
} |
} |
|
|
# See if we're being passed a specific map |
# See if we're being passed a specific map |
Line 1234 sub render {
|
Line 1347 sub render {
|
|
|
$args->{'iterator'} = $it = $navmap->getIterator($firstResource, $finishResource, $filterHash, $condition); |
$args->{'iterator'} = $it = $navmap->getIterator($firstResource, $finishResource, $filterHash, $condition); |
} else { |
} else { |
$args->{'iterator'} = $it = $navmap->getIterator(undef, undef, $filterHash, $condition); |
$args->{'iterator'} = $it = $navmap->getIterator(undef, undef, $filterHash, $condition,undef,$args->{'include_top_level_map'}); |
} |
} |
} |
} |
|
|
Line 1267 sub render {
|
Line 1380 sub render {
|
my $printKey = $args->{'printKey'}; |
my $printKey = $args->{'printKey'}; |
my $printCloseAll = $args->{'printCloseAll'}; |
my $printCloseAll = $args->{'printCloseAll'}; |
if (!defined($printCloseAll)) { $printCloseAll = 1; } |
if (!defined($printCloseAll)) { $printCloseAll = 1; } |
|
|
# Print key? |
# Print key? |
if ($printKey) { |
if ($printKey) { |
$result .= '<table border="0" cellpadding="2" cellspacing="0">'; |
$result .= '<table border="0" cellpadding="2" cellspacing="0">'; |
my $date=localtime; |
|
$result.='<tr><td align="right" valign="bottom">Key: </td>'; |
$result.='<tr><td align="right" valign="bottom">Key: </td>'; |
my $location=&Apache::loncommon::lonhttpdurl("/adm/lonMisc"); |
my $location=&Apache::loncommon::lonhttpdurl("/adm/lonMisc"); |
if ($navmap->{LAST_CHECK}) { |
if ($navmap->{LAST_CHECK}) { |
$result .= |
$result .= |
'<img src="'.$location.'/chat.gif"> '.&mt('New discussion since').' '. |
'<img src="'.$location.'/chat.gif" alt="" /> '.&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="'.$location.'/feedback.gif"> '.&mt('New message (click to open)').'<p>'. |
'<img src="'.$location.'/feedback.gif" alt="" /> '.&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="'.$location.'/chat.gif"> '.&mt('Discussions').'</td><td align="center" valign="bottom">'. |
'<img src="'.$location.'/chat.gif" alt="" /> '.&mt('Discussions').'</td><td align="center" valign="bottom">'. |
' <img src="'.$location.'/feedback.gif"> '.&mt('New message (click to open)'). |
' <img src="'.$location.'/feedback.gif" alt="" /> '.&mt('New message (click to open)'). |
'</td>'; |
'</td>'; |
} |
} |
|
|
Line 1294 sub render {
|
Line 1406 sub render {
|
if ($printCloseAll && !$args->{'resource_no_folder_link'}) { |
if ($printCloseAll && !$args->{'resource_no_folder_link'}) { |
my ($link,$text); |
my ($link,$text); |
if ($condition) { |
if ($condition) { |
$link='"navmaps?condition=0&filter=&'.$queryString. |
$link='navmaps?condition=0&filter=&'.$queryString. |
'&here='.&escape($here).'"'; |
'&here='.&escape($here); |
$text='Close all folders'; |
$text='Close all folders'; |
} else { |
} else { |
$link='"navmaps?condition=1&filter=&'.$queryString. |
$link='navmaps?condition=1&filter=&'.$queryString. |
'&here='.&escape($here).'"'; |
'&here='.&escape($here); |
$text='Open all folders'; |
$text='Open all folders'; |
} |
} |
|
if ($env{'form.register'}) { |
|
$link .= '&register='.$env{'form.register'}; |
|
} |
if ($args->{'caller'} eq 'navmapsdisplay') { |
if ($args->{'caller'} eq 'navmapsdisplay') { |
&add_linkitem($args->{'linkitems'},'changefolder', |
&add_linkitem($args->{'linkitems'},'changefolder', |
'location.href='.$link,$text); |
"location.href='$link'",$text); |
} else { |
} else { |
$result.='<a href='.$link.'>'.&mt($text).'</a>'; |
$result.= '<a href="'.$link.'">'.&mt($text).'</a>'; |
} |
} |
$result .= "\n"; |
$result .= "\n"; |
} |
} |
Line 1322 sub render {
|
Line 1437 sub render {
|
<input type="hidden" name="navurl" value="$ENV{'QUERY_STRING'}" /> |
<input type="hidden" name="navurl" value="$ENV{'QUERY_STRING'}" /> |
<input type="hidden" name="navtime" value="$time" /> |
<input type="hidden" name="navtime" value="$time" /> |
END |
END |
|
if ($env{'form.register'}) { |
|
$result .= '<input type="hidden" name="register" value="'.$env{'form.register'}.'" />'; |
|
} |
if ($args->{'sort'} eq 'discussion') { |
if ($args->{'sort'} eq 'discussion') { |
my $totdisc = 0; |
my $totdisc = 0; |
my $haveDisc = ''; |
my $haveDisc = ''; |
Line 1346 END
|
Line 1464 END
|
if ($args->{'caller'} eq 'navmapsdisplay') { |
if ($args->{'caller'} eq 'navmapsdisplay') { |
$result .= '<table><tr><td>'. |
$result .= '<table><tr><td>'. |
&Apache::loncommon::help_open_menu('Navigation Screen','Navigation_Screen',undef,'RAT').'</td>'; |
&Apache::loncommon::help_open_menu('Navigation Screen','Navigation_Screen',undef,'RAT').'</td>'; |
if ($env{'environment.remotenavmap'} ne 'on') { |
|
$result .= '<td> </td>'; |
$result .= '<td> </td>'; |
} else { |
$result.='<td class="LC_middle">'.&mt('Tools:').'</td>'; |
$result .= '</tr><tr>'; |
$result.=&show_linkitems_toolbar($args->{'linkitems'}); |
} |
|
$result.=&show_linkitems($args->{'linkitems'}); |
|
if ($args->{'sort_html'}) { |
if ($args->{'sort_html'}) { |
if ($env{'environment.remotenavmap'} ne 'on') { |
$result.='<td> </td><td> </td><td> </td>'. |
$result.='<td> </td><td> </td><td> </td>'. |
'<td align="right">'.$args->{'sort_html'}.'</td></tr>'; |
'<td align="right">'.$args->{'sort_html'}.'</td></tr>'; |
} |
} else { |
|
$result.='</tr><tr><td align="left"><br />'. |
|
$args->{'sort_html'}.'</td></tr>'; |
|
} |
|
} |
|
$result .= '</table>'; |
$result .= '</table>'; |
} elsif ($args->{'sort_html'}) { |
} elsif ($args->{'sort_html'}) { |
$result.=$args->{'sort_html'}; |
$result.=$args->{'sort_html'}; |
} |
} |
|
|
$result .= "<br />\n"; |
#$result .= "<br />\n"; |
if ($r) { |
if ($r) { |
$r->print($result); |
$r->print($result); |
$r->rflush(); |
$r->rflush(); |
$result = ""; |
$result = ""; |
} |
} |
# End parameter setting |
# End parameter setting |
|
|
|
$result .= "<br />\n"; |
|
|
# Data |
# Data |
$result .= '<table cellspacing="0" cellpadding="3" border="0" bgcolor="#FFFFFF">' ."\n"; |
$result.=&Apache::loncommon::start_data_table("LC_tableOfContent"); |
|
|
my $res = "Apache::lonnavmaps::resource"; |
my $res = "Apache::lonnavmaps::resource"; |
my %condenseStatuses = |
my %condenseStatuses = |
( $res->NETWORK_FAILURE => 1, |
( $res->NETWORK_FAILURE => 1, |
$res->NOTHING_SET => 1, |
$res->NOTHING_SET => 1, |
$res->CORRECT => 1 ); |
$res->CORRECT => 1 ); |
my @backgroundColors = ("#FFFFFF", "#F6F6F6"); |
|
|
|
# Shared variables |
# Shared variables |
$args->{'counter'} = 0; # counts the rows |
$args->{'counter'} = 0; # counts the rows |
$args->{'indentLevel'} = 0; |
$args->{'indentLevel'} = 0; |
$args->{'isNewBranch'} = 0; |
$args->{'isNewBranch'} = 0; |
$args->{'condensed'} = 0; |
$args->{'condensed'} = 0; |
my $location= |
|
&Apache::loncommon::lonhttpdurl("/adm/lonIcons/whitespace1.gif"); |
my $location = &Apache::loncommon::lonhttpdurl("/adm/lonIcons/whitespace_21.gif"); |
$args->{'indentString'} = setDefault($args->{'indentString'}, "<img src='$location' width='25' height='1' alt=' ' border='0' />"); |
$args->{'indentString'} = setDefault($args->{'indentString'}, "<img src='$location' alt='' />"); |
$args->{'displayedHereMarker'} = 0; |
$args->{'displayedHereMarker'} = 0; |
|
|
# If we're suppressing empty sequences, look for them here. Use DFS for speed, |
# If we're suppressing empty sequences, look for them here. Use DFS for speed, |
Line 1609 END
|
Line 1721 END
|
if (defined($anchor)) { $anchor='#'.$anchor; } |
if (defined($anchor)) { $anchor='#'.$anchor; } |
my $srcHasQuestion = $src =~ /\?/; |
my $srcHasQuestion = $src =~ /\?/; |
$args->{"resourceLink"} = $src. |
$args->{"resourceLink"} = $src. |
($srcHasQuestion?'&':'?') . |
($srcHasQuestion?'&':'?') . |
'symb=' . &escape($symb).$anchor; |
'symb=' . &escape($symb).$anchor; |
} |
} |
# Now, we've decided what parts to show. Loop through them and |
# Now, we've decided what parts to show. Loop through them and |
# show them. |
# show them. |
foreach my $part (@parts) { |
foreach my $part (@parts) { |
$rownum ++; |
$rownum ++; |
my $backgroundColor = $backgroundColors[$rownum % scalar(@backgroundColors)]; |
|
|
|
$result .= " <tr bgcolor='$backgroundColor'>\n"; |
$result .= &Apache::loncommon::start_data_table_row(); |
|
|
# Set up some data about the parts that the cols might want |
# Set up some data about the parts that the cols might want |
my $filter = $it->{FILTER}; |
my $filter = $it->{FILTER}; |
Line 1644 END
|
Line 1755 END
|
} |
} |
$result .= $colHTML . "\n"; |
$result .= $colHTML . "\n"; |
} |
} |
$result .= " </tr>\n"; |
$result .= &Apache::loncommon::end_data_table_row(); |
$args->{'isNewBranch'} = 0; |
$args->{'isNewBranch'} = 0; |
} |
} |
|
|
Line 1672 END
|
Line 1783 END
|
# 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 .= " |
$result .= &Apache::lonhtmlcommon::scripttag(" |
<script> |
|
if (location.href.indexOf('#curloc')==-1) { |
if (location.href.indexOf('#curloc')==-1) { |
setTimeout(\"location += '#curloc';\", 0) |
setTimeout(\"location += '#curloc';\", 0) |
} |
} |
</script>"; |
"); |
} |
} |
|
|
$result .= "</table>"; |
$result.=&Apache::loncommon::end_data_table(); |
|
|
if ($r) { |
if ($r) { |
$r->print($result); |
$r->print($result); |
$result = ""; |
$result = ""; |
Line 1697 sub add_linkitem {
|
Line 1807 sub add_linkitem {
|
$$linkitems{$name}{'text'}=&mt($text); |
$$linkitems{$name}{'text'}=&mt($text); |
} |
} |
|
|
sub show_linkitems { |
sub show_linkitems_toolbar { |
my ($linkitems)=@_; |
my ($linkitems,$condition)=@_; |
my @linkorder = ("blank","launchnav","closenav","firsthomework", |
my @linkorder = ('firsthomework','everything','uncompleted', |
"everything","uncompleted","changefolder","clearbubbles"); |
'changefolder','clearbubbles'); |
|
my $result .='<td align="left">'."\n". |
my $result .= (<<ENDBLOCK); |
'<span class="LC_nobreak">'."\n". |
<td align="left"> |
'<ul id="LC_toolbar">'; |
<script type="text/javascript"> |
|
function changeNavDisplay () { |
|
var navchoice = document.linkitems.toplink[document.linkitems.toplink.selectedIndex].value; |
|
ENDBLOCK |
|
foreach my $link (@linkorder) { |
foreach my $link (@linkorder) { |
$result.= "if (navchoice == '$link') {". |
my $link_id = 'LC_content_toolbar_'.$link; |
$linkitems->{$link}{'cmd'}."}\n"; |
if (defined($linkitems->{$link})) { |
} |
if ($linkitems->{$link}{'text'} ne '') { |
$result.='} |
$linkitems->{$link}{'cmd'}=~s/"/'/g; |
</script> |
if ($linkitems->{$link}{'cmd'}) { |
<form name="linkitems" method="post"> |
if ($link eq 'changefolder') { |
<nobr><select name="toplink">'."\n"; |
if ($condition) { |
foreach my $link (@linkorder) { |
$link_id='LC_content_toolbar_changefolder_toggled'; |
if (defined($linkitems->{$link})) { |
} else { |
if ($linkitems->{$link}{'text'} ne '') { |
$link_id='LC_content_toolbar_changefolder'; |
$result .= ' <option value="'.$link.'">'. |
} |
$linkitems->{$link}{'text'}."</option>\n"; |
} |
} |
$result .= '<li><a href="#" '. |
} |
'onclick="'.$linkitems->{$link}{'cmd'}.'" '. |
|
'id="'.$link_id.'" '. |
|
'class="LC_toolbarItem" '. |
|
'title="'.$linkitems->{$link}{'text'}.'">'. |
|
'</a></li>'."\n"; |
|
} |
|
} |
|
} |
} |
} |
$result .= '</select> <input type="button" name="chgnav" |
$result .= '</ul>'. |
value="Go" onClick="javascript:changeNavDisplay()" /> |
'</span></td>'."\n"; |
</nobr></form></td>'."\n"; |
|
|
|
return $result; |
return $result; |
} |
} |
|
|
|
|
1; |
1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
package Apache::lonnavmaps::navmap; |
package Apache::lonnavmaps::navmap; |
|
|
=pod |
=pod |
Line 1802 See iterator documentation below.
|
Line 1922 See iterator documentation below.
|
use strict; |
use strict; |
use GDBM_File; |
use GDBM_File; |
use Apache::lonnet; |
use Apache::lonnet; |
|
use LONCAPA; |
|
|
sub new { |
sub new { |
# magic invocation to create a class instance |
# magic invocation to create a class instance |
Line 1914 sub generate_email_discuss_status {
|
Line 2035 sub generate_email_discuss_status {
|
|
|
foreach my $msgid (@keys) { |
foreach my $msgid (@keys) { |
if ((!$emailstatus{$msgid}) || ($emailstatus{$msgid} eq 'new')) { |
if ((!$emailstatus{$msgid}) || ($emailstatus{$msgid} eq 'new')) { |
my $plain= |
my ($sendtime,$shortsubj,$fromname,$fromdomain,$status,$fromcid, |
&LONCAPA::unescape(&LONCAPA::unescape($msgid)); |
$symb,$error) = &Apache::lonmsg::unpackmsgid($msgid); |
if ($plain=~/ \[([^\]]+)\]\:/) { |
&Apache::lonenc::check_decrypt(\$symb); |
my $url=$1; |
if (($fromcid ne '') && ($fromcid ne $cid)) { |
if ($plain=~/\:Error \[/) { |
next; |
$error{$url}.=','.$msgid; |
} |
} else { |
if (defined($symb)) { |
$feedback{$url}.=','.$msgid; |
if (defined($error) && $error == 1) { |
} |
$error{$symb}.=','.$msgid; |
} |
} else { |
|
$feedback{$symb}.=','.$msgid; |
|
} |
|
} else { |
|
my $plain= |
|
&LONCAPA::unescape(&LONCAPA::unescape($msgid)); |
|
if ($plain=~/ \[([^\]]+)\]\:/) { |
|
my $url=$1; |
|
if ($plain=~/\:Error \[/) { |
|
$error{$url}.=','.$msgid; |
|
} else { |
|
$feedback{$url}.=','.$msgid; |
|
} |
|
} |
|
} |
} |
} |
} |
} |
|
|
#url's of resources that have feedbacks |
#symbs of resources that have feedbacks (will be urls pre-2.3) |
$self->{FEEDBACK} = \%feedback; |
$self->{FEEDBACK} = \%feedback; |
#or errors |
#or errors (will be urls pre 2.3) |
$self->{ERROR_MSG} = \%error; |
$self->{ERROR_MSG} = \%error; |
$self->{DISCUSSION_TIME} = \%discussiontime; |
$self->{DISCUSSION_TIME} = \%discussiontime; |
$self->{EMAIL_STATUS} = \%emailstatus; |
$self->{EMAIL_STATUS} = \%emailstatus; |
Line 2043 sub discussion_info {
|
Line 2178 sub discussion_info {
|
|
|
my $ressymb = $self->wrap_symb($symb); |
my $ressymb = $self->wrap_symb($symb); |
# keys used to store bulletinboard postings use 'unwrapped' symb. |
# keys used to store bulletinboard postings use 'unwrapped' symb. |
my $discsymb = $self->unwrap_symb($ressymb); |
my $discsymb = &escape($self->unwrap_symb($ressymb)); |
my $version = $self->{DISCUSSION_DATA}{'version:'.$discsymb}; |
my $version = $self->{DISCUSSION_DATA}{'version:'.$discsymb}; |
if (!$version) { return; } |
if (!$version) { return; } |
|
|
Line 2116 sub unwrap_symb {
|
Line 2251 sub unwrap_symb {
|
sub getFeedback { |
sub getFeedback { |
my $self = shift; |
my $self = shift; |
my $symb = shift; |
my $symb = shift; |
|
my $source = shift; |
|
|
$self->generate_email_discuss_status(); |
$self->generate_email_discuss_status(); |
|
|
if (!defined($self->{FEEDBACK})) { return ""; } |
if (!defined($self->{FEEDBACK})) { return ""; } |
|
|
return $self->{FEEDBACK}->{$symb}; |
my $feedback; |
|
if ($self->{FEEDBACK}->{$symb}) { |
|
$feedback = $self->{FEEDBACK}->{$symb}; |
|
if ($self->{FEEDBACK}->{$source}) { |
|
$feedback .= ','.$self->{FEEDBACK}->{$source}; |
|
} |
|
} else { |
|
if ($self->{FEEDBACK}->{$source}) { |
|
$feedback = $self->{FEEDBACK}->{$source}; |
|
} |
|
} |
|
return $feedback; |
} |
} |
|
|
# Private method: Get the errors for that resource (by source). |
# Private method: Get the errors for that resource (by source). |
sub getErrors { |
sub getErrors { |
my $self = shift; |
my $self = shift; |
|
my $symb = shift; |
my $src = shift; |
my $src = shift; |
|
|
$self->generate_email_discuss_status(); |
$self->generate_email_discuss_status(); |
|
|
if (!defined($self->{ERROR_MSG})) { return ""; } |
if (!defined($self->{ERROR_MSG})) { return ""; } |
return $self->{ERROR_MSG}->{$src}; |
|
|
my $errors; |
|
if ($self->{ERROR_MSG}->{$symb}) { |
|
$errors = $self->{ERROR_MSG}->{$symb}; |
|
if ($self->{ERROR_MSG}->{$src}) { |
|
$errors .= ','.$self->{ERROR_MSG}->{$src}; |
|
} |
|
} else { |
|
if ($self->{ERROR_MSG}->{$src}) { |
|
$errors = $self->{ERROR_MSG}->{$src}; |
|
} |
|
} |
|
return $errors; |
} |
} |
|
|
=pod |
=pod |
Line 2149 resource object.
|
Line 2309 resource object.
|
Based on the symb of the resource, get a resource object for that |
Based on the symb of the resource, get a resource object for that |
resource. This is one of the proper ways to get a resource object. |
resource. This is one of the proper ways to get a resource object. |
|
|
=item * B<getMapByMapPc>(map_pc): |
=item * B<getByMapPc>(map_pc): |
|
|
Based on the map_pc of the resource, get a resource object for |
Based on the map_pc of the resource, get a resource object for |
the given map. This is one of the proper ways to get a resource object. |
the given map. This is one of the proper ways to get a resource object. |
Line 2158 the given map. This is one of the proper
|
Line 2318 the given map. This is one of the proper
|
|
|
# The strategy here is to cache the resource objects, and only construct them |
# The strategy here is to cache the resource objects, and only construct them |
# as we use them. The real point is to prevent reading any more from the tied |
# as we use them. The real point is to prevent reading any more from the tied |
# hash then we have to, which should hopefully alleviate speed problems. |
# hash than we have to, which should hopefully alleviate speed problems. |
|
|
sub getById { |
sub getById { |
my $self = shift; |
my $self = shift; |
Line 2232 sub finishResource {
|
Line 2392 sub finishResource {
|
# the actual lookup; parmval caches the results. |
# the actual lookup; parmval caches the results. |
sub parmval { |
sub parmval { |
my $self = shift; |
my $self = shift; |
my ($what,$symb)=@_; |
my ($what,$symb,$recurse)=@_; |
my $hashkey = $what."|||".$symb; |
my $hashkey = $what."|||".$symb; |
|
|
if (defined($self->{PARM_CACHE}->{$hashkey})) { |
if (defined($self->{PARM_CACHE}->{$hashkey})) { |
return $self->{PARM_CACHE}->{$hashkey}; |
if (ref($self->{PARM_CACHE}->{$hashkey}) eq 'ARRAY') { |
|
if (defined($self->{PARM_CACHE}->{$hashkey}->[0])) { |
|
if (wantarray) { |
|
return @{$self->{PARM_CACHE}->{$hashkey}}; |
|
} else { |
|
return $self->{PARM_CACHE}->{$hashkey}->[0]; |
|
} |
|
} |
|
} else { |
|
return $self->{PARM_CACHE}->{$hashkey}; |
|
} |
} |
} |
|
my $result = $self->parmval_real($what, $symb, $recurse); |
my $result = $self->parmval_real($what, $symb); |
|
$self->{PARM_CACHE}->{$hashkey} = $result; |
$self->{PARM_CACHE}->{$hashkey} = $result; |
return $result; |
if (wantarray) { |
|
return @{$result}; |
|
} |
|
return $result->[0]; |
} |
} |
|
|
sub parmval_real { |
sub parmval_real { |
Line 2262 sub parmval_real {
|
Line 2434 sub parmval_real {
|
my $uname=$env{'user.name'}; |
my $uname=$env{'user.name'}; |
my $udom=$env{'user.domain'}; |
my $udom=$env{'user.domain'}; |
|
|
unless ($symb) { return ''; } |
unless ($symb) { return ['']; } |
my $result=''; |
my $result=''; |
|
|
my ($mapname,$id,$fn)=&Apache::lonnet::decode_symb($symb); |
my ($mapname,$id,$fn)=&Apache::lonnet::decode_symb($symb); |
Line 2294 sub parmval_real {
|
Line 2466 sub parmval_real {
|
|
|
# ---------------------------------------------------------- first, check user |
# ---------------------------------------------------------- first, check user |
if ($uname and defined($useropt)) { |
if ($uname and defined($useropt)) { |
if (defined($$useropt{$courselevelr})) { return $$useropt{$courselevelr}; } |
if (defined($$useropt{$courselevelr})) { return [$$useropt{$courselevelr},'resource']; } |
if (defined($$useropt{$courselevelm})) { return $$useropt{$courselevelm}; } |
if (defined($$useropt{$courselevelm})) { return [$$useropt{$courselevelm},'map']; } |
if (defined($$useropt{$courselevel})) { return $$useropt{$courselevel}; } |
if (defined($$useropt{$courselevel})) { return [$$useropt{$courselevel},'course']; } |
} |
} |
|
|
# ------------------------------------------------------- second, check course |
# ------------------------------------------------------- second, check course |
if ($cgroup ne '' and defined($courseopt)) { |
if ($cgroup ne '' and defined($courseopt)) { |
if (defined($$courseopt{$grplevelr})) { return $$courseopt{$grplevelr}; } |
if (defined($$courseopt{$grplevelr})) { return [$$courseopt{$grplevelr},'resource']; } |
if (defined($$courseopt{$grplevelm})) { return $$courseopt{$grplevelm}; } |
if (defined($$courseopt{$grplevelm})) { return [$$courseopt{$grplevelm},'map']; } |
if (defined($$courseopt{$grplevel})) { return $$courseopt{$grplevel}; } |
if (defined($$courseopt{$grplevel})) { return [$$courseopt{$grplevel},'course']; } |
} |
} |
|
|
if ($csec and defined($courseopt)) { |
if ($csec and defined($courseopt)) { |
if (defined($$courseopt{$seclevelr})) { return $$courseopt{$seclevelr}; } |
if (defined($$courseopt{$seclevelr})) { return [$$courseopt{$seclevelr},'resource']; } |
if (defined($$courseopt{$seclevelm})) { return $$courseopt{$seclevelm}; } |
if (defined($$courseopt{$seclevelm})) { return [$$courseopt{$seclevelm},'map']; } |
if (defined($$courseopt{$seclevel})) { return $$courseopt{$seclevel}; } |
if (defined($$courseopt{$seclevel})) { return [$$courseopt{$seclevel},'course']; } |
} |
} |
|
|
if (defined($courseopt)) { |
if (defined($courseopt)) { |
if (defined($$courseopt{$courselevelr})) { return $$courseopt{$courselevelr}; } |
if (defined($$courseopt{$courselevelr})) { return [$$courseopt{$courselevelr},'resource']; } |
} |
} |
|
|
# ----------------------------------------------------- third, check map parms |
# ----------------------------------------------------- third, check map parms |
|
|
my $thisparm=$$parmhash{$symbparm}; |
my $thisparm=$$parmhash{$symbparm}; |
if (defined($thisparm)) { return $thisparm; } |
if (defined($thisparm)) { return [$thisparm,'map']; } |
|
|
# ----------------------------------------------------- fourth , check default |
# ----------------------------------------------------- fourth , check default |
|
|
my $meta_rwhat=$rwhat; |
my $meta_rwhat=$rwhat; |
$meta_rwhat=~s/\./_/g; |
$meta_rwhat=~s/\./_/g; |
my $default=&Apache::lonnet::metadata($fn,$meta_rwhat); |
my $default=&Apache::lonnet::metadata($fn,$meta_rwhat); |
if (defined($default)) { return $default} |
if (defined($default)) { return [$default,'resource']} |
$default=&Apache::lonnet::metadata($fn,'parameter_'.$meta_rwhat); |
$default=&Apache::lonnet::metadata($fn,'parameter_'.$meta_rwhat); |
if (defined($default)) { return $default} |
if (defined($default)) { return [$default,'resource']} |
|
|
# --------------------------------------------------- fifth, check more course |
# --------------------------------------------------- fifth, check more course |
if (defined($courseopt)) { |
if (defined($courseopt)) { |
if (defined($$courseopt{$courselevelm})) { return $$courseopt{$courselevelm}; } |
if (defined($$courseopt{$courselevelm})) { return [$$courseopt{$courselevelm},'map']; } |
if (defined($$courseopt{$courselevel})) { return $$courseopt{$courselevel}; } |
if (defined($$courseopt{$courselevel})) { |
|
my $ret = [$$courseopt{$courselevel},'course']; |
|
return $ret; |
|
} |
} |
} |
|
|
# --------------------------------------------------- sixth , cascade up parts |
# --------------------------------------------------- sixth , cascade up parts |
|
|
my ($space,@qualifier)=split(/\./,$rwhat); |
my ($space,@qualifier)=split(/\./,$rwhat); |
Line 2345 sub parmval_real {
|
Line 2518 sub parmval_real {
|
my $id=pop(@parts); |
my $id=pop(@parts); |
my $part=join('_',@parts); |
my $part=join('_',@parts); |
if ($part eq '') { $part='0'; } |
if ($part eq '') { $part='0'; } |
my $partgeneral=$self->parmval($part.".$qualifier",$symb,1); |
my @partgeneral=$self->parmval($part.".$qualifier",$symb,1); |
if (defined($partgeneral)) { return $partgeneral; } |
if (defined($partgeneral[0])) { return \@partgeneral; } |
} |
} |
if ($recurse) { return undef; } |
if ($recurse) { return []; } |
my $pack_def=&Apache::lonnet::packages_tab_default($fn,'resource.'.$what); |
my $pack_def=&Apache::lonnet::packages_tab_default($fn,'resource.'.$rwhat); |
if (defined($pack_def)) { return $pack_def; } |
if (defined($pack_def)) { return [$pack_def,'resource']; } |
return ''; |
return ['']; |
} |
} |
|
|
=pod |
=pod |
Line 2359 sub parmval_real {
|
Line 2532 sub parmval_real {
|
=item * B<getResourceByUrl>(url,multiple): |
=item * B<getResourceByUrl>(url,multiple): |
|
|
Retrieves a resource object by URL of the resource, unless the optional |
Retrieves a resource object by URL of the resource, unless the optional |
multiple parameter is included in wahich caes an array of resource |
multiple parameter is included in which case an array of resource |
objects is returned. If passed a resource object, it will simply return |
objects is returned. If passed a resource object, it will simply return |
it, so it is safe to use this method in code like |
it, so it is safe to use this method in code like |
"$res = $navmap->getResourceByUrl($res)" |
"$res = $navmap->getResourceByUrl($res)" |
Line 2394 all matching resources.
|
Line 2567 all matching resources.
|
|
|
=item * B<hasResource>(map, filterFunc, recursive, showall): |
=item * B<hasResource>(map, filterFunc, recursive, showall): |
|
|
Convience method for |
Convenience method for |
|
|
scalar(retrieveResources($map, $filterFunc, $recursive, 1, $showall)) > 0 |
scalar(retrieveResources($map, $filterFunc, $recursive, 1, $showall)) > 0 |
|
|
Line 2406 in the filter function.
|
Line 2579 in the filter function.
|
Retrieves version infomation for a url. Returns the version (a number, or |
Retrieves version infomation for a url. Returns the version (a number, or |
the string "mostrecent") for resources which have version information in |
the string "mostrecent") for resources which have version information in |
the big hash. |
the big hash. |
|
|
=cut |
=cut |
|
|
|
|
Line 2472 sub retrieveResources {
|
Line 2645 sub retrieveResources {
|
|
|
my @resources = (); |
my @resources = (); |
|
|
|
if (&$filterFunc($map)) { |
|
push(@resources, $map); |
|
} |
|
|
# Run down the iterator and collect the resources. |
# Run down the iterator and collect the resources. |
my $curRes; |
my $curRes; |
|
|
Line 2481 sub retrieveResources {
|
Line 2658 sub retrieveResources {
|
next; |
next; |
} |
} |
|
|
push @resources, $curRes; |
push(@resources, $curRes); |
|
|
if ($bailout) { |
if ($bailout) { |
return @resources; |
return @resources; |
Line 2628 be the tokens described above.
|
Line 2805 be the tokens described above.
|
|
|
Also note there is some old code floating around that trys to track |
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 |
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 |
code. It is difficult to get right and harder to understand than |
this. They should be migrated to this new style. |
this. They should be migrated to this new style. |
|
|
=cut |
=cut |
Line 2812 sub next {
|
Line 2989 sub next {
|
$self->{HAVE_RETURNED_0} = 1; |
$self->{HAVE_RETURNED_0} = 1; |
return $self->{NAV_MAP}->getById('0.0'); |
return $self->{NAV_MAP}->getById('0.0'); |
} |
} |
|
if ($self->{RETURN_0} && !$self->{HAVE_RETURNED_0_BEGIN_MAP}) { |
|
$self->{HAVE_RETURNED_0_BEGIN_MAP} = 1; |
|
return $self->BEGIN_MAP(); |
|
} |
|
|
if ($self->{RECURSIVE_ITERATOR_FLAG}) { |
if ($self->{RECURSIVE_ITERATOR_FLAG}) { |
# grab the next from the recursive iterator |
# grab the next from the recursive iterator |
Line 2913 sub next {
|
Line 3094 sub next {
|
} |
} |
|
|
# Is this the end of a branch? If so, all of the resources examined above |
# Is this the end of a branch? If so, all of the resources examined above |
# led to lower levels then the one we are currently at, so we push a END_BRANCH |
# led to lower levels than the one we are currently at, so we push a END_BRANCH |
# marker onto the stack so we don't forget. |
# marker onto the stack so we don't forget. |
# Example: For the usual A(BC)(DE)F case, when the iterator goes down the |
# Example: For the usual A(BC)(DE)F case, when the iterator goes down the |
# BC branch and gets to C, it will see F as the only next resource, but it's |
# BC branch and gets to C, it will see F as the only next resource, but it's |
Line 3249 X<symb> X<resource, symb>
|
Line 3430 X<symb> X<resource, symb>
|
All resources also have B<symb>s, which uniquely identify a resource |
All resources also have B<symb>s, which uniquely identify a resource |
in a course. Many internal LON-CAPA functions expect a symb. A symb |
in a course. Many internal LON-CAPA functions expect a symb. A symb |
carries along with it the URL of the resource, and the map it appears |
carries along with it the URL of the resource, and the map it appears |
in. Symbs are much larger then resource IDs. |
in. Symbs are much larger than resource IDs. |
|
|
=cut |
=cut |
|
|
Line 3283 sub navHash {
|
Line 3464 sub navHash {
|
my $self = shift; |
my $self = shift; |
my $param = shift; |
my $param = shift; |
my $id = shift; |
my $id = shift; |
return $self->{NAV_MAP}->navhash($param . ($id?$self->{ID}:"")); |
my $arg = $param . ($id?$self->{ID}:""); |
|
if (defined($arg)) { |
|
return $self->{NAV_MAP}->navhash($arg); |
|
} |
|
return; |
} |
} |
|
|
=pod |
=pod |
Line 3325 false.
|
Line 3510 false.
|
|
|
=item * B<randompick>: |
=item * B<randompick>: |
|
|
Returns true for a map if the randompick feature is being used on the |
Returns the number of randomly picked items for a map if the randompick |
map. (?) |
feature is being used on the map. |
|
|
|
=item * B<randomorder>: |
|
|
|
Returns true for a map if the randomorder feature is being used on the |
|
map. |
|
|
=item * B<src>: |
=item * B<src>: |
|
|
Line 3356 sub kind { my $self=shift; return $self-
|
Line 3546 sub kind { my $self=shift; return $self-
|
sub randomout { my $self=shift; return $self->navHash("randomout_", 1); } |
sub randomout { my $self=shift; return $self->navHash("randomout_", 1); } |
sub randompick { |
sub randompick { |
my $self = shift; |
my $self = shift; |
return $self->parmval('randompick'); |
my $randompick = $self->parmval('randompick'); |
|
return $randompick; |
|
} |
|
sub randomorder { |
|
my $self = shift; |
|
my $randomorder = $self->parmval('randomorder'); |
|
return ($randomorder =~ /^yes$/i); |
} |
} |
sub link { |
sub link { |
my $self=shift; |
my $self=shift; |
Line 3434 sub compTitle {
|
Line 3630 sub compTitle {
|
} |
} |
return $title; |
return $title; |
} |
} |
|
|
=pod |
=pod |
|
|
B<Predicate Testing the Resource> |
B<Predicate Testing the Resource> |
Line 3476 sub retrieveResources {
|
Line 3673 sub retrieveResources {
|
|
|
sub is_exam { |
sub is_exam { |
my ($self,$part) = @_; |
my ($self,$part) = @_; |
if ($self->parmval('type',$part) eq 'exam') { |
my $type = $self->parmval('type',$part); |
|
if ($type eq 'exam') { |
return 1; |
return 1; |
} |
} |
if ($self->src() =~ /\.(exam)$/) { |
if ($self->src() =~ /\.(exam)$/) { |
Line 3499 sub is_page {
|
Line 3697 sub is_page {
|
sub is_practice { |
sub is_practice { |
my $self=shift; |
my $self=shift; |
my ($part) = @_; |
my ($part) = @_; |
if ($self->parmval('type',$part) eq 'practice') { |
my $type = $self->parmval('type',$part); |
|
if ($type eq 'practice') { |
return 1; |
return 1; |
} |
} |
return 0; |
return 0; |
Line 3512 sub is_problem {
|
Line 3711 sub is_problem {
|
} |
} |
return 0; |
return 0; |
} |
} |
|
# |
|
# The has below is the set of status that are considered 'incomplete' |
|
# |
|
my %incomplete_hash = |
|
( |
|
TRIES_LEFT() => 1, |
|
OPEN() => 1, |
|
ATTEMPTED() => 1 |
|
|
|
); |
|
# |
|
# Return tru if a problem is incomplete... for now incomplete means that |
|
# any part of the problem is incomplete. |
|
# Note that if the resources is not a problem, 0 is returned. |
|
# |
|
sub is_incomplete { |
|
my $self = shift; |
|
if ($self->is_problem()) { |
|
&Apache::lonnet::logthis('is problem'); |
|
foreach my $part (@{$self->parts()}) { |
|
&Apache::lonnet::logthis("$part status ".$self->status($part)); |
|
if (exists($incomplete_hash{$self->status($part)})) { |
|
return 1; |
|
} |
|
} |
|
} |
|
return 0; |
|
|
|
} |
|
sub is_raw_problem { |
|
my $self=shift; |
|
my $src = $self->src(); |
|
if ($src =~ /\.(problem|exam|quiz|assess|survey|form|library|task)$/) { |
|
return 1; |
|
} |
|
return 0; |
|
} |
|
|
sub contains_problem { |
sub contains_problem { |
my $self=shift; |
my $self=shift; |
if ($self->is_page()) { |
if ($self->is_page()) { |
Line 3520 sub contains_problem {
|
Line 3757 sub contains_problem {
|
} |
} |
return 0; |
return 0; |
} |
} |
|
sub map_contains_problem { |
|
my $self=shift; |
|
if ($self->is_map()) { |
|
my $has_problem= |
|
$self->hasResource($self,sub { $_[0]->is_problem() },1); |
|
return $has_problem; |
|
} |
|
return 0; |
|
} |
sub is_sequence { |
sub is_sequence { |
my $self=shift; |
my $self=shift; |
return $self->navHash("is_map_", 1) && |
return $self->navHash("is_map_", 1) && |
$self->navHash("map_type_" . $self->map_pc()) eq 'sequence'; |
$self->navHash("map_type_" . $self->map_pc()) eq 'sequence'; |
} |
} |
sub is_survey { |
sub is_survey { |
my $self = shift(); |
my $self = shift(); |
my $part = shift(); |
my $part = shift(); |
if ($self->parmval('type',$part) eq 'survey') { |
my $type = $self->parmval('type',$part); |
|
if (($type eq 'survey') || ($type eq 'surveycred')) { |
return 1; |
return 1; |
} |
} |
if ($self->src() =~ /\.(survey)$/) { |
if ($self->src() =~ /\.(survey)$/) { |
Line 3536 sub is_survey {
|
Line 3783 sub is_survey {
|
} |
} |
return 0; |
return 0; |
} |
} |
|
sub is_anonsurvey { |
|
my $self = shift(); |
|
my $part = shift(); |
|
my $type = $self->parmval('type',$part); |
|
if (($type eq 'anonsurvey') || ($type eq 'anonsurveycred')) { |
|
return 1; |
|
} |
|
return 0; |
|
} |
sub is_task { |
sub is_task { |
my $self=shift; |
my $self=shift; |
my $src = $self->src(); |
my $src = $self->src(); |
Line 3589 resource of the map.
|
Line 3845 resource of the map.
|
|
|
Returns a string with the type of the map in it. |
Returns a string with the type of the map in it. |
|
|
|
=item *B<map_hierarchy>: |
|
|
|
Returns a string with a comma-separated ordered list of map_pc IDs |
|
for the hierarchy of maps containing a map, with the top level |
|
map first, then descending to deeper levels, with the enclosing map last. |
|
|
=back |
=back |
|
|
=cut |
=cut |
Line 3619 sub map_type {
|
Line 3881 sub map_type {
|
my $pc = $self->map_pc(); |
my $pc = $self->map_pc(); |
return $self->navHash("map_type_$pc", 0); |
return $self->navHash("map_type_$pc", 0); |
} |
} |
|
sub map_hierarchy { |
|
my $self = shift; |
|
my $pc = $self->map_pc(); |
|
return $self->navHash("map_hierarchy_$pc", 0); |
|
} |
|
|
##### |
##### |
# Property queries |
# Property queries |
Line 3626 sub map_type {
|
Line 3893 sub map_type {
|
|
|
# These functions will be responsible for returning the CORRECT |
# These functions will be responsible for returning the CORRECT |
# VALUE for the parameter, no matter what. So while they may look |
# VALUE for the parameter, no matter what. So while they may look |
# like direct calls to parmval, they can be more then that. |
# like direct calls to parmval, they can be more than that. |
# So, for instance, the duedate function should use the "duedatetype" |
# So, for instance, the duedate function should use the "duedatetype" |
# information, rather then the resource object user. |
# information, rather than the resource object user. |
|
|
=pod |
=pod |
|
|
Line 3704 Get the weight for the problem.
|
Line 3971 Get the weight for the problem.
|
|
|
sub acc { |
sub acc { |
(my $self, my $part) = @_; |
(my $self, my $part) = @_; |
return $self->parmval("acc", $part); |
my $acc = $self->parmval("acc", $part); |
|
return $acc; |
} |
} |
sub answerdate { |
sub answerdate { |
(my $self, my $part) = @_; |
(my $self, my $part) = @_; |
# Handle intervals |
# Handle intervals |
if ($self->parmval("answerdate.type", $part) eq 'date_interval') { |
my $answerdatetype = $self->parmval("answerdate.type", $part); |
return $self->duedate($part) + |
my $answerdate = $self->parmval("answerdate", $part); |
$self->parmval("answerdate", $part); |
my $duedate = $self->parmval("duedate", $part); |
|
if ($answerdatetype eq 'date_interval') { |
|
$answerdate = $duedate + $answerdate; |
} |
} |
return $self->parmval("answerdate", $part); |
return $answerdate; |
} |
} |
sub awarded { |
sub awarded { |
my $self = shift; my $part = shift; |
my $self = shift; my $part = shift; |
Line 3721 sub awarded {
|
Line 3991 sub awarded {
|
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'}; |
} |
} |
|
sub taskversion { |
|
my $self = shift; my $part = shift; |
|
$self->{NAV_MAP}->get_user_data(); |
|
if (!defined($part)) { $part = '0'; } |
|
return $self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$part.'.version'}; |
|
} |
|
sub taskstatus { |
|
my $self = shift; my $part = shift; |
|
$self->{NAV_MAP}->get_user_data(); |
|
if (!defined($part)) { $part = '0'; } |
|
return $self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$self->taskversion($part).'.'.$part.'.status'}; |
|
} |
|
sub solved { |
|
my $self = shift; my $part = shift; |
|
$self->{NAV_MAP}->get_user_data(); |
|
if (!defined($part)) { $part = '0'; } |
|
return $self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$part.'.solved'}; |
|
} |
|
sub checkedin { |
|
my $self = shift; my $part = shift; |
|
$self->{NAV_MAP}->get_user_data(); |
|
if (!defined($part)) { $part = '0'; } |
|
if ($self->is_task()) { |
|
my $version = $self->taskversion($part); |
|
return ($self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$version .'.'.$part.'.checkedin'},$self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$version .'.'.$part.'.checkedin.slot'}); |
|
} else { |
|
return ($self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$part.'.checkedin'},$self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$part.'.checkedin.slot'}); |
|
} |
|
} |
# this should work exactly like the copy in lonhomework.pm |
# this should work exactly like the copy in lonhomework.pm |
sub duedate { |
sub duedate { |
(my $self, my $part) = @_; |
(my $self, my $part) = @_; |
my $date; |
my $date; |
my $interval=$self->parmval("interval", $part); |
my @interval=$self->parmval("interval", $part); |
my $due_date=$self->parmval("duedate", $part); |
my $due_date=$self->parmval("duedate", $part); |
if ($interval =~ /\d+/) { |
if ($interval[0] =~ /\d+/) { |
my $first_access=&Apache::lonnet::get_first_access('map',$self->symb); |
my $first_access=&Apache::lonnet::get_first_access($interval[1], |
|
$self->symb); |
if (defined($first_access)) { |
if (defined($first_access)) { |
$interval = $first_access+$interval; |
my $interval = $first_access+$interval[0]; |
$date = ($interval < $due_date)? $interval : $due_date; |
$date = (!$due_date || $interval < $due_date) ? $interval |
|
: $due_date; |
} else { |
} else { |
$date = $due_date; |
$date = $due_date; |
} |
} |
Line 3742 sub duedate {
|
Line 4043 sub duedate {
|
} |
} |
sub handgrade { |
sub handgrade { |
(my $self, my $part) = @_; |
(my $self, my $part) = @_; |
return $self->parmval("handgrade", $part); |
my @response_ids = $self->responseIds($part); |
|
if (@response_ids) { |
|
foreach my $response_id (@response_ids) { |
|
my $handgrade = $self->parmval("handgrade",$part.'_'.$response_id); |
|
if (lc($handgrade) eq 'yes') { |
|
return 'yes'; |
|
} |
|
} |
|
} |
|
my $handgrade = $self->parmval("handgrade", $part); |
|
return $handgrade; |
} |
} |
sub maxtries { |
sub maxtries { |
(my $self, my $part) = @_; |
(my $self, my $part) = @_; |
return $self->parmval("maxtries", $part); |
my $maxtries = $self->parmval("maxtries", $part); |
|
return $maxtries; |
} |
} |
sub opendate { |
sub opendate { |
(my $self, my $part) = @_; |
(my $self, my $part) = @_; |
if ($self->parmval("opendate.type", $part) eq 'date_interval') { |
my $opendatetype = $self->parmval("opendate.type", $part); |
return $self->duedate($part) - |
my $opendate = $self->parmval("opendate", $part); |
$self->parmval("opendate", $part); |
if ($opendatetype eq 'date_interval') { |
|
my $duedate = $self->duedate($part); |
|
$opendate = $duedate - $opendate; |
} |
} |
return $self->parmval("opendate"); |
return $opendate; |
} |
} |
sub problemstatus { |
sub problemstatus { |
(my $self, my $part) = @_; |
(my $self, my $part) = @_; |
return lc $self->parmval("problemstatus", $part); |
my $problemstatus = $self->parmval("problemstatus", $part); |
|
return lc($problemstatus); |
} |
} |
sub sig { |
sub sig { |
(my $self, my $part) = @_; |
(my $self, my $part) = @_; |
return $self->parmval("sig", $part); |
my $sig = $self->parmval("sig", $part); |
|
return $sig; |
} |
} |
sub tol { |
sub tol { |
(my $self, my $part) = @_; |
(my $self, my $part) = @_; |
return $self->parmval("tol", $part); |
my $tol = $self->parmval("tol", $part); |
|
return $tol; |
} |
} |
sub tries { |
sub tries { |
my $self = shift; |
my $self = shift; |
my $tries = $self->queryRestoreHash('tries', shift); |
my $tries = $self->queryRestoreHash('tries', shift); |
if (!defined($tries)) { return '0';} |
if (!defined($tries)) { return '0';} |
Line 3776 sub tries {
|
Line 4093 sub tries {
|
} |
} |
sub type { |
sub type { |
(my $self, my $part) = @_; |
(my $self, my $part) = @_; |
return $self->parmval("type", $part); |
my $type = $self->parmval("type", $part); |
|
return $type; |
} |
} |
sub weight { |
sub weight { |
my $self = shift; my $part = shift; |
my $self = shift; my $part = shift; |
if (!defined($part)) { $part = '0'; } |
if (!defined($part)) { $part = '0'; } |
return &Apache::lonnet::EXT('resource.'.$part.'.weight', |
my $weight = &Apache::lonnet::EXT('resource.'.$part.'.weight', |
$self->symb(), $env{'user.domain'}, |
$self->symb(), $env{'user.domain'}, |
$env{'user.name'}, |
$env{'user.name'}, |
$env{'request.course.sec'}); |
$env{'request.course.sec'}); |
|
return $weight; |
} |
} |
sub part_display { |
sub part_display { |
my $self= shift(); my $partID = shift(); |
my $self= shift(); my $partID = shift(); |
Line 3796 sub part_display {
|
Line 4115 sub part_display {
|
} |
} |
return $display; |
return $display; |
} |
} |
|
sub slot_control { |
|
my $self=shift(); my $part = shift(); |
|
if (!defined($part)) { $part = '0'; } |
|
my $useslots = $self->parmval("useslots", $part); |
|
my $availablestudent = $self->parmval("availablestudent", $part); |
|
my $available = $self->parmval("available", $part); |
|
return ($useslots,$availablestudent,$available); |
|
} |
|
|
# Multiple things need this |
# Multiple things need this |
sub getReturnHash { |
sub getReturnHash { |
Line 3883 sub discussion_info {
|
Line 4210 sub discussion_info {
|
sub getFeedback { |
sub getFeedback { |
my $self = shift; |
my $self = shift; |
my $source = $self->src(); |
my $source = $self->src(); |
|
my $symb = $self->symb(); |
if ($source =~ /^\/res\//) { $source = substr $source, 5; } |
if ($source =~ /^\/res\//) { $source = substr $source, 5; } |
return $self->{NAV_MAP}->getFeedback($source); |
return $self->{NAV_MAP}->getFeedback($symb,$source); |
} |
} |
|
|
sub getErrors { |
sub getErrors { |
my $self = shift; |
my $self = shift; |
my $source = $self->src(); |
my $source = $self->src(); |
|
my $symb = $self->symb(); |
if ($source =~ /^\/res\//) { $source = substr $source, 5; } |
if ($source =~ /^\/res\//) { $source = substr $source, 5; } |
return $self->{NAV_MAP}->getErrors($source); |
return $self->{NAV_MAP}->getErrors($symb,$source); |
} |
} |
|
|
=pod |
=pod |
Line 4226 sub OPEN { return 1; }
|
Line 4555 sub OPEN { return 1; }
|
sub PAST_DUE_NO_ANSWER { return 2; } |
sub PAST_DUE_NO_ANSWER { return 2; } |
sub PAST_DUE_ANSWER_LATER { return 3; } |
sub PAST_DUE_ANSWER_LATER { return 3; } |
sub ANSWER_OPEN { return 4; } |
sub ANSWER_OPEN { return 4; } |
sub NOTHING_SET { return 5; } |
sub NOTHING_SET { return 5; } |
sub NETWORK_FAILURE { return 100; } |
sub NETWORK_FAILURE { return 100; } |
|
|
# getDateStatus gets the date status for a given problem part. |
# getDateStatus gets the date status for a given problem part. |
Line 4312 Information not available due to network
|
Line 4641 Information not available due to network
|
|
|
Attempted, and not yet graded. |
Attempted, and not yet graded. |
|
|
|
=item * B<CREDIT_ATTEMPTED>: |
|
|
|
Attempted, and credit received for attempt (survey and anonymous survey only). |
|
|
=back |
=back |
|
|
=cut |
=cut |
Line 4323 sub CORRECT { return 13; }
|
Line 4656 sub CORRECT { return 13; }
|
sub CORRECT_BY_OVERRIDE { return 14; } |
sub CORRECT_BY_OVERRIDE { return 14; } |
sub EXCUSED { return 15; } |
sub EXCUSED { return 15; } |
sub ATTEMPTED { return 16; } |
sub ATTEMPTED { return 16; } |
|
sub CREDIT_ATTEMPTED { return 17; } |
|
|
sub getCompletionStatus { |
sub getCompletionStatus { |
my $self = shift; |
my $self = shift; |
Line 4341 sub getCompletionStatus {
|
Line 4675 sub getCompletionStatus {
|
if ($status eq 'incorrect_by_override') {return $self->INCORRECT_BY_OVERRIDE; } |
if ($status eq 'incorrect_by_override') {return $self->INCORRECT_BY_OVERRIDE; } |
if ($status eq 'excused') {return $self->EXCUSED; } |
if ($status eq 'excused') {return $self->EXCUSED; } |
if ($status eq 'ungraded_attempted') {return $self->ATTEMPTED; } |
if ($status eq 'ungraded_attempted') {return $self->ATTEMPTED; } |
|
if ($status eq 'credit_attempted') { |
|
if ($self->is_anonsurvey($part) || $self->is_survey($part)) { |
|
return $self->CREDIT_ATTEMPTED; |
|
} else { |
|
return $self->ATTEMPTED; |
|
} |
|
} |
return $self->NOT_ATTEMPTED; |
return $self->NOT_ATTEMPTED; |
} |
} |
|
|
Line 4430 The item is open and not yet tried.
|
Line 4771 The item is open and not yet tried.
|
|
|
The problem has been attempted. |
The problem has been attempted. |
|
|
|
=item * B<CREDIT_ATTEMPTED>: |
|
|
|
The problem has been attempted, and credit given for the attempt (survey and anonymous survey only). |
|
|
=item * B<ANSWER_SUBMITTED>: |
=item * B<ANSWER_SUBMITTED>: |
|
|
An answer has been submitted, but the student should not see it. |
An answer has been submitted, but the student should not see it. |
Line 4438 An answer has been submitted, but the st
|
Line 4783 An answer has been submitted, but the st
|
|
|
=cut |
=cut |
|
|
sub TRIES_LEFT { return 20; } |
sub TRIES_LEFT { return 20; } |
sub ANSWER_SUBMITTED { return 21; } |
sub ANSWER_SUBMITTED { return 21; } |
sub PARTIALLY_CORRECT{ return 22; } |
sub PARTIALLY_CORRECT { return 22; } |
|
|
|
sub RESERVED_LATER { return 30; } |
|
sub RESERVED { return 31; } |
|
sub RESERVED_LOCATION { return 32; } |
|
sub RESERVABLE { return 33; } |
|
sub RESERVABLE_LATER { return 34; } |
|
sub NOTRESERVABLE { return 35; } |
|
sub NOT_IN_A_SLOT { return 36; } |
|
sub NEEDS_CHECKIN { return 37; } |
|
sub WAITING_FOR_GRADE { return 38; } |
|
sub UNKNOWN { return 39; } |
|
|
sub status { |
sub status { |
my $self = shift; |
my $self = shift; |
Line 4456 sub status {
|
Line 4812 sub status {
|
#if ($self->{RESOURCE_ERROR}) { return NETWORK_FAILURE; } |
#if ($self->{RESOURCE_ERROR}) { return NETWORK_FAILURE; } |
if ($completionStatus == NETWORK_FAILURE) { return NETWORK_FAILURE; } |
if ($completionStatus == NETWORK_FAILURE) { return NETWORK_FAILURE; } |
|
|
my $suppressFeedback = $self->problemstatus($part) eq 'no'; |
my $suppressFeedback = 0; |
|
if (($self->problemstatus($part) eq 'no') || |
|
($self->problemstatus($part) eq 'no_feedback_ever')) { |
|
$suppressFeedback = 1; |
|
} |
# If there's an answer date and we're past it, don't |
# If there's an answer date and we're past it, don't |
# suppress the feedback; student should know |
# suppress the feedback; student should know |
if ($self->duedate($part) && $self->duedate($part) < time() && |
if ($self->duedate($part) && $self->duedate($part) < time() && |
Line 4488 sub status {
|
Line 4848 sub status {
|
return ATTEMPTED; |
return ATTEMPTED; |
} |
} |
|
|
|
if ($completionStatus == CREDIT_ATTEMPTED) { |
|
return CREDIT_ATTEMPTED; |
|
} |
|
|
# If it's EXCUSED, then return that no matter what |
# If it's EXCUSED, then return that no matter what |
if ($completionStatus == EXCUSED) { |
if ($completionStatus == EXCUSED) { |
return EXCUSED; |
return EXCUSED; |
Line 4529 sub status {
|
Line 4893 sub status {
|
return OPEN; |
return OPEN; |
} |
} |
|
|
|
sub check_for_slot { |
|
my $self = shift; |
|
my $part = shift; |
|
my ($use_slots,$available,$availablestudent) = $self->slot_control($part); |
|
if (($use_slots ne '') && ($use_slots !~ /^\s*no\s*$/i)) { |
|
my @slots = (split(/:/,$availablestudent),split(/:/,$available)); |
|
my $cid=$env{'request.course.id'}; |
|
my $cdom=$env{'course.'.$cid.'.domain'}; |
|
my $cnum=$env{'course.'.$cid.'.num'}; |
|
my $now = time; |
|
if (@slots > 0) { |
|
my %slots=&Apache::lonnet::get('slots',[@slots],$cdom,$cnum); |
|
if (&Apache::lonnet::error(%slots)) { |
|
return (UNKNOWN); |
|
} |
|
my @sorted_slots = &Apache::loncommon::sorted_slots(\@slots,\%slots); |
|
my ($checkedin,$checkedinslot); |
|
foreach my $slot_name (@sorted_slots) { |
|
next if (!defined($slots{$slot_name}) || |
|
!ref($slots{$slot_name})); |
|
my $end = $slots{$slot_name}->{'endtime'}; |
|
my $start = $slots{$slot_name}->{'starttime'}; |
|
my $ip = $slots{$slot_name}->{'ip'}; |
|
if ($self->simpleStatus() == OPEN) { |
|
my $startreserve = $slots{$slot_name}->{'startreserve'}; |
|
my @proctors; |
|
if ($slots{$slot_name}->{'proctor'} ne '') { |
|
@proctors = split(',',$slots{$slot_name}->{'proctor'}); |
|
} |
|
if ($end > $now) { |
|
($checkedin,$checkedinslot) = $self->checkedin(); |
|
if ($startreserve < $now) { |
|
if ($start > $now) { |
|
return (RESERVED_LATER,$start,$slot_name); |
|
} else { |
|
if ($ip ne '') { |
|
if (!&Apache::loncommon::check_ip_acc($ip)) { |
|
return (RESERVED_LOCATION,$ip,$slot_name); |
|
} |
|
} |
|
if (@proctors > 0) { |
|
unless ((grep(/^\Q$checkedin\E/,@proctors)) && |
|
($checkedinslot eq $slot_name)) { |
|
return (NEEDS_CHECKIN,undef,$slot_name); |
|
} |
|
} |
|
return (RESERVED,$end,$slot_name); |
|
} |
|
} else { |
|
if ($start > $now) { |
|
return (RESERVABLE,$startreserve,$slot_name); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
my ($is_correct,$got_grade); |
|
if ($self->is_task()) { |
|
my $taskstatus = $self->taskstatus(); |
|
$is_correct = (($taskstatus eq 'pass') || |
|
($self->solved() =~ /^correct_/)); |
|
$got_grade = ($self->solved() =~ /^(?:pass|fail)$/); |
|
} else { |
|
$got_grade = 1; |
|
$is_correct = ($self->solved() =~ /^correct_/); |
|
} |
|
($checkedin,$checkedinslot) = $self->checkedin(); |
|
if ($checkedin) { |
|
if (!$got_grade) { |
|
return (WAITING_FOR_GRADE); |
|
} elsif ($is_correct) { |
|
return (CORRECT); |
|
} |
|
} |
|
return(NOT_IN_A_SLOT); |
|
} else { |
|
if (!$future_slots_checked) { |
|
$future_slots = &get_future_slots($cdom,$cnum,$now); |
|
$future_slots_checked = 1; |
|
} |
|
if ($future_slots) { |
|
return(NOT_IN_A_SLOT); |
|
} |
|
return(NOTRESERVABLE); |
|
} |
|
} |
|
return; |
|
} |
|
|
|
sub get_future_slots { |
|
my ($cdom,$cnum,$now) = @_; |
|
my %slots=&Apache::lonnet::dump('slots',$cdom,$cnum); |
|
my $future_slots = 0; |
|
foreach my $slot (keys(%slots)) { |
|
if (($slots{$slot}->{'starttime'} > $now) && |
|
($slots{$slot}->{'endtime'} > $now)) { |
|
$future_slots ++; |
|
} |
|
} |
|
return $future_slots; |
|
} |
|
|
sub CLOSED { return 23; } |
sub CLOSED { return 23; } |
sub ERROR { return 24; } |
sub ERROR { return 24; } |
|
|
Line 4578 my %compositeToSimple =
|
Line 5044 my %compositeToSimple =
|
INCORRECT() => INCORRECT, |
INCORRECT() => INCORRECT, |
OPEN() => OPEN, |
OPEN() => OPEN, |
ATTEMPTED() => ATTEMPTED, |
ATTEMPTED() => ATTEMPTED, |
|
CREDIT_ATTEMPTED() => CORRECT, |
ANSWER_SUBMITTED() => ATTEMPTED |
ANSWER_SUBMITTED() => ATTEMPTED |
); |
); |
|
|
Line 4652 sub completable {
|
Line 5119 sub completable {
|
# and it is not "attempted" (manually graded problem), it is |
# and it is not "attempted" (manually graded problem), it is |
# not "complete" |
# not "complete" |
if ($self->getCompletionStatus($part) == ATTEMPTED() || |
if ($self->getCompletionStatus($part) == ATTEMPTED() || |
|
$self->getCompletionStatus($part) == CREDIT_ATTEMPTED() || |
$status == ANSWER_SUBMITTED() ) { |
$status == ANSWER_SUBMITTED() ) { |
# did this part already, as well as we can |
# did this part already, as well as we can |
next; |
next; |