sportdb-structs 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,271 +1,271 @@
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
+ ##########
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
@@ -50,7 +50,7 @@ class Team
50
50
  @alt_names = []
51
51
  @alt_names_auto = []
52
52
 
53
- update( kwargs ) unless kwargs.empty?
53
+ update( **kwargs ) unless kwargs.empty?
54
54
  end
55
55
 
56
56
  def update( **kwargs )