Merge branch 'feature/popupregatta' into develop
This commit is contained in:
+4
-1
@@ -6,7 +6,7 @@ from .models import (
|
|||||||
Rower, Workout,GraphImage,FavoriteChart,SiteAnnouncement,
|
Rower, Workout,GraphImage,FavoriteChart,SiteAnnouncement,
|
||||||
Team,TeamInvite,TeamRequest,
|
Team,TeamInvite,TeamRequest,
|
||||||
WorkoutComment,C2WorldClassAgePerformance,PlannedSession,
|
WorkoutComment,C2WorldClassAgePerformance,PlannedSession,
|
||||||
GeoCourse,GeoPolygon,GeoPoint,
|
GeoCourse,GeoPolygon,GeoPoint,VirtualRace,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Register your models here so you can use them in the Admin module
|
# Register your models here so you can use them in the Admin module
|
||||||
@@ -60,6 +60,8 @@ class GeoCourseAdmin(admin.ModelAdmin):
|
|||||||
inlines = (GeoPolygonInline,)
|
inlines = (GeoPolygonInline,)
|
||||||
|
|
||||||
|
|
||||||
|
class VirtualRaceAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('manager','name','startdate','country')
|
||||||
|
|
||||||
admin.site.unregister(User)
|
admin.site.unregister(User)
|
||||||
admin.site.register(User,UserAdmin)
|
admin.site.register(User,UserAdmin)
|
||||||
@@ -75,3 +77,4 @@ admin.site.register(C2WorldClassAgePerformance,
|
|||||||
C2WorldClassAgePerformanceAdmin)
|
C2WorldClassAgePerformanceAdmin)
|
||||||
admin.site.register(PlannedSession,PlannedSessionAdmin)
|
admin.site.register(PlannedSession,PlannedSessionAdmin)
|
||||||
admin.site.register(GeoCourse, GeoCourseAdmin)
|
admin.site.register(GeoCourse, GeoCourseAdmin)
|
||||||
|
admin.site.register(VirtualRace, VirtualRaceAdmin)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import xml.etree.ElementTree as et
|
|||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from timezonefinder import TimezoneFinder
|
||||||
|
|
||||||
import dataprep
|
import dataprep
|
||||||
from rowers.utils import geo_distance
|
from rowers.utils import geo_distance
|
||||||
@@ -38,6 +39,24 @@ class InvalidTrajectoryError(Exception):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return repr(self.value)
|
return repr(self.value)
|
||||||
|
|
||||||
|
def get_course_timezone(course):
|
||||||
|
polygons = GeoPolygon.objects.filter(course = course)
|
||||||
|
points = GeoPoint.objects.filter(polygon = polygons[0])
|
||||||
|
lat = points[0].latitude
|
||||||
|
lon = points[0].longitude
|
||||||
|
|
||||||
|
tf = TimezoneFinder()
|
||||||
|
try:
|
||||||
|
timezone_str = tf.timezone_at(lng=lon,lat=lat)
|
||||||
|
except ValueError:
|
||||||
|
timezone_str = 'UTC'
|
||||||
|
|
||||||
|
if timezone_str is None:
|
||||||
|
timezone_str = tf.closest_timezone_at(lng=lon,lat=lat)
|
||||||
|
if timezone_str is None:
|
||||||
|
timezone_str = 'UTC'
|
||||||
|
|
||||||
|
return timezone_str
|
||||||
|
|
||||||
def polygon_to_path(polygon):
|
def polygon_to_path(polygon):
|
||||||
points = GeoPoint.objects.filter(polygon=polygon).order_by("order_in_poly")
|
points = GeoPoint.objects.filter(polygon=polygon).order_by("order_in_poly")
|
||||||
|
|||||||
+1
-28
@@ -809,34 +809,7 @@ def create_row_df(r,distance,duration,startdatetime,
|
|||||||
|
|
||||||
return (id, message)
|
return (id, message)
|
||||||
|
|
||||||
|
from utils import totaltime_sec_to_string
|
||||||
def totaltime_sec_to_string(totaltime):
|
|
||||||
hours = int(totaltime / 3600.)
|
|
||||||
if hours > 23:
|
|
||||||
message = 'Warning: The workout duration was longer than 23 hours. '
|
|
||||||
hours = 23
|
|
||||||
|
|
||||||
minutes = int((totaltime - 3600. * hours) / 60.)
|
|
||||||
if minutes > 59:
|
|
||||||
minutes = 59
|
|
||||||
if not message:
|
|
||||||
message = 'Warning: there is something wrong with the workout duration'
|
|
||||||
|
|
||||||
seconds = int(totaltime - 3600. * hours - 60. * minutes)
|
|
||||||
if seconds > 59:
|
|
||||||
seconds = 59
|
|
||||||
if not message:
|
|
||||||
message = 'Warning: there is something wrong with the workout duration'
|
|
||||||
|
|
||||||
tenths = int(10 * (totaltime - 3600. * hours - 60. * minutes - seconds))
|
|
||||||
if tenths > 9:
|
|
||||||
tenths = 9
|
|
||||||
if not message:
|
|
||||||
message = 'Warning: there is something wrong with the workout duration'
|
|
||||||
|
|
||||||
duration = "%s:%s:%s.%s" % (hours, minutes, seconds, tenths)
|
|
||||||
|
|
||||||
return duration
|
|
||||||
|
|
||||||
# Processes painsled CSV file to database
|
# Processes painsled CSV file to database
|
||||||
def save_workout_database(f2, r, dosmooth=True, workouttype='rower',
|
def save_workout_database(f2, r, dosmooth=True, workouttype='rower',
|
||||||
|
|||||||
+49
-2
@@ -695,6 +695,19 @@ class WorkoutSessionSelectForm(forms.Form):
|
|||||||
widget = forms.CheckboxSelectMultiple,
|
widget = forms.CheckboxSelectMultiple,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class WorkoutRaceSelectForm(forms.Form):
|
||||||
|
|
||||||
|
def __init__(self, workoutdata, *args, **kwargs):
|
||||||
|
|
||||||
|
super(WorkoutRaceSelectForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self.fields['workouts'] = forms.ChoiceField(
|
||||||
|
label='Workouts',
|
||||||
|
choices = workoutdata['choices'],
|
||||||
|
initial=workoutdata['initial'],
|
||||||
|
widget=forms.RadioSelect,
|
||||||
|
)
|
||||||
|
|
||||||
class PlannedSessionTeamForm(forms.Form):
|
class PlannedSessionTeamForm(forms.Form):
|
||||||
team = forms.ModelMultipleChoiceField(
|
team = forms.ModelMultipleChoiceField(
|
||||||
queryset=Team.objects.all(),
|
queryset=Team.objects.all(),
|
||||||
@@ -716,5 +729,39 @@ class PlannedSessionTeamMemberForm(forms.Form):
|
|||||||
super(PlannedSessionTeamMemberForm,self).__init__(*args,**kwargs)
|
super(PlannedSessionTeamMemberForm,self).__init__(*args,**kwargs)
|
||||||
|
|
||||||
self.fields['members'].queryset = thesession.rower.all()
|
self.fields['members'].queryset = thesession.rower.all()
|
||||||
|
|
||||||
|
from rowers.models import VirtualRace,GeoCourse
|
||||||
|
|
||||||
|
def get_countries():
|
||||||
|
countries = VirtualRace.objects.order_by('country').values_list('country').distinct()
|
||||||
|
countries = tuple([(c[0],c[0]) for c in countries])
|
||||||
|
countries = countries+(('All','All'),)
|
||||||
|
return countries
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class VirtualRaceSelectForm(forms.Form):
|
||||||
|
regattatypechoices = (
|
||||||
|
('upcoming','Upcoming Races'),
|
||||||
|
('ongoing','Ongoing Races'),
|
||||||
|
('previous','Previous Races'),
|
||||||
|
('my','My Races'),
|
||||||
|
('all','All Races'),
|
||||||
|
)
|
||||||
|
|
||||||
|
regattatype = forms.ChoiceField(
|
||||||
|
label='Type',
|
||||||
|
choices = regattatypechoices,
|
||||||
|
initial = 'upcoming',
|
||||||
|
)
|
||||||
|
|
||||||
|
country = forms.ChoiceField(
|
||||||
|
label='Country',
|
||||||
|
choices = get_countries()
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(VirtualRaceSelectForm, self).__init__(*args, **kwargs)
|
||||||
|
self.fields['country'] = forms.ChoiceField(
|
||||||
|
choices = get_countries(),initial='All'
|
||||||
|
)
|
||||||
|
|||||||
+124
-12
@@ -10,7 +10,7 @@ from django.dispatch import receiver
|
|||||||
from django.forms.widgets import SplitDateTimeWidget
|
from django.forms.widgets import SplitDateTimeWidget
|
||||||
from django.forms.extras.widgets import SelectDateWidget
|
from django.forms.extras.widgets import SelectDateWidget
|
||||||
from django.forms.formsets import BaseFormSet
|
from django.forms.formsets import BaseFormSet
|
||||||
from django.contrib.admin.widgets import AdminDateWidget
|
from django.contrib.admin.widgets import AdminDateWidget,AdminTimeWidget,AdminSplitDateTime
|
||||||
from datetimewidget.widgets import DateTimeWidget
|
from datetimewidget.widgets import DateTimeWidget
|
||||||
from django.core.validators import validate_email
|
from django.core.validators import validate_email
|
||||||
import os
|
import os
|
||||||
@@ -422,18 +422,19 @@ def course_length(course):
|
|||||||
|
|
||||||
return int(totaldist)
|
return int(totaldist)
|
||||||
|
|
||||||
|
sexcategories = (
|
||||||
|
('male','male'),
|
||||||
|
('female','female'),
|
||||||
|
('not specified','not specified'),
|
||||||
|
)
|
||||||
|
weightcategories = (
|
||||||
|
('hwt','heavy-weight'),
|
||||||
|
('lwt','light-weight'),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Extension of User with rowing specific data
|
# Extension of User with rowing specific data
|
||||||
class Rower(models.Model):
|
class Rower(models.Model):
|
||||||
weightcategories = (
|
|
||||||
('hwt','heavy-weight'),
|
|
||||||
('lwt','light-weight'),
|
|
||||||
)
|
|
||||||
|
|
||||||
sexcategories = (
|
|
||||||
('male','male'),
|
|
||||||
('female','female'),
|
|
||||||
('not specified','not specified'),
|
|
||||||
)
|
|
||||||
|
|
||||||
stravatypes = (
|
stravatypes = (
|
||||||
('Ride','Ride'),
|
('Ride','Ride'),
|
||||||
@@ -1056,6 +1057,44 @@ class PlannedSession(models.Model):
|
|||||||
|
|
||||||
super(PlannedSession,self).save(*args, **kwargs)
|
super(PlannedSession,self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
from django.core.validators import RegexValidator,validate_email
|
||||||
|
|
||||||
|
class VirtualRace(PlannedSession):
|
||||||
|
has_registration = models.BooleanField(default=False)
|
||||||
|
registration_closure = models.DateTimeField(blank=True,null=True)
|
||||||
|
evaluation_closure = models.DateTimeField(blank=True,null=True)
|
||||||
|
start_time = models.TimeField(blank=True,null=True)
|
||||||
|
end_time = models.TimeField(blank=True,null=True)
|
||||||
|
country = models.CharField(max_length=100,blank=True)
|
||||||
|
|
||||||
|
timezone = models.CharField(default='UTC',
|
||||||
|
choices=timezones,
|
||||||
|
max_length=100)
|
||||||
|
|
||||||
|
phone_regex = RegexValidator(
|
||||||
|
regex=r'^\+?1?\d{9,15}$',
|
||||||
|
message="Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed."
|
||||||
|
)
|
||||||
|
|
||||||
|
contact_phone = models.CharField(validators=[phone_regex], max_length=17, blank=True)
|
||||||
|
|
||||||
|
contact_email = models.EmailField(max_length=254,
|
||||||
|
validators=[validate_email],blank=True)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
|
||||||
|
name = self.name
|
||||||
|
startdate = self.startdate
|
||||||
|
enddate = self.enddate
|
||||||
|
|
||||||
|
stri = u'Virtual Race {n}'.format(
|
||||||
|
n = name,
|
||||||
|
)
|
||||||
|
|
||||||
|
return stri
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Date input utility
|
# Date input utility
|
||||||
class DateInput(forms.DateInput):
|
class DateInput(forms.DateInput):
|
||||||
input_type = 'date'
|
input_type = 'date'
|
||||||
@@ -1093,6 +1132,49 @@ class PlannedSessionForm(ModelForm):
|
|||||||
super(PlannedSessionForm, self).__init__(*args, **kwargs)
|
super(PlannedSessionForm, self).__init__(*args, **kwargs)
|
||||||
self.fields['course'].queryset = GeoCourse.objects.all().order_by("country","name")
|
self.fields['course'].queryset = GeoCourse.objects.all().order_by("country","name")
|
||||||
|
|
||||||
|
class VirtualRaceForm(ModelForm):
|
||||||
|
course = forms.ModelChoiceField(queryset = GeoCourse.objects, empty_label=None)
|
||||||
|
registration_closure = forms.SplitDateTimeField(widget=AdminSplitDateTime(),required=False)
|
||||||
|
evaluation_closure = forms.SplitDateTimeField(widget=AdminSplitDateTime(),required=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = VirtualRace
|
||||||
|
fields = [
|
||||||
|
'name',
|
||||||
|
'startdate',
|
||||||
|
'start_time',
|
||||||
|
'enddate',
|
||||||
|
'end_time',
|
||||||
|
'has_registration',
|
||||||
|
'registration_closure',
|
||||||
|
'evaluation_closure',
|
||||||
|
'course',
|
||||||
|
'comment',
|
||||||
|
'contact_phone',
|
||||||
|
'contact_email',
|
||||||
|
]
|
||||||
|
|
||||||
|
dateTimeOptions = {
|
||||||
|
'format': 'yyyy-mm-dd',
|
||||||
|
'autoclose': True,
|
||||||
|
}
|
||||||
|
|
||||||
|
widgets = {
|
||||||
|
'comment': forms.Textarea,
|
||||||
|
'startdate': AdminDateWidget(),
|
||||||
|
'enddate': AdminDateWidget(),
|
||||||
|
'start_time': AdminTimeWidget(),
|
||||||
|
'end_time': AdminTimeWidget(),
|
||||||
|
'registration_closure':AdminSplitDateTime(),
|
||||||
|
'evaluation_closure':AdminSplitDateTime(),
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self,*args,**kwargs):
|
||||||
|
super(VirtualRaceForm, self).__init__(*args, **kwargs)
|
||||||
|
self.fields['course'].queryset = GeoCourse.objects.all().order_by("country","name")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PlannedSessionFormSmall(ModelForm):
|
class PlannedSessionFormSmall(ModelForm):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -1124,12 +1206,12 @@ class PlannedSessionFormSmall(ModelForm):
|
|||||||
'manager': forms.HiddenInput(),
|
'manager': forms.HiddenInput(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boattypes = types.boattypes
|
||||||
|
|
||||||
# Workout
|
# Workout
|
||||||
class Workout(models.Model):
|
class Workout(models.Model):
|
||||||
workouttypes = types.workouttypes
|
workouttypes = types.workouttypes
|
||||||
workoutsources = types.workoutsources
|
workoutsources = types.workoutsources
|
||||||
boattypes = types.boattypes
|
|
||||||
privacychoices = types.privacychoices
|
privacychoices = types.privacychoices
|
||||||
|
|
||||||
user = models.ForeignKey(Rower)
|
user = models.ForeignKey(Rower)
|
||||||
@@ -1242,6 +1324,36 @@ def auto_delete_strokedata_on_delete(sender, instance, **kwargs):
|
|||||||
conn.close()
|
conn.close()
|
||||||
engine.dispose()
|
engine.dispose()
|
||||||
|
|
||||||
|
# Virtual Race results (for keeping results when workouts are deleted)
|
||||||
|
class VirtualRaceResult(models.Model):
|
||||||
|
user = models.ForeignKey(Rower)
|
||||||
|
teamname = models.CharField(max_length=20,verbose_name = 'Team Name',
|
||||||
|
blank=True,null=True)
|
||||||
|
username = models.CharField(max_length=150)
|
||||||
|
workout = models.ForeignKey(Workout,blank=True,null=True)
|
||||||
|
weightcategory = models.CharField(default="hwt",max_length=10,
|
||||||
|
choices=weightcategories,
|
||||||
|
verbose_name='Weight Category')
|
||||||
|
race = models.ForeignKey(VirtualRace)
|
||||||
|
duration = models.TimeField(default=datetime.time(1,0))
|
||||||
|
boattype = models.CharField(choices=boattypes,max_length=40,
|
||||||
|
default='1x',
|
||||||
|
verbose_name = 'Boat Type'
|
||||||
|
)
|
||||||
|
coursecompleted = models.BooleanField(default=False)
|
||||||
|
sex = models.CharField(default="not specified",
|
||||||
|
max_length=30,
|
||||||
|
choices=sexcategories,
|
||||||
|
verbose_name='Gender')
|
||||||
|
|
||||||
|
age = models.IntegerField(null=True)
|
||||||
|
|
||||||
|
class VirtualRaceResultForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = VirtualRaceResult
|
||||||
|
fields = ['teamname','weightcategory','boattype','age']
|
||||||
|
|
||||||
|
|
||||||
from rowers.metrics import rowingmetrics
|
from rowers.metrics import rowingmetrics
|
||||||
|
|
||||||
strokedatafields = {
|
strokedatafields = {
|
||||||
|
|||||||
+300
-3
@@ -7,8 +7,8 @@ import time
|
|||||||
from django.db import IntegrityError
|
from django.db import IntegrityError
|
||||||
import uuid
|
import uuid
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
import pytz
|
||||||
from utils import myqueue
|
from utils import myqueue,calculate_age,totaltime_sec_to_string
|
||||||
|
|
||||||
import django_rq
|
import django_rq
|
||||||
queue = django_rq.get_queue('default')
|
queue = django_rq.get_queue('default')
|
||||||
@@ -18,7 +18,7 @@ queuehigh = django_rq.get_queue('low')
|
|||||||
from rowers.models import (
|
from rowers.models import (
|
||||||
Rower, Workout,Team,
|
Rower, Workout,Team,
|
||||||
GeoCourse, TrainingMicroCycle,TrainingMesoCycle,TrainingMacroCycle,
|
GeoCourse, TrainingMicroCycle,TrainingMesoCycle,TrainingMacroCycle,
|
||||||
TrainingPlan,PlannedSession,
|
TrainingPlan,PlannedSession,VirtualRaceResult
|
||||||
)
|
)
|
||||||
|
|
||||||
import metrics
|
import metrics
|
||||||
@@ -86,6 +86,30 @@ def timefield_to_seconds_duration(t):
|
|||||||
|
|
||||||
return duration
|
return duration
|
||||||
|
|
||||||
|
|
||||||
|
def get_virtualrace_times(virtualrace):
|
||||||
|
geocourse = GeoCourse.objects.get(id = virtualrace.course.id)
|
||||||
|
timezone_str = courses.get_course_timezone(geocourse)
|
||||||
|
|
||||||
|
startdatetime = datetime.datetime.combine(
|
||||||
|
virtualrace.startdate,virtualrace.start_time)
|
||||||
|
enddatetime = datetime.datetime.combine(
|
||||||
|
virtualrace.enddate,virtualrace.end_time)
|
||||||
|
|
||||||
|
startdatetime = pytz.timezone(timezone_str).localize(
|
||||||
|
startdatetime
|
||||||
|
)
|
||||||
|
enddatetime = pytz.timezone(timezone_str).localize(
|
||||||
|
enddatetime
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'startdatetime':startdatetime,
|
||||||
|
'enddatetime':enddatetime,
|
||||||
|
'evaluation_closure':virtualrace.evaluation_closure,
|
||||||
|
'registration_closure':virtualrace.registration_closure,
|
||||||
|
}
|
||||||
|
|
||||||
def get_session_metrics(ps):
|
def get_session_metrics(ps):
|
||||||
rowers = ps.rower.all()
|
rowers = ps.rower.all()
|
||||||
rscore = []
|
rscore = []
|
||||||
@@ -401,6 +425,13 @@ def get_sessions(r,startdate=date.today(),
|
|||||||
|
|
||||||
return sps
|
return sps
|
||||||
|
|
||||||
|
def get_my_session_ids(r):
|
||||||
|
sps = PlannedSession.objects.filter(
|
||||||
|
rower__in=[r]
|
||||||
|
).order_by("preferreddate","startdate","enddate")
|
||||||
|
|
||||||
|
return [ps.id for ps in sps]
|
||||||
|
|
||||||
def get_workouts_session(r,ps):
|
def get_workouts_session(r,ps):
|
||||||
ws = Workout.objects.filter(user=r,plannedsession=ps)
|
ws = Workout.objects.filter(user=r,plannedsession=ps)
|
||||||
|
|
||||||
@@ -416,3 +447,269 @@ def update_plannedsession(ps,cd):
|
|||||||
ps.save()
|
ps.save()
|
||||||
|
|
||||||
return 1,'Planned Session Updated'
|
return 1,'Planned Session Updated'
|
||||||
|
|
||||||
|
def update_virtualrace(ps,cd):
|
||||||
|
for attr, value in cd.items():
|
||||||
|
if attr == 'comment':
|
||||||
|
value.replace("\r\n", "
");
|
||||||
|
value.replace("\n", "
");
|
||||||
|
setattr(ps, attr, value)
|
||||||
|
|
||||||
|
# correct times
|
||||||
|
|
||||||
|
course = cd['course']
|
||||||
|
geocourse = GeoCourse.objects.get(id= course.id)
|
||||||
|
timezone_str = courses.get_course_timezone(geocourse)
|
||||||
|
|
||||||
|
startdatetime = datetime.combine(cd['startdate'],cd['start_time'])
|
||||||
|
enddatetime = datetime.combine(cd['enddate'],cd['end_time'])
|
||||||
|
|
||||||
|
startdatetime = pytz.timezone(timezone_str).localize(
|
||||||
|
startdatetime
|
||||||
|
)
|
||||||
|
enddatetime = pytz.timezone(timezone_str).localize(
|
||||||
|
enddatetime
|
||||||
|
)
|
||||||
|
ps.evaluation_closure = pytz.timezone(timezone_str).localize(
|
||||||
|
ps.evaluation_closure.replace(tzinfo=None)
|
||||||
|
)
|
||||||
|
ps.registration_closure = pytz.timezone(timezone_str).localize(
|
||||||
|
ps.registration_closure.replace(tzinfo=None)
|
||||||
|
)
|
||||||
|
|
||||||
|
ps.timezone = timezone_str
|
||||||
|
|
||||||
|
ps.save()
|
||||||
|
|
||||||
|
return 1,'Virtual Race Updated'
|
||||||
|
|
||||||
|
def race_rower_status(r,race):
|
||||||
|
|
||||||
|
ws = Workout.objects.filter(user=r,plannedsession=race)
|
||||||
|
|
||||||
|
is_complete = is_session_complete_ws(ws,race)[1]
|
||||||
|
|
||||||
|
has_registered = r in race.rower.all()
|
||||||
|
|
||||||
|
return is_complete,has_registered
|
||||||
|
|
||||||
|
def race_can_edit(r,race):
|
||||||
|
if r.user != race.manager:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
start_time = race.start_time
|
||||||
|
start_date = race.startdate
|
||||||
|
startdatetime = datetime.combine(start_date,start_time)
|
||||||
|
startdatetime = pytz.timezone(race.timezone).localize(
|
||||||
|
startdatetime
|
||||||
|
)
|
||||||
|
if timezone.now()<startdatetime:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def race_can_submit(r,race):
|
||||||
|
if r not in race.rower.all():
|
||||||
|
return False
|
||||||
|
|
||||||
|
start_time = race.start_time
|
||||||
|
start_date = race.startdate
|
||||||
|
startdatetime = datetime.combine(start_date,start_time)
|
||||||
|
startdatetime = pytz.timezone(race.timezone).localize(
|
||||||
|
startdatetime
|
||||||
|
)
|
||||||
|
evaluation_closure = race.evaluation_closure
|
||||||
|
|
||||||
|
|
||||||
|
if timezone.now() > startdatetime and timezone.now() < evaluation_closure:
|
||||||
|
is_complete,has_registered = race_rower_status(r,race)
|
||||||
|
if is_complete == 'not done':
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def race_can_resubmit(r,race):
|
||||||
|
if r not in race.rower.all():
|
||||||
|
return False
|
||||||
|
|
||||||
|
start_time = race.start_time
|
||||||
|
start_date = race.startdate
|
||||||
|
startdatetime = datetime.combine(start_date,start_time)
|
||||||
|
startdatetime = pytz.timezone(race.timezone).localize(
|
||||||
|
startdatetime
|
||||||
|
)
|
||||||
|
evaluation_closure = race.evaluation_closure
|
||||||
|
|
||||||
|
|
||||||
|
if timezone.now() > startdatetime and timezone.now() < evaluation_closure:
|
||||||
|
is_complete,has_registered = race_rower_status(r,race)
|
||||||
|
if is_complete in ['partial','completed']:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def race_can_withdraw(r,race):
|
||||||
|
if r not in race.rower.all():
|
||||||
|
return False
|
||||||
|
|
||||||
|
start_time = race.start_time
|
||||||
|
start_date = race.startdate
|
||||||
|
startdatetime = datetime.combine(start_date,start_time)
|
||||||
|
startdatetime = pytz.timezone(race.timezone).localize(
|
||||||
|
startdatetime
|
||||||
|
)
|
||||||
|
|
||||||
|
registration_closure = race.registration_closure
|
||||||
|
if registration_closure is not None and registration_closure != '':
|
||||||
|
if timezone.now() > registration_closure:
|
||||||
|
return False
|
||||||
|
elif timezone.now() > startdatetime:
|
||||||
|
return False
|
||||||
|
elif timezone.now() > startdatetime:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def race_can_register(r,race):
|
||||||
|
if r in race.rower.all():
|
||||||
|
return False
|
||||||
|
|
||||||
|
start_time = race.start_time
|
||||||
|
start_date = race.startdate
|
||||||
|
startdatetime = datetime.combine(start_date,start_time)
|
||||||
|
startdatetime = pytz.timezone(race.timezone).localize(
|
||||||
|
startdatetime
|
||||||
|
)
|
||||||
|
|
||||||
|
registration_closure = race.registration_closure
|
||||||
|
if registration_closure is not None and registration_closure != '':
|
||||||
|
if timezone.now() > registration_closure:
|
||||||
|
return False
|
||||||
|
elif timezone.now() > startdatetime:
|
||||||
|
return False
|
||||||
|
elif timezone.now() > startdatetime:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def add_rower_race(r,race):
|
||||||
|
race.rower.add(r)
|
||||||
|
race.save()
|
||||||
|
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def remove_rower_race(r,race):
|
||||||
|
race.rower.remove(r)
|
||||||
|
|
||||||
|
records = VirtualRaceResult.objects.filter(user=r,
|
||||||
|
workout__isnull=True,
|
||||||
|
race=race)
|
||||||
|
|
||||||
|
for r in records:
|
||||||
|
r.delete()
|
||||||
|
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# Low Level functions - to be called by higher level methods
|
||||||
|
def add_workout_race(ws,race,r):
|
||||||
|
result = 0
|
||||||
|
comments = []
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
start_time = race.start_time
|
||||||
|
start_date = race.startdate
|
||||||
|
startdatetime = datetime.combine(start_date,start_time)
|
||||||
|
startdatetime = pytz.timezone(race.timezone).localize(
|
||||||
|
startdatetime
|
||||||
|
)
|
||||||
|
|
||||||
|
end_time = race.end_time
|
||||||
|
end_date = race.enddate
|
||||||
|
enddatetime = datetime.combine(end_date,end_time)
|
||||||
|
enddatetime = pytz.timezone(race.timezone).localize(
|
||||||
|
enddatetime
|
||||||
|
)
|
||||||
|
|
||||||
|
# check if all sessions have same date
|
||||||
|
dates = [w.date for w in ws]
|
||||||
|
if (not all(d == dates[0] for d in dates)) and race.sessiontype not in ['challenge','cycletarget']:
|
||||||
|
errors.append('For tests and training sessions, selected workouts must all be done on the same date')
|
||||||
|
return result,comments,errors
|
||||||
|
|
||||||
|
if len(ws)>1 and race.sessiontype == 'test':
|
||||||
|
errors.append('For tests, you can only attach one workout')
|
||||||
|
return result,comments,errors
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ids = [w.id for w in ws]
|
||||||
|
ids = list(set(ids))
|
||||||
|
|
||||||
|
if len(ids)>1 and race.sessiontype in ['test','coursetest']:
|
||||||
|
errors.append('For tests, you can only attach one workout')
|
||||||
|
return result,comments,errors
|
||||||
|
|
||||||
|
# start adding sessions
|
||||||
|
for w in ws:
|
||||||
|
if w.startdatetime>=startdatetime and w.startdatetime<=enddatetime:
|
||||||
|
w.plannedsession = race
|
||||||
|
w.save()
|
||||||
|
result += 1
|
||||||
|
|
||||||
|
comments.append('Your result has been submitted')
|
||||||
|
else:
|
||||||
|
errors.append('Workout %i did not match the race window' % w.id)
|
||||||
|
return result,comments,errors
|
||||||
|
|
||||||
|
if result>0:
|
||||||
|
username = r.user.first_name+' '+r.user.last_name
|
||||||
|
if r.birthdate:
|
||||||
|
age = calculate_age(r.birthdate)
|
||||||
|
else:
|
||||||
|
age = None
|
||||||
|
(
|
||||||
|
coursetime,
|
||||||
|
coursemeters,
|
||||||
|
coursecompleted
|
||||||
|
) = courses.get_time_course(ws,race.course)
|
||||||
|
if not coursecompleted:
|
||||||
|
errors.append('Your trajectory did not match the race course')
|
||||||
|
return result,comments,errors
|
||||||
|
|
||||||
|
duration = totaltime_sec_to_string(coursetime)
|
||||||
|
|
||||||
|
record = VirtualRaceResult(
|
||||||
|
user=r,
|
||||||
|
username=username,
|
||||||
|
workout = ws[0],
|
||||||
|
race = race,
|
||||||
|
coursecompleted=coursecompleted,
|
||||||
|
duration = duration,
|
||||||
|
boattype = ws[0].boattype,
|
||||||
|
sex = r.sex,
|
||||||
|
age = age,
|
||||||
|
)
|
||||||
|
|
||||||
|
record.save()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return result,comments,errors
|
||||||
|
|
||||||
|
def delete_race_result(workout,race):
|
||||||
|
results = VirtualRaceResult.objects.filter(workout=workout,race=race)
|
||||||
|
for r in results:
|
||||||
|
r.delete()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -63,11 +63,11 @@
|
|||||||
</table>
|
</table>
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
</div>
|
</div>
|
||||||
<div class="grid_2">
|
<div class="grid_2 alpha">
|
||||||
<input name='daterange' class="button green" type="submit" value="Submit"> </form>
|
<input name='daterange' class="button green" type="submit" value="Submit"> </form>
|
||||||
</div>
|
</div>
|
||||||
{% if user.is_authenticated and user|is_manager %}
|
{% if user.is_authenticated and user|is_manager %}
|
||||||
<div class="grid_2 omega dropdown">
|
<div class="grid_2 dropdown">
|
||||||
<button class="grid_2 alpha button green small dropbtn">
|
<button class="grid_2 alpha button green small dropbtn">
|
||||||
{{ rower.user.first_name }} {{ rower.user.last_name }}
|
{{ rower.user.first_name }} {{ rower.user.last_name }}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
<div id="right" class="grid_6 omega">
|
<div id="right" class="grid_6 omega">
|
||||||
{% if plannedsession.sessiontype == 'test' or plannedsession.sessiontype == 'coursetest' %}
|
{% if plannedsession.sessiontype == 'test' or plannedsession.sessiontype == 'coursetest' %}
|
||||||
<h1>Ranking</h1>
|
<h1>Ranking</h1>
|
||||||
<table class="listtable shortpadded" width="80%">
|
<table id="rankingtable" class="listtable shortpadded tablesorter" width="80%">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Nr</th>
|
<th>Nr</th>
|
||||||
@@ -158,4 +158,14 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
|
<script type="text/javascript" src="/static/admin/js/jquery.min.js"></script>
|
||||||
|
<script type="text/javascript" src="/static/admin/js/jquery.tablesorter.min.js"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="/static/admin/js/jquery.tablesorter.widgets.js"></script>
|
||||||
|
<script>
|
||||||
|
$( document ).ready(function() {
|
||||||
|
$("#rankingtable").tablesorter();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
{% load rowerfilters %}
|
||||||
|
|
||||||
|
{% block title %}Submit Race Result{% endblock %}
|
||||||
|
|
||||||
|
{% block meta %}
|
||||||
|
<script type='text/javascript'
|
||||||
|
src='https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js'>
|
||||||
|
</script>
|
||||||
|
<script type='text/javascript'
|
||||||
|
src='https://ajax.aspnetcdn.com/ajax/jquery.validate/1.14.0/jquery.validate.min.js'>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="grid_12 alpha">
|
||||||
|
<div class="grid_6 alpha">
|
||||||
|
<h1>Submit Your Result for {{ race.name }}</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form id="race_submit_form"
|
||||||
|
method="post">
|
||||||
|
<div class="grid_12 alpha">
|
||||||
|
<div class="grid_6 alpha">
|
||||||
|
<p>Select one of the following workouts that you rowed within the race window</p>
|
||||||
|
<table width="100%">
|
||||||
|
<tr>
|
||||||
|
{% for field in w_form.hidden_fields %}
|
||||||
|
{{ field }}
|
||||||
|
{% endfor %}
|
||||||
|
{% for field in w_form.visible_fields %}
|
||||||
|
<td>
|
||||||
|
{{ field }}
|
||||||
|
</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grid_2 prefix_2 suffix_8">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input class="button green" type="submit" value="Submit">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
{% load rowerfilters %}
|
||||||
|
|
||||||
|
{% block title %}New Virtual Race{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="grid_12 alpha">
|
||||||
|
|
||||||
|
<h1>{{ race.name }}</h1>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid_12 alpha">
|
||||||
|
<div class="grid_8 alpha">
|
||||||
|
<div id="raceinfo">
|
||||||
|
<h2>Race Information</h2>
|
||||||
|
<p>
|
||||||
|
<table class="listtable shortpadded" width="80%">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>Course</th><td>{{ race.course }}</td>
|
||||||
|
</tr>
|
||||||
|
{% if race.has_registration %}
|
||||||
|
<tr>
|
||||||
|
<th>Registration closure</th>
|
||||||
|
<td>{{ race.registration_closure }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
<tr>
|
||||||
|
<th>Date</th><td>{{ race.startdate }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Race Window</th><td>{{ race.startdate }} {{ race.start_time }} to {{ race.enddate }} {{ race.end_time }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Results Submission Deadline</th><td>{{ race.evaluation_closure }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Organizer</th><td>{{ race.manager.first_name }} {{ race.manager.last_name }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Contact Email</th><td>{{ race.contact_email }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Contact Phone</th><td>{{ race.contact_phone }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Comment</th><td>{{ race.comment }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div id="registerbuttons">
|
||||||
|
{% if request.user.is_anonymous %}
|
||||||
|
<p>
|
||||||
|
Registered users of rowsandall.com can participate in this event.
|
||||||
|
</p>
|
||||||
|
{% else %}
|
||||||
|
<p>
|
||||||
|
See race rules below.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{% for button in buttons %}
|
||||||
|
{% if button == 'registerbutton' %}
|
||||||
|
<a href="/rowers/virtualevent/{{ race.id }}/register" class="button gray small grid_2">Register</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if button == 'submitbutton' %}
|
||||||
|
<a href="/rowers/virtualevent/{{ race.id }}/submit" class="button gray small grid_2">Submit Result</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if button == 'resubmitbutton' %}
|
||||||
|
<a href="/rowers/virtualevent/{{ race.id }}/submit" class="button gray small grid_2">Submit New Result</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if button == 'withdrawbutton' %}
|
||||||
|
<a href="/rowers/virtualevent/{{ race.id }}/withdraw" class="button gray small grid_2">Withdraw</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if button == 'editbutton' %}
|
||||||
|
<a href="/rowers/virtualevent/{{ race.id }}/edit" class="button gray small grid_2">Edit Race</a>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div id="results">
|
||||||
|
<h2>Results</h2>
|
||||||
|
<p>
|
||||||
|
{% if results or dns %}
|
||||||
|
<table class="listtable shortpadded" width="100%">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th> </th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Team Name</th>
|
||||||
|
<th> </th>
|
||||||
|
<th> </th>
|
||||||
|
<th> </th>
|
||||||
|
<th>Boat</th>
|
||||||
|
<th>Raw Time</th>
|
||||||
|
<th>In Class</th>
|
||||||
|
<th>Corrected Time</th>
|
||||||
|
<th>Corrected Place</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for result in results %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ forloop.counter }}</td>
|
||||||
|
<td>
|
||||||
|
<a href="/rowers/workout/{{ result.workout.id }}">
|
||||||
|
{{ result.username }}</a></td>
|
||||||
|
<td>{{ result.teamname }}</th>
|
||||||
|
<td>{{ result.age }}</td>
|
||||||
|
<td>{{ result.sex }}</td>
|
||||||
|
<td>{{ result.weightcategory }}</td>
|
||||||
|
<td>{{ result.boattype }}</td>
|
||||||
|
<td>{{ result.duration |durationprint:"%H:%M:%S.%f" }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% for result in dns %}
|
||||||
|
<tr>
|
||||||
|
<td> </td>
|
||||||
|
<td>{{ result.username }}</td>
|
||||||
|
<td>{{ result.teamname }}</td>
|
||||||
|
<td>{{ result.age }}</td>
|
||||||
|
<td>{{ result.sex }}</td>
|
||||||
|
<td>{{ result.weightcategory }}</td>
|
||||||
|
<td>{{ result.boattype }}</td>
|
||||||
|
<td>DNS</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
No results yet
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div id="registered">
|
||||||
|
{% if records %}
|
||||||
|
<h2>Registered Competitors</h2>
|
||||||
|
<table class="listtable shortpadded" width="80%">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Team Name</th>
|
||||||
|
<th>Age</th>
|
||||||
|
<th>Weight Category</th>
|
||||||
|
</tr>
|
||||||
|
<tbody>
|
||||||
|
{% for record in records %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ record.username }}
|
||||||
|
<td>{{ record.teamname }}</td>
|
||||||
|
<td>{{ record.weightcategory }}</td>
|
||||||
|
<td>{{ record.age }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div id="rules">
|
||||||
|
<h2>Rules</h2>
|
||||||
|
<p>
|
||||||
|
As a rowsandall.com user, you can
|
||||||
|
register to take part in this event.
|
||||||
|
If the race organizer has set a registration deadline, you must
|
||||||
|
register before the deadline. Otherwise, it is sufficient to
|
||||||
|
register before the start of the race window.
|
||||||
|
You can always withdraw from participating before the registration
|
||||||
|
deadline or the start of the race window, if no registration
|
||||||
|
deadline was set.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
After the start of the race window and before the submission deadline,
|
||||||
|
you can submit results by linking the race to one of your uploaded
|
||||||
|
workouts. The workout start time must be within the race window
|
||||||
|
and your trajectory must pass through the blue polygons on the course
|
||||||
|
map (in the right order), for your result to be valid.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The results table has a link to a page where details of your workout
|
||||||
|
are shown.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Race results are stored permanently and are not deleted when
|
||||||
|
you delete the respective workout or remove your account.
|
||||||
|
By registering, you agree with this and the race rules.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Virtual Racing on rowsandall.com is honors based. Please be a good
|
||||||
|
sport, submit real results rowed by you, and make sure you set the
|
||||||
|
boat type correctly. For (future functionality) age and gender
|
||||||
|
corrected times, please be sure your gender and birth date are set
|
||||||
|
correctly in your user settings.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grid_4 omega">
|
||||||
|
<h2>Course</h2>
|
||||||
|
{{ coursediv|safe }}
|
||||||
|
|
||||||
|
{{ coursescript|safe }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
{% load rowerfilters %}
|
||||||
|
|
||||||
|
{% block title %}New Virtual Race{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="grid_12 alpha">
|
||||||
|
|
||||||
|
<h1>New Virtual Race</h1>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="grid_8 alpha">
|
||||||
|
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
|
||||||
|
{% if form.errors %}
|
||||||
|
<p style="color: red;">
|
||||||
|
Please correct the error{{ form.errors|pluralize }} below.
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<table>
|
||||||
|
{{ form.as_table }}
|
||||||
|
</table>
|
||||||
|
{% csrf_token %}
|
||||||
|
<div id="formbutton" class="grid_1 prefix_4 suffix_1">
|
||||||
|
<input class="button green" type="submit" value="Save">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="grid_4 omega">
|
||||||
|
<p>
|
||||||
|
<ul>
|
||||||
|
<li>All times are local times in the race course time zone</li>
|
||||||
|
<li>Adding a contact phone number and email is not mandatory, but we
|
||||||
|
strongly recommend it.</li>
|
||||||
|
<li>If your event has a registration closure deadline, participants
|
||||||
|
have to enter (and can withdraw) before the registration closure time.</li>
|
||||||
|
<li>Participants can submit results until the evaluation closure time.</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
{% load rowerfilters %}
|
||||||
|
|
||||||
|
{% block title %}Edit Virtual Race{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="grid_12 alpha">
|
||||||
|
|
||||||
|
<h1>Edit Race {{ race.name }}</h1>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="grid_8 alpha">
|
||||||
|
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
|
||||||
|
{% if form.errors %}
|
||||||
|
<p style="color: red;">
|
||||||
|
Please correct the error{{ form.errors|pluralize }} below.
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<table>
|
||||||
|
{{ form.as_table }}
|
||||||
|
</table>
|
||||||
|
{% csrf_token %}
|
||||||
|
<div id="formbutton" class="grid_1 prefix_4 suffix_1">
|
||||||
|
<input class="button green" type="submit" value="Save">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="grid_4 omega">
|
||||||
|
<p>
|
||||||
|
<ul>
|
||||||
|
<li>All times are local times in the race course time zone</li>
|
||||||
|
<li>Adding a contact phone number and email is not mandatory, but we
|
||||||
|
strongly recommend it.</li>
|
||||||
|
<li>If your event has a registration closure deadline, participants
|
||||||
|
have to enter (and can withdraw) before the registration closure time.</li>
|
||||||
|
<li>Participants can submit results until the evaluation closure time.</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
{% load rowerfilters %}
|
||||||
|
|
||||||
|
{% block title %}Register for a Virtual Race{% endblock %}
|
||||||
|
|
||||||
|
{% block meta %}
|
||||||
|
<script type='text/javascript'
|
||||||
|
src='https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js'>
|
||||||
|
</script>
|
||||||
|
<script type='text/javascript'
|
||||||
|
src='https://ajax.aspnetcdn.com/ajax/jquery.validate/1.14.0/jquery.validate.min.js'>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="grid_12 alpha">
|
||||||
|
<div class="grid_6 alpha">
|
||||||
|
<h1>Register for {{ race.name }}</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form id="race_register_form"
|
||||||
|
method="post">
|
||||||
|
<div class="grid_12 alpha">
|
||||||
|
<p>If you are participating in a single, we will take the age and weight
|
||||||
|
value from your user settings. For other boat types, please fill out
|
||||||
|
crew weight class and average age.
|
||||||
|
</p>
|
||||||
|
<div class="grid_6 alpha">
|
||||||
|
<table width="100%">
|
||||||
|
{{ form.as_table }}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grid_2 prefix_2 suffix_8">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input class="button green" type="submit" value="Submit">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
{% load rowerfilters %}
|
||||||
|
|
||||||
|
{% block title %}Rowsandall Virtual Racing{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="grid_12 alpha">
|
||||||
|
<div class="grid_2 alpha suffix_10">
|
||||||
|
<p>
|
||||||
|
<a class="button green small" href="/rowers/virtualevent/create">Add Race</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid_12 alpha">
|
||||||
|
<form enctype="multipart/form-data" method="post">
|
||||||
|
<div class="grid_8 alpha">
|
||||||
|
{{ form.as_table }}
|
||||||
|
{% csrf_token %}
|
||||||
|
</div>
|
||||||
|
<div class="grid_2">
|
||||||
|
<input name='form' class='button green' type='submit' value="Submit">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid_12 alpha">
|
||||||
|
<p>
|
||||||
|
<table width="100%" class="listtable shortpadded">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Date</th>
|
||||||
|
<th>Event</th>
|
||||||
|
<th>Country</th>
|
||||||
|
<th>Course</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for race in races %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ race.startdate }}</td>
|
||||||
|
<td><a href="/rowers/virtualevent/{{ race.id }}">{{ race.name }}</a></td>
|
||||||
|
<td>{{ race.course.country }}</td>
|
||||||
|
<td>{{ race.course.name }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -5,6 +5,7 @@ import dateutil.parser
|
|||||||
import json
|
import json
|
||||||
import datetime
|
import datetime
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
from rowers.utils import calculate_age
|
||||||
|
|
||||||
def strfdelta(tdelta):
|
def strfdelta(tdelta):
|
||||||
minutes,seconds = divmod(tdelta.seconds,60)
|
minutes,seconds = divmod(tdelta.seconds,60)
|
||||||
@@ -136,6 +137,11 @@ from rowers.views import hasplannedsessions
|
|||||||
def is_planmember(user):
|
def is_planmember(user):
|
||||||
return hasplannedsessions(user)
|
return hasplannedsessions(user)
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def get_age(r):
|
||||||
|
return calculate_age(r.birthdate)
|
||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def user_teams(user):
|
def user_teams(user):
|
||||||
try:
|
try:
|
||||||
@@ -196,6 +202,7 @@ def team_rowers(user):
|
|||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def verbosetimeperiod(timeperiod):
|
def verbosetimeperiod(timeperiod):
|
||||||
table = {
|
table = {
|
||||||
|
|||||||
@@ -143,6 +143,14 @@ urlpatterns = [
|
|||||||
url(r'^u/(?P<userid>\d+)/list-workouts/$',views.workouts_view),
|
url(r'^u/(?P<userid>\d+)/list-workouts/$',views.workouts_view),
|
||||||
url(r'^u/(?P<userid>\d+)/list-workouts/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.workouts_view),
|
url(r'^u/(?P<userid>\d+)/list-workouts/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.workouts_view),
|
||||||
url(r'^list-workouts/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.workouts_view),
|
url(r'^list-workouts/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.workouts_view),
|
||||||
|
url(r'^virtualevents$',views.virtualevents_view),
|
||||||
|
url(r'^virtualevent/create$',views.virtualevent_create_view),
|
||||||
|
url(r'^virtualevent/(?P<id>\d+)$',views.virtualevent_view),
|
||||||
|
url(r'^virtualevent/(?P<id>\d+)/edit$',views.virtualevent_edit_view),
|
||||||
|
url(r'^virtualevent/(?P<id>\d+)/register$',views.virtualevent_register_view),
|
||||||
|
url(r'^virtualevent/(?P<id>\d+)/withdraw$',views.virtualevent_withdraw_view),
|
||||||
|
url(r'^virtualevent/(?P<id>\d+)/submit$',
|
||||||
|
views.virtualevent_submit_result_view),
|
||||||
url(r'^list-workouts/$',views.workouts_view),
|
url(r'^list-workouts/$',views.workouts_view),
|
||||||
url(r'^list-courses/$',views.courses_view),
|
url(r'^list-courses/$',views.courses_view),
|
||||||
url(r'^courses/upload$',views.course_upload_view),
|
url(r'^courses/upload$',views.course_upload_view),
|
||||||
|
|||||||
@@ -337,3 +337,31 @@ def wavg(group, avg_name, weight_name):
|
|||||||
return (d * w).sum() / w.sum()
|
return (d * w).sum() / w.sum()
|
||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
return d.mean()
|
return d.mean()
|
||||||
|
|
||||||
|
def totaltime_sec_to_string(totaltime):
|
||||||
|
hours = int(totaltime / 3600.)
|
||||||
|
if hours > 23:
|
||||||
|
message = 'Warning: The workout duration was longer than 23 hours. '
|
||||||
|
hours = 23
|
||||||
|
|
||||||
|
minutes = int((totaltime - 3600. * hours) / 60.)
|
||||||
|
if minutes > 59:
|
||||||
|
minutes = 59
|
||||||
|
if not message:
|
||||||
|
message = 'Warning: there is something wrong with the workout duration'
|
||||||
|
|
||||||
|
seconds = int(totaltime - 3600. * hours - 60. * minutes)
|
||||||
|
if seconds > 59:
|
||||||
|
seconds = 59
|
||||||
|
if not message:
|
||||||
|
message = 'Warning: there is something wrong with the workout duration'
|
||||||
|
|
||||||
|
tenths = int(10 * (totaltime - 3600. * hours - 60. * minutes - seconds))
|
||||||
|
if tenths > 9:
|
||||||
|
tenths = 9
|
||||||
|
if not message:
|
||||||
|
message = 'Warning: there is something wrong with the workout duration'
|
||||||
|
|
||||||
|
duration = "%s:%s:%s.%s" % (hours, minutes, seconds, tenths)
|
||||||
|
|
||||||
|
return duration
|
||||||
|
|||||||
+473
-2
@@ -34,6 +34,7 @@ from rowers.forms import (
|
|||||||
WorkFlowLeftPanelElement,WorkFlowMiddlePanelElement,
|
WorkFlowLeftPanelElement,WorkFlowMiddlePanelElement,
|
||||||
LandingPageForm,PlannedSessionSelectForm,WorkoutSessionSelectForm,
|
LandingPageForm,PlannedSessionSelectForm,WorkoutSessionSelectForm,
|
||||||
PlannedSessionTeamForm,PlannedSessionTeamMemberForm,
|
PlannedSessionTeamForm,PlannedSessionTeamMemberForm,
|
||||||
|
VirtualRaceSelectForm,WorkoutRaceSelectForm,
|
||||||
)
|
)
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
@@ -65,7 +66,8 @@ from rowers.models import (
|
|||||||
Team,TeamForm,TeamInviteForm,TeamInvite,TeamRequest,
|
Team,TeamForm,TeamInviteForm,TeamInvite,TeamRequest,
|
||||||
WorkoutComment,WorkoutCommentForm,RowerExportForm,
|
WorkoutComment,WorkoutCommentForm,RowerExportForm,
|
||||||
CalcAgePerformance,PowerTimeFitnessMetric,PlannedSessionForm,
|
CalcAgePerformance,PowerTimeFitnessMetric,PlannedSessionForm,
|
||||||
PlannedSessionFormSmall,GeoCourseEditForm,
|
PlannedSessionFormSmall,GeoCourseEditForm,VirtualRace,
|
||||||
|
VirtualRaceForm,VirtualRaceResultForm,
|
||||||
)
|
)
|
||||||
from rowers.models import (
|
from rowers.models import (
|
||||||
FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement,BasePlannedSessionFormSet
|
FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement,BasePlannedSessionFormSet
|
||||||
@@ -146,6 +148,7 @@ import matplotlib.pyplot as plt
|
|||||||
from rowers.emails import send_template_email
|
from rowers.emails import send_template_email
|
||||||
|
|
||||||
from pytz import timezone as tz,utc
|
from pytz import timezone as tz,utc
|
||||||
|
from timezonefinder import TimezoneFinder
|
||||||
import dateutil
|
import dateutil
|
||||||
import mpld3
|
import mpld3
|
||||||
from mpld3 import plugins
|
from mpld3 import plugins
|
||||||
@@ -11511,7 +11514,6 @@ def rower_edit_view(request,rowerid=0,message=""):
|
|||||||
r.sex = sex
|
r.sex = sex
|
||||||
r.birthdate = birthdate
|
r.birthdate = birthdate
|
||||||
if resetbounce and r.emailbounced:
|
if resetbounce and r.emailbounced:
|
||||||
print "aap"
|
|
||||||
r.emailbounced = False
|
r.emailbounced = False
|
||||||
r.save()
|
r.save()
|
||||||
form = RowerForm(instance=r)
|
form = RowerForm(instance=r)
|
||||||
@@ -13313,3 +13315,472 @@ def plannedsession_deleteconfirm_view(request,id=0):
|
|||||||
}
|
}
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def virtualevents_view(request):
|
||||||
|
|
||||||
|
# default races
|
||||||
|
races = VirtualRace.objects.filter(
|
||||||
|
startdate__gte=datetime.date.today()
|
||||||
|
).order_by("startdate","start_time")
|
||||||
|
|
||||||
|
r = getrower(request.user)
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
# process form
|
||||||
|
form = VirtualRaceSelectForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
cd = form.cleaned_data
|
||||||
|
country = cd['country']
|
||||||
|
regattatype = cd['regattatype']
|
||||||
|
if country == 'All':
|
||||||
|
countries = VirtualRace.objects.order_by('country').values_list('country').distinct()
|
||||||
|
else:
|
||||||
|
countries = [country]
|
||||||
|
|
||||||
|
if regattatype == 'upcoming':
|
||||||
|
races = VirtualRace.objects.filter(
|
||||||
|
startdate__gte=datetime.date.today(),
|
||||||
|
country__in=countries
|
||||||
|
).order_by("startdate","start_time")
|
||||||
|
elif regattatype == 'previous':
|
||||||
|
races = VirtualRace.objects.filter(
|
||||||
|
enddate__lt=datetime.date.today(),
|
||||||
|
country__in=countries
|
||||||
|
).order_by("startdate","start_time")
|
||||||
|
elif regattatype == 'ongoing':
|
||||||
|
races = VirtualRace.objects.filter(
|
||||||
|
startdate__lte=datetime.date.today(),
|
||||||
|
evaluation_closure__gte=datetime.date.today(),
|
||||||
|
country__in=countries
|
||||||
|
).order_by("startdate","start_time")
|
||||||
|
elif regattatype == 'my':
|
||||||
|
mysessions = get_my_session_ids(r)
|
||||||
|
races = VirtualRace.objects.filter(
|
||||||
|
id__in=mysessions,
|
||||||
|
country__in=countries
|
||||||
|
).order_by("startdate","start_time")
|
||||||
|
elif regattatype == 'all':
|
||||||
|
races = VirtualRace.objects.filter(
|
||||||
|
country__in=countries
|
||||||
|
).order_by("startdate","start_time")
|
||||||
|
else:
|
||||||
|
|
||||||
|
form = VirtualRaceSelectForm()
|
||||||
|
|
||||||
|
|
||||||
|
return render(request,'virtualevents.html',
|
||||||
|
{ 'races':races,
|
||||||
|
'form':form,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def virtualevent_view(request,id=0):
|
||||||
|
if not request.user.is_anonymous():
|
||||||
|
r = getrower(request.user)
|
||||||
|
else:
|
||||||
|
r = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
race = VirtualRace.objects.get(id=id)
|
||||||
|
except VirtualRace.DoesNotExist:
|
||||||
|
raise Http404("Virtual Race does not exist")
|
||||||
|
|
||||||
|
script,div = course_map(race.course)
|
||||||
|
|
||||||
|
buttons = []
|
||||||
|
|
||||||
|
if not request.user.is_anonymous():
|
||||||
|
if race_can_register(r,race):
|
||||||
|
buttons += ['registerbutton']
|
||||||
|
|
||||||
|
if race_can_submit(r,race):
|
||||||
|
buttons += ['submitbutton']
|
||||||
|
|
||||||
|
if race_can_resubmit(r,race):
|
||||||
|
buttons += ['resubmitbutton']
|
||||||
|
|
||||||
|
if race_can_withdraw(r,race):
|
||||||
|
buttons += ['withdrawbutton']
|
||||||
|
|
||||||
|
if race_can_edit(r,race):
|
||||||
|
buttons += ['editbutton']
|
||||||
|
|
||||||
|
results = VirtualRaceResult.objects.filter(
|
||||||
|
race=race,
|
||||||
|
workout__isnull=False,
|
||||||
|
).order_by("duration")
|
||||||
|
|
||||||
|
# to-do - add DNS
|
||||||
|
dns = []
|
||||||
|
if timezone.now() > race.evaluation_closure:
|
||||||
|
print "aap"
|
||||||
|
dns = VirtualRaceResult.objects.filter(
|
||||||
|
race=race,
|
||||||
|
workout__isnull=True,
|
||||||
|
)
|
||||||
|
print dns[0].username,"noot"
|
||||||
|
|
||||||
|
|
||||||
|
records = VirtualRaceResult.objects.filter(
|
||||||
|
race=race
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
return render(request,'virtualevent.html',
|
||||||
|
{
|
||||||
|
'coursescript':script,
|
||||||
|
'coursediv':div,
|
||||||
|
'race':race,
|
||||||
|
'rower':r,
|
||||||
|
'results':results,
|
||||||
|
'buttons':buttons,
|
||||||
|
'dns':dns,
|
||||||
|
'records':records,
|
||||||
|
})
|
||||||
|
|
||||||
|
@login_required()
|
||||||
|
def virtualevent_withdraw_view(request,id=0):
|
||||||
|
r = getrower(request.user)
|
||||||
|
try:
|
||||||
|
race = VirtualRace.objects.get(id=id)
|
||||||
|
except VirtualRace.DoesNotExist:
|
||||||
|
raise Http404("Virtual Race does not exist")
|
||||||
|
|
||||||
|
if race_can_withdraw(r,race):
|
||||||
|
remove_rower_race(r,race)
|
||||||
|
messages.info(request,
|
||||||
|
"You have successfully withdrawn from this race.")
|
||||||
|
else:
|
||||||
|
messages.error(request,"You cannot withdraw from this race")
|
||||||
|
|
||||||
|
url = reverse(virtualevent_view,
|
||||||
|
kwargs = {
|
||||||
|
'id':race.id
|
||||||
|
})
|
||||||
|
|
||||||
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
|
@login_required()
|
||||||
|
def virtualevent_register_view(request,id=0):
|
||||||
|
r = getrower(request.user)
|
||||||
|
try:
|
||||||
|
race = VirtualRace.objects.get(id=id)
|
||||||
|
except VirtualRace.DoesNotExist:
|
||||||
|
raise Http404("Virtual Race does not exist")
|
||||||
|
|
||||||
|
if not race_can_register(r,race):
|
||||||
|
messages.error(request,"You cannot register for this race")
|
||||||
|
|
||||||
|
url = reverse(virtualevent_view,
|
||||||
|
kwargs = {
|
||||||
|
'id':race.id
|
||||||
|
})
|
||||||
|
|
||||||
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
|
# we're still here
|
||||||
|
if request.method == 'POST':
|
||||||
|
# process form
|
||||||
|
form = VirtualRaceResultForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
cd = form.cleaned_data
|
||||||
|
teamname = cd['teamname']
|
||||||
|
boattype = cd['boattype']
|
||||||
|
if not boattype == '1x':
|
||||||
|
weightcategory = cd['weightcategory']
|
||||||
|
age = cd['age']
|
||||||
|
else:
|
||||||
|
weightcategory = r.weightcategory
|
||||||
|
age = calculate_age(r.birthdate)
|
||||||
|
|
||||||
|
record = VirtualRaceResult(
|
||||||
|
user=r,
|
||||||
|
teamname=teamname,
|
||||||
|
race=race,
|
||||||
|
username = u'{f} {l}'.format(
|
||||||
|
f = r.user.first_name,
|
||||||
|
l = r.user.last_name
|
||||||
|
),
|
||||||
|
weightcategory=weightcategory,
|
||||||
|
duration=datetime.time(0,0),
|
||||||
|
boattype=boattype,
|
||||||
|
coursecompleted=False,
|
||||||
|
sex=r.sex,
|
||||||
|
age=age
|
||||||
|
)
|
||||||
|
|
||||||
|
record.save()
|
||||||
|
|
||||||
|
add_rower_race(r,race)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
"You have successfully registered for this race. Good luck!"
|
||||||
|
)
|
||||||
|
|
||||||
|
url = reverse(virtualevent_view,
|
||||||
|
kwargs = {
|
||||||
|
'id':race.id
|
||||||
|
})
|
||||||
|
|
||||||
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
|
else:
|
||||||
|
initial = {
|
||||||
|
'age': calculate_age(r.birthdate),
|
||||||
|
'weightcategory': r.weightcategory,
|
||||||
|
}
|
||||||
|
|
||||||
|
form = VirtualRaceResultForm(initial=initial)
|
||||||
|
|
||||||
|
return render(request,'virtualeventregister.html',
|
||||||
|
{
|
||||||
|
'form':form,
|
||||||
|
'race':race,
|
||||||
|
'rower':r,
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
@login_required()
|
||||||
|
def virtualevent_create_view(request):
|
||||||
|
r = getrower(request.user)
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
racecreateform = VirtualRaceForm(request.POST)
|
||||||
|
if racecreateform.is_valid():
|
||||||
|
cd = racecreateform.cleaned_data
|
||||||
|
startdate = cd['startdate']
|
||||||
|
start_time = cd['start_time']
|
||||||
|
enddate = cd['enddate']
|
||||||
|
end_time = cd['end_time']
|
||||||
|
comment = cd['comment']
|
||||||
|
course = cd['course']
|
||||||
|
name = cd['name']
|
||||||
|
has_registration = cd['has_registration']
|
||||||
|
registration_closure = cd['registration_closure']
|
||||||
|
evaluation_closure = cd['evaluation_closure']
|
||||||
|
contact_phone = cd['contact_phone']
|
||||||
|
contact_email = cd['contact_email']
|
||||||
|
|
||||||
|
# correct times
|
||||||
|
|
||||||
|
geocourse = GeoCourse.objects.get(id= course.id)
|
||||||
|
timezone_str = courses.get_course_timezone(geocourse)
|
||||||
|
|
||||||
|
startdatetime = datetime.datetime.combine(startdate,start_time)
|
||||||
|
enddatetime = datetime.datetime.combine(enddate,end_time)
|
||||||
|
|
||||||
|
startdatetime = pytz.timezone(timezone_str).localize(
|
||||||
|
startdatetime
|
||||||
|
)
|
||||||
|
enddatetime = pytz.timezone(timezone_str).localize(
|
||||||
|
enddatetime
|
||||||
|
)
|
||||||
|
evaluation_closure = pytz.timezone(timezone_str).localize(
|
||||||
|
evaluation_closure.replace(tzinfo=None)
|
||||||
|
)
|
||||||
|
registration_closure = pytz.timezone(timezone_str).localize(
|
||||||
|
registration_closure.replace(tzinfo=None)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
vs = VirtualRace(
|
||||||
|
name=name,
|
||||||
|
startdate=startdate,
|
||||||
|
preferreddate = startdate,
|
||||||
|
start_time = start_time,
|
||||||
|
enddate=enddate,
|
||||||
|
end_time=end_time,
|
||||||
|
course=geocourse,
|
||||||
|
comment=comment,
|
||||||
|
sessiontype = 'coursetest',
|
||||||
|
timezone=timezone_str,
|
||||||
|
has_registration=has_registration,
|
||||||
|
evaluation_closure=evaluation_closure,
|
||||||
|
registration_closure=registration_closure,
|
||||||
|
contact_phone=contact_phone,
|
||||||
|
contact_email=contact_email,
|
||||||
|
country = course.country,
|
||||||
|
manager=request.user,
|
||||||
|
)
|
||||||
|
|
||||||
|
vs.save()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
url = reverse(virtualevents_view)
|
||||||
|
return HttpResponseRedirect(url)
|
||||||
|
else:
|
||||||
|
|
||||||
|
racecreateform = VirtualRaceForm()
|
||||||
|
|
||||||
|
|
||||||
|
return render(request,'virtualeventcreate.html',
|
||||||
|
{
|
||||||
|
'form':racecreateform,
|
||||||
|
'rower':r,
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
@login_required()
|
||||||
|
def virtualevent_edit_view(request,id=0):
|
||||||
|
r = getrower(request.user)
|
||||||
|
|
||||||
|
try:
|
||||||
|
race = VirtualRace.objects.get(id=id)
|
||||||
|
if race.manager != request.user:
|
||||||
|
raise PermissionDenied("Access denied")
|
||||||
|
except VirtualRace.DoesNotExist:
|
||||||
|
raise Http404("Virtual Race does not exist")
|
||||||
|
|
||||||
|
start_time = race.start_time
|
||||||
|
start_date = race.startdate
|
||||||
|
startdatetime = datetime.datetime.combine(start_date,start_time)
|
||||||
|
startdatetime = pytz.timezone(race.timezone).localize(
|
||||||
|
startdatetime
|
||||||
|
)
|
||||||
|
|
||||||
|
if timezone.now() > startdatetime:
|
||||||
|
messages.error(request,"You cannot edit a race after the start of the race window")
|
||||||
|
url = reverse(virtualevent_view,
|
||||||
|
kwargs={
|
||||||
|
'id':race.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
racecreateform = VirtualRaceForm(request.POST,instance=race)
|
||||||
|
if racecreateform.is_valid():
|
||||||
|
cd = racecreateform.cleaned_data
|
||||||
|
|
||||||
|
res, message = update_virtualrace(race,cd)
|
||||||
|
|
||||||
|
if res:
|
||||||
|
messages.info(request,message)
|
||||||
|
else:
|
||||||
|
messages.error(request,message)
|
||||||
|
|
||||||
|
url = reverse(virtualevent_view,
|
||||||
|
kwargs = {
|
||||||
|
'id':race.id
|
||||||
|
})
|
||||||
|
|
||||||
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
racecreateform = VirtualRaceForm(instance=race)
|
||||||
|
|
||||||
|
|
||||||
|
return render(request,'virtualeventedit.html',
|
||||||
|
{
|
||||||
|
'form':racecreateform,
|
||||||
|
'rower':r,
|
||||||
|
'race':race,
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
@login_required()
|
||||||
|
def virtualevent_submit_result_view(request,id=0):
|
||||||
|
|
||||||
|
r = getrower(request.user)
|
||||||
|
|
||||||
|
try:
|
||||||
|
race = VirtualRace.objects.get(id=id)
|
||||||
|
except VirtualRace.DoesNotExist:
|
||||||
|
raise Http404("Virtual Race does not exist")
|
||||||
|
|
||||||
|
start_time = race.start_time
|
||||||
|
start_date = race.startdate
|
||||||
|
startdatetime = datetime.datetime.combine(start_date, start_time)
|
||||||
|
startdatetime = pytz.timezone(race.timezone).localize(startdatetime)
|
||||||
|
|
||||||
|
end_time = race.end_time
|
||||||
|
end_date = race.enddate
|
||||||
|
enddatetime = datetime.datetime.combine(end_date, end_time)
|
||||||
|
enddatetime = pytz.timezone(race.timezone).localize(enddatetime)
|
||||||
|
|
||||||
|
can_submit = race_can_submit(r,race) or race_can_resubmit(r,race)
|
||||||
|
|
||||||
|
if not can_submit:
|
||||||
|
messages.error(request,'You cannot submit a result to this race')
|
||||||
|
url = reverse(virtualevent_view,
|
||||||
|
kwargs = {
|
||||||
|
'id':id
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
|
ws = Workout.objects.filter(
|
||||||
|
user=r,
|
||||||
|
startdatetime__gte=startdatetime,
|
||||||
|
startdatetime__lte=enddatetime,
|
||||||
|
).order_by("date","startdatetime","id")
|
||||||
|
|
||||||
|
initialworkouts = [w.id for w in Workout.objects.filter(
|
||||||
|
user=r,plannedsession=race
|
||||||
|
)]
|
||||||
|
|
||||||
|
workoutdata = {}
|
||||||
|
workoutdata['initial'] = []
|
||||||
|
|
||||||
|
choices = []
|
||||||
|
|
||||||
|
for w in ws:
|
||||||
|
wtpl = (w.id, w.__unicode__())
|
||||||
|
choices.append(wtpl)
|
||||||
|
if w.id in initialworkouts:
|
||||||
|
workoutdata['initial'].append(w.id)
|
||||||
|
|
||||||
|
workoutdata['choices'] = tuple(choices)
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
w_form = WorkoutRaceSelectForm(workoutdata,request.POST)
|
||||||
|
|
||||||
|
if w_form.is_valid():
|
||||||
|
selectedworkout = w_form.cleaned_data['workouts']
|
||||||
|
else:
|
||||||
|
selectedworkout = None
|
||||||
|
|
||||||
|
|
||||||
|
for w in ws:
|
||||||
|
remove_workout_plannedsession(w,race)
|
||||||
|
|
||||||
|
if selectedworkout is not None:
|
||||||
|
|
||||||
|
for w in ws:
|
||||||
|
remove_workout_plannedsession(w,race)
|
||||||
|
delete_race_result(w,race)
|
||||||
|
|
||||||
|
workouts = Workout.objects.filter(id=selectedworkout)
|
||||||
|
|
||||||
|
result,comments,errors = add_workout_race(workouts,race,r)
|
||||||
|
|
||||||
|
for c in comments:
|
||||||
|
messages.info(request,c)
|
||||||
|
for er in errors:
|
||||||
|
messages.error(request,er)
|
||||||
|
|
||||||
|
# redirect to race page
|
||||||
|
url = reverse(virtualevent_view,
|
||||||
|
kwargs = {
|
||||||
|
'id':race.id
|
||||||
|
})
|
||||||
|
|
||||||
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
|
else:
|
||||||
|
w_form = WorkoutRaceSelectForm(workoutdata=workoutdata)
|
||||||
|
|
||||||
|
return render(request,'race_submit.html',
|
||||||
|
{
|
||||||
|
'race':race,
|
||||||
|
'workouts':ws,
|
||||||
|
'rower':r,
|
||||||
|
'w_form':w_form,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
Vendored
+1
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -39,6 +39,10 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block challenges %}
|
||||||
|
<a class="button gray small" href="/rowers/virtualevents">Racing</a>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -62,6 +62,10 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block challenges %}
|
||||||
|
<a class="button gray small" href="/rowers/virtualevents">Racing</a>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block body_bottom %}
|
{% block body_bottom %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user