File Coverage

blib/lib/Catmandu/Importer/XLSX.pm
Criterion Covered Total %
statement 50 54 92.5
branch 12 18 66.6
condition 3 6 50.0
subroutine 10 10 100.0
pod 0 1 0.0
total 75 89 84.2


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