File:  [LON-CAPA] / loncom / homework / caparesponse / caparesponse.pm
Revision 1.253: download - view: text, annotated - select for diffs
Fri Dec 14 11:17:09 2012 UTC (11 years, 5 months ago) by foxr
Branches: MAIN
CVS tags: HEAD
BZ 6577 - Ensure that computed answers all will have an appropriate number of
significant figures to pass the validation test...well actualy all user answers
get extended to the appropriate number of sigfigs so that answers will never
fail due to significance.
  Students will still need to force answers into a form that has the
correct number of sigfigs (e.g for 3 sigfigs not 300 but 3.00e2)..

    1: # The LearningOnline Network with CAPA
    2: # caparesponse definition
    3: #
    4: # $Id: caparesponse.pm,v 1.249.8.2 2012/02/04 20:40:15 foxr 
    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
   22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   23: #
   24: # /home/httpd/html/adm/gpl.txt
   25: #
   26: # http://www.lon-capa.org/
   27: #
   28: 
   29: package Apache::caparesponse;
   30: use strict;
   31: use capa;
   32: use Safe::Hole;
   33: use Apache::lonmaxima();
   34: use Apache::lonlocal;
   35: use Apache::lonnet;
   36: use Apache::lonmsg();
   37: use Apache::response();
   38: use Storable qw(dclone);
   39: use Apache::lonnet;
   40: 
   41: BEGIN {
   42:     &Apache::lonxml::register('Apache::caparesponse',('numericalresponse','stringresponse','formularesponse'));
   43: }
   44: 
   45: my %answer;
   46: my @answers;
   47: my @alphabet=('A'..'Z');
   48: 
   49: sub get_answer { return %answer; };
   50: sub push_answer{ push(@answers,dclone(\%answer)); undef(%answer) }
   51: sub pop_answer { %answer = %{pop(@answers)}; };
   52: 
   53: my $cur_name;
   54: my $tag_internal_answer_name = 'INTERNAL';
   55: 
   56: sub start_answer {
   57:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_;
   58:     my $result;
   59:     $cur_name = &Apache::lonxml::get_param('name',$parstack,$safeeval);
   60:     if ($cur_name =~ /^\s*$/) { $cur_name = $Apache::lonxml::curdepth; }
   61:     my $type = &Apache::lonxml::get_param('type',$parstack,$safeeval);
   62:     if (!defined($type) && $tagstack->[-2] eq 'answergroup') {
   63: 	$type = &Apache::lonxml::get_param('type',$parstack,$safeeval,-2);
   64:     }
   65:     if (!defined($type)) { $type = 'ordered' };
   66:     $answer{$cur_name}= { 'type' => $type,
   67: 			  'answers' => [] };
   68:     if ($target eq 'edit') {
   69:   	$result.=&Apache::edit::tag_start($target,$token);
   70: 	$result.=&Apache::edit::text_arg('Name:','name',$token);
   71: 	$result.=&Apache::edit::select_arg('Type:','type',
   72: 					   [['ordered',  'Ordered'  ],
   73: 					    ['unordered','Unordered'],],
   74: 					   $token);
   75: 	$result.=&Apache::edit::end_row().&Apache::edit::start_spanning_row();
   76:     } elsif ($target eq 'modified') {
   77: 	my $constructtag = &Apache::edit::get_new_args($token,$parstack,
   78: 						       $safeeval,'name',
   79: 						       'type');
   80: 	if ($constructtag) {
   81: 	    $result = &Apache::edit::rebuild_tag($token);
   82: 	    $result.= &Apache::edit::handle_insert();
   83: 	}
   84:     }
   85:     return $result;
   86: }
   87: 
   88: sub end_answer {
   89:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_;
   90:     my $result;
   91:     if ($target eq 'edit') {
   92: 	$result .= &Apache::edit::tag_end();
   93:     }
   94: 
   95:     undef($cur_name);
   96:     return $result;
   97: }
   98: 
   99: sub insert_answer {
  100:     return '
  101:         <answer>
  102:             <value></value>
  103:         </answer>';
  104: }
  105: 
  106: sub start_answergroup {
  107:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_;
  108:     my $result;
  109:     if ($target eq 'edit') {
  110:   	$result.=&Apache::edit::tag_start($target,$token);
  111: 	$result.=&Apache::edit::select_arg('Type:','type',
  112: 					   [['ordered',  'Ordered'  ],
  113: 					    ['unordered','Unordered'],],
  114: 					   $token);
  115: 	$result.=&Apache::edit::end_row().&Apache::edit::start_spanning_row();
  116:     } elsif ($target eq 'modified') {
  117: 	my $constructtag = &Apache::edit::get_new_args($token,$parstack,
  118: 						       $safeeval,'name',
  119: 						       'type');
  120: 	if ($constructtag) {
  121: 	    $result = &Apache::edit::rebuild_tag($token);
  122: 	    $result.= &Apache::edit::handle_insert();
  123: 	}
  124:     }
  125:     return $result;
  126: }
  127: 
  128: sub end_answergroup {
  129:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_;
  130:     my $result;
  131:     if ($target eq 'web') {
  132:     	if (  &Apache::response::show_answer() ) {  
  133: 	    my $partid = $Apache::inputtags::part;
  134: 	    my $id = $Apache::inputtags::response[-1];
  135: 	    &set_answertext($Apache::lonhomework::history{"resource.$partid.$id.answername"},
  136: 			    $target,$token,$tagstack,$parstack,$parser,
  137: 			    $safeeval,-2);
  138: 	}
  139:     } elsif ($target eq 'edit') {
  140: 	$result .= &Apache::edit::tag_end();
  141:     }
  142:     return $result;
  143: }
  144: 
  145: sub insert_answergroup {
  146:     return '
  147:     <answergroup>
  148:         <answer>
  149:             <value></value>
  150:         </answer>
  151:     </answergroup>';
  152: }
  153: 
  154: sub start_value {
  155:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  156:     my $result;
  157:     if ( $target eq 'web' || $target eq 'tex' ||
  158: 	 $target eq 'grade' || $target eq 'webgrade' ||
  159: 	 $target eq 'answer' || $target eq 'analyze' ) {
  160: 	my $bodytext = &Apache::lonxml::get_all_text("/value",$parser,$style);
  161: 	$bodytext = &Apache::run::evaluate($bodytext,$safeeval,
  162: 					   $$parstack[-1]);
  163: 	
  164: 	push(@{ $answer{$cur_name}{'answers'} },[$bodytext]);
  165: 	
  166:     } elsif ($target eq 'edit') {
  167:   	$result.=&Apache::edit::tag_start($target,$token);
  168: 	my $bodytext = &Apache::lonxml::get_all_text("/value",$parser,$style);
  169: 	$result.=&Apache::edit::editline($token->[1],$bodytext,undef,40).
  170: 	    &Apache::edit::end_row();
  171:     } elsif ($target eq 'modified') {
  172: 	$result=$token->[4].&Apache::edit::modifiedfield('/value',$parser);
  173:     }
  174:     return $result;
  175: }
  176: 
  177: sub end_value {
  178:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_;
  179:     my $result;
  180:     if ($target eq 'edit') {
  181: 	$result = &Apache::edit::end_table();
  182:     }
  183:     return $result;
  184: }
  185: 
  186: sub insert_value {
  187:     return '
  188:             <value></value>';
  189: }
  190: 
  191: sub start_vector {
  192:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  193:     my $result;
  194:     if ( $target eq 'web' || $target eq 'tex' ||
  195: 	 $target eq 'grade' || $target eq 'webgrade' ||
  196: 	 $target eq 'answer' || $target eq 'analyze' ) {
  197: 	my $bodytext = &Apache::lonxml::get_all_text("/vector",$parser,$style);
  198: 	my @values = &Apache::run::run($bodytext,$safeeval,$$parstack[-1]);
  199: 	if (@values == 1) {
  200: 	    @values = split(',',$values[0]);
  201: 	}
  202: 	push(@{ $answer{$cur_name}{'answers'} },\@values);
  203:     } elsif ($target eq 'edit') {
  204:   	$result.=&Apache::edit::tag_start($target,$token);
  205: 	my $bodytext = &Apache::lonxml::get_all_text("/vector",$parser,$style);
  206: 	$result.=&Apache::edit::editline($token->[1],$bodytext,undef,40).
  207: 	    &Apache::edit::end_row();
  208:     } elsif ($target eq 'modified') {
  209: 	$result=$token->[4].&Apache::edit::modifiedfield('/vector',$parser);
  210:     }
  211:     return $result;
  212: }
  213: 
  214: sub end_vector {
  215:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_;
  216:     my $result;
  217:     if ($target eq 'edit') {
  218: 	$result = &Apache::edit::end_table();
  219:     }
  220:     return $result;
  221: }
  222: 
  223: sub insert_vector {
  224:     return '
  225:             <value></value>';
  226: }
  227: 
  228: sub start_array {
  229:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  230:     my $result;
  231:     if ( $target eq 'web' || $target eq 'tex' ||
  232: 	 $target eq 'grade' || $target eq 'webgrade' ||
  233: 	 $target eq 'answer' || $target eq 'analyze' ) {
  234: 	my $bodytext = &Apache::lonxml::get_all_text("/array",$parser,$style);
  235: 	my @values = &Apache::run::evaluate($bodytext,$safeeval,
  236: 					    $$parstack[-1]);
  237: 	push(@{ $answer{$cur_name}{'answers'} },@values);
  238:     }
  239:     return $result;
  240: }
  241: 
  242: sub end_array {
  243:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_;
  244:     my $result;
  245:     return $result;
  246: }
  247: 
  248: sub start_unit {
  249:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_;
  250:     my $result;
  251:     return $result;
  252: }
  253: 
  254: sub end_unit {
  255:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_;
  256:     my $result;
  257:     return $result;
  258: }
  259: 
  260: sub start_numericalresponse {
  261:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_;
  262:     &Apache::lonxml::register('Apache::caparesponse',
  263: 			      ('answer','answergroup','value','array','unit',
  264: 			       'vector'));
  265:     push(@Apache::lonxml::namespace,'caparesponse');
  266:     my $id = &Apache::response::start_response($parstack,$safeeval);
  267:     my $result;
  268:     undef(%answer);
  269:     undef(%{$safeeval->varglob('LONCAPA::CAPAresponse_args')});
  270:     if ($target eq 'edit') {
  271: 	$result.=&Apache::edit::tag_start($target,$token);
  272: 	$result.=&Apache::edit::text_arg('Answer:','answer',$token);
  273: 	if ($token->[1] eq 'numericalresponse') {
  274: 	    $result.=&Apache::edit::text_arg('Incorrect Answers:','incorrect',
  275: 					     $token).
  276: 		&Apache::loncommon::help_open_topic('numerical_wrong_answers');
  277: 	    $result.=&Apache::edit::text_arg('Unit:','unit',$token,5).
  278: 		&Apache::loncommon::help_open_topic('Physical_Units');
  279: 	    $result.=&Apache::edit::text_arg('Format:','format',$token,4).
  280: 		&Apache::loncommon::help_open_topic('Numerical_Response_Format');
  281: 	} elsif ($token->[1] eq 'formularesponse') {
  282: 	    $result.=&Apache::edit::text_arg('Sample Points:','samples',
  283: 					     $token,40).
  284: 	      &Apache::loncommon::help_open_topic('Formula_Response_Sampling');
  285: 	}
  286:         $result.=&Apache::edit::text_arg('Pre-Processor Subroutine:','preprocess',
  287:                                              $token,10);
  288: 	$result.=&Apache::edit::end_row().&Apache::edit::start_spanning_row();
  289:     } elsif ($target eq 'modified') {
  290: 	my $constructtag;
  291: 	if ($token->[1] eq 'numericalresponse') {
  292: 	    $constructtag=&Apache::edit::get_new_args($token,$parstack,
  293: 						      $safeeval,'answer',
  294:  						      'incorrect','unit',
  295: 						      'format','preprocess');
  296: 	} elsif ($token->[1] eq 'formularesponse') {
  297: 	    $constructtag=&Apache::edit::get_new_args($token,$parstack,
  298: 						      $safeeval,'answer',
  299: 						      'samples','preprocess');
  300: 	}
  301: 	if ($constructtag) {
  302: 	    $result = &Apache::edit::rebuild_tag($token);
  303: 	    $result.=&Apache::edit::handle_insert();
  304: 	}
  305:     } elsif ($target eq 'meta') {
  306: 	$result=&Apache::response::meta_package_write('numericalresponse');
  307:     } elsif ($target eq 'answer' || $target eq 'grade') {
  308: 	&Apache::response::reset_params();
  309:     } elsif ($target eq 'web') {
  310: 	my $partid = $Apache::inputtags::part;
  311: 	my $hideunit=&Apache::lonnet::EXT('resource.'.$partid.'_'.$id.'.turnoffunit');
  312: 	&Apache::lonxml::debug("Got unit $hideunit for $partid $id");
  313: 	#no way to enter units, with radio buttons
  314: 	if ((lc($hideunit) eq "yes") && ($Apache::lonhomework::type ne 'exam')) {
  315: 	    my $unit=&Apache::lonxml::get_param_var('unit',$parstack,
  316: 						    $safeeval);
  317: 	    if ($unit =~ /\S/) { $result.=" (in $unit) "; }
  318: 	}
  319: 	if (  &Apache::response::show_answer() ) {
  320: 	    &set_answertext($tag_internal_answer_name,$target,$token,$tagstack,
  321: 			    $parstack,$parser,$safeeval,-1);
  322: 	}
  323:     }
  324:     return $result;
  325: }
  326: 
  327: sub set_answertext {
  328:     my ($name,$target,$token,$tagstack,$parstack,$parser,$safeeval,
  329: 	$response_level) = @_;
  330:     &add_in_tag_answer($parstack,$safeeval,$response_level);
  331: 
  332:     if ($name eq '' || !ref($answer{$name})) {
  333: 	if (ref($answer{$tag_internal_answer_name})) {
  334: 	    $name = $tag_internal_answer_name;
  335: 	} else {
  336: 	    $name = (sort(keys(%answer)))[0];
  337: 	}
  338:     }
  339:     return if ($name eq '' || !ref($answer{$name}));
  340: 
  341:     my (@formats)=&Apache::lonxml::get_param_var('format',$parstack,
  342: 						 $safeeval,$response_level);
  343:     my $unit=&Apache::lonxml::get_param_var('unit',$parstack,$safeeval,
  344: 					    $response_level);
  345: 
  346:     &Apache::lonxml::debug("answer looks to be $name");
  347:     my @answertxt;
  348:     for (my $i=0; $i < scalar(@{$answer{$name}{'answers'}}); $i++) {
  349: 	my $answertxt;
  350: 	my $answer=$answer{$name}{'answers'}[$i];
  351: 	foreach my $element (@$answer) {
  352: 	    if ( scalar(@$tagstack)
  353: 		 && $tagstack->[$response_level] ne 'numericalresponse') {
  354: 		$answertxt.=$element.',';
  355: 	    } else {
  356: 		my $format;
  357: 		if ($#formats > 0) {
  358: 		    $format=$formats[$i];
  359: 		} else {
  360: 		    $format=$formats[0];
  361: 		}
  362: 		if ($unit=~/\$/) { $format="\$".$format; $unit=~s/\$//g; }
  363: 		if ($unit=~/\,/) { $format="\,".$format; $unit=~s/\,//g; }
  364: 		my $formatted=&format_number($element,$format,$target,
  365: 					     $safeeval);
  366: 		$answertxt.=' '.$formatted.',';
  367: 	    }
  368: 	    
  369: 	}
  370: 	chop($answertxt);
  371: 	if ($target eq 'web') {
  372: 	    $answertxt.=" $unit ";
  373: 	}
  374: 
  375: 	push(@answertxt,$answertxt)
  376:     }
  377: 	
  378:     my $id = $Apache::inputtags::response[-1];
  379:     $Apache::inputtags::answertxt{$id}=\@answertxt;
  380: }
  381: 
  382: sub setup_capa_args {
  383:     my ($safeeval,$parstack,$args,$response) = @_;
  384:     my $args_ref= \%{$safeeval->varglob('LONCAPA::CAPAresponse_args')};
  385:     undef(%{ $args_ref });
  386:  
  387:     foreach my $arg (@{$args}) {
  388: 	$$args_ref{$arg}=
  389: 	    &Apache::lonxml::get_param($arg,$parstack,$safeeval);
  390:     }
  391:     foreach my $key (keys(%Apache::inputtags::params)) {
  392: 	$$args_ref{$key}=$Apache::inputtags::params{$key};
  393:     }
  394:     &setup_capa_response($args_ref,$response);
  395:     return $args_ref;
  396: }
  397: 
  398: sub setup_capa_response {
  399:     my ($args_ref,$response) = @_;   
  400: 
  401:     if (ref($response)) {
  402: 	$$args_ref{'response'}=dclone($response);
  403:     } else {
  404: 	$$args_ref{'response'}=dclone([$response]);
  405:     }
  406: }
  407: 
  408: sub check_submission {
  409:     my ($response,$partid,$id,$tag,$parstack,$safeeval,$ignore_sig)=@_;
  410:     my @args = ('type','tol','sig','format','unit','calc','samples','preprocess');
  411:     my $args_ref = &setup_capa_args($safeeval,$parstack,\@args,$response);
  412: 
  413:     my $hideunit=
  414: 	&Apache::lonnet::EXT('resource.'.$partid.'_'.$id.'.turnoffunit');
  415:         #no way to enter units, with radio buttons
  416:     if ($Apache::lonhomework::type eq 'exam' ||
  417: 	lc($hideunit) eq "yes") {
  418: 	delete($$args_ref{'unit'});
  419:     }
  420:     #sig fig don't make much sense either
  421:     if (($Apache::lonhomework::type eq 'exam' ||
  422: 	 &Apache::response::submitted('scantron') ||
  423: 	 $ignore_sig) &&
  424: 	$tag eq 'numericalresponse') {
  425: 	delete($$args_ref{'sig'});
  426:     }
  427:     
  428:     if ($tag eq 'formularesponse') {
  429: 	if ($$args_ref{'samples'}) {
  430: 	    $$args_ref{'type'}='fml';
  431: 	} else {
  432: 	    $$args_ref{'type'}='math';
  433: 	}
  434:     } elsif ($tag eq 'numericalresponse') {
  435: 	$$args_ref{'type'}='float';
  436:     } elsif ($tag eq 'stringresponse') {
  437:         if ($$args_ref{'type'} eq '') {
  438:             $$args_ref{'type'} = 'ci';
  439:         }
  440:     }
  441: 
  442:     &add_in_tag_answer($parstack,$safeeval);
  443: 
  444:     if (!%answer) {
  445: 	&Apache::lonxml::error("No answers are defined");
  446:     }
  447: 
  448:     my (@final_awards,@final_msgs,@names);
  449:     # $Apache::lonxml::debug = 1;	# DEBUG
  450:     foreach my $name (keys(%answer)) {
  451: 	&Apache::lonxml::debug(" doing $name with ".join(':',@{ $answer{$name}{'answers'} }));
  452: 	
  453: 	${$safeeval->varglob('LONCAPA::CAPAresponse_answer')}=dclone($answer{$name});
  454: 	&setup_capa_response($args_ref,$response);
  455: 	use Time::HiRes;
  456: 	my $t0 = [Time::HiRes::gettimeofday()];
  457: 	my ($result,@msgs) = 
  458: 	    &Apache::run::run("&caparesponse_check_list()",$safeeval);
  459: 	&Apache::lonxml::debug("checking $name $result with $response took ".&Time::HiRes::tv_interval($t0));
  460: 	&Apache::lonxml::debug('msgs are '.join(':',@msgs));
  461: 	my ($awards)=split(/:/,$result);
  462: 	my @awards= split(/,/,$awards);
  463: 	my ($ad, $msg) = &Apache::inputtags::finalizeawards(\@awards,\@msgs);
  464: 	push(@final_awards,$ad);
  465: 	push(@final_msgs,$msg);
  466: 	push(@names,$name);
  467:     }
  468:     # $Apache::lonxml::debug = 0;	# DEBUG
  469:     my ($ad, $msg, $name) = &Apache::inputtags::finalizeawards(\@final_awards,
  470: 							       \@final_msgs,
  471: 							       \@names,1);
  472:     &Apache::lonxml::debug(" name of picked award is $name from ".join(', ',@names));
  473:     return($ad,$msg, $name);
  474: }
  475: 
  476: sub stringresponse_gradechange {
  477:     my ($part,$id,$previous,$caller,$response,$ad,$type) = @_;
  478:     return unless (ref($previous) eq 'HASH');
  479:     my ($prevarray,$prevaward);
  480:     my %typenames = (
  481:                      cs => 'Case sensitive',
  482:                      ci => 'Case insensitive',
  483:                     );
  484:     if ($caller eq 'cs') {
  485:         return unless (ref($previous->{'version'}) eq 'ARRAY');
  486:         $prevarray = $previous->{'version'};
  487:         $prevaward = $previous->{'award'};
  488:     } elsif ($caller eq 'ci') {
  489:         return unless (ref($previous->{'versionci'}) eq 'ARRAY');
  490:         $prevarray = $previous->{'versionci'};
  491:         $prevaward = $previous->{'awardci'};
  492:     } else {
  493:         return;
  494:     }
  495:     my $count=0;
  496:     my %count_lookup;
  497:     foreach my $i (1..$Apache::lonhomework::history{'version'}) {
  498:         my $prefix = $i.":resource.$part";
  499:         next if (!exists($Apache::lonhomework::history{"$prefix.award"}));
  500:         $count++;
  501:         $count_lookup{$i} = $count;
  502:     }
  503:     my ($symb,$courseid,$domain,$name) = &Apache::lonnet::whichuser();
  504:     my %coursedesc = &Apache::lonnet::coursedescription($courseid);
  505:     my $cdom = $coursedesc{'domain'};
  506:     my $versions = ' (submissions: '.join(', ',map {$count_lookup{$_} } @{$prevarray}).')';
  507:     my $warning = "String Response ($typenames{$type}) grading discrepancy: award for response of $response changed from $prevaward".$versions." to $ad; user: $name:$domain in course: $courseid for part: $part response: $id for symb: $symb";
  508:     &Apache::lonnet::logthis($warning);
  509:     my $origmail = $Apache::lonnet::perlvar{'lonAdmEMail'};
  510:     my $recipients = &Apache::loncommon::build_recipient_list(undef,'errormail',
  511:                                                               $cdom,$origmail);
  512:     if ($recipients ne '') {
  513:         &Apache::lonmsg::sendemail($recipients,'Stringresponse Grading Discrepancy',$warning);
  514:     }
  515:     return;
  516: }
  517: 
  518: sub add_in_tag_answer {
  519:     my ($parstack,$safeeval,$response_level) = @_;
  520:     my @answer=&Apache::lonxml::get_param_var('answer',$parstack,$safeeval,
  521: 					      $response_level);
  522:     &Apache::lonxml::debug('answer is'.join(':',@answer));
  523:     if (@answer && $answer[0] =~ /\S/) {
  524: 	$answer{$tag_internal_answer_name}= {'type' => 'ordered',
  525: 					     'answers' => [\@answer] };
  526:     }
  527: }
  528: 
  529: sub capa_formula_fix {
  530:    my ($expression)=@_;
  531:    return &Apache::response::implicit_multiplication($expression);
  532: }
  533: 
  534: sub end_numericalresponse {
  535:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
  536: 
  537:     &Apache::lonxml::deregister('Apache::caparesponse',
  538: 			      ('answer','answergroup','value','array','unit',
  539: 			       'vector'));
  540:     pop(@Apache::lonxml::namespace);
  541: 
  542:     my $increment=1;
  543:     my $result = '';
  544:     if (!$Apache::lonxml::default_homework_loaded) {
  545: 	&Apache::lonxml::default_homework_load($safeeval);
  546:     }
  547:     my $partid = $Apache::inputtags::part;
  548:     my $id = $Apache::inputtags::response[-1];
  549:     my $tag;
  550:     my $safehole = new Safe::Hole;
  551:     $safeeval->share_from('capa',['&caparesponse_capa_check_answer']);
  552: 
  553:     if (scalar(@$tagstack)) { $tag=$$tagstack[-1]; }
  554:     if ( $target eq 'grade' && &Apache::response::submitted() ) {
  555: 	&Apache::response::setup_params($tag,$safeeval);
  556: 	if ($Apache::lonhomework::type eq 'exam' && 
  557: 	    (($tag eq 'formularesponse') || ($tag eq 'mathresponse'))) {
  558: 	    $increment=&Apache::response::scored_response($partid,$id);
  559: 	} else {
  560: 	    my $response = &Apache::response::getresponse();
  561: 	    if ( $response =~ /[^\s]/) {
  562: 		my %previous = &Apache::response::check_for_previous($response,$partid,$id);
  563: 		&Apache::lonxml::debug("submitted a $response<br>\n");
  564: 		&Apache::lonxml::debug($$parstack[-1] . "\n<br>");
  565: 		
  566: 		if ( &Apache::response::submitted('scantron')) {
  567: 	    &add_in_tag_answer($parstack,$safeeval);
  568: 	    my ($values,$display)=&make_numerical_bubbles($partid,$id,
  569: 					  $target,$parstack,$safeeval);
  570: 	    $response=$values->[$response];
  571: 	}
  572: 	$Apache::lonhomework::results{"resource.$partid.$id.submission"}=$response;
  573: 		my ($ad,$msg,$name)=&check_submission($response,$partid,$id,
  574: 						      $tag,$parstack,
  575: 						      $safeeval);
  576: 
  577: 		&Apache::lonxml::debug('ad is'.$ad);
  578: 		if ($ad eq 'SIG_FAIL') {
  579: 		    my ($sig_u,$sig_l)=
  580: 			&get_sigrange($Apache::inputtags::params{'sig'});
  581: 		    $msg=join(':',$msg,$sig_l,$sig_u);
  582: 		    &Apache::lonxml::debug("sigs bad $sig_u $sig_l ".
  583: 					   $Apache::inputtags::params{'sig'});
  584: 		}
  585: 		&Apache::lonxml::debug("\n<br>result:$result:$Apache::lonxml::curdepth<br>\n");
  586:                 if (($ad eq 'INCORRECT' || $ad eq 'APPROX_ANS' ||
  587:                      $ad eq 'EXACT_ANS')) {
  588: 		    if ($Apache::lonhomework::type eq 'survey') {
  589: 		        $ad='SUBMITTED';
  590: 		    } elsif ($Apache::lonhomework::type eq 'surveycred') {
  591:                         $ad='SUBMITTED_CREDIT';
  592:                     } elsif ($Apache::lonhomework::type eq 'anonsurvey') {
  593:                         $ad='ANONYMOUS';
  594:                     } elsif ($Apache::lonhomework::type eq 'anonsurveycred') {
  595:                         $ad='ANONYMOUS_CREDIT';                     
  596:                     }
  597:                 }
  598: 		&Apache::response::handle_previous(\%previous,$ad);
  599: 		$Apache::lonhomework::results{"resource.$partid.$id.awarddetail"}=$ad;
  600: 		$Apache::lonhomework::results{"resource.$partid.$id.awardmsg"}=$msg;
  601: 		$Apache::lonhomework::results{"resource.$partid.$id.answername"}=$name;
  602: 		$result='';
  603: 	    }
  604: 	}
  605:     } elsif ($target eq 'web' || $target eq 'tex') {
  606: 	&check_for_answer_errors($parstack,$safeeval);
  607: 	my $award = $Apache::lonhomework::history{"resource.$Apache::inputtags::part.solved"};
  608: 	my $status = $Apache::inputtags::status['-1'];
  609: 	if ($Apache::lonhomework::type eq 'exam') {
  610: 	    # FIXME support multi dimensional numerical problems
  611:             #       in exam bubbles
  612: 	    my ($bubble_values,$bubble_display)=
  613: 		&make_numerical_bubbles($partid,$id,$target,$parstack,
  614: 					$safeeval);
  615: 	    my $number_of_bubbles = scalar(@{ $bubble_values });
  616: 	    my $unit=&Apache::lonxml::get_param_var('unit',$parstack,
  617: 						    $safeeval);
  618: 	    if ($target eq 'web') {
  619: 		if ($tag eq 'numericalresponse') {
  620: 		    if ($unit=~/\S/) {$result.=' (in '.$unit.')<br /><br />';}
  621: 		    $result.= '<table border="1"><tr>';
  622: 		    my $previous=$Apache::lonhomework::history{"resource.$Apache::inputtags::part.$id.submission"};
  623: 		    for (my $ind=0;$ind<$number_of_bubbles;$ind++) {
  624: 			my $checked='';
  625: 			if ($previous eq $bubble_values->[$ind]) {
  626: 			    $checked=" checked='on' ";
  627: 			}
  628: 			$result.='<td><input type="radio" name="HWVAL_'.$id.
  629: 			    '" value="'.$bubble_values->[$ind].'" '.$checked
  630: 			    .' /><b>'.$alphabet[$ind].'</b>: '.
  631: 			    $bubble_display->[$ind].'</td>';
  632: 		    }
  633: 		    $result.='</tr></table>';
  634: 		}
  635: 	    } elsif ($target eq 'tex') {
  636: 		if ((defined $unit) and ($unit=~/\S/) and ($Apache::lonhomework::type eq 'exam')) {
  637: 		    $result.=' \textit{(in} \verb|'.$unit.'|\textit{)} ';
  638: 		}
  639: 		if ($tag eq 'numericalresponse') {
  640: 		    $result .= &make_horizontal_latex_bubbles($bubble_values, $bubble_display,
  641: 			'$\bigcirc$');
  642: 		} else {
  643: 		    $increment = &Apache::response::repetition();
  644: 		}
  645: 	    }
  646: 	}
  647:         if (($target eq 'web') && ($tag eq 'formularesponse')
  648:             && ($Apache::lonhomework::type ne 'exam') && ($Apache::inputtags::status['-1'] eq 'CAN_ANSWER')
  649: 	    && (&Apache::lonnet::EXT('resource.'.$partid.'_'.$id.'.turnoffeditor') ne 'yes')) {
  650:             $result.=&Apache::response::edit_mathresponse_button($id,"HWVAL_$id");
  651:         }
  652:             
  653: 	&Apache::response::setup_prior_tries_hash(\&format_prior_response_numerical);
  654:     } elsif ($target eq 'edit') {
  655: 	$result.='</td></tr>'.&Apache::edit::end_table;
  656:     } elsif ($target eq 'answer' || $target eq 'analyze') {
  657: 	my $part_id="$partid.$id";
  658: 	if ($target eq 'analyze') {
  659: 	    push (@{ $Apache::lonhomework::analyze{"parts"} },$part_id);
  660: 	    $Apache::lonhomework::analyze{"$part_id.type"} = $tag;
  661: 	    my (@incorrect)=&Apache::lonxml::get_param_var('incorrect',$parstack,$safeeval);
  662: 	    if ($#incorrect eq 0) { @incorrect=(split(/,/,$incorrect[0])); }
  663: 	    push (@{ $Apache::lonhomework::analyze{"$part_id.incorrect"} }, @incorrect);
  664: 	    &Apache::response::check_if_computed($token,$parstack,
  665: 						 $safeeval,'answer');
  666: 	}
  667: 	if (scalar(@$tagstack)) {
  668: 	    &Apache::response::setup_params($tag,$safeeval);
  669: 	}
  670: 	&add_in_tag_answer($parstack,$safeeval);
  671: 	my (@formats)=&Apache::lonxml::get_param_var('format',$parstack,$safeeval);
  672: 
  673: 	my $unit=&Apache::lonxml::get_param_var('unit',$parstack,$safeeval);
  674: 	
  675: 	if ($target eq 'answer') {
  676: 	    $result.=&Apache::response::answer_header($tag,undef,
  677: 						      scalar(keys(%answer)));
  678: 	    if ($tag eq 'numericalresponse'
  679: 		&& $Apache::lonhomework::type eq 'exam') {
  680: 		my ($bubble_values,undef,$correct) = &make_numerical_bubbles($partid,
  681: 					     $id,$target,$parstack,$safeeval);
  682: 		$result.=&Apache::response::answer_part($tag,$correct);
  683: 	    }
  684: 	}
  685: 	foreach my $name (sort(keys(%answer))) {
  686: 	    my @answers = @{ $answer{$name}{'answers'} };
  687: 	    if ($target eq 'analyze') {
  688: 		foreach my $info ('answer','ans_high','ans_low','format') {
  689: 		    $Apache::lonhomework::analyze{"$part_id.$info"}{$name}=[];
  690: 		}
  691: 	    }
  692: 	    my ($sigline,$tolline);
  693: 	    if ($name ne $tag_internal_answer_name 
  694: 		|| scalar(keys(%answer)) > 1) {
  695: 		$result.=&Apache::response::answer_part($tag,$name);
  696: 	    }
  697: 	    for(my $i=0;$i<=$#answers;$i++) {
  698: 		my $ans=$answers[$i];
  699: 		my $fmt=$formats[0];
  700: 		if (@formats && $#formats) {$fmt=$formats[$i];}
  701: 		my ($sighigh,$siglow);
  702: 		if ($Apache::inputtags::params{'sig'}) {
  703: 		    ($sighigh,$siglow)=&get_sigrange($Apache::inputtags::params{'sig'});
  704: 		}
  705: 		my @vector;
  706: 		if (ref($ans)) {
  707: 		    @vector = @{ $ans };
  708: 		} else {
  709: 		    @vector = ($ans);
  710: 		}
  711: 		my @all_answer_info;
  712: 		foreach my $element (@vector) {
  713: 		    my ($high,$low);
  714: 		    if ($Apache::inputtags::params{'tol'}) {
  715: 			($high,$low)=&get_tolrange($element,$Apache::inputtags::params{'tol'});
  716: 		    }
  717: 		    if ($target eq 'answer') {
  718: 			if ($fmt && $tag eq 'numericalresponse') {
  719: 			    $fmt=~s/e/E/g;
  720: 			    if ($unit=~/\$/) { $fmt="\$".$fmt; $unit=~s/\$//g; }
  721: 			    if ($unit=~/\,/) { $fmt="\,".$fmt; $unit=~s/\,//g; }
  722: 			    $element = &format_number($element,$fmt,$target,$safeeval);
  723: 			    #if ($high) {
  724: 			    #    $high=&format_number($high,$fmt,$target,$safeeval);
  725: 			    #    $low =&format_number($low,$fmt,$target,$safeeval);
  726: 			    #}
  727: 			}
  728: 			if ($high && $tag eq 'numericalresponse') {
  729: 			    $element.='; ['.$low.'; '.$high.']';
  730: 			    $tolline .= "[$low, $high]";
  731: 			}
  732: 			if (defined($sighigh) && $tag eq 'numericalresponse') {
  733: 			    if ($env{'form.answer_output_mode'} eq 'tex') {
  734: 				$element.= "; Sig $siglow - $sighigh";
  735: 			    } else {
  736: 				$element.= " Sig <i>$siglow - $sighigh</i>";
  737: 				$sigline .= "[$siglow, $sighigh]";
  738: 			    }
  739: 			}
  740: 			push(@all_answer_info,$element);
  741: 			
  742: 		    } elsif ($target eq 'analyze') {
  743: 			push (@{ $Apache::lonhomework::analyze{"$part_id.answer"}{$name}[$i] }, $element);
  744: 			if ($high) {
  745: 			    push (@{ $Apache::lonhomework::analyze{"$part_id.ans_high"}{$name}[$i] }, $high);
  746: 			    push (@{ $Apache::lonhomework::analyze{"$part_id.ans_low"}{$name}[$i] }, $low);
  747: 			}
  748: 			if ($fmt) {
  749: 			    push (@{ $Apache::lonhomework::analyze{"$part_id.format"}{$name}[$i] }, $fmt);
  750: 			}
  751: 		    }
  752: 		}
  753: 		if ($target eq 'answer') {
  754: 		    $result.= &Apache::response::answer_part($tag,join('; ',@all_answer_info));
  755: 		}
  756: 	    }
  757: 
  758: 	    my @fmt_ans;
  759: 	    for(my $i=0;$i<=$#answers;$i++) {
  760: 		my $ans=$answers[$i];
  761: 		my $fmt=$formats[0];
  762: 		if (@formats && $#formats) {$fmt=$formats[$i];}
  763: 		foreach my $element (@$ans) {		    
  764: 		    if ($fmt && $tag eq 'numericalresponse') {
  765: 			$fmt=~s/e/E/g;
  766: 			if ($unit=~/\$/) { $fmt="\$".$fmt; $unit=~s/\$//g; }
  767: 			if ($unit=~/\,/) { $fmt="\,".$fmt; $unit=~s/\,//g; }
  768: 			$element = &format_number($element,$fmt,$target,
  769: 						  $safeeval);
  770: 			if ($fmt=~/\$/ && $unit!~/\$/) { $element=~s/\$//; }
  771: 		    }
  772: 		}
  773: 		push(@fmt_ans,join(',',@$ans));
  774: 	    }
  775: 	    my $response=\@fmt_ans;
  776: 
  777: 	    my $hideunit=&Apache::lonnet::EXT('resource.'.$partid.'_'.
  778: 					      $id.'.turnoffunit');
  779: 	    if ($unit ne ''  && 
  780: 		! ($Apache::lonhomework::type eq 'exam' ||
  781: 		   lc($hideunit) eq "yes") )  {
  782: 		my $cleanunit=$unit;
  783: 		$cleanunit=~s/\$\,//g;
  784: 		foreach my $ans (@fmt_ans) {
  785: 		    $ans.=" $cleanunit";
  786: 		}
  787: 	    }
  788: 	    #
  789: 	    # The computer's response must be cast in a form that 
  790: 	    # ensures it has sufficient significant figures.
  791: 	    # the only way to do this is to sprintf it into scientific notation
  792: 	    # due to the ambiguity of trailing zeros.
  793: 	    # See Bug 6577
  794: 	    #
  795: 	    my ($sigMax, $sigmin) = &get_sigrange($Apache::inputtags::params{'sig'});
  796: 	    my $sigfigs = $sigMax-1;
  797: 
  798: 	    for (my $i = 0; $i < scalar @$response; $i++) {
  799: 		$response->[$i] = sprintf('%.' . $sigfigs . 'e', $response->[$i]);
  800: 	    }
  801: 	    my ($ad,$msg)=&check_submission($response,$partid,$id,$tag,
  802: 					    $parstack,$safeeval);
  803: 	    if ($ad ne 'EXACT_ANS' && $ad ne 'APPROX_ANS') {
  804: 		my $error;
  805: 		if ($tag eq 'formularesponse') {
  806: 		    $error=&mt("Computer's answer is incorrect ([_1]).",'"'.join(', ',@$response).'"');
  807: 		} else {
  808: 		    # answer failed check if it is sig figs that is failing
  809: 		    my ($ad,$msg)=&check_submission($response,$partid,$id,
  810: 						    $tag,$parstack,
  811: 						    $safeeval,1);
  812: 		    $error=&mt("Computer's answer is incorrect ([_1]).",'"'.join(', ',@$response).'"').' ';
  813: 		    if ($sigline ne '') {
  814: 			$error.=&mt('It is likely that the tolerance range [_1] or significant figures [_2] need to be adjusted.',$tolline,$sigline);
  815: 		    } else {
  816: 			$error.=&mt('It is likely that the tolerance range [_1] needs to be adjusted.',$tolline);
  817: 		    }
  818: 		}
  819: 		if ($ad ne 'EXACT_ANS' && $ad ne 'APPROX_ANS') {
  820: 		    &Apache::lonxml::error($error);
  821: 		} else {
  822: 		    &Apache::lonxml::warning($error);
  823: 		}
  824: 	    }
  825: 
  826: 	    if (defined($unit) and ($unit ne '') and
  827: 		$tag eq 'numericalresponse') {
  828: 		if ($target eq 'answer') {
  829: 		    if ($env{'form.answer_output_mode'} eq 'tex') {
  830: 			$result.=&Apache::response::answer_part($tag,
  831: 								" Unit: $unit ");
  832: 		    } else {
  833: 			$result.=&Apache::response::answer_part($tag,
  834: 								"Unit: <b>$unit</b>");
  835: 		    }
  836: 		} elsif ($target eq 'analyze') {
  837: 		    push (@{ $Apache::lonhomework::analyze{"$part_id.unit"} }, $unit);
  838: 		}
  839: 	    }
  840: 	    if ($tag eq 'formularesponse' && $target eq 'answer') {
  841: 		my $samples=&Apache::lonxml::get_param('samples',$parstack,$safeeval);
  842: 		$result.=&Apache::response::answer_part($tag,$samples);
  843: 	    }
  844: 	    $result.=&Apache::response::next_answer($tag,$name);
  845: 	}
  846: 	if ($target eq 'answer') {
  847: 	    $result.=&Apache::response::answer_footer($tag);
  848: 	}
  849:     }
  850:     if ($target eq 'grade' || $target eq 'web' || $target eq 'answer' || 
  851: 	$target eq 'tex' || $target eq 'analyze') {
  852:         if (($tag eq 'formularesponse') && ($target eq 'analyze')) {
  853:             my $type = &Apache::lonnet::EXT('resource.'.$partid.'_'.$id.'.type');
  854:             if ($type eq 'exam') {
  855:                 $increment = &Apache::response::repetition();
  856:             }
  857:         }
  858: 	&Apache::lonxml::increment_counter($increment,"$partid.$id");
  859: 	if ($target eq 'analyze') {
  860: 	    &Apache::lonhomework::set_bubble_lines();
  861: 	}
  862:     }
  863:     &Apache::response::end_response();
  864:     return $result;
  865: }
  866: 
  867: sub format_prior_response_numerical {
  868:     my ($mode,$answer) = @_;
  869:     if (ref($answer)) {
  870: 	my $result = '<table class="LC_prior_numerical"><tr>';
  871: 	foreach my $element (@{ $answer }) {
  872: 	    $result.= '<td><span class="LC_prior_numerical">'.
  873: 		&HTML::Entities::encode($element,'"<>&').'</span></td>';
  874: 	}
  875: 	$result.='</tr></table>';
  876: 	return $result;
  877:     }
  878:     return '<span class="LC_prior_numerical">'.
  879: 	    &HTML::Entities::encode($answer,'"<>&').'</span>';
  880: 
  881: }
  882: 
  883: sub check_for_answer_errors {
  884:     my ($parstack,$safeeval) = @_;
  885:     &add_in_tag_answer($parstack,$safeeval);
  886:     my %counts;
  887:     foreach my $name (keys(%answer)) {
  888: 	push(@{$counts{scalar(@{$answer{$name}{'answers'}})}},$name);
  889:     }
  890:     if (scalar(keys(%counts)) > 1) {
  891: 	my $counts = join(' ',map {
  892: 	    my $count = $_;
  893: 	    &mt("Answers [_1] had [_2] components.",
  894: 		'<tt>'.join(', ',@{$counts{$count}}).'</tt>',
  895: 		$count);
  896: 	} (sort(keys(%counts))));
  897: 	&Apache::lonxml::error(&mt("All answers must have the same number of components. Varying numbers of answers were seen. ").$counts);
  898:     }
  899:     my $expected_number_of_inputs = (keys(%counts))[0];
  900:     if ( $expected_number_of_inputs > 0 
  901: 	 && $expected_number_of_inputs != scalar(@Apache::inputtags::inputlist)) {
  902: 	&Apache::lonxml::error(&mt("Expected [_1] input fields, but there were only [_2] seen.", 
  903: 				   $expected_number_of_inputs,
  904: 				   scalar(@Apache::inputtags::inputlist)));
  905:     }
  906: }
  907: 
  908: sub get_table_sizes {
  909:     my ($number_of_bubbles,$rbubble_values)=@_;
  910:     my $scale=2; #mm for one digit
  911:     my $cell_width=0;
  912:     foreach my $member (@$rbubble_values) {
  913: 	my $cell_width_real=0;
  914: 	if ($member=~/(\+|-)?(\d*)\.?(\d*)\s*\$?\\times\s*10\^{(\+|-)?(\d+)}\$?/) {
  915: 	    $cell_width_real=(length($2)+length($3)+length($5)+7)*$scale;
  916: 	} elsif ($member=~/(\d*)\.?(\d*)(E|e)(\+|-)?(\d*)/) {
  917: 	    $cell_width_real=(length($1)+length($2)+length($5)+9)*$scale;
  918:         } elsif ($member=~/(\d*)\.(\d*)/) {
  919: 	    $cell_width_real=(length($1)+length($2)+3)*$scale;
  920: 	} else {
  921: 	    $cell_width_real=(length($member)+1)*$scale*0.9;
  922: 	}
  923: 	if ($cell_width_real>$cell_width) {$cell_width=$cell_width_real;}
  924:     }
  925:     $cell_width+=8; 
  926:     my $textwidth;
  927:     if ($env{'form.textwidth'} ne '') {
  928: 	$env{'form.textwidth'}=~/(\d*)\.?(\d*)/;
  929: 	$textwidth=$1.'.'.$2;
  930:     } else {
  931: 	$env{'form.textwidth'}=~/(\d+)\.?(\d*)/;
  932: 	$textwidth=$1.'.'.$2;
  933:     }
  934:     my $bubbles_per_line=int($textwidth/$cell_width);
  935:     if ($bubbles_per_line > $number_of_bubbles) {
  936: 	$bubbles_per_line=$number_of_bubbles;
  937:     } elsif (($bubbles_per_line > $number_of_bubbles/2) 
  938: 	     && ($number_of_bubbles % 2==0)) {
  939: 	$bubbles_per_line=$number_of_bubbles/2;
  940:     }
  941:     if ($bubbles_per_line < 1) {
  942: 	$bubbles_per_line=1;
  943:     }
  944:     my $number_of_tables = int($number_of_bubbles/$bubbles_per_line);
  945:     my @table_range = ();
  946:     for (my $i=0;$i<$number_of_tables;$i++) {push @table_range,$bubbles_per_line;}
  947:     if ($number_of_bubbles % $bubbles_per_line) {
  948: 	$number_of_tables++;
  949: 	push @table_range,($number_of_bubbles % $bubbles_per_line);
  950:     }
  951:     $cell_width-=8;
  952:     $cell_width=$cell_width*3/4;
  953:     return ($cell_width,$number_of_tables,@table_range);
  954: }
  955: 
  956: sub format_number {
  957:     my ($number,$format,$target,$safeeval)=@_;
  958:     my $ans;
  959:     if ($format eq '') {
  960: 	#What is the number? (integer,decimal,floating point)
  961: 	if ($number=~/^(\d*\.?\d*)(E|e)[+\-]?(\d*)$/) {
  962: 	    $format = '3e';
  963: 	} elsif ($number=~/^(\d*)\.(\d*)$/) {
  964: 	    $format = '4f';
  965: 	} elsif ($number=~/^(\d*)$/) {
  966: 	    $format = 'd';
  967: 	}
  968:     }
  969:     if (!$Apache::lonxml::default_homework_loaded) {
  970: 	&Apache::lonxml::default_homework_load($safeeval);
  971:     }
  972:     $ans=&Apache::run::run("&prettyprint(q\0$number\0,q\0$format\0,q\0$target\0)",$safeeval);
  973:     return $ans;
  974: }
  975: 
  976: sub make_numerical_bubbles {
  977:     my ($part,$id,$target,$parstack,$safeeval) =@_;
  978: 
  979:     if (!%answer) {
  980: 	&Apache::lonxml::error(&mt("No answers defined for response [_1] in part [_2] to make bubbles for.",$id,$part));
  981: 	return ([],[],undef);
  982:     }
  983:     
  984:     my $number_of_bubbles = 
  985: 	&Apache::response::get_response_param($part.'_'.$id,'numbubbles',8);
  986: 
  987:     #
  988:     # Fixes for BZ 6519 - number of bubbles <= 0 or non-integer.
  989:     # 
  990:     $number_of_bubbles = int($number_of_bubbles + 0.5);
  991:     if ($number_of_bubbles <= 0) {
  992: 	$number_of_bubbles = 8;
  993:     }
  994:     
  995: 
  996:     my ($format)=&Apache::lonxml::get_param_var('format',$parstack,$safeeval);
  997:     my $name = (exists($answer{$tag_internal_answer_name}) 
  998: 		? $tag_internal_answer_name
  999: 		: (sort(keys(%answer)))[0]);
 1000: 
 1001:     if ( scalar(@{$answer{$name}{'answers'}}) > 1) {
 1002: 	&Apache::lonxml::error("Only answers with 1 component are supported in exam mode");
 1003:     }
 1004:     if (scalar(@{$answer{$name}{'answers'}[0]}) > 1) {
 1005: 	&Apache::lonxml::error("Vector answers are unsupported in exam mode.");
 1006:     }
 1007: 
 1008:     my $answer = $answer{$name}{'answers'}[0][0];
 1009:     my (@incorrect)=&Apache::lonxml::get_param_var('incorrect',$parstack,
 1010: 						   $safeeval);
 1011:     if ($#incorrect eq 0) { @incorrect=(split(/,/,$incorrect[0])); }
 1012:     
 1013:     my @bubble_values=();
 1014:     my @alphabet=('A'..'Z');
 1015: 
 1016:     &Apache::lonxml::debug("answer is $answer incorrect is @incorrect");
 1017:     my @oldseed=&Math::Random::random_get_seed();
 1018:     if (@incorrect) {
 1019: 	&Apache::lonxml::debug("inside ".(scalar(@incorrect)+1 gt $number_of_bubbles));
 1020: 	if (defined($incorrect[0]) &&
 1021: 	    scalar(@incorrect)+1 >= $number_of_bubbles) {
 1022: 	    &Apache::lonxml::debug("inside ".(scalar(@incorrect)+1).":$number_of_bubbles");
 1023: 	    &Apache::response::setrandomnumber();
 1024: 	    my @rand_inc=&Math::Random::random_permutation(@incorrect);
 1025: 	    @bubble_values=@rand_inc[0..($number_of_bubbles-2)];
 1026: 	    @bubble_values=sort {$a <=> $b} (@bubble_values,$answer);
 1027: 	    &Apache::lonxml::debug("Answer was :$answer: returning :".$#bubble_values.": which are :".join(':',@bubble_values));
 1028: 	    &Math::Random::random_set_seed(@oldseed);
 1029: 
 1030: 	    my $correct;
 1031: 	    for(my $i=0; $i<=$#bubble_values;$i++) {
 1032: 		if ($bubble_values[$i] eq $answer) {
 1033: 		    $correct = $alphabet[$i];
 1034: 		    last;
 1035: 		}
 1036: 	    }
 1037: 
 1038: 	    if (defined($format) && $format ne '') {
 1039: 		my @bubble_display;
 1040: 		foreach my $value (@bubble_values) {
 1041: 		    push(@bubble_display,
 1042: 			 &format_number($value,$format,$target,$safeeval));
 1043: 		}
 1044: 		return (\@bubble_values,\@bubble_display,$correct);
 1045: 	    } else {
 1046: 		return (\@bubble_values,\@bubble_values,$correct);
 1047: 	    }
 1048: 	}
 1049: 	if (defined($incorrect[0]) &&
 1050: 	    scalar(@incorrect)+1 < $number_of_bubbles) {
 1051: 	    &Apache::lonxml::warning("Not enough incorrect answers were specified in the incorrect array, ignoring the specified incorrect answers and instead generating them (".join(',',@incorrect).").");
 1052: 	}
 1053:     }
 1054:     my @factors = (1.13,1.17,1.25,1.33,1.45); #default values of factors
 1055:     my @powers = (1..$number_of_bubbles);
 1056:     &Apache::response::setrandomnumber();
 1057:     my $ind=&Math::Random::random_uniform_integer(1,0,$#powers);
 1058:     my $power = $powers[$ind];
 1059:     $ind=&Math::Random::random_uniform_integer(1,0,$#factors);
 1060:     my $factor = $factors[$ind];
 1061:     my @bubble_display;
 1062:     my $answerfactor=$answer;
 1063:     if ($answer==0) { 
 1064:        $answerfactor=&Math::Random::random_uniform_integer(1,1,100)/
 1065:                      &Math::Random::random_uniform_integer(1,1,10);
 1066:     }
 1067:     for ($ind=0;$ind<$number_of_bubbles;$ind++) {
 1068: 	$bubble_values[$ind] = $answerfactor*($factor**($power-$powers[$#powers-$ind]));
 1069: 	$bubble_display[$ind] = &format_number($bubble_values[$ind],
 1070: 					       $format,$target,$safeeval);
 1071:     }
 1072:     my $correct = $alphabet[$number_of_bubbles-$power];
 1073:     if ($answer==0) {
 1074:        $correct='A';
 1075:        $bubble_values[0]=0;
 1076:        $bubble_display[0] = &format_number($bubble_values[0],
 1077:                                            $format,$target,$safeeval);
 1078:     }
 1079:     &Math::Random::random_set_seed(@oldseed);
 1080:     return (\@bubble_values,\@bubble_display,$correct);
 1081: }
 1082: 
 1083: ##
 1084: # Produce LaTeX bubbles laid out horizontally given a set of bubble values:
 1085: #
 1086: # @param bubble_values  - reference to an array of bubble 'values'
 1087: # @param bubble_display - reference to the array of texts to display to the user
 1088: #                         for each bubble_value (this is mostly for numerical response
 1089: #                         when the displayed value may not be an exact
 1090: #                         representation of the bubble value. 
 1091: # @param bubble_fragment- The LaTeX fragment that will be plugged in to make
 1092: #                         the bubble itself. Note that the code will autonomously
 1093: #                         label each bubble with a lable...and that it's perfectly
 1094: #                         acceptable to use "" for the bubble_fragment.
 1095: # 
 1096: # @return string - the LaTeX fragment that produces the bubbles.
 1097: #
 1098: sub make_horizontal_latex_bubbles {
 1099:     my ($bubble_values, $bubble_display, $bubble_fragment)     = @_;
 1100:     my $result;
 1101: 
 1102:     my $number_of_bubbles = scalar(@{$bubble_values}); 
 1103: 
 1104:     # Get the number of rows and columns in each row of the bubble
 1105:     # table:
 1106: 
 1107:     my ($celllength, $number_of_tables, @table_range) =
 1108: 	&get_table_sizes($number_of_bubbles, $bubble_display);
 1109: 
 1110:     my $j=0;
 1111:     my $cou=0;
 1112:     $result.='\vskip 2mm \noindent ';
 1113:     $result .= '\textbf{'.$Apache::lonxml::counter.'.} \vskip -3mm ';
 1114: 
 1115:     for (my $i=0;$i<$number_of_tables;$i++) {
 1116: 	if ($i == 0) {
 1117: 	    $result .= '\vskip -1mm ';
 1118: 	} else {
 1119: 	    $result .= '\vskip 1mm ';
 1120: 	}
 1121: 	$result.='\noindent \setlength{\tabcolsep}{2 mm}\hskip 2pc\begin{tabular}{';
 1122: 	for (my $ind=0;$ind<$table_range[$j];$ind++) {
 1123: 	    $result.='p{4 mm}p{'.$celllength.' mm}';
 1124: 	}
 1125: 	$result.='}';
 1126: 	for (my $ind=$cou;$ind<$cou+$table_range[$j];$ind++) {
 1127: 	    $result.='\hskip -4 mm {\small \textbf{ '.$alphabet[$ind].'}}'
 1128: 		. $bubble_fragment 
 1129: 		. '& \hskip -3 mm {\small '.$bubble_display->[$ind].'} ';
 1130: 	    if ($ind != $cou+$table_range[$j]-1) {
 1131: 		$result.=' & ';
 1132: 	    }
 1133: 	}
 1134: 	$cou += $table_range[$j];
 1135: 	$j++;
 1136: 	$result.='\\\\\end{tabular}\vskip 0 mm ';
 1137:     }
 1138:     return $result;
 1139: }
 1140: 
 1141: sub get_tolrange {
 1142:     my ($ans,$tol)=@_;
 1143:     my ($high,$low);
 1144:     if ($tol =~ /%$/) {
 1145: 	chop($tol);
 1146: 	my $change=$ans*($tol/100.0);
 1147: 	$high=$ans+$change;
 1148: 	$low=$ans-$change;
 1149:     } else {
 1150: 	$high=$ans+$tol;
 1151: 	$low=$ans-$tol;
 1152:     }
 1153:     return ($high,$low);
 1154: }
 1155: 
 1156: sub get_sigrange {
 1157:     my ($sig)=@_;
 1158:     #&Apache::lonxml::debug("Got a sig of :$sig:");
 1159:     my $courseid=$env{'request.course.id'};
 1160:     if ($env{'request.state'} ne 'construct'
 1161: 	&& lc($env{"course.$courseid.disablesigfigs"}) eq 'yes') {
 1162: 	return (15,0);
 1163:     }
 1164:     my $sig_lbound;
 1165:     my $sig_ubound;
 1166:     if ($sig eq '') {
 1167: 	$sig_lbound = 0; #SIG_LB_DEFAULT
 1168: 	$sig_ubound =15; #SIG_UB_DEFAULT
 1169:     } else {
 1170: 	($sig_lbound,$sig_ubound) = split(/,/,$sig);
 1171: 	if (!defined($sig_lbound)) {
 1172: 	    $sig_lbound = 0; #SIG_LB_DEFAULT
 1173: 	    $sig_ubound =15; #SIG_UB_DEFAULT
 1174: 	}
 1175: 	if (!defined($sig_ubound)) { $sig_ubound=$sig_lbound; }
 1176:     }
 1177:     if (($sig_ubound<$sig_lbound) ||
 1178: 	($sig_lbound > 15) ||
 1179: 	($sig =~/(\+|-)/ ) ) {
 1180: 	my $errormsg=&mt("Invalid Significant figures detected")." ($sig)";
 1181: 	if ($env{'request.state'} eq 'construct') {
 1182: 	    $errormsg.=
 1183: 		&Apache::loncommon::help_open_topic('Significant_Figures');
 1184: 	}
 1185: 	&Apache::lonxml::error($errormsg);
 1186:     }
 1187:     return ($sig_ubound,$sig_lbound);
 1188: }
 1189: 
 1190: sub format_prior_response_string {
 1191:     my ($mode,$answer) =@_;
 1192:     return '<span class="LC_prior_string">'.
 1193: 	    &HTML::Entities::encode($answer,'"<>&').'</span>';
 1194: }
 1195: 
 1196: sub start_stringresponse {
 1197:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
 1198:     my $result;
 1199:     my $id = &Apache::response::start_response($parstack,$safeeval);
 1200:     undef(%answer);
 1201:     if ($target eq 'meta') {
 1202: 	$result=&Apache::response::meta_package_write('stringresponse');
 1203:     } elsif ($target eq 'edit') {
 1204: 	$result.=&Apache::edit::tag_start($target,$token);
 1205: 	$result.=&Apache::edit::text_arg('Answer:','answer',$token);
 1206: 	$result.=&Apache::edit::select_arg('Type:','type',
 1207: 			 [['cs','Case Sensitive'],['ci','Case Insensitive'],
 1208: 			  ['mc','Case Insensitive, Any Order'],
 1209: 			  ['re','Regular Expression']],$token);
 1210: 	$result.=&Apache::edit::text_arg('String to display for answer:',
 1211: 					 'answerdisplay',$token);
 1212:         $result.=&Apache::edit::text_arg('Pre-Processor Subroutine:','preprocess',
 1213:                                              $token,10);
 1214: 	$result.=&Apache::edit::end_row().&Apache::edit::start_spanning_row();
 1215:     } elsif ($target eq 'modified') {
 1216: 	my $constructtag;
 1217: 	$constructtag=&Apache::edit::get_new_args($token,$parstack,
 1218: 						  $safeeval,'answer',
 1219: 						  'type','answerdisplay','preprocess');
 1220: 	if ($constructtag) {
 1221: 	    $result = &Apache::edit::rebuild_tag($token);
 1222: 	    $result.=&Apache::edit::handle_insert();
 1223: 	}
 1224:     } elsif ($target eq 'web') {
 1225: 	if (  &Apache::response::show_answer() ) {
 1226: 	    my $answer=
 1227: 	       &Apache::lonxml::get_param('answerdisplay',$parstack,$safeeval);
 1228: 	    if (!defined $answer || $answer eq '') {
 1229: 		$answer=
 1230: 		    &Apache::lonxml::get_param('answer',$parstack,$safeeval);
 1231: 	    }
 1232: 	    $Apache::inputtags::answertxt{$id}=[$answer];
 1233: 	} 
 1234:     } elsif ($target eq 'answer' || $target eq 'grade') {
 1235: 	&Apache::response::reset_params();
 1236:     }
 1237:     return $result;
 1238: }
 1239: 
 1240: sub end_stringresponse {
 1241:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
 1242: 
 1243:     my $result = '';
 1244:     my $part=$Apache::inputtags::part;
 1245:     my $id=$Apache::inputtags::response[-1];
 1246:     my $answer=&Apache::lonxml::get_param('answer',$parstack,$safeeval);
 1247:     my $type=&Apache::lonxml::get_param('type',$parstack,$safeeval);
 1248:     my $answerdisplay=&Apache::lonxml::get_param('answerdisplay',$parstack,$safeeval);
 1249:     &Apache::lonxml::debug("current $answer ".$token->[2]);
 1250:     if (!$Apache::lonxml::default_homework_loaded) {
 1251: 	&Apache::lonxml::default_homework_load($safeeval);
 1252:     }
 1253:     if ( $target eq 'grade' && &Apache::response::submitted() ) {
 1254: 	&Apache::response::setup_params('stringresponse',$safeeval);
 1255: 	$safeeval->share_from('capa',['&caparesponse_capa_check_answer']);
 1256: 	if ($Apache::lonhomework::type eq 'exam' ||
 1257: 	    &Apache::response::submitted('scantron')) {
 1258: 	    &Apache::response::scored_response($part,$id);
 1259: 
 1260: 	} else {
 1261: 	    my $response = &Apache::response::getresponse();
 1262: 	    if ( $response =~ /[^\s]/) {
 1263: 		my %previous = &Apache::response::check_for_previous($response,
 1264: 								    $part,$id,
 1265:                                                                     undef,$type);
 1266: 		&Apache::lonxml::debug("submitted a $response<br>\n");
 1267: 		&Apache::lonxml::debug($$parstack[-1] . "\n<br>");
 1268: 		$Apache::lonhomework::results{"resource.$part.$id.submission"}=
 1269: 		    $response;
 1270: 		my ($ad,$msg);
 1271: 		if ($type eq 're' ) { 
 1272: 		    # if the RE wasn't in a var it likely got munged,
 1273:                     # thus grab it from the var directly
 1274: #		    my $testans=$token->[2]->{'answer'};
 1275: #		    if ($testans !~ m/^\s*\$/) {
 1276: #			$answer=$token->[2]->{'answer'};
 1277: #		    }
 1278: 		    ${$safeeval->varglob('LONCAPA::response')}=$response;
 1279:                     my $preprocess=&Apache::lonxml::get_param('preprocess',$parstack,$safeeval);
 1280:                     $preprocess=~s/^\&//;
 1281:                     if (defined($preprocess)) {
 1282:                         &Apache::run::run('$LONCAPA::response=&'.$preprocess.'($LONCAPA::response);',$safeeval);
 1283:                     }
 1284: 		    $result = &Apache::run::run('if ($LONCAPA::response=~m'.$answer.') { return 1; } else { return 0; }',$safeeval);
 1285: 		    &Apache::lonxml::debug("current $response");
 1286: 		    &Apache::lonxml::debug("current $answer");
 1287: 		    $ad = ($result) ? 'APPROX_ANS' : 'INCORRECT';
 1288: 		} else {
 1289: 		    my @args = ('type','preprocess');
 1290: 		    my $args_ref = &setup_capa_args($safeeval,$parstack,
 1291: 						    \@args,$response);
 1292:                     if ($$args_ref{'type'} eq '') {
 1293:                         $$args_ref{'type'} = 'ci';
 1294:                     }
 1295: 		    &add_in_tag_answer($parstack,$safeeval);
 1296: 		    my (@final_awards,@final_msgs,@names,%ansstring);
 1297: 		    foreach my $name (keys(%answer)) {
 1298: 			&Apache::lonxml::debug(" doing $name with ".join(':',@{ $answer{$name}{'answers'} }));
 1299: 			${$safeeval->varglob('LONCAPA::CAPAresponse_answer')}=dclone($answer{$name});
 1300: 			my ($result, @msgs)=&Apache::run::run("&caparesponse_check_list()",$safeeval);
 1301:                         if ($$args_ref{'type'} =~ /^c[si]$/) {
 1302:                             $ansstring{$name} = pop(@msgs);
 1303:                             my $control_chars_removed = pop(@msgs);
 1304:                             my $error = pop(@msgs);
 1305:                             if (($error ne '') || 
 1306:                                 ($control_chars_removed)) {
 1307:                                 my ($symb,$courseid,$sdomain,$sname) =
 1308:                                     &Apache::lonnet::whichuser();
 1309:                                 if ($control_chars_removed) {
 1310:                                     my $showresponse = $response;
 1311:                                     if ($response =~ /[\000-\037]/) {
 1312:                                         $response =~ s/[\000-\037]//g;
 1313:                                     }
 1314:                                     if ($showresponse  =~ /[\r\n\f]/) {
 1315:                                         my @lines = split(/[\r\n\f]+/,$showresponse);
 1316:                                         $showresponse = join('\\n',@lines);
 1317:                                     }
 1318:                                     &Apache::lonnet::logthis("Stringresponse grading: control characters stripped from submission ".$showresponse." for $sname:$sdomain in $courseid for part: $part response: $id and symb: $symb");
 1319:                                     $Apache::lonhomework::results{"resource.$part.$id.submission"} = $response;
 1320:                                 }
 1321:                                 if ($error ne '') {
 1322:                                     &Apache::lonnet::logthis("Stringresponse grading error: $error for $sname:$sdomain in $courseid for part: $part response: $id and symb: $symb");
 1323:                                 }
 1324:                             }
 1325:                         }
 1326: 			&Apache::lonxml::debug('msgs are'.join(':',@msgs));
 1327: 			my ($awards)=split(/:/,$result);
 1328: 			my (@awards) = split(/,/,$awards);
 1329: 			($ad,$msg) = 
 1330: 			    &Apache::inputtags::finalizeawards(\@awards,\@msgs);
 1331: 			push(@final_awards,$ad);
 1332: 			push(@final_msgs,$msg);
 1333: 			push(@names,$name);
 1334: 			&Apache::lonxml::debug("\n<br>result:$result:$Apache::lonxml::curdepth<br>\n");
 1335: 		    }
 1336: 		    ($ad, $msg, my $name) = 
 1337: 			&Apache::inputtags::finalizeawards(\@final_awards,
 1338: 							   \@final_msgs,
 1339: 							   \@names,1);
 1340:                     if (keys(%ansstring) > 0) {
 1341:                         $Apache::lonhomework::results{"resource.$part.$id.answerstring"} = &Apache::lonnet::hash2str(%ansstring);
 1342:                     }
 1343: 		}
 1344:                 if (($ad eq 'INCORRECT' || $ad eq 'APPROX_ANS' ||
 1345:                      $ad eq 'EXACT_ANS')) {
 1346: 		    if ($Apache::lonhomework::type eq 'survey') {
 1347: 		        $ad='SUBMITTED';
 1348: 		    } elsif ($Apache::lonhomework::type eq 'surveycred') {
 1349:                         $ad='SUBMITTED_CREDIT';
 1350:                     } elsif ($Apache::lonhomework::type eq 'anonsurvey') {
 1351:                         $ad='ANONYMOUS';
 1352:                     } elsif ($Apache::lonhomework::type eq 'anonsurveycred') {
 1353:                         $ad='ANONYMOUS_CREDIT';
 1354:                     }
 1355:                 }
 1356:                 unless (($env{'request.state'} eq 'construct') || 
 1357:                         ($Apache::lonhomework::type eq 'randomizetry')) {
 1358:                     if (($ad eq 'INCORRECT' || $ad eq 'APPROX_ANS' || $ad eq 'EXACT_ANS')) {
 1359:                         if ($previous{'used'}) {
 1360:                             if ($ad ne $previous{'award'}) {
 1361:                                 if (($previous{'award'} eq 'INCORRECT' || 
 1362:                                      $previous{'award'} eq 'APPROX_ANS' ||
 1363:                                      $previous{'award'} eq 'EXACT_ANS')) {
 1364:                                     &stringresponse_gradechange($part,$id,\%previous,
 1365:                                                                 'cs',$response,$ad,$type);
 1366:                                 }
 1367:                             }
 1368:                         } elsif ($previous{'usedci'}) {
 1369:                             if ($ad ne $previous{'awardci'}) {
 1370:                                 if (($previous{'awardci'} eq 'INCORRECT' || 
 1371:                                      $previous{'awardci'} eq 'APPROX_ANS' ||
 1372:                                      $previous{'awardci'} eq 'EXACT_ANS')) {
 1373:                                     &stringresponse_gradechange($part,$id,\%previous,
 1374:                                                                 'ci',$response,$ad,$type);
 1375:                                 }
 1376:                             }
 1377:                         }
 1378:                     }
 1379:                 }
 1380: 		&Apache::response::handle_previous(\%previous,$ad);
 1381: 		$Apache::lonhomework::results{"resource.$part.$id.awarddetail"}=$ad;
 1382: 		$Apache::lonhomework::results{"resource.$part.$id.awardmsg"}=$msg;
 1383: 	    }
 1384: 	}
 1385:     } elsif ($target eq 'answer' || $target eq 'analyze') {
 1386: 	&add_in_tag_answer($parstack,$safeeval);
 1387: 	if ($target eq 'analyze') {
 1388: 	    push (@{ $Apache::lonhomework::analyze{"parts"} },"$part.$id");
 1389: 	    $Apache::lonhomework::analyze{"$part.$id.type"} = 'stringresponse';
 1390: 	    &Apache::response::check_if_computed($token,$parstack,$safeeval,
 1391: 						 'answer');
 1392: 	}
 1393: 	&Apache::response::setup_params('stringresponse',$safeeval);
 1394: 	if ($target eq 'answer') {
 1395: 	    $result.=&Apache::response::answer_header('stringresponse');
 1396: 	}
 1397: 	foreach my $name (keys(%answer)) {
 1398: 	    my @answers = @{ $answer{$name}{'answers'} };
 1399: 	    for (my $i=0;$i<=$#answers;$i++) {
 1400: 		my $answer_part = $answers[$i];
 1401: 		foreach my $element (@{$answer_part}) {
 1402: 		    if ($target eq 'answer') {
 1403: 			$result.=&Apache::response::answer_part('stringresponse',
 1404: 								$element);
 1405:                         if ($env{'form.grade_retrieveanswers'}) {
 1406:                             $env{'form.grade_answers.resource.'.$part.'.'.$id} = $element;
 1407:                         }
 1408: 		    } elsif ($target eq 'analyze') {
 1409: 			push (@{ $Apache::lonhomework::analyze{"$part.$id.answer"}{$name}[$i] },
 1410: 			      $element);
 1411: 		    }
 1412: 		}
 1413: 		if ($target eq 'answer' && $type eq 're') {
 1414: 		    $result.=&Apache::response::answer_part('stringresponse',
 1415: 							    $answerdisplay);
 1416: 		}
 1417: 	    }
 1418: 	}
 1419: 	my $string='Case Insensitive';
 1420: 	if ($type eq 'mc') {
 1421: 	    $string='Multiple Choice';
 1422: 	} elsif ($type eq 'cs') {
 1423: 	    $string='Case Sensitive';
 1424: 	} elsif ($type eq 'ci') {
 1425: 	    $string='Case Insensitive';
 1426: 	} elsif ($type eq 're') {
 1427: 	    $string='Regular Expression';
 1428: 	}
 1429: 	if ($target eq 'answer') {
 1430: 	    if ($env{'form.answer_output_mode'} eq 'tex') {
 1431: 		$result.=&Apache::response::answer_part('stringresponse',
 1432: 							"$string");
 1433: 	    } else {
 1434: 		$result.=&Apache::response::answer_part('stringresponse',
 1435: 							"<b>$string</b>");
 1436: 	    }
 1437: 	} elsif ($target eq 'analyze') {
 1438: 	    push (@{$Apache::lonhomework::analyze{"$part.$id.str_type"}},
 1439: 		  $type);
 1440: 	}
 1441: 	if ($target eq 'answer') {
 1442: 	    $result.=&Apache::response::answer_footer('stringresponse');
 1443: 	}
 1444:     } elsif ($target eq 'edit') {
 1445: 	$result.='</td></tr>'.&Apache::edit::end_table;
 1446:     } elsif ($target eq 'web' || $target eq 'tex') {
 1447: 	&Apache::response::setup_prior_tries_hash(\&format_prior_response_string);
 1448:     }
 1449:     if ($target eq 'grade' || $target eq 'web' || $target eq 'answer' || 
 1450: 	$target eq 'tex' || $target eq 'analyze') {
 1451:         my $repetition = &Apache::response::repetition();
 1452: 	&Apache::lonxml::increment_counter($repetition,"$part.$id");
 1453: 	if ($target eq 'analyze') {
 1454: 	    &Apache::lonhomework::set_bubble_lines();
 1455: 	}
 1456:     }
 1457:     &Apache::response::end_response;
 1458:     return $result;
 1459: }
 1460: 
 1461: sub start_formularesponse {
 1462:     my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
 1463:     my $result;
 1464:     if ($target eq 'meta') {
 1465: 	&Apache::response::start_response($parstack,$safeeval);
 1466: 	$result=&Apache::response::meta_package_write('formularesponse');
 1467: 	&Apache::response::end_response();
 1468:     } else {
 1469: 	$result.=&start_numericalresponse(@_);
 1470:     }
 1471:     return $result;
 1472: }
 1473: 
 1474: sub end_formularesponse {
 1475:     return end_numericalresponse(@_);
 1476: }
 1477: 
 1478: 1;
 1479: __END__
 1480: 

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