[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 5626 - (download) (as text) (annotate)
Wed Mar 26 02:43:07 2008 UTC (11 years, 9 months ago) by gage
File size: 25426 byte(s)
Fixed bug in base64Q subroutine

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9