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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +4 -0
- data/Manifest.txt +34 -0
- data/README.md +25 -0
- data/Rakefile +32 -0
- data/bin/fbhub +18 -0
- data/bin/fbpp +14 -0
- data/lib/fbtxt-pp/config.rb +62 -0
- data/lib/fbtxt-pp/config_clubs.csv +37 -0
- data/lib/fbtxt-pp/config_more.rb +65 -0
- data/lib/fbtxt-pp/config_worldcup.rb +27 -0
- data/lib/fbtxt-pp/helper.rb +69 -0
- data/lib/fbtxt-pp/models/document.rb +130 -0
- data/lib/fbtxt-pp/models/goals.rb +60 -0
- data/lib/fbtxt-pp/models/match.rb +76 -0
- data/lib/fbtxt-pp/models/officials.rb +26 -0
- data/lib/fbtxt-pp/models/penalties.rb +29 -0
- data/lib/fbtxt-pp/models/players.rb +274 -0
- data/lib/fbtxt-pp/models/score.rb +85 -0
- data/lib/fbtxt-pp/models/stadiums.rb +114 -0
- data/lib/fbtxt-pp/models/teams.rb +161 -0
- data/lib/fbtxt-pp/pp/ppbookings.rb +55 -0
- data/lib/fbtxt-pp/pp/ppgoals.rb +71 -0
- data/lib/fbtxt-pp/pp/pplineup.rb +98 -0
- data/lib/fbtxt-pp/pp/ppmatch.rb +127 -0
- data/lib/fbtxt-pp/pp/ppmatch_full.rb +222 -0
- data/lib/fbtxt-pp/pp/ppmatch_min.rb +68 -0
- data/lib/fbtxt-pp/pp/ppopts.rb +92 -0
- data/lib/fbtxt-pp/pp/pppenalties.rb +32 -0
- data/lib/fbtxt-pp/pp/ppsquads.rb +224 -0
- data/lib/fbtxt-pp/pp/ppstats.rb +60 -0
- data/lib/fbtxt-pp/tool-fbhub.rb +204 -0
- data/lib/fbtxt-pp/tool-fbpp.rb +136 -0
- data/lib/fbtxt-pp/version.rb +24 -0
- data/lib/fbtxt-pp.rb +69 -0
- metadata +147 -0
|
@@ -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
|