Private
Public Access
1
0

Merge branch 'release/v6.64'

This commit is contained in:
Sander Roosendaal
2018-05-15 13:36:18 +02:00
11 changed files with 252 additions and 34 deletions

View File

@@ -28,9 +28,12 @@ from rowers.models import (
Rower, Workout, Rower, Workout,
GeoPoint,GeoPolygon, GeoCourse, GeoPoint,GeoPolygon, GeoCourse,
course_length,course_coord_center,course_coord_maxmin, course_length,course_coord_center,course_coord_maxmin,
polygon_coord_center,PlannedSession polygon_coord_center,PlannedSession,
polygon_to_path,coordinate_in_path
) )
from utils import geo_distance
# low level methods # low level methods
class InvalidTrajectoryError(Exception): class InvalidTrajectoryError(Exception):
def __init__(self,value): def __init__(self,value):
@@ -39,6 +42,7 @@ class InvalidTrajectoryError(Exception):
def __str__(self): def __str__(self):
return repr(self.value) return repr(self.value)
def get_course_timezone(course): def get_course_timezone(course):
polygons = GeoPolygon.objects.filter(course = course) polygons = GeoPolygon.objects.filter(course = course)
points = GeoPoint.objects.filter(polygon = polygons[0]) points = GeoPoint.objects.filter(polygon = polygons[0])
@@ -58,19 +62,6 @@ def get_course_timezone(course):
return timezone_str return timezone_str
def polygon_to_path(polygon):
points = GeoPoint.objects.filter(polygon=polygon).order_by("order_in_poly")
s = []
for point in points:
s.append([point.latitude,point.longitude])
p = path.Path(s[:-1])
return p
def coordinate_in_path(latitude,longitude, p):
return p.contains_points([(latitude,longitude)])[0]
@@ -293,7 +284,7 @@ def get_time_course(ws,course):
rowdata = rowdata.resample('100ms',on='dt').mean() rowdata = rowdata.resample('100ms',on='dt').mean()
rowdata = rowdata.interpolate() rowdata = rowdata.interpolate()
polygons = GeoPolygon.objects.filter(course=course) polygons = GeoPolygon.objects.filter(course=course).order_by("order_in_course")
paths = [] paths = []
for polygon in polygons: for polygon in polygons:
path = polygon_to_path(polygon) path = polygon_to_path(polygon)

View File

