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>