Private
Public Access
1
0

Merge branch 'feature/python3' into develop

This commit is contained in:
Sander Roosendaal
2019-02-28 22:53:16 +01:00
51 changed files with 114 additions and 1514 deletions
+15 -4
View File
@@ -16,6 +16,8 @@ import rowers.mytypes as mytypes
from rowers.mytypes import otwtypes
from iso8601 import ParseError
import numpy
from rowsandall_app.settings import (
C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET
)
@@ -424,7 +426,11 @@ def createc2workoutdata(w):
hr = 0*d
stroke_data = []
for i in range(len(t)):
thisrecord = {"t":t[i],"d":d[i],"p":p[i],"spm":spm[i],"hr":hr[i]}
thisrecord = {"t":t[i].astype(int),
"d":d[i].astype(int),
"p":p[i].astype(int),
"spm":spm[i].astype(int),
"hr":hr[i].astype(int)}
stroke_data.append(thisrecord)
try:
@@ -440,7 +446,7 @@ def createc2workoutdata(w):
startdatetime = w.startdatetime.isoformat()
except AttributeError:
startdate = datetime.datetime.combine(w.date,datetime.time())
data = {
"type": mytypes.c2mapping[workouttype],
"date": w.startdatetime.isoformat(),
@@ -551,7 +557,7 @@ def make_authorization_url(request):
params = {"client_id": C2_CLIENT_ID,
"response_type": "code",
"redirect_uri": C2_REDIRECT_URI}
url = "https://log.concept2.com/oauth/authorize?"+ urllib.urlencode(params)
url = "https://log.concept2.com/oauth/authorize?"+ urllib.parse.urlencode(params)
url += "&scope="+scope
return HttpResponseRedirect(url)
@@ -697,6 +703,10 @@ def process_callback(request):
return HttpResponse("got a user name: %s" % username)
def default(o):
if isinstance(o, numpy.int64): return int(o)
raise TypeError
# Uploading workout
def workout_c2_upload(user,w):
message = 'trying C2 upload'
@@ -717,6 +727,7 @@ def workout_c2_upload(user,w):
raise NoTokenError
data = createc2workoutdata(w)
if data == 0:
return "Error: No data file. Contact info@rowsandall.com if the problem persists",0
@@ -726,7 +737,7 @@ def workout_c2_upload(user,w):
'Content-Type': 'application/json'}
import urllib
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,default=default))
if (response.status_code == 409 ):
message = "Concept2 Duplicate error"
+1 -1
View File
@@ -280,7 +280,7 @@ def imports_make_authorization_url(oauth_data):
import urllib
url = oauth_data['authorizaton_uri']+urllib.urlencode(params)
url = oauth_data['authorizaton_uri']+urllib.parse.urlencode(params)
return HttpResponseRedirect(url)
+10 -1
View File
@@ -1,13 +1,17 @@
#!/srv/venv/bin/python
""" Process emails """
import sys
import os
PY3K = sys.version_info >= (3, 0)
import zipfile
import re
import time
from time import strftime
import io
from django.core.management.base import BaseCommand
from django_mailbox.models import Message, MessageAttachment,Mailbox
from django.core.urlresolvers import reverse
@@ -62,7 +66,7 @@ def processattachment(rower, fileobj, title, uploadoptions,testing=False):
# test if file exists and is not empty
try:
with open('media/'+filename,'r') as fop:
with io.open('media/'+filename,'rb') as fop:
line = fop.readline()
except (IOError, UnicodeEncodeError):
if testing:
@@ -164,6 +168,11 @@ def get_from_address(message):
except IndexError:
first_line = ''
try:
first_line = first_line.decode('utf-8')
except AttributeError:
pass
if "quiske" in first_line:
match = re.search(r'[\w\.-]+@[\w\.-]+', first_line)
return match.group(0)
+1 -1
View File
@@ -128,7 +128,7 @@ def make_authorization_url(request):
import urllib
url = "http://localhost:8000/rowers/o/authorize" +urllib.urlencode(params)
url = "http://localhost:8000/rowers/o/authorize" +urllib.parse.urlencode(params)
return HttpResponseRedirect(url)
+1 -1
View File
@@ -106,7 +106,7 @@ def make_authorization_url():
"redirect_uri": POLAR_REDIRECT_URI,
"scope":"write"}
import urllib
url = "https://flow.polar.com/oauth2/authorization" +urllib.urlencode(params)
url = "https://flow.polar.com/oauth2/authorization" +urllib.parse.urlencode(params)
return HttpResponseRedirect(url)
+7 -1
View File
@@ -29,6 +29,8 @@ oauth_data = {
}
import numpy
def splitrunkeeperlatlongdata(lijst,tname,latname,lonname):
t = []
lat = []
@@ -262,6 +264,10 @@ def get_userid(access_token):
return str(res)
def default(o):
if isinstance(o, numpy.int64): return int(o)
raise TypeError
def workout_runkeeper_upload(user,w):
message = "Uploading to Runkeeper"
rkid = 0
@@ -287,7 +293,7 @@ def workout_runkeeper_upload(user,w):
'Content-Length':'nnn'}
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,default=default))
# check for duplicate error first
if (response.status_code == 409 ):
+8 -1
View File
@@ -6,6 +6,8 @@ from __future__ import unicode_literals
from __future__ import unicode_literals, absolute_import
# All the functionality to connect to SportTracks
import numpy
from rowers.imports import *
import re
from rowsandall_app.settings import (
@@ -235,6 +237,11 @@ def getidfromresponse(response):
return int(id)
def default(o):
if isinstance(o, numpy.int64): return int(o)
raise TypeError
def workout_sporttracks_upload(user,w):
message = "Uploading to SportTracks"
@@ -257,7 +264,7 @@ def workout_sporttracks_upload(user,w):
'Content-Type': 'application/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,default=default))
# check for duplicate error first
if (response.status_code == 409 ):
-227
View File
@@ -1,227 +0,0 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}{{ workout.name }} Advanced {% endblock %}
{% block content %}
<div id="workouts" class="grid_6 alpha">
<div class="grid_6 alpha">
{% if form.errors %}
<p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below.
</p>
{% endif %}
<h1>{{ workout.name }} - Advanced</h1>
{% if user.rower.rowerplan == 'basic' %}
<p>Functionality marked with an asterisk (*) is limited to the paid plans. See
<a href="/rowers/paidplans">the page about Pro membership</a>
for more information and to sign up for Pro Membership</a></p>
{% endif %}
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id|encode }}/edit">Edit Workout</a>
</p>
</div>
<div class="grid_2">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id|encode }}/workflow">Workflow View</a>
</p>
</div>
<div class="grid_2 omega">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id|encode }}/export">Export</a>
</p>
</div>
</div>
<div class="grid_6 alpha">
<table width=100%>
<tr>
<th>Date:</th><td>{{ workout.date }}</td>
</tr><tr>
<th>Time:</th><td>{{ workout.starttime }}</td>
</tr><tr>
<th>Distance:</th><td>{{ workout.distance }}m</td>
</tr><tr>
<th>Duration:</th><td>{{ workout.duration |durationprint:"%H:%M:%S.%f" }}</td>
</tr>
<th>Public link to this workout</th>
<td>
<a href="/rowers/workout/{{ workout.id|encode }}">https://rowsandall.com/rowers/workout/{{ workout.id|encode }}</a>
<td>
</table>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/compare/{{ workout.id|encode }}">Compare Workouts</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans/">Compare Workouts*</a>
{% endif %}
<p>
Compare this workout to other workouts. Plot HR, SPM, or pace vs time or distance for the two workouts.
</p>
</div>
<div class="grid_2">
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/flexchart">
Flexible Interactive Plot
</a>
<p>
Flexible Interactive plot. Pick your own X and Y axis parameters.
</p>
</div>
<div class="grid_2 omega tooltip">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/editintervals">Edit Intervals</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Edit Intervals*</a>
{% endif %}
</p>
<span class="tooltiptext">Enter or change the interval and summary data for your workout</span>
<p>
Enter or change the interval and summary data for your workout
</p>
</div>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/adddistanceplot2">Dist Metrics Plot</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Dist Metrics Plot*</a>
{% endif %}
</p>
<p>
Various advanced stroke metrics plotted versus distance.
</p>
</div>
<div class="grid_2">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/addtimeplot2">Time Metrics Plot</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Time Metrics Plot*</a>
{% endif %}
</p>
<p>
Various advanced stroke metrics plotted versus time.
</p>
</div>
<div class="grid_2 omega">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/histo">Power Histogram</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Power Histogram*</a>
{% endif %}
</p>
<p>
Plot the Power Histogram of this workout
</p>
</div>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workouts-join-select">Glue</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Glue*</a>
{% endif %}
</p>
<p>
Glue multiple workouts together (into one workout). For example, to combine separately recorded intervals.
</p>
</div>
<div class="grid_2">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/fusion/{{ workout.id|encode }}/">Sensor Fusion</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Sensor Fusion*</a>
{% endif %}
</p>
<p>
Merge data from another source into this workout
</p>
</div>
<div class="grid_2 omega">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/split">Split Workout</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Split Workout*</a>
{% endif %}
</p>
<p>
Split workout into two seperate workouts
</p>
</div>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/interactiveplot">Big Interactive Plot</a>
</p>
<p>
See (and save) the big interactive plot
</p>
</div>
<div class="grid_2">
<p>
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/instroke">In-Stroke Metrics</a></p>
<p>
If your workout has in-stroke metrics (like a stroke power curve or boat acceleration curve), you will find it here.
</p>
</div>
</div>
</div>
<div id="advancedplots" class="grid_6 omega">
<div class="grid_6 alpha">
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ interactiveplot |safe }}
<script>
// Set things up to resize the plot on a window resize. You can play with
// the arguments of resize_width_height() to change the plot's behavior.
var plot_resize_setup = function () {
var plotid = Object.keys(Bokeh.index)[0]; // assume we have just one plot
var plot = Bokeh.index[plotid];
var plotresizer = function() {
// arguments: use width, use height, maintain aspect ratio
plot.resize_width_height(true, true, true);
};
window.addEventListener('resize', plotresizer);
plotresizer();
};
window.addEventListener('load', plot_resize_setup);
</script>
<style>
/* Need this to get the page in "desktop mode"; not having an infinite height.*/
html, body {height: 100%; margin:5px;}
</style>
<div id="interactiveplot" class="grid_6 omega">
{{ the_div |safe }}
</div>
</div>
</div>
{% endblock %}
-284
View File
@@ -1,284 +0,0 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}{{ workout.name }} Advanced {% endblock %}
{% block content %}
<div id="workouts" class="grid_6 alpha">
{% if form.errors %}
<p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below.
</p>
{% endif %}
<h1>{{ workout.name }} - Advanced OTW</h1>
{% if user.rower.rowerplan == 'basic' %}
<p>Functionality marked with an asterisk (*) is limited to the paid plans. See
<a href="/rowers/paidplans">the page about Pro membership</a>
for more information and to sign up for Pro Membership</a></p>
{% endif %}
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id|encode }}/edit">Edit Workout</a>
</p>
</div>
<div class="grid_2">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id|encode }}/workflow">Workflow View</a>
</p>
</div>
<div class="grid_2 omega">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id|encode }}/export">Export</a>
</p>
</div>
<div class="grid_6 alpha">
<table width=100%>
<tr>
<th>Date:</th><td>{{ workout.date }}</td>
</tr><tr>
<th>Time:</th><td>{{ workout.starttime }}</td>
</tr><tr>
<th>Distance:</th><td>{{ workout.distance }}m</td>
</tr><tr>
<th>Duration:</th><td>{{ workout.duration |durationprint:"%H:%M:%S.%f" }}</td>
</tr>
<th>Public link to this workout</th>
<td>
<a href="/rowers/workout/{{ workout.id|encode }}">https://rowsandall.com/rowers/workout/{{ workout.id|encode }}</a>
<td>
</table>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/compare/{{ workout.id|encode }}">Compare Workouts</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Compare Workouts*</a>
{% endif %}
</p>
<p>
Compare this workout to other workouts. Plot HR, SPM, or pace vs time or distance for the two workouts.
</p>
</div>
<div class="grid_2">
<p>
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/flexchart">Flexible Interactive Plot</a>
</p>
<p>
Flexible Interactive plot. Pick your own X and Y axis parameters.
</p>
</div>
<div class="grid_2 omega tooltip">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/editintervals">Edit Intervals</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Edit Intervals*</a>
{% endif %}
</p>
<span class="tooltiptext">Enter or change the interval and summary data for your workout</span>
<p>
Enter or change the interval and summary data for your workout
</p>
</div>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
{% if user|is_promember %}
<a class="button blue small"href="/rowers/workout/{{ workout.id|encode }}/crewnerdsummary">CrewNerd Summary</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">CrewNerd Summary*</a>
{% endif %}
</p>
<p>
Upload a CrewNerd Summary (CSV file) to this workout.
</p>
</div>
<div class="grid_2 tooltip">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/forcecurve">Stroke Profile (Empower)</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Stroke Profile (Empower)*</a>
{% endif %}
</p>
<span class="tooltiptext">Analyze your stroke force profile (need Empower Oarlock data)</span> <p>
Interactive plot of Stroke Profile (with Empower Oarlock)
</p>
</div>
<div class="grid_2 omega tooltip">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/addotwpowerplot">OTW Power Plot</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">OTW Power Plot*</a>
{% endif %}
</p>
<span class="tooltiptext">Note: You must run the OTW calculations under Geeky Stuff first. Otherwise the plot will be empty</span>
<p>
Pace, wind corrected pace, power, equivalent erg power in a static plot
</p>
</div>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/geeky">Geeky Stuff</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Geeky Stuff*</a>
{% endif %}
</p>
<p>
Add weather and current data and OTW power calculations.
</p>
</div>
<div class="grid_2 tooltip">
<p>
{% if user|is_promember %}
<a class="button blue small"href="/rowers/workout/{{ workout.id|encode }}/smoothenpace">Smooth out Pace Data</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Smooth out Pace Data*</a>
{% endif %}
</p>
<span class="tooltiptext">This will reduce noise on your pace data. The smoothing is irreversible but you can use the reset smoothing button.</span>
<p>
Pace data too noisy? Press this button!
</p>
</div>
<div class="grid_2 omega">
<p>
{% if user|is_promember %}
<a class="button blue small"href="/rowers/workout/{{ workout.id|encode }}/undosmoothenpace">Raw Data</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Reset Smoothing*</a>
{% endif %}
</p>
<p>
Reset pace data to values before smoothing
</p>
</div>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workouts-join-select">Glue</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Glue*</a>
{% endif %}
</p>
<p>
Glue multiple workouts together (into one workout). For example, to combine separately recorded intervals.
</p>
</div>
<div class="grid_2">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/fusion/{{ workout.id|encode }}/">Sensor Fusion</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Sensor Fusion*</a>
{% endif %}
</p>
<p>
Merge data from another source into this workout
</p>
</div>
<div class="grid_2 omega">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/split">Split Workout</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Split Workout*</a>
{% endif %}
</p>
<p>
Split workout into two seperate workouts
</p>
</div>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/interactiveplot">Big Interactive Plot</a>
</p>
<p>
See (and save) the big interactive plot
</p>
</div>
<div class="grid_2">
<p>
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/instroke">In-Stroke Metrics</a></p>
<p>
If your workout has in-stroke metrics (like a stroke power curve or boat acceleration curve), you will find it here.
</p>
</div>
</div>
</div>
<div id="advancedplots" class="grid_6 omega">
<div class="grid_6 alpha">
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ interactiveplot |safe }}
<script>
// Set things up to resize the plot on a window resize. You can play with
// the arguments of resize_width_height() to change the plot's behavior.
var plot_resize_setup = function () {
var plotid = Object.keys(Bokeh.index)[0]; // assume we have just one plot
var plot = Bokeh.index[plotid];
var plotresizer = function() {
// arguments: use width, use height, maintain aspect ratio
plot.resize_width_height(true, true, true);
};
window.addEventListener('resize', plotresizer);
plotresizer();
};
window.addEventListener('load', plot_resize_setup);
</script>
<style>
/* Need this to get the page in "desktop mode"; not having an infinite height.*/
html, body {height: 100%; margin:5px;}
</style>
<div id="interactiveplot" class="grid_6 omega">
{{ the_div |safe }}
</div>
</div>
</div>
{% endblock %}
+1 -1
View File
@@ -6,7 +6,7 @@
{% block main %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
+1 -1
View File
@@ -6,7 +6,7 @@
{% block main %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
-232
View File
@@ -1,232 +0,0 @@
{% load cookielaw_tags %}
{% load leaflet_tags %}
{% load tz_detect %}
{% tz_detect %}
{% load analytical %}
{% block filters %}
{% endblock %}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
{% block headtext %}{% endblock %}
<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="stylesheet" href="https://webapiv2.navionics.com/dist/webapi/webapi.min.css" >
<script type="text/javascript" src="https://webapiv2.navionics.com/dist/webapi/webapi.min.no-dep.js"></script>
<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>{% block title %}Rowsandall{% endblock %}</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" />
<link rel="stylesheet" href="/static/css/leaflet.css" />
<script src="/static/css/leaflet.js" ></script>
{% 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/paidplans">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_1">
<p id="footer">
<a href="/rowers/brochure">Brochure</a></p>
</div>
<div class="grid_1">
<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>
-194
View File
@@ -1,194 +0,0 @@
{% load rowerfilters %}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<script src="/static/cookielaw/js/cookielaw.js"></script>
<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/myicon.png" />
<link rel="shortcut icon" href="/static/img/favicon.ico" />
<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 %}
</head>
<body>
<div class="container_12">
<div id="logo" class="grid_2">
{% if user|is_promember %}
<p><a href="/"><img src="/static/img/logocroppedpro.gif"
alt="Rowsandall logo" width="110" heigt="110"></a></p>
{% else %}
<p><a href="/"><img src="/static/img/logocropped.gif"
alt="Rowsandall logo" width="110" heigt="110"></a></p>
{% endif %}
</div>
<div class="grid_10 omega">
<div class="grid_8 alpha"><p>&nbsp</p></div>
<div class="grid_2 omega">
{% if user.is_authenticated %}
<p><a class="button gray small" href="/password_change/">Password Change</a></p>
{% else %}
<p><a class="button gray small" href="/password_reset/">Forgotten Password?</a></p>
{% endif %}
</div>
</div>
<div class="grid_10 omega">
<div class="grid_4 suffix_2 alpha">
<p>Free Data and Analysis. For Rowers. By Rowers.</p>
</div>
<div class="grid_3">
{% if user|is_promember %}
<h6>Pro Member</h6>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_1 omega">
{% 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_10" omega>
<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_2 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 suffix_1 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 omega 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 data, e.g. heart rate zones</span>
{% else %}
<p><a class="button gray small" href="{% url 'login' %}">login</a> </p>
{% endif %}
</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_1">
<p id="footer">
<a href="/rowers/brochure">Brochure</a></p>
</div>
<div class="grid_1">
<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/physics">Physics</a></p>
</div>
<div class="grid_2">
<p id="footer">
<a href="/rowers/videos">Videos</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_1 omega">
<p id="footer">
<a href="/rowers/email">Contact</a></p>
</div>
{% endblock %}
</div>
</div>
<!-- end container -->
</body>
</html>
-60
View File
@@ -1,60 +0,0 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}Rowsandall {% endblock %}
{% block content %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ interactiveplot |safe }}
<script>
// Set things up to resize the plot on a window resize. You can play with
// the arguments of resize_width_height() to change the plot's behavior.
var plot_resize_setup = function () {
var plotid = Object.keys(Bokeh.index)[0]; // assume we have just one plot
var plot = Bokeh.index[plotid];
var plotresizer = function() {
// arguments: use width, use height, maintain aspect ratio
plot.resize_width_height(true, false, false);
};
window.addEventListener('resize', plotresizer);
plotresizer();
};
window.addEventListener('load', plot_resize_setup);
</script>
<style>
/* Need this to get the page in "desktop mode"; not having an infinite height.*/
html, body {height: 100%; margin:5px;}
</style>
<div id="workouts" class="grid_12 alpha">
<h1>Interactive Plot</h1>
{% if user.is_authenticated and mayedit %}
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id|encode }}/edit">Edit Workout</a>
</p>
</div>
<div class="grid_2 suffix_2 omega">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id|encode }}/advanced">Advanced Edit</a>
</p>
</div>
{% endif %}
{{ the_div|safe }}
</div>
{% endblock %}
+1 -1
View File
@@ -6,7 +6,7 @@
{% block main %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
-77
View File
@@ -1,77 +0,0 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}Compare Workouts {% endblock %}
{% block content %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ interactiveplot |safe }}
<script>
// Set things up to resize the plot on a window resize. You can play with
// the arguments of resize_width_height() to change the plot's behavior.
var plot_resize_setup = function () {
var plotid = Object.keys(Bokeh.index)[0]; // assume we have just one plot
var plot = Bokeh.index[plotid];
var plotresizer = function() {
// arguments: use width, use height, maintain aspect ratio
plot.resize_width_height(true, true, false);
};
window.addEventListener('resize', plotresizer);
plotresizer();
};
window.addEventListener('load', plot_resize_setup);
</script>
<style>
/* Need this to get the page in "desktop mode"; not having an infinite height.*/
html, body {height: 100%; margin:5px;}
</style>
<div id="other" class="grid_12 alpha">
<div class="grid_2 alpha suffix_10">
<a class="button blue small"
href="/rowers/workout/compare/{{ id2 }}/{{ id1 }}/{{ xparam }}/{{ yparam }}">Swap Workouts</a>
</div>
</div>
<p>&nbsp;</p>
<div id="plotbuttons" class="grid_12 alpha">
<div id="x-axis" class="grid_6 alpha">
<div class="grid_2 alpha">
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/time/{{ yparam }}">Time</a>
</div>
<div class="grid_2 suffix_2 omega">
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/distance/{{ yparam }}">Distance</a>
</div>
</div>
<div id="y-axis" class="grid_6 omega">
<div class="grid_2 alpha">
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/pace">Pace</a>
</div>
<div class="grid_2">
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/hr">Heart Rate</a>
</div>
<div class="grid_2 omega">
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/spm">SPM</a>
</div>
</div>
</div>
<div id="theplot" class="grid_12 alpha">
{{ the_div|safe }}
</div>
{% endblock %}
-131
View File
@@ -1,131 +0,0 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %} Comparison Plot {% endblock %}
{% block content %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ interactiveplot |safe }}
<script>
// Set things up to resize the plot on a window resize. You can play with
// the arguments of resize_width_height() to change the plot's behavior.
var plot_resize_setup = function () {
var plotid = Object.keys(Bokeh.index)[0]; // assume we have just one plot
var plot = Bokeh.index[plotid];
var plotresizer = function() {
// arguments: use width, use height, maintain aspect ratio
plot.resize_width_height(true, true, false);
};
window.addEventListener('resize', plotresizer);
plotresizer();
};
window.addEventListener('load', plot_resize_setup);
</script>
<style>
/* Need this to get the page in "desktop mode"; not having an infinite height.*/
html, body {height: 100%; margin:5px;}
</style>
<div id="navigation" class="grid_12 alpha">
{% if user.is_authenticated and mayedit %}
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ id }}/edit">Edit Workout</a>
</p>
</div>
<div class="grid_2 suffix_8 omega">
<p>
<a class="button gray small" href="/rowers/workout/compare/{{ id }}/advanced">Advanced Edit</a>
</p>
</div>
{% endif %}
</div>
<div id="plotbuttons" class="grid_12 alpha">
<div id="x-axis" class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
<a class="button blue small"
href="/rowers/workout/compare/{{ id2 }}/{{ id1 }}/{{ xparam }}/{{ yparam }}/{{ plottype }}">Swap Workouts</a>
</p>
</div>
<div class="grid_2 alpha dropdown">
<button class="grid_2 alpha button blue small dropbtn">X-axis</button>
<div class="dropdown-content">
{% for key, value in axchoicesbasic.items %}
{% if key != 'None' %}
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1}}/{{ id2 }}/{{ key }}/{{ yparam }}/{{ plottype }}">{{ value }}</a>
{% endif %}
{% endfor %}
{% if promember %}
{% for key, value in axchoicespro.items %}
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ key }}/{{ yparam }}/{{ plottype }}">{{ value }}</a>
{% endfor %}
{% else %}
{% for key, value in axchoicespro.items %}
<a class="button rosy small" href="/rowers/paidplans">{{ value }}</a>
{% endfor %}
{% endif %}
</div>
</div>
<div class="grid_2 omega dropdown">
<button class="grid_2 alpha button blue small dropbtn">Y-axis</button>
<div class="dropdown-content">
{% for key, value in axchoicesbasic.items %}
{% if key not in noylist and key != 'None' %}
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/{{ key }}/{{ plottype }}">{{ value }}</a>
{% endif %}
{% endfor %}
{% if promember %}
{% for key, value in axchoicespro.items %}
{% if key not in noylist %}
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/{{ key }}/{{ plottype }}">{{ value }}</a>
{% endif %}
{% endfor %}
{% else %}
{% for key, value in axchoicespro.items %}
{% if key not in noylist %}
<a class="button rosy small" href="/rowers/paidplans">{{ value }} (Pro)</a>
{% endif %}
{% endfor %}
{% endif %}
</div>
</div>
</div>
<div id="y-axis" class="grid_6 omega">
<div class="grid_2 prefix_2 alpha">
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/{{ yparam }}/line">Line Plot</a>
</div>
<div class="grid_2 omega">
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/{{ yparam }}/scatter">Scatter Plot</a>
</div>
</div>
</div>
<div id="theplot" class="grid_12 alpha flexplot">
{{ the_div|safe }}
</div>
{% endblock %}
+4 -9
View File
@@ -58,18 +58,13 @@
</script>
<div id="id_css_res">
<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="stylesheet" href="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.css" type="text/css" />
<link rel="stylesheet" href="http://cdn.pydata.org/bokeh/release/bokeh-widgets-1.0.4.min.css" type="text/css" />
</div>
<div id="id_js_res">
<script
type="text/javascript" src="/static/js/bokeh-0.12.3.min.js">
</script>
<script
type="text/javascript" src="/static/js/bokeh-widgets-0.12.3.min.js">
</script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-widgets-1.0.4.min.js"></script>
</div>
+4 -8
View File
@@ -60,16 +60,12 @@
<div id="id_css_res">
<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="stylesheet" href="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.css" type="text/css" />
<link rel="stylesheet" href="http://cdn.pydata.org/bokeh/release/bokeh-widgets-1.0.4.min.css" type="text/css" />
</div>
<div id="id_js_res">
<script
type="text/javascript" src="/static/js/bokeh-0.12.3.min.js">
</script>
<script
type="text/javascript" src="/static/js/bokeh-widgets-0.12.3.min.js">
</script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-widgets-1.0.4.min.js"></script>
</div>
+1 -1
View File
@@ -98,7 +98,7 @@
</li>
{% endif %}
<li class="grid_2">
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
+1 -1
View File
@@ -48,7 +48,7 @@
});
</script>
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
-182
View File
@@ -1,182 +0,0 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% load tz %}
{% block title %} Flexible Plot {% endblock %}
{% localtime on %}
{% block content %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script type="text/javascript" src="/static/js/bokeh-widgets-0.12.3.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ interactiveplot |safe }}
{{ widgetscript | safe }}
<script>
// Set things up to resize the plot on a window resize. You can play with
// the arguments of resize_width_height() to change the plot's behavior.
var plot_resize_setup = function () {
var plotid = Object.keys(Bokeh.index)[0]; // assume we have just one plot
var plot = Bokeh.index[plotid];
var plotresizer = function() {
// arguments: use width, use height, maintain aspect ratio
plot.resize_width_height(true, true, false);
};
window.addEventListener('resize', plotresizer);
plotresizer();
};
window.addEventListener('load', plot_resize_setup);
</script>
<style>
/* Need this to get the page in "desktop mode"; not having an infinite height.*/
html, body {height: 100%; margin:5px;}
</style>
<div id="navigation" class="grid_12 alpha">
{% if user.is_authenticated and mayedit %}
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ id }}/edit">Edit Workout</a>
</p>
</div>
<div class="grid_2 suffix_8 omega">
<p>
<a class="button gray small" href="/rowers/workout/{{ id }}/advanced">Advanced Edit</a>
</p>
</div>
{% endif %}
</div>
<p>&nbsp;</p>
<div id="plotbuttons" class="grid_12 alpha">
<div id="x-axis" class="grid_6 alpha">
<div class="grid_2 alpha dropdown">
<button class="grid_2 alpha button blue small dropbtn">X-axis</button>
<div class="dropdown-content">
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/time/{{ yparam1 }}/{{ yparam2 }}/{{ plottype }}">Time</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/distance/{{ yparam1 }}/{{ yparam2 }}/{{ plottype }}">Distance</a>
{% if promember %}
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/power/{{ yparam1 }}/{{ yparam2 }}/scatter">Power</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/hr/{{ yparam1 }}/{{ yparam2 }}/scatter">HR</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/spm/{{ yparam1 }}/{{ yparam2 }}/scatter">SPM</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/peakforce/{{ yparam1 }}/{{ yparam2 }}/scatter">Peak Force</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/averageforce/{{ yparam1 }}/{{ yparam2 }}/scatter">Average Force</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/drivelength/{{ yparam1 }}/{{ yparam2 }}/scatter">Drive Length</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/driveenergy/{{ yparam1 }}/{{ yparam2 }}/scatter">Work per Stroke</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/drivespeed/{{ yparam1 }}/{{ yparam2 }}/scatter">Drive Speed</a>
{% else %}
<a class="button rosy small" href="/rowers/paidplans">Power (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">HR (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">SPM (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Peak Force (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Average Force (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Drive Length (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Work per Stroke (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Drive Speed (Pro)</a>
{% endif %}
</div>
</div>
<div class="grid_2 dropdown">
<button class="grid_2 alpha button blue small dropbtn">Left</button>
<div class="dropdown-content">
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/pace/{{ yparam2 }}/{{ plottype }}">Pace</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/hr/{{ yparam2 }}/{{ plottype }}">HR</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/spm/{{ yparam2 }}/{{ plottype }}">SPM</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/power/{{ yparam2 }}/{{ plottype }}">Power</a>
{% if promember %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/peakforce/{{ yparam2 }}/{{ plottype }}">Peak Force</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/averageforce/{{ yparam2 }}/{{ plottype }}">Average Force</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/drivelength/{{ yparam2 }}/{{ plottype }}">Drive Length</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/driveenergy/{{ yparam2 }}/{{ plottype }}">Work per Stroke</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/drivespeed/{{ yparam2 }}/{{ plottype }}">Drive Speed</a>
{% else %}
<a class="button rosy small" href="/rowers/paidplans">Peak Force (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Average Force (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Drive Length (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Work per Stroke (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Drive Speed (Pro)</a>
{% endif %}
</div>
</div>
<div class="grid_2 dropdown omega">
<button class="grid_2 alpha button blue small dropbtn">Right</button>
<div class="dropdown-content">
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/hr/{{ plottype }}">HR</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/spm/{{ plottype }}">SPM</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/power/{{ plottype }}">Power</a>
{% if promember %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/peakforce/{{ plottype }}">Peak Force</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/averageforce/{{ plottype }}">Average Force</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/drivelength/{{ plottype }}">Drive Length</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/driveenergy/{{ plottype }}">Work per Stroke</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/drivespeed/{{ plottype }}">Drive Speed</a>
{% else %}
<a class="button rosy small" href="/rowers/paidplans">Peak Force (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Average Force (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Drive Length (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Work per Stroke (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Drive Speed (Pro)</a>
{% endif %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/None/{{ plottype }}">None</a>
</div>
</div>
</div>
<div id="y-axis" class="grid_6 omega">
<div class="grid_2 alpha tooltip">
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
{% csrf_token %}
{% if workstrokesonly %}
<input type="hidden" name="workstrokesonly" value="True">
{% else %}
<input class="grid_2 alpha button blue small" type="hidden" name="workstrokesonly" value="False">
{% endif %}
<input class="grid_2 alpha button blue small" value="Toggle Work Strokes" type="Submit">
</form>
<span class="tooltiptext">If your data source allows, this will show or hide strokes taken during rest intervals.</span>
</div>
<div class="grid_2">
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/{{ yparam2 }}/line">Line Plot</a>
</div>
<div class="grid_2 omega">
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/{{ yparam2 }}/scatter">Scatter Plot</a>
</div>
</div>
</div>
<div id="theplot" class="grid_12 alpha flexplot">
{{ the_div|safe }}
</div>
<div id="thewidgets" class="grid_12 alpha">
{{ widgetdiv1|safe }}
{{ widgetdiv2|safe }}
</div>
{% endblock %}
{% endlocaltime %}
+2 -2
View File
@@ -11,8 +11,8 @@
{{ js_res | safe }}
{{ css_res| safe }}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script type="text/javascript" src="/static/js/bokeh-widgets-0.12.3.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-widgets-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
+4 -9
View File
@@ -60,17 +60,12 @@
<div id="id_css_res">
<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="stylesheet" href="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.css" type="text/css" />
<link rel="stylesheet" href="http://cdn.pydata.org/bokeh/release/bokeh-widgets-1.0.4.min.css" type="text/css" />
</div>
<div id="id_js_res">
<script
type="text/javascript" src="/static/js/bokeh-0.12.3.min.js">
</script>
<script
type="text/javascript" src="/static/js/bokeh-widgets-0.12.3.min.js">
</script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-widgets-1.0.4.min.js"></script>
</div>
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
+1 -1
View File
@@ -6,7 +6,7 @@
{% block main %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
+1 -1
View File
@@ -6,7 +6,7 @@
{% block main %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
+1 -1
View File
@@ -6,7 +6,7 @@
{% block main %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
+1 -1
View File
@@ -6,7 +6,7 @@
{% block main %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
+1 -1
View File
@@ -10,7 +10,7 @@
{% block main %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
+1 -1
View File
@@ -138,7 +138,7 @@
<div id="advancedplots" class="grid_6 omega">
<div class="grid_6 alpha">
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
+1 -1
View File
@@ -6,7 +6,7 @@
{% block main %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
+1 -1
View File
@@ -10,7 +10,7 @@
{% block main %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
+1 -1
View File
@@ -13,7 +13,7 @@
{% block main %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
+1 -1
View File
@@ -23,7 +23,7 @@
</li>
<li class="grid_2">
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
+1 -1
View File
@@ -31,7 +31,7 @@
<img src="/static/img/rivercurrent.jpg" width="400">
</li>
<li class="grid_2">
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
+1 -1
View File
@@ -62,7 +62,7 @@
</form>
</li>
<li class="grid_2">
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
-54
View File
@@ -1,54 +0,0 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %} Flexible Plot {% endblock %}
{% block content %}
{{ js_res | safe }}
{{ css_res| safe }}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script type="text/javascript" src="/static/js/bokeh-widgets-0.12.3.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ the_script |safe }}
<style>
/* Need this to get the page in "desktop mode"; not having an infinite height.*/
html, body {height: 100%; margin:5px;}
</style>
<div id="navigation" class="grid_12 alpha">
{% if user.is_authenticated and mayedit %}
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ id }}/edit">Edit Workout</a>
</p>
</div>
<div class="grid_2 suffix_8 omega">
<p>
<a class="button gray small" href="/rowers/workout/{{ id }}/advanced">Advanced Edit</a>
</p>
</div>
{% endif %}
</div>
<p>&nbsp;</p>
<div id="theplot" class="grid_12 alpha">
{{ the_div|safe }}
</div>
{% endblock %}
+1 -1
View File
@@ -53,7 +53,7 @@
</li>
<li class="grid_2">
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
+1 -1
View File
@@ -13,7 +13,7 @@
{{ workout.date }} - {{ workout.distance }}m - {{ workout.duration |durationprint:"%H:%M:%S.%f" }}{% endblock %}
{% block meta %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
+1 -1
View File
@@ -86,7 +86,7 @@
</li>
<li class="grid_2">
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
+1 -1
View File
@@ -130,7 +130,7 @@ $('#id_workouttype').change();
{% endif %}
{% if mapdiv %}
<li class="grid_2">
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
+1 -1
View File
@@ -121,7 +121,7 @@
</li>
{% endif %}
<li class="grid_2">
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
+3 -2
View File
@@ -16,11 +16,12 @@ class TestErrorPages(TestCase):
factory = RequestFactory()
request = factory.get('/')
response = error404_view(request)
self.assertEqual(response.status_code, 404)
self.assertIn('404 Page not found', unicode(response))
self.assertIn('404 Page not found', str(response.content))
response = error500_view(request)
self.assertEqual(response.status_code, 500)
self.assertIn('500 Internal Server Error', unicode(response))
self.assertIn('500 Internal Server Error', str(response.content))
response = error400_view(request)
self.assertEqual(response.status_code, 400)
Binary file not shown.
+8 -1
View File
@@ -5,6 +5,8 @@ from __future__ import unicode_literals
from __future__ import unicode_literals, absolute_import
from rowers.imports import *
import numpy
import rowers.mytypes as mytypes
from rowers.mytypes import otwtypes
@@ -305,6 +307,11 @@ def get_userid(access_token):
return res
def default(o):
if isinstance(o, numpy.int64): return int(o)
raise TypeError
def workout_ua_upload(user,w):
message = "Uploading to MapMyFitness"
uaid = 0
@@ -331,7 +338,7 @@ def workout_ua_upload(user,w):
}
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,default=default))
# check for duplicate error first
if (response.status_code == 409 ):
+5
View File
@@ -44,6 +44,11 @@ from rowers.utils import (
)
def cleanbody(body):
try:
body = body.decode('utf-8')
except AttributeError:
pass
regex = r".*---\n([\s\S]*?)\.\.\..*"
matches = re.finditer(regex,body)
+9 -3
View File
@@ -5,6 +5,12 @@ from __future__ import unicode_literals
from rowers.views.statements import *
import numpy
def default(o):
if isinstance(o, numpy.int64): return int(o)
raise TypeError
# Send workout to TP
@login_required()
@@ -228,7 +234,7 @@ def workout_runkeeper_upload_view(request,id=0):
'Content-Length':'nnn'}
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,default=default))
# check for duplicate error first
if (response.status_code == 409 ):
@@ -293,7 +299,7 @@ def workout_underarmour_upload_view(request,id=0):
}
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,default=default))
# check for duplicate error first
@@ -356,7 +362,7 @@ def workout_sporttracks_upload_view(request,id=0):
'Content-Type': 'application/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,default=default))
# check for duplicate error first
+7 -4
View File
@@ -28,6 +28,9 @@ import isodate
import re
import cgi
from icalendar import Calendar, Event
from functools import reduce
import rowers.braintreestuff as braintreestuff
import rowers.payments as payments
from rowers.opaque import encoder
@@ -502,7 +505,7 @@ def remove_asynctask(request,id):
def get_job_result(jobid):
if settings.TESTING:
return None
elif settings.DEBUG:
elif settings.CELERY:
result = celery_result.AsyncResult(jobid).result
else:
running_job_ids = rq_registry.get_job_ids()
@@ -541,7 +544,7 @@ def get_job_status(jobid):
'started_at':None,
}
return summary
elif settings.DEBUG:
elif settings.CELERY:
job = celery_result.AsyncResult(jobid)
jobresult = job.result
@@ -592,7 +595,7 @@ def get_job_status(jobid):
return summary
def kill_async_job(request,id='aap'):
if settings.DEBUG:
if settings.CELERY:
job = celery_result.AsyncResult(id)
job.revoke()
else:
@@ -734,7 +737,7 @@ def get_stored_tasks_status(request):
if finished:
cache.set(id,100)
progress = 100
elif cached_progress>0:
elif cached_progress is not None and cached_progress>0:
progress = cached_progress
else:
progress = 0
+1 -1
View File
@@ -4595,7 +4595,7 @@ def workout_split_view(request,id=0):
pass
qdict = {'q':rowname}
url+='?'+urllib.urlencode(qdict)
url+='?'+urllib.parse.urlencode(qdict)
return HttpResponseRedirect(url)
+1 -1
View File
@@ -58,7 +58,7 @@
<link rel="stylesheet" type="text/css" href="/static/admin/css/widgets.css"/>
<link rel="stylesheet" href="/static/css/resetnew.css" />
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-9ralMzdK1QYsk4yBY680hmsb4/hJ98xK3w0TIaJ3ll4POWpWUYaA2bRjGGujGT8w" crossorigin="anonymous">
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-6jHF7Z3XI3fF4XZixAuSu0gGKrXwoX/w3uFPxC56OtjChio7wtTGJWRW53Nhx6Ev" crossorigin="anonymous">
<link rel="stylesheet" href="/static/css/styles2.css">
<link rel="stylesheet" href="/static/css/text2.css" />
<link rel="stylesheet" href="/static/css/rowsandall2.css" />
+1 -1
View File
@@ -56,7 +56,7 @@
<link rel="stylesheet" type="text/css" href="/static/admin/css/widgets.css"/>
<link rel="stylesheet" href="/static/css/resetnew.css" />
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-9ralMzdK1QYsk4yBY680hmsb4/hJ98xK3w0TIaJ3ll4POWpWUYaA2bRjGGujGT8w" crossorigin="anonymous">
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-6jHF7Z3XI3fF4XZixAuSu0gGKrXwoX/w3uFPxC56OtjChio7wtTGJWRW53Nhx6Ev" crossorigin="anonymous">
<link rel="stylesheet" href="/static/css/frontstyles.css">
<link rel="stylesheet" href="/static/css/text2.css" />
<link rel="stylesheet" href="/static/css/rowsandall2.css" />