File Coverage

blib/lib/Chess/ELO/FEDA.pm
Criterion Covered Total %
statement 45 154 29.2
branch 13 80 16.2
condition 3 6 50.0
subroutine 11 20 55.0
pod 5 6 83.3
total 77 266 28.9


line stmt bran cond sub pod time code
1             package Chess::ELO::FEDA;
2             $Chess::ELO::FEDA::VERSION = '0.03';
3 2     2   1071 use strict;
  2         5  
  2         61  
4 2     2   13 use warnings;
  2         4  
  2         49  
5              
6 2     2   2076 use DBI;
  2         26934  
  2         152  
7 2     2   923 use DBD::CSV;
  2         356004  
  2         76  
8 2     2   1158 use DBD::SQLite;
  2         11690  
  2         79  
9 2     2   620 use File::Spec::Functions;
  2         1267  
  2         176  
10 2     2   909 use HTTP::Tiny;
  2         65703  
  2         96  
11 2     2   1111 use IO::Uncompress::Unzip qw/$UnzipError/;
  2         83267  
  2         230  
12 2     2   1261 use Spreadsheet::ParseExcel;
  2         72457  
  2         118  
13 2     2   28 use Encode qw/decode/;
  2         4  
  2         3052  
14              
15             # ABSTRACT: Download FEDA ELO (L) into differents backends (SQLite)
16              
17              
18             sub new {
19 5     5 1 1744 my $class = shift;
20 5         16 my %args = @_;
21 5         40 my $self = {-verbose=>0, -url=>'', -target=>'', -ext=>'', -path=>''};
22 5 100       16 $self->{-path} = $args{-path} if exists $args{-path};
23 5 100       13 $self->{-target} = $args{-target} if exists $args{-target};
24 5 50       16 $self->{-url} = $args{-url} if exists $args{-url};
25 5 50       7 $self->{-verbose} = $args{-verbose} if exists $args{-verbose};
26 5 50       7 $self->{-callback} = $args{-callback} if exists $args{-callback};
27              
28 5 100       33 if( $self->{-target} =~ m!(\w+)$! ) {
29 4         12 $self->{-ext} = lc( $1 );
30             }
31            
32 5 100 100     20 if( ($self->{-ext} ne 'sqlite') && ($self->{-ext} ne 'csv') ) {
33 2         30 die "Unsupported target: [" . $self->{-ext} . "]";
34             }
35              
36 3 100       99 unless(-d $self->{-path} ) {
37 1         11 die "Invalid path: [" . $self->{-path} . "]";
38             }
39 2         15 bless $self, $class;
40             }
41              
42             #-------------------------------------------------------------------------------
43              
44              
45             sub cleanup {
46 0     0 1   my $self = shift;
47 0 0         if( -e $self->{-xls} ) {
48 0 0         $self->{-verbose} and print "+ remove xls file: ", $self->{-xls}, "\n";
49 0           unlink $self->{-xls};
50             }
51             }
52              
53             #-------------------------------------------------------------------------------
54              
55              
56             sub download {
57 0     0 1   my $self = shift;
58            
59 0           my $target_filename = catfile($self->{-path}, $self->{-target});
60 0           my $zip_filename = $target_filename . '.zip';
61 0           my $xls_filename = $target_filename . '.xls';
62              
63             ##my $response = {content=>'', status=>200, reason=>'OK'};
64 0           my $response = HTTP::Tiny->new->get($self->{-url});
65 0 0         die "GET [$self->{-url}] failed" unless $response->{success};
66              
67 0 0         if( length $response->{content} ) {
68 0 0         open my $fhz, ">", $zip_filename or die "Cannot open file: $zip_filename";
69 0           binmode $fhz;
70 0           print $fhz $response->{content} ;
71 0           close $fhz;
72             }
73 0 0         print "+ Download: ", $zip_filename, " => [", $response->{status}, "]: ", $response->{reason}, "\n" if $self->{-verbose};
74 0           $self->_extract_file_from_zip($xls_filename, $zip_filename, qr!\.xls$!i);
75 0           unlink $zip_filename;
76 0 0         print "+ Unzip: ", $xls_filename, "\n" if $self->{-verbose};
77 0           $self->{-xls} = $xls_filename;
78 0 0         return (-e $self->{-xls}) ? 1 : 0;
79             }
80              
81             #-------------------------------------------------------------------------------
82              
83              
84             sub parse {
85 0     0 1   my $self = shift;
86            
87 0           my $rc = 0;
88              
89 0 0         if( $self->{-ext} eq 'sqlite' ) {
    0          
90 0           $rc = $self->_parse_sqlite;
91             }
92             elsif( $self->{-ext} eq 'csv' ) {
93 0           $rc = $self->_parse_csv;
94             }
95             else {
96 0           die "Unsupported target. Not in [sqlite, csv]";
97             }
98              
99 0           return $rc;
100             }
101              
102             #-------------------------------------------------------------------------------
103              
104              
105             sub run {
106 0     0 1   my $self = shift;
107 0           my $rc = 0;
108              
109 0 0         if( $self->download() ) {
110 0           $rc = $self->parse;
111 0           $self->cleanup;
112             }
113              
114 0           return $rc;
115             }
116              
117             #-------------------------------------------------------------------------------
118              
119             sub _extract_file_from_zip {
120 0     0     my ($self, $xlsfile, $zipfile, $regexpr_file_to_extract) = @_;
121 0 0         my $u = new IO::Uncompress::Unzip $zipfile or die "Cannot open $zipfile: $UnzipError";
122 0           my $filename = undef;
123              
124 0           for( my $status = 1; $status > 0; $status = $u->nextStream() )
125             {
126 0           my $name = $u->getHeaderInfo()->{Name};
127 0 0         next unless $name =~ $regexpr_file_to_extract;
128            
129 0           my $buff;
130 0 0         open my $fh, '>', $xlsfile or die "Couldn't write to $name: $!";
131 0           binmode $fh;
132 0           while( ($status = $u->read($buff)) > 0 ) {
133 0           syswrite $fh, $buff;
134             }
135 0           close $fh;
136 0           $filename = $name;
137 0           last;
138             }
139            
140 0 0         return ($filename) ? $xlsfile . "/$filename" : undef;
141             }
142              
143             #-------------------------------------------------------------------------------
144              
145             sub _parse_sqlite {
146 0     0     my $self = shift;
147            
148 0           $self->{-dbfile} = catfile($self->{-path}, $self->{-target});
149 0 0         $self->{-verbose} and print "+ DB File: ", $self->{-dbfile}, "\n";
150 0 0         unlink $self->{-dbfile} if -e $self->{-dbfile};
151              
152 0 0         my $dbh = DBI->connect("dbi:SQLite:dbname=" . $self->{-dbfile}, "", "", {
153             RaiseError=>1,
154             AutoCommit=>0
155             }) or die $DBI::errstr;
156 0           my $rc = $self->_parse_abstract_dbd($dbh);
157 0           $dbh->disconnect;
158 0           return $rc;
159             }
160              
161             #-------------------------------------------------------------------------------
162              
163             sub _parse_csv {
164 0     0     my $self = shift;
165              
166 0           $self->{-dbfile} = catfile($self->{-path}, $self->{-target});
167 0 0         $self->{-verbose} and print "+ DB File: ", $self->{-dbfile}, "\n";
168 0 0         unlink $self->{-dbfile} if -e $self->{-dbfile};
169              
170             my $dbh = DBI->connect ("dbi:CSV:", "", "", {
171             f_schema => undef,
172             f_dir => $self->{-path},
173             f_encoding => "utf8",
174             csv_eol => "\n",
175             csv_sep_char => ",",
176             csv_quote_char => '"',
177             csv_escape_char => '"',
178             csv_class => "Text::CSV_XS",
179             csv_null => 1,
180             csv_always_quote => 1,
181             csv_tables => { elo_feda => { f_file => $self->{-target} } },
182 0 0         RaiseError => 1,
183             AutoCommit => 1
184             }) or die $DBI::errstr;
185 0           my $rc = $self->_parse_abstract_dbd($dbh);
186 0           $dbh->disconnect;
187 0           return $rc;
188             }
189              
190             #-------------------------------------------------------------------------------
191              
192             sub _parse_abstract_dbd {
193 0     0     my $self = shift;
194 0           my $dbh = shift;
195            
196 0           $dbh->do(qq/CREATE TABLE elo_feda(
197             feda_id integer primary key,
198             surname varchar(32) not null,
199             name varchar(32),
200             fed varchar(8),
201             rating integer,
202             games integer,
203             birth integer,
204             title varchar(16),
205             flag varchar(8)
206             )/ );
207 0 0         $self->{-verbose} and print "+ Load XLS: ", $self->{-xls}, "\n";
208              
209 0           my $parser = Spreadsheet::ParseExcel->new;
210             my $workbook = $parser->parse( $self->{-xls} )
211 0 0         or die $parser->error(), "\n";
212              
213 0           my $worksheet = $workbook->worksheet('ELO');
214 0           my ( $row_min, $row_max ) = $worksheet->row_range();
215              
216 0           my $START_XLS_ROW = 4;
217 0           my @player_keys = qw/feda_id name fed rating games birth title flag/;
218              
219             sub new_xls_player {
220 0     0 0   my ($worksheet, $stmt, $player_keys, $callback, $start_row, $stop_row, $verbose) = @_;
221 0           for my $row ( $start_row .. $stop_row ) {
222 0           my %feda_player;
223 0           for my $col_index( 0..7 ) {
224 0           my $cell = $worksheet->get_cell($row, $col_index);
225 0 0         my $value = $cell ? $cell->value : undef;
226 0           $feda_player{ $player_keys->[$col_index] } = $value;
227             }
228            
229             ##next if $feda_player{fed} ne 'CNT';
230            
231 0           $feda_player{name} = decode('latin1', $feda_player{name});
232            
233 0           my $name = $feda_player{name};
234 0           my ($apellidos, $nombre) = split / *, */, $name;
235              
236 0 0 0       if( $apellidos && $nombre ) {
    0          
    0          
237 0           $feda_player{surname} = $apellidos;
238 0           $feda_player{name} = $nombre;
239             }
240             elsif( index($name, '.') >= 0 ) {
241 0           ($apellidos, $nombre) = split / *\. */, $name;
242 0           $feda_player{surname} = $apellidos;
243 0           $feda_player{name} = $nombre;
244             }
245             elsif( $apellidos ) {
246 0           $feda_player{surname} = $apellidos;
247 0           $feda_player{name} = '***';
248             }
249 0           eval {
250             $stmt->execute(
251             $feda_player{feda_id},
252             $feda_player{surname},
253             $feda_player{name},
254             $feda_player{fed},
255             $feda_player{rating},
256             $feda_player{games},
257             $feda_player{birth},
258             $feda_player{title},
259             $feda_player{flag}
260 0           );
261 0 0         $callback and $callback->(\%feda_player);
262             };
263 0 0         if($@) {
264 0 0         $verbose and print "DB Error: $@", "\n";
265             }
266             }
267             }
268              
269 0           my $BLOCK_TXN = 2000;
270 0           my $i = $START_XLS_ROW;
271 0           my $j = $i + $BLOCK_TXN - 1;
272            
273 0           my $stmt = $dbh->prepare("insert into elo_feda (feda_id, surname, name, fed, rating, games, birth, title, flag) values (?,?,?,?,?,?,?,?,?)");
274 0           do {
275 0           new_xls_player($worksheet, $stmt, \@player_keys, $self->{-callback}, $i, $j, $self->{-verbose});
276 0 0         $dbh->commit unless $dbh->{AutoCommit};
277 0           $i += $BLOCK_TXN;
278 0 0         $j = ($i + $BLOCK_TXN -1) > $row_max ? $row_max : $i + $BLOCK_TXN - 1;
279             } while( $i <= $row_max );
280              
281 0           $stmt->finish;
282              
283 0           return 1;
284             }
285              
286             #-------------------------------------------------------------------------------
287              
288             1;
289              
290             __END__