File:  [LON-CAPA] / loncom / homework / radiobuttonresponse.pm
Revision 1.157.2.1: download - view: text, annotated - select for diffs
Tue Apr 7 15:26:24 2015 UTC (9 years ago) by raeburn
Branches: version_2_11_X
CVS tags: version_2_11_1
Diff to branchpoint 1.157: preferred, unified
- For 2.11
  Backport 1.159

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

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