from rowers.emails import htmlstrip, htmlstripnobr 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.tasks import ( handle_sendemail_raceregistration, handle_sendemail_racesubmission ) from rowers.tasks import handle_check_race_course from iso8601 import ParseError import iso8601 import rowers.courses as courses import rowers.datautils as datautils import rowers.dataprep as dataprep import numpy as np import rowers.metrics as metrics import rowers.mytypes as mytypes from rowers.utils import to_pace from rowers.opaque import encoder from rowingdata import rower as rrower from rowingdata import rowingdata as rrdata import arrow import polars as pl import json # 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, ps_dict_order from rowers.rows import handle_uploaded_file import collections import re import django_rq queue = django_rq.get_queue('default') queuelow = django_rq.get_queue('low') queuehigh = django_rq.get_queue('low') def to_time(milliseconds): seconds = milliseconds/1000. hours = int(seconds / 3600) mins = int((seconds % 3600)/60) sec = int((seconds % 3600) % 60) microsec = int(1e6*(seconds % 1)) # print(seconds,hours,mins,sec,millisec) return dt.time(hours, mins, sec, microsec) # 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): # pragma: no cover try: res = rrdata(csvfile=file+'.gz', rower=rower) except (IOError, IndexError, EOFError, FileNotFoundError): res = 0 return res def ps_dict_get_description(d, short=False): # pragma: no cover sdict, totalmeters, totalseconds, totalrscore = ps_dict_order( d, short=short, html=False) s = '' for item in sdict: s += item['string']+'\n' return s def ps_dict_get_description_html(d, short=False): sdict, totalmeters, totalseconds, totalrscore = ps_dict_order( d, short=short) s = '' return s 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: # pragma: no cover 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: # pragma: no cover plans = TrainingPlan.objects.filter(rowers__in=[rower]) if not plans: micros = [] else: sorted_plans = sorted(plans, key= lambda t: t.overlap(startdate,enddate)) plan = plans.reverse()[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") if len(micros)==0: 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) # 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: row = { 'rscore': w.rscore, 'hrtss': w.hrtss, 'rpe': w.rpe, 'duration': w.duration, 'id': w.id } rscore = dataprep.rscore_approx(row) actualscore += rscore for ps in sps: # pragma: no cover 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 = pl.DataFrame({ 'startdate': startdates, 'planned': planned, 'executed': executed, }) return(data, 'ok') def get_indoorraces(workout): if workout is None: return [] races1 = VirtualRace.objects.filter( sessiontype='indoorrace', startdate__lte=workout.date, enddate__gte=workout.date, sessionmode='distance', sessionvalue=workout.distance) if workout.duration is None: workout.duration = timedelta(0) 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=timezone.now()): 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] else: # pragma: no cover return None 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']: # pragma: no cover 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': # pragma: no cover 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', 'fastest_time', 'fastest_distance']: # pragma: no cover 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 %s to session' % encoder.encode_hex(w.id)) if ps.sessiontype == 'coursetest': # pragma: no cover record = CourseTestResult( userid=w.user.id, plannedsession=ps, duration=dt.time(0, 0), coursecompleted=False, ) record.save() _ = 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') if ps.sessiontype == 'fastest_distance': # pragma: no cover records = CourseTestResult.objects.filter( userid=w.user.id, plannedsession=ps) for record in records: record.delete() df = dataprep.read_data( ['time', 'cumdist'], ids=[w.id]) df = dataprep.remove_nulls_pl(df) fastest_milliseconds, starttime, endtime = datautils.getfastest( df, ps.sessionvalue, mode='distance') if fastest_milliseconds > 0: w.plannedsession = ps w.save() duration = to_time(1000.*fastest_milliseconds) record = CourseTestResult( userid=w.user.user.id, plannedsession=ps, duration=duration, coursecompleted=True, workoutid=w.id, distance=ps.sessionvalue, startsecond=starttime, endsecond=endtime, ) record.save() else: errors.append('Could not find a matching interval') if ps.sessiontype == 'fastest_time': # pragma: no cover records = CourseTestResult.objects.filter( userid=w.user.id, plannedsession=ps) for record in records: record.delete() df = dataprep.read_data( ['time', 'cumdist'], ids=[w.id]) df = dataprep.remove_nulls_pl(df) fastest_meters, starttime, endtime = datautils.getfastest( df, ps.sessionvalue, mode='time') if fastest_meters > 0: w.plannedsession = ps w.save() duration = dt.time(0, ps.sessionvalue) record = CourseTestResult( userid=w.user.user.id, workoutid=w.id, plannedsession=ps, duration=duration, coursecompleted=True, distance=fastest_meters, startsecond=starttime, endsecond=endtime, ) record.save() else: errors.append('Could not find a matching interval') else: # pragma: no cover 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): # pragma: no cover 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): # pragma: no cover 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 row = { 'rscore': w.rscore, 'hrtss': w.hrtss, 'rpe': w.rpe, 'duration': w.duration, 'id': w.id } tss = dataprep.rscore_approx(row) rscorev += tss ratio, statusv, completiondate = is_session_complete_ws(ws, ps) try: completedatev = completiondate.strftime('%Y-%m-%d') except AttributeError: # pragma: no cover 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 = timezone.now() if today.date() > 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': # pragma: no cover 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': row = { 'rscore': w.rscore, 'hrtss': w.hrtss, 'rpe': w.rpe, 'duration': w.duration, 'id': w.id } rscore = dataprep.rscore_approx(row) score += rscore if not completiondate and score >= cratiomin*value: completiondate = w.date try: ratio = score/float(int(value)) except ZeroDivisionError: # pragma: no cover 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: # pragma: no cover completiondate = ws.reverse()[0].date return ratio, 'partial', completiondate elif ps.criterium == 'minimum': # pragma: no cover 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': # pragma: no cover 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: # pragma: no cover completiondate = ws.reverse()[0].date return ratio, 'partial', completiondate elif ps.sessiontype == 'race': # pragma: no cover 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 in ['fastest_time', 'fastest_distance']: # pragma: no cover vs = CourseTestResult.objects.filter( plannedsession=ps, userid=ws[0].user.user.id) completiondate = ws.reverse()[0].date wids = [w.id for w in ws] for record in vs: if record.workoutid in wids: if record.coursecompleted: ratio = 1 return ratio, 'on target', completiondate else: return 0, 'partial', completiondate if ws: record = CourseTestResult( userid=ws[0].user.id, plannedsession=ps, workoutid=ws[0].id, duration=dt.time(0, 0), coursecompleted=False ) record.save() return (0, 'not done', None) elif ps.sessiontype == 'coursetest': # pragma: no cover 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() _ = 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: # pragma: no cover if not completiondate: completiondate = ws.reverse()[0].date return ratio, verdict, completiondate def is_session_complete(r, ps): if r not in ps.rower.all(): # pragma: no cover return 0, 'not assigned', None ws = Workout.objects.filter(user=r, plannedsession=ps) return is_session_complete_ws(ws, ps) def rank_results(ps): # pragma: no cover 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): # pragma: no cover 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', rower=None): # 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: # pragma: no cover startdate = timezone.now()-timezone.timedelta(days=5) startdate = startdate.date() enddate = timezone.now().date() if startdate > enddate: e = startdate startdate = enddate enddate = e if rower is not None: tz = pytz.timezone(rower.defaulttimezone) startdate = arrow.get(startdate) enddate = arrow.get(enddate) startdate = startdate.replace(tzinfo=tz) enddate = enddate.replace(tzinfo=tz) else: startdate = dt.datetime.combine(startdate, dt.datetime.min.time()) enddate = dt.datetime.combine(enddate, dt.datetime.min.time()) startdate = startdate.astimezone(pytz.utc) enddate = enddate.astimezone(pytz.utc) # set time to 00:00 in local time startdate = startdate.replace( hour=0, minute=0, second=0, microsecond=0) enddate = enddate.replace(hour=0, minute=0, second=0, microsecond=0) return startdate, enddate daterangetester = re.compile('^(\d+-\d+-\d+)\/(\d+-\d+-\d+)') if timeperiod == 'today': # pragma: no cover startdate = timezone.now() enddate = timezone.now() elif timeperiod == 'last30': startdate = timezone.now()-timezone.timedelta(days=30) enddate = timezone.now()+timezone.timedelta(days=1) elif timeperiod == 'tomorrow': # pragma: no cover startdate = timezone.now()+timezone.timedelta(days=1) enddate = timezone.now()+timezone.timedelta(days=1) elif timeperiod == 'thisweek': # pragma: no cover today = timezone.now() startdate = timezone.now()-timezone.timedelta(days=today.weekday()) enddate = startdate+timezone.timedelta(days=6) elif timeperiod == 'thismonth': # pragma: no cover today = timezone.now() 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': # pragma: no cover today = timezone.now() enddate = today - \ timezone.timedelta(days=today.weekday())-timezone.timedelta(days=1) startdate = enddate-timezone.timedelta(days=6) elif timeperiod == 'nextweek': # pragma: no cover today = timezone.now() startdate = today - \ timezone.timedelta(days=today.weekday())+timezone.timedelta(days=7) enddate = startdate+timezone.timedelta(days=6) elif timeperiod == 'lastmonth': # pragma: no cover today = timezone.now() 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': # pragma: no cover today = timezone.now() 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': # pragma: no cover today = timezone.now() 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() startdate = dt.datetime.combine(startdate, dt.datetime.min.time()) enddate = dt.datetime.combine(enddate, dt.datetime.min.time()) if startdate > enddate: # pragma: no cover startdate2 = enddate enddate = startdate startdate = startdate2 except ValueError: # pragma: no cover startdate = timezone.now() enddate = timezone.now() else: startdate = timezone.now() enddate = timezone.now() if startdatestring != '': try: startdate = iso8601.parse_date(startdatestring) except ParseError: pass if enddatestring != '': try: enddate = iso8601.parse_date(enddatestring) except ParseError: pass if rower is not None: startdate = arrow.get(startdate) enddate = arrow.get(enddate) tz = pytz.timezone(rower.defaulttimezone) startdate = startdate.astimezone(tz) enddate = enddate.astimezone(tz) # set time to 00:00 in local time startdate = startdate.replace(hour=0, minute=0, second=0, microsecond=0) enddate = enddate.replace(hour=0, minute=0, second=0, microsecond=0) return startdate, enddate def get_sessions_manager(m, teamid=0, startdate=timezone.now(), enddate=timezone.now()+timezone.timedelta(+1000)): if teamid: # pragma: no cover 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=timezone.now(), enddate=timezone.now()+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 create_sessions_from_json(plansteps, rower, startdate, manager, planbyrscore=False): trainingdays = plansteps['trainingDays'] planstartdate = startdate for day in trainingdays: for workout in day['workouts']: sessionsport = 'water' try: sessionsport = mytypes.fitmappinginv[workout['sport'].lower()] except KeyError: pass preferreddate = planstartdate+timedelta(days=day['order']) sessionmode = 'time' if planbyrscore: sessionmode = 'rScore' ps = PlannedSession( startdate=preferreddate - timedelta(days=preferreddate.weekday()), enddate=preferreddate + timedelta(days=-preferreddate.weekday()-1, weeks=1), preferreddate=preferreddate, sessionsport=sessionsport, # change this name=workout['workoutName'], steps=workout, manager=manager, sessionmode=sessionmode, comment=workout['description'] ) ps.save() add_rower_session(rower, ps) def update_plannedsession(ps, cd): for attr, value in cd.items(): if attr == 'comment': value.replace("\r\n", " ") value.replace("\n", " ") if attr != 'fitfile': setattr(ps, attr, value) if cd['fitfile']: # pragma: no cover f = cd['fitfile'] try: filename, path_and_filename = handle_uploaded_file(f) ps.fitfile.name = filename ps.steps = {} except FileNotFoundError: pass 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': # pragma: no cover try: registration_closure = pytz.timezone( timezone_str ).localize( registration_closure.replace(tzinfo=None) ) except AttributeError: registration_closure = startdatetime elif registration_form == 'windowstart': # pragma: no cover registration_closure = startdatetime elif registration_form == 'windowend': # pragma: no cover registration_closure = enddatetime else: registration_closure = ps.evaluation_closure ps.registration_closure = registration_closure ps.timezone = timezone_str if ps.sessiontype == 'fastest_distance': # pragma: no cover ps.approximate_distance = ps.sessionvalue if ps.course is not None: # pragma: no cover ps.approximate_distance = ps.course.distance 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': # pragma: no cover try: registration_closure = pytz.timezone( timezone_str ).localize( registration_closure.replace(tzinfo=None) ) except AttributeError: registration_closure = startdatetime elif registration_form == 'windowstart': # pragma: no cover registration_closure = startdatetime elif registration_form == 'windowend': # pragma: no cover registration_closure = enddatetime else: registration_closure = ps.evaluation_closure ps.registration_closure = registration_closure ps.timezone = timezone_str if ps.sessiontype == 'fastest_distance': # pragma: no cover ps.approximate_distance = ps.sessionvalue if ps.course is not None: ps.approximate_distance = ps.course.distance ps.save() return 1, 'Virtual Race Updated' def race_rower_status(r, race): has_registered = False is_complete = False if race.sessiontype in ['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: # pragma: no cover return False return False # pragma: no cover 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 is False: return True else: return True else: return False return False # pragma: no cover def race_can_editentry(r, race): 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 is_complete is False: return True else: # pragma: no cover return False else: # pragma: no cover return False return False # pragma: no cover 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: # pragma: no cover return False return False # pragma: no cover def race_can_adddiscipline(r, race): if race.sessiontype not in ['race', 'fastest_time', 'fastest_distance']: return False if race.sessiontype in ['race']: resultobj = VirtualRaceResult else: # pragma: no cover resultobj = IndoorVirtualRaceResult records = resultobj.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: # pragma: no cover return False else: # pragma: no cover return False return False # pragma: no cover def race_can_withdraw(r, race): if race.sessiontype in ['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 == '': # pragma: no cover registration_closure = startdatetime if timezone.now() > registration_closure: # pragma: no cover return False elif timezone.now() > startdatetime: return False return True def race_can_register(r, race): if race.sessiontype in ['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 == '': # pragma: no cover registration_closure = startdatetime if timezone.now() > registration_closure: # pragma: no cover 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 in ['race']: # pragma: no cover recordobj = VirtualRaceResult else: recordobj = IndoorVirtualRaceResult if recordid: # pragma: no cover 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: # pragma: no cover age = 25 sex = r.sex if sex == 'not specified': sex = 'male' if w is not None: # pragma: no cover boatclass = w.workouttype boattype = w.boattype adaptiveclass = w.adaptiveclass weightclass = w.weightcategory else: if race.sessiontype in ['race', 'fastest_time', 'fastest_distance']: boatclass = 'water' else: # pragma: no cover 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, standardcollection=race.coursestandards, ).order_by("agemax", "-agemin", "boattype", "sex") if standards.count() == 0: # pragma: no cover # omit weight standards = CourseStandard.objects.filter( agemin__lt=age, agemax__gt=age, boatclass=boatclass, adaptiveclass=adaptiveclass, boattype=boattype, sex=sex, standardcollection=race.coursestandards, ).order_by( "agemax", "-agemin", "boattype", "sex", "weightclass", "referencespeed" ) if standards.count() == 0: # omit adaptive class standards = CourseStandard.objects.filter( agemin__lt=age, agemax__gt=age, boattype=boattype, sex=sex, standardcollection=race.coursestandards, ).order_by( "agemax", "-agemin", "boattype", "sex", "weightclass", "referencespeed") if standards.count() == 0: # omit boattype standards = CourseStandard.objects.filter( agemin__lt=age, agemax__gt=age, sex=sex, standardcollection=race.coursestandards, ).order_by( "agemax", "-agemin", "boattype", "sex", "weightclass", "referencespeed") if standards.count() == 0: # omit boattype standards = CourseStandard.objects.filter( agemin__lt=age, agemax__gt=age, sex='male', standardcollection=race.coursestandards ).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', 5.0, None if standards.count() > 0: # find optimum standard s = standards[0] return True, s.boattype, s.boatclass, s.adaptiveclass, s.weightclass, s.sex, s.referencespeed, s # No Course Standard return True, boattype, boatclass, adaptiveclass, weightclass, sex, 5.0, None def add_workout_fastestrace(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 ) ids = [w.id for w in ws] ids = list(set(ids)) if len(ids) > 1 and race.sessiontype in ['test', 'coursetest', 'race', 'indoorrace', 'fastest_time', 'fastest_distance']: # pragma: no cover errors.append('For tests, you can only attach one workout') return result, comments, errors, 0 if r.birthdate: age = calculate_age(r.birthdate) else: # pragma: no cover age = None try: record = IndoorVirtualRaceResult.objects.get( userid=r.id, race=race, id=recordid ) except IndoorVirtualRaceResult.DoesNotExist: # pragma: no cover if doregister: hasinitial, boattype, boatclass, adaptiveclass, weightclass, sex, referencespeed, 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, referencespeed=referencespeed, entrycategory=initialcategory, ) record.save() else: errors.append("Unable to find a suitable start category") return result, comments, errors, 0 else: # pragma: no cover errors.append("Couldn't find this entry") return result, comments, errors, 0 records = IndoorVirtualRaceResult.objects.filter( userid=r.id, race=race, ) if ws[0].workouttype != record.boatclass: # pragma: no cover 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.otwtypes: # pragma: no cover errors.append('You must submit a on-the-water rowing workout') return 0, comments, errors, 0 if record.weightcategory == 'lwt' and ws[0].weightcategory != record.weightcategory: # pragma: no cover 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: # pragma: no cover 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: # pragma: no cover 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: # pragma: no cover oldworkouts = Workout.objects.filter(plannedsession=race) for oldworkout in oldworkouts: oldworkout.plannedsession = None oldworkout.save() otherrecord.workoutid = None otherrecord.coursecompleted = False otherrecord.save() result, comment, errors = add_workouts_plannedsession(ws, race, r) if result: record.coursecompleted = True record.workoutid = ws[0].id if race.sessiontype == 'fastest_distance': df = dataprep.read_data( ['time', 'cumdist'], ids=[ws[0].id]) df = dataprep.remove_nulls_pl(df) fastest_milliseconds, startsecond, endsecond = datautils.getfastest( df, race.sessionvalue, mode='distance') velo = race.sessionvalue/fastest_milliseconds points = 100.*velo/record.referencespeed if fastest_milliseconds > 0: duration = to_time(1000.*fastest_milliseconds) record.coursecompleted = True record.duration = duration record.distance = race.sessionvalue record.points = points record.startsecond = startsecond record.endsecond = endsecond record.save() if race.sessiontype == 'fastest_time': # pragma: no cover df = dataprep.read_data( ['time', 'cumdist'], ids=[ws[0].id]) df = dataprep.remove_nulls_pl(df) fastest_meters, startsecond, endsecond = datautils.getfastest( df, race.sessionvalue, mode='time') velo = fastest_meters/(60.*race.sessionvalue) points = 100.*velo/record.referencespeed if fastest_meters > 0: duration = dt.time(0, race.sessionvalue) record.duration = duration record.distance = fastest_meters record.coursecompleted = True record.points = points record.startsecond = startsecond record.endsecond = endsecond record.save() if ws[0].privacy == 'private': # pragma: no cover 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() else: # pragma: no cover errors.append('Could not find a valid interval in this workout') return result, comments, errors, 0 # 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']: # pragma: no cover 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': # pragma: no cover 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', 'fastest_time', 'fastest_distance']: # pragma: no cover errors.append('For tests, you can only attach one workout') return result, comments, errors, 0 if r.birthdate: age = calculate_age(r.birthdate) else: # pragma: no cover age = None try: record = IndoorVirtualRaceResult.objects.get( userid=r.id, race=race, id=recordid ) except IndoorVirtualRaceResult.DoesNotExist: # pragma: no cover if doregister: hasinitial, boattype, boatclass, adaptiveclass, weightclass, sex, referencespeed, 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, referencespeed=referencespeed, entrycategory=initialcategory, ) record.save() else: errors.append("Unable to find a suitable start category") return result, comments, errors, 0 else: # pragma: no cover 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: # pragma: no cover t = ws[0].duration seconds = t.second+t.minute*60.+t.hour*3600.+t.microsecond/1.e6 if seconds != race.sessionvalue*60.: # pragma: no cover 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: # pragma: no cover errors.append('You must submit a indoor rowing workout') return 0, comments, errors, 0 if record.weightcategory == 'lwt' and ws[0].weightcategory != record.weightcategory: # pragma: no cover 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: # pragma: no cover 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: # pragma: no cover 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: # pragma: no cover otherrecord.workoutid = None otherrecord.coursecompleted = False otherrecord.save() record.coursecompleted = True record.workoutid = ws[0].id if ws[0].privacy == 'private': # pragma: no cover 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']: # pragma: no cover 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': # pragma: no cover 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', 'fastest_time', 'fastest_distance']: # pragma: no cover errors.append('For tests, you can only attach one workout') return result, comments, errors, 0 if r.birthdate: age = calculate_age(r.birthdate) else: # pragma: no cover age = None try: record = VirtualRaceResult.objects.get( userid=r.id, race=race, id=recordid ) except VirtualRaceResult.DoesNotExist: # pragma: no cover if doregister: hasinitial, boattype, boatclass, adaptiveclass, weightclass, sex, referencespeed, 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, referencespeed=referencespeed, ) record.save() add_rower_race(r, race) else: errors.append("Unable to find a suitable start category") return result, comments, errors, 0 else: # pragma: no cover 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: # pragma: no cover 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: # pragma: no cover ws[0].workouttype = record.boatclass ws[0].save() if ws[0].boattype != record.boattype: # pragma: no cover errors.append( 'Your workout boat type did not match the boat type you registered') return 0, comments, errors, 0 if record.weightcategory == 'lwt' and ws[0].weightcategory != record.weightcategory: # pragma: no cover 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: # pragma: no cover 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: # pragma: no cover 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: # pragma: no cover otherrecord.workoutid = None otherrecord.coursecompleted = False otherrecord.save() if ws[0].privacy == 'private': # pragma: no cover 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): # pragma: no cover results = VirtualRaceResult.objects.filter(workoutid=workout.id, race=race) for r in results: r.workoutid = None r.save()