[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 5658 - (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 : sh002i 5658 # $CVSHeader: pg/macros/PG.pl,v 1.35 2008/03/26 02:43:07 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 5658 # ^variable $STRINGforOUTPUT
111 :     my $STRINGforOUTPUT;
112 :     # ^variable $STRINGforHEADER_TEXT
113 :     my $STRINGforHEADER_TEXT;
114 :     # ^variable @PG_ANSWERS
115 :     my @PG_ANSWERS;
116 :     # ^variable @PG_UNLABELED_ANSWERS
117 :     my @PG_UNLABELED_ANSWERS;
118 :     # ^variable %PG_ANSWERS_HASH
119 :     my %PG_ANSWERS_HASH;
120 :    
121 :     # ^variable $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 :     $#="%G"; #FIXME -- check that this works
206 :    
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 : sh002i 5179 =back
616 : sh002i 1050
617 : sh002i 5179 =cut
618 : sh002i 1050
619 : sh002i 5179 ################################################################################
620 : sh002i 1050
621 : sh002i 5179 =head1 PRIVATE MACROS
622 : sh002i 1050
623 : sh002i 5179 These macros should only be used by other macro files. In practice, they are
624 :     used exclusively by L<PGbasicmacros.pl>.
625 :    
626 :     =over
627 :    
628 :     =item inc_ans_rule_count()
629 :    
630 :     NEW_ANS_NAME(inc_ans_rule_count());
631 :    
632 :     Increments the internal count of the number of answer blanks that have been
633 :     defined ($ans_rule_count) and returns the new count. This should only be used
634 :     when one is about to define a new answer blank, for example with NEW_ANS_NAME().
635 :    
636 : sh002i 1050 =cut
637 :    
638 : sh002i 5658 # ^function inc_ans_rule_count
639 :     # ^uses $ans_rule_count
640 : sh002i 5179 sub inc_ans_rule_count {
641 :     eval(q!++$main::ans_rule_count!); # evalute at runtime to get correct main::
642 :     }
643 : sh002i 1050
644 : sh002i 5179 =item RECORD_ANS_NAME()
645 :    
646 :     RECORD_ANS_NAME("label");
647 :    
648 :     Records the label for an answer blank. Used internally by L<PGbasicmacros.pl>
649 :     to record the order of explicitly-labelled answer blanks.
650 :    
651 :     =cut
652 :    
653 : sh002i 5658 # ^function RECORD_ANS_NAME
654 :     # ^uses $PG_STOP_FLAG
655 :     # ^uses @PG_ANSWER_ENTRY_ORDER
656 : sh002i 5179 sub RECORD_ANS_NAME {
657 :     return "" if $PG_STOP_FLAG;
658 :     my $label = shift;
659 :     eval(q!push(@main::PG_ANSWER_ENTRY_ORDER, $label)!);
660 :     $label;
661 :     }
662 :    
663 :     =item NEW_ANS_NAME()
664 :    
665 :     NEW_ANS_NAME($num);
666 :    
667 :     Generates an answer label from the supplied answer number. The label is
668 :     added to the list of implicity-labeled answers. Used internally by
669 :     L<PGbasicmacros.pl> to generate labels for unlabeled answer blanks.
670 :    
671 :     =cut
672 :    
673 : sh002i 5658 # ^function NEW_ANS_NAME
674 :     # ^uses $PG_STOP_FLAG
675 :     # ^uses $QUIZ_PREFIX
676 :     # ^uses $ANSWER_PREFIX
677 :     # ^uses @PG_UNLABELED_ANSWERS
678 : sh002i 5179 sub NEW_ANS_NAME {
679 :     return "" if $PG_STOP_FLAG;
680 :     my $number=shift;
681 :     my $prefix = eval(q!$main::QUIZ_PREFIX.$main::ANSWER_PREFIX!);
682 :     my $label = $prefix.$number;
683 :     push(@PG_UNLABELED_ANSWERS,$label);
684 :     $label;
685 :     }
686 :    
687 :     =item ANS_NUM_TO_NAME()
688 :    
689 :     ANS_NUM_TO_NAME($num);
690 :    
691 :     Generates an answer label from the supplied answer number, but does not add it
692 :     to the list of inplicitly-labeled answers. Used internally by
693 :     L<PGbasicmacros.pl> in generating answers blanks that use radio buttons or
694 :     check boxes. (This type of answer blank uses multiple HTML INPUT elements with
695 :     the same label, but the label should only be added to the list of implicitly-
696 :     labeled answers once.)
697 :    
698 :     =cut
699 :    
700 : sh002i 5658 # ^function ANS_NUM_TO_NAME
701 :     # ^uses $QUIZ_PREFIX
702 :     # ^uses $ANSWER_PREFIX
703 : sh002i 5179 sub ANS_NUM_TO_NAME {
704 :     my $number=shift;
705 :     my $label = eval(q!$main::QUIZ_PREFIX.$main::ANSWER_PREFIX!).$number;
706 :     $label;
707 :     }
708 :    
709 :     my $vecnum;
710 :    
711 :     =item RECORD_FROM_LABEL()
712 :    
713 :     RECORD_FORM_LABEL("label");
714 :    
715 :     Stores the label of a form field in the "extra" answers list. This is used to
716 :     keep track of answer blanks that are not associated with an answer evaluator.
717 :    
718 :     =cut
719 :    
720 : sh002i 5658 # ^function RECORD_FORM_LABEL
721 :     # ^uses $PG_STOP_FLAG
722 :     # ^uses @KEPT_EXTRA_ANSWERS
723 : sh002i 5179 sub RECORD_FORM_LABEL { # this stores form data (such as sticky answers), but does nothing more
724 :     # 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.
725 :     return "" if $PG_STOP_FLAG;
726 :     my $label = shift; # the label of the input box or textarea
727 :     eval(q!push(@main::KEPT_EXTRA_ANSWERS, $label)!); #put the labels into the hash to be caught later for recording purposes
728 :     $label;
729 :     }
730 :    
731 :     =item NEW_ANS_ARRAY_NAME()
732 :    
733 :     NEW_ANS_ARRAY_NAME($num, $row, $col);
734 :    
735 :     Generates a new answer label for an array (vector) element and adds it to the
736 :     list of implicitly-labeled answers.
737 :    
738 :     =cut
739 :    
740 : sh002i 5658 # ^function NEW_ANS_ARRAY_NAME
741 :     # ^uses $PG_STOP_FLAG
742 :     # ^uses $QUIZ_PREFIX
743 :     # ^uses @PG_UNLABELED_ANSWERS
744 : sh002i 5179 sub NEW_ANS_ARRAY_NAME { # this keeps track of the answers which are entered implicitly,
745 :     # rather than with a specific label
746 :     return "" if $PG_STOP_FLAG;
747 :     my $number=shift;
748 :     $vecnum = 0;
749 :     my $row = shift;
750 :     my $col = shift;
751 :     # my $label = "ArRaY"."$number"."["."$vecnum".","."$row".","."$col"."]";
752 :     my $label = eval(q!$main::QUIZ_PREFIX."ArRaY"."$number"."__"."$vecnum".":"."$row".":"."$col"."__"!);
753 :     push(@PG_UNLABELED_ANSWERS,$label);
754 :     $label;
755 :     }
756 :    
757 :     =item NEW_ANS_ARRAY_NAME_EXTENSION()
758 :    
759 :     NEW_ANS_ARRAY_NAME_EXTENSION($num, $row, $col);
760 :    
761 :     Generate an additional answer label for an existing array (vector) element and
762 :     add it to the list of "extra" answers.
763 :    
764 :     =cut
765 :    
766 : sh002i 5658 # ^function NEW_ANS_ARRAY_NAME_EXTENSION
767 :     # ^uses $PG_STOP_FLAG
768 : sh002i 5179 sub NEW_ANS_ARRAY_NAME_EXTENSION { # this keeps track of the answers which are entered implicitly,
769 :     # rather than with a specific label
770 :     return "" if $PG_STOP_FLAG;
771 :     my $number=shift;
772 :     my $row = shift;
773 :     my $col = shift;
774 :     if( $row == 0 && $col == 0 ){
775 :     $vecnum += 1;
776 :     }
777 :     #FIXME change made to conform to HTML 4.01 standards. "Name" attributes can only contain
778 :     # alphanumeric characters, _ : and .
779 :     # Also need to make corresponding changes in PGmorematrixmacros. grep for ArRaY.
780 :     #my $label = "ArRaY"."$number"."["."$vecnum".","."$row".","."$col"."]";
781 :     my $label = eval(q!$main::QUIZ_PREFIX."ArRaY"."$number"."__"."$vecnum".":"."$row".":"."$col"."__"!);
782 :     eval(q!push(@main::KEPT_EXTRA_ANSWERS, $label)!);#put the labels into the hash to be caught later for recording purposes
783 :     $label;
784 :     }
785 :    
786 :     =item get_PG_ANSWERS_HASH()
787 :    
788 :     get_PG_ANSWERS_HASH();
789 :     get_PG_ANSWERS_HASH($key);
790 :    
791 :    
792 :    
793 :     =cut
794 :    
795 : sh002i 5658 # ^function get_PG_ANSWERS_HASH
796 :     # ^uses %PG_ANSWERS_HASH
797 :     # ^uses @PG_UNLABELED_ANSWERS
798 :     # ^uses @PG_ANSWERS
799 : sh002i 5179 sub get_PG_ANSWERS_HASH {
800 :     # update the PG_ANSWWERS_HASH, then report the result.
801 :     # This is used in writing sequential problems
802 :     # if there is an input, use that as a key into the answer hash
803 :     my $key = shift;
804 :     my (%pg_answers_hash, @pg_unlabeled_answers);
805 :     %pg_answers_hash= %PG_ANSWERS_HASH;
806 :     #warn "order ", eval(q!@main::PG_ANSWER_ENTRY_ORDER!);
807 :     #warn "pg answers", %PG_ANSWERS_HASH;
808 :     #warn "unlabeled", @PG_UNLABELED_ANSWERS;
809 :     my $index=0;
810 :     foreach my $label (@PG_UNLABELED_ANSWERS) {
811 :     if ( defined($PG_ANSWERS[$index]) ) {
812 :     $pg_answers_hash{"$label"}= $PG_ANSWERS[$index];
813 :     #warn "recording answer label = $label";
814 :     } else {
815 :     warn "No answer provided by instructor for answer $label";
816 :     }
817 :     $index++;
818 :     }
819 :     if ($key) {
820 :     return $pg_answers_hash{$key};
821 :     } else {
822 :     return %pg_answers_hash;
823 :     }
824 :     }
825 :    
826 : jj 5269 =item includePGproblem($filePath)
827 :    
828 :     includePGproblem($filePath);
829 :    
830 :     Essentially runs the pg problem specified by $filePath, which is
831 :     a path relative to the top of the templates directory. The output
832 :     of that problem appears in the given problem.
833 :    
834 :     =cut
835 :    
836 : sh002i 5658 # ^function includePGproblem
837 :     # ^uses %envir
838 :     # ^uses &read_whole_problem_file
839 :     # ^uses &includePGtext
840 : jj 5269 sub includePGproblem {
841 :     my $filePath = shift;
842 :     my %save_envir = %main::envir;
843 :     my $fullfilePath = $main::envir{templateDirectory}.$filePath;
844 :     my $r_string = read_whole_problem_file($fullfilePath);
845 :     if (ref($r_string) eq 'SCALAR') {
846 :     $r_string = $$r_string;
847 :     }
848 :    
849 :     # The problem calling this should provide DOCUMENT and ENDDOCUMENT,
850 :     # so we remove them from the included file.
851 :     $r_string=~ s/^\s*(END)?DOCUMENT(\(\s*\));?//gm;
852 :    
853 :     # Reset the problem path so that static images can be found via
854 :     # their relative paths.
855 :     eval('$main::envir{probFileName} = $filePath');
856 :     eval('$main::envir{fileName} = $filePath');
857 :     includePGtext($r_string);
858 :     # Reset the environment to what it is before.
859 :     %main::envir = %save_envir;
860 :     }
861 :    
862 :    
863 : sh002i 5179 =back
864 :    
865 :     =head1 SEE ALSO
866 :    
867 :     L<PGbasicmacros.pl>, L<PGanswermacros.pl>.
868 :    
869 :     =cut
870 :    
871 : sh002i 1050 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9