[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 6281 - (view) (download) (as text)

1 : gage 6248
2 :     #use AnswerEvaluator;
3 :    
4 :    
5 :     # provided by the translator
6 :     # initialize PGcore and PGrandom
7 :    
8 :    
9 :     $main::VERSION ="WW2";
10 :    
11 :     sub _PG_init{
12 :     $main::VERSION ="WW2.9+";
13 :     }
14 :     sub not_null {PGcore::not_null(@_)};
15 :    
16 :    
17 :     our $PG;
18 :    
19 :     sub DEBUG_MESSAGE {
20 :     $PG->append_debug_message(@_);
21 :     }
22 :    
23 :    
24 :     sub DOCUMENT {
25 :    
26 :     # get environment
27 :     $rh_envir = \%envir; #KLUDGE FIXME
28 :     # warn "rh_envir is ",ref($rh_envir);
29 :     $PG = new PGcore($rh_envir, # can add key/value options to modify
30 :     );
31 :     $PG->clear_internal_debug_messages;
32 :    
33 :     # initialize main:: variables
34 :    
35 :     $ANSWER_PREFIX = $PG->{ANSWER_PREFIX};
36 :     $QUIZ_PREFIX = $PG->{QUIZ_PREFIX};
37 : gage 6256 $showPartialCorrectAnswers = $PG->{flags}->{showPartialCorrectAnswers};
38 :     $showHint = $PG->{flags}->{showHint};
39 :     $solutionExists = $PG->{flags}->{solutionExists};
40 :     $hintExists = $PG->{flags}->{hintExists};
41 : gage 6248 $pgComment = '';
42 :     %gifs_created = %{ $PG->{gifs_created}};
43 :     %external_refs = %{ $PG->{external_refs}};
44 :    
45 :     @KEPT_EXTRA_ANSWERS =(); #temporary hack
46 :    
47 :     my %envir = %$rh_envir;
48 :     $displayMode = $PG->{displayMode};
49 :     $PG_random_generator = $PG->{PG_random_generator};
50 :     # Save the file name for use in error messages
51 :     # Doesn't appear to be used FIXME
52 :     # my ($callpkg,$callfile) = caller(0);
53 :     # $envir{__files__}{$callfile} = $envir{templateDirectory}.$envir{fileName};
54 :    
55 :     #no strict;
56 :     foreach my $var (keys %envir) {
57 :     PG_restricted_eval(qq!\$main::$var = \$envir{$var}!); #whew!! makes sure $var is interpolated but $main:: is evaluated at run time.
58 :     warn "Problem defining $var while initializing the PG problem: $@" if $@;
59 :     }
60 :     #use strict;
61 :     #FIXME
62 :     # load java script needed for displayModes
63 :     if ($envir{displayMode} eq 'HTML_jsMath') {
64 :     my $prefix = "";
65 :     if (!$envir{jsMath}{reportMissingFonts}) {
66 :     $prefix .= '<SCRIPT>noFontMessage = 1</SCRIPT>'."\n";
67 :     } elsif ($main::envir{jsMath}{missingFontMessage}) {
68 :     $prefix .= '<SCRIPT>missingFontMessage = "'.$main::envir{jsMath}{missingFontMessage}.'"</SCRIPT>'."\n";
69 :     }
70 :     $prefix .= '<SCRIPT>processDoubleClicks = '.($main::envir{jsMath}{processDoubleClicks}?'1':'0')."</SCRIPT>\n";
71 :     TEXT(
72 :     $prefix,
73 :     '<SCRIPT SRC="'.$envir{jsMathURL}. '"></SCRIPT>' . "\n" ,
74 :     '<NOSCRIPT><CENTER><FONT COLOR="#CC0000">' ,
75 :     "$BBOLD", 'Warning: the mathematics on this page requires JavaScript.', ,$BR,
76 :     'If your browser supports it, be sure it is enabled.',
77 :     "$EBOLD",
78 :     '</FONT></CENTER><p>
79 :     </NOSCRIPT>'
80 :     );
81 :     TEXT('<SCRIPT>jsMath.Setup.Script("plugins/noImageFonts.js")</SCRIPT>')
82 :     if ($envir{jsMath}{noImageFonts});
83 :     } elsif ($envir{displayMode} eq 'HTML_asciimath') {
84 :     TEXT('<SCRIPT SRC="'.$main::envir{asciimathURL}.'"></SCRIPT>' . "\n" ,
85 :     '<SCRIPT>mathcolor = "black"</SCRIPT>' );
86 :     } elsif ($envir{displayMode} eq 'HTML_LaTeXMathML') {
87 :     TEXT('<SCRIPT SRC="'.$envir{LaTeXMathMLURL}.'"></SCRIPT>'."\n");
88 :    
89 :     }
90 :    
91 :     }
92 :     $main::displayMode = $PG->{displayMode};
93 :     $main::PG = $PG;
94 :     sub TEXT {
95 :     $PG->TEXT(@_) ;
96 :     }
97 :    
98 :     sub HEADER_TEXT {
99 :     $PG->HEADER_TEXT(@_);
100 :     }
101 :    
102 :     sub LABELED_ANS {
103 : gage 6280 my @in = @_;
104 :     my @out = ();
105 :     #prepend labels with the quiz and section prefixes.
106 :     while (@in ) {
107 :     my $label = shift @in;
108 : gage 6281 $label = join("", $PG->{QUIZ_PREFIX}, $PG->{SECTION_PREFIX}, $label);
109 : gage 6280 $ans_eval = shift @in;
110 :     push @out, $label, $ans_eval;
111 :     }
112 : gage 6281 $PG->LABELED_ANS(@out); # returns pointer to the labeled answer group
113 : gage 6248 }
114 :    
115 :     sub NAMED_ANS {
116 : gage 6280 &LABELED_ANS(@_); # returns pointer to the labeled answer group
117 : gage 6248 }
118 :    
119 :     sub ANS {
120 :     #warn "using PGnew for ANS";
121 :     $PG->ANS(@_); # returns pointer to the labeled answer group
122 :     }
123 :    
124 :     sub RECORD_ANS_NAME {
125 :     $PG->record_ans_name(@_);
126 :     }
127 :    
128 :     sub inc_ans_rule_count {
129 :     #$PG->{unlabeled_answer_blank_count}++;
130 :     #my $num = $PG->{unlabeled_answer_blank_count};
131 :     DEBUG_MESSAGE( " using PG to inc_ans_rule_count = $num ", caller(2));
132 :     warn " using PG to inc_ans_rule_count = $num ", caller(2);
133 :     $PG->{unlabeled_answer_blank_count};
134 :     }
135 :     sub ans_rule_count {
136 :     $PG->{unlabeled_answer_blank_count};
137 :     }
138 :     sub NEW_ANS_NAME {
139 :     return "" if $PG_STOP_FLAG;
140 :     #my $number=shift;
141 :     # we have an internal count so the number not actually used.
142 :     my $name =$PG->record_unlabeled_ans_name();
143 :     $name;
144 :     }
145 :     sub NEW_ARRAY_NAME {
146 :     return "" if $PG_STOP_FLAG;
147 :     my $name =$PG->record_unlabeled_array_name();
148 :     $name;
149 :     }
150 :    
151 :     # new subroutine
152 :     sub NEW_ANS_BLANK {
153 :     return "" if $PG_STOP_FLAG;
154 :     $PG->record_unlabeled_ans_name(@_);
155 :     }
156 :    
157 :     sub ANS_NUM_TO_NAME {
158 :     $PG->new_label(@_); # behaves as in PG.pl
159 :     }
160 :    
161 :     sub store_persistent_data {
162 :     $PG->store_persistent_data(@_); #needs testing
163 :     }
164 :     sub RECORD_FORM_LABEL { # this stores form data (such as sticky answers), but does nothing more
165 :     # it's a bit of hack since we are storing these in the
166 :     # KEPT_EXTRA_ANSWERS queue even if they aren't answers per se.
167 : gage 6264 #FIXME
168 :     # warn "Using RECORD_FORM_LABEL -- deprecated? use $PG->store_persistent_data instead.";
169 : gage 6248 RECORD_EXTRA_ANSWERS(@_);
170 :     }
171 :    
172 :     sub RECORD_EXTRA_ANSWERS {
173 :     return "" if $PG_STOP_FLAG;
174 :     my $label = shift; # the label of the input box or textarea
175 :     eval(q!push(@main::KEPT_EXTRA_ANSWERS, $label)!); #put the labels into the hash to be caught later for recording purposes
176 :     $label;
177 :    
178 :     }
179 :    
180 :    
181 :     sub NEW_ANS_ARRAY_NAME { # this keeps track of the answers within an array which are entered implicitly,
182 :     # rather than with a specific label
183 :     return "" if $PG_STOP_FLAG;
184 :     my $number=shift;
185 :     $main::vecnum = -1;
186 :     my $row = shift;
187 :     my $col = shift;
188 :     # my $array_ans_eval_label = "ArRaY"."$number"."__"."$vecnum".":";
189 :     my $label = $PG->{QUIZ_PREFIX}.$PG->{ARRAY_PREFIX}."$number"."__"."$vecnum".":"."$row".":"."$col"."__";
190 :     # my $response_group = new PGresponsegroup($label,undef);
191 :     # $PG->record_ans_name($array_ans_eval_label, $response_group);
192 :     # What does vecnum do?
193 :     # The name is simply so that it won't conflict when placed on the HTML page
194 :     # my $array_label = shift;
195 :     $PG->record_array_name($label); # returns $array_label, $ans_label
196 :     }
197 :    
198 :     sub NEW_ANS_ARRAY_NAME_EXTENSION {
199 :     NEW_ANS_ARRAY_ELEMENT_NAME(@_);
200 :     }
201 :    
202 :     sub NEW_ANS_ARRAY_ELEMENT_NAME { # creates a new array element answer name and records it
203 :    
204 :     return "" if $PG_STOP_FLAG;
205 :     my $number=shift;
206 :     my $row_num = shift;
207 :     my $col_num = shift;
208 :     if( $row_num == 0 && $col_num == 0 ){
209 :     $main::vecnum += 1;
210 :     }
211 :     # my $ans_label = "ArRaY".sprintf("%04u", $number);
212 :     my $ans_label = $PG->new_array_label($number);
213 :     my $element_ans_label = $PG->new_array_element_label($ans_label,$row_num, $col_num,vec_num=>$vecnum);
214 :     my $response = new PGresponsegroup($ans_label,$element_ans_label, undef);
215 :     $PG->extend_ans_group($ans_label,$response);
216 :     $element_ans_label;
217 :     }
218 :     sub NEW_LABELED_ANS_ARRAY { #not in PG_original
219 :     my $ans_label = shift;
220 :     my @response_list = @_;
221 :     #$PG->extend_ans_group($ans_label,@response_list);
222 :     $PG->{PG_ANSWERS_HASH}->{$ans_label}->insert_responses(@response_list);
223 :     # should this return an array of labeled answer blanks???
224 :     }
225 :     sub EXTEND_ANS_ARRAY { #not in PG_original
226 :     my $ans_label = shift;
227 :     my @response_list = @_;
228 :     #$PG->extend_ans_group($ans_label,@response_list);
229 :     $PG->{PG_ANSWERS_HASH}->{$ans_label}->append_responses(@response_list);
230 :     }
231 :     sub CLEAR_RESPONSES {
232 :     my $ans_label = shift;
233 :     # my $response_label = shift;
234 :     # my $ans_value = shift;
235 :     if (defined ($PG->{PG_ANSWERS_HASH}->{$ans_label}) ) {
236 :     my $responsegroup = $PG->{PG_ANSWERS_HASH}->{$ans_label}->{response};
237 :     if ( ref($responsegroup) ) {
238 :     $responsegroup->clear;
239 :     } else {
240 :     $responsegroup = $PG->{PG_ANSWERS_HASH}->{$ans_label}->{response} = new PGresponsegroup($label);
241 :     }
242 :     }
243 :     '';
244 :     }
245 :     sub INSERT_RESPONSE {
246 :     my $ans_label = shift;
247 :     my $response_label = shift;
248 :     my $ans_value = shift;
249 :     my $selected = shift;
250 :     # warn "\n\nanslabel $ans_label responselabel $response_label value $ans_value";
251 :     if (defined ($PG->{PG_ANSWERS_HASH}->{$ans_label}) ) {
252 :     my $responsegroup = $PG->{PG_ANSWERS_HASH}->{$ans_label}->{response};
253 :     $responsegroup->append_response($response_label, $ans_value, $selected);
254 :     #warn "\n$responsegroup responses are now ", $responsegroup->responses;
255 :     }
256 :     '';
257 :     }
258 :    
259 :     sub EXTEND_RESPONSE { # for radio buttons and checkboxes
260 :     my $ans_label = shift;
261 :     my $response_label = shift;
262 :     my $ans_value = shift;
263 :     my $selected = shift;
264 :     # warn "\n\nanslabel $ans_label responselabel $response_label value $ans_value";
265 :     if (defined ($PG->{PG_ANSWERS_HASH}->{$ans_label}) ) {
266 :     my $responsegroup = $PG->{PG_ANSWERS_HASH}->{$ans_label}->{response};
267 :     $responsegroup->extend_response($response_label, $ans_value,$selected);
268 : gage 6252 #warn "\n$responsegroup responses are now ", pretty_print($response_group);
269 : gage 6248 }
270 :     '';
271 :     }
272 :     sub ENDDOCUMENT {
273 :     # check that answers match
274 :     # gather up PG_FLAGS elements
275 :    
276 : gage 6257 $PG->{flags}->{showPartialCorrectAnswers} = defined($showPartialCorrectAnswers)? $showPartialCorrectAnswers : 1 ;
277 :     $PG->{flags}->{recordSubmittedAnswers} = defined($recordSubmittedAnswers)? $recordSubmittedAnswers : 1 ;
278 :     $PG->{flags}->{refreshCachedImages} = defined($refreshCachedImages)? $refreshCachedImages : 0 ;
279 :     $PG->{flags}->{hintExists} = defined($hintExists)? $hintExists : 0 ;
280 :     $PG->{flags}->{solutionExists} = defined($solutionExists)? $solutionExists : 0 ;
281 :     $PG->{flags}->{comment} = defined($pgComment)? $pgComment :'' ;
282 :     $PG->{flags}->{showHintLimit} = defined($showHint)? $showHint : 0 ;
283 :    
284 : gage 6248
285 :     # install problem grader
286 : gage 6256 if (defined($PG->{flags}->{PROBLEM_GRADER_TO_USE}) ) {
287 : gage 6248 # problem grader defined within problem -- no further action needed
288 :     } elsif ( defined( $rh_envir->{PROBLEM_GRADER_TO_USE} ) ) {
289 :     if (ref($rh_envir->{PROBLEM_GRADER_TO_USE}) eq 'CODE' ) { # user defined grader
290 : gage 6256 $PG->{flags}->{PROBLEM_GRADER_TO_USE} = $rh_envir->{PROBLEM_GRADER_TO_USE};
291 : gage 6248 } elsif ($rh_envir->{PROBLEM_GRADER_TO_USE} eq 'std_problem_grader' ) {
292 :     if (defined(&std_problem_grader) ){
293 : gage 6256 $PG->{flags}->{PROBLEM_GRADER_TO_USE} = \&std_problem_grader; # defined in PGanswermacros.pl
294 : gage 6248 } # std_problem_grader is the default in any case so don't give a warning.
295 :     } elsif ($rh_envir->{PROBLEM_GRADER_TO_USE} eq 'avg_problem_grader' ) {
296 :     if (defined(&avg_problem_grader) ){
297 : gage 6256 $PG->{flags}->{PROBLEM_GRADER_TO_USE} = \&avg_problem_grader; # defined in PGanswermacros.pl
298 : gage 6248 }
299 :     } else {
300 : gage 6256 warn "Error: ". $PG->{flags}->{PROBLEM_GRADER_TO_USE} . "is not a known program grader.";
301 : gage 6248 }
302 :     } elsif (defined(&std_problem_grader)) {
303 : gage 6256 $PG->{flags}->{PROBLEM_GRADER_TO_USE} = \&std_problem_grader; # defined in PGanswermacros.pl
304 : gage 6248 } else {
305 :     # PGtranslator will install its default problem grader
306 :     }
307 :    
308 :     # add javaScripts
309 :     if ($rh_envir->{displayMode} eq 'HTML_jsMath') {
310 :     TEXT('<SCRIPT> jsMath.wwProcess() </SCRIPT>');
311 :     } elsif ($rh_envir->{displayMode} eq 'HTML_asciimath') {
312 :     TEXT('<SCRIPT> translate() </SCRIPT>');
313 :     my $STRING = join("", @{$PG->{HEADER_ARRAY} });
314 :     unless ($STRING =~ m/mathplayer/) {
315 :     HEADER_TEXT('<object id="mathplayer" classid="clsid:32F66A20-7614-11D4-BD11-00104BD3F987">' . "\n" .
316 :     '</object><?import namespace="mml" implementation="#mathplayer"?>'
317 :     );
318 :     }
319 :    
320 :     }
321 :     TEXT( MODES(%{$rh_envir->{problemPostamble}}) );
322 :    
323 :    
324 :    
325 :    
326 :    
327 :     @PG_ANSWERS=();
328 :    
329 :     #warn keys %{ $PG->{PG_ANSWERS_HASH} };
330 :     @PG_ANSWER_ENTRY_ORDER = ();
331 :     my $ans_debug = 0;
332 :     foreach my $key (keys %{ $PG->{PG_ANSWERS_HASH} }) {
333 :     $answergroup = $PG->{PG_ANSWERS_HASH}->{$key};
334 :     #warn "$key is defined =", defined($answergroup), "PG object is $PG";
335 :     #################
336 :     # EXTRA ANSWERS KLUDGE
337 :     #################
338 :     # The first response in each answer group is placed in @PG_ANSER_ENTRY_ORDER and %PG_ANSWERS_HASH
339 :     # The remainder of the response keys are placed in the EXTRA ANSWERS ARRAY
340 :     if (defined($answergroup)) {
341 :     my @response_keys = $answergroup->{response}->response_labels;
342 :     warn pretty_print($answergroup->{response}) if $ans_debug==1;
343 :     my $response_key = shift @response_keys;
344 :     #unshift @response_keys, $response_key unless ($response_key eq $answer_group->{ans_label});
345 :     # don't save the first response key if it is the same as the ans_label
346 :     # maybe we should insure that the first response key is always the same as the answer label?
347 :     # even if no answer blank is printed for it? or a hidden answer blank?
348 :     # this is still a KLUDGE
349 :     # for compatibility the first response key is closer to the old method than the $ans_label
350 :     # this is because a response key might indicate an array but an answer label won't
351 :     push @PG_ANSWERS, $response_key,$answergroup->{ans_eval};
352 :     push @PG_ANSWER_ENTRY_ORDER, $response_key;
353 :     push @KEPT_EXTRA_ANSWERS, @response_keys;
354 :     } else {
355 :     #warn "$key is ", join("|",%{$PG->{PG_ANSWERS_HASH}->{$key}});
356 :     }
357 :     }
358 :     push @KEPT_EXTRA_ANSWERS, keys %{$PG->{PERSISTENCE_HASH}};
359 :     my %PG_ANSWERS_HASH = @PG_ANSWERS;
360 : gage 6256 $PG->{flags}->{KEPT_EXTRA_ANSWERS} = \@KEPT_EXTRA_ANSWERS;
361 :     $PG->{flags}->{ANSWER_ENTRY_ORDER} = \@PG_ANSWER_ENTRY_ORDER;
362 : gage 6248 warn "KEPT_EXTRA_ANSWERS", join(" ", @KEPT_EXTRA_ANSWERS), $BR if $ans_debug==1;
363 :     warn "PG_ANSWER_ENTRY_ORDER",join(" ",@PG_ANSWER_ENTRY_ORDER), $BR if $ans_debug==1;
364 :     warn "DEBUG messages", join( "$BR",@{$PG->get_debug_messages} ) if $ans_debug==1;
365 :     warn "INTERNAL_DEBUG messages", join( "$BR",@{$PG->get_internal_debug_messages} ) if $ans_debug==1;
366 :     $STRINGforOUTPUT = join("", @{$PG->{OUTPUT_ARRAY} });
367 :    
368 :    
369 :     $STRINGforHEADER_TEXT = join("", @{$PG->{HEADER_ARRAY} });
370 :    
371 :     # warn pretty_print($PG->{PG_ANSWERS_HASH});
372 :     #warn "printing another warning";
373 :    
374 : gage 6256 (\$STRINGforOUTPUT, \$STRINGforHEADER_TEXT,\%PG_ANSWERS_HASH, $PG->{flags} , $PG );
375 : gage 6248 }
376 : sh002i 5179 ################################################################################
377 : gage 6248 #
378 :     # macros from dangerousMacros
379 :     #
380 :     ################################################################################
381 :     sub alias {
382 :     #warn "alias called ",@_;
383 :     $PG->{PG_alias}->make_alias(@_) ;
384 :     }
385 :     sub insertGraph {
386 :     $PG->insertGraph(@_);
387 :     }
388 :    
389 :     sub findMacroFile {
390 :     $PG->{PG_alias}->findMacroFile(@_);
391 :     }
392 :     sub check_url {
393 :     $PG->{PG_alias}->check_url(@_);
394 :     }
395 :     sub findAppletCodebase {
396 :     $PG->{PG_alias}->findAppletCodebase(@_);
397 :     }
398 :    
399 :     sub loadMacros {
400 :     $PG->{PG_loadMacros}->loadMacros(@_);
401 :     }
402 :     # FIXME? these were taken from the former dangerousMacros.pl file and might have issues when placed here.
403 :     #
404 :     # Some constants that can be used in perl experssions
405 :     #
406 :    
407 :     # ^function i
408 :     # ^uses $_parser_loaded
409 :     # ^uses &Complex::i
410 :     # ^uses &Value::Package
411 :     sub i () {
412 :     # check if Parser.pl is loaded, otherwise use Complex package
413 :     if (!eval(q!$main::_parser_loaded!)) {return Complex::i}
414 :     return Value->Package("Formula")->new('i')->eval;
415 :     }
416 :    
417 :     # ^function j
418 :     # ^uses $_parser_loaded
419 :     # ^uses &Value::Package
420 :     sub j () {
421 :     if (!eval(q!$main::_parser_loaded!)) {return 'j'}
422 :     Value->Package("Formula")->new('j')->eval;
423 :     }
424 :    
425 :     # ^function k
426 :     # ^uses $_parser_loaded
427 :     # ^uses &Value::Package
428 :     sub k () {
429 :     if (!eval(q!$main::_parser_loaded!)) {return 'k'}
430 :     Value->Package("Formula")->new('k')->eval;
431 :     }
432 :    
433 :     # ^function pi
434 :     # ^uses &Value::Package
435 :     sub pi () {Value->Package("Formula")->new('pi')->eval}
436 :    
437 :     # ^function Infinity
438 :     # ^uses &Value::Package
439 :     sub Infinity () {Value->Package("Infinity")->new()}
440 :    
441 :    
442 :     # ^function abs
443 :     # ^function sqrt
444 :     # ^function exp
445 :     # ^function log
446 :     # ^function sin
447 :     # ^function cos
448 :     # ^function atan2
449 :     #
450 :     # Allow these functions to be overridden
451 :     # (needed for log() to implement $useBaseTenLog)
452 :     #
453 :     use subs 'abs', 'sqrt', 'exp', 'log', 'sin', 'cos', 'atan2';
454 :     sub abs($) {return CORE::abs($_[0])};
455 :     sub sqrt($) {return CORE::sqrt($_[0])};
456 :     sub exp($) {return CORE::exp($_[0])};
457 :     sub log($) {return CORE::log($_[0])};
458 :     sub sin($) {return CORE::sin($_[0])};
459 :     sub cos($) {return CORE::cos($_[0])};
460 :     sub atan2($$) {return CORE::atan2($_[0],$_[1])};
461 :    
462 :     sub Parser::defineLog {eval {sub log($) {CommonFunction->Call("log",@_)}}};
463 :     =head2 Filter utilities
464 :    
465 :     These two subroutines can be used in filters to set default options. They
466 :     help make filters perform in uniform, predictable ways, and also make it
467 :     easy to recognize from the code which options a given filter expects.
468 :    
469 :    
470 :     =head4 assign_option_aliases
471 :    
472 :     Use this to assign aliases for the standard options. It must come before set_default_options
473 :     within the subroutine.
474 :    
475 :     assign_option_aliases(\%options,
476 :     'alias1' => 'option5'
477 :     'alias2' => 'option7'
478 :     );
479 :    
480 :    
481 :     If the subroutine is called with an option " alias1 => 23 " it will behave as if it had been
482 :     called with the option " option5 => 23 "
483 :    
484 :     =cut
485 :    
486 :    
487 :     # ^function assign_option_aliases
488 :     sub assign_option_aliases {
489 :     my $rh_options = shift;
490 :     warn "The first entry to set_default_options must be a reference to the option hash" unless ref($rh_options) eq 'HASH';
491 :     my @option_aliases = @_;
492 :     while (@option_aliases) {
493 :     my $alias = shift @option_aliases;
494 :     my $option_key = shift @option_aliases;
495 :    
496 :     if (defined($rh_options->{$alias} )) { # if the alias appears in the option list
497 :     if (not defined($rh_options->{$option_key}) ) { # and the option itself is not defined,
498 :     $rh_options->{$option_key} = $rh_options->{$alias}; # insert the value defined by the alias into the option value
499 :     # the FIRST alias for a given option takes precedence
500 :     # (after the option itself)
501 :     } else {
502 :     warn "option $option_key is already defined as", $rh_options->{$option_key}, "<br>\n",
503 :     "The attempt to override this option with the alias $alias with value ", $rh_options->{$alias},
504 :     " was ignored.";
505 :     }
506 :     }
507 :     delete($rh_options->{$alias}); # remove the alias from the initial list
508 :     }
509 :    
510 :     }
511 :    
512 :     =head4 set_default_options
513 :    
514 :     set_default_options(\%options,
515 :     '_filter_name' => 'filter',
516 :     'option5' => .0001,
517 :     'option7' => 'ascii',
518 :     'allow_unknown_options => 0,
519 :     }
520 :    
521 :     Note that the first entry is a reference to the options with which the filter was called.
522 :    
523 :     The option5 is set to .0001 unless the option is explicitly set when the subroutine is called.
524 :    
525 :     The B<'_filter_name'> option should always be set, although there is no error if it is missing.
526 :     It is used mainly for debugging answer evaluators and allows
527 :     you to keep track of which filter is currently processing the answer.
528 :    
529 :     If B<'allow_unknown_options'> is set to 0 then if the filter is called with options which do NOT appear in the
530 :     set_default_options list an error will be signaled and a warning message will be printed out. This provides
531 :     error checking against misspelling an option and is generally what is desired for most filters.
532 :    
533 :     Occasionally one wants to write a filter which accepts a long list of options, not all of which are known in advance,
534 :     but only uses a subset of the options
535 :     provided. In this case, setting 'allow_unkown_options' to 1 prevents the error from being signaled.
536 :    
537 :     =cut
538 :    
539 :     # ^function set_default_options
540 :     # ^uses pretty_print
541 :     sub set_default_options {
542 :     my $rh_options = shift;
543 :     warn "The first entry to set_default_options must be a reference to the option hash" unless ref($rh_options) eq 'HASH';
544 :     my %default_options = @_;
545 :     unless ( defined($default_options{allow_unknown_options}) and $default_options{allow_unknown_options} == 1 ) {
546 :     foreach my $key1 (keys %$rh_options) {
547 :     warn "This option |$key1| is not recognized in this subroutine<br> ", pretty_print($rh_options) unless exists($default_options{$key1});
548 :     }
549 :     }
550 :     foreach my $key (keys %default_options) {
551 :     if ( not defined($rh_options->{$key} ) and defined( $default_options{$key} ) ) {
552 :     $rh_options->{$key} = $default_options{$key}; #this allows tol => undef to allow the tol option, but doesn't define
553 :     # this key unless tol is explicitly defined.
554 :     }
555 :     }
556 :     }
557 :     1;
558 :     __END__
559 :    
560 :     ################################################################################
561 : sh002i 5568 # WeBWorK Online Homework Delivery System
562 : gage 6248 # Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
563 : gage 6281 # $CVSHeader: pg/macros/PG.pl,v 1.46 2010/05/27 02:22:51 gage Exp $
564 : sh002i 5568 #
565 : sh002i 5179 # This program is free software; you can redistribute it and/or modify it under
566 :     # the terms of either: (a) the GNU General Public License as published by the
567 :     # Free Software Foundation; either version 2, or (at your option) any later
568 :     # version, or (b) the "Artistic License" which comes with this package.
569 : sh002i 5568 #
570 : sh002i 5179 # This program is distributed in the hope that it will be useful, but WITHOUT
571 :     # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
572 :     # FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
573 :     # Artistic License for more details.
574 :     ################################################################################
575 : gage 4997
576 : sh002i 5179 =head1 NAME
577 : sh002i 1050
578 : sh002i 5179 PG.pl - Provides core Program Generation Language functionality.
579 : sh002i 1050
580 : sh002i 5179 =head1 SYNPOSIS
581 : sh002i 1050
582 : sh002i 5179 In a PG problem:
583 : sh002i 1050
584 : sh002i 5568 DOCUMENT(); # should be the first statment in the problem
585 :    
586 :     loadMacros(.....); # (optional) load other macro files if needed.
587 :     # (loadMacros is defined in F<dangerousMacros.pl>)
588 :    
589 :     HEADER_TEXT(...); # (optional) used only for inserting javaScript into problems.
590 :    
591 :     TEXT( # insert text of problems
592 :     "Problem text to be displayed. ",
593 :     "Enter 1 in this blank:",
594 :     ANS_RULE(1,30) # ANS_RULE() defines an answer blank 30 characters long.
595 :     # It is defined in F<PGbasicmacros.pl>
596 :     );
597 :    
598 :     ANS(answer_evalutors); # see F<PGanswermacros.pl> for examples of answer evaluatiors.
599 :    
600 :     ENDDOCUMENT() # must be the last statement in the problem
601 : sh002i 1050
602 : sh002i 5179 =head1 DESCRIPTION
603 : sh002i 1050
604 : sh002i 5179 This file provides the fundamental macros that define the PG language. It
605 :     maintains a problem's text, header text, and answers:
606 : sh002i 1050
607 : sh002i 5179 =over
608 : sh002i 1050
609 : sh002i 5179 =item *
610 : sh002i 1050
611 : sh002i 5179 Problem text: The text to appear in the body of the problem. See TEXT()
612 :     below.
613 : sh002i 1050
614 : sh002i 5179 =item *
615 : sh002i 1050
616 : sh002i 5179 Header text: When a problem is processed in an HTML-based display mode,
617 :     this variable can contain text that the caller should place in the HEAD of the
618 :     resulting HTML page. See HEADER_TEXT() below.
619 : sh002i 1050
620 : sh002i 5179 =item *
621 : sh002i 1050
622 : sh002i 5179 Implicitly-labeled answers: Answers that have not been explicitly
623 :     assigned names, and are associated with their answer blanks by the order in
624 :     which they appear in the problem. These types of answers are designated using
625 :     the ANS() macro.
626 : sh002i 1050
627 : sh002i 5179 =item *
628 : sh002i 1050
629 : sh002i 5179 Explicitly-labeled answers: Answers that have been explicitly assigned
630 :     names with the LABELED_ANS() macro, or a macro that uses it. An explicitly-
631 :     labeled answer is associated with its answer blank by name.
632 : sh002i 1050
633 : sh002i 5179 =item *
634 : sh002i 1050
635 : sh002i 5179 "Extra" answers: Names of answer blanks that do not have a 1-to-1
636 :     correspondance to an answer evaluator. For example, in matrix problems, there
637 :     will be several input fields that correspond to the same answer evaluator.
638 : sh002i 1050
639 : sh002i 5179 =back
640 : sh002i 1050
641 : sh002i 5179 =head1 USAGE
642 : sh002i 1050
643 : sh002i 5179 This file is automatically loaded into the namespace of every PG problem. The
644 :     macros within can then be called to define the structure of the problem.
645 : sh002i 1050
646 : sh002i 5179 DOCUMENT() should be the first executable statement in any problem. It
647 :     initializes vriables and defines the problem environment.
648 : sh002i 1050
649 : sh002i 5179 ENDDOCUMENT() must be the last executable statement in any problem. It packs
650 :     up the results of problem processing for delivery back to WeBWorK.
651 : sh002i 1050
652 : sh002i 5179 The HEADER_TEXT(), TEXT(), and ANS() macros add to the header text string,
653 :     body text string, and answer evaluator queue, respectively.
654 : sh002i 1050
655 : sh002i 5179 =over
656 :    
657 :     =item HEADER_TEXT()
658 : sh002i 1050
659 : sh002i 5179 HEADER_TEXT("string1", "string2", "string3");
660 : sh002i 1050
661 : sh002i 5179 HEADER_TEXT() concatenates its arguments and appends them to the stored header
662 :     text string. It can be used more than once in a file.
663 : sh002i 1050
664 : sh002i 5179 The macro is used for material which is destined to be placed in the HEAD of
665 :     the page when in HTML mode, such as JavaScript code.
666 : sh002i 1050
667 : sh002i 5179 Spaces are placed between the arguments during concatenation, but no spaces are
668 :     introduced between the existing content of the header text string and the new
669 :     content being appended.
670 : sh002i 1050
671 :    
672 :    
673 : sh002i 5179 =item TEXT()
674 : sh002i 1050
675 : sh002i 5179 TEXT("string1", "string2", "string3");
676 : sh002i 1050
677 : sh002i 5179 TEXT() concatenates its arguments and appends them to the stored problem text
678 :     string. It is used to define the text which will appear in the body of the
679 :     problem. It can be used more than once in a file.
680 : sh002i 1050
681 : sh002i 5179 This macro has no effect if rendering has been stopped with the STOP_RENDERING()
682 :     macro.
683 : sh002i 1050
684 : sh002i 5179 This macro defines text which will appear in the problem. All text must be
685 :     passed to this macro, passed to another macro that calls this macro, or included
686 :     in a BEGIN_TEXT/END_TEXT block, which uses this macro internally. No other
687 :     statements in a PG file will directly appear in the output. Think of this as the
688 :     "print" function for the PG language.
689 :    
690 :     Spaces are placed between the arguments during concatenation, but no spaces are
691 :     introduced between the existing content of the header text string and the new
692 :     content being appended.
693 :    
694 : sh002i 1050
695 : gage 3387
696 : sh002i 5179 =item ANS()
697 : gage 3387
698 : sh002i 5179 TEXT(ans_rule(), ans_rule(), ans_rule());
699 :     ANS($answer_evaluator1, $answer_evaluator2, $answer_evaluator3);
700 : gage 3387
701 : sh002i 5179 Adds the answer evaluators listed to the list of unlabeled answer evaluators.
702 :     They will be paired with unlabeled answer rules (a.k.a. answer blanks) in the
703 :     order entered. This is the standard method for entering answers.
704 : gage 3387
705 : sh002i 5179 In the above example, answer_evaluator1 will be associated with the first
706 :     answer rule, answer_evaluator2 with the second, and answer_evaluator3 with the
707 :     third. In practice, the arguments to ANS() will usually be calls to an answer
708 :     evaluator generator such as the cmp() method of MathObjects or the num_cmp()
709 :     macro in L<PGanswermacros.pl>.
710 :    
711 : gage 3387
712 :    
713 : sh002i 5179 =item LABELED_ANS()
714 : gage 3387
715 : sh002i 5179 TEXT(labeled_ans_rule("name1"), labeled_ans_rule("name2"));
716 :     LABELED_ANS(name1 => answer_evaluator1, name2 => answer_evaluator2);
717 : gage 3387
718 : sh002i 5179 Adds the answer evaluators listed to the list of labeled answer evaluators.
719 :     They will be paired with labeled answer rules (a.k.a. answer blanks) in the
720 :     order entered. This allows pairing of answer evaluators and answer rules that
721 :     may not have been entered in the same order.
722 : gage 3387
723 :    
724 : sh002i 1050
725 :    
726 : sh002i 5179 =item STOP_RENDERING()
727 : sh002i 1050
728 : sh002i 5179 STOP_RENDERING() unless all_answers_are_correct();
729 : lr003k 1122
730 : sh002i 5179 Temporarily suspends accumulation of problem text and storing of answer blanks
731 :     and answer evaluators until RESUME_RENDERING() is called.
732 : lr003k 1122
733 : sh002i 5179
734 : lr003k 1122
735 : sh002i 5179 =item RESUME_RENDERING()
736 : gage 3385
737 : sh002i 5179 RESUME_RENDERING();
738 :    
739 :     Resumes accumulating problem text and storing answer blanks and answer
740 :     evaluators. Reverses the effect of STOP_RENDERING().
741 :    
742 :    
743 : sh002i 1050
744 : sh002i 5179 =item ENDDOCUMENT()
745 : sh002i 1050
746 : sh002i 5179 ENDDOCUMENT();
747 : sh002i 1050
748 : sh002i 5179 When PG problems are evaluated, the result of evaluating the entire problem is
749 :     interpreted as the return value of ENDDOCUMENT(). Therefore, ENDDOCUMENT() must
750 : gage 5442 be the last executable statement of every problem. It can only appear once. It
751 :     returns a list consisting of:
752 : sh002i 1050
753 :    
754 : gage 6248
755 :    
756 : sh002i 5179 =item *
757 : sh002i 1050
758 : sh002i 5179 A reference to a string containing the rendered text of the problem.
759 : sh002i 1050
760 : sh002i 5179 =item *
761 :    
762 :     A reference to a string containing text to be placed in the HEAD block
763 :     when in and HTML-based mode (e.g. for JavaScript).
764 :    
765 :     =item *
766 :    
767 :     A reference to the hash mapping answer labels to answer evaluators.
768 :    
769 :     =item *
770 :    
771 :     A reference to a hash containing various flags:
772 :    
773 :    
774 : gage 6248
775 : sh002i 5179 =item *
776 :    
777 :     C<showPartialCorrectAnswers>: determines whether students are told which of their answers in a problem are wrong.
778 :    
779 :     =item *
780 :    
781 :     C<recordSubmittedAnswers>: determines whether students submitted answers are saved.
782 :    
783 :     =item *
784 :    
785 :     C<refreshCachedImages>: determines whether the cached image of the problem in typeset mode is always refreshed
786 :     (i.e. setting this to 1 means cached images are not used).
787 :    
788 :     =item *
789 :    
790 :     C<solutionExits>: indicates the existence of a solution.
791 :    
792 :     =item *
793 :    
794 :     C<hintExits>: indicates the existence of a hint.
795 :    
796 :     =item *
797 :    
798 :     C<comment>: contents of COMMENT commands if any.
799 :    
800 :     =item *
801 :    
802 :     C<showHintLimit>: determines the number of attempts after which hint(s) will be shown
803 :    
804 :     =item *
805 :    
806 :     C<PROBLEM_GRADER_TO_USE>: a reference to the chosen problem grader.
807 :     ENDDOCUMENT chooses the problem grader as follows:
808 :    
809 :     =over
810 :    
811 :     =item *
812 :    
813 :     If a problem grader has been chosen in the problem by calling
814 :     C<install_problem_grader(\&grader)>, it is used.
815 :    
816 :     =item *
817 :    
818 :     Otherwise, if the C<PROBLEM_GRADER_TO_USE> PG environment variable
819 :     contains a reference to a subroutine, it is used.
820 :    
821 :     =item *
822 :    
823 :     Otherwise, if the C<PROBLEM_GRADER_TO_USE> PG environment variable
824 :     contains the string C<std_problem_grader> or the string C<avg_problem_grader>,
825 :     C<&std_problem_grader> or C<&avg_problem_grader> are used. These graders are defined
826 :     in L<PGanswermacros.pl>.
827 :    
828 :     =item *
829 :    
830 :     Otherwise, the PROBLEM_GRADER_TO_USE flag will contain an empty value
831 :     and the PG translator should select C<&std_problem_grader>.
832 :    
833 :     =back
834 :    
835 :     =back
836 :    
837 :    
838 : gage 6248
839 : sh002i 1050 =cut
840 :    
841 :    
842 : sh002i 5179 ################################################################################
843 : sh002i 1050
844 : sh002i 5179 =head1 PRIVATE MACROS
845 : sh002i 1050
846 : sh002i 5179 These macros should only be used by other macro files. In practice, they are
847 :     used exclusively by L<PGbasicmacros.pl>.
848 :    
849 :     =over
850 :    
851 :     =item inc_ans_rule_count()
852 :    
853 : gage 6248 NEW_ANS_NAME();
854 : sh002i 5179
855 :     Increments the internal count of the number of answer blanks that have been
856 :     defined ($ans_rule_count) and returns the new count. This should only be used
857 :     when one is about to define a new answer blank, for example with NEW_ANS_NAME().
858 :    
859 : sh002i 1050 =cut
860 :    
861 : sh002i 5179 =item RECORD_ANS_NAME()
862 :    
863 : gage 6248 RECORD_ANS_NAME("label", "VALUE");
864 : sh002i 5179
865 :     Records the label for an answer blank. Used internally by L<PGbasicmacros.pl>
866 :     to record the order of explicitly-labelled answer blanks.
867 :    
868 :     =cut
869 :    
870 :     =item NEW_ANS_NAME()
871 :    
872 : gage 6248 NEW_ANS_NAME();
873 : sh002i 5179
874 : gage 6248 Generates an anonymous answer label from the internal count The label is
875 : sh002i 5179 added to the list of implicity-labeled answers. Used internally by
876 :     L<PGbasicmacros.pl> to generate labels for unlabeled answer blanks.
877 :    
878 :     =cut
879 :    
880 :     =item ANS_NUM_TO_NAME()
881 :    
882 :     ANS_NUM_TO_NAME($num);
883 :    
884 :     Generates an answer label from the supplied answer number, but does not add it
885 :     to the list of inplicitly-labeled answers. Used internally by
886 :     L<PGbasicmacros.pl> in generating answers blanks that use radio buttons or
887 :     check boxes. (This type of answer blank uses multiple HTML INPUT elements with
888 :     the same label, but the label should only be added to the list of implicitly-
889 :     labeled answers once.)
890 :    
891 :     =cut
892 :    
893 :     =item RECORD_FROM_LABEL()
894 :    
895 :     RECORD_FORM_LABEL("label");
896 :    
897 :     Stores the label of a form field in the "extra" answers list. This is used to
898 :     keep track of answer blanks that are not associated with an answer evaluator.
899 :    
900 :     =cut
901 :    
902 :     =item NEW_ANS_ARRAY_NAME()
903 :    
904 :     NEW_ANS_ARRAY_NAME($num, $row, $col);
905 :    
906 :     Generates a new answer label for an array (vector) element and adds it to the
907 :     list of implicitly-labeled answers.
908 :    
909 :     =cut
910 :    
911 :     =item NEW_ANS_ARRAY_NAME_EXTENSION()
912 :    
913 :     NEW_ANS_ARRAY_NAME_EXTENSION($num, $row, $col);
914 :    
915 :     Generate an additional answer label for an existing array (vector) element and
916 :     add it to the list of "extra" answers.
917 :    
918 :     =cut
919 :    
920 :     =item get_PG_ANSWERS_HASH()
921 :    
922 :     get_PG_ANSWERS_HASH();
923 :     get_PG_ANSWERS_HASH($key);
924 :    
925 :    
926 :    
927 :     =cut
928 :    
929 : jj 5269 =item includePGproblem($filePath)
930 :    
931 :     includePGproblem($filePath);
932 :    
933 :     Essentially runs the pg problem specified by $filePath, which is
934 :     a path relative to the top of the templates directory. The output
935 :     of that problem appears in the given problem.
936 :    
937 :     =cut
938 :    
939 : sh002i 5179 =back
940 :    
941 :     =head1 SEE ALSO
942 :    
943 :     L<PGbasicmacros.pl>, L<PGanswermacros.pl>.
944 :    
945 :     =cut
946 :    
947 : gage 6248
948 :    
949 :    
950 :     1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9