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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 722 - (view) (download) (as text)

1 : sh002i 478 ################################################################################
2 : sh002i 494 # WeBWorK mod_perl (c) 2000-2002 WeBWorK Project
3 : sh002i 478 # $Id$
4 :     ################################################################################
5 :    
6 :     package WeBWorK::ContentGenerator::Hardcopy;
7 :    
8 :     =head1 NAME
9 :    
10 : sh002i 641 WeBWorK::ContentGenerator::Hardcopy - generate a PDF version of one or more
11 : sh002i 478 problem sets.
12 :    
13 :     =cut
14 :    
15 :     use strict;
16 :     use warnings;
17 :     use base qw(WeBWorK::ContentGenerator);
18 : sh002i 641 #use Apache::Constants qw(:common);
19 : sh002i 478 use CGI qw();
20 : sh002i 502 use File::Path qw(rmtree);
21 :     use File::Temp qw(tempdir);
22 :     use WeBWorK::DB::Classlist;
23 :     use WeBWorK::DB::WW;
24 : sh002i 478 use WeBWorK::Form;
25 :     use WeBWorK::Utils qw(readFile);
26 :    
27 : sh002i 641 sub texBlockComment(@) { return "\n".("%"x80)."\n%% ".join("", @_)."\n".("%"x80)."\n\n"; }
28 : sh002i 492
29 : sh002i 502 sub initialize {
30 : sh002i 641 my ($self, $singleSet, undef) = @_;
31 :    
32 :     my $r = $self->{r};
33 : sh002i 502 my $ce = $self->{courseEnvironment};
34 : sh002i 641 my @sets = $r->param("set");
35 :    
36 :     if (length $singleSet > 0) {
37 :     $singleSet =~ s/^set//;
38 :     unshift @sets, $singleSet;
39 :     }
40 :    
41 : sh002i 502 $self->{cldb} = WeBWorK::DB::Classlist->new($ce);
42 :     $self->{wwdb} = WeBWorK::DB::WW->new($ce);
43 : sh002i 641 $self->{sets} = \@sets;
44 :     $self->{errors} = [];
45 :     $self->{warnings} = [];
46 : sh002i 502 }
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 : sh002i 547 return "Hardcopy Generator";
63 : sh002i 502 }
64 :    
65 :     sub body {
66 : sh002i 641 my $self = shift;
67 : sh002i 478
68 : sh002i 669 STUFF: {
69 :     my $courseName = $self->{courseEnvironment}->{courseName};
70 : malsyned 722 my $effectiveUserName = $self->{r}->param("effectiveUser");
71 : sh002i 669 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 : malsyned 722 $fileName = "$courseName.$effectiveUserName.multiset.pdf"
97 : sh002i 669 } elsif (@sets == 1) {
98 :     # only one set
99 :     my $setName = $sets[0];
100 : malsyned 722 $fileName = "$courseName.$effectiveUserName.$setName.pdf";
101 : sh002i 669 } else {
102 : malsyned 722 $fileName = "$courseName.$effectiveUserName.pdf";
103 : sh002i 669 }
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 : sh002i 641 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 : sh002i 669 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 : sh002i 641 }
130 : sh002i 669
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 : sh002i 641 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 : sh002i 669 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 : sh002i 641 }
158 :     }
159 :    
160 : sh002i 669 # 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 : sh002i 641 return "";
175 : sh002i 502 }
176 :    
177 : sh002i 641 # -----
178 :    
179 : sh002i 502 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 : sh002i 492 my $ce = $self->{courseEnvironment};
185 : sh002i 502
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 : sh002i 562 my $logFile = "$wd/hardcopy.log";
191 : sh002i 502
192 :     # write the tex file
193 :     local *TEX;
194 : sh002i 641 open TEX, ">", $texFile or die "Failed to open $texFile: $!\n";
195 : sh002i 502 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 : sh002i 492 my $pdflatex = $ce->{externalPrograms}->{pdflatex};
201 : sh002i 641 system "cd $wd && $pdflatex $texFile" and die "Failed to call pdflatex: $!\n";
202 : sh002i 478
203 : sh002i 502 if (-e $pdfFile) {
204 :     # move resulting PDF file to appropriate location
205 : sh002i 641 system "/bin/mv", $pdfFile, $finalFile and die "Failed to mv: $!\n";
206 : sh002i 502 }
207 :    
208 :     # remove temporary directory
209 : sh002i 616 rmtree($wd, 0, 1);
210 : sh002i 502
211 : sh002i 641 -e $finalFile or die "Failed to create $finalFile for no apparent reason.\n";
212 : sh002i 478 }
213 :    
214 : sh002i 502 # -----
215 :    
216 :     sub getMultiSetTeX {
217 : sh002i 492 my ($self, @sets) = @_;
218 :     my $ce = $self->{courseEnvironment};
219 : sh002i 502 my $tex = "";
220 : sh002i 492
221 : sh002i 502 # the document preamble
222 :     $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{preamble});
223 : sh002i 492
224 : sh002i 641 while (defined (my $setName = shift @sets)) {
225 :     $tex .= $self->getSetTeX($setName);
226 : sh002i 492 if (@sets) {
227 :     # divide sets, but not after the last set
228 : sh002i 502 $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{setDivider});
229 : sh002i 492 }
230 :     }
231 :    
232 : sh002i 502 # the document postamble
233 :     $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{postamble});
234 :    
235 :     return $tex;
236 : sh002i 492 }
237 :    
238 : sh002i 478 sub getSetTeX {
239 :     my ($self, $setName) = @_;
240 :     my $ce = $self->{courseEnvironment};
241 :     my $wwdb = $self->{wwdb};
242 : malsyned 722 my $effectiveUserName = $self->{r}->param("effectiveUser");
243 :     my @problemNumbers = sort { $a <=> $b } $wwdb->getProblems($effectiveUserName, $setName);
244 : sh002i 478
245 : sh002i 492 # get header and footer
246 : malsyned 722 my $setHeader = $wwdb->getSet($effectiveUserName, $setName)->set_header
247 : sh002i 492 || $ce->{webworkFiles}->{hardcopySnippets}->{setHeader};
248 :     # database doesn't support the following yet :(
249 : malsyned 722 #my $setFooter = $wwdb->getSet($effectiveUserName, $setName)->set_footer
250 : sh002i 492 # || $ce->{webworkFiles}->{hardcopySnippets}->{setFooter};
251 : sh002i 502 # so we don't allow per-set customization, which is probably okay :)
252 :     my $setFooter = $ce->{webworkFiles}->{hardcopySnippets}->{setFooter};
253 : sh002i 478
254 : sh002i 502 my $tex = "";
255 :    
256 : sh002i 492 # render header
257 : sh002i 502 $tex .= texBlockComment("BEGIN $setName : $setHeader");
258 : sh002i 547 $tex .= $self->getProblemTeX($setName, 0, $setHeader);
259 : sh002i 478
260 :     # render each problem
261 : sh002i 492 while (my $problemNumber = shift @problemNumbers) {
262 : sh002i 502 $tex .= texBlockComment("BEGIN $setName : $problemNumber");
263 :     $tex .= $self->getProblemTeX($setName, $problemNumber);
264 : sh002i 492 if (@problemNumbers) {
265 :     # divide problems, but not after the last problem
266 : sh002i 502 $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{problemDivider});
267 : sh002i 492 }
268 : sh002i 478 }
269 :    
270 : sh002i 492 # render footer
271 : sh002i 502 $tex .= texBlockComment("BEGIN $setName : $setFooter");
272 :     $tex .= $self->getProblemTeX($setName, 0, $setFooter);
273 :    
274 :     return $tex;
275 : sh002i 478 }
276 :    
277 :     sub getProblemTeX {
278 : sh002i 502 my ($self, $setName, $problemNumber, $pgFile) = @_;
279 : sh002i 478 my $r = $self->{r};
280 :     my $ce = $self->{courseEnvironment};
281 :    
282 : sh002i 502 my $wwdb = $self->{wwdb};
283 :     my $cldb = $self->{cldb};
284 : malsyned 722 my $effectiveUser = $cldb->getUser($r->param("effectiveUser"));
285 :     my $set = $wwdb->getSet($effectiveUser->id, $setName);
286 :     my $psvn = $wwdb->getPSVN($effectiveUser->id, $setName);
287 : sh002i 502
288 :     # decide what to do about problem number
289 :     my $problem;
290 :     if ($problemNumber) {
291 : malsyned 722 $problem = $wwdb->getProblem($effectiveUser->id, $setName, $problemNumber);
292 : sh002i 502 } elsif ($pgFile) {
293 :     $problem = WeBWorK::Problem->new(
294 :     id => 0,
295 :     set_id => $set->id,
296 : malsyned 722 login_id => $effectiveUser->id,
297 : sh002i 502 source_file => $pgFile,
298 :     # the rest of Problem's fields are not needed, i think
299 :     );
300 :     }
301 :    
302 : sh002i 478 my $pg = WeBWorK::PG->new(
303 :     $ce,
304 : malsyned 722 $effectiveUser,
305 : sh002i 478 $r->param('key'),
306 : sh002i 502 $set,
307 :     $problem,
308 :     $psvn,
309 :     {}, # no form fields!
310 : sh002i 478 { # translation options
311 :     displayMode => "tex",
312 :     showHints => 0,
313 :     showSolutions => 0,
314 :     processAnswers => 0,
315 :     },
316 :     );
317 :    
318 : sh002i 641 if ($pg->{warnings} ne "") {
319 :     push @{$self->{warnings}}, {
320 :     set => $setName,
321 :     problem => $problemNumber,
322 :     message => $pg->{warnings},
323 :     };
324 :     }
325 : sh002i 607
326 : sh002i 641 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 : sh002i 478 return $pg->{body_text};
339 :     }
340 :    
341 : sh002i 492 sub texInclude {
342 :     my ($self, $texFile) = @_;
343 : sh002i 502 my $tex = "";
344 : sh002i 492
345 : sh002i 502 $tex .= texBlockComment("BEGIN: $texFile");
346 : sh002i 492 eval {
347 : sh002i 502 $tex .= readFile($texFile)
348 : sh002i 492 };
349 :     if ($@) {
350 : sh002i 502 $tex .= texBlockComment($@);
351 : sh002i 492 }
352 : sh002i 502
353 :     return $tex;
354 : sh002i 478 }
355 :    
356 :     1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9