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