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,
GeoPoint,GeoPolygon, GeoCourse,
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
class InvalidTrajectoryError(Exception):
def __init__(self,value):
@@ -39,6 +42,7 @@ class InvalidTrajectoryError(Exception):
def __str__(self):
return repr(self.value)
def get_course_timezone(course):
polygons = GeoPolygon.objects.filter(course = course)
points = GeoPoint.objects.filter(polygon = polygons[0])
@@ -58,19 +62,6 @@ def get_course_timezone(course):
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.interpolate()
polygons = GeoPolygon.objects.filter(course=course)
polygons = GeoPolygon.objects.filter(course=course).order_by("order_in_course")
paths = []
for polygon in polygons:
path = polygon_to_path(polygon)

View File

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

View File

@@ -18,6 +18,9 @@ import twitter
import re
import pytz
from scipy.interpolate import splprep, splev, CubicSpline
import numpy as np
from django.conf import settings
from sqlalchemy import create_engine
import sqlalchemy as sa
@@ -29,9 +32,10 @@ import datetime
from django.core.exceptions import ValidationError
from rowers.rows import validate_file_extension
from collections import OrderedDict
from timezonefinder import TimezoneFinder
import types
from matplotlib import path
from rowsandall_app.settings import (
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):
@@ -379,10 +422,12 @@ def course_coord_center(course):
latitude = pd.Series(latitudes).median()
longitude = pd.Series(longitudes).median()
coordinates = pd.DataFrame({
'latitude':latitudes,
'longitude':longitudes,
})
return latitude,longitude,coordinates
@@ -406,6 +451,62 @@ def course_coord_maxmin(course):
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):
polygons = GeoPolygon.objects.filter(course=course).order_by("order_in_course")
@@ -420,7 +521,14 @@ def course_length(course):
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 = (
('male','male'),
@@ -765,9 +873,10 @@ class GeoCourse(models.Model):
name = self.name
country = self.country
return u'{country} - {name}'.format(
return u'{country} - {name} - {d}m'.format(
name=name,
country=country
country=country,
d = course_length(self)
)
class GeoCourseEditForm(ModelForm):
@@ -784,6 +893,16 @@ class GeoPolygon(models.Model):
course = models.ForeignKey(GeoCourse, blank=True)
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
# increase there order_in_course number
@@ -1069,9 +1188,12 @@ registerchoices = (
class VirtualRace(PlannedSession):
# has_registration = models.BooleanField(default=False)
registration_form = models.CharField(max_length=100,
default='windowstart',
choices=registerchoices)
registration_form = models.CharField(
max_length=100,
default='windowstart',
choices=registerchoices,
verbose_name='Registration Closure Quick Selector'
)
registration_closure = models.DateTimeField(blank=True,null=True)
evaluation_closure = models.DateTimeField(blank=True,null=True)
start_time = models.TimeField(blank=True,null=True)
@@ -1104,6 +1226,35 @@ class VirtualRace(PlannedSession):
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
@@ -1142,7 +1293,27 @@ class PlannedSessionForm(ModelForm):
def __init__(self,*args,**kwargs):
super(PlannedSessionForm, self).__init__(*args, **kwargs)
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):
course = forms.ModelChoiceField(queryset = GeoCourse.objects, empty_label=None)
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")
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):

View File

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

View File

@@ -103,7 +103,7 @@
<a class="small" href="/rowers/sessions/{{ ps.id }}/edit/{{ timeperiod }}/rower/{{ rower.id }}">Edit</a>
</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>

View File

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

View File

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

View File

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

View File

@@ -463,9 +463,9 @@ urlpatterns = [
url(r'^sessions/multicreate/(?P<timeperiod>[\w\ ]+.*)$',
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\ ]+.*)$',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/(?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
delta = delta.values
cpvalues = cpvalues.values
delta = delta.values.astype(int)
cpvalues = cpvalues.values.astype(int)
pwr = pwr.astype(int)
res = np.sum(cpvalues>pwr)
res2 = np.sum(cpvalues>pwr2)

View File

@@ -10822,7 +10822,13 @@ def workout_split_view(request,id=id):
splitsecond += splittime.second
splitsecond += splittime.microsecond/1.e6
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:
messages.info(request,message)
@@ -13588,7 +13594,6 @@ def virtualevent_create_view(request):
startdatetime = datetime.datetime.combine(startdate,start_time)
enddatetime = datetime.datetime.combine(enddate,end_time)
print enddatetime
startdatetime = pytz.timezone(timezone_str).localize(
startdatetime