sportdb-catalogs 1.0.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,74 @@
1
+ module CatalogDb
2
+ module Metal
3
+
4
+ class EventInfo < Record
5
+ self.tablename = 'event_infos'
6
+
7
+ self.columns = ['league_key',
8
+ 'season',
9
+ 'teams',
10
+ 'matches',
11
+ 'goals',
12
+ 'start_date',
13
+ 'end_date']
14
+
15
+
16
+ def self._build_event_info( row )
17
+ ## note: cache structs by key (do NOT rebuild duplicates; reuse)
18
+ @cache ||= Hash.new
19
+ ## note: move EventInfo from sports/format to structs - why? why not?
20
+ @cache[ row[0] ] ||= SportDb::Import::EventInfo.new(
21
+ league: _to_league( row[0] ),
22
+ season: Season.parse(row[1]),
23
+ teams: row[2],
24
+ matches: row[3],
25
+ goals: row[4],
26
+ start_date: row[5] ? Date.strptime( row[5], '%Y-%m-%d' ) : nil,
27
+ end_date: row[6] ? Date.strptime( row[6], '%Y-%m-%d' ) : nil,
28
+ )
29
+ end
30
+
31
+
32
+ def self.seasons( league )
33
+ league_key = league.is_a?( String ) ? League.find!( league ).key
34
+ : league.key
35
+
36
+ rows = execute( <<-SQL )
37
+ SELECT #{self.columns.join(', ')}
38
+ FROM event_infos
39
+ WHERE event_infos.league_key = '#{league_key}'
40
+ SQL
41
+
42
+ rows.map {|row| _build_event_info( row ) }
43
+ end
44
+
45
+
46
+ def self.find_by( league:, season: )
47
+ league_key = league.is_a?( String ) ? League.find!( league ).key
48
+ : league.key
49
+ season_key = season.is_a?( String ) ? Season.parse(season).key
50
+ : season.key
51
+
52
+ rows = execute( <<-SQL )
53
+ SELECT #{self.columns.join(', ')}
54
+ FROM event_infos
55
+ WHERE event_infos.league_key = '#{league_key}' AND
56
+ event_infos.season = '#{season_key}'
57
+ SQL
58
+
59
+ if rows.empty?
60
+ nil
61
+ elsif rows.size > 1 ## not possible in theory
62
+ puts "** !!! ERROR - expected zero or one rows; got (#{rows.size}):"
63
+ pp rows
64
+ exit 1
65
+ else
66
+ _build_event_info( rows[0] )
67
+ end
68
+ end
69
+
70
+ end # class EventInfo
71
+ end # module Metal
72
+ end # module CatalogDb
73
+
74
+
@@ -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,170 @@
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
+ 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 )
87
+ ## note: match must for now always include name
88
+ name = normalize( unaccent(name) )
89
+
90
+ rows = nil
91
+ if country.nil?
92
+ ## note: returns empty array if no match and NOT nil
93
+ rows = execute( <<-SQL )
94
+ SELECT #{self.columns.join(', ')}
95
+ FROM leagues
96
+ INNER JOIN league_names ON leagues.key = league_names.key
97
+ WHERE league_names.name = '#{name}'
98
+ SQL
99
+ else ## filter by country
100
+ ## note: also skip international leagues & cups (e.g. champions league etc.) for now - why? why not?
101
+
102
+ ## note: country assumes / allows the country key or fifa code for now
103
+ ## note: allow passing in of country struct too
104
+ country_rec = _country( country )
105
+
106
+ rows = execute( <<-SQL )
107
+ SELECT #{self.columns.join(', ')}
108
+ FROM leagues
109
+ INNER JOIN league_names ON leagues.key = league_names.key
110
+ WHERE league_names.name = '#{name}' AND
111
+ leagues.country_key = '#{country_rec.key}'
112
+
113
+ SQL
114
+ end
115
+
116
+ ## wrap results array into struct records
117
+ rows.map {|row| _build_league( row )}
118
+ end
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
+
166
+ end # class League
167
+ end # module Metal
168
+ end # module CatalogDb
169
+
170
+
@@ -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,13 +1,11 @@
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
10
- PATCH = 0
7
+ MINOR = 2
8
+ PATCH = 1
11
9
  VERSION = [MAJOR,MINOR,PATCH].join('.')
12
10
 
13
11
  def self.version
@@ -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