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