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,105 @@
1
+
2
+
3
+
4
+ def _build_match( m ) ## use _fill/build_match_basics() or such ???
5
+ rec = {}
6
+
7
+ ## 0 => FINISHED/complete (OK)
8
+ ## 1 => SCHEDULED/not yet played
9
+ status = m['MatchStatus']
10
+ timed = m['TimeDefined']
11
+
12
+ if status == 1 && timed ## note: use TIMED (instead of SCHEDULED)
13
+ rec[:status] = 'TIMED'
14
+ else
15
+ rec[:status] = MATCH_STATUS[ status ] || "#{status}-???"
16
+ end
17
+
18
+
19
+
20
+ rec[:stage] = desc( m['StageName'] )
21
+
22
+ ## check optional group, matchday, (match) num
23
+ ## note - matchday is a string!! (check if always a number)
24
+ ## ## convert to int if str - why? why not??
25
+ group = desc( m['GroupName'] )
26
+ rec[:group] = group if group
27
+
28
+ matchday = m['MatchDay']
29
+ rec[:matchday] = matchday.to_i(10) if matchday
30
+
31
+ num = m['MatchNumber']
32
+ rec[:num] = num if num
33
+
34
+
35
+ dateTime = parse_date_utc( m['Date'] )
36
+ localDateTime = parse_date_utc( m['LocalDate'] )
37
+
38
+ if timed
39
+ ## pass along as is for now - why? why not
40
+ ## note - Date is date_utc and
41
+ ## LocalDate is date_local (also in utc BUT different HH:MM or possible day)
42
+ ## note - also in utc, that is, timezone is Z (not +03:00 or such!!!)
43
+
44
+ ## note - use datetime - why? why not?
45
+ ##
46
+ ## note - cut-off/remove seconds
47
+ rec[:datetime_utc] = dateTime.strftime( '%Y-%m-%dT%H:%MZ' )
48
+
49
+ ## note - use 20:30 UTC+1 or 20:30 UTC-3 for timezone format for now
50
+ rec[:datetime_local] = _fmt_date_local( dateTime, localDateTime )
51
+ else
52
+ rec[:date] = dateTime.strftime( '%Y-%m-%d' )
53
+ end
54
+
55
+
56
+
57
+ if m['HomeTeam'] && m['AwayTeam'] ## assume match reports
58
+ team1 = build_team( m['HomeTeam'] )
59
+ team2 = build_team( m['AwayTeam'] )
60
+ ## use full team details here (via lookup)
61
+ ## when report (mode) - why? why not?
62
+ rec[:team1] = team1[:name]
63
+ rec[:team2] = team2[:name]
64
+ else
65
+ team1 = build_team( m['Home'] )
66
+ team2 = build_team( m['Away'] )
67
+ rec[:team1] = team1[:name]
68
+ rec[:team2] = team2[:name]
69
+ end
70
+
71
+
72
+
73
+ #####
74
+ # handle score
75
+ ## m = (full) match hash incl. IdMatch, etc.
76
+ ## returns string e.g. 4-4 or 4-3 a.e.t etc
77
+
78
+ score = _parse_score( m )
79
+ rec[:score] = score unless score.empty?
80
+
81
+
82
+ ## check - never in use?
83
+ rec[:home] = true if m['IsHome']
84
+
85
+
86
+ ## add country to stadium too - why? why not?
87
+ stadium = build_stadium( m['Stadium'] )
88
+
89
+
90
+ if m['HomeTeam'] && m['AwayTeam'] ## assume match reports
91
+ rec[:stadium] = { name: stadium[:name],
92
+ city: stadium[:city],
93
+ street: stadium[:street],
94
+ country: stadium[:country] }
95
+ else
96
+ rec[:stadium] = { name: stadium[:name],
97
+ city: stadium[:city] }
98
+ end
99
+
100
+ attendance = m['Attendance']
101
+ rec[:attendance] = attendance.to_i(10) if attendance
102
+
103
+
104
+ rec
105
+ end
@@ -0,0 +1,135 @@
1
+
2
+
3
+ def _build_report_score( live, timeline=nil )
4
+
5
+ _scores = _build_score_from_goals( live )
6
+
7
+
8
+ score = nil
9
+
10
+ case live['ResultType']
11
+ when 1,4
12
+ ## 1 => 'REGULAR',
13
+ ## 4 => 'REGULAR/AGG', ## aggregate (1st/2nd leg) - regular
14
+ ## add ht
15
+ ## assert ft match
16
+ score = { ht: _scores[:ht][:score],
17
+ ft: _scores[:ft][:score]
18
+ }
19
+ when 3,5,8
20
+ score = { ht: _scores[:ht][:score],
21
+ ft: _scores[:ft][:score],
22
+ et: _scores[:et][:score],
23
+ }
24
+ when 2
25
+ ## todo/fix - pull in timeline check too to check
26
+ ## if any events recorded for extra time!!
27
+ score = { ht: _scores[:ht][:score],
28
+ ft: _scores[:ft][:score],
29
+ }
30
+
31
+ ## note - only incl. extra-time (et) if a goal scored in extra time
32
+ score[:et] = _scores[:et][:score] unless _scores[:et][:minutes].empty?
33
+ end
34
+
35
+ score
36
+ end
37
+
38
+
39
+
40
+ def _build_report( live, timeline=nil )
41
+ rec = {}
42
+
43
+
44
+ ####
45
+ ## add goals
46
+ players = Players.new
47
+ players.add( live['HomeTeam']['Players'] )
48
+ players.add( live['AwayTeam']['Players'] )
49
+
50
+ rec[:goals1] = build_goals( live['HomeTeam']['Goals'], players: players )
51
+ rec[:goals2] = build_goals( live['AwayTeam']['Goals'], players: players )
52
+
53
+
54
+ ##########
55
+ ## add penalty kicks / penalties
56
+ if live['ResultType'] == 2 ## aet, win on pens
57
+ if timeline.nil?
58
+ ##
59
+ team1_code = live['HomeTeam']['Abbreviation']
60
+ team2_code = live['AwayTeam']['Abbreviation']
61
+ puts "!! ERROR - no timeline for match #{team1_code} v #{team2_code} with win on penalties!!"
62
+ exit 1
63
+ else
64
+ ### get timeline with penalty shoot-out details
65
+ ## note - requires team1/2_ids!!!
66
+ team1_id = live['HomeTeam']['IdTeam']
67
+ team2_id = live['AwayTeam']['IdTeam']
68
+
69
+ pens = build_penalties( timeline['Event'],
70
+ players: players,
71
+ team1_id: team1_id,
72
+ team2_id: team2_id )
73
+ pp pens
74
+
75
+ rec[:penalties] = pens
76
+ end
77
+ end
78
+
79
+
80
+ ##
81
+ ## add players by team1/team2
82
+ players1 = Players.new
83
+ players1.add( live['HomeTeam']['Players'] )
84
+
85
+ players2 = Players.new
86
+ players2.add( live['AwayTeam']['Players'] )
87
+
88
+
89
+ rec[:formation1] = live['HomeTeam']['Tactics'] ## e.g. "Tactics": "3-1-4-2"
90
+ rec[:lineup1] = players1.lineup ## starter XI
91
+ rec[:bench1] = players1.bench ## substitutes
92
+
93
+ rec[:formation2] = live['AwayTeam']['Tactics']
94
+ rec[:lineup2] = players2.lineup
95
+ rec[:bench2] = players2.bench
96
+
97
+
98
+ #####
99
+ ## add bookings (yellow/red/red-yellow cards)
100
+ players1.add_bookings( live['HomeTeam']['Bookings'])
101
+ players2.add_bookings( live['AwayTeam']['Bookings'])
102
+
103
+
104
+ yellow1 = players1.yellowcards
105
+ red1 = players1.redcards
106
+ yellowred1 = players1.yellowredcards
107
+ rec[:yellow1] = yellow1 unless yellow1.empty?
108
+ rec[:red1] = red1 unless red1.empty?
109
+ rec[:yellowred1] = yellowred1 unless yellowred1.empty?
110
+
111
+ yellow2 = players2.yellowcards
112
+ red2 = players2.redcards
113
+ yellowred2 = players2.yellowredcards
114
+ rec[:yellow2] = yellow2 unless yellow2.empty?
115
+ rec[:red2] = red2 unless red2.empty?
116
+ rec[:yellowred2] = yellowred2 unless yellowred2.empty?
117
+
118
+
119
+
120
+ ## add substitutions (off/on - in/out)
121
+ rec[:subs1] = build_subs( live['HomeTeam']['Substitutions'], players: players1 )
122
+ rec[:subs2] = build_subs( live['AwayTeam']['Substitutions'], players: players2 )
123
+
124
+
125
+ ## add referees
126
+ officials = build_officials( live['Officials'], id: false )
127
+
128
+ if officials.empty?
129
+ ## puts "!! WARN no refs / officials found"
130
+ else
131
+ rec[:referees] = officials
132
+ end
133
+
134
+ rec
135
+ end
@@ -0,0 +1,114 @@
1
+
2
+
3
+ ##
4
+ ## read & build match report
5
+
6
+ ## use mk/build_report_basename - why? why not?
7
+ ## fix-fix-fix - change to _match_basename !!!
8
+ ## use by (match)report and (match)timeline !!!
9
+ def _report_basename( m )
10
+ ### get match (live) details
11
+ team1_code = m['Home']['Abbreviation']
12
+ team2_code = m['Away']['Abbreviation']
13
+ localDateTime = parse_date_utc( m['LocalDate'] )
14
+
15
+ ## e.g. 2020-06-24_RAP-AUS__54545454
16
+ basename = String.new
17
+ basename += localDateTime.strftime('%Y-%m-%d') ## i) date
18
+ basename += "_#{team1_code}-#{team2_code}" ## ii) team1 v team2
19
+
20
+ basename
21
+ end
22
+
23
+
24
+
25
+ def _read_timeline( m, timeline_dir: )
26
+ return nil if m['Home'].nil? || m['Away'].nil?
27
+
28
+ basename = _report_basename( m )
29
+ basename += "__#{m['IdMatch']}"
30
+
31
+ path = "#{timeline_dir}/#{basename}.json"
32
+
33
+ ## note - skip if no match report available!!!
34
+ if File.file?( path )
35
+ timeline = read_json_v2( path )
36
+ timeline
37
+ else
38
+ nil
39
+ end
40
+ end
41
+
42
+ def _read_report( m, report_dir: )
43
+ return nil if m['Home'].nil? || m['Away'].nil?
44
+
45
+ ## e.g. 2020-06-24_RAP-AUS__54545454
46
+ # note - add match id for "raw" match reports!!!
47
+ basename = _report_basename( m )
48
+ basename += "__#{m['IdMatch']}"
49
+
50
+ ## #{indir}/#{slug}/matches/#{season.to_path}
51
+ path = "#{report_dir}/#{basename}.json"
52
+
53
+ ## note - skip if no match report available!!!
54
+ if File.file?( path )
55
+ live = read_json_v2( path )
56
+ live
57
+ else
58
+ nil
59
+ end
60
+ end
61
+
62
+
63
+
64
+
65
+
66
+
67
+ def convert_reports( slug:, season:,
68
+ indir: '.',
69
+ outdir: './tmp' )
70
+
71
+ season = Season(season)
72
+
73
+ data = read_json_v2( "#{indir}/#{slug}/#{season.to_path}_matches.json" )
74
+ matches = data['Results'] ## only use results (match) array
75
+ puts " #{matches.size} match(es) in season #{season}"
76
+
77
+
78
+ report_dir = "#{indir}/#{slug}/matches/#{season.to_path}"
79
+ timeline_dir = "#{indir}/#{slug}/timelines/#{season.to_path}"
80
+
81
+
82
+
83
+ matches.each_with_index do |m, i|
84
+ live = _read_report( m, report_dir: report_dir )
85
+
86
+ ## no report found; continue
87
+ next if live.nil?
88
+
89
+ ## check for optional timeline
90
+ timeline = _read_timeline( m, timeline_dir: timeline_dir )
91
+
92
+ rec = {
93
+ meta: {
94
+ name: desc(live['SeasonName']),
95
+ slug: slug,
96
+ season: season.to_s,
97
+ generated: Time.now.to_s,
98
+ }
99
+ }.merge( _build_match( live ),
100
+ _build_report( live, timeline ))
101
+
102
+ ## try update of score via goals from report
103
+ score_more = _build_report_score( live, timeline )
104
+ if score_more
105
+ rec[:score] = rec[:score].merge( score_more )
106
+ end
107
+
108
+
109
+ ## build basename e.g 2026-07-15_ARG-ENG
110
+ basename = _report_basename( m )
111
+ outpath = "#{outdir}/#{season.to_path}/#{slug}/#{basename}.json"
112
+ write_json( outpath, rec )
113
+ end
114
+ end
@@ -0,0 +1,163 @@
1
+
2
+
3
+
4
+ def convert( slug:, season:,
5
+ indir: '.',
6
+ outdir: './tmp' )
7
+
8
+ season = Season(season)
9
+
10
+ ## fix - change data to top or ??? - why? why not?
11
+ data = {}
12
+
13
+ ## add slug & seasons (add name to be done!!)
14
+ data[:meta] = { slug: slug,
15
+ season: season.to_s,
16
+ generated: Time.now.to_s,
17
+ }
18
+
19
+
20
+ matches = read_json_v2( "#{indir}/#{slug}/#{season.to_path}_matches.json" )
21
+ matches = matches['Results'] ## only use results (match) array
22
+
23
+ ## pp matches
24
+ puts " #{matches.size} match(es) in season #{season}"
25
+
26
+
27
+ ## read in stages
28
+ ## incl. SequenceOrder, StageLevel (optional)
29
+ stages = Stages.read( "#{indir}/#{slug}/misc/#{season.to_path}_stages.json" )
30
+
31
+
32
+ stages.add_matches( matches )
33
+ data[:stages] = stages.as_json
34
+
35
+
36
+ teams = Teams.new
37
+ teams.add_matches( matches )
38
+ data[:teams] = teams.as_json
39
+
40
+
41
+ stadiums = Stadiums.new
42
+ stadiums.add_matches( matches )
43
+ data[:stadiums] = stadiums.as_json
44
+
45
+
46
+ ## for match-by-match live reports
47
+ report_dir = "#{indir}/#{slug}/matches/#{season.to_path}"
48
+ timeline_dir = "#{indir}/#{slug}/timelines/#{season.to_path}"
49
+
50
+
51
+ recs = []
52
+ matches.each_with_index do |m, i|
53
+
54
+ ## add/track id too - why? why not?
55
+ ## rec[:id] = m['IdMatch']
56
+
57
+
58
+ ## add/fill-up match basic
59
+ rec = _build_match( m )
60
+
61
+
62
+ ### get match (live) details
63
+ ###
64
+ ## check if match report exits
65
+ ## optional for now!!
66
+
67
+ live = _read_report( m, report_dir: report_dir )
68
+
69
+
70
+ ## if live.nil?
71
+ ## puts "warn no match report for #{_report_basename(m)}"
72
+ ## end
73
+
74
+ if live
75
+ ## check for timeline
76
+ timeline = _read_timeline( m, timeline_dir: timeline_dir )
77
+ ## reuse generated output from report
78
+ report = _build_report( live, timeline )
79
+
80
+ ## try update of score via goals from (match) report
81
+ score_more = _build_report_score( live, timeline )
82
+ if score_more
83
+ rec[:score] = rec[:score].merge( score_more )
84
+ end
85
+
86
+
87
+
88
+ goals1 = report[:goals1]
89
+ goals2 = report[:goals2]
90
+
91
+
92
+ if goals1.empty? && goals2.empty?
93
+ ## skip if no goals
94
+ else
95
+ rec[:goals1] = goals1
96
+ rec[:goals2] = goals2
97
+
98
+
99
+
100
+ end
101
+
102
+
103
+ #########
104
+ ## add penalties
105
+ penalties = report[:penalties]
106
+ rec[:penalties] = penalties if penalties && !penalties.empty?
107
+
108
+
109
+ sentoff1 = (report[:red1]||[]) + (report[:yellowred1]||[])
110
+ sentoff2 = (report[:red2]||[]) + (report[:yellowred2]||[])
111
+
112
+ ## sort by minute
113
+ sentoff1 = sentoff1.sort { |l,r| l[:minute] <=> r[:minute] }
114
+ sentoff2 = sentoff2.sort { |l,r| l[:minute] <=> r[:minute] }
115
+
116
+ rec[:sentoff1] = sentoff1 unless sentoff1.empty?
117
+ rec[:sentoff2] = sentoff2 unless sentoff2.empty?
118
+ end
119
+
120
+
121
+
122
+ =begin
123
+ "Officials":
124
+ [{"IdCountry": "BRA",
125
+ "OfficialId": "361561",
126
+ "NameShort": [{"Locale": "en-GB", "Description": "Wilton SAMPAIO"}],
127
+ "Name": [{"Locale": "en-GB", "Description": "Wilton SAMPAIO"}],
128
+ "OfficialType": 1,
129
+ "TypeLocalized": [{"Locale": "en-GB", "Description": "Referee"}]},
130
+ {"IdCountry": "PAR",
131
+ "OfficialId": "416159",
132
+ "NameShort":
133
+ [{"Locale": "en-GB", "Description": "Juan Gabriel BENITEZ"}],
134
+ "Name": [{"Locale": "en-GB", "Description": "Juan Gabriel Benítez"}],
135
+ "OfficialType": 4,
136
+ "TypeLocalized":
137
+ [{"Locale": "en-GB", "Description": "Fourth official"}]}],
138
+ =end
139
+
140
+ ###
141
+ ## add referees
142
+ officials = build_officials( m['Officials'], id: false )
143
+
144
+ if officials.empty?
145
+ ## puts "!! WARN no refs / officials found"
146
+ else
147
+ rec[:referees] = officials
148
+ end
149
+
150
+
151
+
152
+
153
+ recs << rec
154
+ end
155
+ data[:matches] = recs
156
+
157
+
158
+ outpath = "#{outdir}/#{season.to_path}/#{slug}.json"
159
+ write_json( outpath, data)
160
+ puts " written to >#{outpath}<"
161
+
162
+ true
163
+ end
@@ -0,0 +1,58 @@
1
+
2
+
3
+ def _build_score_from_goals( m )
4
+ ##
5
+ ## 5 periods - 3 (1ST_HALF), 5 (2ND_HALF),
6
+ ## 7 (EXTRA_TIME_1ST_HALF), 9 (EXTRA_TIME_2ND_HALF),
7
+ ## 11 (PENALTY_SHOOTOUT) possibly
8
+ ##
9
+ goals = {
10
+ count: 0,
11
+ score: [0,0],
12
+ ht: { score: [0,0], minutes: [] },
13
+ ft: { score: [0,0], minutes: [] },
14
+ et: { score: [0,0], minutes: [] },
15
+ p: { score: [0,0], minutes: [] },
16
+ }
17
+
18
+ ## note - quick fix
19
+ ## add period 2 for USA v Belgium (PKO) - worldcup
20
+
21
+ ## note - i is 0|1 - array index for team
22
+ [m['HomeTeam']['Goals'],
23
+ m['AwayTeam']['Goals']].each_with_index do |recs,i|
24
+ recs.each do |h|
25
+
26
+ h = errata_autofix_goal( h )
27
+
28
+ period = h['Period']
29
+ ## include penalty shootout (11) - why? why not?
30
+ assert( [3,5,7,9,11].include?(period),
31
+ "goal in period 3/5/7/9/11 expected; got #{h.pretty_inspect} in match #{m.pretty_inspect}" )
32
+
33
+ minute = h['Minute']
34
+ key = case period
35
+ when 3 then :ht
36
+ when 5 then :ft
37
+ when 7, 9 then :et
38
+ when 11 then :p
39
+ else
40
+ raise ArgumentError,
41
+ "goal in period 3/5/7/9/11 expected; got #{h.pretty_inspect}"
42
+ end
43
+
44
+ goals[:count] += 1
45
+ goals[:score][i] += 1 unless key == :p
46
+ goals[key][:score][i] += 1
47
+ end
48
+ end
49
+
50
+ ## use/calc cummulative score (BUT not for penalties)
51
+ goals[:ft][:score][0] += goals[:ht][:score][0]
52
+ goals[:ft][:score][1] += goals[:ht][:score][1]
53
+
54
+ goals[:et][:score][0] += goals[:ft][:score][0]
55
+ goals[:et][:score][1] += goals[:ft][:score][1]
56
+
57
+ goals
58
+ end
@@ -0,0 +1,89 @@
1
+
2
+
3
+
4
+
5
+ def build_goal( h, players: )
6
+
7
+ ## split into minute
8
+ ## and offset (stoppage/injury time)
9
+ ## e.g. 90'+11'
10
+
11
+ h = errata_autofix_goal( h )
12
+
13
+ minute_str = h['Minute']
14
+
15
+ period = h['Period']
16
+
17
+ ## note - penalty shootout gets excluded upstream!!
18
+ ## include penalty shootout (11) - why? why not?
19
+ ## assert( [3,5,7,9].include?(period),
20
+ ## "goal in period 3/5/7/9 expected; got #{h.pretty_inspect}" )
21
+
22
+ ## quick fix:
23
+ ## check for weird minute 0 e.g.
24
+ ## Germany-Austria 1934
25
+ if minute_str == "0'"
26
+ h['Minute'] = "1'"
27
+ end
28
+
29
+ if minute_str.nil? || minute_str.empty?
30
+ puts "!! minute in goal is nil or empty:"
31
+ pp h
32
+ exit 1
33
+ end
34
+
35
+ minute = _build_minute( h )
36
+
37
+
38
+ rec = {}
39
+
40
+ idPlayer = h['IdPlayer']
41
+
42
+ if idPlayer.nil?
43
+ puts "!! no idPlayer for goal!"
44
+ pp h
45
+ ##exit 1
46
+ ## use 'N.N.'
47
+
48
+ rec[ :name ] = 'N.N.'
49
+
50
+ else
51
+ player = players.find!( idPlayer )
52
+ rec[ :name ] = player[ :name ]
53
+ end
54
+
55
+ rec[ :minute] = minute
56
+
57
+
58
+ ## check for goal type (og) or (p)
59
+ ## 1 - "penalty"
60
+ ## 2 - "regular"
61
+ ## 3 - "own goal"
62
+
63
+ type = h['Type']
64
+ rec[ :pen ] = true if type == 1
65
+ rec[ :og ] = true if type == 3
66
+
67
+ rec
68
+ end
69
+
70
+
71
+
72
+
73
+ def build_goals( recs, players:, penalties: false )
74
+
75
+ ## note - filter out penalties (from shoot-out)!!
76
+ ## min > 120 (e.g. 121, etc.)
77
+ ## note - use period (more reliable)
78
+ ## period 11 is PENALTY_SHOOTOUT!!
79
+ if penalties == false
80
+ recs = recs.select { |rec| rec['Period'] != 11 }
81
+ end
82
+
83
+
84
+ recs = recs.map { |h| build_goal( h, players: players ) }
85
+
86
+ ## note - sort by minutes; goals may not be sorted
87
+ recs = recs.sort { |l,r| l[:minute] <=> r[:minute] }
88
+ recs
89
+ end
@@ -0,0 +1,13 @@
1
+
2
+
3
+ def _fmt_date_local( date_utc, date_local )
4
+
5
+ ## note: returns Rational (e.g. 3/1 or 1/4 etc.) use to_f/to_i to convert
6
+ diff_in_hours = ((date_local - date_utc) * 24).to_f
7
+ diff_in_days = date_local.jd - date_utc.jd
8
+ ## pp [diff_in_hours, diff_in_days]
9
+
10
+
11
+ ## use 20:30 UTC+1 or 20:30 UTC-3
12
+ "#{date_local.strftime( '%Y-%m-%d %H:%M' )} UTC%+d" % diff_in_hours
13
+ end