--- loncom/interface/lonprintout.pm 2005/07/26 10:50:32 1.380 +++ loncom/interface/lonprintout.pm 2005/12/06 09:41:02 1.401 @@ -1,7 +1,7 @@ -# The LearningOnline Network +# The LearningOnline Network # Printout # -# $Id: lonprintout.pm,v 1.380 2005/07/26 10:50:32 foxr Exp $ +# $Id: lonprintout.pm,v 1.401 2005/12/06 09:41:02 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -42,7 +42,125 @@ use Apache::lonratedt; use POSIX qw(strftime); use Apache::lonlocal; -my $resources_printed = ''; +my %perm; + +# +# 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*//g; + $value =~ s/\s*$//g; + + # 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: @@ -315,6 +433,7 @@ sub character_chart { $result =~ s/&(\#252|uuml);/\\\"{u}/g; $result =~ s/&(\#253|yacute);/\\\'{y}/g; $result =~ s/&(\#255|yuml);/\\\"{y}/g; + $result =~ s/&\#295;/\\ensuremath\{\\hbar\}/g; $result =~ s/&\#952;/\\ensuremath\{\\theta\}/g; #Greek Alphabet $result =~ s/&(alpha|\#945);/\\ensuremath\{\\alpha\}/g; @@ -390,6 +509,7 @@ sub character_chart { $result =~ s/&(prod|\#8719);/\\ensuremath\{\\prod\}/g; $result =~ s/&(sum|\#8721);/\\ensuremath\{\\sum\}/g; $result =~ s/&(minus|\#8722);/\\ensuremath\{-\}/g; + $result =~ s/–/\\ensuremath\{-\}/g; $result =~ s/&(lowast|\#8727);/\\ensuremath\{*\}/g; $result =~ s/&(radic|\#8730);/\\ensuremath\{\\surd\}/g; $result =~ s/&(prop|\#8733);/\\ensuremath\{\\propto\}/g; @@ -698,12 +818,13 @@ sub IndexCreation { sub print_latex_header { my $mode=shift; my $output='\documentclass[letterpaper]{article}'; - if (($mode eq 'batchmode') || (!$env{'request.role.adv'})) { + if (($mode eq 'batchmode') || (!$perm{'pav'})) { $output.='\batchmode'; } $output.='\newcommand{\keephidden}[1]{}\renewcommand{\deg}{$^{\circ}$}'."\n". '\usepackage{longtable}\usepackage{textcomp}\usepackage{makeidx}'."\n". '\usepackage[dvips]{graphicx}\usepackage{epsfig}'."\n". + '\usepackage{wrapfig}'. '\usepackage{picins}\usepackage{calc}'."\n". '\newenvironment{choicelist}{\begin{list}{}{\setlength{\rightmargin}{0in}'."\n". '\setlength{\leftmargin}{0.13in}\setlength{\topsep}{0.05in}'."\n". @@ -785,6 +906,49 @@ sub unsupported { # +# List of recently generated print files +# +sub recently_generated { + my $r=shift; + my $prtspool=$r->dir_config('lonPrtDir'); + my $zip_result; + my $pdf_result; + opendir(DIR,$prtspool); + + my @files = + grep(/^$env{'user.name'}_$env{'user.domain'}_printout_(\d+)_.*\.(pdf|zip)$/,readdir(DIR)); + closedir(DIR); + + @files = sort { + my ($actime) = (stat($prtspool.'/'.$a))[10]; + my ($bctime) = (stat($prtspool.'/'.$b))[10]; + return $bctime <=> $actime; + } (@files); + + foreach my $filename (@files) { + my ($ext) = ($filename =~ m/(pdf|zip)$/); + my ($cdev,$cino,$cmode,$cnlink, + $cuid,$cgid,$crdev,$csize, + $catime,$cmtime,$cctime, + $cblksize,$cblocks)=stat($prtspool.'/'.$filename); + my $result="". + &mt('Generated [_1] ([_2] bytes)', + &Apache::lonlocal::locallocaltime($cctime),$csize). + '
'; + if ($ext eq 'pdf') { $pdf_result .= $result; } + if ($ext eq 'zip') { $zip_result .= $result; } + } + if ($zip_result) { + $r->print('

'.&mt('Recently generated printout zip files')."

\n" + .$zip_result); + } + if ($pdf_result) { + $r->print('

'.&mt('Recently generated printouts')."

