| 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.119 2004/04/05 20:21:48 jj Exp $ |
4 | # $CVSHeader: webwork-modperl/lib/WeBWorK/ContentGenerator/Problem.pm,v 1.136 2004/05/24 20:19:08 toenail 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. |
| … | |
… | |
| 33 | use WeBWorK::PG::IO; |
33 | use WeBWorK::PG::IO; |
| 34 | use WeBWorK::Utils qw(writeLog writeCourseLog encodeAnswers decodeAnswers ref2string makeTempDirectory); |
34 | use WeBWorK::Utils qw(writeLog writeCourseLog encodeAnswers decodeAnswers ref2string makeTempDirectory); |
| 35 | use WeBWorK::DB::Utils qw(global2user user2global findDefaults); |
35 | use WeBWorK::DB::Utils qw(global2user user2global findDefaults); |
| 36 | use WeBWorK::Timing; |
36 | use WeBWorK::Timing; |
| 37 | |
37 | |
|
|
38 | use WeBWorK::Utils::Tasks qw(fake_set fake_problem); |
| 38 | |
39 | |
| 39 | ############################################################ |
40 | ############################################################ |
| 40 | # |
41 | # |
| 41 | # user |
42 | # user |
| 42 | # effectiveUser |
43 | # effectiveUser |
| … | |
… | |
| 93 | # obtain the merged set for $effectiveUser |
94 | # obtain the merged set for $effectiveUser |
| 94 | my $set = $db->getMergedSet($effectiveUserName, $setName); # checked |
95 | my $set = $db->getMergedSet($effectiveUserName, $setName); # checked |
| 95 | |
96 | |
| 96 | # obtain the merged problem for $effectiveUser |
97 | # obtain the merged problem for $effectiveUser |
| 97 | my $problem = $db->getMergedProblem($effectiveUserName, $setName, $problemNumber); # checked |
98 | my $problem = $db->getMergedProblem($effectiveUserName, $setName, $problemNumber); # checked |
| 98 | |
99 | |
| 99 | my $editMode = $r->param("editMode"); |
100 | my $editMode = $r->param("editMode"); |
| 100 | |
101 | |
| 101 | if ($permissionLevel > 0 and defined $editMode) { |
102 | if ($permissionLevel > 0) { |
| 102 | # professors are allowed to fabricate sets and problems not |
103 | # professors are allowed to fabricate sets and problems not |
| 103 | # assigned to them (or anyone). this allows them to use the |
104 | # assigned to them (or anyone). this allows them to use the |
| 104 | # editor to |
105 | # editor to |
| 105 | |
106 | |
| 106 | # if that is not yet defined obtain the global set, convert |
107 | # if that is not yet defined obtain the global set, convert |
| 107 | # it to a user set, and add fake user data |
108 | # it to a user set, and add fake user data |
| 108 | unless (defined $set) { |
109 | unless (defined $set) { |
| 109 | my $userSetClass = $db->{set_user}->{record}; |
110 | my $userSetClass = $db->{set_user}->{record}; |
| 110 | my $globalSet = $db->getGlobalSet($setName); # checked |
111 | my $globalSet = $db->getGlobalSet($setName); # checked |
| 111 | # if the global set doesn't exist either, bail! |
112 | # if the global set doesn't exist either, bail! |
| 112 | die "Set $setName does not exist" |
113 | if(not defined $globalSet) { |
| 113 | unless defined $set; |
114 | $set = fake_set($db); |
|
|
115 | } else { |
| 114 | $set = global2user($userSetClass, $globalSet); |
116 | $set = global2user($userSetClass, $globalSet); |
| 115 | $set->psvn(0); |
117 | $set->psvn(0); |
|
|
118 | |
|
|
119 | # FIXME: This is a temporary fix to fill in the database |
|
|
120 | # We want the published field to contain either 1 or 0 so if it has not been set to 0, default to 1 |
|
|
121 | # this will fill in all the empty fields but not change anything that has been specifically set to 1 or 0 |
|
|
122 | $globalSet->published("1") unless $globalSet->published eq "0"; |
|
|
123 | $db->putGlobalSet($globalSet); |
|
|
124 | } |
| 116 | } |
125 | } |
| 117 | |
126 | |
| 118 | # if that is not yet defined obtain the global problem, |
127 | # if that is not yet defined obtain the global problem, |
| 119 | # convert it to a user problem, and add fake user data |
128 | # convert it to a user problem, and add fake user data |
| 120 | unless (defined $problem) { |
129 | unless (defined $problem) { |
| 121 | my $userProblemClass = $db->{problem_user}->{record}; |
130 | my $userProblemClass = $db->{problem_user}->{record}; |
| 122 | my $globalProblem = $db->getGlobalProblem($setName, $problemNumber); # checked |
131 | my $globalProblem = $db->getGlobalProblem($setName, $problemNumber); # checked |
| 123 | # if the global problem doesn't exist either, bail! |
132 | # if the global problem doesn't exist either, bail! |
|
|
133 | if(not defined $globalProblem) { |
|
|
134 | my $sourceFilePath = $r->param("sourceFilePath"); |
| 124 | die "Problem $problemNumber in set $setName does not exist" |
135 | die "Problem $problemNumber in set $setName does not exist" |
| 125 | unless defined $problem; |
136 | unless defined $sourceFilePath; |
|
|
137 | $problem = fake_problem($db); |
|
|
138 | $problem->problem_id(1); |
|
|
139 | $problem->source_file($sourceFilePath); |
|
|
140 | $problem->user_id($effectiveUserName); |
|
|
141 | } else { |
| 126 | $problem = global2user($userProblemClass, $globalProblem); |
142 | $problem = global2user($userProblemClass, $globalProblem); |
| 127 | $problem->user_id($effectiveUserName); |
143 | $problem->user_id($effectiveUserName); |
| 128 | $problem->problem_seed(0); |
144 | $problem->problem_seed(0); |
| 129 | $problem->status(0); |
145 | $problem->status(0); |
| 130 | $problem->attempted(0); |
146 | $problem->attempted(0); |
| 131 | $problem->last_answer(""); |
147 | $problem->last_answer(""); |
| 132 | $problem->num_correct(0); |
148 | $problem->num_correct(0); |
| 133 | $problem->num_incorrect(0); |
149 | $problem->num_incorrect(0); |
|
|
150 | } |
| 134 | } |
151 | } |
| 135 | |
152 | |
| 136 | # now we're sure we have valid UserSet and UserProblem objects |
153 | # now we're sure we have valid UserSet and UserProblem objects |
| 137 | # yay! |
154 | # yay! |
| 138 | |
155 | |
| 139 | # now deal with possible editor overrides: |
156 | # now deal with possible editor overrides: |
| 140 | |
157 | |
| 141 | # if the caller is asking to override the source file, and |
158 | # if the caller is asking to override the source file, and |
| 142 | # editMode calls for a temporary file, do so |
159 | # editMode calls for a temporary file, do so |
| 143 | my $sourceFilePath = $r->param("sourceFilePath"); |
160 | my $sourceFilePath = $r->param("sourceFilePath"); |
| 144 | if (defined $sourceFilePath and $editMode eq "temporaryFile") { |
161 | if (defined $sourceFilePath and |
|
|
162 | (not defined $editMode or $editMode eq "temporaryFile")) { |
| 145 | $problem->source_file($sourceFilePath); |
163 | $problem->source_file($sourceFilePath); |
| 146 | } |
164 | } |
| 147 | |
165 | |
| 148 | # if the caller is asking to override the problem seed, do so |
166 | # if the caller is asking to override the problem seed, do so |
| 149 | my $problemSeed = $r->param("problemSeed"); |
167 | my $problemSeed = $r->param("problemSeed"); |
| 150 | if (defined $problemSeed) { |
168 | if (defined $problemSeed) { |
| 151 | $problem->problem_seed($problemSeed); |
169 | $problem->problem_seed($problemSeed); |
| 152 | } |
170 | } |
|
|
171 | |
|
|
172 | my $published = ($set->published) ? "visable to students." : "hidden from students."; |
|
|
173 | $self->addmessage(CGI::p("This set is " . CGI::font({class=>$published}, $published))); |
| 153 | } else { |
174 | } else { |
|
|
175 | |
|
|
176 | $self->addmessage(CGI::div({class=>"ResultsWithError"}, CGI::p("This problem will not count towards your grade."))) unless $problem->value;; |
| 154 | # students can't view problems not assigned to them |
177 | # students can't view problems not assigned to them |
| 155 | die "Set $setName is not assigned to $effectiveUserName" |
178 | |
| 156 | unless defined $set; |
179 | # A set is valid if it exists and if it is either published or the user is privileged. |
| 157 | die "Problem $problemNumber in set $setName is not assigned to $effectiveUserName" |
180 | $self->{invalidSet} = (grep /$setName/, $db->listUserSets($effectiveUserName)) == 0 || !($set->published || $permissionLevel->permission > 0); # this is redundant because of the above if |
| 158 | unless defined $problem; |
181 | $self->{invalidProblem} = (grep /$problemNumber/, $db->listUserProblems($effectiveUserName, $setName)) == 0 || !($set->published || $permissionLevel->permission > 0);; |
|
|
182 | |
| 159 | } |
183 | } |
| 160 | |
184 | |
| 161 | $self->{userName} = $userName; |
185 | $self->{userName} = $userName; |
| 162 | $self->{effectiveUserName} = $effectiveUserName; |
186 | $self->{effectiveUserName} = $effectiveUserName; |
| 163 | $self->{user} = $user; |
187 | $self->{user} = $user; |
| … | |
… | |
| 182 | $self->{redisplay} = $redisplay; |
206 | $self->{redisplay} = $redisplay; |
| 183 | $self->{submitAnswers} = $submitAnswers; |
207 | $self->{submitAnswers} = $submitAnswers; |
| 184 | $self->{checkAnswers} = $checkAnswers; |
208 | $self->{checkAnswers} = $checkAnswers; |
| 185 | $self->{previewAnswers} = $previewAnswers; |
209 | $self->{previewAnswers} = $previewAnswers; |
| 186 | $self->{formFields} = $formFields; |
210 | $self->{formFields} = $formFields; |
|
|
211 | |
|
|
212 | # get result and send to message |
|
|
213 | my $success = $r->param("sucess"); |
|
|
214 | my $failure = $r->param("failure"); |
|
|
215 | $self->addmessage(CGI::div({class=>"ResultsWithError"}, CGI::p($failure))) if $failure; |
|
|
216 | $self->addmessage(CGI::div({class=>"ResultsWithoutError"}, CGI::p($success))) if $success; |
|
|
217 | |
|
|
218 | # now that we've set all the necessary variables quit out if the set or problem is invalid |
|
|
219 | return if $self->{invalidSet} || $self->{invalidProblem}; |
| 187 | |
220 | |
| 188 | ##### permissions ##### |
221 | ##### permissions ##### |
| 189 | |
222 | |
| 190 | # are we allowed to view this problem? |
223 | # are we allowed to view this problem? |
| 191 | $self->{isOpen} = time >= $set->open_date || $permissionLevel > 0; |
224 | $self->{isOpen} = time >= $set->open_date || $permissionLevel > 0; |
| … | |
… | |
| 330 | } |
363 | } |
| 331 | |
364 | |
| 332 | sub options { |
365 | sub options { |
| 333 | my ($self) = @_; |
366 | my ($self) = @_; |
| 334 | |
367 | |
|
|
368 | return "" if $self->{invalidProblem}; |
|
|
369 | |
| 335 | return join("", |
370 | return join("", |
| 336 | CGI::start_form("POST", $self->{r}->uri), |
371 | CGI::start_form("POST", $self->{r}->uri), |
| 337 | $self->hidden_authen_fields, |
372 | $self->hidden_authen_fields, |
| 338 | CGI::hr(), |
373 | CGI::hr(), |
| 339 | CGI::start_div({class=>"viewOptions"}), |
374 | CGI::start_div({class=>"viewOptions"}), |
| … | |
… | |
| 364 | my ($self) = @_; |
399 | my ($self) = @_; |
| 365 | my $r = $self->r; |
400 | my $r = $self->r; |
| 366 | my $db = $r->db; |
401 | my $db = $r->db; |
| 367 | my $urlpath = $r->urlpath; |
402 | my $urlpath = $r->urlpath; |
| 368 | |
403 | |
|
|
404 | # can't show sibling problems if the set is invalid |
|
|
405 | return "" if $self->{invalidSet}; |
|
|
406 | |
| 369 | my $courseID = $urlpath->arg("courseID"); |
407 | my $courseID = $urlpath->arg("courseID"); |
| 370 | my $setID = $self->{set}->set_id; |
408 | my $setID = $self->{set}->set_id; |
| 371 | my $eUserID = $r->param("effectiveUser"); |
409 | my $eUserID = $r->param("effectiveUser"); |
| 372 | my @problemIDs = sort { $a <=> $b } $db->listUserProblems($eUserID, $setID); |
410 | my @problemIDs = sort { $a <=> $b } $db->listUserProblems($eUserID, $setID); |
| 373 | |
411 | |
| 374 | print CGI::start_ul({class=>"LinksMenu"}); |
412 | print CGI::start_ul({class=>"LinksMenu"}); |
| 375 | print CGI::start_li(); |
413 | print CGI::start_li(); |
| 376 | print CGI::span({style=>"font-size:larger"}, "Problems"); |
414 | print CGI::span({style=>"font-size:larger"}, "Problems"); |
| 377 | print CGI::start_ul(); |
415 | print CGI::start_ul(); |
| 378 | |
416 | |
| 379 | foreach my $problemID (@problemIDs) { |
417 | foreach my $problemID (@problemIDs) { |
| 380 | my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", |
418 | my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", |
| 381 | courseID => $courseID, setID => $setID, problemID => $problemID); |
419 | courseID => $courseID, setID => $setID, problemID => $problemID); |
| 382 | print CGI::li(CGI::a({href=>$self->systemLink($problemPage)}, "Problem $problemID")); |
420 | print CGI::li(CGI::a({href=>$self->systemLink($problemPage, params=>{displayMode => $self->{displayMode}})}, "Problem $problemID")); |
| 383 | } |
421 | } |
| 384 | |
422 | |
| 385 | print CGI::end_ul(); |
423 | print CGI::end_ul(); |
| 386 | print CGI::end_li(); |
424 | print CGI::end_li(); |
| 387 | print CGI::end_ul(); |
425 | print CGI::end_ul(); |
| … | |
… | |
| 394 | my $r = $self->r; |
432 | my $r = $self->r; |
| 395 | my $db = $r->db; |
433 | my $db = $r->db; |
| 396 | my $urlpath = $r->urlpath; |
434 | my $urlpath = $r->urlpath; |
| 397 | |
435 | |
| 398 | my $courseID = $urlpath->arg("courseID"); |
436 | my $courseID = $urlpath->arg("courseID"); |
| 399 | my $setID = $self->{set}->set_id; |
437 | my $setID = $self->{set}->set_id if !($self->{invalidSet}); |
| 400 | my $problemID = $self->{problem}->problem_id; |
438 | my $problemID = $self->{problem}->problem_id if !($self->{invalidProblem}); |
| 401 | my $eUserID = $r->param("effectiveUser"); |
439 | my $eUserID = $r->param("effectiveUser"); |
| 402 | |
440 | |
| 403 | my ($prevID, $nextID); |
441 | my ($prevID, $nextID); |
| 404 | |
442 | |
|
|
443 | if (!$self->{invalidProblem}) { |
| 405 | my @problemIDs = $db->listUserProblems($eUserID, $setID); |
444 | my @problemIDs = $db->listUserProblems($eUserID, $setID); |
| 406 | foreach my $id (@problemIDs) { |
445 | foreach my $id (@problemIDs) { |
| 407 | $prevID = $id if $id < $problemID |
446 | $prevID = $id if $id < $problemID |
| 408 | and (not defined $prevID or $id > $prevID); |
447 | and (not defined $prevID or $id > $prevID); |
| 409 | $nextID = $id if $id > $problemID |
448 | $nextID = $id if $id > $problemID |
| 410 | and (not defined $nextID or $id < $nextID); |
449 | and (not defined $nextID or $id < $nextID); |
|
|
450 | } |
| 411 | } |
451 | } |
| 412 | |
452 | |
| 413 | my @links; |
453 | my @links; |
| 414 | |
454 | |
| 415 | if ($prevID) { |
455 | if ($prevID) { |
| … | |
… | |
| 434 | return $self->navMacro($args, $tail, @links); |
474 | return $self->navMacro($args, $tail, @links); |
| 435 | } |
475 | } |
| 436 | |
476 | |
| 437 | sub title { |
477 | sub title { |
| 438 | my ($self) = @_; |
478 | my ($self) = @_; |
|
|
479 | |
|
|
480 | # using the url arguments won't break if the set/problem are invalid |
|
|
481 | my $setID = $self->r->urlpath->arg("setID"); |
|
|
482 | my $problemID = $self->r->urlpath->arg("problemID"); |
| 439 | |
483 | |
| 440 | my $setID = $self->{set}->set_id; |
484 | #my $setID = $self->{set}->set_id; |
| 441 | my $problemID = $self->{problem}->problem_id; |
485 | #my $problemID = $self->{problem}->problem_id; |
| 442 | |
486 | |
| 443 | return "$setID : $problemID"; |
487 | return "$setID : $problemID"; |
| 444 | } |
488 | } |
| 445 | |
489 | |
| 446 | sub body { |
490 | sub body { |
| 447 | my $self = shift; |
491 | my $self = shift; |
| 448 | my $r = $self->r; |
492 | my $r = $self->r; |
| 449 | my $ce = $r->ce; |
493 | my $ce = $r->ce; |
| 450 | my $db = $r->db; |
494 | my $db = $r->db; |
| 451 | my $urlpath = $r->urlpath; |
495 | my $urlpath = $r->urlpath; |
|
|
496 | |
|
|
497 | if ($self->{invalidSet}) { |
|
|
498 | return CGI::div({class=>"ResultsWithError"}, |
|
|
499 | CGI::p("The selected problem set (" . $urlpath->arg("setID") . ") is not a valid set for " . $r->param("effectiveUser") . ".")); |
|
|
500 | } |
| 452 | |
501 | |
|
|
502 | if ($self->{invalidProblem}) { |
|
|
503 | return CGI::div({class=>"ResultsWithError"}, |
|
|
504 | CGI::p("The selected problem (" . $urlpath->arg("problemID") . ") is not a valid problem for set " . $self->{set}->set_id . ".")); |
|
|
505 | } |
|
|
506 | |
| 453 | unless ($self->{isOpen}) { |
507 | unless ($self->{isOpen}) { |
| 454 | return CGI::div({class=>"ResultsWithError"}, |
508 | return CGI::div({class=>"ResultsWithError"}, |
| 455 | CGI::p("This problem is not available because the problem set that contains it is not yet open.")); |
509 | CGI::p("This problem is not available because the problem set that contains it is not yet open.")); |
| 456 | } |
510 | } |
| 457 | # unpack some useful variables |
511 | # unpack some useful variables |
| … | |
… | |
| 479 | # $editorLinkMessage = CGI::a({-href=>$ce->{webworkURLs}->{root}."/$courseName/instructor/pgProblemEditor/". |
533 | # $editorLinkMessage = CGI::a({-href=>$ce->{webworkURLs}->{root}."/$courseName/instructor/pgProblemEditor/". |
| 480 | # $set->set_id.'/'.$problem->problem_id.'?'.$self->url_authen_args},'Edit this problem'); |
534 | # $set->set_id.'/'.$problem->problem_id.'?'.$self->url_authen_args},'Edit this problem'); |
| 481 | #} |
535 | #} |
| 482 | |
536 | |
| 483 | my $editorLink = ""; |
537 | my $editorLink = ""; |
|
|
538 | # if we are here without a real problem set, carry that through |
|
|
539 | my $forced_field = []; |
|
|
540 | $forced_field = ['sourceFilePath' => $r->param("sourceFilePath")] if |
|
|
541 | ($set->set_id eq 'Undefined_Set'); |
| 484 | if ($self->{permissionLevel}>=10) { |
542 | if ($self->{permissionLevel}>=10) { |
| 485 | my $editorPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", |
543 | my $editorPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", |
| 486 | courseID => $courseName, setID => $set->set_id, problemID => $problem->problem_id); |
544 | courseID => $courseName, setID => $set->set_id, problemID => $problem->problem_id); |
| 487 | my $editorURL = $self->systemLink($editorPage); |
545 | my $editorURL = $self->systemLink($editorPage, params=>$forced_field); |
| 488 | $editorLink = CGI::a({href=>$editorURL}, "Edit this problem"); |
546 | $editorLink = CGI::a({href=>$editorURL}, "Edit this problem"); |
| 489 | } |
547 | } |
| 490 | |
548 | |
| 491 | ##### translation errors? ##### |
549 | ##### translation errors? ##### |
| 492 | |
550 | |
| … | |
… | |
| 498 | |
556 | |
| 499 | ##### answer processing ##### |
557 | ##### answer processing ##### |
| 500 | $WeBWorK::timer->continue("begin answer processing") if defined($WeBWorK::timer); |
558 | $WeBWorK::timer->continue("begin answer processing") if defined($WeBWorK::timer); |
| 501 | # if answers were submitted: |
559 | # if answers were submitted: |
| 502 | my $scoreRecordedMessage; |
560 | my $scoreRecordedMessage; |
|
|
561 | my $pureProblem; |
| 503 | if ($submitAnswers) { |
562 | if ($submitAnswers) { |
| 504 | # get a "pure" (unmerged) UserProblem to modify |
563 | # get a "pure" (unmerged) UserProblem to modify |
| 505 | # this will be undefined if the problem has not been assigned to this user |
564 | # this will be undefined if the problem has not been assigned to this user |
| 506 | my $pureProblem = $db->getUserProblem($problem->user_id, $problem->set_id, $problem->problem_id); # checked |
565 | $pureProblem = $db->getUserProblem($problem->user_id, $problem->set_id, $problem->problem_id); # checked |
| 507 | if (defined $pureProblem) { |
566 | if (defined $pureProblem) { |
| 508 | # store answers in DB for sticky answers |
567 | # store answers in DB for sticky answers |
| 509 | my %answersToStore; |
568 | my %answersToStore; |
| 510 | my %answerHash = %{ $pg->{answers} }; |
569 | my %answerHash = %{ $pg->{answers} }; |
| 511 | $answersToStore{$_} = $self->{formFields}->{$_} #$answerHash{$_}->{original_student_ans} -- this may have been modified for fields with multiple values. Don't use it!! |
570 | $answersToStore{$_} = $self->{formFields}->{$_} #$answerHash{$_}->{original_student_ans} -- this may have been modified for fields with multiple values. Don't use it!! |
| … | |
… | |
| 570 | } |
629 | } |
| 571 | |
630 | |
| 572 | # logging student answers |
631 | # logging student answers |
| 573 | |
632 | |
| 574 | my $answer_log = $self->{ce}->{courseFiles}->{logs}->{'answer_log'}; |
633 | my $answer_log = $self->{ce}->{courseFiles}->{logs}->{'answer_log'}; |
| 575 | if ( defined($answer_log )) { |
634 | if ( defined($answer_log ) and defined($pureProblem)) { |
| 576 | if ($submitAnswers ) { |
635 | if ($submitAnswers ) { |
| 577 | my $answerString = ""; |
636 | my $answerString = ""; |
| 578 | my %answerHash = %{ $pg->{answers} }; |
637 | my %answerHash = %{ $pg->{answers} }; |
| 579 | # FIXME this is the line 552 error. make sure original student ans is defined. |
638 | # FIXME this is the line 552 error. make sure original student ans is defined. |
| 580 | # The fact that it is not defined is probably due to an error in some answer evaluator. |
639 | # The fact that it is not defined is probably due to an error in some answer evaluator. |
| … | |
… | |
| 607 | # custom message for editor |
666 | # custom message for editor |
| 608 | if ($permissionLevel >= 10 and defined $editMode) { |
667 | if ($permissionLevel >= 10 and defined $editMode) { |
| 609 | if ($editMode eq "temporaryFile") { |
668 | if ($editMode eq "temporaryFile") { |
| 610 | print CGI::p(CGI::i("Editing temporary file: ", $problem->source_file)); |
669 | print CGI::p(CGI::i("Editing temporary file: ", $problem->source_file)); |
| 611 | } elsif ($editMode eq "savedFile") { |
670 | } elsif ($editMode eq "savedFile") { |
|
|
671 | # FIXME: this is all done automatically if submitError exists. |
|
|
672 | #if ( defined($r->param('submiterror')) and $r->param('submiterror') ) { |
|
|
673 | # FIXME The following line doesn't work because the submiterror hook has already been called. |
|
|
674 | # The actions below should take place during the initialization phase. |
|
|
675 | # $self->{submiterror} .= $r->param('submiterror'); |
|
|
676 | # print CGI::p(CGI::div({class=>'ResultsWithError'},$self->{submiterror})); |
|
|
677 | #} else { |
| 612 | print CGI::p(CGI::i("Problem saved to: ", $problem->source_file)); |
678 | # print CGI::p(CGI::div({ class=>'ResultsWithoutError'}, "Problem saved to: ", $problem->source_file)); |
|
|
679 | #} |
| 613 | } |
680 | } |
| 614 | } |
681 | } |
| 615 | |
682 | #FIXME we need error messages here if the problem was really not saved. |
| 616 | # attempt summary |
683 | # attempt summary |
| 617 | #FIXME -- the following is a kludge: if showPartialCorrectAnswers is negative don't show anything. |
684 | #FIXME -- the following is a kludge: if showPartialCorrectAnswers is negative don't show anything. |
| 618 | # until after the due date |
685 | # until after the due date |
| 619 | # do I need to check $wills{howCorrectAnswers} to make preflight work?? |
686 | # do I need to check $wills{howCorrectAnswers} to make preflight work?? |
| 620 | if (($pg->{flags}->{showPartialCorrectAnswers}>= 0 and $submitAnswers) ) { |
687 | if (($pg->{flags}->{showPartialCorrectAnswers}>= 0 and $submitAnswers) ) { |
| … | |
… | |
| 623 | print $self->attemptResults($pg, 1, |
690 | print $self->attemptResults($pg, 1, |
| 624 | $will{showCorrectAnswers}, |
691 | $will{showCorrectAnswers}, |
| 625 | $pg->{flags}->{showPartialCorrectAnswers}, 1, 1); |
692 | $pg->{flags}->{showPartialCorrectAnswers}, 1, 1); |
| 626 | } elsif ($checkAnswers) { |
693 | } elsif ($checkAnswers) { |
| 627 | # print this if user previewed answers |
694 | # print this if user previewed answers |
| 628 | print "ANSWERS ONLY CHECKED -- ",CGI::br(),"ANSWERS NOT RECORDED", CGI::br(); |
695 | print CGI::div({class=>'ResultsWithError'},"ANSWERS ONLY CHECKED -- ",CGI::br(),"ANSWERS NOT RECORDED", CGI::br() ); |
| 629 | print $self->attemptResults($pg, 1, $will{showCorrectAnswers}, 1, 1, 1); |
696 | print $self->attemptResults($pg, 1, $will{showCorrectAnswers}, 1, 1, 1); |
| 630 | # show attempt answers |
697 | # show attempt answers |
| 631 | # show correct answers if asked |
698 | # show correct answers if asked |
| 632 | # show attempt results (correctness) |
699 | # show attempt results (correctness) |
| 633 | # show attempt previews |
700 | # show attempt previews |
| 634 | } elsif ($previewAnswers) { |
701 | } elsif ($previewAnswers) { |
| 635 | # print this if user previewed answers |
702 | # print this if user previewed answers |
| 636 | print "PREVIEW ONLY -- NOT RECORDED",CGI::br(),$self->attemptResults($pg, 1, 0, 0, 0, 1); |
703 | print CGI::div({class=>'ResultsWithError'},"PREVIEW ONLY -- NOT RECORDED"),CGI::br(),$self->attemptResults($pg, 1, 0, 0, 0, 1); |
| 637 | # show attempt answers |
704 | # show attempt answers |
| 638 | # don't show correct answers |
705 | # don't show correct answers |
| 639 | # don't show attempt results (correctness) |
706 | # don't show attempt results (correctness) |
| 640 | # show attempt previews |
707 | # show attempt previews |
| 641 | } |
708 | } |
| 642 | |
709 | |
| 643 | print CGI::end_div(); |
710 | print CGI::end_div(); |
| 644 | |
711 | |
| 645 | print CGI::start_div({class=>"problem"}); |
712 | print CGI::start_div({class=>"problem"}); |
| 646 | |
713 | |
| 647 | # main form |
714 | # main form |
| 648 | print |
715 | print |
| 649 | CGI::startform("POST", $r->uri), |
716 | CGI::startform("POST", $r->uri), |
| 650 | $self->hidden_authen_fields, |
717 | $self->hidden_authen_fields, |
| 651 | CGI::p($pg->{body_text}), |
718 | CGI::p($pg->{body_text}), |
| … | |
… | |
| 710 | $setClosedMessage .= " However, since you are a privileged user, additional attempts will be recorded."; |
777 | $setClosedMessage .= " However, since you are a privileged user, additional attempts will be recorded."; |
| 711 | } else { |
778 | } else { |
| 712 | $setClosedMessage .= " Additional attempts will not be recorded."; |
779 | $setClosedMessage .= " Additional attempts will not be recorded."; |
| 713 | } |
780 | } |
| 714 | } |
781 | } |
|
|
782 | |
|
|
783 | my $notCountedMessage = ($problem->value) ? "" : "(This problem will not count towards your grade.)"; |
| 715 | print CGI::p( |
784 | print CGI::p( |
| 716 | $submitAnswers ? $scoreRecordedMessage . CGI::br() : "", |
785 | $submitAnswers ? $scoreRecordedMessage . CGI::br() : "", |
| 717 | "You have attempted this problem $attempts $attemptsNoun.", CGI::br(), |
786 | "You have attempted this problem $attempts $attemptsNoun.", CGI::br(), |
| 718 | $problem->attempted |
787 | $problem->attempted |
| 719 | ? "Your recorded score is $lastScore." . CGI::br() |
788 | ? "Your recorded score is $lastScore. $notCountedMessage" . CGI::br() |
| 720 | : "", |
789 | : "", |
| 721 | $setClosed ? $setClosedMessage : "You have $attemptsLeft $attemptsLeftNoun remaining." |
790 | $setClosed ? $setClosedMessage : "You have $attemptsLeft $attemptsLeftNoun remaining." |
| 722 | ); |
791 | ); |
| 723 | print CGI::end_div(); |
792 | print CGI::end_div(); |
| 724 | |
793 | |
| … | |
… | |
| 742 | -value => $self->{problem}->{source_file} |
811 | -value => $self->{problem}->{source_file} |
| 743 | )) if defined($self->{problem}->{source_file}); |
812 | )) if defined($self->{problem}->{source_file}); |
| 744 | |
813 | |
| 745 | # end of main form |
814 | # end of main form |
| 746 | print CGI::endform(); |
815 | print CGI::endform(); |
| 747 | |
|
|
| 748 | |
816 | |
| 749 | print CGI::start_div({class=>"problemFooter"}); |
817 | print CGI::start_div({class=>"problemFooter"}); |
| 750 | |
818 | |
| 751 | ## arguments for answer inspection button |
819 | ## arguments for answer inspection button |
| 752 | #my $prof_url = $ce->{webworkURLs}->{oldProf}; |
820 | #my $prof_url = $ce->{webworkURLs}->{oldProf}; |
| … | |
… | |
| 880 | #$header .= CGI::th("Part"); |
948 | #$header .= CGI::th("Part"); |
| 881 | $header .= $showAttemptAnswers ? CGI::th("Entered") : ""; |
949 | $header .= $showAttemptAnswers ? CGI::th("Entered") : ""; |
| 882 | $header .= $showAttemptPreview ? CGI::th("Answer Preview") : ""; |
950 | $header .= $showAttemptPreview ? CGI::th("Answer Preview") : ""; |
| 883 | $header .= $showCorrectAnswers ? CGI::th("Correct") : ""; |
951 | $header .= $showCorrectAnswers ? CGI::th("Correct") : ""; |
| 884 | $header .= $showAttemptResults ? CGI::th("Result") : ""; |
952 | $header .= $showAttemptResults ? CGI::th("Result") : ""; |
| 885 | $header .= $showMessages ? CGI::th("messages") : ""; |
953 | $header .= $showMessages ? CGI::th("Messages") : ""; |
| 886 | my @tableRows = ( $header ); |
954 | my @tableRows = ( $header ); |
| 887 | my $numCorrect; |
955 | my $numCorrect; |
| 888 | foreach my $name (@answerNames) { |
956 | foreach my $name (@answerNames) { |
| 889 | my $answerResult = $pg->{answers}->{$name}; |
957 | my $answerResult = $pg->{answers}->{$name}; |
| 890 | my $studentAnswer = $answerResult->{student_ans}; # original_student_ans |
958 | my $studentAnswer = $answerResult->{student_ans}; # original_student_ans |
| … | |
… | |
| 921 | # my $summary = "On this attempt, you answered $numCorrect out of " |
989 | # my $summary = "On this attempt, you answered $numCorrect out of " |
| 922 | # . scalar @answerNames . " $numIncorrectNoun correct, for a score of $scorePercent."; |
990 | # . scalar @answerNames . " $numIncorrectNoun correct, for a score of $scorePercent."; |
| 923 | my $summary = ""; |
991 | my $summary = ""; |
| 924 | if (scalar @answerNames == 1) { |
992 | if (scalar @answerNames == 1) { |
| 925 | if ($numCorrect == scalar @answerNames) { |
993 | if ($numCorrect == scalar @answerNames) { |
| 926 | $summary .= CGI::div({'class="ResultsWithoutError"'},"The above answer is correct."); |
994 | $summary .= CGI::div({class=>"ResultsWithoutError"},"The above answer is correct."); |
| 927 | } else { |
995 | } else { |
| 928 | $summary .= CGI::div({'class="ResultsWithError"'},"The above answer is NOT correct."); |
996 | $summary .= CGI::div({class=>"ResultsWithError"},"The above answer is NOT correct."); |
| 929 | } |
997 | } |
| 930 | } else { |
998 | } else { |
| 931 | if ($numCorrect == scalar @answerNames) { |
999 | if ($numCorrect == scalar @answerNames) { |
| 932 | $summary .= CGI::div({'class="ResultsWithoutError"'},"All of the above answers are correct."); |
1000 | $summary .= CGI::div({class=>"ResultsWithoutError"},"All of the above answers are correct."); |
| 933 | } else { |
1001 | } else { |
| 934 | $summary .= CGI::div({'class="ResultsWithError"'},"At least one of the above answers is NOT correct."); |
1002 | $summary .= CGI::div({class=>"ResultsWithError"},"At least one of the above answers is NOT correct."); |
| 935 | } |
1003 | } |
| 936 | } |
1004 | } |
| 937 | |
1005 | |
| 938 | return |
1006 | return |
| 939 | CGI::table({-class=>"attemptResults"}, CGI::Tr(\@tableRows)) |
1007 | CGI::table({-class=>"attemptResults"}, CGI::Tr(\@tableRows)) |
| … | |
… | |
| 947 | # # $str is a complex number |
1015 | # # $str is a complex number |
| 948 | #} |
1016 | #} |
| 949 | |
1017 | |
| 950 | sub viewOptions { |
1018 | sub viewOptions { |
| 951 | my ($self) = @_; |
1019 | my ($self) = @_; |
|
|
1020 | my $ce = $self->r->ce; |
| 952 | |
1021 | |
| 953 | my $displayMode = $self->{displayMode}; |
1022 | my $displayMode = $self->{displayMode}; |
| 954 | my %must = %{ $self->{must} }; |
1023 | my %must = %{ $self->{must} }; |
| 955 | my %can = %{ $self->{can} }; |
1024 | my %can = %{ $self->{can} }; |
| 956 | my %will = %{ $self->{will} }; |
1025 | my %will = %{ $self->{will} }; |
| … | |
… | |
| 964 | -label => "Saved answers", |
1033 | -label => "Saved answers", |
| 965 | ), " ".CGI::br(); |
1034 | ), " ".CGI::br(); |
| 966 | |
1035 | |
| 967 | $optionLine and $optionLine .= join "", CGI::br(); |
1036 | $optionLine and $optionLine .= join "", CGI::br(); |
| 968 | |
1037 | |
|
|
1038 | my %display_modes = %{WeBWorK::PG::DISPLAY_MODES()}; |
|
|
1039 | my @active_modes = grep { exists $display_modes{$_} } |
|
|
1040 | @{$ce->{pg}->{displayModes}}; |
|
|
1041 | |
| 969 | return CGI::div({-style=>"border: thin groove; padding: 1ex; margin: 2ex align: left"}, |
1042 | return CGI::div({-style=>"border: thin groove; padding: 1ex; margin: 2ex align: left"}, |
| 970 | "View equations as: ".CGI::br(), |
1043 | "View equations as: ".CGI::br(), |
| 971 | CGI::radio_group( |
1044 | CGI::radio_group( |
| 972 | -name => "displayMode", |
1045 | -name => "displayMode", |
| 973 | -values => ['plainText', 'formattedText', 'images'], |
1046 | -values => \@active_modes, |
| 974 | -default => $displayMode, |
1047 | -default => $displayMode, |
| 975 | -linebreak=>'true', |
1048 | -linebreak=>'true', |
| 976 | -labels => { |
1049 | -labels => { |
| 977 | plainText => "plain", |
1050 | plainText => "plain", |
| 978 | formattedText => "formatted", |
1051 | formattedText => "formatted", |
| 979 | images => "images", |
1052 | images => "images", |
|
|
1053 | jsMath => "jsMath", |
|
|
1054 | asciimath => "asciimath", |
| 980 | } |
1055 | }, |
| 981 | ), CGI::br(),CGI::hr(), |
1056 | ), CGI::br(),CGI::hr(), |
| 982 | $optionLine, |
1057 | $optionLine, |
| 983 | CGI::submit(-name=>"redisplay", -label=>"Save Options"), |
1058 | CGI::submit(-name=>"redisplay", -label=>"Save Options"), |
| 984 | ); |
1059 | ); |
| 985 | } |
1060 | } |
| … | |
… | |
| 1014 | if ($?) { |
1089 | if ($?) { |
| 1015 | return "<b>[tth failed: $? $@]</b>"; |
1090 | return "<b>[tth failed: $? $@]</b>"; |
| 1016 | } |
1091 | } |
| 1017 | return $result; |
1092 | return $result; |
| 1018 | } elsif ($displayMode eq "images") { |
1093 | } elsif ($displayMode eq "images") { |
| 1019 | $imgGen->add($answerResult->{preview_latex_string}); |
1094 | $imgGen->add($tex); |
|
|
1095 | } elsif ($displayMode eq "jsMath") { |
|
|
1096 | |
|
|
1097 | return '<DIV CLASS="math">'.$tex.'</DIV>' ; |
|
|
1098 | |
|
|
1099 | |
|
|
1100 | |
|
|
1101 | |
| 1020 | } |
1102 | } |
| 1021 | } |
1103 | } |
| 1022 | |
1104 | |
| 1023 | ##### permission queries ##### |
1105 | ##### permission queries ##### |
| 1024 | |
1106 | |
| … | |
… | |
| 1060 | sub mustRecordAnswers($) { |
1142 | sub mustRecordAnswers($) { |
| 1061 | my ($permissionLevel) = @_; |
1143 | my ($permissionLevel) = @_; |
| 1062 | return $permissionLevel == 0; |
1144 | return $permissionLevel == 0; |
| 1063 | } |
1145 | } |
| 1064 | |
1146 | |
|
|
1147 | |
|
|
1148 | # FIXME: does this even get used? |
|
|
1149 | sub submiterror { |
|
|
1150 | my $self = shift; |
|
|
1151 | my $submiterror = (defined($self->{submiterror}) ) ? $self->{submiterror} : ''; |
|
|
1152 | $submiterror; |
|
|
1153 | } |
| 1065 | 1; |
1154 | 1; |