fifadat 0.0.1

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.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +4 -0
  3. data/Manifest.txt +91 -0
  4. data/README.md +24 -0
  5. data/Rakefile +32 -0
  6. data/bin/fifadat +14 -0
  7. data/lib/fifadat/api-config.rb +120 -0
  8. data/lib/fifadat/api.rb +110 -0
  9. data/lib/fifadat/config/codes_club-europe.csv +59 -0
  10. data/lib/fifadat/config/codes_club.csv +41 -0
  11. data/lib/fifadat/config/codes_nati.csv +6 -0
  12. data/lib/fifadat/config/seasons/ar.csv +7 -0
  13. data/lib/fifadat/config/seasons/at.csv +7 -0
  14. data/lib/fifadat/config/seasons/at.cup.csv +5 -0
  15. data/lib/fifadat/config/seasons/au.csv +7 -0
  16. data/lib/fifadat/config/seasons/be.csv +7 -0
  17. data/lib/fifadat/config/seasons/br.csv +6 -0
  18. data/lib/fifadat/config/seasons/ch.csv +7 -0
  19. data/lib/fifadat/config/seasons/clubworldcup.csv +2 -0
  20. data/lib/fifadat/config/seasons/cn.csv +7 -0
  21. data/lib/fifadat/config/seasons/co.csv +7 -0
  22. data/lib/fifadat/config/seasons/copa.l.csv +7 -0
  23. data/lib/fifadat/config/seasons/copa.s.csv +7 -0
  24. data/lib/fifadat/config/seasons/cz.csv +7 -0
  25. data/lib/fifadat/config/seasons/de.2.csv +6 -0
  26. data/lib/fifadat/config/seasons/de.csv +7 -0
  27. data/lib/fifadat/config/seasons/de.cup.csv +6 -0
  28. data/lib/fifadat/config/seasons/eg.csv +6 -0
  29. data/lib/fifadat/config/seasons/eng.2.csv +7 -0
  30. data/lib/fifadat/config/seasons/eng.csv +7 -0
  31. data/lib/fifadat/config/seasons/eng.facup.csv +6 -0
  32. data/lib/fifadat/config/seasons/es.csv +6 -0
  33. data/lib/fifadat/config/seasons/fr.csv +6 -0
  34. data/lib/fifadat/config/seasons/gr.csv +7 -0
  35. data/lib/fifadat/config/seasons/hu.csv +6 -0
  36. data/lib/fifadat/config/seasons/ie.csv +6 -0
  37. data/lib/fifadat/config/seasons/il.csv +6 -0
  38. data/lib/fifadat/config/seasons/interconticup.csv +24 -0
  39. data/lib/fifadat/config/seasons/is.csv +6 -0
  40. data/lib/fifadat/config/seasons/it.csv +7 -0
  41. data/lib/fifadat/config/seasons/it.cup.csv +5 -0
  42. data/lib/fifadat/config/seasons/jp.csv +7 -0
  43. data/lib/fifadat/config/seasons/kr.csv +7 -0
  44. data/lib/fifadat/config/seasons/lu.csv +3 -0
  45. data/lib/fifadat/config/seasons/ma.csv +3 -0
  46. data/lib/fifadat/config/seasons/mls.csv +6 -0
  47. data/lib/fifadat/config/seasons/mx.2.csv +5 -0
  48. data/lib/fifadat/config/seasons/mx.csv +6 -0
  49. data/lib/fifadat/config/seasons/mx.cup.csv +3 -0
  50. data/lib/fifadat/config/seasons/nir.csv +6 -0
  51. data/lib/fifadat/config/seasons/nl.csv +7 -0
  52. data/lib/fifadat/config/seasons/no.csv +6 -0
  53. data/lib/fifadat/config/seasons/pl.csv +6 -0
  54. data/lib/fifadat/config/seasons/pt.csv +7 -0
  55. data/lib/fifadat/config/seasons/sa.csv +7 -0
  56. data/lib/fifadat/config/seasons/sco.csv +7 -0
  57. data/lib/fifadat/config/seasons/se.csv +7 -0
  58. data/lib/fifadat/config/seasons/tn.csv +6 -0
  59. data/lib/fifadat/config/seasons/tr.csv +7 -0
  60. data/lib/fifadat/config/seasons/uefa.cl.csv +7 -0
  61. data/lib/fifadat/config/seasons/uefa.con.csv +6 -0
  62. data/lib/fifadat/config/seasons/uefa.europa.csv +6 -0
  63. data/lib/fifadat/config/seasons/uy.csv +6 -0
  64. data/lib/fifadat/config/seasons/wal.csv +6 -0
  65. data/lib/fifadat/config/seasons/worldcup.csv +24 -0
  66. data/lib/fifadat/config/seasons/za.csv +6 -0
  67. data/lib/fifadat/errata.rb +59 -0
  68. data/lib/fifadat/helper.rb +46 -0
  69. data/lib/fifadat/json.rb +113 -0
  70. data/lib/fifadat/pp/build_match.rb +105 -0
  71. data/lib/fifadat/pp/build_report.rb +135 -0
  72. data/lib/fifadat/pp/convert-reports.rb +114 -0
  73. data/lib/fifadat/pp/convert.rb +163 -0
  74. data/lib/fifadat/pp/goals-calc_score.rb +58 -0
  75. data/lib/fifadat/pp/goals.rb +89 -0
  76. data/lib/fifadat/pp/helper-date.rb +13 -0
  77. data/lib/fifadat/pp/helper-minute.rb +81 -0
  78. data/lib/fifadat/pp/helper-score.rb +139 -0
  79. data/lib/fifadat/pp/norm.rb +34 -0
  80. data/lib/fifadat/pp/officials.rb +86 -0
  81. data/lib/fifadat/pp/penalties.rb +132 -0
  82. data/lib/fifadat/pp/players.rb +323 -0
  83. data/lib/fifadat/pp/stadiums.rb +99 -0
  84. data/lib/fifadat/pp/stages.rb +119 -0
  85. data/lib/fifadat/pp/substitutions.rb +444 -0
  86. data/lib/fifadat/pp/teams.rb +91 -0
  87. data/lib/fifadat/ppdebug.rb +128 -0
  88. data/lib/fifadat/ppmatch.rb +78 -0
  89. data/lib/fifadat/prepare.rb +117 -0
  90. data/lib/fifadat/tool.rb +184 -0
  91. data/lib/fifadat/types.rb +166 -0
  92. data/lib/fifadat.rb +54 -0
  93. metadata +215 -0
