sportdb-catalogs 1.0.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,106 @@
1
+ module CatalogDb
2
+ module Metal
3
+
4
+ class Ground < Record
5
+ self.tablename = 'grounds'
6
+
7
+ self.columns = ['key',
8
+ 'name',
9
+ 'alt_names', #2
10
+ 'city_key', #3
11
+ 'country_key', #4
12
+ 'district', #5
13
+ 'address', #6
14
+ ]
15
+
16
+
17
+ def self._build_ground( row )
18
+ ## note: cache structs by key (do NOT rebuild duplicates; reuse)
19
+ @cache ||= Hash.new
20
+ @cache[ row[0] ] ||= begin
21
+ ground = Sports::Ground.new(
22
+ key: row[0],
23
+ name: row[1] )
24
+
25
+ if row[2]
26
+ alt_names = row[2].split( '|' )
27
+ alt_names = alt_names.map { |name| name.strip }
28
+ ground.alt_names += alt_names
29
+ end
30
+ ground.city = _to_city( row[3] )
31
+ ground.country = _to_country( row[4] )
32
+ ground.district = row[5]
33
+ ground.address = row[6]
34
+ ground
35
+ end
36
+ end
37
+
38
+
39
+ def self._find_by_name( name )
40
+ rows = execute( <<-SQL )
41
+ SELECT #{self.columns.join(', ')}
42
+ FROM grounds
43
+ INNER JOIN ground_names ON grounds.key = ground_names.key
44
+ WHERE ground_names.name = '#{name}'
45
+ SQL
46
+ rows
47
+ end
48
+
49
+ def self._find_by_name_and_country( name, country )
50
+ rows = execute( <<-SQL )
51
+ SELECT #{self.columns.join(', ')}
52
+ FROM grounds
53
+ INNER JOIN ground_names ON grounds.key = ground_names.key
54
+ WHERE ground_names.name = '#{name}' AND
55
+ grounds.country_key = '#{country}'
56
+ SQL
57
+ rows
58
+ end
59
+
60
+ def self._find_by_name_and_city( name, city )
61
+ rows = execute( <<-SQL )
62
+ SELECT #{self.columns.join(', ')}
63
+ FROM grounds
64
+ INNER JOIN ground_names ON grounds.key = ground_names.key
65
+ WHERE ground_names.name = '#{name}' AND
66
+ grounds.city_key = '#{city}'
67
+ SQL
68
+ rows
69
+ end
70
+
71
+
72
+
73
+ ## match - always returns an array (with one or more matches)
74
+ def self.match_by( name:,
75
+ country: nil,
76
+ city: nil )
77
+ ## note: allow passing in of country key too (auto-counvert)
78
+ ## and country struct too
79
+ ## - country assumes / allows the country key or fifa code for now
80
+
81
+ ## note: ALWAYS unaccent 1st in normalize
82
+ ## - add upstream - why? why not?
83
+ # note: returns empty array (e.g. []) if no match and NOT nil
84
+ nameq = normalize( unaccent(name) )
85
+
86
+ rows = if country && city.nil?
87
+ country = _country( country )
88
+ countryq = country.key
89
+ _find_by_name_and_country( nameq, countryq )
90
+ elsif city && country.nil?
91
+ city = _city( city )
92
+ cityq = city.key
93
+ _find_by_name_and_city( nameq, cityq )
94
+ elsif city.nil? && country.nil?
95
+ _find_by_name( nameq )
96
+ else
97
+ raise ArgumentError, "unsupported city query params pairs; sorry"
98
+ end
99
+
100
+ rows.map {|row| _build_ground( row )}
101
+ end
102
+ end # class Ground
103
+
104
+ end # module Metal
105
+ end # module CatalogDb
106
+
@@ -0,0 +1,87 @@
1
+ module CatalogDb
2
+ module Metal
3
+
4
+ class League < Record
5
+ self.tablename = 'leagues'
6
+
7
+ self.columns = ['key',
8
+ 'name',
9
+ 'intl', # international tournament?
10
+ 'clubs', # clubs or national teams?
11
+ 'country_key']
12
+
13
+
14
+ def self.cache() @cache ||= Hash.new; end
15
+
16
+
17
+ def self._record( key ) ## use _record! as name - why? why not?
18
+ if (rec = cache[ key ])
19
+ rec ## return cached
20
+ else ## query and cache and return
21
+ rows = execute( <<-SQL )
22
+ SELECT #{self.columns.join(', ')}
23
+ FROM leagues
24
+ WHERE leagues.key = '#{key}'
25
+ SQL
26
+
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
31
+ _build_league( rows[0] )
32
+ end
33
+ end
34
+ end
35
+
36
+ def self._build_league( row )
37
+ ## note: cache structs by key (do NOT rebuild duplicates; reuse)
38
+ cache[ row[0] ] ||= Sports::League.new(
39
+ key: row[0],
40
+ name: row[1],
41
+ intl: _to_bool( row[2] ),
42
+ clubs: _to_bool( row[3] ),
43
+ country: row[4] ? _to_country( row[4] ) : nil,
44
+ )
45
+ end
46
+
47
+
48
+ def self.match_by( name:,
49
+ country: nil )
50
+ ## note: match must for now always include name
51
+ name = normalize( unaccent(name) )
52
+
53
+ rows = nil
54
+ if country.nil?
55
+ ## note: returns empty array if no match and NOT nil
56
+ rows = execute( <<-SQL )
57
+ SELECT #{self.columns.join(', ')}
58
+ FROM leagues
59
+ INNER JOIN league_names ON leagues.key = league_names.key
60
+ WHERE league_names.name = '#{name}'
61
+ SQL
62
+ else ## filter by country
63
+ ## note: also skip international leagues & cups (e.g. champions league etc.) for now - why? why not?
64
+
65
+ ## note: country assumes / allows the country key or fifa code for now
66
+ ## note: allow passing in of country struct too
67
+ country_rec = _country( country )
68
+
69
+ rows = execute( <<-SQL )
70
+ SELECT #{self.columns.join(', ')}
71
+ FROM leagues
72
+ INNER JOIN league_names ON leagues.key = league_names.key
73
+ WHERE league_names.name = '#{name}' AND
74
+ leagues.country_key = '#{country_rec.key}'
75
+
76
+ SQL
77
+ end
78
+
79
+ ## wrap results array into struct records
80
+ rows.map {|row| _build_league( row )}
81
+ end
82
+
83
+ end # class League
84
+ end # module Metal
85
+ end # module CatalogDb
86
+
87
+
@@ -0,0 +1,61 @@
1
+ module CatalogDb
2
+ module Metal
3
+
4
+ class NationalTeam < Record
5
+ self.tablename = 'national_teams'
6
+
7
+ self.columns = ['key',
8
+ 'name',
9
+ 'code',
10
+ 'country_key']
11
+
12
+ def self._build_national_team( row )
13
+ ## note: cache structs by key (do NOT rebuild duplicates; reuse)
14
+ @cache ||= Hash.new
15
+ @cache[ row[0] ] ||= begin
16
+ team = Sports::NationalTeam.new(
17
+ key: row[0],
18
+ name: row[1],
19
+ code: row[2]
20
+ )
21
+
22
+ ## note: country for now NOT supported
23
+ ## via keyword on init!!!
24
+ ## fix - why? why not?
25
+ team.country = row[3] ? _to_country( row[3] ) : nil
26
+ team
27
+ end
28
+ end
29
+
30
+
31
+
32
+ def self.find( q )
33
+ q = normalize( unaccent(q.to_s) ) ## allow symbols too (e.g. use to.s first)
34
+
35
+ rows = execute( <<-SQL )
36
+ SELECT #{self.columns.join(', ')}
37
+ FROM national_teams
38
+ INNER JOIN national_team_names ON national_teams.key = national_team_names.key
39
+ WHERE national_team_names.name = '#{q}'
40
+ SQL
41
+
42
+ if rows.empty?
43
+ nil
44
+ else
45
+ _build_national_team( rows[0] )
46
+ end
47
+ end
48
+
49
+
50
+ def self.find!( q )
51
+ team = find( q )
52
+ if team.nil?
53
+ puts "** !!! ERROR - no match for national team >#{q}< found"
54
+ exit 1
55
+ end
56
+ team
57
+ end
58
+ end # class NationalTeam
59
+
60
+ end # module Metal
61
+ end # module CatalogDb
@@ -0,0 +1,125 @@
1
+ module CatalogDb
2
+ module Metal
3
+
4
+ class Player < PlayerRecord
5
+
6
+ ## note - tablename is persons!! (NOT players or even people)
7
+ self.tablename = 'persons'
8
+
9
+ self.columns = ['id', #0
10
+ 'name', #1
11
+ 'alt_names', #2
12
+ 'nat', #3
13
+ 'height', #4
14
+ 'pos', #5
15
+ 'birthdate', #6
16
+ 'birthplace', #7
17
+ ]
18
+
19
+
20
+ ### todo/fix:
21
+ ## change sqlite config to return records as hash NOT array!!!!
22
+ ##
23
+
24
+ def self._build_player( row )
25
+ ## note: cache structs by key (do NOT rebuild duplicates; reuse)
26
+ @cache ||= Hash.new
27
+ @cache[ row[0] ] ||= begin
28
+ ## use Sports::Player.new - why? why not?
29
+ player = Sports::Player.new(
30
+ name: row[1],
31
+ nat: row[3],
32
+ height: row[4],
33
+ pos: row[5], ## make pos into an array??
34
+ birthdate: row[6], ## todo/fix - convert to date??
35
+ birthplace: row[7],
36
+ )
37
+
38
+ if row[2]
39
+ alt_names = row[2].split( '|' )
40
+ alt_names = alt_names.map { |name| name.strip }
41
+ player.alt_names += alt_names
42
+ end
43
+ player
44
+ end
45
+ end
46
+
47
+
48
+ def self._find_by_name( name )
49
+ rows = execute( <<-SQL )
50
+ SELECT #{self.columns.join(', ')}
51
+ FROM persons
52
+ INNER JOIN person_names ON persons.id = person_names.person_id
53
+ WHERE person_names.name = '#{name}'
54
+ SQL
55
+ rows
56
+ end
57
+
58
+ def self._find_by_name_and_country( name, country )
59
+ rows = execute( <<-SQL )
60
+ SELECT #{self.columns.join(', ')}
61
+ FROM persons
62
+ INNER JOIN person_names ON persons.id = person_names.person_id
63
+ WHERE person_names.name = '#{name}' AND
64
+ persons.nat = '#{country}'
65
+ SQL
66
+ rows
67
+ end
68
+
69
+ def self._find_by_name_and_year( name, year )
70
+ ## check if there's a date function for year
71
+ ## (using integer compare instead of string with qoutes???)
72
+ ## use cast to convert year to integer - why? why not?
73
+
74
+ rows = execute( <<-SQL )
75
+ SELECT #{self.columns.join(', ')}
76
+ FROM persons
77
+ INNER JOIN person_names ON persons.id = person_names.person_id
78
+ WHERE person_names.name = '#{name}' AND
79
+ CAST(strftime('%Y',persons.birthdate) AS INTEGER) = #{year}
80
+ SQL
81
+ rows
82
+ end
83
+
84
+ def self._find_by_name_and_year_and_country( name, year, country )
85
+ rows = execute( <<-SQL )
86
+ SELECT #{self.columns.join(', ')}
87
+ FROM persons
88
+ INNER JOIN person_names ON persons.id = person_names.person_id
89
+ WHERE person_names.name = '#{name}' AND
90
+ CAST(strftime('%Y',persons.birthdate) AS INTEGER) = #{year} AND
91
+ persons.nat = '#{country}'
92
+ SQL
93
+ rows
94
+ end
95
+
96
+
97
+ ## match - always returns an array (with one or more matches)
98
+ def self.match_by( name:,
99
+ country: nil,
100
+ year: nil )
101
+ nameq = normalize( unaccent(name) )
102
+
103
+ rows = if country
104
+ country = _country( country )
105
+ countryq = country.key
106
+ if year
107
+ _find_by_name_and_year_and_country( nameq, year, countryq )
108
+ else ## no year (country only)
109
+ _find_by_name_and_country( nameq, countryq )
110
+ end
111
+ elsif year && country.nil?
112
+ _find_by_name_and_year( nameq, year )
113
+ elsif year.nil? && country.nil?
114
+ _find_by_name( nameq )
115
+ else
116
+ raise ArgumentError, "unsupported query for player; sorry"
117
+ end
118
+
119
+ rows.map {|row| _build_player( row )}
120
+ end
121
+ end # class Player
122
+
123
+ end # module Metal
124
+ end # module CatalogDb
125
+
@@ -1,12 +1,10 @@
1
- # encoding: utf-8
2
-
3
1
 
