Annotation of loncom/interface/lonplacementtest.pm, revision 1.4
1.1 raeburn 1: # The LearningOnline Network with CAPA
1.4 ! raeburn 2: # Handler for a Placement Test course container
1.1 raeburn 3: #
1.4 ! raeburn 4: # $Id: lonplacementtest.pm,v 1.3 2016/04/04 17:24:05 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.4 ! raeburn 22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
! 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();
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;
1.3 raeburn 248: if ($inhibitmenu) {
249: $output = '<hr />';
250: } else {
1.2 raeburn 251: my $brcrum = [{'href' => '/adm/flip?postdata=firstres%3a',
252: 'text' => 'Test Status'},];
253: $output = &Apache::loncommon::start_page('Placement Test Completed',
254: undef,{bread_crumbs=>$brcrum});
255: }
1.1 raeburn 256: if ($complete) {
257: $output .= '<p class="LC_info">'.&mt('Test is complete').'</p>';
258: }
1.2 raeburn 259: $output .= '<p>'.&mt('You scored [quant,_1,point].',$score).'</p>'
260: .&Apache::lonhtmlcommon::actionbox(
261: ['<a href="/adm/flip?postdata=firstres%3a">'.$aclt{'newt'}.'</a></li>',
262: '<a href="/adm/logout">'.$aclt{'exit'}.'</a></li>',
263: ]);
264: unless ($inhibitmenu) {
265: $output .= &Apache::loncommon::end_page();
266: }
267: return $output;
1.1 raeburn 268: }
269:
270: sub showincomplete {
1.2 raeburn 271: my ($incomplete,$inhibitmenu) = @_;
1.1 raeburn 272: my %aclt = &test_action_text();
1.2 raeburn 273: my $output;
1.1 raeburn 274: if ($incomplete == 100) {
1.3 raeburn 275: if ($inhibitmenu) {
276: $output = '<hr />';
277: } else {
1.2 raeburn 278: my $brcrum = [{'href' => '/adm/flip?postdata=firstres%3a',
279: 'text' => 'Test Status'},];
280: $output = &Apache::loncommon::start_page('Placement Test Unattempted',
281: undef,{bread_crumbs=>$brcrum});
282: }
283: $output .= '<p class="LC_warning">'.&mt('Your Placement Test is incomplete.').'<p></p>'
284: .&mt('Currently, you have not submitted any answers for any of the questions.')
285: .'</p>'
286: .&Apache::lonhtmlcommon::actionbox(
287: ['<a href="/adm/flip?postdata=firstres%3a">'.$aclt{'begin'}.'</a></li>',
288: '<a href="/adm/logout">'.$aclt{'exit'}.'</a></li>',
289: ]);
1.1 raeburn 290: } elsif ($incomplete) {
1.3 raeburn 291: if ($inhibitmenu) {
292: $output = '<hr />';
293: } else {
1.2 raeburn 294: my $brcrum = [{'href' => '/adm/flip?postdata=endplacement%3a',
295: 'text' => 'Test Status'},];
296: $output .= &Apache::loncommon::start_page('Incomplete Placement Test',
297: undef,{bread_crumbs=>$brcrum});
298: }
299: $output .= '<p class="LC_warning">'.&mt('Your Placement Test is incomplete.').'<p></p>'
300: .&mt('Currently, you have not provided an answer for [_1]% of the questions.',$incomplete)
301: .'</p>'
302: .&Apache::lonhtmlcommon::actionbox(
303: ['<a href="/adm/flip?postdata=endplacement%3a">'.$aclt{'endt'}.'</a></li>',
304: '<a href="/adm/flip?postdata=firstanswerable%3a">'.$aclt{'comp'}.'</a></li>',
305: '<a href="/adm/logout">'.$aclt{'exit'}.'</a></li>',
306: ]);
307: }
308: unless ($inhibitmenu) {
309: $output .= &Apache::loncommon::end_page();
1.1 raeburn 310: }
1.2 raeburn 311: return $output;
1.1 raeburn 312: }
313:
314: sub test_action_text {
315: return &Apache::lonlocal::texthash(
316: 'exit' => 'Logout',
317: 'newt' => 'Start a new test',
318: 'endt' => 'Mark test as completed',
319: 'comp' => 'Go to first unanswered question',
320: 'begin' => 'Go to start',
321: );
322: }
323:
324: 1;
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>