--- loncom/interface/lonnavmaps.pm 2025/05/27 23:31:49 1.575 +++ 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.575 2025/05/27 23:31:49 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; } @@ -5177,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(); @@ -5266,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); @@ -5844,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) { @@ -5853,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; @@ -6121,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; }