File:  [LON-CAPA] / loncom / homework / radiobuttonresponse.pm
Revision 1.153.6.10: download - view: text, annotated - select for diffs
Sun Feb 5 16:11:57 2012 UTC (12 years, 3 months ago) by foxr
Branches: BZ4492-feature_horizontal_radioresponse
CVS tags: BZ4492-merge
Diff to branchpoint 1.153: preferred, unified
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>&nbsp;'
   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:           . '&nbsp;' x 3
   98:           . &Apache::edit::select_arg( 'Randomize Foil Order:',
   99:             'randomize', [ 'yes', 'no' ], $token )
  100:           . '&nbsp;' 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:  

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>