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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9