Private
Public Access
1
0

Merge branch 'release/v5.94'

This commit is contained in:
Sander Roosendaal
2018-02-21 21:25:17 +01:00
5 changed files with 151 additions and 28 deletions
+99 -17
View File
@@ -14,6 +14,9 @@ from matplotlib import path
import xml.etree.ElementTree as et import xml.etree.ElementTree as et
import pandas as pd import pandas as pd
import numpy as np
import dataprep
ns = {'opengis': 'http://www.opengis.net/kml/2.2'} ns = {'opengis': 'http://www.opengis.net/kml/2.2'}
@@ -83,45 +86,45 @@ def course_coord_maxmin(course):
return lat_min,lat_max,long_min,long_max return lat_min,lat_max,long_min,long_max
def polygon_to_path(polygon): def polygon_to_path(polygon):
points = GeoPoint.objects.filter(polygon==polygon).order_by("order_in_polygon") points = GeoPoint.objects.filter(polygon=polygon).order_by("order_in_poly")
s = [] s = []
for point in points: for point in points:
s.append([point.latitude,point.longitude]) s.append([point.latitude,point.longitude])
p = path.Path(np.array(s)) p = path.Path(s[:-1])
return p return p
def coordinate_in_polygon(latitude,longitude, polygon): def coordinate_in_path(latitude,longitude, p):
p = polygon_to_path(polygon)
return p.contains_points([(latitude,longitude)])[0] return p.contains_points([(latitude,longitude)])[0]
def time_in_polygon(df,polygon,maxmin='max'): def time_in_path(df,p,maxmin='max'):
# df has timestamp, latitude, longitude
p = polygon_to_path(polygon) if df.empty:
return 0
latitude = df.latitude latitude = df.latitude
longitude = df.longitude longitude = df.longitude
f = lambda x: coordinate_in_polygon(x['latitude'],x['longitude'],polygon) f = lambda x: coordinate_in_path(x['latitude'],x['longitude'],p)
df['inpolygon'] = df.apply(f,axis=1) df['inpolygon'] = df.apply(f,axis=1)
mask = df['inpolygon'] == True if maxmin=='max':
b = (~df['inpolygon']).shift(-1)+df['inpolygon']
if df[mask].empty():
raise InvalidTrajectoryError
if maxmin == 'max':
time = df[mask]['time'].max()
else: else:
time = df[mask]['time'].min() b = (~df['inpolygon']).shift(1)+df['inpolygon']
if len(df[b==2]):
return df[b==2]['time'].min()
raise InvalidTrajectoryError("Trajectory doesn't go through path")
return time return 0
def crewnerdcourse(doc): def crewnerdcourse(doc):
courses = [] courses = []
@@ -200,8 +203,16 @@ def createcourse(
j = 0 j = 0
for point in p['points']: for point in p['points']:
if i==0 and j==0: if i==0 and j==0:
try:
loc = geolocator.reverse((point['latitude'],point['longitude'])) loc = geolocator.reverse((point['latitude'],point['longitude']))
country = loc.raw['address']['country'] country = loc.raw['address']['country']
if isinstance(country,unicode):
country = country.encode('utf8')
elif isinstance(country, str):
country = country.decode('utf8')
except GeocoderInsufficientPrivileges:
country = "Unknown Country"
c.country = country c.country = country
c.save() c.save()
obj = GeoPoint( obj = GeoPoint(
@@ -215,3 +226,74 @@ def createcourse(
i += 1 i += 1
return c return c
def coursetime_paths(data,paths):
entrytime = data['time'].max()
coursecompleted = False
# corner case - empty list of paths
if len(paths) == 0:
return 0,True
# end - just the Finish polygon
if len(paths) == 1:
try:
entrytime = time_in_path(data,paths[0],maxmin='min')
coursecompleted = True
print entrytime
except InvalidTrajectoryError:
entrytime = data['time'].max()
coursecompleted = False
return entrytime,coursecompleted
if len(paths) > 1:
try:
time = time_in_path(data, paths[0])
data = data[data['time']>time]
data['time'] = data['time']-time
print time
timenext, coursecompleted = coursetime_paths(data,paths[1:])
return time+timenext, coursecompleted
except InvalidTrajectoryError:
entrytime = data['time'].max()
coursecompleted = False
return entrytime, coursecompleted
def get_time_course(ws,course):
coursetimeseconds = 0.0
coursecompleted = 0
w = ws[0]
columns = ['time',' latitude',' longitude']
rowdata = dataprep.getsmallrowdata_db(
columns,
ids = [w.id],
doclean=True,
workstrokesonly=False
)
rowdata.rename(columns = {
' latitude':'latitude',
' longitude':'longitude',
}, inplace=True)
rowdata['time'] = rowdata['time']/1000.
rowdata.fillna(method='backfill',inplace=True)
rowdata['time'] = rowdata['time']-rowdata.ix[0,'time']
# we may want to expand the time (interpolate)
polygons = GeoPolygon.objects.filter(course=course)
paths = []
for polygon in polygons:
path = polygon_to_path(polygon)
paths.append(path)
coursetimeseconds,coursecompleted = coursetime_paths(rowdata,paths)
print 'course time?',coursetimeseconds
return coursetimeseconds,coursecompleted
+30 -1
View File
@@ -24,6 +24,7 @@ from rowers.models import (
import metrics import metrics
import numpy as np import numpy as np
import dataprep import dataprep
import courses
# Low Level functions - to be called by higher level methods # Low Level functions - to be called by higher level methods
def add_workouts_plannedsession(ws,ps,r): def add_workouts_plannedsession(ws,ps,r):
@@ -47,7 +48,7 @@ def add_workouts_plannedsession(ws,ps,r):
ids = [w.id for w in wold] + [w.id for w in ws] ids = [w.id for w in wold] + [w.id for w in ws]
ids = list(set(ids)) ids = list(set(ids))
if len(ids)>1 and ps.sessiontype == 'test': if len(ids)>1 and ps.sessiontype in ['test','coursetest']:
errors.append('For tests, you can only attach one workout') errors.append('For tests, you can only attach one workout')
return result,comments,errors return result,comments,errors
@@ -234,6 +235,34 @@ def is_session_complete_ws(ws,ps):
if not completiondate: if not completiondate:
completiondate = ws.reverse()[0].date completiondate = ws.reverse()[0].date
return ratio,'partial',completiondate return ratio,'partial',completiondate
elif ps.sessiontype == 'coursetest':
if ps.course:
coursetime,coursecompleted = courses.get_time_course(ws,ps.course)
if coursecompleted:
return 1.0,'completed',completiondate
else:
return ratio,'partial',completiondate
else:
if ps.criterium == 'exact':
if ratio == 1.0:
return ratio,'completed',completiondate
else:
if not completiondate:
completiondate = ws.reverse()[0].date
return ratio,'partial',completiondate
elif ps.criterium == 'minimum':
if ratio >= 1.0:
return ratio,'completed',completiondate
else:
if not completiondate:
completiondate = ws.reverse()[0].date
return ratio,'partial',completiondate
else:
if ratio>cratiomin and ratio<cratiomax:
return ratio,'completed',completiondate
else:
return ratio,'partial',completiondate
else: else:
if not completiondate: if not completiondate:
+11 -3
View File
@@ -289,6 +289,10 @@ def handle_sendemail_breakthrough(workoutid, useremail,
btvalues=pd.DataFrame().to_json(), btvalues=pd.DataFrame().to_json(),
**kwargs): **kwargs):
siteurl = SITE_URL
if debug:
siteurl = SITE_URL_DEV
# send email with attachment # send email with attachment
subject = "A breakthrough workout on rowsandall.com" subject = "A breakthrough workout on rowsandall.com"
message = "Dear " + userfirstname + ",\n" message = "Dear " + userfirstname + ",\n"
@@ -299,11 +303,11 @@ def handle_sendemail_breakthrough(workoutid, useremail,
message += "sustain for a given duration. For more, see this " message += "sustain for a given duration. For more, see this "
message += " article in the analytics blog:\n\n" message += " article in the analytics blog:\n\n"
message += " http://analytics.rowsandall.com/2017/06/17/how-do-we-calculate-critical-power/ \n\n" message += " http://analytics.rowsandall.com/2017/06/17/how-do-we-calculate-critical-power/ \n\n"
message += "Link to the workout http://rowsandall.com/rowers/workout/" message += "Link to the workout "+siteurl+"/rowers/workout/"
message += str(workoutid) message += str(workoutid)
message += "/edit\n\n" message += "/edit\n\n"
message += "To add the workout to your Ranking workouts and see the updated CP plot, click the following link:\n" message += "To add the workout to your Ranking workouts and see the updated CP plot, click the following link:\n"
message += "http://rowsandall.com/rowers/workout/" message += siteurl+"/rowers/workout/"
message += str(workoutid) message += str(workoutid)
message += "/updatecp\n\n" message += "/updatecp\n\n"
@@ -349,6 +353,10 @@ def handle_sendemail_hard(workoutid, useremail,
btvalues=pd.DataFrame().to_json(), btvalues=pd.DataFrame().to_json(),
debug=False,**kwargs): debug=False,**kwargs):
siteurl = SITE_URL
if debug:
siteurl = SITE_URL_DEV
# send email with attachment # send email with attachment
subject = "That was a pretty hard workout on rowsandall.com" subject = "That was a pretty hard workout on rowsandall.com"
message = "Dear " + userfirstname + ",\n" message = "Dear " + userfirstname + ",\n"
@@ -359,7 +367,7 @@ def handle_sendemail_hard(workoutid, useremail,
message += "sustain for a given duration. For more, see this " message += "sustain for a given duration. For more, see this "
message += " article in the analytics blog:\n\n" message += " article in the analytics blog:\n\n"
message += " http://analytics.rowsandall.com/2017/06/17/how-do-we-calculate-critical-power/ \n\n" message += " http://analytics.rowsandall.com/2017/06/17/how-do-we-calculate-critical-power/ \n\n"
message += "Link to the workout http://rowsandall.com/rowers/workout/" message += "Link to the workout: "+siteurl+"/rowers/workout/"
message += str(workoutid) message += str(workoutid)
message += "/edit\n\n" message += "/edit\n\n"
+1 -1
View File
@@ -46,7 +46,7 @@
</table> </table>
</div> </div>
<div id="right" class="grid_6 omega"> <div id="right" class="grid_6 omega">
{% if plannedsession.sessiontype == 'test' %} {% if plannedsession.sessiontype == 'test' or plannedsession.sessiontype == 'coursetest' %}
<h1>Ranking</h1> <h1>Ranking</h1>
<table class="listtable shortpadded" width="80%"> <table class="listtable shortpadded" width="80%">
<thead> <thead>
+5 -1
View File
@@ -8463,6 +8463,10 @@ def course_edit_view(request,id=0):
if form.is_valid(): if form.is_valid():
name = form.cleaned_data['name'] name = form.cleaned_data['name']
notes = form.cleaned_data['notes'] notes = form.cleaned_data['notes']
if isinstance(name,unicode):
name = name.encode('utf8')
elif isinstance(name, str):
name = name.decode('utf8')
course.name = name course.name = name
course.notes = notes course.notes = notes
@@ -12645,7 +12649,7 @@ def plannedsession_view(request,id=0,rowerid=0,
# ranking for test # ranking for test
ranking = [] ranking = []
if ps.sessiontype == 'test': if ps.sessiontype in ['test','coursetest']:
if ps.sessionmode == 'distance': if ps.sessionmode == 'distance':
rankws = Workout.objects.filter( rankws = Workout.objects.filter(
plannedsession=ps).order_by("duration") plannedsession=ps).order_by("duration")