Parent Directory
|
Revision Log
Hints and Solutions now use basically the same mechanism. Fixed hugs in hints and minor bugs in solutions.
1 #!/usr/local/bin/webwork-perl 2 3 # This file provided the fundamental macros for the pg language 4 # These macros define the interface between the problems written by 5 # the professor and the processing which occurs in the script 6 # processProblem.pl 7 8 9 BEGIN { 10 be_strict(); 11 } 12 #sub _PG_export { 13 # my @EXPORT= ( 14 # '&_PG_init', 15 # '&ANS', 16 # '&ANS_NUM_TO_NAME', 17 # '&DOCUMENT', 18 # '&ENDDOCUMENT', 19 # '&HEADER_TEXT', 20 # 'NAMED_ANS', 21 # 'NEW_ANS_NAME', 22 # 'RECORD_ANS_NAME', 23 # 'TEXT', 24 # ); 25 # @EXPORT; 26 #} 27 sub _PG_init{ 28 29 } 30 31 #package PG; 32 33 34 =head1 NAME 35 36 PG.pl --- located in the courseScripts directory. 37 Defines the Program Generating language at the most basic level. 38 39 =head1 SYNPOSIS 40 41 The basic PG problem structure: 42 43 DOCUMENT(); # should be the first statment in the problem 44 loadMacros(.....); # (optional) load other macro files if needed. 45 # (loadMacros is defined in F<dangerousMacros.pl>) 46 47 HEADER_TEXT(...); # (optional) used only for inserting javaScript into problems. 48 49 # # insert text of problems 50 TEXT("Problem text to be", 51 "displayed. Enter 1 in this blank:", 52 ANS_RULE(1,30) # ANS_RULE() defines an answer blank 30 characters long. 53 # It is defined in F<PGbasicmacros.pl> 54 ); 55 56 57 ANS( answer_evalutors); # see F<PGanswermacros.pl> for examples of answer evaluatiors. 58 59 ENDDOCUMENT() # must be the last statement in the problem 60 61 62 63 =head1 DESCRIPTION 64 65 As described in the synopsis, this file and the macros C<DOCUMENT()> and C<ENDDOCUMENT()> determine 66 the interface between problems written in the PG language and the rest of B<WeBWorK>, in particular 67 the subroutine C<createPGtext(()> in the file F<translate.pl>. 68 69 C<DOCUMENT()> must be the first statement in each problem template. 70 It initializes variables, 71 in particular all of the contents of the 72 environment variable become defined in the problem enviroment. 73 (See 74 L</webwork_system_html/docs/techdescription/pglanguage/PGenvironment.html>) 75 76 ENDDOCUMENT() must the last executable statement in any problem template. It returns 77 the rendered problem, answer evaluators and other flags to the rest of B<WeBWorK>, specificially 78 to the routine C<createPGtext()> defined in F<translate.pl> 79 80 81 The C<HEADER_TEXT()>, C<TEXT()>, and C<ANS()> functions load the 82 header text string, the problem text string. 83 and the answer evaulator queue respectively. 84 85 86 =cut 87 88 89 # Private variables for the PG.pl file. 90 91 my ($STRINGforOUTPUT, $STRINGforHEADER_TEXT, @PG_ANSWERS, @PG_UNLABELED_ANSWERS); 92 my %PG_ANSWERS_HASH ; 93 94 # DOCUMENT must come early in every .pg file, before any answers or text are 95 # defined. It initializes the variables. 96 # It can appear only once. 97 98 =head2 DOCUMENT() 99 100 C<DOCUMENT()> must be the first statement in each problem template. It can 101 only be used once in each problem. 102 103 C<DOCUMENT()> initializes some empty variables and via C<INITIALIZE_PG()> unpacks the 104 variables in the C<%envir> variable which is implicitly passed to the problem. It must 105 be the first statement in any problem template. It 106 also unpacks any answers submitted and places them in the C<@submittedAnswer> list, 107 saves the problem seed in C<$PG_original_problemSeed> in case you need it later, and 108 initializes the pseudo random number generator object in C<$PG_random_generator>. 109 110 You can reset the standard number generator using the command: 111 112 $PG_random_generator->srand($new_seed_value); 113 114 (See also C<SRAND> in the L<PGbasicmacros.pl> file.) 115 116 The 117 environment variable contents is defined in 118 L</webwork_system_html/docs/techdescription/pglanguage/PGenvironment.html> 119 120 121 =cut 122 123 sub DOCUMENT { 124 $STRINGforOUTPUT =""; 125 $STRINGforHEADER_TEXT =""; 126 @PG_ANSWERS=(); 127 @main::PG_ANSWER_ENTRY_ORDER = (); 128 @PG_UNLABELED_ANSWERS = (); 129 %PG_ANSWERS_HASH = (); 130 #eval q{ #make sure that "main" points to the current safe compartment by evaluating these lines. 131 # when using forking the safe compartment always has the same name, so this isn't needed. 132 $main::ANSWER_PREFIX = 'AnSwEr'; 133 %main::PG_FLAGS=(); #global flags 134 $main::showPartialCorrectAnswers = 0 unless defined($main::showPartialCorrectAnswers ); 135 $main::showHint = 1 unless defined($main::showHint); 136 $main::solutionExists =0; 137 $main::hintExists =0; 138 %main::gifs_created = (); 139 140 die "The environment variable envir has not been defined" unless defined(%main::envir); 141 #}; 142 foreach my $var ( keys %main::envir ) { 143 eval("\$main::$var =\$main::envir{'$var'}"); 144 warn "Problem defining ", q{\$main::$var}, " while inititializing the PG problem: $@" if $@; 145 } 146 #eval q{ 147 @main::submittedAnswers = @{$main::refSubmittedAnswers} if defined($main::refSubmittedAnswers); 148 $main::PG_original_problemSeed = $main::problemSeed; 149 $main::PG_random_generator = new PGrandom($main::problemSeed) || die "Can't create random number generator."; 150 $main::ans_rule_count = 0; # counts questions 151 #}; 152 #warn "key1", join( "<>",keys %main::); 153 #warn "key2", join( "<>",eval q{ keys %main::}); 154 # end unpacking of environment variables. 155 } 156 157 # HEADER_TEXT is for material which is destined to be placed in the header of the html problem -- such 158 # as javaScript code. 159 160 =head2 HEADER_TEXT() 161 162 163 HEADER_TEXT("string1", "string2", "string3"); 164 165 The C<HEADER_TEXT()> 166 function concatenates its arguments and places them in the output 167 header text string. It is used for material which is destined to be placed in 168 the header of the html problem -- such as javaScript code. 169 It can be used more than once in a file. 170 171 172 =cut 173 174 sub HEADER_TEXT { 175 my @in = @_; 176 $STRINGforHEADER_TEXT .= join(" ",@in); 177 } 178 179 # TEXT is the function which defines text which will appear in the problem. 180 # All text must be an argument to this function. Any other statements 181 # are calculations (done in perl) which will not directly appear in the 182 # output. Think of this as the "print" function for the .pg language. 183 # It can be used more than once in a file. 184 185 =head2 TEXT() 186 187 TEXT("string1", "string2", "string3"); 188 189 The C<TEXT()> function concatenates its arguments and places them in the output 190 text string. C<TEXT()> is the function which defines text which will appear in the problem. 191 All text must be an argument to this function. Any other statements 192 are calculations (done in perl) which will not directly appear in the 193 output. Think of this as the "print" function for the .pg language. 194 It can be used more than once in a file. 195 196 =cut 197 198 sub TEXT { 199 my @in = @_; 200 $STRINGforOUTPUT .= join(" ",@in); 201 } 202 203 204 205 =head2 ANS() 206 207 ANS(answer_evaluator1, answer_evaluator2, answer_evaluator3,...) 208 209 Places the answer evaluators in the unlabeled answer_evaluator queue. They will be paired 210 with unlabeled answer rules (answer entry blanks) in the order entered. This is the standard 211 method for entering answers. 212 213 LABELED_ANS(answer_evaluater_name1, answer_evaluator1, answer_evaluater_name2,answer_evaluator2,...) 214 215 Places the answer evaluators in the labeled answer_evaluator hash. This allows pairing of 216 labeled answer evaluators and labeled answer rules which may not have been entered in the same 217 order. 218 219 =cut 220 221 sub ANS{ # store answer evaluators which have not been explicitly labeled 222 my @in = @_; 223 while (@in ) { 224 warn("<BR><B>Error in ANS:$in[0]</B> -- inputs must be references to 225 subroutines<BR>") 226 unless ref($in[0]); 227 push(@PG_ANSWERS, shift @in ); 228 } 229 } 230 231 sub NAMED_ANS{ # store answer evaluators which have been explicitly labeled (submitted in a hash) 232 my @in = @_; 233 while (@in ) { 234 my $label = shift @in; 235 my $ans_eval = shift @in; 236 TEXT("<BR><B>Error in NAMED_ANS:$in[0]</B> 237 -- inputs must be references to subroutines<BR>") 238 unless ref($ans_eval); 239 $PG_ANSWERS_HASH{$label}= $ans_eval; 240 } 241 } 242 sub RECORD_ANS_NAME { # this maintains the order in which the answer rules are printed. 243 my $label = shift; 244 push(@main::PG_ANSWER_ENTRY_ORDER, $label); 245 $label; 246 } 247 248 sub NEW_ANS_NAME { # this keeps track of the answers which are entered implicitly, 249 # rather than with a specific label 250 my $number=shift; 251 my $label = "$main::ANSWER_PREFIX$number"; 252 push(@PG_UNLABELED_ANSWERS,$label); 253 $label; 254 } 255 sub ANS_NUM_TO_NAME { # This converts a number to an answer label for use in 256 # radio button and check box answers. No new answer 257 # name is recorded. 258 my $number=shift; 259 my $label = "$main::ANSWER_PREFIX$number"; 260 $label; 261 } 262 263 # ENDDOCUMENT must come at the end of every .pg file. 264 # It exports the resulting text of the problem, the text to be used in HTML header material 265 # (for javaScript), the list of answer evaluators and any other flags. It can appear only once and 266 # it MUST be the last statement in the problem. 267 268 =head2 ENDDOCUMENT() 269 270 ENDDOCUMENT() must the last executable statement in any problem template. It can 271 only appear once. It returns 272 an array consisting of 273 274 A reference to a string containing the rendered text of the problem. 275 A reference to a string containing text to be placed in the header 276 (for javaScript) 277 A reference to the array containing the answer evaluators. 278 (May be changed to a hash soon.) 279 A reference to an associative array (hash) containing various flags. 280 281 The following flags are set by ENDDOCUMENT: 282 (1) showPartialCorrectAnswers -- determines whether students are told which 283 of their answers in a problem are wrong. 284 (2) recordSubmittedAnswers -- determines whether students submitted answers 285 are saved. 286 recordSubmittedAnswers 287 (3) solutionExits -- indicates the existence of a solution. 288 (4) hintExits -- indicates the existence of a hint. 289 (5) showHintLimit -- determines the number of attempts after which hint(s) will be shown 290 291 (6) PROBLEM_GRADER_TO_USE -- chooses the problem grader to be used in this order 292 (a) A problem grader specified by the problem using: 293 install_problem_grader(\&grader); 294 (b) One of the standard problem graders defined in PGanswermacros.pl when set to 295 'std_problem_grader' or 'avg_problem_grader' by the environment variable 296 $PG_environment{PROBLEM_GRADER_TO_USE} 297 (c) A subroutine referenced by $PG_environment{PROBLEM_GRADER_TO_USE} 298 (d) The default &std_problem_grader defined in PGanswermacros.pl 299 300 301 =cut 302 303 sub ENDDOCUMENT { 304 305 my $index=0; 306 foreach my $label (@PG_UNLABELED_ANSWERS) { 307 if ( defined($PG_ANSWERS[$index]) ) { 308 $PG_ANSWERS_HASH{"$label"}= $PG_ANSWERS[$index]; 309 } else { 310 warn "No answer provided by instructor for answer $label"; 311 } 312 $index++; 313 } 314 315 $STRINGforOUTPUT .="\n"; 316 ##eval q{ #make sure that "main" points to the current safe compartment by evaluating these lines. 317 $main::PG_FLAGS{'showPartialCorrectAnswers'} = $main::showPartialCorrectAnswers; 318 $main::PG_FLAGS{'recordSubmittedAnswers'} = $main::recordSubmittedAnswers; 319 $main::PG_FLAGS{'hintExists'} = $main::hintExists; 320 $main::PG_FLAGS{'showHintLimit'} = $main::showHint; 321 $main::PG_FLAGS{'solutionExists'} = $main::solutionExists; 322 $main::PG_FLAGS{ANSWER_ENTRY_ORDER} = \@main::PG_ANSWER_ENTRY_ORDER; 323 $main::PG_FLAGS{ANSWER_PREFIX} = $main::ANSWER_PREFIX; 324 # install problem grader 325 if (defined($main::PG_FLAGS{PROBLEM_GRADER_TO_USE}) ) { 326 # problem grader defined within problem -- no further action needed 327 } elsif ( defined( $main::envir{PROBLEM_GRADER_TO_USE} ) ) { 328 if (ref($main::envir{PROBLEM_GRADER_TO_USE}) eq 'CODE' ) { # user defined grader 329 $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = $main::envir{PROBLEM_GRADER_TO_USE}; 330 } elsif ($main::envir{PROBLEM_GRADER_TO_USE} eq 'std_problem_grader' ) { 331 if (defined(&std_problem_grader) ){ 332 $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&std_problem_grader; # defined in PGanswermacros.pl 333 } # std_problem_grader is the default in any case so don't give a warning. 334 } elsif ($main::envir{PROBLEM_GRADER_TO_USE} eq 'avg_problem_grader' ) { 335 if (defined(&avg_problem_grader) ){ 336 $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&avg_problem_grader; # defined in PGanswermacros.pl 337 } else { 338 warn "The problem grader 'avg_problem_grader' has not been defined. Has PGanswermacros.pl been loaded?"; 339 } 340 } else { 341 warn "Error: $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} is not a known program grader."; 342 } 343 } elsif (defined(&std_problem_grader)) { 344 $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&std_problem_grader; # defined in PGanswermacros.pl 345 } else { 346 # PGtranslator will install its default problem grader 347 } 348 ##}; 349 warn "ERROR: The problem grader is not a subroutine" unless ref( $main::PG_FLAGS{PROBLEM_GRADER_TO_USE}) eq 'CODE'; 350 # return results 351 (\$STRINGforOUTPUT, \$STRINGforHEADER_TEXT,\%PG_ANSWERS_HASH,\%main::PG_FLAGS); 352 } 353 354 355 356 =head2 INITIALIZE_PG() 357 358 This is executed each C<DOCUMENT()> is called. For backward compatibility 359 C<loadMacros> also checks whether the C<macroDirectory> has been defined 360 and if not, it runs C<INITIALIZE_PG()> and issues a warning. 361 362 =cut 363 364 ################################################################################ 365 # Initialize the global variables to be used in PG 366 ######### 367 368 #HACK -- fix this 369 #unless (defined(&PGrandom::srand) ) { 370 # 371 # do "$main::envir{courseScriptsDirectory}PGrandom.pm" || 372 # die "Can't read $main::envir{courseScriptsDirectory}PGrandom.pm"; 373 #} 374 # 375 376 377 ######### 378 # Initialization is complete 379 ################################################################################ 380 381 1;
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |