fifadat-convert 0.0.1 → 0.1.0

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: 98a8e2d8a9d493086e5b0758df77f1c372437961fadb6fdbf30a715b50316af7
4
- data.tar.gz: 1efd005037d164e2ffdf71f677616d0427a558ee076654128d8e6d2047f27498
3
+ metadata.gz: 72f197ccce455bdc5de862831d320aabaa06f5ed2665e8496ec324573135398c
4
+ data.tar.gz: 6e2d54b61c281bcbc8d812e8d7a7bc562811a4deb6663d6e3cf6a73c68cc0b60
5
5
  SHA512:
6
- metadata.gz: 1d01a876f48d6a96ddecc640d90daeb1b507ed88e6f513510bfdbba5a0154c05c3502fe5354b6129908e6f3547853897ab09661683c1d7c99d3440c42dec6d26
7
- data.tar.gz: f98f022b43b2b0483fe66446a95fa64f9306a9d2be623759e8a2b93a0664c23f584e3900b340e0b4bb54bbeca7b42d0edb03762ce91903c25763a2906b9b7ced
6
+ metadata.gz: 1c5aca3857ba0409c4070845fcb03dd812cbabb011689108437776ffe24e5d3934dc9145b55fb5895db47f26b17f2f6189f67ef4828a41a9fde799e01c2d0a2e
7
+ data.tar.gz: 340cbd14fd0b74db8af3144426b6ca857b1acf2d79911495e33e82589dd28c48e6ba9c659bffae4799c93df0debd271d70dd0e9dd9bd97e97af6bfd5ad144c9b
data/CHANGELOG.md CHANGED
@@ -1,4 +1,5 @@
1
1
 
2
+ ### 0.1.0
2
3
  ### 0.0.1 / 2026-09-24
3
4
 
4
5
  * Everything is new. First release.
data/Manifest.txt CHANGED
@@ -4,24 +4,28 @@ README.md
4
4
  Rakefile
5
5
  bin/fifadat
6
6
  lib/fifadat/convert.rb
7
- lib/fifadat/convert/build_match.rb
8
- lib/fifadat/convert/build_report.rb
9
7
  lib/fifadat/convert/convert-reports.rb
10
8
  lib/fifadat/convert/convert.rb
11
9
  lib/fifadat/convert/errata.rb
12
- lib/fifadat/convert/goals-calc_score.rb
13
- lib/fifadat/convert/goals.rb
14
10
  lib/fifadat/convert/helper-date.rb
15
11
  lib/fifadat/convert/helper-minute.rb
16
12
  lib/fifadat/convert/helper-score.rb
17
13
  lib/fifadat/convert/norm.rb
18
- lib/fifadat/convert/officials.rb
19
- lib/fifadat/convert/penalties.rb
20
- lib/fifadat/convert/players.rb
21
- lib/fifadat/convert/stadiums.rb
22
- lib/fifadat/convert/stages.rb
23
- lib/fifadat/convert/substitutions.rb
24
- lib/fifadat/convert/teams.rb
14
+ lib/fifadat/convert/reader.rb
25
15
  lib/fifadat/convert/tool.rb
26
16
  lib/fifadat/convert/version.rb
17
+ lib/fifadat/models/goal.rb
18
+ lib/fifadat/models/match.rb
19
+ lib/fifadat/models/match_report.rb
20
+ lib/fifadat/models/official.rb
21
+ lib/fifadat/models/penalty.rb
22
+ lib/fifadat/models/player.rb
23
+ lib/fifadat/models/stadium.rb
24
+ lib/fifadat/models/stage.rb
25
+ lib/fifadat/models/substitution.rb
26
+ lib/fifadat/models/team.rb
27
+ lib/fifadat/tables/players.rb
28
+ lib/fifadat/tables/stadiums.rb
29
+ lib/fifadat/tables/stages.rb
30
+ lib/fifadat/tables/teams.rb
27
31
  lib/fifadata-convert.rb
@@ -3,84 +3,54 @@
3
3
  ##
4
4
  ## read & build match report
5
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
6
 
7
+ def convert_reports( slug:, season:,
8
+ indir: '.',
9
+ outdir: './tmp' )
23
10
 
11
+ season = Season(season)
24
12
 
25
- def _read_timeline( m, timeline_dir: )
26
- return nil if m['Home'].nil? || m['Away'].nil?
13
+ data = read_json_v2( "#{indir}/#{slug}/#{season.to_path}_matches.json" )
14
+ matches = data['Results'] ## only use results (match) array
15
+ puts "==> convert reports - #{matches.size} match(es) in #{slug} #{season}"
27
16
 
28
- basename = _report_basename( m )
29
- basename += "__#{m['IdMatch']}"
30
17
 
31
- path = "#{timeline_dir}/#{basename}.json"
18
+ ## read in stages
19
+ ## incl. SequenceOrder, StageLevel (optional)
20
+ stages = Stages.read( "#{indir}/#{slug}/misc/#{season.to_path}_stages.json" )
21
+ stages.add_matches( matches )
32
22
 
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
23
+ teams = Teams.new
24
+ teams.add_matches( matches )
41
25
 
42
- def _read_report( m, report_dir: )
43
- return nil if m['Home'].nil? || m['Away'].nil?
26
+ stadiums = Stadiums.new
27
+ stadiums.add_matches( matches )
44
28
 
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
29
 
62
30
 
63
31
 
32
+ report_dir = "#{indir}/#{slug}/matches/#{season.to_path}"
33
+ timeline_dir = "#{indir}/#{slug}/timelines/#{season.to_path}"
64
34
 
65
35
 
66
36
 
67
- def convert_reports( slug:, season:,
68
- indir: '.',
69
- outdir: './tmp' )
70
-
71
- season = Season(season)
37
+ matches.each_with_index do |m, i|
72
38
 
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}"
39
+ print "."
40
+ print i+1 if (i+1) %10 == 0
76
41
 
77
42
 
78
- report_dir = "#{indir}/#{slug}/matches/#{season.to_path}"
79
- timeline_dir = "#{indir}/#{slug}/timelines/#{season.to_path}"
43
+ ## note skip if SCHEDULED == 1
44
+ ## 0 => FINISHED/complete (OK)
45
+ ## 1 => SCHEDULED/not yet played
46
+ ## 2 => LIVE
47
+ ## 7 => POSTPONED
80
48
 
49
+ next if m['MatchStatus'] == 1 ||
50
+ m['MatchStatus'] == 2 ||
51
+ m['MatchStatus'] == 7
81
52
 
82
53
 
83
- matches.each_with_index do |m, i|
84
54
  live = _read_report( m, report_dir: report_dir )
85
55
 
86
56
  ## no report found; continue
@@ -89,26 +59,38 @@ def convert_reports( slug:, season:,
89
59
  ## check for optional timeline
90
60
  timeline = _read_timeline( m, timeline_dir: timeline_dir )
91
61
 
92
- rec = {
62
+
63
+ rec = Match.build( live, teams: teams,
64
+ stadiums: stadiums,
65
+ stages: stages )
66
+
67
+
68
+ ## try update of score via goals from (match) report
69
+ score_more = _build_report_score( live, timeline )
70
+ if score_more
71
+ rec.score = {}.merge( rec.score||{}, score_more )
72
+ end
73
+
74
+
75
+ report = MatchReport.build( live, timeline )
76
+
77
+ data = {
93
78
  meta: {
94
79
  name: desc(live['SeasonName']),
95
80
  slug: slug,
96
81
  season: season.to_s,
97
82
  generated: Time.now.to_s,
98
83
  }
99
- }.merge( _build_match( live ),
100
- _build_report( live, timeline ))
84
+ }
101
85
 
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
86
+ data = data.merge( rec.as_json, report.as_json )
107
87
 
108
88
 
109
89
  ## build basename e.g 2026-07-15_ARG-ENG
110
90
  basename = _report_basename( m )
111
91
  outpath = "#{outdir}/#{season.to_path}/#{slug}/#{basename}.json"
112
- write_json( outpath, rec )
92
+ write_json( outpath, data )
113
93
  end
94
+
95
+ print "DONE\n"
114
96
  end
@@ -5,44 +5,49 @@ def convert( slug:, season:,
5
5
  indir: '.',
6
6
  outdir: './tmp' )
7
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
- }
8
+ season = Season(season)
18
9
 
