Private
Public Access
1
0

Merge branch 'feature/fastestchallenges' into develop

This commit is contained in:
Sander Roosendaal
2020-11-14 12:19:03 +01:00
17 changed files with 485 additions and 25 deletions

View File

@@ -452,7 +452,7 @@ def getfastest(df,thevalue,mode='distance'):
starttime = griddata(restime,starttimes,[thevalue*60*1000],method='linear',rescale=True)
duration = griddata(restime,restime,[thevalue*60*1000],method='linear',rescale=True)
endtime = starttime+duration
#print(distance,starttime,endtime )
print(distance,starttime,endtime )
return distance[0],starttime[0]/1000.,endtime[0]/1000.
return 0

View File

@@ -15,7 +15,7 @@ from rowers.rows import validate_file_extension,must_be_csv,validate_image_exten
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django.contrib.admin.widgets import AdminDateWidget
from django.forms.widgets import SelectDateWidget
from django.forms.widgets import SelectDateWidget,HiddenInput
#from django.forms.extras.widgets import SelectDateWidget
from django.utils import timezone,translation
from django.forms import ModelForm, Select
@@ -410,6 +410,8 @@ class UploadOptionsForm(forms.Form):
initial='workout_edit_view',
label='After Upload, go to')
raceid = forms.IntegerField(initial=0,widget=HiddenInput())
class Meta:
fields = ['make_plot','plottype','upload_toc2','makeprivate']
@@ -448,6 +450,7 @@ class UploadOptionsForm(forms.Form):
if int(raceid) in [r.id for r in races]:
therace = VirtualRace.objects.get(id=raceid)
self.fields['raceid'].initial = therace.id
if therace.sessiontype == 'race':
registrations = VirtualRaceResult.objects.filter(race=therace,userid=r.id)
else:

View File

