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

1.1       raeburn     1: # The LearningOnline Network with CAPA
                      2: # Handler to manage dependencies for HTML files uploaded directly
                      3: # to a course.
                      4: #
1.2     ! raeburn     5: # $Id: lonplacementtest.pm,v 1.1 2016/04/02 04:30:21 raeburn Exp $
1.1       raeburn     6: #
                      7: # Copyright Michigan State University Board of Trustees
                      8: #
                      9: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
                     10: #
                     11: # LON-CAPA is free software; you can redistribute it and/or modify
                     12: # it under the terms of the GNU General Public License as published by
                     13: # the Free Software Foundation; either version 2 of the License, or
                     14: # (at your option) any later version.
                     15: #
                     16: # LON-CAPA is distributed in the hope that it will be useful,
                     17: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     18: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     19: # GNU General Public License for more details.
                     20: #
                     21: # You should have received a copy of the GNU General Public License
                     22: # along with LON-CAPA; if not, write to the Free Software
                     23: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA#
                     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();
                    123:         my %bytitle;
                    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:             }
                    130:             my $notdone = 0;
                    131:             my $done = 0;
                    132:             my %storetries;
                    133:             my $points = 0;
                    134:             foreach my $part (@{$res->parts()}) {
                    135:                 my $tries = $res->tries($part);
                    136:                 my $maxtries = $res->maxtries($part);
                    137:                 if ($currmax < $maxtries) {
                    138:                     $currmax = $maxtries;
                    139:                 }
                    140:                 if ($tries < $maxtries) {
                    141:                     $notdone ++;
                    142:                     my $tries = $res->tries($part);
1.2     ! raeburn   143:                     if ($makenew) {
        !           144:                         my @response_ids = $res->responseIds($part);
        !           145:                         if (@response_ids) {
        !           146:                             foreach my $id (@response_ids) {
        !           147:                                 $storetries{"resource.$part.$id.awarded"}=0;
        !           148:                                 $storetries{"resource.$part.$id.awarddetail"}='ASSIGNED_SCORE';
        !           149:                             }
        !           150:                             $storetries{"resource.$part.tries"}=$maxtries;
        !           151:                             $storetries{"resource.$part.solved"}='incorrect_by_override';
        !           152:                             $storetries{"resource.$part.award"}='ASSIGNED_SCORE';
        !           153:                             $storetries{"resource.$part.awarded"}=0;
1.1       raeburn   154:                         }
                    155:                     }
                    156:                 } else {
                    157:                     my $awarded = $res->awarded($part);
                    158:                     my $weight = $res->weight($part);
                    159:                     $points += $awarded * $weight;
                    160:                     $done ++;
                    161:                 }
                    162:             }
                    163:             if ($notdone) {
                    164:                 $totalnotdone += $notdone;
                    165:                 if ($makenew && keys(%storetries)) {
                    166:                     my $result=&Apache::lonnet::cstore(\%storetries,$currsymb,$env{'request.course.id'},
                    167:                                                        $env{'user.domain'},$env{'user.name'});
                    168:                 }
                    169:             }
                    170:             if ($done) {
                    171:                 $totaldone += $done;
                    172:             }
                    173:             $bytitle{$title} += $points;
                    174:             $totalpoints += $points;
                    175:         }
                    176:         if ($makenew) {
                    177:             my $newmax = $currmax + 1;
                    178:             my $result =
                    179:                 &Apache::lonparmset::storeparm_by_symb_inner($firstsymb,'0_maxtries',
                    180:                                                              4,$newmax,'int_pos',
                    181:                                                              $env{'user.name'},
                    182:                                                              $env{'user.domain'});
                    183:             my %grades = (
                    184:                              uname   => $env{'user.name'},
                    185:                              domain  => $env{'user.domain'},
                    186:                              total   => $totalpoints,
                    187:                              bytitle => \%bytitle,
                    188:                          );
                    189:             my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
                    190:             my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                    191:             if (($cnum ne '') && ($cdom ne '')) {
                    192:                 &Apache::lonnet::auto_export_grades($cnum,$cdom,\%grades);
                    193:             }
                    194:         }
                    195:     }
                    196:     my $totalparts = $totalnotdone + $totaldone;
                    197:     if (($totalparts) && ($totalnotdone)) {
                    198:         if (!$totaldone) {
                    199:             $incomplete = 100;
                    200:         } else {
                    201:             my $undonepct = (100*$totalnotdone)/$totalparts;
                    202:             $incomplete = sprintf("%0d",$undonepct);
                    203:         }
                    204:     }
                    205:     return ($totalpoints,$incomplete);
                    206: }
                    207: 
1.2     ! raeburn   208: sub is_lastres {
        !           209:     my ($symb,$navmap) = @_;
        !           210:     return unless (ref($navmap));
        !           211:     my $numforward = 0;
        !           212:     my $currRes = $navmap->getBySymb($symb);
        !           213:     if (ref($currRes)) {
        !           214:         my $it = $navmap->getIterator($currRes,undef,undef,1);
        !           215:         while ( my $res=$it->next()) {
        !           216:             if (ref($res)) {
        !           217:                 unless ($res->symb() eq $symb) {
        !           218:                     $numforward ++;
        !           219:                 }
        !           220:             }
        !           221:         }
        !           222:     }
        !           223:     if (!$numforward) {
        !           224:         return 1;
        !           225:     }
        !           226:     return;
        !           227: }
        !           228: 
        !           229: sub has_tries {
        !           230:     my ($symb,$navmap) = @_;
        !           231:     return unless (ref($navmap));
        !           232:     my $currRes = $navmap->getBySymb($symb);
        !           233:     if (ref($currRes)) {
        !           234:         if ($currRes->is_problem()) {
        !           235:             if ($currRes->tries < $currRes->maxtries) {
        !           236:                 return 1;
        !           237:             }
        !           238:         }
        !           239:     }
        !           240:     return;
        !           241: }
        !           242: 
