fifadat-convert 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.
@@ -0,0 +1,329 @@
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
+
133
+ def find( id_player )
134
+ rec = @recs[ id_player ]
135
+ rec
136
+ end
137
+
138
+ def find!( id_player )
139
+ rec = @recs[ id_player ]
140
+ raise ArgumentError, "no player w/ id >#{id_player}< found; sorry" if rec.nil?
141
+ rec
142
+ end
143
+
144
+
145
+ ###
146
+ ## todo/check
147
+ ## add alias (or rename) starter ??
148
+ ## add new subs to status == 2 - why? why not?
149
+ def lineup
150
+ recs = @recs.values.select { |rec| rec[:status] == 1 }
151
+
152
+ recs = recs.map {|rec| rec.except( :id, :status, :count ) }
153
+ recs
154
+ end
155
+
156
+ def bench
157
+ recs = @recs.values.select { |rec| rec[:status] == 2 }
158
+
159
+ recs = recs.map {|rec| rec.except( :id, :status, :count ) }
160
+ recs
161
+ end
162
+
163
+
164
+
165
+ ## all players with red or red-yellow card (sent off)
166
+ def sentoff
167
+ recs = @recs.values.select { |rec| rec[:r] || rec[:yr] }
168
+
169
+ =begin
170
+ "id": "395516",
171
+ "name": "Cesar MONTES",
172
+ "short_name": "Cesar MONTES",
173
+ "status": 1,
174
+ "pos": 1,
175
+ "captain": true,
176
+ "count": 1,
177
+ "r": {
178
+ "minute": "90+2'"
179
+ =end
180
+
181
+ ## todo/check - add a flag for yellow-red card (e.g. yr=true) - why? why not?
182
+
183
+ recs = recs.map do |rec|
184
+ minute = if rec[:r]
185
+ rec[:r][:minute]
186
+ elsif rec[:yr]
187
+ rec[:yr][:minute]
188
+ else
189
+ raise ArgumentError, "r or yr card expected; got #{rec.inspect}"
190
+ end
191
+ { name: rec[:name],
192
+ minute: minute }
193
+ end
194
+
195
+ recs
196
+ end
197
+
198
+
199
+ def redcards
200
+ recs = @recs.values.select { |rec| rec[:r] }
201
+
202
+ recs = recs.map do |rec|
203
+ { name: rec[:name],
204
+ minute: rec[:r][:minute] }
205
+ end
206
+ recs
207
+ end
208
+
209
+ def yellowcards
210
+ recs = @recs.values.select { |rec| rec[:y] }
211
+
212
+ recs = recs.map do |rec|
213
+ { name: rec[:name],
214
+ minute: rec[:y][:minute] }
215
+ end
216
+ recs
217
+ end
218
+
219
+ def yellowredcards
220
+ recs = @recs.values.select { |rec| rec[:yr] }
221
+
222
+ recs = recs.map do |rec|
223
+ { name: rec[:name],
224
+ minute: rec[:yr][:minute] }
225
+ end
226
+ recs
227
+ end
228
+
229
+
230
+
231
+ def add_bookings( bookings ) ## yellow/red cards
232
+ bookings.each do |b|
233
+
234
+ card = b['Card']
235
+ assert( [0,1,2,3].include?( card ), "card 0/1/2/3 expected; got #{b.pretty_inspect}")
236
+
237
+ ##
238
+ ## what is card 0? ignore for now
239
+ ## Palmeiras v FC Porto 0-0 - 2025-06-15T18:00:00+00:00
240
+ ## !! ASSERT FAILED - card 1/2/3 expected; got {"Card"=>0,
241
+ ## "Period"=>5,
242
+ ## "IdEvent"=>nil,
243
+ ## "EventNumber"=>nil,
244
+ ## "IdPlayer"=>"495048",
245
+ ## "IdCoach"=>nil,
246
+ ## "IdTeam"=>"1884426",
247
+ ## "Minute"=>"72'",
248
+ ## "Reason"=>nil}
249
+ next if card == 0
250
+
251
+
252
+ idPlayer = b['IdPlayer']
253
+
254
+ ## booking (card) for coach or stuff!!!!
255
+ ## skip for now
256
+ next if idPlayer.nil? && (b['IdCoach'] || b['IdStaff'])
257
+
258
+
259
+ player = @recs[ idPlayer ]
260
+ assert( player, "booking player not found; sorry - #{b.pretty_inspect}" )
261
+
262
+ ## note - parse & reformat minute for keep same format
263
+ ## _fmt_minute( *_parse_minute( b['Minute'] ))
264
+ minute = _build_minute( b )
265
+
266
+ if card == 1 ## yellow
267
+ player[ :y ] = { minute: minute }
268
+ elsif card == 2 ## red
269
+ player[ :r ] = { minute: minute }
270
+ elsif card == 3 ## yellow/red
271
+ player[ :yr ] = { minute: minute }
272
+ end
273
+ end
274
+ end
275
+
276
+
277
+
278
+ def add_subs( subs )
279
+ subs.each do |sub|
280
+
281
+ idPlayerOff = sub['IdPlayerOff']
282
+ idPlayerOn = sub['IdPlayerOn']
283
+
284
+ ## note - parse & reformat minute for keep same format
285
+ minute = _build_sub_minute( sub )
286
+
287
+ player_off = @recs[ idPlayerOff ]
288
+ player_on = @recs[ idPlayerOn ]
289
+
290
+ ##
291
+ ## note - skip special case for now
292
+ ## with NO PLAYER ON (e.g. idPlayerOn is nil!!)
293
+ ## next if idPlayerOn.nil?
294
+ ## todo/fix - report/log warning!!!
295
+
296
+
297
+ if player_off.nil?
298
+ puts "!! player_off >#{idPlayerOff}< not found in:"
299
+ pp sub
300
+ puts "---"
301
+ pp @recs.values
302
+ exit 1
303
+ end
304
+
305
+ if player_on.nil?
306
+ puts "!! player_on >#{idPlayerOn}< not found in:"
307
+ pp sub
308
+ puts "---"
309
+ pp @recs.values
310
+ exit 1
311
+ end
312
+
313
+ assert( player_off && player_on,
314
+ "subs player_off or player_on not found; sorry" )
315
+
316
+ player_off[ :sub ] = { minute: minute,
317
+ player_ref: player_on }
318
+ end
319
+ end
320
+
321
+
322
+ def dump
323
+ pp @recs.values
324
+ puts " #{@recs.size} player(s)"
325
+ end
326
+
327
+ def size() @recs.size; end
328
+
329
+ 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