4
2
  module SportDb
5
3
  module Module
6
4
  module Catalogs
7
5
 
8
6
  MAJOR = 1 ## todo: namespace inside version or something - why? why not??
9
- MINOR = 0
7
+ MINOR = 2
10
8
  PATCH = 0
11
9
  VERSION = [MAJOR,MINOR,PATCH].join('.')
12
10
 
@@ -15,7 +13,7 @@ module Catalogs
15
13
  end
16
14
 
17
15
  def self.banner
18
- "sportdb-catalogs/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
16
+ "sportdb-catalogs/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}] in (#{root})"
19
17
  end
20
18
 
21
19
  def self.root
@@ -1,20 +1,182 @@
1
- # encoding: utf-8
2
-
3
1
  ### our own sportdb libs / gems
2
+ ### try min. dependencies - change to structs only (NOT formats) - why? why not?
3
+ ## require 'sportdb/structs'
4
4
  require 'sportdb/formats'
5
5
 
6
- ### "built-in" default dataset libs / gems
7
- require 'fifa' ## get a list of all fifa countries with (three letter) codes
8
- require 'footballdb/leagues'
9
- require 'footballdb/clubs'
6
+ require 'sqlite3'
7
+
10
8
 
11
9
 
12
10
  ###
13
11
  # our own code
