--- trunk/webwork2/lib/WeBWorK/ContentGenerator/Problem.pm 2002/07/03 23:24:26 423 +++ trunk/webwork2/lib/WeBWorK/ContentGenerator/Problem.pm 2002/07/11 19:09:08 424 @@ -3,338 +3,141 @@ use strict; use warnings; -use Apache::Constants qw(:common); -use WeBWorK::ContentGenerator; +use CGI qw(:html :form); +use WeBWorK::Utils qw(ref2string); use WeBWorK::PG; +use WeBWorK::Form; -# "Classic" form fields from processProblem8.pl +# NEW form fields # -# user - user ID -# key - session key -# course - course name -# probSetKey - USUALLY known as the PSVN -# probNum - problem number a.k.a. ID a.k.a. name +# user +# key # -# Mode - display mode (HTML, HTML_tth, or typeset or whatever it's called) -# show_old_answers - whether or not student's old answers should be filled in -# ShowAns - asks for correct answer to be shown -- only available for instructors -# answer$i - student answers -# showEdit - checks if the ShowEditor button should be shown and clicked -# showSol - checks if the solution button ishould be shown and clicked +# displayMode +# showOldAnswers +# showCorrectAnswers +# showHints +# showSolutions # -# source - contains modified problem source when called from the web-based problem editor -# seed - contains problem seed when called from the web-based problem editor -# readSourceFromHTMLQ - if true, problem is read from 'source' instead of file -# action - submit button clicked to invoke script (alledgedly) -# 'Save updated version' -# 'Read problem from disk' -# 'Submit Answers' -# 'Preview Answers' -# 'Preview Again' -# probFileName - name of the PG file being edited -# languageType - afaik, always set to 'pg' +# submitAnswers - name of "Submit Answers" button sub title { - my ($self, $problem_set, $problem) = @_; - my $r = $self->{r}; - my $user = $r->param('user'); - return "Problem $problem of problem set $problem_set for $user"; + my ($self, $setName, $problemNumber) = @_; + my $userName = $self->{r}->param('user'); + return "Problem $problemNumber of problem set $setName for $userName"; } sub body { - my ($self, $problem_set, $problem) = @_; - - # we have to call init_translator like this: - my $pt = WeBWorK::PG->new($courseEnv, $userName, $setName, $problemNumber, $formData); - - # + my ($self, $setName, $problemNumber) = @_; + my $courseEnv = $self->{courseEnvironment}; + my $r = $self->{r}; + my $userName = $r->param('user'); - # ----- this is not a place of honor ----- + # fix format of setName and problem + # (i want dennis to cut "set" and "prob" off before calling me) + $setName =~ s/^set//; + $problemNumber =~ s/^prob//; + + # 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); + + # set options from form fields + my $displayMode = $r->param("displayMode") || $courseEnv->{pg}->{options}->{displayMode}; + my $showOldAnswers = $r->param("showOldAnswers") || $courseEnv->{pg}->{options}->{showOldAnswers}; + my $showCorrectAnswers = $r->param("showCorrectAnswers") || $courseEnv->{pg}->{options}->{showCorrectAnswers}; + my $showHints = $r->param("showHints") || $courseEnv->{pg}->{options}->{showHints}; + my $showSolutions = $r->param("showSolutions") || $courseEnv->{pg}->{options}->{showSolutions}; + my $processAnswers = $r->param("submitAnswers"); + + # coerce form fields into CGI::Vars format + my $formFields = { WeBWorK::Form->new_from_paramable($r)->Vars }; + + # TODO: + # 1. enforce privs for showCorrectAnswers and showSolutions + # (use $PRIV = $canPRIV && $wantPRIV -- cool syntax!) + # 2. if answers were not submitted and there are student answers in the DB, + # decode them and put them into $formFields for the translator + # 3. Latex2HTML massaging code + # 4. store submitted answers hash in database for sticky answers + # 5. deal with the results of answer evaluation and grading :p + # 6. introduce a recordAnswers option, which works on the same principle as + # the other priv-based options + + my $pg = WeBWorK::PG->new( + $courseEnv, + $r->param('user'), + $r->param('key'), + $setName, + $problemNumber, + { # translation options + displayMode => $displayMode, + showHints => $showHints, + showSolutions => $showSolutions, + processAnswers => $processAnswers, + }, + $formFields + ); + +# return ( +# h1("Problem.pm"), +# table( +# Tr(td("user"), td($r->param('userName'))), +# Tr(td("key"), td($r->param('key'))), +# Tr(td("set"), td($setName)), +# Tr(td("problem"), td($problemNumber)), +# ), +# #pre(hash2string($pg, 0)), +# hash2string($pg, 1), +# ); + + # View options form + print startform("POST", $r->uri); + print $self->hidden_authen_fields; + print p("View equations as: ", + radio_group( + -name => "displayMode", + -values => ['plainText', 'formattedText', 'images'], + -default => $displayMode, + -labels => { + plainText => "plain text", + formattedText => "formatted text", + images => "images", + } + ), br(), + checkbox( + -name => "showOldAnswers", + -checked => $showOldAnswers, + -label => "Show old answers", + ), br(), + submit(-name=>'redisplay') + ); + print endform(); + print hr(); + + # Previous answer results + + + # Problem form + print startform("POST", $r->uri); + print $self->hidden_authen_fields; + print p($pg->{body_text}); + print p(submit(-name=>"submitAnswers", -label=>"Submit Answers")); + print endform(); + print hr(); + + # debugging stuff + print h2("debugging information"); + print h3("form fields"); + print ref2string($formFields); + print h3("PG object"); + print ref2string($pg, {'WeBWorK::PG::Translator' => 1}); - # Run the problem (output the html text) but also store it within the object. - # The correct answers are also calculated and stored within the object - $pt ->translate(); - - # print problem output - print "Problem goes here

