Private
Public Access
1
0

Merge branch 'develop' into feature/stravadevice

This commit is contained in:
Sander Roosendaal
2017-04-23 09:58:11 +02:00
37 changed files with 1235 additions and 457 deletions

BIN
logos/tpchecked.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
logos/tpgray.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
logos/tpicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
logos/tpicon.xcf Normal file

Binary file not shown.

View File

@@ -28,6 +28,7 @@ from rowingdata import (
) )
from rowers.models import Team from rowers.models import Team
from rowers.metrics import axes
import os import os
import zipfile import zipfile
@@ -363,7 +364,7 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
velo = 500./pace velo = 500./pace
f = row.df['TimeStamp (sec)'].diff().mean() f = row.df['TimeStamp (sec)'].diff().mean()
if f !=0: if f !=0 and not np.isnan(f):
windowsize = 2*(int(10./(f)))+1 windowsize = 2*(int(10./(f)))+1
else: else:
windowsize = 1 windowsize = 1
@@ -899,6 +900,13 @@ def prepmultipledata(ids,verbose=False):
# Read a set of columns for a set of workout ids, returns data as a # Read a set of columns for a set of workout ids, returns data as a
# pandas dataframe # pandas dataframe
def read_cols_df_sql(ids,columns): def read_cols_df_sql(ids,columns):
# drop columns that are not in offical list
# axx = [ax[0] for ax in axes]
axx = StrokeData._meta.get_all_field_names()
for c in columns:
if not c in axx:
columns.remove(c)
columns = list(columns)+['distance','spm'] columns = list(columns)+['distance','spm']
columns = [x for x in columns if x != 'None'] columns = [x for x in columns if x != 'None']
columns = list(set(columns)) columns = list(set(columns))

View File

