package WeBWork::PG;
# hide PG::* from the not-yet-insane.
use strict;
use warnings;
use WeBWorK::Utils qw(readFile formatDateTime);
use WeBWorK::DB::Classlist;
use WeBWorK::DB::WW;
use WeBWorK::PG::Translator;
sub new($$$$$$) {
my $invocant = shift;
my $class = ref($invocant) || $invocant;
my $courseEnv = shift;
my $userName = shift;
my $setName = shift;
my $problemNumber = shift;
my $formData = shift;
# get database information
my $classlist = WeBWorK::DB::Classlist->new($courseEnv);
my $wwdb = WeBWorK::DB::WW->new($courseEnv);
my $user = $classlist->getUser($userName);
my $set = $wwdb->getSet($userName, $setName);
my $problem = $wwdb->getProblem($userName, $setName, $problemNumber);
my $psvn = $wwdb->getPSVN($userName, $setName);
# create a Translator
my $translator = WeBWorK::PG::Translator->new;
# give it a directory hash
$translator->rh_directories({
courseScriptsDirectory => $courseEnv->{webworkDirs}->{macros},
macroDirectory => $courseEnv->{courseDirs}->{macros},
templateDirectory => $courseEnv->{courseDirs}->{templates},
tempDirectory => $courseEnv->{courseDirs}->{html_temp},
});
# give it modules to evaluate
# give it "extra packages" to load
my $modules = $courseEnv->{pg}->{modules};
foreach $module (keys %$modules) {
my $main_package_loaded = 0;
foreach $package (@{$modules->{$module}}) {
if ($package eq $module) {
# this is the main package
$translator->evaluate_modules($package);
$main_package_loaded = 1;
} else {
# this is an "extra" package
if ($main_package_loaded) {
$translator->load_extra_packages($package);
} else {
warn "Can't load extra package $package: module $module hasn't been evaluated.";
}
}
}
}
# give it an environment (from defineProblemEnvir)
$translator->environment(
defineProblemEnvir($courseEnv, $user, $set, $problem, $psvn, $formData)
);
# initialize it
$translator->initialize();
# have it "unrestricted load" PG.pl and dangerousMacros.pl
my $pg_pl = $courseEnv->{webworkDirs}->{macros} . "/PG.pl";
my $dangerousMacros_pl = $courseEnv->{webworkDirs}->{macros} . "/dangerousMacros.pl"
my $err = $translator->unrestricted_load($pg_pl);
warn "Error while loading $pg_pl: $err" if $err;
$err = $translator->unrestricted_load($dangerousMacros_pl);
warn "Error while loading $dangerousMacros_pl: $err" if $err;
# give it an opcode mask (using default values)
$translator->set_mask();
# give it the problem source
my $sourceFile = $courseEnv->{courseDirs}->{templates}."/".$problem->source_file;
$translator->source_string(readFile($sourceFile));
# install a safety filter (&safetyFilter)
$translator->rf_safety_filter(\&safetyFilter);
# translate the PG source into text
$translator->translate();
# install a grader
my $grader = $courseEnv->{pg}->{grader};
$translator->rf_problem_grader(\&FIXME); # *** need a coderef!
# process student answers (if any)
$translator->process_answers($formData);
# a PG object is a REFERENCE to a Translator object
return bless \$translator, $class;
}
# -----
sub defineProblemEnvir($$$$$$) {
my $courseEnv = shift;
my $user = shift;
my $set = shift;
my $problem = shift;
my $psvn = shift;
my $form = shift;
my %envir;
# PG environment variables
# from docs/pglanguage/pgreference/environmentvariables as of 06/25/2002
# any changes are noted by "ADDED:" or "REMOVED:"
# Vital state information
# ADDED: displayHintsQ, displaySolutionsQ, externalTTHPath
$envir{psvn} = $psvn;
$envir{psvnNumber} = $envir{psvn};
$envir{probNum} = $problem->id;
$envir{questionNumber} = $envir{probNum};
$envir{fileName} = $problem->source_file;
$envir{probFileName} = $envir{fileName};
$envir{problemSeed} = $problem->problem_seed;
$envir{displayMode} = $form->param('Mode');
$envir{languageMode} = $envir{displayMode};
$envir{outputMode} = $envir{displayMode};
$envir{displayHintsQ} = $form->param('ShowHint');
$envir{displaySolutionsQ} = $form->param('ShowSol');
$envir{externalTTHPath} = $courseEnv->{externalPrograms}->{tth};
# Problem Information
# ADDED: courseName
$envir{openDate} = $set->open_date;
$envir{formattedOpenDate} = formatDateTime $envir{openDate};
$envir{dueDate} = $set->due_date;
$envir{formattedDueDate} = formatDateTime $envir{dueDate};
$envir{answerDate} = $set->answer_date;
$envir{formattedAnswerDate} = formatDateTime $envir{answerDate};
$envir{numOfAttempts} = $problem->num_correct + $problem->num_incorrect;
$envir{problemValue} = $problem->value;
$envir{sessionKey} = $form->param('key');
$envir{courseName} = $courseEnv->{courseName};
# Student Information
# ADDED: studentID
$envir{sectionName} = $user->section;
$envir{sectionNumber} = $envir{sectionName};
$envir{recitationName} = $user->recitation;
$envir{recitationNumber} = $envir{recitationName};
$envir{setNumber} = $set->id;
$envir{studentLogin} = $user->id;
$envir{studentName} = $user->first_name . " " . $user->last_name;
$envir{studentID} = $user->student_id
# Answer Information
$envir{inputs_ref} = {}; # *** keys like "Answer1"
$envir{refSubmittedAnswers} = {}; # *** keys like "AnSwEr1"
# Default values for evaluating answers
my $ansEvalDefaults = $courseEnv->{pg}->{ansEvalDefaults};
$envir{$_} = $ansEvalDefaults->{$_} foreach (keys %$ansEvalDefaults);
# Directories and URLs
# REMOVED: courseName
$envir{cgiDirectory} = undef;
$envir{cgiURL} = undef;
$envir{classDirectory} = undef;
$envir{courseScriptsDirectory} = $courseEnv->{webworkDirs}->{macros};
$envir{htmlDirectory} = $courseEnv->{courseDirs}->{html};
$envir{htmlURL} = $courseEnv->{courseURLs}->{html};
$envir{macroDirectory} = $courseEnv->{courseDirs}->{macros};
$envir{templateDirectory} = $courseEnv->{courseDirs}->{templates};
$envir{tempDirectory} = $courseEnv->{courseDirs}->{html_temp};
$envir{tempURL} = $courseEnv->{courseURLs}->{html_temp};
$envir{scriptDirectory} = undef;
$envir{webworkDocsURL} = $courseEnv->{webworkURLs}->{docs};
return \%envir;
}
sub safetyFilter {
my $answer = shift; # accepts one answer and checks it
my $submittedAnswer = $answer;
$answer = '' unless defined $answer;
my ($errorno);
$answer =~ tr/\000-\037/ /;
# Return if answer field is empty
unless ($answer =~ /\S/) {
#$errorno = "
No answer was submitted.";
$errorno = 0; ## don't report blank answer as error
return ($answer,$errorno);
}
# replace ^ with ** (for exponentiation)
# $answer =~ s/\^/**/g;
# Return if forbidden characters are found
unless ($answer =~ /^[a-zA-Z0-9_\-\+ \t\/@%\*\.\n^\(\)]+$/ ) {
$answer =~ tr/a-zA-Z0-9_\-\+ \t\/@%\*\.\n^\(\)/#/c;
$errorno = "
There are forbidden characters in your answer: $submittedAnswer
";
return ($answer,$errorno);
}
$errorno = 0;
return($answer, $errorno);
}
1;