Private
Public Access
1
0

Merge branch 'release/v12.61'

This commit is contained in:
Sander Roosendaal
2020-05-28 12:47:30 +02:00
26 changed files with 1528 additions and 178 deletions
+9 -1
View File
@@ -11,7 +11,7 @@ from .models import (
Team,TeamInvite,TeamRequest,
WorkoutComment,C2WorldClassAgePerformance,PlannedSession,
GeoCourse,GeoPolygon,GeoPoint,VirtualRace,VirtualRaceResult,
PaidPlan,IndoorVirtualRaceResult,ShareKey
PaidPlan,IndoorVirtualRaceResult,ShareKey, CourseStandard,StandardCollection,
)
# Register your models here so you can use them in the Admin module
@@ -141,6 +141,12 @@ class IndoorVirtualRaceResultAdmin(admin.ModelAdmin):
class PaidPlanAdmin(admin.ModelAdmin):
list_display = ('name','shortname','price','paymenttype','paymentprocessor','external_id')
class StandardCollectionAdmin(admin.ModelAdmin):
list_display = ('name','manager')
class CourseStandardAdmin(admin.ModelAdmin):
list_display = ('name','standardcollection')
admin.site.unregister(User)
admin.site.register(User,UserAdmin)
admin.site.register(Workout,WorkoutAdmin)
@@ -160,3 +166,5 @@ admin.site.register(VirtualRaceResult, VirtualRaceResultAdmin)
admin.site.register(IndoorVirtualRaceResult, IndoorVirtualRaceResultAdmin)
admin.site.register(PaidPlan,PaidPlanAdmin)
admin.site.register(ShareKey,ShareKeyAdmin)
admin.site.register(CourseStandard,CourseStandardAdmin)
admin.site.register(StandardCollection,StandardCollectionAdmin)
+5
View File
@@ -2,6 +2,11 @@ from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
# low level methods
def coordinate_in_path(latitude,longitude, p):
+4 -1
View File
@@ -1905,7 +1905,10 @@ def testdata(time, distance, pace, spm):
def getrowdata_db(id=0, doclean=False, convertnewtons=True,
checkefficiency=True):
data = read_df_sql(id)
data['deltat'] = data['time'].diff()
try:
data['deltat'] = data['time'].diff()
except KeyError:
data = pd.DataFrame()
if data.empty:
rowdata, row = getrowdata(id=id)
+33
View File
@@ -212,6 +212,18 @@ class CourseForm(forms.Form):
from django.forms.widgets import HiddenInput
super(CourseForm, self).__init__(*args, **kwargs)
# The form used for uploading images
class StandardsForm(forms.Form):
name = forms.CharField(max_length=150,label='Course Name')
file = forms.FileField(required=False,
validators=[must_be_csv])
notes = forms.CharField(required=False,
max_length=200,label='Course Notes',
widget=forms.Textarea)
def __init__(self, *args, **kwargs):
from django.forms.widgets import HiddenInput
super(StandardsForm, self).__init__(*args, **kwargs)
# The form used for uploading files
class DocumentsForm(forms.Form):
@@ -1218,6 +1230,12 @@ class RaceResultFilterForm(forms.Form):
initial=['None','PR1','PR2','PR3','FES'],
widget=forms.CheckboxSelectMultiple())
entrycategory = forms.MultipleChoiceField(
choices = [],
label = 'Groups',
widget=forms.CheckboxSelectMultiple()
)
def __init__(self, *args, **kwargs):
if 'records' in kwargs:
records = kwargs.pop('records',None)
@@ -1225,6 +1243,21 @@ class RaceResultFilterForm(forms.Form):
super(RaceResultFilterForm,self).__init__(*args,**kwargs)
if records:
# group
thecategories = [record.entrycategory for record in records]
thecategories = list(set(thecategories))
if len(thecategories) <= 1:
del self.fields['entrycategory']
else:
categorychoices = []
for category in thecategories:
if category is not None:
categorychoices.append(
(category.id,category)
)
self.fields['entrycategory'].choices = categorychoices
self.fields['entrycategory'].initial = [cat[0] for cat in categorychoices]
# sex
thesexes = [record.sex for record in records]
thesexes = list(set(thesexes))
File diff suppressed because one or more lines are too long
+98 -19
View File
@@ -243,15 +243,9 @@ def update_records(url=c2url,verbose=True):
class CalcAgePerformance(models.Model):
weightcategories = (
('hwt','heavy-weight'),
('lwt','light-weight'),
)
weightcategories = mytypes.weightcategories
sexcategories = (
('male','male'),
('female','female'),
)
sexcategories = mytypes.sexcategories
weightcategory = models.CharField(default="hwt",
max_length=30,
@@ -288,10 +282,7 @@ class PowerTimeFitnessMetric(models.Model):
@python_2_unicode_compatible
class C2WorldClassAgePerformance(models.Model):
weightcategories = (
('hwt','heavy-weight'),
('lwt','light-weight'),
)
weightcategories = mytypes.weightcategories
sexcategories = (
('male','male'),
@@ -593,10 +584,8 @@ sexcategories = (
('female','female'),
('not specified','not specified'),
)
weightcategories = (
('hwt','heavy-weight'),
('lwt','light-weight'),
)
weightcategories = mytypes.weightcategories
# Plan
@@ -2194,7 +2183,38 @@ class PlannedSession(models.Model):
from django.core.validators import RegexValidator,validate_email
class StandardCollection(models.Model):
name = models.CharField(max_length=150)
manager = models.ForeignKey(User, null=True,on_delete=models.CASCADE)
notes = models.CharField(blank=True,null=True,max_length=1000)
active = models.BooleanField(default=True)
def __str__(self):
return self.name
class CourseStandard(models.Model):
name = models.CharField(max_length=150)
coursedistance = models.IntegerField()
coursetime = models.CharField(max_length=100,default="")
referencespeed = models.FloatField() # average boat speed
agemin = models.IntegerField(default=0)
agemax = models.IntegerField(default=120)
boatclass = models.CharField(max_length=150) # corresponds to workout workouttype
boattype = models.CharField(choices=mytypes.boattypes,max_length=50,default='1x')
sex = models.CharField(max_length=150)
weightclass = models.CharField(max_length=150)
adaptiveclass = models.CharField(choices=mytypes.adaptivetypes,max_length=50,default="None")
skillclass = models.CharField(max_length=150)
standardcollection = models.ForeignKey(StandardCollection,on_delete=models.CASCADE)
class Meta:
unique_together = (
('name','standardcollection')
)
def __str__(self):
return self.name
registerchoices = (
('windowstart','Start of challenge Window'),
@@ -2233,6 +2253,9 @@ class VirtualRace(PlannedSession):
contact_email = models.EmailField(max_length=254,
validators=[validate_email],blank=True)
coursestandards = models.ForeignKey(StandardCollection,null=True,on_delete=models.SET_NULL,
verbose_name='Standard Times',blank=True)
def __str__(self):
name = self.name
@@ -2401,6 +2424,7 @@ class IndoorVirtualRaceForm(ModelForm):
'registration_closure',
'evaluation_closure',
'comment',
'coursestandards',
'contact_phone',
'contact_email',
]
@@ -2434,6 +2458,7 @@ class IndoorVirtualRaceForm(ModelForm):
self.fields['sessionunit'].initial = 'm'
if timezone:
self.fields['timezone'].initial = timezone
self.fields['coursestandards'].queryset = StandardCollection.objects.filter(active=True)
def clean(self):
cd = self.cleaned_data
@@ -2537,6 +2562,7 @@ class VirtualRaceForm(ModelForm):
'registration_closure',
'evaluation_closure',
'course',
'coursestandards',
'comment',
'contact_phone',
'contact_email',
@@ -2560,6 +2586,7 @@ class VirtualRaceForm(ModelForm):
def __init__(self,*args,**kwargs):
super(VirtualRaceForm, self).__init__(*args, **kwargs)
self.fields['course'].queryset = GeoCourse.objects.all().order_by("country","name")
self.fields['coursestandards'].queryset = StandardCollection.objects.filter(active=True)
def clean(self):
@@ -2905,9 +2932,12 @@ class VirtualRaceResult(models.Model):
adaptiveclass = models.CharField(default="None",max_length=50,
choices=mytypes.adaptivetypes,
verbose_name="Adaptive Class")
skillclass = models.CharField(default="Open",max_length=50,
verbose_name="Skill Class")
race = models.ForeignKey(VirtualRace,on_delete=models.CASCADE)
duration = models.TimeField(default=datetime.time(1,0))
distance = models.IntegerField(default=0)
points = models.IntegerField(default=0)
boatclass = models.CharField(choices=boatclasses,
max_length=40,
default='water',
@@ -2928,6 +2958,9 @@ class VirtualRaceResult(models.Model):
startsecond = models.FloatField(default=0)
endsecond = models.FloatField(default=0)
referencespeed = models.FloatField(default=5.0)
entrycategory = models.ForeignKey(CourseStandard,null=True,on_delete=models.SET_NULL,
verbose_name='Group')
def __str__(self):
rr = Rower.objects.get(id=self.userid)
@@ -2936,6 +2969,14 @@ class VirtualRaceResult(models.Model):
u2 = rr.user.last_name,
)
if self.teamname:
if self.entrycategory:
return u'Entry for {n} for "{r}" in {g} with {t}'.format(
n = name,
r = self.race,
g = self.entrycategory,
t = self.teamname,
)
return u'Entry for {n} for "{r}" in {c} {d} with {t} ({s})'.format(
n = name,
r = self.race,
@@ -2945,6 +2986,12 @@ class VirtualRaceResult(models.Model):
s = self.sex,
)
else:
if self.entrycategory:
return u'Entry for {n} for "{r}" in {g}'.format(
n = name,
r = self.race,
g = self.entrycategory,
)
return u'Entry for {n} for "{r}" in {c} {d} ({s})'.format(
n = name,
r = self.race,
@@ -2968,9 +3015,13 @@ class IndoorVirtualRaceResult(models.Model):
adaptiveclass = models.CharField(default="None",max_length=50,
choices=mytypes.adaptivetypes,
verbose_name="Adaptive Class")
skillclass = models.CharField(default="Open",max_length=50,
verbose_name="Skill Class")
race = models.ForeignKey(VirtualRace,on_delete=models.CASCADE)
duration = models.TimeField(default=datetime.time(1,0))
distance = models.IntegerField(default=0)
referencespeed = models.FloatField(default=5.0)
points = models.IntegerField(default=0)
boatclass = models.CharField(choices=boatclasses,
max_length=40,
default='rower',
@@ -2984,6 +3035,8 @@ class IndoorVirtualRaceResult(models.Model):
age = models.IntegerField(null=True)
emailnotifications = models.BooleanField(default=True,
verbose_name = 'Receive challenge notifications by email')
entrycategory = models.ForeignKey(CourseStandard,null=True,on_delete=models.SET_NULL,
verbose_name='Group')
def __str__(self):
rr = Rower.objects.get(id=self.userid)
@@ -2992,6 +3045,13 @@ class IndoorVirtualRaceResult(models.Model):
u2 = rr.user.last_name,
)
if self.teamname:
if self.entrycategory:
return u'Entry for {n} for "{r}" in {g} with {t}'.format(
n = name,
r = self.race,
g = self.entrycategory,
t = self.teamname,
)
return u'Entry for {n} for "{r}" on {c} with {t} ({s})'.format(
n = name,
r = self.race,
@@ -3000,6 +3060,12 @@ class IndoorVirtualRaceResult(models.Model):
s = self.sex,
)
else:
if self.entrycategory:
return u'Entry for {n} for "{r}" in {g}'.format(
n = name,
r = self.race,
g = self.entrycategory,
)
return u'Entry for {n} for "{r}" on {c} ({s})'.format(
n = name,
r = self.race,
@@ -3019,22 +3085,29 @@ class CourseTestResult(models.Model):
class IndoorVirtualRaceResultForm(ModelForm):
class Meta:
model = IndoorVirtualRaceResult
fields = ['teamname','weightcategory','boatclass','age','adaptiveclass']
fields = ['teamname','weightcategory','boatclass','age','adaptiveclass',
'entrycategory']
def __init__(self, *args, **kwargs):
categories = kwargs.pop('categories',None)
super(IndoorVirtualRaceResultForm, self).__init__(*args, **kwargs)
if categories is not None:
self.fields['entrycategory'].queryset = categories
self.fields['entrycategory'].empty_label = None
else:
self.fields.pop('entrycategory')
class VirtualRaceResultForm(ModelForm):
class Meta:
model = VirtualRaceResult
fields = ['teamname','weightcategory','boatclass','boattype',
'age','adaptiveclass']
'age','adaptiveclass','entrycategory']
def __init__(self, *args, **kwargs):
boattypes = kwargs.pop('boattypes',None)
categories = kwargs.pop('categories',None)
super(VirtualRaceResultForm, self).__init__(*args, **kwargs)
if boattypes:
@@ -3044,6 +3117,12 @@ class VirtualRaceResultForm(ModelForm):
required=False,
label='Mixed Gender')
if categories is not None:
self.fields['entrycategory'].queryset = categories
self.fields['entrycategory'].empty_label = None
else:
self.fields.pop('entrycategory')
from rowers.metrics import rowingmetrics
strokedatafields = {
+11 -1
View File
@@ -316,13 +316,23 @@ boattypes = (
)
adaptivetypes = (
('None','None'),
('None','Open'),
('PR1', 'PR1 (Arms and Shoulders)'),
('PR2', 'PR2 (Trunk and Arms)'),
('PR3', 'PR3 (Leg Trunk and Arms)'),
('FES', 'FES (Functional Electrical Stimulation)'),
)
weightcategories = (
('hwt','open-weight'),
('lwt','light-weight'),
)
sexcategories = (
('male','Open'),
('female','Female'),
)
waterboattype = [i[0] for i in boattypes]
privacychoices = (
+3 -1
View File
@@ -1514,6 +1514,7 @@ def add_workout_race(ws,race,r,splitsecond=0,recordid=0):
return 0,comments,errors,0
if ws[0].workouttype != record.boatclass:
print(ws[0].workouttype,record.boatclass)
errors.append('Your workout boat class is different than on your race registration')
return 0,comments,errors,0
@@ -1551,7 +1552,8 @@ def add_workout_race(ws,race,r,splitsecond=0,recordid=0):
comments.append('Workouts submitted to virtual events have to be public. We have changed the workout to a public workout.')
job = myqueue(queue,handle_check_race_course,ws[0].csvfilename,
ws[0].id,race.course.id,record.id,splitsecond=splitsecond)
ws[0].id,race.course.id,record.id,splitsecond=splitsecond,
referencespeed=record.referencespeed,coursedistance=race.course.distance)
+177
View File
@@ -0,0 +1,177 @@
from rowers.models import StandardCollection,CourseStandard, VirtualRaceResult,IndoorVirtualRaceResult
import pandas as pd
import arrow
import datetime
def save_scoring(name,user,filename,id=0,notes=""):
if id==0:
collection = StandardCollection(name=name,manager=user,notes=notes)
collection.save()
standards = CourseStandard.objects.filter(standardcollection=collection)
for standard in standards:
standards.delete()
else:
try:
collection = StandardCollection.objects.get(id=id)
collection.name = name
collection.notes = notes
collection.save()
standards = CourseStandard.objects.filter(standardcollection=collection)
for standard in standards:
records1 = VirtualRaceResult.objects.filter(entrycategory=standard)
records2 = IndoorVirtualRaceResult.objects.filter(entrycategory=standard)
if records1.count()+records2.count() == 0:
standard.delete()
except StandardCollection.DoesNotExist:
return 0
try:
df = pd.read_csv(filename)
except:
return 0
df.rename(
columns={
'name':'Name',
'agemax':'MaxAge',
'agemin':'MinAge',
'adaptiveclass':'AdaptiveClass',
'coursedistance':'CourseDistance',
'coursetime':'CourseStandard',
'boatclass':'BoatClass',
'boattype':'BoatType',
'sex':'Gender',
'weightclass':'WeightClass',
'skillclass':'SkillClass',
},
inplace=True)
df = df.drop_duplicates(['Name'])
for index, row in df.iterrows():
try:
name = row['Name']
except KeyError:
continue
try:
coursedistance = row['CourseDistance']
coursetime = row['CourseStandard']
t = datetime.datetime.strptime(coursetime,'%M:%S.%f')
delta = datetime.timedelta(hours=t.hour, minutes=t.minute, seconds=t.second,microseconds=t.microsecond)
seconds = delta.total_seconds()
referencespeed = coursedistance/seconds
except KeyError:
continue
try:
agemin = row['MinAge']
agemax = row['MaxAge']
agemin = int(agemin)
agemax = int(agemax)
except KeyError:
agemin = 0
agemax = 120
try:
boatclass = row['BoatClass']
if boatclass.lower() in ['standard','olympic','normal','water']:
boatclass = 'water'
elif boatclass.lower() in ['erg','c2','concept','static','rower']:
boatclass = 'rower'
elif boatclass.lower() in ['dynamic']:
boatclass = 'dynamic'
elif boatclass.lower() in ['slides','slide','slider','sliders']:
boatclass = 'slides'
elif boatclass.lower() in ['c','c-boat']:
boatclass = 'c-boat'
elif boatclass.lower() in ['coastal','coast']:
boatclass = 'coastal'
elif boatclass.lower() in ['church','churchboat','finnish','finland']:
boatclass = 'churchboat'
except KeyError:
boatclass = 'water'
try:
boattype = row['BoatType']
except KeyError:
boattype = '1x'
try:
sex = row['Gender']
if sex.lower() in ['m','men','male','open']:
sex = 'male'
elif sex.lower() in ['mix','mixed']:
sex = 'mixed'
else:
sex = 'female'
except KeyError:
sex = 'female'
try:
weightclass = row['WeightClass']
if weightclass.lower() in ['hwt','h','o','heavy','open']:
weightclass = 'hwt'
elif weightclass.lower() in ['lwt','l','light','lights','lighties']:
weightclass = 'lwt'
except KeyError:
weightclass = 'hwt'
adaptiveclass = 'None'
try:
adaptiveclass = row['AdaptiveClass']
if adaptiveclass.lower() in ['o','open','none','no']:
adaptiveclass = 'None'
except KeyError:
adaptiveclass = 'None'
try:
skillclass = row['SkillClass']
except KeyError:
skillclass = 'Open'
# finding existing standard
existingstandards = CourseStandard.objects.filter(name=name,standardcollection=collection)
#print(existingstandards,collection)
if existingstandards:
existingstandards.update(
name=name,
coursedistance=coursedistance,
referencespeed=referencespeed,
coursetime=coursetime,
agemin=agemin,
agemax=agemax,
boatclass=boatclass,
boattype=boattype,
sex=sex,
weightclass=weightclass,
adaptiveclass=adaptiveclass,
skillclass=skillclass,
standardcollection = collection,
)
else:
#print('not')
standard = CourseStandard(
name=name,
coursedistance=coursedistance,
referencespeed=referencespeed,
coursetime=coursetime,
agemin=agemin,
agemax=agemax,
boatclass=boatclass,
boattype=boattype,
sex=sex,
weightclass=weightclass,
adaptiveclass=adaptiveclass,
skillclass=skillclass,
standardcollection = collection,
)
standard.save()
return collection.id
+19 -3
View File
@@ -353,6 +353,16 @@ def handle_check_race_course(self,
else:
splitsecond = 0
if 'referencespeed' in kwargs:
referencespeed = kwargs['referencespeed']
else:
referencespeed = 5.0
if 'coursedistance' in kwargs:
coursedistance = kwargs['coursedistance']
else:
coursedistance = 0
mode = 'race'
if 'mode' in kwargs:
mode = kwargs['mode']
@@ -479,22 +489,28 @@ def handle_check_race_course(self,
else:
coursecompleted = False
points = 0
if coursecompleted:
query = 'UPDATE rowers_virtualraceresult SET coursecompleted = 1, duration = "{duration}", distance = {distance}, workoutid = {workoutid}, startsecond = {startsecond}, endsecond = {endsecond} WHERE id={recordid}'.format(
if coursedistance == 0:
coursedistance = coursemeters
velo = coursedistance/coursetimeseconds
points = int(100*(2.-referencespeed/velo))
query = 'UPDATE rowers_virtualraceresult SET coursecompleted = 1, duration = "{duration}", distance = {distance}, workoutid = {workoutid}, startsecond = {startsecond}, endsecond = {endsecond}, points={points} WHERE id={recordid}'.format(
recordid=recordid,
duration=totaltime_sec_to_string(coursetimeseconds),
distance=int(coursemeters),
points=points,
workoutid=workoutid,
startsecond=startsecond,
endsecond=endsecond,
)
if mode == 'coursetest':
query = 'UPDATE rowers_coursetestresult SET coursecompleted = 1, duration = "{duration}", distance = {distance}, workoutid = {workoutid}, startsecond = {startsecond}, endsecond = {endsecond} WHERE id={recordid}'.format(
query = 'UPDATE rowers_coursetestresult SET coursecompleted = 1, duration = "{duration}", distance = {distance}, workoutid = {workoutid}, startsecond = {startsecond}, endsecond = {endsecond}, points={points} WHERE id={recordid}'.format(
recordid=recordid,
duration=totaltime_sec_to_string(coursetimeseconds),
distance=int(coursemeters),
points=points,
workoutid=workoutid,
startsecond=startsecond,
endsecond=endsecond,
+7
View File
@@ -160,6 +160,13 @@
};
});});
$('textarea').each(function( i ) {
$(this).change(function() {
data.set($(this).attr('name'),$(this).val());
console.log($(this).attr('name'),$(this).val());
});
});
$('select').each(function( i ) {
console.log($(this).attr('name'),$(this).val());
$(this).change(function() {
@@ -55,6 +55,15 @@
If you require a screenshot of the PM monitor, do mention this
in the comment.
</p>
<p>
Standard Times are a way to compare results in a race category with
a course record or golden standard for that event. A point score is calculated
which compares the participant's result with the standard. This offers an
engaging way to compete on points across different categories, boat types, and skill
levels.
If you select a Standard Times set from the drop-down list, race categories will
be limited to those in the selected set of Standard Times.
</p>
</ul>
</p>
</li>
+84
View File
@@ -0,0 +1,84 @@
{% extends "newbase.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}Rowsandall Course Standards List{% endblock %}
{% block scripts %}
{% endblock %}
{% block main %}
<style>
#mypointer {
cursor: pointer;
}
</style>
<h1>Standards Collections</h1>
<ul class="main-content">
<li class="grid_3">
{% if standards %}
<p>
<table width="100%" class="listtable shortpadded">
<thead>
<tr>
<th> Name</th>
<th> Maintainer</th>
</tr>
</thead>
<tbody>
{% for standard in standards %}
<tr>
<td>{{ standard.name }} </td>
<td>{{ standard.manager.first_name }} {{ standard.manager.last_name }}</td>
<td>
{% if standard.manager == user %}
<a href="/rowers/standards/{{ standard.id }}/">{{ standard.name }}</a>
{% else %}
<a href="/rowers/standards/{{ standard.id }}/">{{ standard.name }}</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</p>
{% else %}
<p> No standards found </p>
{% endif %}
</li>
<li>
<p>
<form id="searchform" action="/rowers/list-standards/"
method="get" accept-charset="utf-8">
{{ searchform }}
<input type="submit" value="GO"></input>
</form>
</p>
<p>
<a href="/rowers/standards/upload/">Add Standards</a>
</p>
</li>
<li class="grid_4">
<h2>How-to</h2>
<p>
A set of Course Standard Times allows you to calculate a score for
each participant on how well they have done against the course
standard time for their category. This allows for comparison
and competition between the different categories.
</p>
</li>
</ul>
{% endblock %}
{% block sidebar %}
{% include 'menu_racing.html' %}
{% endblock %}
+8 -1
View File
@@ -55,12 +55,14 @@
<i class="fas fa-file-upload fa-fw"></i>&nbsp;Upload your Challenge result
</a>
</li>
{% if race.sessiontype != 'race' %}
<li>
<a href="/rowers/workout/addmanual/">
<a href="/rowers/workout/addmanual/{{ race.id }}/">
<i class="fas fa-file-plus fa-fw"></i>&nbsp;Enter Result
</a>
</li>
{% endif %}
{% endif %}
{% if button == 'resubmitbutton' %}
<li>
<a href="/rowers/virtualevent/{{ race.id }}/submit/">Submit New Result</a>
@@ -131,6 +133,11 @@
</ul>
</li>
{% endif %}
<li id="standards">
<a href="/rowers/list-standards/">
<i class="fas fa-award fa-fw"></i>&nbsp;Course Time Standards
</a>
</li>
</ul> <!-- cd-accordion-menu -->
{% include 'menuscript.html' %}
+276
View File
@@ -0,0 +1,276 @@
{% extends "newbase.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}File loading{% 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 main %}
<h1>Upload Course Standard Times File</h1>
<ul class="main-content">
<li class="grid_4">
<div id="id_dropregion watermark invisible">
<p>Drag and drop files here.
</p>
<p>
If you're updating an existing set of standard times,
existing groups with challenge entries will be updated. New
groups will be created. All groups with challenge entries will
remain, even if you delete them from the CSV file. So, it
is not possible to remove groups if there have been starts in this group.</p>
<p>
If you want to still remove those groups, it is better to set the
existing set of standard times to inactive and upload a new one.
</p>
</div>
<div id="id_drop-files" class="grid_12 alpha drop-files">
<form id="file_form" enctype="multipart/form-data" 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 %}
<p>
<input type="submit" value="Submit">
</p>
</form>
</div>
</li>
</ul>
{% endblock %}
{% block sidebar %}
{% include 'menu_racing.html' %}
{% endblock %}
{% block scripts %}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script>
var td = new FormData();
var formdatasetok = false;
try {
td.set('aap','noot');
formdatasetok = true;
console.log('FormData.set OK');
}
catch(err) {
console.log('FormData.set not OK');
formdatasetok = false;
}
if (!formdatasetok) {
$("#id_dropregion").remove();
}
if (formdatasetok) {
$(document).ready(function() {
var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
console.log("CSRF token",csrftoken);
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
console.log("Loading dropper");
jQuery.event.props.push('dataTransfer');
$(window).on('dragenter', function() {
$("#id_drop-files").css("background-color","#E9E9E4");
$("#id_dropregion").addClass("watermark").removeClass("invisible");})
$(window).on('dragleave', function() {
$("#id_drop-files").css("background-color","#FFFFFF");
$("#id_dropregion").removeClass("watermark").addClass("invisible");})
var frm = $("#file_form");
if( window.FormData === undefined ) {
console.log('no formdata');
alert("No FormData");
} else {
console.log('we have formdata');
}
var data = new FormData(frm[0]);
$('#id_file').on('change', function(evt) {
var f = this.files[0];
console.log(f);
var istcx = false;
var isgzip = false;
var size1 = 10485760;
var size2 = 1048576;
if ((/\.(tcx|TCX)/i).test(f.name)) {
istcx = true;
console.log('tcx');
if ((/\.(gz|GZ)/i).test(f.name)) {
isgzip = true;
console.log('gzip');
size1 /= 5;
size2 /= 5;
}
}
console.log(size1)
console.log(size2)
if (f.size > size1) {
alert("File Size must be smaller than 10 MB");
this.value = null;
} else {
if (f.size > size2) {
$('#id_offline').val('True');
$('#id_offline').prop('checked','True');
data.set($('#id_offline').attr('name'),$('#id_offline').prop('checked'));
console.log("Set offline to True");
}
}
});
$('input').each(function( i ) {
$(this).change(function() {
if ($(this).attr('type') == 'checkbox') {
data.set($(this).attr('name'),$(this).prop('checked'));
console.log($(this).attr('id'),$(this).attr('name'),$(this).attr('notes'),$(this).prop('checked'));
} else {
data.set($(this).attr('name'),$(this).val());
if ($(this).attr('id') == 'id_file') {
data.set("file",this.files[0]);
}
console.log($(this).attr('name'),$(this).val());
};
});});
$('textarea').each(function( i ) {
$(this).change(function() {
data.set($(this).attr('name'),$(this).val());
console.log($(this).attr('name'),$(this).val());
});
});
$('select').each(function( i ) {
console.log($(this).attr('name'),$(this).val());
$(this).change(function() {
data.set($(this).attr('name'),$(this).val());
console.log($(this).attr('id'),$(this).attr('name'),$(this).val());
});
});
frm.submit(function() {
console.log("Form submission");
$(data.values()).each(function(value) {
console.log(value);
});
$("#id_drop-files").replaceWith(
'<div id="id_waiting"><img src="/static/img/rowingtimer.gif" width="60" height="50" style="width:60px">'
);
$.ajax({
data: data,
type: $(this).attr('method'),
url: window.location.pathname,
contentType: false,
processData: false,
error: function(result) {
$("#id_waiting").replaceWith(
'<div id="id_failed" class="grid_12 alpha message">Your upload failed</div>'
);
setTimeout(function() {
location.reload();
},1000);
},
success: function(result) {
console.log('got something back');
console.log(result);
if (result.result == 1) {
window.location.href = result.url;
} else {
console.log(result," reloading");
location.reload();
};
}
});
return false;
});
$('#id_drop-files').bind({
drop: function(e) {
e.preventDefault();
console.log("you dropped something");
var files = e.dataTransfer.files;
console.log(files[0]);
var f = files[0];
var istcx = false;
var isgzip = false;
var size1 = 10485760;
var size2 = 1048576;
if ((/\.(tcx|TCX)/i).test(f.name)) {
istcx = true;
console.log('tcx');
if ((/\.(gz|GZ)/i).test(f.name)) {
isgzip = true;
console.log('gzip');
size1 /= 5;
size2 /= 5;
}
}
console.log(f);
console.log(size1)
console.log(size2)
if (f.size > size1) {
alert("File Size must be smaller than 10 MB");
$("#id_file").value = 0;
return false;
}
data.set("file",f);
// data.append("file",f);
$("#id_file").replaceWith('<div id="id_file">'+files[0].name+'&nbsp; <a class="remove" href="javascript:void(0);"><b><font color="red">X</font></b></a></div>');
},
mouseenter:function(){$("#id_drop-files").css("background-color","#E9E9E4");},
mouseleave:function(){$("#id_drop-files").css("background-color","#FFFFFF");},
dragover:function(e){
e.preventDefault();
$("#id_drop-files").css("background-color","#E9E9E4");},
dragleave:function(e){ e.preventDefault();},
});
$(document).on("click", "a.remove", function() {
$(this).parent().replaceWith('<td><input id="id_file" name="file" type="file" /></td>');
});
});
};
</script>
{% endblock %}
+79
View File
@@ -0,0 +1,79 @@
{% extends "newbase.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% load leaflet_tags %}
{% block meta %}
{% leaflet_js %}
{% leaflet_css %}
{% endblock %}
{% block scripts %}
{% include "monitorjobs.html" %}
{% endblock %}
{% block title %}{{ standard.name }} {% endblock %}
{% block og_title %}{{ standard.name }} {% endblock %}
{% block main %}
<h1>{{ standard.name }}</h1>
<ul class="main-content">
<li class="grid_2">
<table class="listtable shortpadded" width="100%">
<tr>
<th>Name</th><td>{{ collection.name }}</td>
</tr>
<tr>
<th>Manager</th><td>{{ collection.manager.first_name }} {{ collection.manager.last_name }}</td>
</tr>
<tr>
<th>Notes</th><td>{{ collection.notes|linebreaks }}</td>
</tr>
</table>
</li>
<li>
{% if request.user == collection.manager %}
<p><a href="/rowers/standards/upload/{{ collection.id }}/">Update these Standard Times</a></p>
<p><a href="/rowers/standards/{{ collection.id }}/deactivate/">Deactivate this standard</a></p>
{% endif %}
<p><a href="/rowers/standards/{{ collection.id }}/download/">Download as CSV file</a></p>
</li>
<li class="grid_4">
<h2>Standard Times</h2>
<table class="listtable shortpadded" width="100%"?
<tr>
<th>Name<a href="?order_by=name">&#9650;</a><a href="?order_by=-name">&#9660;</a></th>
<th>Distance<a href="?order_by=coursedistance">&#9650;</a><a href="?order_by=-coursedistance">&#9660;</a></th>
<th>Standard Time<a href="?order_by=coursetime">&#9650;</a><a href="?order_by=-coursetime">&#9660;</a></th>
<th>Boat Class<a href="?order_by=boatclass">&#9650;</a><a href="?order_by=-boatclass">&#9660;</a></th>
<th>Boat Type<a href="?order_by=boattype">&#9650;</a><a href="?order_by=-boattype">&#9660;</a></th>
<th>Gender<a href="?order_by=sex">&#9650;</a><a href="?order_by=-sex">&#9660;</a></th>
<th>Weight Class<a href="?order_by=weightclass">&#9650;</a><a href="?order_by=-weightclass">&#9660;</a></th>
<th>Adaptive Class<a href="?order_by=adaptiveclass">&#9650;</a><a href="?order_by=-adaptiveclass">&#9660;</a></th>
<th>Skill Class<a href="?order_by=skillclass">&#9650;</a><a href="?order_by=-skillclass">&#9660;</a></th>
<th>Minimum<a href="?order_by=agemin">&#9650;</a><a href="?order_by=-agemin">&#9660;</a>/Maximum Age<a href="?order_by=agemax">&#9650;</a><a href="?order_by=-agemax">&#9660;</a></th>
</tr>
{% for standard in standards %}
<tr>
<td>{{ standard.name }}</td>
<td>{{ standard.coursedistance }}</td>
<td>{{ standard.coursetime }}</td>
<td>{{ standard.boatclass|boatclass }}</td>
<td>{{ standard.boattype }}</td>
<td>{{ standard.sex|sex }}</td>
<td>{{ standard.weightclass|weight }}</td>
<td>{{ standard.adaptiveclass|adaptive }}</td>
<td>{{ standard.skillclass }}</td>
<td>{{ standard.agemin }}/{{ standard.agemax }}</td>
</tr>
{% endfor %}
</table>
</li>
</ul>
{% endblock %}
{% block sidebar %}
{% include 'menu_racing.html' %}
{% endblock %}
+48 -9
View File
@@ -104,6 +104,11 @@
<th>Challenge Time Zone</th><td>{{ race.timezone }}</td>
</tr>
{% endif %}
{% if race.coursestandards %}
<tr>
<th>Standard Times</th><td><a href="/rowers/standards/{{ race.coursestandards.id }}/">{{ race.coursestandards }}</a></td>
</tr>
{% endif %}
<tr>
<th>
{{ race.sessionmode }} challenge
@@ -263,7 +268,9 @@
<th>&nbsp;</th>
<th>Name</th>
<th>Team Name</th>
<th>&nbsp;</th>
{% if race.coursestandards %}
<th>Group</th>
{% else %}
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>&nbsp;</th>
@@ -271,8 +278,12 @@
{% if race.sessiontype == 'race' %}
<th>Boat</th>
{% endif %}
<th>Time</th>
<th>Distance</th>
{% endif %}
<th>Time<a href="?order_by=duration">&#9660;</th>
<th>Distance<a href="?order_by=-distance">&#9660;</th>
{% if race.coursestandards %}
<th>Points<a href="?order_by=-points">&#9660;</a></th>
{% endif %}
<th>Details</th>
<th>&nbsp;</th>
</tr>
@@ -285,6 +296,9 @@
<a href="/rowers/workout/{{ result.workoutid|encode }}/view/entry/{{ result.id }}/">
{{ result.username }}</a></td>
<td>{{ result.teamname }}</td>
{% if race.coursestandards %}
<td>{{ result.entrycategory }}</td>
{% else %}
<td>{{ result.age }}</td>
<td>{{ result.sex }}</td>
<td>{{ result.weightcategory }}</td>
@@ -299,11 +313,17 @@
{% if race.sessiontype == 'race' %}
<td>{{ result.boattype }}</td>
{% endif %}
{% endif %}
<td>{{ result.duration |durationprint:"%H:%M:%S.%f" }}</td>
<td>{{ result.distance }} m</td>
{% if race.coursestandards %}
<td>{{ result.points }}</td>
{% endif %}
<td>
<a href="/rowers/workout/{{ result.workoutid|encode }}/view/entry/{{ result.id }}/">
Details</a></td>
Details</a>
</td>
<td>
{% if race.manager == request.user and not race|is_final %}
<a href="/rowers/virtualevent/{{ race.id }}/disqualify/{{ result.id }}/">
@@ -389,22 +409,29 @@
<tr>
<th>Name</th>
<th>Team Name</th>
{% if race.sessiontype == 'race' %}
<th>Class</th>
<th>Boat</th>
{% if race.coursestandards %}
<th>Group</th>
<th>Age</th>
{% else %}
<th>Class</th>
{% if race.sessiontype == 'race' %}
<th>Boat</th>
{% endif %}
<th>Class</th>
<th>Age</th>
<th>Gender</th>
<th>Weight Category</th>
<th>Adaptive</th>
{% endif %}
</tr>
<tbody>
{% for record in records %}
<tr>
<td>{{ record.username }}
<td>{{ record.teamname }}</td>
{% if race.coursestandards %}
<td>{{ record.entrycategory }}</td>
<td>{{ record.age }}</td>
{% else %}
<td>{{ record.boatclass }}</td>
{% if race.sessiontype == 'race' %}
<td>{{ record.boattype }}</td>
@@ -419,6 +446,7 @@
{{ record.adaptiveclass }}
{% endif %}
</td>
{% endif %}
{% if record.userid == rower.id and 'withdrawbutton' in buttons %}
<td>
<a href="/rowers/virtualevent/{{ race.id }}/withdraw/{{ record.id }}" >Withdraw</a>
@@ -431,7 +459,7 @@
{% endif %}
</div>
{% for record in records %}
{% if record.userid == request.user.rower.id %}
{% if record.userid == request.user.rower.id and forloop.counter == 1 %}
{% if race.sessiontype == 'race' %}
{% if record.emailnotifications %}
<a href="/rowers/raceregistration/togglenotification/{{ race.id }}">
@@ -541,6 +569,17 @@
review and reject entries. If you are disqualified in this
way, you will receive an email with the reason.
</p>
{% if race.coursestandards %}
<p>
Standard Times are a way to compare results in a race category with
a course record or golden standard for that event. A point score is calculated
which compares the participant's result with the standard. This offers an
engaging way to compete on points across different categories, boat types, and skill
levels.
If you select a Standard Times set from the drop-down list, race categories will
be limited to those in the selected set of Standard Times.
</p>
{% endif %}
</div>
</li>
</ul>
+9
View File
@@ -49,6 +49,15 @@
is strongly recommended that you fill out a contact email or phone
number.
</p>
<p>
Standard Times are a way to compare results in a race category with
a course record or golden standard for that event. A point score is calculated
which compares the participant's result with the standard. This offers an
engaging way to compete on points across different categories, boat types, and skill
levels.
If you select a Standard Times set from the drop-down list, race categories will
be limited to those in the selected set of Standard Times.
</p>
</li>
</ul>
@@ -35,6 +35,16 @@
as a Male crew. Check the "Mixed gender" check box to register as a
mixed gender crew (except for 1x where this check box does nothing).
</p>
{% if race.coursestandards %}
<p>This race uses standard times and limits the race groups to those where
standard times exist. The "Group" form choice will overrule other selections you
make in the form (boat type, weight, etc) and your entry will be rejected
if the age and gender doesn't match.
</p>
<p>
You can check the valid race groups and standard times <a target="_" href="/rowers/standards/{{ race.coursestandards.id }}/">here</a>.
</p>
{% endif %}
<div class="grid_6 alpha">
<table width="100%">
{{ form.as_table }}
+47 -1
View File
@@ -22,7 +22,9 @@ from rowers import c2stuff, runkeeperstuff
from rowers.c2stuff import c2_open
from rowers.runkeeperstuff import runkeeper_open
from rowers.rower_rules import is_coach_user, is_workout_user, isplanmember,ispromember
from rowers.mytypes import otwtypes
from rowers.mytypes import (
otwtypes,adaptivetypes,sexcategories,weightcategories,workouttypes,
)
from rowers.utils import NoTokenError
import rowers.payments as payments
@@ -38,6 +40,50 @@ from django.template.defaultfilters import stringfilter
from six import string_types
@register.filter
def adaptive(s):
u = s
for e,v in adaptivetypes:
if e.lower() == u.lower():
u = v
continue
return u
@register.filter
def boatclass(s):
u = s
for e,v in workouttypes:
if e.lower() == u.lower():
u = v
continue
return u
@register.filter
def sex(s):
u = s
for e,v in sexcategories:
if e.lower() == u.lower():
u = v
continue
return u
@register.filter
def weight(s):
u = s
for e,v in weightcategories:
if e.lower() == u.lower():
u = v
continue
return u
@register.filter
def sigdig(value, digits = 3):
try:
+9 -1
View File
@@ -201,8 +201,11 @@ urlpatterns = [
views.virtualevent_results_download_view,name='virtualevent_results_download_view'),
re_path(r'^list-workouts/$',views.workouts_view,name='workouts_view'),
re_path(r'^list-courses/$',views.courses_view,name='courses_view'),
re_path(r'^list-standards/$',views.standards_view,name='standards_view'),
re_path(r'^courses/upload/$',views.course_upload_view,name='course_upload_view'),
re_path(r'^workout/addmanual/(?P<raceid>\d+)$',views.addmanual_view,name='addmanual_view'),
re_path(r'^standards/upload/$',views.standards_upload_view,name='standards_upload_view'),
re_path(r'^standards/upload/(?P<id>\d+)/$',views.standards_upload_view,name='standards_upload_view'),
re_path(r'^workout/addmanual/(?P<raceid>\d+)/$',views.addmanual_view,name='addmanual_view'),
re_path(r'^workout/addmanual/$',views.addmanual_view,name='addmanual_view'),
re_path(r'^team-compare-select/workout/(?P<id>\d+)/team/(?P<teamid>\d+)/user/(?P<userid>\d+)/$',views.team_comparison_select,name='team_comparison_select'),
# re_path(r'^team-compare-select/team/(?P<teamid>\d+)/(?P<startdatestring>\d+-\d+-\d+)/(?P<enddatestring>\d+-\d+-\d+)/user/(?P<userid>\d+)/$',views.team_comparison_select,name='team_comparison_select'),
@@ -752,6 +755,11 @@ urlpatterns = [
re_path(r'^courses/(?P<id>\d+)/replace/$',views.course_replace_view,
name='course_replace_view'),
re_path(r'^courses/(?P<id>\d+)/$',views.course_view,name='course_view'),
re_path(r'^standards/(?P<id>\d+)/$',views.standard_view,name='standard_view'),
re_path(r'^standards/(?P<id>\d+)/download/$',views.standards_download_view,
name='standards_download_view'),
re_path(r'^standards/(?P<id>\d+)/deactivate/$',views.standard_deactivate_view,
name='standard_decativate_view'),
re_path(r'^courses/(?P<id>\d+)/map/$',views.course_map_view,name='course_map_view'),
# URLS to be created
re_path(r'^help/$',TemplateView.as_view(template_name='help.html'), name='help'),
+368 -16
View File
@@ -5,6 +5,7 @@ from __future__ import unicode_literals
from rowers.views.statements import *
from rowsandall_app.settings import SITE_URL
from rowers.scoring import *
# List Courses
def courses_view(request):
@@ -35,6 +36,35 @@ def courses_view(request):
'rower':r,
})
# List Courses
def standards_view(request):
r = getrower(request.user)
standards = StandardCollection.objects.filter(active=True).order_by("name")
# add search processing
query = request.GET.get('q')
if query:
query_list = query.split()
standards = StandardCollection.objects.filter(
reduce(operator.and_,
(Q(name__icontains=q) for q in query_list)) |
reduce(operator.and_,
(Q(notes__icontains=q) for q in query_list))
)
searchform = SearchForm(initial={'q':query})
else:
searchform = SearchForm()
return render(request,'list_standards.html',
{'standards':standards,
'active':'nav-racing',
'searchform':searchform,
'rower':r,
})
# for ajax calls
def course_map_view(request,id=0):
@@ -249,6 +279,50 @@ def course_view(request,id=0):
}
)
def standard_view(request,id=0):
try:
collection = StandardCollection.objects.get(id=id)
except StandardCollection.DoesNotExist:
return Http404("Standard Collection does not exist")
r = getrower(request.user)
orderby = request.GET.get('order_by')
if orderby is not None:
standards = CourseStandard.objects.filter(
standardcollection=collection
).order_by(orderby,"-referencespeed","agemax","agemin","sex","name")
else:
standards = CourseStandard.objects.filter(
standardcollection=collection
).order_by("-referencespeed","agemax","agemin","sex","name")
breadcrumbs = [
{
'url': reverse('virtualevents_view'),
'name': 'Challenges'
},
{
'url': reverse(standards_view),
'name': 'Standards'
},
{
'url': reverse(standard_view,kwargs={'id':collection.id}),
'name': collection.name
},
]
return render(request, 'standard_view.html',
{
'active':'nav-racing',
'breadcrumbs':breadcrumbs,
'collection':collection,
'standards':standards,
'rower':r,
}
)
@login_required()
@permission_required('racelogo.delete_logo',fn=get_logo_by_pk,raise_exception=True)
def logo_delete_view(request,id=0):
@@ -387,7 +461,7 @@ def virtualevent_uploadimage_view(request,id=0):
})
# Image upload
# Course upload
@login_required()
def course_upload_view(request):
is_ajax = False
@@ -447,6 +521,125 @@ def course_upload_view(request):
else:
return {'result':0}
# Standards deactivate
@login_required()
def standard_deactivate_view(request,id=0):
is_ajax = False
if request.is_ajax():
is_ajax = True
r = getrower(request.user)
try:
collection = StandardCollection.objects.get(id=id)
except StandardCollection.DoesNotExist:
raise Http404("Does not exist")
if request.user != collection.manager:
raise PermissionDenied("You cannot change this set of time standards")
collection.active = False
collection.save()
url = reverse(standards_view)
return HttpResponseRedirect(url)
def standards_download_view(request,id=0):
try:
collection = StandardCollection.objects.get(id=id)
except StandardCollection.DoesNotExist:
raise Http404("Does not exist")
filename = 'Standard Times {name} {id} {date}.csv'.format(
id=id,
name=collection.name,
date=timezone.now().strftime("%Y-%m-%d %H:%M:%S %Z")
)
standards = CourseStandard.objects.filter(standardcollection=collection)
df = pd.DataFrame.from_records(standards.values())
response = HttpResponse(df.to_csv())
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
response['Content-Type'] = 'application/octet-stream'
return response
# Standards upload
@login_required()
def standards_upload_view(request,id=0):
is_ajax = False
if request.is_ajax():
is_ajax = True
r = getrower(request.user)
if id != 0:
collection = StandardCollection.objects.get(id=id)
if request.user != collection.manager:
raise PermissionDenied("You cannot change this set of time standards")
if request.method == 'POST':
form = StandardsForm(request.POST,request.FILES)
if form.is_valid():
f = form.cleaned_data['file']
name = form.cleaned_data['name']
notes = form.cleaned_data['notes']
if f is not None:
filename,path_and_filename = handle_uploaded_file(f)
id = save_scoring(name,request.user,path_and_filename,notes=notes,id=id)
os.remove(path_and_filename)
if id==0:
url = reverse(standards_view)
else:
url = reverse(standard_view,kwargs={'id':id})
if is_ajax:
return JSONResponse({'result':1,'url':url})
return HttpResponseRedirect(url)
else:
messages.error(request,'Something went wrong - no file attached')
url = reverse(standards_upload_view)
if is_ajax:
return JSONResponse({'result':0,'url':0})
return HttpResponseRedirect(url)
else:
messages.error(request,'Form is not valid')
return render(request,'standard_form.html',
{'form':form,
'active':'nav-racing',
'id':id,
})
else:
if not is_ajax:
form = StandardsForm()
if id != 0:
collection = StandardCollection.objects.get(id=id)
form = StandardsForm(initial={
'name':collection.name,
'notes': collection.notes,
})
return render(request,'standard_form.html',
{'form':form,
'active':'nav-racing',
'id':id,
})
return {'result':0}
def virtualevents_view(request):
is_ajax = False
@@ -523,7 +716,6 @@ def virtualevents_view(request):
country__in=countries
).order_by("startdate","start_time")
else:
form = VirtualRaceSelectForm()
if is_ajax:
@@ -935,6 +1127,11 @@ def virtualevent_view(request,id=0):
except KeyError:
adaptiveclass = ['None','PR1','PR2','PR3','FES']
try:
entrycategory = cd['entrycategory']
except KeyError:
entrycategory = None
if race.sessiontype == 'race':
results = resultobj.objects.filter(
race=race,
@@ -945,7 +1142,7 @@ def virtualevent_view(request,id=0):
weightcategory__in=weightcategory,
adaptiveclass__in=adaptiveclass,
age__gte=age_min,
age__lte=age_max
age__lte=age_max,
).order_by("duration")
else:
results = resultobj.objects.filter(
@@ -956,9 +1153,11 @@ def virtualevent_view(request,id=0):
weightcategory__in=weightcategory,
adaptiveclass__in=adaptiveclass,
age__gte=age_min,
age__lte=age_max
age__lte=age_max,
).order_by("duration","-distance")
if entrycategory is not None:
results = results.filter(entrycategory__in=entrycategory)
# to-do - add DNS
dns = []
@@ -985,8 +1184,6 @@ def virtualevent_view(request,id=0):
else:
form = None
breadcrumbs = [
{
'url':reverse('virtualevents_view'),
@@ -1000,6 +1197,10 @@ def virtualevent_view(request,id=0):
}
]
orderby = request.GET.get('order_by')
if orderby is not None:
results = results.order_by(orderby)
racelogos = race.logos.all()
if racelogos:
@@ -1278,6 +1479,12 @@ def virtualevent_addboat_view(request,id=0):
except VirtualRace.DoesNotExist:
raise Http404("Virtual Challenge does not exist")
categories = None
if race.coursestandards is not None:
categories = CourseStandard.objects.filter(
standardcollection=race.coursestandards).order_by("name")
if not race_can_adddiscipline(r,race):
messages.error(request,"You cannot register for this race")
@@ -1301,7 +1508,7 @@ def virtualevent_addboat_view(request,id=0):
# we're still here
if request.method == 'POST':
# process form
form = VirtualRaceResultForm(request.POST)
form = VirtualRaceResultForm(request.POST,categories=categories)
if form.is_valid():
cd = form.cleaned_data
teamname = cd['teamname']
@@ -1323,7 +1530,7 @@ def virtualevent_addboat_view(request,id=0):
if sex == 'not specified':
sex = 'male'
if boattype in boattypes and boatclass in boatclasses:
if boattype in boattypes and boatclass in boatclasses and race.coursestandards is None:
# check if different sexes
therecords = records.filter(
boattype=boattype,
@@ -1344,6 +1551,49 @@ def virtualevent_addboat_view(request,id=0):
return HttpResponseRedirect(url)
coursestandard = None
referencespeed = 5.0
if race.coursestandards is not None:
coursestandard = cd['entrycategory']
thegroups = [record.entrycategory for record in records]
if coursestandard in thegroups:
messages.error(request,"You have already registered in that group")
url = reverse('virtualevent_view',
kwargs = {
'id': race.id
}
)
return HttpResponseRedirect(url)
referencespeed = coursestandard.referencespeed
boattype = coursestandard.boattype
boatclass = coursestandard.boatclass
weightcategory = coursestandard.weightclass
adaptiveclass = coursestandard.adaptiveclass
skillclass = coursestandard.skillclass
returnurl = reverse(virtualevent_register_view,
kwargs={'id':race.id})
if age < coursestandard.agemin:
messages.error(request,'You are younger than the minimum age for this group')
return HttpResponseRedirect(returnurl)
if age > coursestandard.agemax:
messages.error(request,'You are older than the maximum age for this group')
return HttpResponseRedirect(returnurl)
if sex == 'male' and coursestandard.sex != 'male':
messages.error(request,'Men are not allowed to enter this category')
return HttpResponseRedirect(returnurl)
if sex == 'mixed' and coursestandard.sex not in ['mixed','male']:
messages.error(request,'Mixed crews are not allowed to enter this category')
return HttpResponseRedirect(returnurl)
record = VirtualRaceResult(
userid=r.id,
teamname=teamname,
@@ -1358,8 +1608,10 @@ def virtualevent_addboat_view(request,id=0):
boattype=boattype,
boatclass=boatclass,
coursecompleted=False,
referencespeed=referencespeed,
entrycategory=coursestandard,
sex=sex,
age=age
age=age,
)
record.save()
@@ -1387,7 +1639,13 @@ def virtualevent_addboat_view(request,id=0):
'adaptiveclass': r.adaptiveclass,
}
form = VirtualRaceResultForm(initial=initial)
categories = None
if race.coursestandards is not None:
categories = CourseStandard.objects.filter(
standardcollection=race.coursestandards).order_by("name")
form = VirtualRaceResultForm(initial=initial,categories=categories)
breadcrumbs = [
{
@@ -1448,6 +1706,11 @@ def virtualevent_register_view(request,id=0):
except VirtualRace.DoesNotExist:
raise Http404("Virtual Challenge does not exist")
categories = None
if race.coursestandards is not None:
categories = CourseStandard.objects.filter(
standardcollection=race.coursestandards).order_by("name")
if not race_can_register(r,race):
messages.error(request,"You cannot register for this race")
@@ -1461,7 +1724,7 @@ def virtualevent_register_view(request,id=0):
# we're still here
if request.method == 'POST':
# process form
form = VirtualRaceResultForm(request.POST)
form = VirtualRaceResultForm(request.POST,categories=categories)
if form.is_valid():
cd = form.cleaned_data
teamname = cd['teamname']
@@ -1483,6 +1746,40 @@ def virtualevent_register_view(request,id=0):
if sex == 'not specified':
sex = 'male'
coursestandard = None
referencespeed = 5.0
if race.coursestandards is not None:
coursestandard = cd['entrycategory']
referencespeed = coursestandard.referencespeed
boattype = coursestandard.boattype
boatclass = coursestandard.boatclass
weightcategory = coursestandard.weightclass
adaptiveclass = coursestandard.adaptiveclass
skillclass = coursestandard.skillclass
returnurl = reverse(virtualevent_register_view,
kwargs={'id':race.id})
if age < coursestandard.agemin:
messages.error(request,'You are younger than the minimum age for this group')
return HttpResponseRedirect(returnurl)
if age > coursestandard.agemax:
messages.error(request,'You are older than the maximum age for this group')
return HttpResponseRedirect(returnurl)
if sex == 'male' and coursestandard.sex != 'male':
messages.error(request,'Men are not allowed to enter this category')
return HttpResponseRedirect(returnurl)
if sex == 'mixed' and coursestandard.sex not in ['mixed','male']:
messages.error(request,'Mixed crews are not allowed to enter this category')
return HttpResponseRedirect(returnurl)
record = VirtualRaceResult(
userid=r.id,
teamname=teamname,
@@ -1498,7 +1795,9 @@ def virtualevent_register_view(request,id=0):
boattype=boattype,
coursecompleted=False,
sex=sex,
age=age
age=age,
entrycategory=coursestandard,
referencespeed=referencespeed,
)
record.save()
@@ -1542,7 +1841,12 @@ def virtualevent_register_view(request,id=0):
'adaptiveclass': r.adaptiveclass,
}
form = VirtualRaceResultForm(initial=initial)
categories = None
if race.coursestandards is not None:
categories = CourseStandard.objects.filter(
standardcollection=race.coursestandards).order_by("name")
form = VirtualRaceResultForm(initial=initial,categories=categories)
breadcrumbs = [
{
@@ -1644,6 +1948,11 @@ def indoorvirtualevent_register_view(request,id=0):
except VirtualRace.DoesNotExist:
raise Http404("Virtual Challenge does not exist")
categories = None
if race.coursestandards is not None:
categories = CourseStandard.objects.filter(
standardcollection=race.coursestandards).order_by("name")
if not race_can_register(r,race):
messages.error(request,"You cannot register for this race")
@@ -1657,7 +1966,7 @@ def indoorvirtualevent_register_view(request,id=0):
# we're still here
if request.method == 'POST':
# process form
form = IndoorVirtualRaceResultForm(request.POST)
form = IndoorVirtualRaceResultForm(request.POST,categories=categories)
if form.is_valid():
cd = form.cleaned_data
teamname = cd['teamname']
@@ -1675,6 +1984,38 @@ def indoorvirtualevent_register_view(request,id=0):
if sex == 'not specified':
sex = 'male'
coursestandard = None
referencespeed = 5.0
if race.coursestandards is not None:
coursestandard = cd['entrycategory']
referencespeed = coursestandard.referencespeed
boatclass = coursestandard.boatclass
weightcategory = coursestandard.weightclass
adaptiveclass = coursestandard.adaptiveclass
skillclass = coursestandard.skillclass
returnurl = reverse(virtualevent_register_view,
kwargs={'id':race.id})
if age < coursestandard.agemin:
messages.error(request,'You are younger than the minimum age for this group')
return HttpResponseRedirect(returnurl)
if age > coursestandard.agemax:
messages.error(request,'You are older than the maximum age for this group')
return HttpResponseRedirect(returnurl)
if sex == 'male' and coursestandard.sex != 'male':
messages.error(request,'Men are not allowed to enter this category')
return HttpResponseRedirect(returnurl)
if sex == 'mixed' and coursestandard.sex not in ['mixed','male']:
messages.error(request,'Mixed crews are not allowed to enter this category')
return HttpResponseRedirect(returnurl)
record = IndoorVirtualRaceResult(
userid=r.id,
teamname=teamname,
@@ -1689,7 +2030,9 @@ def indoorvirtualevent_register_view(request,id=0):
boatclass=boatclass,
coursecompleted=False,
sex=sex,
age=age
age=age,
entrycategory=coursestandard,
referencespeed=referencespeed
)
record.save()
@@ -1733,7 +2076,12 @@ def indoorvirtualevent_register_view(request,id=0):
'adaptiveclass': r.adaptiveclass,
}
form = IndoorVirtualRaceResultForm(initial=initial)
categories = None
if race.coursestandards is not None:
categories = CourseStandard.objects.filter(
standardcollection=race.coursestandards).order_by("name")
form = IndoorVirtualRaceResultForm(initial=initial,categories=categories)
breadcrumbs = [
{
@@ -1806,6 +2154,7 @@ def indoorvirtualevent_create_view(request):
evaluation_closure = cd['evaluation_closure']
contact_phone = cd['contact_phone']
contact_email = cd['contact_email']
coursestandards = cd['coursestandards']
# correct times
@@ -1860,6 +2209,7 @@ def indoorvirtualevent_create_view(request):
sessionvalue = sessionvalue,
course=None,
timezone=timezone_str,
coursestandards=coursestandards,
evaluation_closure=evaluation_closure,
registration_closure=registration_closure,
contact_phone=contact_phone,
@@ -1946,6 +2296,7 @@ def virtualevent_create_view(request):
evaluation_closure = cd['evaluation_closure']
contact_phone = cd['contact_phone']
contact_email = cd['contact_email']
coursestandards = cd['coursestandards']
# correct times
@@ -1997,6 +2348,7 @@ def virtualevent_create_view(request):
evaluation_closure=evaluation_closure,
registration_closure=registration_closure,
contact_phone=contact_phone,
coursestandards=coursestandards,
contact_email=contact_email,
country = course.country,
manager=request.user,
+2 -2
View File
@@ -93,7 +93,7 @@ from rowers.forms import (
FitnessMetricForm,PredictedPieceFormNoDistance,
EmailForm, RegistrationForm, RegistrationFormTermsOfService,
RegistrationFormUniqueEmail,RegistrationFormSex,
CNsummaryForm,UpdateWindForm,
CNsummaryForm,UpdateWindForm,StandardsForm,
UpdateStreamForm,WorkoutMultipleCompareForm,ChartParamChoiceForm,
FusionMetricChoiceForm,BoxPlotChoiceForm,MultiFlexChoiceForm,
TrendFlexModalForm,WorkoutSplitForm,WorkoutJoinParamForm,
@@ -113,7 +113,7 @@ from rowers.models import (
RaceLogo,RowerBillingAddressForm,PaidPlan,
AlertEditForm, ConditionEditForm,
PlannedSessionComment,CoachRequest,CoachOffer,
VideoAnalysis,ShareKey,
VideoAnalysis,ShareKey,StandardCollection,CourseStandard,
)
from rowers.models import (
RowerPowerForm,RowerForm,GraphImage,AdvancedWorkoutForm,
+4
View File
@@ -42,6 +42,10 @@ TESTING = False
ALLOWED_HOSTS = CFG['allowed_hosts']
OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL = 'oauth2_provider.AccessToken'
OAUTH2_PROVIDER_APPLICATION_MODEL = 'oauth2_provider.Application'
OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL = 'oauth2_provider.RefreshToken'
# Application definition
+4
View File
@@ -112,6 +112,10 @@ th {
.paddedtable td { padding: 1px 20px }
.shortpadded th { padding: 3px 3px }
.paddedtable th { padding: 1px 20px }
.cortable {
border-collapse: collapse;
}
+1 -1
View File
File diff suppressed because one or more lines are too long