footballdata-api 0.4.1 → 0.4.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0556b03185d985c2f364151400a6dfe04ee9160e8560739e005e00a3e872b806
4
- data.tar.gz: 17711037928d5c213a7915245a2d511e7fba6f4c47ff582357616c87a1ffd7cc
3
+ metadata.gz: 97e75a5cbf4b952cb018a40c4ff790a201c26cbd95af1027bae3e7ac2153d1b5
4
+ data.tar.gz: a4d0cfd2e6ff636f030cd6c4d4d949f2173763cc39352b28b3425ad9aa077796
5
5
  SHA512:
6
- metadata.gz: d1bbeef48c5633b97e984e535857ba294a3326cddf1fd6d20ea426d33c95c0e0c7d6e9d12f9fa4a97a12291ba3c1d12e4751f57a5f0bfe3e736ebcf34be56f51
7
- data.tar.gz: 2640f463715d6a0ff697502dfdd8af38e3c7cf0d6dafc0fb575d44360b548a692ff9c256d6e3ad4e9673dd108636969f372d7a2e153d872ee8c240b2e470e4c6
6
+ metadata.gz: d1c8650effbb80caec23410ee3fc739f379976359f044b29eff3a02d7bd82f23d76f7daffd6b2cfa76dfbdcbc6be53e178a04929b02401a30641f29cb3d0bd18
7
+ data.tar.gz: 39d810683288fc35703f19fd1643af8bb0b8332168ae4d4e819eb681444ea4c27e7c0b36daccf27fc97d02704aa6325696579a6d5b3941988cd6bef8f400d0ec
data/CHANGELOG.md CHANGED
@@ -1,4 +1,4 @@
1
- ### 0.4.1
1
+ ### 0.4.2
2
2
 
3
3
  ### 0.0.1 / 2024-07-03
4
4
 
data/Manifest.txt CHANGED
@@ -3,7 +3,7 @@ Manifest.txt
3
3
  README.md
4
4
  Rakefile
5
5
  bin/fbdat
6
- config/leagues.csv
6
+ config/leagues_tier1.csv
7
7
  lib/footballdata.rb
8
8
  lib/footballdata/convert-score.rb
9
9
  lib/footballdata/convert.rb
data/bin/fbdat CHANGED
@@ -16,14 +16,6 @@ Webcache.root = if File.exist?( '/sports/cache' )
16
16
  './cache'
17
17
  end
18
18
 
19
- ## note - free tier (tier one) plan - 10 requests/minute
20
- ## (one request every 6 seconds 6*10=60 secs)
21
- ## 10 API calls per minute max.
22
- ## note - default sleep (delay in secs) is 3 sec(s)
23
-
24
- ## change from 10 to 1 sec(s) for interactive use
25
- Webget.config.sleep = 1
26
-
27
19
 
28
20
  Footballdata.config.convert.out_dir = if File.exist?( '/sports/cache.api.fbdat' )
29
21
  puts " setting convert out_dir to >/sports/cache.api.fbdat<"
@@ -43,6 +35,7 @@ def self.main( args=ARGV )
43
35
  opts = {
44
36
  cached: false,
45
37
  convert: true,
38
+ file: nil,
46
39
  }
47
40
 
48
41
  parser = OptionParser.new do |parser|
@@ -53,9 +46,20 @@ parser = OptionParser.new do |parser|
53
46
  opts[:cached] = cached
54
47
  end
55
48
 
56
- parser.on( "--no-convert",
57
- "turn off conversion to .csv in #{Footballdata.config.convert.out_dir} - default is (#{!opts[:convert]})" ) do |convert|
58
- opts[:convert] = !convert
49
+ parser.on( "--[no-]convert",
50
+ "turn on/off conversion to .csv in #{Footballdata.config.convert.out_dir} - default is (#{opts[:convert]})" ) do |convert|
51
+ opts[:convert] = convert
52
+ end
53
+
54
+ parser.on( "--print", "--pp",
55
+ "pretty print cached data in #{Webcache.root}; no download & conversion") do |print|
56
+ opts[:cached] = true
57
+ opts[:convert] = false
58
+ end
59
+
60
+ parser.on( "-f FILE", "--file FILE",
61
+ "read leagues (and seasons) via .csv file") do |file|
62
+ opts[:file] = file
59
63
  end
