[system] / trunk / webwork-modperl / lib / WeBWorK / ContentGenerator / Hardcopy.pm Repository:
ViewVC logotype

View of /trunk/webwork-modperl/lib/WeBWorK/ContentGenerator/Hardcopy.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 669 - (download) (as text) (annotate)
Fri Dec 6 17:42:02 2002 UTC (10 years, 5 months ago) by sh002i
File size: 9747 byte(s)
added feedback links to a bunch of modules.
-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   STUFF: {
   69     my $courseName = $self->{courseEnvironment}->{courseName};
   70     my $userName = $self->{r}->param("user");
   71     my @sets = @{$self->{sets}};
   72 
   73     unless (@sets) {
   74       print CGI::p("No problem sets were specified.");
   75       last STUFF;
   76     }
   77 
   78     # determine where hardcopy is going to go
   79     my $tempDir = $self->{courseEnvironment}->{courseDirs}->{html_temp}
   80       . "/hardcopy";
   81     my $tempURL = $self->{courseEnvironment}->{courseURLs}->{html_temp}
   82       . "/hardcopy";
   83 
   84     # make sure tempDir exists
   85     unless (-e $tempDir) {
   86       if (system "mkdir", "-p", $tempDir) {
   87         print CGI::p("An error occured while trying to generate your PDF hardcopy:");
   88         print CGI::blockquote(CGI::pre("Failed to mkdir $tempDir: $!\n"));
   89       }
   90     }
   91 
   92     # determine name of PDF file
   93     my $fileName;
   94     if (@sets > 1) {
   95       # multiset output
   96       $fileName = "$courseName.$userName.multiset.pdf"
   97     } elsif (@sets == 1) {
   98       # only one set
   99       my $setName = $sets[0];
  100       $fileName = "$courseName.$userName.$setName.pdf";
  101     } else {
  102       $fileName = "$courseName.$userName.pdf";
  103     }
  104 
  105     # determine full URL
  106     my $fullURL = "$tempURL/$fileName";
  107 
  108     # generate TeX from sets
  109     my $tex = $self->getMultiSetTeX(@sets);
  110     #print CGI::pre($tex);
  111 
  112     # check for PG errors (fatal)
  113     if (@{$self->{errors}}) {
  114       my @errors = @{$self->{errors}};
  115       print CGI::h2("Software Errors");
  116       print CGI::p(<<EOF);
  117 WeBWorK has encountered one or more software errors while attempting to process these sets.
  118 It is likely that there are error(s) in the problem itself.
  119 If you are a student, contact your professor to have the error(s) corrected.
  120 If you are a professor, please consut the error output below for more informaiton.
  121 EOF
  122       foreach my $error (@errors) {
  123         print CGI::h3("Set: ", $error->{set}, ", Problem: ", $error->{problem});
  124         print CGI::h4("Error messages"), CGI::blockquote(CGI::pre($error->{message}));
  125         print CGI::h4("Error context"), CGI::blockquote(CGI::pre($error->{context}));
  126       }
  127 
  128       last STUFF;
  129     }
  130 
  131     # "try" to generate hardcopy
  132     eval { $self->latex2pdf($tex, $tempDir, $fileName) };
  133     if ($@) {
  134       print CGI::p("An error occured while trying to generate your PDF hardcopy:");
  135       print CGI::blockquote(CGI::pre($@));
  136       last STUFF;
  137     } else {
  138       print CGI::p({-align=>"center"},
  139         CGI::big(CGI::a({-href=>$fullURL}, "Download PDF Hardcopy"))
  140       );
  141     }
  142 
  143     # check for PG warnings (non-fatal)
  144     if (@{$self->{warnings}}) {
  145       my @warnings = @{$self->{warnings}};
  146       print CGI::h2("Software Warnings");
  147       print CGI::p(<<EOF);
  148 WeBWorK has encountered warnings while attempting to process these sets.
  149 It is likely that this indicates an error or ambiguity in the problem(s) themselves.
  150 If you are a student, contact your professor to have the problem(s) corrected.
  151 If you are a professor, please consut the error output below for more informaiton.
  152 EOF
  153       foreach my $warning (@warnings) {
  154         print CGI::h3("Set: ", $warning->{set}, ", Problem: ", $warning->{problem});
  155         print CGI::h4("Warning messages"), CGI::blockquote(CGI::pre($warning->{message}));
  156       }
  157     }
  158   }
  159 
  160   # feedback form
  161   my $ce = $self->{courseEnvironment};
  162   my $root = $ce->{webworkURLs}->{root};
  163   my $courseName = $ce->{courseName};
  164   my $feedbackURL = "$root/$courseName/feedback/";
  165   print
  166     CGI::startform("POST", $feedbackURL),
  167     $self->hidden_authen_fields,
  168     CGI::hidden("module", __PACKAGE__),
  169     CGI::p({-align=>"right"},
  170       CGI::submit(-name=>"feedbackForm", -label=>"Send Feedback")
  171     ),
  172     CGI::endform();
  173 
  174   return "";
  175 }
  176 
  177 # -----
  178 
  179 sub latex2pdf {
  180   # this is a little ad-hoc function which I will replace with a LaTeX
  181   # module at some point (or put it in Utils).
  182   my ($self, $tex, $fileBase, $fileName) = @_;
  183   my $finalFile = "$fileBase/$fileName";
  184   my $ce = $self->{courseEnvironment};
  185 
  186   # create a temporary directory for tex to shit in
  187   my $wd = tempdir("webwork-hardcopy-XXXXXXXX", TMPDIR => 1);
  188   my $texFile = "$wd/hardcopy.tex";
  189   my $pdfFile = "$wd/hardcopy.pdf";
  190   my $logFile = "$wd/hardcopy.log";
  191 
  192   # write the tex file
  193   local *TEX;
  194   open TEX, ">", $texFile or die "Failed to open $texFile: $!\n";
  195   print TEX $tex;
  196   close TEX;
  197 
  198   # call pdflatex - we don't want to chdir in the mod_perl process, as
  199   # that might step on the feet of other things (esp. in Apache 2.0)
  200   my $pdflatex = $ce->{externalPrograms}->{pdflatex};
  201   system "cd $wd && $pdflatex $texFile" and die "Failed to call pdflatex: $!\n";
  202 
  203   if (-e $pdfFile) {
  204     # move resulting PDF file to appropriate location
  205     system "/bin/mv", $pdfFile, $finalFile and die "Failed to mv: $!\n";
  206   }
  207 
  208   # remove temporary directory
  209   rmtree($wd, 0, 1);
  210 
  211   -e $finalFile or die "Failed to create $finalFile for no apparent reason.\n";
  212 }
  213 
  214 # -----
  215 
  216 sub getMultiSetTeX {
  217   my ($self, @sets) = @_;
  218   my $ce = $self->{courseEnvironment};
  219   my $tex = "";
  220 
  221   # the document preamble
  222   $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{preamble});
  223 
  224   while (defined (my $setName = shift @sets)) {
  225     $tex .= $self->getSetTeX($setName);
  226     if (@sets) {
  227       # divide sets, but not after the last set
  228       $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{setDivider});
  229     }
  230   }
  231 
  232   # the document postamble
  233   $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{postamble});
  234 
  235   return $tex;
  236 }
  237 
  238 sub getSetTeX {
  239   my ($self, $setName) = @_;
  240   my $ce = $self->{courseEnvironment};
  241   my $wwdb = $self->{wwdb};
  242   my $user = $self->{r}->param("user");
  243   my @problemNumbers = sort { $a <=> $b } $wwdb->getProblems($user, $setName);
  244 
  245   # get header and footer
  246   my $setHeader = $wwdb->getSet($user, $setName)->set_header
  247     || $ce->{webworkFiles}->{hardcopySnippets}->{setHeader};
  248   # database doesn't support the following yet :(
  249   #my $setFooter = $wwdb->getSet($user, $setName)->set_footer
  250   # || $ce->{webworkFiles}->{hardcopySnippets}->{setFooter};
  251   # so we don't allow per-set customization, which is probably okay :)
  252   my $setFooter = $ce->{webworkFiles}->{hardcopySnippets}->{setFooter};
  253 
  254   my $tex = "";
  255 
  256   # render header
  257   $tex .= texBlockComment("BEGIN $setName : $setHeader");
  258   $tex .= $self->getProblemTeX($setName, 0, $setHeader);
  259 
  260   # render each problem
  261   while (my $problemNumber = shift @problemNumbers) {
  262     $tex .= texBlockComment("BEGIN $setName : $problemNumber");
  263     $tex .= $self->getProblemTeX($setName, $problemNumber);
  264     if (@problemNumbers) {
  265       # divide problems, but not after the last problem
  266       $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{problemDivider});
  267     }
  268   }
  269 
  270   # render footer
  271   $tex .= texBlockComment("BEGIN $setName : $setFooter");
  272   $tex .= $self->getProblemTeX($setName, 0, $setFooter);
  273 
  274   return $tex;
  275 }
  276 
  277 sub getProblemTeX {
  278   my ($self, $setName, $problemNumber, $pgFile) = @_;
  279   my $r = $self->{r};
  280   my $ce = $self->{courseEnvironment};
  281 
  282   my $wwdb = $self->{wwdb};
  283   my $cldb = $self->{cldb};
  284   my $user = $cldb->getUser($r->param("user"));
  285   my $set  = $wwdb->getSet($user->id, $setName);
  286   my $psvn = $wwdb->getPSVN($user->id, $setName);
  287 
  288   # decide what to do about problem number
  289   my $problem;
  290   if ($problemNumber) {
  291     $problem = $wwdb->getProblem($user->id, $setName, $problemNumber);
  292   } elsif ($pgFile) {
  293     $problem = WeBWorK::Problem->new(
  294       id => 0,
  295       set_id => $set->id,
  296       login_id => $user->id,
  297       source_file => $pgFile,
  298       # the rest of Problem's fields are not needed, i think
  299     );
  300   }
  301 
  302   my $pg = WeBWorK::PG->new(
  303     $ce,
  304     $user,
  305     $r->param('key'),
  306     $set,
  307     $problem,
  308     $psvn,
  309     {}, # no form fields!
  310     { # translation options
  311       displayMode     => "tex",
  312       showHints       => 0,
  313       showSolutions   => 0,
  314       processAnswers  => 0,
  315     },
  316   );
  317 
  318   if ($pg->{warnings} ne "") {
  319     push @{$self->{warnings}}, {
  320       set     => $setName,
  321       problem => $problemNumber,
  322       message => $pg->{warnings},
  323     };
  324   }
  325 
  326   if ($pg->{flags}->{error_flag}) {
  327     push @{$self->{errors}}, {
  328       set     => $setName,
  329       problem => $problemNumber,
  330       message => $pg->{errors},
  331       context => $pg->{body_text},
  332     };
  333     # if there was an error, body_text contains
  334     # the error context, not TeX code
  335     $pg->{body_text} = undef;
  336   }
  337 
  338   return $pg->{body_text};
  339 }
  340 
  341 sub texInclude {
  342   my ($self, $texFile) = @_;
  343   my $tex = "";
  344 
  345   $tex .= texBlockComment("BEGIN: $texFile");
  346   eval {
  347     $tex .= readFile($texFile)
  348   };
  349   if ($@) {
  350     $tex .= texBlockComment($@);
  351   }
  352 
  353   return $tex;
  354 }
  355 
  356 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9