sportdb-formats 1.2.0 → 2.0.0

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.
@@ -0,0 +1,87 @@
1
+
2
+ module SportDb
3
+ module NameHelper
4
+
5
+
6
+ ## note: allow placeholder years to e.g. (-___) or (-????)
7
+ ## for marking missing (to be filled in) years
8
+ ## e.g. (1887-1911), (-2013),
9
+ ## (1946-2001, 2013-) etc.
10
+ ## todo/check: make more strict e.g. only accept 4-digit years? - why? why not?
11
+ YEAR_RE = %r{\(
12
+ [0-9, ?_-]+? # note: non-greedy (minimum/first) match
13
+ \)}x
14
+
15
+ def strip_year( name )
16
+ ## check for year(s) e.g. (1887-1911), (-2013),
17
+ ## (1946-2001, 2013-) etc.
18
+ ## todo/check: only sub once (not global) - why? why not?
19
+ name.gsub( YEAR_RE, '' ).strip
20
+ end
21
+
22
+ def has_year?( name ) name =~ YEAR_RE; end
23
+
24
+
25
+ LANG_RE = %r{\[
26
+ [a-z]{1,2} # note also allow single-letter [a] or [d] or [e] - why? why not?
27
+ \]}x
28
+ def strip_lang( name )
29
+ name.gsub( LANG_RE, '' ).strip
30
+ end
31
+
32
+ def has_lang?( name ) name =~ LANG_RE; end
33
+
34
+
35
+ def sanitize( name )
36
+ ## check for year(s) e.g. (1887-1911), (-2013),
37
+ ## (1946-2001,2013-) etc.
38
+ name = strip_year( name )
39
+ ## check lang codes e.g. [en], [fr], etc.
40
+ name = strip_lang( name )
41
+ name
42
+ end
43
+
44
+
45
+ ## note: also add (),’,− etc. e.g.
46
+ ## Estudiantes (LP) => Estudiantes LP
47
+ ## Saint Patrick’s Athletic FC => Saint Patricks Athletic FC
48
+ ## Myllykosken Pallo −47 => Myllykosken Pallo 47
49
+ ##
50
+ ## add & too!!
51
+ ## e.g. Brighton & Hove Albion => Brighton Hove Albion -- and others in England
52
+
53
+ NORM_RE = %r{
54
+ [.'’º/()&_−-]
55
+ }x # note: in [] dash (-) if last doesn't need to get escaped
56
+ ## note: remove all dots (.), dash (-), ', º, /, etc.
57
+ # . U+002E (46) - FULL STOP
58
+ # ' U+0027 (39) - APOSTROPHE
59
+ # ’ U+2019 (8217) - RIGHT SINGLE QUOTATION MARK
60
+ # º U+00BA (186) - MASCULINE ORDINAL INDICATOR
61
+ # / U+002F (47) - SOLIDUS
62
+ # ( U+0028 (40) - LEFT PARENTHESIS
63
+ # ) U+0029 (41) - RIGHT PARENTHESIS
64
+ # − U+2212 (8722) - MINUS SIGN
65
+ # - U+002D (45) - HYPHEN-MINUS
66
+
67
+ ## for norm(alizing) names
68
+ def strip_norm( name )
69
+ name.gsub( NORM_RE, '' )
70
+ end
71
+
72
+ def normalize( name )
73
+ # note: do NOT call sanitize here (keep normalize "atomic" for reuse)
74
+ name = strip_norm( name )
75
+ name = name.gsub( ' ', '' ) # note: also remove all spaces!!!
76
+
77
+ ## todo/check: use our own downcase - why? why not?
78
+ name = downcase_i18n( name ) ## do NOT care about upper and lowercase for now
79
+ name
80
+ end
81
+
82
+
83
+ def variants( name ) Variant.find( name ); end
84
+
85
+ end # module NameHelper
86
+ end # module SportDb
87
+
@@ -6,7 +6,7 @@
6
6
 
7
7
  class SportSearch
8
8
 
9
- class Search ## base search service - use/keep - why? why not?
9
+ class Search ## base search service - use/keep - why? why not?
10
10
  def initialize( service ) @service = service; end
11
11
  end # class Search
12
12
 
@@ -15,48 +15,62 @@ class PlayerSearch < Search
15
15
  ###################
16
16
  ## core required delegates - use delegate generator - why? why not?
17
17
  def match_by( name:, country: nil, year: nil )
18
- @service.match_by( name: name,
18
+ @service.match_by( name: name,
19
19
  country: country,
20
- year: year )
20
+ year: year )
21
21
  end
22
22
 
23
23
  ###############
24
24
  ### more deriv support functions / helpers
25
25
  def match( name ) match_by( name: name ); end
26
- ## add more here - why? why not?
26
+ ## add more here - why? why not?
27
27
  end # class PlayerSearch
28
28
 
29
29
 
30
30
  class LeagueSearch < Search
31
+
31
32
  ###################
32
33
  ## 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 )
34
+ def match_by( name: nil, code: nil, country: nil )
35
+ ## todo/fix upstream - remove "generic" match_by() - why? why not?
36
+ ###
37
+ if code && name.nil?
38
+ @service.match_by_code( code, country: country )
39
+ elsif name && code.nil?
40
+ @service.match_by_name( name, country: country )
41
+ else
42
+ raise ArgumentError, "LeagueSearch#match_by - one (and only one arg) required - code: or name:"
43
+ end
44
+ ## @service.match_by( name: name,
45
+ ## country: country )
36
46
  end
37
47
 
48
+ ## all-in-one query (name or code)
49
+ def match( q, country: nil )
50
+ @service.match_by_name_or_code( q, country: country )
51
+ end
52
+
53
+
38
54
  ###############
39
55
  ### more deriv support functions / helpers
40
- def match( name ) match_by( name: name ); end
41
-
42
- def find!( name )
43
- league = find( name )
56
+ def find!( q )
57
+ league = find( q )
44
58
  if league.nil?
45
- puts "** !!! ERROR - no league match found for >#{name}<, add to leagues table; sorry"
59
+ puts "** !!! ERROR - no league match found for >#{q}<, add to leagues table; sorry"
46
60
  exit 1
47
61
  end
48
62
  league
49
63
  end
50
64
 
51
- def find( name )
65
+ def find( q )
52
66
  league = nil
53
- recs = match( name )
67
+ recs = match( q )
54
68
  # pp m
55
69
 
56
70
  if recs.empty?
57
71
  ## fall through/do nothing
58
72
  elsif recs.size > 1
59
- puts "** !!! ERROR - ambigious league name; too many leagues (#{recs.size}) found:"
73
+ puts "** !!! ERROR - ambigious league query; too many leagues (#{recs.size}) found:"
60
74
  pp recs
61
75
  exit 1
62
76
  else
@@ -68,19 +82,20 @@ class LeagueSearch < Search
68
82
  end # class LeagueSearch
69
83
 
70
84
 
85
+
71
86
  class GroundSearch < Search
72
87
  ###################
73
88
  ## core required delegates - use delegate generator - why? why not?
74
89
  def match_by( name:, country: nil, city: nil )
75
- @service.match_by( name: name,
90
+ @service.match_by( name: name,
76
91
  country: country,
77
- city: city )
92
+ city: city )
78
93
  end
79
94
 
80
95
  ###############
81
96
  ### more deriv support functions / helpers
82
97
  def match( name ) match_by( name: name ); end
83
- ## add more here - why? why not?
98
+ ## add more here - why? why not?
84
99
  end # class GroundSearch
85
100
 
86
101
 
@@ -106,31 +121,31 @@ class ClubSearch < Search
106
121
  league: nil,
107
122
  mods: nil )
108
123
  ## for now assume "global" mods - checks only for name
109
- ##
124
+ ##
110
125
  if mods && mods[ name ]
111
126
  club = mods[ name ]
112
127
  return [club] # note: wrap (single record) in array
113
- end
128
+ end
114
129
 
115
130
  ## note: add "auto-magic" country calculation via league record
116
131
  ## if league is a national league for football clubs
117
132
  if league
118
133
  raise ArgumentError, "match_by - league AND country NOT supported; sorry" if country
119
- ### find countries via league
134
+ ### find countries via league
120
135
  ### support league.intl? too - why? why not?
121
136
  ### or only nationa league
122
137
  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
-
138
+
124
139
  ### calc countries
125
140
  ### uses "global" func in sports-catalogs for now
126
- ## move code here - why? why not?
141
+ ## move code here - why? why not?
127
142
  country = find_countries_for_league( league )
128
- @service.match_by( name: name,
143
+ @service.match_by( name: name,
129
144
  country: country )
130
145
  else
131
- @service.match_by( name: name,
146
+ @service.match_by( name: name,
132
147
  country: country )
133
- end
148
+ end
134
149
  end
135
150
 
136
151
 
@@ -145,10 +160,10 @@ class ClubSearch < Search
145
160
  ###############
146
161
  ### more deriv support functions / helpers
147
162
  def match( name ) match_by( name: name ); end
148
-
163
+
149
164
  ##########
150
- # "legacy" finders - return zero or one club
151
- ## (if more than one match, exit/raise error/exception)
165
+ # "legacy" finders - return zero or one club
166
+ ## (if more than one match, exit/raise error/exception)
152
167
  def find( name ) find_by( name: name ); end
153
168
  def find!( name ) find_by!( name: name ); end
154
169
 
@@ -156,7 +171,7 @@ class ClubSearch < Search
156
171
  ## if there is more than one match than find aborts / fails
157
172
  def find_by!( name:, country: nil,
158
173
  league: nil ) ## todo/fix: add international or league flag?
159
- club = find_by( name: name,
174
+ club = find_by( name: name,
160
175
  country: country,
161
176
  league: league )
162
177
 
@@ -174,8 +189,8 @@ class ClubSearch < Search
174
189
  ## note: allow passing in of country key too (auto-counvert)
175
190
  ## and country struct too
176
191
  ## - country assumes / allows the country key or fifa code for now
177
- recs = match_by( name: name,
178
- country: country,
192
+ recs = match_by( name: name,
193
+ country: country,
179
194
  league: league )
180
195
 
181
196
  club = nil
@@ -243,12 +258,12 @@ end # class EventSearch
243
258
 
244
259
  ####
245
260
  ## 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
261
+ ## note - use EventSeaon to avoid name conflict with (global) Season class
262
+ ## find a better name SeasonInfo or SeasonFinder or SeasonStore
248
263
  ## or SeasonQ or ??
249
264
  class EventSeasonSearch
250
- def initialize( events: )
251
- @events = events
265
+ def initialize( events: )
266
+ @events = events
252
267
  end
253
268
 
254
269
  ###############
@@ -256,7 +271,7 @@ class EventSeasonSearch
256
271
  ##
257
272
  def find_by( date:, league: )
258
273
  date = Date.strptime( date, '%Y-%m-%d' ) if date.is_a?( String )
259
-
274
+
260
275
  infos = @events.seasons( league )
261
276
 
262
277
  infos.each do |info|
@@ -265,17 +280,17 @@ class EventSeasonSearch
265
280
  nil
266
281
  end
267
282
  end # class EventSeasonSearch
268
-
283
+
269
284
 
270
285
  ######
271
286
  ### add virtual team search ( clubs + national teams)
272
287
  ## note: no record base!!!!!
273
288
  class TeamSearch
274
289
  ## note: "virtual" index lets you search clubs and/or national_teams (don't care)
275
-
276
- def initialize( clubs:, national_teams: )
290
+
291
+ def initialize( clubs:, national_teams: )
277
292
  @clubs = clubs
278
- @national_teams = national_teams
293
+ @national_teams = national_teams
279
294
  end
280
295
 
281
296
  ## todo/check: rename to/use map_by! for array version - why? why not?
@@ -290,19 +305,19 @@ class TeamSearch
290
305
  _find_by!( name: name, league: league, mods: mods )
291
306
  end
292
307
  end
293
-
294
-
308
+
309
+
295
310
  def _find_by!( name:, league:, mods: nil )
296
311
  if mods && mods[ league.key ] && mods[ league.key ][ name ]
297
312
  mods[ league.key ][ name ]
298
313
  else
299
314
  if league.clubs?
300
315
  if league.intl? ## todo/fix: add intl? to ActiveRecord league!!!
301
- @club.find!( name )
316
+ @clubs.find!( name )
302
317
  else ## assume clubs in domestic/national league tournament
303
- ## note - search by league countries (may incl. more than one country
318
+ ## note - search by league countries (may incl. more than one country
304
319
  ## e.g. us incl. ca, fr incl. mc, ch incl. li, etc.
305
- @club.find_by!( name: name, league: league )
320
+ @clubs.find_by!( name: name, league: league )
306
321
  end
307
322
  else ## assume national teams (not clubs)
308
323
  @national_teams.find!( name )
@@ -310,8 +325,8 @@ class TeamSearch
310
325
  end
311
326
  end # method _find_by!
312
327
  end # class TeamSearch
313
-
314
-
328
+
329
+
315
330
 
316
331
  def initialize( leagues:,
317
332
  national_teams:,
@@ -320,11 +335,11 @@ class TeamSearch
320
335
  events:,
321
336
  players:
322
337
  )
323
- @leagues = LeagueSearch.new( leagues )
338
+ @leagues = LeagueSearch.new( leagues )
324
339
  @national_teams = NationalTeamSearch.new( national_teams )
325
340
  @clubs = ClubSearch.new( clubs )
326
- @events = EventSearch.new( events )
327
-
341
+ @events = EventSearch.new( events )
342
+
328
343
  @grounds = GroundSearch.new( grounds )
329
344
 
330
345
  @players = PlayerSearch.new( players )
@@ -332,8 +347,8 @@ class TeamSearch
332
347
  ## virtual deriv ("composite") search services
333
348
  @teams = TeamSearch.new( clubs: @clubs,
334
349
  national_teams: @national_teams )
335
- @event_seasons = EventSeasonSearch.new( events: @events )
336
-
350
+ @event_seasons = EventSeasonSearch.new( events: @events )
351
+
337
352
  end
338
353
 
339
354
  def countries
@@ -351,7 +366,7 @@ class TeamSearch
351
366
  def grounds() @grounds; end
352
367
 
353
368
  def players() @players; end
354
-
369
+
355
370
  def teams() @teams; end ## note - virtual table
356
371
  def seasons() @event_seasons; end ## note - virtual table
357
372
  end # class SportSearch
@@ -0,0 +1,116 @@
1
+ ###
2
+ # note - extend all structs for with search api
3
+ #
4
+ # todo - add more helpers!!!!
5
+ #
6
+ #
7
+ # todo - fix - move all non-core search functionality/machinery
8
+ # over here - why? why not?
9
+
10
+
11
+
12
+ module Sports
13
+
14
+ class Country
15
+ def self._search #### use service/api or such - why? why not?
16
+ SportDb::Import.world.countries
17
+ end
18
+ def self.find_by( code: nil, name: nil )
19
+ _search.find_by( code: code, name: name )
20
+ end
21
+
22
+ def self.find( q ) ## find by code (first) or name (second)
23
+ _search.find( q )
24
+ end
25
+
26
+ def self.parse_heading( line )
27
+ ## fix - move parse code here from search - why? why not?
28
+ _search.parse( line )
29
+ end
30
+
31
+ ## add alternate names/aliases
32
+ class << self
33
+ alias_method :[], :find ### keep shortcut - why? why not?
34
+ alias_method :heading, :parse_heading
35
+ end
36
+
37
+
38
+ # open question - what name to use build or parse_line or ?
39
+ # or parse_recs for CountryReader?
40
+ # remove CountryReader helper methods - why? why not?
41
+ # use parse_heading/heading for now !!!
42
+ #
43
+ # def self.parse( line ) or build( line ) ??
44
+ # SportDb::Import.world.countries.parse( line )
45
+ # end
46
+ #
47
+ # !!!! note - conflict with
48
+ # def self.read( path ) CountryReader.read( path ); end
49
+ # def self.parse( txt ) CountryReader.parse( txt ); end
50
+ #
51
+ end # class Country
52
+
53
+
54
+ ###
55
+ ## todo/fix - add find_by( code: ), find_by( name: )
56
+ ## split - why? why not?
57
+
58
+
59
+ class League
60
+ def self._search #### use service/api or such - why? why not?
61
+ SportDb::Import.catalog.leagues
62
+ end
63
+ def self.match_by( name: nil, code: nil,
64
+ country: nil )
65
+ _search.match_by( name: name, code: code,
66
+ country: country )
67
+ end
68
+ def self.match( q ) _search.match( q ); end
69
+
70
+ def self.find!( q ) _search.find!( q ); end
71
+ def self.find( q ) _search_find( q ); end
72
+ end # class League
73
+
74
+
75
+ class NationalTeam
76
+ def self._search #### use service/api or such - why? why not?
77
+ SportDb::Import.catalog.national_teams
78
+ end
79
+
80
+ def self.find( q ) _search.find( q ); end
81
+ def self.find!( q ) _search_find!( q ); end
82
+ end # class NationalTeam
83
+
84
+
85
+ class Club
86
+ def self._search #### use service/api or such - why? why not?
87
+ SportDb::Import.catalog.clubs
88
+ end
89
+
90
+ def self.match_by( name:, country: nil,
91
+ league: nil,
92
+ mods: nil )
93
+ _search.match_by( name: name, country: country,
94
+ league: league, mods: mods )
95
+ end
96
+ def self.match( name ) match_by( name: name ); end
97
+
98
+ def self.find( name ) _search.find_by( name: name ); end
99
+ def self.find!( name ) _search.find_by!( name: name ); end
100
+
101
+ def self.find_by!( name:, country: nil,
102
+ league: nil )
103
+ _search.find_by!( name: name, country: country,
104
+ league: league )
105
+ end
106
+ def self.find_by( name:, country: nil,
107
+ league: nil )
108
+ _search.find_by( name: name, country: country,
109
+ league: league )
110
+ end
111
+
112
+ def self.build_mods( mods )
113
+ _search_build_mods( mods )
114
+ end
115
+ end # class Club
116
+ end # module Sports
@@ -6,7 +6,7 @@
6
6
  # - .find_by_name
7
7
 
8
8
 
9
- class WorldSearch
9
+ class WorldSearch
10
10
 
11
11
  class CitySearch
12
12
  def initialize( service ) @service = service; end
@@ -14,29 +14,45 @@ class CitySearch
14
14
  ###################
15
15
  ## core required delegates - use delegate generator - why? why not?
16
16
  def match_by( name: )
17
- @service.match_by( name: name )
17
+ @service.match_by( name: name )
18
18
  end
19
19
  end # class CitySearch
20
20
 
21
21
 
22
22
  class CountrySearch
23
23
  def initialize( service ) @service = service; end
24
-
24
+
25
25
  ###################
26
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
27
+ def find_by_code( code )
28
+ puts "!! DEPRECATED - use CountrySearch#find_by( code: )"
29
+ @service.find_by_code( code )
30
+ end
31
+ def find_by_name( name )
32
+ puts "!! DEPRECATED - use CountrySearch#find_by( name: )"
33
+ @service.find_by_name( name )
34
+ end
29
35
 
36
+ def find_by( code: nil, name: nil )
37
+ ## todo/fix upstream - change to find_by( code:, name:, ) too
38
+ if code && name.nil?
39
+ @service.find_by_code( code )
40
+ elsif name && code.nil?
41
+ @service.find_by_name( name )
42
+ else
43
+ raise ArgumentError, "CountrySearch#find_by - one (and only one arg) required - code: or name:"
44
+ end
45
+ end
30
46
 
31
- ###############
32
- ### more deriv support functions / helpers
33
47
  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
48
+ @service.find_by_name_or_code( q )
37
49
  end
38
50
  alias_method :[], :find ### keep shortcut - why? why not?
39
-
51
+
52
+
53
+ ###############
54
+ ### more deriv support functions / helpers
55
+
40
56
  ###
41
57
  ## split/parse country line
42
58
  ##
@@ -56,29 +72,31 @@ class CountrySearch
56
72
  country = nil
57
73
  values.each do |value|
58
74
  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)
75
+ ## check for trailing country code e.g. (at), (eng), etc
76
+ ## allow code 1 to 5 for now - northern cyprus(fifa) with 5 letters?.
77
+ ## add/allow gb-eng, gb-wal (official iso2!!), in the future too - why? why not?
78
+ if value =~ /[ ]+\((?<code>[A-Za-z]{1,5})\)$/ ## e.g. Austria (at)
61
79
  code = $~[:code]
62
80
  name = value[0...(value.size-code.size-2)].strip ## note: add -2 for brackets
63
- candidates = [ find_by_code( code ), find_by_name( name ) ]
81
+ candidates = [ find_by( code: code ), find_by( name: name ) ]
64
82
  if candidates[0].nil?
65
- puts "** !!! ERROR !!! country - unknown code >#{code}< in line: #{line}"
83
+ puts "** !!! ERROR Country.parse_heading - unknown code >#{code}< in line: #{line}"
66
84
  pp line
67
85
  exit 1
68
86
  end
69
87
  if candidates[1].nil?
70
- puts "** !!! ERROR !!! country - unknown name >#{code}< in line: #{line}"
88
+ puts "** !!! ERROR Country.parse_heading - unknown name >#{code}< in line: #{line}"
71
89
  pp line
72
90
  exit 1
73
91
  end
74
92
  if candidates[0] != candidates[1]
75
- puts "** !!! ERROR !!! country - name and code do NOT match the same country:"
93
+ puts "** !!! ERROR Country.parse_heading - name and code do NOT match the same country:"
76
94
  pp line
77
95
  pp candidates
78
96
  exit 1
79
97
  end
80
98
  if country && country != candidates[0]
81
- puts "** !!! ERROR !!! country - names do NOT match the same country:"
99
+ puts "** !!! ERROR Country.parse_heading - names do NOT match the same country:"
82
100
  pp line
83
101
  pp country
84
102
  pp candidates
@@ -89,12 +107,12 @@ class CountrySearch
89
107
  ## just assume value is name or code
90
108
  candidate = find( value )
91
109
  if candidate.nil?
92
- puts "** !!! ERROR !!! country - unknown name or code >#{value}< in line: #{line}"
110
+ puts "** !!! ERROR Country.parse_heading - unknown name or code >#{value}< in line: #{line}"
93
111
  pp line
94
112
  exit 1
95
113
  end
96
114
  if country && country != candidate
97
- puts "** !!! ERROR !!! country - names do NOT match the same country:"
115
+ puts "** !!! ERROR Country.parse_heading - names do NOT match the same country:"
98
116
  pp line
99
117
  pp country
100
118
  pp candidate
@@ -126,9 +144,9 @@ end # class WorldSearch
126
144
 
127
145
 
128
146
  class DummyCountrySearch
129
- def find_by_code( code )
147
+ def find_by_code( code )
130
148
  puts "[WARN] no world search configured; cannot find country by code"
131
- nil
149
+ nil
132
150
  end
133
151
  def find_by_name( name )
134
152
  puts "[WARN] no world search configured; cannot find country by name"
@@ -2,8 +2,8 @@ module SportDb
2
2
  module Module
3
3
  module Formats
4
4
 
5
- MAJOR = 1 ## todo: namespace inside version or something - why? why not??
6
- MINOR = 2
5
+ MAJOR = 2 ## todo: namespace inside version or something - why? why not??
6
+ MINOR = 0
7
7
  PATCH = 0
8
8
  VERSION = [MAJOR,MINOR,PATCH].join('.')
9
9