sportdb-formats 1.2.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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