![]() ![]() | ![]() |
BZ4492 - Code cleanup and more refactoring.
1: # The LearningOnline Network with CAPA 2: # mutliple choice style responses 3: # 4: # $Id: radiobuttonresponse.pm,v 1.153.6.10 2012/02/05 16:11:57 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: use Apache::caparesponse; 35: 36: my $default_bubbles_per_line = 10; 37: my @alphabet = ( 'A' .. 'Z' ); # Foil labels. 38: 39: 40: 41: BEGIN { 42: &Apache::lonxml::register( 'Apache::radiobuttonresponse', 43: ('radiobuttonresponse') ); 44: } 45: 46: #--------------------------------------------------------------------------- 47: # 48: # Generic utility subs. 49: 50: sub bubble_line_count { 51: my ( $numfoils, $bubbles_per_line ) = @_; 52: my $bubble_lines; 53: $bubble_lines = int( $numfoils / $bubbles_per_line ); 54: if ( ( $numfoils % $bubbles_per_line ) != 0 ) { 55: $bubble_lines++; 56: } 57: return $bubble_lines; 58: 59: } 60: 61: 62: #------------------------------------------------------------------------------ 63: # 64: # XML handlers. 65: sub start_radiobuttonresponse { 66: my ( $target, $token, $tagstack, $parstack, $parser, $safeeval, $style ) = 67: @_; 68: my $result; 69: 70: #when in a radiobutton response use these 71: &Apache::lonxml::register( 'Apache::radiobuttonresponse', 72: ( 'foilgroup', 'foil', 'conceptgroup' ) ); 73: push( @Apache::lonxml::namespace, 'radiobuttonresponse' ); 74: my $id = &Apache::response::start_response( $parstack, $safeeval ); 75: 76: %Apache::hint::radiobutton = (); 77: undef(%Apache::response::foilnames); 78: if ( $target eq 'meta' ) { 79: $result = &Apache::response::meta_package_write('radiobuttonresponse'); 80: } 81: elsif ( $target eq 'edit' ) { 82: $result .= 83: &Apache::edit::start_table($token) 84: . '<tr><td>' 85: . &Apache::lonxml::description($token) 86: . &Apache::loncommon::help_open_topic('Radio_Response_Problems') 87: . '</td>' 88: . '<td><span class="LC_nobreak">' 89: . &mt('Delete?') . ' ' 90: . &Apache::edit::deletelist( $target, $token ) 91: . '</span></td>' 92: . '<td> ' 93: . &Apache::edit::end_row() 94: . &Apache::edit::start_spanning_row(); 95: $result .= &Apache::edit::text_arg( 'Max Number Of Shown Foils:', 96: 'max', $token, '4' ) 97: . ' ' x 3 98: . &Apache::edit::select_arg( 'Randomize Foil Order:', 99: 'randomize', [ 'yes', 'no' ], $token ) 100: . ' ' x 3 101: . &Apache::edit::select_arg( 102: 'Display Direction:', 'direction', 103: [ 'vertical', 'horizontal' ], $token 104: ) 105: . &Apache::edit::end_row() 106: . &Apache::edit::start_spanning_row() . "\n"; 107: } 108: elsif ( $target eq 'modified' ) { 109: my $constructtag = 110: &Apache::edit::get_new_args( $token, $parstack, $safeeval, 'max', 111: 'randomize', 'direction' ); 112: if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); } 113: } 114: elsif ( $target eq 'tex' ) { 115: my $type = 116: &Apache::lonxml::get_param( 'TeXtype', $parstack, $safeeval, undef, 117: 0 ); 118: if ( $type eq '1' ) { 119: $result .= ' \renewcommand{\labelenumi}{\arabic{enumi}.}'; 120: } 121: elsif ( $type eq 'A' ) { 122: $result .= ' \renewcommand{\labelenumi}{\Alph{enumi}.}'; 123: } 124: elsif ( $type eq 'a' ) { 125: $result .= ' \renewcommand{\labelenumi}{\alph{enumi}.}'; 126: } 127: elsif ( $type eq 'i' ) { 128: $result .= ' \renewcommand{\labelenumi}{\roman{enumi}.}'; 129: } 130: else { 131: $result .= ' \renewcommand{\labelenumi}{\Alph{enumi}.}'; 132: } 133: 134: } 135: elsif ( $target eq 'analyze' ) { 136: my $part_id = "$Apache::inputtags::part.$id"; 137: $Apache::lonhomework::analyze{"$part_id.type"} = 'radiobuttonresponse'; 138: push( @{ $Apache::lonhomework::analyze{"parts"} }, $part_id ); 139: } 140: return $result; 141: } 142: 143: sub end_radiobuttonresponse { 144: my ( $target, $token, $tagstack, $parstack, $parser, $safeeval, $style ) = 145: @_; 146: my $result; 147: if ( $target eq 'edit' ) { $result = &Apache::edit::end_table(); } 148: 149: &Apache::response::end_response; 150: pop @Apache::lonxml::namespace; 151: &Apache::lonxml::deregister( 'Apache::radiobuttonresponse', 152: ( 'foilgroup', 'foil', 'conceptgroup' ) ); 153: undef(%Apache::response::foilnames); 154: return $result; 155: } 156: 157: %Apache::response::foilgroup = (); 158: 159: sub start_foilgroup { 160: my ( $target, $token, $tagstack, $parstack, $parser, $safeeval, $style ) = 161: @_; 162: %Apache::response::foilgroup = (); 163: $Apache::radiobuttonresponse::conceptgroup = 0; 164: &Apache::response::pushrandomnumber( undef, $target ); 165: return; 166: } 167: 168: sub storesurvey { 169: my ($style) = @_; 170: if ( !&Apache::response::submitted() ) { return ''; } 171: my $response = $env{ 'form.HWVAL_' . $Apache::inputtags::response['-1'] }; 172: &Apache::lonxml::debug("Here I am!:$response:"); 173: if ( $response !~ /[0-9]+/ ) { return ''; } 174: my $part = $Apache::inputtags::part; 175: my $id = $Apache::inputtags::response['-1']; 176: my @whichfoils = @{ $Apache::response::foilgroup{'names'} }; 177: my %responsehash; 178: $responsehash{ $whichfoils[$response] } = $response; 179: my $responsestr = &Apache::lonnet::hash2str(%responsehash); 180: $Apache::lonhomework::results{"resource.$part.$id.submission"} = 181: $responsestr; 182: my %previous = 183: &Apache::response::check_for_previous( $responsestr, $part, $id ); 184: my $ad; 185: 186: if ( $style eq 'anonsurvey' ) { 187: $ad = $Apache::lonhomework::results{"resource.$part.$id.awarddetail"} = 188: 'ANONYMOUS'; 189: } 190: elsif ( $style eq 'anonsurveycred' ) { 191: $ad = $Apache::lonhomework::results{"resource.$part.$id.awarddetail"} = 192: 'ANONYMOUS_CREDIT'; 193: } 194: elsif ( $style eq 'surveycred' ) { 195: $ad = $Apache::lonhomework::results{"resource.$part.$id.awarddetail"} = 196: 'SUBMITTED_CREDIT'; 197: } 198: else { 199: $ad = $Apache::lonhomework::results{"resource.$part.$id.awarddetail"} = 200: 'SUBMITTED'; 201: } 202: &Apache::response::handle_previous( \%previous, $ad ); 203: &Apache::lonxml::debug("submitted a $response<br />\n"); 204: return ''; 205: } 206: 207: sub grade_response { 208: my ( $answer, $whichfoils, $bubbles_per_line ) = @_; 209: 210: if ( !&Apache::response::submitted() ) { return; } 211: my $response; 212: 213: if ( $env{'form.submitted'} eq 'scantron' ) { 214: $response = 215: &Apache::response::getresponse( 1, undef, 216: &bubble_line_count( scalar( @{$whichfoils} ), $bubbles_per_line ), 217: $bubbles_per_line ); 218: 219: } 220: else { 221: $response = $env{ 'form.HWVAL_' . $Apache::inputtags::response['-1'] }; 222: } 223: 224: if ( $response !~ /[0-9]+/ ) { return; } 225: my $part = $Apache::inputtags::part; 226: my $id = $Apache::inputtags::response['-1']; 227: my %responsehash; 228: $responsehash{ $whichfoils->[$response] } = $response; 229: my $responsestr = &Apache::lonnet::hash2str(%responsehash); 230: my %previous = 231: &Apache::response::check_for_previous( $responsestr, $part, $id ); 232: $Apache::lonhomework::results{"resource.$part.$id.submission"} = 233: $responsestr; 234: &Apache::lonxml::debug("submitted a $response<br />\n"); 235: my $ad; 236: 237: if ( $response == $answer ) { 238: $ad = 'EXACT_ANS'; 239: } 240: else { 241: $ad = 'INCORRECT'; 242: } 243: $Apache::lonhomework::results{"resource.$part.$id.awarddetail"} = $ad; 244: &Apache::response::handle_previous( \%previous, $ad ); 245: } 246: 247: sub end_foilgroup { 248: my ( $target, $token, $tagstack, $parstack, $parser, $safeeval, $style ) = 249: @_; 250: 251: my $result; 252: my $bubble_lines; 253: my $answer_count; 254: my $id = $Apache::inputtags::response['-1']; 255: my $part = $Apache::inputtags::part; 256: my $bubbles_per_line = &getbubblesnum( $part, $id ); 257: 258: 259: if ( $target eq 'grade' 260: || $target eq 'web' 261: || $target eq 'answer' 262: || $target eq 'tex' 263: || $target eq 'analyze' ) 264: { 265: my $style = $Apache::lonhomework::type; 266: my $direction = 267: &Apache::lonxml::get_param( 'direction', $parstack, $safeeval, '-2' ); 268: if ( 269: ( 270: ( $style eq 'survey' ) 271: || ( $style eq 'surveycred' ) 272: || ( $style eq 'anonsurvey' ) 273: || ( $style eq 'anonsurveycred' ) 274: ) 275: && ( $target ne 'analyze' ) 276: ) 277: { 278: if ( $target eq 'web' || $target eq 'tex' ) { 279: $result = &displayallfoils( $direction, $target ); 280: } 281: elsif ( $target eq 'answer' ) { 282: $result = &displayallanswers(); 283: } 284: elsif ( $target eq 'grade' ) { 285: $result = &storesurvey($style); 286: } 287: $answer_count = 288: scalar( @{ $Apache::response::foilgroup{'names'} } ); 289: 290: } 291: else { 292: 293: my $name; 294: my $max = 295: &Apache::lonxml::get_param( 'max', $parstack, $safeeval, '-2' ); 296: my $randomize = 297: &Apache::lonxml::get_param( 'randomize', $parstack, $safeeval, 298: '-2' ); 299: my ( $answer, @shown ) = &whichfoils( $max, $randomize ); 300: $answer_count = scalar(@shown); 301: 302: if ( $target eq 'web' || $target eq 'tex' ) { 303: $result = 304: &displayfoils( $target, $answer, \@shown, $direction, 305: $bubbles_per_line ); 306: } 307: elsif ( $target eq 'answer' ) { 308: $result = 309: &displayanswers( $answer, \@shown, $bubbles_per_line ); 310: } 311: elsif ( $target eq 'grade' ) { 312: &grade_response( $answer, \@shown, $bubbles_per_line ); 313: } 314: elsif ( $target eq 'analyze' ) { 315: my $bubble_lines = 316: &bubble_line_count( $answer_count, $bubbles_per_line ); 317: &Apache::response::analyze_store_foilgroup( \@shown, 318: [ 'text', 'value', 'location' ] ); 319: my $part_id = "$part.$id"; 320: push( 321: @{ $Apache::lonhomework::analyze{"$part_id.options"} }, 322: ( 'true', 'false' ) 323: ); 324: 325: } 326: } 327: $Apache::lonxml::post_evaluate = 0; 328: } 329: if ( $target eq 'web' ) { 330: &Apache::response::setup_prior_tries_hash( \&format_prior_answer, 331: [ \%Apache::response::foilgroup ] ); 332: } 333: &Apache::response::poprandomnumber(); 334: $bubble_lines = &bubble_line_count( $answer_count, $bubbles_per_line ); 335: &Apache::lonxml::increment_counter( $bubble_lines, "$part.$id" ); 336: if ( $target eq 'analyze' ) { 337: &Apache::lonhomework::set_bubble_lines(); 338: } 339: return $result; 340: } 341: 342: sub getbubblesnum { 343: my ( $part, $id ) = @_; 344: my $bubbles_per_line; 345: my $default_numbubbles = $default_bubbles_per_line; 346: if ( ( $env{'form.bubbles_per_row'} =~ /^\d+$/ ) 347: && ( $env{'form.bubbles_per_row'} > 0 ) ) 348: { 349: $default_numbubbles = $env{'form.bubbles_per_row'}; 350: } 351: $bubbles_per_line = &Apache::response::get_response_param( $part . "_$id", 352: 'numbubbles', $default_numbubbles ); 353: return $bubbles_per_line; 354: } 355: 356: sub getfoilcounts { 357: my @names; 358: my $truecnt = 0; 359: my $falsecnt = 0; 360: my $name; 361: if ( $Apache::response::foilgroup{'names'} ) { 362: @names = @{ $Apache::response::foilgroup{'names'} }; 363: } 364: foreach $name (@names) { 365: if ( $Apache::response::foilgroup{ $name . '.value' } eq 'true' ) { 366: $truecnt++; 367: } 368: elsif ( $Apache::response::foilgroup{ $name . '.value' } eq 'false' ) { 369: $falsecnt++; 370: } 371: } 372: return ( $truecnt, $falsecnt ); 373: } 374: 375: sub format_prior_answer { 376: my ( $mode, $answer, $other_data ) = @_; 377: my $foil_data = $other_data->[0]; 378: my %response = &Apache::lonnet::str2hash($answer); 379: my ($name) = keys(%response); 380: return 381: '<span class="LC_prior_radiobutton">' 382: . $foil_data->{ $name . '.text' } 383: . '</span>'; 384: 385: } 386: 387: ## 388: # Return the last survey response. The logic is slightly different than that of 389: # get_last_responses. TODO: See if there are chunks of code betweenthis and 390: # get_last_reponses that are common and can be factored. 391: # 392: # @param $part - Problem part under consideration. 393: # @param $showanswer - True if answers should be shown. 394: # @param $id - Problem id. 395: # 396: # @return hash reference. 397: # @retval reference to the has indexed by answer selection that 398: # indicates the most recent answer. 399: # 400: sub get_last_survey_response { 401: my ($part, $showanswer, $id) = @_; 402: 403: my $newvariation; 404: my $lastresponse; # stringified last response. 405: 406: if ( 407: ( 408: ( 409: $Apache::lonhomework::history{"resource.$part.type"} eq 410: 'randomizetry' 411: ) 412: || ( $Apache::lonhomework::type eq 'randomizetry' ) 413: ) 414: && ( $Apache::inputtags::status[-1] eq 'CAN_ANSWER' ) 415: ) 416: { 417: if ( $env{ 'form.' . $part . '.rndseed' } ne 418: $Apache::lonhomework::history{"resource.$part.rndseed"} ) 419: { 420: $newvariation = 1; 421: } 422: } 423: unless ( 424: ( 425: ( 426: $Apache::lonhomework::history{"resource.$part.type"} eq 427: 'anonsurvey' 428: ) 429: || ( $Apache::lonhomework::history{"resource.$part.type"} eq 430: 'anonsurveycred' ) 431: ) 432: && ( defined( $env{'form.grade_symb'} ) ) 433: || ( $newvariation && !$showanswer ) 434: ) 435: { 436: $lastresponse = 437: $Apache::lonhomework::history{"resource.$part.$id.submission"}; 438: } 439: my %lastresponse = &Apache::lonnet::str2hash($lastresponse); 440: 441: 442: return \%lastresponse; 443: 444: } 445: ## 446: # Removes the names from a foil group that are marked as unused. 447: # 448: # @param $names - reference to the array of names to filter. 449: # 450: # @return arrayref 451: # @retval reference to the filtered array. 452: # 453: sub remove_unused { 454: my ($names) = @_; 455: my @result; 456: 457: foreach my $name (@{$names}) { 458: if ($Apache::response::foilgroup{$name . '.value'} ne 'unused') { 459: push(@result, $name); 460: } 461: } 462: return \@result; 463: } 464: ## 465: # Displays all foils in a survey type problem for HTML rendition. 466: # TODO: See if there is any logic in this sub that can be shared 467: # with display_foils_html 468: # 469: # @param $names - ref to array of names of the foils to display. 470: # @param $part - Problem part number. 471: # @param $showanswer - If true, show the answers. 472: # @param $lastresponse - Ref to the last response hash. 473: # @param $direction - Display direction of the radiobuttons. 474: # 475: # @return string 476: # @retval HTML required to display the resource in a browser. 477: # 478: sub display_survey_html { 479: my ($names, $part, $showanswer, $lastresponse, $direction) = @_; 480: my $result; 481: 482: # Figure out a few fragments of html that depend onthe 483: # orientation of the radiobuttons: 484: # closing_html - HTML to emit at the end of the resource. 485: # pre_foil - HTML to emit prior to each foil. 486: # post_foil - HTML to emit following each foil. 487: # 488: # The opening HTML is just added to the $result now 489: # 490: # Figuring these outin advance compresses the loop over foils into something 491: # pretty simple: 492: # 493: # NOTE: There's probably a really cool way to do this with style sheets 494: # and picking the selector based on the orientation, if someone wants to puzzle 495: # that out. In that case, probably the whole thing lives in a <div> and each 496: # foil lives in a <p> 497: # 498: 499: 500: my ($opening_html, $closing_html, $pre_foil, $post_foil) = 501: &html_direction_fragments($direction); 502: 503: $result = $opening_html; 504: 505: # Different rendering depending on whether answers are shown: 506: # I played with different factorings but this seems the most concise/clear... 507: # although I don't like the $showanswer conditino inside the loop. Other things I tried 508: # - two loops..much longer code..no gain in clarity. 509: # - Using a visitor patttern passing it the rendering code chunklets and 510: # an anonymous hash reference for state data etc. Very cool but 511: # quite a bit more code and quite a bit less clear. 512: 513: my $temp = 0; 514: foreach my $name (@{$names}) { 515: $result .= $pre_foil; 516: 517: if ($showanswer) { 518: my $foiltext = $Apache::response::foilgroup{$name . '.text'}; 519: 520: # Bold the prior response: 521: 522: if (defined($lastresponse->{$name})) { 523: $result .= '<b>' . $foiltext . '</b>'; 524: } else { 525: $result .= $foiltext; 526: } 527: } else { 528: $result .= &html_radiobutton( 529: $part, $Apache::inputtags::response['-1'], $name, $lastresponse, $temp 530: ); 531: } 532: 533: $result .= $post_foil; 534: $temp++; 535: } 536: 537: 538: $result .= $closing_html; 539: return $result; 540: 541: } 542: 543: ## 544: # Generate LaTeX for surveys. 545: # 546: # @param $names - names of the foils to display. 547: # @param $showanswer - flag that is true to display answers. 548: # @param $lastresponse - Reference to a hash the indicates the last response. 549: # @param $direction - Orientation of foils ('horiztonal' or otherwise). 550: # @param $venv - LaTeX name for vertical env. 551: # 552: # @return string 553: # @retval LaTeX rendering of the survey question. 554: 555: sub latex_survey { 556: my ($names, $showanswer, $lastresponse, $direction, $venv) = @_; 557: 558: my $result; 559: 560: 561: 562: 563: if ($showanswer) { 564: 565: $result .= "\\begin{$venv}"; 566: foreach my $name (@{$names}) { 567: 568: 569: $result .= '\item \vskip -2mm '; 570: 571: if ( defined( $lastresponse->{$name} ) ) { 572: $result .= '}'; 573: } 574: $result .= $Apache::response::foilgroup{ $name . '.text' } . ' '; 575: } 576: $result .= "\\end{$venv}"; 577: 578: } elsif ( $env{'form.pdfFormFields'} eq 'yes' 579: && $Apache::inputtags::status[-1] eq 'CAN_ANSWER') { 580: $result .= &display_pdf_form($names, $direction, $venv); 581: } else { 582: if ($direction eq 'horizontal') { 583: my @foil_texts = &get_foil_texts($names); 584: $result .= &Apache::caparesponse::make_horizontal_latex_bubbles( 585: $names, \@foil_texts, '$\bigcirc$'); 586: } else { 587: $result .= "\\begin{$venv}"; 588: 589: my $temp = 0; 590: my $i = 0; 591: foreach my $name (@{$names}) { 592: 593: $result .= '\item \vskip -2mm '; 594: 595: if ($env{'form.pdfFormFields'} ne 'yes' 596: or $Apache::inputtags::status[-1] ne 'CAN_ANSWER' ) 597: { 598: $result .= 599: '$\bigcirc$' 600: . $Apache::response::foilgroup{ $name . '.text' } 601: . '\\\\'; #' stupid emacs 602: } 603: 604: $i++; 605: $temp++; 606: 607: $result .= '\vskip 0 mm '; 608: } 609: $result .= "\\end{$venv}"; 610: } 611: 612: 613: } 614: 615: return $result; 616: 617: 618: } 619: ## 620: # Figure out the LaTeX environment in which to wrap the LaTeX vertical output. 621: # 622: # @return string 623: # @retval the environment name. The LaTeX should be wrapped a 624: # \begin{retval} \end{retval} pair. 625: # 626: sub latex_vertical_environment { 627: if ($env{'form.pdfFormFields'} eq 'yes' 628: && $Apache::inputtags::status[-1] eq 'CAN_ANSWER') { 629: return 'itemize'; 630: } else { 631: return 'enumerate'; 632: } 633: } 634: 635: ## 636: # Figure out the key html fragments that depend on the rendering direction: 637: # 638: # @param $direction - 'horizontal' for horizontal direction. 639: # 640: # @return list 641: # @retval (part_start, part_end, foil_start, foil_end) 642: # Where: 643: # - part_start is the HTML to emit at the start of the part. 644: # - part_end is the HTML to emit at the end of the part. 645: # - foil_start is the HTML to emit prior to each foil. 646: # - foil_end is the HTML to emit after each foil 647: # 648: sub html_direction_fragments { 649: my $direction = shift; 650: if ($direction eq 'horizontal') { 651: return ('<table><tr>', '</tr></table>', '<td>', '</td>'); 652: } else { 653: return ('', '<br />', '<br />', ''); 654: } 655: } 656: 657: ## 658: # 659: # Displays all the foils of a problem in a format suitable for 660: # surveys, surveys for credit, anonymous surveys and anonymous surveys for credit. 661: # 662: # @param $direction - Display direction of the choices ('horiztonal' or not). 663: # @param $target - Rendering target. 664: # 665: # @return string 666: # @retval Text that renders for the selected target. 667: # 668: sub displayallfoils{ 669: my ( $direction, $target ) = @_; 670: my $result; 671: &Apache::lonxml::debug("survey style display"); 672: 673: my @names; 674: 675: if ( $Apache::response::foilgroup{'names'} ) { 676: @names = @{ $Apache::response::foilgroup{'names'} }; 677: } 678: 679: 680: my $id = $Apache::inputtags::response['-1']; 681: my $part = $Apache::inputtags::part; 682: 683: my $showanswer = &Apache::response::show_answer(); 684: my $lastresponse = &get_last_survey_response($part, $showanswer, $id); 685: my $used_names = &remove_unused(\@names); 686: 687: 688: if ($target ne 'tex') { 689: $result .= &display_survey_html( 690: $used_names, $part, $showanswer, $lastresponse, $direction 691: ); 692: } else { 693: 694: my $vertical_env = &latex_vertical_environment(); 695: $result .= &latex_survey( 696: $used_names, $showanswer, $lastresponse, $direction, $vertical_env 697: ); 698: 699: } 700: 701: 702: 703: return $result; 704: } 705: 706: 707: 708: sub whichfoils { 709: my ( $max, $randomize ) = @_; 710: 711: my @truelist; 712: my @falselist; 713: my @whichfalse = (); 714: my ( $truecnt, $falsecnt ) = &getfoilcounts(); 715: my $count = 0; 716: 717: # we will add in 1 of the true statements 718: if ( $max > 0 && ( $falsecnt + 1 ) > $max ) { $count = $max } 719: else { $count = $falsecnt + 1; $max = $count; } 720: my $answer = int( &Math::Random::random_uniform() * ($count) ); 721: &Apache::lonxml::debug("Count is $count, $answer is $answer"); 722: my @names; 723: if ( $Apache::response::foilgroup{'names'} ) { 724: @names = @{ $Apache::response::foilgroup{'names'} }; 725: } 726: if ( &Apache::response::showallfoils() ) { 727: @whichfalse = @names; 728: } 729: elsif ( $randomize eq 'no' ) { 730: &Apache::lonxml::debug("No randomization"); 731: my $havetrue = 0; 732: foreach my $name (@names) { 733: if ( $Apache::response::foilgroup{ $name . '.value' } eq 'true' ) { 734: if ( !$havetrue ) { 735: push( @whichfalse, $name ); 736: $havetrue++; 737: $answer = $#whichfalse; 738: } 739: } 740: elsif ( 741: $Apache::response::foilgroup{ $name . '.value' } eq 'false' ) 742: { 743: push( @whichfalse, $name ); 744: } 745: elsif ( 746: $Apache::response::foilgroup{ $name . '.value' } eq 'unused' ) 747: { 748: } 749: else { 750: &Apache::lonxml::error( 751: &HTML::Entities::encode( 752: "No valid value assigned ($Apache::response::foilgroup{$name.'.value'}) for foil $name in <foilgroup>", 753: '<>&"' 754: ) 755: ); 756: } 757: } 758: if ( ( !$havetrue ) 759: && ( $Apache::lonhomework::type ne 'survey' ) 760: && ( $Apache::lonhomework::type ne 'surveycred' ) 761: && ( $Apache::lonhomework::type ne 'anonsurvey' ) 762: && ( $Apache::lonhomework::type ne 'anonsurveycred' ) ) 763: { 764: &Apache::lonxml::error( 765: &mt('There are no true statements available.') . '<br />' ); 766: } 767: } 768: else { 769: my $current = 0; 770: &Apache::lonhomework::showhash(%Apache::response::foilgroup); 771: my ( %top, %bottom ); 772: 773: #first find out where everyone wants to be 774: foreach my $name (@names) { 775: $current++; 776: if ( $Apache::response::foilgroup{ $name . '.value' } eq 'true' ) { 777: push( @truelist, $name ); 778: if ( $Apache::response::foilgroup{ $name . '.location' } eq 779: 'top' ) 780: { 781: $top{$name} = $current; 782: } 783: elsif ( $Apache::response::foilgroup{ $name . '.location' } eq 784: 'bottom' ) 785: { 786: $bottom{$name} = $current; 787: } 788: } 789: elsif ( 790: $Apache::response::foilgroup{ $name . '.value' } eq 'false' ) 791: { 792: push( @falselist, $name ); 793: if ( $Apache::response::foilgroup{ $name . '.location' } eq 794: 'top' ) 795: { 796: $top{$name} = $current; 797: } 798: elsif ( $Apache::response::foilgroup{ $name . '.location' } eq 799: 'bottom' ) 800: { 801: $bottom{$name} = $current; 802: } 803: } 804: elsif ( 805: $Apache::response::foilgroup{ $name . '.value' } eq 'unused' ) 806: { 807: } 808: else { 809: &Apache::lonxml::error( 810: &HTML::Entities::encode( 811: "No valid value assigned ($Apache::response::foilgroup{$name.'.value'}) for foil $name in <foilgroup>", 812: '<>&"' 813: ) 814: ); 815: } 816: } 817: 818: #pick a true statement 819: my $notrue = 0; 820: if ( scalar(@truelist) == 0 ) { $notrue = 1; } 821: my $whichtrue = 822: int( &Math::Random::random_uniform() * ( $#truelist + 1 ) ); 823: &Apache::lonxml::debug( 824: "Max is $max, From $#truelist elms, picking $whichtrue"); 825: my ( @toplist, @bottomlist ); 826: my $topcount = 0; 827: my $bottomcount = 0; 828: 829: # assign everyone to either toplist/bottomlist or whichfalse 830: # which false is randomized, toplist bottomlist are in order 831: while (( ( $#whichfalse + $topcount + $bottomcount ) < $max - 2 ) 832: && ( $#falselist > -1 ) ) 833: { 834: &Apache::lonxml::debug("Have $#whichfalse max is $max"); 835: my $afalse = 836: int( &Math::Random::random_uniform() * ( $#falselist + 1 ) ); 837: &Apache::lonxml::debug("From $#falselist elms, picking $afalse"); 838: $afalse = splice( @falselist, $afalse, 1 ); 839: &Apache::lonxml::debug("Picked $afalse"); 840: &Apache::lonhomework::showhash( ( 'names' => \@names ) ); 841: &Apache::lonhomework::showhash(%top); 842: if ( $top{$afalse} ) { 843: $toplist[ $top{$afalse} ] = $afalse; 844: $topcount++; 845: } 846: elsif ( $bottom{$afalse} ) { 847: $bottomlist[ $bottom{$afalse} ] = $afalse; 848: $bottomcount++; 849: } 850: else { 851: push( @whichfalse, $afalse ); 852: } 853: } 854: &Apache::lonxml::debug("Answer wants $answer"); 855: my $truename = $truelist[$whichtrue]; 856: my $dosplice = 1; 857: if ( ($notrue) 858: && ( $Apache::lonhomework::type ne 'survey' ) 859: && ( $Apache::lonhomework::type ne 'surveycred' ) 860: && ( $Apache::lonhomework::type ne 'anonsurvey' ) 861: && ( $Apache::lonhomework::type ne 'anonsurveycred' ) ) 862: { 863: $dosplice = 0; 864: &Apache::lonxml::error( 865: &mt('There are no true statements available.') . '<br />' ); 866: } 867: 868: #insert the true statement, keeping track of where it wants to be 869: if ( $Apache::response::foilgroup{ $truename . '.location' } eq 'top' 870: && $dosplice ) 871: { 872: $toplist[ $top{$truename} ] = $truename; 873: $answer = -1; 874: foreach my $top ( reverse(@toplist) ) { 875: if ($top) { $answer++; } 876: if ( $top eq $truename ) { last; } 877: } 878: $dosplice = 0; 879: } 880: elsif ( 881: $Apache::response::foilgroup{ $truename . '.location' } eq 'bottom' 882: && $dosplice ) 883: { 884: $bottomlist[ $bottom{$truename} ] = $truename; 885: $answer = -1; 886: foreach my $bot (@bottomlist) { 887: if ($bot) { $answer++; } 888: if ( $bot eq $truename ) { last; } 889: } 890: $answer += $topcount + $#whichfalse + 1; 891: $dosplice = 0; 892: } 893: else { 894: if ( $topcount > 0 || $bottomcount > 0 ) { 895: my $inc = 1; 896: if ( ( $bottomcount > 0 ) 897: && ( $Apache::lonhomework::type ne 'exam' ) ) 898: { 899: $inc = 2; 900: } 901: $answer = int( 902: &Math::Random::random_uniform() * ( $#whichfalse + $inc ) ) 903: + $topcount; 904: } 905: } 906: &Apache::lonxml::debug("Answer now wants $answer"); 907: 908: #add the top items to the top, bottom items to the bottom 909: for ( my $i = 0 ; $i <= $#toplist ; $i++ ) { 910: if ( $toplist[$i] ) { unshift( @whichfalse, $toplist[$i] ) } 911: } 912: for ( my $i = 0 ; $i <= $#bottomlist ; $i++ ) { 913: if ( $bottomlist[$i] ) { push( @whichfalse, $bottomlist[$i] ) } 914: } 915: 916: #if the true statement is randomized insert it into the list 917: if ($dosplice) { 918: splice( @whichfalse, $answer, 0, $truelist[$whichtrue] ); 919: } 920: } 921: &Apache::lonxml::debug("Answer is $answer"); 922: return ( $answer, @whichfalse ); 923: } 924: 925: ## 926: # Return a list of foil texts given foil names. 927: # 928: # @param $whichfoils - Reference to a list of foil names. 929: # 930: # @return array 931: # @retval foil texts 932: # 933: sub get_foil_texts { 934: my ($whichfoils) = @_; 935: my @foil_texts; 936: 937: foreach my $name (@{$whichfoils}) { 938: push(@foil_texts, $Apache::response::foilgroup{$name . '.text'}); 939: } 940: return @foil_texts; 941: } 942: 943: ## 944: # Generate the HTML for a single html foil. 945: # @param $part - The part for which the response is being generated. 946: # @param $fieldname - The basename of the radiobutton field 947: # @param $name - The foilname. 948: # @param $last_responses - Reference to a hash that holds the most recent 949: # responses. 950: # @param $value - radiobutton value. 951: # 952: # @return text 953: # @retval The generated html. 954: # 955: sub html_radiobutton { 956: my ($part, $fieldname, $name, $last_responses, $value) = @_; 957: 958: my $result='<label>'; 959: 960: $result .= '<input type="radio" 961: onchange="javascript:setSubmittedPart(' . "'$part');\"" 962: . 'name="HWVAL_' . $fieldname . '"' 963: . "value='$value'"; 964: 965: if (defined($last_responses->{$name})) { 966: $result .= ' checked="checked" '; 967: } 968: $result .= ' />'; 969: $result .= $Apache::response::foilgroup{$name . '.text'}; 970: $result .= '</label>'; 971: 972: return $result; 973: 974: } 975: ## 976: # Return a reference to the last response hash. This hash has exactly 977: # one or zero entries. The one entry is keyed by the foil 'name' of 978: # the prior response 979: # 980: # @param $part - Number of the problem part. 981: # 982: # @return reference to a hash. 983: # @retval see above. 984: # 985: sub get_last_response { 986: my ($part) = @_; 987: 988: my $id = $Apache::inputtags::response['-1']; 989: my ( $lastresponse, $newvariation ); 990: 991: if ((( $Apache::lonhomework::history{"resource.$part.type"} eq 'randomizetry') 992: || ( $Apache::lonhomework::type eq 'randomizetry' ) 993: ) 994: && ( $Apache::inputtags::status[-1] eq 'CAN_ANSWER' ) 995: ) 996: { 997: 998: if ( $env{ 'form.' . $part . '.rndseed' } ne 999: $Apache::lonhomework::history{"resource.$part.rndseed"} ) 1000: { 1001: $newvariation = 1; 1002: } 1003: } 1004: unless ($newvariation) { 1005: $lastresponse = 1006: $Apache::lonhomework::history{"resource.$part.$id.submission"}; 1007: } 1008: my %lastresponse = &Apache::lonnet::str2hash($lastresponse); 1009: 1010: return \%lastresponse; 1011: } 1012: 1013: ## 1014: # Display foils in html rendition.: 1015: # 1016: # @param $whichfoils - Set of foils to display. 1017: # @param $target - Rendition target...there are several html targets. 1018: # @param $direction - 'horizontal' if layout is horizontal. 1019: # @param $part - Part of the problem that's being displayed. 1020: # @param $show_answer- True if answers should be shown. 1021: # 1022: # @return string 1023: # @retval generated html. 1024: # 1025: sub display_foils_html { 1026: my ($whichfoils, $target, $direction, $part, $show_answer) = @_; 1027: my $result; 1028: 1029: 1030: # if the answers get shown, we need to label each item as correct or 1031: # incorrect. 1032: 1033: my ($opening_html, $finalclose, $item_pretext, $item_posttext) = 1034: &html_direction_fragments($direction); 1035: 1036: $result .= $opening_html; 1037: 1038: 1039: if ($show_answer) { 1040: 1041: foreach my $name (@{$whichfoils}) { 1042: 1043: # If the item gets further surrounded by tags, this 1044: # holds the closures for those tages. 1045: 1046: my $item_closetag = ''; 1047: 1048: $result .= $item_pretext; 1049: 1050: # Label each foil as correct or incorrect: 1051: 1052: if ($Apache::response::foilgroup{$name . '.value'} eq 'true') { 1053: $result .= &mt('Correct:') . '<b>'; 1054: $item_closetag .= '</b>'; 1055: 1056: } else { 1057: $result .= &mt('Incorrect'); 1058: } 1059: 1060: # Web rendition encloses the 1061: # item text in a label tag as well: 1062: 1063: if ($target eq 'web') { 1064: $result .= '<label>'; 1065: $item_closetag = '</label>' . $item_closetag; 1066: } 1067: $result .= $Apache::response::foilgroup{$name . '.text'}; 1068: $result .= $item_closetag; 1069: $result .= $item_posttext; 1070: $result .= "\n"; # make the html a bit more readable. 1071: } 1072: 1073: 1074: } else { 1075: my $lastresponse = &get_last_response($part); 1076: 1077: my $item_no = 0; 1078: foreach my $name (@{$whichfoils}) { 1079: $result .= $item_pretext; 1080: $result .= &html_radiobutton( 1081: $part, $Apache::inputtags::response[-1], 1082: $name, $lastresponse, $item_no 1083: ); 1084: $result .= $item_posttext; 1085: $item_no++; 1086: } 1087: 1088: } 1089: $result .= $finalclose; 1090: 1091: return $result; 1092: } 1093: ## 1094: # Display foils in exam mode for latex 1095: # 1096: # @param $whichfoils - Reference to an array that contains the foil names to display 1097: # @param $bubbles_per_line - Number of bubbles on a line. 1098: # @param $direction - Rendering direction 'horizontal' is what we're looking for. 1099: # @param $venv - Name of LaTeX environment to use for vertical rendering. 1100: # 1101: # @return string 1102: # @return the latex rendering of the exam problem. 1103: # 1104: # 1105: sub display_latex_exam { 1106: my ($whichfoils, $bubbles_per_line, $direction, $venv) = @_; 1107: my $result; 1108: my $numlines; 1109: my $bubble_number = 0; 1110: my $line = 0; 1111: my $i = 0; 1112: 1113: 1114: if ($direction eq 'horizontal') { 1115: 1116: # Marshall the display text for each foil and turn things over to 1117: # Apache::response::make_horizontal_bubbles: 1118: 1119: my @foil_texts = &get_foil_texts($whichfoils); 1120: $result .= &Apache::caparesponse::make_horizontal_latex_bubbles( 1121: $whichfoils, \@foil_texts, '$\bigcirc$'); 1122: 1123: } else { 1124: $result .= "\\begin{$venv}"; 1125: 1126: # This section puts out the prefix that tells the user 1127: # (if necessary) to only choose one bubble in the next n lines 1128: # for problems with more than one line worth of bubbles in the grid sheet: 1129: 1130: my $numitems = scalar( @{$whichfoils} ); 1131: $numlines = int( $numitems / $bubbles_per_line ); 1132: if ( ( $numitems % $bubbles_per_line ) != 0 ) { 1133: $numlines++; 1134: } 1135: if ( $numlines < 1 ) { 1136: $numlines = 1; 1137: } 1138: if ( $numlines > 1 ) { 1139: my $linetext; 1140: for ( my $i = 0 ; $i < $numlines ; $i++ ) { 1141: $linetext .= $Apache::lonxml::counter + $i . ', '; 1142: } 1143: $linetext =~ s/,\s$//; 1144: $result .= 1145: '\item[\small {\textbf{' 1146: . $linetext . '}}]' 1147: . ' {\footnotesize ' 1148: . &mt( '(Bubble once in [_1] lines)', $numlines ) 1149: . '} \hspace*{\fill} \\\\'; 1150: } 1151: else { 1152: $result .= '\item[\textbf{' . $Apache::lonxml::counter . '}.]'; 1153: } 1154: 1155: # Now output the bubbles themselves: 1156: 1157: foreach my $name (@{$whichfoils}) { 1158: if ( $bubble_number >= $bubbles_per_line ) { 1159: $line++; 1160: $i = 0; 1161: $bubble_number = 0; 1162: } 1163: my $identifier; 1164: if ( $numlines > 1 ) { 1165: $identifier = $Apache::lonxml::counter + $line; 1166: } 1167: $result .= 1168: '{\small \textbf{' 1169: . $identifier 1170: . $alphabet[$i] 1171: . '}}$\bigcirc$' 1172: . $Apache::response::foilgroup{ $name . '.text' } 1173: . '\\\\'; #' stupid emacs -- it thinks it needs that apostrophe to close the quote 1174: 1175: $i++; 1176: $bubble_number++; 1177: } 1178: $result .= "\\end{$venv}"; 1179: 1180: } 1181: 1182: return $result; 1183: 1184: } 1185: 1186: ## 1187: # Display latex when exam mode is not on. 1188: # 1189: # @param $whichfoils - The foils to display 1190: # @param $direction - Display direction ('horizontal' is what matters to us). 1191: # @param $venv - Vertical env. to use for vertical rendering. 1192: # @param $vend - End the vertical environment being used. 1193: # 1194: # @return string 1195: # @retval - The LaTeX rendering of the resource.' 1196: # 1197: sub display_latex { 1198: my ($whichfoils, $direction, $venv) = @_; 1199: my $result; 1200: 1201: # how we render depends on the direction. 1202: # Vertical is some kind of list environment determined by vbegin/vend. 1203: # Horizontal is a table that is generated by 1204: # Apache::caparesponse::make_horizontal_latex_bubbles with an empty string 1205: # for the actual bubble text. 1206: 1207: if ($direction eq 'horizontal') { 1208: my @foil_texts = &get_foil_texts($whichfoils); 1209: $result .= &Apache::caparesponse::make_horizontal_latex_bubbles( 1210: $whichfoils, \@foil_texts, ''); 1211: } else { 1212: $result .= "\\begin{$venv}"; 1213: foreach my $name (@{$whichfoils}) { 1214: $result .= '\vspace*{-2 mm}\item ' 1215: . $Apache::response::foilgroup{ $name . '.text' }; 1216: } 1217: 1218: $result .= "\\end{$venv}"; 1219: } 1220: return $result; 1221: } 1222: 1223: 1224: ## 1225: # Render foils for a PDF form. This is a variant of tex rednering that provides 1226: # sufficient markup that the final PDF is a form that can be filled in online, 1227: # or offline. 1228: # 1229: # @param $whichfoils - References an array of foils to display in the order in which 1230: # they should be displayed. 1231: # @param $direction - Rendering direction. 'horiztonal' means inputs are laid out 1232: # horizontally otherwise they are stacked vertically. 1233: # @param $venv - Vertical environment in which to wrap the foils. 1234: # 1235: # @return string 1236: # @retval String containing the rendering of the resource. 1237: # 1238: # TODO: Take into account direction!!! 1239: # 1240: sub display_pdf_form { 1241: my ($whichfoils, $direction, $venv) = @_; 1242: my $temp = 0; 1243: my $result; 1244: 1245: $result .= "\\begin{$venv}"; 1246: foreach my $name ( @{$whichfoils} ) { 1247: 1248: my $fieldname = 1249: $env{'request.symb'} 1250: . '&part_' 1251: . $Apache::inputtags::part 1252: . '&radiobuttonresponse' 1253: . '&HWVAL_' 1254: . $Apache::inputtags::response['-1']; 1255: $result .= '\item[{' 1256: . &Apache::lonxml::print_pdf_radiobutton( $fieldname, 1257: $temp ) 1258: . '}]' 1259: . $Apache::response::foilgroup{ $name . '.text' } 1260: . "\n"; 1261: 1262: $temp++; 1263: } 1264: $result .= "\\end{$venv}"; 1265: 1266: return $result; 1267: } 1268: 1269: 1270: ## 1271: # Display selected foils: This is really just a dispatchter to appropriate renderers 1272: # 1273: # @param $target - Target (e.g. 'tex'...). 1274: # @param $answer - True if answers should be shown. 1275: # @param $whichfoils - Array of foil selectors that indicate which foils shouild be 1276: # rendered, in rendering order. 1277: # @param $direction- Rendering direction ('horizontal' is the one we look for, 1278: # otherwise foils are rendered one per line vertically. 1279: # @param $bubbles_per_line - number of exam bubbles per line. 1280: # 1281: # @return string 1282: # @retval The rendered problem. 1283: 1284: sub displayfoils { 1285: my ( $target, $answer, $whichfoils, $direction, $bubbles_per_line ) = @_; 1286: my $result; 1287: 1288: my $part = $Apache::inputtags::part; 1289: my $solved = $Apache::lonhomework::history{"resource.$part.solved"}; 1290: 1291: # Show answers html. 1292: 1293: if ( ( $target ne 'tex' ) 1294: && &Apache::response::show_answer() ) 1295: { 1296: 1297: $result = &display_foils_html( 1298: $whichfoils, $target, $direction, $part, 1); 1299: 1300: # other html 1301: } elsif ($target ne 'tex') { 1302: $result = &display_foils_html($whichfoils, $target, $direction, $part, 1303: 0, 0); 1304: 1305: # LaTeX rendering: 1306: } else { 1307: 1308: 1309: my $id = $Apache::inputtags::response['-1']; 1310: my $part = $Apache::inputtags::part; 1311: my $numlines; 1312: 1313: # Decide how to bracket the list of foils: 1314: 1315: my $vertical_env = &latex_vertical_environment(); 1316: 1317: # Rendering for latex exams. 1318: 1319: if ( ( $Apache::lonhomework::type eq 'exam' ) ) 1320: { 1321: $result .= &display_latex_exam( 1322: $whichfoils, $bubbles_per_line, $direction, $vertical_env); 1323: 1324: $result .= '\vskip 0mm '; 1325: 1326: } else { 1327: 1328: # Different rendering for PDF form than for a 1329: # 'regular' answer direction is honored in both of those 1330: # 1331: 1332: if ( ($env{'form.pdfFormFields'} eq 'yes') 1333: && ($Apache::inputtags::status[-1] eq 'CAN_ANSWER')) 1334: { 1335: $result .= &display_pdf_form($whichfoils, $direction, $vertical_env); 1336: } else { 1337: $result .= &display_latex($whichfoils, $direction, $vertical_env ); 1338: } 1339: $result .= '\vskip 0 mm '; 1340: 1341: } 1342: 1343: 1344: } 1345: return $result; 1346: } 1347: 1348: sub displayallanswers { 1349: my @names; 1350: if ( $Apache::response::foilgroup{'names'} ) { 1351: @names = @{ $Apache::response::foilgroup{'names'} }; 1352: } 1353: my $result = &Apache::response::answer_header('radiobuttonresponse'); 1354: foreach my $name (@names) { 1355: $result .= 1356: &Apache::response::answer_part( 'radiobuttonresponse', 1357: $Apache::response::foilgroup{ $name . '.value' } ); 1358: } 1359: $result .= &Apache::response::answer_footer('radiobuttonresponse'); 1360: return $result; 1361: } 1362: 1363: sub displayanswers { 1364: my ( $answer, $whichopt, $bubbles_per_line ) = @_; 1365: my $result; 1366: 1367: if ( $Apache::lonhomework::type eq 'exam' ) { 1368: my $line = int( $answer / $bubbles_per_line ); 1369: my $correct = ( 'A' .. 'Z' )[ $answer % $bubbles_per_line ]; 1370: $result .= 1371: &Apache::response::answer_header( 'radiobuttonresponse', $line ); 1372: $result .= 1373: &Apache::response::answer_part( 'radiobuttonresponse', $correct ); 1374: } 1375: else { 1376: $result .= &Apache::response::answer_header('radiobuttonresponse'); 1377: } 1378: foreach my $name ( @{$whichopt} ) { 1379: $result .= 1380: &Apache::response::answer_part( 'radiobuttonresponse', 1381: $Apache::response::foilgroup{ $name . '.value' } ); 1382: } 1383: $result .= &Apache::response::answer_footer('radiobuttonresponse'); 1384: return $result; 1385: } 1386: 1387: sub start_conceptgroup { 1388: my ( $target, $token, $tagstack, $parstack, $parser, $safeeval, $style ) = 1389: @_; 1390: $Apache::radiobuttonresponse::conceptgroup = 1; 1391: %Apache::response::conceptgroup = (); 1392: my $result; 1393: if ( $target eq 'edit' ) { 1394: $result .= &Apache::edit::tag_start( $target, $token ); 1395: $result .= 1396: &Apache::edit::text_arg( 'Concept:', 'concept', $token, '50' ) 1397: . &Apache::edit::end_row() 1398: . &Apache::edit::start_spanning_row(); 1399: } 1400: elsif ( $target eq 'modified' ) { 1401: my $constructtag = 1402: &Apache::edit::get_new_args( $token, $parstack, $safeeval, 1403: 'concept' ); 1404: if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); } 1405: } 1406: return $result; 1407: } 1408: 1409: sub end_conceptgroup { 1410: my ( $target, $token, $tagstack, $parstack, $parser, $safeeval, $style ) = 1411: @_; 1412: $Apache::radiobuttonresponse::conceptgroup = 0; 1413: my $result; 1414: if ( $target eq 'web' 1415: || $target eq 'grade' 1416: || $target eq 'answer' 1417: || $target eq 'tex' 1418: || $target eq 'analyze' ) 1419: { 1420: &Apache::response::pick_foil_for_concept( $target, 1421: [ 'value', 'text', 'location' ], 1422: \%Apache::hint::radiobutton, $parstack, $safeeval ); 1423: } 1424: elsif ( $target eq 'edit' ) { 1425: $result = &Apache::edit::end_table(); 1426: } 1427: return $result; 1428: } 1429: 1430: sub insert_conceptgroup { 1431: my $result = 1432: "\n\t\t<conceptgroup concept=\"\">" 1433: . &insert_foil() 1434: . "\n\t\t</conceptgroup>\n"; 1435: return $result; 1436: } 1437: 1438: sub start_foil { 1439: my ( $target, $token, $tagstack, $parstack, $parser, $safeeval, $style ) = 1440: @_; 1441: my $result = ''; 1442: if ( $target eq 'web' || $target eq 'tex' || $target eq 'analyze' ) { 1443: &Apache::lonxml::startredirection; 1444: if ( $target eq 'analyze' ) { 1445: &Apache::response::check_if_computed( $token, $parstack, $safeeval, 1446: 'value' ); 1447: } 1448: } 1449: elsif ( $target eq 'edit' ) { 1450: $result = &Apache::edit::tag_start( $target, $token ); 1451: $result .= &Apache::edit::text_arg( 'Name:', 'name', $token ); 1452: $result .= &Apache::edit::select_or_text_arg( 1453: 'Correct Option:', 'value', 1454: [ 'unused', 'true', 'false' ], $token 1455: ); 1456: my $randomize = 1457: &Apache::lonxml::get_param( 'randomize', $parstack, $safeeval, '-3' ); 1458: if ( $randomize ne 'no' ) { 1459: $result .= 1460: &Apache::edit::select_arg( 'Location:', 'location', 1461: [ 'random', 'top', 'bottom' ], $token ); 1462: } 1463: $result .= 1464: &Apache::edit::end_row() . &Apache::edit::start_spanning_row(); 1465: } 1466: elsif ( $target eq 'modified' ) { 1467: my $constructtag = 1468: &Apache::edit::get_new_args( $token, $parstack, $safeeval, 'value', 1469: 'name', 'location' ); 1470: if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); } 1471: } 1472: return $result; 1473: } 1474: 1475: sub end_foil { 1476: my ( $target, $token, $tagstack, $parstack, $parser, $safeeval, $style ) = 1477: @_; 1478: my $text = ''; 1479: if ( $target eq 'web' || $target eq 'tex' || $target eq 'analyze' ) { 1480: $text = &Apache::lonxml::endredirection; 1481: } 1482: if ( $target eq 'web' 1483: || $target eq 'grade' 1484: || $target eq 'answer' 1485: || $target eq 'tex' 1486: || $target eq 'analyze' ) 1487: { 1488: my $value = &Apache::lonxml::get_param( 'value', $parstack, $safeeval ); 1489: if ( $value ne 'unused' ) { 1490: my $name = 1491: &Apache::lonxml::get_param( 'name', $parstack, $safeeval ); 1492: if ( $name eq "" ) { 1493: &Apache::lonxml::warning( 1494: &mt( 1495: 'Foils without names exist. This can cause problems to malfunction.' 1496: ) 1497: ); 1498: $name = $Apache::lonxml::curdepth; 1499: } 1500: if ( defined( $Apache::response::foilnames{$name} ) ) { 1501: &Apache::lonxml::error( 1502: &mt( 1503: 'Foil name [_1] appears more than once. Foil names need to be unique.', 1504: '<b><tt>' . $name . '</tt></b>' 1505: ) 1506: ); 1507: } 1508: $Apache::response::foilnames{$name}++; 1509: my $location = 1510: &Apache::lonxml::get_param( 'location', $parstack, $safeeval ); 1511: if ( $Apache::radiobuttonresponse::conceptgroup 1512: && !&Apache::response::showallfoils() ) 1513: { 1514: push @{ $Apache::response::conceptgroup{'names'} }, $name; 1515: $Apache::response::conceptgroup{"$name.value"} = $value; 1516: $Apache::response::conceptgroup{"$name.text"} = $text; 1517: $Apache::response::conceptgroup{"$name.location"} = $location; 1518: } 1519: else { 1520: push @{ $Apache::response::foilgroup{'names'} }, $name; 1521: $Apache::response::foilgroup{"$name.value"} = $value; 1522: $Apache::response::foilgroup{"$name.text"} = $text; 1523: $Apache::response::foilgroup{"$name.location"} = $location; 1524: } 1525: } 1526: } 1527: return ''; 1528: } 1529: 1530: sub insert_foil { 1531: return ' 1532: <foil name="" value="unused"> 1533: <startouttext /> 1534: <endouttext /> 1535: </foil>'; 1536: } 1537: 1538: 1; 1539: __END__ 1540: 1541: 1542: 1543: =head1 NAME 1544: 1545: Apache::radiobuttonresponse 1546: 1547: =head1 SYNOPSIS 1548: 1549: Handles multiple-choice style responses. 1550: 1551: This is part of the LearningOnline Network with CAPA project 1552: described at http://www.lon-capa.org. 1553: 1554: =head1 SUBROUTINES 1555: 1556: =over 1557: 1558: =item start_radiobuttonresponse() 1559: 1560: =item bubble_line_count() 1561: 1562: =item end_radiobuttonresponse() 1563: 1564: =item start_foilgroup() 1565: 1566: =item storesurvey() 1567: 1568: =item grade_response() 1569: 1570: =item end_foilgroup() 1571: 1572: =item getfoilcounts() 1573: 1574: =item format_prior_answer() 1575: 1576: =item displayallfoils() 1577: 1578: =item &whichfoils($max,$randomize) 1579: 1580: Randomizes the list of foils. 1581: Respects 1582: - each foils desire to be randomized 1583: - the existance of Concept groups of foils (select 1 foil from each) 1584: - and selects a single correct statement from all possilble true statments 1585: - and limits it to a toal of $max foils 1586: 1587: WARNING: this routine uses the random number generator, it should only 1588: be called once per target, otherwise it can cause randomness changes in 1589: homework problems. 1590: 1591: Arguments 1592: $max - maximum number of foils to select (including the true one) 1593: (so a max of 5 is: 1 true, 4 false) 1594: 1595: $randomize - whether to randomize the listing of foils, by default 1596: will randomize, only if randomize is 'no' will it not 1597: 1598: Returns 1599: $answer - location in the array of the correct answer 1600: @foils - array of foil names in to display order 1601: 1602: =item displayfoils() 1603: 1604: =item displayallanswers() 1605: 1606: =item displayanswers() 1607: 1608: =item start_conceptgroup() 1609: 1610: =item end_conceptgroup() 1611: 1612: =item insert_conceptgroup() 1613: 1614: =item start_foil() 1615: 1616: =item end_foil() 1617: 1618: =item insert_foil() 1619: 1620: =back 1621: 1622: =cut 1623: