--- loncom/interface/lonnavmaps.pm 2009/03/16 15:25:40 1.431 +++ loncom/interface/lonnavmaps.pm 2010/12/03 15:19:09 1.453 @@ -1,7 +1,8 @@ # The LearningOnline Network with CAPA # Navigate Maps Handler # -# $Id: lonnavmaps.pm,v 1.431 2009/03/16 15:25:40 schulted Exp $ +# $Id: lonnavmaps.pm,v 1.453 2010/12/03 15:19:09 www Exp $ + # # Copyright Michigan State University Board of Trustees # @@ -31,7 +32,7 @@ =head1 NAME -Apache::lonnavmaps.pm +Apache::lonnavmaps - Subroutines to handle and render the navigation =head1 SYNOPSIS @@ -477,6 +478,7 @@ use Apache::loncommon(); use Apache::lonenc(); use Apache::lonlocal; use Apache::lonnet; + use POSIX qw (floor strftime); use Time::HiRes qw( gettimeofday tv_interval ); use LONCAPA; @@ -491,7 +493,7 @@ sub NOTHING { return 3; } my $resObj = "Apache::lonnavmaps::resource"; -# Keep these mappings in sync with lonquickgrades, which uses the colors +# Keep these mappings in sync with lonquickgrades, which usesthe colors # instead of the icons. my %statusIconMap = ( @@ -504,10 +506,13 @@ my %statusIconMap = $resObj->ERROR => '' ); -my %iconAltTags = - ( 'navmap.correct.gif' => 'Correct', - 'navmap.wrong.gif' => 'Incorrect', - 'navmap.open.gif' => 'Open' ); +my %iconAltTags = #texthash does not work here + ( 'navmap.correct.gif' => 'Correct', + 'navmap.wrong.gif' => 'Incorrect', + 'navmap.open.gif' => 'Open', + 'navmap.partial.gif' => 'Partially Correct', + 'navmap.ellipsis.gif' => 'Attempted', + ); # Defines a status->color mapping, null string means don't color my %colormap = @@ -523,6 +528,7 @@ my %colormap = $resObj->OPEN => '', $resObj->NOTHING_SET => '', $resObj->ATTEMPTED => '', + $resObj->CREDIT_ATTEMPTED => '', $resObj->ANSWER_SUBMITTED => '', $resObj->PARTIALLY_CORRECT => '#006600' ); @@ -530,34 +536,8 @@ my %colormap = # is not yet done and due in less than 24 hours my $hurryUpColor = "#FF0000"; -sub close { - if ($env{'environment.remotenavmap'} ne 'on') { return ''; } - return(< -window.status='Accessing Nav Control'; -menu=window.open("/adm/rat/empty.html","loncapanav", - "height=600,width=400,scrollbars=1"); -window.status='Closing Nav Control'; -menu.close(); -window.status='Done.'; - -ENDCLOSE -} - -sub update { - if ($env{'environment.remotenavmap'} ne 'on') { return ''; } - if (!$env{'request.course.id'}) { return ''; } - if ($ENV{'REQUEST_URI'}=~m|^/adm/navmaps|) { return ''; } - return(< - -ENDUPDATE -} - +my $future_slots_checked = 0; +my $future_slots = 0; sub addToFilter { my $hashIn = shift; @@ -594,7 +574,7 @@ sub getLinkForResource { my ($map,$id,$src)=&Apache::lonnet::decode_symb($res->symb()); if ($map=~/\.page$/) { my $url=&Apache::lonnet::clutter($map); - $anchor=&escape($src->shown_symb()); + $anchor=&escape($res->shown_symb()); return ($url,$res->shown_symb(),$anchor); } } @@ -634,6 +614,35 @@ sub getDescription { if ($status == $res->OPEN_LATER) { return &mt("Open ") .timeToHumanString($open,'start'); } + if ($res->simpleStatus($part) == $res->OPEN) { + unless (&Apache::lonnet::allowed('mgr',$env{'request.course.id'})) { + my ($slot_status,$slot_time,$slot_name)=$res->check_for_slot($part); + if ($slot_status == $res->UNKNOWN) { + return &mt('Reservation status unknown'); + } elsif ($slot_status == $res->RESERVED) { + return &mt('Reserved - ends [_1]', + timeToHumanString($slot_time,'end')); + } elsif ($slot_status == $res->RESERVED_LOCATION) { + return &mt('Reserved - specific location(s) - ends [_1]', + timeToHumanString($slot_time,'end')); + } elsif ($slot_status == $res->RESERVED_LATER) { + return &mt('Reserved - next open [_1]', + timeToHumanString($slot_time,'start')); + } elsif ($slot_status == $res->RESERVABLE) { + return &mt('Reservable ending [_1]', + timeToHumanString($slot_time,'end')); + } elsif ($slot_status == $res->RESERVABLE_LATER) { + return &mt('Reservable starting [_1]', + timeToHumanString($slot_time,'start')); + } elsif ($slot_status == $res->NOT_IN_A_SLOT) { + return &mt('Reserve a time/place to work'); + } elsif ($slot_status == $res->NOTRESERVABLE) { + return &mt('Reservation not available'); + } elsif ($slot_status == $res->WAITING_FOR_GRADE) { + return &mt('Submission in grading queue'); + } + } + } if ($status == $res->OPEN) { if ($due) { if ($res->is_practice()) { @@ -663,7 +672,16 @@ sub getDescription { return &mt("Excused by instructor"); } if ($status == $res->ATTEMPTED) { - return &mt("Answer submitted, not yet graded"); + if ($res->is_anonsurvey($part) || $res->is_survey($part)) { + return &mt("Survey submission recorded"); + } else { + 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); @@ -848,11 +866,6 @@ sub render_resource { my $link = $params->{"resourceLink"}; # The URL part is not escaped at this point, but the symb is... - # The stuff to the left of the ? must have ' replaced by \' since - # it will be quoted with ' in the href. - - my ($left,$right) = split(/\?/, $link); - $link = $left.'?'.$right; my $src = $resource->src(); my $it = $params->{"iterator"}; @@ -979,12 +992,8 @@ sub render_resource { $nonLinkedText .= ' ('.&mt('[_1] parts', $resource->countParts()).')'; } - my $target; - if ($env{'environment.remotenavmap'} eq 'on') { - $target=' target="loncapaclient" '; - } if (!$params->{'resource_nolink'} && !$resource->is_sequence() && !$resource->is_empty_sequence) { - $result .= "$curMarkerBegin$title$partLabel$curMarkerEnd$nonLinkedText"; + $result .= "$curMarkerBegin$title$partLabel$curMarkerEnd$nonLinkedText"; } else { $result .= "$curMarkerBegin$linkopen$title$partLabel$curMarkerEnd$nonLinkedText"; } @@ -997,16 +1006,12 @@ sub render_communication_status { my $discussionHTML = ""; my $feedbackHTML = ""; my $errorHTML = ""; my $link = $params->{"resourceLink"}; - my $target; - if ($env{'environment.remotenavmap'} eq 'on') { - $target=' target="loncapaclient" '; - } - my $linkopen = ""; + my $linkopen = ""; my $linkclose = ""; my $location=&Apache::loncommon::lonhttpdurl("/adm/lonMisc"); if ($resource->hasDiscussion()) { $discussionHTML = $linkopen . - ''.&mt('New Discussion').'' . + ''.&mt('New Discussion').'' . $linkclose; } @@ -1014,9 +1019,9 @@ sub render_communication_status { my $feedback = $resource->getFeedback(); foreach my $msgid (split(/\,/, $feedback)) { if ($msgid) { - $feedbackHTML .= ' ' - . ''.&mt('New E-mail').''; + . ''.&mt('New E-mail').''; } } } @@ -1028,9 +1033,9 @@ sub render_communication_status { last if ($errorcount>=10); # Only output 10 bombs maximum if ($msgid) { $errorcount++; - $errorHTML .= ' ' - . ''.&mt('New Error').''; + . ''.&mt('New Error').''; } } } @@ -1048,11 +1053,7 @@ sub render_quick_status { $params->{'multipart'} && $part eq "0"; my $link = $params->{"resourceLink"}; - my $target; - if ($env{'environment.remotenavmap'} eq 'on') { - $target=' target="loncapaclient" '; - } - my $linkopen = ""; + my $linkopen = ""; my $linkclose = ""; $result .= ''; @@ -1063,7 +1064,7 @@ sub render_quick_status { if ($icon) { my $location= &Apache::loncommon::lonhttpdurl("/adm/lonIcons/$icon"); - $result .= "$linkopen$alt$linkclose"; + $result .= $linkopen.''.&mt($alt).''.$linkclose; } else { $result .= " "; } @@ -1089,9 +1090,10 @@ sub render_long_status { } } - if ($resource->kind() eq "res" && + if (($resource->kind() eq "res" && ($resource->is_problem() || $resource->is_practice()) && - !$firstDisplayed) { + !$firstDisplayed) && + $resource->is_raw_problem()) { if ($color) {$result .= ""; } $result .= getDescription($resource, $part); if ($color) {$result .= ""; } @@ -1387,15 +1389,15 @@ sub render { my $location=&Apache::loncommon::lonhttpdurl("/adm/lonMisc"); if ($navmap->{LAST_CHECK}) { $result .= - ' '.&mt('New discussion since').' '. + ' '.&mt('New discussion since').' '. strftime("%A, %b %e at %I:%M %P", localtime($navmap->{LAST_CHECK})). '  '. - ' '.&mt('New message (click to open)').'

