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

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

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

Revision 492 Revision 623
1################################################################################ 1################################################################################
2# WeBWorK mod_perl (c) 1995-2002 WeBWorK Team, Univeristy of Rochester 2# WeBWorK mod_perl (c) 2000-2002 WeBWorK Project
3# $Id$ 3# $Id$
4################################################################################ 4################################################################################
5 5
6package WeBWorK::ContentGenerator::Problem; 6package WeBWorK::ContentGenerator::Problem;
7 7
13 13
14use strict; 14use strict;
15use warnings; 15use warnings;
16use base qw(WeBWorK::ContentGenerator); 16use base qw(WeBWorK::ContentGenerator);
17use CGI qw(); 17use CGI qw();
18use File::Temp qw(tempdir);
18use WeBWorK::Form; 19use WeBWorK::Form;
19use WeBWorK::PG; 20use WeBWorK::PG;
21use WeBWorK::PG::IO;
20use WeBWorK::Utils qw(ref2string encodeAnswers decodeAnswers); 22use WeBWorK::Utils qw(writeLog encodeAnswers decodeAnswers ref2string);
21
22# TODO:
23# 7. make warnings work
24 23
25############################################################ 24############################################################
26# 25#
27# user 26# user
28# key 27# key
44 my ($self, $setName, $problemNumber) = @_; 43 my ($self, $setName, $problemNumber) = @_;
45 my $courseEnv = $self->{courseEnvironment}; 44 my $courseEnv = $self->{courseEnvironment};
46 my $r = $self->{r}; 45 my $r = $self->{r};
47 my $userName = $r->param('user'); 46 my $userName = $r->param('user');
48 47
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 ##### 48 ##### database setup #####
57 49
58 my $cldb = WeBWorK::DB::Classlist->new($courseEnv); 50 my $cldb = WeBWorK::DB::Classlist->new($courseEnv);
59 my $wwdb = WeBWorK::DB::WW->new($courseEnv); 51 my $wwdb = WeBWorK::DB::WW->new($courseEnv);
60 my $authdb = WeBWorK::DB::Auth->new($courseEnv); 52 my $authdb = WeBWorK::DB::Auth->new($courseEnv);
61 53
62 my $user = $cldb->getUser($userName); 54 my $user = $cldb->getUser($userName);
63 my $set = $wwdb->getSet($userName, $setName); 55 my $set = $wwdb->getSet($userName, $setName);
64 my $problem = $wwdb->getProblem($userName, $setName, $problemNumber); 56 my $problem = $wwdb->getProblem($userName, $setName, $problemNumber);
57 my $psvn = $wwdb->getPSVN($userName, $setName);
65 my $permissionLevel = $authdb->getPermissions($userName); 58 my $permissionLevel = $authdb->getPermissions($userName);
66 59
67 ##### form processing ##### 60 ##### form processing #####
68 61
69 # set options from form fields (see comment at top of file for names) 62 # set options from form fields (see comment at top of file for names)
70 my $displayMode = $r->param("displayMode") || $courseEnv->{pg}->{options}->{displayMode}; 63 my $displayMode = $r->param("displayMode") || $courseEnv->{pg}->{options}->{displayMode};
71 my $redisplay = $r->param("redisplay"); 64 my $redisplay = $r->param("redisplay");
72 my $submitAnswers = $r->param("submitAnswers"); 65 my $submitAnswers = $r->param("submitAnswers");
66 my $previewAnswers = $r->param("previewAnswers");
73 67
74 # coerce form fields into CGI::Vars format 68 # coerce form fields into CGI::Vars format
75 my $formFields = { WeBWorK::Form->new_from_paramable($r)->Vars }; 69 my $formFields = { WeBWorK::Form->new_from_paramable($r)->Vars };
76 70
77 ##### permissions ##### 71 ##### permissions #####
105 # num_correct+num_incorrect+1 -- as this happens before updating $problem 99 # num_correct+num_incorrect+1 -- as this happens before updating $problem
106 ); 100 );
107 101
108 # final values for options 102 # final values for options
109 my %will; 103 my %will;
110 foreach(keys %must) { 104 foreach (keys %must) {
111 $will{$_} = $can{$_} && ($want{$_} || $must{$_}); 105 $will{$_} = $can{$_} && ($want{$_} || $must{$_});
106 #warn "$_: can? $can{$_} want? $want{$_} must? $must{$_} will? $will{$_}\n";
112 } 107 }
113 108
114 ##### sticky answers ##### 109 ##### sticky answers #####
115 110
116 if (not $submitAnswers and $will{showOldAnswers}) { 111 if (not $submitAnswers and $will{showOldAnswers}) {
121 116
122 ##### translation ##### 117 ##### translation #####
123 118
124 my $pg = WeBWorK::PG->new( 119 my $pg = WeBWorK::PG->new(
125 $courseEnv, 120 $courseEnv,
126 $r->param('user'), 121 $user,
127 $r->param('key'), 122 $r->param('key'),
128 $setName, 123 $set,
129 $problemNumber, 124 $problem,
125 $psvn,
126 $formFields,
130 { # translation options 127 { # translation options
131 displayMode => $displayMode, 128 displayMode => $displayMode,
132 showHints => $will{showHints}, 129 showHints => $will{showHints},
133 showSolutions => $will{showSolutions}, 130 showSolutions => $will{showSolutions},
134 refreshMath2img => $will{showHints} || $will{showSolutions}, 131 refreshMath2img => $will{showHints} || $will{showSolutions},
135 # try leaving processAnswers on all the time? 132 # try leaving processAnswers on all the time?
136 processAnswers => 1, #$submitAnswers ? 1 : 0, 133 processAnswers => 1, #$submitAnswers ? 1 : 0,
137 }, 134 },
138 $formFields
139 ); 135 );
140 136
141 ##### store fields ##### 137 ##### store fields #####
142 138
143 $self->{cldb} = $cldb; 139 $self->{cldb} = $cldb;
147 $self->{user} = $user; 143 $self->{user} = $user;
148 $self->{set} = $set; 144 $self->{set} = $set;
149 $self->{problem} = $problem; 145 $self->{problem} = $problem;
150 $self->{permissionLevel} = $permissionLevel; 146 $self->{permissionLevel} = $permissionLevel;
151 147
152 $self->{displayMode} = $displayMode; 148 $self->{displayMode} = $displayMode;
153 $self->{redisplay} = $redisplay; 149 $self->{redisplay} = $redisplay;
154 $self->{submitAnswers} = $submitAnswers; 150 $self->{submitAnswers} = $submitAnswers;
151 $self->{previewAnswers} = $previewAnswers;
155 $self->{formFields} = $formFields; 152 $self->{formFields} = $formFields;
156 153
157 $self->{want} = \%want; 154 $self->{want} = \%want;
158 $self->{must} = \%must; 155 $self->{must} = \%must;
159 $self->{can} = \%can; 156 $self->{can} = \%can;
160 $self->{will} = \%will; 157 $self->{will} = \%will;
161 158
162 $self->{pg} = $pg; 159 $self->{pg} = $pg;
163} 160}
164 161
162sub if_warnings($$) {
163 my ($self, $arg) = @_;
164 return $self->{pg}->{warnings} ne "";
165}
166
167sub if_errors($$) {
168 my ($self, $arg) = @_;
169 return $self->{pg}->{flags}->{error_flag};
170}
171
165#sub header { 172sub head {
166# # *** we need to print $pg->{header_text} here! 173 my $self = shift;
167#} 174
175 return $self->{pg}->{head_text} if $self->{pg}->{head_text};
176}
168 177
169sub path { 178sub path {
170 my $self = shift; 179 my $self = shift;
171 my $args = $_[-1]; 180 my $args = $_[-1];
172 my $setName = $self->{set}->id; 181 my $setName = $self->{set}->id;
176 my $root = $ce->{webworkURLs}->{root}; 185 my $root = $ce->{webworkURLs}->{root};
177 my $courseName = $ce->{courseName}; 186 my $courseName = $ce->{courseName};
178 return $self->pathMacro($args, 187 return $self->pathMacro($args,
179 "Home" => "$root", 188 "Home" => "$root",
180 $courseName => "$root/$courseName", 189 $courseName => "$root/$courseName",
181 $setName => "$root/$courseName/set$setName", 190 $setName => "$root/$courseName/$setName",
182 "Problem $problemNumber" => "", 191 "Problem $problemNumber" => "",
183 ); 192 );
184} 193}
185 194
186sub siblings { 195sub siblings {
189 my $problemNumber = $self->{problem}->id; 198 my $problemNumber = $self->{problem}->id;
190 199
191 my $ce = $self->{courseEnvironment}; 200 my $ce = $self->{courseEnvironment};
192 my $root = $ce->{webworkURLs}->{root}; 201 my $root = $ce->{webworkURLs}->{root};
193 my $courseName = $ce->{courseName}; 202 my $courseName = $ce->{courseName};
203
204 print CGI::strong("Problems"), CGI::br();
194 205
195 my $wwdb = $self->{wwdb}; 206 my $wwdb = $self->{wwdb};
196 my $user = $self->{r}->param("user"); 207 my $user = $self->{r}->param("user");
197 my @problems; 208 my @problems;
198 push @problems, $wwdb->getProblem($user, $setName, $_) 209 push @problems, $wwdb->getProblem($user, $setName, $_)
214 my $courseName = $ce->{courseName}; 225 my $courseName = $ce->{courseName};
215 226
216 my $wwdb = $self->{wwdb}; 227 my $wwdb = $self->{wwdb};
217 my $user = $self->{r}->param("user"); 228 my $user = $self->{r}->param("user");
218 229
219 my @links = ("Problem List" => "$root/$courseName/set$setName"); 230 my @links = ("Problem List" => "$root/$courseName/$setName");
220 231
221 my $prevProblem = $wwdb->getProblem($user, $setName, $problemNumber-1); 232 my $prevProblem = $wwdb->getProblem($user, $setName, $problemNumber-1);
222 my $nextProblem = $wwdb->getProblem($user, $setName, $problemNumber+1); 233 my $nextProblem = $wwdb->getProblem($user, $setName, $problemNumber+1);
223 unshift @links, "Previous Problem" => "$root/$courseName/set$setName/prob".$prevProblem->id 234 unshift @links, "Previous Problem" => $prevProblem
224 if $prevProblem; 235 ? "$root/$courseName/$setName/".$prevProblem->id
225 push @links, "Next Problem" => "$root/$courseName/set$setName/prob".$nextProblem->id 236 : "";
226 if $nextProblem; 237 push @links, "Next Problem" => $nextProblem
238 ? "$root/$courseName/$setName/".$nextProblem->id
239 : "";
227 240
228 return $self->navMacro($args, @links); 241 return $self->navMacro($args, @links);
229} 242}
230 243
231sub title { 244sub title {
236 return "$setName : Problem $problemNumber"; 249 return "$setName : Problem $problemNumber";
237} 250}
238 251
239sub body { 252sub body {
240 my $self = shift; 253 my $self = shift;
241
242 #$self->prepare(@_);
243 254
244 # unpack some useful variables 255 # unpack some useful variables
245 my $r = $self->{r}; 256 my $r = $self->{r};
246 my $wwdb = $self->{wwdb}; 257 my $wwdb = $self->{wwdb};
247 my $set = $self->{set}; 258 my $set = $self->{set};
248 my $problem = $self->{problem}; 259 my $problem = $self->{problem};
249 my $permissionLevel = $self->{permissionLevel}; 260 my $permissionLevel = $self->{permissionLevel};
250 my $submitAnswers = $self->{submitAnswers}; 261 my $submitAnswers = $self->{submitAnswers};
262 my $previewAnswers = $self->{previewAnswers};
251 my %will = %{ $self->{will} }; 263 my %will = %{ $self->{will} };
252 my $pg = $self->{pg}; 264 my $pg = $self->{pg};
253 265
254 ##### translation errors? ##### 266 ##### translation errors? #####
255 267
256 if ($pg->{flags}->{error_flag}) { 268 if ($pg->{flags}->{error_flag}) {
257 print translationError($pg->{errors}, $pg->{body_text}); 269 return translationError($pg->{errors}, $pg->{body_text});
258 return "";
259 } 270 }
260 271
261 ##### answer processing ##### 272 ##### answer processing #####
262 273
263 # if answers were submitted: 274 # if answers were submitted:
277 $problem->attempted(1); 288 $problem->attempted(1);
278 $problem->status($pg->{state}->{recorded_score}); 289 $problem->status($pg->{state}->{recorded_score});
279 $problem->num_correct($pg->{state}->{num_of_correct_ans}); 290 $problem->num_correct($pg->{state}->{num_of_correct_ans});
280 $problem->num_incorrect($pg->{state}->{num_of_incorrect_ans}); 291 $problem->num_incorrect($pg->{state}->{num_of_incorrect_ans});
281 $wwdb->setProblem($problem); 292 $wwdb->setProblem($problem);
293 # write to the transaction log, just to make sure
294 writeLog($self->{courseEnvironment}, "transaction",
295 $problem->id."\t".
296 $problem->set_id."\t".
297 $problem->login_id."\t".
298 $problem->source_file."\t".
299 $problem->value."\t".
300 $problem->max_attempts."\t".
301 $problem->problem_seed."\t".
302 $problem->status."\t".
303 $problem->attempted."\t".
304 $problem->last_answer."\t".
305 $problem->num_correct."\t".
306 $problem->num_incorrect
307 );
282 } 308 }
283 } 309 }
284 310
285 ##### output ##### 311 ##### output #####
286 312
287 # attempt summary 313 # attempt summary
288 if ($submitAnswers or $will{showCorrectAnswers}) { 314 if ($submitAnswers or $will{showCorrectAnswers}) {
289 # print this if user submitted answers OR requested correct answers 315 # print this if user submitted answers OR requested correct answers
290 print attemptResults($pg, $submitAnswers, $will{showCorrectAnswers}, 316 print $self->attemptResults($pg, $submitAnswers, $will{showCorrectAnswers},
291 $pg->{flags}->{showPartialCorrectAnswers}); 317 $pg->{flags}->{showPartialCorrectAnswers});
318 } elsif ($previewAnswers) {
319 # print this if user previewed answers
320 print $self->attemptResults($pg, 1, 0, 0);
321 # don't show correctness
322 # don't show correct answers
292 } 323 }
293 324
294 # score summary 325 # score summary
295 my $attempts = $problem->num_correct + $problem->num_incorrect; 326 my $attempts = $problem->num_correct + $problem->num_incorrect;
296 my $attemptsNoun = $attempts != 1 ? "times" : "time"; 327 my $attemptsNoun = $attempts != 1 ? "times" : "time";
333 364
334 # main form 365 # main form
335 print 366 print
336 CGI::startform("POST", $r->uri), 367 CGI::startform("POST", $r->uri),
337 $self->hidden_authen_fields, 368 $self->hidden_authen_fields,
369 $self->viewOptions,
338 CGI::p(CGI::i($pg->{result}->{msg})), 370 CGI::p(CGI::i($pg->{result}->{msg})),
339 CGI::p($pg->{body_text}), 371 CGI::p($pg->{body_text}),
372 CGI::p(
340 CGI::p(CGI::submit(-name=>"submitAnswers", -label=>"Submit Answers")), 373 CGI::submit(-name=>"submitAnswers", -label=>"Submit Answers"),
341 $self->viewOptions, 374 CGI::submit(-name=>"previewAnswers", -label=>"Preview Answers"),
375 ),
342 CGI::endform(); 376 CGI::endform();
343 377
378 # warning output
379 if ($pg->{warnings} ne "") {
380 print CGI::hr(), warningOutput($pg->{warnings});
381 }
382
344 # debugging stuff 383 # debugging stuff
345 #print 384 print
346 # hr(), 385 CGI::hr(),
347 # h2("debugging information"), 386 CGI::h2("debugging information"),
348 # h3("form fields"), 387 CGI::h3("form fields"),
349 # ref2string($formFields), 388 ref2string($self->{formFields}),
350 # h3("user object"), 389 CGI::h3("user object"),
351 # ref2string($user), 390 ref2string($self->{user}),
352 # h3("set object"), 391 CGI::h3("set object"),
353 # ref2string($set), 392 ref2string($set),
354 # h3("problem object"), 393 CGI::h3("problem object"),
355 # ref2string($problem), 394 ref2string($problem),
356 # h3("PG object"), 395 CGI::h3("PG object"),
357 # ref2string($pg, {'WeBWorK::PG::Translator' => 1}); 396 ref2string($pg, {'WeBWorK::PG::Translator' => 1});
358 397
359 return ""; 398 return "";
360} 399}
361 400
362##### output utilities ##### 401##### output utilities #####
363 402
403# this is used by ProblemSet.pm too, so don't fuck it up
364sub translationError($$) { 404sub translationError($$) {
365 my ($error, $details) = @_; 405 my ($error, $details) = @_;
366 return 406 return
367 CGI::h2("Software Error"), 407 CGI::h2("Software Error"),
368 CGI::p(<<EOF), 408 CGI::p(<<EOF),
373EOF 413EOF
374 CGI::h3("Error messages"), CGI::blockquote(CGI::pre($error)), 414 CGI::h3("Error messages"), CGI::blockquote(CGI::pre($error)),
375 CGI::h3("Error context"), CGI::blockquote(CGI::pre($details)); 415 CGI::h3("Error context"), CGI::blockquote(CGI::pre($details));
376} 416}
377 417
418# this is used by ProblemSet.pm too, so don't fuck it up
419sub warningOutput($) {
420 my $warnings = shift;
421
422 return
423 CGI::h2("Software Warnings"),
424 CGI::p(<<EOF),
425WeBWorK has encountered warnings while attempting to process this problem.
426It is likely that this indicates an error or ambiguity in the problem itself.
427If you are a student, contact your professor to have the problem corrected.
428If you are a professor, please consut the error output below for more informaiton.
429EOF
430 CGI::h3("Warning messages"),
431 CGI::blockquote(CGI::pre($warnings)),
432 ;
433}
434
378sub attemptResults($$$) { 435sub attemptResults($$$$$) {
436 my $self = shift;
379 my $pg = shift; 437 my $pg = shift;
380 my $showAttemptAnswers = shift; 438 my $showAttemptAnswers = shift;
381 my $showCorrectAnswers = shift; 439 my $showCorrectAnswers = shift;
382 my $showAttemptResults = $showAttemptAnswers && shift; 440 my $showAttemptResults = $showAttemptAnswers && shift;
383 my $problemResult = $pg->{result}; # the overall result of the problem 441 my $problemResult = $pg->{result}; # the overall result of the problem
384 my @answerNames = @{ $pg->{flags}->{ANSWER_ENTRY_ORDER} }; 442 my @answerNames = @{ $pg->{flags}->{ANSWER_ENTRY_ORDER} };
385 443
386 my $header = CGI::th("answer"); 444 my $header = CGI::th("answer");
387 $header .= $showAttemptAnswers ? CGI::th("attempt") : ""; 445 $header .= $showAttemptAnswers ? CGI::th("attempt") : "";
446 $header .= $showAttemptAnswers ? CGI::th("preview") : "";
388 $header .= $showCorrectAnswers ? CGI::th("correct") : ""; 447 $header .= $showCorrectAnswers ? CGI::th("correct") : "";
389 $header .= $showAttemptResults ? CGI::th("result") : ""; 448 $header .= $showAttemptResults ? CGI::th("result") : "";
390 $header .= $showAttemptAnswers ? CGI::th("messages") : ""; 449 $header .= $showAttemptAnswers ? CGI::th("messages") : "";
391 my @tableRows = ( $header ); 450 my @tableRows = ( $header );
392 my $numCorrect; 451 my $numCorrect;
393 foreach my $name (@answerNames) { 452 foreach my $name (@answerNames) {
394 my $answerResult = $pg->{answers}->{$name}; 453 my $answerResult = $pg->{answers}->{$name};
395 my $studentAnswer = $answerResult->{student_ans}; # original_student_ans 454 my $studentAnswer = $answerResult->{student_ans}; # original_student_ans
455 my $preview = $self->previewAnswer($answerResult);
396 my $correctAnswer = $answerResult->{correct_ans}; 456 my $correctAnswer = $answerResult->{correct_ans};
397 my $answerScore = $answerResult->{score}; 457 my $answerScore = $answerResult->{score};
398 my $answerMessage = $showAttemptAnswers ? $answerResult->{ans_message} : ""; 458 my $answerMessage = $showAttemptAnswers ? $answerResult->{ans_message} : "";
399 459
400 $numCorrect += $answerScore > 0; 460 $numCorrect += $answerScore > 0;
401 my $resultString = $answerScore ? "correct :^)" : "incorrect >:("; 461 my $resultString = $answerScore ? "correct" : "incorrect";
462
463 # get rid of the goofy prefix on the answer names (supposedly, the format
464 # of the answer names is changeable. this only fixes
465 $name =~ s/^AnSwEr//;
402 466
403 my $row = CGI::td($name); 467 my $row = CGI::td($name);
404 $row .= $showAttemptAnswers ? CGI::td($studentAnswer) : ""; 468 $row .= $showAttemptAnswers ? CGI::td($studentAnswer) : "";
469 $row .= $showAttemptAnswers ? CGI::td($preview) : "";
405 $row .= $showCorrectAnswers ? CGI::td($correctAnswer) : ""; 470 $row .= $showCorrectAnswers ? CGI::td($correctAnswer) : "";
406 $row .= $showAttemptResults ? CGI::td($resultString) : ""; 471 $row .= $showAttemptResults ? CGI::td($resultString) : "";
407 $row .= $answerMessage ? CGI::td($answerMessage) : ""; 472 $row .= $answerMessage ? CGI::td($answerMessage) : "";
408 push @tableRows, $row; 473 push @tableRows, $row;
409 } 474 }
465 $optionLine, 530 $optionLine,
466 CGI::submit(-name=>"redisplay", -label=>"Redisplay Problem"), 531 CGI::submit(-name=>"redisplay", -label=>"Redisplay Problem"),
467 ); 532 );
468} 533}
469 534
535sub previewAnswer($$) {
536 my ($self, $answerResult) = @_;
537 my $ce = $self->{courseEnvironment};
538 my $user = $self->{user};
539 my $set = $self->{set};
540 my $problem = $self->{problem};
541
542 # how are we going to name this?
543 my $targetPathCommon = "/png/"
544 . $user->id . "."
545 . $set->id . "."
546 . $problem->id . "."
547 . $answerResult->{ans_name} . ".png";
548
549 # figure out where to put things
550 my $wd = tempdir("webwork-dvipng-XXXXXXXX", DIR => $ce->{courseDirs}->{html_temp});
551 my $latex = $ce->{externalPrograms}->{latex};
552 my $dvipng = $ce->{externalPrograms}->{dvipng};
553 my $tex = $answerResult->{preview_latex_string};
554 my $targetPath = $ce->{courseDirs}->{html_temp} . $targetPathCommon;
555 # should use surePathToTmpFile, but we have to
556 # isolate it from the problem enivronment first
557 my $targetURL = $ce->{courseURLs}->{html_temp} . $targetPathCommon;
558
559 # call dvipng to generate a preview
560 dvipng($wd, $latex, $dvipng, $tex, $targetPath);
561 if (-e $targetPath) {
562 return "<img src=\"$targetURL\" alt=\"$tex\" />";
563 } else {
564 return "<b>[math2img failed]</b>";
565 }
566}
567
470##### permission queries ##### 568##### permission queries #####
471 569
472# this stuff should be abstracted out into the permissions system 570# this stuff should be abstracted out into the permissions system
473# however, the permission system only knows about things in the 571# however, the permission system only knows about things in the
474# course environment and the username. hmmm... 572# course environment and the username. hmmm...
489 587
490sub canRecordAnswers($$$$$) { 588sub canRecordAnswers($$$$$) {
491 my ($permissionLevel, $openDate, $dueDate, $maxAttempts, $attempts) = @_; 589 my ($permissionLevel, $openDate, $dueDate, $maxAttempts, $attempts) = @_;
492 my $permHigh = $permissionLevel > 0; 590 my $permHigh = $permissionLevel > 0;
493 my $timeOK = time >= $openDate && time <= $dueDate; 591 my $timeOK = time >= $openDate && time <= $dueDate;
494 my $attemptsOK = $attempts <= $maxAttempts; 592 my $attemptsOK = $maxAttempts == -1 || $attempts <= $maxAttempts;
495 return $permHigh || ($timeOK && $attemptsOK); 593 my $recordAnswers = $permHigh || ($timeOK && $attemptsOK);
594 return $recordAnswers;
496} 595}
497 596
498sub mustRecordAnswers($) { 597sub mustRecordAnswers($) {
499 my ($permissionLevel) = @_; 598 my ($permissionLevel) = @_;
500 return $permissionLevel == 0; 599 return $permissionLevel == 0;

Legend:
Removed from v.492  
changed lines
  Added in v.623

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9