football-sources 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
metadata CHANGED
@@ -1,43 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: football-sources
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-21 00:00:00.000000000 Z
11
+ date: 2024-07-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: webget-football
14
+ name: sportdb-catalogs
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.1.1
19
+ version: '0'
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.1.1
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: sportdb-catalogs
28
+ name: sportdb-writers
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: footballdata-api
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: worldfootball
29
57
  requirement: !ruby/object:Gem::Requirement
30
58
  requirements:
31
59
  - - ">="
32
60
  - !ruby/object:Gem::Version
33
- version: 1.0.0
61
+ version: '0'
34
62
  type: :runtime
35
63
  prerelease: false
36
64
  version_requirements: !ruby/object:Gem::Requirement
37
65
  requirements:
38
66
  - - ">="
39
67
  - !ruby/object:Gem::Version
40
- version: 1.0.0
68
+ version: '0'
41
69
  - !ruby/object:Gem::Dependency
42
70
  name: rdoc
43
71
  requirement: !ruby/object:Gem::Requirement
@@ -64,18 +92,19 @@ dependencies:
64
92
  requirements:
65
93
  - - "~>"
66
94
  - !ruby/object:Gem::Version
67
- version: '3.22'
95
+ version: '4.1'
68
96
  type: :development
69
97
  prerelease: false
70
98
  version_requirements: !ruby/object:Gem::Requirement
71
99
  requirements:
72
100
  - - "~>"
73
101
  - !ruby/object:Gem::Version
74
- version: '3.22'
75
- description: football-sources - get football data via web pages or web api (json)
76
- calls
77
- email: opensport@googlegroups.com
78
- executables: []
102
+ version: '4.1'
103
+ description: football-sources - get football match data (leagues, cups & more) via
104
+ web pages or web api (json) calls
105
+ email: gerald.bauer@gmail.com
106
+ executables:
107
+ - fbgen
79
108
  extensions: []
80
109
  extra_rdoc_files:
81
110
  - CHANGELOG.md
@@ -86,34 +115,16 @@ files:
86
115
  - Manifest.txt
87
116
  - README.md
88
117
  - Rakefile
118
+ - bin/fbgen
89
119
  - lib/football-sources.rb
90
- - lib/football-sources/apis.rb
91
- - lib/football-sources/apis/config.rb
92
- - lib/football-sources/apis/convert.rb
93
- - lib/football-sources/apis/convert_cl.rb
94
- - lib/football-sources/apis/mods.rb
95
- - lib/football-sources/apis/stat.rb
96
- - lib/football-sources/fbref.rb
97
- - lib/football-sources/fbref/build.rb
98
- - lib/football-sources/fbref/config.rb
99
- - lib/football-sources/fbref/convert.rb
120
+ - lib/football-sources/process.rb
100
121
  - lib/football-sources/version.rb
101
- - lib/football-sources/worldfootball.rb
102
- - lib/football-sources/worldfootball/build.rb
103
- - lib/football-sources/worldfootball/config.rb
104
- - lib/football-sources/worldfootball/convert.rb
105
- - lib/football-sources/worldfootball/convert_reports.rb
106
- - lib/football-sources/worldfootball/jobs.rb
107
- - lib/football-sources/worldfootball/mods.rb
108
- - lib/football-sources/worldfootball/vacuum.rb
109
122
  - lib/football/sources.rb
110
- - test/helper.rb
111
- - test/test_version.rb
112
123
  homepage: https://github.com/sportdb/sport.db
113
124
  licenses:
114
125
  - Public Domain
115
126
  metadata: {}
116
- post_install_message:
127
+ post_install_message:
117
128
  rdoc_options:
118
129
  - "--main"
119
130
  - README.md
@@ -130,9 +141,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
130
141
  - !ruby/object:Gem::Version
131
142
  version: '0'
132
143
  requirements: []
