[system] / trunk / pg / macros / PG.pl Repository:
ViewVC logotype

Annotation of /trunk/pg/macros/PG.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 5789 - (view) (download) (as text)

1 : sh002i 5179 ################################################################################
2 : sh002i 5568 # WeBWorK Online Homework Delivery System
3 :     # Copyright 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
4 : gage 5789 # $CVSHeader: pg/macros/PG.pl,v 1.29.2.2.2.2 2008/06/25 20:05:23 gage Exp $
5 : sh002i 5568 #
6 : sh002i 5179 # 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 : sh002i 5568 #
11 : sh002i 5179 # 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.
15 :     ################################################################################
16 : gage 4997
17 : sh002i 5179 =head1 NAME
18 : sh002i 1050
19 : sh002i 5179 PG.pl - Provides core Program Generation Language functionality.
20 : sh002i 1050
21 : sh002i 5179 =head1 SYNPOSIS
22 : sh002i 1050
23 : sh002i 5179 In a PG problem:
24 : sh002i 1050
25 : sh002i 5568 DOCUMENT(); # should be the first statment in the problem
26 :    
27 :     loadMacros(.....); # (optional) load other macro files if needed.
28 :     # (loadMacros is defined in F<dangerousMacros.pl>)
29 :    
30 :     HEADER_TEXT(...); # (optional) used only for inserting javaScript into problems.
31 :    
32 :     TEXT( # insert text of problems
33 :     "Problem text to be displayed. ",
34 :     "Enter 1 in this blank:",
35 :     ANS_RULE(1,30) # ANS_RULE() defines an answer blank 30 characters long.
36 :     # It is defined in F<PGbasicmacros.pl>
37 :     );
38 :    
39 :     ANS(answer_evalutors); # see F<PGanswermacros.pl> for examples of answer evaluatiors.
40 :    
41 :     ENDDOCUMENT() # must be the last statement in the problem
42 : sh002i 1050
43 : sh002i 5179 =head1 DESCRIPTION
44 : sh002i 1050
45 : sh002i 5179 This file provides the fundamental macros that define the PG language. It
46 :     maintains a problem's text, header text, and answers:
47 : sh002i 1050
48 : sh002i 5179 =over
49 : sh002i 1050
50 : sh002i 5179 =item *
51 : sh002i 1050
52 : sh002i 5179 Problem text: The text to appear in the body of the problem. See TEXT()
53 :     below.
54 : sh002i 1050
55 : sh002i 5179 =item *
56 : sh002i 1050
57 : sh002i 5179 Header text: When a problem is processed in an HTML-based display mode,
58 :     this variable can contain text that the caller should place in the HEAD of the
59 :     resulting HTML page. See HEADER_TEXT() below.
60 : sh002i 1050
61 : sh002i 5179 =item *
62 : sh002i 1050
63 : sh002i 5179 Implicitly-labeled answers: Answers that have not been explicitly
64 :     assigned names, and are associated with their answer blanks by the order in
65 :     which they appear in the problem. These types of answers are designated using
66 :     the ANS() macro.
67 : sh002i 1050
68 : sh002i 5179 =item *
69 : sh002i 1050
70 : sh002i 5179 Explicitly-labeled answers: Answers that have been explicitly assigned
71 :     names with the LABELED_ANS() macro, or a macro that uses it. An explicitly-
72 :     labeled answer is associated with its answer blank by name.
73 : sh002i 1050
74 : sh002i 5179 =item *
75 : sh002i 1050
76 : sh002i 5179 "Extra" answers: Names of answer blanks that do not have a 1-to-1
77 :     correspondance to an answer evaluator. For example, in matrix problems, there
78 :     will be several input fields that correspond to the same answer evaluator.
79 : sh002i 1050
80 : sh002i 5179 =back
81 : sh002i 1050
82 : sh002i 5179 =head1 USAGE
83 : sh002i 1050
84 : sh002i 5179 This file is automatically loaded into the namespace of every PG problem. The
85 :     macros within can then be called to define the structure of the problem.
86 : sh002i 1050
87 : sh002i 5179 DOCUMENT() should be the first executable statement in any problem. It
88 :     initializes vriables and defines the problem environment.
89 : sh002i 1050
90 : sh002i 5179 ENDDOCUMENT() must be the last executable statement in any problem. It packs
91 :     up the results of problem processing for delivery back to WeBWorK.
92 : sh002i 1050
93 : sh002i 5179 The HEADER_TEXT(), TEXT(), and ANS() macros add to the header text string,
94 :     body text string, and answer evaluator queue, respectively.
95 : sh002i 1050
96 :     =cut
97 :    
98 : sh002i 5179 BEGIN {
99 :     be_strict();
100 :     }
101 : sh002i 1050
102 : sh002i 5179 sub _PG_init{
103 :    
104 :     }
105 :    
106 :     #package PG;
107 :    
108 : sh002i 1050 # Private variables for the PG.pl file.
109 :    
110 : sh002i 5663 # ^variable my $STRINGforOUTPUT
111 : sh002i 5658 my $STRINGforOUTPUT;
112 : sh002i 5663 # ^variable my $STRINGforHEADER_TEXT
113 : sh002i 5658 my $STRINGforHEADER_TEXT;
114 : sh002i 5663 # ^variable my @PG_ANSWERS
115 : sh002i 5658 my @PG_ANSWERS;
116 : sh002i 5663 # ^variable my @PG_UNLABELED_ANSWERS
117 : sh002i 5658 my @PG_UNLABELED_ANSWERS;
118 : sh002i 5663 # ^variable my %PG_ANSWERS_HASH
119 : sh002i 5658 my %PG_ANSWERS_HASH;
120 :    
121 : sh002i 5663 # ^variable our $PG_STOP_FLAG
122 : gage 3385 our $PG_STOP_FLAG;
123 : malsyned 1280
124 : gage 3385 # my variables are unreliable if two DOCUMENTS were to be called before an ENDDOCUMENT
125 : gage 1266 # there could be conflicts. As I understand the behavior of the Apache child
126 :     # this cannot occur -- a child finishes with one request before obtaining the next
127 : sh002i 1050
128 : sh002i 5179 ################################################################################
129 : sh002i 1050
130 : sh002i 5179 =head1 MACROS
131 : sh002i 1050
132 : sh002i 5179 These macros may be used from PG problem files.
133 :    
134 :     =over
135 :    
136 :     =item DOCUMENT()
137 :    
138 :     DOCUMENT() should be the first statement in each problem template. It can
139 : sh002i 1050 only be used once in each problem.
140 :    
141 : sh002i 5179 DOCUMENT() initializes some empty variables and unpacks the variables in the
142 :     %envir hash which is implicitly passed from WeBWorK to the problem. It must be
143 :     the first statement in any problem. It also unpacks any answers submitted and
144 :     places them in the @submittedAnswer list, saves the problem seed in
145 :     $PG_original_problemSeed in case you need it later, and initializes the pseudo
146 :     random number generator object in $PG_random_generator.
147 : sh002i 1050
148 :     You can reset the standard number generator using the command:
149 :    
150 : sh002i 5179 $PG_random_generator->srand($new_seed_value);
151 : sh002i 1050
152 : sh002i 5179 See also SRAND() in the L<PGbasicmacros.pl> file.
153 : sh002i 1050
154 :     =cut
155 :    
156 : sh002i 5658 # ^function DOCUMENT
157 :     # ^uses $STRINGforOUTPUT
158 :     # ^uses $STRINGforHEADER_TEXT
159 :     # ^uses @PG_ANSWERS
160 :     # ^uses $PG_STOP_FLAG
161 :     # ^uses @PG_UNLABELED_ANSWERS
162 :     # ^uses %PG_ANSWERS_HASH
163 :     # ^uses @PG_ANSWER_ENTRY_ORDER
164 :     # ^uses $ANSWER_PREFIX
165 :     # ^uses %PG_FLAGS
166 :     # ^uses $showPartialCorrectAnswers
167 :     # ^uses $showHints
168 :     # ^uses $solutionExists
169 :     # ^uses $hintExists
170 :     # ^uses $pgComment
171 :     # ^uses %gifs_created
172 :     # ^uses %envir
173 :     # ^uses $refSubmittedAnswers
174 :     # ^uses @submittedAnswers
175 :     # ^uses $PG_original_problemSeed
176 :     # ^uses $problemSeed
177 :     # ^uses $PG_random_generator
178 :     # ^uses $ans_rule_count
179 :     # ^uses $QUIZ_PREFIX
180 :     # (Also creates a package scalar named after each key in %envir containing a copy of the corresponding value.)
181 :     # ^uses &PGrandom::new
182 : sh002i 1050 sub DOCUMENT {
183 : gage 1253
184 : sh002i 1050 $STRINGforOUTPUT ="";
185 :     $STRINGforHEADER_TEXT ="";
186 :     @PG_ANSWERS=();
187 : gage 3385 $PG_STOP_FLAG=0;
188 : sh002i 1050 @PG_UNLABELED_ANSWERS = ();
189 :     %PG_ANSWERS_HASH = ();
190 : gage 1253 # FIXME: We are initializing these variables into both Safe::Root1 (the cached safe compartment)
191 :     # and Safe::Root2 (the current one)
192 :     # There is a good chance they won't be properly updated in one or the other of these compartments.
193 :    
194 : gage 5626
195 : gage 1266 # @main::PG_ANSWER_ENTRY_ORDER = ();
196 :     # $main::ANSWER_PREFIX = 'AnSwEr';
197 :     # %main::PG_FLAGS=(); #global flags
198 :     # $main::showPartialCorrectAnswers = 0 unless defined($main::showPartialCorrectAnswers );
199 :     # $main::showHint = 1 unless defined($main::showHint);
200 :     # $main::solutionExists =0;
201 :     # $main::hintExists =0;
202 :     # %main::gifs_created = ();
203 : gage 1253 eval(q!
204 : gage 5626 # set perl to use capital E for scientific notation: e.g. 5.4E-05 instead of 5.4e-05
205 : gage 5789 # $#="%G"; #FIXME -- this causes bad warnings in perl 5.10
206 : gage 5626
207 : gage 1253 @main::PG_ANSWER_ENTRY_ORDER = ();
208 :     $main::ANSWER_PREFIX = 'AnSwEr';
209 :     %main::PG_FLAGS=(); #global flags
210 :     $main::showPartialCorrectAnswers = 0 unless defined($main::showPartialCorrectAnswers );
211 :     $main::showHint = 1 unless defined($main::showHint);
212 :     $main::solutionExists =0;
213 :     $main::hintExists =0;
214 : jj 3520 $main::pgComment = '';
215 : gage 1253 %main::gifs_created = ();
216 : malsyned 1280
217 : gage 1253 !);
218 : gage 1266 # warn eval(q! "PG.pl: The envir variable $main::{envir} is".join(" ",%main::envir)!);
219 :     my $rh_envir = eval(q!\%main::envir!);
220 :     my %envir = %$rh_envir;
221 : dpvc 2698
222 :     # Save the file name for use in error messages
223 :     my ($callpkg,$callfile) = caller(0);
224 :     $envir{__files__}{$callfile} = $envir{templateDirectory}.$envir{fileName};
225 :    
226 : gage 1304 #no strict;
227 : gage 1266 foreach my $var (keys %envir) {
228 :     eval(q!$main::!.$var.q! = $main::envir{!.$var.q!}! ); #whew!! makes sure $var is interpolated but $main:: is evaluated at run time.
229 :     # warn eval(q! "var $var is defined ". $main::!.$var);
230 :     warn "Problem defining ", q{\$main::}.$var, " while initializing the PG problem: $@" if $@;
231 :     }
232 : gage 1304 #use strict;
233 :     #FIXME these strict pragmas don't seem to be needed and they cause trouble in perl 5.6.0
234 : sh002i 1050
235 : gage 1266
236 :    
237 : gage 1253 eval(q!
238 :     @main::submittedAnswers = @{$main::refSubmittedAnswers} if defined($main::refSubmittedAnswers);
239 :     $main::PG_original_problemSeed = $main::problemSeed;
240 :     $main::PG_random_generator = new PGrandom($main::problemSeed) || die "Can't create random number generator.";
241 :     $main::ans_rule_count = 0; # counts questions
242 : lr003k 1122
243 : gage 1253 # end unpacking of environment variables.
244 :     $main::QUIZ_PREFIX = '' unless defined($main::QUIZ_PREFIX)
245 :    
246 :     !);
247 : gage 1266 # @main::submittedAnswers = @{$main::refSubmittedAnswers} if defined($main::refSubmittedAnswers);
248 :     # $main::PG_original_problemSeed = $main::problemSeed;
249 :     # $main::PG_random_generator = new PGrandom($main::problemSeed) || die "Can't create random number generator.";
250 :     # $main::ans_rule_count = 0; # counts questions
251 : sh002i 1050
252 :     # end unpacking of environment variables.
253 : gage 1266 # $main::QUIZ_PREFIX = '' unless defined($main::QUIZ_PREFIX)
254 : dpvc 2166
255 : dpvc 2984 if ($main::envir{displayMode} eq 'HTML_jsMath') {
256 :     my $prefix = "";
257 :     if (!$main::envir{jsMath}{reportMissingFonts}) {
258 : dpvc 3765 $prefix .= '<SCRIPT>noFontMessage = 1</SCRIPT>'."\n";
259 : dpvc 2984 } elsif ($main::envir{jsMath}{missingFontMessage}) {
260 : dpvc 3765 $prefix .= '<SCRIPT>missingFontMessage = "'.$main::envir{jsMath}{missingFontMessage}.'"</SCRIPT>'."\n";
261 : dpvc 2984 }
262 : dpvc 3765 $prefix .= '<SCRIPT>processDoubleClicks = '.($main::envir{jsMath}{processDoubleClicks}?'1':'0')."</SCRIPT>\n";
263 : dpvc 2984 $STRINGforOUTPUT =
264 :     $prefix .
265 :     '<SCRIPT SRC="'.$main::envir{jsMathURL}.'"></SCRIPT>' . "\n" .
266 :     '<NOSCRIPT><CENTER><FONT COLOR="#CC0000">' .
267 :     '<B>Warning: the mathematics on this page requires JavaScript.<BR>' .
268 :     'If your browser supports it, be sure it is enabled.</B>'.
269 :     '</FONT></CENTER><p></NOSCRIPT>' .
270 :     $STRINGforOUTPUT;
271 : dpvc 3539 $STRINGforOUTPUT .=
272 :     '<SCRIPT>jsMath.Setup.Script("plugins/noImageFonts.js")</SCRIPT>'
273 :     if ($main::envir{jsMath}{noImageFonts});
274 : dpvc 2984 }
275 : gage 1253
276 : dpvc 2199 $STRINGforOUTPUT = '<SCRIPT SRC="'.$main::envir{asciimathURL}.'"></SCRIPT>' . "\n" .
277 :     '<SCRIPT>mathcolor = "black"</SCRIPT>' . $STRINGforOUTPUT
278 :     if ($main::envir{displayMode} eq 'HTML_asciimath');
279 : dpvc 4386
280 :     $STRINGforOUTPUT = '<SCRIPT SRC="'.$main::envir{LaTeXMathMLURL}.'"></SCRIPT>'."\n" . $STRINGforOUTPUT
281 :     if ($main::envir{displayMode} eq 'HTML_LaTeXMathML');
282 :    
283 : sh002i 1050 }
284 :    
285 : sh002i 5179 =item HEADER_TEXT()
286 : sh002i 1050
287 : sh002i 5179 HEADER_TEXT("string1", "string2", "string3");
288 : sh002i 1050
289 : sh002i 5179 HEADER_TEXT() concatenates its arguments and appends them to the stored header
290 :     text string. It can be used more than once in a file.
291 : sh002i 1050
292 : sh002i 5179 The macro is used for material which is destined to be placed in the HEAD of
293 :     the page when in HTML mode, such as JavaScript code.
294 : sh002i 1050
295 : sh002i 5179 Spaces are placed between the arguments during concatenation, but no spaces are
296 :     introduced between the existing content of the header text string and the new
297 :     content being appended.
298 : sh002i 1050
299 :     =cut
300 :    
301 : sh002i 5658 # ^function HEADER_TEXT
302 :     # ^uses $STRINGforHEADER_TEXT
303 : sh002i 1050 sub HEADER_TEXT {
304 :     my @in = @_;
305 :     $STRINGforHEADER_TEXT .= join(" ",@in);
306 :     }
307 :    
308 : sh002i 5179 =item TEXT()
309 : sh002i 1050
310 : sh002i 5179 TEXT("string1", "string2", "string3");
311 : sh002i 1050
312 : sh002i 5179 TEXT() concatenates its arguments and appends them to the stored problem text
313 :     string. It is used to define the text which will appear in the body of the
314 :     problem. It can be used more than once in a file.
315 : sh002i 1050
316 : sh002i 5179 This macro has no effect if rendering has been stopped with the STOP_RENDERING()
317 :     macro.
318 : sh002i 1050
319 : sh002i 5179 This macro defines text which will appear in the problem. All text must be
320 :     passed to this macro, passed to another macro that calls this macro, or included
321 :     in a BEGIN_TEXT/END_TEXT block, which uses this macro internally. No other
322 :     statements in a PG file will directly appear in the output. Think of this as the
323 :     "print" function for the PG language.
324 :    
325 :     Spaces are placed between the arguments during concatenation, but no spaces are
326 :     introduced between the existing content of the header text string and the new
327 :     content being appended.
328 :    
329 : sh002i 1050 =cut
330 :    
331 : sh002i 5658 # ^function TEXT
332 :     # ^uses $PG_STOP_FLAG
333 :     # ^uses $STRINGforOUTPUT
334 : sh002i 1050 sub TEXT {
335 : gage 3385 return "" if $PG_STOP_FLAG;
336 : sh002i 1050 my @in = @_;
337 :     $STRINGforOUTPUT .= join(" ",@in);
338 : gage 3385 }
339 : gage 3387
340 : sh002i 5179 =item ANS()
341 : gage 3387
342 : sh002i 5179 TEXT(ans_rule(), ans_rule(), ans_rule());
343 :     ANS($answer_evaluator1, $answer_evaluator2, $answer_evaluator3);
344 : gage 3387
345 : sh002i 5179 Adds the answer evaluators listed to the list of unlabeled answer evaluators.
346 :     They will be paired with unlabeled answer rules (a.k.a. answer blanks) in the
347 :     order entered. This is the standard method for entering answers.
348 : gage 3387
349 : sh002i 5179 In the above example, answer_evaluator1 will be associated with the first
350 :     answer rule, answer_evaluator2 with the second, and answer_evaluator3 with the
351 :     third. In practice, the arguments to ANS() will usually be calls to an answer
352 :     evaluator generator such as the cmp() method of MathObjects or the num_cmp()
353 :     macro in L<PGanswermacros.pl>.
354 :    
355 : gage 3387 =cut
356 :    
357 : sh002i 5658 # ^function ANS
358 :     # ^uses $PG_STOP_FLAG
359 :     # ^uses @PG_ANSWERS
360 : sh002i 5179 sub ANS{
361 :     return "" if $PG_STOP_FLAG;
362 :     my @in = @_;
363 :     while (@in ) {
364 :     warn("<BR><B>Error in ANS:$in[0]</B> -- inputs must be references to
365 :     subroutines<BR>")
366 :     unless ref($in[0]);
367 :     push(@PG_ANSWERS, shift @in );
368 :     }
369 : gage 3385 }
370 : gage 3387
371 : sh002i 5179 =item LABELED_ANS()
372 : gage 3387
373 : sh002i 5179 TEXT(labeled_ans_rule("name1"), labeled_ans_rule("name2"));
374 :     LABELED_ANS(name1 => answer_evaluator1, name2 => answer_evaluator2);
375 : gage 3387
376 : sh002i 5179 Adds the answer evaluators listed to the list of labeled answer evaluators.
377 :     They will be paired with labeled answer rules (a.k.a. answer blanks) in the
378 :     order entered. This allows pairing of answer evaluators and answer rules that
379 :     may not have been entered in the same order.
380 : gage 3387
381 :     =cut
382 :    
383 : sh002i 5658 # ^function LABELED_ANS
384 :     # ^uses &NAMED_ANS
385 : sh002i 5179 sub LABELED_ANS {
386 :     &NAMED_ANS;
387 : gage 3385 }
388 : sh002i 1050
389 : sh002i 5179 =item NAMED_ANS()
390 : sh002i 1050
391 : sh002i 5179 Old name for LABELED_ANS(). DEPRECATED.
392 : sh002i 1050
393 :     =cut
394 :    
395 : sh002i 5658 # ^function NAMED_ANS
396 :     # ^uses $PG_STOP_FLAG
397 : sh002i 5179 sub NAMED_ANS{
398 : gage 3385 return "" if $PG_STOP_FLAG;
399 : sh002i 1050 my @in = @_;
400 :     while (@in ) {
401 :     my $label = shift @in;
402 : malsyned 1280 $label = eval(q!$main::QUIZ_PREFIX.$label!);
403 : sh002i 1050 my $ans_eval = shift @in;
404 :     TEXT("<BR><B>Error in NAMED_ANS:$in[0]</B>
405 :     -- inputs must be references to subroutines<BR>")
406 :     unless ref($ans_eval);
407 :     $PG_ANSWERS_HASH{$label}= $ans_eval;
408 :     }
409 :     }
410 :    
411 : sh002i 5179 =item STOP_RENDERING()
412 : sh002i 1050
413 : sh002i 5179 STOP_RENDERING() unless all_answers_are_correct();
414 : lr003k 1122
415 : sh002i 5179 Temporarily suspends accumulation of problem text and storing of answer blanks
416 :     and answer evaluators until RESUME_RENDERING() is called.
417 : lr003k 1122
418 : sh002i 5179 =cut
419 :    
420 : sh002i 5658 # ^function STOP_RENDERING
421 :     # ^uses $PG_STOP_FLAG
422 : sh002i 5179 sub STOP_RENDERING {
423 :     $PG_STOP_FLAG=1;
424 :     "";
425 : lr003k 1122 }
426 :    
427 : sh002i 5179 =item RESUME_RENDERING()
428 : gage 3385
429 : sh002i 5179 RESUME_RENDERING();
430 :    
431 :     Resumes accumulating problem text and storing answer blanks and answer
432 :     evaluators. Reverses the effect of STOP_RENDERING().
433 :    
434 :     =cut
435 :    
436 : sh002i 5658 # ^function RESUME_RENDERING
437 :     # ^uses $PG_STOP_FLAG
438 : sh002i 5179 sub RESUME_RENDERING {
439 :     $PG_STOP_FLAG=0;
440 :     "";
441 : gage 3385 }
442 : sh002i 1050
443 : sh002i 5179 =item ENDDOCUMENT()
444 : sh002i 1050
445 : sh002i 5179 ENDDOCUMENT();
446 : sh002i 1050
447 : sh002i 5179 When PG problems are evaluated, the result of evaluating the entire problem is
448 :     interpreted as the return value of ENDDOCUMENT(). Therefore, ENDDOCUMENT() must
449 : gage 5442 be the last executable statement of every problem. It can only appear once. It
450 :     returns a list consisting of:
451 : sh002i 1050
452 : sh002i 5179 =over
453 : sh002i 1050
454 : sh002i 5179 =item *
455 : sh002i 1050
456 : sh002i 5179 A reference to a string containing the rendered text of the problem.
457 : sh002i 1050
458 : sh002i 5179 =item *
459 :    
460 :     A reference to a string containing text to be placed in the HEAD block
461 :     when in and HTML-based mode (e.g. for JavaScript).
462 :    
463 :     =item *
464 :    
465 :     A reference to the hash mapping answer labels to answer evaluators.
466 :    
467 :     =item *
468 :    
469 :     A reference to a hash containing various flags:
470 :    
471 :     =over
472 :    
473 :     =item *
474 :    
475 :     C<showPartialCorrectAnswers>: determines whether students are told which of their answers in a problem are wrong.
476 :    
477 :     =item *
478 :    
479 :     C<recordSubmittedAnswers>: determines whether students submitted answers are saved.
480 :    
481 :     =item *
482 :    
483 :     C<refreshCachedImages>: determines whether the cached image of the problem in typeset mode is always refreshed
484 :     (i.e. setting this to 1 means cached images are not used).
485 :    
486 :     =item *
487 :    
488 :     C<solutionExits>: indicates the existence of a solution.
489 :    
490 :     =item *
491 :    
492 :     C<hintExits>: indicates the existence of a hint.
493 :    
494 :     =item *
495 :    
496 :     C<comment>: contents of COMMENT commands if any.
497 :    
498 :     =item *
499 :    
500 :     C<showHintLimit>: determines the number of attempts after which hint(s) will be shown
501 :    
502 :     =item *
503 :    
504 :     C<PROBLEM_GRADER_TO_USE>: a reference to the chosen problem grader.
505 :     ENDDOCUMENT chooses the problem grader as follows:
506 :    
507 :     =over
508 :    
509 :     =item *
510 :    
511 :     If a problem grader has been chosen in the problem by calling
512 :     C<install_problem_grader(\&grader)>, it is used.
513 :    
514 :     =item *
515 :    
516 :     Otherwise, if the C<PROBLEM_GRADER_TO_USE> PG environment variable
517 :     contains a reference to a subroutine, it is used.
518 :    
519 :     =item *
520 :    
521 :     Otherwise, if the C<PROBLEM_GRADER_TO_USE> PG environment variable
522 :     contains the string C<std_problem_grader> or the string C<avg_problem_grader>,
523 :     C<&std_problem_grader> or C<&avg_problem_grader> are used. These graders are defined
524 :     in L<PGanswermacros.pl>.
525 :    
526 :     =item *
527 :    
528 :     Otherwise, the PROBLEM_GRADER_TO_USE flag will contain an empty value
529 :     and the PG translator should select C<&std_problem_grader>.
530 :    
531 :     =back
532 :    
533 :     =back
534 :    
535 :     =back
536 :    
537 : sh002i 1050 =cut
538 :    
539 : sh002i 5658 # ^function ENDDOCUMENT
540 :     # ^uses @PG_UNLABELED_ANSWERS
541 :     # ^uses %PG_ANSWERS_HASH
542 :     # ^uses @PG_ANSWERS
543 : sh002i 1050 sub ENDDOCUMENT {
544 :    
545 :     my $index=0;
546 :     foreach my $label (@PG_UNLABELED_ANSWERS) {
547 :     if ( defined($PG_ANSWERS[$index]) ) {
548 :     $PG_ANSWERS_HASH{"$label"}= $PG_ANSWERS[$index];
549 : malsyned 1280 #warn "recording answer label = $label";
550 : sh002i 1050 } else {
551 :     warn "No answer provided by instructor for answer $label";
552 :     }
553 :     $index++;
554 :     }
555 :    
556 :     $STRINGforOUTPUT .="\n";
557 : gage 1266 eval q{ #make sure that "main" points to the current safe compartment by evaluating these lines.
558 : sh002i 1050 $main::PG_FLAGS{'showPartialCorrectAnswers'} = $main::showPartialCorrectAnswers;
559 :     $main::PG_FLAGS{'recordSubmittedAnswers'} = $main::recordSubmittedAnswers;
560 :     $main::PG_FLAGS{'refreshCachedImages'} = $main::refreshCachedImages;
561 : jj 3520 $main::PG_FLAGS{'comment'} = $main::pgComment;
562 : sh002i 1050 $main::PG_FLAGS{'hintExists'} = $main::hintExists;
563 :     $main::PG_FLAGS{'showHintLimit'} = $main::showHint;
564 :     $main::PG_FLAGS{'solutionExists'} = $main::solutionExists;
565 :     $main::PG_FLAGS{ANSWER_ENTRY_ORDER} = \@main::PG_ANSWER_ENTRY_ORDER;
566 : lr003k 1365 $main::PG_FLAGS{KEPT_EXTRA_ANSWERS} = \@main::KEPT_EXTRA_ANSWERS;##need to keep array labels that don't call "RECORD_ANS_NAME"
567 : sh002i 1050 $main::PG_FLAGS{ANSWER_PREFIX} = $main::ANSWER_PREFIX;
568 :     # install problem grader
569 :     if (defined($main::PG_FLAGS{PROBLEM_GRADER_TO_USE}) ) {
570 :     # problem grader defined within problem -- no further action needed
571 :     } elsif ( defined( $main::envir{PROBLEM_GRADER_TO_USE} ) ) {
572 :     if (ref($main::envir{PROBLEM_GRADER_TO_USE}) eq 'CODE' ) { # user defined grader
573 :     $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = $main::envir{PROBLEM_GRADER_TO_USE};
574 :     } elsif ($main::envir{PROBLEM_GRADER_TO_USE} eq 'std_problem_grader' ) {
575 :     if (defined(&std_problem_grader) ){
576 :     $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&std_problem_grader; # defined in PGanswermacros.pl
577 :     } # std_problem_grader is the default in any case so don't give a warning.
578 :     } elsif ($main::envir{PROBLEM_GRADER_TO_USE} eq 'avg_problem_grader' ) {
579 :     if (defined(&avg_problem_grader) ){
580 :     $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&avg_problem_grader; # defined in PGanswermacros.pl
581 :     }
582 :     #else { # avg_problem_grader will be installed by PGtranslator so there is no need for a warning.
583 :     # warn "The problem grader 'avg_problem_grader' has not been defined. Has PGanswermacros.pl been loaded?";
584 :     #}
585 :     } else {
586 :     warn "Error: $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} is not a known program grader.";
587 :     }
588 :     } elsif (defined(&std_problem_grader)) {
589 :     $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&std_problem_grader; # defined in PGanswermacros.pl
590 :     } else {
591 :     # PGtranslator will install its default problem grader
592 :     }
593 : gage 1266
594 : sh002i 1050 warn "ERROR: The problem grader is not a subroutine" unless ref( $main::PG_FLAGS{PROBLEM_GRADER_TO_USE}) eq 'CODE'
595 :     or $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = 'std_problem_grader'
596 :     or $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = 'avg_problem_grader';
597 :     # return results
598 : gage 1266 };
599 : dpvc 2166
600 : dpvc 3566 $STRINGforOUTPUT .= '<SCRIPT> jsMath.wwProcess() </SCRIPT>'
601 : dpvc 2166 if ($main::envir{displayMode} eq 'HTML_jsMath');
602 : dpvc 2199
603 : dpvc 2201 if ($main::envir{displayMode} eq 'HTML_asciimath') {
604 :     $STRINGforOUTPUT .= '<SCRIPT> translate() </SCRIPT>';
605 :     $STRINGforHEADER_TEXT .=
606 :     '<object id="mathplayer" classid="clsid:32F66A20-7614-11D4-BD11-00104BD3F987">' . "\n" .
607 :     '</object><?import namespace="mml" implementation="#mathplayer"?>'
608 :     unless ($STRINGforHEADER_TEXT =~ m/mathplayer/);
609 :     }
610 : jj 3553 $STRINGforOUTPUT .= MODES(%{PG_restricted_eval('$main::problemPostamble')});
611 : malsyned 1280
612 : gage 1266 (\$STRINGforOUTPUT, \$STRINGforHEADER_TEXT,\%PG_ANSWERS_HASH,eval(q!\%main::PG_FLAGS!));
613 : sh002i 1050 }
614 :    
615 :    
616 : sh002i 5179 ################################################################################
617 : sh002i 1050
618 : sh002i 5179 =head1 PRIVATE MACROS
619 : sh002i 1050
620 : sh002i 5179 These macros should only be used by other macro files. In practice, they are
621 :     used exclusively by L<PGbasicmacros.pl>.
622 :    
623 :     =over
624 :    
625 :     =item inc_ans_rule_count()
626 :    
627 :     NEW_ANS_NAME(inc_ans_rule_count());
628 :    
629 :     Increments the internal count of the number of answer blanks that have been
630 :     defined ($ans_rule_count) and returns the new count. This should only be used
631 :     when one is about to define a new answer blank, for example with NEW_ANS_NAME().
632 :    
633 : sh002i 1050 =cut
634 :    
635 : sh002i 5658 # ^function inc_ans_rule_count
636 :     # ^uses $ans_rule_count
637 : sh002i 5179 sub inc_ans_rule_count {
638 :     eval(q!++$main::ans_rule_count!); # evalute at runtime to get correct main::
639 :     }
640 : sh002i 1050
641 : sh002i 5179 =item RECORD_ANS_NAME()
642 :    
643 :     RECORD_ANS_NAME("label");
644 :    
645 :     Records the label for an answer blank. Used internally by L<PGbasicmacros.pl>
646 :     to record the order of explicitly-labelled answer blanks.
647 :    
648 :     =cut
649 :    
650 : sh002i 5658 # ^function RECORD_ANS_NAME
651 :     # ^uses $PG_STOP_FLAG
652 :     # ^uses @PG_ANSWER_ENTRY_ORDER
653 : sh002i 5179 sub RECORD_ANS_NAME {
654 :     return "" if $PG_STOP_FLAG;
655 :     my $label = shift;
656 :     eval(q!push(@main::PG_ANSWER_ENTRY_ORDER, $label)!);
657 :     $label;
658 :     }
659 :    
660 :     =item NEW_ANS_NAME()
661 :    
662 :     NEW_ANS_NAME($num);
663 :    
664 :     Generates an answer label from the supplied answer number. The label is
665 :     added to the list of implicity-labeled answers. Used internally by
666 :     L<PGbasicmacros.pl> to generate labels for unlabeled answer blanks.
667 :    
668 :     =cut
669 :    
670 : sh002i 5658 # ^function NEW_ANS_NAME
671 :     # ^uses $PG_STOP_FLAG
672 :     # ^uses $QUIZ_PREFIX
673 :     # ^uses $ANSWER_PREFIX
674 :     # ^uses @PG_UNLABELED_ANSWERS
675 : sh002i 5179 sub NEW_ANS_NAME {
676 :     return "" if $PG_STOP_FLAG;
677 :     my $number=shift;
678 :     my $prefix = eval(q!$main::QUIZ_PREFIX.$main::ANSWER_PREFIX!);
679 :     my $label = $prefix.$number;
680 :     push(@PG_UNLABELED_ANSWERS,$label);
681 :     $label;
682 :     }
683 :    
684 :     =item ANS_NUM_TO_NAME()
685 :    
686 :     ANS_NUM_TO_NAME($num);
687 :    
688 :     Generates an answer label from the supplied answer number, but does not add it
689 :     to the list of inplicitly-labeled answers. Used internally by
690 :     L<PGbasicmacros.pl> in generating answers blanks that use radio buttons or
691 :     check boxes. (This type of answer blank uses multiple HTML INPUT elements with
692 :     the same label, but the label should only be added to the list of implicitly-
693 :     labeled answers once.)
694 :    
695 :     =cut
696 :    
697 : sh002i 5658 # ^function ANS_NUM_TO_NAME
698 :     # ^uses $QUIZ_PREFIX
699 :     # ^uses $ANSWER_PREFIX
700 : sh002i 5179 sub ANS_NUM_TO_NAME {
701 :     my $number=shift;
702 :     my $label = eval(q!$main::QUIZ_PREFIX.$main::ANSWER_PREFIX!).$number;
703 :     $label;
704 :     }
705 :    
706 :     my $vecnum;
707 :    
708 :     =item RECORD_FROM_LABEL()
709 :    
710 :     RECORD_FORM_LABEL("label");
711 :    
712 :     Stores the label of a form field in the "extra" answers list. This is used to
713 :     keep track of answer blanks that are not associated with an answer evaluator.
714 :    
715 :     =cut
716 :    
717 : sh002i 5658 # ^function RECORD_FORM_LABEL
718 :     # ^uses $PG_STOP_FLAG
719 :     # ^uses @KEPT_EXTRA_ANSWERS
720 : sh002i 5179 sub RECORD_FORM_LABEL { # this stores form data (such as sticky answers), but does nothing more
721 :     # it's a bit of hack since we are storing these in the KEPT_EXTRA_ANSWERS queue even if they aren't answers per se.
722 :     return "" if $PG_STOP_FLAG;
723 :     my $label = shift; # the label of the input box or textarea
724 :     eval(q!push(@main::KEPT_EXTRA_ANSWERS, $label)!); #put the labels into the hash to be caught later for recording purposes
725 :     $label;
726 :     }
727 :    
728 :     =item NEW_ANS_ARRAY_NAME()
729 :    
730 :     NEW_ANS_ARRAY_NAME($num, $row, $col);
731 :    
732 :     Generates a new answer label for an array (vector) element and adds it to the
733 :     list of implicitly-labeled answers.
734 :    
735 :     =cut
736 :    
737 : sh002i 5658 # ^function NEW_ANS_ARRAY_NAME
738 :     # ^uses $PG_STOP_FLAG
739 :     # ^uses $QUIZ_PREFIX
740 :     # ^uses @PG_UNLABELED_ANSWERS
741 : sh002i 5179 sub NEW_ANS_ARRAY_NAME { # this keeps track of the answers which are entered implicitly,
742 :     # rather than with a specific label
743 :     return "" if $PG_STOP_FLAG;
744 :     my $number=shift;
745 :     $vecnum = 0;
746 :     my $row = shift;
747 :     my $col = shift;
748 :     # my $label = "ArRaY"."$number"."["."$vecnum".","."$row".","."$col"."]";
749 :     my $label = eval(q!$main::QUIZ_PREFIX."ArRaY"."$number"."__"."$vecnum".":"."$row".":"."$col"."__"!);
750 :     push(@PG_UNLABELED_ANSWERS,$label);
751 :     $label;
752 :     }
753 :    
754 :     =item NEW_ANS_ARRAY_NAME_EXTENSION()
755 :    
756 :     NEW_ANS_ARRAY_NAME_EXTENSION($num, $row, $col);
757 :    
758 :     Generate an additional answer label for an existing array (vector) element and
759 :     add it to the list of "extra" answers.
760 :    
761 :     =cut
762 :    
763 : sh002i 5658 # ^function NEW_ANS_ARRAY_NAME_EXTENSION
764 :     # ^uses $PG_STOP_FLAG
765 : sh002i 5179 sub NEW_ANS_ARRAY_NAME_EXTENSION { # this keeps track of the answers which are entered implicitly,
766 :     # rather than with a specific label
767 :     return "" if $PG_STOP_FLAG;
768 :     my $number=shift;
769 :     my $row = shift;
770 :     my $col = shift;
771 :     if( $row == 0 && $col == 0 ){
772 :     $vecnum += 1;
773 :     }
774 :     #FIXME change made to conform to HTML 4.01 standards. "Name" attributes can only contain
775 :     # alphanumeric characters, _ : and .
776 :     # Also need to make corresponding changes in PGmorematrixmacros. grep for ArRaY.
777 :     #my $label = "ArRaY"."$number"."["."$vecnum".","."$row".","."$col"."]";
778 :     my $label = eval(q!$main::QUIZ_PREFIX."ArRaY"."$number"."__"."$vecnum".":"."$row".":"."$col"."__"!);
779 :     eval(q!push(@main::KEPT_EXTRA_ANSWERS, $label)!);#put the labels into the hash to be caught later for recording purposes
780 :     $label;
781 :     }
782 :    
783 :     =item get_PG_ANSWERS_HASH()
784 :    
785 :     get_PG_ANSWERS_HASH();
786 :     get_PG_ANSWERS_HASH($key);
787 :    
788 :    
789 :    
790 :     =cut
791 :    
792 : sh002i 5658 # ^function get_PG_ANSWERS_HASH
793 :     # ^uses %PG_ANSWERS_HASH
794 :     # ^uses @PG_UNLABELED_ANSWERS
795 :     # ^uses @PG_ANSWERS
796 : sh002i 5179 sub get_PG_ANSWERS_HASH {
797 :     # update the PG_ANSWWERS_HASH, then report the result.
798 :     # This is used in writing sequential problems
799 :     # if there is an input, use that as a key into the answer hash
800 :     my $key = shift;
801 :     my (%pg_answers_hash, @pg_unlabeled_answers);
802 :     %pg_answers_hash= %PG_ANSWERS_HASH;
803 :     #warn "order ", eval(q!@main::PG_ANSWER_ENTRY_ORDER!);
804 :     #warn "pg answers", %PG_ANSWERS_HASH;
805 :     #warn "unlabeled", @PG_UNLABELED_ANSWERS;
806 :     my $index=0;
807 :     foreach my $label (@PG_UNLABELED_ANSWERS) {
808 :     if ( defined($PG_ANSWERS[$index]) ) {
809 :     $pg_answers_hash{"$label"}= $PG_ANSWERS[$index];
810 :     #warn "recording answer label = $label";
811 :     } else {
812 :     warn "No answer provided by instructor for answer $label";
813 :     }
814 :     $index++;
815 :     }
816 :     if ($key) {
817 :     return $pg_answers_hash{$key};
818 :     } else {
819 :     return %pg_answers_hash;
820 :     }
821 :     }
822 :    
823 : jj 5269 =item includePGproblem($filePath)
824 :    
825 :     includePGproblem($filePath);
826 :    
827 :     Essentially runs the pg problem specified by $filePath, which is
828 :     a path relative to the top of the templates directory. The output
829 :     of that problem appears in the given problem.
830 :    
831 :     =cut
832 :    
833 : sh002i 5658 # ^function includePGproblem
834 :     # ^uses %envir
835 :     # ^uses &read_whole_problem_file
836 :     # ^uses &includePGtext
837 : jj 5269 sub includePGproblem {
838 :     my $filePath = shift;
839 :     my %save_envir = %main::envir;
840 :     my $fullfilePath = $main::envir{templateDirectory}.$filePath;
841 :     my $r_string = read_whole_problem_file($fullfilePath);
842 :     if (ref($r_string) eq 'SCALAR') {
843 :     $r_string = $$r_string;
844 :     }
845 :    
846 :     # The problem calling this should provide DOCUMENT and ENDDOCUMENT,
847 :     # so we remove them from the included file.
848 :     $r_string=~ s/^\s*(END)?DOCUMENT(\(\s*\));?//gm;
849 :    
850 :     # Reset the problem path so that static images can be found via
851 :     # their relative paths.
852 :     eval('$main::envir{probFileName} = $filePath');
853 :     eval('$main::envir{fileName} = $filePath');
854 :     includePGtext($r_string);
855 :     # Reset the environment to what it is before.
856 :     %main::envir = %save_envir;
857 :     }
858 :    
859 :    
860 : sh002i 5179 =back
861 :    
862 :     =head1 SEE ALSO
863 :    
864 :     L<PGbasicmacros.pl>, L<PGanswermacros.pl>.
865 :    
866 :     =cut
867 :    
868 : sh002i 1050 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9