Annotation of loncom/interface/lonplacementtest.pm, revision 1.5

1.1       raeburn     1: # The LearningOnline Network with CAPA
1.5     ! raeburn     2: # Handler for a Placement Test course container 
1.1       raeburn     3: #
1.5     ! raeburn     4: # $Id: lonplacementtest.pm,v 1.4 2016/04/14 15:43:45 raeburn Exp $
1.1       raeburn     5: #
                      6: # Copyright Michigan State University Board of Trustees
                      7: #
                      8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
                      9: #
                     10: # LON-CAPA is free software; you can redistribute it and/or modify
                     11: # it under the terms of the GNU General Public License as published by
                     12: # the Free Software Foundation; either version 2 of the License, or
                     13: # (at your option) any later version.
                     14: #
                     15: # LON-CAPA is distributed in the hope that it will be useful,
                     16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     18: # GNU General Public License for more details.
                     19: #
                     20: # You should have received a copy of the GNU General Public License
                     21: # along with LON-CAPA; if not, write to the Free Software
1.5     ! raeburn    22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
1.4       raeburn    23: #
1.1       raeburn    24: # /home/httpd/html/adm/gpl.txt
                     25: #
                     26: # http://www.lon-capa.org/
                     27: #
                     28: #
                     29: ###############################################################
                     30: ###############################################################
                     31: 
                     32: =pod
                     33: 
                     34: =head1 NAME
                     35: 
                     36: lonplacementtest - Handler to provide initial screen for student 
                     37: after log-in and/or role selection for a Placement Test course container
                     38: which is currently partially completed.
                     39: 
                     40: =head1 SYNOPSIS
                     41: 
                     42: lonplacementtest provides an interface for the student to choose 
                     43: whether to continue with an existing, partially completed test,
                     44: or whether to start a new test. Also provides utility functions
                     45: used to compute/export scores, and increment the course-wide maxtries
                     46: parameter for the user, when an instance of a test is complete. 
                     47: 
                     48: =head1 DESCRIPTION
                     49: 
                     50: This module is used after student log-in and/or role selection
                     51: for a Placement Test course contained, if there is a current,
                     52: partially completed version of the test.  The student is prompted
                     53: to choose whether to continue with the current test or start a
                     54: new one.
                     55: 
                     56: =head1 INTERNAL SUBROUTINES
                     57: 
                     58: =over
                     59: 
                     60: =item check_completion()
                     61: 
                     62: =back
                     63: 
                     64: =cut
                     65: 
                     66: package Apache::lonplacementtest;
                     67: 
                     68: use strict;
                     69: use Apache::Constants qw(:common :http);
                     70: use Apache::lonnet;
                     71: use Apache::loncommon;
                     72: use Apache::lonhtmlcommon;
                     73: use Apache::lonnavmaps;
                     74: use Apache::lonpageflip;
                     75: use Apache::lonroles;
                     76: use Apache::lonparmset;
                     77: use Apache::lonlocal;
                     78: use LONCAPA qw(:DEFAULT :match);
                     79: 
                     80: sub handler {
                     81:     my $r=shift;
                     82:     if ($r->header_only) {
                     83:         &Apache::loncommon::content_type($r,'text/html');
                     84:         $r->send_http_header;
                     85:         return OK;
                     86:     }
                     87:     if (!$env{'request.course.fn'}) {
                     88:         # Not in a course.
                     89:         $env{'user.error.msg'}="/adm/lonplacementtest:bre:0:0:Not in a course";
                     90:         return HTTP_NOT_ACCEPTABLE;
                     91:     } elsif ($env{'course.'.$env{'request.course.id'}.'.type'} ne 'Placement') {
                     92:         # Not in a Placement Test
                     93:         $env{'user.error.msg'}="/adm/lonplacementtest:bre:0:0:Not in a placement test";
                     94:         return HTTP_NOT_ACCEPTABLE;
                     95:     }
                     96:     &Apache::loncommon::content_type($r,'text/html');
                     97:     $r->send_http_header;
                     98:     my ($totalpoints,$incomplete) = &check_completion(undef,undef,1);
                     99:     if (($incomplete) && ($incomplete < 100) && (!$env{'request.role.adv'})) {
                    100:         $r->print(&showincomplete($incomplete));
                    101:     } else {
                    102:         my $furl = &Apache::lonpageflip::first_accessible_resource();
                    103:         my $cdesc = $env{'course.'.$env{'request.course.id'}.'.description'};
                    104:         my $msg = &mt('Entering [_1] ...',$cdesc);
                    105:         &Apache::lonroles::redirect_user($r, &mt('Entering [_1]',$cdesc),$furl, $msg);
                    106:     }
                    107:     return OK;
                    108: }
                    109: 
                    110: sub check_completion {
                    111:     my ($makenew,$map,$recursive) = @_;
                    112:     my $navmap = Apache::lonnavmaps::navmap->new();
                    113:     return unless (ref($navmap));
                    114:     my @resources = $navmap->retrieveResources($map,
                    115:                                                sub { $_[0]->is_problem() },$recursive);
                    116:     my $currmax = 0;
                    117:     my $totalpoints = 0;
                    118:     my $totaldone = 0;
                    119:     my $totalnotdone = 0;
                    120:     my $incomplete;
                    121:     if (@resources) {
                    122:         my $firstsymb = $resources[0]->symb();
1.5     ! raeburn   123:         my (%bytitle,%bysymb);
1.1       raeburn   124:         foreach my $res (@resources) {
                    125:             my $currsymb = $res->symb();
                    126:             my $title = $res->compTitle;
                    127:             unless (exists($bytitle{$title})) {
                    128:                 $bytitle{$title} = 0;
                    129:             }
1.5     ! raeburn   130:             unless (exists($bysymb{$currsymb})) {
        !           131:                 $bysymb{$currsymb} = 0;
        !           132:             }
1.1       raeburn   133:             my $notdone = 0;
                    134:             my $done = 0;
                    135:             my %storetries;
                    136:             my $points = 0;
                    137:             foreach my $part (@{$res->parts()}) {
                    138:                 my $tries = $res->tries($part);
                    139:                 my $maxtries = $res->maxtries($part);
                    140:                 if ($currmax < $maxtries) {
                    141:                     $currmax = $maxtries;
                    142:                 }
                    143:                 if ($tries < $maxtries) {
                    144:                     $notdone ++;
                    145:                     my $tries = $res->tries($part);
1.2       raeburn   146:                     if ($makenew) {
                    147:                         my @response_ids = $res->responseIds($part);
                    148:                         if (@response_ids) {
                    149:                             foreach my $id (@response_ids) {
                    150:                                 $storetries{"resource.$part.$id.awarded"}=0;
                    151:                                 $storetries{"resource.$part.$id.awarddetail"}='ASSIGNED_SCORE';
                    152:                             }
                    153:                             $storetries{"resource.$part.tries"}=$maxtries;
                    154:                             $storetries{"resource.$part.solved"}='incorrect_by_override';
                    155:                             $storetries{"resource.$part.award"}='ASSIGNED_SCORE';
                    156:                             $storetries{"resource.$part.awarded"}=0;
1.1       raeburn   157:                         }
                    158:                     }
                    159:                 } else {
                    160:                     my $awarded = $res->awarded($part);
                    161:                     my $weight = $res->weight($part);
                    162:                     $points += $awarded * $weight;
                    163:                     $done ++;
                    164:                 }
                    165:             }
                    166:             if ($notdone) {
                    167:                 $totalnotdone += $notdone;
                    168:                 if ($makenew && keys(%storetries)) {
                    169:                     my $result=&Apache::lonnet::cstore(\%storetries,$currsymb,$env{'request.course.id'},
                    170:                                                        $env{'user.domain'},$env{'user.name'});
                    171:                 }
                    172:             }
                    173:             if ($done) {
                    174:                 $totaldone += $done;
                    175:             }
                    176:             $bytitle{$title} += $points;
1.5     ! raeburn   177:             $bysymb{$currsymb} += $points;
1.1       raeburn   178:             $totalpoints += $points;
                    179:         }
                    180:         if ($makenew) {
                    181:             my $newmax = $currmax + 1;
                    182:             my $result =
                    183:                 &Apache::lonparmset::storeparm_by_symb_inner($firstsymb,'0_maxtries',
                    184:                                                              4,$newmax,'int_pos',
                    185:                                                              $env{'user.name'},
                    186:                                                              $env{'user.domain'});
1.5     ! raeburn   187:             my $user = $env{'user.name'}.':'.$env{'user.domain'};
        !           188:             if ($user) {
        !           189:                 my %grades = (
        !           190:                                $user => {
        !           191:                                           role            => $env{'request.role'},
        !           192:                                           id              => $env{'environment.id'},
        !           193:                                           status          => $env{'environment.inststatus'},
        !           194:                                           lastname        => $env{'environment.lastname'},
        !           195:                                           firstname       => $env{'environment.firstname'},
        !           196:                                           permanentemail  => $env{'environment.permanentemail'},
        !           197:                                           section         => $env{'request.course.sec'},
        !           198:                                           total           => $totalpoints,
        !           199:                                           category        => '',
        !           200:                                           gradebookcolumn => '',
        !           201:                                           context         => $map,
        !           202:                                         },
        !           203:                              );
        !           204:                 $grades{$user}{bytitle} = \%bytitle;
        !           205:                 $grades{$user}{bysymb} = \%bysymb;
        !           206:                 my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
        !           207:                 my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
        !           208:                 my $scope = 'map';
        !           209:                 my $instcode = $env{'course.'.$env{'request.course.id'}.'.internal.coursecode'};
        !           210:                 my $crstype = $env{'course.'.$env{'request.course.id'}.'.type'};
        !           211:                 my $context = 'completion';
        !           212:                 if (($cnum ne '') && ($cdom ne '')) {
        !           213:                     my %info = (
        !           214:                                   scope    => $scope,
        !           215:                                   instcode => $instcode,
        !           216:                                   crstype  => $crstype,
        !           217:                                   context  => $context, 
        !           218:                                ); 
        !           219:                     my $response = &Apache::lonnet::auto_export_grades($cdom,$cnum,\%info,\%grades);
        !           220:                     my $outcome;
        !           221:                     if (ref($response) eq 'HASH') {
        !           222:                         if ($response->{$user}) {
        !           223:                             $outcome = 'ok';
        !           224:                         } else {
        !           225:                             $outcome = 'fail';
        !           226:                         }
        !           227:                     } else {
        !           228:                         $outcome = $response;
        !           229:                     }
        !           230:                     unless ($outcome eq 'ok') {
        !           231:                         &Apache::lonnet::logthis("Placement Test grade export for $env{'user.name'}:$env{'user.domain'} in $env{'request.course.id'} was: $outcome"); 
        !           232:                     }
        !           233:                 }
1.1       raeburn   234:             }
                    235:         }
                    236:     }
                    237:     my $totalparts = $totalnotdone + $totaldone;
                    238:     if (($totalparts) && ($totalnotdone)) {
                    239:         if (!$totaldone) {
                    240:             $incomplete = 100;
                    241:         } else {
                    242:             my $undonepct = (100*$totalnotdone)/$totalparts;
                    243:             $incomplete = sprintf("%0d",$undonepct);
                    244:         }
                    245:     }
                    246:     return ($totalpoints,$incomplete);
                    247: }
                    248: 
