257 lines
8.2 KiB
Python
257 lines
8.2 KiB
Python
from django.db import models
|
|
from django import forms
|
|
from django.core.exceptions import ValidationError
|
|
|
|
import collections
|
|
|
|
import datetime
|
|
from django.utils import timezone
|
|
import trueskill
|
|
|
|
from rowers.utils import dologging
|
|
|
|
def current_day(ttz=None):
|
|
if ttz is None:
|
|
return (datetime.datetime.now(tz=timezone.utc)).date()
|
|
return datetime.datetime.utcnow().astimezone(pytz.timezone(ttz)).date()
|
|
|
|
# Create your models here.
|
|
class Athlete(models.Model):
|
|
first_name = models.CharField(max_length=200)
|
|
last_name = models.CharField(max_length=200)
|
|
club = models.CharField(max_length=200)
|
|
trueskill_mu = models.FloatField(default=25.)
|
|
trueskill_sigma = models.FloatField(default=25./3.)
|
|
trueskill_exposed = models.FloatField(default=0)
|
|
birth_year = models.IntegerField(default=1972)
|
|
gender = models.CharField(max_length=200, choices=(('m','M'),('f','F')))
|
|
dummy = models.BooleanField(default=False)
|
|
|
|
class Meta:
|
|
unique_together = ('first_name','last_name','birth_year','gender')
|
|
|
|
def __str__(self):
|
|
return u'{f} {l}'.format(f = self.first_name, l=self.last_name)
|
|
|
|
def save(self, *args, **kwargs):
|
|
name = '{f} {l}'.format(f = self.first_name, l = self.last_name)
|
|
athletes = Athlete.objects.filter(gender=self.gender)
|
|
if self.pk is not None:
|
|
athletes = athletes.exclude(pk=self.pk)
|
|
for a in athletes:
|
|
aname = '{f} {l}'.format(f = a.first_name, l = a.last_name)
|
|
if name == aname:
|
|
raise ValidationError("Duplicate:{id}".format(id=a.id))
|
|
rating = trueskill.Rating(self.trueskill_mu, self.trueskill_sigma)
|
|
self.trueskill_exposed = trueskill.expose(rating)
|
|
|
|
super(Athlete, self).save(*args, **kwargs)
|
|
|
|
def get_absolute_url(self):
|
|
return "/boatmovers/athlete/%i/" % self.id
|
|
|
|
class athleteForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Athlete
|
|
fields = ['first_name','last_name','club','birth_year']
|
|
|
|
class Crew(models.Model):
|
|
athletes = models.ManyToManyField(Athlete, related_name='athlete_crews')
|
|
name = models.CharField(max_length=200)
|
|
|
|
def __str__(self):
|
|
return u'{n}'.format(n=self.name)
|
|
|
|
def save(self, *args, **kwargs):
|
|
super(Crew, self).save(*args, **kwargs)
|
|
|
|
def size(self):
|
|
return self.athletes.all().count()
|
|
|
|
class crewForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Crew
|
|
fields = ['name', 'athletes']
|
|
|
|
class Race(models.Model):
|
|
name = models.CharField(max_length=200)
|
|
resulturl = models.URLField(null=True, verbose_name='URL Link to results')
|
|
date = models.DateField(default=current_day, verbose_name='Race Date')
|
|
#resultlist = models.ManyToManyField(Result,through='Result')
|
|
crew_size = models.IntegerField(default=1,verbose_name='Nr of rowers per crew (1, 2, 4, 8)',
|
|
choices=((1,1),(2,2),(4,4),(8,8)))
|
|
verified = models.BooleanField(default=False)
|
|
processed = models.BooleanField(default=False)
|
|
gender = models.CharField(max_length=200,choices=(('m','M'),('f','F')),default='m')
|
|
|
|
class Meta:
|
|
unique_together = ('date','name')
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
def save(self, *args, **kwargs):
|
|
results = self.results.all()
|
|
crews = []
|
|
athletes = []
|
|
for result in results:
|
|
crews.append(result.crew.id)
|
|
for athlete in result.crew.athletes.all():
|
|
athletes.append(athlete.id)
|
|
|
|
if len(crews) != len(set(crews)):
|
|
raise ValidationError(
|
|
"Cannot have the same crew more than one time in a race"
|
|
)
|
|
|
|
#if len(athletes) != len(set(athletes)):
|
|
# raise ValidationError(
|
|
# "Cannot have the same athlete in different crews in a race"
|
|
# )
|
|
|
|
super(Race, self).save(*args, **kwargs)
|
|
|
|
def validate(self, verbose=False):
|
|
if len(self.results.all()) < 2:
|
|
if verbose:
|
|
print('False: Less than 2 results')
|
|
self.verified = False
|
|
self.save()
|
|
return False
|
|
|
|
l = self.results.all()[0].crew.size()
|
|
for result in self.results.all():
|
|
if result.crew.size() != l:
|
|
if verbose:
|
|
print('False: crew {c} has different crew size'.format(c=result.crew))
|
|
self.verified = False
|
|
self.save()
|
|
return False
|
|
|
|
if l not in [1,2,4,8]:
|
|
if verbose:
|
|
print('False: Crew size not in 1, 2, 4, or 8')
|
|
self.verified = False
|
|
self.save()
|
|
return False
|
|
|
|
results = self.results.all()
|
|
crews = []
|
|
athletes = []
|
|
for result in results:
|
|
crews.append(result.crew.id)
|
|
for athlete in result.crew.athletes.all():
|
|
if not athlete.dummy:
|
|
athletes.append(athlete.id)
|
|
|
|
if len(crews) != len(set(crews)):
|
|
if verbose:
|
|
print('False: Same crew competing twice')
|
|
self.verified = False
|
|
self.save()
|
|
return False
|
|
|
|
if len(athletes) != len(set(athletes)):
|
|
if verbose:
|
|
print('False: Duplicate athletes')
|
|
self.verified = False
|
|
self.save()
|
|
return False
|
|
|
|
self.verified = True
|
|
self.save()
|
|
|
|
def process(self):
|
|
if not self.verified:
|
|
if not self.validate():
|
|
return False
|
|
|
|
if self.processed:
|
|
return True
|
|
|
|
# validate the race
|
|
results = self.results.all().order_by('order')
|
|
crews = []
|
|
ranks = []
|
|
|
|
for result in results:
|
|
crew = result.crew
|
|
crewdict = {}
|
|
for athlete in crew.athletes.all():
|
|
crewdict[athlete.id] = trueskill.Rating(
|
|
athlete.trueskill_mu, athlete.trueskill_sigma)
|
|
crews.append(crewdict)
|
|
ranks.append(result.order)
|
|
|
|
rated_crews = trueskill.rate(crews, ranks)
|
|
|
|
for crew in rated_crews:
|
|
|
|
for id, rating in crew.items():
|
|
athlete = Athlete.objects.get(id=id)
|
|
athlete.trueskill_mu = rating.mu
|
|
athlete.trueskill_sigma = rating.sigma
|
|
athlete.save()
|
|
u = '{id},{f},{l},{mu},{sigma},{rid},{rname}'.format(
|
|
id = id,
|
|
f = athlete.first_name,
|
|
l = athlete.last_name,
|
|
mu = rating.mu,
|
|
sigma = rating.sigma,
|
|
rid = self.id,
|
|
rname = self.name,
|
|
)
|
|
dologging('ratings.csv',u)
|
|
|
|
self.processed = True
|
|
self.save()
|
|
|
|
return True
|
|
|
|
|
|
class raceForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Race
|
|
fields = ['name','date','resulturl','crew_size','gender']
|
|
|
|
|
|
class Result(models.Model):
|
|
crew = models.ForeignKey(Crew, on_delete=models.CASCADE,
|
|
related_name='results')
|
|
race = models.ForeignKey(Race, on_delete=models.CASCADE,
|
|
related_name='results')
|
|
order = models.PositiveIntegerField()
|
|
|
|
class Meta:
|
|
unique_together = ('crew','order')
|
|
|
|
def __str__(self):
|
|
return u'{r}: {o} - {c}'.format(
|
|
r=self.race,
|
|
o=self.order,
|
|
c=self.crew,
|
|
)
|
|
|
|
def save(self, *args, **kwargs):
|
|
allresults = self.race.results.all()
|
|
athletes = []
|
|
for result in allresults:
|
|
for athlete in result.crew.athletes.all():
|
|
athletes.append(athlete.id)
|
|
if result.crew.id == self.crew.id:
|
|
raise ValidationError(
|
|
"Cannot have the same crew more than one time in a race"
|
|
)
|
|
if len(athletes) != len(set(athletes)):
|
|
print([item for item, count in collections.Counter(athletes).items() if count>1])
|
|
raise ValidationError(
|
|
"Cannot have the same athlete in different crews in a race"
|
|
)
|
|
|
|
super(Result,self).save(*args, **kwargs)
|
|
|
|
class resultForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Result
|
|
fields = ['crew','race','order']
|