--- loncom/homework/default_homework.lcpm 2008/03/05 16:06:39 1.127 +++ loncom/homework/default_homework.lcpm 2017/01/04 19:54:47 1.172 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # used by lonxml::xmlparse() as input variable $safeinit to Apache::run::run() # -# $Id: default_homework.lcpm,v 1.127 2008/03/05 16:06:39 www Exp $ +# $Id: default_homework.lcpm,v 1.172 2017/01/04 19:54:47 damieng Exp $ # # Copyright Michigan State University Board of Trustees # @@ -143,7 +143,6 @@ sub caparesponse_check { my $sig_lbound=''; #done my $sig_ubound=''; #done - #type's definitons come from capaParser.h #remove leading and trailing whitespace @@ -152,12 +151,10 @@ sub caparesponse_check { } if ($response=~ /^\s|\s$/) { $response=~ s:^\s+|\s+$::g; - &LONCAPA_INTERNAL_DEBUG("Removed ws now :$response:"); } - #&LONCAPA_INTERNAL_DEBUG(" type is $type "); if ($type eq 'cs' || $type eq 'ci') { - #for string answers make surec all places spaces occur, there is + #for string answers make sure all places spaces occur, there is #really only 1 space, in both the answer and the response $answer=~s/ +/ /g; $response=~s/ +/ /g; @@ -179,7 +176,6 @@ sub caparesponse_check { if (length($response) > 500) { return ('TOO_LONG',undef); } if ($type eq '' ) { - &LONCAPA_INTERNAL_DEBUG("Didn't find a type :$type: defaulting"); if ( $answer eq ($answer *1.0)) { $type = 2; } else { $type = 3; } } else { @@ -199,7 +195,6 @@ sub caparesponse_check { #formula type setup the sample points if ($type eq '8') { ($id_list,$points)=split(/@/,$samples); - &LONCAPA_INTERNAL_DEBUG("Found :$id_list:$points: points in $samples"); } if ($tol eq '') { $tol=0.0; @@ -217,7 +212,13 @@ sub caparesponse_check { my $reterror=""; my $result; - if ((($type eq '9') || ($type eq '8')) && ($response=~/\=/)) { return ('BAD_FORMULA','Please submit just an expression, not an equation.'); } + if (($type eq '9') || ($type eq '8')) { + if ($response=~/\=/) { + return ('BAD_FORMULA','Please submit just an expression, not an equation.'); + } elsif ($response =~ /\,/ and $response !~ /^\s*\{.*\}\s*$/) { + return ('BAD_FORMULA'); + } + } if ($type eq '9') { $result = &maxima_check(&maxima_cas_formula_fix($response),&maxima_cas_formula_fix($answer),\$reterror); } else { @@ -252,22 +253,20 @@ sub caparesponse_check { elsif ($result =='15') { $result='UNIT_IRRECONCIBLE'; } else {$result = "ERROR: Unknown Result:$result:$@:";} - &LONCAPA_INTERNAL_DEBUG("RetError $reterror: Answer $answer: Response $response: type-$type|$tol|$tol_type|$sig:$sig_lbound:$sig_ubound|$unit|"); - &LONCAPA_INTERNAL_DEBUG(" $answer $response $result "); - return ($result,$reterror) + return ($result,$reterror); } sub caparesponse_check_list { my $responses=$LONCAPA::CAPAresponse_args{'response'}; -# &LONCAPA_INTERNAL_DEBUG(" answer is ". -# &LONCAPA_INTERNAL_Dumper($LONCAPA::CAPAresponse_answer).":\n"); -# &LONCAPA_INTERNAL_DEBUG(" respons is ". -# &LONCAPA_INTERNAL_Dumper($responses).":\n"); &LONCAPA_INTERNAL_DEBUG("args ".join(':',%LONCAPA::CAPAresponse_args)); my $type = $LONCAPA::CAPAresponse_args{'type'}; - &LONCAPA_INTERNAL_DEBUG("Got type :$type:\n"); - + my $answerunit=$LONCAPA::CAPAresponse_args{'unit'}; + &LONCAPA_INTERNAL_DEBUG("Got type :$type: answer unit :$answerunit:\n"); + + my $preprocess=$LONCAPA::CAPAresponse_args{'preprocess'}; + $preprocess=~s/^\&//; + my $num_input_lines = scalar(@{$LONCAPA::CAPAresponse_answer->{'answers'}}); @@ -291,8 +290,6 @@ sub caparesponse_check_list { $responses->[$which]=[$responses->[$which]]; } } -# &LONCAPA_INTERNAL_DEBUG(" parsed response is ". -# &LONCAPA_INTERNAL_Dumper($responses).":\n"); foreach my $which (0..($num_input_lines-1)) { my $answer_size = scalar(@{$LONCAPA::CAPAresponse_answer->{'answers'}[$which]}); @@ -306,24 +303,74 @@ sub caparesponse_check_list { } } - &LONCAPA_INTERNAL_DEBUG("Initial final response :$responses->[0][-1]:"); my $unit; - if ($type eq '' || $type eq 'float') { + my ($allowalgebra)=($LONCAPA::CAPAresponse_args{'allowalgebra'}=~/^(yes|1|on)$/i); + if ($type eq 'float' || $type eq '') { #for numerical problems split off the unit - if ( $responses->[0][-1]=~ /(.*[^\s])\s+([^\s]+)/ ) { - $responses->[0][-1]=$1; - $unit=$2; + my $part1; + my $part2; + if ($allowalgebra) { + ($part1,$part2)=($responses->[0][-1]=~ /^(.*[^\s])\s+([^\s]+)$/); + } else { + ($part1,$part2)=($responses->[0][-1]=~ /^([\d\.\,\s\$]*(?:(?:[xX\*]10[\^\*]*|[eE]*)[\+\-]*\d*)*(?:^|\S)\d+)([\$\s\w\^\*\/\(\)\+\-]*[^\d\.\s\,][\$\s\w\^\*\/\(\)\+\-]*)$/); + } + if (defined($part1) && defined($part2)) { + $responses->[0][-1]=$part1; + $unit=&capa_formula_fix($part2); + my $customunits=$LONCAPA::CAPAresponse_args{'customunits'}; + if ($customunits =~ /\S/) { + foreach my $replacement (split(/\s*\,\s*/,$customunits)) { + my ($which,$what)=split(/\s*\=\s*/,$replacement); + if ((defined($which)) && (defined($what))) { + $what=&capa_formula_fix($what); + $unit=~s/$which/\($what\)/g; + } + } + } } } - &LONCAPA_INTERNAL_DEBUG("Final final response :$responses->[0][-1]:$unit:"); $unit=~s/\s//; - if ($unit ne '') { - foreach my $response (@$responses) { - foreach my $element (@$response) { - $element =~ s/\s//g; - $element .= " $unit"; - } - } + my $error; + foreach my $response (@$responses) { + foreach my $element (@$response) { + # See if we have preprocessor + if ($preprocess=~/\S/) { + if (defined(&$preprocess)) { + no strict 'refs'; + $element=&$preprocess($element,$unit); + use strict 'refs'; + } + } + if (($type eq 'float') || (($type eq '') && ($unit ne ''))) { + $element =~ s/\s//g; + } + my $appendunit=$unit; +# Deal with percentages +# unit is unit entered by student, answerunit is unit by author +# Deprecated: divide answer by 100 if student entered percent, +# but author did not. Too much confusion +# if (($unit=~/\%/) && ($answerunit ne '%')) { +# $element=$element/100; +# $appendunit=~s/\%//; +# } +# Author entered percent, student did not + if (($unit!~/\%/) && ($answerunit=~/\%/)) { + $element=$element*100; + $appendunit='%'.$appendunit; + } +# Zero does not need a dimension + if (($element =~ /^[0\.]+$/) && ($unit!~/\w/) && ($answerunit=~/\w/)) { + $appendunit=$answerunit; + } +# Do the math for the student if allowed + if ($allowalgebra) { + $element=&cas('maxima',$element); + } + if ($appendunit ne '') { + $element .= " $appendunit"; + } + &LONCAPA_INTERNAL_DEBUG("Made response element :$element:"); + } } foreach my $thisanswer (@{ $LONCAPA::CAPAresponse_answer->{'answers'} }) { @@ -332,6 +379,23 @@ sub caparesponse_check_list { } } + my $allow_control_char = 0; + my $control_chars_removed = 0; + my $ansstring; + if ($type eq 'cs' || $type eq 'ci') { + if (ref($LONCAPA::CAPAresponse_answer->{'answers'}) eq 'ARRAY') { + foreach my $strans (@{$LONCAPA::CAPAresponse_answer->{'answers'}}) { + if (ref($strans) eq 'ARRAY') { + $ansstring = join("\0",@{$strans}); + foreach my $item (@{$strans}) { + if ($item =~ /[\000-\037]/) { + $allow_control_char = 1; + } + } + } + } + } + } # &LONCAPA_INTERNAL_DEBUG(&LONCAPA_INTERNAL_Dumper($responses)); my %memoized; @@ -341,9 +405,30 @@ sub caparesponse_check_list { my $response = $responses->[$i]; my $key = "$answer\0$response"; my (@awards,@msgs); - for (my $j=0; $j[$j] =~ /[\000-\037]/) { + $response->[$j] =~ s/[\000-\037]//g; + $control_chars_removed = 1; + } + } + } + # See if we have preprocessor for string responses + if (($preprocess=~/\S/) && ($type eq 'cs' || $type eq 'ci')) { + if (defined(&$preprocess)) { + no strict 'refs'; + $response->[$j]=&$preprocess($response->[$j]); + use strict 'refs'; + } + } + my ($award,$msg) = &caparesponse_check($answer->[$j], $response->[$j]); + if ($type eq 'cs' || $type eq 'ci') { + $error = &verify_stringresponse($type,$award,$response->[$j], + $answer->[$j]); + } push(@awards,$award); push(@msgs, $msg); } @@ -368,8 +453,29 @@ sub caparesponse_check_list { } else { my (@awards,@msgs); for (my $j=0; $j[$j] =~ /[\000-\037]/) { + $response->[$j] =~ s/[\000-\037]//g; + $control_chars_removed = 1; + } + } + } + # See if we have preprocessor + if (($preprocess=~/\S/) && ($type eq 'cs' || $type eq 'ci')) { + if (defined(&$preprocess)) { + no strict 'refs'; + $response->[$j]=&$preprocess($response->[$j]); + use strict 'refs'; + } + } + my ($award,$msg) = &caparesponse_check($answer->[$j], $response->[$j]); + if ($type eq 'cs' || $type eq 'ci') { + $error = &verify_stringresponse($type,$award,$response->[$j], + $answer->[$j]); + } push(@awards,$award); push(@msgs, $msg); } @@ -416,18 +522,75 @@ sub caparesponse_check_list { &LONCAPA_INTERNAL_DEBUG(" all final_awards ".join(':',@final_awards)); my ($final_award,$final_msg) = &LONCAPA_INTERNAL_FINALIZEAWARDS(\@final_awards,\@final_msg,undef,1); - return ($final_award,$final_msg); + return ($final_award,$final_msg,$error,$control_chars_removed,$ansstring); +} + +sub verify_stringresponse { + my ($type,$award,$resp,$ans) = @_; + return if ($award eq 'EXACT_ANS'); + my $error; + if ($resp =~ /^\s|\s$/) { + $resp =~ s{^\s+|\s+$}{}g; + } + if ($ans =~ /^\s|\s$/) { + $ans =~ s{^\s+|\s+$}{}g; + } + if ($type eq 'ci') { + $resp = lc($resp); + $ans = lc($ans); + } + if ($resp eq $ans) { + if ($award eq 'INCORRECT') { + $error = 'MISGRADED'; + } + } + return $error; } sub cas { - my ($system,$input)=@_; + my ($system,$input,$library)=@_; my $output; + my $dump; if ($system eq 'maxima') { - $output=&maxima_eval($input); + $output=&maxima_eval($input,$library); + } elsif ($system eq 'R') { + ($output,$dump)=&r_eval($input,$library,0); + } else { + $output='Error: unrecognized CAS'; } return $output; } +sub cas_hashref { + my ($system,$input,$library)=@_; + if ($system eq 'maxima') { + return 'Error: unsupported CAS'; + } elsif ($system eq 'R') { + return &r_eval($input,$library,1); + } else { + return 'Error: unrecognized CAS'; + } +} + +# +# cas_hashref_entry takes a list of indices and gets the entry in a hash generated by Rreturn. +# Call: cas_hashref_entry(Rvalue, index1, index2, ...) where Rvalue is a hash returned by Rreturn. +# Rentry will return the first scalar value it encounters (ignoring excess indices). +# If an invalid key is given, it returns undef. +# +sub cas_hashref_entry { + return &Rentry(@_); +} + +# +# cas_hashref_array takes a list of indices and gets a column array from a hash generated by Rreturn. +# Call: cas_hashref_array(Rvalue, index1, index2, ...) where Rvalue is a hash returned by Rreturn. +# If an invalid key is given, it returns undef. +# +sub cas_hashref_array { + return &Rarray(@_); +} + sub tex { if ( $external::target eq "tex" ) { return $_[0]; @@ -653,6 +816,7 @@ sub cos { CORE::cos(shift) } sub exp { CORE::exp(shift) } sub int { CORE::int(shift) } sub log { CORE::log(shift) } +sub ln { CORE::log(shift) } sub atan2 { CORE::atan2($_[0],$_[1]) } sub sqrt { CORE::sqrt(shift) } @@ -738,11 +902,19 @@ sub chemparse { my $formula = ''; foreach my $token (@tokens) { if ($token eq '->' ) { - $formula .= '\ensuremath{\rightarrow} '; + if ($external::target eq 'web') { + $formula .= '→ '; + } else { + $formula .= '\ensuremath{\rightarrow} '; + } next; } if ($token eq '<-' ) { - $formula .= '\ensuremath{\leftarrow} '; + if ($external::target eq 'web') { + $formula .= '← '; + } else { + $formula .= '\ensuremath{\leftarrow} '; + } next; } if ($token eq '<=>') { @@ -879,16 +1051,47 @@ sub format_significant_figures { my ($zeros) = ($xint =~ /(0+)$/); # return number to original magnitude my $numSig = $xint*10**($x10-$sig+$power); - # insert trailing zero's if have decimal point - $numSig =~ s/^(\d+)\.(\d+)(\e?(.*)?)$/$1\.$2$zeros$3/; - # put a decimal pt for number ending with 0 and length = # of sig fig - $numSig.='.' if (length($numSig) == $sig && $numSig =~ /0$/); - if (length($numSig) < $sig) { - $numSig.='.'.substr($zeros,0,($sig-length($numSig))); + if ($numSig =~ /^(\d+)\.(\d+)/) { + # insert trailing zero's if have decimal point + my @digarray = split('',$1.$2); + my $sigcount; + while (@digarray > 0) { + my $item = shift(@digarray); + if ($item) { + $sigcount = 1 + @digarray; + last; + } + } + if (($sigcount) && ($sig >= $sigcount)) { + $zeros = substr($zeros,0,($sig - $sigcount)); + } + $numSig =~ s/^(\d+)\.(\d+)(\e?(.*)?)$/$1\.$2$zeros$3/; + } else { + if ($numSig =~ /^(\d+)e([\+\-]\d+)$/i) { + my $pre_exp = $1; + my $exponent = $2; + $numSig = $pre_exp.'.'.$zeros.'E'.$exponent; + } elsif ($numSig =~ /0$/) { + # add decimal pt for number ending with 0 and length == # of sig figs + if (length($numSig) == $sig) { + $numSig.='.'; + } elsif (length($numSig) > $sig) { + # exponential form for number ending with 0 and length > # of sig figs + my $fmtsig = $sig-1; + if ($fmtsig) { + $numSig = sprintf('%.'.$fmtsig.'E',$numSig); + } + } elsif (length($numSig) < $sig) { + $numSig.='.'.substr($zeros,0,($sig-length($numSig))); + } + } else { + if (length($numSig) < $sig) { + $numSig.='.'.substr($zeros,0,($sig-length($numSig))); + } + } } # return number with sign return $sign.$numSig; - } sub map { @@ -1002,12 +1205,24 @@ sub class { return $course; } +sub classid { + my $courseid = &EXT('request.course.id'); + $courseid = '' if $courseid eq ""; + return $courseid; +} + sub firstname { my $firstname = &EXT('environment.firstname'); $firstname = '' if $firstname eq ""; return $firstname; } - + +sub middlename { + my $middlename = &EXT('environment.middlename'); + $middlename = '' if $middlename eq ""; + return $middlename; +} + sub lastname { my $lastname = &EXT('environment.lastname'); $lastname = '' if $lastname eq ""; @@ -1020,40 +1235,159 @@ sub sec { return $sec; } -sub open_date { - my @dc = split(/\s+/,localtime(&EXT('resource.0.opendate'))); - return '' if ($dc[0] eq "Wed" and $dc[2] == 31 and $dc[4] == 1969); - my @hm = split(/:/,$dc[3]); - my $ampm = " am"; - if ($hm[0] > 12) { - $hm[0]-=12; - $ampm = " pm"; - } - return $dc[0].', '.$dc[1].' '.$dc[2].', '.$dc[4].' at '.$hm[0].':'.$hm[1].$ampm; -} - -sub due_date { - my @dc = split(/\s+/,localtime(&EXT('resource.0.duedate'))); - return '' if ($dc[0] eq "Wed" and $dc[2] == 31 and $dc[4] == 1969); - my @hm = split(/:/,$dc[3]); - my $ampm = " am"; - if ($hm[0] > 12) { - $hm[0]-=12; - $ampm = " pm"; +sub submission { + my ($partid,$responseid,$subnumber,$encode,$cleanupnum,$mapalias)=@_; + my $sub=''; + if ($subnumber) { $sub=$subnumber.':'; } + my $output = + &EXT('user.resource.'.$sub.'resource.'.$partid.'.'.$responseid.'.submission',$mapalias); + if (ref($output) eq 'ARRAY') { + my @items = @{$output}; + if ($encode) { + @items = map { &encode_response($_); } @items; + } + if (ref($cleanupnum) eq 'HASH') { + @items = map { &cleanup_numerical_response($cleanupnum,$_); } @items; + } + return \@items; + } else { + if ($encode) { + $output = &encode_response($output); + } + if (ref($cleanupnum) eq 'HASH') { + $output = &cleanup_numerical_response($cleanupnum,$output); + } + return $output; + } +} + +sub encode_response { + my ($value) = @_; + $value =~ s/&/&/g; + $value =~ s//>/g; + $value =~ s/"/"/g; + return $value; +} + +sub cleanup_numerical_response { + my ($cleanupnum,$value) = @_; + if (ref($cleanupnum) eq 'HASH') { + if ($cleanupnum->{exponent}) { + if ($value =~ m{^(.*)[\*xX]\s*10\s*\^\s*(\+|\-)?\s*(\d+)(.*)$}) { + my $pre_exp = $1; + my $sign = $2; + my $exponent = $3; + my $post_exp = $4; + if ($pre_exp !~ /\./) { + $pre_exp .= '.'; + } + if ($sign eq '') { + $sign = '+'; + } + $value = $pre_exp.'E'.$sign.$exponent.$post_exp; + } + } + if ($cleanupnum->{comma}) { + $value =~ s{(\d+),(\d+)}{$1$2}; + } + if ($cleanupnum->{letterforzero}) { + $value =~ s/^\s*o(\.\d+)/0$1/i; + } + if ($cleanupnum->{spaces}) { + $value =~ s{^\s+|\s+$}{}g; + if ($value =~ m{^(.*)\.\s+(\d+)(.*)$}) { + my $pre_pt = $1; + my $decimal = $2; + my $post_dec = $3; + $value = $pre_pt.'.'.$decimal.$post_dec; + } + } + if ($cleanupnum->{format} =~ /^\d+s$/i) { + $value = &format_significant_figures($value,$cleanupnum->{format}); + } } - return $dc[0].', '.$dc[1].' '.$dc[2].', '.$dc[4].' at '.$hm[0].':'.$hm[1].$ampm; + return $value; +} + +sub currentpart { + return $external::part; +} + +sub eval_time { + my ($timestamp)=@_; + unless ($timestamp) { return ''; } + return &locallocaltime($timestamp); +} + +sub open_date { + my ($partid)=@_; + unless ($partid) { $partid=0; } + return &eval_time(&EXT('resource.'.$partid.'.opendate')); +} + +sub due_date { + my ($partid)=@_; + unless ($partid) { $partid=0; } + return &eval_time(&EXT('resource.'.$partid.'.duedate')); } sub answer_date { - my @dc = split(/\s+/,localtime(&EXT('resource.0.answerdate'))); - return '' if ($dc[0] eq "Wed" and $dc[2] == 31 and $dc[4] == 1969); - my @hm = split(/:/,$dc[3]); - my $ampm = " am"; - if ($hm[0] > 12) { - $hm[0]-=12; - $ampm = " pm"; + my ($partid)=@_; + unless ($partid) { $partid=0; } + return &eval_time(&EXT('resource.'.$partid.'.answerdate')); +} + +sub open_date_epoch { + my ($partid)=@_; + unless ($partid) { $partid=0; } + return &EXT('resource.'.$partid.'.opendate'); +} + +sub due_date_epoch { + my ($partid)=@_; + unless ($partid) { $partid=0; } + return &EXT('resource.'.$partid.'.duedate'); +} + +sub answer_date_epoch { + my ($partid)=@_; + unless ($partid) { $partid=0; } + return &EXT('resource.'.$partid.'.answerdate'); +} + +sub parameter_setting { + my ($which,$partid)=@_; + unless ($partid) { $partid=0; } + return &EXT('resource.'.$partid.'.'.$which); +} + +sub stored_data { + my ($which,$partid)=@_; + unless ($partid) { $partid=0; } + return &EXT('user.resource.resource.'.$partid.'.'.$which); +} + +sub wrong_bubbles { + my ($correct,$lower,$upper,$step,@given)=@_; + my @array=(); + my %hash=(); + foreach my $new (@given) { + $hash{$new}=1; + } + my $num=int(¶meter_setting('numbubbles',¤tpart())); + unless ($num) { $num=8; } + if ($num>1) { + for (my $i=0;$i<=500;$i++) { + my $new=&random($lower,$upper,$step); + if ($hash{$new}) { next; } + if (abs($new-$correct)<$step) { next; } + $hash{$new}=1; + @array=keys(%hash); + if ($#array+2>=$num) { last; } + } } - return $dc[0].', '.$dc[1].' '.$dc[2].', '.$dc[4].' at '.$hm[0].':'.$hm[1].$ampm; + return @array; } sub array_moments { @@ -1132,3 +1466,8 @@ sub proper_path { } } +sub input_id { + my ($part_id, $response_id, $textline_id) = @_; + return 'HWVAL_'.$part_id.'_'.$response_id.'_'.$textline_id; +} + 500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.