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