1730 lines
53 KiB
Python
1730 lines
53 KiB
Python
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import print_function
|
|
from __future__ import unicode_literals
|
|
|
|
# Python
|
|
from django.utils import timezone
|
|
from datetime import datetime
|
|
import datetime as dt
|
|
from datetime import timedelta
|
|
from datetime import date
|
|
import time
|
|
from django.db import IntegrityError
|
|
import uuid
|
|
from django.conf import settings
|
|
import pytz
|
|
from dateutil import parser
|
|
from rowers.utils import myqueue,calculate_age,totaltime_sec_to_string
|
|
import re
|
|
import django_rq
|
|
queue = django_rq.get_queue('default')
|
|
queuelow = django_rq.get_queue('low')
|
|
queuehigh = django_rq.get_queue('low')
|
|
|
|
import pandas as pd
|
|
|
|
from rowingdata import rowingdata as rrdata
|
|
from rowingdata import rower as rrower
|
|
|
|
# Wrapper around the rowingdata call to catch some exceptions
|
|
# Checks for CSV file, then for gzipped CSV file, and if all fails, returns 0
|
|
def rdata(file,rower=rrower()):
|
|
try:
|
|
res = rrdata(csvfile=file,rower=rower)
|
|
except (IOError, IndexError, EOFError,FileNotFoundError):
|
|
try:
|
|
res = rrdata(csvfile=file+'.gz',rower=rower)
|
|
except (IOError, IndexError, EOFError,FileNotFoundError):
|
|
res = 0
|
|
|
|
return res
|
|
|
|
from rowers.models import (
|
|
Rower, Workout,Team,
|
|
GeoCourse, TrainingMicroCycle,TrainingMesoCycle,TrainingMacroCycle,
|
|
TrainingPlan,PlannedSession,VirtualRaceResult,CourseTestResult,
|
|
get_course_timezone, IndoorVirtualRaceResult,VirtualRace,createmacrofillers,
|
|
createmesofillers,createmicrofillers,CourseStandard,
|
|
)
|
|
|
|
from rowers.courses import get_time_course
|
|
|
|
from rowers.emails import htmlstrip,htmlstripnobr
|
|
|
|
import rowers.mytypes as mytypes
|
|
|
|
import rowers.metrics as metrics
|
|
import numpy as np
|
|
import rowers.dataprep as dataprep
|
|
import rowers.courses as courses
|
|
import iso8601
|
|
from iso8601 import ParseError
|
|
from rowers.tasks import handle_check_race_course
|
|
from rowers.tasks import (
|
|
handle_sendemail_raceregistration,handle_sendemail_racesubmission
|
|
)
|
|
from rowers.utils import totaltime_sec_to_string
|
|
|
|
def checkscores(r,macrocycles):
|
|
for m in macrocycles:
|
|
createmesofillers(m)
|
|
m.plantime = 0
|
|
m.actualtime = 0
|
|
m.plandistance = 0
|
|
m.actualdistance = 0
|
|
m.planrscore = 0
|
|
m.actualrscore = 0
|
|
m.plantrimp = 0
|
|
m.actualtrimp = 0
|
|
|
|
|
|
mesocycles = TrainingMesoCycle.objects.filter(
|
|
plan=m,
|
|
type='userdefined').order_by("startdate")
|
|
|
|
for me in mesocycles:
|
|
me.plantime = 0
|
|
me.actualtime = 0
|
|
me.plandistance = 0
|
|
me.actualdistance = 0
|
|
me.planrscore = 0
|
|
me.actualrscore = 0
|
|
me.plantrimp = 0
|
|
me.actualtrimp = 0
|
|
|
|
microcycles = TrainingMicroCycle.objects.filter(
|
|
plan=me,
|
|
type='userdefined').order_by("startdate")
|
|
|
|
for mm in microcycles:
|
|
sps = get_sessions(r,startdate=mm.startdate,enddate=mm.enddate)
|
|
|
|
# sps = PlannedSession.objects.filter(
|
|
# rower = r,
|
|
# startdate__lte=mm.enddate,
|
|
# enddate__gte=mm.startdate)
|
|
|
|
|
|
mm.plantime = 0
|
|
mm.actualtime = 0
|
|
mm.plandistance = 0
|
|
mm.actualdistance = 0
|
|
mm.planrscore = 0
|
|
mm.actualrscore = 0
|
|
mm.plantrimp = 0
|
|
mm.actualtrimp = 0
|
|
|
|
|
|
if mm.type == 'userdefined':
|
|
for ps in sps:
|
|
ratio, status, cdate = is_session_complete(r,ps)
|
|
if ps.sessionmode == 'time':
|
|
mm.plantime += ps.sessionvalue
|
|
mm.actualtime += int(ps.sessionvalue*ratio)
|
|
elif ps.sessionmode == 'distance' and ps.sessiontype != 'race':
|
|
mm.plandistance += ps.sessionvalue
|
|
mm.actualdistance += int(ps.sessionvalue*ratio)
|
|
elif ps.sessionmode == 'rScore':
|
|
mm.planrscore += ps.sessionvalue
|
|
mm.actualrscore += int(ps.sessionvalue*ratio)
|
|
elif ps.sessionmode == 'TRIMP':
|
|
mm.plantrimp += ps.sessionvalue
|
|
mm.actualtrimp += int(ps.sessionvalue*ratio)
|
|
|
|
mm.save()
|
|
|
|
me.plantime += mm.plantime
|
|
me.actualtime += mm.actualtime
|
|
me.plandistance += mm.plandistance
|
|
me.actualdistance += mm.actualdistance
|
|
me.planrscore += mm.planrscore
|
|
me.actualrscore += mm.actualrscore
|
|
me.plantrimp += mm.plantrimp
|
|
me.actualtrimp += mm.actualtrimp
|
|
|
|
if me.type == 'userdefined':
|
|
me.save()
|
|
|
|
m.plantime += me.plantime
|
|
m.actualtime += me.actualtime
|
|
m.plandistance += me.plandistance
|
|
m.actualdistance += me.actualdistance
|
|
m.planrscore += me.planrscore
|
|
m.actualrscore += me.actualrscore
|
|
m.plantrimp += me.plantrimp
|
|
m.actualtrimp += me.actualtrimp
|
|
|
|
|
|
|
|
if m.type == 'userdefined':
|
|
m.save()
|
|
|
|
|
|
def get_execution_report(rower,startdate,enddate,plan=None):
|
|
if plan:
|
|
macros = TrainingMacroCycle.objects.filter(plan=plan).order_by("startdate")
|
|
checkscores(rower,macros)
|
|
mesos = TrainingMesoCycle.objects.filter(plan__in=macros).order_by("startdate")
|
|
micros = TrainingMicroCycle.objects.filter(plan__in=mesos).order_by("startdate")
|
|
micros = micros.exclude(enddate__lte=startdate).exclude(startdate__gte=enddate)
|
|
else:
|
|
plans = TrainingPlan.objects.filter(startdate__lte=startdate,enddate__gte=startdate)
|
|
plans2 = TrainingPlan.objects.filter(enddate__lte=enddate,startdate__lte=enddate)
|
|
plans = plans | plans2
|
|
|
|
plans = plans.exclude(status=False).order_by("-enddate")
|
|
|
|
if not plans:
|
|
# make week cycles here
|
|
# get monday before startdate
|
|
startdate += timedelta(days=1-startdate.isoweekday())
|
|
startdate = startdate-timedelta(days=7)
|
|
micros = []
|
|
while startdate <= enddate:
|
|
micro = type('micros',(object,),
|
|
{
|
|
'startdate':startdate,
|
|
'enddate':startdate+timedelta(days=7)
|
|
})
|
|
micros.append(micro)
|
|
startdate += timedelta(days=7)
|
|
else:
|
|
plan = plans[0]
|
|
macros = TrainingMacroCycle.objects.filter(plan=plan).order_by("startdate")
|
|
checkscores(rower,macros)
|
|
mesos = TrainingMesoCycle.objects.filter(plan__in=macros).order_by("startdate")
|
|
micros = TrainingMicroCycle.objects.filter(
|
|
plan__in=mesos,
|
|
enddate__gte=startdate,
|
|
startdate__lte=enddate,
|
|
).order_by("startdate")
|
|
|
|
# we've got micros, now get sessions
|
|
startdates = []
|
|
planned = []
|
|
executed = []
|
|
|
|
for mm in micros:
|
|
plannedscore = 0
|
|
actualscore = 0
|
|
sps = get_sessions(rower,startdate=mm.startdate,enddate=mm.enddate)
|
|
unmatchedworkouts = Workout.objects.filter(
|
|
user=rower,
|
|
plannedsession=None,
|
|
date__gte=mm.startdate,date__lte=mm.enddate).exclude(duplicate=True)
|
|
for w in unmatchedworkouts:
|
|
if w.rscore != 0:
|
|
actualscore += w.rscore
|
|
elif w.hrtss != 0:
|
|
actualscore += w.hrtss
|
|
else:
|
|
minutes = w.duration.hour*60+w.duration.minute
|
|
actualscore += minutes
|
|
for ps in sps:
|
|
ratio, status, cdate = is_session_complete(rower,ps)
|
|
if ps.sessionmode == 'rScore':
|
|
plannedscore += ps.sessionvalue
|
|
actualscore += ratio*ps.sessionvalue
|
|
else:
|
|
ws = Workout.objects.filter(user=rower,plannedsession=ps)
|
|
if not ws:
|
|
if ps.sessionmode == 'time':
|
|
plannedscore += ps.sessionvalue
|
|
elif ps.sessionmode == 'distance':
|
|
plannedscore += 60.
|
|
elif ps.sessionmode == 'TRIMP':
|
|
plannedscore += ps.sessionvalue/2.
|
|
|
|
for w in ws:
|
|
if w.rscore != 0:
|
|
if ratio > 0:
|
|
plannedscore += w.rscore/ratio
|
|
actualscore += w.rscore
|
|
else:
|
|
plannedscore += 60
|
|
actualscore += 0
|
|
elif w.hrtss != 0:
|
|
if ratio > 0:
|
|
plannedscore += w.hrtss/ratio
|
|
actualscore += w.hrtss
|
|
else:
|
|
plannedscore += 60
|
|
actualscore += 0
|
|
else:
|
|
minutes = w.duration.hour*60+w.duration.minute
|
|
if ratio > 0:
|
|
plannedscore += minutes/ratio
|
|
else:
|
|
plannedscore += 60
|
|
|
|
actualscore += minutes
|
|
|
|
actualscore = int(actualscore)
|
|
plannedscore = int(plannedscore)
|
|
|
|
startdates += [mm.startdate]
|
|
planned += [plannedscore]
|
|
executed += [actualscore]
|
|
|
|
|
|
data = pd.DataFrame({
|
|
'startdate':startdates,
|
|
'planned':planned,
|
|
'executed':executed,
|
|
})
|
|
|
|
return(data,'ok')
|
|
|
|
def get_indoorraces(workout):
|
|
races1 = VirtualRace.objects.filter(
|
|
sessiontype='indoorrace',
|
|
startdate__lte=workout.date,
|
|
enddate__gte=workout.date,
|
|
sessionmode='distance',
|
|
sessionvalue=workout.distance)
|
|
|
|
|
|
if workout.duration.second == 0 and workout.duration.microsecond == 0:
|
|
duration = 60*workout.duration.hour+workout.duration.minute
|
|
|
|
|
|
races2 = VirtualRace.objects.filter(
|
|
sessiontype='indoorrace',
|
|
startdate__lte=workout.date,
|
|
enddate__gte=workout.date,
|
|
sessionmode='time',
|
|
sessionvalue=duration)
|
|
|
|
races = races1 | races2
|
|
else:
|
|
races = races1
|
|
|
|
registrations = IndoorVirtualRaceResult.objects.filter(
|
|
race__in = races,
|
|
boatclass = workout.workouttype,
|
|
userid=workout.user.id)
|
|
|
|
races = [r.race for r in registrations]
|
|
|
|
|
|
return races
|
|
|
|
def get_todays_micro(plan,thedate=date.today()):
|
|
thismicro = None
|
|
|
|
thismacro = TrainingMacroCycle.objects.filter(
|
|
plan=plan,
|
|
startdate__lte = thedate,
|
|
enddate__gte = thedate
|
|
)
|
|
|
|
if thismacro:
|
|
thismeso = TrainingMesoCycle.objects.filter(
|
|
plan=thismacro[0],
|
|
startdate__lte = thedate,
|
|
enddate__gte = thedate
|
|
)
|
|
|
|
if thismeso:
|
|
thismicro = TrainingMicroCycle.objects.filter(
|
|
plan=thismeso[0],
|
|
startdate__lte = thedate,
|
|
enddate__gte = thedate
|
|
)
|
|
|
|
if thismicro:
|
|
thismicro = thismicro[0]
|
|
|
|
return thismicro
|
|
|
|
# Low Level functions - to be called by higher level methods
|
|
def add_workouts_plannedsession(ws,ps,r):
|
|
result = 0
|
|
comments = []
|
|
errors = []
|
|
|
|
# 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 ps.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 ps.sessiontype == 'test':
|
|
errors.append('For tests, you can only attach one workout')
|
|
return result,comments,errors
|
|
|
|
|
|
|
|
wold = Workout.objects.filter(plannedsession=ps,user=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']:
|
|
errors.append('For tests, you can only attach one workout')
|
|
return result,comments,errors
|
|
|
|
# start adding sessions
|
|
for w in ws:
|
|
if w.date>=ps.startdate and w.date<=ps.enddate:
|
|
w.plannedsession = ps
|
|
w.save()
|
|
result += 1
|
|
comments.append('Attached workout %i to session' % w.id)
|
|
if ps.sessiontype == 'coursetest':
|
|
record = CourseTestResult(
|
|
userid=w.user.id,
|
|
plannedsession=ps,
|
|
duration=dt.time(0,0),
|
|
coursecompleted=False,
|
|
)
|
|
record.save()
|
|
job = myqueue(queue,handle_check_race_course,w.csvfilename,
|
|
w.id,ps.course.id,record.id,
|
|
w.user.user.email,w.user.user.first_name,
|
|
mode='coursetest')
|
|
else:
|
|
errors.append('Workout %i did not match session dates' % w.id)
|
|
|
|
return result,comments,errors
|
|
|
|
|
|
def remove_workout_plannedsession(w,ps):
|
|
if w.plannedsession == ps:
|
|
w.plannedsession = None
|
|
w.save()
|
|
return 1
|
|
|
|
return 0
|
|
|
|
def clone_planned_session(ps):
|
|
ps.save()
|
|
ps.pk = None # creates new instance
|
|
ps.save()
|
|
|
|
def timefield_to_seconds_duration(t):
|
|
duration = t.hour*3600.
|
|
duration += t.minute * 60.
|
|
duration += t.second
|
|
duration += t.microsecond/1.e6
|
|
|
|
return duration
|
|
|
|
|
|
def get_virtualrace_times(virtualrace):
|
|
geocourse = GeoCourse.objects.get(id = virtualrace.course.id)
|
|
timezone_str = get_course_timezone(geocourse)
|
|
|
|
startdatetime = datetime.datetime.combine(
|
|
virtualrace.startdate,virtualrace.start_time)
|
|
enddatetime = datetime.datetime.combine(
|
|
virtualrace.enddate,virtualrace.end_time)
|
|
|
|
startdatetime = pytz.timezone(timezone_str).localize(
|
|
startdatetime
|
|
)
|
|
enddatetime = pytz.timezone(timezone_str).localize(
|
|
enddatetime
|
|
)
|
|
|
|
return {
|
|
'startdatetime':startdatetime,
|
|
'enddatetime':enddatetime,
|
|
'evaluation_closure':virtualrace.evaluation_closure,
|
|
'registration_closure':virtualrace.registration_closure,
|
|
}
|
|
|
|
def get_session_metrics(ps):
|
|
rowers = ps.rower.all()
|
|
rscore = []
|
|
trimp = []
|
|
duration = []
|
|
distance = []
|
|
firstname = []
|
|
lastname = []
|
|
completedate = []
|
|
status = []
|
|
|
|
for r in rowers:
|
|
rscorev = 0
|
|
trimpv = 0
|
|
durationv = 0
|
|
distancev = 0
|
|
completedatev = ''
|
|
statusv = 0
|
|
|
|
ws = Workout.objects.filter(user=r,plannedsession=ps).order_by("date")
|
|
|
|
if ws.count() != 0:
|
|
for w in ws:
|
|
distancev += w.distance
|
|
durationv += timefield_to_seconds_duration(w.duration)
|
|
thetrimp,hrtss = dataprep.workout_trimp(w)
|
|
trimpv += thetrimp
|
|
tss = dataprep.workout_rscore(w)[0]
|
|
if not np.isnan(tss) and tss != 0:
|
|
rscorev += tss
|
|
elif tss == 0:
|
|
rscorev += hrtss
|
|
|
|
ratio,statusv,completiondate = is_session_complete_ws(ws,ps)
|
|
try:
|
|
completedatev = completiondate.strftime('%Y-%m-%d')
|
|
except AttributeError:
|
|
completedatev = ''
|
|
durationv /= 60.
|
|
|
|
|
|
trimp.append(int(trimpv))
|
|
duration.append(int(durationv))
|
|
distance.append(int(distancev))
|
|
rscore.append(int(rscorev))
|
|
firstname.append(r.user.first_name)
|
|
lastname.append(r.user.last_name)
|
|
status.append(statusv)
|
|
completedate.append(completedatev)
|
|
|
|
thedict = {
|
|
'first_name':firstname,
|
|
'last_name':lastname,
|
|
'duration':duration,
|
|
'distance':distance,
|
|
'rscore':rscore,
|
|
'trimp':trimp,
|
|
'completedate':completedate,
|
|
'status':status,
|
|
}
|
|
|
|
return thedict
|
|
|
|
cratiocolors = {
|
|
'better than nothing': "lightgreen",
|
|
'partial': "mediumgreen",
|
|
'on target': "green",
|
|
'over target': "purple",
|
|
'way over target': "violet",
|
|
'missed': "black",
|
|
'not assigned': "",
|
|
'not done': "white",
|
|
}
|
|
|
|
def is_session_complete_ws(ws,ps):
|
|
ws = ws.order_by("date")
|
|
if ws.count()==0:
|
|
today = date.today()
|
|
if today > ps.enddate:
|
|
verdict = 'missed'
|
|
ratio = 0
|
|
return ratio,verdict,None
|
|
else:
|
|
return 0,'not done',None
|
|
|
|
value = ps.sessionvalue
|
|
if ps.sessionunit == 'min':
|
|
value *= 60.
|
|
elif ps.sessionunit == 'km':
|
|
value *= 1000.
|
|
|
|
cratiomin = 1
|
|
cratiomax = 1
|
|
|
|
cratios = {
|
|
'better than nothing':0,
|
|
'partial':0.6,
|
|
'on target':0.8,
|
|
'over target':1.2,
|
|
'way over target': 1.5
|
|
}
|
|
|
|
|
|
if ps.criterium == 'none':
|
|
if ps.sessiontype == 'session':
|
|
cratiomin = 0.8
|
|
cratiomax = 1.2
|
|
else:
|
|
cratios['on target'] = 0.9167
|
|
cratios['over target'] = 1.0833
|
|
cratiomin = 0.9167
|
|
cratiomax = 1.0833
|
|
|
|
|
|
score = 0
|
|
completiondate = None
|
|
for w in ws:
|
|
if ps.sessionmode == 'distance':
|
|
score += w.distance
|
|
elif ps.sessionmode == 'time':
|
|
durationseconds = timefield_to_seconds_duration(w.duration)
|
|
score += durationseconds
|
|
elif ps.sessionmode == 'TRIMP':
|
|
trimp,hrtss = dataprep.workout_trimp(w)
|
|
score += trimp
|
|
elif ps.sessionmode == 'rScore':
|
|
rscore = dataprep.workout_rscore(w)[0]
|
|
if not np.isnan(rscore) and rscore != 0:
|
|
score += rscore
|
|
elif rscore == 0:
|
|
trimp,hrtss = dataprep.workout_trimp(w)
|
|
score += hrtss
|
|
|
|
if not completiondate and score>=cratiomin*value:
|
|
completiondate = w.date
|
|
|
|
try:
|
|
ratio = score/float(int(value))
|
|
except ZeroDivisionError:
|
|
ratio = 0
|
|
|
|
verdict = 'better than nothing'
|
|
|
|
if ps.sessiontype in ['session','cycletarget']:
|
|
if ps.criterium == 'exact':
|
|
if ratio == 1.0:
|
|
return ratio,'on target',completiondate
|
|
else:
|
|
if not completiondate:
|
|
completiondate = ws.reverse()[0].date
|
|
return ratio,'partial',completiondate
|
|
elif ps.criterium == 'minimum':
|
|
if ratio >= 1.0:
|
|
return ratio,'on target',completiondate
|
|
else:
|
|
if not completiondate:
|
|
completiondate = ws.reverse()[0].date
|
|
|
|
return ratio,'partial',completiondate
|
|
else:
|
|
thevalue = 0
|
|
for key,value in cratios.items():
|
|
if ratio>value and value>thevalue:
|
|
verdict = key
|
|
thevalue = value
|
|
|
|
completiondate = ws.reverse()[0].date
|
|
return ratio,verdict,completiondate
|
|
elif ps.sessiontype == 'test':
|
|
if ratio==1.0:
|
|
return ratio,'on target',completiondate
|
|
else:
|
|
return ratio,'partial',completiondate
|
|
elif ps.sessiontype == 'challenge':
|
|
if ps.criterium == 'exact':
|
|
if ratio == 1.0:
|
|
return ratio,'on target',completiondate
|
|
else:
|
|
return ratio,'partial',completiondate
|
|
elif ps.criterium == 'minimum':
|
|
if ratio > 1.0:
|
|
return ratio,'on target',completiondate
|
|
else:
|
|
if not completiondate:
|
|
completiondate = ws.reverse()[0].date
|
|
return ratio,'partial',completiondate
|
|
else:
|
|
if not completiondate:
|
|
completiondate = ws.reverse()[0].date
|
|
return ratio,'partial',completiondate
|
|
elif ps.sessiontype == 'race':
|
|
vs = VirtualRaceResult.objects.filter(race=ps)
|
|
wids = [w.id for w in ws]
|
|
for record in vs:
|
|
if record.workoutid in wids:
|
|
if record.coursecompleted:
|
|
ratio = record.distance/ps.sessionvalue
|
|
return ratio,'on target',completiondate
|
|
else:
|
|
ratio = record.distance/ps.sessionvalue
|
|
return ratio,'partial',completiondate
|
|
return (0,'partial',None)
|
|
elif ps.sessiontype == 'coursetest':
|
|
vs = CourseTestResult.objects.filter(plannedsession=ps)
|
|
wids = [w.id for w in ws]
|
|
for record in vs:
|
|
if record.workoutid in wids:
|
|
if record.coursecompleted:
|
|
ratio = record.distance/float(ps.sessionvalue)
|
|
return ratio,'on target',completiondate
|
|
else:
|
|
ratio = record.distance/float(ps.sessionvalue)
|
|
return ratio,'partial',completiondate
|
|
|
|
# we're still here - no record, need to create one
|
|
if ws:
|
|
record = CourseTestResult(
|
|
userid=ws[0].user.id,
|
|
plannedsession=ps,
|
|
workoutid=ws[0].id,
|
|
duration=dt.time(0,0),
|
|
coursecompleted=False,
|
|
)
|
|
record.save()
|
|
job = myqueue(queue,handle_check_race_course,ws[0].csvfilename,
|
|
ws[0].id,ps.course.id,record.id,
|
|
ws[0].user.user.email,ws[0].user.user.first_name,
|
|
mode='coursetest')
|
|
|
|
return (0,'not done',None)
|
|
|
|
|
|
else:
|
|
if not completiondate:
|
|
completiondate = ws.reverse()[0].date
|
|
return ratio,verdict,completiondate
|
|
|
|
|
|
def is_session_complete(r,ps):
|
|
verdict = 'not done'
|
|
|
|
|
|
if r not in ps.rower.all():
|
|
return 0,'not assigned',None
|
|
|
|
ws = Workout.objects.filter(user=r,plannedsession=ps)
|
|
|
|
return is_session_complete_ws(ws,ps)
|
|
|
|
|
|
def rank_results(ps):
|
|
return 1
|
|
|
|
def add_team_session(t,ps):
|
|
ps.team.add(t)
|
|
ps.save()
|
|
|
|
return 1
|
|
|
|
def add_rower_session(r,ps):
|
|
teams = Team.objects.filter(manager=ps.manager)
|
|
members = Rower.objects.filter(team__in=teams).distinct()
|
|
if r in members and r.rowerplan != 'freecoach':
|
|
ps.rower.add(r)
|
|
ps.save()
|
|
|
|
return 1
|
|
elif ps.manager.rower == r and r.rowerplan != 'freecoach':
|
|
ps.rower.add(r)
|
|
ps.save()
|
|
|
|
return 0
|
|
|
|
def remove_team_session(t,ps):
|
|
ps.team.remove(t)
|
|
|
|
return 1
|
|
|
|
def remove_rower_session(r,ps):
|
|
ps.rower.remove(r)
|
|
|
|
return 1
|
|
|
|
def get_team(request):
|
|
teamid = request.GET.get('team')
|
|
return teamid
|
|
|
|
def get_dates_timeperiod(request,startdatestring='',enddatestring='',
|
|
defaulttimeperiod='thisweek'):
|
|
# set start end date according timeperiod
|
|
# should always return datetime.date
|
|
|
|
timeperiod = request.GET.get('when')
|
|
|
|
if not timeperiod:
|
|
timeperiod = defaulttimeperiod
|
|
|
|
if startdatestring == '':
|
|
startdatestring = request.GET.get('startdate')
|
|
if enddatestring == '':
|
|
enddatestring = request.GET.get('enddate')
|
|
|
|
if startdatestring and enddatestring:
|
|
try:
|
|
startdate = dt.datetime.strptime(startdatestring,'%Y-%m-%d').date()
|
|
enddate = dt.datetime.strptime(enddatestring,'%Y-%m-%d').date()
|
|
except ValueError:
|
|
try:
|
|
startdate = parser.parse(startdatestring,fuzzy=True).date()
|
|
enddate = parser.parse(enddatestring, fuzzy=True).date()
|
|
except ValueError:
|
|
startdate = timezone.now()-timezone.timedelta(days=5)
|
|
startdate = startdate.date()
|
|
enddate = timezone.now().date()
|
|
|
|
if startdate>enddate:
|
|
e = startdate
|
|
startdate = enddate
|
|
enddate = e
|
|
|
|
return startdate,enddate
|
|
|
|
daterangetester = re.compile('^(\d+-\d+-\d+)\/(\d+-\d+-\d+)')
|
|
|
|
if timeperiod=='today':
|
|
startdate=date.today()
|
|
enddate=date.today()
|
|
elif timeperiod=='tomorrow':
|
|
startdate=date.today()+timezone.timedelta(days=1)
|
|
enddate=date.today()+timezone.timedelta(days=1)
|
|
elif timeperiod=='thisweek':
|
|
today = date.today()
|
|
startdate = date.today()-timezone.timedelta(days=today.weekday())
|
|
enddate = startdate+timezone.timedelta(days=6)
|
|
elif timeperiod=='thismonth':
|
|
today = date.today()
|
|
startdate = today.replace(day=1)
|
|
enddate = startdate+timezone.timedelta(days=32)
|
|
enddate = enddate.replace(day=1)
|
|
enddate = enddate-timezone.timedelta(days=1)
|
|
elif timeperiod=='lastweek':
|
|
today = date.today()
|
|
enddate = today-timezone.timedelta(days=today.weekday())-timezone.timedelta(days=1)
|
|
startdate = enddate-timezone.timedelta(days=6)
|
|
elif timeperiod=='nextweek':
|
|
today = date.today()
|
|
startdate = today-timezone.timedelta(days=today.weekday())+timezone.timedelta(days=7)
|
|
enddate = startdate+timezone.timedelta(days=6)
|
|
elif timeperiod=='lastmonth':
|
|
today = date.today()
|
|
startdate = today.replace(day=1)
|
|
startdate = startdate-timezone.timedelta(days=3)
|
|
startdate = startdate.replace(day=1)
|
|
enddate = startdate+timezone.timedelta(days=32)
|
|
enddate = enddate.replace(day=1)
|
|
enddate = enddate-timezone.timedelta(days=1)
|
|
elif timeperiod=='nextmonth':
|
|
today = date.today()
|
|
startdate = today.replace(day=1)
|
|
startdate = startdate+timezone.timedelta(days=32)
|
|
startdate = startdate.replace(day=1)
|
|
enddate = startdate+timezone.timedelta(days=32)
|
|
enddate = enddate.replace(day=1)
|
|
enddate = enddate-timezone.timedelta(days=1)
|
|
elif timeperiod=='lastyear':
|
|
today = date.today()
|
|
startdate = today-timezone.timedelta(days=365)
|
|
enddate = today+timezone.timedelta(days=1)
|
|
elif daterangetester.match(timeperiod):
|
|
tstartdatestring = daterangetester.match(timeperiod).group(1)
|
|
tenddatestring = daterangetester.match(timeperiod).group(2)
|
|
try:
|
|
startdate = dt.datetime.strptime(tstartdatestring,'%Y-%m-%d').date()
|
|
enddate = dt.datetime.strptime(tenddatestring,'%Y-%m-%d').date()
|
|
if startdate > enddate:
|
|
startdate2 = enddate
|
|
enddate = startdate
|
|
startdate = startdate2
|
|
except ValueError:
|
|
startdate = date.today()
|
|
enddate = date.today()
|
|
else:
|
|
startdate = date.today()
|
|
enddate = date.today()
|
|
|
|
|
|
if startdatestring != '':
|
|
try:
|
|
startdate = iso8601.parse_date(startdatestring)
|
|
except ParseError:
|
|
pass
|
|
|
|
if enddatestring != '':
|
|
try:
|
|
enddate = iso8601.parse_date(enddatestring)
|
|
except ParseError:
|
|
pass
|
|
|
|
return startdate,enddate
|
|
|
|
def get_sessions_manager(m,teamid=0,startdate=date.today(),
|
|
enddate=date.today()+timezone.timedelta(+1000)):
|
|
if teamid:
|
|
t = Team.objects.get(id=teamid)
|
|
rs = Rower.objects.filter(team__in=[t]).distinct()
|
|
sps = PlannedSession.objects.filter(
|
|
rower__in=rs,
|
|
manager=m,
|
|
startdate__lte=enddate,
|
|
enddate__gte=startdate,
|
|
is_template=False,
|
|
).order_by("preferreddate","startdate","enddate").exclude(
|
|
sessiontype='race').exclude(sessiontype='indoorrace')
|
|
else:
|
|
sps = PlannedSession.objects.filter(
|
|
manager=m,
|
|
startdate__lte=enddate,
|
|
enddate__gte=startdate,
|
|
is_template=False,
|
|
).order_by("preferreddate","startdate","enddate").exclude(
|
|
sessiontype='race').exclude(sessiontype='indoorrace')
|
|
|
|
return sps
|
|
|
|
def get_sessions(r,startdate=date.today(),
|
|
enddate=date.today()+timezone.timedelta(+1000)):
|
|
|
|
sps = PlannedSession.objects.filter(
|
|
rower__in=[r],
|
|
startdate__lte=enddate,
|
|
enddate__gte=startdate,
|
|
is_template=False,
|
|
).order_by("preferreddate","startdate","enddate").exclude(
|
|
sessiontype='race').exclude(sessiontype='indoorrace')
|
|
|
|
return sps
|
|
|
|
def get_my_session_ids(r):
|
|
sps = PlannedSession.objects.filter(
|
|
rower__in=[r]
|
|
).order_by("preferreddate","startdate","enddate").exclude(
|
|
sessiontype='race')
|
|
|
|
return [ps.id for ps in sps]
|
|
|
|
def get_workouts_session(r,ps):
|
|
ws = Workout.objects.filter(user=r,plannedsession=ps)
|
|
|
|
return ws
|
|
|
|
def update_plannedsession(ps,cd):
|
|
for attr, value in cd.items():
|
|
if attr == 'comment':
|
|
value.replace("\r\n", "
");
|
|
value.replace("\n", "
");
|
|
setattr(ps, attr, value)
|
|
|
|
ps.save()
|
|
|
|
return 1,'Planned Session Updated'
|
|
|
|
def update_indoorvirtualrace(ps,cd):
|
|
for attr, value in cd.items():
|
|
if attr == 'comment':
|
|
value = htmlstripnobr(value)
|
|
value.replace("\r\n", "
");
|
|
value.replace("\n", "
");
|
|
setattr(ps, attr, value)
|
|
|
|
timezone_str = cd['timezone']
|
|
|
|
# correct times
|
|
|
|
startdatetime = datetime.combine(cd['startdate'],cd['start_time'])
|
|
enddatetime = datetime.combine(cd['enddate'],cd['end_time'])
|
|
|
|
startdatetime = pytz.timezone(timezone_str).localize(
|
|
startdatetime
|
|
)
|
|
enddatetime = pytz.timezone(timezone_str).localize(
|
|
enddatetime
|
|
)
|
|
ps.evaluation_closure = pytz.timezone(timezone_str).localize(
|
|
ps.evaluation_closure.replace(tzinfo=None)
|
|
)
|
|
|
|
registration_form = cd['registration_form']
|
|
registration_closure = cd['registration_closure']
|
|
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 = ps.evaluation_closure
|
|
|
|
ps.registration_closure = registration_closure
|
|
|
|
ps.timezone = timezone_str
|
|
|
|
ps.save()
|
|
|
|
return 1,'Virtual Race Updated'
|
|
|
|
def update_virtualrace(ps,cd):
|
|
for attr, value in cd.items():
|
|
if attr == 'comment':
|
|
value = htmlstripnobr(value)
|
|
value.replace("\r\n", "
");
|
|
value.replace("\n", "
");
|
|
setattr(ps, attr, value)
|
|
|
|
# correct times
|
|
|
|
course = cd['course']
|
|
geocourse = GeoCourse.objects.get(id= course.id)
|
|
timezone_str = get_course_timezone(geocourse)
|
|
|
|
startdatetime = datetime.combine(cd['startdate'],cd['start_time'])
|
|
enddatetime = datetime.combine(cd['enddate'],cd['end_time'])
|
|
|
|
startdatetime = pytz.timezone(timezone_str).localize(
|
|
startdatetime
|
|
)
|
|
enddatetime = pytz.timezone(timezone_str).localize(
|
|
enddatetime
|
|
)
|
|
ps.evaluation_closure = pytz.timezone(timezone_str).localize(
|
|
ps.evaluation_closure.replace(tzinfo=None)
|
|
)
|
|
|
|
registration_form = cd['registration_form']
|
|
registration_closure = cd['registration_closure']
|
|
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 = ps.evaluation_closure
|
|
|
|
ps.registration_closure = registration_closure
|
|
|
|
ps.timezone = timezone_str
|
|
|
|
ps.save()
|
|
|
|
return 1,'Virtual Race Updated'
|
|
|
|
def race_rower_status(r,race):
|
|
|
|
has_registered = False
|
|
is_complete = False
|
|
|
|
if race.sessiontype == 'race':
|
|
resultobj = VirtualRaceResult
|
|
else:
|
|
resultobj = IndoorVirtualRaceResult
|
|
|
|
vs = resultobj.objects.filter(userid=r.id,race=race)
|
|
if vs:
|
|
has_registered = True
|
|
is_complete = vs[0].coursecompleted
|
|
|
|
return is_complete,has_registered
|
|
|
|
def race_can_edit(r,race):
|
|
if r.user != race.manager:
|
|
return False
|
|
else:
|
|
start_time = race.start_time
|
|
start_date = race.startdate
|
|
startdatetime = datetime.combine(start_date,start_time)
|
|
startdatetime = pytz.timezone(race.timezone).localize(
|
|
startdatetime
|
|
)
|
|
end_time = race.end_time
|
|
end_date = race.enddate
|
|
enddatetime = datetime.combine(end_date,end_time)
|
|
enddatetime = pytz.timezone(race.timezone).localize(
|
|
enddatetime
|
|
)
|
|
if timezone.now()<enddatetime:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
|
|
return False
|
|
|
|
def race_can_submit(r,race):
|
|
#if r not in race.rower.all():
|
|
# return False
|
|
|
|
start_time = race.start_time
|
|
start_date = race.startdate
|
|
startdatetime = datetime.combine(start_date,start_time)
|
|
startdatetime = pytz.timezone(race.timezone).localize(
|
|
startdatetime
|
|
)
|
|
evaluation_closure = race.evaluation_closure
|
|
|
|
|
|
if timezone.now() > startdatetime and timezone.now() < evaluation_closure:
|
|
is_complete,has_registered = race_rower_status(r,race)
|
|
if is_complete == False:
|
|
return True
|
|
else:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
return False
|
|
|
|
def race_can_resubmit(r,race):
|
|
records = VirtualRaceResult.objects.filter(
|
|
userid=r.id,
|
|
race=race)
|
|
|
|
|
|
if not records:
|
|
return False
|
|
|
|
start_time = race.start_time
|
|
start_date = race.startdate
|
|
startdatetime = datetime.combine(start_date,start_time)
|
|
startdatetime = pytz.timezone(race.timezone).localize(
|
|
startdatetime
|
|
)
|
|
evaluation_closure = race.evaluation_closure
|
|
|
|
|
|
|
|
if timezone.now() > startdatetime and timezone.now() < evaluation_closure:
|
|
is_complete,has_registered = race_rower_status(r,race)
|
|
return is_complete
|
|
else:
|
|
return False
|
|
|
|
return False
|
|
|
|
def race_can_adddiscipline(r,race):
|
|
|
|
if race.sessiontype != 'race':
|
|
return False
|
|
|
|
records = VirtualRaceResult.objects.filter(
|
|
userid=r.id,
|
|
race=race)
|
|
|
|
if not records:
|
|
return False
|
|
|
|
|
|
start_time = race.start_time
|
|
start_date = race.startdate
|
|
startdatetime = datetime.combine(start_date,start_time)
|
|
startdatetime = pytz.timezone(race.timezone).localize(
|
|
startdatetime
|
|
)
|
|
evaluation_closure = race.evaluation_closure
|
|
|
|
|
|
if timezone.now() < evaluation_closure:
|
|
is_complete,has_registered = race_rower_status(r,race)
|
|
if has_registered:
|
|
return True
|
|
else:
|
|
return False
|
|
else:
|
|
return False
|
|
|
|
return False
|
|
|
|
|
|
def race_can_withdraw(r,race):
|
|
if race.sessiontype == 'race':
|
|
recordobj = VirtualRaceResult
|
|
else:
|
|
recordobj = IndoorVirtualRaceResult
|
|
|
|
records = recordobj.objects.filter(
|
|
userid=r.id,
|
|
race=race
|
|
)
|
|
|
|
|
|
|
|
if not records:
|
|
return False
|
|
|
|
start_time = race.start_time
|
|
start_date = race.startdate
|
|
startdatetime = datetime.combine(start_date,start_time)
|
|
startdatetime = pytz.timezone(race.timezone).localize(
|
|
startdatetime
|
|
)
|
|
|
|
registration_closure = race.registration_closure
|
|
if registration_closure is None or registration_closure == '':
|
|
registration_closure = startdatetime
|
|
|
|
if timezone.now() > registration_closure:
|
|
return False
|
|
elif timezone.now() > startdatetime:
|
|
return False
|
|
|
|
|
|
return True
|
|
|
|
def email_submit_race(r,race,workoutid):
|
|
try:
|
|
w = Workout.objects.get(id=workoutid)
|
|
except Workout.DoesNotExist:
|
|
return 0
|
|
|
|
if race.sessionmode == 'time':
|
|
wduration = timefield_to_seconds_duration(w.duration)
|
|
delta = wduration - (60.*race.sessionvalue)
|
|
|
|
if delta > -2 and delta < 2:
|
|
w.duration = totaltime_sec_to_string(60.*race.sessionvalue)
|
|
w.save()
|
|
|
|
|
|
elif race.sessionmode == 'distance':
|
|
delta = w.distance - race.sessionvalue
|
|
|
|
if delta > -5 and delta < 5:
|
|
w.distance = race.sessionvalue
|
|
w.save()
|
|
|
|
|
|
if race_can_register(r,race):
|
|
teamname = ''
|
|
weightcategory = w.weightcategory
|
|
sex = r.sex
|
|
if sex == 'not specified':
|
|
sex = 'male'
|
|
|
|
if not r.birthdate:
|
|
return 0
|
|
|
|
age = calculate_age(r.birthdate)
|
|
|
|
adaptiveclass = r.adaptiveclass
|
|
boatclass = w.workouttype
|
|
|
|
record = IndoorVirtualRaceResult(
|
|
userid = r.id,
|
|
teamname=teamname,
|
|
race=race,
|
|
username = u'{f} {l}'.format(
|
|
f = r.user.first_name,
|
|
l = r.user.last_name
|
|
),
|
|
weightcategory=weightcategory,
|
|
adaptiveclass=adaptiveclass,
|
|
duration=dt.time(0,0),
|
|
boatclass=boatclass,
|
|
coursecompleted=False,
|
|
sex=sex,
|
|
age=age
|
|
)
|
|
|
|
record.save()
|
|
|
|
result = add_rower_race(r,race)
|
|
|
|
otherrecords = IndoorVirtualRaceResult.objects.filter(
|
|
race = race)
|
|
|
|
|
|
for otherrecord in otherrecords:
|
|
otheruser = Rower.objects.get(id=otherrecord.userid)
|
|
othername = otheruser.user.first_name+' '+otheruser.user.last_name
|
|
registeredname = r.user.first_name+' '+r.user.last_name
|
|
if otherrecord.emailnotifications:
|
|
job = myqueue(
|
|
queue,
|
|
handle_sendemail_raceregistration,
|
|
otheruser.user.email, othername,
|
|
registeredname,
|
|
race.name,
|
|
race.id
|
|
)
|
|
|
|
|
|
if race_can_submit(r,race):
|
|
records = IndoorVirtualRaceResult.objects.filter(
|
|
userid = r.id,
|
|
race=race
|
|
)
|
|
|
|
if not records:
|
|
return 0
|
|
|
|
record = records[0]
|
|
|
|
workouts = Workout.objects.filter(id=w.id)
|
|
|
|
result,comments,errors,jobid = add_workout_indoorrace(
|
|
workouts,race,r,recordid=record.id
|
|
)
|
|
|
|
|
|
if result:
|
|
otherrecords = IndoorVirtualRaceResult.objects.filter(
|
|
race = race)
|
|
|
|
for otherrecord in otherrecords:
|
|
otheruser = Rower.objects.get(id=otherrecord.userid)
|
|
othername = otheruser.user.first_name+' '+otheruser.user.last_name
|
|
registeredname = r.user.first_name+' '+r.user.last_name
|
|
if otherrecord.emailnotifications:
|
|
job = myqueue(
|
|
queue,
|
|
handle_sendemail_racesubmission,
|
|
otheruser.user.email, othername,
|
|
registeredname,
|
|
race.name,
|
|
race.id
|
|
)
|
|
|
|
return 1
|
|
else:
|
|
return 0
|
|
else:
|
|
|
|
return 0
|
|
|
|
return 0
|
|
|
|
|
|
def race_can_register(r,race):
|
|
if race.sessiontype == 'race':
|
|
recordobj = VirtualRaceResult
|
|
else:
|
|
recordobj = IndoorVirtualRaceResult
|
|
|
|
records = recordobj.objects.filter(
|
|
userid=r.id,
|
|
race=race)
|
|
|
|
if records:
|
|
return False
|
|
|
|
start_time = race.start_time
|
|
start_date = race.startdate
|
|
startdatetime = datetime.combine(start_date,start_time)
|
|
startdatetime = pytz.timezone(race.timezone).localize(
|
|
startdatetime
|
|
)
|
|
|
|
registration_closure = race.registration_closure
|
|
if registration_closure is None or registration_closure == '':
|
|
registration_closure = startdatetime
|
|
|
|
if timezone.now() > registration_closure:
|
|
return False
|
|
|
|
return True
|
|
|
|
def add_rower_race(r,race):
|
|
race.rower.add(r)
|
|
race.save()
|
|
|
|
return 1
|
|
|
|
def remove_rower_race(r,race,recordid=None):
|
|
race.rower.remove(r)
|
|
|
|
if race.sessiontype == 'race':
|
|
recordobj = VirtualRaceResult
|
|
else:
|
|
recordobj = IndoorVirtualRaceResult
|
|
|
|
if recordid:
|
|
records = recordobj.objects.filter(userid=r.id,
|
|
workoutid__isnull=True,
|
|
race=race,
|
|
id=recordid)
|
|
else:
|
|
records = recordobj.objects.filter(userid=r.id,
|
|
workoutid__isnull=True,
|
|
race=race,)
|
|
for r in records:
|
|
r.delete()
|
|
|
|
return 1
|
|
|
|
def default_class(r,w,race):
|
|
if r.birthdate:
|
|
age = calculate_age(r.birthdate)
|
|
else:
|
|
age = 25
|
|
|
|
sex = r.sex
|
|
if sex=='not specified':
|
|
sex='male'
|
|
|
|
if w is not None:
|
|
boatclass = w.workouttype
|
|
boattype = w.boattype
|
|
|
|
adaptiveclass = w.adaptiveclass
|
|
weightclass = w.weightcategory
|
|
else:
|
|
if race.sessiontype == 'race':
|
|
boatclass = 'water'
|
|
else:
|
|
boatclass = 'rower'
|
|
boattype = '1x'
|
|
adaptiveclass = 'None'
|
|
weightclass = 'hwt'
|
|
|
|
if race.coursestandards:
|
|
standards = CourseStandard.objects.filter(
|
|
agemin__lt=age,agemax__gt=age,
|
|
boatclass=boatclass,
|
|
adaptiveclass=adaptiveclass,
|
|
boattype=boattype,
|
|
weightclass=weightclass,
|
|
sex=sex,
|
|
).order_by("agemax","-agemin","boattype","sex")
|
|
|
|
|
|
if standards.count()==0:
|
|
# omit weight
|
|
standards = CourseStandard.objects.filter(
|
|
agemin__lt=age,agemax__gt=age,
|
|
boatclass=boatclass,
|
|
adaptiveclass=adaptiveclass,
|
|
boattype=boattype,
|
|
).order_by(
|
|
"agemax","-agemin","boattype","sex","weightclass",
|
|
"referencespeed"
|
|
)
|
|
if standards.count()==0:
|
|
standards = CourseStandard.objects.filter(
|
|
agemin__lt=age,agemax__gt=age,
|
|
boattype=boattype
|
|
).order_by(
|
|
"agemax","-agemin","boattype","sex",
|
|
"weightclass","referencespeed")
|
|
if standards.count()==0:
|
|
standards = CourseStandard.objects.filter(
|
|
agemin__lt=age,agemax__gt=age
|
|
).order_by(
|
|
"agemax","-agemin","boattype","sex",
|
|
"weightclass","referencespeed")
|
|
|
|
if standards.count()==0:
|
|
# boolean, boattype, boatclass, adaptiveclass, weightclass, sex, coursestandard,
|
|
return False,'1x','water',None,'hwt','male',None
|
|
|
|
if standards.count()>0:
|
|
# find optimum standard
|
|
s = standards[0]
|
|
return True,s.boattype,s.boatclass,s.adaptiveclass,s.weightclass,s.sex,s
|
|
|
|
# No Course Standard
|
|
return True,boattype,boatclass,adaptiveclass,weightclass,sex,None
|
|
|
|
|
|
|
|
# Low Level functions - to be called by higher level methods
|
|
def add_workout_indoorrace(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
|
|
)
|
|
|
|
# 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,0
|
|
|
|
|
|
if len(ws)>1 and race.sessiontype == 'test':
|
|
errors.append('For tests, you can only attach one workout')
|
|
return result,comments,errors,0
|
|
|
|
|
|
|
|
ids = [w.id for w in ws]
|
|
ids = list(set(ids))
|
|
|
|
if len(ids)>1 and race.sessiontype in ['test','coursetest','race','indoorrace']:
|
|
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,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,
|
|
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 race.sessionmode == 'distance':
|
|
if ws[0].distance != race.sessionvalue:
|
|
errors.append('Your workout did not have the correct distance')
|
|
return 0,comments, errors, 0
|
|
else:
|
|
record.distance = ws[0].distance
|
|
record.duration = ws[0].duration
|
|
else:
|
|
t = ws[0].duration
|
|
seconds = t.second+t.minute*60.+t.hour*3600.+t.microsecond/1.e6
|
|
if seconds != race.sessionvalue*60.:
|
|
errors.append('Your workout did not have the correct duration')
|
|
return 0, comments, errors, 0
|
|
else:
|
|
record.distance = ws[0].distance
|
|
record.duration = ws[0].duration
|
|
|
|
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.otetypes:
|
|
errors.append('You must submit a indoor rowing workout')
|
|
return 0,comments, errors, 0
|
|
|
|
if 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()
|
|
|
|
record.coursecompleted = True
|
|
record.workoutid = ws[0].id
|
|
|
|
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()
|
|
|
|
add_workouts_plannedsession(ws,race,r)
|
|
|
|
|
|
return result,comments,errors,0
|
|
|
|
|
|
def add_workout_race(ws,race,r,splitsecond=0,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
|
|
)
|
|
|
|
# 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,0
|
|
|
|
if len(ws)>1 and race.sessiontype == 'test':
|
|
errors.append('For tests, you can only attach one workout')
|
|
return result,comments,errors,0
|
|
|
|
|
|
|
|
ids = [w.id for w in ws]
|
|
ids = list(set(ids))
|
|
|
|
if len(ids)>1 and race.sessiontype in ['test','coursetest','race']:
|
|
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 = VirtualRaceResult.objects.get(
|
|
userid=r.id,
|
|
race=race,
|
|
id=recordid
|
|
)
|
|
except VirtualRaceResult.DoesNotExist:
|
|
if doregister:
|
|
hasinitial,boattype,boatclass,adaptiveclass,weightclass,sex,initialcategory = default_class(r,ws[0],race)
|
|
if hasinitial:
|
|
record = VirtualRaceResult(
|
|
userid = r.id,
|
|
username = r.user.first_name+' '+r.user.last_name,
|
|
weightcategory=weightclass,
|
|
adaptiveclass=adaptiveclass,
|
|
race=race,
|
|
boatclass=boatclass,
|
|
boattype=boattype,
|
|
sex=sex,
|
|
age = age,
|
|
entrycategory=initialcategory,
|
|
)
|
|
record.save()
|
|
add_rower_race(r,race)
|
|
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 = VirtualRaceResult.objects.filter(
|
|
userid=r.id,
|
|
race=race,
|
|
workoutid = ws[0].id
|
|
)
|
|
|
|
if not record and not doregister:
|
|
errors.append("Couldn't find this entry")
|
|
return result,comments,errors,0
|
|
|
|
if ws[0].workouttype not in mytypes.otwtypes:
|
|
errors.append('You have to submit a rowing on water workout')
|
|
return 0,comments,errors,0
|
|
|
|
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].boattype != record.boattype:
|
|
errors.append('Your workout boat type did not match the boat type you registered')
|
|
return 0,comments,errors,0
|
|
|
|
if 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 workout 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:
|
|
# convert to gps
|
|
row = rdata(ws[0].csvfilename)
|
|
success = row.use_gpsdata()
|
|
if success:
|
|
row.write_csv(ws[0].csvfilename)
|
|
dataprep.update_strokedata(ws[0].id,row.df)
|
|
ws[0].impeller = False
|
|
|
|
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()
|
|
|
|
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.')
|
|
|
|
job = myqueue(queue,handle_check_race_course,ws[0].csvfilename,
|
|
ws[0].id,race.course.id,record.id,
|
|
ws[0].user.user.email,ws[0].user.user.first_name,
|
|
splitsecond=splitsecond,
|
|
referencespeed=record.referencespeed,coursedistance=race.course.distance
|
|
)
|
|
|
|
comments.append('We are now checking adherence to the race course. This may take a few minutes to complete')
|
|
|
|
|
|
|
|
add_workouts_plannedsession(ws,race,r)
|
|
|
|
|
|
return result,comments,errors,job.id
|
|
|
|
def delete_race_result(workout,race):
|
|
results = VirtualRaceResult.objects.filter(workoutid=workout.id,race=race)
|
|
for r in results:
|
|
r.workoutid = None
|
|
r.save()
|