@@ -82,6 +82,7 @@ def interactive_forcecurve(theworkouts,workstrokesonly=False):
'workoutstate','driveenergy'] 'workoutstate','driveenergy']
rowdata = dataprep.getsmallrowdata_db(columns,ids=ids) rowdata = dataprep.getsmallrowdata_db(columns,ids=ids)
rowdata.dropna(axis=1,how='all',inplace=True) rowdata.dropna(axis=1,how='all',inplace=True)
rowdata.dropna(axis=0,how='any',inplace=True) rowdata.dropna(axis=0,how='any',inplace=True)
@@ -369,6 +370,8 @@ def interactive_histoall(theworkouts):
ids = [int(w.id) for w in theworkouts] ids = [int(w.id) for w in theworkouts]
rowdata = dataprep.getsmallrowdata_db(['power'],ids=ids,doclean=True) rowdata = dataprep.getsmallrowdata_db(['power'],ids=ids,doclean=True)
rowdata.dropna(axis=0,how='any',inplace=True) rowdata.dropna(axis=0,how='any',inplace=True)
if rowdata.empty: if rowdata.empty:
@@ -802,6 +805,7 @@ def interactive_chart(id=0,promember=0):
columns = ['time','pace','hr','fpace','ftime'] columns = ['time','pace','hr','fpace','ftime']
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)
if datadf.empty: if datadf.empty:
@@ -883,6 +887,17 @@ def interactive_cum_flex_chart2(theworkouts,promember=0,
columns = [xparam,yparam1,yparam2,'spm','driveenergy','distance'] columns = [xparam,yparam1,yparam2,'spm','driveenergy','distance']
datadf = dataprep.getsmallrowdata_db(columns,ids=ids,doclean=False) datadf = dataprep.getsmallrowdata_db(columns,ids=ids,doclean=False)
try:
tests = rowdata[yparam2]
except KeyError:
yparam2 = 'None'
try:
tests = rowdata[yparam1]
except KeyError:
yparam1 = 'None'
yparamname1 = axlabels[yparam1] yparamname1 = axlabels[yparam1]
if yparam2 != 'None': if yparam2 != 'None':
yparamname2 = axlabels[yparam2] yparamname2 = axlabels[yparam2]
@@ -1167,6 +1182,16 @@ def interactive_flex_chart2(id=0,promember=0,
rowdata = dataprep.getsmallrowdata_db(columns,ids=[id],doclean=True, rowdata = dataprep.getsmallrowdata_db(columns,ids=[id],doclean=True,
workstrokesonly=workstrokesonly) workstrokesonly=workstrokesonly)
try:
tests = rowdata[yparam2]
except KeyError:
yparam2 = 'None'
try:
tests = rowdata[yparam1]
except KeyError:
yparam1 = 'None'
rowdata.dropna(axis=1,how='all',inplace=True) rowdata.dropna(axis=1,how='all',inplace=True)
rowdata.dropna(axis=0,how='any',inplace=True) rowdata.dropna(axis=0,how='any',inplace=True)

View File

@@ -199,10 +199,17 @@ class Rower(models.Model):
underarmourtokenexpirydate = models.DateTimeField(blank=True,null=True) underarmourtokenexpirydate = models.DateTimeField(blank=True,null=True)
underarmourrefreshtoken = models.CharField(default='',max_length=200, underarmourrefreshtoken = models.CharField(default='',max_length=200,
blank=True,null=True) blank=True,null=True)
tptoken = models.CharField(default='',max_length=200,blank=True,null=True)
tptokenexpirydate = models.DateTimeField(blank=True,null=True)
tprefreshtoken = models.CharField(default='',max_length=200,
blank=True,null=True)
stravatoken = models.CharField(default='',max_length=200,blank=True,null=True) stravatoken = models.CharField(default='',max_length=200,blank=True,null=True)
runkeepertoken = models.CharField(default='',max_length=200, runkeepertoken = models.CharField(default='',max_length=200,
blank=True,null=True) blank=True,null=True)
# runkeepertokenexpirydate = models.DateTimeField(blank=True,null=True) # runkeepertokenexpirydate = models.DateTimeField(blank=True,null=True)
# runkeeperrefreshtoken = models.CharField(default='',max_length=200, # runkeeperrefreshtoken = models.CharField(default='',max_length=200,
# blank=True,null=True) # blank=True,null=True)
@@ -365,6 +372,7 @@ class Workout(models.Model):
uploadedtostrava = models.IntegerField(default=0) uploadedtostrava = models.IntegerField(default=0)
uploadedtosporttracks = models.IntegerField(default=0) uploadedtosporttracks = models.IntegerField(default=0)
uploadedtounderarmour = models.IntegerField(default=0) uploadedtounderarmour = models.IntegerField(default=0)
uploadedtotp = models.IntegerField(default=0)
uploadedtorunkeeper = models.IntegerField(default=0) uploadedtorunkeeper = models.IntegerField(default=0)
# empower stuff # empower stuff

View File

@@ -14,6 +14,7 @@ import time
import math import math
from math import sin,cos,atan2,sqrt from math import sin,cos,atan2,sqrt
import os,sys import os,sys
import gzip
# Django # Django
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
@@ -230,6 +231,13 @@ def createstravaworkoutdata(w):
row = rowingdata(filename) row = rowingdata(filename)
tcxfilename = filename[:-4]+'.tcx' tcxfilename = filename[:-4]+'.tcx'
row.exporttotcx(tcxfilename,notes=w.notes) row.exporttotcx(tcxfilename,notes=w.notes)
gzfilename = tcxfilename+'.gz'
with file(tcxfilename,'rb') as inF:
s = inF.read()
with gzip.GzipFile(gzfilename,'wb') as outF:
outF.write(s)
os.remove(tcxfilename)
return gzfilename
except: except:
tcxfilename = 0 tcxfilename = 0
@@ -241,7 +249,7 @@ def handle_stravaexport(f2,workoutname,stravatoken,description=''):
# w = Workout.objects.get(id=workoutid) # w = Workout.objects.get(id=workoutid)
client = stravalib.Client(access_token=stravatoken) client = stravalib.Client(access_token=stravatoken)
act = client.upload_activity(f2,'tcx',name=workoutname) act = client.upload_activity(f2,'tcx.gz',name=workoutname)
try: try:
res = act.wait(poll_interval=5.0,timeout=30) res = act.wait(poll_interval=5.0,timeout=30)
message = 'Workout successfully synchronized to Strava' message = 'Workout successfully synchronized to Strava'

View File

@@ -1,4 +1,4 @@
{% extends "base.html" %} {% extends "basenofilters.html" %}
{% load staticfiles %} {% load staticfiles %}
{% load rowerfilters %} {% load rowerfilters %}

View File

@@ -1,4 +1,4 @@
{% extends "base.html" %} {% extends "basenofilters.html" %}
{% load staticfiles %} {% load staticfiles %}
{% load rowerfilters %} {% load rowerfilters %}

View File

@@ -1,6 +1,5 @@
{% extends "base.html" %} {% extends "basenofilters.html" %}
{% load staticfiles %} {% load staticfiles %}
{% load rowerfilters %}
{% block title %}Change Workout {% endblock %} {% block title %}Change Workout {% endblock %}

View File

@@ -1,6 +1,5 @@
{% extends "base.html" %} {% extends "basenofilters.html" %}
{% load staticfiles %} {% load staticfiles %}
{% load rowerfilters %}
{% block title %}Change Workout {% endblock %} {% block title %}Change Workout {% endblock %}

View File

@@ -1,149 +1,9 @@
{% load cookielaw_tags %} {% extends "basebase.html" %}
{% load analytical %} {% block filters %}
{% load rowerfilters %} {% load rowerfilters %}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> {% endblock %}
<html lang="en">
<head>
<script src="/static/cookielaw/js/cookielaw.js"></script>
{% analytical_head_top %}
{% if GOOGLE_ANALYTICS_PROPERTY_ID %}
{% include "ga.html" %}
{% endif %}
<link rel="stylesheet" href="/static/css/bokeh-0.12.3.min.css" type="text/css" />
<link rel="stylesheet" href="/static/css/bokeh-widgets-0.12.3.min.css" type="text/css" />
<link rel="shortcut icon" href="/static/img/favicon.ico" type="image/x-icon" /> {% block teams %}
<link rel="icon" sizes="32x32" href="/static/img/favicon-32x32.png" type="image/png"/>
<link rel="icon" sizes="64x64" href="/static/img/favicon-64x64.png" type="image/png"/>
<link rel="icon" sizes="192x192" href="/static/img/favicon-192x192.png" type="image/png"/>
<link rel="icon" sizes="16x16" href="/static/img/favicon-16x16.png" type="image/png"/>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=0.67">
<title>Rowsandall</title>
<link rel="stylesheet" href="/static/css/reset.css" />
<link rel="stylesheet" href="/static/css/text.css" />
<link rel="stylesheet" href="/static/css/960_12_col.css" />
<link rel="stylesheet" href="/static/css/rowsandall.css" />
{% block meta %} {% endblock %}
{% analytical_head_bottom %}
</head>
<body>
{% analytical_body_top %}
<div class="container_12">
<div class="grid_12">
&nbsp;
</div>
<div class="grid_12">
<div id="logo" class="grid_6 alpha">
{% if user.rower.rowerplan == 'pro' or user.rower.rowerplan == 'coach' %}
<p><a href="/"><img src="/static/img/logo7.png"
alt="Rowsandall logo" height="80"></a></p>
{% else %}
<p><a href="/"><img src="/static/img/logo7.png"
alt="Rowsandall logo" height="80"></a></p>
{% endif %}
</div>
<div class="grid_6 omega">
<div class="grid_4 alpha">
<div class="grid_1 alpha">
<p id="header">
<a class="button gray small" href="/rowers/videos">Videos</a></p>
</div>
<div class="grid_2">
<p id="header">
<a class="button gray small" href="http://analytics.rowsandall.com/">Rowing Analytics BLOG</a></p>
</div>
<div class="grid_1 omega">
<p id="header">
<a class="button gray small" href="/rowers/email">Contact</a>
</p>
</div>
<div class="grid_4 alpha">
<p>Free Data and Analysis. For Rowers. By Rowers.</p>
</div>
</div>
<div class="grid_1">
<div class="grid_1 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/me/edit">{{ user.first_name }}</a>
</p>
<span class="tooltiptext">Edit user account, e.g. heart rate zones, power zones, email, teams</span>
{% else %}
<p><a class="button gray small" href="{% url 'login' %}">login</a> </p>
{% endif %}
</div>
<div class="grid_1">
{% if user.is_authenticated %}
<p><a class="button gray small" href="{% url 'logout' %}">logout</a></p>
{% else %}
<p>&nbsp</p>
{% endif %}
</div>
</div>
<div class="grid_1 omega">
{% if user.rower.rowerplan == 'pro' or user.rower.rowerplan == 'coach' %}
<h6 class="graytext">Pro Member</h6>
{% else %}
<div class="grid_1"><a class="button green small" href="/rowers/promembership">Upgrade to Pro</a></div>
{% endif %}
</div>
</div>
</div>
<div class="grid_12">
<div class="grid_1 alpha tooltip">
{% if user.is_authenticated %}
<p><a class="button gray small" href="/rowers/workout/upload/">Upload</a></p>
<span class="tooltiptext">Upload CSV, TCX, FIT data files to rowsandall.com</span>
{% else %}
<p><a class="button green small" href="/rowers/register">Register (free)</a></p>
{% endif %}
</div>
<div class="grid_1 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/imports/">Import</a>
</p>
<span class="tooltiptext">Import workouts from Strava, SportTracks, and C2 logbook</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_2 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/list-workouts/">Workouts</a>
</p>
<span class="tooltiptext">See your list of workouts</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_1 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/list-graphs/">Graphs</a>
</p>
<span class="tooltiptext">See your most recent charts</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_2 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/analysis">Analysis</a>
</p>
<span class="tooltiptext">Analysis of workouts over a period of time</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_1 tooltip">
{% if user.is_authenticated and user|has_teams %} {% if user.is_authenticated and user|has_teams %}
<div class="grid_1 alpha dropdown"> <div class="grid_1 alpha dropdown">
<button class="grid_1 alpha button gray small dropbtn"> <button class="grid_1 alpha button gray small dropbtn">
@@ -175,71 +35,8 @@
{% else %} {% else %}
<p>&nbsp;</p> <p>&nbsp;</p>
{% endif %} {% endif %}
</div> {% endblock %}
</div>
{% block content %}
<div class="clear"></div>
<div class="grid_12"> {% endblock %}
{% block message %}
{% if message %}
<p class="message">
{{ message }}
</p>
{% endif %}
{% if successmessage %}
<p class="successmessage">
{{ successmessage }}
</p>
{% endif %}
{% endblock %}
</div>
<div class="grid_12">
{% load tz %}
{% block content %}{% endblock %}
</div>
<div class="clear"></div>
<div class="grid_12 omega" >
{% block footer %}
<p id="footer">{{ versionstring }}</p>
<div class="grid_2 alpha">
<p id="footer"><a href="/rowers/email/">&copy; Sander Roosendaal</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/about">About</a></p>
</div>
<div class="grid_2">
<p id="footer">
<a href="/rowers/developers">Developers</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/legal">Legal</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/partners">Partners</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/physics">Physics</a></p>
</div>
<div class="grid_2">
<p id="footer">
<a href="http://analytics.rowsandall.com/">Rowing Analytics BLOG</a></p>
</div>
<div class="grid_2 omega">
<p id="footer">
<a href="https://www.facebook.com/groups/rowsandall/">Facebook group</a></p>
</div>
{% endblock %}
</div>
{% cookielaw_banner %}
</div>
<!-- end container -->
{% analytical_body_bottom %}
</body>
</html>

View File

@@ -0,0 +1,219 @@
{% load cookielaw_tags %}
{% load analytical %}
{% block filters %}
{% endblock %}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<script src="/static/cookielaw/js/cookielaw.js"></script>
{% analytical_head_top %}
{% if GOOGLE_ANALYTICS_PROPERTY_ID %}
{% include "ga.html" %}
{% endif %}
<link rel="stylesheet" href="/static/css/bokeh-0.12.3.min.css" type="text/css" />
<link rel="stylesheet" href="/static/css/bokeh-widgets-0.12.3.min.css" type="text/css" />
<link rel="shortcut icon" href="/static/img/favicon.ico" type="image/x-icon" />
<link rel="icon" sizes="32x32" href="/static/img/favicon-32x32.png" type="image/png"/>
<link rel="icon" sizes="64x64" href="/static/img/favicon-64x64.png" type="image/png"/>
<link rel="icon" sizes="192x192" href="/static/img/favicon-192x192.png" type="image/png"/>
<link rel="icon" sizes="16x16" href="/static/img/favicon-16x16.png" type="image/png"/>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=0.67">
<title>Rowsandall</title>
<link rel="stylesheet" href="/static/css/reset.css" />
<link rel="stylesheet" href="/static/css/text.css" />
<link rel="stylesheet" href="/static/css/960_12_col.css" />
<link rel="stylesheet" href="/static/css/rowsandall.css" />
{% block meta %} {% endblock %}
{% analytical_head_bottom %}
</head>
<body>
{% analytical_body_top %}
{% block body_top %}{% endblock %}
<div class="container_12">
<div class="grid_12">
&nbsp;
</div>
<div class="grid_12">
<div id="logo" class="grid_6 alpha">
{% if user.rower.rowerplan == 'pro' or user.rower.rowerplan == 'coach' %}
<p><a href="/"><img src="/static/img/logo7.png"
alt="Rowsandall logo" height="80"></a></p>
{% else %}
<p><a href="/"><img src="/static/img/logo7.png"
alt="Rowsandall logo" height="80"></a></p>
{% endif %}
</div>
<div class="grid_6 omega">
<div class="grid_4 alpha">
<div class="grid_1 alpha">
<p id="header">
<a class="button gray small" href="/rowers/videos">Videos</a></p>
</div>
<div class="grid_2">
<p id="header">
<a class="button gray small" href="http://analytics.rowsandall.com/">Rowing Analytics BLOG</a></p>
</div>
<div class="grid_1 omega">
<p id="header">
<a class="button gray small" href="/rowers/email">Contact</a>
</p>
</div>
<div class="grid_4 alpha">
<p>Free Data and Analysis. For Rowers. By Rowers.</p>
</div>
</div>
<div class="grid_1">
<div class="grid_1 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/me/edit">{{ user.first_name }}</a>
</p>
<span class="tooltiptext">Edit user account, e.g. heart rate zones, power zones, email, teams</span>
{% else %}
<p><a class="button gray small" href="{% url 'login' %}">login</a> </p>
{% endif %}
</div>
<div class="grid_1">
{% if user.is_authenticated %}
<p><a class="button gray small" href="{% url 'logout' %}">logout</a></p>
{% else %}
<p>&nbsp</p>
{% endif %}
</div>
</div>
<div class="grid_1 omega">
{% if user.rower.rowerplan == 'pro' or user.rower.rowerplan == 'coach' %}
<h6 class="graytext">Pro Member</h6>
{% else %}
<div class="grid_1"><a class="button green small" href="/rowers/promembership">Upgrade to Pro</a></div>
{% endif %}
</div>
</div>
</div>
<div class="grid_12">
<div class="grid_1 alpha tooltip">
{% if user.is_authenticated %}
<p><a class="button gray small" href="/rowers/workout/upload/">Upload</a></p>
<span class="tooltiptext">Upload CSV, TCX, FIT data files to rowsandall.com</span>
{% else %}
<p><a class="button green small" href="/rowers/register">Register (free)</a></p>
{% endif %}
</div>
<div class="grid_1 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/imports/">Import</a>
</p>
<span class="tooltiptext">Import workouts from Strava, SportTracks, and C2 logbook</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_2 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/list-workouts/">Workouts</a>
</p>
<span class="tooltiptext">See your list of workouts</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_1 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/list-graphs/">Graphs</a>
</p>
<span class="tooltiptext">See your most recent charts</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_2 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/analysis">Analysis</a>
</p>
<span class="tooltiptext">Analysis of workouts over a period of time</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_1 tooltip">
{% block teams %}
{% endblock %}
</div>
</div>
<div class="clear"></div>
<div class="grid_12">
{% block message %}
{% if message %}
<p class="message">
{{ message }}
</p>
{% endif %}
{% if successmessage %}
<p class="successmessage">
{{ successmessage }}
</p>
{% endif %}
{% endblock %}
</div>
<div class="grid_12">
{% load tz %}
{% block content %}{% endblock %}
</div>
<div class="clear"></div>
<div class="grid_12 omega" >
{% block footer %}
<p id="footer">{{ versionstring }}</p>
<div class="grid_2 alpha">
<p id="footer"><a href="/rowers/email/">&copy; Sander Roosendaal</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/about">About</a></p>
</div>
<div class="grid_2">
<p id="footer">
<a href="/rowers/developers">Developers</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/legal">Legal</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/partners">Partners</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/physics">Physics</a></p>
</div>
<div class="grid_2">
<p id="footer">
<a href="http://analytics.rowsandall.com/">Rowing Analytics BLOG</a></p>
</div>
<div class="grid_2 omega">
<p id="footer">
<a href="https://www.facebook.com/groups/rowsandall/">Facebook group</a></p>
</div>
{% endblock %}
</div>
{% cookielaw_banner %}
</div>
<!-- end container -->
{% analytical_body_bottom %}
{% block body_bottom %}{% endblock %}
</body>
</html>

View File

@@ -1,34 +1,12 @@
{% load cookielaw_tags %} {% extends "basebase.html" %}
{% load analytical %} {% block filters %}
{% load rowerfilters %} {% load rowerfilters %}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> {% endblock %}
<html lang="en">
<head>
<script src="/static/cookielaw/js/cookielaw.js"></script>
{% analytical_head_top %}
{% if GOOGLE_ANALYTICS_PROPERTY_ID %}
{% include "ga.html" %}
{% endif %}
<link rel="stylesheet" href="/static/css/bokeh-0.12.3.min.css" type="text/css" />
<link rel="stylesheet" href="/static/css/bokeh-widgets-0.12.3.min.css" type="text/css" />
<link rel="shortcut icon" href="/static/img/favicon.ico" type="image/x-icon" />
<link rel="icon" sizes="32x32" href="/static/img/favicon-32x32.png" type="image/png"/>
<link rel="icon" sizes="64x64" href="/static/img/favicon-64x64.png" type="image/png"/>
<link rel="icon" sizes="192x192" href="/static/img/favicon-192x192.png" type="image/png"/>
<link rel="icon" sizes="16x16" href="/static/img/favicon-16x16.png" type="image/png"/>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=0.67">
<title>Rowsandall</title>
<link rel="stylesheet" href="/static/css/reset.css" />
<link rel="stylesheet" href="/static/css/text.css" />
<link rel="stylesheet" href="/static/css/960_12_col.css" />
<link rel="stylesheet" href="/static/css/rowsandall.css" />
{% block meta %} {% endblock %}
{% analytical_head_bottom %}
</head>
<body>
{% block meta %}
{% endblock %}
{% block body_top %}
<style> <style>
.splash { .splash {
background-color: transparent; background-color: transparent;
@@ -44,129 +22,17 @@
.container_12 {background-color: rgba(255,255,255,0.0);} .container_12 {background-color: rgba(255,255,255,0.0);}
.container_top {background-color: rgba(255,255,255,0.7);} .container_top {background-color: rgba(255,255,255,0.7);}
</style> </style>
<div id="bgpic" class="splash">
{% analytical_body_top %}
<div class="container_top">
<div class="container_12">
<div class="grid_12">
&nbsp;
</div>
<div class="grid_12">
<div id="logo" class="grid_6 alpha">
{% if user.rower.rowerplan == 'pro' or user.rower.rowerplan == 'coach' %}
<p><a href="/"><img src="/static/img/logo7.png"
alt="Rowsandall logo" height="80"></a></p>
{% else %}
<p><a href="/"><img src="/static/img/logo7.png"
alt="Rowsandall logo" height="80"></a></p>
{% endif %}
</div>
<div class="grid_6 omega">
<div class="grid_4 alpha">
<div class="grid_1 alpha">
<p id="header">
<a class="button gray small" href="/rowers/videos">Videos</a></p>
</div>
<div class="grid_2">
<p id="header">
<a class="button gray small" href="http://analytics.rowsandall.com/">Rowing Analytics BLOG</a></p>
</div>
<div class="grid_1 omega">
<p id="header">
<a class="button gray small" href="/rowers/email">Contact</a>
</p>
</div>
<div class="grid_4 alpha">
<p>Free Data and Analysis. For Rowers. By Rowers.</p>
</div>
</div>
<div class="grid_1"> <div id="bgpic" class="splash">
<div class="grid_1 tooltip">
{% if user.is_authenticated %} <div class="container_top">
<p> {% endblock %}
<a class="button gray small" href="/rowers/me/edit">{{ user.first_name }}</a>
</p> {% block teams %}
<span class="tooltiptext">Edit user account, e.g. heart rate zones, power zones, email, teams</span> {% if user.is_authenticated and user|has_teams %}
{% else %}
<p><a class="button gray small" href="{% url 'login' %}">login</a> </p>
{% endif %}
</div>
<div class="grid_1">
{% if user.is_authenticated %}
<p><a class="button gray small" href="{% url 'logout' %}">logout</a></p>
{% else %}
<p>&nbsp</p>
{% endif %}
</div>
</div>
<div class="grid_1 omega">
{% if user.rower.rowerplan == 'pro' or user.rower.rowerplan == 'coach' %}
<h6 class="graytext">Pro Member</h6>
{% else %}
<div class="grid_1"><a class="button green small" href="/rowers/promembership">Upgrade to Pro</a></div>
{% endif %}
</div>
</div>
</div>
<div class="grid_12">
<div class="grid_1 alpha tooltip">
{% if user.is_authenticated %}
<p><a class="button gray small" href="/rowers/workout/upload/">Upload</a></p>
<span class="tooltiptext">Upload CSV, TCX, FIT data files to rowsandall.com</span>
{% else %}
<p><a class="button green small" href="/rowers/register">Register (free)</a></p>
{% endif %}
</div>
<div class="grid_1 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/imports/">Import</a>
</p>
<span class="tooltiptext">Import workouts from Strava, SportTracks, and C2 logbook</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_2 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/list-workouts/">Workouts</a>
</p>
<span class="tooltiptext">See your list of workouts</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_1 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/list-graphs/">Graphs</a>
</p>
<span class="tooltiptext">See your most recent charts</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_2 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/analysis">Analysis</a>
</p>
<span class="tooltiptext">Analysis of workouts over a period of time</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_1 tooltip">
{% if user.is_authenticated and user|user_teams %}
<div class="grid_1 alpha dropdown"> <div class="grid_1 alpha dropdown">
<button class="grid_1 alpha button gray small dropbtn"> <button class="grid_1 alpha button gray small dropbtn">
Teams Teams
</button> </button>
<div class="dropdown-content"> <div class="dropdown-content">
<a class="button gray small" href="/rowers/me/teams/">Manage Teams</a> <a class="button gray small" href="/rowers/me/teams/">Manage Teams</a>
@@ -194,73 +60,11 @@
{% else %} {% else %}
<p>&nbsp;</p> <p>&nbsp;</p>
{% endif %} {% endif %}
</div> {% endblock %}
</div>
<div class="clear"></div> {% block body_bottom %}
<div class="grid_12">
{% block message %}
{% if message %}
<p class="message">
{{ message }}
</p>
{% endif %}
{% if successmessage %}
<p class="successmessage">
{{ successmessage }}
</p>
{% endif %}
{% endblock %}
</div> </div>
<div class="grid_12">
{% load tz %}
{% block content %}{% endblock %}
</div>
<div class="clear"></div>
<div class="grid_12 omega" >
{% block footer %}
<p id="footer">{{ versionstring }}</p>
<div class="grid_2 alpha">
<p id="footer"><a href="/rowers/email/">&copy; Sander Roosendaal</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/about">About</a></p>
</div>
<div class="grid_2">
<p id="footer">
<a href="/rowers/developers">Developers</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/legal">Legal</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/partners">Partners</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/physics">Physics</a></p>
</div>
<div class="grid_2">
<p id="footer">
<a href="http://analytics.rowsandall.com/">Rowing Analytics BLOG</a></p>
</div>
<div class="grid_2 omega">
<p id="footer">
<a href="https://www.facebook.com/groups/rowsandall/">Facebook group</a></p>
</div>
{% endblock %}
</div>
{% cookielaw_banner %}
</div>
</div>
<!-- end container -->
{% analytical_body_bottom %}
<script type="text/javascript"> <script type="text/javascript">
var num = (Math.floor(Math.random()*4)); var num = (Math.floor(Math.random()*4));
@@ -272,5 +76,5 @@
elem.classList.add(array[num]); elem.classList.add(array[num]);
</script> </script>
</body> {% endblock %}
</html>

View File

@@ -0,0 +1,26 @@
{% extends "basebase.html" %}
{% block filters %}
{% load rowerfilters %}
{% endblock %}
{% block teams %}
{% if user.is_authenticated and user.rower.team.all %}
<div class="grid_1 alpha dropdown">
<button class="grid_1 alpha button gray small dropbtn">
Teams
</button>
<div class="dropdown-content">
{% for t in user.rower.team.all %}
<a class="button gray small" href="/rowers/list-workouts/team/{{ t.id }}/">{{ t.name }}</a>
{% endfor %}
</div>
</div>
<span class="tooltiptext">See recent workouts for your team</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
{% endblock %}
{% block content %}
{% endblock %}

View File

@@ -128,6 +128,24 @@
<img src="/static/img/uachecked.png" alt="Underarmour icon" width="60" height="60"></a> <img src="/static/img/uachecked.png" alt="Underarmour icon" width="60" height="60"></a>
</div> </div>
{% endif %} {% endif %}
{% if workout.uploadedtotp == 0 %}
{% if user.rower.tptoken == None or user.rower.tptoken == '' %}
<div class="grid_1">
<a href="/rowers/me/tpauthorize">
<img src="/static/img/tpgray.png" alt="TrainingPeaks icon" width="60" height="60"></a>
</div>
{% else %}
<div class="grid_1">
<a href="/rowers/workout/{{ workout.id }}/tpuploadw"><img src="/static/img/tpicon.png" alt="Tp icon" width="60" height="60"></a>
</div>
{% endif %}
{% else %}
<div class="grid_1">
<a href="https://app.sandbox.trainingpeaks.com">
<img src="/static/img/tpchecked.png" alt="TrainingPeaks icon" width="60" height="60"></a>
</div>
{% endif %}
</div> </div>
@@ -162,6 +180,9 @@
<div class="grid_2"> <div class="grid_2">
<p><a href="/rowers/me/underarmourauthorize/"><img src="/static/img/UAbtn.png" alt="connect with Under Armour" width="120"></a></p> <p><a href="/rowers/me/underarmourauthorize/"><img src="/static/img/UAbtn.png" alt="connect with Under Armour" width="120"></a></p>
</div> </div>
<div class="grid_2 omega">
<p><a href="/rowers/me/tpauthorize/"><img src="/static/img/TP_logo_horz_2_color.png" alt="connect with TrainingPeaks" width="150"></a></p>
</div>
</div> </div>
</div> </div>

251
rowers/tpstuff.py Normal file
View File

@@ -0,0 +1,251 @@
# All the functionality needed to connect to Runkeeper
# Python
import oauth2 as oauth
import cgi
import requests
import requests.auth
import json
from django.utils import timezone
from datetime import datetime
import numpy as np
from dateutil import parser
import time
import math
import gzip
from math import sin,cos,atan2,sqrt
import os,sys
import urllib
import base64
from io import BytesIO
# Django
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect, HttpResponse,JsonResponse
from django.conf import settings
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
# Project
# from .models import Profile
from rowingdata import rowingdata
import pandas as pd
from rowers.models import Rower,Workout
from rowsandall_app.settings import (
C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET,
STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET,
TP_CLIENT_ID, TP_CLIENT_SECRET,
TP_REDIRECT_URI,TP_CLIENT_KEY,
)
tpapilocation = "https://api.sandbox.trainingpeaks.com"
# Custom error class - to raise a NoTokenError
class TPNoTokenError(Exception):
def __init__(self,value):
self.value=value
def __str__(self):
return repr(self.value)
# Exponentially weighted moving average
# Used for data smoothing of the jagged data obtained by Strava
# See bitbucket issue 72
def ewmovingaverage(interval,window_size):
# Experimental code using Exponential Weighted moving average
try:
intervaldf = pd.DataFrame({'v':interval})
idf_ewma1 = intervaldf.ewm(span=window_size)
idf_ewma2 = intervaldf[::-1].ewm(span=window_size)
i_ewma1 = idf_ewma1.mean().ix[:,'v']
i_ewma2 = idf_ewma2.mean().ix[:,'v']
interval2 = np.vstack((i_ewma1,i_ewma2[::-1]))
interval2 = np.mean( interval2, axis=0) # average
except ValueError:
interval2 = interval
return interval2
from utils import geo_distance
# Custom exception handler, returns a 401 HTTP message
# with exception details in the json data
def custom_exception_handler(exc,message):
response = {
"errors": [
{
"code": str(exc),
"detail": message,
}
]
}
res = HttpResponse(message)
res.status_code = 401
res.json = json.dumps(response)
return res
# Refresh ST token using refresh token
def do_refresh_token(refreshtoken):
client_auth = requests.auth.HTTPBasicAuth(TP_CLIENT_KEY, TP_CLIENT_SECRET)
post_data = {"grant_type": "refresh_token",
"client_secret": TP_CLIENT_SECRET,
"client_id":TP_CLIENT_KEY,
"refresh_token": refreshtoken,
}
headers = {'user-agent': 'sanderroosendaal',
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
}
url = "https://oauth.sandbox.trainingpeaks.com/oauth/token"
response = requests.post(url,
data=post_data,
headers=headers)
token_json = response.json()
thetoken = token_json['access_token']
expires_in = token_json['expires_in']
try:
refresh_token = token_json['refresh_token']
except KeyError:
refresh_token = refreshtoken
return [thetoken,expires_in,refresh_token]
# Exchange access code for long-lived access token
def get_token(code):
client_auth = requests.auth.HTTPBasicAuth(TP_CLIENT_KEY, TP_CLIENT_SECRET)
post_data = {
"client_id":TP_CLIENT_KEY,
"grant_type": "authorization_code",
"code": code,
"redirect_uri":TP_REDIRECT_URI,
"client_secret": TP_CLIENT_SECRET,
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
}
response = requests.post("https://oauth.sandbox.trainingpeaks.com/oauth/token",
data=post_data)
try:
token_json = response.json()
thetoken = token_json['access_token']
expires_in = token_json['expires_in']
refresh_token = token_json['refresh_token']
except KeyError:
thetoken = 0
return thetoken,expires_in,refresh_token
# Make authorization URL including random string
def make_authorization_url(request):
# Generate a random string for the state parameter
# Save it for use later to prevent xsrf attacks
from uuid import uuid4
state = str(uuid4())
params = {"client_id": TP_CLIENT_KEY,
"response_type": "code",
"redirect_uri": TP_REDIRECT_URI,
"scope": "file:write",
}
url = "https://oauth.sandbox.trainingpeaks.com/oauth/authorize?" +urllib.urlencode(params)
return HttpResponseRedirect(url)
def getidfromresponse(response):
t = json.loads(response.text)
links = t["_links"]
id = links["self"][0]["id"]
return int(id)
def createtpworkoutdata(w):
filename = w.csvfilename
try:
row = rowingdata(filename)
tcxfilename = filename[:-4]+'.tcx'
row.exporttotcx(tcxfilename,notes=w.notes)
# with file(tcxfilename,'rb') as inF:
# s = inF.read()
# with gzip.GzipFile(tcxfilename+'.gz','wb') as outF:
# outF.write(s)
return tcxfilename
except:
tcxfilename = 0
return tcxfilename
def tp_check(access_token):
headers = {
"Content-Type": "application/json",
'Accept': 'application/json',
'authorization': 'Bearer %s' % access_token
}
resp = requests.post(tpapilocation+"/v1/info/version",
headers=headers)
return resp
def uploadactivity(access_token,filename,description='',
name='Rowsandall.com workout'):
data_gz = BytesIO()
with file(filename,'rb') as inF:
s = inF.read()
with gzip.GzipFile(fileobj=data_gz,mode="w") as gzf:
gzf.write(s)
headers = {
"Content-Type": "application/json",
'Accept': 'application/json',
'authorization': 'Bearer %s' % access_token
}
data = {
"UploadClient": "rowsandall",
"Filename": filename,
"SetWorkoutPublic": True,
"Title":name,
"Type": "rowing",
"Comment": description,
"Data": base64.b64encode(data_gz.getvalue()).decode("ascii")
}
resp = requests.post(tpapilocation+"/v1/file",
data = json.dumps(data),
headers=headers)
if resp.status_code != 200:
print resp.status_code
print resp.reason
print ""
print headers
print ""
return 0
else:
return resp.json()[0]["Id"]
return 0

View File

@@ -142,6 +142,8 @@ def get_token(code):
refresh_token = token_json['refresh_token'] refresh_token = token_json['refresh_token']
except KeyError: except KeyError:
thetoken = 0 thetoken = 0
expires_in = 30
refresh_token = ''
return thetoken,expires_in,refresh_token return thetoken,expires_in,refresh_token

View File

@@ -110,6 +110,7 @@ urlpatterns = [
url(r'^api-docs$', views.schema_view), url(r'^api-docs$', views.schema_view),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')), url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^api/workouts/(?P<id>\d+)/strokedata$',views.strokedatajson), url(r'^api/workouts/(?P<id>\d+)/strokedata$',views.strokedatajson),
url(r'^500v/$',views.error500_view),
url(r'^500/$', TemplateView.as_view(template_name='500.html'),name='500'), url(r'^500/$', TemplateView.as_view(template_name='500.html'),name='500'),
url(r'^404/$', TemplateView.as_view(template_name='404.html'),name='404'), url(r'^404/$', TemplateView.as_view(template_name='404.html'),name='404'),
url(r'^400/$', TemplateView.as_view(template_name='400.html'),name='400'), url(r'^400/$', TemplateView.as_view(template_name='400.html'),name='400'),
@@ -238,6 +239,7 @@ urlpatterns = [
url(r'^workout/(\d+)/sporttracksuploadw/$',views.workout_sporttracks_upload_view), url(r'^workout/(\d+)/sporttracksuploadw/$',views.workout_sporttracks_upload_view),
url(r'^workout/(\d+)/runkeeperuploadw/$',views.workout_runkeeper_upload_view), url(r'^workout/(\d+)/runkeeperuploadw/$',views.workout_runkeeper_upload_view),
url(r'^workout/(\d+)/underarmouruploadw/$',views.workout_underarmour_upload_view), url(r'^workout/(\d+)/underarmouruploadw/$',views.workout_underarmour_upload_view),
url(r'^workout/(\d+)/tpuploadw/$',views.workout_tp_upload_view),
url(r'^multi-compare$',views.multi_compare_view), url(r'^multi-compare$',views.multi_compare_view),
url(r'^me/teams/c/(?P<message>\w+.*)/s/(?P<successmessage>\w+.*)$',views.rower_teams_view), url(r'^me/teams/c/(?P<message>\w+.*)/s/(?P<successmessage>\w+.*)$',views.rower_teams_view),
url(r'^me/teams/s/(?P<successmessage>\w+.*)$',views.rower_teams_view), url(r'^me/teams/s/(?P<successmessage>\w+.*)$',views.rower_teams_view),
@@ -273,9 +275,11 @@ urlpatterns = [
url(r'^me/stravaauthorize/$',views.rower_strava_authorize), url(r'^me/stravaauthorize/$',views.rower_strava_authorize),
url(r'^me/sporttracksauthorize/$',views.rower_sporttracks_authorize), url(r'^me/sporttracksauthorize/$',views.rower_sporttracks_authorize),
url(r'^me/underarmourauthorize/$',views.rower_underarmour_authorize), url(r'^me/underarmourauthorize/$',views.rower_underarmour_authorize),
url(r'^me/tpauthorize/$',views.rower_tp_authorize),
url(r'^me/runkeeperauthorize/$',views.rower_runkeeper_authorize), url(r'^me/runkeeperauthorize/$',views.rower_runkeeper_authorize),
url(r'^me/sporttracksrefresh/$',views.rower_sporttracks_token_refresh), url(r'^me/sporttracksrefresh/$',views.rower_sporttracks_token_refresh),
url(r'^me/underarmourrefresh/$',views.rower_underarmour_token_refresh), url(r'^me/underarmourrefresh/$',views.rower_underarmour_token_refresh),
url(r'^me/tprefresh/$',views.rower_tp_token_refresh),
url(r'^me/c2refresh/$',views.rower_c2_token_refresh), url(r'^me/c2refresh/$',views.rower_c2_token_refresh),
url(r'^me/favoritecharts/$',views.rower_favoritecharts_view), url(r'^me/favoritecharts/$',views.rower_favoritecharts_view),
url(r'^email/send/$', views.sendmail), url(r'^email/send/$', views.sendmail),

View File

@@ -3,6 +3,7 @@ import timestring
import zipfile import zipfile
import operator import operator
import warnings import warnings
import urllib
from numbers import Number from numbers import Number
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
from django.db.models import Q from django.db.models import Q
@@ -53,10 +54,12 @@ import c2stuff
from c2stuff import C2NoTokenError from c2stuff import C2NoTokenError
from runkeeperstuff import RunKeeperNoTokenError from runkeeperstuff import RunKeeperNoTokenError
from sporttracksstuff import SportTracksNoTokenError from sporttracksstuff import SportTracksNoTokenError
from tpstuff import TPNoTokenError
from iso8601 import ParseError from iso8601 import ParseError
import stravastuff import stravastuff
import sporttracksstuff import sporttracksstuff
import underarmourstuff import underarmourstuff
import tpstuff
import runkeeperstuff import runkeeperstuff
import ownapistuff import ownapistuff
from ownapistuff import TEST_CLIENT_ID, TEST_CLIENT_SECRET, TEST_REDIRECT_URI from ownapistuff import TEST_CLIENT_ID, TEST_CLIENT_SECRET, TEST_REDIRECT_URI
@@ -68,6 +71,7 @@ from rowsandall_app.settings import (
UNDERARMOUR_CLIENT_ID, UNDERARMOUR_REDIRECT_URI, UNDERARMOUR_CLIENT_ID, UNDERARMOUR_REDIRECT_URI,
UNDERARMOUR_CLIENT_SECRET,UNDERARMOUR_CLIENT_KEY, UNDERARMOUR_CLIENT_SECRET,UNDERARMOUR_CLIENT_KEY,
RUNKEEPER_CLIENT_ID,RUNKEEPER_REDIRECT_URI,RUNKEEPER_CLIENT_SECRET, RUNKEEPER_CLIENT_ID,RUNKEEPER_REDIRECT_URI,RUNKEEPER_CLIENT_SECRET,
TP_CLIENT_ID,TP_REDIRECT_URI,TP_CLIENT_KEY,TP_CLIENT_SECRET,
) )
import requests import requests
@@ -260,8 +264,12 @@ from utils import geo_distance,serialize_list,deserialize_list
# Check if a user is a Coach member # Check if a user is a Coach member
def iscoachmember(user): def iscoachmember(user):
r = Rower.objects.get(user=user) if not user.is_anonymous():
result = user.is_authenticated() and (r.rowerplan=='coach') r = Rower.objects.get(user=user)
result = user.is_authenticated() and (r.rowerplan=='coach')
else:
result = False
return result return result
# Check if a user is a Pro member # Check if a user is a Pro member
@@ -1071,6 +1079,26 @@ def underarmour_open(user):
return thetoken return thetoken
# Checks if user has UnderArmour token, renews them if they are expired
def tp_open(user):
r = Rower.objects.get(user=user)
if (r.tptoken == '') or (r.tptoken is None):
s = "Token doesn't exist. Need to authorize"
raise TPNoTokenError("User has no token")
else:
if (timezone.now()>r.tptokenexpirydate):
res = tpstuff.do_refresh_token(r.tprefreshtoken)
r.tptoken = res[0]
r.tprefreshtoken = res[2]
expirydatetime = timezone.now()+datetime.timedelta(seconds=res[1])
r.tptokenexpirydate = expirydatetime
r.save()
thetoken = r.tptoken
else:
thetoken = r.tptoken
return thetoken
# Checks if user has SportTracks token, renews them if they are expired # Checks if user has SportTracks token, renews them if they are expired
def runkeeper_open(user): def runkeeper_open(user):
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
@@ -1186,6 +1214,73 @@ def workout_csvemail_view(request,id=0):
return response return response
# Send workout to TP
@login_required()
def workout_tp_upload_view(request,id=0):
message = ""
r = Rower.objects.get(user=request.user)
res = -1
try:
thetoken = tp_open(r.user)
except TPNoTokenError:
return HttpResponseRedirect("/rowers/me/tpauthorize/")
# ready to upload. Hurray
try:
w = Workout.objects.get(id=id)
r = w.user
except Workout.DoesNotExist:
raise Http404("Workout doesn't exist")
if (checkworkoutuser(request.user,w)):
tcxfile = tpstuff.createtpworkoutdata(w)
if tcxfile:
res = tpstuff.uploadactivity(r.tptoken,tcxfile,
name=w.name)
if res == 0:
message = "Upload to TrainingPeaks failed"
w.uploadedtotp = -1
w.save()
try:
os.remove(tcxfile)
except WindowsError:
pass
url = reverse(workout_export_view,
kwargs = {
'id':str(w.id),
'message':message,
})
else: # res != 0
w.uploadedtotp = res
w.save()
os.remove(tcxfile)
url = reverse(workout_export_view,
kwargs = {
'id':str(w.id),
'successmessage':'Uploaded to TP',
})
else: # no tcxfile
message = "Upload to TrainingPeaks failed"
w.uploadedtotp = -1
w.save()
url = reverse(workout_export_view,
kwargs = {
'id':str(w.id),
'message':message,
})
else: # not allowed to upload
message = "You are not allowed to export this workout to TP"
url = reverse(workout_export_view,
kwargs = {
'id':str(w.id),
'message':message,
})
return HttpResponseRedirect(url)
# Send workout to Strava # Send workout to Strava
# abundance of error logging here because there were/are some bugs # abundance of error logging here because there were/are some bugs
@login_required() @login_required()
@@ -1315,7 +1410,6 @@ def workout_c2_upload_view(request,id=0):
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'} 'Content-Type': 'application/json'}
import urllib
try: try:
url = "https://log.concept2.com/api/users/%s/results" % (c2userid) url = "https://log.concept2.com/api/users/%s/results" % (c2userid)
response = requests.post(url,headers=headers,data=json.dumps(data)) response = requests.post(url,headers=headers,data=json.dumps(data))
@@ -1400,7 +1494,6 @@ def workout_runkeeper_upload_view(request,id=0):
'Content-Type': 'application/vnd.com.runkeeper.NewFitnessActivity+json', 'Content-Type': 'application/vnd.com.runkeeper.NewFitnessActivity+json',
'Content-Length':'nnn'} 'Content-Length':'nnn'}
import urllib
url = "https://api.runkeeper.com/fitnessActivities" url = "https://api.runkeeper.com/fitnessActivities"
response = requests.post(url,headers=headers,data=json.dumps(data)) response = requests.post(url,headers=headers,data=json.dumps(data))
@@ -1466,7 +1559,6 @@ def workout_underarmour_upload_view(request,id=0):
'Content-Type': 'application/json', 'Content-Type': 'application/json',
} }
import urllib
url = "https://api.ua.com/v7.1/workout/" url = "https://api.ua.com/v7.1/workout/"
response = requests.post(url,headers=headers,data=json.dumps(data)) response = requests.post(url,headers=headers,data=json.dumps(data))
@@ -1529,7 +1621,6 @@ def workout_sporttracks_upload_view(request,id=0):
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'} 'Content-Type': 'application/json'}
import urllib
url = "https://api.sporttracks.mobi/api/v2/fitnessActivities.json" url = "https://api.sporttracks.mobi/api/v2/fitnessActivities.json"
response = requests.post(url,headers=headers,data=json.dumps(data)) response = requests.post(url,headers=headers,data=json.dumps(data))
@@ -1571,7 +1662,6 @@ def rower_c2_authorize(request):
params = {"client_id": C2_CLIENT_ID, params = {"client_id": C2_CLIENT_ID,
"response_type": "code", "response_type": "code",
"redirect_uri": C2_REDIRECT_URI} "redirect_uri": C2_REDIRECT_URI}
import urllib
url = "http://log.concept2.com/oauth/authorize?"+ urllib.urlencode(params) url = "http://log.concept2.com/oauth/authorize?"+ urllib.urlencode(params)
url += "&scope="+scope url += "&scope="+scope
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
@@ -1589,7 +1679,6 @@ def rower_strava_authorize(request):
"redirect_uri": STRAVA_REDIRECT_URI, "redirect_uri": STRAVA_REDIRECT_URI,
"scope": "write"} "scope": "write"}
import urllib
url = "https://www.strava.com/oauth/authorize?"+ urllib.urlencode(params) url = "https://www.strava.com/oauth/authorize?"+ urllib.urlencode(params)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
@@ -1607,7 +1696,6 @@ def rower_runkeeper_authorize(request):
"state": state, "state": state,
"redirect_uri": RUNKEEPER_REDIRECT_URI} "redirect_uri": RUNKEEPER_REDIRECT_URI}
import urllib
url = "https://runkeeper.com/apps/authorize?"+ urllib.urlencode(params) url = "https://runkeeper.com/apps/authorize?"+ urllib.urlencode(params)
@@ -1626,7 +1714,6 @@ def rower_sporttracks_authorize(request):
"state": state, "state": state,
"redirect_uri": SPORTTRACKS_REDIRECT_URI} "redirect_uri": SPORTTRACKS_REDIRECT_URI}
import urllib
url = "https://api.sporttracks.mobi/oauth2/authorize?"+ urllib.urlencode(params) url = "https://api.sporttracks.mobi/oauth2/authorize?"+ urllib.urlencode(params)
@@ -1649,6 +1736,23 @@ def rower_underarmour_authorize(request):
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
# Underarmour Authorization
@login_required()
def rower_tp_authorize(request):
# Generate a random string for the state parameter
# Save it for use later to prevent xsrf attacks
from uuid import uuid4
state = str(uuid4())
params = {"client_id": TP_CLIENT_KEY,
"response_type": "code",
"redirect_uri": TP_REDIRECT_URI,
"scope": "file:write",
}
url = "https://oauth.sandbox.trainingpeaks.com/oauth/authorize/?" +urllib.urlencode(params)
return HttpResponseRedirect(url)
# Concept2 token refresh. URL for manual refresh. Not visible to users # Concept2 token refresh. URL for manual refresh. Not visible to users
@login_required() @login_required()
def rower_c2_token_refresh(request): def rower_c2_token_refresh(request):
@@ -1698,6 +1802,30 @@ def rower_underarmour_token_refresh(request):
return imports_view(request,successmessage=successmessage) return imports_view(request,successmessage=successmessage)
# TrainingPeaks token refresh. URL for manual refresh. Not visible to users
@login_required()
def rower_tp_token_refresh(request):
r = Rower.objects.get(user=request.user)
res = tpstuff.do_refresh_token(
r.tprefreshtoken,
r.tptoken
)
access_token = res[0]
expires_in = res[1]
refresh_token = res[2]
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
r = Rower.objects.get(user=request.user)
r.tptoken = access_token
r.tptokenexpirydate = expirydatetime
r.tprefreshtoken = refresh_token
r.save()
successmessage = "Tokens refreshed. Good to go"
return imports_view(request,successmessage=successmessage)
# SportTracks token refresh. URL for manual refresh. Not visible to users # SportTracks token refresh. URL for manual refresh. Not visible to users
@login_required() @login_required()
def rower_sporttracks_token_refresh(request): def rower_sporttracks_token_refresh(request):
@@ -1859,6 +1987,28 @@ def rower_process_underarmourcallback(request):
successmessage = "Tokens stored. Good to go" successmessage = "Tokens stored. Good to go"
return imports_view(request,successmessage=successmessage) return imports_view(request,successmessage=successmessage)
# Process TrainingPeaks callback
@login_required()
def rower_process_tpcallback(request):
code = request.GET['code']
res = tpstuff.get_token(code)
access_token = res[0]
expires_in = res[1]
refresh_token = res[2]
expirydatetime = timezone.now()+datetime.timedelta(seconds=expires_in)
r = Rower.objects.get(user=request.user)
r.tptoken = access_token
r.tptokenexpirydate = expirydatetime
r.tprefreshtoken = refresh_token
r.save()
successmessage = "Tokens stored. Good to go"
return imports_view(request,successmessage=successmessage)
# Process Own API callback - for API testing purposes # Process Own API callback - for API testing purposes
@login_required() @login_required()
def rower_process_testcallback(request): def rower_process_testcallback(request):
@@ -4198,14 +4348,15 @@ def workout_flexchart3_view(request,*args,**kwargs):
workstrokesonly = False workstrokesonly = False
if request.method == 'POST' and 'savefavorite' in request.POST: if request.method == 'POST' and 'savefavorite' in request.POST:
workstrokesonly = request.POST['workstrokesonlysave'] if not request.user.is_anonymous():
reststrokes = not workstrokesonly workstrokesonly = request.POST['workstrokesonlysave']
r = Rower.objects.get(user=request.user) reststrokes = not workstrokesonly
f = FavoriteChart(user=r,xparam=xparam, r = Rower.objects.get(user=request.user)
yparam1=yparam1,yparam2=yparam2, f = FavoriteChart(user=r,xparam=xparam,
plottype=plottype,workouttype=workouttype, yparam1=yparam1,yparam2=yparam2,
reststrokes=reststrokes) plottype=plottype,workouttype=workouttype,
f.save() reststrokes=reststrokes)
f.save()
if request.method == 'POST' and 'workstrokesonly' in request.POST: if request.method == 'POST' and 'workstrokesonly' in request.POST:
workstrokesonly = request.POST['workstrokesonly'] workstrokesonly = request.POST['workstrokesonly']
@@ -5715,7 +5866,7 @@ def workout_upload_view(request,message="",
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'} 'Content-Type': 'application/json'}
import urllib
url = "https://log.concept2.com/api/users/%s/results" % (c2userid) url = "https://log.concept2.com/api/users/%s/results" % (c2userid)
response = requests.post(url,headers=headers,data=json.dumps(data)) response = requests.post(url,headers=headers,data=json.dumps(data))

View File

@@ -242,6 +242,13 @@ UNDERARMOUR_CLIENT_KEY = CFG['underarmour_client_key']
UNDERARMOUR_REDIRECT_URI = "http://rowsandall.com/underarmour_callback" UNDERARMOUR_REDIRECT_URI = "http://rowsandall.com/underarmour_callback"
#UNDERARMOUR_REDIRECT_URI = "http://localhost:8000/underarmour_callback" #UNDERARMOUR_REDIRECT_URI = "http://localhost:8000/underarmour_callback"
# TrainingPeaks
TP_CLIENT_ID = CFG["tp_client_id"]
TP_CLIENT_SECRET = CFG["tp_client_secret"]
TP_REDIRECT_URI = "http://rowsandall.com/tp_callback"
#TP_REDIRECT_URI = "http://localhost:8000/tp_callback"
TP_CLIENT_KEY = TP_CLIENT_ID
# RQ stuff # RQ stuff
RQ_QUEUES = { RQ_QUEUES = {

View File

@@ -45,8 +45,9 @@ CELERY_SEND_TASK_SENT_EVENT = True
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True DEBUG = True
TEMPLATE_DEBUG = DEBUG
ALLOWED_HOSTS = [] ALLOWED_HOSTS = ['localhost']
# Application definition # Application definition

View File

@@ -21,6 +21,16 @@ from rowsandall_app.views import rootview
from django.contrib.auth import views as auth_views from django.contrib.auth import views as auth_views
from rowers import views as rowersviews from rowers import views as rowersviews
from django.conf.urls import (
handler400, handler403, handler404, handler500
)
handler400 = 'rowers.views.error400_view'
handler403 = 'rowers.views.error403_view'
handler404 = 'rowers.views.error404_view'
handler500 = 'rowers.views.error500_view'
urlpatterns = [ urlpatterns = [
url(r'^password_change_done/$',auth_views.password_change_done,name='password_change_done'), url(r'^password_change_done/$',auth_views.password_change_done,name='password_change_done'),
url(r'^password_change/$',auth_views.password_change), url(r'^password_change/$',auth_views.password_change),
@@ -57,6 +67,7 @@ urlpatterns += [
url(r'^sporttracks\_callback',rowersviews.rower_process_sporttrackscallback), url(r'^sporttracks\_callback',rowersviews.rower_process_sporttrackscallback),
url(r'^underarmour\_callback',rowersviews.rower_process_underarmourcallback), url(r'^underarmour\_callback',rowersviews.rower_process_underarmourcallback),
url(r'^runkeeper\_callback',rowersviews.rower_process_runkeepercallback), url(r'^runkeeper\_callback',rowersviews.rower_process_runkeepercallback),
url(r'^tp\_callback',rowersviews.rower_process_tpcallback),
url(r'^twitter\_callback',rowersviews.rower_process_twittercallback), url(r'^twitter\_callback',rowersviews.rower_process_twittercallback),
url(r'^i18n/', include('django.conf.urls.i18n')), url(r'^i18n/', include('django.conf.urls.i18n')),
] ]

View File

@@ -1,4 +1,5 @@
from django.shortcuts import render, redirect, render_to_response from django.shortcuts import render, redirect, render_to_response
from django.template import RequestContext
from django.conf import settings from django.conf import settings
from rowingdata import main as rmain from rowingdata import main as rmain

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

BIN
static/img/tpchecked.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
static/img/tpgray.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
static/img/tpicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

16
templates/400.html Normal file
View File

@@ -0,0 +1,16 @@
{% extends "basenofilters.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}Change Workout {% endblock %}
{% block content %}
<div class="grid_12">
<h1>Bad Request</h1>
<p>
HTTP Error 400 Bad Request.
</p>
</div>
{% endblock %}

17
templates/403.html Normal file
View File

@@ -0,0 +1,17 @@
{% extends "basenofilters.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}Change Workout {% endblock %}
{% block content %}
<div class="grid_12">
<h1>Forbidden</h1>
<p>
Access forbidden. You probably tried to access functionality on a workout
or chart that is not owned by you.
</p>
</div>
{% endblock %}

15
templates/404.html Normal file
View File

@@ -0,0 +1,15 @@
{% extends "basenofilters.html" %}
{% load staticfiles %}
{% block title %}Change Workout {% endblock %}
{% block content %}
<div class="grid_12">
<h1>Error 404 Page not found</h1>
<p>
We could not find the page on our server.
</p>
</div>
{% endblock %}

21
templates/500.html Normal file
View File

@@ -0,0 +1,21 @@
{% extends "basenofilters.html" %}
{% load staticfiles %}
{% block title %}Change Workout {% endblock %}
{% block content %}
<div class="grid_12">
<h1>Error 500 Internal Server Error</h1>
<p>
The site reported an internal server error. The site developer has been
notified automatically with a full error report. You can help the developer
by reporting an issue on Bitbucket using the button below.
</p>
<div class="grid_2 alpha">
<a class="button red small" href="https://bitbucket.org/sanderroosendaal/rowsandall/issues/new">Report an issue</a>
</div>
</div>
{% endblock %}

42
templates/base.html Normal file
View File

@@ -0,0 +1,42 @@
{% extends "basebase.html" %}
{% block filters %}
{% load rowerfilters %}
{% endblock %}
{% block teams %}
{% if user.is_authenticated and user|has_teams %}
<div class="grid_1 alpha dropdown">
<button class="grid_1 alpha button gray small dropbtn">
Teams
</button>
<div class="dropdown-content">
<a class="button gray small" href="/rowers/me/teams/">Manage Teams</a>
{% if user|is_manager %}
<a class="button gray small" href="/rowers/workout/upload/team/">Upload Team Member Workout</a>
{% endif %}
{% for t in user|user_teams %}
<a class="button gray small" href="/rowers/list-workouts/team/{{ t.id }}/">{{ t.name }}</a>
{% endfor %}
</div>
</div>
<span class="tooltiptext">See recent workouts for your team</span>
{% elif user.is_authenticated and user.rower.team.all %}
<div class="grid_1 alpha dropdown">
<button class="grid_1 alpha button gray small dropbtn">
Teams
</button>
<div class="dropdown-content">
{% for t in user.rower.team.all %}
<a class="button gray small" href="/rowers/list-workouts/team/{{ t.id }}/">{{ t.name }}</a>
{% endfor %}
</div>
</div>
<span class="tooltiptext">See recent workouts for your team</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
{% endblock %}
{% block content %}
{% endblock %}

219
templates/basebase.html Normal file
View File

@@ -0,0 +1,219 @@
{% load cookielaw_tags %}
{% load analytical %}
{% block filters %}
{% endblock %}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<script src="/static/cookielaw/js/cookielaw.js"></script>
{% analytical_head_top %}
{% if GOOGLE_ANALYTICS_PROPERTY_ID %}
{% include "ga.html" %}
{% endif %}
<link rel="stylesheet" href="/static/css/bokeh-0.12.3.min.css" type="text/css" />
<link rel="stylesheet" href="/static/css/bokeh-widgets-0.12.3.min.css" type="text/css" />
<link rel="shortcut icon" href="/static/img/favicon.ico" type="image/x-icon" />
<link rel="icon" sizes="32x32" href="/static/img/favicon-32x32.png" type="image/png"/>
<link rel="icon" sizes="64x64" href="/static/img/favicon-64x64.png" type="image/png"/>
<link rel="icon" sizes="192x192" href="/static/img/favicon-192x192.png" type="image/png"/>
<link rel="icon" sizes="16x16" href="/static/img/favicon-16x16.png" type="image/png"/>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=0.67">
<title>Rowsandall</title>
<link rel="stylesheet" href="/static/css/reset.css" />
<link rel="stylesheet" href="/static/css/text.css" />
<link rel="stylesheet" href="/static/css/960_12_col.css" />
<link rel="stylesheet" href="/static/css/rowsandall.css" />
{% block meta %} {% endblock %}
{% analytical_head_bottom %}
</head>
<body>
{% analytical_body_top %}
{% block body_top %}{% endblock %}
<div class="container_12">
<div class="grid_12">
&nbsp;
</div>
<div class="grid_12">
<div id="logo" class="grid_6 alpha">
{% if user.rower.rowerplan == 'pro' or user.rower.rowerplan == 'coach' %}
<p><a href="/"><img src="/static/img/logo7.png"
alt="Rowsandall logo" height="80"></a></p>
{% else %}
<p><a href="/"><img src="/static/img/logo7.png"
alt="Rowsandall logo" height="80"></a></p>
{% endif %}
</div>
<div class="grid_6 omega">
<div class="grid_4 alpha">
<div class="grid_1 alpha">
<p id="header">
<a class="button gray small" href="/rowers/videos">Videos</a></p>
</div>
<div class="grid_2">
<p id="header">
<a class="button gray small" href="http://analytics.rowsandall.com/">Rowing Analytics BLOG</a></p>
</div>
<div class="grid_1 omega">
<p id="header">
<a class="button gray small" href="/rowers/email">Contact</a>
</p>
</div>
<div class="grid_4 alpha">
<p>Free Data and Analysis. For Rowers. By Rowers.</p>
</div>
</div>
<div class="grid_1">
<div class="grid_1 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/me/edit">{{ user.first_name }}</a>
</p>
<span class="tooltiptext">Edit user account, e.g. heart rate zones, power zones, email, teams</span>
{% else %}
<p><a class="button gray small" href="{% url 'login' %}">login</a> </p>
{% endif %}
</div>
<div class="grid_1">
{% if user.is_authenticated %}
<p><a class="button gray small" href="{% url 'logout' %}">logout</a></p>
{% else %}
<p>&nbsp</p>
{% endif %}
</div>
</div>
<div class="grid_1 omega">
{% if user.rower.rowerplan == 'pro' or user.rower.rowerplan == 'coach' %}
<h6 class="graytext">Pro Member</h6>
{% else %}
<div class="grid_1"><a class="button green small" href="/rowers/promembership">Upgrade to Pro</a></div>
{% endif %}
</div>
</div>
</div>
<div class="grid_12">
<div class="grid_1 alpha tooltip">
{% if user.is_authenticated %}
<p><a class="button gray small" href="/rowers/workout/upload/">Upload</a></p>
<span class="tooltiptext">Upload CSV, TCX, FIT data files to rowsandall.com</span>
{% else %}
<p><a class="button green small" href="/rowers/register">Register (free)</a></p>
{% endif %}
</div>
<div class="grid_1 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/imports/">Import</a>
</p>
<span class="tooltiptext">Import workouts from Strava, SportTracks, and C2 logbook</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_2 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/list-workouts/">Workouts</a>
</p>
<span class="tooltiptext">See your list of workouts</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_1 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/list-graphs/">Graphs</a>
</p>
<span class="tooltiptext">See your most recent charts</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_2 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/analysis">Analysis</a>
</p>
<span class="tooltiptext">Analysis of workouts over a period of time</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_1 tooltip">
{% block teams %}
{% endblock %}
</div>
</div>
<div class="clear"></div>
<div class="grid_12">
{% block message %}
{% if message %}
<p class="message">
{{ message }}
</p>
{% endif %}
{% if successmessage %}
<p class="successmessage">
{{ successmessage }}
</p>
{% endif %}
{% endblock %}
</div>
<div class="grid_12">
{% load tz %}
{% block content %}{% endblock %}
</div>
<div class="clear"></div>
<div class="grid_12 omega" >
{% block footer %}
<p id="footer">{{ versionstring }}</p>
<div class="grid_2 alpha">
<p id="footer"><a href="/rowers/email/">&copy; Sander Roosendaal</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/about">About</a></p>
</div>
<div class="grid_2">
<p id="footer">
<a href="/rowers/developers">Developers</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/legal">Legal</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/partners">Partners</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/physics">Physics</a></p>
</div>
<div class="grid_2">
<p id="footer">
<a href="http://analytics.rowsandall.com/">Rowing Analytics BLOG</a></p>
</div>
<div class="grid_2 omega">
<p id="footer">
<a href="https://www.facebook.com/groups/rowsandall/">Facebook group</a></p>
</div>
{% endblock %}
</div>
{% cookielaw_banner %}
</div>
<!-- end container -->
{% analytical_body_bottom %}
{% block body_bottom %}{% endblock %}
</body>
</html>

80
templates/basefront.html Normal file
View File

@@ -0,0 +1,80 @@
{% extends "basebase.html" %}
{% block filters %}
{% load rowerfilters %}
{% endblock %}
{% block meta %}
{% endblock %}
{% block body_top %}
<style>
.splash {
background-color: transparent;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
min-height: 759px;
min-width: 1024px;
width: 100%;
height: auto;
}
.container_12 {background-color: rgba(255,255,255,0.0);}
.container_top {background-color: rgba(255,255,255,0.7);}
</style>
<div id="bgpic" class="splash">
<div class="container_top">
{% endblock %}
{% block teams %}
{% if user.is_authenticated and user|has_teams %}
<div class="grid_1 alpha dropdown">
<button class="grid_1 alpha button gray small dropbtn">
Teams
</button>
<div class="dropdown-content">
<a class="button gray small" href="/rowers/me/teams/">Manage Teams</a>
{% if user|is_manager %}
<a class="button gray small" href="/rowers/workout/upload/team/">Upload Team Member Workout</a>
{% endif %}
{% for t in user|user_teams %}
<a class="button gray small" href="/rowers/list-workouts/team/{{ t.id }}/">{{ t.name }}</a>
{% endfor %}
</div>
</div>
<span class="tooltiptext">See recent workouts for your team</span>
{% elif user.is_authenticated and user.rower.team.all %}
<div class="grid_1 alpha dropdown">
<button class="grid_1 alpha button gray small dropbtn">
Teams
</button>
<div class="dropdown-content">
{% for t in user.rower.team.all %}
<a class="button gray small" href="/rowers/list-workouts/team/{{ t.id }}/">{{ t.name }}</a>
{% endfor %}
</div>
</div>
<span class="tooltiptext">See recent workouts for your team</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
{% endblock %}
{% block body_bottom %}
</div>
<script type="text/javascript">
var num = (Math.floor(Math.random()*4));
var array = ['one', 'two', 'three', 'four'];
var elem = document.getElementById('bgpic');
console.log(elem);
elem.classList.add(array[num]);
</script>
{% endblock %}

View File

@@ -0,0 +1,26 @@
{% extends "basebase.html" %}
{% block filters %}
{% load rowerfilters %}
{% endblock %}
{% block teams %}
{% if user.is_authenticated and user.rower.team.all %}
<div class="grid_1 alpha dropdown">
<button class="grid_1 alpha button gray small dropbtn">
Teams
</button>
<div class="dropdown-content">
{% for t in user.rower.team.all %}
<a class="button gray small" href="/rowers/list-workouts/team/{{ t.id }}/">{{ t.name }}</a>
{% endfor %}
</div>
</div>
<span class="tooltiptext">See recent workouts for your team</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
{% endblock %}
{% block content %}
{% endblock %}