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