[system] / trunk / webwork-modperl / lib / WeBWorK / PG.pm Repository:
ViewVC logotype

Diff of /trunk/webwork-modperl/lib/WeBWorK/PG.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

Revision 455 Revision 526
1################################################################################ 1################################################################################
2# WeBWorK mod_perl (c) 1995-2002 WeBWorK Team, Univeristy of Rochester 2# WeBWorK mod_perl (c) 2000-2002 WeBWorK Project
3# $Id$ 3# $Id$
4################################################################################ 4################################################################################
5 5
6package WeBWorK::PG; 6package WeBWorK::PG;
7 7
8=head1 NAME 8=head1 NAME
9 9
10WeBWorK::PG - Wrap the action of the PG Translator in an easy-to-use API 10WeBWorK::PG - Wrap the action of the PG Translator in an easy-to-use API.
11 11
12=cut 12=cut
13 13
14use strict; 14use strict;
15use warnings; 15use warnings;
16use File::Temp qw(tempdir);
16use WeBWorK::DB::Classlist; 17use WeBWorK::DB::Classlist;
17use WeBWorK::DB::WW; 18use WeBWorK::DB::WW;
18use WeBWorK::PG::Translator; 19use WeBWorK::PG::Translator;
20use WeBWorK::Problem;
19use WeBWorK::Utils qw(readFile formatDateTime); 21use WeBWorK::Utils qw(readFile formatDateTime);
20 22
21sub new($$$$$$$$) { 23sub new($$$$$$$$) {
22 my $invocant = shift; 24 my $invocant = shift;
23 my $class = ref($invocant) || $invocant; 25 my $class = ref($invocant) || $invocant;
24 my ( 26 my (
25 $courseEnv, 27 $courseEnv,
26 $userName, 28 $user,
27 $key, 29 $key,
28 $setName, 30 $set,
29 $problemNumber, 31 $problem,
32 $psvn,
33 $formFields, # in CGI::Vars format
30 $translationOptions, # hashref containing options for the 34 $translationOptions, # hashref containing options for the
31 # translator, such as whether to show 35 # translator, such as whether to show
32 # hints and the display mode to use 36 # hints and the display mode to use
33 $formFields, # in CGI::Vars format
34 ) = @_; 37 ) = @_;
35 38
36 # get database information 39# # get database information
37 my $classlist = WeBWorK::DB::Classlist->new($courseEnv); 40# my $classlist = WeBWorK::DB::Classlist->new($courseEnv);
38 my $wwdb = WeBWorK::DB::WW->new($courseEnv); 41# my $wwdb = WeBWorK::DB::WW->new($courseEnv);
39 my $user = $classlist->getUser($userName); 42# my $user = $classlist->getUser($userName);
40 my $set = $wwdb->getSet($userName, $setName); 43# my $set = $wwdb->getSet($userName, $setName);
41 my $problem = $wwdb->getProblem($userName, $setName, $problemNumber);
42 my $psvn = $wwdb->getPSVN($userName, $setName); 44# my $psvn = $wwdb->getPSVN($userName, $setName);
45#
46# my $problem;
47# if ($problemNumber =~ /^\d+$/) {
48# $problem = $wwdb->getProblem($userName, $setName, $problemNumber);
49# } else {
50# # This is the fun part: if $problemNumber is NON-NUMERIC, the
51# # user wants to specify a PG file directly. We manufacture a
52# # Problem object using fake data and the specified source file.
53# # This is potentially dangerous since an untrusted user is
54# # allowed to specifiy an arbitrary file to be evaluated as PG.
55# # A user of PG.pm MUST MAKE SURE that if $problemNumber is
56# # supplied by an untrusted source (i.e. the Apache request),
57# # it is numberic. A simple
58# #
59# # die unless $problemNumber =~ /^\d+$/;
60# #
61# # should suffice.
62# $problem = WeBWorK::Problem->new(
63# id => 0,
64# set_id => $set->id,
65# login_id => $user->id,
66# source_file => $problemNumber,
67# # the rest of Problem's fields are not needed
68# );
69# }
43 70
44 # create a Translator 71 # create a Translator
45 warn "PG: creating a Translator\n"; 72 warn "PG: creating a Translator\n";
46 my $translator = WeBWorK::PG::Translator->new; 73 my $translator = WeBWorK::PG::Translator->new;
47 74
55 }); 82 });
56 83
57 # evaluate modules and "extra packages" 84 # evaluate modules and "extra packages"
58 warn "PG: evaluating modules and \"extra packages\"\n"; 85 warn "PG: evaluating modules and \"extra packages\"\n";
59 my @modules = @{ $courseEnv->{pg}->{modules} }; 86 my @modules = @{ $courseEnv->{pg}->{modules} };
60 foreach my $module_packages (@modules) { 87 foreach my $module_packages_ref (@modules) {
61 # the first item in $module_packages is the main package 88 my ($module, @extra_packages) = @$module_packages_ref;
89 # the first item is the main package
62 $translator->evaluate_modules(shift @$module_packages); 90 $translator->evaluate_modules($module);
63 # the remaining items are "extra" packages 91 # the remaining items are "extra" packages
64 $translator->load_extra_packages(@$module_packages); 92 $translator->load_extra_packages(@extra_packages);
65 } 93 }
66 94
67 # set the environment (from defineProblemEnvir) 95 # set the environment (from defineProblemEnvir)
68 warn "PG: setting the environment (from defineProblemEnvir)\n"; 96 warn "PG: setting the environment (from defineProblemEnvir)\n";
69 $translator->environment(defineProblemEnvir( 97 $translator->environment(defineProblemEnvir(
70 $courseEnv, $user, $key, $set, $problem, $psvn, $formFields, $translationOptions)); 98 $courseEnv,
99 $user,
100 $key,
101 $set,
102 $problem,
103 $psvn,
104 $formFields,
105 $translationOptions,
106 ));
71 107
72 # initialize the Translator 108 # initialize the Translator
73 warn "PG: initializing the Translator\n"; 109 warn "PG: initializing the Translator\n";
74 $translator->initialize(); 110 $translator->initialize();
75 111
88 warn "PG: setting the opcode mask (using default values)\n"; 124 warn "PG: setting the opcode mask (using default values)\n";
89 $translator->set_mask(); 125 $translator->set_mask();
90 126
91 # store the problem source 127 # store the problem source
92 warn "PG: storing the problem source\n"; 128 warn "PG: storing the problem source\n";
129 my $sourceFile = $problem->source_file;
93 my $sourceFile = $courseEnv->{courseDirs}->{templates}."/".$problem->source_file; 130 $sourceFile = $courseEnv->{courseDirs}->{templates}."/".$sourceFile
131 unless ($sourceFile =~ /^\//);
94 $translator->source_string(readFile($sourceFile)); 132 eval { $translator->source_string(readFile($sourceFile)) };
133 if ($@) {
134 # well, we couldn't get the problem source, for some reason.
135 return bless {
136 translator => $translator,
137 head_text => "",
138 body_text => <<EOF,
139WeBWorK::Utils::readFile($sourceFile) says:
140$@
141EOF
142 answers => {},
143 result => {},
144 state => {},
145 errors => "Failed to read the problem source file.",
146 warnings => undef,
147 flags => {error_flag => 1},
148 }, $class;
149 }
95 150
96 # install a safety filter (&safetyFilter) 151 # install a safety filter (&safetyFilter)
97 warn "PG: installing a safety filter\n"; 152 warn "PG: installing a safety filter\n";
98 $translator->rf_safety_filter(\&safetyFilter); 153 $translator->rf_safety_filter(\&safetyFilter);
99 154
100 # translate the PG source into text 155 # translate the PG source into text
101 warn "PG: translating the PG source into text\n"; 156 warn "PG: translating the PG source into text\n";
102 $translator->translate(); 157 $translator->translate();
103 158
104 # [in Problem.pm and processProblem8.pl, "install a grader" is here] 159 my ($result, $state); # we'll need these on the other side of the if block!
105 160 if ($translationOptions->{processAnswers}) {
161
106 # process student answers 162 # process student answers
107 warn "PG: processing student answers\n"; 163 warn "PG: processing student answers\n";
108 $translator->process_answers($formFields); 164 $translator->process_answers($formFields);
109 165
110 # retrieve the problem state and give it to the translator 166 # retrieve the problem state and give it to the translator
111 warn "PG: retrieving the problem state and giving it to the translator\n"; 167 warn "PG: retrieving the problem state and giving it to the translator\n";
112 $translator->rh_problem_state({ 168 $translator->rh_problem_state({
113 recorded_score => $problem->status, 169 recorded_score => $problem->status,
114 num_of_correct_ans => $problem->num_correct, 170 num_of_correct_ans => $problem->num_correct,
115 num_of_incorrect_ans => $problem->num_incorrect, 171 num_of_incorrect_ans => $problem->num_incorrect,
116 }); 172 });
117 173
118 # determine an entry order -- the ANSWER_ENTRY_ORDER flag is built by 174 # determine an entry order -- the ANSWER_ENTRY_ORDER flag is built by
119 # the PG macro package (PG.pl) 175 # the PG macro package (PG.pl)
120 warn "PG: determining an entry order\n"; 176 warn "PG: determining an entry order\n";
121 my @answerOrder = 177 my @answerOrder =
122 $translator->rh_flags->{ANSWER_ENTRY_ORDER} 178 $translator->rh_flags->{ANSWER_ENTRY_ORDER}
123 ? @{ $translator->rh_flags->{ANSWER_ENTRY_ORDER} } 179 ? @{ $translator->rh_flags->{ANSWER_ENTRY_ORDER} }
124 : keys %{ $translator->rh_evaluated_answers }; 180 : keys %{ $translator->rh_evaluated_answers };
125 181
126 # install a grader -- use the one specified in the problem, 182 # install a grader -- use the one specified in the problem,
127 # or fall back on the default from the course environment. 183 # or fall back on the default from the course environment.
128 # (two magic strings are accepted, to avoid having to 184 # (two magic strings are accepted, to avoid having to
129 # reference code when it would be difficult.) 185 # reference code when it would be difficult.)
130 warn "PG: installing a grader\n"; 186 warn "PG: installing a grader\n";
131 my $grader = $translator->rh_flags->{PROBLEM_GRADER_TO_USE} 187 my $grader = $translator->rh_flags->{PROBLEM_GRADER_TO_USE}
132 || $courseEnv->{pg}->{options}->{grader}; 188 || $courseEnv->{pg}->{options}->{grader};
133 $grader = $translator->rf_std_problem_grader 189 $grader = $translator->rf_std_problem_grader
134 if $grader eq "std_problem_grader"; 190 if $grader eq "std_problem_grader";
135 $grader = $translator->rf_avg_problem_grader 191 $grader = $translator->rf_avg_problem_grader
136 if $grader eq "avg_problem_grader"; 192 if $grader eq "avg_problem_grader";
137 die "Problem grader $grader is not a CODE reference." 193 die "Problem grader $grader is not a CODE reference."
138 unless ref $grader eq "CODE"; 194 unless ref $grader eq "CODE";
139 $translator->rf_problem_grader($grader); 195 $translator->rf_problem_grader($grader);
140 196
141 # grade the problem 197 # grade the problem
142 warn "PG: grading the problem\n"; 198 warn "PG: grading the problem\n";
143 my ($result, $state) = $translator->grade_problem( 199 ($result, $state) = $translator->grade_problem(
144 answers_submitted => $translationOptions->{processAnswers}, 200 answers_submitted => $translationOptions->{processAnswers},
145 ANSWER_ENTRY_ORDER => \@answerOrder, 201 ANSWER_ENTRY_ORDER => \@answerOrder,
146 ); 202 );
203
204 }
147 205
148 # return an object which contains the translator and the results of 206 # return an object which contains the translator and the results of
149 # the translation process. this is DIFFERENT from the "format expected 207 # the translation process. this is DIFFERENT from the "format expected
150 # by Webwork.pm (and I believe processProblem8, but check.)" 208 # by Webwork.pm (and I believe processProblem8, but check.)"
151 return bless { 209 return bless {
180 # PG environment variables 238 # PG environment variables
181 # from docs/pglanguage/pgreference/environmentvariables as of 06/25/2002 239 # from docs/pglanguage/pgreference/environmentvariables as of 06/25/2002
182 # any changes are noted by "ADDED:" or "REMOVED:" 240 # any changes are noted by "ADDED:" or "REMOVED:"
183 241
184 # Vital state information 242 # Vital state information
185 # ADDED: displayHintsQ, displaySolutionsQ 243 # ADDED: displayHintsQ, displaySolutionsQ, refreshMath2img
186 244
187 $envir{psvn} = $psvn; 245 $envir{psvn} = $psvn;
188 $envir{psvnNumber} = $envir{psvn}; 246 $envir{psvnNumber} = $envir{psvn};
189 $envir{probNum} = $problem->id; 247 $envir{probNum} = $problem->id;
190 $envir{questionNumber} = $envir{probNum}; 248 $envir{questionNumber} = $envir{probNum};
228 # REMOVED: refSubmittedAnswers 286 # REMOVED: refSubmittedAnswers
229 287
230 $envir{inputs_ref} = $formFields; 288 $envir{inputs_ref} = $formFields;
231 289
232 # External Programs 290 # External Programs
291 # ADDED: externalLaTeXPath, externalDvipngPath, externalMath2imgPath
233 292
234 $envir{externalTTHPath} = $courseEnv->{externalPrograms}->{tth}; 293 $envir{externalTTHPath} = $courseEnv->{externalPrograms}->{tth};
294 $envir{externalLaTeXPath} = $courseEnv->{externalPrograms}->{latex};
295 $envir{externalDvipngPath} = $courseEnv->{externalPrograms}->{dvipng};
235 $envir{externalMath2imgPath} = $courseEnv->{externalPrograms}->{math2img}; 296 $envir{externalMath2imgPath} = $courseEnv->{externalPrograms}->{math2img};
236 297
237 # Directories and URLs 298 # Directories and URLs
238 # REMOVED: courseName 299 # REMOVED: courseName
300 # ADDED: dvipngTempDir
301
239 302
240 $envir{cgiDirectory} = undef; 303 $envir{cgiDirectory} = undef;
241 $envir{cgiURL} = undef; 304 $envir{cgiURL} = undef;
242 $envir{classDirectory} = undef; 305 $envir{classDirectory} = undef;
243 $envir{courseScriptsDirectory} = $courseEnv->{webworkDirs}->{macros}."/"; 306 $envir{courseScriptsDirectory} = $courseEnv->{webworkDirs}->{macros}."/";
247 $envir{templateDirectory} = $courseEnv->{courseDirs}->{templates}."/"; 310 $envir{templateDirectory} = $courseEnv->{courseDirs}->{templates}."/";
248 $envir{tempDirectory} = $courseEnv->{courseDirs}->{html_temp}."/"; 311 $envir{tempDirectory} = $courseEnv->{courseDirs}->{html_temp}."/";
249 $envir{tempURL} = $courseEnv->{courseURLs}->{html_temp}; 312 $envir{tempURL} = $courseEnv->{courseURLs}->{html_temp};
250 $envir{scriptDirectory} = undef; 313 $envir{scriptDirectory} = undef;
251 $envir{webworkDocsURL} = $courseEnv->{webworkURLs}->{docs}; 314 $envir{webworkDocsURL} = $courseEnv->{webworkURLs}->{docs};
315 $envir{dvipngTempDir} = tempdir("webwork-dvipng-XXXXXXXX", TMPDIR => 1);
252 316
253 # Default values for evaluating answers 317 # Default values for evaluating answers
254 318
255 my $ansEvalDefaults = $courseEnv->{pg}->{ansEvalDefaults}; 319 my $ansEvalDefaults = $courseEnv->{pg}->{ansEvalDefaults};
256 $envir{$_} = $ansEvalDefaults->{$_} foreach (keys %$ansEvalDefaults); 320 $envir{$_} = $ansEvalDefaults->{$_} foreach (keys %$ansEvalDefaults);
263} 327}
264 328
265sub translateDisplayModeNames($) { 329sub translateDisplayModeNames($) {
266 my $name = shift; 330 my $name = shift;
267 return { 331 return {
332 tex => "TeX",
268 plainText => "HTML", 333 plainText => "HTML",
269 formattedText => "HTML_tth", 334 formattedText => "HTML_tth",
270 images => "HTML_img" 335 images => "HTML_img"
271 }->{$name}; 336 }->{$name};
272} 337}
300__END__ 365__END__
301 366
302=head1 SYNOPSIS 367=head1 SYNOPSIS
303 368
304 $pg = WeBWorK::PG->new( 369 $pg = WeBWorK::PG->new(
305 $courseEnv, # a WeBWorK::CourseEnvironment object 370 $courseEnv, # a WeBWorK::CourseEnvironment object
306 $userName, 371 $user, # a WeBWorK::User object
307 $sessionKey, 372 $sessionKey,
308 $setName, 373 $set, # a WeBWorK::Set object
309 $problemNumber, 374 $problem, # a WeBWorK::Problem object
375 $psvn,
376 $formFields # in &WeBWorK::Form::Vars format
310 { # translation options 377 { # translation options
311 displayMode => "images", # (plainText|formattedText|images) 378 displayMode => "images", # (plainText|formattedText|images)
312 showHints => 1, # (0|1) 379 showHints => 1, # (0|1)
313 showSolutions => 0, # (0|1) 380 showSolutions => 0, # (0|1)
314 refreshMath2img => 0, # (0|1) 381 refreshMath2img => 0, # (0|1)
315 processAnswers => 1, # (0|1) 382 processAnswers => 1, # (0|1)
316 }, 383 },
317 $formFields # in WeBWorK::Form::Vars format
318 ); 384 );
319 385
320 $translator = $pg->{translator}; # WeBWorK::PG::Translator 386 $translator = $pg->{translator}; # WeBWorK::PG::Translator
321 $body = $pg->{body_text}; # text string 387 $body = $pg->{body_text}; # text string
322 $header = $pg->{head_text}; # text string 388 $header = $pg->{head_text}; # text string
335 401
336=head1 CONSTRUCTION 402=head1 CONSTRUCTION
337 403
338=over 404=over
339 405
340=item new (ENVIRONMENT, USER, KEY, SET, PROBLEM, OPTIONS, FIELDS) 406=item new (ENVIRONMENT, USER, KEY, SET, PROBLEM, PSVN, FIELDS, OPTIONS)
341 407
342The C<new> method creates a translator, initializes it using the parameters 408The C<new> method creates a translator, initializes it using the parameters
343specified, translates a PG file, and processes answers. It returns a reference 409specified, translates a PG file, and processes answers. It returns a reference
344to a blessed hash containing the results of the translation process. 410to a blessed hash containing the results of the translation process.
345 411
353 419
354a WeBWorK::CourseEnvironment object 420a WeBWorK::CourseEnvironment object
355 421
356=item USER 422=item USER
357 423
358the name of the user for whom to render 424a WeBWorK::User object
359 425
360=item KEY 426=item KEY
361 427
362the session key of the current session 428the session key of the current session
363 429
364=item SET 430=item SET
365 431
366the name of the problem set from which to get the problem 432a WeBWorK::Set object
367 433
368=item PROBLEM 434=item PROBLEM
369 435
370the number of the problem to render 436a WeBWorK::Problem object. The contents of the source_file field can specify a
437PG file either by absolute path or path relative to the "templates" directory.
438I<The caller should remove taint from this value before passing!>
371 439
372=item OPTIONS 440=item PSVN
373 441
374a reference to a hash containing the following data: 442the problem set version number
375
376=over
377
378=item displayMode
379
380one of "plainText", "formattedText", or "images"
381
382=item showHints
383
384boolean, render hints
385
386=item showSolutions
387
388boolean, render solutions
389
390=item refreshMath2img
391
392boolean, force images created by math2img (in "images" mode) to be recreated,
393even if the PG source has not been updated.
394
395=item processAnswers
396
397boolean, call answer evaluators and graders
398
399=back
400 443
401=item FIELDS 444=item FIELDS
402 445
403a reference to a hash (as returned by &WeBWorK::Form::Vars) containing form 446a reference to a hash (as returned by &WeBWorK::Form::Vars) containing form
404fields submitted by a problem processor. The translator will look for fields 447fields submitted by a problem processor. The translator will look for fields
405like "AnSwEr[0-9]" containing submitted student answers. 448like "AnSwEr[0-9]" containing submitted student answers.
406 449
450=item OPTIONS
451
452a reference to a hash containing the following data:
453
454=over
455
456=item displayMode
457
458one of "plainText", "formattedText", or "images"
459
460=item showHints
461
462boolean, render hints
463
464=item showSolutions
465
466boolean, render solutions
467
468=item refreshMath2img
469
470boolean, force images created by math2img (in "images" mode) to be recreated,
471even if the PG source has not been updated.
472
473=item processAnswers
474
475boolean, call answer evaluators and graders
476
477=back
478
407=back 479=back
408 480
409=head2 RETURN VALUE 481=head2 RETURN VALUE
410 482
411The C<new> method returns a blessed hash reference containing the following 483The C<new> method returns a blessed hash reference containing the following

Legend:
Removed from v.455  
changed lines
  Added in v.526

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9