Skip to content

Commit c2563d7

Browse files
pstaabpdrgrice1
authored andcommitted
PG critic script.
1 parent 3d1d351 commit c2563d7

File tree

2 files changed

+472
-0
lines changed

2 files changed

+472
-0
lines changed

bin/pg-critic.pl

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#!/usr/bin/env perl
2+
3+
=head1 NAME
4+
5+
pg-critic.pl -- Analyze a pg file for use of old and current methods.
6+
7+
=head1 SYNOPSIS
8+
9+
pg-critic.pl [options] file1 file2 ...
10+
11+
Options:
12+
13+
-s|--score Give a score for each file.
14+
-f|--format Format of the output. Default ('text') is a plain text listing of the filename
15+
and the score. 'JSON' will make a JSON file.
16+
For output format 'JSON', the filename output must also be assigned,
17+
however for 'text', the output is optional.
18+
-o|--output-file Filename for the output. Note: this is required if JSON is the output format.
19+
-d|--details Include the details in the output. (Only used if the format is JSON).
20+
-v|--verbose Increase the verbosity of the output.
21+
-h|--help Show the help message.
22+
23+
=head1 DESCRIPTION
24+
25+
This script analyzes the input files for old/deprecated functions and macros as well
26+
as features for current best practices features.
27+
28+
See L<PGProblemCritic.pm> for details on what features are determined presence.
29+
30+
=head1 OPTIONS
31+
32+
The option C<-v> or C<--verbose> gives more information (on STDOUT) as the
33+
script is run.
34+
35+
The option C<-s> or C<--score> will return a score for each given PG problem.
36+
37+
=cut
38+
39+
use strict;
40+
use warnings;
41+
use experimental 'signatures';
42+
use feature 'say';
43+
44+
use Mojo::File qw(curfile);
45+
use Mojo::Util qw(dumper);
46+
use Mojo::JSON qw(encode_json);
47+
use Getopt::Long;
48+
use Pod::Usage;
49+
50+
use lib curfile->dirname->dirname . '/lib';
51+
52+
use WeBWorK::PG::PGProblemCritic qw(analyzePGfile);
53+
54+
my ($verbose, $show_score, $details, $show_help) = (0, 1, 0, 0);
55+
my ($format, $filename) = ('text', '');
56+
GetOptions(
57+
's|score' => \$show_score,
58+
'f|format=s' => \$format,
59+
'o|output-file=s' => \$filename,
60+
'd|details' => \$details,
61+
"v|verbose" => \$verbose,
62+
'h|help' => \$show_help
63+
);
64+
pod2usage(2) if $show_help || !$show_score;
65+
66+
die 'arguments must have a list of pg files' unless @ARGV > 0;
67+
die "The output format must be 'text' or 'JSON'" if (scalar(grep { $_ eq $format } qw(text JSON)) == 0);
68+
69+
my $output_file;
70+
unless ($format eq 'text' && $filename eq '') {
71+
die "The output-file is required if using the format: $format" if $filename eq '';
72+
$output_file = Mojo::File->new($filename);
73+
my $dir = $output_file->dirname->realpath;
74+
die "The output directory $dir does not exist or is not a directory" unless -d $dir->to_string;
75+
}
76+
77+
# Give a problem an assessment score:
78+
79+
my $rubric = {
80+
metadata => -5, # score for each missing required metadta
81+
good => {
82+
PGML => 20,
83+
solution => 30,
84+
hint => 10,
85+
scaffold => 50,
86+
custom_checker => 50,
87+
multianswer => 30,
88+
answer_hints => 20,
89+
nicetable => 10,
90+
contexts => { base_n => 10, units => 10, boolean => 10, reaction => 10 },
91+
parsers => { radio_buttons => 10, checkbox_list => 10, radio_multianswer => 10, graph_tool => 10 },
92+
macros => {
93+
random_person => 10,
94+
plots => 10,
95+
tikz => 10,
96+
plotly3D => 10,
97+
latex_image => 10,
98+
scaffold => 10,
99+
answer_hints => 10,
100+
}
101+
},
102+
bad => {
103+
BEGIN_TEXT => -10,
104+
beginproblem => -5,
105+
oldtable => -25,
106+
num_cmp => -75,
107+
str_cmp => -75,
108+
fun_cmp => -75,
109+
context_texstrings => -5,
110+
multiple_loadmacros => -20,
111+
showPartialCorrect => -5,
112+
lines_below_enddocument => -5,
113+
macros => { ww_plot => -20, PGchoicemacros => -20 }
114+
},
115+
deprecated_macros => -10
116+
};
117+
118+
sub scoreProblem ($prob) {
119+
my $score = 0;
120+
$score += (1 - $prob->{metadata}{$_}) * $rubric->{metadata} for (keys %{ $prob->{metadata} });
121+
$score += $prob->{good}{$_} * $rubric->{good}{$_} for (keys %{ $prob->{good} });
122+
$score += $prob->{bad}{$_} * $rubric->{bad}{$_} for (keys %{ $prob->{bad} });
123+
$score += $rubric->{deprecated_macros} for (@{ $prob->{deprecated_macros} });
124+
return $score;
125+
}
126+
127+
my @scores;
128+
129+
for (grep { $_ =~ /\.pg$/ } @ARGV) {
130+
say $_ if $verbose;
131+
my $features = analyzePGfile($_);
132+
my $file_info = { file => $_, score => scoreProblem($features) };
133+
$file_info->{details} = $features if $details;
134+
push(@scores, $file_info);
135+
}
136+
137+
if ($format eq 'text') {
138+
my $output_str = '';
139+
for my $score (@scores) {
140+
$output_str .= "filename: $score->{file}; score: $score->{score}\n";
141+
}
142+
if ($filename eq '') {
143+
say $output_str;
144+
} else {
145+
$output_file->spew($output_str);
146+
say "Results written in text format to $output_file" if $verbose;
147+
}
148+
} elsif ($format eq 'JSON') {
149+
$output_file->spew(encode_json(\@scores));
150+
say "Results written in JSON format to $output_file" if $verbose;
151+
}
152+
153+
1;

0 commit comments

Comments
 (0)