sportdb-catalogs 1.2.0 → 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5912ceb7d640f68512000d72620ff285eb8aaba12758fb6eda9db0f2bba6c3c8
4
- data.tar.gz: 6a1022811416867e7ae9fc7bb4519a4fa6cd42cc6ad12a32b1675a26a3d51ab6
3
+ metadata.gz: '09d5c1b4a2fea093abfc294ccbf6149315a335a07a5e934d25fb652215e0ab23'
4
+ data.tar.gz: 991f6cd9ee018fa4f7d84be847c5868a024ac240ec5d29cb60636addbd1984c5
5
5
  SHA512:
6
- metadata.gz: 33efb321b2fa039a836f037acd4c57367bea8f2f5d9faeffa77b5eec047590feaa405eb230c15cf073c7e42a6818f60d73914d4d0bd576b7f5baec1c85686722
7
- data.tar.gz: 3d2862394fad3aa68e0c970e9b6e74b6ed7f2a34eb04a28e8578ff83ce2379651eb146580593d7b13f75705a7ee55b643e73cce2aee8725391e2fea2e787b684
6
+ metadata.gz: ffaca4423b4dc8adba0db61187dfcf2cc0b1fb5c47255953ed08e8e13bc1a0e2967673d7f1a60f5d6a7045b93ff3575fc27114ed86be15c27f72041287e59a91
7
+ data.tar.gz: ded797ffeae2ce160c1d6477416fa90d8b26433e78aca4bdc61c82bf0bdf8eef95f346c2f2fe95e070be962d8789d511d222379057245a9d649388b0a40d93c1
data/CHANGELOG.md CHANGED
@@ -1,4 +1,4 @@
1
- ### 1.2.0
1
+ ### 1.2.2
2
2
  ### 0.0.1 / 2019-06-29
3
3
 
4
4
  * Everything is new. First release.
data/README.md CHANGED
@@ -9,23 +9,19 @@
9
9
 
10
10
 
11
11
 
12
- ## Usage
12
+ ## Usage
13
13
 
14
14
  Let's use the [/clubs datasets](https://github.com/openfootball/clubs)
15
- (1500+ football clubs from around the world)
15
+ (3000+ football clubs from around the world)
16
16
  to match name "variants" e.g. `Arsenal` to canonical global unique
17
17
  names e.g. `Arsenal FC, London, England`:
18
18
 
