File Coverage

blib/lib/NetSDS/Util/FileImport.pm
Criterion Covered Total %
statement 24 90 26.6
branch 0 32 0.0
condition 0 12 0.0
subroutine 8 12 66.6
pod 1 1 100.0
total 33 147 22.4


line stmt bran cond sub pod time code
1              
2             =head1 NAME
3              
4             NetSDS::Util::FileImport - import table structure from file
5              
6             =head1 SYNOPSIS
7              
8              
9             =head1 DESCRIPTION
10              
11             =cut
12              
13             package NetSDS::Util::FileImport;
14              
15 2     2   14366 use 5.8.0;
  2         9  
  2         123  
16 2     2   12 use strict;
  2         5  
  2         76  
17 2     2   60 use warnings;
  2         4  
  2         66  
18              
19 2     2   2594 use File::MMagic; # Determine MIME type of file
  2         27782  
  2         97  
20 2     2   2573 use Spreadsheet::Read; # Parse spreadsheet files
  2         65149  
  2         209  
21              
22 2     2   21 use base 'Exporter';
  2         6  
  2         205  
23              
24 2     2   124 use version; our $VERSION = "1.044";
  2         4  
  2         31  
25              
26             our @EXPORT = qw(
27             import_table
28             );
29              
30             # TODO
31 2     2   321 use constant PREVIEW_LIMIT => 5; # Number of records to process for previews
  2         10  
  2         2710  
32              
33             =head1 CLASS API
34              
35             =over
36              
37             =item B<import_table()> - import table data from file
38              
39             takes $content of a file, $pre_parse (true or false it means: return all table or only 5 first rows
40              
41             params it could be
42             patterns => { qr#name#i => { qr#last#i => 'last_name', qr#first#i => 'first_name' } }
43             separator => could be ,;\t:
44             fields => [ email last_name ]
45             substitute => { E-mail => email, Last Name => last_name, .. }
46              
47             Depends of a params parse would be different
48              
49             Returns a structure like this
50             [ { last_name => undef, first_name => yana, ... }, { last_name => kornienko, first_name => test, ... } .. ]
51              
52             =cut
53              
54             sub import_table($;$$) {
55 0     0 1   my ( $file_name, $params, $pre_parse ) = @_;
56 0           my ( $separator, $data, $rows ) = ( $params->{'separator'}, [], [] );
57 0 0         return "Can't find file [$file_name]." unless -e $file_name;
58             # [ { 'last_name' => 'my name', 'First Name' => 'my first name' ... }, {'last_name' => undef, 'First Name' => 'test_name' ... } ... ]
59              
60 0           my @title = ();
61 0 0         if ( File::MMagic->new->checktype_filename($file_name) eq 'text/plain' ) {
62              
63 0 0         open my $FILE, '<', $file_name or return $!;
64 0           my @lines = <$FILE>;
65 0           chomp for @lines;
66 0           close $FILE;
67              
68 0           $lines[0] =~ s/^["']|["']$//;
69 0   0       $separator ||= ( $lines[0] =~ m![\w\s"]+?([,;:\t])! )[0];
70 0 0         return "Parse error while parsing csv file" unless $separator;
71              
72 0           $lines[0] =~ s/["']$//;
73 0           @title = split /["']{0,1}$separator["']{0,1}/, $lines[0];
74              
75 0 0 0       my @rows = ( ( $pre_parse and @lines > PREVIEW_LIMIT ) ? @lines[ 1 .. PREVIEW_LIMIT + 1 ] : @lines );
76 0 0 0       $rows = [ map { [ split /["']*$separator["']*/, $_ ] } grep { ( $_ and $_ =~ s/^['"]// ) or $_ } @rows ];
  0            
  0            
77              
78             } else {
79              
80 0           my $struct = ReadData($file_name);
81 0 0         return "Parse error while parsing data xls file" unless $struct;
82              
83 0           my @content = @{ $struct->[1]{'cell'} };
  0            
84 0           @title = map { $content[$_]->[1] } 1 .. $#content;
  0            
85 0 0 0       my $count = ( ( $pre_parse and @{ $content[1] } > ( PREVIEW_LIMIT + 2 ) ) ? PREVIEW_LIMIT : scalar @{ $content[1] } - 1 );
  0            
86              
87 0           for my $i ( 2 .. $count ) {
88 0           push @$rows, [ map { $content[$_]->[$i] } 1 .. @title ];
  0            
89             }
90             }
91              
92 0           my @original_fields = @title;
93 0 0         if ( $params->{'patterns'} ) {
    0          
    0          
94 0           _order_data_by_patterns( \@title, $params->{'patterns'} );
95             } elsif ( $params->{'fields'} ) { #return only specific fields that has the same name
96 0           _order_data_by_fields( $data, \@title, $params->{'fields'}, $rows );
97 0           return $data;
98             } elsif ( $params->{'substitute'} ) { #return only specific fields the names of which has been changed
99 0           _order_data_with_substitute( $data, \@title, $params->{'substitute'}, $rows );
100 0           return $data;
101             }
102              
103 0           for my $row (@$rows) {
104 0           push @$data, { map { ( $title[$_] => $row->[$_] ) } 0 .. $#title };
  0            
105             }
106              
107 0 0         return wantarray ? ( $data, \@original_fields ) : $data;
108             } ## end sub import_table($;$$)
109              
110             sub _order_data_by_patterns($$) {
111 0     0     my ( $title, $patterns ) = @_;
112              
113 0           for ( my $i = 0 ; $title->[$i] ; $i++ ) { #TODO multi
114 0           for my $pattern ( keys %$patterns ) {
115 0 0         if ( $title->[$i] =~ $pattern ) {
116 0 0         if ( ref $patterns->{$pattern} ) {
117 0           for my $subpattern ( keys %{ $patterns->{$pattern} } ) {
  0            
118 0 0         if ( $title->[$i] =~ $subpattern ) {
119 0           $title->[$i] = $patterns->{$pattern}{$subpattern};
120 0           last;
121             }
122             }
123             } else {
124 0           $title->[$i] = $patterns->{$pattern};
125             }
126             }
127             }
128             }
129             } ## end sub _order_data_by_patterns($$)
130              
131             sub _order_data_by_fields($$$$) {
132 0     0     my ( $data, $title, $fields, $rows ) = @_;
133 0           my @res = ();
134              
135 0           for my $field (@$fields) {
136 0           for ( my $i = 0 ; $title->[$i] ; $i++ ) {
137 0 0         if ( $title->[$i] eq $field ) {
138 0           push @res, $i;
139 0           last;
140             }
141             }
142             }
143              
144 0           for my $row (@$rows) {
145 0           push @$data, { map { ( $title->[$_] => $row->[$_] ) } @res };
  0            
146             }
147             }
148              
149             sub _order_data_with_substitute($$$$) {
150 0     0     my ( $data, $title, $substitute, $rows ) = @_;
151 0           my $pattern = join '|', map { quotemeta $_ } keys %$substitute;
  0            
152              
153 0           $_ =~ s/^($pattern)$/$substitute->{$1}/ for @$title;
154              
155 0           _order_data_by_fields( $data, $title, [ values %$substitute ], $rows );
156             }
157              
158             1;