File:  [LON-CAPA] / loncom / homework / radiobuttonresponse.pm
Revision 1.158: download - view: text, annotated - select for diffs
Mon Jan 19 15:35:53 2015 UTC (9 years, 3 months ago) by goltermann
Branches: MAIN
CVS tags: HEAD
authoring space overhaul
this update tries to improve the user experience of the authoring space.

added codemirror for xml editor and script tags in colorful editor
added possibility to deactivate codemirror in author settings
added dropdown menu to insert problem templates into xml editor (thanks to tobias reinhardt)
added feature of saving current scrollposition on save when editing problems
added possibility to fold blocks in colorful editor, this state will be saved and restored
added shortcuts to create empty problems, html files and directories

and other smaller features and bugfixes

    1: # The LearningOnline Network with CAPA
    2: # mutliple choice style responses
    3: #
    4: # $Id: radiobuttonresponse.pm,v 1.158 2015/01/19 15:35:53 goltermann 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 ('', '<br />', '<br />', '');
  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: 	}
 1035: 	
 1036: 	# Now output the bubbles themselves:
 1037: 	
 1038: 	foreach my $name (@{$whichfoils}) {
 1039: 	    if ( $bubble_number >= $bubbles_per_line ) {
 1040: 		$line++;
 1041: 		$i             = 0;
 1042: 		$bubble_number = 0;
 1043: 	    }
 1044: 	    my $identifier;
 1045: 	    if ( $numlines > 1 ) {
 1046: 		$identifier = $Apache::lonxml::counter + $line;
 1047: 	    }
 1048: 	    my $preindent;
 1049: 	    if ($bubble_number > 0) {
 1050: 		$preindent = '\hspace*{3 mm}';
 1051: 	    }
 1052: 	    my $foiltext = $Apache::response::foilgroup{$name . '.text'};
 1053: 	    $foiltext =~ s/\\noindent//; # forgive me for I have sinned..
 1054: 	    $result .= '{\small \textbf{'
 1055: 		. $identifier  .$preindent
 1056: 		. $alphabet[$i]
 1057: 		. '}}$\bigcirc$'
 1058: 		. $foiltext
 1059: 	    . '\\\\';    #' stupid emacs -- it thinks it needs that apostrophe to close the quote
 1060: 	    
 1061: 	    $i++;
 1062: 	    $bubble_number++;
 1063: 	}
 1064: 
 1065:     }	
 1066: 
 1067:     return $result;
 1068:     
 1069: }
 1070: 
 1071: ##
 1072: #  Display latex when exam mode is not on.
 1073: #
 1074: #  @param $whichfoils - The foils to display
 1075: #  @param $direction  - Display direction ('horizontal' is what matters to us).
 1076: #  @param $venv       - Vertical env. to use for vertical rendering.
 1077: #  @param  $vend      - End the vertical environment being used.
 1078: #
 1079: #  @return string
 1080: #  @retval - The LaTeX rendering of the resource.'
 1081: #
 1082: sub display_latex {
 1083:     my ($whichfoils, $direction, $venv) = @_;
 1084:     my $result;
 1085: 
 1086:     # how we render depends on the direction.
 1087:     # Vertical is some kind of list environment determined by vbegin/vend.
 1088:     # Horizontal is a table that is generated by 
 1089:     # Apache::caparesponse::make_horizontal_latex_bubbles with an empty string
 1090:     # for the actual bubble text.
 1091: 
 1092:     if ($direction eq 'horizontal') {
 1093: 	my @foil_texts = &get_foil_texts($whichfoils);
 1094: 	$result .= &Apache::caparesponse::make_horizontal_latex_bubbles(
 1095: 	    $whichfoils, \@foil_texts, '');
 1096:     } else {
 1097: 	$result .= "\\begin{$venv}";
 1098: 	foreach my $name (@{$whichfoils}) {
 1099: 	    $result .=  '\vspace*{-2 mm}\item '
 1100: 		. $Apache::response::foilgroup{ $name . '.text' };
 1101: 	}
 1102: 	
 1103: 	$result .= "\\end{$venv}";
 1104:     }
 1105:     return $result;
 1106: }
 1107: 
 1108: 
 1109: ##
 1110: #  Render foils for a PDF form. This is a variant of tex rednering that provides
 1111: #  sufficient markup that the final PDF is a form that can be filled in online,
 1112: #  or offline.
 1113: #
 1114: # @param $whichfoils - References an array of foils to display in the order in which
 1115: #                     they should be displayed.
 1116: # @param $direction  - Rendering direction.  'horiztonal' means inputs are laid out
 1117: #                      horizontally otherwise they are stacked vertically.
 1118: # @param $venv       - Vertical environment in which to wrap the foils.
 1119: #
 1120: # @return string
 1121: # @retval String containing the rendering of the resource.
 1122: #
 1123: # TODO: Take into account direction!!!
 1124: #
 1125: sub display_pdf_form {
 1126:     my ($whichfoils, $direction, $venv) = @_;
 1127:     my $temp = 0;
 1128:     my $result;
 1129: 
 1130:     $result .= "\\begin{$venv}";
 1131:     foreach my $name ( @{$whichfoils} ) {
 1132: 	
 1133: 	my $fieldname =
 1134: 	    $env{'request.symb'} 
 1135: 	. '&part_'
 1136: 	    . $Apache::inputtags::part
 1137: 	    . '&radiobuttonresponse'
 1138: 	    . '&HWVAL_'
 1139: 	    . $Apache::inputtags::response['-1'];
 1140: 	$result .= '\item[{'
 1141: 	    . &Apache::lonxml::print_pdf_radiobutton( $fieldname,
 1142: 						      $temp )
 1143: 	    . '}]'
 1144: 	    . $Apache::response::foilgroup{ $name . '.text' }
 1145: 	. "\n";
 1146: 	
 1147: 	$temp++;
 1148:     }
 1149:     $result .= "\\end{$venv}";
 1150: 
 1151:     return $result;
 1152: }
 1153: 
 1154: 
 1155: ##
 1156: # Display selected foils:  This is really just a dispatchter to appropriate renderers
 1157: #  
 1158: # @param $target   - Target (e.g. 'tex'...).
 1159: # @param $answer   - True if answers should be shown.
 1160: # @param $whichfoils - Array of foil selectors that indicate which foils shouild be
 1161: #                      rendered, in rendering order.
 1162: # @param $direction- Rendering direction ('horizontal' is the one we look for,
 1163: #                    otherwise foils are rendered one per line vertically.
 1164: # @param $bubbles_per_line - number of exam bubbles per line.
 1165: #
 1166: # @return string
 1167: # @retval The rendered problem.
 1168: 
 1169: sub displayfoils {
 1170:     my ($target,$answer,$whichfoils,$direction, $bubbles_per_line)=@_;
 1171:     my $result;
 1172: 
 1173: 
 1174:     my $part   = $Apache::inputtags::part;
 1175:     my $solved = $Apache::lonhomework::history{"resource.$part.solved"};
 1176: 
 1177:     # Show answers html.
 1178: 
 1179:     if ( ( $target ne 'tex' )
 1180:         && &Apache::response::show_answer() )
 1181:     {
 1182: 
 1183: 	$result = &display_foils_html(
 1184: 	    $whichfoils, $target, $direction, $part,  1);
 1185: 	
 1186: 	# other html
 1187:     }  elsif ($target ne 'tex') {
 1188: 	    $result = &display_foils_html($whichfoils, $target, $direction, $part,
 1189: 					  0, 0);
 1190: 
 1191:        # LaTeX rendering:
 1192:     } else {
 1193: 
 1194: 
 1195:         my $id            = $Apache::inputtags::response['-1'];
 1196:         my $part          = $Apache::inputtags::part;
 1197: 	my $numlines;
 1198: 	
 1199: 	# Decide how to bracket the list of foils:
 1200: 
 1201: 	my $vertical_env = &latex_vertical_environment();
 1202: 
 1203: 	# Rendering for latex exams.
 1204: 	
 1205: 	if ( ( $Apache::lonhomework::type eq 'exam' ) )
 1206: 	{
 1207: 	    $result .= &display_latex_exam(
 1208: 		$whichfoils, $bubbles_per_line, $direction, $vertical_env);
 1209: 
 1210: 	    $result .= '\vskip 0mm ';
 1211: 	    
 1212: 	} else {	    
 1213: 
 1214: 	    # Different rendering for PDF form than for a
 1215: 	    # 'regular' answer direction is honored in both of those
 1216: 	    #
 1217: 
 1218: 	    if ( ($env{'form.pdfFormFields'} eq 'yes')
 1219: 		    && ($Apache::inputtags::status[-1] eq 'CAN_ANSWER'))
 1220: 	    {
 1221: 		$result .= &display_pdf_form($whichfoils, $direction, $vertical_env);
 1222: 	    } else {
 1223: 		$result .= &display_latex($whichfoils,  $direction, $vertical_env );
 1224: 	    }
 1225: 	    $result .= '\vskip 0 mm '; 
 1226:        
 1227: 	}
 1228:     }
 1229:     return $result;
 1230: }
 1231: 
 1232: sub displayallanswers {
 1233:     my @names;
 1234:     if ( $Apache::response::foilgroup{'names'} ) {
 1235: 	@names= @{ $Apache::response::foilgroup{'names'} };
 1236:     }
 1237:     my $result=&Apache::response::answer_header('radiobuttonresponse');
 1238:     foreach my $name (@names) {
 1239: 	$result.=&Apache::response::answer_part('radiobuttonresponse',
 1240: 				$Apache::response::foilgroup{$name.'.value'});
 1241:     }
 1242:     $result.=&Apache::response::answer_footer('radiobuttonresponse');
 1243:     return $result;
 1244: }
 1245: 
 1246: sub displayanswers {
 1247:     my ($answer, $whichopt, $bubbles_per_line)=@_;
 1248:     my $result;
 1249: 
 1250:     if ($Apache::lonhomework::type eq 'exam') {
 1251: 	my $line = int($answer/$bubbles_per_line);
 1252: 	my $correct = ('A'..'Z')[$answer%$bubbles_per_line];
 1253: 	$result .= &Apache::response::answer_header('radiobuttonresponse',
 1254: 						    $line);
 1255: 	$result .= &Apache::response::answer_part('radiobuttonresponse',
 1256: 						  $correct);
 1257:     } else {
 1258: 	$result .= &Apache::response::answer_header('radiobuttonresponse');
 1259:     }
 1260:     foreach my $name (@{ $whichopt }) {
 1261: 	$result.=&Apache::response::answer_part('radiobuttonresponse',
 1262: 						$Apache::response::foilgroup{$name.'.value'});
 1263:     }
 1264:     $result.=&Apache::response::answer_footer('radiobuttonresponse');
 1265:     return $result;
 1266: }
 1267: 
 1268: sub start_conceptgroup {
 1269:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
 1270:     $Apache::radiobuttonresponse::conceptgroup=1;
 1271:     %Apache::response::conceptgroup=();
 1272:     my $result;
 1273:     if ($target eq 'edit') {
 1274: 	$result.=&Apache::edit::tag_start($target,$token);
 1275: 	$result.=&Apache::edit::text_arg('Concept:','concept',$token,'50').
 1276: 	    &Apache::edit::end_row().&Apache::edit::start_spanning_row();
 1277:     } elsif ($target eq 'modified') {
 1278: 	my $constructtag=&Apache::edit::get_new_args($token,$parstack,
 1279: 						     $safeeval,'concept');
 1280: 	if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); }
 1281:     }
 1282:     return $result;
 1283: }
 1284: 
 1285: sub end_conceptgroup {
 1286:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
 1287:     $Apache::radiobuttonresponse::conceptgroup=0;
 1288:     my $result;
 1289:     if ($target eq 'web' || $target eq 'grade' || $target eq 'answer'  ||
 1290: 	$target eq 'tex' || $target eq 'analyze') {
 1291: 	&Apache::response::pick_foil_for_concept($target,
 1292: 						 ['value','text','location'],
 1293: 						 \%Apache::hint::radiobutton,
 1294: 						 $parstack,$safeeval);
 1295:     } elsif ($target eq 'edit') {
 1296: 	$result=&Apache::edit::end_table();
 1297:     }
 1298:     return $result;
 1299: }
 1300: 
 1301: sub insert_conceptgroup {
 1302:     my $result="\n\t\t<conceptgroup concept=\"\">".&insert_foil()."\n\t\t</conceptgroup>\n";
 1303:     return $result;
 1304: }
 1305: 
 1306: sub start_foil {
 1307:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
 1308:     my $result='';
 1309:     if ($target eq 'web' || $target eq 'tex' || $target eq 'analyze') {
 1310: 	&Apache::lonxml::startredirection;
 1311: 	if ($target eq 'analyze') {
 1312: 	    &Apache::response::check_if_computed($token,$parstack,$safeeval,'value');
 1313: 	}
 1314:     } elsif ($target eq 'edit') {
 1315: 	$result=&Apache::edit::tag_start($target,$token);
 1316: 	$result.=&Apache::edit::text_arg('Name:','name',$token);
 1317: 	$result.=&Apache::edit::select_or_text_arg('Correct Option:','value',
 1318: 						   ['unused','true','false'],
 1319: 						   $token);
 1320: 	my $randomize=&Apache::lonxml::get_param('randomize',$parstack,
 1321: 						 $safeeval,'-3');
 1322: 	if ($randomize ne 'no') {
 1323: 	    $result.=&Apache::edit::select_arg('Location:','location',
 1324: 					       ['random','top','bottom'],$token);
 1325: 	}
 1326: 	$result.=&Apache::edit::end_row().&Apache::edit::start_spanning_row();
 1327:     } elsif ($target eq 'modified') {
 1328: 	my $constructtag=&Apache::edit::get_new_args($token,$parstack,
 1329: 						     $safeeval,'value','name',
 1330: 						     'location');
 1331: 	if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); }
 1332:     } 
 1333:     return $result;
 1334: }
 1335: 
 1336: sub end_foil {
 1337:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
 1338:     my $text='';
 1339:     if ($target eq 'web' || $target eq 'tex' || $target eq 'analyze') {
 1340: 	$text=&Apache::lonxml::endredirection;
 1341:     }
 1342:     if ($target eq 'web' || $target eq 'grade' || $target eq 'answer'
 1343: 	|| $target eq 'tex' || $target eq 'analyze') {
 1344: 	my $value = &Apache::lonxml::get_param('value',$parstack,$safeeval);
 1345: 	if ($value ne 'unused') {
 1346: 	    my $name = &Apache::lonxml::get_param('name',$parstack,$safeeval);
 1347: 	    if ($name eq "") {
 1348: 		&Apache::lonxml::warning(&mt('Foils without names exist. This can cause problems to malfunction.'));
 1349: 		$name=$Apache::lonxml::curdepth;
 1350: 	    }
 1351: 	    if (defined($Apache::response::foilnames{$name})) {
 1352: 		&Apache::lonxml::error(&mt('Foil name [_1] appears more than once. Foil names need to be unique.','<b><tt>'.$name.'</tt></b>'));
 1353: 	    }
 1354: 	    $Apache::response::foilnames{$name}++;
 1355: 	    my $location =&Apache::lonxml::get_param('location',$parstack,
 1356: 						     $safeeval);
 1357: 	    if ( $Apache::radiobuttonresponse::conceptgroup
 1358: 		 && !&Apache::response::showallfoils() ) {
 1359: 		push @{ $Apache::response::conceptgroup{'names'} }, $name;
 1360: 		$Apache::response::conceptgroup{"$name.value"} = $value;
 1361: 		$Apache::response::conceptgroup{"$name.text"} = $text;	
 1362: 		$Apache::response::conceptgroup{"$name.location"} = $location;
 1363: 	    } else {
 1364: 		push @{ $Apache::response::foilgroup{'names'} }, $name;
 1365: 		$Apache::response::foilgroup{"$name.value"} = $value;
 1366: 		$Apache::response::foilgroup{"$name.text"} = $text;
 1367: 		$Apache::response::foilgroup{"$name.location"} = $location;
 1368: 	    }
 1369: 	}
 1370:     }
 1371:     return '';
 1372: }
 1373: 
 1374: sub insert_foil {
 1375:     return '
 1376: <foil name="" value="unused">
 1377: <startouttext />
 1378: <endouttext />
 1379: </foil>';
 1380: }
 1381: 
 1382: 1;
 1383: __END__
 1384: 
 1385: 
 1386: 
 1387: =head1 NAME
 1388: 
 1389: Apache::radiobuttonresponse
 1390: 
 1391: =head1 SYNOPSIS
 1392: 
 1393: Handles multiple-choice style responses.
 1394: 
 1395: This is part of the LearningOnline Network with CAPA project
 1396: described at http://www.lon-capa.org.
 1397: 
 1398: =head1 SUBROUTINES
 1399: 
 1400: =over
 1401: 
 1402: =item start_radiobuttonresponse()
 1403: 
 1404: =item bubble_line_count()
 1405: 
 1406: =item end_radiobuttonresponse()
 1407: 
 1408: =item start_foilgroup()
 1409: 
 1410: =item storesurvey()
 1411: 
 1412: =item grade_response()
 1413: 
 1414: =item end_foilgroup()
 1415: 
 1416: =item getfoilcounts()
 1417: 
 1418: =item format_prior_answer()
 1419: 
 1420: =item displayallfoils()
 1421: 
 1422: =item &whichfoils($max,$randomize)
 1423: 
 1424: Randomizes the list of foils.
 1425: Respects
 1426:   - each foils desire to be randomized
 1427:   - the existance of Concept groups of foils (select 1 foil from each)
 1428:   - and selects a single correct statement from all possilble true statments
 1429:   - and limits it to a toal of $max foils
 1430: 
 1431: WARNING: this routine uses the random number generator, it should only
 1432: be called once per target, otherwise it can cause randomness changes in
 1433: homework problems.
 1434: 
 1435: Arguments
 1436:   $max - maximum number of foils to select (including the true one)
 1437:          (so a max of 5 is: 1 true, 4 false)
 1438: 
 1439:   $randomize - whether to randomize the listing of foils, by default
 1440:                will randomize, only if randomize is 'no' will it not
 1441: 
 1442: Returns
 1443:   $answer - location in the array of the correct answer
 1444:   @foils  - array of foil names in to display order
 1445: 
 1446: =item displayfoils()
 1447: 
 1448: =item displayallanswers()
 1449: 
 1450: =item displayanswers()
 1451: 
 1452: =item start_conceptgroup()
 1453: 
 1454: =item end_conceptgroup()
 1455: 
 1456: =item insert_conceptgroup()
 1457: 
 1458: =item start_foil()
 1459: 
 1460: =item end_foil()
 1461: 
 1462: =item insert_foil()
 1463: 
 1464: =back
 1465: 
 1466: =cut
 1467:  

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