File:  [LON-CAPA] / loncom / interface / lonplacementtest.pm
Revision 1.2: download - view: text, annotated - select for diffs
Mon Apr 4 15:56:58 2016 UTC (8 years, 2 months ago) by raeburn
Branches: MAIN
CVS tags: HEAD
- Bug 6808. New course container -- "Placement" for Placement Tests.

    1: # The LearningOnline Network with CAPA
    2: # Handler to manage dependencies for HTML files uploaded directly
    3: # to a course.
    4: #
    5: # $Id: lonplacementtest.pm,v 1.2 2016/04/04 15:56:58 raeburn Exp $
    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);
  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;
  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: 
  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: 
  243: sub showresult {
  244:     my ($complete,$inhibitmenu) = @_;
  245:     my ($score) = &Apache::lonplacementtest::check_completion(1,undef,1);
  246:     my %aclt = &test_action_text();
  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:     }
  254:     if ($complete) {
  255:         $output .= '<p class="LC_info">'.&mt('Test is complete').'</p>';
  256:     }
  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;
  266: }
  267: 
  268: sub showincomplete {
  269:     my ($incomplete,$inhibitmenu) = @_;
  270:     my %aclt = &test_action_text();
  271:     my $output;
  272:     if ($incomplete == 100) {
  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:                       ]);
  286:     } elsif ($incomplete) {
  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();
  304:     }
  305:     return $output;
  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>