From f606a7aa0944c66762599475f7bced991f0bb4dd Mon Sep 17 00:00:00 2001
From: Sander Roosendaal
Date: Fri, 24 Jun 2022 16:17:18 +0200
Subject: [PATCH] Basic functionality is there
---
.../migrations/0008_auto_20220624_1135.py | 23 ++++
.../migrations/0009_alter_race_crew_size.py | 18 ++++
.../migrations/0010_remove_race_resultlist.py | 17 +++
.../migrations/0011_alter_race_processed.py | 18 ++++
boatmovers/models.py | 102 ++++++++++++++++--
boatmovers/results.py | 18 ++--
boatmovers/templates/boatmovers.html | 69 ++++++++++++
.../templates/boatmovers/race_form.html | 4 +
.../templates/boatmovers/result_form.html | 4 +
boatmovers/templates/race.html | 45 ++++++++
boatmovers/urls.py | 5 +
boatmovers/views.py | 62 ++++++++++-
12 files changed, 366 insertions(+), 19 deletions(-)
create mode 100644 boatmovers/migrations/0008_auto_20220624_1135.py
create mode 100644 boatmovers/migrations/0009_alter_race_crew_size.py
create mode 100644 boatmovers/migrations/0010_remove_race_resultlist.py
create mode 100644 boatmovers/migrations/0011_alter_race_processed.py
create mode 100644 boatmovers/templates/boatmovers.html
create mode 100644 boatmovers/templates/boatmovers/race_form.html
create mode 100644 boatmovers/templates/boatmovers/result_form.html
create mode 100644 boatmovers/templates/race.html
diff --git a/boatmovers/migrations/0008_auto_20220624_1135.py b/boatmovers/migrations/0008_auto_20220624_1135.py
new file mode 100644
index 00000000..d5492e47
--- /dev/null
+++ b/boatmovers/migrations/0008_auto_20220624_1135.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.2.12 on 2022-06-24 11:35
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('boatmovers', '0007_auto_20220624_0820'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='race',
+ name='processed',
+ field=models.BooleanField(default=True),
+ ),
+ migrations.AlterField(
+ model_name='race',
+ name='crew_size',
+ field=models.IntegerField(default=1, verbose_name='Nr of rowers per crew (1, 2, 4, 8)'),
+ ),
+ ]
diff --git a/boatmovers/migrations/0009_alter_race_crew_size.py b/boatmovers/migrations/0009_alter_race_crew_size.py
new file mode 100644
index 00000000..4207ae30
--- /dev/null
+++ b/boatmovers/migrations/0009_alter_race_crew_size.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.12 on 2022-06-24 12:48
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('boatmovers', '0008_auto_20220624_1135'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='race',
+ name='crew_size',
+ field=models.IntegerField(choices=[(1, 1), (2, 2), (4, 4), (8, 8)], default=1, verbose_name='Nr of rowers per crew (1, 2, 4, 8)'),
+ ),
+ ]
diff --git a/boatmovers/migrations/0010_remove_race_resultlist.py b/boatmovers/migrations/0010_remove_race_resultlist.py
new file mode 100644
index 00000000..e6d9403e
--- /dev/null
+++ b/boatmovers/migrations/0010_remove_race_resultlist.py
@@ -0,0 +1,17 @@
+# Generated by Django 3.2.12 on 2022-06-24 12:50
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('boatmovers', '0009_alter_race_crew_size'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='race',
+ name='resultlist',
+ ),
+ ]
diff --git a/boatmovers/migrations/0011_alter_race_processed.py b/boatmovers/migrations/0011_alter_race_processed.py
new file mode 100644
index 00000000..f8b81cb7
--- /dev/null
+++ b/boatmovers/migrations/0011_alter_race_processed.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.12 on 2022-06-24 12:53
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('boatmovers', '0010_remove_race_resultlist'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='race',
+ name='processed',
+ field=models.BooleanField(default=False),
+ ),
+ ]
diff --git a/boatmovers/models.py b/boatmovers/models.py
index 8785c275..b2664c4b 100644
--- a/boatmovers/models.py
+++ b/boatmovers/models.py
@@ -52,6 +52,9 @@ class Crew(models.Model):
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
@@ -59,12 +62,13 @@ class crewForm(forms.ModelForm):
class Race(models.Model):
name = models.CharField(max_length=200)
- resulturl = models.URLField(null=True)
- date = models.DateField(default=current_day)
- resultlist = models.ManyToManyField(Crew,through='Result')
- crew_size = models.IntegerField(default=1,verbose_name='Nr of rowers per crew (1, 2, 4, 8)')
+ 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=True)
+ processed = models.BooleanField(default=False)
class Meta:
unique_together = ('date','name')
@@ -93,10 +97,87 @@ class Race(models.Model):
super(Race, self).save(*args, **kwargs)
+ def validate(self):
+ if len(self.results.all()) < 2:
+ 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:
+ self.verified = False
+ self.save()
+ return False
+
+ if l not in [1,2,4,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():
+ athletes.append(athlete.id)
+
+ if len(crews) != len(set(crews)):
+ self.verified = False
+ self.save()
+ return False
+
+ if len(athletes) != len(set(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()
+
+ self.processed = True
+ self.save()
+
+ return True
+
+
class raceForm(forms.ModelForm):
class Meta:
model = Race
- fields = ['name','date','resulturl','crew_size','resultlist']
+ fields = ['name','date','resulturl','crew_size']
class Result(models.Model):
@@ -107,7 +188,14 @@ class Result(models.Model):
order = models.PositiveIntegerField()
class Meta:
- unique_together = ('crew','race','order')
+ 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()
diff --git a/boatmovers/results.py b/boatmovers/results.py
index ebfdc0df..168a51a6 100644
--- a/boatmovers/results.py
+++ b/boatmovers/results.py
@@ -38,30 +38,30 @@ class Result:
def __init__(self, crews, name, validated=False, processed=False):
self.crews = crews
self.name = name
- self.validated = validated
+ self.verified = validated
self.processed = processed
def validate(self):
# crews need to be more than 2
if len(self.crews) < 2:
- self.validated = False
+ self.verified = False
return False
# crews need to be all same length
l = self.crews[0].size()
for crew in self.crews:
if crew.size() != l:
- self.validated = False
+ self.verified = False
return False
# crew length need to be 1, 2, 4 or 8
if l not in [1,2,4,8]:
- self.validated = False
+ self.verified = False
return False
# cannot have same crew multiple times in same race
if len(self.crews) != len(set(self.crews)):
- self.validated = False
+ self.verified = False
return False
# cannot have same athletes in different crews in same race
@@ -71,14 +71,14 @@ class Result:
allathletes.append(athlete)
if len(allathletes) != len(set(allathletes)):
- self.validated = False
+ self.verified = False
return False
- self.validated = True
- return self.validated
+ self.verified = True
+ return self.verified
def process(self):
- if not self.validated:
+ if not self.verified:
if not self.validate():
return False
diff --git a/boatmovers/templates/boatmovers.html b/boatmovers/templates/boatmovers.html
new file mode 100644
index 00000000..8337956e
--- /dev/null
+++ b/boatmovers/templates/boatmovers.html
@@ -0,0 +1,69 @@
+
+
+
+ | Rank |
+ Score |
+ Name | |
+ Club |
+ Gender |
+ Year of Birth |
+
+ {% for athlete in athletes %}
+
+ | {{ forloop.counter }} |
+ {{ athlete.trueskill_exposed|floatformat:2 }} |
+ {{ athlete.first_name }} |
+ {{ athlete.last_name }} |
+ {{ athlete.club }} |
+ {{ athlete.gender }} |
+ {{ athlete.birth_year }} |
+
+ {% endfor %}
+
+
+
+ This ranking was based on results from following races:
+
+
+
+ {% for race in races %}
+
+ | {{ race.date }} | {{ race.name }} |
+
+ View Race
+ |
+
+ {% endfor %}
+
+
+{% if user.is_authenticated and user.is_staff %}
+
+ Unprocessed races
+
+
+
+ {% for race in new_races %}
+
+ | {{ race.date }} | {{ race.name }} |
+
+ Manage Race
+ |
+
+ {% endfor %}
+
+
+
+ Add Athlete
+
+
+ Add Crew
+
+{% endif %}
+
+ Add Race
+
+{% if user.is_authenticated and user.is_staff %}
+
+ Add Result
+
+{% endif %}
diff --git a/boatmovers/templates/boatmovers/race_form.html b/boatmovers/templates/boatmovers/race_form.html
new file mode 100644
index 00000000..a9b32c5f
--- /dev/null
+++ b/boatmovers/templates/boatmovers/race_form.html
@@ -0,0 +1,4 @@
+
diff --git a/boatmovers/templates/boatmovers/result_form.html b/boatmovers/templates/boatmovers/result_form.html
new file mode 100644
index 00000000..a9b32c5f
--- /dev/null
+++ b/boatmovers/templates/boatmovers/result_form.html
@@ -0,0 +1,4 @@
+
diff --git a/boatmovers/templates/race.html b/boatmovers/templates/race.html
new file mode 100644
index 00000000..c26bcbff
--- /dev/null
+++ b/boatmovers/templates/race.html
@@ -0,0 +1,45 @@
+
+ {{ race.name }}
+
+
+ {{ race.date }}
+
+
+
+
+ | Order |
+ Crew | |
+
+ {% for result in results %}
+
+ | {{ result.order }} |
+ {{ result.crew.name }} |
+
+ {% endfor %}
+
+
+{% if user.is_authenticated and user.is_staff %}
+{% if race.verified %}
+
+ Race has been verified
+
+{% if race.processed %}
+
+ Race has been processed
+
+{% else %}
+
+ Race is not processed. Process Race
+
+{% endif %}
+{% else %}
+
+ Race is not verified. Verify Race
+
+{% endif %}
+{% if not race.verified and not race.processed %}
+
+ Add Result
+
+{% endif %}
+{% endif %}
diff --git a/boatmovers/urls.py b/boatmovers/urls.py
index 71b8da8e..0a849670 100644
--- a/boatmovers/urls.py
+++ b/boatmovers/urls.py
@@ -7,5 +7,10 @@ import boatmovers.views as views
urlpatterns = [
url(r'athlete/add/$',views.AthleteCreateView.as_view(),name='athlete_add'),
url(r'crew/add/$',views.CrewCreateView.as_view(),name='crew_add'),
+ url(r'race/add/$',views.RaceCreateView.as_view(),name='race_add'),
+ url(r'result/add/$',views.ResultCreateView.as_view(),name='result_add'),
+ url(r'race/(?P\d+)/$',views.race_view,name='race_view'),
+ url(r'race/(?P\d+)/verify/$',views.race_verify,name='race_verify'),
+ url(r'race/(?P\d+)/process/$',views.race_process,name='race_process'),
url(r'^$',views.boatmovers_view,name='boatmovers')
]
diff --git a/boatmovers/views.py b/boatmovers/views.py
index 04ab5de1..65ed0bea 100644
--- a/boatmovers/views.py
+++ b/boatmovers/views.py
@@ -1,9 +1,11 @@
from django.shortcuts import render
-from django.http import HttpResponse
+from django.http import HttpResponse, HttpResponseRedirect
+from django.shortcuts import get_object_or_404
+from django.urls import reverse
# Create your views here.
from django.views.generic.edit import CreateView
-from boatmovers.models import Athlete, Crew
+from boatmovers.models import Athlete, Crew, Race, Result
class AthleteCreateView(CreateView):
model = Athlete
@@ -25,5 +27,59 @@ class CrewCreateView(CreateView):
success_url = '/boatmovers/'
+class RaceCreateView(CreateView):
+ model = Race
+ fields = [
+ 'name',
+ 'resulturl',
+ 'date',
+ 'crew_size',
+ #'resultlist',
+ ]
+
+ success_url = '/boatmovers/'
+
+class ResultCreateView(CreateView):
+ model = Result
+ fields = [
+ 'crew',
+ 'race',
+ 'order'
+ ]
+
+ success_url = '/boatmovers/'
+
def boatmovers_view(request):
- return HttpResponse("1")
+ athletes = Athlete.objects.all().order_by('-trueskill_exposed','-birth_year','last_name','first_name')
+
+ races = Race.objects.filter(verified=True,processed=True).order_by('-date')
+ new_races = Race.objects.filter(processed=False).order_by('date')
+
+ return render(request,
+ 'boatmovers.html',
+ {'athletes':athletes,
+ 'races': races,
+ 'new_races': new_races}
+ )
+
+def race_view(request,id=0):
+ race = get_object_or_404(Race, pk=id)
+ results = race.results.all().order_by('order')
+
+ return render(request,
+ 'race.html',
+ {'race':race,
+ 'results':results}
+ )
+
+def race_verify(request, id=0):
+ race = get_object_or_404(Race, pk=id)
+ outcome = race.validate()
+
+ return HttpResponseRedirect(reverse('race_view',kwargs={'id':race.id}))
+
+def race_process(request, id=0):
+ race = get_object_or_404(Race, pk=id)
+ outcome = race.process()
+
+ return HttpResponseRedirect(reverse('race_view',kwargs={'id':race.id}))