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') from rowers.models import ( Rower, Workout,Team, GeoCourse, TrainingMicroCycle,TrainingMesoCycle,TrainingMacroCycle, TrainingPlan,PlannedSession,VirtualRaceResult,CourseTestResult, get_course_timezone, IndoorVirtualRaceResult,VirtualRace ) 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 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 len(ws) != 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 len(ws)==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: ps.rower.add(r) ps.save() return 1 elif ps.manager.rower == r: 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_dates_timeperiod(request,startdatestring='',enddatestring='', defaulttimeperiod='thisweek'): # set start end date according timeperiod 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) enddate = parser.parse(enddatestring, fuzzy=True) 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, ).order_by("preferreddate","startdate","enddate").exclude( sessiontype='race').exclude(sessiontype='indoorrace') else: sps = PlannedSession.objects.filter( manager=m, startdate__lte=enddate, enddate__gte=startdate, ).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, ).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 ) 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 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 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 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 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()