File Coverage

lib/Clearbuilt/ExcelErator.pm
Criterion Covered Total %
statement 6 116 5.1
branch 0 38 0.0
condition 0 8 0.0
subroutine 2 13 15.3
pod 1 2 50.0
total 9 177 5.0


line stmt bran cond sub pod time code
1             package Clearbuilt::ExcelErator;
2 1     1   236601 use Modern::Perl;
  1         9859  
  1         7  
3             our $VERSION = '2.0001'; # VERSION
4             our $AUTHORITY = 'cpan:CLEARBLT'; # AUTHORITY
5             # ABSTRACT: Write XLSX files in a Clearbuilt-standard way
6 1     1   809 use Moo;
  1         11575  
  1         6  
7             extends 'Excel::Writer::XLSX';
8              
9             #
10             # Attributes
11             #
12              
13             has color => (
14             is => 'ro',
15             lazy => 1,
16             builder => sub {
17 0     0     my ($self) = @_;
18             return {
19 0           gray30 => $self->set_custom_color( 40, 77, 77, 77 ),
20             gray50 => $self->set_custom_color( 40, 127, 127, 127 ),
21             gray80 => $self->set_custom_color( 41, 205, 205, 205 ),
22             blueaccent1darker50 => $self->set_custom_color( 42, 31, 78, 121 ),
23             };
24             },
25             );
26              
27             has column_lengths => (
28             is => 'ro',
29             builder => sub {
30 0     0     return {};
31             },
32             );
33              
34             has filename => ( is => 'ro', );
35              
36             has format_cache => (
37             is => 'rwp',
38             lazy => 1,
39             builder => sub {
40 0     0     my ($self) = @_;
41             return {
42             richnormal => $self->add_format(),
43             richbold => $self->add_format( bold => 1 ),
44             richitalic => $self->add_format( italic => 1 ),
45             settings => {
46             color => {
47             'black' => { color => 'black' },
48             'blue' => { color => 'blue' },
49             'brown' => { color => 'brown' },
50             'cyan' => { color => 'cyan' },
51             'gray' => { color => 'gray' },
52             'green' => { color => 'green' },
53             'lime' => { color => 'lime' },
54             'magenta' => { color => 'magenta' },
55             'navy' => { color => 'navy' },
56             'orange' => { color => 'orange' },
57             'pink' => { color => 'pink' },
58             'purple' => { color => 'purple' },
59             'red' => { color => 'red' },
60             'silver' => { color => 'silver' },
61             'white' => { color => 'white' },
62             'yellow' => { color => 'yellow' },
63             },
64             type => {
65             normal => {},
66             wrap => { text_wrap => 1, },
67             currency => { num_format => '$#,##0.00', },
68             currencywhole => { num_format => '$#,##0', },
69             currencyplain => { num_format => '0.00', },
70             currencyacct => { num_format => '$#,##0.00;($#,##0.00)', },
71             percent => { num_format => '0.00%', },
72             multiplier => { num_format => '0.0000', },
73             dec1comma => { num_format => '#,##0.0' },
74             },
75             font => {
76             normal => {},
77             underline => { underline => 1 },
78             underline20 => { underline => 1, size => 20, },
79             bold => { bold => 1 },
80             bold16 => { bold => 1, size => 16, },
81             bold18 => { bold => 1, size => 18, },
82             bold20 => { bold => 1, size => 20, },
83             bold26 => { bold => 1, size => 26, },
84             italic => { italic => 1 },
85             italic20 => { italic => 1, size => 20, },
86             },
87             bg => {
88             none => {},
89             white => { bg_color => 'white' },
90             gray => { bg_color => 'gray' },
91             yellow => { bg_color => 'yellow' },
92             blue => { bg_color => 'blue', color => 'white' },
93             gray30 => { bg_color => $self->color->{gray30}, color => 'white' },
94             gray50 => { bg_color => $self->color->{gray50} },
95             gray80 => { bg_color => $self->color->{gray80} },
96             blueaccent1darker50 =>
97             { bg_color => $self->color->{blueaccent1darker50}, color => 'white' },
98             },
99             border_color => {
100             black => {},
101             gray30 => { border_color => $self->color->{gray30} },
102             gray80 => { border_color => $self->color->{gray80} },
103             blueaccent1darker50 => { border_color => $self->color->{blueaccent1darker50} },
104             },
105 0           halign => {
106             left => { align => 'left' },
107             center => { align => 'center' },
108             right => { align => 'right' },
109             indent2 => { indent => 2 },
110             indent3 => { indent => 3 },
111             },
112             valign => {
113             top => { valign => 'top' },
114             vcenter => { valign => 'vcenter' },
115             bottom => { valign => 'bottom' },
116             vjustify => { valign => 'vjustify' },
117             },
118             bt => {
119             0 => { top => 0 },
120             1 => { top => 1 },
121             2 => { top => 2 },
122             },
123             bb => {
124             0 => { bottom => 0 },
125             1 => { bottom => 1 },
126             2 => { bottom => 2 },
127             },
128             bl => {
129             0 => { left => 0 },
130             1 => { left => 1 },
131             2 => { left => 2 },
132             },
133             br => {
134             0 => { right => 0 },
135             1 => { right => 1 },
136             2 => { right => 2 },
137             },
138             },
139             };
140             },
141             );
142              
143             #
144             # Builder
145             #
146              
147             sub FOREIGNBUILDARGS {
148 0     0 0   my ( $self, $options ) = @_;
149 0           return $options->{filename};
150             }
151              
152             #
153             # Public methods
154             #
155              
156             sub write_the_book {
157 0     0 1   my ( $self, $spreadsheet ) = @_;
158 0           foreach my $sheet ( @{$spreadsheet} ) {
  0            
159 0           my $worksheet = $self->add_worksheet( $sheet->{title} );
160 0           my $row = 0;
161 0 0         if ( defined $sheet->{col_widths} ) {
162 0           $self->_set_default_column_widths( $worksheet, $sheet->{col_widths} );
163             }
164 0           foreach my $datarow ( @{ $sheet->{rows} } ) {
  0            
165 0           my $col = 0;
166 0           my $cells;
167 0 0         if ( !defined $datarow ) {
168 0           $row++;
169 0           next;
170             }
171 0 0         if ( ref $datarow eq 'ARRAY' ) {
172 0           $cells = $datarow;
173             }
174             else {
175 0           $cells = $datarow->{cells};
176 0           my $rowformat = $self->_format_with_defaults();
177 0 0         if ( $datarow->{format} ) {
178 0           $rowformat = $self->_format_with_defaults( @{ $datarow->{format} } );
  0            
179             }
180             $worksheet->set_row(
181             $row, $datarow->{height}, $rowformat,
182             ( $datarow->{hidden} // 0 ),
183             ( $datarow->{outline_level} // 0 ),
184 0   0       ( $datarow->{collapsed} // 0 )
      0        
      0        
185             );
186             }
187 0           foreach my $cell ( @{$cells} ) {
  0            
188 0           my $format = $self->_format_with_defaults();
189 0 0         if ( ref($cell) ne 'HASH' ) {
190 0 0         if ( defined $cell ) {
191 0           $self->_update_column_length( $sheet->{title}, $col, $cell );
192 0           $worksheet->write( $row, $col, $cell, $format );
193             }
194 0           $col++;
195 0           next;
196             }
197 0 0         unless ( $cell->{nowidth} ) {
198 0           $self->_update_column_length( $sheet->{title}, $col, $cell->{value} );
199             }
200 0 0         if ( $cell->{format} ) {
201 0           $format = $self->_format_with_defaults( @{ $cell->{format} } );
  0            
202             }
203 0 0         if ( $cell->{across} ) {
204 0           $worksheet->merge_range( $cell->{across}, $cell->{value}, $format );
205 0           $col++;
206 0           next;
207             }
208 0 0         my $writer = $cell->{as_text} ? 'write_string' : 'write';
209 0           $worksheet->$writer( $row, $col, $cell->{value}, $format );
210 0 0         if ($cell->{comment}) {
211 0 0         if (ref $cell->{comment} ne 'HASH') {
212 0           $worksheet->write_comment( $row, $col, $cell->{comment} );
213             } else {
214 0           $worksheet->write_comment( $row, $col, $cell->{comment}->{value}, @{$cell->{comment}->{format}});
  0            
215             }
216             }
217 0           $col++;
218             }
219 0           $row++;
220             }
221 0           $self->_close();
222             }
223             }
224              
225             #
226             # Private methods
227             #
228              
229             sub _close {
230 0     0     my $self = shift;
231 0           $self->_set_column_widths;
232 0           $self->SUPER::close();
233             }
234              
235             sub _format_with_defaults {
236 0     0     my $self = shift;
237 0           my %args = (
238             type => 'normal',
239             font => 'normal',
240             color => 'black',
241             bg => 'none',
242             border_color => 'black',
243             halign => 'left',
244             valign => 'bottom',
245             bt => 0,
246             bb => 0,
247             bl => 0,
248             br => 0,
249             );
250 0 0         if (@_) {
251 0           %args = (
252             type => 'normal',
253             font => 'normal',
254             color => 'black',
255             bg => 'none',
256             border_color => 'black',
257             halign => 'left',
258             valign => 'bottom',
259             bt => 0,
260             bb => 0,
261             bl => 0,
262             br => 0,
263             @_,
264             );
265             }
266              
267 0           my $cache_key = join( '|', map { $args{$_} } qw(type font color bg halign valign bt bb bl br) );
  0            
268 0 0         return $self->format_cache->{$cache_key} if ( exists $self->format_cache->{$cache_key} );
269              
270             $self->format_cache->{$cache_key} = $self->add_format(
271 0           %{ $self->format_cache->{settings}{type}{ $args{type} } },
272 0           %{ $self->format_cache->{settings}{font}{ $args{font} } },
273 0           %{ $self->format_cache->{settings}{color}{ $args{color} } },
274 0           %{ $self->format_cache->{settings}{bg}{ $args{bg} } },
275 0           %{ $self->format_cache->{settings}{border_color}{ $args{border_color} } },
276 0           %{ $self->format_cache->{settings}{halign}{ $args{halign} } },
277 0           %{ $self->format_cache->{settings}{valign}{ $args{valign} } },
278 0           %{ $self->format_cache->{settings}{bt}{ $args{bt} } },
279 0           %{ $self->format_cache->{settings}{bb}{ $args{bb} } },
280 0           %{ $self->format_cache->{settings}{bl}{ $args{bl} } },
281 0           %{ $self->format_cache->{settings}{br}{ $args{br} } },
  0            
282             );
283 0           return $self->format_cache->{$cache_key};
284             }
285              
286             sub _no_more_than {
287 0     0     my ( $self, $max, $val ) = @_;
288 0 0         return $val if $val < $max;
289 0           return $max;
290             }
291              
292             sub _set_column_widths {
293 0     0     my ( $self, $factor ) = @_;
294 0 0         $factor = 1.3 unless ( defined $factor );
295 0           foreach my $sheet_name ( keys %{ $self->column_lengths } ) {
  0            
296 0           my $sheet = $self->get_worksheet_by_name($sheet_name);
297 0           foreach my $column ( keys %{ $self->column_lengths->{$sheet_name} } ) {
  0            
298             $sheet->set_column( $column, $column,
299 0           $factor * $self->column_lengths->{$sheet_name}->{$column} );
300             }
301             }
302             }
303              
304             sub _set_default_column_widths {
305 0     0     my ( $self, $worksheet, $format_info ) = @_;
306 0           foreach my $element ( keys %{$format_info} ) {
  0            
307 0           foreach my $range ( split /,/, $element ) {
308 0           my $first_element = $range;
309 0           my $last_element = $range;
310 0 0         if ( $range =~ /\-/ ) { ( $first_element, $last_element ) = $range =~ /(\d+)\-(\d+)/; }
  0            
311 0           my $curr = $first_element;
312 0           while ( $curr <= $last_element ) {
313 0           $worksheet->set_column( $curr, $curr, $format_info->{$element} );
314             $self->column_lengths->{ $worksheet->get_name() }->{$curr} =
315 0           $format_info->{$element};
316 0           $curr++;
317             }
318             }
319             }
320             }
321              
322             sub _update_column_length {
323 0     0     my ( $self, $sheet, $current_column, $value ) = @_;
324 0 0         if ( defined $self->column_lengths->{$sheet}->{$current_column} ) {
325 0   0       my $cell_length = length($value) // 0;
326 0 0         if ( $cell_length > $self->column_lengths->{$sheet}->{$current_column} ) {
327 0           $self->column_lengths->{$sheet}->{$current_column} =
328             $self->_no_more_than( 80, $cell_length );
329             }
330             }
331             else {
332 0           $self->column_lengths->{$sheet}->{$current_column} =
333             $self->_no_more_than( 80, length($value) );
334             }
335             }
336              
337             1;
338              
339             __END__