[system] / branches / rel-2-1-a1 / webwork2 / lib / WeBWorK / ContentGenerator / Problem.pm Repository:
ViewVC logotype

Diff of /branches/rel-2-1-a1/webwork2/lib/WeBWorK/ContentGenerator/Problem.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

Revision 2531 Revision 2532
1################################################################################ 1################################################################################
2# WeBWorK Online Homework Delivery System 2# WeBWorK Online Homework Delivery System
3# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ 3# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/
4# $CVSHeader: webwork-modperl/lib/WeBWorK/ContentGenerator/Problem.pm,v 1.142 2004/06/04 23:21:48 jj Exp $ 4# $CVSHeader: webwork2/lib/WeBWorK/ContentGenerator/Problem.pm,v 1.156 2004/07/15 00:13:19 sh002i Exp $
5# 5#
6# This program is free software; you can redistribute it and/or modify it under 6# This program is free software; you can redistribute it and/or modify it under
7# the terms of either: (a) the GNU General Public License as published by the 7# the terms of either: (a) the GNU General Public License as published by the
8# Free Software Foundation; either version 2, or (at your option) any later 8# Free Software Foundation; either version 2, or (at your option) any later
9# version, or (b) the "Artistic License" which comes with this package. 9# version, or (b) the "Artistic License" which comes with this package.
35use WeBWorK::DB::Utils qw(global2user user2global findDefaults); 35use WeBWorK::DB::Utils qw(global2user user2global findDefaults);
36use WeBWorK::Timing; 36use WeBWorK::Timing;
37 37
38use WeBWorK::Utils::Tasks qw(fake_set fake_problem); 38use WeBWorK::Utils::Tasks qw(fake_set fake_problem);
39 39
40############################################################ 40################################################################################
41# CGI param interface to this module (up-to-date as of v1.153)
42################################################################################
43
44# Standard params:
41# 45#
42# user 46# user - user ID of real user
43# effectiveUser 47# key - session key
44# key 48# effectiveUser - user ID of effective user
45# 49#
46# displayMode 50# Integration with PGProblemEditor:
47# showOldAnswers
48# showCorrectAnswers
49# showHints
50# showSolutions
51# 51#
52# AnSwEr# - answer blanks in problem 52# editMode - if set, indicates alternate problem source location.
53# can be "temporaryFile" or "savedFile".
53# 54#
54# redisplay - name of the "Redisplay Problem" button 55# sourceFilePath - path to file to be edited
55# submitAnswers - name of "Submit Answers" button 56# problemSeed - force problem seed to value
56# checkAnswers - name of the "Check Answers" button 57# success - success message to display
57# previewAnswers - name of the "Preview Answers" button 58# failure - failure message to display
58# 59#
59# FIXME: this table is heinously out of date 60# Rendering options:
60# 61#
62# displayMode - name of display mode to use
63#
64# showOldAnswers - request that last entered answer be shown (if allowed)
65# showCorrectAnswers - request that correct answers be shown (if allowed)
66# showHints - request that hints be shown (if allowed)
67# showSolutions - request that solutions be shown (if allowed)
68#
69# Problem interaction:
70#
71# AnSwEr# - answer blanks in problem
72#
73# redisplay - name of the "Redisplay Problem" button
74# submitAnswers - name of "Submit Answers" button
75# checkAnswers - name of the "Check Answers" button
76# previewAnswers - name of the "Preview Answers" button
77
61############################################################ 78################################################################################
79# "can" methods
80################################################################################
62 81
63# FIXME: what is this? 82# Subroutines to determine if a user "can" perform an action. Each subroutine is
64sub templateName { 83# called with the following arguments:
65 "problem"; 84#
85# ($self, $User, $PermissionLevel, $EffectiveUser, $Set, $Problem)
86
87sub can_showOldAnswers {
88 #my ($self, $User, $PermissionLevel, $EffectiveUser, $Set, $Problem) = @_;
89
90 return 1;
66} 91}
92
93sub can_showCorrectAnswers {
94 my ($self, $User, $PermissionLevel, $EffectiveUser, $Set, $Problem) = @_;
95 my $authz = $self->r->authz;
96
97 return
98 after($Set->answer_date)
99 ||
100 $authz->hasPermissions($User->user_id, "show_correct_answers_before_answer_date")
101 ;
102}
103
104sub can_showHints {
105 #my ($self, $User, $PermissionLevel, $EffectiveUser, $Set, $Problem) = @_;
106
107 return 1;
108}
109
110sub can_showSolutions {
111 my ($self, $User, $PermissionLevel, $EffectiveUser, $Set, $Problem) = @_;
112 my $authz = $self->r->authz;
113
114 return
115 after($Set->answer_date)
116 ||
117 $authz->hasPermissions($User->user_id, "show_solutions_before_answer_date")
118 ;
119}
120
121sub can_recordAnswers {
122 my ($self, $User, $PermissionLevel, $EffectiveUser, $Set, $Problem) = @_;
123 my $authz = $self->r->authz;
124 if ($User->user_id ne $EffectiveUser->user_id) {
125 return $authz->hasPermissions($User->user_id, "record_answers_when_acting_as_student");
126 }
127 if (before($Set->open_date)) {
128 return $authz->hasPermissions($User->user_id, "record_answers_before_open_date");
129 } elsif (between($Set->open_date, $Set->due_date)) {
130 my $max_attempts = $Problem->max_attempts;
131 my $attempts_used = $Problem->num_correct + $Problem->num_incorrect + 1;
132 if ($max_attempts == -1 or $attempts_used < $max_attempts) {
133 return $authz->hasPermissions($User->user_id, "record_answers_after_open_date_with_attempts");
134 } else {
135 return $authz->hasPermissions($User->user_id, "record_answers_after_open_date_without_attempts");
136 }
137 } elsif (between($Set->due_date, $Set->answer_date)) {
138 return $authz->hasPermissions($User->user_id, "record_answers_after_due_date");
139 } elsif (after($Set->answer_date)) {
140 return $authz->hasPermissions($User->user_id, "record_answers_after_answer_date");
141 }
142}
143
144sub can_checkAnswers {
145 my ($self, $User, $PermissionLevel, $EffectiveUser, $Set, $Problem) = @_;
146 my $authz = $self->r->authz;
147
148 if (before($Set->open_date)) {
149 return $authz->hasPermissions($User->user_id, "check_answers_before_open_date");
150 } elsif (between($Set->open_date, $Set->due_date)) {
151 my $max_attempts = $Problem->max_attempts;
152 my $attempts_used = $Problem->num_correct + $Problem->num_incorrect + 1;
153 if ($max_attempts == -1 or $attempts_used < $max_attempts) {
154 return $authz->hasPermissions($User->user_id, "check_answers_after_open_date_with_attempts");
155 } else {
156 return $authz->hasPermissions($User->user_id, "check_answers_after_open_date_without_attempts");
157 }
158 } elsif (between($Set->due_date, $Set->answer_date)) {
159 return $authz->hasPermissions($User->user_id, "check_answers_after_due_date");
160 } elsif (after($Set->answer_date)) {
161 return $authz->hasPermissions($User->user_id, "check_answers_after_answer_date");
162 }
163}
164
165# Helper functions for calculating times
166sub before { return time < $_[0] }
167sub after { return time > $_[0] }
168sub between { my $t = time; return $t > $_[0] && $t < $_[1] }
169
170################################################################################
171# output utilities
172################################################################################
173
174sub attemptResults {
175 my $self = shift;
176 my $pg = shift;
177 my $showAttemptAnswers = shift;
178 my $showCorrectAnswers = shift;
179 my $showAttemptResults = $showAttemptAnswers && shift;
180 my $showSummary = shift;
181 my $showAttemptPreview = shift || 0;
182
183 my $ce = $self->r->ce;
184
185 my $problemResult = $pg->{result}; # the overall result of the problem
186 my @answerNames = @{ $pg->{flags}->{ANSWER_ENTRY_ORDER} };
187
188 my $showMessages = $showAttemptAnswers && grep { $pg->{answers}->{$_}->{ans_message} } @answerNames;
189
190 my $basename = "equation-" . $self->{set}->psvn. "." . $self->{problem}->problem_id . "-preview";
191
192 # to make grabbing these options easier, we'll pull them out now...
193 my %imagesModeOptions = %{$ce->{pg}->{displayModeOptions}->{images}};
194
195 my $imgGen = WeBWorK::PG::ImageGenerator->new(
196 tempDir => $ce->{webworkDirs}->{tmp},
197 latex => $ce->{externalPrograms}->{latex},
198 dvipng => $ce->{externalPrograms}->{dvipng},
199 useCache => 1,
200 cacheDir => $ce->{webworkDirs}->{equationCache},
201 cacheURL => $ce->{webworkURLs}->{equationCache},
202 cacheDB => $ce->{webworkFiles}->{equationCacheDB},
203 dvipng_align => $imagesModeOptions{dvipng_align},
204 dvipng_depth_db => $imagesModeOptions{dvipng_depth_db},
205 );
206
207 my $header;
208 #$header .= CGI::th("Part");
209 $header .= $showAttemptAnswers ? CGI::th("Entered") : "";
210 $header .= $showAttemptPreview ? CGI::th("Answer Preview") : "";
211 $header .= $showCorrectAnswers ? CGI::th("Correct") : "";
212 $header .= $showAttemptResults ? CGI::th("Result") : "";
213 $header .= $showMessages ? CGI::th("Messages") : "";
214 my @tableRows = ( $header );
215 my $numCorrect = 0;
216 foreach my $name (@answerNames) {
217 my $answerResult = $pg->{answers}->{$name};
218 my $studentAnswer = $answerResult->{student_ans}; # original_student_ans
219 my $preview = ($showAttemptPreview
220 ? $self->previewAnswer($answerResult, $imgGen)
221 : "");
222 my $correctAnswer = $answerResult->{correct_ans};
223 my $answerScore = $answerResult->{score};
224 my $answerMessage = $showMessages ? $answerResult->{ans_message} : "";
225 #FIXME --Can we be sure that $answerScore is an integer-- could the problem give partial credit?
226 $numCorrect += $answerScore > 0;
227 my $resultString = $answerScore == 1 ? "correct" : "incorrect";
228
229 # get rid of the goofy prefix on the answer names (supposedly, the format
230 # of the answer names is changeable. this only fixes it for "AnSwEr"
231 #$name =~ s/^AnSwEr//;
232
233 my $row;
234 #$row .= CGI::td($name);
235 $row .= $showAttemptAnswers ? CGI::td($self->nbsp($studentAnswer)) : "";
236 $row .= $showAttemptPreview ? CGI::td($self->nbsp($preview)) : "";
237 $row .= $showCorrectAnswers ? CGI::td($self->nbsp($correctAnswer)) : "";
238 $row .= $showAttemptResults ? CGI::td($self->nbsp($resultString)) : "";
239 $row .= $showMessages ? CGI::td($self->nbsp($answerMessage)) : "";
240 push @tableRows, $row;
241 }
242
243 # render equation images
244 $imgGen->render(refresh => 1);
245
246# my $numIncorrectNoun = scalar @answerNames == 1 ? "question" : "questions";
247 my $scorePercent = sprintf("%.0f%%", $problemResult->{score} * 100);
248# FIXME -- I left the old code in in case we have to back out.
249# my $summary = "On this attempt, you answered $numCorrect out of "
250# . scalar @answerNames . " $numIncorrectNoun correct, for a score of $scorePercent.";
251 my $summary = "";
252 if (scalar @answerNames == 1) {
253 if ($numCorrect == scalar @answerNames) {
254 $summary .= CGI::div({class=>"ResultsWithoutError"},"The above answer is correct.");
255 } else {
256 $summary .= CGI::div({class=>"ResultsWithError"},"The above answer is NOT correct.");
257 }
258 } else {
259 if ($numCorrect == scalar @answerNames) {
260 $summary .= CGI::div({class=>"ResultsWithoutError"},"All of the above answers are correct.");
261 } else {
262 $summary .= CGI::div({class=>"ResultsWithError"},"At least one of the above answers is NOT correct.");
263 }
264 }
265
266 return
267 CGI::table({-class=>"attemptResults"}, CGI::Tr(\@tableRows))
268 . ($showSummary ? CGI::p({class=>'emphasis'},$summary) : "");
269}
270
271sub viewOptions {
272 my ($self) = @_;
273 my $ce = $self->r->ce;
274
275 # don't show options if we don't have anything to show
276 return if $self->{invalidSet} or $self->{invalidProblem};
277 return unless $self->{isOpen};
278
279 my $displayMode = $self->{displayMode};
280 my %must = %{ $self->{must} };
281 my %can = %{ $self->{can} };
282 my %will = %{ $self->{will} };
283
284 my $optionLine;
285 $can{showOldAnswers} and $optionLine .= join "",
286 "Show: &nbsp;".CGI::br(),
287 CGI::checkbox(
288 -name => "showOldAnswers",
289 -checked => $will{showOldAnswers},
290 -label => "Saved answers",
291 ), "&nbsp;&nbsp;".CGI::br();
292
293 $optionLine and $optionLine .= join "", CGI::br();
294
295 my %display_modes = %{WeBWorK::PG::DISPLAY_MODES()};
296 my @active_modes = grep { exists $display_modes{$_} }
297 @{$ce->{pg}->{displayModes}};
298 my $modeLine = (scalar(@active_modes) > 1) ?
299 "View&nbsp;equations&nbsp;as:&nbsp;&nbsp;&nbsp;&nbsp;".CGI::br().
300 CGI::radio_group(
301 -name => "displayMode",
302 -values => \@active_modes,
303 -default => $displayMode,
304 -linebreak=>'true',
305 -labels => {
306 plainText => "plain",
307 formattedText => "formatted",
308 images => "images",
309 jsMath => "jsMath",
310 asciimath => "asciimath",
311 },
312 ). CGI::br().CGI::hr() : '';
313
314 return CGI::div({-style=>"border: thin groove; padding: 1ex; margin: 2ex align: left"},
315 $modeLine,
316 $optionLine,
317 CGI::submit(-name=>"redisplay", -label=>"Apply Options"),
318 );
319}
320
321sub previewAnswer {
322 my ($self, $answerResult, $imgGen) = @_;
323 my $ce = $self->r->ce;
324 my $effectiveUser = $self->{effectiveUser};
325 my $set = $self->{set};
326 my $problem = $self->{problem};
327 my $displayMode = $self->{displayMode};
328
329 # note: right now, we have to do things completely differently when we are
330 # rendering math from INSIDE the translator and from OUTSIDE the translator.
331 # so we'll just deal with each case explicitly here. there's some code
332 # duplication that can be dealt with later by abstracting out tth/dvipng/etc.
333
334 my $tex = $answerResult->{preview_latex_string};
335
336 return "" unless defined $tex and $tex ne "";
337
338 if ($displayMode eq "plainText") {
339 return $tex;
340 } elsif ($displayMode eq "formattedText") {
341 my $tthCommand = $ce->{externalPrograms}->{tth}
342 . " -L -f5 -r 2> /dev/null <<END_OF_INPUT; echo > /dev/null\n"
343 . "\\(".$tex."\\)\n"
344 . "END_OF_INPUT\n";
345
346 # call tth
347 my $result = `$tthCommand`;
348 if ($?) {
349 return "<b>[tth failed: $? $@]</b>";
350 } else {
351 return $result;
352 }
353 } elsif ($displayMode eq "images") {
354 $imgGen->add($tex);
355 } elsif ($displayMode eq "jsMath") {
356 return '<DIV CLASS="math">'.$tex.'</DIV>' ;
357 }
358}
359
360################################################################################
361# Template escape implementations
362################################################################################
67 363
68sub pre_header_initialize { 364sub pre_header_initialize {
69 my ($self) = @_; 365 my ($self) = @_;
70 my $r = $self->r; 366 my $r = $self->r;
71 my $ce = $r->ce; 367 my $ce = $r->ce;
72 my $db = $r->db; 368 my $db = $r->db;
369 my $authz = $r->authz;
73 my $urlpath = $r->urlpath; 370 my $urlpath = $r->urlpath;
74 371
75 my $setName = $urlpath->arg("setID"); 372 my $setName = $urlpath->arg("setID");
76 my $problemNumber = $r->urlpath->arg("problemID"); 373 my $problemNumber = $r->urlpath->arg("problemID");
77 my $userName = $r->param('user'); 374 my $userName = $r->param('user');
97 # obtain the merged problem for $effectiveUser 394 # obtain the merged problem for $effectiveUser
98 my $problem = $db->getMergedProblem($effectiveUserName, $setName, $problemNumber); # checked 395 my $problem = $db->getMergedProblem($effectiveUserName, $setName, $problemNumber); # checked
99 396
100 my $editMode = $r->param("editMode"); 397 my $editMode = $r->param("editMode");
101 398
102 if ($permissionLevel > 0) { 399 if ($authz->hasPermissions($userName, "modify_problem_sets")) {
103 # professors are allowed to fabricate sets and problems not 400 # professors are allowed to fabricate sets and problems not
104 # assigned to them (or anyone). this allows them to use the 401 # assigned to them (or anyone). this allows them to use the
105 # editor to 402 # editor to
106 403
107 # if that is not yet defined obtain the global set, convert 404 # if that is not yet defined obtain the global set, convert
130 my $userProblemClass = $db->{problem_user}->{record}; 427 my $userProblemClass = $db->{problem_user}->{record};
131 my $globalProblem = $db->getGlobalProblem($setName, $problemNumber); # checked 428 my $globalProblem = $db->getGlobalProblem($setName, $problemNumber); # checked
132 # if the global problem doesn't exist either, bail! 429 # if the global problem doesn't exist either, bail!
133 if(not defined $globalProblem) { 430 if(not defined $globalProblem) {
134 my $sourceFilePath = $r->param("sourceFilePath"); 431 my $sourceFilePath = $r->param("sourceFilePath");
432 # These are problems from setmaker. If declared invalid, they won't come up
433 $self->{invalidProblem} = $self->{invalidSet} = 1 unless defined $sourceFilePath;
135 die "Problem $problemNumber in set $setName does not exist" 434# die "Problem $problemNumber in set $setName does not exist" unless defined $sourceFilePath;
136 unless defined $sourceFilePath;
137 $problem = fake_problem($db); 435 $problem = fake_problem($db);
138 $problem->problem_id(1); 436 $problem->problem_id(1);
139 $problem->source_file($sourceFilePath); 437 $problem->source_file($sourceFilePath);
140 $problem->user_id($effectiveUserName); 438 $problem->user_id($effectiveUserName);
141 } else { 439 } else {
161 if (defined $sourceFilePath and 459 if (defined $sourceFilePath and
162 (not defined $editMode or $editMode eq "temporaryFile")) { 460 (not defined $editMode or $editMode eq "temporaryFile")) {
163 $problem->source_file($sourceFilePath); 461 $problem->source_file($sourceFilePath);
164 } 462 }
165 463
464 # if the problem does not have a source file or no source file has been passed in
465 # then this is really an invalid problem (probably from a bad URL)
466 $self->{invalidProblem} = not (defined $sourceFilePath or $problem->source_file);
467
166 # if the caller is asking to override the problem seed, do so 468 # if the caller is asking to override the problem seed, do so
167 my $problemSeed = $r->param("problemSeed"); 469 my $problemSeed = $r->param("problemSeed");
168 if (defined $problemSeed) { 470 if (defined $problemSeed) {
169 $problem->problem_seed($problemSeed); 471 $problem->problem_seed($problemSeed);
170 } 472 }
172 my $publishedClass = ($set->published) ? "Published" : "Unpublished"; 474 my $publishedClass = ($set->published) ? "Published" : "Unpublished";
173 my $publishedText = ($set->published) ? "visible to students." : "hidden from students."; 475 my $publishedText = ($set->published) ? "visible to students." : "hidden from students.";
174 $self->addmessage(CGI::p("This set is " . CGI::font({class=>$publishedClass}, $publishedText))); 476 $self->addmessage(CGI::p("This set is " . CGI::font({class=>$publishedClass}, $publishedText)));
175 } else { 477 } else {
176 478
177 $self->addmessage(CGI::div({class=>"ResultsWithError"}, CGI::p("This problem will not count towards your grade."))) unless $problem->value;;
178 # students can't view problems not assigned to them 479 # students can't view problems not assigned to them
179 480
180 # A set is valid if it exists and if it is either published or the user is privileged. 481 # A set is valid if it exists and if it is either published or the user is privileged.
181 $self->{invalidSet} = (grep /$setName/, $db->listUserSets($effectiveUserName)) == 0 || !($set->published || $permissionLevel->permission > 0); # this is redundant because of the above if 482 $self->{invalidSet} = ((grep /^$setName/, $db->listUserSets($effectiveUserName)) == 0)
483 || not defined $set
484 || !($set->published || $authz->hasPermissions($userName, "view_unpublished_sets"));
182 $self->{invalidProblem} = (grep /$problemNumber/, $db->listUserProblems($effectiveUserName, $setName)) == 0 || !($set->published || $permissionLevel->permission > 0);; 485 $self->{invalidProblem} = ((grep /^$problemNumber/, $db->listUserProblems($effectiveUserName, $setName)) == 0)
486 || not defined $problem
487 || !($set->published || $authz->hasPermissions($userName, "view_unpublished_sets"));
183 488
489 $self->addbadmessage(CGI::p("This problem will not count towards your grade.")) if $problem and not $problem->value and not $self->{invalidProblem};
184 } 490 }
185 491
186 $self->{userName} = $userName; 492 $self->{userName} = $userName;
187 $self->{effectiveUserName} = $effectiveUserName; 493 $self->{effectiveUserName} = $effectiveUserName;
188 $self->{user} = $user; 494 $self->{user} = $user;
189 $self->{effectiveUser} = $effectiveUser; 495 $self->{effectiveUser} = $effectiveUser;
190 $self->{permissionLevel} = $permissionLevel; 496 $self->{permissionLevel} = $permissionLevel;
211 $self->{formFields} = $formFields; 517 $self->{formFields} = $formFields;
212 518
213 # get result and send to message 519 # get result and send to message
214 my $success = $r->param("sucess"); 520 my $success = $r->param("sucess");
215 my $failure = $r->param("failure"); 521 my $failure = $r->param("failure");
216 $self->addmessage(CGI::div({class=>"ResultsWithError"}, CGI::p($failure))) if $failure; 522 $self->addbadmessage(CGI::p($failure)) if $failure;
217 $self->addmessage(CGI::div({class=>"ResultsWithoutError"}, CGI::p($success))) if $success; 523 $self->addgoodmessage(CGI::p($success)) if $success;
218 524
219 # now that we've set all the necessary variables quit out if the set or problem is invalid 525 # now that we've set all the necessary variables quit out if the set or problem is invalid
220 return if $self->{invalidSet} || $self->{invalidProblem}; 526 return if $self->{invalidSet} || $self->{invalidProblem};
221 527
222 ##### permissions ##### 528 ##### permissions #####
223 529
224 # are we allowed to view this problem? 530 # are we allowed to view this problem?
225 $self->{isOpen} = time >= $set->open_date || $permissionLevel > 0; 531 $self->{isOpen} = after($set->open_date) || $authz->hasPermissions($userName, "view_unopened_sets");
226 return unless $self->{isOpen}; 532 return unless $self->{isOpen};
227 533
228 # what does the user want to do? 534 # what does the user want to do?
229 my %want = ( 535 my %want = (
230 showOldAnswers => $r->param("showOldAnswers") || $ce->{pg}->{options}->{showOldAnswers}, 536 showOldAnswers => $r->param("showOldAnswers") || $ce->{pg}->{options}->{showOldAnswers},
239 my %must = ( 545 my %must = (
240 showOldAnswers => 0, 546 showOldAnswers => 0,
241 showCorrectAnswers => 0, 547 showCorrectAnswers => 0,
242 showHints => 0, 548 showHints => 0,
243 showSolutions => 0, 549 showSolutions => 0,
244 recordAnswers => mustRecordAnswers($permissionLevel), 550 recordAnswers => ! $authz->hasPermissions($userName, "avoid_recording_answers"),
245 checkAnswers => 0, 551 checkAnswers => 0,
246 ); 552 );
247 553
248 # does the user have permission to use certain options? 554 # does the user have permission to use certain options?
555 my @args = ($user, $PermissionLevel, $effectiveUser, $set, $problem);
249 my %can = ( 556 my %can = (
250 showOldAnswers => 1, 557 showOldAnswers => $self->can_showOldAnswers(@args),
251 showCorrectAnswers => canShowCorrectAnswers($permissionLevel, $set->answer_date), 558 showCorrectAnswers => $self->can_showCorrectAnswers(@args),
252 showHints => 1, 559 showHints => $self->can_showHints(@args),
253 showSolutions => canShowSolutions($permissionLevel, $set->answer_date), 560 showSolutions => $self->can_showSolutions(@args),
254 recordAnswers => canRecordAnswers($permissionLevel, $set->open_date, $set->due_date, 561 recordAnswers => $self->can_recordAnswers(@args),
255 $problem->max_attempts, $problem->num_correct + $problem->num_incorrect + 1), 562 checkAnswers => $self->can_checkAnswers(@args),
256 # attempts=num_correct+num_incorrect+1, as this happens before updating $problem
257 checkAnswers => canCheckAnswers($permissionLevel, $set->due_date),
258 ); 563 );
259 564
565# # does the user have permission to use certain options?
566# my %can = (
567# showOldAnswers => 1,
568# showCorrectAnswers => canShowCorrectAnswers($permissionLevel, $set->answer_date),
569# showHints => 1,
570# showSolutions => canShowSolutions($permissionLevel, $set->answer_date),
571# recordAnswers => canRecordAnswers($permissionLevel, $set->open_date, $set->due_date,
572# $problem->max_attempts, $problem->num_correct + $problem->num_incorrect + 1),
573# # attempts=num_correct+num_incorrect+1, as this happens before updating $problem
574# checkAnswers => canCheckAnswers($permissionLevel, $set->due_date),
575# );
576#
260 # more complicated logic for showing check answer button: 577# # more complicated logic for showing check answer button:
261 # checkAnswers button shows up after due date -- once a student can't record anymore 578# # checkAnswers button shows up after due date -- once a student can't record anymore
262 # checkAnswers button always shows up when an instructor or TA is acting 579# # checkAnswers button always shows up when an instructor or TA is acting
263 # as someone else (the $user and $effectiveUserName aren't the same). 580# # as someone else (the $user and $effectiveUserName aren't the same).
264 $can{checkAnswers} = ( 581# $can{checkAnswers} = (
265 # $can{recordAnswers} will be false if the due date has passed OR the 582# # $can{recordAnswers} will be false if the due date has passed OR the
266 # student has used up all of her attempts 583# # student has used up all of her attempts
267 ($can{checkAnswers} and not $can{recordAnswers}) 584# ($can{checkAnswers} and not $can{recordAnswers})
268 or 585# or
269 ( 586# (
270 # FIXME: this is not the right way to check for this. 587# # FIXME: this is not the right way to check for this.
271 # also, canCheckAnswers() will show this button if the permission 588# # also, canCheckAnswers() will show this button if the permission
272 # level is positive, which is always true when an instructor is 589# # level is positive, which is always true when an instructor is
273 # acting as a student 590# # acting as a student
274 defined($userName) 591# defined($userName)
275 and 592# and
276 defined($effectiveUserName) 593# defined($effectiveUserName)
277 and 594# and
278 ($userName ne $effectiveUserName) 595# ($userName ne $effectiveUserName)
279 ) 596# )
280 ); 597# );
281 598#
282 # more complicated logif for showing "submit answer" button: 599# # more complicated logic for showing "submit answer" button:
283 # We hide the submit answer button if someone is acting as a student 600# # We hide the submit answer button if someone is acting as a student
284 # This prevents errors where you accidently submit the answer for a student 601# # This prevents errors where you accidently submit the answer for a student
285 # Not sure whether this a feature or a bug 602# # Not sure whether this a feature or a bug
286 $can{recordAnswers} = ( 603# $can{recordAnswers} = (
287 $can{recordAnswers} 604# $can{recordAnswers}
288 and not 605# and not
289 ( 606# (
290 # FIXME: this is not the right way to check for this. 607# # FIXME: this is not the right way to check for this.
291 defined($userName) 608# defined($userName)
292 and 609# and
293 defined($effectiveUserName) 610# defined($effectiveUserName)
294 and 611# and
295 ($userName ne $effectiveUserName) 612# ($userName ne $effectiveUserName)
296 ) 613# )
297 ); 614# );
298 615
299 # final values for options 616 # final values for options
300 my %will; 617 my %will;
301 foreach (keys %must) { 618 foreach (keys %must) {
302 $will{$_} = $can{$_} && ($want{$_} || $must{$_}); 619 $will{$_} = $can{$_} && ($want{$_} || $must{$_});
329 processAnswers => 1, 646 processAnswers => 1,
330 }, 647 },
331 ); 648 );
332 649
333 $WeBWorK::timer->continue("end pg processing") if defined($WeBWorK::timer); 650 $WeBWorK::timer->continue("end pg processing") if defined($WeBWorK::timer);
651
334 ##### fix hint/solution options ##### 652 ##### fix hint/solution options #####
335 653
336 $can{showHints} &&= $pg->{flags}->{hintExists} 654 $can{showHints} &&= $pg->{flags}->{hintExists}
337 &&= $pg->{flags}->{showHintLimit}<=$pg->{state}->{num_of_incorrect_ans}; 655 &&= $pg->{flags}->{showHintLimit}<=$pg->{state}->{num_of_incorrect_ans};
338 $can{showSolutions} &&= $pg->{flags}->{solutionExists}; 656 $can{showSolutions} &&= $pg->{flags}->{solutionExists};
383 CGI::end_div(), 701 CGI::end_div(),
384 CGI::end_form() 702 CGI::end_form()
385 ); 703 );
386} 704}
387 705
388#sub path {
389# my $self = shift;
390# my $args = $_[-1];
391# my $setName = $self->{set}->set_id;
392# my $problemNumber = $self->{problem}->problem_id;
393#
394# my $ce = $self->{ce};
395# my $root = $ce->{webworkURLs}->{root};
396# my $courseName = $ce->{courseName};
397# return $self->pathMacro($args,
398# "Home" => "$root",
399# $courseName => "$root/$courseName",
400# $setName => "$root/$courseName/$setName",
401# "Problem $problemNumber" => "",
402# );
403#}
404
405sub siblings { 706sub siblings {
406 my ($self) = @_; 707 my ($self) = @_;
407 my $r = $self->r; 708 my $r = $self->r;
408 my $db = $r->db; 709 my $db = $r->db;
409 my $urlpath = $r->urlpath; 710 my $urlpath = $r->urlpath;
486 787
487 # using the url arguments won't break if the set/problem are invalid 788 # using the url arguments won't break if the set/problem are invalid
488 my $setID = $self->r->urlpath->arg("setID"); 789 my $setID = $self->r->urlpath->arg("setID");
489 my $problemID = $self->r->urlpath->arg("problemID"); 790 my $problemID = $self->r->urlpath->arg("problemID");
490 791
491 #my $setID = $self->{set}->set_id;
492 #my $problemID = $self->{problem}->problem_id;
493
494 return "$setID : $problemID"; 792 return "$setID : $problemID";
495} 793}
496 794
497sub body { 795sub body {
498 my $self = shift; 796 my $self = shift;
499 my $r = $self->r; 797 my $r = $self->r;
500 my $ce = $r->ce; 798 my $ce = $r->ce;
501 my $db = $r->db; 799 my $db = $r->db;
800 my $authz = $r->authz;
502 my $urlpath = $r->urlpath; 801 my $urlpath = $r->urlpath;
802 my $user = $r->param('user');
803 my $effectiveUser = $r->param('effectiveUser');
503 804
504 if ($self->{invalidSet}) { 805 if ($self->{invalidSet}) {
505 return CGI::div({class=>"ResultsWithError"}, 806 return CGI::div({class=>"ResultsWithError"},
506 CGI::p("The selected problem set (" . $urlpath->arg("setID") . ") is not a valid set for " . $r->param("effectiveUser") . ".")); 807 CGI::p("The selected problem set (" . $urlpath->arg("setID") . ") is not a valid set for " . $r->param("effectiveUser") . "."));
507 } 808 }
527 my %can = %{ $self->{can} }; 828 my %can = %{ $self->{can} };
528 my %must = %{ $self->{must} }; 829 my %must = %{ $self->{must} };
529 my %will = %{ $self->{will} }; 830 my %will = %{ $self->{will} };
530 my $pg = $self->{pg}; 831 my $pg = $self->{pg};
531 832
532 #my $root = $ce->{webworkURLs}->{root};
533 my $courseName = $urlpath->arg("courseID"); 833 my $courseName = $urlpath->arg("courseID");
534 834
535 #####create Editor link ##### 835 # FIXME: move editor link to top, next to problem number.
536 ## print editor link if the user is an instructor AND the file is not in temporary editing mode 836 # format as "[edit]" like we're doing with course info file, etc.
537 #my $editorLinkMessage = ''; 837 # add edit link for set as well.
538 ## and ( (not defined($self->{editMode})) or $self->{editMode} eq 'savedFile') # FIXME is this needed?
539 #if ($self->{permissionLevel}>=10 ) {
540 # $editorLinkMessage = CGI::a({-href=>$ce->{webworkURLs}->{root}."/$courseName/instructor/pgProblemEditor/".
541 # $set->set_id.'/'.$problem->problem_id.'?'.$self->url_authen_args},'Edit this problem');
542 #}
543
544 my $editorLink = ""; 838 my $editorLink = "";
545 # if we are here without a real problem set, carry that through 839 # if we are here without a real problem set, carry that through
546 my $forced_field = []; 840 my $forced_field = [];
547 $forced_field = ['sourceFilePath' => $r->param("sourceFilePath")] if 841 $forced_field = ['sourceFilePath' => $r->param("sourceFilePath")] if
548 ($set->set_id eq 'Undefined_Set'); 842 ($set->set_id eq 'Undefined_Set');
549 if ($self->{permissionLevel}>=10) { 843 if ($authz->hasPermissions($user, "modify_problem_sets")) {
550 my $editorPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", 844 my $editorPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor",
551 courseID => $courseName, setID => $set->set_id, problemID => $problem->problem_id); 845 courseID => $courseName, setID => $set->set_id, problemID => $problem->problem_id);
552 my $editorURL = $self->systemLink($editorPage, params=>$forced_field); 846 my $editorURL = $self->systemLink($editorPage, params=>$forced_field);
553 $editorLink = CGI::a({href=>$editorURL}, "Edit this problem"); 847 $editorLink = CGI::a({href=>$editorURL}, "Edit this problem");
554 } 848 }
622 $pureProblem->last_answer."\t". 916 $pureProblem->last_answer."\t".
623 $pureProblem->num_correct."\t". 917 $pureProblem->num_correct."\t".
624 $pureProblem->num_incorrect 918 $pureProblem->num_incorrect
625 ); 919 );
626 } else { 920 } else {
627 if (time < $set->open_date or time > $set->due_date) { 921 if (before($set->open_date) or after($set->due_date)) {
628 $scoreRecordedMessage = "Your score was not recorded because this problem set is closed."; 922 $scoreRecordedMessage = "Your score was not recorded because this problem set is closed.";
629 } else { 923 } else {
630 $scoreRecordedMessage = "Your score was not recorded."; 924 $scoreRecordedMessage = "Your score was not recorded.";
631 } 925 }
632 } 926 }
633 } else { 927 } else {
634 $scoreRecordedMessage = "Your score was not recorded because this problem has not been built for you."; 928 $scoreRecordedMessage = "Your score was not recorded because this problem has not been assigned to you.";
635 } 929 }
636 } 930 }
637 931
638 # logging student answers 932 # logging student answers
639 933
669 ##### output ##### 963 ##### output #####
670 964
671 print CGI::start_div({class=>"problemHeader"}); 965 print CGI::start_div({class=>"problemHeader"});
672 966
673 # custom message for editor 967 # custom message for editor
674 if ($permissionLevel >= 10 and defined $editMode) { 968 if ($authz->hasPermissions($user, "modify_problem_sets") and defined $editMode) {
675 if ($editMode eq "temporaryFile") { 969 if ($editMode eq "temporaryFile") {
676 print CGI::p(CGI::i("Editing temporary file: ", $problem->source_file)); 970 print CGI::p(CGI::i("Editing temporary file: ", $problem->source_file));
677 } elsif ($editMode eq "savedFile") { 971 } elsif ($editMode eq "savedFile") {
678 # FIXME: this is all done automatically if submitError exists. 972 # taken care of in the initialization phase
679 #if ( defined($r->param('submiterror')) and $r->param('submiterror') ) {
680 # FIXME The following line doesn't work because the submiterror hook has already been called.
681 # The actions below should take place during the initialization phase.
682 # $self->{submiterror} .= $r->param('submiterror');
683 # print CGI::p(CGI::div({class=>'ResultsWithError'},$self->{submiterror}));
684 #} else {
685 # print CGI::p(CGI::div({ class=>'ResultsWithoutError'}, "Problem saved to: ", $problem->source_file));
686 #}
687 } 973 }
688 } 974 }
689 #FIXME we need error messages here if the problem was really not saved. 975
690 # attempt summary 976 # attempt summary
691 #FIXME -- the following is a kludge: if showPartialCorrectAnswers is negative don't show anything. 977 #FIXME -- the following is a kludge: if showPartialCorrectAnswers is negative don't show anything.
692 # until after the due date 978 # until after the due date
693 # do I need to check $wills{howCorrectAnswers} to make preflight work?? 979 # do I need to check $will{showCorrectAnswers} to make preflight work??
694 if (($pg->{flags}->{showPartialCorrectAnswers}>= 0 and $submitAnswers) ) { 980 if (($pg->{flags}->{showPartialCorrectAnswers} >= 0 and $submitAnswers) ) {
695 # print this if user submitted answers OR requested correct answers 981 # print this if user submitted answers OR requested correct answers
696 982
697 print $self->attemptResults($pg, 1, 983 print $self->attemptResults($pg, 1,
698 $will{showCorrectAnswers}, 984 $will{showCorrectAnswers},
699 $pg->{flags}->{showPartialCorrectAnswers}, 1, 1); 985 $pg->{flags}->{showPartialCorrectAnswers}, 1, 1);
714 # show attempt previews 1000 # show attempt previews
715 } 1001 }
716 1002
717 print CGI::end_div(); 1003 print CGI::end_div();
718 1004
1005 # main form
1006 print CGI::startform("POST", $r->uri);
1007 print $self->hidden_authen_fields;
1008
719 print CGI::start_div({class=>"problem"}); 1009 print CGI::start_div({class=>"problem"});
720
721 # main form
722 print
723 CGI::startform("POST", $r->uri),
724 $self->hidden_authen_fields,
725 CGI::p($pg->{body_text}), 1010 print CGI::p($pg->{body_text});
726 CGI::p($pg->{result}->{msg} ? CGI::b("Note: ") : "", CGI::i($pg->{result}->{msg})), 1011 print CGI::p(CGI::b("Note: "), CGI::i($pg->{result}->{msg})) if $pg->{result}->{msg};
727 CGI::p( 1012 print CGI::end_div();
1013
1014 print CGI::start_p();
1015
728 ($can{showCorrectAnswers} 1016 if ($can{showCorrectAnswers}) {
729 ? CGI::checkbox( 1017 print CGI::checkbox(
730 -name => "showCorrectAnswers", 1018 -name => "showCorrectAnswers",
731 -checked => $will{showCorrectAnswers}, 1019 -checked => $will{showCorrectAnswers},
732 -label => "Show correct answers", 1020 -label => "Show correct answers",
733 ) ." "
734 : "" ),
735 ($can{showHints}
736 ? '<div style="color:red">'. CGI::checkbox(
737 -name => "showHints",
738 -checked => $will{showHints},
739 -label => "Show Hints",
740 ) . "</div> "
741 : " " ),
742 ($can{showSolutions}
743 ? CGI::checkbox(
744 -name => "showSolutions",
745 -checked => $will{showSolutions},
746 -label => "Show Solutions",
747 ) . " "
748 : " " ),CGI::br(),
749 CGI::submit(-name=>"previewAnswers",
750 -label=>"Preview Answers"),
751 ($can{recordAnswers}
752 ? CGI::submit(-name=>"submitAnswers",
753 -label=>"Submit Answers")
754 : ""),
755 ( $can{checkAnswers}
756 ? CGI::submit(-name=>"checkAnswers",
757 -label=>"Check Answers")
758 : ""),
759 ); 1021 );
1022 }
1023 if ($can{showHints}) {
1024 print CGI::div({style=>"color:red"},
1025 CGI::checkbox(
1026 -name => "showHints",
1027 -checked => $will{showHints},
1028 -label => "Show Hints",
1029 )
1030 );
1031 }
1032 if ($can{showSolutions}) {
1033 print CGI::checkbox(
1034 -name => "showSolutions",
1035 -checked => $will{showSolutions},
1036 -label => "Show Solutions",
1037 );
1038 }
1039
1040 if ($can{showCorrectAnswers} or $can{showHints} or $can{showSolutions}) {
1041 print CGI::br();
1042 }
1043
1044 print CGI::submit(-name=>"previewAnswers", -label=>"Preview Answers");
1045 if ($can{checkAnswers}) {
1046 print CGI::submit(-name=>"checkAnswers", -label=>"Check Answers");
1047 }
1048 if ($can{recordAnswers}) {
1049 if ($user ne $effectiveUser) {
1050 # if acting as a student, make it clear that answer submissions will
1051 # apply to the student's records, not the professor's.
1052 print CGI::submit(-name=>"submitAnswers", -label=>"Submit Answers for $effectiveUser");
1053 } else {
1054 print CGI::submit(-name=>"submitAnswers", -label=>"Submit Answers");
1055 }
1056 }
1057
760 print CGI::end_div(); 1058 print CGI::end_p();
761 1059
762 print CGI::start_div({class=>"scoreSummary"}); 1060 print CGI::start_div({class=>"scoreSummary"});
763 1061
764 # score summary 1062 # score summary
765 my $attempts = $problem->num_correct + $problem->num_incorrect; 1063 my $attempts = $problem->num_correct + $problem->num_incorrect;
775 $attemptsLeftNoun = $attemptsLeft == 1 ? "attempt" : "attempts"; 1073 $attemptsLeftNoun = $attemptsLeft == 1 ? "attempt" : "attempts";
776 } 1074 }
777 1075
778 my $setClosed = 0; 1076 my $setClosed = 0;
779 my $setClosedMessage; 1077 my $setClosedMessage;
780 if (time < $set->open_date or time > $set->due_date) { 1078 if (before($set->open_date) or after($set->due_date)) {
781 $setClosed = 1; 1079 $setClosed = 1;
782 $setClosedMessage = "This problem set is closed."; 1080 $setClosedMessage = "This problem set is closed.";
783 if ($permissionLevel > 0) { 1081 if ($authz->hasPermissions($user, "view_answers")) {
784 $setClosedMessage .= " However, since you are a privileged user, additional attempts will be recorded."; 1082 $setClosedMessage .= " However, since you are a privileged user, additional attempts will be recorded.";
785 } else { 1083 } else {
786 $setClosedMessage .= " Additional attempts will not be recorded."; 1084 $setClosedMessage .= " Additional attempts will not be recorded.";
787 } 1085 }
788 } 1086 }
839 my $pastAnswersPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::ShowAnswers", 1137 my $pastAnswersPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::ShowAnswers",
840 courseID => $courseName); 1138 courseID => $courseName);
841 my $showPastAnswersURL = $self->systemLink($pastAnswersPage, authen => 0); # no authen info for form action 1139 my $showPastAnswersURL = $self->systemLink($pastAnswersPage, authen => 0); # no authen info for form action
842 1140
843 # print answer inspection button 1141 # print answer inspection button
844 if ($self->{permissionLevel} > 0) { 1142 if ($authz->hasPermissions($user, "view_answers")) {
845 print "\n", 1143 print "\n",
846 CGI::start_form(-method=>"POST",-action=>$showPastAnswersURL,-target=>"information"),"\n", 1144 CGI::start_form(-method=>"POST",-action=>$showPastAnswersURL,-target=>"information"),"\n",
847 $self->hidden_authen_fields,"\n", 1145 $self->hidden_authen_fields,"\n",
848 CGI::hidden(-name => 'courseID', -value=>$courseName), "\n", 1146 CGI::hidden(-name => 'courseID', -value=>$courseName), "\n",
849 CGI::hidden(-name => 'problemID', -value=>$problem->problem_id), "\n", 1147 CGI::hidden(-name => 'problemID', -value=>$problem->problem_id), "\n",
853 CGI::submit(-name => 'action', -value=>'Show Past Answers') 1151 CGI::submit(-name => 'action', -value=>'Show Past Answers')
854 ), "\n", 1152 ), "\n",
855 CGI::endform(); 1153 CGI::endform();
856 } 1154 }
857 1155
858 ## arguments for feedback form
859 #my $feedbackURL = "$root/$courseName/feedback/";
860 #
861 ##print feedback form
862 #print
863 # CGI::start_form(-method=>"POST", -action=>$feedbackURL),"\n",
864 # $self->hidden_authen_fields,"\n",
865 # CGI::hidden("module", __PACKAGE__),"\n",
866 # CGI::hidden("set", $set->set_id),"\n",
867 # CGI::hidden("problem", $problem->problem_id),"\n",
868 # CGI::hidden("displayMode", $self->{displayMode}),"\n",
869 # CGI::hidden("showOldAnswers", $will{showOldAnswers}),"\n",
870 # CGI::hidden("showCorrectAnswers", $will{showCorrectAnswers}),"\n",
871 # CGI::hidden("showHints", $will{showHints}),"\n",
872 # CGI::hidden("showSolutions", $will{showSolutions}),"\n",
873 # CGI::p({-align=>"left"},
874 # CGI::submit(-name=>"feedbackForm", -label=>"Email instructor")
875 # ),
876 # CGI::endform(),"\n";
877
878 # feedback form url 1156 # feedback form url
879 my $feedbackPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Feedback", 1157 my $feedbackPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Feedback",
880 courseID => $courseName); 1158 courseID => $courseName);
881 my $feedbackURL = $self->systemLink($feedbackPage, authen => 0); # no authen info for form action 1159 my $feedbackURL = $self->systemLink($feedbackPage, authen => 0); # no authen info for form action
882 1160
900 # FIXME print editor link 1178 # FIXME print editor link
901 print $editorLink; #empty unless it is appropriate to have an editor link. 1179 print $editorLink; #empty unless it is appropriate to have an editor link.
902 1180
903 print CGI::end_div(); 1181 print CGI::end_div();
904 1182
905 # warning output
906 #if ($pg->{warnings} ne "") {
907 # print CGI::hr(), $self->warningOutput($pg->{warnings});
908 #}
909
910 # debugging stuff 1183 # debugging stuff
911 if (0) { 1184 if (0) {
912 print 1185 print
913 CGI::hr(), 1186 CGI::hr(),
914 CGI::h2("debugging information"), 1187 CGI::h2("debugging information"),
925 } 1198 }
926 1199
927 return ""; 1200 return "";
928} 1201}
929 1202
930##### output utilities #####
931
932sub attemptResults {
933 my $self = shift;
934 my $pg = shift;
935 my $showAttemptAnswers = shift;
936 my $showCorrectAnswers = shift;
937 my $showAttemptResults = $showAttemptAnswers && shift;
938 my $showSummary = shift;
939 my $showAttemptPreview = shift || 0;
940
941 my $ce = $self->r->ce;
942
943 my $problemResult = $pg->{result}; # the overall result of the problem
944 my @answerNames = @{ $pg->{flags}->{ANSWER_ENTRY_ORDER} };
945
946 my $showMessages = $showAttemptAnswers && grep { $pg->{answers}->{$_}->{ans_message} } @answerNames;
947
948 my $basename = "equation-" . $self->{set}->psvn. "." . $self->{problem}->problem_id . "-preview";
949 my $imgGen = WeBWorK::PG::ImageGenerator->new(
950 tempDir => $ce->{webworkDirs}->{tmp},
951 latex => $ce->{externalPrograms}->{latex},
952 dvipng => $ce->{externalPrograms}->{dvipng},
953 useCache => 1,
954 cacheDir => $ce->{webworkDirs}->{equationCache},
955 cacheURL => $ce->{webworkURLs}->{equationCache},
956 cacheDB => $ce->{webworkFiles}->{equationCacheDB},
957 );
958
959 my $header;
960 #$header .= CGI::th("Part");
961 $header .= $showAttemptAnswers ? CGI::th("Entered") : "";
962 $header .= $showAttemptPreview ? CGI::th("Answer Preview") : "";
963 $header .= $showCorrectAnswers ? CGI::th("Correct") : "";
964 $header .= $showAttemptResults ? CGI::th("Result") : "";
965 $header .= $showMessages ? CGI::th("Messages") : "";
966 my @tableRows = ( $header );
967 my $numCorrect;
968 foreach my $name (@answerNames) {
969 my $answerResult = $pg->{answers}->{$name};
970 my $studentAnswer = $answerResult->{student_ans}; # original_student_ans
971 my $preview = ($showAttemptPreview
972 ? $self->previewAnswer($answerResult, $imgGen)
973 : "");
974 my $correctAnswer = $answerResult->{correct_ans};
975 my $answerScore = $answerResult->{score};
976 my $answerMessage = $showMessages ? $answerResult->{ans_message} : "";
977 #FIXME --Can we be sure that $answerScore is an integer-- could the problem give partial credit?
978 $numCorrect += $answerScore > 0;
979 my $resultString = $answerScore == 1 ? "correct" : "incorrect";
980
981 # get rid of the goofy prefix on the answer names (supposedly, the format
982 # of the answer names is changeable. this only fixes it for "AnSwEr"
983 #$name =~ s/^AnSwEr//;
984
985 my $row;
986 #$row .= CGI::td($name);
987 $row .= $showAttemptAnswers ? CGI::td($self->nbsp($studentAnswer)) : "";
988 $row .= $showAttemptPreview ? CGI::td($self->nbsp($preview)) : "";
989 $row .= $showCorrectAnswers ? CGI::td($self->nbsp($correctAnswer)) : "";
990 $row .= $showAttemptResults ? CGI::td($self->nbsp($resultString)) : "";
991 $row .= $showMessages ? CGI::td($self->nbsp($answerMessage)) : "";
992 push @tableRows, $row;
993 }
994
995 # render equation images
996 $imgGen->render(refresh => 1);
997
998# my $numIncorrectNoun = scalar @answerNames == 1 ? "question" : "questions";
999 my $scorePercent = sprintf("%.0f%%", $problemResult->{score} * 100);
1000# FIXME -- I left the old code in in case we have to back out.
1001# my $summary = "On this attempt, you answered $numCorrect out of "
1002# . scalar @answerNames . " $numIncorrectNoun correct, for a score of $scorePercent.";
1003 my $summary = "";
1004 if (scalar @answerNames == 1) {
1005 if ($numCorrect == scalar @answerNames) {
1006 $summary .= CGI::div({class=>"ResultsWithoutError"},"The above answer is correct.");
1007 } else {
1008 $summary .= CGI::div({class=>"ResultsWithError"},"The above answer is NOT correct.");
1009 }
1010 } else {
1011 if ($numCorrect == scalar @answerNames) {
1012 $summary .= CGI::div({class=>"ResultsWithoutError"},"All of the above answers are correct.");
1013 } else {
1014 $summary .= CGI::div({class=>"ResultsWithError"},"At least one of the above answers is NOT correct.");
1015 }
1016 }
1017
1018 return
1019 CGI::table({-class=>"attemptResults"}, CGI::Tr(\@tableRows))
1020 . ($showSummary ? CGI::p({class=>'emphasis'},$summary) : "");
1021}
1022
1023#sub nbsp {
1024# my $str = shift;
1025# ($str =~/\S/) ? $str : '&nbsp;' ; # returns non-breaking space for empty strings
1026# # tricky cases: $str =0;
1027# # $str is a complex number
1028#}
1029
1030sub viewOptions {
1031 my ($self) = @_;
1032 my $ce = $self->r->ce;
1033
1034 my $displayMode = $self->{displayMode};
1035 my %must = %{ $self->{must} };
1036 my %can = %{ $self->{can} };
1037 my %will = %{ $self->{will} };
1038
1039 my $optionLine;
1040 $can{showOldAnswers} and $optionLine .= join "",
1041 "Show: &nbsp;".CGI::br(),
1042 CGI::checkbox(
1043 -name => "showOldAnswers",
1044 -checked => $will{showOldAnswers},
1045 -label => "Saved answers",
1046 ), "&nbsp;&nbsp;".CGI::br();
1047
1048 $optionLine and $optionLine .= join "", CGI::br();
1049
1050 my %display_modes = %{WeBWorK::PG::DISPLAY_MODES()};
1051 my @active_modes = grep { exists $display_modes{$_} }
1052 @{$ce->{pg}->{displayModes}};
1053 my $modeLine = (scalar(@active_modes)>1) ?
1054 "View&nbsp;equations&nbsp;as:&nbsp;&nbsp;&nbsp;&nbsp;".CGI::br().
1055 CGI::radio_group(
1056 -name => "displayMode",
1057 -values => \@active_modes,
1058 -default => $displayMode,
1059 -linebreak=>'true',
1060 -labels => {
1061 plainText => "plain",
1062 formattedText => "formatted",
1063 images => "images",
1064 jsMath => "jsMath",
1065 asciimath => "asciimath",
1066 },
1067 ). CGI::br().CGI::hr() : '';
1068
1069 return CGI::div({-style=>"border: thin groove; padding: 1ex; margin: 2ex align: left"},
1070 $modeLine,
1071 $optionLine,
1072 CGI::submit(-name=>"redisplay", -label=>"Save Options"),
1073 );
1074}
1075
1076sub previewAnswer {
1077 my ($self, $answerResult, $imgGen) = @_;
1078 my $ce = $self->r->ce;
1079 my $effectiveUser = $self->{effectiveUser};
1080 my $set = $self->{set};
1081 my $problem = $self->{problem};
1082 my $displayMode = $self->{displayMode};
1083
1084 # note: right now, we have to do things completely differently when we are
1085 # rendering math from INSIDE the translator and from OUTSIDE the translator.
1086 # so we'll just deal with each case explicitly here. there's some code
1087 # duplication that can be dealt with later by abstracting out tth/dvipng/etc.
1088
1089 my $tex = $answerResult->{preview_latex_string};
1090
1091 return "" unless defined $tex and $tex ne "";
1092
1093 if ($displayMode eq "plainText") {
1094 return $tex;
1095 } elsif ($displayMode eq "formattedText") {
1096 my $tthCommand = $ce->{externalPrograms}->{tth}
1097 . " -L -f5 -r 2> /dev/null <<END_OF_INPUT; echo > /dev/null\n"
1098 . "\\(".$tex."\\)\n"
1099 . "END_OF_INPUT\n";
1100
1101 # call tth
1102 my $result = `$tthCommand`;
1103 if ($?) {
1104 return "<b>[tth failed: $? $@]</b>";
1105 }
1106 return $result;
1107 } elsif ($displayMode eq "images") {
1108 $imgGen->add($tex);
1109 } elsif ($displayMode eq "jsMath") {
1110
1111 return '<DIV CLASS="math">'.$tex.'</DIV>' ;
1112
1113
1114
1115
1116 }
1117}
1118
1119##### permission queries #####
1120
1121# this stuff should be abstracted out into the permissions system
1122# however, the permission system only knows about things in the
1123# course environment and the username. hmmm...
1124
1125# also, i should fix these so that they have a consistent calling
1126# format -- perhaps:
1127# canPERM($ce, $user, $set, $problem, $permissionLevel)
1128
1129sub canShowCorrectAnswers($$) {
1130 my ($permissionLevel, $answerDate) = @_;
1131 return $permissionLevel > 0 || time > $answerDate;
1132}
1133
1134sub canShowSolutions($$) {
1135 my ($permissionLevel, $answerDate) = @_;
1136 return canShowCorrectAnswers($permissionLevel, $answerDate);
1137}
1138
1139sub canRecordAnswers($$$$$) {
1140 my ($permissionLevel, $openDate, $dueDate, $maxAttempts, $attempts) = @_;
1141 my $permHigh = $permissionLevel > 0;
1142 my $timeOK = time >= $openDate && time <= $dueDate;
1143 my $attemptsOK = $maxAttempts == -1 || $attempts <= $maxAttempts;
1144 my $recordAnswers = $permHigh || ($timeOK && $attemptsOK);
1145 return $recordAnswers;
1146}
1147
1148sub canCheckAnswers($$) {
1149 my ($permissionLevel, $dueDate) = @_;
1150 my $permHigh = $permissionLevel > 0;
1151 my $timeOK = time >= $dueDate;
1152 my $recordAnswers = $permHigh || $timeOK;
1153 return $recordAnswers;
1154}
1155
1156sub mustRecordAnswers($) {
1157 my ($permissionLevel) = @_;
1158 return $permissionLevel == 0;
1159}
1160
1161
1162# FIXME: does this even get used?
1163sub submiterror {
1164 my $self = shift;
1165 my $submiterror = (defined($self->{submiterror}) ) ? $self->{submiterror} : '';
1166 $submiterror;
1167}
11681; 12031;

Legend:
Removed from v.2531  
changed lines
  Added in v.2532

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9