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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9