60
64
  end
61
65
  parser.parse!( args )
@@ -68,6 +72,19 @@ puts "ARGV:"
68
72
  p args
69
73
 
70
74
 
75
+ ## note - free tier (tier one) plan - 10 requests/minute
76
+ ## (one request every 6 seconds 6*10=60 secs)
77
+ ## 10 API calls per minute max.
78
+ ## note - default sleep (delay in secs) is 3 sec(s)
79
+
80
+ ## change from 10 to 1 sec(s) for interactive use
81
+ ## assume --file/-f as non-interactive/batch use for now
82
+ Webget.config.sleep = opts[:file] ? 10 : 1
83
+
84
+
85
+
86
+
87
+
71
88
  ## try special args
72
89
 
73
90
  if ['plan', 'plans',
@@ -118,11 +135,13 @@ end
118
135
  ##
119
136
  ## todo - add more date offsets - t+2,t+3,t+4, etc.
120
137
 
138
+
139
+
121
140
  date = if ['y', 'yesterday', 't-1', '-1'].include?( args[0] )
122
141
  Date.today-1
123
142
  elsif ['t', 'tomorrow', 't+1', '1', '+1'].include?( args[0] )
124
143
  Date.today+1
125
- elsif ['m', 'match', 'matches', 'today'].include?( args[0] || 'today' ) ## make default - why? why not?
144
+ elsif ['m', 'match', 'matches', 'today'].include?( args[0] )
126
145
  Date.today
127
146
  else
128
147
  nil
@@ -170,51 +189,81 @@ end
170
189
  ##
171
190
  ## note - only use "generic" uniform league codes for now!!
172
191
 
173
- league_code = (args[0] || 'eng.1').downcase
174
-
175
- ## todo - find a better name
176
- ## use internal_league_code or such - why? why not?
177
- ### convenience helpers - lets you use eng.1, euro, etc.
178
- ## check if mapping for league_code
179
- metal_league_code = find_league!( league_code )
180
192
 
181
-
182
- season = Season( args[1] ||
183
- (['euro',
184
- 'copa.l',
185
- 'br.1'].include?(league_code) ? '2024' : '2024/25'))
186
-
187
- season_start_year = season.start_year ## use year - why? why not?
188
-
189
- pp [metal_league_code, season_start_year]
190
-
191
- if opts[:cached]
192
- ## do nothing
193
- else
194
- ## download dataset(s)
195
- ## try download
196
- ## note: include teams (for convert) for now too!!
197
- Metal.teams( metal_league_code, season_start_year )
198
- Metal.matches( metal_league_code, season_start_year )
193
+ datasets = if opts[:file]
194
+ read_datasets( opts[:file] )
195
+ else
196
+ parse_datasets_args( args )
197
+ end
198
+
199
+
200
+ ## step 0 - validate and fill-up seasons etc.
201
+ datasets.each do |dataset|
202
+ league_key, seasons = dataset
203
+
204
+ ## todo - find a better name
205
+ ## use internal_league_code or such - why? why not?
206
+ ### convenience helpers - lets you use eng.1, euro, etc.
207
+ ## check if mapping for league_code
208
+ metal_league_code = find_league!( league_key )
209
+
210
+ ## note - default to latest season of league
211
+ ## might be 2024/25 or 2024 or
212
+ # for world cup 2022 or such
213
+ if seasons.empty?
214
+ seasons = case league_key
215
+ when 'world' then [Season('2022')]
216
+ when 'euro' then [Season('2024')]
217
+ when 'br.1', 'copa.l' then [Season('2024')]
218
+ else [Season('2024/25')]
219
+ end
220
+ dataset[1] = seasons
221
+ end
199
222
  end
200
223
 
201
- url = Metal.competition_matches_url( metal_league_code,
202
- season_start_year )
203
- pp url
204
- #=> "http://api.football-data.org/v4/competitions/EC/matches?season=2024"
224
+ ## step 1 - download
225
+ datasets.each do |league_key, seasons|
226
+ ## todo - find a better name
227
+ ## use internal_league_code or such - why? why not?
228
+ ### convenience helpers - lets you use eng.1, euro, etc.
229
+ ## check if mapping for league_code
230
+ metal_league_code = find_league!( league_key )
231
+ seasons.each do |season|
232
+ season_start_year = season.start_year ## use year - why? why not?
233
+ pp [metal_league_code, season_start_year]
234
+
235
+ if opts[:cached]
236
+ ## do nothing
237
+ else
238
+ ## download dataset(s)
239
+ ## try download
240
+ ## note: include teams (for convert) for now too!!
241
+ Metal.teams( metal_league_code, season_start_year )
242
+ Metal.matches( metal_league_code, season_start_year )
243
+ end
244
+
245
+ url = Metal.competition_matches_url( metal_league_code,
246
+ season_start_year )
247
+ pp url
248
+ #=> "http://api.football-data.org/v4/competitions/EC/matches?season=2024"
205
249
 
206
- data = Webcache.read_json( url )
207
- ## pp data
250
+ data = Webcache.read_json( url )
251
+ ## pp data
208
252
 
209
- pp_matches( data )
253
+ pp_matches( data )
254
+ end # each season
255
+ end # each dataset
210
256
 
211
257
 
212
258
  if opts[:convert]
213
- puts "==> converting to .csv"
214
- convert( league: league_code, season: season )
259
+ puts "==> converting to .csv"
260
+ datasets.each do |league_key, seasons|
261
+ seasons.each do |season|
262
+ convert( league: league_key, season: season )
263
+ end
264
+ end
215
265
  end
216
266
 
217
-
218
267
  end # def self.main
219
268
  end # module Footballdata
220
269
 
@@ -0,0 +1,44 @@
1
+ key, code, seasons
2
+
3
+ eng.1, PL, 2024/25 2023/24 2022/23 2021/22 2020/21
4
+ # incl. team(s) from wales
5
+ eng.2, ELC, 2024/25 2023/24 2022/23 2021/22 2020/21
6
+ # PL - Premier League, England 27 seasons | 2019-08-09 - 2020-07-25 / matchday 31
7
+ # ELC - Championship, England 3 seasons | 2019-08-02 - 2020-07-22 / matchday 38
8
+
9
+ es.1, PD, 2024/25 2023/24 2022/23 2021/22 2020/21
10
+ # PD - Primera Division, Spain 27 seasons | 2019-08-16 - 2020-07-19 / matchday 31
11
+
12
+ pt.1, PPL, 2024/25 2023/24 2022/23 2021/22 2020/21
13
+ # PPL - Primeira Liga, Portugal 9 seasons | 2019-08-10 - 2020-07-26 / matchday 28
14
+
15
+ de.1, BL1, 2024/25 2023/24 2022/23 2021/22 2020/21
16
+ # BL1 - Bundesliga, Germany 24 seasons | 2019-08-16 - 2020-06-27 / matchday 34
17
+
18
+ nl.1, DED, 2024/25 2023/24 2022/23 2021/22 2020/21
19
+ # DED - Eredivisie, Netherlands 10 seasons | 2019-08-09 - 2020-03-08 / matchday 34
20
+
21
+ fr.1, FL1, 2024/25 2023/24 2022/23 2021/22 2020/21
22
+ # incl. team(s) monaco
23
+
24
+ it.1, SA, 2024/25 2023/24 2022/23 2021/22 2020/21
25
+ # SA - Serie A, Italy 15 seasons | 2019-08-24 - 2020-08-02 / matchday 27
26
+
27
+ br.1, BSA, 2024 2023 2022 2021 2020
28
+
29
+
30
+ ########
31
+ ## int'l cups
32
+ uefa.cl, CL, 2024/25 2023/24 2022/23 2021/22 2020/21
33
+ ## note: cl is country code for chile!! - use champs - why? why not?
34
+ ## was europe.cl / cl
35
+ ## todo/check: use champs and NOT cl - why? why not?
36
+
37
+ copa.l, CLI, 2024 2023 2022 2021
38
+ ## Copa Libertadores
39
+
40
+
41
+ ############
42
+ ## national teams
43
+ euro, EC, 2024 2021
44
+ world, WC, 2022
@@ -6,15 +6,16 @@ module Footballdata
6
6
  STAGES = {
7
7
  'REGULAR_SEASON' => ['Regular'],
8
8
 
9
+ 'QUALIFICATION' => ['Qualifying'],
9
10
  'PRELIMINARY_ROUND' => ['Qualifying', 'Preliminary Round' ],
10
11
  'PRELIMINARY_SEMI_FINALS' => ['Qualifying', 'Preliminary Semifinals' ],
11
12
  'PRELIMINARY_FINAL' => ['Qualifying', 'Preliminary Final' ],
12
- '1ST_QUALIFYING_ROUND' => ['Qualifying', 'Qual. Round 1' ],
13
- '2ND_QUALIFYING_ROUND' => ['Qualifying', 'Qual. Round 2' ],
14
- '3RD_QUALIFYING_ROUND' => ['Qualifying', 'Qual. Round 3' ],
15
- 'QUALIFICATION_ROUND_1' => ['Qualifying', 'Qual. Round 1' ],
16
- 'QUALIFICATION_ROUND_2' => ['Qualifying', 'Qual. Round 2' ],
17
- 'QUALIFICATION_ROUND_3' => ['Qualifying', 'Qual. Round 3' ],
13
+ '1ST_QUALIFYING_ROUND' => ['Qualifying', 'Round 1' ],
14
+ '2ND_QUALIFYING_ROUND' => ['Qualifying', 'Round 2' ],
15
+ '3RD_QUALIFYING_ROUND' => ['Qualifying', 'Round 3' ],
16
+ 'QUALIFICATION_ROUND_1' => ['Qualifying', 'Round 1' ],
17
+ 'QUALIFICATION_ROUND_2' => ['Qualifying', 'Round 2' ],
18
+ 'QUALIFICATION_ROUND_3' => ['Qualifying', 'Round 3' ],
18
19
  'ROUND_1' => ['Qualifying', 'Round 1'], ## use Qual. Round 1 - why? why not?
19
20
  'ROUND_2' => ['Qualifying', 'Round 2'],
20
21
  'ROUND_3' => ['Qualifying', 'Round 3'],
@@ -29,6 +30,7 @@ STAGES = {
29
30
  'LAST_16' => ['Finals', 'Round of 16'], ## use Last 16 - why? why not?
30
31
  'QUARTER_FINALS' => ['Finals', 'Quarterfinals'],
31
32
  'SEMI_FINALS' => ['Finals', 'Semifinals'],
33
+ 'THIRD_PLACE' => ['Finals', 'Third place play-off'],
32
34
  'FINAL' => ['Finals', 'Final'],
33
35
  }
34
36
 
@@ -95,12 +97,40 @@ matches.each do |m|
95
97
  score = m['score']
96
98
 
97
99
 
98
- stage_key = m['stage']
99
100
 
101
+ group = m['group']
102
+ ## GROUP_A
103
+ ## shorten group to A|B|C etc.
104
+ if group && group =~ /^(GROUP_|Group )/
105
+ group = group.sub( /^(GROUP_|Group )/, '' )
106
+ else
107
+ if group.nil?
108
+ group = ''
109
+ else
110
+ puts "!! WARN - group defined with NON GROUP!? >#{group}< reset to empty"
111
+ puts " and matchday to >#{m['matchday']}<"
112
+ ## reset group to empty
113
+ group = ''
114
+ end
115
+ end
116
+
117
+
118
+ stage_key = m['stage']
100
119
  stats['stage'][ stage_key ] += 1 ## track stage counts
101
120
 
102
- ## map stage to stage + round
103
- stage, stage_round = STAGES[ stage_key ]
121
+
122
+ stage, stage_round = if group.empty?
123
+ ## map stage to stage + round
124
+ STAGES[ stage_key ]
125
+ else
126
+ ## if group defined ignore stage
127
+ ## hard-core always to group for now
128
+ if stage_key != 'GROUP_STAGE'
129
+ puts "!! WARN - group defined BUT stage set to >#{stage_key}<"
130
+ puts " and matchday to >#{m['matchday']}<"
131
+ end
132
+ ['Group', nil]
133
+ end
104
134
 
105
135
  if stage.nil?
106
136
  puts "!! ERROR - no stage mapping found for stage >#{stage_key}<"
@@ -115,23 +145,12 @@ matches.each do |m|
115
145
  ## matchday 1, 2 etc.
116
146
  matchday = matchday_num.to_s
117
147
  else
118
- ## note - if matchday defined; assume leg e.g. 1|2
119
- ## skip if different than one or two for now
120
- matchday = String.new
121
- matchday << stage_round
122
- matchday << " | Leg #{matchday_num}" if matchday_num &&
123
- (matchday_num == 1 || matchday_num == 2)
148
+ ## note - if matchday/round defined, use it
149
+ ## note - ignore possible leg in matchday for now
150
+ matchday = stage_round
124
151
  end
125
152
 
126
153
 
127
-
128
- group = m['group'] || ''
129
- ## GROUP_A
130
- ## shorten group to A|B|C etc.
131
- group = group.sub( /^GROUP_/, '' )
132
-
133
-
134
-
135
154
  teams[ team1 ] += 1
136
155
  teams[ team2 ] += 1
137
156
 
@@ -164,7 +183,7 @@ matches.each do |m|
164
183
  ht = ''
165
184
  when 'FINISHED'
166
185
  ft, ht, et, pen = convert_score( score )
167
- when 'AWARDED'
186
+ when 'AWARDED' # AWARDED
168
187
  assert( score['duration'] == 'REGULAR', 'score.duration REGULAR expected' )
169
188
  ft = "#{score['fullTime']['home']}-#{score['fullTime']['away']}"
170
189
  ft << ' (*)'
@@ -206,7 +225,17 @@ matches.each do |m|
206
225
  local = tz.to_local( utc )
207
226
  date = local.strftime( '%Y-%m-%d' )
208
227
  time = local.strftime( '%H:%M' )
209
- timezone = local.strftime( '%Z/%z' )
228
+
229
+ ## pretty print timezone
230
+ ### todo/fix - bundle into fmt_timezone method or such for reuse
231
+ tz_abbr = local.strftime( '%Z' ) ## e.g. EEST or if not available +03 or such
232
+ tz_offset = local.strftime( '%z' ) ## e.g. +0300
233
+
234
+ timezone = if tz_abbr =~ /^[+-][0-9]+$/ ## only digits (no abbrev.)
235
+ tz_offset
236
+ else
237
+ "#{tz_abbr}/#{tz_offset}"
238
+ end
210
239
  end
211
240
 
212
241
 
@@ -2,7 +2,7 @@ module Footballdata
2
2
 
3
3
  def self.find_league!( league )
4
4
  @leagues ||= begin
5
- recs = read_csv( "#{FootballdataApi.root}/config/leagues.csv" )
5
+ recs = read_csv( "#{FootballdataApi.root}/config/leagues_tier1.csv" )
6
6
  leagues = {}
7
7
  recs.each do |rec|
8
8
  leagues[ rec['key'] ] = rec['code']
@@ -39,6 +39,7 @@ def self.fmt_match( rec )
39
39
  TIMED
40
40
  FINISHED
41
41
  POSTPONED
42
+ AWARDED
42
43
  IN_PLAY
43
44
  ].include?( status ), "unknown status - #{status}" )
