--- loncom/interface/lonnavmaps.pm 2025/01/03 05:38:17 1.567 +++ loncom/interface/lonnavmaps.pm 2025/06/28 17:34:00 1.577 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Navigate Maps Handler # -# $Id: lonnavmaps.pm,v 1.567 2025/01/03 05:38:17 raeburn Exp $ +# $Id: lonnavmaps.pm,v 1.577 2025/06/28 17:34:00 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -48,7 +48,8 @@ described at http://www.lon-capa.org. =head1 OVERVIEW -X When a user enters a course, LON-CAPA examines the +X +When a user enters a course, LON-CAPA examines the course structure and caches it in what is often referred to as the "big hash" X. You can see it if you are logged into LON-CAPA, in a course, by going to /adm/test. The content of @@ -424,6 +425,11 @@ Convenience function, so others can use 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 graceEndsUnder24Hours() + +Convenience function, so others can use it: Is the problem past-due with a grace period +which ends in less than 24 hours, and still can be done? + =item advancedUser() This puts a human-readable name on the env variable. @@ -636,6 +642,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) { @@ -689,11 +696,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; } @@ -748,8 +764,14 @@ sub getDescription { } } if ($due) { - return &mt("Due [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'end'),$res->symb(),'duedate',$part)) . - " $triesString"; + my $now = time; + if (($now >= $due) && ($overdue) && ($now < $overdue)) { + return &mt("Grace period ends [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($overdue,'end'),$res->symb(),'grace',$part)). + " $triesString"; + } else { + 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"; } @@ -783,6 +805,19 @@ sub lastTry { $res->duedate($part) > time(); } +sub graceEndsUnder24Hours { + my $res = shift; + my $part = shift; + my $status = $res->status($part); + my $now = time(); + + return ($status == $res->OPEN() || + $status == $res->TRIES_LEFT()) && + $res->duedate($part) && $res->duedate($part) < $now && + $res->overduedate($part) && $res->overduedate($part) < time()+(24*60*60) && + $res->duedate($part) && $res->duedate($part) < $now+(24*60*60) && + $res->overduedate($part) > $now; +} sub advancedUser { return $env{'request.role.adv'}; @@ -1120,7 +1155,7 @@ sub render_resource { } # Decide what to display - $result .= "$newBranchText$linkopen$icon$linkclose"; + $result .= "$newBranchText$linkopen$icon"; my $curMarkerBegin = ''; my $curMarkerEnd = ''; @@ -1160,7 +1195,7 @@ sub render_resource { $linkopen = ""; } } - $result .= "$curMarkerBegin$linkopen$title$partLabel$linkclose$curMarkerEnd$editmapLink$nonLinkedText"; + $result .= "$curMarkerBegin$title$partLabel$curMarkerEnd$linkclose$editmapLink$nonLinkedText"; return $result; } @@ -1253,6 +1288,9 @@ sub render_long_status { if (dueInLessThan24Hours($resource, $part)) { $color = $hurryUpColor; $info = ' title="'.&mt('Due in less than 24 hours!').'"'; + } elsif (graceEndsUnder24Hours($resource, $part)) { + $color = $hurryUpColor; + $info = ' title="'.&mt('Grace period ends in less than 24 hours!').'"'; } elsif (lastTry($resource, $part)) { unless (($resource->problemstatus($part) eq 'no') || ($resource->problemstatus($part) eq 'no_feedback_ever')) { @@ -1379,13 +1417,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'}; @@ -1572,10 +1614,15 @@ 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 + # when $jump is empty. Note: we still need + # $counter to be 1 in that case if there is at + # least one resource. + last if (($jump eq '') && ($counter)); + if (ref($curRes) && $jump eq $curRes->symb()) { # This is why we have to use the main iterator instead of the @@ -1594,27 +1641,19 @@ sub render { my $printKey = $args->{'printKey'}; my $printCloseAll = $args->{'printCloseAll'}; if (!defined($printCloseAll)) { $printCloseAll = 1; } - + # Print key? if ($printKey) { - $result .= ''; - $result.=''; - my $location=&Apache::loncommon::lonhttpdurl("/adm/lonMisc"); - if ($navmap->{LAST_CHECK}) { - $result .= - ' '.&mt('New discussion since').' '. - strftime("%A, %b %e at %I:%M %P", localtime($navmap->{LAST_CHECK})). - ''; - } else { - $result .= ''; - } - - $result .= '
Key:    '. - ' '.&mt('New message (click to open)').'

'. - '

  '. - ' '.&mt('Discussions').''. - '   '.&mt('New message (click to open)'). - '
'; + my $location = &Apache::loncommon::lonhttpdurl("/adm/lonMisc"); + $legend = '
'."\n". + ''.&mt('Key').':'. + '  '. + ' '.&mt('Unread Discussion'). + '  '. + ' '. + &mt('New message (click to open)'). + '
'. + '
'."\n"; } if ($printCloseAll && !$args->{'resource_no_folder_link'}) { @@ -1634,9 +1673,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. @@ -1648,7 +1687,7 @@ sub render { my $time=time; my $submit = &mt($markread); my $querystr = &HTML::Entities::encode($ENV{'QUERY_STRING'},'<>&"'); - $result .= (<