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.
@@ -33,7 +33,10 @@
33
33
 
34
34
 
35
35
 
36
- def _build_sub_minute( h )
36
+ class Sub ## Substitution/SubOnOff Event
37
+
38
+
39
+ def self._build_sub_minute( h )
37
40
 
38
41
  ## split into minute
39
42
  ## and offset (stoppage/injury/added time)
@@ -69,39 +72,111 @@ def _build_sub_minute( h )
69
72
  end
70
73
 
71
74
 
72
-
73
- def build_sub( h, players: )
75
+ def self.build( h, players: )
74
76
 
75
77
  minute = _build_sub_minute( h )
76
78
 
77
79
  playerOff = players.find( h['IdPlayerOff'] )
78
80
  playerOn = players.find( h['IdPlayerOn'] )
79
81
 
82
+ ## fix-fix-fix
83
+ ## report warning if playerON nil!!!
80
84
  ## use N.N. for nil - why? why not?
81
- playerOn = { name: 'N.N.'} if playerOn.nil?
85
+ playerOn = Player::PLAYER_NIL if playerOn.nil?
82
86
 
83
87
  assert( playerOff && playerOn,
84
88
  "playerOff and/or playerOn is NIL in #{h.pretty_inspect}")
85
89
 
86
- rec = { off: playerOff[ :name ],
87
- on: playerOn[ :name ],
90
+ rec = { off: playerOff,
91
+ on: playerOn,
88
92
  minute: minute
89
93
  }
90
94
 
91
- rec
95
+ new( **rec )
92
96
  end
93
97
 
94
98
 
95
99
 
96
- def build_subs( recs, players: )
97
- recs = recs.map { |h| build_sub( h, players: players ) }
100
+ def self._build_ary( recs, players: )
101
+ recs = recs.map { |h| build( h, players: players ) }
98
102
  recs
99
103
  end
100
104
 
101
105
 
106
+
107
+ attr_reader :off, :on, :minute
108
+
109
+ def initialize( off:, on:, minute: )
110
+ @off = off
111
+ @on = on
112
+ @minute = minute
113
+ end
114
+
115
+ def as_json(*)
116
+ h = {
117
+ 'off' => off.name,
118
+ 'on' => on.name,
119
+ 'minute' => minute
120
+ }
121
+ h
122
+ end
123
+
124
+ end ## class Sub(stitution)
125
+
126
+
127
+
128
+
129
+
102
130
  __END__
103
131
 
104
132
 
133
+
134
+ =begin
135
+ def add_subs( subs )
136
+ subs.each do |sub|
137
+
138
+ idPlayerOff = sub['IdPlayerOff']
139
+ idPlayerOn = sub['IdPlayerOn']
140
+
141
+ ## note - parse & reformat minute for keep same format
142
+ minute = _build_sub_minute( sub )
143
+
144
+ player_off = @recs[ idPlayerOff ]
145
+ player_on = @recs[ idPlayerOn ]
146
+
147
+ ##
148
+ ## note - skip special case for now
149
+ ## with NO PLAYER ON (e.g. idPlayerOn is nil!!)
150
+ ## next if idPlayerOn.nil?
151
+ ## todo/fix - report/log warning!!!
152
+
153
+
154
+ if player_off.nil?
155
+ puts "!! player_off >#{idPlayerOff}< not found in:"
156
+ pp sub
157
+ puts "---"
158
+ pp @recs.values
159
+ exit 1
160
+ end
161
+
162
+ if player_on.nil?
163
+ puts "!! player_on >#{idPlayerOn}< not found in:"
164
+ pp sub
165
+ puts "---"
166
+ pp @recs.values
167
+ exit 1
168
+ end
169
+
170
+ assert( player_off && player_on,
171
+ "subs player_off or player_on not found; sorry" )
172
+
173
+ player_off[ :sub ] = { minute: minute,
174
+ player_ref: player_on }
175
+ end
176
+ end
177
+ =end
178
+
179
+
105
180
  !! ASSERT FAILED - playerOff and/or playerOn is NIL in {"IdEvent"=>nil,
106
181
  "Period"=>5,
107
182
  "Reason"=>0,
@@ -0,0 +1,85 @@
1
+
2
+
3
+ class Team
4
+
5
+
6
+ def self.build( h )
7
+ if h
8
+ name = desc( h['TeamName'] )
9
+ ## name = norm_team( name )
10
+
11
+ new( id: h['IdTeam'],
12
+ name: name,
13
+ code: h['Abbreviation'],
14
+ country: h['IdCountry'] ## change to cc (country code) - why? why not?
15
+ )
16
+ else ## assume nil - dummy record
17
+ TEAM_NIL
18
+ end
19
+ end
20
+
21
+
22
+
23
+ attr_reader :id, :name, :code, :country
24
+
25
+ attr_accessor :count
26
+
27
+ def initialize( name:,
28
+ id: nil,
29
+ code: nil, country: nil )
30
+ @name = name
31
+ @id = id
32
+ @code = code
33
+ @country = country
34
+
35
+ @count = Hash.new(0) ## match (reference/usage) count(er) by stage (name)!!
36
+ end
37
+
38
+
39
+ ## shared dummy/nil record
40
+ TEAM_NIL = self.new( id: '<nil>',
41
+ name: '?',
42
+ code: '?', ## use nil - why? why not?
43
+ country: '?' ) ## use nil - why? why not?
44
+
45
+
46
+
47
+ =begin
48
+ !! ASSERT FAILED - team records NOT matching -
49
+ {:id=>"44129",
50
+ :name=>"Vasco da Gama",
51
+ :code=>"VDG",
52
+ :country=>"BRA",
53
+ :count=>7}
54
+ !=
55
+ {:id=>"44129",
56
+ :name=>"Vasco da Gama",
57
+ :code=>"VAS",
58
+ :country=>"BRA"}
59
+ =end
60
+
61
+
62
+ def ==(other)
63
+ return false unless other.is_a?(Team)
64
+
65
+ ## check code too - why? why not?
66
+ ## note - ignore code for now
67
+ self.id == other.id &&
68
+ self.name == other.name &&
69
+ self.country == other.country
70
+ end
71
+
72
+
73
+ def as_json(*)
74
+ h = {
75
+ 'name' => name,
76
+ 'code' => code,
77
+ 'country' => country,
78
+ 'count' => count,
79
+ 'id' => id
80
+ }
81
+ h
82
+ end
83
+
84
+
85
+ end # class Team
@@ -0,0 +1,140 @@
1
+ #
2
+ # note - players here are for lineup (subs+bookings) and incl. starter/bench AND pos etc.
3
+ # do NOT use for team squads
4
+
5
+
6
+
7
+
8
+ class Players
9
+ def initialize
10
+ @recs = {}
11
+ end
12
+
13
+ def add( recs ) ## rename to collect - why? why not?
14
+ recs.each do |rec|
15
+ raise ArgumentError,
16
+ "player (obj) expected; got #{rec.class.name} - #{rec.inspect}" unless rec.is_a?( Player )
17
+
18
+ rec = _add( rec )
19
+ rec.count += 1 ## track usage - why? why not?
20
+ end
21
+ end
22
+
23
+
24
+ def _add( new_rec )
25
+ rec = @recs[ new_rec.id ]
26
+
27
+ if rec.nil?
28
+ rec = new_rec
29
+ @recs[ new_rec.id ] = new_rec
30
+ else
31
+ ## assert attributes equal - why? why not?
32
+ assert( new_rec == rec,
33
+ "player records NOT matching - #{rec.pretty_inspect} != #{new_rec.pretty_inspect}")
34
+ end
35
+
36
+ rec
37
+ end
38
+
39
+
40
+
41
+ def find( id_player )
42
+ rec = @recs[ id_player ]
43
+ rec
44
+ end
45
+
46
+ def find!( id_player )
47
+ rec = @recs[ id_player ]
48
+ raise ArgumentError, "no player w/ id >#{id_player}< found; sorry" if rec.nil?
49
+ rec
50
+ end
51
+
52
+
53
+
54
+
55
+ ###
56
+ ## todo/check
57
+ ## add alias (or rename) starter ??
58
+ ## add new subs to status == 2 - why? why not?
59
+ def lineup
60
+ @recs.values.select { |rec| rec.starter? }
61
+ end
62
+
63
+ def bench
64
+ @recs.values.select { |rec| rec.bench? }
65
+ end
66
+
67
+
68
+ def redcards
69
+ @recs.values.select { |rec| rec.red? }
70
+ .map { |rec| rec.r }
71
+ end
72
+
73
+ def yellowcards
74
+ @recs.values.select { |rec| rec.yellow? }
75
+ .map { |rec| rec.y }
76
+ end
77
+
78
+ def yellowredcards
79
+ @recs.values.select { |rec| rec.yellowred? }
80
+ .map { |rec| rec.yr }
81
+ end
82
+
83
+
84
+
85
+
86
+ def add_bookings( bookings ) ## yellow/red cards
87
+ bookings.each do |b|
88
+
89
+ card = b['Card']
90
+ assert( [0,1,2,3].include?( card ), "card 0/1/2/3 expected; got #{b.pretty_inspect}")
91
+
92
+ ##
93
+ ## what is card 0? ignore for now
94
+ ## Palmeiras v FC Porto 0-0 - 2025-06-15T18:00:00+00:00
95
+ ## !! ASSERT FAILED - card 1/2/3 expected; got {"Card"=>0,
96
+ ## "Period"=>5,
97
+ ## "IdEvent"=>nil,
98
+ ## "EventNumber"=>nil,
99
+ ## "IdPlayer"=>"495048",
100
+ ## "IdCoach"=>nil,
101
+ ## "IdTeam"=>"1884426",
102
+ ## "Minute"=>"72'",
103
+ ## "Reason"=>nil}
104
+ next if card == 0
105
+
106
+
107
+ idPlayer = b['IdPlayer']
108
+
109
+ ## booking (card) for coach or stuff!!!!
110
+ ## skip for now
111
+ next if idPlayer.nil? && (b['IdCoach'] || b['IdStaff'])
112
+
113
+
114
+ player = @recs[ idPlayer ]
115
+ assert( player, "booking player not found; sorry - #{b.pretty_inspect}" )
116
+
117
+ ## note - parse & reformat minute for keep same format
118
+ ## _fmt_minute( *_parse_minute( b['Minute'] ))
119
+ minute = _build_minute( b )
120
+
121
+ if card == 1 ## yellow
122
+ player.y = Player::Card.new( player: player, type: 'Y', minute: minute )
123
+ elsif card == 2 ## red
124
+ player.r = Player::Card.new( player: player, type: 'R', minute: minute )
125
+ elsif card == 3 ## yellow/red
126
+ player.yr = Player::Card.new( player: player, type: 'YR', minute: minute )
127
+ end
128
+ end
129
+ end
130
+
131
+
132
+
133
+ def dump
134
+ pp @recs.values
135
+ puts " #{@recs.size} player(s)"
136
+ end
137
+
138
+ def size() @recs.size; end
139
+
140
+ end # class Players
@@ -0,0 +1,71 @@
1
+
2
+
3
+
4
+ class Stadiums
5
+ def initialize
6
+ @recs = {}
7
+ end
8
+
9
+ def add_matches( matches ) ## rename to add_matches - why? why not?
10
+ matches.each_with_index do |m|
11
+ stadium = Stadium.build( m['Stadium'] )
12
+ rec = _add( stadium )
13
+
14
+ ## update match counter by team
15
+ team_name = desc(m['Home']['TeamName'])
16
+ rec.count[ team_name ] += 1
17
+ end
18
+ end
19
+
20
+
21
+
22
+ def _add( new_rec )
23
+ rec = @recs[ new_rec.id ]
24
+ if rec.nil?
25
+ rec = new_rec
26
+ @recs[ new_rec.id] = new_rec
27
+ else
28
+ ## assert attributes equal - why? why not?
29
+ assert( new_rec == rec,
30
+ "stadium records NOT matching - #{rec.pretty_inspect} != #{new_rec.pretty_inspect}")
31
+ end
32
+ rec
33
+ end
34
+
35
+
36
+
37
+ def find!( h )
38
+ if h
39
+ name = desc( h['Name'] )
40
+ city = desc( h['CityName'] )
41
+ ## auto-generate id
42
+ ## use slug of name plus id_city
43
+ ## add city name
44
+ ## fix- add 0-9 & dash(-) to name too - why? why not?
45
+ id = name.downcase.gsub( /[^a-z]/, '' )
46
+ id += "_" + city.downcase.gsub( /[^a-z]/, '' )
47
+ else
48
+ puts "warn: empty stadium lookup:"
49
+ pp h
50
+ id = '<nil>'
51
+ end
52
+
53
+ ## check for nil - why? why not?
54
+ rec = @recs[ id ]
55
+ if rec.nil?
56
+ puts "#{@recs.size} stadiums:"
57
+ pp @recs.keys
58
+ raise ArgumentError, "stadium not found using >#{id}< - #{h.pretty_inspect}"
59
+ end
60
+ rec
61
+ end
62
+
63
+
64
+
65
+ def as_json( id: false ) ## note ignore remove id for now
66
+ @recs.values.as_json
67
+ end
68
+
69
+ def size() @recs.size; end
70
+
71
+ end # class Stadiums
@@ -0,0 +1,58 @@
1
+
2
+
3
+
4
+ class Stages
5
+
6
+ def self.read( path )
7
+ data = read_json_v2( path )
8
+ obj = new
9
+ obj.add( data['Results'] )
10
+ obj
11
+ end
12
+
13
+
14
+ def initialize
15
+ @recs = {}
16
+ end
17
+
18
+ def add( recs ) ## rename to collect - why? why not?
19
+ recs.each { |h| _add( Stage.build(h)) }
20
+ end
21
+
22
+
23
+ def _add( new_rec )
24
+ rec = @recs[ new_rec.name ]
25
+ if rec.nil?
26
+ @recs[ new_rec.name ] = new_rec
27
+ else
28
+ raise ArgumentError, "duplicate stage entry: #{new_rec.pretty_inspect}"
29
+ end
30
+ end
31
+
32
+
33
+ ## add match stats
34
+ def add_matches( matches )
35
+ matches.each do |m|
36
+ name = desc( m['StageName'] )
37
+ rec = find!( name )
38
+ rec.count += 1
39
+ end
40
+ end
41
+
42
+
43
+ def find!( name )
44
+ rec = @recs[ name ]
45
+ raise ArgumentError, "no stage w/ name >#{name}< found; sorry" if rec.nil?
46
+ rec
47
+ end
48
+
49
+ def as_json(*)
50
+ ## note - sort by seq
51
+ @recs.values.sort do |l,r|
52
+ l.seq <=> r.seq
53
+ end.as_json
54
+ end
55
+
56
+ def size() @recs.size; end
57
+
58
+ end # class Stages
@@ -0,0 +1,57 @@
1
+
2
+
3
+
4
+ class Teams ## use/change to TeamsRegistry, TeamsLookup, etc
5
+ def initialize
6
+ @recs = {}
7
+ end
8
+
9
+ def add_matches( matches )
10
+ ## note - if team nil - will (auto-)add nil team (<nil>,?,?,?)
11
+ matches.each do |m|
12
+ rec1 = _add( Team.build( m['Home'] ) )
13
+ rec2 = _add( Team.build( m['Away'] ) )
14
+
15
+ stage_name = desc(m['StageName'])
16
+ rec1.count[stage_name] += 1
17
+ rec2.count[stage_name] += 1
18
+ end
19
+ end
20
+
21
+
22
+
23
+ def _add( new_rec )
24
+ rec = @recs[ new_rec.id ]
25
+ if rec.nil?
26
+ rec = new_rec
27
+ @recs[ new_rec.id ] = new_rec
28
+ else
29
+ ## assert attributes equal - why? why not?
30
+ assert( new_rec == rec,
31
+ "team records NOT matching - #{rec.pretty_inspect} != #{new_rec.pretty_inspect}")
32
+ end
33
+ rec
34
+ end
35
+
36
+
37
+ def find!( h )
38
+ ## team1 = teams.find( m['HomeTeam'] )
39
+ ## team2 = teams.find( m['AwayTeam'] )
40
+ ## use lookup by "IdTeam": "33161" for now - why? why not?
41
+
42
+ ## check for nil - why? why not?
43
+ rec = @recs[ h['IdTeam'] ]
44
+ raise ArgumentError, "team not found using #{h.pretty_inspect} " if rec.nil?
45
+ rec
46
+ end
47
+
48
+
49
+
50
+
51
+ def as_json( id: false ) ## note ignore remove id for now
52
+ @recs.values.as_json
53
+ end
54
+
55
+ def size() @recs.size; end
56
+
57
+ end # class Teams
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fifadat-convert
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-09-24 00:00:00.000000000 Z
11
+ date: 2026-09-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fifadat
@@ -74,26 +74,30 @@ files:
74
74
  - Rakefile
75
75
  - bin/fifadat
76
76
  - lib/fifadat/convert.rb
77
- - lib/fifadat/convert/build_match.rb
78
- - lib/fifadat/convert/build_report.rb
79
77
  - lib/fifadat/convert/convert-reports.rb
80
78
  - lib/fifadat/convert/convert.rb
81
79
  - lib/fifadat/convert/errata.rb
82
- - lib/fifadat/convert/goals-calc_score.rb
83
- - lib/fifadat/convert/goals.rb
84
80
  - lib/fifadat/convert/helper-date.rb
85
81
  - lib/fifadat/convert/helper-minute.rb
86
82
  - lib/fifadat/convert/helper-score.rb
87
83
  - lib/fifadat/convert/norm.rb
88
- - lib/fifadat/convert/officials.rb
89
- - lib/fifadat/convert/penalties.rb
90
- - lib/fifadat/convert/players.rb
91
- - lib/fifadat/convert/stadiums.rb
92
- - lib/fifadat/convert/stages.rb
93
- - lib/fifadat/convert/substitutions.rb
94
- - lib/fifadat/convert/teams.rb
84
+ - lib/fifadat/convert/reader.rb
95
85
  - lib/fifadat/convert/tool.rb
96
86
  - lib/fifadat/convert/version.rb
87
+ - lib/fifadat/models/goal.rb
88
+ - lib/fifadat/models/match.rb
89
+ - lib/fifadat/models/match_report.rb
90
+ - lib/fifadat/models/official.rb
91
+ - lib/fifadat/models/penalty.rb
92
+ - lib/fifadat/models/player.rb
93
+ - lib/fifadat/models/stadium.rb
94
+ - lib/fifadat/models/stage.rb
95
+ - lib/fifadat/models/substitution.rb
96
+ - lib/fifadat/models/team.rb
97
+ - lib/fifadat/tables/players.rb
98
+ - lib/fifadat/tables/stadiums.rb
99
+ - lib/fifadat/tables/stages.rb
100
+ - lib/fifadat/tables/teams.rb
97
101
  - lib/fifadata-convert.rb
98
102
  homepage: https://github.com/sportdb/sport.db
99
103
  licenses:
@@ -1,105 +0,0 @@
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