| 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.128 2004/05/13 18:38:19 toenail Exp $ |
4 | # $CVSHeader: webwork-modperl/lib/WeBWorK/ContentGenerator/Problem.pm,v 1.141 2004/06/04 21:40:07 jj 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. |
| … | |
… | |
| 167 | my $problemSeed = $r->param("problemSeed"); |
167 | my $problemSeed = $r->param("problemSeed"); |
| 168 | if (defined $problemSeed) { |
168 | if (defined $problemSeed) { |
| 169 | $problem->problem_seed($problemSeed); |
169 | $problem->problem_seed($problemSeed); |
| 170 | } |
170 | } |
| 171 | |
171 | |
| 172 | my $published = ($set->published) ? "Published" : "Unpublished"; |
172 | my $publishedClass = ($set->published) ? "Published" : "Unpublished"; |
|
|
173 | my $publishedText = ($set->published) ? "visable to students." : "hidden from students."; |
| 173 | $self->addmessage(CGI::p("This set is " . CGI::font({class=>$published}, $published))); |
174 | $self->addmessage(CGI::p("This set is " . CGI::font({class=>$publishedClass}, $publishedText))); |
| 174 | } else { |
175 | } else { |
| 175 | |
176 | |
| 176 | $self->addmessage(CGI::div({class=>"ResultsWithError"}, CGI::p("This problem will not count towards your grade."))) unless $problem->value;; |
177 | $self->addmessage(CGI::div({class=>"ResultsWithError"}, CGI::p("This problem will not count towards your grade."))) unless $problem->value;; |
| 177 | # students can't view problems not assigned to them |
178 | # students can't view problems not assigned to them |
| 178 | |
179 | |
| … | |
… | |
| 251 | showHints => 1, |
252 | showHints => 1, |
| 252 | showSolutions => canShowSolutions($permissionLevel, $set->answer_date), |
253 | showSolutions => canShowSolutions($permissionLevel, $set->answer_date), |
| 253 | recordAnswers => canRecordAnswers($permissionLevel, $set->open_date, $set->due_date, |
254 | recordAnswers => canRecordAnswers($permissionLevel, $set->open_date, $set->due_date, |
| 254 | $problem->max_attempts, $problem->num_correct + $problem->num_incorrect + 1), |
255 | $problem->max_attempts, $problem->num_correct + $problem->num_incorrect + 1), |
| 255 | # attempts=num_correct+num_incorrect+1, as this happens before updating $problem |
256 | # attempts=num_correct+num_incorrect+1, as this happens before updating $problem |
| 256 | checkAnswers => canCheckAnswers($permissionLevel, $set->answer_date), |
257 | checkAnswers => canCheckAnswers($permissionLevel, $set->due_date), |
| 257 | ); |
258 | ); |
| 258 | |
259 | |
| 259 | # more complicated logic for showing check answer button: |
260 | # more complicated logic for showing check answer button: |
| 260 | # checkAnswers button shows up after due date -- once a student can't record anymore |
261 | # checkAnswers button shows up after due date -- once a student can't record anymore |
| 261 | # checkAnswers button always shows up when an instructor or TA is acting |
262 | # checkAnswers button always shows up when an instructor or TA is acting |
| … | |
… | |
| 364 | |
365 | |
| 365 | sub options { |
366 | sub options { |
| 366 | my ($self) = @_; |
367 | my ($self) = @_; |
| 367 | |
368 | |
| 368 | return "" if $self->{invalidProblem}; |
369 | return "" if $self->{invalidProblem}; |
|
|
370 | my $sourceFilePathfield = ''; |
|
|
371 | if($self->r->param("sourceFilePath")) { |
|
|
372 | $sourceFilePathfield = CGI::hidden(-name => "sourceFilePath", |
|
|
373 | -value => $self->r->param("sourceFilePath")); |
|
|
374 | } |
| 369 | |
375 | |
| 370 | return join("", |
376 | return join("", |
| 371 | CGI::start_form("POST", $self->{r}->uri), |
377 | CGI::start_form("POST", $self->{r}->uri), |
| 372 | $self->hidden_authen_fields, |
378 | $self->hidden_authen_fields, |
|
|
379 | $sourceFilePathfield, |
| 373 | CGI::hr(), |
380 | CGI::hr(), |
| 374 | CGI::start_div({class=>"viewOptions"}), |
381 | CGI::start_div({class=>"viewOptions"}), |
| 375 | $self->viewOptions(), |
382 | $self->viewOptions(), |
| 376 | CGI::end_div(), |
383 | CGI::end_div(), |
| 377 | CGI::end_form() |
384 | CGI::end_form() |
| … | |
… | |
| 411 | |
418 | |
| 412 | print CGI::start_ul({class=>"LinksMenu"}); |
419 | print CGI::start_ul({class=>"LinksMenu"}); |
| 413 | print CGI::start_li(); |
420 | print CGI::start_li(); |
| 414 | print CGI::span({style=>"font-size:larger"}, "Problems"); |
421 | print CGI::span({style=>"font-size:larger"}, "Problems"); |
| 415 | print CGI::start_ul(); |
422 | print CGI::start_ul(); |
| 416 | |
423 | |
| 417 | foreach my $problemID (@problemIDs) { |
424 | foreach my $problemID (@problemIDs) { |
| 418 | my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", |
425 | my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", |
| 419 | courseID => $courseID, setID => $setID, problemID => $problemID); |
426 | courseID => $courseID, setID => $setID, problemID => $problemID); |
| 420 | print CGI::li(CGI::a({href=>$self->systemLink($problemPage)}, "Problem $problemID")); |
427 | print CGI::li(CGI::a({href=>$self->systemLink($problemPage, params=>{displayMode => $self->{displayMode}})}, "Problem $problemID")); |
| 421 | } |
428 | } |
| 422 | |
429 | |
| 423 | print CGI::end_ul(); |
430 | print CGI::end_ul(); |
| 424 | print CGI::end_li(); |
431 | print CGI::end_li(); |
| 425 | print CGI::end_ul(); |
432 | print CGI::end_ul(); |
| … | |
… | |
| 556 | |
563 | |
| 557 | ##### answer processing ##### |
564 | ##### answer processing ##### |
| 558 | $WeBWorK::timer->continue("begin answer processing") if defined($WeBWorK::timer); |
565 | $WeBWorK::timer->continue("begin answer processing") if defined($WeBWorK::timer); |
| 559 | # if answers were submitted: |
566 | # if answers were submitted: |
| 560 | my $scoreRecordedMessage; |
567 | my $scoreRecordedMessage; |
|
|
568 | my $pureProblem; |
| 561 | if ($submitAnswers) { |
569 | if ($submitAnswers) { |
| 562 | # get a "pure" (unmerged) UserProblem to modify |
570 | # get a "pure" (unmerged) UserProblem to modify |
| 563 | # this will be undefined if the problem has not been assigned to this user |
571 | # this will be undefined if the problem has not been assigned to this user |
| 564 | my $pureProblem = $db->getUserProblem($problem->user_id, $problem->set_id, $problem->problem_id); # checked |
572 | $pureProblem = $db->getUserProblem($problem->user_id, $problem->set_id, $problem->problem_id); # checked |
| 565 | if (defined $pureProblem) { |
573 | if (defined $pureProblem) { |
| 566 | # store answers in DB for sticky answers |
574 | # store answers in DB for sticky answers |
| 567 | my %answersToStore; |
575 | my %answersToStore; |
| 568 | my %answerHash = %{ $pg->{answers} }; |
576 | my %answerHash = %{ $pg->{answers} }; |
| 569 | $answersToStore{$_} = $self->{formFields}->{$_} #$answerHash{$_}->{original_student_ans} -- this may have been modified for fields with multiple values. Don't use it!! |
577 | $answersToStore{$_} = $self->{formFields}->{$_} #$answerHash{$_}->{original_student_ans} -- this may have been modified for fields with multiple values. Don't use it!! |
| … | |
… | |
| 628 | } |
636 | } |
| 629 | |
637 | |
| 630 | # logging student answers |
638 | # logging student answers |
| 631 | |
639 | |
| 632 | my $answer_log = $self->{ce}->{courseFiles}->{logs}->{'answer_log'}; |
640 | my $answer_log = $self->{ce}->{courseFiles}->{logs}->{'answer_log'}; |
| 633 | if ( defined($answer_log )) { |
641 | if ( defined($answer_log ) and defined($pureProblem)) { |
| 634 | if ($submitAnswers ) { |
642 | if ($submitAnswers ) { |
| 635 | my $answerString = ""; |
643 | my $answerString = ""; |
| 636 | my %answerHash = %{ $pg->{answers} }; |
644 | my %answerHash = %{ $pg->{answers} }; |
| 637 | # FIXME this is the line 552 error. make sure original student ans is defined. |
645 | # FIXME this is the line 552 error. make sure original student ans is defined. |
| 638 | # The fact that it is not defined is probably due to an error in some answer evaluator. |
646 | # The fact that it is not defined is probably due to an error in some answer evaluator. |
| … | |
… | |
| 707 | } |
715 | } |
| 708 | |
716 | |
| 709 | print CGI::end_div(); |
717 | print CGI::end_div(); |
| 710 | |
718 | |
| 711 | print CGI::start_div({class=>"problem"}); |
719 | print CGI::start_div({class=>"problem"}); |
| 712 | |
720 | |
| 713 | # main form |
721 | # main form |
| 714 | print |
722 | print |
| 715 | CGI::startform("POST", $r->uri), |
723 | CGI::startform("POST", $r->uri), |
| 716 | $self->hidden_authen_fields, |
724 | $self->hidden_authen_fields, |
| 717 | CGI::p($pg->{body_text}), |
725 | CGI::p($pg->{body_text}), |
| … | |
… | |
| 807 | ) if defined($self->{editMode}) and $self->{editMode} eq 'temporaryFile'; |
815 | ) if defined($self->{editMode}) and $self->{editMode} eq 'temporaryFile'; |
| 808 | print( CGI::hidden( |
816 | print( CGI::hidden( |
| 809 | -name => 'sourceFilePath', |
817 | -name => 'sourceFilePath', |
| 810 | -value => $self->{problem}->{source_file} |
818 | -value => $self->{problem}->{source_file} |
| 811 | )) if defined($self->{problem}->{source_file}); |
819 | )) if defined($self->{problem}->{source_file}); |
|
|
820 | |
|
|
821 | print( CGI::hidden( |
|
|
822 | -name => 'problemSeed', |
|
|
823 | -value => $r->param("problemSeed") |
|
|
824 | )) if defined($r->param("problemSeed")); |
| 812 | |
825 | |
| 813 | # end of main form |
826 | # end of main form |
| 814 | print CGI::endform(); |
827 | print CGI::endform(); |
| 815 | |
|
|
| 816 | |
828 | |
| 817 | print CGI::start_div({class=>"problemFooter"}); |
829 | print CGI::start_div({class=>"problemFooter"}); |
| 818 | |
830 | |
| 819 | ## arguments for answer inspection button |
831 | ## arguments for answer inspection button |
| 820 | #my $prof_url = $ce->{webworkURLs}->{oldProf}; |
832 | #my $prof_url = $ce->{webworkURLs}->{oldProf}; |
| … | |
… | |
| 948 | #$header .= CGI::th("Part"); |
960 | #$header .= CGI::th("Part"); |
| 949 | $header .= $showAttemptAnswers ? CGI::th("Entered") : ""; |
961 | $header .= $showAttemptAnswers ? CGI::th("Entered") : ""; |
| 950 | $header .= $showAttemptPreview ? CGI::th("Answer Preview") : ""; |
962 | $header .= $showAttemptPreview ? CGI::th("Answer Preview") : ""; |
| 951 | $header .= $showCorrectAnswers ? CGI::th("Correct") : ""; |
963 | $header .= $showCorrectAnswers ? CGI::th("Correct") : ""; |
| 952 | $header .= $showAttemptResults ? CGI::th("Result") : ""; |
964 | $header .= $showAttemptResults ? CGI::th("Result") : ""; |
| 953 | $header .= $showMessages ? CGI::th("messages") : ""; |
965 | $header .= $showMessages ? CGI::th("Messages") : ""; |
| 954 | my @tableRows = ( $header ); |
966 | my @tableRows = ( $header ); |
| 955 | my $numCorrect; |
967 | my $numCorrect; |
| 956 | foreach my $name (@answerNames) { |
968 | foreach my $name (@answerNames) { |
| 957 | my $answerResult = $pg->{answers}->{$name}; |
969 | my $answerResult = $pg->{answers}->{$name}; |
| 958 | my $studentAnswer = $answerResult->{student_ans}; # original_student_ans |
970 | my $studentAnswer = $answerResult->{student_ans}; # original_student_ans |
| … | |
… | |
| 1015 | # # $str is a complex number |
1027 | # # $str is a complex number |
| 1016 | #} |
1028 | #} |
| 1017 | |
1029 | |
| 1018 | sub viewOptions { |
1030 | sub viewOptions { |
| 1019 | my ($self) = @_; |
1031 | my ($self) = @_; |
|
|
1032 | my $ce = $self->r->ce; |
| 1020 | |
1033 | |
| 1021 | my $displayMode = $self->{displayMode}; |
1034 | my $displayMode = $self->{displayMode}; |
| 1022 | my %must = %{ $self->{must} }; |
1035 | my %must = %{ $self->{must} }; |
| 1023 | my %can = %{ $self->{can} }; |
1036 | my %can = %{ $self->{can} }; |
| 1024 | my %will = %{ $self->{will} }; |
1037 | my %will = %{ $self->{will} }; |
| … | |
… | |
| 1032 | -label => "Saved answers", |
1045 | -label => "Saved answers", |
| 1033 | ), " ".CGI::br(); |
1046 | ), " ".CGI::br(); |
| 1034 | |
1047 | |
| 1035 | $optionLine and $optionLine .= join "", CGI::br(); |
1048 | $optionLine and $optionLine .= join "", CGI::br(); |
| 1036 | |
1049 | |
| 1037 | return CGI::div({-style=>"border: thin groove; padding: 1ex; margin: 2ex align: left"}, |
1050 | my %display_modes = %{WeBWorK::PG::DISPLAY_MODES()}; |
|
|
1051 | my @active_modes = grep { exists $display_modes{$_} } |
|
|
1052 | @{$ce->{pg}->{displayModes}}; |
|
|
1053 | my $modeLine = (scalar(@active_modes)>1) ? |
| 1038 | "View equations as: ".CGI::br(), |
1054 | "View equations as: ".CGI::br(). |
| 1039 | CGI::radio_group( |
1055 | CGI::radio_group( |
| 1040 | -name => "displayMode", |
1056 | -name => "displayMode", |
| 1041 | -values => ['plainText', 'formattedText', 'images'], |
1057 | -values => \@active_modes, |
| 1042 | -default => $displayMode, |
1058 | -default => $displayMode, |
| 1043 | -linebreak=>'true', |
1059 | -linebreak=>'true', |
| 1044 | -labels => { |
1060 | -labels => { |
| 1045 | plainText => "plain", |
1061 | plainText => "plain", |
| 1046 | formattedText => "formatted", |
1062 | formattedText => "formatted", |
| 1047 | images => "images", |
1063 | images => "images", |
|
|
1064 | jsMath => "jsMath", |
|
|
1065 | asciimath => "asciimath", |
| 1048 | } |
1066 | }, |
| 1049 | ), CGI::br(),CGI::hr(), |
1067 | ). CGI::br().CGI::hr() : ''; |
|
|
1068 | |
|
|
1069 | return CGI::div({-style=>"border: thin groove; padding: 1ex; margin: 2ex align: left"}, |
|
|
1070 | $modeLine, |
| 1050 | $optionLine, |
1071 | $optionLine, |
| 1051 | CGI::submit(-name=>"redisplay", -label=>"Save Options"), |
1072 | CGI::submit(-name=>"redisplay", -label=>"Save Options"), |
| 1052 | ); |
1073 | ); |
| 1053 | } |
1074 | } |
| 1054 | |
1075 | |
| … | |
… | |
| 1082 | if ($?) { |
1103 | if ($?) { |
| 1083 | return "<b>[tth failed: $? $@]</b>"; |
1104 | return "<b>[tth failed: $? $@]</b>"; |
| 1084 | } |
1105 | } |
| 1085 | return $result; |
1106 | return $result; |
| 1086 | } elsif ($displayMode eq "images") { |
1107 | } elsif ($displayMode eq "images") { |
| 1087 | $imgGen->add($answerResult->{preview_latex_string}); |
1108 | $imgGen->add($tex); |
|
|
1109 | } elsif ($displayMode eq "jsMath") { |
|
|
1110 | |
|
|
1111 | return '<DIV CLASS="math">'.$tex.'</DIV>' ; |
|
|
1112 | |
|
|
1113 | |
|
|
1114 | |
|
|
1115 | |
| 1088 | } |
1116 | } |
| 1089 | } |
1117 | } |
| 1090 | |
1118 | |
| 1091 | ##### permission queries ##### |
1119 | ##### permission queries ##### |
| 1092 | |
1120 | |
| … | |
… | |
| 1116 | my $recordAnswers = $permHigh || ($timeOK && $attemptsOK); |
1144 | my $recordAnswers = $permHigh || ($timeOK && $attemptsOK); |
| 1117 | return $recordAnswers; |
1145 | return $recordAnswers; |
| 1118 | } |
1146 | } |
| 1119 | |
1147 | |
| 1120 | sub canCheckAnswers($$) { |
1148 | sub canCheckAnswers($$) { |
| 1121 | my ($permissionLevel, $answerDate) = @_; |
1149 | my ($permissionLevel, $dueDate) = @_; |
| 1122 | my $permHigh = $permissionLevel > 0; |
1150 | my $permHigh = $permissionLevel > 0; |
| 1123 | my $timeOK = time >= $answerDate; |
1151 | my $timeOK = time >= $dueDate; |
| 1124 | my $recordAnswers = $permHigh || $timeOK; |
1152 | my $recordAnswers = $permHigh || $timeOK; |
| 1125 | return $recordAnswers; |
1153 | return $recordAnswers; |
| 1126 | } |
1154 | } |
| 1127 | |
1155 | |
| 1128 | sub mustRecordAnswers($) { |
1156 | sub mustRecordAnswers($) { |