Merge branch 'release/v20.6.1'
This commit is contained in:
+157
-16
@@ -4,6 +4,7 @@ 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,
|
||||||
|
course_coord_crewnerd_navigation,
|
||||||
polygon_coord_center, PlannedSession,
|
polygon_coord_center, PlannedSession,
|
||||||
polygon_to_path, coordinate_in_path
|
polygon_to_path, coordinate_in_path
|
||||||
)
|
)
|
||||||
@@ -22,6 +23,7 @@ import time
|
|||||||
from django.db import IntegrityError
|
from django.db import IntegrityError
|
||||||
import uuid
|
import uuid
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
import math
|
||||||
|
|
||||||
import geocoder
|
import geocoder
|
||||||
|
|
||||||
@@ -120,6 +122,50 @@ xmlns_uris_dict = {'gx': 'http://www.google.com/kml/ext/2.2',
|
|||||||
'': "http://www.opengis.net/kml/2.2"}
|
'': "http://www.opengis.net/kml/2.2"}
|
||||||
|
|
||||||
|
|
||||||
|
def get_polar_angle(point, reference_point):
|
||||||
|
"""
|
||||||
|
Calculate the polar angle of a point with respect to a reference point.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
delta_x = point.longitude - reference_point.longitude
|
||||||
|
delta_y = point.latitude - reference_point.latitude
|
||||||
|
except AttributeError:
|
||||||
|
delta_x = point['longitude'] - reference_point.longitude
|
||||||
|
delta_y = point['latitude'] - reference_point.latitude
|
||||||
|
|
||||||
|
|
||||||
|
return math.atan2(delta_y, delta_x)
|
||||||
|
|
||||||
|
class Coordinate:
|
||||||
|
def __init__(self, latitude, longitude):
|
||||||
|
self.latitude = latitude
|
||||||
|
self.longitude = longitude
|
||||||
|
|
||||||
|
def get_coordinates_centerpoint(coordinates):
|
||||||
|
try:
|
||||||
|
centroid_latitude = sum(coord.latitude for coord in coordinates) / len(coordinates)
|
||||||
|
centroid_longitude = sum(coord.longitude for coord in coordinates) / len(coordinates)
|
||||||
|
centroid = Coordinate(centroid_latitude, centroid_longitude)
|
||||||
|
except AttributeError:
|
||||||
|
centroid_latitude = sum(coord['latitude'] for coord in coordinates) / len(coordinates)
|
||||||
|
centroid_longitude = sum(coord['longitude'] for coord in coordinates) / len(coordinates)
|
||||||
|
centroid = Coordinate(centroid_latitude, centroid_longitude)
|
||||||
|
|
||||||
|
return centroid
|
||||||
|
|
||||||
|
def sort_coordinates_ccw(coordinates):
|
||||||
|
"""
|
||||||
|
Sort coordinates in counterclockwise order.
|
||||||
|
"""
|
||||||
|
# Find the reference point (centroid)
|
||||||
|
centroid = get_coordinates_centerpoint(coordinates)
|
||||||
|
|
||||||
|
# Sort coordinates based on polar angle with respect to the centroid
|
||||||
|
sorted_coords = sorted(coordinates, key=lambda coord: get_polar_angle(coord, centroid))
|
||||||
|
|
||||||
|
return sorted_coords
|
||||||
|
|
||||||
|
|
||||||
def crewnerdcourse(doc):
|
def crewnerdcourse(doc):
|
||||||
courses = []
|
courses = []
|
||||||
for course in doc:
|
for course in doc:
|
||||||
@@ -162,6 +208,8 @@ def get_polygons(polygonpms):
|
|||||||
'latitude': float(coordinates[1]),
|
'latitude': float(coordinates[1]),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
points = sort_coordinates_ccw(points)
|
||||||
|
|
||||||
polygons.append({
|
polygons.append({
|
||||||
'name': name,
|
'name': name,
|
||||||
'points': points
|
'points': points
|
||||||
@@ -169,34 +217,45 @@ def get_polygons(polygonpms):
|
|||||||
|
|
||||||
return polygons
|
return polygons
|
||||||
|
|
||||||
|
def crewnerdify(polygons):
|
||||||
|
polygons[0].name = "Start"
|
||||||
|
polygons[len(polygons)-1].name = "Finish"
|
||||||
|
|
||||||
def coursetokml(course):
|
if len(polygons) > 2:
|
||||||
top = Element('kml')
|
for i in range(1,len(polygons)-1):
|
||||||
for prefix, uri in xmlns_uris_dict.items():
|
polygons[i].name = 'WP{n}'.format(n=polygons[i].order_in_course)
|
||||||
if prefix != '':
|
|
||||||
top.attrib['xmlns:' + prefix] = uri
|
|
||||||
else:
|
|
||||||
top.attrib['xmlns'] = uri
|
|
||||||
|
|
||||||
document = SubElement(top, 'Document')
|
return polygons
|
||||||
name = SubElement(document, 'name')
|
|
||||||
name.text = 'Courses.kml'
|
def removewhitespace(s):
|
||||||
folder = SubElement(document, 'Folder')
|
regels = s.split('\n')
|
||||||
foldername = SubElement(folder, 'name')
|
regels = [regel for regel in regels if regel.strip()]
|
||||||
foldername.text = 'Courses'
|
return "\n".join(regels)
|
||||||
folder2 = SubElement(folder, 'Folder')
|
|
||||||
|
def getcoursefolder(course, document, cn=False):
|
||||||
|
folder2 = SubElement(document, 'Folder', id="{id}".format(id=course.id))
|
||||||
coursename = SubElement(folder2, 'name')
|
coursename = SubElement(folder2, 'name')
|
||||||
coursename.text = course.name
|
coursename.text = course.name
|
||||||
open = SubElement(folder2, 'open')
|
openst = SubElement(folder2, 'open')
|
||||||
open.text = '1'
|
openst.text = '1'
|
||||||
|
coursedescription = SubElement(folder2, 'description')
|
||||||
|
if len(removewhitespace(course.notes)):
|
||||||
|
coursedescription.text = "Rowsandall.com\n" + removewhitespace(course.notes)
|
||||||
|
else:
|
||||||
|
coursedescription.text = "Rowsandall.com " + course.name
|
||||||
|
|
||||||
polygons = GeoPolygon.objects.filter(
|
polygons = GeoPolygon.objects.filter(
|
||||||
course=course).order_by("order_in_course")
|
course=course).order_by("order_in_course")
|
||||||
|
|
||||||
|
if cn:
|
||||||
|
polygons = crewnerdify(polygons)
|
||||||
|
|
||||||
for polygon in polygons:
|
for polygon in polygons:
|
||||||
placemark = SubElement(folder2, 'Placemark')
|
placemark = SubElement(folder2, 'Placemark')
|
||||||
polygonname = SubElement(placemark, 'name')
|
polygonname = SubElement(placemark, 'name')
|
||||||
polygonname.text = polygon.name
|
polygonname.text = polygon.name
|
||||||
|
polygonstyle = SubElement(placemark, 'styleUrl')
|
||||||
|
polygonstyle.text = "#default0"
|
||||||
p = SubElement(placemark, 'Polygon')
|
p = SubElement(placemark, 'Polygon')
|
||||||
tessellate = SubElement(p, 'tessellate')
|
tessellate = SubElement(p, 'tessellate')
|
||||||
tessellate.text = '1'
|
tessellate.text = '1'
|
||||||
@@ -206,6 +265,7 @@ def coursetokml(course):
|
|||||||
coordinates.text = ''
|
coordinates.text = ''
|
||||||
points = GeoPoint.objects.filter(
|
points = GeoPoint.objects.filter(
|
||||||
polygon=polygon).order_by("order_in_poly")
|
polygon=polygon).order_by("order_in_poly")
|
||||||
|
points = sort_coordinates_ccw(points)
|
||||||
for point in points:
|
for point in points:
|
||||||
coordinates.text += '{lon},{lat},0 '.format(
|
coordinates.text += '{lon},{lat},0 '.format(
|
||||||
lat=point.latitude,
|
lat=point.latitude,
|
||||||
@@ -215,6 +275,87 @@ def coursetokml(course):
|
|||||||
lat=points[0].latitude,
|
lat=points[0].latitude,
|
||||||
lon=points[0].longitude,
|
lon=points[0].longitude,
|
||||||
)
|
)
|
||||||
|
return folder2
|
||||||
|
|
||||||
|
def getstyle(document, id):
|
||||||
|
style = SubElement(document,'Style', id=id)
|
||||||
|
iconstyle = SubElement(style, 'IconStyle')
|
||||||
|
scale = SubElement(iconstyle, 'scale')
|
||||||
|
scale.text = "1.2"
|
||||||
|
linestyle = SubElement(style, 'LineStyle')
|
||||||
|
linestylecolor = SubElement(linestyle, 'color')
|
||||||
|
linestylecolor.text = "ff00ffff"
|
||||||
|
polystyle = SubElement(style, 'PolyStyle')
|
||||||
|
polystylecolor = SubElement(polystyle, 'color')
|
||||||
|
polystylecolor.text = "ff7fffff"
|
||||||
|
|
||||||
|
return style
|
||||||
|
|
||||||
|
def getstylemap(document, id):
|
||||||
|
stylemap = SubElement(document, 'StyleMap', id=id)
|
||||||
|
pair1 = SubElement(stylemap, 'Pair')
|
||||||
|
pair1key = SubElement(pair1, 'key')
|
||||||
|
pair1key.text = "normal"
|
||||||
|
pair1styleurl = SubElement(pair1, 'styleUrl')
|
||||||
|
pair1styleurl.text = "#default"
|
||||||
|
pair2 = SubElement(stylemap, 'Pair')
|
||||||
|
pair2key = SubElement(pair2, 'key')
|
||||||
|
pair2key.text = "highlight"
|
||||||
|
pair2styleurl = SubElement(pair2, 'styleUrl')
|
||||||
|
pair2styleurl.text = "#hl"
|
||||||
|
|
||||||
|
return stylemap
|
||||||
|
|
||||||
|
def coursestokml(courseids, cn=False):
|
||||||
|
courses = GeoCourse.objects.filter(id__in=courseids)
|
||||||
|
top = Element('kml')
|
||||||
|
for prefix, uri in xmlns_uris_dict.items():
|
||||||
|
if prefix != '':
|
||||||
|
top.attrib['xmlns:' + prefix] = uri
|
||||||
|
else:
|
||||||
|
top.attrib['xmlns'] = uri
|
||||||
|
|
||||||
|
document = SubElement(top, 'Document')
|
||||||
|
name = SubElement(document, 'name')
|
||||||
|
name.text = 'courses'
|
||||||
|
|
||||||
|
opendoc = SubElement(document,'open')
|
||||||
|
opendoc.text = '1'
|
||||||
|
|
||||||
|
style = getstyle(document, "default")
|
||||||
|
|
||||||
|
stylemap = getstylemap(document, 'default0')
|
||||||
|
|
||||||
|
stylehl = getstyle(document, "hl")
|
||||||
|
|
||||||
|
for course in courses:
|
||||||
|
coursefolder = getcoursefolder(course, document, cn=cn)
|
||||||
|
|
||||||
|
return prettify(top)
|
||||||
|
|
||||||
|
def coursetokml(course,cn=False):
|
||||||
|
top = Element('kml')
|
||||||
|
for prefix, uri in xmlns_uris_dict.items():
|
||||||
|
if prefix != '':
|
||||||
|
top.attrib['xmlns:' + prefix] = uri
|
||||||
|
else:
|
||||||
|
top.attrib['xmlns'] = uri
|
||||||
|
|
||||||
|
document = SubElement(top, 'Document')
|
||||||
|
name = SubElement(document, 'name')
|
||||||
|
name.text = 'courses'
|
||||||
|
|
||||||
|
opendoc = SubElement(document,'open')
|
||||||
|
opendoc.text = '1'
|
||||||
|
|
||||||
|
style = getstyle(document, "default")
|
||||||
|
|
||||||
|
stylemap = getstylemap(document, 'default0')
|
||||||
|
|
||||||
|
stylehl = getstyle(document, "hl")
|
||||||
|
|
||||||
|
coursefolder = getcoursefolder(course, document, cn=cn)
|
||||||
|
|
||||||
|
|
||||||
return prettify(top)
|
return prettify(top)
|
||||||
|
|
||||||
|
|||||||
+81
-31
@@ -31,6 +31,11 @@ from zipfile import BadZipFile
|
|||||||
import zipfile
|
import zipfile
|
||||||
import os
|
import os
|
||||||
from rowers.models import strokedatafields
|
from rowers.models import strokedatafields
|
||||||
|
import grpc
|
||||||
|
import grpc.experimental
|
||||||
|
import rowers.rowing_workout_metrics_pb2 as metrics_pb2
|
||||||
|
import rowers.rowing_workout_metrics_pb2_grpc as metrics_pb2_grpc
|
||||||
|
import traceback
|
||||||
|
|
||||||
from rowingdata import (
|
from rowingdata import (
|
||||||
KinoMapParser,
|
KinoMapParser,
|
||||||
@@ -120,10 +125,10 @@ from rowers.datautils import rpetotss
|
|||||||
def rscore_approx(row):
|
def rscore_approx(row):
|
||||||
if row['rscore'] > 0:
|
if row['rscore'] > 0:
|
||||||
return row['rscore']
|
return row['rscore']
|
||||||
if row['rscore'] == 0:
|
if row['rscore'] == 0: # pragma: no cover
|
||||||
if row['hrtss'] > 0:
|
if row['hrtss'] > 0:
|
||||||
return row['hrtss']
|
return row['hrtss']
|
||||||
else:
|
else: # pragma: no cover
|
||||||
dd = 3600*row['duration'].hour+60*row['duration'].minute+row['duration'].second
|
dd = 3600*row['duration'].hour+60*row['duration'].minute+row['duration'].second
|
||||||
dd = dd/3600
|
dd = dd/3600
|
||||||
try:
|
try:
|
||||||
@@ -135,7 +140,7 @@ def rscore_approx(row):
|
|||||||
w = Workout.objects.get(id=row['id'])
|
w = Workout.objects.get(id=row['id'])
|
||||||
trimp, hrtss = workout_trimp(w)
|
trimp, hrtss = workout_trimp(w)
|
||||||
rscore, normp = workout_rscore(w)
|
rscore, normp = workout_rscore(w)
|
||||||
if row['rpe'] and row['rpe'] > 0:
|
if row['rpe'] and row['rpe'] > 0: # pragma: no cover
|
||||||
dd = 3600*row['duration'].hour+60*row['duration'].minute+row['duration'].second
|
dd = 3600*row['duration'].hour+60*row['duration'].minute+row['duration'].second
|
||||||
dd = dd/3600
|
dd = dd/3600
|
||||||
return int(rpetotss[row['rpe']]*dd)
|
return int(rpetotss[row['rpe']]*dd)
|
||||||
@@ -143,7 +148,7 @@ def rscore_approx(row):
|
|||||||
minutes = row['duration'].hour*60+row['duration'].minute
|
minutes = row['duration'].hour*60+row['duration'].minute
|
||||||
return minutes
|
return minutes
|
||||||
|
|
||||||
return 0
|
return 0 # pragma: no cover
|
||||||
|
|
||||||
|
|
||||||
thetimezone = get_current_timezone()
|
thetimezone = get_current_timezone()
|
||||||
@@ -177,7 +182,7 @@ def workout_goldmedalstandard(workout, reset=False):
|
|||||||
if workout.workouttype in otwtypes:
|
if workout.workouttype in otwtypes:
|
||||||
try:
|
try:
|
||||||
factor = 100./(100.-workout.user.otwslack)
|
factor = 100./(100.-workout.user.otwslack)
|
||||||
except ZeroDivisionError:
|
except ZeroDivisionError: # pragma: no cover
|
||||||
factor = 1.
|
factor = 1.
|
||||||
workout.user.otwslack = 0
|
workout.user.otwslack = 0
|
||||||
workout.user.save()
|
workout.user.save()
|
||||||
@@ -186,8 +191,8 @@ def workout_goldmedalstandard(workout, reset=False):
|
|||||||
workout.goldmedalseconds = goldmedalseconds
|
workout.goldmedalseconds = goldmedalseconds
|
||||||
workout.save()
|
workout.save()
|
||||||
return goldmedalstandard, goldmedalseconds
|
return goldmedalstandard, goldmedalseconds
|
||||||
else:
|
|
||||||
return 0, 0
|
return 0, 0 # pragma: no cover
|
||||||
|
|
||||||
|
|
||||||
def check_marker(workout):
|
def check_marker(workout):
|
||||||
@@ -497,53 +502,85 @@ def calculate_goldmedalstandard(rower, workout, recurrance=True):
|
|||||||
|
|
||||||
|
|
||||||
def setcp(workout, background=False, recurrance=True):
|
def setcp(workout, background=False, recurrance=True):
|
||||||
|
try:
|
||||||
filename = 'media/cpdata_{id}.parquet.gz'.format(id=workout.id)
|
filename = 'media/cpdata_{id}.parquet.gz'.format(id=workout.id)
|
||||||
|
df = pd.read_parquet(filename)
|
||||||
|
|
||||||
|
if not df.empty:
|
||||||
|
# check dts
|
||||||
|
tarr = datautils.getlogarr(4000)
|
||||||
|
if df['delta'][0] in tarr:
|
||||||
|
return(df, df['delta'], df['cp'])
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
strokesdf = getsmallrowdata_db(
|
strokesdf = getsmallrowdata_db(
|
||||||
['power', 'workoutid', 'time'], ids=[workout.id])
|
['power', 'workoutid', 'time'], ids=[workout.id])
|
||||||
|
|
||||||
try:
|
if strokesdf.empty:
|
||||||
if strokesdf['power'].std() == 0:
|
|
||||||
return pd.DataFrame(), pd.Series(dtype='float'), pd.Series(dtype='float')
|
|
||||||
except (KeyError, TypeError):
|
|
||||||
return pd.DataFrame(), pd.Series(dtype='float'), pd.Series(dtype='float')
|
|
||||||
|
|
||||||
if background: # pragma: no cover
|
|
||||||
_ = myqueue(queuelow, handle_setcp, strokesdf, filename, workout.id)
|
|
||||||
return pd.DataFrame({'delta': [], 'cp': []}), pd.Series(dtype='float'), pd.Series(dtype='float')
|
return pd.DataFrame({'delta': [], 'cp': []}), pd.Series(dtype='float'), pd.Series(dtype='float')
|
||||||
|
|
||||||
if not strokesdf.empty:
|
|
||||||
totaltime = strokesdf['time'].max()
|
totaltime = strokesdf['time'].max()
|
||||||
try:
|
maxt = totaltime/1000.
|
||||||
powermean = strokesdf['power'].mean()
|
|
||||||
except KeyError: # pragma: no cover
|
|
||||||
powermean = 0
|
|
||||||
|
|
||||||
if powermean != 0:
|
|
||||||
thesecs = totaltime
|
|
||||||
maxt = 1.05 * thesecs
|
|
||||||
|
|
||||||
if maxt > 0:
|
|
||||||
logarr = datautils.getlogarr(maxt)
|
logarr = datautils.getlogarr(maxt)
|
||||||
dfgrouped = strokesdf.groupby(['workoutid'])
|
|
||||||
delta, cpvalues, avgpower = datautils.getcp(dfgrouped, logarr)
|
csvfilename = workout.csvfilename
|
||||||
|
# check what the real file name is
|
||||||
|
if os.path.exists(csvfilename):
|
||||||
|
csvfile = csvfilename
|
||||||
|
elif os.path.exists(csvfilename+'.csv'): # pragma: no cover
|
||||||
|
csvfile = csvfilename+'.csv'
|
||||||
|
elif os.path.exists(csvfilename+'.gz'): # pragma: no cover
|
||||||
|
csvfile = csvfilename+'.gz'
|
||||||
|
else: # pragma: no cover
|
||||||
|
return pd.DataFrame({'delta': [], 'cp': []}), pd.Series(dtype='float'), pd.Series(dtype='float')
|
||||||
|
csvfile = os.path.abspath(csvfile)
|
||||||
|
|
||||||
|
|
||||||
|
with grpc.insecure_channel(
|
||||||
|
target='localhost:50052',
|
||||||
|
options=[('grpc.lb_policy_name', 'pick_first'),
|
||||||
|
('grpc.enable_retries', 0), ('grpc.keepalive_timeout_ms',
|
||||||
|
10000)]
|
||||||
|
) as channel:
|
||||||
|
try:
|
||||||
|
grpc.channel_ready_future(channel).result(timeout=10)
|
||||||
|
except grpc.FutureTimeoutError: # pragma: no cover
|
||||||
|
dologging('metrics.log','grpc channel time out in setcp')
|
||||||
|
return pd.DataFrame({'delta': [], 'cp': []}), pd.Series(dtype='float'), pd.Series(dtype='float')
|
||||||
|
|
||||||
|
stub = metrics_pb2_grpc.MetricsStub(channel)
|
||||||
|
req = metrics_pb2.CPRequest(filename = csvfile, filetype = "CSV", tarr = logarr)
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = stub.GetCP(req, timeout=60)
|
||||||
|
except Exception as e:
|
||||||
|
dologging('metrics.log', traceback.format_exc())
|
||||||
|
return pd.DataFrame({'delta': [], 'cp': []}), pd.Series(dtype='float'), pd.Series(dtype='float')
|
||||||
|
|
||||||
|
delta = pd.Series(np.array(response.delta))
|
||||||
|
cpvalues = pd.Series(np.array(response.power))
|
||||||
|
powermean = response.avgpower
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
df = pd.DataFrame({
|
df = pd.DataFrame({
|
||||||
'delta': delta,
|
'delta': delta,
|
||||||
'cp': cpvalues,
|
'cp': cpvalues,
|
||||||
'id': workout.id,
|
'id': workout.id,
|
||||||
})
|
})
|
||||||
df.to_parquet(filename, engine='fastparquet',
|
|
||||||
compression='GZIP')
|
df.to_parquet(filename, engine='fastparquet', compression='GZIP')
|
||||||
|
|
||||||
if recurrance:
|
if recurrance:
|
||||||
goldmedalstandard, goldmedalduration = calculate_goldmedalstandard(
|
goldmedalstandard, goldmedalduration = calculate_goldmedalstandard(
|
||||||
workout.user, workout)
|
workout.user, workout)
|
||||||
workout.goldmedalstandard = goldmedalstandard
|
workout.goldmedalstandard = goldmedalstandard
|
||||||
workout.goldmedalduration = goldmedalduration
|
workout.goldmedalduration = goldmedalduration
|
||||||
workout.save()
|
workout.save()
|
||||||
|
|
||||||
return df, delta, cpvalues
|
return df, delta, cpvalues
|
||||||
|
|
||||||
return pd.DataFrame({'delta': [], 'cp': []}), pd.Series(dtype='float'), pd.Series(dtype='float')
|
|
||||||
|
|
||||||
|
|
||||||
def update_wps(r, types, mode='water', asynchron=True):
|
def update_wps(r, types, mode='water', asynchron=True):
|
||||||
@@ -679,9 +716,11 @@ def join_workouts(r, ids, title='Joined Workout',
|
|||||||
def fetchcp_new(rower, workouts):
|
def fetchcp_new(rower, workouts):
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
|
|
||||||
for workout in workouts:
|
for workout in workouts:
|
||||||
cpfile = 'media/cpdata_{id}.parquet.gz'.format(id=workout.id)
|
cpfile = 'media/cpdata_{id}.parquet.gz'.format(id=workout.id)
|
||||||
try:
|
try:
|
||||||
|
df, delta, cpvalues = setcp(workout)
|
||||||
df = pd.read_parquet(cpfile)
|
df = pd.read_parquet(cpfile)
|
||||||
df['workout'] = str(workout)
|
df['workout'] = str(workout)
|
||||||
df['url'] = workout.url()
|
df['url'] = workout.url()
|
||||||
@@ -698,12 +737,15 @@ def fetchcp_new(rower, workouts):
|
|||||||
if len(data) > 1:
|
if len(data) > 1:
|
||||||
df = pd.concat(data, axis=0)
|
df = pd.concat(data, axis=0)
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
df = df[df['cp'] == df.groupby(['delta'])['cp'].transform('max')]
|
df = df[df['cp'] == df.groupby(['delta'])['cp'].transform('max')]
|
||||||
except KeyError: # pragma: no cover
|
except KeyError: # pragma: no cover
|
||||||
return pd.Series(dtype='float'), pd.Series(dtype='float'), 0, pd.Series(dtype='float'), pd.Series(dtype='float')
|
return pd.Series(dtype='float'), pd.Series(dtype='float'), 0, pd.Series(dtype='float'), pd.Series(dtype='float')
|
||||||
|
|
||||||
df = df.sort_values(['delta']).reset_index()
|
df = df.sort_values(['delta']).reset_index()
|
||||||
|
df = df[df['cp']>20]
|
||||||
|
|
||||||
|
|
||||||
return df['delta'], df['cp'], 0, df['workout'], df['url']
|
return df['delta'], df['cp'], 0, df['workout'], df['url']
|
||||||
|
|
||||||
@@ -1011,14 +1053,22 @@ def checkbreakthrough(w, r):
|
|||||||
cpdf, delta, cpvalues = setcp(w)
|
cpdf, delta, cpvalues = setcp(w)
|
||||||
if not cpdf.empty:
|
if not cpdf.empty:
|
||||||
if workouttype in otwtypes:
|
if workouttype in otwtypes:
|
||||||
|
try:
|
||||||
res, btvalues, res2 = utils.isbreakthrough(
|
res, btvalues, res2 = utils.isbreakthrough(
|
||||||
delta, cpvalues, r.p0, r.p1, r.p2, r.p3, r.cpratio)
|
delta, cpvalues, r.p0, r.p1, r.p2, r.p3, r.cpratio)
|
||||||
_ = update_rolling_cp(r, otwtypes, 'water')
|
_ = update_rolling_cp(r, otwtypes, 'water')
|
||||||
|
except ValueError:
|
||||||
|
res = 0
|
||||||
|
res2 = 0
|
||||||
|
|
||||||
elif workouttype in otetypes:
|
elif workouttype in otetypes:
|
||||||
|
try:
|
||||||
res, btvalues, res2 = utils.isbreakthrough(
|
res, btvalues, res2 = utils.isbreakthrough(
|
||||||
delta, cpvalues, r.ep0, r.ep1, r.ep2, r.ep3, r.ecpratio)
|
delta, cpvalues, r.ep0, r.ep1, r.ep2, r.ep3, r.ecpratio)
|
||||||
_ = update_rolling_cp(r, otetypes, 'erg')
|
_ = update_rolling_cp(r, otetypes, 'erg')
|
||||||
|
except ValueError:
|
||||||
|
res = 0
|
||||||
|
res2 = 0
|
||||||
else: # pragma: no cover
|
else: # pragma: no cover
|
||||||
res = 0
|
res = 0
|
||||||
res2 = 0
|
res2 = 0
|
||||||
|
|||||||
+8
-9
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from scipy.interpolate import griddata
|
from scipy.interpolate import griddata
|
||||||
@@ -124,18 +123,18 @@ def cpfit(powerdf, fraclimit=0.0001, nmax=1000):
|
|||||||
|
|
||||||
|
|
||||||
def getlogarr(maxt):
|
def getlogarr(maxt):
|
||||||
maxlog10 = np.log10(maxt-5)
|
dtmin = 10
|
||||||
|
maxlog10 = np.log10(maxt)
|
||||||
# print(maxlog10,round(maxlog10))
|
# print(maxlog10,round(maxlog10))
|
||||||
aantal = 10*round(maxlog10)
|
aantal = 40
|
||||||
logarr = np.arange(aantal+1)/10.
|
logarr = np.arange(aantal+1)/10.
|
||||||
|
vs = 10.**logarr
|
||||||
|
|
||||||
|
|
||||||
res = []
|
res = []
|
||||||
for la in logarr:
|
for v in vs:
|
||||||
try:
|
if v > dtmin and v<maxt:
|
||||||
v = 5+int(10.**(la))
|
res.append(int(v))
|
||||||
except ValueError: # pragma: no cover
|
|
||||||
v = 0
|
|
||||||
res.append(v)
|
|
||||||
|
|
||||||
logarr = pd.Series(res, dtype='float')
|
logarr = pd.Series(res, dtype='float')
|
||||||
logarr.drop_duplicates(keep='first', inplace=True)
|
logarr.drop_duplicates(keep='first', inplace=True)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import datetime
|
|||||||
from rowers import mytypes
|
from rowers import mytypes
|
||||||
from rowers.courses import (
|
from rowers.courses import (
|
||||||
course_coord_center, course_coord_maxmin,
|
course_coord_center, course_coord_maxmin,
|
||||||
polygon_coord_center
|
polygon_coord_center, course_coord_crewnerd_navigation,
|
||||||
)
|
)
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
@@ -2165,6 +2165,8 @@ def interactive_histoall(theworkouts, histoparam, includereststrokes,
|
|||||||
|
|
||||||
def course_map(course):
|
def course_map(course):
|
||||||
latmean, lonmean, coordinates = course_coord_center(course)
|
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)
|
lat_min, lat_max, long_min, long_max = course_coord_maxmin(course)
|
||||||
|
|
||||||
coordinates = course_spline(coordinates)
|
coordinates = course_spline(coordinates)
|
||||||
@@ -4424,6 +4426,7 @@ def instroke_interactive_chart(df,metric, workout, spm_min, spm_max,
|
|||||||
df_plot
|
df_plot
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
TIPS = OrderedDict([
|
TIPS = OrderedDict([
|
||||||
('x','@x'),
|
('x','@x'),
|
||||||
('median','@median'),
|
('median','@median'),
|
||||||
@@ -4513,7 +4516,6 @@ def instroke_interactive_chart(df,metric, workout, spm_min, spm_max,
|
|||||||
script = ""
|
script = ""
|
||||||
div = "Something went wrong with the chart"
|
div = "Something went wrong with the chart"
|
||||||
|
|
||||||
|
|
||||||
return (script, div)
|
return (script, div)
|
||||||
|
|
||||||
|
|
||||||
@@ -4529,6 +4531,7 @@ def interactive_chart(id=0, promember=0, intervaldata={}):
|
|||||||
columns = ['time', 'pace', 'hr', 'fpace', 'ftime', 'spm']
|
columns = ['time', 'pace', 'hr', 'fpace', 'ftime', 'spm']
|
||||||
datadf = dataprep.getsmallrowdata_db(columns, ids=[id])
|
datadf = dataprep.getsmallrowdata_db(columns, ids=[id])
|
||||||
|
|
||||||
|
|
||||||
datadf.dropna(axis=0, how='any', inplace=True)
|
datadf.dropna(axis=0, how='any', inplace=True)
|
||||||
|
|
||||||
row = Workout.objects.get(id=id)
|
row = Workout.objects.get(id=id)
|
||||||
@@ -4576,6 +4579,7 @@ def interactive_chart(id=0, promember=0, intervaldata={}):
|
|||||||
)
|
)
|
||||||
|
|
||||||
plot.line('time', 'pace', source=source, legend_label="Pace", name="pace")
|
plot.line('time', 'pace', source=source, legend_label="Pace", name="pace")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
plot.title.text = row.name
|
plot.title.text = row.name
|
||||||
except ValueError: # pragma: no cover
|
except ValueError: # pragma: no cover
|
||||||
@@ -4645,7 +4649,6 @@ def interactive_chart(id=0, promember=0, intervaldata={}):
|
|||||||
|
|
||||||
hover.mode = 'mouse'
|
hover.mode = 'mouse'
|
||||||
# hover.name = ["spm", "pace"]
|
# hover.name = ["spm", "pace"]
|
||||||
|
|
||||||
script, div = components(plot)
|
script, div = components(plot)
|
||||||
|
|
||||||
return [script, div]
|
return [script, div]
|
||||||
@@ -5913,7 +5916,7 @@ def interactive_flex_chart2(id, r, promember=0,
|
|||||||
popt, pcov = optimize.curve_fit(func, x1, y1)
|
popt, pcov = optimize.curve_fit(func, x1, y1)
|
||||||
ytrend = func(x1, popt[0], popt[1])
|
ytrend = func(x1, popt[0], popt[1])
|
||||||
rowdata['ytrend'] = ytrend
|
rowdata['ytrend'] = ytrend
|
||||||
except TypeError:
|
except TypeError: # pragma: no cover
|
||||||
rowdata['ytrend'] = y1
|
rowdata['ytrend'] = y1
|
||||||
|
|
||||||
# prepare data
|
# prepare data
|
||||||
|
|||||||
+80
-1
@@ -8,7 +8,7 @@ from rowers.utils import (
|
|||||||
steps_read_fit, steps_write_fit, ps_dict_order, uniqify
|
steps_read_fit, steps_write_fit, ps_dict_order, uniqify
|
||||||
)
|
)
|
||||||
from rowers.metrics import axlabels
|
from rowers.metrics import axlabels
|
||||||
from rowers.utils import geo_distance
|
from rowers.utils import geo_distance, move_one_meter
|
||||||
from rowers.formfields import *
|
from rowers.formfields import *
|
||||||
from rowers.database import *
|
from rowers.database import *
|
||||||
import uuid
|
import uuid
|
||||||
@@ -594,6 +594,64 @@ def course_spline(coordinates):
|
|||||||
|
|
||||||
return newcoordinates
|
return newcoordinates
|
||||||
|
|
||||||
|
def polygon_nearest_point(polygon, latitude, longitude,debug=False):
|
||||||
|
points = GeoPoint.objects.filter(polygon=polygon)
|
||||||
|
points = sorted(points, key = lambda p: geo_distance(p.latitude, p.longitude, latitude, longitude))
|
||||||
|
|
||||||
|
#if debug:
|
||||||
|
# for p in points:
|
||||||
|
# print(p,p.latitude, p.longitude, latitude, longitude, geo_distance(p.latitude, p.longitude, latitude, longitude))
|
||||||
|
|
||||||
|
return points[0].latitude, points[0].longitude
|
||||||
|
|
||||||
|
def polygon_exit_point(polygon, lat1, lon1, lat2, lon2):
|
||||||
|
dist, bearing = geo_distance(lat1, lon1, lat2, lon2)
|
||||||
|
dirveclat = (lat2-lat1)/dist
|
||||||
|
dirveclon = (lon2-lon1)/dist
|
||||||
|
|
||||||
|
newlat, newlon = move_one_meter(lat2, lon2, bearing)
|
||||||
|
path = polygon_to_path(polygon)
|
||||||
|
while path.contains_points([(newlat, newlon)])[0]:
|
||||||
|
newlat, newlon = move_one_meter(newlat, newlon, bearing)
|
||||||
|
|
||||||
|
return newlat, newlon
|
||||||
|
|
||||||
|
def course_coord_crewnerd_navigation(course):
|
||||||
|
polygons = GeoPolygon.objects.filter(
|
||||||
|
course=course).order_by("order_in_course")
|
||||||
|
|
||||||
|
latitudes = []
|
||||||
|
longitudes = []
|
||||||
|
|
||||||
|
latitude, longitude = polygon_coord_center(polygons[0])
|
||||||
|
|
||||||
|
latitudes.append(latitude)
|
||||||
|
longitudes.append(longitude)
|
||||||
|
|
||||||
|
debug = True
|
||||||
|
|
||||||
|
for p in polygons[1:]:
|
||||||
|
oldlat = latitude
|
||||||
|
oldlon = longitude
|
||||||
|
latitude, longitude = polygon_nearest_point(p,latitude,longitude, debug=debug)
|
||||||
|
debug = False
|
||||||
|
latitudes.append(latitude)
|
||||||
|
longitudes.append(longitude)
|
||||||
|
latitude, longitude = polygon_exit_point(p, oldlat, oldlon, latitude, longitude)
|
||||||
|
latitudes.append(latitude)
|
||||||
|
longitudes.append(longitude)
|
||||||
|
|
||||||
|
|
||||||
|
latitude = pd.Series(latitudes).median()
|
||||||
|
longitude = pd.Series(longitudes).median()
|
||||||
|
|
||||||
|
coordinates = pd.DataFrame({
|
||||||
|
'latitude': latitudes,
|
||||||
|
'longitude': longitudes,
|
||||||
|
})
|
||||||
|
|
||||||
|
return latitude, longitude, coordinates
|
||||||
|
|
||||||
|
|
||||||
def course_coord_center(course):
|
def course_coord_center(course):
|
||||||
|
|
||||||
@@ -1590,6 +1648,24 @@ class GeoCourse(models.Model):
|
|||||||
def coord(self):
|
def coord(self):
|
||||||
return course_coord_center(self)
|
return course_coord_center(self)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def with_cn_nav_waypoints(self):
|
||||||
|
polygons = GeoPolygon.objects.filter(course=self).order_by("order_in_course")
|
||||||
|
|
||||||
|
if polygons[0].name != "Start":
|
||||||
|
return False
|
||||||
|
if polygons[len(polygons)-1].name != "Finish":
|
||||||
|
return False
|
||||||
|
for i in range(1,len(polygons)-1):
|
||||||
|
if polygons[i].name[0:2].lower() != 'wp':
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
getal = float(polygons[i].name[2:])
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class GeoCourseEditForm(ModelForm):
|
class GeoCourseEditForm(ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -1747,6 +1823,7 @@ class InstantPlan(models.Model):
|
|||||||
authorizationstring = 'Bearer '+settings.WORKOUTS_FIT_TOKEN
|
authorizationstring = 'Bearer '+settings.WORKOUTS_FIT_TOKEN
|
||||||
url = settings.WORKOUTS_FIT_URL+"/trainingplan/"
|
url = settings.WORKOUTS_FIT_URL+"/trainingplan/"
|
||||||
headers = {'Authorization': authorizationstring}
|
headers = {'Authorization': authorizationstring}
|
||||||
|
try:
|
||||||
response = requests.post(url=url, headers=headers, data=yamltext)
|
response = requests.post(url=url, headers=headers, data=yamltext)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
data = response.json()
|
data = response.json()
|
||||||
@@ -1756,6 +1833,8 @@ class InstantPlan(models.Model):
|
|||||||
self.description = data['description']
|
self.description = data['description']
|
||||||
self.duration = data['duration']
|
self.duration = data['duration']
|
||||||
self.yaml = None
|
self.yaml = None
|
||||||
|
except:
|
||||||
|
pass
|
||||||
super(InstantPlan, self).save(*args, **kwargs)
|
super(InstantPlan, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,185 +1,31 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
# source: otw-power-calculator.proto
|
# source: otw-power-calculator.proto
|
||||||
|
# Protobuf Python Version: 4.25.1
|
||||||
from google.protobuf import symbol_database as _symbol_database
|
"""Generated protocol buffer code."""
|
||||||
from google.protobuf import reflection as _reflection
|
|
||||||
from google.protobuf import message as _message
|
|
||||||
from google.protobuf import descriptor as _descriptor
|
from google.protobuf import descriptor as _descriptor
|
||||||
import sys
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||||
_b = sys.version_info[0] < 3 and (
|
from google.protobuf import symbol_database as _symbol_database
|
||||||
lambda x: x) or (lambda x: x.encode('latin1'))
|
from google.protobuf.internal import builder as _builder
|
||||||
# @@protoc_insertion_point(imports)
|
# @@protoc_insertion_point(imports)
|
||||||
|
|
||||||
_sym_db = _symbol_database.Default()
|
_sym_db = _symbol_database.Default()
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
|
||||||
name='otw-power-calculator.proto',
|
|
||||||
package='otw_power_calculator',
|
|
||||||
syntax='proto3',
|
|
||||||
serialized_options=None,
|
|
||||||
serialized_pb=_b('\n\x1aotw-power-calculator.proto\x12\x14otw_power_calculator\"\xc0\x01\n\x13WorkoutPowerRequest\x12\x10\n\x08\x66ilename\x18\x01 \x01(\t\x12\x10\n\x08\x62oattype\x18\x02 \x01(\t\x12\x10\n\x08\x63rewmass\x18\x03 \x01(\x01\x12\x15\n\rpowermeasured\x18\x04 \x01(\x08\x12\x13\n\x0bprogressurl\x18\x05 \x01(\t\x12\x0e\n\x06secret\x18\x06 \x01(\t\x12\x0e\n\x06silent\x18\x07 \x01(\x08\x12\x11\n\tboatclass\x18\x08 \x01(\t\x12\x14\n\x0c\x63oastalbrand\x18\t \x01(\t\"#\n\x11\x43\x61lculationResult\x12\x0e\n\x06result\x18\x01 \x01(\x05\x32j\n\x05Power\x12\x61\n\tCalcPower\x12).otw_power_calculator.WorkoutPowerRequest\x1a\'.otw_power_calculator.CalculationResult\"\x00\x62\x06proto3')
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
_WORKOUTPOWERREQUEST = _descriptor.Descriptor(
|
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1aotw-power-calculator.proto\x12\x14otw_power_calculator\"\xc0\x01\n\x13WorkoutPowerRequest\x12\x10\n\x08\x66ilename\x18\x01 \x01(\t\x12\x10\n\x08\x62oattype\x18\x02 \x01(\t\x12\x10\n\x08\x63rewmass\x18\x03 \x01(\x01\x12\x15\n\rpowermeasured\x18\x04 \x01(\x08\x12\x13\n\x0bprogressurl\x18\x05 \x01(\t\x12\x0e\n\x06secret\x18\x06 \x01(\t\x12\x0e\n\x06silent\x18\x07 \x01(\x08\x12\x11\n\tboatclass\x18\x08 \x01(\t\x12\x14\n\x0c\x63oastalbrand\x18\t \x01(\t\"#\n\x11\x43\x61lculationResult\x12\x0e\n\x06result\x18\x01 \x01(\x05\x32j\n\x05Power\x12\x61\n\tCalcPower\x12).otw_power_calculator.WorkoutPowerRequest\x1a\'.otw_power_calculator.CalculationResult\"\x00\x42\x18Z\x16./otw-power-calculatorb\x06proto3')
|
||||||
name='WorkoutPowerRequest',
|
|
||||||
full_name='otw_power_calculator.WorkoutPowerRequest',
|
|
||||||
filename=None,
|
|
||||||
file=DESCRIPTOR,
|
|
||||||
containing_type=None,
|
|
||||||
fields=[
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='filename', full_name='otw_power_calculator.WorkoutPowerRequest.filename', index=0,
|
|
||||||
number=1, type=9, cpp_type=9, label=1,
|
|
||||||
has_default_value=False, default_value=_b("").decode('utf-8'),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='boattype', full_name='otw_power_calculator.WorkoutPowerRequest.boattype', index=1,
|
|
||||||
number=2, type=9, cpp_type=9, label=1,
|
|
||||||
has_default_value=False, default_value=_b("").decode('utf-8'),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='crewmass', full_name='otw_power_calculator.WorkoutPowerRequest.crewmass', index=2,
|
|
||||||
number=3, type=1, cpp_type=5, label=1,
|
|
||||||
has_default_value=False, default_value=float(0),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='powermeasured', full_name='otw_power_calculator.WorkoutPowerRequest.powermeasured', index=3,
|
|
||||||
number=4, type=8, cpp_type=7, label=1,
|
|
||||||
has_default_value=False, default_value=False,
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='progressurl', full_name='otw_power_calculator.WorkoutPowerRequest.progressurl', index=4,
|
|
||||||
number=5, type=9, cpp_type=9, label=1,
|
|
||||||
has_default_value=False, default_value=_b("").decode('utf-8'),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='secret', full_name='otw_power_calculator.WorkoutPowerRequest.secret', index=5,
|
|
||||||
number=6, type=9, cpp_type=9, label=1,
|
|
||||||
has_default_value=False, default_value=_b("").decode('utf-8'),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='silent', full_name='otw_power_calculator.WorkoutPowerRequest.silent', index=6,
|
|
||||||
number=7, type=8, cpp_type=7, label=1,
|
|
||||||
has_default_value=False, default_value=False,
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='boatclass', full_name='otw_power_calculator.WorkoutPowerRequest.boatclass', index=7,
|
|
||||||
number=8, type=9, cpp_type=9, label=1,
|
|
||||||
has_default_value=False, default_value=_b("").decode('utf-8'),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='coastalbrand', full_name='otw_power_calculator.WorkoutPowerRequest.coastalbrand', index=8,
|
|
||||||
number=9, type=9, cpp_type=9, label=1,
|
|
||||||
has_default_value=False, default_value=_b("").decode('utf-8'),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
],
|
|
||||||
extensions=[
|
|
||||||
],
|
|
||||||
nested_types=[],
|
|
||||||
enum_types=[
|
|
||||||
],
|
|
||||||
serialized_options=None,
|
|
||||||
is_extendable=False,
|
|
||||||
syntax='proto3',
|
|
||||||
extension_ranges=[],
|
|
||||||
oneofs=[
|
|
||||||
],
|
|
||||||
serialized_start=53,
|
|
||||||
serialized_end=245,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
_CALCULATIONRESULT = _descriptor.Descriptor(
|
|
||||||
name='CalculationResult',
|
|
||||||
full_name='otw_power_calculator.CalculationResult',
|
|
||||||
filename=None,
|
|
||||||
file=DESCRIPTOR,
|
|
||||||
containing_type=None,
|
|
||||||
fields=[
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='result', full_name='otw_power_calculator.CalculationResult.result', index=0,
|
|
||||||
number=1, type=5, cpp_type=1, label=1,
|
|
||||||
has_default_value=False, default_value=0,
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
],
|
|
||||||
extensions=[
|
|
||||||
],
|
|
||||||
nested_types=[],
|
|
||||||
enum_types=[
|
|
||||||
],
|
|
||||||
serialized_options=None,
|
|
||||||
is_extendable=False,
|
|
||||||
syntax='proto3',
|
|
||||||
extension_ranges=[],
|
|
||||||
oneofs=[
|
|
||||||
],
|
|
||||||
serialized_start=247,
|
|
||||||
serialized_end=282,
|
|
||||||
)
|
|
||||||
|
|
||||||
DESCRIPTOR.message_types_by_name['WorkoutPowerRequest'] = _WORKOUTPOWERREQUEST
|
|
||||||
DESCRIPTOR.message_types_by_name['CalculationResult'] = _CALCULATIONRESULT
|
|
||||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
|
||||||
|
|
||||||
WorkoutPowerRequest = _reflection.GeneratedProtocolMessageType('WorkoutPowerRequest', (_message.Message,), {
|
|
||||||
'DESCRIPTOR': _WORKOUTPOWERREQUEST,
|
|
||||||
'__module__': 'otw_power_calculator_pb2'
|
|
||||||
# @@protoc_insertion_point(class_scope:otw_power_calculator.WorkoutPowerRequest)
|
|
||||||
})
|
|
||||||
_sym_db.RegisterMessage(WorkoutPowerRequest)
|
|
||||||
|
|
||||||
CalculationResult = _reflection.GeneratedProtocolMessageType('CalculationResult', (_message.Message,), {
|
|
||||||
'DESCRIPTOR': _CALCULATIONRESULT,
|
|
||||||
'__module__': 'otw_power_calculator_pb2'
|
|
||||||
# @@protoc_insertion_point(class_scope:otw_power_calculator.CalculationResult)
|
|
||||||
})
|
|
||||||
_sym_db.RegisterMessage(CalculationResult)
|
|
||||||
|
|
||||||
|
|
||||||
_POWER = _descriptor.ServiceDescriptor(
|
|
||||||
name='Power',
|
|
||||||
full_name='otw_power_calculator.Power',
|
|
||||||
file=DESCRIPTOR,
|
|
||||||
index=0,
|
|
||||||
serialized_options=None,
|
|
||||||
serialized_start=284,
|
|
||||||
serialized_end=390,
|
|
||||||
methods=[
|
|
||||||
_descriptor.MethodDescriptor(
|
|
||||||
name='CalcPower',
|
|
||||||
full_name='otw_power_calculator.Power.CalcPower',
|
|
||||||
index=0,
|
|
||||||
containing_service=None,
|
|
||||||
input_type=_WORKOUTPOWERREQUEST,
|
|
||||||
output_type=_CALCULATIONRESULT,
|
|
||||||
serialized_options=None,
|
|
||||||
),
|
|
||||||
])
|
|
||||||
_sym_db.RegisterServiceDescriptor(_POWER)
|
|
||||||
|
|
||||||
DESCRIPTOR.services_by_name['Power'] = _POWER
|
|
||||||
|
|
||||||
|
_globals = globals()
|
||||||
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'otw_power_calculator_pb2', _globals)
|
||||||
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
|
_globals['DESCRIPTOR']._options = None
|
||||||
|
_globals['DESCRIPTOR']._serialized_options = b'Z\026./otw-power-calculator'
|
||||||
|
_globals['_WORKOUTPOWERREQUEST']._serialized_start=53
|
||||||
|
_globals['_WORKOUTPOWERREQUEST']._serialized_end=245
|
||||||
|
_globals['_CALCULATIONRESULT']._serialized_start=247
|
||||||
|
_globals['_CALCULATIONRESULT']._serialized_end=282
|
||||||
|
_globals['_POWER']._serialized_start=284
|
||||||
|
_globals['_POWER']._serialized_end=390
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
|
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
|
||||||
|
"""Client and server classes corresponding to protobuf-defined services."""
|
||||||
import grpc
|
import grpc
|
||||||
|
|
||||||
import rowers.otw_power_calculator_pb2 as otw__power__calculator__pb2
|
import rowers.otw_power_calculator_pb2 as otw__power__calculator__pb2
|
||||||
@@ -25,23 +26,44 @@ class PowerServicer(object):
|
|||||||
"""Power service definition
|
"""Power service definition
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def CalcPower(self, request, context): # pragma: no cover
|
def CalcPower(self, request, context):
|
||||||
# missing associated documentation comment in .proto file
|
"""Missing associated documentation comment in .proto file."""
|
||||||
pass
|
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED) # pragma: no cover
|
context.set_details('Method not implemented!')
|
||||||
context.set_details('Method not implemented!') # pragma: no cover
|
raise NotImplementedError('Method not implemented!')
|
||||||
raise NotImplementedError(
|
|
||||||
'Method not implemented!') # pragma: no cover
|
|
||||||
|
|
||||||
|
|
||||||
def add_PowerServicer_to_server(servicer, server): # pragma: no cover
|
def add_PowerServicer_to_server(servicer, server):
|
||||||
rpc_method_handlers = {
|
rpc_method_handlers = {
|
||||||
'CalcPower': grpc.unary_unary_rpc_method_handler(
|
'CalcPower': grpc.unary_unary_rpc_method_handler(
|
||||||
servicer.CalcPower,
|
servicer.CalcPower,
|
||||||
request_deserializer=otw__power__calculator__pb2.WorkoutPowerRequest.FromString,
|
request_deserializer=otw__power__calculator__pb2.WorkoutPowerRequest.FromString,
|
||||||
response_serializer=otw__power__calculator__pb2.CalculationResult.SerializeToString,
|
response_serializer=otw__power__calculator__pb2.CalculationResult.SerializeToString,
|
||||||
),
|
),
|
||||||
} # pragma: no cover
|
}
|
||||||
generic_handler = grpc.method_handlers_generic_handler(
|
generic_handler = grpc.method_handlers_generic_handler(
|
||||||
'otw_power_calculator.Power', rpc_method_handlers)
|
'otw_power_calculator.Power', rpc_method_handlers)
|
||||||
server.add_generic_rpc_handlers((generic_handler,))
|
server.add_generic_rpc_handlers((generic_handler,))
|
||||||
|
|
||||||
|
|
||||||
|
# This class is part of an EXPERIMENTAL API.
|
||||||
|
class Power(object):
|
||||||
|
"""Power service definition
|
||||||
|
"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def CalcPower(request,
|
||||||
|
target,
|
||||||
|
options=(),
|
||||||
|
channel_credentials=None,
|
||||||
|
call_credentials=None,
|
||||||
|
insecure=False,
|
||||||
|
compression=None,
|
||||||
|
wait_for_ready=None,
|
||||||
|
timeout=None,
|
||||||
|
metadata=None):
|
||||||
|
return grpc.experimental.unary_unary(request, target, '/otw_power_calculator.Power/CalcPower',
|
||||||
|
otw__power__calculator__pb2.WorkoutPowerRequest.SerializeToString,
|
||||||
|
otw__power__calculator__pb2.CalculationResult.FromString,
|
||||||
|
options, channel_credentials,
|
||||||
|
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
||||||
|
|||||||
@@ -1,210 +1,35 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||||
# source: rowing-workout-metrics.proto
|
# source: rowing-workout-metrics.proto
|
||||||
|
# Protobuf Python Version: 4.25.1
|
||||||
from google.protobuf import symbol_database as _symbol_database
|
"""Generated protocol buffer code."""
|
||||||
from google.protobuf import reflection as _reflection
|
|
||||||
from google.protobuf import message as _message
|
|
||||||
from google.protobuf import descriptor as _descriptor
|
from google.protobuf import descriptor as _descriptor
|
||||||
import sys
|
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||||
_b = sys.version_info[0] < 3 and (
|
from google.protobuf import symbol_database as _symbol_database
|
||||||
lambda x: x) or (lambda x: x.encode('latin1'))
|
from google.protobuf.internal import builder as _builder
|
||||||
# @@protoc_insertion_point(imports)
|
# @@protoc_insertion_point(imports)
|
||||||
|
|
||||||
_sym_db = _symbol_database.Default()
|
_sym_db = _symbol_database.Default()
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
|
||||||
name='rowing-workout-metrics.proto',
|
|
||||||
package='rowing_workout_metrics',
|
|
||||||
syntax='proto3',
|
|
||||||
serialized_options=None,
|
|
||||||
serialized_pb=_b('\n\x1crowing-workout-metrics.proto\x12\x16'
|
|
||||||
'rowing_workout_metrics\"p\n\x15WorkoutMetricsRequest'
|
|
||||||
'\x12\x10\n\x08\x66ilename'
|
|
||||||
'\x18\x01 \x01(\t\x12\x0b\n\x03sex'
|
|
||||||
'\x18\x02 \x01(\t\x12\x0b\n\x03\x66tp\x18\x03 \x01'
|
|
||||||
'(\x01\x12\r\n\x05hrftp\x18\x04 \x01(\x01\x12\r\n\x05hrmax'
|
|
||||||
'\x18\x05 \x01(\x01\x12\r\n\x05hrmin\x18\x06 \x01(\x01\"p\n\x16WorkoutMetricsResponse'
|
|
||||||
'\x12\x0b\n\x03tss\x18\x01 \x01(\x01\x12\r\n\x05normp\x18\x02 \x01(\x01\x12\r\n\x05trimp'
|
|
||||||
'\x18\x03 \x01(\x01\x12\r\n\x05hrtss\x18\x04 \x01(\x01\x12\r\n\x05normv'
|
|
||||||
'\x18\x05 \x01(\x01\x12\r\n\x05normw\x18\x06 \x01(\x01\x32w\n\x07Metrics'
|
|
||||||
'\x12l\n\x0b\x43\x61lcMetrics\x12-.rowing_workout_metrics.WorkoutMetricsRequest'
|
|
||||||
'\x1a..rowing_workout_metrics.WorkoutMetricsResponseb\x06proto3')
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
_WORKOUTMETRICSREQUEST = _descriptor.Descriptor(
|
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1crowing-workout-metrics.proto\x12\x16rowing_workout_metrics\"p\n\x15WorkoutMetricsRequest\x12\x10\n\x08\x66ilename\x18\x01 \x01(\t\x12\x0b\n\x03sex\x18\x02 \x01(\t\x12\x0b\n\x03\x66tp\x18\x03 \x01(\x01\x12\r\n\x05hrftp\x18\x04 \x01(\x01\x12\r\n\x05hrmax\x18\x05 \x01(\x01\x12\r\n\x05hrmin\x18\x06 \x01(\x01\"p\n\x16WorkoutMetricsResponse\x12\x0b\n\x03tss\x18\x01 \x01(\x01\x12\r\n\x05normp\x18\x02 \x01(\x01\x12\r\n\x05trimp\x18\x03 \x01(\x01\x12\r\n\x05hrtss\x18\x04 \x01(\x01\x12\r\n\x05normv\x18\x05 \x01(\x01\x12\r\n\x05normw\x18\x06 \x01(\x01\"=\n\tCPRequest\x12\x10\n\x08\x66ilename\x18\x01 \x01(\t\x12\x10\n\x08\x66iletype\x18\x02 \x01(\t\x12\x0c\n\x04tarr\x18\x03 \x03(\x01\"<\n\nCPResponse\x12\r\n\x05\x64\x65lta\x18\x01 \x03(\x01\x12\r\n\x05power\x18\x02 \x03(\x01\x12\x10\n\x08\x61vgpower\x18\x03 \x01(\x01\x32\xc7\x01\n\x07Metrics\x12l\n\x0b\x43\x61lcMetrics\x12-.rowing_workout_metrics.WorkoutMetricsRequest\x1a..rowing_workout_metrics.WorkoutMetricsResponse\x12N\n\x05GetCP\x12!.rowing_workout_metrics.CPRequest\x1a\".rowing_workout_metrics.CPResponseB\x1aZ\x18./rowing-workout-metricsb\x06proto3')
|
||||||
name='WorkoutMetricsRequest',
|
|
||||||
full_name='rowing_workout_metrics.WorkoutMetricsRequest',
|
|
||||||
filename=None,
|
|
||||||
file=DESCRIPTOR,
|
|
||||||
containing_type=None,
|
|
||||||
fields=[
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='filename', full_name='rowing_workout_metrics.WorkoutMetricsRequest.filename', index=0,
|
|
||||||
number=1, type=9, cpp_type=9, label=1,
|
|
||||||
has_default_value=False, default_value=_b("").decode('utf-8'),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='sex', full_name='rowing_workout_metrics.WorkoutMetricsRequest.sex', index=1,
|
|
||||||
number=2, type=9, cpp_type=9, label=1,
|
|
||||||
has_default_value=False, default_value=_b("").decode('utf-8'),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='ftp', full_name='rowing_workout_metrics.WorkoutMetricsRequest.ftp', index=2,
|
|
||||||
number=3, type=1, cpp_type=5, label=1,
|
|
||||||
has_default_value=False, default_value=float(0),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='hrftp', full_name='rowing_workout_metrics.WorkoutMetricsRequest.hrftp', index=3,
|
|
||||||
number=4, type=1, cpp_type=5, label=1,
|
|
||||||
has_default_value=False, default_value=float(0),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='hrmax', full_name='rowing_workout_metrics.WorkoutMetricsRequest.hrmax', index=4,
|
|
||||||
number=5, type=1, cpp_type=5, label=1,
|
|
||||||
has_default_value=False, default_value=float(0),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='hrmin', full_name='rowing_workout_metrics.WorkoutMetricsRequest.hrmin', index=5,
|
|
||||||
number=6, type=1, cpp_type=5, label=1,
|
|
||||||
has_default_value=False, default_value=float(0),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
],
|
|
||||||
extensions=[
|
|
||||||
],
|
|
||||||
nested_types=[],
|
|
||||||
enum_types=[
|
|
||||||
],
|
|
||||||
serialized_options=None,
|
|
||||||
is_extendable=False,
|
|
||||||
syntax='proto3',
|
|
||||||
extension_ranges=[],
|
|
||||||
oneofs=[
|
|
||||||
],
|
|
||||||
serialized_start=56,
|
|
||||||
serialized_end=168,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
_WORKOUTMETRICSRESPONSE = _descriptor.Descriptor(
|
|
||||||
name='WorkoutMetricsResponse',
|
|
||||||
full_name='rowing_workout_metrics.WorkoutMetricsResponse',
|
|
||||||
filename=None,
|
|
||||||
file=DESCRIPTOR,
|
|
||||||
containing_type=None,
|
|
||||||
fields=[
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='tss', full_name='rowing_workout_metrics.WorkoutMetricsResponse.tss', index=0,
|
|
||||||
number=1, type=1, cpp_type=5, label=1,
|
|
||||||
has_default_value=False, default_value=float(0),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='normp', full_name='rowing_workout_metrics.WorkoutMetricsResponse.normp', index=1,
|
|
||||||
number=2, type=1, cpp_type=5, label=1,
|
|
||||||
has_default_value=False, default_value=float(0),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='trimp', full_name='rowing_workout_metrics.WorkoutMetricsResponse.trimp', index=2,
|
|
||||||
number=3, type=1, cpp_type=5, label=1,
|
|
||||||
has_default_value=False, default_value=float(0),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='hrtss', full_name='rowing_workout_metrics.WorkoutMetricsResponse.hrtss', index=3,
|
|
||||||
number=4, type=1, cpp_type=5, label=1,
|
|
||||||
has_default_value=False, default_value=float(0),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='normv', full_name='rowing_workout_metrics.WorkoutMetricsResponse.normv', index=4,
|
|
||||||
number=5, type=1, cpp_type=5, label=1,
|
|
||||||
has_default_value=False, default_value=float(0),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
_descriptor.FieldDescriptor(
|
|
||||||
name='normw', full_name='rowing_workout_metrics.WorkoutMetricsResponse.normw', index=5,
|
|
||||||
number=6, type=1, cpp_type=5, label=1,
|
|
||||||
has_default_value=False, default_value=float(0),
|
|
||||||
message_type=None, enum_type=None, containing_type=None,
|
|
||||||
is_extension=False, extension_scope=None,
|
|
||||||
serialized_options=None, file=DESCRIPTOR),
|
|
||||||
],
|
|
||||||
extensions=[
|
|
||||||
],
|
|
||||||
nested_types=[],
|
|
||||||
enum_types=[
|
|
||||||
],
|
|
||||||
serialized_options=None,
|
|
||||||
is_extendable=False,
|
|
||||||
syntax='proto3',
|
|
||||||
extension_ranges=[],
|
|
||||||
oneofs=[
|
|
||||||
],
|
|
||||||
serialized_start=170,
|
|
||||||
serialized_end=282,
|
|
||||||
)
|
|
||||||
|
|
||||||
DESCRIPTOR.message_types_by_name['WorkoutMetricsRequest'] = _WORKOUTMETRICSREQUEST
|
|
||||||
DESCRIPTOR.message_types_by_name['WorkoutMetricsResponse'] = _WORKOUTMETRICSRESPONSE
|
|
||||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
|
||||||
|
|
||||||
WorkoutMetricsRequest = _reflection.GeneratedProtocolMessageType('WorkoutMetricsRequest', (_message.Message,), {
|
|
||||||
'DESCRIPTOR': _WORKOUTMETRICSREQUEST,
|
|
||||||
'__module__': 'rowing_workout_metrics_pb2'
|
|
||||||
# @@protoc_insertion_point(class_scope:rowing_workout_metrics.WorkoutMetricsRequest)
|
|
||||||
})
|
|
||||||
_sym_db.RegisterMessage(WorkoutMetricsRequest)
|
|
||||||
|
|
||||||
WorkoutMetricsResponse = _reflection.GeneratedProtocolMessageType('WorkoutMetricsResponse', (_message.Message,), {
|
|
||||||
'DESCRIPTOR': _WORKOUTMETRICSRESPONSE,
|
|
||||||
'__module__': 'rowing_workout_metrics_pb2'
|
|
||||||
# @@protoc_insertion_point(class_scope:rowing_workout_metrics.WorkoutMetricsResponse)
|
|
||||||
})
|
|
||||||
_sym_db.RegisterMessage(WorkoutMetricsResponse)
|
|
||||||
|
|
||||||
|
|
||||||
_METRICS = _descriptor.ServiceDescriptor(
|
|
||||||
name='Metrics',
|
|
||||||
full_name='rowing_workout_metrics.Metrics',
|
|
||||||
file=DESCRIPTOR,
|
|
||||||
index=0,
|
|
||||||
serialized_options=None,
|
|
||||||
serialized_start=284,
|
|
||||||
serialized_end=403,
|
|
||||||
methods=[
|
|
||||||
_descriptor.MethodDescriptor(
|
|
||||||
name='CalcMetrics',
|
|
||||||
full_name='rowing_workout_metrics.Metrics.CalcMetrics',
|
|
||||||
index=0,
|
|
||||||
containing_service=None,
|
|
||||||
input_type=_WORKOUTMETRICSREQUEST,
|
|
||||||
output_type=_WORKOUTMETRICSRESPONSE,
|
|
||||||
serialized_options=None,
|
|
||||||
),
|
|
||||||
])
|
|
||||||
_sym_db.RegisterServiceDescriptor(_METRICS)
|
|
||||||
|
|
||||||
DESCRIPTOR.services_by_name['Metrics'] = _METRICS
|
|
||||||
|
|
||||||
|
_globals = globals()
|
||||||
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||||
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'rowing_workout_metrics_pb2', _globals)
|
||||||
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||||
|
_globals['DESCRIPTOR']._options = None
|
||||||
|
_globals['DESCRIPTOR']._serialized_options = b'Z\030./rowing-workout-metrics'
|
||||||
|
_globals['_WORKOUTMETRICSREQUEST']._serialized_start=56
|
||||||
|
_globals['_WORKOUTMETRICSREQUEST']._serialized_end=168
|
||||||
|
_globals['_WORKOUTMETRICSRESPONSE']._serialized_start=170
|
||||||
|
_globals['_WORKOUTMETRICSRESPONSE']._serialized_end=282
|
||||||
|
_globals['_CPREQUEST']._serialized_start=284
|
||||||
|
_globals['_CPREQUEST']._serialized_end=345
|
||||||
|
_globals['_CPRESPONSE']._serialized_start=347
|
||||||
|
_globals['_CPRESPONSE']._serialized_end=407
|
||||||
|
_globals['_METRICS']._serialized_start=410
|
||||||
|
_globals['_METRICS']._serialized_end=609
|
||||||
# @@protoc_insertion_point(module_scope)
|
# @@protoc_insertion_point(module_scope)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
|
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
|
||||||
|
"""Client and server classes corresponding to protobuf-defined services."""
|
||||||
import grpc
|
import grpc
|
||||||
|
|
||||||
import rowers.rowing_workout_metrics_pb2 as rowing__workout__metrics__pb2
|
import rowers.rowing_workout_metrics_pb2 as rowing__workout__metrics__pb2
|
||||||
@@ -19,6 +20,11 @@ class MetricsStub(object):
|
|||||||
request_serializer=rowing__workout__metrics__pb2.WorkoutMetricsRequest.SerializeToString,
|
request_serializer=rowing__workout__metrics__pb2.WorkoutMetricsRequest.SerializeToString,
|
||||||
response_deserializer=rowing__workout__metrics__pb2.WorkoutMetricsResponse.FromString,
|
response_deserializer=rowing__workout__metrics__pb2.WorkoutMetricsResponse.FromString,
|
||||||
)
|
)
|
||||||
|
self.GetCP = channel.unary_unary(
|
||||||
|
'/rowing_workout_metrics.Metrics/GetCP',
|
||||||
|
request_serializer=rowing__workout__metrics__pb2.CPRequest.SerializeToString,
|
||||||
|
response_deserializer=rowing__workout__metrics__pb2.CPResponse.FromString,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class MetricsServicer(object):
|
class MetricsServicer(object):
|
||||||
@@ -26,22 +32,71 @@ class MetricsServicer(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def CalcMetrics(self, request, context):
|
def CalcMetrics(self, request, context):
|
||||||
# missing associated documentation comment in .proto file
|
"""Missing associated documentation comment in .proto file."""
|
||||||
pass
|
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED) # pragma: no cover
|
context.set_details('Method not implemented!')
|
||||||
context.set_details('Method not implemented!') # pragma: no cover
|
raise NotImplementedError('Method not implemented!')
|
||||||
raise NotImplementedError(
|
|
||||||
'Method not implemented!') # pragma: no cover
|
def GetCP(self, request, context):
|
||||||
|
"""Missing associated documentation comment in .proto file."""
|
||||||
|
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||||
|
context.set_details('Method not implemented!')
|
||||||
|
raise NotImplementedError('Method not implemented!')
|
||||||
|
|
||||||
|
|
||||||
def add_MetricsServicer_to_server(servicer, server): # pragma: no cover
|
def add_MetricsServicer_to_server(servicer, server):
|
||||||
rpc_method_handlers = {
|
rpc_method_handlers = {
|
||||||
'CalcMetrics': grpc.unary_unary_rpc_method_handler(
|
'CalcMetrics': grpc.unary_unary_rpc_method_handler(
|
||||||
servicer.CalcMetrics,
|
servicer.CalcMetrics,
|
||||||
request_deserializer=rowing__workout__metrics__pb2.WorkoutMetricsRequest.FromString,
|
request_deserializer=rowing__workout__metrics__pb2.WorkoutMetricsRequest.FromString,
|
||||||
response_serializer=rowing__workout__metrics__pb2.WorkoutMetricsResponse.SerializeToString,
|
response_serializer=rowing__workout__metrics__pb2.WorkoutMetricsResponse.SerializeToString,
|
||||||
),
|
),
|
||||||
|
'GetCP': grpc.unary_unary_rpc_method_handler(
|
||||||
|
servicer.GetCP,
|
||||||
|
request_deserializer=rowing__workout__metrics__pb2.CPRequest.FromString,
|
||||||
|
response_serializer=rowing__workout__metrics__pb2.CPResponse.SerializeToString,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
generic_handler = grpc.method_handlers_generic_handler(
|
generic_handler = grpc.method_handlers_generic_handler(
|
||||||
'rowing_workout_metrics.Metrics', rpc_method_handlers)
|
'rowing_workout_metrics.Metrics', rpc_method_handlers)
|
||||||
server.add_generic_rpc_handlers((generic_handler,))
|
server.add_generic_rpc_handlers((generic_handler,))
|
||||||
|
|
||||||
|
|
||||||
|
# This class is part of an EXPERIMENTAL API.
|
||||||
|
class Metrics(object):
|
||||||
|
"""OTW-metrics service definition
|
||||||
|
"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def CalcMetrics(request,
|
||||||
|
target,
|
||||||
|
options=(),
|
||||||
|
channel_credentials=None,
|
||||||
|
call_credentials=None,
|
||||||
|
insecure=False,
|
||||||
|
compression=None,
|
||||||
|
wait_for_ready=None,
|
||||||
|
timeout=None,
|
||||||
|
metadata=None):
|
||||||
|
return grpc.experimental.unary_unary(request, target, '/rowing_workout_metrics.Metrics/CalcMetrics',
|
||||||
|
rowing__workout__metrics__pb2.WorkoutMetricsRequest.SerializeToString,
|
||||||
|
rowing__workout__metrics__pb2.WorkoutMetricsResponse.FromString,
|
||||||
|
options, channel_credentials,
|
||||||
|
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def GetCP(request,
|
||||||
|
target,
|
||||||
|
options=(),
|
||||||
|
channel_credentials=None,
|
||||||
|
call_credentials=None,
|
||||||
|
insecure=False,
|
||||||
|
compression=None,
|
||||||
|
wait_for_ready=None,
|
||||||
|
timeout=None,
|
||||||
|
metadata=None):
|
||||||
|
return grpc.experimental.unary_unary(request, target, '/rowing_workout_metrics.Metrics/GetCP',
|
||||||
|
rowing__workout__metrics__pb2.CPRequest.SerializeToString,
|
||||||
|
rowing__workout__metrics__pb2.CPResponse.FromString,
|
||||||
|
options, channel_credentials,
|
||||||
|
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
||||||
|
|||||||
+22
-23
@@ -94,7 +94,7 @@ SITE_URL_DEV = CFG['site_url']
|
|||||||
PROGRESS_CACHE_SECRET = CFG['progress_cache_secret']
|
PROGRESS_CACHE_SECRET = CFG['progress_cache_secret']
|
||||||
try:
|
try:
|
||||||
SETTINGS_NAME = CFG['settings_name']
|
SETTINGS_NAME = CFG['settings_name']
|
||||||
except KeyError:
|
except KeyError: # pragma: no cover
|
||||||
SETTINGS_NAME = 'rowsandall_ap.settings'
|
SETTINGS_NAME = 'rowsandall_ap.settings'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -321,7 +321,7 @@ def summaryfromsplitdata(splitdata, data, filename, sep='|', workouttype='rower'
|
|||||||
return sums, sa, results
|
return sums, sa, results
|
||||||
|
|
||||||
@app.task
|
@app.task
|
||||||
def handle_post_workout_api(uploadoptions, debug=False, **kwargs):
|
def handle_post_workout_api(uploadoptions, debug=False, **kwargs): # pragma: no cover
|
||||||
session = requests.session()
|
session = requests.session()
|
||||||
newHeaders = {'Content-type': 'application/json', 'Accept': 'text/plain'}
|
newHeaders = {'Content-type': 'application/json', 'Accept': 'text/plain'}
|
||||||
session.headers.update(newHeaders)
|
session.headers.update(newHeaders)
|
||||||
@@ -335,14 +335,14 @@ def handle_post_workout_api(uploadoptions, debug=False, **kwargs):
|
|||||||
|
|
||||||
|
|
||||||
@app.task
|
@app.task
|
||||||
def handle_remove_workouts_team(ws, t, debug=False, **kwargs):
|
def handle_remove_workouts_team(ws, t, debug=False, **kwargs): # pragma: no cover
|
||||||
for w in ws:
|
for w in ws:
|
||||||
w.team.remove(t)
|
w.team.remove(t)
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
@app.task
|
@app.task
|
||||||
def handle_add_workouts_team(ws, t, debug=False, **kwargs):
|
def handle_add_workouts_team(ws, t, debug=False, **kwargs): # pragma: no cover
|
||||||
|
|
||||||
for w in ws:
|
for w in ws:
|
||||||
w.team.add(t)
|
w.team.add(t)
|
||||||
@@ -350,7 +350,7 @@ def handle_add_workouts_team(ws, t, debug=False, **kwargs):
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
def uploadactivity(access_token, filename, description='',
|
def uploadactivity(access_token, filename, description='',
|
||||||
name='Rowsandall.com workout'):
|
name='Rowsandall.com workout'): # pragma: no cover
|
||||||
|
|
||||||
data_gz = BytesIO()
|
data_gz = BytesIO()
|
||||||
try:
|
try:
|
||||||
@@ -393,7 +393,7 @@ def uploadactivity(access_token, filename, description='',
|
|||||||
|
|
||||||
|
|
||||||
@app.task
|
@app.task
|
||||||
def check_tp_workout_id(workout, location, attempts=5, debug=False, **kwargs):
|
def check_tp_workout_id(workout, location, attempts=5, debug=False, **kwargs): # pragma: no cover
|
||||||
authorizationstring = str('Bearer ' + workout.user.tptoken)
|
authorizationstring = str('Bearer ' + workout.user.tptoken)
|
||||||
headers = {'Authorization': authorizationstring,
|
headers = {'Authorization': authorizationstring,
|
||||||
'user-agent': 'sanderroosendaal',
|
'user-agent': 'sanderroosendaal',
|
||||||
@@ -411,7 +411,7 @@ def check_tp_workout_id(workout, location, attempts=5, debug=False, **kwargs):
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
@app.task
|
@app.task
|
||||||
def handle_workout_tp_upload(w, thetoken, tcxfilename, debug=False, **kwargs):
|
def handle_workout_tp_upload(w, thetoken, tcxfilename, debug=False, **kwargs): # pragma: no cover
|
||||||
tpid = 0
|
tpid = 0
|
||||||
r = w.user
|
r = w.user
|
||||||
if not tcxfilename:
|
if not tcxfilename:
|
||||||
@@ -443,7 +443,7 @@ def handle_workout_tp_upload(w, thetoken, tcxfilename, debug=False, **kwargs):
|
|||||||
return tpid
|
return tpid
|
||||||
|
|
||||||
@app.task
|
@app.task
|
||||||
def instroke_static(w, metric, debug=False, **kwargs):
|
def instroke_static(w, metric, debug=False, **kwargs): # pragma: no cover
|
||||||
f1 = w.csvfilename[6:-4]
|
f1 = w.csvfilename[6:-4]
|
||||||
rowdata = rdata(csvfile=w.csvfilename)
|
rowdata = rdata(csvfile=w.csvfilename)
|
||||||
|
|
||||||
@@ -512,7 +512,7 @@ def handle_c2_sync(workoutid, url, headers, data, debug=False, **kwargs):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
workout = Workout.objects.get(id=workoutid)
|
workout = Workout.objects.get(id=workoutid)
|
||||||
except Workout.DoesNotExist:
|
except Workout.DoesNotExist: # pragma: no cover
|
||||||
dologging('c2_log.log','failed for c2id {c2id}'.format(c2id=c2id))
|
dologging('c2_log.log','failed for c2id {c2id}'.format(c2id=c2id))
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@@ -531,7 +531,7 @@ def handle_c2_sync(workoutid, url, headers, data, debug=False, **kwargs):
|
|||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def splitstdata(lijst):
|
def splitstdata(lijst): # pragma: no cover
|
||||||
t = []
|
t = []
|
||||||
latlong = []
|
latlong = []
|
||||||
while len(lijst) >= 2:
|
while len(lijst) >= 2:
|
||||||
@@ -543,7 +543,7 @@ def splitstdata(lijst):
|
|||||||
|
|
||||||
@app.task
|
@app.task
|
||||||
def handle_sporttracks_workout_from_data(user, importid, source,
|
def handle_sporttracks_workout_from_data(user, importid, source,
|
||||||
workoutsource, debug=False, **kwargs):
|
workoutsource, debug=False, **kwargs): # pragma: no cover
|
||||||
|
|
||||||
r = user.rower
|
r = user.rower
|
||||||
authorizationstring = str('Bearer ' + r.sporttrackstoken)
|
authorizationstring = str('Bearer ' + r.sporttrackstoken)
|
||||||
@@ -812,7 +812,7 @@ def handle_strava_sync(stravatoken,
|
|||||||
if not failed:
|
if not failed:
|
||||||
try:
|
try:
|
||||||
workout = Workout.objects.get(id=workoutid)
|
workout = Workout.objects.get(id=workoutid)
|
||||||
except Workout.DoesNotExist:
|
except Workout.DoesNotExist: # pragma: no cover
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
workout.uploadedtostrava = res.id
|
workout.uploadedtostrava = res.id
|
||||||
@@ -1477,7 +1477,6 @@ def handle_calctrimp(id,
|
|||||||
csvfile = csvfilename+'.gz'
|
csvfile = csvfilename+'.gz'
|
||||||
else: # pragma: no cover
|
else: # pragma: no cover
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
csvfile = os.path.abspath(csvfile)
|
csvfile = os.path.abspath(csvfile)
|
||||||
|
|
||||||
with grpc.insecure_channel(
|
with grpc.insecure_channel(
|
||||||
@@ -1562,7 +1561,7 @@ def handle_calctrimp(id,
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
workout = Workout.objects.get(id=id)
|
workout = Workout.objects.get(id=id)
|
||||||
except Workout.DoesNotExist:
|
except Workout.DoesNotExist: # pragma: no cover
|
||||||
dologging('metrics.log','Could not find workout {id}'.format(id=id))
|
dologging('metrics.log','Could not find workout {id}'.format(id=id))
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@@ -2037,7 +2036,7 @@ def handle_sendemail_expired(useremail, userfirstname, userlastname, expireddate
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
@app.task
|
@app.task
|
||||||
def handle_sendemail_newftp(rower,power,mode, **kwargs):
|
def handle_sendemail_newftp(rower,power,mode, **kwargs): # pragma: no cover
|
||||||
subject = "You may want to update your FTP on rowsandall.com"
|
subject = "You may want to update your FTP on rowsandall.com"
|
||||||
from_email = 'Rowsandall <info@rowsandall.com>'
|
from_email = 'Rowsandall <info@rowsandall.com>'
|
||||||
|
|
||||||
@@ -2069,7 +2068,7 @@ def handle_sendemail_breakthrough(workoutid, useremail,
|
|||||||
|
|
||||||
lastname = ''
|
lastname = ''
|
||||||
|
|
||||||
if surname:
|
if surname: # pragma: no cover
|
||||||
lastname = userlastname
|
lastname = userlastname
|
||||||
|
|
||||||
tablevalues = [
|
tablevalues = [
|
||||||
@@ -2122,7 +2121,7 @@ def handle_sendemail_hard(workoutid, useremail,
|
|||||||
]
|
]
|
||||||
|
|
||||||
lastname = ''
|
lastname = ''
|
||||||
if surname:
|
if surname: # pragma: no cover
|
||||||
lastname = userlastname
|
lastname = userlastname
|
||||||
|
|
||||||
# send email with attachment
|
# send email with attachment
|
||||||
@@ -3414,7 +3413,7 @@ def handle_nk_async_workout(alldata, userid, nktoken, nkid, delaysec, defaulttim
|
|||||||
jsonData = response.json()
|
jsonData = response.json()
|
||||||
try:
|
try:
|
||||||
strokeData = jsonData[str(nkid)]
|
strokeData = jsonData[str(nkid)]
|
||||||
except KeyError:
|
except KeyError: # pragma: no cover
|
||||||
dologging('nklog.log','Could not find strokeData')
|
dologging('nklog.log','Could not find strokeData')
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@@ -3424,7 +3423,7 @@ def handle_nk_async_workout(alldata, userid, nktoken, nkid, delaysec, defaulttim
|
|||||||
if oarlockSessions:
|
if oarlockSessions:
|
||||||
oarlocksession = oarlockSessions[0]
|
oarlocksession = oarlockSessions[0]
|
||||||
seatNumber = oarlocksession['seatNumber']
|
seatNumber = oarlocksession['seatNumber']
|
||||||
except KeyError:
|
except KeyError: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
|
|
||||||
df = strokeDataToDf(strokeData, seatIndex=seatNumber)
|
df = strokeDataToDf(strokeData, seatIndex=seatNumber)
|
||||||
@@ -3439,14 +3438,14 @@ def handle_nk_async_workout(alldata, userid, nktoken, nkid, delaysec, defaulttim
|
|||||||
workoutid, error = add_workout_from_data(userid, nkid, data, df)
|
workoutid, error = add_workout_from_data(userid, nkid, data, df)
|
||||||
|
|
||||||
# dologging('nklog.log','NK Workout ID {id}'.format(id=workoutid))
|
# dologging('nklog.log','NK Workout ID {id}'.format(id=workoutid))
|
||||||
if workoutid == 0:
|
if workoutid == 0: # pragma: no cover
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
workout = Workout.objects.get(id=workoutid)
|
workout = Workout.objects.get(id=workoutid)
|
||||||
newnkid = workout.uploadedtonk
|
newnkid = workout.uploadedtonk
|
||||||
sr = create_or_update_syncrecord(workout.user, workout, nkid=newnkid)
|
sr = create_or_update_syncrecord(workout.user, workout, nkid=newnkid)
|
||||||
except Workout.DoesNotExist:
|
except Workout.DoesNotExist: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return workoutid
|
return workoutid
|
||||||
@@ -3775,7 +3774,7 @@ def handle_c2_async_workout(alldata, userid, c2token, c2id, delaysec,
|
|||||||
return workoutid
|
return workoutid
|
||||||
|
|
||||||
@app.task
|
@app.task
|
||||||
def fetch_rojabo_session(id,alldata,userid,rowerid,debug=False, **kwargs):
|
def fetch_rojabo_session(id,alldata,userid,rowerid,debug=False, **kwargs): # pragma: no cover
|
||||||
try:
|
try:
|
||||||
item = alldata[id]
|
item = alldata[id]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -3880,7 +3879,7 @@ def fetch_strava_workout(stravatoken, oauth_data, stravaid, csvfilename, userid,
|
|||||||
wsize = round(5./dt)
|
wsize = round(5./dt)
|
||||||
|
|
||||||
velo2 = ewmovingaverage(velo, wsize)
|
velo2 = ewmovingaverage(velo, wsize)
|
||||||
except ValueError:
|
except ValueError: # pragma: no cover
|
||||||
velo2 = velo
|
velo2 = velo
|
||||||
|
|
||||||
if coords is not None:
|
if coords is not None:
|
||||||
|
|||||||
@@ -23,9 +23,13 @@
|
|||||||
<li class="grid_3">
|
<li class="grid_3">
|
||||||
{% if courses %}
|
{% if courses %}
|
||||||
<p>
|
<p>
|
||||||
|
<form enctype="multipart/form-data" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input name="courses" type="submit" value="Download selected courses">
|
||||||
<table width="100%" class="listtable shortpadded">
|
<table width="100%" class="listtable shortpadded">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
<th> Download</th>
|
||||||
<th> Country</th>
|
<th> Country</th>
|
||||||
<th> Name</th>
|
<th> Name</th>
|
||||||
<th> Distance</th>
|
<th> Distance</th>
|
||||||
@@ -34,6 +38,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for course in courses %}
|
{% for course in courses %}
|
||||||
<tr>
|
<tr>
|
||||||
|
<td> <input type="checkbox" value={{ course.id }} name="courseid"> </td>
|
||||||
<td> {{ course.country }} </td>
|
<td> {{ course.country }} </td>
|
||||||
<td>
|
<td>
|
||||||
<a href="/rowers/courses/{{ course.id }}/">{{ course.name }}</a>
|
<a href="/rowers/courses/{{ course.id }}/">{{ course.name }}</a>
|
||||||
@@ -47,6 +52,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</form>
|
||||||
</p>
|
</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<p> No courses found </p>
|
<p> No courses found </p>
|
||||||
@@ -106,6 +112,7 @@
|
|||||||
<p>CrewNerd has published a nice video tutorial of the process.
|
<p>CrewNerd has published a nice video tutorial of the process.
|
||||||
<a href="https://youtu.be/whhWFmMJbhM">Click here</a> to see the video. The part
|
<a href="https://youtu.be/whhWFmMJbhM">Click here</a> to see the video. The part
|
||||||
we're interested in starts at 2:05.
|
we're interested in starts at 2:05.
|
||||||
|
There is also a <a href="https://performancephones.com/custom-courses/">written tutorial</a> by CrewNerd.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ class WorkoutFactory(factory.DjangoModelFactory):
|
|||||||
startdatetime = FuzzyDateTime(start_dt=one_month_ago, force_year=datetime.date.today().year)
|
startdatetime = FuzzyDateTime(start_dt=one_month_ago, force_year=datetime.date.today().year)
|
||||||
starttime = get_random_file(name=faker.word())['starttime']
|
starttime = get_random_file(name=faker.word())['starttime']
|
||||||
workouttype='water'
|
workouttype='water'
|
||||||
date=timezone.now().date()
|
date=startdatetime.evaluate(2,None, False).date()
|
||||||
duration=get_random_file(name=faker.word())['duration']
|
duration=get_random_file(name=faker.word())['duration']
|
||||||
distance=get_random_file(name=faker.word())['totaldist']
|
distance=get_random_file(name=faker.word())['totaldist']
|
||||||
csvfilename=get_random_file(name=faker.word())['filename']
|
csvfilename=get_random_file(name=faker.word())['filename']
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ class ListWorkoutTest(TestCase):
|
|||||||
|
|
||||||
self.c = Client()
|
self.c = Client()
|
||||||
self.user_workouts = WorkoutFactory.create_batch(5, user=self.r)
|
self.user_workouts = WorkoutFactory.create_batch(5, user=self.r)
|
||||||
|
for w in self.user_workouts:
|
||||||
|
if w.date != w.startdatetime.date():
|
||||||
|
w.date = w.startdatetime.date()
|
||||||
|
w.save()
|
||||||
|
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
self.password = faker.word()
|
self.password = faker.word()
|
||||||
self.u.set_password(self.password)
|
self.u.set_password(self.password)
|
||||||
@@ -79,6 +84,10 @@ class PlannedSessionTests(TestCase):
|
|||||||
|
|
||||||
self.c = Client()
|
self.c = Client()
|
||||||
self.user_workouts = WorkoutFactory.create_batch(5, user=self.r)
|
self.user_workouts = WorkoutFactory.create_batch(5, user=self.r)
|
||||||
|
for w in self.user_workouts:
|
||||||
|
if w.date != w.startdatetime.date():
|
||||||
|
w.date = w.startdatetime.date()
|
||||||
|
w.save()
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
self.password = faker.word()
|
self.password = faker.word()
|
||||||
self.u.set_password(self.password)
|
self.u.set_password(self.password)
|
||||||
@@ -130,6 +139,22 @@ class ForcecurveTest(TestCase):
|
|||||||
|
|
||||||
self.c = Client()
|
self.c = Client()
|
||||||
self.user_workouts = WorkoutFactory.create_batch(5, user=self.r)
|
self.user_workouts = WorkoutFactory.create_batch(5, user=self.r)
|
||||||
|
for w in self.user_workouts:
|
||||||
|
if w.date != w.startdatetime.date():
|
||||||
|
w.date = w.startdatetime.date()
|
||||||
|
w.save()
|
||||||
|
|
||||||
|
result = get_random_file(filename='rowers/tests/testdata/quiske_in_stroke.csv')
|
||||||
|
|
||||||
|
self.winstroke = WorkoutFactory(user=self.r,
|
||||||
|
csvfilename=result['filename'],
|
||||||
|
starttime=result['starttime'],
|
||||||
|
startdatetime=result['startdatetime'],
|
||||||
|
duration=result['duration'],
|
||||||
|
distance=result['totaldist'],
|
||||||
|
workouttype = 'water',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
self.password = faker.word()
|
self.password = faker.word()
|
||||||
@@ -143,6 +168,7 @@ class ForcecurveTest(TestCase):
|
|||||||
except (IOError, FileNotFoundError,OSError):
|
except (IOError, FileNotFoundError,OSError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@patch('rowers.dataprep.getsmallrowdata_db',side_effect = mocked_getempowerdata_db)
|
@patch('rowers.dataprep.getsmallrowdata_db',side_effect = mocked_getempowerdata_db)
|
||||||
def test_forcecurve_plot(self, mocked_getsmallrowdata_db):
|
def test_forcecurve_plot(self, mocked_getsmallrowdata_db):
|
||||||
login = self.c.login(username=self.u.username, password = self.password)
|
login = self.c.login(username=self.u.username, password = self.password)
|
||||||
@@ -182,7 +208,62 @@ class ForcecurveTest(TestCase):
|
|||||||
response = self.c.get(url)
|
response = self.c.get(url)
|
||||||
self.assertEqual(response.status_code,200)
|
self.assertEqual(response.status_code,200)
|
||||||
|
|
||||||
|
url = '/rowers/analysis/forcecurveanalysis/1/delete/'
|
||||||
|
response = self.c.get(url)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
form_data = {}
|
||||||
|
response = self.c.post(url, form_data, follow=True)
|
||||||
|
|
||||||
|
expected_url = '/rowers/analysis/forcecurveanalysis/'
|
||||||
|
self.assertRedirects(response, expected_url=expected_url, status_code=302,target_status_code=200)
|
||||||
|
|
||||||
|
|
||||||
|
def test_instroke_analysis(self):
|
||||||
|
login = self.c.login(username=self.u.username, password = self.password)
|
||||||
|
self.assertTrue(login)
|
||||||
|
|
||||||
|
encoded1 = str(encoder.encode_hex(self.winstroke.id))
|
||||||
|
url = '/rowers/workout/'+encoded1+'/instroke/interactive/'
|
||||||
|
|
||||||
|
response = self.c.get(url)
|
||||||
|
self.assertEqual(response.status_code,200)
|
||||||
|
|
||||||
|
# saving
|
||||||
|
form_data = {
|
||||||
|
'spm_min': 19,
|
||||||
|
'spm_max': 25,
|
||||||
|
'activeminutesmin': 0,
|
||||||
|
'activeminutesmax': 10,
|
||||||
|
'individual_curves': False,
|
||||||
|
'notes': 'aap',
|
||||||
|
'name': 'sdsd',
|
||||||
|
'metric': 'boat accelerator curve',
|
||||||
|
'_save_as_new':'Save'
|
||||||
|
}
|
||||||
|
|
||||||
|
form = InstrokeForm(form_data,choices=['boat accelerator curve', 'oar angle velocity curve'])
|
||||||
|
result = form.is_valid()
|
||||||
|
if not result:
|
||||||
|
print(form.errors)
|
||||||
|
self.assertTrue(form.is_valid())
|
||||||
|
|
||||||
|
response = self.c.post(url, form_data)
|
||||||
|
self.assertEqual(response.status_code,200)
|
||||||
|
|
||||||
|
url = '/rowers/analysis/instrokeanalysis/'
|
||||||
|
response = self.c.get(url)
|
||||||
|
self.assertEqual(response.status_code,200)
|
||||||
|
|
||||||
|
url = '/rowers/analysis/instrokeanalysis/1/delete/'
|
||||||
|
response = self.c.get(url)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
form_data = {}
|
||||||
|
response = self.c.post(url, form_data, follow=True)
|
||||||
|
|
||||||
|
expected_url = '/rowers/analysis/instrokeanalysis/'
|
||||||
|
self.assertRedirects(response, expected_url=expected_url, status_code=302,target_status_code=200)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -198,6 +279,10 @@ class WorkoutCompareTestNew(TestCase):
|
|||||||
|
|
||||||
self.c = Client()
|
self.c = Client()
|
||||||
self.user_workouts = WorkoutFactory.create_batch(5, user=self.r)
|
self.user_workouts = WorkoutFactory.create_batch(5, user=self.r)
|
||||||
|
for w in self.user_workouts:
|
||||||
|
if w.date != w.startdatetime.date():
|
||||||
|
w.date = w.startdatetime.date()
|
||||||
|
w.save()
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
self.password = faker.word()
|
self.password = faker.word()
|
||||||
self.u.set_password(self.password)
|
self.u.set_password(self.password)
|
||||||
@@ -296,6 +381,10 @@ class WorkoutBoxPlotTestNew(TestCase):
|
|||||||
|
|
||||||
self.c = Client()
|
self.c = Client()
|
||||||
self.user_workouts = WorkoutFactory.create_batch(5, user=self.r)
|
self.user_workouts = WorkoutFactory.create_batch(5, user=self.r)
|
||||||
|
for w in self.user_workouts:
|
||||||
|
if w.date != w.startdatetime.date():
|
||||||
|
w.date = w.startdatetime.date()
|
||||||
|
w.save()
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
self.password = faker.word()
|
self.password = faker.word()
|
||||||
self.u.set_password(self.password)
|
self.u.set_password(self.password)
|
||||||
@@ -393,6 +482,10 @@ class WorkoutHistoTestNew(TestCase):
|
|||||||
|
|
||||||
self.c = Client()
|
self.c = Client()
|
||||||
self.user_workouts = WorkoutFactory.create_batch(5, user=self.r)
|
self.user_workouts = WorkoutFactory.create_batch(5, user=self.r)
|
||||||
|
for w in self.user_workouts:
|
||||||
|
if w.date != w.startdatetime.date():
|
||||||
|
w.date = w.startdatetime.date()
|
||||||
|
w.save()
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
self.password = faker.word()
|
self.password = faker.word()
|
||||||
self.u.set_password(self.password)
|
self.u.set_password(self.password)
|
||||||
@@ -490,6 +583,10 @@ class History(TestCase):
|
|||||||
|
|
||||||
self.c = Client()
|
self.c = Client()
|
||||||
self.user_workouts = WorkoutFactory.create_batch(20, user=self.r)
|
self.user_workouts = WorkoutFactory.create_batch(20, user=self.r)
|
||||||
|
for w in self.user_workouts:
|
||||||
|
if w.date != w.startdatetime.date():
|
||||||
|
w.date = w.startdatetime.date()
|
||||||
|
w.save()
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
self.password = faker.word()
|
self.password = faker.word()
|
||||||
self.u.set_password(self.password)
|
self.u.set_password(self.password)
|
||||||
@@ -577,6 +674,10 @@ class GoldMedalScores(TestCase):
|
|||||||
|
|
||||||
self.c = Client()
|
self.c = Client()
|
||||||
self.user_workouts = WorkoutFactory.create_batch(20, user=self.r)
|
self.user_workouts = WorkoutFactory.create_batch(20, user=self.r)
|
||||||
|
for w in self.user_workouts:
|
||||||
|
if w.date != w.startdatetime.date():
|
||||||
|
w.date = w.startdatetime.date()
|
||||||
|
w.save()
|
||||||
result = get_random_file(filename='rowers/tests/testdata/onwater2.csv')
|
result = get_random_file(filename='rowers/tests/testdata/onwater2.csv')
|
||||||
self.w1 = WorkoutFactory(user=self.r,
|
self.w1 = WorkoutFactory(user=self.r,
|
||||||
csvfilename=result['filename'],
|
csvfilename=result['filename'],
|
||||||
@@ -677,6 +778,10 @@ class WorkoutFlexallTestNew(TestCase):
|
|||||||
|
|
||||||
self.c = Client()
|
self.c = Client()
|
||||||
self.user_workouts = WorkoutFactory.create_batch(5, user=self.r)
|
self.user_workouts = WorkoutFactory.create_batch(5, user=self.r)
|
||||||
|
for w in self.user_workouts:
|
||||||
|
if w.date != w.startdatetime.date():
|
||||||
|
w.date = w.startdatetime.date()
|
||||||
|
w.save()
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
self.password = faker.word()
|
self.password = faker.word()
|
||||||
self.u.set_password(self.password)
|
self.u.set_password(self.password)
|
||||||
@@ -774,6 +879,10 @@ class WorkoutStatsTestNew(TestCase):
|
|||||||
|
|
||||||
self.c = Client()
|
self.c = Client()
|
||||||
self.user_workouts = WorkoutFactory.create_batch(5, user=self.r)
|
self.user_workouts = WorkoutFactory.create_batch(5, user=self.r)
|
||||||
|
for w in self.user_workouts:
|
||||||
|
if w.date != w.startdatetime.date():
|
||||||
|
w.date = w.startdatetime.date()
|
||||||
|
w.save()
|
||||||
|
|
||||||
today = datetime.date.today()
|
today = datetime.date.today()
|
||||||
i = 0
|
i = 0
|
||||||
@@ -1082,6 +1191,10 @@ class MarkerPerformanceTest(TestCase):
|
|||||||
|
|
||||||
self.c = Client()
|
self.c = Client()
|
||||||
self.user_workouts = WorkoutFactory.create_batch(5, user=self.r)
|
self.user_workouts = WorkoutFactory.create_batch(5, user=self.r)
|
||||||
|
for w in self.user_workouts:
|
||||||
|
if w.date != w.startdatetime.date():
|
||||||
|
w.date = w.startdatetime.date()
|
||||||
|
w.save()
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
self.password = faker.word()
|
self.password = faker.word()
|
||||||
self.u.set_password(self.password)
|
self.u.set_password(self.password)
|
||||||
@@ -1095,6 +1208,8 @@ class MarkerPerformanceTest(TestCase):
|
|||||||
workouttype='rower',
|
workouttype='rower',
|
||||||
rankingpiece=True,
|
rankingpiece=True,
|
||||||
)
|
)
|
||||||
|
self.werg2k.date = self.werg2k.startdatetime.date()
|
||||||
|
self.werg2k.save()
|
||||||
|
|
||||||
# need a 2k, 5k, 1h row
|
# need a 2k, 5k, 1h row
|
||||||
self.werg5k = WorkoutFactory(
|
self.werg5k = WorkoutFactory(
|
||||||
@@ -1104,6 +1219,8 @@ class MarkerPerformanceTest(TestCase):
|
|||||||
workouttype='rower',
|
workouttype='rower',
|
||||||
rankingpiece=True,
|
rankingpiece=True,
|
||||||
)
|
)
|
||||||
|
self.werg5k.date = self.werg5k.startdatetime.date()
|
||||||
|
self.werg5k.save()
|
||||||
|
|
||||||
# need a 2k, 5k, 1h row
|
# need a 2k, 5k, 1h row
|
||||||
self.werg1h = WorkoutFactory(
|
self.werg1h = WorkoutFactory(
|
||||||
@@ -1113,6 +1230,8 @@ class MarkerPerformanceTest(TestCase):
|
|||||||
workouttype='rower',
|
workouttype='rower',
|
||||||
rankingpiece=True,
|
rankingpiece=True,
|
||||||
)
|
)
|
||||||
|
self.werg1h.date = self.werg1h.startdatetime.date()
|
||||||
|
self.werg1h.save()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
for workout in self.user_workouts:
|
for workout in self.user_workouts:
|
||||||
@@ -1280,6 +1399,10 @@ class AlertTest(TestCase):
|
|||||||
|
|
||||||
self.c = Client()
|
self.c = Client()
|
||||||
self.user_workouts = WorkoutFactory.create_batch(5, user=self.r)
|
self.user_workouts = WorkoutFactory.create_batch(5, user=self.r)
|
||||||
|
for w in self.user_workouts:
|
||||||
|
if w.date != w.startdatetime.date():
|
||||||
|
w.date = w.startdatetime.date()
|
||||||
|
w.save()
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
self.password = faker.word()
|
self.password = faker.word()
|
||||||
self.u.set_password(self.password)
|
self.u.set_password(self.password)
|
||||||
|
|||||||
@@ -71,8 +71,6 @@ class ListWorkoutTest(TestCase):
|
|||||||
login = self.c.login(username=self.u.username, password=self.password)
|
login = self.c.login(username=self.u.username, password=self.password)
|
||||||
self.assertTrue(login)
|
self.assertTrue(login)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
url = reverse('workouts_view')
|
url = reverse('workouts_view')
|
||||||
|
|
||||||
response = self.c.get(url)
|
response = self.c.get(url)
|
||||||
@@ -88,6 +86,60 @@ class ListWorkoutTest(TestCase):
|
|||||||
response = self.c.get(url3)
|
response = self.c.get(url3)
|
||||||
self.assertEqual(response.status_code,200)
|
self.assertEqual(response.status_code,200)
|
||||||
|
|
||||||
|
@patch('rowers.dataprep.create_engine')
|
||||||
|
@patch('rowers.dataprep.getsmallrowdata_db')
|
||||||
|
@patch('rowers.dataprep.myqueue')
|
||||||
|
def test_bulk_workouts(self, mocked_sqlalchemy,
|
||||||
|
mocked_getsmallrowdata_db,
|
||||||
|
mocked_myqueue):
|
||||||
|
|
||||||
|
login = self.c.login(username=self.u.username, password=self.password)
|
||||||
|
self.assertTrue(login)
|
||||||
|
|
||||||
|
|
||||||
|
url = '/rowers/list-workouts/?selectworkouts=true'
|
||||||
|
response = self.c.get(url)
|
||||||
|
self.assertEqual(response.status_code,200)
|
||||||
|
|
||||||
|
|
||||||
|
form_data = {
|
||||||
|
'action': ['remove'],
|
||||||
|
'selectworkouts': ['Submit'],
|
||||||
|
'workoutid': [encoder.encode_hex(1), encoder.encode_hex(2)],
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.c.post(url, form_data, follow=True)
|
||||||
|
expected_url = reverse('workouts_bulk_actions')
|
||||||
|
self.assertRedirects(response,
|
||||||
|
expected_url=expected_url,
|
||||||
|
status_code=302,
|
||||||
|
target_status_code=200)
|
||||||
|
|
||||||
|
form_data = {
|
||||||
|
'workouts': ['1','2'],
|
||||||
|
'action': ['export'],
|
||||||
|
'destination': 'strava',
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.c.post(expected_url, form_data, follow=True)
|
||||||
|
url = expected_url
|
||||||
|
expected_url = reverse('workouts_view')
|
||||||
|
self.assertRedirects(response,
|
||||||
|
expected_url=expected_url,
|
||||||
|
status_code=302,
|
||||||
|
target_status_code=200)
|
||||||
|
form_data = {
|
||||||
|
'workouts': ['1','2'],
|
||||||
|
'action': ['remove'],
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.c.post(url, form_data, follow=True)
|
||||||
|
expected_url = reverse('workouts_view')
|
||||||
|
self.assertRedirects(response,
|
||||||
|
expected_url=expected_url,
|
||||||
|
status_code=302,
|
||||||
|
target_status_code=200)
|
||||||
|
|
||||||
@override_settings(TESTING=True)
|
@override_settings(TESTING=True)
|
||||||
class WorkoutViewTest(TestCase):
|
class WorkoutViewTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@@ -118,11 +170,6 @@ class WorkoutViewTest(TestCase):
|
|||||||
workouttype = 'water',
|
workouttype = 'water',
|
||||||
)
|
)
|
||||||
|
|
||||||
# self.comments = CommentFactory.create_batch(5,
|
|
||||||
# user = self.u,
|
|
||||||
# created = timezone.now,
|
|
||||||
# workout = self.wwater)
|
|
||||||
|
|
||||||
result = get_random_file(filename='rowers/tests/testdata/quiske_in_stroke.csv')
|
result = get_random_file(filename='rowers/tests/testdata/quiske_in_stroke.csv')
|
||||||
|
|
||||||
self.winstroke = WorkoutFactory(user=self.r,
|
self.winstroke = WorkoutFactory(user=self.r,
|
||||||
@@ -184,6 +231,8 @@ class WorkoutViewTest(TestCase):
|
|||||||
for v in vs:
|
for v in vs:
|
||||||
v.delete()
|
v.delete()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@patch('rowers.dataprep.create_engine')
|
@patch('rowers.dataprep.create_engine')
|
||||||
@patch('rowers.dataprep.getsmallrowdata_db')
|
@patch('rowers.dataprep.getsmallrowdata_db')
|
||||||
@patch('rowers.middleware.myqueue')
|
@patch('rowers.middleware.myqueue')
|
||||||
@@ -205,6 +254,34 @@ class WorkoutViewTest(TestCase):
|
|||||||
response = self.c.post(url,form_data)
|
response = self.c.post(url,form_data)
|
||||||
self.assertEqual(response.status_code,200)
|
self.assertEqual(response.status_code,200)
|
||||||
|
|
||||||
|
@patch('rowers.dataprep.create_engine')
|
||||||
|
@patch('rowers.dataprep.getsmallrowdata_db')
|
||||||
|
@patch('rowers.middleware.myqueue')
|
||||||
|
def test_resample(self, mocked_sqlalchemy, mocked_getsmallrowdata_db,
|
||||||
|
mocked_myqueue):
|
||||||
|
login = self.c.login(username=self.u.username, password=self.password)
|
||||||
|
self.assertTrue(login)
|
||||||
|
|
||||||
|
url = reverse('workout_resample_view',kwargs={'id':encoder.encode_hex(self.wwater.id)})
|
||||||
|
|
||||||
|
response = self.c.get(url)
|
||||||
|
self.assertEqual(response.status_code,200)
|
||||||
|
|
||||||
|
form_data = {
|
||||||
|
'resamplechoice': 'copy',
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.c.post(url,form_data)
|
||||||
|
self.assertEqual(response.status_code,200)
|
||||||
|
|
||||||
|
form_data = {
|
||||||
|
'resamplechoice': 'overwrite',
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.c.post(url,form_data)
|
||||||
|
self.assertEqual(response.status_code,200)
|
||||||
|
|
||||||
|
|
||||||
@patch('rowers.dataprep.create_engine')
|
@patch('rowers.dataprep.create_engine')
|
||||||
@patch('rowers.dataprep.getsmallrowdata_db')
|
@patch('rowers.dataprep.getsmallrowdata_db')
|
||||||
@pytest.mark.django_db(transaction=True)
|
@pytest.mark.django_db(transaction=True)
|
||||||
@@ -555,11 +632,6 @@ class WorkoutViewTest(TestCase):
|
|||||||
'file':screenshot_file,
|
'file':screenshot_file,
|
||||||
}
|
}
|
||||||
|
|
||||||
#form = ImageForm(form_data, file_data)
|
|
||||||
#if not form.is_valid():
|
|
||||||
# print form.errors
|
|
||||||
#self.assertTrue(form.is_valid())
|
|
||||||
|
|
||||||
response = self.c.post(url,form_data,format='multipart',follow=True)
|
response = self.c.post(url,form_data,format='multipart',follow=True)
|
||||||
|
|
||||||
expected_url = reverse(self.r.defaultlandingpage,
|
expected_url = reverse(self.r.defaultlandingpage,
|
||||||
|
|||||||
@@ -61,3 +61,15 @@ class SimpleViewTest(TestCase):
|
|||||||
self.assertRedirects(response,
|
self.assertRedirects(response,
|
||||||
expected_url='/rowers/list-workouts/',
|
expected_url='/rowers/list-workouts/',
|
||||||
status_code=302,target_status_code=200)
|
status_code=302,target_status_code=200)
|
||||||
|
|
||||||
|
def test_start_coachtrial(self):
|
||||||
|
login = self.c.login(username=self.u.username, password=self.password)
|
||||||
|
self.assertTrue(login)
|
||||||
|
|
||||||
|
url = '/rowers/startcoachtrial/'
|
||||||
|
|
||||||
|
response = self.c.get(url,follow=True)
|
||||||
|
|
||||||
|
self.assertRedirects(response,
|
||||||
|
expected_url='/rowers/list-workouts/',
|
||||||
|
status_code=302,target_status_code=200)
|
||||||
|
|||||||
@@ -28,15 +28,6 @@ class ViewTest(TestCase):
|
|||||||
|
|
||||||
self.nu = datetime.datetime.now()
|
self.nu = datetime.datetime.now()
|
||||||
|
|
||||||
def test_upload_view_notloggedin(self):
|
|
||||||
response = self.c.post('/rowers/workout/upload/',follow=True)
|
|
||||||
|
|
||||||
|
|
||||||
self.assertRedirects(response, expected_url='/login/?next=/rowers/workout/upload/',
|
|
||||||
status_code=302,target_status_code=200)
|
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
@patch('rowers.dataprep.create_engine')
|
@patch('rowers.dataprep.create_engine')
|
||||||
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db)
|
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db)
|
||||||
def test_upload_view_sled(self, mocked_sqlalchemy,mocked_getsmallrowdata_db):
|
def test_upload_view_sled(self, mocked_sqlalchemy,mocked_getsmallrowdata_db):
|
||||||
@@ -129,7 +120,7 @@ class ViewTest(TestCase):
|
|||||||
f_to_be_deleted = w.csvfilename
|
f_to_be_deleted = w.csvfilename
|
||||||
try:
|
try:
|
||||||
os.remove(f_to_be_deleted+'.gz')
|
os.remove(f_to_be_deleted+'.gz')
|
||||||
except (FileNotFoundError,OSError):
|
except (FileNotFoundError,OSError): # pragma: no cover
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@patch('rowers.dataprep.create_engine')
|
@patch('rowers.dataprep.create_engine')
|
||||||
@@ -230,7 +221,7 @@ class ViewTest(TestCase):
|
|||||||
f_to_be_deleted = w.csvfilename
|
f_to_be_deleted = w.csvfilename
|
||||||
try:
|
try:
|
||||||
os.remove(f_to_be_deleted+'.gz')
|
os.remove(f_to_be_deleted+'.gz')
|
||||||
except (FileNotFoundError,OSError):
|
except (FileNotFoundError,OSError): # pragma: no cover
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -271,7 +262,7 @@ class ViewTest(TestCase):
|
|||||||
f_to_be_deleted = w.csvfilename
|
f_to_be_deleted = w.csvfilename
|
||||||
try:
|
try:
|
||||||
os.remove(f_to_be_deleted+'.gz')
|
os.remove(f_to_be_deleted+'.gz')
|
||||||
except (FileNotFoundError,OSError):
|
except (FileNotFoundError,OSError): # pragma: no cover
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@patch('rowers.dataprep.create_engine')
|
@patch('rowers.dataprep.create_engine')
|
||||||
@@ -341,19 +332,6 @@ class ViewTest(TestCase):
|
|||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
filename = 'rowers/tests/testdata/crewnerddata.CSV'
|
|
||||||
f = open(filename,'rb')
|
|
||||||
file_data = {'file': f}
|
|
||||||
form_data = {}
|
|
||||||
form = CNsummaryForm(form_data,file_data)
|
|
||||||
|
|
||||||
response = self.c.post('rowers/workout/'+encoded1+'/crewnerdsummary',
|
|
||||||
form_data,
|
|
||||||
follow=True)
|
|
||||||
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
w = Workout.objects.get(id=1)
|
w = Workout.objects.get(id=1)
|
||||||
f_to_be_deleted = w.csvfilename
|
f_to_be_deleted = w.csvfilename
|
||||||
try:
|
try:
|
||||||
@@ -660,7 +638,7 @@ class ViewTest(TestCase):
|
|||||||
|
|
||||||
@patch('rowers.dataprep.create_engine')
|
@patch('rowers.dataprep.create_engine')
|
||||||
@patch('rowers.dataprep.TCXParser')
|
@patch('rowers.dataprep.TCXParser')
|
||||||
def test_upload_view_TCX_CN(self, mocked_sqlalchemy,
|
def test_upload_view_TCX_Rim(self, mocked_sqlalchemy,
|
||||||
mocked_tcx_parser):
|
mocked_tcx_parser):
|
||||||
self.c.login(username='john',password='koeinsloot')
|
self.c.login(username='john',password='koeinsloot')
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from __future__ import unicode_literals
|
|||||||
from .statements import *
|
from .statements import *
|
||||||
nu = datetime.datetime.now()
|
nu = datetime.datetime.now()
|
||||||
|
|
||||||
|
import rowers.courses as courses
|
||||||
|
|
||||||
|
|
||||||
tested = [
|
tested = [
|
||||||
@@ -27,6 +28,14 @@ class URLTests(TestCase):
|
|||||||
r = Rower.objects.create(user=u,rowerplan='coach',gdproptin=True, ftpset=True,
|
r = Rower.objects.create(user=u,rowerplan='coach',gdproptin=True, ftpset=True,
|
||||||
gdproptindate=timezone.now())
|
gdproptindate=timezone.now())
|
||||||
self.c = Client()
|
self.c = Client()
|
||||||
|
cs = courses.kmltocourse('rowers/tests/testdata/thyro.kml')
|
||||||
|
course = cs[0]
|
||||||
|
cname = course['name']
|
||||||
|
cnotes = course['description']
|
||||||
|
polygons = course['polygons']
|
||||||
|
self.ThyroBaantje = courses.createcourse(r,cname,polygons,notes=cnotes)
|
||||||
|
self.ThyroBaantje.save()
|
||||||
|
|
||||||
|
|
||||||
self.nu = datetime.datetime.now()
|
self.nu = datetime.datetime.now()
|
||||||
filename = 'rowers/tests/testdata/testdata.csv'
|
filename = 'rowers/tests/testdata/testdata.csv'
|
||||||
@@ -168,6 +177,19 @@ class URLTests(TestCase):
|
|||||||
'/rowers/workout/upload/team/',
|
'/rowers/workout/upload/team/',
|
||||||
'/rowers/workouts-join/',
|
'/rowers/workouts-join/',
|
||||||
'/rowers/workouts-join-select/',
|
'/rowers/workouts-join-select/',
|
||||||
|
'/rowers/api/courses/',
|
||||||
|
'/rowers/api/courses/?name=Brno',
|
||||||
|
'/rowers/api/courses/?course_distance=2000',
|
||||||
|
'/rowers/api/courses/?course_distance=aap',
|
||||||
|
'/rowers/api/courses/?distance_from=52&latitude=42&longitude=7',
|
||||||
|
'/rowers/api/courses/?distance_from=50&latitude=42&longitude=-aap',
|
||||||
|
'/rowers/api/courses/',
|
||||||
|
'/rowers/api/courses/1/',
|
||||||
|
'/rowers/list-workouts/?selectworkouts=true',
|
||||||
|
'/rowers/api/courses/kml/',
|
||||||
|
'/rowers/api/courses/kml/?id=1',
|
||||||
|
'/rowers/api/courses/kml?id=1&id=4/',
|
||||||
|
'/rowers/api/courses/kml/?id=aap',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
BIN
Binary file not shown.
@@ -251,6 +251,9 @@ urlpatterns = [
|
|||||||
name='strokedatajson_v3'),
|
name='strokedatajson_v3'),
|
||||||
re_path(r'^api/TCX/workouts/$', views.strokedata_tcx,
|
re_path(r'^api/TCX/workouts/$', views.strokedata_tcx,
|
||||||
name='strokedata_tcx'),
|
name='strokedata_tcx'),
|
||||||
|
re_path(r'^api/courses/$', views.course_list, name='course_list'),
|
||||||
|
re_path(r'^api/courses/(?P<id>\d+)/$', views.get_crewnerd_kml, name='get_crewnerd_kml'),
|
||||||
|
re_path(r'^api/courses/kml/$', views.get_crewnerd_multiple, name='get_crewnerd_multiple'),
|
||||||
re_path(r'^500v/$', views.error500_view, name='error500_view'),
|
re_path(r'^500v/$', views.error500_view, name='error500_view'),
|
||||||
re_path(r'^500q/$', views.servererror_view, name='servererror_view'),
|
re_path(r'^500q/$', views.servererror_view, name='servererror_view'),
|
||||||
path('502/', TemplateView.as_view(template_name='502.html'), name='502'),
|
path('502/', TemplateView.as_view(template_name='502.html'), name='502'),
|
||||||
|
|||||||
+28
-3
@@ -312,6 +312,31 @@ def geo_distance(lat1, lon1, lat2, lon2):
|
|||||||
|
|
||||||
return [distance, bearing]
|
return [distance, bearing]
|
||||||
|
|
||||||
|
def move_one_meter(latitude, longitude, bearing):
|
||||||
|
# Earth radius in meters
|
||||||
|
earth_radius = 6371000
|
||||||
|
|
||||||
|
# Convert latitude and longitude from degrees to radians
|
||||||
|
lat_rad = math.radians(latitude)
|
||||||
|
lon_rad = math.radians(longitude)
|
||||||
|
|
||||||
|
# Convert bearing from degrees to radians
|
||||||
|
bearing_rad = math.radians(bearing)
|
||||||
|
|
||||||
|
# Calculate the new latitude
|
||||||
|
new_lat = math.asin(math.sin(lat_rad) * math.cos(1/earth_radius) +
|
||||||
|
math.cos(lat_rad) * math.sin(1/earth_radius) * math.cos(bearing_rad))
|
||||||
|
|
||||||
|
# Calculate the new longitude
|
||||||
|
new_lon = lon_rad + math.atan2(math.sin(bearing_rad) * math.sin(1/earth_radius) * math.cos(lat_rad),
|
||||||
|
math.cos(1/earth_radius) - math.sin(lat_rad) * math.sin(new_lat))
|
||||||
|
|
||||||
|
# Convert new latitude and longitude from radians to degrees
|
||||||
|
new_lat = math.degrees(new_lat)
|
||||||
|
new_lon = math.degrees(new_lon)
|
||||||
|
|
||||||
|
return new_lat, new_lon
|
||||||
|
|
||||||
|
|
||||||
def isbreakthrough(delta, cpvalues, p0, p1, p2, p3, ratio):
|
def isbreakthrough(delta, cpvalues, p0, p1, p2, p3, ratio):
|
||||||
pwr = abs(p0)/(1+(delta/abs(p2)))
|
pwr = abs(p0)/(1+(delta/abs(p2)))
|
||||||
@@ -322,9 +347,9 @@ def isbreakthrough(delta, cpvalues, p0, p1, p2, p3, ratio):
|
|||||||
|
|
||||||
pwr *= ratio
|
pwr *= ratio
|
||||||
|
|
||||||
delta = delta.values.astype(int)
|
delta = delta.astype(int, errors='ignore').values
|
||||||
cpvalues = cpvalues.values.astype(int)
|
cpvalues = cpvalues.astype(int, errors='ignore').values
|
||||||
pwr = pwr.astype(int)
|
pwr = pwr.astype(int, errors='ignore').values
|
||||||
|
|
||||||
res = np.sum(cpvalues > pwr+1)
|
res = np.sum(cpvalues > pwr+1)
|
||||||
res2 = np.sum(cpvalues > pwr2+1)
|
res2 = np.sum(cpvalues > pwr2+1)
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ def analysis_new(request,
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
thesession = PlannedSession.objects.get(id=session)
|
thesession = PlannedSession.objects.get(id=session)
|
||||||
if not can_view_session(user, thesession):
|
if not can_view_session(user, thesession): # pragma: no cover
|
||||||
raise PermissionDenied("you cannot view this session")
|
raise PermissionDenied("you cannot view this session")
|
||||||
except PlannedSession.DoesNotExist:
|
except PlannedSession.DoesNotExist:
|
||||||
thesession = None
|
thesession = None
|
||||||
@@ -102,7 +102,7 @@ def analysis_new(request,
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
waterboattype = options['waterboattype']
|
waterboattype = options['waterboattype']
|
||||||
except KeyError:
|
except KeyError: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
@@ -188,7 +188,7 @@ def analysis_new(request,
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
rankingtypes = [False,True]
|
rankingtypes = [False,True]
|
||||||
if ranking:
|
if ranking: # pragma: no cover
|
||||||
rankingtypes = [True]
|
rankingtypes = [True]
|
||||||
|
|
||||||
|
|
||||||
@@ -272,7 +272,7 @@ def analysis_new(request,
|
|||||||
request.session['options'] = options
|
request.session['options'] = options
|
||||||
|
|
||||||
savedata = options.get('savedata',False)
|
savedata = options.get('savedata',False)
|
||||||
if savedata:
|
if savedata: # pragma: no cover
|
||||||
ids = options.get('ids',[])
|
ids = options.get('ids',[])
|
||||||
tw = []
|
tw = []
|
||||||
for id in ids:
|
for id in ids:
|
||||||
@@ -526,7 +526,7 @@ def trendflexdata(workouts, options, userid=0):
|
|||||||
extratitle = ' '+u.first_name+' '+u.last_name
|
extratitle = ' '+u.first_name+' '+u.last_name
|
||||||
|
|
||||||
savedata = options.get('savedata',False)
|
savedata = options.get('savedata',False)
|
||||||
if savedata:
|
if savedata: # pragma: no cover
|
||||||
return df
|
return df
|
||||||
|
|
||||||
script, div = interactive_multiflex(df, xparam, yparam,
|
script, div = interactive_multiflex(df, xparam, yparam,
|
||||||
@@ -561,7 +561,7 @@ def flexalldata(workouts, options):
|
|||||||
extratitle = ' '+u.first_name+' '+u.last_name
|
extratitle = ' '+u.first_name+' '+u.last_name
|
||||||
|
|
||||||
savedata = options.get('savedata',False)
|
savedata = options.get('savedata',False)
|
||||||
if savedata:
|
if savedata: # pragma: no cover
|
||||||
workstrokesonly = not includereststrokes
|
workstrokesonly = not includereststrokes
|
||||||
columns = [xparam, yparam1, yparam2, 'spm', 'driveenergy', 'distance']
|
columns = [xparam, yparam1, yparam2, 'spm', 'driveenergy', 'distance']
|
||||||
ids = [int(w.id) for w in workouts]
|
ids = [int(w.id) for w in workouts]
|
||||||
@@ -603,7 +603,7 @@ def histodata(workouts, options):
|
|||||||
extratitle = ' '+u.first_name+' '+u.last_name
|
extratitle = ' '+u.first_name+' '+u.last_name
|
||||||
|
|
||||||
savedata = options.get('savedata',False)
|
savedata = options.get('savedata',False)
|
||||||
if savedata:
|
if savedata: # pragma: no cover
|
||||||
workstrokesonly = not includereststrokes
|
workstrokesonly = not includereststrokes
|
||||||
ids = [int(w.id) for w in workouts]
|
ids = [int(w.id) for w in workouts]
|
||||||
df = dataprep.getsmallrowdata_db([plotfield], ids=ids,
|
df = dataprep.getsmallrowdata_db([plotfield], ids=ids,
|
||||||
@@ -640,7 +640,7 @@ def cpdata(workouts, options):
|
|||||||
})
|
})
|
||||||
|
|
||||||
savedata = options.get('savedata',False)
|
savedata = options.get('savedata',False)
|
||||||
if savedata:
|
if savedata: # pragma: no cover
|
||||||
return powerdf
|
return powerdf
|
||||||
|
|
||||||
if powerdf.empty: # pragma: no cover
|
if powerdf.empty: # pragma: no cover
|
||||||
@@ -869,7 +869,7 @@ def comparisondata(workouts, options):
|
|||||||
}
|
}
|
||||||
|
|
||||||
savedata = options.get('savedata',False)
|
savedata = options.get('savedata',False)
|
||||||
if savedata:
|
if savedata: # pragma: no cover
|
||||||
workstrokesonly = not includereststrokes
|
workstrokesonly = not includereststrokes
|
||||||
columns = [xparam, yparam1,
|
columns = [xparam, yparam1,
|
||||||
'ftime', 'distance', 'fpace',
|
'ftime', 'distance', 'fpace',
|
||||||
@@ -950,7 +950,7 @@ def boxplotdata(workouts, options):
|
|||||||
extratitle = ' '+u.first_name+' '+u.last_name
|
extratitle = ' '+u.first_name+' '+u.last_name
|
||||||
|
|
||||||
savedata = options.get('savedata',False)
|
savedata = options.get('savedata',False)
|
||||||
if savedata:
|
if savedata: # pragma: no cover
|
||||||
return datadf
|
return datadf
|
||||||
|
|
||||||
script, div = interactive_boxchart(datadf, plotfield,
|
script, div = interactive_boxchart(datadf, plotfield,
|
||||||
@@ -1425,7 +1425,7 @@ def rankings_view2(request, userid=0,
|
|||||||
wcdurations = options['wcdurations']
|
wcdurations = options['wcdurations']
|
||||||
wcpower = options['wcpower']
|
wcpower = options['wcpower']
|
||||||
lastupdated = options['lastupdated']
|
lastupdated = options['lastupdated']
|
||||||
except KeyError:
|
except KeyError: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
userid = options['userid']
|
userid = options['userid']
|
||||||
@@ -2017,7 +2017,7 @@ class ForceCurveAnalysisDelete(DeleteView):
|
|||||||
def get_object(self, *args, **kwargs):
|
def get_object(self, *args, **kwargs):
|
||||||
obj = super(ForceCurveAnalysisDelete, self).get_object(*args, **kwargs)
|
obj = super(ForceCurveAnalysisDelete, self).get_object(*args, **kwargs)
|
||||||
|
|
||||||
if obj.rower != self.request.user.rower:
|
if obj.rower != self.request.user.rower: # pragma: no cover
|
||||||
raise PermissionDenied("You are not allowed to delete this Analysis")
|
raise PermissionDenied("You are not allowed to delete this Analysis")
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
@@ -2039,8 +2039,7 @@ class SavedAnalysisView(UserPassesTestMixin, View):
|
|||||||
analyses = None
|
analyses = None
|
||||||
chart = instroke_multi_interactive_chart
|
chart = instroke_multi_interactive_chart
|
||||||
|
|
||||||
|
def handle_no_permission(self): # pragma: no cover
|
||||||
def handle_no_permission(self):
|
|
||||||
if self.request.user.is_anonymous:
|
if self.request.user.is_anonymous:
|
||||||
url = reverse('login')+'?next='+self.request.path
|
url = reverse('login')+'?next='+self.request.path
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
@@ -2079,7 +2078,7 @@ class SavedAnalysisView(UserPassesTestMixin, View):
|
|||||||
super(SavedAnalysisView, self).setup(request, *args, **kwargs)
|
super(SavedAnalysisView, self).setup(request, *args, **kwargs)
|
||||||
|
|
||||||
self.r = getrequestrower(request, userid=self.userid)
|
self.r = getrequestrower(request, userid=self.userid)
|
||||||
if self.r is not None and not is_coach_user(self.request.user, self.r.user):
|
if self.r is not None and not is_coach_user(self.request.user, self.r.user): # pragma: no cover
|
||||||
raise PermissionDenied("You are not allowed to see analyses for this user")
|
raise PermissionDenied("You are not allowed to see analyses for this user")
|
||||||
|
|
||||||
self.analyses = self.analysis_class.objects.filter(rower=self.r).order_by("-date","-id")
|
self.analyses = self.analysis_class.objects.filter(rower=self.r).order_by("-date","-id")
|
||||||
@@ -2087,7 +2086,7 @@ class SavedAnalysisView(UserPassesTestMixin, View):
|
|||||||
startdate = request.GET.get('startdate')
|
startdate = request.GET.get('startdate')
|
||||||
enddate = request.GET.get('enddate')
|
enddate = request.GET.get('enddate')
|
||||||
self.searchform = SearchForm()
|
self.searchform = SearchForm()
|
||||||
if query:
|
if query: # pragma: no cover
|
||||||
query_list = query.split()
|
query_list = query.split()
|
||||||
if self.analysis_class == InStrokeAnalysis:
|
if self.analysis_class == InStrokeAnalysis:
|
||||||
self.analyses = self.analyses.filter(
|
self.analyses = self.analyses.filter(
|
||||||
@@ -2111,13 +2110,13 @@ class SavedAnalysisView(UserPassesTestMixin, View):
|
|||||||
)
|
)
|
||||||
self.searchform = SearchForm(initial={'q': query})
|
self.searchform = SearchForm(initial={'q': query})
|
||||||
date_initial = {}
|
date_initial = {}
|
||||||
if startdate:
|
if startdate: # pragma: no cover
|
||||||
try:
|
try:
|
||||||
self.analyses = self.analyses.filter(date__gte=startdate)
|
self.analyses = self.analyses.filter(date__gte=startdate)
|
||||||
date_initial['startdate'] = startdate
|
date_initial['startdate'] = startdate
|
||||||
except ValidationError:
|
except ValidationError:
|
||||||
messages.error(request,"{startdate} is not a valid start date".format(startdate=startdate))
|
messages.error(request,"{startdate} is not a valid start date".format(startdate=startdate))
|
||||||
if enddate:
|
if enddate: # pragma: no cover
|
||||||
self.analyses = self.analyses.filter(date__lte=enddate)
|
self.analyses = self.analyses.filter(date__lte=enddate)
|
||||||
date_initial['enddate'] = enddate
|
date_initial['enddate'] = enddate
|
||||||
self.dateform = DateRangeForm(initial=date_initial)
|
self.dateform = DateRangeForm(initial=date_initial)
|
||||||
@@ -2136,7 +2135,7 @@ class SavedAnalysisView(UserPassesTestMixin, View):
|
|||||||
'dateform': self.dateform,
|
'dateform': self.dateform,
|
||||||
})
|
})
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs): # pragma: no cover
|
||||||
form = self.form_class(request.POST)
|
form = self.form_class(request.POST)
|
||||||
|
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
@@ -2211,7 +2210,7 @@ class InStrokeAnalysisDelete(DeleteView):
|
|||||||
def get_object(self, *args, **kwargs):
|
def get_object(self, *args, **kwargs):
|
||||||
obj = super(InStrokeAnalysisDelete, self).get_object(*args, **kwargs)
|
obj = super(InStrokeAnalysisDelete, self).get_object(*args, **kwargs)
|
||||||
|
|
||||||
if obj.rower != self.request.user.rower:
|
if obj.rower != self.request.user.rower: # pragma: no cover
|
||||||
raise PermissionDenied("You are not allowed to delete this Analysis")
|
raise PermissionDenied("You are not allowed to delete this Analysis")
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
@@ -2356,7 +2355,7 @@ def alert_report_view(request, id=0, userid=0, nperiod=0):
|
|||||||
stats = alert_get_stats(alert, nperiod=nperiod)
|
stats = alert_get_stats(alert, nperiod=nperiod)
|
||||||
|
|
||||||
is_ajax = request_is_ajax(request)
|
is_ajax = request_is_ajax(request)
|
||||||
if is_ajax:
|
if is_ajax: # pragma: no cover
|
||||||
return JSONResponse({
|
return JSONResponse({
|
||||||
"stats": stats,
|
"stats": stats,
|
||||||
})
|
})
|
||||||
@@ -2680,9 +2679,10 @@ def history_view(request, userid=0):
|
|||||||
last28 = timezone.now() - datetime.timedelta(days=28)
|
last28 = timezone.now() - datetime.timedelta(days=28)
|
||||||
|
|
||||||
today = timezone.now()
|
today = timezone.now()
|
||||||
|
lastyear = today - datetime.timedelta(days=365)
|
||||||
|
|
||||||
lastyear = datetime.datetime(
|
#lastyear = datetime.datetime(
|
||||||
year=today.year - 1, month=today.month, day=today.day)
|
# year=today.year - 1, month=today.month, day=today.day)
|
||||||
|
|
||||||
firstmay = datetime.datetime(
|
firstmay = datetime.datetime(
|
||||||
year=today.year, month=5, day=1).astimezone(usertimezone)
|
year=today.year, month=5, day=1).astimezone(usertimezone)
|
||||||
@@ -2771,7 +2771,7 @@ def history_view_data(request, userid=0):
|
|||||||
df = getsmallrowdata_db(columns, ids=ids)
|
df = getsmallrowdata_db(columns, ids=ids)
|
||||||
try:
|
try:
|
||||||
df['deltat'] = df['time'].diff().clip(lower=0)
|
df['deltat'] = df['time'].diff().clip(lower=0)
|
||||||
except KeyError:
|
except KeyError: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
df = dataprep.clean_df_stats(df, workstrokesonly=True,
|
df = dataprep.clean_df_stats(df, workstrokesonly=True,
|
||||||
ignoreadvanced=True, ignorehr=False)
|
ignoreadvanced=True, ignorehr=False)
|
||||||
@@ -2824,7 +2824,7 @@ def history_view_data(request, userid=0):
|
|||||||
ddict['powermean'] = int(wavg(ddf, 'power', 'deltat'))
|
ddict['powermean'] = int(wavg(ddf, 'power', 'deltat'))
|
||||||
try:
|
try:
|
||||||
ddict['powermax'] = ddf['power'].max().astype(int)
|
ddict['powermax'] = ddf['power'].max().astype(int)
|
||||||
except KeyError:
|
except KeyError: # pragma: no cover
|
||||||
ddict['powermax'] = 0
|
ddict['powermax'] = 0
|
||||||
ddict['nrworkouts'] = a_workouts.count()
|
ddict['nrworkouts'] = a_workouts.count()
|
||||||
listofdicts.append(ddict)
|
listofdicts.append(ddict)
|
||||||
@@ -2839,7 +2839,7 @@ def history_view_data(request, userid=0):
|
|||||||
try:
|
try:
|
||||||
totalsdict['powermean'] = int(wavg(df, 'power', 'deltat'))
|
totalsdict['powermean'] = int(wavg(df, 'power', 'deltat'))
|
||||||
totalsdict['powermax'] = df['power'].max().astype(int)
|
totalsdict['powermax'] = df['power'].max().astype(int)
|
||||||
except KeyError:
|
except KeyError: # pragma: no cover
|
||||||
totalsdict['powermean'] = 0
|
totalsdict['powermean'] = 0
|
||||||
totalsdict['powermax'] = 0
|
totalsdict['powermax'] = 0
|
||||||
try:
|
try:
|
||||||
|
|||||||
+129
-16
@@ -1,6 +1,7 @@
|
|||||||
from rowers.views.statements import *
|
from rowers.views.statements import *
|
||||||
from rowers.tasks import handle_calctrimp
|
from rowers.tasks import handle_calctrimp
|
||||||
from rowers.opaque import encoder
|
from rowers.opaque import encoder
|
||||||
|
from rowers.courses import coursetokml, coursestokml
|
||||||
from xml.etree import ElementTree as ET
|
from xml.etree import ElementTree as ET
|
||||||
|
|
||||||
import arrow
|
import arrow
|
||||||
@@ -11,6 +12,8 @@ from rowers.dataroutines import get_workouttype_from_tcx, get_startdate_time_zon
|
|||||||
from rest_framework.decorators import parser_classes
|
from rest_framework.decorators import parser_classes
|
||||||
from rest_framework.parsers import BaseParser
|
from rest_framework.parsers import BaseParser
|
||||||
|
|
||||||
|
from rowers.utils import geo_distance
|
||||||
|
|
||||||
from datetime import datetime as dt
|
from datetime import datetime as dt
|
||||||
|
|
||||||
import rowingdata.tcxtools as tcxtools
|
import rowingdata.tcxtools as tcxtools
|
||||||
@@ -24,7 +27,7 @@ class XMLParser(BaseParser):
|
|||||||
dologging("apilog.log", "XML Parser")
|
dologging("apilog.log", "XML Parser")
|
||||||
try:
|
try:
|
||||||
s = ET.parse(stream).getroot()
|
s = ET.parse(stream).getroot()
|
||||||
except Exception as e:
|
except Exception as e: # pragma: no cover
|
||||||
dologging("apilog.log",e)
|
dologging("apilog.log",e)
|
||||||
return HttpResponse(status=500)
|
return HttpResponse(status=500)
|
||||||
return s
|
return s
|
||||||
@@ -228,13 +231,123 @@ def strokedataform_v2(request, id=0):
|
|||||||
def part_of_day(hour):
|
def part_of_day(hour):
|
||||||
if 5 <= hour < 12:
|
if 5 <= hour < 12:
|
||||||
return "Morning"
|
return "Morning"
|
||||||
elif 12 <= hour < 18:
|
elif 12 <= hour < 18: # pragma: no cover
|
||||||
return "Afternoon"
|
return "Afternoon"
|
||||||
elif 18 <= hour < 24:
|
elif 18 <= hour < 24: # pragma: no cover
|
||||||
return "Evening"
|
return "Evening"
|
||||||
else:
|
else: # pragma: no cover
|
||||||
return "Night"
|
return "Night"
|
||||||
|
|
||||||
|
# KML API views
|
||||||
|
"""
|
||||||
|
- Get a list of courses OK
|
||||||
|
- Nearby a certain coordinate
|
||||||
|
- Filtered by
|
||||||
|
- Get a (KML) course (in response.content rather than as attachment) OK
|
||||||
|
- Get multiple courses as one KML in response.content OK
|
||||||
|
- GET with parameters?
|
||||||
|
Optional, not for CN
|
||||||
|
- Create one or more new courses from KML
|
||||||
|
- Should check for duplicates (Placemark ID)
|
||||||
|
- Update one or more new courses from KML
|
||||||
|
"""
|
||||||
|
@api_view(["GET"])
|
||||||
|
@permission_classes([AllowAny])
|
||||||
|
def course_list(request):
|
||||||
|
if request.method != 'GET': # pragma: no cover
|
||||||
|
dologging('apilog.log','{m} request to KML endpoint'.format(m=request.method))
|
||||||
|
return HttpResponseNotAllowed("Method not supported")
|
||||||
|
|
||||||
|
query_data = {}
|
||||||
|
name = request.GET.get('name')
|
||||||
|
distance = request.GET.get('course_distance')
|
||||||
|
latitude = request.GET.get('latitude')
|
||||||
|
longitude = request.GET.get('longitude')
|
||||||
|
distance_from = request.GET.get('distance_from')
|
||||||
|
if name is not None:
|
||||||
|
query_data['name__contains'] = name
|
||||||
|
if distance is not None:
|
||||||
|
try:
|
||||||
|
query_data['distance__lte'] = int(distance)+50
|
||||||
|
query_data['distance__gte'] = int(distance)-50
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
courses = GeoCourse.objects.filter(**query_data)
|
||||||
|
if latitude is not None and longitude is not None and distance_from is not None:
|
||||||
|
try:
|
||||||
|
newlist = []
|
||||||
|
for c in courses:
|
||||||
|
distance = geo_distance(float(latitude), float(longitude), c.coord[0], c.coord[1])[0]
|
||||||
|
if distance < float(distance_from): # pragma: no cover
|
||||||
|
newlist.append(c)
|
||||||
|
courses = newlist
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
courselist = []
|
||||||
|
for c in courses:
|
||||||
|
d = {
|
||||||
|
'id': c.id,
|
||||||
|
'name':c.name,
|
||||||
|
'country':c.country,
|
||||||
|
'distance':c.distance,
|
||||||
|
'notes':c.notes,
|
||||||
|
'location':(c.coord[0], c.coord[1]),
|
||||||
|
}
|
||||||
|
courselist.append(d)
|
||||||
|
|
||||||
|
response_dict = {'courses': courselist}
|
||||||
|
|
||||||
|
return JsonResponse(response_dict, content_type='application/json; charset=utf8')
|
||||||
|
|
||||||
|
@api_view(["GET"])
|
||||||
|
@permission_classes([AllowAny])
|
||||||
|
def get_crewnerd_kml(request,id=0):
|
||||||
|
if request.method != 'GET': # pragma: no cover
|
||||||
|
dologging('apilog.log','{m} request to CrewNerd KML endpoint'.format(m=request.method))
|
||||||
|
return HttpResponseNotAllowed("Method not supported")
|
||||||
|
|
||||||
|
try:
|
||||||
|
c = GeoCourse.objects.get(id=id)
|
||||||
|
except GeoCourse.DoesNotExist: # pragma: no cover
|
||||||
|
raise Http404("This course does not exist")
|
||||||
|
|
||||||
|
kml = coursetokml(c, cn=True)
|
||||||
|
|
||||||
|
return HttpResponse(kml)
|
||||||
|
|
||||||
|
#@csrf_exempt
|
||||||
|
@api_view(["GET"])
|
||||||
|
@permission_classes([AllowAny])
|
||||||
|
def get_crewnerd_multiple(request):
|
||||||
|
if request.method != 'GET': # pragma: no cover
|
||||||
|
dologging('apilog.log','{m} request to CrewNerd KML endpoint'.format(m=request.method))
|
||||||
|
return HttpResponseNotAllowed("Method not supported")
|
||||||
|
|
||||||
|
ids = request.GET.get('id')
|
||||||
|
if ids is not None:
|
||||||
|
tdict = dict(request.GET.lists())
|
||||||
|
idsnew = []
|
||||||
|
for id in tdict['id']:
|
||||||
|
try:
|
||||||
|
idsnew.append(int(id))
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
ids = idsnew
|
||||||
|
else:
|
||||||
|
gcs = GeoCourse.objects.all()
|
||||||
|
ids = [c.id for c in gcs]
|
||||||
|
|
||||||
|
kml = coursestokml(ids, cn=True)
|
||||||
|
|
||||||
|
return HttpResponse(kml)
|
||||||
|
|
||||||
|
# Stroke data views
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@login_required()
|
@login_required()
|
||||||
@api_view(["POST"])
|
@api_view(["POST"])
|
||||||
@@ -244,11 +357,11 @@ def strokedata_tcx(request):
|
|||||||
"""
|
"""
|
||||||
Upload a TCX file through API
|
Upload a TCX file through API
|
||||||
"""
|
"""
|
||||||
if request.method != 'POST':
|
if request.method != 'POST': # pragma: no cover
|
||||||
dologging('apilog.log','GET request to TCX endpoint')
|
dologging('apilog.log','GET request to TCX endpoint')
|
||||||
return HttpResponseNotAllowed("Method not supported") # pragma: no cover
|
return HttpResponseNotAllowed("Method not supported")
|
||||||
|
|
||||||
if 'application/xml' not in request.content_type.lower():
|
if 'application/xml' not in request.content_type.lower(): # pragma: no cover
|
||||||
dologging('apilog.log','POST data not application/xml, request to TCX endpoint')
|
dologging('apilog.log','POST data not application/xml, request to TCX endpoint')
|
||||||
dologging('apilog.log', request.content_type.lower())
|
dologging('apilog.log', request.content_type.lower())
|
||||||
return HttpResponseNotAllowed("Need application/xml")
|
return HttpResponseNotAllowed("Need application/xml")
|
||||||
@@ -275,7 +388,7 @@ def strokedata_tcx(request):
|
|||||||
lap_duration_seconds = float(lap_duration_node.text)
|
lap_duration_seconds = float(lap_duration_node.text)
|
||||||
total_duration += lap_duration_seconds
|
total_duration += lap_duration_seconds
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e: # pragma: no cover
|
||||||
dologging('apilog.log','TCX')
|
dologging('apilog.log','TCX')
|
||||||
dologging('apilog.log',e)
|
dologging('apilog.log',e)
|
||||||
return HttpResponseNotAllowed("Could not parse TCX data")
|
return HttpResponseNotAllowed("Could not parse TCX data")
|
||||||
@@ -329,7 +442,7 @@ def strokedata_tcx(request):
|
|||||||
"workout id": workoutid,
|
"workout id": workoutid,
|
||||||
"status": "success",
|
"status": "success",
|
||||||
})
|
})
|
||||||
except Exception as e:
|
except Exception as e: # pragma: no cover
|
||||||
dologging('apilog.log','TCX API endpoint')
|
dologging('apilog.log','TCX API endpoint')
|
||||||
dologging('apilog.log',e)
|
dologging('apilog.log',e)
|
||||||
return HttpResponse(status=500)
|
return HttpResponse(status=500)
|
||||||
@@ -382,7 +495,7 @@ def strokedatajson_v3(request):
|
|||||||
title = request.data.get('name','')
|
title = request.data.get('name','')
|
||||||
try:
|
try:
|
||||||
elapsedTime = request.data['elapsedTime']
|
elapsedTime = request.data['elapsedTime']
|
||||||
except KeyError:
|
except KeyError: # pragma: no cover
|
||||||
try:
|
try:
|
||||||
duration = request.data['duration']
|
duration = request.data['duration']
|
||||||
try:
|
try:
|
||||||
@@ -394,7 +507,7 @@ def strokedatajson_v3(request):
|
|||||||
return HttpResponse("Missing Elapsed Time", status=400)
|
return HttpResponse("Missing Elapsed Time", status=400)
|
||||||
try:
|
try:
|
||||||
totalDistance = request.data['distance']
|
totalDistance = request.data['distance']
|
||||||
except KeyError:
|
except KeyError: # pragma: no cover
|
||||||
return HttpResponse("Missing Total Distance", status=400)
|
return HttpResponse("Missing Total Distance", status=400)
|
||||||
timeZone = request.data.get('timezone','UTC')
|
timeZone = request.data.get('timezone','UTC')
|
||||||
workouttype = request.data.get('workouttype','rower')
|
workouttype = request.data.get('workouttype','rower')
|
||||||
@@ -415,12 +528,12 @@ def strokedatajson_v3(request):
|
|||||||
df = pd.DataFrame()
|
df = pd.DataFrame()
|
||||||
try:
|
try:
|
||||||
strokes = request.data['strokes']
|
strokes = request.data['strokes']
|
||||||
except KeyError:
|
except KeyError: # pragma: no cover
|
||||||
return HttpResponse("No Stroke Data in JSON", status=400)
|
return HttpResponse("No Stroke Data in JSON", status=400)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
df = pd.DataFrame(strokes['data'])
|
df = pd.DataFrame(strokes['data'])
|
||||||
except KeyError:
|
except KeyError: # pragma: no cover
|
||||||
try:
|
try:
|
||||||
df = pd.DataFrame(request.data['strokedata'])
|
df = pd.DataFrame(request.data['strokedata'])
|
||||||
except:
|
except:
|
||||||
@@ -431,7 +544,7 @@ def strokedatajson_v3(request):
|
|||||||
|
|
||||||
status, comment, data = api_get_dataframe(startdatetime, df)
|
status, comment, data = api_get_dataframe(startdatetime, df)
|
||||||
|
|
||||||
if status != 200:
|
if status != 200: # pragma: no cover
|
||||||
return HttpResponse(comment, status=status)
|
return HttpResponse(comment, status=status)
|
||||||
|
|
||||||
|
|
||||||
@@ -541,7 +654,7 @@ def strokedatajson_v2(request, id):
|
|||||||
try:
|
try:
|
||||||
for d in request.data['data']:
|
for d in request.data['data']:
|
||||||
dologging('apilog.log',json.dumps(d))
|
dologging('apilog.log',json.dumps(d))
|
||||||
except KeyError:
|
except KeyError: # pragma: no cover
|
||||||
try:
|
try:
|
||||||
for d in request.data['strokedata']:
|
for d in request.data['strokedata']:
|
||||||
dologging('apilog.log',json.dumps(d))
|
dologging('apilog.log',json.dumps(d))
|
||||||
@@ -565,7 +678,7 @@ def strokedatajson_v2(request, id):
|
|||||||
df.sort_index(inplace=True)
|
df.sort_index(inplace=True)
|
||||||
|
|
||||||
status, comment, data = api_get_dataframe(row.startdatetime, df)
|
status, comment, data = api_get_dataframe(row.startdatetime, df)
|
||||||
if status != 200:
|
if status != 200: # pragma: no cover
|
||||||
return HttpResponse(comment, status=status)
|
return HttpResponse(comment, status=status)
|
||||||
|
|
||||||
r = getrower(request.user)
|
r = getrower(request.user)
|
||||||
|
|||||||
+16
-16
@@ -41,7 +41,7 @@ def workout_export_view(request, id=0, source='c2'):
|
|||||||
integration = importsources[source](request.user)
|
integration = importsources[source](request.user)
|
||||||
try:
|
try:
|
||||||
id = integration.workout_export(w)
|
id = integration.workout_export(w)
|
||||||
except NoTokenError:
|
except NoTokenError: # pragma: no cover
|
||||||
return HttpResponseRedirect(integration.make_authorization_url())
|
return HttpResponseRedirect(integration.make_authorization_url())
|
||||||
|
|
||||||
messages.info(
|
messages.info(
|
||||||
@@ -73,7 +73,7 @@ def rower_rojabo_authorize(request): # pragma: no cover
|
|||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
|
|
||||||
@login_required()
|
@login_required()
|
||||||
def rower_integration_authorize(request, source='c2'):
|
def rower_integration_authorize(request, source='c2'): # pragma: no cover
|
||||||
try:
|
try:
|
||||||
integration = importsources[source](request.user)
|
integration = importsources[source](request.user)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -107,7 +107,7 @@ def rower_polar_authorize(request, source='polar'): # pragma: no cover
|
|||||||
def rower_integration_token_refresh(request, source='c2'):
|
def rower_integration_token_refresh(request, source='c2'):
|
||||||
try:
|
try:
|
||||||
integration = importsources[source](request.user)
|
integration = importsources[source](request.user)
|
||||||
except KeyError:
|
except KeyError: # pragma: no cover
|
||||||
url = reverse('workouts_view')
|
url = reverse('workouts_view')
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
try:
|
try:
|
||||||
@@ -217,7 +217,7 @@ def rower_process_polarcallback(request):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
error = polar_user_data['error']
|
error = polar_user_data['error']
|
||||||
if error['error_type'] == 'user_already_registered':
|
if error['error_type'] == 'user_already_registered': # pragma: no cover
|
||||||
s = error['message']
|
s = error['message']
|
||||||
tester = re.compile(r'.*userid:(?P<id>\d+)')
|
tester = re.compile(r'.*userid:(?P<id>\d+)')
|
||||||
testresult = tester.match(s)
|
testresult = tester.match(s)
|
||||||
@@ -371,7 +371,7 @@ def workout_import_view(request, source='c2'):
|
|||||||
r = getrequestrower(request)
|
r = getrequestrower(request)
|
||||||
try:
|
try:
|
||||||
integration = importsources[source](request.user)
|
integration = importsources[source](request.user)
|
||||||
except KeyError:
|
except KeyError: # pragma: no cover
|
||||||
messages.error(request,"This integration is not supported")
|
messages.error(request,"This integration is not supported")
|
||||||
url = reverse('workouts_view')
|
url = reverse('workouts_view')
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
@@ -428,7 +428,7 @@ def workout_import_view(request, source='c2'):
|
|||||||
startdate=startdate,
|
startdate=startdate,
|
||||||
enddate=enddate
|
enddate=enddate
|
||||||
)
|
)
|
||||||
except NoTokenError:
|
except NoTokenError: # pragma: no cover
|
||||||
messages.error(request,"You must first make the connection to {source}".format(
|
messages.error(request,"You must first make the connection to {source}".format(
|
||||||
source=source
|
source=source
|
||||||
))
|
))
|
||||||
@@ -504,7 +504,7 @@ def rower_process_stravacallback(request):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
res = strava_integration.get_token(code,logfile='strava_log.log')
|
res = strava_integration.get_token(code,logfile='strava_log.log')
|
||||||
except NoTokenError:
|
except NoTokenError: # pragma: no cover
|
||||||
message = "Something went wrong with the Strava authorization"
|
message = "Something went wrong with the Strava authorization"
|
||||||
messages.error(request, message)
|
messages.error(request, message)
|
||||||
url = reverse('rower_exportsettings_view')
|
url = reverse('rower_exportsettings_view')
|
||||||
@@ -526,7 +526,7 @@ def rower_process_stravacallback(request):
|
|||||||
r.save()
|
r.save()
|
||||||
try:
|
try:
|
||||||
_ = strava_integration.set_strava_athlete_id()
|
_ = strava_integration.set_strava_athlete_id()
|
||||||
except NoTokenError:
|
except NoTokenError: # pragma: no cover
|
||||||
messages.error(request,'Something went wrong storing your Strava ID. Please authorize again')
|
messages.error(request,'Something went wrong storing your Strava ID. Please authorize again')
|
||||||
url = reverse('rower_exportsettings_view')
|
url = reverse('rower_exportsettings_view')
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
@@ -540,7 +540,7 @@ def rower_process_stravacallback(request):
|
|||||||
messages.error(request, message)
|
messages.error(request, message)
|
||||||
url = reverse('rower_exportsettings_view')
|
url = reverse('rower_exportsettings_view')
|
||||||
|
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url) # pragma: no cover
|
||||||
|
|
||||||
|
|
||||||
# Process SportTracks callback
|
# Process SportTracks callback
|
||||||
@@ -653,7 +653,7 @@ def rower_process_testcallback(request): # pragma: no cover
|
|||||||
message="This functionality requires a Pro plan or higher",
|
message="This functionality requires a Pro plan or higher",
|
||||||
redirect_field_name=None)
|
redirect_field_name=None)
|
||||||
@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True)
|
@permission_required('rower.is_coach', fn=get_user_by_userid, raise_exception=True)
|
||||||
def workout_rojaboimport_view(request, message="", userid=0):
|
def workout_rojaboimport_view(request, message="", userid=0): # pragma: no cover
|
||||||
r = getrequestrower(request, userid=userid)
|
r = getrequestrower(request, userid=userid)
|
||||||
if r.user != request.user:
|
if r.user != request.user:
|
||||||
messages.error(
|
messages.error(
|
||||||
@@ -728,21 +728,21 @@ def workout_rojaboimport_view(request, message="", userid=0):
|
|||||||
item['warm_up']['steps'],
|
item['warm_up']['steps'],
|
||||||
warmup=True
|
warmup=True
|
||||||
)
|
)
|
||||||
except KeyError:
|
except KeyError: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
steps = steps + rojabo_stuff.stepsconvert(
|
steps = steps + rojabo_stuff.stepsconvert(
|
||||||
item['primary']['steps'],
|
item['primary']['steps'],
|
||||||
startid=len(steps)
|
startid=len(steps)
|
||||||
)
|
)
|
||||||
except KeyError:
|
except KeyError: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
steps = steps + rojabo_stuff.stepsconvert(
|
steps = steps + rojabo_stuff.stepsconvert(
|
||||||
item['cool_down']['steps'],
|
item['cool_down']['steps'],
|
||||||
cooldown=True,
|
cooldown=True,
|
||||||
startid=len(steps))
|
startid=len(steps))
|
||||||
except KeyError:
|
except KeyError: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if steps:
|
if steps:
|
||||||
@@ -755,7 +755,7 @@ def workout_rojaboimport_view(request, message="", userid=0):
|
|||||||
ps.save()
|
ps.save()
|
||||||
|
|
||||||
messages.info(request,'Saved planned session {id}'.format(id=ps.id))
|
messages.info(request,'Saved planned session {id}'.format(id=ps.id))
|
||||||
except KeyError:
|
except KeyError: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
@@ -1019,7 +1019,7 @@ def garmin_details_view(request):
|
|||||||
def workout_getimportview(request, externalid, source='c2', do_async=True):
|
def workout_getimportview(request, externalid, source='c2', do_async=True):
|
||||||
try:
|
try:
|
||||||
integration = importsources[source](request.user)
|
integration = importsources[source](request.user)
|
||||||
except (TypeError, NotImplementedError, KeyError):
|
except (TypeError, NotImplementedError, KeyError): # pragma: no cover
|
||||||
return reverse("workouts_view")
|
return reverse("workouts_view")
|
||||||
if 'startdate' in request.session and source == 'nk': # pragma: no cover
|
if 'startdate' in request.session and source == 'nk': # pragma: no cover
|
||||||
startdate = request.session.get('startdate')
|
startdate = request.session.get('startdate')
|
||||||
@@ -1051,7 +1051,7 @@ def workout_getimportview(request, externalid, source='c2', do_async=True):
|
|||||||
|
|
||||||
# Imports all new workouts from SportTracks
|
# Imports all new workouts from SportTracks
|
||||||
@login_required()
|
@login_required()
|
||||||
def workout_getsporttracksworkout_all(request):
|
def workout_getsporttracksworkout_all(request): # pragma: no cover
|
||||||
st_integration = importsources['sporttracks'](request.user)
|
st_integration = importsources['sporttracks'](request.user)
|
||||||
try:
|
try:
|
||||||
_ = st_integration.get_workouts()
|
_ = st_integration.get_workouts()
|
||||||
|
|||||||
@@ -7,7 +7,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
|
||||||
|
|
||||||
from rowers.courses import getnearestraces, getnearestcourses
|
from rowers.courses import getnearestraces, getnearestcourses,coursetokml, coursestokml
|
||||||
|
|
||||||
# List Courses
|
# List Courses
|
||||||
|
|
||||||
@@ -16,6 +16,25 @@ def courses_view(request):
|
|||||||
r = getrower(request.user)
|
r = getrower(request.user)
|
||||||
g = GeoIP2()
|
g = GeoIP2()
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
try:
|
||||||
|
tdict = dict(request.POST.lists())
|
||||||
|
ids = tdict['courseid']
|
||||||
|
courseids = [int(id) for id in ids]
|
||||||
|
|
||||||
|
kmlstring = coursestokml(courseids)
|
||||||
|
kmlfilename = 'courses.kml'
|
||||||
|
response = HttpResponse(kmlstring)
|
||||||
|
response['Content-Disposition'] = 'attachment; filename="{filename}"'.format(
|
||||||
|
filename=kmlfilename)
|
||||||
|
response['Content-Type'] = 'application/octet-stream'
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
ip = request.META.get('HTTP_X_REAL_IP', '1.1.1.1')
|
ip = request.META.get('HTTP_X_REAL_IP', '1.1.1.1')
|
||||||
try:
|
try:
|
||||||
lat_lon = g.lat_lon(ip)
|
lat_lon = g.lat_lon(ip)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ from scipy.special import lambertw
|
|||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
import rowers.plots as plots
|
import rowers.plots as plots
|
||||||
from rowers.permissions import IsOwnerOrNot, IsCompetitorOrNot
|
from rowers.permissions import IsOwnerOrNot, IsCompetitorOrNot
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated, AllowAny
|
||||||
from rest_framework.decorators import api_view, renderer_classes, permission_classes
|
from rest_framework.decorators import api_view, renderer_classes, permission_classes
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rq.job import Job
|
from rq.job import Job
|
||||||
|
|||||||
@@ -258,7 +258,7 @@ def user_messages(request,userid=0):
|
|||||||
'rower':r})
|
'rower':r})
|
||||||
|
|
||||||
@login_required()
|
@login_required()
|
||||||
def user_message_delete(request,id=0):
|
def user_message_delete(request,id=0): # pragma: no cover
|
||||||
try:
|
try:
|
||||||
msg = UserMessage.objects.get(id=id)
|
msg = UserMessage.objects.get(id=id)
|
||||||
except UserMessage.DoesNotExist:
|
except UserMessage.DoesNotExist:
|
||||||
@@ -278,7 +278,7 @@ def user_message_delete(request,id=0):
|
|||||||
|
|
||||||
|
|
||||||
@login_required()
|
@login_required()
|
||||||
def user_message_markread(request,id=0):
|
def user_message_markread(request,id=0): # pragma: no cover
|
||||||
try:
|
try:
|
||||||
msg = UserMessage.objects.get(id=id)
|
msg = UserMessage.objects.get(id=id)
|
||||||
except UserMessage.DoesNotExist:
|
except UserMessage.DoesNotExist:
|
||||||
@@ -448,13 +448,13 @@ def rower_exportsettings_view(request, userid=0):
|
|||||||
if not user_is_not_basic(r.user): # pragma: no cover
|
if not user_is_not_basic(r.user): # pragma: no cover
|
||||||
if not user_is_coachee(r.user):
|
if not user_is_coachee(r.user):
|
||||||
doset = False
|
doset = False
|
||||||
if not doset:
|
if not doset: # pragma: no cover
|
||||||
before = getattr(r, attr)
|
before = getattr(r, attr)
|
||||||
if before == value:
|
if before == value:
|
||||||
doset = True
|
doset = True
|
||||||
if doset:
|
if doset:
|
||||||
setattr(r, attr, value)
|
setattr(r, attr, value)
|
||||||
else:
|
else: # pragma: no cover
|
||||||
if user_is_not_basic(r.user):
|
if user_is_not_basic(r.user):
|
||||||
messages.error(
|
messages.error(
|
||||||
request, 'Could not set '+attr+'. You need to create the connection first.')
|
request, 'Could not set '+attr+'. You need to create the connection first.')
|
||||||
@@ -605,7 +605,7 @@ def rower_simpleprefs_view(request, userid=0):
|
|||||||
r = getrequestrowercoachee(request, userid=userid, notpermanent=True)
|
r = getrequestrowercoachee(request, userid=userid, notpermanent=True)
|
||||||
|
|
||||||
firsttime = False
|
firsttime = False
|
||||||
if not r.ftpset:
|
if not r.ftpset: # pragma: no cover
|
||||||
r.ftpset = True
|
r.ftpset = True
|
||||||
r.save()
|
r.save()
|
||||||
firsttime = True
|
firsttime = True
|
||||||
@@ -623,7 +623,7 @@ def rower_simpleprefs_view(request, userid=0):
|
|||||||
|
|
||||||
form = SimpleRowerPowerForm(instance=r)
|
form = SimpleRowerPowerForm(instance=r)
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST': # pragma: no cover
|
||||||
form = SimpleRowerPowerForm(request.POST, instance=r)
|
form = SimpleRowerPowerForm(request.POST, instance=r)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
form.save(commit=True)
|
form.save(commit=True)
|
||||||
@@ -714,7 +714,7 @@ def rower_prefs_view(request, userid=0, message=""):
|
|||||||
r.pw_ut1,
|
r.pw_ut1,
|
||||||
r.pw_at,
|
r.pw_at,
|
||||||
r.pw_tr, r.pw_an])/r.ftp
|
r.pw_tr, r.pw_an])/r.ftp
|
||||||
if cogganzones:
|
if cogganzones: # pragma: no cover
|
||||||
powerfrac = np.array([55.,75.,90.,105.,120.])
|
powerfrac = np.array([55.,75.,90.,105.,120.])
|
||||||
powerzones = ['Rest',
|
powerzones = ['Rest',
|
||||||
'Active Recovery',
|
'Active Recovery',
|
||||||
|
|||||||
@@ -1664,7 +1664,7 @@ def course_compare_view(request, id=0):
|
|||||||
labeldict = {
|
labeldict = {
|
||||||
int(w.id): w.__str__() for w in workouts
|
int(w.id): w.__str__() for w in workouts
|
||||||
}
|
}
|
||||||
except:
|
except: # pragma: no cover
|
||||||
labeldict = {}
|
labeldict = {}
|
||||||
|
|
||||||
res = interactive_multiple_compare_chart(workoutids, xparam, yparam,
|
res = interactive_multiple_compare_chart(workoutids, xparam, yparam,
|
||||||
@@ -1846,7 +1846,7 @@ def virtualevent_compare_view(request, id=0):
|
|||||||
labeldict = {
|
labeldict = {
|
||||||
int(w.id): w.__str__() for w in workouts
|
int(w.id): w.__str__() for w in workouts
|
||||||
}
|
}
|
||||||
except:
|
except: # pragma: no cover
|
||||||
labeldict = {}
|
labeldict = {}
|
||||||
|
|
||||||
res = interactive_multiple_compare_chart(workoutids, xparam, yparam,
|
res = interactive_multiple_compare_chart(workoutids, xparam, yparam,
|
||||||
@@ -2048,9 +2048,9 @@ def workouts_bulk_actions(request):
|
|||||||
w = get_workout_by_opaqueid(request, encid)
|
w = get_workout_by_opaqueid(request, encid)
|
||||||
if w.user == r:
|
if w.user == r:
|
||||||
workouts.append(w)
|
workouts.append(w)
|
||||||
else:
|
else: # pragma: no cover
|
||||||
messages.error(request,'Bulk actions are not accessible to coaches')
|
messages.error(request,'Bulk actions are not accessible to coaches')
|
||||||
except KeyError:
|
except KeyError: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
@@ -2058,7 +2058,7 @@ def workouts_bulk_actions(request):
|
|||||||
form = WorkoutMultipleCompareForm(request.POST)
|
form = WorkoutMultipleCompareForm(request.POST)
|
||||||
if form.is_valid() and actionform.is_valid():
|
if form.is_valid() and actionform.is_valid():
|
||||||
workouts = form.cleaned_data['workouts']
|
workouts = form.cleaned_data['workouts']
|
||||||
if len(workouts) == 0:
|
if len(workouts) == 0: # pragma: no cover
|
||||||
url = reverse('workouts_view')
|
url = reverse('workouts_view')
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
action = actionform.cleaned_data['action']
|
action = actionform.cleaned_data['action']
|
||||||
@@ -2078,14 +2078,14 @@ def workouts_bulk_actions(request):
|
|||||||
'Workout {id} exported to {destination}'.format(
|
'Workout {id} exported to {destination}'.format(
|
||||||
id=encoder.encode_hex(w.id),
|
id=encoder.encode_hex(w.id),
|
||||||
destination=destination))
|
destination=destination))
|
||||||
except NoTokenError:
|
except NoTokenError: # pragma: no cover
|
||||||
messages.error(request,
|
messages.error(request,
|
||||||
'Export to {destination} of workout {id} failed'.format(
|
'Export to {destination} of workout {id} failed'.format(
|
||||||
id=encoder.encode_hex(w.id),
|
id=encoder.encode_hex(w.id),
|
||||||
destination=destination))
|
destination=destination))
|
||||||
url = reverse('workouts_view')
|
url = reverse('workouts_view')
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
else:
|
else: # pragma: no cover
|
||||||
if len(workouts) == 0:
|
if len(workouts) == 0:
|
||||||
url = reverse(workouts_view)
|
url = reverse(workouts_view)
|
||||||
return HttpResponseRedirect(url)
|
return HttpResponseRedirect(url)
|
||||||
@@ -3299,7 +3299,6 @@ def instroke_chart_interactive(request, id=0, analysis=0, userid=0):
|
|||||||
|
|
||||||
rowdata = rrdata(csvfile=w.csvfilename)
|
rowdata = rrdata(csvfile=w.csvfilename)
|
||||||
instrokemetrics = rowdata.get_instroke_columns()
|
instrokemetrics = rowdata.get_instroke_columns()
|
||||||
|
|
||||||
form = InstrokeForm(choices=instrokemetrics)
|
form = InstrokeForm(choices=instrokemetrics)
|
||||||
|
|
||||||
breadcrumbs = [
|
breadcrumbs = [
|
||||||
@@ -3513,6 +3512,7 @@ def instroke_chart_interactive(request, id=0, analysis=0, userid=0):
|
|||||||
name=name,notes=notes,
|
name=name,notes=notes,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# change to range spm_min to spm_max
|
# change to range spm_min to spm_max
|
||||||
vals, units, typ = rowdata.updateinterval_range(
|
vals, units, typ = rowdata.updateinterval_range(
|
||||||
' Cadence (stokes/min)', spm_min, spm_max,
|
' Cadence (stokes/min)', spm_min, spm_max,
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ DEBUG = True
|
|||||||
|
|
||||||
TEMPLATES[0]['OPTIONS']['debug'] = DEBUG
|
TEMPLATES[0]['OPTIONS']['debug'] = DEBUG
|
||||||
|
|
||||||
ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'dunav.ngrok.io']
|
ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'dunav.ngrok.io','rowsandall-2']
|
||||||
|
|
||||||
# INSTALLED_APPS += ['debug_toolbar',]
|
# INSTALLED_APPS += ['debug_toolbar',]
|
||||||
# INSTALLED_APPS += ["sslserver"]
|
# INSTALLED_APPS += ["sslserver"]
|
||||||
|
|||||||
Reference in New Issue
Block a user