[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 1395 - (download) (as text) (annotate)
Tue Jul 15 19:36:25 2003 UTC (9 years, 10 months ago) by gage
File size: 31226 byte(s)
Added ShowAnswers.pm module which reads the log file of the students answers
Modifications to WeBWorK (dispatcher) and Problem (the Show Past Answer button
had to be redirected) were also made.
--Mike

    1 ################################################################################
    2 # WeBWorK mod_perl (c) 2000-2002 WeBWorK Project
    3 # $Id$
    4 ################################################################################
    5 
    6 package WeBWorK::ContentGenerator::Problem;
    7 use base qw(WeBWorK::ContentGenerator);
    8 
    9 =head1 NAME
   10 
   11 WeBWorK::ContentGenerator::Problem - Allow a student to interact with a problem.
   12 
   13 =cut
   14 my $timer0_ON=1;  # times pg translation phase
   15 use strict;
   16 use warnings;
   17 use CGI qw();
   18 use File::Path qw(rmtree);
   19 use WeBWorK::Form;
   20 use WeBWorK::PG;
   21 use WeBWorK::PG::ImageGenerator;
   22 use WeBWorK::PG::IO;
   23 use WeBWorK::Utils qw(writeLog writeCourseLog encodeAnswers decodeAnswers ref2string makeTempDirectory);
   24 use WeBWorK::DB::Utils qw(global2user user2global findDefaults);
   25 use WeBWorK::Timing;
   26 
   27 ############################################################
   28 #
   29 # user
   30 # effectiveUser
   31 # key
   32 #
   33 # displayMode
   34 # showOldAnswers
   35 # showCorrectAnswers
   36 # showHints
   37 # showSolutions
   38 #
   39 # AnSwEr# - answer blanks in problem
   40 #
   41 # redisplay - name of the "Redisplay Problem" button
   42 # submitAnswers - name of "Submit Answers" button
   43 # checkAnswers - name of the "Check Answers" button
   44 # previewAnswers - name of the "Preview Answers" button
   45 #
   46 ############################################################
   47 
   48 sub pre_header_initialize {
   49   my ($self, $setName, $problemNumber) = @_;
   50   my $r                    = $self->{r};
   51   my $courseEnv            = $self->{ce};
   52   my $db                   = $self->{db};
   53   my $userName             = $r->param('user');
   54   my $effectiveUserName    = $r->param('effectiveUser');
   55   my $key                  = $r->param('key');
   56   my $user                 = $db->getUser($userName);
   57   my $effectiveUser        = $db->getUser($effectiveUserName);
   58   my $permissionLevel      = $db->getPermissionLevel($userName)->permission();
   59 
   60   # obtain the merged set for $effectiveUser
   61   my $set = $db->getMergedSet($effectiveUserName, $setName);
   62 
   63   # obtain the merged problem for $effectiveUser
   64   my $problem = $db->getMergedProblem($effectiveUserName, $setName, $problemNumber);
   65 
   66   my $editMode = $r->param("editMode");
   67 
   68   if ($permissionLevel > 0 and defined $editMode) {
   69     # professors are allowed to fabricate sets and problems not
   70     # assigned to them (or anyone). this allows them to use the
   71     # editor to
   72 
   73     # if that is not yet defined obtain the global set, convert
   74     # it to a user set, and add fake user data
   75     unless (defined $set) {
   76       my $userSetClass = $db->{set_user}->{record};
   77       $set = global2user($userSetClass,
   78         $db->getGlobalSet($setName));
   79       die "Set $setName does not exist"
   80         unless defined $set;
   81       $set->psvn(0);
   82     }
   83 
   84     # if that is not yet defined obtain the global problem,
   85     # convert it to a user problem, and add fake user data
   86     unless (defined $problem) {
   87       my $userProblemClass = $db->{problem_user}->{record};
   88       $problem = global2user($userProblemClass,
   89         $db->getGlobalProblem($setName,$problemNumber));
   90       die "Problem $problemNumber in set $setName does not exist"
   91         unless defined $problem;
   92       $problem->user_id($effectiveUserName);
   93       $problem->problem_seed(0);
   94       $problem->status(0);
   95       $problem->attempted(0);
   96       $problem->last_answer("");
   97       $problem->num_correct(0);
   98       $problem->num_incorrect(0);
   99     }
  100 
  101     # now we're sure we have valid UserSet and UserProblem objects
  102     # yay!
  103 
  104     # now deal with possible editor overrides:
  105 
  106     # if the caller is asking to override the source file, and
  107     # editMode calls for a temporary file, do so
  108     my $sourceFilePath = $r->param("sourceFilePath");
  109     if (defined $sourceFilePath and $editMode eq "temporaryFile") {
  110       $problem->source_file($sourceFilePath);
  111     }
  112 
  113     # if the caller is asking to override the problem seed, do so
  114     my $problemSeed = $r->param("problemSeed");
  115     if (defined $problemSeed) {
  116       $problem->problem_seed($problemSeed);
  117     }
  118   } else {
  119     # students can't view problems not assigned to them
  120     die "Set $setName is not assigned to $effectiveUserName"
  121       unless defined $set;
  122     die "Problem $problemNumber in set $setName is not assigned to $effectiveUserName"
  123       unless defined $problem;
  124   }
  125 
  126   $self->{userName}          = $userName;
  127   $self->{effectiveUserName} = $effectiveUserName;
  128   $self->{user}              = $user;
  129   $self->{effectiveUser}     = $effectiveUser;
  130   $self->{permissionLevel}   = $permissionLevel;
  131   $self->{set}               = $set;
  132   $self->{problem}           = $problem;
  133   $self->{editMode}          = $editMode;
  134 
  135   ##### form processing #####
  136 
  137   # set options from form fields (see comment at top of file for names)
  138   my $displayMode        = $r->param("displayMode") || $courseEnv->{pg}->{options}->{displayMode};
  139   my $redisplay          = $r->param("redisplay");
  140   my $submitAnswers      = $r->param("submitAnswers");
  141   my $checkAnswers       = $r->param("checkAnswers");
  142   my $previewAnswers     = $r->param("previewAnswers");
  143 
  144   # fields which may be defined when using Problem Editor
  145   #my $override_seed = ($permissionLevel>=10) ? $r->param('problemSeed') : undef;
  146   #my $override_problem_source = ($permissionLevel>=10) ? $r->param('sourceFilePath') : undef;
  147   #my $editMode = undef;
  148   #my $submit_button = $r->param('submit_button');
  149   #if ( defined($submit_button ) ) {
  150   # $editMode = "temporaryFile" if $submit_button eq 'Refresh';
  151   # $editMode = 'savedFile'     if $submit_button eq 'Save';
  152   #}
  153   #
  154   ##override using the source file data from the form field
  155   #$problem->source_file($override_problem_source) if defined($override_problem_source);
  156   #$problem->problem_seed($override_seed)          if defined($override_seed);
  157   #
  158   ## store path to source file for title.
  159   #$self->{problem_source_name}    =  $problem->source_file;
  160   #$self->{edit_mode}   = $editMode;
  161   #$self->{current_problem_source}  = (defined($override_problem_source) ) ?
  162 
  163   # coerce form fields into CGI::Vars format
  164   my $formFields = { WeBWorK::Form->new_from_paramable($r)->Vars };
  165 
  166   $self->{displayMode}    = $displayMode;
  167   $self->{redisplay}      = $redisplay;
  168   $self->{submitAnswers}  = $submitAnswers;
  169   $self->{checkAnswers}   = $checkAnswers;
  170   $self->{previewAnswers} = $previewAnswers;
  171   $self->{formFields}     = $formFields;
  172 
  173   ##### permissions #####
  174 
  175   # are we allowed to view this problem?
  176   $self->{isOpen} = time >= $set->open_date || $permissionLevel > 0;
  177   return unless $self->{isOpen};
  178 
  179   # what does the user want to do?
  180   my %want = (
  181     showOldAnswers     => $r->param("showOldAnswers")     || $courseEnv->{pg}->{options}->{showOldAnswers},
  182     showCorrectAnswers => $r->param("showCorrectAnswers") || $courseEnv->{pg}->{options}->{showCorrectAnswers},
  183     showHints          => $r->param("showHints")          || $courseEnv->{pg}->{options}->{showHints},
  184     showSolutions      => $r->param("showSolutions")      || $courseEnv->{pg}->{options}->{showSolutions},
  185     recordAnswers      => $submitAnswers,
  186     checkAnswers       => $checkAnswers,
  187   );
  188 
  189   # are certain options enforced?
  190   my %must = (
  191     showOldAnswers     => 0,
  192     showCorrectAnswers => 0,
  193     showHints          => 0,
  194     showSolutions      => 0,
  195     recordAnswers      => mustRecordAnswers($permissionLevel),
  196     checkAnswers       => 0,
  197   );
  198 
  199   # does the user have permission to use certain options?
  200   my %can = (
  201     showOldAnswers     => 1,
  202     showCorrectAnswers => canShowCorrectAnswers($permissionLevel, $set->answer_date),
  203     showHints          => 1,
  204     showSolutions      => canShowSolutions($permissionLevel, $set->answer_date),
  205     recordAnswers      => canRecordAnswers($permissionLevel, $set->open_date, $set->due_date,
  206       $problem->max_attempts, $problem->num_correct + $problem->num_incorrect + 1),
  207       # attempts=num_correct+num_incorrect+1, as this happens before updating $problem
  208     checkAnswers       => canCheckAnswers($permissionLevel, $set->answer_date),
  209   );
  210 
  211   # final values for options
  212   my %will;
  213   foreach (keys %must) {
  214     $will{$_} = $can{$_} && ($want{$_} || $must{$_});
  215   }
  216 
  217   ##### sticky answers #####
  218 
  219   if (not ($submitAnswers or $previewAnswers or $checkAnswers) and $will{showOldAnswers}) {
  220     # do this only if new answers are NOT being submitted
  221     my %oldAnswers = decodeAnswers($problem->last_answer);
  222     $formFields->{$_} = $oldAnswers{$_} foreach keys %oldAnswers;
  223   }
  224 
  225   ##### translation #####
  226 
  227   $WeBWorK::timer0->continue("begin pg processing") if $timer0_ON;
  228   my $pg = WeBWorK::PG->new(
  229     $courseEnv,
  230     $effectiveUser,
  231     $key,
  232     $set,
  233     $problem,
  234     $set->psvn, # FIXME: this field should be removed
  235     $formFields,
  236     { # translation options
  237       displayMode     => $displayMode,
  238       showHints       => $will{showHints},
  239       showSolutions   => $will{showSolutions},
  240       refreshMath2img => $will{showHints} || $will{showSolutions},
  241       processAnswers  => 1,
  242     },
  243   );
  244 
  245   $WeBWorK::timer0->continue("end pg processing") if $timer0_ON;
  246   ##### fix hint/solution options #####
  247 
  248   $can{showHints}     &&= $pg->{flags}->{hintExists};
  249   $can{showSolutions} &&= $pg->{flags}->{solutionExists};
  250 
  251   ##### store fields #####
  252 
  253   $self->{want} = \%want;
  254   $self->{must} = \%must;
  255   $self->{can}  = \%can;
  256   $self->{will} = \%will;
  257 
  258   $self->{pg} = $pg;
  259 }
  260 
  261 #sub if_warnings($$) {
  262 # my ($self, $arg) = @_;
  263 # return 0 unless $self->{isOpen};
  264 # return $self->{pg}->{warnings} ne "";
  265 #}
  266 
  267 sub if_errors($$) {
  268   my ($self, $arg) = @_;
  269   return 0 unless $self->{isOpen};
  270   return $self->{pg}->{flags}->{error_flag};
  271 }
  272 
  273 sub head {
  274   my $self = shift;
  275   return "" unless $self->{isOpen};
  276   return $self->{pg}->{head_text} if $self->{pg}->{head_text};
  277 }
  278 
  279 sub options {
  280   my $self = shift;
  281   return join("",
  282     CGI::start_form("POST", $self->{r}->uri),
  283     $self->hidden_authen_fields,
  284     CGI::hr(),
  285     CGI::start_div({class=>"viewOptions"}),
  286     $self->viewOptions(),
  287     CGI::end_div(),
  288     CGI::end_form()
  289   );
  290 }
  291 
  292 sub path {
  293   my $self = shift;
  294   my $args = $_[-1];
  295   my $setName = $self->{set}->set_id;
  296   my $problemNumber = $self->{problem}->problem_id;
  297 
  298   my $ce = $self->{ce};
  299   my $root = $ce->{webworkURLs}->{root};
  300   my $courseName = $ce->{courseName};
  301   return $self->pathMacro($args,
  302     "Home" => "$root",
  303     $courseName => "$root/$courseName",
  304     $setName => "$root/$courseName/$setName",
  305     "Problem $problemNumber" => "",
  306   );
  307 }
  308 
  309 sub siblings {
  310   my $self = shift;
  311   my $setName = $self->{set}->set_id;
  312   my $problemNumber = $self->{problem}->problem_id;
  313 
  314   my $ce = $self->{ce};
  315   my $db = $self->{db};
  316   my $root = $ce->{webworkURLs}->{root};
  317   my $courseName = $ce->{courseName};
  318   print CGI::strong("Problems"), CGI::br();
  319 
  320   my $effectiveUser = $self->{r}->param("effectiveUser");
  321   my @problemIDs = $db->listUserProblems($effectiveUser, $setName);
  322   foreach my $problem (sort { $a <=> $b } @problemIDs) {
  323     print '&nbsp;&nbsp;'.CGI::a({-href=>"$root/$courseName/$setName/".$problem."/?"
  324       . $self->url_authen_args . "&displayMode=" . $self->{displayMode}},
  325       "Problem ".$problem), CGI::br();
  326   }
  327 
  328   return "";
  329 }
  330 
  331 sub nav {
  332   $WeBWorK::timer0->continue("begin nav subroutine") if $timer0_ON;
  333   my $self = shift;
  334   my $args = $_[-1];
  335   my $setName = $self->{set}->set_id;
  336   my $problemNumber = $self->{problem}->problem_id;
  337 
  338   my $ce = $self->{ce};
  339   my $db = $self->{db};
  340   my $root = $ce->{webworkURLs}->{root};
  341   my $courseName = $ce->{courseName};
  342 
  343   my $wwdb          = $self->{wwdb};
  344   my $effectiveUser = $self->{r}->param("effectiveUser");
  345   my $tail = "&displayMode=".$self->{displayMode};
  346 
  347   my @links = ("Problem List" , "$root/$courseName/$setName", "navProbList");
  348 
  349   my @problemIDs = $db->listUserProblems($effectiveUser, $setName);
  350   my ($prevID, $nextID);
  351   foreach my $id (@problemIDs) {
  352     $prevID = $id if $id < $problemNumber
  353       and (not defined $prevID or $id > $prevID);
  354     $nextID = $id if $id > $problemNumber
  355       and (not defined $nextID or $id < $nextID);
  356   }
  357   unshift @links, "Previous Problem" , ($prevID
  358     ? "$root/$courseName/$setName/".$prevID
  359     : "") , "navPrev";
  360   push @links, "Next Problem" , ($nextID
  361     ? "$root/$courseName/$setName/".$nextID
  362     : "") , "navNext";
  363 
  364   my $result = $self->navMacro($args, $tail, @links);
  365   $WeBWorK::timer0->continue("end nav subroutine") if $timer0_ON;
  366   return $result;
  367 }
  368 
  369 sub title {
  370   my $self = shift;
  371   my $setName = $self->{set}->set_id;
  372   my $problemNumber = $self->{problem}->problem_id;
  373 
  374   return "$setName : Problem $problemNumber";
  375 }
  376 
  377 sub body {
  378   my $self = shift;
  379 
  380   return CGI::p(CGI::font({-color=>"red"}, "This problem is not available because the problem set that contains it is not yet open."))
  381     unless $self->{isOpen};
  382 
  383   # unpack some useful variables
  384   my $r               = $self->{r};
  385   my $db              = $self->{db};
  386   my $set             = $self->{set};
  387   my $problem         = $self->{problem};
  388   my $editMode        = $self->{editMode};
  389   my $permissionLevel = $self->{permissionLevel};
  390   my $submitAnswers   = $self->{submitAnswers};
  391   my $checkAnswers    = $self->{checkAnswers};
  392   my $previewAnswers  = $self->{previewAnswers};
  393   my %want            = %{ $self->{want} };
  394   my %can             = %{ $self->{can}  };
  395   my %must            = %{ $self->{must} };
  396   my %will            = %{ $self->{will} };
  397   my $pg              = $self->{pg};
  398 
  399   ##### translation errors? #####
  400 
  401   if ($pg->{flags}->{error_flag}) {
  402     return $self->errorOutput($pg->{errors}, $pg->{body_text});
  403   }
  404 
  405   ##### answer processing #####
  406   $WeBWorK::timer0->continue("begin answer processing") if $timer0_ON;
  407   # if answers were submitted:
  408   my $scoreRecordedMessage;
  409   if ($submitAnswers) {
  410     # get a "pure" (unmerged) UserProblem to modify
  411     # this will be undefined if the problem has not been assigned to this user
  412     my $pureProblem = $db->getUserProblem($problem->user_id, $problem->set_id, $problem->problem_id);
  413     if (defined $pureProblem) {
  414       # store answers in DB for sticky answers
  415       my %answersToStore;
  416       my %answerHash = %{ $pg->{answers} };
  417       $answersToStore{$_} = $answerHash{$_}->{original_student_ans}
  418         foreach (keys %answerHash);
  419       my $answerString = encodeAnswers(%answersToStore,
  420         @{ $pg->{flags}->{ANSWER_ENTRY_ORDER} });
  421 
  422       # store last answer to database
  423       $problem->last_answer($answerString);
  424       $pureProblem->last_answer($answerString);
  425       $db->putUserProblem($pureProblem);
  426 
  427       # store state in DB if it makes sense
  428       if ($will{recordAnswers}) {
  429         $problem->status($pg->{state}->{recorded_score});
  430         $problem->attempted(1);
  431         $problem->num_correct($pg->{state}->{num_of_correct_ans});
  432         $problem->num_incorrect($pg->{state}->{num_of_incorrect_ans});
  433         $pureProblem->status($pg->{state}->{recorded_score});
  434         $pureProblem->attempted(1);
  435         $pureProblem->num_correct($pg->{state}->{num_of_correct_ans});
  436         $pureProblem->num_incorrect($pg->{state}->{num_of_incorrect_ans});
  437         if ($db->putUserProblem($pureProblem)) {
  438           $scoreRecordedMessage = "Your score was recorded.";
  439         } else {
  440           $scoreRecordedMessage = "Your score was not recorded because there was a failure in storing the problem record to the database.";
  441         }
  442         # write to the transaction log, just to make sure
  443         writeLog($self->{ce}, "transaction",
  444           $problem->problem_id."\t".
  445           $problem->set_id."\t".
  446           $problem->user_id."\t".
  447           $problem->source_file."\t".
  448           $problem->value."\t".
  449           $problem->max_attempts."\t".
  450           $problem->problem_seed."\t".
  451           $pureProblem->status."\t".
  452           $pureProblem->attempted."\t".
  453           $pureProblem->last_answer."\t".
  454           $pureProblem->num_correct."\t".
  455           $pureProblem->num_incorrect
  456         );
  457       } else {
  458         if (time < $set->open_date or time > $set->due_date) {
  459           $scoreRecordedMessage = "Your score was not recorded because this problem set is closed.";
  460         } else {
  461           $scoreRecordedMessage = "Your score was not recorded.";
  462         }
  463       }
  464     } else {
  465       $scoreRecordedMessage = "Your score was not recorded because this problem has not been built for you.";
  466     }
  467   }
  468 
  469   # logging student answers
  470 
  471   my $answer_log    = $self->{ce}->{courseFiles}->{logs}->{'answer_log'};
  472   if ( defined($answer_log )) {
  473     if ($submitAnswers ) {
  474       my $answerString = "";
  475       my %answerHash = %{ $pg->{answers} };
  476       $answerString = $answerString . $answerHash{$_}->{original_student_ans}."\t"
  477         foreach (sort keys  %answerHash);
  478       $answerString = '' unless defined($answerString); # insure string is defined.
  479       writeCourseLog($self->{ce}, "answer_log",
  480               join("",
  481             '|', $problem->user_id,
  482             '|', $problem->set_id,
  483             '|', $problem->problem_id,
  484             '|',"\t",
  485             time(),"\t",
  486             $answerString,
  487           ),
  488       );
  489 
  490     }
  491   }
  492 
  493   $WeBWorK::timer0->continue("end answer processing") if $timer0_ON;
  494 
  495   ##### output #####
  496 
  497   print CGI::start_div({class=>"problemHeader"});
  498 
  499   # custom message for editor
  500   if ($permissionLevel >= 10 and defined $editMode) {
  501     if ($editMode eq "temporaryFile") {
  502       print CGI::p(CGI::i("Editing temporary file: ", $problem->source_file));
  503     } elsif ($editMode eq "savedFile") {
  504       print CGI::p(CGI::i("Problem saved to: ", $problem->source_file));
  505     }
  506   }
  507 
  508   # attempt summary
  509   if ($submitAnswers or $will{showCorrectAnswers}) {
  510     # print this if user submitted answers OR requested correct answers
  511     print $self->attemptResults($pg, $submitAnswers,
  512       $will{showCorrectAnswers},
  513       $pg->{flags}->{showPartialCorrectAnswers}, 1, 1);
  514   } elsif ($checkAnswers) {
  515     # print this if user previewed answers
  516     print $self->attemptResults($pg, 1, 0, 1, 1, 1);
  517       # show attempt answers
  518       # don't show correct answers
  519       # show attempt results (correctness)
  520       # don't show attempt previews
  521   } elsif ($previewAnswers) {
  522     # print this if user previewed answers
  523     print $self->attemptResults($pg, 1, 0, 0, 0, 1);
  524       # show attempt answers
  525       # don't show correct answers
  526       # don't show attempt results (correctness)
  527       # show attempt previews
  528   }
  529 
  530   print CGI::end_div();
  531 
  532   print CGI::start_div({class=>"problem"});
  533 
  534   # main form
  535   print
  536     CGI::startform("POST", $r->uri),
  537     $self->hidden_authen_fields,
  538     CGI::p($pg->{body_text}),
  539     CGI::p($pg->{result}->{msg} ? CGI::b("Note: ") : "", CGI::i($pg->{result}->{msg})),
  540     CGI::p(
  541       ($can{recordAnswers}
  542         ? CGI::submit(-name=>"submitAnswers",
  543           -label=>"Submit Answers")
  544         : ""),
  545       ($can{checkAnswers}
  546         ? CGI::submit(-name=>"checkAnswers",
  547           -label=>"Check Answers")
  548         : ""),
  549       CGI::submit(-name=>"previewAnswers",
  550         -label=>"Preview Answers"),
  551     );
  552   print CGI::end_div();
  553 
  554   print CGI::start_div({class=>"scoreSummary"});
  555 
  556   # score summary
  557   my $attempts = $problem->num_correct + $problem->num_incorrect;
  558   my $attemptsNoun = $attempts != 1 ? "times" : "time";
  559   my $lastScore = sprintf("%.0f%%", $problem->status * 100); # Round to whole number
  560   my ($attemptsLeft, $attemptsLeftNoun);
  561   if ($problem->max_attempts == -1) {
  562     # unlimited attempts
  563     $attemptsLeft = "unlimited";
  564     $attemptsLeftNoun = "attempts";
  565   } else {
  566     $attemptsLeft = $problem->max_attempts - $attempts;
  567     $attemptsLeftNoun = $attemptsLeft == 1 ? "attempt" : "attempts";
  568   }
  569 
  570   my $setClosed = 0;
  571   my $setClosedMessage;
  572   if (time < $set->open_date or time > $set->due_date) {
  573     $setClosed = 1;
  574     $setClosedMessage = "This problem set is closed.";
  575     if ($permissionLevel > 0) {
  576       $setClosedMessage .= " However, since you are a privileged user, additional attempts will be recorded.";
  577     } else {
  578       $setClosedMessage .= " Additional attempts will not be recorded.";
  579     }
  580   }
  581   print CGI::p(
  582     $submitAnswers ? $scoreRecordedMessage . CGI::br() : "",
  583     "You have attempted this problem $attempts $attemptsNoun.", CGI::br(),
  584     $problem->attempted
  585       ? "Your recorded score is $lastScore." . CGI::br()
  586       : "",
  587     $setClosed ? $setClosedMessage : "You have $attemptsLeft $attemptsLeftNoun remaining."
  588   );
  589   print CGI::end_div();
  590 
  591   # save state for viewOptions
  592   print CGI::hidden(
  593       -name  => "showOldAnswers",
  594       -value => $will{showOldAnswers}
  595     ),
  596     CGI::hidden(
  597       -name  => "showCorrectAnswers",
  598       -value => $will{showCorrectAnswers}
  599     ),
  600     CGI::hidden(
  601       -name  => "showHints",
  602       -value => $will{showHints}),
  603     CGI::hidden(
  604       -name  => "showSolutions",
  605       -value => $will{showSolutions},
  606     ),
  607     CGI::hidden(
  608       -name  => "displayMode",
  609       -value => $self->{displayMode}
  610     );
  611 
  612   # end of main form
  613   print CGI::endform();
  614 
  615   # stuff we need below (pull these out at the beginning?)
  616   my $ce = $self->{ce};
  617   my $root = $ce->{webworkURLs}->{root};
  618   my $courseName = $ce->{courseName};
  619 
  620   print  CGI::start_div({class=>"problemFooter"});
  621 
  622   # arguments for answer inspection button
  623   my $prof_url = $ce->{webworkURLs}->{oldProf};
  624   my $webworkURL = $ce->{webworkURLs}->{root};
  625   my $cgi_url = $prof_url;
  626   $cgi_url=~ s|/[^/]*$||;  # clip profLogin.pl
  627   my $authen_args = $self->url_authen_args();
  628   my $showPastAnswersURL = "$webworkURL/$courseName/instructor/show_answers/";
  629 
  630   # print answer inspection button
  631   if ($self->{permissionLevel} > 0) {
  632     print "\n",
  633       CGI::start_form(-method=>"POST",-action=>$showPastAnswersURL,-target=>"information"),"\n",
  634       $self->hidden_authen_fields,"\n",
  635       CGI::hidden(-name => 'course',  -value=>$courseName), "\n",
  636       CGI::hidden(-name => 'problemNumber', -value=>$problem->problem_id), "\n",
  637       CGI::hidden(-name => 'setName',  -value=>$problem->set_id), "\n",
  638       CGI::hidden(-name => 'studentUser',    -value=>$problem->user_id), "\n",
  639       CGI::p( {-align=>"left"},
  640         CGI::submit(-name => 'action',  -value=>'Show Past Answers')
  641       ), "\n",
  642       CGI::endform();
  643   }
  644 
  645   #print CGI::end_div();
  646   #
  647   #print CGI::start_div();
  648 
  649   # arguments for feedback form
  650   my $feedbackURL = "$root/$courseName/feedback/";
  651 
  652   #print feedback form
  653   print
  654     CGI::start_form(-method=>"POST", -action=>$feedbackURL),"\n",
  655     $self->hidden_authen_fields,"\n",
  656     CGI::hidden("module",             __PACKAGE__),"\n",
  657     CGI::hidden("set",                $set->set_id),"\n",
  658     CGI::hidden("problem",            $problem->problem_id),"\n",
  659     CGI::hidden("displayMode",        $self->{displayMode}),"\n",
  660     CGI::hidden("showOldAnswers",     $will{showOldAnswers}),"\n",
  661     CGI::hidden("showCorrectAnswers", $will{showCorrectAnswers}),"\n",
  662     CGI::hidden("showHints",          $will{showHints}),"\n",
  663     CGI::hidden("showSolutions",      $will{showSolutions}),"\n",
  664     CGI::p({-align=>"left"},
  665       CGI::submit(-name=>"feedbackForm", -label=>"Contact instructor")
  666     ),
  667     CGI::endform(),"\n";
  668 
  669   # FIXME print editor link
  670   # print editor link if the user is an instructor AND the file is not in temporary editing mode
  671   if ($self->{permissionLevel}>=10 and ( (not defined($self->{edit_mode}))  or $self->{edit_mode} eq 'savedFile') ) {
  672     print CGI::a({-href=>$ce->{webworkURLs}->{root}."/$courseName/instructor/pgProblemEditor/".$set->set_id.
  673     '/'.$problem->problem_id.'?'.$self->url_authen_args},'Edit this problem');
  674   }
  675 
  676   print CGI::end_div();
  677 
  678   # warning output
  679   #if ($pg->{warnings} ne "") {
  680   # print CGI::hr(), $self->warningOutput($pg->{warnings});
  681   #}
  682 
  683   # debugging stuff
  684   if (0) {
  685     print
  686       CGI::hr(),
  687       CGI::h2("debugging information"),
  688       CGI::h3("form fields"),
  689       ref2string($self->{formFields}),
  690       CGI::h3("user object"),
  691       ref2string($self->{user}),
  692       CGI::h3("set object"),
  693       ref2string($set),
  694       CGI::h3("problem object"),
  695       ref2string($problem),
  696       CGI::h3("PG object"),
  697       ref2string($pg, {'WeBWorK::PG::Translator' => 1});
  698   }
  699 
  700   return "";
  701 }
  702 
  703 ##### output utilities #####
  704 
  705 sub attemptResults($$$$$$) {
  706   my $self = shift;
  707   my $pg = shift;
  708   my $showAttemptAnswers = shift;
  709   my $showCorrectAnswers = shift;
  710   my $showAttemptResults = $showAttemptAnswers && shift;
  711   my $showSummary = shift;
  712   my $showAttemptPreview = shift || 0;
  713   my $ce = $self->{ce};
  714   my $problemResult = $pg->{result}; # the overall result of the problem
  715   my @answerNames = @{ $pg->{flags}->{ANSWER_ENTRY_ORDER} };
  716 
  717   my $showMessages = $showAttemptAnswers && grep { $pg->{answers}->{$_}->{ans_message} } @answerNames;
  718 
  719   my $basename = "equation-" . $self->{set}->psvn. "." . $self->{problem}->problem_id . "-preview";
  720   my $imgGen = WeBWorK::PG::ImageGenerator->new(
  721     tempDir  => $ce->{webworkDirs}->{tmp},
  722     dir  => $ce->{courseDirs}->{html_temp},
  723     url  => $ce->{courseURLs}->{html_temp},
  724     basename => $basename,
  725     latex  => $ce->{externalPrograms}->{latex},
  726     dvipng   => $ce->{externalPrograms}->{dvipng},
  727   );
  728 
  729   my $header;
  730   #$header .= CGI::th("Part");
  731   $header .= $showAttemptAnswers ? CGI::th("Entered")  : "";
  732   $header .= $showAttemptPreview ? CGI::th("Answer Preview")  : "";
  733   $header .= $showCorrectAnswers ? CGI::th("Correct")  : "";
  734   $header .= $showAttemptResults ? CGI::th("Result")   : "";
  735   $header .= $showMessages       ? CGI::th("messages") : "";
  736   my @tableRows = ( $header );
  737   my $numCorrect;
  738   foreach my $name (@answerNames) {
  739     my $answerResult  = $pg->{answers}->{$name};
  740     my $studentAnswer = $answerResult->{student_ans}; # original_student_ans
  741     my $preview       = ($showAttemptPreview
  742                           ? $self->previewAnswer($answerResult, $imgGen)
  743                           : "");
  744     my $correctAnswer = $answerResult->{correct_ans};
  745     my $answerScore   = $answerResult->{score};
  746     my $answerMessage = $showMessages ? $answerResult->{ans_message} : "";
  747 
  748     $numCorrect += $answerScore > 0;
  749     my $resultString = $answerScore ? "correct" : "incorrect";
  750 
  751     # get rid of the goofy prefix on the answer names (supposedly, the format
  752     # of the answer names is changeable. this only fixes it for "AnSwEr"
  753     $name =~ s/^AnSwEr//;
  754 
  755     my $row;
  756     #$row .= CGI::td($name);
  757     $row .= $showAttemptAnswers ? CGI::td(nbsp($studentAnswer)) : "";
  758     $row .= $showAttemptPreview ? CGI::td(nbsp($preview))       : "";
  759     $row .= $showCorrectAnswers ? CGI::td(nbsp($correctAnswer)) : "";
  760     $row .= $showAttemptResults ? CGI::td(nbsp($resultString))  : "";
  761     $row .= $answerMessage      ? CGI::td(nbsp($answerMessage)) : "";
  762     push @tableRows, $row;
  763   }
  764 
  765   # render equation images
  766   $imgGen->render(refresh => 1);
  767 
  768   my $numIncorrectNoun = scalar @answerNames == 1 ? "question" : "questions";
  769   my $scorePercent = sprintf("%.0f%%", $problemResult->{score} * 100);
  770   my $summary = "On this attempt, you answered $numCorrect out of "
  771     . scalar @answerNames . " $numIncorrectNoun correct, for a score of $scorePercent.";
  772   return CGI::table({-class=>"attemptResults"}, CGI::Tr(\@tableRows)) . ($showSummary ? CGI::p({class=>'emphasis'},$summary) : "");
  773 }
  774 sub nbsp {
  775   my $str = shift;
  776   ($str =~/\S/) ? $str : '&nbsp;'  ;  # returns non-breaking space for empty strings
  777                                       # tricky cases:   $str =0;
  778                                       #  $str is a complex number
  779 }
  780 sub viewOptions($) {
  781   my $self = shift;
  782   my $displayMode = $self->{displayMode};
  783   my %must = %{ $self->{must} };
  784   my %can  = %{ $self->{can}  };
  785   my %will = %{ $self->{will} };
  786 
  787   my $optionLine;
  788   $can{showOldAnswers} and $optionLine .= join "",
  789     "Show: &nbsp;".CGI::br(),
  790     CGI::checkbox(
  791       -name    => "showOldAnswers",
  792       -checked => $will{showOldAnswers},
  793       -label   => "Saved answers",
  794     ), "&nbsp;&nbsp;".CGI::br();
  795   $can{showCorrectAnswers} and $optionLine .= join "",
  796     CGI::checkbox(
  797       -name    => "showCorrectAnswers",
  798       -checked => $will{showCorrectAnswers},
  799       -label   => "Correct answers",
  800     ), "&nbsp;&nbsp;".CGI::br();
  801   $can{showHints} and $optionLine .= join "",
  802     CGI::checkbox(
  803       -name    => "showHints",
  804       -checked => $will{showHints},
  805       -label   => "Hints",
  806     ), "&nbsp;&nbsp;".CGI::br();
  807   $can{showSolutions} and $optionLine .= join "",
  808     CGI::checkbox(
  809       -name    => "showSolutions",
  810       -checked => $will{showSolutions},
  811       -label   => "Solutions",
  812     ), "&nbsp;&nbsp;".CGI::br();
  813   $optionLine and $optionLine .= join "", CGI::br();
  814 
  815   return CGI::div({-style=>"border: thin groove; padding: 1ex; margin: 2ex align: left"},
  816       "View&nbsp;equations&nbsp;as:&nbsp;&nbsp;&nbsp;&nbsp;".CGI::br(),
  817     CGI::radio_group(
  818       -name    => "displayMode",
  819       -values  => ['plainText', 'formattedText', 'images'],
  820       -default => $displayMode,
  821       -linebreak=>'true',
  822       -labels  => {
  823         plainText     => "plain",
  824         formattedText => "formatted",
  825         images        => "images",
  826       }
  827     ), CGI::br(),CGI::hr(),
  828     $optionLine,
  829     CGI::submit(-name=>"redisplay", -label=>"Save Options"),
  830   );
  831 }
  832 
  833 sub previewAnswer($$) {
  834   my ($self, $answerResult, $imgGen) = @_;
  835   my $ce            = $self->{ce};
  836   my $effectiveUser = $self->{effectiveUser};
  837   my $set           = $self->{set};
  838   my $problem       = $self->{problem};
  839   my $displayMode   = $self->{displayMode};
  840 
  841   # note: right now, we have to do things completely differently when we are
  842   # rendering math from INSIDE the translator and from OUTSIDE the translator.
  843   # so we'll just deal with each case explicitly here. there's some code
  844   # duplication that can be dealt with later by abstracting out tth/dvipng/etc.
  845 
  846   my $tex = $answerResult->{preview_latex_string};
  847 
  848   return "" unless defined $tex and $tex ne "";
  849 
  850   if ($displayMode eq "plainText") {
  851     return $tex;
  852   } elsif ($displayMode eq "formattedText") {
  853     my $tthCommand = $ce->{externalPrograms}->{tth}
  854       . " -L -f5 -r 2> /dev/null <<END_OF_INPUT; echo > /dev/null\n"
  855       . "\\(".$tex."\\)\n"
  856       . "END_OF_INPUT\n";
  857 
  858     # call tth
  859     my $result = `$tthCommand`;
  860     if ($?) {
  861       return "<b>[tth failed: $? $@]</b>";
  862     }
  863     return $result;
  864   } elsif ($displayMode eq "images") {
  865     ## how are we going to name this?
  866     #my $targetPathCommon = "/m2i/"
  867     # . $effectiveUser->user_id . "."
  868     # . $set->set_id . "."
  869     # . $problem->problem_id . "."
  870     # . $answerResult->{ans_name} . ".png";
  871     #
  872     ## figure out where to put things
  873     #my $wd = makeTempDirectory($ce->{courseDirs}->{html_temp}, "webwork-dvipng");
  874     #my $latex = $ce->{externalPrograms}->{latex};
  875     #my $dvipng = $ce->{externalPrograms}->{dvipng};
  876     #my $targetPath = $ce->{courseDirs}->{html_temp} . $targetPathCommon;
  877     #   # should use surePathToTmpFile, but we have to
  878     #   # isolate it from the problem enivronment first
  879     #my $targetURL = $ce->{courseURLs}->{html_temp} . $targetPathCommon;
  880     #
  881     ## call dvipng to generate a preview
  882     #dvipng($wd, $latex, $dvipng, $tex, $targetPath);
  883     #rmtree($wd, 0, 0);
  884     #if (-e $targetPath) {
  885     # return "<img src=\"$targetURL\" alt=\"$tex\" />";
  886     #} else {
  887     # return "<b>[math2img failed]</b>";
  888     #}
  889     $imgGen->add($answerResult->{preview_latex_string});
  890 
  891   }
  892 }
  893 
  894 ##### logging subroutine ####
  895 
  896 
  897 
  898 ##### permission queries #####
  899 
  900 # this stuff should be abstracted out into the permissions system
  901 # however, the permission system only knows about things in the
  902 # course environment and the username. hmmm...
  903 
  904 # also, i should fix these so that they have a consistent calling
  905 # format -- perhaps:
  906 #   canPERM($courseEnv, $user, $set, $problem, $permissionLevel)
  907 
  908 sub canShowCorrectAnswers($$) {
  909   my ($permissionLevel, $answerDate) = @_;
  910   return $permissionLevel > 0 || time > $answerDate;
  911 }
  912 
  913 sub canShowSolutions($$) {
  914   my ($permissionLevel, $answerDate) = @_;
  915   return canShowCorrectAnswers($permissionLevel, $answerDate);
  916 }
  917 
  918 sub canRecordAnswers($$$$$) {
  919   my ($permissionLevel, $openDate, $dueDate, $maxAttempts, $attempts) = @_;
  920   my $permHigh = $permissionLevel > 0;
  921   my $timeOK = time >= $openDate && time <= $dueDate;
  922   my $attemptsOK = $maxAttempts == -1 || $attempts <= $maxAttempts;
  923   my $recordAnswers = $permHigh || ($timeOK && $attemptsOK);
  924   return $recordAnswers;
  925 }
  926 
  927 sub canCheckAnswers($$) {
  928   my ($permissionLevel, $answerDate) = @_;
  929   my $permHigh = $permissionLevel > 0;
  930   my $timeOK = time >= $answerDate;
  931   my $recordAnswers = $permHigh || $timeOK;
  932   return $recordAnswers;
  933 }
  934 
  935 sub mustRecordAnswers($) {
  936   my ($permissionLevel) = @_;
  937   return $permissionLevel == 0;
  938 }
  939 
  940 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9