Merge branch 'release/v13.30'
This commit is contained in:
@@ -70,6 +70,7 @@ import zipfile
|
|||||||
import pandas as pd
|
import pandas as pd
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import itertools
|
import itertools
|
||||||
|
from fitparse import FitFile
|
||||||
import math
|
import math
|
||||||
from rowers.tasks import (
|
from rowers.tasks import (
|
||||||
handle_sendemail_unrecognized, handle_sendemail_breakthrough,
|
handle_sendemail_unrecognized, handle_sendemail_breakthrough,
|
||||||
@@ -94,6 +95,7 @@ queuehigh = django_rq.get_queue('default')
|
|||||||
|
|
||||||
from rowsandall_app.settings import SITE_URL
|
from rowsandall_app.settings import SITE_URL
|
||||||
from rowers.mytypes import otwtypes,otetypes
|
from rowers.mytypes import otwtypes,otetypes
|
||||||
|
from rowers import mytypes
|
||||||
|
|
||||||
from rowers.database import *
|
from rowers.database import *
|
||||||
from rowers.opaque import encoder
|
from rowers.opaque import encoder
|
||||||
@@ -747,6 +749,19 @@ def clean_df_stats(datadf, workstrokesonly=True, ignorehr=True,
|
|||||||
|
|
||||||
return datadf
|
return datadf
|
||||||
|
|
||||||
|
def getpartofday(dt):
|
||||||
|
h = dt.hour
|
||||||
|
if h < 12:
|
||||||
|
return "Morning"
|
||||||
|
elif h < 18:
|
||||||
|
return "Afternoon"
|
||||||
|
elif h < 22:
|
||||||
|
return "Evening"
|
||||||
|
else:
|
||||||
|
return "Night"
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
def getstatsfields():
|
def getstatsfields():
|
||||||
fielddict = {name:d['verbose_name'] for name,d in rowingmetrics}
|
fielddict = {name:d['verbose_name'] for name,d in rowingmetrics}
|
||||||
|
|
||||||
@@ -1138,8 +1153,7 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower',
|
|||||||
impeller=False):
|
impeller=False):
|
||||||
|
|
||||||
message = None
|
message = None
|
||||||
if title is None:
|
|
||||||
title = 'Workout'
|
|
||||||
|
|
||||||
powerperc = 100 * np.array([r.pw_ut2,
|
powerperc = 100 * np.array([r.pw_ut2,
|
||||||
r.pw_ut1,
|
r.pw_ut1,
|
||||||
@@ -1153,6 +1167,16 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower',
|
|||||||
powerperc=powerperc, powerzones=r.powerzones)
|
powerperc=powerperc, powerzones=r.powerzones)
|
||||||
row = rdata(f2, rower=rr)
|
row = rdata(f2, rower=rr)
|
||||||
|
|
||||||
|
if title is None or title == '':
|
||||||
|
title = 'Workout'
|
||||||
|
partofday = getpartofday(row.rowdatetime)
|
||||||
|
|
||||||
|
if partofday is not None:
|
||||||
|
title = '{partofday} workout {workouttype}'.format(
|
||||||
|
partofday=partofday,
|
||||||
|
workouttype=workouttype,
|
||||||
|
)
|
||||||
|
|
||||||
if row.df.empty:
|
if row.df.empty:
|
||||||
return (0, 'Error: CSV data file was empty')
|
return (0, 'Error: CSV data file was empty')
|
||||||
|
|
||||||
@@ -1533,6 +1557,38 @@ def handle_nonpainsled(f2, fileformat, summary=''):
|
|||||||
# This routine should be used everywhere in views.py and mailprocessing.py
|
# This routine should be used everywhere in views.py and mailprocessing.py
|
||||||
# Currently there is code duplication
|
# Currently there is code duplication
|
||||||
|
|
||||||
|
def get_workouttype_from_fit(filename,workouttype='water'):
|
||||||
|
fitfile = FitFile(filename,check_crc=False)
|
||||||
|
records = fitfile.messages
|
||||||
|
fittype = 'rowing'
|
||||||
|
for record in records:
|
||||||
|
if record.name == 'sport':
|
||||||
|
fittype = record.get_values()['sport'].lower()
|
||||||
|
try:
|
||||||
|
workouttype = mytypes.fitmappinginv[fittype]
|
||||||
|
except KeyError:
|
||||||
|
return workouttype
|
||||||
|
|
||||||
|
return workouttype
|
||||||
|
|
||||||
|
import rowingdata.tcxtools as tcxtools
|
||||||
|
|
||||||
|
def get_workouttype_from_tcx(filename,workouttype='water'):
|
||||||
|
d = tcxtools.tcx_getdict(filename)
|
||||||
|
tcxtype = 'rowing'
|
||||||
|
try:
|
||||||
|
tcxtype = d['Activities']['Activity']['@Sport'].lower()
|
||||||
|
if tcxtype == 'other':
|
||||||
|
tcxtype = 'rowing'
|
||||||
|
except KeyError:
|
||||||
|
return workouttype
|
||||||
|
|
||||||
|
try:
|
||||||
|
workouttype = mytypes.garminmappinginv[tcxtype.upper()]
|
||||||
|
except KeyError:
|
||||||
|
return workouttype
|
||||||
|
|
||||||
|
return workouttype
|
||||||
|
|
||||||
def new_workout_from_file(r, f2,
|
def new_workout_from_file(r, f2,
|
||||||
workouttype='rower',
|
workouttype='rower',
|
||||||
@@ -1554,6 +1610,8 @@ def new_workout_from_file(r, f2,
|
|||||||
summary = ''
|
summary = ''
|
||||||
oarlength = 2.89
|
oarlength = 2.89
|
||||||
inboard = 0.88
|
inboard = 0.88
|
||||||
|
|
||||||
|
# Save zip files to email box for further processing
|
||||||
if len(fileformat) == 3 and fileformat[0] == 'zip':
|
if len(fileformat) == 3 and fileformat[0] == 'zip':
|
||||||
uploadoptions['fromuploadform'] = True
|
uploadoptions['fromuploadform'] = True
|
||||||
bodyyaml = yaml.safe_dump(uploadoptions,default_flow_style=False)
|
bodyyaml = yaml.safe_dump(uploadoptions,default_flow_style=False)
|
||||||
@@ -1621,10 +1679,17 @@ def new_workout_from_file(r, f2,
|
|||||||
r.user.email)
|
r.user.email)
|
||||||
|
|
||||||
return (0, message, f2)
|
return (0, message, f2)
|
||||||
|
|
||||||
if fileformat == 'att':
|
if fileformat == 'att':
|
||||||
# email attachment which can safely be ignored
|
# email attachment which can safely be ignored
|
||||||
return (0, '', f2)
|
return (0, '', f2)
|
||||||
|
|
||||||
|
# Get workout type from fit & tcx
|
||||||
|
if (fileformat == 'fit'):
|
||||||
|
workouttype = get_workouttype_from_fit(f2,workouttype=workouttype)
|
||||||
|
if (fileformat == 'tcx'):
|
||||||
|
workouttype = get_workouttype_from_tcx(f2,workouttype=workouttype)
|
||||||
|
|
||||||
# handle non-Painsled by converting it to painsled compatible CSV
|
# handle non-Painsled by converting it to painsled compatible CSV
|
||||||
if (fileformat != 'csv'):
|
if (fileformat != 'csv'):
|
||||||
f2, summary, oarlength, inboard, fileformat, impeller = handle_nonpainsled(
|
f2, summary, oarlength, inboard, fileformat, impeller = handle_nonpainsled(
|
||||||
@@ -1636,6 +1701,8 @@ def new_workout_from_file(r, f2,
|
|||||||
message = 'Something went wrong'
|
message = 'Something went wrong'
|
||||||
return (0, message, '')
|
return (0, message, '')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
dosummary = (fileformat != 'fit' and 'speedcoach2' not in fileformat)
|
dosummary = (fileformat != 'fit' and 'speedcoach2' not in fileformat)
|
||||||
dosummary = dosummary or summary == ''
|
dosummary = dosummary or summary == ''
|
||||||
|
|
||||||
|
|||||||
@@ -112,6 +112,42 @@ garmincollection = (
|
|||||||
|
|
||||||
garminmapping = {key:value for key,value in Reverse(garmincollection)}
|
garminmapping = {key:value for key,value in Reverse(garmincollection)}
|
||||||
|
|
||||||
|
fitcollection = (
|
||||||
|
('water','rowing'),
|
||||||
|
('rower','rowing'),
|
||||||
|
('skierg','cross_country_skiing'),
|
||||||
|
('Bike','cycling'),
|
||||||
|
('bikeerg','cycling'),
|
||||||
|
('dynamic','rowing'),
|
||||||
|
('slides','rowing'),
|
||||||
|
('paddle','paddling'),
|
||||||
|
('snow','cross_country_skiing'),
|
||||||
|
('coastal','rowing'),
|
||||||
|
('c-boat','rowing'),
|
||||||
|
('churchboat','rowing'),
|
||||||
|
('Ride','cycling'),
|
||||||
|
('Run','running'),
|
||||||
|
('NordicSki','cross_country_skiing'),
|
||||||
|
('Swim','swimming'),
|
||||||
|
('Hike','hiking'),
|
||||||
|
('RollerSki','cross_country_skiing'),
|
||||||
|
('Walk','walking'),
|
||||||
|
('Canoeing','boating'),
|
||||||
|
('Crossfit','fitness_equipment'),
|
||||||
|
('StandUpPaddling','stand_up_paddle_boarding'),
|
||||||
|
('IceSkate','ice_skating'),
|
||||||
|
('WeightTraining','training'),
|
||||||
|
('InlineSkate','inline_skating'),
|
||||||
|
('Kayaking','kayaking'),
|
||||||
|
('Workout','generic'),
|
||||||
|
('Yoga','generic'),
|
||||||
|
('other','generic'),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fitmapping = {key:value for key,value in Reverse(fitcollection)}
|
||||||
|
|
||||||
stcollection = (
|
stcollection = (
|
||||||
('water','Rowing'),
|
('water','Rowing'),
|
||||||
('rower','Rowing'),
|
('rower','Rowing'),
|
||||||
@@ -289,6 +325,8 @@ polarmappinginv = {value:key for key,value in Reverse(polarcollection) if value
|
|||||||
|
|
||||||
garminmappinginv = {value:key for key, value in Reverse(garmincollection) if value is not None}
|
garminmappinginv = {value:key for key, value in Reverse(garmincollection) if value is not None}
|
||||||
|
|
||||||
|
fitmappinginv = {value:key for key,value in Reverse(fitcollection) if value is not None}
|
||||||
|
|
||||||
otwtypes = (
|
otwtypes = (
|
||||||
'water',
|
'water',
|
||||||
'coastal',
|
'coastal',
|
||||||
|
|||||||
@@ -707,7 +707,6 @@ def handle_get_garmin_file(client_id,
|
|||||||
)+filetype
|
)+filetype
|
||||||
|
|
||||||
response = garmin.get(url, stream=True)
|
response = garmin.get(url, stream=True)
|
||||||
print(response.status_code,filename)
|
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
with open(filename, 'wb') as out_file:
|
with open(filename, 'wb') as out_file:
|
||||||
shutil.copyfileobj(response.raw, out_file)
|
shutil.copyfileobj(response.raw, out_file)
|
||||||
@@ -718,7 +717,7 @@ def handle_get_garmin_file(client_id,
|
|||||||
'secret':UPLOAD_SERVICE_SECRET,
|
'secret':UPLOAD_SERVICE_SECRET,
|
||||||
'user':userid,
|
'user':userid,
|
||||||
'file': filename,
|
'file': filename,
|
||||||
'title': filename[6:],
|
'title': '',
|
||||||
'workouttype':'water',
|
'workouttype':'water',
|
||||||
'boattype':'1x',
|
'boattype':'1x',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,12 +123,12 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
<h2>On-line Racing</h2>
|
<h2>On-line Challenges</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a href="/rowers/virtualevents">On-line racing</a> is a
|
<a href="/rowers/virtualevents">On-line challenges</a> are a
|
||||||
fun way to race other Rowsandall.com users
|
fun way to test your boat speed and course line against other Rowsandall.com users
|
||||||
rowing on the same stretch of water.
|
rowing on the same stretch of water or on the Concept2 ergometer.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Training Plan</h2>
|
<h2>Training Plan</h2>
|
||||||
|
|||||||
@@ -1035,11 +1035,7 @@ def garmin_summaries_view(request):
|
|||||||
def garmin_newfiles_ping(request):
|
def garmin_newfiles_ping(request):
|
||||||
t = time.localtime()
|
t = time.localtime()
|
||||||
timestamp = time.strftime('%b-%d-%Y_%H%M', t)
|
timestamp = time.strftime('%b-%d-%Y_%H%M', t)
|
||||||
with open('garminlog.log','a') as f:
|
|
||||||
f.write('\n')
|
|
||||||
f.write(timestamp)
|
|
||||||
f.write(' ')
|
|
||||||
f.write(str(request.body))
|
|
||||||
|
|
||||||
if request.method != 'POST':
|
if request.method != 'POST':
|
||||||
return HttpResponse(status=200)
|
return HttpResponse(status=200)
|
||||||
@@ -1090,11 +1086,6 @@ def garmin_details_view(request):
|
|||||||
|
|
||||||
t = time.localtime()
|
t = time.localtime()
|
||||||
timestamp = time.strftime('%b-%d-%Y_%H%M', t)
|
timestamp = time.strftime('%b-%d-%Y_%H%M', t)
|
||||||
with open('garminlog.log','a') as f:
|
|
||||||
f.write('\n')
|
|
||||||
f.write(timestamp)
|
|
||||||
f.write(' ')
|
|
||||||
f.write(str(request.body))
|
|
||||||
|
|
||||||
# POST request
|
# POST request
|
||||||
data = json.loads(request.body)
|
data = json.loads(request.body)
|
||||||
|
|||||||
Reference in New Issue
Block a user