Module: SportDb::StandingsHelper

Defined in:
lib/sportdb/calc.rb

Defined Under Namespace

Classes: Stats

Class Method Summary collapse

Class Method Details

.calc(games, opts = {}) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/sportdb/calc.rb', line 58

def self.calc( games, opts={} )
   
   ##
   #  possible opts include:
   #    pts_won:              3 or 2 or n   (default 3)
   #    pts_exclude_scorep    false or true (default false)  -- exclude penalty shotout scores (e.g. count a draw/tie - no winner)
   #
  recs = calc_stats( games, opts )

  ## update pos (that is, ranking e.g. 1.,2., 3. etc.)
  recs= update_ranking( recs )

  pp recs
  recs
end

.calc_for_events(events, opts = {}) ⇒ Object



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/sportdb/calc.rb', line 76

def self.calc_for_events( events, opts={} )

  ## todo:
  ##  - add tracker for appeareances (stats records counter)

  alltime_recs = {}  # stats recs indexed by team_key

  events.each do |event|
    puts "  update standings for #{event.title}"
    recs = calc_stats( event.games, opts )

    recs.each do |team_key, rec|
      alltime_rec = alltime_recs[ team_key ] || Stats.new

      ## add stats values
      alltime_rec.add( rec )

      alltime_recs[ team_key ] = alltime_rec 
    end
  end


  ### fix:
  # - make merge team into a helper method (for reuse)

  ## check for merging teams
  # e.g. all time world cup
  #   Germany incl. West Germany
  #   Russia  incl. Soviet Union etc.
  
  # todo: change opts para to :includes  instead of :merge ? why? why not??

  merge = opts[:merge]
  if merge
    puts "  merging teams (stats records):"
    pp merge

    merge.each do |k,v|
      # note: assume key is destition team key and
      #         value is source team key  e.g.  'GER' => 'FRG'
      #         or array (for mulitple teamss e.g.  'GER' => ['FRG','GDR']
      team_key_dest = k.to_s

      if v.kind_of? Array
        team_keys_src = v
      else
        team_keys_src = [v]    # turn single value arg into array w/ single item
      end
      team_keys_src = team_keys_src.map { |src| src.to_s }  # turn all to string (might be symbol)

      alltime_rec_dest = alltime_recs[ team_key_dest ] || Stats.new

      team_keys_src.each do |team_key_src|
        alltime_rec_src  = alltime_recs[ team_key_src]

        if alltime_rec_src  # stats record found?
          alltime_rec_dest.add( alltime_rec_src )  # add stats values
          alltime_recs.delete( team_key_src )      # remove old src entry
        end
      end

      alltime_recs[ team_key_dest ] = alltime_rec_dest
    end
  end

  ## update pos (that is, ranking e.g. 1.,2., 3. etc.)
  alltime_recs= update_ranking( alltime_recs )

  ## pp alltime_recs
  alltime_recs
end

.calc_stats(games, opts = {}) ⇒ Object



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/sportdb/calc.rb', line 150

def self.calc_stats( games, opts={} )

  ## fix:
  #   passing in e.g. pts for win (3? 2? etc.)
  #    default to 3 for now

  # note:
  #   returns stats records w/ stats records counter always set to one (recs==1)

  ## todo/fix: find a better way to include logger (do NOT hardcode usage of root logger)!!!
  logger = LogUtils::Logger.root


  ## lets you pass in 2 as an alterantive, for example
  pts_won  = opts[:pts_won] || 3

  ## lets you exclude penalty shootout (e.g. match gets scored as draw/tie 1 pt each)
  #  e.g. why? used for alltime standings formula in world cup, for example
  #   todo: check other standings - exclude penalty shootout too - e.g. championsleague ?? if yes - make it true as default??
  pts_exclude_scorep = opts[:pts_exclude_scorep].present? ? opts[:pts_exclude_scorep] : false


  recs = {}

  games.each_with_index do |g,i|   # note: index(i) starts w/ zero (0)
    puts "  [#{i+1}] #{g.team1.title} - #{g.team2.title}  #{g.score_str}"
    unless g.over?
      puts "    !!!! skipping match - not yet over (play_at date in the future)"
      next
    end
    unless g.complete?
      logger.error "[StandingsHelper.calc_stats] skipping match #{g.team1.title} - #{g.team2.title} - scores incomplete #{g.score_str}"
      next
    end

    rec1 = recs[ g.team1.key ] || Stats.new
    rec2 = recs[ g.team2.key ] || Stats.new

    ## set stats records counter to one if new (first) record update 
    rec1.recs = 1   if rec1.recs == 0
    rec2.recs = 1   if rec2.recs == 0

    rec1.played += 1
    rec2.played += 1

    ## check - if winner (excludes penalty shootout scores in calc? start w/ extra time e.g winneret)
    if pts_exclude_scorep
       winner = g.winneret || g.winner90     ## if no extra time (et) score; try 90min (regular time score)
    else
       winner = g.winner   ## note: might include penalty shoot scores
    end

    if winner == 1
      rec1.won  += 1
      rec2.lost += 1
      rec1.pts  += pts_won
    elsif winner == 2
      rec1.lost  += 1
      rec2.won   += 1
      rec2.pts   += pts_won
    else  ## assume drawn/tie (that is, 0)
      rec1.drawn += 1
      rec2.drawn += 1
      rec1.pts   += 1
      rec2.pts   += 1
    end

    rec1.goals_for     += g.score1
    rec1.goals_against += g.score2
    
    rec2.goals_for     += g.score2
    rec2.goals_against += g.score1

    ## add overtime and penalty??
    ##  - for now add only overtime if present
    
    rec1.goals_for     += (g.score1et-g.score1)  if g.score1et.present?
    rec1.goals_against += (g.score2et-g.score2)  if g.score2et.present?

    rec2.goals_for     += (g.score2et-g.score2)  if g.score2et.present?
    rec2.goals_against += (g.score1et-g.score1)  if g.score1et.present?

    recs[ g.team1.key ] = rec1
    recs[ g.team2.key ] = rec2
  end  # each game

  recs # return records; hash indexed by team key
end

.update_ranking(recs) ⇒ Object



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/sportdb/calc.rb', line 240

def self.update_ranking( recs )
  #############################
  ### calc ranking / pos
  ##
  ## fix/allow sampe pos e.g. all 1 or more than one team 3rd etc.
  ##   see sportbook for an example

  # build array from hash
  ary = []
  recs.each do |k,v|
    ary << [k,v]
  end

  ary.sort! do |l,r|
    ## note: reverse order (thus, change l,r to r,l)
    value =  r[1].pts <=> l[1].pts
    if value == 0    # same pts try goal diff
      value =   (r[1].goals_for-r[1].goals_against) <=> (l[1].goals_for-l[1].goals_against)
      if value == 0  # same goal diff too; try assume more goals better for now
        value =   r[1].goals_for <=> l[1].goals_for
      end
    end
    value
  end

  ## update pos using ordered array
  ary.each_with_index  do |rec,i|
    k = rec[0]
    v = rec[1]
    v.pos = i+1    ## add ranking (e.g. 1,2,3 etc.)  - note: i starts w/ zero (0)
    recs[ k ] = v   ## update recs
  end
  
  recs
end