--- loncom/homework/response.pm 2008/12/11 04:11:09 1.206.2.1
+++ loncom/homework/response.pm 2011/12/15 01:21:28 1.230
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# various response type definitons response definition
#
-# $Id: response.pm,v 1.206.2.1 2008/12/11 04:11:09 raeburn Exp $
+# $Id: response.pm,v 1.230 2011/12/15 01:21:28 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -26,11 +26,36 @@
# http://www.lon-capa.org/
#
+=pod
+
+=head1 NAME
+
+Apache::response.pm
+
+=head1 SYNOPSIS
+
+This is part of the LearningOnline Network with CAPA project
+described at http://www.lon-capa.org.
+
+
+=head1 NOTABLE SUBROUTINES
+
+=over
+
+=item
+
+=back
+
+=cut
+
+
package Apache::response;
use strict;
use Apache::lonlocal;
use Apache::lonnet;
+use Apache::inputtags();
use Apache::lonmaxima();
+use Apache::lonr();
BEGIN {
&Apache::lonxml::register('Apache::response',('responseparam','parameter','dataresponse','customresponse','mathresponse'));
@@ -112,9 +137,10 @@ sub poprandomnumber {
}
sub setrandomnumber {
- my ($ignore_id2) = @_;
- my $rndseed;
- $rndseed=&Apache::structuretags::setup_rndseed();
+ my ($ignore_id2,$target,$rndseed) = @_;
+ if (!defined($rndseed)) {
+ $rndseed=&Apache::structuretags::setup_rndseed(undef,$target);
+ }
if (!defined($rndseed)) { $rndseed=&Apache::lonnet::rndseed(); }
&Apache::lonxml::debug("randseed $rndseed");
# $rndseed=unpack("%32i",$rndseed);
@@ -154,7 +180,7 @@ sub setrandomnumber {
} else {
($rndmod,$rndmod2)=&Apache::lonnet::digest("$id1,$id2");
}
-
+ $Apache::lonhomework::results{'resource.'.$id1.'.rawrndseed'}=$rndseed;
if ($rndseed =~/([,:])/) {
my $char=$1;
use integer;
@@ -171,6 +197,7 @@ sub setrandomnumber {
}
}
&Apache::lonxml::debug("randseed $rndmod $rndseed");
+ $Apache::lonhomework::results{'resource.'.$id1.'.rndseed'}=$rndseed;
&Apache::lonnet::setup_random_from_rndseed($rndseed);
return '';
}
@@ -216,35 +243,26 @@ sub meta_stores_write {
">\n";
}
+=pod
+
+=item mandatory_part_meta()
+
+Autogenerate metadata for mandatory
+input (from RAT or lonparmset) and
+output (to lonspreadsheet)
+of each part
+
+Note: responseid-specific data 'submission' and 'awarddetail'
+not available to spreadsheet -> skip here
+
+=cut
+
+
sub mandatory_part_meta {
-#
-# Autogenerate metadata for mandatory
-# input (from RAT or lonparmset) and
-# output (to lonspreadsheet)
-# of each part
-#
- return
-# &meta_parameter_write('opendate','date_start','',
-# 'Opening Date').
-# &meta_parameter_write('duedate','date_end','',
-# 'Due Date').
-# &meta_parameter_write('answerdate','date_start','',
-# 'Show Answer Date').
-# &meta_parameter_write('weight','int_zeropos','',
-# 'Available Points').
-# &meta_parameter_write('maxtries','int_pos','',
-# 'Maximum Number of Tries').
- &meta_package_write('part').
- &meta_stores_write('solved','string',
- 'Problem Status').
- &meta_stores_write('tries','int_zeropos',
- 'Number of Attempts').
- &meta_stores_write('awarded','float',
- 'Partial Credit Factor');
-#
-# Note: responseid-specific data 'submission' and 'awarddetail'
-# not available to spreadsheet -> skip here
-#
+ return &meta_package_write('part').
+ &meta_stores_write('solved','string','Problem Status').
+ &meta_stores_write('tries','int_zeropos','Number of Attempts').
+ &meta_stores_write('awarded','float','Partial Credit Factor');
}
sub meta_part_order {
@@ -265,32 +283,51 @@ sub meta_response_order {
}
sub check_for_previous {
- my ($curresponse,$partid,$id,$last) = @_;
+ my ($curresponse,$partid,$id,$last,$type) = @_;
my %previous;
$previous{'used'} = 0;
+ my $questiontype = $Apache::lonhomework::type;
+ my $curr_rndseed = $env{'form.'.$partid.'.rndseed'};
foreach my $key (sort(keys(%Apache::lonhomework::history))) {
- if ($key =~ /resource\.$partid\.$id\.submission$/) {
+ if ($key =~ /resource\.\Q$partid\E\.\Q$id\E\.submission$/) {
if ( $last && $key =~ /^(\d+):/ ) {
next if ($1 >= $last);
}
&Apache::lonxml::debug("Trying $key");
my $pastresponse=$Apache::lonhomework::history{$key};
if ($pastresponse eq $curresponse) {
- $previous{'used'} = 1;
my $history;
if ( $key =~ /^(\d+):/ ) {
- $history=$1;
+ $history=$1;
+ next if ((($questiontype eq 'randomizetry') ||
+ ($Apache::lonhomework::history{"$history:resource.$partid.type"} eq 'randomizetry')) &&
+ ($curr_rndseed ne $Apache::lonhomework::history{"$history:resource.$partid.rndseed"}));
$previous{'award'} = $Apache::lonhomework::history{"$history:resource.$partid.$id.awarddetail"};
$previous{'last'}='0';
push(@{ $previous{'version'} },$history);
} else {
+ next if ((($questiontype eq 'randomizetry') ||
+ ($Apache::lonhomework::history{"resource.$partid.type"} eq 'randomizetry')) &&
+ ($curr_rndseed ne $Apache::lonhomework::history{"resource.$partid.rndseed"}));
$previous{'award'} = $Apache::lonhomework::history{"resource.$partid.$id.awarddetail"};
$previous{'last'}='1';
}
+ $previous{'used'} = 1;
if (! $previous{'award'} ) { $previous{'award'} = 'UNKNOWN'; }
if ($previous{'award'} eq 'INTERNAL_ERROR') { $previous{'used'}=0; }
&Apache::lonxml::debug("got a match :$previous{'award'}:$previous{'used'}:");
- }
+ } elsif ($type eq 'ci') {
+ if (lc($pastresponse) eq lc($curresponse)) {
+ if ($key =~ /^(\d+):/) {
+ my $history = $1;
+ next if (($questiontype eq 'randomizetry') &&
+ ($curr_rndseed ne $Apache::lonhomework::history{"$history:resource.$partid.rndseed"}));
+ push (@{$previous{'versionci'}},$history);
+ $previous{'awardci'} = $Apache::lonhomework::history{"$history:resource.$partid.$id.awarddetail"};
+ $previous{'usedci'} = 1;
+ }
+ }
+ }
}
}
&Apache::lonhomework::showhash(%previous);
@@ -303,7 +340,10 @@ sub handle_previous {
if ($$previous{'last'}) {
push(@Apache::inputtags::previous,'PREVIOUSLY_LAST');
push(@Apache::inputtags::previous_version,$$previous{'version'});
- } elsif ($Apache::lonhomework::type ne 'survey') {
+ } elsif (($Apache::lonhomework::type ne 'survey') &&
+ ($Apache::lonhomework::type ne 'surveycred') &&
+ ($Apache::lonhomework::type ne 'anonsurvey') &&
+ ($Apache::lonhomework::type ne 'anonsurveycred')) {
push(@Apache::inputtags::previous,'PREVIOUSLY_USED');
push(@Apache::inputtags::previous_version,$$previous{'version'});
}
@@ -384,7 +424,7 @@ sub start_customresponse {
} elsif ($target eq 'edit') {
$result.=&Apache::edit::tag_start($target,$token);
$result.=&Apache::edit::text_arg('String to display for answer:',
- 'answerdisplay',$token);
+ 'answerdisplay',$token,'50');
$result.=&Apache::edit::end_row().&Apache::edit::start_spanning_row();
} elsif ($target eq 'modified') {
my $constructtag;
@@ -424,7 +464,7 @@ sub end_customresponse {
${$safeeval->varglob('LONCAPA::customresponse_submission')}=
$response;
- my $award = &Apache::run::run('{ my $submission=$LONCAPA::customresponse_submission;'.$Apache::response::custom_answer[-1].'}',$safeeval);
+ my ($award,$score) = &Apache::run::run('{ my $submission=$LONCAPA::customresponse_submission;'.$Apache::response::custom_answer[-1].'}',$safeeval);
if (!&Apache::inputtags::valid_award($award)) {
$error = $award;
$award = 'ERROR';
@@ -432,6 +472,9 @@ sub end_customresponse {
&Apache::response::handle_previous(\%previous,$award);
$Apache::lonhomework::results{"resource.$part.$id.awarddetail"}=
$award;
+ if ($award eq 'ASSIGNED_SCORE') {
+ $Apache::lonhomework::results{"resource.$part.$id.awarded"}=1.0*$score;
+ }
if ($error) {
$Apache::lonhomework::results{"resource.$part.$id.awardmsg"}=
$error;
@@ -448,12 +491,12 @@ sub end_customresponse {
$result .= &Apache::response::answer_footer('customresponse');
}
if ($target eq 'web') {
- &setup_prior_tries_hash(\&format_prior_response_math);
+ &setup_prior_tries_hash(\&format_prior_response_custom);
}
if ($target eq 'grade' || $target eq 'web' || $target eq 'answer' ||
$target eq 'tex' || $target eq 'analyze') {
- &Apache::lonxml::increment_counter(&Apache::response::repetition(),
- "$part.$id");
+ my $repetition = &repetition();
+ &Apache::lonxml::increment_counter($repetition,"$part.$id");
if ($target eq 'analyze') {
$Apache::lonhomework::analyze{"$part.$id.type"} = 'customresponse';
&Apache::lonhomework::set_bubble_lines();
@@ -469,6 +512,9 @@ sub end_customresponse {
sub format_prior_response_custom {
my ($mode,$answer) =@_;
+ if (ref($answer) eq 'ARRAY') {
+ $answer = '('.join(', ', @{ $answer }).')';
+ }
return ''.
&HTML::Entities::encode($answer,'"<>&').'';
}
@@ -491,10 +537,10 @@ sub start_mathresponse {
} elsif ($target eq 'edit') {
$result.=&Apache::edit::tag_start($target,$token);
$result.=&Apache::edit::text_arg('String to display for answer:',
- 'answerdisplay',$token);
+ 'answerdisplay',$token,'50');
$result.=&Apache::edit::select_arg('Algebra System:',
'cas',
- ['maxima'],
+ ['maxima','R'],
$token);
$result.=&Apache::edit::text_arg('Argument Array:',
'args',$token).
@@ -524,14 +570,14 @@ sub edit_mathresponse_button {
# my $helplink=&Apache::loncommon::help_open_topic('Formula_Editor');
my $iconpath=$Apache::lonnet::perlvar{'lonIconsURL'};
return(<
+
-
+
ENDFORMULABUTTON
}
@@ -558,6 +604,12 @@ sub end_mathresponse {
$award=&Apache::lonmaxima::maxima_run($Apache::response::custom_answer[-1],$response,$args,
&Apache::lonxml::get_param('libraries',$parstack,$safeeval));
}
+ if ($cas eq 'R') {
+ my $args = [&Apache::lonxml::get_param_var('args',$parstack,$safeeval)];
+ $award=&Apache::lonr::r_run($Apache::response::custom_answer[-1],$response,$args,
+ &Apache::lonxml::get_param('libraries',$parstack,$safeeval));
+ }
+
if (!&Apache::inputtags::valid_award($award)) {
$error = $award;
$award = 'ERROR';
@@ -883,12 +935,16 @@ sub showallfoils {
}
}
if ($Apache::lonhomework::type eq 'survey') { return 1; }
+ if ($Apache::lonhomework::type eq 'surveycred') { return 1; }
+ if ($Apache::lonhomework::type eq 'anonsurvey') { return 1; }
+ if ($Apache::lonhomework::type eq 'anonsurveycred') { return 1; }
+
return 0;
}
=pod
-=item &getresponse($offset,$resulttype);
+=item &getresponse();
Retreives the current submitted response, helps out in the case of
scantron mode.
@@ -934,29 +990,35 @@ sub getresponse {
my $id = $Apache::inputtags::response[-1];
my $line;
+ my $startline = $env{'form.scantron_questnum_start.'.$part.'.'.$id};
+ if (!$startline) {
+ $startline = $Apache::lonxml::counter;
+ }
for ($line = 0; $line < $lines; $line++) {
- my $theline = $Apache::lonxml::counter+$offset-1+$line;
+ my $theline = $startline+$offset-1+$line;
$response = $env{"scantron.$theline.answer"};
if ((defined($response)) && ($response ne "") && ($response ne " ")) {
last;
}
-
+
}
# save bubbled letter for later
$Apache::lonhomework::results{"resource.$part.$id.scantron"}.=
$response;
if ($resulttype ne 'letter') {
- if ($resulttype eq 'A is 1') {
- $response = $let_to_num{$response}+1;
- } else {
- $response = $let_to_num{$response};
+ $response = $let_to_num{$response};
+ if ($resulttype eq 'A is 1') {
+ if ($response ne "") {
+ $response = $response+1;
+ }
}
if ($response ne "") {
$response += $line * $bubbles_per_line;
}
} else {
if ($response ne "") {
+ my $raw = $response;
$response = chr(ord($response) + $line * $bubbles_per_line);
}
}
@@ -976,8 +1038,17 @@ sub getresponse {
=item &repetition();
-Returns the number of lines that are required to encode the weight.
-(Currently expects that there are 10 bubbles per line)
+In scalar context:
+
+returns: the number of lines that are required to encode the weight.
+(Default is for 10 bubbles per bubblesheet item; other (integer)
+values can be specified by using a custom Bubblesheet format file
+with an eighteenth entry (BubblesPerRow) set to the integer
+appropriate for the bubblesheets which will be used to assign weights.
+
+In array context:
+
+returns: number of lines required to encode weight, and bubbles/line.
=cut
@@ -985,14 +1056,30 @@ sub repetition {
my $id = $Apache::inputtags::part;
my $weight = &Apache::lonnet::EXT("resource.$id.weight");
if (!defined($weight) || ($weight eq '')) { $weight=1; }
- my $repetition = int($weight/10);
- if ($weight % 10 != 0) { $repetition++; }
+ my $bubbles_per_row;
+ if (($env{'form.bubbles_per_row'} =~ /^\d+$/) &&
+ ($env{'form.bubbles_per_row'} > 0)) {
+ $bubbles_per_row = $env{'form.bubbles_per_row'};
+ } else {
+ $bubbles_per_row = 10;
+ }
+ my $denominator = $bubbles_per_row;
+ if (($env{'form.scantron_lastbubblepoints'} == 0) &&
+ ($bubbles_per_row > 1)) {
+ $denominator = $bubbles_per_row - 1;
+ }
+ my $repetition = int($weight/$denominator);
+ if ($weight % $denominator != 0) { $repetition++; }
+ if (wantarray) {
+ return ($repetition,$bubbles_per_row);
+ }
return $repetition;
+
}
=pod
-=item &scored_response($part_id,$response_id);
+=item &scored_response();
Sets the results hash elements
@@ -1017,11 +1104,24 @@ Arguments
sub scored_response {
my ($part,$id)=@_;
my $repetition=&repetition();
+ my $bubbles_per_row;
+ if (($env{'form.bubbles_per_row'} =~ /^\d+$/) &&
+ ($env{'form.bubbles_per_row'} > 0)) {
+ $bubbles_per_row = $env{'form.bubbles_per_row'};
+ } else {
+ $bubbles_per_row = 10;
+ }
my $score=0;
for (my $i=0;$i<$repetition;$i++) {
- # A is 1, B is 2, etc. (get response return 0-9 and then we add 1)
+ # A is 1, B is 2, etc.
my $increase=&Apache::response::getresponse($i+1);
- if ($increase ne '') { $score+=$increase+1; }
+ unless (($increase == $bubbles_per_row-1) &&
+ ($env{'form.scantron_lastbubblepoints'} == 0)) {
+ # (get response return 0-9 and then we add 1)
+ if ($increase ne '') {
+ $score+=$increase+1;
+ }
+ }
}
my $weight = &Apache::lonnet::EXT("resource.$part.weight");
if (!defined($weight) || $weight eq '' || $weight eq 0) { $weight = 1; }
@@ -1033,7 +1133,7 @@ sub scored_response {
}
sub whichorder {
- my ($max,$randomize,$showall,$hash)=@_;
+ my ($max,$randomize,$showall,$hash,$rndseed)=@_;
#&Apache::lonxml::debug("man $max randomize $randomize");
if (!defined(@{ $$hash{'names'} })) { return; }
my @names = @{ $$hash{'names'} };
@@ -1087,9 +1187,15 @@ sub show_answer {
my $part = $Apache::inputtags::part;
my $award = $Apache::lonhomework::history{"resource.$part.solved"};
my $status = $Apache::inputtags::status[-1];
- return ( ($award =~ /^correct/
- && &Apache::lonhomework::show_problem_status())
- || $status eq "SHOW_ANSWER");
+ my $canshow = 0;
+ if ($award =~ /^correct/) {
+ if (($Apache::lonhomework::history{"resource.$part.awarded"} >= 1) ||
+ (&Apache::lonnet::EXT("resource.$part.retrypartial") !~/^1|on|yes$/)) {
+ $canshow = 1;
+ }
+ }
+ return (($canshow && &Apache::lonhomework::show_problem_status())
+ || $status eq "SHOW_ANSWER");
}
sub analyze_store_foilgroup {
@@ -1149,17 +1255,20 @@ sub pick_foil_for_concept {
$Apache::response::conceptgroup{'names'};
}
-#------------------------------------------------------------
-#
-# Get a parameter associated with a problem.
-# Parameters:
-# $id - the id of the paramater, either a part id,
-# or a partid and responspe id joined by _
-# $name - Name of the parameter to fetch
-# $default - Default value for the paramter.
-#
-#
-#
+
+=pod
+
+=item get_response_param()
+
+Get a parameter associated with a problem.
+Parameters:
+ $id - the id of the paramater, either a part id,
+ or a partid and responspe id joined by _
+ $name - Name of the parameter to fetch
+ $default - Default value for the paramter.
+
+=cut
+
sub get_response_param {
my ($id,$name,$default)=@_;
my $parameter;
@@ -1220,14 +1329,21 @@ sub add_to_gradingqueue {
}
}
-# basically undef and 0 (both false) mean that they still have work to do
-# and all true values mean that they can't do any more work
-#
-# a return of undef means it is unattempted
-# a return of 0 means it is attmpted and wrong but still has tries
-# a return of 1 means it is marked correct
-# a return of 2 means they have exceed maximum number of tries
-# a return of 3 means it after the answer date
+=pod
+
+=item check_status()
+
+basically undef and 0 (both false) mean that they still have work to do
+and all true values mean that they can't do any more work
+
+ a return of undef means it is unattempted
+ a return of 0 means it is attmpted and wrong but still has tries
+ a return of 1 means it is marked correct
+ a return of 2 means they have exceed maximum number of tries
+ a return of 3 means it after the answer date
+
+=cut
+
sub check_status {
my ($id)=@_;
if (!defined($id)) { $id=$Apache::inputtags::part; }
@@ -1252,7 +1368,7 @@ sub check_status {
=pod
-=item setup_prior_tries_hash($func,$data)
+=item setup_prior_tries_hash()
Foreach each past .submission $func is called with 3 arguments
- the mode to set things up for (currently always 'grade')
@@ -1263,25 +1379,40 @@ sub check_status {
- scalars that are other elements of the history hash to pass to $func
- ref to data to be passed untouched to $func
+ $questiontype is the questiontype (currently only passed in if
+ randomizebytry.
+
=cut
sub setup_prior_tries_hash {
- my ($func,$data) = @_;
+ my ($func,$data,$questiontype) = @_;
my $part = $Apache::inputtags::part;
- my $id = $Apache::inputtags::response[-1];
+ my $id = $Apache::inputtags::response[-1];
foreach my $i (1..$Apache::lonhomework::history{'version'}) {
- my $sub_key = "$i:resource.$part.$id.submission";
+ my $partprefix = "$i:resource.$part";
+ my $sub_key = "$partprefix.$id.submission";
next if (!exists($Apache::lonhomework::history{$sub_key}));
+ my $type_key = "$partprefix.type";
+ my $type = $Apache::lonhomework::history{$type_key};
my @other_data;
- foreach my $datum (@{ $data }) {
- if (ref($datum)) {
- push(@other_data,$datum);
- } else {
- my $info_key = "$i:resource.$part.$id.$datum";
- push(@other_data,$Apache::lonhomework::history{$info_key});
+ if (ref($data) eq 'ARRAY') {
+ foreach my $datum (@{ $data }) {
+ if (ref($datum)) {
+ push(@other_data,$datum);
+ } else {
+ my $info_key = "$i:resource.$part.$id.$datum";
+ push(@other_data,$Apache::lonhomework::history{$info_key});
+ }
}
- }
-
+ }
+ if ($questiontype eq 'randomizetry') {
+ my $order_key = "$partprefix.$id.foilorder";
+ my @whichopts = &Apache::lonnet::str2array($Apache::lonhomework::history{$order_key});
+ if (@whichopts > 0) {
+ shift(@other_data);
+ unshift(@other_data,\@whichopts);
+ }
+ }
my $output =
&$func('grade',
$Apache::lonhomework::history{$sub_key},
@@ -1295,3 +1426,6 @@ sub setup_prior_tries_hash {
1;
__END__
+=pod
+
+=cut