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 = 'Rating athlete {id} {f} {l} mu = {mu} sigma = {sigma} race {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.log',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']