Parent Directory
|
Revision Log
Fixed a reference to the nullSafety filter --Mike
1 ################################################################################ 2 # WeBWorK mod_perl (c) 2000-2002 WeBWorK Project 3 # $Id$ 4 ################################################################################ 5 6 package WeBWorK::PG::Local; 7 use base qw(WeBWorK::PG); 8 9 =head1 NAME 10 11 WeBWorK::PG::Local - Use the WeBWorK::PG API to invoke a local 12 WeBWorK::PG::Translator object. 13 14 =head1 DESCRIPTION 15 16 WeBWorK::PG::Local encapsulates the PG translation process, making multiple 17 calls to WeBWorK::PG::Translator. Much of the flexibility of the Translator is 18 hidden, instead making choices that are appropriate for the webwork-modperl 19 system 20 21 It implements the WeBWorK::PG interface and uses a local 22 WeBWorK::PG::Translator to perform problem rendering. See the documentation for 23 the WeBWorK::PG module for information about the API. 24 25 =cut 26 27 use strict; 28 use warnings; 29 use File::Path qw(rmtree); 30 use WeBWorK::PG::Translator; 31 use WeBWorK::Utils qw(readFile writeTimingLogEntry); 32 33 BEGIN { 34 # This safe compartment is used to read the large macro files such as 35 # PG.pl, PGbasicmacros.pl and PGanswermacros and cache the results so that 36 # future calls have preloaded versions of these large files. This saves a 37 # significant amount of time. 38 $WeBWorK::PG::Local::safeCache = new Safe; 39 } 40 41 sub new { 42 my $invocant = shift; 43 my $class = ref($invocant) || $invocant; 44 my ( 45 $ce, 46 $user, 47 $key, 48 $set, 49 $problem, 50 $psvn, 51 $formFields, # in CGI::Vars format 52 $translationOptions, # hashref containing options for the 53 # translator, such as whether to show 54 # hints and the display mode to use 55 ) = @_; 56 57 # write timing log entry 58 writeTimingLogEntry($ce, "WeBWorK::PG::new", 59 "user=".$user->user_id.",problem=".$ce->{courseName}."/".$set->set_id."/".$problem->problem_id.",mode=".$translationOptions->{displayMode}, 60 "begin"); 61 62 # install a local warn handler to collect warnings 63 my $warnings = ""; 64 local $SIG{__WARN__} = sub { $warnings .= shift } 65 if $ce->{pg}->{options}->{catchWarnings}; 66 67 # create a Translator 68 #warn "PG: creating a Translator\n"; 69 my $translator = WeBWorK::PG::Translator->new; 70 71 # set the directory hash 72 #warn "PG: setting the directory hash\n"; 73 $translator->rh_directories({ 74 courseScriptsDirectory => $ce->{pg}->{directories}->{macros}, 75 macroDirectory => $ce->{courseDirs}->{macros}, 76 templateDirectory => $ce->{courseDirs}->{templates}, 77 tempDirectory => $ce->{courseDirs}->{html_temp}, 78 }); 79 80 # evaluate modules and "extra packages" 81 #warn "PG: evaluating modules and \"extra packages\"\n"; 82 my @modules = @{ $ce->{pg}->{modules} }; 83 foreach my $module_packages_ref (@modules) { 84 my ($module, @extra_packages) = @$module_packages_ref; 85 # the first item is the main package 86 $translator->evaluate_modules($module); 87 # the remaining items are "extra" packages 88 $translator->load_extra_packages(@extra_packages); 89 } 90 91 # set the environment (from defineProblemEnvir) 92 #warn "PG: setting the environment (from defineProblemEnvir)\n"; 93 my $envir = $class->defineProblemEnvir( 94 $ce, 95 $user, 96 $key, 97 $set, 98 $problem, 99 $psvn, 100 $formFields, 101 $translationOptions, 102 ); 103 $translator->environment($envir); 104 105 # initialize the Translator 106 #warn "PG: initializing the Translator\n"; 107 $translator->initialize(); 108 109 # Preload the macros files which are used routinely: PG.pl, 110 # dangerousMacros.pl, IO.pl, PGbasicmacros.pl, and PGanswermacros.pl 111 # (Preloading the last two files safes a significant amount of time.) 112 # 113 # IO.pl, PG.pl, and dangerousMacros.pl are loaded using 114 # unrestricted_load This is hard wired into the 115 # Translator::pre_load_macro_files subroutine. I'd like to change this 116 # at some point to have the same sort of interface to global.conf that 117 # the module loading does -- have a list of macros to load 118 # unrestrictedly. 119 # 120 # This has been replaced by the pre_load_macro_files subroutine. It 121 # loads AND caches the files. While PG.pl and dangerousMacros are not 122 # large, they are referred to by PGbasicmacros and PGanswermacros. 123 # Because these are loaded into the cached name space (e.g. 124 # Safe::Root1::) all calls to, say NEW_ANSWER_NAME are actually calls 125 # to Safe::Root1::NEW_ANSWER_NAME. It is useful to have these names 126 # inside the Safe::Root1: cached safe compartment. (NEW_ANSWER_NAME 127 # and all other subroutine names are also automatically exported into 128 # the current safe compartment Safe::Rootx:: 129 # 130 # The headers of both PGbasicmacros and PGanswermacros has code that 131 # insures that the constants used are imported into the current safe 132 # compartment. This involves evaluating references to, say 133 # $main::displayMode, at runtime to insure that main refers to 134 # Safe::Rootx:: and NOT to Safe::Root1::, which is the value of main:: 135 # at compile time. 136 # 137 # TO ENABLE CACHEING UNCOMMENT THE FOLLOWING: 138 eval{$translator->pre_load_macro_files( 139 $WeBWorK::PG::Local::safeCache, 140 $ce->{pg}->{directories}->{macros}, 141 'PG.pl', 'dangerousMacros.pl','IO.pl','PGbasicmacros.pl','PGanswermacros.pl' 142 )}; 143 warn "Error while preloading macro files: $@" if $@; 144 145 # STANDARD LOADING CODE: for cached script files, this merely 146 # initializes the constants. 147 foreach (qw(PG.pl dangerousMacros.pl IO.pl)) { 148 my $macroPath = $ce->{pg}->{directories}->{macros} . "/$_"; 149 my $err = $translator->unrestricted_load($macroPath); 150 warn "Error while loading $macroPath: $err" if $err; 151 } 152 153 # set the opcode mask (using default values) 154 #warn "PG: setting the opcode mask (using default values)\n"; 155 $translator->set_mask(); 156 157 # store the problem source 158 #warn "PG: storing the problem source\n"; 159 my $sourceFile = $problem->source_file; 160 $sourceFile = $ce->{courseDirs}->{templates}."/".$sourceFile 161 unless ($sourceFile =~ /^\//); 162 eval { $translator->source_string(readFile($sourceFile)) }; 163 if ($@) { 164 # well, we couldn't get the problem source, for some reason. 165 return bless { 166 translator => $translator, 167 head_text => "", 168 body_text => <<EOF, 169 WeBWorK::Utils::readFile($sourceFile) says: 170 $@ 171 EOF 172 answers => {}, 173 result => {}, 174 state => {}, 175 errors => "Failed to read the problem source file.", 176 warnings => $warnings, 177 flags => {error_flag => 1}, 178 }, $class; 179 } 180 181 # install a safety filter 182 #warn "PG: installing a safety filter\n"; 183 #$translator->rf_safety_filter(\&oldSafetyFilter); 184 $translator->rf_safety_filter(\&WeBWorK::PG::nullSafetyFilter); 185 186 # write timing log entry -- the translator is now all set up 187 writeTimingLogEntry($ce, "WeBWorK::PG::new", 188 "initialized", 189 "intermediate"); 190 191 # translate the PG source into text 192 #warn "PG: translating the PG source into text\n"; 193 $translator->translate(); 194 195 # after we're done translating, we may have to clean up after the 196 # translator: 197 198 # for example, HTML_img mode uses a tempdir for dvipng's temp files.\ 199 # We have to remove it. 200 if ($envir->{dvipngTempDir}) { 201 rmtree($envir->{dvipngTempDir}, 0, 0); 202 } 203 204 # HTML_dpng, on the other hand, uses an ImageGenerator. We have to 205 # render the queued equations. 206 if ($envir->{imagegen}) { 207 my $sourceFile = $ce->{courseDirs}->{templates} . "/" . $problem->source_file; 208 my %mtimeOption = -e $sourceFile 209 ? (mtime => (stat $sourceFile)[9]) 210 : (); 211 212 $envir->{imagegen}->render( 213 refresh => $translationOptions->{refreshMath2img}, 214 %mtimeOption, 215 ); 216 } 217 218 my ($result, $state); # we'll need these on the other side of the if block! 219 if ($translationOptions->{processAnswers}) { 220 221 # process student answers 222 #warn "PG: processing student answers\n"; 223 $translator->process_answers($formFields); 224 225 # retrieve the problem state and give it to the translator 226 #warn "PG: retrieving the problem state and giving it to the translator\n"; 227 $translator->rh_problem_state({ 228 recorded_score => $problem->status, 229 num_of_correct_ans => $problem->num_correct, 230 num_of_incorrect_ans => $problem->num_incorrect, 231 }); 232 233 # determine an entry order -- the ANSWER_ENTRY_ORDER flag is built by 234 # the PG macro package (PG.pl) 235 #warn "PG: determining an entry order\n"; 236 my @answerOrder = 237 $translator->rh_flags->{ANSWER_ENTRY_ORDER} 238 ? @{ $translator->rh_flags->{ANSWER_ENTRY_ORDER} } 239 : keys %{ $translator->rh_evaluated_answers }; 240 241 # install a grader -- use the one specified in the problem, 242 # or fall back on the default from the course environment. 243 # (two magic strings are accepted, to avoid having to 244 # reference code when it would be difficult.) 245 #warn "PG: installing a grader\n"; 246 my $grader = $translator->rh_flags->{PROBLEM_GRADER_TO_USE} 247 || $ce->{pg}->{options}->{grader}; 248 $grader = $translator->rf_std_problem_grader 249 if $grader eq "std_problem_grader"; 250 $grader = $translator->rf_avg_problem_grader 251 if $grader eq "avg_problem_grader"; 252 die "Problem grader $grader is not a CODE reference." 253 unless ref $grader eq "CODE"; 254 $translator->rf_problem_grader($grader); 255 256 # grade the problem 257 #warn "PG: grading the problem\n"; 258 ($result, $state) = $translator->grade_problem( 259 answers_submitted => $translationOptions->{processAnswers}, 260 ANSWER_ENTRY_ORDER => \@answerOrder, 261 ); 262 263 } 264 265 # write timing log entry 266 writeTimingLogEntry($ce, "WeBWorK::PG::new", "", "end"); 267 268 # return an object which contains the translator and the results of 269 # the translation process. this is DIFFERENT from the "format expected 270 # by Webwork.pm (and I believe processProblem8, but check.)" 271 return bless { 272 translator => $translator, 273 head_text => ${ $translator->r_header }, 274 body_text => ${ $translator->r_text }, 275 answers => $translator->rh_evaluated_answers, 276 result => $result, 277 state => $state, 278 errors => $translator->errors, 279 warnings => $warnings, 280 flags => $translator->rh_flags, 281 }, $class; 282 } 283 284 1; 285 286 __END__ 287 288 =head1 OPERATION 289 290 WeBWorK::PG::Local goes through the following operations when constructed: 291 292 =over 293 294 =item Create a translator 295 296 Instantiate a WeBWorK::PG::Translator object. 297 298 =item Set the directory hash 299 300 Set the translator's directory hash (courseScripts, macros, templates, and temp 301 directories) from the course environment. 302 303 =item Evaluate PG modules 304 305 Using the module list from the course environment (pg->modules), perform a 306 "use"-like operation to evaluate modules at runtime. 307 308 =item Set the problem environment 309 310 Use data from the user, set, and problem, as well as the course 311 environemnt and translation options, to set the problem environment. The 312 default subroutine, &WeBWorK::PG::defineProblemEnvir, is used. 313 314 =item Initialize the translator 315 316 Call &WeBWorK::PG::Translator::initialize. What more do you want? 317 318 =item Load IO.pl, PG.pl and dangerousMacros.pl 319 320 These macros must be loaded without opcode masking, so they are loaded here. 321 322 =item Set the opcode mask 323 324 Set the opcode mask to the default specified by WeBWorK::PG::Translator. 325 326 =item Load the problem source 327 328 Give the problem source to the translator. 329 330 =item Install a safety filter 331 332 The safety filter is used to preprocess student input before evaluation. The 333 default safety filter, &WeBWorK::PG::safetyFilter, is used. 334 335 =item Translate the problem source 336 337 Call &WeBWorK::PG::Translator::translate to render the problem source into the 338 format given by the display mode. 339 340 =item Process student answers 341 342 Use form field inputs to evaluate student answers. 343 344 =item Load the problem state 345 346 Use values from the database to initialize the problem state, so that the 347 grader will have a point of reference. 348 349 =item Determine an entry order 350 351 Use the ANSWER_ENTRY_ORDER flag to determine the order of answers in the 352 problem. This is important for problems with dependancies among parts. 353 354 =item Install a grader 355 356 Use the PROBLEM_GRADER_TO_USE flag, or a default from the course environment, 357 to install a grader. 358 359 =item Grade the problem 360 361 Use the selected grader to grade the problem. 362 363 =back 364 365 =head1 AUTHOR 366 367 Written by Sam Hathaway, sh002i (at) math.rochester.edu. 368 369 =cut
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |