File:  [LON-CAPA] / loncom / homework / radiobuttonresponse.pm
Revision 1.153.6.9: download - view: text, annotated - select for diffs
Sat Feb 4 20:40:07 2012 UTC (12 years, 3 months ago) by foxr
Branches: BZ4492-feature_horizontal_radioresponse
Diff to branchpoint 1.153: preferred, unified
BZ 4492 - Get survey printing to honor the $direction.

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

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