@@ -371,7 +371,7 @@ def add_workouts_plannedsession(ws,ps,r):
ids = [w.id for w in wold] + [w.id for w in ws]
ids = list(set(ids))
if len(ids)>1 and ps.sessiontype in ['test','coursetest','race','fastest_distance','fastest_time']:
if len(ids)>1 and ps.sessiontype in ['test','coursetest','race']:
errors.append('For tests, you can only attach one workout')
return result,comments,errors
@@ -1496,23 +1496,33 @@ def default_class(r,w,race):
boatclass=boatclass,
adaptiveclass=adaptiveclass,
boattype=boattype,
sex=sex,
).order_by(
"agemax","-agemin","boattype","sex","weightclass",
"referencespeed"
)
if standards.count()==0:
# omit adaptive class
standards = CourseStandard.objects.filter(
agemin__lt=age,agemax__gt=age,
boattype=boattype
boattype=boattype,sex=sex,
).order_by(
"agemax","-agemin","boattype","sex",
"weightclass","referencespeed")
if standards.count()==0:
# omit boattype
standards = CourseStandard.objects.filter(
agemin__lt=age,agemax__gt=age
agemin__lt=age,agemax__gt=age,sex=sex,
).order_by(
"agemax","-agemin","boattype","sex",
"weightclass","referencespeed")
if standards.count()==0:
# omit boattype
standards = CourseStandard.objects.filter(
agemin__lt=age,agemax__gt=age,sex='male',
).order_by(
"agemax","-agemin","boattype","sex",
"weightclass","referencespeed")
if standards.count()==0:
# boolean, boattype, boatclass, adaptiveclass, weightclass, sex, coursestandard,
@@ -1526,6 +1536,153 @@ def default_class(r,w,race):
# No Course Standard
return True,boattype,boatclass,adaptiveclass,weightclass,sex,5.0,None
def add_workout_fastestrace(ws, race, r, recordid=0, doregister=False):
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
)
ids = [w.id for w in ws]
ids = list(set(ids))
if len(ids)>1 and race.sessiontype in ['test','coursetest','race','indoorrace','fastest_time','fastest_distance']:
errors.append('For tests, you can only attach one workout')
return result,comments,errors,0
username = r.user.first_name+' '+r.user.last_name
if r.birthdate:
age = calculate_age(r.birthdate)
else:
age = None
try:
record = IndoorVirtualRaceResult.objects.get(
userid=r.id,
race=race,
id=recordid
)
except IndoorVirtualRaceResult.DoesNotExist:
if doregister:
hasinitial,boattype,boatclass,adaptiveclass,weightclass,sex,referencespeed,initialcategory = default_class(r,ws[0],race)
if hasinitial:
record = IndoorVirtualRaceResult(
userid = r.id,
username = r.user.first_name+' '+r.user.last_name,
weightcategory=weightclass,
adaptiveclass=adaptiveclass,
race=race,
boatclass=boatclass,
sex=sex,
age = age,
referencespeed=referencespeed,
entrycategory=initialcategory,
)
record.save()
else:
errors.append("Unable to find a suitable start category")
return result,comments,errors,0
else:
errors.append("Couldn't find this entry")
return result,comments,errors,0
records = IndoorVirtualRaceResult.objects.filter(
userid=r.id,
race=race,
workoutid = ws[0].id
)
if ws[0].workouttype != record.boatclass:
errors.append('Your workout boat class is different than on your race registration')
return 0,comments,errors,0
if ws[0].workouttype not in mytypes.otwtypes:
errors.append('You must submit a on-the-water rowing workout')
return 0,comments, errors, 0
if record.weightcategory == 'lwt' and ws[0].weightcategory != record.weightcategory:
errors.append('Your workout weight category did not match the weight category you registered')
return 0,comments, errors,0
if ws[0].adaptiveclass != record.adaptiveclass:
errors.append('Your adaptive classification did not match the registration')
return 0,comments, errors, 0
# start adding sessions
if ws[0].startdatetime>=startdatetime and ws[0].startdatetime<=enddatetime:
ws[0].plannedsession = race
ws[0].save()
result += 1
else:
errors.append('Workout %i did not match the race window' % ws[0].id)
return result,comments,errors,0
if result>0:
for otherrecord in records:
otherrecord.workoutid = None
otherrecord.coursecompleted = False
otherrecord.save()
result, comment, errors = add_workouts_plannedsession(ws,race,r)
if result:
record.coursecompleted = True
record.workoutid = ws[0].id
if race.sessiontype == 'fastest_distance':
df = dataprep.getsmallrowdata_db(['time','cumdist'],ids=[ws[0].id])
fastest_milliseconds,startsecond,endsecond = datautils.getfastest(df,race.sessionvalue,mode='distance')
velo = race.sessionvalue/fastest_milliseconds
points = 100.*velo/record.referencespeed
if fastest_milliseconds > 0:
duration = to_time(1000.*fastest_milliseconds)
record.coursecompleted = True
record.duration = duration
record.distance = race.sessionvalue
record.points = points
record.startsecond = startsecond
record.endsecond = endsecond
record.save()
if race.sessiontype == 'fastest_time':
df = dataprep.getsmallrowdata_db(['time','cumdist'],ids=[ws[0].id])
fastest_meters, startsecond, endsecond = datautils.getfastest(df,race.sessionvalue,mode='time')
velo = fastest_meters/(60.*race.sessionvalue)
points = 100.*velo/record.referencespeed
if fastest_meters > 0:
duration = dt.time(0,race.sessionvalue)
record.duration = duration
record.distance = fastest_meters
record.coursecompleted = True
record.points = points
record.startsecond = startsecond
record.endsecond = endsecond
record.save()
if ws[0].privacy == 'private':
ws[0].privacy = 'visible'
ws[0].save()
comments.append('Workouts submitted to virtual events have to be public. We have changed the workout to a public workout.')
record.save()
else:
errors.append('Could not find a valid interval in this workout')
return result, comments, errors, 0
# Low Level functions - to be called by higher level methods