44
45
 
@@ -89,7 +90,11 @@ def self.fmt_match( rec )
89
90
  score << "#{et} a.e.t. "
90
91
  score << "(#{ft}, #{ht})"
91
92
  else
92
- score << "#{ft} (#{ht})"
93
+ if !ht.empty?
94
+ score << "#{ft} (#{ht})"
95
+ else
96
+ score << "#{ft}"
97
+ end
93
98
  end
94
99
 
95
100
  buf << score
@@ -2,7 +2,7 @@
2
2
  module FootballdataApi
3
3
  MAJOR = 0 ## todo: namespace inside version or something - why? why not??
4
4
  MINOR = 4
5
- PATCH = 1
5
+ PATCH = 2
6
6
  VERSION = [MAJOR,MINOR,PATCH].join('.')
7
7
 
8
8
  def self.version
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: footballdata-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.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-09-11 00:00:00.000000000 Z
11
+ date: 2024-09-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: football-timezones
@@ -88,7 +88,7 @@ files:
88
88
  - README.md
89
89
  - Rakefile
90
90
  - bin/fbdat
91
- - config/leagues.csv
91
+ - config/leagues_tier1.csv
92
92
  - lib/footballdata.rb
93
93
  - lib/footballdata/convert-score.rb
94
94
  - lib/footballdata/convert.rb