\n"; - print "Problem output
\n"; - print "


"; - print ${$pt->r_text()}; - print "
"; - print "

End of problem output
"; - - - # print source code - print "Source code

\n";
-	print $SOURCE1;
-	print "
End source code

"; - - # The format for the output is described here. We'll need a local variable - # to handle the warnings. From within the problem the warning command - # has been slaved to the __WARNINGS__ routine which is defined in Global. - # We'll need to provide an alternate mechanism. - # The base64 encoding is only needed for xml transmission. - print "


"; - print "Warnings output
"; - my $WARNINGS = "Let this be a warning:"; - - print $WARNINGS; - - # Install the standard problem grader. See gage/xmlrpc/daemon.pm or processProblem8 for detailed - # code on how to choose which problem grader to install, depending on courseEnvironment and problem data. - # See also PG.pl which provides for problem by problem overrides. - $pt->rf_problem_grader($pt->rf_std_problem_grader); - - # creates and stores a hash of answer results inside the object: $rh_answer_results - $pt -> process_answers($rh->{envir}->{inputs_ref}); - - - # THE UPDATE AND GRADING LOGIC COULD USE AN OVERHAUL. IT WAS SOMEWHAT CONSTRAINED - # BY LEGACY CONDITIONS IN THE ORIGINAL PROCESSPROBLEM8. IT'S NOT BAD - # BUT IT COULD PROBABLY BE MADE A LITTLE MORE STRAIGHT FORWARD. - # - # updates the problem state stored by the translator object from the problemEnvironment data - - # $pt->rh_problem_state({ recorded_score => $rh->{problem_state}->{recorded_score}, - # num_of_correct_ans => $rh->{problem_state}->{num_of_correct_ans} , - # num_of_incorrect_ans => $rh->{problem_state}->{num_of_incorrect_ans} - # } ); - - # grade the problem (and update the problem state again.) - # - # Define an entry order -- the default is the order they are received from the browser. - # (Which as I understand it is NOT guaranteed to be the Left->Right Up-> Down order we're - # used to in the West. - - my %PG_FLAGS = $pt->h_flags; - my $ra_answer_entry_order = ( defined($PG_FLAGS{ANSWER_ENTRY_ORDER}) ) ? - $PG_FLAGS{ANSWER_ENTRY_ORDER} : [ keys %{$pt->rh_evaluated_answers} ] ; - # Decide whether any answers were submitted. - my $answers_submitted = 0; - $answers_submitted = 1 if defined( $rh->{answer_form_submitted} ) and 1 == $rh->{answer_form_submitted}; - # If there are answers, grade them - my ($rh_problem_result,$rh_problem_state) = $pt->grade_problem( answers_submitted => $answers_submitted, - ANSWER_ENTRY_ORDER => $ra_answer_entry_order - ); # grades the problem. - - # Output format expected by Webwork.pm (and I believe processProblem8, but check.) - my $out = { - text => ${$pt ->r_text()}, # encode_base64( ${$pt ->r_text()} ), - header_text => $pt->r_header, # encode_base64( ${ $pt->r_header } ), - answers => $pt->rh_evaluated_answers, - errors => $pt-> errors(), - WARNINGS => $WARNINGS, #encode_base64($WARNINGS ), - problem_result => $rh_problem_result, - problem_state => $rh_problem_state, - PG_flag => \%PG_FLAGS - }; - - # Debugging printout of environment tables - print "

Request item

\n\n"; - print ""; - print $self->print_form_data(''); - print "
','','
\n"; - print "path info
\n"; - print $r->path_info(); - print "

\n\ncourseEnvironment

\n\n"; - print pretty_print_rh($courseEnvironment); - print "

\n\nproblemEnvironment

