[system] / trunk / webwork-modperl / lib / WeBWorK / PG.pm Repository:
ViewVC logotype

View of /trunk/webwork-modperl/lib/WeBWorK/PG.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 476 - (download) (as text) (annotate)
Tue Aug 20 01:07:18 2002 UTC (10 years, 9 months ago) by sh002i
File size: 16483 byte(s)
fixed problem with deciding when to generate images in math2img mode
finished adding template escapes to ProblemSets, ProblemSet, and Problem

fixed a problem where modules were removed from the courseEnv while
being loaded (whups.)
-sam

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9