data/config/leagues.csv DELETED
@@ -1,54 +0,0 @@
1
- key, code
2
-
3
- eng.1, PL # incl. team(s) from wales
4
- eng.2, ELC
5
- # PL - Premier League, England 27 seasons | 2019-08-09 - 2020-07-25 / matchday 31
6
- # ELC - Championship, England 3 seasons | 2019-08-02 - 2020-07-22 / matchday 38
7
- #
8
- # 2019 => 2019/20
9
- # 2018 => 2018/19
10
- # 2017 => xxx 2017-18 - requires subscription !!!
11
-
12
- es.1, PD
13
- # PD - Primera Division, Spain 27 seasons | 2019-08-16 - 2020-07-19 / matchday 31
14
-
15
- pt.1, PPL
16
- # PPL - Primeira Liga, Portugal 9 seasons | 2019-08-10 - 2020-07-26 / matchday 28
17
-
18
- de.1, BL1
19
- # BL1 - Bundesliga, Germany 24 seasons | 2019-08-16 - 2020-06-27 / matchday 34
20
-
21
- nl.1, DED
22
- # DED - Eredivisie, Netherlands 10 seasons | 2019-08-09 - 2020-03-08 / matchday 34
23
-
24
- fr.1, FL1 # incl. team(s) monaco
25
- # FL1 - Ligue 1, France
26
- # 9 seasons | 2019-08-09 - 2020-05-31 / matchday 38
27
- #
28
- # 2019 => 2019/20
29
- # 2018 => 2018/19
30
- # 2017 => xxx 2017-18 - requires subscription !!!
31
-
32
- it.1, SA
33
- # SA - Serie A, Italy 15 seasons | 2019-08-24 - 2020-08-02 / matchday 27
34
-
35
- br.1, BSA
36
- # BSA - Série A, Brazil
37
- # 4 seasons | 2020-05-03 - 2020-12-06 / matchday 10
38
- #
39
- # 2020 => 2020
40
- # 2019 => 2019
41
- # 2018 => 2018
42
- # 2017 => xxx 2017 - requires subscription !!!
43
-
44
- uefa.cl, CL ## note: cl is country code for chile!! - use champs - why? why not?
45
- ## was europe.cl / cl
46
- ## todo/check: use champs and NOT cl - why? why not?
47
-
48
- copa.l, CLI
49
- ## Copa Libertadores
50
-
51
- ############
52
- ## national teams
53
- euro, EC
54
- world, WC