--- loncom/interface/lonnavmaps.pm 2010/06/24 14:21:39 1.449 +++ loncom/interface/lonnavmaps.pm 2011/09/15 01:06:41 1.463 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Navigate Maps Handler # -# $Id: lonnavmaps.pm,v 1.449 2010/06/24 14:21:39 bisitz Exp $ +# $Id: lonnavmaps.pm,v 1.463 2011/09/15 01:06:41 raeburn Exp $ # # Copyright Michigan State University Board of Trustees @@ -483,7 +483,7 @@ use POSIX qw (floor strftime); use Time::HiRes qw( gettimeofday tv_interval ); use LONCAPA; use DateTime(); - +use Data::Dumper; # symbolic constants sub SYMB { return 1; } sub URL { return 2; } @@ -509,7 +509,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', ); @@ -528,6 +528,7 @@ my %colormap = $resObj->OPEN => '', $resObj->NOTHING_SET => '', $resObj->ATTEMPTED => '', + $resObj->CREDIT_ATTEMPTED => '', $resObj->ANSWER_SUBMITTED => '', $resObj->PARTIALLY_CORRECT => '#006600' ); @@ -608,10 +609,10 @@ sub getDescription { return &mt("Having technical difficulties; please check status later"); } if ($status == $res->NOTHING_SET) { - return &mt("Not currently assigned."); + return &Apache::lonhtmlcommon::direct_parm_link(&mt("Not currently assigned.",$res->symb(),'opendate'),$part); } if ($status == $res->OPEN_LATER) { - return &mt("Open ") .timeToHumanString($open,'start'); + 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'})) { @@ -645,27 +646,27 @@ sub getDescription { if ($status == $res->OPEN) { if ($due) { if ($res->is_practice()) { - return &mt("Closes ")." " .timeToHumanString($due,'start'); + return &mt("Closes [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'start')),$res->symb(),'duedate',$part); } else { - return &mt("Due")." " .timeToHumanString($due,'end'); + return &mt("Due [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'end')),$res->symb(),'duedate',$part); } } else { - return &mt("Open, no due date"); + 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")." " .timeToHumanString($answer,'start'); + 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")." " . timeToHumanString($due,'start'); + return &mt("Closed [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'start')),$res->symb(),'answerdate,duedate',$part); } else { - return &mt("Was due")." " . timeToHumanString($due,'end'); + 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) && $res->handgrade($part) ne 'yes') { - return &mt("Answer available"); + return &Apache::lonhtmlcommon::direct_parm_link(&mt("Answer available"),$res->symb(),'answerdate,duedate',$part); } if ($status == $res->EXCUSED) { return &mt("Excused by instructor"); @@ -677,6 +678,11 @@ sub getDescription { return &mt("Answer submitted, not yet graded"); } } + if ($status == $res->CREDIT_ATTEMPTED) { + if ($res->is_anonsurvey($part) || $res->is_survey($part)) { + return &mt("Credit for survey submission"); + } + } if ($status == $res->TRIES_LEFT) { my $tries = $res->tries($part); my $maxtries = $res->maxtries($part); @@ -688,10 +694,10 @@ sub getDescription { } } if ($due) { - return &mt("Due")." " . timeToHumanString($due,'end') . + return &mt("Due [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'end')),$res->symb(),'duedate',$part) . " $triesString"; } else { - return &mt("No due date")." $triesString"; + return &Apache::lonhtmlcommon::direct_parm_link(&mt("No due date"),$res->symb(),'duedate',$part)." $triesString"; } } if ($status == $res->ANSWER_SUBMITTED) { @@ -939,10 +945,10 @@ sub render_resource { } if ($resource->randomout()) { - $nonLinkedText .= ' ('.&mt('hidden').') '; + $nonLinkedText .= ' ('.&mt('hidden').') '; } if (!$resource->condval()) { - $nonLinkedText .= ' ('.&mt('conditionally hidden').') '; + $nonLinkedText .= ' ('.&mt('conditionally hidden').') '; } if (($resource->is_practice()) && ($resource->is_raw_problem())) { $nonLinkedText .=' '.&mt('not graded').''; @@ -1085,7 +1091,7 @@ sub render_long_status { } if ($resource->kind() eq "res" && - ($resource->is_problem() || $resource->is_practice()) && + $resource->is_raw_problem() && !$firstDisplayed) { if ($color) {$result .= ""; } $result .= getDescription($resource, $part); @@ -1924,6 +1930,9 @@ sub new { my $class = ref($proto) || $proto; my $self = {}; + $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} = {}; @@ -1962,8 +1971,9 @@ sub generate_course_user_opt { my $self = shift; if ($self->{COURSE_USER_OPT_GENERATED}) { return; } - my $uname=$env{'user.name'}; - my $udom=$env{'user.domain'}; + my $uname=$self->{USERNAME}; + my $udom=$self->{DOMAIN}; + my $cid=$env{'request.course.id'}; my $cdom=$env{'course.'.$cid.'.domain'}; my $cnum=$env{'course.'.$cid.'.num'}; @@ -2006,7 +2016,7 @@ sub generate_email_discuss_status { my $cdom=$env{'course.'.$cid.'.domain'}; my $cnum=$env{'course.'.$cid.'.num'}; - my %emailstatus = &Apache::lonnet::dump('email_status'); + my %emailstatus = &Apache::lonnet::dump('email_status',$self->{DOMAIN},$self->{USERNAME}); my $logoutTime = $emailstatus{'logout'}; my $courseLeaveTime = $emailstatus{'logout_'.$env{'request.course.id'}}; $self->{LAST_CHECK} = (($courseLeaveTime > $logoutTime) ? @@ -2014,7 +2024,7 @@ sub generate_email_discuss_status { my %discussiontime = &Apache::lonnet::dump('discussiontimes', $cdom, $cnum); my %lastread = &Apache::lonnet::dump('nohist_'.$cid.'_discuss', - $env{'user.domain'},$env{'user.name'},'lastread'); + $self->{DOMAIN},$self->{USERNAME},'lastread'); my %lastreadtime = (); foreach my $key (keys %lastread) { my $shortkey = $key; @@ -2024,8 +2034,8 @@ sub generate_email_discuss_status { my %feedback=(); my %error=(); - my @keys = &Apache::lonnet::getkeys('nohist_email',$env{'user.domain'}, - $env{'user.name'}); + my @keys = &Apache::lonnet::getkeys('nohist_email',$self->{DOMAIN}, + $self->{USERNAME}); foreach my $msgid (@keys) { if ((!$emailstatus{$msgid}) || ($emailstatus{$msgid} eq 'new')) { @@ -2073,8 +2083,8 @@ sub get_user_data { # Retrieve performance data on problems my %student_data = Apache::lonnet::currentdump($env{'request.course.id'}, - $env{'user.domain'}, - $env{'user.name'}); + $self->{DOMAIN}, + $self->{USERNAME}); $self->{STUDENT_DATA} = \%student_data; $self->{RETRIEVED_USER_DATA} = 1; @@ -2388,7 +2398,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])) { @@ -2425,8 +2435,8 @@ sub parmval_real { @cgrps = sort(@cgrps); $cgroup = $cgrps[0]; } - my $uname=$env{'user.name'}; - my $udom=$env{'user.domain'}; + my $uname=$self->{USERNAME}; + my $udom=$self->{DOMAIN}; unless ($symb) { return ['']; } my $result=''; @@ -2828,6 +2838,9 @@ sub new { weaken($self->{NAV_MAP} = shift); return undef unless ($self->{NAV_MAP}); + $self->{USERNAME} = $self->{NAV_MAP}->{USERNAME}; + $self->{DOMAIN} = $self->{NAV_MAP}->{DOMAIN}; + # Handle the parameters $self->{FIRST_RESOURCE} = shift || $self->{NAV_MAP}->firstResource(); $self->{FINISH_RESOURCE} = shift || $self->{NAV_MAP}->finishResource(); @@ -3201,6 +3214,9 @@ sub new { weaken($self->{NAV_MAP} = shift); return undef unless ($self->{NAV_MAP}); + $self->{USERNAME} = $self->{NAV_MAP}->{USERNAME}; + $self->{DOMAIN} = $self->{NAV_MAP}->{DOMAIN}; + $self->{FIRST_RESOURCE} = shift || $self->{NAV_MAP}->firstResource(); $self->{FINISH_RESOURCE} = shift || $self->{NAV_MAP}->finishResource(); @@ -3437,6 +3453,9 @@ sub new { weaken($self->{NAV_MAP} = shift); $self->{ID} = shift; + $self->{USERNAME} = $self->{NAV_MAP}->{USERNAME}; + $self->{DOMAIN} = $self->{NAV_MAP}->{DOMAIN}; + # Store this new resource in the parent nav map's cache. $self->{NAV_MAP}->{RESOURCE_CACHE}->{$self->{ID}} = $self; $self->{RESOURCE_ERROR} = 0; @@ -3459,7 +3478,7 @@ sub navHash { my $param = shift; my $id = shift; my $arg = $param . ($id?$self->{ID}:""); - if (defined($arg)) { + if (ref($self) && ref($self->{NAV_MAP}) && defined($arg)) { return $self->{NAV_MAP}->navhash($arg); } return; @@ -3723,9 +3742,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; } @@ -3910,7 +3927,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. @@ -3963,6 +3990,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); @@ -4015,6 +4091,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; @@ -4094,8 +4175,8 @@ sub weight { my $self = shift; my $part = shift; if (!defined($part)) { $part = '0'; } my $weight = &Apache::lonnet::EXT('resource.'.$part.'.weight', - $self->symb(), $env{'user.domain'}, - $env{'user.name'}, + $self->symb(), $self->{DOMAIN}, + $self->{USERNAME}, $env{'request.course.sec'}); return $weight; } @@ -4123,7 +4204,7 @@ sub getReturnHash { my $self = shift; if (!defined($self->{RETURN_HASH})) { - my %tmpHash = &Apache::lonnet::restore($self->symb()); + my %tmpHash = &Apache::lonnet::restore($self->symb(),undef,$self->{DOMAIN},$self->{USERNAME}); $self->{RETURN_HASH} = \%tmpHash; } } @@ -4635,6 +4716,10 @@ Information not available due to network Attempted, and not yet graded. +=item * B: + +Attempted, and credit received for attempt (survey and anonymous survey only). + =back =cut @@ -4646,6 +4731,7 @@ sub CORRECT { return 13; } sub CORRECT_BY_OVERRIDE { return 14; } sub EXCUSED { return 15; } sub ATTEMPTED { return 16; } +sub CREDIT_ATTEMPTED { return 17; } sub getCompletionStatus { my $self = shift; @@ -4664,6 +4750,13 @@ sub getCompletionStatus { if ($status eq 'incorrect_by_override') {return $self->INCORRECT_BY_OVERRIDE; } if ($status eq 'excused') {return $self->EXCUSED; } if ($status eq 'ungraded_attempted') {return $self->ATTEMPTED; } + if ($status eq 'credit_attempted') { + if ($self->is_anonsurvey($part) || $self->is_survey($part)) { + return $self->CREDIT_ATTEMPTED; + } else { + return $self->ATTEMPTED; + } + } return $self->NOT_ATTEMPTED; } @@ -4753,6 +4846,10 @@ The item is open and not yet tried. The problem has been attempted. +=item * B: + +The problem has been attempted, and credit given for the attempt (survey and anonymous survey only). + =item * B: An answer has been submitted, but the student should not see it. @@ -4826,6 +4923,10 @@ sub status { return ATTEMPTED; } + if ($completionStatus == CREDIT_ATTEMPTED) { + return CREDIT_ATTEMPTED; + } + # If it's EXCUSED, then return that no matter what if ($completionStatus == EXCUSED) { return EXCUSED; @@ -4928,7 +5029,7 @@ sub check_for_slot { my $taskstatus = $self->taskstatus(); $is_correct = (($taskstatus eq 'pass') || ($self->solved() =~ /^correct_/)); - $got_grade = ($self->solved() =~ /^(?:pass|fail)$/); + $got_grade = ($taskstatus =~ /^(?:pass|fail)$/); } else { $got_grade = 1; $is_correct = ($self->solved() =~ /^correct_/); @@ -5018,6 +5119,7 @@ my %compositeToSimple = INCORRECT() => INCORRECT, OPEN() => OPEN, ATTEMPTED() => ATTEMPTED, + CREDIT_ATTEMPTED() => CORRECT, ANSWER_SUBMITTED() => ATTEMPTED ); @@ -5092,6 +5194,7 @@ sub completable { # and it is not "attempted" (manually graded problem), it is # not "complete" if ($self->getCompletionStatus($part) == ATTEMPTED() || + $self->getCompletionStatus($part) == CREDIT_ATTEMPTED() || $status == ANSWER_SUBMITTED() ) { # did this part already, as well as we can next;