diff --git a/rowers/dataprep.py b/rowers/dataprep.py index f54cf9d7..9eb8f65c 100644 --- a/rowers/dataprep.py +++ b/rowers/dataprep.py @@ -809,34 +809,7 @@ def create_row_df(r,distance,duration,startdatetime, return (id, message) - -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 +from utils import totaltime_sec_to_string # Processes painsled CSV file to database def save_workout_database(f2, r, dosmooth=True, workouttype='rower', diff --git a/rowers/forms.py b/rowers/forms.py index 1e7a7032..19665e58 100644 --- a/rowers/forms.py +++ b/rowers/forms.py @@ -695,6 +695,19 @@ class WorkoutSessionSelectForm(forms.Form): 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): team = forms.ModelMultipleChoiceField( queryset=Team.objects.all(), diff --git a/rowers/models.py b/rowers/models.py index 20b86584..722e087c 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -1335,12 +1335,14 @@ class VirtualRaceResult(models.Model): 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) + from rowers.metrics import rowingmetrics diff --git a/rowers/plannedsessions.py b/rowers/plannedsessions.py index 7aa5f311..ce3ba8d5 100644 --- a/rowers/plannedsessions.py +++ b/rowers/plannedsessions.py @@ -8,7 +8,7 @@ from django.db import IntegrityError import uuid from django.conf import settings import pytz -from utils import myqueue +from utils import myqueue,calculate_age,totaltime_sec_to_string import django_rq queue = django_rq.get_queue('default') @@ -18,7 +18,7 @@ queuehigh = django_rq.get_queue('low') from rowers.models import ( Rower, Workout,Team, GeoCourse, TrainingMicroCycle,TrainingMesoCycle,TrainingMacroCycle, - TrainingPlan,PlannedSession, + TrainingPlan,PlannedSession,VirtualRaceResult ) import metrics @@ -596,3 +596,95 @@ def remove_rower_race(r,race): race.rower.remove(r) 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 + + + + wold = Workout.objects.filter(plannedsession=race,user=r) + ids = [w.id for w in wold] + [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) + + 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') + + 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() + + diff --git a/rowers/templates/race_submit.html b/rowers/templates/race_submit.html new file mode 100644 index 00000000..e8678c04 --- /dev/null +++ b/rowers/templates/race_submit.html @@ -0,0 +1,59 @@ +{% extends "base.html" %} +{% load staticfiles %} +{% load rowerfilters %} + +{% block title %}Submit Race Result{% endblock %} + +{% block meta %} + + + +{% endblock %} + + +{% block content %} +
+
+

Submit Your Result for {{ race.name }}

+
+ +
+ +
+
+
+

 

+
+
+

Workouts

+ + + {% for field in w_form.hidden_fields %} + {{ field }} + {% endfor %} + {% for field in w_form.visible_fields %} + + {% endfor %} + +
+ {{ field }} +
+
+
+
+ {% csrf_token %} + +
+ + +
+{% endblock %} + +{% block scripts %} +{% endblock %} diff --git a/rowers/urls.py b/rowers/urls.py index c28fbbc7..21618817 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -149,6 +149,8 @@ urlpatterns = [ url(r'^virtualevent/(?P\d+)/edit$',views.virtualevent_edit_view), url(r'^virtualevent/(?P\d+)/register$',views.virtualevent_register_view), url(r'^virtualevent/(?P\d+)/withdraw$',views.virtualevent_withdraw_view), + url(r'^virtualevent/(?P\d+)/submit$', + views.virtualevent_submit_result_view), url(r'^list-workouts/$',views.workouts_view), url(r'^list-courses/$',views.courses_view), url(r'^courses/upload$',views.course_upload_view), diff --git a/rowers/utils.py b/rowers/utils.py index fa159aba..1e5d03e3 100644 --- a/rowers/utils.py +++ b/rowers/utils.py @@ -337,3 +337,31 @@ def wavg(group, avg_name, weight_name): return (d * w).sum() / w.sum() except ZeroDivisionError: 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 diff --git a/rowers/views.py b/rowers/views.py index 320ac3e3..5c27dd0f 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -34,7 +34,7 @@ from rowers.forms import ( WorkFlowLeftPanelElement,WorkFlowMiddlePanelElement, LandingPageForm,PlannedSessionSelectForm,WorkoutSessionSelectForm, PlannedSessionTeamForm,PlannedSessionTeamMemberForm, - VirtualRaceSelectForm + VirtualRaceSelectForm,WorkoutRaceSelectForm, ) from django.core.urlresolvers import reverse from django.core.exceptions import PermissionDenied @@ -13581,3 +13581,101 @@ def virtualevent_edit_view(request,id=0): '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(): + selectedworkouts = [w_form.cleaned_data['workouts']] + else: + selectedworkouts = [] + + if len(selectedworkouts) == 0: + for w in ws: + remove_workout_plannedsession(w,race) + + if selectedworkouts: + workouts = Workout.objects.filter(user=r, + id__in=selectedworkouts) + + for w in ws: + if w.id not in selectedworkouts: + remove_workout_plannedsession(w,race) + delete_race_result(w,race) + + 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 + else: + w_form = WorkoutRaceSelectForm(workoutdata=workoutdata) + + return render(request,'race_submit.html', + { + 'race':race, + 'workouts':ws, + 'rower':r, + 'w_form':w_form, + }) + + +