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

Legend:
Removed from v.1147  
changed lines
  Added in v.1471

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9