--- loncom/interface/lonnavmaps.pm 2023/01/20 22:51:11 1.509.2.14.2.7 +++ loncom/interface/lonnavmaps.pm 2023/07/06 20:32:17 1.509.2.14.2.9 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Navigate Maps Handler # -# $Id: lonnavmaps.pm,v 1.509.2.14.2.7 2023/01/20 22:51:11 raeburn Exp $ +# $Id: lonnavmaps.pm,v 1.509.2.14.2.9 2023/07/06 20:32:17 raeburn Exp $ # # Copyright Michigan State University Board of Trustees @@ -534,6 +534,10 @@ my %colormap = $resObj->EXCUSED => '#3333FF', $resObj->PAST_DUE_ANSWER_LATER => '', $resObj->PAST_DUE_NO_ANSWER => '', + $resObj->PAST_DUE_ATMPT_ANS => '', + $resObj->PAST_DUE_ATMPT_NOANS => '', + $resObj->PAST_DUE_NO_ATMT_ANS => '', + $resObj->PAST_DUE_NO_ATMT_NOANS => '', $resObj->ANSWER_OPEN => '#006600', $resObj->OPEN_LATER => '', $resObj->TRIES_LEFT => '', @@ -679,10 +683,10 @@ sub getDescription { return &Apache::lonhtmlcommon::direct_parm_link(&mt("Open, no due date"),$res->symb(),'duedate',$part).$slotinfo; } } - if ($status == $res->PAST_DUE_ANSWER_LATER) { + if (($status == $res->PAST_DUE_ANSWER_LATER) || ($status == $res->PAST_DUE_ATMPT_ANS) || ($status == $res->PAST_DUE_NO_ATMT_ANS)) { 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 (($status == $res->PAST_DUE_NO_ANSWER) || ($status == $res->PAST_DUE_ATMPT_NOANS) || ($status == $res->PAST_DUE_NO_ATMT_NOANS)) { if ($res->is_practice()) { return &mt("Closed [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'start'),$res->symb(),'answerdate,duedate',$part)); } else { @@ -691,7 +695,17 @@ sub getDescription { } if (($status == $res->ANSWER_OPEN || $status == $res->PARTIALLY_CORRECT) && $res->handgrade($part) ne 'yes') { - return &Apache::lonhtmlcommon::direct_parm_link(&mt("Answer available"),$res->symb(),'answerdate,duedate',$part); + my $msg = &mt('Answer available'); + my $parmlist = 'answerdate,duedate'; + if (($res->is_tool) && ($res->is_gradable())) { + if (($status == $res->PARTIALLY_CORRECT) && ($res->parmval('retrypartial',$part))) { + $msg = &mt('Grade received'); + $parmlist = 'retrypartial'; + } else { + $msg = &mt('Grade available'); + } + } + return &Apache::lonhtmlcommon::direct_parm_link($msg,$res->symb(),$parmlist,$part); } if ($status == $res->EXCUSED) { return &mt("Excused by instructor"); @@ -1195,7 +1209,7 @@ sub render_quick_status { my $linkclose = ""; $result .= ''; - if ($resource->is_problem() && + if ($resource->is_gradable() && !$firstDisplayed) { my $icon = $statusIconMap{$resource->simpleStatus($part)}; my $alt = $iconAltTags{$icon}; @@ -1220,7 +1234,7 @@ sub render_long_status { my $color; my $info = ''; - if ($resource->is_problem() || $resource->is_practice()) { + if ($resource->is_gradable() || $resource->is_practice()) { $color = $colormap{$resource->status}; if (dueInLessThan24Hours($resource, $part)) { @@ -1235,8 +1249,8 @@ sub render_long_status { } } - if ($resource->kind() eq "res" && - $resource->is_raw_problem() && + if (($resource->kind() eq "res") && + ($resource->is_raw_problem() || $resource->is_gradable()) && !$firstDisplayed) { if ($color) {$result .= ''; } $result .= getDescription($resource, $part); @@ -1283,7 +1297,7 @@ my @statuses = ($resObj->CORRECT, $resOb sub render_parts_summary_status { my ($resource, $part, $params) = @_; - if (!$resource->is_problem() && !$resource->contains_problem) { return ''; } + if (!$resource->is_gradable() && !$resource->contains_problem) { return ''; } if ($params->{showParts}) { return ''; } @@ -2845,6 +2859,10 @@ sub parmval_real { my ($mapname,$id,$fn)=&Apache::lonnet::decode_symb($symb); $mapname = &Apache::lonnet::deversion($mapname); + my $toolsymb = ''; + if ($fn =~ /ext\.tool$/) { + $toolsymb = $symb; + } # ----------------------------------------------------- Cascading lookup scheme my $rwhat=$what; $what=~s/^parameter\_//; @@ -2908,9 +2926,9 @@ sub parmval_real { my $meta_rwhat=$rwhat; $meta_rwhat=~s/\./_/g; - my $default=&Apache::lonnet::metadata($fn,$meta_rwhat); + my $default=&Apache::lonnet::metadata($fn,$meta_rwhat,$toolsymb); if (defined($default)) { return [$default,'resource']} - $default=&Apache::lonnet::metadata($fn,'parameter_'.$meta_rwhat); + $default=&Apache::lonnet::metadata($fn,'parameter_'.$meta_rwhat,$toolsymb); if (defined($default)) { return [$default,'resource']} # --------------------------------------------------- fifth, check more course if (defined($courseopt)) { @@ -2933,7 +2951,7 @@ sub parmval_real { if (defined($partgeneral[0])) { return \@partgeneral; } } if ($recurse) { return []; } - my $pack_def=&Apache::lonnet::packages_tab_default($fn,'resource.'.$rwhat); + my $pack_def=&Apache::lonnet::packages_tab_default($fn,'resource.'.$rwhat,$toolsymb); if (defined($pack_def)) { return [$pack_def,'resource']; } return ['']; } @@ -3485,6 +3503,71 @@ sub usedVersion { return $self->navhash("version_$linkurl"); } +sub isFirstResource { + my $self = shift; + my $map = shift; + my $symb = shift; + return unless (ref($map)); + my $isfirst; + my $firstResource = $map->map_start(); + if (ref($firstResource)) { + if ((!$firstResource->is_map()) && ($firstResource->src() ne '')) { + if ($firstResource->symb() eq $symb) { + $isfirst = 1; + } else { + $isfirst = 0; + } + } else { + my $it = $self->getIterator($firstResource,undef,undef,1); + while ( my $res=$it->next()) { + if ((ref($res)) && ($res->src() ne '') && (!$res->is_map())) { + if ($res->symb() eq $symb) { + $isfirst = 1; + } else { + $isfirst = 0; + } + last; + } + } + } + } + return $isfirst; +} + +sub isLastResource { + my $self = shift; + my $map = shift; + my $symb = shift; + return unless (ref($map)); + my $islast; + my $lastResource = $map->map_finish(); + if (ref($lastResource)) { + if ((!$lastResource->is_map()) && ($lastResource->src() ne '')) { + if ($lastResource->symb() eq $symb) { + $islast = 1; + } else { + $islast = 0; + } + } else { + my $currRes = $self->getBySymb($symb); + if (ref($currRes)) { + my $it = $self->getIterator($currRes,undef,undef,1); + while ( my $res=$it->next()) { + if ((ref($res)) && ($res->src() ne '') && (!$res->is_map())) { + if ($res->symb() eq $symb) { + $islast = 1; + } else { + $islast = 0; + } + last; + } + } + } + } + } + return $islast; +} + 1; package Apache::lonnavmaps::iterator; @@ -4542,6 +4625,19 @@ sub is_problem { } return 0; } +sub is_tool { + my $self=shift; + my $src = $self->src(); + return ($src =~ /ext\.tool$/); +} +sub is_gradable { + my $self=shift; + my $src = $self->src(); + if (($src =~ /$LONCAPA::assess_re/) || + (($self->is_tool()) && ($self->parmval('gradable',0) =~ /^yes$/i))) { + return !($self->is_practice()); + } +} # # The has below is the set of status that are considered 'incomplete' # @@ -5241,6 +5337,8 @@ sub parts { my $self = shift; if ($self->ext) { return []; } + if (($self->is_tool()) && + ($self->is_gradable())) { return ['0']; } $self->extractParts(); return $self->{PARTS}; @@ -5331,7 +5429,7 @@ sub extractParts { my %parts; # Retrieve part count, if this is a problem - if ($self->is_problem()) { + if ($self->is_raw_problem()) { my $partorder = &Apache::lonnet::metadata($self->src(), 'partorder'); my $metadata = &Apache::lonnet::metadata($self->src(), 'packages'); @@ -5498,7 +5596,6 @@ The problem will be opened later. Open and not yet due. - =item * B: The due date has passed, but the answer date has not yet arrived. @@ -5511,6 +5608,26 @@ The due date has passed and there is no The answer date is here. +=item * B: + +No dates have been set for this problem at all. + +=item * B: + +The due date has passed, feedback is suppressed, the problem was attempted, and the answer date has not yet arrived. + +=item * B: + +The due date has passed, feedback is suppressed, the problem was attempted, and there is no answer opening date set. + +=item * B: + +The due date has passed, feedback is suppressed, the problem was not attempted, and the answer date has not yet arrived. + +=item * B: + +The due date has passed, feedback is suppressed, the problem was not attempted, and there is no answer opening date set. + =item * B: The information is unknown due to network failure. @@ -5526,6 +5643,10 @@ sub PAST_DUE_NO_ANSWER { return 2; } sub PAST_DUE_ANSWER_LATER { return 3; } sub ANSWER_OPEN { return 4; } sub NOTHING_SET { return 5; } +sub PAST_DUE_ATMPT_ANS { return 6; } +sub PAST_DUE_ATMPT_NOANS { return 7; } +sub PAST_DUE_NO_ATMT_ANS { return 8; } +sub PAST_DUE_NO_ATMT_NOANS { return 9; } sub NETWORK_FAILURE { return 100; } # getDateStatus gets the date status for a given problem part. @@ -5615,6 +5736,14 @@ Attempted, and not yet graded. Attempted, and credit received for attempt (survey and anonymous survey only). +=item * B: + +Attempted, but wrong for LTI Tool Provider by passback of grade + +=item * B: + +Correct for LTI Tool Provider by passback of grade + =back =cut @@ -5627,6 +5756,8 @@ sub CORRECT_BY_OVERRIDE { return 14; } sub EXCUSED { return 15; } sub ATTEMPTED { return 16; } sub CREDIT_ATTEMPTED { return 17; } +sub INCORRECT_BY_PASSBACK { return 18; } +sub CORRECT_BY_PASSBACK { return 19; } sub getCompletionStatus { my $self = shift; @@ -5641,8 +5772,12 @@ sub getCompletionStatus { if ($status eq 'correct_by_override') { return $self->CORRECT_BY_OVERRIDE; } + if ($status eq 'correct_by_passback') { + return $self->CORRECT_BY_PASSBACK; + } if ($status eq 'incorrect_attempted') {return $self->INCORRECT; } if ($status eq 'incorrect_by_override') {return $self->INCORRECT_BY_OVERRIDE; } + if ($status eq 'incorrect_by_passback') {return $self->INCORRECT_BY_PASSBACK; } if ($status eq 'excused') {return $self->EXCUSED; } if ($status eq 'ungraded_attempted') {return $self->ATTEMPTED; } if ($status eq 'credit_attempted') { @@ -5715,6 +5850,26 @@ set. The problem is past due, not considered correct, and an answer date in the future is set. +=item * B: + +The problem is past due, feedback is suppressed, the problem was +attempted and an answer date in the future is set. + +=item * B: + +The problem is past due, feedback is suppressed, the problem was +attempted and no answer date is set. + +=item * B: + +The problem is past due, feedback is suppressed, the problem was +not attempted and an answer date in the future is set. + +=item * B: + +The problem is past due, feedback is suppressed, the problem was +not attempted and no answer date is set. + =item * B: The problem is past due, not correct, and the answer is now available. @@ -5796,8 +5951,20 @@ sub status { # There are a few whole rows we can dispose of: if ($completionStatus == CORRECT || - $completionStatus == CORRECT_BY_OVERRIDE ) { - if ( $suppressFeedback ) { return ANSWER_SUBMITTED } + $completionStatus == CORRECT_BY_OVERRIDE || + $completionStatus == CORRECT_BY_PASSBACK ) { + if ( $suppressFeedback ) { + if ($dateStatus == PAST_DUE_ANSWER_LATER || + $dateStatus == PAST_DUE_NO_ANSWER ) { + if ($dateStatus == PAST_DUE_ANSWER_LATER) { + return PAST_DUE_ATMPT_ANS; + } else { + return PAST_DUE_ATMPT_NOANS; + } + } else { + return ANSWER_SUBMITTED; + } + } my $awarded=$self->awarded($part); if ($awarded < 1 && $awarded > 0) { return PARTIALLY_CORRECT; @@ -5809,7 +5976,8 @@ sub status { # If it's WRONG... and not open if ( ($completionStatus == INCORRECT || - $completionStatus == INCORRECT_BY_OVERRIDE) + $completionStatus == INCORRECT_BY_OVERRIDE || + $completionStatus == INCORRECT_BY_PASSBACK) && (!$self->opendate($part) || $self->opendate($part) > time()) ) { return INCORRECT; } @@ -5836,7 +6004,23 @@ sub status { if ($dateStatus == PAST_DUE_ANSWER_LATER || $dateStatus == PAST_DUE_NO_ANSWER ) { - return $suppressFeedback ? ANSWER_SUBMITTED : $dateStatus; + if ($suppressFeedback) { + if ($completionStatus == NOT_ATTEMPTED) { + if ($dateStatus == PAST_DUE_ANSWER_LATER) { + return PAST_DUE_NO_ATMT_ANS; + } else { + return PAST_DUE_NO_ATMT_NOANS; + } + } else { + if ($dateStatus == PAST_DUE_ANSWER_LATER) { + return PAST_DUE_ATMPT_ANS; + } else { + return PAST_DUE_ATMPT_NOANS; + } + } + } else { + return $dateStatus; + } } if ($dateStatus == ANSWER_OPEN) { @@ -5851,7 +6035,8 @@ sub status { } # If it's WRONG... - if ($completionStatus == INCORRECT || $completionStatus == INCORRECT_BY_OVERRIDE) { + if ($completionStatus == INCORRECT || $completionStatus == INCORRECT_BY_OVERRIDE || + $completionStatus == INCORRECT_BY_PASSBACK) { # and there are TRIES LEFT: if ($self->tries($part) < $self->maxtries($part) || !$self->maxtries($part)) { return $suppressFeedback ? ANSWER_SUBMITTED : TRIES_LEFT; @@ -6046,6 +6231,10 @@ my %compositeToSimple = EXCUSED() => CORRECT, PAST_DUE_NO_ANSWER() => INCORRECT, PAST_DUE_ANSWER_LATER() => INCORRECT, + PAST_DUE_ATMPT_ANS() => ATTEMPTED, + PAST_DUE_ATMPT_NOANS() => ATTEMPTED, + PAST_DUE_NO_ATMT_ANS() => CLOSED, + PAST_DUE_NO_ATMT_NOANS() => CLOSED, ANSWER_OPEN() => INCORRECT, OPEN_LATER() => CLOSED, TRIES_LEFT() => OPEN,