[system] / trunk / webwork-modperl / lib / WeBWorK / PG / Local.pm Repository:
ViewVC logotype

View of /trunk/webwork-modperl/lib/WeBWorK/PG/Local.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3224 - (download) (as text) (annotate)
Sat May 14 01:32:19 2005 UTC (8 years, 1 month ago) by dpvc
File size: 13951 byte(s)
Changes needed to make loadMacros() look through a path of directories
rather than just course/templates/macros and pg/macros.  You can
specify the path in the global.conf file.

You also need to make the update to pg/macros/dangerousMacros.pl

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9