--- loncom/interface/lonprintout.pm 2005/07/25 10:27:51 1.379 +++ loncom/interface/lonprintout.pm 2005/08/16 10:25:15 1.386 @@ -1,7 +1,7 @@ # The LearningOnline Network # Printout # -# $Id: lonprintout.pm,v 1.379 2005/07/25 10:27:51 foxr Exp $ +# $Id: lonprintout.pm,v 1.386 2005/08/16 10:25:15 foxr Exp $ # # Copyright Michigan State University Board of Trustees # @@ -44,6 +44,124 @@ use Apache::lonlocal; my $resources_printed = ''; +# +# Convert a numeric code to letters +# +sub num_to_letters { + my ($num) = @_; + my @nums= split('',$num); + my @num_to_let=('A'..'Z'); + my $word; + foreach my $digit (@nums) { $word.=$num_to_let[$digit]; } + return $word; +} +# Convert a letter code to numeric. +# +sub letters_to_num { + my ($letters) = @_; + my @letters = split('', uc($letters)); + my %substitution; + my $digit = 0; + foreach my $letter ('A'..'J') { + $substitution{$letter} = $digit; + $digit++; + } + # The substitution is done as below to preserve leading + # zeroes which are needed to keep the code size exact + # + my $result =""; + foreach my $letter (@letters) { + $result.=$substitution{$letter}; + } + return $result; +} + +# Determine if a code is a valid numeric code. Valid +# numeric codes must be comprised entirely of digits and +# have a correct number of digits. +# +# Parameters: +# value - proposed code value. +# num_digits - Number of digits required. +# +sub is_valid_numeric_code { + my ($value, $num_digits) = @_; + # Remove leading/trailing whitespace; + $value =~ s/^\s*//; + $value =~ s/\s*$//; + + # All digits? + if ($value =~ /^[0-9]+$/) { + return "Numeric code $value has invalid characters - must only be digits"; + } + if (length($value) != $num_digits) { + return "Numeric code $value incorrect number of digits (correct = $num_digits)"; + } + return undef; +} +# Determines if a code is a valid alhpa code. Alpha codes +# are ciphers that map [A-J,a-j] -> 0..9 0..9. +# They also have a correct digit count. +# Parameters: +# value - Proposed code value. +# num_letters - correct number of letters. +# Note: +# leading and trailing whitespace are ignored. +# +sub is_valid_alpha_code { + my ($value, $num_letters) = @_; + + # strip leading and trailing spaces. + + $value =~ s/^\s*//g; + $value =~ s/\s*$//g; + + # All alphas in the right range? + if ($value !~ /^[A-J,a-j]+$/) { + return "Invalid letter code $value must only contain A-J"; + } + if (length($value) != $num_letters) { + return "Letter code $value has incorrect number of letters (correct = $num_letters)"; + } + return undef; +} + +# Determine if a code entered by the user in a helper is valid. +# valid depends on the code type and the type of code selected. +# The type of code selected can either be numeric or +# Alphabetic. If alphabetic, the code, in fact is a simple +# substitution cipher for the actual numeric code: 0->A, 1->B ... +# We'll be nice and be case insensitive for alpha codes. +# Parameters: +# code_value - the value of the code the user typed in. +# code_option - The code type selected from the set in the scantron format +# table. +# Returns: +# undef - The code is valid. +# other - An error message indicating what's wrong. +# +sub is_code_valid { + my ($code_value, $code_option) = @_; + my ($code_type, $code_length) = ('letter', 6); # defaults. + open(FG, $Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab'); + foreach my $line () { + my ($name, $type, $length) = (split(/:/, $line))[0,2,4]; + if($name eq $code_option) { + $code_length = $length; + if($type eq 'number') { + $code_type = 'number'; + } + } + } + my $valid; + if ($code_type eq 'number') { + return &is_valid_numeric_code($code_value, $code_length); + } else { + return &is_valid_alpha_code($code_value, $code_length); + } + +} + # Compare two students by name. The students are in the form # returned by the helper: # user:domain:section:last, first:status @@ -998,13 +1116,15 @@ ENDPART if((($helper->{'VARS'}->{'ANSWER_TYPE'} eq 'no') || ($helper->{'VARS'}->{'ANSWER_TYPE'} eq 'only')) && ($urlp=~/\.(problem|exam|quiz|assess|survey|form|library|page)$/)) { - $form{'grade_target'}='answer'; - $form{'answer_output_mode'}='tex'; - $form{'rndseed'}=$rndseed; - $form{'problem_split'}=$parmhash{'problem_stream_switch'}; + # Don't permanently modify %$form... + my %answerform = %form; + $answerform{'grade_target'}='answer'; + $answerform{'answer_output_mode'}='tex'; + $answerform{'rndseed'}=$rndseed; + $answerform{'problem_split'}=$parmhash{'problem_stream_switch'}; if ($urlp=~/\/res\//) {$env{'request.state'}='published';} $resources_printed .= $urlp.':'; - my $answer=&Apache::lonnet::ssi($urlp,%form); + my $answer=&Apache::lonnet::ssi($urlp,%answerform); if ($helper->{'VARS'}->{'ANSWER_TYPE'} eq 'no') { $texversion=~s/(\\keephidden{ENDOFPROBLEM})/$answer$1/; } else { @@ -1105,10 +1225,12 @@ ENDPART my $current_counter=$env{'form.counter'}; if(($helper->{'VARS'}->{'ANSWER_TYPE'} eq 'no') || ($helper->{'VARS'}->{'ANSWER_TYPE'} eq 'only')) { - $form{'grade_target'}='answer'; - $form{'answer_output_mode'}='tex'; + # Don't permanently pervert the %form hash + my %answerform = %form; + $answerform{'grade_target'}='answer'; + $answerform{'answer_output_mode'}='tex'; $resources_printed .= $urlp.':'; - my $answer=&Apache::lonnet::ssi($urlp,%form); + my $answer=&Apache::lonnet::ssi($urlp,%answerform); &Apache::lonnet::appenv(('form.counter' => $current_counter)); if ($helper->{'VARS'}->{'ANSWER_TYPE'} eq 'no') { $texversion=~s/(\\keephidden{ENDOFPROBLEM})/$answer$1/; @@ -1240,23 +1362,51 @@ ENDPART my $num_todo=$helper->{'VARS'}->{'NUMBER_TO_PRINT_TOTAL'}; my $code_name=$helper->{'VARS'}->{'ANON_CODE_STORAGE_NAME'}; my $old_name=$helper->{'VARS'}->{'REUSE_OLD_CODES'}; + my $single_code = $helper->{'VARS'}->{'SINGLE_CODE'}; + my $code_option=$helper->{'VARS'}->{'CODE_OPTION'}; + open(FH,$Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab'); + my ($code_type,$code_length)=('letter',6); + foreach my $line () { + my ($name,$type,$length) = (split(/:/,$line))[0,2,4]; + if ($name eq $code_option) { + $code_length=$length; + if ($type eq 'number') { $code_type = 'number'; } + } + } my %moreenv = ('textwidth' => &get_textwidth($helper,$LaTeXwidth)); $moreenv{'problem_split'} = $parmhash{'problem_stream_switch'}; my $seed=time+($$<<16)+($$); my @allcodes; if ($old_name) { - my %result=&Apache::lonnet::get('CODEs',[$old_name],$cdom,$cnum); + my %result=&Apache::lonnet::get('CODEs', + [$old_name,"type\0$old_name"], + $cdom,$cnum); + $code_type=$result{"type\0$old_name"}; @allcodes=split(',',$result{$old_name}); $num_todo=scalar(@allcodes); + } elsif ($single_code) { + + # If an alpha code have to convert to numbers so it can be + # converted back to letters again :-) + # + if ($code_type ne 'number') { + $single_code = &letters_to_num($single_code); + $num_todo = 1; + } + @allcodes = ($single_code); } else { my %allcodes; srand($seed); for (my $i=0;$i<$num_todo;$i++) { - $moreenv{'CODE'}=&get_CODE(\%allcodes,$i,$seed,'6'); + $moreenv{'CODE'}=&get_CODE(\%allcodes,$i,$seed,$code_length, + $code_type); } if ($code_name) { &Apache::lonnet::put('CODEs', - {$code_name =>join(',',keys(%allcodes))}, + { + $code_name =>join(',',keys(%allcodes)), + "type\0$code_name" => $code_type + }, $cdom,$cnum); } @allcodes=keys(%allcodes); @@ -1272,7 +1422,11 @@ ENDPART my $count=0; foreach my $code (sort(@allcodes)) { my $file_num=int($count/$number_per_page); - $moreenv{'CODE'}=&num_to_letters($code); + if ($code_type eq 'number') { + $moreenv{'CODE'}=$code; + } else { + $moreenv{'CODE'}=&num_to_letters($code); + } my ($output,$fullname, $printed)= &print_resources($r,$helper,'anonymous',$type,\%moreenv, \@master_seq,$flag_latex_header_remove, @@ -1312,12 +1466,14 @@ ENDPART my $texversion=&Apache::lonnet::ssi($urlp,%form); if(($helper->{'VARS'}->{'ANSWER_TYPE'} eq 'no') || ($helper->{'VARS'}->{'ANSWER_TYPE'} eq 'only')) { - $form{'grade_target'}='answer'; - $form{'answer_output_mode'}='tex'; - $form{'latex_type'}=$helper->{'VARS'}->{'LATEX_TYPE'}; - $form{'rndseed'}=$rndseed; + # Don't permanently pervert %form: + my %answerform = %form; + $answerform{'grade_target'}='answer'; + $answerform{'answer_output_mode'}='tex'; + $answerform{'latex_type'}=$helper->{'VARS'}->{'LATEX_TYPE'}; + $answerform{'rndseed'}=$rndseed; $resources_printed .= $urlp.':'; - my $answer=&Apache::lonnet::ssi($urlp,%form); + my $answer=&Apache::lonnet::ssi($urlp,%answerform); if ($helper->{'VARS'}->{'ANSWER_TYPE'} eq 'no') { $texversion=~s/(\\keephidden{ENDOFPROBLEM})/$answer$1/; } else { @@ -1463,24 +1619,20 @@ $r->print(<{'VARS'}->{'ANSWER_TYPE'} eq 'no') || ($helper->{'VARS'}->{'ANSWER_TYPE'} eq 'only')) { - $moreenv->{'answer_output_mode'}='tex'; - $moreenv->{'latex_type'}=$helper->{'VARS'}->{'LATEX_TYPE'}; - my $ansrendered = &Apache::loncommon::get_student_answers($curresline,$username,$userdomain,$env{'request.course.id'},%{$moreenv}); + # Use a copy of the hash so we don't pervert it on future loop passes. + my %answerenv = %{$moreenv}; + $answerenv{'answer_output_mode'}='tex'; + $answerenv{'latex_type'}=$helper->{'VARS'}->{'LATEX_TYPE'}; + my $ansrendered = &Apache::loncommon::get_student_answers($curresline,$username,$userdomain,$env{'request.course.id'},%answerenv); &Apache::lonnet::appenv(('form.counter' => $current_counter)); if ($helper->{'VARS'}->{'ANSWER_TYPE'} eq 'no') { $rendered=~s/(\\keephidden{ENDOFPROBLEM})/$ansrendered$1/; @@ -1931,8 +2085,22 @@ CHOOSE_STUDENTS my $namechoice=''; foreach my $name (sort {uc($a) cmp uc($b)} @names) { if ($name =~ /^error: 2 /) { next; } + if ($name =~ /^type\0/) { next; } $namechoice.=''.$name.''; } + open(FH,$Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab'); + my $codechoice=''; + foreach my $line () { + my ($name,$description,$code_type,$code_length)= + (split(/:/,$line))[0,1,2,4]; + if ($code_length > 0 && + $code_type =~/^(letter|number|-1)/) { + $codechoice.=''.$description.''; + } + } + if ($codechoice eq '') { + $codechoice='Default'; + } &Apache::lonxml::xmlparse($r, 'helper', < PAGESIZE @@ -1941,16 +2109,36 @@ CHOOSE_STUDENTS if (((\$helper->{'VARS'}{'NUMBER_TO_PRINT_TOTAL'}+0) < 1) && - !\$helper->{'VARS'}{'REUSE_OLD_CODES'}) { + !\$helper->{'VARS'}{'REUSE_OLD_CODES'} && + !\$helper->{'VARS'}{'SINGLE_CODE'}) { return "You need to specify the number of assignments to print"; } return undef; + Value of CODE to print? + + + if(!\$helper->{'VARS'}{'NUMBER_TO_PRINT_TOTAL'} && + !\$helper->{'VARS'}{'REUSE_OLD_CODES'}) { + return &Apache::lonprintout::is_code_valid(\$helper->{'VARS'}{'SINGLE_CODE'}, + \$helper->{'VARS'}{'CODE_OPTION'}); + } else { + return undef; # Other forces control us. + } + + + Names to store the CODEs under for later: + + Bubble sheet type: + + + $codechoice +
Reprint a set of saved CODEs: @@ -2008,16 +2196,36 @@ CHOOSE_STUDENTS1 if (((\$helper->{'VARS'}{'NUMBER_TO_PRINT_TOTAL'}+0) < 1) && - !\$helper->{'VARS'}{'REUSE_OLD_CODES'}) { + !\$helper->{'VARS'}{'REUSE_OLD_CODES'} && + !\$helper->{'VARS'}{'SINGLE_CODE'}) { return "You need to specify the number of assignments to print"; } return undef; + Value of CODE to print? + + + if(!\$helper->{'VARS'}{'NUMBER_TO_PRINT_TOTAL'} && + !\$helper->{'VARS'}{'REUSE_OLD_CODES'}) { + return &Apache::lonprintout::is_code_valid(\$helper->{'VARS'}{'SINGLE_CODE'}, + \$helper->{'VARS'}{'CODE_OPTION'}); + } else { + return undef; # Other forces control us. + } + + + Names to store the CODEs under for later: + + Bubble sheet type: + + + $codechoice +
Reprint a set of saved CODEs: