File Coverage

blib/lib/Chess/FIDE.pm
Criterion Covered Total %
statement 136 179 75.9
branch 34 60 56.6
condition 7 19 36.8
subroutine 19 21 90.4
pod 10 10 100.0
total 206 289 71.2


line stmt bran cond sub pod time code
1             package Chess::FIDE;
2              
3 6     6   6427 use 5.008;
  6         20  
4 6     6   25 use strict;
  6         8  
  6         125  
5 6     6   25 use warnings FATAL => 'all';
  6         10  
  6         193  
6              
7 6     6   24 use Exporter;
  6         9  
  6         193  
8 6     6   34 use Carp;
  6         10  
  6         408  
9 6     6   5514 use LWP::UserAgent;
  6         336803  
  6         196  
10 6     6   4436 use IO::File;
  6         57832  
  6         786  
11 6     6   4409 use IO::String;
  6         14756  
  6         194  
12 6     6   5972 use Archive::Zip qw( :ERROR_CODES :CONSTANTS );
  6         7339163  
  6         921  
13 6     6   4206 use Archive::Zip::MemberRead;
  6         7932  
  6         177  
14              
15 6     6   2709 use Chess::FIDE::Player;
  6         15  
  6         11980  
16              
17             our @ISA = qw(Exporter);
18              
19             our $VERSION = '1.21';
20              
21             our $DEFAULT_FIDE_URL = 'http://ratings.fide.com/download/players_list.zip';
22             our @FIDE_SEARCH_KEYS = sort keys %FIDE_defaults;
23              
24             our @EXPORT = qw($DEFAULT_FIDE_URL @FIDE_SEARCH_KEYS);
25              
26             sub new ($;@) {
27              
28 6     6 1 43184 my $class = shift;
29 6         23 my %param = @_;
30              
31 6         37 my $fide = {
32             meta => {},
33             players => [],
34             };
35 6         15 my $line;
36              
37 6         15 bless $fide, $class;
38 6 100 66     54 if ($param{-file} || $param{-www}) {
39 3         22 my $result = $fide->load(%param);
40 3 50       218 return 0 unless $result;
41             }
42             else {
43 3 50       16 warn "No source (-file or -www) given, empty object initialized" if $ENV{CHESS_FIDE_VERBOSE};
44             }
45 6         32 return $fide;
46             }
47              
48             sub load ($%) {
49              
50 3     3 1 6 my $fide = shift;
51 3         7 my %param = @_;
52 3 50       12 if ($param{-file}) {
    0          
53 3         36 my $fh = IO::File->new($param{-file}, 'r');
54 3 50       439 if (defined $fh) {
55 3 50       17 print "Loading $param{-file}...\n" if $ENV{CHESS_FIDE_VERBOSE};
56 3         16 $fide->parseFile($fh);
57             }
58             else {
59 0         0 warn "Couldn't read file $param{-file} $!: $param{-file}\n";
60 0         0 return {};
61             }
62             }
63             elsif ($param{-www}) {
64 0         0 my $ua = LWP::UserAgent->new();
65 0 0       0 $ua->proxy(['http'], $param{-proxy}) if $param{-proxy};
66 0   0     0 my $url = $param{-url} || $DEFAULT_FIDE_URL;
67 0 0       0 print "Trying to get $url...\n" if $ENV{CHESS_FIDE_VERBOSE};
68 0         0 my $response = $ua->get($url);
69 0         0 my $webcontent;
70 0 0       0 if ($response->is_success) {
71 0         0 $webcontent = $response->content();
72             }
73             else {
74 0         0 warn "Cannot download playerfile: Check your network connection\n";
75 0         0 return 0;
76             }
77 0 0       0 my $fh = IO::String->new(\$webcontent) or die "BLAAAH\n";
78 0         0 my $zip = Archive::Zip->new();
79 0         0 my $status = $zip->readFromFileHandle($fh);
80 0 0       0 unless ($status == AZ_OK) {
81 0         0 warn "Problems unzipping the downloaded file";
82 0         0 return 0;
83             }
84 0         0 my $membername;
85 0         0 for $membername ($zip->memberNames()) {
86 0         0 my $fh2 = Archive::Zip::MemberRead->new($zip, $membername);
87 0 0       0 return 0 unless defined $fh2;
88 0         0 $fide->parseFile($fh2);
89             }
90 0         0 $fh->close();
91             }
92             }
93              
94             sub convertOldHeaderNames ($) {
95              
96 12     12 1 17 my $fide = shift;
97              
98 12 100       43 $fide->{meta}{sgm} = delete $fide->{meta}{game} if $fide->{meta}{game};
99 12   66     125 $fide->{meta}{bday} = delete $fide->{meta}{born} || delete $fide->{meta}{'b-day'};
100             }
101              
102             sub parseHeader ($$) {
103              
104 12     12 1 19520 my $fide = shift;
105 12         21 my $header = shift;
106              
107 12         32 chomp $header;
108 12         35 $header = lc $header;
109 12         50 $header =~ s/id number/id_number/;
110 12         40 $header =~ s/titlfed/tit fed/;
111 12         27 $header =~ s/gamesborn/game born/;
112 12         14 my $last_field;
113             my $last_start;
114 12         75 while($header =~ /(\S+)/gc) {
115 156         263 my $field = lc $1;
116 156         175 my $lf = length($field);
117 156 100       298 if ($field =~ /^\D\D\D\d\d$/) {
118 6         10 $field = 'srtng';
119             }
120 156         177 my $pos = pos($header);
121 156         218 my $start = $pos - $lf;
122 156 100       255 if ($start) {
123 144         386 $fide->{meta}{$last_field} = [ $last_start, $start - $last_start ];
124             }
125 156         180 $last_field = $field;
126 156         489 $last_start = $start;
127             }
128 12         40 $fide->{meta}{$last_field} = [ $last_start, length($header) - $last_start ];
129 12         32 $fide->{meta}{id} = delete $fide->{meta}{id_number};
130 12         39 $fide->convertOldHeaderNames();
131             }
132              
133             sub parseName ($$) {
134              
135 30013     30013 1 45910 my $fide = shift;
136 30013         32414 my $info = shift;
137 30013 100       60284 return unless $info->{name};
138 30009         46492 $info->{fidename} = $info->{name};
139 30009         53186 $info->{name} =~ s/^\W+//;
140 30009         45417 $info->{name} =~ s/\, Dr\.//;
141 30009 100       139875 if ($info->{name} =~ /^(\S.*)\s*\,\s*(\S.*)/) {
    100          
142 26217         60675 $info->{givenname} = $2;
143 26217         50242 $info->{surname} = $1;
144 26217         72157 $info->{name} = "$info->{givenname} $info->{surname}";
145             }
146             elsif ($info->{name} =~ /^(\S.*\S)\s+(\S+)$/) {
147 3643         8869 $info->{givenname} = $1;
148 3643         7079 $info->{surname} = $2;
149 3643         10775 $info->{name} = "$info->{givenname} $info->{surname}";
150             }
151             else {
152 149 50       396 warn "Strange name $info->{name}, assuming both given and sur" if $ENV{CHESS_FIDE_VERBOSE};
153 149         368 $info->{givenname} = $info->{surname} = $info->{name};
154             }
155             }
156              
157             sub parseLine ($$) {
158              
159 30004     30004 1 35341 my $fide = shift;
160 30004         37136 my $line = shift;
161              
162 30004         34151 chomp $line;
163 30004         44128 my %info = ();
164 30004         34337 my $orig_line = $line;
165 30004         30371 for my $field (keys %{$fide->{meta}}) {
  30004         106141  
166 340062         385799 $line = $orig_line;
167 340062 100       782705 if (length($line) <= $fide->{meta}{$field}[0]-1) {
168 4         11 $info{$field} = '';
169 4         8 next;
170             }
171             my $value = $fide->{meta}{$field}[0] ?
172             substr($line, $fide->{meta}{$field}[0]-1, $fide->{meta}{$field}[1]) :
173 340058 100       918147 substr($line, $fide->{meta}{$field}[0], $fide->{meta}{$field}[1]-1);
174 340058         803024 $value =~ s/^\s+//;
175 340058         605071 $value =~ s/\s+$//;
176 340058         447386 $value =~ s/\s+/ /g;
177 340058         697423 $info{$field} = $value;
178             }
179 30004         89873 $fide->parseName(\%info);
180 30004         330358 return %info;
181             }
182              
183             sub parseFile ($$) {
184              
185 3     3 1 6 my $fide = shift;
186 3         6 my $fh = shift;
187              
188 3         4 my $line;
189 3         11 my $l = 0;
190 3         150 while (defined($line = $fh->getline())) {
191 30000         797621 $l++;
192 30000 100       153212 if ($line =~ /^id/i) {
    50          
    50          
193 3         13 $fide->parseHeader($line);
194             }
195             elsif ($line =~ /Mr., Jonathan Rose/) {
196             # bogus entry in the rating list
197 0         0 next;
198             }
199             elsif ($line =~ /^\s*\d/) {
200 29997         61970 my %info = $fide->parseLine($line);
201 29997 100 66     203974 if ($info{name} &&$info{name} =~ /\S/) {
202 29995         143740 my $player = Chess::FIDE::Player->new(%info);
203 29995 50       105651 push(@{$fide->{players}}, $player) if $player;
  29995         776978  
204             }
205             }
206             else {
207 0 0       0 warn "Line $l: $line - format not recognized, ignoring" if $ENV{CHESS_FIDE_VERBOSE};
208             }
209             }
210 3         117 $fh->close();
211             }
212              
213             sub fideSearch ($$;$) {
214              
215 2     2 1 888 my $fide = shift;
216 2         5 my $criteria = shift;
217 2   33     12 my $players = shift || $fide->{players};
218              
219 2         4 my $found = 0;
220 2         11 for my $field (keys %FIDE_defaults) {
221 34 100       228 if ($criteria =~ /^$field /i) {
222 2         40 $criteria =~ s/^($field)/'$_->{'.lc($field).'}'/gei;
  2         12  
223 2         5 $found = 1;
224 2         6 last;
225             }
226             }
227 2 50       9 die "Invalid criteria $criteria supplied" unless $found;
228             my @found_players = grep {
229 19998         781084 eval $criteria
230 2         4 } @{$players};
  2         165  
231 2         15 @found_players;
232             }
233              
234             sub dumpHeader ($) {
235              
236 0     0 1   my $fide = shift;
237              
238 0           my $header = '';
239 0           for my $field (sort { $fide->{meta}{$a}[0] <=> $fide->{meta}{$b}[0]} keys %{$fide->{meta}}) {
  0            
  0            
240 0           $header .= $field . (' ' x ($fide->{meta}{$field}->[1] - length($field)));
241             }
242 0           $header .= "\n";
243 0           $header;
244             }
245              
246             sub dumpPlayer ($$) {
247              
248 0     0 1   my $fide = shift;
249 0           my $player = shift;
250              
251 0           my $dump = '';
252 0           for my $field (sort { $fide->{meta}{$a}[0] <=> $fide->{meta}{$b}[0]} keys %{$fide->{meta}}) {
  0            
  0            
253 0   0       $dump .= ($player->$field || '') . (' ' x ($fide->{meta}{$field}->[1] - length($player->$field || '')));
      0        
254             }
255 0           $dump .= "\n";
256 0           $dump;
257             }
258             1;
259              
260             __END__