--- loncom/interface/lonnavmaps.pm 2025/02/03 03:04:24 1.570
+++ loncom/interface/lonnavmaps.pm 2025/06/28 14:34:46 1.576
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Navigate Maps Handler
#
-# $Id: lonnavmaps.pm,v 1.570 2025/02/03 03:04:24 raeburn Exp $
+# $Id: lonnavmaps.pm,v 1.576 2025/06/28 14:34:46 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -637,6 +637,7 @@ sub getDescription {
my $open = $res->opendate($part);
my $due = $res->duedate($part);
+ my $overdue = $res->overduedate($part);
my $answer = $res->answerdate($part);
if ($status == $res->NETWORK_FAILURE) {
@@ -690,11 +691,20 @@ sub getDescription {
}
if ($status == $res->OPEN) {
if ($due) {
- if ($res->is_practice()) {
- return &mt("Closes [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'start'),$res->symb(),'duedate',$part)).$slotinfo;
- } else {
- return &mt("Due [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'end'),$res->symb(),'duedate',$part)).$slotinfo;
- }
+ my $now = time;
+ if (($now >= $due) && ($overdue) && ($now < $overdue)) {
+ if ($res->is_practice()) {
+ return &mt("Closes [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($overdue,'start'),$res->symb(),'duedate',$part)).$slotinfo;
+ } else {
+ return &mt("Grace period ends [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($overdue,'end'),$res->symb(),'grace',$part)).$slotinfo;
+ }
+ } else {
+ if ($res->is_practice()) {
+ return &mt("Closes [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'start'),$res->symb(),'duedate',$part)).$slotinfo;
+ } else {
+ return &mt("Due [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'end'),$res->symb(),'duedate',$part)).$slotinfo;
+ }
+ }
} else {
return &Apache::lonhtmlcommon::direct_parm_link(&mt("Open, no due date"),$res->symb(),'duedate',$part).$slotinfo;
}
@@ -1380,13 +1390,17 @@ sub cmp_title {
sub render {
my $args = shift;
&Apache::loncommon::get_unprocessed_cgi($ENV{QUERY_STRING});
- my $result = '';
# Configure the renderer.
my $cols = $args->{'cols'};
if (!defined($cols)) {
# no columns, no nav maps.
return '';
}
+ my $legend = '';
+ my $tools = '';
+ my $result = '';
+ my $tools_printed = 0;
+ my $tablestarted = 0;
my $navmap;
if (defined($args->{'navmap'})) {
$navmap = $args->{'navmap'};
@@ -1573,7 +1587,7 @@ sub render {
my $curRes;
my $foundJump = 0;
my $counter = 0;
-
+
while (($curRes = $mapIterator->next()) && !$foundJump) {
if (ref($curRes)) { $counter++; }
# Speed up display after course initialization
@@ -1604,14 +1618,15 @@ sub render {
# Print key?
if ($printKey) {
my $location = &Apache::loncommon::lonhttpdurl("/adm/lonMisc");
- $result .= '
'.
- 'Key: | '.
- ' '.
- ' '.&mt('Unread Discussion').
- ' | '.
- ' '.
- &mt('New message (click to open)').
- ' |
'."\n";
+ $legend = ''.
+ ''."\n";
}
if ($printCloseAll && !$args->{'resource_no_folder_link'}) {
@@ -1631,9 +1646,9 @@ sub render {
"location.href='$link'",$text);
}
} else {
- $result.= ''.&mt($text).'';
+ $tools = ''.&mt($text).'';
}
- $result .= "\n";
+ $tools .= "\n";
}
# Check for any unread discussions in all resources.
@@ -1645,7 +1660,7 @@ sub render {
my $time=time;
my $submit = &mt($markread);
my $querystr = &HTML::Entities::encode($ENV{'QUERY_STRING'},'<>&"');
- $result .= (<
@@ -1662,12 +1677,12 @@ END
}
if ($totdisc > 0) {
$haveDisc =~ s/:$//;
- $result .= (<
END
}
}
- $result .= <
END
@@ -1692,25 +1707,15 @@ END
}
if ($args->{'caller'} eq 'navmapsdisplay') {
- $result .= &show_linkitems_toolbar($args,$condition);
+ $tools .= &show_linkitems_toolbar($args,$condition);
} elsif ($args->{'sort_html'}) {
- $result.=$args->{'sort_html'};
+ $tools .= $args->{'sort_html'};
}
- #$result .= "
\n";
- if ($r) {
- $r->print($result);
- $r->rflush();
- $result = "";
- }
+ #$tools .= "
\n";
# End parameter setting
-
- $result .= "
\n";
# Data
- if ($counter) {
- $result.=&Apache::loncommon::start_data_table("LC_tableOfContent");
- }
my $res = "Apache::lonnavmaps::resource";
my %condenseStatuses =
@@ -1722,7 +1727,8 @@ END
$args->{'counter'} = 0; # counts the rows
$args->{'indentLevel'} = 0;
$args->{'isNewBranch'} = 0;
- $args->{'condensed'} = 0;
+ $args->{'condensed'} = 0;
+ $args->{'deeplinknolist'} = 0;
my $location = &Apache::loncommon::lonhttpdurl("/adm/lonIcons/whitespace_21.gif");
$args->{'indentString'} = setDefault($args->{'indentString'}, "
");
@@ -1897,6 +1903,9 @@ END
# If this has been filtered out, continue on
if (!(&$filterFunc($curRes))) {
+ if (!$userCanSeeHidden && !$curRes->randomout && $curRes->deeplinkout) {
+ $args->{'deeplinknolist'} ++;
+ }
$args->{'isNewBranch'} = 0; # Don't falsely remember this
next;
}
@@ -1921,6 +1930,7 @@ END
if ($userCanSeeHidden) {
$args->{'mapHiddenDeepLink'} = 1;
} else {
+ $args->{'deeplinknolist'} ++;
next;
}
} else {
@@ -1930,6 +1940,7 @@ END
if ($userCanSeeHidden) {
$args->{'mapUnlisted'} = 1;
} else {
+ $args->{'deeplinknolist'} ++;
next;
}
}
@@ -1943,6 +1954,16 @@ END
}
$args->{'counter'}++;
+ unless ($tablestarted) {
+ $result.=&Apache::loncommon::start_data_table("LC_tableOfContent").
+ &Apache::loncommon::start_data_table_header_row('LC_visually_hidden').
+ ''.&mt('Resource or Folder').' | '.
+ ''.&mt('Alerts').' | '.
+ ''.&mt('Status Icon').' | '.
+ ''.&mt('Date/Completion Status').' | '.
+ &Apache::loncommon::end_data_table_row()."\n";
+ $tablestarted = 1;
+ }
# Does it have multiple parts?
$args->{'multipart'} = 0;
@@ -2093,6 +2114,12 @@ END
}
if ($r && $rownum % 20 == 0) {
+ unless ($tools_printed) {
+ $r->print($legend.$tools);
+ $legend = "";
+ $tools = "";
+ $tools_printed = 1;
+ }
$r->print($result);
$result = "";
$r->rflush();
@@ -2108,8 +2135,8 @@ END
}
}
- if ($counter) {
- $result.=&Apache::loncommon::end_data_table();
+ if ($tablestarted) {
+ $result .= &Apache::loncommon::end_data_table();
}
# Print out the part that jumps to #curloc if it exists
@@ -2128,12 +2155,23 @@ if (location.href.indexOf('#curloc')==-1
}
if ($r) {
+ unless ($tools_printed) {
+ if (($args->{'counter'}) || ($userCanSeeHidden) ||
+ (($args->{'caller'} eq 'navmapsdisplay') &&
+ ($env{'form.showOnlyHomework'} ||
+ $ENV{QUERY_STRING} =~ /^jumpToFirstHomework/))) {
+ $r->print($legend.$tools);
+ }
+ $legend = "";
+ $tools = "";
+ $tools_printed = 1;
+ }
$r->print($result);
$result = "";
$r->rflush();
}
- return $result;
+ return $legend.$tools.$result;
}
sub add_linkitem {
@@ -2149,14 +2187,12 @@ sub show_linkitems_toolbar {
if (ref($args->{'linkitems'}) eq 'HASH') {
my $numlinks = scalar(keys(%{$args->{'linkitems'}}));
if ($numlinks > 1) {
- $result = ''.
+ $result = ' '.
&Apache::loncommon::help_open_menu('Navigation Screen','Navigation_Screen',
undef,'RAT').
- ' | '.
- ' | '.
- ''.&mt('Tools:').' | ';
+ ' '.&mt('Tools:').'
';
}
- $result .= ''."\n".
+ $result .= ' | ';
+ '';
if (($numlinks==1) && (exists($args->{'linkitems'}{'edittoplevel'}))) {
- $result .= ''.
- &mt('Content Editor').' | ';
+ $result .= '';
}
}
if ($args->{'sort_html'}) {
- $result .= ' | | | '.
- ''.$args->{'sort_html'}.' | ';
+ $result .= ' '.
+ $args->{'sort_html'}.'
';
}
}
if ($result) {
- $result = "";
+ $result = ''.$result.'
'."\n".
+ ''."\n";
}
return $result;
}
@@ -3060,7 +3098,7 @@ sub parmval_real {
}
sub recurseup_maps {
- my ($self,$mapname,$getsymb) = @_;
+ my ($self,$mapname,$getsymb,$inclusive) = @_;
my @recurseup;
if ($mapname) {
my $res = $self->getResourceByUrl($mapname);
@@ -3074,6 +3112,13 @@ sub recurseup_maps {
@recurseup = map { &Apache::lonnet::declutter($self->getByMapPc($_)->src()); } reverse(@pcs);
}
}
+ if ($inclusive) {
+ if ($getsymb) {
+ unshift(@recurseup,$res->symb());
+ } else {
+ unshift(@recurseup,$mapname);
+ }
+ }
}
}
return @recurseup;
@@ -5142,6 +5187,12 @@ sub awarded {
if (!defined($part)) { $part = '0'; }
return $self->{NAV_MAP}->{STUDENT_DATA}->{$self->{SYMB}}->{'resource.'.$part.'.awarded'};
}
+sub latefrac {
+ 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.'.latefrac'};
+}
sub taskversion {
my $self = shift; my $part = shift;
$self->{NAV_MAP}->get_user_data();
@@ -5231,6 +5282,71 @@ sub opendate {
}
return $opendate;
}
+sub overduedate {
+ my ($self,$part) = @_;
+ my $duedate = $self->parmval("duedate", $part);
+ my $overduedate;
+ if ($duedate) {
+ my $grace = $self->parmval("grace", $part);
+ if ($grace) {
+ my $grace_end = (split(/,/,$grace))[-1];
+ my ($offset) = split(/:/,$grace_end,2);
+ if ($offset > 0) {
+ $overduedate = $offset + $duedate;
+ }
+ }
+ }
+ return $overduedate;
+}
+sub partial_credit_overdue {
+ my ($self,$part) = @_;
+ my $reduction;
+ my $duedate = $self->parmval("duedate", $part);
+ if ($duedate) {
+ my $grace = $self->parmval("grace",$part);
+ if ($grace) {
+ my $lateness = time - $duedate;
+ if ($lateness > 0) {
+ my ($start,$end,$startfrac,$endfrac,$usegrad);
+ $start = 0;
+ $startfrac = 1.0;
+ $usegrad = 0;
+ foreach my $item (split(/,/,$grace)) {
+ my ($offset,$frac,$grad) = split(/:/,$item);
+ if ($lateness > $offset) {
+ $start = $offset;
+ $startfrac = $frac;
+ next;
+ } elsif ($lateness <= $offset) {
+ $end = $offset;
+ $endfrac = $frac;
+ $usegrad = $grad;
+ last;
+ }
+ }
+ if ($end) {
+ if (($end == $start) || ($startfrac == $endfrac)) {
+ $reduction = $endfrac;
+ } elsif ($end - $start > 0) {
+ if (($endfrac <= 1.0) && ($endfrac >= 0)) {
+ $reduction = $endfrac;
+ if ($usegrad) {
+ my $decline = $startfrac - $endfrac;
+ my $fraction = ($lateness - $start)/($end - $start);
+ if (($fraction <= 1) && ($fraction >= 0)) {
+ my $value = $startfrac - ($decline*$fraction);
+ $reduction = sprintf("%.2f", $value);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return $reduction;
+}
+
sub problemstatus {
(my $self, my $part) = @_;
my $problemstatus = $self->parmval("problemstatus", $part);
@@ -5809,6 +5925,7 @@ sub getDateStatus {
my $open = $self->opendate($part);
my $due = $self->duedate($part);
+ my $overdue = $self->overduedate($part);
my $answer = $self->answerdate($part);
if (!$open && !$due && !$answer) {
@@ -5818,6 +5935,7 @@ sub getDateStatus {
}
if (!$open || $now < $open) {return $self->OPEN_LATER}
if (!$due || $now < $due) {return $self->OPEN}
+ if ($overdue && $now < $overdue) {return $self->OPEN}
if ($answer && $now < $answer) {return $self->PAST_DUE_ANSWER_LATER}
if ($answer) { return $self->ANSWER_OPEN; }
return PAST_DUE_NO_ANSWER;
@@ -6086,6 +6204,7 @@ sub status {
# If there's an answer date and we're past it, don't
# suppress the feedback; student should know
if ($self->duedate($part) && $self->duedate($part) < time() &&
+ (!$self->overduedate($part) || $self->overduedate($part) < time()) &&
$self->answerdate($part) && $self->answerdate($part) < time()) {
$suppressFeedback = 0;
}