line
stmt
bran
cond
sub
pod
time
code
1
package App::RecordStream::Operation::tognuplot;
2
3
our $VERSION = "4.0.23";
4
5
2
2
1096
use strict;
2
6
2
49
6
2
2
10
use warnings;
2
5
2
50
7
8
2
2
10
use base qw(App::RecordStream::Operation);
2
4
2
129
9
10
2
2
619
use File::Temp qw(tempfile);
2
15357
2
2600
11
12
sub init {
13
1
1
0
3
my $this = shift;
14
1
3
my $args = shift;
15
16
1
2
my $bar_graph;
17
1
2
my $dump_to_screen = 0;
18
1
3
my $gnuplot_command = 'gnuplot';
19
1
2
my $lines;
20
1
2
my $png_file = 'tognuplot.png';
21
1
7
my $title;
22
my @labels;
23
1
0
my @plots;
24
1
0
my @precommands;
25
1
0
my @using;
26
27
1
9
my $key_groups = App::RecordStream::KeyGroups->new();
28
29
my $spec = {
30
"file=s" => \$png_file,
31
1
1
1685
"key|k|fields|f=s" => sub { $key_groups->add_groups($_[1]); },
32
0
0
0
"label=s" => sub { push @labels, split(/,/, $_[1]); },
33
0
0
0
"plot=s" => sub { push @plots, split(/,/, $_[1]); },
34
0
0
0
"precommand=s" => sub { push @precommands, split(/,/, $_[1]); },
35
"title=s" => \$title,
36
0
0
0
"using=s" => sub { push @using, $_[1]; },
37
1
17
'bargraph' => \$bar_graph,
38
'dump-to-screen' => \$dump_to_screen,
39
'gnuplot-command=s' => \$gnuplot_command,
40
'lines' => \$lines,
41
$this->site_args(),
42
};
43
44
1
8
$this->parse_options($args, $spec);
45
46
1
50
5
die 'Must specify at least one field' unless ( $key_groups->has_any_group() );
47
48
1
50
33
12
if ( $bar_graph && $lines ) {
49
0
0
die 'Must specify one of --bargraph or --lines';
50
}
51
52
1
50
7
$png_file .= '.png' unless ( $png_file =~ m/\.png$/ );
53
54
1
50
5
if ( ! $dump_to_screen ) {
55
0
0
0
if ( open(my $fh, '|-', $gnuplot_command) ) {
56
0
0
close $fh;
57
}
58
else {
59
0
0
warn "Could not run gnuplot command: $gnuplot_command: $!\n";
60
0
0
warn "May want to specify a binary with --gnuplot-command\n";
61
0
0
exit 0;
62
}
63
}
64
65
1
6
my ($tempfh, $tempfile) = tempfile();
66
67
1
583
$this->{'BAR_GRAPH'} = $bar_graph;
68
1
3
$this->{'DUMP_TO_SCREEN'} = $dump_to_screen;
69
1
3
$this->{'FIRST_RECORD'} = 1;
70
1
3
$this->{'GNUPLOT_COMMAND'} = $gnuplot_command;
71
1
4
$this->{'KEY_GROUPS'} = $key_groups;
72
1
3
$this->{'LABELS'} = \@labels;
73
1
2
$this->{'LINES'} = $lines;
74
1
3
$this->{'PLOTS'} = \@plots;
75
1
3
$this->{'PNG_FILE'} = $png_file;
76
1
3
$this->{'PRECOMMANDS'} = \@precommands;
77
1
5
$this->{'TEMPFH'} = $tempfh;
78
1
4
$this->{'TEMPFILE'} = $tempfile;
79
1
6
$this->{'TITLE'} = $title;
80
1
29
$this->{'USING'} = \@using;
81
}
82
83
sub init_fields {
84
1
1
0
3
my $this = shift;
85
1
2
my $record = shift;
86
87
1
6
my $specs = $this->{'KEY_GROUPS'}->get_keyspecs($record);
88
1
3
my $using = $this->{'USING'};
89
1
3
my $bar_graph = $this->{'BAR_GRAPH'};
90
1
3
my $lines = $this->{'LINES'};
91
1
2
my $title = $this->{'TITLE'};
92
93
1
50
33
7
if ( ! $bar_graph && !$lines ) {
94
0
0
0
0
die 'Must specify using if more than 2 fields' if ( scalar @$specs > 2 ) && (! scalar @$using > 0);
95
}
96
97
1
50
4
if ( ! $title ) {
98
1
3
$title = join(', ', @$specs);
99
}
100
101
1
50
4
if ( scalar @$using == 0 ) {
102
1
50
33
7
if ( $bar_graph || $lines ) {
0
0
103
1
4
my $using_spec = "1 title \"$specs->[0]\"";
104
105
1
4
foreach my $idx (2..@$specs) {
106
0
0
my $title = $specs->[$idx-1];
107
0
0
$using_spec .= ", '' using $idx title \"$title\"";
108
}
109
110
1
3
push @$using, $using_spec;
111
}
112
elsif ( scalar @$specs == 1 ) {
113
0
0
push @$using, "1";
114
}
115
elsif ( scalar @$specs == 2 ) {
116
0
0
push @$using, "1:2";
117
}
118
}
119
120
1
3
$this->{'FIELDS'} = $specs;
121
1
4
$this->{'TITLE'} = $title;
122
}
123
124
# hook for additional args
125
1
0
sub site_args {
126
}
127
128
sub accept_record {
129
9
9
0
21
my ($this, $record) = @_;
130
131
9
100
23
if ( $this->{'FIRST_RECORD'} ) {
132
1
3
$this->{'FIRST_RECORD'} = 0;
133
1
4
$this->init_fields($record);
134
}
135
136
9
18
my $line = '';
137
9
14
foreach my $key (@{$this->{'FIELDS'}}) {
9
22
138
9
17
my $value = ${$record->guess_key_from_spec($key)};
9
25
139
9
50
25
$value = 0 if not defined $value;
140
9
46
$line .= "$value ";
141
}
142
143
9
21
chop $line;
144
9
50
22
if ( $this->{'DUMP_TO_SCREEN'} ) {
145
9
33
$this->push_line($line);
146
}
147
else {
148
0
0
my $tempfh = $this->{'TEMPFH'};
149
0
0
print $tempfh $line . "\n";
150
}
151
152
9
55
return 1;
153
}
154
155
sub stream_done {
156
1
1
0
3
my ($this) = @_;
157
158
1
13
close $this->{'TEMPFH'};
159
160
1
3
my $plot_script = '';
161
1
4
$plot_script .= "set terminal png\n";
162
1
5
$plot_script .= "set output '" . $this->{'PNG_FILE'} . "'\n";
163
1
3
$plot_script .= "set title '" . $this->{'TITLE'} . "'\n";
164
165
1
50
7
if ( $this->{'BAR_GRAPH'} ) {
50
166
0
0
$plot_script .= <
167
set style data histogram
168
set style histogram cluster gap 1
169
set style fill solid border -1
170
CMDS
171
}
172
elsif ( $this->{'LINES'} ) {
173
1
3
$plot_script .= "set style data linespoints\n";
174
}
175
176
1
9
foreach my $command (@{$this->{'PRECOMMANDS'}}) {
1
4
177
0
0
$plot_script .= $command . "\n";
178
}
179
180
1
3
my $plot_cmd = 'plot ';
181
182
1
2
my $index = 0;
183
1
3
my $default_label = join(', ', @{$this->{'FIELDS'}});
1
3
184
185
1
2
foreach my $use_spec (@{$this->{'USING'}}) {
1
3
186
1
50
4
if ( $this->{'DUMP_TO_SCREEN'} ) {
187
1
3
$plot_cmd .= "'screen' using $use_spec ";
188
}
189
else {
190
0
0
$plot_cmd .= "'" . $this->{'TEMPFILE'} . "' using $use_spec ";
191
}
192
193
1
50
6
if ( not ($use_spec =~ m/title/) ) {
194
0
0
my $label = $default_label;
195
196
0
0
0
if ( $this->{'LABELS'}->[$index] ) {
197
0
0
$label = $this->{'LABELS'}->[$index];
198
}
199
200
0
0
$plot_cmd .= "title '$label'";
201
}
202
203
1
3
$plot_cmd .= ', ';
204
1
2
$index++;
205
}
206
207
1
6
chop $plot_cmd;
208
1
2
chop $plot_cmd;
209
210
1
50
2
if ( @{$this->{'PLOTS'}} ) {
1
5
211
0
0
$plot_cmd .= ', ' . join(', ', @{$this->{'PLOTS'}});
0
0
212
}
213
214
1
3
$plot_script .= $plot_cmd;
215
216
1
50
3
if ( $this->{'DUMP_TO_SCREEN'} ) {
217
1
4
$this->push_line($plot_script);
218
}
219
else {
220
0
0
open(my $plot, '|-', $this->{'GNUPLOT_COMMAND'});
221
0
0
print $plot $plot_script;
222
0
0
close $plot;
223
}
224
225
1
50
5
if ( $? ) {
226
0
0
warn "Gnuplot failed, bailing!\n";
227
0
0
$this->_set_exit_value($?);
228
0
0
return;
229
}
230
231
1
6
$this->push_line("Wrote graph file: " . $this->{'PNG_FILE'});
232
}
233
234
sub DESTROY {
235
1
1
485
my ($this) = @_;
236
237
1
50
5
if ( $this->{'TEMPFH'} ) {
238
1
3
close $this->{'TEMPFH'};
239
}
240
241
1
50
5
if ( $this->{'TEMPFILE'} ) {
242
1
97
unlink $this->{'TEMPFILE'};
243
}
244
}
245
246
sub add_help_types {
247
1
1
0
65
my $this = shift;
248
1
16
$this->use_help_type('keyspecs');
249
1
6
$this->use_help_type('keygroups');
250
1
4
$this->use_help_type('keys');
251
}
252
253
sub usage {
254
0
0
0
my $this = shift;
255
256
0
my $options = [
257
['key|-k ', 'May be specified multiple times, may be comma separated. These are the keys to graph. If you have more than 2 keys, you must specify a --using statement or use --bargraph or --lines May be a keyspec or keygroup, see \'--help-keys\' for more information'],
258
['using ', 'A \'using\' string passed directly to gnuplot, you can use keys specified with --key in the order specified. For instance --key count,date,avg with --using \'3:2\' would plot avg vs. date. May be specified multiple times'],
259
['plot ', 'May be specified multiple times, may be comma separated. A directive passed directly to plot, e.g. --plot \'5 title "threshold"\''],
260
['precommand ', 'May be specified multiple times, may be comma separated. A command executed by gnuplot before executing plot, e.g. --precommand \'set xlabel "foo"\''],
261
['title ', 'Specify a title for the entire graph'],
262
['label ', 'Labels each --using line with the indicated label'],
263
['file ', 'Name of output png file. Will append .png if not present Defaults to tognuplot.png'],
264
['lines', 'Draw lines between points, may specify more than 2 key, each field is a line'],
265
['bargraph', 'Draw a bar graph, each field is a bar, may specify than 2 key, each field is a bar'],
266
['gnuplot-command', 'Location of gnuplot binary if not on path'],
267
['dump-to-screen', 'Instead of making a graph, dump the generated gnuplot script to STDOUT'],
268
];
269
270
0
my $args_string = $this->options_string($options);
271
272
0
return <
273
Usage: recs-tognuplot []
274
__FORMAT_TEXT__
275
Create a graph of points from a record stream using GNU Plot. Defaults to
276
creatinga scatterplot of points, can also create a bar or line graph
277
278
For the --using and --plot arguments, you may want to reference a GNU Plot
279
tutorial, though it can get quite complex, here is one example:
280
281
http://www.gnuplot.info/docs/node100.html
282
__FORMAT_TEXT__
283
284
Arguments:
285
$args_string
286
287
Graph the count field
288
recs-tognuplot --field count
289
Graph count vs. date with a threshold line
290
recs-tognuplot --field count,date --plot "5 title 'threshold'"
291
Graph a complicated expression, with a label
292
recs-tognuplot --field count,date,adjust --using '(\$1-\$3):2' --label "counts"
293
Graph count vs. date, with a title
294
recs-tognuplot --field count,date --title 'counts over time'
295
Graph count1, count2, count3 as 3 different bars in a bar graph
296
recs-tognuplot --field count1,count2,count3
297
USAGE
298
}
299
300
1;