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