@@ -43,6 +43,8 @@ from courses import (
polygon_coord_center polygon_coord_center
) )
from rowers.models import course_spline
import datetime import datetime
import math import math
import numpy as np import numpy as np
@@ -853,6 +855,8 @@ def course_map(course):
latmean,lonmean,coordinates = course_coord_center(course) latmean,lonmean,coordinates = course_coord_center(course)
lat_min, lat_max, long_min, long_max = course_coord_maxmin(course) lat_min, lat_max, long_min, long_max = course_coord_maxmin(course)
coordinates = course_spline(coordinates)
scoordinates = "[" scoordinates = "["
for index,row in coordinates.iterrows(): for index,row in coordinates.iterrows():
@@ -2208,7 +2212,6 @@ def interactive_chart(id=0,promember=0):
script, div = components(plot) script, div = components(plot)
return [script,div] return [script,div]
def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='', def interactive_multiflex(datadf,xparam,yparam,groupby,extratitle='',

View File

@@ -18,6 +18,9 @@ import twitter
import re import re
import pytz import pytz
from scipy.interpolate import splprep, splev, CubicSpline
import numpy as np
from django.conf import settings from django.conf import settings
from sqlalchemy import create_engine from sqlalchemy import create_engine
import sqlalchemy as sa import sqlalchemy as sa
@@ -29,9 +32,10 @@ import datetime
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from rowers.rows import validate_file_extension from rowers.rows import validate_file_extension
from collections import OrderedDict from collections import OrderedDict
from timezonefinder import TimezoneFinder
import types import types
from matplotlib import path
from rowsandall_app.settings import ( from rowsandall_app.settings import (
TWEET_ACCESS_TOKEN_KEY, TWEET_ACCESS_TOKEN_KEY,
@@ -362,7 +366,46 @@ def polygon_coord_center(polygon):
def polygon_to_path(polygon):
points = GeoPoint.objects.filter(polygon=polygon).order_by("order_in_poly")
s = []
for point in points:
s.append([point.latitude,point.longitude])
p = path.Path(s[:-1])
return p
def coordinate_in_path(latitude,longitude, p):
return p.contains_points([(latitude,longitude)])[0]
def course_spline(coordinates):
latitudes = coordinates['latitude'].values
longitudes = coordinates['longitude'].values
# spline parameters
s = 1.0
k = min([5,len(latitudes)-1])
nest = -1
t = np.linspace(0,1,len(latitudes))
tnew = np.linspace(0,1,100)
latnew = CubicSpline(t,latitudes,bc_type='clamped')(tnew)
lonnew = CubicSpline(t,longitudes,bc_type='clamped')(tnew)
# latnew = CubicSpline(t,latitudes,bc_type='natural')(tnew)
# lonnew = CubicSpline(t,longitudes,bc_type='natural')(tnew)
# tckp,u = splprep([t,latitudes,longitudes],s=s,k=k,nest=nest)
# tnew,latnew,lonnew = splev(np.linspace(0,1,100),tckp)
newcoordinates = pd.DataFrame({
'latitude':latnew,
'longitude':lonnew,
})
return newcoordinates
def course_coord_center(course): def course_coord_center(course):
@@ -379,11 +422,13 @@ def course_coord_center(course):
latitude = pd.Series(latitudes).median() latitude = pd.Series(latitudes).median()
longitude = pd.Series(longitudes).median() longitude = pd.Series(longitudes).median()
coordinates = pd.DataFrame({ coordinates = pd.DataFrame({
'latitude':latitudes, 'latitude':latitudes,
'longitude':longitudes, 'longitude':longitudes,
}) })
return latitude,longitude,coordinates return latitude,longitude,coordinates
def course_coord_maxmin(course): def course_coord_maxmin(course):
@@ -406,6 +451,62 @@ def course_coord_maxmin(course):
return lat_min,lat_max,long_min,long_max return lat_min,lat_max,long_min,long_max
def get_dir_vector(polygon1,polygon2):
lat1,lon1 = polygon_coord_center(polygon1)
lat2,lon2 = polygon_coord_center(polygon2)
return [lat2-lat1,lon2-lon1]
def get_delta(vector,polygon):
x = pd.Series(range(10000))/9999.
vlat = vector[0]
vlon = vector[1]
lat1,lon1 = polygon_coord_center(polygon)
lat = x.apply(lambda x:lat1+x*vlat)
lon = x.apply(lambda x:lon1+x*vlon)
totdist,bearing = geo_distance(lat1,lon1,lat1+vlat,lon1+vlon)
dist = x*totdist
p = polygon_to_path(polygon)
f = lambda x: coordinate_in_path(x['lat'],x['lon'],p)
df = pd.DataFrame({'x':x,
'lat':lat,
'lon':lon,
'dist':dist,
})
df['inpolygon'] = df.apply(f,axis=1)
b = (~df['inpolygon']).shift(-1)+df['inpolygon']
if len(df[b==2]):
return 1.0e3*df[b==2]['dist'].min()
else:
return 0
def get_delta_start(course):
polygons = GeoPolygon.objects.filter(course=course).order_by("order_in_course")
vector = get_dir_vector(polygons[0],polygons[1])
delta = get_delta(vector,polygons[0])
return delta
def get_delta_finish(course):
polygons = GeoPolygon.objects.filter(course=course).order_by("-order_in_course")
vector = get_dir_vector(polygons[0],polygons[1])
delta = get_delta(vector,polygons[0])
return delta
def course_length(course): def course_length(course):
polygons = GeoPolygon.objects.filter(course=course).order_by("order_in_course") polygons = GeoPolygon.objects.filter(course=course).order_by("order_in_course")
@@ -420,7 +521,14 @@ def course_length(course):
totaldist += 1000.*dist[0] totaldist += 1000.*dist[0]
return int(totaldist) vector = get_dir_vector(polygons[0],polygons[1])
deltastart = get_delta(vector,polygons[0])
polygons = polygons.reverse()
vector = get_dir_vector(polygons[0],polygons[1])
deltafinish = get_delta(vector,polygons[0])
return int(totaldist-deltastart-deltafinish)
sexcategories = ( sexcategories = (
('male','male'), ('male','male'),
@@ -765,9 +873,10 @@ class GeoCourse(models.Model):
name = self.name name = self.name
country = self.country country = self.country
return u'{country} - {name}'.format( return u'{country} - {name} - {d}m'.format(
name=name, name=name,
country=country country=country,
d = course_length(self)
) )
class GeoCourseEditForm(ModelForm): class GeoCourseEditForm(ModelForm):
@@ -784,6 +893,16 @@ class GeoPolygon(models.Model):
course = models.ForeignKey(GeoCourse, blank=True) course = models.ForeignKey(GeoCourse, blank=True)
order_in_course = models.IntegerField(default=0) order_in_course = models.IntegerField(default=0)
def __unicode__(self):
name = self.name
coursename = self.course.name
return u'{coursename} - {name}'.format(
name=name,
coursename=coursename
)
# Need error checking to insert new polygons into existing course (all later polygons # Need error checking to insert new polygons into existing course (all later polygons
# increase there order_in_course number # increase there order_in_course number
@@ -1069,9 +1188,12 @@ registerchoices = (
class VirtualRace(PlannedSession): class VirtualRace(PlannedSession):
# has_registration = models.BooleanField(default=False) # has_registration = models.BooleanField(default=False)
registration_form = models.CharField(max_length=100, registration_form = models.CharField(
max_length=100,
default='windowstart', default='windowstart',
choices=registerchoices) choices=registerchoices,
verbose_name='Registration Closure Quick Selector'
)
registration_closure = models.DateTimeField(blank=True,null=True) registration_closure = models.DateTimeField(blank=True,null=True)
evaluation_closure = models.DateTimeField(blank=True,null=True) evaluation_closure = models.DateTimeField(blank=True,null=True)
start_time = models.TimeField(blank=True,null=True) start_time = models.TimeField(blank=True,null=True)
@@ -1105,6 +1227,35 @@ class VirtualRace(PlannedSession):
return stri return stri
def save(self, *args, **kwargs):
# test race window logic
start_time = self.start_time
start_date = self.startdate
startdatetime = datetime.datetime.combine(start_date,start_time)
startdatetime = pytz.timezone(self.timezone).localize(
startdatetime
)
end_time = self.end_time
end_date = self.enddate
enddatetime = datetime.datetime.combine(end_date,end_time)
enddatetime = pytz.timezone(self.timezone).localize(
enddatetime
)
if startdatetime > enddatetime:
self.start_time = end_time
self.startdate = end_date
self.end_time = start_time
self.enddate = start_date
enddatetime = startdatetime
if self.evaluation_closure < enddatetime:
self.evaluation_closure = enddatetime + timezone.timedelta(days=1)
super(VirtualRace,self).save(*args, **kwargs)
# Date input utility # Date input utility
class DateInput(forms.DateInput): class DateInput(forms.DateInput):
@@ -1143,6 +1294,26 @@ 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")
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
class VirtualRaceForm(ModelForm): class VirtualRaceForm(ModelForm):
course = forms.ModelChoiceField(queryset = GeoCourse.objects, empty_label=None) course = forms.ModelChoiceField(queryset = GeoCourse.objects, empty_label=None)
registration_closure = forms.SplitDateTimeField(widget=AdminSplitDateTime(),required=False) registration_closure = forms.SplitDateTimeField(widget=AdminSplitDateTime(),required=False)
@@ -1187,6 +1358,44 @@ class VirtualRaceForm(ModelForm):
self.fields['course'].queryset = GeoCourse.objects.all().order_by("country","name") self.fields['course'].queryset = GeoCourse.objects.all().order_by("country","name")
def clean(self):
cd = self.cleaned_data
course = cd['course']
geocourse = GeoCourse.objects.get(id=course.id)
timezone_str = get_course_timezone(geocourse)
start_time = cd['start_time']
start_date = cd['startdate']
startdatetime = datetime.datetime.combine(start_date,start_time)
startdatetime = pytz.timezone(timezone_str).localize(
startdatetime
)
end_time = cd['end_time']
end_date = cd['enddate']
enddatetime = datetime.datetime.combine(end_date,end_time)
enddatetime = pytz.timezone(timezone_str).localize(
enddatetime
)
if startdatetime > enddatetime:
raise forms.ValidationError("The Start of the Race Window should be before the End of the Race Window")
try:
evaluation_closure = cd['evaluation_closure']
except KeyError:
evaluation_closure = enddatetime+datetime.timedelta(days=1)
cd['evaluation_closure'] = evaluation_closure
if cd['evaluation_closure'] <= enddatetime:
raise forms.ValidationError("Evaluation closure deadline should be after the Race Window closes")
if cd['evaluation_closure'] <= timezone.now():
raise forms.ValidationError("Evaluation closure cannot be in the past")
return cd
class PlannedSessionFormSmall(ModelForm): class PlannedSessionFormSmall(ModelForm):

View File

@@ -28,6 +28,7 @@
<tr> <tr>
<th> Country</th> <th> Country</th>
<th> Name</th> <th> Name</th>
<th> Distance</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -41,6 +42,9 @@
<a href="/rowers/courses/{{ course.id }}">{{ course.name }}</a> <a href="/rowers/courses/{{ course.id }}">{{ course.name }}</a>
{% endif %} {% endif %}
</td> </td>
<td>
{{ course|courselength }} m
</td>
</tr> </tr>

View File

@@ -103,7 +103,7 @@
<a class="small" href="/rowers/sessions/{{ ps.id }}/edit/{{ timeperiod }}/rower/{{ rower.id }}">Edit</a> <a class="small" href="/rowers/sessions/{{ ps.id }}/edit/{{ timeperiod }}/rower/{{ rower.id }}">Edit</a>
</td> </td>
<td> <td>
<a class="small" href="/rowers/sessions/{{ ps.id }}/clone/{{ timeperoid }}/rower/{{ rower.id }}">Clone</a> <a class="small" href="/rowers/sessions/{{ ps.id }}/clone/{{ timeperiod }}/rower/{{ rower.id }}">Clone</a>
</td> </td>
<td> <td>

View File

@@ -96,7 +96,8 @@
<a class="small" href="/rowers/sessions/{{ ps.id }}/edit/{{ timeperiod }}/rower/{{ rower.id }}">Edit</a> <a class="small" href="/rowers/sessions/{{ ps.id }}/edit/{{ timeperiod }}/rower/{{ rower.id }}">Edit</a>
</td> </td>
<td> <td>
<a class="small" href="/rowers/sessions/{{ ps.id }}/clone/{{ timeperiod }}/rower/{{ rower.id }}">Clone</a> <a class="small"
href="/rowers/sessions/{{ ps.id }}/clone/{{ timeperiod }}/rower/{{ rower.id }}">Clone</a>
</td> </td>
<td> <td>
<a class="small" href="/rowers/sessions/{{ ps.id }}/deleteconfirm">Delete</a> <a class="small" href="/rowers/sessions/{{ ps.id }}/deleteconfirm">Delete</a>

View File

@@ -2,7 +2,7 @@
{% load staticfiles %} {% load staticfiles %}
{% load rowerfilters %} {% load rowerfilters %}
{% block title %}New Virtual Race{% endblock %} {% block title %}Rowsandall Virtual Race{% endblock %}
{% block content %} {% block content %}

View File

@@ -7,6 +7,7 @@ import json
import datetime import datetime
register = template.Library() register = template.Library()
from rowers.utils import calculate_age from rowers.utils import calculate_age
from rowers.models import course_length
from rowers.plannedsessions import ( from rowers.plannedsessions import (
race_can_register, race_can_submit,race_rower_status race_can_register, race_can_submit,race_rower_status
) )
@@ -75,6 +76,9 @@ def deltatimeprint(d):
else: else:
return strfdeltah(d) return strfdeltah(d)
@register.filter
def courselength(course):
return course_length(course)
@register.filter(is_safe=True) @register.filter(is_safe=True)
def jsdict(dict,key): def jsdict(dict,key):

View File

@@ -463,9 +463,9 @@ urlpatterns = [
url(r'^sessions/multicreate/(?P<timeperiod>[\w\ ]+.*)$', url(r'^sessions/multicreate/(?P<timeperiod>[\w\ ]+.*)$',
views.plannedsession_multicreate_view), views.plannedsession_multicreate_view),
url(r'^sessions/(?P<id>\d+)/edit$',views.plannedsession_edit_view),
url(r'^sessions/(?P<id>\d+)/edit/(?P<timeperiod>[\w\ ]+.*)/rower/(?P<rowerid>\d+)$',views.plannedsession_edit_view), url(r'^sessions/(?P<id>\d+)/edit/(?P<timeperiod>[\w\ ]+.*)/rower/(?P<rowerid>\d+)$',views.plannedsession_edit_view),
url(r'^sessions/(?P<id>\d+)/edit/(?P<timeperiod>[\w\ ]+.*)$',views.plannedsession_edit_view), url(r'^sessions/(?P<id>\d+)/edit/(?P<timeperiod>[\w\ ]+.*)$',views.plannedsession_edit_view),
url(r'^sessions/(?P<id>\d+)/edit$',views.plannedsession_edit_view),
url(r'^sessions/(?P<id>\d+)/clone$',views.plannedsession_clone_view), url(r'^sessions/(?P<id>\d+)/clone$',views.plannedsession_clone_view),
url(r'^sessions/(?P<id>\d+)/clone/(?P<timeperiod>[\w\ ]+.*)/rower/(?P<rowerid>\d+)$',views.plannedsession_clone_view), url(r'^sessions/(?P<id>\d+)/clone/(?P<timeperiod>[\w\ ]+.*)/rower/(?P<rowerid>\d+)$',views.plannedsession_clone_view),

View File

@@ -253,8 +253,9 @@ def isbreakthrough(delta,cpvalues,p0,p1,p2,p3,ratio):
pwr *= ratio pwr *= ratio
delta = delta.values delta = delta.values.astype(int)
cpvalues = cpvalues.values cpvalues = cpvalues.values.astype(int)
pwr = pwr.astype(int)
res = np.sum(cpvalues>pwr) res = np.sum(cpvalues>pwr)
res2 = np.sum(cpvalues>pwr2) res2 = np.sum(cpvalues>pwr2)

View File

@@ -10822,7 +10822,13 @@ def workout_split_view(request,id=id):
splitsecond += splittime.second splitsecond += splittime.second
splitsecond += splittime.microsecond/1.e6 splitsecond += splittime.microsecond/1.e6
splitmode = form.cleaned_data['splitmode'] splitmode = form.cleaned_data['splitmode']
ids,mesgs = dataprep.split_workout(r,row,splitsecond,splitmode) try:
ids,mesgs = dataprep.split_workout(
r,row,splitsecond,splitmode
)
except IndexError:
messages.error("Something went wrong in Split")
for message in mesgs: for message in mesgs:
messages.info(request,message) messages.info(request,message)
@@ -13588,7 +13594,6 @@ def virtualevent_create_view(request):
startdatetime = datetime.datetime.combine(startdate,start_time) startdatetime = datetime.datetime.combine(startdate,start_time)
enddatetime = datetime.datetime.combine(enddate,end_time) enddatetime = datetime.datetime.combine(enddate,end_time)
print enddatetime
startdatetime = pytz.timezone(timezone_str).localize( startdatetime = pytz.timezone(timezone_str).localize(
startdatetime startdatetime