'. + ' '.&mt('New message (click to open)').'

'. ''; } else { $result .= '  '. - ' '.&mt('Discussions').''. - '   '.&mt('New message (click to open)'). + ' '.&mt('Discussions').''. + '   '.&mt('New message (click to open)'). ''; } @@ -1405,19 +1407,22 @@ sub render { if ($printCloseAll && !$args->{'resource_no_folder_link'}) { my ($link,$text); if ($condition) { - $link='"navmaps?condition=0&filter=&'.$queryString. - '&here='.&escape($here).'"'; + $link='navmaps?condition=0&filter=&'.$queryString. + '&here='.&escape($here); $text='Close all folders'; } else { - $link='"navmaps?condition=1&filter=&'.$queryString. - '&here='.&escape($here).'"'; + $link='navmaps?condition=1&filter=&'.$queryString. + '&here='.&escape($here); $text='Open all folders'; } + if ($env{'form.register'}) { + $link .= '&register='.$env{'form.register'}; + } if ($args->{'caller'} eq 'navmapsdisplay') { &add_linkitem($args->{'linkitems'},'changefolder', - 'location.href='.$link,$text); + "location.href='$link'",$text); } else { - $result.=''.&mt($text).''; + $result.= ''.&mt($text).''; } $result .= "\n"; } @@ -1433,6 +1438,9 @@ sub render { END + if ($env{'form.register'}) { + $result .= ''; + } if ($args->{'sort'} eq 'discussion') { my $totdisc = 0; my $haveDisc = ''; @@ -1454,26 +1462,16 @@ END $result.=''; } - if ($args->{'caller'} eq 'navmapsdisplay') { $result .= ''; - if ($env{'environment.remotenavmap'} ne 'on') { $result .= ''; - } else { - $result .= ''; - } - $result.=""; + $result.=''; $result.=&show_linkitems_toolbar($args->{'linkitems'}); if ($args->{'sort_html'}) { - if ($env{'environment.remotenavmap'} ne 'on') { - $result.=''. - ''; - } else { - $result.=''; - } - } + $result.=''. + ''; + } $result .= '
'. &Apache::loncommon::help_open_menu('Navigation Screen','Navigation_Screen',undef,'RAT').' 
".mt('Tools:')."'.&mt('Tools:').'   '.$args->{'sort_html'}.'

