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,
|
Rower, Workout,GraphImage,FavoriteChart,SiteAnnouncement,
|
||||||
Team,TeamInvite,TeamRequest,
|
Team,TeamInvite,TeamRequest,
|
||||||
WorkoutComment,C2WorldClassAgePerformance,PlannedSession,
|
WorkoutComment,C2WorldClassAgePerformance,PlannedSession,
|
||||||
GeoCourse,GeoPolygon,GeoPoint,
|
GeoCourse,GeoPolygon,GeoPoint,VirtualRace,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Register your models here so you can use them in the Admin module
|
# Register your models here so you can use them in the Admin module
|
||||||
@@ -60,6 +60,8 @@ class GeoCourseAdmin(admin.ModelAdmin):
|
|||||||
inlines = (GeoPolygonInline,)
|
inlines = (GeoPolygonInline,)
|
||||||
|
|
||||||
|
|
||||||
|
class VirtualRaceAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('manager','name','startdate','country')
|
||||||
|
|
||||||
admin.site.unregister(User)
|
admin.site.unregister(User)
|
||||||
admin.site.register(User,UserAdmin)
|
admin.site.register(User,UserAdmin)
|
||||||
@@ -75,3 +77,4 @@ admin.site.register(C2WorldClassAgePerformance,
|
|||||||
C2WorldClassAgePerformanceAdmin)
|
C2WorldClassAgePerformanceAdmin)
|
||||||
admin.site.register(PlannedSession,PlannedSessionAdmin)
|
admin.site.register(PlannedSession,PlannedSessionAdmin)
|
||||||
admin.site.register(GeoCourse, GeoCourseAdmin)
|
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 pandas as pd
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from timezonefinder import TimezoneFinder
|
||||||
|
|
||||||
import dataprep
|
import dataprep
|
||||||
from rowers.utils import geo_distance
|
from rowers.utils import geo_distance
|
||||||
@@ -38,6 +39,24 @@ class InvalidTrajectoryError(Exception):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return repr(self.value)
|
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):
|
def polygon_to_path(polygon):
|
||||||
points = GeoPoint.objects.filter(polygon=polygon).order_by("order_in_poly")
|
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
|
# clean data remove zeros and negative values
|
||||||
|
|
||||||
# bring metrics which have negative values to positive domain
|
# bring metrics which have negative values to positive domain
|
||||||
|
if datadf.empty:
|
||||||
|
return datadf
|
||||||
try:
|
try:
|
||||||
datadf['catch'] = -datadf['catch']
|
datadf['catch'] = -datadf['catch']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -807,34 +809,7 @@ def create_row_df(r,distance,duration,startdatetime,
|
|||||||
|
|
||||||
return (id, message)
|
return (id, message)
|
||||||
|
|
||||||
|
from utils import totaltime_sec_to_string
|
||||||
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
|
|
||||||
|
|
||||||
# Processes painsled CSV file to database
|
# Processes painsled CSV file to database
|
||||||
def save_workout_database(f2, r, dosmooth=True, workouttype='rower',
|
def save_workout_database(f2, r, dosmooth=True, workouttype='rower',
|
||||||
@@ -1676,7 +1651,7 @@ def getsmallrowdata_db(columns, ids=[], doclean=True, workstrokesonly=True):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
f = row.df['TimeStamp (sec)'].diff().mean()
|
f = row.df['TimeStamp (sec)'].diff().mean()
|
||||||
except AttributeError:
|
except (AttributeError,KeyError) as e:
|
||||||
f = 0
|
f = 0
|
||||||
|
|
||||||
if f != 0 and not np.isnan(f):
|
if f != 0 and not np.isnan(f):
|
||||||
|
|||||||
+49
-2
@@ -695,6 +695,19 @@ class WorkoutSessionSelectForm(forms.Form):
|
|||||||
widget = forms.CheckboxSelectMultiple,
|
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):
|
class PlannedSessionTeamForm(forms.Form):
|
||||||
team = forms.ModelMultipleChoiceField(
|
team = forms.ModelMultipleChoiceField(
|
||||||
queryset=Team.objects.all(),
|
queryset=Team.objects.all(),
|
||||||
@@ -716,5 +729,39 @@ class PlannedSessionTeamMemberForm(forms.Form):
|
|||||||
super(PlannedSessionTeamMemberForm,self).__init__(*args,**kwargs)
|
super(PlannedSessionTeamMemberForm,self).__init__(*args,**kwargs)
|
||||||
|
|
||||||
self.fields['members'].queryset = thesession.rower.all()
|
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:
|
if rowdata.empty:
|
||||||
return "","No Valid Data Available","",""
|
return "","No Valid Data Available","",""
|
||||||
|
|
||||||
histopwr = rowdata['power'].values
|
try:
|
||||||
|
histopwr = rowdata['power'].values
|
||||||
|
except KeyError:
|
||||||
|
return "","No power data","",""
|
||||||
if len(histopwr) == 0:
|
if len(histopwr) == 0:
|
||||||
return "","No valid data available","",""
|
return "","No valid data available","",""
|
||||||
|
|
||||||
@@ -3468,8 +3471,11 @@ def thumbnail_flex_chart(rowdata,id=0,promember=0,
|
|||||||
plot.yaxis.axis_label = 'Y'
|
plot.yaxis.axis_label = 'Y'
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
yrange1 = Range1d(start=yaxminima[yparam1],end=yaxmaxima[yparam1])
|
yrange1 = Range1d(start=yaxminima[yparam1],end=yaxmaxima[yparam1])
|
||||||
|
except KeyError:
|
||||||
|
yrange1 = Range1d(start=yparam1.min(), end=yparam1.max())
|
||||||
|
|
||||||
plot.y_range = yrange1
|
plot.y_range = yrange1
|
||||||
|
|
||||||
if (xparam != 'time') and (xparam != 'distance') and (xparam != 'cumdist'):
|
if (xparam != 'time') and (xparam != 'distance') and (xparam != 'cumdist'):
|
||||||
|
|||||||
+124
-12
@@ -10,7 +10,7 @@ from django.dispatch import receiver
|
|||||||
from django.forms.widgets import SplitDateTimeWidget
|
from django.forms.widgets import SplitDateTimeWidget
|
||||||
from django.forms.extras.widgets import SelectDateWidget
|
from django.forms.extras.widgets import SelectDateWidget
|
||||||
from django.forms.formsets import BaseFormSet
|
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 datetimewidget.widgets import DateTimeWidget
|
||||||
from django.core.validators import validate_email
|
from django.core.validators import validate_email
|
||||||
import os
|
import os
|
||||||
@@ -422,18 +422,19 @@ def course_length(course):
|
|||||||
|
|
||||||
return int(totaldist)
|
return int(totaldist)
|
||||||
|
|
||||||
|
sexcategories = (
|
||||||
|
('male','male'),
|
||||||
|
('female','female'),
|
||||||
|
('not specified','not specified'),
|
||||||
|
)
|
||||||
|
weightcategories = (
|
||||||
|
('hwt','heavy-weight'),
|
||||||
|
('lwt','light-weight'),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Extension of User with rowing specific data
|
# Extension of User with rowing specific data
|
||||||
class Rower(models.Model):
|
class Rower(models.Model):
|
||||||
weightcategories = (
|
|
||||||
('hwt','heavy-weight'),
|
|
||||||
('lwt','light-weight'),
|
|
||||||
)
|
|
||||||
|
|
||||||
sexcategories = (
|
|
||||||
('male','male'),
|
|
||||||
('female','female'),
|
|
||||||
('not specified','not specified'),
|
|
||||||
)
|
|
||||||
|
|
||||||
stravatypes = (
|
stravatypes = (
|
||||||
('Ride','Ride'),
|
('Ride','Ride'),
|
||||||
@@ -1056,6 +1057,44 @@ class PlannedSession(models.Model):
|
|||||||
|
|
||||||
super(PlannedSession,self).save(*args, **kwargs)
|
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
|
# Date input utility
|
||||||
class DateInput(forms.DateInput):
|
class DateInput(forms.DateInput):
|
||||||
input_type = 'date'
|
input_type = 'date'
|
||||||
@@ -1093,6 +1132,49 @@ class PlannedSessionForm(ModelForm):
|
|||||||
super(PlannedSessionForm, self).__init__(*args, **kwargs)
|
super(PlannedSessionForm, self).__init__(*args, **kwargs)
|
||||||
self.fields['course'].queryset = GeoCourse.objects.all().order_by("country","name")
|
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 PlannedSessionFormSmall(ModelForm):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -1124,12 +1206,12 @@ class PlannedSessionFormSmall(ModelForm):
|
|||||||
'manager': forms.HiddenInput(),
|
'manager': forms.HiddenInput(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boattypes = types.boattypes
|
||||||
|
|
||||||
# Workout
|
# Workout
|
||||||
class Workout(models.Model):
|
class Workout(models.Model):
|
||||||
workouttypes = types.workouttypes
|
workouttypes = types.workouttypes
|
||||||
workoutsources = types.workoutsources
|
workoutsources = types.workoutsources
|
||||||
boattypes = types.boattypes
|
|
||||||
privacychoices = types.privacychoices
|
privacychoices = types.privacychoices
|
||||||
|
|
||||||
user = models.ForeignKey(Rower)
|
user = models.ForeignKey(Rower)
|
||||||
@@ -1242,6 +1324,36 @@ def auto_delete_strokedata_on_delete(sender, instance, **kwargs):
|
|||||||
conn.close()
|
conn.close()
|
||||||
engine.dispose()
|
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
|
from rowers.metrics import rowingmetrics
|
||||||
|
|
||||||
strokedatafields = {
|
strokedatafields = {
|
||||||
|
|||||||
+310
-3
@@ -7,8 +7,8 @@ import time
|
|||||||
from django.db import IntegrityError
|
from django.db import IntegrityError
|
||||||
import uuid
|
import uuid
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
import pytz
|
||||||
from utils import myqueue
|
from utils import myqueue,calculate_age,totaltime_sec_to_string
|
||||||
|
|
||||||
import django_rq
|
import django_rq
|
||||||
queue = django_rq.get_queue('default')
|
queue = django_rq.get_queue('default')
|
||||||
@@ -18,7 +18,7 @@ queuehigh = django_rq.get_queue('low')
|
|||||||
from rowers.models import (
|
from rowers.models import (
|
||||||
Rower, Workout,Team,
|
Rower, Workout,Team,
|
||||||
GeoCourse, TrainingMicroCycle,TrainingMesoCycle,TrainingMacroCycle,
|
GeoCourse, TrainingMicroCycle,TrainingMesoCycle,TrainingMacroCycle,
|
||||||
TrainingPlan,PlannedSession,
|
TrainingPlan,PlannedSession,VirtualRaceResult
|
||||||
)
|
)
|
||||||
|
|
||||||
import metrics
|
import metrics
|
||||||
@@ -86,6 +86,30 @@ def timefield_to_seconds_duration(t):
|
|||||||
|
|
||||||
return duration
|
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):
|
def get_session_metrics(ps):
|
||||||
rowers = ps.rower.all()
|
rowers = ps.rower.all()
|
||||||
rscore = []
|
rscore = []
|
||||||
@@ -401,6 +425,13 @@ def get_sessions(r,startdate=date.today(),
|
|||||||
|
|
||||||
return sps
|
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):
|
def get_workouts_session(r,ps):
|
||||||
ws = Workout.objects.filter(user=r,plannedsession=ps)
|
ws = Workout.objects.filter(user=r,plannedsession=ps)
|
||||||
|
|
||||||
@@ -416,3 +447,279 @@ def update_plannedsession(ps,cd):
|
|||||||
ps.save()
|
ps.save()
|
||||||
|
|
||||||
return 1,'Planned Session Updated'
|
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
|
<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
|
that rowers use to capture the data (both indoor and OTW). For a full
|
||||||
list of currently supported devides/apps, click
|
list of currently supported devides/apps, click
|
||||||
<a href="compatibility">here</a>.
|
<a href="/rowers/compatibility">here</a>.
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -141,9 +141,9 @@
|
|||||||
<div class="grid_2 suffix_2 alpha">
|
<div class="grid_2 suffix_2 alpha">
|
||||||
<p>
|
<p>
|
||||||
{% if user|is_planmember %}
|
{% 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 %}
|
{% else %}
|
||||||
<a class="button blue small" href="/rowers/promembership">Lab</a>
|
<a class="button blue small" href="/rowers/promembership">The Labs</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@@ -7,14 +7,15 @@
|
|||||||
<h2>Import Compatibility</h2>
|
<h2>Import Compatibility</h2>
|
||||||
|
|
||||||
<p>Rowsandall.com tries to be compatible with the most important tools
|
<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
|
that rowers use to capture the data (both indoor and OTW).
|
||||||
list of currently supported devides/apps, click
|
|
||||||
<a href="compatibility.html">here</a>.
|
|
||||||
|
|
||||||
<p>On The Water
|
<p>On The Water
|
||||||
<ul>
|
<ul>
|
||||||
<li> CrewNerd (TCX)</li>
|
<li> CrewNerd (TCX)</li>
|
||||||
<li> Rowing In Motion (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 XL (CSV)</li>
|
||||||
<li> Speedcoach GPS (FIT and CSV)</li></ul></p>
|
<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>
|
</table>
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
</div>
|
</div>
|
||||||
<div class="grid_2">
|
<div class="grid_2 alpha">
|
||||||
<input name='daterange' class="button green" type="submit" value="Submit"> </form>
|
<input name='daterange' class="button green" type="submit" value="Submit"> </form>
|
||||||
</div>
|
</div>
|
||||||
{% if user.is_authenticated and user|is_manager %}
|
{% 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">
|
<button class="grid_2 alpha button green small dropbtn">
|
||||||
{{ rower.user.first_name }} {{ rower.user.last_name }}
|
{{ rower.user.first_name }} {{ rower.user.last_name }}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -6,11 +6,8 @@
|
|||||||
<div class="grid_6 alpha">
|
<div class="grid_6 alpha">
|
||||||
<h2>Coach and Self-Coach Membership</h2>
|
<h2>Coach and Self-Coach Membership</h2>
|
||||||
|
|
||||||
<p>You have arrived at this page, because you tried to create a
|
<p>Rowsandall.com's Training Planning functionality
|
||||||
training plan for yourself.</p>
|
is part of the paid "Self-Coach" and "Coach" plans.</p>
|
||||||
|
|
||||||
<p>This option is restricted to rowers on our "Self-Coach" plan or
|
|
||||||
coaches on our "Coach" plan.</p>
|
|
||||||
|
|
||||||
<p>On the "Self-Coach" plan, you can plan your own sessions.</p>
|
<p>On the "Self-Coach" plan, you can plan your own sessions.</p>
|
||||||
|
|
||||||
@@ -32,7 +29,8 @@
|
|||||||
<div class="grid_6 omega">
|
<div class="grid_6 omega">
|
||||||
<h2>What training planning functionality do we offer?</h2>
|
<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>
|
Our current roadmap is to deploy the following and more:</li>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
@@ -45,12 +43,15 @@
|
|||||||
<li><b>Implemented:</b>Track your teams performance against plan. See how well each
|
<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>
|
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>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
|
allows you, for example, to assign "Row the 6km from bridge A to
|
||||||
bridge B on Saturday" to your team members. The resulting workout
|
bridge B on Saturday" to your team members. The resulting workout
|
||||||
tracks will be evaluated against the course, and you will receive
|
tracks will be evaluated against the course, and you will receive
|
||||||
a results table for the net time spent between the start and finish
|
a results table for the net time spent between the start and finish
|
||||||
points on the course. It's like a mini head race.
|
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>
|
</ul>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
<div id="right" class="grid_6 omega">
|
<div id="right" class="grid_6 omega">
|
||||||
{% if plannedsession.sessiontype == 'test' or plannedsession.sessiontype == 'coursetest' %}
|
{% if plannedsession.sessiontype == 'test' or plannedsession.sessiontype == 'coursetest' %}
|
||||||
<h1>Ranking</h1>
|
<h1>Ranking</h1>
|
||||||
<table class="listtable shortpadded" width="80%">
|
<table id="rankingtable" class="listtable shortpadded tablesorter" width="80%">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Nr</th>
|
<th>Nr</th>
|
||||||
@@ -158,4 +158,14 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block scripts %}
|
{% 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 %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -135,11 +135,13 @@ You will be taken to the secure PayPal payment site.
|
|||||||
<h2>Free Trial</h2>
|
<h2>Free Trial</h2>
|
||||||
<p>
|
<p>
|
||||||
You qualify for a 14 day free trial. No credit card needed.
|
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
|
sign up for the trial. After your trial period expires, you will be
|
||||||
automatically reset to the Basic plan, unless you upgrade to Pro.
|
automatically reset to the Basic plan, unless you upgrade to Pro.
|
||||||
</p>
|
</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"><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 %}
|
{% endif %}
|
||||||
|
|
||||||
<h2>Recurring Payment</h2>
|
<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>
|
</p>
|
||||||
<div class="grid_2 alpha">
|
<div class="grid_2 alpha">
|
||||||
<p>
|
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -86,7 +86,9 @@
|
|||||||
<span class="tooltiptext">
|
<span class="tooltiptext">
|
||||||
<p>rPower: Equivalent steady state power for the duration of the workout.</p>
|
<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>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: 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>
|
</span>
|
||||||
<h2>Other Stats</h2>
|
<h2>Other Stats</h2>
|
||||||
<table width="100%" class="listtable">
|
<table width="100%" class="listtable">
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import dateutil.parser
|
|||||||
import json
|
import json
|
||||||
import datetime
|
import datetime
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
from rowers.utils import calculate_age
|
||||||
|
|
||||||
def strfdelta(tdelta):
|
def strfdelta(tdelta):
|
||||||
minutes,seconds = divmod(tdelta.seconds,60)
|
minutes,seconds = divmod(tdelta.seconds,60)
|
||||||
@@ -136,6 +137,11 @@ from rowers.views import hasplannedsessions
|
|||||||
def is_planmember(user):
|
def is_planmember(user):
|
||||||
return hasplannedsessions(user)
|
return hasplannedsessions(user)
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def get_age(r):
|
||||||
|
return calculate_age(r.birthdate)
|
||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def user_teams(user):
|
def user_teams(user):
|
||||||
try:
|
try:
|
||||||
@@ -196,6 +202,7 @@ def team_rowers(user):
|
|||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def verbosetimeperiod(timeperiod):
|
def verbosetimeperiod(timeperiod):
|
||||||
table = {
|
table = {
|
||||||
|
|||||||
+4
-2
@@ -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.test import TestCase, Client,override_settings
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
from django.utils.six import StringIO
|
from django.utils.six import StringIO
|
||||||
@@ -34,8 +37,7 @@ from dataprep import delete_strokedata
|
|||||||
from redis import StrictRedis
|
from redis import StrictRedis
|
||||||
redis_connection = StrictRedis()
|
redis_connection = StrictRedis()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class DjangoTestCase(TestCase, MockTestCase):
|
class DjangoTestCase(TestCase, MockTestCase):
|
||||||
def _pre_setup(self):
|
def _pre_setup(self):
|
||||||
MockTestCase.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/$',views.workouts_view),
|
||||||
url(r'^u/(?P<userid>\d+)/list-workouts/(?P<startdatestring>\w+.*)/(?P<enddatestring>\w+.*)$',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'^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-workouts/$',views.workouts_view),
|
||||||
url(r'^list-courses/$',views.courses_view),
|
url(r'^list-courses/$',views.courses_view),
|
||||||
url(r'^courses/upload$',views.course_upload_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'^compatibility', TemplateView.as_view(template_name='compatibility.html'),name='about'),
|
||||||
url(r'^videos', TemplateView.as_view(template_name='videos.html'),name='videos'),
|
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'^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'^promembership', TemplateView.as_view(template_name='promembership.html'),name='promembership'),
|
||||||
url(r'^starttrial$',views.start_trial_view),
|
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'^planmembership', TemplateView.as_view(template_name='planmembership.html'),name='planmembership'),
|
||||||
url(r'^paypaltest', TemplateView.as_view(template_name='paypaltest.html'),name='paypaltest'),
|
url(r'^paypaltest', TemplateView.as_view(template_name='paypaltest.html'),name='paypaltest'),
|
||||||
url(r'^legal', TemplateView.as_view(template_name='legal.html'),name='legal'),
|
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()
|
return (d * w).sum() / w.sum()
|
||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
return d.mean()
|
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
|
||||||
|
|||||||
+572
-41
@@ -34,6 +34,7 @@ from rowers.forms import (
|
|||||||
WorkFlowLeftPanelElement,WorkFlowMiddlePanelElement,
|
WorkFlowLeftPanelElement,WorkFlowMiddlePanelElement,
|
||||||
LandingPageForm,PlannedSessionSelectForm,WorkoutSessionSelectForm,
|
LandingPageForm,PlannedSessionSelectForm,WorkoutSessionSelectForm,
|
||||||
PlannedSessionTeamForm,PlannedSessionTeamMemberForm,
|
PlannedSessionTeamForm,PlannedSessionTeamMemberForm,
|
||||||
|
VirtualRaceSelectForm,WorkoutRaceSelectForm,
|
||||||
)
|
)
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
@@ -65,7 +66,8 @@ from rowers.models import (
|
|||||||
Team,TeamForm,TeamInviteForm,TeamInvite,TeamRequest,
|
Team,TeamForm,TeamInviteForm,TeamInvite,TeamRequest,
|
||||||
WorkoutComment,WorkoutCommentForm,RowerExportForm,
|
WorkoutComment,WorkoutCommentForm,RowerExportForm,
|
||||||
CalcAgePerformance,PowerTimeFitnessMetric,PlannedSessionForm,
|
CalcAgePerformance,PowerTimeFitnessMetric,PlannedSessionForm,
|
||||||
PlannedSessionFormSmall,GeoCourseEditForm,
|
PlannedSessionFormSmall,GeoCourseEditForm,VirtualRace,
|
||||||
|
VirtualRaceForm,VirtualRaceResultForm,
|
||||||
)
|
)
|
||||||
from rowers.models import (
|
from rowers.models import (
|
||||||
FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement,BasePlannedSessionFormSet
|
FavoriteForm,BaseFavoriteFormSet,SiteAnnouncement,BasePlannedSessionFormSet
|
||||||
@@ -146,6 +148,7 @@ import matplotlib.pyplot as plt
|
|||||||
from rowers.emails import send_template_email
|
from rowers.emails import send_template_email
|
||||||
|
|
||||||
from pytz import timezone as tz,utc
|
from pytz import timezone as tz,utc
|
||||||
|
from timezonefinder import TimezoneFinder
|
||||||
import dateutil
|
import dateutil
|
||||||
import mpld3
|
import mpld3
|
||||||
from mpld3 import plugins
|
from mpld3 import plugins
|
||||||
@@ -876,6 +879,8 @@ def hasplannedsessions(user):
|
|||||||
r.save()
|
r.save()
|
||||||
|
|
||||||
result = user.is_authenticated() and (r.rowerplan=='coach' or r.rowerplan=='plan')
|
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:
|
else:
|
||||||
result = False
|
result = False
|
||||||
|
|
||||||
@@ -1039,7 +1044,36 @@ def start_trial_view(request):
|
|||||||
['roosendaalsander@gmail.com'])
|
['roosendaalsander@gmail.com'])
|
||||||
|
|
||||||
return HttpResponseRedirect(url)
|
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
|
# Create workout data from Strava or Concept2
|
||||||
# data and create the associated Workout object and save it
|
# data and create the associated Workout object and save it
|
||||||
def add_workout_from_strokedata(user,importid,data,strokedata,
|
def add_workout_from_strokedata(user,importid,data,strokedata,
|
||||||
@@ -2681,7 +2715,7 @@ def rower_process_testcallback(request):
|
|||||||
@login_required()
|
@login_required()
|
||||||
def histo_all(request,theuser=0,
|
def histo_all(request,theuser=0,
|
||||||
startdate=timezone.now()-datetime.timedelta(days=10),
|
startdate=timezone.now()-datetime.timedelta(days=10),
|
||||||
enddate=timezone.now()+datetime.timedelta(days=1),
|
enddate=timezone.now(),
|
||||||
deltadays=-1,
|
deltadays=-1,
|
||||||
startdatestring="",
|
startdatestring="",
|
||||||
enddatestring="",
|
enddatestring="",
|
||||||
@@ -2713,6 +2747,10 @@ def histo_all(request,theuser=0,
|
|||||||
if enddatestring != "":
|
if enddatestring != "":
|
||||||
enddate = iso8601.parse_date(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:
|
if enddate < startdate:
|
||||||
s = enddate
|
s = enddate
|
||||||
enddate = startdate
|
enddate = startdate
|
||||||
@@ -2863,7 +2901,7 @@ def cum_flex(request,theuser=0,
|
|||||||
yparam1='power',
|
yparam1='power',
|
||||||
yparam2='None',
|
yparam2='None',
|
||||||
startdate=timezone.now()-datetime.timedelta(days=10),
|
startdate=timezone.now()-datetime.timedelta(days=10),
|
||||||
enddate=timezone.now()+datetime.timedelta(days=1),
|
enddate=timezone.now(),
|
||||||
deltadays=-1,
|
deltadays=-1,
|
||||||
startdatestring="",
|
startdatestring="",
|
||||||
enddatestring="",
|
enddatestring="",
|
||||||
@@ -3592,7 +3630,7 @@ def rankings_view(request,theuser=0,
|
|||||||
# test to fix bug
|
# test to fix bug
|
||||||
startdate = datetime.datetime.combine(startdate,datetime.time())
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
||||||
enddate = enddate+datetime.timedelta(days=1)
|
#enddate = enddate+datetime.timedelta(days=1)
|
||||||
|
|
||||||
thedistances = []
|
thedistances = []
|
||||||
theworkouts = []
|
theworkouts = []
|
||||||
@@ -3998,7 +4036,7 @@ def rankings_view2(request,theuser=0,
|
|||||||
# test to fix bug
|
# test to fix bug
|
||||||
startdate = datetime.datetime.combine(startdate,datetime.time())
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
||||||
enddate = enddate+datetime.timedelta(days=1)
|
#enddate = enddate+datetime.timedelta(days=1)
|
||||||
|
|
||||||
|
|
||||||
thedistances = []
|
thedistances = []
|
||||||
@@ -4376,7 +4414,7 @@ def otwrankings_view(request,theuser=0,
|
|||||||
# test to fix bug
|
# test to fix bug
|
||||||
startdate = datetime.datetime.combine(startdate,datetime.time())
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
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
|
# test to fix bug
|
||||||
startdate = datetime.datetime.combine(startdate,datetime.time())
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
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='',
|
message='',
|
||||||
successmessage='',
|
successmessage='',
|
||||||
startdate=timezone.now()-datetime.timedelta(days=30),
|
startdate=timezone.now()-datetime.timedelta(days=30),
|
||||||
enddate=timezone.now()+datetime.timedelta(days=1),
|
enddate=timezone.now(),
|
||||||
teamid=0):
|
teamid=0):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -5121,7 +5159,7 @@ def workouts_join_select(request,
|
|||||||
|
|
||||||
startdate = datetime.datetime.combine(startdate,datetime.time())
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
||||||
enddate = enddate+datetime.timedelta(days=1)
|
#enddate = enddate+datetime.timedelta(days=1)
|
||||||
|
|
||||||
if startdatestring:
|
if startdatestring:
|
||||||
startdate = iso8601.parse_date(startdatestring)
|
startdate = iso8601.parse_date(startdatestring)
|
||||||
@@ -5208,7 +5246,7 @@ def team_comparison_select(request,
|
|||||||
message='',
|
message='',
|
||||||
successmessage='',
|
successmessage='',
|
||||||
startdate=timezone.now()-datetime.timedelta(days=30),
|
startdate=timezone.now()-datetime.timedelta(days=30),
|
||||||
enddate=timezone.now()+datetime.timedelta(days=1),
|
enddate=timezone.now(),
|
||||||
teamid=0):
|
teamid=0):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -5280,7 +5318,7 @@ def team_comparison_select(request,
|
|||||||
|
|
||||||
startdate = datetime.datetime.combine(startdate,datetime.time())
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
||||||
enddate = enddate+datetime.timedelta(days=1)
|
#enddate = enddate+datetime.timedelta(days=1)
|
||||||
|
|
||||||
if startdatestring:
|
if startdatestring:
|
||||||
startdate = iso8601.parse_date(startdatestring)
|
startdate = iso8601.parse_date(startdatestring)
|
||||||
@@ -5447,13 +5485,13 @@ def multi_compare_view(request):
|
|||||||
# Multi Flex Chart with Grouping
|
# Multi Flex Chart with Grouping
|
||||||
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
@user_passes_test(ispromember,login_url="/",redirect_field_name=None)
|
||||||
def user_multiflex_select(request,
|
def user_multiflex_select(request,
|
||||||
startdatestring="",
|
startdatestring="",
|
||||||
enddatestring="",
|
enddatestring="",
|
||||||
message='',
|
message='',
|
||||||
successmessage='',
|
successmessage='',
|
||||||
startdate=timezone.now()-datetime.timedelta(days=30),
|
startdate=timezone.now()-datetime.timedelta(days=30),
|
||||||
enddate=timezone.now()+datetime.timedelta(days=1),
|
enddate=timezone.now(),
|
||||||
userid=0):
|
userid=0):
|
||||||
|
|
||||||
if userid == 0:
|
if userid == 0:
|
||||||
user = request.user
|
user = request.user
|
||||||
@@ -5544,7 +5582,7 @@ def user_multiflex_select(request,
|
|||||||
|
|
||||||
startdate = datetime.datetime.combine(startdate,datetime.time())
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
||||||
enddate = enddate+datetime.timedelta(days=1)
|
#enddate = enddate+datetime.timedelta(days=1)
|
||||||
|
|
||||||
if startdatestring:
|
if startdatestring:
|
||||||
startdate = iso8601.parse_date(startdatestring)
|
startdate = iso8601.parse_date(startdatestring)
|
||||||
@@ -5986,7 +6024,7 @@ def user_boxplot_select(request,
|
|||||||
message='',
|
message='',
|
||||||
successmessage='',
|
successmessage='',
|
||||||
startdate=timezone.now()-datetime.timedelta(days=30),
|
startdate=timezone.now()-datetime.timedelta(days=30),
|
||||||
enddate=timezone.now()+datetime.timedelta(days=1),
|
enddate=timezone.now(),
|
||||||
options={
|
options={
|
||||||
'includereststrokes':False,
|
'includereststrokes':False,
|
||||||
'workouttypes':['rower','dynamic','slides'],
|
'workouttypes':['rower','dynamic','slides'],
|
||||||
@@ -6082,7 +6120,7 @@ def user_boxplot_select(request,
|
|||||||
|
|
||||||
startdate = datetime.datetime.combine(startdate,datetime.time())
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
||||||
enddate = enddate+datetime.timedelta(days=1)
|
#enddate = enddate+datetime.timedelta(days=1)
|
||||||
|
|
||||||
if startdatestring:
|
if startdatestring:
|
||||||
startdate = iso8601.parse_date(startdatestring)
|
startdate = iso8601.parse_date(startdatestring)
|
||||||
@@ -6380,9 +6418,8 @@ def courses_view(request):
|
|||||||
# List Workouts
|
# List Workouts
|
||||||
@login_required()
|
@login_required()
|
||||||
def workouts_view(request,message='',successmessage='',
|
def workouts_view(request,message='',successmessage='',
|
||||||
startdatestring="",enddatestring="",
|
startdatestring='',
|
||||||
startdate=timezone.now()-datetime.timedelta(days=365),
|
enddatestring='',
|
||||||
enddate=timezone.now()+datetime.timedelta(days=1),
|
|
||||||
teamid=0,rankingonly=False,rowerid=0,userid=0):
|
teamid=0,rankingonly=False,rowerid=0,userid=0):
|
||||||
|
|
||||||
request.session['referer'] = absolute(request)['PATH']
|
request.session['referer'] = absolute(request)['PATH']
|
||||||
@@ -6402,13 +6439,26 @@ def workouts_view(request,message='',successmessage='',
|
|||||||
if not checkaccessuser(request.user,r):
|
if not checkaccessuser(request.user,r):
|
||||||
raise PermissionDenied("Access denied")
|
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':
|
if request.method == 'POST':
|
||||||
dateform = DateRangeForm(request.POST)
|
dateform = DateRangeForm(request.POST)
|
||||||
if dateform.is_valid():
|
if dateform.is_valid():
|
||||||
startdate = dateform.cleaned_data['startdate']
|
startdate = dateform.cleaned_data['startdate']
|
||||||
enddate = dateform.cleaned_data['enddate']
|
enddate = dateform.cleaned_data['enddate']
|
||||||
startdatestring = None
|
|
||||||
enddatestring = None
|
|
||||||
else:
|
else:
|
||||||
dateform = DateRangeForm(initial={
|
dateform = DateRangeForm(initial={
|
||||||
'startdate':startdate,
|
'startdate':startdate,
|
||||||
@@ -6417,18 +6467,17 @@ def workouts_view(request,message='',successmessage='',
|
|||||||
|
|
||||||
startdate = datetime.datetime.combine(startdate,datetime.time())
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
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:
|
if enddate < startdate:
|
||||||
s = enddate
|
s = enddate
|
||||||
enddate = startdate
|
enddate = startdate
|
||||||
startdate = s
|
startdate = s
|
||||||
|
|
||||||
|
startdatestring = startdate.strftime('%Y-%m-%d')
|
||||||
|
enddatestring = enddate.strftime('%Y-%m-%d')
|
||||||
|
|
||||||
# start date for the small graph
|
# start date for the small graph
|
||||||
activity_startdate = enddate-datetime.timedelta(days=15)
|
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())
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
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:
|
if enddate < startdate:
|
||||||
s = enddate
|
s = enddate
|
||||||
@@ -6637,7 +6686,7 @@ def workout_fusion_list(request,id=0,message='',successmessage='',
|
|||||||
|
|
||||||
startdate = datetime.datetime.combine(startdate,datetime.time())
|
startdate = datetime.datetime.combine(startdate,datetime.time())
|
||||||
enddate = datetime.datetime.combine(enddate,datetime.time(23,59,59))
|
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:
|
if enddate < startdate:
|
||||||
s = enddate
|
s = enddate
|
||||||
@@ -8382,11 +8431,20 @@ def workout_flexchart3_view(request,*args,**kwargs):
|
|||||||
workstrokesonly = request.POST['workstrokesonlysave']
|
workstrokesonly = request.POST['workstrokesonlysave']
|
||||||
reststrokes = not workstrokesonly
|
reststrokes = not workstrokesonly
|
||||||
r = getrower(request.user)
|
r = getrower(request.user)
|
||||||
f = FavoriteChart(user=r,xparam=xparam,
|
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,
|
yparam1=yparam1,yparam2=yparam2,
|
||||||
plottype=plottype,workouttype=workouttype,
|
plottype=plottype,workouttype=workouttype,
|
||||||
reststrokes=reststrokes)
|
reststrokes=reststrokes)
|
||||||
f.save()
|
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:
|
if request.method == 'POST' and 'workstrokesonly' in request.POST:
|
||||||
workstrokesonly = request.POST['workstrokesonly']
|
workstrokesonly = request.POST['workstrokesonly']
|
||||||
@@ -10780,8 +10838,6 @@ def workout_summary_restore_view(request,id,message="",successmessage=""):
|
|||||||
intervalstats = rowdata.allstats()
|
intervalstats = rowdata.allstats()
|
||||||
row.summary = intervalstats
|
row.summary = intervalstats
|
||||||
row.save()
|
row.save()
|
||||||
itime,idist,itype = rowdata.intervalstats_values()
|
|
||||||
nrintervals = len(idist)
|
|
||||||
|
|
||||||
# create interactive plot
|
# create interactive plot
|
||||||
try:
|
try:
|
||||||
@@ -10957,7 +11013,10 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
|
|||||||
if rowdata == 0:
|
if rowdata == 0:
|
||||||
return HttpResponse("Error: CSV Data File Not Found")
|
return HttpResponse("Error: CSV Data File Not Found")
|
||||||
intervalstats = rowdata.allstats()
|
intervalstats = rowdata.allstats()
|
||||||
itime,idist,itype = rowdata.intervalstats_values()
|
try:
|
||||||
|
itime,idist,itype = rowdata.intervalstats_values()
|
||||||
|
except TypeError:
|
||||||
|
return HttpResponse("Error: CSV Data File Not Found")
|
||||||
nrintervals = len(idist)
|
nrintervals = len(idist)
|
||||||
|
|
||||||
# create interactive plot
|
# create interactive plot
|
||||||
@@ -11455,7 +11514,6 @@ def rower_edit_view(request,rowerid=0,message=""):
|
|||||||
r.sex = sex
|
r.sex = sex
|
||||||
r.birthdate = birthdate
|
r.birthdate = birthdate
|
||||||
if resetbounce and r.emailbounced:
|
if resetbounce and r.emailbounced:
|
||||||
print "aap"
|
|
||||||
r.emailbounced = False
|
r.emailbounced = False
|
||||||
r.save()
|
r.save()
|
||||||
form = RowerForm(instance=r)
|
form = RowerForm(instance=r)
|
||||||
@@ -12178,7 +12236,7 @@ def plannedsession_multiclone_view(
|
|||||||
request,timeperiod='none',
|
request,timeperiod='none',
|
||||||
rowerid=0,
|
rowerid=0,
|
||||||
startdate=timezone.now()-datetime.timedelta(days=30),
|
startdate=timezone.now()-datetime.timedelta(days=30),
|
||||||
enddate=timezone.now()+datetime.timedelta(days=1)):
|
enddate=timezone.now()):
|
||||||
|
|
||||||
if rowerid==0:
|
if rowerid==0:
|
||||||
r = getrower(request.user)
|
r = getrower(request.user)
|
||||||
@@ -12868,7 +12926,7 @@ def plannedsessions_manage_view(request,timeperiod='thisweek',rowerid=0,
|
|||||||
user=r,date__gte=startdate,
|
user=r,date__gte=startdate,
|
||||||
date__lte=enddate
|
date__lte=enddate
|
||||||
).order_by(
|
).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 %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block challenges %}
|
||||||
|
<a class="button gray small" href="/rowers/virtualevents">Racing</a>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
+10
-2
@@ -158,10 +158,12 @@
|
|||||||
{% elif user.rower.rowerplan == 'plan' %}
|
{% elif user.rower.rowerplan == 'plan' %}
|
||||||
<h6 class="graytext">Self-Coach</h6>
|
<h6 class="graytext">Self-Coach</h6>
|
||||||
{% else %}
|
{% 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 %}
|
{% endif %}
|
||||||
{% if user.rower.rowerplan == 'basic' and user.rower.protrialexpires|date_dif == 1 %}
|
{% 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>
|
<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 %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -243,8 +245,14 @@
|
|||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if user.rower.protrialexpires and user.rower.protrialexpires|is_future_date %}
|
{% if user.rower.protrialexpires and user.rower.protrialexpires|is_future_date %}
|
||||||
|
{% if user.rower.plantrialexpires %}
|
||||||
<p class="successmessage">
|
<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>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if user.rower.emailbounced %}
|
{% if user.rower.emailbounced %}
|
||||||
|
|||||||
@@ -62,6 +62,10 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block challenges %}
|
||||||
|
<a class="button gray small" href="/rowers/virtualevents">Racing</a>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block body_bottom %}
|
{% block body_bottom %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user