14
- require 'sportdb/catalogs/version' # let version always go first
15
- require 'sportdb/catalogs/wiki_index'
16
- require 'sportdb/catalogs/catalog'
17
- require 'sportdb/catalogs/config'
12
+ require_relative 'catalogs/version' # let version always go first
13
+
14
+ require_relative 'catalogs/base' ## base record
15
+ require_relative 'catalogs/country'
16
+ require_relative 'catalogs/city'
17
+
18
+ require_relative 'catalogs/club'
19
+ require_relative 'catalogs/national_team'
20
+ require_relative 'catalogs/league'
21
+ require_relative 'catalogs/event_info'
22
+ require_relative 'catalogs/ground'
23
+
24
+ ## more
25
+ require_relative 'catalogs/player'
26
+
27
+
28
+
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
+ ###
90
+ # add status
91
+ module CatalogDb
92
+ module Metal
93
+
94
+ def self.tables
95
+
96
+ 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"
101
+ puts " #{NationalTeam.count} national teams"
102
+ puts " #{League.count} leagues"
103
+ puts " #{Club.count} clubs"
104
+ puts " #{Ground.count} grounds"
105
+ ## add more
106
+
107
+ else
108
+ puts " - no catalog.db set - "
109
+ end
110
+
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)})"
116
+ puts " #{Player.count} players"
117
+ else
118
+ puts " - no players.db set -"
119
+ end
120
+ end
121
+
122
+ end # module Metal
123
+ end # module CatalogDb
124
+
125
+
126
+
127
+ ###
128
+ # global helpers
129
+ # find a better name/place in module(s) or such
130
+ ##
131
+ ## what name to use?
132
+ ## find_countries_for_league_clubs or such
133
+ ## find_countries_for_league - why? why not?
134
+ ## note - returns array of countries OR single country
135
+
136
+ def find_countries_for_league( league )
137
+ ## todo/fix: assert league is a League with country record/struct !!!!!
138
+
139
+ countries = []
140
+ countries << league.country ### assume league.country is already db record/struct - why? why not?
141
+ ## check for 2nd countries for known leagues
142
+ ## (re)try with second country - quick hacks for known leagues
143
+ ## e.g. Swanse, cardiff in premier league
144
+ ## san mariono in serie a (italy)
145
+ ## monaco in ligue 1 (france)
146
+ ## etc.
147
+ ## add andorra to spanish la liga (e.g. andorra fc???)
148
+
149
+ case league.country.key
150
+ when 'eng' then countries << CatalogDb::Metal::Country._record('wal')
151
+ 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')
155
+ when 'it' then countries << CatalogDb::Metal::Country._record('sm')
156
+ when 'ch' then countries << CatalogDb::Metal::Country._record('li')
157
+ when 'us' then countries << CatalogDb::Metal::Country._record('ca')
158
+ when 'au' then countries << CatalogDb::Metal::Country._record('nz')
159
+ end
160
+
161
+ ## use single ("unwrapped") item for one country
162
+ ## otherwise use array
163
+ country = countries.size == 1 ? countries[0] : countries
164
+ country
165
+ end
166
+
167
+
168
+
169
+
170
+
171
+
172
+ require 'footballdb/data' ## pull in catalog.db (built-in/default data/db)
173
+
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
+
18
180
 
19
181
 
20
182
  puts SportDb::Module::Catalogs.banner # say hello