File Coverage

blib/lib/App/RecordStream/Operation/totable.pm
Criterion Covered Total %
statement 94 110 85.4
branch 24 36 66.6
condition 3 3 100.0
subroutine 14 15 93.3
pod 0 8 0.0
total 135 172 78.4


line stmt bran cond sub pod time code
1             package App::RecordStream::Operation::totable;
2              
3             our $VERSION = "4.0.24";
4              
5 3     3   807 use strict;
  3         6  
  3         74  
6 3     3   15 use warnings;
  3         6  
  3         78  
7              
8 3     3   12 use base qw(App::RecordStream::Accumulator App::RecordStream::Operation);
  3         8  
  3         533  
9              
10 3     3   20 use App::RecordStream::OutputStream;
  3         7  
  3         2586  
11              
12             sub init {
13 7     7 0 42 my $this = shift;
14 7         12 my $args = shift;
15              
16 7         13 my $no_header = 0;
17 7         13 my $delimiter = "\t";
18 7         13 my $spreadsheet = 0;
19 7         14 my $clear = 0;
20              
21 7         41 my $key_groups = App::RecordStream::KeyGroups->new();
22              
23             my $spec = {
24             "no-header|n" => \$no_header,
25 3     3   2344 "key|k|field|f=s" => sub { $key_groups->add_groups($_[1]); },
26 7         61 "delim|d=s" => \$delimiter,
27             "clear" => \$clear,
28             "spreadsheet|s" => \$spreadsheet,
29             };
30              
31 7         36 $this->parse_options($args, $spec);
32              
33 7 100       31 if ( ! $key_groups->has_any_group() ) {
34 4         13 $key_groups->add_groups('!.!returnrefs!sort');
35             }
36              
37 7         23 $this->{'NO_HEADER'} = $no_header;
38 7         21 $this->{'KEY_GROUPS'} = $key_groups;
39 7         21 $this->{'DELIMITER'} = $delimiter;
40 7         16 $this->{'SPREADSHEET'} = $spreadsheet;
41 7         80 $this->{'CLEAR'} = $clear;
42             }
43              
44             sub stream_done {
45 7     7 0 15 my $this = shift;
46              
47 7         18 my $records = $this->get_records();
48 7         15 my $key_group = $this->{'KEY_GROUPS'};
49 7         13 my %widths;
50 7         21 my $fields = [];
51              
52 7         21 foreach my $record (@$records) {
53 35         81 my $specs = $key_group->get_keyspecs_for_record($record);
54              
55 35         63 foreach my $field (@$specs) {
56 65 100       139 if(!exists($widths{$field})) {
57 13         25 $widths{$field} = 0;
58 13         23 push @$fields, $field;
59             }
60              
61 65         135 $widths{$field} = max($widths{$field}, length($this->extract_field($record, $field)));
62             }
63             }
64              
65 7         15 my $no_header = $this->{'NO_HEADER'};
66 7 100       19 if(!$no_header) {
67 6         18 foreach my $field (keys(%widths)) {
68 11         21 $widths{$field} = max($widths{$field}, length($field));
69             }
70             }
71              
72 7         15 $this->{'FIELDS'} = $fields;
73              
74 7 100       20 if(!$no_header) {
75             $this->push_line(
76             $this->format_row(
77             $fields,
78             \%widths,
79 11     11   19 sub { return $_[1]; },
80 6         43 ""
81             )
82             );
83              
84 6 100       29 if ( ! $this->{'SPREADSHEET'} ) {
85             $this->push_line(
86             $this->format_row(
87             $fields,
88             \%widths,
89 7     7   21 sub { return ("-" x $widths{$_[1]}); },
90 4         19 ""
91             )
92             );
93             }
94             }
95              
96 7         25 my %last = map { $_ => "" } (keys(%widths));
  13         38  
97 7         21 foreach my $record (@$records) {
98 35         103 $this->push_line(
99             $this->format_row(
100             $fields,
101             \%widths,
102             \&format_field,
103             [$record, \%last]
104             )
105             );
106             }
107             }
108              
109             sub format_field
110             {
111 65     65 0 114 my ($this, $field, $thunk) = @_;
112 65         106 my ($r, $lastr) = @$thunk;
113              
114 65         81 my $value = ${$r->guess_key_from_spec($field)};
  65         129  
115 65 50       147 $value = '' if ( ! defined $value );
116              
117 65 50       127 if ( ref($value) )
118             {
119 0         0 $value = App::RecordStream::OutputStream::hashref_string($value);
120             }
121              
122 65 50       117 if($this->{'CLEAR'})
123             {
124 0 0       0 if($value eq $lastr->{$field})
125             {
126             # This column matches the "last" value so we clear the cell.
127 0         0 $value = "";
128             }
129             else
130             {
131             # This column did not match so we do not clear the cell. We also
132             # invalidate all "last" field values to the right of this column.
133 0         0 my $startInvalidating = 0;
134 0         0 for(@{$this->{'FIELDS'}})
  0         0  
135             {
136 0 0       0 if($_ eq $field)
    0          
137             {
138 0         0 $startInvalidating = 1;
139             }
140             elsif($startInvalidating)
141             {
142 0         0 $lastr->{$_} = "";
143             }
144             }
145 0         0 $lastr->{$field} = $value;
146             }
147             }
148              
149 65         136 return $value;
150             }
151              
152              
153             sub format_row {
154 45     45 0 91 my ($this, $fieldsr, $widthsr, $format_fieldr, $thunk) = @_;
155              
156 45         62 my $first = 1;
157 45         57 my $row_string = "";
158              
159 45         76 foreach my $field (@$fieldsr) {
160 83         130 my $field_string = $format_fieldr->($this, $field, $thunk);
161              
162 83 50       150 unless ( defined $field_string ) {
163 0         0 $field_string = '';
164             }
165              
166 83 100 100     242 if ( (! $this->{'SPREADSHEET'}) &&
167             (length($field_string) < $widthsr->{$field})) {
168              
169 23         56 $field_string .= " " x ($widthsr->{$field} - length($field_string));
170             }
171              
172 83 100       139 if($first) {
173 45         65 $first = 0;
174             }
175             else {
176 38 100       73 $row_string .= ($this->{'SPREADSHEET'}) ? $this->{'DELIMITER'} : " ";
177             }
178              
179 83         145 $row_string .= $field_string;
180             }
181              
182 45         174 return $row_string;
183             }
184              
185             # Max helper function
186             sub max {
187 76     76 0 119 my $max = shift;
188              
189 76         124 foreach my $value (@_) {
190 76 100       139 if($value > $max) {
191 19         32 $max = $value;
192             }
193             }
194              
195 76         192 return $max;
196             }
197              
198             sub extract_field {
199 65     65 0 87 my $this = shift;
200 65         80 my $record = shift;
201 65         83 my $field = shift;
202              
203 65         82 my $value = ${$record->guess_key_from_spec($field)};
  65         155  
204 65 50       131 $value = '' if ( ! defined $value );
205              
206 65 50       107 if ( ref($value) )
207             {
208 0         0 $value = App::RecordStream::OutputStream::hashref_string($value);
209             }
210              
211 65         158 return $value;
212             }
213              
214             sub add_help_types {
215 7     7 0 16 my $this = shift;
216 7         34 $this->use_help_type('keyspecs');
217 7         24 $this->use_help_type('keygroups');
218 7         21 $this->use_help_type('keys');
219             }
220              
221             sub usage {
222 0     0 0   my $this = shift;
223              
224 0           my $options = [
225             ['no-header|n', 'Do not print column headers'],
226             ['key|k ', 'May be comma separated, may be specified multiple times. Specifies the fields to put in the table. May be a keyspec or a keygroup, see --help-keys'],
227             ['spreadsheet', 'Print out in a format suitable for excel. 1. Does not print line of -s after header 2. Separates by single character rather than series of spaces'],
228             ['delimiter ', 'Only useful with --spreadsheet, delimit with rather than the default of a tab'],
229             ['clear', 'Put blanks in cells where all of the row so far matches the row above.'],
230             ];
231              
232 0           my $args_string = $this->options_string($options);
233              
234 0           return <
235             Usage: recs-totable []
236             __FORMAT_TEXT__
237             Pretty prints a table of records to the screen. Will read in the entire
238             record stream to determine column size, and number of columns
239             __FORMAT_TEXT__
240              
241             $args_string
242              
243             Examples:
244             Display a table
245             recs-totable
246             Display only one field
247             recs-totable -f foo
248             Display two fields without a header
249             recs-totable -f foo -f bar --no-header
250             USAGE
251             }
252              
253             1;