1.1       raeburn   243: sub showresult {
1.2     ! raeburn   244:     my ($complete,$inhibitmenu) = @_;
1.1       raeburn   245:     my ($score) = &Apache::lonplacementtest::check_completion(1,undef,1);
                    246:     my %aclt = &test_action_text();
1.2     ! raeburn   247:     my $output;
        !           248:     unless ($inhibitmenu) {
        !           249:         my $brcrum = [{'href' => '/adm/flip?postdata=firstres%3a',
        !           250:                        'text' => 'Test Status'},];
        !           251:         $output = &Apache::loncommon::start_page('Placement Test Completed',
        !           252:                                                  undef,{bread_crumbs=>$brcrum});
        !           253:     }
1.1       raeburn   254:     if ($complete) {
                    255:         $output .= '<p class="LC_info">'.&mt('Test is complete').'</p>';
                    256:     }
1.2     ! raeburn   257:     $output .= '<p>'.&mt('You scored [quant,_1,point].',$score).'</p>'
        !           258:               .&Apache::lonhtmlcommon::actionbox(
        !           259:                   ['<a href="/adm/flip?postdata=firstres%3a">'.$aclt{'newt'}.'</a></li>',
        !           260:                    '<a href="/adm/logout">'.$aclt{'exit'}.'</a></li>',
        !           261:                   ]);
        !           262:     unless ($inhibitmenu) {
        !           263:         $output .= &Apache::loncommon::end_page();
        !           264:     }
        !           265:     return $output;
1.1       raeburn   266: }
                    267: 
                    268: sub showincomplete {
1.2     ! raeburn   269:     my ($incomplete,$inhibitmenu) = @_;
1.1       raeburn   270:     my %aclt = &test_action_text();
1.2     ! raeburn   271:     my $output;
1.1       raeburn   272:     if ($incomplete == 100) {
1.2     ! raeburn   273:         unless ($inhibitmenu) {
        !           274:             my $brcrum = [{'href' => '/adm/flip?postdata=firstres%3a',
        !           275:                            'text' => 'Test Status'},];
        !           276:             $output = &Apache::loncommon::start_page('Placement Test Unattempted',
        !           277:                                                      undef,{bread_crumbs=>$brcrum});
        !           278:         }
        !           279:         $output .= '<p class="LC_warning">'.&mt('Your Placement Test is incomplete.').'<p></p>'
        !           280:                   .&mt('Currently, you have not submitted any answers for any of the questions.')
        !           281:                   .'</p>'
        !           282:                   .&Apache::lonhtmlcommon::actionbox(
        !           283:                       ['<a href="/adm/flip?postdata=firstres%3a">'.$aclt{'begin'}.'</a></li>',
        !           284:                        '<a href="/adm/logout">'.$aclt{'exit'}.'</a></li>',
        !           285:                       ]);
1.1       raeburn   286:     } elsif ($incomplete) {
1.2     ! raeburn   287:         unless ($inhibitmenu) {
        !           288:             my $brcrum = [{'href' => '/adm/flip?postdata=endplacement%3a',
        !           289:                            'text' => 'Test Status'},];
        !           290:             $output .=  &Apache::loncommon::start_page('Incomplete Placement Test',
        !           291:                                                       undef,{bread_crumbs=>$brcrum});
        !           292:         }
        !           293:         $output .= '<p class="LC_warning">'.&mt('Your Placement Test is incomplete.').'<p></p>'
        !           294:                   .&mt('Currently, you have not provided an answer for [_1]% of the questions.',$incomplete)
        !           295:                   .'</p>'
        !           296:                   .&Apache::lonhtmlcommon::actionbox(
        !           297:                       ['<a href="/adm/flip?postdata=endplacement%3a">'.$aclt{'endt'}.'</a></li>',
        !           298:                        '<a href="/adm/flip?postdata=firstanswerable%3a">'.$aclt{'comp'}.'</a></li>',
        !           299:                        '<a href="/adm/logout">'.$aclt{'exit'}.'</a></li>',
        !           300:                       ]);
        !           301:     }
        !           302:     unless ($inhibitmenu) {
        !           303:         $output .= &Apache::loncommon::end_page();
1.1       raeburn   304:     }
1.2     ! raeburn   305:     return $output;
1.1       raeburn   306: }
                    307: 
                    308: sub test_action_text {
                    309:     return &Apache::lonlocal::texthash(
                    310:                                         'exit'  => 'Logout',
                    311:                                         'newt'  => 'Start a new test',
                    312:                                         'endt'  => 'Mark test as completed',
                    313:                                         'comp'  => 'Go to first unanswered question',
                    314:                                         'begin' => 'Go to start',
                    315:                                       );
                    316: }
                    317: 
                    318: 1;

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