another batch of files with a light sprinkle of comments
This commit is contained in:
@@ -4,8 +4,9 @@ from django.contrib.auth.models import User
|
|||||||
|
|
||||||
from .models import Rower, Workout,GraphImage,FavoriteChart,SiteAnnouncement
|
from .models import Rower, Workout,GraphImage,FavoriteChart,SiteAnnouncement
|
||||||
|
|
||||||
# Register your models here.
|
# Register your models here so you can use them in the Admin module
|
||||||
|
|
||||||
|
# Rower details directly under the User
|
||||||
class RowerInline(admin.StackedInline):
|
class RowerInline(admin.StackedInline):
|
||||||
model = Rower
|
model = Rower
|
||||||
can_delete = False
|
can_delete = False
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
# Store metadata for the app
|
||||||
class RowersConfig(AppConfig):
|
class RowersConfig(AppConfig):
|
||||||
name = 'rowers'
|
name = 'rowers'
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import os
|
|||||||
|
|
||||||
from celery import Celery
|
from celery import Celery
|
||||||
|
|
||||||
|
# Only used for testing with Celery on localhost. RQ is not available
|
||||||
|
# on Windows, so I use Celery on my notebook.
|
||||||
|
|
||||||
# set the default Django settings module for the 'celery' program.
|
# set the default Django settings module for the 'celery' program.
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'rowsandall_app.settings')
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'rowsandall_app.settings')
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# This is Data prep used for testing purposes (no Django environment)
|
||||||
|
# Uses the debug SQLite database for stroke data
|
||||||
from rowingdata import rowingdata as rrdata
|
from rowingdata import rowingdata as rrdata
|
||||||
|
|
||||||
from rowingdata import rower as rrower
|
from rowingdata import rower as rrower
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# Processes emails sent to workouts@rowsandall.com
|
||||||
import time
|
import time
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from rowers.tasks import handle_sendemail_unrecognized
|
from rowers.tasks import handle_sendemail_unrecognized
|
||||||
@@ -21,7 +22,9 @@ from scipy.signal import savgol_filter
|
|||||||
|
|
||||||
import zipfile
|
import zipfile
|
||||||
import os
|
import os
|
||||||
|
import rowers.dataprep as dataprep
|
||||||
|
|
||||||
|
# Sends a confirmation with a link to the workout
|
||||||
def send_confirm(u,name,link):
|
def send_confirm(u,name,link):
|
||||||
fullemail = u.email
|
fullemail = u.email
|
||||||
subject = 'Workout added: '+name
|
subject = 'Workout added: '+name
|
||||||
@@ -38,6 +41,7 @@ def send_confirm(u,name,link):
|
|||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
# Reads a "rowingdata" object, plus some error protections
|
||||||
def rdata(file,rower=rrower()):
|
def rdata(file,rower=rrower()):
|
||||||
try:
|
try:
|
||||||
res = rrdata(file,rower=rower)
|
res = rrdata(file,rower=rower)
|
||||||
@@ -49,13 +53,18 @@ def rdata(file,rower=rrower()):
|
|||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
# Some error protection around process attachments
|
||||||
def safeprocessattachments():
|
def safeprocessattachments():
|
||||||
try:
|
try:
|
||||||
return processattachments()
|
return processattachments()
|
||||||
except:
|
except:
|
||||||
return [0]
|
return [0]
|
||||||
|
|
||||||
|
# This is duplicated in management/commands/processemail
|
||||||
|
# Need to double check the code there, update here, and only
|
||||||
|
# use the code here.
|
||||||
def processattachments():
|
def processattachments():
|
||||||
|
# in res, we store the ids of the new workouts
|
||||||
res = []
|
res = []
|
||||||
attachments = MessageAttachment.objects.all()
|
attachments = MessageAttachment.objects.all()
|
||||||
for a in attachments:
|
for a in attachments:
|
||||||
@@ -73,7 +82,6 @@ def processattachments():
|
|||||||
try:
|
try:
|
||||||
wid = [make_new_workout_from_email(rr,a.document,name)]
|
wid = [make_new_workout_from_email(rr,a.document,name)]
|
||||||
res += wid
|
res += wid
|
||||||
print wid
|
|
||||||
link = 'https://rowsandall.com/rowers/workout/'+str(wid[0])+'/edit'
|
link = 'https://rowsandall.com/rowers/workout/'+str(wid[0])+'/edit'
|
||||||
dd = send_confirm(u,name,link)
|
dd = send_confirm(u,name,link)
|
||||||
except:
|
except:
|
||||||
@@ -92,6 +100,7 @@ def processattachments():
|
|||||||
# no attachments, so can be deleted
|
# no attachments, so can be deleted
|
||||||
m.delete()
|
m.delete()
|
||||||
|
|
||||||
|
# Delete remaining messages (which should not have attachments)
|
||||||
mm = Message.objects.all()
|
mm = Message.objects.all()
|
||||||
for m in mm:
|
for m in mm:
|
||||||
if m.attachments.exists()==False:
|
if m.attachments.exists()==False:
|
||||||
@@ -99,6 +108,7 @@ def processattachments():
|
|||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
# As above, but with some print commands for debugging purposes
|
||||||
def processattachments_debug():
|
def processattachments_debug():
|
||||||
res = []
|
res = []
|
||||||
attachments = MessageAttachment.objects.all()
|
attachments = MessageAttachment.objects.all()
|
||||||
@@ -144,7 +154,9 @@ def processattachments_debug():
|
|||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
# Process the attachment file, create new workout
|
||||||
|
# The code here is duplication of the code in views.py (workout_upload_view)
|
||||||
|
# Need to move the code to a subroutine used both in views.py and here
|
||||||
def make_new_workout_from_email(rr,f2,name,cntr=0):
|
def make_new_workout_from_email(rr,f2,name,cntr=0):
|
||||||
workouttype = 'rower'
|
workouttype = 'rower'
|
||||||
f2 = f2.name
|
f2 = f2.name
|
||||||
@@ -305,6 +317,16 @@ def make_new_workout_from_email(rr,f2,name,cntr=0):
|
|||||||
startdatetime=row.rowdatetime)
|
startdatetime=row.rowdatetime)
|
||||||
|
|
||||||
w.save()
|
w.save()
|
||||||
|
|
||||||
|
print w.id
|
||||||
|
print row.df.info()
|
||||||
|
|
||||||
|
# put stroke data in database
|
||||||
|
res = dataprep.dataprep(row.df,id=w.id,
|
||||||
|
bands=True,barchart=True,
|
||||||
|
otwpower=True,empower=True)
|
||||||
|
|
||||||
|
|
||||||
return w.id
|
return w.id
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Interactions with Rowsandall.com API. Not fully complete.
|
||||||
|
|
||||||
# Python
|
# Python
|
||||||
import oauth2 as oauth
|
import oauth2 as oauth
|
||||||
import cgi
|
import cgi
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# Defines permissions for objects (API related)
|
||||||
|
|
||||||
from rest_framework import permissions
|
from rest_framework import permissions
|
||||||
from rowers.models import Rower
|
from rowers.models import Rower
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import matplotlib.pyplot as plt
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
# Make pace ticks for pace axis '2:00', '1:50', etc
|
||||||
def format_pace_tick(x,pos=None):
|
def format_pace_tick(x,pos=None):
|
||||||
min=int(x/60)
|
min=int(x/60)
|
||||||
sec=int(x-min*60.)
|
sec=int(x-min*60.)
|
||||||
@@ -10,6 +11,7 @@ def format_pace_tick(x,pos=None):
|
|||||||
template='%d:%s'
|
template='%d:%s'
|
||||||
return template % (min,sec_str)
|
return template % (min,sec_str)
|
||||||
|
|
||||||
|
# Returns a pace string from a pace in seconds/500m, '1:45.4'
|
||||||
def format_pace(x,pos=None):
|
def format_pace(x,pos=None):
|
||||||
if isinf(x) or isnan(x):
|
if isinf(x) or isnan(x):
|
||||||
x=0
|
x=0
|
||||||
@@ -24,6 +26,7 @@ def format_pace(x,pos=None):
|
|||||||
|
|
||||||
return str1
|
return str1
|
||||||
|
|
||||||
|
# Returns a time string from a time in seconds
|
||||||
def format_time(x,pos=None):
|
def format_time(x,pos=None):
|
||||||
|
|
||||||
|
|
||||||
@@ -37,11 +40,13 @@ def format_time(x,pos=None):
|
|||||||
|
|
||||||
return str1
|
return str1
|
||||||
|
|
||||||
|
# Formatting the distance tick marks
|
||||||
def format_dist_tick(x,pos=None):
|
def format_dist_tick(x,pos=None):
|
||||||
km = x/1000.
|
km = x/1000.
|
||||||
template='%6.3f'
|
template='%6.3f'
|
||||||
return template % (km)
|
return template % (km)
|
||||||
|
|
||||||
|
# Formatting the time tick marks (h:mm)
|
||||||
def format_time_tick(x,pos=None):
|
def format_time_tick(x,pos=None):
|
||||||
hour=int(x/3600)
|
hour=int(x/3600)
|
||||||
min=int((x-hour*3600.)/60)
|
min=int((x-hour*3600.)/60)
|
||||||
@@ -49,6 +54,11 @@ def format_time_tick(x,pos=None):
|
|||||||
template='%d:%s'
|
template='%d:%s'
|
||||||
return template % (hour,min_str)
|
return template % (hour,min_str)
|
||||||
|
|
||||||
|
# Utility to select reasonable y axis range
|
||||||
|
# Basically the data range plus some padding, but with ultimate
|
||||||
|
# you can set the slowest paces to fall off the axis.
|
||||||
|
# Useful for OTW rowing where you sometimes stops and pace runs out of
|
||||||
|
# the boundaries
|
||||||
def y_axis_range(ydata,miny=0,padding=.1,ultimate=[-1e9,1e9]):
|
def y_axis_range(ydata,miny=0,padding=.1,ultimate=[-1e9,1e9]):
|
||||||
|
|
||||||
# ydata must by a numpy array
|
# ydata must by a numpy array
|
||||||
@@ -86,6 +96,7 @@ def y_axis_range(ydata,miny=0,padding=.1,ultimate=[-1e9,1e9]):
|
|||||||
|
|
||||||
return [yrangemin,yrangemax]
|
return [yrangemin,yrangemax]
|
||||||
|
|
||||||
|
# Make a plot (this one is only used for testing)
|
||||||
def mkplot(row,title):
|
def mkplot(row,title):
|
||||||
df = row.df
|
df = row.df
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
# Serializers. Defines which fields from an object get to the JSON object
|
||||||
|
# Also optionally define POST, PATCH methods (create, update)
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rowers.models import Workout,Rower,StrokeData,FavoriteChart
|
from rowers.models import Workout,Rower,StrokeData,FavoriteChart
|
||||||
|
|
||||||
@@ -104,6 +107,7 @@ class WorkoutSerializer(serializers.ModelSerializer):
|
|||||||
instance.save()
|
instance.save()
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
# This is just a fake one for URL registration purposes
|
||||||
class StrokeDataSerializer(serializers.Serializer):
|
class StrokeDataSerializer(serializers.Serializer):
|
||||||
workoutid = serializers.IntegerField
|
workoutid = serializers.IntegerField
|
||||||
strokedata = serializers.JSONField
|
strokedata = serializers.JSONField
|
||||||
|
|||||||
@@ -21,12 +21,12 @@ from rowers.dataprepnodjango import update_strokedata
|
|||||||
|
|
||||||
from django.core.mail import send_mail, BadHeaderError,EmailMessage
|
from django.core.mail import send_mail, BadHeaderError,EmailMessage
|
||||||
|
|
||||||
|
# testing task
|
||||||
@app.task
|
@app.task
|
||||||
def add(x, y):
|
def add(x, y):
|
||||||
return x + y
|
return x + y
|
||||||
|
|
||||||
|
# send email to me when an unrecognized file is uploaded
|
||||||
@app.task
|
@app.task
|
||||||
def handle_sendemail_unrecognized(unrecognizedfile,useremail):
|
def handle_sendemail_unrecognized(unrecognizedfile,useremail):
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ def handle_sendemail_unrecognized(unrecognizedfile,useremail):
|
|||||||
os.remove(unrecognizedfile)
|
os.remove(unrecognizedfile)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
# Send email with TCX attachment
|
||||||
@app.task
|
@app.task
|
||||||
def handle_sendemailtcx(first_name,last_name,email,tcxfile):
|
def handle_sendemailtcx(first_name,last_name,email,tcxfile):
|
||||||
|
|
||||||
@@ -75,6 +75,7 @@ def handle_sendemailtcx(first_name,last_name,email,tcxfile):
|
|||||||
os.remove(tcxfile)
|
os.remove(tcxfile)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
# Send email with CSV attachment
|
||||||
@app.task
|
@app.task
|
||||||
def handle_sendemailcsv(first_name,last_name,email,csvfile):
|
def handle_sendemailcsv(first_name,last_name,email,csvfile):
|
||||||
|
|
||||||
@@ -104,6 +105,7 @@ def handle_sendemailcsv(first_name,last_name,email,csvfile):
|
|||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
# Calculate wind and stream corrections for OTW rowing
|
||||||
@app.task
|
@app.task
|
||||||
def handle_otwsetpower(f1,boattype,weightvalue,
|
def handle_otwsetpower(f1,boattype,weightvalue,
|
||||||
first_name,last_name,email,workoutid,
|
first_name,last_name,email,workoutid,
|
||||||
@@ -129,7 +131,7 @@ def handle_otwsetpower(f1,boattype,weightvalue,
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
rg = rowingdata.getrigging('static/rigging/1x.txt')
|
rg = rowingdata.getrigging('static/rigging/1x.txt')
|
||||||
|
|
||||||
# do calculation
|
# do calculation, but do not overwrite NK Empower Power data
|
||||||
powermeasured = False
|
powermeasured = False
|
||||||
try:
|
try:
|
||||||
w = rowdata.df['wash']
|
w = rowdata.df['wash']
|
||||||
@@ -166,6 +168,7 @@ def handle_otwsetpower(f1,boattype,weightvalue,
|
|||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
# This function generates all the static (PNG image) plots
|
||||||
@app.task
|
@app.task
|
||||||
def handle_makeplot(f1,f2,t,hrdata,plotnr,imagename):
|
def handle_makeplot(f1,f2,t,hrdata,plotnr,imagename):
|
||||||
hrmax = hrdata['hrmax']
|
hrmax = hrdata['hrmax']
|
||||||
@@ -222,5 +225,6 @@ def handle_makeplot(f1,f2,t,hrdata,plotnr,imagename):
|
|||||||
return imagename
|
return imagename
|
||||||
|
|
||||||
|
|
||||||
|
# Another simple task for debugging purposes
|
||||||
def add2(x,y):
|
def add2(x,y):
|
||||||
return x+y
|
return x+y
|
||||||
|
|||||||
@@ -8,9 +8,8 @@ from rowers.models import Rower, Workout
|
|||||||
|
|
||||||
from rowsandall_app.settings import FORECAST_IO_KEY
|
from rowsandall_app.settings import FORECAST_IO_KEY
|
||||||
|
|
||||||
|
# Get weather data from the DarkSky API
|
||||||
def get_weather_data(long,lat,unixtime):
|
def get_weather_data(long,lat,unixtime):
|
||||||
# url = "https://api.forecast.io/forecast/"+FORECAST_IO_KEY+"/"
|
|
||||||
url = "https://api.darksky.net/forecast/"+FORECAST_IO_KEY+"/"
|
url = "https://api.darksky.net/forecast/"+FORECAST_IO_KEY+"/"
|
||||||
url += str(long)+","+str(lat)+","+str(unixtime)
|
url += str(long)+","+str(lat)+","+str(unixtime)
|
||||||
|
|
||||||
@@ -21,6 +20,7 @@ def get_weather_data(long,lat,unixtime):
|
|||||||
else:
|
else:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
# Get wind data (and translate from knots to m/s)
|
||||||
def get_wind_data(lat,long,unixtime):
|
def get_wind_data(lat,long,unixtime):
|
||||||
data = get_weather_data(lat,long,unixtime)
|
data = get_weather_data(lat,long,unixtime)
|
||||||
if data:
|
if data:
|
||||||
@@ -39,6 +39,7 @@ def get_wind_data(lat,long,unixtime):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
temperature = data['currently']['temperature']
|
temperature = data['currently']['temperature']
|
||||||
|
# Temp is given in Fahrenheit, so convert to Celsius for Europeans
|
||||||
temperaturec = (temperature-32.)*(5./9.)
|
temperaturec = (temperature-32.)*(5./9.)
|
||||||
temperaturec = int(10*temperaturec)/10.
|
temperaturec = int(10*temperaturec)/10.
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -54,7 +55,8 @@ def get_wind_data(lat,long,unixtime):
|
|||||||
windbearing = 0
|
windbearing = 0
|
||||||
message = 'Not able to get weather data'
|
message = 'Not able to get weather data'
|
||||||
|
|
||||||
# apply Hellman's coefficient for neutral air above human inhabitated areas
|
# apply Hellman's coefficient for neutral air above human
|
||||||
|
# inhabitated areas
|
||||||
windspeed = windspeed*(0.1)**0.34
|
windspeed = windspeed*(0.1)**0.34
|
||||||
windspeed = 0.01*int(100*windspeed)
|
windspeed = 0.01*int(100*windspeed)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user