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 +4 -4
- data/CHANGELOG.md +1 -0
- data/Manifest.txt +15 -11
- data/lib/fifadat/convert/convert-reports.rb +49 -67
- data/lib/fifadat/convert/convert.rb +67 -96
- data/lib/fifadat/convert/helper-date.rb +12 -0
- data/lib/fifadat/convert/helper-score.rb +104 -61
- data/lib/fifadat/convert/reader.rb +55 -0
- data/lib/fifadat/convert/tool.rb +1 -1
- data/lib/fifadat/convert/version.rb +2 -2
- data/lib/fifadat/convert.rb +54 -14
- data/lib/fifadat/{convert/goals.rb → models/goal.rb} +49 -18
- data/lib/fifadat/models/match.rb +218 -0
- data/lib/fifadat/models/match_report.rb +205 -0
- data/lib/fifadat/{convert/officials.rb → models/official.rb} +58 -20
- data/lib/fifadat/{convert/penalties.rb → models/penalty.rb} +50 -16
- data/lib/fifadat/models/player.rb +218 -0
- data/lib/fifadat/models/stadium.rb +98 -0
- data/lib/fifadat/models/stage.rb +99 -0
- data/lib/fifadat/{convert/substitutions.rb → models/substitution.rb} +84 -9
- data/lib/fifadat/models/team.rb +85 -0
- data/lib/fifadat/tables/players.rb +140 -0
- data/lib/fifadat/tables/stadiums.rb +71 -0
- data/lib/fifadat/tables/stages.rb +58 -0
- data/lib/fifadat/tables/teams.rb +57 -0
- metadata +17 -13
- data/lib/fifadat/convert/build_match.rb +0 -105
- data/lib/fifadat/convert/build_report.rb +0 -135
- data/lib/fifadat/convert/goals-calc_score.rb +0 -58
- data/lib/fifadat/convert/players.rb +0 -329
- data/lib/fifadat/convert/stadiums.rb +0 -99
- data/lib/fifadat/convert/stages.rb +0 -119
- data/lib/fifadat/convert/teams.rb +0 -91
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
## use mk/build_report_basename - why? why not?
|
|
2
|
+
## fix-fix-fix - change to _match_basename !!!
|
|
3
|
+
## use by (match)report and (match)timeline !!!
|
|
4
|
+
def _report_basename( m )
|
|
5
|
+
### get match (live) details
|
|
6
|
+
team1_code = m['Home']['Abbreviation']
|
|
7
|
+
team2_code = m['Away']['Abbreviation']
|
|
8
|
+
localDateTime = parse_date_utc( m['LocalDate'] )
|
|
9
|
+
|
|
10
|
+
## e.g. 2020-06-24_RAP-AUS__54545454
|
|
11
|
+
basename = String.new
|
|
12
|
+
basename += localDateTime.strftime('%Y-%m-%d') ## i) date
|
|
13
|
+
basename += "_#{team1_code}-#{team2_code}" ## ii) team1 v team2
|
|
14
|
+
|
|
15
|
+
basename
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _read_timeline( m, timeline_dir: )
|
|
21
|
+
return nil if m['Home'].nil? || m['Away'].nil?
|
|
22
|
+
|
|
23
|
+
basename = _report_basename( m )
|
|
24
|
+
basename += "__#{m['IdMatch']}"
|
|
25
|
+
|
|
26
|
+
path = "#{timeline_dir}/#{basename}.json"
|
|
27
|
+
|
|
28
|
+
## note - skip if no match report available!!!
|
|
29
|
+
if File.file?( path )
|
|
30
|
+
timeline = read_json_v2( path )
|
|
31
|
+
timeline
|
|
32
|
+
else
|
|
33
|
+
nil
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def _read_report( m, report_dir: )
|
|
38
|
+
return nil if m['Home'].nil? || m['Away'].nil?
|
|
39
|
+
|
|
40
|
+
## e.g. 2020-06-24_RAP-AUS__54545454
|
|
41
|
+
# note - add match id for "raw" match reports!!!
|
|
42
|
+
basename = _report_basename( m )
|
|
43
|
+
basename += "__#{m['IdMatch']}"
|
|
44
|
+
|
|
45
|
+
## #{indir}/#{slug}/matches/#{season.to_path}
|
|
46
|
+
path = "#{report_dir}/#{basename}.json"
|
|
47
|
+
|
|
48
|
+
## note - skip if no match report available!!!
|
|
49
|
+
if File.file?( path )
|
|
50
|
+
live = read_json_v2( path )
|
|
51
|
+
live
|
|
52
|
+
else
|
|
53
|
+
nil
|
|
54
|
+
end
|
|
55
|
+
end
|
data/lib/fifadat/convert/tool.rb
CHANGED
|
@@ -77,7 +77,7 @@ else
|
|
|
77
77
|
idCompetition = h[:idCompetition]
|
|
78
78
|
comp = Fifa::COMPETITIONS[idCompetition]
|
|
79
79
|
seasons = comp.values.map { |c| c[:season] }
|
|
80
|
-
puts " #{code} (#{seasons.size}) #{seasons.join('
|
|
80
|
+
puts " #{code} (#{seasons.size}) #{seasons.join(' ')}"
|
|
81
81
|
end
|
|
82
82
|
exit 1
|
|
83
83
|
end
|
data/lib/fifadat/convert.rb
CHANGED
|
@@ -3,6 +3,37 @@ $LOAD_PATH.unshift( '/sports/yorobot/fifadat/fifadat/lib' )
|
|
|
3
3
|
require 'fifadat'
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
# module JsonSerializable
|
|
7
|
+
# def to_json(*args)
|
|
8
|
+
# as_json.to_json(*args)
|
|
9
|
+
# end
|
|
10
|
+
# end
|
|
11
|
+
|
|
12
|
+
###
|
|
13
|
+
## add as_json to Object/Array/Hash
|
|
14
|
+
class Object
|
|
15
|
+
def as_json(*)
|
|
16
|
+
self
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class Array
|
|
21
|
+
def as_json(*)
|
|
22
|
+
map { |v| v.as_json }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class Hash
|
|
27
|
+
def as_json(*)
|
|
28
|
+
each_with_object({}) do |(k, v), h|
|
|
29
|
+
h[k.to_s] = v.as_json
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
6
37
|
## our own code
|
|
7
38
|
require_relative 'convert/version'
|
|
8
39
|
require_relative 'convert/tool' ## fifadat command-line tool
|
|
@@ -11,20 +42,7 @@ require_relative 'convert/tool' ## fifadat command-line tool
|
|
|
11
42
|
require_relative 'convert/norm'
|
|
12
43
|
require_relative 'convert/errata'
|
|
13
44
|
|
|
14
|
-
require_relative 'convert/
|
|
15
|
-
require_relative 'convert/teams'
|
|
16
|
-
require_relative 'convert/stadiums'
|
|
17
|
-
require_relative 'convert/players'
|
|
18
|
-
|
|
19
|
-
require_relative 'convert/officials' ## aka referees
|
|
20
|
-
require_relative 'convert/goals'
|
|
21
|
-
require_relative 'convert/goals-calc_score'
|
|
22
|
-
require_relative 'convert/penalties'
|
|
23
|
-
require_relative 'convert/substitutions'
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
require_relative 'convert/build_match'
|
|
27
|
-
require_relative 'convert/build_report'
|
|
45
|
+
require_relative 'convert/reader'
|
|
28
46
|
require_relative 'convert/convert'
|
|
29
47
|
require_relative 'convert/convert-reports'
|
|
30
48
|
|
|
@@ -34,4 +52,26 @@ require_relative 'convert/helper-minute'
|
|
|
34
52
|
|
|
35
53
|
|
|
36
54
|
|
|
55
|
+
require_relative 'models/stage'
|
|
56
|
+
require_relative 'models/team'
|
|
57
|
+
require_relative 'models/stadium'
|
|
58
|
+
require_relative 'models/match'
|
|
59
|
+
require_relative 'models/match_report'
|
|
60
|
+
require_relative 'models/goal'
|
|
61
|
+
require_relative 'models/penalty'
|
|
62
|
+
require_relative 'models/substitution'
|
|
63
|
+
require_relative 'models/official' ## aka referees
|
|
64
|
+
require_relative 'models/player' # note - is lineup player incl. starter/bench, position, etc.
|
|
65
|
+
# (NOT squad player)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
require_relative 'tables/stages'
|
|
70
|
+
require_relative 'tables/teams'
|
|
71
|
+
require_relative 'tables/stadiums'
|
|
72
|
+
require_relative 'tables/players'
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
37
77
|
puts FifadatConvert.banner ## say hello
|
|
@@ -1,8 +1,29 @@
|
|
|
1
1
|
|
|
2
|
+
class Goal
|
|
2
3
|
|
|
3
4
|
|
|
5
|
+
## add alias _build_all or _build_collection or ?? - why? why not?
|
|
6
|
+
def self._build_ary( recs, players:,
|
|
7
|
+
penalties: false )
|
|
8
|
+
|
|
9
|
+
## note - filter out penalties (from shoot-out)!!
|
|
10
|
+
## min > 120 (e.g. 121, etc.)
|
|
11
|
+
## note - use period (more reliable)
|
|
12
|
+
## period 11 is PENALTY_SHOOTOUT!!
|
|
13
|
+
if penalties == false
|
|
14
|
+
recs = recs.select { |rec| rec['Period'] != 11 }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
recs = recs.map { |h| build( h, players: players ) }
|
|
19
|
+
|
|
20
|
+
## note - sort by minutes; goals may not be sorted
|
|
21
|
+
recs = recs.sort { |l,r| l.minute <=> r.minute }
|
|
22
|
+
recs
|
|
23
|
+
end
|
|
4
24
|
|
|
5
|
-
|
|
25
|
+
|
|
26
|
+
def self.build( h, players: )
|
|
6
27
|
|
|
7
28
|
## split into minute
|
|
8
29
|
## and offset (stoppage/injury time)
|
|
@@ -40,16 +61,15 @@ def build_goal( h, players: )
|
|
|
40
61
|
idPlayer = h['IdPlayer']
|
|
41
62
|
|
|
42
63
|
if idPlayer.nil?
|
|
43
|
-
puts "!! no idPlayer for goal
|
|
64
|
+
puts "!! no idPlayer for goal (live):"
|
|
44
65
|
pp h
|
|
45
66
|
##exit 1
|
|
46
67
|
## use 'N.N.'
|
|
47
68
|
|
|
48
|
-
rec[ :
|
|
69
|
+
rec[ :player ] = Player::PLAYER_NIL ## 'N.N.'
|
|
49
70
|
|
|
50
71
|
else
|
|
51
|
-
player = players.find!( idPlayer )
|
|
52
|
-
rec[ :name ] = player[ :name ]
|
|
72
|
+
rec[ :player ] = players.find!( idPlayer )
|
|
53
73
|
end
|
|
54
74
|
|
|
55
75
|
rec[ :minute] = minute
|
|
@@ -64,26 +84,37 @@ def build_goal( h, players: )
|
|
|
64
84
|
rec[ :pen ] = true if type == 1
|
|
65
85
|
rec[ :og ] = true if type == 3
|
|
66
86
|
|
|
67
|
-
rec
|
|
87
|
+
new( **rec )
|
|
68
88
|
end
|
|
69
89
|
|
|
70
90
|
|
|
71
91
|
|
|
72
92
|
|
|
73
|
-
def build_goals( recs, players:, penalties: false )
|
|
74
93
|
|
|
75
|
-
|
|
76
|
-
|
|
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
|
|
94
|
+
attr_reader :player, :minute,
|
|
95
|
+
:pen, :og
|
|
82
96
|
|
|
97
|
+
def initialize(
|
|
98
|
+
player:,
|
|
99
|
+
minute:,
|
|
100
|
+
pen: false, og: false)
|
|
101
|
+
@player = player
|
|
102
|
+
@minute = minute
|
|
103
|
+
@pen = pen
|
|
104
|
+
@og = og
|
|
105
|
+
end
|
|
83
106
|
|
|
84
|
-
recs = recs.map { |h| build_goal( h, players: players ) }
|
|
85
107
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
108
|
+
def as_json(*)
|
|
109
|
+
h = {
|
|
110
|
+
'name' => player.name,
|
|
111
|
+
'minute' => minute,
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
h['og'] = og if og
|
|
115
|
+
h['pen'] = pen if pen
|
|
116
|
+
h
|
|
89
117
|
end
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
end # class Goal
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
class Match
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def self.build( m, teams:, stadiums:, stages: ) ## use _fill/build_match_basics() or such ???
|
|
7
|
+
rec = {}
|
|
8
|
+
|
|
9
|
+
## 0 => FINISHED/complete (OK)
|
|
10
|
+
## 1 => SCHEDULED/not yet played
|
|
11
|
+
status = m['MatchStatus']
|
|
12
|
+
timed = m['TimeDefined']
|
|
13
|
+
rec[:timed] = timed ### note - keep timed flag too - why? why not?
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
if status == 1 && timed ## note: use TIMED (instead of SCHEDULED)
|
|
17
|
+
rec[:status] = 'TIMED'
|
|
18
|
+
else
|
|
19
|
+
rec[:status] = MATCH_STATUS[ status ] || "#{status}-???"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
rec[:stage] = stages.find!( desc( m['StageName'] ))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## check optional group, matchday, (match) num
|
|
28
|
+
## note - matchday is a string!! (check if always a number)
|
|
29
|
+
## ## convert to int if str - why? why not??
|
|
30
|
+
group = desc( m['GroupName'] )
|
|
31
|
+
rec[:group] = group if group
|
|
32
|
+
|
|
33
|
+
matchday = m['MatchDay']
|
|
34
|
+
rec[:matchday] = matchday.to_i(10) if matchday
|
|
35
|
+
|
|
36
|
+
num = m['MatchNumber']
|
|
37
|
+
rec[:num] = num if num
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
## e.g. if timed == false (in at)
|
|
41
|
+
## "Date": "2027-02-27T00:00:00Z",
|
|
42
|
+
## "LocalDate": "2027-02-27T01:00:00Z",
|
|
43
|
+
|
|
44
|
+
dateTime = parse_date_utc( m['Date'] )
|
|
45
|
+
localDateTime = parse_date_utc( m['LocalDate'] )
|
|
46
|
+
|
|
47
|
+
if timed
|
|
48
|
+
## pass along as is for now - why? why not
|
|
49
|
+
## note - Date is date_utc and
|
|
50
|
+
## LocalDate is date_local (also in utc BUT different HH:MM or possible day)
|
|
51
|
+
## note - also in utc, that is, timezone is Z (not +03:00 or such!!!)
|
|
52
|
+
|
|
53
|
+
## note - use datetime - why? why not?
|
|
54
|
+
##
|
|
55
|
+
## note - cut-off/remove seconds
|
|
56
|
+
rec[:datetime_utc] = dateTime.strftime( '%Y-%m-%dT%H:%MZ' )
|
|
57
|
+
|
|
58
|
+
## note - use 20:30 UTC+1 or 20:30 UTC-3 for timezone format for now
|
|
59
|
+
rec[:date_local] = localDateTime.strftime( '%Y-%m-%d' )
|
|
60
|
+
rec[:time_local] = _fmt_time_local( dateTime, localDateTime )
|
|
61
|
+
else
|
|
62
|
+
## note - uses
|
|
63
|
+
## todo/fix - assert that utc hour/minute is 00:00 e.g.
|
|
64
|
+
## "2027-02-27T00:00:00Z"
|
|
65
|
+
rec[:date_local] = dateTime.strftime( '%Y-%m-%d' )
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
if m['HomeTeam'] && m['AwayTeam'] ## assume match reports (json-style)
|
|
71
|
+
team1 = teams.find!( m['HomeTeam'] )
|
|
72
|
+
team2 = teams.find!( m['AwayTeam'] )
|
|
73
|
+
else
|
|
74
|
+
team1 = teams.find!( m['Home'] )
|
|
75
|
+
team2 = teams.find!( m['Away'] )
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
rec[:team1] = team1
|
|
79
|
+
rec[:team2] = team2
|
|
80
|
+
|
|
81
|
+
#####
|
|
82
|
+
# handle score
|
|
83
|
+
## m = (full) match hash incl. IdMatch, etc.
|
|
84
|
+
## returns string e.g. 4-4 or 4-3 a.e.t etc
|
|
85
|
+
|
|
86
|
+
##
|
|
87
|
+
## fix-fix-fix
|
|
88
|
+
## use a score class!!!
|
|
89
|
+
## see openliga etc.
|
|
90
|
+
|
|
91
|
+
score = _parse_score( m )
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
rec[:score] = score
|
|
95
|
+
|
|
96
|
+
## check - never in use?
|
|
97
|
+
rec[:home] = true if m['IsHome']
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
## add country to stadium too - why? why not?
|
|
101
|
+
rec[:stadium] = stadiums.find!( m['Stadium'] )
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
attendance = m['Attendance']
|
|
105
|
+
rec[:attendance] = attendance.to_i(10) if attendance
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
new( **rec )
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
attr_reader :status,
|
|
113
|
+
:team1, :team2,
|
|
114
|
+
:stage, :group, :matchday, :num,
|
|
115
|
+
:datetime_utc,
|
|
116
|
+
:date_local,
|
|
117
|
+
:time_local, :timed,
|
|
118
|
+
:stadium, :home, :attendance
|
|
119
|
+
|
|
120
|
+
attr_accessor :score,
|
|
121
|
+
:goals1, :goals2,
|
|
122
|
+
:penalties,
|
|
123
|
+
:sentoff1, :sentoff2,
|
|
124
|
+
:officials
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def initialize(
|
|
128
|
+
status:,
|
|
129
|
+
team1:,
|
|
130
|
+
team2:,
|
|
131
|
+
score: nil,
|
|
132
|
+
|
|
133
|
+
stage: nil,
|
|
134
|
+
group: nil,
|
|
135
|
+
matchday: nil,
|
|
136
|
+
num: nil,
|
|
137
|
+
|
|
138
|
+
datetime_utc: nil,
|
|
139
|
+
date_local: nil,
|
|
140
|
+
time_local: nil,
|
|
141
|
+
timed: nil,
|
|
142
|
+
|
|
143
|
+
stadium: nil,
|
|
144
|
+
home: nil,
|
|
145
|
+
attendance: nil)
|
|
146
|
+
@status = status
|
|
147
|
+
@team1 = team1
|
|
148
|
+
@team2 = team2
|
|
149
|
+
@score = score
|
|
150
|
+
|
|
151
|
+
@stage = stage
|
|
152
|
+
@group = group
|
|
153
|
+
@matchday = matchday
|
|
154
|
+
@num = num
|
|
155
|
+
|
|
156
|
+
@datetime_utc = datetime_utc
|
|
157
|
+
@date_local = date_local
|
|
158
|
+
@time_local = time_local
|
|
159
|
+
@timed = timed
|
|
160
|
+
|
|
161
|
+
@stadium = stadium
|
|
162
|
+
@home = home
|
|
163
|
+
@attendance = attendance
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def as_json(*)
|
|
169
|
+
h = {
|
|
170
|
+
'status' => status,
|
|
171
|
+
'stage' => stage.name,
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
h['matchday'] = matchday if matchday
|
|
175
|
+
h['num'] = num if num
|
|
176
|
+
h['group'] = group if group
|
|
177
|
+
|
|
178
|
+
if timed
|
|
179
|
+
h['datetime_utc'] = datetime_utc
|
|
180
|
+
h['date_local'] = date_local
|
|
181
|
+
h['time_local'] = time_local
|
|
182
|
+
else
|
|
183
|
+
h['date_local'] = date_local
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
h['team1'] = team1.name
|
|
187
|
+
h['team2'] = team2.name
|
|
188
|
+
|
|
189
|
+
h['score'] = score if score && !score.empty?
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
if stadium
|
|
193
|
+
h['stadium'] = { 'name' => stadium.name,
|
|
194
|
+
'city' => stadium.city }
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
h['attendance'] if attendance
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
if goals1 || goals2
|
|
202
|
+
h['goals1'] = (goals1 || []).as_json
|
|
203
|
+
h['goals2'] = (goals2 || []).as_json
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
h['penalities'] = penalties.as_json if penalties
|
|
207
|
+
|
|
208
|
+
if sentoff1 || sentoff2
|
|
209
|
+
h['sentoff1'] = (sentoff1 || []).as_json
|
|
210
|
+
h['sentoff2'] = (sentoff2 || []).as_json
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
h['referees'] = officials.as_json if officials
|
|
215
|
+
h
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
end # class Match
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
## holds details of match (kind of match sheet/report)
|
|
5
|
+
##
|
|
6
|
+
|
|
7
|
+
## fix-fix-fix move to tables - yes or rename MatchReportBuilder ?
|
|
8
|
+
## move to tables - why? why not?
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
## fix-fix-fix rename to MatchDetails !!!
|
|
12
|
+
## keep MatchReport/Sheet reserved for later
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MatchReport
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def self.build( live, timeline=nil )
|
|
19
|
+
_players1 = Player._build_ary( live['HomeTeam']['Players'] )
|
|
20
|
+
_players2 = Player._build_ary( live['AwayTeam']['Players'] )
|
|
21
|
+
|
|
22
|
+
players = Players.new ## lookup all players (team1+team2)
|
|
23
|
+
players.add( _players1 )
|
|
24
|
+
players.add( _players2 )
|
|
25
|
+
|
|
26
|
+
players1 = Players.new ## lookup players by team1/team2 only
|
|
27
|
+
players1.add( _players1 )
|
|
28
|
+
players2 = Players.new
|
|
29
|
+
players2.add( _players2 )
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
o = new
|
|
33
|
+
###
|
|
34
|
+
## add goals
|
|
35
|
+
o.goals1 = Goal._build_ary( live['HomeTeam']['Goals'], players: players )
|
|
36
|
+
o.goals2 = Goal._build_ary( live['AwayTeam']['Goals'], players: players )
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
##########
|
|
40
|
+
## add penalty kicks / penalties
|
|
41
|
+
## note - requires timeline !!!
|
|
42
|
+
if live['ResultType'] == 2 ## win on pens
|
|
43
|
+
if timeline.nil?
|
|
44
|
+
##
|
|
45
|
+
team1_code = live['HomeTeam']['Abbreviation']
|
|
46
|
+
team2_code = live['AwayTeam']['Abbreviation']
|
|
47
|
+
puts "!! ERROR - no timeline for match #{team1_code} v #{team2_code} with win on penalties!!"
|
|
48
|
+
exit 1
|
|
49
|
+
else
|
|
50
|
+
### get timeline with penalty shoot-out details
|
|
51
|
+
## note - requires team1/2_ids!!!
|
|
52
|
+
team1_id = live['HomeTeam']['IdTeam']
|
|
53
|
+
team2_id = live['AwayTeam']['IdTeam']
|
|
54
|
+
|
|
55
|
+
pens = Penalty._build_ary( timeline['Event'],
|
|
56
|
+
players: players,
|
|
57
|
+
team1_id: team1_id,
|
|
58
|
+
team2_id: team2_id )
|
|
59
|
+
## pp pens
|
|
60
|
+
|
|
61
|
+
o.penalties = pens
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
#######
|
|
67
|
+
## add lineup/bench
|
|
68
|
+
o.formation1 = live['HomeTeam']['Tactics'] ## e.g. "Tactics": "3-1-4-2"
|
|
69
|
+
o.lineup1 = players1.lineup ## starter XI (only excludes bench)
|
|
70
|
+
o.bench1 = players1.bench ## substitutes
|
|
71
|
+
|
|
72
|
+
o.formation2 = live['AwayTeam']['Tactics']
|
|
73
|
+
o.lineup2 = players2.lineup
|
|
74
|
+
o.bench2 = players2.bench
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
#####
|
|
78
|
+
## add bookings (yellow/red/red-yellow cards)
|
|
79
|
+
players1.add_bookings( live['HomeTeam']['Bookings'])
|
|
80
|
+
players2.add_bookings( live['AwayTeam']['Bookings'])
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
##
|
|
85
|
+
## maybe collect cards on demand (not ahead of time) - why? why not?
|
|
86
|
+
yellow1 = players1.yellowcards
|
|
87
|
+
red1 = players1.redcards
|
|
88
|
+
yellowred1 = players1.yellowredcards
|
|
89
|
+
|
|
90
|
+
o.yellow1 = yellow1 unless yellow1.empty?
|
|
91
|
+
o.red1 = red1 unless red1.empty?
|
|
92
|
+
o.yellowred1 = yellowred1 unless yellowred1.empty?
|
|
93
|
+
|
|
94
|
+
yellow2 = players2.yellowcards
|
|
95
|
+
red2 = players2.redcards
|
|
96
|
+
yellowred2 = players2.yellowredcards
|
|
97
|
+
|
|
98
|
+
o.yellow2 = yellow2 unless yellow2.empty?
|
|
99
|
+
o.red2 = red2 unless red2.empty?
|
|
100
|
+
o.yellowred2 = yellowred2 unless yellowred2.empty?
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
## add substitutions (off/on - in/out)
|
|
104
|
+
o.subs1 = Sub._build_ary( live['HomeTeam']['Substitutions'], players: players1 )
|
|
105
|
+
o.subs2 = Sub._build_ary( live['AwayTeam']['Substitutions'], players: players2 )
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
## add referees
|
|
109
|
+
officials = Official._build_ary( live['Officials'] )
|
|
110
|
+
|
|
111
|
+
if officials.empty?
|
|
112
|
+
puts "!! WARN no refs / officials found"
|
|
113
|
+
else
|
|
114
|
+
o.officials = officials
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
o
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
attr_accessor :goals1, :goals2,
|
|
123
|
+
:penalties,
|
|
124
|
+
:lineup1, :lineup2,
|
|
125
|
+
:bench1, :bench2,
|
|
126
|
+
:formation1, :formation2,
|
|
127
|
+
:yellow1, :yellow2,
|
|
128
|
+
:yellowred1, :yellowred2,
|
|
129
|
+
:red1, :red2,
|
|
130
|
+
:subs1, :subs2,
|
|
131
|
+
:officials # aka referees
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def initialize
|
|
135
|
+
@goals1 = [], @goals2 = []
|
|
136
|
+
@penalties = nil ## or use [] - why? why not?
|
|
137
|
+
|
|
138
|
+
@lineup1 = [], @lineup2 = []
|
|
139
|
+
@bench1 = [], @bench2 = []
|
|
140
|
+
|
|
141
|
+
@formation1 = nil, @formation2 = nil
|
|
142
|
+
|
|
143
|
+
@yellow1 = []
|
|
144
|
+
@yellow2 = []
|
|
145
|
+
@yellowred1 = []
|
|
146
|
+
@yellowred2 = []
|
|
147
|
+
@red1 = []
|
|
148
|
+
@red2 = []
|
|
149
|
+
|
|
150
|
+
@subs1 = [], @subs2 = []
|
|
151
|
+
|
|
152
|
+
@officials = nil
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def sentoff1
|
|
157
|
+
## pp self
|
|
158
|
+
sentoff1 = (red1||[]) + (yellowred1||[])
|
|
159
|
+
## pp red1
|
|
160
|
+
## pp yellowred1
|
|
161
|
+
## pp sentoff1
|
|
162
|
+
sentoff1.sort { |l,r| l.minute <=> r.minute } ## sort by minute
|
|
163
|
+
end
|
|
164
|
+
def sentoff2
|
|
165
|
+
sentoff2 = (red2||[]) + (yellowred2||[])
|
|
166
|
+
sentoff2.sort { |l,r| l.minute <=> r.minute } ## sort by minute
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def as_json(*)
|
|
172
|
+
h = {}
|
|
173
|
+
|
|
174
|
+
if goals1 || goals2
|
|
175
|
+
h['goals1'] = (goals1 || []).as_json
|
|
176
|
+
h['goals2'] = (goals2 || []).as_json
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
h['penalities'] = penalties.as_json if penalties
|
|
180
|
+
|
|
181
|
+
h['formation1'] = formation1 if formation1
|
|
182
|
+
h['lineup1'] = lineup1.as_json
|
|
183
|
+
h['bench1'] = bench1.as_json
|
|
184
|
+
|
|
185
|
+
h['formation2'] = formation2 if formation2
|
|
186
|
+
h['lineup2'] = lineup2.as_json
|
|
187
|
+
h['bench2'] = bench2.as_json
|
|
188
|
+
|
|
189
|
+
h['yellow1'] = yellow1.as_json if yellow1 && !yellow1.empty?
|
|
190
|
+
h['yellow2'] = yellow2.as_json if yellow2 && !yellow2.empty?
|
|
191
|
+
h['yellowred1'] = yellowred1.as_json if yellowred1 && !yellowred1.empty?
|
|
192
|
+
h['yellowred2'] = yellowred2.as_json if yellowred2 && !yellowred2.empty?
|
|
193
|
+
h['red1'] = red1.as_json if red1 && !red1.empty?
|
|
194
|
+
h['red2'] = red2.as_json if red2 && !red2.empty?
|
|
195
|
+
|
|
196
|
+
h['subs1'] = subs1.as_json
|
|
197
|
+
h['subs2'] = subs2.as_json
|
|
198
|
+
|
|
199
|
+
h['referees'] = officials.as_json if officials
|
|
200
|
+
|
|
201
|
+
h
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
end ## class MatchReport
|