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__ |