[system] / trunk / webwork2 / lib / WeBWorK / ContentGenerator / Hardcopy.pm Repository:
ViewVC logotype

View of /trunk/webwork2/lib/WeBWorK/ContentGenerator/Hardcopy.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1663 - (download) (as text) (annotate)
Tue Dec 9 01:12:32 2003 UTC (9 years, 6 months ago) by sh002i
File size: 24979 byte(s)
Normalized headers. All files now contain the text below as a header.
This is important since all files now (a) use the full name of the
package, (b) assign copyright to "The WeBWorK Project", (c) give the
full path of the file (relative to CVSROOT) instead of simply the file
name, and (d) include license and warranty information.

Here is the new header:

################################################################################
# WeBWorK Online Homework Delivery System
# Copyright © 2000-2003 The WeBWorK Projcct, http://openwebwork.sf.net/
# $CVSHeader$
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See either the GNU General Public License or the
# Artistic License for more details.
################################################################################

    1 ################################################################################
    2 # WeBWorK Online Homework Delivery System
    3 # Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/
    4 # $CVSHeader$
    5 #
    6 # This program is free software; you can redistribute it and/or modify it under
    7 # the terms of either: (a) the GNU General Public License as published by the
    8 # Free Software Foundation; either version 2, or (at your option) any later
    9 # version, or (b) the "Artistic License" which comes with this package.
   10 #
   11 # This program is distributed in the hope that it will be useful, but WITHOUT
   12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   13 # FOR A PARTICULAR PURPOSE.  See either the GNU General Public License or the
   14 # Artistic License for more details.
   15 ################################################################################
   16 
   17 package WeBWorK::ContentGenerator::Hardcopy;
   18 use base qw(WeBWorK::ContentGenerator);
   19 
   20 
   21 =head1 NAME
   22 
   23 WeBWorK::ContentGenerator::Hardcopy - generate a PDF version of one or more
   24 problem sets.
   25 
   26 =cut
   27 
   28 use strict;
   29 use warnings;
   30 use CGI qw();
   31 use File::Path qw(rmtree);
   32 use WeBWorK::Form;
   33 use WeBWorK::PG;
   34 use WeBWorK::Utils qw(readFile makeTempDirectory);
   35 use Apache::Constants qw(:common REDIRECT);
   36 
   37 sub pre_header_initialize {
   38   my ($self, $singleSet) = @_;
   39 
   40   my $r               = $self->{r};
   41   my $ce              = $self->{ce};
   42   my $db              = $self->{db};
   43   my @sets            = $r->param("hcSet");
   44   my @users           = $r->param("hcUser");
   45   my $hardcopy_format = $r->param('hardcopy_format') ? $r->param('hardcopy_format') : '';
   46 
   47 
   48   # add singleSet to the list of sets
   49   if (length $singleSet > 0) {
   50     $singleSet =~ s/^set//;
   51     unshift @sets, $singleSet unless grep { $_ eq $singleSet } @sets;
   52   }
   53 
   54   # default user is the effectiveUser
   55   unless (@users) {
   56     unshift @users, $r->param("effectiveUser");
   57   }
   58 
   59   $self->{user}            = $db->getUser($r->param("user")); # checked
   60   die "user ", $r->param("user"), " (real user) not found."
   61     unless $self->{user};
   62 
   63   $self->{effectiveUser}   = $db->getUser($r->param("effectiveUser")); # checked
   64   die "user ", $r->param("effectiveUser"), " (effective user) not found."
   65     unless $self->{effectiveUser};
   66 
   67   my $PermissionLevel = $db->getPermissionLevel($r->param("user")); # checked
   68   if ($PermissionLevel) {
   69     $self->{permissionLevel} = $PermissionLevel->permission();
   70   } else {
   71     die "permission level for user ", $r->param("user"), " (real user) not found.";
   72   }
   73 
   74   $self->{sets}            = \@sets;
   75   $self->{users}           = \@users;
   76   $self->{hardcopy_format} = $hardcopy_format;
   77   $self->{errors}          = [];
   78   $self->{warnings}        = [];
   79 
   80 
   81   # security checks
   82   my $multiSet    = $self->{permissionLevel} > 0;
   83   my $multiUser   = $self->{permissionLevel} > 0;
   84 
   85   if (@sets > 1 and not $multiSet) {
   86     $self->{generationError} = ["SIMPLE", "You are not permitted to generate hardcopy for multiple sets. Please select a single set and try again."];
   87   }
   88   if (@users > 1 and not $multiUser) {
   89     $self->{generationError} = ["SIMPLE", "You are not permitted to generate hardcopy for multiple users. Please select a single user and try again."];
   90   }
   91   if ($users[0] ne $self->{effectiveUser}->user_id and not $multiUser) {
   92     $self->{generationError} = ["SIMPLE", "You are not permitted to generate hardcopy for other users."];
   93   }
   94 
   95   unless ($self->{generationError}) {
   96     if ($r->param("generateHardcopy")) {
   97 #     my ($tempDir, $fileName) = eval { $self->generateHardcopy() };
   98       my ($pdfFileURL) = eval { $self->generateHardcopy() };
   99 
  100       $self->{generationError} = $@ if $@;
  101 #       warn "pdfFileURL is $pdfFileURL";
  102 #       warn "generation error is ".$self->{generationError};
  103 #       warn "hardcopy_format is ".$self->{hardcopy_format};
  104       if ( $self->{generationError}  ) {
  105 
  106         # In this case no correct pdf file was generated.
  107         # throw the error up higher.
  108         # The error is reported in body.
  109         # the tempDir was removed in generateHardcopy
  110       } elsif ( $self->{hardcopy_format} eq 'tex')   {
  111         # Only tex output was asked for, proceed to have the tex output handled by the subroutine
  112         # "body".
  113 
  114       } else {
  115           # information for redirect
  116             $self->{pdfFileURL} = $pdfFileURL;
  117       }
  118     }
  119   }
  120 }
  121 
  122 sub header {
  123   my ($self) = @_;
  124   my $r = $self->{r};
  125 
  126   if (exists $self->{pdfFileURL}) {
  127     $r->header_out(Location => $self->{pdfFileURL} );
  128     $self->{noContent} = 1;
  129     return REDIRECT;
  130   }
  131   $r->content_type("text/html");
  132   $r->send_http_header();
  133 }
  134 
  135 # -----
  136 
  137 sub path {
  138   my ($self, undef, $args) = @_;
  139 
  140   my $ce = $self->{ce};
  141   my $root = $ce->{webworkURLs}->{root};
  142   my $courseName = $ce->{courseName};
  143   return $self->pathMacro($args,
  144     "Home" => "$root",
  145     $courseName => "$root/$courseName",
  146     "Hardcopy Generator" => "",
  147   );
  148 }
  149 
  150 sub title {
  151   return "Hardcopy Generator";
  152 }
  153 
  154 sub body {
  155   my $self = shift;
  156 
  157   if ($self->{generationError}) {
  158     if (ref $self->{generationError} eq "ARRAY") {
  159       my ($disposition, @rest) = @{$self->{generationError}};
  160       if ($disposition eq "PGFAIL") {
  161         $self->multiErrorOutput(@{$self->{errors}});
  162         return "";
  163       } elsif ($disposition eq "FAIL") {
  164         print $self->errorOutput(@rest);
  165         return "";
  166       } elsif ($disposition eq "RETRY") {
  167         print $self->errorOutput(@rest);
  168       } else { # a "simple" error
  169         print CGI::p(CGI::font({-color=>"red"}, @rest));
  170       }
  171     } else {
  172       # not something we were expecting...
  173       die $self->{generationError};
  174     }
  175   }
  176   if (@{$self->{warnings}}) {
  177     # FIXME: this code will only be reached if there was also a
  178     # generation error, because otherwise the module will send
  179     # the PDF instead. DAMN!
  180     $self->multiWarningOutput(@{$self->{warnings}});
  181   }
  182   if ($self->{hardcopy_format} eq 'tex') {
  183 
  184     my $r_tex_content = $self->{r_tex_content};
  185     return $$r_tex_content;
  186 
  187 
  188   }
  189   $self->displayForm();
  190 }
  191 
  192 sub multiErrorOutput($@) {
  193   my ($self, @errors) = @_;
  194 
  195   print CGI::h2("Compile Errors");
  196   print CGI::p(<<EOF);
  197 WeBWorK has encountered one or more  errors while attempting to process
  198 these problem sets. It is likely that there are errors in the problems
  199 themselves. If you are a student, contact your professor to have the errors
  200 corrected. If you are a professor, please consult the error output below for
  201 more information.
  202 EOF
  203   foreach my $error (@errors) {
  204       my $user = $error->{user};
  205       my $userName = $user->user_id . ' ('.$user->first_name.' '.$user->last_name. ')';
  206     print CGI::h3("Set: ", $error->{set}, ", Problem: ", $error->{problem}, "for $userName");
  207     print CGI::h4("Error messages"), CGI::blockquote(CGI::pre($error->{message}));
  208     print CGI::h4("Error context"), CGI::blockquote(CGI::pre($error->{context}));
  209   }
  210 }
  211 
  212 sub multiWarningOutput($@) {
  213   my ($self, @warnings) = @_;
  214 
  215   print CGI::h2("Software Warnings");
  216   print CGI::p(<<EOF);
  217 WeBWorK has encountered one or more warnings while attempting to process these
  218 problem sets. It is likely that this indicates errors or ambiguitiees in the
  219 problems themselves. If you are a student, contact your professor to have the
  220 problems corrected. If you are a professor, please consut the warning output
  221 below for more informaiton.
  222 EOF
  223   foreach my $warning (@warnings) {
  224     print CGI::h3("Set: ", $warning->{set}, ", Problem: ", $warning->{problem});
  225     print CGI::h4("Error messages"), CGI::blockquote(CGI::pre($warning->{message}));
  226   }
  227 }
  228 
  229 # -----
  230 
  231 sub displayForm($) {
  232   my $self = shift;
  233   my $r = $self->{r};
  234   my $db = $self->{db};
  235 
  236   print CGI::start_p(), "Select the problem sets for which to generate hardcopy versions.";
  237   if ($self->{permissionLevel} > 0) {
  238     print "You may also select multiple users from the users list. You will receive hardcopy for each (set, user) pair.";
  239   }
  240   print CGI::end_p();
  241 
  242   my $download_texQ = $self->{permissionLevel} > 0;
  243 
  244   #  ##########construct action URL #################
  245   my $ce         = $self->{ce};
  246   my $root       = $ce->{webworkURLs}->{root};
  247   my $courseName = $ce->{courseName};
  248   my $actionURL  = "$root/$courseName/hardcopy/";
  249   #  ################################################
  250 
  251   print CGI::start_form(-method=>"POST", -action=>$actionURL);
  252   print $self->hidden_authen_fields();
  253   print CGI::h3("Options");
  254   print CGI::p("You may choose to show any of the following data. Correct answers and solutions are only available to privileged users or after the answer date of the problem set.");
  255   print CGI::p(
  256     CGI::checkbox(
  257       -name    => "showCorrectAnswers",
  258       -checked => $r->param("showCorrectAnswers") || 0,
  259       -label   => "Correct answers",
  260     ), CGI::br(),
  261     CGI::checkbox(
  262       -name    => "showHints",
  263       -checked => $r->param("showHints") || 0,
  264       -label   => "Hints",
  265     ), CGI::br(),
  266     CGI::checkbox(
  267       -name    => "showSolutions",
  268       -checked => $r->param("showSolutions") || 0,
  269       -label   => "Solutions",
  270     ),
  271   );
  272   print CGI::start_table({-width=>"100%"}), CGI::start_Tr({-valign=>"top"});
  273 
  274   my $multiSet          = $self->{permissionLevel} > 0;
  275   my $multiUser         = $self->{permissionLevel} > 0;
  276   my $preOpenSets       = $self->{permissionLevel} > 0;
  277   my $effectiveUserName = $self->{effectiveUser}->user_id;
  278   my @setNames     = $db->listUserSets($effectiveUserName);
  279   my @sets         = $db->getMergedSets( map { [$effectiveUserName, $_] }  @setNames ); # checked
  280   @sets = grep { defined $_ } @sets;
  281   @sets            = sort { $a->set_id cmp $b->set_id } @sets;
  282   @setNames        = map( {$_->set_id } @sets );  # get sorted version of setNames
  283   my %setLabels    = map( {($_->set_id, "set ".$_->set_id )} @sets );
  284   my (@users, @userNames,%userLabels);
  285 
  286   if ($multiUser) {
  287     @userNames    = $self->{db}->listUsers();
  288     @users        = $self->{db}->getUsers(@userNames); # checked
  289     @users = grep { defined $_ } @users;
  290     @users        = sort { $a->last_name cmp $b->last_name } @users;
  291     @userNames    = map( {$_->user_id} @users );  # get sorted version of user names
  292     %userLabels   = map( {($_->user_id , $_->last_name .", ". $_->first_name ." --- ". $_->user_id   ) } @users );
  293   }
  294   # set selection menu
  295   {
  296     print CGI::start_td();
  297     my $number_of_sets   = @{$self->{sets}};
  298     print CGI::h3("Sets: $number_of_sets pre-selected");
  299     print CGI::scrolling_list(-name=>'hcSet',
  300                  -values=>\@setNames,
  301                  -labels=>\%setLabels,
  302                  -size  => 10,
  303                  -multiple => $multiSet,
  304                  -defaults => $self->{sets},
  305     );
  306     print CGI::end_td();
  307   }
  308 
  309   # user selection menu
  310   if ($multiUser) {
  311     print CGI::start_td();
  312     my $number_of_users       =   @{$self->{users}};
  313     print CGI::h3("Users: $number_of_users pre-selected");
  314 
  315     print CGI::scrolling_list(-name=>'hcUser',
  316                  -values=>\@userNames,
  317                  -labels=>\%userLabels,
  318                  -size  => 10,
  319                  -multiple => 'true',
  320                  -defaults => $self->{users},
  321     );
  322     print CGI::end_td();
  323   }
  324 
  325   print CGI::end_Tr(), CGI::end_table();
  326   if ($download_texQ) {  # provide choice of pdf or tex output
  327     print CGI::p( {-align => "center"},
  328         CGI::radio_group(
  329               -name=>"hardcopy_format",
  330               -values=>['pdf', 'tex'],
  331               -default=>'pdf',
  332               -labels=>{'tex'=>'TeX','pdf'=>'PDF'}
  333         ),
  334     );
  335   } else {   # only pdf output available
  336     print CGI::hidden(-name=>'hardcopy_format',-value=>'pdf');
  337   }
  338   print CGI::p({-align=>"center"},
  339     CGI::submit(-name=>"generateHardcopy", -label=>"Generate Hardcopy"));
  340   print CGI::end_form();
  341 
  342   return "";
  343 }
  344 
  345 sub generateHardcopy($) {
  346   my $self = shift;
  347   my $ce = $self->{ce};
  348   my @sets = @{$self->{sets}};
  349   my @users = @{$self->{users}};
  350   my $multiSet = $self->{permissionLevel} > 0;
  351   my $multiUser = $self->{permissionLevel} > 0;
  352   # sanity checks
  353   unless (@sets) {
  354     die ["RETRY", "No sets were specified."];
  355   }
  356   unless (@users) {
  357     die ["RETRY", "No users were specified."];
  358   }
  359 
  360   # determine where hardcopy is going to go
  361   my $tempDir = makeTempDirectory($ce->{webworkDirs}->{tmp}, "webwork-hardcopy");
  362 
  363   # determine name of PDF file  #FIXME it might be best to have the effective user in here somewhere
  364   my $courseName = $self->{ce}->{courseName};
  365   my $fileNameSet = (@sets > 1 ? "multiset" : $sets[0]);
  366   my $fileNameUser = (@users > 1 ? "multiuser" : $users[0]);
  367   my $fileName = "$courseName.$fileNameUser.$fileNameSet.pdf";
  368 
  369   # for each user ... generate TeX for each set
  370   my $tex;
  371   #
  372   # the document tex preamble
  373   $tex .= $self->texInclude($self->{ce}->{webworkFiles}->{hardcopySnippets}->{preamble});
  374   # separate users by page break, or something
  375   foreach my $user (@users) {
  376     $tex .=  $self->getMultiSetTeX($user, @sets);
  377       if (@users) {
  378       # separate users, but not after the last set
  379       $tex .= $self->texInclude($self->{ce}->{webworkFiles}->{hardcopySnippets}->{userDivider});
  380     }
  381 
  382   }
  383   # the document postamble
  384   $tex .= $self->texInclude($self->{ce}->{webworkFiles}->{hardcopySnippets}->{postamble});
  385 
  386   # deal with PG errors
  387   if (@{$self->{errors}}) {
  388     die ["PGFAIL"];
  389   }
  390 
  391   # FIXME: add something like:
  392   #if (@{$self->{warnings}}) {
  393   # $self->{generationWarnings} = 1;
  394   #}
  395   # ???????
  396 
  397   # "try" to generate pdf or return TeX file
  398   my $pdfFileURL = undef;
  399   if ($self->{hardcopy_format} eq 'pdf' ) {
  400     my $errors = '';
  401     $pdfFileURL = eval { $self->latex2pdf($tex, $tempDir, $fileName) };
  402     if ($@) {
  403       $errors = $@;
  404       #$errors =~ s/\n/<br>/g;  # make this readable on HTML FIXME make this a Utils. filter (Error2HTML)
  405       # clean up temp directory
  406       # FIXME this clean up done in latex2pdf?  rmtree($tempDir);
  407       die ["FAIL", "Failed to generate PDF from tex", $errors]; #throw error to subroutine body
  408     } else {
  409         # pass the relative temp file path back up to go subroutine
  410         # to have an appropriate redirect generated.
  411 
  412 
  413     }
  414   } elsif ($self->{hardcopy_format} eq 'tex')    {
  415       $tex = protect_HTML($tex);
  416       #$tex =~ s/\n/\<br\>\n/g;
  417       $tex = join('', ("<pre>\n",$tex,"\n</pre>\n"));
  418     $self->{r_tex_content} = \$tex;
  419 
  420 
  421   } else {
  422 
  423 
  424     die["FAIL", "Hard copy format |".$self->{hardcopy_format}. "| not recognized."];
  425 
  426   }
  427   #return $tempDir, $fileName;
  428   # return $pdfFilePath;
  429   return $pdfFileURL;
  430 }
  431 
  432 # -----
  433 
  434 sub latex2pdf {
  435   # this is a little ad-hoc function which I will replace with a LaTeX
  436   # module at some point (or put it in Utils).
  437   my ($self, $tex, $tempDir, $fileName) = @_;
  438   my $finalFile = "$tempDir/$fileName";
  439   my $ce = $self->{ce};
  440 
  441   # Location for hardcopy file to be downloaded
  442   # FIXME  this should use surePathToTmpFile
  443   my $hardcopyTempDirectory = $ce->{courseDirs}->{html_temp}."/hardcopy";
  444   mkdir ($hardcopyTempDirectory)  or die "Unable to make $hardcopyTempDirectory" unless -e $hardcopyTempDirectory;
  445   my $hardcopyFilePath        =  "$hardcopyTempDirectory/$fileName";
  446   my $hardcopyFileURL         =  $ce->{courseURLs}->{html_temp}."/hardcopy/$fileName";
  447   $self->{hardcopyFilePath}   =  $hardcopyFilePath;
  448   $self->{hardcopyFileURL}    =  $hardcopyFileURL;
  449 
  450   ## create a temporary directory for tex to shit in
  451   # - we're using the existing temp dir. now
  452 
  453   my $wd = $tempDir;
  454   my $texFile = "$wd/hardcopy.tex";
  455   my $pdfFile = "$wd/hardcopy.pdf";
  456   my $logFile = "$wd/hardcopy.log";
  457 
  458   # write the tex file
  459   local *TEX;
  460   open TEX, ">", $texFile or die "Failed to open $texFile: $!\n".CGI::br();
  461   print TEX $tex;
  462   close TEX;
  463 
  464   # call pdflatex - we don't want to chdir in the mod_perl process, as
  465   # that might step on the feet of other things (esp. in Apache 2.0)
  466   my $pdflatex = $ce->{externalPrograms}->{pdflatex};
  467   my $pdflatexResult = system "cd $wd && $pdflatex $texFile";
  468 
  469   # Even with errors there may be a valid pdfFile.  Move it to where we can get it.
  470   if (-e $pdfFile) {
  471 
  472        # moving to course tmp/hardcopy directory
  473       system "/bin/mv", $pdfFile, $hardcopyFilePath
  474       and die "Failed to mv: $pdfFile to  $hardcopyFilePath<br> Quite likely this means that there ".
  475               "is not sufficient write permission for some directory.<br>$!\n".CGI::br();
  476   }
  477   # Alert the world that the tex file did not process perfectly.
  478   if ($pdflatexResult) {
  479     # something bad happened
  480     my $textErrorMessage = "Call to $pdflatex failed: $!\n".CGI::br();
  481 
  482     if (-e $hardcopyFilePath ) {
  483        # FIXME  Misuse of html tags!!!
  484       $textErrorMessage.= "<h4>Some pdf output was produced and is available ". CGI::a({-href=>$hardcopyFileURL},"here.</h4>").CGI::hr();
  485     }
  486     # report logfile
  487     if (-e $logFile) {
  488       $textErrorMessage .= "pdflatex ran, but did not succeed. This suggests an error in the TeX\n".CGI::br();
  489       $textErrorMessage .= "version of one of the problems, or a problem with the pdflatex system.\n".CGI::br();
  490       my $logFileContents = eval { readTexErrorLog($logFile) };
  491       $logFileContents    .=  CGI::hr().CGI::hr();
  492       $logFileContents    .= eval { formatTexFile($texFile)     };
  493       if ($@) {
  494         $textErrorMessage .= "Additionally, the pdflatex log file could not be read, though it exists.\n".CGI::br();
  495       } else {
  496         $textErrorMessage .= "The essential contents of the TeX log are as follows:\n".CGI::hr().CGI::br();
  497         $textErrorMessage .= "$logFileContents\n".CGI::br().CGI::br();
  498       }
  499     } else {
  500       $textErrorMessage .= "No log file was created, suggesting that pdflatex never ran. Check the WeBWorK\n".CGI::br();
  501       $textErrorMessage .= "configuration to ensure that the path to pdflatex is correct.\n".CGI::br();
  502     }
  503     die $textErrorMessage;
  504   }
  505 
  506 
  507 
  508   ## remove temporary directory
  509   rmtree($wd, 0, 0);
  510 
  511 
  512   -e $hardcopyFilePath or die "Failed to create $finalFile for no apparent reason.\n";
  513   # return hardcopyFilePath;
  514   return $hardcopyFileURL;
  515 }
  516 
  517 # -----
  518 # FIXME move to Utils? probably not
  519 
  520 sub readTexErrorLog {
  521   my $filePath = shift;
  522   my $print_error_switch = 0;
  523   my $line='';
  524   my @message=();
  525   #local($/ ) = "\n";
  526     open(LOGFILE,"<$filePath") or die "Can't read $filePath";
  527     while (<LOGFILE>) {
  528       $line = $_;
  529       $print_error_switch = 1  if $line =~ /^!/;  # after a fatal error start printing messages
  530     push(@message, protect_HTML($line)) if $print_error_switch;
  531     }
  532     close(LOGFILE);
  533     join("<br>\n",@message);
  534 }
  535 
  536 sub formatTexFile {
  537   my $texFilePath   = shift;
  538     open (TEXFILE, "$texFilePath")
  539                  or die "Can't open tex source file: path= $texFilePath: $!";
  540 
  541   my @message       = ();
  542     push @message, '<BR>\n<h3>TeX Source File:</h3><BR>\n',     ;
  543 
  544     my $lineNumber    = 1;
  545     while (<TEXFILE>) {
  546     push @message, protect_HTML("$lineNumber $_")."\n";
  547         $lineNumber++;
  548     }
  549     close(TEXFILE);
  550     #push @message, '</pre>';
  551     join("<br>\n",@message);
  552 }
  553 sub protect_HTML {
  554   my $line = shift;
  555   chomp($line);
  556   $line =~s/\&/&amp;/g;
  557   $line =~s/</&lt;/g;
  558   $line =~s/>/&gt;/g;
  559   $line;
  560 }
  561 sub texBlockComment(@) { return "\n".("%"x80)."\n%% ".join("", @_)."\n".("%"x80)."\n\n"; }
  562 
  563 sub getMultiSetTeX {
  564   my ($self, $effectiveUserName,@sets) = @_;
  565   my $ce = $self->{ce};
  566   my $tex = "";
  567 
  568 
  569 
  570   while (defined (my $setName = shift @sets)) {
  571     $tex .= $self->getSetTeX($effectiveUserName, $setName);
  572     if (@sets) {
  573       # divide sets, but not after the last set
  574       $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{setDivider});
  575     }
  576   }
  577 
  578 
  579 
  580   return $tex;
  581 }
  582 
  583 sub getSetTeX {
  584   my ($self, $effectiveUserName, $setName) = @_;
  585   my $ce = $self->{ce};
  586   my $db = $self->{db};
  587 
  588   # FIXME (debug code line next)
  589   # print STDERR "Creating set $setName for $effectiveUserName \n";
  590 
  591   # FIXME We could define a default for the effective user if no correct name is passed in.
  592   # I'm not sure that it is wise.
  593   my $effectiveUser = $db->getUser($effectiveUserName); # checked
  594   die "effective user ($effectiveUserName) does not exist."
  595     unless defined $effectiveUser;
  596 
  597   my @problemNumbers = sort { $a <=> $b }
  598     $db->listUserProblems($effectiveUserName, $setName);
  599 
  600   # get header and footer
  601   my $set       = $db->getMergedSet($effectiveUserName, $setName); # checked
  602   my $setHeader = (ref($set) && $set->set_header) ? $set->set_header : $ce->{webworkFiles}->{hardcopySnippets}->{setHeader};
  603   # database doesn't support the following yet :(
  604   #my $setFooter = $wwdb->getMergedSet($effectiveUserName, $setName)->set_footer
  605   # || $ce->{webworkFiles}->{hardcopySnippets}->{setFooter};
  606   # so we don't allow per-set customization, which is probably okay :)
  607   my $setFooter = $ce->{webworkFiles}->{hardcopySnippets}->{setFooter};
  608 
  609   my $tex = "";
  610 
  611   # render header
  612   $tex .= texBlockComment("BEGIN $setName : $setHeader");
  613   $tex .= $self->getProblemTeX($effectiveUser,$setName, 0, $setHeader);
  614 
  615   # render each problem
  616   while (my $problemNumber = shift @problemNumbers) {
  617     $tex .= texBlockComment("BEGIN $setName : $problemNumber");
  618     $tex .= $self->getProblemTeX($effectiveUser,$setName, $problemNumber);
  619     if (@problemNumbers) {
  620       # divide problems, but not after the last problem
  621       $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{problemDivider});
  622     }
  623   }
  624 
  625   # render footer
  626   $tex .= texBlockComment("BEGIN $setName : $setFooter");
  627   $tex .= $self->getProblemTeX($effectiveUser,$setName, 0, $setFooter);
  628 
  629   return $tex;
  630 }
  631 
  632 sub getProblemTeX {
  633     $WeBWorK::timer1 ->continue("hardcopy: begin processing problem") if defined($WeBWorK::timer1);
  634   my ($self, $effectiveUser, $setName, $problemNumber, $pgFile) = @_;
  635   my $r = $self->{r};
  636   my $ce = $self->{ce};
  637   my $db = $self->{db};
  638 
  639   # Should we provide a default user ? I think not FIXME
  640 
  641   # $effectiveUser = $self->{effectiveUser} unless defined($effectiveUser);
  642   my $permissionLevel = $self->{permissionLevel};
  643   my $set  = $db->getMergedSet($effectiveUser->user_id, $setName); # checked
  644   unless (ref($set) )  {  # return error if no set is defined
  645     push(@{$self->{warnings}},
  646          setName => $setName,
  647          problem => 0,
  648          message => "No set $setName exists for ".$effectiveUser->first_name.' '.
  649                         $effectiveUser->last_name.' ('.$effectiveUser->user_id.' )'
  650       );
  651       return "No set $setName for ".$effectiveUser->user_id;
  652   }
  653 
  654   my $psvn = $set->psvn();
  655 
  656   # decide what to do about problem number
  657   my $problem;
  658   if ($problemNumber) {  # problem number defined and not zero
  659     $problem = $db->getMergedProblem($effectiveUser->user_id, $setName, $problemNumber); # checked
  660   } elsif ($pgFile) {
  661     $problem = WeBWorK::DB::Record::UserProblem->new(
  662       set_id => $set->set_id,
  663       problem_id => 0,
  664       login_id => $effectiveUser->user_id,
  665       source_file => $pgFile,
  666       # the rest of Problem's fields are not needed, i think
  667     );
  668   }
  669   unless (ref($problem) )  {  # return error if no problem is defined
  670       $problemNumber = 'undefined problem number' unless defined($problemNumber);
  671       $setName       = 'undefined set Name' unless defined($setName);
  672       my $msg        = "Problem $setName/problem $problemNumber not assigned to ".
  673                     $effectiveUser->first_name.' '.
  674                         $effectiveUser->last_name.' ('.$effectiveUser->user_id.' )';
  675     push(@{$self->{warnings}},
  676          setName => $setName,
  677          problem => $problemNumber,
  678          message => $msg,
  679       );
  680       $msg =~ s/_/\\_/;  # escape underbars to protect them from TeX FIXME--this could be more general??
  681       return $msg;
  682   }
  683   # figure out if we're allowed to get solutions and call PG->new accordingly.
  684   my $showCorrectAnswers = $r->param("showCorrectAnswers") || 0;
  685   my $showHints          = $r->param("showHints") || 0;
  686   my $showSolutions      = $r->param("showSolutions") || 0;
  687   unless ($permissionLevel > 0 or time > $set->answer_date) {
  688     $showCorrectAnswers = 0;
  689     $showSolutions      = 0;
  690   }
  691 
  692   my $pg = WeBWorK::PG->new(
  693     $ce,
  694     $effectiveUser,
  695     $r->param('key'),
  696     $set,
  697     $problem,
  698     $psvn,
  699     {}, # no form fields!
  700     { # translation options
  701       displayMode     => "tex",
  702       showHints       => $showHints,
  703       showSolutions   => $showSolutions,
  704       processAnswers  => $showCorrectAnswers,
  705     },
  706   );
  707 
  708   if ($pg->{warnings} ne "") {
  709     push @{$self->{warnings}}, {
  710       set     => $setName,
  711       problem => $problemNumber,
  712       message => $pg->{warnings},
  713     };
  714   }
  715 
  716   if ($pg->{flags}->{error_flag}) {
  717     push @{$self->{errors}}, {
  718       set     => $setName,
  719       problem => $problemNumber,
  720       user    => $effectiveUser,
  721       message => $pg->{errors},
  722       context => $pg->{body_text},
  723     };
  724     # if there was an error, body_text contains
  725     # the error context, not TeX code FIXME (should this error context be used?)
  726     $pg->{body_text} = ''; #   FIXME using undef causes error unless it is caught undef;
  727   } else {
  728     # append list of correct answers to body text
  729     if ($showCorrectAnswers && $problemNumber != 0) {
  730       my $correctTeX = "Correct Answers:\\par\\begin{itemize}\n";
  731       foreach my $ansName (@{$pg->{flags}->{ANSWER_ENTRY_ORDER}}) {
  732         my $correctAnswer = $pg->{answers}->{$ansName}->{correct_ans};
  733         $correctAnswer =~ s/\^/\\\^\{\}/g;
  734         $correctAnswer =~ s/\_/\\\_/g;
  735         $correctTeX .= "\\item $correctAnswer\n";
  736       }
  737       $correctTeX .= "\\end{itemize} \\par\n";
  738       $pg->{body_text} .= $correctTeX;
  739     }
  740   }
  741   $WeBWorK::timer1 ->continue("hardcopy: end processing problem") if defined($WeBWorK::timer1);
  742   return $pg->{body_text};
  743 }
  744 
  745 sub texInclude {
  746   my ($self, $texFile) = @_;
  747   my $tex = "";
  748 
  749   $tex .= texBlockComment("BEGIN: $texFile");
  750   eval {
  751     $tex .= readFile($texFile)
  752   };
  753   if ($@) {
  754     $tex .= texBlockComment($@);
  755   }
  756 
  757   return $tex;
  758 }
  759 
  760 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9