Parent Directory
|
Revision Log
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 |