sportdb-formats 1.1.6 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +53 -9
- 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 +130 -13
- 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
|
+
@clubs.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
|
+
@clubs.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
|
+
|