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