| … | |
… | |
| 9 | =head1 NAME |
9 | =head1 NAME |
| 10 | |
10 | |
| 11 | WeBWorK::ContentGenerator::Problem - Allow a student to interact with a problem. |
11 | WeBWorK::ContentGenerator::Problem - Allow a student to interact with a problem. |
| 12 | |
12 | |
| 13 | =cut |
13 | =cut |
| 14 | my $timer0_ON=0; # times pg translation phase |
14 | my $timer0_ON=1; # times pg translation phase |
| 15 | use strict; |
15 | use strict; |
| 16 | use warnings; |
16 | use warnings; |
| 17 | use CGI qw(); |
17 | use CGI qw(); |
| 18 | use File::Path qw(rmtree); |
18 | use File::Path qw(rmtree); |
| 19 | use WeBWorK::Form; |
19 | use WeBWorK::Form; |
| 20 | use WeBWorK::PG; |
20 | use WeBWorK::PG; |
|
|
21 | use WeBWorK::PG::ImageGenerator; |
| 21 | use WeBWorK::PG::IO; |
22 | use WeBWorK::PG::IO; |
| 22 | use WeBWorK::Utils qw(writeLog encodeAnswers decodeAnswers ref2string makeTempDirectory); |
23 | use WeBWorK::Utils qw(writeLog writeCourseLog encodeAnswers decodeAnswers ref2string makeTempDirectory); |
| 23 | use WeBWorK::DB::Utils qw(global2user user2global findDefaults); |
24 | use WeBWorK::DB::Utils qw(global2user user2global findDefaults); |
| 24 | use WeBWorK::Timing; |
25 | use WeBWorK::Timing; |
| 25 | |
26 | |
| 26 | ############################################################ |
27 | ############################################################ |
| 27 | # |
28 | # |
| … | |
… | |
| 42 | # checkAnswers - name of the "Check Answers" button |
43 | # checkAnswers - name of the "Check Answers" button |
| 43 | # previewAnswers - name of the "Preview Answers" button |
44 | # previewAnswers - name of the "Preview Answers" button |
| 44 | # |
45 | # |
| 45 | ############################################################ |
46 | ############################################################ |
| 46 | |
47 | |
|
|
48 | sub templateName { |
|
|
49 | "problem"; |
|
|
50 | } |
|
|
51 | |
| 47 | sub pre_header_initialize { |
52 | sub pre_header_initialize { |
| 48 | my ($self, $setName, $problemNumber) = @_; |
53 | my ($self, $setName, $problemNumber) = @_; |
| 49 | my $r = $self->{r}; |
54 | my $r = $self->{r}; |
| 50 | my $courseEnv = $self->{ce}; |
55 | my $courseEnv = $self->{ce}; |
| 51 | my $db = $self->{db}; |
56 | my $db = $self->{db}; |
| … | |
… | |
| 204 | recordAnswers => canRecordAnswers($permissionLevel, $set->open_date, $set->due_date, |
209 | recordAnswers => canRecordAnswers($permissionLevel, $set->open_date, $set->due_date, |
| 205 | $problem->max_attempts, $problem->num_correct + $problem->num_incorrect + 1), |
210 | $problem->max_attempts, $problem->num_correct + $problem->num_incorrect + 1), |
| 206 | # attempts=num_correct+num_incorrect+1, as this happens before updating $problem |
211 | # attempts=num_correct+num_incorrect+1, as this happens before updating $problem |
| 207 | checkAnswers => canCheckAnswers($permissionLevel, $set->answer_date), |
212 | checkAnswers => canCheckAnswers($permissionLevel, $set->answer_date), |
| 208 | ); |
213 | ); |
|
|
214 | ######################################################### |
|
|
215 | # more complicated logic for showing check answer button: |
|
|
216 | ######################################################### |
|
|
217 | # checkAnswers button shows up after due date -- once a student can't record anymore |
|
|
218 | # checkAnswers button always shows up when an instructor or TA is acting |
|
|
219 | # as someone else (the $user and $effectiveUserName aren't the same). |
|
|
220 | $can{checkAnswers} = ($can{checkAnswers} && not $can{recordAnswers} ) || |
|
|
221 | ( defined($userName) and defined($effectiveUserName) and |
|
|
222 | ($userName ne $effectiveUserName) |
|
|
223 | ); |
|
|
224 | ######################################################### |
|
|
225 | # more complicated logif for showing "submit answer" button |
|
|
226 | ######################################################### |
|
|
227 | # We hide the submit answer button if someone is acting as a student |
|
|
228 | # This prevents errors where you accidently submit the answer for a student |
|
|
229 | # Not sure whether this a feature or a bug |
| 209 | |
230 | |
|
|
231 | $can{recordAnswers} = ($can{recordAnswers} and not |
|
|
232 | ( defined($userName) and defined($effectiveUserName) and |
|
|
233 | ($userName ne $effectiveUserName) |
|
|
234 | ) |
|
|
235 | ); |
| 210 | # final values for options |
236 | # final values for options |
| 211 | my %will; |
237 | my %will; |
| 212 | foreach (keys %must) { |
238 | foreach (keys %must) { |
| 213 | $will{$_} = $can{$_} && ($want{$_} || $must{$_}); |
239 | $will{$_} = $can{$_} && ($want{$_} || $must{$_}); |
| 214 | } |
240 | } |
| 215 | |
241 | |
| 216 | ##### sticky answers ##### |
242 | ##### sticky answers ##### |
| 217 | |
243 | |
| 218 | if (not $submitAnswers and $will{showOldAnswers}) { |
244 | if (not ($submitAnswers or $previewAnswers or $checkAnswers) and $will{showOldAnswers}) { |
| 219 | # do this only if new answers are NOT being submitted |
245 | # do this only if new answers are NOT being submitted |
| 220 | my %oldAnswers = decodeAnswers($problem->last_answer); |
246 | my %oldAnswers = decodeAnswers($problem->last_answer); |
| 221 | $formFields->{$_} = $oldAnswers{$_} foreach keys %oldAnswers; |
247 | $formFields->{$_} = $oldAnswers{$_} foreach keys %oldAnswers; |
| 222 | } |
248 | } |
| 223 | |
249 | |
| … | |
… | |
| 317 | print CGI::strong("Problems"), CGI::br(); |
343 | print CGI::strong("Problems"), CGI::br(); |
| 318 | |
344 | |
| 319 | my $effectiveUser = $self->{r}->param("effectiveUser"); |
345 | my $effectiveUser = $self->{r}->param("effectiveUser"); |
| 320 | my @problemIDs = $db->listUserProblems($effectiveUser, $setName); |
346 | my @problemIDs = $db->listUserProblems($effectiveUser, $setName); |
| 321 | foreach my $problem (sort { $a <=> $b } @problemIDs) { |
347 | foreach my $problem (sort { $a <=> $b } @problemIDs) { |
| 322 | print CGI::a({-href=>"$root/$courseName/$setName/".$problem."/?" |
348 | print ' '.CGI::a({-href=>"$root/$courseName/$setName/".$problem."/?" |
| 323 | . $self->url_authen_args . "&displayMode=" . $self->{displayMode}}, |
349 | . $self->url_authen_args . "&displayMode=" . $self->{displayMode}}, |
| 324 | "Problem ".$problem), CGI::br(); |
350 | "Problem ".$problem), CGI::br(); |
| 325 | } |
351 | } |
| 326 | |
352 | |
| 327 | return ""; |
353 | return ""; |
| … | |
… | |
| 464 | $scoreRecordedMessage = "Your score was not recorded because this problem has not been built for you."; |
490 | $scoreRecordedMessage = "Your score was not recorded because this problem has not been built for you."; |
| 465 | } |
491 | } |
| 466 | } |
492 | } |
| 467 | |
493 | |
| 468 | # logging student answers |
494 | # logging student answers |
| 469 | my $pastAnswerLog = undef; |
495 | |
| 470 | if (defined( $self->{ce}->{webworkFiles}->{logs}->{'pastAnswerList'} )) { |
496 | my $answer_log = $self->{ce}->{courseFiles}->{logs}->{'answer_log'}; |
| 471 | $pastAnswerLog = $self->{ce}->{webworkFiles}->{logs}->{'pastAnswerList'}; |
497 | if ( defined($answer_log )) { |
| 472 | if ($submitAnswers and defined $pastAnswerLog) { |
498 | if ($submitAnswers ) { |
| 473 | my $answerString = ""; |
499 | my $answerString = ""; |
| 474 | my %answerHash = %{ $pg->{answers} }; |
500 | my %answerHash = %{ $pg->{answers} }; |
| 475 | $answerString = $answerString . $answerHash{$_}->{original_student_ans}."\t" |
501 | $answerString = $answerString . $answerHash{$_}->{original_student_ans}."\t" |
| 476 | foreach (sort keys %answerHash); |
502 | foreach (sort keys %answerHash); |
| 477 | $answerString = '' unless defined($answerString); # insure string is defined. |
503 | $answerString = '' unless defined($answerString); # insure string is defined. |
| 478 | writeLog($self->{ce}, "pastAnswerList", |
504 | writeCourseLog($self->{ce}, "answer_log", |
|
|
505 | join("", |
| 479 | '|'.$problem->user_id. |
506 | '|', $problem->user_id, |
| 480 | '|'.$problem->set_id. |
507 | '|', $problem->set_id, |
| 481 | '|'.$problem->problem_id.'|'."\t". |
508 | '|', $problem->problem_id, |
|
|
509 | '|',"\t", |
| 482 | time()."\t". |
510 | time(),"\t", |
| 483 | $answerString, |
511 | $answerString, |
|
|
512 | ), |
| 484 | ); |
513 | ); |
|
|
514 | |
| 485 | } |
515 | } |
| 486 | } |
516 | } |
| 487 | |
517 | |
| 488 | $WeBWorK::timer0->continue("end answer processing") if $timer0_ON; |
518 | $WeBWorK::timer0->continue("end answer processing") if $timer0_ON; |
| 489 | |
519 | |
| … | |
… | |
| 499 | print CGI::p(CGI::i("Problem saved to: ", $problem->source_file)); |
529 | print CGI::p(CGI::i("Problem saved to: ", $problem->source_file)); |
| 500 | } |
530 | } |
| 501 | } |
531 | } |
| 502 | |
532 | |
| 503 | # attempt summary |
533 | # attempt summary |
| 504 | if ($submitAnswers or $will{showCorrectAnswers}) { |
534 | #FIXME -- the following is a kludge: if showPartialCorrectAnswers is negative don't show anything. |
|
|
535 | # until after the due date |
|
|
536 | if (($pg->{flags}->{showPartialCorrectAnswers}>= 0 and $submitAnswers) or $will{showCorrectAnswers}) { |
| 505 | # print this if user submitted answers OR requested correct answers |
537 | # print this if user submitted answers OR requested correct answers |
|
|
538 | |
| 506 | print $self->attemptResults($pg, $submitAnswers, |
539 | print $self->attemptResults($pg, $submitAnswers, |
| 507 | $will{showCorrectAnswers}, |
540 | $will{showCorrectAnswers}, |
| 508 | $pg->{flags}->{showPartialCorrectAnswers}, 1, 1); |
541 | $pg->{flags}->{showPartialCorrectAnswers}, 1, 1); |
| 509 | } elsif ($checkAnswers) { |
542 | } elsif ($checkAnswers) { |
| 510 | # print this if user previewed answers |
543 | # print this if user previewed answers |
|
|
544 | print "ANSWERS ONLY CHECKED -- ",CGI::br(),"ANSWERS NOT RECORDED", CGI::br(); |
| 511 | print $self->attemptResults($pg, 1, 0, 1, 1, 1); |
545 | print $self->attemptResults($pg, 1, 0, 1, 1, 1); |
| 512 | # show attempt answers |
546 | # show attempt answers |
| 513 | # don't show correct answers |
547 | # don't show correct answers |
| 514 | # show attempt results (correctness) |
548 | # show attempt results (correctness) |
| 515 | # don't show attempt previews |
549 | # don't show attempt previews |
| 516 | } elsif ($previewAnswers) { |
550 | } elsif ($previewAnswers) { |
| 517 | # print this if user previewed answers |
551 | # print this if user previewed answers |
| 518 | print $self->attemptResults($pg, 1, 0, 0, 0, 1); |
552 | print "PREVIEW ONLY -- NOT RECORDED",CGI::br(),$self->attemptResults($pg, 1, 0, 0, 0, 1); |
| 519 | # show attempt answers |
553 | # show attempt answers |
| 520 | # don't show correct answers |
554 | # don't show correct answers |
| 521 | # don't show attempt results (correctness) |
555 | # don't show attempt results (correctness) |
| 522 | # show attempt previews |
556 | # show attempt previews |
| 523 | } |
557 | } |
| … | |
… | |
| 531 | CGI::startform("POST", $r->uri), |
565 | CGI::startform("POST", $r->uri), |
| 532 | $self->hidden_authen_fields, |
566 | $self->hidden_authen_fields, |
| 533 | CGI::p($pg->{body_text}), |
567 | CGI::p($pg->{body_text}), |
| 534 | CGI::p($pg->{result}->{msg} ? CGI::b("Note: ") : "", CGI::i($pg->{result}->{msg})), |
568 | CGI::p($pg->{result}->{msg} ? CGI::b("Note: ") : "", CGI::i($pg->{result}->{msg})), |
| 535 | CGI::p( |
569 | CGI::p( |
|
|
570 | ($can{showCorrectAnswers} |
|
|
571 | ? CGI::checkbox( |
|
|
572 | -name => "showCorrectAnswers", |
|
|
573 | -checked => $will{showCorrectAnswers}, |
|
|
574 | -label => "Show correct answers", |
|
|
575 | ) ." " |
|
|
576 | : "" ), |
|
|
577 | ($can{showHints} |
|
|
578 | ? CGI::checkbox( |
|
|
579 | -name => "showHints", |
|
|
580 | -checked => $will{showHints}, |
|
|
581 | -label => "Show Hints", |
|
|
582 | ) . " " |
|
|
583 | : " " ), |
|
|
584 | ($can{showSolutions} |
|
|
585 | ? CGI::checkbox( |
|
|
586 | -name => "showSolutions", |
|
|
587 | -checked => $will{showSolutions}, |
|
|
588 | -label => "Show Solutions", |
|
|
589 | ) . " " |
|
|
590 | : " " ),CGI::br(), |
|
|
591 | CGI::submit(-name=>"previewAnswers", |
|
|
592 | -label=>"Preview Answers"), |
| 536 | ($can{recordAnswers} |
593 | ($can{recordAnswers} |
| 537 | ? CGI::submit(-name=>"submitAnswers", |
594 | ? CGI::submit(-name=>"submitAnswers", |
| 538 | -label=>"Submit Answers") |
595 | -label=>"Submit Answers") |
| 539 | : ""), |
596 | : ""), |
| 540 | ($can{checkAnswers} |
597 | ( $can{checkAnswers} |
| 541 | ? CGI::submit(-name=>"checkAnswers", |
598 | ? CGI::submit(-name=>"checkAnswers", |
| 542 | -label=>"Check Answers") |
599 | -label=>"Check Answers") |
| 543 | : ""), |
600 | : ""), |
| 544 | CGI::submit(-name=>"previewAnswers", |
|
|
| 545 | -label=>"Preview Answers"), |
|
|
| 546 | ); |
601 | ); |
| 547 | print CGI::end_div(); |
602 | print CGI::end_div(); |
| 548 | |
603 | |
| 549 | print CGI::start_div({class=>"scoreSummary"}); |
604 | print CGI::start_div({class=>"scoreSummary"}); |
| 550 | |
605 | |
| … | |
… | |
| 586 | # save state for viewOptions |
641 | # save state for viewOptions |
| 587 | print CGI::hidden( |
642 | print CGI::hidden( |
| 588 | -name => "showOldAnswers", |
643 | -name => "showOldAnswers", |
| 589 | -value => $will{showOldAnswers} |
644 | -value => $will{showOldAnswers} |
| 590 | ), |
645 | ), |
| 591 | CGI::hidden( |
646 | |
| 592 | -name => "showCorrectAnswers", |
|
|
| 593 | -value => $will{showCorrectAnswers} |
|
|
| 594 | ), |
|
|
| 595 | CGI::hidden( |
|
|
| 596 | -name => "showHints", |
|
|
| 597 | -value => $will{showHints}), |
|
|
| 598 | CGI::hidden( |
|
|
| 599 | -name => "showSolutions", |
|
|
| 600 | -value => $will{showSolutions}, |
|
|
| 601 | ), |
|
|
| 602 | CGI::hidden( |
647 | CGI::hidden( |
| 603 | -name => "displayMode", |
648 | -name => "displayMode", |
| 604 | -value => $self->{displayMode} |
649 | -value => $self->{displayMode} |
| 605 | ); |
650 | ); |
| 606 | |
651 | |
| … | |
… | |
| 614 | |
659 | |
| 615 | print CGI::start_div({class=>"problemFooter"}); |
660 | print CGI::start_div({class=>"problemFooter"}); |
| 616 | |
661 | |
| 617 | # arguments for answer inspection button |
662 | # arguments for answer inspection button |
| 618 | my $prof_url = $ce->{webworkURLs}->{oldProf}; |
663 | my $prof_url = $ce->{webworkURLs}->{oldProf}; |
|
|
664 | my $webworkURL = $ce->{webworkURLs}->{root}; |
| 619 | my $cgi_url = $prof_url; |
665 | my $cgi_url = $prof_url; |
| 620 | $cgi_url=~ s|/[^/]*$||; # clip profLogin.pl |
666 | $cgi_url=~ s|/[^/]*$||; # clip profLogin.pl |
| 621 | my $authen_args = $self->url_authen_args(); |
667 | my $authen_args = $self->url_authen_args(); |
| 622 | my $showPastAnswersURL = "$cgi_url/showPastAnswers.pl"; |
668 | my $showPastAnswersURL = "$webworkURL/$courseName/instructor/show_answers/"; |
| 623 | |
669 | |
| 624 | # print answer inspection button |
670 | # print answer inspection button |
| 625 | if ($self->{permissionLevel} > 0) { |
671 | if ($self->{permissionLevel} > 0) { |
| 626 | print "\n", |
672 | print "\n", |
| 627 | CGI::start_form(-method=>"POST",-action=>$showPastAnswersURL,-target=>"information"),"\n", |
673 | CGI::start_form(-method=>"POST",-action=>$showPastAnswersURL,-target=>"information"),"\n", |
| 628 | $self->hidden_authen_fields,"\n", |
674 | $self->hidden_authen_fields,"\n", |
| 629 | CGI::hidden(-name => 'course', -value=>$courseName), "\n", |
675 | CGI::hidden(-name => 'course', -value=>$courseName), "\n", |
| 630 | CGI::hidden(-name => 'probNum', -value=>$problem->problem_id), "\n", |
676 | CGI::hidden(-name => 'problemNumber', -value=>$problem->problem_id), "\n", |
| 631 | CGI::hidden(-name => 'setNum', -value=>$problem->set_id), "\n", |
677 | CGI::hidden(-name => 'setName', -value=>$problem->set_id), "\n", |
| 632 | CGI::hidden(-name => 'User', -value=>$problem->user_id), "\n", |
678 | CGI::hidden(-name => 'studentUser', -value=>$problem->user_id), "\n", |
| 633 | CGI::p( {-align=>"left"}, |
679 | CGI::p( {-align=>"left"}, |
| 634 | CGI::submit(-name => 'action', -value=>'Show Past Answers') |
680 | CGI::submit(-name => 'action', -value=>'Show Past Answers') |
| 635 | ), "\n", |
681 | ), "\n", |
| 636 | CGI::endform(); |
682 | CGI::endform(); |
| 637 | } |
683 | } |
| … | |
… | |
| 654 | CGI::hidden("showOldAnswers", $will{showOldAnswers}),"\n", |
700 | CGI::hidden("showOldAnswers", $will{showOldAnswers}),"\n", |
| 655 | CGI::hidden("showCorrectAnswers", $will{showCorrectAnswers}),"\n", |
701 | CGI::hidden("showCorrectAnswers", $will{showCorrectAnswers}),"\n", |
| 656 | CGI::hidden("showHints", $will{showHints}),"\n", |
702 | CGI::hidden("showHints", $will{showHints}),"\n", |
| 657 | CGI::hidden("showSolutions", $will{showSolutions}),"\n", |
703 | CGI::hidden("showSolutions", $will{showSolutions}),"\n", |
| 658 | CGI::p({-align=>"left"}, |
704 | CGI::p({-align=>"left"}, |
| 659 | CGI::submit(-name=>"feedbackForm", -label=>"Contact instructor") |
705 | CGI::submit(-name=>"feedbackForm", -label=>"Email instructor") |
| 660 | ), |
706 | ), |
| 661 | CGI::endform(),"\n"; |
707 | CGI::endform(),"\n"; |
| 662 | |
708 | |
| 663 | # FIXME print editor link |
709 | # FIXME print editor link |
| 664 | # print editor link if the user is an instructor AND the file is not in temporary editing mode |
710 | # print editor link if the user is an instructor AND the file is not in temporary editing mode |
| … | |
… | |
| 702 | my $showAttemptAnswers = shift; |
748 | my $showAttemptAnswers = shift; |
| 703 | my $showCorrectAnswers = shift; |
749 | my $showCorrectAnswers = shift; |
| 704 | my $showAttemptResults = $showAttemptAnswers && shift; |
750 | my $showAttemptResults = $showAttemptAnswers && shift; |
| 705 | my $showSummary = shift; |
751 | my $showSummary = shift; |
| 706 | my $showAttemptPreview = shift || 0; |
752 | my $showAttemptPreview = shift || 0; |
|
|
753 | my $ce = $self->{ce}; |
| 707 | my $problemResult = $pg->{result}; # the overall result of the problem |
754 | my $problemResult = $pg->{result}; # the overall result of the problem |
| 708 | my @answerNames = @{ $pg->{flags}->{ANSWER_ENTRY_ORDER} }; |
755 | my @answerNames = @{ $pg->{flags}->{ANSWER_ENTRY_ORDER} }; |
| 709 | |
756 | |
| 710 | my $showMessages = $showAttemptAnswers && grep { $pg->{answers}->{$_}->{ans_message} } @answerNames; |
757 | my $showMessages = $showAttemptAnswers && grep { $pg->{answers}->{$_}->{ans_message} } @answerNames; |
|
|
758 | |
|
|
759 | my $basename = "equation-" . $self->{set}->psvn. "." . $self->{problem}->problem_id . "-preview"; |
|
|
760 | my $imgGen = WeBWorK::PG::ImageGenerator->new( |
|
|
761 | tempDir => $ce->{webworkDirs}->{tmp}, |
|
|
762 | dir => $ce->{courseDirs}->{html_temp}, |
|
|
763 | url => $ce->{courseURLs}->{html_temp}, |
|
|
764 | basename => $basename, |
|
|
765 | latex => $ce->{externalPrograms}->{latex}, |
|
|
766 | dvipng => $ce->{externalPrograms}->{dvipng}, |
|
|
767 | ); |
| 711 | |
768 | |
| 712 | my $header; |
769 | my $header; |
| 713 | #$header .= CGI::th("Part"); |
770 | #$header .= CGI::th("Part"); |
| 714 | $header .= $showAttemptAnswers ? CGI::th("Entered") : ""; |
771 | $header .= $showAttemptAnswers ? CGI::th("Entered") : ""; |
| 715 | $header .= $showAttemptPreview ? CGI::th("Answer Preview") : ""; |
772 | $header .= $showAttemptPreview ? CGI::th("Answer Preview") : ""; |
| … | |
… | |
| 720 | my $numCorrect; |
777 | my $numCorrect; |
| 721 | foreach my $name (@answerNames) { |
778 | foreach my $name (@answerNames) { |
| 722 | my $answerResult = $pg->{answers}->{$name}; |
779 | my $answerResult = $pg->{answers}->{$name}; |
| 723 | my $studentAnswer = $answerResult->{student_ans}; # original_student_ans |
780 | my $studentAnswer = $answerResult->{student_ans}; # original_student_ans |
| 724 | my $preview = ($showAttemptPreview |
781 | my $preview = ($showAttemptPreview |
| 725 | ? $self->previewAnswer($answerResult) |
782 | ? $self->previewAnswer($answerResult, $imgGen) |
| 726 | : ""); |
783 | : ""); |
| 727 | my $correctAnswer = $answerResult->{correct_ans}; |
784 | my $correctAnswer = $answerResult->{correct_ans}; |
| 728 | my $answerScore = $answerResult->{score}; |
785 | my $answerScore = $answerResult->{score}; |
| 729 | my $answerMessage = $showMessages ? $answerResult->{ans_message} : ""; |
786 | my $answerMessage = $showMessages ? $answerResult->{ans_message} : ""; |
| 730 | |
787 | #FIXME --Can we be sure that $answerScore is an integer-- could the problem give partial credit? |
| 731 | $numCorrect += $answerScore > 0; |
788 | $numCorrect += $answerScore > 0; |
| 732 | my $resultString = $answerScore ? "correct" : "incorrect"; |
789 | my $resultString = $answerScore ? "correct" : "incorrect"; |
| 733 | |
790 | |
| 734 | # get rid of the goofy prefix on the answer names (supposedly, the format |
791 | # get rid of the goofy prefix on the answer names (supposedly, the format |
| 735 | # of the answer names is changeable. this only fixes it for "AnSwEr" |
792 | # of the answer names is changeable. this only fixes it for "AnSwEr" |
| 736 | $name =~ s/^AnSwEr//; |
793 | #$name =~ s/^AnSwEr//; |
| 737 | |
794 | |
| 738 | my $row; |
795 | my $row; |
| 739 | #$row .= CGI::td($name); |
796 | #$row .= CGI::td($name); |
| 740 | $row .= $showAttemptAnswers ? CGI::td(nbsp($studentAnswer)) : ""; |
797 | $row .= $showAttemptAnswers ? CGI::td(nbsp($studentAnswer)) : ""; |
| 741 | $row .= $showAttemptPreview ? CGI::td(nbsp($preview)) : ""; |
798 | $row .= $showAttemptPreview ? CGI::td(nbsp($preview)) : ""; |
| … | |
… | |
| 743 | $row .= $showAttemptResults ? CGI::td(nbsp($resultString)) : ""; |
800 | $row .= $showAttemptResults ? CGI::td(nbsp($resultString)) : ""; |
| 744 | $row .= $answerMessage ? CGI::td(nbsp($answerMessage)) : ""; |
801 | $row .= $answerMessage ? CGI::td(nbsp($answerMessage)) : ""; |
| 745 | push @tableRows, $row; |
802 | push @tableRows, $row; |
| 746 | } |
803 | } |
| 747 | |
804 | |
|
|
805 | # render equation images |
|
|
806 | $imgGen->render(refresh => 1); |
|
|
807 | |
| 748 | my $numIncorrectNoun = scalar @answerNames == 1 ? "question" : "questions"; |
808 | # my $numIncorrectNoun = scalar @answerNames == 1 ? "question" : "questions"; |
| 749 | my $scorePercent = sprintf("%.0f%%", $problemResult->{score} * 100); |
809 | my $scorePercent = sprintf("%.0f%%", $problemResult->{score} * 100); |
|
|
810 | # FIXME -- I left the old code in in case we have to back out. |
| 750 | my $summary = "On this attempt, you answered $numCorrect out of " |
811 | # my $summary = "On this attempt, you answered $numCorrect out of " |
| 751 | . scalar @answerNames . " $numIncorrectNoun correct, for a score of $scorePercent."; |
812 | # . scalar @answerNames . " $numIncorrectNoun correct, for a score of $scorePercent."; |
|
|
813 | my $summary = ""; |
|
|
814 | if (scalar @answerNames == 1) { |
|
|
815 | if ($numCorrect == scalar @answerNames) { |
|
|
816 | $summary .= "The above answer is correct."; |
|
|
817 | } else { |
|
|
818 | $summary .= "The above answer is NOT correct."; |
|
|
819 | } |
|
|
820 | } else { |
|
|
821 | if ($numCorrect == scalar @answerNames) { |
|
|
822 | $summary .= "All of the above answers are correct."; |
|
|
823 | } else { |
|
|
824 | $summary .= "At least one of the above answers is NOT correct."; |
|
|
825 | } |
|
|
826 | } |
|
|
827 | #FIXME there must be a better way to force refresh. |
|
|
828 | my $refresh_warning = 'Hold down shift and click "refresh" or "reload" to update answer preview images.'; |
| 752 | return CGI::table({-class=>"attemptResults"}, CGI::Tr(\@tableRows)) . ($showSummary ? CGI::p($summary) : ""); |
829 | return CGI::table({-class=>"attemptResults"}, CGI::Tr(\@tableRows)) . |
|
|
830 | CGI::div({style=>'color:red; font-size:10pt'},$refresh_warning) . |
|
|
831 | ($showSummary ? CGI::p({class=>'emphasis'},$summary) : ""); |
| 753 | } |
832 | } |
| 754 | sub nbsp { |
833 | sub nbsp { |
| 755 | my $str = shift; |
834 | my $str = shift; |
| 756 | ($str) ? $str : ' '; # returns non-breaking space for empty strings |
835 | ($str =~/\S/) ? $str : ' ' ; # returns non-breaking space for empty strings |
|
|
836 | # tricky cases: $str =0; |
|
|
837 | # $str is a complex number |
| 757 | } |
838 | } |
| 758 | sub viewOptions($) { |
839 | sub viewOptions($) { |
| 759 | my $self = shift; |
840 | my $self = shift; |
| 760 | my $displayMode = $self->{displayMode}; |
841 | my $displayMode = $self->{displayMode}; |
| 761 | my %must = %{ $self->{must} }; |
842 | my %must = %{ $self->{must} }; |
| … | |
… | |
| 768 | CGI::checkbox( |
849 | CGI::checkbox( |
| 769 | -name => "showOldAnswers", |
850 | -name => "showOldAnswers", |
| 770 | -checked => $will{showOldAnswers}, |
851 | -checked => $will{showOldAnswers}, |
| 771 | -label => "Saved answers", |
852 | -label => "Saved answers", |
| 772 | ), " ".CGI::br(); |
853 | ), " ".CGI::br(); |
| 773 | $can{showCorrectAnswers} and $optionLine .= join "", |
854 | |
| 774 | CGI::checkbox( |
|
|
| 775 | -name => "showCorrectAnswers", |
|
|
| 776 | -checked => $will{showCorrectAnswers}, |
|
|
| 777 | -label => "Correct answers", |
|
|
| 778 | ), " ".CGI::br(); |
|
|
| 779 | $can{showHints} and $optionLine .= join "", |
|
|
| 780 | CGI::checkbox( |
|
|
| 781 | -name => "showHints", |
|
|
| 782 | -checked => $will{showHints}, |
|
|
| 783 | -label => "Hints", |
|
|
| 784 | ), " ".CGI::br(); |
|
|
| 785 | $can{showSolutions} and $optionLine .= join "", |
|
|
| 786 | CGI::checkbox( |
|
|
| 787 | -name => "showSolutions", |
|
|
| 788 | -checked => $will{showSolutions}, |
|
|
| 789 | -label => "Solutions", |
|
|
| 790 | ), " ".CGI::br(); |
|
|
| 791 | $optionLine and $optionLine .= join "", CGI::br(); |
855 | $optionLine and $optionLine .= join "", CGI::br(); |
| 792 | |
856 | |
| 793 | return CGI::div({-style=>"border: thin groove; padding: 1ex; margin: 2ex align: left"}, |
857 | return CGI::div({-style=>"border: thin groove; padding: 1ex; margin: 2ex align: left"}, |
| 794 | "View equations as: ".CGI::br(), |
858 | "View equations as: ".CGI::br(), |
| 795 | CGI::radio_group( |
859 | CGI::radio_group( |
| … | |
… | |
| 807 | CGI::submit(-name=>"redisplay", -label=>"Save Options"), |
871 | CGI::submit(-name=>"redisplay", -label=>"Save Options"), |
| 808 | ); |
872 | ); |
| 809 | } |
873 | } |
| 810 | |
874 | |
| 811 | sub previewAnswer($$) { |
875 | sub previewAnswer($$) { |
| 812 | my ($self, $answerResult) = @_; |
876 | my ($self, $answerResult, $imgGen) = @_; |
| 813 | my $ce = $self->{ce}; |
877 | my $ce = $self->{ce}; |
| 814 | my $effectiveUser = $self->{effectiveUser}; |
878 | my $effectiveUser = $self->{effectiveUser}; |
| 815 | my $set = $self->{set}; |
879 | my $set = $self->{set}; |
| 816 | my $problem = $self->{problem}; |
880 | my $problem = $self->{problem}; |
| 817 | my $displayMode = $self->{displayMode}; |
881 | my $displayMode = $self->{displayMode}; |
| … | |
… | |
| 838 | if ($?) { |
902 | if ($?) { |
| 839 | return "<b>[tth failed: $? $@]</b>"; |
903 | return "<b>[tth failed: $? $@]</b>"; |
| 840 | } |
904 | } |
| 841 | return $result; |
905 | return $result; |
| 842 | } elsif ($displayMode eq "images") { |
906 | } elsif ($displayMode eq "images") { |
| 843 | # how are we going to name this? |
907 | ## how are we going to name this? |
| 844 | my $targetPathCommon = "/m2i/" |
908 | #my $targetPathCommon = "/m2i/" |
| 845 | . $effectiveUser->user_id . "." |
909 | # . $effectiveUser->user_id . "." |
| 846 | . $set->set_id . "." |
910 | # . $set->set_id . "." |
| 847 | . $problem->problem_id . "." |
911 | # . $problem->problem_id . "." |
| 848 | . $answerResult->{ans_name} . ".png"; |
912 | # . $answerResult->{ans_name} . ".png"; |
| 849 | |
913 | # |
| 850 | # figure out where to put things |
914 | ## figure out where to put things |
| 851 | my $wd = makeTempDirectory($ce->{courseDirs}->{html_temp}, "webwork-dvipng"); |
915 | #my $wd = makeTempDirectory($ce->{courseDirs}->{html_temp}, "webwork-dvipng"); |
| 852 | my $latex = $ce->{externalPrograms}->{latex}; |
916 | #my $latex = $ce->{externalPrograms}->{latex}; |
| 853 | my $dvipng = $ce->{externalPrograms}->{dvipng}; |
917 | #my $dvipng = $ce->{externalPrograms}->{dvipng}; |
| 854 | my $targetPath = $ce->{courseDirs}->{html_temp} . $targetPathCommon; |
918 | #my $targetPath = $ce->{courseDirs}->{html_temp} . $targetPathCommon; |
| 855 | # should use surePathToTmpFile, but we have to |
919 | # # should use surePathToTmpFile, but we have to |
| 856 | # isolate it from the problem enivronment first |
920 | # # isolate it from the problem enivronment first |
| 857 | my $targetURL = $ce->{courseURLs}->{html_temp} . $targetPathCommon; |
921 | #my $targetURL = $ce->{courseURLs}->{html_temp} . $targetPathCommon; |
| 858 | |
922 | # |
| 859 | # call dvipng to generate a preview |
923 | ## call dvipng to generate a preview |
| 860 | dvipng($wd, $latex, $dvipng, $tex, $targetPath); |
924 | #dvipng($wd, $latex, $dvipng, $tex, $targetPath); |
| 861 | rmtree($wd, 0, 0); |
925 | #rmtree($wd, 0, 0); |
| 862 | if (-e $targetPath) { |
926 | #if (-e $targetPath) { |
| 863 | return "<img src=\"$targetURL\" alt=\"$tex\" />"; |
927 | # return "<img src=\"$targetURL\" alt=\"$tex\" />"; |
| 864 | } else { |
928 | #} else { |
| 865 | return "<b>[math2img failed]</b>"; |
929 | # return "<b>[math2img failed]</b>"; |
| 866 | } |
930 | #} |
|
|
931 | $imgGen->add($answerResult->{preview_latex_string}); |
|
|
932 | |
| 867 | } |
933 | } |
| 868 | } |
934 | } |
| 869 | |
935 | |
| 870 | ##### logging subroutine #### |
936 | ##### logging subroutine #### |
| 871 | |
937 | |