File Coverage

blib/lib/Excel/Grinder.pm
Criterion Covered Total %
statement 67 68 98.5
branch 12 22 54.5
condition 5 11 45.4
subroutine 8 8 100.0
pod 3 3 100.0
total 95 112 84.8


line stmt bran cond sub pod time code
1             package Excel::Grinder;
2              
3             our $VERSION = "1.0";
4              
5             # time to grow up
6 1     1   786 use strict;
  1         2  
  1         30  
7 1     1   6 use warnings;
  1         1  
  1         24  
8 1     1   19 use Carp;
  1         2  
  1         59  
9              
10             # this stands on the feet of giants
11 1     1   1436 use Excel::Writer::XLSX;
  1         224820  
  1         69  
12 1     1   558 use Spreadsheet::XLSX;
  1         66678  
  1         948  
13              
14             # OO out of habit
15             sub new {
16 1     1 1 589 my ($class, $default_directory) = @_;
17            
18             # default the default directory to /tmp/excel_grinder
19 1   50     7 $default_directory ||= '/tmp/excel_grinder';
20            
21             # make sure that directory exists
22 1 50       287 mkdir $default_directory if !(-d $default_directory);
23            
24             # if it still does exist, bail out
25 1 50       20 croak "Error: $default_directory does not exist and cannot be auto-created." if !(-d $default_directory);
26            
27             # become!
28 1         5 my $self = bless {
29             'default_directory' => $default_directory,
30             }, $class;
31            
32 1         5 return $self;
33             }
34              
35             # method to convert a three-level array into a nice excel file
36             sub write_excel {
37             # required arguments are (1) the filename and (2) the data structure to turn into an XLSX file
38 1     1 1 441 my ($self, %args) = @_;
39             # looks like:
40             # 'filename' => 'some_file.xlsx', # will be saved under /opt/majestica/tmp/DATABASE_NAME/some_file.xlsx; required
41             # 'the_data' => @$three_level_arrayref, # worksheets->rows->columns; see below; required
42             # 'headings_in_data' => 1, # if filled, first row of each worksheet will be captialized; optional
43             # 'worksheet_names' => ['Names','of','Worksheets'], # if filled, will be the names to give the worksheets
44              
45 1         3 my ($tmp_dir, $item, $col, @bits, $workbook, $worksheet_data, $worksheet, $n, $row_array, $row_upper, $worksheet_name);
46              
47             # fail without a filename
48 1 50       7 croak 'Error: Filename required for write_excel()' if !$args{filename};
49              
50             # the data structure must be an array of arrays of arrays
51             # three levels: worksheets, rows, columns
52 1 50       3 croak 'Error: Must send a three-level arrayref (workbook->rows->columns) to write_excel()' if !$args{the_data}[0][0][0];
53              
54             # place into default_directory unless they specified a directory
55              
56 1 50       11 $args{filename} = $self->{default_directory}.'/'.$args{filename} if $args{filename} !~ /\//;
57 1 50       6 $args{filename} .= '.xlsx' if $args{filename} !~ /\.xlsx$/;
58              
59             # start our workbook
60 1         9 $workbook = Excel::Writer::XLSX->new( $args{filename} );
61              
62             # Set the format for dates.
63 1         715 my $date_format = $workbook->add_format( num_format => 'mm/dd/yy' );
64              
65             # start adding worksheets
66 1         103 foreach $worksheet_data (@{ $args{the_data} }) {
  1         3  
67 2         4 $worksheet_name = shift @{ $args{worksheet_names} }; # if it's there
  2         5  
68 2         17 $worksheet_name =~ s/[^0-9a-z\-\s]//gi; # clean it up
69 2         13 $worksheet = $workbook->add_worksheet($worksheet_name);
70            
71             # go thru each row...
72 2         680 $n = 0;
73 2         6 foreach $row_array (@$worksheet_data) {
74              
75             # do they want the first row to the headings?
76 9 100 66     41 if ($args{headings_in_data} && $n == 0) { # uppercase the first row
77 2         5 @$row_upper = map { uc($_) } @$row_array;
  6         18  
78 2         4 $row_array = $row_upper;
79             }
80            
81             # now each column...
82 9         12 $col = 0;
83 9         16 foreach $item (@$row_array) {
84             # dates are no funzies
85 27 50       1586 if ($item =~ /^(\d{4})-(\d{2})-(\d{2})$/) { # special routine for dates
86 0         0 $worksheet->write_date_time( $n, $col++, $1.'-'.$2.'-'.$3.'T', $date_format );
87             } else {
88 27         68 $worksheet->write( $n, $col++, $item );
89             }
90              
91             }
92 9         688 $n++;
93             }
94             }
95              
96             # that's not so hard, now is it?
97 1         8 return $args{filename};
98             }
99              
100             # method to import an excel file into a nice three-level array
101             sub read_excel {
102             # require argument is the filename or full path to the excel xlsx file
103             # if it's just a filename, look in the default directory
104 1     1 1 40532 my ($self,$filename) = @_;
105              
106 1 50       10 $filename = $self->{default_directory}.'/'.$filename if $filename !~ /\//;
107 1 50       7 $filename .= '.xlsx' if $filename !~ /\.xlsx$/;
108              
109             # gotta exist, after all that
110 1 50       21 croak 'Error: Must send a valid full file path to an XLSX file to read_excel()' if !(-e "$filename");
111            
112 1         3 my ($excel, $sheet_num, $sheet, $row_num, $row, @the_data, $cell, $col);
113              
114             # again, stand on the shoulders of giants
115 1         11 $excel = Spreadsheet::XLSX->new($filename);
116              
117             # read it in, sheet by sheet
118 1         18066 $sheet_num = 0;
119 1         3 foreach $sheet (@{$excel->{Worksheet}}) {
  1         4  
120              
121             # set the max = 0 if there is one or none rows
122 2   33     7 $sheet->{MaxRow} ||= $sheet->{MinRow};
123              
124             # same for the columns
125 2   33     4 $sheet->{MaxCol} ||= $sheet->{MinCol};
126              
127             # cycle through each row
128 2         5 $row_num = 0;
129 2         6 foreach $row ($sheet->{MinRow} .. $sheet->{MaxRow}) {
130             # go through each available column
131 9         17 foreach $col ($sheet->{MinCol} .. $sheet->{MaxCol}) {
132              
133             # get ahold of the actual cell object
134 27         36 $cell = $sheet->{Cells}[$row][$col];
135            
136             # next if !$cell; # skip if blank
137              
138             # add it to our nice array
139 27         31 push (@{ $the_data[$sheet_num][$row] }, $cell->{Val} );
  27         52  
140             }
141             # advance
142 9         13 $row_num++;
143             }
144 2         4 $sheet_num++;
145             }
146              
147             # send it back
148 1         32 return \@the_data;
149             }
150              
151             1;
152              
153             __END__