[system] / trunk / webwork2 / lib / WeBWorK / ContentGenerator / Problem.pm Repository:
ViewVC logotype

View of /trunk/webwork2/lib/WeBWorK/ContentGenerator/Problem.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 492 - (download) (as text) (annotate)
Wed Aug 21 15:34:35 2002 UTC (10 years, 9 months ago) by sh002i
File size: 15561 byte(s)
made progress towards working hardcopy generation. next, i have to fix
the call to pdflatex. screw pip. it's not working right, and it doesn't
give me any speed gain, since latex has to goofily write the WHOLE DAMN
FILE in whatever order it pleases before letting me have it.
-sam

    1 ################################################################################
    2 # WeBWorK mod_perl (c) 1995-2002 WeBWorK Team, Univeristy of Rochester
    3 # $Id$
    4 ################################################################################
    5 
    6 package WeBWorK::ContentGenerator::Problem;
    7 
    8 =head1 NAME
    9 
   10 WeBWorK::ContentGenerator::Problem - Allow a student to interact with a problem.
   11 
   12 =cut
   13 
   14 use strict;
   15 use warnings;
   16 use base qw(WeBWorK::ContentGenerator);
   17 use CGI qw();
   18 use WeBWorK::Form;
   19 use WeBWorK::PG;
   20 use WeBWorK::Utils qw(ref2string encodeAnswers decodeAnswers);
   21 
   22 # TODO:
   23 # 7. make warnings work
   24 
   25 ############################################################
   26 #
   27 # user
   28 # key
   29 #
   30 # displayMode
   31 # showOldAnswers
   32 # showCorrectAnswers
   33 # showHints
   34 # showSolutions
   35 #
   36 # AnSwEr# - answer blanks in problem
   37 #
   38 # redisplay - name of the "Redisplay Problem" button
   39 # submitAnswers - name of "Submit Answers" button
   40 #
   41 ############################################################
   42 
   43 sub pre_header_initialize {
   44   my ($self, $setName, $problemNumber) = @_;
   45   my $courseEnv = $self->{courseEnvironment};
   46   my $r = $self->{r};
   47   my $userName = $r->param('user');
   48 
   49   # make sure $problemNumber is numeric (see PG.pm)
   50   die "Problem must be numeric!\n" unless $problemNumber =~ /^\d+$/;
   51 
   52   # fix format of setName and problem
   53   $setName =~ s/^set//;
   54   $problemNumber =~ s/^prob//;
   55 
   56   ##### database setup #####
   57 
   58   my $cldb   = WeBWorK::DB::Classlist->new($courseEnv);
   59   my $wwdb   = WeBWorK::DB::WW->new($courseEnv);
   60   my $authdb = WeBWorK::DB::Auth->new($courseEnv);
   61 
   62   my $user            = $cldb->getUser($userName);
   63   my $set             = $wwdb->getSet($userName, $setName);
   64   my $problem         = $wwdb->getProblem($userName, $setName, $problemNumber);
   65   my $permissionLevel = $authdb->getPermissions($userName);
   66 
   67   ##### form processing #####
   68 
   69   # set options from form fields (see comment at top of file for names)
   70   my $displayMode        = $r->param("displayMode")        || $courseEnv->{pg}->{options}->{displayMode};
   71   my $redisplay          = $r->param("redisplay");
   72   my $submitAnswers      = $r->param("submitAnswers");
   73 
   74   # coerce form fields into CGI::Vars format
   75   my $formFields = { WeBWorK::Form->new_from_paramable($r)->Vars };
   76 
   77   ##### permissions #####
   78 
   79   # what does the user want to do?
   80   my %want = (
   81     showOldAnswers     => $r->param("showOldAnswers")     || $courseEnv->{pg}->{options}->{showOldAnswers},
   82     showCorrectAnswers => $r->param("showCorrectAnswers") || $courseEnv->{pg}->{options}->{showCorrectAnswers},
   83     showHints          => $r->param("showHints")          || $courseEnv->{pg}->{options}->{showHints},
   84     showSolutions      => $r->param("showSolutions")      || $courseEnv->{pg}->{options}->{showSolutions},
   85     recordAnswers      => $r->param("recordAnswers")      || 1,
   86   );
   87 
   88   # are certain options enforced?
   89   my %must = (
   90     showOldAnswers     => 0,
   91     showCorrectAnswers => 0,
   92     showHints          => 0,
   93     showSolutions      => 0,
   94     recordAnswers      => mustRecordAnswers($permissionLevel),
   95   );
   96 
   97   # does the user have permission to use certain options?
   98   my %can = (
   99     showOldAnswers     => 1,
  100     showCorrectAnswers => canShowCorrectAnswers($permissionLevel, $set->answer_date),
  101     showHints          => 1,
  102     showSolutions      => canShowSolutions($permissionLevel, $set->answer_date),
  103     recordAnswers      => canRecordAnswers($permissionLevel, $set->open_date, $set->due_date,
  104       $problem->max_attempts, $problem->num_correct + $problem->num_incorrect + 1),
  105       # num_correct+num_incorrect+1 -- as this happens before updating $problem
  106   );
  107 
  108   # final values for options
  109   my %will;
  110   foreach(keys %must) {
  111     $will{$_} = $can{$_} && ($want{$_} || $must{$_});
  112   }
  113 
  114   ##### sticky answers #####
  115 
  116   if (not $submitAnswers and $will{showOldAnswers}) {
  117     # do this only if new answers are NOT being submitted
  118     my %oldAnswers = decodeAnswers($problem->last_answer);
  119     $formFields->{$_} = $oldAnswers{$_} foreach keys %oldAnswers;
  120   }
  121 
  122   ##### translation #####
  123 
  124   my $pg = WeBWorK::PG->new(
  125     $courseEnv,
  126     $r->param('user'),
  127     $r->param('key'),
  128     $setName,
  129     $problemNumber,
  130     { # translation options
  131       displayMode     => $displayMode,
  132       showHints       => $will{showHints},
  133       showSolutions   => $will{showSolutions},
  134       refreshMath2img => $will{showHints} || $will{showSolutions},
  135       # try leaving processAnswers on all the time?
  136       processAnswers  => 1, #$submitAnswers ? 1 : 0,
  137     },
  138     $formFields
  139   );
  140 
  141   ##### store fields #####
  142 
  143   $self->{cldb}            = $cldb;
  144   $self->{wwdb}            = $wwdb;
  145   $self->{authdb}          = $authdb;
  146 
  147   $self->{user}            = $user;
  148   $self->{set}             = $set;
  149   $self->{problem}         = $problem;
  150   $self->{permissionLevel} = $permissionLevel;
  151 
  152   $self->{displayMode}   = $displayMode;
  153   $self->{redisplay}     = $redisplay;
  154   $self->{submitAnswers} = $submitAnswers;
  155   $self->{formFields}    = $formFields;
  156 
  157   $self->{want} = \%want;
  158   $self->{must} = \%must;
  159   $self->{can}  = \%can;
  160   $self->{will} = \%will;
  161 
  162   $self->{pg} = $pg;
  163 }
  164 
  165 #sub header {
  166 # # *** we need to print $pg->{header_text} here!
  167 #}
  168 
  169 sub path {
  170   my $self = shift;
  171   my $args = $_[-1];
  172   my $setName = $self->{set}->id;
  173   my $problemNumber = $self->{problem}->id;
  174 
  175   my $ce = $self->{courseEnvironment};
  176   my $root = $ce->{webworkURLs}->{root};
  177   my $courseName = $ce->{courseName};
  178   return $self->pathMacro($args,
  179     "Home" => "$root",
  180     $courseName => "$root/$courseName",
  181     $setName => "$root/$courseName/set$setName",
  182     "Problem $problemNumber" => "",
  183   );
  184 }
  185 
  186 sub siblings {
  187   my $self = shift;
  188   my $setName = $self->{set}->id;
  189   my $problemNumber = $self->{problem}->id;
  190 
  191   my $ce = $self->{courseEnvironment};
  192   my $root = $ce->{webworkURLs}->{root};
  193   my $courseName = $ce->{courseName};
  194 
  195   my $wwdb = $self->{wwdb};
  196   my $user = $self->{r}->param("user");
  197   my @problems;
  198   push @problems, $wwdb->getProblem($user, $setName, $_)
  199     foreach ($wwdb->getProblems($user, $setName));
  200   foreach my $problem (sort { $a->id <=> $b->id } @problems) {
  201     print CGI::a({-href=>"$root/$courseName/$setName/".$problem->id."/?"
  202       . $self->url_authen_args}, "Problem ".$problem->id), CGI::br();
  203   }
  204 }
  205 
  206 sub nav {
  207   my $self = shift;
  208   my $args = $_[-1];
  209   my $setName = $self->{set}->id;
  210   my $problemNumber = $self->{problem}->id;
  211 
  212   my $ce = $self->{courseEnvironment};
  213   my $root = $ce->{webworkURLs}->{root};
  214   my $courseName = $ce->{courseName};
  215 
  216   my $wwdb = $self->{wwdb};
  217   my $user = $self->{r}->param("user");
  218 
  219   my @links = ("Problem List" => "$root/$courseName/set$setName");
  220 
  221   my $prevProblem = $wwdb->getProblem($user, $setName, $problemNumber-1);
  222   my $nextProblem = $wwdb->getProblem($user, $setName, $problemNumber+1);
  223   unshift @links, "Previous Problem" => "$root/$courseName/set$setName/prob".$prevProblem->id
  224     if $prevProblem;
  225   push @links, "Next Problem" => "$root/$courseName/set$setName/prob".$nextProblem->id
  226     if $nextProblem;
  227 
  228   return $self->navMacro($args, @links);
  229 }
  230 
  231 sub title {
  232   my $self = shift;
  233   my $setName = $self->{set}->id;
  234   my $problemNumber = $self->{problem}->id;
  235 
  236   return "$setName : Problem $problemNumber";
  237 }
  238 
  239 sub body {
  240   my $self = shift;
  241 
  242   #$self->prepare(@_);
  243 
  244   # unpack some useful variables
  245   my $r               = $self->{r};
  246   my $wwdb            = $self->{wwdb};
  247   my $set             = $self->{set};
  248   my $problem         = $self->{problem};
  249   my $permissionLevel = $self->{permissionLevel};
  250   my $submitAnswers   = $self->{submitAnswers};
  251   my %will            = %{ $self->{will} };
  252   my $pg              = $self->{pg};
  253 
  254   ##### translation errors? #####
  255 
  256   if ($pg->{flags}->{error_flag}) {
  257     print translationError($pg->{errors}, $pg->{body_text});
  258     return "";
  259   }
  260 
  261   ##### answer processing #####
  262 
  263   # if answers were submitted:
  264   if ($submitAnswers) {
  265     # store answers in DB for sticky answers
  266     my %answersToStore;
  267     my %answerHash = %{ $pg->{answers} };
  268     $answersToStore{$_} = $answerHash{$_}->{original_student_ans}
  269       foreach (keys %answerHash);
  270     my $answerString = encodeAnswers(%answersToStore,
  271       @{ $pg->{flags}->{ANSWER_ENTRY_ORDER} });
  272     $problem->last_answer($answerString);
  273     $wwdb->setProblem($problem);
  274 
  275     # store state in DB if it makes sense
  276     if ($will{recordAnswers}) {
  277       $problem->attempted(1);
  278       $problem->status($pg->{state}->{recorded_score});
  279       $problem->num_correct($pg->{state}->{num_of_correct_ans});
  280       $problem->num_incorrect($pg->{state}->{num_of_incorrect_ans});
  281       $wwdb->setProblem($problem);
  282     }
  283   }
  284 
  285   ##### output #####
  286 
  287   # attempt summary
  288   if ($submitAnswers or $will{showCorrectAnswers}) {
  289     # print this if user submitted answers OR requested correct answers
  290     print attemptResults($pg, $submitAnswers, $will{showCorrectAnswers},
  291       $pg->{flags}->{showPartialCorrectAnswers});
  292   }
  293 
  294   # score summary
  295   my $attempts = $problem->num_correct + $problem->num_incorrect;
  296   my $attemptsNoun = $attempts != 1 ? "times" : "time";
  297   my $lastScore = int ($problem->status * 100) . "%";
  298   my ($attemptsLeft, $attemptsLeftNoun);
  299   if ($problem->max_attempts == -1) {
  300     # unlimited attempts
  301     $attemptsLeft = "unlimited";
  302     $attemptsLeftNoun = "attempts";
  303   } else {
  304     $attemptsLeft = $problem->max_attempts - $attempts;
  305     $attemptsLeftNoun = $attemptsLeft == 1 ? "attempt" : "attempts";
  306   }
  307   my $setClosedMessage;
  308   if (time < $set->open_date or time > $set->due_date) {
  309     $setClosedMessage = "This problem set is closed.";
  310     if ($permissionLevel > 0) {
  311       $setClosedMessage .= " Since you are a privileged user, additional attempts will be recorded.";
  312     } else {
  313       $setClosedMessage .= " Additional attempts will not be recorded.";
  314     }
  315   }
  316   print CGI::p(
  317     "You have attempted this problem $attempts $attemptsNoun.", CGI::br(),
  318     $problem->attempted
  319       ? "Your recorded score is $lastScore." . CGI::br()
  320       : "",
  321     "You have $attemptsLeft $attemptsLeftNoun remaining.", CGI::br(),
  322     $setClosedMessage,
  323   );
  324 
  325   # BY THE WAY..........
  326   # we have to figure out some way to tell the student if their NEW answer,
  327   # on THIS attempt, has been recorded. however, this is decided in part by
  328   # the grader, so is there any way for us to know? we can rule out several
  329   # cases where the answer is NOT being recorded, because of things decided
  330   # in &canRecordAnswers...
  331 
  332   print CGI::hr();
  333 
  334   # main form
  335   print
  336     CGI::startform("POST", $r->uri),
  337     $self->hidden_authen_fields,
  338     CGI::p(CGI::i($pg->{result}->{msg})),
  339     CGI::p($pg->{body_text}),
  340     CGI::p(CGI::submit(-name=>"submitAnswers", -label=>"Submit Answers")),
  341     $self->viewOptions,
  342     CGI::endform();
  343 
  344   # debugging stuff
  345   #print
  346   # hr(),
  347   # h2("debugging information"),
  348   # h3("form fields"),
  349   # ref2string($formFields),
  350   # h3("user object"),
  351   # ref2string($user),
  352   # h3("set object"),
  353   # ref2string($set),
  354   # h3("problem object"),
  355   # ref2string($problem),
  356   # h3("PG object"),
  357   # ref2string($pg, {'WeBWorK::PG::Translator' => 1});
  358 
  359   return "";
  360 }
  361 
  362 ##### output utilities #####
  363 
  364 sub translationError($$) {
  365   my ($error, $details) = @_;
  366   return
  367     CGI::h2("Software Error"),
  368     CGI::p(<<EOF),
  369 WeBWorK has encountered a software error while attempting to process this problem.
  370 It is likely that there is an error in the problem itself.
  371 If you are a student, contact your professor to have the error corrected.
  372 If you are a professor, please consut the error output below for more informaiton.
  373 EOF
  374     CGI::h3("Error messages"), CGI::blockquote(CGI::pre($error)),
  375     CGI::h3("Error context"), CGI::blockquote(CGI::pre($details));
  376 }
  377 
  378 sub attemptResults($$$) {
  379   my $pg = shift;
  380   my $showAttemptAnswers = shift;
  381   my $showCorrectAnswers = shift;
  382   my $showAttemptResults = $showAttemptAnswers && shift;
  383   my $problemResult = $pg->{result}; # the overall result of the problem
  384   my @answerNames = @{ $pg->{flags}->{ANSWER_ENTRY_ORDER} };
  385 
  386   my $header = CGI::th("answer");
  387   $header .= $showAttemptAnswers ? CGI::th("attempt")  : "";
  388   $header .= $showCorrectAnswers ? CGI::th("correct")  : "";
  389   $header .= $showAttemptResults ? CGI::th("result")   : "";
  390   $header .= $showAttemptAnswers ? CGI::th("messages") : "";
  391   my @tableRows = ( $header );
  392   my $numCorrect;
  393   foreach my $name (@answerNames) {
  394     my $answerResult  = $pg->{answers}->{$name};
  395     my $studentAnswer = $answerResult->{student_ans}; # original_student_ans
  396     my $correctAnswer = $answerResult->{correct_ans};
  397     my $answerScore   = $answerResult->{score};
  398     my $answerMessage = $showAttemptAnswers ? $answerResult->{ans_message} : "";
  399 
  400     $numCorrect += $answerScore > 0;
  401     my $resultString = $answerScore ? "correct :^)" : "incorrect >:(";
  402 
  403     my $row = CGI::td($name);
  404     $row .= $showAttemptAnswers ? CGI::td($studentAnswer) : "";
  405     $row .= $showCorrectAnswers ? CGI::td($correctAnswer) : "";
  406     $row .= $showAttemptResults ? CGI::td($resultString)  : "";
  407     $row .= $answerMessage      ? CGI::td($answerMessage) : "";
  408     push @tableRows, $row;
  409   }
  410 
  411   my $numCorrectNoun = $numCorrect == 1 ? "question" : "questions";
  412   my $scorePercent = int ($problemResult->{score} * 100) . "\%";
  413   my $summary = "On this attempt, you answered $numCorrect $numCorrectNoun out of "
  414     . scalar @answerNames . " correct, for a score of $scorePercent.";
  415   return CGI::table({-border=>1}, CGI::Tr(\@tableRows)) . CGI::p($summary);
  416 }
  417 
  418 sub viewOptions($) {
  419   my $self = shift;
  420   my $displayMode = $self->{displayMode};
  421   my %must = %{ $self->{must} };
  422   my %can  = %{ $self->{can}  };
  423   my %will = %{ $self->{will} };
  424 
  425   my $optionLine;
  426   $can{showOldAnswers} and $optionLine .= join "",
  427     "Show: &nbsp;",
  428     CGI::checkbox(
  429       -name    => "showOldAnswers",
  430       -checked => $will{showOldAnswers},
  431       -label   => "Saved answers",
  432     ), "&nbsp;&nbsp;";
  433   $can{showCorrectAnswers} and $optionLine .= join "",
  434     CGI::checkbox(
  435       -name    => "showCorrectAnswers",
  436       -checked => $will{showCorrectAnswers},
  437       -label   => "Correct answers",
  438     ), "&nbsp;&nbsp;";
  439   $can{showHints} and $optionLine .= join "",
  440     CGI::checkbox(
  441       -name    => "showHints",
  442       -checked => $will{showHints},
  443       -label   => "Hints",
  444     ), "&nbsp;&nbsp;";
  445   $can{showSolutions} and $optionLine .= join "",
  446     CGI::checkbox(
  447       -name    => "showSolutions",
  448       -checked => $will{showSolutions},
  449       -label   => "Solutions",
  450     ), "&nbsp;&nbsp;";
  451   $optionLine and $optionLine .= join "", CGI::br();
  452 
  453   return CGI::div({-style=>"border: thin groove; padding: 1ex; margin: 2ex"},
  454       "View equations as: &nbsp;",
  455     CGI::radio_group(
  456       -name    => "displayMode",
  457       -values  => ['plainText', 'formattedText', 'images'],
  458       -default => $displayMode,
  459       -labels  => {
  460         plainText     => "plain text",
  461         formattedText => "formatted text",
  462         images        => "images",
  463       }
  464     ), CGI::br(),
  465     $optionLine,
  466     CGI::submit(-name=>"redisplay", -label=>"Redisplay Problem"),
  467   );
  468 }
  469 
  470 ##### permission queries #####
  471 
  472 # this stuff should be abstracted out into the permissions system
  473 # however, the permission system only knows about things in the
  474 # course environment and the username. hmmm...
  475 
  476 # also, i should fix these so that they have a consistent calling
  477 # format -- perhaps:
  478 #   canPERM($courseEnv, $user, $set, $problem, $permissionLevel)
  479 
  480 sub canShowCorrectAnswers($$) {
  481   my ($permissionLevel, $answerDate) = @_;
  482   return $permissionLevel > 0 || time > $answerDate;
  483 }
  484 
  485 sub canShowSolutions($$) {
  486   my ($permissionLevel, $answerDate) = @_;
  487   return canShowCorrectAnswers($permissionLevel, $answerDate);
  488 }
  489 
  490 sub canRecordAnswers($$$$$) {
  491   my ($permissionLevel, $openDate, $dueDate, $maxAttempts, $attempts) = @_;
  492   my $permHigh = $permissionLevel > 0;
  493   my $timeOK = time >= $openDate && time <= $dueDate;
  494   my $attemptsOK = $attempts <= $maxAttempts;
  495   return $permHigh || ($timeOK && $attemptsOK);
  496 }
  497 
  498 sub mustRecordAnswers($) {
  499   my ($permissionLevel) = @_;
  500   return $permissionLevel == 0;
  501 }
  502 
  503 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9