\n" + .$pdf_result); + } +} + +# # Retrieve the hash of page breaks. # # Inputs: @@ -808,6 +972,7 @@ sub get_page_breaks { sub output_data { my ($r,$helper,$rparmhash) = @_; my %parmhash = %$rparmhash; + my $resources_printed = ''; my $html=&Apache::lonxml::xmlbegin(); my $bodytag=&Apache::loncommon::bodytag('Preparing Printout'); $r->print(<{'VARS'}->{'probstatus'} eq 'exam') { + $form{'problemtype'}='exam'; + } $resources_printed .= $currentURL.':'; my $answer=&Apache::lonnet::ssi($currentURL,%form); if ($helper->{'VARS'}->{'ANSWER_TYPE'} eq 'no') { @@ -1244,23 +1412,56 @@ 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 $selected_code = $helper->{'VARS'}->{'CODE_SELECTED_FROM_LIST'}; + + 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 ($selected_code) { # Selection value is always numeric. + $num_todo = 1; + @allcodes = ($selected_code); + } elsif ($single_code) { + + $num_todo = 1; # Unconditionally one code to do. + # 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); + } + @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); @@ -1276,7 +1477,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, @@ -1450,7 +1655,7 @@ ENDPART 'cgi.'.$identifier.'.selection' => $selectionmade, 'cgi.'.$identifier.'.tableofcontents' => $helper->{'VARS'}->{'TABLE_CONTENTS'}, 'cgi.'.$identifier.'.tableofindex' => $helper->{'VARS'}->{'TABLE_INDEX'}, - 'cgi.'.$identifier.'.role' => $env{'request.role.adv'}, + 'cgi.'.$identifier.'.role' => $perm{'pav'}, 'cgi.'.$identifier.'.numberoffiles' => $#print_array, 'cgi.'.$identifier.'.studentnames' => $student_names, 'cgi.'.$identifier.'.backref' => $URLback,); @@ -1469,24 +1674,20 @@ $r->print(<{'VARS'}}) { @@ -1657,6 +1858,20 @@ sub addMessage { use Data::Dumper; +sub init_perm { + undef(%perm); + $perm{'pav'}=&Apache::lonnet::allowed('pav',$env{'request.course.id'}); + if (!$perm{'pav'}) { + $perm{'pav'}=&Apache::lonnet::allowed('pav', + $env{'request.course.id'}.'/'.$env{'request.course.sec'}); + } + $perm{'pfo'}=&Apache::lonnet::allowed('pav',$env{'request.course.id'}); + if (!$perm{'pfo'}) { + $perm{'pfo'}=&Apache::lonnet::allowed('pfo', + $env{'request.course.id'}.'/'.$env{'request.course.sec'}); + } +} + sub printHelper { my $r = shift; @@ -1700,21 +1915,27 @@ sub printHelper { $helper->declareVar("showallfoils"); # The page breaks can get loaded initially from the course environment: + # But we only do this in the initial state so that they are allowed to change. + # - if((!defined($env{"form.CURRENT_STATE"})) || - ($env{'form.CURRENT_STATE'} == "START")) { - $helper->{VARS}->{FINISHPAGE} = ""; # In case they did a back e.g. - } - - + $helper->{VARS}->{FINISHPAGE} = ''; &Apache::loncommon::restore_course_settings('print', {'pagebreaks' => 'scalar', 'lastprinttype' => 'scalar'}); - - if("$helper->{VARS}->{PRINT_TYPE}" eq "$env{'form.lastprinttype'}") { - $helper->{VARS}->{FINISHPAGE} = $env{'form.pagebreaks'}; + + if($helper->{VARS}->{PRINT_TYPE} eq $env{'form.lastprinttype'}) { + if (!defined ($env{"form.CURRENT_STATE"})) { + + $helper->{VARS}->{FINISHPAGE} = $env{'form.pagebreaks'}; + } else { + my $state = $env{"form.CURRENT_STATE"}; + if ($state eq "START") { + $helper->{VARS}->{FINISHPAGE} = $env{'form.pagebreaks'}; + } + } + } @@ -1779,9 +2000,6 @@ sub printHelper { } my $userCanSeeHidden = Apache::lonnavmaps::advancedUser(); - my $userPriviledged = ($env{'request.role'}=~m/^cc\./ or - $env{'request.role'}=~m/^in\./ or - $env{'request.role'}=~m/^ta\./); Apache::lonhelper::registerHelperTags(); @@ -1824,7 +2042,7 @@ sub printHelper { $helper->declareVar('SEQUENCE'); # Useful for debugging: Dump the help vars -# $r->print(Dumper($helper->{VARS})); +# $r->print(Dumper($helper->{VARS})); # $r->print($map); # If we're in a sequence... @@ -1866,9 +2084,9 @@ HELPERFRAGMENT &Apache::lonxml::xmlparse($r, 'helper', $helperFragment); } - # If the user is privileged, allow them to print all + # If the user has pfo (print for otheres) allow them to print all # problems and resources in the entier course, optionally for selected students - if ($userPriviledged && + if ($perm{'pfo'} && ($helper->{VARS}->{'postdata'}=~/\/res\// || $helper->{VARS}->{'postdata'}=~/\/(syllabus|smppg|aboutme|bulletinboard)$/)) { push @{$printChoices}, ['Problems from entire course', 'all_problems', 'ALL_PROBLEMS']; @@ -1939,8 +2157,43 @@ 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.''; } + + + my %all_codes = &Apache::lonnet::get('CODEs', + \@names, $cdom,$cnum); + my %code_values; + my @all_code_array; + foreach my $key (keys %all_codes) { + %code_values = &Apache::grades::get_codes($key, $cdom, $cnum); + foreach my $key (keys %code_values) { + push (@all_code_array, "$key"); + } + } + + my $code_selection = "\n"; + foreach my $code (sort {uc($a) cmp uc($b)} @all_code_array) { + my $choice = $code; + if ($code =~ /^[A-Z]+$/) { # Alpha code + $choice = &letters_to_num($code); + } + $code_selection .= ' '.$code."\n"; + } + 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 @@ -1949,16 +2202,44 @@ 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'} && + !\$helper->{'VARS'}{'CODE_SELECTED_FROM_LIST'}) { 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'} && + !\$helper->{'VARS'}{'CODE_SELECTED_FROM_LIST'}) { + return &Apache::lonprintout::is_code_valid(\$helper->{'VARS'}{'SINGLE_CODE'}, + \$helper->{'VARS'}{'CODE_OPTION'}); + } else { + return undef; # Other forces control us. + } + + + + Choose single code from list + + + $code_selection + + Names to store the CODEs under for later: + + Bubble sheet type: + + + $codechoice +
Reprint a set of saved CODEs: @@ -2016,16 +2297,44 @@ 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'} && + !\$helper->{'VARS'}{'CODE_SELECTED_FROM_LIST'}) { 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'} && + !\$helper->{'VARS'}{'CODE_SELECTED_FROM_LIST'}) { + return &Apache::lonprintout::is_code_valid(\$helper->{'VARS'}{'SINGLE_CODE'}, + \$helper->{'VARS'}{'CODE_OPTION'}); + } else { + return undef; # Other forces control us. + } + + + + Choose single code from list + + + $code_selection + + Names to store the CODEs under for later: + + Bubble sheet type: + + + $codechoice +
Reprint a set of saved CODEs: @@ -2039,7 +2348,7 @@ CHOOSE_ANON2 } # FIXME: That RE should come from a library somewhere. - if ((((&Apache::lonnet::allowed('bre',$subdir) eq 'F') and ($helper->{VARS}->{'postdata'}=~/\.(problem|exam|quiz|assess|survey|form|library|page|xml|html|htm|xhtml|xhtm)/)) or defined $helper->{'VARS'}->{'construction'}) and $env{'request.role.adv'} and $subdir ne $Apache::lonnet::perlvar{'lonDocRoot'}.'/res/') { + if ((((&Apache::lonnet::allowed('bre',$subdir) eq 'F') and ($helper->{VARS}->{'postdata'}=~/\.(problem|exam|quiz|assess|survey|form|library|page|xml|html|htm|xhtml|xhtm)/)) or defined $helper->{'VARS'}->{'construction'}) and $perm{'pav'} and $subdir ne $Apache::lonnet::perlvar{'lonDocRoot'}.'/res/') { push @{$printChoices}, ["".&mt('Problems')." ".&mt('from current subdirectory')." $subdir", 'problems_from_directory', 'CHOOSE_FROM_SUBDIR']; my $f = '$filename'; @@ -2079,6 +2388,8 @@ CHOOSE_FROM_SUBDIR CHOOSE_FROM_ANY_SEQUENCE return \$res->is_sequence; return $urlValue; + return \$res->hasResource(\$res,sub { return !\$_[0]->is_sequence() },0,0); +