View File

@@ -55,7 +55,11 @@
The participants can row this challenge on any course, and their fastest time
over the challenge distance (respectively the largest distance achieved over the
challenge duration) is automatically extracted from the workout. No
<<<<<<< HEAD
need to program a set piece.
=======
need to program a set piece.
>>>>>>> develop
</p>
<p>
Standard Times are a way to compare results in a race category with

View File

@@ -6,15 +6,10 @@
</a>
</li>
<li id="races-new">
<a href="/rowers/virtualevent/create/">
<a href="/rowers/virtualevent/createchoice/">
<i class="far fa-flag fa-fw"></i>&nbsp;New Challenge
</a>
</li>
<li id="indoor-new">
<a href="/rowers/virtualevent/createindoor/">
<i class="far fa-flag fa-fw"></i>&nbsp;New Indoor Challenge
</a>
</li>
{% if race %}
{% if results %}
<li id="compare">

View File

@@ -0,0 +1,60 @@
{% extends "newbase.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}New Virtual Challenge{% endblock %}
{% block main %}
<h1>What type of challenge do you want to create?</h1>
<ul class="main-content">
<li class="rounder">
<h2>Course Challenge</h2>
<a href="/rowers/virtualevent/create/">
<div class="vignet">
<img src="/static/img/HOCR.png"
alt="Marked Course">
</div>
</a>
<p>On-the-water challenge with a course. You have to row through
all the gates on the course to create a valid entry. Ideal for
local challenges.
</p>
</li>
<li class="rounder">
<h2>Time or Distance Challenge</h2>
<a href="/rowers/virtualevent/createfastest/">
<div class="vignet">
<img src="/static/img/sculler.png"
alt="Marked Course">
</div>
</a>
<p>On-the-water challenge over a set time or distance. This can be rowed
on any body of water in the world. Ideal to create a global on-the-water
challenge.
</p>
</li>
<li class="rounder">
<h2>Indoor Rowing Challenge</h2>
<a href="/rowers/virtualevent/createindoor/">
<div class="vignet">
<img src="/static/img/domca.png"
alt="Marked Course">
</div>
</a>
<p>Indoor rowing challenge over a set time or distance. This can be rowed
on any indoor rowing machine. Dial up the given distance or time and row!
Ideal to create a global indoor rowing
challenge.
</p>
</li>
</ul>
{% endblock %}
{% block sidebar %}
{% include 'menu_racing.html' %}
{% endblock %}

View File

@@ -27,6 +27,10 @@
<td><a href="/rowers/virtualevent/{{ race.id }}">{{ race.name }}</a></td>
{% if race.sessiontype == 'race' %}
<td>{{ race.course.country }}</td>
{% elif race.sessiontype == 'fastest_distance' %}
<td>Worldwide On-the-water Challenge</td>
{% elif race.sessiontype == 'fastest_time' %}
<td>Worldwide On-the-water Challenge</td>
{% else %}
<td>Worldwide Indoor Challenge</td>
{% endif %}

View File

@@ -96,6 +96,14 @@
<tr>
<th>Course</th><td><a href="/rowers/courses/{{ race.course.id }}">{{ race.course }}</a></td>
</tr>
{% elif race.sessiontype == 'fastest_time' %}
<tr>
<th>Time Challenge</th><td>To be rowed on the water</td>
</tr>
{% elif race.sessiontype == 'fastest_distance' %}
<tr>
<th>Distance Challenge</th><td>To be rowed on the water</td>
</tr>
{% else %}
<tr>
<th>Indoor Race</th><td>To be rowed on a Concept2 ergometer</td>
@@ -314,8 +322,19 @@
<tr>
<td>{{ forloop.counter }}</td>
<td>
{% if race.sessiontype == 'race' %}
<a href="/rowers/workout/{{ result.workoutid|encode }}/view/entry/{{ result.id }}/">
{{ result.username }}</a></td>
{{ result.username }}</a>
{% elif race.sessiontype == 'fastest_time' %}
<a href="/rowers/workout/{{ result.workoutid|encode }}/view/entry/{{ result.id }}/nocourse/">
{{ result.username }}</a>
{% elif race.sessiontype == 'fastest_distance' %}
<a href="/rowers/workout/{{ result.workoutid|encode }}/view/entry/{{ result.id }}/nocourse/">
{{ result.username }}</a>
{% else %}
{{ result.username }}
{% endif %}
</td>
<td>{{ result.teamname }}</td>
{% if race.coursestandards %}
<td>{{ result.entrycategory }}</td>
@@ -586,6 +605,21 @@
You cannot submit results rowed
on other bodies of water.
</p>
{% elif race.sessiontype == 'fastest_time' %}
<p>
This on-the-water challenge asks you to try to get
as far as you can over the
race duration. It automatically finds the fastest interval
of the given duration
in your entire workout.
</p>
{% elif race.sessiontype == 'fastest_distance' %}
<p>
This on-the-water challenge asks you to try row as hard
as you can over a set distance.
It automatically finds the fastest interval of the given length
in your entire workout.
</p>
{% else %}
<p>
Indoor challenges are open for all, wherever you live.

View File

@@ -260,8 +260,11 @@ urlpatterns = [
# re_path(r'^list-workouts/(?P<startdatestring>\d+-\d+-\d+)/(?P<enddatestring>\d+-\d+-\d+)/$',views.workouts_view,
# name='workouts_view'),
re_path(r'^virtualevents/$',views.virtualevents_view,name='virtualevents_view'),
re_path(r'^virtualevent/createchoice/$', TemplateView.as_view(template_name='newchallengechoice.html'),name='newchallengechoice'),
re_path(r'^virtualevent/create/$',views.virtualevent_create_view,name='virtualevent_create_view'),
re_path(r'^virtualevent/createindoor/$',views.indoorvirtualevent_create_view,name='indoorvirtualevent_create_view'),
re_path(r'^virtualevent/createfastest/$',views.fastestvirtualevent_create_view,
name='fastestvirtualevent_create_view'),
re_path(r'^raceregistration/togglenotification/(?P<id>\d+)/$',
views.virtualevent_toggle_email_view,name='virtualevent_toggle_email_view'),
re_path(r'^indoorraceregistration/togglenotification/(?P<id>\d+)/$',
@@ -455,6 +458,7 @@ urlpatterns = [
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/split/$',views.workout_split_view,name='workout_split_view'),
# re_path(r'^workout/(?P<id>\d+)/interactiveplot/$',views.workout_biginteractive_view),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/view/entry/(?P<raceresult>\d+)/$',views.workout_view,name='workout_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/view/entry/(?P<nocourseraceresult>\d+)/nocourse/$',views.workout_view,name='workout_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/view/session/(?P<sessionresult>\d+)/$',views.workout_view,name='workout_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/view/$',views.workout_view,name='workout_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/video/$',views.workout_video_create_view,

View File

@@ -1881,12 +1881,14 @@ def plannedsession_view(request,id=0,userid=0):
ps = get_object_or_404(PlannedSession,pk=id)
if ps.sessiontype in ['race','indoorrace']:
try:
r = VirtualRace.objects.get(id=ps.id)
url = reverse('virtualevent_view',
kwargs={'id':ps.id}
)
kwargs={'id':ps.id}
)
return HttpResponseRedirect(url)
except VirtualRace.DoesNotExist:
pass
if ps.course:
coursescript,coursediv = course_map(ps.course)

View File

@@ -2706,6 +2706,153 @@ def indoorvirtualevent_create_view(request):
})
@login_required()
def fastestvirtualevent_create_view(request):
r = getrower(request.user)
if request.method == 'POST':
racecreateform = IndoorVirtualRaceForm(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']
sessionunit = cd['sessionunit']
sessionvalue = cd['sessionvalue']
name = cd['name']
registration_form = cd['registration_form']
registration_closure = cd['registration_closure']
evaluation_closure = cd['evaluation_closure']
contact_phone = cd['contact_phone']
contact_email = cd['contact_email']
coursestandards = cd['coursestandards']
# correct times
timezone_str = cd['timezone']
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)
)
if registration_form == 'manual':
try:
registration_closure = pytz.timezone(
timezone_str
).localize(
registration_closure.replace(tzinfo=None)
)
except AttributeError:
registration_closure = startdatetime
elif registration_form == 'windowstart':
registration_closure = startdatetime
elif registration_form == 'windowend':
registration_closure = enddatetime
else:
registration_closure = evaluation_closure
if sessionunit == 'min':
sessionmode = 'time'
sessiontype = 'fastest_time'
else:
sessionmode = 'distance'
sessiontype = 'fastest_distance'
vs = VirtualRace(
name=name,
startdate=startdate,
preferreddate = startdate,
start_time = start_time,
enddate=enddate,
end_time=end_time,
comment=comment,
sessiontype = sessiontype,
sessionunit = sessionunit,
sessionmode = sessionmode,
sessionvalue = sessionvalue,
course=None,
timezone=timezone_str,
coursestandards=coursestandards,
evaluation_closure=evaluation_closure,
registration_closure=registration_closure,
contact_phone=contact_phone,
contact_email=contact_email,
country = 'Indoor',
manager=request.user,
)
vs.save()
# create Site Announcement & Tweet
if settings.DEBUG or settings.TESTING:
dotweet = False
elif 'dev' in settings.SITE_URL:
dotweet = False
else:
dotweet = True
announcementshort = "New Virtual Challenge on rowsandall.com: {name}".format(
name = name,
)
announcement = announcementshort + " {siteurl}/rowers/virtualevent/{raceid}/".format(
siteurl = SITE_URL,
raceid = vs.id
)
if len(announcement)>250:
announcement = announcementshort
sa = SiteAnnouncement(
announcement = announcement,
dotweet = dotweet
)
sa.save()
url = reverse('virtualevents_view')
return HttpResponseRedirect(url)
else:
racecreateform = IndoorVirtualRaceForm(timezone=r.defaulttimezone)
breadcrumbs = [
{
'url':reverse('virtualevents_view'),
'name': 'Challenges'
},
{
'url':reverse('indoorvirtualevent_create_view',
),
'name': 'New Indoor Virtual Regatta'
},
]
return render(request,'fastestvirtualeventcreate.html',
{
'form':racecreateform,
'breadcrumbs':breadcrumbs,
'rower':r,
'active':'nav-racing',
})
@login_required()
def virtualevent_create_view(request):
r = getrower(request.user)
@@ -3172,13 +3319,16 @@ def virtualevent_submit_result_view(request,id=0,workoutid=0):
if selectedworkout is not None:
workouts = Workout.objects.filter(id=selectedworkout)
if race.sessiontype == 'race':
result,comments,errors,jobid = add_workout_race(
workouts,race,r,
splitsecond=splitsecond,recordid=recordid)
elif race.sessiontype in ['fastest_time','fastest_distance']:
result, comments, errors, jobid = add_workout_fastestrace(
workouts,race,r,recordid=recordid
)
else:
result,comments,errors,jobid = add_workout_indoorrace(
workouts,race,r,recordid=recordid)

