Merge branch 'feature/segments' into develop
This commit is contained in:
@@ -22,6 +22,80 @@ import xml.etree.ElementTree as et
|
|||||||
from xml.etree.ElementTree import Element, SubElement, Comment, tostring
|
from xml.etree.ElementTree import Element, SubElement, Comment, tostring
|
||||||
from xml.dom import minidom
|
from xml.dom import minidom
|
||||||
|
|
||||||
|
from rowers.models import VirtualRace
|
||||||
|
|
||||||
|
# distance of course from lat_lon in km
|
||||||
|
def howfaris(lat_lon,course):
|
||||||
|
coords = course.coord
|
||||||
|
distance = geo_distance(lat_lon[0],lat_lon[1],coords[0],coords[1])[0]
|
||||||
|
|
||||||
|
return distance
|
||||||
|
|
||||||
|
#whatisnear = 150
|
||||||
|
|
||||||
|
# get nearest races
|
||||||
|
def getnearestraces(lat_lon,races,whatisnear=150):
|
||||||
|
newlist = []
|
||||||
|
counter = 0
|
||||||
|
for race in races:
|
||||||
|
if race.course is None: # pragma: no cover
|
||||||
|
newlist.append(race)
|
||||||
|
else:
|
||||||
|
c = race.course
|
||||||
|
coords = c.coord
|
||||||
|
distance = howfaris(lat_lon,c)
|
||||||
|
if distance < whatisnear:
|
||||||
|
newlist.append(race)
|
||||||
|
counter += 1
|
||||||
|
|
||||||
|
if counter>0:
|
||||||
|
races = newlist
|
||||||
|
else:
|
||||||
|
courseraces = races.exclude(course__isnull=True)
|
||||||
|
orders = [(c.id,howfaris(lat_lon,c.course)) for c in courseraces]
|
||||||
|
orders = sorted(orders,key = lambda tup:tup[1])
|
||||||
|
ids = [id for id,distance in orders[0:4]]
|
||||||
|
for id, distance in orders[5:]: # pragma: no cover
|
||||||
|
if distance<whatisnear:
|
||||||
|
ids.append(id)
|
||||||
|
|
||||||
|
for id in ids:
|
||||||
|
newlist.append(VirtualRace.objects.get(id=id))
|
||||||
|
races = newlist
|
||||||
|
|
||||||
|
return races
|
||||||
|
|
||||||
|
def getnearestcourses(lat_lon,courses,whatisnear=150,strict=False):
|
||||||
|
|
||||||
|
newlist = []
|
||||||
|
counter = 0
|
||||||
|
for c in courses:
|
||||||
|
coords = c.coord
|
||||||
|
distance = howfaris(lat_lon,c)
|
||||||
|
|
||||||
|
if distance < whatisnear:
|
||||||
|
newlist.append(c)
|
||||||
|
counter += 1
|
||||||
|
|
||||||
|
if counter>0:
|
||||||
|
courses = newlist
|
||||||
|
elif strict:
|
||||||
|
courses = newlist
|
||||||
|
else:
|
||||||
|
orders = [(c.id,howfaris(lat_lon,c)) for c in courses]
|
||||||
|
orders = sorted(orders,key = lambda tup:tup[1])
|
||||||
|
ids = [id for id,distance in orders[0:4]]
|
||||||
|
for id, distance in orders[5:]:
|
||||||
|
if distance<whatisnear: # pragma: no cover
|
||||||
|
ids.append(id)
|
||||||
|
|
||||||
|
for id in ids:
|
||||||
|
newlist.append(GeoCourse.objects.get(id=id))
|
||||||
|
courses = newlist
|
||||||
|
|
||||||
|
return courses
|
||||||
|
|
||||||
|
|
||||||
def prettify(elem):
|
def prettify(elem):
|
||||||
"""Return a pretty-printed XML string for the Element.
|
"""Return a pretty-printed XML string for the Element.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -309,6 +309,20 @@ def get_latlon_time(id):
|
|||||||
|
|
||||||
return df
|
return df
|
||||||
|
|
||||||
|
def workout_has_latlon(id):
|
||||||
|
latitude, longitude = get_latlon(id)
|
||||||
|
latmean = latitude.mean()
|
||||||
|
lonmean = longitude.mean()
|
||||||
|
|
||||||
|
if latmean == 0 and lonmean == 0:
|
||||||
|
return False,latmean,lonmean
|
||||||
|
|
||||||
|
if latitude.std() > 0 and longitude.std() > 0:
|
||||||
|
return True, latmean,lonmean
|
||||||
|
|
||||||
|
return False, latmean,lonmean
|
||||||
|
|
||||||
|
|
||||||
def workout_summary_to_df(
|
def workout_summary_to_df(
|
||||||
rower,
|
rower,
|
||||||
startdate=datetime.datetime(1970,1,1),
|
startdate=datetime.datetime(1970,1,1),
|
||||||
|
|||||||
+12
-5
@@ -1128,27 +1128,28 @@ class PlanSelectForm(forms.Form):
|
|||||||
"price","shortname"
|
"price","shortname"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class CourseSelectForm(forms.Form):
|
class CourseSelectForm(forms.Form):
|
||||||
course = forms.ModelChoiceField(queryset=GeoCourse.objects.filter())
|
course = forms.ModelChoiceField(queryset=GeoCourse.objects.filter())
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs): # pragma: no cover
|
def __init__(self, *args, **kwargs): # pragma: no cover
|
||||||
course = kwargs.pop('course',None)
|
course = kwargs.pop('course',None)
|
||||||
manager = kwargs.pop('manager',None)
|
manager = kwargs.pop('manager',None)
|
||||||
|
choices = kwargs.pop('choices',[])
|
||||||
super(CourseSelectForm,self).__init__(*args,**kwargs)
|
super(CourseSelectForm,self).__init__(*args,**kwargs)
|
||||||
|
if len(choices)>0:
|
||||||
|
self.fields['course'].queryset = GeoCourse.objects.filter(id__in=[c.id for c in choices])
|
||||||
if course is not None:
|
if course is not None:
|
||||||
d_min = 0.5*course.distance
|
d_min = 0.5*course.distance
|
||||||
d_max = 2*course.distance
|
d_max = 2*course.distance
|
||||||
country = course.country
|
country = course.country
|
||||||
countries = ['unknown',country]
|
countries = ['unknown',country]
|
||||||
print(countries)
|
|
||||||
self.fields['course'].queryset = self.fields['course'].queryset.filter(
|
self.fields['course'].queryset = self.fields['course'].queryset.filter(
|
||||||
distance__gt = d_min,distance__lt = d_max,
|
distance__gt = d_min,distance__lt = d_max,
|
||||||
country__in = countries
|
country__in = countries
|
||||||
).exclude(id=course.id)
|
).exclude(id=course.id)
|
||||||
if manager is not None:
|
if manager is not None:
|
||||||
self.fields['course'].queryset = self.fields['course'].queryset.filter(manager=manager)
|
self.fields['course'].queryset = self.fields['course'].queryset.filter(manager=manager)
|
||||||
print(self.fields['course'].queryset)
|
|
||||||
|
|
||||||
class WorkoutSingleSelectForm(forms.Form):
|
class WorkoutSingleSelectForm(forms.Form):
|
||||||
workout = forms.ModelChoiceField(
|
workout = forms.ModelChoiceField(
|
||||||
@@ -1471,17 +1472,21 @@ class RaceResultFilterForm(forms.Form):
|
|||||||
entrycategory = forms.MultipleChoiceField(
|
entrycategory = forms.MultipleChoiceField(
|
||||||
choices = [],
|
choices = [],
|
||||||
label = 'Groups',
|
label = 'Groups',
|
||||||
widget=forms.CheckboxSelectMultiple()
|
widget=forms.CheckboxSelectMultiple(),
|
||||||
|
required=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
if 'records' in kwargs:
|
|
||||||
records = kwargs.pop('records',None)
|
records = kwargs.pop('records',None)
|
||||||
|
groups = kwargs.pop('groups',None)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
super(RaceResultFilterForm,self).__init__(*args,**kwargs)
|
super(RaceResultFilterForm,self).__init__(*args,**kwargs)
|
||||||
|
|
||||||
if records:
|
if records:
|
||||||
# group
|
# group
|
||||||
|
if groups:
|
||||||
thecategories = [record.entrycategory for record in records]
|
thecategories = [record.entrycategory for record in records]
|
||||||
thecategories = list(set(thecategories))
|
thecategories = list(set(thecategories))
|
||||||
if len(thecategories) <= 1:
|
if len(thecategories) <= 1:
|
||||||
@@ -1495,6 +1500,8 @@ class RaceResultFilterForm(forms.Form):
|
|||||||
)
|
)
|
||||||
self.fields['entrycategory'].choices = categorychoices
|
self.fields['entrycategory'].choices = categorychoices
|
||||||
self.fields['entrycategory'].initial = [cat[0] for cat in categorychoices]
|
self.fields['entrycategory'].initial = [cat[0] for cat in categorychoices]
|
||||||
|
else:
|
||||||
|
del self.fields['entrycategory']
|
||||||
|
|
||||||
# sex
|
# sex
|
||||||
thesexes = [record.sex for record in records]
|
thesexes = [record.sex for record in records]
|
||||||
|
|||||||
+236
-31
@@ -66,7 +66,7 @@ from rowers.courses import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from rowers import mytypes
|
from rowers import mytypes
|
||||||
from rowers.models import course_spline
|
from rowers.models import course_spline,VirtualRaceResult
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import math
|
import math
|
||||||
@@ -2419,47 +2419,185 @@ def course_map(course):
|
|||||||
|
|
||||||
return script,div
|
return script,div
|
||||||
|
|
||||||
def leaflet_chart(lat,lon,name=""):
|
|
||||||
if lat.empty or lon.empty: # pragma: no cover
|
|
||||||
return [0,"invalid coordinate data"]
|
|
||||||
|
|
||||||
|
def get_map_script_course(
|
||||||
|
latmean,
|
||||||
|
lonmean,
|
||||||
|
latbegin,
|
||||||
|
latend,
|
||||||
|
longbegin,
|
||||||
|
longend,
|
||||||
|
scoordinates,
|
||||||
|
course,
|
||||||
|
):
|
||||||
|
latmean,lonmean,coordinates = course_coord_center(course)
|
||||||
|
lat_min, lat_max, long_min, long_max = course_coord_maxmin(course)
|
||||||
|
|
||||||
# Throw out 0,0
|
coordinates = course_spline(coordinates)
|
||||||
df = pd.DataFrame({
|
|
||||||
'lat':lat,
|
|
||||||
'lon':lon
|
|
||||||
})
|
|
||||||
|
|
||||||
df = df.replace(0,np.nan)
|
|
||||||
df = df.loc[(df!=0).any(axis=1)]
|
|
||||||
df.fillna(method='bfill',axis=0,inplace=True)
|
|
||||||
df.fillna(method='ffill',axis=0,inplace=True)
|
|
||||||
lat = df['lat']
|
|
||||||
lon = df['lon']
|
|
||||||
if lat.empty or lon.empty: # pragma: no cover
|
|
||||||
return [0,"invalid coordinate data"]
|
|
||||||
|
|
||||||
latmean = lat.mean()
|
|
||||||
lonmean = lon.mean()
|
|
||||||
|
|
||||||
latbegin = lat[lat.index[0]]
|
|
||||||
longbegin = lon[lon.index[0]]
|
|
||||||
latend = lat[lat.index[-1]]
|
|
||||||
longend = lon[lon.index[-1]]
|
|
||||||
|
|
||||||
coordinates = zip(lat,lon)
|
|
||||||
|
|
||||||
scoordinates = "["
|
scoordinates = "["
|
||||||
|
|
||||||
for x,y in coordinates:
|
for index,row in coordinates.iterrows():
|
||||||
scoordinates += """[{x},{y}],
|
scoordinates += """[{x},{y}],
|
||||||
""".format(
|
""".format(
|
||||||
x=x,
|
x=row['latitude'],
|
||||||
y=y
|
y=row['longitude']
|
||||||
)
|
)
|
||||||
|
|
||||||
scoordinates +="]"
|
scoordinates +="]"
|
||||||
|
|
||||||
|
polygons = GeoPolygon.objects.filter(course=course).order_by("order_in_course")
|
||||||
|
|
||||||
|
plabels = ''
|
||||||
|
|
||||||
|
for p in polygons:
|
||||||
|
coords = polygon_coord_center(p)
|
||||||
|
|
||||||
|
plabels += """
|
||||||
|
var marker = L.marker([{latbegin}, {longbegin}]).addTo(mymap);
|
||||||
|
marker.bindPopup("<b>{name}</b>");
|
||||||
|
|
||||||
|
""".format(
|
||||||
|
latbegin = coords[0],
|
||||||
|
longbegin = coords[1],
|
||||||
|
name = p.name
|
||||||
|
)
|
||||||
|
|
||||||
|
pcoordinates = """[
|
||||||
|
"""
|
||||||
|
|
||||||
|
for p in polygons:
|
||||||
|
pcoordinates += """[
|
||||||
|
["""
|
||||||
|
|
||||||
|
points = GeoPoint.objects.filter(polygon=p).order_by("order_in_poly")
|
||||||
|
|
||||||
|
for pt in points:
|
||||||
|
pcoordinates += "[{x},{y}],".format(
|
||||||
|
x = pt.latitude,
|
||||||
|
y = pt.longitude
|
||||||
|
)
|
||||||
|
|
||||||
|
# remove last comma
|
||||||
|
pcoordinates = pcoordinates[:-1]
|
||||||
|
pcoordinates += """]
|
||||||
|
],
|
||||||
|
"""
|
||||||
|
|
||||||
|
pcoordinates += """
|
||||||
|
]"""
|
||||||
|
|
||||||
|
script = """
|
||||||
|
<script>
|
||||||
|
|
||||||
|
|
||||||
|
var streets = L.tileLayer(
|
||||||
|
'https://api.mapbox.com/styles/v1/{{id}}/tiles/{{z}}/{{x}}/{{y}}?access_token={{accessToken}}', {{
|
||||||
|
attribution: '© <a href="https://www.mapbox.com/about/maps/">Mapbox</a> © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> <strong><a href="https://www.mapbox.com/map-feedback/" target="_blank">Improve this map</a></strong>',
|
||||||
|
tileSize: 512,
|
||||||
|
maxZoom: 18,
|
||||||
|
zoomOffset: -1,
|
||||||
|
id: 'mapbox/streets-v11',
|
||||||
|
accessToken: 'pk.eyJ1Ijoic2FuZGVycm9vc2VuZGFhbCIsImEiOiJjajY3aTRkeWQwNmx6MzJvMTN3andlcnBlIn0.MFG8Xt0kDeSA9j7puZQ9hA'
|
||||||
|
}}
|
||||||
|
),
|
||||||
|
|
||||||
|
satellite = L.tileLayer(
|
||||||
|
'https://api.mapbox.com/styles/v1/{{id}}/tiles/{{z}}/{{x}}/{{y}}?access_token={{accessToken}}', {{
|
||||||
|
attribution: '© <a href="https://www.mapbox.com/about/maps/">Mapbox</a> © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> <strong><a href="https://www.mapbox.com/map-feedback/" target="_blank">Improve this map</a></strong>',
|
||||||
|
tileSize: 512,
|
||||||
|
maxZoom: 18,
|
||||||
|
zoomOffset: -1,
|
||||||
|
id: 'mapbox/satellite-v9',
|
||||||
|
accessToken: 'pk.eyJ1Ijoic2FuZGVycm9vc2VuZGFhbCIsImEiOiJjajY3aTRkeWQwNmx6MzJvMTN3andlcnBlIn0.MFG8Xt0kDeSA9j7puZQ9hA'
|
||||||
|
}}
|
||||||
|
),
|
||||||
|
|
||||||
|
outdoors = L.tileLayer(
|
||||||
|
'https://api.mapbox.com/styles/v1/{{id}}/tiles/{{z}}/{{x}}/{{y}}?access_token={{accessToken}}', {{
|
||||||
|
attribution: '© <a href="https://www.mapbox.com/about/maps/">Mapbox</a> © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> <strong><a href="https://www.mapbox.com/map-feedback/" target="_blank">Improve this map</a></strong>',
|
||||||
|
tileSize: 512,
|
||||||
|
maxZoom: 18,
|
||||||
|
zoomOffset: -1,
|
||||||
|
id: 'mapbox/outdoors-v11',
|
||||||
|
accessToken: 'pk.eyJ1Ijoic2FuZGVycm9vc2VuZGFhbCIsImEiOiJjajY3aTRkeWQwNmx6MzJvMTN3andlcnBlIn0.MFG8Xt0kDeSA9j7puZQ9hA'
|
||||||
|
}}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var mymap = L.map('map_canvas', {{
|
||||||
|
center: [{latmean}, {lonmean}],
|
||||||
|
zoom: 13,
|
||||||
|
layers: [streets, satellite]
|
||||||
|
}}).setView([{latmean},{lonmean}], 13);
|
||||||
|
|
||||||
|
var navionics = new JNC.Leaflet.NavionicsOverlay({{
|
||||||
|
navKey: 'Navionics_webapi_03205',
|
||||||
|
chartType: JNC.NAVIONICS_CHARTS.NAUTICAL,
|
||||||
|
isTransparent: true,
|
||||||
|
zIndex: 1
|
||||||
|
}});
|
||||||
|
|
||||||
|
|
||||||
|
var osmUrl2='http://tiles.openseamap.org/seamark/{{z}}/{{x}}/{{y}}.png';
|
||||||
|
var osmUrl='http://{{s}}.tile.openstreetmap.org/{{z}}/{{x}}/{{y}}.png';
|
||||||
|
|
||||||
|
|
||||||
|
//create two TileLayer
|
||||||
|
var nautical=new L.TileLayer(osmUrl,{{
|
||||||
|
maxZoom:18}});
|
||||||
|
|
||||||
|
|
||||||
|
L.control.layers({{
|
||||||
|
"Streets": streets,
|
||||||
|
"Satellite": satellite,
|
||||||
|
"Outdoors": outdoors,
|
||||||
|
"Nautical": nautical,
|
||||||
|
}},{{
|
||||||
|
"Navionics":navionics,
|
||||||
|
}}).addTo(mymap);
|
||||||
|
|
||||||
|
var latlongs = {scoordinates}
|
||||||
|
var polyline = L.polyline(latlongs, {{color:'red'}}).addTo(mymap)
|
||||||
|
mymap.fitBounds(polyline.getBounds())
|
||||||
|
|
||||||
|
var platlongs = {pcoordinates}
|
||||||
|
var polygons = L.polygon(platlongs, {{color:'blue'}}).addTo(mymap)
|
||||||
|
|
||||||
|
{plabels}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var latlongs = {scoordinates}
|
||||||
|
var polyline = L.polyline(latlongs, {{color:'red'}}).addTo(mymap)
|
||||||
|
mymap.fitBounds(polyline.getBounds())
|
||||||
|
|
||||||
|
</script>
|
||||||
|
""".format(
|
||||||
|
latmean=latmean,
|
||||||
|
lonmean=lonmean,
|
||||||
|
latbegin = latbegin,
|
||||||
|
latend=latend,
|
||||||
|
longbegin=longbegin,
|
||||||
|
longend=longend,
|
||||||
|
scoordinates=scoordinates,
|
||||||
|
pcoordinates=pcoordinates,
|
||||||
|
plabels=plabels
|
||||||
|
)
|
||||||
|
|
||||||
|
return script
|
||||||
|
|
||||||
|
|
||||||
|
def get_map_script(
|
||||||
|
latmean,
|
||||||
|
lonmean,
|
||||||
|
latbegin,
|
||||||
|
latend,
|
||||||
|
longbegin,
|
||||||
|
longend,
|
||||||
|
scoordinates,
|
||||||
|
):
|
||||||
script = """
|
script = """
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
@@ -2551,6 +2689,73 @@ def leaflet_chart(lat,lon,name=""):
|
|||||||
scoordinates=scoordinates,
|
scoordinates=scoordinates,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return script
|
||||||
|
|
||||||
|
def leaflet_chart(lat,lon,name="",raceresult=0):
|
||||||
|
if lat.empty or lon.empty: # pragma: no cover
|
||||||
|
return [0,"invalid coordinate data"]
|
||||||
|
|
||||||
|
|
||||||
|
# Throw out 0,0
|
||||||
|
df = pd.DataFrame({
|
||||||
|
'lat':lat,
|
||||||
|
'lon':lon
|
||||||
|
})
|
||||||
|
|
||||||
|
df = df.replace(0,np.nan)
|
||||||
|
df = df.loc[(df!=0).any(axis=1)]
|
||||||
|
df.fillna(method='bfill',axis=0,inplace=True)
|
||||||
|
df.fillna(method='ffill',axis=0,inplace=True)
|
||||||
|
lat = df['lat']
|
||||||
|
lon = df['lon']
|
||||||
|
if lat.empty or lon.empty: # pragma: no cover
|
||||||
|
return [0,"invalid coordinate data"]
|
||||||
|
|
||||||
|
latmean = lat.mean()
|
||||||
|
lonmean = lon.mean()
|
||||||
|
|
||||||
|
latbegin = lat[lat.index[0]]
|
||||||
|
longbegin = lon[lon.index[0]]
|
||||||
|
latend = lat[lat.index[-1]]
|
||||||
|
longend = lon[lon.index[-1]]
|
||||||
|
|
||||||
|
coordinates = zip(lat,lon)
|
||||||
|
|
||||||
|
scoordinates = "["
|
||||||
|
|
||||||
|
for x,y in coordinates:
|
||||||
|
scoordinates += """[{x},{y}],
|
||||||
|
""".format(
|
||||||
|
x=x,
|
||||||
|
y=y
|
||||||
|
)
|
||||||
|
|
||||||
|
scoordinates += "]"
|
||||||
|
|
||||||
|
if raceresult == 0:
|
||||||
|
script = get_map_script(
|
||||||
|
latmean,
|
||||||
|
lonmean,
|
||||||
|
latbegin,
|
||||||
|
latend,
|
||||||
|
longbegin,
|
||||||
|
longend,
|
||||||
|
scoordinates,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
record = VirtualRaceResult.objects.get(id=raceresult)
|
||||||
|
course = record.course
|
||||||
|
script = get_map_script_course(
|
||||||
|
latmean,
|
||||||
|
lonmean,
|
||||||
|
latbegin,
|
||||||
|
latend,
|
||||||
|
longbegin,
|
||||||
|
longend,
|
||||||
|
scoordinates,
|
||||||
|
course,
|
||||||
|
)
|
||||||
|
|
||||||
div = """
|
div = """
|
||||||
<div id="map_canvas" style="width: 100%; height: 400px;"><p> </p></div>
|
<div id="map_canvas" style="width: 100%; height: 400px;"><p> </p></div>
|
||||||
"""
|
"""
|
||||||
|
|||||||
+17
-2
@@ -3408,7 +3408,10 @@ class VirtualRaceResult(models.Model):
|
|||||||
verbose_name="Adaptive Class")
|
verbose_name="Adaptive Class")
|
||||||
skillclass = models.CharField(default="Open",max_length=50,
|
skillclass = models.CharField(default="Open",max_length=50,
|
||||||
verbose_name="Skill Class")
|
verbose_name="Skill Class")
|
||||||
race = models.ForeignKey(VirtualRace,on_delete=models.CASCADE,related_name='entries')
|
race = models.ForeignKey(VirtualRace,on_delete=models.CASCADE,related_name='entries',
|
||||||
|
blank=True,null=True)
|
||||||
|
course = models.ForeignKey(GeoCourse,on_delete=models.CASCADE,null=True,blank=True)
|
||||||
|
|
||||||
duration = models.TimeField(default=datetime.time(1,0))
|
duration = models.TimeField(default=datetime.time(1,0))
|
||||||
distance = models.IntegerField(default=0)
|
distance = models.IntegerField(default=0)
|
||||||
points = models.FloatField(default=0)
|
points = models.FloatField(default=0)
|
||||||
@@ -3448,6 +3451,10 @@ class VirtualRaceResult(models.Model):
|
|||||||
return False
|
return False
|
||||||
if self.skillclass != other.skillclass:
|
if self.skillclass != other.skillclass:
|
||||||
return False
|
return False
|
||||||
|
if self.race is None and other.race is not None:
|
||||||
|
return False
|
||||||
|
if self.race is not None and other.race is None:
|
||||||
|
return False
|
||||||
if self.race != other.race:
|
if self.race != other.race:
|
||||||
return False
|
return False
|
||||||
if self.boatclass != other.boatclass:
|
if self.boatclass != other.boatclass:
|
||||||
@@ -3466,6 +3473,10 @@ class VirtualRaceResult(models.Model):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if self.race and not self.course:
|
||||||
|
self.course = self.race.course
|
||||||
|
return super(VirtualRaceResult, self).save(*args, **kwargs)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
rr = Rower.objects.get(id=self.userid)
|
rr = Rower.objects.get(id=self.userid)
|
||||||
@@ -3522,7 +3533,7 @@ class IndoorVirtualRaceResult(models.Model):
|
|||||||
verbose_name="Adaptive Class")
|
verbose_name="Adaptive Class")
|
||||||
skillclass = models.CharField(default="Open",max_length=50,
|
skillclass = models.CharField(default="Open",max_length=50,
|
||||||
verbose_name="Skill Class")
|
verbose_name="Skill Class")
|
||||||
race = models.ForeignKey(VirtualRace,on_delete=models.CASCADE)
|
race = models.ForeignKey(VirtualRace,on_delete=models.CASCADE,null=True,blank=True)
|
||||||
duration = models.TimeField(default=datetime.time(1,0))
|
duration = models.TimeField(default=datetime.time(1,0))
|
||||||
distance = models.IntegerField(default=0)
|
distance = models.IntegerField(default=0)
|
||||||
referencespeed = models.FloatField(default=5.0)
|
referencespeed = models.FloatField(default=5.0)
|
||||||
@@ -3554,6 +3565,10 @@ class IndoorVirtualRaceResult(models.Model):
|
|||||||
endsecond = models.FloatField(default=0)
|
endsecond = models.FloatField(default=0)
|
||||||
|
|
||||||
def isduplicate(self,other): # pragma: no cover
|
def isduplicate(self,other): # pragma: no cover
|
||||||
|
if self.race is None and other.race is not None:
|
||||||
|
return False
|
||||||
|
if self.race is not None and other.race is None:
|
||||||
|
return False
|
||||||
if self.userid != other.userid:
|
if self.userid != other.userid:
|
||||||
return False
|
return False
|
||||||
if self.weightcategory != other.weightcategory:
|
if self.weightcategory != other.weightcategory:
|
||||||
|
|||||||
@@ -540,6 +540,10 @@ def handle_check_race_course(self,
|
|||||||
if 'mode' in kwargs: # pragma: no cover
|
if 'mode' in kwargs: # pragma: no cover
|
||||||
mode = kwargs['mode']
|
mode = kwargs['mode']
|
||||||
|
|
||||||
|
summary = False
|
||||||
|
if 'summary' in kwargs:
|
||||||
|
summary = kwargs['summary']
|
||||||
|
|
||||||
columns = ['time',' latitude',' longitude','cum_dist']
|
columns = ['time',' latitude',' longitude','cum_dist']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -599,6 +603,7 @@ def handle_check_race_course(self,
|
|||||||
courseid=courseid
|
courseid=courseid
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
with engine.connect() as conn, conn.begin():
|
with engine.connect() as conn, conn.begin():
|
||||||
result = conn.execute(query)
|
result = conn.execute(query)
|
||||||
polygons = result.fetchall()
|
polygons = result.fetchall()
|
||||||
@@ -733,6 +738,33 @@ def handle_check_race_course(self,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
with engine.connect() as conn, conn.begin():
|
||||||
|
result = conn.execute(query)
|
||||||
|
|
||||||
|
if summary:
|
||||||
|
|
||||||
|
try:
|
||||||
|
row = rdata(csvfile=f1)
|
||||||
|
except IOError: # pragma: no cover
|
||||||
|
try:
|
||||||
|
row = rdata(csvfile=f1 + '.csv')
|
||||||
|
except IOError: # pragma: no cover
|
||||||
|
try:
|
||||||
|
row = rdata(csvfile=f1 + '.gz')
|
||||||
|
except IOError: # pragma: no cover
|
||||||
|
pass
|
||||||
|
|
||||||
|
vals, units, typ = row.updateinterval_metric(
|
||||||
|
' AverageBoatSpeed (m/s)',0.1,mode='larger',
|
||||||
|
debug=False,smoothwindow=15.,
|
||||||
|
activewindow=[startsecond,endsecond]
|
||||||
|
)
|
||||||
|
|
||||||
|
summary = row.allstats()
|
||||||
|
row.write_csv(f1,gzip=True)
|
||||||
|
|
||||||
|
query = "UPDATE `rowers_workout` SET `summary` = '%s' WHERE `id` = %s" % (summary, workoutid)
|
||||||
|
|
||||||
with engine.connect() as conn, conn.begin():
|
with engine.connect() as conn, conn.begin():
|
||||||
result = conn.execute(query)
|
result = conn.execute(query)
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,71 @@
|
|||||||
{{ mapscript|safe }}
|
{{ mapscript|safe }}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
{% if records %}
|
||||||
|
<li class="grid_4">
|
||||||
|
<h2>Course Results</h2>
|
||||||
|
<table class="listtable shortpadded">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Boat</th>
|
||||||
|
<th>Class</th>
|
||||||
|
<th>Age</th>
|
||||||
|
<th>Gender</th>
|
||||||
|
<th>Weight Category</th>
|
||||||
|
<th>Adaptive</th>
|
||||||
|
<th>Time</th>
|
||||||
|
<th>Distance</th>
|
||||||
|
<th>Date</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for record in records %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ record.username }}</td>
|
||||||
|
<td>{{ record.boattype }}</td>
|
||||||
|
<td>{{ record.boatclass }}</td>
|
||||||
|
<td>{{ record.age }}</td>
|
||||||
|
<td>{{ record.sex }}</td>
|
||||||
|
<td>{{ record.weightcategory }}</td>
|
||||||
|
<td>
|
||||||
|
{% if record.adaptiveclass == 'None' %}
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
{{ record.adaptiveclass }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>{{ record.duration |durationprint:"%H:%M:%S.%f" }}</td>
|
||||||
|
<td>{{ record.distance }} m</td>
|
||||||
|
<td>{{ record.workoutid|workoutdate }}</td>
|
||||||
|
<td>
|
||||||
|
<a title="Details" href="/rowers/workout/{{ record.workoutid|encode }}/view/entry/{{ record.id }}/">
|
||||||
|
<i class="fas fa-search-plus fa-fw"></i></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if form %}
|
||||||
|
<li class="grid_4">
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<h2>Filter Results</h2>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
<form id="result_filter_form", method="post">
|
||||||
|
<table>
|
||||||
|
{{ form.as_table }}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="submit" value="Submit">
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -36,11 +36,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td> {{ course.country }} </td>
|
<td> {{ course.country }} </td>
|
||||||
<td>
|
<td>
|
||||||
{% if course.manager.user == user %}
|
|
||||||
<a href="/rowers/courses/{{ course.id }}/edit/">{{ course.name }}</a>
|
|
||||||
{% else %}
|
|
||||||
<a href="/rowers/courses/{{ course.id }}/">{{ course.name }}</a>
|
<a href="/rowers/courses/{{ course.id }}/">{{ course.name }}</a>
|
||||||
{% endif %}
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ course.distance }} m
|
{{ course.distance }} m
|
||||||
|
|||||||
@@ -158,7 +158,7 @@
|
|||||||
|
|
||||||
<p>A typical interval is described as "<b>10min/5min</b>", with the work part before the "<b>/</b>" and the rest part after it. A zero rest can be omitted, so a single 1000m piece could be described either as "<b>1km</b>" or "<b>1000m</b>". The basic units can be combined with "<b>+</b>" and "<b>Nx</b>". You can use parentheses as in the example below.</p>
|
<p>A typical interval is described as "<b>10min/5min</b>", with the work part before the "<b>/</b>" and the rest part after it. A zero rest can be omitted, so a single 1000m piece could be described either as "<b>1km</b>" or "<b>1000m</b>". The basic units can be combined with "<b>+</b>" and "<b>Nx</b>". You can use parentheses as in the example below.</p>
|
||||||
|
|
||||||
<p>Here are a few examples.</p>
|
<p>Here are a few examples</p>
|
||||||
<table class="listtable" width=100%>
|
<table class="listtable" width=100%>
|
||||||
<tr>
|
<tr>
|
||||||
<td>8x500m/2min</td><td>8 times 500m with 2 minutes rest</td>
|
<td>8x500m/2min</td><td>8 times 500m with 2 minutes rest</td>
|
||||||
@@ -182,6 +182,23 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</li>
|
</li>
|
||||||
|
{% if courses %}
|
||||||
|
<li>
|
||||||
|
<h1>Interval by Course</h1>
|
||||||
|
<p>
|
||||||
|
This functionality allows you to record a time on a set course that you've rowed during the workout.
|
||||||
|
The summary will be updated to show time on course, and you can compare this with other
|
||||||
|
attempts.
|
||||||
|
</p>
|
||||||
|
<form ecntype="multipart/form-data" method="post">
|
||||||
|
<table>
|
||||||
|
{{ courseselectform.as_table }}
|
||||||
|
</table>
|
||||||
|
{% csrf_token %}
|
||||||
|
<input class="button" type="submit" value="Select Course">
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
@@ -106,6 +106,16 @@ $('#id_workouttype').change();
|
|||||||
<a href="/rowers/workout/{{ workout.id|encode }}/">https://rowsandall.com/rowers/workout/{{ workout.id|encode }}/</a>
|
<a href="/rowers/workout/{{ workout.id|encode }}/">https://rowsandall.com/rowers/workout/{{ workout.id|encode }}/</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{% for course in courses %}
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
Timed Course:
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<a href="/rowers/courses/{{ course.id }}"/>{{ course }}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
</li>
|
</li>
|
||||||
<li class="grid_2">
|
<li class="grid_2">
|
||||||
|
|||||||
@@ -104,6 +104,16 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% for course in courses %}
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
Timed Course:
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<a href="/rowers/courses/{{ course.id }}"/>{{ course }}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
</li>
|
</li>
|
||||||
<li class="grid_2">
|
<li class="grid_2">
|
||||||
|
|||||||
@@ -43,6 +43,14 @@ from django.template.defaultfilters import stringfilter
|
|||||||
|
|
||||||
from six import string_types
|
from six import string_types
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def workoutdate(id):
|
||||||
|
try:
|
||||||
|
w = Workout.objects.get(id=id)
|
||||||
|
return w.date
|
||||||
|
except Workout.DoesNotExist:
|
||||||
|
return 'unknown'
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def isfollower(user,id):
|
def isfollower(user,id):
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ except NameError:
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from pandas.core.common import SettingWithCopyWarning
|
from pandas.core.common import SettingWithCopyWarning
|
||||||
|
from rowers.courses import howfaris
|
||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
warnings.filterwarnings("error",
|
warnings.filterwarnings("error",
|
||||||
|
|||||||
+56
-66
@@ -11,72 +11,7 @@ from django.contrib.gis.geoip2 import GeoIP2
|
|||||||
from django import forms
|
from django import forms
|
||||||
from rowers.plannedsessions import timefield_to_seconds_duration
|
from rowers.plannedsessions import timefield_to_seconds_duration
|
||||||
|
|
||||||
# distance of course from lat_lon in km
|
from rowers.courses import getnearestraces, getnearestcourses
|
||||||
def howfaris(lat_lon,course):
|
|
||||||
coords = course.coord
|
|
||||||
distance = geo_distance(lat_lon[0],lat_lon[1],coords[0],coords[1])[0]
|
|
||||||
|
|
||||||
return distance
|
|
||||||
|
|
||||||
whatisnear = 150
|
|
||||||
|
|
||||||
# get nearest races
|
|
||||||
def getnearestraces(lat_lon,races):
|
|
||||||
newlist = []
|
|
||||||
counter = 0
|
|
||||||
for race in races:
|
|
||||||
if race.course is None: # pragma: no cover
|
|
||||||
newlist.append(race)
|
|
||||||
else:
|
|
||||||
c = race.course
|
|
||||||
coords = c.coord
|
|
||||||
distance = howfaris(lat_lon,c)
|
|
||||||
if distance < whatisnear:
|
|
||||||
newlist.append(race)
|
|
||||||
counter += 1
|
|
||||||
|
|
||||||
if counter>0:
|
|
||||||
races = newlist
|
|
||||||
else:
|
|
||||||
courseraces = races.exclude(course__isnull=True)
|
|
||||||
orders = [(c.id,howfaris(lat_lon,c.course)) for c in courseraces]
|
|
||||||
orders = sorted(orders,key = lambda tup:tup[1])
|
|
||||||
ids = [id for id,distance in orders[0:4]]
|
|
||||||
for id, distance in orders[5:]: # pragma: no cover
|
|
||||||
if distance<whatisnear:
|
|
||||||
ids.append(id)
|
|
||||||
|
|
||||||
for id in ids:
|
|
||||||
newlist.append(VirtualRace.objects.get(id=id))
|
|
||||||
races = newlist
|
|
||||||
|
|
||||||
return races
|
|
||||||
|
|
||||||
def getnearestcourses(lat_lon,courses):
|
|
||||||
newlist = []
|
|
||||||
counter = 0
|
|
||||||
for c in courses:
|
|
||||||
coords = c.coord
|
|
||||||
distance = howfaris(lat_lon,c)
|
|
||||||
if distance < whatisnear:
|
|
||||||
newlist.append(c)
|
|
||||||
counter += 1
|
|
||||||
|
|
||||||
if counter>0:
|
|
||||||
courses = newlist
|
|
||||||
else:
|
|
||||||
orders = [(c.id,howfaris(lat_lon,c)) for c in courses]
|
|
||||||
orders = sorted(orders,key = lambda tup:tup[1])
|
|
||||||
ids = [id for id,distance in orders[0:4]]
|
|
||||||
for id, distance in orders[5:]:
|
|
||||||
if distance<whatisnear: # pragma: no cover
|
|
||||||
ids.append(id)
|
|
||||||
|
|
||||||
for id in ids:
|
|
||||||
newlist.append(GeoCourse.objects.get(id=id))
|
|
||||||
courses = newlist
|
|
||||||
|
|
||||||
return courses
|
|
||||||
|
|
||||||
# List Courses
|
# List Courses
|
||||||
def courses_view(request):
|
def courses_view(request):
|
||||||
@@ -291,6 +226,59 @@ def course_view(request,id=0):
|
|||||||
|
|
||||||
script,div = course_map(course)
|
script,div = course_map(course)
|
||||||
|
|
||||||
|
# get results
|
||||||
|
records = VirtualRaceResult.objects.filter(
|
||||||
|
course=course,
|
||||||
|
workoutid__isnull=False,
|
||||||
|
coursecompleted=True).order_by("duration","-distance")
|
||||||
|
|
||||||
|
form = RaceResultFilterForm(records=records,groups=False)
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = RaceResultFilterForm(request.POST,records=records,groups=False)
|
||||||
|
if form.is_valid():
|
||||||
|
cd = form.cleaned_data
|
||||||
|
try:
|
||||||
|
sex = cd['sex']
|
||||||
|
except KeyError:
|
||||||
|
sex = ['female','male','mixed']
|
||||||
|
|
||||||
|
try:
|
||||||
|
boattype = cd['boattype']
|
||||||
|
except KeyError:
|
||||||
|
boattype = mytypes.waterboattype
|
||||||
|
|
||||||
|
try:
|
||||||
|
boatclass = cd['boatclass']
|
||||||
|
except KeyError:
|
||||||
|
boatclass = [t for t in mytypes.otwtypes]
|
||||||
|
|
||||||
|
age_min = cd['age_min']
|
||||||
|
age_max = cd['age_max']
|
||||||
|
|
||||||
|
try:
|
||||||
|
weightcategory = cd['weightcategory']
|
||||||
|
except KeyError:
|
||||||
|
weightcategory = ['hwt','lwt']
|
||||||
|
|
||||||
|
try:
|
||||||
|
adaptiveclass = cd['adaptiveclass']
|
||||||
|
except KeyError:
|
||||||
|
adaptiveclass = ['None','PR1','PR2','PR3','FES']
|
||||||
|
|
||||||
|
records = VirtualRaceResult.objects.filter(
|
||||||
|
course=course,
|
||||||
|
workoutid__isnull=False,
|
||||||
|
coursecompleted=True,
|
||||||
|
weightcategory__in=weightcategory,
|
||||||
|
sex__in=sex,
|
||||||
|
age__gte=age_min,
|
||||||
|
age__lte=age_max,
|
||||||
|
boatclass__in=boatclass,
|
||||||
|
boattype__in=boattype,
|
||||||
|
adaptiveclass__in=adaptiveclass,
|
||||||
|
).order_by("duration","-distance")
|
||||||
|
|
||||||
|
|
||||||
breadcrumbs = [
|
breadcrumbs = [
|
||||||
{
|
{
|
||||||
'url': reverse('virtualevents_view'),
|
'url': reverse('virtualevents_view'),
|
||||||
@@ -314,7 +302,9 @@ def course_view(request,id=0):
|
|||||||
'mapscript':script,
|
'mapscript':script,
|
||||||
'mapdiv':div,
|
'mapdiv':div,
|
||||||
'nosessions':False,
|
'nosessions':False,
|
||||||
|
'records':records,
|
||||||
'rower':r,
|
'rower':r,
|
||||||
|
'form':form,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ from urllib.parse import urlparse, parse_qs
|
|||||||
from json.decoder import JSONDecodeError
|
from json.decoder import JSONDecodeError
|
||||||
|
|
||||||
import ruptures as rpt
|
import ruptures as rpt
|
||||||
|
from rowers.courses import getnearestraces, getnearestcourses
|
||||||
|
|
||||||
def default(o): # pragma: no cover
|
def default(o): # pragma: no cover
|
||||||
if isinstance(o, numpy.int64): return int(o)
|
if isinstance(o, numpy.int64): return int(o)
|
||||||
@@ -2363,7 +2364,7 @@ def workout_view(request,id=0,raceresult=0,sessionresult=0,nocourseraceresult=0)
|
|||||||
else: # pragma: no cover
|
else: # pragma: no cover
|
||||||
hascoordinates = 0
|
hascoordinates = 0
|
||||||
|
|
||||||
|
courses = []
|
||||||
if hascoordinates:
|
if hascoordinates:
|
||||||
if intervaldata: # pragma: no cover
|
if intervaldata: # pragma: no cover
|
||||||
rowdata.df['reltime'] = rowdata.df['TimeStamp (sec)']-rowdata.df.loc[0,'TimeStamp (sec)']
|
rowdata.df['reltime'] = rowdata.df['TimeStamp (sec)']-rowdata.df.loc[0,'TimeStamp (sec)']
|
||||||
@@ -2373,7 +2374,10 @@ def workout_view(request,id=0,raceresult=0,sessionresult=0,nocourseraceresult=0)
|
|||||||
else:
|
else:
|
||||||
latitudes = rowdata.df[' latitude']
|
latitudes = rowdata.df[' latitude']
|
||||||
longitudes = rowdata.df[' longitude']
|
longitudes = rowdata.df[' longitude']
|
||||||
mapscript,mapdiv = leaflet_chart(latitudes,longitudes,row.name,)
|
mapscript,mapdiv = leaflet_chart(latitudes,longitudes,row.name,raceresult=raceresult)
|
||||||
|
records = VirtualRaceResult.objects.filter(workoutid=row.id,userid=row.user.user.id,coursecompleted=True)
|
||||||
|
if records.count()>0:
|
||||||
|
courses = list(set([record.course for record in records]))
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@@ -2413,6 +2417,7 @@ def workout_view(request,id=0,raceresult=0,sessionresult=0,nocourseraceresult=0)
|
|||||||
'mapscript':mapscript,
|
'mapscript':mapscript,
|
||||||
'mapdiv':mapdiv,
|
'mapdiv':mapdiv,
|
||||||
'teams':get_my_teams(request.user),
|
'teams':get_my_teams(request.user),
|
||||||
|
'courses':courses,
|
||||||
'the_div':div})
|
'the_div':div})
|
||||||
|
|
||||||
|
|
||||||
@@ -4445,6 +4450,7 @@ def workout_edit_view(request,id=0,message="",successmessage=""):
|
|||||||
rowdata = rdata(csvfile=f1)
|
rowdata = rdata(csvfile=f1)
|
||||||
|
|
||||||
hascoordinates = 1
|
hascoordinates = 1
|
||||||
|
courses = []
|
||||||
if rowdata != 0:
|
if rowdata != 0:
|
||||||
try:
|
try:
|
||||||
latitude = rowdata.df[' latitude']
|
latitude = rowdata.df[' latitude']
|
||||||
@@ -4456,6 +4462,8 @@ def workout_edit_view(request,id=0,message="",successmessage=""):
|
|||||||
except (KeyError,AttributeError):
|
except (KeyError,AttributeError):
|
||||||
hascoordinates = 0
|
hascoordinates = 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
else: # pragma: no cover
|
else: # pragma: no cover
|
||||||
hascoordinates = 0
|
hascoordinates = 0
|
||||||
|
|
||||||
@@ -4472,6 +4480,11 @@ def workout_edit_view(request,id=0,message="",successmessage=""):
|
|||||||
except KeyError: # pragma: no cover
|
except KeyError: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
records = VirtualRaceResult.objects.filter(workoutid=row.id,userid=row.user.user.id,coursecompleted=True)
|
||||||
|
if records.count()>0:
|
||||||
|
courses = list(set([record.course for record in records]))
|
||||||
|
|
||||||
|
|
||||||
breadcrumbs = [
|
breadcrumbs = [
|
||||||
{
|
{
|
||||||
'url':'/rowers/list-workouts/',
|
'url':'/rowers/list-workouts/',
|
||||||
@@ -4509,6 +4522,7 @@ def workout_edit_view(request,id=0,message="",successmessage=""):
|
|||||||
'mapscript':mapscript,
|
'mapscript':mapscript,
|
||||||
'mapdiv':mapdiv,
|
'mapdiv':mapdiv,
|
||||||
'rower':r,
|
'rower':r,
|
||||||
|
'courses':courses,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@@ -6157,10 +6171,60 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
|
|||||||
data['selector'] = 'pace'
|
data['selector'] = 'pace'
|
||||||
powerorpace = 'pace'
|
powerorpace = 'pace'
|
||||||
|
|
||||||
|
# looking for courses
|
||||||
|
courses = []
|
||||||
|
courseselectform = CourseSelectForm()
|
||||||
|
has_latlon,lat_mean,lon_mean = dataprep.workout_has_latlon(row.id)
|
||||||
|
if has_latlon:
|
||||||
|
courses = getnearestcourses([lat_mean,lon_mean],GeoCourse.objects.all(),whatisnear=25,
|
||||||
|
strict=True)
|
||||||
|
courseselectform = CourseSelectForm(choices=courses)
|
||||||
|
|
||||||
|
|
||||||
powerupdateform = PowerIntervalUpdateForm(initial=data)
|
powerupdateform = PowerIntervalUpdateForm(initial=data)
|
||||||
|
|
||||||
|
if request.method == 'POST' and "course" in request.POST:
|
||||||
|
courseselectform = CourseSelectForm(request.POST,choices=courses)
|
||||||
|
if courseselectform.is_valid():
|
||||||
|
course = courseselectform.cleaned_data['course']
|
||||||
|
# get or create a record
|
||||||
|
records = VirtualRaceResult.objects.filter(
|
||||||
|
userid=r.id,
|
||||||
|
course=course,
|
||||||
|
workoutid=row.id
|
||||||
|
)
|
||||||
|
if records:
|
||||||
|
record = records[0]
|
||||||
|
else:
|
||||||
|
# create record
|
||||||
|
record = VirtualRaceResult(
|
||||||
|
userid = r.id,
|
||||||
|
username = r.user.first_name+' '+r.user.last_name,
|
||||||
|
workoutid = row.id,
|
||||||
|
weightcategory = r.weightcategory,
|
||||||
|
adaptiveclass = r.adaptiveclass,
|
||||||
|
course = course,
|
||||||
|
distance = course.distance,
|
||||||
|
boatclass = row.workouttype,
|
||||||
|
boattype = row.boattype,
|
||||||
|
sex = r.sex,
|
||||||
|
age = calculate_age(r.birthdate),
|
||||||
|
)
|
||||||
|
record.save()
|
||||||
|
|
||||||
|
job = myqueue(
|
||||||
|
queue,
|
||||||
|
handle_check_race_course,
|
||||||
|
row.csvfilename,
|
||||||
|
row.id,
|
||||||
|
course.id,
|
||||||
|
record.id,
|
||||||
|
r.user.email,
|
||||||
|
r.user.first_name,
|
||||||
|
summary=True,
|
||||||
|
)
|
||||||
|
messages.info(request,'We are checking your time on the course in the background')
|
||||||
|
|
||||||
# feeling lucky / ruptures
|
# feeling lucky / ruptures
|
||||||
if request.method == 'POST' and "ruptures" in request.POST:
|
if request.method == 'POST' and "ruptures" in request.POST:
|
||||||
df = pd.DataFrame({
|
df = pd.DataFrame({
|
||||||
@@ -6490,6 +6554,8 @@ def workout_summary_edit_view(request,id,message="",successmessage=""
|
|||||||
'intervalstring':s,
|
'intervalstring':s,
|
||||||
'savebutton':savebutton,
|
'savebutton':savebutton,
|
||||||
'formvalues':formvalues,
|
'formvalues':formvalues,
|
||||||
|
'courses':courses,
|
||||||
|
'courseselectform':courseselectform,
|
||||||
})
|
})
|
||||||
|
|
||||||
class VideoDelete(DeleteView):
|
class VideoDelete(DeleteView):
|
||||||
|
|||||||
Reference in New Issue
Block a user