--- loncom/homework/caparesponse/caparesponse.pm 2007/06/06 17:03:54 1.216 +++ loncom/homework/caparesponse/caparesponse.pm 2010/12/16 16:01:08 1.243 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # caparesponse definition # -# $Id: caparesponse.pm,v 1.216 2007/06/06 17:03:54 albertel Exp $ +# $Id: caparesponse.pm,v 1.243 2010/12/16 16:01:08 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -33,6 +33,7 @@ use Safe::Hole; use Apache::lonmaxima(); use Apache::lonlocal; use Apache::lonnet; +use Apache::lonmsg(); use Apache::response(); use Storable qw(dclone); @@ -305,7 +306,7 @@ sub start_numericalresponse { my $hideunit=&Apache::lonnet::EXT('resource.'.$partid.'_'.$id.'.turnoffunit'); &Apache::lonxml::debug("Got unit $hideunit for $partid $id"); #no way to enter units, with radio buttons - if (lc($hideunit) eq "yes") { + if ((lc($hideunit) eq "yes") && ($Apache::lonhomework::type ne 'exam')) { my $unit=&Apache::lonxml::get_param_var('unit',$parstack, $safeeval); if ($unit =~ /\S/) { $result.=" (in $unit) "; } @@ -427,10 +428,18 @@ sub check_submission { } } elsif ($tag eq 'numericalresponse') { $$args_ref{'type'}='float'; + } elsif ($tag eq 'stringresponse') { + if ($$args_ref{'type'} eq '') { + $$args_ref{'type'} = 'ci'; + } } - + &add_in_tag_answer($parstack,$safeeval); + if (!%answer) { + &Apache::lonxml::error("No answers are defined"); + } + my (@final_awards,@final_msgs,@names); foreach my $name (keys(%answer)) { &Apache::lonxml::debug(" doing $name with ".join(':',@{ $answer{$name}{'answers'} })); @@ -442,6 +451,7 @@ sub check_submission { my ($result,@msgs) = &Apache::run::run("&caparesponse_check_list()",$safeeval); &Apache::lonxml::debug("checking $name $result with $response took ".&Time::HiRes::tv_interval($t0)); + &Apache::lonxml::debug('msgs are '.join(':',@msgs)); my ($awards)=split(/:/,$result); my @awards= split(/,/,$awards); @@ -457,6 +467,48 @@ sub check_submission { return($ad,$msg, $name); } +sub stringresponse_gradechange { + my ($part,$id,$previous,$caller,$response,$ad,$type) = @_; + return unless (ref($previous) eq 'HASH'); + my ($prevarray,$prevaward); + my %typenames = ( + cs => 'Case sensitive', + ci => 'Case insensitive', + ); + if ($caller eq 'cs') { + return unless (ref($previous->{'version'}) eq 'ARRAY'); + $prevarray = $previous->{'version'}; + $prevaward = $previous->{'award'}; + } elsif ($caller eq 'ci') { + return unless (ref($previous->{'versionci'}) eq 'ARRAY'); + $prevarray = $previous->{'versionci'}; + $prevaward = $previous->{'awardci'}; + } else { + return; + } + my $count=0; + my %count_lookup; + foreach my $i (1..$Apache::lonhomework::history{'version'}) { + my $prefix = $i.":resource.$part"; + next if (!exists($Apache::lonhomework::history{"$prefix.award"})); + $count++; + $count_lookup{$i} = $count; + } + my ($symb,$courseid,$domain,$name) = &Apache::lonnet::whichuser(); + my %coursedesc = &Apache::lonnet::coursedescription($courseid); + my $cdom = $coursedesc{'domain'}; + my $versions = ' (submissions: '.join(', ',map {$count_lookup{$_} } @{$prevarray}).')'; + 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"; + &Apache::lonnet::logthis($warning); + my $origmail = $Apache::lonnet::perlvar{'lonAdmEMail'}; + my $recipients = &Apache::loncommon::build_recipient_list(undef,'errormail', + $cdom,$origmail); + if ($recipients ne '') { + &Apache::lonmsg::sendemail($recipients,'Stringresponse Grading Discrepancy',$warning); + } + return; +} + sub add_in_tag_answer { my ($parstack,$safeeval,$response_level) = @_; my @answer=&Apache::lonxml::get_param_var('answer',$parstack,$safeeval, @@ -506,12 +558,12 @@ sub end_numericalresponse { &Apache::lonxml::debug($$parstack[-1] . "\n
"); if ( &Apache::response::submitted('scantron')) { - &add_in_tag_answer($parstack,$safeeval); - my ($values,$display)=&make_numerical_bubbles($partid,$id, - $target,$parstack,$safeeval); - $response=$values->[$response]; - } - $Apache::lonhomework::results{"resource.$partid.$id.submission"}=$response; + &add_in_tag_answer($parstack,$safeeval); + my ($values,$display)=&make_numerical_bubbles($partid,$id, + $target,$parstack,$safeeval); + $response=$values->[$response]; + } + $Apache::lonhomework::results{"resource.$partid.$id.submission"}=$response; my ($ad,$msg,$name)=&check_submission($response,$partid,$id, $tag,$parstack, $safeeval); @@ -525,11 +577,18 @@ sub end_numericalresponse { $Apache::inputtags::params{'sig'}); } &Apache::lonxml::debug("\n
result:$result:$Apache::lonxml::curdepth
\n"); - if ($Apache::lonhomework::type eq 'survey' && - ($ad eq 'INCORRECT' || $ad eq 'APPROX_ANS' || - $ad eq 'EXACT_ANS')) { - $ad='SUBMITTED'; - } + if (($ad eq 'INCORRECT' || $ad eq 'APPROX_ANS' || + $ad eq 'EXACT_ANS')) { + if ($Apache::lonhomework::type eq 'survey') { + $ad='SUBMITTED'; + } elsif ($Apache::lonhomework::type eq 'surveycred') { + $ad='SUBMITTED_CREDIT'; + } elsif ($Apache::lonhomework::type eq 'anonsurvey') { + $ad='ANONYMOUS'; + } elsif ($Apache::lonhomework::type eq 'anonsurveycred') { + $ad='ANONYMOUS_CREDIT'; + } + } &Apache::response::handle_previous(\%previous,$ad); $Apache::lonhomework::results{"resource.$partid.$id.awarddetail"}=$ad; $Apache::lonhomework::results{"resource.$partid.$id.awardmsg"}=$msg; @@ -577,9 +636,16 @@ sub end_numericalresponse { &get_table_sizes($number_of_bubbles,$bubble_display); my $j=0; my $cou=0; - $result.='\vskip -1 mm \noindent \begin{enumerate}\item[\textbf{'.$Apache::lonxml::counter.'}.]'; + $result.='\vskip 2mm \noindent '; + $result .= '\textbf{'.$Apache::lonxml::counter.'.} \vskip -3mm '; + for (my $i=0;$i<$number_of_tables;$i++) { - $result.='\vskip -1 mm \noindent \setlength{\tabcolsep}{2 mm}\begin{tabular}{'; + if ($i == 0) { + $result .= '\vskip -1mm '; + } else { + $result .= '\vskip 1mm '; + } + $result.='\noindent \setlength{\tabcolsep}{2 mm}\hskip 2pc\begin{tabular}{'; for (my $ind=0;$ind<$table_range[$j];$ind++) { $result.='p{3 mm}p{'.$celllength.' mm}'; } @@ -592,12 +658,17 @@ sub end_numericalresponse { $j++; $result.='\\\\\end{tabular}\vskip 0 mm '; } - $result.='\end{enumerate}'; } else { $increment = &Apache::response::repetition(); } } } + if (($target eq 'web') && ($tag eq 'formularesponse') + && ($Apache::lonhomework::type ne 'exam') && ($Apache::inputtags::status['-1'] eq 'CAN_ANSWER') + && (&Apache::lonnet::EXT('resource.'.$partid.'_'.$id.'.turnoffeditor') ne 'yes')) { + $result.=&Apache::response::edit_mathresponse_button($id,"HWVAL_$id"); + } + &Apache::response::setup_prior_tries_hash(\&format_prior_response_numerical); } elsif ($target eq 'edit') { $result.=''.&Apache::edit::end_table; @@ -674,12 +745,12 @@ sub end_numericalresponse { #} } if ($high && $tag eq 'numericalresponse') { - $element.=' ['.$low.','.$high.']'; + $element.='; ['.$low.'; '.$high.']'; $tolline .= "[$low, $high]"; } if (defined($sighigh) && $tag eq 'numericalresponse') { if ($env{'form.answer_output_mode'} eq 'tex') { - $element.= " Sig $siglow - $sighigh"; + $element.= "; Sig $siglow - $sighigh"; } else { $element.= " Sig $siglow - $sighigh"; $sigline .= "[$siglow, $sighigh]"; @@ -699,7 +770,7 @@ sub end_numericalresponse { } } if ($target eq 'answer') { - $result.= &Apache::response::answer_part($tag,join(', ',@all_answer_info)); + $result.= &Apache::response::answer_part($tag,join('; ',@all_answer_info)); } } @@ -738,16 +809,17 @@ sub end_numericalresponse { if ($ad ne 'EXACT_ANS' && $ad ne 'APPROX_ANS') { my $error; if ($tag eq 'formularesponse') { - $error=&mt('Computer\'s answer is incorrect ("[_1]").',join(', ',@$response)); + $error=&mt("Computer's answer is incorrect ([_1]).",'"'.join(', ',@$response).'"'); } else { # answer failed check if it is sig figs that is failing my ($ad,$msg)=&check_submission($response,$partid,$id, $tag,$parstack, $safeeval,1); + $error=&mt("Computer's answer is incorrect ([_1]).",'"'.join(', ',@$response).'"').' '; if ($sigline ne '') { - $error=&mt('Computer\'s answer is incorrect ("[_1]"). It is likely that the tolerance range [_2] or significant figures [_3] need to be adjusted.',join(', ',@$response),$tolline,$sigline); + $error.=&mt('It is likely that the tolerance range [_1] or significant figures [_2] need to be adjusted.',$tolline,$sigline); } else { - $error=&mt('Computer\'s answer is incorrect ("[_1]"). It is likely that the tolerance range [_2] needs to be adjusted.',join(', ',@$response),$tolline); + $error.=&mt('It is likely that the tolerance range [_1] needs to be adjusted.',$tolline); } } if ($ad ne 'EXACT_ANS' && $ad ne 'APPROX_ANS') { @@ -783,7 +855,16 @@ sub end_numericalresponse { } if ($target eq 'grade' || $target eq 'web' || $target eq 'answer' || $target eq 'tex' || $target eq 'analyze') { - &Apache::lonxml::increment_counter($increment); + if (($tag eq 'formularesponse') && ($target eq 'analyze')) { + my $type = &Apache::lonnet::EXT('resource.'.$partid.'_'.$id.'.type'); + if ($type eq 'exam') { + $increment = &Apache::response::repetition(); + } + } + &Apache::lonxml::increment_counter($increment,"$partid.$id"); + if ($target eq 'analyze') { + &Apache::lonhomework::set_bubble_lines(); + } } &Apache::response::end_response(); return $result; @@ -975,13 +1056,23 @@ sub make_numerical_bubbles { $ind=&Math::Random::random_uniform_integer(1,0,$#factors); my $factor = $factors[$ind]; my @bubble_display; + my $answerfactor=$answer; + if ($answer==0) { + $answerfactor=&Math::Random::random_uniform_integer(1,1,100)/ + &Math::Random::random_uniform_integer(1,1,10); + } for ($ind=0;$ind<$number_of_bubbles;$ind++) { - $bubble_values[$ind] = $answer*($factor**($power-$powers[$#powers-$ind])); + $bubble_values[$ind] = $answerfactor*($factor**($power-$powers[$#powers-$ind])); $bubble_display[$ind] = &format_number($bubble_values[$ind], $format,$target,$safeeval); - } my $correct = $alphabet[$number_of_bubbles-$power]; + if ($answer==0) { + $correct='A'; + $bubble_values[0]=0; + $bubble_display[0] = &format_number($bubble_values[0], + $format,$target,$safeeval); + } &Math::Random::random_set_seed(@oldseed); return (\@bubble_values,\@bubble_display,$correct); } @@ -1005,7 +1096,8 @@ sub get_sigrange { my ($sig)=@_; #&Apache::lonxml::debug("Got a sig of :$sig:"); my $courseid=$env{'request.course.id'}; - if (lc($env{"course.$courseid.disablesigfigs"}) eq 'yes') { + if ($env{'request.state'} ne 'construct' + && lc($env{"course.$courseid.disablesigfigs"}) eq 'yes') { return (15,0); } my $sig_lbound; @@ -1105,7 +1197,8 @@ sub end_stringresponse { my $response = &Apache::response::getresponse(); if ( $response =~ /[^\s]/) { my %previous = &Apache::response::check_for_previous($response, - $part,$id); + $part,$id, + undef,$type); &Apache::lonxml::debug("submitted a $response
\n"); &Apache::lonxml::debug($$parstack[-1] . "\n
"); $Apache::lonhomework::results{"resource.$part.$id.submission"}= @@ -1127,13 +1220,42 @@ sub end_stringresponse { my @args = ('type'); my $args_ref = &setup_capa_args($safeeval,$parstack, \@args,$response); - + if ($$args_ref{'type'} eq '') { + $$args_ref{'type'} = 'ci'; + } &add_in_tag_answer($parstack,$safeeval); - my (@final_awards,@final_msgs,@names); + my (@final_awards,@final_msgs,@names,%ansstring); foreach my $name (keys(%answer)) { &Apache::lonxml::debug(" doing $name with ".join(':',@{ $answer{$name}{'answers'} })); ${$safeeval->varglob('LONCAPA::CAPAresponse_answer')}=dclone($answer{$name}); my ($result, @msgs)=&Apache::run::run("&caparesponse_check_list()",$safeeval); + if ($$args_ref{'type'} =~ /^c[si]$/) { + $ansstring{$name} = pop(@msgs); + } + if ($$args_ref{'type'} =~ /^c[si]$/) { + my $control_chars_removed = pop(@msgs); + my $error = pop(@msgs); + if (($error ne '') || + ($control_chars_removed ne '')) { + my ($symb,$courseid,$sdomain,$sname) = + &Apache::lonnet::whichuser(); + if ($control_chars_removed ne '') { + my $showresponse = $response; + if ($response =~ /[\000-\037]/) { + $response =~ s/[\000-\037]//g; + } + if ($showresponse =~ /[\r\n\f]/) { + my @lines = split(/[\r\n\f]+/,$showresponse); + $showresponse = join('\\n',@lines); + } + &Apache::lonnet::logthis("Stringresponse grading: control characters stripped from submission ".$showresponse." for $sname:$sdomain in $courseid for part: $part response: $id and symb: $symb"); + $Apache::lonhomework::results{"resource.$part.$id.submission"} = $response; + } + if ($error ne '') { + &Apache::lonnet::logthis("Stringresponse grading error: $error for $sname:$sdomain in $courseid for part: $part response: $id and symb: $symb"); + } + } + } &Apache::lonxml::debug('msgs are'.join(':',@msgs)); my ($awards)=split(/:/,$result); my (@awards) = split(/,/,$awards); @@ -1144,16 +1266,39 @@ sub end_stringresponse { push(@names,$name); &Apache::lonxml::debug("\n
result:$result:$Apache::lonxml::curdepth
\n"); } - my ($ad, $msg, $name) = + ($ad, $msg, my $name) = &Apache::inputtags::finalizeawards(\@final_awards, \@final_msgs, \@names,1); - } - if ($Apache::lonhomework::type eq 'survey' && - ($ad eq 'INCORRECT' || $ad eq 'APPROX_ANS' || - $ad eq 'EXACT_ANS')) { - $ad='SUBMITTED'; - } + if (keys(%ansstring) > 0) { + $Apache::lonhomework::results{"resource.$part.$id.answerstring"} = &Apache::lonnet::hash2str(%ansstring); + } + } + if (($ad eq 'INCORRECT' || $ad eq 'APPROX_ANS' || + $ad eq 'EXACT_ANS')) { + if ($Apache::lonhomework::type eq 'survey') { + $ad='SUBMITTED'; + } elsif ($Apache::lonhomework::type eq 'surveycred') { + $ad='SUBMITTED_CREDIT'; + } elsif ($Apache::lonhomework::type eq 'anonsurvey') { + $ad='ANONYMOUS'; + } elsif ($Apache::lonhomework::type eq 'anonsurveycred') { + $ad='ANONYMOUS_CREDIT'; + } + } + unless ($env{'request.state'} eq 'construct') { + if ($previous{'used'}) { + if ($ad ne $previous{'award'} && $previous{'award'} ne '') { + &stringresponse_gradechange($part,$id,\%previous, + 'cs',$response,$ad,$type); + } + } elsif ($previous{'usedci'}) { + if ($ad ne $previous{'awardci'} && $previous{'awardci'} ne '') { + &stringresponse_gradechange($part,$id,\%previous, + 'ci',$response,$ad,$type); + } + } + } &Apache::response::handle_previous(\%previous,$ad); $Apache::lonhomework::results{"resource.$part.$id.awarddetail"}=$ad; $Apache::lonhomework::results{"resource.$part.$id.awardmsg"}=$msg; @@ -1179,6 +1324,9 @@ sub end_stringresponse { if ($target eq 'answer') { $result.=&Apache::response::answer_part('stringresponse', $element); + if ($env{'form.grade_retrieveanswers'}) { + $env{'form.grade_answers.resource.'.$part.'.'.$id} = $element; + } } elsif ($target eq 'analyze') { push (@{ $Apache::lonhomework::analyze{"$part.$id.answer"}{$name}[$i] }, $element); @@ -1222,7 +1370,11 @@ sub end_stringresponse { } if ($target eq 'grade' || $target eq 'web' || $target eq 'answer' || $target eq 'tex' || $target eq 'analyze') { - &Apache::lonxml::increment_counter(&Apache::response::repetition()); + &Apache::lonxml::increment_counter(&Apache::response::repetition(), + "$part.$id"); + if ($target eq 'analyze') { + &Apache::lonhomework::set_bubble_lines(); + } } &Apache::response::end_response; return $result;