File Coverage

blib/lib/Catmandu/Importer/XLSX.pm
Criterion Covered Total %
statement 50 54 92.5
branch 11 16 68.7
condition 3 6 50.0
subroutine 10 10 100.0
pod 0 1 0.0
total 74 87 85.0


line stmt bran cond sub pod time code
1             package Catmandu::Importer::XLSX;
2              
3             our $VERSION = '0.06';
4              
5 3     3   2795 use namespace::clean;
  3         6  
  3         25  
6 3     3   381 use Catmandu::Sane;
  3         5  
  3         23  
7 3     3   1451 use Encode qw(decode);
  3         9250  
  3         288  
8 3     3   2060 use Spreadsheet::XLSX;
  3         82615  
  3         111  
9 3     3   32 use Spreadsheet::ParseExcel::Utility qw(int2col);
  3         6  
  3         149  
10 3     3   16 use Moo;
  3         4  
  3         31  
11              
12             with 'Catmandu::Importer';
13              
14             has xlsx => (is => 'ro', builder => '_build_xlsx');
15             has header => (is => 'ro', default => sub { 1 });
16             has columns => (is => 'ro' , default => sub { 0 });
17             has fields => (
18             is => 'rw',
19             coerce => sub {
20             my $fields = $_[0];
21             if (ref $fields eq 'ARRAY') { return $fields }
22             if (ref $fields eq 'HASH') { return [sort keys %$fields] }
23             return [split ',', $fields];
24             },
25             );
26             has _n => (is => 'rw', default => sub { 0 });
27             has _row_min => (is => 'rw');
28             has _row_max => (is => 'rw');
29             has _col_min => (is => 'rw');
30             has _col_max => (is => 'rw');
31              
32             sub BUILD {
33 9     9 0 170 my $self = shift;
34              
35 9 100       74 if ( $self->header ) {
36 8 100       158 if ( $self->fields ) {
    100          
37 1         26 $self->{_n}++;
38             }
39             elsif ( $self->columns ) {
40 1         15 $self->fields([$self->_get_cols]);
41 1         27 $self->{_n}++;
42             }
43             else {
44 6         1379 $self->fields([$self->_get_row]);
45 6         166 $self->{_n}++;
46             }
47             }
48             else {
49 1 50 33     22 if ( !$self->fields || $self->columns ) {
50 1         16 $self->fields([$self->_get_cols]);
51             }
52             }
53             }
54              
55             sub _build_xlsx {
56 9     9   112 my ($self) = @_;
57 9 50       79 my $xlsx = Spreadsheet::XLSX->new( $self->file ) or die "Could not open file " . $self->file;
58              
59             # process only first worksheet
60 9         732799 $xlsx = $xlsx->{Worksheet}->[0];
61 9         71 $self->{_col_min} = $xlsx->{MinCol};
62 9         39 $self->{_col_max} = $xlsx->{MaxCol};
63 9         39 $self->{_row_min} = $xlsx->{MinRow};
64 9         45 $self->{_row_max} = $xlsx->{MaxRow};
65 9         383 return $xlsx;
66             }
67              
68             sub generator {
69             my ($self) = @_;
70             sub {
71             while ($self->_n <= $self->_row_max) {
72             my @data = $self->_get_row();
73             $self->{_n}++;
74             my @fields = @{$self->fields()};
75             my %hash = map {
76             my $key = shift @fields;
77             defined $_ ? ($key => $_) : ()
78             } @data;
79             return \%hash;
80             }
81             return;
82             }
83             }
84              
85             sub _get_row {
86 129     129   158 my ($self) = @_;
87 129         124 my @row;
88 129         330 for my $col ( $self->_col_min .. $self->_col_max ) {
89 364         8103 my $cell = $self->xlsx->{Cells}[$self->_n][$col];
90 364 100       620 if ($cell) {
91 276         712 push(@row, decode('UTF-8',$cell->{Val}));
92             }
93             else{
94 88         148 push(@row, undef);
95             }
96             }
97 129         1863 return @row;
98             }
99              
100             sub _get_cols {
101 2     2   4 my ($self) = @_;
102 2         3 my @row;
103 2         15 for my $col ( $self->_col_min .. $self->_col_max ) {
104              
105 6 50 66     69 if (!$self->header || $self->columns) {
106 6         22 push(@row,int2col($col));
107             }
108             else {
109 0         0 my $cell = $self->xlsx->{Cells}[$self->_n][$col];
110 0 0       0 if ($cell) {
111 0         0 push(@row, decode('UTF-8',$cell->{Val}));
112             }
113             else{
114 0         0 push(@row, undef);
115             }
116             }
117             }
118 2         58 return @row;
119             }
120              
121              
122             =head1 NAME
123              
124             Catmandu::Importer::XLSX - Package that imports XLSX files
125              
126             =head1 SYNOPSIS
127              
128             # On the command line
129             $ catmandu convert XLSX < ./t/test.xlsx
130             $ catmandu convert XLSX --header 0 < ./t/test.xlsx
131             $ catmandu convert XLSX --fields 1,2,3 < ./t/test.xlsx
132             $ catmandu convert XLSX --columns 1 < ./t/test.xlsx
133              
134             # Or in Perl
135             use Catmandu::Importer::XLSX;
136              
137             my $importer = Catmandu::Importer::XLSX->new(file => "./t/test.xlsx");
138              
139             my $n = $importer->each(sub {
140             my $hashref = $_[0];
141             # ...
142             });
143              
144             =head1 DESCRIPTION
145              
146             L importer for XLSX files.
147              
148             Only the first worksheet from the Excel workbook is imported.
149              
150             =head1 METHODS
151            
152             This module inherits all methods of L and by this
153             L.
154            
155             =head1 CONFIGURATION
156            
157             In addition to the configuration provided by L (C,
158             C, etc.) the importer can be configured with the following parameters:
159            
160             =over
161            
162             =item header
163              
164             By default object fields are read from the XLS header line. If no header
165             line is avaiable object fields are named as column coordinates (A,B,C,...). Default: 1.
166              
167             =item fields
168              
169             Provide custom object field names as array, hash reference or comma-
170             separated list.
171              
172             =item columns
173              
174             When the 'columns' option is provided, then the object fields are named as
175             column coordinates (A,B,C,...). Default: 0.
176            
177             =back
178              
179             =head1 SEE ALSO
180              
181             L, L, L, L.
182              
183             =cut
184              
185             1;