Parent Directory
|
Revision Log
continuing work on hardcopy generation. changed interface to PG.pm to take user, problem, and set objects instead of names. -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::Test - 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 = shift; 31 my $ce = $self->{courseEnvironment}; 32 $self->{cldb} = WeBWorK::DB::Classlist->new($ce); 33 $self->{wwdb} = WeBWorK::DB::WW->new($ce); 34 } 35 36 sub path { 37 my ($self, undef, $args) = @_; 38 39 my $ce = $self->{courseEnvironment}; 40 my $root = $ce->{webworkURLs}->{root}; 41 my $courseName = $ce->{courseName}; 42 return $self->pathMacro($args, 43 "Home" => "$root", 44 $courseName => "$root/$courseName", 45 "Hardcopy Generator" => "", 46 ); 47 } 48 49 sub title { 50 return "Hardcore Generator"; 51 } 52 53 sub body { 54 my ($self, $singleSet) = @_; 55 $singleSet =~ s/^set//; 56 my $r = $self->{r}; 57 my $ce = $self->{courseEnvironment}; 58 $self->{wwdb} = WeBWorK::DB::WW->new($ce); 59 60 my @sets = $r->param("set"); 61 unshift @sets, $singleSet; 62 unless (@sets) { 63 print CGI::p("No problem sets were specified."); 64 return OK; 65 } 66 67 print CGI::p("Generating your hardcopy..."); 68 my $url = $self->makeHardcopy(@sets); 69 if ($url) { 70 print CGI::p("Ok, your hardcopy is ready. Click the following link to download it."); 71 print CGI::p({-align=>"center"}, 72 CGI::big(CGI::a({-href=>$url}, "Download PDF Hardcopy")) 73 ); 74 } else { 75 print CGI::p("Hmm, looks like I was unable to generate the hardcopy you requested. I'm really sorry... :("); 76 } 77 78 return ""; 79 } 80 81 # ----- 82 83 sub makeHardcopy { 84 my ($self, @sets) = @_; 85 my $courseName = $self->{courseEnvironment}->{courseName}; 86 my $userName = $self->{r}->param("user"); 87 my $tempDir = $self->{courseEnvironment}->{courseDirs}->{html_temp} 88 . "/hardcopy"; 89 my $tempURL = $self->{courseEnvironment}->{courseURLs}->{html_temp} 90 . "/hardcopy"; 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 my $tex = $self->getMultiSetTeX(@sets); 106 $self->latex2pdf($tex, $tempDir, $fileName) or return; 107 108 return "$tempURL/$fileName"; 109 } 110 111 sub latex2pdf { 112 # this is a little ad-hoc function which I will replace with a LaTeX 113 # module at some point (or put it in Utils). 114 my ($self, $tex, $fileBase, $fileName) = @_; 115 my $finalFile = "$fileBase/$fileName"; 116 my $ce = $self->{courseEnvironment}; 117 118 # create a temporary directory for tex to shit in 119 my $wd = tempdir("webwork-hardcopy-XXXXXXXX", TMPDIR => 1); 120 my $texFile = "$wd/hardcopy.tex"; 121 my $pdfFile = "$wd/hardcopy.pdf"; 122 123 # write the tex file 124 local *TEX; 125 open TEX, ">", $texFile; 126 print TEX $tex; 127 close TEX; 128 129 # call pdflatex - we don't want to chdir in the mod_perl process, as 130 # that might step on the feet of other things (esp. in Apache 2.0) 131 my $pdflatex = $ce->{externalPrograms}->{pdflatex}; 132 system "cd $wd && $pdflatex $texFile"; 133 134 if (-e $pdfFile) { 135 # move resulting PDF file to appropriate location 136 my $mv = $ce->{externalPrograms}->{mv}; 137 system $mv, $pdfFile, $finalFile and die "Failed to mv: $!\n"; 138 } 139 140 # remove temporary directory 141 rmtree($wd, 0, 1); 142 143 return -e $finalFile; 144 } 145 146 # ----- 147 148 sub getMultiSetTeX { 149 my ($self, @sets) = @_; 150 my $ce = $self->{courseEnvironment}; 151 my $tex = ""; 152 153 # the document preamble 154 $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{preamble}); 155 156 while (my $set = shift @sets) { 157 $tex .= $self->getSetTeX($set); 158 if (@sets) { 159 # divide sets, but not after the last set 160 $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{setDivider}); 161 } 162 } 163 164 # the document postamble 165 $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{postamble}); 166 167 return $tex; 168 } 169 170 sub getSetTeX { 171 my ($self, $setName) = @_; 172 my $ce = $self->{courseEnvironment}; 173 my $wwdb = $self->{wwdb}; 174 my $user = $self->{r}->param("user"); 175 my @problemNumbers = sort { $a <=> $b } $wwdb->getProblems($user, $setName); 176 177 # get header and footer 178 my $setHeader = $wwdb->getSet($user, $setName)->set_header 179 || $ce->{webworkFiles}->{hardcopySnippets}->{setHeader}; 180 # database doesn't support the following yet :( 181 #my $setFooter = $wwdb->getSet($user, $setName)->set_footer 182 # || $ce->{webworkFiles}->{hardcopySnippets}->{setFooter}; 183 # so we don't allow per-set customization, which is probably okay :) 184 my $setFooter = $ce->{webworkFiles}->{hardcopySnippets}->{setFooter}; 185 186 my $tex = ""; 187 188 # render header 189 $tex .= texBlockComment("BEGIN $setName : $setHeader"); 190 #$tex .= $self->getProblemTeX($setName, 0, $setHeader); 191 192 # render each problem 193 while (my $problemNumber = shift @problemNumbers) { 194 $tex .= texBlockComment("BEGIN $setName : $problemNumber"); 195 $tex .= $self->getProblemTeX($setName, $problemNumber); 196 if (@problemNumbers) { 197 # divide problems, but not after the last problem 198 $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{problemDivider}); 199 } 200 } 201 202 # render footer 203 $tex .= texBlockComment("BEGIN $setName : $setFooter"); 204 $tex .= $self->getProblemTeX($setName, 0, $setFooter); 205 206 return $tex; 207 } 208 209 sub getProblemTeX { 210 my ($self, $setName, $problemNumber, $pgFile) = @_; 211 my $r = $self->{r}; 212 my $ce = $self->{courseEnvironment}; 213 214 my $wwdb = $self->{wwdb}; 215 my $cldb = $self->{cldb}; 216 my $user = $cldb->getUser($r->param("user")); 217 my $set = $wwdb->getSet($user->id, $setName); 218 my $psvn = $wwdb->getPSVN($user->id, $setName); 219 220 # decide what to do about problem number 221 my $problem; 222 if ($problemNumber) { 223 $problem = $wwdb->getProblem($user->id, $setName, $problemNumber); 224 } elsif ($pgFile) { 225 $problem = WeBWorK::Problem->new( 226 id => 0, 227 set_id => $set->id, 228 login_id => $user->id, 229 source_file => $pgFile, 230 # the rest of Problem's fields are not needed, i think 231 ); 232 } 233 234 my $pg = WeBWorK::PG->new( 235 $ce, 236 $user, 237 $r->param('key'), 238 $set, 239 $problem, 240 $psvn, 241 {}, # no form fields! 242 { # translation options 243 displayMode => "tex", 244 showHints => 0, 245 showSolutions => 0, 246 processAnswers => 0, 247 }, 248 ); 249 250 # *** # handle errors/warnings here! 251 return $pg->{body_text}; 252 } 253 254 sub texInclude { 255 my ($self, $texFile) = @_; 256 my $tex = ""; 257 258 $tex .= texBlockComment("BEGIN: $texFile"); 259 eval { 260 $tex .= readFile($texFile) 261 }; 262 if ($@) { 263 $tex .= texBlockComment($@); 264 } 265 266 return $tex; 267 } 268 269 1;
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |