[system] / branches / rel-2-4-patches / webwork2 / lib / WeBWorK / PG.pm Repository:
ViewVC logotype

View of /branches/rel-2-4-patches/webwork2/lib/WeBWorK/PG.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 612 - (download) (as text) (annotate)
Mon Oct 28 21:21:26 2002 UTC (10 years, 6 months ago) by sh002i
Original Path: trunk/webwork2/lib/WeBWorK/PG.pm
File size: 18749 byte(s)
added texDisposition problem environment variable -- always set to "pdf"
cleaned up unrestricted_load code, made IO.pl get loaded first
-sam

    1 ################################################################################
    2 # WeBWorK mod_perl (c) 2000-2002 WeBWorK Project
    3 # $Id$
    4 ################################################################################
    5 
    6 package WeBWorK::PG;
    7 
    8 =head1 NAME
    9 
   10 WeBWorK::PG - Wrap the action of the PG Translator in an easy-to-use API.
   11 
   12 =cut
   13 
   14 use strict;
   15 use warnings;
   16 use File::Path qw(rmtree);
   17 use File::Temp qw(tempdir);
   18 use WeBWorK::DB::Classlist;
   19 use WeBWorK::DB::WW;
   20 use WeBWorK::PG::Translator;
   21 use WeBWorK::Problem;
   22 use WeBWorK::Utils qw(readFile formatDateTime writeTimingLogEntry);
   23 
   24 sub new($$$$$$$$) {
   25   my $invocant = shift;
   26   my $class = ref($invocant) || $invocant;
   27   my (
   28     $courseEnv,
   29     $user,
   30     $key,
   31     $set,
   32     $problem,
   33     $psvn,
   34     $formFields, # in CGI::Vars format
   35     $translationOptions, # hashref containing options for the
   36                          # translator, such as whether to show
   37              # hints and the display mode to use
   38   ) = @_;
   39 
   40   # write timing log entry
   41   writeTimingLogEntry($courseEnv, "WeBWorK::PG::new",
   42     "user=".$user->id.",problem=".$courseEnv->{courseName}."/".$set->id."/".$problem->id.",mode=".$translationOptions->{displayMode},
   43     "begin");
   44 
   45   # install a local warn handler to collect warnings
   46   my $warnings = "";
   47   if ($courseEnv->{pg}->{options}->{catchWarnings}) {
   48     warn "PG: about to install a warnings handler...\n"; # ***
   49     local $SIG{__WARN__} = sub { $warnings .= shift };
   50   }
   51 
   52   # create a Translator
   53   #warn "PG: creating a Translator\n";
   54   my $translator = WeBWorK::PG::Translator->new;
   55 
   56   # set the directory hash
   57   #warn "PG: setting the directory hash\n";
   58   $translator->rh_directories({
   59     courseScriptsDirectory => $courseEnv->{webworkDirs}->{macros},
   60     macroDirectory         => $courseEnv->{courseDirs}->{macros},
   61     templateDirectory      => $courseEnv->{courseDirs}->{templates},
   62     tempDirectory          => $courseEnv->{courseDirs}->{html_temp},
   63   });
   64 
   65   # evaluate modules and "extra packages"
   66   #warn "PG: evaluating modules and \"extra packages\"\n";
   67   my @modules = @{ $courseEnv->{pg}->{modules} };
   68   foreach my $module_packages_ref (@modules) {
   69     my ($module, @extra_packages) = @$module_packages_ref;
   70     # the first item is the main package
   71     $translator->evaluate_modules($module);
   72     # the remaining items are "extra" packages
   73     $translator->load_extra_packages(@extra_packages);
   74   }
   75 
   76   # set the environment (from defineProblemEnvir)
   77   #warn "PG: setting the environment (from defineProblemEnvir)\n";
   78   my $envir = defineProblemEnvir(
   79     $courseEnv,
   80     $user,
   81     $key,
   82     $set,
   83     $problem,
   84     $psvn,
   85     $formFields,
   86     $translationOptions,
   87   );
   88   $translator->environment($envir);
   89 
   90   # initialize the Translator
   91   #warn "PG: initializing the Translator\n";
   92   $translator->initialize();
   93 
   94   # load IO.pl, PG.pl, and dangerousMacros.pl using unrestricted_load
   95   # i'd like to change this at some point to have the same sort of interface to global.conf
   96   # that the module loading does -- have a list of macros to load unrestrictedly.
   97   #warn "PG: loading IO.pl, PG.pl, and dangerousMacros.pl using unrestricted_load\n";
   98   foreach (qw(IO.pl PG.pl dangerousMacros.pl)) {
   99     my $macroPath = $courseEnv->{webworkDirs}->{macros} . "/$_";
  100     my $err = $translator->unrestricted_load($macroPath);
  101     warn "Error while loading $macroPath: $err" if $err;
  102   }
  103   #my $pg_pl              = $courseEnv->{webworkDirs}->{macros} . "/PG.pl";
  104   #my $dangerousMacros_pl = $courseEnv->{webworkDirs}->{macros} . "/dangerousMacros.pl";
  105   #my $io_pl              = $courseEnv->{webworkDirs}->{macros} . "/IO.pl";
  106   #my $err = $translator->unrestricted_load($pg_pl);
  107   #warn "Error while loading $pg_pl: $err" if $err;
  108   #$err = $translator->unrestricted_load($dangerousMacros_pl);
  109   #warn "Error while loading $dangerousMacros_pl: $err" if $err;
  110   #$err = $translator->unrestricted_load($io_pl);
  111   #warn "Error while loading $io_pl: $err" if $err;
  112 
  113   # set the opcode mask (using default values)
  114   #warn "PG: setting the opcode mask (using default values)\n";
  115   $translator->set_mask();
  116 
  117   # store the problem source
  118   #warn "PG: storing the problem source\n";
  119   my $sourceFile = $problem->source_file;
  120   $sourceFile = $courseEnv->{courseDirs}->{templates}."/".$sourceFile
  121     unless ($sourceFile =~ /^\//);
  122   eval { $translator->source_string(readFile($sourceFile)) };
  123   if ($@) {
  124     # well, we couldn't get the problem source, for some reason.
  125     return bless {
  126       translator => $translator,
  127       head_text  => "",
  128       body_text  => <<EOF,
  129 WeBWorK::Utils::readFile($sourceFile) says:
  130 $@
  131 EOF
  132       answers    => {},
  133       result     => {},
  134       state      => {},
  135       errors     => "Failed to read the problem source file.",
  136       warnings   => $warnings,
  137       flags      => {error_flag => 1},
  138     }, $class;
  139   }
  140 
  141   # install a safety filter (&safetyFilter)
  142   #warn "PG: installing a safety filter\n";
  143   $translator->rf_safety_filter(\&safetyFilter);
  144 
  145   # translate the PG source into text
  146   #warn "PG: translating the PG source into text\n";
  147   $translator->translate();
  148 
  149   # after we're done translating, we may have to clean up after the translator.
  150   # for example, 'images' mode uses a tempdir for dvipng's temp files. We have
  151   # to remove it.
  152   if ($translationOptions->{displayMode} eq 'images' && $envir->{dvipngTempDir}) {
  153     rmtree($envir->{dvipngTempDir}, 0, 0);
  154   }
  155 
  156   my ($result, $state); # we'll need these on the other side of the if block!
  157   if ($translationOptions->{processAnswers}) {
  158 
  159     # process student answers
  160     #warn "PG: processing student answers\n";
  161     $translator->process_answers($formFields);
  162 
  163     # retrieve the problem state and give it to the translator
  164     #warn "PG: retrieving the problem state and giving it to the translator\n";
  165     $translator->rh_problem_state({
  166       recorded_score =>       $problem->status,
  167       num_of_correct_ans =>   $problem->num_correct,
  168       num_of_incorrect_ans => $problem->num_incorrect,
  169     });
  170 
  171     # determine an entry order -- the ANSWER_ENTRY_ORDER flag is built by
  172     # the PG macro package (PG.pl)
  173     #warn "PG: determining an entry order\n";
  174     my @answerOrder =
  175       $translator->rh_flags->{ANSWER_ENTRY_ORDER}
  176         ? @{ $translator->rh_flags->{ANSWER_ENTRY_ORDER} }
  177         : keys %{ $translator->rh_evaluated_answers };
  178 
  179     # install a grader -- use the one specified in the problem,
  180     # or fall back on the default from the course environment.
  181     # (two magic strings are accepted, to avoid having to
  182     # reference code when it would be difficult.)
  183     #warn "PG: installing a grader\n";
  184     my $grader = $translator->rh_flags->{PROBLEM_GRADER_TO_USE}
  185       || $courseEnv->{pg}->{options}->{grader};
  186     $grader = $translator->rf_std_problem_grader
  187       if $grader eq "std_problem_grader";
  188     $grader = $translator->rf_avg_problem_grader
  189       if $grader eq "avg_problem_grader";
  190     die "Problem grader $grader is not a CODE reference."
  191       unless ref $grader eq "CODE";
  192     $translator->rf_problem_grader($grader);
  193 
  194     # grade the problem
  195     #warn "PG: grading the problem\n";
  196     ($result, $state) = $translator->grade_problem(
  197       answers_submitted  => $translationOptions->{processAnswers},
  198       ANSWER_ENTRY_ORDER => \@answerOrder,
  199     );
  200 
  201   }
  202 
  203   # write timing log entry
  204   writeTimingLogEntry($courseEnv, "WeBWorK::PG::new", "", "end");
  205 
  206   # return an object which contains the translator and the results of
  207   # the translation process. this is DIFFERENT from the "format expected
  208   # by Webwork.pm (and I believe processProblem8, but check.)"
  209   return bless {
  210     translator => $translator,
  211     head_text  => ${ $translator->r_header },
  212     body_text  => ${ $translator->r_text   },
  213     answers    => $translator->rh_evaluated_answers,
  214     result     => $result,
  215     state      => $state,
  216     errors     => $translator->errors,
  217     warnings   => $warnings,
  218     flags      => $translator->rh_flags,
  219   }, $class;
  220 }
  221 
  222 # -----
  223 
  224 sub defineProblemEnvir($$$$$$$) {
  225   my (
  226     $courseEnv,
  227     $user,
  228     $key,
  229     $set,
  230     $problem,
  231     $psvn,
  232     $formFields,
  233     $options,
  234   ) = @_;
  235 
  236   my %envir;
  237 
  238   # PG environment variables
  239   # from docs/pglanguage/pgreference/environmentvariables as of 06/25/2002
  240   # any changes are noted by "ADDED:" or "REMOVED:"
  241 
  242   # Vital state information
  243   # ADDED: displayHintsQ, displaySolutionsQ, refreshMath2img,
  244   #        texDisposition
  245 
  246   $envir{psvn}              = $psvn;
  247   $envir{psvnNumber}        = $envir{psvn};
  248   $envir{probNum}           = $problem->id;
  249   $envir{questionNumber}    = $envir{probNum};
  250   $envir{fileName}          = $problem->source_file;
  251   $envir{probFileName}      = $envir{fileName};
  252   $envir{problemSeed}       = $problem->problem_seed;
  253   $envir{displayMode}       = translateDisplayModeNames($options->{displayMode});
  254   $envir{languageMode}      = $envir{displayMode};
  255   $envir{outputMode}        = $envir{displayMode};
  256   $envir{displayHintsQ}     = $options->{hints};
  257   $envir{displaySolutionsQ} = $options->{solutions};
  258   $envir{refreshMath2img}   = $options->{refreshMath2img};
  259   $envir{texDisposition}    = "pdf"; # in webwork-modperl, we use pdflatex
  260 
  261   # Problem Information
  262   # ADDED: courseName
  263 
  264   $envir{openDate}            = $set->open_date;
  265   $envir{formattedOpenDate}   = formatDateTime($envir{openDate});
  266   $envir{dueDate}             = $set->due_date;
  267   $envir{formattedDueDate}    = formatDateTime($envir{dueDate});
  268   $envir{answerDate}          = $set->answer_date;
  269   $envir{formattedAnswerDate} = formatDateTime($envir{answerDate});
  270   $envir{numOfAttempts}       = ($problem->num_correct || 0) + ($problem->num_incorrect || 0);
  271   $envir{problemValue}        = $problem->value;
  272   $envir{sessionKey}          = $key;
  273   $envir{courseName}          = $courseEnv->{courseName};
  274 
  275   # Student Information
  276   # ADDED: studentID
  277 
  278   $envir{sectionName}      = $user->section;
  279   $envir{sectionNumber}    = $envir{sectionName};
  280   $envir{recitationName}   = $user->recitation;
  281   $envir{recitationNumber} = $envir{recitationName};
  282   $envir{setNumber}        = $set->id;
  283   $envir{studentLogin}     = $user->id;
  284   $envir{studentName}      = $user->first_name . " " . $user->last_name;
  285   $envir{studentID}        = $user->student_id;
  286 
  287   # Answer Information
  288   # REMOVED: refSubmittedAnswers
  289 
  290   $envir{inputs_ref} = $formFields;
  291 
  292   # External Programs
  293   # ADDED: externalLaTeXPath, externalDvipngPath,
  294   #        externalGif2EpsPath, externalPng2EpsPath
  295 
  296   $envir{externalTTHPath}      = $courseEnv->{externalPrograms}->{tth};
  297   $envir{externalLaTeXPath}    = $courseEnv->{externalPrograms}->{latex};
  298   $envir{externalDvipngPath}   = $courseEnv->{externalPrograms}->{dvipng};
  299   $envir{externalGif2EpsPath}  = $courseEnv->{externalPrograms}->{gif2eps};
  300   $envir{externalPng2EpsPath}  = $courseEnv->{externalPrograms}->{png2eps};
  301   $envir{externalGif2PngPath}  = $courseEnv->{externalPrograms}->{gif2png};
  302 
  303   # Directories and URLs
  304   # REMOVED: courseName
  305   # ADDED: dvipngTempDir
  306 
  307   $envir{cgiDirectory}           = undef;
  308   $envir{cgiURL}                 = undef;
  309   $envir{classDirectory}         = undef;
  310   $envir{courseScriptsDirectory} = $courseEnv->{webworkDirs}->{macros}."/";
  311   $envir{htmlDirectory}          = $courseEnv->{courseDirs}->{html}."/";
  312   $envir{htmlURL}                = $courseEnv->{courseURLs}->{html}."/";
  313   $envir{macroDirectory}         = $courseEnv->{courseDirs}->{macros}."/";
  314   $envir{templateDirectory}      = $courseEnv->{courseDirs}->{templates}."/";
  315   $envir{tempDirectory}          = $courseEnv->{courseDirs}->{html_temp}."/";
  316   $envir{tempURL}                = $courseEnv->{courseURLs}->{html_temp}."/";
  317   $envir{scriptDirectory}        = undef;
  318   $envir{webworkDocsURL}         = $courseEnv->{webworkURLs}->{docs}."/";
  319   $envir{dvipngTempDir}          = $options->{displayMode} eq 'images'
  320     ? tempdir("webwork-dvipng-XXXXXXXX", DIR => $envir{tempDirectory})
  321     : undef;
  322 
  323   # Default values for evaluating answers
  324 
  325   my $ansEvalDefaults = $courseEnv->{pg}->{ansEvalDefaults};
  326   $envir{$_} = $ansEvalDefaults->{$_} foreach (keys %$ansEvalDefaults);
  327 
  328   # Other things...
  329 
  330   $envir{PROBLEM_GRADER_TO_USE} = $courseEnv->{pg}->{options}->{grader};
  331 
  332   return \%envir;
  333 }
  334 
  335 sub translateDisplayModeNames($) {
  336   my $name = shift;
  337   return {
  338     tex           => "TeX",
  339     plainText     => "HTML",
  340     formattedText => "HTML_tth",
  341     images        => "HTML_img"
  342   }->{$name};
  343 }
  344 
  345 sub safetyFilter {
  346   my $answer = shift; # accepts one answer and checks it
  347   my $submittedAnswer = $answer;
  348   $answer = '' unless defined $answer;
  349   my ($errorno);
  350   $answer =~ tr/\000-\037/ /;
  351   # Return if answer field is empty
  352   unless ($answer =~ /\S/) {
  353     #$errorno = "<BR>No answer was submitted.";
  354     $errorno = 0;  ## don't report blank answer as error
  355     return ($answer,$errorno);
  356   }
  357   # replace ^ with **    (for exponentiation)
  358   # $answer =~ s/\^/**/g;
  359   # Return if  forbidden characters are found
  360   unless ($answer =~ /^[a-zA-Z0-9_\-\+ \t\/@%\*\.\n^\(\)]+$/ )  {
  361     $answer =~ tr/a-zA-Z0-9_\-\+ \t\/@%\*\.\n^\(\)/#/c;
  362     $errorno = "<BR>There are forbidden characters in your answer: $submittedAnswer<BR>";
  363     return ($answer,$errorno);
  364   }
  365   $errorno = 0;
  366   return($answer, $errorno);
  367 }
  368 
  369 1;
  370 
  371 __END__
  372 
  373 =head1 SYNOPSIS
  374 
  375  $pg = WeBWorK::PG->new(
  376    $courseEnv,  # a WeBWorK::CourseEnvironment object
  377    $user,       # a WeBWorK::User object
  378    $sessionKey,
  379    $set,        # a WeBWorK::Set object
  380    $problem,    # a WeBWorK::Problem object
  381    $psvn,
  382    $formFields  # in &WeBWorK::Form::Vars format
  383    { # translation options
  384      displayMode     => "images", # (plainText|formattedText|images)
  385      showHints       => 1,        # (0|1)
  386      showSolutions   => 0,        # (0|1)
  387      refreshMath2img => 0,        # (0|1)
  388      processAnswers  => 1,        # (0|1)
  389    },
  390  );
  391 
  392  $translator = $pg->{translator}; # WeBWorK::PG::Translator
  393  $body       = $pg->{body_text};  # text string
  394  $header     = $pg->{head_text};  # text string
  395  $answerHash = $pg->{answers};    # WeBWorK::PG::AnswerHash
  396  $result     = $pg->{result};     # hash reference
  397  $state      = $pg->{state};      # hash reference
  398  $errors     = $pg->{errors};     # text string
  399  $warnings   = $pg->{warnings};   # text string
  400  $flags      = $pg->{flags};      # hash reference
  401 
  402 =head1 DESCRIPTION
  403 
  404 WeBWorK::PG encapsulates the PG translation process, making multiple calls to
  405 WeBWorK::PG::Translator. Much of the flexibility of the Translator is hidden,
  406 instead making choices that are appropriate for the webwork-modperl system.
  407 
  408 =head1 CONSTRUCTION
  409 
  410 =over
  411 
  412 =item new (ENVIRONMENT, USER, KEY, SET, PROBLEM, PSVN, FIELDS, OPTIONS)
  413 
  414 The C<new> method creates a translator, initializes it using the parameters
  415 specified, translates a PG file, and processes answers. It returns a reference
  416 to a blessed hash containing the results of the translation process.
  417 
  418 =back
  419 
  420 =head2 Parameters
  421 
  422 =over
  423 
  424 =item ENVIRONMENT
  425 
  426 a WeBWorK::CourseEnvironment object
  427 
  428 =item USER
  429 
  430 a WeBWorK::User object
  431 
  432 =item KEY
  433 
  434 the session key of the current session
  435 
  436 =item SET
  437 
  438 a WeBWorK::Set object
  439 
  440 =item PROBLEM
  441 
  442 a WeBWorK::Problem object. The contents of the source_file field can specify a
  443 PG file either by absolute path or path relative to the "templates" directory.
  444 I<The caller should remove taint from this value before passing!>
  445 
  446 =item PSVN
  447 
  448 the problem set version number
  449 
  450 =item FIELDS
  451 
  452 a reference to a hash (as returned by &WeBWorK::Form::Vars) containing form
  453 fields submitted by a problem processor. The translator will look for fields
  454 like "AnSwEr[0-9]" containing submitted student answers.
  455 
  456 =item OPTIONS
  457 
  458 a reference to a hash containing the following data:
  459 
  460 =over
  461 
  462 =item displayMode
  463 
  464 one of "plainText", "formattedText", or "images"
  465 
  466 =item showHints
  467 
  468 boolean, render hints
  469 
  470 =item showSolutions
  471 
  472 boolean, render solutions
  473 
  474 =item refreshMath2img
  475 
  476 boolean, force images created by math2img (in "images" mode) to be recreated,
  477 even if the PG source has not been updated.
  478 
  479 =item processAnswers
  480 
  481 boolean, call answer evaluators and graders
  482 
  483 =back
  484 
  485 =back
  486 
  487 =head2 RETURN VALUE
  488 
  489 The C<new> method returns a blessed hash reference containing the following
  490 fields. More information can be found in the documentation for
  491 WeBWorK::PG::Translator.
  492 
  493 =over
  494 
  495 =item translator
  496 
  497 The WeBWorK::PG::Translator object used to render the problem.
  498 
  499 =item head_text
  500 
  501 HTML code for the E<lt>headE<gt> block of an resulting web page. Used for
  502 JavaScript features.
  503 
  504 =item body_text
  505 
  506 HTML code for the E<lt>bodyE<gt> block of an resulting web page.
  507 
  508 =item answers
  509 
  510 An C<AnswerHash> object containing submitted answers, and results of answer
  511 evaluation.
  512 
  513 =item result
  514 
  515 A hash containing the results of grading the problem.
  516 
  517 =item state
  518 
  519 A hash containing the new problem state.
  520 
  521 =item errors
  522 
  523 A string containing any errors encountered while rendering the problem.
  524 
  525 =item warnings
  526 
  527 A string containing any warnings encountered while rendering the problem.
  528 
  529 =item flags
  530 
  531 A hash containing PG_flags (see the Translator docs).
  532 
  533 =back
  534 
  535 =head1 OPERATION
  536 
  537 WeBWorK::PG goes through the following operations when constructed:
  538 
  539 =over
  540 
  541 =item Get database information
  542 
  543 Retrieve information about the current user, set, and problem from the
  544 database.
  545 
  546 =item Create a translator
  547 
  548 Instantiate a WeBWorK::PG::Translator object.
  549 
  550 =item Set the directory hash
  551 
  552 Set the translator's directory hash (courseScripts, macros, templates, and temp
  553 directories) from the course environment.
  554 
  555 =item Evaluate PG modules
  556 
  557 Using the module list from the course environment (pg->modules), perform a
  558 "use"-like operation to evaluate modules at runtime.
  559 
  560 =item Set the problem environment
  561 
  562 Use data from the user, set, and problem, as well as the course environemnt and
  563 translation options, to set the problem environment.
  564 
  565 =item Initialize the translator
  566 
  567 Call &WeBWorK::PG::Translator::initialize. What more do you want?
  568 
  569 =item Load PG.pl and dangerousMacros.pl
  570 
  571 These macros must be loaded without opcode masking, so they are loaded here.
  572 
  573 =item Set the opcode mask
  574 
  575 Set the opcode mask to the default specified by WeBWorK::PG::Translator.
  576 
  577 =item Load the problem source
  578 
  579 Give the problem source to the translator.
  580 
  581 =item Install a safety filter
  582 
  583 The safety filter is used to preprocess student input before evaluation. The
  584 default safety filter, &WeBWorK::PG::safetyFilter, is used.
  585 
  586 =item Translate the problem source
  587 
  588 Call &WeBWorK::PG::Translator::translate to render the problem source into the
  589 format given by the display mode.
  590 
  591 =item Process student answers
  592 
  593 Use form field inputs to evaluate student answers.
  594 
  595 =item Load the problem state
  596 
  597 Use values from the database to initialize the problem state, so that the
  598 grader will have a point of reference.
  599 
  600 =item Determine an entry order
  601 
  602 Use the ANSWER_ENTRY_ORDER flag to determine the order of answers in the
  603 problem. This is important for problems with dependancies among parts.
  604 
  605 =item Install a grader
  606 
  607 Use the PROBLEM_GRADER_TO_USE flag, or a default from the course environment,
  608 to install a grader.
  609 
  610 =item Grade the problem
  611 
  612 Use the selected grader to grade the problem.
  613 
  614 =back
  615 
  616 =head1 AUTHOR
  617 
  618 Written by Sam Hathaway, sh002i (at) math.rochester.edu.
  619 
  620 =cut

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9