19
10
 
11
+ ### fix-fix-fix change to read_json_results or ??? - why? why not?
20
12
  matches = read_json_v2( "#{indir}/#{slug}/#{season.to_path}_matches.json" )
21
13
  matches = matches['Results'] ## only use results (match) array
22
14
 
23
15
  ## 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" )
16
+ puts "==> convert - #{matches.size} match(es) in #{slug} #{season}"
30
17
 
31
18
 
32
- stages.add_matches( matches )
33
- data[:stages] = stages.as_json
34
-
19
+ ## read in stages
20
+ ## incl. SequenceOrder, StageLevel (optional)
21
+ stages = Stages.read( "#{indir}/#{slug}/misc/#{season.to_path}_stages.json" )
22
+ stages.add_matches( matches )
35
23
 
36
24
  teams = Teams.new
37
25
  teams.add_matches( matches )
38
- data[:teams] = teams.as_json
39
-
40
26
 
41
27
  stadiums = Stadiums.new
42
28
  stadiums.add_matches( matches )
29
+
30
+
31
+
32
+ data = {}
33
+
34
+ ## add slug & seasons (add name to be done!!)
35
+ data[:meta] = { slug: slug,
36
+ season: season.to_s,
37
+ generated: Time.now.to_s,
38
+ version: FifadatConvert::VERSION,
39
+ teams: teams.size,
40
+ matches: matches.size,
41
+ stages: stages.size,
42
+ stadiums: stadiums.size,
43
+ }
44
+
45
+ data[:stages] = stages.as_json
46
+ data[:teams] = teams.as_json
43
47
  data[:stadiums] = stadiums.as_json
44
48
 
45
49
 
50
+
46
51
  ## for match-by-match live reports
47
52
  report_dir = "#{indir}/#{slug}/matches/#{season.to_path}"
48
53
  timeline_dir = "#{indir}/#{slug}/timelines/#{season.to_path}"
