00001 from django.template.loader import render_to_string
00002 from dataplot import R,GenericPlot
00003 from dataplot import plotmodels as models
00004
00005 import pdb,os
00006
00007
00008 R.SquareScatter.default_kwargs={'marginal':False}
00009
00010 PLAYER_STATS=(
00011 'games',
00012 'wins',
00013 'win_percent',
00014 'goals_per_game',
00015 'team_goals_per_game',
00016 'goals_allowed_per_game',
00017 'differential',
00018 )
00019
00020 class PlayerManager(models.Manager):
00021 """Adds plotting methods.
00022
00023 """
00024
00025 def get_ts_data(self):
00026 """Plot args for making multiple time series of win%
00027
00028 We should replace this data filtering method with some
00029 database-backed method, so statistics can be looked up and
00030 plotted faster/easier.
00031
00032 """
00033 data=[
00034 dict(P.win_percent_plot_args(),name=P.name)
00035 for P in self.plotable()]
00036 for D in data:
00037 v=D['y'][0]
00038 while v==0 or v==1:
00039 D['y'].pop(0)
00040 D['d'].pop(0)
00041 v=D['y'][0]
00042 return data
00043
00044 def plotable(self):
00045 return self.frequent_players()
00046
00047 def frequent_players(self):
00048 return self.filter(games__gte=5)
00049
00050 def get_goals_and_victories_args(self):
00051 qs=self.plotable()
00052 return {
00053 'x':[x.goals_per_game for x in qs],
00054 'xlab':"Goals per game",
00055 'y':[x.win_percent for x in qs],
00056 'ylab':"Win percent",
00057 'main':"Goals = victories?",
00058 'ann':['%s\n%s'%(x.name,x.games) for x in qs],
00059 }
00060
00061 def wins_diff_args(self):
00062 qs=self.plotable()
00063 return {
00064 'y':[x.win_percent for x in qs],
00065 'ylab':"Win percent",
00066 'x':[x.differential for x in qs],
00067 'xlab':"Differential (average number of points your team wins/loses by)",
00068 'main':"Win percent versus differential",
00069 'ann':[x.name for x in qs],
00070 }
00071
00072 def SquareScatter_args(self):
00073 qs=self.plotable()
00074 return {
00075 'y':[x.team_goals_per_game for x in qs],
00076 'ylab':"Team goals per game",
00077 'x':[x.goals_per_game for x in qs],
00078 'xlab':"Goals per game",
00079 'main':"Your contribution to your team's goals",
00080 'ann':[x.name for x in qs],
00081 }
00082
00083 class Player(models.Model):
00084 name=models.CharField(
00085 max_length=100,null=False,blank=True,default='',unique=True)
00086 goals_per_game=models.FloatField(blank=True,null=True)
00087 team_goals_per_game=models.FloatField(blank=True,null=True)
00088 goals_allowed_per_game=models.FloatField(blank=True,null=True)
00089 differential=models.FloatField(blank=True,null=True)
00090 games=models.IntegerField(blank=True,null=True)
00091 wins=models.IntegerField(blank=True,null=True)
00092 win_percent=models.FloatField(blank=True,null=True)
00093
00094 objects=PlayerManager()
00095
00096 URLID='name'
00097
00098 class Admin:
00099 fields=(
00100 (None,{'fields':['name']}),
00101 )
00102 list_display=tuple(['name']+list(PLAYER_STATS))
00103
00104 class Meta:
00105 ordering=[('name')]
00106
00107 MANAGER_DATAPLOTS=[
00108 (R.SquareScatter,{'attribute':'offense_defense','get_plot_args':{
00109 'x':'team_goals_per_game',
00110 'y':'inverse_goals_allowed',
00111 'ann':'name',
00112 'main':"Defense v. offense"
00113 }}),
00114 R.SquareScatter,
00115 (R.SquareScatter,{'attribute':'wins_diff'}),
00116 (R.multi_time_series,{'get_plot_args':{
00117 'x':'get_ts_data',
00118 'main':"Win percent over time",
00119 'ylab':"Win percent",
00120 'xlab':"Game date",
00121 },'init_args':{'h':9,'w':6.5}}),
00122 ]
00123
00124 DATAPLOTS=[
00125 (R.TimeSeries,{'qs':'scores_ordered','get_plot_args':{
00126 'd':'game_seconds',
00127 'y':'goals',
00128 }}),
00129 (R.TimeSeries,{'qs':'scores_by_date','attribute':'win_percent_plot',
00130 'get_plot_args':{
00131 'd':'game_seconds',
00132 'y':'win_percent_until_now',
00133 'ylab':'Win percent',
00134 'xlab':'Game date',
00135 'main':'Win percent change over time'
00136 }}),
00137 ]
00138
00139 def get_absolute_url(self):
00140 return '/soccer/player/%s/'%self.id
00141
00142 def inverse_goals_allowed(self):
00143 return 1/(self.goals_allowed_per_game+1)
00144
00145 def get_stats(self):
00146 return [self.get_stat(at) for at in PLAYER_STATS]
00147
00148 def get_stat(self,at):
00149 v=getattr(self,at)
00150 if v==None:
00151 return
00152 stat={
00153 'name':at.replace("_"," ").capitalize(),
00154 'value':v,
00155 }
00156 for side in 'gt','lt','exact':
00157 di={'%s__%s'%(at,side):v}
00158 stat[side]=qs=Player.objects.filter(**di)
00159 return stat
00160
00161 def scores_ordered(self):
00162 return order_scores(self.score_set.all())
00163
00164 def scores_by_date(self):
00165 return self.score_set.select_related().order_by('soccer_game.date')
00166
00167 def __str__(self):
00168 return self.name
00169
00170 def save(self):
00171 self.cache()
00172 super(Player,self).save()
00173
00174 def save_plots(self):
00175 self.make_model_plots()
00176
00177 def cache(self):
00178 scores=self.score_set.all()
00179 N=self.games=scores.count()
00180 if not N:
00181 return
00182 self.goals_per_game=float(sum([
00183 sc.goals for sc in scores]))/N
00184
00185 self.goals_allowed_per_game=float(sum([
00186 sc.points_allowed() for sc in scores]))/N
00187 self.team_goals_per_game=float(sum([
00188 sc.team.points for sc in scores]))/N
00189 diffs=[sc.differential() for sc in scores]
00190 self.differential=float(sum(diffs))/N
00191
00192 self.wins=sum([1 for d in diffs if d > 0])
00193 self.win_percent=float(self.wins)/N
00194
00195 def get_absolute_url(self):
00196 return '/soccer/player/%s/'%self.name
00197
00198 GAME_STATS=(
00199 'hi_score',
00200 'lo_score',
00201 'differential',
00202 )
00203
00204 class GameManager(models.Manager):
00205
00206 DEFAULT_DATAFILE=os.path.join(os.path.dirname(__file__),'games.txt')
00207 TUPKEYS=('name','goals','own_goals')
00208
00209 def get_latest(self):
00210 try:
00211 return self.order_by('-date')[0]
00212 except IndexError:
00213 self.create_from_file()
00214 return self.order_by('-date')[0]
00215
00216 def create_from_file(self,filename=None):
00217 """Create new bike rides based on records in a text file.
00218
00219 """
00220 filename=filename or self.DEFAULT_DATAFILE
00221 print "Loading data from %s"%filename
00222 lines=open(filename).readlines()
00223 for line in lines:
00224 print line
00225 self.create_from_line(line)
00226
00227 def create_from_line(self,line):
00228 """Create one game based on line of text datafile.
00229
00230 """
00231 from dataplot import GenericPlot
00232 oldcache=GenericPlot.enable_caching
00233 GenericPlot.enable_caching=False
00234 teamlists=[i.split() for i in line.split(',')]
00235 G=Game.objects.create(date=teamlists[0].pop(0))
00236 for L in teamlists:
00237 T=G.team_set.create(name=L.pop(0))
00238 like=[L[i::len(self.TUPKEYS)] for i in range(len(self.TUPKEYS))]
00239 dictlist=[dict(zip(self.TUPKEYS,tup)) for tup in zip(*like)]
00240 for D in dictlist:
00241 D['player'],cr=Player.objects.get_or_create(name=D.pop('name'))
00242 sc=T.score_set.create(**D)
00243 T.save()
00244 GenericPlot.enable_caching=oldcache
00245 return G
00246
00247 def get_export_text(self):
00248 """Text for exported data file.
00249
00250 """
00251 return '\n'.join([g.export() for g in self.all()])
00252
00253 def export_games_to_file(self,filename=None):
00254 """Export data to a text file.
00255
00256 """
00257 filename=filename or self.DEFAULT_DATAFILE
00258 text=self.get_export_text()
00259 f=open(filename,'w')
00260 f.write(text)
00261 f.close()
00262
00263 class Game(models.Model):
00264 date=models.DateField(blank=False,null=False)
00265 hi_score=models.IntegerField(blank=True,null=True)
00266 lo_score=models.IntegerField(blank=True,null=True)
00267 differential=models.IntegerField(blank=True,null=True)
00268
00269 objects=GameManager()
00270
00271 URLID='date'
00272
00273 class Admin:
00274 list_display=[
00275 'date',
00276 'hi_score',
00277 'lo_score',
00278 'differential',
00279 ]
00280 fields=(
00281 (None,{
00282 'fields':['date']}),
00283 )
00284
00285 class Meta:
00286 ordering=[('date')]
00287
00288 def export(self):
00289 teams=self.teams_ordered()
00290 teamtext=','.join([T.export() for T in teams])
00291 return '%s %s'%(self.date,teamtext)
00292
00293 def teams_ordered(self):
00294 return self.team_set.order_by('-points')
00295
00296 def cache(self):
00297 try:
00298 teams=self.teams_ordered()
00299 self.hi_score,self.lo_score=[getattr(t,'points') for t in teams]
00300 except:
00301 pass
00302 try:
00303 self.differential=self.hi_score-self.lo_score
00304 except:
00305 pass
00306
00307 def __str__(self):
00308 return '%s'%self.date
00309
00310 def get_absolute_url(self):
00311 return '/soccer/game/%s/'%self.date
00312
00313 def save(self):
00314 self.cache()
00315 super(Game,self).save()
00316
00317 def order_scores(qs):
00318 return qs.select_related().order_by(
00319 '-goals','own_goals','soccer_player.name')
00320
00321 class Team(models.Model):
00322 name=models.CharField(max_length=100)
00323 game=models.ForeignKey(Game)
00324 goals=models.IntegerField(blank=True,null=True)
00325 own_goals=models.IntegerField(blank=True,null=True)
00326 points=models.IntegerField(blank=True,null=True)
00327 won=models.BooleanField(blank=True,null=False,default=False)
00328 tied=models.BooleanField(blank=True,null=False,default=False)
00329 players=models.IntegerField(blank=True,null=True)
00330
00331 def export(self):
00332 scores=self.scores_ordered()
00333 sctext=' '.join([
00334 '%s %s %s'%(sc.player.name,sc.goals,sc.own_goals)
00335 for sc in scores])
00336 return '%s %s'%(self.name,sctext)
00337
00338 def summary(self):
00339 return render_to_string(
00340 'soccer/team_summary.html',{'t':self})
00341
00342 def scores_ordered(self):
00343 p=getattr(self,'HILITE',None)
00344 qs=order_scores(self.score_set.all())
00345 if p:
00346 for sc in qs:
00347 sc.is_player=sc.player==self.HILITE
00348 return qs
00349
00350 def cache(self):
00351 """Save both teams relevant to this game.
00352
00353 """
00354 self.other=self.other_team()
00355 self.other.other=self
00356 teams=(self,self.other)
00357 for t in teams:
00358 qs=t.score_set.all()
00359 t.goals=sum([sc.goals for sc in qs])
00360 t.own_goals=sum([sc.own_goals for sc in qs])
00361 t.players=qs.count()
00362 for t in teams:
00363 t.points=t.goals+sum([
00364 sc.own_goals for sc in t.other.score_set.all()])
00365 for t in teams:
00366 t.won=t.points>t.other.points
00367 t.tied=t.points==t.other.points
00368 super(Team,t).save()
00369 for p in Player.objects.filter(
00370 score__team__game__id__exact=self.game.id).distinct():
00371 p.save()
00372 Player.make_manager_plots()
00373 self.game.save()
00374
00375 def other_team(self):
00376 try:
00377 return self.game.team_set.exclude(id=self.id)[0]
00378 except:
00379 pass
00380
00381 def save(self):
00382 if self.game.team_set.count()==2:
00383 self.cache()
00384 else:
00385 super(Team,self).save()
00386
00387 def __str__(self):
00388 return '%s'%self.name
00389
00390 class Admin:
00391 list_display=[
00392 'name',
00393 'game',
00394 'players',
00395 'points',
00396 'goals',
00397 'own_goals',
00398 ]
00399 fields=[
00400 (None,{
00401 'fields':['name','game']}),
00402 ]
00403
00404
00405 class Score(models.Model):
00406 player=models.ForeignKey(Player,blank=True,null=False,core=True)
00407 team=models.ForeignKey(Team,edit_inline=True,num_in_admin=10)
00408 goals=models.IntegerField(null=False,blank=False,default=0,core=True)
00409 own_goals=models.IntegerField(null=False,blank=False,default=0,core=True)
00410
00411 def __str__(self):
00412 return '%s: %s'%(self.player,self.goals)
00413
00414 def game_seconds(self):
00415 return self.team.game.date.strftime("%s")
00416
00417 def win_percent_until_now(self):
00418 qs=self.player.score_set.filter(
00419 team__game__date__lte=self.team.game.date)
00420 wins=qs.filter(team__won__exact=True).count()
00421 ties=qs.filter(team__tied__exact=True).count()
00422 games=qs.count()
00423 pct=(wins*1.0+ties*0.5)/games
00424 return pct
00425
00426 def points_allowed(self):
00427 return self.team.other_team().points
00428
00429 def differential(self):
00430 return self.team.points-self.team.other_team().points
00431
00432 def player_summary(self):
00433 self.team.HILITE=self.player
00434 return self.team.summary()
00435