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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9