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