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 +4 -4
- data/CHANGELOG.md +1 -1
- data/README.md +16 -20
- data/Rakefile +3 -3
- data/lib/sportdb/catalogs/base.rb +30 -21
- data/lib/sportdb/catalogs/country.rb +67 -30
- data/lib/sportdb/catalogs/event_info.rb +17 -18
- data/lib/sportdb/catalogs/league.rb +104 -21
- data/lib/sportdb/catalogs/version.rb +1 -1
- data/lib/sportdb/catalogs.rb +41 -97
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '09d5c1b4a2fea093abfc294ccbf6149315a335a07a5e934d25fb652215e0ab23'
|
4
|
+
data.tar.gz: 991f6cd9ee018fa4f7d84be847c5868a024ac240ec5d29cb60636addbd1984c5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ffaca4423b4dc8adba0db61187dfcf2cc0b1fb5c47255953ed08e8e13bc1a0e2967673d7f1a60f5d6a7045b93ff3575fc27114ed86be15c27f72041287e59a91
|
7
|
+
data.tar.gz: ded797ffeae2ce160c1d6477416fa90d8b26433e78aca4bdc61c82bf0bdf8eef95f346c2f2fe95e070be962d8789d511d222379057245a9d649388b0a40d93c1
|
data/CHANGELOG.md
CHANGED
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
|
-
(
|
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
|
-
|
23
|
-
## see https://github.com/openfootball/clubs
|
24
|
-
SportDb::Import.config.clubs_dir = './clubs'
|
22
|
+
Club = CatalogDb::Metal::Club
|
25
23
|
|
26
|
-
|
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 =
|
35
|
+
m = Club.match_by( name: 'Arsenal', country: 'eng' )
|
40
36
|
# -or- try alternative names (and auto-generated spelling variants)
|
41
|
-
m =
|
42
|
-
m =
|
43
|
-
m =
|
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 =
|
45
|
+
m = Club.match_by( name: 'Arsenal', country: 'ar' )
|
50
46
|
# -or- try alternative names (and auto-generated spelling variants)
|
51
|
-
m =
|
52
|
-
m =
|
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 =
|
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 =
|
60
|
+
m = Club.match_by( name: 'Bayern' )
|
65
61
|
# -or- try alternative names (and auto-generated spelling variants)
|
66
|
-
m =
|
67
|
-
m =
|
68
|
-
m =
|
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-
|
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: '>=
|
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(
|
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
|
-
|
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
|
-
|
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.
|
49
|
-
|
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
|
data/lib/sportdb/catalogs.rb
CHANGED
@@ -1,7 +1,4 @@
|
|
1
|
-
|
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
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
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
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
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 "
|
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.
|
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-
|
11
|
+
date: 2024-08-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name: sportdb-
|
14
|
+
name: sportdb-structs
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
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:
|
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:
|
128
|
+
version: 3.1.0
|
129
129
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
130
|
requirements:
|
131
131
|
- - ">="
|