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