'. - $args->{'sort_html'}.'
   '.$args->{'sort_html'}.'
'; } elsif ($args->{'sort_html'}) { $result.=$args->{'sort_html'}; @@ -1497,7 +1495,6 @@ END ( $res->NETWORK_FAILURE => 1, $res->NOTHING_SET => 1, $res->CORRECT => 1 ); - my @backgroundColors = ("LC_trEven", "LC_trOdd"); # Shared variables $args->{'counter'} = 0; # counts the rows @@ -1725,14 +1722,13 @@ END if (defined($anchor)) { $anchor='#'.$anchor; } my $srcHasQuestion = $src =~ /\?/; $args->{"resourceLink"} = $src. - ($srcHasQuestion?'&':'?') . + ($srcHasQuestion?'&':'?') . 'symb=' . &escape($symb).$anchor; } # Now, we've decided what parts to show. Loop through them and # show them. foreach my $part (@parts) { $rownum ++; - my $backgroundColor = $backgroundColors[$rownum % scalar(@backgroundColors)]; $result .= &Apache::loncommon::start_data_table_row(); @@ -1788,12 +1784,11 @@ END # it's quite likely this might fix other browsers, too, and # certainly won't hurt anything. if ($displayedJumpMarker) { - $result .= " -"; +"); } $result.=&Apache::loncommon::end_data_table(); @@ -1813,71 +1808,38 @@ sub add_linkitem { $$linkitems{$name}{'text'}=&mt($text); } -sub show_linkitems { - my ($linkitems)=@_; - my @linkorder = ("blank","launchnav","closenav","firsthomework", - "everything","uncompleted","changefolder","clearbubbles"); - - my $result .= (< - -