1.2       raeburn   249: sub is_lastres {
                    250:     my ($symb,$navmap) = @_;
                    251:     return unless (ref($navmap));
                    252:     my $numforward = 0;
                    253:     my $currRes = $navmap->getBySymb($symb);
                    254:     if (ref($currRes)) {
                    255:         my $it = $navmap->getIterator($currRes,undef,undef,1);
                    256:         while ( my $res=$it->next()) {
                    257:             if (ref($res)) {
                    258:                 unless ($res->symb() eq $symb) {
                    259:                     $numforward ++;
                    260:                 }
                    261:             }
                    262:         }
                    263:     }
                    264:     if (!$numforward) {
                    265:         return 1;
                    266:     }
                    267:     return;
                    268: }
                    269: 
                    270: sub has_tries {
                    271:     my ($symb,$navmap) = @_;
                    272:     return unless (ref($navmap));
                    273:     my $currRes = $navmap->getBySymb($symb);
                    274:     if (ref($currRes)) {
                    275:         if ($currRes->is_problem()) {
                    276:             if ($currRes->tries < $currRes->maxtries) {
                    277:                 return 1;
                    278:             }
                    279:         }
                    280:     }
                    281:     return;
                    282: }
                    283: 
1.1       raeburn   284: sub showresult {
1.2       raeburn   285:     my ($complete,$inhibitmenu) = @_;
1.1       raeburn   286:     my ($score) = &Apache::lonplacementtest::check_completion(1,undef,1);
                    287:     my %aclt = &test_action_text();
1.2       raeburn   288:     my $output;
1.3       raeburn   289:     if ($inhibitmenu) {
                    290:         $output = '<hr />';
                    291:     } else {
1.2       raeburn   292:         my $brcrum = [{'href' => '/adm/flip?postdata=firstres%3a',
                    293:                        'text' => 'Test Status'},];
                    294:         $output = &Apache::loncommon::start_page('Placement Test Completed',
                    295:                                                  undef,{bread_crumbs=>$brcrum});
                    296:     }
1.1       raeburn   297:     if ($complete) {
                    298:         $output .= '<p class="LC_info">'.&mt('Test is complete').'</p>';
                    299:     }
1.2       raeburn   300:     $output .= '<p>'.&mt('You scored [quant,_1,point].',$score).'</p>'
                    301:               .&Apache::lonhtmlcommon::actionbox(
                    302:                   ['<a href="/adm/flip?postdata=firstres%3a">'.$aclt{'newt'}.'</a></li>',
                    303:                    '<a href="/adm/logout">'.$aclt{'exit'}.'</a></li>',
                    304:                   ]);
                    305:     unless ($inhibitmenu) {
                    306:         $output .= &Apache::loncommon::end_page();
                    307:     }
                    308:     return $output;
1.1       raeburn   309: }
                    310: 
                    311: sub showincomplete {
1.2       raeburn   312:     my ($incomplete,$inhibitmenu) = @_;
1.1       raeburn   313:     my %aclt = &test_action_text();
1.2       raeburn   314:     my $output;
1.1       raeburn   315:     if ($incomplete == 100) {
1.3       raeburn   316:         if ($inhibitmenu) {
                    317:             $output = '<hr />';
                    318:         } else {
1.2       raeburn   319:             my $brcrum = [{'href' => '/adm/flip?postdata=firstres%3a',
                    320:                            'text' => 'Test Status'},];
                    321:             $output = &Apache::loncommon::start_page('Placement Test Unattempted',
                    322:                                                      undef,{bread_crumbs=>$brcrum});
                    323:         }
                    324:         $output .= '<p class="LC_warning">'.&mt('Your Placement Test is incomplete.').'<p></p>'
                    325:                   .&mt('Currently, you have not submitted any answers for any of the questions.')
                    326:                   .'</p>'
                    327:                   .&Apache::lonhtmlcommon::actionbox(
                    328:                       ['<a href="/adm/flip?postdata=firstres%3a">'.$aclt{'begin'}.'</a></li>',
                    329:                        '<a href="/adm/logout">'.$aclt{'exit'}.'</a></li>',
                    330:                       ]);
1.1       raeburn   331:     } elsif ($incomplete) {
1.3       raeburn   332:         if ($inhibitmenu) {
                    333:             $output = '<hr />';
                    334:         } else {
1.2       raeburn   335:             my $brcrum = [{'href' => '/adm/flip?postdata=endplacement%3a',
                    336:                            'text' => 'Test Status'},];
                    337:             $output .=  &Apache::loncommon::start_page('Incomplete Placement Test',
                    338:                                                       undef,{bread_crumbs=>$brcrum});
                    339:         }
                    340:         $output .= '<p class="LC_warning">'.&mt('Your Placement Test is incomplete.').'<p></p>'
                    341:                   .&mt('Currently, you have not provided an answer for [_1]% of the questions.',$incomplete)
                    342:                   .'</p>'
                    343:                   .&Apache::lonhtmlcommon::actionbox(
                    344:                       ['<a href="/adm/flip?postdata=endplacement%3a">'.$aclt{'endt'}.'</a></li>',
                    345:                        '<a href="/adm/flip?postdata=firstanswerable%3a">'.$aclt{'comp'}.'</a></li>',
                    346:                        '<a href="/adm/logout">'.$aclt{'exit'}.'</a></li>',
                    347:                       ]);
                    348:     }
                    349:     unless ($inhibitmenu) {
                    350:         $output .= &Apache::loncommon::end_page();
1.1       raeburn   351:     }
1.2       raeburn   352:     return $output;
1.1       raeburn   353: }
                    354: 
                    355: sub test_action_text {
                    356:     return &Apache::lonlocal::texthash(
                    357:                                         'exit'  => 'Logout',
                    358:                                         'newt'  => 'Start a new test',
                    359:                                         'endt'  => 'Mark test as completed',
                    360:                                         'comp'  => 'Go to first unanswered question',
                    361:                                         'begin' => 'Go to start',
                    362:                                       );
                    363: }
                    364: 
                    365: 1;

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>