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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9