-   -
'."\n"; - - return $result; -} - sub show_linkitems_toolbar { my ($linkitems,$condition)=@_; - my @linkorder = ("blank","launchnav","closenav","firsthomework", - "everything","uncompleted","changefolder","clearbubbles"); - - my $result .=' - - '."\n
    "; - foreach my $link (@linkorder) { - my $link_id = "LC_content_toolbar_".$link; - if (defined($linkitems->{$link})) { - if ($linkitems->{$link}{'text'} ne '') { - $linkitems->{$link}{'cmd'}=~s/"/'/g; - if($linkitems->{$link}{'cmd'}){ - if($link eq 'changefolder'){ - if($condition){$link_id='LC_content_toolbar_changefolder_toggled'} - else{$link_id='LC_content_toolbar_changefolder'} - } - $result .= '
  • '."\n"; - } - - } - } + my @linkorder = ('firsthomework','everything','uncompleted', + 'changefolder','clearbubbles'); + my $result .=''."\n". + ''."\n". + '
      '; + foreach my $link (@linkorder) { + my $link_id = 'LC_content_toolbar_'.$link; + if (defined($linkitems->{$link})) { + if ($linkitems->{$link}{'text'} ne '') { + $linkitems->{$link}{'cmd'}=~s/"/'/g; + if ($linkitems->{$link}{'cmd'}) { + if ($link eq 'changefolder') { + if ($condition) { + $link_id='LC_content_toolbar_changefolder_toggled'; + } else { + $link_id='LC_content_toolbar_changefolder'; + } + } + $result .= '
    • '. + '
    • '."\n"; + } + } + } } - $result .= '
    '; - $result .= '
    '."\n"; - + $result .= '
'. + '
'."\n"; return $result; } @@ -1969,6 +1931,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} = {}; @@ -2007,8 +1972,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'}; @@ -2059,7 +2025,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; @@ -2069,8 +2035,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')) { @@ -2118,8 +2084,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; @@ -2348,7 +2314,7 @@ resource object. Based on the symb of the resource, get a resource object for that resource. This is one of the proper ways to get a resource object. -=item * B(map_pc): +=item * B(map_pc): Based on the map_pc of the resource, get a resource object for the given map. This is one of the proper ways to get a resource object. @@ -2470,8 +2436,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=''; @@ -2618,7 +2584,7 @@ in the filter function. Retrieves version infomation for a url. Returns the version (a number, or the string "mostrecent") for resources which have version information in the big hash. - + =cut @@ -3503,7 +3469,11 @@ sub navHash { my $self = shift; my $param = shift; my $id = shift; - return $self->{NAV_MAP}->navhash($param . ($id?$self->{ID}:"")); + my $arg = $param . ($id?$self->{ID}:""); + if (ref($self) && ref($self->{NAV_MAP}) && defined($arg)) { + return $self->{NAV_MAP}->navhash($arg); + } + return; } =pod @@ -3746,6 +3716,35 @@ sub is_problem { } return 0; } +# +# The has below is the set of status that are considered 'incomplete' +# +my %incomplete_hash = +( + TRIES_LEFT() => 1, + OPEN() => 1, + ATTEMPTED() => 1 + + ); +# +# Return tru if a problem is incomplete... for now incomplete means that +# any part of the problem is incomplete. +# Note that if the resources is not a problem, 0 is returned. +# +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; + } + } + } + return 0; + +} sub is_raw_problem { my $self=shift; my $src = $self->src(); @@ -3781,7 +3780,7 @@ sub is_survey { my $self = shift(); my $part = shift(); my $type = $self->parmval('type',$part); - if ($type eq 'survey') { + if (($type eq 'survey') || ($type eq 'surveycred')) { return 1; } if ($self->src() =~ /\.(survey)$/) { @@ -3789,6 +3788,15 @@ sub is_survey { } return 0; } +sub is_anonsurvey { + my $self = shift(); + my $part = shift(); + my $type = $self->parmval('type',$part); + if (($type eq 'anonsurvey') || ($type eq 'anonsurveycred')) { + return 1; + } + return 0; +} sub is_task { my $self=shift; my $src = $self->src(); @@ -3842,6 +3850,12 @@ resource of the map. Returns a string with the type of the map in it. +=item *B: + +Returns a string with a comma-separated ordered list of map_pc IDs +for the hierarchy of maps containing a map, with the top level +map first, then descending to deeper levels, with the enclosing map last. + =back =cut @@ -3872,6 +3886,11 @@ sub map_type { my $pc = $self->map_pc(); return $self->navHash("map_type_$pc", 0); } +sub map_hierarchy { + my $self = shift; + my $pc = $self->map_pc(); + return $self->navHash("map_hierarchy_$pc", 0); +} ##### # Property queries @@ -3977,6 +3996,35 @@ sub awarded { if (!defined($part)) { $part = '0'; } return $self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$part.'.awarded'}; } +sub taskversion { + 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.'.version'}; +} +sub taskstatus { + 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.'.$self->taskversion($part).'.'.$part.'.status'}; +} +sub solved { + 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.'.solved'}; +} +sub checkedin { + my $self = shift; my $part = shift; + $self->{NAV_MAP}->get_user_data(); + if (!defined($part)) { $part = '0'; } + if ($self->is_task()) { + my $version = $self->taskversion($part); + return ($self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$version .'.'.$part.'.checkedin'},$self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$version .'.'.$part.'.checkedin.slot'}); + } else { + return ($self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$part.'.checkedin'},$self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$part.'.checkedin.slot'}); + } +} # this should work exactly like the copy in lonhomework.pm sub duedate { (my $self, my $part) = @_; @@ -4057,8 +4105,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; } @@ -4072,6 +4120,14 @@ sub part_display { } return $display; } +sub slot_control { + my $self=shift(); my $part = shift(); + if (!defined($part)) { $part = '0'; } + my $useslots = $self->parmval("useslots", $part); + my $availablestudent = $self->parmval("availablestudent", $part); + my $available = $self->parmval("available", $part); + return ($useslots,$availablestudent,$available); +} # Multiple things need this sub getReturnHash { @@ -4504,7 +4560,7 @@ sub OPEN { return 1; } 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 NOTHING_SET { return 5; } sub NETWORK_FAILURE { return 100; } # getDateStatus gets the date status for a given problem part. @@ -4590,6 +4646,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 @@ -4601,6 +4661,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; @@ -4619,6 +4680,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; } @@ -4708,6 +4776,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. @@ -4716,9 +4788,20 @@ An answer has been submitted, but the st =cut -sub TRIES_LEFT { return 20; } -sub ANSWER_SUBMITTED { return 21; } -sub PARTIALLY_CORRECT{ return 22; } +sub TRIES_LEFT { return 20; } +sub ANSWER_SUBMITTED { return 21; } +sub PARTIALLY_CORRECT { return 22; } + +sub RESERVED_LATER { return 30; } +sub RESERVED { return 31; } +sub RESERVED_LOCATION { return 32; } +sub RESERVABLE { return 33; } +sub RESERVABLE_LATER { return 34; } +sub NOTRESERVABLE { return 35; } +sub NOT_IN_A_SLOT { return 36; } +sub NEEDS_CHECKIN { return 37; } +sub WAITING_FOR_GRADE { return 38; } +sub UNKNOWN { return 39; } sub status { my $self = shift; @@ -4770,6 +4853,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; @@ -4811,6 +4898,108 @@ sub status { return OPEN; } +sub check_for_slot { + my $self = shift; + my $part = shift; + my ($use_slots,$available,$availablestudent) = $self->slot_control($part); + if (($use_slots ne '') && ($use_slots !~ /^\s*no\s*$/i)) { + my @slots = (split(/:/,$availablestudent),split(/:/,$available)); + my $cid=$env{'request.course.id'}; + my $cdom=$env{'course.'.$cid.'.domain'}; + my $cnum=$env{'course.'.$cid.'.num'}; + my $now = time; + if (@slots > 0) { + my %slots=&Apache::lonnet::get('slots',[@slots],$cdom,$cnum); + if (&Apache::lonnet::error(%slots)) { + return (UNKNOWN); + } + my @sorted_slots = &Apache::loncommon::sorted_slots(\@slots,\%slots); + my ($checkedin,$checkedinslot); + foreach my $slot_name (@sorted_slots) { + next if (!defined($slots{$slot_name}) || + !ref($slots{$slot_name})); + my $end = $slots{$slot_name}->{'endtime'}; + my $start = $slots{$slot_name}->{'starttime'}; + my $ip = $slots{$slot_name}->{'ip'}; + if ($self->simpleStatus() == OPEN) { + my $startreserve = $slots{$slot_name}->{'startreserve'}; + my @proctors; + if ($slots{$slot_name}->{'proctor'} ne '') { + @proctors = split(',',$slots{$slot_name}->{'proctor'}); + } + if ($end > $now) { + ($checkedin,$checkedinslot) = $self->checkedin(); + if ($startreserve < $now) { + if ($start > $now) { + return (RESERVED_LATER,$start,$slot_name); + } else { + if ($ip ne '') { + if (!&Apache::loncommon::check_ip_acc($ip)) { + return (RESERVED_LOCATION,$ip,$slot_name); + } + } + if (@proctors > 0) { + unless ((grep(/^\Q$checkedin\E/,@proctors)) && + ($checkedinslot eq $slot_name)) { + return (NEEDS_CHECKIN,undef,$slot_name); + } + } + return (RESERVED,$end,$slot_name); + } + } else { + if ($start > $now) { + return (RESERVABLE,$startreserve,$slot_name); + } + } + } + } + } + my ($is_correct,$got_grade); + if ($self->is_task()) { + my $taskstatus = $self->taskstatus(); + $is_correct = (($taskstatus eq 'pass') || + ($self->solved() =~ /^correct_/)); + $got_grade = ($self->solved() =~ /^(?:pass|fail)$/); + } else { + $got_grade = 1; + $is_correct = ($self->solved() =~ /^correct_/); + } + ($checkedin,$checkedinslot) = $self->checkedin(); + if ($checkedin) { + if (!$got_grade) { + return (WAITING_FOR_GRADE); + } elsif ($is_correct) { + return (CORRECT); + } + } + return(NOT_IN_A_SLOT); + } else { + if (!$future_slots_checked) { + $future_slots = &get_future_slots($cdom,$cnum,$now); + $future_slots_checked = 1; + } + if ($future_slots) { + return(NOT_IN_A_SLOT); + } + return(NOTRESERVABLE); + } + } + return; +} + +sub get_future_slots { + my ($cdom,$cnum,$now) = @_; + my %slots=&Apache::lonnet::dump('slots',$cdom,$cnum); + my $future_slots = 0; + foreach my $slot (keys(%slots)) { + if (($slots{$slot}->{'starttime'} > $now) && + ($slots{$slot}->{'endtime'} > $now)) { + $future_slots ++; + } + } + return $future_slots; +} + sub CLOSED { return 23; } sub ERROR { return 24; } @@ -4860,6 +5049,7 @@ my %compositeToSimple = INCORRECT() => INCORRECT, OPEN() => OPEN, ATTEMPTED() => ATTEMPTED, + CREDIT_ATTEMPTED() => CORRECT, ANSWER_SUBMITTED() => ATTEMPTED ); @@ -4934,6 +5124,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;