Private
Public Access
1
0

Merge branch 'develop' into feature/charts-microservice

This commit is contained in:
2024-04-05 13:17:16 +02:00
11 changed files with 416 additions and 14 deletions

View File

@@ -926,6 +926,164 @@ def interactive_histoall(theworkouts, histoparam, includereststrokes,
def course_map(course): def course_map(course):
<<<<<<< HEAD
=======
latmean, lonmean, coordinates = course_coord_center(course)
if course.with_cn_nav_waypoints:
latmean, lonmean, coordinates = course_coord_crewnerd_navigation(course)
lat_min, lat_max, long_min, long_max = course_coord_maxmin(course)
coordinates = course_spline(coordinates)
scoordinates = "["
for index, row in coordinates.iterrows():
scoordinates += """[{x},{y}],
""".format(
x=row['latitude'],
y=row['longitude']
)
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_{id}', {{
center: [{latmean}, {lonmean}],
zoom: 13,
layers: [outdoors]
}}).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}
</script>
""".format(
id=course.id,
latmean=latmean,
lonmean=lonmean,
scoordinates=scoordinates,
pcoordinates=pcoordinates,
plabels=plabels
)
div = """
<div id="map_canvas_{id}" style="width: 100%; height: 400px; margin:0; padding:0;grid-gap:0;"></div>
""".format(
id=course.id,
)
>>>>>>> develop
course_dict = GeoCourseSerializer(course).data course_dict = GeoCourseSerializer(course).data

View File

@@ -4158,6 +4158,7 @@ class IndoorVirtualRaceResult(models.Model):
class CourseTestResult(models.Model): class CourseTestResult(models.Model):
userid = models.IntegerField(default=0) userid = models.IntegerField(default=0)
courseid = models.IntegerField(default=0)
workoutid = models.IntegerField(null=True) workoutid = models.IntegerField(null=True)
plannedsession = models.ForeignKey( plannedsession = models.ForeignKey(
PlannedSession, on_delete=models.CASCADE) PlannedSession, on_delete=models.CASCADE)
@@ -4167,6 +4168,13 @@ class CourseTestResult(models.Model):
startsecond = models.FloatField(default=0) startsecond = models.FloatField(default=0)
endsecond = models.FloatField(default=0) endsecond = models.FloatField(default=0)
def save(self, *args, **kwargs):
if self.userid == 0:
w = Workout.objects.get(id=self.workoutid)
self.userid = w.user.id
super(CourseTestResult, self).save(*args, **kwargs)
class IndoorVirtualRaceResultForm(ModelForm): class IndoorVirtualRaceResultForm(ModelForm):
class Meta: class Meta:

View File

@@ -1149,9 +1149,11 @@ def handle_check_race_course(self,
record.duration = totaltime_sec_to_string(coursetimeseconds) record.duration = totaltime_sec_to_string(coursetimeseconds)
record.distance = int(coursemeters) record.distance = int(coursemeters)
record.workoutid = workoutid record.workoutid = workoutid
record.courseid = courseid
record.startsecond = startsecond record.startsecond = startsecond
record.endsecond = endsecond record.endsecond = endsecond
record.points = points record.points = points
record.coursecompleted = 1
record.save() record.save()
if summary: # pragma: no cover if summary: # pragma: no cover
@@ -1178,7 +1180,6 @@ def handle_check_race_course(self,
workout.summary = summary workout.summary = summary
workout.save() workout.save()
if successemail: # pragma: no cover if successemail: # pragma: no cover
handle_sendemail_coursesucceed( handle_sendemail_coursesucceed(
useremail, userfirstname, logfile, workoutid useremail, userfirstname, logfile, workoutid

View File

@@ -136,7 +136,36 @@
</p> </p>
</li> </li>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if ownrecords %}
<li class="grid_4">
<h2>Own (Private) Results</h2>
<p>
<table class="listtable shortpadded">
<thead>
<tr>
<th>Time</th>
<th>Distance</th>
<th>Date</th>
</tr>
</thead>
<tbody>
{% for record in ownrecords %}
<tr>
<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/">
<i class="fas fa-search-plus fa-fw"></i></a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</p>
</li>
{% endif %}
</ul> </ul>
{% endblock %} {% endblock %}

View File

@@ -0,0 +1,105 @@
{% extends "newbase.html" %}
{% load static %}
{% load rowerfilters %}
{% load leaflet_tags %}
{% block meta %}
{% leaflet_js %}
{% leaflet_css %}
{% endblock %}
{% block title %}Rowsandall Virtual Challenges{% endblock %}
{% block scripts %}
<script type='text/javascript'
src='https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js'>
</script>
{% endblock %}
{% block main %}
<ul class="main-content">
<li class="grid_4">
<h1>Courses you might like</h1>
</li>
<li>
<p>
<a href="/rowers/list-courses/">All Courses</a>
</p>
<a href="/rowers/list-courses/?liked=true">Courses I like</a>
</p>
</li>
{% for course in coursesdicts %}
<li class="rounder">
<a href="/rowers/courses/{{ course.course.id }}/">
<h2>{{ course.course.name }}</h2>
</a>
<p>{{ course.course.country }}</p>
<p>{{ course.course.distance }}m</p>
<div class="mapdiv" id="{{ course.course.id }}">
{{ course.div|safe}}
{{ course.script|safe}}
</div>
</li>
{% endfor %}
<li class="grid_4">
<h1>Interesting Challenges</h1>
</li>
<li>
<p>
<a href="/rowers/virtualevents/">All Active Challenges</a>
</p>
<a href="/rowers/virtualevents/?nearby=true">Nearby Challenges</a>
</p>
</li>
{% for challenge in challengesdicts %}
<li class="rounder">
<a href="/rowers/virtualevent/{{ challenge.challenge.id }}/">
<h2>{{ challenge.challenge.name }}</h2>
</a>
<p>{{ challenge.challenge.startdate|date:"Y-m-d" }}
{{ challenge.challenge.start_time|time:"H:i e" }}
to
{{ challenge.challenge.enddate|date:"Y-m-d" }}
{{ challenge.challenge.end_time|time:"H:i e" }}
({{ challenge.challenge.timezone }})</p>
<p>{% if challenge.challenge.sessiontype == 'fastest_time' %}
Time Challenge: To be rowed on the water
{% elif challenge.challenge.sessiontype == 'fastest_distance' %}
Distance Challenge: To be rowed on the water
{% elif challenge.challenge.sessiontype != 'race' %}
Indoor Race: To be rowed on a Concept2 ergometer
{% endif %}
</p>
{% if challenge.challenge.sessiontype == 'fastest_time' %}
<div class="vignet">
<img src="/static/img/sculler.png"
alt="Marked Course">
</div>
{% elif challenge.challenge.sessiontype == 'fastest_distance' %}
<div class="vignet">
<img src="/static/img/sculler.png"
alt="Marked Course">
</div>
{% elif challenge.challenge.sessiontype != 'race' %}
<div class="vignet">
<img src="/static/img/domca.png"
alt="Marked Course">
</div>
{% endif %}
<div class="mapdiv" id="{{ challenge.challenge.id }}">
{{ challenge.div|safe}}
{{ challenge.script|safe}}
</div>
</li>
{% endfor %}
</ul>
{% endblock %}
{% block sidebar %}
{% include 'menu_racing.html' %}
{% endblock %}

View File

@@ -172,7 +172,7 @@
</li> </li>
{% if coursescript %} {% if coursescript %}
<li class="grid_2"> <li class="grid_2">
<h2>Course</h2> <h2><a href="/rowers/courses/{{ plannedsession.course.id }}">{{ plannedsession.course.name }}</h2>
{{ coursediv|safe }} {{ coursediv|safe }}
{{ coursescript|safe }} {{ coursescript|safe }}

View File

@@ -311,6 +311,8 @@ urlpatterns = [
name='workouts_view'), name='workouts_view'),
re_path(r'^list-workouts/user/(?P<userid>\d+)/$', views.workouts_view, re_path(r'^list-workouts/user/(?P<userid>\d+)/$', views.workouts_view,
name='workouts_view'), name='workouts_view'),
re_path(r'^courses/$', views.courses_challenges_view,
name='courses_challenges_view'),
re_path(r'^virtualevents/$', views.virtualevents_view, re_path(r'^virtualevents/$', views.virtualevents_view,
name='virtualevents_view'), name='virtualevents_view'),
re_path(r'^virtualevent/createchoice/$', TemplateView.as_view( re_path(r'^virtualevent/createchoice/$', TemplateView.as_view(

View File

@@ -410,12 +410,15 @@ def trendflexdata(workouts, options, userid=0):
except (ValueError, AttributeError): # pragma: no cover except (ValueError, AttributeError): # pragma: no cover
return ('', 'Error: not enough data') return ('', 'Error: not enough data')
else: # pragma: no cover else: # pragma: no cover
bins = np.arange(datadf['days ago'].min()-binsize, try:
datadf['days ago'].max()+binsize, bins = np.arange(datadf['days ago'].min()-binsize,
binsize, datadf['days ago'].max()+binsize,
) binsize,
groups = datadf.groupby(pd.cut(datadf['days ago'], bins, )
labels=False)) groups = datadf.groupby(pd.cut(datadf['days ago'], bins,
labels=False))
except (ValueError, AttributeError): # pragma: no cover
return ('', 'Error: not enough data')
xvalues = [] xvalues = []
yvalues = [] yvalues = []

View File

@@ -2332,7 +2332,6 @@ def plannedsession_view(request, id=0, userid=0):
if ps.sessiontype == 'coursetest': # pragma: no cover if ps.sessiontype == 'coursetest': # pragma: no cover
vs = CourseTestResult.objects.filter(plannedsession=ps, vs = CourseTestResult.objects.filter(plannedsession=ps,
workoutid=w.id) workoutid=w.id)
if vs: if vs:
for record in vs: for record in vs:
if record.workoutid == w.id: if record.workoutid == w.id:

View File

@@ -8,10 +8,99 @@ from django import forms
from rowers.plannedsessions import timefield_to_seconds_duration from rowers.plannedsessions import timefield_to_seconds_duration
from rowers.courses import getnearestraces, getnearestcourses,coursetokml, coursestokml from rowers.courses import getnearestraces, getnearestcourses,coursetokml, coursestokml
from random import sample
# landing page for challenges & courses
def courses_challenges_view(request):
r = getrower(request.user)
g = GeoIP2()
ip = request.META.get('HTTP_X_REAL_IP', '1.1.1.1')
try:
lat_lon = g.lat_lon(ip)
city = g.city(ip)
except: # pragma: no cover
lat_lon = None
city = {
'city': '',
'country_name': '',
'time_zone': '',
}
courses = GeoCourse.objects.all().order_by("country", "name", "distance")
nearby_courses = getnearestcourses(lat_lon, courses, whatisnear=2000)
liked_courses = GeoCourse.objects.filter(followers=r)
courses = GeoCourse.objects.filter(id__in=[course.id for course in nearby_courses]) | liked_courses
if courses.count() >= 3:
courses = sample(list(courses),3)
else:
courses = GeoCourse.objects.all().order_by("country", "name", "distance")
coursesdicts = []
for course in courses:
script, div = course_map(course)
coursesdicts.append({
'course': course,
'script': script,
'div': div,
})
allchallenges = VirtualRace.objects.all().order_by("-startdate")
totalnrchallenges = allchallenges.count()
challenges = VirtualRace.objects.filter(startdate__gte=timezone.now())
challenges2 = VirtualRace.objects.filter(startdate__lte=timezone.now(),
evaluation_closure__gte=timezone.now()-datetime.timedelta(days=3))
challenges = challenges | challenges2
count = 3
if totalnrchallenges > count:
allchallenges = list(allchallenges)
while len(challenges) < count:
try:
challenges = list(challenges)+sample(allchallenges, count-len(challenges))
except ValueError:
count = count-1
challenges = list(set(challenges))
else:
challenges = VirtualRace.objects.all()
count = challenges.count()
challenges = sample(list(challenges),count)
challengesdicts = []
for challenge in challenges:
script = ''
div = ''
if challenge.course:
script, div = course_map(challenge.course)
challengesdicts.append(
{
'script': script,
'div': div,
'challenge': challenge,
}
)
breadcrumbs = [
{
'url': reverse('courses_challenges_view'),
'name': 'Courses and Challenges'
}
]
return render(request, "courses_challenges.html",
{
'coursesdicts': coursesdicts,
'rower': r,
'challengesdicts': challengesdicts,
'breadcrumbs': breadcrumbs,
})
# List Courses # List Courses
def courses_view(request): def courses_view(request):
r = getrower(request.user) r = getrower(request.user)
g = GeoIP2() g = GeoIP2()
@@ -280,6 +369,13 @@ def course_view(request, id=0):
workoutid__isnull=False, workoutid__isnull=False,
coursecompleted=True).order_by("duration", "-distance") coursecompleted=True).order_by("duration", "-distance")
# get own training results
ownrecords = CourseTestResult.objects.filter(
courseid = course.id,
userid = r.id,
coursecompleted=True
).order_by("duration", "-distance")
if request.user.is_authenticated: if request.user.is_authenticated:
notsharing = Rower.objects.filter( notsharing = Rower.objects.filter(
share_course_results=False).exclude(id=r.id) share_course_results=False).exclude(id=r.id)
@@ -371,6 +467,7 @@ def course_view(request, id=0):
'mapdiv': div, 'mapdiv': div,
'nosessions': False, 'nosessions': False,
'records': records, 'records': records,
'ownrecords': ownrecords,
'rower': r, 'rower': r,
'form': form, 'form': form,
'onlyme': onlyme, 'onlyme': onlyme,

View File

@@ -206,8 +206,8 @@
</a> </a>
</li> </li>
<li id="nav-racing"> <li id="nav-racing">
<a href="/rowers/virtualevents/"> <a href="/rowers/courses/">
<i class="fas fa-flag-checkered"></i>&nbsp;Challenges <i class="fas fa-flag-checkered"></i>&nbsp;Challenges, Courses
</a> </a>
</li> </li>
<li id="nav-analysis"> <li id="nav-analysis">