footballdata-api 2026.7.2 → 2026.7.12
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 +4 -4
- data/CHANGELOG.md +1 -1
- data/Manifest.txt +4 -1
- data/README.md +2 -2
- data/bin/fbdat +19 -2
- data/config/leagues_tier1.csv +12 -12
- data/lib/footballdata/convert-score.rb +59 -0
- data/lib/footballdata/convert-stages.rb +20 -0
- data/lib/footballdata/convert-teams.rb +155 -0
- data/lib/footballdata/{convert.rb → convert_csv.rb} +123 -178
- data/lib/footballdata/convert_json.rb +160 -0
- data/lib/footballdata/download.rb +5 -2
- data/lib/footballdata/leagues.rb +7 -1
- data/lib/footballdata/version.rb +1 -1
- data/lib/footballdata.rb +6 -1
- metadata +6 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 301b4ba1575a7275090ed592dce4a7da171d739d3fbd7599a4b19794d589df0d
|
|
4
|
+
data.tar.gz: 467180eeb2bdd379d40fe46021748ddfca3ed72dab132aacdf397c762b155b5e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0c103f1473a73a21a494b86e435182d6bfe22744b098b7f2a661b7ac2f66e4b6a81965f5d18ec0b726b950746496ce8ed44b96266a66256a625784d2f5b0ba86
|
|
7
|
+
data.tar.gz: 4d538238d329037c41ffbbe1cdfdb1f0c21309c50250c1e403855ed7599b02c459a5430d305b82e7929245c0c101d5781c3df41354b20fe233d2b496297a70f8
|
data/CHANGELOG.md
CHANGED
data/Manifest.txt
CHANGED
|
@@ -6,7 +6,10 @@ bin/fbdat
|
|
|
6
6
|
config/leagues_tier1.csv
|
|
7
7
|
lib/footballdata.rb
|
|
8
8
|
lib/footballdata/convert-score.rb
|
|
9
|
-
lib/footballdata/convert.rb
|
|
9
|
+
lib/footballdata/convert-stages.rb
|
|
10
|
+
lib/footballdata/convert-teams.rb
|
|
11
|
+
lib/footballdata/convert_csv.rb
|
|
12
|
+
lib/footballdata/convert_json.rb
|
|
10
13
|
lib/footballdata/download.rb
|
|
11
14
|
lib/footballdata/leagues.rb
|
|
12
15
|
lib/footballdata/mods.rb
|
data/README.md
CHANGED
|
@@ -84,10 +84,10 @@ require 'footballdata'
|
|
|
84
84
|
'pt.1',
|
|
85
85
|
'uefa.cl',
|
|
86
86
|
].each do |league|
|
|
87
|
-
Footballdata.
|
|
87
|
+
Footballdata.convert_csv( league: league, season: '2020/21' )
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
Footballdata.
|
|
90
|
+
Footballdata.convert_csv( league: 'br.1', season: '2020' )
|
|
91
91
|
```
|
|
92
92
|
|
|
93
93
|
Note: By default all datasets get written into the `./o`
|
data/bin/fbdat
CHANGED
|
@@ -36,8 +36,10 @@ opts = {
|
|
|
36
36
|
cached: false,
|
|
37
37
|
convert: true,
|
|
38
38
|
file: nil,
|
|
39
|
+
json: nil,
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
|
|
41
43
|
parser = OptionParser.new do |parser|
|
|
42
44
|
parser.banner = "Usage: #{$PROGRAM_NAME} [options] [args]"
|
|
43
45
|
|
|
@@ -51,6 +53,11 @@ parser = OptionParser.new do |parser|
|
|
|
51
53
|
opts[:convert] = convert
|
|
52
54
|
end
|
|
53
55
|
|
|
56
|
+
parser.on( "--json",
|
|
57
|
+
"turn on conversion to .json in #{Footballdata.config.convert.out_dir}" ) do |json|
|
|
58
|
+
opts[:json] = json
|
|
59
|
+
end
|
|
60
|
+
|
|
54
61
|
parser.on( "--print", "--pp",
|
|
55
62
|
"pretty print cached data in #{Webcache.root}; no download & conversion") do |print|
|
|
56
63
|
opts[:cached] = true
|
|
@@ -200,6 +207,11 @@ autofiller = ->(league_query) {
|
|
|
200
207
|
datasets = if opts[:file]
|
|
201
208
|
read_leagueset( opts[:file], autofill: autofiller )
|
|
202
209
|
else
|
|
210
|
+
|
|
211
|
+
### todo or fix/fix/fix - change to build_leagueset
|
|
212
|
+
## and reserve parse_leagueset( txt ) for
|
|
213
|
+
## read_leagueset with inline text
|
|
214
|
+
|
|
203
215
|
parse_leagueset_args( args, autofill: autofiller )
|
|
204
216
|
end
|
|
205
217
|
|
|
@@ -243,7 +255,13 @@ if opts[:convert]
|
|
|
243
255
|
puts "==> converting to .csv"
|
|
244
256
|
datasets.each do |league_key, seasons|
|
|
245
257
|
seasons.each do |season|
|
|
246
|
-
|
|
258
|
+
### quick hack - for json only conversion
|
|
259
|
+
if opts[:json]
|
|
260
|
+
convert_json( league: league_key, season: season )
|
|
261
|
+
else
|
|
262
|
+
convert_csv( league: league_key, season: season )
|
|
263
|
+
convert_json( league: league_key, season: season )
|
|
264
|
+
end
|
|
247
265
|
end
|
|
248
266
|
end
|
|
249
267
|
end
|
|
@@ -257,4 +275,3 @@ Footballdata.main( ARGV )
|
|
|
257
275
|
|
|
258
276
|
|
|
259
277
|
puts "bye"
|
|
260
|
-
|
data/config/leagues_tier1.csv
CHANGED
|
@@ -1,44 +1,44 @@
|
|
|
1
1
|
key, code, seasons
|
|
2
2
|
|
|
3
|
-
eng.1, PL, 2024/25 2023/24 2022/23 2021/22 2020/21
|
|
3
|
+
eng.1, PL, 2026/27 2025/26 2024/25 2023/24 2022/23 2021/22 2020/21
|
|
4
4
|
# incl. team(s) from wales
|
|
5
|
-
eng.2, ELC, 2024/25 2023/24 2022/23 2021/22 2020/21
|
|
5
|
+
eng.2, ELC, 2026/27 2025/26 2024/25 2023/24 2022/23 2021/22 2020/21
|
|
6
6
|
# PL - Premier League, England 27 seasons | 2019-08-09 - 2020-07-25 / matchday 31
|
|
7
7
|
# ELC - Championship, England 3 seasons | 2019-08-02 - 2020-07-22 / matchday 38
|
|
8
8
|
|
|
9
|
-
es.1, PD, 2024/25 2023/24 2022/23 2021/22 2020/21
|
|
9
|
+
es.1, PD, 2026/27 2025/26 2024/25 2023/24 2022/23 2021/22 2020/21
|
|
10
10
|
# PD - Primera Division, Spain 27 seasons | 2019-08-16 - 2020-07-19 / matchday 31
|
|
11
11
|
|
|
12
|
-
pt.1, PPL, 2024/25 2023/24 2022/23 2021/22 2020/21
|
|
12
|
+
pt.1, PPL, 2026/27 2025/26 2024/25 2023/24 2022/23 2021/22 2020/21
|
|
13
13
|
# PPL - Primeira Liga, Portugal 9 seasons | 2019-08-10 - 2020-07-26 / matchday 28
|
|
14
14
|
|
|
15
|
-
de.1, BL1, 2024/25 2023/24 2022/23 2021/22 2020/21
|
|
15
|
+
de.1, BL1, 2026/27 2025/26 2024/25 2023/24 2022/23 2021/22 2020/21
|
|
16
16
|
# BL1 - Bundesliga, Germany 24 seasons | 2019-08-16 - 2020-06-27 / matchday 34
|
|
17
17
|
|
|
18
|
-
nl.1, DED, 2024/25 2023/24 2022/23 2021/22 2020/21
|
|
18
|
+
nl.1, DED, 2026/27 2025/26 2024/25 2023/24 2022/23 2021/22 2020/21
|
|
19
19
|
# DED - Eredivisie, Netherlands 10 seasons | 2019-08-09 - 2020-03-08 / matchday 34
|
|
20
20
|
|
|
21
|
-
fr.1, FL1, 2024/25 2023/24 2022/23 2021/22 2020/21
|
|
21
|
+
fr.1, FL1, 2026/27 2025/26 2024/25 2023/24 2022/23 2021/22 2020/21
|
|
22
22
|
# incl. team(s) monaco
|
|
23
23
|
|
|
24
|
-
it.1, SA, 2024/25 2023/24 2022/23 2021/22 2020/21
|
|
24
|
+
it.1, SA, 2026/27 2025/26 2024/25 2023/24 2022/23 2021/22 2020/21
|
|
25
25
|
# SA - Serie A, Italy 15 seasons | 2019-08-24 - 2020-08-02 / matchday 27
|
|
26
26
|
|
|
27
|
-
br.1, BSA, 2025 2024 2023 2022 2021 2020
|
|
27
|
+
br.1, BSA, 2026 2025 2024 2023 2022 2021 2020
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
########
|
|
31
31
|
## int'l cups
|
|
32
|
-
uefa.cl, CL, 2024/25 2023/24 2022/23 2021/22 2020/21
|
|
32
|
+
uefa.cl, CL, 2025/26 2024/25 2023/24 2022/23 2021/22 2020/21
|
|
33
33
|
## note: cl is country code for chile!! - use champs - why? why not?
|
|
34
34
|
## was europe.cl / cl
|
|
35
35
|
## todo/check: use champs and NOT cl - why? why not?
|
|
36
36
|
|
|
37
|
-
copa.l, CLI, 2025 2024 2023 2022 2021
|
|
37
|
+
copa.l, CLI, 2026 2025 2024 2023 2022 2021
|
|
38
38
|
## Copa Libertadores
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
############
|
|
42
42
|
## national teams
|
|
43
43
|
euro, EC, 2024 2021
|
|
44
|
-
world, WC, 2022
|
|
44
|
+
world, WC, 2026 2022
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
|
|
2
2
|
module Footballdata
|
|
3
3
|
|
|
4
|
+
##
|
|
5
|
+
## note - "weirdo" rule
|
|
6
|
+
## fullTime is only "classic" fulltime if REGULAR
|
|
7
|
+
##
|
|
8
|
+
## otherwise fullTime is aggregate of extraTime AND penalties!!
|
|
9
|
+
##
|
|
10
|
+
##
|
|
11
|
+
|
|
12
|
+
|
|
4
13
|
def self.convert_score( score )
|
|
5
14
|
## duration: REGULAR · PENALTY_SHOOTOUT · EXTRA_TIME
|
|
6
15
|
ft, ht, et, pen = ["","","",""]
|
|
@@ -41,4 +50,54 @@ def self.convert_score( score )
|
|
|
41
50
|
|
|
42
51
|
[ft,ht,et,pen]
|
|
43
52
|
end
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def self.convert_score_to_hash( score )
|
|
57
|
+
## duration: REGULAR · PENALTY_SHOOTOUT · EXTRA_TIME
|
|
58
|
+
|
|
59
|
+
if score['duration'] == 'REGULAR'
|
|
60
|
+
{ ft: [score['fullTime']['home'],
|
|
61
|
+
score['fullTime']['away']],
|
|
62
|
+
ht: [score['halfTime']['home'],
|
|
63
|
+
score['halfTime']['away']]
|
|
64
|
+
}
|
|
65
|
+
elsif score['duration'] == 'EXTRA_TIME'
|
|
66
|
+
{ et: [score['regularTime']['home']+score['extraTime']['home'],
|
|
67
|
+
score['regularTime']['away']+score['extraTime']['away']],
|
|
68
|
+
ft: [score['regularTime']['home'],
|
|
69
|
+
score['regularTime']['away']],
|
|
70
|
+
ht: [score['halfTime']['home'],
|
|
71
|
+
score['halfTime']['away']]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
elsif score['duration'] == 'PENALTY_SHOOTOUT'
|
|
75
|
+
if score['extraTime']
|
|
76
|
+
## quick & dirty hack - calc et via regulartime+extratime
|
|
77
|
+
{ pen: [score['penalties']['home'],
|
|
78
|
+
score['penalties']['away']],
|
|
79
|
+
et: [score['regularTime']['home']+score['extraTime']['home'],
|
|
80
|
+
score['regularTime']['away']+score['extraTime']['away']],
|
|
81
|
+
ft: [score['regularTime']['home'],
|
|
82
|
+
score['regularTime']['away']],
|
|
83
|
+
ht: [score['halfTime']['home'],
|
|
84
|
+
score['halfTime']['away']]
|
|
85
|
+
}
|
|
86
|
+
else ### south american-style (no extra time)
|
|
87
|
+
## quick & dirty hacke - calc ft via fullTime-penalties
|
|
88
|
+
{ pen: [score['penalties']['home'],
|
|
89
|
+
score['penalties']['away']],
|
|
90
|
+
ft: [score['fullTime']['home']-score['penalties']['home'],
|
|
91
|
+
score['fullTime']['away']-score['penalties']['away']],
|
|
92
|
+
ht: [score['halfTime']['home'],
|
|
93
|
+
score['halfTime']['away']]
|
|
94
|
+
}
|
|
95
|
+
end
|
|
96
|
+
else
|
|
97
|
+
raise ArgumentError, "!! unknown score duration - #{score.inspect}"
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
44
103
|
end # module Footballdata
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
|
|
2
|
+
class Stages
|
|
3
|
+
def initialize
|
|
4
|
+
@recs = {}
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def add_matches( matches )
|
|
8
|
+
matches.each do |m|
|
|
9
|
+
name = m['stage']
|
|
10
|
+
rec = @recs[name] ||= { name: name,
|
|
11
|
+
count: 0 }
|
|
12
|
+
rec[:count] +=1
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def as_json() @recs.values; end
|
|
17
|
+
|
|
18
|
+
def size() @recs.size; end
|
|
19
|
+
|
|
20
|
+
end # class Stages
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
|
|
2
|
+
=begin
|
|
3
|
+
|
|
4
|
+
note - for address "null null null",
|
|
5
|
+
is street + city + postal_code/
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
{"area"=>{"id"=>2061, "name"=>"Cyprus", "code"=>"CYP", "flag"=>nil},
|
|
9
|
+
"id"=>11034,
|
|
10
|
+
"name"=>"Paphos FC",
|
|
11
|
+
"shortName"=>"Paphos FC",
|
|
12
|
+
"tla"=>"AEP",
|
|
13
|
+
"address"=>"null null null",
|
|
14
|
+
"website"=>nil,
|
|
15
|
+
"founded"=>nil,
|
|
16
|
+
"clubColors"=>nil,
|
|
17
|
+
"venue"=>"Alphamega Stadium",
|
|
18
|
+
|
|
19
|
+
{"area"=>{"id"=>2119, "name"=>"Kazakhstan", "code"=>"KAZ", "flag"=>nil},
|
|
20
|
+
"id"=>10601,
|
|
21
|
+
"name"=>"FK Kairat",
|
|
22
|
+
"shortName"=>"FK Kairat",
|
|
23
|
+
"tla"=>"KAI",
|
|
24
|
+
"address"=>"null null null",
|
|
25
|
+
"website"=>nil,
|
|
26
|
+
"founded"=>nil,
|
|
27
|
+
"clubColors"=>nil,
|
|
28
|
+
"venue"=>nil,
|
|
29
|
+
|
|
30
|
+
{"area"=>{"id"=>2060, "name"=>"Curaçao", "code"=>"CUW", "flag"=>"https://crests.football-data.org/curacao.svg"},
|
|
31
|
+
"id"=>9460,
|
|
32
|
+
"name"=>"Curaçao",
|
|
33
|
+
"shortName"=>"Curaçao",
|
|
34
|
+
"tla"=>"CUW",
|
|
35
|
+
"address"=>"null null null",
|
|
36
|
+
"website"=>nil,
|
|
37
|
+
"founded"=>nil,
|
|
38
|
+
"clubColors"=>nil,
|
|
39
|
+
"venue"=>nil,
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
=end
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def build_team( h )
|
|
47
|
+
|
|
48
|
+
##
|
|
49
|
+
## note: convert country code to "standard" fifa code
|
|
50
|
+
## use official fifa country code
|
|
51
|
+
country_name = h['area']['name']
|
|
52
|
+
|
|
53
|
+
country = Fifa.world.find_by_name( country_name )
|
|
54
|
+
if country.nil?
|
|
55
|
+
raise ArgumentError, "[fifa world] no country record found for #{country_name} for: #{h.inspect}"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
country_code = country.code
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
rec = { id: h['id'],
|
|
62
|
+
name: h['name'],
|
|
63
|
+
short_name: h['shortName'],
|
|
64
|
+
code: h['tla'],
|
|
65
|
+
address: h['address'], ### street / city / postal_code
|
|
66
|
+
founded: h['founded'],
|
|
67
|
+
ground: h['venue'], ### use stadium or venue ??
|
|
68
|
+
|
|
69
|
+
##
|
|
70
|
+
## or use country { code:, name }
|
|
71
|
+
## change to cc (country code) - why? why not?
|
|
72
|
+
country: { name: country_name,
|
|
73
|
+
code: country_code,
|
|
74
|
+
code_bak: h['area']['code'] },
|
|
75
|
+
|
|
76
|
+
count: 0, ## track - match counts
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
rec
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class Teams
|
|
84
|
+
|
|
85
|
+
def initialize
|
|
86
|
+
@recs = []
|
|
87
|
+
@by_name = {}
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def add( recs )
|
|
92
|
+
recs.each do |h|
|
|
93
|
+
rec = build_team( h )
|
|
94
|
+
@recs << rec
|
|
95
|
+
|
|
96
|
+
## note - assert/make sure team name is uniq
|
|
97
|
+
if @by_name.has_key?(rec[:name])
|
|
98
|
+
raise ArgumentError, "duplicate team name - #{h.inspect}"
|
|
99
|
+
else
|
|
100
|
+
@by_name[rec[:name]] = rec
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
if rec[:name] != rec[:short_name]
|
|
104
|
+
if @by_name.has_key?(rec[:short_name])
|
|
105
|
+
raise ArgumentError, "duplicate team (short) name - #{h.inspect}"
|
|
106
|
+
else
|
|
107
|
+
@by_name[rec[:short_name]] = rec
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def find_by!( name: )
|
|
116
|
+
rec = @by_name[ name ]
|
|
117
|
+
if rec.nil?
|
|
118
|
+
raise ArgumentError, "team >#{name}< not found; sorry"
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
rec
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def add_matches( matches ) ## use/rename to add_matches - why? why not?
|
|
126
|
+
matches.each do |m|
|
|
127
|
+
|
|
128
|
+
## note - skip teams without name (e.g. N.N.)
|
|
129
|
+
if m['homeTeam']['name']
|
|
130
|
+
rec = find_by!( name: m['homeTeam']['name'] )
|
|
131
|
+
rec[:count] += 1
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
if m['awayTeam']['name']
|
|
135
|
+
rec = find_by!( name: m['awayTeam']['name'] )
|
|
136
|
+
rec[:count] += 1
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def each( &blk) @recs.each( &blk); end
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def as_json( id: false )
|
|
146
|
+
if id
|
|
147
|
+
@recs
|
|
148
|
+
else
|
|
149
|
+
@recs.map { |rec| rec.except(:id ) }
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def size() @recs.size; end
|
|
154
|
+
|
|
155
|
+
end # class Teams
|
|
@@ -1,73 +1,50 @@
|
|
|
1
1
|
|
|
2
2
|
module Footballdata
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
##
|
|
7
|
-
## or * (all)
|
|
4
|
+
|
|
5
|
+
####
|
|
6
|
+
## beautify stages constant names
|
|
8
7
|
STAGES = {
|
|
9
|
-
'REGULAR_SEASON' =>
|
|
10
|
-
|
|
11
|
-
'QUALIFICATION' =>
|
|
12
|
-
'PRELIMINARY_ROUND' =>
|
|
13
|
-
'PRELIMINARY_SEMI_FINALS' =>
|
|
14
|
-
'PRELIMINARY_FINAL' =>
|
|
15
|
-
'1ST_QUALIFYING_ROUND' =>
|
|
16
|
-
'2ND_QUALIFYING_ROUND' =>
|
|
17
|
-
'3RD_QUALIFYING_ROUND' =>
|
|
18
|
-
'QUALIFICATION_ROUND_1' =>
|
|
19
|
-
'QUALIFICATION_ROUND_2' =>
|
|
20
|
-
'QUALIFICATION_ROUND_3' =>
|
|
21
|
-
'ROUND_1' =>
|
|
22
|
-
'ROUND_2' =>
|
|
23
|
-
'ROUND_3' =>
|
|
24
|
-
'PLAY_OFF_ROUND' =>
|
|
25
|
-
'PLAYOFF_ROUND_1' =>
|
|
26
|
-
|
|
27
|
-
'LEAGUE_STAGE' =>
|
|
28
|
-
'GROUP_STAGE' =>
|
|
29
|
-
'PLAYOFFS' =>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
'
|
|
33
|
-
'
|
|
34
|
-
'
|
|
35
|
-
'
|
|
36
|
-
'
|
|
8
|
+
'REGULAR_SEASON' => 'Regular Season',
|
|
9
|
+
|
|
10
|
+
'QUALIFICATION' => 'Qualification',
|
|
11
|
+
'PRELIMINARY_ROUND' => 'Preliminary Round',
|
|
12
|
+
'PRELIMINARY_SEMI_FINALS' => 'Preliminary Semifinals',
|
|
13
|
+
'PRELIMINARY_FINAL' => 'Preliminary Final',
|
|
14
|
+
'1ST_QUALIFYING_ROUND' => '1st Qualifying Round',
|
|
15
|
+
'2ND_QUALIFYING_ROUND' => '2nd Qualifying Round',
|
|
16
|
+
'3RD_QUALIFYING_ROUND' => '3rd Qualifying Round',
|
|
17
|
+
'QUALIFICATION_ROUND_1' => 'Qualification Round 1',
|
|
18
|
+
'QUALIFICATION_ROUND_2' => 'Qualification Round 2',
|
|
19
|
+
'QUALIFICATION_ROUND_3' => 'Qualification Round 3',
|
|
20
|
+
'ROUND_1' => 'Round 1',
|
|
21
|
+
'ROUND_2' => 'Round 2',
|
|
22
|
+
'ROUND_3' => 'Round 3',
|
|
23
|
+
'PLAY_OFF_ROUND' => 'Playoff Round',
|
|
24
|
+
'PLAYOFF_ROUND_1' => 'Playoff Round 1',
|
|
25
|
+
|
|
26
|
+
'LEAGUE_STAGE' => 'League', ## add Stage - why? why not?
|
|
27
|
+
'GROUP_STAGE' => 'Group',
|
|
28
|
+
'PLAYOFFS' => 'Playoffs', ## -- used in champs
|
|
29
|
+
'PLAY_OFFS' => 'Playoffs', ## -- used in copa liber.
|
|
30
|
+
|
|
31
|
+
'LAST_32' => 'Round of 32',
|
|
32
|
+
'ROUND_OF_16' => 'Round of 16',
|
|
33
|
+
'LAST_16' => 'Round of 16', ## use Last 16 - why? why not?
|
|
34
|
+
'QUARTER_FINALS' => 'Quarterfinals',
|
|
35
|
+
'SEMI_FINALS' => 'Semifinals',
|
|
36
|
+
'THIRD_PLACE' => 'Third place',
|
|
37
|
+
'FINAL' => 'Final',
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def self.find_team_country_code( name, teams: )
|
|
44
|
-
## add (fifa) country code e.g.
|
|
45
|
-
## Liverpool FC => Liverpool FC (ENG)
|
|
46
|
-
## or Liverpool FC => Liverpool FC (URU)
|
|
47
|
-
rec = teams[ name ]
|
|
48
|
-
if rec.nil?
|
|
49
|
-
puts "!! ERROR - no team record found in teams.json for #{name}"
|
|
50
|
-
pp teams.keys
|
|
51
|
-
exit 1
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
country_name = rec['area']['name']
|
|
55
|
-
country = Fifa.world.find_by_name( country_name )
|
|
56
|
-
if country.nil?
|
|
57
|
-
puts "!! ERROR - no country record found for #{country_name}"
|
|
58
|
-
exit 1
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
country.code
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def self.convert( league:, season: )
|
|
42
|
+
def self.convert_csv( league:, season: )
|
|
67
43
|
|
|
68
44
|
season = Season( season ) ## cast (ensure) season class (NOT string, integer, etc.)
|
|
69
45
|
|
|
70
|
-
### note - find_league returns the metal_league_code
|
|
46
|
+
### note - find_league returns the metal_league_code
|
|
47
|
+
## e.g. eng.1 => PL
|
|
71
48
|
league_code = find_league!( league )
|
|
72
49
|
|
|
73
50
|
matches_url = Metal.competition_matches_url( league_code, season.start_year )
|
|
@@ -77,6 +54,17 @@ def self.convert( league:, season: )
|
|
|
77
54
|
data_teams = Webcache.read_json( teams_url )
|
|
78
55
|
|
|
79
56
|
|
|
57
|
+
## build a team lookup by name (& short_name)
|
|
58
|
+
|
|
59
|
+
teams = Teams.new
|
|
60
|
+
teams.add( data_teams['teams'] )
|
|
61
|
+
puts "#{teams.size} team(s)"
|
|
62
|
+
|
|
63
|
+
## add/update match counts
|
|
64
|
+
teams.add_matches( data['matches'] )
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
80
68
|
###
|
|
81
69
|
## todo/fix - use find_by! - add upstream!!!
|
|
82
70
|
league_info = LeagueCodes.find_by( code: league, season: season )
|
|
@@ -93,135 +81,102 @@ def self.convert( league:, season: )
|
|
|
93
81
|
'copa.l'].include?(league.downcase) ? true : false
|
|
94
82
|
|
|
95
83
|
|
|
84
|
+
mods = MODS[ league.downcase ] || {}
|
|
96
85
|
|
|
97
|
-
## build a (reverse) team lookup by name
|
|
98
|
-
puts "#{data_teams['teams'].size} teams"
|
|
99
86
|
|
|
100
|
-
teams_by_name = data_teams['teams'].reduce( {} ) do |h,rec|
|
|
101
|
-
h[ rec['name'] ] = rec
|
|
102
|
-
h
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
## pp teams_by_name.keys
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
mods = MODS[ league.downcase ] || {}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
recs = []
|
|
113
|
-
|
|
114
|
-
teams = Hash.new( 0 )
|
|
115
87
|
|
|
88
|
+
recs = []
|
|
116
89
|
|
|
117
|
-
# stat = Stat.new
|
|
118
|
-
|
|
90
|
+
# stat = Stat.new
|
|
119
91
|
## track stage, match status et
|
|
120
92
|
stats = { 'status' => Hash.new(0),
|
|
121
93
|
'stage' => Hash.new(0),
|
|
122
94
|
}
|
|
123
95
|
|
|
124
96
|
|
|
125
|
-
|
|
126
|
-
|
|
127
97
|
matches = data[ 'matches']
|
|
128
98
|
matches.each do |m|
|
|
129
99
|
# stat.update( m )
|
|
130
100
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
101
|
+
# use ? or N.N. or ? for nil - why? why not?
|
|
102
|
+
team1_name = m['homeTeam']['name']
|
|
103
|
+
team2_name = m['awayTeam']['name']
|
|
134
104
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if m['status'] == 'IN_PLAY' &&
|
|
138
|
-
team1 == 'Club Aurora' && team2 == 'FBC Melgar'
|
|
139
|
-
m['status'] = 'FINISHED'
|
|
140
|
-
end
|
|
105
|
+
team1 = team1_name ? teams.find_by!(name: team1_name ) : { name: 'N.N.' }
|
|
106
|
+
team2 = team2_name ? teams.find_by!(name: team2_name ) : { name: 'N.N.' }
|
|
141
107
|
|
|
142
108
|
|
|
109
|
+
## use "normed" team names
|
|
110
|
+
team1_name = team1[:name]
|
|
111
|
+
team2_name = team2[:name]
|
|
143
112
|
|
|
144
|
-
|
|
113
|
+
### mods - rename club names
|
|
114
|
+
unless mods.nil? || mods.empty?
|
|
115
|
+
team1_name = mods[ team1_name ] if mods[ team1_name ]
|
|
116
|
+
team2_name = mods[ team2_name ] if mods[ team2_name ]
|
|
117
|
+
end
|
|
145
118
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
puts "!! WARN - group defined with NON GROUP!? >#{group}< reset to empty"
|
|
156
|
-
puts " and matchday to >#{m['matchday']}<"
|
|
157
|
-
## reset group to empty
|
|
158
|
-
group = ''
|
|
159
|
-
end
|
|
119
|
+
####
|
|
120
|
+
# auto-add (fifa) country code if int'l club tournament
|
|
121
|
+
if clubs_intl
|
|
122
|
+
if team1_name != 'N.N.' ## note - ignore no team/placeholder (e.g. N.N)
|
|
123
|
+
team1_name = "#{team1_name} (#{team1[:country][:code]})"
|
|
124
|
+
end
|
|
125
|
+
if team2_name != 'N.N.' ## note - ignore no team/placeholder (e.g. N.N)
|
|
126
|
+
team2_name = "#{team2_name} (#{team2[:country][:code]})"
|
|
127
|
+
end
|
|
160
128
|
end
|
|
161
129
|
|
|
162
130
|
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
group = m['group']
|
|
163
135
|
stage_key = m['stage']
|
|
164
136
|
stats['stage'][ stage_key ] += 1 ## track stage counts
|
|
137
|
+
## add group stats?
|
|
165
138
|
|
|
166
139
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
end
|
|
177
|
-
['Group', nil]
|
|
178
|
-
end
|
|
140
|
+
## GROUP_A
|
|
141
|
+
## shorten group to A|B|C etc.
|
|
142
|
+
if group
|
|
143
|
+
## assert group only used with GROUP_STAGE
|
|
144
|
+
if stage_key != 'GROUP_STAGE'
|
|
145
|
+
raise ArgumentError,
|
|
146
|
+
"group defined BUT stage set to >#{stage_key}< (expected GROUP_STAGE)" +
|
|
147
|
+
"and matchday to >#{m['matchday']}<"
|
|
148
|
+
end
|
|
179
149
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
150
|
+
if group =~ /^(GROUP_|Group )/
|
|
151
|
+
group = group.sub( /^(GROUP_|Group )/, '' )
|
|
152
|
+
else
|
|
153
|
+
raise ArgumentError,
|
|
154
|
+
"group defined with NON GROUP!? >#{group}< reset to empty"+
|
|
155
|
+
" and matchday to >#{m['matchday']}<"
|
|
156
|
+
end
|
|
157
|
+
else ## assume group.nil?
|
|
158
|
+
group = ''
|
|
183
159
|
end
|
|
184
160
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
if
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
## note - ignore possible leg in matchday for now
|
|
195
|
-
matchday = stage_round
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
## map stage to stage + round
|
|
164
|
+
## note - if no mapping defined; use (fallback to) upcase version
|
|
165
|
+
stage = STAGES[ stage_key ]
|
|
166
|
+
|
|
167
|
+
if stage.nil?
|
|
168
|
+
puts "!! WARN - no stage mapping found for stage >#{stage_key}<"
|
|
169
|
+
stage = stage_key
|
|
196
170
|
end
|
|
197
171
|
|
|
198
172
|
|
|
199
|
-
teams[ team1 ] += 1
|
|
200
|
-
teams[ team2 ] += 1
|
|
201
173
|
|
|
174
|
+
matchday = m['matchday']
|
|
175
|
+
matchday = nil if matchday == 0 ## change 0 to nil (empty) too
|
|
202
176
|
|
|
203
|
-
### mods - rename club names
|
|
204
|
-
unless mods.nil? || mods.empty?
|
|
205
|
-
team1 = mods[ team1 ] if mods[ team1 ]
|
|
206
|
-
team2 = mods[ team2 ] if mods[ team2 ]
|
|
207
|
-
end
|
|
208
177
|
|
|
209
|
-
####
|
|
210
|
-
# auto-add (fifa) country code if int'l club tournament
|
|
211
|
-
if clubs_intl
|
|
212
|
-
## note - use "original" name (not moded) for lookup
|
|
213
|
-
team1_org = m['homeTeam']['name']
|
|
214
|
-
team2_org = m['awayTeam']['name']
|
|
215
|
-
if team1_org ## note - ignore no team/placeholder (e.g. N.N)
|
|
216
|
-
country_code = find_team_country_code( team1_org, teams: teams_by_name )
|
|
217
|
-
team1 = "#{team1} (#{country_code})"
|
|
218
|
-
end
|
|
219
|
-
if team2_org ## note - ignore no team/placeholder (e.g. N.N)
|
|
220
|
-
country_code = find_team_country_code( team2_org, teams: teams_by_name )
|
|
221
|
-
team2 = "#{team2} (#{country_code})"
|
|
222
|
-
end
|
|
223
|
-
end
|
|
224
178
|
|
|
179
|
+
score = m['score']
|
|
225
180
|
|
|
226
181
|
|
|
227
182
|
comments = ''
|
|
@@ -256,9 +211,8 @@ matches.each do |m|
|
|
|
256
211
|
ht = ''
|
|
257
212
|
comments = 'postponed'
|
|
258
213
|
else
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
exit 1
|
|
214
|
+
raise ArgumentError,
|
|
215
|
+
"unsupported match status >#{m['status']}< - sorry:"
|
|
262
216
|
end
|
|
263
217
|
|
|
264
218
|
|
|
@@ -297,14 +251,14 @@ matches.each do |m|
|
|
|
297
251
|
|
|
298
252
|
recs << [stage,
|
|
299
253
|
group,
|
|
300
|
-
matchday,
|
|
254
|
+
matchday.to_s, ## not convert nil or Integer to str e.g. "", "1"
|
|
301
255
|
date,
|
|
302
256
|
time,
|
|
303
257
|
timezone,
|
|
304
|
-
|
|
258
|
+
team1_name,
|
|
305
259
|
ft,
|
|
306
260
|
ht,
|
|
307
|
-
|
|
261
|
+
team2_name,
|
|
308
262
|
et,
|
|
309
263
|
pen,
|
|
310
264
|
comments,
|
|
@@ -333,7 +287,7 @@ dates = "#{start_date.strftime('%b %-d')} - #{end_date.strftime('%b %-d')}"
|
|
|
333
287
|
|
|
334
288
|
buf = ''
|
|
335
289
|
buf << "#{season.key} (#{dates}) - "
|
|
336
|
-
buf << "#{teams.
|
|
290
|
+
buf << "#{teams.size} teams, "
|
|
337
291
|
buf << "#{recs.size} matches"
|
|
338
292
|
# buf << "#{stat[:regular_season][:goals]} goals"
|
|
339
293
|
buf << "\n"
|
|
@@ -420,27 +374,17 @@ recs, headers = vacuum( recs )
|
|
|
420
374
|
headers: headers )
|
|
421
375
|
|
|
422
376
|
|
|
423
|
-
teams.each do |
|
|
424
|
-
rec
|
|
425
|
-
print
|
|
426
|
-
print name
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
print " › #{rec['area']['name']}"
|
|
430
|
-
print " - #{rec['address']}"
|
|
431
|
-
else
|
|
432
|
-
if name == 'N.N.'
|
|
433
|
-
## ignore missing record
|
|
434
|
-
else
|
|
435
|
-
puts "!! ERROR - no team record found in teams.json for #{name}"
|
|
436
|
-
exit 1
|
|
437
|
-
end
|
|
438
|
-
end
|
|
377
|
+
teams.each do |rec|
|
|
378
|
+
print " #{rec[:count]}x "
|
|
379
|
+
print rec[:name]
|
|
380
|
+
print " | #{rec[:short_name]} " if rec[:name] != rec[:short_name]
|
|
381
|
+
print " › #{rec[:country][:name]} (#{rec[:country][:code]})"
|
|
382
|
+
print " - #{rec[:address]}"
|
|
439
383
|
print "\n"
|
|
440
384
|
end
|
|
441
385
|
|
|
442
386
|
## pp stat
|
|
443
|
-
end # method
|
|
387
|
+
end # method convert_csv
|
|
444
388
|
|
|
445
389
|
|
|
446
390
|
|
|
@@ -471,6 +415,9 @@ MIN_HEADERS = [ ## always keep even if all empty
|
|
|
471
415
|
|
|
472
416
|
|
|
473
417
|
|
|
418
|
+
###
|
|
419
|
+
### move upstream to cocos!! (re)use vacuum_csv !!!!
|
|
420
|
+
|
|
474
421
|
def self.vacuum( rows, headers: MAX_HEADERS, fixed_headers: MIN_HEADERS )
|
|
475
422
|
## check for unused columns and strip/remove
|
|
476
423
|
counter = Array.new( MAX_HEADERS.size, 0 )
|
|
@@ -513,5 +460,3 @@ def self.vacuum( rows, headers: MAX_HEADERS, fixed_headers: MIN_HEADERS )
|
|
|
513
460
|
[rows, headers]
|
|
514
461
|
end
|
|
515
462
|
end # module Footballdata
|
|
516
|
-
|
|
517
|
-
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
|
|
2
|
+
module Footballdata
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def self.convert_json( league:, season: )
|
|
9
|
+
|
|
10
|
+
season = Season( season ) ## cast (ensure) season class (NOT string, integer, etc.)
|
|
11
|
+
|
|
12
|
+
### note - find_league returns the metal_league_code
|
|
13
|
+
league_code = find_league!( league )
|
|
14
|
+
|
|
15
|
+
matches_url = Metal.competition_matches_url( league_code, season.start_year )
|
|
16
|
+
teams_url = Metal.competition_teams_url( league_code, season.start_year )
|
|
17
|
+
|
|
18
|
+
data = Webcache.read_json( matches_url )
|
|
19
|
+
data_teams = Webcache.read_json( teams_url )
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## note - for international club tournaments
|
|
23
|
+
## auto-add (fifa) country code e.g.
|
|
24
|
+
## Liverpool FC => Liverpool FC (ENG)
|
|
25
|
+
##
|
|
26
|
+
## todo/fix - move flag to league_info via .csv config - why? why not?
|
|
27
|
+
## clubs_intl = ['uefa.cl',
|
|
28
|
+
## 'copa.l'].include?(league.downcase) ? true : false
|
|
29
|
+
|
|
30
|
+
## build a team lookup by name (& short_name)
|
|
31
|
+
|
|
32
|
+
teams = Teams.new
|
|
33
|
+
teams.add( data_teams['teams'] )
|
|
34
|
+
puts "#{teams.size} team(s)"
|
|
35
|
+
|
|
36
|
+
## add/update match counts
|
|
37
|
+
teams.add_matches( data['matches'] )
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
stages = Stages.new
|
|
41
|
+
stages.add_matches( data['matches'] )
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
recs = []
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
## track (meta stats) stage, match status et
|
|
48
|
+
stats = { status: Hash.new(0), ## e.g. TIMED,etc
|
|
49
|
+
score: Hash.new(0), ## e.g. REGULAR,etc.
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
data[ 'matches'].each do |m|
|
|
56
|
+
|
|
57
|
+
## use ? or N.N. or ? for nil - why? why not?
|
|
58
|
+
team1_name = m['homeTeam']['name']
|
|
59
|
+
team2_name = m['awayTeam']['name']
|
|
60
|
+
|
|
61
|
+
team1 = team1_name ? teams.find_by!(name: team1_name ) : { name: 'N.N.' }
|
|
62
|
+
team2 = team2_name ? teams.find_by!(name: team2_name ) : { name: 'N.N.' }
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
stage = m['stage']
|
|
66
|
+
group = m['group']
|
|
67
|
+
|
|
68
|
+
matchday = m['matchday']
|
|
69
|
+
matchday = nil if matchday == 0 ## change 0 to nil (empty) too
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
stats[:status][m['status']] += 1 ## track status counts
|
|
73
|
+
stats[:score][m['score']['duration']] += 1
|
|
74
|
+
|
|
75
|
+
case m['status']
|
|
76
|
+
when 'SCHEDULED', 'TIMED',
|
|
77
|
+
'PAUSED', 'IN_PLAY' ## IN_PLAY, PAUSED
|
|
78
|
+
score = []
|
|
79
|
+
when 'FINISHED'
|
|
80
|
+
score = convert_score_to_hash( m['score'] )
|
|
81
|
+
when 'AWARDED' # AWARDED
|
|
82
|
+
assert( m['score']['duration'] == 'REGULAR', 'score.duration REGULAR expected' )
|
|
83
|
+
score = [m['score']['fullTime']['home'],
|
|
84
|
+
m['score']['fullTime']['away']]
|
|
85
|
+
when 'CANCELLED'
|
|
86
|
+
## note cancelled might have scores!! -- add/fix later!!!
|
|
87
|
+
## ht only or ft+ht!!! (see fr 2021/22)
|
|
88
|
+
## score = convert_score_to_hash( m['score'] )
|
|
89
|
+
##
|
|
90
|
+
## note - there is cancelled (e.g. not/played) AND
|
|
91
|
+
## annulled/voided (e.g. played!! with score)
|
|
92
|
+
score = []
|
|
93
|
+
when 'POSTPONED'
|
|
94
|
+
score = []
|
|
95
|
+
else
|
|
96
|
+
raise ArgumentError,
|
|
97
|
+
"unsupported match status >#{m['status']}< - sorry: #{m.pretty_inspect}"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
rec = {
|
|
103
|
+
status: m['status'],
|
|
104
|
+
stage: stage
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
utc = UTC.strptime( m['utcDate'], '%Y-%m-%dT%H:%M:%SZ' )
|
|
109
|
+
assert( utc.strftime( '%Y-%m-%dT%H:%M:%SZ' ) == m['utcDate'], 'utc time mismatch' )
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
## do NOT add time if status is SCHEDULED
|
|
113
|
+
## or POSTPONED for now
|
|
114
|
+
## otherwise assume time always present - why? why not?
|
|
115
|
+
##
|
|
116
|
+
## assume NOT valid utc time if 00:00
|
|
117
|
+
if utc.hour == 0 && utc.min == 0 &&
|
|
118
|
+
['SCHEDULED','POSTPONED'].include?( m['status'] )
|
|
119
|
+
## assume local date - why? why not?
|
|
120
|
+
rec[:date] = utc.strftime( '%Y-%m-%d' )
|
|
121
|
+
else
|
|
122
|
+
rec[:date_utc] = utc.strftime( '%Y-%m-%dT%H:%M:%SZ' )
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
rec[:group] = group if group
|
|
128
|
+
rec[:matchday] = matchday if matchday
|
|
129
|
+
|
|
130
|
+
rec[:team1] = team1[:name]
|
|
131
|
+
rec[:team2] = team2[:name]
|
|
132
|
+
|
|
133
|
+
rec[:score] = score unless score.empty?
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
recs << rec
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
stats[:matches] = recs.size
|
|
141
|
+
stats[:teams] = teams.size
|
|
142
|
+
|
|
143
|
+
data = {
|
|
144
|
+
slug: league,
|
|
145
|
+
season: season.to_s,
|
|
146
|
+
meta: stats,
|
|
147
|
+
stages: stages.as_json,
|
|
148
|
+
teams: teams.as_json,
|
|
149
|
+
matches: recs
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
path = "#{config.convert.out_dir}/#{season.to_path}/#{league.downcase}.json"
|
|
154
|
+
## note: change season_key from 2019/20 to 2019-20 (for path/directory!!!!)
|
|
155
|
+
puts " writing to >#{path}<"
|
|
156
|
+
write_json( path, data )
|
|
157
|
+
end # method convert_json
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
end # module Footballdata
|
|
@@ -53,6 +53,7 @@ class Metal
|
|
|
53
53
|
|
|
54
54
|
request_headers = request_headers.merge( headers ) unless headers.empty?
|
|
55
55
|
|
|
56
|
+
## pp request_headers
|
|
56
57
|
|
|
57
58
|
## note: add format: 'json' for pretty printing json (before) save in cache
|
|
58
59
|
response = Webget.call( url, headers: request_headers )
|
|
@@ -117,10 +118,12 @@ class Metal
|
|
|
117
118
|
get( "#{BASE_URL}/teams/#{id}" )
|
|
118
119
|
end
|
|
119
120
|
|
|
120
|
-
|
|
121
|
-
|
|
121
|
+
|
|
122
|
+
def self.match( id, headers: {} )
|
|
123
|
+
get( "#{BASE_URL}/matches/#{id}", headers: headers )
|
|
122
124
|
end
|
|
123
125
|
|
|
126
|
+
|
|
124
127
|
def self.person( id )
|
|
125
128
|
get( "#{BASE_URL}/persons/#{id}" )
|
|
126
129
|
end
|
data/lib/footballdata/leagues.rb
CHANGED
data/lib/footballdata/version.rb
CHANGED
data/lib/footballdata.rb
CHANGED
|
@@ -36,8 +36,13 @@ require_relative 'footballdata/download'
|
|
|
36
36
|
require_relative 'footballdata/prettyprint'
|
|
37
37
|
|
|
38
38
|
require_relative 'footballdata/mods'
|
|
39
|
-
require_relative 'footballdata/
|
|
39
|
+
require_relative 'footballdata/convert_csv'
|
|
40
|
+
require_relative 'footballdata/convert_json'
|
|
41
|
+
require_relative 'footballdata/convert-stages'
|
|
42
|
+
require_relative 'footballdata/convert-teams'
|
|
40
43
|
require_relative 'footballdata/convert-score'
|
|
44
|
+
|
|
45
|
+
|
|
41
46
|
require_relative 'footballdata/teams'
|
|
42
47
|
|
|
43
48
|
|
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: 2026.7.
|
|
4
|
+
version: 2026.7.12
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gerald Bauer
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-07-
|
|
11
|
+
date: 2026-07-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: leagues
|
|
@@ -105,7 +105,10 @@ files:
|
|
|
105
105
|
- config/leagues_tier1.csv
|
|
106
106
|
- lib/footballdata.rb
|
|
107
107
|
- lib/footballdata/convert-score.rb
|
|
108
|
-
- lib/footballdata/convert.rb
|
|
108
|
+
- lib/footballdata/convert-stages.rb
|
|
109
|
+
- lib/footballdata/convert-teams.rb
|
|
110
|
+
- lib/footballdata/convert_csv.rb
|
|
111
|
+
- lib/footballdata/convert_json.rb
|
|
109
112
|
- lib/footballdata/download.rb
|
|
110
113
|
- lib/footballdata/leagues.rb
|
|
111
114
|
- lib/footballdata/mods.rb
|