File Coverage

blib/lib/Excel/Grinder.pm
Criterion Covered Total %
statement 70 73 95.8
branch 13 24 54.1
condition 5 12 41.6
subroutine 8 8 100.0
pod 3 3 100.0
total 99 120 82.5


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