19
19
  ``` ruby
20
20
  require 'sportdb/catalogs'
21
21
 
22
- ## note: requires a local copy of the football.db clubs datasets
23
- ## see https://github.com/openfootball/clubs
24
- SportDb::Import.config.clubs_dir = './clubs'
22
+ Club = CatalogDb::Metal::Club
25
23
 
26
- CLUBS = SportDb::Import.catalog.clubs
27
-
28
- m = CLUBS.match( 'Arsenal' )
24
+ m = Club.match_by( name: 'Arsenal' )
29
25
  m.size # 3 club matches found
30
26
  #=> 3
31
27
  m[0].name; m[0].city; m[0].country
@@ -36,20 +32,20 @@ m[2].name; m[2].city; m[2].country
36
32
  #=> "Arsenal de Sarandí", "Sarandí", "Argentina"
37
33
 
38
34
 
39
- m = CLUBS.match_by( name: 'Arsenal', country: 'eng' )
35
+ m = Club.match_by( name: 'Arsenal', country: 'eng' )
40
36
  # -or- try alternative names (and auto-generated spelling variants)
41
- m = CLUBS.match_by( name: 'Arsenal FC', country: 'eng' )
42
- m = CLUBS.match_by( name: 'Arsenal F.C.', country: 'eng' )
43
- m = CLUBS.match_by( name: '...A.r.s.e.n.a.l... F.C...', country: 'eng' )
37
+ m = Club.match_by( name: 'Arsenal FC', country: 'eng' )
38
+ m = Club.match_by( name: 'Arsenal F.C.', country: 'eng' )
39
+ m = Club.match_by( name: '...A.r.s.e.n.a.l... F.C...', country: 'eng' )
44
40
  m.size # 1 club match found
45
41
  #=> 1
46
42
  m[0].name; m[0].city; m[0].country
47
43
  #=> "Arsenal FC", "London", "England"
48
44
 
49
- m = CLUBS.match_by( name: 'Arsenal', country: 'ar' )
45
+ m = Club.match_by( name: 'Arsenal', country: 'ar' )
50
46
  # -or- try alternative names (and auto-generated spelling variants)
51
- m = CLUBS.match_by( name: 'Arsenal Sarandí', country: 'ar' )
52
- m = CLUBS.match_by( name: 'Arsenal Sarandi', country: 'ar' )
47
+ m = Club.match_by( name: 'Arsenal Sarandí', country: 'ar' )
48
+ m = Club.match_by( name: 'Arsenal Sarandi', country: 'ar' )
53
49
  m.size # 1 club match found
54
50
  #=> 1
55
51
  m[0].name; m[0].city; m[0].country
@@ -57,15 +53,15 @@ m[0].name; m[0].city; m[0].country
57
53
 
58
54
 
59
55
  # try some more
60
- m = CLUBS.match( 'AZ' )
56
+ m = Club.match_by( name: 'AZ' )
61
57
  m[0].name; m[0].city; m[0].country
62
58
  #=> "AZ Alkmaar", "Alkmaar", "Netherlands"
63
59
 
64
- m = CLUBS.match( 'Bayern' )
60
+ m = Club.match_by( name: 'Bayern' )
65
61
  # -or- try alternative names (and auto-generated spelling variants)
66
- m = CLUBS.match( 'Bayern München' )
67
- m = CLUBS.match( 'Bayern Munchen' )
68
- m = CLUBS.match( 'Bayern Muenchen' )
62
+ m = Club.match_by( name: 'Bayern München' )
63
+ m = Club.match_by( name: 'Bayern Munchen' )
64
+ m = Club.match_by( name: 'Bayern Muenchen' )
69
65
  m[0].name; m[0].city; m[0].country
70
66
  #=> "Bayern München", "München", "Germany"
71
67
 
data/Rakefile CHANGED
@@ -20,13 +20,13 @@ Hoe.spec 'sportdb-catalogs' do
20
20
  self.licenses = ['Public Domain']
21
21
 
22
22
  self.extra_deps = [
23
- ['sportdb-formats'],
23
+ ['sportdb-structs', '>= 0.3.1'],
24
24
  ['sqlite3'], ## add sqlite for "metal" use (no activerecord etc.)
25
- ['footballdb-data'] ## add builtin default db
25
+ ['footballdb-data'] ## add builtin default db
26
26
  ]
27
27
 
28
28
  self.spec_extras = {
29
- required_ruby_version: '>= 2.2.2'
29
+ required_ruby_version: '>= 3.1.0'
30
30
  }
31
31
 
32
32
  end
@@ -8,29 +8,29 @@ module Metal
8
8
 
9
9
 
10
10
  class BaseRecord ## or just use Base or such - why? why not?
11
-
12
- def self.execute( sql )
11
+
12
+ def self.execute( sql )
13
13
  ## puts "==> sql query [#{self.name}]"
14
14
  ## puts sql
15
- database.execute( sql )
15
+ database.execute( sql )
16
16
  end
17
-
17
+
18
18
  def self.tablename=(name) @tablename = name; end
19
19
  def self.tablename() @tablename; end
20
-
21
- def self.columns=(names)
20
+
21
+ def self.columns=(names)
22
22
  ## note: auto-add table name to qualify
23
- @columns = names.map {|name| "#{self.tablename}.#{name}" }
23
+ @columns = names.map {|name| "#{self.tablename}.#{name}" }
24
24
  @columns
25
25
  end
26
26
  def self.columns() @columns; end
27
-
27
+
28
28
  def self.count
29
29
  sql = "SELECT count(*) FROM #{self.tablename}"
30
30
  rows = execute( sql )
31
31
  rows[0][0] # e.g. returns [[241]]
32
32
  end
33
-
33
+
34
34
  ## helpers from country - use a helper module for includes (share with clubs etc.) - why? why not?
35
35
  # include NameHelper
36
36
  extend SportDb::NameHelper
@@ -48,7 +48,7 @@ class BaseRecord ## or just use Base or such - why? why not?
48
48
  ## use TypeError - why? why not? or if exits ValueError?
49
49
  raise ArgumentError, "0 or 1 expected for bool in sqlite; got #{value}"
50
50
  end
51
- end
51
+ end
52
52
 
53
53
 
54
54
  ##
@@ -58,7 +58,7 @@ class BaseRecord ## or just use Base or such - why? why not?
58
58
  if country.is_a?( String ) || country.is_a?( Symbol )
59
59
  # note: query/find country via catalog db
60
60
  rec = Country.find_by_code( country )
61
- rec = Country.find_by_name( country ) if rec.nil?
61
+ rec = Country.find_by_name( country ) if rec.nil?
62
62
  if rec.nil?
63
63
  puts "** !!! ERROR !!! - unknown country >#{country}< - no match found, sorry - add to world/countries.txt in config"
64
64
  exit 1
@@ -72,41 +72,50 @@ end # class BaseRecord
72
72
 
73
73
 
74
74
  class PlayerRecord < BaseRecord
75
+
76
+ ## lets you query if datbase setup
77
+ def self.database?() defined?( @@db ); end
78
+
75
79
  def self.database
76
80
  ### note: only one database for all derived records/tables!!!
77
81
  ## thus MUST use @@ and not @!!!!!
78
82
  ## todo - change later to built-in database
79
83
  ## or download on request???
80
- @@db ||= SQLite3::Database.new( './players.db' )
84
+ @@db ||= SQLite3::Database.new( './players.db', readonly: true )
81
85
  @@db
82
86
  end
83
-
87
+
84
88
  def self.database=(path)
85
89
  puts "==> setting (internal) players db to: >#{path}<"
86
- @@db = SQLite3::Database.new( path )
90
+ @@db = SQLite3::Database.new( path, readonly: true )
87
91
  pp @@db
88
92
  @@db
89
- end
93
+ end
90
94
  end # class PlayerRecord
91
95
 
92
96
 
93
97
 
94
98
  ##############
95
99
  ### todo (fix) / rename to CatalogRecord - why? why not?
96
- class Record < BaseRecord
100
+ class Record < BaseRecord
97
101
  ## add db alias why? why not?
102
+
103
+ ## lets you query if datbase setup
104
+ def self.database?() defined?( @@db ); end
105
+
98
106
  def self.database
99
107
  ### note: only one database for all derived records/tables!!!
100
108
  ## thus MUST use @@ and not @!!!!!
101
109
  ##
102
110
  ## default to built-in via footballdb-data gem for now!!!
103
- @@db ||= SQLite3::Database.new( './catalog.db' )
111
+ @@db ||= SQLite3::Database.new( "#{FootballDb::Data.data_dir}/catalog.db",
112
+ readonly: true )
104
113
  @@db
105
114
  end
106
115
 
107
116
  def self.database=(path)
108
117
  puts "==> setting (internal) catalog db to: >#{path}<"
109
- @@db = SQLite3::Database.new( path )
118
+ @@db = SQLite3::Database.new( path, readonly: true )
110
119
  pp @@db
111
120
  @@db
112
121
  end
@@ -118,14 +127,14 @@ def self._to_league( key )
118
127
  League._record( key )
119
128
  end
120
129
 
121
- def self._to_country( key )
130
+ def self._to_country( key )
122
131
  # note: use cached record or (faster) key lookup on fallback
123
- Country._record( key )
132
+ Country._record( key )
124
133
  end
125
134
 
126
135
  def self._to_city( key ) ### rename; use find_by_key / find_by( key: )
127
136
  # note: use cached record or (faster) key lookup on fallback
128
- City._record( key )
137
+ City._record( key )
129
138
  end
130
139
 
131
140
 
@@ -5,8 +5,8 @@ module Metal
5
5
  class Country < Record
6
6
  self.tablename = 'countries'
7
7
 
8
- self.columns = ['key',
9
- 'name',
8
+ self.columns = ['key',
9
+ 'name',
10
10
  'code',
11
11
  'tags',
12
12
  'alt_names']
@@ -20,42 +20,42 @@ class Country < Record
20
20
  else ## query and cache and return
21
21
  rows = execute( <<-SQL )
22
22
  SELECT #{self.columns.join(', ')}
23
- FROM countries
24
- WHERE countries.key = '#{key}'
23
+ FROM countries
24
+ WHERE countries.key = '#{key}'
25
25
  SQL
26
-
26
+
27
27
  ## todo/fix: also assert for rows == 1 AND NOT MULTIPLE records - why? why not?
28
- if rows.empty?
29
- raise ArgumentError, "country record with key #{key} not found"
30
- else
28
+ if rows.empty?
29
+ raise ArgumentError, "country record with key #{key} not found"
30
+ else
31
31
  _build_country( rows[0] )
32
32
  end
33
33
  end
34
34
  end
35
35
 
36
-
36
+
37
37
  def self._build_country( row )
38
38
  ## note: cache structs by key (do NOT rebuild duplicates; reuse)
39
39
  cache[ row[0] ] ||= begin
40
40
  ## note: split tags & alt names (PLUS remove leading//trailing spaces)
41
41
  tags = row[3].split(',').map {|tag| tag.strip }
42
- alt_names = row[4].split('|').map {|alt_name| alt_name.strip }
42
+ alt_names = row[4].split('|').map {|alt_name| alt_name.strip }
43
43
  country = Sports::Country.new(
44
44
  key: row[0],
45
45
  name: row[1],
46
46
  code: row[2],
47
- tags: tags
47
+ tags: tags
48
48
  )
49
49
  country.alt_names = alt_names
50
50
  country
51
51
  end
52
- end
53
-
52
+ end
53
+
54
54
 
55
55
  ## fix/todo: add find_by (alias for find_by_name/find_by_code)
56
56
  def self.find_by_code( code )
57
57
  q = code.to_s.downcase ## allow symbols (and always downcase e.g. AUT to aut etc.)
58
-
58
+
59
59
  ## note: results in
60
60
  ## Côte d'Ivoire => côte d'ivoire
61
61
  ## quote will break sql!!!
@@ -69,15 +69,16 @@ SQL
69
69
 
70
70
  rows = execute( <<-SQL )
71
71
  SELECT #{self.columns.join(', ')}
72
- FROM countries
72
+ FROM countries
73
73
  INNER JOIN country_codes ON countries.key = country_codes.key
74
- WHERE country_codes.code = '#{q}'
74
+ WHERE country_codes.code = '#{q}'
75
75
  SQL
76
-
77
- if rows.empty?
78
- nil
79
- else
80
- _build_country( rows[0] )
76
+
77
+ if rows.empty?
78
+ nil
79
+ else
80
+ ## todo/fix - raise error if more than once record found!!!
81
+ _build_country( rows[0] )
81
82
  end
82
83
  end
83
84
 
@@ -85,26 +86,62 @@ SQL
85
86
  ##
86
87
  ##
87
88
  ## note - need to escape name ?
88
- ## e.g. Côte d'Ivoire
89
+ ## e.g. Côte d'Ivoire
89
90
  ## make sure normalize removes single quotes (')!!!
90
91
 
91
92
  def self.find_by_name( name )
92
93
  q = normalize( unaccent( name.to_s )) ## allow symbols too (e.g. use to.s first)
93
-
94
+
94
95
  rows = execute( <<-SQL )
95
96
  SELECT #{self.columns.join(', ')}
96
- FROM countries
97
+ FROM countries
97
98
  INNER JOIN country_names ON countries.key = country_names.key
98
- WHERE country_names.name = '#{q}'
99
+ WHERE country_names.name = '#{q}'
99
100
  SQL
100
101
 
101
- if rows.empty?
102
- nil
103
- else
102
+ if rows.empty?
103
+ nil
104
+ else
105
+ ## todo/fix - raise error if more than once record found!!!
104
106
  _build_country( rows[0] )
105
- end
107
+ end
106
108
  end
109
+
110
+
111
+ def self.find_by_name_or_code( q )
112
+ name = normalize( unaccent( q.to_s )) ## allow symbols too (e.g. use to.s first)
113
+
114
+ code = q.to_s.downcase ## allow symbols (and always downcase e.g. AUT to aut etc.)
115
+ ## note: results in
116
+ ## Côte d'Ivoire => côte d'ivoire
117
+ ## quote will break sql!!!
118
+ ## remove - spaces etc.
119
+ ## for now remove only single quote (will break sql) - add more?
120
+ ##
121
+ ## or use escape_sql_string - possible??
122
+ code = code.gsub( /[']/, '' )
123
+
124
+
125
+ rows = execute( <<-SQL )
126
+ SELECT #{self.columns.join(', ')}
127
+ FROM countries
128
+ INNER JOIN country_names ON countries.key = country_names.key
129
+ WHERE country_names.name = '#{name}'
130
+ UNION
131
+ SELECT #{self.columns.join(', ')}
132
+ FROM countries
133
+ INNER JOIN country_codes ON countries.key = country_codes.key
134
+ WHERE country_codes.code = '#{code}'
135
+ SQL
136
+
137
+ if rows.empty?
138
+ nil
139
+ else
140
+ ## todo/fix - raise error if more than once record found!!!
141
+ _build_country( rows[0] )
142
+ end
143
+ end
144
+
107
145
  end # class Country
108
146
  end # module Metal
109
147
  end # module CatalogDb
110
-
@@ -4,20 +4,19 @@ module Metal
4
4
  class EventInfo < Record
5
5
  self.tablename = 'event_infos'
6
6
 
7
- self.columns = ['league_key',
8
- 'season',
9
- 'teams',
7
+ self.columns = ['league_key',
8
+ 'season',
9
+ 'teams',
10
10
  'matches',
11
11
  'goals',
12
- 'start_date',
12
+ 'start_date',
13
13
  'end_date']
14
-
14
+
15
15
 
16
16
  def self._build_event_info( row )
17
17
  ## note: cache structs by key (do NOT rebuild duplicates; reuse)
18
18
  @cache ||= Hash.new
19
- ## note: move EventInfo from sports/format to structs - why? why not?
20
- @cache[ row[0] ] ||= SportDb::Import::EventInfo.new(
19
+ @cache[ row[0] ] ||= Sports::EventInfo.new(
21
20
  league: _to_league( row[0] ),
22
21
  season: Season.parse(row[1]),
23
22
  teams: row[2],
@@ -25,35 +24,35 @@ class EventInfo < Record
25
24
  goals: row[4],
26
25
  start_date: row[5] ? Date.strptime( row[5], '%Y-%m-%d' ) : nil,
27
26
  end_date: row[6] ? Date.strptime( row[6], '%Y-%m-%d' ) : nil,
28
- )
29
- end
30
-
27
+ )
28
+ end
29
+
31
30
 
32
31
  def self.seasons( league )
33
- league_key = league.is_a?( String ) ? League.find!( league ).key
32
+ league_key = league.is_a?( String ) ? League.find!( league ).key
34
33
  : league.key
35
34
 
36
35
  rows = execute( <<-SQL )
37
36
  SELECT #{self.columns.join(', ')}
38
- FROM event_infos
39
- WHERE event_infos.league_key = '#{league_key}'
37
+ FROM event_infos
38
+ WHERE event_infos.league_key = '#{league_key}'
40
39
  SQL
41
40
 
42
41
  rows.map {|row| _build_event_info( row ) }
43
42
  end
44
43
 
45
-
44
+
46
45
  def self.find_by( league:, season: )
47
- league_key = league.is_a?( String ) ? League.find!( league ).key
46
+ league_key = league.is_a?( String ) ? League.find!( league ).key
48
47
  : league.key
49
- season_key = season.is_a?( String ) ? Season.parse(season).key
48
+ season_key = season.is_a?( String ) ? Season.parse(season).key
50
49
  : season.key
51
50
 
52
51
  rows = execute( <<-SQL )
53
52
  SELECT #{self.columns.join(', ')}
54
- FROM event_infos
53
+ FROM event_infos
55
54
  WHERE event_infos.league_key = '#{league_key}' AND
56
- event_infos.season = '#{season_key}'
55
+ event_infos.season = '#{season_key}'
57
56
  SQL
58
57
 
59
58
  if rows.empty?
@@ -4,12 +4,12 @@ module Metal
4
4
  class League < Record
5
5
  self.tablename = 'leagues'
6
6
 
7
- self.columns = ['key',
8
- 'name',
7
+ self.columns = ['key',
8
+ 'name',
9
9
  'intl', # international tournament?
10
- 'clubs', # clubs or national teams?
10
+ 'clubs', # clubs or national teams?
11
11
  'country_key']
12
-
12
+
13
13
 
14
14
  def self.cache() @cache ||= Hash.new; end
15
15
 
@@ -20,14 +20,14 @@ class League < Record
20
20
  else ## query and cache and return
21
21
  rows = execute( <<-SQL )
22
22
  SELECT #{self.columns.join(', ')}
23
- FROM leagues
24
- WHERE leagues.key = '#{key}'
23
+ FROM leagues
24
+ WHERE leagues.key = '#{key}'
25
25
  SQL
26
-
26
+
27
27
  ## todo/fix: also assert for rows == 1 AND NOT MULTIPLE records - why? why not?
28
- if rows.empty?
29
- raise ArgumentError, "league record with key #{key} not found"
30
- else
28
+ if rows.empty?
29
+ raise ArgumentError, "league record with key #{key} not found"
30
+ else
31
31
  _build_league( rows[0] )
32
32
  end
33
33
  end
@@ -38,37 +38,74 @@ SQL
38
38
  cache[ row[0] ] ||= Sports::League.new(
39
39
  key: row[0],
40
40
  name: row[1],
41
- intl: _to_bool( row[2] ),
41
+ intl: _to_bool( row[2] ),
42
42
  clubs: _to_bool( row[3] ),
43
43
  country: row[4] ? _to_country( row[4] ) : nil,
44
44
  )
45
- end
46
-
45
+ end
47
46
 
48
- def self.match_by( name:,
49
- country: nil )
47
+ def self.match_by_code( code,
48
+ country: nil )
49
+ ## note: match must for now always include name
50
+ ### todo/fix: allow special normalize formula for
51
+ ## code - why? why not?
52
+ ## e.g. allow ö1 or ö or such - why? why not?
53
+ code = normalize( unaccent(code) )
54
+
55
+ rows = nil
56
+ if country.nil?
57
+ ## note: returns empty array if no match and NOT nil
58
+ rows = execute( <<-SQL )
59
+ SELECT #{self.columns.join(', ')}
60
+ FROM leagues
61
+ INNER JOIN league_codes ON leagues.key = league_codes.key
62
+ WHERE league_codes.code = '#{code}'
63
+ SQL
64
+ else ## filter by country
65
+ ## note: also skip international leagues & cups (e.g. champions league etc.) for now - why? why not?
66
+
67
+ ## note: country assumes / allows the country key or fifa code for now
68
+ ## note: allow passing in of country struct too
69
+ country_rec = _country( country )
70
+
71
+ rows = execute( <<-SQL )
72
+ SELECT #{self.columns.join(', ')}
73
+ FROM leagues
74
+ INNER JOIN league_codes ON leagues.key = league_codes.key
75
+ WHERE league_codes.code = '#{code}' AND
76
+ leagues.country_key = '#{country_rec.key}'
77
+
78
+ SQL
79
+ end
80
+
81
+ ## wrap results array into struct records
82
+ rows.map {|row| _build_league( row )}
83
+ end
84
+
85
+ def self.match_by_name( name,
86
+ country: nil )
50
87
  ## note: match must for now always include name
51
88
  name = normalize( unaccent(name) )
52
89
 
53
- rows = nil
54
- if country.nil?
90
+ rows = nil
91
+ if country.nil?
55
92
  ## note: returns empty array if no match and NOT nil
56
93
  rows = execute( <<-SQL )
57
94
  SELECT #{self.columns.join(', ')}
58
- FROM leagues
95
+ FROM leagues
59
96
  INNER JOIN league_names ON leagues.key = league_names.key
60
- WHERE league_names.name = '#{name}'
97
+ WHERE league_names.name = '#{name}'
61
98
  SQL
62
99
  else ## filter by country
63
100
  ## note: also skip international leagues & cups (e.g. champions league etc.) for now - why? why not?
64
-
101
+
65
102
  ## note: country assumes / allows the country key or fifa code for now
66
103
  ## note: allow passing in of country struct too
67
104
  country_rec = _country( country )
68
105
 
69
106
  rows = execute( <<-SQL )
70
107
  SELECT #{self.columns.join(', ')}
71
- FROM leagues
108
+ FROM leagues
72
109
  INNER JOIN league_names ON leagues.key = league_names.key
73
110
  WHERE league_names.name = '#{name}' AND
74
111
  leagues.country_key = '#{country_rec.key}'
@@ -80,6 +117,52 @@ SQL
80
117
  rows.map {|row| _build_league( row )}
81
118
  end
82
119
 
120
+ def self.match_by_name_or_code( q,
121
+ country: nil )
122
+ name = normalize( unaccent(q) )
123
+ code = normalize( unaccent(q) )
124
+
125
+ rows = nil
126
+ if country.nil?
127
+ ## note: returns empty array if no match and NOT nil
128
+ rows = execute( <<-SQL )
129
+ SELECT #{self.columns.join(', ')}
130
+ FROM leagues
131
+ INNER JOIN league_names ON leagues.key = league_names.key
132
+ WHERE league_names.name = '#{name}'
133
+ UNION
134
+ SELECT #{self.columns.join(', ')}
135
+ FROM leagues
136
+ INNER JOIN league_codes ON leagues.key = league_codes.key
137
+ WHERE league_codes.code = '#{code}'
138
+ SQL
139
+ else ## filter by country
140
+ ## note: also skip international leagues & cups (e.g. champions league etc.) for now - why? why not?
141
+
142
+ ## note: country assumes / allows the country key or fifa code for now
143
+ ## note: allow passing in of country struct too
144
+ country_rec = _country( country )
145
+
146
+ rows = execute( <<-SQL )
147
+ SELECT #{self.columns.join(', ')}
148
+ FROM leagues
149
+ INNER JOIN league_names ON leagues.key = league_names.key
150
+ WHERE league_names.name = '#{name}' AND
151
+ leagues.country_key = '#{country_rec.key}'
152
+ UNION
153
+ SELECT #{self.columns.join(', ')}
154
+ FROM leagues
155
+ INNER JOIN league_codes ON leagues.key = league_codes.key
156
+ WHERE league_codes.code = '#{code}' AND
157
+ leagues.country_key = '#{country_rec.key}'
158
+ SQL
159
+ end
160
+
161
+ ## wrap results array into struct records
162
+ rows.map {|row| _build_league( row )}
163
+ end
164
+
165
+
83
166
  end # class League
84
167
  end # module Metal
85
168
  end # module CatalogDb
@@ -5,7 +5,7 @@ module Catalogs
5
5
 
6
6
  MAJOR = 1 ## todo: namespace inside version or something - why? why not??
7
7
  MINOR = 2
8
- PATCH = 0
8
+ PATCH = 2
9
9
  VERSION = [MAJOR,MINOR,PATCH].join('.')
10
10
 
11
11
  def self.version
@@ -1,7 +1,4 @@
1
- ### our own sportdb libs / gems
2
- ### try min. dependencies - change to structs only (NOT formats) - why? why not?
3
- ## require 'sportdb/structs'
4
- require 'sportdb/formats'
1
+ require 'sportdb/structs'
5
2
 
6
3
  require 'sqlite3'
7
4
 
@@ -15,77 +12,17 @@ require_relative 'catalogs/base' ## base record
15
12
  require_relative 'catalogs/country'
16
13
  require_relative 'catalogs/city'
17
14
 
18
- require_relative 'catalogs/club'
15
+ require_relative 'catalogs/club'
19
16
  require_relative 'catalogs/national_team'
20
17
  require_relative 'catalogs/league'
21
18
  require_relative 'catalogs/event_info'
22
- require_relative 'catalogs/ground'
19
+ require_relative 'catalogs/ground'
23
20
 
24
21
  ## more
25
22
  require_relative 'catalogs/player'
26
23
 
27
24
 
28
25
 
29
- module SportDb
30
- module Import
31
-
32
- class Configuration
33
- ## note: add more configs (open class), see sportdb-structs for original config!!!
34
-
35
- ###
36
- # find a better name for setting - why? why not?
37
- # how about catalogdb or ???
38
- attr_reader :catalog_path
39
- def catalog_path=(path)
40
- @catalog_path = path
41
- ########
42
- # reset database here to new path
43
- CatalogDb::Metal::Record.database = path
44
-
45
- ## plus automagically set world search too (to use CatalogDb)
46
- self.world = WorldSearch.new(
47
- countries: CatalogDb::Metal::Country,
48
- cities: CatalogDb::Metal::City,
49
- )
50
-
51
- @catalog_path
52
- end
53
-
54
- def catalog
55
- @catalog ||= SportSearch.new(
56
- leagues: CatalogDb::Metal::League,
57
- national_teams: CatalogDb::Metal::NationalTeam,
58
- clubs: CatalogDb::Metal::Club,
59
- grounds: CatalogDb::Metal::Ground,
60
- events: CatalogDb::Metal::EventInfo,
61
- players: CatalogDb::Metal::Player, # note - via players.db !!!
62
- )
63
- end
64
-
65
- ###
66
- # find a better name for setting - why? why not?
67
- # how about playersdb or ???
68
- attr_reader :players_path
69
- def players_path=(path)
70
- @players_path = path
71
- ########
72
- # reset database here to new path
73
- CatalogDb::Metal::PlayerRecord.database = path
74
-
75
- @players_path
76
- end
77
- end # class Configuration
78
-
79
-
80
- ## e.g. use config.catalog -- keep Import.catalog as a shortcut (for "read-only" access)
81
- def self.catalog() config.catalog; end
82
- end # module Import
83
- end # module SportDb
84
-
85
-
86
-
87
-
88
-
89
26
  ###
90
27
  # add status
91
28
  module CatalogDb
@@ -94,34 +31,48 @@ module Metal
94
31
  def self.tables
95
32
 
96
33
  puts "==> table stats"
97
- catalog_path = SportDb::Import.config.catalog_path
98
- if catalog_path
99
- puts " #{File.basename(catalog_path)} in (#{File.dirname(catalog_path)})"
100
- puts " #{Country.count} countries / #{City.count} cities"
34
+ if Record.database?
35
+ db = Record.database
36
+ filename = db.filename
37
+ if filename # note - nil for memory or tempary db?
38
+ puts " #{File.basename(filename)} in (#{File.dirname(filename)})"
39
+ else
40
+ puts "no filename; memory or temporary db?"
41
+ end
42
+
43
+ ## pp Record.database
44
+ ## puts
45
+ puts " #{Country.count} countries / #{City.count} cities"
101
46
  puts " #{NationalTeam.count} national teams"
102
47
  puts " #{League.count} leagues"
103
48
  puts " #{Club.count} clubs"
104
49
  puts " #{Ground.count} grounds"
105
50
  ## add more
106
-
107
51
  else
108
52
  puts " - no catalog.db set - "
109
53
  end
110
54
 
111
- ## todo/fix:
112
- ## check if players_path configured???
113
- players_path = SportDb::Import.config.players_path
114
- if players_path
115
- puts " #{File.basename(players_path)} in (#{File.dirname(players_path)})"
55
+
56
+ if PlayerRecord.database?
57
+ db = PlayerRecord.database
58
+ filename = db.filename
59
+ if filename # note - nil for memory or tempary db?
60
+ puts " #{File.basename(filename)} in (#{File.dirname(filename)})"
61
+ else
62
+ puts "no filename; memory or temporary db?"
63
+ end
64
+
65
+ ## pp PlayerRecord.database
66
+ ## puts
116
67
  puts " #{Player.count} players"
117
68
  else
118
- puts " - no players.db set -"
69
+ puts " - no players.db set -"
119
70
  end
120
71
  end
121
72
 
122
73
  end # module Metal
123
74
  end # module CatalogDb
124
-
75
+
125
76
 
126
77
 
127
78
  ###
@@ -131,34 +82,34 @@ end # module CatalogDb
131
82
  ## what name to use?
132
83
  ## find_countries_for_league_clubs or such
133
84
  ## find_countries_for_league - why? why not?
134
- ## note - returns array of countries OR single country
85
+ ## note - returns array of countries OR single country
135
86
 
136
87
  def find_countries_for_league( league )
137
- ## todo/fix: assert league is a League with country record/struct !!!!!
88
+ ## todo/fix: assert league is a League with country record/struct !!!!!
138
89
 
139
- countries = []
90
+ countries = []
140
91
  countries << league.country ### assume league.country is already db record/struct - why? why not?
141
- ## check for 2nd countries for known leagues
92
+ ## check for 2nd countries for known leagues
142
93
  ## (re)try with second country - quick hacks for known leagues
143
94
  ## e.g. Swanse, cardiff in premier league
144
95
  ## san mariono in serie a (italy)
145
96
  ## monaco in ligue 1 (france)
146
97
  ## etc.
147
98
  ## add andorra to spanish la liga (e.g. andorra fc???)
148
-
99
+
149
100
  case league.country.key
150
- when 'eng' then countries << CatalogDb::Metal::Country._record('wal')
101
+ when 'eng' then countries << CatalogDb::Metal::Country._record('wal')
151
102
  when 'sco' then countries << CatalogDb::Metal::Country._record('eng')
152
- when 'ie' then countries << CatalogDb::Metal::Country._record('nir')
153
- when 'fr' then countries << CatalogDb::Metal::Country._record('mc')
154
- when 'es' then countries << CatalogDb::Metal::Country._record('ad')
103
+ when 'ie' then countries << CatalogDb::Metal::Country._record('nir')
104
+ when 'fr' then countries << CatalogDb::Metal::Country._record('mc')
105
+ when 'es' then countries << CatalogDb::Metal::Country._record('ad')
155
106
  when 'it' then countries << CatalogDb::Metal::Country._record('sm')
156
- when 'ch' then countries << CatalogDb::Metal::Country._record('li')
107
+ when 'ch' then countries << CatalogDb::Metal::Country._record('li')
157
108
  when 'us' then countries << CatalogDb::Metal::Country._record('ca')
158
109
  when 'au' then countries << CatalogDb::Metal::Country._record('nz')
159
- end
110
+ end
160
111
 
161
- ## use single ("unwrapped") item for one country
112
+ ## use single ("unwrapped") item for one country
162
113
  ## otherwise use array
163
114
  country = countries.size == 1 ? countries[0] : countries
164
115
  country
@@ -168,15 +119,8 @@ end
168
119
 
169
120
 
170
121
 
171
-
172
122
  require 'footballdb/data' ## pull in catalog.db (built-in/default data/db)
173
123
 
174
- ###
175
- ## add default/built-in catalog here - why? why not?
176
- ## todo/fix - set catalog_path on demand
177
- ## note: for now required for world search setup etc.
178
- SportDb::Import.config.catalog_path = "#{FootballDb::Data.data_dir}/catalog.db"
179
-
180
124
 
181
125
 
182
126
  puts SportDb::Module::Catalogs.banner # say hello
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sportdb-catalogs
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-06-22 00:00:00.000000000 Z
11
+ date: 2024-08-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: sportdb-formats
14
+ name: sportdb-structs
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 0.3.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 0.3.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: sqlite3
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -125,7 +125,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
125
125
  requirements:
126
126
  - - ">="
127
127
  - !ruby/object:Gem::Version
128
- version: 2.2.2
128
+ version: 3.1.0
129
129
  required_rubygems_version: !ruby/object:Gem::Requirement
130
130
  requirements:
131
131
  - - ">="