diff --git a/htdocs/js/PGProblemEditor/pgproblemeditor.js b/htdocs/js/PGProblemEditor/pgproblemeditor.js index f48214574f..8e9fa78a54 100644 --- a/htdocs/js/PGProblemEditor/pgproblemeditor.js +++ b/htdocs/js/PGProblemEditor/pgproblemeditor.js @@ -231,6 +231,30 @@ .catch((err) => showMessage(`Error: ${err?.message ?? err}`)); }; + // Send a request to the server to run the PG critic in the CodeMirror editor. + const runPGCritic = () => { + const request_object = { courseID: document.getElementsByName('courseID')[0]?.value }; + + const user = document.getElementsByName('user')[0]; + if (user) request_object.user = user.value; + const sessionKey = document.getElementsByName('key')[0]; + if (sessionKey) request_object.key = sessionKey.value; + + request_object.rpc_command = 'runPGCritic'; + request_object.pgCode = + webworkConfig?.pgCodeMirror?.source ?? document.getElementById('problemContents')?.value ?? ''; + + fetch(webserviceURL, { method: 'post', mode: 'same-origin', body: new URLSearchParams(request_object) }) + .then((response) => response.json()) + .then((data) => { + renderArea.innerHTML = data.result_data.html; + }) + .catch((err) => { + console.log(err); + showMessage(`Error: ${err?.message ?? err}`); + }); + }; + document.getElementById('take_action')?.addEventListener('click', async (e) => { if (document.getElementById('current_action')?.value === 'format_code') { e.preventDefault(); @@ -240,6 +264,8 @@ document.querySelector('input[name="action.format_code"]:checked').value == 'convertCodeToPGML' ) { convertCodeToPGML(); + } else if (document.querySelector('input[name="action.format_code"]:checked').value == 'runPGCritic') { + runPGCritic(); } return; } diff --git a/lib/WebworkWebservice.pm b/lib/WebworkWebservice.pm index 1bd113b619..f44e4e5e5c 100644 --- a/lib/WebworkWebservice.pm +++ b/lib/WebworkWebservice.pm @@ -270,6 +270,7 @@ sub command_permission { putPastAnswer => 'problem_grader', tidyPGCode => 'access_instructor_tools', convertCodeToPGML => 'access_instructor_tools', + runPGCritic => 'access_instructor_tools', # WebworkWebservice::RenderProblem renderProblem => 'proctor_quiz_login', diff --git a/lib/WebworkWebservice/ProblemActions.pm b/lib/WebworkWebservice/ProblemActions.pm index 7684872367..9ec13c57ec 100644 --- a/lib/WebworkWebservice/ProblemActions.pm +++ b/lib/WebworkWebservice/ProblemActions.pm @@ -21,8 +21,9 @@ use warnings; use Data::Structure::Util qw(unbless); -use WeBWorK::PG::Tidy qw(pgtidy); -use WeBWorK::PG::ConvertToPGML qw(convertToPGML); +use WeBWorK::PG::Tidy qw(pgtidy); +use WeBWorK::PG::ConvertToPGML qw(convertToPGML); +use WeBWorK::PG::PGProblemCritic qw(analyzePGcode); sub getUserProblem { my ($invocant, $self, $params) = @_; @@ -180,4 +181,21 @@ sub convertCodeToPGML { } +sub runPGCritic { + my ($invocant, $self, $params) = @_; + my $pg_critic_results = analyzePGcode($params->{pgCode}); + + my $html_output = $self->c->render_to_string( + template => 'ContentGenerator/Instructor/PGProblemEditor/pg_critic', + results => $pg_critic_results + ); + + return { + ra_out => { + html => $html_output + }, + text => 'The script pg-critic has been run successfully.' + }; +} + 1; diff --git a/templates/ContentGenerator/Instructor/PGProblemEditor/format_code_form.html.ep b/templates/ContentGenerator/Instructor/PGProblemEditor/format_code_form.html.ep index 69da3d5a95..0ddab6d09f 100644 --- a/templates/ContentGenerator/Instructor/PGProblemEditor/format_code_form.html.ep +++ b/templates/ContentGenerator/Instructor/PGProblemEditor/format_code_form.html.ep @@ -30,4 +30,18 @@ <%= maketext('PGML Conversion Help') %> +
The following lists required metadata. If any is missing, the given tag must be filled in. + However, make sure that the categories are correct, especially if the problem has been + copied.
+ +% sub showIcon { my $show = shift; +% return $show ? q!! : q!!; +%} + +DBsubject | <%== showIcon($results->{metadata}{DBsection}) %> |
---|---|
DBchapter | <%== showIcon($results->{metadata}{DBchapter}) %> |
DBsection | <%== showIcon($results->{metadata}{DBsection}) %> |
Keywords | <%== showIcon($results->{metadata}{KEYWORDS}) %> |
PGML | This problem uses PGML, the current preferred way to write problem (text), solution and hint + blocks. |
---|---|
Solutions | This problem has a solution block. Every problem should have solutions that the + student can view after the answer data. |
Hints | This problem has a hint. This can be helpful for students after attempting the problem + a few times (this can be set by the instructor). +% } +% if ($pos->{randomness}) { + |
Randomness | This problem uses randomness. This is desired to give to a class of students, each + of whom may have a different problem. | +% } +% # list of the positive contexts: +% my @good_contexts = grep { $pos->{contexts}{$_} } keys %{$pos->{parsers}}; +% if (@good_contexts) { +
Modern Contexts | This problem uses the following modern contexts: + <%= join(', ', @good_contexts) %> | +% } +% my @good_parsers = grep { $pos->{parsers}->{$_} } keys %{$pos->{parsers}}; +% if (@good_parsers) { +
Modern Parsers | This problem uses features of the following modern parsers: + <%= join(', ', @good_parsers) %> | +% } +% my @good_macros = grep { $pos->{macros}->{$_} } keys %{$pos->{macros}}; +% if (@good_macros) { +
Modern Macros | This problem uses functionality from the following modern macros: + <%= join(', ', @good_macros) %> | +% } +
This problem has the following deprecated macros: <%= join(', ',@{$results->{deprecated_macros}} ) %>
+ +These should be removed from the problem in that these macros will be deleted from PG in a future + version. The functions from these macros may be listed below to help aid in transitioning away from + these macros.
+% } + +% my $has_bad_features = 0; +% $has_bad_features += $results->{negative}{$_} for (keys %{$results->{negative}}); + +% if ($has_bad_features || !$pos->{solution}) { +There are features in this problem that contain old or deprecated features. The following + list gives feedback of how the problem can be improved.
+%} + +