sports 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,271 +0,0 @@
1
- ##########
2
- # todo/fix:
3
- ## reuse standings helper/calculator from sportdb
4
- ## do NOT duplicate
5
-
6
-
7
- module Sports
8
-
9
-
10
- class Standings
11
-
12
- class StandingsLine ## nested class StandinsLine - todo/fix: change to Line - why? why not?
13
- attr_accessor :rank, :team,
14
- :played, :won, :lost, :drawn, ## -- total
15
- :goals_for, :goals_against, :pts,
16
- :home_played, :home_won, :home_lost, :home_drawn, ## -- home
17
- :home_goals_for, :home_goals_against, :home_pts,
18
- :away_played, :away_won, :away_lost, :away_drawn, ## -- away
19
- :away_goals_for, :away_goals_against, :away_pts
20
-
21
- alias_method :team_name, :team ## note: team for now always a string
22
- alias_method :pos, :rank ## rename back to use pos instead of rank - why? why not?
23
-
24
-
25
- def initialize( team )
26
- @rank = nil # use 0? why? why not?
27
- ## change rank back to pos - why? why not?
28
- @team = team
29
- @played = @home_played = @away_played = 0
30
- @won = @home_won = @away_won = 0
31
- @lost = @home_lost = @away_lost = 0
32
- @drawn = @home_drawn = @away_drawn = 0
33
- @goals_for = @home_goals_for = @away_goals_for = 0
34
- @goals_against = @home_goals_against = @away_goals_against = 0
35
- @pts = @home_pts = @away_pts = 0
36
- end
37
- end # (nested) class StandingsLine
38
-
39
-
40
- def initialize( match_or_matches=nil, opts={} )
41
- ## fix:
42
- # passing in e.g. pts for win (3? 2? etc.)
43
- # default to 3 for now
44
-
45
- ## lets you pass in 2 as an alterantive, for example
46
- @pts_won = opts[:pts_won] || 3
47
-
48
- @lines = {} # StandingsLines cached by team name/key
49
-
50
- ## add init and update all-in-one convenience shortcut
51
- update( match_or_matches ) if match_or_matches
52
- end
53
-
54
-
55
- def update( match_or_matches )
56
- ## convenience - update all matches at once
57
- ## todo/check: check for ActiveRecord_Associations_CollectionProxy and than use to_a (to "force" array) - why? why not?
58
- matches = if match_or_matches.is_a?(Array)
59
- match_or_matches
60
- else
61
- [match_or_matches]
62
- end
63
-
64
- matches.each_with_index do |match,i| # note: index(i) starts w/ zero (0)
65
- update_match( match )
66
- end
67
- self # note: return self to allow chaining
68
- end
69
-
70
-
71
- ## note: add a convenience shortcut
72
- ## to_a will sort and add rank (1,2,3) to standing lines
73
- def each( &block ) to_a.each( &block ); end
74
-
75
- def to_a
76
- ## return lines; sort and add rank
77
- ## note: will update rank!!!! (side effect)
78
-
79
- #############################
80
- ### calc ranking position (rank)
81
- ## fix/allow same rank e.g. all 1 or more than one team 3rd etc.
82
-
83
- # build array from hash
84
- ary = []
85
- @lines.each do |k,v|
86
- ary << v
87
- end
88
-
89
- ary.sort! do |l,r|
90
- ## note: reverse order (thus, change l,r to r,l)
91
- value = r.pts <=> l.pts
92
- if value == 0 # same pts try goal diff
93
- value = (r.goals_for-r.goals_against) <=> (l.goals_for-l.goals_against)
94
- if value == 0 # same goal diff too; try assume more goals better for now
95
- value = r.goals_for <=> l.goals_for
96
- end
97
- end
98
- value
99
- end
100
-
101
- ## update rank using ordered array
102
- ary.each_with_index do |line,i|
103
- line.rank = i+1 ## add ranking (e.g. 1,2,3 etc.) - note: i starts w/ zero (0)
104
- end
105
-
106
- ary
107
- end # to_a
108
-
109
-
110
-
111
- #####
112
- ###
113
- ## fix: move build to StandingsPart/Report !!!!
114
- def build( source: nil ) ## build / pretty print standings table in string buffer
115
- ## keep pretty printer in struct - why? why not?
116
-
117
-
118
- ## add standings table in markdown to buffer (buf)
119
-
120
- ## todo: use different styles/formats (simple/ etc ???)
121
-
122
- ## simple table (only totals - no home/away)
123
- ## standings.to_a.each do |l|
124
- ## buf << '%2d. ' % l.rank
125
- ## buf << '%-28s ' % l.team
126
- ## buf << '%2d ' % l.played
127
- ## buf << '%3d ' % l.won
128
- ## buf << '%3d ' % l.drawn
129
- ## buf << '%3d ' % l.lost
130
- ## buf << '%3d:%-3d ' % [l.goals_for,l.goals_against]
131
- ## buf << '%3d' % l.pts
132
- ## buf << "\n"
133
- ## end
134
-
135
- buf = ''
136
- buf << "\n"
137
- buf << "```\n"
138
- buf << " - Home - - Away - - Total -\n"
139
- buf << " Pld W D L F:A W D L F:A F:A +/- Pts\n"
140
-
141
- to_a.each do |l|
142
- buf << '%2d. ' % l.rank
143
- buf << '%-28s ' % l.team
144
- buf << '%2d ' % l.played
145
-
146
- buf << '%2d ' % l.home_won
147
- buf << '%2d ' % l.home_drawn
148
- buf << '%2d ' % l.home_lost
149
- buf << '%3d:%-3d ' % [l.home_goals_for,l.home_goals_against]
150
-
151
- buf << '%2d ' % l.away_won
152
- buf << '%2d ' % l.away_drawn
153
- buf << '%2d ' % l.away_lost
154
- buf << '%3d:%-3d ' % [l.away_goals_for,l.away_goals_against]
155
-
156
- buf << '%3d:%-3d ' % [l.goals_for,l.goals_against]
157
-
158
- goals_diff = l.goals_for-l.goals_against
159
- if goals_diff > 0
160
- buf << '%3s ' % "+#{goals_diff}"
161
- elsif goals_diff < 0
162
- buf << '%3s ' % "#{goals_diff}"
163
- else ## assume 0
164
- buf << ' '
165
- end
166
-
167
- buf << '%3d' % l.pts
168
- buf << "\n"
169
- end
170
-
171
- buf << "```\n"
172
- buf << "\n"
173
-
174
- ## optinal: add data source if known / present
175
- ## assume (relative) markdown link for now in README.md
176
- if source
177
- buf << "(Source: [`#{source}`](#{source}))\n"
178
- buf << "\n"
179
- end
180
-
181
- buf
182
- end
183
-
184
-
185
- private
186
- def update_match( m ) ## add a match
187
-
188
- ## note: always use team as string for now
189
- ## for now allow passing in of string OR struct - why? why not?
190
- ## todo/fix: change to m.team1_name and m.team2_name - why? why not?
191
- team1 = m.team1.is_a?( String ) ? m.team1 : m.team1.name
192
- team2 = m.team2.is_a?( String ) ? m.team2 : m.team2.name
193
-
194
- score = m.score.to_s
195
-
196
- ## puts " #{team1} - #{team2} #{score}"
197
-
198
- unless m.over?
199
- puts " !!!! skipping match - not yet over (date in the future) => #{m.date}"
200
- return
201
- end
202
-
203
- unless m.complete?
204
- puts "!!! [calc_standings] skipping match #{team1} - #{team2} w/ past date #{m.date} - scores incomplete => #{score}"
205
- return
206
- end
207
-
208
-
209
-
210
- line1 = @lines[ team1 ] || StandingsLine.new( team1 )
211
- line2 = @lines[ team2 ] || StandingsLine.new( team2 )
212
-
213
- line1.played += 1
214
- line1.home_played += 1
215
-
216
- line2.played += 1
217
- line2.away_played += 1
218
-
219
- if m.winner == 1
220
- line1.won += 1
221
- line1.home_won += 1
222
-
223
- line2.lost += 1
224
- line2.away_lost += 1
225
-
226
- line1.pts += @pts_won
227
- line1.home_pts += @pts_won
228
- elsif m.winner == 2
229
- line1.lost += 1
230
- line1.home_lost += 1
231
-
232
- line2.won += 1
233
- line2.away_won += 1
234
-
235
- line2.pts += @pts_won
236
- line2.away_pts += @pts_won
237
- else ## assume drawn/tie (that is, 0)
238
- line1.drawn += 1
239
- line1.home_drawn += 1
240
-
241
- line2.drawn += 1
242
- line2.away_drawn += 1
243
-
244
- line1.pts += 1
245
- line1.home_pts += 1
246
- line2.pts += 1
247
- line2.away_pts += 1
248
- end
249
-
250
- if m.score1 && m.score2
251
- line1.goals_for += m.score1
252
- line1.home_goals_for += m.score1
253
- line1.goals_against += m.score2
254
- line1.home_goals_against += m.score2
255
-
256
- line2.goals_for += m.score2
257
- line2.away_goals_for += m.score2
258
- line2.goals_against += m.score1
259
- line2.away_goals_against += m.score1
260
- else
261
- puts "*** warn: [standings] skipping match with missing scores: #{m.inspect}"
262
- end
263
-
264
- @lines[ team1 ] = line1
265
- @lines[ team2 ] = line2
266
- end # method update_match
267
-
268
- end # class Standings
269
-
270
-
271
- end # module Sports
@@ -1,147 +0,0 @@
1
-
2
- module Sports
3
-
4
- ##
5
- ## todo/fix: remove self.create in structs!!! use just new!!!
6
-
7
- class Team
8
- ## todo: use just names for alt_names - why? why not?
9
- attr_accessor :key, :name, :alt_names,
10
- :code, ## code == abbreviation e.g. ARS etc.
11
- :year, :year_end, ## todo/fix: change year to start_year and year_end to end_year (like in season)!!!
12
- :country
13
-
14
-
15
- def names
16
- ## todo/check: add alt_names_auto too? - why? why not?
17
- [@name] + @alt_names
18
- end ## all names
19
-
20
- def key
21
- ## note: auto-generate key "on-the-fly" if missing for now - why? why not?
22
- ## note: quick hack - auto-generate key, that is, remove all non-ascii chars and downcase
23
- @key || @name.downcase.gsub( /[^a-z]/, '' )
24
- end
25
-
26
-
27
- ## special import only attribs
28
- attr_accessor :alt_names_auto ## auto-generated alt names
29
- attr_accessor :wikipedia # wikipedia page name (for english (en))
30
-
31
-
32
- def historic?() @year_end ? true : false; end
33
- alias_method :past?, :historic?
34
-
35
- def wikipedia?() @wikipedia; end
36
- def wikipedia_url
37
- if @wikipedia
38
- ## note: replace spaces with underscore (-)
39
- ## e.g. Club Brugge KV => Club_Brugge_KV
40
- ## todo/check/fix:
41
- ## check if "plain" dash (-) needs to get replaced with typographic dash??
42
- "https://en.wikipedia.org/wiki/#{@wikipedia.gsub(' ','_')}"
43
- else
44
- nil
45
- end
46
- end
47
-
48
-
49
- def initialize( **kwargs )
50
- @alt_names = []
51
- @alt_names_auto = []
52
-
53
- update( kwargs ) unless kwargs.empty?
54
- end
55
-
56
- def update( **kwargs )
57
- @key = kwargs[:key] if kwargs.has_key? :key
58
- @name = kwargs[:name] if kwargs.has_key? :name
59
- @code = kwargs[:code] if kwargs.has_key? :code
60
- @alt_names = kwargs[:alt_names] if kwargs.has_key? :alt_names
61
- self ## note - MUST return self for chaining
62
- end
63
-
64
-
65
-
66
- ##############################
67
- ## helper methods for import only??
68
- ## check for duplicates
69
- include SportDb::NameHelper
70
-
71
- def duplicates?
72
- names = [name] + alt_names + alt_names_auto
73
- names = names.map { |name| normalize( sanitize(name) ) }
74
-
75
- names.size != names.uniq.size
76
- end
77
-
78
- def duplicates
79
- names = [name] + alt_names + alt_names_auto
80
-
81
- ## calculate (count) frequency and select if greater than one
82
- names.reduce( {} ) do |h,name|
83
- norm = normalize( sanitize(name) )
84
- h[norm] ||= []
85
- h[norm] << name; h
86
- end.select { |norm,names| names.size > 1 }
87
- end
88
-
89
-
90
- def add_variants( name_or_names )
91
- names = name_or_names.is_a?(Array) ? name_or_names : [name_or_names]
92
- names.each do |name|
93
- name = sanitize( name )
94
- self.alt_names_auto += variants( name )
95
- end
96
- end
97
- end # class Team
98
-
99
-
100
-
101
- class NationalTeam < Team
102
- def initialize( **kwargs )
103
- super
104
- end
105
-
106
- def update( **kwargs )
107
- super
108
- self ## note - MUST return self for chaining
109
- end
110
-
111
- end # class NationalTeam
112
-
113
-
114
- ########
115
- # more attribs - todo/fix - also add "upstream" to struct & model!!!!!
116
- # district, geos, year_end, country, etc.
117
-
118
- class Club < Team
119
- attr_accessor :ground
120
-
121
- attr_accessor :a, :b
122
- def a?() @a == nil; end ## is a (1st) team / club (i)? if a is NOT set
123
- def b?() @a != nil; end ## is b (2nd/reserve/jr) team / club (ii) if a is set
124
-
125
- ## note: delegate/forward all geo attributes for team b for now (to team a) - keep - why? why not?
126
- attr_writer :city, :district, :geos
127
- def city() @a == nil ? @city : @a.city; end
128
- def district() @a == nil ? @district : @a.district; end
129
- def country() @a == nil ? @country : @a.country; end
130
- def geos() @a == nil ? @geos : @a.geos; end
131
-
132
-
133
- def initialize( **kwargs )
134
- super
135
- end
136
-
137
- def update( **kwargs )
138
- super
139
- @city = kwargs[:city] if kwargs.has_key? :city
140
- ## todo/fix: use city struct - why? why not?
141
- ## todo/fix: add country too or report unused keywords / attributes - why? why not?
142
-
143
- self ## note - MUST return self for chaining
144
- end
145
- end # class Club
146
-
147
- end # module Sports
@@ -1,84 +0,0 @@
1
-
2
- module Sports
3
-
4
-
5
- class TeamUsage
6
-
7
- class TeamUsageLine ## nested class
8
- attr_accessor :team,
9
- :matches, ## number of matches (played),
10
- :seasons, ## (optianl) array of seasons, use seasons.size for count
11
- :levels ## (optional) hash of levels (holds mapping level to TeamUsageLine)
12
-
13
- def initialize( team )
14
- @team = team
15
-
16
- @matches = 0
17
- @seasons = []
18
- @levels = {}
19
- end
20
- end # (nested) class TeamUsageLine
21
-
22
-
23
-
24
- def initialize( opts={} )
25
- @lines = {} # StandingsLines cached by team name/key
26
- end
27
-
28
-
29
- def update( matches, season: '?', level: nil )
30
- ## convenience - update all matches at once
31
- matches.each_with_index do |match,i| # note: index(i) starts w/ zero (0)
32
- update_match( match, season: season, level: level )
33
- end
34
- self # note: return self to allow chaining
35
- end
36
-
37
- def to_a
38
- ## return lines; sort
39
-
40
- # build array from hash
41
- ary = []
42
- @lines.each do |k,v|
43
- ary << v
44
- end
45
-
46
- ## for now sort just by name (a-z)
47
- ary.sort! do |l,r|
48
- ## note: reverse order (thus, change l,r to r,l)
49
- l.team <=> r.team
50
- end
51
-
52
- ary
53
- end # to_a
54
-
55
-
56
- private
57
- def update_match( m, season: '?', level: nil ) ## add a match
58
-
59
- line1 = @lines[ m.team1 ] ||= TeamUsageLine.new( m.team1 )
60
- line2 = @lines[ m.team2 ] ||= TeamUsageLine.new( m.team2 )
61
-
62
- line1.matches +=1
63
- line2.matches +=1
64
-
65
- ## include season if not seen before (allow season in multiple files!!!)
66
- line1.seasons << season unless line1.seasons.include?( season )
67
- line2.seasons << season unless line2.seasons.include?( season )
68
-
69
- if level
70
- line1_level = line1.levels[ level ] ||= TeamUsageLine.new( m.team1 )
71
- line2_level = line2.levels[ level ] ||= TeamUsageLine.new( m.team2 )
72
-
73
- line1_level.matches +=1
74
- line2_level.matches +=1
75
-
76
- line1_level.seasons << season unless line1_level.seasons.include?( season )
77
- line2_level.seasons << season unless line2_level.seasons.include?( season )
78
- end
79
- end # method update_match
80
-
81
-
82
- end # class TeamUsage
83
-
84
- end # module Sports