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