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