[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 641 - (download) (as text) (annotate)
Wed Nov 20 21:47:25 2002 UTC (10 years, 6 months ago) by sh002i
File size: 9007 byte(s)
fixed hardcopy generation for sets named "0" (a false, but defined value)
added error and warning reporting.
-sam

    1 ################################################################################
    2 # WeBWorK mod_perl (c) 2000-2002 WeBWorK Project
    3 # $Id$
    4 ################################################################################
    5 
    6 package WeBWorK::ContentGenerator::Hardcopy;
    7 
    8 =head1 NAME
    9 
   10 WeBWorK::ContentGenerator::Hardcopy - generate a PDF version of one or more
   11 problem sets.
   12 
   13 =cut
   14 
   15 use strict;
   16 use warnings;
   17 use base qw(WeBWorK::ContentGenerator);
   18 #use Apache::Constants qw(:common);
   19 use CGI qw();
   20 use File::Path qw(rmtree);
   21 use File::Temp qw(tempdir);
   22 use WeBWorK::DB::Classlist;
   23 use WeBWorK::DB::WW;
   24 use WeBWorK::Form;
   25 use WeBWorK::Utils qw(readFile);
   26 
   27 sub texBlockComment(@) { return "\n".("%"x80)."\n%% ".join("", @_)."\n".("%"x80)."\n\n"; }
   28 
   29 sub initialize {
   30   my ($self, $singleSet, undef) = @_;
   31 
   32   my $r = $self->{r};
   33   my $ce = $self->{courseEnvironment};
   34   my @sets = $r->param("set");
   35 
   36   if (length $singleSet > 0) {
   37     $singleSet =~ s/^set//;
   38     unshift @sets, $singleSet;
   39   }
   40 
   41   $self->{cldb} = WeBWorK::DB::Classlist->new($ce);
   42   $self->{wwdb} = WeBWorK::DB::WW->new($ce);
   43   $self->{sets} = \@sets;
   44   $self->{errors} = [];
   45   $self->{warnings} = [];
   46 }
   47 
   48 sub path {
   49   my ($self, undef, $args) = @_;
   50 
   51   my $ce = $self->{courseEnvironment};
   52   my $root = $ce->{webworkURLs}->{root};
   53   my $courseName = $ce->{courseName};
   54   return $self->pathMacro($args,
   55     "Home" => "$root",
   56     $courseName => "$root/$courseName",
   57     "Hardcopy Generator" => "",
   58   );
   59 }
   60 
   61 sub title {
   62   return "Hardcopy Generator";
   63 }
   64 
   65 sub body {
   66   my $self = shift;
   67 
   68   my $courseName = $self->{courseEnvironment}->{courseName};
   69   my $userName = $self->{r}->param("user");
   70   my @sets = @{$self->{sets}};
   71 
   72   unless (@sets) {
   73     print CGI::p("No problem sets were specified.");
   74     return "";
   75   }
   76 
   77   # determine where hardcopy is going to go
   78   my $tempDir = $self->{courseEnvironment}->{courseDirs}->{html_temp}
   79     . "/hardcopy";
   80   my $tempURL = $self->{courseEnvironment}->{courseURLs}->{html_temp}
   81     . "/hardcopy";
   82 
   83   # determine name of PDF file
   84   my $fileName;
   85   if (@sets > 1) {
   86     # multiset output
   87     $fileName = "$courseName.$userName.multiset.pdf"
   88   } elsif (@sets == 1) {
   89     # only one set
   90     my $setName = $sets[0];
   91     $fileName = "$courseName.$userName.$setName.pdf";
   92   } else {
   93     $fileName = "$courseName.$userName.pdf";
   94   }
   95 
   96   # determine full URL
   97   my $fullURL = "$tempURL/$fileName";
   98 
   99   # generate TeX from sets
  100   my $tex = $self->getMultiSetTeX(@sets);
  101   #print CGI::pre($tex);
  102 
  103   # check for PG errors (fatal)
  104   if (@{$self->{errors}}) {
  105     my @errors = @{$self->{errors}};
  106     print CGI::h2("Software Errors");
  107     print CGI::p(<<EOF);
  108 WeBWorK has encountered one or more software errors while attempting to process these sets.
  109 It is likely that there are error(s) in the problem itself.
  110 If you are a student, contact your professor to have the error(s) corrected.
  111 If you are a professor, please consut the error output below for more informaiton.
  112 EOF
  113     foreach my $error (@errors) {
  114       print CGI::h3("Set: ", $error->{set}, ", Problem: ", $error->{problem});
  115       print CGI::h4("Error messages"), CGI::blockquote(CGI::pre($error->{message}));
  116       print CGI::h4("Error context"), CGI::blockquote(CGI::pre($error->{context}));
  117     }
  118 
  119     return "";
  120   }
  121 
  122   # "try" to generate hardcopy
  123   eval { $self->latex2pdf($tex, $tempDir, $fileName) };
  124   if ($@) {
  125     print CGI::p("An error occured while trying to generate your PDF hardcopy:");
  126     print CGI::blockquote(CGI::pre($@));
  127     return "";
  128   } else {
  129     print CGI::p({-align=>"center"},
  130       CGI::big(CGI::a({-href=>$fullURL}, "Download PDF Hardcopy"))
  131     );
  132   }
  133 
  134   # check for PG warnings (non-fatal)
  135   if (@{$self->{warnings}}) {
  136     my @warnings = @{$self->{warnings}};
  137     print CGI::h2("Software Warnings");
  138     print CGI::p(<<EOF);
  139 WeBWorK has encountered warnings while attempting to process these sets.
  140 It is likely that this indicates an error or ambiguity in the problem(s) themselves.
  141 If you are a student, contact your professor to have the problem(s) corrected.
  142 If you are a professor, please consut the error output below for more informaiton.
  143 EOF
  144     foreach my $warning (@warnings) {
  145       print CGI::h3("Set: ", $warning->{set}, ", Problem: ", $warning->{problem});
  146       print CGI::h4("Warning messages"), CGI::blockquote(CGI::pre($warning->{message}));
  147     }
  148   }
  149 
  150   return "";
  151 }
  152 
  153 # -----
  154 
  155 sub latex2pdf {
  156   # this is a little ad-hoc function which I will replace with a LaTeX
  157   # module at some point (or put it in Utils).
  158   my ($self, $tex, $fileBase, $fileName) = @_;
  159   my $finalFile = "$fileBase/$fileName";
  160   my $ce = $self->{courseEnvironment};
  161 
  162   # create a temporary directory for tex to shit in
  163   my $wd = tempdir("webwork-hardcopy-XXXXXXXX", TMPDIR => 1);
  164   my $texFile = "$wd/hardcopy.tex";
  165   my $pdfFile = "$wd/hardcopy.pdf";
  166   my $logFile = "$wd/hardcopy.log";
  167 
  168   # write the tex file
  169   local *TEX;
  170   open TEX, ">", $texFile or die "Failed to open $texFile: $!\n";
  171   print TEX $tex;
  172   close TEX;
  173 
  174   # call pdflatex - we don't want to chdir in the mod_perl process, as
  175   # that might step on the feet of other things (esp. in Apache 2.0)
  176   my $pdflatex = $ce->{externalPrograms}->{pdflatex};
  177   system "cd $wd && $pdflatex $texFile" and die "Failed to call pdflatex: $!\n";
  178 
  179   if (-e $pdfFile) {
  180     # move resulting PDF file to appropriate location
  181     system "/bin/mv", $pdfFile, $finalFile and die "Failed to mv: $!\n";
  182   }
  183 
  184   # remove temporary directory
  185   rmtree($wd, 0, 1);
  186 
  187   -e $finalFile or die "Failed to create $finalFile for no apparent reason.\n";
  188 }
  189 
  190 # -----
  191 
  192 sub getMultiSetTeX {
  193   my ($self, @sets) = @_;
  194   my $ce = $self->{courseEnvironment};
  195   my $tex = "";
  196 
  197   # the document preamble
  198   $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{preamble});
  199 
  200   while (defined (my $setName = shift @sets)) {
  201     $tex .= $self->getSetTeX($setName);
  202     if (@sets) {
  203       # divide sets, but not after the last set
  204       $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{setDivider});
  205     }
  206   }
  207 
  208   # the document postamble
  209   $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{postamble});
  210 
  211   return $tex;
  212 }
  213 
  214 sub getSetTeX {
  215   my ($self, $setName) = @_;
  216   my $ce = $self->{courseEnvironment};
  217   my $wwdb = $self->{wwdb};
  218   my $user = $self->{r}->param("user");
  219   my @problemNumbers = sort { $a <=> $b } $wwdb->getProblems($user, $setName);
  220 
  221   # get header and footer
  222   my $setHeader = $wwdb->getSet($user, $setName)->set_header
  223     || $ce->{webworkFiles}->{hardcopySnippets}->{setHeader};
  224   # database doesn't support the following yet :(
  225   #my $setFooter = $wwdb->getSet($user, $setName)->set_footer
  226   # || $ce->{webworkFiles}->{hardcopySnippets}->{setFooter};
  227   # so we don't allow per-set customization, which is probably okay :)
  228   my $setFooter = $ce->{webworkFiles}->{hardcopySnippets}->{setFooter};
  229 
  230   my $tex = "";
  231 
  232   # render header
  233   $tex .= texBlockComment("BEGIN $setName : $setHeader");
  234   $tex .= $self->getProblemTeX($setName, 0, $setHeader);
  235 
  236   # render each problem
  237   while (my $problemNumber = shift @problemNumbers) {
  238     $tex .= texBlockComment("BEGIN $setName : $problemNumber");
  239     $tex .= $self->getProblemTeX($setName, $problemNumber);
  240     if (@problemNumbers) {
  241       # divide problems, but not after the last problem
  242       $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{problemDivider});
  243     }
  244   }
  245 
  246   # render footer
  247   $tex .= texBlockComment("BEGIN $setName : $setFooter");
  248   $tex .= $self->getProblemTeX($setName, 0, $setFooter);
  249 
  250   return $tex;
  251 }
  252 
  253 sub getProblemTeX {
  254   my ($self, $setName, $problemNumber, $pgFile) = @_;
  255   my $r = $self->{r};
  256   my $ce = $self->{courseEnvironment};
  257 
  258   my $wwdb = $self->{wwdb};
  259   my $cldb = $self->{cldb};
  260   my $user = $cldb->getUser($r->param("user"));
  261   my $set  = $wwdb->getSet($user->id, $setName);
  262   my $psvn = $wwdb->getPSVN($user->id, $setName);
  263 
  264   # decide what to do about problem number
  265   my $problem;
  266   if ($problemNumber) {
  267     $problem = $wwdb->getProblem($user->id, $setName, $problemNumber);
  268   } elsif ($pgFile) {
  269     $problem = WeBWorK::Problem->new(
  270       id => 0,
  271       set_id => $set->id,
  272       login_id => $user->id,
  273       source_file => $pgFile,
  274       # the rest of Problem's fields are not needed, i think
  275     );
  276   }
  277 
  278   my $pg = WeBWorK::PG->new(
  279     $ce,
  280     $user,
  281     $r->param('key'),
  282     $set,
  283     $problem,
  284     $psvn,
  285     {}, # no form fields!
  286     { # translation options
  287       displayMode     => "tex",
  288       showHints       => 0,
  289       showSolutions   => 0,
  290       processAnswers  => 0,
  291     },
  292   );
  293 
  294   if ($pg->{warnings} ne "") {
  295     push @{$self->{warnings}}, {
  296       set     => $setName,
  297       problem => $problemNumber,
  298       message => $pg->{warnings},
  299     };
  300   }
  301 
  302   if ($pg->{flags}->{error_flag}) {
  303     push @{$self->{errors}}, {
  304       set     => $setName,
  305       problem => $problemNumber,
  306       message => $pg->{errors},
  307       context => $pg->{body_text},
  308     };
  309     # if there was an error, body_text contains
  310     # the error context, not TeX code
  311     $pg->{body_text} = undef;
  312   }
  313 
  314   return $pg->{body_text};
  315 }
  316 
  317 sub texInclude {
  318   my ($self, $texFile) = @_;
  319   my $tex = "";
  320 
  321   $tex .= texBlockComment("BEGIN: $texFile");
  322   eval {
  323     $tex .= readFile($texFile)
  324   };
  325   if ($@) {
  326     $tex .= texBlockComment($@);
  327   }
  328 
  329   return $tex;
  330 }
  331 
  332 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9