Private
Public Access
1
0
Files
rowsandall/rowers/plannedsessions.py
2020-01-04 10:59:50 +01:00

1553 lines
47 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 rowers.models import (
Rower, Workout,Team,
GeoCourse, TrainingMicroCycle,TrainingMesoCycle,TrainingMacroCycle,
TrainingPlan,PlannedSession,VirtualRaceResult,CourseTestResult,
get_course_timezone, IndoorVirtualRaceResult,VirtualRace,createmacrofillers,
createmesofillers,createmicrofillers,
)
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,
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,
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
startdatestring = request.GET.get('startdate')
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:
startdate = parser.parse(startdatestring,fuzzy=True).date()
enddate = parser.parse(enddatestring, fuzzy=True).date()
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", "&#10");
value.replace("\n", "&#10");
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", "&#10");
value.replace("\n", "&#10");
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", "&#10");
value.replace("\n", "&#10");
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
)
if timezone.now()<startdatetime:
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
# Low Level functions - to be called by higher level methods
def add_workout_indoorrace(ws,race,r,recordid=0):
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 ws.count()>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
record = IndoorVirtualRaceResult.objects.get(
userid=r.id,
race=race,
id=recordid
)
records = IndoorVirtualRaceResult.objects.filter(
userid=r.id,
race=race,
workoutid = ws[0].id
)
if not record:
errors.append("Couldn't find this entry")
return result,comments,errors,0
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):
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 ws.count()>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
record = VirtualRaceResult.objects.get(
userid=r.id,
race=race,
id=recordid
)
records = VirtualRaceResult.objects.filter(
userid=r.id,
race=race,
workoutid = ws[0].id
)
if not record:
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:
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,splitsecond=splitsecond)
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()