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.
Files changed (49) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +2 -0
  3. data/Manifest.txt +4 -25
  4. data/Rakefile +1 -1
  5. data/lib/sportdb/formats/country/country_reader.rb +142 -142
  6. data/lib/sportdb/formats/datafile.rb +59 -59
  7. data/lib/sportdb/formats/event/event_reader.rb +184 -183
  8. data/lib/sportdb/formats/goals.rb +37 -1
  9. data/lib/sportdb/formats/ground/ground_reader.rb +289 -0
  10. data/lib/sportdb/formats/league/league_reader.rb +152 -168
  11. data/lib/sportdb/formats/lines_reader.rb +47 -0
  12. data/lib/sportdb/formats/match/match_parser.rb +102 -12
  13. data/lib/sportdb/formats/match/match_parser_auto_conf.rb +270 -202
  14. data/lib/sportdb/formats/outline_reader.rb +0 -1
  15. data/lib/sportdb/formats/package.rb +394 -374
  16. data/lib/sportdb/formats/search/sport.rb +357 -0
  17. data/lib/sportdb/formats/search/world.rb +139 -0
  18. data/lib/sportdb/formats/team/club_index_history.rb +134 -134
  19. data/lib/sportdb/formats/team/club_reader.rb +318 -350
  20. data/lib/sportdb/formats/team/club_reader_history.rb +203 -203
  21. data/lib/sportdb/formats/team/wiki_reader.rb +108 -108
  22. data/lib/sportdb/formats/version.rb +4 -7
  23. data/lib/sportdb/formats.rb +60 -27
  24. metadata +13 -35
  25. data/lib/sportdb/formats/country/country_index.rb +0 -192
  26. data/lib/sportdb/formats/event/event_index.rb +0 -141
  27. data/lib/sportdb/formats/league/league_index.rb +0 -178
  28. data/lib/sportdb/formats/team/club_index.rb +0 -338
  29. data/lib/sportdb/formats/team/national_team_index.rb +0 -114
  30. data/lib/sportdb/formats/team/team_index.rb +0 -43
  31. data/test/helper.rb +0 -132
  32. data/test/test_club_index.rb +0 -183
  33. data/test/test_club_index_history.rb +0 -107
  34. data/test/test_club_reader.rb +0 -201
  35. data/test/test_club_reader_history.rb +0 -212
  36. data/test/test_club_reader_props.rb +0 -54
  37. data/test/test_country_index.rb +0 -63
  38. data/test/test_country_reader.rb +0 -89
  39. data/test/test_datafile.rb +0 -30
  40. data/test/test_datafile_package.rb +0 -46
  41. data/test/test_goals.rb +0 -113
  42. data/test/test_league_index.rb +0 -157
  43. data/test/test_league_outline_reader.rb +0 -55
  44. data/test/test_league_reader.rb +0 -72
  45. data/test/test_outline_reader.rb +0 -31
  46. data/test/test_package.rb +0 -78
  47. data/test/test_package_match.rb +0 -102
  48. data/test/test_regex.rb +0 -67
  49. 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
+