[system] / trunk / webwork2 / lib / WeBWorK / PG / Local.pm Repository:
ViewVC logotype

View of /trunk/webwork2/lib/WeBWorK/PG/Local.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1563 - (download) (as text) (annotate)
Sun Oct 5 22:45:38 2003 UTC (9 years, 7 months ago) by gage
File size: 11959 byte(s)
Fixed a reference to the nullSafety filter
--Mike

    1 ################################################################################
    2 # WeBWorK mod_perl (c) 2000-2002 WeBWorK Project
    3 # $Id$
    4 ################################################################################
    5 
    6 package WeBWorK::PG::Local;
    7 use base qw(WeBWorK::PG);
    8 
    9 =head1 NAME
   10 
   11 WeBWorK::PG::Local - Use the WeBWorK::PG API to invoke a local
   12 WeBWorK::PG::Translator object.
   13 
   14 =head1 DESCRIPTION
   15 
   16 WeBWorK::PG::Local encapsulates the PG translation process, making multiple
   17 calls to WeBWorK::PG::Translator. Much of the flexibility of the Translator is
   18 hidden, instead making choices that are appropriate for the webwork-modperl
   19 system
   20 
   21 It implements the WeBWorK::PG interface and uses a local
   22 WeBWorK::PG::Translator to perform problem rendering. See the documentation for
   23 the WeBWorK::PG module for information about the API.
   24 
   25 =cut
   26 
   27 use strict;
   28 use warnings;
   29 use File::Path qw(rmtree);
   30 use WeBWorK::PG::Translator;
   31 use WeBWorK::Utils qw(readFile writeTimingLogEntry);
   32 
   33 BEGIN {
   34   # This safe compartment is used to read the large macro files such as
   35   # PG.pl, PGbasicmacros.pl and PGanswermacros and cache the results so that
   36   # future calls have preloaded versions of these large files. This saves a
   37   # significant amount of time.
   38   $WeBWorK::PG::Local::safeCache = new Safe;
   39 }
   40 
   41 sub new {
   42   my $invocant = shift;
   43   my $class = ref($invocant) || $invocant;
   44   my (
   45     $ce,
   46     $user,
   47     $key,
   48     $set,
   49     $problem,
   50     $psvn,
   51     $formFields, # in CGI::Vars format
   52     $translationOptions, # hashref containing options for the
   53                          # translator, such as whether to show
   54                          # hints and the display mode to use
   55   ) = @_;
   56 
   57   # write timing log entry
   58   writeTimingLogEntry($ce, "WeBWorK::PG::new",
   59     "user=".$user->user_id.",problem=".$ce->{courseName}."/".$set->set_id."/".$problem->problem_id.",mode=".$translationOptions->{displayMode},
   60     "begin");
   61 
   62   # install a local warn handler to collect warnings
   63   my $warnings = "";
   64   local $SIG{__WARN__} = sub { $warnings .= shift }
   65     if $ce->{pg}->{options}->{catchWarnings};
   66 
   67   # create a Translator
   68   #warn "PG: creating a Translator\n";
   69   my $translator = WeBWorK::PG::Translator->new;
   70 
   71   # set the directory hash
   72   #warn "PG: setting the directory hash\n";
   73   $translator->rh_directories({
   74     courseScriptsDirectory => $ce->{pg}->{directories}->{macros},
   75     macroDirectory         => $ce->{courseDirs}->{macros},
   76     templateDirectory      => $ce->{courseDirs}->{templates},
   77     tempDirectory          => $ce->{courseDirs}->{html_temp},
   78   });
   79 
   80   # evaluate modules and "extra packages"
   81   #warn "PG: evaluating modules and \"extra packages\"\n";
   82   my @modules = @{ $ce->{pg}->{modules} };
   83   foreach my $module_packages_ref (@modules) {
   84     my ($module, @extra_packages) = @$module_packages_ref;
   85     # the first item is the main package
   86     $translator->evaluate_modules($module);
   87     # the remaining items are "extra" packages
   88     $translator->load_extra_packages(@extra_packages);
   89   }
   90 
   91   # set the environment (from defineProblemEnvir)
   92   #warn "PG: setting the environment (from defineProblemEnvir)\n";
   93   my $envir = $class->defineProblemEnvir(
   94     $ce,
   95     $user,
   96     $key,
   97     $set,
   98     $problem,
   99     $psvn,
  100     $formFields,
  101     $translationOptions,
  102   );
  103   $translator->environment($envir);
  104 
  105   # initialize the Translator
  106   #warn "PG: initializing the Translator\n";
  107   $translator->initialize();
  108 
  109   # Preload the macros files which are used routinely: PG.pl,
  110   # dangerousMacros.pl, IO.pl, PGbasicmacros.pl, and PGanswermacros.pl
  111   # (Preloading the last two files safes a significant amount of time.)
  112   #
  113   # IO.pl, PG.pl, and dangerousMacros.pl are loaded using
  114   # unrestricted_load This is hard wired into the
  115   # Translator::pre_load_macro_files subroutine. I'd like to change this
  116   # at some point to have the same sort of interface to global.conf that
  117   # the module loading does -- have a list of macros to load
  118   # unrestrictedly.
  119   #
  120   # This has been replaced by the pre_load_macro_files subroutine.  It
  121   # loads AND caches the files. While PG.pl and dangerousMacros are not
  122   # large, they are referred to by PGbasicmacros and PGanswermacros.
  123   # Because these are loaded into the cached name space (e.g.
  124   # Safe::Root1::) all calls to, say NEW_ANSWER_NAME are actually calls
  125   # to Safe::Root1::NEW_ANSWER_NAME.  It is useful to have these names
  126   # inside the Safe::Root1: cached safe compartment.  (NEW_ANSWER_NAME
  127   # and all other subroutine names are also automatically exported into
  128   # the current safe compartment Safe::Rootx::
  129   #
  130   # The headers of both PGbasicmacros and PGanswermacros has code that
  131   # insures that the constants used are imported into the current safe
  132   # compartment.  This involves evaluating references to, say
  133   # $main::displayMode, at runtime to insure that main refers to
  134   # Safe::Rootx:: and NOT to Safe::Root1::, which is the value of main::
  135   # at compile time.
  136   #
  137   # TO ENABLE CACHEING UNCOMMENT THE FOLLOWING:
  138   eval{$translator->pre_load_macro_files(
  139     $WeBWorK::PG::Local::safeCache,
  140     $ce->{pg}->{directories}->{macros},
  141     'PG.pl', 'dangerousMacros.pl','IO.pl','PGbasicmacros.pl','PGanswermacros.pl'
  142   )};
  143     warn "Error while preloading macro files: $@" if $@;
  144 
  145   # STANDARD LOADING CODE: for cached script files, this merely
  146   # initializes the constants.
  147   foreach (qw(PG.pl dangerousMacros.pl IO.pl)) {
  148     my $macroPath = $ce->{pg}->{directories}->{macros} . "/$_";
  149     my $err = $translator->unrestricted_load($macroPath);
  150     warn "Error while loading $macroPath: $err" if $err;
  151   }
  152 
  153   # set the opcode mask (using default values)
  154   #warn "PG: setting the opcode mask (using default values)\n";
  155   $translator->set_mask();
  156 
  157   # store the problem source
  158   #warn "PG: storing the problem source\n";
  159   my $sourceFile = $problem->source_file;
  160   $sourceFile = $ce->{courseDirs}->{templates}."/".$sourceFile
  161     unless ($sourceFile =~ /^\//);
  162   eval { $translator->source_string(readFile($sourceFile)) };
  163   if ($@) {
  164     # well, we couldn't get the problem source, for some reason.
  165     return bless {
  166       translator => $translator,
  167       head_text  => "",
  168       body_text  => <<EOF,
  169 WeBWorK::Utils::readFile($sourceFile) says:
  170 $@
  171 EOF
  172       answers    => {},
  173       result     => {},
  174       state      => {},
  175       errors     => "Failed to read the problem source file.",
  176       warnings   => $warnings,
  177       flags      => {error_flag => 1},
  178     }, $class;
  179   }
  180 
  181   # install a safety filter
  182   #warn "PG: installing a safety filter\n";
  183   #$translator->rf_safety_filter(\&oldSafetyFilter);
  184   $translator->rf_safety_filter(\&WeBWorK::PG::nullSafetyFilter);
  185 
  186   # write timing log entry -- the translator is now all set up
  187   writeTimingLogEntry($ce, "WeBWorK::PG::new",
  188     "initialized",
  189     "intermediate");
  190 
  191   # translate the PG source into text
  192   #warn "PG: translating the PG source into text\n";
  193   $translator->translate();
  194 
  195   # after we're done translating, we may have to clean up after the
  196   # translator:
  197 
  198   # for example, HTML_img mode uses a tempdir for dvipng's temp files.\
  199   # We have to remove it.
  200   if ($envir->{dvipngTempDir}) {
  201     rmtree($envir->{dvipngTempDir}, 0, 0);
  202   }
  203 
  204   # HTML_dpng, on the other hand, uses an ImageGenerator. We have to
  205   # render the queued equations.
  206   if ($envir->{imagegen}) {
  207     my $sourceFile = $ce->{courseDirs}->{templates} . "/" . $problem->source_file;
  208     my %mtimeOption = -e $sourceFile
  209       ? (mtime => (stat $sourceFile)[9])
  210       : ();
  211 
  212     $envir->{imagegen}->render(
  213       refresh => $translationOptions->{refreshMath2img},
  214       %mtimeOption,
  215     );
  216   }
  217 
  218   my ($result, $state); # we'll need these on the other side of the if block!
  219   if ($translationOptions->{processAnswers}) {
  220 
  221     # process student answers
  222     #warn "PG: processing student answers\n";
  223     $translator->process_answers($formFields);
  224 
  225     # retrieve the problem state and give it to the translator
  226     #warn "PG: retrieving the problem state and giving it to the translator\n";
  227     $translator->rh_problem_state({
  228       recorded_score =>       $problem->status,
  229       num_of_correct_ans =>   $problem->num_correct,
  230       num_of_incorrect_ans => $problem->num_incorrect,
  231     });
  232 
  233     # determine an entry order -- the ANSWER_ENTRY_ORDER flag is built by
  234     # the PG macro package (PG.pl)
  235     #warn "PG: determining an entry order\n";
  236     my @answerOrder =
  237       $translator->rh_flags->{ANSWER_ENTRY_ORDER}
  238         ? @{ $translator->rh_flags->{ANSWER_ENTRY_ORDER} }
  239         : keys %{ $translator->rh_evaluated_answers };
  240 
  241     # install a grader -- use the one specified in the problem,
  242     # or fall back on the default from the course environment.
  243     # (two magic strings are accepted, to avoid having to
  244     # reference code when it would be difficult.)
  245     #warn "PG: installing a grader\n";
  246     my $grader = $translator->rh_flags->{PROBLEM_GRADER_TO_USE}
  247       || $ce->{pg}->{options}->{grader};
  248     $grader = $translator->rf_std_problem_grader
  249       if $grader eq "std_problem_grader";
  250     $grader = $translator->rf_avg_problem_grader
  251       if $grader eq "avg_problem_grader";
  252     die "Problem grader $grader is not a CODE reference."
  253       unless ref $grader eq "CODE";
  254     $translator->rf_problem_grader($grader);
  255 
  256     # grade the problem
  257     #warn "PG: grading the problem\n";
  258     ($result, $state) = $translator->grade_problem(
  259       answers_submitted  => $translationOptions->{processAnswers},
  260       ANSWER_ENTRY_ORDER => \@answerOrder,
  261     );
  262 
  263   }
  264 
  265   # write timing log entry
  266   writeTimingLogEntry($ce, "WeBWorK::PG::new", "", "end");
  267 
  268   # return an object which contains the translator and the results of
  269   # the translation process. this is DIFFERENT from the "format expected
  270   # by Webwork.pm (and I believe processProblem8, but check.)"
  271   return bless {
  272     translator => $translator,
  273     head_text  => ${ $translator->r_header },
  274     body_text  => ${ $translator->r_text   },
  275     answers    => $translator->rh_evaluated_answers,
  276     result     => $result,
  277     state      => $state,
  278     errors     => $translator->errors,
  279     warnings   => $warnings,
  280     flags      => $translator->rh_flags,
  281   }, $class;
  282 }
  283 
  284 1;
  285 
  286 __END__
  287 
  288 =head1 OPERATION
  289 
  290 WeBWorK::PG::Local goes through the following operations when constructed:
  291 
  292 =over
  293 
  294 =item Create a translator
  295 
  296 Instantiate a WeBWorK::PG::Translator object.
  297 
  298 =item Set the directory hash
  299 
  300 Set the translator's directory hash (courseScripts, macros, templates, and temp
  301 directories) from the course environment.
  302 
  303 =item Evaluate PG modules
  304 
  305 Using the module list from the course environment (pg->modules), perform a
  306 "use"-like operation to evaluate modules at runtime.
  307 
  308 =item Set the problem environment
  309 
  310 Use data from the user, set, and problem, as well as the course
  311 environemnt and translation options, to set the problem environment. The
  312 default subroutine, &WeBWorK::PG::defineProblemEnvir, is used.
  313 
  314 =item Initialize the translator
  315 
  316 Call &WeBWorK::PG::Translator::initialize. What more do you want?
  317 
  318 =item Load IO.pl, PG.pl and dangerousMacros.pl
  319 
  320 These macros must be loaded without opcode masking, so they are loaded here.
  321 
  322 =item Set the opcode mask
  323 
  324 Set the opcode mask to the default specified by WeBWorK::PG::Translator.
  325 
  326 =item Load the problem source
  327 
  328 Give the problem source to the translator.
  329 
  330 =item Install a safety filter
  331 
  332 The safety filter is used to preprocess student input before evaluation. The
  333 default safety filter, &WeBWorK::PG::safetyFilter, is used.
  334 
  335 =item Translate the problem source
  336 
  337 Call &WeBWorK::PG::Translator::translate to render the problem source into the
  338 format given by the display mode.
  339 
  340 =item Process student answers
  341 
  342 Use form field inputs to evaluate student answers.
  343 
  344 =item Load the problem state
  345 
  346 Use values from the database to initialize the problem state, so that the
  347 grader will have a point of reference.
  348 
  349 =item Determine an entry order
  350 
  351 Use the ANSWER_ENTRY_ORDER flag to determine the order of answers in the
  352 problem. This is important for problems with dependancies among parts.
  353 
  354 =item Install a grader
  355 
  356 Use the PROBLEM_GRADER_TO_USE flag, or a default from the course environment,
  357 to install a grader.
  358 
  359 =item Grade the problem
  360 
  361 Use the selected grader to grade the problem.
  362 
  363 =back
  364 
  365 =head1 AUTHOR
  366 
  367 Written by Sam Hathaway, sh002i (at) math.rochester.edu.
  368 
  369 =cut

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9