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