--- loncom/interface/lonnavmaps.pm 2011/03/14 15:48:00 1.458
+++ loncom/interface/lonnavmaps.pm 2011/11/07 20:05:56 1.466
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Navigate Maps Handler
#
-# $Id: lonnavmaps.pm,v 1.458 2011/03/14 15:48:00 bisitz Exp $
+# $Id: lonnavmaps.pm,v 1.466 2011/11/07 20:05:56 www Exp $
#
# Copyright Michigan State University Board of Trustees
@@ -478,12 +478,18 @@ use Apache::loncommon();
use Apache::lonenc();
use Apache::lonlocal;
use Apache::lonnet;
+use Apache::lonmap;
use POSIX qw (floor strftime);
use Time::HiRes qw( gettimeofday tv_interval );
use LONCAPA;
use DateTime();
+# For debugging
+
+use Data::Dumper;
+
+
# symbolic constants
sub SYMB { return 1; }
sub URL { return 2; }
@@ -509,7 +515,7 @@ my %statusIconMap =
my %iconAltTags = #texthash does not work here
( 'navmap.correct.gif' => 'Correct',
'navmap.wrong.gif' => 'Incorrect',
- 'navmap.open.gif' => 'Open',
+ 'navmap.open.gif' => 'Is Open',
'navmap.partial.gif' => 'Partially Correct',
'navmap.ellipsis.gif' => 'Attempted',
);
@@ -612,7 +618,7 @@ sub getDescription {
return &Apache::lonhtmlcommon::direct_parm_link(&mt("Not currently assigned.",$res->symb(),'opendate'),$part);
}
if ($status == $res->OPEN_LATER) {
- return &mt("Open ") .&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($open,'start'),$res->symb(),'opendate',$part);
+ return &mt("Open [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($open,'start')),$res->symb(),'opendate',$part);
}
if ($res->simpleStatus($part) == $res->OPEN) {
unless (&Apache::lonnet::allowed('mgr',$env{'request.course.id'})) {
@@ -646,22 +652,22 @@ sub getDescription {
if ($status == $res->OPEN) {
if ($due) {
if ($res->is_practice()) {
- return &mt("Closes ")." " .&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'start'),$res->symb(),'duedate',$part);
+ return &mt("Closes [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'start')),$res->symb(),'duedate',$part);
} else {
- return &mt("Due")." " .&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'end'),$res->symb(),'duedate',$part);
+ return &mt("Due [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'end')),$res->symb(),'duedate',$part);
}
} else {
return &Apache::lonhtmlcommon::direct_parm_link(&mt("Open, no due date"),$res->symb(),'duedate',$part);
}
}
if ($status == $res->PAST_DUE_ANSWER_LATER) {
- return &mt("Answer open")." " .&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($answer,'start'),$res->symb(),'answerdate',$part);
+ return &mt("Answer open [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($answer,'start')),$res->symb(),'answerdate',$part);
}
if ($status == $res->PAST_DUE_NO_ANSWER) {
if ($res->is_practice()) {
- return &mt("Closed")." " . &Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'start'),$res->symb(),'answerdate,duedate',$part);
+ return &mt("Closed [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'start')),$res->symb(),'answerdate,duedate',$part);
} else {
- return &mt("Was due")." " .&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'end'),$res->symb(),'answerdate,duedate',$part);
+ return &mt("Was due [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'end')),$res->symb(),'answerdate,duedate',$part);
}
}
if (($status == $res->ANSWER_OPEN || $status == $res->PARTIALLY_CORRECT)
@@ -694,7 +700,7 @@ sub getDescription {
}
}
if ($due) {
- return &mt("Due")." " . &Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'end'),$res->symb(),'duedate',$part) .
+ return &mt("Due [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'end')),$res->symb(),'duedate',$part) .
" $triesString";
} else {
return &Apache::lonhtmlcommon::direct_parm_link(&mt("No due date"),$res->symb(),'duedate',$part)." $triesString";
@@ -779,14 +785,31 @@ sub timeToHumanString {
# Less than an hour
if ( $delta < $hour ) {
- # If so, use minutes
+ # If so, use minutes; or minutes, seconds (if format requires)
my $minutes = floor($delta / 60);
+ if (($format ne '') && ($format =~ /\%(T|S)/)) {
+ my $display;
+ if ($minutes == 1) {
+ $display = "${prefix}1 minute";
+ } else {
+ $display = "$prefix$minutes minutes";
+ }
+ my $seconds = $delta % $minute;
+ if ($seconds == 0) {
+ $display .= $tense;
+ } elsif ($seconds == 1) {
+ $display .= ", 1 second$tense";
+ } else {
+ $display .= ", $seconds seconds$tense";
+ }
+ return $display;
+ }
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
+ # display hours + minutes, (and + seconds, if format specified it)
if ( $delta < $hour * 24) {
my $hours = floor($delta / $hour);
my $minutes = floor(($delta % $hour) / $minute);
@@ -801,15 +824,30 @@ sub timeToHumanString {
if ($minutes == 0) {
$minuteString = "";
}
+ if (($format ne '') && ($format =~ /\%(T|S)/)) {
+ my $display = "$prefix$hourString$minuteString";
+ my $seconds = $delta-(($hours * $hour)+($minutes * $minute));
+ if ($seconds == 0) {
+ $display .= $tense;
+ } elsif ($seconds == 1) {
+ $display .= ", 1 second$tense";
+ } else {
+ $display .= ", $seconds seconds$tense";
+ }
+ return $display;
+ }
return "$prefix$hourString$minuteString$tense";
}
+ # Date/time is more than 24 hours away
+
my $dt = DateTime->from_epoch(epoch => $time)
->set_time_zone(&Apache::lonlocal::gettimezone());
- # If there's a caller supplied format, use it.
+ # If there's a caller supplied format, use it, unless it only displays
+ # H:M:S or H:M.
- if ($format ne '') {
+ if (($format ne '') && ($format ne '%T') && ($format ne '%R')) {
my $timeStr = $dt->strftime($format);
return $timeStr.' ('.$dt->time_zone_short_name().')';
}
@@ -1090,10 +1128,9 @@ sub render_long_status {
}
}
- if (($resource->kind() eq "res" &&
- ($resource->is_problem() || $resource->is_practice()) &&
- !$firstDisplayed) &&
- $resource->is_raw_problem()) {
+ if ($resource->kind() eq "res" &&
+ $resource->is_raw_problem() &&
+ !$firstDisplayed) {
if ($color) {$result .= ""; }
$result .= getDescription($resource, $part);
if ($color) {$result .= ""; }
@@ -1930,10 +1967,13 @@ sub new {
my $proto = shift;
my $class = ref($proto) || $proto;
my $self = {};
+ bless($self); # So we can call change_user if neceesary
$self->{USERNAME} = shift || $env{'user.name'};
$self->{DOMAIN} = shift || $env{'user.domain'};
+
+
# Resource cache stores navmap resources as we reference them. We generate
# them on-demand so we don't pay for creating resources unless we use them.
$self->{RESOURCE_CACHE} = {};
@@ -1942,31 +1982,90 @@ sub new {
# failed
$self->{NETWORK_FAILURE} = 0;
- # tie the nav hash
+ # We can only tie the nav hash as done below if the username/domain
+ # match the env one. Otherwise change_user does everything we need...since we can't
+ # assume there are course hashes for the specific requested user@domamin:
+ #
- my %navmaphash;
- my %parmhash;
- my $courseFn = $env{"request.course.fn"};
- if (!(tie(%navmaphash, 'GDBM_File', "${courseFn}.db",
- &GDBM_READER(), 0640))) {
- return undef;
+ if (($self->{USERNAME} eq $env{'user.name'}) && ($self->{DOMAIN} eq $env{'user.domain'})) {
+
+ # tie the nav hash
+
+ my %navmaphash;
+ my %parmhash;
+ my $courseFn = $env{"request.course.fn"};
+ if (!(tie(%navmaphash, 'GDBM_File', "${courseFn}.db",
+ &GDBM_READER(), 0640))) {
+ return undef;
+ }
+
+ if (!(tie(%parmhash, 'GDBM_File', "${courseFn}_parms.db",
+ &GDBM_READER(), 0640)))
+ {
+ untie %{$self->{PARM_HASH}};
+ return undef;
+ }
+
+ $self->{NAV_HASH} = \%navmaphash;
+ $self->{PARM_HASH} = \%parmhash;
+ $self->{PARM_CACHE} = {};
+ } else {
+ $self->change_user($self->{USERNAME}, $self->{DOMAIN});
}
+
+ return $self;
+}
+
+#
+# In some instances it is useful to be able to dynamically change the
+# username/domain associated with a navmap (e.g. to navigate for someone
+# else besides the current user...if sufficiently privileged.
+# Parameters:
+# user - New user.
+# domain- Domain the user belongs to.
+# Implicit inputs:
+#
+sub change_user {
+ my $self = shift;
+ $self->{USERNAME} = shift;
+ $self->{DOMAIN} = shift;
+
+ # If the hashes are already tied make sure to break that bond:
+
+ untie %{$self->{NAV_HASH}};
+ untie %{$self->{PARM_HASH}};
+
+ # The assumption is that we have to
+ # use lonmap here to re-read the hash and from it reconstruct
+ # new big and parameter hashes. An implicit assumption at this time
+ # is that the course file is probably not created locally yet
+ # an that we will therefore just read without tying.
+
+ my ($cdom, $cnum) = split(/\_/, $env{'request.course.id'});
+
+ my %big_hash;
+ &Apache::lonmap::loadmap($cnum, $cdom, $self->{USERNAME}, $self->{DOMAIN}, \%big_hash);
+ $self->{NAV_HASH} = \%big_hash;
+
+ # Now clear the parm cache and reconstruct the parm hash fromt he big_hash
+ # param.xxxx keys.
+
+ $self->{PARM_CACHE} = {};
- if (!(tie(%parmhash, 'GDBM_File', "${courseFn}_parms.db",
- &GDBM_READER(), 0640)))
- {
- untie %{$self->{PARM_HASH}};
- return undef;
+ my %parm_hash = {};
+ foreach my $key (keys %big_hash) {
+ if ($key =~ /^param\./) {
+ my $param_key = $key;
+ $param_key =~ s/^param\.//;
+ $parm_hash{$param_key} = $big_hash{$key};
+ }
}
- $self->{NAV_HASH} = \%navmaphash;
- $self->{PARM_HASH} = \%parmhash;
- $self->{PARM_CACHE} = {};
+ $self->{PARM_HASH} = \%parm_hash;
- bless($self);
-
- return $self;
-}
+
+
+}
sub generate_course_user_opt {
my $self = shift;
@@ -2399,7 +2498,7 @@ sub parmval {
my $self = shift;
my ($what,$symb,$recurse)=@_;
my $hashkey = $what."|||".$symb;
-
+ my $cache = $self->{PARM_CACHE};
if (defined($self->{PARM_CACHE}->{$hashkey})) {
if (ref($self->{PARM_CACHE}->{$hashkey}) eq 'ARRAY') {
if (defined($self->{PARM_CACHE}->{$hashkey}->[0])) {
@@ -3720,7 +3819,7 @@ sub is_practice {
sub is_problem {
my $self=shift;
my $src = $self->src();
- if ($src =~ /\.(problem|exam|quiz|assess|survey|form|library|task)$/) {
+ if ($src =~ /$LONCAPA::assess_re/) {
return !($self->is_practice());
}
return 0;
@@ -3743,9 +3842,7 @@ my %incomplete_hash =
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;
}
@@ -3757,7 +3854,7 @@ sub is_incomplete {
sub is_raw_problem {
my $self=shift;
my $src = $self->src();
- if ($src =~ /\.(problem|exam|quiz|assess|survey|form|library|task)$/) {
+ if ($src =~ /$LONCAPA::assess_re/) {
return 1;
}
return 0;
@@ -3930,7 +4027,17 @@ their code.)
=over 4
-=item * B:
+=item * B
+
+returns true if the current date is such that the
+specified resource part is printable.
+
+=item * B
+
+Returns true if all parts in the resource are printable making the
+entire resource printable.
+
+=item * B
Get the Client IP/Name Access Control information.
@@ -3983,6 +4090,55 @@ Get the weight for the problem.
=cut
+sub printable {
+
+ my ($self, $part) = @_;
+
+ # Get the print open/close dates for the resource.
+
+ my $start = $self->parmval("prinstartdate", $part);
+ my $end = $self->parmval("printenddate", $part);
+
+ # The following cases apply:
+ # - No dates set: Printable.
+ # - Start date set but no end date: Printable if now >= start date.
+ # - End date set but no start date: Printable if now <= end date.
+ # - both defined: printable if start <= now <= end
+ #
+ my $now = time();
+
+ my $startok = 1;
+ my $endok = 1;
+
+ if ((defined $start) && ($start ne '')) {
+ $startok = $start <= $now;
+ }
+ if ((defined $end) && ($end != '')) {
+ $endok = $end >= $now;
+ }
+ return $startok && $endok;
+}
+
+sub resprintable {
+ my $self = shift;
+
+ # get parts...or realize there are no parts.
+
+ my $partsref = $self->parts();
+ my @parts = @$partsref;
+
+ if ((!defined(@parts)) || (scalar(@parts) == 0)) {
+ return $self->printable(0);
+ } else {
+ foreach my $part (@parts) {
+ if (!$self->printable($part)) {
+ return 0;
+ }
+ }
+ return 1;
+ }
+}
+
sub acc {
(my $self, my $part) = @_;
my $acc = $self->parmval("acc", $part);
@@ -4035,6 +4191,11 @@ sub checkedin {
}
}
# this should work exactly like the copy in lonhomework.pm
+# Why is there a copy in lonhomework? Why not centralized?
+#
+# TODO: Centralize duedate.
+#
+
sub duedate {
(my $self, my $part) = @_;
my $date;