From d37576d8a63c62182b5033bcb37f6e007e8212dd Mon Sep 17 00:00:00 2001 From: Glenn Rice Date: Mon, 18 Sep 2023 08:35:23 -0500 Subject: [PATCH] TODO --- lib/FormatRenderedProblem.pm | 24 +- lib/WeBWorK/ContentGenerator/GatewayQuiz.pm | 84 ++--- lib/WeBWorK/ContentGenerator/Problem.pm | 103 ++---- lib/WeBWorK/HTML/AttemptsTable.pm | 326 ++++-------------- lib/WeBWorK/Utils/Rendering.pm | 9 + lib/WebworkWebservice/RenderProblem.pm | 10 +- .../ContentGenerator/GatewayQuiz.html.ep | 19 +- 7 files changed, 158 insertions(+), 417 deletions(-) diff --git a/lib/FormatRenderedProblem.pm b/lib/FormatRenderedProblem.pm index 005b9c2c37..0b75d3538a 100644 --- a/lib/FormatRenderedProblem.pm +++ b/lib/FormatRenderedProblem.pm @@ -154,24 +154,14 @@ sub formatRenderedProblem { my $answerTemplate = ''; # Do not produce an AttemptsTable when we had a rendering error. - if (!$renderErrorOccurred) { - my $tbl = WeBWorK::HTML::AttemptsTable->new( + if (!$renderErrorOccurred && !$previewMode) { + $answerTemplate = WeBWorK::HTML::AttemptsTable->new( $rh_result->{answers} // {}, $ws->c, - answersSubmitted => $ws->{inputs_ref}{answersSubmitted} // 0, - answerOrder => $rh_result->{flags}{ANSWER_ENTRY_ORDER} // [], - displayMode => $displayMode, - showAnswerNumbers => $showAnswerNumbers, - ce => $ce, - showAttemptPreviews => $previewMode || $submitMode || $showCorrectMode, - showAttemptResults => $submitMode || $showCorrectMode, - showCorrectAnswers => $showCorrectMode, - showMessages => $previewMode || $submitMode || $showCorrectMode, - showSummary => (($showSummary && ($submitMode || $showCorrectMode)) // 0) ? 1 : 0, - maketext => WeBWorK::Localize::getLoc($formLanguage), - summary => $problemResult->{summary} // '', # can be set by problem grader - ); - $answerTemplate = $tbl->answerTemplate; - $tbl->imgGen->render(refresh => 1) if $tbl->displayMode eq 'images'; + answersSubmitted => $ws->{inputs_ref}{answersSubmitted} // 0, + answerOrder => $rh_result->{flags}{ANSWER_ENTRY_ORDER} // [], + showSummary => (($showSummary && ($submitMode || $showCorrectMode)) // 0) ? 1 : 0, + summary => $problemResult->{summary} // '' # can be set by problem grader + )->answerTemplate; } # Answer hash in XML format used by the PTX format. diff --git a/lib/WeBWorK/ContentGenerator/GatewayQuiz.pm b/lib/WeBWorK/ContentGenerator/GatewayQuiz.pm index bf49e59896..0c67d26e9c 100644 --- a/lib/WeBWorK/ContentGenerator/GatewayQuiz.pm +++ b/lib/WeBWorK/ContentGenerator/GatewayQuiz.pm @@ -238,42 +238,17 @@ sub can_useMathQuill ($c) { } # Output utility -sub attemptResults ($c, $pg, $showCorrectAnswers, $showAttemptResults, $showSummary) { +sub attemptResults ($c, $pg, $showSummary) { my $ce = $c->ce; - # Create AttemptsTable object - my $tbl = WeBWorK::HTML::AttemptsTable->new( + return WeBWorK::HTML::AttemptsTable->new( $pg->{answers}, $c, - answersSubmitted => 1, - answerOrder => $pg->{flags}{ANSWER_ENTRY_ORDER}, - displayMode => $c->{displayMode}, - showHeadline => 0, - showAnswerNumbers => 0, - showAttemptAnswers => $ce->{pg}{options}{showEvaluatedAnswers}, - showAttemptPreviews => 1, - showAttemptResults => $showAttemptResults, - showCorrectAnswers => $showCorrectAnswers, - showMessages => 1, - showSummary => $showSummary, - imgGen => WeBWorK::PG::ImageGenerator->new( - tempDir => $ce->{webworkDirs}{tmp}, - latex => $ce->{externalPrograms}{latex}, - dvipng => $ce->{externalPrograms}{dvipng}, - useCache => 1, - cacheDir => $ce->{webworkDirs}{equationCache}, - cacheURL => $ce->{webworkURLs}{equationCache}, - cacheDB => $ce->{webworkFiles}{equationCacheDB}, - useMarkers => 1, - dvipng_align => $ce->{pg}{displayModeOptions}{images}{dvipng_align}, - dvipng_depth_db => $ce->{pg}{displayModeOptions}{images}{dvipng_depth_db}, - ), - ); - - my $answerTemplate = $tbl->answerTemplate; - $tbl->imgGen->render(body_text => $answerTemplate) if $tbl->displayMode eq 'images'; - - return $answerTemplate; + answersSubmitted => 1, + answerOrder => $pg->{flags}{ANSWER_ENTRY_ORDER}, + showHeadline => 0, + showSummary => $showSummary, + )->answerTemplate; } sub get_instructor_comment ($c, $problem) { @@ -1482,18 +1457,6 @@ sub warningMessage ($c) { # hash of parameters from the input form that need to be passed to the translator, and $mergedProblem # is what we'd expect. async sub getProblemHTML ($c, $effectiveUser, $set, $formFields, $mergedProblem) { - my $setID = $set->set_id; - my $setVersionNumber = $set->version_id; - - # Figure out solutions are allowed and call renderPG accordingly. - my $showCorrectAnswers = $c->{will}{showCorrectAnswers}; - my $showHints = $c->{will}{showHints}; - my $showSolutions = $c->{will}{showSolutions}; - my $processAnswers = $c->{will}{checkAnswers}; - - # FIXME: I'm not sure that problem_id is what we want here. - my $problemNumber = $mergedProblem->problem_id; - my $pg = await renderPG( $c, $effectiveUser, @@ -1502,17 +1465,26 @@ async sub getProblemHTML ($c, $effectiveUser, $set, $formFields, $mergedProblem) $set->psvn, $formFields, { - displayMode => $c->{displayMode}, - showHints => $showHints, - showSolutions => $showSolutions, - refreshMath2img => $showHints || $showSolutions, - processAnswers => 1, - QUIZ_PREFIX => 'Q' . sprintf('%04d', $problemNumber) . '_', - useMathQuill => $c->{will}{useMathQuill}, - useMathView => $c->{will}{useMathView}, - forceScaffoldsOpen => 1, - isInstructor => $c->authz->hasPermissions($c->{userID}, 'view_answers'), - debuggingOptions => getTranslatorDebuggingOptions($c->authz, $c->{userID}) + displayMode => $c->{displayMode}, + showHints => $c->{will}{showHints}, + showSolutions => $c->{will}{showSolutions}, + refreshMath2img => $c->{will}{showHints} || $c->{will}{showSolutions}, + processAnswers => 1, + QUIZ_PREFIX => 'Q' . sprintf('%04d', $mergedProblem->problem_id) . '_', + useMathQuill => $c->{will}{useMathQuill}, + useMathView => $c->{will}{useMathView}, + forceScaffoldsOpen => 1, + isInstructor => $c->authz->hasPermissions($c->{userID}, 'view_answers'), + showFeedback => $c->{submitAnswers} || $c->{previewAnswers} || $c->{will}{checkAnswers}, + showAttemptAnswers => $c->ce->{pg}{options}{showEvaluatedAnswers}, + showAttemptPreviews => 1, + showAttemptResults => !$c->{previewAnswers} && $c->{can}{showProblemScores}, + forceShowAttemptResults => $c->{will}{showProblemGrader}, + showMessages => 1, + showCorrectAnswers => ($c->{submitAnswers} || $c->{will}{checkAnswers} || $c->{will}{showProblemGrader}) + ? $c->{will}{showCorrectAnswers} + : 0, + debuggingOptions => getTranslatorDebuggingOptions($c->authz, $c->{userID}) }, ); @@ -1523,7 +1495,7 @@ async sub getProblemHTML ($c, $effectiveUser, $set, $formFields, $mergedProblem) if ($pg->{flags}{error_flag}) { push @{ $c->{errors} }, { - set => "$setID,v$setVersionNumber", + set => $set->set_id . ',v' . $set->version_id, problem => $mergedProblem->problem_id, message => $pg->{errors}, context => $pg->{body_text}, diff --git a/lib/WeBWorK/ContentGenerator/Problem.pm b/lib/WeBWorK/ContentGenerator/Problem.pm index 3cbf75816b..85aeb35dff 100644 --- a/lib/WeBWorK/ContentGenerator/Problem.pm +++ b/lib/WeBWorK/ContentGenerator/Problem.pm @@ -246,42 +246,16 @@ sub can_showMeAnother ($c, $user, $effectiveUser, $set, $problem, $submitAnswers return 0; } -sub attemptResults ($c, $pg, $showCorrectAnswers, $showAttemptResults, $showSummary) { +sub attemptResults ($c, $pg, $showSummary) { my $ce = $c->ce; - # Create AttemptsTable object - my $tbl = WeBWorK::HTML::AttemptsTable->new( + return WeBWorK::HTML::AttemptsTable->new( $pg->{answers}, $c, - answersSubmitted => 1, - answerOrder => $pg->{flags}{ANSWER_ENTRY_ORDER}, - displayMode => $c->{displayMode}, - showAnswerNumbers => 0, - showAttemptAnswers => $ce->{pg}{options}{showEvaluatedAnswers}, - showAttemptPreviews => 1, - showAttemptResults => $showAttemptResults, - showCorrectAnswers => $showCorrectAnswers, - showMessages => 1, - showSummary => $showSummary, - imgGen => WeBWorK::PG::ImageGenerator->new( - tempDir => $ce->{webworkDirs}{tmp}, - latex => $ce->{externalPrograms}{latex}, - dvipng => $ce->{externalPrograms}{dvipng}, - useCache => 1, - cacheDir => $ce->{webworkDirs}{equationCache}, - cacheURL => $ce->{webworkURLs}{equationCache}, - cacheDB => $ce->{webworkFiles}{equationCacheDB}, - useMarkers => 1, - dvipng_align => $ce->{pg}{displayModeOptions}{images}{dvipng_align}, - dvipng_depth_db => $ce->{pg}{displayModeOptions}{images}{dvipng_depth_db}, - ), - ); - - # Render equation images - my $answerTemplate = $tbl->answerTemplate; - $tbl->imgGen->render(body_text => \$answerTemplate) if $tbl->displayMode eq 'images'; - - return $answerTemplate; + answersSubmitted => 1, + answerOrder => $pg->{flags}{ANSWER_ENTRY_ORDER}, + showSummary => $showSummary, + )->answerTemplate; } async sub pre_header_initialize ($c) { @@ -451,6 +425,7 @@ async sub pre_header_initialize ($c) { { $c->{submitAnswers} = 0; $c->{resubmitDetected} = 1; + delete $formFields->{submitAnswers}; } $c->{displayMode} = $displayMode; @@ -581,6 +556,24 @@ async sub pre_header_initialize ($c) { # Final values for options my %will = map { $_ => $can{$_} && ($want{$_} || $must{$_}) } keys %must; + if ($prEnabled && $problem->{prCount} >= $rerandomizePeriod && !after($c->{set}->due_date, $c->submitTime)) { + $showMeAnother{active} = 0; + $must{requestNewSeed} = 1; + $can{requestNewSeed} = 1; + $want{requestNewSeed} = 1; + $will{requestNewSeed} = 1; + $c->{showCorrectOnRandomize} = $ce->{pg}{options}{showCorrectOnRandomize}; + # If this happens, it means that the page was refreshed. So prevent the answers from + # being recorded and the number of attempts from being increased. + if ($problem->{prCount} > $rerandomizePeriod) { + $c->{resubmitDetected} = 1; + $must{recordAnswers} = 0; + $can{recordAnswers} = 0; + $want{recordAnswers} = 0; + $will{recordAnswers} = 0; + } + } + # Sticky answers if (!($c->{submitAnswers} || $previewAnswers || $checkAnswers) && $will{showOldAnswers}) { my %oldAnswers = decodeAnswers($problem->last_answer); @@ -615,7 +608,16 @@ async sub pre_header_initialize ($c) { useMathView => $will{useMathView}, forceScaffoldsOpen => 0, isInstructor => $authz->hasPermissions($userID, 'view_answers'), - debuggingOptions => getTranslatorDebuggingOptions($authz, $userID) + showFeedback => $c->{submitAnswers} || $c->{previewAnswers}, + showAttemptAnswers => $ce->{pg}{options}{showEvaluatedAnswers}, + showAttemptPreviews => 1, + showAttemptResults => $c->{submitAnswers}, + forceShowAttemptResults => $will{checkAnswers} || $will{showProblemGrader}, + showMessages => 1, + showCorrectAnswers => $c->{submitAnswers} ? ($c->{showCorrectOnRandomize} // $will{showCorrectAnswers}) + : $will{checkAnswers} || $will{showProblemGrader} ? $will{showCorrectAnswers} + : 0, + debuggingOptions => getTranslatorDebuggingOptions($authz, $userID) } ); @@ -630,24 +632,6 @@ async sub pre_header_initialize ($c) { id => 'num_attempts' ); - if ($prEnabled && $problem->{prCount} >= $rerandomizePeriod && !after($c->{set}->due_date, $c->submitTime)) { - $showMeAnother{active} = 0; - $must{requestNewSeed} = 1; - $can{requestNewSeed} = 1; - $want{requestNewSeed} = 1; - $will{requestNewSeed} = 1; - $c->{showCorrectOnRandomize} = $ce->{pg}{options}{showCorrectOnRandomize}; - # If this happens, it means that the page was refreshed. So prevent the answers from - # being recorded and the number of attempts from being increased. - if ($problem->{prCount} > $rerandomizePeriod) { - $c->{resubmitDetected} = 1; - $must{recordAnswers} = 0; - $can{recordAnswers} = 0; - $want{recordAnswers} = 0; - $will{recordAnswers} = 0; - } - } - # Update and fix hint/solution options after PG processing $can{showHints} &&= $pg->{flags}{hintExists}; $can{showSolutions} &&= $pg->{flags}{solutionExists}; @@ -1484,18 +1468,8 @@ sub output_summary ($c) { my $output = $c->c; # Attempt summary - if (defined $pg->{flags}{showPartialCorrectAnswers} - && $pg->{flags}{showPartialCorrectAnswers} >= 0 - && $c->{submitAnswers}) - { - push( - @$output, - $c->attemptResults( - $pg, - $c->{showCorrectOnRandomize} // $will{showCorrectAnswers}, - $pg->{flags}{showPartialCorrectAnswers}, 1 - ) - ); + if ($c->{submitAnswers}) { + push(@$output, $c->attemptResults($pg, 1)); } elsif ($will{checkAnswers} || $c->{will}{showProblemGrader}) { push( @$output, @@ -1504,7 +1478,7 @@ sub output_summary ($c) { class => 'ResultsWithError d-inline-block mb-3', $c->maketext('ANSWERS ONLY CHECKED -- ANSWERS NOT RECORDED') ), - $c->attemptResults($pg, $will{showCorrectAnswers}, 1, 1) + $c->attemptResults($pg, 1) ); } elsif ($c->{previewAnswers}) { push( @@ -1514,7 +1488,6 @@ sub output_summary ($c) { class => 'ResultsWithError d-inline-block mb-3', $c->maketext('PREVIEW ONLY -- ANSWERS NOT RECORDED') ), - $c->attemptResults($pg, 0, 0, 0) ); } diff --git a/lib/WeBWorK/HTML/AttemptsTable.pm b/lib/WeBWorK/HTML/AttemptsTable.pm index 15103e0638..12cc8ec7fe 100644 --- a/lib/WeBWorK/HTML/AttemptsTable.pm +++ b/lib/WeBWorK/HTML/AttemptsTable.pm @@ -23,19 +23,9 @@ $answers, answersSubmitted => 1, answerOrder => $pg->{flags}{ANSWER_ENTRY_ORDER}, - displayMode => 'MathJax', - showAnswerNumbers => 0, - showAttemptAnswers => $showAttemptAnswers && $showEvaluatedAnswers, - showAttemptPreviews => $showAttemptPreview, - showAttemptResults => $showAttemptResults, - showCorrectAnswers => $showCorrectAnswers, - showMessages => $showAttemptAnswers, # internally checks for messages showSummary => $showSummary, - imgGen => $imgGen, # not needed if ce is present , - ce => '', # not needed if $imgGen is present maketext => WeBWorK::Localize::getLoc("en"), ); - $tbl->{imgGen}->render(refresh => 1) if $tbl->displayMode eq 'images'; my $answerTemplate = $tbl->answerTemplate; @@ -50,17 +40,8 @@ answer to a WeBWorK problem. It is used in Problem.pm, OpaqueServer.pm, standAl $answers, answersSubmitted => 1, answerOrder => $pg->{flags}{ANSWER_ENTRY_ORDER}, - displayMode => 'MathJax', showHeadline => 1, - showAnswerNumbers => 0, - showAttemptAnswers => $showAttemptAnswers && $showEvaluatedAnswers, - showAttemptPreviews => $showAttemptPreview, - showAttemptResults => $showAttemptResults, - showCorrectAnswers => $showCorrectAnswers, - showMessages => $showAttemptAnswers, # internally checks for messages showSummary => $showSummary, - imgGen => $imgGen, # not needed if ce is present , - ce => '', # not needed if $imgGen is present maketext => WeBWorK::Localize::getLoc("en"), summary =>'', ); @@ -68,26 +49,13 @@ answer to a WeBWorK problem. It is used in Problem.pm, OpaqueServer.pm, standAl $answers -- a hash of student answers e.g. $pg->{answers} answersSubmitted if 0 then then the attemptsTable is not displayed (???) answerOrder -- an array indicating the order the answers appear on the page. - displayMode 'MathJax' and 'images' are the most common showHeadline Show the header line 'Results for this submission' - showAnswerNumbers, showAttemptAnswers, showAttemptPreviews,showAttemptResults, - showCorrectAnswers and showMessages control the display of each column in the table. - - attemptAnswers the student's typed in answer (possibly simplified numerically) - attemptPreview the student's answer after typesetting - attemptResults "correct", "_% correct", "incorrect" or "ungraded"- links to the answer blank - correctAnswers typeset version (untypeset versions are available via popups) - messages warns of formatting typos in the answer, or - more detailed messages about a wrong answer summary is obtained from $pg->{result}{summary}. If this is empty then a (localized) version of "all answers are correct" or "at least one answer is not coorrect" - imgGen points to a prebuilt image generator objectfor "images" mode - ce points to the CourseEnvironment -- it is needed if AttemptsTable - is required to build its own imgGen object maketext points to a localization subroutine =head2 Methods @@ -104,11 +72,6 @@ Returns HTML which formats the analysis of the student's answers to the problem. =over 4 -=item showMessages, - -This can be switched on or off before exporting the answerTemplate, perhaps -under instructions from the PG problem. - =item summary The contents of the summary can be defined when the attemptsTable object is created. @@ -138,264 +101,110 @@ use Mojo::Base 'Class::Accessor', -signatures; use Scalar::Util 'blessed'; use WeBWorK::Utils 'wwRound'; -# %options may contain: displayMode, submitted, imgGen, ce -# At least one of imgGen or ce must be provided if displayMode is 'images'. sub new ($class, $rh_answers, $c, %options) { - $class = ref $class || $class; - ref($rh_answers) =~ /HASH/ or die 'The first entry to AttemptsTable must be a hash of answers'; - $c->isa('WeBWorK::Controller') or die 'The second entry to AttemptsTable must be a WeBWorK::Controller'; + die 'The first entry to AttemptsTable must be a hash of answers' unless ref($rh_answers) =~ /HASH/; + die 'The second entry to AttemptsTable must be a WeBWorK::Controller' unless $c->isa('WeBWorK::Controller'); + my $self = bless { - answers => $rh_answers, - c => $c, - answerOrder => $options{answerOrder} // [], - answersSubmitted => $options{answersSubmitted} // 0, - summary => undef, # summary provided by problem grader (set in _init) - displayMode => $options{displayMode} || 'MathJax', - showHeadline => $options{showHeadline} // 1, - showAnswerNumbers => $options{showAnswerNumbers} // 1, - showAttemptAnswers => $options{showAttemptAnswers} // 1, # show student answer as entered and parsed - showAttemptPreviews => $options{showAttemptPreviews} // 1, # show preview of student answer - showAttemptResults => $options{showAttemptResults} // 1, # show results of grading student answer - showMessages => $options{showMessages} // 1, # show messages generated by evaluation - showCorrectAnswers => $options{showCorrectAnswers} // 0, # show the correct answers - showSummary => $options{showSummary} // 1, # show result summary - imgGen => undef, # set or created in _init method - }, $class; + answers => $rh_answers, + c => $c, + answerOrder => $options{answerOrder} // [], + answersSubmitted => $options{answersSubmitted} // 0, + summary => undef, # summary provided by problem grader (set below) + showHeadline => $options{showHeadline} // 1, + showSummary => $options{showSummary} // 1, # show result summary + submitted => $options{submitted} // 0, + }, + ref $class || $class; # Create accessors/mutators - $self->mk_ro_accessors(qw(answers c answerOrder answersSubmitted displayMode imgGen showAnswerNumbers - showAttemptAnswers showHeadline showAttemptPreviews showAttemptResults showCorrectAnswers showSummary)); - $self->mk_accessors(qw(showMessages summary)); - - # Sanity check and initialize imgGenerator. - $self->_init(%options); - - return $self; -} - -# Verify the display mode, and build imgGen if it is not supplied. -sub _init ($self, %options) { - $self->{submitted} = $options{submitted} // 0; - $self->{displayMode} = $options{displayMode} || 'MathJax'; + $self->mk_ro_accessors(qw(answers c answerOrder answersSubmitted showHeadline showSummary)); + $self->mk_accessors(qw(summary)); # Only show message column if there is at least one message. my @reallyShowMessages = grep { $self->answers->{$_}{ans_message} } @{ $self->answerOrder }; - $self->showMessages($self->showMessages && !!@reallyShowMessages); - - # Only used internally. Accessors are not needed. - $self->{numCorrect} = 0; - $self->{numBlanks} = 0; - $self->{numEssay} = 0; - - if ($self->displayMode eq 'images') { - if (blessed($options{imgGen}) && $options{imgGen}->isa('WeBWorK::PG::ImageGenerator')) { - $self->{imgGen} = $options{imgGen}; - } elsif (blessed($options{ce}) && $options{ce}->isa('WeBWorK::CourseEnvironment')) { - my $ce = $options{ce}; - - $self->{imgGen} = WeBWorK::PG::ImageGenerator->new( - tempDir => $ce->{webworkDirs}{tmp}, - latex => $ce->{externalPrograms}{latex}, - dvipng => $ce->{externalPrograms}{dvipng}, - useCache => 1, - cacheDir => $ce->{webworkDirs}{equationCache}, - cacheURL => $ce->{server_root_url} . $ce->{webworkURLs}{equationCache}, - cacheDB => $ce->{webworkFiles}{equationCacheDB}, - dvipng_align => $ce->{pg}{displayModeOptions}{images}{dvipng_align}, - dvipng_depth_db => $ce->{pg}{displayModeOptions}{images}{dvipng_depth_db}, - ); - } else { - warn 'Must provide image Generator (imgGen) or a course environment (ce) to build attempts table.'; - } - } # Make sure that the provided summary is a Mojo::ByteStream object. $self->summary(blessed($options{summary}) && $options{summary}->isa('Mojo::ByteStream') ? $options{summary} : $self->c->b($options{summary} // '')); - return; -} - -sub formatAnswerRow ($self, $rh_answer, $ans_id, $answerNumber) { - my $c = $self->c; - - my $answerString = $rh_answer->{student_ans} // ''; - my $answerPreview = $self->previewAnswer($rh_answer) // ' '; - my $correctAnswer = $rh_answer->{correct_ans} // ''; - my $correctAnswerPreview = $self->previewCorrectAnswer($rh_answer) // ' '; - - my $answerMessage = $rh_answer->{ans_message} // ''; - $answerMessage =~ s/\n/
/g; - my $answerScore = $rh_answer->{score} // 0; - $self->{numCorrect} += $answerScore >= 1; - $self->{numEssay} += ($rh_answer->{type} // '') eq 'essay'; - $self->{numBlanks}++ unless $answerString =~ /\S/ || $answerScore >= 1; - - my $feedbackMessageClass = ($answerMessage eq '') ? '' : $c->maketext('FeedbackMessage'); - - my $resultString; - my $resultStringClass; - if ($answerScore >= 1) { - $resultString = $c->maketext('correct'); - $resultStringClass = 'ResultsWithoutError'; - } elsif (($rh_answer->{type} // '') eq 'essay') { - $resultString = $c->maketext('Ungraded'); - $self->{essayFlag} = 1; - } elsif ($answerScore == 0) { - $resultStringClass = 'ResultsWithError'; - $resultString = $c->maketext('incorrect'); - } else { - $resultString = $c->maketext('[_1]% correct', wwRound(0, $answerScore * 100)); - } - my $attemptResults = $c->tag( - 'td', - class => $resultStringClass, - $c->tag('a', href => '#', data => { answer_id => $ans_id }, $self->nbsp($resultString)) - ); - - return $c->c( - $self->showAnswerNumbers ? $c->tag('td', $answerNumber) : '', - $self->showAttemptAnswers ? $c->tag('td', dir => 'auto', $self->nbsp($answerString)) : '', - $self->showAttemptPreviews - ? (((defined $answerPreview && $answerPreview ne '') || $self->showAttemptAnswers) - ? $self->formatToolTip($answerString, $answerPreview) - : $c->tag('td', dir => 'auto', $self->nbsp($answerString))) - : '', - $self->showAttemptResults ? $attemptResults : '', - $self->showCorrectAnswers ? $self->formatToolTip($correctAnswer, $correctAnswerPreview) : '', - $self->showMessages ? $c->tag('td', class => $feedbackMessageClass, $self->nbsp($answerMessage)) : '' - )->join(''); + return $self; } # Determine whether any answers were submitted and create answer template if they have been. sub answerTemplate ($self) { my $c = $self->c; - return '' unless $self->answersSubmitted; # Only print if there is at least one non-blank answer - - my $tableRows = $c->c; - - push( - @$tableRows, - $c->tag( - 'tr', - $c->c( - $self->showAnswerNumbers ? $c->tag('th', '#') : '', - $self->showAttemptAnswers ? $c->tag('th', $c->maketext('Entered')) : '', - $self->showAttemptPreviews ? $c->tag('th', $c->maketext('Answer Preview')) : '', - $self->showAttemptResults ? $c->tag('th', $c->maketext('Result')) : '', - $self->showCorrectAnswers ? $c->tag('th', $c->maketext('Correct Answer')) : '', - $self->showMessages ? $c->tag('th', $c->maketext('Message')) : '' - )->join('') - ) - ); - - my $answerNumber = 0; - for (@{ $self->answerOrder() }) { - push @$tableRows, $c->tag('tr', $self->formatAnswerRow($self->{answers}{$_}, $_, ++$answerNumber)); - } + return '' unless $self->answersSubmitted; # Only print if answers were submitted. return $c->c( $self->showHeadline ? $c->tag('h2', class => 'attemptResultsHeader', $c->maketext('Results for this submission')) : '', - $c->tag( - 'div', - class => 'table-responsive', - $c->tag('table', class => 'attemptResults table table-sm table-bordered', $tableRows->join('')) - ), $self->showSummary ? $self->createSummary : '' )->join(''); } -sub previewAnswer ($self, $answerResult) { - my $displayMode = $self->displayMode; - my $imgGen = $self->imgGen; - - my $tex = $answerResult->{preview_latex_string}; - - return '' unless defined $tex and $tex ne ''; - - return $tex if $answerResult->{non_tex_preview}; - - if ($displayMode eq 'plainText') { - return $tex; - } elsif (($answerResult->{type} // '') eq 'essay') { - return $tex; - } elsif ($displayMode eq 'images') { - return $imgGen->add($tex); - } elsif ($displayMode eq 'MathJax') { - return $self->c->tag('script', type => 'math/tex; mode=display', $self->c->b($tex)); - } -} - -sub previewCorrectAnswer ($self, $answerResult) { - my $displayMode = $self->displayMode; - my $imgGen = $self->imgGen; - - my $tex = $answerResult->{correct_ans_latex_string}; - - # Some answers don't have latex strings defined return the raw correct answer - # unless defined $tex and $tex contains non whitespace characters; - return $answerResult->{correct_ans} - unless defined $tex and $tex =~ /\S/; - - return $tex if $answerResult->{non_tex_preview}; - - if ($displayMode eq 'plainText') { - return $tex; - } elsif ($displayMode eq 'images') { - return $imgGen->add($tex); - } elsif ($displayMode eq 'MathJax') { - return $self->c->tag('script', type => 'math/tex; mode=display', $self->c->b($tex)); - } -} - # Create summary sub createSummary ($self) { my $c = $self->c; - my $numCorrect = $self->{numCorrect}; - my $numBlanks = $self->{numBlanks}; - my $numEssay = $self->{numEssay}; - my $summary; unless (defined($self->summary) and $self->summary =~ /\S/) { + my $numCorrect = 0; + my $numBlank = 0; + my $numEssay = 0; + + for (@{ $self->answerOrder() }) { + my $answerScore = $self->{answers}{$_}{score} // 0; + my $isEssay = ($self->{answers}{$_}{type} // '') eq 'essay'; + ++$numCorrect if $answerScore >= 1; + ++$numEssay if $isEssay; + ++$numBlank unless $isEssay || (($self->{answers}{$_}{student_ans} // '') =~ /\S/ || $answerScore >= 1); + } + # Default messages $summary = $c->c; my @answerNames = @{ $self->answerOrder() }; - if (scalar @answerNames == 1) { - if ($numCorrect == scalar @answerNames) { + if (@answerNames == 1) { + if ($numCorrect == 1) { push( @$summary, $c->tag( 'div', class => 'ResultsWithoutError mb-2', - $c->maketext('The answer above is correct.') + $c->maketext('The answer is correct.') + ) + ); + } elsif ($numEssay) { + push( + @$summary, + $c->tag( + 'div', + class => 'ResultsAlert mb-2', + $c->maketext('The answer will be graded later.') ) ); - } elsif ($self->{essayFlag}) { - push(@$summary, $c->tag('div', $c->maketext('Some answers will be graded later.'))); } else { push( @$summary, $c->tag( 'div', class => 'ResultsWithError mb-2', - $c->maketext('The answer above is NOT correct.') + $c->maketext('The answer is NOT correct.') ) ); } } else { - if ($numCorrect + $numEssay == scalar @answerNames) { + if ($numCorrect + $numEssay == @answerNames) { if ($numEssay) { push( @$summary, $c->tag( 'div', class => 'ResultsWithoutError mb-2', - $c->maketext('All of the gradeable answers above are correct.') + $c->maketext('All of the gradeable answers are correct.') ) ); } else { @@ -404,63 +213,54 @@ sub createSummary ($self) { $c->tag( 'div', class => 'ResultsWithoutError mb-2', - $c->maketext('All of the answers above are correct.') + $c->maketext('All of the answers are correct.') ) ); } - } elsif ($numBlanks + $numEssay != scalar(@answerNames)) { + } elsif ($numBlank + $numEssay + $numCorrect != @answerNames) { push( @$summary, $c->tag( 'div', class => 'ResultsWithError mb-2', - $c->maketext('At least one of the answers above is NOT correct.') + $c->maketext( + '[_1] of the answers [plural,_1,is,are] NOT correct.', + @answerNames - $numBlank - $numCorrect - $numEssay + ) ) ); } - if ($numBlanks > $numEssay) { - my $s = ($numBlanks > 1) ? '' : 's'; + if ($numBlank) { push( @$summary, $c->tag( 'div', class => 'ResultsAlert mb-2', $c->maketext( - '[quant,_1,of the questions remains,of the questions remain] unanswered.', $numBlanks + '[quant,_1,of the questions remains,of the questions remain] unanswered.', $numBlank ) ) ); } + if ($numEssay) { + push( + @$summary, + $c->tag( + 'div', + class => 'ResultsAlert mb-2', + $c->maketext('[_1] of the answers will be graded later.', $numEssay) + ) + ); + } } $summary = $summary->join(''); } else { $summary = $self->summary; # Summary defined by grader } + $summary = $c->tag('div', role => 'alert', class => 'attemptResultsSummary', $summary); $self->summary($summary); return $summary; } -# Utility subroutine that prevents unwanted line breaks, and ensures that the return value is a Mojo::ByteStream object. -sub nbsp ($self, $str) { - return $self->c->b(defined $str && $str =~ /\S/ ? $str : ' '); -} - -# Note that formatToolTip output includes the wrapper. -sub formatToolTip ($self, $answer, $formattedAnswer) { - return $self->c->tag( - 'td', - $self->c->tag( - 'div', - class => 'answer-preview', - data => { - bs_toggle => 'popover', - bs_content => $answer, - bs_placement => 'bottom', - }, - $self->nbsp($formattedAnswer) - ) - ); -} - 1; diff --git a/lib/WeBWorK/Utils/Rendering.pm b/lib/WeBWorK/Utils/Rendering.pm index b80c6c96c4..aeac0629bc 100644 --- a/lib/WeBWorK/Utils/Rendering.pm +++ b/lib/WeBWorK/Utils/Rendering.pm @@ -152,6 +152,15 @@ sub constructPGOptions ($ce, $user, $set, $problem, $psvn, $formFields, $transla $options{inputs_ref} = $formFields; $options{processAnswers} = $translationOptions->{processAnswers}; + # Attempt Results + $options{showFeedback} = $translationOptions->{showFeedback}; + $options{showAttemptAnswers} = $translationOptions->{showAttemptAnswers}; + $options{showAttemptPreviews} = $translationOptions->{showAttemptPreviews}; + $options{forceShowAttemptResults} = $translationOptions->{forceShowAttemptResults}; + $options{showAttemptResults} = $translationOptions->{showAttemptResults}; + $options{showMessages} = $translationOptions->{showMessages}; + $options{showCorrectAnswers} = $translationOptions->{showCorrectAnswers}; + # External Data $options{external_data} = decode_json($set->{external_data} || '{}'); diff --git a/lib/WebworkWebservice/RenderProblem.pm b/lib/WebworkWebservice/RenderProblem.pm index ac2f5ef6ab..f039322613 100644 --- a/lib/WebworkWebservice/RenderProblem.pm +++ b/lib/WebworkWebservice/RenderProblem.pm @@ -228,7 +228,15 @@ async sub renderProblem { isInstructor => $rh->{isInstructor} // 0, forceScaffoldsOpen => $rh->{forceScaffoldsOpen} // 0, QUIZ_PREFIX => $rh->{answerPrefix}, - debuggingOptions => { + showFeedback => $rh->{preview} || $rh->{WWsubmit} || $rh->{WWcorrectAns}, + showAttemptAnswers => $rh->{showAttemptAnswers} // 1, + showAttemptPreviews => $rh->{showAttemptPreviews} + // ($rh->{preview} || $rh->{WWsubmit} || $rh->{WWcorrectAns}), + showAttemptResults => $rh->{showAttemptResults} // ($rh->{WWsubmit} || $rh->{WWcorrectAns}), + forceShowAttemptResults => $rh->{forceShowAttemptResults}, + showMessages => $rh->{showMessages} // ($rh->{preview} || $rh->{WWsubmit} || $rh->{WWcorrectAns}), + showCorrectAnswers => $rh->{showCorrectAnswers} // $rh->{WWcorrectAns}, + debuggingOptions => { show_resource_info => $rh->{show_resource_info} // 0, view_problem_debugging_info => $rh->{view_problem_debugging_info} // 0, show_pg_info => $rh->{show_pg_info} // 0, diff --git a/templates/ContentGenerator/GatewayQuiz.html.ep b/templates/ContentGenerator/GatewayQuiz.html.ep index 4b35ff4100..6e317de15d 100644 --- a/templates/ContentGenerator/GatewayQuiz.html.ep +++ b/templates/ContentGenerator/GatewayQuiz.html.ep @@ -490,7 +490,7 @@ <%= $jumpLinks->() =%> % % # Print out problems and attempt results, as appropriate. - % # Note: Usage is $c->attemptResults($pg, $showCorrectAnswers, $showAttemptResults, $showSummary) + % # Note: Usage is $c->attemptResults($pg, $showSummary) % for my $i (0 .. $#$pg_results) { % my $pg = $pg_results->[ $probOrder->[$i] ]; % @@ -498,34 +498,23 @@ % my $recordMessage = ''; % my $resultsTable = ''; % - % if ($pg->{flags}{showPartialCorrectAnswers} >= 0 && $c->{submitAnswers}) { + % if ($c->{submitAnswers}) { % if ($c->{scoreRecordedMessage}[ $probOrder->[$i] ] ne 'recorded') { % $recordMessage = tag('div', class => 'ResultsWithError d-inline-block mb-2', % maketext('ANSWERS NOT RECORDED -- [_1]', $c->{scoreRecordedMessage}[ $probOrder->[$i] ]) % ); % } - % $resultsTable = $c->attemptResults( - % $pg, - % $c->{will}{showCorrectAnswers}, - % $pg->{flags}{showPartialCorrectAnswers} && $c->{can}{showProblemScores}, - % $c->{can}{showProblemScores} - % ); + % $resultsTable = $c->attemptResults($pg, $c->{can}{showProblemScores}); % } elsif ($c->{will}{checkAnswers} || $c->{will}{showProblemGrader}) { % $recordMessage = tag('div', class => 'ResultsWithError d-inline-block mb-2', % maketext('ANSWERS ONLY CHECKED -- ANSWERS NOT RECORDED') % ); % - % $resultsTable = $c->attemptResults( - % $pg, - % $c->{will}{showCorrectAnswers}, - % $pg->{flags}{showPartialCorrectAnswers} && $c->{can}{showProblemScores}, - % $c->{can}{showProblemScores} - % ); + % $resultsTable = $c->attemptResults($pg, $c->{can}{showProblemScores}); % } elsif ($c->{previewAnswers}) { % $recordMessage = tag('div', class => 'ResultsWithError d-inline-block mb-2', % maketext('PREVIEW ONLY -- ANSWERS NOT RECORDED') % ); - % $resultsTable = $c->attemptResults($pg, 0, 0, 0); % } %