File:  [LON-CAPA] / loncom / homework / radiobuttonresponse.pm
Revision 1.162: download - view: text, annotated - select for diffs
Wed Dec 19 04:07:35 2018 UTC (5 years, 4 months ago) by raeburn
Branches: MAIN
CVS tags: version_2_12_X, HEAD
- Bug 6724.

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

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