[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 684 - (download) (as text) (annotate)
Mon Jan 6 19:34:36 2003 UTC (10 years, 4 months ago) by sh002i
Original Path: trunk/webwork2/lib/WeBWorK/PG.pm
File size: 18844 byte(s)
removed user name propogation from Logout -> Login
disabled "show hint/solution" when there's no hint/solution

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9