1: # The LearningOnline Network with CAPA
2: # mutliple choice style responses
3: #
4: # $Id: radiobuttonresponse.pm,v 1.153 2012/01/05 11:56:34 foxr Exp $
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# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22: #
23: # /home/httpd/html/adm/gpl.txt
24: #
25: # http://www.lon-capa.org/
26: #
27:
28: package Apache::radiobuttonresponse;
29: use strict;
30: use HTML::Entities();
31: use Apache::lonlocal;
32: use Apache::lonnet;
33: use Apache::response;
34:
35: my $default_bubbles_per_line = 10;
36:
37: BEGIN {
38: &Apache::lonxml::register( 'Apache::radiobuttonresponse',
39: ('radiobuttonresponse') );
40: }
41:
42: sub bubble_line_count {
43: my ( $numfoils, $bubbles_per_line ) = @_;
44: my $bubble_lines;
45: $bubble_lines = int( $numfoils / $bubbles_per_line );
46: if ( ( $numfoils % $bubbles_per_line ) != 0 ) {
47: $bubble_lines++;
48: }
49: return $bubble_lines;
50:
51: }
52:
53: sub start_radiobuttonresponse {
54: my ( $target, $token, $tagstack, $parstack, $parser, $safeeval, $style ) =
55: @_;
56: my $result;
57:
58: #when in a radiobutton response use these
59: &Apache::lonxml::register( 'Apache::radiobuttonresponse',
60: ( 'foilgroup', 'foil', 'conceptgroup' ) );
61: push( @Apache::lonxml::namespace, 'radiobuttonresponse' );
62: my $id = &Apache::response::start_response( $parstack, $safeeval );
63:
64: %Apache::hint::radiobutton = ();
65: undef(%Apache::response::foilnames);
66: if ( $target eq 'meta' ) {
67: $result = &Apache::response::meta_package_write('radiobuttonresponse');
68: }
69: elsif ( $target eq 'edit' ) {
70: $result .=
71: &Apache::edit::start_table($token)
72: . '<tr><td>'
73: . &Apache::lonxml::description($token)
74: . &Apache::loncommon::help_open_topic('Radio_Response_Problems')
75: . '</td>'
76: . '<td><span class="LC_nobreak">'
77: . &mt('Delete?') . ' '
78: . &Apache::edit::deletelist( $target, $token )
79: . '</span></td>'
80: . '<td> '
81: . &Apache::edit::end_row()
82: . &Apache::edit::start_spanning_row();
83: $result .= &Apache::edit::text_arg( 'Max Number Of Shown Foils:',
84: 'max', $token, '4' )
85: . ' ' x 3
86: . &Apache::edit::select_arg( 'Randomize Foil Order:',
87: 'randomize', [ 'yes', 'no' ], $token )
88: . ' ' x 3
89: . &Apache::edit::select_arg(
90: 'Display Direction:', 'direction',
91: [ 'vertical', 'horizontal' ], $token
92: )
93: . &Apache::edit::end_row()
94: . &Apache::edit::start_spanning_row() . "\n";
95: }
96: elsif ( $target eq 'modified' ) {
97: my $constructtag =
98: &Apache::edit::get_new_args( $token, $parstack, $safeeval, 'max',
99: 'randomize', 'direction' );
100: if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); }
101: }
102: elsif ( $target eq 'tex' ) {
103: my $type =
104: &Apache::lonxml::get_param( 'TeXtype', $parstack, $safeeval, undef,
105: 0 );
106: if ( $type eq '1' ) {
107: $result .= ' \renewcommand{\labelenumi}{\arabic{enumi}.}';
108: }
109: elsif ( $type eq 'A' ) {
110: $result .= ' \renewcommand{\labelenumi}{\Alph{enumi}.}';
111: }
112: elsif ( $type eq 'a' ) {
113: $result .= ' \renewcommand{\labelenumi}{\alph{enumi}.}';
114: }
115: elsif ( $type eq 'i' ) {
116: $result .= ' \renewcommand{\labelenumi}{\roman{enumi}.}';
117: }
118: else {
119: $result .= ' \renewcommand{\labelenumi}{\Alph{enumi}.}';
120: }
121: if ( $env{'form.pdfFormFields'} eq 'yes'
122: && $Apache::inputtags::status[-1] eq 'CAN_ANSWER' )
123: {
124: $result .= '\begin{itemize}';
125: }
126: else {
127: $result .= '\begin{enumerate}';
128: }
129: }
130: elsif ( $target eq 'analyze' ) {
131: my $part_id = "$Apache::inputtags::part.$id";
132: $Apache::lonhomework::analyze{"$part_id.type"} = 'radiobuttonresponse';
133: push( @{ $Apache::lonhomework::analyze{"parts"} }, $part_id );
134: }
135: return $result;
136: }
137:
138: sub end_radiobuttonresponse {
139: my ( $target, $token, $tagstack, $parstack, $parser, $safeeval, $style ) =
140: @_;
141: my $result;
142: if ( $target eq 'edit' ) { $result = &Apache::edit::end_table(); }
143: if ( $target eq 'tex' ) {
144: if ( $env{'form.pdfFormFields'} eq 'yes'
145: and $Apache::inputtags::status[-1] eq 'CAN_ANSWER' )
146: {
147: $result .= '\end{itemize}';
148: }
149: else {
150: $result .= '\end{enumerate}';
151: }
152: }
153: &Apache::response::end_response;
154: pop @Apache::lonxml::namespace;
155: &Apache::lonxml::deregister( 'Apache::radiobuttonresponse',
156: ( 'foilgroup', 'foil', 'conceptgroup' ) );
157: undef(%Apache::response::foilnames);
158: return $result;
159: }
160:
161: %Apache::response::foilgroup = ();
162:
163: sub start_foilgroup {
164: my ( $target, $token, $tagstack, $parstack, $parser, $safeeval, $style ) =
165: @_;
166: %Apache::response::foilgroup = ();
167: $Apache::radiobuttonresponse::conceptgroup = 0;
168: &Apache::response::pushrandomnumber( undef, $target );
169: return;
170: }
171:
172: sub storesurvey {
173: my ($style) = @_;
174: if ( !&Apache::response::submitted() ) { return ''; }
175: my $response = $env{ 'form.HWVAL_' . $Apache::inputtags::response['-1'] };
176: &Apache::lonxml::debug("Here I am!:$response:");
177: if ( $response !~ /[0-9]+/ ) { return ''; }
178: my $part = $Apache::inputtags::part;
179: my $id = $Apache::inputtags::response['-1'];
180: my @whichfoils = @{ $Apache::response::foilgroup{'names'} };
181: my %responsehash;
182: $responsehash{ $whichfoils[$response] } = $response;
183: my $responsestr = &Apache::lonnet::hash2str(%responsehash);
184: $Apache::lonhomework::results{"resource.$part.$id.submission"} =
185: $responsestr;
186: my %previous =
187: &Apache::response::check_for_previous( $responsestr, $part, $id );
188: my $ad;
189:
190: if ( $style eq 'anonsurvey' ) {
191: $ad = $Apache::lonhomework::results{"resource.$part.$id.awarddetail"} =
192: 'ANONYMOUS';
193: }
194: elsif ( $style eq 'anonsurveycred' ) {
195: $ad = $Apache::lonhomework::results{"resource.$part.$id.awarddetail"} =
196: 'ANONYMOUS_CREDIT';
197: }
198: elsif ( $style eq 'surveycred' ) {
199: $ad = $Apache::lonhomework::results{"resource.$part.$id.awarddetail"} =
200: 'SUBMITTED_CREDIT';
201: }
202: else {
203: $ad = $Apache::lonhomework::results{"resource.$part.$id.awarddetail"} =
204: 'SUBMITTED';
205: }
206: &Apache::response::handle_previous( \%previous, $ad );
207: &Apache::lonxml::debug("submitted a $response<br />\n");
208: return '';
209: }
210:
211: sub grade_response {
212: my ( $answer, $whichfoils, $bubbles_per_line ) = @_;
213:
214: if ( !&Apache::response::submitted() ) { return; }
215: my $response;
216:
217: if ( $env{'form.submitted'} eq 'scantron' ) {
218: $response =
219: &Apache::response::getresponse( 1, undef,
220: &bubble_line_count( scalar( @{$whichfoils} ), $bubbles_per_line ),
221: $bubbles_per_line );
222:
223: }
224: else {
225: $response = $env{ 'form.HWVAL_' . $Apache::inputtags::response['-1'] };
226: }
227:
228: if ( $response !~ /[0-9]+/ ) { return; }
229: my $part = $Apache::inputtags::part;
230: my $id = $Apache::inputtags::response['-1'];
231: my %responsehash;
232: $responsehash{ $whichfoils->[$response] } = $response;
233: my $responsestr = &Apache::lonnet::hash2str(%responsehash);
234: my %previous =
235: &Apache::response::check_for_previous( $responsestr, $part, $id );
236: $Apache::lonhomework::results{"resource.$part.$id.submission"} =
237: $responsestr;
238: &Apache::lonxml::debug("submitted a $response<br />\n");
239: my $ad;
240:
241: if ( $response == $answer ) {
242: $ad = 'EXACT_ANS';
243: }
244: else {
245: $ad = 'INCORRECT';
246: }
247: $Apache::lonhomework::results{"resource.$part.$id.awarddetail"} = $ad;
248: &Apache::response::handle_previous( \%previous, $ad );
249: }
250:
251: sub end_foilgroup {
252: my ( $target, $token, $tagstack, $parstack, $parser, $safeeval, $style ) =
253: @_;
254:
255: my $result;
256: my $bubble_lines;
257: my $answer_count;
258: my $id = $Apache::inputtags::response['-1'];
259: my $part = $Apache::inputtags::part;
260: my $bubbles_per_line = &getbubblesnum( $part, $id );
261:
262: if ( $target eq 'grade'
263: || $target eq 'web'
264: || $target eq 'answer'
265: || $target eq 'tex'
266: || $target eq 'analyze' )
267: {
268: my $style = $Apache::lonhomework::type;
269: my $direction =
270: &Apache::lonxml::get_param( 'direction', $parstack, $safeeval, '-2' );
271: if (
272: (
273: ( $style eq 'survey' )
274: || ( $style eq 'surveycred' )
275: || ( $style eq 'anonsurvey' )
276: || ( $style eq 'anonsurveycred' )
277: )
278: && ( $target ne 'analyze' )
279: )
280: {
281: if ( $target eq 'web' || $target eq 'tex' ) {
282: $result = &displayallfoils( $direction, $target );
283: }
284: elsif ( $target eq 'answer' ) {
285: $result = &displayallanswers();
286: }
287: elsif ( $target eq 'grade' ) {
288: $result = &storesurvey($style);
289: }
290: $answer_count =
291: scalar( @{ $Apache::response::foilgroup{'names'} } );
292:
293: }
294: else {
295:
296: my $name;
297: my $max =
298: &Apache::lonxml::get_param( 'max', $parstack, $safeeval, '-2' );
299: my $randomize =
300: &Apache::lonxml::get_param( 'randomize', $parstack, $safeeval,
301: '-2' );
302: my ( $answer, @shown ) = &whichfoils( $max, $randomize );
303: $answer_count = scalar(@shown);
304:
305: if ( $target eq 'web' || $target eq 'tex' ) {
306: $result =
307: &displayfoils( $target, $answer, \@shown, $direction,
308: $bubbles_per_line );
309: }
310: elsif ( $target eq 'answer' ) {
311: $result =
312: &displayanswers( $answer, \@shown, $bubbles_per_line );
313: }
314: elsif ( $target eq 'grade' ) {
315: &grade_response( $answer, \@shown, $bubbles_per_line );
316: }
317: elsif ( $target eq 'analyze' ) {
318: my $bubble_lines =
319: &bubble_line_count( $answer_count, $bubbles_per_line );
320: &Apache::response::analyze_store_foilgroup( \@shown,
321: [ 'text', 'value', 'location' ] );
322: my $part_id = "$part.$id";
323: push(
324: @{ $Apache::lonhomework::analyze{"$part_id.options"} },
325: ( 'true', 'false' )
326: );
327:
328: }
329: }
330: $Apache::lonxml::post_evaluate = 0;
331: }
332: if ( $target eq 'web' ) {
333: &Apache::response::setup_prior_tries_hash( \&format_prior_answer,
334: [ \%Apache::response::foilgroup ] );
335: }
336: &Apache::response::poprandomnumber();
337: $bubble_lines = &bubble_line_count( $answer_count, $bubbles_per_line );
338: &Apache::lonxml::increment_counter( $bubble_lines, "$part.$id" );
339: if ( $target eq 'analyze' ) {
340: &Apache::lonhomework::set_bubble_lines();
341: }
342: return $result;
343: }
344:
345: sub getbubblesnum {
346: my ( $part, $id ) = @_;
347: my $bubbles_per_line;
348: my $default_numbubbles = $default_bubbles_per_line;
349: if ( ( $env{'form.bubbles_per_row'} =~ /^\d+$/ )
350: && ( $env{'form.bubbles_per_row'} > 0 ) )
351: {
352: $default_numbubbles = $env{'form.bubbles_per_row'};
353: }
354: $bubbles_per_line = &Apache::response::get_response_param( $part . "_$id",
355: 'numbubbles', $default_numbubbles );
356: return $bubbles_per_line;
357: }
358:
359: sub getfoilcounts {
360: my @names;
361: my $truecnt = 0;
362: my $falsecnt = 0;
363: my $name;
364: if ( $Apache::response::foilgroup{'names'} ) {
365: @names = @{ $Apache::response::foilgroup{'names'} };
366: }
367: foreach $name (@names) {
368: if ( $Apache::response::foilgroup{ $name . '.value' } eq 'true' ) {
369: $truecnt++;
370: }
371: elsif ( $Apache::response::foilgroup{ $name . '.value' } eq 'false' ) {
372: $falsecnt++;
373: }
374: }
375: return ( $truecnt, $falsecnt );
376: }
377:
378: sub format_prior_answer {
379: my ( $mode, $answer, $other_data ) = @_;
380: my $foil_data = $other_data->[0];
381: my %response = &Apache::lonnet::str2hash($answer);
382: my ($name) = keys(%response);
383: return
384: '<span class="LC_prior_radiobutton">'
385: . $foil_data->{ $name . '.text' }
386: . '</span>';
387:
388: }
389:
390: sub displayallfoils {
391: my ( $direction, $target ) = @_;
392: my $result;
393: &Apache::lonxml::debug("survey style display");
394: my @names;
395: if ( $Apache::response::foilgroup{'names'} ) {
396: @names = @{ $Apache::response::foilgroup{'names'} };
397: }
398:
399: my $temp = 0;
400: my $i = 0;
401: my $id = $Apache::inputtags::response['-1'];
402: my $part = $Apache::inputtags::part;
403: my ( $lastresponse, $newvariation, $showanswer );
404: if (
405: (
406: (
407: $Apache::lonhomework::history{"resource.$part.type"} eq
408: 'randomizetry'
409: )
410: || ( $Apache::lonhomework::type eq 'randomizetry' )
411: )
412: && ( $Apache::inputtags::status[-1] eq 'CAN_ANSWER' )
413: )
414: {
415: if ( $env{ 'form.' . $part . '.rndseed' } ne
416: $Apache::lonhomework::history{"resource.$part.rndseed"} )
417: {
418: $newvariation = 1;
419: }
420: }
421: $showanswer = &Apache::response::show_answer();
422: unless (
423: (
424: (
425: $Apache::lonhomework::history{"resource.$part.type"} eq
426: 'anonsurvey'
427: )
428: || ( $Apache::lonhomework::history{"resource.$part.type"} eq
429: 'anonsurveycred' )
430: )
431: && ( defined( $env{'form.grade_symb'} ) )
432: || ( $newvariation && !$showanswer )
433: )
434: {
435: $lastresponse =
436: $Apache::lonhomework::history{"resource.$part.$id.submission"};
437: }
438: if ( $direction eq 'horizontal' ) { $result .= '<table><tr>'; }
439: my %lastresponse = &Apache::lonnet::str2hash($lastresponse);
440: if ($showanswer) {
441: foreach my $name (@names) {
442: if ( $Apache::response::foilgroup{ $name . '.value' } ne 'unused' )
443: {
444: if ( ( $direction eq 'horizontal' ) && ( $target ne 'tex' ) ) {
445: $result .= "<td>";
446: }
447: else {
448: if ( $target eq 'tex' ) {
449: $result .= '\item \vskip -2mm ';
450: }
451: else {
452: $result .= "<br />";
453: }
454: }
455: if ( defined( $lastresponse{$name} ) ) {
456: if ( $target eq 'tex' ) {
457: $result .= '}';
458: }
459: else {
460: $result .= '<b>';
461: }
462: }
463: $result .= $Apache::response::foilgroup{ $name . '.text' };
464: if ( defined( $lastresponse{$name} ) && ( $target ne 'tex' ) ) {
465: $result .= '</b>';
466: }
467: if ( ( $direction eq 'horizontal' ) && ( $target ne 'tex' ) ) {
468: $result .= "</td>";
469: }
470: }
471: }
472: }
473: else {
474: foreach my $name (@names) {
475: if ( $Apache::response::foilgroup{ $name . '.value' } ne 'unused' )
476: {
477: if ( $direction eq 'horizontal' ) {
478: $result .= "<td>";
479: }
480: else {
481: if ( $target eq 'tex' ) {
482: if ( $env{'form.pdfFormFields'} eq 'yes'
483: && $Apache::inputtags::status[-1] eq 'CAN_ANSWER' )
484: {
485: my $fieldname =
486: $env{'request.symb'}
487: . '&part_'
488: . $Apache::inputtags::part
489: . '&radiobuttonresponse'
490: . '&HWVAL_'
491: . $Apache::inputtags::response['-1'];
492: $result .= '\item[{'
493: . &Apache::lonxml::print_pdf_radiobutton(
494: $fieldname, $temp )
495: . '}]'
496: . $Apache::response::foilgroup{ $name . '.text' }
497: . "\n";
498: }
499: else {
500: $result .= '\item \vskip -2mm ';
501: }
502: }
503: else {
504: $result .= "<br />";
505: }
506: }
507: if ( $target eq 'tex' ) {
508: if ( $env{'form.pdfFormFields'} ne 'yes'
509: or $Apache::inputtags::status[-1] ne 'CAN_ANSWER' )
510: {
511: $result .=
512: '$\bigcirc$'
513: . $Apache::response::foilgroup{ $name . '.text' }
514: . '\\\\'; #' stupid emacs
515: }
516: $i++;
517: }
518: else {
519: $result .= '<label>';
520: $result .= "<input
521: onchange=\"javascript:setSubmittedPart('$part');\"
522: type=\"radio\"
523: name=\"HWVAL_$Apache::inputtags::response['-1']\"
524: value=\"$temp\"";
525:
526: if ( defined( $lastresponse{$name} ) ) {
527: $result .= ' checked="checked"';
528: }
529: $result .= ' />'
530: . $Apache::response::foilgroup{ $name . '.text' }
531: . '</label>';
532: }
533: $temp++;
534: if ( $target ne 'tex' ) {
535: if ( ( $direction eq 'horizontal' )
536: && ( $target ne 'tex' ) )
537: {
538: $result .= "</td>";
539: }
540: }
541: else {
542: $result .= '\vskip 0 mm ';
543: }
544: }
545: }
546: }
547:
548: if ( ( $direction eq 'horizontal' ) && ( $target ne 'tex' ) ) {
549: $result .= '</tr></table>';
550: }
551: return $result;
552: }
553:
554: sub whichfoils {
555: my ( $max, $randomize ) = @_;
556:
557: my @truelist;
558: my @falselist;
559: my @whichfalse = ();
560: my ( $truecnt, $falsecnt ) = &getfoilcounts();
561: my $count = 0;
562:
563: # we will add in 1 of the true statements
564: if ( $max > 0 && ( $falsecnt + 1 ) > $max ) { $count = $max }
565: else { $count = $falsecnt + 1; $max = $count; }
566: my $answer = int( &Math::Random::random_uniform() * ($count) );
567: &Apache::lonxml::debug("Count is $count, $answer is $answer");
568: my @names;
569: if ( $Apache::response::foilgroup{'names'} ) {
570: @names = @{ $Apache::response::foilgroup{'names'} };
571: }
572: if ( &Apache::response::showallfoils() ) {
573: @whichfalse = @names;
574: }
575: elsif ( $randomize eq 'no' ) {
576: &Apache::lonxml::debug("No randomization");
577: my $havetrue = 0;
578: foreach my $name (@names) {
579: if ( $Apache::response::foilgroup{ $name . '.value' } eq 'true' ) {
580: if ( !$havetrue ) {
581: push( @whichfalse, $name );
582: $havetrue++;
583: $answer = $#whichfalse;
584: }
585: }
586: elsif (
587: $Apache::response::foilgroup{ $name . '.value' } eq 'false' )
588: {
589: push( @whichfalse, $name );
590: }
591: elsif (
592: $Apache::response::foilgroup{ $name . '.value' } eq 'unused' )
593: {
594: }
595: else {
596: &Apache::lonxml::error(
597: &HTML::Entities::encode(
598: "No valid value assigned ($Apache::response::foilgroup{$name.'.value'}) for foil $name in <foilgroup>",
599: '<>&"'
600: )
601: );
602: }
603: }
604: if ( ( !$havetrue )
605: && ( $Apache::lonhomework::type ne 'survey' )
606: && ( $Apache::lonhomework::type ne 'surveycred' )
607: && ( $Apache::lonhomework::type ne 'anonsurvey' )
608: && ( $Apache::lonhomework::type ne 'anonsurveycred' ) )
609: {
610: &Apache::lonxml::error(
611: &mt('There are no true statements available.') . '<br />' );
612: }
613: }
614: else {
615: my $current = 0;
616: &Apache::lonhomework::showhash(%Apache::response::foilgroup);
617: my ( %top, %bottom );
618:
619: #first find out where everyone wants to be
620: foreach my $name (@names) {
621: $current++;
622: if ( $Apache::response::foilgroup{ $name . '.value' } eq 'true' ) {
623: push( @truelist, $name );
624: if ( $Apache::response::foilgroup{ $name . '.location' } eq
625: 'top' )
626: {
627: $top{$name} = $current;
628: }
629: elsif ( $Apache::response::foilgroup{ $name . '.location' } eq
630: 'bottom' )
631: {
632: $bottom{$name} = $current;
633: }
634: }
635: elsif (
636: $Apache::response::foilgroup{ $name . '.value' } eq 'false' )
637: {
638: push( @falselist, $name );
639: if ( $Apache::response::foilgroup{ $name . '.location' } eq
640: 'top' )
641: {
642: $top{$name} = $current;
643: }
644: elsif ( $Apache::response::foilgroup{ $name . '.location' } eq
645: 'bottom' )
646: {
647: $bottom{$name} = $current;
648: }
649: }
650: elsif (
651: $Apache::response::foilgroup{ $name . '.value' } eq 'unused' )
652: {
653: }
654: else {
655: &Apache::lonxml::error(
656: &HTML::Entities::encode(
657: "No valid value assigned ($Apache::response::foilgroup{$name.'.value'}) for foil $name in <foilgroup>",
658: '<>&"'
659: )
660: );
661: }
662: }
663:
664: #pick a true statement
665: my $notrue = 0;
666: if ( scalar(@truelist) == 0 ) { $notrue = 1; }
667: my $whichtrue =
668: int( &Math::Random::random_uniform() * ( $#truelist + 1 ) );
669: &Apache::lonxml::debug(
670: "Max is $max, From $#truelist elms, picking $whichtrue");
671: my ( @toplist, @bottomlist );
672: my $topcount = 0;
673: my $bottomcount = 0;
674:
675: # assign everyone to either toplist/bottomlist or whichfalse
676: # which false is randomized, toplist bottomlist are in order
677: while (( ( $#whichfalse + $topcount + $bottomcount ) < $max - 2 )
678: && ( $#falselist > -1 ) )
679: {
680: &Apache::lonxml::debug("Have $#whichfalse max is $max");
681: my $afalse =
682: int( &Math::Random::random_uniform() * ( $#falselist + 1 ) );
683: &Apache::lonxml::debug("From $#falselist elms, picking $afalse");
684: $afalse = splice( @falselist, $afalse, 1 );
685: &Apache::lonxml::debug("Picked $afalse");
686: &Apache::lonhomework::showhash( ( 'names' => \@names ) );
687: &Apache::lonhomework::showhash(%top);
688: if ( $top{$afalse} ) {
689: $toplist[ $top{$afalse} ] = $afalse;
690: $topcount++;
691: }
692: elsif ( $bottom{$afalse} ) {
693: $bottomlist[ $bottom{$afalse} ] = $afalse;
694: $bottomcount++;
695: }
696: else {
697: push( @whichfalse, $afalse );
698: }
699: }
700: &Apache::lonxml::debug("Answer wants $answer");
701: my $truename = $truelist[$whichtrue];
702: my $dosplice = 1;
703: if ( ($notrue)
704: && ( $Apache::lonhomework::type ne 'survey' )
705: && ( $Apache::lonhomework::type ne 'surveycred' )
706: && ( $Apache::lonhomework::type ne 'anonsurvey' )
707: && ( $Apache::lonhomework::type ne 'anonsurveycred' ) )
708: {
709: $dosplice = 0;
710: &Apache::lonxml::error(
711: &mt('There are no true statements available.') . '<br />' );
712: }
713:
714: #insert the true statement, keeping track of where it wants to be
715: if ( $Apache::response::foilgroup{ $truename . '.location' } eq 'top'
716: && $dosplice )
717: {
718: $toplist[ $top{$truename} ] = $truename;
719: $answer = -1;
720: foreach my $top ( reverse(@toplist) ) {
721: if ($top) { $answer++; }
722: if ( $top eq $truename ) { last; }
723: }
724: $dosplice = 0;
725: }
726: elsif (
727: $Apache::response::foilgroup{ $truename . '.location' } eq 'bottom'
728: && $dosplice )
729: {
730: $bottomlist[ $bottom{$truename} ] = $truename;
731: $answer = -1;
732: foreach my $bot (@bottomlist) {
733: if ($bot) { $answer++; }
734: if ( $bot eq $truename ) { last; }
735: }
736: $answer += $topcount + $#whichfalse + 1;
737: $dosplice = 0;
738: }
739: else {
740: if ( $topcount > 0 || $bottomcount > 0 ) {
741: my $inc = 1;
742: if ( ( $bottomcount > 0 )
743: && ( $Apache::lonhomework::type ne 'exam' ) )
744: {
745: $inc = 2;
746: }
747: $answer = int(
748: &Math::Random::random_uniform() * ( $#whichfalse + $inc ) )
749: + $topcount;
750: }
751: }
752: &Apache::lonxml::debug("Answer now wants $answer");
753:
754: #add the top items to the top, bottom items to the bottom
755: for ( my $i = 0 ; $i <= $#toplist ; $i++ ) {
756: if ( $toplist[$i] ) { unshift( @whichfalse, $toplist[$i] ) }
757: }
758: for ( my $i = 0 ; $i <= $#bottomlist ; $i++ ) {
759: if ( $bottomlist[$i] ) { push( @whichfalse, $bottomlist[$i] ) }
760: }
761:
762: #if the true statement is randomized insert it into the list
763: if ($dosplice) {
764: splice( @whichfalse, $answer, 0, $truelist[$whichtrue] );
765: }
766: }
767: &Apache::lonxml::debug("Answer is $answer");
768: return ( $answer, @whichfalse );
769: }
770:
771: sub displayfoils {
772: my ( $target, $answer, $whichfoils, $direction, $bubbles_per_line ) = @_;
773: my $result;
774:
775: my $part = $Apache::inputtags::part;
776: my $solved = $Apache::lonhomework::history{"resource.$part.solved"};
777: if ( ( $target ne 'tex' )
778: && &Apache::response::show_answer() )
779: {
780: if ( $direction eq 'horizontal' ) {
781: if ( $target ne 'tex' ) {
782: $result .= '<table><tr>';
783: }
784: }
785: foreach my $name ( @{$whichfoils} ) {
786: if ( $direction eq 'horizontal' ) {
787: if ( $target ne 'tex' ) { $result .= '<td>'; }
788: }
789: if ( $target ne 'tex' ) {
790: $result .= "<br />";
791: }
792: else {
793: $result .= '\item \vskip -2 mm ';
794: }
795: if ( $Apache::response::foilgroup{ $name . '.value' } eq 'true' ) {
796: if ( $target ne 'tex' ) {
797: $result .= &mt('Correct:') . '<b>';
798: }
799: else {
800: $result .= &mt('Correct:') . ' \textbf{';
801: }
802: }
803: else {
804: $result .= &mt('Incorrect:');
805: }
806: if ( $target eq 'web' ) { $result .= "<label>"; }
807: $result .= $Apache::response::foilgroup{ $name . '.text' };
808: if ( $target eq 'web' ) { $result .= "</label>"; }
809: if ( $Apache::response::foilgroup{ $name . '.value' } eq 'true' ) {
810: if ( $target ne 'tex' ) { $result .= '</b>'; }
811: else { $result .= '}'; }
812: }
813: if ( $direction eq 'horizontal' ) {
814: if ( $target ne 'tex' ) { $result .= '</td>'; }
815: }
816: }
817: if ( $direction eq 'horizontal' ) {
818: if ( $target ne 'tex' ) {
819: $result .= '</tr></table>';
820: }
821: }
822: }
823: else {
824: my @alphabet = ( 'A' .. 'Z' );
825: my $i = 0;
826: my $bubble_number = 0;
827: my $line = 0;
828: my $temp = 0;
829: my $id = $Apache::inputtags::response['-1'];
830: my $part = $Apache::inputtags::part;
831: my ( $lastresponse, $newvariation );
832:
833: if (
834: (
835: (
836: $Apache::lonhomework::history{"resource.$part.type"} eq
837: 'randomizetry'
838: )
839: || ( $Apache::lonhomework::type eq 'randomizetry' )
840: )
841: && ( $Apache::inputtags::status[-1] eq 'CAN_ANSWER' )
842: )
843: {
844:
845: if ( $env{ 'form.' . $part . '.rndseed' } ne
846: $Apache::lonhomework::history{"resource.$part.rndseed"} )
847: {
848: $newvariation = 1;
849: }
850: }
851: unless ($newvariation) {
852: $lastresponse =
853: $Apache::lonhomework::history{"resource.$part.$id.submission"};
854: }
855: my %lastresponse = &Apache::lonnet::str2hash($lastresponse);
856: if ( $target ne 'tex' && $direction eq 'horizontal' ) {
857: $result .= "<table><tr>";
858: }
859: my $numlines;
860: if ( ( $target eq 'tex' ) && ( $Apache::lonhomework::type eq 'exam' ) )
861: {
862: my $numitems = scalar( @{$whichfoils} );
863: $numlines = int( $numitems / $bubbles_per_line );
864: if ( ( $numitems % $bubbles_per_line ) != 0 ) {
865: $numlines++;
866: }
867: if ( $numlines < 1 ) {
868: $numlines = 1;
869: }
870: if ( $numlines > 1 ) {
871: my $linetext;
872: for ( my $i = 0 ; $i < $numlines ; $i++ ) {
873: $linetext .= $Apache::lonxml::counter + $i . ', ';
874: }
875: $linetext =~ s/,\s$//;
876: $result .=
877: '\item[\small {\textbf{'
878: . $linetext . '}}]'
879: . ' {\footnotesize '
880: . &mt( '(Bubble once in [_1] lines)', $numlines )
881: . '} \hspace*{\fill} \\\\';
882: }
883: else {
884: $result .= '\item[\textbf{' . $Apache::lonxml::counter . '}.]';
885: }
886: }
887: foreach my $name ( @{$whichfoils} ) {
888: if ( $target ne 'tex' ) {
889: if ( $direction eq 'horizontal' ) {
890: $result .= "<td>";
891: }
892: else {
893: $result .= "<br />";
894: }
895: }
896: if ( $target ne 'tex' ) {
897: $result .= '<label>';
898: $result .= "<input type=\"radio\"
899: onchange=\"javascript:setSubmittedPart('$part');\"
900: name=\"HWVAL_$Apache::inputtags::response['-1']\"
901: value=\"$temp\"";
902: if ( defined( $lastresponse{$name} ) ) {
903: $result .= ' checked="checked"';
904: }
905: $result .= ' />'
906: . $Apache::response::foilgroup{ $name . '.text' }
907: . "</label>";
908: }
909: else {
910: if ( $Apache::lonhomework::type eq 'exam' ) {
911: if ( $bubble_number >= $bubbles_per_line ) {
912: $line++;
913: $i = 0;
914: $bubble_number = 0;
915: }
916: my $identifier;
917: if ( $numlines > 1 ) {
918: $identifier = $Apache::lonxml::counter + $line;
919: }
920: $result .=
921: '{\small \textbf{'
922: . $identifier
923: . $alphabet[$i]
924: . '}}$\bigcirc$'
925: . $Apache::response::foilgroup{ $name . '.text' }
926: . '\\\\'; #' stupid emacs
927: $i++;
928: $bubble_number++;
929: }
930: else {
931: if ( $env{'form.pdfFormFields'} eq 'yes'
932: && $Apache::inputtags::status[-1] eq 'CAN_ANSWER' )
933: {
934: my $fieldname =
935: $env{'request.symb'}
936: . '&part_'
937: . $Apache::inputtags::part
938: . '&radiobuttonresponse'
939: . '&HWVAL_'
940: . $Apache::inputtags::response['-1'];
941: $result .= '\item[{'
942: . &Apache::lonxml::print_pdf_radiobutton( $fieldname,
943: $temp )
944: . '}]'
945: . $Apache::response::foilgroup{ $name . '.text' }
946: . "\n";
947: }
948: else {
949: $result .= '\vspace*{-2 mm}\item '
950: . $Apache::response::foilgroup{ $name . '.text' };
951: }
952: }
953: }
954: if ( $target ne 'tex' && $direction eq 'horizontal' ) {
955: $result .= "</td>";
956: }
957: $temp++;
958: }
959: if ( $target ne 'tex' && $direction eq 'horizontal' ) {
960: $result .= "</tr></table>";
961: }
962: }
963: if ( $target ne 'tex' ) {
964: if ( $direction ne 'horizontal' ) { $result .= "<br />"; }
965: }
966: else { $result .= '\vskip 0 mm '; }
967: return $result;
968: }
969:
970: sub displayallanswers {
971: my @names;
972: if ( $Apache::response::foilgroup{'names'} ) {
973: @names = @{ $Apache::response::foilgroup{'names'} };
974: }
975: my $result = &Apache::response::answer_header('radiobuttonresponse');
976: foreach my $name (@names) {
977: $result .=
978: &Apache::response::answer_part( 'radiobuttonresponse',
979: $Apache::response::foilgroup{ $name . '.value' } );
980: }
981: $result .= &Apache::response::answer_footer('radiobuttonresponse');
982: return $result;
983: }
984:
985: sub displayanswers {
986: my ( $answer, $whichopt, $bubbles_per_line ) = @_;
987: my $result;
988:
989: if ( $Apache::lonhomework::type eq 'exam' ) {
990: my $line = int( $answer / $bubbles_per_line );
991: my $correct = ( 'A' .. 'Z' )[ $answer % $bubbles_per_line ];
992: $result .=
993: &Apache::response::answer_header( 'radiobuttonresponse', $line );
994: $result .=
995: &Apache::response::answer_part( 'radiobuttonresponse', $correct );
996: }
997: else {
998: $result .= &Apache::response::answer_header('radiobuttonresponse');
999: }
1000: foreach my $name ( @{$whichopt} ) {
1001: $result .=
1002: &Apache::response::answer_part( 'radiobuttonresponse',
1003: $Apache::response::foilgroup{ $name . '.value' } );
1004: }
1005: $result .= &Apache::response::answer_footer('radiobuttonresponse');
1006: return $result;
1007: }
1008:
1009: sub start_conceptgroup {
1010: my ( $target, $token, $tagstack, $parstack, $parser, $safeeval, $style ) =
1011: @_;
1012: $Apache::radiobuttonresponse::conceptgroup = 1;
1013: %Apache::response::conceptgroup = ();
1014: my $result;
1015: if ( $target eq 'edit' ) {
1016: $result .= &Apache::edit::tag_start( $target, $token );
1017: $result .=
1018: &Apache::edit::text_arg( 'Concept:', 'concept', $token, '50' )
1019: . &Apache::edit::end_row()
1020: . &Apache::edit::start_spanning_row();
1021: }
1022: elsif ( $target eq 'modified' ) {
1023: my $constructtag =
1024: &Apache::edit::get_new_args( $token, $parstack, $safeeval,
1025: 'concept' );
1026: if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); }
1027: }
1028: return $result;
1029: }
1030:
1031: sub end_conceptgroup {
1032: my ( $target, $token, $tagstack, $parstack, $parser, $safeeval, $style ) =
1033: @_;
1034: $Apache::radiobuttonresponse::conceptgroup = 0;
1035: my $result;
1036: if ( $target eq 'web'
1037: || $target eq 'grade'
1038: || $target eq 'answer'
1039: || $target eq 'tex'
1040: || $target eq 'analyze' )
1041: {
1042: &Apache::response::pick_foil_for_concept( $target,
1043: [ 'value', 'text', 'location' ],
1044: \%Apache::hint::radiobutton, $parstack, $safeeval );
1045: }
1046: elsif ( $target eq 'edit' ) {
1047: $result = &Apache::edit::end_table();
1048: }
1049: return $result;
1050: }
1051:
1052: sub insert_conceptgroup {
1053: my $result =
1054: "\n\t\t<conceptgroup concept=\"\">"
1055: . &insert_foil()
1056: . "\n\t\t</conceptgroup>\n";
1057: return $result;
1058: }
1059:
1060: sub start_foil {
1061: my ( $target, $token, $tagstack, $parstack, $parser, $safeeval, $style ) =
1062: @_;
1063: my $result = '';
1064: if ( $target eq 'web' || $target eq 'tex' || $target eq 'analyze' ) {
1065: &Apache::lonxml::startredirection;
1066: if ( $target eq 'analyze' ) {
1067: &Apache::response::check_if_computed( $token, $parstack, $safeeval,
1068: 'value' );
1069: }
1070: }
1071: elsif ( $target eq 'edit' ) {
1072: $result = &Apache::edit::tag_start( $target, $token );
1073: $result .= &Apache::edit::text_arg( 'Name:', 'name', $token );
1074: $result .= &Apache::edit::select_or_text_arg(
1075: 'Correct Option:', 'value',
1076: [ 'unused', 'true', 'false' ], $token
1077: );
1078: my $randomize =
1079: &Apache::lonxml::get_param( 'randomize', $parstack, $safeeval, '-3' );
1080: if ( $randomize ne 'no' ) {
1081: $result .=
1082: &Apache::edit::select_arg( 'Location:', 'location',
1083: [ 'random', 'top', 'bottom' ], $token );
1084: }
1085: $result .=
1086: &Apache::edit::end_row() . &Apache::edit::start_spanning_row();
1087: }
1088: elsif ( $target eq 'modified' ) {
1089: my $constructtag =
1090: &Apache::edit::get_new_args( $token, $parstack, $safeeval, 'value',
1091: 'name', 'location' );
1092: if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); }
1093: }
1094: return $result;
1095: }
1096:
1097: sub end_foil {
1098: my ( $target, $token, $tagstack, $parstack, $parser, $safeeval, $style ) =
1099: @_;
1100: my $text = '';
1101: if ( $target eq 'web' || $target eq 'tex' || $target eq 'analyze' ) {
1102: $text = &Apache::lonxml::endredirection;
1103: }
1104: if ( $target eq 'web'
1105: || $target eq 'grade'
1106: || $target eq 'answer'
1107: || $target eq 'tex'
1108: || $target eq 'analyze' )
1109: {
1110: my $value = &Apache::lonxml::get_param( 'value', $parstack, $safeeval );
1111: if ( $value ne 'unused' ) {
1112: my $name =
1113: &Apache::lonxml::get_param( 'name', $parstack, $safeeval );
1114: if ( $name eq "" ) {
1115: &Apache::lonxml::warning(
1116: &mt(
1117: 'Foils without names exist. This can cause problems to malfunction.'
1118: )
1119: );
1120: $name = $Apache::lonxml::curdepth;
1121: }
1122: if ( defined( $Apache::response::foilnames{$name} ) ) {
1123: &Apache::lonxml::error(
1124: &mt(
1125: 'Foil name [_1] appears more than once. Foil names need to be unique.',
1126: '<b><tt>' . $name . '</tt></b>'
1127: )
1128: );
1129: }
1130: $Apache::response::foilnames{$name}++;
1131: my $location =
1132: &Apache::lonxml::get_param( 'location', $parstack, $safeeval );
1133: if ( $Apache::radiobuttonresponse::conceptgroup
1134: && !&Apache::response::showallfoils() )
1135: {
1136: push @{ $Apache::response::conceptgroup{'names'} }, $name;
1137: $Apache::response::conceptgroup{"$name.value"} = $value;
1138: $Apache::response::conceptgroup{"$name.text"} = $text;
1139: $Apache::response::conceptgroup{"$name.location"} = $location;
1140: }
1141: else {
1142: push @{ $Apache::response::foilgroup{'names'} }, $name;
1143: $Apache::response::foilgroup{"$name.value"} = $value;
1144: $Apache::response::foilgroup{"$name.text"} = $text;
1145: $Apache::response::foilgroup{"$name.location"} = $location;
1146: }
1147: }
1148: }
1149: return '';
1150: }
1151:
1152: sub insert_foil {
1153: return '
1154: <foil name="" value="unused">
1155: <startouttext />
1156: <endouttext />
1157: </foil>';
1158: }
1159:
1160: 1;
1161: __END__
1162:
1163:
1164:
1165: =head1 NAME
1166:
1167: Apache::radiobuttonresponse
1168:
1169: =head1 SYNOPSIS
1170:
1171: Handles multiple-choice style responses.
1172:
1173: This is part of the LearningOnline Network with CAPA project
1174: described at http://www.lon-capa.org.
1175:
1176: =head1 SUBROUTINES
1177:
1178: =over
1179:
1180: =item start_radiobuttonresponse()
1181:
1182: =item bubble_line_count()
1183:
1184: =item end_radiobuttonresponse()
1185:
1186: =item start_foilgroup()
1187:
1188: =item storesurvey()
1189:
1190: =item grade_response()
1191:
1192: =item end_foilgroup()
1193:
1194: =item getfoilcounts()
1195:
1196: =item format_prior_answer()
1197:
1198: =item displayallfoils()
1199:
1200: =item &whichfoils($max,$randomize)
1201:
1202: Randomizes the list of foils.
1203: Respects
1204: - each foils desire to be randomized
1205: - the existance of Concept groups of foils (select 1 foil from each)
1206: - and selects a single correct statement from all possilble true statments
1207: - and limits it to a toal of $max foils
1208:
1209: WARNING: this routine uses the random number generator, it should only
1210: be called once per target, otherwise it can cause randomness changes in
1211: homework problems.
1212:
1213: Arguments
1214: $max - maximum number of foils to select (including the true one)
1215: (so a max of 5 is: 1 true, 4 false)
1216:
1217: $randomize - whether to randomize the listing of foils, by default
1218: will randomize, only if randomize is 'no' will it not
1219:
1220: Returns
1221: $answer - location in the array of the correct answer
1222: @foils - array of foil names in to display order
1223:
1224: =item displayfoils()
1225:
1226: =item displayallanswers()
1227:
1228: =item displayanswers()
1229:
1230: =item start_conceptgroup()
1231:
1232: =item end_conceptgroup()
1233:
1234: =item insert_conceptgroup()
1235:
1236: =item start_foil()
1237:
1238: =item end_foil()
1239:
1240: =item insert_foil()
1241:
1242: =back
1243:
1244: =cut
1245:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>