Merge branch 'release/sliders'
This commit is contained in:
@@ -1,244 +0,0 @@
|
||||
#: .\cvkbrno\models.py:16
|
||||
msgid "Boat Category"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:19
|
||||
msgid "boat category"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:20
|
||||
msgid "boat categories"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:34
|
||||
msgid "coach"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:35 .\cvkbrno\models.py:40
|
||||
msgid "member"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:36
|
||||
msgid "administrator"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:41
|
||||
msgid "members"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:49
|
||||
msgid "Boat Categories"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:50
|
||||
msgid "Club"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:51
|
||||
msgid "Hours Worked"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:63
|
||||
msgid "boat"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:64
|
||||
msgid "boats"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:66
|
||||
msgid "Boat Name"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:67
|
||||
msgid "Boat Code"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:69
|
||||
msgid "Nr of Seats"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:71
|
||||
msgid "water"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:72
|
||||
msgid "hangar"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:73 .\cvkbrno\views.py:661
|
||||
msgid "damaged"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:74
|
||||
msgid "races"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:94 .\cvkbrno\models.py:95
|
||||
msgid "member work"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:97
|
||||
msgid "Date"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:98
|
||||
msgid "Hours"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:99
|
||||
msgid "Work Event Name"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:102
|
||||
msgid "planned"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:103
|
||||
msgid "executed"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:111
|
||||
msgid "outing"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:112
|
||||
msgid "outings"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:115
|
||||
msgid "Start Date/Time"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:116
|
||||
msgid "End Date/Time"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:117
|
||||
msgid "Distance"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:118
|
||||
msgid "Comment"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:120
|
||||
msgid "reservation"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:121
|
||||
msgid "active"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:122
|
||||
msgid "completed"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:123
|
||||
msgid "race"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\models.py:127
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:29
|
||||
msgid "Welcome,"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:33
|
||||
msgid "Password Change"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:45
|
||||
msgid "Members Work"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:59
|
||||
msgid "Go Rowing"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:69
|
||||
msgid "Reservations"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:78
|
||||
msgid "Boats"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:86
|
||||
msgid "Members"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:94
|
||||
msgid "Transport"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:102
|
||||
msgid "Categories"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:109
|
||||
msgid "logout"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\views.py:245
|
||||
msgid "boat deleted"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\views.py:259
|
||||
msgid "member deleted"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\views.py:273
|
||||
msgid "category deleted"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\views.py:304 .\cvkbrno\views.py:398 .\cvkbrno\views.py:445
|
||||
#: .\cvkbrno\views.py:493
|
||||
msgid "invalid form"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\views.py:537 .\cvkbrno\views.py:543 .\cvkbrno\views.py:921
|
||||
msgid "Changes saved"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\views.py:643
|
||||
msgid "You have no active outings. Go rowing first."
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\views.py:649
|
||||
msgid "You are not out. Go rowing first."
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\views.py:714
|
||||
msgid "Conflicting reservations exist"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\views.py:731
|
||||
msgid "Invalid Form"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\views.py:747
|
||||
msgid "Make a reservation"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\views.py:773 .\cvkbrno\views.py:814
|
||||
msgid "There is already a boat with that code"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\views.py:823
|
||||
msgid "Changes Saved"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\views.py:848
|
||||
msgid "The two passwords are not equal"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\views.py:855
|
||||
msgid "There is already a user with that username"
|
||||
msgstr ""
|
||||
|
||||
#: .\cvkbrno\views.py:880
|
||||
msgid "New Member Created"
|
||||
msgstr ""
|
||||
@@ -1,263 +0,0 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-19 08:27+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: .\cvkbrno\models.py:16
|
||||
msgid "Boat Category"
|
||||
msgstr "Kategorie Lodí"
|
||||
|
||||
#: .\cvkbrno\models.py:19
|
||||
msgid "boat category"
|
||||
msgstr "kategorie lodí"
|
||||
|
||||
#: .\cvkbrno\models.py:20
|
||||
msgid "boat categories"
|
||||
msgstr "kategorie lodí"
|
||||
|
||||
#: .\cvkbrno\models.py:34
|
||||
msgid "coach"
|
||||
msgstr "trenér"
|
||||
|
||||
#: .\cvkbrno\models.py:35 .\cvkbrno\models.py:40
|
||||
msgid "member"
|
||||
msgstr "clen"
|
||||
|
||||
#: .\cvkbrno\models.py:36
|
||||
msgid "administrator"
|
||||
msgstr "správce"
|
||||
|
||||
#: .\cvkbrno\models.py:41
|
||||
msgid "members"
|
||||
msgstr "clenové"
|
||||
|
||||
#: .\cvkbrno\models.py:49
|
||||
msgid "Boat Categories"
|
||||
msgstr "Kategorie Lodí"
|
||||
|
||||
#: .\cvkbrno\models.py:50
|
||||
msgid "Club"
|
||||
msgstr "Klub"
|
||||
|
||||
#: .\cvkbrno\models.py:51
|
||||
msgid "Hours Worked"
|
||||
msgstr "Odpracované hodiny"
|
||||
|
||||
#: .\cvkbrno\models.py:63
|
||||
msgid "boat"
|
||||
msgstr "lod"
|
||||
|
||||
#: .\cvkbrno\models.py:64
|
||||
msgid "boats"
|
||||
msgstr "lode"
|
||||
|
||||
#: .\cvkbrno\models.py:66
|
||||
msgid "Boat Name"
|
||||
msgstr "název lodi"
|
||||
|
||||
#: .\cvkbrno\models.py:67
|
||||
msgid "Boat Code"
|
||||
msgstr "Kód Lode"
|
||||
|
||||
#: .\cvkbrno\models.py:69
|
||||
msgid "Nr of Seats"
|
||||
msgstr "Pocet míst"
|
||||
|
||||
#: .\cvkbrno\models.py:71
|
||||
msgid "water"
|
||||
msgstr "na vode"
|
||||
|
||||
#: .\cvkbrno\models.py:72
|
||||
msgid "hangar"
|
||||
msgstr "v hangaru"
|
||||
|
||||
#: .\cvkbrno\models.py:73 .\cvkbrno\views.py:656
|
||||
msgid "damaged"
|
||||
msgstr "poškozená"
|
||||
|
||||
#: .\cvkbrno\models.py:74
|
||||
msgid "races"
|
||||
msgstr "na závodech"
|
||||
|
||||
#: .\cvkbrno\models.py:94 .\cvkbrno\models.py:95
|
||||
msgid "member work"
|
||||
msgstr "brigády"
|
||||
|
||||
#: .\cvkbrno\models.py:97
|
||||
msgid "Date"
|
||||
msgstr "Datum"
|
||||
|
||||
#: .\cvkbrno\models.py:98
|
||||
msgid "Hours"
|
||||
msgstr "Hodiny"
|
||||
|
||||
#: .\cvkbrno\models.py:99
|
||||
msgid "Work Event Name"
|
||||
msgstr "Název Brigády"
|
||||
|
||||
#: .\cvkbrno\models.py:102
|
||||
msgid "planned"
|
||||
msgstr "plánovaná"
|
||||
|
||||
#: .\cvkbrno\models.py:103
|
||||
msgid "executed"
|
||||
msgstr "hotová"
|
||||
|
||||
#: .\cvkbrno\models.py:111
|
||||
msgid "outing"
|
||||
msgstr "výjezd"
|
||||
|
||||
#: .\cvkbrno\models.py:112
|
||||
msgid "outings"
|
||||
msgstr "výjezdy"
|
||||
|
||||
#: .\cvkbrno\models.py:115
|
||||
msgid "Start Date/Time"
|
||||
msgstr "Startovní Datum/Cas"
|
||||
|
||||
#: .\cvkbrno\models.py:116
|
||||
msgid "End Date/Time"
|
||||
msgstr "Datum/Cas ukoncení"
|
||||
|
||||
#: .\cvkbrno\models.py:117
|
||||
msgid "Distance"
|
||||
msgstr "Vzdálenost"
|
||||
|
||||
#: .\cvkbrno\models.py:118
|
||||
msgid "Comment"
|
||||
msgstr "Komentár"
|
||||
|
||||
#: .\cvkbrno\models.py:120
|
||||
msgid "reservation"
|
||||
msgstr "rezervace"
|
||||
|
||||
#: .\cvkbrno\models.py:121
|
||||
msgid "active"
|
||||
msgstr "aktivní"
|
||||
|
||||
#: .\cvkbrno\models.py:122
|
||||
msgid "completed"
|
||||
msgstr "dokoncený"
|
||||
|
||||
#: .\cvkbrno\models.py:123
|
||||
msgid "race"
|
||||
msgstr "závod"
|
||||
|
||||
#: .\cvkbrno\models.py:127
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:26
|
||||
msgid "Welcome,"
|
||||
msgstr "Vítej,"
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:30
|
||||
msgid "Password Change"
|
||||
msgstr "Zmenit Heslo"
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:42
|
||||
msgid "Members Work"
|
||||
msgstr "Brigády"
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:56
|
||||
msgid "Go Rowing"
|
||||
msgstr "Jdi na vodu"
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:66
|
||||
msgid "Reservations"
|
||||
msgstr "Rezervace"
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:75
|
||||
msgid "Boats"
|
||||
msgstr "Lode"
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:83
|
||||
msgid "Members"
|
||||
msgstr "Clenové"
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:91
|
||||
msgid "Transport"
|
||||
msgstr "Doprava"
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:99
|
||||
msgid "Categories"
|
||||
msgstr "Kategorie"
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:106
|
||||
msgid "logout"
|
||||
msgstr "odhlásit"
|
||||
|
||||
#: .\cvkbrno\views.py:240
|
||||
msgid "boat deleted"
|
||||
msgstr "lod smazána"
|
||||
|
||||
#: .\cvkbrno\views.py:254
|
||||
msgid "member deleted"
|
||||
msgstr "clen smazán"
|
||||
|
||||
#: .\cvkbrno\views.py:268
|
||||
msgid "category deleted"
|
||||
msgstr "kategorie smazána"
|
||||
|
||||
#: .\cvkbrno\views.py:299 .\cvkbrno\views.py:393 .\cvkbrno\views.py:440
|
||||
#: .\cvkbrno\views.py:488
|
||||
msgid "invalid form"
|
||||
msgstr "neplatný formulár"
|
||||
|
||||
#: .\cvkbrno\views.py:532 .\cvkbrno\views.py:538 .\cvkbrno\views.py:916
|
||||
msgid "Changes saved"
|
||||
msgstr "Zmeny uloženy"
|
||||
|
||||
#: .\cvkbrno\views.py:638
|
||||
msgid "You have no active outings. Go rowing first."
|
||||
msgstr "Nemáte žádné aktivní výjezdy. Jdete nejdríve na vodu."
|
||||
|
||||
#: .\cvkbrno\views.py:644
|
||||
msgid "You are not out. Go rowing first."
|
||||
msgstr "Nejste na vode. Jdete nejdríve na vodu."
|
||||
|
||||
#: .\cvkbrno\views.py:709
|
||||
msgid "Conflicting reservations exist"
|
||||
msgstr "Konflikt rezervací"
|
||||
|
||||
#: .\cvkbrno\views.py:726
|
||||
msgid "Invalid Form"
|
||||
msgstr "Neplatný Formulár"
|
||||
|
||||
#: .\cvkbrno\views.py:742
|
||||
msgid "Make a reservation"
|
||||
msgstr "Rezervujte"
|
||||
|
||||
#: .\cvkbrno\views.py:768 .\cvkbrno\views.py:809
|
||||
msgid "There is already a boat with that code"
|
||||
msgstr "Již existuje jiná lod s tímto kódem."
|
||||
|
||||
#: .\cvkbrno\views.py:818
|
||||
msgid "Changes Saved"
|
||||
msgstr "Zmeny Uloženy"
|
||||
|
||||
#: .\cvkbrno\views.py:843
|
||||
msgid "The two passwords are not equal"
|
||||
msgstr "Nazadal jste stejná hesla."
|
||||
|
||||
#: .\cvkbrno\views.py:850
|
||||
msgid "There is already a user with that username"
|
||||
msgstr "Uživatel s tímto uživatelském jménem již existuje"
|
||||
|
||||
#: .\cvkbrno\views.py:875
|
||||
msgid "New Member Created"
|
||||
msgstr "Nový clen vytvoren"
|
||||
@@ -1,31 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}{% trans "Create Boat" %}{% 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>{% trans "Create Boat" %}</h1>
|
||||
|
||||
|
||||
<form enctype="multipart/form-data" action="" method="post">
|
||||
<table width=100%>
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
<div id="formbutton" class="grid_1 prefix_4 suffix_1">
|
||||
<input type="submit" value="Save">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,25 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}{% "Delete Boat" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="workouts" class="grid_6 alpha">
|
||||
<div class="grid_2 suffix_4 alpha">
|
||||
<p class="deletelink">
|
||||
<a href="/cvkbrno/boats/{{ id }}/delete">
|
||||
{% trans "Confirm Delete" %}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% if form.errors %}
|
||||
<p style="color: red;">
|
||||
Please correct the error{{ form.errors|pluralize }} below.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,66 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}{% trans "Edit Boat" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="workouts" class="grid_6 alpha">
|
||||
<div class="grid_2 suffix_4 alpha">
|
||||
<p>
|
||||
<a class="button red small" href="/cvkbrno/boats/{{ id }}/deleteconfirm">
|
||||
{% trans "Delete" %}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% if form.errors %}
|
||||
<p style="color: red;">
|
||||
Please correct the error{{ form.errors|pluralize }} below.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<h1>{% trans "Edit Boat" %}</h1>
|
||||
|
||||
|
||||
<form enctype="multipart/form-data" action="" method="post">
|
||||
<table width=100%>
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
<div id="formbutton" class="grid_1 prefix_4 suffix_1">
|
||||
<input class="button green" type="submit" value="{% trans 'Save' %}">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div id="outings" class="grid_6 omega">
|
||||
<h1>{% trans "Recent Outings with this boat" %}</h1>
|
||||
|
||||
<div class="grid_2 suffix_4 alpha">
|
||||
<p>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<table class="listtable" width=100%>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Date" %}</th>
|
||||
<th>{% trans "Rower" %}</th>
|
||||
<th>{% trans "Status" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for outing in theoutings %}
|
||||
<tr>
|
||||
<td>{{ outing.starttime }}</td>
|
||||
<td>{{ outing.rower.user.first_name }} {{ outing.rower.user.last_name }}</td>
|
||||
<td>{% trans outing.status %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,80 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}{% trans "Boats Admin" %}{% 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>{% trans "Boats Admin" %}</h1>
|
||||
|
||||
|
||||
<div id="addboat" class="grid_2 suffix_4 alpha">
|
||||
<p>
|
||||
<a class="button green small" href="/cvkbrno/boats/add">{% trans "Add Boat" %}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<table class="listtable" width=100%>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Code" %}</th>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Status" %}</th>
|
||||
<th>{% trans "Edit" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for boat in theboats %}
|
||||
<tr>
|
||||
<td>{{ boat.boatcode }}</td>
|
||||
<td>{{ boat.boatname }}</td>
|
||||
<td>{% trans boat.status %}</td>
|
||||
<td><a href="/cvkbrno/boats/{{ boat.id }}/edit">E</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tbody>
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
</div>
|
||||
<div id="outings" class="grid_6 omega">
|
||||
<h1>{% trans "Recent Outings" %}</h1>
|
||||
|
||||
<div class="grid_2 suffix_4 alpha">
|
||||
<p>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<table class="listtable" width=100%>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Date" %}</th>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Rower" %}</th>
|
||||
<th>{% trans "Status" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for outing in theoutings %}
|
||||
<tr>
|
||||
<td>{{ outing.starttime }}</td>
|
||||
<td>{{ outing.boat.boatname }}</td>
|
||||
<td>{{ outing.rower.user.first_name }} {{ outing.rower.user.last_name }}</td>
|
||||
<td>{% trans outing.status %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tbody>
|
||||
</table>
|
||||
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,45 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}{% trans "Boat Category Admin" %}{% 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>{% trans "Boat Category Admin" %}</h1>
|
||||
|
||||
|
||||
<div id="addcategory" class="grid_2 suffix_4 alpha">
|
||||
<p class="greenbar">
|
||||
<a href="/cvkbrno/categories/add">{% trans "Add Boat Category" %}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<table class="listtable" width=100%>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Edit" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for category in thecats %}
|
||||
<tr>
|
||||
<td>{{ category.name }}</td>
|
||||
<td><a href="/cvkbrno/categories/{{ category.id }}/edit">E</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tbody>
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,24 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}{% trans "Delete Boat Category" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="workouts" class="grid_6 alpha">
|
||||
<div class="grid_2 suffix_4 alpha">
|
||||
<p class="deletelink">
|
||||
<a href="/cvkbrno/categories/{{ id }}/delete">{% trans "Confirm Delete" %}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% if form.errors %}
|
||||
<p style="color: red;">
|
||||
Please correct the error{{ form.errors|pluralize }} below.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,37 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}{% trans "Edit Category" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="workouts" class="grid_6 alpha">
|
||||
<div class="grid_2 suffix_4 alpha">
|
||||
<p class="deletelink">
|
||||
<a href="/cvkbrno/categories/{{ id }}/deleteconfirm">
|
||||
{% trans "Delete" %}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% if form.errors %}
|
||||
<p style="color: red;">
|
||||
Please correct the error{{ form.errors|pluralize }} below.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<h1>{% trans "Edit Category" %}</h1>
|
||||
|
||||
|
||||
<form enctype="multipart/form-data" action="" method="post">
|
||||
<table width=100%>
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
<div id="formbutton" class="grid_1 prefix_4 suffix_1">
|
||||
<input type="submit" value="{% trans 'Save' %}">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,31 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}{% trans "Create Category" %}{% 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>{% trans "Create Category" %}</h1>
|
||||
|
||||
|
||||
<form enctype="multipart/form-data" action="" method="post">
|
||||
<table width=100%>
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
<div id="formbutton" class="grid_1 prefix_4 suffix_1">
|
||||
<input type="submit" value="Save">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,81 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="grid_12 alpha">
|
||||
{% if form.errors %}
|
||||
<p>{% trans "Your username and password didn't match. Please try again." %}</p>
|
||||
{% endif %}
|
||||
|
||||
<form method="post" action="">
|
||||
{% csrf_token %}
|
||||
{% if next %}
|
||||
<input type="hidden" name="next" value="{{ next }}" />
|
||||
{% endif %}
|
||||
<table align="center">
|
||||
<tr><th>
|
||||
<label class="control-label" for="username">{% trans "Username" %}</label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="text" id="username" name="username" placeholder="Username">
|
||||
</td></tr>
|
||||
<tr><th>
|
||||
<label class="control-label" for="password">
|
||||
{% trans "Password" %}</label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="password" name="password" id="password" placeholder="Password">
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td> </p>
|
||||
<td>
|
||||
<button type="submit" class="btn">{% trans "Login" %}</button>
|
||||
</td></tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="grid_6 alpha">
|
||||
<h1>{% trans "Boats Out" %}</h1>
|
||||
|
||||
<table class="listtable" width=100%>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Boat" %}</th>
|
||||
<th>{% trans "Departure time" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for outing in activeoutings %}
|
||||
<tr>
|
||||
<td> {{ outing.boat.boatname }} </td>
|
||||
<td> {{ outing.starttime }} </td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="grid_6 omega">
|
||||
<h1>{% trans "Reservations" %}</h1>
|
||||
|
||||
<table class="listtable" width=100%>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Boat" %}</th>
|
||||
<th>{% trans "Departure time" %}</th>
|
||||
<th>{% trans "Reserved by" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for res in reservations %}
|
||||
<tr>
|
||||
<td> {{ res.boat.boatname }} </td>
|
||||
<td> {{ res.starttime }} </td>
|
||||
<td> {{ res.rower.user.first_name }} {{ res.rower.user.last_name }} </td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
||||
@@ -1,33 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}{% trans "Create Member" %}{% 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>{% trans "Create Member" %}</h1>
|
||||
|
||||
|
||||
<form enctype="multipart/form-data" action="" method="post">
|
||||
<table width=100%>
|
||||
{{ form1.as_table }}
|
||||
{{ form2.as_table }}
|
||||
{{ form3.as_table }}
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
<div id="formbutton" class="grid_1 prefix_4 suffix_1">
|
||||
<input type="submit" value="{% trans 'Save' %}">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,38 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}{% trans "Edit Member" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="workouts" class="grid_6 alpha">
|
||||
<div class="grid_2 suffix_4 alpha">
|
||||
<p>
|
||||
<a class="button small red" href="/cvkbrno/members/{{ id }}/deleteconfirm">
|
||||
{% trans "Delete" %}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% if form.errors %}
|
||||
<p style="color: red;">
|
||||
Please correct the error{{ form.errors|pluralize }} below.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<h1>{% trans "Edit Member" %}</h1>
|
||||
|
||||
|
||||
<form enctype="multipart/form-data" action="" method="post">
|
||||
<table width=100%>
|
||||
{{ form1.as_table }}
|
||||
{{ form2.as_table }}
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
<div id="formbutton" class="grid_1 prefix_4 suffix_1">
|
||||
<input type="submit" value="{% trans 'Save' %}">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,53 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}{% trans "Members Admin" %}{% 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>{% trans "Members Admin" %}</h1>
|
||||
|
||||
|
||||
<div id="addboat" class="grid_2 suffix_4 alpha">
|
||||
<p class="greenbar">
|
||||
<a href="/cvkbrno/members/add">{% trans "Add Member" %}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% if themembers %}
|
||||
|
||||
<table class="listtable" width=100%>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Last Name" %}</th>
|
||||
<th>{% trans "First Name" %}</th>
|
||||
<th>{% trans "Contributions" %}</th>
|
||||
<th>{% trans "Edit" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for member in themembers %}
|
||||
<tr>
|
||||
<td>{{ member.user.last_name }}</td>
|
||||
<td>{{ member.user.first_name }}</td>
|
||||
<td>{{ member.contributions }}</td>
|
||||
<td><a href="/cvkbrno/members/{{ member.id }}/edit">E</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tbody>
|
||||
</table>
|
||||
|
||||
{% endif %}
|
||||
{% csrf_token %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,24 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}{% trans "Delete Member" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="workouts" class="grid_6 alpha">
|
||||
<div class="grid_2 suffix_4 alpha">
|
||||
<p class="deletelink">
|
||||
<a href="/cvkbrno/members/{{ id }}/delete">{% trans "Confirm Delete" %}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% if form.errors %}
|
||||
<p style="color: red;">
|
||||
Please correct the error{{ form.errors|pluralize }} below.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,53 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}{% trans "Members Admin" %}{% 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>{% trans "Members Admin" %}</h1>
|
||||
|
||||
|
||||
<div id="addboat" class="grid_2 suffix_4 alpha">
|
||||
<p>
|
||||
<a class="button green small" href="/cvkbrno/members/add">{% trans "Add Member" %}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% if themembers %}
|
||||
|
||||
<table class="listtable" width=100%>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Last Name" %}</th>
|
||||
<th>{% trans "First Name" %}</th>
|
||||
<th>{% trans "Contributions" %}</th>
|
||||
<th>{% trans "Edit" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for member in themembers %}
|
||||
<tr>
|
||||
<td>{{ member.user.last_name }}</td>
|
||||
<td>{{ member.user.first_name }}</td>
|
||||
<td>{{ member.contributions }}</td>
|
||||
<td><a href="/cvkbrno/members/{{ member.id }}/edit">E</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tbody>
|
||||
</table>
|
||||
|
||||
{% endif %}
|
||||
{% csrf_token %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,66 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}{% trans "Edit Member Work" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="workouts" class="grid_6 alpha">
|
||||
<div class="grid_2 suffix_4 alpha">
|
||||
<p>
|
||||
<a class="button red small" href="/cvkbrno/boats/{{ id }}/deleteconfirm">
|
||||
{% trans "Delete" %}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% if form.errors %}
|
||||
<p style="color: red;">
|
||||
Please correct the error{{ form.errors|pluralize }} below.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<h1>{% trans "Edit Boat" %}</h1>
|
||||
|
||||
|
||||
<form enctype="multipart/form-data" action="" method="post">
|
||||
<table width=100%>
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
<div id="formbutton" class="grid_1 prefix_4 suffix_1">
|
||||
<input class="button green" type="submit" value="{% trans 'Save' %}">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div id="outings" class="grid_6 omega">
|
||||
<h1>{% trans "Recent Outings with this boat" %}</h1>
|
||||
|
||||
<div class="grid_2 suffix_4 alpha">
|
||||
<p>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<table class="listtable" width=100%>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Date" %}</th>
|
||||
<th>{% trans "Rower" %}</th>
|
||||
<th>{% trans "Status" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for outing in theoutings %}
|
||||
<tr>
|
||||
<td>{{ outing.starttime }}</td>
|
||||
<td>{{ outing.rower.user.first_name }} {{ outing.rower.user.last_name }}</td>
|
||||
<td><a href="/cvkbrno/outing/{{ outing.id }}">{% trans outing.status %}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,50 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}{% trans "Meterboard" %}{% 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>{% trans "Meterboard" %}</h1>
|
||||
|
||||
|
||||
{{ data|safe }}
|
||||
|
||||
{% csrf_token %}
|
||||
</div>
|
||||
<div id="outings" class="grid_6 omega">
|
||||
<h1>{% trans "Recent Outings" %}</h1>
|
||||
|
||||
<table class="listtable" width=100%>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Date" %}</th>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Rower" %}</th>
|
||||
<th>{% trans "Status" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for outing in theoutings %}
|
||||
<tr>
|
||||
<td>{{ outing.starttime }}</td>
|
||||
<td>{{ outing.boat.boatname }}</td>
|
||||
<td>{{ outing.rower.user.first_name }} {{ outing.rower.user.last_name }}</td>
|
||||
<td><a href="/cvkbrno/outing/{{ outing.id }}">{% trans outing.status %}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tbody>
|
||||
</table>
|
||||
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,66 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}{% trans "View Outing" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="workouts" class="grid_6 alpha">
|
||||
<div class="grid_2 suffix_4 alpha">
|
||||
<p>
|
||||
<a class="button red small" href="/cvkbrno/boats/{{ id }}/deleteconfirm">
|
||||
{% trans "Delete" %}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% if form.errors %}
|
||||
<p style="color: red;">
|
||||
Please correct the error{{ form.errors|pluralize }} below.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<h1>{% trans "Edit Boat" %}</h1>
|
||||
|
||||
|
||||
<form enctype="multipart/form-data" action="" method="post">
|
||||
<table width=100%>
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
<div id="formbutton" class="grid_1 prefix_4 suffix_1">
|
||||
<input class="button green" type="submit" value="{% trans 'Save' %}">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div id="outings" class="grid_6 omega">
|
||||
<h1>{% trans "Recent Outings with this boat" %}</h1>
|
||||
|
||||
<div class="grid_2 suffix_4 alpha">
|
||||
<p>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<table class="listtable" width=100%>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Date" %}</th>
|
||||
<th>{% trans "Rower" %}</th>
|
||||
<th>{% trans "Status" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for outing in theoutings %}
|
||||
<tr>
|
||||
<td>{{ outing.starttime }}</td>
|
||||
<td>{{ outing.rower.user.first_name }} {{ outing.rower.user.last_name }}</td>
|
||||
<td><a href="/cvkbrno/outing/{{ outing.id }}">{% trans outing.status %}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,40 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}{% trans "Return from outing" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="workouts" class="grid_12 alpha">
|
||||
|
||||
{% if form.errors %}
|
||||
<p style="color: red;">
|
||||
Please correct the error{{ form.errors|pluralize }} below.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<h1>{% trans "Return from outing" %}</h1>
|
||||
<h2>{{ outing.boat.boatcode }} {{ outing.boat.boatname }}</h2>
|
||||
<p>{% trans "Please check the boat" %}</p>
|
||||
|
||||
<form enctype="multipart/form-data" action="" method="post">
|
||||
<table width=100%>
|
||||
<tr>
|
||||
<th><label for="id_damagecheck">{% trans "Is boat OK?" %}</label></th>
|
||||
<td><select id="id_damagecheck" name="damagecheck">
|
||||
<option value="Yes" selected="selected">{% trans 'Yes' %}</option>
|
||||
<option value="No">{% trans 'No' %}</option>
|
||||
</select></td>
|
||||
</tr>
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
<div id="formbutton" class="grid_1 prefix_4 suffix_1">
|
||||
<input type="submit" value="{% trans 'Save' %}">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,182 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
{% load tz %}
|
||||
|
||||
{% block title %}{% trans "Going Rowing" %} {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% localtime on %}
|
||||
<script type="text/JavaScript">
|
||||
var themembers = {{ jsonmembers|safe }};
|
||||
|
||||
function getformfields(nrseat) {
|
||||
var s = "<table>"
|
||||
for (i=2;i<=nrseat;i++) {
|
||||
s+='<tr><td>'
|
||||
s+=i
|
||||
s+=': </td><td><select id="idcrew'
|
||||
s+=i
|
||||
s+='" name="crew'
|
||||
s+=i
|
||||
s+='"><option disabled selected value> -- </option>'
|
||||
for (var id in themembers) {
|
||||
s+= "<option value="
|
||||
s+= id
|
||||
s+= ">"+themembers[id]+"</option>"
|
||||
}
|
||||
s+= '</select></td></tr>'
|
||||
}
|
||||
s+= "</table>"
|
||||
return s
|
||||
}
|
||||
</script>
|
||||
|
||||
<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>{% trans "Going Rowing" %}</h1>
|
||||
|
||||
|
||||
<form enctype="multipart/form-data" action="" method="post" content="text/html;charset=UTF-8">
|
||||
<table width=100%>
|
||||
{{ form.as_table }}
|
||||
<tr><th><label for="id_boat">{% trans "Boat" %}:</label></th>
|
||||
<td><select id="id_boat" name="boat"
|
||||
onchange="document.getElementById('crew').innerHTML =
|
||||
getformfields(this.options[this.selectedIndex].getAttribute('data-nrseat'));"
|
||||
onload="document.getElementById('crew').innerHTML =
|
||||
getformfields(this.options[this.selectedIndex].getAttribute('data-nrseat'));">
|
||||
|
||||
|
||||
{% if selectedboat %}
|
||||
<option value="{{ selectedboat.id }}" data-nrseat="{{ selectedboat.nrseats }}" selected="selected">
|
||||
{{ selectedboat.boatcode }} {{ selectedboat.boatname }}
|
||||
({{ selectedboat.nrseats }})</option>
|
||||
{% else %}
|
||||
<option value="" selected="selected">---------</option>
|
||||
{% endif %}
|
||||
{% for boat in theboats %}
|
||||
<option value="{{ boat.id }}" data-nrseat="{{ boat.nrseats }}">{{ boat.boatcode }} {{ boat.boatname }} ({{ boat.nrseats }})</option>
|
||||
{% endfor %}
|
||||
</select></td></tr>
|
||||
</table>
|
||||
<p> </p>
|
||||
|
||||
<p>1: {{ user.first_name }} {{ user.last_name }}</p>
|
||||
<p id="crew"></p>
|
||||
{% csrf_token %}
|
||||
<div id="formbutton" class="grid_1 prefix_4 suffix_1">
|
||||
<input class="button green" type="submit" value="{% trans 'Save' %}">
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
<div id="reservations" class="grid_6 omega">
|
||||
<h1>{% trans "Reservations" %}</h1>
|
||||
|
||||
<table class="listtable" width=100%>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Boat" %}</th>
|
||||
<th>{% trans "Departure time" %}</th>
|
||||
<th>{% trans "Reserved by" %}</th>
|
||||
<th>{% trans "Delete" %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for outing in reservations %}
|
||||
<tr>
|
||||
<td> {{ outing.boat.boatname }} </td>
|
||||
<td> {{ outing.starttime }} </td>
|
||||
<td> {{ outing.rower.user.first_name }} {{ outing.rower.user.last_name }} </td>
|
||||
{% if user == outing.rower.user or thisclub_admin %}
|
||||
<td><a href="/cvkbrno/outing/{{ outing.id }}/delete">D</a></td>
|
||||
{% else %}
|
||||
<td> </td<
|
||||
{% endif %}
|
||||
<td>
|
||||
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="damaged" class="grid_6 omega">
|
||||
{% if damagedboats %}
|
||||
<h1>{% trans "Temporarily Unavailable" %}</h1>
|
||||
<table class="listtable" width=100%>
|
||||
<tbody>
|
||||
{% for boat in damagedboats %}
|
||||
<tr>
|
||||
<td> {{ boat.boatname }} </td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% if races %}
|
||||
<h1>{% trans "Upcoming Races" %}</h1>
|
||||
<table class="listtable" width=100%>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Start Date" %}</th>
|
||||
<th>{% trans "End Date" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for race in races %}
|
||||
<tr>
|
||||
<td>{{ race.name }}</td>
|
||||
<td>{{ race.startdatetime }}</td>
|
||||
<td>{{ race.enddatetime }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div id="plannedwork" class="grid_6 omega">
|
||||
{% if plannedwork %}
|
||||
<h1>{% trans "Upcoming Members Work" %}</h1>
|
||||
<table class="listtable" width=100%>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Date" %}</th>
|
||||
<th>{% trans "Action" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for w in plannedwork %}
|
||||
<tr>
|
||||
<td>{{ w.name }}</td>
|
||||
<td>{{ w.date }}</td>
|
||||
{% if member in w.worker.all %}
|
||||
<td><a class="button red small" href="/cvkbrno/memberswork/{{ w.id }}/removeme">{% trans "Remove me" %}</a></td>
|
||||
{% else %}
|
||||
<td><a class="button green small" href="/cvkbrno/memberswork/{{ w.id }}/addme">{% trans "Add me" %}</a></td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endlocaltime %}
|
||||
{% endblock %}
|
||||
@@ -1,31 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}{% trans "Create Races Event" %}{% 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>{% trans "Create Races Event" %}</h1>
|
||||
|
||||
|
||||
<form enctype="multipart/form-data" action="" method="post">
|
||||
<table width=100%>
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
<div id="formbutton" class="grid_1 prefix_4 suffix_1">
|
||||
<input type="submit" value="Save">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,45 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}{% trans "Races Admin" %}{% 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>{% trans "Races Admin" %}</h1>
|
||||
|
||||
|
||||
<div id="addcategory" class="grid_2 suffix_4 alpha">
|
||||
<p>
|
||||
<a class="button small green" href="/cvkbrno/transport/add">{% trans "Add Race Event" %}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<table class="listtable" width=100%>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Edit" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for race in races %}
|
||||
<tr>
|
||||
<td>{{ race.name }}</td>
|
||||
<td><a href="/cvkbrno/transport/{{ race.id }}/edit">E</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tbody>
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,74 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}{% trans "Make a Reservation" %}{% 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>{% trans "Make a reservation" %}</h1>
|
||||
|
||||
|
||||
<form enctype="multipart/form-data" action="" method="post">
|
||||
<table width=100%>
|
||||
{{ form.as_table }}
|
||||
<tr><th><label for="id_boat">{% trans "Boat:" %}</label></th>
|
||||
<td><select id="id_boat" name="boat">
|
||||
<option value="" selected="selected">---------</option>
|
||||
{% for boat in theboats %}
|
||||
<option value="{{ boat.id }}">{{boat.boatcode}} {{ boat.boatname }}</option>
|
||||
{% endfor %}
|
||||
</select></td>
|
||||
</tr>
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
<div id="formbutton" class="grid_1 prefix_4 suffix_1">
|
||||
<input class="button green" type="submit" value="{% trans 'Save' %}">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div id="reservations" class="grid_6 omega">
|
||||
<h1>{% trans "Reservations" %}</h1>
|
||||
|
||||
<table class="listtable" width=100%>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Boat" %}</th>
|
||||
<th>{% trans "Date" %}</th>
|
||||
<th>{% trans "Departure time" %}</th>
|
||||
<th>{% trans "Return time" %}</th>
|
||||
<th>{% trans "Reserved by" %}</th>
|
||||
<th>{% trans "Delete" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for outing in reservations %}
|
||||
<tr>
|
||||
<td> {{ outing.boat.boatname }} </td>
|
||||
<td> {{ outing.starttime |date:'d/m/y ' }} </td>
|
||||
<td> {{ outing.starttime |time:'H:i ' }} </td>
|
||||
<td> {{ outing.endtime |time:'H:i '}} </td>
|
||||
<td> {{ outing.rower.user.first_name }} {{ outing.rower.user.last_name }} </td>
|
||||
{% if user == outing.rower.user or thisclub_admin %}
|
||||
<td><a href="/cvkbrno/outing/{{ outing.id }}/delete">D</a></td>
|
||||
{% else %}
|
||||
<td> </td<
|
||||
{% endif %}
|
||||
<td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,24 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}Jdu na vodu {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% load i18n %}
|
||||
|
||||
<form action="{% url 'set_language' %}" method="post">{% csrf_token %}
|
||||
<input name="next" type="hidden" value="{{ redirect_to }}" />
|
||||
<select name="language">
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% get_available_languages as LANGUAGES %}
|
||||
{% get_language_info_list for LANGUAGES as languages %}
|
||||
{% for language in languages %}
|
||||
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
|
||||
{{ language.name_local }} ({{ language.code }})
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -1,193 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
{% load i18n %}
|
||||
{% load tz %}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% language LANGUAGE_CODE %}
|
||||
{% get_available_languages as LANGUAGES %}
|
||||
{% get_current_language_bidi as LANGUAGE_BIDI %}
|
||||
<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>CVK Brno</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" />
|
||||
|
||||
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
|
||||
{% block meta %} {% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<div class="container_12">
|
||||
<div id="logo" class="grid_2">
|
||||
<p><a href="/cvkbrno"><img src="/static/img/cvkbrnologo.gif"
|
||||
alt="Rowsandall logo" width="160" heigt="160"></a></p>
|
||||
</div>
|
||||
<div class="grid_10 omega">
|
||||
<div class="grid_5 alpha">
|
||||
<h2>{% trans "Welcome," %} {{ user.first_name }}</h2>
|
||||
</div>
|
||||
<div class="grid_1">
|
||||
<a href="/cvkbrno/language/cs/">
|
||||
<img src="/static/flags_iso/24/cz.png" alt="CZ flag">
|
||||
</a>
|
||||
</div>
|
||||
<div class="grid_1">
|
||||
<a href="/cvkbrno/language/en-GB/">
|
||||
<img src="/static/flags_iso/24/gb.png" alt="EN flag">
|
||||
</a>
|
||||
</div>
|
||||
<div class="grid_1">
|
||||
<a href="/cvkbrno/language/nl/">
|
||||
<img src="/static/flags_iso/24/nl.png" alt="NL flag">
|
||||
</a>
|
||||
</div>
|
||||
<div class="grid_2 omega">
|
||||
{% if user.is_authenticated %}
|
||||
<p><a class="button gray" href="/password_change/">
|
||||
{% trans "Password Change" %}</a></p>
|
||||
{% else %}
|
||||
<p><a class="button gray small" href="/password_reset/">{% trans "Forgotten Password?" %}</a></p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid_10">
|
||||
<div class="grid_2 alpha">
|
||||
<p>{% trans "Worked hours" %}: {{ worked_hours }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2">
|
||||
{% if user.is_authenticated %}
|
||||
<p>
|
||||
<a class="button blue small" href="/cvkbrno/meterboard">
|
||||
{% trans "Meterboard" %}
|
||||
</a>
|
||||
</p>
|
||||
{% else %}
|
||||
<p> </p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="grid_2 prefix_3">
|
||||
{% if thisclub_admin %}
|
||||
<p>
|
||||
<a class="button rosy small" href="/cvkbrno/memberswork">
|
||||
{% trans "Members Work" %}</a>
|
||||
</p>
|
||||
{% else %}
|
||||
<p> </p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="grid_10" omega>
|
||||
<div class="grid_2 alpha">
|
||||
{% if user.is_authenticated %}
|
||||
<p>
|
||||
<a class="button blue" href="/cvkbrno/createouting">
|
||||
{% trans "Go Rowing" %}</a>
|
||||
</p>
|
||||
{% else %}
|
||||
<p> </p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="grid_2">
|
||||
{% if user.is_authenticated %}
|
||||
<p>
|
||||
<a class="button blue" href="/cvkbrno/makereservation">
|
||||
{% trans "Reservations" %}</a>
|
||||
</p>
|
||||
{% else %}
|
||||
<p> </p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="grid_1">
|
||||
{% if thisclub_admin %}
|
||||
<p><a class="button rosy" href="/cvkbrno/boats">
|
||||
{% trans "Boats" %}</a></p>
|
||||
{% else %}
|
||||
<p> </p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="grid_1">
|
||||
{% if thisclub_admin %}
|
||||
<p><a class="button rosy" href="/cvkbrno/members">
|
||||
{% trans "Members" %}</a></p>
|
||||
{% else %}
|
||||
<p> </p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="grid_1">
|
||||
{% if thisclub_admin %}
|
||||
<p><a class="button rosy" href="/cvkbrno/transport">
|
||||
{% trans "Transport" %}</a></p>
|
||||
{% else %}
|
||||
<p> </p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="grid_2">
|
||||
{% if thisclub_admin %}
|
||||
<p><a class="button rosy" href="/cvkbrno/categories">
|
||||
{% trans "Categories" %}</a></p>
|
||||
{% else %}
|
||||
<p> </p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="grid_1 omega">
|
||||
{% if user.is_authenticated %}
|
||||
<p><a class="button blue" href="/cvkbrno/logout/">{% trans "logout" %}</a></p>
|
||||
|
||||
{% else %}
|
||||
<p> </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">
|
||||
{% timezone "Europe/Paris" %}
|
||||
{% block content %}{% endblock %}
|
||||
{% endtimezone %}
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
|
||||
<div class="grid_12 omega" >
|
||||
{% block footer %}
|
||||
<p id="footer"
|
||||
>{{ versionstring }}</p>
|
||||
<div class="grid_3 prefix_1 suffix_1 alpha">
|
||||
<p id="footer">© Sander Roosendaal</p>
|
||||
</div>
|
||||
<div class="grid_2">
|
||||
<p id="footer">About</p>
|
||||
</div>
|
||||
<div class="grid_1 prefix_2 suffix_2 omega">
|
||||
<p id="footer">Contact</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
<!-- end container -->
|
||||
</body>
|
||||
{% endlanguage %}
|
||||
</html>
|
||||
@@ -1,53 +0,0 @@
|
||||
{% extends "cvkbrnobase.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% if form.errors %}
|
||||
<p>Your username and password didn't match. Please try again.</p>
|
||||
{% endif %}
|
||||
|
||||
<form method="post" action="{% url 'django.contrib.auth.views.login' %}">
|
||||
{% csrf_token %}
|
||||
<table>
|
||||
<tr>
|
||||
<td>{{ form.username.label_tag }}</td>
|
||||
<td>{{ form.username }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ form.password.label_tag }}</td>
|
||||
<td>{{ form.password }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<input type="submit" value="login" />
|
||||
|
||||
{% if next %}
|
||||
<input type="hidden" name="next" value="{{ next }}" />
|
||||
{% else %}
|
||||
<input type="hidden" name="next" value="." />
|
||||
{% endif %}
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
<h1>Lodě na vodě</h1>
|
||||
|
||||
<table class="listtable" width=100%>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Boat</th>
|
||||
<th>Departure time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for outing in activeoutings %}
|
||||
<tr>
|
||||
<td> {{ outing.boat.boatname }} </td>
|
||||
<td> {{ outing.starttime }} </td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,7 +0,0 @@
|
||||
from django import template
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@register.filter
|
||||
def filteriternum(d):
|
||||
return d+1
|
||||
@@ -1,3 +0,0 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
@@ -1,96 +0,0 @@
|
||||
from django.conf import settings
|
||||
from django.conf.urls import url
|
||||
|
||||
from . import views
|
||||
from django.contrib.auth import views as auth_views
|
||||
|
||||
from forms import BoatEditForm
|
||||
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
# url(r'^login$',auth_views.login,
|
||||
# {'template_name':'registration/cvkbrno_login.html',
|
||||
# 'extra_context':{
|
||||
# 'activeoutings':views.get_active_outings(),
|
||||
# 'reservations':views.get_future_reservations(),
|
||||
# },
|
||||
# }),
|
||||
url(r'^login/$',views.login_user_view),
|
||||
url(r'^logout/$',views.logout_then_login),
|
||||
url(r'^meterboard/$',views.meterboard_view),
|
||||
url(r'^meterboard$',views.meterboard_view),
|
||||
url(r'^memberswork/c/(?P<message>\w+.*)/s/(?P<successmessage>\w+.*)$',views.members_work_admin_view),
|
||||
url(r'^memberswork/s/(?P<successmessage>\w+.*)$',views.members_work_admin_view),
|
||||
url(r'^memberswork/c/(?P<message>\w+.*)$',views.members_work_admin_view),
|
||||
url(r'^memberswork/add$',views.members_work_add_view),
|
||||
url(r'^memberswork/(?P<id>\d+)/addme$',views.members_work_addme),
|
||||
url(r'^memberswork/(?P<id>\d+)/removeme$',views.members_work_removeme),
|
||||
url(r'^memberswork/$',views.members_work_admin_view),
|
||||
url(r'^memberswork/(?P<id>\d+)/edit/c/(?P<message>\w+.*)/s/(?P<successmessage>\w+.*)$',views.members_work_edit_view),
|
||||
url(r'^memberswork/(?P<id>\d+)/edit/s/(?P<successmessage>\w+.*)$',views.members_work_edit_view),
|
||||
url(r'^memberswork/(?P<id>\d+)/edit$',views.members_work_edit_view),
|
||||
url(r'^memberswork/(?P<id>\d+)/edit/c/(?P<message>\w+.*)$',views.members_work_edit_view),
|
||||
url(r'^memberswork/add$',views.members_work_add_view),
|
||||
url(r'^memberswork/(?P<id>\d+)/delete$',views.members_work_delete_view),
|
||||
url(r'^memberswork/$',views.members_work_admin_view),
|
||||
url(r'^memberswork/(?P<id>\d+)/complete$',views.members_work_complete_view),
|
||||
url(r'^createouting/c/(?P<message>\w+.*)/s/(?P<successmessage>\w+.*)$',views.createouting_view),
|
||||
url(r'^createouting/c/(?P<message>\w+.*)$',views.createouting_view),
|
||||
url(r'^createouting$',views.createouting_view),
|
||||
url(r'^closeouting/c/(?P<message>\w+.*)$',views.closeouting_view),
|
||||
url(r'^closeouting$',views.closeouting_view),
|
||||
url(r'^makereservation/c/(?P<message>\w+.*)/s/(?P<successmessage>\w+.*)$',views.makereservation_view),
|
||||
url(r'^makereservation/c/(?P<message>\w+.*)$',views.makereservation_view),
|
||||
url(r'^makereservation$',views.makereservation_view),
|
||||
url(r'^transport/c/(?P<message>\w+.*)/s/(?P<successmessage>\w+.*)$',views.transport_view),
|
||||
url(r'^transport/c/(?P<message>\w+.*)$',views.transport_view),
|
||||
url(r'^transport/s/(?P<successmessage>\w+.*)$',views.transport_view),
|
||||
url(r'^transport$',views.transport_view),
|
||||
url(r'^transport/add$',views.transport_add_view),
|
||||
url(r'^transport/(?P<id>\d+)/delete$',views.races_admin_delete_view),
|
||||
# url(r'^c/(?P<message>\w+.*)/s/(?P<successmessage>\w+.*)$',views.base_view),
|
||||
# url(r'^c/(?P<message>\w+.*)$',views.base_view),
|
||||
# url(r'^s/(?P<successmessage>\w+.*)$',views.base_view),
|
||||
url(r'^$',views.base_view),
|
||||
url(r'^outing/(?P<id>\d+)$',views.outing_admin_view),
|
||||
url(r'^boats/c/(?P<message>\w+.*)/s/(?P<successmessage>\w+.*)$',views.boats_admin_view),
|
||||
url(r'^boats/(?P<id>\d+)/edit/c/(?P<message>\w+.*)/s/(?P<successmessage>\w+.*)$',views.boats_admin_edit_view),
|
||||
url(r'^boats/(?P<id>\d+)/edit/c/(?P<message>\w+.*)$',views.boats_admin_edit_view),
|
||||
url(r'^boats/(?P<id>\d+)/edit$',views.boats_admin_edit_view),
|
||||
url(r'^boats/c/(?P<message>\w+.*)$',views.boats_admin_view),
|
||||
url(r'^boats/s/(?P<successmessage>\w+.*)$',views.boats_admin_view),
|
||||
url(r'^boats$',views.boats_admin_view),
|
||||
url(r'^boats/(\d+)/deleteconfirm$',views.boats_admin_delete_confirm_view),
|
||||
url(r'^boats/(\d+)/delete$',views.boats_admin_delete_view),
|
||||
url(r'^boats/add/c/(?P<message>\w+.*)$',views.boats_admin_add_view),
|
||||
url(r'^boats/add$',views.boats_admin_add_view),
|
||||
url(r'^categories/c/(?P<message>\w+.*)/s/(?P<successmessage>\w+.*)$',views.categories_admin_view),
|
||||
url(r'^categories/(?P<id>\d+)/edit/c/(?P<message>\w+.*)/s/(?P<successmessage>\w+.*)$',views.categories_admin_edit_view),
|
||||
url(r'^categories/(?P<id>\d+)/edit/c/(?P<message>\w+.*)$',views.categories_admin_edit_view),
|
||||
url(r'^categories/(?P<id>\d+)/edit$',views.categories_admin_edit_view),
|
||||
url(r'^categories/c/(?P<message>\w+.*)$',views.categories_admin_view),
|
||||
url(r'^categories/s/(?P<successmessage>\w+.*)$',views.categories_admin_view),
|
||||
url(r'^categories$',views.categories_admin_view),
|
||||
url(r'^categories/(\d+)/deleteconfirm$',views.categories_admin_delete_confirm_view),
|
||||
url(r'^categories/(\d+)/delete$',views.categories_admin_delete_view),
|
||||
url(r'^categories/add/c/(?P<message>\w+.*)$',views.categories_admin_add_view),
|
||||
url(r'^categories/add$',views.categories_admin_add_view),
|
||||
|
||||
url(r'^members/c/(?P<message>\w+.*)/s/(?P<successmessage>\w+.*)$',views.members_admin_view),
|
||||
url(r'^members/(?P<id>\d+)/edit/c/(?P<message>\w+.*)/s/(?P<successmessage>\w+.*)$',views.members_admin_edit_view),
|
||||
url(r'^members/(?P<id>\d+)/edit/c/(?P<message>\w+.*)$',views.members_admin_edit_view),
|
||||
url(r'^members/(?P<id>\d+)/edit$',views.members_admin_edit_view),
|
||||
url(r'^members/c/(?P<message>\w+.*)$',views.members_admin_view),
|
||||
url(r'^members/s/(?P<successmessage>\w+.*)$',views.members_admin_view),
|
||||
url(r'^members$',views.members_admin_view),
|
||||
url(r'^members/(\d+)/deleteconfirm$',views.members_admin_delete_confirm_view),
|
||||
url(r'^members/(\d+)/delete$',views.members_admin_delete_view),
|
||||
url(r'^members/add/c/(?P<message>\w+.*)$',views.members_admin_add_view),
|
||||
url(r'^members/add$',views.members_admin_add_view),
|
||||
url(r'^outing/(\d+)/delete$',views.members_outing_delete_view),
|
||||
url(r'^language/(?P<languagecode>[A-z]{2})/$',views.change_language_view),
|
||||
url(r'^language/(?P<languagecode>[A-z]{2}\-[A-z]{2})/$',views.change_language_view),
|
||||
# url(r'^i18n/', include('django.conf.urls.i18n')),
|
||||
]
|
||||
|
||||
-1763
File diff suppressed because it is too large
Load Diff
@@ -1,7 +0,0 @@
|
||||
#: .\rowsandall_app\settings.py:135
|
||||
msgid "Czech"
|
||||
msgstr ""
|
||||
|
||||
#: .\rowsandall_app\settings.py:136
|
||||
msgid "Dutch"
|
||||
msgstr ""
|
||||
@@ -1,263 +0,0 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2016-05-19 08:27+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: .\cvkbrno\models.py:16
|
||||
msgid "Boat Category"
|
||||
msgstr "Kategorie Lodí"
|
||||
|
||||
#: .\cvkbrno\models.py:19
|
||||
msgid "boat category"
|
||||
msgstr "kategorie lodí"
|
||||
|
||||
#: .\cvkbrno\models.py:20
|
||||
msgid "boat categories"
|
||||
msgstr "kategorie lodí"
|
||||
|
||||
#: .\cvkbrno\models.py:34
|
||||
msgid "coach"
|
||||
msgstr "trenér"
|
||||
|
||||
#: .\cvkbrno\models.py:35 .\cvkbrno\models.py:40
|
||||
msgid "member"
|
||||
msgstr "clen"
|
||||
|
||||
#: .\cvkbrno\models.py:36
|
||||
msgid "administrator"
|
||||
msgstr "správce"
|
||||
|
||||
#: .\cvkbrno\models.py:41
|
||||
msgid "members"
|
||||
msgstr "clenové"
|
||||
|
||||
#: .\cvkbrno\models.py:49
|
||||
msgid "Boat Categories"
|
||||
msgstr "Kategorie Lodí"
|
||||
|
||||
#: .\cvkbrno\models.py:50
|
||||
msgid "Club"
|
||||
msgstr "Klub"
|
||||
|
||||
#: .\cvkbrno\models.py:51
|
||||
msgid "Hours Worked"
|
||||
msgstr "Odpracované hodiny"
|
||||
|
||||
#: .\cvkbrno\models.py:63
|
||||
msgid "boat"
|
||||
msgstr "lod"
|
||||
|
||||
#: .\cvkbrno\models.py:64
|
||||
msgid "boats"
|
||||
msgstr "lode"
|
||||
|
||||
#: .\cvkbrno\models.py:66
|
||||
msgid "Boat Name"
|
||||
msgstr "název lodi"
|
||||
|
||||
#: .\cvkbrno\models.py:67
|
||||
msgid "Boat Code"
|
||||
msgstr "Kód Lode"
|
||||
|
||||
#: .\cvkbrno\models.py:69
|
||||
msgid "Nr of Seats"
|
||||
msgstr "Pocet míst"
|
||||
|
||||
#: .\cvkbrno\models.py:71
|
||||
msgid "water"
|
||||
msgstr "na vode"
|
||||
|
||||
#: .\cvkbrno\models.py:72
|
||||
msgid "hangar"
|
||||
msgstr "v hangaru"
|
||||
|
||||
#: .\cvkbrno\models.py:73 .\cvkbrno\views.py:656
|
||||
msgid "damaged"
|
||||
msgstr "poškozená"
|
||||
|
||||
#: .\cvkbrno\models.py:74
|
||||
msgid "races"
|
||||
msgstr "na závodech"
|
||||
|
||||
#: .\cvkbrno\models.py:94 .\cvkbrno\models.py:95
|
||||
msgid "member work"
|
||||
msgstr "brigády"
|
||||
|
||||
#: .\cvkbrno\models.py:97
|
||||
msgid "Date"
|
||||
msgstr "Datum"
|
||||
|
||||
#: .\cvkbrno\models.py:98
|
||||
msgid "Hours"
|
||||
msgstr "Hodiny"
|
||||
|
||||
#: .\cvkbrno\models.py:99
|
||||
msgid "Work Event Name"
|
||||
msgstr "Název Brigády"
|
||||
|
||||
#: .\cvkbrno\models.py:102
|
||||
msgid "planned"
|
||||
msgstr "plánovaná"
|
||||
|
||||
#: .\cvkbrno\models.py:103
|
||||
msgid "executed"
|
||||
msgstr "hotová"
|
||||
|
||||
#: .\cvkbrno\models.py:111
|
||||
msgid "outing"
|
||||
msgstr "výjezd"
|
||||
|
||||
#: .\cvkbrno\models.py:112
|
||||
msgid "outings"
|
||||
msgstr "výjezdy"
|
||||
|
||||
#: .\cvkbrno\models.py:115
|
||||
msgid "Start Date/Time"
|
||||
msgstr "Startovní Datum/Cas"
|
||||
|
||||
#: .\cvkbrno\models.py:116
|
||||
msgid "End Date/Time"
|
||||
msgstr "Datum/Cas ukoncení"
|
||||
|
||||
#: .\cvkbrno\models.py:117
|
||||
msgid "Distance"
|
||||
msgstr "Vzdálenost"
|
||||
|
||||
#: .\cvkbrno\models.py:118
|
||||
msgid "Comment"
|
||||
msgstr "Komentár"
|
||||
|
||||
#: .\cvkbrno\models.py:120
|
||||
msgid "reservation"
|
||||
msgstr "rezervace"
|
||||
|
||||
#: .\cvkbrno\models.py:121
|
||||
msgid "active"
|
||||
msgstr "aktivní"
|
||||
|
||||
#: .\cvkbrno\models.py:122
|
||||
msgid "completed"
|
||||
msgstr "dokoncený"
|
||||
|
||||
#: .\cvkbrno\models.py:123
|
||||
msgid "race"
|
||||
msgstr "závod"
|
||||
|
||||
#: .\cvkbrno\models.py:127
|
||||
msgid "Status"
|
||||
msgstr "Status"
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:26
|
||||
msgid "Welcome,"
|
||||
msgstr "Vítej,"
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:30
|
||||
msgid "Password Change"
|
||||
msgstr "Zmenit Heslo"
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:42
|
||||
msgid "Members Work"
|
||||
msgstr "Brigády"
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:56
|
||||
msgid "Go Rowing"
|
||||
msgstr "Jdi na vodu"
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:66
|
||||
msgid "Reservations"
|
||||
msgstr "Rezervace"
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:75
|
||||
msgid "Boats"
|
||||
msgstr "Lode"
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:83
|
||||
msgid "Members"
|
||||
msgstr "Clenové"
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:91
|
||||
msgid "Transport"
|
||||
msgstr "Doprava"
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:99
|
||||
msgid "Categories"
|
||||
msgstr "Kategorie"
|
||||
|
||||
#: .\cvkbrno\templates\cvkbrnobase.html:106
|
||||
msgid "logout"
|
||||
msgstr "odhlásit"
|
||||
|
||||
#: .\cvkbrno\views.py:240
|
||||
msgid "boat deleted"
|
||||
msgstr "lod smazána"
|
||||
|
||||
#: .\cvkbrno\views.py:254
|
||||
msgid "member deleted"
|
||||
msgstr "clen smazán"
|
||||
|
||||
#: .\cvkbrno\views.py:268
|
||||
msgid "category deleted"
|
||||
msgstr "kategorie smazána"
|
||||
|
||||
#: .\cvkbrno\views.py:299 .\cvkbrno\views.py:393 .\cvkbrno\views.py:440
|
||||
#: .\cvkbrno\views.py:488
|
||||
msgid "invalid form"
|
||||
msgstr "neplatný formulár"
|
||||
|
||||
#: .\cvkbrno\views.py:532 .\cvkbrno\views.py:538 .\cvkbrno\views.py:916
|
||||
msgid "Changes saved"
|
||||
msgstr "Zmeny uloženy"
|
||||
|
||||
#: .\cvkbrno\views.py:638
|
||||
msgid "You have no active outings. Go rowing first."
|
||||
msgstr "Nemáte žádné aktivní výjezdy. Jdete nejdríve na vodu."
|
||||
|
||||
#: .\cvkbrno\views.py:644
|
||||
msgid "You are not out. Go rowing first."
|
||||
msgstr "Nejste na vode. Jdete nejdríve na vodu."
|
||||
|
||||
#: .\cvkbrno\views.py:709
|
||||
msgid "Conflicting reservations exist"
|
||||
msgstr "Konflikt rezervací"
|
||||
|
||||
#: .\cvkbrno\views.py:726
|
||||
msgid "Invalid Form"
|
||||
msgstr "Neplatný Formulár"
|
||||
|
||||
#: .\cvkbrno\views.py:742
|
||||
msgid "Make a reservation"
|
||||
msgstr "Rezervujte"
|
||||
|
||||
#: .\cvkbrno\views.py:768 .\cvkbrno\views.py:809
|
||||
msgid "There is already a boat with that code"
|
||||
msgstr "Již existuje jiná lod s tímto kódem."
|
||||
|
||||
#: .\cvkbrno\views.py:818
|
||||
msgid "Changes Saved"
|
||||
msgstr "Zmeny Uloženy"
|
||||
|
||||
#: .\cvkbrno\views.py:843
|
||||
msgid "The two passwords are not equal"
|
||||
msgstr "Nazadal jste stejná hesla."
|
||||
|
||||
#: .\cvkbrno\views.py:850
|
||||
msgid "There is already a user with that username"
|
||||
msgstr "Uživatel s tímto uživatelském jménem již existuje"
|
||||
|
||||
#: .\cvkbrno\views.py:875
|
||||
msgid "New Member Created"
|
||||
msgstr "Nový clen vytvoren"
|
||||
@@ -1,9 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block title %}Here's your plot{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
Waiting for the task to complete.
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{% block title %}{% endblock %}</title>
|
||||
{% block meta %} {% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<h1>My helpful rowing site</h1>
|
||||
{% block content %}{% endblock %}
|
||||
{% block footer %}
|
||||
<hr>
|
||||
<p>{{ versionstring }}.</p>
|
||||
<p>Thanks for visiting my site.</p>
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,24 +0,0 @@
|
||||
# contact_form.html
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Contact us</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Contact us</h1>
|
||||
|
||||
{% if form.errors %}
|
||||
<p style="color: red;">
|
||||
Please correct the error{{ form.errors|pluralize }} below.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<form action="" method="post">
|
||||
<table>
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
<h1>It is now {{ current_date }} </h1>
|
||||
@@ -1,21 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block title %}File loading{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>File Upload</h1>
|
||||
{% if form.errors %}
|
||||
<p style="color: red;">
|
||||
Please correct the error{{ form.errors|pluralize }} below.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
|
||||
<table>
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="Submit">
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,7 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Current time{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>It is now {{ current_date }}.</p>
|
||||
{% endblock %}
|
||||
@@ -1,10 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block title %}Here's your plot{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1> Here's your plot</h1>
|
||||
<p><image src="{% static imagename %}"/></p>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block title %}Workouts{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>My Workouts</h1>
|
||||
{% if workouts %}
|
||||
<table width="70%">
|
||||
<tr>
|
||||
<td> <strong>Date</strong> </td>
|
||||
<td> <strong>Time </strong></td>
|
||||
<td> <strong>Name </strong></td>
|
||||
<td> <strong>Type </strong></td>
|
||||
<td> <strong>Distance</strong> </td>
|
||||
<td> <strong>Duration</strong> </td>
|
||||
<td> <strong>Edit</strong></td>
|
||||
</tr>
|
||||
|
||||
{% for workout in workouts %}
|
||||
<tr>
|
||||
<td> {{ workout.date }} </td>
|
||||
<td> {{ workout.starttime }} </td>
|
||||
<td> {{ workout.name }} </td>
|
||||
<td> {{ workout.workouttype }} </td>
|
||||
<td> {{ workout.distance }}m</td>
|
||||
<td> {{ workout.duration }} </td>
|
||||
<td> <a href="/rowers/workout/{{ workout.id }}/edit">E</td>
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<p> No workouts found </p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -1,22 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>File selection</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Select File</h1>
|
||||
|
||||
{% if form.errors %}
|
||||
<p style="color: red;">
|
||||
Please correct the error{{ form.errors|pluralize }} below.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
|
||||
<table>
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,19 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Change Rower {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if form.errors %}
|
||||
<p style="color: red;">
|
||||
Please correct the error{{ form.errors|pluralize }} below.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<form enctype="multipart/form-data" action="" method="post">
|
||||
<table>
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="Submit">
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,7 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Rowingdata Version{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>{{ versionstring }}.</p>
|
||||
{% endblock %}
|
||||
@@ -1,11 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block title %}Waiting{% endblock %}
|
||||
{% block meta %}<meta http-equiv="refresh" content="30">{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Waiting for your image to be processed</h1>
|
||||
<p> {{ message }} </p>
|
||||
<p> Page should refresh automatically (or you can hit reload)</p>
|
||||
{% endblock %}
|
||||
@@ -1,19 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Change Workout {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if form.errors %}
|
||||
<p style="color: red;">
|
||||
Please correct the error{{ form.errors|pluralize }} below.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<form enctype="multipart/form-data" action="" method="post">
|
||||
<table>
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
<input type="submit" value="Submit">
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,3 +0,0 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .tasks import app as celery_app
|
||||
@@ -1,23 +0,0 @@
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.admin import UserAdmin
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from .models import Rower, Workout,GraphImage
|
||||
|
||||
# Register your models here.
|
||||
|
||||
class RowerInline(admin.StackedInline):
|
||||
model = Rower
|
||||
can_delete = False
|
||||
verbose_name_plural = 'rower'
|
||||
|
||||
class UserAdmin(UserAdmin):
|
||||
inlines = (RowerInline,)
|
||||
|
||||
class WorkoutAdmin(admin.ModelAdmin):
|
||||
list_display = ('date','user','name','workouttype')
|
||||
|
||||
admin.site.unregister(User)
|
||||
admin.site.register(User,UserAdmin)
|
||||
admin.site.register(Workout,WorkoutAdmin)
|
||||
admin.site.register(GraphImage)
|
||||
@@ -1,432 +0,0 @@
|
||||
# Python
|
||||
import oauth2 as oauth
|
||||
import cgi
|
||||
import requests
|
||||
import requests.auth
|
||||
import json
|
||||
from django.utils import timezone
|
||||
from datetime import datetime
|
||||
from datetime import timedelta
|
||||
|
||||
# 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
|
||||
import numpy as np
|
||||
from rowers.models import Rower,Workout
|
||||
|
||||
from rowsandall_app.settings import C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET
|
||||
|
||||
class C2NoTokenError(Exception):
|
||||
def __init__(self,value):
|
||||
self.value=value
|
||||
|
||||
def __str__(self):
|
||||
return repr(self.value)
|
||||
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
def checkworkoutuser(user,workout):
|
||||
try:
|
||||
r = Rower.objects.get(user=user)
|
||||
return (workout.user == r)
|
||||
except Rower.DoesNotExist:
|
||||
return(False)
|
||||
|
||||
|
||||
def makeseconds(t):
|
||||
seconds = t.hour*3600.+t.minute*60.+t.second+0.1*int(t.microsecond/1.e5)
|
||||
return seconds
|
||||
|
||||
def c2wc(weightclass):
|
||||
if (weightclass=="lwt"):
|
||||
res = "L"
|
||||
else:
|
||||
res = "H"
|
||||
|
||||
return res
|
||||
|
||||
def createc2workoutdata_as_splits(w):
|
||||
filename = w.csvfilename
|
||||
row = rowingdata(filename)
|
||||
|
||||
# resize per minute
|
||||
df = row.df.groupby(lambda x:x/60).mean()
|
||||
|
||||
averagehr = int(df[' HRCur (bpm)'].mean())
|
||||
maxhr = int(df[' HRCur (bpm)'].max())
|
||||
|
||||
# adding diff, trying to see if this is valid
|
||||
t = 10*df.ix[:,' ElapsedTime (sec)'].diff().values
|
||||
t[0] = t[1]
|
||||
d = df.ix[:,' Horizontal (meters)'].diff().values
|
||||
d[0] = d[1]
|
||||
p = 10*df.ix[:,' Stroke500mPace (sec/500m)'].values
|
||||
t = t.astype(int)
|
||||
d = d.astype(int)
|
||||
p = p.astype(int)
|
||||
spm = df[' Cadence (stokes/min)'].astype(int)
|
||||
spm[0] = spm[1]
|
||||
hr = df[' HRCur (bpm)'].astype(int)
|
||||
split_data = []
|
||||
for i in range(len(t)):
|
||||
thisrecord = {"time":t[i],"distance":d[i],"stroke_rate":spm[i],
|
||||
"heart_rate":{
|
||||
"average:":hr[i]
|
||||
}
|
||||
}
|
||||
split_data.append(thisrecord)
|
||||
|
||||
try:
|
||||
durationstr = datetime.strptime(str(w.duration),"%H:%M:%S.%f")
|
||||
except ValueError:
|
||||
durationstr = datetime.strptime(str(w.duration),"%H:%M:%S")
|
||||
|
||||
|
||||
data = {
|
||||
"type": w.workouttype,
|
||||
"date": str(w.date)+" "+str(w.starttime),
|
||||
"distance": int(w.distance),
|
||||
"time": int(10*makeseconds(durationstr)),
|
||||
"timezone": "Etc/UTC",
|
||||
"weight_class": c2wc(w.weightcategory),
|
||||
"comments": w.notes,
|
||||
"heart_rate": {
|
||||
"average": averagehr,
|
||||
"max": maxhr,
|
||||
},
|
||||
"splits": split_data,
|
||||
}
|
||||
|
||||
|
||||
return data
|
||||
|
||||
def createc2workoutdata_grouped(w):
|
||||
filename = w.csvfilename
|
||||
row = rowingdata(filename)
|
||||
|
||||
# resize per minute
|
||||
df = row.df.groupby(lambda x:x/10).mean()
|
||||
|
||||
averagehr = int(df[' HRCur (bpm)'].mean())
|
||||
maxhr = int(df[' HRCur (bpm)'].max())
|
||||
|
||||
# adding diff, trying to see if this is valid
|
||||
t = 10*df.ix[:,' ElapsedTime (sec)'].values
|
||||
t[0] = t[1]
|
||||
d = df.ix[:,' Horizontal (meters)'].values
|
||||
d[0] = d[1]
|
||||
p = 10*df.ix[:,' Stroke500mPace (sec/500m)'].values
|
||||
t = t.astype(int)
|
||||
d = d.astype(int)
|
||||
p = p.astype(int)
|
||||
spm = df[' Cadence (stokes/min)'].astype(int)
|
||||
spm[0] = spm[1]
|
||||
hr = df[' HRCur (bpm)'].astype(int)
|
||||
stroke_data = []
|
||||
for i in range(len(t)):
|
||||
thisrecord = {"t":t[i],"d":d[i],"p":p[i],"spm":spm[i],"hr":hr[i]}
|
||||
stroke_data.append(thisrecord)
|
||||
|
||||
|
||||
try:
|
||||
durationstr = datetime.strptime(str(w.duration),"%H:%M:%S.%f")
|
||||
except ValueError:
|
||||
durationstr = datetime.strptime(str(w.duration),"%H:%M:%S")
|
||||
|
||||
|
||||
data = {
|
||||
"type": w.workouttype,
|
||||
"date": str(w.date)+" "+str(w.starttime),
|
||||
"distance": int(w.distance),
|
||||
"time": int(10*makeseconds(durationstr)),
|
||||
"weight_class": c2wc(w.weightcategory),
|
||||
"timezone": "Etc/UTC",
|
||||
"comments": w.notes,
|
||||
"heart_rate": {
|
||||
"average": averagehr,
|
||||
"max": maxhr,
|
||||
},
|
||||
"stroke_data": stroke_data,
|
||||
}
|
||||
|
||||
|
||||
return data
|
||||
|
||||
def createc2workoutdata(w):
|
||||
filename = w.csvfilename
|
||||
row = rowingdata(filename)
|
||||
averagehr = int(row.df[' HRCur (bpm)'].mean())
|
||||
maxhr = int(row.df[' HRCur (bpm)'].max())
|
||||
|
||||
# adding diff, trying to see if this is valid
|
||||
t = 10*row.df.ix[:,'TimeStamp (sec)'].values-10*row.df.ix[0,'TimeStamp (sec)']
|
||||
t[0] = t[1]
|
||||
d = 10*row.df.ix[:,' Horizontal (meters)'].values
|
||||
d[0] = d[1]
|
||||
p = abs(10*row.df.ix[:,' Stroke500mPace (sec/500m)'].values)
|
||||
p = np.clip(p,0,3600)
|
||||
t = t.astype(int)
|
||||
d = d.astype(int)
|
||||
p = p.astype(int)
|
||||
spm = row.df[' Cadence (stokes/min)'].astype(int)
|
||||
spm[0] = spm[1]
|
||||
hr = row.df[' HRCur (bpm)'].astype(int)
|
||||
stroke_data = []
|
||||
for i in range(len(t)):
|
||||
thisrecord = {"t":t[i],"d":d[i],"p":p[i],"spm":spm[i],"hr":hr[i]}
|
||||
stroke_data.append(thisrecord)
|
||||
|
||||
try:
|
||||
durationstr = datetime.strptime(str(w.duration),"%H:%M:%S.%f")
|
||||
except ValueError:
|
||||
durationstr = datetime.strptime(str(w.duration),"%H:%M:%S")
|
||||
|
||||
|
||||
data = {
|
||||
"type": w.workouttype,
|
||||
"date": str(w.date)+" "+str(w.starttime),
|
||||
"timezone": "Etc/UTC",
|
||||
"distance": int(w.distance),
|
||||
"time": int(10*makeseconds(durationstr)),
|
||||
"weight_class": c2wc(w.weightcategory),
|
||||
"comments": w.notes,
|
||||
"heart_rate": {
|
||||
"average": averagehr,
|
||||
"max": maxhr,
|
||||
},
|
||||
"stroke_data": stroke_data,
|
||||
}
|
||||
|
||||
|
||||
return data
|
||||
|
||||
def do_refresh_token(refreshtoken):
|
||||
client_auth = requests.auth.HTTPBasicAuth(C2_CLIENT_ID, C2_CLIENT_SECRET)
|
||||
post_data = {"grant_type": "refresh_token",
|
||||
"client_secret": C2_CLIENT_SECRET,
|
||||
"client_id":C2_CLIENT_ID,
|
||||
"refresh_token": refreshtoken,
|
||||
}
|
||||
headers = {'user-agent': 'sanderroosendaal'}
|
||||
response = requests.post("https://log.concept2.com/oauth/access_token",
|
||||
data=post_data,
|
||||
headers=headers)
|
||||
|
||||
token_json = response.json()
|
||||
thetoken = token_json['access_token']
|
||||
expires_in = token_json['expires_in']
|
||||
refresh_token = token_json['refresh_token']
|
||||
|
||||
return [thetoken,expires_in,refresh_token]
|
||||
|
||||
|
||||
def get_token(code):
|
||||
client_auth = requests.auth.HTTPBasicAuth(C2_CLIENT_ID, C2_CLIENT_SECRET)
|
||||
post_data = {"grant_type": "authorization_code",
|
||||
"code": code,
|
||||
"redirect_uri": C2_REDIRECT_URI,
|
||||
"client_secret": C2_CLIENT_SECRET,
|
||||
"client_id":C2_CLIENT_ID,
|
||||
}
|
||||
headers = {'user-agent': 'sanderroosendaal'}
|
||||
response = requests.post("https://log.concept2.com/oauth/access_token",
|
||||
data=post_data,
|
||||
headers=headers)
|
||||
token_json = response.json()
|
||||
thetoken = token_json['access_token']
|
||||
expires_in = token_json['expires_in']
|
||||
refresh_token = token_json['refresh_token']
|
||||
|
||||
return [thetoken,expires_in,refresh_token]
|
||||
|
||||
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": CLIENT_ID,
|
||||
"response_type": "code",
|
||||
"redirect_uri": REDIRECT_URI}
|
||||
import urllib
|
||||
url = "https://log.concept2.com/oauth/authorize?"+ urllib.urlencode(params)
|
||||
# url = "https://ssl.reddit.com/api/v1/authorize?" + urllib.urlencode(params)
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
def get_c2_workout(user,c2id):
|
||||
r = Rower.objects.get(user=user)
|
||||
if (r.c2token == '') or (r.c2token is None):
|
||||
s = "Token doesn't exist. Need to authorize"
|
||||
return custom_exception_handler(401,s)
|
||||
elif (timezone.now()>r.tokenexpirydate):
|
||||
s = "Token expired. Needs to refresh."
|
||||
return custom_exception_handler(401,s)
|
||||
else:
|
||||
# ready to fetch. Hurray
|
||||
authorizationstring = str('Bearer ' + r.c2token)
|
||||
headers = {'Authorization': authorizationstring,
|
||||
'user-agent': 'sanderroosendaal',
|
||||
'Content-Type': 'application/json'}
|
||||
url = "https://log.concept2.com/api/users/me/results/"+str(c2id)
|
||||
s = requests.get(url,headers=headers)
|
||||
|
||||
return s
|
||||
|
||||
def get_c2_workout_strokes(user,c2id):
|
||||
r = Rower.objects.get(user=user)
|
||||
if (r.c2token == '') or (r.c2token is None):
|
||||
return custom_exception_handler(401,s)
|
||||
s = "Token doesn't exist. Need to authorize"
|
||||
elif (timezone.now()>r.tokenexpirydate):
|
||||
s = "Token expired. Needs to refresh."
|
||||
return custom_exception_handler(401,s)
|
||||
else:
|
||||
# ready to fetch. Hurray
|
||||
authorizationstring = str('Bearer ' + r.c2token)
|
||||
headers = {'Authorization': authorizationstring,
|
||||
'user-agent': 'sanderroosendaal',
|
||||
'Content-Type': 'application/json'}
|
||||
url = "https://log.concept2.com/api/users/me/results/"+str(c2id)+"/strokes"
|
||||
s = requests.get(url,headers=headers)
|
||||
|
||||
return s
|
||||
|
||||
def get_c2_workout_list(user):
|
||||
r = Rower.objects.get(user=user)
|
||||
if (r.c2token == '') or (r.c2token is None):
|
||||
s = "Token doesn't exist. Need to authorize"
|
||||
return custom_exception_handler(401,s)
|
||||
elif (timezone.now()>r.tokenexpirydate):
|
||||
s = "Token expired. Needs to refresh."
|
||||
return custom_exception_handler(401,s)
|
||||
else:
|
||||
# ready to fetch. Hurray
|
||||
authorizationstring = str('Bearer ' + r.c2token)
|
||||
headers = {'Authorization': authorizationstring,
|
||||
'user-agent': 'sanderroosendaal',
|
||||
'Content-Type': 'application/json'}
|
||||
url = "https://log.concept2.com/api/users/me/results"
|
||||
s = requests.get(url,headers=headers)
|
||||
|
||||
return s
|
||||
|
||||
|
||||
|
||||
def get_username(access_token):
|
||||
authorizationstring = str('Bearer ' + access_token)
|
||||
headers = {'Authorization': authorizationstring,
|
||||
'user-agent': 'sanderroosendaal',
|
||||
'Content-Type': 'application/json'}
|
||||
import urllib
|
||||
url = "https://log.concept2.com/api/users/me"
|
||||
response = requests.get(url,headers=headers)
|
||||
|
||||
|
||||
me_json = response.json()
|
||||
|
||||
|
||||
return me_json['data']['username']
|
||||
|
||||
def get_userid(access_token):
|
||||
authorizationstring = str('Bearer ' + access_token)
|
||||
headers = {'Authorization': authorizationstring,
|
||||
'user-agent': 'sanderroosendaal',
|
||||
'Content-Type': 'application/json'}
|
||||
import urllib
|
||||
url = "https://log.concept2.com/api/users/me"
|
||||
response = requests.get(url,headers=headers)
|
||||
|
||||
|
||||
me_json = response.json()
|
||||
|
||||
|
||||
return me_json['data']['id']
|
||||
|
||||
def process_callback(request):
|
||||
# need error handling
|
||||
|
||||
code = request.GET['code']
|
||||
|
||||
access_token = get_token(code)
|
||||
|
||||
username = get_username(access_token)
|
||||
|
||||
return HttpResponse("got a user name: %s" % username)
|
||||
|
||||
def workout_c2_upload(user,w):
|
||||
response = 'trying C2 upload'
|
||||
r = Rower.objects.get(user=user)
|
||||
if (r.c2token == '') or (r.c2token is None):
|
||||
s = "Token doesn't exist. Need to authorize"
|
||||
return custom_exception_handler(401,s)
|
||||
elif (timezone.now()>r.tokenexpirydate):
|
||||
s = "Token expired. Needs to refresh."
|
||||
return custom_exception_handler(401,s)
|
||||
else:
|
||||
# ready to upload. Hurray
|
||||
if (checkworkoutuser(user,w)):
|
||||
c2userid = get_userid(r.c2token)
|
||||
data = createc2workoutdata(w)
|
||||
# if (w.workouttype=='water'):
|
||||
# data = createc2workoutdata_as_splits(w)
|
||||
authorizationstring = str('Bearer ' + r.c2token)
|
||||
headers = {'Authorization': authorizationstring,
|
||||
'user-agent': 'sanderroosendaal',
|
||||
'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))
|
||||
if (response.status_code == 201):
|
||||
s= json.loads(response.text)
|
||||
c2id = s['data']['id']
|
||||
w.uploadedtoc2 = c2id
|
||||
w.save()
|
||||
else:
|
||||
response = "You are not authorized to upload this workout"
|
||||
|
||||
return response
|
||||
|
||||
def rower_c2_token_refresh(user):
|
||||
r = Rower.objects.get(user=user)
|
||||
res = do_refresh_token(r.c2refreshtoken)
|
||||
access_token = res[0]
|
||||
expires_in = res[1]
|
||||
refresh_token = res[2]
|
||||
expirydatetime = timezone.now()+timedelta(seconds=expires_in)
|
||||
|
||||
r = Rower.objects.get(user=user)
|
||||
r.c2token = access_token
|
||||
r.tokenexpirydate = expirydatetime
|
||||
r.c2refreshtoken = refresh_token
|
||||
|
||||
r.save()
|
||||
return r.c2token
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
import time
|
||||
from django.views.generic.base import TemplateView
|
||||
from django.shortcuts import render
|
||||
from django.http import HttpResponse, HttpResponseRedirect
|
||||
from django.contrib.auth import authenticate, login, logout
|
||||
from rowers.forms import LoginForm,DocumentsForm,UploadOptionsForm
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
from django.utils.datastructures import MultiValueDictKeyError
|
||||
from django.utils import timezone,translation
|
||||
from django.core.mail import send_mail, BadHeaderError
|
||||
from rowers.forms import EmailForm, RegistrationForm, RegistrationFormTermsOfService,RegistrationFormUniqueEmail,CNsummaryForm,UpdateWindForm,UpdateStreamForm
|
||||
from rowers.forms import PredictedPieceForm
|
||||
from rowers.models import Workout, User, Rower, WorkoutForm,RowerForm,GraphImage,AdvancedWorkoutForm
|
||||
import StringIO
|
||||
from django.contrib.auth.decorators import login_required,user_passes_test
|
||||
from time import strftime,strptime,mktime,time,daylight
|
||||
import os,sys
|
||||
import datetime
|
||||
import iso8601
|
||||
import c2stuff
|
||||
from c2stuff import C2NoTokenError
|
||||
from iso8601 import ParseError
|
||||
import stravastuff
|
||||
import sporttracksstuff
|
||||
from rowsandall_app.settings import C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET
|
||||
from rowsandall_app.settings import SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI, SPORTTRACKS_CLIENT_SECRET
|
||||
import requests
|
||||
import json
|
||||
from rowsandall_app.rows import handle_uploaded_file
|
||||
from rowers.tasks import handle_makeplot,handle_otwsetpower,handle_sendemailtcx
|
||||
from scipy.signal import savgol_filter
|
||||
|
||||
from rowingdata import rower as rrower
|
||||
from rowingdata import main as rmain
|
||||
from rowingdata import rowingdata as rdata
|
||||
from rowingdata import TCXParser,RowProParser,ErgDataParser,TCXParserNoHR
|
||||
from rowingdata import painsledDesktopParser,speedcoachParser,ErgStickParser
|
||||
from rowingdata import SpeedCoach2Parser,FITParser,fitsummarydata
|
||||
from rowingdata import make_cumvalues
|
||||
from rowingdata import summarydata,get_file_type
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from pytz import timezone as tz,utc
|
||||
import dateutil
|
||||
import mpld3
|
||||
from mpld3 import plugins
|
||||
import stravalib
|
||||
from stravalib.exc import ActivityUploadFailed,TimeoutExceeded
|
||||
from weather import get_wind_data
|
||||
|
||||
import django_rq
|
||||
queue = django_rq.get_queue('default')
|
||||
queuelow = django_rq.get_queue('low')
|
||||
queuehigh = django_rq.get_queue('low')
|
||||
|
||||
import plots
|
||||
|
||||
from io import BytesIO
|
||||
from scipy.special import lambertw
|
||||
|
||||
def emailall(emailfile,subject):
|
||||
rowers = Rower.objects.all()
|
||||
for rower in rowers:
|
||||
email = rower.user.email
|
||||
firstname = rower.user.first_name
|
||||
|
||||
with open(emailfile) as f:
|
||||
message = f.read()
|
||||
|
||||
message = '\nDear '+firstname+'\n\n'+str(message)
|
||||
print message
|
||||
|
||||
send_mail(
|
||||
subject,
|
||||
message,
|
||||
'info@rowsandall.com',
|
||||
[email],
|
||||
)
|
||||
@@ -1,9 +0,0 @@
|
||||
It is a little while since you registered at Rowsandall.com and we do hope you are enjoying analysing your rowing data using the tools we have designed. We are continuing to add features and functionality to the site as we grow and hopefully you will have seen some of these developments since you registered.
|
||||
|
||||
We are always interested in users' views on Rowsandall.com as we strive to improve the interface and functionality of the site. We have therefore compiled a short questionnaire to capture your thoughts and experiences. The questionnaire can be found at:
|
||||
|
||||
https://surveynuts.com/surveys/take?id=108060&c=764200337FDHD
|
||||
|
||||
We do ask that you take a few moments to complete the survey and we would also welcome any other suggestions you may have directly to us (insert email address) if you feel a free-form response suits you better. Respondents to the questionnaire will receive a complementary Pro membership for 3 months.
|
||||
|
||||
Best regards, Sander Roosendaal & the Rowsandall Team
|
||||
@@ -1,215 +0,0 @@
|
||||
from django import forms
|
||||
from rowers.models import Workout
|
||||
from rowsandall_app.rows import validate_file_extension,must_be_csv
|
||||
from django.contrib.auth.forms import UserCreationForm
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.admin.widgets import AdminDateWidget
|
||||
from django.forms.extras.widgets import SelectDateWidget
|
||||
from django.utils import timezone,translation
|
||||
|
||||
import datetime
|
||||
|
||||
class LoginForm(forms.Form):
|
||||
username = forms.CharField()
|
||||
password = forms.CharField(widget=forms.PasswordInput())
|
||||
|
||||
class EmailForm(forms.Form):
|
||||
firstname = forms.CharField(max_length=255)
|
||||
lastname = forms.CharField(max_length=255)
|
||||
email = forms.EmailField()
|
||||
subject = forms.CharField(max_length=255)
|
||||
botcheck = forms.CharField(max_length=5)
|
||||
message = forms.CharField()
|
||||
|
||||
class CNsummaryForm(forms.Form):
|
||||
file = forms.FileField(required=True,validators=[must_be_csv])
|
||||
|
||||
class SummaryStringForm(forms.Form):
|
||||
intervalstring = forms.CharField(max_length=255,label='Workout Description')
|
||||
|
||||
class DocumentsForm(forms.Form):
|
||||
filetypechoices = (
|
||||
('csv' , 'Painsled iOS CSV'),
|
||||
('tcx' , 'TCX'),
|
||||
('tcxnohr' , 'TCX No HR'),
|
||||
('rp' , 'RowPro CSV'),
|
||||
('speedcoach' , 'SpeedCoach GPS CSV'),
|
||||
('speedcoach2' , 'SpeedCoach GPS 2 CSV'),
|
||||
('ergdata' , 'ErgData CSV'),
|
||||
('ergstick' , 'ErgStick CSV'),
|
||||
('painsleddesktop' , 'Painsled Desktop CSV'),
|
||||
)
|
||||
title = forms.CharField(required=False)
|
||||
file = forms.FileField(required=True,
|
||||
validators=[validate_file_extension])
|
||||
workouttype = forms.ChoiceField(required=True,
|
||||
choices=Workout.workouttypes,
|
||||
initial='rower')
|
||||
# fileformat = forms.ChoiceField(required=True,
|
||||
# choices=filetypechoices,
|
||||
# initial='csv')
|
||||
notes = forms.CharField(required=False,
|
||||
widget=forms.Textarea)
|
||||
|
||||
class Meta:
|
||||
fields = ['title','file','workouttype','fileformat']
|
||||
|
||||
|
||||
|
||||
class UploadOptionsForm(forms.Form):
|
||||
plotchoices = (
|
||||
('timeplot','Time Plot'),
|
||||
('distanceplot','Distance Plot'),
|
||||
('pieplot','Pie Chart'),
|
||||
)
|
||||
make_plot = forms.BooleanField(initial=False)
|
||||
plottype = forms.ChoiceField(required=False,
|
||||
choices=plotchoices,
|
||||
initial='timeplot')
|
||||
upload_to_C2 = forms.BooleanField(initial=False)
|
||||
|
||||
class Meta:
|
||||
fields = ['make_plot','plottype','upload_toc2']
|
||||
|
||||
class PredictedPieceForm(forms.Form):
|
||||
unitchoices = (
|
||||
('t','minutes'),
|
||||
('d','meters'),
|
||||
)
|
||||
pieceunit = forms.ChoiceField(required=True,choices=unitchoices,
|
||||
initial='t',label='Unit')
|
||||
value = forms.IntegerField(initial=10,label='Value')
|
||||
|
||||
class Meta:
|
||||
fields = ['value','pieceunit']
|
||||
|
||||
class UpdateStreamForm(forms.Form):
|
||||
unitchoices = (
|
||||
('m','m/s'),
|
||||
('f','foot/s'),
|
||||
('k','knots'),
|
||||
('p','pace difference (sec/500m)'),
|
||||
)
|
||||
dist1 = forms.FloatField(initial=0,label = 'Distance 1')
|
||||
dist2 = forms.FloatField(initial=1000,label = 'Distance 2')
|
||||
stream1 = forms.FloatField(initial=0,label = 'Stream velocity 1')
|
||||
stream2 = forms.FloatField(initial=0,label = 'Stream velocity 2')
|
||||
streamunit = forms.ChoiceField(required=True,
|
||||
choices=unitchoices,
|
||||
initial='m',
|
||||
label='Unit')
|
||||
|
||||
class Meta:
|
||||
fields = ['dist1','dist2','stream1', 'stream2','streamunit']
|
||||
|
||||
class UpdateWindForm(forms.Form):
|
||||
unitchoices = (
|
||||
('m','m/s'),
|
||||
('k','knots'),
|
||||
('b','beaufort'),
|
||||
('kmh','km/h'),
|
||||
('mph','miles/hour'),
|
||||
)
|
||||
dist1 = forms.FloatField(initial=0,label = 'Distance 1')
|
||||
dist2 = forms.FloatField(initial=1000,label = 'Distance 2')
|
||||
vwind1 = forms.FloatField(initial=0,required=False,label = 'Wind Speed 1')
|
||||
vwind2 = forms.FloatField(initial=0,required=False,label = 'Wind Speed 2')
|
||||
windunit = forms.ChoiceField(required=True,
|
||||
choices=unitchoices,
|
||||
initial='m',
|
||||
label='Unit')
|
||||
winddirection1 = forms.IntegerField(initial=0,required=False,
|
||||
label = 'Wind Direction 1')
|
||||
winddirection2 = forms.IntegerField(initial=0,required=False,
|
||||
label = 'Wind Direction 2')
|
||||
|
||||
class Meta:
|
||||
fields = ['dist1','dist2',
|
||||
'vwind1','vwind2',
|
||||
'windunit',
|
||||
'winddirection1','winddirection2']
|
||||
|
||||
|
||||
|
||||
class DateRangeForm(forms.Form):
|
||||
startdate = forms.DateField(initial=timezone.now()-datetime.timedelta(days=365),
|
||||
widget=SelectDateWidget(years=range(1990,2050)),
|
||||
label='Start Date')
|
||||
enddate = forms.DateField(initial=timezone.now(),
|
||||
widget=SelectDateWidget(years=range(1990,2050)),
|
||||
label='End Date')
|
||||
|
||||
class Meta:
|
||||
fields = ['startdate','enddate']
|
||||
|
||||
class DeltaDaysForm(forms.Form):
|
||||
deltadays = forms.IntegerField(initial=0,required=False,label='')
|
||||
|
||||
class RegistrationForm(UserCreationForm):
|
||||
"""
|
||||
Form for registering a new user account.
|
||||
Validates that the requested username is not already in use, and
|
||||
requires the password to be entered twice to catch typos.
|
||||
Subclasses should feel free to add any additional validation they
|
||||
need, but should avoid defining a ``save()`` method -- the actual
|
||||
saving of collected user data is delegated to the active
|
||||
registration backend.
|
||||
"""
|
||||
required_css_class = 'required'
|
||||
email = forms.EmailField(label="E-mail")
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ("username", "first_name", "last_name", "email", "password1", "password2")
|
||||
|
||||
class RegistrationFormTermsOfService(RegistrationForm):
|
||||
"""
|
||||
Subclass of ``RegistrationForm`` which adds a required checkbox
|
||||
for agreeing to a site's Terms of Service.
|
||||
"""
|
||||
tos = forms.BooleanField(widget=forms.CheckboxInput,
|
||||
label='I have read and agree to the Terms of Service',
|
||||
error_messages={'required': "You must agree to the terms to register"})
|
||||
|
||||
|
||||
class RegistrationFormUniqueEmail(RegistrationFormTermsOfService):
|
||||
"""
|
||||
Subclass of ``RegistrationFormTermsOfService`` which enforces uniqueness of
|
||||
email addresses.
|
||||
"""
|
||||
def clean_email(self):
|
||||
"""
|
||||
Validate that the supplied email address is unique for the
|
||||
site.
|
||||
"""
|
||||
if User.objects.filter(email__iexact=self.cleaned_data['email']):
|
||||
raise forms.ValidationError("This email address is already in use. Please supply a different email address.")
|
||||
return self.cleaned_data['email']
|
||||
|
||||
|
||||
class IntervalUpdateForm(forms.Form):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
typechoices = (
|
||||
(1,'single time'),
|
||||
(2,'single distance'),
|
||||
(3,'rest (time based)'),
|
||||
(3,'rest (distance based)'),
|
||||
(4,'work (time based)'),
|
||||
(5,'work (distance based)'),
|
||||
)
|
||||
aantal = int(kwargs.pop('aantal'))
|
||||
super(IntervalUpdateForm, self).__init__(*args, **kwargs)
|
||||
|
||||
for i in range(aantal):
|
||||
self.fields['intervalt_%s' % i] = forms.TimeField(label='Time '+str(i+1))
|
||||
self.fields['intervald_%s' % i] = forms.IntegerField(label='Distance '+str(i+1))
|
||||
self.fields['type_%s' % i] = forms.ChoiceField(choices=typechoices,
|
||||
required=True,
|
||||
initial=4,
|
||||
label = 'Type '+str(i+1))
|
||||
self.fields['intervalt_%s' % i].widget.attrs['style'] = 'width:76px; height: 16px;'
|
||||
self.fields['intervald_%s' % i].widget.attrs['style'] = 'width:76px; height: 16px;'
|
||||
self.fields['type_%s' % i].widget.attrs['style'] = 'width:156px; height: 22px;'
|
||||
|
||||
|
||||
+307
-122
@@ -21,6 +21,7 @@ from bokeh.models import (
|
||||
GMapPlot, GMapOptions, ColumnDataSource, Circle,
|
||||
DataRange1d, PanTool, WheelZoomTool, BoxSelectTool,
|
||||
SaveTool, ResizeTool, ResetTool, TapTool,CrosshairTool,BoxZoomTool,
|
||||
Span,
|
||||
)
|
||||
#from bokeh.models.widgets import Slider, Select, TextInput
|
||||
from bokeh.core.properties import value
|
||||
@@ -786,10 +787,24 @@ def interactive_cum_flex_chart(theworkouts,promember=0,
|
||||
|
||||
thedata['driveenergy'] = thedata[' DriveLength (meters)']*thedata[' AverageDriveForce (lbs)']*4.44822
|
||||
|
||||
# throw out zeros from dataframe
|
||||
thedata = thedata[thedata[csvcolumns[yparam1]] > 0]
|
||||
thedata = thedata[thedata[csvcolumns[xparam]] > 0]
|
||||
if yparam2 != 'None':
|
||||
thedata = thedata[thedata[csvcolumns[yparam2]] > 0]
|
||||
|
||||
# check if dataframe not empty
|
||||
if thedata.empty:
|
||||
return ['','<p>No non-zero data in selection</p>','','']
|
||||
|
||||
spm = thedata.ix[:,csvcolumns['spm']]
|
||||
|
||||
f = thedata['TimeStamp (sec)'].diff().mean()
|
||||
if not np.isnan(f):
|
||||
windowsize = 2*(int(10./(f)))+1
|
||||
else:
|
||||
windowsize = 5
|
||||
|
||||
if windowsize <= 3:
|
||||
windowsize = 5
|
||||
|
||||
@@ -805,8 +820,6 @@ def interactive_cum_flex_chart(theworkouts,promember=0,
|
||||
|
||||
thedata['drivespeed'] = drivelength/thedata[' DriveTime (ms)']*1.0e3
|
||||
|
||||
# get user
|
||||
# u = User.objects.get(id=row.user.id)
|
||||
|
||||
x1 = thedata.ix[:,csvcolumns[xparam]]
|
||||
|
||||
@@ -818,9 +831,31 @@ def interactive_cum_flex_chart(theworkouts,promember=0,
|
||||
|
||||
|
||||
if xparam=='time':
|
||||
xaxmax = get_datetimes([x1.max()])[0]
|
||||
xaxmin = get_datetimes([x1.min()])[0]
|
||||
xaxmax = x1.max()
|
||||
xaxmin = x1.min()
|
||||
xaxmax = get_datetimes([xaxmax],tzinfo=1)[0]
|
||||
xaxmin = get_datetimes([xaxmin],tzinfo=1)[0]
|
||||
x1 = get_datetimes(x1,tzinfo=1)
|
||||
elif xparam=='distance':
|
||||
xaxmax = x1.max()
|
||||
xaxmin = x1.min()
|
||||
else:
|
||||
xaxmax = yaxmaxima[xparam]
|
||||
xaxmin = yaxminima[xparam]
|
||||
|
||||
# average values
|
||||
if xparam != 'time':
|
||||
x1mean = x1.mean()
|
||||
else:
|
||||
x1mean = 0
|
||||
|
||||
y1mean = y1.mean()
|
||||
y2mean = y2.mean()
|
||||
|
||||
if xparam != 'time':
|
||||
xvals = xaxmin+np.arange(100)*(xaxmax-xaxmin)/100.
|
||||
else:
|
||||
xvals = np.arange(100)
|
||||
|
||||
x_axis_type = 'linear'
|
||||
y_axis_type = 'linear'
|
||||
@@ -835,7 +870,6 @@ def interactive_cum_flex_chart(theworkouts,promember=0,
|
||||
|
||||
|
||||
time = thedata.ix[:,csvcolumns['time']]
|
||||
time = time-time[0]
|
||||
|
||||
hr = thedata.ix[:,csvcolumns['hr']]
|
||||
if windowsize > 3:
|
||||
@@ -848,6 +882,7 @@ def interactive_cum_flex_chart(theworkouts,promember=0,
|
||||
|
||||
power = thedata.ix[:,csvcolumns['power']]
|
||||
|
||||
|
||||
# Add hover to this comma-separated string and see what changes
|
||||
if (promember==1):
|
||||
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
|
||||
@@ -855,11 +890,21 @@ def interactive_cum_flex_chart(theworkouts,promember=0,
|
||||
TOOLS = 'pan,box_zoom,wheel_zoom,reset,tap,hover,crosshair'
|
||||
|
||||
plot = Figure(x_axis_type=x_axis_type,y_axis_type=y_axis_type,
|
||||
plot_width=900,
|
||||
tools=TOOLS,
|
||||
toolbar_location="above",
|
||||
toolbar_sticky=False)
|
||||
|
||||
x1means = Span(location=x1mean,dimension='height',line_color='green',
|
||||
line_dash=[6,6], line_width=2)
|
||||
|
||||
y1means = Span(location=y1mean,dimension='width',line_color='blue',
|
||||
line_dash=[6,6],line_width=2)
|
||||
y2means = y1means
|
||||
|
||||
if (xparam != 'time') and (xparam != 'distance'):
|
||||
plot.add_layout(x1means)
|
||||
|
||||
plot.add_layout(y1means)
|
||||
|
||||
source = ColumnDataSource(
|
||||
data = dict(
|
||||
@@ -876,9 +921,24 @@ def interactive_cum_flex_chart(theworkouts,promember=0,
|
||||
)
|
||||
)
|
||||
|
||||
source2 = ColumnDataSource(
|
||||
data = dict(
|
||||
x1=x1,
|
||||
y1=y1,
|
||||
y2=y2,
|
||||
time=niceformat(get_datetimes(time,tzinfo=1)),
|
||||
pace=nicepaceformat(get_datetimes(pace)),
|
||||
hr = hr,
|
||||
spm = spm,
|
||||
spmc=np.rint(10*spm)/10.,
|
||||
distance=distance,
|
||||
power=power,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# plot.circle('x1','y1',source=source,legend=yparam1,size=3)
|
||||
plot.circle('x1','y1',source=source,fill_alpha=0.3,line_color=None,
|
||||
plot.circle('x1','y1',source=source2,fill_alpha=0.3,line_color=None,
|
||||
legend=yparamname1,
|
||||
)
|
||||
|
||||
@@ -920,11 +980,17 @@ def interactive_cum_flex_chart(theworkouts,promember=0,
|
||||
|
||||
plot.circle('x1','y2',color="red",y_range_name="yax2",
|
||||
legend=yparamname2,
|
||||
source=source,fill_alpha=0.3,line_color=None)
|
||||
source=source2,fill_alpha=0.3,line_color=None)
|
||||
|
||||
plot.add_layout(LinearAxis(y_range_name="yax2",
|
||||
axis_label=axlabels[yparam2]),'right')
|
||||
|
||||
y2means = Span(location=y2mean,dimension='width',line_color='red',
|
||||
line_dash=[6,6],line_width=2,y_range_name="yax2")
|
||||
|
||||
|
||||
plot.add_layout(y2means)
|
||||
|
||||
hover = plot.select(dict(type=HoverTool))
|
||||
|
||||
|
||||
@@ -938,11 +1004,119 @@ def interactive_cum_flex_chart(theworkouts,promember=0,
|
||||
|
||||
hover.mode = 'mouse'
|
||||
|
||||
callback = CustomJS(args = dict(source=source,source2=source2,
|
||||
x1means=x1means,
|
||||
y1means=y1means,
|
||||
y2means=y2means), code="""
|
||||
var data = source.data
|
||||
var data2 = source2.data
|
||||
var x1 = data['x1']
|
||||
var y1 = data['y1']
|
||||
var y2 = data['y2']
|
||||
var spm1 = data['spm']
|
||||
var time1 = data['time']
|
||||
var pace1 = data['pace']
|
||||
var hr1 = data['hr']
|
||||
var spmc1 = data['spmc']
|
||||
var distance1 = data['distance']
|
||||
var power1 = data['power']
|
||||
|
||||
var minspm = minspm.value
|
||||
var maxspm = maxspm.value
|
||||
var mindist = mindist.value
|
||||
var maxdist = maxdist.value
|
||||
var xm = 0
|
||||
var ym1 = 0
|
||||
var ym2 = 0
|
||||
|
||||
data2['x1'] = []
|
||||
data2['y1'] = []
|
||||
data2['y2'] = []
|
||||
data2['spm'] = []
|
||||
data2['time'] = []
|
||||
data2['pace'] = []
|
||||
data2['hr'] = []
|
||||
data2['spmc'] = []
|
||||
data2['distance'] = []
|
||||
data2['power'] = []
|
||||
data2['x1mean'] = []
|
||||
data2['y1mean'] = []
|
||||
data2['y2mean'] = []
|
||||
data2['xvals'] = []
|
||||
data2['y1vals'] = []
|
||||
data2['y2vals'] = []
|
||||
|
||||
for (i=0; i<x1.length; i++) {
|
||||
if (spm1[i]>=minspm && spm1[i]<=maxspm) {
|
||||
if (distance1[i]>=mindist && distance1[i]<=maxdist) {
|
||||
data2['x1'].push(x1[i])
|
||||
data2['y1'].push(y1[i])
|
||||
data2['y2'].push(y2[i])
|
||||
data2['spm'].push(spm1[i])
|
||||
data2['time'].push(time1[i])
|
||||
data2['pace'].push(pace1[i])
|
||||
data2['hr'].push(hr1[i])
|
||||
data2['spmc'].push(spmc1[i])
|
||||
data2['distance'].push(distance1[i])
|
||||
data2['power'].push(power1[i])
|
||||
|
||||
xm += x1[i]
|
||||
ym1 += y1[i]
|
||||
ym2 += y2[i]
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xm /= data2['x1'].length
|
||||
ym1 /= data2['x1'].length
|
||||
ym2 /= data2['x1'].length
|
||||
|
||||
data2['x1mean'] = [xm,xm]
|
||||
data2['y1mean'] = [ym1,ym1]
|
||||
data2['y2mean'] = [ym2,ym2]
|
||||
x1means.location = xm
|
||||
y1means.location = ym1
|
||||
y2means.location = ym2
|
||||
|
||||
source2.trigger('change');
|
||||
""")
|
||||
|
||||
slider_spm_min = Slider(start=15.0, end=55,value=15.0, step=.1,
|
||||
title="Min SPM",callback=callback)
|
||||
callback.args["minspm"] = slider_spm_min
|
||||
|
||||
|
||||
script, div = components(plot)
|
||||
slider_spm_max = Slider(start=15.0, end=55,value=55.0, step=.1,
|
||||
title="Max SPM",callback=callback)
|
||||
callback.args["maxspm"] = slider_spm_max
|
||||
|
||||
return [script,div]
|
||||
distmax = 100+100*int(distance.max()/100.)
|
||||
|
||||
slider_dist_min = Slider(start=0,end=distmax,value=0,step=1,
|
||||
title="Min Distance",callback=callback)
|
||||
callback.args["mindist"] = slider_dist_min
|
||||
|
||||
slider_dist_max = Slider(start=0,end=distmax,value=distmax,
|
||||
step=1,
|
||||
title="Max Distance",callback=callback)
|
||||
callback.args["maxdist"] = slider_dist_max
|
||||
|
||||
layout = layoutrow([layoutcolumn([slider_spm_min,
|
||||
slider_spm_max,
|
||||
slider_dist_min,
|
||||
slider_dist_max,
|
||||
],
|
||||
),
|
||||
plot])
|
||||
|
||||
script, div = components(layout)
|
||||
js_resources = INLINE.render_js()
|
||||
css_resources = INLINE.render_css()
|
||||
|
||||
|
||||
|
||||
return [script,div,js_resources,css_resources]
|
||||
|
||||
|
||||
|
||||
@@ -1388,35 +1562,31 @@ def interactive_flex_chart2(id=0,promember=0,
|
||||
y2 = y1
|
||||
|
||||
if xparam=='time':
|
||||
|
||||
xaxmax = x1.max()
|
||||
xaxmin = x1.min()
|
||||
xaxmax = get_datetimes([xaxmax],tzinfo=1)[0]
|
||||
xaxmin = get_datetimes([xaxmin],tzinfo=1)[0]
|
||||
x1 = get_datetimes(x1,tzinfo=1)
|
||||
|
||||
if xparam=='distance':
|
||||
elif xparam=='distance':
|
||||
xaxmax = x1.max()
|
||||
xaxmin = x1.min()
|
||||
else:
|
||||
xaxmax = yaxmaxima[xparam]
|
||||
xaxmin = yaxminima[xparam]
|
||||
|
||||
# average values
|
||||
y1mean = y1.mean()+0.0*np.arange(100)
|
||||
y2mean = y2.mean()+0.0*np.arange(100)
|
||||
if xparam != 'time':
|
||||
x1mean = x1.mean()+0.0*np.arange(100)
|
||||
x1mean = x1.mean()
|
||||
else:
|
||||
x1mean = 0+0.0*np.arange(100)
|
||||
x1mean = 0
|
||||
|
||||
y1mean = y1.mean()
|
||||
y2mean = y2.mean()
|
||||
|
||||
|
||||
if xparam != 'time' and xparam != 'distance' and yparam1 != 'pace':
|
||||
xvals = yaxminima[xparam]+np.arange(100)*(yaxmaxima[xparam]-yaxminima[xparam])/100.
|
||||
y1vals = yaxminima[yparam1]+np.arange(100)*(yaxmaxima[yparam1]-yaxminima[yparam1])/100.
|
||||
if xparam != 'time':
|
||||
xvals = xaxmin+np.arange(100)*(xaxmax-xaxmin)/100.
|
||||
else:
|
||||
xvals = np.arange(100)
|
||||
y1vals = np.arange(100)
|
||||
|
||||
|
||||
|
||||
# constant power plot
|
||||
if yparam1 == 'driveenergy':
|
||||
@@ -1466,11 +1636,10 @@ def interactive_flex_chart2(id=0,promember=0,
|
||||
spmc=np.rint(10*spm)/10.,
|
||||
distance=distance,
|
||||
power=power,
|
||||
xvals=xvals,
|
||||
y1mean=y1mean,
|
||||
y2mean=y2mean,
|
||||
x1mean=x1mean,
|
||||
y1vals=y1vals,
|
||||
# xvals=xvals,
|
||||
y1mean=[y1mean,y1mean],
|
||||
y2mean=[y2mean,y2mean],
|
||||
x1mean=[x1mean,x1mean],
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1487,11 +1656,10 @@ def interactive_flex_chart2(id=0,promember=0,
|
||||
spmc=np.rint(10*spm)/10.,
|
||||
distance=distance,
|
||||
power=power,
|
||||
xvals=xvals,
|
||||
y1mean=y1mean,
|
||||
y2mean=y2mean,
|
||||
x1mean=x1mean,
|
||||
y1vals=y1vals,
|
||||
# xvals=xvals,
|
||||
y1mean=[y1mean,y1mean],
|
||||
y2mean=[y2mean,y2mean],
|
||||
x1mean=[x1mean,x1mean],
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1510,9 +1678,109 @@ def interactive_flex_chart2(id=0,promember=0,
|
||||
toolbar_sticky=False,
|
||||
# plot_width=900,
|
||||
)
|
||||
x1means = Span(location=x1mean,dimension='height',line_color='green',
|
||||
line_dash=[6,6], line_width=2)
|
||||
|
||||
y1means = Span(location=y1mean,dimension='width',line_color='blue',
|
||||
line_dash=[6,6],line_width=2)
|
||||
y2means = y1means
|
||||
|
||||
if (xparam != 'time') and (xparam != 'distance'):
|
||||
plot.add_layout(x1means)
|
||||
|
||||
plot.add_layout(y1means)
|
||||
|
||||
|
||||
callback = CustomJS(args = dict(source=source,source2=source2), code="""
|
||||
|
||||
# average values
|
||||
if yparam1 == 'driveenergy':
|
||||
if xparam == 'spm':
|
||||
plot.line(xvals,yconstantpower,color="green",legend="Constant Power")
|
||||
|
||||
if plottype=='line':
|
||||
plot.line('x1','y1',source=source2,legend=axlabels[yparam1])
|
||||
elif plottype=='scatter':
|
||||
# plot.circle('x1','y1',source=source2,legend=yparam1,size=3)
|
||||
plot.scatter('x1','y1',source=source2,legend=axlabels[yparam1],fill_alpha=0.4,
|
||||
line_color=None)
|
||||
|
||||
plot.title.text = row.name
|
||||
plot.title.text_font_size=value("1.0em")
|
||||
plot.xaxis.axis_label = axlabels[xparam]
|
||||
plot.yaxis.axis_label = axlabels[yparam1]
|
||||
|
||||
|
||||
|
||||
yrange1 = Range1d(start=yaxminima[yparam1],end=yaxmaxima[yparam1])
|
||||
plot.y_range = yrange1
|
||||
|
||||
if (xparam != 'time') and (xparam != 'distance'):
|
||||
xrange1 = Range1d(start=yaxminima[xparam],end=yaxmaxima[xparam])
|
||||
plot.x_range = xrange1
|
||||
|
||||
if xparam == 'time':
|
||||
xrange1 = Range1d(start=xaxmin,end=xaxmax)
|
||||
plot.x_range = xrange1
|
||||
plot.xaxis[0].formatter = DatetimeTickFormatter(
|
||||
hours = ["%H"],
|
||||
minutes = ["%M"],
|
||||
seconds = ["%S"],
|
||||
days = ["0"],
|
||||
months = [""],
|
||||
years = [""]
|
||||
)
|
||||
|
||||
|
||||
if yparam1 == 'pace':
|
||||
plot.y_range = Range1d(ymin,ymax)
|
||||
plot.yaxis[0].formatter = DatetimeTickFormatter(
|
||||
seconds = ["%S"],
|
||||
minutes = ["%M"]
|
||||
)
|
||||
|
||||
if yparam2 != 'None':
|
||||
yrange2 = Range1d(start=yaxminima[yparam2],end=yaxmaxima[yparam2])
|
||||
plot.extra_y_ranges = {"yax2": yrange2}
|
||||
|
||||
if plottype=='line':
|
||||
plot.line('x1','y2',color="red",y_range_name="yax2",
|
||||
legend=axlabels[yparam2],
|
||||
source=source2)
|
||||
|
||||
elif plottype=='scatter':
|
||||
# plot.circle(x1,y2,color="red",y_range_name="yax2",legend=yparam2,
|
||||
# source=source,size=3)
|
||||
plot.scatter('x1','y2',source=source2,legend=axlabels[yparam2]
|
||||
,fill_alpha=0.4,
|
||||
line_color=None,color="red",y_range_name="yax2")
|
||||
|
||||
plot.add_layout(LinearAxis(y_range_name="yax2",
|
||||
axis_label=axlabels[yparam2]),'right')
|
||||
|
||||
y2means = Span(location=y2mean,dimension='width',line_color='red',
|
||||
line_dash=[6,6],line_width=2,y_range_name="yax2")
|
||||
|
||||
|
||||
plot.add_layout(y2means)
|
||||
|
||||
hover = plot.select(dict(type=HoverTool))
|
||||
|
||||
|
||||
hover.tooltips = OrderedDict([
|
||||
('Time','@time'),
|
||||
('Distance','@distance'),
|
||||
('Pace','@pace'),
|
||||
('HR','@hr'),
|
||||
('SPM','@spmc{1.1}'),
|
||||
('Power','@power{int}'),
|
||||
])
|
||||
|
||||
hover.mode = 'mouse'
|
||||
|
||||
callback = CustomJS(args = dict(source=source,source2=source2,
|
||||
x1means=x1means,
|
||||
y1means=y1means,
|
||||
y2means=y2means), code="""
|
||||
var data = source.data
|
||||
var data2 = source2.data
|
||||
var x1 = data['x1']
|
||||
@@ -1568,6 +1836,7 @@ def interactive_flex_chart2(id=0,promember=0,
|
||||
xm += x1[i]
|
||||
ym1 += y1[i]
|
||||
ym2 += y2[i]
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1579,10 +1848,9 @@ def interactive_flex_chart2(id=0,promember=0,
|
||||
data2['x1mean'] = [xm,xm]
|
||||
data2['y1mean'] = [ym1,ym1]
|
||||
data2['y2mean'] = [ym2,ym2]
|
||||
|
||||
data2['xvals'] = [0,3e4]
|
||||
data2['y1vals'] = [0,3e4]
|
||||
data2['y2vals'] = [0,3e4]
|
||||
x1means.location = xm
|
||||
y1means.location = ym1
|
||||
y2means.location = ym2
|
||||
|
||||
source2.trigger('change');
|
||||
""")
|
||||
@@ -1607,89 +1875,6 @@ def interactive_flex_chart2(id=0,promember=0,
|
||||
title="Max Distance",callback=callback)
|
||||
callback.args["maxdist"] = slider_dist_max
|
||||
|
||||
# average values
|
||||
plot.line('xvals','y1mean',color="black",source=source2)
|
||||
plot.line('x1mean','y1vals',color="black",source=source2)
|
||||
if yparam1 == 'driveenergy':
|
||||
if xparam == 'spm':
|
||||
plot.line(xvals,yconstantpower,color="green",legend="Constant Power")
|
||||
|
||||
if plottype=='line':
|
||||
plot.line('x1','y1',source=source2,legend=axlabels[yparam1])
|
||||
elif plottype=='scatter':
|
||||
# plot.circle('x1','y1',source=source2,legend=yparam1,size=3)
|
||||
plot.scatter('x1','y1',source=source2,legend=axlabels[yparam1],fill_alpha=0.4,
|
||||
line_color=None)
|
||||
|
||||
plot.title.text = row.name
|
||||
plot.title.text_font_size=value("1.0em")
|
||||
plot.xaxis.axis_label = axlabels[xparam]
|
||||
plot.yaxis.axis_label = axlabels[yparam1]
|
||||
|
||||
|
||||
yrange1 = Range1d(start=yaxminima[yparam1],end=yaxmaxima[yparam1])
|
||||
plot.y_range = yrange1
|
||||
|
||||
if (xparam != 'time') and (xparam != 'distance'):
|
||||
xrange1 = Range1d(start=yaxminima[xparam],end=yaxmaxima[xparam])
|
||||
plot.x_range = xrange1
|
||||
|
||||
if xparam == 'time':
|
||||
xrange1 = Range1d(start=xaxmin,end=xaxmax)
|
||||
plot.x_range = xrange1
|
||||
plot.xaxis[0].formatter = DatetimeTickFormatter(
|
||||
hours = ["%H"],
|
||||
minutes = ["%M"],
|
||||
seconds = ["%S"],
|
||||
days = ["0"],
|
||||
months = [""],
|
||||
years = [""]
|
||||
)
|
||||
|
||||
|
||||
if yparam1 == 'pace':
|
||||
plot.y_range = Range1d(ymin,ymax)
|
||||
plot.yaxis[0].formatter = DatetimeTickFormatter(
|
||||
seconds = ["%S"],
|
||||
minutes = ["%M"]
|
||||
)
|
||||
|
||||
if yparam2 != 'None':
|
||||
yrange2 = Range1d(start=yaxminima[yparam2],end=yaxmaxima[yparam2])
|
||||
plot.extra_y_ranges = {"yax2": yrange2}
|
||||
plot.line('xvals','y2mean',color="black",y_range_name="yax2",
|
||||
source=source2)
|
||||
|
||||
if plottype=='line':
|
||||
plot.line('x1','y2',color="red",y_range_name="yax2",
|
||||
legend=axlabels[yparam2],
|
||||
source=source2)
|
||||
|
||||
elif plottype=='scatter':
|
||||
# plot.circle(x1,y2,color="red",y_range_name="yax2",legend=yparam2,
|
||||
# source=source,size=3)
|
||||
plot.scatter('x1','y2',source=source2,legend=axlabels[yparam2]
|
||||
,fill_alpha=0.4,
|
||||
line_color=None,color="red",y_range_name="yax2")
|
||||
|
||||
plot.add_layout(LinearAxis(y_range_name="yax2",
|
||||
axis_label=axlabels[yparam2]),'right')
|
||||
|
||||
|
||||
hover = plot.select(dict(type=HoverTool))
|
||||
|
||||
|
||||
hover.tooltips = OrderedDict([
|
||||
('Time','@time'),
|
||||
('Distance','@distance'),
|
||||
('Pace','@pace'),
|
||||
('HR','@hr'),
|
||||
('SPM','@spmc{1.1}'),
|
||||
('Power','@power{int}'),
|
||||
])
|
||||
|
||||
hover.mode = 'mouse'
|
||||
|
||||
layout = layoutrow([layoutcolumn([slider_spm_min,
|
||||
slider_spm_max,
|
||||
slider_dist_min,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,225 +0,0 @@
|
||||
import time
|
||||
from django.conf import settings
|
||||
from rowers.tasks import handle_sendemail_unrecognized
|
||||
from django_mailbox.models import Mailbox,Message,MessageAttachment
|
||||
from rowers.models import Workout, User, Rower, WorkoutForm,RowerForm,GraphImage,AdvancedWorkoutForm
|
||||
from django.core.files.base import ContentFile
|
||||
|
||||
from rowsandall_app.settings import BASE_DIR
|
||||
|
||||
from rowingdata import rower as rrower
|
||||
from rowingdata import main as rmain
|
||||
from rowingdata import rowingdata as rrdata
|
||||
from rowingdata import TCXParser,RowProParser,ErgDataParser,TCXParserNoHR
|
||||
from rowingdata import MysteryParser
|
||||
from rowingdata import painsledDesktopParser,speedcoachParser,ErgStickParser
|
||||
from rowingdata import SpeedCoach2Parser,FITParser,fitsummarydata
|
||||
from rowingdata import make_cumvalues
|
||||
from rowingdata import summarydata,get_file_type
|
||||
|
||||
from scipy.signal import savgol_filter
|
||||
|
||||
def rdata(file,rower=rrower()):
|
||||
try:
|
||||
res = rrdata(file,rower=rower)
|
||||
except IOError:
|
||||
res = 0
|
||||
|
||||
return res
|
||||
|
||||
def safeprocessattachments():
|
||||
try:
|
||||
return processattachments()
|
||||
except:
|
||||
return [0]
|
||||
|
||||
def processattachments():
|
||||
res = []
|
||||
attachments = MessageAttachment.objects.all()
|
||||
for a in attachments:
|
||||
donotdelete = 0
|
||||
m = Message.objects.get(id=a.message_id)
|
||||
from_address = m.from_address[0]
|
||||
name = m.subject
|
||||
|
||||
# get a list of users
|
||||
theusers = User.objects.filter(email=from_address)
|
||||
for u in theusers:
|
||||
try:
|
||||
rr = Rower.objects.get(user=u.id)
|
||||
# move attachment and make workout
|
||||
try:
|
||||
res += [make_new_workout_from_email(rr,a.document,name)]
|
||||
except:
|
||||
# replace with code to process error
|
||||
res += ['fail: '+name]
|
||||
donotdelete = 1
|
||||
except Rower.DoesNotExist:
|
||||
pass
|
||||
|
||||
|
||||
# remove attachment
|
||||
if donotdelete == 0:
|
||||
a.delete()
|
||||
|
||||
if m.attachments.exists()==False:
|
||||
# no attachments, so can be deleted
|
||||
m.delete()
|
||||
|
||||
mm = Message.objects.all()
|
||||
for m in mm:
|
||||
if m.attachments.exists()==False:
|
||||
m.delete()
|
||||
|
||||
return res
|
||||
|
||||
def make_new_workout_from_email(rr,f2,name):
|
||||
workouttype = 'rower'
|
||||
f2 = f2.name
|
||||
fileformat = get_file_type('media/'+f2)
|
||||
if fileformat == 'unknown':
|
||||
if settings.DEBUG:
|
||||
res = handle_sendemail_unrecognized.delay(f2,
|
||||
"roosendaalsander@gmail.com")
|
||||
|
||||
else:
|
||||
res = queuehigh.enqueue(handle_sendemail_unrecognized,
|
||||
f2,"roosendaalsander@gmail.com")
|
||||
|
||||
return 0
|
||||
|
||||
summary = ''
|
||||
# handle non-Painsled
|
||||
if (fileformat != 'csv'):
|
||||
# handle RowPro:
|
||||
if (fileformat == 'rp'):
|
||||
row = RowProParser('media/'+f2)
|
||||
|
||||
# handle TCX
|
||||
if (fileformat == 'tcx'):
|
||||
row = TCXParser('media/'+f2)
|
||||
|
||||
# handle Mystery
|
||||
if (fileformat == 'mystery'):
|
||||
row = MysteryParser('media/'+f2)
|
||||
|
||||
# handle TCX no HR
|
||||
if (fileformat == 'tcxnohr'):
|
||||
row = TCXParserNoHR('media/'+f2)
|
||||
|
||||
# handle ErgData
|
||||
if (fileformat == 'ergdata'):
|
||||
row = ErgDataParser('media/'+f2)
|
||||
|
||||
# handle painsled desktop
|
||||
if (fileformat == 'painsleddesktop'):
|
||||
row = painsledDesktopParser('media/'+f2)
|
||||
|
||||
# handle speed coach GPS
|
||||
if (fileformat == 'speedcoach'):
|
||||
row = speedcoachParser('media/'+f2)
|
||||
|
||||
# handle speed coach GPS 2
|
||||
if (fileformat == 'speedcoach2'):
|
||||
row = SpeedCoach2Parser('media/'+f2)
|
||||
|
||||
# handle ErgStick
|
||||
if (fileformat == 'ergstick'):
|
||||
row = ErgStickParser('media/'+f2)
|
||||
|
||||
# handle FIT
|
||||
if (fileformat == 'fit'):
|
||||
row = FITParser('media/'+f2)
|
||||
s = fitsummarydata('media/'+f2)
|
||||
s.setsummary()
|
||||
summary = s.summarytext
|
||||
|
||||
timestr = time.strftime("%Y%m%d-%H%M%S")
|
||||
filename = timestr+'o.csv'
|
||||
row.write_csv('media/'+filename)
|
||||
f2 = filename
|
||||
|
||||
# make workout and put in database
|
||||
#r = rrower(hrmax=rr.max,hrut2=rr.ut2,
|
||||
# hrut1=rr.ut1,hrat=rr.at,
|
||||
# hrtr=rr.tr,hran=rr.an)
|
||||
row = rdata('media/'+f2) #,rower=r)
|
||||
if row == 0:
|
||||
return 0
|
||||
|
||||
# change filename
|
||||
if f2[:5] != 'media':
|
||||
timestr = time.strftime("%Y%m%d-%H%M%S")
|
||||
f2 = 'media/'+timestr+'o.csv'
|
||||
|
||||
# auto smoothing
|
||||
pace = row.df[' Stroke500mPace (sec/500m)'].values
|
||||
velo = 500./pace
|
||||
|
||||
f = row.df['TimeStamp (sec)'].diff().mean()
|
||||
windowsize = 2*(int(10./(f)))+1
|
||||
|
||||
if not 'originalvelo' in row.df:
|
||||
row.df['originalvelo'] = velo
|
||||
|
||||
if windowsize > 3:
|
||||
velo2 = savgol_filter(velo,windowsize,3)
|
||||
else:
|
||||
velo2 = velo
|
||||
|
||||
pace2 = 500./abs(velo2)
|
||||
row.df[' Stroke500mPace (sec/500m)'] = pace2
|
||||
|
||||
row.df = row.df.fillna(0)
|
||||
|
||||
row.write_csv(f2)
|
||||
|
||||
# recalculate power data
|
||||
if workouttype == 'rower' or workouttype == 'dynamic' or workouttype == 'slides':
|
||||
try:
|
||||
row.erg_recalculatepower()
|
||||
# row.spm_fromtimestamps()
|
||||
row.write_csv(f2)
|
||||
except:
|
||||
pass
|
||||
|
||||
if fileformat != 'fit':
|
||||
summary = row.summary()
|
||||
summary += '\n'
|
||||
summary += row.intervalstats_painsled()
|
||||
|
||||
averagehr = row.df[' HRCur (bpm)'].mean()
|
||||
maxhr = row.df[' HRCur (bpm)'].max()
|
||||
|
||||
totaldist = row.df['cum_dist'].max()
|
||||
totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min()
|
||||
totaltime = totaltime+row.df.ix[0,' ElapsedTime (sec)']
|
||||
|
||||
|
||||
hours = int(totaltime/3600.)
|
||||
minutes = int((totaltime - 3600.*hours)/60.)
|
||||
seconds = int(totaltime - 3600.*hours - 60.*minutes)
|
||||
tenths = int(10*(totaltime - 3600.*hours - 60.*minutes - seconds))
|
||||
|
||||
|
||||
duration = "%s:%s:%s.%s" % (hours,minutes,seconds,tenths)
|
||||
|
||||
workoutdate = row.rowdatetime.strftime('%Y-%m-%d')
|
||||
workoutstarttime = row.rowdatetime.strftime('%H:%M:%S')
|
||||
|
||||
notes = 'imported through email'
|
||||
|
||||
w = Workout(user=rr,name=name,date=workoutdate,
|
||||
workouttype=workouttype,
|
||||
duration=duration,distance=totaldist,
|
||||
weightcategory=rr.weightcategory,
|
||||
starttime=workoutstarttime,
|
||||
csvfilename=f2,notes=notes,summary=summary,
|
||||
maxhr=maxhr,averagehr=averagehr,
|
||||
startdatetime=row.rowdatetime)
|
||||
|
||||
w.save()
|
||||
return 1
|
||||
|
||||
|
||||
|
||||
@@ -1,236 +0,0 @@
|
||||
#!/srv/venv/bin/python
|
||||
import sys
|
||||
import os
|
||||
# If you find a solution that does not need the two paths, please comment!
|
||||
sys.path.append('$path_to_root_of_project$')
|
||||
sys.path.append('$path_to_root_of_project$/$project_name$')
|
||||
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = '$project_name$.settings'
|
||||
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.conf import settings
|
||||
#from rowers.mailprocessing import processattachments
|
||||
import time
|
||||
from django.conf import settings
|
||||
from rowers.tasks import handle_sendemail_unrecognized
|
||||
from django_mailbox.models import Mailbox,Message,MessageAttachment
|
||||
from rowers.models import Workout, User, Rower, WorkoutForm,RowerForm,GraphImage,AdvancedWorkoutForm
|
||||
from django.core.files.base import ContentFile
|
||||
|
||||
from rowsandall_app.settings import BASE_DIR
|
||||
|
||||
from rowingdata import rower as rrower
|
||||
from rowingdata import main as rmain
|
||||
from rowingdata import rowingdata as rrdata
|
||||
from rowingdata import TCXParser,RowProParser,ErgDataParser,TCXParserNoHR
|
||||
from rowingdata import MysteryParser
|
||||
from rowingdata import painsledDesktopParser,speedcoachParser,ErgStickParser
|
||||
from rowingdata import SpeedCoach2Parser,FITParser,fitsummarydata
|
||||
from rowingdata import make_cumvalues
|
||||
from rowingdata import summarydata,get_file_type
|
||||
|
||||
from scipy.signal import savgol_filter
|
||||
|
||||
def rdata(file,rower=rrower()):
|
||||
try:
|
||||
res = rrdata(file,rower=rower)
|
||||
except IOError:
|
||||
res = 0
|
||||
|
||||
return res
|
||||
|
||||
def make_new_workout_from_email(rr,f2,name):
|
||||
workouttype = 'rower'
|
||||
f2 = f2.name
|
||||
fileformat = get_file_type('media/'+f2)
|
||||
if fileformat == 'unknown':
|
||||
if settings.DEBUG:
|
||||
res = handle_sendemail_unrecognized.delay(f2,
|
||||
"roosendaalsander@gmail.com")
|
||||
|
||||
else:
|
||||
res = queuehigh.enqueue(handle_sendemail_unrecognized,
|
||||
f2,"roosendaalsander@gmail.com")
|
||||
|
||||
return 0
|
||||
|
||||
summary = ''
|
||||
# handle non-Painsled
|
||||
if (fileformat != 'csv'):
|
||||
# handle RowPro:
|
||||
if (fileformat == 'rp'):
|
||||
row = RowProParser('media/'+f2)
|
||||
|
||||
# handle TCX
|
||||
if (fileformat == 'tcx'):
|
||||
row = TCXParser('media/'+f2)
|
||||
|
||||
# handle Mystery
|
||||
if (fileformat == 'mystery'):
|
||||
row = MysteryParser('media/'+f2)
|
||||
|
||||
# handle TCX no HR
|
||||
if (fileformat == 'tcxnohr'):
|
||||
row = TCXParserNoHR('media/'+f2)
|
||||
|
||||
# handle ErgData
|
||||
if (fileformat == 'ergdata'):
|
||||
row = ErgDataParser('media/'+f2)
|
||||
|
||||
# handle painsled desktop
|
||||
if (fileformat == 'painsleddesktop'):
|
||||
row = painsledDesktopParser('media/'+f2)
|
||||
|
||||
# handle speed coach GPS
|
||||
if (fileformat == 'speedcoach'):
|
||||
row = speedcoachParser('media/'+f2)
|
||||
|
||||
# handle speed coach GPS 2
|
||||
if (fileformat == 'speedcoach2'):
|
||||
row = SpeedCoach2Parser('media/'+f2)
|
||||
|
||||
# handle ErgStick
|
||||
if (fileformat == 'ergstick'):
|
||||
row = ErgStickParser('media/'+f2)
|
||||
|
||||
# handle FIT
|
||||
if (fileformat == 'fit'):
|
||||
row = FITParser('media/'+f2)
|
||||
s = fitsummarydata('media/'+f2)
|
||||
s.setsummary()
|
||||
summary = s.summarytext
|
||||
|
||||
timestr = time.strftime("%Y%m%d-%H%M%S")
|
||||
filename = timestr+'o.csv'
|
||||
row.write_csv('media/'+filename)
|
||||
f2 = filename
|
||||
|
||||
# make workout and put in database
|
||||
#r = rrower(hrmax=rr.max,hrut2=rr.ut2,
|
||||
# hrut1=rr.ut1,hrat=rr.at,
|
||||
# hrtr=rr.tr,hran=rr.an)
|
||||
row = rdata('media/'+f2) #,rower=r)
|
||||
if row == 0:
|
||||
return 0
|
||||
|
||||
# change filename
|
||||
if f2[:5] != 'media':
|
||||
timestr = time.strftime("%Y%m%d-%H%M%S")
|
||||
f2 = 'media/'+timestr+'o.csv'
|
||||
|
||||
# auto smoothing
|
||||
pace = row.df[' Stroke500mPace (sec/500m)'].values
|
||||
velo = 500./pace
|
||||
|
||||
f = row.df['TimeStamp (sec)'].diff().mean()
|
||||
windowsize = 2*(int(10./(f)))+1
|
||||
|
||||
if not 'originalvelo' in row.df:
|
||||
row.df['originalvelo'] = velo
|
||||
|
||||
if windowsize > 3:
|
||||
velo2 = savgol_filter(velo,windowsize,3)
|
||||
else:
|
||||
velo2 = velo
|
||||
|
||||
pace2 = 500./abs(velo2)
|
||||
row.df[' Stroke500mPace (sec/500m)'] = pace2
|
||||
|
||||
row.df = row.df.fillna(0)
|
||||
|
||||
row.write_csv(f2)
|
||||
|
||||
# recalculate power data
|
||||
if workouttype == 'rower' or workouttype == 'dynamic' or workouttype == 'slides':
|
||||
try:
|
||||
row.erg_recalculatepower()
|
||||
# row.spm_fromtimestamps()
|
||||
row.write_csv(f2)
|
||||
except:
|
||||
pass
|
||||
|
||||
if fileformat != 'fit':
|
||||
summary = row.summary()
|
||||
summary += '\n'
|
||||
summary += row.intervalstats_painsled()
|
||||
|
||||
averagehr = row.df[' HRCur (bpm)'].mean()
|
||||
maxhr = row.df[' HRCur (bpm)'].max()
|
||||
|
||||
totaldist = row.df['cum_dist'].max()
|
||||
totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min()
|
||||
totaltime = totaltime+row.df.ix[0,' ElapsedTime (sec)']
|
||||
|
||||
|
||||
hours = int(totaltime/3600.)
|
||||
minutes = int((totaltime - 3600.*hours)/60.)
|
||||
seconds = int(totaltime - 3600.*hours - 60.*minutes)
|
||||
tenths = int(10*(totaltime - 3600.*hours - 60.*minutes - seconds))
|
||||
|
||||
|
||||
duration = "%s:%s:%s.%s" % (hours,minutes,seconds,tenths)
|
||||
|
||||
workoutdate = row.rowdatetime.strftime('%Y-%m-%d')
|
||||
workoutstarttime = row.rowdatetime.strftime('%H:%M:%S')
|
||||
|
||||
notes = 'imported through email'
|
||||
|
||||
w = Workout(user=rr,name=name,date=workoutdate,
|
||||
workouttype=workouttype,
|
||||
duration=duration,distance=totaldist,
|
||||
weightcategory=rr.weightcategory,
|
||||
starttime=workoutstarttime,
|
||||
csvfilename=f2,notes=notes,summary=summary,
|
||||
maxhr=maxhr,averagehr=averagehr,
|
||||
startdatetime=row.rowdatetime)
|
||||
|
||||
w.save()
|
||||
return 1
|
||||
|
||||
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
def handle(self, *args, **options):
|
||||
res = []
|
||||
attachments = MessageAttachment.objects.all()
|
||||
for a in attachments:
|
||||
donotdelete = 0
|
||||
m = Message.objects.get(id=a.message_id)
|
||||
from_address = m.from_address[0]
|
||||
name = m.subject
|
||||
|
||||
# get a list of users
|
||||
theusers = User.objects.filter(email=from_address)
|
||||
for u in theusers:
|
||||
try:
|
||||
rr = Rower.objects.get(user=u.id)
|
||||
# move attachment and make workout
|
||||
try:
|
||||
res += [make_new_workout_from_email(rr,a.document,name)]
|
||||
except:
|
||||
# replace with code to process error
|
||||
res += ['fail: '+name]
|
||||
donotdelete = 1
|
||||
except Rower.DoesNotExist:
|
||||
pass
|
||||
|
||||
|
||||
# remove attachment
|
||||
if donotdelete == 0:
|
||||
a.delete()
|
||||
|
||||
if m.attachments.exists()==False:
|
||||
# no attachments, so can be deleted
|
||||
m.delete()
|
||||
|
||||
mm = Message.objects.all()
|
||||
for m in mm:
|
||||
if m.attachments.exists()==False:
|
||||
m.delete()
|
||||
|
||||
self.stdout.write(self.style.SUCCESS('Successfully processed email attachments'))
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,301 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django import forms
|
||||
from django.forms import ModelForm
|
||||
from django.dispatch import receiver
|
||||
from django.forms.widgets import SplitDateTimeWidget
|
||||
from datetimewidget.widgets import DateTimeWidget
|
||||
import os
|
||||
|
||||
# Create your models here.
|
||||
|
||||
class Team(models.Model):
|
||||
name = models.CharField(max_length=150)
|
||||
notes = models.CharField(blank=True,max_length=200)
|
||||
|
||||
|
||||
class Rower(models.Model):
|
||||
weightcategories = (
|
||||
('hwt','heavy-weight'),
|
||||
('lwt','light-weight'),
|
||||
)
|
||||
user = models.OneToOneField(User)
|
||||
max = models.IntegerField(default=192,verbose_name="Max Heart Rate")
|
||||
rest = models.IntegerField(default=48,verbose_name="Resting Heart Rate")
|
||||
ut2 = models.IntegerField(default=105,verbose_name="UT2 band lower HR")
|
||||
ut1 = models.IntegerField(default=146,verbose_name="UT1 band lower HR")
|
||||
at = models.IntegerField(default=160,verbose_name="AT band lower HR")
|
||||
tr = models.IntegerField(default=167,verbose_name="TR band lower HR")
|
||||
an = models.IntegerField(default=180,verbose_name="AN band lower HR")
|
||||
weightcategory = models.CharField(default="hwt",
|
||||
max_length=30,
|
||||
choices=weightcategories)
|
||||
|
||||
c2token = models.CharField(default='',max_length=200,blank=True,null=True)
|
||||
tokenexpirydate = models.DateTimeField(blank=True,null=True)
|
||||
c2refreshtoken = models.CharField(default='',max_length=200,blank=True,null=True)
|
||||
sporttrackstoken = models.CharField(default='',max_length=200,blank=True,null=True)
|
||||
sporttrackstokenexpirydate = models.DateTimeField(blank=True,null=True)
|
||||
sporttracksrefreshtoken = models.CharField(default='',max_length=200,
|
||||
blank=True,null=True)
|
||||
stravatoken = models.CharField(default='',max_length=200,blank=True,null=True)
|
||||
|
||||
plans = (
|
||||
('basic','basic'),
|
||||
('pro','pro'),
|
||||
('coach','coach')
|
||||
)
|
||||
rowerplan = models.CharField(default='basic',max_length=30,
|
||||
choices=plans)
|
||||
friends = models.ManyToManyField("self")
|
||||
|
||||
team = models.ForeignKey(Team,blank=True,null=True)
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return self.user.username
|
||||
|
||||
class Workout(models.Model):
|
||||
workouttypes = (
|
||||
('water','On-water'),
|
||||
('rower','Indoor Rower'),
|
||||
('skierg','Ski Erg'),
|
||||
('dynamic','Dynamic Indoor Rower'),
|
||||
('slides','Indoor Rower on Slides'),
|
||||
('paddle','Paddle Adapter'),
|
||||
('snow','On-snow'),
|
||||
)
|
||||
|
||||
boattypes = (
|
||||
('1x', '1x (single)'),
|
||||
('2x', '2x (double)'),
|
||||
('2-', '2- (pair)'),
|
||||
('4x', '4x (quad)'),
|
||||
('4-', '4- (four)'),
|
||||
('8+', '8+ (eight)'),
|
||||
)
|
||||
|
||||
user = models.ForeignKey(Rower)
|
||||
team = models.ForeignKey(Team,blank=True,null=True)
|
||||
name = models.CharField(max_length=150)
|
||||
date = models.DateField()
|
||||
workouttype = models.CharField(choices=workouttypes,max_length=50)
|
||||
boattype = models.CharField(choices=boattypes,max_length=50,
|
||||
default='1x (single)',
|
||||
verbose_name = 'Boat Type')
|
||||
starttime = models.TimeField(blank=True,null=True)
|
||||
startdatetime = models.DateTimeField(blank=True,null=True)
|
||||
distance = models.IntegerField(default=0,blank=True)
|
||||
duration = models.TimeField(default=1,blank=True)
|
||||
weightcategory = models.CharField(default="hwt",max_length=10)
|
||||
weightvalue = models.FloatField(default=80.0,blank=True,verbose_name = 'Average Crew Weight (kg)')
|
||||
csvfilename = models.CharField(blank=True,max_length=150)
|
||||
uploadedtoc2 = models.IntegerField(default=0)
|
||||
averagehr = models.IntegerField(blank=True,null=True)
|
||||
maxhr = models.IntegerField(blank=True,null=True)
|
||||
uploadedtostrava = models.IntegerField(default=0)
|
||||
uploadedtosporttracks = models.IntegerField(default=0)
|
||||
notes = models.CharField(blank=True,max_length=200)
|
||||
summary = models.TextField(blank=True)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
date = self.date
|
||||
name = self.name
|
||||
|
||||
str = date.strftime('%Y-%m-%d')+'_'+name
|
||||
|
||||
return str
|
||||
|
||||
# delete files belonging to workout instance
|
||||
# related GraphImage objects should be deleted automatically
|
||||
@receiver(models.signals.post_delete,sender=Workout)
|
||||
def auto_delete_file_on_delete(sender, instance, **kwargs):
|
||||
# delete CSV file
|
||||
if instance.csvfilename:
|
||||
if os.path.isfile(instance.csvfilename):
|
||||
os.remove(instance.csvfilename)
|
||||
|
||||
|
||||
|
||||
class GraphImage(models.Model):
|
||||
filename = models.CharField(default='',max_length=150,blank=True,null=True)
|
||||
creationdatetime = models.DateTimeField()
|
||||
workout = models.ForeignKey(Workout)
|
||||
|
||||
def __str__(self):
|
||||
return self.filename
|
||||
|
||||
# delete related file object
|
||||
@receiver(models.signals.post_delete,sender=GraphImage)
|
||||
def auto_delete_image_on_delete(sender,instance, **kwargs):
|
||||
if instance.filename:
|
||||
if os.path.isfile(instance.filename):
|
||||
os.remove(instance.filename)
|
||||
else:
|
||||
print "couldn't find the file "+instance.filename
|
||||
|
||||
|
||||
class DateInput(forms.DateInput):
|
||||
input_type = 'date'
|
||||
|
||||
|
||||
class WorkoutForm(ModelForm):
|
||||
duration = forms.TimeInput(format='%H:%M:%S.%f')
|
||||
class Meta:
|
||||
model = Workout
|
||||
fields = ['name','date','starttime','duration','distance','workouttype','notes']
|
||||
widgets = {
|
||||
'date': DateInput(),
|
||||
'notes': forms.Textarea,
|
||||
'duration': forms.TimeInput(format='%H:%M:%S.%f'),
|
||||
}
|
||||
|
||||
class AdvancedWorkoutForm(ModelForm):
|
||||
class Meta:
|
||||
model = Workout
|
||||
fields = ['boattype','weightvalue']
|
||||
|
||||
|
||||
class RowerForm(ModelForm):
|
||||
class Meta:
|
||||
model = Rower
|
||||
fields = ['rest','ut2','ut1','at','tr','an','max','weightcategory']
|
||||
|
||||
|
||||
def clean_rest(self):
|
||||
rest = self.cleaned_data['rest']
|
||||
|
||||
if rest<10:
|
||||
self.data['rest']=10
|
||||
raise forms.ValidationError("Resting heart rate should be higher than 10 bpm")
|
||||
|
||||
|
||||
if rest>250:
|
||||
self.data['rest'] = 250
|
||||
raise forms.ValidationError("Resting heart rate should be lower than 250 bpm")
|
||||
|
||||
|
||||
return rest
|
||||
|
||||
def clean_ut2(self):
|
||||
ut2 = self.cleaned_data['ut2']
|
||||
|
||||
if ut2<10:
|
||||
raise forms.ValidationError("UT2 heart rate should be higher than 10 bpm")
|
||||
|
||||
if ut2>250:
|
||||
raise forms.ValidationError("UT2 heart rate should be lower than 250 bpm")
|
||||
|
||||
return ut2
|
||||
|
||||
def clean_ut1(self):
|
||||
ut1 = self.cleaned_data['ut1']
|
||||
|
||||
if ut1<10:
|
||||
raise forms.ValidationError("UT1 heart rate should be higher than 10 bpm")
|
||||
|
||||
if ut1>250:
|
||||
raise forms.ValidationError("Resting heart rate should be lower than 250 bpm")
|
||||
|
||||
return ut1
|
||||
|
||||
def clean_at(self):
|
||||
at = self.cleaned_data['at']
|
||||
|
||||
if at<10:
|
||||
raise forms.ValidationError("AT heart rate should be higher than 10 bpm")
|
||||
|
||||
if at>250:
|
||||
raise forms.ValidationError("AT heart rate should be lower than 250 bpm")
|
||||
|
||||
return at
|
||||
|
||||
def clean_tr(self):
|
||||
tr = self.cleaned_data['tr']
|
||||
|
||||
if tr<10:
|
||||
raise forms.ValidationError("TR heart rate should be higher than 10 bpm")
|
||||
|
||||
if tr>250:
|
||||
raise forms.ValidationError("TR heart rate should be lower than 250 bpm")
|
||||
|
||||
return tr
|
||||
|
||||
def clean_an(self):
|
||||
an = self.cleaned_data['an']
|
||||
|
||||
if an<10:
|
||||
raise forms.ValidationError("AN heart rate should be higher than 10 bpm")
|
||||
|
||||
if an>250:
|
||||
raise forms.ValidationError("AN heart rate should be lower than 250 bpm")
|
||||
|
||||
return an
|
||||
|
||||
def clean_max(self):
|
||||
max = self.cleaned_data['max']
|
||||
|
||||
if max<10:
|
||||
raise forms.ValidationError("Max heart rate should be higher than 10 bpm")
|
||||
|
||||
if max>250:
|
||||
raise forms.ValidationError("Max heart rate should be lower than 250 bpm")
|
||||
|
||||
return max
|
||||
|
||||
|
||||
def clean(self):
|
||||
|
||||
|
||||
try:
|
||||
rest = self.cleaned_data['rest']
|
||||
except:
|
||||
rest = int(self.data['rest'])
|
||||
|
||||
try:
|
||||
ut2 = self.cleaned_data['ut2']
|
||||
except:
|
||||
ut2 = self.data['ut2']
|
||||
|
||||
try:
|
||||
ut1 = self.cleaned_data['ut1']
|
||||
except:
|
||||
ut1 = self.data['ut1']
|
||||
|
||||
try:
|
||||
at = self.cleaned_data['at']
|
||||
except:
|
||||
at = self.data['at']
|
||||
|
||||
try:
|
||||
an = self.cleaned_data['an']
|
||||
except:
|
||||
an = self.data['an']
|
||||
|
||||
try:
|
||||
tr = self.cleaned_data['tr']
|
||||
except:
|
||||
tr = self.data['tr']
|
||||
|
||||
try:
|
||||
max = self.cleaned_data['max']
|
||||
except:
|
||||
max = self.data['max']
|
||||
|
||||
if rest>=ut2:
|
||||
raise forms.ValidationError("Resting heart rate should be lower than UT2")
|
||||
if ut2>=ut1:
|
||||
raise forms.ValidationError("UT2 should be lower than UT1")
|
||||
if ut2>=ut1:
|
||||
raise forms.ValidationError("UT2 should be lower than UT1")
|
||||
if ut1>=at:
|
||||
raise forms.ValidationError("UT1 should be lower than AT")
|
||||
if at>=tr:
|
||||
raise forms.ValidationError("AT should be lower than TR")
|
||||
if tr>=an:
|
||||
raise forms.ValidationError("TR should be lower than AN")
|
||||
if an>=max:
|
||||
raise forms.ValidationError("AN should be lower than Max")
|
||||
@@ -1,133 +0,0 @@
|
||||
from matplotlib.ticker import MultipleLocator,FuncFormatter,NullFormatter
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
import numpy as np
|
||||
|
||||
def format_pace_tick(x,pos=None):
|
||||
min=int(x/60)
|
||||
sec=int(x-min*60.)
|
||||
sec_str=str(sec).zfill(2)
|
||||
template='%d:%s'
|
||||
return template % (min,sec_str)
|
||||
|
||||
def format_pace(x,pos=None):
|
||||
if isinf(x) or isnan(x):
|
||||
x=0
|
||||
|
||||
min=int(x/60)
|
||||
sec=(x-min*60.)
|
||||
|
||||
str1 = "{min:0>2}:{sec:0>4.1f}".format(
|
||||
min = min,
|
||||
sec = sec
|
||||
)
|
||||
|
||||
return str1
|
||||
|
||||
def format_time(x,pos=None):
|
||||
|
||||
|
||||
min = int(x/60.)
|
||||
sec = int(x-min*60)
|
||||
|
||||
str1 = "{min:0>2}:{sec:0>4.1f}".format(
|
||||
min=min,
|
||||
sec=sec,
|
||||
)
|
||||
|
||||
return str1
|
||||
|
||||
def format_dist_tick(x,pos=None):
|
||||
km = x/1000.
|
||||
template='%6.3f'
|
||||
return template % (km)
|
||||
|
||||
def format_time_tick(x,pos=None):
|
||||
hour=int(x/3600)
|
||||
min=int((x-hour*3600.)/60)
|
||||
min_str=str(min).zfill(2)
|
||||
template='%d:%s'
|
||||
return template % (hour,min_str)
|
||||
|
||||
def y_axis_range(ydata,miny=0,padding=.1,ultimate=[-1e9,1e9]):
|
||||
|
||||
# ydata must by a numpy array
|
||||
|
||||
ymin = np.ma.masked_invalid(ydata).min()
|
||||
ymax = np.ma.masked_invalid(ydata).max()
|
||||
|
||||
|
||||
yrange = ymax-ymin
|
||||
yrangemin = ymin
|
||||
yrangemax = ymax
|
||||
|
||||
|
||||
|
||||
if (yrange == 0):
|
||||
if ymin == 0:
|
||||
yrangemin = -padding
|
||||
else:
|
||||
yrangemin = ymin-ymin*padding
|
||||
if ymax == 0:
|
||||
yrangemax = padding
|
||||
else:
|
||||
yrangemax = ymax+ymax*padding
|
||||
else:
|
||||
yrangemin = ymin-padding*yrange
|
||||
yrangemax = ymax+padding*yrange
|
||||
|
||||
if (yrangemin < ultimate[0]):
|
||||
yrangemin = ultimate[0]
|
||||
|
||||
if (yrangemax > ultimate[1]):
|
||||
yrangemax = ultimate[1]
|
||||
|
||||
|
||||
|
||||
return [yrangemin,yrangemax]
|
||||
|
||||
def mkplot(row,title):
|
||||
df = row.df
|
||||
|
||||
t = df.ix[:,' ElapsedTime (sec)']
|
||||
p = df.ix[:,' Stroke500mPace (sec/500m)']
|
||||
hr = df.ix[:,' HRCur (bpm)']
|
||||
end_time = int(df.ix[df.shape[0]-1,'TimeStamp (sec)'])
|
||||
|
||||
fig, ax1 = plt.subplots(figsize=(5,4))
|
||||
|
||||
ax1.plot(t,p,'b-')
|
||||
ax1.set_xlabel('Time (h:m)')
|
||||
ax1.set_ylabel('(sec/500)')
|
||||
|
||||
yrange = y_axis_range(df.ix[:,' Stroke500mPace (sec/500m)'],
|
||||
ultimate = [85,190])
|
||||
plt.axis([0,end_time,yrange[1],yrange[0]])
|
||||
|
||||
ax1.set_xticks(range(1000,end_time,1000))
|
||||
ax1.set_yticks(range(185,90,-10))
|
||||
ax1.set_title(title)
|
||||
plt.grid(True)
|
||||
majorFormatter = FuncFormatter(format_pace_tick)
|
||||
majorLocator = (5)
|
||||
timeTickFormatter = NullFormatter()
|
||||
|
||||
ax1.yaxis.set_major_formatter(majorFormatter)
|
||||
|
||||
for tl in ax1.get_yticklabels():
|
||||
tl.set_color('b')
|
||||
|
||||
ax2 = ax1.twinx()
|
||||
ax2.plot(t,hr,'r-')
|
||||
ax2.set_ylabel('Heart Rate',color='r')
|
||||
majorTimeFormatter = FuncFormatter(format_time_tick)
|
||||
majorLocator = (15*60)
|
||||
ax2.xaxis.set_major_formatter(majorTimeFormatter)
|
||||
ax2.patch.set_alpha(0.0)
|
||||
for tl in ax2.get_yticklabels():
|
||||
tl.set_color('r')
|
||||
|
||||
plt.subplots_adjust(hspace=0)
|
||||
|
||||
return fig
|
||||
|
||||
@@ -1,271 +0,0 @@
|
||||
# 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
|
||||
from math import sin,cos,atan2,sqrt
|
||||
|
||||
import urllib
|
||||
import c2stuff
|
||||
|
||||
# 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, SPORTTRACKS_CLIENT_SECRET, SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI
|
||||
|
||||
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
|
||||
|
||||
def do_refresh_token(refreshtoken):
|
||||
client_auth = requests.auth.HTTPBasicAuth(SPORTTRACKS_CLIENT_ID, SPORTTRACKS_CLIENT_SECRET)
|
||||
post_data = {"grant_type": "refresh_token",
|
||||
"client_secret": SPORTTRACKS_CLIENT_SECRET,
|
||||
"client_id":SPORTTRACKS_CLIENT_ID,
|
||||
"refresh_token": refreshtoken,
|
||||
}
|
||||
headers = {'user-agent': 'sanderroosendaal',
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'}
|
||||
|
||||
url = "https://api.sporttracks.mobi/oauth2/token"
|
||||
|
||||
response = requests.post(url,
|
||||
data=json.dumps(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]
|
||||
|
||||
|
||||
def get_token(code):
|
||||
client_auth = requests.auth.HTTPBasicAuth(SPORTTRACKS_CLIENT_ID, SPORTTRACKS_CLIENT_SECRET)
|
||||
post_data = {"grant_type": "authorization_code",
|
||||
"code": code,
|
||||
"redirect_uri": SPORTTRACKS_REDIRECT_URI,
|
||||
"client_secret": SPORTTRACKS_CLIENT_SECRET,
|
||||
"client_id":SPORTTRACKS_CLIENT_ID,
|
||||
}
|
||||
headers = {'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'}
|
||||
|
||||
url = "https://api.sporttracks.mobi/oauth2/token"
|
||||
|
||||
|
||||
response = requests.post(url,
|
||||
data=json.dumps(post_data),
|
||||
headers=headers)
|
||||
token_json = response.json()
|
||||
thetoken = token_json['access_token']
|
||||
expires_in = token_json['expires_in']
|
||||
refresh_token = token_json['refresh_token']
|
||||
|
||||
return [thetoken,expires_in,refresh_token]
|
||||
|
||||
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": SPORTTRACKS_CLIENT_ID,
|
||||
"response_type": "code",
|
||||
"redirect_uri": SPORTTRACKS_REDIRECT_URI,
|
||||
"scope":"write",
|
||||
"state":state}
|
||||
|
||||
|
||||
import urllib
|
||||
url = "https://api.sporttracks.mobi/oauth2/authorize" +urllib.urlencode(params)
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
|
||||
def rower_sporttracks_token_refresh(user):
|
||||
r = Rower.objects.get(user=user)
|
||||
res = do_refresh_token(r.sporttracksrefreshtoken)
|
||||
access_token = res[0]
|
||||
expires_in = res[1]
|
||||
refresh_token = res[2]
|
||||
expirydatetime = timezone.now()+timedelta(seconds=expires_in)
|
||||
|
||||
r = Rower.objects.get(user=user)
|
||||
r.sporttrackstoken = access_token
|
||||
r.tokenexpirydate = expirydatetime
|
||||
r.sporttracksrefreshtoken = refresh_token
|
||||
|
||||
r.save()
|
||||
return r.sporttrackstoken
|
||||
|
||||
def get_sporttracks_workout_list(user):
|
||||
r = Rower.objects.get(user=user)
|
||||
if (r.sporttrackstoken == '') or (r.sporttrackstoken is None):
|
||||
s = "Token doesn't exist. Need to authorize"
|
||||
return custom_exception_handler(401,s)
|
||||
elif (timezone.now()>r.sporttrackstokenexpirydate):
|
||||
s = "Token expired. Needs to refresh."
|
||||
return custom_exception_handler(401,s)
|
||||
else:
|
||||
# ready to fetch. Hurray
|
||||
authorizationstring = str('Bearer ' + r.sporttrackstoken)
|
||||
headers = {'Authorization': authorizationstring,
|
||||
'user-agent': 'sanderroosendaal',
|
||||
'Content-Type': 'application/json'}
|
||||
url = "https://api.sporttracks.mobi/api/v2/fitnessActivities"
|
||||
s = requests.get(url,headers=headers)
|
||||
|
||||
return s
|
||||
|
||||
|
||||
def get_sporttracks_workout(user,sporttracksid):
|
||||
r = Rower.objects.get(user=user)
|
||||
if (r.sporttrackstoken == '') or (r.sporttrackstoken is None):
|
||||
return custom_exception_handler(401,s)
|
||||
s = "Token doesn't exist. Need to authorize"
|
||||
elif (timezone.now()>r.sporttrackstokenexpirydate):
|
||||
s = "Token expired. Needs to refresh."
|
||||
return custom_exception_handler(401,s)
|
||||
else:
|
||||
# ready to fetch. Hurray
|
||||
authorizationstring = str('Bearer ' + r.sporttrackstoken)
|
||||
headers = {'Authorization': authorizationstring,
|
||||
'user-agent': 'sanderroosendaal',
|
||||
'Content-Type': 'application/json'}
|
||||
url = "https://api.sporttracks.mobi/api/v2/fitnessActivities/"+str(sporttracksid)
|
||||
s = requests.get(url,headers=headers)
|
||||
|
||||
return s
|
||||
|
||||
def createsporttracksworkoutdata(w):
|
||||
filename = w.csvfilename
|
||||
row = rowingdata(filename)
|
||||
averagehr = int(row.df[' HRCur (bpm)'].mean())
|
||||
maxhr = int(row.df[' HRCur (bpm)'].max())
|
||||
|
||||
# adding diff, trying to see if this is valid
|
||||
t = row.df.ix[:,'TimeStamp (sec)'].values-10*row.df.ix[0,'TimeStamp (sec)']
|
||||
t[0] = t[1]
|
||||
d = row.df.ix[:,'cum_dist'].values
|
||||
d[0] = d[1]
|
||||
t = t.astype(int)
|
||||
d = d.astype(int)
|
||||
spm = row.df[' Cadence (stokes/min)'].astype(int)
|
||||
spm[0] = spm[1]
|
||||
hr = row.df[' HRCur (bpm)'].astype(int)
|
||||
|
||||
haslatlon=1
|
||||
|
||||
try:
|
||||
lat = row.df[' latitude'].values
|
||||
lon = row.df[' longitude'].values
|
||||
except KeyError:
|
||||
haslatlon = 0
|
||||
|
||||
haspower = 1
|
||||
try:
|
||||
power = row.df[' Power (watts)'].values
|
||||
except KeyError:
|
||||
haspower = 0
|
||||
|
||||
locdata = []
|
||||
hrdata = []
|
||||
spmdata = []
|
||||
distancedata = []
|
||||
powerdata = []
|
||||
|
||||
for i in range(len(t)):
|
||||
hrdata.append(t[i])
|
||||
hrdata.append(hr[i])
|
||||
distancedata.append(t[i])
|
||||
distancedata.append(d[i])
|
||||
spmdata.append(t[i])
|
||||
spmdata.append(spm[i])
|
||||
if haslatlon:
|
||||
locdata.append(t[i])
|
||||
locdata.append([lat[i],lon[i]])
|
||||
if haspower:
|
||||
powerdata.append(t[i])
|
||||
powerdata.append(power[i])
|
||||
|
||||
|
||||
if haslatlon:
|
||||
data = {
|
||||
"type": "Rowing",
|
||||
"name": w.name,
|
||||
"start_time": str(w.date)+"T"+str(w.starttime)+"Z",
|
||||
"total_distance": int(w.distance),
|
||||
"duration": int(max(t)),
|
||||
"notes": w.notes,
|
||||
"avg_heartrate": averagehr,
|
||||
"max_heartrate": maxhr,
|
||||
"location": locdata,
|
||||
"distance": distancedata,
|
||||
"cadence": spmdata,
|
||||
"heartrate": hrdata,
|
||||
}
|
||||
else:
|
||||
data = {
|
||||
"type": "Rowing",
|
||||
"name": w.name,
|
||||
"start_time": str(w.date)+"T"+str(w.starttime)+"Z",
|
||||
"total_distance": int(w.distance),
|
||||
"duration": int(max(t)),
|
||||
"notes": w.notes,
|
||||
"avg_heartrate": averagehr,
|
||||
"max_heartrate": maxhr,
|
||||
"distance": distancedata,
|
||||
"cadence": spmdata,
|
||||
"heartrate": hrdata,
|
||||
}
|
||||
|
||||
if haspower:
|
||||
data['power'] = powerdata
|
||||
|
||||
return data
|
||||
|
||||
def getidfromresponse(response):
|
||||
t = json.loads(response.text)
|
||||
uri = t['uris'][0]
|
||||
id = uri[len(uri)-13:len(uri)-5]
|
||||
|
||||
return int(id)
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
# Python
|
||||
import oauth2 as oauth
|
||||
import cgi
|
||||
import requests
|
||||
import requests.auth
|
||||
import json
|
||||
from django.utils import timezone
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
# 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
|
||||
@@ -1,267 +0,0 @@
|
||||
# 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
|
||||
from math import sin,cos,atan2,sqrt
|
||||
|
||||
# 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
|
||||
|
||||
import stravalib
|
||||
|
||||
from rowsandall_app.settings import C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_SECRET
|
||||
|
||||
def ewmovingaverage(interval,window_size):
|
||||
# Experimental code using Exponential Weighted moving average
|
||||
|
||||
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
|
||||
|
||||
return interval2
|
||||
|
||||
def geo_distance(lat1,lon1,lat2,lon2):
|
||||
""" Approximate distance and bearing between two points
|
||||
defined by lat1,lon1 and lat2,lon2
|
||||
This is a slight underestimate but is close enough for our purposes,
|
||||
We're never moving more than 10 meters between trackpoints
|
||||
|
||||
Bearing calculation fails if one of the points is a pole.
|
||||
|
||||
"""
|
||||
|
||||
# radius of earth in km
|
||||
R = 6373.0
|
||||
|
||||
# pi
|
||||
pi = math.pi
|
||||
|
||||
lat1 = math.radians(lat1)
|
||||
lat2 = math.radians(lat2)
|
||||
lon1 = math.radians(lon1)
|
||||
lon2 = math.radians(lon2)
|
||||
|
||||
dlon = lon2 - lon1
|
||||
dlat = lat2 - lat1
|
||||
|
||||
a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2
|
||||
c = 2 * atan2(sqrt(a), sqrt(1 - a))
|
||||
|
||||
distance = R * c
|
||||
|
||||
tc1 = atan2(sin(lon2-lon1)*cos(lat2),
|
||||
cos(lat1)*sin(lat2)-sin(lat1)*cos(lat2)*cos(lon2-lon1))
|
||||
|
||||
tc1 = tc1 % (2*pi)
|
||||
|
||||
bearing = math.degrees(tc1)
|
||||
|
||||
return [distance,bearing]
|
||||
|
||||
|
||||
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
|
||||
|
||||
def get_token(code):
|
||||
client_auth = requests.auth.HTTPBasicAuth(STRAVA_CLIENT_ID, STRAVA_CLIENT_SECRET)
|
||||
post_data = {"grant_type": "authorization_code",
|
||||
"code": code,
|
||||
"redirect_uri": STRAVA_REDIRECT_URI,
|
||||
"client_secret": STRAVA_CLIENT_SECRET,
|
||||
"client_id":STRAVA_CLIENT_ID,
|
||||
}
|
||||
headers = {'user-agent': 'sanderroosendaal'}
|
||||
response = requests.post("https://www.strava.com/oauth/token",
|
||||
data=post_data,
|
||||
headers=headers)
|
||||
token_json = response.json()
|
||||
thetoken = token_json['access_token']
|
||||
|
||||
return [thetoken]
|
||||
|
||||
|
||||
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": STRAVA_CLIENT_ID,
|
||||
"response_type": "code",
|
||||
"redirect_uri": STRAVA_REDIRECT_URI,
|
||||
"scope":"write"}
|
||||
import urllib
|
||||
url = "https://www.strava.com/oauth/authorize" +urllib.urlencode(params)
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
def get_strava_workout_list(user):
|
||||
r = Rower.objects.get(user=user)
|
||||
if (r.stravatoken == '') or (r.stravatoken is None):
|
||||
s = "Token doesn't exist. Need to authorize"
|
||||
return custom_exception_handler(401,s)
|
||||
else:
|
||||
# ready to fetch. Hurray
|
||||
authorizationstring = str('Bearer ' + r.stravatoken)
|
||||
headers = {'Authorization': authorizationstring,
|
||||
'user-agent': 'sanderroosendaal',
|
||||
'Content-Type': 'application/json'}
|
||||
url = "https://www.strava.com/api/v3/athlete/activities"
|
||||
s = requests.get(url,headers=headers)
|
||||
|
||||
return s
|
||||
|
||||
def get_strava_workout(user,stravaid):
|
||||
r = Rower.objects.get(user=user)
|
||||
if (r.stravatoken == '') or (r.stravatoken is None):
|
||||
s = "Token doesn't exist. Need to authorize"
|
||||
return custom_exception_handler(401,s)
|
||||
else:
|
||||
# ready to fetch. Hurray
|
||||
fetchresolution = 'high'
|
||||
authorizationstring = str('Bearer ' + r.stravatoken)
|
||||
headers = {'Authorization': authorizationstring,
|
||||
'user-agent': 'sanderroosendaal',
|
||||
'Content-Type': 'application/json',
|
||||
'resolution': 'medium',}
|
||||
url = "https://www.strava.com/api/v3/activities/"+str(stravaid)
|
||||
workoutsummary = requests.get(url,headers=headers).json()
|
||||
|
||||
workoutsummary['timezone'] = "Etc/UTC"
|
||||
startdatetime = workoutsummary['start_date']
|
||||
|
||||
url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/cadence?resolution="+fetchresolution
|
||||
spmjson = requests.get(url,headers=headers)
|
||||
url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/heartrate?resolution="+fetchresolution
|
||||
hrjson = requests.get(url,headers=headers)
|
||||
url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/time?resolution="+fetchresolution
|
||||
timejson = requests.get(url,headers=headers)
|
||||
url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/velocity_smooth?resolution="+fetchresolution
|
||||
velojson = requests.get(url,headers=headers)
|
||||
url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/distance?resolution="+fetchresolution
|
||||
distancejson = requests.get(url,headers=headers)
|
||||
url = "https://www.strava.com/api/v3/activities/"+str(stravaid)+"/streams/latlng?resolution="+fetchresolution
|
||||
latlongjson = requests.get(url,headers=headers)
|
||||
|
||||
t = np.array(timejson.json()[0]['data'])
|
||||
d = np.array(distancejson.json()[0]['data'])
|
||||
nr_rows = len(t)
|
||||
|
||||
try:
|
||||
spm = np.array( spmjson.json()[1]['data'])
|
||||
except IndexError:
|
||||
spm = np.zeros(nr_rows)
|
||||
|
||||
try:
|
||||
hr = np.array(hrjson.json()[1]['data'])
|
||||
except IndexError:
|
||||
hr = np.zeros(nr_rows)
|
||||
|
||||
try:
|
||||
velo = np.array(velojson.json()[1]['data'])
|
||||
except IndexError:
|
||||
velo = np.zeros(nr_rows)
|
||||
|
||||
dt = np.diff(t).mean()
|
||||
wsize = round(5./dt)
|
||||
|
||||
velo2 = ewmovingaverage(velo,wsize)
|
||||
coords = np.array(latlongjson.json()[0]['data'])
|
||||
try:
|
||||
lat = coords[:,0]
|
||||
lon = coords[:,1]
|
||||
except IndexError:
|
||||
lat = np.zeros(len(t))
|
||||
lon = np.zeros(len(t))
|
||||
|
||||
|
||||
|
||||
|
||||
strokelength = velo*60./(spm)
|
||||
strokelength[np.isinf(strokelength)] = 0.0
|
||||
|
||||
pace = 500./(1.0*velo2)
|
||||
pace[np.isinf(pace)] = 0.0
|
||||
|
||||
df = pd.DataFrame({'t':10*t,
|
||||
'd':10*d,
|
||||
'p':10*pace,
|
||||
'spm':spm,
|
||||
'hr':hr,
|
||||
'lat':lat,
|
||||
'lon':lon,
|
||||
'strokelength':strokelength,
|
||||
})
|
||||
|
||||
# startdatetime = datetime.datetime.strptime(startdatetime,"%Y-%m-%d-%H:%M:%S")
|
||||
|
||||
return [workoutsummary,df]
|
||||
|
||||
def createstravaworkoutdata(w):
|
||||
filename = w.csvfilename
|
||||
row = rowingdata(filename)
|
||||
tcxfilename = filename[:-4]+'.tcx'
|
||||
row.exporttotcx(tcxfilename)
|
||||
|
||||
print tcxfilename
|
||||
|
||||
return tcxfilename
|
||||
|
||||
def handle_stravaexport(file,workoutname,stravatoken,description=''):
|
||||
# w = Workout.objects.get(id=workoutid)
|
||||
client = stravalib.Client(access_token=stravatoken)
|
||||
|
||||
act = client.upload_activity(file,'tcx',name=workoutname)
|
||||
res = act.wait(poll_interval=5.0)
|
||||
|
||||
|
||||
# description doesn't work yet. Have to wait for stravalib to update
|
||||
act = client.update_activity(res.id,activity_type='Rowing',description=description)
|
||||
|
||||
|
||||
# w.uploadedtostrava = res.id
|
||||
# w.save()
|
||||
file.close()
|
||||
|
||||
return res.id
|
||||
|
||||
|
||||
@@ -1,172 +0,0 @@
|
||||
from celery import Celery,app
|
||||
import os
|
||||
import time
|
||||
import gc
|
||||
|
||||
import rowingdata
|
||||
from rowingdata import main as rmain
|
||||
from rowingdata import rowingdata as rdata
|
||||
import rowingdata
|
||||
#from rowers.models import Workout
|
||||
from matplotlib.backends.backend_agg import FigureCanvas
|
||||
#from matplotlib.backends.backend_cairo import FigureCanvasCairo as FigureCanvas
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib import figure
|
||||
|
||||
import stravalib
|
||||
|
||||
from django.core.mail import send_mail, BadHeaderError,EmailMessage
|
||||
|
||||
|
||||
@app.task
|
||||
def add(x, y):
|
||||
return x + y
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_sendemail_unrecognized(unrecognizedfile,useremail):
|
||||
|
||||
# send email with attachment
|
||||
fullemail = 'roosendaalsander@gmail.com'
|
||||
subject = "Unrecognized file from Rowsandall.com"
|
||||
message = "Dear Sander,\n\n"
|
||||
message += "Please find attached a file that someone tried to upload to rowsandall.com. The file was not recognized as a valid file type.\n\n"
|
||||
message += "User Email "+useremail+"\n\n"
|
||||
message += "Best Regards, the Rowsandall Team"
|
||||
|
||||
email = EmailMessage(subject, message,
|
||||
'Rowsandall <info@rowsandall.com>',
|
||||
[fullemail])
|
||||
|
||||
|
||||
email.attach_file(unrecognizedfile)
|
||||
|
||||
res = email.send()
|
||||
|
||||
# remove tcx file
|
||||
os.remove(unrecognizedfile)
|
||||
return 1
|
||||
|
||||
|
||||
@app.task
|
||||
def handle_sendemailtcx(first_name,last_name,email,tcxfile):
|
||||
|
||||
# send email with attachment
|
||||
fullemail = first_name + " " + last_name + " " + "<" + email + ">"
|
||||
subject = "File from Rowsandall.com"
|
||||
message = "Dear "+first_name+",\n\n"
|
||||
message += "Please find attached the requested file for your workout.\n\n"
|
||||
message += "Best Regards, the Rowsandall Team"
|
||||
|
||||
email = EmailMessage(subject, message,
|
||||
'Rowsandall <info@rowsandall.com>',
|
||||
[fullemail])
|
||||
|
||||
|
||||
email.attach_file(tcxfile)
|
||||
|
||||
res = email.send()
|
||||
|
||||
# remove tcx file
|
||||
os.remove(tcxfile)
|
||||
return 1
|
||||
|
||||
@app.task
|
||||
def handle_otwsetpower(f1,boattype,weightvalue,first_name,last_name,email,workoutid):
|
||||
rowdata = rdata(f1)
|
||||
weightvalue = float(weightvalue)
|
||||
|
||||
# do something with boat type
|
||||
boatfile = {
|
||||
'1x':'static/rigging/1x.txt',
|
||||
'2x':'static/rigging/2x.txt',
|
||||
'2-':'static/rigging/2-.txt',
|
||||
'4x':'static/rigging/4x.txt',
|
||||
'4-':'static/rigging/4-.txt',
|
||||
'8+':'static/rigging/8+.txt',
|
||||
}
|
||||
try:
|
||||
rg = rowingdata.getrigging(boatfile[boattype])
|
||||
except KeyError:
|
||||
rg = rowingdata.getrigging('static/rigging/1x.txt')
|
||||
|
||||
# do calculation
|
||||
rowdata.otw_setpower_silent(skiprows=5,mc=weightvalue,rg=rg)
|
||||
|
||||
# save data
|
||||
rowdata.write_csv(f1)
|
||||
|
||||
# send email
|
||||
fullemail = first_name + " " + last_name + " " + "<" + email + ">"
|
||||
subject = "Your Rowsandall OTW calculations are ready"
|
||||
message = "Dear "+first_name+",\n\n"
|
||||
message += "Your Rowsandall OTW calculations are ready.\n"
|
||||
# message += "You can now create OTW plots with power information and wind corrections.\n\n"
|
||||
message += "Thank you for using rowsandall.com.\n\n"
|
||||
message += "Rowsandall OTW calculations have not been fully implemented yet.\n"
|
||||
message += "We are now running an experimental version for debugging purposes. \n"
|
||||
message += "Your wind/stream corrected plot is available here: http://rowsandall.com/rowers/workout/"
|
||||
message += workoutid
|
||||
message +="/interactiveotwplot\n\n"
|
||||
# message += "This functionality will be available soon, though.\n\n"
|
||||
message += "Please report any bugs/inconsistencies/unexpected results at rowsandall.slack.com or by reply to this email.\n\n"
|
||||
message += "Best Regards, The Rowsandall Physics Department."
|
||||
|
||||
send_mail(subject, message,
|
||||
'Rowsandall Physics Department <info@rowsandall.com>',
|
||||
[fullemail])
|
||||
|
||||
return 1
|
||||
|
||||
@app.task
|
||||
def handle_makeplot(f1,f2,t,hrdata,plotnr,imagename):
|
||||
hrmax = hrdata['hrmax']
|
||||
hrut2 = hrdata['hrut2']
|
||||
hrut1 = hrdata['hrut1']
|
||||
hrat = hrdata['hrat']
|
||||
hrtr = hrdata['hrtr']
|
||||
hran = hrdata['hran']
|
||||
|
||||
|
||||
rr = rowingdata.rower(hrmax=hrmax,hrut2=hrut2,
|
||||
hrut1=hrut1,hrat=hrat,
|
||||
hrtr=hrtr,hran=hran)
|
||||
row = rdata(f2,rower=rr)
|
||||
nr_rows = len(row.df)
|
||||
if (plotnr in [1,2,4,5,8,11,9,12]) and (nr_rows > 1200):
|
||||
bin = int(nr_rows/1200.)
|
||||
df = row.df.groupby(lambda x:x/bin).mean()
|
||||
row.df = df
|
||||
nr_rows = len(row.df)
|
||||
if (plotnr==1):
|
||||
fig1 = row.get_timeplot_erg(t)
|
||||
elif (plotnr==2):
|
||||
fig1 = row.get_metersplot_erg(t)
|
||||
elif (plotnr==3):
|
||||
fig1 = row.get_piechart(t)
|
||||
elif (plotnr==4):
|
||||
fig1 = row.get_timeplot_otw(t)
|
||||
elif (plotnr==5):
|
||||
fig1 = row.get_metersplot_otw(t)
|
||||
elif (plotnr==6):
|
||||
fig1 = row.get_piechart(t)
|
||||
elif (plotnr==7) or (plotnr==10):
|
||||
fig1 = row.get_metersplot_erg2(t)
|
||||
elif (plotnr==8) or (plotnr==11):
|
||||
fig1 = row.get_timeplot_erg2(t)
|
||||
elif (plotnr==9) or (plotnr==12):
|
||||
fig1 = row.get_time_otwpower(t)
|
||||
|
||||
canvas = FigureCanvas(fig1)
|
||||
|
||||
# plt.savefig('static/plots/'+imagename,format='png')
|
||||
canvas.print_figure('static/plots/'+imagename)
|
||||
# plt.imsave(fname='static/plots/'+imagename)
|
||||
plt.close(fig1)
|
||||
fig1.clf()
|
||||
gc.collect()
|
||||
return imagename
|
||||
|
||||
|
||||
def add2(x,y):
|
||||
return x+y
|
||||
@@ -1,16 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}Change Workout {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="grid_12">
|
||||
<h1>Page not found</h1>
|
||||
<p>
|
||||
We could not find the page on our server.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,16 +0,0 @@
|
||||
{% extends "base.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 %}
|
||||
@@ -1,16 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}Change Workout {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="grid_12">
|
||||
<h1>Page not found</h1>
|
||||
<p>
|
||||
We could not find the page on our server.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,16 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}Change Workout {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="grid_12">
|
||||
<h1>Internal Server Error</h1>
|
||||
<p>
|
||||
The site reported an internal server error. If this behavior repeats, please inform us by using the contact form at the bottom of this page. This allows us to improve the site's behaviour.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,123 +0,0 @@
|
||||
|
||||
{% extends "base.html" %}
|
||||
{% block title %}About us{% endblock title %}
|
||||
{% block content %}
|
||||
|
||||
<div class="grid_6 alpha">
|
||||
<h2>Introduction</h2>
|
||||
<p>This is a solution for the self-tracking rowers.</p>
|
||||
<p>Some of us use Concept2 rowing machines. Some of us are On-The-Water
|
||||
rowers. All of us will use smartphone apps, smart watches, fitness (GPS)
|
||||
watches, etc. to track our activities.</p>
|
||||
<p>Most of them will cross-train. Bike. Run. Skate.</p>
|
||||
<p>That means, the Concept2 logbook is not a sufficient training logbook for us.</p>
|
||||
<p>At the same time, the Concept2 logbook is used in rankings, for challenges,
|
||||
and more. Many of us will want to log all our rowing on the Concept2 logbook.</p>
|
||||
<p>So there are a couple of challenges here:</p>
|
||||
<ul>
|
||||
<li><p>How do I get my erg rows on Strava/SportTracks/Garmin Connect?</p>
|
||||
<blockquote>
|
||||
<ul>
|
||||
<li>Use an ANT+ device, like explained here: <a href="https://dr3do.wordpress.com/2015/07/09/hurray/" rel="nofollow">https://dr3do.wordpress.com/2015/07/09/hurray/</a></li>
|
||||
<li>Import from RowPro to SportTracks</li>
|
||||
<li>There are many smartphone apps to capture data from the PM3/4/5 erg monitor. Not many of them export in a format that is suitable for upload to the above-mentioned sites.</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
</li>
|
||||
<li><p>How do I get all my rows (including OTW) into the Concept2 logbook</p>
|
||||
<blockquote>
|
||||
<ul>
|
||||
<li>For On-Water and Erg: Add them manually</li>
|
||||
<li>For erg: Upload from ErgData, RowPro, Concept2 utility</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
</li>
|
||||
</ul>
|
||||
<p>This project aims at giving you ways to:</p>
|
||||
<ul>
|
||||
<li><p>Upload fitness data captured in TCX format to the Concept2 logbook (implemented)</p>
|
||||
<blockquote>
|
||||
<ul>
|
||||
<li>This should cover all your On-Water activities, whether they are captured with a SpeedCoach, a GPS fitness watch, your phone, or any other device. As long as you are able to export a valid TCX file.</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
</li>
|
||||
<li><p>Get erg data captured with apps that have no <q>upload to Concept2</q> functionality and upload them to the Concept2 logbook (implemented)</p>
|
||||
<blockquote>
|
||||
<ul>
|
||||
<li>For example: painsled</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
<li><p>Create useful plots. Who wants to be limited to what the on-line logbooks plot. Get your data and create:</p>
|
||||
<blockquote>
|
||||
<ul>
|
||||
<li>Color HR band charts or Pie Charts (implemented)</li>
|
||||
<li>Plot drive length, drive time, and other erg related parameters as a function of time or distance (to be implemented)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="grid_6 omega">
|
||||
<div class="grid_6">
|
||||
<h2>Credits</h2>
|
||||
<p>The project is based on python plotting code by
|
||||
Greg Smith (<a href="https://quantifiedrowing.wordpress.com/" rel="nofollow">https://quantifiedrowing.wordpress.com/</a>)
|
||||
and inspired by the RowPro Dan Burpee spreadsheet
|
||||
(<a href="http://www.sub7irc.com/RP_Split_Template.zip" rel="nofollow">http://www.sub7irc.com/RP_Split_Template.zip</a>).</p>
|
||||
</div>
|
||||
<div class="grid_6">
|
||||
<h2>Pro Membership</h2>
|
||||
|
||||
<p>Donations are welcome to keep this web site going. To help cover the hosting
|
||||
costs, I have created a <q>Pro</q> membership option (for only 5 EURO per year). Once I process your
|
||||
donation, I will give you access to some <q>special</q> features on this
|
||||
website. </p>
|
||||
|
||||
<p>Currently, the Pro membership will give you the following extra functionality (and more will follow):
|
||||
<ul>
|
||||
<li>More stroke metrics plots</li>
|
||||
<li>Power curves for OTW rowing</li>
|
||||
<li>Power histogram</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<p>Click on the PayPal button to pay for your Pro membership. It will be valid for one year with automatic renewal which you can stop at any time.
|
||||
You will be taken to the secure PayPal payment site.
|
||||
<ul>
|
||||
<li>Please mention the username you are registered under in "instructions to seller".</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
|
||||
<input type="hidden" name="cmd" value="_s-xclick">
|
||||
<input type="hidden" name="hosted_button_id" value="964GLEXX3THAW">
|
||||
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_subscribeCC_LG_global.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online.">
|
||||
<img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
|
||||
</form>
|
||||
|
||||
<h2>What's new?</h2>
|
||||
|
||||
<p>
|
||||
<ul>
|
||||
<li>2016-09-30 Stroke Analysis Plot - with date range filtering</li>
|
||||
<li>2016-09-29 Improved Flex plot, Power Histogram and Ranking Pieces - with date range filtering</li>
|
||||
<li>2016-09-20 Added the Power histogram</li>
|
||||
<li>2016-08-31 Added the Ranking Piece summary and pace predictor</li>
|
||||
<li>2016-08-02 Added support for the SpeedCoach GPS 2 CSV/FIT file export</li>
|
||||
<li>2016-07-19 Added the possibility to download wind data from <a href="http://forecast.io">The Dark Sky / Forecast.io</a></li>
|
||||
<li>2016-07-19 New Flexible interactive charts for OTE and OTW (pick your own axes parameters)</li>
|
||||
<li>2016-07-07 Wind and Stream corrections for OTW (Pro functionality)</li>
|
||||
<li>2016-06-23 Pro users can now compare workouts</li>
|
||||
<li>2016-06-20 Fixed Strava upload and added SportTracks import and export. The export is not working reliably. We are debugging this,</li>
|
||||
<li>2016-06-08 Added possibility to upload CrewNerd summary CSV file for Pro Members</li>
|
||||
<li>2016-06-08 Added workout summaries</li>
|
||||
<li>2016-06-05 Export to Strava is working</li>
|
||||
<li>2016-06-01 We're approved on the Concept2 logbook!!!!
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock content %}
|
||||
@@ -1,9 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block title %}Here's your result{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
Waiting for task with result {{ task }}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}Advanced Features {% 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>Advanced Workout Editor</h1>
|
||||
{% if user.rower.rowerplan == 'basic' %}
|
||||
<p>This is a preview of the page with advanced functionality for Pro users. See <a href="/rowers/promembership">the page about Pro membership</a> for more information and to sign up for Pro Membership</a>
|
||||
{% endif %}
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/edit">Edit Workout</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2 suffix_2 omega">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/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 }}">http://rowsandall.com/rowers/workout/{{ workout.id }}</a>
|
||||
<td>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="grid_6 alpha">
|
||||
<div class="grid_2 alpha">
|
||||
{% if user.rower.rowerplan == 'pro' %}
|
||||
<a class="button blue small" href="/rowers/workout/compare/{{ workout.id }}">Compare Workouts</a>
|
||||
{% else %}
|
||||
<a class="button blue small" href="/rowers/promembership/">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 }}/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.rower.rowerplan == 'pro' %}
|
||||
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/editintervals">Edit Intervals</a>
|
||||
{% else %}
|
||||
<a class="button blue small" href="/rowers/promembership">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.rower.rowerplan == 'pro' %}
|
||||
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/adddistanceplot2">Dist Metrics Plot</a>
|
||||
{% else %}
|
||||
<a class="button blue small" href="/rowers/promembership">Dist Metrics Plot</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
<p>
|
||||
Various advanced stroke metrics plotted versus distance.
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2">
|
||||
<p>
|
||||
{% if user.rower.rowerplan == 'pro' %}
|
||||
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/addtimeplot2">Time Metrics Plot</a>
|
||||
{% else %}
|
||||
<a class="button blue small" href="/rowers/promembership">Time Metrics Plot</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
<p>
|
||||
Various advanced stroke metrics plotted versus time.
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2 omega">
|
||||
<p>
|
||||
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/interactiveplot">Big Interactive Plot</a>
|
||||
</p>
|
||||
<p>
|
||||
See (and save) the big interactive plot
|
||||
</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.11.1.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>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,208 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}Advanced Features {% 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>Advanced OTW features</h1>
|
||||
{% if user.rower.rowerplan == 'basic' %}
|
||||
<p>This is a preview of the page with advanced functionality for Pro users. See <a href="/rowers/promembership">the page about Pro membership</a> for more information and to sign up for Pro Membership</a>
|
||||
{% endif %}
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/edit">Edit Workout</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2 suffix_2 omega">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/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 }}">http://rowsandall.com/rowers/workout/{{ workout.id }}</a>
|
||||
<td>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="grid_6 alpha">
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
{% if user.rower.rowerplan == 'pro' %}
|
||||
<a class="button blue small" href="/rowers/workout/compare/{{ workout.id }}">Compare Workouts</a>
|
||||
{% else %}
|
||||
<a class="button blue small" href="/rowers/promembership">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>
|
||||
{% if user.rower.rowerplan == 'pro' %}
|
||||
<a class="button blue small"href="/rowers/workout/{{ workout.id }}/smoothenpace">Smooth out Pace Data</a>
|
||||
{% else %}
|
||||
<a class="button blue small" href="/rowers/promembership">Smooth out Pace Data</a>
|
||||
{% endif %}
|
||||
|
||||
</p>
|
||||
<p>
|
||||
This will reduce noise on your pace data (EWMA average). The smoothing is irreversible
|
||||
but you can use the reset smoothing button.
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2 omega">
|
||||
<p>
|
||||
{% if user.rower.rowerplan == 'pro' %}
|
||||
<a class="button blue small"href="/rowers/workout/{{ workout.id }}/undosmoothenpace">Raw Data</a>
|
||||
{% else %}
|
||||
<a class="button blue small" href="/rowers/promembership">Reset Smoothing</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
<p>
|
||||
Reset pace data to values before smoothing (as originally imported/uploaded)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid_6 alpha">
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
{% if user.rower.rowerplan == 'pro' %}
|
||||
<a class="button blue small"href="/rowers/workout/{{ workout.id }}/crewnerdsummary">CrewNerd Summary</a>
|
||||
{% else %}
|
||||
<a class="button blue small" href="/rowers/promembership">CrewNerd Summary</a>
|
||||
{% endif %}
|
||||
|
||||
</p>
|
||||
<p>
|
||||
Upload a CrewNerd Summary (CSV file) to this workout.
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2">
|
||||
<p>
|
||||
{% if user.rower.rowerplan == 'pro' %}
|
||||
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/geeky">Geeky Stuff</a>
|
||||
{% else %}
|
||||
<a class="button blue small" href="/rowers/promembership">Geeky Stuff</a>
|
||||
{% endif %}
|
||||
|
||||
</p>
|
||||
<p>
|
||||
Add weather and current data and OTW power calculations.
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2 omega">
|
||||
<p>
|
||||
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/interactiveplot">Big Interactive Plot</a>
|
||||
</p>
|
||||
<p>
|
||||
See (and save) the big interactive plot
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="grid_6 alpha">
|
||||
|
||||
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/flexchart">Flexible Interactive Plot</a>
|
||||
</p>
|
||||
<p>
|
||||
Flexible Interactive plot. Pick your own X and Y axis parameters.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<div class="grid_2 tooltip">
|
||||
<p>
|
||||
{% if user.rower.rowerplan == 'pro' %}
|
||||
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/addotwpowerplot">OTW Power Plot</a>
|
||||
{% else %}
|
||||
<a class="button blue small" href="/rowers/promembership">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 class="grid_2 omega tooltip">
|
||||
<p>
|
||||
{% if user.rower.rowerplan == 'pro' %}
|
||||
<a class="button blue small" href="/rowers/workout/{{ workout.id }}/editintervals">Edit Intervals</a>
|
||||
{% else %}
|
||||
<a class="button blue small" href="/rowers/promembership">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>
|
||||
<div id="advancedplots" class="grid_6 omega">
|
||||
<div class="grid_6 alpha">
|
||||
|
||||
<script type="text/javascript" src="/static/js/bokeh-0.11.1.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>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,76 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}Analysis {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>Analysis</h1>
|
||||
<p>Functionality to analyze multiple workouts.</p>
|
||||
|
||||
|
||||
<div class="grid_12 alpha">
|
||||
<div class="grid_6 alpha">
|
||||
<h2>Basic</h2>
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
<a class="button blue small" href="/rowers/ote-bests">Ranking Pieces</a></p>
|
||||
<p>Analyze your Concept2 ranking pieces over the past 12 months and predict your pace on other pieces.</p>
|
||||
</div>
|
||||
<div class="grid_2">
|
||||
<p class="button white small">
|
||||
Analysis Feature 2
|
||||
</p>
|
||||
<p>
|
||||
Reserved for future functionality.
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2 omega">
|
||||
<p class="button white small">
|
||||
Analysis Feature 3
|
||||
</p>
|
||||
<p>
|
||||
Reserved for future functionality.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="grid_6 omega">
|
||||
<h2>Pro</h2>
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
{% if user.rower.rowerplan == 'pro' %}
|
||||
<a class="button blue small" href="/rowers/histo">Power Histogram</a>
|
||||
{% else %}
|
||||
<a class="button blue small" href="/rowers/about">Power Histogram</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
<p>
|
||||
Plot a power histogram of all your strokes over the past 12 months.
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2">
|
||||
<p class="button white small">
|
||||
Pro Feature 2
|
||||
</p>
|
||||
<p>
|
||||
Reserved for future functionality.
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2 omega">
|
||||
<p class="button white small">
|
||||
Pro Feature 3
|
||||
</p>
|
||||
<p>
|
||||
Reserved for future functionality.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,182 +0,0 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<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.rower.rowerplan == 'pro' %}
|
||||
<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> </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.rower.rowerplan == 'pro' %}
|
||||
<h6>Pro Member</h6>
|
||||
{% else %}
|
||||
<p> </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> </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> </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> </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> </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> </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_3 alpha">
|
||||
<p id="footer"><a href="/rowers/email/">© Sander Roosendaal</a></p>
|
||||
</div>
|
||||
<div class="grid_1 suffix_1">
|
||||
<p id="footer">
|
||||
<a href="/rowers/about">About</a></p>
|
||||
</div>
|
||||
<div class="grid_1 suffix_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_1 omega">
|
||||
<p id="footer">
|
||||
<a href="/rowers/email">Contact</a></p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
<!-- end container -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,60 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}View Workout {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<script type="text/javascript" src="/static/js/bokeh-0.11.1.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 }}/edit">Edit Workout</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2 suffix_2 omega">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/advanced">Advanced Edit</a>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{{ the_div|safe }}
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,48 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}Workouts{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Available on C2 Logbook</h1>
|
||||
{% if data %}
|
||||
<table width="70%" class="listtable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th> Distance </th>
|
||||
<th> Duration </th>
|
||||
<th> Date</th>
|
||||
<th> Type</th>
|
||||
<th> Import</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for workout in data %}
|
||||
<tr>
|
||||
{% for key,value in workout.items %}
|
||||
{% if key == "date" %}
|
||||
<td>{{ value }}</td>
|
||||
{% endif %}
|
||||
{% if key == "type" %}
|
||||
<td>{{ value }}</td>
|
||||
{% endif %}
|
||||
{% if key == "distance" %}
|
||||
<td>{{ value }}m</td>
|
||||
{% endif %}
|
||||
{% if key == "time_formatted" %}
|
||||
<td>{{ value }}</td>
|
||||
{% endif %}
|
||||
{% if key == "id" %}
|
||||
<td><a href="/rowers/workout/c2import/{{ value }}/">Import</a></td>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p> No workouts found </p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -1,39 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}Workouts{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Available on C2 Logbook</h1>
|
||||
{% if workouts %}
|
||||
<table width="70%" class="listtable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th> Import </th>
|
||||
<th> Date/Time </th>
|
||||
<th> Duration </th>
|
||||
<th> Total Distance</th>
|
||||
<th> Type</th>
|
||||
<th> Source</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for workout in workouts %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/rowers/workout/c2import/{{ workout|lookup:'id' }}/">Import</a></td>
|
||||
<td>{{ workout|lookup:'starttime' }}</td>
|
||||
<td>{{ workout|lookup:'duration' }}</td>
|
||||
<td>{{ workout|lookup:'distance' }}</td>
|
||||
<td>{{ workout|lookup:'rowtype' }}</td>
|
||||
<td>{{ workout|lookup:'source' }}</td>
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p> No workouts found </p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -1,20 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Upload CrewNerd Summary CSV{% endblock title %}
|
||||
{% block content %}
|
||||
<div id="emailform" class="grid_6 alpha">
|
||||
|
||||
{% if form.errors %}
|
||||
<p style="color: red;">
|
||||
Please correct the error{{ form.errors|pluralize }} below.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
|
||||
<form method="post" action="/rowers/workout/{{ workout.id }}/crewnerdsummary">{% csrf_token %}
|
||||
<table>
|
||||
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}Workouts{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="workouts" class="grid_4 alpha">
|
||||
|
||||
|
||||
<h1>Workout {{ id }}</h1>
|
||||
<table width=100%>
|
||||
<tr>
|
||||
<th>Rower:</th><td>{{ first_name }} {{ last_name }}</td>
|
||||
</tr><tr>
|
||||
<tr>
|
||||
<th>Name:</th><td>{{ workout.name }}</td>
|
||||
</tr><tr>
|
||||
<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><tr>
|
||||
<th>Type:</th><td>{{ workout.workouttype }}</td>
|
||||
</tr><tr>
|
||||
<th>Weight Category:</th><td>{{ workout.weightcategory }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="comparison" class="grid_8 omega">
|
||||
<h1>Compare this workout to:</h1>
|
||||
{% if workouts %}
|
||||
<table width="100%" class="listtable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th> Date</th>
|
||||
<th> Time</th>
|
||||
<th> Name</th>
|
||||
<th> Type</th>
|
||||
<th> Distance </th>
|
||||
<th> Duration </th>
|
||||
<th> Avg HR </th>
|
||||
<th> Max HR </th>
|
||||
<th> Compare</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</tbody>
|
||||
{% for cworkout in workouts %}
|
||||
{% if id != cworkout.id %}
|
||||
<tr>
|
||||
<td> {{ cworkout.date }} </td>
|
||||
<td> {{ cworkout.starttime }} </td>
|
||||
<td> <a href="/rowers/workout/{{ workout.id }}/edit">{{ cworkout.name }}</a> </td>
|
||||
<td> {{ cworkout.workouttype }} </td>
|
||||
<td> {{ cworkout.distance }}m</td>
|
||||
<td> {{ cworkout.duration |durationprint:"%H:%M:%S.%f" }} </td>
|
||||
<td> {{ cworkout.averagehr }} </td>
|
||||
<td> {{ cworkout.maxhr }} </td>
|
||||
<td> <a class="button blue small" href="/rowers/workout/compare/{{ id }}/{{ cworkout.id }}/time/hr">Compare</a> </td>
|
||||
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p> No workouts found </p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -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.11.1.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> </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 %}
|
||||
@@ -1,146 +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.11.1.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="other" class="grid_12 alpha">
|
||||
<div class="grid_2 alpha">
|
||||
<a class="button blue small"
|
||||
href="/rowers/workout/compare/{{ id2 }}/{{ id1 }}/{{ xparam }}/{{ yparam }}/{{ plottype }}">Swap Workouts</a>
|
||||
</div>
|
||||
<div class="grid_2">
|
||||
<a class="button blue small"
|
||||
href="/rowers/workout/{{ id1 }}/edit">Edit Workout</a>
|
||||
</div>
|
||||
<div class="grid_2 suffix_6 omega">
|
||||
<a class="button blue small"
|
||||
href="/rowers/workout/{{ id1 }}/advanced">Advanced Edit</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p> </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/compare/{{ id1 }}/{{ id2 }}/time/{{ yparam }}/{{ plottype }}">Time</a>
|
||||
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/distance/{{ yparam }}/{{ plottype }}">Distance</a>
|
||||
{% if promember %}
|
||||
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/power/{{ yparam }}/scatter">Power</a>
|
||||
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/hr/{{ yparam }}/scatter">HR</a>
|
||||
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/spm/{{ yparam }}/scatter">SPM</a>
|
||||
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/peakforce/{{ yparam }}/scatter">Peak Force</a>
|
||||
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/averageforce/{{ yparam }}/scatter">Average Force</a>
|
||||
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/drivelength/{{ yparam }}/scatter">Drive Length</a>
|
||||
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/driveenergy/{{ yparam }}/scatter">Drive Energy</a>
|
||||
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/drivespeed/{{ yparam }}/scatter">Drive Speed</a>
|
||||
{% else %}
|
||||
<a class="button rosy small" href="/rowers/promembership">Power (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">HR (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">SPM (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Peak Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Average Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Drive Length (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Drive Energy (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Drive Speed (Pro)</a>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid_2 suffix_2 omega dropdown">
|
||||
<button class="grid_2 alpha button blue small dropbtn">Y-axis</button>
|
||||
<div class="dropdown-content">
|
||||
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/pace/{{ plottype }}">Pace</a>
|
||||
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/hr/{{ plottype }}">HR</a>
|
||||
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/spm/{{ plottype }}">SPM</a>
|
||||
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/power/{{ plottype }}">Power</a>
|
||||
{% if promember %}
|
||||
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/peakforce/{{ plottype }}">Peak Force</a>
|
||||
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/averageforce/{{ plottype }}">Average Force</a>
|
||||
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/drivelength/{{ plottype }}">Drive Length</a>
|
||||
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/driveenergy/{{ plottype }}">Drive Energy</a>
|
||||
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/drivespeed/{{ plottype }}">Drive Speed</a>
|
||||
{% else %}
|
||||
<a class="button rosy small" href="/rowers/promembership">Peak Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Average Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Drive Length (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Drive Energy (Pro)</a>
|
||||
{% 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 %}
|
||||
@@ -1,55 +0,0 @@
|
||||
body {
|
||||
background: #123;
|
||||
color: #333;
|
||||
font-size: 11px;
|
||||
height: auto;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: Georgia, serif;
|
||||
font-weight: normal;
|
||||
padding-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h2 {
|
||||
padding-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
p {
|
||||
border: 1px solid #666;
|
||||
overflow: hidden;
|
||||
padding: 10px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.container_12,
|
||||
.container_16,
|
||||
.container_24 {
|
||||
background-color: #fff;
|
||||
background-repeat: repeat-y;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.container_12 {
|
||||
background-image: url(../img/12_col.gif);
|
||||
}
|
||||
|
||||
.container_16 {
|
||||
background-image: url(../img/16_col.gif);
|
||||
}
|
||||
|
||||
.container_24 {
|
||||
background-image: url(../img/24_col.gif);
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
body {
|
||||
background: #123;
|
||||
color: #333;
|
||||
font-size: 11px;
|
||||
height: auto;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: Georgia, serif;
|
||||
font-weight: normal;
|
||||
padding-top: 20px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
h2 {
|
||||
padding-top: 20px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
p {
|
||||
border: 1px solid #666;
|
||||
overflow: hidden;
|
||||
padding: 10px 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.container_12,
|
||||
.container_16,
|
||||
.container_24 {
|
||||
background-color: #fff;
|
||||
background-repeat: repeat-y;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.container_12 {
|
||||
background-image: url(../img/12_col.gif);
|
||||
}
|
||||
|
||||
.container_16 {
|
||||
background-image: url(../img/16_col.gif);
|
||||
}
|
||||
|
||||
.container_24 {
|
||||
background-image: url(../img/24_col.gif);
|
||||
}
|
||||
@@ -6,28 +6,17 @@
|
||||
|
||||
{% 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>
|
||||
|
||||
{{ 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;}
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}View Workout {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<script type="text/javascript" src="/static/js/bokeh-0.11.1.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="title" class="grid_12 alpha">
|
||||
<h1>Indoor Rower Stroke Analysis</h1>
|
||||
</div>
|
||||
|
||||
<div id="summary" class="grid_6 alpha">
|
||||
|
||||
|
||||
|
||||
<p>Summary for {{ theuser.first_name }} {{ theuser.last_name }}
|
||||
between {{ startdate|date }} and {{ enddate|date }}</p>
|
||||
|
||||
|
||||
<div id="plotbuttons" 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/flexall/time/{{ yparam1 }}/{{ yparam2 }}">Time</a>
|
||||
<a class="button blue small alpha" href="/rowers/flexall/distance/{{ yparam1 }}/{{ yparam2 }}">Distance</a>
|
||||
{% if promember %}
|
||||
<a class="button blue small alpha" href="/rowers/flexall/power/{{ yparam1 }}/{{ yparam2 }}">Power</a>
|
||||
<a class="button blue small alpha" href="/rowers/flexall/hr/{{ yparam1 }}/{{ yparam2 }}">HR</a>
|
||||
<a class="button blue small alpha" href="/rowers/flexall/spm/{{ yparam1 }}/{{ yparam2 }}">SPM</a>
|
||||
<a class="button blue small alpha" href="/rowers/flexall/peakforce/{{ yparam1 }}/{{ yparam2 }}">Peak Force</a>
|
||||
<a class="button blue small alpha" href="/rowers/flexall/averageforce/{{ yparam1 }}/{{ yparam2 }}">Average Force</a>
|
||||
<a class="button blue small alpha" href="/rowers/flexall/drivelength/{{ yparam1 }}/{{ yparam2 }}">Drive Length</a>
|
||||
<a class="button blue small alpha" href="/rowers/flexall/driveenergy/{{ yparam1 }}/{{ yparam2 }}">Drive Energy</a>
|
||||
<a class="button blue small alpha" href="/rowers/flexall/drivespeed/{{ yparam1 }}/{{ yparam2 }}">Drive Speed</a>
|
||||
{% else %}
|
||||
<a class="button rosy small" href="/rowers/promembership">Power (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">HR (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">SPM (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Peak Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Average Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Drive Length (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Drive Energy (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">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/flexall/{{ xparam }}/pace/{{ yparam2 }}">Pace</a>
|
||||
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/hr/{{ yparam2 }}">HR</a>
|
||||
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/spm/{{ yparam2 }}">SPM</a>
|
||||
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/power/{{ yparam2 }}">Power</a>
|
||||
{% if promember %}
|
||||
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/peakforce/{{ yparam2 }}">Peak Force</a>
|
||||
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/averageforce/{{ yparam2 }}">Average Force</a>
|
||||
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/drivelength/{{ yparam2 }}">Drive Length</a>
|
||||
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/driveenergy/{{ yparam2 }}">Drive Energy</a>
|
||||
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/drivespeed/{{ yparam2 }}">Drive Speed</a>
|
||||
{% else %}
|
||||
<a class="button rosy small" href="/rowers/promembership">Peak Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Average Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Drive Length (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Drive Energy (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">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/flexall/{{ xparam }}/{{ yparam1 }}/hr">HR</a>
|
||||
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/spm">SPM</a>
|
||||
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/power">Power</a>
|
||||
{% if promember %}
|
||||
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/peakforce">Peak Force</a>
|
||||
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/averageforce">Average Force</a>
|
||||
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/drivelength">Drive Length</a>
|
||||
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/driveenergy">Drive Energy</a>
|
||||
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/drivespeed">Drive Speed</a>
|
||||
{% else %}
|
||||
<a class="button rosy small" href="/rowers/promembership">Peak Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Average Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Drive Length (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Drive Energy (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Drive Speed (Pro)</a>
|
||||
{% endif %}
|
||||
|
||||
<a class="button blue small" href="/rowers/flexall/{{ xparam }}/{{ yparam1 }}/None">None</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="grid_6 alpha">
|
||||
<p> </p>
|
||||
<p>Warning: Large date ranges may take a long time to load. Huge date ranges may crash your browser.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="form" class="grid_6 omega">
|
||||
<p>Use this form to select a different date range:</p>
|
||||
<p>
|
||||
Select start and end date for a date range:
|
||||
<div class="grid_4 alpha">
|
||||
|
||||
<form enctype="multipart/form-data" action="" method="post">
|
||||
|
||||
<table>
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
</div>
|
||||
<div class="grid_2 omega">
|
||||
<input name='daterange' class="button green" type="submit" value="Submit"> </form>
|
||||
</div>
|
||||
<div class="grid_4 alpha">
|
||||
<form enctype="multipart/form-data" action="" method="post">
|
||||
Or use the last {{ deltaform }} days.
|
||||
</div>
|
||||
<div class="grid_2 omega">
|
||||
{% csrf_token %}
|
||||
<input name='datedelta' class="button green" type="submit" value="Submit">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div id="graph" class="grid_12 alpha">
|
||||
|
||||
{{ the_div|safe }}
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,114 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
|
||||
|
||||
{% block title %}Graphs{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="workouts" class="grid_6 alpha">
|
||||
<h1>Workouts</h1>
|
||||
{% if workouts %}
|
||||
<table class="listtable" width=100% >
|
||||
<thead>
|
||||
<tr>
|
||||
<th> Date</th>
|
||||
<th> Time</th>
|
||||
<th> Name</th>
|
||||
<th> Type</th>
|
||||
<th> Edit</th>
|
||||
<th> Delete</th>
|
||||
<th> C2 upload</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for workout in workouts %}
|
||||
<tr>
|
||||
<td> {{ workout.date }} </td>
|
||||
<td> {{ workout.starttime }} </td>
|
||||
<td><a href="/rowers/workout/{{workout.id}}"> {{ workout.name }}</a> </td>
|
||||
<td> {{ workout.workouttype }} </td>
|
||||
<td> <a href="/rowers/workout/{{ workout.id }}/edit">E</td>
|
||||
<td> <a href="/rowers/workout/{{ workout.id }}/deleteconfirm">D</td>
|
||||
{% if not workout.uploadedtoc2 %}
|
||||
<td> <a href="/rowers/workout/{{ workout.id }}/c2upload">C2</td>
|
||||
{% else %}
|
||||
<td> </td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
</table>
|
||||
</tbody>
|
||||
{% else %}
|
||||
<p>No workouts found</p>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
<div id="graphs" class="grid_6 omega">
|
||||
<h1>Recent Graphs</h1>
|
||||
{% if graphs1 %}
|
||||
|
||||
{% for graph in graphs1 %}
|
||||
{% if forloop.counter == 1 %}
|
||||
<div id="thumb-container" class="grid_2 alpha">
|
||||
<a href="/rowers/graph/{{ graph.id }}/">
|
||||
<img src="/{{ graph.filename }}"
|
||||
onerror="this.src='/static/img/waiting.png'"
|
||||
alt="{{ graph.filename }}" width="120" height="100"></a>
|
||||
</div>
|
||||
{% elif forloop.counter == 3 %}
|
||||
<div id="thumb-container" class="grid_2 omega">
|
||||
<a href="/rowers/graph/{{ graph.id }}/">
|
||||
<img src="/{{ graph.filename }}"
|
||||
onerror="this.src='/static/img/waiting.png'"
|
||||
alt="{{ graph.filename }}" width="120" height="100"></a>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<div id="thumb-container" class="grid_2">
|
||||
<a href="/rowers/graph/{{ graph.id }}/">
|
||||
<img src="/{{ graph.filename }}"
|
||||
onerror="this.src='/static/img/waiting.png'"
|
||||
alt="{{ graph.filename }}" width="120" height="100"></a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% for graph in graphs2 %}
|
||||
{% if forloop.counter == 1 %}
|
||||
<div id="thumb-container" class="grid_2 alpha">
|
||||
<a href="/rowers/graph/{{ graph.id }}/">
|
||||
<img src="/{{ graph.filename }}"
|
||||
onerror="this.src='/static/img/waiting.png'"
|
||||
alt="{{ graph.filename }}" width="120" height="100"></a>
|
||||
</div>
|
||||
{% elif forloop.counter == 3 %}
|
||||
<div id="thumb-container" class="grid_2 omega">
|
||||
<a href="/rowers/graph/{{ graph.id }}/">
|
||||
<img src="/{{ graph.filename }}"
|
||||
onerror="this.src='/static/img/waiting.png'"
|
||||
alt="{{ graph.filename }}" width="120" height="100"></a>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<div id="thumb-container" class="grid_2">
|
||||
<a href="/rowers/graph/{{ graph.id }}/">
|
||||
<img src="/{{ graph.filename }}"
|
||||
onerror="this.src='/static/img/waiting.png'"
|
||||
alt="{{ graph.filename }}" width="120" height="100"></a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
|
||||
|
||||
{% else %}
|
||||
<p> No graphs found </p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,50 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block title %}File loading{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
|
||||
<div id="left" class="grid_6 alpha">
|
||||
<h1>Upload Workout File</h1>
|
||||
{% if form.errors %}
|
||||
<p style="color: red;">
|
||||
Please correct the error{{ form.errors|pluralize }} below.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<table>
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
<div id="formbutton" class="grid_1 prefix_4 suffix_1">
|
||||
<input class="button green" type="submit" value="Submit">
|
||||
</div>
|
||||
</div>
|
||||
<div id="right" class="grid_6 omega">
|
||||
<h1>Optional extra actions</h1>
|
||||
<p>
|
||||
<table>
|
||||
{{ optionsform.as_table }}
|
||||
|
||||
</table>
|
||||
</p>
|
||||
<p>
|
||||
Valid file types are:
|
||||
<ul>
|
||||
<li>Painsled iOS Stroke Export (CSV)</li>
|
||||
<li>Painsled desktop version Stroke Export (CSV)</li>
|
||||
<li>A TCX file with location data (lat,long) - with or without Heart Rate value, for example from RiM or CrewNerd</li>
|
||||
<li>RowPro CSV export</li>
|
||||
<li>SpeedCoach GPS and SpeedCoach GPS 2 CSV export</li>
|
||||
<li>ErgData CSV export</li>
|
||||
<li>ErgStick CSV export</li>
|
||||
<li>A FIT file with location data (experimental)</li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -1,60 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Contact Us{% endblock title %}
|
||||
{% block content %}
|
||||
<div id="emailform" class="grid_6 alpha">
|
||||
|
||||
{% if form.errors %}
|
||||
<p style="color: red;">
|
||||
Please correct the error{{ form.errors|pluralize }} below.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
|
||||
<form method="post" action="/rowers/email/send/">{% csrf_token %}
|
||||
<table>
|
||||
<tr><td>
|
||||
<label class="label">Name <span class="required">*</span></label>
|
||||
<span class="span">
|
||||
</td><td>
|
||||
<input name= "firstname" class="inputtext" maxlength="255" size="12" />
|
||||
<label class="spanlabel">First</label>
|
||||
</td></tr>
|
||||
<tr><td>
|
||||
</span>
|
||||
<span class="span">
|
||||
</td><td>
|
||||
<input name= "lastname" class="inputtext" maxlength="255" size="18" />
|
||||
<label class="spanlabel">Last</label>
|
||||
</span>
|
||||
</td></tr>
|
||||
<tr><td>
|
||||
<label class="label">Email Address <span class="required">*</span></label>
|
||||
</td><td>
|
||||
<input name="email" class="inputtext" type="text" maxlength="255" size="35" />
|
||||
</td></tr>
|
||||
<tr><td>
|
||||
<label class="label">Subject <span class="required">*</span></label>
|
||||
</td><td>
|
||||
<input name="subject" class="inputtext" type="text" maxlength="255" size="45" />
|
||||
</td></tr>
|
||||
</table>
|
||||
<label class="label">You must answer <u>YES</u> to the question below to approve sending this email. <span class="required">*</span></label>
|
||||
<table>
|
||||
<tr><td>
|
||||
Do you want to send me an email?
|
||||
</td><td>
|
||||
<input name="botcheck" class="inputtext" type="text" maxlength="5" size="5" />
|
||||
</td></tr>
|
||||
<tr><td>
|
||||
<label class="label">Message <span class="required">*</span></label>
|
||||
</td><td>
|
||||
<textarea name="message" class="inputtextarea" rows="11" cols="45"></textarea>
|
||||
</td></tr>
|
||||
<tr><td>
|
||||
<input class="submitform" type="submit" name="submitform" value="Send Message" />
|
||||
</td></tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}Export {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="exportbuttons" class="grid_6 alpha">
|
||||
|
||||
|
||||
<h3>Export Workout</h3>
|
||||
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/edit">Edit Workout</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_2 suffix_2 omega">
|
||||
<p>
|
||||
<a class="button gray small" href="/rowers/workout/{{ workout.id }}/advanced">Advanced Edit</a>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="grid_6 alpha">
|
||||
<p>
|
||||
Click on the icon to upload this workout to your site of choice. A checkmark indicates that the workout has already been uploaded. If the button is grayed out, click it to authorize the connection to that site. Use TCX export to email a TCX file of your workout to yourself.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
{% if workout.uploadedtoc2 == 0 %}
|
||||
{% if user.rower.c2token == None or user.rower.c2token == '' %}
|
||||
<div class="grid_1 alpha">
|
||||
<a href="/rowers/me/c2authorize">
|
||||
<img src="/static/img/c2square_gray.png" alt="C2 icon" width="60" height="60"></a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="grid_1 alpha">
|
||||
<a href="/rowers/workout/{{ workout.id }}/c2uploadw"><img src="/static/img/c2square.jpg" alt="Concept2 icon" width="60" height="60"></a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="grid_1 alpha">
|
||||
<img src="/static/img/c2square_checked.png" alt="Concept2 icon" width="60" height="60"></a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if workout.uploadedtostrava == 0 %}
|
||||
{% if user.rower.stravatoken == None or user.rower.stravatoken == '' %}
|
||||
<div class="grid_1">
|
||||
<a href="/rowers/me/stravaauthorize">
|
||||
<img src="/static/img/stravasquare_gray.png" alt="Strava icon" width="60" height="60"></a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="grid_1">
|
||||
<a href="/rowers/workout/{{ workout.id }}/stravauploadw"><img src="/static/img/stravasquare.png" alt="Strava icon" width="60" height="60"></a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="grid_1">
|
||||
<img src="/static/img/stravasquare_checked.png" alt="Concept2 icon" width="60" height="60"></a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if workout.uploadedtosporttracks == 0 %}
|
||||
{% if user.rower.sporttrackstoken == None or user.rower.sporttrackstoken == '' %}
|
||||
<div class="grid_1">
|
||||
<a href="/rowers/me/sporttracksauthorize">
|
||||
<img src="/static/img/sporttrackssquare_gray.png" alt="SportTracks icon" width="60" height="60"></a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="grid_1">
|
||||
<a href="/rowers/workout/{{ workout.id }}/sporttracksuploadw">
|
||||
<img src="/static/img/sporttrackssquare.png" alt="SportTracks icon" width="60" height="60"></a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="grid_1">
|
||||
<img src="/static/img/sporttrackssquare_checked.png" alt="Concept2 icon" width="60" height="60"></a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="grid_1">
|
||||
<a href="/rowers/workout/{{ workout.id }}/emailtcx">
|
||||
<img src="/static/img/export.png" alt="TCX Export" width="60" height="60"></a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="grid_6 omega">
|
||||
<h3>Connect</h3>
|
||||
|
||||
<div class="grid_6">
|
||||
<p>Click one of the below logos to connect to the service of your choice.
|
||||
You only need to do this once. After that, the site will have access until you
|
||||
revoke the authorization for the "rowingdata" app.</p>
|
||||
|
||||
<div class="grid_2 alpha">
|
||||
<p><a href="/rowers/me/stravaauthorize/"><img src="/static/img/ConnectWithStrava.png" alt="connect with strava" width="120"></a></p>
|
||||
|
||||
</div>
|
||||
<div class="grid_2">
|
||||
<p><a href="/rowers/me/c2authorize/"><img src="/static/img/blueC2logo.png" alt="connect with Concept2" width="120"></a></p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="grid_2 omega">
|
||||
<p><a href="/rowers/me/sporttracksauthorize/"><img src="/static/img/sporttracks-button.png" alt="connect with SportTracks" width="120"></a></p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,95 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %} Flexible Plot {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<script type="text/javascript" src="/static/js/bokeh-0.11.1.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/{{ id }}/advanced">Advanced Edit</a>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
||||
<p> </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/{{ id }}/flexchart2/time/{{ yparam1 }}/{{ yparam2 }}">Time</a>
|
||||
</div>
|
||||
<div class="grid_2">
|
||||
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/distance/{{ yparam1 }}/{{ yparam2 }}">Distance</a>
|
||||
</div>
|
||||
<div class="grid_2 omega">
|
||||
<a class="button blue small"
|
||||
href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/{{ yparam2 }}/{{ yparam1 }}">Swap Y axes</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="y-axis" class="grid_6 omega">
|
||||
<div class="grid_1 prefix_2 alpha">
|
||||
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/{{ yparam1 }}/pace">Pace</a>
|
||||
</div>
|
||||
<div class="grid_1">
|
||||
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/{{ yparam1 }}/hr">HR</a>
|
||||
</div>
|
||||
<div class="grid_1">
|
||||
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/{{ yparam1 }}/spm">SPM</a>
|
||||
</div>
|
||||
<div class="grid_1 omega">
|
||||
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/{{ yparam1 }}/power">Power</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="theplot" class="grid_12 alpha">
|
||||
|
||||
|
||||
{{ the_div|safe }}
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -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> </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/promembership">Power (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">HR (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">SPM (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Peak Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Average Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Drive Length (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Work per Stroke (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">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/promembership">Peak Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Average Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Drive Length (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Work per Stroke (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">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/promembership">Peak Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Average Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Drive Length (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Work per Stroke (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">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 %}
|
||||
@@ -51,17 +51,17 @@
|
||||
<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 }}/flexchart2/time/{{ yparam1 }}/{{ yparam2 }}/{{ plottype }}">Time</a>
|
||||
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart2/distance/{{ yparam1 }}/{{ yparam2 }}/{{ plottype }}">Distance</a>
|
||||
<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 }}/flexchart2/power/{{ yparam1 }}/{{ yparam2 }}/scatter">Power</a>
|
||||
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart2/hr/{{ yparam1 }}/{{ yparam2 }}/scatter">HR</a>
|
||||
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart2/spm/{{ yparam1 }}/{{ yparam2 }}/scatter">SPM</a>
|
||||
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart2/peakforce/{{ yparam1 }}/{{ yparam2 }}/scatter">Peak Force</a>
|
||||
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart2/averageforce/{{ yparam1 }}/{{ yparam2 }}/scatter">Average Force</a>
|
||||
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart2/drivelength/{{ yparam1 }}/{{ yparam2 }}/scatter">Drive Length</a>
|
||||
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart2/driveenergy/{{ yparam1 }}/{{ yparam2 }}/scatter">Work per Stroke</a>
|
||||
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart2/drivespeed/{{ yparam1 }}/{{ yparam2 }}/scatter">Drive Speed</a>
|
||||
<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/promembership">Power (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">HR (Pro)</a>
|
||||
@@ -79,16 +79,16 @@
|
||||
<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 }}/flexchart2/{{ xparam }}/pace/{{ yparam2 }}/{{ plottype }}">Pace</a>
|
||||
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/hr/{{ yparam2 }}/{{ plottype }}">HR</a>
|
||||
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/spm/{{ yparam2 }}/{{ plottype }}">SPM</a>
|
||||
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/power/{{ yparam2 }}/{{ plottype }}">Power</a>
|
||||
<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 }}/flexchart2/{{ xparam }}/peakforce/{{ yparam2 }}/{{ plottype }}">Peak Force</a>
|
||||
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/averageforce/{{ yparam2 }}/{{ plottype }}">Average Force</a>
|
||||
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/drivelength/{{ yparam2 }}/{{ plottype }}">Drive Length</a>
|
||||
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/driveenergy/{{ yparam2 }}/{{ plottype }}">Work per Stroke</a>
|
||||
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/drivespeed/{{ yparam2 }}/{{ plottype }}">Drive Speed</a>
|
||||
<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/promembership">Peak Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Average Force (Pro)</a>
|
||||
@@ -103,15 +103,15 @@
|
||||
<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 }}/flexchart2/{{ xparam }}/{{ yparam1 }}/hr/{{ plottype }}">HR</a>
|
||||
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/{{ yparam1 }}/spm/{{ plottype }}">SPM</a>
|
||||
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/{{ yparam1 }}/power/{{ plottype }}">Power</a>
|
||||
<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 }}/flexchart2/{{ xparam }}/{{ yparam1 }}/peakforce/{{ plottype }}">Peak Force</a>
|
||||
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/{{ yparam1 }}/averageforce/{{ plottype }}">Average Force</a>
|
||||
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/{{ yparam1 }}/drivelength/{{ plottype }}">Drive Length</a>
|
||||
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/{{ yparam1 }}/driveenergy/{{ plottype }}">Work per Stroke</a>
|
||||
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/{{ yparam1 }}/drivespeed/{{ plottype }}">Drive Speed</a>
|
||||
<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/promembership">Peak Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Average Force (Pro)</a>
|
||||
@@ -120,7 +120,7 @@
|
||||
<a class="button rosy small" href="/rowers/promembership">Drive Speed (Pro)</a>
|
||||
{% endif %}
|
||||
|
||||
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart2/{{ xparam }}/{{ yparam1 }}/None/{{ plottype }}">None</a>
|
||||
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/None/{{ plottype }}">None</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -141,10 +141,10 @@
|
||||
<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 }}/flexchart2/{{ xparam }}/{{ yparam1 }}/{{ yparam2 }}/line">Line Plot</a>
|
||||
<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 }}/flexchart2/{{ xparam }}/{{ yparam1 }}/{{ yparam2 }}/scatter">Scatter Plot</a>
|
||||
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/{{ yparam2 }}/scatter">Scatter Plot</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
{% load tz %}
|
||||
|
||||
{% block title %} Flexible Plot {% endblock %}
|
||||
|
||||
{% localtime on %}
|
||||
{% 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> </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/promembership">Power (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">HR (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">SPM (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Peak Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Average Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Drive Length (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Work per Stroke (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">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/promembership">Peak Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Average Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Drive Length (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Work per Stroke (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">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/promembership">Peak Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Average Force (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Drive Length (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">Work per Stroke (Pro)</a>
|
||||
<a class="button rosy small" href="/rowers/promembership">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">
|
||||
|
||||
|
||||
{{ the_div|safe }}
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
{% endlocaltime %}
|
||||
@@ -1,14 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}Change Workout {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="grid_12">
|
||||
<p class="midden">
|
||||
<a title="By Photoglob Zurich, reprinted by Detroit Publishing Co. [Public domain], via Wikimedia Commons" href="https://commons.wikimedia.org/wiki/File%3AHenley_Regatta%2C_Henley-on-Thames%2C_England%2C_1890s.jpg"><img width="512" alt="Henley Regatta, Henley-on-Thames, England, 1890s" src="https://upload.wikimedia.org/wikipedia/commons/thumb/d/d9/Henley_Regatta%2C_Henley-on-Thames%2C_England%2C_1890s.jpg/512px-Henley_Regatta%2C_Henley-on-Thames%2C_England%2C_1890s.jpg"/></a></p>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,51 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}Delete Graph Image {% 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>Confirm Workout Delete</h1>
|
||||
This will delete the following workout and all linked graph images:
|
||||
|
||||
<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>
|
||||
</table>
|
||||
|
||||
<div class="grid_2 alpha">
|
||||
<p>
|
||||
<a class="button green small" href="/rowers/dashboard/">Cancel</a>
|
||||
</div>
|
||||
|
||||
<div class="grid_2">
|
||||
<p>
|
||||
<a class="button red small" href="/rowers/workout/{{ workout.id }}/delete">Delete</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="images" class="grid_6 omega">
|
||||
<p>
|
||||
|
||||
</b>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,85 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}View Workout {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<script type="text/javascript" src="/static/js/bokeh-0.11.1.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="title" class="grid_12 alpha">
|
||||
<h1>Indoor Rower Power Histogram</h1>
|
||||
</div>
|
||||
|
||||
<div id="summary" class="grid_6 alpha">
|
||||
|
||||
|
||||
|
||||
<p>Summary for {{ theuser.first_name }} {{ theuser.last_name }}
|
||||
between {{ startdate|date }} and {{ enddate|date }}</p>
|
||||
|
||||
<p>Direct link for other Pro users:
|
||||
<a href="/rowers/{{ id }}/histo/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}">http://rowsandall.com/rowers/{{ id }}/histo/{{ startdate|date:"Y-m-d" }}/{{ enddate|date:"Y-m-d" }}</a>
|
||||
</p>
|
||||
</div>
|
||||
<div id="form" class="grid_6 omega">
|
||||
<p>Use this form to select a different date range:</p>
|
||||
<p>
|
||||
Select start and end date for a date range:
|
||||
<div class="grid_4 alpha">
|
||||
|
||||
<form enctype="multipart/form-data" action="" method="post">
|
||||
|
||||
<table>
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
{% csrf_token %}
|
||||
</div>
|
||||
<div class="grid_2 omega">
|
||||
<input name='daterange' class="button green" type="submit" value="Submit"> </form>
|
||||
</div>
|
||||
<div class="grid_4 alpha">
|
||||
<form enctype="multipart/form-data" action="" method="post">
|
||||
Or use the last {{ deltaform }} days.
|
||||
</div>
|
||||
<div class="grid_2 omega">
|
||||
{% csrf_token %}
|
||||
<input name='datedelta' class="button green" type="submit" value="Submit">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="graph" class="grid_12 alpha">
|
||||
|
||||
{{ the_div|safe }}
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,53 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}View Workout {% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<script type="text/javascript" src="/static/js/bokeh-0.11.1.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>Indoor Rower Power Histogram</h1>
|
||||
|
||||
<p>Summary of the past 12 months for {{ theuser.first_name }} {{ theuser.last_name }}</p>
|
||||
|
||||
<p>Direct link for other Pro users:
|
||||
<a href="/rowers/{{ id }}/histo-all">http://rowsandall.com/rowers/{{ id }}/histo-all</a>
|
||||
</p>
|
||||
|
||||
|
||||
{{ the_div|safe }}
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,64 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Contact Us{% endblock title %}
|
||||
{% block content %}
|
||||
|
||||
<div class="grid_6 alpha">
|
||||
<h3>Import Workouts</h2>
|
||||
|
||||
<div class="grid_6">
|
||||
<div class="grid_3 alpha">
|
||||
<p>
|
||||
<a href="/rowers/workout/stravaimport"><img src="/static/img/stravalogo.png" alt="strava logo" width="140"></a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_3 omega">
|
||||
<p>Import workouts from Strava</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid_6">
|
||||
<div class="grid_3 alpha">
|
||||
<p>
|
||||
<a href="/rowers/workout/c2import"><img src="/static/img/blueC2logo.png" alt="Concept2 logo" width="140"></a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_3 omega">
|
||||
<p>Import workouts from the Concept2 logbook</p>
|
||||
</div>
|
||||
<div class="grid_3 alpha">
|
||||
<p>
|
||||
<a href="/rowers/workout/sporttracksimport"><img src="/static/img/sporttracks-button.png" alt="SportTracks logo" width="140"></a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid_3 omega">
|
||||
<p>Import workouts from SportTracks</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid_6 omega">
|
||||
<h3>Connect</h3>
|
||||
|
||||
<div class="grid_6">
|
||||
<p>Click one of the below logos to connect to the service of your choice.
|
||||
You only need to do this once. After that, the site will have access until you
|
||||
revoke the authorization for the "rowingdata" app.</p>
|
||||
|
||||
<div class="grid_2 alpha">
|
||||
<p><a href="/rowers/me/stravaauthorize/"><img src="/static/img/ConnectWithStrava.png" alt="connect with strava" width="120"></a></p>
|
||||
|
||||
</div>
|
||||
<div class="grid_2">
|
||||
<p><a href="/rowers/me/c2authorize/"><img src="/static/img/blueC2logo.png" alt="connect with Concept2" width="120"></a></p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="grid_2 omega">
|
||||
<p><a href="/rowers/me/sporttracksauthorize/"><img src="/static/img/sporttracks-button.png" alt="connect with SportTracks" width="120"></a></p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
graph = {{ my_data|safe }};
|
||||
|
||||
mpld3.draw_figure("fig01", graph);
|
||||
@@ -1,68 +0,0 @@
|
||||
|
||||
{% extends "base.html" %}
|
||||
{% block title %}About us{% endblock title %}
|
||||
{% block content %}
|
||||
|
||||
<div class="grid_6 alpha">
|
||||
<h2>Introduction</h2>
|
||||
<p>This is a solution for the self-tracking rowers.</p>
|
||||
<p>Some of us use Concept2 rowing machines. Some of us are On-The-Water
|
||||
rowers. All of us will use smartphone apps, smart watches, fitness (GPS)
|
||||
watches, etc. to track our activities.</p>
|
||||
<p>Most of them will cross-train. Bike. Run. Skate.</p>
|
||||
<p>That means, the Concept2 logbook is not a sufficient training logbook for us.</p>
|
||||
<p>At the same time, the Concept2 logbook is used in rankings, for challenges,
|
||||
and more. Many of us will want to log all our rowing on the Concept2 logbook.</p>
|
||||
<p>So there are a couple of challenges here:</p>
|
||||
<ul>
|
||||
<li><p>How do I get my erg rows on Strava/SportTracks/Garmin Connect?</p>
|
||||
<blockquote>
|
||||
<ul>
|
||||
<li>Use an ANT+ device, like explained here: <a href="https://dr3do.wordpress.com/2015/07/09/hurray/" rel="nofollow">https://dr3do.wordpress.com/2015/07/09/hurray/</a></li>
|
||||
<li>Import from RowPro to SportTracks</li>
|
||||
<li>There are many smartphone apps to capture data from the PM3/4/5 erg monitor. Not many of them export in a format that is suitable for upload to the above-mentioned sites.</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
</li>
|
||||
<li><p>How do I get all my rows (including OTW) into the Concept2 logbook</p>
|
||||
<blockquote>
|
||||
<ul>
|
||||
<li>For On-Water and Erg: Add them manually</li>
|
||||
<li>For erg: Upload from ErgData, RowPro, Concept2 utility</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
</li>
|
||||
</ul>
|
||||
<p>This project aims at giving you ways to:</p>
|
||||
<ul>
|
||||
<li><p>Upload fitness data captured in TCX format to the Concept2 logbook (implemented)</p>
|
||||
<blockquote>
|
||||
<ul>
|
||||
<li>This should cover all your On-Water activities, whether they are captured with a SpeedCoach, a GPS fitness watch, your phone, or any other device. As long as you are able to export a valid TCX file.</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
</li>
|
||||
<li><p>Get erg data captured with apps that have no <q>upload to Concept2</q> functionality and upload them to the Concept2 logbook (implemented)</p>
|
||||
<blockquote>
|
||||
<ul>
|
||||
<li>For example: painsled</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
<li><p>Create useful plots. Who wants to be limited to what the on-line logbooks plot. Get your data and create:</p>
|
||||
<blockquote>
|
||||
<ul>
|
||||
<li>Color HR band charts or Pie Charts (implemented)</li>
|
||||
<li>Plot drive length, drive time, and other erg related parameters as a function of time or distance (to be implemented)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="grid_6 omega">
|
||||
<h2>Credits</h2>
|
||||
<p>The project is based on python plotting code by
|
||||
Greg Smith (<a href="https://quantifiedrowing.wordpress.com/" rel="nofollow">https://quantifiedrowing.wordpress.com/</a>)
|
||||
and inspired by the RowPro Dan Burpee spreadsheet
|
||||
(<a href="http://www.sub7irc.com/RP_Split_Template.zip" rel="nofollow">http://www.sub7irc.com/RP_Split_Template.zip</a>).</p>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
@@ -1,54 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}Graphs{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Recent Graphs</h1>
|
||||
{% if graphs1 %}
|
||||
<div class="grid_1 alpha">
|
||||
<p> </p>
|
||||
</div>
|
||||
|
||||
{% for graph in graphs1 %}
|
||||
<div id="thumb-container" class="grid_2">
|
||||
<a href="/rowers/graph/{{ graph.id }}/">
|
||||
<img src="/{{ graph.filename }}"
|
||||
onerror="this.src='/static/img/waiting.png'"
|
||||
alt="{{ graph.filename }}" width="120" height="100"></a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<div class="grid_1 omega">
|
||||
<p> </p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="grid_12">
|
||||
<p> </p>
|
||||
</div>
|
||||
|
||||
<div class="grid_1 alpha">
|
||||
<p> </p>
|
||||
</div>
|
||||
|
||||
{% for graph in graphs2 %}
|
||||
<div id="thumb-container" class="grid_2">
|
||||
<a href="/rowers/graph/{{ graph.id }}/">
|
||||
<img src="/{{ graph.filename }}"
|
||||
onerror="this.src='/static/img/waiting.png'"
|
||||
alt="{{ graph.filename }}" width="120" height="100"></a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
<div class="grid_1 omega">
|
||||
<p> </p>
|
||||
</div>
|
||||
|
||||
|
||||
{% else %}
|
||||
<p> No graphs found </p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -1,52 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}Workouts{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>My Workouts</h1>
|
||||
{% if workouts %}
|
||||
<table width="70%" class="listtable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th> Date</th>
|
||||
<th> Time</th>
|
||||
<th> Name</th>
|
||||
<th> Type</th>
|
||||
<th> Distance </th>
|
||||
<th> Duration </th>
|
||||
<th> Avg HR </th>
|
||||
<th> Max HR </th>
|
||||
<th> Delete</th>
|
||||
<th> Export</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</tbody>
|
||||
{% for workout in workouts %}
|
||||
<tr>
|
||||
<td> {{ workout.date }} </td>
|
||||
<td> {{ workout.starttime }} </td>
|
||||
<td>
|
||||
{% if user.rower.rowerplan == 'pro' %}
|
||||
<a href="/rowers/workout/{{ workout.id }}/edit">{{ workout.name }}</a> </td>
|
||||
{% else %}
|
||||
<a href="/rowers/workout/{{ workout.id }}/edit">{{ workout.name }}</a> </td>
|
||||
{% endif %}
|
||||
<td> {{ workout.workouttype }} </td>
|
||||
<td> {{ workout.distance }}m</td>
|
||||
<td> {{ workout.duration |durationprint:"%H:%M:%S.%f" }} </td>
|
||||
<td> {{ workout.averagehr }} </td>
|
||||
<td> {{ workout.maxhr }} </td>
|
||||
<td> <a class="button red small" href="/rowers/workout/{{ workout.id }}/deleteconfirm">Delete</td>
|
||||
<td> <a class="button blue small" href="/rowers/workout/{{ workout.id }}/export">Export</a> </td>
|
||||
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p> No workouts found </p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user