File:  [LON-CAPA] / loncom / homework / radiobuttonresponse.pm
Revision 1.156: download - view: text, annotated - select for diffs
Thu Apr 25 17:58:32 2013 UTC (11 years ago) by bisitz
Branches: MAIN
CVS tags: HEAD
Improved spacing in colorful editor:
- More and consistent spacing between options
- Minimize missunderstanding to which element a help icon belongs to
- Less needed total window width in editor

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

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