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,81 @@
1
+
2
+
3
+ ##
4
+ ## note - check if m(inute) might be unknown e.g. '??' or such
5
+ ##
6
+ ## offset (aka stoppage/injury/added time)
7
+
8
+ Minute = Struct.new( :m, :offset, :period) do
9
+ def as_json(*) to_s; end
10
+
11
+ ## note - format minute WITHOUT minute marker/quote (')
12
+ def to_s
13
+ buf = String.new
14
+ buf << "#{m}"
15
+ buf << "+#{offset}" if offset
16
+ buf
17
+ end
18
+
19
+ ## add compare (for sort)
20
+ def <=>( other )
21
+ res = self.m <=> other.m
22
+ res = (self.offset||0) <=> (other.offset||0) if res == 0
23
+ res
24
+ end
25
+ end ## struct (class) Minute
26
+
27
+
28
+
29
+ def _build_minute( h )
30
+
31
+ ## split into minute
32
+ ## and offset (stoppage/injury/added time)
33
+ ## e.g. 90'+11'
34
+
35
+ minute_str = h['Minute']
36
+ if minute_str.nil? || minute_str.empty?
37
+ puts "!! minute is nil or empty:"
38
+ pp h
39
+ exit 1
40
+ end
41
+
42
+ minute, offset = _parse_minute( minute_str )
43
+
44
+ Minute.new( m: minute,
45
+ offset: offset,
46
+ period: h['Period'] )
47
+ end
48
+
49
+
50
+ MINUTE_RE = %r{ \A
51
+ (?<minute>\d{1,3}) '
52
+ ( \+
53
+ (?<offset>\d{1,2}) '
54
+ )?
55
+ \z
56
+ }x
57
+
58
+
59
+ def _parse_minute( str )
60
+
61
+ ## support weirdo 120'+-30' -- remove minuts
62
+ str = str.gsub( '-', '' )
63
+
64
+ m = MINUTE_RE.match( str )
65
+ raise ArgumentError, "unknown goal minute format in #{str.inspect}" if m.nil?
66
+
67
+ minute = m[:minute].to_i(10)
68
+ offset = m[:offset] ? m[:offset].to_i(10) : nil
69
+
70
+ [minute,offset]
71
+ end
72
+
73
+ def _fmt_minute( minute, offset )
74
+ ## pp [minute,offset]
75
+
76
+ buf = String.new
77
+ buf << "#{minute}"
78
+ buf << "+#{offset}" if offset
79
+ buf << "'"
80
+ buf
81
+ end
@@ -0,0 +1,139 @@
1
+
2
+ #
3
+ # add quick fix for club world cup?!
4
+ # elsif resultType == 1 || ## assume 1 - regular (90 mins+stoppage/injury time)
5
+ # resultType == 4 ||
6
+ # m['IdMatch'] == '400019191' ## fix for pachuca vs salzburg !!!
7
+ # has resultType == 0!!!
8
+
9
+
10
+ def _parse_score( m )
11
+ h = { }
12
+
13
+ m = errata_autofix_score( m )
14
+
15
+
16
+ resultType = m['ResultType']
17
+
18
+ score = if m['HomeTeam'] && m['AwayTeam'] ## assume inside (match) report
19
+ [m['HomeTeam']['Score'], m['AwayTeam']['Score']].compact
20
+ else ## assume (seasons) matches list
21
+ ## note - report has no "inline/flat" HomeTeamScore/AwayTeamScore
22
+ [m['HomeTeamScore'], m['AwayTeamScore']].compact
23
+ end
24
+
25
+
26
+ ## is score after extra-time or not?
27
+ ## note - CANNOT tell for resultType 2!!!
28
+
29
+ if score.empty?
30
+ ## do nothing
31
+ else
32
+ if [1,4].include?( resultType )
33
+ ## 1 - regular - 90min
34
+ ## 4 - regular - 90min (on aggregate)
35
+ h[:ft] = score
36
+ elsif [3,5,8].include?( resultType )
37
+ ## 3 - aet (for sure)
38
+ ## 5 - aet [on aggregate] (for sure)
39
+ ## 8 - golden goal in extra time (aet/gg)
40
+ ## fix/fix/fix - add gold_goal flag for 8!!!
41
+ h[:et] = score ## assume after extra-time !!!
42
+ elsif resultType == 2
43
+ ### note - 2 win on pens
44
+ ## 2 - win on pens (with or WITHOUT aet!!)
45
+
46
+ ## check for competition for now
47
+ ## incl. south american
48
+ if false ## add some competitions here
49
+ h[:ft] = score
50
+ else
51
+ h[:et] = score
52
+ end
53
+ else
54
+ h[:score] = score
55
+ end
56
+ end
57
+
58
+
59
+ penScore = [m['HomeTeamPenaltyScore'], m['AwayTeamPenaltyScore']].compact
60
+ aggScore = [m['AggregateHomeTeamScore'],m['AggregateAwayTeamScore']].compact
61
+
62
+
63
+ ## note - worldcup has penScore [0,0] for all matches, for example!!!
64
+ if penScore.empty? || penScore == [0,0]
65
+ else
66
+ h[:p] = penScore
67
+ end
68
+
69
+ h[:agg] = aggScore if !aggScore.empty?
70
+ h
71
+ end
72
+
73
+
74
+
75
+ def _fmt_score( m )
76
+
77
+ m = errata_autofix_score( m )
78
+
79
+ ## m = (full) match hash incl. IdMatch, etc.
80
+ ## returns string e.g. 4-4 or 4-3 a.e.t etc
81
+
82
+ resultType = m['ResultType']
83
+
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
112
+
113
+
114
+
115
+
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
@@ -0,0 +1,34 @@
1
+
2
+
3
+ ALPHA_RE = %r{ \A
4
+ \p{L}
5
+ [\p{L} '.-]*
6
+ \z
7
+ }ix
8
+
9
+ ##
10
+ ## J. HARTING - note: include .
11
+ ## Jose BUSTAMANTE-NAVA - note: include -
12
+ ## Yannick N'Djeng - note: include '
13
+ ## Ismail JAKOBS Ismail JAKOBS
14
+
15
+ def is_alpha?( name ) ## use is_alpha_name? or is_unialpha? or such??
16
+ ALPHA_RE.match( name ) ? true : false
17
+ end
18
+
19
+
20
+ def pp_alpha( name )
21
+ ##
22
+ ## note: e is decomposed !!!! - will break lexer!!!
23
+ ## e (101)
24
+ ## ́ (769)
25
+ ## vs
26
+ ## é (233)
27
+
28
+ buf = String.new
29
+ buf << ">#{name}< => "
30
+ name.each_char do |char|
31
+ buf << "#{char} (#{char.ord}) | "
32
+ end
33
+ buf
34
+ end
@@ -0,0 +1,86 @@
1
+
2
+ =begin
3
+ "Officials":
4
+ [{"IdCountry": "URU",
5
+ "OfficialId": "61038",
6
+ "NameShort": [{"Locale": "en-GB", "Description": "Domingo LOMBARDI"}],
7
+ "Name": [{"Locale": "en-GB", "Description": "Domingo LOMBARDI"}],
8
+ "OfficialType": 1,
9
+ "TypeLocalized": [{"Locale": "en-GB", "Description": "Referee"}]},
10
+ {"IdCountry": "BEL",
11
+ "OfficialId": "60664",
12
+ "NameShort": [{"Locale": "en-GB", "Description": "Henry CRISTOPHE"}],
13
+ "Name": [{"Locale": "en-GB", "Description": "Henry CRISTOPHE"}],
14
+ "OfficialType": 2,
15
+ "TypeLocalized":
16
+ [{"Locale": "en-GB", "Description": "Assistant Referee 1"}]},
17
+ {"IdCountry": "BRA",
18
+ "OfficialId": "61289",
19
+ "NameShort": [{"Locale": "en-GB", "Description": "Gilberto REGO"}],
20
+ "Name": [{"Locale": "en-GB", "Description": "Gilberto REGO"}],
21
+ "OfficialType": 3,
22
+ "TypeLocalized":
23
+ [{"Locale": "en-GB", "Description": "Assistant Referee 2"}]}],
24
+
25
+ =end
26
+
27
+
28
+ TYPE_OFFICIAL = {
29
+ 1 => 'Referee',
30
+ 2 => 'Assistant Referee 1',
31
+ 3 => 'Assistant Referee 2',
32
+ }
33
+
34
+ def build_official( h, id: )
35
+ name = desc( h['Name'] )
36
+
37
+ ## fix - use norm_official
38
+ ## name = norm_official( name )
39
+
40
+ idCountry = h['IdCountry']
41
+ type = h['OfficialType']
42
+
43
+
44
+ assert( is_alpha?(name), "official name alpha expected; got #{pp_alpha(name)}" )
45
+
46
+
47
+
48
+ assert( [1,2,3,4,5,6,7,8,9,10].include?( type ), "official type 1/2/3/4/5/6/7/8/9/10 expected; got #{type}" )
49
+
50
+ rec = {}
51
+ rec[:id] = h['OfficialId'] if id
52
+
53
+ rec.merge!( name: name,
54
+ country: idCountry,
55
+ type: type )
56
+
57
+ rec
58
+ end
59
+
60
+
61
+ def build_officials( recs, id: false ) ## use referees?
62
+ recs = recs.map { |h| build_official( h, id: id ) }
63
+
64
+ ## skip fourth official (4) for now
65
+ recs = recs.select { |h| [1,2,3].include?( h[:type] ) }
66
+
67
+ ## sort by type 1/2/3
68
+ ## 1 - referee
69
+ ## 2 - assistant referee 1
70
+ ## 3 - assistant referee 2
71
+ ## 4 - fourth official
72
+ ## 5 - video assistant referee (var)
73
+ ## 6 - reserve referee
74
+ ## 7 - offside var
75
+ ## 8 - assistant var
76
+ ## 9 - support var
77
+ ## 10 - reserve assistant referee
78
+
79
+
80
+ recs = recs.sort { |l,r| l[:type] <=> r[:type] }
81
+
82
+ ## change type to literal string
83
+ recs = recs.map { |h| h[:type]=TYPE_OFFICIAL[h[:type]]; h }
84
+
85
+ recs
86
+ end
@@ -0,0 +1,132 @@
1
+
2
+ =begin
3
+ {"EventId": "120171",
4
+ "IdTeam": "43942",
5
+ "IdPlayer": "95982",
6
+ "Timestamp": "2022-11-14T10:47:50.8028496Z",
7
+ "MatchMinute": "120'",
8
+ "Period": 11,
9
+ "HomeGoals": 2,
10
+ "AwayGoals": 2,
11
+ "Type": 60,
12
+ "Qualifiers": [],
13
+ "TypeLocalized": [{"Locale": "en-GB", "Description": "Penalty missed"}],
14
+ "HomePenaltyGoals": 4,
15
+ "AwayPenaltyGoals": 3,
16
+ "EventDescription":
17
+ [{"Locale": "en-GB",
18
+ "Description":
19
+ "David BATTY (England) misses from the penalty spot!"}]}],
20
+ "Properties": {},
21
+ "IsUpdateable": null}
22
+ =end
23
+
24
+
25
+ def build_penalty( h, players:,
26
+ team1_id:, team2_id: )
27
+
28
+ ## note - do NOT care about minutes in penalty shootout
29
+ ## check for period == 11 (penalty shotout!!)
30
+ ## period = h['Period']
31
+
32
+
33
+ ##########
34
+ ## 0 - goal (when penalty awared used before)
35
+ ## 41 - penalty goal
36
+ ## 46 - penalty missed
37
+ ## Dragan STOJKOVIC (Yugoslavia) rattles the crossbar from the spot!
38
+ ## 51 - penalty missed
39
+ ## JULIO CESAR (Brazil) hits the post from the spot!
40
+ ## 60 - penalty missed
41
+ ## COMAN (France) sees his penalty saved by the goalkeeper.
42
+ ## Maxime BOSSIS (France) misses from the penalty spot! and many more
43
+ ## 65 - penalty missed
44
+ ## TCHOUAMENI (France) misses from the penalty spot!
45
+
46
+ type = h['Type'] ## (timeline) event type
47
+
48
+ assert( [0,41,46,51,60,65].include?(type),
49
+ "event type 0/41/46/51/60/65 expected; got #{type}")
50
+
51
+
52
+ team = if h['IdTeam'] == team1_id
53
+ 1
54
+ elsif h['IdTeam'] == team2_id
55
+ 2
56
+ else
57
+ assert( false, "team1/2_id #{team1_id/team2_id} expected; got #{h.pretty_inspect}")
58
+ end
59
+
60
+ rec = { team: team, ## 1|2
61
+ score: [h['HomePenaltyGoals'],
62
+ h['AwayPenaltyGoals']],
63
+ scored: [0,41].include?( type ) ? true : false,
64
+ }
65
+
66
+ idPlayer = h['IdPlayer']
67
+
68
+
69
+ =begin
70
+ -- try IdSubPlayer - why? why not?
71
+ IdSubPlayer is goalkeeper!!!
72
+ ## what is SubPlayer/SubTeam in event??
73
+ !! no idPlayer for penaltyl!
74
+ {"EventId"=>"17807200001266",
75
+ "IdTeam"=>"1884426",
76
+ "IdSubPlayer"=>"408948",
77
+ "IdSubTeam"=>"1897032",
78
+ =end
79
+
80
+ if idPlayer.nil?
81
+ puts "!! no idPlayer for penalty!"
82
+ pp h
83
+ ##exit 1
84
+ ## use 'N.N.'
85
+
86
+ rec[ :name ] = 'N.N.'
87
+
88
+ else
89
+ rec[ :name ] = players.find!( idPlayer )[:name]
90
+ end
91
+
92
+ rec[:meta] = { type: type,
93
+ desc: desc(h['EventDescription']) ## add event desc too - why? why not?
94
+ }
95
+ rec
96
+ end
97
+
98
+
99
+
100
+
101
+ def build_penalties( recs, players:,
102
+ team1_id:, team2_id: )
103
+ ################
104
+ ## type:
105
+ ## check for "old/legacy" format used where?
106
+ ## not using period 11 ???
107
+ ## incl. weirdo format with
108
+ ## 6 - penalty awarded
109
+ ## 0 - goal or 60 - penalty missed!
110
+ ##
111
+ ## 2 - yellow card
112
+ ## 3 - red card
113
+ ## 7 - start time
114
+ ## 8 - end time
115
+
116
+ recs = recs.select do |h|
117
+ if h['Period'] == 11 ## penalty shoot-out
118
+ assert( [0,2,3,6,7,8,41,46,51,60,65].include?( h['Type'] ),
119
+ "expected event type 2/3/7/8/41/46/51/60/65 for pens; got #{h.pretty_inspect}"
120
+ )
121
+ [0,41,46,51,60,65].include?( h['Type'] ) ? true : false
122
+ else
123
+ false
124
+ end
125
+ end
126
+
127
+
128
+ recs = recs.map { |h| build_penalty( h, players: players,
129
+ team1_id: team1_id,
130
+ team2_id: team2_id ) }
131
+ recs
132
+ end