fbtxt-pp 0.0.2

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.
@@ -0,0 +1,26 @@
1
+
2
+ ###
3
+ # officials (referees)
4
+
5
+
6
+ class Official
7
+
8
+ def self.build( h )
9
+ new(
10
+ name: h['name'],
11
+ country: h['country'],
12
+ type: h['type']
13
+ )
14
+ end
15
+
16
+ attr_reader :name, :country,
17
+ :type
18
+
19
+ def initialize( name:,
20
+ country: nil,
21
+ type: nil )
22
+ @name = name
23
+ @country = country
24
+ @type = type
25
+ end
26
+ end ## class Official
@@ -0,0 +1,29 @@
1
+
2
+
3
+ class Penalty
4
+ def self.build( h )
5
+
6
+ new( name: h['name'],
7
+ scored: h['scored'], ## true|false for now only
8
+ score: h['score'],
9
+ team: h['team'] ## 1|2
10
+ )
11
+ end
12
+
13
+
14
+ attr_reader :name,
15
+ :scored, :score, :team
16
+
17
+ def initialize( name:,
18
+ scored:, score:,
19
+ team: )
20
+ @name = name
21
+ @scored = scored
22
+ @score = score ## maybe allow empty score - why? why not?
23
+
24
+ @team = team
25
+ end
26
+
27
+ def scored?() @scored; end
28
+
29
+ end # class Penalty
@@ -0,0 +1,274 @@
1
+
2
+
3
+ class Player
4
+
5
+ def self.build( h )
6
+
7
+ ## rec = { id: h['IdPlayer'],
8
+ ##
9
+ ## status: h['Status'],
10
+ ## pos: h['Position'],
11
+
12
+ new(
13
+ name: h['name'],
14
+ short_name: h['short_name'],
15
+ captain: h['captain'],
16
+ id: h['id']
17
+ )
18
+ end
19
+
20
+
21
+
22
+ attr_reader :id,
23
+ :name, :short_name,
24
+ :alt_names,
25
+ :captain, ## true|false
26
+ :pos,
27
+ :y, :yr, :r, ## cards/bookings - yellow, yellow-red, red
28
+ :sub ## (recursive) sub(stitution) record
29
+
30
+ attr_accessor :status ## e.g. 1-starter, 2-bench
31
+
32
+
33
+
34
+ def initialize( name:, short_name: nil,
35
+ captain: false,
36
+ id: nil )
37
+ @alt_names = []
38
+
39
+ norm = norm_name( name )
40
+ if norm != name
41
+ puts " NORM PLAYER NAME >#{name}< => >#{norm}<"
42
+ @alt_names << name
43
+ name = norm
44
+ end
45
+
46
+ @name = name
47
+ @short_name = short_name
48
+
49
+ @captain = captain
50
+
51
+ @id = id
52
+
53
+ @pos = nil ## fix-fix-fix - add pos(ition)
54
+ @status = nil ## e.g. starter/bench|sub/etc.
55
+
56
+ ## todo/check - add "generic" sentoff for pre-cards era - why? why not?
57
+ @y, @yr, @r = nil,nil,nil
58
+
59
+ @sub = nil
60
+ end
61
+
62
+ ## get lookup keys (id+name+alt_names)
63
+ def keys
64
+ keys = []
65
+ keys << @id if @id
66
+ keys << slugify(@name)
67
+ keys += @alt_names.map {|name| slugify(name)} unless @alt_names.empty?
68
+
69
+ ## note - make sure keys are unique
70
+ keys.uniq
71
+ end
72
+
73
+
74
+
75
+
76
+ def captain?() @captain; end
77
+ def starter?() @status == 1; end
78
+ def bench?() @status == 2; end
79
+ ## add is_ variants - why? why not?
80
+ alias_method :is_starter?, :starter?
81
+ alias_method :is_bench?, :bench?
82
+
83
+
84
+ def yellow?() @y; end
85
+ def yellowred?() @yr; end
86
+ def red?() @r; end
87
+
88
+
89
+ class Booking ## (nested) booking record/struct
90
+ ## add :reason or such later
91
+ attr_reader :minute
92
+ def initialize( minute: nil )
93
+ @minute = minute
94
+ end
95
+ end ## (nested) booking record/struct
96
+
97
+ def add_yellow( minute: nil ) @y = Booking.new( minute: minute ); end
98
+ def add_yellowred( minute: nil ) @yr = Booking.new( minute: minute ); end
99
+ def add_red( minute: nil ) @r = Booking.new( minute: minute ); end
100
+
101
+
102
+
103
+ class Sub ## (nested) sub(stitution) record/struct
104
+ attr_reader :minute,
105
+ :player
106
+ def initialize( player:,
107
+ minute: nil )
108
+ @player = player
109
+ @minute = minute
110
+ end
111
+ end ## (nested) class Sub
112
+
113
+ def add_sub( player:, minute: nil )
114
+ @sub = Sub.new( player: player,
115
+ minute: minute )
116
+ end
117
+ end ## class Player
118
+
119
+
120
+
121
+
122
+
123
+
124
+
125
+ class Players
126
+ def initialize
127
+ @recs = []
128
+ @lookup = {}
129
+ end
130
+
131
+
132
+ def add_starter( recs )
133
+ recs.each do |h|
134
+ rec = Player.build( h )
135
+ rec.status = 1 ## set status to starter flag (1)
136
+ _add( rec )
137
+ end
138
+ end
139
+
140
+ def add_bench( recs )
141
+ recs.each do |h|
142
+ rec = Player.build( h )
143
+ rec.status = 2
144
+ _add( rec )
145
+ end
146
+ end
147
+
148
+
149
+ ###
150
+ # note:
151
+ # PAK Nam Chol - North Korea is two players with same name in the same team!!
152
+ # with different jersey number 4/14
153
+
154
+ def _add( new_rec )
155
+ @recs << new_rec
156
+
157
+ new_rec.keys.each do |key|
158
+ rec = @lookup[ key ]
159
+ if rec.nil?
160
+ @lookup[ key ] = new_rec
161
+ else
162
+ if key == 'paknamchol' ||
163
+ key == 'deniskovalevich'
164
+ ## ignore for now
165
+ ## fix later (use/prefer numeric ids!!!)
166
+ else
167
+ raise ArgumentError,
168
+ "duplicate player records #{rec.pretty_inspect} == #{new_rec.pretty_inspect}"
169
+ end
170
+ end
171
+ end
172
+ end
173
+
174
+
175
+ def find( q )
176
+ key = slugify( q )
177
+ @lookup[ key ]
178
+ end
179
+
180
+
181
+
182
+ def add_subs( subs )
183
+ subs.each do |sub|
184
+
185
+ player_off = find( sub['off'] )
186
+ player_on = find( sub['on'] )
187
+
188
+ minute = sub['minute']
189
+
190
+ if player_off.nil?
191
+ puts "!! player_off not found in:"
192
+ pp sub
193
+ puts "---"
194
+ pp @recs
195
+ exit 1
196
+ end
197
+
198
+ if player_on.nil?
199
+ ### quick fix for N.N.
200
+ if sub['on'] == 'N.N.'
201
+ player_on = Player.build( { 'name' => 'N.N.'} )
202
+ else
203
+ puts "!! player_on not found in:"
204
+ pp sub
205
+ puts "---"
206
+ pp @recs
207
+ exit 1
208
+ end
209
+ end
210
+
211
+ assert( player_off && player_on,
212
+ "subs player_off or player_on not found; sorry" )
213
+
214
+ player_off.add_sub( player: player_on,
215
+ minute: minute )
216
+ end
217
+ end
218
+
219
+
220
+ def add_yellow( bookings) _add_bookings( bookings, type: 'y' ); end
221
+ def add_yellowred( bookings) _add_bookings( bookings, type: 'yr' ); end
222
+ def add_red( bookings) _add_bookings( bookings, type: 'r' ); end
223
+
224
+ def _add_bookings( bookings, type: )
225
+ bookings.each do |b|
226
+
227
+ ## note - ignores cards coach and stuff for now upstream!!!
228
+ player = find( b['name'])
229
+ assert( player, "booking player not found; sorry- #{b.pretty_inspect}" )
230
+
231
+ minute = b['minute']
232
+
233
+ case type.to_sym
234
+ when :y then player.add_yellow( minute: minute )
235
+ when :r then player.add_red( minute: minute )
236
+ when :yr then player.add_yellowred( minute: minute )
237
+ else
238
+ raise ArgumentError,
239
+ "expected :y|:r|:yr - unknown card type for booking: #{b.pretty_inspect}"
240
+ end
241
+ end
242
+ end
243
+
244
+
245
+
246
+ ###
247
+ ## todo/check
248
+ ## add alias (or rename) starter ??
249
+ ## add new subs to status == 2 - why? why not?
250
+ def lineup
251
+ recs = @recs.select { |rec| rec.starter? }
252
+ recs
253
+ end
254
+
255
+
256
+ =begin
257
+ def find!( id_player )
258
+ rec = @recs[ id_player ]
259
+ raise ArgumentError, "no player w/ id >#{id_player}< found; sorry" if rec.nil?
260
+ rec
261
+ end
262
+
263
+
264
+ ## all players with red or red-yellow card (sent off)
265
+ def sentoff
266
+ recs = @recs.values.select { |rec| rec[:r] || rec[:yr] }
267
+ recs
268
+ end
269
+ =end
270
+
271
+
272
+ def size() @recs.size; end
273
+
274
+ end # class Players
@@ -0,0 +1,85 @@
1
+
2
+ class Score
3
+
4
+
5
+ def self.build( score )
6
+ if score.nil?
7
+ raise ArgumentError, "Score.build - expected Hash or Array; got nil"
8
+ elsif score.is_a?(Hash)
9
+ new( **score.transform_keys(&:to_sym) )
10
+ elsif score.is_a?(Array)
11
+ new( reported: score )
12
+ else
13
+ raise ArgumentError, "Score.build - expected Hash or Array; got type #{score} #{score.class.name}"
14
+ end
15
+ end
16
+
17
+
18
+
19
+ attr_reader :ht, :ft, :et, :p, :agg,
20
+ :reported
21
+
22
+
23
+ def initialize( ht: nil, ft: nil, et: nil, p: nil, agg: nil,
24
+ reported: nil,
25
+ score: nil )
26
+ @ht, @ft, @et, @p, @agg = ht, ft, et, p, agg
27
+
28
+ ##
29
+ ## note: use reported for "generic" score where
30
+ ## period is not known (might be full-time or aet)
31
+ ## or undefined e.g. for abandoned or awarded (administered) score
32
+ @reported = reported
33
+
34
+ if score
35
+ puts " DEPRECATED - score got score keyword (use reported):"
36
+ pp [ ht, ft, et, p, agg, reported, score]
37
+ @reported = score
38
+ end
39
+ end
40
+
41
+
42
+
43
+ def to_s
44
+ if @reported
45
+ ## check for ary empty - why? why not?
46
+ "#{@reported[0]}-#{@reported[1]}"
47
+ else
48
+ if @et && @p
49
+ if @ft && @ht
50
+ "#{@et[0]}-#{@et[1]} a.e.t." +
51
+ " (#{@ft[0]}-#{@ft[1]}, #{@ht[0]}-#{@ht[1]}), " +
52
+ "#{@p[0]}-#{@p[1]} pen."
53
+ elsif @ft
54
+ "#{@et[0]}-#{@et[1]} a.e.t." +
55
+ " (#{@ft[0]}-#{@ft[1]}), " +
56
+ "#{@p[0]}-#{@p[1]} pen."
57
+ else
58
+ "#{@et[0]}-#{@et[1]} a.e.t., " +
59
+ "#{@p[0]}-#{@p[1]} pen."
60
+ end
61
+ elsif @et && @p.nil?
62
+ if @ft && @ht
63
+ "#{@et[0]}-#{@et[1]} a.e.t."+
64
+ " (#{@ft[0]}-#{@ft[1]}, #{@ht[0]}-#{@ht[1]})"
65
+ elsif @ft
66
+ "#{@et[0]}-#{@et[1]} a.e.t."+
67
+ " (#{@ft[0]}-#{@ft[1]})"
68
+ else
69
+ "#{@et[0]}-#{@et[1]} a.e.t."
70
+ end
71
+ elsif @ft && @et.nil? && @p.nil?
72
+ if @ht
73
+ "#{@ft[0]}-#{@ft[1]} (#{@ht[0]}-#{@ht[1]})"
74
+ else
75
+ "#{@ft[0]}-#{@ft[1]}"
76
+ end
77
+ else
78
+ ## raise error/warn - about unknown (or empty) score type - why? why not?
79
+ ''
80
+ end
81
+ end
82
+ end
83
+
84
+
85
+ end # class Score
@@ -0,0 +1,114 @@
1
+
2
+
3
+ class Stadium
4
+
5
+ def self.build( h )
6
+ if h['name'].nil?
7
+ puts "stadium without name:"
8
+ pp h
9
+ exit 1
10
+ end
11
+
12
+ new( name: h['name'],
13
+ city: h['city'],
14
+ country: h['country'],
15
+ street: h['street']
16
+ )
17
+ end
18
+
19
+
20
+ attr_reader :id,
21
+ :name, :city, :street,
22
+ :country
23
+
24
+ def initialize( name:, city:, street: nil,
25
+ country: nil,
26
+ id: nil)
27
+
28
+ ## name, city_name = norm_stadium( name, city_name: city_name )
29
+
30
+ if id.nil?
31
+ ## auto-generate id
32
+ ## use slug of name plus id_city
33
+ ## add city name
34
+ id = slugify( name )
35
+ id += '_' + slugify( city )
36
+ end
37
+
38
+ @id = id
39
+
40
+ @name = name
41
+ @city = city
42
+ @street = street
43
+ @country = country
44
+ end
45
+ end ## class Stadium
46
+
47
+
48
+
49
+
50
+
51
+ class Stadiums
52
+ def initialize
53
+ @recs = {}
54
+ @by_city = {} ## track by cities
55
+ end
56
+
57
+
58
+ def add( recs )
59
+ recs.each do |rec|
60
+ _add( Stadium.build( rec ) )
61
+ end
62
+ end
63
+
64
+
65
+ def _add( new_rec )
66
+ rec = @recs[ new_rec.id ]
67
+ if rec.nil?
68
+ rec = new_rec
69
+ @recs[ new_rec.id ] = new_rec
70
+
71
+ city_rec = @by_city[ new_rec.city] ||= []
72
+ city_rec << new_rec
73
+ else
74
+ raise ArgumentError,
75
+ "duplicate stadium records #{rec.pretty_inspect} == #{new_rec.pretty_inspect}"
76
+ end
77
+ end
78
+
79
+
80
+ def find!( h )
81
+ ## quick & dirty verion
82
+ name = h['name']
83
+ city = h['city']
84
+
85
+ id = slugify( name )
86
+ id += '_' + slugify( city )
87
+
88
+ rec = @recs[ id ]
89
+ raise ArgumentError, "no stadium found for #{h.inspect} using id >#{id}<" if rec.nil?
90
+ rec
91
+ end
92
+
93
+
94
+
95
+ def each( sort: true, &blk )
96
+ recs = if sort
97
+ @recs.values.sort do |l,r|
98
+ res = l.country <=> r.country
99
+ res = l.city <=> r.city if res == 0
100
+ res
101
+ end
102
+ else
103
+ @recs.values
104
+ end
105
+
106
+ recs.each( &blk )
107
+ end
108
+
109
+
110
+ def size() @recs.size; end
111
+
112
+ def cities() @by_city.keys; end
113
+
114
+ end # class Stadiums
@@ -0,0 +1,161 @@
1
+
2
+
3
+
4
+
5
+ TEAM_MODS = {
6
+ ## nati(onal) teams (e.g. world cup)
7
+ ## map "offical" country names to common country names
8
+ 'Germany FR' => 'West Germany',
9
+ 'German DR' => 'East Germany',
10
+ 'Korea Republic' => 'South Korea',
11
+ 'Korea DPR' => 'North Korea',
12
+ 'China PR' => 'China',
13
+ 'Republic of Ireland' => 'Ireland',
14
+ 'IR Iran' => 'Iran',
15
+ 'United States' => 'USA',
16
+ 'Czechia' => 'Czech Republic',
17
+ 'Türkiye' => 'Turkey',
18
+ ## Côte d'Ivoire [fr] => Ivory Coast ???
19
+
20
+ ## austria (at)
21
+ ## remove cut-out (commerical) sponsor names
22
+ 'RZ Pellets WAC' => 'Wolfsberger AC', ## WAC
23
+ 'CASHPOINT SCR Altach' => 'SCR Altach',
24
+ 'SV Guntamatic Ried' => 'SV Ried',
25
+ }
26
+
27
+
28
+ class Team
29
+ def self.build( h )
30
+ ## pp h
31
+ new( id: h['id'],
32
+ name: h['name'],
33
+ code: h['code'],
34
+ country: h['country'] )
35
+ end
36
+
37
+
38
+ attr_reader :id,
39
+ :name,
40
+ :alt_names,
41
+ :code, :country
42
+ ## attr_accessor :count ## read/write - let's you update (match) count
43
+
44
+ def initialize( name:,
45
+ code:,
46
+ country:,
47
+ id: nil)
48
+ @alt_names = []
49
+
50
+ norm = norm_name( name )
51
+ if norm != name
52
+ puts " NORM TEAM NAME >#{name}< => >#{norm}<"
53
+ @alt_names << name
54
+ name = norm
55
+ end
56
+
57
+ ## check for mods/canonical name
58
+ mod = TEAM_MODS[ name ]
59
+ if mod
60
+ puts " MOD TEAM NAME >#{name}< => >#{mod}<"
61
+ @alt_names << name
62
+ name = mod
63
+ end
64
+
65
+ @name = name
66
+
67
+ @code = code
68
+ @country = country
69
+
70
+ @id = id
71
+
72
+ ## @count = 0
73
+ end
74
+
75
+
76
+
77
+ ## get lookup keys (id+name+alt_names)
78
+ def keys
79
+ keys = []
80
+ keys << @id if @id
81
+ keys << slugify(@name)
82
+ keys += @alt_names.map {|name| slugify(name)} unless @alt_names.empty?
83
+
84
+ ## note - make sure keys are unique
85
+ keys.uniq
86
+ end
87
+
88
+
89
+ def dummy?() @name == 'N.N.'; end
90
+ alias_method :unknown?, :dummy?
91
+
92
+ DUMMY = build( { 'name' => 'N.N.',
93
+ 'code' => 'UNK',
94
+ 'country' => 'UNK', } )
95
+ end # class Team
96
+
97
+
98
+
99
+
100
+
101
+ class Teams
102
+ def initialize
103
+ @recs = []
104
+ @lookup = {}
105
+ end
106
+
107
+ def add( recs )
108
+ recs.each do |rec|
109
+ _add( Team.build( rec ) )
110
+ end
111
+ end
112
+
113
+ def _add( new_rec )
114
+ @recs << new_rec
115
+
116
+ new_rec.keys.each do |key|
117
+ rec = @lookup[ key ]
118
+ if rec.nil?
119
+ @lookup[ key ] = new_rec
120
+ else
121
+ raise ArgumentError,
122
+ "duplicate team records #{rec.pretty_inspect} == #{new_rec.pretty_inspect}"
123
+ end
124
+ end
125
+ end
126
+
127
+
128
+ ## note - search by id, name or alt_names !!
129
+ def find!( q )
130
+ if q == '?' ## return dummy
131
+ rec = Team::DUMMY
132
+ else
133
+ key = slugify( q )
134
+ rec = @lookup[ key ]
135
+ if rec.nil?
136
+ raise ArgumentError, "no team found for q(uery) >#{q}< using key >#{key}<"
137
+ end
138
+ rec
139
+ end
140
+ end
141
+
142
+
143
+ =begin
144
+ def recs( sort: true )
145
+ recs = @recs.values
146
+ if sort
147
+ recs = recs.sort do |l,r|
148
+ res = r[:count] <=> l[:count]
149
+ res = l[:name] <=> r[:name] if res == 0
150
+ res
151
+ end
152
+ end
153
+ recs
154
+ end
155
+ =end
156
+
157
+
158
+ def each( &blk ) @recs.each( &blk ); end
159
+ def size() @recs.size; end
160
+
161
+ end # class Teams