Merge branch 'release/v6.45'
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?><gpx xmlns="http://www.topografix.com/GPX/1/1" xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3" xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1" creator="Oregon 400t" version="1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd"><metadata><link href="http://www.garmin.com"><text>Garmin International</text></link><time>2018-03-17T12:59:13</time></metadata><trk><name>Export by rowingdata</name>
|
||||
</trk></gpx>
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<TrainingCenterDatabase xmlns="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2">
|
||||
<Activities>
|
||||
<Activity Sport="Other">
|
||||
<Id>2015-03-28T20:45:15.000Z</Id>
|
||||
<Creator xsi:type="Device_t" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<Name>Empty File</Name>
|
||||
<UnitId>0</UnitId>
|
||||
<ProductID>0</ProductID>
|
||||
</Creator>
|
||||
</Activity>
|
||||
</Activities>
|
||||
</TrainingCenterDatabase>
|
||||
+4
-1
@@ -6,7 +6,7 @@ from .models import (
|
||||
Rower, Workout,GraphImage,FavoriteChart,SiteAnnouncement,
|
||||
Team,TeamInvite,TeamRequest,
|
||||
WorkoutComment,C2WorldClassAgePerformance,PlannedSession,
|
||||
GeoCourse,GeoPolygon,GeoPoint,
|
||||
GeoCourse,GeoPolygon,GeoPoint,VirtualRace,
|
||||
)
|
||||
|
||||
# Register your models here so you can use them in the Admin module
|
||||
@@ -60,6 +60,8 @@ class GeoCourseAdmin(admin.ModelAdmin):
|
||||
inlines = (GeoPolygonInline,)
|
||||
|
||||
|
||||
class VirtualRaceAdmin(admin.ModelAdmin):
|
||||
list_display = ('manager','name','startdate','country')
|
||||
|
||||
admin.site.unregister(User)
|
||||
admin.site.register(User,UserAdmin)
|
||||
@@ -75,3 +77,4 @@ admin.site.register(C2WorldClassAgePerformance,
|
||||
C2WorldClassAgePerformanceAdmin)
|
||||
admin.site.register(PlannedSession,PlannedSessionAdmin)
|
||||
admin.site.register(GeoCourse, GeoCourseAdmin)
|
||||
admin.site.register(VirtualRace, VirtualRaceAdmin)
|
||||
|
||||
@@ -16,6 +16,7 @@ import xml.etree.ElementTree as et
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from timezonefinder import TimezoneFinder
|
||||
|
||||
import dataprep
|
||||
from rowers.utils import geo_distance
|
||||
@@ -38,6 +39,24 @@ class InvalidTrajectoryError(Exception):
|
||||
def __str__(self):
|
||||
return repr(self.value)
|
||||
|
||||
def get_course_timezone(course):
|
||||
polygons = GeoPolygon.objects.filter(course = course)
|
||||
points = GeoPoint.objects.filter(polygon = polygons[0])
|
||||
lat = points[0].latitude
|
||||
lon = points[0].longitude
|
||||
|
||||
tf = TimezoneFinder()
|
||||
try:
|
||||
timezone_str = tf.timezone_at(lng=lon,lat=lat)
|
||||
except ValueError:
|
||||
timezone_str = 'UTC'
|
||||
|
||||
if timezone_str is None:
|
||||
timezone_str = tf.closest_timezone_at(lng=lon,lat=lat)
|
||||
if timezone_str is None:
|
||||
timezone_str = 'UTC'
|
||||
|
||||
return timezone_str
|
||||
|
||||
def polygon_to_path(polygon):
|
||||
points = GeoPoint.objects.filter(polygon=polygon).order_by("order_in_poly")
|
||||
|
||||
+4
-29
@@ -309,6 +309,8 @@ def clean_df_stats(datadf, workstrokesonly=True, ignorehr=True,
|
||||
# clean data remove zeros and negative values
|
||||
|
||||
# bring metrics which have negative values to positive domain
|
||||
if datadf.empty:
|
||||
return datadf
|
||||
try:
|
||||
datadf['catch'] = -datadf['catch']
|
||||
except KeyError:
|
||||
@@ -807,34 +809,7 @@ def create_row_df(r,distance,duration,startdatetime,
|
||||
|
||||
return (id, message)
|
||||
|
||||
|
||||
def totaltime_sec_to_string(totaltime):
|
||||
hours = int(totaltime / 3600.)
|
||||
if hours > 23:
|
||||
message = 'Warning: The workout duration was longer than 23 hours. '
|
||||
hours = 23
|
||||
|
||||
minutes = int((totaltime - 3600. * hours) / 60.)
|
||||
if minutes > 59:
|
||||
minutes = 59
|
||||
if not message:
|
||||
message = 'Warning: there is something wrong with the workout duration'
|
||||
|
||||
seconds = int(totaltime - 3600. * hours - 60. * minutes)
|
||||
if seconds > 59:
|
||||
seconds = 59
|
||||
if not message:
|
||||
message = 'Warning: there is something wrong with the workout duration'
|
||||
|
||||
tenths = int(10 * (totaltime - 3600. * hours - 60. * minutes - seconds))
|
||||
if tenths > 9:
|
||||
tenths = 9
|
||||
if not message:
|
||||
message = 'Warning: there is something wrong with the workout duration'
|
||||
|
||||
duration = "%s:%s:%s.%s" % (hours, minutes, seconds, tenths)
|
||||
|
||||
return duration
|
||||
from utils import totaltime_sec_to_string
|
||||
|
||||
# Processes painsled CSV file to database
|
||||
def save_workout_database(f2, r, dosmooth=True, workouttype='rower',
|
||||
@@ -1676,7 +1651,7 @@ def getsmallrowdata_db(columns, ids=[], doclean=True, workstrokesonly=True):
|
||||
|
||||
try:
|
||||
f = row.df['TimeStamp (sec)'].diff().mean()
|
||||
except AttributeError:
|
||||
except (AttributeError,KeyError) as e:
|
||||
f = 0
|
||||
|
||||
if f != 0 and not np.isnan(f):
|
||||
|
||||
@@ -695,6 +695,19 @@ class WorkoutSessionSelectForm(forms.Form):
|
||||
widget = forms.CheckboxSelectMultiple,
|
||||
)
|
||||
|
||||
class WorkoutRaceSelectForm(forms.Form):
|
||||
|
||||
def __init__(self, workoutdata, *args, **kwargs):
|
||||
|
||||
super(WorkoutRaceSelectForm, self).__init__(*args, **kwargs)
|
||||
|
||||
self.fields['workouts'] = forms.ChoiceField(
|
||||
label='Workouts',
|
||||
choices = workoutdata['choices'],
|
||||
initial=workoutdata['initial'],
|
||||
widget=forms.RadioSelect,
|
||||
)
|
||||
|
||||
class PlannedSessionTeamForm(forms.Form):
|
||||
team = forms.ModelMultipleChoiceField(
|
||||
queryset=Team.objects.all(),
|
||||
@@ -717,4 +730,38 @@ class PlannedSessionTeamMemberForm(forms.Form):
|
||||
|
||||
self.fields['members'].queryset = thesession.rower.all()
|
||||
|
||||
from rowers.models import VirtualRace,GeoCourse
|
||||
|
||||
def get_countries():
|
||||
countries = VirtualRace.objects.order_by('country').values_list('country').distinct()
|
||||
countries = tuple([(c[0],c[0]) for c in countries])
|
||||
countries = countries+(('All','All'),)
|
||||
return countries
|
||||
|
||||
|
||||
|
||||
class VirtualRaceSelectForm(forms.Form):
|
||||
regattatypechoices = (
|
||||
('upcoming','Upcoming Races'),
|
||||
('ongoing','Ongoing Races'),
|
||||
('previous','Previous Races'),
|
||||
('my','My Races'),
|
||||
('all','All Races'),
|
||||
)
|
||||
|
||||
regattatype = forms.ChoiceField(
|
||||
label='Type',
|
||||
choices = regattatypechoices,
|
||||
initial = 'upcoming',
|
||||
)
|
||||
|
||||
country = forms.ChoiceField(
|
||||
label='Country',
|
||||
choices = get_countries()
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(VirtualRaceSelectForm, self).__init__(*args, **kwargs)
|
||||
self.fields['country'] = forms.ChoiceField(
|
||||
choices = get_countries(),initial='All'
|
||||
)
|
||||
|
||||
@@ -774,7 +774,10 @@ def interactive_histoall(theworkouts):
|
||||
if rowdata.empty:
|
||||
return "","No Valid Data Available","",""
|
||||
|
||||
try:
|
||||
histopwr = rowdata['power'].values
|
||||
except KeyError:
|
||||
return "","No power data","",""
|
||||
if len(histopwr) == 0:
|
||||
return "","No valid data available","",""
|
||||
|
||||
@@ -3468,8 +3471,11 @@ def thumbnail_flex_chart(rowdata,id=0,promember=0,
|
||||
plot.yaxis.axis_label = 'Y'
|
||||
|
||||
|
||||
|
||||
try:
|
||||
yrange1 = Range1d(start=yaxminima[yparam1],end=yaxmaxima[yparam1])
|
||||
except KeyError:
|
||||
yrange1 = Range1d(start=yparam1.min(), end=yparam1.max())
|
||||
|
||||
plot.y_range = yrange1
|
||||
|
||||
if (xparam != 'time') and (xparam != 'distance') and (xparam != 'cumdist'):
|
||||
|
||||
+123
-11
@@ -10,7 +10,7 @@ from django.dispatch import receiver
|
||||
from django.forms.widgets import SplitDateTimeWidget
|
||||
from django.forms.extras.widgets import SelectDateWidget
|
||||
from django.forms.formsets import BaseFormSet
|
||||
from django.contrib.admin.widgets import AdminDateWidget
|
||||
from django.contrib.admin.widgets import AdminDateWidget,AdminTimeWidget,AdminSplitDateTime
|
||||
from datetimewidget.widgets import DateTimeWidget
|
||||
from django.core.validators import validate_email
|
||||
import os
|
||||
@@ -422,18 +422,19 @@ def course_length(course):
|
||||
|
||||
return int(totaldist)
|
||||
|
||||
# Extension of User with rowing specific data
|
||||
class Rower(models.Model):
|
||||
weightcategories = (
|
||||
('hwt','heavy-weight'),
|
||||
('lwt','light-weight'),
|
||||
)
|
||||
|
||||
sexcategories = (
|
||||
sexcategories = (
|
||||
('male','male'),
|
||||
('female','female'),
|
||||
('not specified','not specified'),
|
||||
)
|
||||
)
|
||||
weightcategories = (
|
||||
('hwt','heavy-weight'),
|
||||
('lwt','light-weight'),
|
||||
)
|
||||
|
||||
|
||||
# Extension of User with rowing specific data
|
||||
class Rower(models.Model):
|
||||
|
||||
stravatypes = (
|
||||
('Ride','Ride'),
|
||||
@@ -1056,6 +1057,44 @@ class PlannedSession(models.Model):
|
||||
|
||||
super(PlannedSession,self).save(*args, **kwargs)
|
||||
|
||||
from django.core.validators import RegexValidator,validate_email
|
||||
|
||||
class VirtualRace(PlannedSession):
|
||||
has_registration = models.BooleanField(default=False)
|
||||
registration_closure = models.DateTimeField(blank=True,null=True)
|
||||
evaluation_closure = models.DateTimeField(blank=True,null=True)
|
||||
start_time = models.TimeField(blank=True,null=True)
|
||||
end_time = models.TimeField(blank=True,null=True)
|
||||
country = models.CharField(max_length=100,blank=True)
|
||||
|
||||
timezone = models.CharField(default='UTC',
|
||||
choices=timezones,
|
||||
max_length=100)
|
||||
|
||||
phone_regex = RegexValidator(
|
||||
regex=r'^\+?1?\d{9,15}$',
|
||||
message="Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed."
|
||||
)
|
||||
|
||||
contact_phone = models.CharField(validators=[phone_regex], max_length=17, blank=True)
|
||||
|
||||
contact_email = models.EmailField(max_length=254,
|
||||
validators=[validate_email],blank=True)
|
||||
|
||||
def __unicode__(self):
|
||||
|
||||
name = self.name
|
||||
startdate = self.startdate
|
||||
enddate = self.enddate
|
||||
|
||||
stri = u'Virtual Race {n}'.format(
|
||||
n = name,
|
||||
)
|
||||
|
||||
return stri
|
||||
|
||||
|
||||
|
||||
# Date input utility
|
||||
class DateInput(forms.DateInput):
|
||||
input_type = 'date'
|
||||
@@ -1093,6 +1132,49 @@ class PlannedSessionForm(ModelForm):
|
||||
super(PlannedSessionForm, self).__init__(*args, **kwargs)
|
||||
self.fields['course'].queryset = GeoCourse.objects.all().order_by("country","name")
|
||||
|
||||
class VirtualRaceForm(ModelForm):
|
||||
course = forms.ModelChoiceField(queryset = GeoCourse.objects, empty_label=None)
|
||||
registration_closure = forms.SplitDateTimeField(widget=AdminSplitDateTime(),required=False)
|
||||
evaluation_closure = forms.SplitDateTimeField(widget=AdminSplitDateTime(),required=False)
|
||||
|
||||
class Meta:
|
||||
model = VirtualRace
|
||||
fields = [
|
||||
'name',
|
||||
'startdate',
|
||||
'start_time',
|
||||
'enddate',
|
||||
'end_time',
|
||||
'has_registration',
|
||||
'registration_closure',
|
||||
'evaluation_closure',
|
||||
'course',
|
||||
'comment',
|
||||
'contact_phone',
|
||||
'contact_email',
|
||||
]
|
||||
|
||||
dateTimeOptions = {
|
||||
'format': 'yyyy-mm-dd',
|
||||
'autoclose': True,
|
||||
}
|
||||
|
||||
widgets = {
|
||||
'comment': forms.Textarea,
|
||||
'startdate': AdminDateWidget(),
|
||||
'enddate': AdminDateWidget(),
|
||||
'start_time': AdminTimeWidget(),
|
||||
'end_time': AdminTimeWidget(),
|
||||
'registration_closure':AdminSplitDateTime(),
|
||||
'evaluation_closure':AdminSplitDateTime(),
|
||||
}
|
||||
|
||||
def __init__(self,*args,**kwargs):
|
||||
super(VirtualRaceForm, self).__init__(*args, **kwargs)
|
||||
self.fields['course'].queryset = GeoCourse.objects.all().order_by("country","name")
|
||||
|
||||
|
||||
|
||||
class PlannedSessionFormSmall(ModelForm):
|
||||
|
||||
class Meta:
|
||||
@@ -1124,12 +1206,12 @@ class PlannedSessionFormSmall(ModelForm):
|
||||
'manager': forms.HiddenInput(),
|
||||
}
|
||||
|
||||
boattypes = types.boattypes
|
||||
|
||||
# Workout
|
||||
class Workout(models.Model):
|
||||
workouttypes = types.workouttypes
|
||||
workoutsources = types.workoutsources
|
||||
boattypes = types.boattypes
|
||||
privacychoices = types.privacychoices
|
||||
|
||||
user = models.ForeignKey(Rower)
|
||||
@@ -1242,6 +1324,36 @@ def auto_delete_strokedata_on_delete(sender, instance, **kwargs):
|
||||
conn.close()
|
||||
engine.dispose()
|
||||
|
||||
# Virtual Race results (for keeping results when workouts are deleted)
|
||||
class VirtualRaceResult(models.Model):
|
||||
user = models.ForeignKey(Rower)
|
||||
teamname = models.CharField(max_length=20,verbose_name = 'Team Name',
|
||||
blank=True,null=True)
|
||||
username = models.CharField(max_length=150)
|
||||
workout = models.ForeignKey(Workout,blank=True,null=True)
|
||||
weightcategory = models.CharField(default="hwt",max_length=10,
|
||||
choices=weightcategories,
|
||||
verbose_name='Weight Category')
|
||||
race = models.ForeignKey(VirtualRace)
|
||||
duration = models.TimeField(default=datetime.time(1,0))
|
||||
boattype = models.CharField(choices=boattypes,max_length=40,
|
||||
default='1x',
|
||||
verbose_name = 'Boat Type'
|
||||
)
|
||||
coursecompleted = models.BooleanField(default=False)
|
||||
sex = models.CharField(default="not specified",
|
||||
max_length=30,
|
||||
choices=sexcategories,
|
||||
verbose_name='Gender')
|
||||
|
||||
age = models.IntegerField(null=True)
|
||||
|
||||
class VirtualRaceResultForm(ModelForm):
|
||||
class Meta:
|
||||
model = VirtualRaceResult
|
||||
fields = ['teamname','weightcategory','boattype','age']
|
||||
|
||||
|
||||
from rowers.metrics import rowingmetrics
|
||||
|
||||
strokedatafields = {
|
||||
|
||||
+310
-3
@@ -7,8 +7,8 @@ import time
|
||||
from django.db import IntegrityError
|
||||
import uuid
|
||||
from django.conf import settings
|
||||
|
||||
from utils import myqueue
|
||||
import pytz
|
||||
from utils import myqueue,calculate_age,totaltime_sec_to_string
|
||||
|
||||
import django_rq
|
||||
queue = django_rq.get_queue('default')
|
||||
@@ -18,7 +18,7 @@ queuehigh = django_rq.get_queue('low')
|
||||
from rowers.models import (
|
||||
Rower, Workout,Team,
|
||||
GeoCourse, TrainingMicroCycle,TrainingMesoCycle,TrainingMacroCycle,
|
||||
TrainingPlan,PlannedSession,
|
||||
TrainingPlan,PlannedSession,VirtualRaceResult
|
||||
)
|
||||
|
||||
import metrics
|
||||
@@ -86,6 +86,30 @@ def timefield_to_seconds_duration(t):
|
||||
|
||||
return duration
|
||||
|
||||
|
||||
def get_virtualrace_times(virtualrace):
|
||||
geocourse = GeoCourse.objects.get(id = virtualrace.course.id)
|
||||
timezone_str = courses.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 = []
|
||||
@@ -401,6 +425,13 @@ def get_sessions(r,startdate=date.today(),
|
||||
|
||||
return sps
|
||||
|
||||
def get_my_session_ids(r):
|
||||
sps = PlannedSession.objects.filter(
|
||||
rower__in=[r]
|
||||
).order_by("preferreddate","startdate","enddate")
|
||||
|
||||
return [ps.id for ps in sps]
|
||||
|
||||
def get_workouts_session(r,ps):
|
||||
ws = Workout.objects.filter(user=r,plannedsession=ps)
|
||||
|
||||
@@ -416,3 +447,279 @@ def update_plannedsession(ps,cd):
|
||||
ps.save()
|
||||
|
||||
return 1,'Planned Session Updated'
|
||||
|
||||
def update_virtualrace(ps,cd):
|
||||
for attr, value in cd.items():
|
||||
if attr == 'comment':
|
||||
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 = courses.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)
|
||||
)
|
||||
try:
|
||||
ps.registration_closure = pytz.timezone(timezone_str).localize(
|
||||
ps.registration_closure.replace(tzinfo=None)
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
ps.timezone = timezone_str
|
||||
|
||||
ps.save()
|
||||
|
||||
return 1,'Virtual Race Updated'
|
||||
|
||||
def race_rower_status(r,race):
|
||||
|
||||
ws = Workout.objects.filter(user=r,plannedsession=race)
|
||||
|
||||
is_complete = is_session_complete_ws(ws,race)[1]
|
||||
|
||||
has_registered = r in race.rower.all()
|
||||
|
||||
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 == 'not done':
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
def race_can_resubmit(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 in ['partial','completed']:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
def race_can_withdraw(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
|
||||
)
|
||||
|
||||
registration_closure = race.registration_closure
|
||||
if registration_closure is not None and registration_closure != '':
|
||||
if timezone.now() > registration_closure:
|
||||
return False
|
||||
elif timezone.now() > startdatetime:
|
||||
return False
|
||||
elif timezone.now() > startdatetime:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def race_can_register(r,race):
|
||||
if r 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
|
||||
)
|
||||
|
||||
registration_closure = race.registration_closure
|
||||
if registration_closure is not None and registration_closure != '':
|
||||
if timezone.now() > registration_closure:
|
||||
return False
|
||||
elif timezone.now() > startdatetime:
|
||||
return False
|
||||
elif timezone.now() > startdatetime:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def add_rower_race(r,race):
|
||||
race.rower.add(r)
|
||||
race.save()
|
||||
|
||||
return 1
|
||||
|
||||
def remove_rower_race(r,race):
|
||||
race.rower.remove(r)
|
||||
|
||||
records = VirtualRaceResult.objects.filter(user=r,
|
||||
workout__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_race(ws,race,r):
|
||||
result = 0
|
||||
comments = []
|
||||
errors = []
|
||||
|
||||
start_time = race.start_time
|
||||
start_date = race.startdate
|
||||
startdatetime = datetime.combine(start_date,start_time)
|
||||
startdatetime = pytz.timezone(race.timezone).localize(
|
||||
startdatetime
|
||||
)
|
||||
|
||||
end_time = race.end_time
|
||||
end_date = race.enddate
|
||||
enddatetime = datetime.combine(end_date,end_time)
|
||||
enddatetime = pytz.timezone(race.timezone).localize(
|
||||
enddatetime
|
||||
)
|
||||
|
||||
# check if all sessions have same date
|
||||
dates = [w.date for w in ws]
|
||||
if (not all(d == dates[0] for d in dates)) and race.sessiontype not in ['challenge','cycletarget']:
|
||||
errors.append('For tests and training sessions, selected workouts must all be done on the same date')
|
||||
return result,comments,errors
|
||||
|
||||
if len(ws)>1 and race.sessiontype == 'test':
|
||||
errors.append('For tests, you can only attach one workout')
|
||||
return result,comments,errors
|
||||
|
||||
|
||||
|
||||
ids = [w.id for w in ws]
|
||||
ids = list(set(ids))
|
||||
|
||||
if len(ids)>1 and race.sessiontype in ['test','coursetest']:
|
||||
errors.append('For tests, you can only attach one workout')
|
||||
return result,comments,errors
|
||||
|
||||
# start adding sessions
|
||||
for w in ws:
|
||||
if w.startdatetime>=startdatetime and w.startdatetime<=enddatetime:
|
||||
w.plannedsession = race
|
||||
w.save()
|
||||
result += 1
|
||||
|
||||
comments.append('Your result has been submitted')
|
||||
else:
|
||||
errors.append('Workout %i did not match the race window' % w.id)
|
||||
return result,comments,errors
|
||||
|
||||
if result>0:
|
||||
username = r.user.first_name+' '+r.user.last_name
|
||||
if r.birthdate:
|
||||
age = calculate_age(r.birthdate)
|
||||
else:
|
||||
age = None
|
||||
(
|
||||
coursetime,
|
||||
coursemeters,
|
||||
coursecompleted
|
||||
) = courses.get_time_course(ws,race.course)
|
||||
if not coursecompleted:
|
||||
errors.append('Your trajectory did not match the race course')
|
||||
return result,comments,errors
|
||||
|
||||
duration = totaltime_sec_to_string(coursetime)
|
||||
|
||||
records = VirtualRaceResult.objects.filter(
|
||||
user=r,
|
||||
race=race
|
||||
)
|
||||
|
||||
record = records[0]
|
||||
|
||||
if ws[0].boattype != record.boattype:
|
||||
errors.append('Your workout boat type did not match the boat type you registered')
|
||||
return result,comments,errors
|
||||
|
||||
if ws[0].weightcategory != record.weightcategory:
|
||||
errors.append('Your workout weight category did not match the weight category you registered')
|
||||
return result,comments, errors
|
||||
|
||||
record.coursecompleted=coursecompleted
|
||||
record.workout=ws[0]
|
||||
record.duration = duration
|
||||
record.save()
|
||||
|
||||
|
||||
|
||||
|
||||
return result,comments,errors
|
||||
|
||||
def delete_race_result(workout,race):
|
||||
results = VirtualRaceResult.objects.filter(workout=workout,race=race)
|
||||
for r in results:
|
||||
r.delete()
|
||||
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
<p>Rowsandall.com tries to be compatible with the most important tools
|
||||
that rowers use to capture the data (both indoor and OTW). For a full
|
||||
list of currently supported devides/apps, click
|
||||
<a href="compatibility">here</a>.
|
||||
<a href="/rowers/compatibility">here</a>.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@@ -141,9 +141,9 @@
|
||||
<div class="grid_2 suffix_2 alpha">
|
||||
<p>
|
||||
{% if user|is_planmember %}
|
||||
<a class="button blue small" href="/rowers/fitness-progress">Lab</a>
|
||||
<a class="button blue small" href="/rowers/laboratory">The Labs</a>
|
||||
{% else %}
|
||||
<a class="button blue small" href="/rowers/promembership">Lab</a>
|
||||
<a class="button blue small" href="/rowers/promembership">The Labs</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
<p>
|
||||
|
||||
@@ -7,14 +7,15 @@
|
||||
<h2>Import Compatibility</h2>
|
||||
|
||||
<p>Rowsandall.com tries to be compatible with the most important tools
|
||||
that rowers use to capture the data (both indoor and OTW). For a full
|
||||
list of currently supported devides/apps, click
|
||||
<a href="compatibility.html">here</a>.
|
||||
that rowers use to capture the data (both indoor and OTW).
|
||||
|
||||
<p>On The Water
|
||||
<ul>
|
||||
<li> CrewNerd (TCX)</li>
|
||||
<li> Rowing In Motion (TCX)</li>
|
||||
<li> BoatCoach </li>
|
||||
<li> RowingCoach </li>
|
||||
<li> Quiske RowP</li>
|
||||
<li> Speedcoach XL (CSV)</li>
|
||||
<li> Speedcoach GPS (FIT and CSV)</li></ul></p>
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}Rowsandall - Analysis {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>The Labs</h1>
|
||||
<p>This is where whacky new ideas are tested</p>
|
||||
|
||||
|
||||
<div class="grid_12 alpha">
|
||||
<div class="grid_6 suffix_6 alpha">
|
||||
<img src="/static/img/rivercurrent.jpg" width="400">
|
||||
</div>
|
||||
<div class="grid_6 alpha">
|
||||
<h2>Basic</h2>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="grid_6 omega">
|
||||
<h2>Pro</h2>
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
{% if user|is_planmember %}
|
||||
<a class="button blue small" href="/rowers/fitness-progress">Power Progress</a>
|
||||
{% else %}
|
||||
<a class="button blue small" href="/rowers/promembership">Power Progress</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
<p>
|
||||
Monitoring power duration evidence from all your workouts. Feel free to explore.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
@@ -63,11 +63,11 @@
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
</div>
|
||||
<div class="grid_2">
|
||||
<div class="grid_2 alpha">
|
||||
<input name='daterange' class="button green" type="submit" value="Submit"> </form>
|
||||
</div>
|
||||
{% if user.is_authenticated and user|is_manager %}
|
||||
<div class="grid_2 omega dropdown">
|
||||
<div class="grid_2 dropdown">
|
||||
<button class="grid_2 alpha button green small dropbtn">
|
||||
{{ rower.user.first_name }} {{ rower.user.last_name }}
|
||||
</button>
|
||||
|
||||
@@ -6,11 +6,8 @@
|
||||
<div class="grid_6 alpha">
|
||||
<h2>Coach and Self-Coach Membership</h2>
|
||||
|
||||
<p>You have arrived at this page, because you tried to create a
|
||||
training plan for yourself.</p>
|
||||
|
||||
<p>This option is restricted to rowers on our "Self-Coach" plan or
|
||||
coaches on our "Coach" plan.</p>
|
||||
<p>Rowsandall.com's Training Planning functionality
|
||||
is part of the paid "Self-Coach" and "Coach" plans.</p>
|
||||
|
||||
<p>On the "Self-Coach" plan, you can plan your own sessions.</p>
|
||||
|
||||
@@ -32,7 +29,8 @@
|
||||
<div class="grid_6 omega">
|
||||
<h2>What training planning functionality do we offer?</h2>
|
||||
|
||||
<p>Over the spring of 2018, we will gradually expand this functionality.
|
||||
<p>Over the spring of 2018, we are rolling out the first phases
|
||||
of Training Planning functionality.
|
||||
Our current roadmap is to deploy the following and more:</li>
|
||||
|
||||
<p>
|
||||
@@ -45,12 +43,15 @@
|
||||
<li><b>Implemented:</b>Track your teams performance against plan. See how well each
|
||||
of your team members adhere to their (team or personalized) plan.</li>
|
||||
<li><b>Implemented:</b>See test outcomes ranked by performance.</li>
|
||||
<li><b>Implemented:</b>Attach courses to your OTW tests. This advanced functionality
|
||||
<li><b>Implemented:</b>Attach courses to your OTW tests.
|
||||
This advanced functionality
|
||||
allows you, for example, to assign "Row the 6km from bridge A to
|
||||
bridge B on Saturday" to your team members. The resulting workout
|
||||
tracks will be evaluated against the course, and you will receive
|
||||
a results table for the net time spent between the start and finish
|
||||
points on the course. It's like a mini head race.
|
||||
<li>Define your own macro, meso and microcycle start and end dates</li>
|
||||
<li>More to come ... </li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
<div id="right" class="grid_6 omega">
|
||||
{% if plannedsession.sessiontype == 'test' or plannedsession.sessiontype == 'coursetest' %}
|
||||
<h1>Ranking</h1>
|
||||
<table class="listtable shortpadded" width="80%">
|
||||
<table id="rankingtable" class="listtable shortpadded tablesorter" width="80%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nr</th>
|
||||
@@ -158,4 +158,14 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script type="text/javascript" src="/static/admin/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="/static/admin/js/jquery.tablesorter.min.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/static/admin/js/jquery.tablesorter.widgets.js"></script>
|
||||
<script>
|
||||
$( document ).ready(function() {
|
||||
$("#rankingtable").tablesorter();
|
||||
});
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -135,11 +135,13 @@ You will be taken to the secure PayPal payment site.
|
||||
<h2>Free Trial</h2>
|
||||
<p>
|
||||
You qualify for a 14 day free trial. No credit card needed.
|
||||
Try out Pro membership for two weeks. Click the button below to
|
||||
Try out Pro or Self-Coach membership for two weeks. Click the button below to
|
||||
sign up for the trial. After your trial period expires, you will be
|
||||
automatically reset to the Basic plan, unless you upgrade to Pro.
|
||||
</p>
|
||||
<div class="grid_6"><p><a class="button green small" href="/rowers/starttrial">Yes, I want to try Pro membership for 14 days for free. No strings attached.</a></p></div>
|
||||
<div class="grid_6"> </div>
|
||||
<div class="grid_6"><p><a class="button green small" href="/rowers/startplantrial">Yes, I want to try Self-Coach membership for 14 days for free. No strings attached.</a></p></div>
|
||||
{% endif %}
|
||||
|
||||
<h2>Recurring Payment</h2>
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}Submit Race Result{% endblock %}
|
||||
|
||||
{% block meta %}
|
||||
<script type='text/javascript'
|
||||
src='https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js'>
|
||||
</script>
|
||||
<script type='text/javascript'
|
||||
src='https://ajax.aspnetcdn.com/ajax/jquery.validate/1.14.0/jquery.validate.min.js'>
|
||||
</script>
|
||||
<script>
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class="grid_12 alpha">
|
||||
<div class="grid_6 alpha">
|
||||
<h1>Submit Your Result for {{ race.name }}</h1>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<form id="race_submit_form"
|
||||
method="post">
|
||||
<div class="grid_12 alpha">
|
||||
<div class="grid_6 alpha">
|
||||
<p>Select one of the following workouts that you rowed within the race window</p>
|
||||
<table width="100%">
|
||||
<tr>
|
||||
{% for field in w_form.hidden_fields %}
|
||||
{{ field }}
|
||||
{% endfor %}
|
||||
{% for field in w_form.visible_fields %}
|
||||
<td>
|
||||
{{ field }}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid_2 prefix_2 suffix_8">
|
||||
{% csrf_token %}
|
||||
<input class="button green" type="submit" value="Submit">
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,210 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}New Virtual Race{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="grid_12 alpha">
|
||||
|
||||
<h1>{{ race.name }}</h1>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="grid_12 alpha">
|
||||
<div class="grid_8 alpha">
|
||||
<div id="raceinfo">
|
||||
<h2>Race Information</h2>
|
||||
<p>
|
||||
<table class="listtable shortpadded" width="80%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Course</th><td>{{ race.course }}</td>
|
||||
</tr>
|
||||
{% if race.has_registration %}
|
||||
<tr>
|
||||
<th>Registration closure</th>
|
||||
<td>{{ race.registration_closure }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<th>Date</th><td>{{ race.startdate }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Race Window</th><td>{{ race.startdate }} {{ race.start_time }} to {{ race.enddate }} {{ race.end_time }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Results Submission Deadline</th><td>{{ race.evaluation_closure }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Organizer</th><td>{{ race.manager.first_name }} {{ race.manager.last_name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Contact Email</th><td>{{ race.contact_email }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Contact Phone</th><td>{{ race.contact_phone }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Comment</th><td>{{ race.comment }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</p>
|
||||
</div>
|
||||
<div id="registerbuttons">
|
||||
{% if request.user.is_anonymous %}
|
||||
<p>
|
||||
Registered users of rowsandall.com can participate in this event.
|
||||
</p>
|
||||
{% else %}
|
||||
<p>
|
||||
See race rules below.
|
||||
</p>
|
||||
<p>
|
||||
{% for button in buttons %}
|
||||
{% if button == 'registerbutton' %}
|
||||
<a href="/rowers/virtualevent/{{ race.id }}/register" class="button gray small grid_2">Register</a>
|
||||
{% endif %}
|
||||
{% if button == 'submitbutton' %}
|
||||
<a href="/rowers/virtualevent/{{ race.id }}/submit" class="button gray small grid_2">Submit Result</a>
|
||||
{% endif %}
|
||||
{% if button == 'resubmitbutton' %}
|
||||
<a href="/rowers/virtualevent/{{ race.id }}/submit" class="button gray small grid_2">Submit New Result</a>
|
||||
{% endif %}
|
||||
{% if button == 'withdrawbutton' %}
|
||||
<a href="/rowers/virtualevent/{{ race.id }}/withdraw" class="button gray small grid_2">Withdraw</a>
|
||||
{% endif %}
|
||||
{% if button == 'editbutton' %}
|
||||
<a href="/rowers/virtualevent/{{ race.id }}/edit" class="button gray small grid_2">Edit Race</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div id="results">
|
||||
<h2>Results</h2>
|
||||
<p>
|
||||
{% if results or dns %}
|
||||
<table class="listtable shortpadded" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th> </th>
|
||||
<th>Name</th>
|
||||
<th>Team Name</th>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
<th> </th>
|
||||
<th>Boat</th>
|
||||
<th>Raw Time</th>
|
||||
<th>In Class</th>
|
||||
<th>Corrected Time</th>
|
||||
<th>Corrected Place</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for result in results %}
|
||||
<tr>
|
||||
<td>{{ forloop.counter }}</td>
|
||||
<td>
|
||||
<a href="/rowers/workout/{{ result.workout.id }}">
|
||||
{{ result.username }}</a></td>
|
||||
<td>{{ result.teamname }}</th>
|
||||
<td>{{ result.age }}</td>
|
||||
<td>{{ result.sex }}</td>
|
||||
<td>{{ result.weightcategory }}</td>
|
||||
<td>{{ result.boattype }}</td>
|
||||
<td>{{ result.duration |durationprint:"%H:%M:%S.%f" }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% for result in dns %}
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>{{ result.username }}</td>
|
||||
<td>{{ result.teamname }}</td>
|
||||
<td>{{ result.age }}</td>
|
||||
<td>{{ result.sex }}</td>
|
||||
<td>{{ result.weightcategory }}</td>
|
||||
<td>{{ result.boattype }}</td>
|
||||
<td>DNS</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
No results yet
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
<div id="registered">
|
||||
{% if records %}
|
||||
<h2>Registered Competitors</h2>
|
||||
<table class="listtable shortpadded" width="80%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Team Name</th>
|
||||
<th>Age</th>
|
||||
<th>Weight Category</th>
|
||||
</tr>
|
||||
<tbody>
|
||||
{% for record in records %}
|
||||
<tr>
|
||||
<td>{{ record.username }}
|
||||
<td>{{ record.teamname }}</td>
|
||||
<td>{{ record.weightcategory }}</td>
|
||||
<td>{{ record.age }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div id="rules">
|
||||
<h2>Rules</h2>
|
||||
<p>
|
||||
As a rowsandall.com user, you can
|
||||
register to take part in this event.
|
||||
If the race organizer has set a registration deadline, you must
|
||||
register before the deadline. Otherwise, it is sufficient to
|
||||
register before the start of the race window.
|
||||
You can always withdraw from participating before the registration
|
||||
deadline or the start of the race window, if no registration
|
||||
deadline was set.
|
||||
</p>
|
||||
<p>
|
||||
After the start of the race window and before the submission deadline,
|
||||
you can submit results by linking the race to one of your uploaded
|
||||
workouts. The workout start time must be within the race window
|
||||
and your trajectory must pass through the blue polygons on the course
|
||||
map (in the right order), for your result to be valid.
|
||||
</p>
|
||||
<p>
|
||||
The results table has a link to a page where details of your workout
|
||||
are shown.
|
||||
</p>
|
||||
<p>
|
||||
Race results are stored permanently and are not deleted when
|
||||
you delete the respective workout or remove your account.
|
||||
By registering, you agree with this and the race rules.
|
||||
</p>
|
||||
<p>
|
||||
Virtual Racing on rowsandall.com is honors based. Please be a good
|
||||
sport, submit real results rowed by you, and make sure you set the
|
||||
boat type correctly. For (future functionality) age and gender
|
||||
corrected times, please be sure your gender and birth date are set
|
||||
correctly in your user settings.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid_4 omega">
|
||||
<h2>Course</h2>
|
||||
{{ coursediv|safe }}
|
||||
|
||||
{{ coursescript|safe }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,49 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}New Virtual Race{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="grid_12 alpha">
|
||||
|
||||
<h1>New Virtual Race</h1>
|
||||
|
||||
|
||||
<div class="grid_8 alpha">
|
||||
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
|
||||
{% if form.errors %}
|
||||
<p style="color: red;">
|
||||
Please correct the error{{ form.errors|pluralize }} below.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<table>
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
<div id="formbutton" class="grid_1 prefix_4 suffix_1">
|
||||
<input class="button green" type="submit" value="Save">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="grid_4 omega">
|
||||
<p>
|
||||
<ul>
|
||||
<li>All times are local times in the race course time zone</li>
|
||||
<li>Adding a contact phone number and email is not mandatory, but we
|
||||
strongly recommend it.</li>
|
||||
<li>If your event has a registration closure deadline, participants
|
||||
have to enter (and can withdraw) before the registration closure time.</li>
|
||||
<li>Participants can submit results until the evaluation closure time.</li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,49 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}Edit Virtual Race{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="grid_12 alpha">
|
||||
|
||||
<h1>Edit Race {{ race.name }}</h1>
|
||||
|
||||
|
||||
<div class="grid_8 alpha">
|
||||
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
|
||||
{% if form.errors %}
|
||||
<p style="color: red;">
|
||||
Please correct the error{{ form.errors|pluralize }} below.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<table>
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
<div id="formbutton" class="grid_1 prefix_4 suffix_1">
|
||||
<input class="button green" type="submit" value="Save">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="grid_4 omega">
|
||||
<p>
|
||||
<ul>
|
||||
<li>All times are local times in the race course time zone</li>
|
||||
<li>Adding a contact phone number and email is not mandatory, but we
|
||||
strongly recommend it.</li>
|
||||
<li>If your event has a registration closure deadline, participants
|
||||
have to enter (and can withdraw) before the registration closure time.</li>
|
||||
<li>Participants can submit results until the evaluation closure time.</li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,50 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}Register for a Virtual Race{% endblock %}
|
||||
|
||||
{% block meta %}
|
||||
<script type='text/javascript'
|
||||
src='https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js'>
|
||||
</script>
|
||||
<script type='text/javascript'
|
||||
src='https://ajax.aspnetcdn.com/ajax/jquery.validate/1.14.0/jquery.validate.min.js'>
|
||||
</script>
|
||||
<script>
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class="grid_12 alpha">
|
||||
<div class="grid_6 alpha">
|
||||
<h1>Register for {{ race.name }}</h1>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<form id="race_register_form"
|
||||
method="post">
|
||||
<div class="grid_12 alpha">
|
||||
<p>If you are participating in a single, we will take the age and weight
|
||||
value from your user settings. For other boat types, please fill out
|
||||
crew weight class and average age.
|
||||
</p>
|
||||
<div class="grid_6 alpha">
|
||||
<table width="100%">
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid_2 prefix_2 suffix_8">
|
||||
{% csrf_token %}
|
||||
<input class="button green" type="submit" value="Submit">
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,59 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}Rowsandall Virtual Racing{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="grid_12 alpha">
|
||||
<div class="grid_2 alpha suffix_10">
|
||||
<p>
|
||||
<a class="button green small" href="/rowers/virtualevent/create">Add Race</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid_12 alpha">
|
||||
<form enctype="multipart/form-data" method="post">
|
||||
<div class="grid_8 alpha">
|
||||
{{ form.as_table }}
|
||||
{% csrf_token %}
|
||||
</div>
|
||||
<div class="grid_2">
|
||||
<input name='form' class='button green' type='submit' value="Submit">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="grid_12 alpha">
|
||||
<p>
|
||||
<table width="100%" class="listtable shortpadded">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Event</th>
|
||||
<th>Country</th>
|
||||
<th>Course</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for race in races %}
|
||||
<tr>
|
||||
<td>{{ race.startdate }}</td>
|
||||
<td><a href="/rowers/virtualevent/{{ race.id }}">{{ race.name }}</a></td>
|
||||
<td>{{ race.course.country }}</td>
|
||||
<td>{{ race.course.name }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
@@ -235,7 +235,7 @@
|
||||
</p>
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
<a class="button green small" href="recalcsummary">Update Summary</a>
|
||||
<a class="button green small" href="/rowers/workout/{{ workout.id }}/recalcsummary">Update Summary</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -86,7 +86,9 @@
|
||||
<span class="tooltiptext">
|
||||
<p>rPower: Equivalent steady state power for the duration of the workout.</p>
|
||||
<p>Heart Rate Drift: Comparing heart rate normalized for average power for the first and second half of the workout</p>
|
||||
<p>TRIMP: TRaining IMPact. A way to combine duration and heart rate into a single number.</p>
|
||||
<p>rScore: Score based on rPower and workout duration to estimate training effect</p>
|
||||
<p>rScore (HR): Score based on heart rate, designed to give values comparable to rScore. Used instead of rScore for workouts without power data.</p>
|
||||
</span>
|
||||
<h2>Other Stats</h2>
|
||||
<table width="100%" class="listtable">
|
||||
|
||||
@@ -5,6 +5,7 @@ import dateutil.parser
|
||||
import json
|
||||
import datetime
|
||||
register = template.Library()
|
||||
from rowers.utils import calculate_age
|
||||
|
||||
def strfdelta(tdelta):
|
||||
minutes,seconds = divmod(tdelta.seconds,60)
|
||||
@@ -136,6 +137,11 @@ from rowers.views import hasplannedsessions
|
||||
def is_planmember(user):
|
||||
return hasplannedsessions(user)
|
||||
|
||||
@register.filter
|
||||
def get_age(r):
|
||||
return calculate_age(r.birthdate)
|
||||
|
||||
|
||||
@register.filter
|
||||
def user_teams(user):
|
||||
try:
|
||||
@@ -196,6 +202,7 @@ def team_rowers(user):
|
||||
|
||||
return []
|
||||
|
||||
|
||||
@register.filter
|
||||
def verbosetimeperiod(timeperiod):
|
||||
table = {
|
||||
|
||||
+3
-1
@@ -1,3 +1,6 @@
|
||||
from __future__ import print_function
|
||||
from bs4 import BeautifulSoup
|
||||
import re
|
||||
from django.test import TestCase, Client,override_settings
|
||||
from django.core.management import call_command
|
||||
from django.utils.six import StringIO
|
||||
@@ -35,7 +38,6 @@ from redis import StrictRedis
|
||||
redis_connection = StrictRedis()
|
||||
|
||||
|
||||
|
||||
class DjangoTestCase(TestCase, MockTestCase):
|
||||
def _pre_setup(self):
|
||||
MockTestCase.setUp(self)
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
from __future__ import print_function
|
||||
from bs4 import BeautifulSoup
|
||||
import re
|
||||
from django.test import TestCase, Client,override_settings
|
||||
from django.core.management import call_command
|
||||
from django.utils.six import StringIO
|
||||
from django.test.client import RequestFactory
|
||||
from .views import checkworkoutuser,c2_open
|
||||
from rowers.models import Workout, User, Rower, WorkoutForm,RowerForm,GraphImage
|
||||
from rowers.forms import DocumentsForm,CNsummaryForm,RegistrationFormUniqueEmail
|
||||
import rowers.plots as plots
|
||||
import rowers.interactiveplots as iplots
|
||||
import datetime
|
||||
from rowingdata import rowingdata as rdata
|
||||
from rowingdata import rower as rrower
|
||||
from django.utils import timezone
|
||||
from rowers.rows import handle_uploaded_file
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from time import strftime,strptime,mktime,time,daylight
|
||||
import os
|
||||
from rowers.tasks import handle_makeplot
|
||||
from rowers.utils import serialize_list,deserialize_list
|
||||
from rowers.c2stuff import C2NoTokenError
|
||||
from shutil import copyfile
|
||||
|
||||
from minimocktest import MockTestCase
|
||||
import pandas as pd
|
||||
|
||||
import json
|
||||
import numpy as np
|
||||
|
||||
from rowers import urls
|
||||
from rowers.views import error500_view,error404_view,error400_view,error403_view
|
||||
|
||||
from dataprep import delete_strokedata
|
||||
|
||||
from redis import StrictRedis
|
||||
redis_connection = StrictRedis()
|
||||
|
||||
VERBOSE = True
|
||||
|
||||
class TraverseLinksTest(TestCase):
|
||||
def setUp(self):
|
||||
self.u = User.objects.create_superuser(
|
||||
'superuser1',
|
||||
'superuser1@example.com','pwd')
|
||||
self.r = Rower.objects.create(user=self.u,gdproptin=True,gdproptindate=timezone.now())
|
||||
nu = datetime.datetime.now()
|
||||
|
||||
self.w = Workout.objects.create(
|
||||
name='testworkout',workouttype='On-water',
|
||||
user=self.r,date=nu.strftime('%Y-%m-%d'),
|
||||
starttime=nu.strftime('%H:%M:%S'),
|
||||
duration="0:55:00",distance=8000)
|
||||
self.w2 = Workout.objects.create(
|
||||
name='testworkout 2',workouttype='On-water',
|
||||
user=self.r,date=nu.strftime('%Y-%m-%d'),
|
||||
starttime=nu.strftime('%H:%M:%S'),
|
||||
duration="0:55:00",distance=8000)
|
||||
if self.client.login(
|
||||
username="superuser1", password="pwd"):
|
||||
if VERBOSE:
|
||||
print('\nLogin as superuser OK')
|
||||
else:
|
||||
raise BaseException('Login failed')
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
# Initialise your database here as needed
|
||||
pass
|
||||
|
||||
|
||||
def test_traverse_urls(self):
|
||||
# Fill these lists as needed with your site specific URLs to check and to avoid
|
||||
to_traverse_list = ['/rowers/list-workouts']
|
||||
to_avoid_list = ['^/$', '^$', 'javascript:history\.back()',
|
||||
'javascript:history\.go\(-1\)', '^mailto:.*',
|
||||
'.*github\.io.*', 'javascript:.*',
|
||||
'.*biorow\.com.*','.*facebook.*',
|
||||
'.*wordpress.*','.*analytics.*','.*freenet.*',
|
||||
'.*twitter.*','^blog.*',
|
||||
'.*\d+-\d+-\d+.*',
|
||||
'.*flexchart/.*',
|
||||
'.*heroku.*',
|
||||
'.*oauth.*',
|
||||
'.*rowingdata.*',
|
||||
'.*thisisant.*',
|
||||
'.*garmin.*',
|
||||
'.*sub7.*',
|
||||
'.*bitbucket.*',
|
||||
'.*rathburn.*',
|
||||
'.*team.*',
|
||||
'.*concept2.*',
|
||||
'.*static.*',
|
||||
'.*authorize.*',
|
||||
'.*youtu.*',
|
||||
'.*earth.*',
|
||||
'.*underarmour.*',
|
||||
'.*runkeeper.*',
|
||||
'.*c2list.*',
|
||||
'.*stravaimport.*',
|
||||
'.*performancephones.*',
|
||||
'.*sporttracks.*',
|
||||
'.*join-select.*',
|
||||
]
|
||||
|
||||
done_list = []
|
||||
error_list = []
|
||||
source_of_link = dict()
|
||||
for link in to_traverse_list:
|
||||
source_of_link[link] = 'initial'
|
||||
|
||||
(to_traverse_list, to_avoid_list, done_list, error_list, source_of_link) = \
|
||||
self.recurse_into_path(to_traverse_list, to_avoid_list, done_list, error_list, source_of_link)
|
||||
|
||||
print('END REACHED\nStats:')
|
||||
if VERBOSE: print('\nto_traverse_list = ' + str(to_traverse_list))
|
||||
if VERBOSE: print('\nto_avoid_list = ' + str(to_avoid_list))
|
||||
if VERBOSE: print('\nsource_of_link = ' + str(source_of_link))
|
||||
if VERBOSE: print('\ndone_list = ' + str(done_list))
|
||||
print('Followed ' + str(len(done_list)) + ' links successfully')
|
||||
print('Avoided ' + str(len(to_avoid_list)) + ' links')
|
||||
|
||||
if error_list:
|
||||
print('!! ' + str(len(error_list)) + ' error(s) : ')
|
||||
for error in error_list:
|
||||
print(str(error) + ' found in page ' + source_of_link[error[0]])
|
||||
|
||||
print('Errors found traversing links')
|
||||
assert False
|
||||
else:
|
||||
print('No errors')
|
||||
|
||||
def recurse_into_path(self, to_traverse_list, to_avoid_list, done_list, error_list, source_of_link):
|
||||
""" Dives into first item of to_traverse_list
|
||||
Returns: (to_traverse_list, to_avoid_list, done_list, source_of_link)
|
||||
"""
|
||||
|
||||
if to_traverse_list:
|
||||
url = to_traverse_list.pop()
|
||||
|
||||
if not match_any(url, to_avoid_list):
|
||||
print('Surfing to ' + str(url) + ', discovered in ' + str(source_of_link[url]))
|
||||
response = self.client.get(url, follow=True)
|
||||
|
||||
if response.status_code == 200:
|
||||
soup = BeautifulSoup(response.content, 'html.parser')
|
||||
|
||||
text = soup.get_text()
|
||||
|
||||
for link in soup.find_all('a'):
|
||||
new_link = link.get('href')
|
||||
if VERBOSE: print(' Found link: ' + str(new_link))
|
||||
if match_any(new_link, to_avoid_list):
|
||||
if VERBOSE: print(' Avoiding it')
|
||||
elif new_link in done_list:
|
||||
if VERBOSE: print(' Already done, ignoring')
|
||||
elif new_link in to_traverse_list:
|
||||
if VERBOSE: print(' Already in to traverse list, ignoring')
|
||||
else:
|
||||
if VERBOSE: print(' New, unknown link: Storing it to traverse later')
|
||||
source_of_link[new_link] = url
|
||||
to_traverse_list.append(new_link)
|
||||
|
||||
done_list.append(url)
|
||||
if VERBOSE: print('Done')
|
||||
else:
|
||||
error_list.append((url, response.status_code))
|
||||
to_avoid_list.append(url)
|
||||
|
||||
if VERBOSE: print('Diving into next level')
|
||||
return self.recurse_into_path(to_traverse_list, to_avoid_list, done_list, error_list, source_of_link)
|
||||
|
||||
else:
|
||||
# Nothing to traverse
|
||||
if VERBOSE: print('Returning to upper level')
|
||||
return to_traverse_list, to_avoid_list, done_list, error_list, source_of_link
|
||||
|
||||
|
||||
def match_any(my_string, regexp_list):
|
||||
if my_string:
|
||||
combined = "(" + ")|(".join(regexp_list) + ")"
|
||||
return re.match(combined, my_string)
|
||||
else:
|
||||
# 'None' as string always matches
|
||||
return True
|
||||
@@ -143,6 +143,14 @@ urlpatterns = [
|
||||
url(r'^u/(?P<userid>\d+)/list-workouts/$',views.workouts_view),
|
||||
url(r'^u/(?P<userid>\d+)/list-workouts/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.workouts_view),
|
||||
url(r'^list-workouts/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',views.workouts_view),
|
||||
url(r'^virtualevents$',views.virtualevents_view),
|
||||
url(r'^virtualevent/create$',views.virtualevent_create_view),
|
||||
url(r'^virtualevent/(?P<id>\d+)$',views.virtualevent_view),
|
||||
url(r'^virtualevent/(?P<id>\d+)/edit$',views.virtualevent_edit_view),
|
||||
url(r'^virtualevent/(?P<id>\d+)/register$',views.virtualevent_register_view),
|
||||
url(r'^virtualevent/(?P<id>\d+)/withdraw$',views.virtualevent_withdraw_view),
|
||||
url(r'^virtualevent/(?P<id>\d+)/submit$',
|
||||
views.virtualevent_submit_result_view),
|
||||
url(r'^list-workouts/$',views.workouts_view),
|
||||
url(r'^list-courses/$',views.courses_view),
|
||||
url(r'^courses/upload$',views.course_upload_view),
|
||||
@@ -393,8 +401,10 @@ urlpatterns = [
|
||||
url(r'^compatibility', TemplateView.as_view(template_name='compatibility.html'),name='about'),
|
||||
url(r'^videos', TemplateView.as_view(template_name='videos.html'),name='videos'),
|
||||
url(r'^analysis', TemplateView.as_view(template_name='analysis.html'),name='analysis'),
|
||||
url(r'^laboratory', TemplateView.as_view(template_name='laboratory.html'),name='laboratory'),
|
||||
url(r'^promembership', TemplateView.as_view(template_name='promembership.html'),name='promembership'),
|
||||
url(r'^starttrial$',views.start_trial_view),
|
||||
url(r'^startplantrial$',views.start_plantrial_view),
|
||||
url(r'^planmembership', TemplateView.as_view(template_name='planmembership.html'),name='planmembership'),
|
||||
url(r'^paypaltest', TemplateView.as_view(template_name='paypaltest.html'),name='paypaltest'),
|
||||
url(r'^legal', TemplateView.as_view(template_name='legal.html'),name='legal'),
|
||||
|
||||
@@ -337,3 +337,31 @@ def wavg(group, avg_name, weight_name):
|
||||
return (d * w).sum() / w.sum()
|
||||
except ZeroDivisionError:
|
||||
return d.mean()
|
||||
|
||||
def totaltime_sec_to_string(totaltime):
|
||||
hours = int(totaltime / 3600.)
|
||||
if hours > 23:
|
||||
message = 'Warning: The workout duration was longer than 23 hours. '
|
||||
hours = 23
|
||||
|
||||
minutes = int((totaltime - 3600. * hours) / 60.)
|
||||
if minutes > 59:
|
||||
minutes = 59
|
||||
if not message:
|
||||
message = 'Warning: there is something wrong with the workout duration'
|
||||
|
||||
seconds = int(totaltime - 3600. * hours - 60. * minutes)
|
||||
if seconds > 59:
|
||||
seconds = 59
|
||||
if not message:
|
||||
message = 'Warning: there is something wrong with the workout duration'
|
||||
|
||||
tenths = int(10 * (totaltime - 3600. * hours - 60. * minutes - seconds))
|
||||
if tenths > 9:
|
||||
tenths = 9
|
||||
if not message:
|
||||
message = 'Warning: there is something wrong with the workout duration'
|
||||
|
||||
duration = "%s:%s:%s.%s" % (hours, minutes, seconds, tenths)
|
||||
|
||||
return duration
|
||||
|
||||
+563
-32
@@ -34,6 +34,7 @@ from rowers.forms import (
|
||||
WorkFlowLeftPanelElement,WorkFlowMiddlePanelElement,
|
||||
LandingPageForm,PlannedSessionSelectForm,WorkoutSessionSelectForm,
|
||||
PlannedSessionTeamForm,PlannedSessionTeamMemberForm,
|
||||
VirtualRaceSelectForm,WorkoutRaceSelectForm,
|
||||
)
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.exceptions import PermissionDenied
|
||||
@@ -65,7 +66,8 @@ from rowers.models import (
|
||||
Team,TeamForm,TeamInviteForm,TeamInvite,TeamRequest,
|
||||
WorkoutComment,WorkoutCommentForm,RowerExportForm,
|
||||
CalcAgePerformance,PowerTimeFitnessMetric,PlannedSessionForm,
|
||||
PlannedSessionFormSmall,GeoCourseEditForm,
|
||||
PlannedSessionFormSmall,GeoCourseEditForm,VirtualRace,
|
||||
VirtualRaceForm,VirtualRaceResultForm,
|
||||
)
|
||||
from rowers.models import (
|
||||
FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement,BasePlannedSessionFormSet
|
||||
@@ -146,6 +148,7 @@ import matplotlib.pyplot as plt
|
||||
from rowers.emails import send_template_email
|
||||
|
||||
from pytz import timezone as tz,utc
|
||||
from timezonefinder import TimezoneFinder
|
||||
import dateutil
|
||||
import mpld3
|
||||
from mpld3 import plugins
|
||||
@@ -876,6 +879,8 @@ def hasplannedsessions(user):
|
||||
r.save()
|
||||
|
||||
result = user.is_authenticated() and (r.rowerplan=='coach' or r.rowerplan=='plan')
|
||||
if not result and r.plantrialexpires:
|
||||
result = user.is_authenticaded() and r.rowerplan=='basic' and r.plantrialexpires >= datetime.date.today()
|
||||
else:
|
||||
result = False
|
||||
|
||||
@@ -1040,6 +1045,35 @@ def start_trial_view(request):
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
@login_required()
|
||||
def start_plantrial_view(request):
|
||||
r = getrower(request.user)
|
||||
|
||||
if r.plantrialexpires is not None:
|
||||
messages.error(request,'You do not qualify for a trial')
|
||||
url = '/rowers/promembership'
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
r.plantrialexpires = datetime.date.today()+datetime.timedelta(13)
|
||||
r.protrialexpires = datetime.date.today()+datetime.timedelta(13)
|
||||
r.save()
|
||||
|
||||
url = reverse(workouts_view)
|
||||
|
||||
messages.info(request,'We have started your 14 day trial period')
|
||||
|
||||
subject2 = "User started Pro Trial"
|
||||
message2 = "User Started Pro Trial.\n"
|
||||
message2 += request.user.email + "\n"
|
||||
message2 += "User name: "+request.user.username
|
||||
|
||||
send_mail(subject2, message2,
|
||||
'Rowsandall Server <info@rowsandall.com>',
|
||||
['roosendaalsander@gmail.com'])
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
|
||||
# Create workout data from Strava or Concept2
|
||||
# data and create the associated Workout object and save it
|
||||
def add_workout_from_strokedata(user,importid,data,strokedata,
|
||||
@@ -2681,7 +2715,7 @@ def rower_process_testcallback(request):
|
||||
@login_required()
|
||||
def histo_all(request,theuser=0,
|
||||
startdate=timezone.now()-datetime.timedelta(days=10),
|
||||
enddate=timezone.now()+datetime.timedelta(days=1),
|
||||
enddate=timezone.now(),
|
||||
deltadays=-1,
|
||||
startdatestring="",
|
||||
enddatestring="",
|
||||
@@ -2713,6 +2747,10 @@ def histo_all(request,theuser=0,
|
||||
if enddatestring != "":
|
||||
enddate = iso8601.parse_date(enddatestring)
|
||||
|
||||
|
||||
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
||||
|
||||
if enddate < startdate:
|
||||
s = enddate
|
||||
enddate = startdate
|
||||
@@ -2863,7 +2901,7 @@ def cum_flex(request,theuser=0,
|
||||
yparam1='power',
|
||||
yparam2='None',
|
||||
startdate=timezone.now()-datetime.timedelta(days=10),
|
||||
enddate=timezone.now()+datetime.timedelta(days=1),
|
||||
enddate=timezone.now(),
|
||||
deltadays=-1,
|
||||
startdatestring="",
|
||||
enddatestring="",
|
||||
@@ -3592,7 +3630,7 @@ def rankings_view(request,theuser=0,
|
||||
# test to fix bug
|
||||
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
||||
enddate = enddate+datetime.timedelta(days=1)
|
||||
#enddate = enddate+datetime.timedelta(days=1)
|
||||
|
||||
thedistances = []
|
||||
theworkouts = []
|
||||
@@ -3998,7 +4036,7 @@ def rankings_view2(request,theuser=0,
|
||||
# test to fix bug
|
||||
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
||||
enddate = enddate+datetime.timedelta(days=1)
|
||||
#enddate = enddate+datetime.timedelta(days=1)
|
||||
|
||||
|
||||
thedistances = []
|
||||
@@ -4376,7 +4414,7 @@ def otwrankings_view(request,theuser=0,
|
||||
# test to fix bug
|
||||
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
||||
enddate = enddate+datetime.timedelta(days=1)
|
||||
#enddate = enddate+datetime.timedelta(days=1)
|
||||
|
||||
|
||||
|
||||
@@ -4652,7 +4690,7 @@ def oterankings_view(request,theuser=0,
|
||||
# test to fix bug
|
||||
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
||||
enddate = enddate+datetime.timedelta(days=1)
|
||||
#enddate = enddate+datetime.timedelta(days=1)
|
||||
|
||||
|
||||
|
||||
@@ -5049,7 +5087,7 @@ def workouts_join_select(request,
|
||||
message='',
|
||||
successmessage='',
|
||||
startdate=timezone.now()-datetime.timedelta(days=30),
|
||||
enddate=timezone.now()+datetime.timedelta(days=1),
|
||||
enddate=timezone.now(),
|
||||
teamid=0):
|
||||
|
||||
try:
|
||||
@@ -5121,7 +5159,7 @@ def workouts_join_select(request,
|
||||
|
||||
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
||||
enddate = enddate+datetime.timedelta(days=1)
|
||||
#enddate = enddate+datetime.timedelta(days=1)
|
||||
|
||||
if startdatestring:
|
||||
startdate = iso8601.parse_date(startdatestring)
|
||||
@@ -5208,7 +5246,7 @@ def team_comparison_select(request,
|
||||
message='',
|
||||
successmessage='',
|
||||
startdate=timezone.now()-datetime.timedelta(days=30),
|
||||
enddate=timezone.now()+datetime.timedelta(days=1),
|
||||
enddate=timezone.now(),
|
||||
teamid=0):
|
||||
|
||||
try:
|
||||
@@ -5280,7 +5318,7 @@ def team_comparison_select(request,
|
||||
|
||||
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
||||
enddate = enddate+datetime.timedelta(days=1)
|
||||
#enddate = enddate+datetime.timedelta(days=1)
|
||||
|
||||
if startdatestring:
|
||||
startdate = iso8601.parse_date(startdatestring)
|
||||
@@ -5452,7 +5490,7 @@ def user_multiflex_select(request,
|
||||
message='',
|
||||
successmessage='',
|
||||
startdate=timezone.now()-datetime.timedelta(days=30),
|
||||
enddate=timezone.now()+datetime.timedelta(days=1),
|
||||
enddate=timezone.now(),
|
||||
userid=0):
|
||||
|
||||
if userid == 0:
|
||||
@@ -5544,7 +5582,7 @@ def user_multiflex_select(request,
|
||||
|
||||
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
||||
enddate = enddate+datetime.timedelta(days=1)
|
||||
#enddate = enddate+datetime.timedelta(days=1)
|
||||
|
||||
if startdatestring:
|
||||
startdate = iso8601.parse_date(startdatestring)
|
||||
@@ -5986,7 +6024,7 @@ def user_boxplot_select(request,
|
||||
message='',
|
||||
successmessage='',
|
||||
startdate=timezone.now()-datetime.timedelta(days=30),
|
||||
enddate=timezone.now()+datetime.timedelta(days=1),
|
||||
enddate=timezone.now(),
|
||||
options={
|
||||
'includereststrokes':False,
|
||||
'workouttypes':['rower','dynamic','slides'],
|
||||
@@ -6082,7 +6120,7 @@ def user_boxplot_select(request,
|
||||
|
||||
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
||||
enddate = enddate+datetime.timedelta(days=1)
|
||||
#enddate = enddate+datetime.timedelta(days=1)
|
||||
|
||||
if startdatestring:
|
||||
startdate = iso8601.parse_date(startdatestring)
|
||||
@@ -6380,9 +6418,8 @@ def courses_view(request):
|
||||
# List Workouts
|
||||
@login_required()
|
||||
def workouts_view(request,message='',successmessage='',
|
||||
startdatestring="",enddatestring="",
|
||||
startdate=timezone.now()-datetime.timedelta(days=365),
|
||||
enddate=timezone.now()+datetime.timedelta(days=1),
|
||||
startdatestring='',
|
||||
enddatestring='',
|
||||
teamid=0,rankingonly=False,rowerid=0,userid=0):
|
||||
|
||||
request.session['referer'] = absolute(request)['PATH']
|
||||
@@ -6402,13 +6439,26 @@ def workouts_view(request,message='',successmessage='',
|
||||
if not checkaccessuser(request.user,r):
|
||||
raise PermissionDenied("Access denied")
|
||||
|
||||
if startdatestring:
|
||||
startdate = iso8601.parse_date(startdatestring)
|
||||
else:
|
||||
startdate = datetime.date.today()-datetime.timedelta(days=365)
|
||||
|
||||
if enddatestring:
|
||||
enddate = iso8601.parse_date(enddatestring)
|
||||
else:
|
||||
enddate = datetime.date.today()
|
||||
|
||||
|
||||
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
||||
|
||||
|
||||
if request.method == 'POST':
|
||||
dateform = DateRangeForm(request.POST)
|
||||
if dateform.is_valid():
|
||||
startdate = dateform.cleaned_data['startdate']
|
||||
enddate = dateform.cleaned_data['enddate']
|
||||
startdatestring = None
|
||||
enddatestring = None
|
||||
else:
|
||||
dateform = DateRangeForm(initial={
|
||||
'startdate':startdate,
|
||||
@@ -6417,18 +6467,17 @@ def workouts_view(request,message='',successmessage='',
|
||||
|
||||
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
||||
enddate = enddate+datetime.timedelta(days=1)
|
||||
#enddate = enddate+datetime.timedelta(days=1)
|
||||
|
||||
if startdatestring:
|
||||
startdate = iso8601.parse_date(startdatestring)
|
||||
if enddatestring:
|
||||
enddate = iso8601.parse_date(enddatestring)
|
||||
|
||||
if enddate < startdate:
|
||||
s = enddate
|
||||
enddate = startdate
|
||||
startdate = s
|
||||
|
||||
startdatestring = startdate.strftime('%Y-%m-%d')
|
||||
enddatestring = enddate.strftime('%Y-%m-%d')
|
||||
|
||||
# start date for the small graph
|
||||
activity_startdate = enddate-datetime.timedelta(days=15)
|
||||
|
||||
@@ -6559,7 +6608,7 @@ def workout_comparison_list(request,id=0,message='',successmessage='',
|
||||
|
||||
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
||||
enddate = enddate+datetime.timedelta(days=1)
|
||||
#enddate = enddate+datetime.timedelta(days=1)
|
||||
|
||||
if enddate < startdate:
|
||||
s = enddate
|
||||
@@ -6637,7 +6686,7 @@ def workout_fusion_list(request,id=0,message='',successmessage='',
|
||||
|
||||
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
||||
enddate = enddate+datetime.timedelta(days=1)
|
||||
#enddate = enddate+datetime.timedelta(days=1)
|
||||
|
||||
if enddate < startdate:
|
||||
s = enddate
|
||||
@@ -8382,12 +8431,21 @@ def workout_flexchart3_view(request,*args,**kwargs):
|
||||
workstrokesonly = request.POST['workstrokesonlysave']
|
||||
reststrokes = not workstrokesonly
|
||||
r = getrower(request.user)
|
||||
try:
|
||||
r = metrics.yaxmaxima[xparam]
|
||||
if yparam1 is not None:
|
||||
r = metrics.yaxmaxima[yparam1]
|
||||
if yparam2 is not None:
|
||||
r = metrics.yaxmaxima[yparam2]
|
||||
f = FavoriteChart(user=r,xparam=xparam,
|
||||
yparam1=yparam1,yparam2=yparam2,
|
||||
plottype=plottype,workouttype=workouttype,
|
||||
reststrokes=reststrokes)
|
||||
f.save()
|
||||
|
||||
except KeyError:
|
||||
messages.error(request,'We cannot save the ad hoc metrics in a favorite chart')
|
||||
|
||||
if request.method == 'POST' and 'workstrokesonly' in request.POST:
|
||||
workstrokesonly = request.POST['workstrokesonly']
|
||||
if workstrokesonly == 'True':
|
||||
@@ -10780,8 +10838,6 @@ def workout_summary_restore_view(request,id,message="",successmessage=""):
|
||||
intervalstats = rowdata.allstats()
|
||||
row.summary = intervalstats
|
||||
row.save()
|
||||
itime,idist,itype = rowdata.intervalstats_values()
|
||||
nrintervals = len(idist)
|
||||
|
||||
# create interactive plot
|
||||
try:
|
||||
@@ -10957,7 +11013,10 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
|
||||
if rowdata == 0:
|
||||
return HttpResponse("Error: CSV Data File Not Found")
|
||||
intervalstats = rowdata.allstats()
|
||||
try:
|
||||
itime,idist,itype = rowdata.intervalstats_values()
|
||||
except TypeError:
|
||||
return HttpResponse("Error: CSV Data File Not Found")
|
||||
nrintervals = len(idist)
|
||||
|
||||
# create interactive plot
|
||||
@@ -11455,7 +11514,6 @@ def rower_edit_view(request,rowerid=0,message=""):
|
||||
r.sex = sex
|
||||
r.birthdate = birthdate
|
||||
if resetbounce and r.emailbounced:
|
||||
print "aap"
|
||||
r.emailbounced = False
|
||||
r.save()
|
||||
form = RowerForm(instance=r)
|
||||
@@ -12178,7 +12236,7 @@ def plannedsession_multiclone_view(
|
||||
request,timeperiod='none',
|
||||
rowerid=0,
|
||||
startdate=timezone.now()-datetime.timedelta(days=30),
|
||||
enddate=timezone.now()+datetime.timedelta(days=1)):
|
||||
enddate=timezone.now()):
|
||||
|
||||
if rowerid==0:
|
||||
r = getrower(request.user)
|
||||
@@ -12868,7 +12926,7 @@ def plannedsessions_manage_view(request,timeperiod='thisweek',rowerid=0,
|
||||
user=r,date__gte=startdate,
|
||||
date__lte=enddate
|
||||
).order_by(
|
||||
"date","id"
|
||||
"date","startdatetime","id"
|
||||
)
|
||||
|
||||
|
||||
@@ -13257,3 +13315,476 @@ def plannedsession_deleteconfirm_view(request,id=0):
|
||||
}
|
||||
|
||||
)
|
||||
|
||||
def virtualevents_view(request):
|
||||
|
||||
# default races
|
||||
races = VirtualRace.objects.filter(
|
||||
startdate__gte=datetime.date.today()
|
||||
).order_by("startdate","start_time")
|
||||
|
||||
r = getrower(request.user)
|
||||
|
||||
if request.method == 'POST':
|
||||
# process form
|
||||
form = VirtualRaceSelectForm(request.POST)
|
||||
if form.is_valid():
|
||||
cd = form.cleaned_data
|
||||
country = cd['country']
|
||||
regattatype = cd['regattatype']
|
||||
if country == 'All':
|
||||
countries = VirtualRace.objects.order_by('country').values_list('country').distinct()
|
||||
else:
|
||||
countries = [country]
|
||||
|
||||
if regattatype == 'upcoming':
|
||||
races = VirtualRace.objects.filter(
|
||||
startdate__gte=datetime.date.today(),
|
||||
country__in=countries
|
||||
).order_by("startdate","start_time")
|
||||
elif regattatype == 'previous':
|
||||
races = VirtualRace.objects.filter(
|
||||
enddate__lt=datetime.date.today(),
|
||||
country__in=countries
|
||||
).order_by("startdate","start_time")
|
||||
elif regattatype == 'ongoing':
|
||||
races = VirtualRace.objects.filter(
|
||||
startdate__lte=datetime.date.today(),
|
||||
evaluation_closure__gte=datetime.date.today(),
|
||||
country__in=countries
|
||||
).order_by("startdate","start_time")
|
||||
elif regattatype == 'my':
|
||||
mysessions = get_my_session_ids(r)
|
||||
races = VirtualRace.objects.filter(
|
||||
id__in=mysessions,
|
||||
country__in=countries
|
||||
).order_by("startdate","start_time")
|
||||
elif regattatype == 'all':
|
||||
races = VirtualRace.objects.filter(
|
||||
country__in=countries
|
||||
).order_by("startdate","start_time")
|
||||
else:
|
||||
|
||||
form = VirtualRaceSelectForm()
|
||||
|
||||
|
||||
return render(request,'virtualevents.html',
|
||||
{ 'races':races,
|
||||
'form':form,
|
||||
}
|
||||
)
|
||||
|
||||
def virtualevent_view(request,id=0):
|
||||
if not request.user.is_anonymous():
|
||||
r = getrower(request.user)
|
||||
else:
|
||||
r = None
|
||||
|
||||
try:
|
||||
race = VirtualRace.objects.get(id=id)
|
||||
except VirtualRace.DoesNotExist:
|
||||
raise Http404("Virtual Race does not exist")
|
||||
|
||||
script,div = course_map(race.course)
|
||||
|
||||
buttons = []
|
||||
|
||||
if not request.user.is_anonymous():
|
||||
if race_can_register(r,race):
|
||||
buttons += ['registerbutton']
|
||||
|
||||
if race_can_submit(r,race):
|
||||
buttons += ['submitbutton']
|
||||
|
||||
if race_can_resubmit(r,race):
|
||||
buttons += ['resubmitbutton']
|
||||
|
||||
if race_can_withdraw(r,race):
|
||||
buttons += ['withdrawbutton']
|
||||
|
||||
if race_can_edit(r,race):
|
||||
buttons += ['editbutton']
|
||||
|
||||
results = VirtualRaceResult.objects.filter(
|
||||
race=race,
|
||||
workout__isnull=False,
|
||||
).order_by("duration")
|
||||
|
||||
# to-do - add DNS
|
||||
dns = []
|
||||
if timezone.now() > race.evaluation_closure:
|
||||
print "aap"
|
||||
dns = VirtualRaceResult.objects.filter(
|
||||
race=race,
|
||||
workout__isnull=True,
|
||||
)
|
||||
print dns[0].username,"noot"
|
||||
|
||||
|
||||
records = VirtualRaceResult.objects.filter(
|
||||
race=race
|
||||
)
|
||||
|
||||
|
||||
return render(request,'virtualevent.html',
|
||||
{
|
||||
'coursescript':script,
|
||||
'coursediv':div,
|
||||
'race':race,
|
||||
'rower':r,
|
||||
'results':results,
|
||||
'buttons':buttons,
|
||||
'dns':dns,
|
||||
'records':records,
|
||||
})
|
||||
|
||||
@login_required()
|
||||
def virtualevent_withdraw_view(request,id=0):
|
||||
r = getrower(request.user)
|
||||
try:
|
||||
race = VirtualRace.objects.get(id=id)
|
||||
except VirtualRace.DoesNotExist:
|
||||
raise Http404("Virtual Race does not exist")
|
||||
|
||||
if race_can_withdraw(r,race):
|
||||
remove_rower_race(r,race)
|
||||
messages.info(request,
|
||||
"You have successfully withdrawn from this race.")
|
||||
else:
|
||||
messages.error(request,"You cannot withdraw from this race")
|
||||
|
||||
url = reverse(virtualevent_view,
|
||||
kwargs = {
|
||||
'id':race.id
|
||||
})
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
@login_required()
|
||||
def virtualevent_register_view(request,id=0):
|
||||
r = getrower(request.user)
|
||||
try:
|
||||
race = VirtualRace.objects.get(id=id)
|
||||
except VirtualRace.DoesNotExist:
|
||||
raise Http404("Virtual Race does not exist")
|
||||
|
||||
if not race_can_register(r,race):
|
||||
messages.error(request,"You cannot register for this race")
|
||||
|
||||
url = reverse(virtualevent_view,
|
||||
kwargs = {
|
||||
'id':race.id
|
||||
})
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
# we're still here
|
||||
if request.method == 'POST':
|
||||
# process form
|
||||
form = VirtualRaceResultForm(request.POST)
|
||||
if form.is_valid():
|
||||
cd = form.cleaned_data
|
||||
teamname = cd['teamname']
|
||||
boattype = cd['boattype']
|
||||
if not boattype == '1x':
|
||||
weightcategory = cd['weightcategory']
|
||||
age = cd['age']
|
||||
else:
|
||||
weightcategory = r.weightcategory
|
||||
age = calculate_age(r.birthdate)
|
||||
|
||||
record = VirtualRaceResult(
|
||||
user=r,
|
||||
teamname=teamname,
|
||||
race=race,
|
||||
username = u'{f} {l}'.format(
|
||||
f = r.user.first_name,
|
||||
l = r.user.last_name
|
||||
),
|
||||
weightcategory=weightcategory,
|
||||
duration=datetime.time(0,0),
|
||||
boattype=boattype,
|
||||
coursecompleted=False,
|
||||
sex=r.sex,
|
||||
age=age
|
||||
)
|
||||
|
||||
record.save()
|
||||
|
||||
add_rower_race(r,race)
|
||||
|
||||
|
||||
|
||||
messages.info(
|
||||
request,
|
||||
"You have successfully registered for this race. Good luck!"
|
||||
)
|
||||
|
||||
url = reverse(virtualevent_view,
|
||||
kwargs = {
|
||||
'id':race.id
|
||||
})
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
else:
|
||||
initial = {
|
||||
'age': calculate_age(r.birthdate),
|
||||
'weightcategory': r.weightcategory,
|
||||
}
|
||||
|
||||
form = VirtualRaceResultForm(initial=initial)
|
||||
|
||||
return render(request,'virtualeventregister.html',
|
||||
{
|
||||
'form':form,
|
||||
'race':race,
|
||||
'rower':r,
|
||||
|
||||
})
|
||||
|
||||
@login_required()
|
||||
def virtualevent_create_view(request):
|
||||
r = getrower(request.user)
|
||||
|
||||
if request.method == 'POST':
|
||||
racecreateform = VirtualRaceForm(request.POST)
|
||||
if racecreateform.is_valid():
|
||||
cd = racecreateform.cleaned_data
|
||||
startdate = cd['startdate']
|
||||
start_time = cd['start_time']
|
||||
enddate = cd['enddate']
|
||||
end_time = cd['end_time']
|
||||
comment = cd['comment']
|
||||
course = cd['course']
|
||||
name = cd['name']
|
||||
has_registration = cd['has_registration']
|
||||
registration_closure = cd['registration_closure']
|
||||
evaluation_closure = cd['evaluation_closure']
|
||||
contact_phone = cd['contact_phone']
|
||||
contact_email = cd['contact_email']
|
||||
|
||||
# correct times
|
||||
|
||||
geocourse = GeoCourse.objects.get(id= course.id)
|
||||
timezone_str = courses.get_course_timezone(geocourse)
|
||||
|
||||
startdatetime = datetime.datetime.combine(startdate,start_time)
|
||||
enddatetime = datetime.datetime.combine(enddate,end_time)
|
||||
|
||||
startdatetime = pytz.timezone(timezone_str).localize(
|
||||
startdatetime
|
||||
)
|
||||
enddatetime = pytz.timezone(timezone_str).localize(
|
||||
enddatetime
|
||||
)
|
||||
evaluation_closure = pytz.timezone(timezone_str).localize(
|
||||
evaluation_closure.replace(tzinfo=None)
|
||||
)
|
||||
try:
|
||||
registration_closure = pytz.timezone(
|
||||
timezone_str
|
||||
).localize(
|
||||
registration_closure.replace(tzinfo=None)
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
vs = VirtualRace(
|
||||
name=name,
|
||||
startdate=startdate,
|
||||
preferreddate = startdate,
|
||||
start_time = start_time,
|
||||
enddate=enddate,
|
||||
end_time=end_time,
|
||||
course=geocourse,
|
||||
comment=comment,
|
||||
sessiontype = 'coursetest',
|
||||
timezone=timezone_str,
|
||||
has_registration=has_registration,
|
||||
evaluation_closure=evaluation_closure,
|
||||
registration_closure=registration_closure,
|
||||
contact_phone=contact_phone,
|
||||
contact_email=contact_email,
|
||||
country = course.country,
|
||||
manager=request.user,
|
||||
)
|
||||
|
||||
vs.save()
|
||||
|
||||
|
||||
|
||||
url = reverse(virtualevents_view)
|
||||
return HttpResponseRedirect(url)
|
||||
else:
|
||||
|
||||
racecreateform = VirtualRaceForm()
|
||||
|
||||
|
||||
return render(request,'virtualeventcreate.html',
|
||||
{
|
||||
'form':racecreateform,
|
||||
'rower':r,
|
||||
|
||||
})
|
||||
|
||||
@login_required()
|
||||
def virtualevent_edit_view(request,id=0):
|
||||
r = getrower(request.user)
|
||||
|
||||
try:
|
||||
race = VirtualRace.objects.get(id=id)
|
||||
if race.manager != request.user:
|
||||
raise PermissionDenied("Access denied")
|
||||
except VirtualRace.DoesNotExist:
|
||||
raise Http404("Virtual Race does not exist")
|
||||
|
||||
start_time = race.start_time
|
||||
start_date = race.startdate
|
||||
startdatetime = datetime.datetime.combine(start_date,start_time)
|
||||
startdatetime = pytz.timezone(race.timezone).localize(
|
||||
startdatetime
|
||||
)
|
||||
|
||||
if timezone.now() > startdatetime:
|
||||
messages.error(request,"You cannot edit a race after the start of the race window")
|
||||
url = reverse(virtualevent_view,
|
||||
kwargs={
|
||||
'id':race.id,
|
||||
})
|
||||
|
||||
if request.method == 'POST':
|
||||
racecreateform = VirtualRaceForm(request.POST,instance=race)
|
||||
if racecreateform.is_valid():
|
||||
cd = racecreateform.cleaned_data
|
||||
|
||||
res, message = update_virtualrace(race,cd)
|
||||
|
||||
if res:
|
||||
messages.info(request,message)
|
||||
else:
|
||||
messages.error(request,message)
|
||||
|
||||
url = reverse(virtualevent_view,
|
||||
kwargs = {
|
||||
'id':race.id
|
||||
})
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
else:
|
||||
|
||||
racecreateform = VirtualRaceForm(instance=race)
|
||||
|
||||
|
||||
return render(request,'virtualeventedit.html',
|
||||
{
|
||||
'form':racecreateform,
|
||||
'rower':r,
|
||||
'race':race,
|
||||
|
||||
})
|
||||
|
||||
@login_required()
|
||||
def virtualevent_submit_result_view(request,id=0):
|
||||
|
||||
r = getrower(request.user)
|
||||
|
||||
try:
|
||||
race = VirtualRace.objects.get(id=id)
|
||||
except VirtualRace.DoesNotExist:
|
||||
raise Http404("Virtual Race does not exist")
|
||||
|
||||
start_time = race.start_time
|
||||
start_date = race.startdate
|
||||
startdatetime = datetime.datetime.combine(start_date, start_time)
|
||||
startdatetime = pytz.timezone(race.timezone).localize(startdatetime)
|
||||
|
||||
end_time = race.end_time
|
||||
end_date = race.enddate
|
||||
enddatetime = datetime.datetime.combine(end_date, end_time)
|
||||
enddatetime = pytz.timezone(race.timezone).localize(enddatetime)
|
||||
|
||||
can_submit = race_can_submit(r,race) or race_can_resubmit(r,race)
|
||||
|
||||
if not can_submit:
|
||||
messages.error(request,'You cannot submit a result to this race')
|
||||
url = reverse(virtualevent_view,
|
||||
kwargs = {
|
||||
'id':id
|
||||
}
|
||||
)
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
ws = Workout.objects.filter(
|
||||
user=r,
|
||||
startdatetime__gte=startdatetime,
|
||||
startdatetime__lte=enddatetime,
|
||||
).order_by("date","startdatetime","id")
|
||||
|
||||
initialworkouts = [w.id for w in Workout.objects.filter(
|
||||
user=r,plannedsession=race
|
||||
)]
|
||||
|
||||
workoutdata = {}
|
||||
workoutdata['initial'] = []
|
||||
|
||||
choices = []
|
||||
|
||||
for w in ws:
|
||||
wtpl = (w.id, w.__unicode__())
|
||||
choices.append(wtpl)
|
||||
if w.id in initialworkouts:
|
||||
workoutdata['initial'].append(w.id)
|
||||
|
||||
workoutdata['choices'] = tuple(choices)
|
||||
|
||||
if request.method == 'POST':
|
||||
w_form = WorkoutRaceSelectForm(workoutdata,request.POST)
|
||||
|
||||
if w_form.is_valid():
|
||||
selectedworkout = w_form.cleaned_data['workouts']
|
||||
else:
|
||||
selectedworkout = None
|
||||
|
||||
|
||||
for w in ws:
|
||||
remove_workout_plannedsession(w,race)
|
||||
|
||||
if selectedworkout is not None:
|
||||
|
||||
for w in ws:
|
||||
remove_workout_plannedsession(w,race)
|
||||
delete_race_result(w,race)
|
||||
|
||||
workouts = Workout.objects.filter(id=selectedworkout)
|
||||
|
||||
result,comments,errors = add_workout_race(workouts,race,r)
|
||||
|
||||
for c in comments:
|
||||
messages.info(request,c)
|
||||
for er in errors:
|
||||
messages.error(request,er)
|
||||
|
||||
# redirect to race page
|
||||
url = reverse(virtualevent_view,
|
||||
kwargs = {
|
||||
'id':race.id
|
||||
})
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
else:
|
||||
w_form = WorkoutRaceSelectForm(workoutdata=workoutdata)
|
||||
|
||||
return render(request,'race_submit.html',
|
||||
{
|
||||
'race':race,
|
||||
'workouts':ws,
|
||||
'rower':r,
|
||||
'w_form':w_form,
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
+1
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
Vendored
+1
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -39,6 +39,10 @@
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block challenges %}
|
||||
<a class="button gray small" href="/rowers/virtualevents">Racing</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
+10
-2
@@ -158,10 +158,12 @@
|
||||
{% elif user.rower.rowerplan == 'plan' %}
|
||||
<h6 class="graytext">Self-Coach</h6>
|
||||
{% else %}
|
||||
<div class="grid_1"><p><a class="button green small" href="/rowers/promembership">Upgrade to Pro</a></p></div>
|
||||
<div class="grid_1"><p><a class="button green small" href="/rowers/promembership"><b>Upgrade</b></a></p></div>
|
||||
{% endif %}
|
||||
{% if user.rower.rowerplan == 'basic' and user.rower.protrialexpires|date_dif == 1 %}
|
||||
<div class="grid_1"><p><a class="button green small" href="/rowers/promembership">Start Free Pro trial</a></p></div>
|
||||
{% elif user.rower.rowerplan == 'basic' and user.rower.plantrialexpires|date_dif == 1 %}
|
||||
<div class="grid_1"><p><a class="button green small" href="/rowers/promembership">Start Free Plan trial</a></p></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -243,8 +245,14 @@
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if user.rower.protrialexpires and user.rower.protrialexpires|is_future_date %}
|
||||
{% if user.rower.plantrialexpires %}
|
||||
<p class="successmessage">
|
||||
{{ user.rower.protrialexpires|date_dif|ddays }} days left in Pro trial
|
||||
{{ user.rower.protrialexpires|date_dif|ddays }} days left of your Self-Coach trial - Would you like to <a href="/rowers/planmembership">upgrade now?</a>
|
||||
|
||||
{% else %}
|
||||
<p class="successmessage">
|
||||
{{ user.rower.protrialexpires|date_dif|ddays }} days left of your Pro trial - Would you like to <a href="/rowers/promembership">upgrade now?</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if user.rower.emailbounced %}
|
||||
|
||||
@@ -62,6 +62,10 @@
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block challenges %}
|
||||
<a class="button gray small" href="/rowers/virtualevents">Racing</a>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block body_bottom %}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user