sportdb-formats 1.1.6 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +2 -0
- data/Manifest.txt +4 -25
- data/Rakefile +1 -1
- data/lib/sportdb/formats/country/country_reader.rb +142 -142
- data/lib/sportdb/formats/datafile.rb +59 -59
- data/lib/sportdb/formats/event/event_reader.rb +184 -183
- data/lib/sportdb/formats/goals.rb +37 -1
- data/lib/sportdb/formats/ground/ground_reader.rb +289 -0
- data/lib/sportdb/formats/league/league_reader.rb +152 -168
- data/lib/sportdb/formats/lines_reader.rb +47 -0
- data/lib/sportdb/formats/match/match_parser.rb +102 -12
- data/lib/sportdb/formats/match/match_parser_auto_conf.rb +270 -202
- data/lib/sportdb/formats/outline_reader.rb +0 -1
- data/lib/sportdb/formats/package.rb +394 -374
- data/lib/sportdb/formats/search/sport.rb +357 -0
- data/lib/sportdb/formats/search/world.rb +139 -0
- data/lib/sportdb/formats/team/club_index_history.rb +134 -134
- data/lib/sportdb/formats/team/club_reader.rb +318 -350
- data/lib/sportdb/formats/team/club_reader_history.rb +203 -203
- data/lib/sportdb/formats/team/wiki_reader.rb +108 -108
- data/lib/sportdb/formats/version.rb +4 -7
- data/lib/sportdb/formats.rb +60 -27
- metadata +13 -35
- data/lib/sportdb/formats/country/country_index.rb +0 -192
- data/lib/sportdb/formats/event/event_index.rb +0 -141
- data/lib/sportdb/formats/league/league_index.rb +0 -178
- data/lib/sportdb/formats/team/club_index.rb +0 -338
- data/lib/sportdb/formats/team/national_team_index.rb +0 -114
- data/lib/sportdb/formats/team/team_index.rb +0 -43
- data/test/helper.rb +0 -132
- data/test/test_club_index.rb +0 -183
- data/test/test_club_index_history.rb +0 -107
- data/test/test_club_reader.rb +0 -201
- data/test/test_club_reader_history.rb +0 -212
- data/test/test_club_reader_props.rb +0 -54
- data/test/test_country_index.rb +0 -63
- data/test/test_country_reader.rb +0 -89
- data/test/test_datafile.rb +0 -30
- data/test/test_datafile_package.rb +0 -46
- data/test/test_goals.rb +0 -113
- data/test/test_league_index.rb +0 -157
- data/test/test_league_outline_reader.rb +0 -55
- data/test/test_league_reader.rb +0 -72
- data/test/test_outline_reader.rb +0 -31
- data/test/test_package.rb +0 -78
- data/test/test_package_match.rb +0 -102
- data/test/test_regex.rb +0 -67
- data/test/test_wiki_reader.rb +0 -77
@@ -0,0 +1,357 @@
|
|
1
|
+
###
|
2
|
+
# sport search service api for leagues, clubs and more
|
3
|
+
#
|
4
|
+
# core api is:
|
5
|
+
|
6
|
+
|
7
|
+
class SportSearch
|
8
|
+
|
9
|
+
class Search ## base search service - use/keep - why? why not?
|
10
|
+
def initialize( service ) @service = service; end
|
11
|
+
end # class Search
|
12
|
+
|
13
|
+
|
14
|
+
class PlayerSearch < Search
|
15
|
+
###################
|
16
|
+
## core required delegates - use delegate generator - why? why not?
|
17
|
+
def match_by( name:, country: nil, year: nil )
|
18
|
+
@service.match_by( name: name,
|
19
|
+
country: country,
|
20
|
+
year: year )
|
21
|
+
end
|
22
|
+
|
23
|
+
###############
|
24
|
+
### more deriv support functions / helpers
|
25
|
+
def match( name ) match_by( name: name ); end
|
26
|
+
## add more here - why? why not?
|
27
|
+
end # class PlayerSearch
|
28
|
+
|
29
|
+
|
30
|
+
class LeagueSearch < Search
|
31
|
+
###################
|
32
|
+
## core required delegates - use delegate generator - why? why not?
|
33
|
+
def match_by( name:, country: nil )
|
34
|
+
@service.match_by( name: name,
|
35
|
+
country: country )
|
36
|
+
end
|
37
|
+
|
38
|
+
###############
|
39
|
+
### more deriv support functions / helpers
|
40
|
+
def match( name ) match_by( name: name ); end
|
41
|
+
|
42
|
+
def find!( name )
|
43
|
+
league = find( name )
|
44
|
+
if league.nil?
|
45
|
+
puts "** !!! ERROR - no league match found for >#{name}<, add to leagues table; sorry"
|
46
|
+
exit 1
|
47
|
+
end
|
48
|
+
league
|
49
|
+
end
|
50
|
+
|
51
|
+
def find( name )
|
52
|
+
league = nil
|
53
|
+
recs = match( name )
|
54
|
+
# pp m
|
55
|
+
|
56
|
+
if recs.empty?
|
57
|
+
## fall through/do nothing
|
58
|
+
elsif recs.size > 1
|
59
|
+
puts "** !!! ERROR - ambigious league name; too many leagues (#{recs.size}) found:"
|
60
|
+
pp recs
|
61
|
+
exit 1
|
62
|
+
else
|
63
|
+
league = recs[0]
|
64
|
+
end
|
65
|
+
|
66
|
+
league
|
67
|
+
end
|
68
|
+
end # class LeagueSearch
|
69
|
+
|
70
|
+
|
71
|
+
class GroundSearch < Search
|
72
|
+
###################
|
73
|
+
## core required delegates - use delegate generator - why? why not?
|
74
|
+
def match_by( name:, country: nil, city: nil )
|
75
|
+
@service.match_by( name: name,
|
76
|
+
country: country,
|
77
|
+
city: city )
|
78
|
+
end
|
79
|
+
|
80
|
+
###############
|
81
|
+
### more deriv support functions / helpers
|
82
|
+
def match( name ) match_by( name: name ); end
|
83
|
+
## add more here - why? why not?
|
84
|
+
end # class GroundSearch
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
class NationalTeamSearch < Search
|
89
|
+
###################
|
90
|
+
## core required delegates - use delegate generator - why? why not?
|
91
|
+
|
92
|
+
## changle core api to match( q ) only - why? why not?
|
93
|
+
## and make find and find! derivs???
|
94
|
+
def find( q ) @service.find( q ); end
|
95
|
+
def find!( q ) @service.find!( q ); end
|
96
|
+
end # class NationalTeamSearch
|
97
|
+
|
98
|
+
|
99
|
+
class ClubSearch < Search
|
100
|
+
###################
|
101
|
+
## core required delegates - use delegate generator - why? why not?
|
102
|
+
|
103
|
+
## add mods here - why? why not?
|
104
|
+
|
105
|
+
def match_by( name:, country: nil,
|
106
|
+
league: nil,
|
107
|
+
mods: nil )
|
108
|
+
## for now assume "global" mods - checks only for name
|
109
|
+
##
|
110
|
+
if mods && mods[ name ]
|
111
|
+
club = mods[ name ]
|
112
|
+
return [club] # note: wrap (single record) in array
|
113
|
+
end
|
114
|
+
|
115
|
+
## note: add "auto-magic" country calculation via league record
|
116
|
+
## if league is a national league for football clubs
|
117
|
+
if league
|
118
|
+
raise ArgumentError, "match_by - league AND country NOT supported; sorry" if country
|
119
|
+
### find countries via league
|
120
|
+
### support league.intl? too - why? why not?
|
121
|
+
### or only nationa league
|
122
|
+
raise ArgumentError, "match_by - league - only national club leagues supported (not int'l or national teams for now); sorry" unless league.national? && league.clubs?
|
123
|
+
|
124
|
+
### calc countries
|
125
|
+
### uses "global" func in sports-catalogs for now
|
126
|
+
## move code here - why? why not?
|
127
|
+
country = find_countries_for_league( league )
|
128
|
+
@service.match_by( name: name,
|
129
|
+
country: country )
|
130
|
+
else
|
131
|
+
@service.match_by( name: name,
|
132
|
+
country: country )
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
## todo/fix/check: use rename to find_canon or find_canonical() or something??
|
138
|
+
## remove (getting used?) - why? why not?
|
139
|
+
# def []( name ) ## lookup by canoncial name only; todo/fix: add find alias why? why not?
|
140
|
+
# puts "WARN!! do not use ClubIndex#[] for lookup >#{name}< - will get removed!!!"
|
141
|
+
# @clubs[ name ]
|
142
|
+
# end
|
143
|
+
|
144
|
+
|
145
|
+
###############
|
146
|
+
### more deriv support functions / helpers
|
147
|
+
def match( name ) match_by( name: name ); end
|
148
|
+
|
149
|
+
##########
|
150
|
+
# "legacy" finders - return zero or one club
|
151
|
+
## (if more than one match, exit/raise error/exception)
|
152
|
+
def find( name ) find_by( name: name ); end
|
153
|
+
def find!( name ) find_by!( name: name ); end
|
154
|
+
|
155
|
+
## find - always returns a single record / match or nil
|
156
|
+
## if there is more than one match than find aborts / fails
|
157
|
+
def find_by!( name:, country: nil,
|
158
|
+
league: nil ) ## todo/fix: add international or league flag?
|
159
|
+
club = find_by( name: name,
|
160
|
+
country: country,
|
161
|
+
league: league )
|
162
|
+
|
163
|
+
if club.nil?
|
164
|
+
puts "** !!! ERROR - no match for club >#{name}<"
|
165
|
+
exit 1
|
166
|
+
end
|
167
|
+
|
168
|
+
club
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
def find_by( name:, country: nil,
|
173
|
+
league: nil ) ## todo/fix: add international or league flag?
|
174
|
+
## note: allow passing in of country key too (auto-counvert)
|
175
|
+
## and country struct too
|
176
|
+
## - country assumes / allows the country key or fifa code for now
|
177
|
+
recs = match_by( name: name,
|
178
|
+
country: country,
|
179
|
+
league: league )
|
180
|
+
|
181
|
+
club = nil
|
182
|
+
if recs.empty?
|
183
|
+
## puts "** !!! WARN !!! no match for club >#{name}<"
|
184
|
+
elsif recs.size > 1
|
185
|
+
puts "** !!! ERROR - too many matches (#{recs.size}) for club >#{name}<:"
|
186
|
+
pp recs
|
187
|
+
exit 1
|
188
|
+
else # bingo; match - assume size == 1
|
189
|
+
club = recs[0]
|
190
|
+
end
|
191
|
+
|
192
|
+
club
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
#######
|
197
|
+
# more support methods
|
198
|
+
def build_mods( mods )
|
199
|
+
## e.g.
|
200
|
+
## { 'Arsenal | Arsenal FC' => 'Arsenal, ENG',
|
201
|
+
## 'Liverpool | Liverpool FC' => 'Liverpool, ENG',
|
202
|
+
## 'Barcelona' => 'Barcelona, ESP',
|
203
|
+
## 'Valencia' => 'Valencia, ESP' }
|
204
|
+
|
205
|
+
mods.reduce({}) do |h,(club_names, club_line)|
|
206
|
+
|
207
|
+
values = club_line.split( ',' )
|
208
|
+
values = values.map { |value| value.strip } ## strip all spaces
|
209
|
+
|
210
|
+
## todo/fix: make sure country is present !!!!
|
211
|
+
club_name, country_name = values
|
212
|
+
club = find_by!( name: club_name, country: country_name )
|
213
|
+
|
214
|
+
values = club_names.split( '|' )
|
215
|
+
values = values.map { |value| value.strip } ## strip all spaces
|
216
|
+
|
217
|
+
values.each do |club_name|
|
218
|
+
h[club_name] = club
|
219
|
+
end
|
220
|
+
h
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end # class ClubSearch
|
224
|
+
|
225
|
+
|
226
|
+
|
227
|
+
## todo/check - change to EventInfoSearch - why? why not?
|
228
|
+
class EventSearch < Search
|
229
|
+
##
|
230
|
+
## todo - eventinfo search still open / up for change
|
231
|
+
|
232
|
+
###################
|
233
|
+
## core required delegates - use delegate generator - why? why not?
|
234
|
+
def seasons( league )
|
235
|
+
@service.seasons( league )
|
236
|
+
end
|
237
|
+
def find_by( league:, season: )
|
238
|
+
@service.find_by( league: league,
|
239
|
+
season: season )
|
240
|
+
end
|
241
|
+
end # class EventSearch
|
242
|
+
|
243
|
+
|
244
|
+
####
|
245
|
+
## virtual table for season lookup
|
246
|
+
## note - use EventSeaon to avoid name conflict with (global) Season class
|
247
|
+
## find a better name SeasonInfo or SeasonFinder or SeasonStore
|
248
|
+
## or SeasonQ or ??
|
249
|
+
class EventSeasonSearch
|
250
|
+
def initialize( events: )
|
251
|
+
@events = events
|
252
|
+
end
|
253
|
+
|
254
|
+
###############
|
255
|
+
## todo/fix: find a better algo to guess season for date!!!
|
256
|
+
##
|
257
|
+
def find_by( date:, league: )
|
258
|
+
date = Date.strptime( date, '%Y-%m-%d' ) if date.is_a?( String )
|
259
|
+
|
260
|
+
infos = @events.seasons( league )
|
261
|
+
|
262
|
+
infos.each do |info|
|
263
|
+
return info.season if info.include?( date )
|
264
|
+
end
|
265
|
+
nil
|
266
|
+
end
|
267
|
+
end # class EventSeasonSearch
|
268
|
+
|
269
|
+
|
270
|
+
######
|
271
|
+
### add virtual team search ( clubs + national teams)
|
272
|
+
## note: no record base!!!!!
|
273
|
+
class TeamSearch
|
274
|
+
## note: "virtual" index lets you search clubs and/or national_teams (don't care)
|
275
|
+
|
276
|
+
def initialize( clubs:, national_teams: )
|
277
|
+
@clubs = clubs
|
278
|
+
@national_teams = national_teams
|
279
|
+
end
|
280
|
+
|
281
|
+
## todo/check: rename to/use map_by! for array version - why? why not?
|
282
|
+
def find_by!( name:, league:, mods: nil )
|
283
|
+
if name.is_a?( Array )
|
284
|
+
recs = []
|
285
|
+
name.each do |q|
|
286
|
+
recs << _find_by!( name: q, league: league, mods: mods )
|
287
|
+
end
|
288
|
+
recs
|
289
|
+
else ## assume single name
|
290
|
+
_find_by!( name: name, league: league, mods: mods )
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
|
295
|
+
def _find_by!( name:, league:, mods: nil )
|
296
|
+
if mods && mods[ league.key ] && mods[ league.key ][ name ]
|
297
|
+
mods[ league.key ][ name ]
|
298
|
+
else
|
299
|
+
if league.clubs?
|
300
|
+
if league.intl? ## todo/fix: add intl? to ActiveRecord league!!!
|
301
|
+
@club.find!( name )
|
302
|
+
else ## assume clubs in domestic/national league tournament
|
303
|
+
## note - search by league countries (may incl. more than one country
|
304
|
+
## e.g. us incl. ca, fr incl. mc, ch incl. li, etc.
|
305
|
+
@club.find_by!( name: name, league: league )
|
306
|
+
end
|
307
|
+
else ## assume national teams (not clubs)
|
308
|
+
@national_teams.find!( name )
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end # method _find_by!
|
312
|
+
end # class TeamSearch
|
313
|
+
|
314
|
+
|
315
|
+
|
316
|
+
def initialize( leagues:,
|
317
|
+
national_teams:,
|
318
|
+
clubs:,
|
319
|
+
grounds:,
|
320
|
+
events:,
|
321
|
+
players:
|
322
|
+
)
|
323
|
+
@leagues = LeagueSearch.new( leagues )
|
324
|
+
@national_teams = NationalTeamSearch.new( national_teams )
|
325
|
+
@clubs = ClubSearch.new( clubs )
|
326
|
+
@events = EventSearch.new( events )
|
327
|
+
|
328
|
+
@grounds = GroundSearch.new( grounds )
|
329
|
+
|
330
|
+
@players = PlayerSearch.new( players )
|
331
|
+
|
332
|
+
## virtual deriv ("composite") search services
|
333
|
+
@teams = TeamSearch.new( clubs: @clubs,
|
334
|
+
national_teams: @national_teams )
|
335
|
+
@event_seasons = EventSeasonSearch.new( events: @events )
|
336
|
+
|
337
|
+
end
|
338
|
+
|
339
|
+
def countries
|
340
|
+
puts
|
341
|
+
puts "[WARN] do NOT use catalog.countries, deprecated!!!"
|
342
|
+
puts " please, switch to new world.countries search service"
|
343
|
+
puts
|
344
|
+
exit 1
|
345
|
+
end
|
346
|
+
|
347
|
+
def leagues() @leagues; end
|
348
|
+
def national_teams() @national_teams; end
|
349
|
+
def clubs() @clubs; end
|
350
|
+
def events() @events; end
|
351
|
+
def grounds() @grounds; end
|
352
|
+
|
353
|
+
def players() @players; end
|
354
|
+
|
355
|
+
def teams() @teams; end ## note - virtual table
|
356
|
+
def seasons() @event_seasons; end ## note - virtual table
|
357
|
+
end # class SportSearch
|
@@ -0,0 +1,139 @@
|
|
1
|
+
###
|
2
|
+
# world search service api for countries and more
|
3
|
+
#
|
4
|
+
# core api is:
|
5
|
+
# - world.countries.find_by_code
|
6
|
+
# - .find_by_name
|
7
|
+
|
8
|
+
|
9
|
+
class WorldSearch
|
10
|
+
|
11
|
+
class CitySearch
|
12
|
+
def initialize( service ) @service = service; end
|
13
|
+
|
14
|
+
###################
|
15
|
+
## core required delegates - use delegate generator - why? why not?
|
16
|
+
def match_by( name: )
|
17
|
+
@service.match_by( name: name )
|
18
|
+
end
|
19
|
+
end # class CitySearch
|
20
|
+
|
21
|
+
|
22
|
+
class CountrySearch
|
23
|
+
def initialize( service ) @service = service; end
|
24
|
+
|
25
|
+
###################
|
26
|
+
## core required delegates - use delegate generator - why? why not?
|
27
|
+
def find_by_code( code ) @service.find_by_code( code ); end
|
28
|
+
def find_by_name( name ) @service.find_by_name( name ); end
|
29
|
+
|
30
|
+
|
31
|
+
###############
|
32
|
+
### more deriv support functions / helpers
|
33
|
+
def find( q )
|
34
|
+
country = find_by_code( q )
|
35
|
+
country = find_by_name( q ) if country.nil? ## try lookup / find by (normalized) name
|
36
|
+
country
|
37
|
+
end
|
38
|
+
alias_method :[], :find ### keep shortcut - why? why not?
|
39
|
+
|
40
|
+
###
|
41
|
+
## split/parse country line
|
42
|
+
##
|
43
|
+
## split on bullet e.g.
|
44
|
+
## split into name and code with regex - make code optional
|
45
|
+
##
|
46
|
+
## Examples:
|
47
|
+
## Österreich • Austria (at)
|
48
|
+
## Österreich • Austria
|
49
|
+
## Austria
|
50
|
+
## Deutschland (de) • Germany
|
51
|
+
##
|
52
|
+
## todo/check: support more formats - why? why not?
|
53
|
+
## e.g. Austria, AUT (e.g. with comma - why? why not?)
|
54
|
+
def parse( line )
|
55
|
+
values = line.split( '•' ) ## use/support multi-lingual separator
|
56
|
+
country = nil
|
57
|
+
values.each do |value|
|
58
|
+
value = value.strip
|
59
|
+
## check for trailing country code e.g. (at), (eng), etc.
|
60
|
+
if value =~ /[ ]+\((?<code>[a-z]{1,4})\)$/ ## e.g. Austria (at)
|
61
|
+
code = $~[:code]
|
62
|
+
name = value[0...(value.size-code.size-2)].strip ## note: add -2 for brackets
|
63
|
+
candidates = [ find_by_code( code ), find_by_name( name ) ]
|
64
|
+
if candidates[0].nil?
|
65
|
+
puts "** !!! ERROR !!! country - unknown code >#{code}< in line: #{line}"
|
66
|
+
pp line
|
67
|
+
exit 1
|
68
|
+
end
|
69
|
+
if candidates[1].nil?
|
70
|
+
puts "** !!! ERROR !!! country - unknown name >#{code}< in line: #{line}"
|
71
|
+
pp line
|
72
|
+
exit 1
|
73
|
+
end
|
74
|
+
if candidates[0] != candidates[1]
|
75
|
+
puts "** !!! ERROR !!! country - name and code do NOT match the same country:"
|
76
|
+
pp line
|
77
|
+
pp candidates
|
78
|
+
exit 1
|
79
|
+
end
|
80
|
+
if country && country != candidates[0]
|
81
|
+
puts "** !!! ERROR !!! country - names do NOT match the same country:"
|
82
|
+
pp line
|
83
|
+
pp country
|
84
|
+
pp candidates
|
85
|
+
exit 1
|
86
|
+
end
|
87
|
+
country = candidates[0]
|
88
|
+
else
|
89
|
+
## just assume value is name or code
|
90
|
+
candidate = find( value )
|
91
|
+
if candidate.nil?
|
92
|
+
puts "** !!! ERROR !!! country - unknown name or code >#{value}< in line: #{line}"
|
93
|
+
pp line
|
94
|
+
exit 1
|
95
|
+
end
|
96
|
+
if country && country != candidate
|
97
|
+
puts "** !!! ERROR !!! country - names do NOT match the same country:"
|
98
|
+
pp line
|
99
|
+
pp country
|
100
|
+
pp candidate
|
101
|
+
exit 1
|
102
|
+
end
|
103
|
+
country = candidate
|
104
|
+
end
|
105
|
+
end
|
106
|
+
country
|
107
|
+
end # method parse
|
108
|
+
end # class CountrySearch
|
109
|
+
|
110
|
+
|
111
|
+
def initialize( countries:, cities: )
|
112
|
+
## change service to country_service or such - why? why not?
|
113
|
+
## add city_service and such later
|
114
|
+
@countries = CountrySearch.new( countries )
|
115
|
+
@cities = CitySearch.new( cities )
|
116
|
+
end
|
117
|
+
|
118
|
+
####
|
119
|
+
# note: for now setup only for countries
|
120
|
+
def countries() @countries; end
|
121
|
+
def cities() @cities; end
|
122
|
+
end # class WorldSearch
|
123
|
+
|
124
|
+
|
125
|
+
|
126
|
+
|
127
|
+
|
128
|
+
class DummyCountrySearch
|
129
|
+
def find_by_code( code )
|
130
|
+
puts "[WARN] no world search configured; cannot find country by code"
|
131
|
+
nil
|
132
|
+
end
|
133
|
+
def find_by_name( name )
|
134
|
+
puts "[WARN] no world search configured; cannot find country by name"
|
135
|
+
nil
|
136
|
+
end
|
137
|
+
end # class DummyCountrySearch
|
138
|
+
|
139
|
+
|