diff --git a/rowers/forms.py b/rowers/forms.py
index d0e78a23..27deb56f 100644
--- a/rowers/forms.py
+++ b/rowers/forms.py
@@ -389,18 +389,26 @@ class UploadOptionsForm(forms.Form):
super(UploadOptionsForm, self).__init__(*args, **kwargs)
r = Rower.objects.get(user=self.request.user)
races = VirtualRace.objects.filter(
- registration_closure__gt=timezone.now(),
- sessiontype='indoorrace')
+ registration_closure__gt=timezone.now())
registrations = IndoorVirtualRaceResult.objects.filter(
race__in = races,
userid = r.id)
+ registrations2 = VirtualRaceResult.objects.filter(
+ race__in = races,
+ userid = r.id,
+ )
+
raceids = [r.race.id for r in registrations]
+ raceids2 = [r.race.id for r in registrations2]
+
+ raceids = raceids+raceids2
races = VirtualRace.objects.filter(
id__in=raceids
)
+
if races:
self.fields['submitrace'].queryset = races
else:
@@ -409,6 +417,8 @@ class UploadOptionsForm(forms.Form):
if int(raceid) in raceids:
self.fields['submitrace'].initial = VirtualRace.objects.get(id=raceid)
+
+
# The form to indicate additional actions to be performed immediately
# after a successful upload. This version allows the Team manager to select
# a team member
diff --git a/rowers/plannedsessions.py b/rowers/plannedsessions.py
index 63286c21..4c859b65 100644
--- a/rowers/plannedsessions.py
+++ b/rowers/plannedsessions.py
@@ -1472,7 +1472,7 @@ def add_workout_race(ws,race,r,splitsecond=0,recordid=0):
errors.append('For tests and training sessions, selected workouts must all be done on the same date')
return result,comments,errors,0
- if ws.count()>1 and race.sessiontype == 'test':
+ if len(ws)>1 and race.sessiontype == 'test':
errors.append('For tests, you can only attach one workout')
return result,comments,errors,0
@@ -1553,6 +1553,8 @@ def add_workout_race(ws,race,r,splitsecond=0,recordid=0):
job = myqueue(queue,handle_check_race_course,ws[0].csvfilename,
ws[0].id,race.course.id,record.id,splitsecond=splitsecond)
+
+
add_workouts_plannedsession(ws,race,r)
diff --git a/rowers/tasks.py b/rowers/tasks.py
index 14a6753d..f46caa62 100644
--- a/rowers/tasks.py
+++ b/rowers/tasks.py
@@ -1148,6 +1148,37 @@ def handle_send_disqualification_email(
return 1
+@app.task
+def handle_send_withdraw_email(
+ useremail,username,reason,message, racename, **kwargs):
+
+ if 'debug' in kwargs:
+ debug = kwargs['debug']
+ else:
+ debug = True
+
+ subject = "Your result for {n} has been removed on rowsandall.com".format(
+ n = racename
+ )
+
+ from_email = 'Rowsandall '
+
+ d = {
+ 'username':username,
+ 'reason':reason,
+ 'siteurl':siteurl,
+ 'message': htmlstrip(message),
+ 'racename':racename,
+ }
+
+ res = send_template_email(from_email,[useremail],
+ subject,
+ 'withdraw_email.html',
+ d,**kwargs)
+
+ return 1
+
+
@app.task
def handle_sendemail_expired(useremail,userfirstname,userlastname,expireddate,
**kwargs):
diff --git a/rowers/templates/disqualification_view.html b/rowers/templates/disqualification_view.html
index b2a7ef4f..f1b696c4 100644
--- a/rowers/templates/disqualification_view.html
+++ b/rowers/templates/disqualification_view.html
@@ -114,7 +114,7 @@
Please select a reason and add a comment (mandatory).
- Pressing 'Reject' disqualifies the submitted result.
+ Pressing 'Reject' removes the submitted result.
An email will be sent to the rower. There is no "undo".
+ Before you reject this challenge result, please carefully review it
+ using the information below. If you still want to reject the entry,
+ scroll down to the rejection form.
+
+ Please select a reason and add a comment (mandatory).
+ Pressing 'Reject' removes the submitted result.
+ An email will be sent to the rower. There is no "undo".
+
+
+
+
+
+
+{% endblock %}
+
+{% block sidebar %}
+{% include 'menu_racing.html' %}
+{% endblock %}
diff --git a/rowers/urls.py b/rowers/urls.py
index 5df0f9ba..097ad88c 100644
--- a/rowers/urls.py
+++ b/rowers/urls.py
@@ -195,6 +195,8 @@ urlpatterns = [
views.virtualevent_submit_result_view,name='virtualevent_submit_result_view'),
re_path(r'^virtualevent/(?P\d+)/disqualify/(?P\d+)/',
views.virtualevent_disqualify_view,name='virtualevent_disqualify_view'),
+ re_path(r'^virtualevent/(?P\d+)/withdrawresult/(?P\d+)/',
+ views.virtualevent_withdrawresult_view,name='virtualevent_withdrawresult_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'^courses/upload/$',views.course_upload_view,name='course_upload_view'),
diff --git a/rowers/views/racesviews.py b/rowers/views/racesviews.py
index 845a79cf..b0d2c9d7 100644
--- a/rowers/views/racesviews.py
+++ b/rowers/views/racesviews.py
@@ -647,7 +647,7 @@ def virtualevent_disqualify_view(request,id=0,recordid=0):
},
{
'url':reverse(virtualevent_disqualify_view,
- kwargs={'raceid':raceid,
+ kwargs={'id':id,
'recordid':recordid}),
'name': 'Disqualify Entry'
},
@@ -688,6 +688,149 @@ def virtualevent_disqualify_view(request,id=0,recordid=0):
'record':record,
})
+@login_required()
+def virtualevent_withdrawresult_view(request,id=0,recordid=0):
+
+ r = getrower(request.user)
+ race = get_object_or_404(VirtualRace,pk=id)
+
+
+ if race.sessiontype == 'race':
+ recordobj = VirtualRaceResult
+ else:
+ recordobj = IndoorVirtualRaceResult
+
+ # datum moet voor race evaluation date zijn (ook in template controleren)
+
+ try:
+ record = recordobj.objects.get(id=recordid)
+ except recordobj.DoesNotExist:
+ messages.error(request,"We couldn't find the record")
+
+ if r.id != record.userid:
+ raise PermissionDenied("You are not the owner of this result")
+
+ if timezone.now() > race.evaluation_closure+datetime.timedelta(hours=1):
+ messages.error(request,"The evaluation is already closed and the results are official")
+ url = reverse('virtualevent_view',kwargs={'id':raceid})
+
+ return HttpResponseRedirect(url)
+
+ if request.method == 'POST':
+ form = DisqualificationForm(request.POST)
+ if form.is_valid():
+ message = form.cleaned_data['message']
+ reason = form.cleaned_data['reason']
+ disqualifier = disqualifiers[reason]
+
+ r = Rower.objects.get(id=record.userid)
+ name = record.username
+
+ job = myqueue(queue,handle_send_withdraw_email,
+ r.user.email, name,
+ disqualifier,message,race.name)
+
+ messages.info(request,"We have invalidated the result for: "+str(record))
+
+ record.coursecompleted = False
+ record.save()
+
+ url = reverse('virtualevent_view',kwargs={'id':id})
+
+ return HttpResponseRedirect(url)
+
+ else:
+ form = DisqualificationForm(request.POST)
+
+ workout = Workout.objects.get(id=record.workoutid)
+
+ g = GraphImage.objects.filter(workout=workout).order_by("-creationdatetime")
+ for i in g:
+ try:
+ width,height = Image.open(i.filename).size
+ i.width = width
+ i.height = height
+ i.save()
+ except:
+ pass
+
+ script, div = interactive_chart(record.workoutid)
+
+ f1 = workout.csvfilename
+ rowdata = rdata(f1)
+ hascoordinates = 1
+ if rowdata != 0:
+ try:
+ latitude = rowdata.df[' latitude']
+ if not latitude.std():
+ hascoordinates = 0
+ except (KeyError, AttributeError):
+ hascoordinates = 0
+ else:
+ hascoordinates = 0
+
+ if hascoordinates:
+ mapscript, mapdiv = leaflet_chart(rowdata.df[' latitude'],
+ rowdata.df[' longitude'],
+ workout.name)
+ else:
+ mapscript = ""
+ mapdiv = ""
+
+ breadcrumbs = [
+ {
+ 'url':reverse('virtualevents_view'),
+ 'name': 'Challenges'
+ },
+ {
+ 'url':reverse('virtualevent_view',
+ kwargs={'id':race.id}),
+ 'name': race.name
+ },
+ {
+ 'url':reverse(virtualevent_disqualify_view,
+ kwargs={'id':id,
+ 'recordid':recordid}),
+ 'name': 'Disqualify Entry'
+ },
+ ]
+
+ buttons = []
+
+ if not request.user.is_anonymous:
+ if race_can_register(r,race):
+ buttons += ['registerbutton']
+
+ if race_can_adddiscipline(r,race):
+ buttons += ['adddisciplinebutton']
+
+ 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']
+
+ return render(request,"withdraw_view.html",
+ {'workout':workout,
+ 'active':'nav-racing',
+ 'graphs':g,
+ 'buttons':buttons,
+ 'interactiveplot':script,
+ 'the_div':div,
+ 'mapscript':mapscript,
+ 'mapdiv':mapdiv,
+ 'form':form,
+ 'race':race,
+ 'record':record,
+ })
+
+
def virtualevent_view(request,id=0):
results = []
diff --git a/rowers/views/statements.py b/rowers/views/statements.py
index 695e5d91..156919f9 100644
--- a/rowers/views/statements.py
+++ b/rowers/views/statements.py
@@ -195,6 +195,7 @@ from rowers.tasks import (
handle_sendemail_unrecognized,handle_sendemailnewcomment,
handle_sendemailsummary,
handle_send_disqualification_email,
+ handle_send_withdraw_email,
handle_sendemailfile,
handle_sendemailkml,
handle_sendemailnewresponse, handle_updatedps,
diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py
index 7a0c6d3a..6e851339 100644
--- a/rowers/views/workoutviews.py
+++ b/rowers/views/workoutviews.py
@@ -4965,26 +4965,46 @@ def workout_upload_view(request,
messages.error(request,message)
if race and race_can_submit(r,race):
- records = IndoorVirtualRaceResult.objects.filter(
- race=race,
- userid=r.id
- )
-
- if records:
-
- result,comments,errors,jobid = add_workout_indoorrace(
- [w],race,r,recordid=records[0].id
+ if race.sessiontype == 'indoorrace':
+ records = IndoorVirtualRaceResult.objects.filter(
+ race=race,
+ userid=r.id
)
- if result:
- messages.info(
- request,
- "We have submitted your workout to the race")
+ if records:
- for c in comments:
- messages.info(request,c)
- for er in errors:
- messages.error(request,er)
+ result,comments,errors,jobid = add_workout_indoorrace(
+ [w],race,r,recordid=records[0].id
+ )
+
+ 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 race.sessiontype == 'race':
+ records = VirtualRaceResult.objects.filter(
+ race=race,userid=r.id
+ )
+
+
+ if records:
+ result,comments,errors,jobid = add_workout_race(
+ [w], race,r,recordid=records[0].id
+ )
+ 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 landingpage != 'workout_upload_view':