Parent Directory
|
Revision Log
Added caching code for reading PGbascimacros and PGanswermacros as well as PG.pl, dangerousMacros and IO.pl into a cached safe compartment and sharing the subroutines with the current safe compartment It speeds up each problem significantly (about .2 seconds on webwork3). Changes are required in PGbasicmacros and PGanswermacros to fix assumptions that are not met when a file is compiled and run in different name spaces. The caching code is turned off by default. It must be turned on by by changing commenting in lines 133 to 147 in WeBWorK::PG:Local.pm --Mike
1 ################################################################################ 2 # WeBWorK mod_perl (c) 2000-2002 WeBWorK Project 3 # $Id$ 4 ################################################################################ 5 6 package WeBWorK::PG::Local; 7 8 =head1 NAME 9 10 WeBWorK::PG::Local - Use the WeBWorK::PG API to invoke a local 11 WeBWorK::PG::Translator object. 12 13 =head1 DESCRIPTION 14 15 WeBWorK::PG::Local encapsulates the PG translation process, making multiple 16 calls to WeBWorK::PG::Translator. Much of the flexibility of the Translator is 17 hidden, instead making choices that are appropriate for the webwork-modperl 18 system 19 20 It implements the WeBWorK::PG interface and uses a local 21 WeBWorK::PG::Translator to perform problem rendering. See the documentation for 22 the WeBWorK::PG module for information about the API. 23 24 =cut 25 26 use strict; 27 use warnings; 28 use File::Path qw(rmtree); 29 use WeBWorK::PG::ImageGenerator; 30 use WeBWorK::PG::Translator; 31 use WeBWorK::Utils qw(readFile formatDateTime writeTimingLogEntry makeTempDirectory); 32 BEGIN { 33 34 # This safe compartment is used to read the large macro files such as 35 # PG.pl, PGbasicmacros.pl and PGanswermacros and cache the results so that 36 # future calls have preloaded versions of these large files. 37 # This saves a significant amount of time. 38 39 $WeBWorK::PG::Local::safeCache = new Safe; 40 # warn "Creating new Safe cache compartment ".$WeBWorK::PG::Local::safeCache->root; 41 } 42 sub new { 43 my $invocant = shift; 44 my $class = ref($invocant) || $invocant; 45 my ( 46 $ce, 47 $user, 48 $key, 49 $set, 50 $problem, 51 $psvn, 52 $formFields, # in CGI::Vars format 53 $translationOptions, # hashref containing options for the 54 # translator, such as whether to show 55 # hints and the display mode to use 56 ) = @_; 57 58 # write timing log entry 59 writeTimingLogEntry($ce, "WeBWorK::PG::new", 60 "user=".$user->user_id.",problem=".$ce->{courseName}."/".$set->set_id."/".$problem->problem_id.",mode=".$translationOptions->{displayMode}, 61 "begin"); 62 63 # install a local warn handler to collect warnings 64 my $warnings = ""; 65 local $SIG{__WARN__} = sub { $warnings .= shift } 66 if $ce->{pg}->{options}->{catchWarnings}; 67 68 # create a Translator 69 #warn "PG: creating a Translator\n"; 70 my $translator = WeBWorK::PG::Translator->new; 71 72 # set the directory hash 73 #warn "PG: setting the directory hash\n"; 74 $translator->rh_directories({ 75 courseScriptsDirectory => $ce->{pg}->{directories}->{macros}, 76 macroDirectory => $ce->{courseDirs}->{macros}, 77 templateDirectory => $ce->{courseDirs}->{templates}, 78 tempDirectory => $ce->{courseDirs}->{html_temp}, 79 }); 80 81 # evaluate modules and "extra packages" 82 #warn "PG: evaluating modules and \"extra packages\"\n"; 83 my @modules = @{ $ce->{pg}->{modules} }; 84 foreach my $module_packages_ref (@modules) { 85 my ($module, @extra_packages) = @$module_packages_ref; 86 # the first item is the main package 87 $translator->evaluate_modules($module); 88 # the remaining items are "extra" packages 89 $translator->load_extra_packages(@extra_packages); 90 } 91 92 # set the environment (from defineProblemEnvir) 93 #warn "PG: setting the environment (from defineProblemEnvir)\n"; 94 my $envir = defineProblemEnvir( 95 $ce, 96 $user, 97 $key, 98 $set, 99 $problem, 100 $psvn, 101 $formFields, 102 $translationOptions, 103 ); 104 $translator->environment($envir); 105 106 # initialize the Translator 107 #warn "PG: initializing the Translator\n"; 108 $translator->initialize(); 109 # $translator->dumpSafe; # debugging code 110 ############################################################################### 111 # Preload the macros files which are used routinely: PG.pl, dangerousMacros.pl, IO.pl 112 # PGbasicmacros.pl and PGanswermacros.pl 113 # Preloading the last two files safes a significant amount of time. 114 ############################################################################### 115 116 # IO.pl, PG.pl, and dangerousMacros.pl are loaded using unrestricted_load 117 # This is hard wired into the Translator::pre_load_macro_files subroutine 118 # I'd like to change this at some point to have the same sort of interface to global.conf 119 # that the module loading does -- have a list of macros to load unrestrictedly. 120 121 # This has been replaced by the pre_load_macro_files subroutine. It loads AND caches the files. 122 # While PG.pl and dangerousMacros are not large, they are referred to by PGbasicmacros and PGanswermacros. 123 # Because these are loaded into the cached name space (e.g. Safe::Root1::) all calls to, say NEW_ANSWER_NAME 124 # are actually calls to Safe::Root1::NEW_ANSWER_NAME. It is useful to have these names inside the Safe::Root1: 125 # cached safe compartment. (NEW_ANSWER_NAME and all other subroutine names are also automatically exported into 126 # the current safe compartment Safe::Rootx:: 127 128 # The headers of both PGbasicmacros and PGanswermacros has code that insures that the constants used are imported into 129 # the current safe compartment. This involves evaluating references to, say $main::displayMode, at runtime to insure that main 130 # refers to Safe::Rootx:: and NOT to Safe::Root1::, which is the value of main:: at compile time. 131 132 133 ############################################################################### 134 # TO ENABLE CACHEING UNCOMMENT THE CACHEING CODE AND COMMENT OUT THE STANDARD LOADING CODE 135 # On webwork3 cached code is .2 seconds faster than non-cached code for an existing child. 136 137 # CACHING CODE: 138 # $translator->pre_load_macro_files($WeBWorK::PG::Local::safeCache, $ce->{pg}->{directories}->{macros}, 139 # 'PG.pl', 'dangerousMacros.pl','IO.pl','PGbasicmacros.pl','PGanswermacros.pl'); 140 141 # STANDARD LOADING CODE: 142 foreach (qw(IO.pl PG.pl dangerousMacros.pl)) { 143 my $macroPath = $ce->{pg}->{directories}->{macros} . "/$_"; 144 my $err = $translator->unrestricted_load($macroPath); 145 warn "Error while loading $macroPath: |$err|" if $err; 146 } 147 ############################################################################### 148 149 # set the opcode mask (using default values) 150 #warn "PG: setting the opcode mask (using default values)\n"; 151 $translator->set_mask(); 152 153 # store the problem source 154 #warn "PG: storing the problem source\n"; 155 my $sourceFile = $problem->source_file; 156 $sourceFile = $ce->{courseDirs}->{templates}."/".$sourceFile 157 unless ($sourceFile =~ /^\//); 158 eval { $translator->source_string(readFile($sourceFile)) }; 159 if ($@) { 160 # well, we couldn't get the problem source, for some reason. 161 return bless { 162 translator => $translator, 163 head_text => "", 164 body_text => <<EOF, 165 WeBWorK::Utils::readFile($sourceFile) says: 166 $@ 167 EOF 168 answers => {}, 169 result => {}, 170 state => {}, 171 errors => "Failed to read the problem source file.", 172 warnings => $warnings, 173 flags => {error_flag => 1}, 174 }, $class; 175 } 176 177 # install a safety filter (&safetyFilter) 178 #warn "PG: installing a safety filter\n"; 179 $translator->rf_safety_filter(\&safetyFilter); 180 181 # write timing log entry -- the translator is now all set up 182 writeTimingLogEntry($ce, "WeBWorK::PG::new", 183 "initialized", 184 "intermediate"); 185 186 # translate the PG source into text 187 #warn "PG: translating the PG source into text\n"; 188 $translator->translate(); 189 190 # after we're done translating, we may have to clean up after the 191 # translator: 192 193 # for example, HTML_img mode uses a tempdir for dvipng's temp files.\ 194 # We have to remove it. 195 if ($envir->{dvipngTempDir}) { 196 rmtree($envir->{dvipngTempDir}, 0, 0); 197 } 198 199 # HTML_dpng, on the other hand, uses an ImageGenerator. We have to 200 # render the queued equations. 201 if ($envir->{imagegen}) { 202 my $sourceFile = $ce->{courseDirs}->{templates} . "/" . $problem->source_file; 203 my %mtimeOption = -e $sourceFile 204 ? (mtime => (stat $sourceFile)[9]) 205 : (); 206 207 $envir->{imagegen}->render( 208 refresh => $translationOptions->{refreshMath2img}, 209 %mtimeOption, 210 ); 211 } 212 213 my ($result, $state); # we'll need these on the other side of the if block! 214 if ($translationOptions->{processAnswers}) { 215 216 # process student answers 217 #warn "PG: processing student answers\n"; 218 $translator->process_answers($formFields); 219 220 # retrieve the problem state and give it to the translator 221 #warn "PG: retrieving the problem state and giving it to the translator\n"; 222 $translator->rh_problem_state({ 223 recorded_score => $problem->status, 224 num_of_correct_ans => $problem->num_correct, 225 num_of_incorrect_ans => $problem->num_incorrect, 226 }); 227 228 # determine an entry order -- the ANSWER_ENTRY_ORDER flag is built by 229 # the PG macro package (PG.pl) 230 #warn "PG: determining an entry order\n"; 231 my @answerOrder = 232 $translator->rh_flags->{ANSWER_ENTRY_ORDER} 233 ? @{ $translator->rh_flags->{ANSWER_ENTRY_ORDER} } 234 : keys %{ $translator->rh_evaluated_answers }; 235 236 # install a grader -- use the one specified in the problem, 237 # or fall back on the default from the course environment. 238 # (two magic strings are accepted, to avoid having to 239 # reference code when it would be difficult.) 240 #warn "PG: installing a grader\n"; 241 my $grader = $translator->rh_flags->{PROBLEM_GRADER_TO_USE} 242 || $ce->{pg}->{options}->{grader}; 243 $grader = $translator->rf_std_problem_grader 244 if $grader eq "std_problem_grader"; 245 $grader = $translator->rf_avg_problem_grader 246 if $grader eq "avg_problem_grader"; 247 die "Problem grader $grader is not a CODE reference." 248 unless ref $grader eq "CODE"; 249 $translator->rf_problem_grader($grader); 250 251 # grade the problem 252 #warn "PG: grading the problem\n"; 253 ($result, $state) = $translator->grade_problem( 254 answers_submitted => $translationOptions->{processAnswers}, 255 ANSWER_ENTRY_ORDER => \@answerOrder, 256 ); 257 258 } 259 260 # write timing log entry 261 writeTimingLogEntry($ce, "WeBWorK::PG::new", "", "end"); 262 263 # return an object which contains the translator and the results of 264 # the translation process. this is DIFFERENT from the "format expected 265 # by Webwork.pm (and I believe processProblem8, but check.)" 266 return bless { 267 translator => $translator, 268 head_text => ${ $translator->r_header }, 269 body_text => ${ $translator->r_text }, 270 answers => $translator->rh_evaluated_answers, 271 result => $result, 272 state => $state, 273 errors => $translator->errors, 274 warnings => $warnings, 275 flags => $translator->rh_flags, 276 }, $class; 277 } 278 279 # ----- 280 281 sub defineProblemEnvir { 282 my ( 283 $ce, 284 $user, 285 $key, 286 $set, 287 $problem, 288 $psvn, 289 $formFields, 290 $options, 291 ) = @_; 292 293 my %envir; 294 295 # ---------------------------------------------------------------------- 296 297 # PG environment variables 298 # from docs/pglanguage/pgreference/environmentvariables as of 06/25/2002 299 # any changes are noted by "ADDED:" or "REMOVED:" 300 301 # Vital state information 302 # ADDED: displayHintsQ, displaySolutionsQ, refreshMath2img, 303 # texDisposition 304 305 $envir{psvn} = $set->psvn; 306 $envir{psvnNumber} = $envir{psvn}; 307 $envir{probNum} = $problem->problem_id; 308 $envir{questionNumber} = $envir{probNum}; 309 $envir{fileName} = $problem->source_file; 310 $envir{probFileName} = $envir{fileName}; 311 $envir{problemSeed} = $problem->problem_seed; 312 $envir{displayMode} = translateDisplayModeNames($options->{displayMode}); 313 $envir{languageMode} = $envir{displayMode}; 314 $envir{outputMode} = $envir{displayMode}; 315 $envir{displayHintsQ} = $options->{showHints}; 316 $envir{displaySolutionsQ} = $options->{showSolutions}; 317 # FIXME: this is HTML_img specific 318 #$envir{refreshMath2img} = $options->{refreshMath2img}; 319 $envir{texDisposition} = "pdf"; # in webwork-modperl, we use pdflatex 320 321 # Problem Information 322 # ADDED: courseName, formatedDueDate 323 324 $envir{openDate} = $set->open_date; 325 $envir{formattedOpenDate} = formatDateTime($envir{openDate}); 326 $envir{dueDate} = $set->due_date; 327 $envir{formattedDueDate} = formatDateTime($envir{dueDate}); 328 $envir{formatedDueDate} = $envir{formattedDueDate}; # typo in many header files 329 $envir{answerDate} = $set->answer_date; 330 $envir{formattedAnswerDate} = formatDateTime($envir{answerDate}); 331 $envir{numOfAttempts} = ($problem->num_correct || 0) + ($problem->num_incorrect || 0); 332 $envir{problemValue} = $problem->value; 333 $envir{sessionKey} = $key; 334 $envir{courseName} = $ce->{courseName}; 335 336 # Student Information 337 # ADDED: studentID 338 339 $envir{sectionName} = $user->section; 340 $envir{sectionNumber} = $envir{sectionName}; 341 $envir{recitationName} = $user->recitation; 342 $envir{recitationNumber} = $envir{recitationName}; 343 $envir{setNumber} = $set->set_id; 344 $envir{studentLogin} = $user->user_id; 345 $envir{studentName} = $user->first_name . " " . $user->last_name; 346 $envir{studentID} = $user->student_id; 347 348 # Answer Information 349 # REMOVED: refSubmittedAnswers 350 351 $envir{inputs_ref} = $formFields; 352 353 # External Programs 354 # ADDED: externalLaTeXPath, externalDvipngPath, 355 # externalGif2EpsPath, externalPng2EpsPath 356 357 $envir{externalTTHPath} = $ce->{externalPrograms}->{tth}; 358 $envir{externalLaTeXPath} = $ce->{externalPrograms}->{latex}; 359 $envir{externalDvipngPath} = $ce->{externalPrograms}->{dvipng}; 360 $envir{externalGif2EpsPath} = $ce->{externalPrograms}->{gif2eps}; 361 $envir{externalPng2EpsPath} = $ce->{externalPrograms}->{png2eps}; 362 $envir{externalGif2PngPath} = $ce->{externalPrograms}->{gif2png}; 363 364 # Directories and URLs 365 # REMOVED: courseName 366 # ADDED: dvipngTempDir 367 368 $envir{cgiDirectory} = undef; 369 $envir{cgiURL} = undef; 370 $envir{classDirectory} = undef; 371 $envir{courseScriptsDirectory} = $ce->{pg}->{directories}->{macros}."/"; 372 $envir{htmlDirectory} = $ce->{courseDirs}->{html}."/"; 373 $envir{htmlURL} = $ce->{courseURLs}->{html}."/"; 374 $envir{macroDirectory} = $ce->{courseDirs}->{macros}."/"; 375 $envir{templateDirectory} = $ce->{courseDirs}->{templates}."/"; 376 $envir{tempDirectory} = $ce->{courseDirs}->{html_temp}."/"; 377 $envir{tempURL} = $ce->{courseURLs}->{html_temp}."/"; 378 $envir{scriptDirectory} = undef; 379 $envir{webworkDocsURL} = $ce->{webworkURLs}->{docs}."/"; 380 # FIXME: this is HTML_img mode-specific 381 #$envir{dvipngTempDir} = $options->{displayMode} eq 'images' 382 # ? makeTempDirectory($envir{tempDirectory}, "webwork-dvipng") 383 # : undef; 384 385 # Information for sending mail 386 387 $envir{mailSmtpServer} = $ce->{mail}->{smtpServer}; 388 $envir{mailSmtpSender} = $ce->{mail}->{smtpSender}; 389 $envir{ALLOW_MAIL_TO} = $ce->{mail}->{allowedRecipients}; 390 391 # Default values for evaluating answers 392 393 my $ansEvalDefaults = $ce->{pg}->{ansEvalDefaults}; 394 $envir{$_} = $ansEvalDefaults->{$_} foreach (keys %$ansEvalDefaults); 395 396 # ---------------------------------------------------------------------- 397 398 my $basename = "equation-$envir{psvn}.$envir{probNum}"; 399 $basename .= ".$envir{problemSeed}" if $envir{problemSeed}; 400 401 # Object for generating equation images 402 $envir{imagegen} = WeBWorK::PG::ImageGenerator->new( 403 tempDir => $ce->{webworkDirs}->{tmp}, # global temp dir 404 dir => $envir{tempDirectory}, 405 url => $envir{tempURL}, 406 basename => $basename, 407 latex => $envir{externalLaTeXPath}, 408 dvipng => $envir{externalDvipngPath}, 409 ); 410 411 # Other things... 412 $envir{QUIZ_PREFIX} = $options->{QUIZ_PREFIX}; # used by quizzes 413 $envir{PROBLEM_GRADER_TO_USE} = $ce->{pg}->{options}->{grader}; 414 $envir{PRINT_FILE_NAMES_FOR} = $ce->{pg}->{specialPGEnvironmentVars}->{PRINT_FILE_NAMES_FOR}; 415 416 # variables for interpreting capa problems. 417 $envir{CAPA_Tools} = $ce->{pg}->{specialPGEnvironmentVars}->{CAPA_Tools}; 418 $envir{CAPA_MCTools} = $ce->{pg}->{specialPGEnvironmentVars}->{CAPA_MCTools}; 419 $envir{CAPA_Graphics_URL} = $ce->{pg}->{specialPGEnvironmentVars}->{CAPA_Graphics_URL}; 420 $envir{CAPA_GraphicsDirectory} = $ce->{pg}->{specialPGEnvironmentVars}->{CAPA_GraphicsDirectory}; 421 422 return \%envir; 423 } 424 425 sub translateDisplayModeNames($) { 426 my $name = shift; 427 return { 428 tex => "TeX", 429 plainText => "HTML", 430 formattedText => "HTML_tth", 431 images => "HTML_dpng", # "HTML_img", 432 }->{$name}; 433 } 434 435 sub safetyFilter { 436 my $answer = shift; # accepts one answer and checks it 437 my $submittedAnswer = $answer; 438 $answer = '' unless defined $answer; 439 my ($errorno); 440 $answer =~ tr/\000-\037/ /; 441 # Return if answer field is empty 442 unless ($answer =~ /\S/) { 443 #$errorno = "<BR>No answer was submitted."; 444 $errorno = 0; ## don't report blank answer as error 445 return ($answer,$errorno); 446 } 447 # replace ^ with ** (for exponentiation) 448 # $answer =~ s/\^/**/g; 449 # Return if forbidden characters are found 450 unless ($answer =~ /^[a-zA-Z0-9_\-\+ \t\/@%\*\.\n^\[\]\(\)\,\|]+$/ ) { 451 $answer =~ tr/a-zA-Z0-9_\-\+ \t\/@%\*\.\n^\(\)/#/c; 452 $errorno = "<BR>There are forbidden characters in your answer: $submittedAnswer<BR>"; 453 return ($answer,$errorno); 454 } 455 $errorno = 0; 456 return($answer, $errorno); 457 } 458 459 1; 460 461 __END__ 462 463 =head1 OPERATION 464 465 WeBWorK::PG goes through the following operations when constructed: 466 467 =over 468 469 =item Get database information 470 471 Retrieve information about the current user, set, and problem from the 472 database. 473 474 =item Create a translator 475 476 Instantiate a WeBWorK::PG::Translator object. 477 478 =item Set the directory hash 479 480 Set the translator's directory hash (courseScripts, macros, templates, and temp 481 directories) from the course environment. 482 483 =item Evaluate PG modules 484 485 Using the module list from the course environment (pg->modules), perform a 486 "use"-like operation to evaluate modules at runtime. 487 488 =item Set the problem environment 489 490 Use data from the user, set, and problem, as well as the course environemnt and 491 translation options, to set the problem environment. 492 493 =item Initialize the translator 494 495 Call &WeBWorK::PG::Translator::initialize. What more do you want? 496 497 =item Load PG.pl and dangerousMacros.pl 498 499 These macros must be loaded without opcode masking, so they are loaded here. 500 501 =item Set the opcode mask 502 503 Set the opcode mask to the default specified by WeBWorK::PG::Translator. 504 505 =item Load the problem source 506 507 Give the problem source to the translator. 508 509 =item Install a safety filter 510 511 The safety filter is used to preprocess student input before evaluation. The 512 default safety filter, &WeBWorK::PG::safetyFilter, is used. 513 514 =item Translate the problem source 515 516 Call &WeBWorK::PG::Translator::translate to render the problem source into the 517 format given by the display mode. 518 519 =item Process student answers 520 521 Use form field inputs to evaluate student answers. 522 523 =item Load the problem state 524 525 Use values from the database to initialize the problem state, so that the 526 grader will have a point of reference. 527 528 =item Determine an entry order 529 530 Use the ANSWER_ENTRY_ORDER flag to determine the order of answers in the 531 problem. This is important for problems with dependancies among parts. 532 533 =item Install a grader 534 535 Use the PROBLEM_GRADER_TO_USE flag, or a default from the course environment, 536 to install a grader. 537 538 =item Grade the problem 539 540 Use the selected grader to grade the problem. 541 542 =back 543 544 =head1 AUTHOR 545 546 Written by Sam Hathaway, sh002i (at) math.rochester.edu. 547 548 =cut
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |