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   28977 use 5.008;
  6         23  
4 6     6   32 use strict;
  6         13  
  6         170  
5 6     6   30 use warnings FATAL => 'all';
  6         14  
  6         298  
6              
7 6     6   38 use Exporter;
  6         12  
  6         261  
8 6     6   32 use Carp;
  6         16  
  6         484  
9 6     6   10074 use LWP::UserAgent;
  6         418568  
  6         210  
10 6     6   4768 use IO::File;
  6         66663  
  6         884  
11 6     6   23365 use IO::String;
  6         16677  
  6         229  
12 6     6   14678 use Archive::Zip qw( :ERROR_CODES :CONSTANTS );
  6         619700  
  6         1200  
13 6     6   5698 use Archive::Zip::MemberRead;
  6         9489  
  6         192  
14              
15 6     6   3375 use Chess::FIDE::Player;
  6         16  
  6         12132  
16              
17             our @ISA = qw(Exporter);
18              
19             our $VERSION = '1.20';
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 57829 my $class = shift;
29 6         32 my %param = @_;
30              
31 6         35 my $fide = {
32             meta => {},
33             players => [],
34             };
35 6         15 my $line;
36              
37 6         15 bless $fide, $class;
38 6 100 66     69 if ($param{-file} || $param{-www}) {
39 3         26 my $result = $fide->load(%param);
40 3 50       280 return 0 unless $result;
41             }
42             else {
43 3 50       14 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 7 my $fide = shift;
51 3         13 my %param = @_;
52 3 50       15 if ($param{-file}) {
    0          
53 3         41 my $fh = IO::File->new($param{-file}, 'r');
54 3 50       607 if (defined $fh) {
55 3 50       21 print "Loading $param{-file}...\n" if $ENV{CHESS_FIDE_VERBOSE};
56 3         19 $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 18 my $fide = shift;
97              
98 12 100       94 $fide->{meta}{sgm} = delete $fide->{meta}{game} if $fide->{meta}{game};
99 12   66     204 $fide->{meta}{bday} = delete $fide->{meta}{born} || delete $fide->{meta}{'b-day'};
100             }
101              
102             sub parseHeader ($$) {
103              
104 12     12 1 14820 my $fide = shift;
105 12         23 my $header = shift;
106              
107 12         34 chomp $header;
108 12         40 $header = lc $header;
109 12         57 $header =~ s/id number/id_number/;
110 12         45 $header =~ s/titlfed/tit fed/;
111 12         33 $header =~ s/gamesborn/game born/;
112 12         19 my $last_field;
113             my $last_start;
114 12         83 while($header =~ /(\S+)/gc) {
115 156         277 my $field = lc $1;
116 156         202 my $lf = length($field);
117 156 100       361 if ($field =~ /^\D\D\D\d\d$/) {
118 6         12 $field = 'srtng';
119             }
120 156         190 my $pos = pos($header);
121 156         189 my $start = $pos - $lf;
122 156 100       289 if ($start) {
123 144         416 $fide->{meta}{$last_field} = [ $last_start, $start - $last_start ];
124             }
125 156         209 $last_field = $field;
126 156         504 $last_start = $start;
127             }
128 12         47 $fide->{meta}{$last_field} = [ $last_start, length($header) - $last_start ];
129 12         33 $fide->{meta}{id} = delete $fide->{meta}{id_number};
130 12         38 $fide->convertOldHeaderNames();
131             }
132              
133             sub parseName ($$) {
134              
135 30013     30013 1 41234 my $fide = shift;
136 30013         32990 my $info = shift;
137 30013 100       68055 return unless $info->{name};
138 30009         47569 $info->{fidename} = $info->{name};
139 30009         57728 $info->{name} =~ s/^\W+//;
140 30009         54304 $info->{name} =~ s/\, Dr\.//;
141 30009 100       147491 if ($info->{name} =~ /^(\S.*)\s*\,\s*(\S.*)/) {
    100          
142 26217         61202 $info->{givenname} = $2;
143 26217         53454 $info->{surname} = $1;
144 26217         75495 $info->{name} = "$info->{givenname} $info->{surname}";
145             }
146             elsif ($info->{name} =~ /^(\S.*\S)\s+(\S+)$/) {
147 3643         8513 $info->{givenname} = $1;
148 3643         7203 $info->{surname} = $2;
149 3643         10446 $info->{name} = "$info->{givenname} $info->{surname}";
150             }
151             else {
152 149 50       1425 warn "Strange name $info->{name}, assuming both given and sur" if $ENV{CHESS_FIDE_VERBOSE};
153 149         405 $info->{givenname} = $info->{surname} = $info->{name};
154             }
155             }
156              
157             sub parseLine ($$) {
158              
159 30004     30004 1 37837 my $fide = shift;
160 30004         40396 my $line = shift;
161              
162 30004         38779 chomp $line;
163 30004         43240 my %info = ();
164 30004         36795 my $orig_line = $line;
165 30004         31156 for my $field (keys %{$fide->{meta}}) {
  30004         123110  
166 340062         420202 $line = $orig_line;
167 340062 100       757545 if (length($line) <= $fide->{meta}{$field}[0]-1) {
168 4         8 $info{$field} = '';
169 4         11 next;
170             }
171             my $value = $fide->{meta}{$field}[0] ?
172             substr($line, $fide->{meta}{$field}[0]-1, $fide->{meta}{$field}[1]) :
173 340058 100       894673 substr($line, $fide->{meta}{$field}[0], $fide->{meta}{$field}[1]-1);
174 340058         848984 $value =~ s/^\s+//;
175 340058         616852 $value =~ s/\s+$//;
176 340058         487517 $value =~ s/\s+/ /g;
177 340058         704563 $info{$field} = $value;
178             }
179 30004         95642 $fide->parseName(\%info);
180 30004         337217 return %info;
181             }
182              
183             sub parseFile ($$) {
184              
185 3     3 1 7 my $fide = shift;
186 3         8 my $fh = shift;
187              
188 3         6 my $line;
189 3         7 my $l = 0;
190 3         203 while (defined($line = $fh->getline())) {
191 30000         850363 $l++;
192 30000 100       160739 if ($line =~ /^id/i) {
    50          
    50          
193 3         144 $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         64397 my %info = $fide->parseLine($line);
201 29997 100 66     217888 if ($info{name} &&$info{name} =~ /\S/) {
202 29995         142060 my $player = Chess::FIDE::Player->new(%info);
203 29995 50       96999 push(@{$fide->{players}}, $player) if $player;
  29995         794572  
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         139 $fh->close();
211             }
212              
213             sub fideSearch ($$;$) {
214              
215 2     2 1 988 my $fide = shift;
216 2         5 my $criteria = shift;
217 2   33     14 my $players = shift || $fide->{players};
218              
219 2         6 my $found = 0;
220 2         18 for my $field (keys %FIDE_defaults) {
221 21 100       197 if ($criteria =~ /^$field /i) {
222 2         47 $criteria =~ s/^($field)/'$_->{'.lc($field).'}'/gei;
  2         15  
223 2         8 $found = 1;
224 2         7 last;
225             }
226             }
227 2 50       11 die "Invalid criteria $criteria supplied" unless $found;
228             my @found_players = grep {
229 19998         978718 eval $criteria
230 2         5 } @{$players};
  2         198  
231 2         16 @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__