@@ -0,0 +1,323 @@
1
+
2
+
3
+
4
+ def build_players( recs )
5
+ recs = recs.map { |h| build_player( h ) }
6
+ recs
7
+ end
8
+
9
+
10
+ # TYPE_PLAYER_POS = {
11
+ # 0 => 'GK',
12
+ # 1 => 'DF',
13
+ # 2 => 'MF',
14
+ # 3 => 'FW',
15
+ # 4 => '??',
16
+ # 5 => '??',
17
+ # 6 => '??'
18
+ # }
19
+
20
+ def build_player( h )
21
+ name = desc( h['PlayerName'] )
22
+
23
+ if name.nil?
24
+ ## "PlayerName"=>[],
25
+ ## "ShortName"=>[{"Locale"=>"en-gb", "Description"=>"S. Lainer"}],
26
+
27
+ ## hack:
28
+ ## use shortname
29
+ name = desc( h['ShortName'] )
30
+ end
31
+
32
+ if name.nil?
33
+ ## puts "name is nil:"
34
+ ## pp h
35
+ ## exit 1
36
+ ## report/log error/warn
37
+ ##
38
+ ## quick fix use N.N. for now
39
+ name = 'N.N.'
40
+ end
41
+
42
+ ## name = norm_player( name )
43
+
44
+
45
+ short_name = desc( h['ShortName'] )
46
+
47
+ ##
48
+ ## todo/check - add IdCountry if available??
49
+
50
+
51
+ status = h['Status']
52
+ assert( [1,2].include?( status ), "player status 1,2 expected; got #{h.inspect}")
53
+
54
+ ## what is pos 6 ??
55
+ ## e.g.
56
+ ## !! ASSERT FAILED
57
+ ## {"IdPlayer"=>"419456", "IdTeam"=>"32759", "ShirtNumber"=>4, "Status"=>1,
58
+ ## "PlayerName"=>[{"Locale"=>"en-GB", "Description"=>"Ismaila COULIBALY"}],
59
+ ## "ShortName"=>[{"Locale"=>"en-GB", "Description"=>"Ismaila COULIBALY"}],
60
+ ## "Position"=>6, "PlayerPicture"=>nil,
61
+ ## "FieldStatus"=>2, "LineupX"=>nil, "LineupY"=>nil}
62
+ ##
63
+ ## what is pos 5 ??
64
+ ## e.g.
65
+ ## {"IdPlayer"=>"7czui1ih46j3zitzysxvjqjv8", "IdTeam"=>"2000019848", "ShirtNumber"=>33,
66
+ ## "Status"=>1,
67
+ ## "PlayerName"=>[{"Locale"=>"en-gb", "Description"=>"Maximilian Hennig"}],
68
+ ## "ShortName"=>[{"Locale"=>"en-gb", "Description"=>"M. Hennig"}],
69
+ ## "Position"=>5, "PlayerPicture"=>nil, "FieldStatus"=>2, "LineupX"=>nil, "LineupY"=>nil}
70
+ ##
71
+ ## what is pos 4 ??
72
+ ## {"IdPlayer"=>"254150", "IdTeam"=>"2000017585", "ShirtNumber"=>15,
73
+ ## "Status"=>2, "SpecialStatus"=>nil, "Captain"=>false,
74
+ ## "PlayerName"=>[{"Locale"=>"en-GB", "Description"=>"Nemanja RNIC"}],
75
+ ## "ShortName"=>[{"Locale"=>"en-gb", "Description"=>"N. Rnić"}],
76
+ ## "Position"=>4, "PlayerPicture"=>nil, "FieldStatus"=>1, "LineupX"=>nil, "LineupY"=>nil}
77
+
78
+
79
+ pos = h['Position']
80
+ assert( [0,1,2,3,4,5,6].include?( pos ), "player pos 0,1,2,3,4,5,6 expected; got #{h.inspect}" )
81
+
82
+ ## 0 - is always goal keeper
83
+ ## check meaning of 1 to 6
84
+ ## on website !!!
85
+ ## pos is NOT (simply) matching tactics/formation (index number)!!
86
+
87
+ rec = { id: h['IdPlayer'],
88
+ name: name,
89
+ short_name: short_name,
90
+ status: status,
91
+ pos: pos,
92
+ }
93
+
94
+ ## check for shirtNumber too
95
+ num = h['ShirtNumber']
96
+ rec[:num] = num if num
97
+
98
+ rec[:captain] = true if h['Captain']
99
+
100
+ rec
101
+ end
102
+
103
+
104
+
105
+ class Players
106
+ def initialize
107
+ @recs = {}
108
+ end
109
+
110
+ def add( recs ) ## rename to collect - why? why not?
111
+ recs.each { |h| _add( build_player(h)) }
112
+ end
113
+
114
+
115
+ def _add( new_rec )
116
+ rec = @recs[ new_rec[:id] ]
117
+ if rec.nil?
118
+ rec = new_rec
119
+ rec[:count] = 1 ## add counter - why? why not?
120
+ @recs[ new_rec[:id]] = new_rec
121
+
122
+ else
123
+ rec[:count] += 1
124
+ ## assert attributes equal - why? why not?
125
+
126
+ assert( new_rec[:name] == rec[:name] &&
127
+ new_rec[:short_name] == rec[:short_name],
128
+ "player records NOT matching - #{rec.pretty_inspect} != #{new_rec.pretty_inspect}")
129
+ end
130
+ end
131
+
132
+ def find!( id_player )
133
+ rec = @recs[ id_player ]
134
+ raise ArgumentError, "no player w/ id >#{id_player}< found; sorry" if rec.nil?
135
+ rec
136
+ end
137
+
138
+
139
+ ###
140
+ ## todo/check
141
+ ## add alias (or rename) starter ??
142
+ ## add new subs to status == 2 - why? why not?
143
+ def lineup
144
+ recs = @recs.values.select { |rec| rec[:status] == 1 }
145
+
146
+ recs = recs.map {|rec| rec.except( :id, :status, :count ) }
147
+ recs
148
+ end
149
+
150
+ def bench
151
+ recs = @recs.values.select { |rec| rec[:status] == 2 }
152
+
153
+ recs = recs.map {|rec| rec.except( :id, :status, :count ) }
154
+ recs
155
+ end
156
+
157
+
158
+
159
+ ## all players with red or red-yellow card (sent off)
160
+ def sentoff
161
+ recs = @recs.values.select { |rec| rec[:r] || rec[:yr] }
162
+
163
+ =begin
164
+ "id": "395516",
165
+ "name": "Cesar MONTES",
166
+ "short_name": "Cesar MONTES",
167
+ "status": 1,
168
+ "pos": 1,
169
+ "captain": true,
170
+ "count": 1,
171
+ "r": {
172
+ "minute": "90+2'"
173
+ =end
174
+
175
+ ## todo/check - add a flag for yellow-red card (e.g. yr=true) - why? why not?
176
+
177
+ recs = recs.map do |rec|
178
+ minute = if rec[:r]
179
+ rec[:r][:minute]
180
+ elsif rec[:yr]
181
+ rec[:yr][:minute]
182
+ else
183
+ raise ArgumentError, "r or yr card expected; got #{rec.inspect}"
184
+ end
185
+ { name: rec[:name],
186
+ minute: minute }
187
+ end
188
+
189
+ recs
190
+ end
191
+
192
+
193
+ def redcards
194
+ recs = @recs.values.select { |rec| rec[:r] }
195
+
196
+ recs = recs.map do |rec|
197
+ { name: rec[:name],
198
+ minute: rec[:r][:minute] }
199
+ end
200
+ recs
201
+ end
202
+
203
+ def yellowcards
204
+ recs = @recs.values.select { |rec| rec[:y] }
205
+
206
+ recs = recs.map do |rec|
207
+ { name: rec[:name],
208
+ minute: rec[:y][:minute] }
209
+ end
210
+ recs
211
+ end
212
+
213
+ def yellowredcards
214
+ recs = @recs.values.select { |rec| rec[:yr] }
215
+
216
+ recs = recs.map do |rec|
217
+ { name: rec[:name],
218
+ minute: rec[:yr][:minute] }
219
+ end
220
+ recs
221
+ end
222
+
223
+
224
+
225
+ def add_bookings( bookings ) ## yellow/red cards
226
+ bookings.each do |b|
227
+
228
+ card = b['Card']
229
+ assert( [0,1,2,3].include?( card ), "card 0/1/2/3 expected; got #{b.pretty_inspect}")
230
+
231
+ ##
232
+ ## what is card 0? ignore for now
233
+ ## Palmeiras v FC Porto 0-0 - 2025-06-15T18:00:00+00:00
234
+ ## !! ASSERT FAILED - card 1/2/3 expected; got {"Card"=>0,
235
+ ## "Period"=>5,
236
+ ## "IdEvent"=>nil,
237
+ ## "EventNumber"=>nil,
238
+ ## "IdPlayer"=>"495048",
239
+ ## "IdCoach"=>nil,
240
+ ## "IdTeam"=>"1884426",
241
+ ## "Minute"=>"72'",
242
+ ## "Reason"=>nil}
243
+ next if card == 0
244
+
245
+
246
+ idPlayer = b['IdPlayer']
247
+
248
+ ## booking (card) for coach or stuff!!!!
249
+ ## skip for now
250
+ next if idPlayer.nil? && (b['IdCoach'] || b['IdStaff'])
251
+
252
+
253
+ player = @recs[ idPlayer ]
254
+ assert( player, "booking player not found; sorry - #{b.pretty_inspect}" )
255
+
256
+ ## note - parse & reformat minute for keep same format
257
+ ## _fmt_minute( *_parse_minute( b['Minute'] ))
258
+ minute = _build_minute( b )
259
+
260
+ if card == 1 ## yellow
261
+ player[ :y ] = { minute: minute }
262
+ elsif card == 2 ## red
263
+ player[ :r ] = { minute: minute }
264
+ elsif card == 3 ## yellow/red
265
+ player[ :yr ] = { minute: minute }
266
+ end
267
+ end
268
+ end
269
+
270
+
271
+
272
+ def add_subs( subs )
273
+ subs.each do |sub|
274
+
275
+ idPlayerOff = sub['IdPlayerOff']
276
+ idPlayerOn = sub['IdPlayerOn']
277
+
278
+ ## note - parse & reformat minute for keep same format
279
+ minute = _build_sub_minute( sub )
280
+
281
+ player_off = @recs[ idPlayerOff ]
282
+ player_on = @recs[ idPlayerOn ]
283
+
284
+ ##
285
+ ## note - skip special case for now
286
+ ## with NO PLAYER ON (e.g. idPlayerOn is nil!!)
287
+ ## next if idPlayerOn.nil?
288
+ ## todo/fix - report/log warning!!!
289
+
290
+
291
+ if player_off.nil?
292
+ puts "!! player_off >#{idPlayerOff}< not found in:"
293
+ pp sub
294
+ puts "---"
295
+ pp @recs.values
296
+ exit 1
297
+ end
298
+
299
+ if player_on.nil?
300
+ puts "!! player_on >#{idPlayerOn}< not found in:"
301
+ pp sub
302
+ puts "---"
303
+ pp @recs.values
304
+ exit 1
305
+ end
306
+
307
+ assert( player_off && player_on,
308
+ "subs player_off or player_on not found; sorry" )
309
+
310
+ player_off[ :sub ] = { minute: minute,
311
+ player_ref: player_on }
312
+ end
313
+ end
314
+
315
+
316
+ def dump
317
+ pp @recs.values
318
+ puts " #{@recs.size} player(s)"
319
+ end
320
+
321
+ def size() @recs.size; end
322
+
323
+ end # class Players
@@ -0,0 +1,99 @@
1
+
2
+
3
+ def build_stadium( h )
4
+ if h
5
+ id = h['IdStadium']
6
+ id_country = h['IdCountry']
7
+ ## id_city = h['IdCity']
8
+
9
+ name = desc( h['Name'] )
10
+ city_name = desc( h['CityName'] )
11
+
12
+
13
+ if name.nil?
14
+ puts "stadium without name:"
15
+ pp h
16
+ exit 1
17
+ end
18
+
19
+ ## name, city_name = norm_stadium( name, city_name: city_name )
20
+
21
+
22
+ if id.nil?
23
+ ## auto-generate id
24
+ ## use slug of name plus id_city
25
+ ## add city name
26
+ ## fix- add 0-9 & dash(-) to name too - why? why not?
27
+ id = name.downcase.gsub( /[^a-z]/, '' )
28
+ id += "_" + city_name.downcase.gsub( /[^a-z]/, '' )
29
+ end
30
+
31
+
32
+ rec = { id: id,
33
+ name: name,
34
+ city: city_name
35
+ }
36
+
37
+ rec[:street] = h['Street'] if h['Street']
38
+
39
+ rec[:country] = id_country ### fix - change to cc/country code - why? why not?
40
+
41
+ rec
42
+ else ## assume nil - dummy record
43
+ rec = {
44
+ id: '<nil>',
45
+ name: '?',
46
+ city: '?', ## use nil - why? why not?
47
+ country: '?' ## use nil - why? why not?
48
+ }
49
+ rec
50
+ end
51
+ end
52
+
53
+
54
+
55
+
56
+ class Stadiums
57
+ def initialize
58
+ @recs = {}
59
+ end
60
+
61
+ def add_matches( matches ) ## rename to add_matches - why? why not?
62
+ matches.each_with_index do |m|
63
+ stadium = build_stadium( m['Stadium'] )
64
+ _add( stadium )
65
+ end
66
+ end
67
+
68
+
69
+
70
+ def _add( new_rec )
71
+ rec = @recs[ new_rec[:id] ]
72
+ if rec.nil?
73
+ rec = new_rec
74
+ rec[:count] = 1 ## add counter - why? why not?
75
+ @recs[ new_rec[:id]] = new_rec
76
+ else
77
+ rec[:count] += 1
78
+
79
+ ## assert attributes equal - why? why not?
80
+ assert( new_rec[:name] == rec[:name] &&
81
+ new_rec[:city] == rec[:city] &&
82
+ new_rec[:country] == rec[:country],
83
+ "stadium records NOT matching - #{rec.pretty_inspect} != #{new_rec.pretty_inspect}")
84
+ end
85
+ end
86
+
87
+
88
+ def as_json( id: false )
89
+ if id
90
+ @recs.values
91
+ else
92
+ @recs.values.map { |rec| rec.except(:id ) }
93
+ end
94
+ end
95
+
96
+
97
+ def size() @recs.size; end
98
+
99
+ end # class Stadiums
@@ -0,0 +1,119 @@
1
+
2
+
3
+ =begin
4
+ [{"IdStage": "286472",
5
+ "Name": [{"Locale": "en-GB", "Description": "Final"}],
6
+ "IdSeason": "286466",
7
+ "SeasonName":
8
+ [{"Locale": "en-GB", "Description": "FIFA Club World Cup Morocco 2022™"}],
9
+ "IdCompetition": "107",
10
+ "StageLevel": null,
11
+ "StartDate": "2023-02-11T19:00:00Z",
12
+ "EndDate": "2023-02-11T19:00:00Z",
13
+ "Type": 0,
14
+ "SequenceOrder": 6,
15
+ "Properties": {"IdIFES": "286472"},
16
+ "IsUpdateable": null},
17
+ =end
18
+
19
+
20
+ def build_stage( h )
21
+ name = desc( h['Name'] )
22
+ seq = h['SequenceOrder']
23
+ level = h['StageLevel'] ## note - is optional
24
+
25
+ rec = {
26
+ ## id: h['IdStage'], ## convert to number - why? why not?
27
+ seq: seq,
28
+ name: name,
29
+ }
30
+
31
+
32
+ if h['StartDate'] && h['EndDate']
33
+ ## note - start/end dates are utc (NOT local)
34
+ start_date = parse_date_utc( h['StartDate'] ) ## expects ...:00Z format
35
+ end_date = parse_date_utc( h['EndDate'] ) ## expects ...:00Z format
36
+
37
+ ## check if HH::MM is 00:00
38
+ ## only use date for now; ignore time
39
+ ## report warn(ing) if time present!!!
40
+ if start_date.hour != 0 || start_date.min != 0 ||
41
+ end_date.hour != 0 || end_date.min != 0
42
+ ## issue warn start/end_date with HH:MM (NOT 00:00)
43
+ puts "!! warn: stage start/end_date with HH:MM (expected 00:00)"
44
+ pp h
45
+ end
46
+
47
+ rec[:start_date] = start_date.strftime('%Y-%m-%d')
48
+ rec[:end_date] = end_date.strftime('%Y-%m-%d')
49
+ else
50
+ ## maybe add to log later - why? why not?
51
+ ## puts "!! warn: stage witouout start/end_date"
52
+ ## pp h
53
+ end
54
+
55
+ rec[:count] = 0
56
+
57
+ rec[:level] = level if level
58
+
59
+ rec
60
+ end
61
+
62
+
63
+
64
+ class Stages
65
+
66
+ def self.read( path )
67
+ data = read_json_v2( path )
68
+ obj = new
69
+ obj.add( data['Results'] )
70
+ obj
71
+ end
72
+
73
+
74
+ def initialize
75
+ @recs = {}
76
+ end
77
+
78
+ def add( recs ) ## rename to collect - why? why not?
79
+ recs.each { |h| _add( build_stage(h)) }
80
+ end
81
+
82
+
83
+ def _add( new_rec )
84
+ rec = @recs[ new_rec[:name] ]
85
+ if rec.nil?
86
+ @recs[ new_rec[:name]] = new_rec
87
+ else
88
+ raise ArgumentError, "duplicate stage entry: #{new_rec.pretty_inspect}"
89
+ end
90
+ end
91
+
92
+
93
+ ## add match stats
94
+ def add_matches( matches )
95
+ matches.each do |m|
96
+ name = desc( m['StageName'] )
97
+ rec = find!( name )
98
+ rec[:count] += 1
99
+ end
100
+ end
101
+
102
+
103
+ def find!( name )
104
+ rec = @recs[ name ]
105
+ raise ArgumentError, "no stage w/ name >#{name}< found; sorry" if rec.nil?
106
+ rec
107
+ end
108
+
109
+ def as_json
110
+ ## note - sort by seq
111
+
112
+ @recs.values.sort do |l,r|
113
+ l[:seq] <=> r[:seq]
114
+ end
115
+ end
116
+
117
+ def size() @recs.size; end
118
+
119
+ end # class Stages