| … | |
… | |
| 13 | # FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the |
13 | # FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the |
| 14 | # Artistic License for more details. |
14 | # Artistic License for more details. |
| 15 | ################################################################################ |
15 | ################################################################################ |
| 16 | |
16 | |
| 17 | package WeBWorK::ContentGenerator::Problem; |
17 | package WeBWorK::ContentGenerator::Problem; |
| 18 | use base qw(WeBWorK WeBWorK::ContentGenerator WeBWorK::ContentGenerator::ProblemUtil::ProblemUtil); |
18 | use base qw(WeBWorK::ContentGenerator); |
| 19 | |
19 | |
| 20 | =head1 NAME |
20 | =head1 NAME |
| 21 | |
21 | |
| 22 | WeBWorK::ContentGenerator::Problem - Allow a student to interact with a problem. |
22 | WeBWorK::ContentGenerator::Problem - Allow a student to interact with a problem. |
| 23 | |
23 | |
| … | |
… | |
| 35 | use WeBWorK::PG::IO; |
35 | use WeBWorK::PG::IO; |
| 36 | use WeBWorK::Utils qw(readFile writeLog writeCourseLog encodeAnswers decodeAnswers |
36 | use WeBWorK::Utils qw(readFile writeLog writeCourseLog encodeAnswers decodeAnswers |
| 37 | ref2string makeTempDirectory path_is_subdir sortByName before after between); |
37 | ref2string makeTempDirectory path_is_subdir sortByName before after between); |
| 38 | use WeBWorK::DB::Utils qw(global2user user2global); |
38 | use WeBWorK::DB::Utils qw(global2user user2global); |
| 39 | use URI::Escape; |
39 | use URI::Escape; |
| 40 | |
40 | use WeBWorK::Localize; |
| 41 | use WeBWorK::Utils::Tasks qw(fake_set fake_problem); |
41 | use WeBWorK::Utils::Tasks qw(fake_set fake_problem); |
| 42 | |
42 | |
| 43 | ################################################################################ |
43 | ################################################################################ |
| 44 | # CGI param interface to this module (up-to-date as of v1.153) |
44 | # CGI param interface to this module (up-to-date as of v1.153) |
| 45 | ################################################################################ |
45 | ################################################################################ |
| … | |
… | |
| 191 | # Note: the substance of attemptResults is lifted into GatewayQuiz.pm, |
191 | # Note: the substance of attemptResults is lifted into GatewayQuiz.pm, |
| 192 | # with some changes to the output format |
192 | # with some changes to the output format |
| 193 | |
193 | |
| 194 | sub attemptResults { |
194 | sub attemptResults { |
| 195 | my $self = shift; |
195 | my $self = shift; |
|
|
196 | my $r = $self->r; |
| 196 | my $pg = shift; |
197 | my $pg = shift; |
| 197 | my $showAttemptAnswers = shift; |
198 | my $showAttemptAnswers = shift; |
| 198 | my $showCorrectAnswers = shift; |
199 | my $showCorrectAnswers = shift; |
| 199 | my $showAttemptResults = $showAttemptAnswers && shift; |
200 | my $showAttemptResults = $showAttemptAnswers && shift; |
| 200 | my $showSummary = shift; |
201 | my $showSummary = shift; |
| … | |
… | |
| 232 | my $showEvaluatedAnswers = $ce->{pg}->{options}->{showEvaluatedAnswers}; |
233 | my $showEvaluatedAnswers = $ce->{pg}->{options}->{showEvaluatedAnswers}; |
| 233 | |
234 | |
| 234 | my $header; |
235 | my $header; |
| 235 | #$header .= CGI::th("Part"); |
236 | #$header .= CGI::th("Part"); |
| 236 | if ($showEvaluatedAnswers) { |
237 | if ($showEvaluatedAnswers) { |
| 237 | $header .= $showAttemptAnswers ? CGI::th("Entered") : ""; |
238 | $header .= $showAttemptAnswers ? CGI::th($r->maketext("Entered")) : ""; |
| 238 | } |
239 | } |
| 239 | $header .= $showAttemptPreview ? CGI::th("Answer Preview") : ""; |
240 | $header .= $showAttemptPreview ? CGI::th($r->maketext("Answer Preview")) : ""; |
| 240 | $header .= $showCorrectAnswers ? CGI::th("Correct") : ""; |
241 | $header .= $showCorrectAnswers ? CGI::th($r->maketext("Correct")) : ""; |
| 241 | $header .= $showAttemptResults ? CGI::th("Result") : ""; |
242 | $header .= $showAttemptResults ? CGI::th($r->maketext("Result")) : ""; |
| 242 | $header .= $showMessages ? CGI::th("Messages") : ""; |
243 | $header .= $showMessages ? CGI::th($r->maketext("Messages")) : ""; |
| 243 | my $fully = ''; |
244 | my $fully = ''; |
| 244 | my @tableRows = ( $header ); |
245 | my @tableRows = ( $header ); |
| 245 | my $numCorrect = 0; |
246 | my $numCorrect = 0; |
| 246 | my $numBlanks =0; |
247 | my $numBlanks =0; |
| 247 | my $tthPreambleCache; |
248 | my $tthPreambleCache; |
| … | |
… | |
| 256 | my $answerScore = $answerResult->{score}; |
257 | my $answerScore = $answerResult->{score}; |
| 257 | my $answerMessage = $showMessages ? $answerResult->{ans_message} : ""; |
258 | my $answerMessage = $showMessages ? $answerResult->{ans_message} : ""; |
| 258 | $answerMessage =~ s/\n/<BR>/g; |
259 | $answerMessage =~ s/\n/<BR>/g; |
| 259 | $numCorrect += $answerScore >= 1; |
260 | $numCorrect += $answerScore >= 1; |
| 260 | $numBlanks++ unless $studentAnswer =~/\S/ || $answerScore >= 1; # unless student answer contains entry |
261 | $numBlanks++ unless $studentAnswer =~/\S/ || $answerScore >= 1; # unless student answer contains entry |
| 261 | my $resultString = $answerScore >= 1 ? CGI::span({class=>"ResultsWithoutError"}, "correct") : |
262 | my $resultString = $answerScore >= 1 ? CGI::span({class=>"ResultsWithoutError"}, $r->maketext("correct")) : |
| 262 | $answerScore > 0 ? int($answerScore*100)."% correct" : |
263 | $answerScore > 0 ? $r->maketext("[_1]% correct", int($answerScore*100)): |
| 263 | CGI::span({class=>"ResultsWithError"}, "incorrect"); |
264 | CGI::span({class=>"ResultsWithError"}, $r->maketext("incorrect")); |
| 264 | $fully = 'completely ' if $answerScore >0 and $answerScore < 1; |
265 | $fully = $r->maketext("completely ") if $answerScore >0 and $answerScore < 1; |
| 265 | |
266 | |
|
|
267 | |
|
|
268 | #warn "answer $name score $answerScore"; |
| 266 | push @correct_ids, $name if $answerScore == 1; |
269 | push @correct_ids, $name if $answerScore == 1; |
| 267 | push @incorrect_ids, $name if $answerScore < 1; |
270 | push @incorrect_ids, $name if $answerScore < 1; |
| 268 | |
271 | |
| 269 | # need to capture auxiliary answers as well and identify their ids. |
272 | # need to capture auxiliary answers as well and identify their ids. |
| 270 | |
273 | |
| … | |
… | |
| 297 | # . scalar @answerNames . " $numIncorrectNoun correct, for a score of $scorePercent."; |
300 | # . scalar @answerNames . " $numIncorrectNoun correct, for a score of $scorePercent."; |
| 298 | my $summary = ""; |
301 | my $summary = ""; |
| 299 | unless (defined($problemResult->{summary}) and $problemResult->{summary} =~ /\S/) { |
302 | unless (defined($problemResult->{summary}) and $problemResult->{summary} =~ /\S/) { |
| 300 | if (scalar @answerNames == 1) { #default messages |
303 | if (scalar @answerNames == 1) { #default messages |
| 301 | if ($numCorrect == scalar @answerNames) { |
304 | if ($numCorrect == scalar @answerNames) { |
| 302 | $summary .= CGI::div({class=>"ResultsWithoutError"},"The answer above is correct."); |
305 | $summary .= CGI::div({class=>"ResultsWithoutError"},$r->maketext("The answer above is correct.")); |
| 303 | } else { |
306 | } else { |
| 304 | $summary .= CGI::div({class=>"ResultsWithError"},"The answer above is NOT ${fully}correct."); |
307 | $summary .= CGI::div({class=>"ResultsWithError"},$r->maketext("The answer above is NOT [_1]correct.", $fully)); |
| 305 | } |
308 | } |
| 306 | } else { |
309 | } else { |
| 307 | if ($numCorrect == scalar @answerNames) { |
310 | if ($numCorrect == scalar @answerNames) { |
| 308 | $summary .= CGI::div({class=>"ResultsWithoutError"},"All of the answers above are correct."); |
311 | $summary .= CGI::div({class=>"ResultsWithoutError"},$r->maketext("All of the answers above are correct.")); |
| 309 | } |
312 | } |
| 310 | #unless ($numCorrect + $numBlanks == scalar( @answerNames)) { # this allowed you to figure out if you got one answer right. |
313 | #unless ($numCorrect + $numBlanks == scalar( @answerNames)) { # this allowed you to figure out if you got one answer right. |
| 311 | elsif ($numBlanks != scalar( @answerNames)) { |
314 | elsif ($numBlanks != scalar( @answerNames)) { |
| 312 | $summary .= CGI::div({class=>"ResultsWithError"},"At least one of the answers above is NOT ${fully}correct."); |
315 | $summary .= CGI::div({class=>"ResultsWithError"},$r->maketext("At least one of the answers above is NOT [_1]correct.", $fully)); |
| 313 | } |
316 | } |
| 314 | if ($numBlanks) { |
317 | if ($numBlanks) { |
| 315 | my $s = ($numBlanks>1)?'':'s'; |
318 | my $s = ($numBlanks>1)?'':'s'; |
| 316 | $summary .= CGI::div({class=>"ResultsAlert"},"$numBlanks of the questions remain$s unanswered."); |
319 | $summary .= CGI::div({class=>"ResultsAlert"},$r->maketext("[quant,_1,of the questions remains,of the questions remain] unanswered.", $numBlanks)); |
| 317 | } |
320 | } |
| 318 | } |
321 | } |
| 319 | } else { |
322 | } else { |
| 320 | $summary = $problemResult->{summary}; # summary has been defined by grader |
323 | $summary = $problemResult->{summary}; # summary has been defined by grader |
| 321 | } |
324 | } |
| … | |
… | |
| 598 | |
601 | |
| 599 | # test for additional problem validity if it's not already invalid |
602 | # test for additional problem validity if it's not already invalid |
| 600 | } else { |
603 | } else { |
| 601 | $self->{invalidProblem} = !(defined $problem and ($set->visible || $authz->hasPermissions($userName, "view_hidden_sets"))); |
604 | $self->{invalidProblem} = !(defined $problem and ($set->visible || $authz->hasPermissions($userName, "view_hidden_sets"))); |
| 602 | |
605 | |
| 603 | $self->addbadmessage(CGI::p("This problem will not count towards your grade.")) if $problem and not $problem->value and not $self->{invalidProblem}; |
606 | $self->addbadmessage(CGI::p($r->maketext("This problem will not count towards your grade."))) if $problem and not $problem->value and not $self->{invalidProblem}; |
| 604 | } |
607 | } |
| 605 | |
608 | |
| 606 | $self->{userName} = $userName; |
609 | $self->{userName} = $userName; |
| 607 | $self->{effectiveUserName} = $effectiveUserName; |
610 | $self->{effectiveUserName} = $effectiveUserName; |
| 608 | $self->{user} = $user; |
611 | $self->{user} = $user; |
| … | |
… | |
| 747 | |
750 | |
| 748 | return "" if ( $self->{invalidSet} ); |
751 | return "" if ( $self->{invalidSet} ); |
| 749 | return $self->{pg}->{head_text} if $self->{pg}->{head_text}; |
752 | return $self->{pg}->{head_text} if $self->{pg}->{head_text}; |
| 750 | } |
753 | } |
| 751 | |
754 | |
| 752 | sub post_head { |
|
|
| 753 | my ($self) = @_; |
|
|
| 754 | |
|
|
| 755 | return "" if ( $self->{invalidSet} ); |
|
|
| 756 | return $self->{pg}->{head_text} if $self->{pg}->{post_header_text}; |
|
|
| 757 | } |
|
|
| 758 | |
|
|
| 759 | sub options { |
755 | sub options { |
| 760 | my ($self) = @_; |
756 | my ($self) = @_; |
| 761 | #warn "doing options in Problem"; |
757 | #warn "doing options in Problem"; |
| 762 | |
758 | |
| 763 | # don't show options if we don't have anything to show |
759 | # don't show options if we don't have anything to show |
| … | |
… | |
| 790 | my $setID = $self->{set}->set_id; |
786 | my $setID = $self->{set}->set_id; |
| 791 | my $eUserID = $r->param("effectiveUser"); |
787 | my $eUserID = $r->param("effectiveUser"); |
| 792 | my @problemIDs = sort { $a <=> $b } $db->listUserProblems($eUserID, $setID); |
788 | my @problemIDs = sort { $a <=> $b } $db->listUserProblems($eUserID, $setID); |
| 793 | |
789 | |
| 794 | print CGI::start_div({class=>"info-box", id=>"fisheye"}); |
790 | print CGI::start_div({class=>"info-box", id=>"fisheye"}); |
| 795 | print CGI::h2("Problems"); |
791 | print CGI::h2($r->maketext("Problems")); |
| 796 | #print CGI::start_ul({class=>"LinksMenu"}); |
792 | #print CGI::start_ul({class=>"LinksMenu"}); |
| 797 | #print CGI::start_li(); |
793 | #print CGI::start_li(); |
| 798 | #print CGI::span({style=>"font-size:larger"}, "Problems"); |
794 | #print CGI::span({style=>"font-size:larger"}, "Problems"); |
| 799 | print CGI::start_ul(); |
795 | print CGI::start_ul(); |
| 800 | |
796 | |
| 801 | foreach my $problemID (@problemIDs) { |
797 | foreach my $problemID (@problemIDs) { |
| 802 | my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", |
798 | my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", $r, |
| 803 | courseID => $courseID, setID => $setID, problemID => $problemID); |
799 | courseID => $courseID, setID => $setID, problemID => $problemID); |
| 804 | print CGI::li(CGI::a( {href=>$self->systemLink($problemPage, |
800 | print CGI::li(CGI::a( {href=>$self->systemLink($problemPage, |
| 805 | params=>{ displayMode => $self->{displayMode}, |
801 | params=>{ displayMode => $self->{displayMode}, |
| 806 | showOldAnswers => $self->{will}->{showOldAnswers} |
802 | showOldAnswers => $self->{will}->{showOldAnswers} |
| 807 | })}, "Problem $problemID") |
803 | })}, $r->maketext("Problem [_1]",$problemID)) |
| 808 | ); |
804 | ); |
| 809 | } |
805 | } |
| 810 | |
806 | |
| 811 | print CGI::end_ul(); |
807 | print CGI::end_ul(); |
| 812 | #print CGI::end_li(); |
808 | #print CGI::end_li(); |
| … | |
… | |
| 842 | } |
838 | } |
| 843 | |
839 | |
| 844 | my @links; |
840 | my @links; |
| 845 | |
841 | |
| 846 | if ($prevID) { |
842 | if ($prevID) { |
| 847 | my $prevPage = $urlpath->newFromModule(__PACKAGE__, |
843 | my $prevPage = $urlpath->newFromModule(__PACKAGE__, $r, |
| 848 | courseID => $courseID, setID => $setID, problemID => $prevID); |
844 | courseID => $courseID, setID => $setID, problemID => $prevID); |
| 849 | push @links, "Previous Problem", $r->location . $prevPage->path, "navPrev"; |
845 | push @links, $r->maketext("Previous Problem"), $r->location . $prevPage->path, $r->maketext("navPrev"); |
| 850 | } else { |
846 | } else { |
| 851 | push @links, "Previous Problem", "", "navPrevGrey"; |
847 | push @links, $r->maketext("Previous Problem"), "", $r->maketext("navPrevGrey"); |
| 852 | } |
848 | } |
| 853 | |
849 | |
| 854 | if (defined($setID) && $setID ne 'Undefined_Set') { |
850 | if (defined($setID) && $setID ne 'Undefined_Set') { |
| 855 | push @links, "Problem List", $r->location . $urlpath->parent->path, "navProbList"; |
851 | push @links, $r->maketext("Problem List"), $r->location . $urlpath->parent->path, $r->maketext("navProbList"); |
| 856 | } else { |
852 | } else { |
| 857 | push @links, "Problem List", "", "navProbListGrey"; |
853 | push @links, $r->maketext("Problem List"), "", $r->maketext("navProbListGrey"); |
| 858 | } |
854 | } |
| 859 | |
855 | |
| 860 | if ($nextID) { |
856 | if ($nextID) { |
| 861 | my $nextPage = $urlpath->newFromModule(__PACKAGE__, |
857 | my $nextPage = $urlpath->newFromModule(__PACKAGE__, $r, |
| 862 | courseID => $courseID, setID => $setID, problemID => $nextID); |
858 | courseID => $courseID, setID => $setID, problemID => $nextID); |
| 863 | push @links, "Next Problem", $r->location . $nextPage->path, "navNext"; |
859 | push @links, $r->maketext("Next Problem"), $r->location . $nextPage->path, $r->maketext("navNext"); |
| 864 | } else { |
860 | } else { |
| 865 | push @links, "Next Problem", "", "navNextGrey"; |
861 | push @links, $r->maketext("Next Problem"), "", $r->maketext("navNextGrey"); |
| 866 | } |
862 | } |
| 867 | |
863 | |
| 868 | my $tail = ""; |
864 | my $tail = ""; |
| 869 | |
865 | |
| 870 | $tail .= "&displayMode=".$self->{displayMode} if defined $self->{displayMode}; |
866 | $tail .= "&displayMode=".$self->{displayMode} if defined $self->{displayMode}; |
| … | |
… | |
| 873 | return $self->navMacro($args, $tail, @links); |
869 | return $self->navMacro($args, $tail, @links); |
| 874 | } |
870 | } |
| 875 | |
871 | |
| 876 | sub title { |
872 | sub title { |
| 877 | my ($self) = @_; |
873 | my ($self) = @_; |
| 878 | |
874 | my $r = $self->r; |
| 879 | # using the url arguments won't break if the set/problem are invalid |
875 | # using the url arguments won't break if the set/problem are invalid |
| 880 | my $setID = WeBWorK::ContentGenerator::underscore2nbsp($self->r->urlpath->arg("setID")); |
876 | my $setID = WeBWorK::ContentGenerator::underscore2nbsp($self->r->urlpath->arg("setID")); |
| 881 | my $problemID = $self->r->urlpath->arg("problemID"); |
877 | my $problemID = $self->r->urlpath->arg("problemID"); |
| 882 | |
878 | |
| 883 | return "$setID: Problem $problemID"; |
879 | return $r->maketext("[_1]: Problem [_2]",$setID, $problemID); |
| 884 | } |
880 | } |
| 885 | |
881 | |
| 886 | |
|
|
| 887 | # now altered to outsource most output operations to the template, main functions now are simply error checking and answer processing - ghe3 |
|
|
| 888 | # sub body { |
|
|
| 889 | # my $self = shift; |
|
|
| 890 | # my $r = $self->r; |
|
|
| 891 | # my $ce = $r->ce; |
|
|
| 892 | # my $db = $r->db; |
|
|
| 893 | # my $authz = $r->authz; |
|
|
| 894 | # my $urlpath = $r->urlpath; |
|
|
| 895 | # my $user = $r->param('user'); |
|
|
| 896 | # my $effectiveUser = $r->param('effectiveUser'); |
|
|
| 897 | # if ( $self->{invalidSet} ) { |
|
|
| 898 | # return CGI::div({class=>"ResultsWithError"}, |
|
|
| 899 | # CGI::p("The selected problem set (" . |
|
|
| 900 | # $urlpath->arg("setID") . ") is not " . |
|
|
| 901 | # "a valid set for $effectiveUser:"), |
|
|
| 902 | # CGI::p($self->{invalidSet})); |
|
|
| 903 | # } |
|
|
| 904 | # |
|
|
| 905 | # if ($self->{invalidProblem}) { |
|
|
| 906 | # return CGI::div({class=>"ResultsWithError"}, |
|
|
| 907 | # CGI::p("The selected problem (" . $urlpath->arg("problemID") . ") is not a valid problem for set " . $self->{set}->set_id . ".")); |
|
|
| 908 | # } |
|
|
| 909 | # |
|
|
| 910 | # # unpack some useful variables |
|
|
| 911 | # my $set = $self->{set}; |
|
|
| 912 | # my $problem = $self->{problem}; |
|
|
| 913 | # my $editMode = $self->{editMode}; |
|
|
| 914 | # my $submitAnswers = $self->{submitAnswers}; |
|
|
| 915 | # my $checkAnswers = $self->{checkAnswers}; |
|
|
| 916 | # my $previewAnswers = $self->{previewAnswers}; |
|
|
| 917 | # my %want = %{ $self->{want} }; |
|
|
| 918 | # my %can = %{ $self->{can} }; |
|
|
| 919 | # my %must = %{ $self->{must} }; |
|
|
| 920 | # my %will = %{ $self->{will} }; |
|
|
| 921 | # my $pg = $self->{pg}; |
|
|
| 922 | # |
|
|
| 923 | # my $courseName = $urlpath->arg("courseID"); |
|
|
| 924 | # |
|
|
| 925 | # # FIXME: move editor link to top, next to problem number. |
|
|
| 926 | # # format as "[edit]" like we're doing with course info file, etc. |
|
|
| 927 | # # add edit link for set as well. |
|
|
| 928 | # my $editorLink = ""; |
|
|
| 929 | # # if we are here without a real homework set, carry that through |
|
|
| 930 | # my $forced_field = []; |
|
|
| 931 | # $forced_field = ['sourceFilePath' => $r->param("sourceFilePath")] if |
|
|
| 932 | # ($set->set_id eq 'Undefined_Set'); |
|
|
| 933 | # if ($authz->hasPermissions($user, "modify_problem_sets")) { |
|
|
| 934 | # my $editorPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", |
|
|
| 935 | # courseID => $courseName, setID => $set->set_id, problemID => $problem->problem_id); |
|
|
| 936 | # my $editorURL = $self->systemLink($editorPage, params=>$forced_field); |
|
|
| 937 | # $editorLink = CGI::p(CGI::a({href=>$editorURL,target =>'WW_Editor'}, "Edit this problem")); |
|
|
| 938 | # } |
|
|
| 939 | # |
|
|
| 940 | # ##### translation errors? ##### |
|
|
| 941 | # |
|
|
| 942 | # if ($pg->{flags}->{error_flag}) { |
|
|
| 943 | # if ($authz->hasPermissions($user, "view_problem_debugging_info")) { |
|
|
| 944 | # print $self->errorOutput($pg->{errors}, $pg->{body_text}); |
|
|
| 945 | # } else { |
|
|
| 946 | # print $self->errorOutput($pg->{errors}, "You do not have permission to view the details of this error."); |
|
|
| 947 | # } |
|
|
| 948 | # print $editorLink; |
|
|
| 949 | # return ""; |
|
|
| 950 | # } |
|
|
| 951 | # |
|
|
| 952 | # ##### answer processing ##### |
|
|
| 953 | # debug("begin answer processing"); |
|
|
| 954 | # # if answers were submitted: |
|
|
| 955 | # my $scoreRecordedMessage; |
|
|
| 956 | # my $pureProblem; |
|
|
| 957 | # if ($submitAnswers) { |
|
|
| 958 | # # get a "pure" (unmerged) UserProblem to modify |
|
|
| 959 | # # this will be undefined if the problem has not been assigned to this user |
|
|
| 960 | # $pureProblem = $db->getUserProblem($problem->user_id, $problem->set_id, $problem->problem_id); # checked |
|
|
| 961 | # if (defined $pureProblem) { |
|
|
| 962 | # # store answers in DB for sticky answers |
|
|
| 963 | # my %answersToStore; |
|
|
| 964 | # my %answerHash = %{ $pg->{answers} }; |
|
|
| 965 | # $answersToStore{$_} = $self->{formFields}->{$_} #$answerHash{$_}->{original_student_ans} -- this may have been modified for fields with multiple values. Don't use it!! |
|
|
| 966 | # foreach (keys %answerHash); |
|
|
| 967 | # |
|
|
| 968 | # # There may be some more answers to store -- one which are auxiliary entries to a primary answer. Evaluating |
|
|
| 969 | # # matrices works in this way, only the first answer triggers an answer evaluator, the rest are just inputs |
|
|
| 970 | # # however we need to store them. Fortunately they are still in the input form. |
|
|
| 971 | # my @extra_answer_names = @{ $pg->{flags}->{KEPT_EXTRA_ANSWERS}}; |
|
|
| 972 | # $answersToStore{$_} = $self->{formFields}->{$_} foreach (@extra_answer_names); |
|
|
| 973 | # |
|
|
| 974 | # # Now let's encode these answers to store them -- append the extra answers to the end of answer entry order |
|
|
| 975 | # my @answer_order = (@{$pg->{flags}->{ANSWER_ENTRY_ORDER}}, @extra_answer_names); |
|
|
| 976 | # my $answerString = encodeAnswers(%answersToStore, |
|
|
| 977 | # @answer_order); |
|
|
| 978 | # |
|
|
| 979 | # # store last answer to database |
|
|
| 980 | # $problem->last_answer($answerString); |
|
|
| 981 | # $pureProblem->last_answer($answerString); |
|
|
| 982 | # $db->putUserProblem($pureProblem); |
|
|
| 983 | # |
|
|
| 984 | # # store state in DB if it makes sense |
|
|
| 985 | # if ($will{recordAnswers}) { |
|
|
| 986 | # $problem->status($pg->{state}->{recorded_score}); |
|
|
| 987 | # $problem->sub_status($pg->{state}->{sub_recorded_score}); |
|
|
| 988 | # $problem->attempted(1); |
|
|
| 989 | # $problem->num_correct($pg->{state}->{num_of_correct_ans}); |
|
|
| 990 | # $problem->num_incorrect($pg->{state}->{num_of_incorrect_ans}); |
|
|
| 991 | # $pureProblem->status($pg->{state}->{recorded_score}); |
|
|
| 992 | # $pureProblem->sub_status($pg->{state}->{sub_recorded_score}); |
|
|
| 993 | # $pureProblem->attempted(1); |
|
|
| 994 | # $pureProblem->num_correct($pg->{state}->{num_of_correct_ans}); |
|
|
| 995 | # $pureProblem->num_incorrect($pg->{state}->{num_of_incorrect_ans}); |
|
|
| 996 | # if ($db->putUserProblem($pureProblem)) { |
|
|
| 997 | # $scoreRecordedMessage = "Your score was recorded."; |
|
|
| 998 | # } else { |
|
|
| 999 | # $scoreRecordedMessage = "Your score was not recorded because there was a failure in storing the problem record to the database."; |
|
|
| 1000 | # } |
|
|
| 1001 | # # write to the transaction log, just to make sure |
|
|
| 1002 | # writeLog($self->{ce}, "transaction", |
|
|
| 1003 | # $problem->problem_id."\t". |
|
|
| 1004 | # $problem->set_id."\t". |
|
|
| 1005 | # $problem->user_id."\t". |
|
|
| 1006 | # $problem->source_file."\t". |
|
|
| 1007 | # $problem->value."\t". |
|
|
| 1008 | # $problem->max_attempts."\t". |
|
|
| 1009 | # $problem->problem_seed."\t". |
|
|
| 1010 | # $pureProblem->status."\t". |
|
|
| 1011 | # $pureProblem->attempted."\t". |
|
|
| 1012 | # $pureProblem->last_answer."\t". |
|
|
| 1013 | # $pureProblem->num_correct."\t". |
|
|
| 1014 | # $pureProblem->num_incorrect |
|
|
| 1015 | # ); |
|
|
| 1016 | # } else { |
|
|
| 1017 | # if (before($set->open_date) or after($set->due_date)) { |
|
|
| 1018 | # $scoreRecordedMessage = "Your score was not recorded because this homework set is closed."; |
|
|
| 1019 | # } else { |
|
|
| 1020 | # $scoreRecordedMessage = "Your score was not recorded."; |
|
|
| 1021 | # } |
|
|
| 1022 | # } |
|
|
| 1023 | # } else { |
|
|
| 1024 | # $scoreRecordedMessage = "Your score was not recorded because this problem has not been assigned to you."; |
|
|
| 1025 | # } |
|
|
| 1026 | # } |
|
|
| 1027 | # |
|
|
| 1028 | # # logging student answers |
|
|
| 1029 | # |
|
|
| 1030 | # my $answer_log = $self->{ce}->{courseFiles}->{logs}->{'answer_log'}; |
|
|
| 1031 | # if ( defined($answer_log ) and defined($pureProblem)) { |
|
|
| 1032 | # if ($submitAnswers && !$authz->hasPermissions($effectiveUser, "dont_log_past_answers")) { |
|
|
| 1033 | # my $answerString = ""; my $scores = ""; |
|
|
| 1034 | # my %answerHash = %{ $pg->{answers} }; |
|
|
| 1035 | # # FIXME this is the line 552 error. make sure original student ans is defined. |
|
|
| 1036 | # # The fact that it is not defined is probably due to an error in some answer evaluator. |
|
|
| 1037 | # # But I think it is useful to suppress this error message in the log. |
|
|
| 1038 | # foreach (sortByName(undef, keys %answerHash)) { |
|
|
| 1039 | # my $orig_ans = $answerHash{$_}->{original_student_ans}; |
|
|
| 1040 | # my $student_ans = defined $orig_ans ? $orig_ans : ''; |
|
|
| 1041 | # $answerString .= $student_ans."\t"; |
|
|
| 1042 | # $scores .= $answerHash{$_}->{score} >= 1 ? "1" : "0"; |
|
|
| 1043 | # } |
|
|
| 1044 | # $answerString = '' unless defined($answerString); # insure string is defined. |
|
|
| 1045 | # writeCourseLog($self->{ce}, "answer_log", |
|
|
| 1046 | # join("", |
|
|
| 1047 | # '|', $problem->user_id, |
|
|
| 1048 | # '|', $problem->set_id, |
|
|
| 1049 | # '|', $problem->problem_id, |
|
|
| 1050 | # '|', $scores, "\t", |
|
|
| 1051 | # time(),"\t", |
|
|
| 1052 | # $answerString, |
|
|
| 1053 | # ), |
|
|
| 1054 | # ); |
|
|
| 1055 | # |
|
|
| 1056 | # } |
|
|
| 1057 | # } |
|
|
| 1058 | # |
|
|
| 1059 | # debug("end answer processing"); |
|
|
| 1060 | # ##### javaScripts ############# |
|
|
| 1061 | # my $site_url = $ce->{webworkURLs}->{htdocs}; |
|
|
| 1062 | # print CGI::start_script({type=>"text/javascript", src=>"$site_url/js/wz_tooltip.js"}), CGI::end_script(); |
|
|
| 1063 | # |
|
|
| 1064 | # ##### output ##### |
|
|
| 1065 | # # custom message for editor |
|
|
| 1066 | # if ($authz->hasPermissions($user, "modify_problem_sets") and defined $editMode) { |
|
|
| 1067 | # if ($editMode eq "temporaryFile") { |
|
|
| 1068 | # print CGI::p(CGI::div({class=>'temporaryFile'}, "Viewing temporary file: ", $problem->source_file)); |
|
|
| 1069 | # } elsif ($editMode eq "savedFile") { |
|
|
| 1070 | # # taken care of in the initialization phase |
|
|
| 1071 | # } |
|
|
| 1072 | # } |
|
|
| 1073 | # print CGI::start_div({class=>"problemHeader"}); |
|
|
| 1074 | # |
|
|
| 1075 | # |
|
|
| 1076 | # |
|
|
| 1077 | # # attempt summary |
|
|
| 1078 | # #FIXME -- the following is a kludge: if showPartialCorrectAnswers is negative don't show anything. |
|
|
| 1079 | # # until after the due date |
|
|
| 1080 | # # do I need to check $will{showCorrectAnswers} to make preflight work?? |
|
|
| 1081 | # if (($pg->{flags}->{showPartialCorrectAnswers} >= 0 and $submitAnswers) ) { |
|
|
| 1082 | # # print this if user submitted answers OR requested correct answers |
|
|
| 1083 | # |
|
|
| 1084 | # print $self->attemptResults($pg, 1, |
|
|
| 1085 | # $will{showCorrectAnswers}, |
|
|
| 1086 | # $pg->{flags}->{showPartialCorrectAnswers}, 1, 1); |
|
|
| 1087 | # } elsif ($checkAnswers) { |
|
|
| 1088 | # # print this if user previewed answers |
|
|
| 1089 | # print CGI::div({class=>'ResultsWithError'},"ANSWERS ONLY CHECKED -- ANSWERS NOT RECORDED"), CGI::br(); |
|
|
| 1090 | # print $self->attemptResults($pg, 1, $will{showCorrectAnswers}, 1, 1, 1); |
|
|
| 1091 | # # show attempt answers |
|
|
| 1092 | # # show correct answers if asked |
|
|
| 1093 | # # show attempt results (correctness) |
|
|
| 1094 | # # show attempt previews |
|
|
| 1095 | # } elsif ($previewAnswers) { |
|
|
| 1096 | # # print this if user previewed answers |
|
|
| 1097 | # print CGI::div({class=>'ResultsWithError'},"PREVIEW ONLY -- ANSWERS NOT RECORDED"),CGI::br(),$self->attemptResults($pg, 1, 0, 0, 0, 1); |
|
|
| 1098 | # # show attempt answers |
|
|
| 1099 | # # don't show correct answers |
|
|
| 1100 | # # don't show attempt results (correctness) |
|
|
| 1101 | # # show attempt previews |
|
|
| 1102 | # } |
|
|
| 1103 | # |
|
|
| 1104 | # print CGI::end_div(); |
|
|
| 1105 | # |
|
|
| 1106 | # |
|
|
| 1107 | # ########################### |
|
|
| 1108 | # # print style sheet for correct and incorrect answers |
|
|
| 1109 | # ########################### |
|
|
| 1110 | # # always show colors for checkAnswers |
|
|
| 1111 | # # show colors for submit answer if |
|
|
| 1112 | # if (($self->{checkAnswers}) or ($self->{submitAnswers} and $pg->{flags}->{showPartialCorrectAnswers}) ) { |
|
|
| 1113 | # print CGI::start_style({type=>"text/css"}); |
|
|
| 1114 | # #FIXME -- this hack is no longer needed? |
|
|
| 1115 | # # my $string =""; |
|
|
| 1116 | # # foreach my $ans_name (@{ $self->{correct_ids} }) { |
|
|
| 1117 | # # $string .= '#'. ( $ans_name ). $ce->{pg}{options}{correct_answer}."\n"; |
|
|
| 1118 | # # } |
|
|
| 1119 | # # print $string; |
|
|
| 1120 | # # $string =""; |
|
|
| 1121 | # # foreach my $ans_name (@{ $self->{incorrect_ids} }) { |
|
|
| 1122 | # # $string .= '#'. ($ ans_name). $ce->{pg}{options}{incorrect_answer}."\n"; |
|
|
| 1123 | # # } |
|
|
| 1124 | # # print $string; |
|
|
| 1125 | # # the above method keeps one bad array ID from ruining all of the assignments. |
|
|
| 1126 | # print '#'.join(', #', @{ $self->{correct_ids} }), $ce->{pg}{options}{correct_answer},"\n" if ref( $self->{correct_ids} )=~/ARRAY/; #correct green |
|
|
| 1127 | # print '#'.join(', #', @{ $self->{incorrect_ids} }), $ce->{pg}{options}{incorrect_answer},"\n" if ref( $self->{incorrect_ids})=~/ARRAY/; #incorrect reddish |
|
|
| 1128 | # print CGI::end_style(); |
|
|
| 1129 | # } |
|
|
| 1130 | # ########################### |
|
|
| 1131 | # # post_header material |
|
|
| 1132 | # ########################### |
|
|
| 1133 | # print CGI::p($pg->{post_header_text}); |
|
|
| 1134 | # ########################### |
|
|
| 1135 | # # main form |
|
|
| 1136 | # ########################### |
|
|
| 1137 | # print "\n"; |
|
|
| 1138 | # |
|
|
| 1139 | # print CGI::start_form(-method=>"POST", -action=> $r->uri,-name=>"problemMainForm", onsubmit=>"submitAction()"); |
|
|
| 1140 | # print $self->hidden_authen_fields; |
|
|
| 1141 | # print "\n"; |
|
|
| 1142 | # print CGI::start_div({class=>"problem"}); |
|
|
| 1143 | # print CGI::p($pg->{body_text}); |
|
|
| 1144 | # print CGI::p(CGI::b("Note: "). CGI::i($pg->{result}->{msg})) if $pg->{result}->{msg}; |
|
|
| 1145 | # print $editorLink; # this is empty unless it is appropriate to have an editor link. |
|
|
| 1146 | # print CGI::end_div(); |
|
|
| 1147 | # |
|
|
| 1148 | # print CGI::start_p(); |
|
|
| 1149 | # |
|
|
| 1150 | # if ($can{showCorrectAnswers}) { |
|
|
| 1151 | # print CGI::checkbox( |
|
|
| 1152 | # -name => "showCorrectAnswers", |
|
|
| 1153 | # -checked => $will{showCorrectAnswers}, |
|
|
| 1154 | # -label => "Show correct answers", |
|
|
| 1155 | # -value => 1, |
|
|
| 1156 | # ); |
|
|
| 1157 | # } |
|
|
| 1158 | # if ($can{showHints}) { |
|
|
| 1159 | # print CGI::div({style=>"color:red"}, |
|
|
| 1160 | # CGI::checkbox( |
|
|
| 1161 | # -name => "showHints", |
|
|
| 1162 | # -checked => $will{showHints}, |
|
|
| 1163 | # -label => "Show Hints", |
|
|
| 1164 | # -value =>1, |
|
|
| 1165 | # ) |
|
|
| 1166 | # ); |
|
|
| 1167 | # } |
|
|
| 1168 | # if ($can{showSolutions}) { |
|
|
| 1169 | # print CGI::checkbox( |
|
|
| 1170 | # -name => "showSolutions", |
|
|
| 1171 | # -checked => $will{showSolutions}, |
|
|
| 1172 | # -label => "Show Solutions", |
|
|
| 1173 | # -value => 1, |
|
|
| 1174 | # ); |
|
|
| 1175 | # } |
|
|
| 1176 | # |
|
|
| 1177 | # if ($can{showCorrectAnswers} or $can{showHints} or $can{showSolutions}) { |
|
|
| 1178 | # print CGI::br(); |
|
|
| 1179 | # } |
|
|
| 1180 | # |
|
|
| 1181 | # print CGI::submit(-name=>"previewAnswers", -label=>"Preview Answers"); |
|
|
| 1182 | # if ($can{checkAnswers}) { |
|
|
| 1183 | # print CGI::submit(-name=>"checkAnswers", -label=>"Check Answers"); |
|
|
| 1184 | # } |
|
|
| 1185 | # if ($can{getSubmitButton}) { |
|
|
| 1186 | # if ($user ne $effectiveUser) { |
|
|
| 1187 | # # if acting as a student, make it clear that answer submissions will |
|
|
| 1188 | # # apply to the student's records, not the professor's. |
|
|
| 1189 | # print CGI::submit(-name=>"submitAnswers", -label=>"Submit Answers for $effectiveUser"); |
|
|
| 1190 | # } else { |
|
|
| 1191 | # #print CGI::submit(-name=>"submitAnswers", -label=>"Submit Answers", -onclick=>"alert('submit button clicked')"); |
|
|
| 1192 | # print CGI::submit(-name=>"submitAnswers", -label=>"Submit Answers", -onclick=>""); |
|
|
| 1193 | # # FIXME for unknown reasons the -onclick label seems to have to be there in order to allow the forms onsubmit to trigger |
|
|
| 1194 | # # WFT??? |
|
|
| 1195 | # } |
|
|
| 1196 | # } |
|
|
| 1197 | # |
|
|
| 1198 | # print CGI::end_p(); |
|
|
| 1199 | # |
|
|
| 1200 | # print CGI::start_div({class=>"scoreSummary"}); |
|
|
| 1201 | # |
|
|
| 1202 | # # score summary |
|
|
| 1203 | # my $attempts = $problem->num_correct + $problem->num_incorrect; |
|
|
| 1204 | # my $attemptsNoun = $attempts != 1 ? "times" : "time"; |
|
|
| 1205 | # my $problem_status = $problem->status || 0; |
|
|
| 1206 | # my $lastScore = sprintf("%.0f%%", $problem_status * 100); # Round to whole number |
|
|
| 1207 | # my ($attemptsLeft, $attemptsLeftNoun); |
|
|
| 1208 | # if ($problem->max_attempts == -1) { |
|
|
| 1209 | # # unlimited attempts |
|
|
| 1210 | # $attemptsLeft = "unlimited"; |
|
|
| 1211 | # $attemptsLeftNoun = "attempts"; |
|
|
| 1212 | # } else { |
|
|
| 1213 | # $attemptsLeft = $problem->max_attempts - $attempts; |
|
|
| 1214 | # $attemptsLeftNoun = $attemptsLeft == 1 ? "attempt" : "attempts"; |
|
|
| 1215 | # } |
|
|
| 1216 | # |
|
|
| 1217 | # my $setClosed = 0; |
|
|
| 1218 | # my $setClosedMessage; |
|
|
| 1219 | # if (before($set->open_date) or after($set->due_date)) { |
|
|
| 1220 | # $setClosed = 1; |
|
|
| 1221 | # if (before($set->open_date)) { |
|
|
| 1222 | # $setClosedMessage = "This homework set is not yet open."; |
|
|
| 1223 | # } elsif (after($set->due_date)) { |
|
|
| 1224 | # $setClosedMessage = "This homework set is closed."; |
|
|
| 1225 | # } |
|
|
| 1226 | # } |
|
|
| 1227 | # #if (before($set->open_date) or after($set->due_date)) { |
|
|
| 1228 | # # $setClosed = 1; |
|
|
| 1229 | # # $setClosedMessage = "This homework set is closed."; |
|
|
| 1230 | # # if ($authz->hasPermissions($user, "view_answers")) { |
|
|
| 1231 | # # $setClosedMessage .= " However, since you are a privileged user, additional attempts will be recorded."; |
|
|
| 1232 | # # } else { |
|
|
| 1233 | # # $setClosedMessage .= " Additional attempts will not be recorded."; |
|
|
| 1234 | # # } |
|
|
| 1235 | # #} |
|
|
| 1236 | # unless (defined( $pg->{state}->{state_summary_msg}) and $pg->{state}->{state_summary_msg}=~/\S/) { |
|
|
| 1237 | # my $notCountedMessage = ($problem->value) ? "" : "(This problem will not count towards your grade.)"; |
|
|
| 1238 | # print CGI::p(join("", |
|
|
| 1239 | # $submitAnswers ? $scoreRecordedMessage . CGI::br() : "", |
|
|
| 1240 | # "You have attempted this problem $attempts $attemptsNoun.", CGI::br(), |
|
|
| 1241 | # $submitAnswers ?"You received a score of ".sprintf("%.0f%%", $pg->{result}->{score} * 100)." for this attempt.".CGI::br():'', |
|
|
| 1242 | # $problem->attempted |
|
|
| 1243 | # ? "Your overall recorded score is $lastScore. $notCountedMessage" . CGI::br() |
|
|
| 1244 | # : "", |
|
|
| 1245 | # $setClosed ? $setClosedMessage : "You have $attemptsLeft $attemptsLeftNoun remaining." |
|
|
| 1246 | # )); |
|
|
| 1247 | # }else { |
|
|
| 1248 | # print CGI::p($pg->{state}->{state_summary_msg}); |
|
|
| 1249 | # } |
|
|
| 1250 | # |
|
|
| 1251 | # print CGI::end_div(); |
|
|
| 1252 | # print CGI::start_div(); |
|
|
| 1253 | # |
|
|
| 1254 | # my $pgdebug = join(CGI::br(), @{$pg->{pgcore}->{flags}->{DEBUG_messages}} ); |
|
|
| 1255 | # my $pgwarning = join(CGI::br(), @{$pg->{pgcore}->{flags}->{WARNING_messages}} ); |
|
|
| 1256 | # my $pginternalerrors = join(CGI::br(), @{$pg->{pgcore}->get_internal_debug_messages} ); |
|
|
| 1257 | # my $pgerrordiv = $pgdebug||$pgwarning||$pginternalerrors; # is 1 if any of these are non-empty |
|
|
| 1258 | # |
|
|
| 1259 | # print CGI::p({style=>"color:red;"}, "Checking additional error messages") if $pgerrordiv ; |
|
|
| 1260 | # print CGI::p("pg debug<br/> $pgdebug" ) if $pgdebug ; |
|
|
| 1261 | # print CGI::p("pg warning<br/>$pgwarning" ) if $pgwarning ; |
|
|
| 1262 | # print CGI::p("pg internal errors<br/> $pginternalerrors") if $pginternalerrors; |
|
|
| 1263 | # print CGI::end_div() if $pgerrordiv ; |
|
|
| 1264 | # |
|
|
| 1265 | # # save state for viewOptions |
|
|
| 1266 | # print CGI::hidden( |
|
|
| 1267 | # -name => "showOldAnswers", |
|
|
| 1268 | # -value => $will{showOldAnswers} |
|
|
| 1269 | # ), |
|
|
| 1270 | # |
|
|
| 1271 | # CGI::hidden( |
|
|
| 1272 | # -name => "displayMode", |
|
|
| 1273 | # -value => $self->{displayMode} |
|
|
| 1274 | # ); |
|
|
| 1275 | # print( CGI::hidden( |
|
|
| 1276 | # -name => 'editMode', |
|
|
| 1277 | # -value => $self->{editMode}, |
|
|
| 1278 | # ) |
|
|
| 1279 | # ) if defined($self->{editMode}) and $self->{editMode} eq 'temporaryFile'; |
|
|
| 1280 | # |
|
|
| 1281 | # # this is a security risk -- students can use this to find the source code for the problem |
|
|
| 1282 | # |
|
|
| 1283 | # my $permissionLevel = $db->getPermissionLevel($user)->permission; |
|
|
| 1284 | # my $professorPermissionLevel = $ce->{userRoles}->{professor}; |
|
|
| 1285 | # print( CGI::hidden( |
|
|
| 1286 | # -name => 'sourceFilePath', |
|
|
| 1287 | # -value => $self->{problem}->{source_file} |
|
|
| 1288 | # )) if defined($self->{problem}->{source_file}) and $permissionLevel>= $professorPermissionLevel; # only allow this for professors |
|
|
| 1289 | # |
|
|
| 1290 | # print( CGI::hidden( |
|
|
| 1291 | # -name => 'problemSeed', |
|
|
| 1292 | # -value => $r->param("problemSeed") |
|
|
| 1293 | # )) if defined($r->param("problemSeed")) and $permissionLevel>= $professorPermissionLevel; # only allow this for professors |
|
|
| 1294 | # |
|
|
| 1295 | # |
|
|
| 1296 | # # end of main form |
|
|
| 1297 | # print CGI::endform(); |
|
|
| 1298 | # |
|
|
| 1299 | # print CGI::start_div({class=>"problemFooter"}); |
|
|
| 1300 | # |
|
|
| 1301 | # |
|
|
| 1302 | # my $pastAnswersPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::ShowAnswers", |
|
|
| 1303 | # courseID => $courseName); |
|
|
| 1304 | # my $showPastAnswersURL = $self->systemLink($pastAnswersPage, authen => 0); # no authen info for form action |
|
|
| 1305 | # |
|
|
| 1306 | # # print answer inspection button |
|
|
| 1307 | # if ($authz->hasPermissions($user, "view_answers")) { |
|
|
| 1308 | # print "\n", |
|
|
| 1309 | # CGI::start_form(-method=>"POST",-action=>$showPastAnswersURL,-target=>"WW_Info"),"\n", |
|
|
| 1310 | # $self->hidden_authen_fields,"\n", |
|
|
| 1311 | # CGI::hidden(-name => 'courseID', -value=>$courseName), "\n", |
|
|
| 1312 | # CGI::hidden(-name => 'problemID', -value=>$problem->problem_id), "\n", |
|
|
| 1313 | # CGI::hidden(-name => 'setID', -value=>$problem->set_id), "\n", |
|
|
| 1314 | # CGI::hidden(-name => 'studentUser', -value=>$problem->user_id), "\n", |
|
|
| 1315 | # CGI::p( {-align=>"left"}, |
|
|
| 1316 | # CGI::submit(-name => 'action', -value=>'Show Past Answers') |
|
|
| 1317 | # ), "\n", |
|
|
| 1318 | # CGI::endform(); |
|
|
| 1319 | # } |
|
|
| 1320 | # |
|
|
| 1321 | # |
|
|
| 1322 | # print $self->feedbackMacro( |
|
|
| 1323 | # module => __PACKAGE__, |
|
|
| 1324 | # set => $self->{set}->set_id, |
|
|
| 1325 | # problem => $problem->problem_id, |
|
|
| 1326 | # displayMode => $self->{displayMode}, |
|
|
| 1327 | # showOldAnswers => $will{showOldAnswers}, |
|
|
| 1328 | # showCorrectAnswers => $will{showCorrectAnswers}, |
|
|
| 1329 | # showHints => $will{showHints}, |
|
|
| 1330 | # showSolutions => $will{showSolutions}, |
|
|
| 1331 | # pg_object => $pg, |
|
|
| 1332 | # ); |
|
|
| 1333 | # |
|
|
| 1334 | # print CGI::end_div(); |
|
|
| 1335 | # |
|
|
| 1336 | # # debugging stuff |
|
|
| 1337 | # if (0) { |
|
|
| 1338 | # print |
|
|
| 1339 | # CGI::hr(), |
|
|
| 1340 | # CGI::h2("debugging information"), |
|
|
| 1341 | # CGI::h3("form fields"), |
|
|
| 1342 | # ref2string($self->{formFields}), |
|
|
| 1343 | # CGI::h3("user object"), |
|
|
| 1344 | # ref2string($self->{user}), |
|
|
| 1345 | # CGI::h3("set object"), |
|
|
| 1346 | # ref2string($set), |
|
|
| 1347 | # CGI::h3("problem object"), |
|
|
| 1348 | # ref2string($problem), |
|
|
| 1349 | # CGI::h3("PG object"), |
|
|
| 1350 | # ref2string($pg, {'WeBWorK::PG::Translator' => 1}); |
|
|
| 1351 | # } |
|
|
| 1352 | # debug("leaving body of Problem.pm"); |
|
|
| 1353 | # return ""; |
|
|
| 1354 | # } |
|
|
| 1355 | |
|
|
| 1356 | # now altered to outsource most output operations to the template, main functions now are simply error checking and answer processing - ghe3 |
|
|
| 1357 | sub body { |
882 | sub body { |
| 1358 | my $self = shift; |
883 | my $self = shift; |
|
|
884 | my $r = $self->r; |
|
|
885 | my $ce = $r->ce; |
|
|
886 | my $db = $r->db; |
|
|
887 | my $authz = $r->authz; |
|
|
888 | my $urlpath = $r->urlpath; |
|
|
889 | my $user = $r->param('user'); |
|
|
890 | my $effectiveUser = $r->param('effectiveUser'); |
|
|
891 | |
|
|
892 | if ( $self->{invalidSet} ) { |
|
|
893 | return CGI::div({class=>"ResultsWithError"}, |
|
|
894 | CGI::p($r->maketext("The selected problem set ([_1]) is not a valid set for [_2]:", $urlpath->arg("setID"), $effectiveUser)), CGI::p($self->{invalidSet})); |
|
|
895 | } |
|
|
896 | |
|
|
897 | if ($self->{invalidProblem}) { |
|
|
898 | return CGI::div({class=>"ResultsWithError"}, |
|
|
899 | CGI::p($r->maketext("The selected problem([_1]) is not a valid problem for set [_2].", $urlpath->arg("problemID"), $self->{set}->set_id ))); |
|
|
900 | } |
|
|
901 | |
|
|
902 | # unpack some useful variables |
| 1359 | my $set = $self->{set}; |
903 | my $set = $self->{set}; |
| 1360 | my $problem = $self->{problem}; |
904 | my $problem = $self->{problem}; |
|
|
905 | my $editMode = $self->{editMode}; |
|
|
906 | my $submitAnswers = $self->{submitAnswers}; |
|
|
907 | my $checkAnswers = $self->{checkAnswers}; |
|
|
908 | my $previewAnswers = $self->{previewAnswers}; |
|
|
909 | my %want = %{ $self->{want} }; |
|
|
910 | my %can = %{ $self->{can} }; |
|
|
911 | my %must = %{ $self->{must} }; |
|
|
912 | my %will = %{ $self->{will} }; |
| 1361 | my $pg = $self->{pg}; |
913 | my $pg = $self->{pg}; |
| 1362 | |
914 | |
| 1363 | my $valid = WeBWorK::ContentGenerator::ProblemUtil::ProblemUtil::check_invalid($self); |
915 | my $courseName = $urlpath->arg("courseID"); |
| 1364 | unless($valid eq "valid"){ |
916 | |
| 1365 | return $valid; |
917 | # FIXME: move editor link to top, next to problem number. |
|
|
918 | # format as "[edit]" like we're doing with course info file, etc. |
|
|
919 | # add edit link for set as well. |
|
|
920 | my $editorLink = ""; |
|
|
921 | # if we are here without a real homework set, carry that through |
|
|
922 | my $forced_field = []; |
|
|
923 | $forced_field = ['sourceFilePath' => $r->param("sourceFilePath")] if |
|
|
924 | ($set->set_id eq 'Undefined_Set'); |
|
|
925 | if ($authz->hasPermissions($user, "modify_problem_sets")) { |
|
|
926 | my $editorPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", $r, |
|
|
927 | courseID => $courseName, setID => $set->set_id, problemID => $problem->problem_id); |
|
|
928 | my $editorURL = $self->systemLink($editorPage, params=>$forced_field); |
|
|
929 | $editorLink = CGI::p(CGI::a({href=>$editorURL,target =>'WW_Editor'}, "Edit this problem")); |
|
|
930 | } |
|
|
931 | |
|
|
932 | ##### translation errors? ##### |
|
|
933 | |
|
|
934 | if ($pg->{flags}->{error_flag}) { |
|
|
935 | if ($authz->hasPermissions($user, "view_problem_debugging_info")) { |
|
|
936 | print $self->errorOutput($pg->{errors}, $pg->{body_text}); |
|
|
937 | } else { |
|
|
938 | print $self->errorOutput($pg->{errors}, $r->maketext("You do not have permission to view the details of this error.")); |
| 1366 | } |
939 | } |
| 1367 | |
940 | print $editorLink; |
| 1368 | #################################################### |
|
|
| 1369 | # Move to header in new templates |
|
|
| 1370 | #print $self->output_tabber_JS(); |
|
|
| 1371 | print $self->output_coloring_JS(); |
|
|
| 1372 | |
|
|
| 1373 | ##### javaScripts ############# |
|
|
| 1374 | # WeBWorK::ContentGenerator::ProblemUtil::ProblemUtil::output_JS($self); |
|
|
| 1375 | print $self->output_JS; |
|
|
| 1376 | |
|
|
| 1377 | #################################################### |
|
|
| 1378 | |
|
|
| 1379 | # my $editorLink = WeBWorK::ContentGenerator::ProblemUtil::ProblemUtil::process_editorLink($self); |
|
|
| 1380 | # if($editorLink eq "permission_error"){ |
|
|
| 1381 | # return ""; |
941 | return ""; |
| 1382 | # } |
942 | } |
| 1383 | |
|
|
| 1384 | |
943 | |
| 1385 | ##### answer processing ##### |
944 | ##### answer processing ##### |
| 1386 | debug("begin answer processing"); |
945 | debug("begin answer processing"); |
| 1387 | # if answers were submitted: |
946 | # if answers were submitted: |
| 1388 | my $scoreRecordedMessage = WeBWorK::ContentGenerator::ProblemUtil::ProblemUtil::process_and_log_answer($self); |
947 | my $scoreRecordedMessage; |
|
|
948 | my $pureProblem; |
|
|
949 | if ($submitAnswers) { |
|
|
950 | # get a "pure" (unmerged) UserProblem to modify |
|
|
951 | # this will be undefined if the problem has not been assigned to this user |
|
|
952 | $pureProblem = $db->getUserProblem($problem->user_id, $problem->set_id, $problem->problem_id); # checked |
|
|
953 | if (defined $pureProblem) { |
|
|
954 | # store answers in DB for sticky answers |
|
|
955 | my %answersToStore; |
|
|
956 | my %answerHash = %{ $pg->{answers} }; |
|
|
957 | $answersToStore{$_} = $self->{formFields}->{$_} #$answerHash{$_}->{original_student_ans} -- this may have been modified for fields with multiple values. Don't use it!! |
|
|
958 | foreach (keys %answerHash); |
|
|
959 | |
|
|
960 | # There may be some more answers to store -- one which are auxiliary entries to a primary answer. Evaluating |
|
|
961 | # matrices works in this way, only the first answer triggers an answer evaluator, the rest are just inputs |
|
|
962 | # however we need to store them. Fortunately they are still in the input form. |
|
|
963 | my @extra_answer_names = @{ $pg->{flags}->{KEPT_EXTRA_ANSWERS}}; |
|
|
964 | $answersToStore{$_} = $self->{formFields}->{$_} foreach (@extra_answer_names); |
|
|
965 | |
|
|
966 | # Now let's encode these answers to store them -- append the extra answers to the end of answer entry order |
|
|
967 | my @answer_order = (@{$pg->{flags}->{ANSWER_ENTRY_ORDER}}, @extra_answer_names); |
|
|
968 | my $answerString = encodeAnswers(%answersToStore, |
|
|
969 | @answer_order); |
|
|
970 | |
|
|
971 | # store last answer to database |
|
|
972 | $problem->last_answer($answerString); |
|
|
973 | $pureProblem->last_answer($answerString); |
|
|
974 | $db->putUserProblem($pureProblem); |
|
|
975 | |
|
|
976 | # store state in DB if it makes sense |
|
|
977 | if ($will{recordAnswers}) { |
|
|
978 | $problem->status($pg->{state}->{recorded_score}); |
|
|
979 | $problem->sub_status($pg->{state}->{sub_recorded_score}); |
|
|
980 | $problem->attempted(1); |
|
|
981 | $problem->num_correct($pg->{state}->{num_of_correct_ans}); |
|
|
982 | $problem->num_incorrect($pg->{state}->{num_of_incorrect_ans}); |
|
|
983 | $pureProblem->status($pg->{state}->{recorded_score}); |
|
|
984 | $pureProblem->sub_status($pg->{state}->{sub_recorded_score}); |
|
|
985 | $pureProblem->attempted(1); |
|
|
986 | $pureProblem->num_correct($pg->{state}->{num_of_correct_ans}); |
|
|
987 | $pureProblem->num_incorrect($pg->{state}->{num_of_incorrect_ans}); |
|
|
988 | if ($db->putUserProblem($pureProblem)) { |
|
|
989 | $scoreRecordedMessage = $r->maketext("Your score was recorded."); |
|
|
990 | } else { |
|
|
991 | $scoreRecordedMessage = $r->maketext("Your score was not recorded because there was a failure in storing the problem record to the database."); |
|
|
992 | } |
|
|
993 | # write to the transaction log, just to make sure |
|
|
994 | writeLog($self->{ce}, "transaction", |
|
|
995 | $problem->problem_id."\t". |
|
|
996 | $problem->set_id."\t". |
|
|
997 | $problem->user_id."\t". |
|
|
998 | $problem->source_file."\t". |
|
|
999 | $problem->value."\t". |
|
|
1000 | $problem->max_attempts."\t". |
|
|
1001 | $problem->problem_seed."\t". |
|
|
1002 | $pureProblem->status."\t". |
|
|
1003 | $pureProblem->attempted."\t". |
|
|
1004 | $pureProblem->last_answer."\t". |
|
|
1005 | $pureProblem->num_correct."\t". |
|
|
1006 | $pureProblem->num_incorrect |
|
|
1007 | ); |
|
|
1008 | } else { |
|
|
1009 | if (before($set->open_date) or after($set->due_date)) { |
|
|
1010 | $scoreRecordedMessage = $r->maketext("Your score was not recorded because this homework set is closed."); |
|
|
1011 | } else { |
|
|
1012 | $scoreRecordedMessage = $r->maketext("Your score was not recorded."); |
|
|
1013 | } |
|
|
1014 | } |
|
|
1015 | } else { |
|
|
1016 | $scoreRecordedMessage = $r->maketext("Your score was not recorded because this problem has not been assigned to you."); |
|
|
1017 | } |
|
|
1018 | } |
|
|
1019 | |
|
|
1020 | # logging student answers |
|
|
1021 | |
|
|
1022 | my $answer_log = $self->{ce}->{courseFiles}->{logs}->{'answer_log'}; |
|
|
1023 | if ( defined($answer_log ) and defined($pureProblem)) { |
|
|
1024 | if ($submitAnswers && !$authz->hasPermissions($effectiveUser, "dont_log_past_answers")) { |
|
|
1025 | my $answerString = ""; my $scores = ""; |
|
|
1026 | my %answerHash = %{ $pg->{answers} }; |
|
|
1027 | # FIXME this is the line 552 error. make sure original student ans is defined. |
|
|
1028 | # The fact that it is not defined is probably due to an error in some answer evaluator. |
|
|
1029 | # But I think it is useful to suppress this error message in the log. |
|
|
1030 | foreach (sortByName(undef, keys %answerHash)) { |
|
|
1031 | my $orig_ans = $answerHash{$_}->{original_student_ans}; |
|
|
1032 | my $student_ans = defined $orig_ans ? $orig_ans : ''; |
|
|
1033 | $answerString .= $student_ans."\t"; |
|
|
1034 | $scores .= $answerHash{$_}->{score} >= 1 ? "1" : "0"; |
|
|
1035 | } |
|
|
1036 | $answerString = '' unless defined($answerString); # insure string is defined. |
|
|
1037 | writeCourseLog($self->{ce}, "answer_log", |
|
|
1038 | join("", |
|
|
1039 | '|', $problem->user_id, |
|
|
1040 | '|', $problem->set_id, |
|
|
1041 | '|', $problem->problem_id, |
|
|
1042 | '|', $scores, "\t", |
|
|
1043 | time(),"\t", |
|
|
1044 | $answerString, |
|
|
1045 | ), |
|
|
1046 | ); |
|
|
1047 | |
|
|
1048 | } |
|
|
1049 | } |
|
|
1050 | |
| 1389 | debug("end answer processing"); |
1051 | debug("end answer processing"); |
|
|
1052 | ##### javaScripts ############# |
|
|
1053 | my $site_url = $ce->{webworkURLs}->{htdocs}; |
|
|
1054 | print CGI::start_script({type=>"text/javascript", src=>"$site_url/js/wz_tooltip.js"}), CGI::end_script(); |
|
|
1055 | |
|
|
1056 | ##### output ##### |
|
|
1057 | # custom message for editor |
|
|
1058 | if ($authz->hasPermissions($user, "modify_problem_sets") and defined $editMode) { |
|
|
1059 | if ($editMode eq "temporaryFile") { |
|
|
1060 | print CGI::p(CGI::div({class=>'temporaryFile'}, $r->maketext("Viewing temporary file: "), $problem->source_file)); |
|
|
1061 | } elsif ($editMode eq "savedFile") { |
|
|
1062 | # taken care of in the initialization phase |
|
|
1063 | } |
|
|
1064 | } |
|
|
1065 | print CGI::start_div({class=>"problemHeader"}); |
|
|
1066 | |
|
|
1067 | |
|
|
1068 | |
|
|
1069 | # attempt summary |
|
|
1070 | #FIXME -- the following is a kludge: if showPartialCorrectAnswers is negative don't show anything. |
|
|
1071 | # until after the due date |
|
|
1072 | # do I need to check $will{showCorrectAnswers} to make preflight work?? |
|
|
1073 | if (($pg->{flags}->{showPartialCorrectAnswers} >= 0 and $submitAnswers) ) { |
|
|
1074 | # print this if user submitted answers OR requested correct answers |
|
|
1075 | |
|
|
1076 | print $self->attemptResults($pg, 1, |
|
|
1077 | $will{showCorrectAnswers}, |
|
|
1078 | $pg->{flags}->{showPartialCorrectAnswers}, 1, 1); |
|
|
1079 | } elsif ($checkAnswers) { |
|
|
1080 | # print this if user previewed answers |
|
|
1081 | print CGI::div({class=>'ResultsWithError'},$r->maketext("ANSWERS ONLY CHECKED -- ANSWERS NOT RECORDED")), CGI::br(); |
|
|
1082 | print $self->attemptResults($pg, 1, $will{showCorrectAnswers}, 1, 1, 1); |
|
|
1083 | # show attempt answers |
|
|
1084 | # show correct answers if asked |
|
|
1085 | # show attempt results (correctness) |
|
|
1086 | # show attempt previews |
|
|
1087 | } elsif ($previewAnswers) { |
|
|
1088 | # print this if user previewed answers |
|
|
1089 | print CGI::div({class=>'ResultsWithError'},$r->maketext("PREVIEW ONLY -- ANSWERS NOT RECORDED")),CGI::br(),$self->attemptResults($pg, 1, 0, 0, 0, 1); |
|
|
1090 | # show attempt answers |
|
|
1091 | # don't show correct answers |
|
|
1092 | # don't show attempt results (correctness) |
|
|
1093 | # show attempt previews |
|
|
1094 | } |
|
|
1095 | |
|
|
1096 | print CGI::end_div(); |
|
|
1097 | |
| 1390 | |
1098 | |
| 1391 | ########################### |
1099 | ########################### |
| 1392 | # print style sheet for correct and incorrect answers |
1100 | # print style sheet for correct and incorrect answers |
| 1393 | ########################### |
1101 | ########################### |
| 1394 | |
1102 | # always show colors for checkAnswers |
| 1395 | # WeBWorK::ContentGenerator::ProblemUtil::ProblemUtil::output_CSS($self); |
1103 | # show colors for submit answer if |
| 1396 | print $self->output_CSS; |
1104 | if (($self->{checkAnswers}) or ($self->{submitAnswers} and $pg->{flags}->{showPartialCorrectAnswers}) ) { |
| 1397 | |
1105 | print CGI::start_style({type=>"text/css"}); |
| 1398 | |
1106 | #FIXME -- this hack is no longer needed? |
|
|
1107 | # my $string =""; |
|
|
1108 | # foreach my $ans_name (@{ $self->{correct_ids} }) { |
|
|
1109 | # $string .= '#'. ( $ans_name ). $ce->{pg}{options}{correct_answer}."\n"; |
|
|
1110 | # } |
|
|
1111 | # print $string; |
|
|
1112 | # $string =""; |
|
|
1113 | # foreach my $ans_name (@{ $self->{incorrect_ids} }) { |
|
|
1114 | # $string .= '#'. ($ ans_name). $ce->{pg}{options}{incorrect_answer}."\n"; |
|
|
1115 | # } |
|
|
1116 | # print $string; |
|
|
1117 | # the above method keeps one bad array ID from ruining all of the assignments. |
|
|
1118 | print '#'.join(', #', @{ $self->{correct_ids} }), $ce->{pg}{options}{correct_answer},"\n" if ref( $self->{correct_ids} )=~/ARRAY/; #correct green |
|
|
1119 | print '#'.join(', #', @{ $self->{incorrect_ids} }), $ce->{pg}{options}{incorrect_answer},"\n" if ref( $self->{incorrect_ids})=~/ARRAY/; #incorrect reddish |
|
|
1120 | print CGI::end_style(); |
|
|
1121 | } |
| 1399 | ########################### |
1122 | ########################### |
| 1400 | # post_header material |
1123 | # post_header material |
| 1401 | |
|
|
| 1402 | #FIXME -- this should be pulled by the template. |
|
|
| 1403 | ########################### |
1124 | ########################### |
| 1404 | print CGI::p($pg->{post_header_text}); |
1125 | print CGI::p($pg->{post_header_text}); |
| 1405 | |
|
|
| 1406 | |
|
|
| 1407 | ##### output ##### |
|
|
| 1408 | # WeBWorK::ContentGenerator::ProblemUtil::ProblemUtil::output_summary($self); |
|
|
| 1409 | print $self->output_custom_edit_message(); |
|
|
| 1410 | print $self->output_summary(); |
|
|
| 1411 | print $self->output_form_start(); |
|
|
| 1412 | print $self->output_problem_body(); |
|
|
| 1413 | print $self->output_message(); |
|
|
| 1414 | print $self->output_editorLink(); |
|
|
| 1415 | print $self->output_checkboxes(); |
|
|
| 1416 | print $self->output_submit_buttons(); |
|
|
| 1417 | print $self->output_score_summary(); |
|
|
| 1418 | print $self->output_misc(); |
|
|
| 1419 | print "\n</form>\n"; |
|
|
| 1420 | |
|
|
| 1421 | |
|
|
| 1422 | $self->output_email_instructor(); |
|
|
| 1423 | $self->output_past_answer_button(); |
|
|
| 1424 | |
|
|
| 1425 | |
|
|
| 1426 | # |
|
|
| 1427 | # |
|
|
| 1428 | # ########################### |
1126 | ########################### |
| 1429 | # # main form |
1127 | # main form |
| 1430 | # ########################### |
1128 | ########################### |
| 1431 | # |
1129 | print "\n"; |
| 1432 | # # WeBWorK::ContentGenerator::ProblemUtil::ProblemUtil::output_main_form($self,$editorLink); |
1130 | |
| 1433 | # |
1131 | print CGI::start_form(-method=>"POST", -action=> $r->uri,-name=>"problemMainForm", onsubmit=>"submitAction()"); |
| 1434 | # # WeBWorK::ContentGenerator::ProblemUtil::ProblemUtil::output_footer($self); |
1132 | print $self->hidden_authen_fields; |
|
|
1133 | print "\n"; |
|
|
1134 | print CGI::start_div({class=>"problem"}); |
|
|
1135 | print CGI::p($pg->{body_text}); |
|
|
1136 | print CGI::p(CGI::b("Note: "). CGI::i($pg->{result}->{msg})) if $pg->{result}->{msg}; |
|
|
1137 | print $editorLink; # this is empty unless it is appropriate to have an editor link. |
|
|
1138 | print CGI::end_div(); |
|
|
1139 | |
|
|
1140 | print CGI::start_p(); |
|
|
1141 | |
|
|
1142 | if ($can{showCorrectAnswers}) { |
|
|
1143 | print CGI::checkbox( |
|
|
1144 | -name => "showCorrectAnswers", |
|
|
1145 | -checked => $will{showCorrectAnswers}, |
|
|
1146 | -label => $r->maketext("Show correct answers"), |
|
|
1147 | -value => 1, |
|
|
1148 | ); |
|
|
1149 | } |
|
|
1150 | if ($can{showHints}) { |
|
|
1151 | print CGI::div({style=>"color:red"}, |
|
|
1152 | CGI::checkbox( |
|
|
1153 | -name => "showHints", |
|
|
1154 | -checked => $will{showHints}, |
|
|
1155 | -label => $r->maketext("Show Hints"), |
|
|
1156 | -value =>1, |
|
|
1157 | ) |
|
|
1158 | ); |
|
|
1159 | } |
|
|
1160 | if ($can{showSolutions}) { |
|
|
1161 | print CGI::checkbox( |
|
|
1162 | -name => "showSolutions", |
|
|
1163 | -checked => $will{showSolutions}, |
|
|
1164 | -label => $r->maketext("Show Solutions"), |
|
|
1165 | -value => 1, |
|
|
1166 | ); |
|
|
1167 | } |
|
|
1168 | |
|
|
1169 | if ($can{showCorrectAnswers} or $can{showHints} or $can{showSolutions}) { |
|
|
1170 | print CGI::br(); |
|
|
1171 | } |
|
|
1172 | |
|
|
1173 | print CGI::submit(-name=>"previewAnswers", -label=>$r->maketext("Preview Answers")); |
|
|
1174 | if ($can{checkAnswers}) { |
|
|
1175 | print CGI::submit(-name=>"checkAnswers", -label=>$r->maketext("Check Answers")); |
|
|
1176 | } |
|
|
1177 | if ($can{getSubmitButton}) { |
|
|
1178 | if ($user ne $effectiveUser) { |
|
|
1179 | # if acting as a student, make it clear that answer submissions will |
|
|
1180 | # apply to the student's records, not the professor's. |
|
|
1181 | print CGI::submit(-name=>"submitAnswers", -label=>$r->maketext("Submit answers for [_1]",$effectiveUser)); |
|
|
1182 | } else { |
|
|
1183 | #print CGI::submit(-name=>"submitAnswers", -label=>"Submit Answers", -onclick=>"alert('submit button clicked')"); |
|
|
1184 | print CGI::submit(-name=>"submitAnswers", -label=>$r->maketext("Submit answers"), -onclick=>""); |
|
|
1185 | # FIXME for unknown reasons the -onclick label seems to have to be there in order to allow the forms onsubmit to trigger |
|
|
1186 | # WTF??? |
|
|
1187 | } |
|
|
1188 | } |
|
|
1189 | |
|
|
1190 | print CGI::end_p(); |
|
|
1191 | |
|
|
1192 | print CGI::start_div({class=>"scoreSummary"}); |
|
|
1193 | |
|
|
1194 | # score summary |
|
|
1195 | my $attempts = $problem->num_correct + $problem->num_incorrect; |
|
|
1196 | #my $attemptsNoun = $attempts != 1 ? $r->maketext("times") : $r->maketext("time"); |
|
|
1197 | my $problem_status = $problem->status || 0; |
|
|
1198 | my $lastScore = sprintf("%.0f%%", $problem_status * 100); # Round to whole number |
|
|
1199 | #my ($attemptsLeft, $attemptsLeftNoun); |
|
|
1200 | my $attemptsLeft = $problem->max_attempts - $attempts; |
|
|
1201 | # if ($problem->max_attempts == -1) { |
|
|
1202 | # # unlimited attempts |
|
|
1203 | # $attemptsLeft = $r->maketext("unlimited"); |
|
|
1204 | # $attemptsLeftNoun = $r->maketext("attempts"); |
|
|
1205 | # } else { |
|
|
1206 | # $attemptsLeft = $problem->max_attempts - $attempts; |
|
|
1207 | # $attemptsLeftNoun = $attemptsLeft == 1 ? $r->maketext("attempt") : $r->maketext("attempts"); |
|
|
1208 | # } |
|
|
1209 | |
|
|
1210 | my $setClosed = 0; |
|
|
1211 | my $setClosedMessage; |
|
|
1212 | if (before($set->open_date) or after($set->due_date)) { |
|
|
1213 | $setClosed = 1; |
|
|
1214 | if (before($set->open_date)) { |
|
|
1215 | $setClosedMessage = $r->maketext("This homework set is not yet open."); |
|
|
1216 | } elsif (after($set->due_date)) { |
|
|
1217 | $setClosedMessage = $r->maketext("This homework set is closed."); |
|
|
1218 | } |
|
|
1219 | } |
|
|
1220 | #if (before($set->open_date) or after($set->due_date)) { |
|
|
1221 | # $setClosed = 1; |
|
|
1222 | # $setClosedMessage = "This homework set is closed."; |
|
|
1223 | # if ($authz->hasPermissions($user, "view_answers")) { |
|
|
1224 | # $setClosedMessage .= " However, since you are a privileged user, additional attempts will be recorded."; |
|
|
1225 | # } else { |
|
|
1226 | # $setClosedMessage .= " Additional attempts will not be recorded."; |
|
|
1227 | # } |
|
|
1228 | #} |
|
|
1229 | unless (defined( $pg->{state}->{state_summary_msg}) and $pg->{state}->{state_summary_msg}=~/\S/) { |
|
|
1230 | my $notCountedMessage = ($problem->value) ? "" : $r->maketext("(This problem will not count towards your grade.)"); |
|
|
1231 | print CGI::p(join("", |
|
|
1232 | $submitAnswers ? $scoreRecordedMessage . CGI::br() : "", |
|
|
1233 | $r->maketext("You have attempted this problem [quant,_1,time,times].",$attempts), CGI::br(), |
|
|
1234 | $submitAnswers ? $r->maketext("You received a score of [_1] for this attempt.",sprintf("%.0f%%", $pg->{result}->{score} * 100)) . CGI::br():'', |
|
|
1235 | $problem->attempted |
|
|
1236 | ? $r->maketext("Your overall recorded score is [_1]. [_2]",$lastScore,$notCountedMessage) . CGI::br() |
|
|
1237 | : "", |
|
|
1238 | # $setClosed ? $setClosedMessage : $r->maketext("You have [_1] [_2] remaining.",$attemptsLeft,$attemptsLeftNoun) |
|
|
1239 | $setClosed ? $setClosedMessage : $r->maketext("You have [negquant,_1,unlimited attempts,attempt,attempts] remaining.",$attemptsLeft) |
|
|
1240 | )); |
|
|
1241 | }else { |
|
|
1242 | print CGI::p($pg->{state}->{state_summary_msg}); |
|
|
1243 | } |
|
|
1244 | |
|
|
1245 | print CGI::end_div(); |
|
|
1246 | print CGI::start_div(); |
|
|
1247 | |
|
|
1248 | my $pgdebug = join(CGI::br(), @{$pg->{pgcore}->{flags}->{DEBUG_messages}} ); |
|
|
1249 | my $pgwarning = join(CGI::br(), @{$pg->{pgcore}->{flags}->{WARNING_messages}} ); |
|
|
1250 | my $pginternalerrors = join(CGI::br(), @{$pg->{pgcore}->get_internal_debug_messages} ); |
|
|
1251 | my $pgerrordiv = $pgdebug||$pgwarning||$pginternalerrors; # is 1 if any of these are non-empty |
|
|
1252 | |
|
|
1253 | print CGI::p({style=>"color:red;"}, "Checking additional error messages") if $pgerrordiv ; |
|
|
1254 | print CGI::p("pg debug<br/> $pgdebug" ) if $pgdebug ; |
|
|
1255 | print CGI::p("pg warning<br/>$pgwarning" ) if $pgwarning ; |
|
|
1256 | print CGI::p("pg internal errors<br/> $pginternalerrors") if $pginternalerrors; |
|
|
1257 | print CGI::end_div() if $pgerrordiv ; |
|
|
1258 | |
|
|
1259 | # save state for viewOptions |
|
|
1260 | print CGI::hidden( |
|
|
1261 | -name => "showOldAnswers", |
|
|
1262 | -value => $will{showOldAnswers} |
|
|
1263 | ), |
|
|
1264 | |
|
|
1265 | CGI::hidden( |
|
|
1266 | -name => "displayMode", |
|
|
1267 | -value => $self->{displayMode} |
|
|
1268 | ); |
|
|
1269 | print( CGI::hidden( |
|
|
1270 | -name => 'editMode', |
|
|
1271 | -value => $self->{editMode}, |
|
|
1272 | ) |
|
|
1273 | ) if defined($self->{editMode}) and $self->{editMode} eq 'temporaryFile'; |
|
|
1274 | |
|
|
1275 | # this is a security risk -- students can use this to find the source code for the problem |
|
|
1276 | |
|
|
1277 | my $permissionLevel = $db->getPermissionLevel($user)->permission; |
|
|
1278 | my $professorPermissionLevel = $ce->{userRoles}->{professor}; |
|
|
1279 | print( CGI::hidden( |
|
|
1280 | -name => 'sourceFilePath', |
|
|
1281 | -value => $self->{problem}->{source_file} |
|
|
1282 | )) if defined($self->{problem}->{source_file}) and $permissionLevel>= $professorPermissionLevel; # only allow this for professors |
|
|
1283 | |
|
|
1284 | print( CGI::hidden( |
|
|
1285 | -name => 'problemSeed', |
|
|
1286 | -value => $r->param("problemSeed") |
|
|
1287 | )) if defined($r->param("problemSeed")) and $permissionLevel>= $professorPermissionLevel; # only allow this for professors |
|
|
1288 | |
|
|
1289 | |
|
|
1290 | # end of main form |
|
|
1291 | print CGI::endform(); |
|
|
1292 | |
|
|
1293 | print CGI::start_div({class=>"problemFooter"}); |
|
|
1294 | |
|
|
1295 | |
|
|
1296 | my $pastAnswersPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::ShowAnswers", $r, |
|
|
1297 | courseID => $courseName); |
|
|
1298 | my $showPastAnswersURL = $self->systemLink($pastAnswersPage, authen => 0); # no authen info for form action |
|
|
1299 | |
|
|
1300 | # print answer inspection button |
|
|
1301 | if ($authz->hasPermissions($user, "view_answers")) { |
|
|
1302 | print "\n", |
|
|
1303 | CGI::start_form(-method=>"POST",-action=>$showPastAnswersURL,-target=>"WW_Info"),"\n", |
|
|
1304 | $self->hidden_authen_fields,"\n", |
|
|
1305 | CGI::hidden(-name => 'courseID', -value=>$courseName), "\n", |
|
|
1306 | CGI::hidden(-name => 'problemID', -value=>$problem->problem_id), "\n", |
|
|
1307 | CGI::hidden(-name => 'setID', -value=>$problem->set_id), "\n", |
|
|
1308 | CGI::hidden(-name => 'studentUser', -value=>$problem->user_id), "\n", |
|
|
1309 | CGI::p( {-align=>"left"}, |
|
|
1310 | CGI::submit(-name => 'action', -value=>$r->maketext("Show Past Answers")) |
|
|
1311 | ), "\n", |
|
|
1312 | CGI::endform(); |
|
|
1313 | } |
|
|
1314 | |
|
|
1315 | |
|
|
1316 | print $self->feedbackMacro( |
|
|
1317 | module => __PACKAGE__, |
|
|
1318 | set => $self->{set}->set_id, |
|
|
1319 | problem => $problem->problem_id, |
|
|
1320 | displayMode => $self->{displayMode}, |
|
|
1321 | showOldAnswers => $will{showOldAnswers}, |
|
|
1322 | showCorrectAnswers => $will{showCorrectAnswers}, |
|
|
1323 | showHints => $will{showHints}, |
|
|
1324 | showSolutions => $will{showSolutions}, |
|
|
1325 | pg_object => $pg, |
|
|
1326 | ); |
|
|
1327 | |
|
|
1328 | print CGI::end_div(); |
| 1435 | |
1329 | |
| 1436 | # debugging stuff |
1330 | # debugging stuff |
| 1437 | if (0) { |
1331 | if (0) { |
| 1438 | print |
1332 | print |
| 1439 | CGI::hr(), |
1333 | CGI::hr(), |
| … | |
… | |
| 1451 | } |
1345 | } |
| 1452 | debug("leaving body of Problem.pm"); |
1346 | debug("leaving body of Problem.pm"); |
| 1453 | return ""; |
1347 | return ""; |
| 1454 | } |
1348 | } |
| 1455 | |
1349 | |
| 1456 | # output_form_start subroutine |
|
|
| 1457 | |
|
|
| 1458 | # prints out the beginning of the main form, and the necessary hidden authentication fields |
|
|
| 1459 | |
|
|
| 1460 | sub output_form_start{ |
|
|
| 1461 | my $self = shift; |
|
|
| 1462 | my $r = $self->r; |
|
|
| 1463 | print CGI::start_form(-method=>"POST", -action=> $r->uri,-name=>"problemMainForm", onsubmit=>"submitAction()"); |
|
|
| 1464 | print $self->hidden_authen_fields; |
|
|
| 1465 | return ""; |
|
|
| 1466 | } |
|
|
| 1467 | |
|
|
| 1468 | # output_problem_body subroutine |
|
|
| 1469 | |
|
|
| 1470 | # prints out the body of the current problem |
|
|
| 1471 | |
|
|
| 1472 | sub output_problem_body{ |
|
|
| 1473 | my $self = shift; |
|
|
| 1474 | my $pg = $self->{pg}; |
|
|
| 1475 | |
|
|
| 1476 | print "\n"; |
|
|
| 1477 | print CGI::p($pg->{body_text}); |
|
|
| 1478 | return ""; |
|
|
| 1479 | } |
|
|
| 1480 | |
|
|
| 1481 | # output_message subroutine |
|
|
| 1482 | |
|
|
| 1483 | # prints out a message about the problem |
|
|
| 1484 | |
|
|
| 1485 | sub output_message{ |
|
|
| 1486 | my $self = shift; |
|
|
| 1487 | my $pg = $self->{pg}; |
|
|
| 1488 | |
|
|
| 1489 | print CGI::p(CGI::b("Note: "). CGI::i($pg->{result}->{msg})) if $pg->{result}->{msg}; |
|
|
| 1490 | return ""; |
|
|
| 1491 | } |
|
|
| 1492 | |
|
|
| 1493 | # output_editorLink subroutine |
|
|
| 1494 | |
|
|
| 1495 | # processes and prints out the correct link to the editor of the current problem |
|
|
| 1496 | |
|
|
| 1497 | sub output_editorLink{ |
|
|
| 1498 | |
|
|
| 1499 | my $self = shift; |
|
|
| 1500 | |
|
|
| 1501 | my $set = $self->{set}; |
|
|
| 1502 | my $problem = $self->{problem}; |
|
|
| 1503 | my $pg = $self->{pg}; |
|
|
| 1504 | |
|
|
| 1505 | my $r = $self->r; |
|
|
| 1506 | |
|
|
| 1507 | my $authz = $r->authz; |
|
|
| 1508 | my $urlpath = $r->urlpath; |
|
|
| 1509 | my $user = $r->param('user'); |
|
|
| 1510 | |
|
|
| 1511 | my $courseName = $urlpath->arg("courseID"); |
|
|
| 1512 | |
|
|
| 1513 | # FIXME: move editor link to top, next to problem number. |
|
|
| 1514 | # format as "[edit]" like we're doing with course info file, etc. |
|
|
| 1515 | # add edit link for set as well. |
|
|
| 1516 | my $editorLink = ""; |
|
|
| 1517 | # if we are here without a real homework set, carry that through |
|
|
| 1518 | my $forced_field = []; |
|
|
| 1519 | $forced_field = ['sourceFilePath' => $r->param("sourceFilePath")] if |
|
|
| 1520 | ($set->set_id eq 'Undefined_Set'); |
|
|
| 1521 | if ($authz->hasPermissions($user, "modify_problem_sets")) { |
|
|
| 1522 | my $editorPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", |
|
|
| 1523 | courseID => $courseName, setID => $set->set_id, problemID => $problem->problem_id); |
|
|
| 1524 | my $editorURL = $self->systemLink($editorPage, params=>$forced_field); |
|
|
| 1525 | $editorLink = CGI::p(CGI::a({href=>$editorURL,target =>'WW_Editor'}, "Edit this problem")); |
|
|
| 1526 | } |
|
|
| 1527 | |
|
|
| 1528 | ##### translation errors? ##### |
|
|
| 1529 | |
|
|
| 1530 | if ($pg->{flags}->{error_flag}) { |
|
|
| 1531 | if ($authz->hasPermissions($user, "view_problem_debugging_info")) { |
|
|
| 1532 | print $self->errorOutput($pg->{errors}, $pg->{body_text}); |
|
|
| 1533 | } else { |
|
|
| 1534 | print $self->errorOutput($pg->{errors}, "You do not have permission to view the details of this error."); |
|
|
| 1535 | } |
|
|
| 1536 | print ""; |
|
|
| 1537 | } |
|
|
| 1538 | else{ |
|
|
| 1539 | print $editorLink; |
|
|
| 1540 | } |
|
|
| 1541 | return ""; |
|
|
| 1542 | } |
|
|
| 1543 | |
|
|
| 1544 | # output_checkboxes subroutine |
|
|
| 1545 | |
|
|
| 1546 | # prints out the checkbox input elements that are available for the current problem |
|
|
| 1547 | |
|
|
| 1548 | sub output_checkboxes{ |
|
|
| 1549 | my $self = shift; |
|
|
| 1550 | my %can = %{ $self->{can} }; |
|
|
| 1551 | my %will = %{ $self->{will} }; |
|
|
| 1552 | |
|
|
| 1553 | if ($can{showCorrectAnswers}) { |
|
|
| 1554 | print WeBWorK::CGI_labeled_input( |
|
|
| 1555 | -type => "checkbox", |
|
|
| 1556 | -id => "showCorrectAnswers_id", |
|
|
| 1557 | -label_text => "Show correct answers", |
|
|
| 1558 | -input_attr => $will{showCorrectAnswers} ? |
|
|
| 1559 | { |
|
|
| 1560 | -name => "showCorrectAnswers", |
|
|
| 1561 | -checked => "checked", |
|
|
| 1562 | -value => 1, |
|
|
| 1563 | } |
|
|
| 1564 | : |
|
|
| 1565 | { |
|
|
| 1566 | -name => "showCorrectAnswers", |
|
|
| 1567 | -value => 1, |
|
|
| 1568 | } |
|
|
| 1569 | ); |
|
|
| 1570 | } |
|
|
| 1571 | if ($can{showHints}) { |
|
|
| 1572 | print CGI::div({style=>"color:red"}, |
|
|
| 1573 | WeBWorK::CGI_labeled_input( |
|
|
| 1574 | -type => "checkbox", |
|
|
| 1575 | -id => "showHints_id", |
|
|
| 1576 | -label_text => "Show Hints", |
|
|
| 1577 | -input_attr => $will{showHints} ? |
|
|
| 1578 | { |
|
|
| 1579 | -name => "showHints", |
|
|
| 1580 | -checked => "checked", |
|
|
| 1581 | -value => 1, |
|
|
| 1582 | } |
|
|
| 1583 | : |
|
|
| 1584 | { |
|
|
| 1585 | -name => "showCorrectAnswers", |
|
|
| 1586 | -value => 1, |
|
|
| 1587 | } |
|
|
| 1588 | ) |
|
|
| 1589 | ); |
|
|
| 1590 | } |
|
|
| 1591 | if ($can{showSolutions}) { |
|
|
| 1592 | print WeBWorK::CGI_labeled_input( |
|
|
| 1593 | -type => "checkbox", |
|
|
| 1594 | -id => "showSolutions_id", |
|
|
| 1595 | -label_text => "Show Solutions", |
|
|
| 1596 | -input_attr => $will{showSolutions} ? |
|
|
| 1597 | { |
|
|
| 1598 | -name => "showSolutions", |
|
|
| 1599 | -checked => "checked", |
|
|
| 1600 | -value => 1, |
|
|
| 1601 | } |
|
|
| 1602 | : |
|
|
| 1603 | { |
|
|
| 1604 | -name => "showCorrectAnswers", |
|
|
| 1605 | -value => 1, |
|
|
| 1606 | } |
|
|
| 1607 | ); |
|
|
| 1608 | } |
|
|
| 1609 | |
|
|
| 1610 | if ($can{showCorrectAnswers} or $can{showHints} or $can{showSolutions}) { |
|
|
| 1611 | print CGI::br(); |
|
|
| 1612 | } |
|
|
| 1613 | |
|
|
| 1614 | return ""; |
|
|
| 1615 | } |
|
|
| 1616 | |
|
|
| 1617 | # output_submit_buttons |
|
|
| 1618 | |
|
|
| 1619 | # prints out the submit button input elements that are available for the current problem |
|
|
| 1620 | |
|
|
| 1621 | sub output_submit_buttons{ |
|
|
| 1622 | my $self = shift; |
|
|
| 1623 | my $r = $self->r; |
|
|
| 1624 | my %can = %{ $self->{can} }; |
|
|
| 1625 | |
|
|
| 1626 | my $user = $r->param('user'); |
|
|
| 1627 | my $effectiveUser = $r->param('effectiveUser'); |
|
|
| 1628 | |
|
|
| 1629 | print WeBWorK::CGI_labeled_input(-type=>"submit", -id=>"previewAnswers_id", -input_attr=>{-name=>"previewAnswers", -value=>"Preview Answers"}); |
|
|
| 1630 | if ($can{checkAnswers}) { |
|
|
| 1631 | print WeBWorK::CGI_labeled_input(-type=>"submit", -id=>"checkAnswers_id", -input_attr=>{-name=>"checkAnswers", -value=>"Check Answers"}); |
|
|
| 1632 | } |
|
|
| 1633 | if ($can{getSubmitButton}) { |
|
|
| 1634 | if ($user ne $effectiveUser) { |
|
|
| 1635 | # if acting as a student, make it clear that answer submissions will |
|
|
| 1636 | # apply to the student's records, not the professor's. |
|
|
| 1637 | print WeBWorK::CGI_labeled_input(-type=>"submit", -id=>"submitAnswers_id", -input_attr=>{-name=>"submitAnswers", -value=>"Submit Answers for $effectiveUser"}); |
|
|
| 1638 | } else { |
|
|
| 1639 | #print CGI::submit(-name=>"submitAnswers", -label=>"Submit Answers", -onclick=>"alert('submit button clicked')"); |
|
|
| 1640 | print WeBWorK::CGI_labeled_input(-type=>"submit", -id=>"submitAnswers_id", -input_attr=>{-name=>"submitAnswers", -label=>"Submit Answers", -onclick=>""}); |
|
|
| 1641 | # FIXME for unknown reasons the -onclick label seems to have to be there in order to allow the forms onsubmit to trigger |
|
|
| 1642 | # WFT??? |
|
|
| 1643 | } |
|
|
| 1644 | } |
|
|
| 1645 | |
|
|
| 1646 | return ""; |
|
|
| 1647 | } |
|
|
| 1648 | |
|
|
| 1649 | # output_score_summary subroutine |
|
|
| 1650 | |
|
|
| 1651 | # prints out a summary of the student's current progress and status on the current problem |
|
|
| 1652 | |
|
|
| 1653 | sub output_score_summary{ |
|
|
| 1654 | my $self = shift; |
|
|
| 1655 | my $problem = $self->{problem}; |
|
|
| 1656 | my $set = $self->{set}; |
|
|
| 1657 | my $pg = $self->{pg}; |
|
|
| 1658 | my $scoreRecordedMessage = ""; |
|
|
| 1659 | unless(defined $self->{scoreRecordedMessage}){ |
|
|
| 1660 | $scoreRecordedMessage = $self->{scoreRecordedMessage}; |
|
|
| 1661 | } |
|
|
| 1662 | my $submitAnswers = $self->{submitAnswers}; |
|
|
| 1663 | |
|
|
| 1664 | # score summary |
|
|
| 1665 | my $attempts = $problem->num_correct + $problem->num_incorrect; |
|
|
| 1666 | my $attemptsNoun = $attempts != 1 ? "times" : "time"; |
|
|
| 1667 | my $problem_status = $problem->status || 0; |
|
|
| 1668 | my $lastScore = sprintf("%.0f%%", $problem_status * 100); # Round to whole number |
|
|
| 1669 | my ($attemptsLeft, $attemptsLeftNoun); |
|
|
| 1670 | if ($problem->max_attempts == -1) { |
|
|
| 1671 | # unlimited attempts |
|
|
| 1672 | $attemptsLeft = "unlimited"; |
|
|
| 1673 | $attemptsLeftNoun = "attempts"; |
|
|
| 1674 | } else { |
|
|
| 1675 | $attemptsLeft = $problem->max_attempts - $attempts; |
|
|
| 1676 | $attemptsLeftNoun = $attemptsLeft == 1 ? "attempt" : "attempts"; |
|
|
| 1677 | } |
|
|
| 1678 | |
|
|
| 1679 | my $setClosed = 0; |
|
|
| 1680 | my $setClosedMessage; |
|
|
| 1681 | if (before($set->open_date) or after($set->due_date)) { |
|
|
| 1682 | $setClosed = 1; |
|
|
| 1683 | if (before($set->open_date)) { |
|
|
| 1684 | $setClosedMessage = "This homework set is not yet open."; |
|
|
| 1685 | } elsif (after($set->due_date)) { |
|
|
| 1686 | $setClosedMessage = "This homework set is closed."; |
|
|
| 1687 | } |
|
|
| 1688 | } |
|
|
| 1689 | #if (before($set->open_date) or after($set->due_date)) { |
|
|
| 1690 | # $setClosed = 1; |
|
|
| 1691 | # $setClosedMessage = "This homework set is closed."; |
|
|
| 1692 | # if ($authz->hasPermissions($user, "view_answers")) { |
|
|
| 1693 | # $setClosedMessage .= " However, since you are a privileged user, additional attempts will be recorded."; |
|
|
| 1694 | # } else { |
|
|
| 1695 | # $setClosedMessage .= " Additional attempts will not be recorded."; |
|
|
| 1696 | # } |
|
|
| 1697 | #} |
|
|
| 1698 | unless (defined( $pg->{state}->{state_summary_msg}) and $pg->{state}->{state_summary_msg}=~/\S/) { |
|
|
| 1699 | my $notCountedMessage = ($problem->value) ? "" : "(This problem will not count towards your grade.)"; |
|
|
| 1700 | print CGI::p(join("", |
|
|
| 1701 | $submitAnswers ? $scoreRecordedMessage . CGI::br() : "", |
|
|
| 1702 | "You have attempted this problem $attempts $attemptsNoun.", CGI::br(), |
|
|
| 1703 | $submitAnswers ?"You received a score of ".sprintf("%.0f%%", $pg->{result}->{score} * 100)." for this attempt.".CGI::br():'', |
|
|
| 1704 | $problem->attempted |
|
|
| 1705 | ? "Your overall recorded score is $lastScore. $notCountedMessage" . CGI::br() |
|
|
| 1706 | : "", |
|
|
| 1707 | $setClosed ? $setClosedMessage : "You have $attemptsLeft $attemptsLeftNoun remaining." |
|
|
| 1708 | )); |
|
|
| 1709 | }else { |
|
|
| 1710 | print CGI::p($pg->{state}->{state_summary_msg}); |
|
|
| 1711 | } |
|
|
| 1712 | |
|
|
| 1713 | return ""; |
|
|
| 1714 | } |
|
|
| 1715 | |
|
|
| 1716 | # output_misc subroutine |
|
|
| 1717 | |
|
|
| 1718 | # prints out other necessary elements |
|
|
| 1719 | |
|
|
| 1720 | sub output_misc{ |
|
|
| 1721 | |
|
|
| 1722 | my $self = shift; |
|
|
| 1723 | my $r = $self->r; |
|
|
| 1724 | my $ce = $r->ce; |
|
|
| 1725 | my $db = $r->db; |
|
|
| 1726 | my $pg = $self->{pg}; |
|
|
| 1727 | my %will = %{ $self->{will} }; |
|
|
| 1728 | my $user = $r->param('user'); |
|
|
| 1729 | |
|
|
| 1730 | print CGI::start_div(); |
|
|
| 1731 | |
|
|
| 1732 | my $pgdebug = join(CGI::br(), @{$pg->{pgcore}->{flags}->{DEBUG_messages}} ); |
|
|
| 1733 | my $pgwarning = join(CGI::br(), @{$pg->{pgcore}->{flags}->{WARNING_messages}} ); |
|
|
| 1734 | my $pginternalerrors = join(CGI::br(), @{$pg->{pgcore}->get_internal_debug_messages} ); |
|
|
| 1735 | my $pgerrordiv = $pgdebug||$pgwarning||$pginternalerrors; # is 1 if any of these are non-empty |
|
|
| 1736 | |
|
|
| 1737 | print CGI::p({style=>"color:red;"}, "Checking additional error messages") if $pgerrordiv ; |
|
|
| 1738 | print CGI::p("pg debug<br/> $pgdebug" ) if $pgdebug ; |
|
|
| 1739 | print CGI::p("pg warning<br/>$pgwarning" ) if $pgwarning ; |
|
|
| 1740 | print CGI::p("pg internal errors<br/> $pginternalerrors") if $pginternalerrors; |
|
|
| 1741 | print CGI::end_div() if $pgerrordiv ; |
|
|
| 1742 | |
|
|
| 1743 | # save state for viewOptions |
|
|
| 1744 | print CGI::hidden( |
|
|
| 1745 | -name => "showOldAnswers", |
|
|
| 1746 | -value => $will{showOldAnswers} |
|
|
| 1747 | ), |
|
|
| 1748 | |
|
|
| 1749 | CGI::hidden( |
|
|
| 1750 | -name => "displayMode", |
|
|
| 1751 | -value => $self->{displayMode} |
|
|
| 1752 | ); |
|
|
| 1753 | print( CGI::hidden( |
|
|
| 1754 | -name => 'editMode', |
|
|
| 1755 | -value => $self->{editMode}, |
|
|
| 1756 | ) |
|
|
| 1757 | ) if defined($self->{editMode}) and $self->{editMode} eq 'temporaryFile'; |
|
|
| 1758 | |
|
|
| 1759 | # this is a security risk -- students can use this to find the source code for the problem |
|
|
| 1760 | |
|
|
| 1761 | my $permissionLevel = $db->getPermissionLevel($user)->permission; |
|
|
| 1762 | my $professorPermissionLevel = $ce->{userRoles}->{professor}; |
|
|
| 1763 | print( CGI::hidden( |
|
|
| 1764 | -name => 'sourceFilePath', |
|
|
| 1765 | -value => $self->{problem}->{source_file} |
|
|
| 1766 | )) if defined($self->{problem}->{source_file}) and $permissionLevel>= $professorPermissionLevel; # only allow this for professors |
|
|
| 1767 | |
|
|
| 1768 | print( CGI::hidden( |
|
|
| 1769 | -name => 'problemSeed', |
|
|
| 1770 | -value => $r->param("problemSeed") |
|
|
| 1771 | )) if defined($r->param("problemSeed")) and $permissionLevel>= $professorPermissionLevel; # only allow this for professors |
|
|
| 1772 | |
|
|
| 1773 | return ""; |
|
|
| 1774 | } |
|
|
| 1775 | |
|
|
| 1776 | # output_summary subroutine |
|
|
| 1777 | |
|
|
| 1778 | # prints out the summary of the questions that the student has answered for the current problem, along with available information about correctness |
|
|
| 1779 | |
|
|
| 1780 | sub output_summary{ |
|
|
| 1781 | |
|
|
| 1782 | my $self = shift; |
|
|
| 1783 | |
|
|
| 1784 | my $editMode = $self->{editMode}; |
|
|
| 1785 | my $problem = $self->{problem}; |
|
|
| 1786 | my $pg = $self->{pg}; |
|
|
| 1787 | my $submitAnswers = $self->{submitAnswers}; |
|
|
| 1788 | my %will = %{ $self->{will} }; |
|
|
| 1789 | my $checkAnswers = $self->{checkAnswers}; |
|
|
| 1790 | my $previewAnswers = $self->{previewAnswers}; |
|
|
| 1791 | |
|
|
| 1792 | my $r = $self->r; |
|
|
| 1793 | |
|
|
| 1794 | my $authz = $r->authz; |
|
|
| 1795 | my $user = $r->param('user'); |
|
|
| 1796 | |
|
|
| 1797 | # attempt summary |
|
|
| 1798 | #FIXME -- the following is a kludge: if showPartialCorrectAnswers is negative don't show anything. |
|
|
| 1799 | # until after the due date |
|
|
| 1800 | # do I need to check $will{showCorrectAnswers} to make preflight work?? |
|
|
| 1801 | if (($pg->{flags}->{showPartialCorrectAnswers} >= 0 and $submitAnswers) ) { |
|
|
| 1802 | # print this if user submitted answers OR requested correct answers |
|
|
| 1803 | |
|
|
| 1804 | print $self->attemptResults($pg, 1, |
|
|
| 1805 | $will{showCorrectAnswers}, |
|
|
| 1806 | $pg->{flags}->{showPartialCorrectAnswers}, 1, 1); |
|
|
| 1807 | } elsif ($checkAnswers) { |
|
|
| 1808 | # print this if user previewed answers |
|
|
| 1809 | print CGI::div({class=>'ResultsWithError'},"ANSWERS ONLY CHECKED -- ANSWERS NOT RECORDED"), CGI::br(); |
|
|
| 1810 | print $self->attemptResults($pg, 1, $will{showCorrectAnswers}, 1, 1, 1); |
|
|
| 1811 | # show attempt answers |
|
|
| 1812 | # show correct answers if asked |
|
|
| 1813 | # show attempt results (correctness) |
|
|
| 1814 | # show attempt previews |
|
|
| 1815 | } elsif ($previewAnswers) { |
|
|
| 1816 | # print this if user previewed answers |
|
|
| 1817 | print CGI::div({class=>'ResultsWithError'},"PREVIEW ONLY -- ANSWERS NOT RECORDED"),CGI::br(),$self->attemptResults($pg, 1, 0, 0, 0, 1); |
|
|
| 1818 | # show attempt answers |
|
|
| 1819 | # don't show correct answers |
|
|
| 1820 | # don't show attempt results (correctness) |
|
|
| 1821 | # show attempt previews |
|
|
| 1822 | } |
|
|
| 1823 | |
|
|
| 1824 | return ""; |
|
|
| 1825 | } |
|
|
| 1826 | |
|
|
| 1827 | # output_custom_edit_message |
|
|
| 1828 | |
|
|
| 1829 | # prints out a custom edit message |
|
|
| 1830 | |
|
|
| 1831 | sub output_custom_edit_message{ |
|
|
| 1832 | my $self = shift; |
|
|
| 1833 | my $r = $self->r; |
|
|
| 1834 | my $authz = $r->authz; |
|
|
| 1835 | my $user = $r->param('user'); |
|
|
| 1836 | my $editMode = $self->{editMode}; |
|
|
| 1837 | my $problem = $self->{problem}; |
|
|
| 1838 | |
|
|
| 1839 | # custom message for editor |
|
|
| 1840 | if ($authz->hasPermissions($user, "modify_problem_sets") and defined $editMode) { |
|
|
| 1841 | if ($editMode eq "temporaryFile") { |
|
|
| 1842 | print CGI::p(CGI::div({class=>'temporaryFile'}, "Viewing temporary file: ", $problem->source_file)); |
|
|
| 1843 | } elsif ($editMode eq "savedFile") { |
|
|
| 1844 | # taken care of in the initialization phase |
|
|
| 1845 | } |
|
|
| 1846 | } |
|
|
| 1847 | |
|
|
| 1848 | return ""; |
|
|
| 1849 | } |
|
|
| 1850 | |
|
|
| 1851 | # output_JS subroutine |
|
|
| 1852 | |
|
|
| 1853 | # prints out the wz_tooltip.js script for the current site. |
|
|
| 1854 | |
|
|
| 1855 | sub output_JS{ |
|
|
| 1856 | |
|
|
| 1857 | my $self = shift; |
|
|
| 1858 | my $r = $self->r; |
|
|
| 1859 | my $ce = $r->ce; |
|
|
| 1860 | |
|
|
| 1861 | my $site_url = $ce->{webworkURLs}->{htdocs}; |
|
|
| 1862 | print CGI::start_script({type=>"text/javascript", src=>"$site_url/js/wz_tooltip.js"}), CGI::end_script(); |
|
|
| 1863 | return ""; |
|
|
| 1864 | } |
|
|
| 1865 | |
|
|
| 1866 | # output_CSS subroutine |
|
|
| 1867 | |
|
|
| 1868 | # prints the CSS scripts to page. Does some PERL trickery to form the styles for the correct answers and the incorrect answers (which may be substituted with JS sometime in the future). |
|
|
| 1869 | |
|
|
| 1870 | sub output_CSS{ |
|
|
| 1871 | |
|
|
| 1872 | my $self = shift; |
|
|
| 1873 | my $r = $self->r; |
|
|
| 1874 | my $ce = $r->ce; |
|
|
| 1875 | my $pg = $self->{pg}; |
|
|
| 1876 | |
|
|
| 1877 | # always show colors for checkAnswers |
|
|
| 1878 | # show colors for submit answer if |
|
|
| 1879 | if (($self->{checkAnswers}) or ($self->{submitAnswers} and $pg->{flags}->{showPartialCorrectAnswers}) ) { |
|
|
| 1880 | print CGI::start_style({type=>"text/css"}); |
|
|
| 1881 | print '#'.join(', #', @{ $self->{correct_ids} }), $ce->{pg}{options}{correct_answer} if ref( $self->{correct_ids} )=~/ARRAY/; #correct green |
|
|
| 1882 | print '#'.join(', #', @{ $self->{incorrect_ids} }), $ce->{pg}{options}{incorrect_answer} if ref( $self->{incorrect_ids})=~/ARRAY/; #incorrect reddish |
|
|
| 1883 | print CGI::end_style(); |
|
|
| 1884 | } |
|
|
| 1885 | |
|
|
| 1886 | return ""; |
|
|
| 1887 | } |
|
|
| 1888 | |
|
|
| 1889 | # output_past_answer_button |
|
|
| 1890 | |
|
|
| 1891 | # prints out the "Show Past Answers" button |
|
|
| 1892 | |
|
|
| 1893 | sub output_past_answer_button{ |
|
|
| 1894 | my $self = shift; |
|
|
| 1895 | my $r = $self->r; |
|
|
| 1896 | my $problem = $self->{problem}; |
|
|
| 1897 | |
|
|
| 1898 | my $authz = $r->authz; |
|
|
| 1899 | my $urlpath = $r->urlpath; |
|
|
| 1900 | my $user = $r->param('user'); |
|
|
| 1901 | |
|
|
| 1902 | my $courseName = $urlpath->arg("courseID"); |
|
|
| 1903 | |
|
|
| 1904 | my $pastAnswersPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::ShowAnswers", |
|
|
| 1905 | courseID => $courseName); |
|
|
| 1906 | my $showPastAnswersURL = $self->systemLink($pastAnswersPage, authen => 0); # no authen info for form action |
|
|
| 1907 | |
|
|
| 1908 | # print answer inspection button |
|
|
| 1909 | if ($authz->hasPermissions($user, "view_answers")) { |
|
|
| 1910 | print "\n", |
|
|
| 1911 | CGI::start_form(-method=>"POST",-action=>$showPastAnswersURL,-target=>"WW_Info"),"\n", |
|
|
| 1912 | $self->hidden_authen_fields,"\n", |
|
|
| 1913 | CGI::hidden(-name => 'courseID', -value=>$courseName), "\n", |
|
|
| 1914 | CGI::hidden(-name => 'problemID', -value=>$problem->problem_id), "\n", |
|
|
| 1915 | CGI::hidden(-name => 'setID', -value=>$problem->set_id), "\n", |
|
|
| 1916 | CGI::hidden(-name => 'studentUser', -value=>$problem->user_id), "\n", |
|
|
| 1917 | CGI::p( {-align=>"left"}, |
|
|
| 1918 | CGI::submit(-name => 'action', -value=>'Show Past Answers') |
|
|
| 1919 | ), "\n", |
|
|
| 1920 | CGI::endform(); |
|
|
| 1921 | } |
|
|
| 1922 | |
|
|
| 1923 | return ""; |
|
|
| 1924 | } |
|
|
| 1925 | |
|
|
| 1926 | # output_email_instructor subroutine |
|
|
| 1927 | |
|
|
| 1928 | # prints out the "Email Instructor" button |
|
|
| 1929 | |
|
|
| 1930 | sub output_email_instructor{ |
|
|
| 1931 | my $self = shift; |
|
|
| 1932 | my $problem = $self->{problem}; |
|
|
| 1933 | my %will = %{ $self->{will} }; |
|
|
| 1934 | my $pg = $self->{pg}; |
|
|
| 1935 | |
|
|
| 1936 | print $self->feedbackMacro( |
|
|
| 1937 | module => __PACKAGE__, |
|
|
| 1938 | set => $self->{set}->set_id, |
|
|
| 1939 | problem => $problem->problem_id, |
|
|
| 1940 | displayMode => $self->{displayMode}, |
|
|
| 1941 | showOldAnswers => $will{showOldAnswers}, |
|
|
| 1942 | showCorrectAnswers => $will{showCorrectAnswers}, |
|
|
| 1943 | showHints => $will{showHints}, |
|
|
| 1944 | showSolutions => $will{showSolutions}, |
|
|
| 1945 | pg_object => $pg, |
|
|
| 1946 | ); |
|
|
| 1947 | |
|
|
| 1948 | return ""; |
|
|
| 1949 | } |
|
|
| 1950 | |
|
|
| 1951 | sub output_hidden_info{ |
|
|
| 1952 | my $self = shift; |
|
|
| 1953 | |
|
|
| 1954 | if(defined $self->{correct_ids}){ |
|
|
| 1955 | my $correctRef = $self->{correct_ids}; |
|
|
| 1956 | my @correct = @$correctRef; |
|
|
| 1957 | foreach(@correct){ |
|
|
| 1958 | print CGI::hidden(-name=>"correct_ids", -value=>$_."_val"); |
|
|
| 1959 | } |
|
|
| 1960 | } |
|
|
| 1961 | if(defined $self->{incorrect_ids}){ |
|
|
| 1962 | my $incorrectRef = $self->{incorrect_ids}; |
|
|
| 1963 | my @incorrect = @$incorrectRef; |
|
|
| 1964 | foreach(@incorrect){ |
|
|
| 1965 | print CGI::hidden(-name=>"incorrect_ids", -value=>$_."_val"); |
|
|
| 1966 | } |
|
|
| 1967 | } |
|
|
| 1968 | |
|
|
| 1969 | return ""; |
|
|
| 1970 | } |
|
|
| 1971 | |
|
|
| 1972 | sub output_coloring_JS{ |
|
|
| 1973 | my $self = shift; |
|
|
| 1974 | my $r = $self->r; |
|
|
| 1975 | my $ce = $r->ce; |
|
|
| 1976 | |
|
|
| 1977 | my $site_url = $ce->{webworkURLs}->{htdocs}; |
|
|
| 1978 | print CGI::start_script({type=>"text/javascript", src=>"$site_url/js/color.js"}), CGI::end_script(); |
|
|
| 1979 | return ""; |
|
|
| 1980 | } |
|
|
| 1981 | |
|
|
| 1982 | 1; |
1350 | 1; |