\n\n"; - print pretty_print_rh($problemEnvir_rh); - - ""; -} - -sub pretty_print_rh { - my $r_input = shift; - my $out = ''; - if ( not ref($r_input) ) { - $out = $r_input; # not a reference - } elsif (is_hash_ref($r_input)) { - local($^W) = 0; - $out .= ""; - foreach my $key (sort keys %$r_input ) { - $out .= ""; - } - $out .="
$key=> ".pretty_print_rh($r_input->{$key}) . "
"; - } elsif (is_array_ref($r_input) ) { - my @array = @$r_input; - $out .= "( " ; - while (@array) { - $out .= pretty_print_rh(shift @array) . " , "; - } - $out .= " )"; - } elsif (ref($r_input) eq 'CODE') { - $out = "$r_input"; - } else { - $out = $r_input; - } - $out; -} - -sub is_hash_ref { - my $in =shift; - my $save_SIG_die_trap = $SIG{__DIE__}; - $SIG{__DIE__} = sub {CORE::die(@_) }; - my $out = eval{ %{ $in } }; - $out = ($@ eq '') ? 1 : 0; - $@=''; - $SIG{__DIE__} = $save_SIG_die_trap; - $out; -} -sub is_array_ref { - my $in =shift; - my $save_SIG_die_trap = $SIG{__DIE__}; - $SIG{__DIE__} = sub {CORE::die(@_) }; - my $out = eval{ @{ $in } }; - $out = ($@ eq '') ? 1 : 0; - $@=''; - $SIG{__DIE__} = $save_SIG_die_trap; - $out; + return ""; } 1; - -__END__ - -my $foo =0; - -# The warning mechanism. This needs to be turned into an object of its own -############### -## Error message routines cribbed from CGI -############### - -BEGIN { #error message routines cribbed from CGI - - my $CarpLevel = 0; # How many extra package levels to skip on carp. - my $MaxEvalLen = 0; # How much eval '...text...' to show. 0 = all. - - sub longmess { - my $error = shift; - my $mess = ""; - my $i = 1 + $CarpLevel; - my ($pack,$file,$line,$sub,$eval,$require); - - while (($pack,$file,$line,$sub,undef,undef,$eval,$require) = caller($i++)) { - if ($error =~ m/\n$/) { - $mess .= $error; - } - else { - if (defined $eval) { - if ($require) { - $sub = "require $eval"; - } - else { - $eval =~ s/[\\\']/\\$&/g; - if ($MaxEvalLen && length($eval) > $MaxEvalLen) { - substr($eval,$MaxEvalLen) = '...'; - } - $sub = "eval '$eval'"; - } - } - elsif ($sub eq '(eval)') { - $sub = 'eval {...}'; - } - - $mess .= "\t$sub " if $error eq "called"; - $mess .= "$error at $file line $line\n"; - } - - $error = "called"; - } - - $mess || $error; - } -} -############### -### Our error messages for giving maximum feedback to the user for errors within problems. -############### -BEGIN { - sub PG_floating_point_exception_handler { # 1st argument is signal name - my($sig) = @_; - print "Content-type: text/html\n\n

There was a floating point arithmetic error (exception SIG$sig )

--perhaps - you divided by zero or took the square root of a negative number? -
\n Use the back button to return to the previous page and recheck your entries.
\n"; - exit(0); - } - - $SIG{'FPE'} = \&PG_floating_point_exception_handler; -#!/usr/bin/perl -w - sub PG_warnings_handler { - my @input = @_; - my $msg_string = longmess(@_); - my @msg_array = split("\n",$msg_string); - my $out_string = ''; - - # Extra stack information is provided in this next block - # If the warning message does NOT end in \n then a line - # number is appended (see Perl manual about warn function) - # The presence of the line number is detected below and extra - # stack information is added. - # To suppress the line number and the extra stack information - # add \n to the end of a warn message (in .pl files. In .pg - # files add ~~n instead - - if ($input[$#input]=~/line \d*\.\s*$/) { - $out_string .= "##More details:
\n----"; - foreach my $line (@msg_array) { - chomp($line); - next unless $line =~/\w+\:\:/; - $out_string .= "----" .$line . "
\n"; - } - } - - $Global::WARNINGS .="* " . join("
",@input) . "
\n" . $out_string . - "
\n--------------------------------------
\n
\n"; - $Global::background_plain_url = $Global::background_warn_url; - $Global::bg_color = '#FF99CC'; #for warnings -- this change may come too late - } - - $SIG{__WARN__}=\&PG_warnings_handler; - - $SIG{__DIE__} = sub { - my $message = longmess(@_); - $message =~ s/\n/
\n/; - my ($package, $filename, $line) = caller(); - # use standard die for errors eminating from XML::Parser::Expat - # it uses a trapped eval which sometimes fails -- apparently on purpose - # and the error is handled by Expat itself. We don't want - # to interfer with that. - - if ($package eq 'XML::Parser::Expat') { - die @_; - } - #print "$package $filename $line \n"; - print - "Content-type: text/html\r\n\r\n

Software error

\n\n$message\n

\n - Please inform the webwork meister.

\n - In addition to the error message above the following warnings were detected: -


- $Global::WARNINGS; -
- It's sometimes hard to tell exactly what has gone wrong since the - full error message may have been sent to - standard error instead of to standard out. -

To debug you can -

- Good luck.

\n" ; - }; - - - -}