[system] / trunk / pg / macros / PG.pl Repository:
ViewVC logotype

View of /trunk/pg/macros/PG.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 6058 - (download) (as text) (annotate)
Thu Jun 25 23:28:44 2009 UTC (10 years, 6 months ago) by gage
File size: 27538 byte(s)
syncing pg HEAD with pg2.4.7 on 6/25/2009

    1 ################################################################################
    2 # WeBWorK Online Homework Delivery System
    3 # Copyright  2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
    4 # $CVSHeader$
    5 #
    6 # This program is free software; you can redistribute it and/or modify it under
    7 # the terms of either: (a) the GNU General Public License as published by the
    8 # Free Software Foundation; either version 2, or (at your option) any later
    9 # version, or (b) the "Artistic License" which comes with this package.
   10 #
   11 # This program is distributed in the hope that it will be useful, but WITHOUT
   12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   13 # FOR A PARTICULAR PURPOSE.  See either the GNU General Public License or the
   14 # Artistic License for more details.
   15 ################################################################################
   16 
   17 =head1 NAME
   18 
   19 PG.pl - Provides core Program Generation Language functionality.
   20 
   21 =head1 SYNPOSIS
   22 
   23 In a PG problem:
   24 
   25   DOCUMENT();             # should be the first statment in the problem
   26 
   27   loadMacros(.....);      # (optional) load other macro files if needed.
   28                           # (loadMacros is defined in F<dangerousMacros.pl>)
   29 
   30   HEADER_TEXT(...);       # (optional) used only for inserting javaScript into problems.
   31 
   32   TEXT(                   # insert text of problems
   33     "Problem text to be displayed. ",
   34     "Enter 1 in this blank:",
   35     ANS_RULE(1,30)      # ANS_RULE() defines an answer blank 30 characters long.
   36                       # It is defined in F<PGbasicmacros.pl>
   37   );
   38 
   39   ANS(answer_evalutors);  # see F<PGanswermacros.pl> for examples of answer evaluatiors.
   40 
   41   ENDDOCUMENT()           # must be the last statement in the problem
   42 
   43 =head1 DESCRIPTION
   44 
   45 This file provides the fundamental macros that define the PG language. It
   46 maintains a problem's text, header text, and answers:
   47 
   48 =over
   49 
   50 =item *
   51 
   52 Problem text: The text to appear in the body of the problem. See TEXT()
   53 below.
   54 
   55 =item *
   56 
   57 Header text: When a problem is processed in an HTML-based display mode,
   58 this variable can contain text that the caller should place in the HEAD of the
   59 resulting HTML page. See HEADER_TEXT() below.
   60 
   61 =item *
   62 
   63 Implicitly-labeled answers: Answers that have not been explicitly
   64 assigned names, and are associated with their answer blanks by the order in
   65 which they appear in the problem. These types of answers are designated using
   66 the ANS() macro.
   67 
   68 =item *
   69 
   70 Explicitly-labeled answers: Answers that have been explicitly assigned
   71 names with the LABELED_ANS() macro, or a macro that uses it. An explicitly-
   72 labeled answer is associated with its answer blank by name.
   73 
   74 =item *
   75 
   76 "Extra" answers: Names of answer blanks that do not have a 1-to-1
   77 correspondance to an answer evaluator. For example, in matrix problems, there
   78 will be several input fields that correspond to the same answer evaluator.
   79 
   80 =back
   81 
   82 =head1 USAGE
   83 
   84 This file is automatically loaded into the namespace of every PG problem. The
   85 macros within can then be called to define the structure of the problem.
   86 
   87 DOCUMENT() should be the first executable statement in any problem. It
   88 initializes vriables and defines the problem environment.
   89 
   90 ENDDOCUMENT() must be the last executable statement in any problem. It packs
   91 up the results of problem processing for delivery back to WeBWorK.
   92 
   93 The HEADER_TEXT(), TEXT(), and ANS() macros add to the header text string,
   94 body text string, and answer evaluator queue, respectively.
   95 
   96 =cut
   97 
   98 BEGIN {
   99   be_strict();
  100 }
  101 
  102 sub _PG_init{
  103 
  104 }
  105 
  106 #package PG;
  107 
  108 #  Private variables for the PG.pl file.
  109 
  110 # ^variable my $STRINGforOUTPUT
  111 my $STRINGforOUTPUT;
  112 # ^variable my $STRINGforHEADER_TEXT
  113 my $STRINGforHEADER_TEXT;
  114 # ^variable my @PG_ANSWERS
  115 my @PG_ANSWERS;
  116 # ^variable my @PG_UNLABELED_ANSWERS
  117 my @PG_UNLABELED_ANSWERS;
  118 # ^variable my %PG_ANSWERS_HASH
  119 my %PG_ANSWERS_HASH;
  120 
  121 # ^variable our $PG_STOP_FLAG
  122 our $PG_STOP_FLAG;
  123 
  124 # my variables are unreliable if two DOCUMENTS were to be called before an ENDDOCUMENT
  125 # there could be conflicts.  As I understand the behavior of the Apache child
  126 # this cannot occur -- a child finishes with one request before obtaining the next
  127 
  128 ################################################################################
  129 
  130 =head1 MACROS
  131 
  132 These macros may be used from PG problem files.
  133 
  134 =over
  135 
  136 =item DOCUMENT()
  137 
  138 DOCUMENT() should be the first statement in each problem template.  It can
  139 only be used once in each problem.
  140 
  141 DOCUMENT() initializes some empty variables and unpacks the variables in the
  142 %envir hash which is implicitly passed from WeBWorK to the problem. It must be
  143 the first statement in any problem. It also unpacks any answers submitted and
  144 places them in the @submittedAnswer list, saves the problem seed in
  145 $PG_original_problemSeed in case you need it later, and initializes the pseudo
  146 random number generator object in $PG_random_generator.
  147 
  148 You can reset the standard number generator using the command:
  149 
  150  $PG_random_generator->srand($new_seed_value);
  151 
  152 See also SRAND() in the L<PGbasicmacros.pl> file.
  153 
  154 =cut
  155 
  156 # ^function DOCUMENT
  157 # ^uses $STRINGforOUTPUT
  158 # ^uses $STRINGforHEADER_TEXT
  159 # ^uses @PG_ANSWERS
  160 # ^uses $PG_STOP_FLAG
  161 # ^uses @PG_UNLABELED_ANSWERS
  162 # ^uses %PG_ANSWERS_HASH
  163 # ^uses @PG_ANSWER_ENTRY_ORDER
  164 # ^uses $ANSWER_PREFIX
  165 # ^uses %PG_FLAGS
  166 # ^uses $showPartialCorrectAnswers
  167 # ^uses $showHints
  168 # ^uses $solutionExists
  169 # ^uses $hintExists
  170 # ^uses $pgComment
  171 # ^uses %gifs_created
  172 # ^uses %envir
  173 # ^uses $refSubmittedAnswers
  174 # ^uses @submittedAnswers
  175 # ^uses $PG_original_problemSeed
  176 # ^uses $problemSeed
  177 # ^uses $PG_random_generator
  178 # ^uses $ans_rule_count
  179 # ^uses $QUIZ_PREFIX
  180 # (Also creates a package scalar named after each key in %envir containing a copy of the corresponding value.)
  181 # ^uses &PGrandom::new
  182 sub DOCUMENT {
  183 
  184   $STRINGforOUTPUT ="";
  185     $STRINGforHEADER_TEXT ="";
  186   @PG_ANSWERS=();
  187   $PG_STOP_FLAG=0;
  188   @PG_UNLABELED_ANSWERS = ();
  189   %PG_ANSWERS_HASH = ();
  190   # FIXME:  We are initializing these variables into both Safe::Root1 (the cached safe compartment)
  191   # and Safe::Root2 (the current one)
  192   # There is a good chance they won't be properly updated in one or the other of these compartments.
  193 
  194 
  195 #   @main::PG_ANSWER_ENTRY_ORDER = ();
  196 #   $main::ANSWER_PREFIX = 'AnSwEr';
  197 #   %main::PG_FLAGS=();  #global flags
  198 #   $main::showPartialCorrectAnswers = 0 unless defined($main::showPartialCorrectAnswers );
  199 #   $main::showHint = 1 unless defined($main::showHint);
  200 #   $main::solutionExists =0;
  201 #   $main::hintExists =0;
  202 #   %main::gifs_created = ();
  203   eval(q!
  204   # set perl to use capital E for scientific notation:  e.g.  5.4E-05 instead of 5.4e-05
  205   # $#="%G";  #FIXME  -- this causes bad warnings in perl 5.10
  206 
  207   @main::PG_ANSWER_ENTRY_ORDER = ();
  208   $main::ANSWER_PREFIX = 'AnSwEr';
  209   %main::PG_FLAGS=();  #global flags
  210   $main::showPartialCorrectAnswers = 0 unless defined($main::showPartialCorrectAnswers );
  211   $main::showHint = 1 unless defined($main::showHint);
  212   $main::solutionExists =0;
  213   $main::hintExists =0;
  214   $main::pgComment = '';
  215   %main::gifs_created = ();
  216 
  217     !);
  218 #    warn eval(q! "PG.pl:  The envir variable $main::{envir} is".join(" ",%main::envir)!);
  219     my $rh_envir = eval(q!\%main::envir!);
  220     my %envir    = %$rh_envir;
  221 
  222     # Save the file name for use in error messages
  223     my ($callpkg,$callfile) = caller(0);
  224     $envir{__files__}{$callfile} = $envir{templateDirectory}.$envir{fileName};
  225 
  226     #no strict;
  227     foreach  my  $var (keys %envir) {
  228       eval(q!$main::!.$var.q! = $main::envir{!.$var.q!}! );  #whew!! makes sure $var is interpolated but $main:: is evaluated at run time.
  229     #    warn eval(q! "var $var is defined ". $main::!.$var);
  230         warn "Problem defining ", q{\$main::}.$var, " while initializing the PG problem: $@" if $@;
  231     }
  232     #use strict;
  233     #FIXME these strict pragmas don't seem to be needed and they cause trouble in perl 5.6.0
  234 
  235 
  236 
  237     eval(q!
  238   @main::submittedAnswers = @{$main::refSubmittedAnswers} if defined($main::refSubmittedAnswers);
  239   $main::PG_original_problemSeed = $main::problemSeed;
  240   $main::PG_random_generator = new PGrandom($main::problemSeed) || die "Can't create random number generator.";
  241   $main::ans_rule_count = 0;  # counts questions
  242 
  243     # end unpacking of environment variables.
  244     $main::QUIZ_PREFIX = '' unless defined($main::QUIZ_PREFIX)
  245 
  246   !);
  247 #   @main::submittedAnswers = @{$main::refSubmittedAnswers} if defined($main::refSubmittedAnswers);
  248 #   $main::PG_original_problemSeed = $main::problemSeed;
  249 #   $main::PG_random_generator = new PGrandom($main::problemSeed) || die "Can't create random number generator.";
  250 #   $main::ans_rule_count = 0;  # counts questions
  251 
  252     # end unpacking of environment variables.
  253 #   $main::QUIZ_PREFIX = '' unless defined($main::QUIZ_PREFIX)
  254 
  255   if ($main::envir{displayMode} eq 'HTML_jsMath') {
  256     my $prefix = "";
  257     if (!$main::envir{jsMath}{reportMissingFonts}) {
  258       $prefix .= '<SCRIPT>noFontMessage = 1</SCRIPT>'."\n";
  259     } elsif ($main::envir{jsMath}{missingFontMessage}) {
  260       $prefix .= '<SCRIPT>missingFontMessage = "'.$main::envir{jsMath}{missingFontMessage}.'"</SCRIPT>'."\n";
  261     }
  262     $prefix .= '<SCRIPT>processDoubleClicks = '.($main::envir{jsMath}{processDoubleClicks}?'1':'0')."</SCRIPT>\n";
  263     $STRINGforOUTPUT =
  264       $prefix .
  265       '<SCRIPT SRC="'.$main::envir{jsMathURL}.'"></SCRIPT>' . "\n" .
  266       '<NOSCRIPT><CENTER><FONT COLOR="#CC0000">' .
  267       '<B>Warning: the mathematics on this page requires JavaScript.<BR>' .
  268       'If your browser supports it, be sure it is enabled.</B>'.
  269       '</FONT></CENTER><p></NOSCRIPT>' .
  270       $STRINGforOUTPUT;
  271     $STRINGforOUTPUT .=
  272       '<SCRIPT>jsMath.Setup.Script("plugins/noImageFonts.js")</SCRIPT>'
  273         if ($main::envir{jsMath}{noImageFonts});
  274   }
  275 
  276   $STRINGforOUTPUT = '<SCRIPT SRC="'.$main::envir{asciimathURL}.'"></SCRIPT>' . "\n" .
  277                            '<SCRIPT>mathcolor = "black"</SCRIPT>' . $STRINGforOUTPUT
  278     if ($main::envir{displayMode} eq 'HTML_asciimath');
  279 
  280   $STRINGforOUTPUT = '<SCRIPT SRC="'.$main::envir{LaTeXMathMLURL}.'"></SCRIPT>'."\n" . $STRINGforOUTPUT
  281     if ($main::envir{displayMode} eq 'HTML_LaTeXMathML');
  282 
  283 }
  284 
  285 =item HEADER_TEXT()
  286 
  287  HEADER_TEXT("string1", "string2", "string3");
  288 
  289 HEADER_TEXT() concatenates its arguments and appends them to the stored header
  290 text string. It can be used more than once in a file.
  291 
  292 The macro is used for material which is destined to be placed in the HEAD of
  293 the page when in HTML mode, such as JavaScript code.
  294 
  295 Spaces are placed between the arguments during concatenation, but no spaces are
  296 introduced between the existing content of the header text string and the new
  297 content being appended.
  298 
  299 =cut
  300 
  301 # ^function HEADER_TEXT
  302 # ^uses $STRINGforHEADER_TEXT
  303 sub HEADER_TEXT {
  304   my @in = @_;
  305   $STRINGforHEADER_TEXT .= join(" ",@in);
  306   }
  307 
  308 =item TEXT()
  309 
  310  TEXT("string1", "string2", "string3");
  311 
  312 TEXT() concatenates its arguments and appends them to the stored problem text
  313 string. It is used to define the text which will appear in the body of the
  314 problem. It can be used more than once in a file.
  315 
  316 This macro has no effect if rendering has been stopped with the STOP_RENDERING()
  317 macro.
  318 
  319 This macro defines text which will appear in the problem. All text must be
  320 passed to this macro, passed to another macro that calls this macro, or included
  321 in a BEGIN_TEXT/END_TEXT block, which uses this macro internally. No other
  322 statements in a PG file will directly appear in the output. Think of this as the
  323 "print" function for the PG language.
  324 
  325 Spaces are placed between the arguments during concatenation, but no spaces are
  326 introduced between the existing content of the header text string and the new
  327 content being appended.
  328 
  329 =cut
  330 
  331 # ^function TEXT
  332 # ^uses $PG_STOP_FLAG
  333 # ^uses $STRINGforOUTPUT
  334 sub TEXT {
  335   return "" if $PG_STOP_FLAG;
  336   my @in = @_;
  337   $STRINGforOUTPUT .= join(" ",@in);
  338 }
  339 
  340 =item ANS()
  341 
  342  TEXT(ans_rule(), ans_rule(), ans_rule());
  343  ANS($answer_evaluator1, $answer_evaluator2, $answer_evaluator3);
  344 
  345 Adds the answer evaluators listed to the list of unlabeled answer evaluators.
  346 They will be paired with unlabeled answer rules (a.k.a. answer blanks) in the
  347 order entered. This is the standard method for entering answers.
  348 
  349 In the above example, answer_evaluator1 will be associated with the first
  350 answer rule, answer_evaluator2 with the second, and answer_evaluator3 with the
  351 third. In practice, the arguments to ANS() will usually be calls to an answer
  352 evaluator generator such as the cmp() method of MathObjects or the num_cmp()
  353 macro in L<PGanswermacros.pl>.
  354 
  355 =cut
  356 
  357 # ^function ANS
  358 # ^uses $PG_STOP_FLAG
  359 # ^uses @PG_ANSWERS
  360 sub ANS{
  361   return "" if $PG_STOP_FLAG;
  362   my @in = @_;
  363   while (@in ) {
  364          warn("<BR><B>Error in ANS:$in[0]</B> -- inputs must be references to
  365                       subroutines<BR>")
  366       unless ref($in[0]);
  367       push(@PG_ANSWERS, shift @in );
  368   }
  369 }
  370 
  371 =item LABELED_ANS()
  372 
  373  TEXT(labeled_ans_rule("name1"), labeled_ans_rule("name2"));
  374  LABELED_ANS(name1 => answer_evaluator1, name2 => answer_evaluator2);
  375 
  376 Adds the answer evaluators listed to the list of labeled answer evaluators.
  377 They will be paired with labeled answer rules (a.k.a. answer blanks) in the
  378 order entered. This allows pairing of answer evaluators and answer rules that
  379 may not have been entered in the same order.
  380 
  381 =cut
  382 
  383 # ^function LABELED_ANS
  384 # ^uses &NAMED_ANS
  385 sub LABELED_ANS {
  386   &NAMED_ANS;
  387 }
  388 
  389 =item NAMED_ANS()
  390 
  391 Old name for LABELED_ANS(). DEPRECATED.
  392 
  393 =cut
  394 
  395 # ^function NAMED_ANS
  396 # ^uses $PG_STOP_FLAG
  397 sub NAMED_ANS{
  398   return "" if $PG_STOP_FLAG;
  399   my @in = @_;
  400   while (@in ) {
  401     my $label = shift @in;
  402     $label = eval(q!$main::QUIZ_PREFIX.$label!);
  403     my $ans_eval = shift @in;
  404     TEXT("<BR><B>Error in NAMED_ANS:$in[0]</B>
  405           -- inputs must be references to subroutines<BR>")
  406       unless ref($ans_eval);
  407     $PG_ANSWERS_HASH{$label}= $ans_eval;
  408   }
  409 }
  410 
  411 =item STOP_RENDERING()
  412 
  413  STOP_RENDERING() unless all_answers_are_correct();
  414 
  415 Temporarily suspends accumulation of problem text and storing of answer blanks
  416 and answer evaluators until RESUME_RENDERING() is called.
  417 
  418 =cut
  419 
  420 # ^function STOP_RENDERING
  421 # ^uses $PG_STOP_FLAG
  422 sub STOP_RENDERING {
  423   $PG_STOP_FLAG=1;
  424   "";
  425 }
  426 
  427 =item RESUME_RENDERING()
  428 
  429  RESUME_RENDERING();
  430 
  431 Resumes accumulating problem text and storing answer blanks and answer
  432 evaluators. Reverses the effect of STOP_RENDERING().
  433 
  434 =cut
  435 
  436 # ^function RESUME_RENDERING
  437 # ^uses $PG_STOP_FLAG
  438 sub RESUME_RENDERING {
  439   $PG_STOP_FLAG=0;
  440   "";
  441 }
  442 
  443 =item ENDDOCUMENT()
  444 
  445  ENDDOCUMENT();
  446 
  447 When PG problems are evaluated, the result of evaluating the entire problem is
  448 interpreted as the return value of ENDDOCUMENT(). Therefore, ENDDOCUMENT() must
  449 be the last executable statement of every problem. It can only appear once. It
  450 returns a list consisting of:
  451 
  452 =over
  453 
  454 =item *
  455 
  456 A reference to a string containing the rendered text of the problem.
  457 
  458 =item *
  459 
  460 A reference to a string containing text to be placed in the HEAD block
  461 when in and HTML-based mode (e.g. for JavaScript).
  462 
  463 =item *
  464 
  465 A reference to the hash mapping answer labels to answer evaluators.
  466 
  467 =item *
  468 
  469 A reference to a hash containing various flags:
  470 
  471 =over
  472 
  473 =item *
  474 
  475 C<showPartialCorrectAnswers>: determines whether students are told which of their answers in a problem are wrong.
  476 
  477 =item *
  478 
  479 C<recordSubmittedAnswers>: determines whether students submitted answers are saved.
  480 
  481 =item *
  482 
  483 C<refreshCachedImages>: determines whether the cached image of the problem in typeset mode is always refreshed
  484 (i.e. setting this to 1 means cached images are not used).
  485 
  486 =item *
  487 
  488 C<solutionExits>: indicates the existence of a solution.
  489 
  490 =item *
  491 
  492 C<hintExits>: indicates the existence of a hint.
  493 
  494 =item *
  495 
  496 C<comment>: contents of COMMENT commands if any.
  497 
  498 =item *
  499 
  500 C<showHintLimit>: determines the number of attempts after which hint(s) will be shown
  501 
  502 =item *
  503 
  504 C<PROBLEM_GRADER_TO_USE>: a reference to the chosen problem grader.
  505 ENDDOCUMENT chooses the problem grader as follows:
  506 
  507 =over
  508 
  509 =item *
  510 
  511 If a problem grader has been chosen in the problem by calling
  512 C<install_problem_grader(\&grader)>, it is used.
  513 
  514 =item *
  515 
  516 Otherwise, if the C<PROBLEM_GRADER_TO_USE> PG environment variable
  517 contains a reference to a subroutine, it is used.
  518 
  519 =item *
  520 
  521 Otherwise, if the C<PROBLEM_GRADER_TO_USE> PG environment variable
  522 contains the string C<std_problem_grader> or the string C<avg_problem_grader>,
  523 C<&std_problem_grader> or C<&avg_problem_grader> are used. These graders are defined
  524 in L<PGanswermacros.pl>.
  525 
  526 =item *
  527 
  528 Otherwise, the PROBLEM_GRADER_TO_USE flag will contain an empty value
  529 and the PG translator should select C<&std_problem_grader>.
  530 
  531 =back
  532 
  533 =back
  534 
  535 =back
  536 
  537 =cut
  538 
  539 # ^function ENDDOCUMENT
  540 # ^uses @PG_UNLABELED_ANSWERS
  541 # ^uses %PG_ANSWERS_HASH
  542 # ^uses @PG_ANSWERS
  543 sub ENDDOCUMENT {
  544 
  545     my $index=0;
  546     foreach my $label (@PG_UNLABELED_ANSWERS) {
  547         if ( defined($PG_ANSWERS[$index]) ) {
  548         $PG_ANSWERS_HASH{"$label"}= $PG_ANSWERS[$index];
  549       #warn "recording answer label = $label";
  550       } else {
  551         warn "No answer provided by instructor for answer $label";
  552       }
  553       $index++;
  554     }
  555 
  556     $STRINGforOUTPUT .="\n";
  557    eval q{  #make sure that "main" points to the current safe compartment by evaluating these lines.
  558     $main::PG_FLAGS{'showPartialCorrectAnswers'} = $main::showPartialCorrectAnswers;
  559     $main::PG_FLAGS{'recordSubmittedAnswers'} = $main::recordSubmittedAnswers;
  560     $main::PG_FLAGS{'refreshCachedImages'} = $main::refreshCachedImages;
  561     $main::PG_FLAGS{'comment'} = $main::pgComment;
  562     $main::PG_FLAGS{'hintExists'} = $main::hintExists;
  563     $main::PG_FLAGS{'showHintLimit'} = $main::showHint;
  564     $main::PG_FLAGS{'solutionExists'} = $main::solutionExists;
  565     $main::PG_FLAGS{ANSWER_ENTRY_ORDER} = \@main::PG_ANSWER_ENTRY_ORDER;
  566     $main::PG_FLAGS{KEPT_EXTRA_ANSWERS} = \@main::KEPT_EXTRA_ANSWERS;##need to keep array labels that don't call "RECORD_ANS_NAME"
  567     $main::PG_FLAGS{ANSWER_PREFIX} = $main::ANSWER_PREFIX;
  568     # install problem grader
  569     if (defined($main::PG_FLAGS{PROBLEM_GRADER_TO_USE}) ) {
  570       # problem grader defined within problem -- no further action needed
  571     } elsif ( defined( $main::envir{PROBLEM_GRADER_TO_USE} ) ) {
  572       if (ref($main::envir{PROBLEM_GRADER_TO_USE}) eq 'CODE' ) {         # user defined grader
  573         $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = $main::envir{PROBLEM_GRADER_TO_USE};
  574       } elsif ($main::envir{PROBLEM_GRADER_TO_USE} eq 'std_problem_grader' ) {
  575         if (defined(&std_problem_grader) ){
  576           $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&std_problem_grader; # defined in PGanswermacros.pl
  577         } # std_problem_grader is the default in any case so don't give a warning.
  578       } elsif ($main::envir{PROBLEM_GRADER_TO_USE} eq 'avg_problem_grader' ) {
  579         if (defined(&avg_problem_grader) ){
  580           $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&avg_problem_grader; # defined in PGanswermacros.pl
  581         }
  582         #else { # avg_problem_grader will be installed by PGtranslator so there is no need for a warning.
  583         # warn "The problem grader 'avg_problem_grader' has not been defined.  Has PGanswermacros.pl been loaded?";
  584         #}
  585       } else {
  586         warn "Error:  $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} is not a known program grader.";
  587       }
  588     } elsif (defined(&std_problem_grader)) {
  589       $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&std_problem_grader; # defined in PGanswermacros.pl
  590     } else {
  591       # PGtranslator will install its default problem grader
  592     }
  593 
  594     warn "ERROR: The problem grader is not a subroutine" unless ref( $main::PG_FLAGS{PROBLEM_GRADER_TO_USE}) eq 'CODE'
  595                      or $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = 'std_problem_grader'
  596                      or $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = 'avg_problem_grader';
  597      # return results
  598     };
  599 
  600     $STRINGforOUTPUT .= '<SCRIPT> jsMath.wwProcess() </SCRIPT>'
  601       if ($main::envir{displayMode} eq 'HTML_jsMath');
  602 
  603     if ($main::envir{displayMode} eq 'HTML_asciimath') {
  604       $STRINGforOUTPUT .= '<SCRIPT> translate() </SCRIPT>';
  605       $STRINGforHEADER_TEXT .=
  606         '<object id="mathplayer" classid="clsid:32F66A20-7614-11D4-BD11-00104BD3F987">' . "\n" .
  607         '</object><?import namespace="mml" implementation="#mathplayer"?>'
  608   unless ($STRINGforHEADER_TEXT =~ m/mathplayer/);
  609     }
  610   $STRINGforOUTPUT .= MODES(%{PG_restricted_eval('$main::problemPostamble')});
  611 
  612   (\$STRINGforOUTPUT, \$STRINGforHEADER_TEXT,\%PG_ANSWERS_HASH,eval(q!\%main::PG_FLAGS!));
  613 }
  614 
  615 
  616 ################################################################################
  617 
  618 =head1 PRIVATE MACROS
  619 
  620 These macros should only be used by other macro files. In practice, they are
  621 used exclusively by L<PGbasicmacros.pl>.
  622 
  623 =over
  624 
  625 =item inc_ans_rule_count()
  626 
  627  NEW_ANS_NAME(inc_ans_rule_count());
  628 
  629 Increments the internal count of the number of answer blanks that have been
  630 defined ($ans_rule_count) and returns the new count. This should only be used
  631 when one is about to define a new answer blank, for example with NEW_ANS_NAME().
  632 
  633 =cut
  634 
  635 # ^function inc_ans_rule_count
  636 # ^uses $ans_rule_count
  637 sub inc_ans_rule_count {
  638   eval(q!++$main::ans_rule_count!); # evalute at runtime to get correct main::
  639 }
  640 
  641 =item RECORD_ANS_NAME()
  642 
  643  RECORD_ANS_NAME("label");
  644 
  645 Records the label for an answer blank. Used internally by L<PGbasicmacros.pl>
  646 to record the order of explicitly-labelled answer blanks.
  647 
  648 =cut
  649 
  650 # ^function RECORD_ANS_NAME
  651 # ^uses $PG_STOP_FLAG
  652 # ^uses @PG_ANSWER_ENTRY_ORDER
  653 sub RECORD_ANS_NAME {
  654     return "" if $PG_STOP_FLAG;
  655   my $label = shift;
  656   eval(q!push(@main::PG_ANSWER_ENTRY_ORDER, $label)!);
  657   $label;
  658 }
  659 
  660 =item NEW_ANS_NAME()
  661 
  662  NEW_ANS_NAME($num);
  663 
  664 Generates an answer label from the supplied answer number. The label is
  665 added to the list of implicity-labeled answers. Used internally by
  666 L<PGbasicmacros.pl> to generate labels for unlabeled answer blanks.
  667 
  668 =cut
  669 
  670 # ^function NEW_ANS_NAME
  671 # ^uses $PG_STOP_FLAG
  672 # ^uses $QUIZ_PREFIX
  673 # ^uses $ANSWER_PREFIX
  674 # ^uses @PG_UNLABELED_ANSWERS
  675 sub NEW_ANS_NAME {
  676         return "" if $PG_STOP_FLAG;
  677     my $number=shift;
  678     my $prefix = eval(q!$main::QUIZ_PREFIX.$main::ANSWER_PREFIX!);
  679     my $label = $prefix.$number;
  680     push(@PG_UNLABELED_ANSWERS,$label);
  681     $label;
  682 }
  683 
  684 =item ANS_NUM_TO_NAME()
  685 
  686  ANS_NUM_TO_NAME($num);
  687 
  688 Generates an answer label from the supplied answer number, but does not add it
  689 to the list of inplicitly-labeled answers. Used internally by
  690 L<PGbasicmacros.pl> in generating answers blanks that use radio buttons or
  691 check boxes. (This type of answer blank uses multiple HTML INPUT elements with
  692 the same label, but the label should only be added to the list of implicitly-
  693 labeled answers once.)
  694 
  695 =cut
  696 
  697 # ^function ANS_NUM_TO_NAME
  698 # ^uses $QUIZ_PREFIX
  699 # ^uses $ANSWER_PREFIX
  700 sub ANS_NUM_TO_NAME {
  701     my $number=shift;
  702     my $label = eval(q!$main::QUIZ_PREFIX.$main::ANSWER_PREFIX!).$number;
  703     $label;
  704 }
  705 
  706 my $vecnum;
  707 
  708 =item RECORD_FROM_LABEL()
  709 
  710  RECORD_FORM_LABEL("label");
  711 
  712 Stores the label of a form field in the "extra" answers list. This is used to
  713 keep track of answer blanks that are not associated with an answer evaluator.
  714 
  715 =cut
  716 
  717 # ^function RECORD_FORM_LABEL
  718 # ^uses $PG_STOP_FLAG
  719 # ^uses @KEPT_EXTRA_ANSWERS
  720 sub RECORD_FORM_LABEL  {             # this stores form data (such as sticky answers), but does nothing more
  721                                      # it's a bit of hack since we are storing these in the KEPT_EXTRA_ANSWERS queue even if they aren't answers per se.
  722   return "" if $PG_STOP_FLAG;
  723   my $label   = shift;             # the label of the input box or textarea
  724     eval(q!push(@main::KEPT_EXTRA_ANSWERS, $label)!); #put the labels into the hash to be caught later for recording purposes
  725     $label;
  726 }
  727 
  728 =item NEW_ANS_ARRAY_NAME()
  729 
  730  NEW_ANS_ARRAY_NAME($num, $row, $col);
  731 
  732 Generates a new answer label for an array (vector) element and adds it to the
  733 list of implicitly-labeled answers.
  734 
  735 =cut
  736 
  737 # ^function NEW_ANS_ARRAY_NAME
  738 # ^uses $PG_STOP_FLAG
  739 # ^uses $QUIZ_PREFIX
  740 # ^uses @PG_UNLABELED_ANSWERS
  741 sub NEW_ANS_ARRAY_NAME {        # this keeps track of the answers which are entered implicitly,
  742                           # rather than with a specific label
  743         return "" if $PG_STOP_FLAG;
  744     my $number=shift;
  745     $vecnum = 0;
  746     my $row = shift;
  747     my $col = shift;
  748 #   my $label = "ArRaY"."$number"."["."$vecnum".","."$row".","."$col"."]";
  749     my $label = eval(q!$main::QUIZ_PREFIX."ArRaY"."$number"."__"."$vecnum".":"."$row".":"."$col"."__"!);
  750     push(@PG_UNLABELED_ANSWERS,$label);
  751     $label;
  752 }
  753 
  754 =item NEW_ANS_ARRAY_NAME_EXTENSION()
  755 
  756  NEW_ANS_ARRAY_NAME_EXTENSION($num, $row, $col);
  757 
  758 Generate an additional answer label for an existing array (vector) element and
  759 add it to the list of "extra" answers.
  760 
  761 =cut
  762 
  763 # ^function NEW_ANS_ARRAY_NAME_EXTENSION
  764 # ^uses $PG_STOP_FLAG
  765 sub NEW_ANS_ARRAY_NAME_EXTENSION {        # this keeps track of the answers which are entered implicitly,
  766                                           # rather than with a specific label
  767         return "" if $PG_STOP_FLAG;
  768     my $number=shift;
  769     my $row = shift;
  770     my $col = shift;
  771     if( $row == 0 && $col == 0 ){
  772       $vecnum += 1;
  773     }
  774     #FIXME   change made to conform to HTML 4.01 standards.  "Name" attributes can only contain
  775     # alphanumeric characters,   _ : and .
  776     # Also need to make corresponding changes in PGmorematrixmacros.  grep for ArRaY.
  777     #my $label = "ArRaY"."$number"."["."$vecnum".","."$row".","."$col"."]";
  778     my $label = eval(q!$main::QUIZ_PREFIX."ArRaY"."$number"."__"."$vecnum".":"."$row".":"."$col"."__"!);
  779     eval(q!push(@main::KEPT_EXTRA_ANSWERS, $label)!);#put the labels into the hash to be caught later for recording purposes
  780     $label;
  781 }
  782 
  783 =item get_PG_ANSWERS_HASH()
  784 
  785  get_PG_ANSWERS_HASH();
  786  get_PG_ANSWERS_HASH($key);
  787 
  788 
  789 
  790 =cut
  791 
  792 # ^function get_PG_ANSWERS_HASH
  793 # ^uses %PG_ANSWERS_HASH
  794 # ^uses @PG_UNLABELED_ANSWERS
  795 # ^uses @PG_ANSWERS
  796 sub get_PG_ANSWERS_HASH {
  797   # update the PG_ANSWWERS_HASH, then report the result.
  798   # This is used in writing sequential problems
  799   # if there is an input, use that as a key into the answer hash
  800   my $key = shift;
  801   my (%pg_answers_hash, @pg_unlabeled_answers);
  802   %pg_answers_hash= %PG_ANSWERS_HASH;
  803   #warn "order ", eval(q!@main::PG_ANSWER_ENTRY_ORDER!);
  804   #warn "pg answers", %PG_ANSWERS_HASH;
  805   #warn "unlabeled", @PG_UNLABELED_ANSWERS;
  806     my $index=0;
  807     foreach my $label (@PG_UNLABELED_ANSWERS) {
  808         if ( defined($PG_ANSWERS[$index]) ) {
  809         $pg_answers_hash{"$label"}= $PG_ANSWERS[$index];
  810       #warn "recording answer label = $label";
  811       } else {
  812         warn "No answer provided by instructor for answer $label";
  813       }
  814       $index++;
  815     }
  816     if ($key) {
  817       return $pg_answers_hash{$key};
  818     } else {
  819       return %pg_answers_hash;
  820     }
  821 }
  822 
  823 =item includePGproblem($filePath)
  824 
  825  includePGproblem($filePath);
  826 
  827  Essentially runs the pg problem specified by $filePath, which is
  828  a path relative to the top of the templates directory.  The output
  829  of that problem appears in the given problem.
  830 
  831 =cut
  832 
  833 # ^function includePGproblem
  834 # ^uses %envir
  835 # ^uses &read_whole_problem_file
  836 # ^uses &includePGtext
  837 sub includePGproblem {
  838     my $filePath = shift;
  839     my %save_envir = %main::envir;
  840     my $fullfilePath = $main::envir{templateDirectory}.$filePath;
  841     my $r_string =  read_whole_problem_file($fullfilePath);
  842     if (ref($r_string) eq 'SCALAR') {
  843         $r_string = $$r_string;
  844     }
  845 
  846   # The problem calling this should provide DOCUMENT and ENDDOCUMENT,
  847   # so we remove them from the included file.
  848     $r_string=~ s/^\s*(END)?DOCUMENT(\(\s*\));?//gm;
  849 
  850   # Reset the problem path so that static images can be found via
  851   # their relative paths.
  852     eval('$main::envir{probFileName} = $filePath');
  853     eval('$main::envir{fileName} = $filePath');
  854     includePGtext($r_string);
  855     # Reset the environment to what it is before.
  856     %main::envir = %save_envir;
  857 }
  858 
  859 
  860 =back
  861 
  862 =head1 SEE ALSO
  863 
  864 L<PGbasicmacros.pl>, L<PGanswermacros.pl>.
  865 
  866 =cut
  867 
  868 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9