View File

@@ -2323,7 +2323,7 @@ def workout_fusion_list(request,id=0,
# Basic view of workout
@permission_required('workout.view_workout',fn=get_workout_by_opaqueid,raise_exception=True)
def workout_view(request,id=0,raceresult=0,sessionresult=0):
def workout_view(request,id=0,raceresult=0,sessionresult=0,nocourseraceresult=0):
request.session['referer'] = absolute(request)['PATH']
if not request.user.is_anonymous:
@@ -2373,6 +2373,26 @@ def workout_view(request,id=0,raceresult=0,sessionresult=0):
except CourseTestResult.DoesNotExist:
pass
if nocourseraceresult != 0:
try:
result = IndoorVirtualRaceResult.objects.get(id=nocourseraceresult)
startsecond = result.startsecond
endsecond = result.endsecond
duration = row.duration
durationsecs = duration.hour*3600+duration.minute*60+duration.second
itime = [startsecond,endsecond-startsecond]
itype = [3,4]
intervaldata['itime'] = itime
intervaldata['itype'] = itype
rowdata.updateinterval_metric(' AverageBoatSpeed (m/s)',0.1,mode='larger',
debug=False,smoothwindow=15.,
activewindow = [startsecond,endsecond])
summary = rowdata.allstats()
except CourseTestResult.DoesNotExist:
pass
if raceresult != 0:
try:
result = VirtualRaceResult.objects.get(id=raceresult)
@@ -5042,7 +5062,7 @@ def workout_upload_view(request,
response = {}
if request.method == 'POST':
form = DocumentsForm(request.POST,request.FILES)
optionsform = UploadOptionsForm(request.POST,request=request,raceid=raceid)
optionsform = UploadOptionsForm(request.POST,request=request)
if form.is_valid():
# f = request.FILES['file']
@@ -5083,6 +5103,7 @@ def workout_upload_view(request,
upload_to_tp = optionsform.cleaned_data['upload_to_TrainingPeaks']
makeprivate = optionsform.cleaned_data['makeprivate']
landingpage = optionsform.cleaned_data['landingpage']
raceid = optionsform.cleaned_data['raceid']
try:
registrationid = optionsform.cleaned_data['submitrace']
@@ -5310,11 +5331,27 @@ def workout_upload_view(request,
messages.info(request,c)
for er in errors:
messages.error(request,er)
elif race.sessiontype in ['fastest_time','fastest_distance']:
result,comments,errors,jobid = add_workout_fastestrace(
[w], race,r,doregister=True,
)
if result:
messages.info(request,"We have submitted your workout to the race")
for c in comments:
messages.info(request,c)
for er in errors:
messages.error(request,er)
if int(registrationid)>0:
races = VirtualRace.objects.filter(
registration_closure__gt=timezone.now()
)
if raceid != 0:
races = VirtualRace.objects.filter(
registration_closure__gt=timezone.now(),
id=raceid,
)
registrations = IndoorVirtualRaceResult.objects.filter(
race__in = races,
id=registrationid,
@@ -5331,8 +5368,13 @@ def workout_upload_view(request,
registrations = registrations.filter(id=registrationid)
if registrations:
race = registrations[0].race
result,comments,errors,jobid = add_workout_indoorrace(
[w],race,r,recordid=registrations[0].id
if race.sessiontype == 'indoorrace':
result,comments,errors,jobid = add_workout_indoorrace(
[w],race,r,recordid=registrations[0].id
)
elif race.sessiontype in ['fastest_time','fastest_distance']:
result,comments, errors,jobid = add_workout_fastestrace(
[w],race,r,recordid=registrations[0].id
)
if result:
@@ -5351,9 +5393,14 @@ def workout_upload_view(request,
registrations = registrations2.filter(id=registrationid)
if registrations:
race = registrations[0].race
result,comments,errors,jobid = add_workout_race(
[w], race,r,recordid=registrations[0].id
)
if race.sessiontype == 'race':
result,comments,errors,jobid = add_workout_race(
[w], race,r,recordid=registrations[0].id
)
elif race.sessiontype in ['fastest_time','fastest_distance']:
result, comments, errors, jobid = add_workout_fastestrace(
[w],race,r,recordid=registrations[0].id
)
if result:
messages.info(
request,

BIN
static/img/HOCR.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

BIN
static/img/domca.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
static/img/domca.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

BIN
static/img/erg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
static/img/sculler.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 530 KiB