@@ -56,112 +61,78 @@ def convert( slug:, season:,
56
61
 
57
62
 
58
63
  ## add/fill-up match basic
59
- rec = _build_match( m )
60
-
64
+ rec = Match.build( m, teams: teams,
65
+ stadiums: stadiums,
66
+ stages: stages )
61
67
 
62
- ### get match (live) details
63
68
  ###
64
- ## check if match report exits
65
- ## optional for now!!
69
+ ## note - skip check for match events / goals etc.
70
+ ## if not yet played!!!
71
+ # 1-scheduled and
72
+ # 2-live
73
+ # 7-postponed (too)
74
+ # return empty score!!!
75
+ if !(m['MatchStatus'] == 1 ||
76
+ m['MatchStatus'] == 2 ||
77
+ m['MatchStatus'] == 7)
78
+
66
79
 
67
- live = _read_report( m, report_dir: report_dir )
68
80
 
81
+ ### get match (live) details
82
+ ###
83
+ ## check if match report & timeline exits
84
+ ## optional for now!!
85
+
86
+ live = _read_report( m, report_dir: report_dir )
87
+ timeline = _read_timeline( m, timeline_dir: timeline_dir )
69
88
 
70
89
  ## if live.nil?
71
90
  ## puts "warn no match report for #{_report_basename(m)}"
72
91
  ## end
73
92
 
74
93
 
75
- ###
76
- ## note - skip check for match events / goals etc.
77
- ## if not yet played!!!
78
-
79
- if live && !(rec[:status] == 'TIMED' ||
80
- rec[:status] == 'SCHED' || rec[:status] == 'SCHEDULED' ||
81
- rec[:status] == 'LIVE')
82
-
83
-
84
- ## check for timeline
85
- timeline = _read_timeline( m, timeline_dir: timeline_dir )
86
- ## reuse generated output from report
87
- report = _build_report( live, timeline )
88
-
89
94
  ## try update of score via goals from (match) report
90
95
  score_more = _build_report_score( live, timeline )
91
96
  if score_more
92
- rec[:score] = rec[:score].merge( score_more )
97
+ rec.score = {}.merge( rec.score||{}, score_more )
93
98
  end
94
99
 
95
100
 
101
+ ## reuse generated output from report
102
+ report = MatchReport.build( live, timeline )
96
103
 
97
- goals1 = report[:goals1]
98
- goals2 = report[:goals2]
99
-
104
+ ############
105
+ ## add goals
100
106
 
101
- if goals1.empty? && goals2.empty?
107
+ if report.goals1.empty? && report.goals2.empty?
102
108
  ## skip if no goals
103
109
  else
104
- rec[:goals1] = goals1
105
- rec[:goals2] = goals2
106
-
107
-
108
-
110
+ rec.goals1 = report.goals1
111
+ rec.goals2 = report.goals2
109
112
  end
110
113
 
111
114
 
112
115
  #########
113
- ## add penalties
114
- penalties = report[:penalties]
115
- rec[:penalties] = penalties if penalties && !penalties.empty?
116
-
117
-
118
- sentoff1 = (report[:red1]||[]) + (report[:yellowred1]||[])
119
- sentoff2 = (report[:red2]||[]) + (report[:yellowred2]||[])
120
-
121
- ## sort by minute
122
- sentoff1 = sentoff1.sort { |l,r| l[:minute] <=> r[:minute] }
123
- sentoff2 = sentoff2.sort { |l,r| l[:minute] <=> r[:minute] }
116
+ ## add penalties
117
+ rec.penalties = report.penalties if report.penalties &&
118
+ !report.penalties.empty?
124
119
 
125
- rec[:sentoff1] = sentoff1 unless sentoff1.empty?
126
- rec[:sentoff2] = sentoff2 unless sentoff2.empty?
127
- end
128
-
129
-
130
-
131
- =begin
132
- "Officials":
133
- [{"IdCountry": "BRA",
134
- "OfficialId": "361561",
135
- "NameShort": [{"Locale": "en-GB", "Description": "Wilton SAMPAIO"}],
136
- "Name": [{"Locale": "en-GB", "Description": "Wilton SAMPAIO"}],
137
- "OfficialType": 1,
138
- "TypeLocalized": [{"Locale": "en-GB", "Description": "Referee"}]},
139
- {"IdCountry": "PAR",
140
- "OfficialId": "416159",
141
- "NameShort":
142
- [{"Locale": "en-GB", "Description": "Juan Gabriel BENITEZ"}],
143
- "Name": [{"Locale": "en-GB", "Description": "Juan Gabriel Benítez"}],
144
- "OfficialType": 4,
145
- "TypeLocalized":
146
- [{"Locale": "en-GB", "Description": "Fourth official"}]}],
147
- =end
148
-
149
- ###
150
- ## add referees
151
- officials = build_officials( m['Officials'], id: false )
152
-
153
- if officials.empty?
154
- ## puts "!! WARN no refs / officials found"
155
- else
156
- rec[:referees] = officials
157
- end
158
120
 
121
+ sentoff1 = report.sentoff1
122
+ sentoff2 = report.sentoff2
123
+ rec.sentoff1 = sentoff1 unless sentoff1.empty?
124
+ rec.sentoff2 = sentoff2 unless sentoff2.empty?
159
125
 
126
+ ###
127
+ ## add referees
128
+ rec.officials = report.officials if report.officials && !report.officials.empty?
129
+ end
160
130
 
161
131
 
162
132
  recs << rec
163
133
  end
164
- data[:matches] = recs
134
+ data[:matches] = recs.as_json
135
+
165
136
 
166
137
 
167
138
  outpath = "#{outdir}/#{season.to_path}/#{slug}.json"
@@ -11,3 +11,15 @@ def _fmt_date_local( date_utc, date_local )
11
11
  ## use 20:30 UTC+1 or 20:30 UTC-3
12
12
  "#{date_local.strftime( '%Y-%m-%d %H:%M' )} UTC%+d" % diff_in_hours
13
13
  end
14
+
15
+ def _fmt_time_local( date_utc, date_local )
16
+
17
+ ## note: returns Rational (e.g. 3/1 or 1/4 etc.) use to_f/to_i to convert
18
+ diff_in_hours = ((date_local - date_utc) * 24).to_f
19
+ diff_in_days = date_local.jd - date_utc.jd
20
+ ## pp [diff_in_hours, diff_in_days]
21
+
22
+
23
+ ## use 20:30 UTC+1 or 20:30 UTC-3
24
+ "#{date_local.strftime( '%H:%M' )} UTC%+d" % diff_in_hours
25
+ end
@@ -1,4 +1,20 @@
1
1
 
2
+
3
+ def _parse_score( m )
4
+ h = { }
5
+
6
+ ###
7
+ # note: for match status
8
+ # 1-scheduled and
9
+ # 2-live
10
+ # 7-postponed
11
+ # return empty score!!!
12
+
13
+ return h if m['MatchStatus'] == 1 ||
14
+ m['MatchStatus'] == 2 ||
15
+ m['MatchStatus'] == 7
16
+
17
+
2
18
  #
3
19
  # add quick fix for club world cup?!
4
20
  # elsif resultType == 1 || ## assume 1 - regular (90 mins+stoppage/injury time)
@@ -6,14 +22,13 @@
6
22
  # m['IdMatch'] == '400019191' ## fix for pachuca vs salzburg !!!
7
23
  # has resultType == 0!!!
8
24
 
9
-
10
- def _parse_score( m )
11
- h = { }
12
-
13
25
  m = errata_autofix_score( m )
14
26
 
15
27
 
16
28
  resultType = m['ResultType']
29
+ ## add score[:type] - why? why not?
30
+ h[:type] = RESULT_TYPE[resultType] || "???-#{resultType}"
31
+
17
32
 
18
33
  score = if m['HomeTeam'] && m['AwayTeam'] ## assume inside (match) report
19
34
  [m['HomeTeam']['Score'], m['AwayTeam']['Score']].compact
@@ -72,68 +87,96 @@ end
72
87
 
73
88
 
74
89
 
75
- def _fmt_score( m )
90
+ def _build_report_score( live, timeline=nil )
76
91
 
77
- m = errata_autofix_score( m )
92
+ _scores = _build_score_from_goals( live )
78
93
 
79
- ## m = (full) match hash incl. IdMatch, etc.
80
- ## returns string e.g. 4-4 or 4-3 a.e.t etc
81
94
 
82
- resultType = m['ResultType']
95
+ score = nil
83
96
 
84
- # resultType
85
- # 0 => no result / not played yet
86
- # 1 => regular (90 mins)
87
- # 2 => aet (120 mins), win on pens
88
- # 3 => aet (120 mins)
89
- # 8 => same as 3? -aet with golden goal/silver goal in 1998 FRA-PAR
90
-
91
- score = if resultType == 2 ## aet, win on pens
92
- "#{m['HomeTeamScore']}-#{m['AwayTeamScore']}" +
93
- " a.e.t., #{m['HomeTeamPenaltyScore']}-#{m['AwayTeamPenaltyScore']} pen."
94
- elsif resultType == 3 || resultType == 8 ## aet
95
- "#{m['HomeTeamScore']}-#{m['AwayTeamScore']} a.e.t."
96
- elsif resultType == 1 || ## assume 1 - regular (90 mins+stoppage/injury time)
97
- resultType == 4
98
- "#{m['HomeTeamScore']}-#{m['AwayTeamScore']}"
99
- elsif resultType == 0
100
- ## double check if score present
101
- ## e.g. pachuca vs salzburg in cwc 2025??
102
- raise ArgumentError,
103
- " resultType == 0 but score present idMatch #{m['IdMatch']} #{m['HomeTeamScore']}-#{m['AwayTeamScore']}" if m['HomeTeamScore'] &&
104
- m['AwayTeamScore']
105
- ""
106
- else
107
- raise ArgumentError, "unknown/unexpected result type #{resultType}"
108
- end
109
-
110
- score
111
- end
97
+ case live['ResultType']
98
+ when 1,4
99
+ ## 1 => 'REGULAR',
100
+ ## 4 => 'REGULAR/AGG', ## aggregate (1st/2nd leg) - regular
101
+ ## add ht
102
+ ## assert ft match
103
+ score = { ht: _scores[:ht][:score],
104
+ ft: _scores[:ft][:score]
105
+ }
106
+ when 3,5,8
107
+ score = { ht: _scores[:ht][:score],
108
+ ft: _scores[:ft][:score],
109
+ et: _scores[:et][:score],
110
+ }
111
+ when 2
112
+ ## todo/fix - pull in timeline check too to check
113
+ ## if any events recorded for extra time!!
114
+ score = { ht: _scores[:ht][:score],
115
+ ft: _scores[:ft][:score],
116
+ }
112
117
 
118
+ ## note - only incl. extra-time (et) if a goal scored in extra time
119
+ score[:et] = _scores[:et][:score] unless _scores[:et][:minutes].empty?
120
+ end
121
+
122
+ score
123
+ end
113
124
 
114
125
 
115
126
 
116
- __END__
117
-
118
- score = if resultType == 2 ## aet, win on pens
119
- { et: [m['HomeTeamScore'],m['AwayTeamScore']],
120
- p: [m['HomeTeamPenaltyScore'],m['AwayTeamPenaltyScore']] }
121
- elsif resultType == 3 || resultType == 8 ## aet
122
- { et: [m['HomeTeamScore'], m['AwayTeamScore']] }
123
- elsif resultType == 1 || ## assume 1 - regular (90 mins+stoppage/injury time)
124
- resultType == 4 ||
125
- m['IdMatch'] == '400019191' ## fix for pachuca vs salzburg !!!
126
- { ft: [m['HomeTeamScore'],m['AwayTeamScore']] }
127
- elsif resultType == 12
128
- ## fix/fix/fix - check for score too
129
- nil
130
- elsif resultType == 0
131
- ## pachuca vs salzburg in cwc 2025??
132
- ## double check if score present
133
- raise ArgumentError,
134
- " resultType == 0 but score present idMatch #{m['IdMatch']} #{m['HomeTeamScore']}-#{m['AwayTeamScore']}" if m['HomeTeamScore'] &&
135
- m['AwayTeamScore']
136
- nil
137
- else
138
- raise ArgumentError, "unknown/unexpected result type #{resultType}"
139
- end
127
+ def _build_score_from_goals( m )
128
+ ##
129
+ ## 5 periods - 3 (1ST_HALF), 5 (2ND_HALF),
130
+ ## 7 (EXTRA_TIME_1ST_HALF), 9 (EXTRA_TIME_2ND_HALF),
131
+ ## 11 (PENALTY_SHOOTOUT) possibly
132
+ ##
133
+ goals = {
134
+ count: 0,
135
+ score: [0,0],
136
+ ht: { score: [0,0], minutes: [] },
137
+ ft: { score: [0,0], minutes: [] },
138
+ et: { score: [0,0], minutes: [] },
139
+ p: { score: [0,0], minutes: [] },
140
+ }
141
+
142
+ ## note - quick fix
143
+ ## add period 2 for USA v Belgium (PKO) - worldcup
144
+
145
+ ## note - i is 0|1 - array index for team
146
+ [m['HomeTeam']['Goals'],
147
+ m['AwayTeam']['Goals']].each_with_index do |recs,i|
148
+ recs.each do |h|
149
+
150
+ h = errata_autofix_goal( h )
151
+
152
+ period = h['Period']
153
+ ## include penalty shootout (11) - why? why not?
154
+ assert( [3,5,7,9,11].include?(period),
155
+ "goal in period 3/5/7/9/11 expected; got #{h.pretty_inspect} in match #{m.pretty_inspect}" )
156
+
157
+ minute = h['Minute']
158
+ key = case period
159
+ when 3 then :ht
160
+ when 5 then :ft
161
+ when 7, 9 then :et
162
+ when 11 then :p
163
+ else
164
+ raise ArgumentError,
165
+ "goal in period 3/5/7/9/11 expected; got #{h.pretty_inspect}"
166
+ end
167
+
168
+ goals[:count] += 1
169
+ goals[:score][i] += 1 unless key == :p
170
+ goals[key][:score][i] += 1
171
+ end
172
+ end
173
+
174
+ ## use/calc cummulative score (BUT not for penalties)
175
+ goals[:ft][:score][0] += goals[:ht][:score][0]
176
+ goals[:ft][:score][1] += goals[:ht][:score][1]
177
+
178
+ goals[:et][:score][0] += goals[:ft][:score][0]
179
+ goals[:et][:score][1] += goals[:ft][:score][1]
180
+
181
+ goals
182
+ end