[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 1239 Revision 2166
1################################################################################ 1################################################################################
2# WeBWorK mod_perl (c) 2000-2002 WeBWorK Project 2# WeBWorK Online Homework Delivery System
3# $Id$ 3# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/
4# $CVSHeader: webwork-modperl/lib/WeBWorK/PG.pm,v 1.47 2004/01/05 01:02:41 sh002i Exp $
5#
6# This program is free software; you can redistribute it and/or modify it under
7# the terms of either: (a) the GNU General Public License as published by the
8# Free Software Foundation; either version 2, or (at your option) any later
9# version, or (b) the "Artistic License" which comes with this package.
10#
11# This program is distributed in the hope that it will be useful, but WITHOUT
12# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
14# Artistic License for more details.
4################################################################################ 15################################################################################
5 16
6package WeBWorK::PG; 17package WeBWorK::PG;
7 18
8=head1 NAME 19=head1 NAME
9 20
10WeBWorK::PG - Wrap the action of the PG Translator in an easy-to-use API. 21WeBWorK::PG - Invoke one of several PG rendering methods using an easy-to-use
22API.
11 23
12=cut 24=cut
13 25
14use strict; 26use strict;
15use warnings; 27use warnings;
16use File::Path qw(rmtree);
17use WeBWorK::PG::ImageGenerator; 28use WeBWorK::PG::ImageGenerator;
18use WeBWorK::PG::Translator;
19use WeBWorK::Utils qw(readFile formatDateTime writeTimingLogEntry makeTempDirectory); 29use WeBWorK::Utils qw(runtime_use formatDateTime makeTempDirectory);
20 30
21sub new { 31sub new {
22 my $invocant = shift; 32 shift; # throw away invocant -- we don't need it
23 my $class = ref($invocant) || $invocant; 33 my ($ce, $user, $key, $set, $problem, $psvn, $formFields,
24 my (
25 $ce,
26 $user,
27 $key,
28 $set,
29 $problem,
30 $psvn,
31 $formFields, # in CGI::Vars format
32 $translationOptions, # hashref containing options for the
33 # translator, such as whether to show
34 # hints and the display mode to use
35 ) = @_;
36
37 # write timing log entry
38 writeTimingLogEntry($ce, "WeBWorK::PG::new",
39 "user=".$user->user_id.",problem=".$ce->{courseName}."/".$set->set_id."/".$problem->problem_id.",mode=".$translationOptions->{displayMode},
40 "begin");
41
42 # install a local warn handler to collect warnings
43 my $warnings = "";
44 local $SIG{__WARN__} = sub { $warnings .= shift }
45 if $ce->{pg}->{options}->{catchWarnings};
46
47 # create a Translator
48 #warn "PG: creating a Translator\n";
49 my $translator = WeBWorK::PG::Translator->new;
50
51 # set the directory hash
52 #warn "PG: setting the directory hash\n";
53 $translator->rh_directories({
54 courseScriptsDirectory => $ce->{pg}->{directories}->{macros},
55 macroDirectory => $ce->{courseDirs}->{macros},
56 templateDirectory => $ce->{courseDirs}->{templates},
57 tempDirectory => $ce->{courseDirs}->{html_temp},
58 });
59
60 # evaluate modules and "extra packages"
61 #warn "PG: evaluating modules and \"extra packages\"\n";
62 my @modules = @{ $ce->{pg}->{modules} };
63 foreach my $module_packages_ref (@modules) {
64 my ($module, @extra_packages) = @$module_packages_ref;
65 # the first item is the main package
66 $translator->evaluate_modules($module);
67 # the remaining items are "extra" packages
68 $translator->load_extra_packages(@extra_packages);
69 }
70
71 # set the environment (from defineProblemEnvir)
72 #warn "PG: setting the environment (from defineProblemEnvir)\n";
73 my $envir = defineProblemEnvir(
74 $ce,
75 $user,
76 $key,
77 $set,
78 $problem,
79 $psvn,
80 $formFields,
81 $translationOptions, 34 $translationOptions) = @_;
82 );
83 $translator->environment($envir);
84 35
85 # initialize the Translator 36 my $renderer = $ce->{pg}->{renderer};
86 #warn "PG: initializing the Translator\n";
87 $translator->initialize();
88 37
89 # load IO.pl, PG.pl, and dangerousMacros.pl using unrestricted_load 38 runtime_use $renderer;
90 # i'd like to change this at some point to have the same sort of interface to global.conf
91 # that the module loading does -- have a list of macros to load unrestrictedly.
92 #warn "PG: loading IO.pl, PG.pl, and dangerousMacros.pl using unrestricted_load\n";
93 foreach (qw(IO.pl PG.pl dangerousMacros.pl)) {
94 my $macroPath = $ce->{pg}->{directories}->{macros} . "/$_";
95 my $err = $translator->unrestricted_load($macroPath);
96 warn "Error while loading $macroPath: $err" if $err;
97 }
98 39
99 # set the opcode mask (using default values) 40 return $renderer->new(@_);
100 #warn "PG: setting the opcode mask (using default values)\n";
101 $translator->set_mask();
102
103 # store the problem source
104 #warn "PG: storing the problem source\n";
105 my $sourceFile = $problem->source_file;
106 $sourceFile = $ce->{courseDirs}->{templates}."/".$sourceFile
107 unless ($sourceFile =~ /^\//);
108 eval { $translator->source_string(readFile($sourceFile)) };
109 if ($@) {
110 # well, we couldn't get the problem source, for some reason.
111 return bless {
112 translator => $translator,
113 head_text => "",
114 body_text => <<EOF,
115WeBWorK::Utils::readFile($sourceFile) says:
116$@
117EOF
118 answers => {},
119 result => {},
120 state => {},
121 errors => "Failed to read the problem source file.",
122 warnings => $warnings,
123 flags => {error_flag => 1},
124 }, $class;
125 }
126
127 # install a safety filter (&safetyFilter)
128 #warn "PG: installing a safety filter\n";
129 $translator->rf_safety_filter(\&safetyFilter);
130
131 # write timing log entry -- the translator is now all set up
132 writeTimingLogEntry($ce, "WeBWorK::PG::new",
133 "initialized",
134 "intermediate");
135
136 # translate the PG source into text
137 #warn "PG: translating the PG source into text\n";
138 $translator->translate();
139
140 # after we're done translating, we may have to clean up after the
141 # translator:
142
143 # for example, HTML_img mode uses a tempdir for dvipng's temp files.\
144 # We have to remove it.
145 if ($envir->{dvipngTempDir}) {
146 rmtree($envir->{dvipngTempDir}, 0, 0);
147 }
148
149 # HTML_dpng, on the other hand, uses an ImageGenerator. We have to
150 # render the queued equations.
151 if ($envir->{imagegen}) {
152 my $sourceFile = $ce->{courseDirs}->{templates} . "/" . $problem->source_file;
153 my %mtimeOption = -e $sourceFile
154 ? (mtime => (stat $sourceFile)[9])
155 : ();
156
157 $envir->{imagegen}->render(
158 refresh => $translationOptions->{refreshMath2img},
159 %mtimeOption,
160 );
161 }
162
163 my ($result, $state); # we'll need these on the other side of the if block!
164 if ($translationOptions->{processAnswers}) {
165
166 # process student answers
167 #warn "PG: processing student answers\n";
168 $translator->process_answers($formFields);
169
170 # retrieve the problem state and give it to the translator
171 #warn "PG: retrieving the problem state and giving it to the translator\n";
172 $translator->rh_problem_state({
173 recorded_score => $problem->status,
174 num_of_correct_ans => $problem->num_correct,
175 num_of_incorrect_ans => $problem->num_incorrect,
176 });
177
178 # determine an entry order -- the ANSWER_ENTRY_ORDER flag is built by
179 # the PG macro package (PG.pl)
180 #warn "PG: determining an entry order\n";
181 my @answerOrder =
182 $translator->rh_flags->{ANSWER_ENTRY_ORDER}
183 ? @{ $translator->rh_flags->{ANSWER_ENTRY_ORDER} }
184 : keys %{ $translator->rh_evaluated_answers };
185
186 # install a grader -- use the one specified in the problem,
187 # or fall back on the default from the course environment.
188 # (two magic strings are accepted, to avoid having to
189 # reference code when it would be difficult.)
190 #warn "PG: installing a grader\n";
191 my $grader = $translator->rh_flags->{PROBLEM_GRADER_TO_USE}
192 || $ce->{pg}->{options}->{grader};
193 $grader = $translator->rf_std_problem_grader
194 if $grader eq "std_problem_grader";
195 $grader = $translator->rf_avg_problem_grader
196 if $grader eq "avg_problem_grader";
197 die "Problem grader $grader is not a CODE reference."
198 unless ref $grader eq "CODE";
199 $translator->rf_problem_grader($grader);
200
201 # grade the problem
202 #warn "PG: grading the problem\n";
203 ($result, $state) = $translator->grade_problem(
204 answers_submitted => $translationOptions->{processAnswers},
205 ANSWER_ENTRY_ORDER => \@answerOrder,
206 );
207
208 }
209
210 # write timing log entry
211 writeTimingLogEntry($ce, "WeBWorK::PG::new", "", "end");
212
213 # return an object which contains the translator and the results of
214 # the translation process. this is DIFFERENT from the "format expected
215 # by Webwork.pm (and I believe processProblem8, but check.)"
216 return bless {
217 translator => $translator,
218 head_text => ${ $translator->r_header },
219 body_text => ${ $translator->r_text },
220 answers => $translator->rh_evaluated_answers,
221 result => $result,
222 state => $state,
223 errors => $translator->errors,
224 warnings => $warnings,
225 flags => $translator->rh_flags,
226 }, $class;
227} 41}
228
229# -----
230 42
231sub defineProblemEnvir { 43sub defineProblemEnvir {
232 my ( 44 my (
45 $self,
233 $ce, 46 $ce,
234 $user, 47 $user,
235 $key, 48 $key,
236 $set, 49 $set,
237 $problem, 50 $problem,
262 $envir{displayMode} = translateDisplayModeNames($options->{displayMode}); 75 $envir{displayMode} = translateDisplayModeNames($options->{displayMode});
263 $envir{languageMode} = $envir{displayMode}; 76 $envir{languageMode} = $envir{displayMode};
264 $envir{outputMode} = $envir{displayMode}; 77 $envir{outputMode} = $envir{displayMode};
265 $envir{displayHintsQ} = $options->{showHints}; 78 $envir{displayHintsQ} = $options->{showHints};
266 $envir{displaySolutionsQ} = $options->{showSolutions}; 79 $envir{displaySolutionsQ} = $options->{showSolutions};
267 # FIXME: this is HTML_img specific
268 #$envir{refreshMath2img} = $options->{refreshMath2img};
269 $envir{texDisposition} = "pdf"; # in webwork-modperl, we use pdflatex 80 $envir{texDisposition} = "pdf"; # in webwork2, we use pdflatex
270 81
271 # Problem Information 82 # Problem Information
272 # ADDED: courseName, formatedDueDate 83 # ADDED: courseName, formatedDueDate
273 84
274 $envir{openDate} = $set->open_date; 85 $envir{openDate} = $set->open_date;
312 $envir{externalGif2PngPath} = $ce->{externalPrograms}->{gif2png}; 123 $envir{externalGif2PngPath} = $ce->{externalPrograms}->{gif2png};
313 124
314 # Directories and URLs 125 # Directories and URLs
315 # REMOVED: courseName 126 # REMOVED: courseName
316 # ADDED: dvipngTempDir 127 # ADDED: dvipngTempDir
128 # ADDED: jsMathURL
317 129
318 $envir{cgiDirectory} = undef; 130 $envir{cgiDirectory} = undef;
319 $envir{cgiURL} = undef; 131 $envir{cgiURL} = undef;
320 $envir{classDirectory} = undef; 132 $envir{classDirectory} = undef;
321 $envir{courseScriptsDirectory} = $ce->{pg}->{directories}->{macros}."/"; 133 $envir{courseScriptsDirectory} = $ce->{pg}->{directories}->{macros}."/";
325 $envir{templateDirectory} = $ce->{courseDirs}->{templates}."/"; 137 $envir{templateDirectory} = $ce->{courseDirs}->{templates}."/";
326 $envir{tempDirectory} = $ce->{courseDirs}->{html_temp}."/"; 138 $envir{tempDirectory} = $ce->{courseDirs}->{html_temp}."/";
327 $envir{tempURL} = $ce->{courseURLs}->{html_temp}."/"; 139 $envir{tempURL} = $ce->{courseURLs}->{html_temp}."/";
328 $envir{scriptDirectory} = undef; 140 $envir{scriptDirectory} = undef;
329 $envir{webworkDocsURL} = $ce->{webworkURLs}->{docs}."/"; 141 $envir{webworkDocsURL} = $ce->{webworkURLs}->{docs}."/";
330 # FIXME: this is HTML_img mode-specific 142 $envir{jsMathURL} = $ce->{webworkURLs}->{jsMath};
331 #$envir{dvipngTempDir} = $options->{displayMode} eq 'images'
332 # ? makeTempDirectory($envir{tempDirectory}, "webwork-dvipng")
333 # : undef;
334 143
335 # Information for sending mail 144 # Information for sending mail
336 145
337 $envir{mailSmtpServer} = $ce->{mail}->{smtpServer}; 146 $envir{mailSmtpServer} = $ce->{mail}->{smtpServer};
338 $envir{mailSmtpSender} = $ce->{mail}->{smtpSender}; 147 $envir{mailSmtpSender} = $ce->{mail}->{smtpSender};
349 $basename .= ".$envir{problemSeed}" if $envir{problemSeed}; 158 $basename .= ".$envir{problemSeed}" if $envir{problemSeed};
350 159
351 # Object for generating equation images 160 # Object for generating equation images
352 $envir{imagegen} = WeBWorK::PG::ImageGenerator->new( 161 $envir{imagegen} = WeBWorK::PG::ImageGenerator->new(
353 tempDir => $ce->{webworkDirs}->{tmp}, # global temp dir 162 tempDir => $ce->{webworkDirs}->{tmp}, # global temp dir
354 dir => $envir{tempDirectory},
355 url => $envir{tempURL},
356 basename => $basename,
357 latex => $envir{externalLaTeXPath}, 163 latex => $envir{externalLaTeXPath},
358 dvipng => $envir{externalDvipngPath}, 164 dvipng => $envir{externalDvipngPath},
165 useCache => 1,
166 cacheDir => $ce->{webworkDirs}->{equationCache},
167 cacheURL => $ce->{webworkURLs}->{equationCache},
168 cacheDB => $ce->{webworkFiles}->{equationCacheDB},
359 ); 169 );
360 170
361 # Other things... 171 # Other things...
362 $envir{QUIZ_PREFIX} = $options->{QUIZ_PREFIX}; # used by quizzes 172 $envir{QUIZ_PREFIX} = $options->{QUIZ_PREFIX}; # used by quizzes
363 $envir{PROBLEM_GRADER_TO_USE} = $ce->{pg}->{options}->{grader}; 173 $envir{PROBLEM_GRADER_TO_USE} = $ce->{pg}->{options}->{grader};
377 return { 187 return {
378 tex => "TeX", 188 tex => "TeX",
379 plainText => "HTML", 189 plainText => "HTML",
380 formattedText => "HTML_tth", 190 formattedText => "HTML_tth",
381 images => "HTML_dpng", # "HTML_img", 191 images => "HTML_dpng", # "HTML_img",
192 jsMath => "HTML_jsMath",
382 }->{$name}; 193 }->{$name};
383} 194}
384 195
385sub safetyFilter { 196sub oldSafetyFilter {
386 my $answer = shift; # accepts one answer and checks it 197 my $answer = shift; # accepts one answer and checks it
387 my $submittedAnswer = $answer; 198 my $submittedAnswer = $answer;
388 $answer = '' unless defined $answer; 199 $answer = '' unless defined $answer;
389 my ($errorno); 200 my ($errorno);
390 $answer =~ tr/\000-\037/ /; 201 $answer =~ tr/\000-\037/ /;
404 } 215 }
405 $errorno = 0; 216 $errorno = 0;
406 return($answer, $errorno); 217 return($answer, $errorno);
407} 218}
408 219
220sub nullSafetyFilter {
221 return shift, 0; # no errors
222}
223
4091; 2241;
410 225
411__END__ 226__END__
412 227
413=head1 SYNOPSIS 228=head1 SYNOPSIS
439 $warnings = $pg->{warnings}; # text string 254 $warnings = $pg->{warnings}; # text string
440 $flags = $pg->{flags}; # hash reference 255 $flags = $pg->{flags}; # hash reference
441 256
442=head1 DESCRIPTION 257=head1 DESCRIPTION
443 258
444WeBWorK::PG encapsulates the PG translation process, making multiple calls to 259WeBWorK::PG is a factory for modules which use the WeBWorK::PG API. Notable
445WeBWorK::PG::Translator. Much of the flexibility of the Translator is hidden, 260modules which use this API (and exist) are WeBWorK::PG::Local and
446instead making choices that are appropriate for the webwork-modperl system. 261WeBWorK::PG::Remote. The course environment key $pg{renderer} is consulted to
262determine which render to use.
447 263
448=head1 CONSTRUCTION 264=head1 THE WEBWORK::PG API
265
266Modules which support this API must implement the following method:
449 267
450=over 268=over
451 269
452=item new (ENVIRONMENT, USER, KEY, SET, PROBLEM, PSVN, FIELDS, OPTIONS) 270=item new ENVIRONMENT, USER, KEY, SET, PROBLEM, PSVN, FIELDS, OPTIONS
453 271
454The C<new> method creates a translator, initializes it using the parameters 272The C<new> method creates a translator, initializes it using the parameters
455specified, translates a PG file, and processes answers. It returns a reference 273specified, translates a PG file, and processes answers. It returns a reference
456to a blessed hash containing the results of the translation process. 274to a blessed hash containing the results of the translation process.
457 275
513boolean, render solutions 331boolean, render solutions
514 332
515=item refreshMath2img 333=item refreshMath2img
516 334
517boolean, force images created by math2img (in "images" mode) to be recreated, 335boolean, force images created by math2img (in "images" mode) to be recreated,
518even if the PG source has not been updated. FIXME: change the name of this 336even if the PG source has not been updated. FIXME: remove this option.
519option to "refreshEquations" and update the docs accordingly.
520 337
521=item processAnswers 338=item processAnswers
522 339
523boolean, call answer evaluators and graders 340boolean, call answer evaluators and graders
524 341
572 389
573A hash containing PG_flags (see the Translator docs). 390A hash containing PG_flags (see the Translator docs).
574 391
575=back 392=back
576 393
577=head1 OPERATION 394=head1 METHODS PROVIDED BY THE BASE CLASS
578 395
579WeBWorK::PG goes through the following operations when constructed: 396The following methods are provided for use by subclasses of WeBWorK::PG.
580 397
581=over 398=over
582 399
583=item Get database information 400=item defineProblemEnvir ENVIRONMENT, USER, KEY, SET, PROBLEM, PSVN, FIELDS, OPTIONS
584 401
585Retrieve information about the current user, set, and problem from the 402Generate a problem environment hash to pass to the renderer.
586database.
587 403
588=item Create a translator 404=item translateDisplayModeNames NAME
589 405
590Instantiate a WeBWorK::PG::Translator object. 406NAME contains
591
592=item Set the directory hash
593
594Set the translator's directory hash (courseScripts, macros, templates, and temp
595directories) from the course environment.
596
597=item Evaluate PG modules
598
599Using the module list from the course environment (pg->modules), perform a
600"use"-like operation to evaluate modules at runtime.
601
602=item Set the problem environment
603
604Use data from the user, set, and problem, as well as the course environemnt and
605translation options, to set the problem environment.
606
607=item Initialize the translator
608
609Call &WeBWorK::PG::Translator::initialize. What more do you want?
610
611=item Load PG.pl and dangerousMacros.pl
612
613These macros must be loaded without opcode masking, so they are loaded here.
614
615=item Set the opcode mask
616
617Set the opcode mask to the default specified by WeBWorK::PG::Translator.
618
619=item Load the problem source
620
621Give the problem source to the translator.
622
623=item Install a safety filter
624
625The safety filter is used to preprocess student input before evaluation. The
626default safety filter, &WeBWorK::PG::safetyFilter, is used.
627
628=item Translate the problem source
629
630Call &WeBWorK::PG::Translator::translate to render the problem source into the
631format given by the display mode.
632
633=item Process student answers
634
635Use form field inputs to evaluate student answers.
636
637=item Load the problem state
638
639Use values from the database to initialize the problem state, so that the
640grader will have a point of reference.
641
642=item Determine an entry order
643
644Use the ANSWER_ENTRY_ORDER flag to determine the order of answers in the
645problem. This is important for problems with dependancies among parts.
646
647=item Install a grader
648
649Use the PROBLEM_GRADER_TO_USE flag, or a default from the course environment,
650to install a grader.
651
652=item Grade the problem
653
654Use the selected grader to grade the problem.
655 407
656=back 408=back
657 409
658=head1 AUTHOR 410=head1 AUTHOR
659 411

Legend:
Removed from v.1239  
changed lines
  Added in v.2166

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9