[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 5442 - (download) (as text) (annotate)
Tue Aug 28 23:08:44 2007 UTC (12 years, 5 months ago) by gage
File size: 25285 byte(s)
Backporting changes from HEAD to rel-2-4-dev

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9