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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9