133
- rubyforge_project:
134
- rubygems_version: 2.5.2
135
- signing_key:
144
+ rubygems_version: 3.4.10
145
+ signing_key:
136
146
  specification_version: 4
137
- summary: football-sources - get football data via web pages or web api (json) calls
147
+ summary: football-sources - get football match data (leagues, cups & more) via web
148
+ pages or web api (json) calls
138
149
  test_files: []
@@ -1,17 +0,0 @@
1
-
2
- module Footballdata
3
-
4
- ### add some more config options / settings
5
- class Configuration
6
- #########
7
- ## nested configuration classes - use - why? why not?
8
- class Convert
9
- def out_dir() @out_dir || './o'; end
10
- def out_dir=(value) @out_dir = value; end
11
- end
12
-
13
- def convert() @convert ||= Convert.new; end
14
- end # class Configuration
15
-
16
-
17
- end # module Footballdata
@@ -1,239 +0,0 @@
1
-
2
-
3
- module Footballdata
4
-
5
-
6
- def self.convert( league:, season: )
7
-
8
- ### note/fix: cl (champions league for now is a "special" case)
9
- if league.downcase == 'cl'
10
- convert_cl( league: league,
11
- season: season )
12
- return
13
- end
14
-
15
-
16
-
17
- season = Season( season ) ## cast (ensure) season class (NOT string, integer, etc.)
18
-
19
- data = Webcache.read_json( Metal.competition_matches_url( LEAGUES[league.downcase], season.start_year ))
20
- data_teams = Webcache.read_json( Metal.competition_teams_url( LEAGUES[league.downcase], season.start_year ))
21
-
22
-
23
- ## build a (reverse) team lookup by name
24
- puts "#{data_teams['teams'].size} teams"
25
-
26
- teams_by_name = data_teams['teams'].reduce( {} ) do |h,rec|
27
- h[ rec['name'] ] = rec
28
- h
29
- end
30
-
31
- pp teams_by_name.keys
32
-
33
-
34
-
35
- mods = MODS[ league.downcase ] || {}
36
-
37
-
38
- recs = []
39
-
40
- teams = Hash.new( 0 )
41
-
42
- stat = Stat.new
43
-
44
- matches = data[ 'matches']
45
- matches.each do |m|
46
- stat.update( m )
47
-
48
- team1 = m['homeTeam']['name']
49
- team2 = m['awayTeam']['name']
50
-
51
- score = m['score']
52
-
53
-
54
-
55
- if m['stage'] == 'REGULAR_SEASON'
56
- teams[ team1 ] += 1
57
- teams[ team2 ] += 1
58
-
59
- ### mods - rename club names
60
- unless mods.nil? || mods.empty?
61
- team1 = mods[ team1 ] if mods[ team1 ]
62
- team2 = mods[ team2 ] if mods[ team2 ]
63
- end
64
-
65
-
66
- ## e.g. "utcDate": "2020-05-09T00:00:00Z",
67
- date_str = m['utcDate']
68
- date = DateTime.strptime( date_str, '%Y-%m-%dT%H:%M:%SZ' )
69
-
70
-
71
- comments = ''
72
- ft = ''
73
- ht = ''
74
-
75
- case m['status']
76
- when 'SCHEDULED', 'IN_PLAY'
77
- ft = ''
78
- ht = ''
79
- when 'FINISHED'
80
- ## todo/fix: assert duration == "REGULAR"
81
- ft = "#{score['fullTime']['homeTeam']}-#{score['fullTime']['awayTeam']}"
82
- ht = "#{score['halfTime']['homeTeam']}-#{score['halfTime']['awayTeam']}"
83
- when 'AWARDED'
84
- ## todo/fix: assert duration == "REGULAR"
85
- ft = "#{score['fullTime']['homeTeam']}-#{score['fullTime']['awayTeam']}"
86
- ft << ' (*)'
87
- ht = ''
88
- comments = 'awarded'
89
- when 'CANCELLED'
90
- ft = '(*)'
91
- ht = ''
92
- comments = 'canceled' ## us eng ? -> canceled, british eng. cancelled ?
93
- when 'POSTPONED'
94
- ft = '(*)'
95
- ht = ''
96
- comments = 'postponed'
97
- else
98
- puts "!! ERROR: unsupported match status >#{m['status']}< - sorry:"
99
- pp m
100
- exit 1
101
- end
102
-
103
-
104
- ## todo/fix: assert matchday is a number e.g. 1,2,3, etc.!!!
105
- recs << [m['matchday'],
106
- date.to_date.strftime( '%Y-%m-%d' ),
107
- team1,
108
- ft,
109
- ht,
110
- team2,
111
- comments
112
- ]
113
-
114
-
115
- print '%2s' % m['matchday']
116
- print ' - '
117
- print '%-24s' % team1
118
- print ' '
119
- print ft
120
- print ' '
121
- print "(#{ht})" unless ht.empty?
122
- print ' '
123
- print '%-24s' % team2
124
- print ' '
125
- print comments
126
- print ' | '
127
- ## print date.to_date ## strip time
128
- print date.to_date.strftime( '%a %b %-d %Y' )
129
- print ' -- '
130
- print date
131
- print "\n"
132
- else
133
- puts "-- skipping #{m['stage']}"
134
- end
135
- end # each match
136
-
137
-
138
-
139
- ## note: get season from first match
140
- ## assert - all other matches include the same season
141
- ## e.g.
142
- # "season": {
143
- # "id": 154,
144
- # "startDate": "2018-08-03",
145
- # "endDate": "2019-05-05",
146
- # "currentMatchday": 46
147
- # }
148
-
149
- start_date = Date.strptime( matches[0]['season']['startDate'], '%Y-%m-%d' )
150
- end_date = Date.strptime( matches[0]['season']['endDate'], '%Y-%m-%d' )
151
-
152
- dates = "#{start_date.strftime('%b %-d')} - #{end_date.strftime('%b %-d')}"
153
-
154
- buf = ''
155
- buf << "#{season.key} (#{dates}) - "
156
- buf << "#{teams.keys.size} clubs, "
157
- buf << "#{stat[:regular_season][:matches]} matches, "
158
- buf << "#{stat[:regular_season][:goals]} goals"
159
- buf << "\n"
160
-
161
- puts buf
162
-
163
-
164
-
165
- ## note: warn if stage is greater one and not regular season!!
166
- File.open( './errors.txt' , 'a:utf-8' ) do |f|
167
- if stat[:all][:stage].keys != ['REGULAR_SEASON']
168
- f.write "!! WARN - league: #{league}, season: #{season.key} includes non-regular stage(s):\n"
169
- f.write " #{stat[:all][:stage].keys.inspect}\n"
170
- end
171
- end
172
-
173
-
174
- File.open( './logs.txt', 'a:utf-8' ) do |f|
175
- f.write "\n================================\n"
176
- f.write "==== #{league} =============\n"
177
- f.write buf
178
- f.write " match status: #{stat[:regular_season][:status].inspect}\n"
179
- f.write " match duration: #{stat[:regular_season][:duration].inspect}\n"
180
-
181
- f.write "#{teams.keys.size} teams:\n"
182
- teams.each do |name, count|
183
- rec = teams_by_name[ name ]
184
- f.write " #{count}x #{name}"
185
- if rec
186
- f.write " | #{rec['shortName']} " if name != rec['shortName']
187
- f.write " › #{rec['area']['name']}"
188
- f.write " - #{rec['address']}"
189
- else
190
- puts "!! ERROR - no team record found in teams.json for >#{name}<"
191
- exit 1
192
- end
193
- f.write "\n"
194
- end
195
- end
196
-
197
-
198
-
199
-
200
- # recs = recs.sort { |l,r| l[1] <=> r[1] }
201
- ## reformat date / beautify e.g. Sat Aug 7 1993
202
- recs.each { |rec| rec[1] = Date.strptime( rec[1], '%Y-%m-%d' ).strftime( '%a %b %-d %Y' ) }
203
-
204
- headers = [
205
- 'Matchday',
206
- 'Date',
207
- 'Team 1',
208
- 'FT',
209
- 'HT',
210
- 'Team 2',
211
- 'Comments'
212
- ]
213
-
214
- ## note: change season_key from 2019/20 to 2019-20 (for path/directory!!!!)
215
- Cache::CsvMatchWriter.write( "#{config.convert.out_dir}/#{season.to_path}/#{league.downcase}.csv",
216
- recs,
217
- headers: headers )
218
-
219
-
220
- teams.each do |name, count|
221
- rec = teams_by_name[ name ]
222
- print " #{count}x "
223
- print name
224
- if rec
225
- print " | #{rec['shortName']} " if name != rec['shortName']
226
- print " › #{rec['area']['name']}"
227
- print " - #{rec['address']}"
228
- else
229
- puts "!! ERROR - no team record found in teams.json for #{name}"
230
- exit 1
231
- end
232
- print "\n"
233
- end
234
-
235
- pp stat
236
- end # method convert
237
- end # module Footballdata
238
-
239
-
@@ -1,267 +0,0 @@
1
- ##########
2
- ## todo/fix:
3
- ## use a more generic name?
4
- ## might also works for euro or worldcup - check?
5
-
6
-
7
- module Footballdata
8
-
9
-
10
- STAGE_TO_STAGE = {
11
- 'PRELIMINARY_SEMI_FINALS' => 'Qualifying',
12
- 'PRELIMINARY_FINAL' => 'Qualifying',
13
- '1ST_QUALIFYING_ROUND' => 'Qualifying',
14
- '2ND_QUALIFYING_ROUND' => 'Qualifying',
15
- '3RD_QUALIFYING_ROUND' => 'Qualifying',
16
- 'PLAY_OFF_ROUND' => 'Qualifying',
17
- 'ROUND_OF_16' => 'Knockout',
18
- 'QUARTER_FINALS' => 'Knockout',
19
- 'SEMI_FINALS' => 'Knockout',
20
- 'FINAL' => 'Knockout',
21
- }
22
-
23
- STAGE_TO_ROUND = {
24
- 'PRELIMINARY_SEMI_FINALS' => 'Preliminary Semifinals',
25
- 'PRELIMINARY_FINAL' => 'Preliminary Final',
26
- '1ST_QUALIFYING_ROUND' => 'Qual. Round 1',
27
- '2ND_QUALIFYING_ROUND' => 'Qual. Round 2',
28
- '3RD_QUALIFYING_ROUND' => 'Qual. Round 3',
29
- 'PLAY_OFF_ROUND' => 'Playoff Round',
30
- 'ROUND_OF_16' => 'Round of 16',
31
- 'QUARTER_FINALS' => 'Quarterfinals',
32
- 'SEMI_FINALS' => 'Semifinals',
33
- 'FINAL' => 'Final',
34
- }
35
-
36
-
37
-
38
-
39
- def self.convert_cl( league:, season: )
40
- season = Season( season ) ## cast (ensure) season class (NOT string, integer, etc.)
41
-
42
- data = Webcache.read_json( Metal.competition_matches_url( LEAGUES[league.downcase], season.start_year ))
43
- data_teams = Webcache.read_json( Metal.competition_teams_url( LEAGUES[league.downcase], season.start_year ))
44
-
45
-
46
- ## build a (reverse) team lookup by name
47
- puts "#{data_teams['teams'].size} teams"
48
-
49
- teams_by_name = data_teams['teams'].reduce( {} ) do |h,rec|
50
- h[ rec['name'] ] = rec
51
- h
52
- end
53
-
54
- pp teams_by_name.keys
55
-
56
-
57
-
58
- mods = MODS[ league.downcase ] || {}
59
-
60
-
61
- recs = []
62
-
63
- teams = Hash.new( 0 )
64
- stat = Stat.new
65
-
66
- matches = data[ 'matches' ]
67
- matches.each do |m|
68
- stat.update( m )
69
-
70
- team1 = m['homeTeam']['name']
71
- team2 = m['awayTeam']['name']
72
-
73
-
74
- score = m['score']
75
-
76
-
77
-
78
- if m['stage'] == 'GROUP_STAGE'
79
- stage = 'Group'
80
- round = "Matchday #{m['matchday']}"
81
- if m['group'] =~ /Group ([A-Z])/
82
- group = $1
83
- else
84
- puts "!! ERROR - no group name found for group >#{m['group']}<"
85
- exit 1
86
- end
87
- else
88
- stage = STAGE_TO_STAGE[ m['stage'] ]
89
- if stage.nil?
90
- puts "!! ERROR - no stage mapping found for stage >#{m['stage']}<"
91
- exit 1
92
- end
93
- round = STAGE_TO_ROUND[ m['stage'] ]
94
- if round.nil?
95
- puts "!! ERROR - no round mapping found for stage >#{m['stage']}<"
96
- exit 1
97
- end
98
- group = ''
99
- end
100
-
101
-
102
- teams[ team1 ] += 1
103
- teams[ team2 ] += 1
104
-
105
- ### mods - rename club names
106
- unless mods.nil? || mods.empty?
107
- team1 = mods[ team1 ] if mods[ team1 ]
108
- team2 = mods[ team2 ] if mods[ team2 ]
109
- end
110
-
111
-
112
- ## e.g. "utcDate": "2020-05-09T00:00:00Z",
113
- date_str = m['utcDate']
114
- date = DateTime.strptime( date_str, '%Y-%m-%dT%H:%M:%SZ' )
115
-
116
-
117
- comments = ''
118
- ft = ''
119
- ht = ''
120
- et = ''
121
- pen = ''
122
-
123
- case m['status']
124
- when 'SCHEDULED', 'IN_PLAY'
125
- ft = ''
126
- ht = ''
127
- et = ''
128
- pen = ''
129
- when 'AWARDED'
130
- ## todo/fix: assert duration == "REGULAR"
131
- ft = "#{score['fullTime']['homeTeam']}-#{score['fullTime']['awayTeam']}"
132
- ft << ' (*)'
133
- ht = ''
134
- comments = 'awarded'
135
- when 'FINISHED'
136
- ## note: if extraTime present
137
- ## than fullTime is extraTime score!!
138
- ## AND fullTime - extraTime is fullTime score!!
139
- ## double check in other season too??
140
- ## - checked in cl 2018/19
141
-
142
- if score['extraTime']['homeTeam'] && score['extraTime']['awayTeam']
143
- et = "#{score['fullTime']['homeTeam']}-#{score['fullTime']['awayTeam']}"
144
- ft = "#{score['fullTime']['homeTeam']-score['extraTime']['homeTeam']}-#{score['fullTime']['awayTeam']-score['extraTime']['awayTeam']}"
145
- else
146
- ft = "#{score['fullTime']['homeTeam']}-#{score['fullTime']['awayTeam']}"
147
- end
148
-
149
- ht = "#{score['halfTime']['homeTeam']}-#{score['halfTime']['awayTeam']}"
150
-
151
- pen = if score['penalties']['homeTeam'] && score['penalties']['awayTeam']
152
- "#{score['penalties']['homeTeam']}-#{score['penalties']['awayTeam']}"
153
- else
154
- ''
155
- end
156
- else
157
- puts "!! ERROR: unsupported match status >#{m['status']}< - sorry:"
158
- pp m
159
- exit 1
160
- end
161
-
162
-
163
- recs << [stage,
164
- round,
165
- group,
166
- date.to_date.strftime( '%Y-%m-%d' ),
167
- team1,
168
- ft,
169
- ht,
170
- team2,
171
- et,
172
- pen,
173
- comments
174
- ]
175
-
176
-
177
- print '%2s' % m['matchday']
178
- print ' - '
179
- print '%-24s' % team1
180
- print ' '
181
- print ft
182
- print ' '
183
- print "(#{ht})" unless ht.empty?
184
- print ' '
185
- print '%-24s' % team2
186
- print ' '
187
- print comments
188
- print ' | '
189
- ## print date.to_date ## strip time
190
- print date.to_date.strftime( '%a %b %-d %Y' )
191
- print ' -- '
192
- print date
193
- print "\n"
194
-
195
- end # each match
196
-
197
-
198
- ## note: get season from first match
199
- ## assert - all other matches include the same season
200
- ## e.g.
201
- # "season": {
202
- # "id": 154,
203
- # "startDate": "2018-08-03",
204
- # "endDate": "2019-05-05",
205
- # "currentMatchday": 46
206
- # }
207
-
208
- start_date = Date.strptime( matches[0]['season']['startDate'], '%Y-%m-%d' )
209
- end_date = Date.strptime( matches[0]['season']['endDate'], '%Y-%m-%d' )
210
-
211
- dates = "#{start_date.strftime('%b %-d')} - #{end_date.strftime('%b %-d')}"
212
-
213
- buf = ''
214
- buf << "#{season.key} (#{dates}) - "
215
- buf << "#{teams.keys.size} clubs, "
216
- buf << "#{stat[:all][:matches]} matches, "
217
- buf << "#{stat[:all][:goals]} goals"
218
- buf << "\n"
219
-
220
- puts buf
221
-
222
-
223
-
224
- # recs = recs.sort { |l,r| l[1] <=> r[1] }
225
- ## reformat date / beautify e.g. Sat Aug 7 1993
226
- recs.each { |rec| rec[3] = Date.strptime( rec[3], '%Y-%m-%d' ).strftime( '%a %b %-d %Y' ) }
227
-
228
- headers = [
229
- 'Stage',
230
- 'Round',
231
- 'Group',
232
- 'Date',
233
- 'Team 1',
234
- 'FT',
235
- 'HT',
236
- 'Team 2',
237
- 'ET',
238
- 'P',
239
- 'Comments'
240
- ]
241
-
242
- ## note: change season_key from 2019/20 to 2019-20 (for path/directory!!!!)
243
- Cache::CsvMatchWriter.write( "#{config.convert.out_dir}/#{season.to_path}/#{league.downcase}.csv",
244
- recs,
245
- headers: headers )
246
-
247
-
248
- teams.each do |name, count|
249
- rec = teams_by_name[ name ]
250
- print " #{count}x "
251
- print name
252
- if rec
253
- print " | #{rec['shortName']} " if name != rec['shortName']
254
- print " › #{rec['area']['name']}"
255
- print " - #{rec['address']}"
256
- else
257
- print "!! ERROR - no team record found in teams.json for #{name}"
258
- exit 1
259
- end
260
- print "\n"
261
- end
262
-
263
- pp stat
264
- end # method convert_cl
265
- end # module Footballdata
266
-
267
-
@@ -1,20 +0,0 @@
1
- module Footballdata
2
-
3
- #########
4
- ## Mods
5
- # e.g.
6
- # Cardiff City FC | Cardiff › Wales - Cardiff City Stadium, Leckwith Road Cardiff CF11 8AZ
7
- # AS Monaco FC | Monaco › Monaco - Avenue des Castellans Monaco 98000
8
-
9
-
10
- MODS = {
11
- 'br.1' => {
12
- 'América FC' => 'América MG', # in year 2018
13
- },
14
- 'pt.1' => {
15
- 'Vitória SC' => 'Vitória Guimarães', ## avoid easy confusion with Vitória SC <=> Vitória FC
16
- 'Vitória FC' => 'Vitória Setúbal',
17
- },
18
- }
19
-
20
- end # module Footballdata