Parent Directory
|
Revision Log
added texDisposition problem environment variable -- always set to "pdf" cleaned up unrestricted_load code, made IO.pl get loaded first -sam
1 ################################################################################ 2 # WeBWorK mod_perl (c) 2000-2002 WeBWorK Project 3 # $Id$ 4 ################################################################################ 5 6 package WeBWorK::PG; 7 8 =head1 NAME 9 10 WeBWorK::PG - Wrap the action of the PG Translator in an easy-to-use API. 11 12 =cut 13 14 use strict; 15 use warnings; 16 use File::Path qw(rmtree); 17 use File::Temp qw(tempdir); 18 use WeBWorK::DB::Classlist; 19 use WeBWorK::DB::WW; 20 use WeBWorK::PG::Translator; 21 use WeBWorK::Problem; 22 use WeBWorK::Utils qw(readFile formatDateTime writeTimingLogEntry); 23 24 sub new($$$$$$$$) { 25 my $invocant = shift; 26 my $class = ref($invocant) || $invocant; 27 my ( 28 $courseEnv, 29 $user, 30 $key, 31 $set, 32 $problem, 33 $psvn, 34 $formFields, # in CGI::Vars format 35 $translationOptions, # hashref containing options for the 36 # translator, such as whether to show 37 # hints and the display mode to use 38 ) = @_; 39 40 # write timing log entry 41 writeTimingLogEntry($courseEnv, "WeBWorK::PG::new", 42 "user=".$user->id.",problem=".$courseEnv->{courseName}."/".$set->id."/".$problem->id.",mode=".$translationOptions->{displayMode}, 43 "begin"); 44 45 # install a local warn handler to collect warnings 46 my $warnings = ""; 47 if ($courseEnv->{pg}->{options}->{catchWarnings}) { 48 warn "PG: about to install a warnings handler...\n"; # *** 49 local $SIG{__WARN__} = sub { $warnings .= shift }; 50 } 51 52 # create a Translator 53 #warn "PG: creating a Translator\n"; 54 my $translator = WeBWorK::PG::Translator->new; 55 56 # set the directory hash 57 #warn "PG: setting the directory hash\n"; 58 $translator->rh_directories({ 59 courseScriptsDirectory => $courseEnv->{webworkDirs}->{macros}, 60 macroDirectory => $courseEnv->{courseDirs}->{macros}, 61 templateDirectory => $courseEnv->{courseDirs}->{templates}, 62 tempDirectory => $courseEnv->{courseDirs}->{html_temp}, 63 }); 64 65 # evaluate modules and "extra packages" 66 #warn "PG: evaluating modules and \"extra packages\"\n"; 67 my @modules = @{ $courseEnv->{pg}->{modules} }; 68 foreach my $module_packages_ref (@modules) { 69 my ($module, @extra_packages) = @$module_packages_ref; 70 # the first item is the main package 71 $translator->evaluate_modules($module); 72 # the remaining items are "extra" packages 73 $translator->load_extra_packages(@extra_packages); 74 } 75 76 # set the environment (from defineProblemEnvir) 77 #warn "PG: setting the environment (from defineProblemEnvir)\n"; 78 my $envir = defineProblemEnvir( 79 $courseEnv, 80 $user, 81 $key, 82 $set, 83 $problem, 84 $psvn, 85 $formFields, 86 $translationOptions, 87 ); 88 $translator->environment($envir); 89 90 # initialize the Translator 91 #warn "PG: initializing the Translator\n"; 92 $translator->initialize(); 93 94 # load IO.pl, PG.pl, and dangerousMacros.pl using unrestricted_load 95 # i'd like to change this at some point to have the same sort of interface to global.conf 96 # that the module loading does -- have a list of macros to load unrestrictedly. 97 #warn "PG: loading IO.pl, PG.pl, and dangerousMacros.pl using unrestricted_load\n"; 98 foreach (qw(IO.pl PG.pl dangerousMacros.pl)) { 99 my $macroPath = $courseEnv->{webworkDirs}->{macros} . "/$_"; 100 my $err = $translator->unrestricted_load($macroPath); 101 warn "Error while loading $macroPath: $err" if $err; 102 } 103 #my $pg_pl = $courseEnv->{webworkDirs}->{macros} . "/PG.pl"; 104 #my $dangerousMacros_pl = $courseEnv->{webworkDirs}->{macros} . "/dangerousMacros.pl"; 105 #my $io_pl = $courseEnv->{webworkDirs}->{macros} . "/IO.pl"; 106 #my $err = $translator->unrestricted_load($pg_pl); 107 #warn "Error while loading $pg_pl: $err" if $err; 108 #$err = $translator->unrestricted_load($dangerousMacros_pl); 109 #warn "Error while loading $dangerousMacros_pl: $err" if $err; 110 #$err = $translator->unrestricted_load($io_pl); 111 #warn "Error while loading $io_pl: $err" if $err; 112 113 # set the opcode mask (using default values) 114 #warn "PG: setting the opcode mask (using default values)\n"; 115 $translator->set_mask(); 116 117 # store the problem source 118 #warn "PG: storing the problem source\n"; 119 my $sourceFile = $problem->source_file; 120 $sourceFile = $courseEnv->{courseDirs}->{templates}."/".$sourceFile 121 unless ($sourceFile =~ /^\//); 122 eval { $translator->source_string(readFile($sourceFile)) }; 123 if ($@) { 124 # well, we couldn't get the problem source, for some reason. 125 return bless { 126 translator => $translator, 127 head_text => "", 128 body_text => <<EOF, 129 WeBWorK::Utils::readFile($sourceFile) says: 130 $@ 131 EOF 132 answers => {}, 133 result => {}, 134 state => {}, 135 errors => "Failed to read the problem source file.", 136 warnings => $warnings, 137 flags => {error_flag => 1}, 138 }, $class; 139 } 140 141 # install a safety filter (&safetyFilter) 142 #warn "PG: installing a safety filter\n"; 143 $translator->rf_safety_filter(\&safetyFilter); 144 145 # translate the PG source into text 146 #warn "PG: translating the PG source into text\n"; 147 $translator->translate(); 148 149 # after we're done translating, we may have to clean up after the translator. 150 # for example, 'images' mode uses a tempdir for dvipng's temp files. We have 151 # to remove it. 152 if ($translationOptions->{displayMode} eq 'images' && $envir->{dvipngTempDir}) { 153 rmtree($envir->{dvipngTempDir}, 0, 0); 154 } 155 156 my ($result, $state); # we'll need these on the other side of the if block! 157 if ($translationOptions->{processAnswers}) { 158 159 # process student answers 160 #warn "PG: processing student answers\n"; 161 $translator->process_answers($formFields); 162 163 # retrieve the problem state and give it to the translator 164 #warn "PG: retrieving the problem state and giving it to the translator\n"; 165 $translator->rh_problem_state({ 166 recorded_score => $problem->status, 167 num_of_correct_ans => $problem->num_correct, 168 num_of_incorrect_ans => $problem->num_incorrect, 169 }); 170 171 # determine an entry order -- the ANSWER_ENTRY_ORDER flag is built by 172 # the PG macro package (PG.pl) 173 #warn "PG: determining an entry order\n"; 174 my @answerOrder = 175 $translator->rh_flags->{ANSWER_ENTRY_ORDER} 176 ? @{ $translator->rh_flags->{ANSWER_ENTRY_ORDER} } 177 : keys %{ $translator->rh_evaluated_answers }; 178 179 # install a grader -- use the one specified in the problem, 180 # or fall back on the default from the course environment. 181 # (two magic strings are accepted, to avoid having to 182 # reference code when it would be difficult.) 183 #warn "PG: installing a grader\n"; 184 my $grader = $translator->rh_flags->{PROBLEM_GRADER_TO_USE} 185 || $courseEnv->{pg}->{options}->{grader}; 186 $grader = $translator->rf_std_problem_grader 187 if $grader eq "std_problem_grader"; 188 $grader = $translator->rf_avg_problem_grader 189 if $grader eq "avg_problem_grader"; 190 die "Problem grader $grader is not a CODE reference." 191 unless ref $grader eq "CODE"; 192 $translator->rf_problem_grader($grader); 193 194 # grade the problem 195 #warn "PG: grading the problem\n"; 196 ($result, $state) = $translator->grade_problem( 197 answers_submitted => $translationOptions->{processAnswers}, 198 ANSWER_ENTRY_ORDER => \@answerOrder, 199 ); 200 201 } 202 203 # write timing log entry 204 writeTimingLogEntry($courseEnv, "WeBWorK::PG::new", "", "end"); 205 206 # return an object which contains the translator and the results of 207 # the translation process. this is DIFFERENT from the "format expected 208 # by Webwork.pm (and I believe processProblem8, but check.)" 209 return bless { 210 translator => $translator, 211 head_text => ${ $translator->r_header }, 212 body_text => ${ $translator->r_text }, 213 answers => $translator->rh_evaluated_answers, 214 result => $result, 215 state => $state, 216 errors => $translator->errors, 217 warnings => $warnings, 218 flags => $translator->rh_flags, 219 }, $class; 220 } 221 222 # ----- 223 224 sub defineProblemEnvir($$$$$$$) { 225 my ( 226 $courseEnv, 227 $user, 228 $key, 229 $set, 230 $problem, 231 $psvn, 232 $formFields, 233 $options, 234 ) = @_; 235 236 my %envir; 237 238 # PG environment variables 239 # from docs/pglanguage/pgreference/environmentvariables as of 06/25/2002 240 # any changes are noted by "ADDED:" or "REMOVED:" 241 242 # Vital state information 243 # ADDED: displayHintsQ, displaySolutionsQ, refreshMath2img, 244 # texDisposition 245 246 $envir{psvn} = $psvn; 247 $envir{psvnNumber} = $envir{psvn}; 248 $envir{probNum} = $problem->id; 249 $envir{questionNumber} = $envir{probNum}; 250 $envir{fileName} = $problem->source_file; 251 $envir{probFileName} = $envir{fileName}; 252 $envir{problemSeed} = $problem->problem_seed; 253 $envir{displayMode} = translateDisplayModeNames($options->{displayMode}); 254 $envir{languageMode} = $envir{displayMode}; 255 $envir{outputMode} = $envir{displayMode}; 256 $envir{displayHintsQ} = $options->{hints}; 257 $envir{displaySolutionsQ} = $options->{solutions}; 258 $envir{refreshMath2img} = $options->{refreshMath2img}; 259 $envir{texDisposition} = "pdf"; # in webwork-modperl, we use pdflatex 260 261 # Problem Information 262 # ADDED: courseName 263 264 $envir{openDate} = $set->open_date; 265 $envir{formattedOpenDate} = formatDateTime($envir{openDate}); 266 $envir{dueDate} = $set->due_date; 267 $envir{formattedDueDate} = formatDateTime($envir{dueDate}); 268 $envir{answerDate} = $set->answer_date; 269 $envir{formattedAnswerDate} = formatDateTime($envir{answerDate}); 270 $envir{numOfAttempts} = ($problem->num_correct || 0) + ($problem->num_incorrect || 0); 271 $envir{problemValue} = $problem->value; 272 $envir{sessionKey} = $key; 273 $envir{courseName} = $courseEnv->{courseName}; 274 275 # Student Information 276 # ADDED: studentID 277 278 $envir{sectionName} = $user->section; 279 $envir{sectionNumber} = $envir{sectionName}; 280 $envir{recitationName} = $user->recitation; 281 $envir{recitationNumber} = $envir{recitationName}; 282 $envir{setNumber} = $set->id; 283 $envir{studentLogin} = $user->id; 284 $envir{studentName} = $user->first_name . " " . $user->last_name; 285 $envir{studentID} = $user->student_id; 286 287 # Answer Information 288 # REMOVED: refSubmittedAnswers 289 290 $envir{inputs_ref} = $formFields; 291 292 # External Programs 293 # ADDED: externalLaTeXPath, externalDvipngPath, 294 # externalGif2EpsPath, externalPng2EpsPath 295 296 $envir{externalTTHPath} = $courseEnv->{externalPrograms}->{tth}; 297 $envir{externalLaTeXPath} = $courseEnv->{externalPrograms}->{latex}; 298 $envir{externalDvipngPath} = $courseEnv->{externalPrograms}->{dvipng}; 299 $envir{externalGif2EpsPath} = $courseEnv->{externalPrograms}->{gif2eps}; 300 $envir{externalPng2EpsPath} = $courseEnv->{externalPrograms}->{png2eps}; 301 $envir{externalGif2PngPath} = $courseEnv->{externalPrograms}->{gif2png}; 302 303 # Directories and URLs 304 # REMOVED: courseName 305 # ADDED: dvipngTempDir 306 307 $envir{cgiDirectory} = undef; 308 $envir{cgiURL} = undef; 309 $envir{classDirectory} = undef; 310 $envir{courseScriptsDirectory} = $courseEnv->{webworkDirs}->{macros}."/"; 311 $envir{htmlDirectory} = $courseEnv->{courseDirs}->{html}."/"; 312 $envir{htmlURL} = $courseEnv->{courseURLs}->{html}."/"; 313 $envir{macroDirectory} = $courseEnv->{courseDirs}->{macros}."/"; 314 $envir{templateDirectory} = $courseEnv->{courseDirs}->{templates}."/"; 315 $envir{tempDirectory} = $courseEnv->{courseDirs}->{html_temp}."/"; 316 $envir{tempURL} = $courseEnv->{courseURLs}->{html_temp}."/"; 317 $envir{scriptDirectory} = undef; 318 $envir{webworkDocsURL} = $courseEnv->{webworkURLs}->{docs}."/"; 319 $envir{dvipngTempDir} = $options->{displayMode} eq 'images' 320 ? tempdir("webwork-dvipng-XXXXXXXX", DIR => $envir{tempDirectory}) 321 : undef; 322 323 # Default values for evaluating answers 324 325 my $ansEvalDefaults = $courseEnv->{pg}->{ansEvalDefaults}; 326 $envir{$_} = $ansEvalDefaults->{$_} foreach (keys %$ansEvalDefaults); 327 328 # Other things... 329 330 $envir{PROBLEM_GRADER_TO_USE} = $courseEnv->{pg}->{options}->{grader}; 331 332 return \%envir; 333 } 334 335 sub translateDisplayModeNames($) { 336 my $name = shift; 337 return { 338 tex => "TeX", 339 plainText => "HTML", 340 formattedText => "HTML_tth", 341 images => "HTML_img" 342 }->{$name}; 343 } 344 345 sub safetyFilter { 346 my $answer = shift; # accepts one answer and checks it 347 my $submittedAnswer = $answer; 348 $answer = '' unless defined $answer; 349 my ($errorno); 350 $answer =~ tr/\000-\037/ /; 351 # Return if answer field is empty 352 unless ($answer =~ /\S/) { 353 #$errorno = "<BR>No answer was submitted."; 354 $errorno = 0; ## don't report blank answer as error 355 return ($answer,$errorno); 356 } 357 # replace ^ with ** (for exponentiation) 358 # $answer =~ s/\^/**/g; 359 # Return if forbidden characters are found 360 unless ($answer =~ /^[a-zA-Z0-9_\-\+ \t\/@%\*\.\n^\(\)]+$/ ) { 361 $answer =~ tr/a-zA-Z0-9_\-\+ \t\/@%\*\.\n^\(\)/#/c; 362 $errorno = "<BR>There are forbidden characters in your answer: $submittedAnswer<BR>"; 363 return ($answer,$errorno); 364 } 365 $errorno = 0; 366 return($answer, $errorno); 367 } 368 369 1; 370 371 __END__ 372 373 =head1 SYNOPSIS 374 375 $pg = WeBWorK::PG->new( 376 $courseEnv, # a WeBWorK::CourseEnvironment object 377 $user, # a WeBWorK::User object 378 $sessionKey, 379 $set, # a WeBWorK::Set object 380 $problem, # a WeBWorK::Problem object 381 $psvn, 382 $formFields # in &WeBWorK::Form::Vars format 383 { # translation options 384 displayMode => "images", # (plainText|formattedText|images) 385 showHints => 1, # (0|1) 386 showSolutions => 0, # (0|1) 387 refreshMath2img => 0, # (0|1) 388 processAnswers => 1, # (0|1) 389 }, 390 ); 391 392 $translator = $pg->{translator}; # WeBWorK::PG::Translator 393 $body = $pg->{body_text}; # text string 394 $header = $pg->{head_text}; # text string 395 $answerHash = $pg->{answers}; # WeBWorK::PG::AnswerHash 396 $result = $pg->{result}; # hash reference 397 $state = $pg->{state}; # hash reference 398 $errors = $pg->{errors}; # text string 399 $warnings = $pg->{warnings}; # text string 400 $flags = $pg->{flags}; # hash reference 401 402 =head1 DESCRIPTION 403 404 WeBWorK::PG encapsulates the PG translation process, making multiple calls to 405 WeBWorK::PG::Translator. Much of the flexibility of the Translator is hidden, 406 instead making choices that are appropriate for the webwork-modperl system. 407 408 =head1 CONSTRUCTION 409 410 =over 411 412 =item new (ENVIRONMENT, USER, KEY, SET, PROBLEM, PSVN, FIELDS, OPTIONS) 413 414 The C<new> method creates a translator, initializes it using the parameters 415 specified, translates a PG file, and processes answers. It returns a reference 416 to a blessed hash containing the results of the translation process. 417 418 =back 419 420 =head2 Parameters 421 422 =over 423 424 =item ENVIRONMENT 425 426 a WeBWorK::CourseEnvironment object 427 428 =item USER 429 430 a WeBWorK::User object 431 432 =item KEY 433 434 the session key of the current session 435 436 =item SET 437 438 a WeBWorK::Set object 439 440 =item PROBLEM 441 442 a WeBWorK::Problem object. The contents of the source_file field can specify a 443 PG file either by absolute path or path relative to the "templates" directory. 444 I<The caller should remove taint from this value before passing!> 445 446 =item PSVN 447 448 the problem set version number 449 450 =item FIELDS 451 452 a reference to a hash (as returned by &WeBWorK::Form::Vars) containing form 453 fields submitted by a problem processor. The translator will look for fields 454 like "AnSwEr[0-9]" containing submitted student answers. 455 456 =item OPTIONS 457 458 a reference to a hash containing the following data: 459 460 =over 461 462 =item displayMode 463 464 one of "plainText", "formattedText", or "images" 465 466 =item showHints 467 468 boolean, render hints 469 470 =item showSolutions 471 472 boolean, render solutions 473 474 =item refreshMath2img 475 476 boolean, force images created by math2img (in "images" mode) to be recreated, 477 even if the PG source has not been updated. 478 479 =item processAnswers 480 481 boolean, call answer evaluators and graders 482 483 =back 484 485 =back 486 487 =head2 RETURN VALUE 488 489 The C<new> method returns a blessed hash reference containing the following 490 fields. More information can be found in the documentation for 491 WeBWorK::PG::Translator. 492 493 =over 494 495 =item translator 496 497 The WeBWorK::PG::Translator object used to render the problem. 498 499 =item head_text 500 501 HTML code for the E<lt>headE<gt> block of an resulting web page. Used for 502 JavaScript features. 503 504 =item body_text 505 506 HTML code for the E<lt>bodyE<gt> block of an resulting web page. 507 508 =item answers 509 510 An C<AnswerHash> object containing submitted answers, and results of answer 511 evaluation. 512 513 =item result 514 515 A hash containing the results of grading the problem. 516 517 =item state 518 519 A hash containing the new problem state. 520 521 =item errors 522 523 A string containing any errors encountered while rendering the problem. 524 525 =item warnings 526 527 A string containing any warnings encountered while rendering the problem. 528 529 =item flags 530 531 A hash containing PG_flags (see the Translator docs). 532 533 =back 534 535 =head1 OPERATION 536 537 WeBWorK::PG goes through the following operations when constructed: 538 539 =over 540 541 =item Get database information 542 543 Retrieve information about the current user, set, and problem from the 544 database. 545 546 =item Create a translator 547 548 Instantiate a WeBWorK::PG::Translator object. 549 550 =item Set the directory hash 551 552 Set the translator's directory hash (courseScripts, macros, templates, and temp 553 directories) from the course environment. 554 555 =item Evaluate PG modules 556 557 Using the module list from the course environment (pg->modules), perform a 558 "use"-like operation to evaluate modules at runtime. 559 560 =item Set the problem environment 561 562 Use data from the user, set, and problem, as well as the course environemnt and 563 translation options, to set the problem environment. 564 565 =item Initialize the translator 566 567 Call &WeBWorK::PG::Translator::initialize. What more do you want? 568 569 =item Load PG.pl and dangerousMacros.pl 570 571 These macros must be loaded without opcode masking, so they are loaded here. 572 573 =item Set the opcode mask 574 575 Set the opcode mask to the default specified by WeBWorK::PG::Translator. 576 577 =item Load the problem source 578 579 Give the problem source to the translator. 580 581 =item Install a safety filter 582 583 The safety filter is used to preprocess student input before evaluation. The 584 default safety filter, &WeBWorK::PG::safetyFilter, is used. 585 586 =item Translate the problem source 587 588 Call &WeBWorK::PG::Translator::translate to render the problem source into the 589 format given by the display mode. 590 591 =item Process student answers 592 593 Use form field inputs to evaluate student answers. 594 595 =item Load the problem state 596 597 Use values from the database to initialize the problem state, so that the 598 grader will have a point of reference. 599 600 =item Determine an entry order 601 602 Use the ANSWER_ENTRY_ORDER flag to determine the order of answers in the 603 problem. This is important for problems with dependancies among parts. 604 605 =item Install a grader 606 607 Use the PROBLEM_GRADER_TO_USE flag, or a default from the course environment, 608 to install a grader. 609 610 =item Grade the problem 611 612 Use the selected grader to grade the problem. 613 614 =back 615 616 =head1 AUTHOR 617 618 Written by Sam Hathaway, sh002i (at) math.rochester.edu. 619 620 =cut
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |