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