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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9