Private
Public Access
1
0

Merge branch 'release/v9.36'

This commit is contained in:
Sander Roosendaal
2019-03-02 16:03:34 +01:00
5328 changed files with 255544 additions and 26642 deletions
-1
View File
@@ -27,7 +27,6 @@ conftest.py
# temporary test files # temporary test files
/rowers/tests/testdata/temp /rowers/tests/testdata/temp
/rowers/tests/testdata/testdata.csv.gz /rowers/tests/testdata/testdata.csv.gz
rowers/tests/testdata/testdata.csv.gz
/rowers/tests/testdata/testdata.tcx /rowers/tests/testdata/testdata.tcx
@@ -0,0 +1,6 @@
{
"cells": [],
"metadata": {},
"nbformat": 4,
"nbformat_minor": 2
}
-574
View File
@@ -1,574 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?><gpx xmlns="http://www.topografix.com/GPX/1/1" xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3" xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1" creator="Oregon 400t" version="1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd"><metadata><link href="http://www.garmin.com"><text>Garmin International</text></link><time>2016-05-20T15:41:26</time></metadata><trk><name>Export by rowingdata</name><trkseg> <trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:41:26+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:41:29.238150+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:41:32.148290+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:41:35.269000+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:41:38.152180+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:41:41.148270+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:41:44.148910+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:41:46.908250+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:41:49.819010+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:41:52.942510+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:41:55.639670+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:41:58.370000+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:42:01.188270+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:42:04.008300+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:42:06.888990+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:42:09.678900+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:42:12.469140+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:42:15.199010+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:42:17.963080+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:42:20.658340+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:42:23.538800+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:42:26.269790+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:42:28.848350+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:42:31.729550+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:42:34.398400+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:42:37.038360+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:42:39.499250+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:42:42.349070+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:42:45.079070+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:42:47.752890+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:42:50.452350+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:42:53.182630+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:42:55.789410+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:42:58.671890+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:43:01.338860+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:43:04.068490+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:43:06.862620+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:43:09.618500+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:43:12.379160+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:43:15.229200+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:43:17.963150+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:43:20.692490+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:43:23.628520+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:43:26.329210+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:43:29.148960+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:43:31.668570+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:43:34.490920+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:43:37.369250+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:43:40.189230+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:43:42.798860+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:43:45.708750+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:43:48.318590+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:43:51.199500+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:43:53.869290+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:43:56.572490+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:43:59.212410+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:44:01.912890+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:44:04.459350+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:44:07.249360+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:44:09.949930+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:44:12.619870+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:44:15.378800+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:44:18.049420+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:44:20.719440+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:44:23.298970+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:44:26.178820+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:44:28.669980+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:44:31.429270+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:44:34.042790+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:44:36.589070+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:44:39.412800+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:44:42.078870+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:44:44.783760+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:44:47.450710+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:44:50.149400+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:44:52.789720+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:44:55.429750+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:44:58.069700+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:45:00.742790+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:45:03.442700+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:45:06.139610+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:45:08.689490+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:45:11.479530+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:45:14.119610+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:45:16.792860+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:45:19.368950+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:45:22.158960+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:45:24.889580+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:45:27.558940+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:45:30.469760+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:45:33.259860+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:45:36.079590+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:45:38.899560+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:45:41.689980+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:45:44.568940+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:45:47.329670+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:45:50.149560+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:45:52.969660+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:45:55.879910+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:45:58.789690+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:46:01.729660+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:46:04.669610+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:46:07.549730+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:46:10.458930+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:46:13.488980+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:46:16.429320+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:46:19.519650+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:46:22.459630+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:46:25.338880+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:46:28.459530+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:46:31.401590+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:46:34.339560+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:46:37.309450+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:46:40.098920+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:46:43.039950+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:46:46.039490+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:46:48.979630+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:46:51.949590+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:46:54.709590+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:46:57.589710+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:47:00.503120+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:47:03.408950+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:47:06.323410+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:47:09.229670+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:47:12.198960+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:47:15.079930+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:47:17.989660+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:47:20.959680+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:47:23.869730+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:47:26.782970+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:47:29.688910+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:47:32.539570+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:47:35.449720+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:47:38.329080+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:47:41.148960+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:47:44.088880+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:47:47.150600+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:47:50.029750+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:47:52.998850+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:47:55.880360+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:47:58.789400+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:48:01.639760+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:48:04.492770+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:48:07.429530+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:48:10.373270+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:48:13.309500+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:48:16.279570+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:48:19.160740+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:48:21.948820+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:48:25.039520+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:48:27.949340+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:48:30.890880+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:48:33.648790+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:48:36.770050+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:48:39.499600+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:48:42.559140+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:48:45.439020+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:48:48.439810+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:48:51.379570+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:48:54.259600+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:48:57.139300+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:49:00.049550+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:49:02.838790+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:49:05.839540+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:49:08.749400+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:49:11.689540+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:49:14.538900+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:49:17.389440+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:49:20.058880+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:49:23.059530+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:49:25.880610+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:49:28.608730+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:49:31.582600+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:49:34.278700+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:49:37.068660+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:49:40.039460+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:49:42.889790+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:49:45.772580+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:49:48.708690+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:49:51.679450+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:49:54.499470+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:49:57.409440+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:50:00.439330+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:50:03.408680+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:50:06.378680+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:50:09.168860+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:50:12.229650+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:50:15.138650+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:50:18.049470+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:50:20.959460+00:00</time>
</trkpt>
<trkpt lat="0.0" lon="0.0">
<time>2016-05-20T13:50:23.242360+00:00</time>
</trkpt>
</trkseg></trk></gpx>
+69 -69
View File
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*- # -*- Coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import models from django.db import models
@@ -18,86 +18,86 @@ class BoatCategory(models.Model):
club = models.CharField(_("Club"),max_length=30) club = models.CharField(_("Club"),max_length=30)
class Meta: class Meta:
verbose_name = _("boat category") verbose_name = _("boat category")
verbose_name_plural = _("boat categories") verbose_name_plural = _("boat categories")
ordering = ['name'] ordering = ['name']
def __str__(self): def __str__(self):
str = self.name str = self.name
return str return str
def __unicode__(self): def __unicode__(self):
str = self.name str = self.name
return str return str
class Member(models.Model): class Member(models.Model):
statuses = ( statuses = (
("coach", _("coach")), ("coach", _("coach")),
("member", _("member")), ("member", _("member")),
("administrator", _("administrator")), ("administrator", _("administrator")),
) )
class Meta: class Meta:
verbose_name = _("member") verbose_name = _("member")
verbose_name_plural = _("members") verbose_name_plural = _("members")
ordering = ['user'] ordering = ['user']
user = models.OneToOneField(User) user = models.OneToOneField(User,on_delete=models.CASCADE)
contributions = models.BooleanField(default=True) contributions = models.BooleanField(default=True)
status = models.CharField(default="member", status = models.CharField(default="member",
choices=statuses, choices=statuses,
max_length=30) max_length=30)
categories = models.ManyToManyField(BoatCategory, categories = models.ManyToManyField(BoatCategory,
verbose_name=_("Boat Categories")) verbose_name=_("Boat Categories"))
club = models.CharField(_("Club"),max_length=30) club = models.CharField(_("Club"),max_length=30)
hoursworked = models.FloatField(_("Hours Worked")) hoursworked = models.FloatField(_("Hours Worked"))
def __str__(self): def __str__(self):
str = self.user.first_name+' '+self.user.last_name str = self.user.first_name+' '+self.user.last_name
return str return str
def __unicode__(self): def __unicode__(self):
str = self.user.first_name+' '+self.user.last_name str = self.user.first_name+' '+self.user.last_name
return str return str
class Boat(models.Model): class Boat(models.Model):
class Meta: class Meta:
verbose_name = _("boat") verbose_name = _("boat")
verbose_name_plural = _("boats") verbose_name_plural = _("boats")
ordering = ['boatcode'] ordering = ['boatcode']
boatname = models.CharField(_("Boat Name"),max_length=30) boatname = models.CharField(_("Boat Name"),max_length=30)
boatcode = models.CharField(_("Boat Code"),max_length=10) boatcode = models.CharField(_("Boat Code"),max_length=10)
categories = models.ManyToManyField(BoatCategory) categories = models.ManyToManyField(BoatCategory)
nrseats = models.IntegerField(_("Nr of Seats")) nrseats = models.IntegerField(_("Nr of Seats"))
statuses = ( statuses = (
("water",_("water")), ("water",_("water")),
("hangar",_("hangar")), ("hangar",_("hangar")),
("damaged",_("damaged")), ("damaged",_("damaged")),
("races",_("races")), ("races",_("races")),
) )
status = models.CharField(default="hangar", status = models.CharField(default="hangar",
choices=statuses, choices=statuses,
max_length=30) max_length=30)
comment = models.CharField(_("Comment"),blank=True,max_length=100) comment = models.CharField(_("Comment"),blank=True,max_length=100)
def __str__(self): def __str__(self):
str = self.boatcode+' '+self.boatname str = self.boatcode+' '+self.boatname
return str return str
def __unicode__(self): def __unicode__(self):
str = self.boatcode+' '+self.boatname str = self.boatcode+' '+self.boatname
return str return str
class MemberWork(models.Model): class MemberWork(models.Model):
class Meta: class Meta:
verbose_name = _("member work") verbose_name = _("member work")
verbose_name_plural = _("member work") verbose_name_plural = _("member work")
date = models.DateField(_("Date")) date = models.DateField(_("Date"))
hours = models.FloatField(_("Hours")) hours = models.FloatField(_("Hours"))
@@ -105,16 +105,16 @@ class MemberWork(models.Model):
comment = models.CharField(_("Comment"),blank=True,max_length=100) comment = models.CharField(_("Comment"),blank=True,max_length=100)
worker = models.ManyToManyField(Member,_("Worker")) worker = models.ManyToManyField(Member,_("Worker"))
statuses = ( statuses = (
("planned",_("planned")), ("planned",_("planned")),
("executed",_("executed")) ("executed",_("executed"))
) )
status = models.CharField(default="planned", status = models.CharField(default="planned",
choices=statuses, choices=statuses,
max_length=30) max_length=30)
class Races(models.Model): class Races(models.Model):
class Meta: class Meta:
verbose_name = _("race") verbose_name = _("race")
verbose_name_plural = _("races") verbose_name_plural = _("races")
name = models.CharField(max_length = 30) name = models.CharField(max_length = 30)
@@ -124,20 +124,20 @@ class Races(models.Model):
transportboats = models.ManyToManyField(Boat) transportboats = models.ManyToManyField(Boat)
def __str__(self): def __str__(self):
str = self.name str = self.name
str = str+' '+self.startdatetime.strftime('%Y-%m-%d %H:%M:%S') str = str+' '+self.startdatetime.strftime('%Y-%m-%d %H:%M:%S')
return str return str
def __unicode__(self): def __unicode__(self):
str = self.name str = self.name
str = str+' '+self.startdatetime.strftime('%Y-%m-%d %H:%M:%S') str = str+' '+self.startdatetime.strftime('%Y-%m-%d %H:%M:%S')
return str return str
class Outing(models.Model): class Outing(models.Model):
class Meta: class Meta:
verbose_name = _("outing") verbose_name = _("outing")
verbose_name_plural = _("outings") verbose_name_plural = _("outings")
starttime = models.DateTimeField(verbose_name = _("Start Time"),default=timezone.now) starttime = models.DateTimeField(verbose_name = _("Start Time"),default=timezone.now)
@@ -145,27 +145,27 @@ class Outing(models.Model):
distance = models.FloatField(_("Distance (km)"),default=12) distance = models.FloatField(_("Distance (km)"),default=12)
comment = models.CharField(_("Comment"),blank=True,max_length=100) comment = models.CharField(_("Comment"),blank=True,max_length=100)
statuses = ( statuses = (
("reservation",_("reservation")), ("reservation",_("reservation")),
("active", _("active")), ("active", _("active")),
("completed", _("completed")), ("completed", _("completed")),
("race", _("race")), ("race", _("race")),
) )
boat = models.ForeignKey(Boat) boat = models.ForeignKey(Boat,on_delete=models.CASCADE)
rower = models.ForeignKey(Member) rower = models.ForeignKey(Member,on_delete=models.CASCADE)
race = models.ManyToManyField(Races,blank=True) race = models.ManyToManyField(Races,blank=True)
otherrowers = models.ManyToManyField(Member,related_name="otherrowers") otherrowers = models.ManyToManyField(Member,related_name="otherrowers")
status = models.CharField(_("Status"),default="active", status = models.CharField(_("Status"),default="active",
choices=statuses, choices=statuses,
max_length=30) max_length=30)
def __str__(self): def __str__(self):
str = self.boat.boatcode str = self.boat.boatcode
str = str+' '+self.starttime.strftime('%Y-%m-%d %H:%M:%S') str = str+' '+self.starttime.strftime('%Y-%m-%d %H:%M:%S')
return str return str
def __unicode__(self): def __unicode__(self):
str = self.boat.boatcode str = self.boat.boatcode
str = str+' '+self.starttime.strftime('%Y-%m-%d %H:%M:%S') str = str+' '+self.starttime.strftime('%Y-%m-%d %H:%M:%S')
return str return str
+1 -1
View File
@@ -1,4 +1,4 @@
from celery import Celery,app from .celery import Celery,app
import os import os
import time import time
import gc import gc
+128 -185
View File
@@ -1,221 +1,164 @@
alabaster==0.7.7 amqp==2.4.1
amqp==1.4.9 arrow==0.13.1
anyjson==0.3.3
appdirs==1.4.3
argcomplete==1.0.0
arrow==0.12.1
asn1crypto==0.23.0
astropy==1.1.2
atomicwrites==1.3.0 atomicwrites==1.3.0
attrs==18.2.0 attrs==18.2.0
awscli==1.14.57 backcall==0.1.0
Babel==2.2.0 beautifulsoup4==4.7.1
backports-abc==0.4 billiard==3.5.0.5
backports.functools-lru-cache==1.5 bleach==3.1.0
backports.shutil-get-terminal-size==1.0.0 bokeh==1.0.4
backports.ssl-match-hostname==3.4.0.2 braintree==3.51.0
beautifulsoup4==4.6.0 celery==4.2.1
billiard==3.3.0.23 certifi==2018.11.29
bitarray==0.8.1
blaze==0.9.1
bleach==2.0.0
bokeh==0.12.3
boto==2.48.0
boto3==1.6.6
botocore==1.9.10
bottle==0.12.9
Bottleneck==1.0.0
braintree==3.50.0
cairocffi==0.7.2
celery==3.1.23
certifi==2017.11.5
cffi==1.11.2
chardet==3.0.4 chardet==3.0.4
chest==0.2.3 Click==7.0
click==6.6 colorama==0.4.1
cloudpickle==0.1.1 cookies==2.2.1
clyent==1.2.1 coreapi==2.3.3
colorama==0.3.7 coreschema==0.0.4
comtypes==1.1.7
configobj==5.0.6
coreapi==2.0.9
cryptography==2.1.3
cycler==0.10.0 cycler==0.10.0
Cython==0.23.4 dask==1.1.1
cytoolz==0.7.5 decorator==4.3.2
dask==0.8.1 defusedxml==0.5.0
datashape==0.5.1
decorator==4.0.9
dill==0.2.4
Django==1.9.5 Django==1.9.5
django-analytical==2.2.2 django-analytical==2.5.0
django-analytics==0.0.1
django-async-messages==0.3.1 django-async-messages==0.3.1
django-braces==1.8.1 django-braces==1.13.0
django-celery==3.1.17
django-classy-tags==0.8.0 django-classy-tags==0.8.0
django-cookie-law==1.0.12 django-cookie-law==2.0.1
django-cors-headers==1.1.0 django-cors-headers==2.4.0
django-cors-middleware==1.3.1 django-countries==5.3.3
django-countries==5.3.2
django-datetime-widget==0.9.3 django-datetime-widget==0.9.3
django-debug-toolbar==1.4 django-debug-toolbar==1.4
django-durationfield==0.5.2 django-extensions==2.1.6
django-extensions==1.7.5
django-filter==1.0.0
django-geckoboard==2.0.0
django-geojson==2.10.0
django-htmlmin==0.10.0 django-htmlmin==0.10.0
django-leaflet==0.22.0 django-leaflet==0.24.0
django-mailbox==4.6.0 django-mailbox==4.7.1
django-modelcluster==1.1
django-oauth-toolkit==0.10.0 django-oauth-toolkit==0.10.0
django-oauth2-provider==0.2.6.1 django-oauth2-provider==0.2.6.1
django-rest-swagger==2.1.0 django-rest-framework==0.1.0
django-rq==0.8.0 django-rest-swagger==2.2.0
django-rq-dashboard==0.3.0 django-rq==1.3.0
django-ses==0.8.5 django-rq-dashboard==0.3.3
django-social-share==1.1.2 django-shell-plus==1.1.7
django-suit==0.2.25 django-social-share==1.3.2
django-suit==0.2.26
django-suit-rq==1.0.1 django-suit-rq==1.0.1
django-taggit==0.18.1 django-tz-detect==0.2.9
django-translation-manager==0.3.4
django-treebeard==4.0
django-tz-detect==0.2.8
djangorestframework==3.5.3 djangorestframework==3.5.3
docopt==0.6.2 docopt==0.6.2
docutils==0.12 docutils==0.14
enum34==1.1.2 entrypoints==0.3
et-xmlfile==1.0.1 factory-boy==2.11.1
Factory==1.2 Faker==1.0.2
fastcache==1.0.2
filemagic==1.6
fitparse==1.0.1 fitparse==1.0.1
Flask==0.10.1 future==0.17.1
Flask-Cors==2.1.2 geocoder==1.38.1
Flask-Login==0.3.2 holoviews==1.11.2
Flask-OpenID==1.2.5
Flask-Uploads==0.2.0
flower==0.9.1
funcsigs==1.0.2
functools32==3.2.3.post2
future==0.16.0
futures==3.0.5
geocoder==1.37.0
geopy==1.11.0
gevent==1.1.0
greenlet==0.4.9
grin==1.2.1
gunicorn==19.4.5
HeapDict==1.0.0
honcho==0.6.6
html5lib==1.0.1 html5lib==1.0.1
httplib2==0.9.2 htmlmin==0.1.12
HTMLParser==0.0.2
httplib2==0.12.1
icalendar==4.0.3 icalendar==4.0.3
idna==2.6 idna==2.8
ipaddress==1.0.14 image==1.5.27
ipython==4.2.0 importlib-resources==1.0.2
ipython-genutils==0.1.0 ipykernel==5.1.0
ipython==7.3.0
ipython-genutils==0.2.0
ipywidgets==7.4.2
iso8601==0.1.12 iso8601==0.1.12
isodate==0.6.0 isodate==0.6.0
itsdangerous==0.24
itypes==1.1.0 itypes==1.1.0
Jinja2==2.8 jedi==0.13.3
jmespath==0.9.3 Jinja2==2.10
jsonschema==2.5.1 jsonschema==3.0.0
jupyter==1.0.0
jupyter-client==5.2.4
jupyter-console==6.0.0
jupyter-core==4.4.0
kiwisolver==1.0.1 kiwisolver==1.0.1
kombu==3.0.35 kombu==4.3.0
lxml==4.2.1 lxml==4.3.1
Markdown==2.6.7 Markdown==3.0.1
MarkupSafe==0.23 MarkupSafe==1.1.1
matplotlib==2.2.2 matplotlib==3.0.2
mechanize==0.3.6
MiniMockTest==0.5 MiniMockTest==0.5
mistune==0.8.4
mock==2.0.0 mock==2.0.0
more-itertools==5.0.0 more-itertools==5.0.0
mpld3==0.2 mpld3==0.3
mpmath==0.19 nbconvert==5.4.1
multipledispatch==0.4.8 nbformat==4.4.0
networkx==1.11
nose==1.3.7 nose==1.3.7
nose-parameterized==0.6.0 nose-parameterized==0.6.0
numpy==1.14.2 notebook==5.7.4
numpy==1.16.1
oauth2==1.9.0.post1 oauth2==1.9.0.post1
oauthlib==1.0.3 oauthlib==1.0.3
odo==0.4.2 openapi-codec==1.3.2
openapi-codec==1.1.7 packaging==19.0
packaging==16.8 pandas==0.24.1
pandas==0.22.0 pandocfilters==1.4.2
pathlib2==2.3.3 param==1.8.2
pathspec==0.5.5 parso==0.3.4
pathspec==0.5.9
pbr==5.1.2 pbr==5.1.2
pexpect==4.0.1 pexpect==4.6.0
pickleshare==0.7.2 pickleshare==0.7.5
Pillow==3.2.0 Pillow==5.4.1
pluggy==0.8.1 pluggy==0.9.0
polib==1.0.7 prometheus-client==0.6.0
psutil==4.1.0 prompt-toolkit==2.0.9
ptyprocess==0.5.1 ptyprocess==0.6.0
py==1.7.0 py==1.8.0
pyasn1==0.1.9 Pygments==2.3.1
pycparser==2.14 pyparsing==2.3.1
pycrypto==2.6.1 pyrsistent==0.14.11
PyMySQL==0.7.4 pytest==4.3.0
pync==1.6.1 pytest-django==3.4.7
pyparsing==2.2.0 pytest-runner==4.4
pytest==4.2.0
pytest-sugar==0.9.2 pytest-sugar==0.9.2
python-dateutil==2.7.2 python-dateutil==2.8.0
python-memcached==1.58 python-twitter==3.5
python-metar==1.4.0 pytz==2018.9
python-openid==2.2.5 pyviz-comms==0.7.0
python-twitter==3.2 PyYAML==3.13
pytz==2018.4 pyzmq==18.0.0
PyYAML==3.12 qtconsole==4.4.3
pyzmq==15.2.0
ratelim==0.1.6 ratelim==0.1.6
redis==2.10.5 redis==3.2.0
req==1.0.0 requests==2.21.0
requests==2.12.3 requests-oauthlib==1.2.0
requests-oauthlib==0.7.0 rowingdata==2.0.6
rowingdata==2.0.0 rowingphysics==0.5.0
rowingphysics==0.3.10 rq==0.13.0
rq==0.6.0 scipy==1.2.1
rq-dashboard==0.3.7 Send2Trash==1.5.0
rsa==3.4.2 shell==1.0.1
s3transfer==0.1.13 shortuuid==0.5.0
scandir==1.6 simplejson==3.16.0
scikit-image==0.12.3 six==1.12.0
scikit-learn==0.17.1 soupsieve==1.8
scipy==1.0.1 SQLAlchemy==1.2.18
seaborn==0.7.0 sqlparse==0.2.4
Shapely==1.6.1 stravalib==0.10.2
shortuuid==0.4.3
simplegeneric==0.8.1
simplejson==3.10.0
singledispatch==3.4.0.3
six==1.11.0
SQLAlchemy==1.0.12
sqlparse==0.1.19
-e git+git@bitbucket.org:sanderroosendaal/rowsandall.git@c9695a9f8f5ee0e459de066007a5ef4cef58255b#egg=stravalib
subprocess32==3.2.7
sympy==1.0
termcolor==1.1.0 termcolor==1.1.0
timestring==1.6.2 terminado==0.8.1
timezonefinder==2.1.2 testpath==0.4.2
toolz==0.7.4 text-unidecode==1.2
tornado==4.4.2 timezonefinder==3.4.2
tqdm==4.23.0 tornado==5.1.1
traitlets==4.2.1 tqdm==4.31.1
tzwhere==3.0.3 traitlets==4.3.2
Unidecode==0.4.19 units==0.7
units==0.6
uritemplate==3.0.0 uritemplate==3.0.0
urllib3==1.22 urllib3==1.24.1
VerbalExpressions==0.0.1 VerbalExpressions==0.0.2
vine==1.2.0
wcwidth==0.1.7
webencodings==0.5.1 webencodings==0.5.1
Werkzeug==0.11.8 widgetsnbextension==3.4.2
xlrd==1.0.0 xmltodict==0.12.0
xmltodict==0.11.0
yamjam==0.1.7 yamjam==0.1.7
yamllint==1.8.1 yamllint==1.15.0
+142
View File
@@ -0,0 +1,142 @@
amqp==2.4.1
arrow==0.13.1
atomicwrites==1.3.0
attrs==18.2.0
backcall==0.1.0
beautifulsoup4==4.7.1
billiard==3.5.0.5
bleach==3.1.0
bokeh==1.0.4
braintree==3.51.0
celery==4.2.1
certifi==2018.11.29
chardet==3.0.4
Click==7.0
colorama==0.4.1
cookies==2.2.1
coreapi==2.3.3
coreschema==0.0.4
cycler==0.10.0
dask==1.1.1
decorator==4.3.2
Django==1.9.5
django-analytical==2.5.0
django-async-messages==0.3.1
django-braces==1.13.0
django-classy-tags==0.8.0
django-cookie-law==2.0.1
django-cors-headers==2.4.0
django-countries==5.3.3
django-datetime-widget==0.9.3
django-debug-toolbar==1.4
django-extensions==2.1.6
django-htmlmin==0.10.0
django-leaflet==0.24.0
django-mailbox==4.7.1
django-oauth-toolkit==0.10.0
django-oauth2-provider==0.2.6.1
django-rest-framework==0.1.0
django-rest-swagger==2.2.0
django-rq==1.3.0
django-rq-dashboard==0.3.3
django-shell-plus==1.1.7
django-social-share==1.3.2
django-suit==0.2.26
django-suit-rq==1.0.1
django-tz-detect==0.2.9
djangorestframework==3.5.3
docopt==0.6.2
docutils==0.14
factory-boy==2.11.1
Faker==1.0.2
fitparse==1.0.1
future==0.17.1
geocoder==1.38.1
holoviews==1.11.2
html5lib==1.0.1
htmlmin==0.1.12
HTMLParser==0.0.2
httplib2==0.12.1
icalendar==4.0.3
idna==2.8
image==1.5.27
importlib-resources==1.0.2
ipython==7.3.0
ipython-genutils==0.2.0
iso8601==0.1.12
isodate==0.6.0
itypes==1.1.0
jedi==0.13.3
Jinja2==2.10
kiwisolver==1.0.1
kombu==4.3.0
lxml==4.3.1
Markdown==3.0.1
MarkupSafe==1.1.1
matplotlib==3.0.2
MiniMockTest==0.5
mock==2.0.0
more-itertools==5.0.0
mpld3==0.3
nose==1.3.7
nose-parameterized==0.6.0
numpy==1.16.1
oauth2==1.9.0.post1
oauthlib==1.0.3
openapi-codec==1.3.2
packaging==19.0
pandas==0.24.1
param==1.8.2
parso==0.3.4
pathspec==0.5.9
pbr==5.1.2
pexpect==4.6.0
pickleshare==0.7.5
Pillow==5.4.1
pluggy==0.9.0
prompt-toolkit==2.0.9
ptyprocess==0.6.0
py==1.8.0
Pygments==2.3.1
pyparsing==2.3.1
pytest==4.3.0
pytest-django==3.4.7
pytest-runner==4.4
pytest-sugar==0.9.2
python-dateutil==2.8.0
python-twitter==3.5
pytz==2018.9
pyviz-comms==0.7.0
PyYAML==3.13
ratelim==0.1.6
redis==3.2.0
requests==2.21.0
requests-oauthlib==1.2.0
rowingdata==2.0.6
rowingphysics==0.5.0
rq==0.13.0
scipy==1.2.1
shell==1.0.1
shortuuid==0.5.0
simplejson==3.16.0
six==1.12.0
soupsieve==1.8
SQLAlchemy==1.2.18
sqlparse==0.2.4
stravalib==0.10.2
termcolor==1.1.0
text-unidecode==1.2
timezonefinder==3.4.2
tornado==5.1.1
tqdm==4.31.1
traitlets==4.3.2
units==0.7
uritemplate==3.0.0
urllib3==1.24.1
VerbalExpressions==0.0.2
vine==1.2.0
wcwidth==0.1.7
webencodings==0.5.1
xmltodict==0.12.0
yamjam==0.1.7
yamllint==1.15.0
+6 -2
View File
@@ -1,5 +1,9 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import division
from .tasks import app as celery_app from __future__ import print_function
from __future__ import unicode_literals
from rowers.celery import app as celery_app
__all__ = ('celery_app',)
+4
View File
@@ -1,3 +1,7 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from django.contrib import admin from django.contrib import admin
from django.contrib.auth.admin import UserAdmin from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User from django.contrib.auth.models import User
+3
View File
@@ -1,3 +1,6 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals from __future__ import unicode_literals
from django.apps import AppConfig from django.apps import AppConfig
+5
View File
@@ -1,3 +1,8 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import braintree import braintree
from django.utils import timezone from django.utils import timezone
import datetime import datetime
+173 -156
View File
@@ -1,3 +1,9 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
# The interactions with the Concept2 logbook API # The interactions with the Concept2 logbook API
# All C2 related functions should be defined here # All C2 related functions should be defined here
# (There is still some stuff defined directly in views.py. Need to # (There is still some stuff defined directly in views.py. Need to
@@ -6,10 +12,12 @@
from rowers.imports import * from rowers.imports import *
import datetime import datetime
from requests import Request, Session from requests import Request, Session
import mytypes import rowers.mytypes as mytypes
from rowers.mytypes import otwtypes from rowers.mytypes import otwtypes
from iso8601 import ParseError from iso8601 import ParseError
import numpy
from rowsandall_app.settings import ( from rowsandall_app.settings import (
C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET
) )
@@ -19,7 +27,7 @@ import django_rq
queue = django_rq.get_queue('default') queue = django_rq.get_queue('default')
queuelow = django_rq.get_queue('low') queuelow = django_rq.get_queue('low')
queuehigh = django_rq.get_queue('low') queuehigh = django_rq.get_queue('low')
from utils import myqueue from rowers.utils import myqueue
oauth_data = { oauth_data = {
'client_id': C2_CLIENT_ID, 'client_id': C2_CLIENT_ID,
@@ -41,19 +49,19 @@ oauth_data = {
def c2_open(user): def c2_open(user):
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
if (r.c2token == '') or (r.c2token is None): if (r.c2token == '') or (r.c2token is None):
s = "Token doesn't exist. Need to authorize" s = "Token doesn't exist. Need to authorize"
raise NoTokenError("User has no token") raise NoTokenError("User has no token")
else: else:
if (timezone.now()>r.tokenexpirydate): if (timezone.now()>r.tokenexpirydate):
res = rower_c2_token_refresh(user) res = rower_c2_token_refresh(user)
if res == None: if res == None:
raise NoTokenError("User has no token") raise NoTokenError("User has no token")
if res[0] != None: if res[0] != None:
thetoken = res[0] thetoken = res[0]
else: else:
raise NoTokenError("User has no token") raise NoTokenError("User has no token")
else: else:
thetoken = r.c2token thetoken = r.c2token
return thetoken return thetoken
@@ -61,11 +69,11 @@ def add_stroke_data(user,c2id,workoutid,startdatetime,csvfilename,
workouttype='rower'): workouttype='rower'):
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
if (r.c2token == '') or (r.c2token is None): if (r.c2token == '') or (r.c2token is None):
return custom_exception_handler(401,s) return custom_exception_handler(401,s)
s = "Token doesn't exist. Need to authorize" s = "Token doesn't exist. Need to authorize"
elif (timezone.now()>r.tokenexpirydate): elif (timezone.now()>r.tokenexpirydate):
s = "Token expired. Needs to refresh." s = "Token expired. Needs to refresh."
return custom_exception_handler(401,s) return custom_exception_handler(401,s)
else: else:
starttimeunix = arrow.get(startdatetime).timestamp starttimeunix = arrow.get(startdatetime).timestamp
@@ -179,9 +187,9 @@ def makeseconds(t):
# convert our weight class code to Concept2 weight class code # convert our weight class code to Concept2 weight class code
def c2wc(weightclass): def c2wc(weightclass):
if (weightclass=="lwt"): if (weightclass=="lwt"):
res = "L" res = "L"
else: else:
res = "H" res = "H"
return res return res
@@ -343,17 +351,17 @@ def createc2workoutdata_as_splits(w):
hr = df[' HRCur (bpm)'].astype(int) hr = df[' HRCur (bpm)'].astype(int)
split_data = [] split_data = []
for i in range(len(t)): for i in range(len(t)):
thisrecord = {"time":t[i],"distance":d[i],"stroke_rate":spm[i], thisrecord = {"time":t[i],"distance":d[i],"stroke_rate":spm[i],
"heart_rate":{ "heart_rate":{
"average:":hr[i] "average:":hr[i]
} }
} }
split_data.append(thisrecord) split_data.append(thisrecord)
try: try:
durationstr = datetime.datetime.strptime(str(w.duration),"%H:%M:%S.%f") durationstr = datetime.datetime.strptime(str(w.duration),"%H:%M:%S.%f")
except ValueError: except ValueError:
durationstr = datetime.datetime.strptime(str(w.duration),"%H:%M:%S") durationstr = datetime.datetime.strptime(str(w.duration),"%H:%M:%S")
try: try:
newnotes = w.notes+'\n from '+w.workoutsource+' via rowsandall.com' newnotes = w.notes+'\n from '+w.workoutsource+' via rowsandall.com'
@@ -365,19 +373,19 @@ def createc2workoutdata_as_splits(w):
wtype = 'water' wtype = 'water'
data = { data = {
"type": wtype, "type": wtype,
"date": w.startdatetime.isoformat(), "date": w.startdatetime.isoformat(),
"distance": int(w.distance), "distance": int(w.distance),
"time": int(10*makeseconds(durationstr)), "time": int(10*makeseconds(durationstr)),
"timezone": w.timezone, "timezone": w.timezone,
"weight_class": c2wc(w.weightcategory), "weight_class": c2wc(w.weightcategory),
"comments": newnotes, "comments": newnotes,
"heart_rate": { "heart_rate": {
"average": averagehr, "average": averagehr,
"max": maxhr, "max": maxhr,
}, },
"splits": split_data, "splits": split_data,
} }
return data return data
@@ -418,13 +426,17 @@ def createc2workoutdata(w):
hr = 0*d hr = 0*d
stroke_data = [] stroke_data = []
for i in range(len(t)): for i in range(len(t)):
thisrecord = {"t":t[i],"d":d[i],"p":p[i],"spm":spm[i],"hr":hr[i]} thisrecord = {"t":t[i].astype(int),
stroke_data.append(thisrecord) "d":d[i].astype(int),
"p":p[i].astype(int),
"spm":spm[i].astype(int),
"hr":hr[i].astype(int)}
stroke_data.append(thisrecord)
try: try:
durationstr = datetime.datetime.strptime(str(w.duration),"%H:%M:%S.%f") durationstr = datetime.datetime.strptime(str(w.duration),"%H:%M:%S.%f")
except ValueError: except ValueError:
durationstr = datetime.datetime.strptime(str(w.duration),"%H:%M:%S") durationstr = datetime.datetime.strptime(str(w.duration),"%H:%M:%S")
workouttype = w.workouttype workouttype = w.workouttype
if workouttype in otwtypes: if workouttype in otwtypes:
@@ -436,19 +448,19 @@ def createc2workoutdata(w):
startdate = datetime.datetime.combine(w.date,datetime.time()) startdate = datetime.datetime.combine(w.date,datetime.time())
data = { data = {
"type": mytypes.c2mapping[workouttype], "type": mytypes.c2mapping[workouttype],
"date": w.startdatetime.isoformat(), "date": w.startdatetime.isoformat(),
"timezone": w.timezone, "timezone": w.timezone,
"distance": int(w.distance), "distance": int(w.distance),
"time": int(10*makeseconds(durationstr)), "time": int(10*makeseconds(durationstr)),
"weight_class": c2wc(w.weightcategory), "weight_class": c2wc(w.weightcategory),
"comments": w.notes, "comments": w.notes,
"heart_rate": { "heart_rate": {
"average": averagehr, "average": averagehr,
"max": maxhr, "max": maxhr,
}, },
"stroke_data": stroke_data, "stroke_data": stroke_data,
} }
return data return data
@@ -458,10 +470,10 @@ def do_refresh_token(refreshtoken):
scope = "results:write,user:read" scope = "results:write,user:read"
client_auth = requests.auth.HTTPBasicAuth(C2_CLIENT_ID, C2_CLIENT_SECRET) client_auth = requests.auth.HTTPBasicAuth(C2_CLIENT_ID, C2_CLIENT_SECRET)
post_data = {"grant_type": "refresh_token", post_data = {"grant_type": "refresh_token",
"client_secret": C2_CLIENT_SECRET, "client_secret": C2_CLIENT_SECRET,
"client_id":C2_CLIENT_ID, "client_id":C2_CLIENT_ID,
"refresh_token": refreshtoken, "refresh_token": refreshtoken,
} }
headers = {'user-agent': 'sanderroosendaal'} headers = {'user-agent': 'sanderroosendaal'}
url = "https://log.concept2.com/oauth/access_token" url = "https://log.concept2.com/oauth/access_token"
s = Session() s = Session()
@@ -498,9 +510,9 @@ def get_token(code):
post_data = {"grant_type": "authorization_code", post_data = {"grant_type": "authorization_code",
"code": code, "code": code,
"redirect_uri": C2_REDIRECT_URI, "redirect_uri": C2_REDIRECT_URI,
"client_secret": C2_CLIENT_SECRET, "client_secret": C2_CLIENT_SECRET,
"client_id":C2_CLIENT_ID, "client_id":C2_CLIENT_ID,
} }
headers = {'user-agent': 'sanderroosendaal'} headers = {'user-agent': 'sanderroosendaal'}
url = "https://log.concept2.com/oauth/access_token" url = "https://log.concept2.com/oauth/access_token"
s = Session() s = Session()
@@ -545,7 +557,7 @@ def make_authorization_url(request):
params = {"client_id": C2_CLIENT_ID, params = {"client_id": C2_CLIENT_ID,
"response_type": "code", "response_type": "code",
"redirect_uri": C2_REDIRECT_URI} "redirect_uri": C2_REDIRECT_URI}
url = "https://log.concept2.com/oauth/authorize?"+ urllib.urlencode(params) url = "https://log.concept2.com/oauth/authorize?"+ urllib.parse.urlencode(params)
url += "&scope="+scope url += "&scope="+scope
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
@@ -554,19 +566,19 @@ def make_authorization_url(request):
def get_workout(user,c2id): def get_workout(user,c2id):
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
if (r.c2token == '') or (r.c2token is None): if (r.c2token == '') or (r.c2token is None):
s = "Token doesn't exist. Need to authorize" s = "Token doesn't exist. Need to authorize"
return custom_exception_handler(401,s) ,0 return custom_exception_handler(401,s) ,0
elif (timezone.now()>r.tokenexpirydate): elif (timezone.now()>r.tokenexpirydate):
s = "Token expired. Needs to refresh." s = "Token expired. Needs to refresh."
return custom_exception_handler(401,s),0 return custom_exception_handler(401,s),0
else: else:
# ready to fetch. Hurray # ready to fetch. Hurray
authorizationstring = str('Bearer ' + r.c2token) authorizationstring = str('Bearer ' + r.c2token)
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'} 'Content-Type': 'application/json'}
url = "https://log.concept2.com/api/users/me/results/"+str(c2id) url = "https://log.concept2.com/api/users/me/results/"+str(c2id)
s = requests.get(url,headers=headers) s = requests.get(url,headers=headers)
data = s.json()['data'] data = s.json()['data']
@@ -583,9 +595,9 @@ def get_workout(user,c2id):
# Check if workout has stroke data, and get the stroke data # Check if workout has stroke data, and get the stroke data
if data['stroke_data']: if data['stroke_data']:
res2 = get_c2_workout_strokes(user,c2id) res2 = get_c2_workout_strokes(user,c2id)
if res2.status_code == 200: if res2.status_code == 200:
strokedata = pd.DataFrame.from_dict(res2.json()['data']) strokedata = pd.DataFrame.from_dict(res2.json()['data'])
else: else:
strokedata = pd.DataFrame() strokedata = pd.DataFrame()
else: else:
@@ -597,19 +609,19 @@ def get_workout(user,c2id):
def get_c2_workout_strokes(user,c2id): def get_c2_workout_strokes(user,c2id):
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
if (r.c2token == '') or (r.c2token is None): if (r.c2token == '') or (r.c2token is None):
return custom_exception_handler(401,s) return custom_exception_handler(401,s)
s = "Token doesn't exist. Need to authorize" s = "Token doesn't exist. Need to authorize"
elif (timezone.now()>r.tokenexpirydate): elif (timezone.now()>r.tokenexpirydate):
s = "Token expired. Needs to refresh." s = "Token expired. Needs to refresh."
return custom_exception_handler(401,s) return custom_exception_handler(401,s)
else: else:
# ready to fetch. Hurray # ready to fetch. Hurray
authorizationstring = str('Bearer ' + r.c2token) authorizationstring = str('Bearer ' + r.c2token)
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'} 'Content-Type': 'application/json'}
url = "https://log.concept2.com/api/users/me/results/"+str(c2id)+"/strokes" url = "https://log.concept2.com/api/users/me/results/"+str(c2id)+"/strokes"
s = requests.get(url,headers=headers) s = requests.get(url,headers=headers)
return s return s
@@ -618,21 +630,21 @@ def get_c2_workout_strokes(user,c2id):
def get_c2_workout_list(user,page=1): def get_c2_workout_list(user,page=1):
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
if (r.c2token == '') or (r.c2token is None): if (r.c2token == '') or (r.c2token is None):
s = "Token doesn't exist. Need to authorize" s = "Token doesn't exist. Need to authorize"
return custom_exception_handler(401,s) return custom_exception_handler(401,s)
elif (timezone.now()>r.tokenexpirydate): elif (timezone.now()>r.tokenexpirydate):
s = "Token expired. Needs to refresh." s = "Token expired. Needs to refresh."
return custom_exception_handler(401,s) return custom_exception_handler(401,s)
else: else:
# ready to fetch. Hurray # ready to fetch. Hurray
authorizationstring = str('Bearer ' + r.c2token) authorizationstring = str('Bearer ' + r.c2token)
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'} 'Content-Type': 'application/json'}
url = "https://log.concept2.com/api/users/me/results" url = "https://log.concept2.com/api/users/me/results"
url += "?page={page}".format(page=page) url += "?page={page}".format(page=page)
s = requests.get(url,headers=headers) s = requests.get(url,headers=headers)
return s return s
@@ -642,8 +654,8 @@ def get_c2_workout_list(user,page=1):
def get_username(access_token): def get_username(access_token):
authorizationstring = str('Bearer ' + access_token) authorizationstring = str('Bearer ' + access_token)
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'} 'Content-Type': 'application/json'}
import urllib import urllib
url = "https://log.concept2.com/api/users/me" url = "https://log.concept2.com/api/users/me"
response = requests.get(url,headers=headers) response = requests.get(url,headers=headers)
@@ -664,8 +676,8 @@ def get_username(access_token):
def get_userid(access_token): def get_userid(access_token):
authorizationstring = str('Bearer ' + access_token) authorizationstring = str('Bearer ' + access_token)
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'} 'Content-Type': 'application/json'}
import urllib import urllib
url = "https://log.concept2.com/api/users/me" url = "https://log.concept2.com/api/users/me"
response = requests.get(url,headers=headers) response = requests.get(url,headers=headers)
@@ -691,6 +703,10 @@ def process_callback(request):
return HttpResponse("got a user name: %s" % username) return HttpResponse("got a user name: %s" % username)
def default(o):
if isinstance(o, numpy.int64): return int(o)
raise TypeError
# Uploading workout # Uploading workout
def workout_c2_upload(user,w): def workout_c2_upload(user,w):
message = 'trying C2 upload' message = 'trying C2 upload'
@@ -706,40 +722,41 @@ def workout_c2_upload(user,w):
# ready to upload. Hurray # ready to upload. Hurray
if (checkworkoutuser(user,w)): if (checkworkoutuser(user,w)):
c2userid = get_userid(r.c2token) c2userid = get_userid(r.c2token)
if not c2userid: if not c2userid:
raise NoTokenError raise NoTokenError
data = createc2workoutdata(w) data = createc2workoutdata(w)
if data == 0: if data == 0:
return "Error: No data file. Contact info@rowsandall.com if the problem persists",0 return "Error: No data file. Contact info@rowsandall.com if the problem persists",0
authorizationstring = str('Bearer ' + r.c2token) authorizationstring = str('Bearer ' + r.c2token)
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'} 'Content-Type': 'application/json'}
import urllib import urllib
url = "https://log.concept2.com/api/users/%s/results" % (c2userid) url = "https://log.concept2.com/api/users/%s/results" % (c2userid)
response = requests.post(url,headers=headers,data=json.dumps(data)) response = requests.post(url,headers=headers,data=json.dumps(data,default=default))
if (response.status_code == 409 ): if (response.status_code == 409 ):
message = "Concept2 Duplicate error" message = "Concept2 Duplicate error"
w.uploadedtoc2 = -1 w.uploadedtoc2 = -1
c2id = -1 c2id = -1
w.save() w.save()
elif (response.status_code == 201 or response.status_code == 200): elif (response.status_code == 201 or response.status_code == 200):
try: try:
# s= json.loads(response.text) # s= json.loads(response.text)
s = response.json() s = response.json()
c2id = s['data']['id'] c2id = s['data']['id']
w.uploadedtoc2 = c2id w.uploadedtoc2 = c2id
w.save() w.save()
message = "Upload to Concept2 was successful" message = "Upload to Concept2 was successful"
except: except:
message = "Something went wrong in workout_c2_upload_view. Response code 200/201 but C2 sync failed: "+response.text message = "Something went wrong in workout_c2_upload_view. Response code 200/201 but C2 sync failed: "+response.text
c2id = 0 c2id = 0
else: else:
message = "Something went wrong in workout_c2_upload_view. Response code 200/201 but C2 sync failed: "+response.text message = "Something went wrong in workout_c2_upload_view. Response code 200/201 but C2 sync failed: "+response.text
c2id = 0 c2id = 0
@@ -776,41 +793,41 @@ def add_workout_from_data(user,importid,data,strokedata,
workouttype = 'rower' workouttype = 'rower'
if workouttype not in [x[0] for x in Workout.workouttypes]: if workouttype not in [x[0] for x in Workout.workouttypes]:
workouttype = 'other' workouttype = 'other'
try: try:
comments = data['comments'] comments = data['comments']
except: except:
comments = ' ' comments = ' '
try: try:
thetimezone = tz(data['timezone']) thetimezone = tz(data['timezone'])
except: except:
thetimezone = 'UTC' thetimezone = 'UTC'
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
try: try:
rowdatetime = iso8601.parse_date(data['date_utc']) rowdatetime = iso8601.parse_date(data['date_utc'])
except KeyError: except KeyError:
rowdatetime = iso8601.parse_date(data['start_date']) rowdatetime = iso8601.parse_date(data['start_date'])
except ParseError: except ParseError:
rowdatetime = iso8601.parse_date(data['date']) rowdatetime = iso8601.parse_date(data['date'])
try: try:
c2intervaltype = data['workout_type'] c2intervaltype = data['workout_type']
except KeyError: except KeyError:
c2intervaltype = '' c2intervaltype = ''
try: try:
title = data['name'] title = data['name']
except KeyError: except KeyError:
title = "" title = ""
try: try:
t = data['comments'].split('\n', 1)[0] t = data['comments'].split('\n', 1)[0]
title += t[:20] title += t[:20]
except: except:
title = 'Imported' title = 'Imported'
starttimeunix = arrow.get(rowdatetime).timestamp starttimeunix = arrow.get(rowdatetime).timestamp
@@ -825,17 +842,17 @@ def add_workout_from_data(user,importid,data,strokedata,
nr_rows = len(unixtime) nr_rows = len(unixtime)
try: try:
latcoord = strokedata.loc[:,'lat'] latcoord = strokedata.loc[:,'lat']
loncoord = strokedata.loc[:,'lon'] loncoord = strokedata.loc[:,'lon']
except: except:
latcoord = np.zeros(nr_rows) latcoord = np.zeros(nr_rows)
loncoord = np.zeros(nr_rows) loncoord = np.zeros(nr_rows)
try: try:
strokelength = strokedata.loc[:,'strokelength'] strokelength = strokedata.loc[:,'strokelength']
except: except:
strokelength = np.zeros(nr_rows) strokelength = np.zeros(nr_rows)
dist2 = 0.1*strokedata.loc[:,'d'] dist2 = 0.1*strokedata.loc[:,'d']
@@ -862,23 +879,23 @@ def add_workout_from_data(user,importid,data,strokedata,
# save csv # save csv
# Create data frame with all necessary data to write to csv # Create data frame with all necessary data to write to csv
df = pd.DataFrame({'TimeStamp (sec)':unixtime, df = pd.DataFrame({'TimeStamp (sec)':unixtime,
' Horizontal (meters)': dist2, ' Horizontal (meters)': dist2,
' Cadence (stokes/min)':spm, ' Cadence (stokes/min)':spm,
' HRCur (bpm)':hr, ' HRCur (bpm)':hr,
' longitude':loncoord, ' longitude':loncoord,
' latitude':latcoord, ' latitude':latcoord,
' Stroke500mPace (sec/500m)':pace, ' Stroke500mPace (sec/500m)':pace,
' Power (watts)':power, ' Power (watts)':power,
' DragFactor':np.zeros(nr_rows), ' DragFactor':np.zeros(nr_rows),
' DriveLength (meters)':np.zeros(nr_rows), ' DriveLength (meters)':np.zeros(nr_rows),
' StrokeDistance (meters)':strokelength, ' StrokeDistance (meters)':strokelength,
' DriveTime (ms)':np.zeros(nr_rows), ' DriveTime (ms)':np.zeros(nr_rows),
' StrokeRecoveryTime (ms)':np.zeros(nr_rows), ' StrokeRecoveryTime (ms)':np.zeros(nr_rows),
' AverageDriveForce (lbs)':np.zeros(nr_rows), ' AverageDriveForce (lbs)':np.zeros(nr_rows),
' PeakDriveForce (lbs)':np.zeros(nr_rows), ' PeakDriveForce (lbs)':np.zeros(nr_rows),
' lapIdx':lapidx, ' lapIdx':lapidx,
' ElapsedTime (sec)':seconds ' ElapsedTime (sec)':seconds
}) })
df.sort_values(by='TimeStamp (sec)',ascending=True) df.sort_values(by='TimeStamp (sec)',ascending=True)
+3
View File
@@ -1,4 +1,7 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import os import os
+5
View File
@@ -1,3 +1,8 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from django.conf import settings # import the settings file from django.conf import settings # import the settings file
def braintree_merchant(request): def braintree_merchant(request):
+7 -2
View File
@@ -1,3 +1,8 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
# All the Courses related methods # All the Courses related methods
# Python # Python
@@ -28,7 +33,7 @@ import pandas as pd
import numpy as np import numpy as np
from timezonefinder import TimezoneFinder from timezonefinder import TimezoneFinder
import dataprep import rowers.dataprep as dataprep
from rowers.utils import geo_distance from rowers.utils import geo_distance
ns = {'opengis': 'http://www.opengis.net/kml/2.2'} ns = {'opengis': 'http://www.opengis.net/kml/2.2'}
@@ -47,7 +52,7 @@ from rowers.models import (
polygon_to_path,coordinate_in_path polygon_to_path,coordinate_in_path
) )
from utils import geo_distance
from rowers.courseutils import coursetime_paths, coursetime_first,time_in_path from rowers.courseutils import coursetime_paths, coursetime_first,time_in_path
+6 -2
View File
@@ -1,3 +1,7 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
# low level methods # low level methods
def coordinate_in_path(latitude,longitude, p): def coordinate_in_path(latitude,longitude, p):
@@ -5,10 +9,10 @@ def coordinate_in_path(latitude,longitude, p):
class InvalidTrajectoryError(Exception): class InvalidTrajectoryError(Exception):
def __init__(self,value): def __init__(self,value):
self.value=value self.value=value
def __str__(self): def __str__(self):
return repr(self.value) return repr(self.value)
def time_in_path(df,p,maxmin='max',getall=False): def time_in_path(df,p,maxmin='max',getall=False):
+5
View File
@@ -1,3 +1,8 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from django.conf import settings from django.conf import settings
user = settings.DATABASES['default']['USER'] user = settings.DATABASES['default']['USER']
+26 -17
View File
@@ -1,5 +1,11 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
# All the data preparation, data cleaning and data mangling should # All the data preparation, data cleaning and data mangling should
# be defined here # be defined here
from __future__ import unicode_literals, absolute_import
from rowers.models import Workout, StrokeData,Team from rowers.models import Workout, StrokeData,Team
import pytz import pytz
@@ -55,7 +61,7 @@ import pandas as pd
import numpy as np import numpy as np
import itertools import itertools
import math import math
from tasks import ( from rowers.tasks import (
handle_sendemail_unrecognized, handle_sendemail_breakthrough, handle_sendemail_unrecognized, handle_sendemail_breakthrough,
handle_sendemail_hard, handle_updatecp,handle_updateergcp, handle_sendemail_hard, handle_updatecp,handle_updateergcp,
handle_calctrimp, handle_calctrimp,
@@ -65,9 +71,9 @@ from django.conf import settings
from sqlalchemy import create_engine from sqlalchemy import create_engine
import sqlalchemy as sa import sqlalchemy as sa
import sys import sys
import utils import rowers.utils as utils
import datautils import rowers.datautils as datautils
from utils import lbstoN,myqueue,is_ranking_piece,wavg from rowers.utils import lbstoN,myqueue,is_ranking_piece,wavg
from timezonefinder import TimezoneFinder from timezonefinder import TimezoneFinder
@@ -531,7 +537,10 @@ def getstatsfields():
fielddict.pop('hr_bottom') fielddict.pop('hr_bottom')
fielddict.pop('cumdist') fielddict.pop('cumdist')
fieldlist = [field for field, value in fielddict.iteritems()] try:
fieldlist = [field for field, value in fielddict.iteritems()]
except AttributeError:
fieldlist = [field for field, value in fielddict.items()]
return fieldlist, fielddict return fieldlist, fielddict
@@ -638,7 +647,7 @@ def deletecpdata_sql(rower_id,table='cpdata'):
try: try:
result = conn.execute(query) result = conn.execute(query)
except: except:
print "Database locked" print("Database locked")
conn.close() conn.close()
engine.dispose() engine.dispose()
@@ -855,7 +864,7 @@ def create_row_df(r,distance,duration,startdatetime,workouttype='rower',
return (id, message) return (id, message)
from utils import totaltime_sec_to_string from rowers.utils import totaltime_sec_to_string
# Processes painsled CSV file to database # Processes painsled CSV file to database
def save_workout_database(f2, r, dosmooth=True, workouttype='rower', def save_workout_database(f2, r, dosmooth=True, workouttype='rower',
@@ -904,7 +913,7 @@ def save_workout_database(f2, r, dosmooth=True, workouttype='rower',
try: try:
checks = row.check_consistency() checks = row.check_consistency()
allchecks = 1 allchecks = 1
for key, value in checks.iteritems(): for key, value in checks.items():
if not value: if not value:
allchecks = 0 allchecks = 0
if consistencychecks: if consistencychecks:
@@ -1564,7 +1573,7 @@ def compare_data(id):
res = conn.execute(query) res = conn.execute(query)
l2 = res.fetchall()[0][0] l2 = res.fetchall()[0][0]
except: except:
print "Database Locked" print("Database Locked")
conn.close() conn.close()
engine.dispose() engine.dispose()
lfile = l1 lfile = l1
@@ -1583,13 +1592,13 @@ def repair_data(verbose=False):
test, ldb, lfile = compare_data(w.id) test, ldb, lfile = compare_data(w.id)
if not test: if not test:
if verbose: if verbose:
print w.id, lfile, ldb print(w.id, lfile, ldb)
try: try:
rowdata = rdata(w.csvfilename) rowdata = rdata(w.csvfilename)
if rowdata and len(rowdata.df): if rowdata and len(rowdata.df):
update_strokedata(w.id, rowdata.df) update_strokedata(w.id, rowdata.df)
except IOError, AttributeError: except (IOError, AttributeError):
pass pass
if lfile == 0: if lfile == 0:
@@ -1618,10 +1627,10 @@ def repair_data(verbose=False):
def rdata(file, rower=rrower()): def rdata(file, rower=rrower()):
try: try:
res = rrdata(csvfile=file, rower=rower) res = rrdata(csvfile=file, rower=rower)
except IOError, IndexError: except (IOError, IndexError):
try: try:
res = rrdata(csvfile=file + '.gz', rower=rower) res = rrdata(csvfile=file + '.gz', rower=rower)
except IOError, IndexError: except (IOError, IndexError):
res = rrdata() res = rrdata()
except: except:
res = rrdata() res = rrdata()
@@ -1640,7 +1649,7 @@ def delete_strokedata(id):
try: try:
result = conn.execute(query) result = conn.execute(query)
except: except:
print "Database Locked" print("Database Locked")
conn.close() conn.close()
engine.dispose() engine.dispose()
@@ -1792,7 +1801,7 @@ def prepmultipledata(ids, verbose=False):
for id in res: for id in res:
rowdata, row = getrowdata(id=id) rowdata, row = getrowdata(id=id)
if verbose: if verbose:
print id print(id)
if rowdata and len(rowdata.df): if rowdata and len(rowdata.df):
data = dataprep(rowdata.df, id=id, bands=True, data = dataprep(rowdata.df, id=id, bands=True,
barchart=True, otwpower=True) barchart=True, otwpower=True)
@@ -2029,7 +2038,7 @@ def fix_newtons(id=0, limit=3000):
peakforce = rowdata['peakforce'] peakforce = rowdata['peakforce']
if peakforce.mean() > limit: if peakforce.mean() > limit:
w = Workout.objects.get(id=id) w = Workout.objects.get(id=id)
print "fixing ", id print("fixing ", id)
rowdata = rdata(w.csvfilename) rowdata = rdata(w.csvfilename)
if rowdata and len(rowdata.df): if rowdata and len(rowdata.df):
update_strokedata(w.id, rowdata.df) update_strokedata(w.id, rowdata.df)
@@ -2084,7 +2093,7 @@ def dataprep(rowdatadf, id=0, bands=True, barchart=True, otwpower=True,
if rowdatadf.empty: if rowdatadf.empty:
return 0 return 0
rowdatadf.set_index([range(len(rowdatadf))], inplace=True) #rowdatadf.set_index([range(len(rowdatadf))], inplace=True)
t = rowdatadf.loc[:, 'TimeStamp (sec)'] t = rowdatadf.loc[:, 'TimeStamp (sec)']
t = pd.Series(t - rowdatadf.loc[:, 'TimeStamp (sec)'].iloc[0]) t = pd.Series(t - rowdatadf.loc[:, 'TimeStamp (sec)'].iloc[0])
+146 -141
View File
@@ -1,3 +1,8 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
# This is Data prep used for testing purposes (no Django environment) # This is Data prep used for testing purposes (no Django environment)
# Uses the debug SQLite database for stroke data # Uses the debug SQLite database for stroke data
from rowingdata import rowingdata as rrdata from rowingdata import rowingdata as rrdata
@@ -18,7 +23,7 @@ import sqlalchemy as sa
from rowsandall_app.settings import DATABASES from rowsandall_app.settings import DATABASES
from rowsandall_app.settings_dev import DATABASES as DEV_DATABASES from rowsandall_app.settings_dev import DATABASES as DEV_DATABASES
from utils import lbstoN from rowers.utils import lbstoN
try: try:
@@ -83,32 +88,32 @@ import datetime
def niceformat(values): def niceformat(values):
out = [] out = []
for v in values: for v in values:
formattedv = strfdelta(v) formattedv = strfdelta(v)
out.append(formattedv) out.append(formattedv)
return out return out
def strfdelta(tdelta): def strfdelta(tdelta):
try: try:
minutes,seconds = divmod(tdelta.seconds,60) minutes,seconds = divmod(tdelta.seconds,60)
tenths = int(tdelta.microseconds/1e5) tenths = int(tdelta.microseconds/1e5)
except AttributeError: except AttributeError:
minutes,seconds = divmod(tdelta.view(np.int64),60e9) minutes,seconds = divmod(tdelta.view(np.int64),60e9)
seconds,rest = divmod(seconds,1e9) seconds,rest = divmod(seconds,1e9)
tenths = int(rest/1e8) tenths = int(rest/1e8)
res = "{minutes:0>2}:{seconds:0>2}.{tenths:0>1}".format( res = "{minutes:0>2}:{seconds:0>2}.{tenths:0>1}".format(
minutes=minutes, minutes=minutes,
seconds=seconds, seconds=seconds,
tenths=tenths, tenths=tenths,
) )
return res return res
def nicepaceformat(values): def nicepaceformat(values):
out = [] out = []
for v in values: for v in values:
formattedv = strfdelta(v) formattedv = strfdelta(v)
out.append(formattedv) out.append(formattedv)
return out return out
@@ -124,16 +129,16 @@ def timedeltaconv(x):
def rdata(file,rower=rrower()): def rdata(file,rower=rrower()):
try: try:
res = rrdata(file,rower=rower) res = rrdata(file,rower=rower)
except IOError: except IOError:
try: try:
res = rrdata(file+'.gz',rower=rower) res = rrdata(file+'.gz',rower=rower)
except IOError: except IOError:
res = 0 res = 0
return res return res
from utils import totaltime_sec_to_string from rowers.utils import totaltime_sec_to_string
# Creates C2 stroke data # Creates C2 stroke data
@@ -175,22 +180,22 @@ def create_c2_stroke_data_db(
df = pd.DataFrame({ df = pd.DataFrame({
'TimeStamp (sec)': unixtime, 'TimeStamp (sec)': unixtime,
' Horizontal (meters)': d, ' Horizontal (meters)': d,
' Cadence (stokes/min)': spm, ' Cadence (stokes/min)': spm,
' Stroke500mPace (sec/500m)':pace, ' Stroke500mPace (sec/500m)':pace,
' ElapsedTime (sec)':elapsed, ' ElapsedTime (sec)':elapsed,
' Power (watts)':power, ' Power (watts)':power,
' HRCur (bpm)':np.zeros(nr_strokes), ' HRCur (bpm)':np.zeros(nr_strokes),
' longitude':np.zeros(nr_strokes), ' longitude':np.zeros(nr_strokes),
' latitude':np.zeros(nr_strokes), ' latitude':np.zeros(nr_strokes),
' DragFactor':np.zeros(nr_strokes), ' DragFactor':np.zeros(nr_strokes),
' DriveLength (meters)':np.zeros(nr_strokes), ' DriveLength (meters)':np.zeros(nr_strokes),
' StrokeDistance (meters)':np.zeros(nr_strokes), ' StrokeDistance (meters)':np.zeros(nr_strokes),
' DriveTime (ms)':np.zeros(nr_strokes), ' DriveTime (ms)':np.zeros(nr_strokes),
' StrokeRecoveryTime (ms)':np.zeros(nr_strokes), ' StrokeRecoveryTime (ms)':np.zeros(nr_strokes),
' AverageDriveForce (lbs)':np.zeros(nr_strokes), ' AverageDriveForce (lbs)':np.zeros(nr_strokes),
' PeakDriveForce (lbs)':np.zeros(nr_strokes), ' PeakDriveForce (lbs)':np.zeros(nr_strokes),
' lapIdx':np.zeros(nr_strokes), ' lapIdx':np.zeros(nr_strokes),
'cum_dist': d 'cum_dist': d
}) })
@@ -220,17 +225,17 @@ def add_c2_stroke_data_db(strokedata,workoutid,starttimeunix,csvfilename,
nr_rows = len(unixtime) nr_rows = len(unixtime)
try: try:
latcoord = strokedata.ix[:,'lat'] latcoord = strokedata.ix[:,'lat']
loncoord = strokedata.ix[:,'lon'] loncoord = strokedata.ix[:,'lon']
except: except:
latcoord = np.zeros(nr_rows) latcoord = np.zeros(nr_rows)
loncoord = np.zeros(nr_rows) loncoord = np.zeros(nr_rows)
try: try:
strokelength = strokedata.ix[:,'strokelength'] strokelength = strokedata.ix[:,'strokelength']
except: except:
strokelength = np.zeros(nr_rows) strokelength = np.zeros(nr_rows)
dist2 = 0.1*strokedata.ix[:,'d'] dist2 = 0.1*strokedata.ix[:,'d']
@@ -258,24 +263,24 @@ def add_c2_stroke_data_db(strokedata,workoutid,starttimeunix,csvfilename,
# save csv # save csv
# Create data frame with all necessary data to write to csv # Create data frame with all necessary data to write to csv
df = pd.DataFrame({'TimeStamp (sec)':unixtime, df = pd.DataFrame({'TimeStamp (sec)':unixtime,
' Horizontal (meters)': dist2, ' Horizontal (meters)': dist2,
' Cadence (stokes/min)':spm, ' Cadence (stokes/min)':spm,
' HRCur (bpm)':hr, ' HRCur (bpm)':hr,
' longitude':loncoord, ' longitude':loncoord,
' latitude':latcoord, ' latitude':latcoord,
' Stroke500mPace (sec/500m)':pace, ' Stroke500mPace (sec/500m)':pace,
' Power (watts)':power, ' Power (watts)':power,
' DragFactor':np.zeros(nr_rows), ' DragFactor':np.zeros(nr_rows),
' DriveLength (meters)':np.zeros(nr_rows), ' DriveLength (meters)':np.zeros(nr_rows),
' StrokeDistance (meters)':strokelength, ' StrokeDistance (meters)':strokelength,
' DriveTime (ms)':np.zeros(nr_rows), ' DriveTime (ms)':np.zeros(nr_rows),
' StrokeRecoveryTime (ms)':np.zeros(nr_rows), ' StrokeRecoveryTime (ms)':np.zeros(nr_rows),
' AverageDriveForce (lbs)':np.zeros(nr_rows), ' AverageDriveForce (lbs)':np.zeros(nr_rows),
' PeakDriveForce (lbs)':np.zeros(nr_rows), ' PeakDriveForce (lbs)':np.zeros(nr_rows),
' lapIdx':lapidx, ' lapIdx':lapidx,
' ElapsedTime (sec)':seconds, ' ElapsedTime (sec)':seconds,
'cum_dist': dist2 'cum_dist': dist2
}) })
df.sort_values(by='TimeStamp (sec)',ascending=True) df.sort_values(by='TimeStamp (sec)',ascending=True)
@@ -312,8 +317,8 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
# make workout and put in database # make workout and put in database
rr = rrower(hrmax=r.max,hrut2=r.ut2, rr = rrower(hrmax=r.max,hrut2=r.ut2,
hrut1=r.ut1,hrat=r.at, hrut1=r.ut1,hrat=r.at,
hrtr=r.tr,hran=r.an,ftp=r.ftp, hrtr=r.tr,hran=r.an,ftp=r.ftp,
powerperc=powerperc,powerzones=r.powerzones) powerperc=powerperc,powerzones=r.powerzones)
row = rdata(f2,rower=rr) row = rdata(f2,rower=rr)
@@ -329,7 +334,7 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
if row == 0: if row == 0:
return (0,'Error: CSV data file not found') return (0,'Error: CSV data file not found')
if dosmooth: if dosmooth:
# auto smoothing # auto smoothing
@@ -342,12 +347,12 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
else: else:
windowsize = 1 windowsize = 1
if not 'originalvelo' in row.df: if not 'originalvelo' in row.df:
row.df['originalvelo'] = velo row.df['originalvelo'] = velo
if windowsize > 3 and windowsize<len(velo): if windowsize > 3 and windowsize<len(velo):
velo2 = savgol_filter(velo,windowsize,3) velo2 = savgol_filter(velo,windowsize,3)
else: else:
velo2 = velo velo2 = velo
velo3 = pd.Series(velo2) velo3 = pd.Series(velo2)
velo3 = velo3.replace([-np.inf,np.inf],np.nan) velo3 = velo3.replace([-np.inf,np.inf],np.nan)
@@ -367,11 +372,11 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
# recalculate power data # recalculate power data
if workouttype == 'rower' or workouttype == 'dynamic' or workouttype == 'slides': if workouttype == 'rower' or workouttype == 'dynamic' or workouttype == 'slides':
try: try:
row.erg_recalculatepower() row.erg_recalculatepower()
row.write_csv(f2,gzip=True) row.write_csv(f2,gzip=True)
except: except:
pass pass
averagehr = row.df[' HRCur (bpm)'].mean() averagehr = row.df[' HRCur (bpm)'].mean()
maxhr = row.df[' HRCur (bpm)'].max() maxhr = row.df[' HRCur (bpm)'].max()
@@ -425,22 +430,22 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
# check for duplicate start times # check for duplicate start times
ws = Workout.objects.filter(startdatetime=workoutstartdatetime, ws = Workout.objects.filter(startdatetime=workoutstartdatetime,
user=r) user=r)
if (len(ws) != 0): if (len(ws) != 0):
message = "Warning: This workout probably already exists in the database" message = "Warning: This workout probably already exists in the database"
privacy = 'private' privacy = 'private'
w = Workout(user=r,name=title,date=workoutdate, w = Workout(user=r,name=title,date=workoutdate,
workouttype=workouttype, workouttype=workouttype,
workoutsource=workoutsource, workoutsource=workoutsource,
duration=duration,distance=totaldist, duration=duration,distance=totaldist,
weightcategory=r.weightcategory, weightcategory=r.weightcategory,
starttime=workoutstarttime, starttime=workoutstarttime,
csvfilename=f2,notes=notes,summary=summary, csvfilename=f2,notes=notes,summary=summary,
maxhr=maxhr,averagehr=averagehr, maxhr=maxhr,averagehr=averagehr,
startdatetime=workoutstartdatetime, startdatetime=workoutstartdatetime,
inboard=inboard,oarlength=oarlength, inboard=inboard,oarlength=oarlength,
privacy=privacy) privacy=privacy)
@@ -463,14 +468,14 @@ def handle_nonpainsled(f2,fileformat,summary=''):
inboard = 0.88 inboard = 0.88
# handle RowPro: # handle RowPro:
if (fileformat == 'rp'): if (fileformat == 'rp'):
row = RowProParser(f2) row = RowProParser(f2)
# handle TCX # handle TCX
if (fileformat == 'tcx'): if (fileformat == 'tcx'):
row = TCXParser(f2) row = TCXParser(f2)
# handle Mystery # handle Mystery
if (fileformat == 'mystery'): if (fileformat == 'mystery'):
row = MysteryParser(f2) row = MysteryParser(f2)
# handle RowPerfect # handle RowPerfect
if (fileformat == 'rowperfect3'): if (fileformat == 'rowperfect3'):
@@ -478,11 +483,11 @@ def handle_nonpainsled(f2,fileformat,summary=''):
# handle ErgData # handle ErgData
if (fileformat == 'ergdata'): if (fileformat == 'ergdata'):
row = ErgDataParser(f2) row = ErgDataParser(f2)
# handle CoxMate # handle CoxMate
if (fileformat == 'coxmate'): if (fileformat == 'coxmate'):
row = CoxMateParser(f2) row = CoxMateParser(f2)
# handle Mike # handle Mike
if (fileformat == 'bcmike'): if (fileformat == 'bcmike'):
@@ -494,19 +499,19 @@ def handle_nonpainsled(f2,fileformat,summary=''):
# handle BoatCoach # handle BoatCoach
if (fileformat == 'boatcoach'): if (fileformat == 'boatcoach'):
row = BoatCoachParser(f2) row = BoatCoachParser(f2)
# handle painsled desktop # handle painsled desktop
if (fileformat == 'painsleddesktop'): if (fileformat == 'painsleddesktop'):
row = painsledDesktopParser(f2) row = painsledDesktopParser(f2)
# handle speed coach GPS # handle speed coach GPS
if (fileformat == 'speedcoach'): if (fileformat == 'speedcoach'):
row = speedcoachParser(f2) row = speedcoachParser(f2)
# handle speed coach GPS 2 # handle speed coach GPS 2
if (fileformat == 'speedcoach2'): if (fileformat == 'speedcoach2'):
row = SpeedCoach2Parser(f2) row = SpeedCoach2Parser(f2)
try: try:
oarlength,inboard = get_empower_rigging(f2) oarlength,inboard = get_empower_rigging(f2)
summary = row.allstats() summary = row.allstats()
@@ -516,14 +521,14 @@ def handle_nonpainsled(f2,fileformat,summary=''):
# handle ErgStick # handle ErgStick
if (fileformat == 'ergstick'): if (fileformat == 'ergstick'):
row = ErgStickParser(f2) row = ErgStickParser(f2)
# handle FIT # handle FIT
if (fileformat == 'fit'): if (fileformat == 'fit'):
row = FITParser(f2) row = FITParser(f2)
s = fitsummarydata(f2) s = fitsummarydata(f2)
s.setsummary() s.setsummary()
summary = s.summarytext summary = s.summarytext
f_to_be_deleted = f2 f_to_be_deleted = f2
@@ -533,7 +538,7 @@ def handle_nonpainsled(f2,fileformat,summary=''):
#os.remove(f2) #os.remove(f2)
try: try:
os.remove(f_to_be_deleted) os.remove(f_to_be_deleted)
except: except:
os.remove(f_to_be_deleted+'.gz') os.remove(f_to_be_deleted+'.gz')
@@ -588,14 +593,14 @@ def new_workout_from_file(r,f2,
# for me to check if it is a bug, or a new file type # for me to check if it is a bug, or a new file type
# worth supporting # worth supporting
if fileformat == 'unknown': if fileformat == 'unknown':
message = "We couldn't recognize the file type" message = "We couldn't recognize the file type"
if settings.DEBUG: if settings.DEBUG:
res = handle_sendemail_unrecognized.delay(f2, res = handle_sendemail_unrecognized.delay(f2,
r.user.email) r.user.email)
else: else:
res = queuehigh.enqueue(handle_sendemail_unrecognized, res = queuehigh.enqueue(handle_sendemail_unrecognized,
f2,r.user.email) f2,r.user.email)
return (0,message,f2) return (0,message,f2)
# handle non-Painsled by converting it to painsled compatible CSV # handle non-Painsled by converting it to painsled compatible CSV
@@ -635,14 +640,14 @@ def delete_strokedata(id,debug=False):
try: try:
result = conn.execute(query) result = conn.execute(query)
except: except:
print "Database Locked" print("Database Locked")
conn.close() conn.close()
engine.dispose() engine.dispose()
def update_strokedata(id,df,debug=False): def update_strokedata(id,df,debug=False):
delete_strokedata(id,debug=debug) delete_strokedata(id,debug=debug)
if debug: if debug:
print "updating ",id print("updating ",id)
rowdata = dataprep(df,id=id,bands=True,barchart=True,otwpower=True, rowdata = dataprep(df,id=id,bands=True,barchart=True,otwpower=True,
debug=debug) debug=debug)
@@ -676,11 +681,11 @@ def update_empower(id, inboard, oarlength, boattype, df, f1, debug=False):
if success: if success:
delete_strokedata(id,debug=debug) delete_strokedata(id,debug=debug)
if debug: if debug:
print "updated ",id print("updated ",id)
print "correction ",corr_factor print("correction ",corr_factor)
else: else:
if debug: if debug:
print "not updated ",id print("not updated ",id)
rowdata = dataprep(df,id=id,bands=True,barchart=True,otwpower=True, rowdata = dataprep(df,id=id,bands=True,barchart=True,otwpower=True,
@@ -784,8 +789,8 @@ def read_cols_df_sql(ids,columns,debug=False):
def read_df_sql(id,debug=False): def read_df_sql(id,debug=False):
if debug: if debug:
engine = create_engine(database_url_debug, echo=False) engine = create_engine(database_url_debug, echo=False)
print "read_df",id print("read_df",id)
print database_url_debug print(database_url_debug)
else: else:
engine = create_engine(database_url, echo=False) engine = create_engine(database_url, echo=False)
@@ -826,7 +831,7 @@ def deletecpdata_sql(rower_id,table='cpdata',debug=False):
try: try:
result = conn.execute(query) result = conn.execute(query)
except: except:
print "Database locked" print("Database locked")
conn.close() conn.close()
engine.dispose() engine.dispose()
@@ -846,7 +851,7 @@ def delete_agegroup_db(age,sex,weightcategory,debug=False):
try: try:
result = conn.execute(query) result = conn.execute(query)
except: except:
print "Database locked" print("Database locked")
conn.close() conn.close()
engine.dispose() engine.dispose()
@@ -956,11 +961,11 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
if rowdatadf.empty: if rowdatadf.empty:
if debug: if debug:
print "empty" print("empty")
return 0 return 0
if debug: if debug:
print "dataprep",id print("dataprep",id)
rowdatadf.set_index([range(len(rowdatadf))],inplace=True) rowdatadf.set_index([range(len(rowdatadf))],inplace=True)
t = rowdatadf.ix[:,'TimeStamp (sec)'] t = rowdatadf.ix[:,'TimeStamp (sec)']
@@ -1006,12 +1011,12 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
else: else:
windowsize = 1 windowsize = 1
if windowsize <= 3: if windowsize <= 3:
windowsize = 5 windowsize = 5
if windowsize > 3 and windowsize<len(hr): if windowsize > 3 and windowsize<len(hr):
spm = savgol_filter(spm,windowsize,3) spm = savgol_filter(spm,windowsize,3)
hr = savgol_filter(hr,windowsize,3) hr = savgol_filter(hr,windowsize,3)
drivelength = savgol_filter(drivelength,windowsize,3) drivelength = savgol_filter(drivelength,windowsize,3)
forceratio = savgol_filter(forceratio,windowsize,3) forceratio = savgol_filter(forceratio,windowsize,3)
try: try:
@@ -1072,14 +1077,14 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
) )
if bands: if bands:
# HR bands # HR bands
data['hr_ut2'] = rowdatadf.ix[:,'hr_ut2'] data['hr_ut2'] = rowdatadf.ix[:,'hr_ut2']
data['hr_ut1'] = rowdatadf.ix[:,'hr_ut1'] data['hr_ut1'] = rowdatadf.ix[:,'hr_ut1']
data['hr_at'] = rowdatadf.ix[:,'hr_at'] data['hr_at'] = rowdatadf.ix[:,'hr_at']
data['hr_tr'] = rowdatadf.ix[:,'hr_tr'] data['hr_tr'] = rowdatadf.ix[:,'hr_tr']
data['hr_an'] = rowdatadf.ix[:,'hr_an'] data['hr_an'] = rowdatadf.ix[:,'hr_an']
data['hr_max'] = rowdatadf.ix[:,'hr_max'] data['hr_max'] = rowdatadf.ix[:,'hr_max']
data['hr_bottom'] = 0.0*data['hr'] data['hr_bottom'] = 0.0*data['hr']
try: try:
@@ -1088,13 +1093,13 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
rowdatadf[' ElapsedTime (sec)'] = rowdatadf['TimeStamp (sec)'] rowdatadf[' ElapsedTime (sec)'] = rowdatadf['TimeStamp (sec)']
if barchart: if barchart:
# time increments for bar chart # time increments for bar chart
time_increments = rowdatadf.ix[:,' ElapsedTime (sec)'].diff() time_increments = rowdatadf.ix[:,' ElapsedTime (sec)'].diff()
time_increments[0] = time_increments[1] time_increments[0] = time_increments[1]
time_increments = 0.5*time_increments+0.5*np.abs(time_increments) time_increments = 0.5*time_increments+0.5*np.abs(time_increments)
x_right = (t2+time_increments.apply(lambda x:timedeltaconv(x))) x_right = (t2+time_increments.apply(lambda x:timedeltaconv(x)))
data['x_right'] = x_right data['x_right'] = x_right
if empower: if empower:
try: try:
@@ -1207,29 +1212,29 @@ def dataprep(rowdatadf,id=0,bands=True,barchart=True,otwpower=True,
pass pass
if otwpower: if otwpower:
try: try:
nowindpace = rowdatadf.ix[:,'nowindpace'] nowindpace = rowdatadf.ix[:,'nowindpace']
except KeyError: except KeyError:
nowindpace = p nowindpace = p
try: try:
equivergpower = rowdatadf.ix[:,'equivergpower'] equivergpower = rowdatadf.ix[:,'equivergpower']
except KeyError: except KeyError:
equivergpower = 0*p+50. equivergpower = 0*p+50.
nowindpace2 = nowindpace.apply(lambda x: timedeltaconv(x)) nowindpace2 = nowindpace.apply(lambda x: timedeltaconv(x))
ergvelo = (equivergpower/2.8)**(1./3.) ergvelo = (equivergpower/2.8)**(1./3.)
ergpace = 500./ergvelo ergpace = 500./ergvelo
ergpace[ergpace == np.inf] = 240. ergpace[ergpace == np.inf] = 240.
ergpace2 = ergpace.apply(lambda x: timedeltaconv(x)) ergpace2 = ergpace.apply(lambda x: timedeltaconv(x))
data['ergpace'] = ergpace*1e3 data['ergpace'] = ergpace*1e3
data['nowindpace'] = nowindpace*1e3 data['nowindpace'] = nowindpace*1e3
data['equivergpower'] = equivergpower data['equivergpower'] = equivergpower
data['fergpace'] = nicepaceformat(ergpace2) data['fergpace'] = nicepaceformat(ergpace2)
data['fnowindpace'] = nicepaceformat(nowindpace2) data['fnowindpace'] = nicepaceformat(nowindpace2)
data['efficiency'] = efficiency data['efficiency'] = efficiency
data = data.replace([-np.inf,np.inf],np.nan) data = data.replace([-np.inf,np.inf],np.nan)
+11 -6
View File
@@ -1,3 +1,8 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import pandas as pd import pandas as pd
import numpy as np import numpy as np
from scipy.interpolate import griddata from scipy.interpolate import griddata
@@ -49,14 +54,14 @@ def cpfit(powerdf):
if len(thesecs)>=4: if len(thesecs)>=4:
try: try:
p1, success = optimize.leastsq(errfunc, p0[:], args = (thesecs,theavpower)) p1, success = optimize.leastsq(errfunc, p0[:], args = (thesecs,theavpower))
except: except:
factor = fitfunc(p0,thesecs.mean())/theavpower.mean() factor = fitfunc(p0,thesecs.mean())/theavpower.mean()
p1 = [p0[0]/factor,p0[1]/factor,p0[2],p0[3]] p1 = [p0[0]/factor,p0[1]/factor,p0[2],p0[3]]
else: else:
factor = fitfunc(p0,thesecs.mean())/theavpower.mean() factor = fitfunc(p0,thesecs.mean())/theavpower.mean()
p1 = [p0[0]/factor,p0[1]/factor,p0[2],p0[3]] p1 = [p0[0]/factor,p0[1]/factor,p0[2],p0[3]]
p1 = [abs(p) for p in p1] p1 = [abs(p) for p in p1]
@@ -259,7 +264,7 @@ def getcp(dfgrouped,logarr):
length = len(ww) length = len(ww)
dt = [] dt = []
cpw = [] cpw = []
for i in xrange(length-2): for i in range(length-2):
deltat,wmax = getmaxwattinterval(tt,ww,i) deltat,wmax = getmaxwattinterval(tt,ww,i)
if not np.isnan(deltat) and not np.isnan(wmax): if not np.isnan(deltat) and not np.isnan(wmax):
+5
View File
@@ -1,3 +1,8 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from django.contrib.auth.decorators import login_required,user_passes_test from django.contrib.auth.decorators import login_required,user_passes_test
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
+5
View File
@@ -1,3 +1,8 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import os import os
import time import time
import gc import gc
+95 -88
View File
@@ -1,3 +1,9 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from __future__ import unicode_literals, absolute_import
from django import forms from django import forms
from django.contrib.admin.widgets import FilteredSelectMultiple from django.contrib.admin.widgets import FilteredSelectMultiple
from rowers.models import ( from rowers.models import (
@@ -9,15 +15,16 @@ from rowers.rows import validate_file_extension,must_be_csv,validate_image_exten
from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.admin.widgets import AdminDateWidget from django.contrib.admin.widgets import AdminDateWidget
from django.forms.extras.widgets import SelectDateWidget from django.forms.widgets import SelectDateWidget
#from django.forms.extras.widgets import SelectDateWidget
from django.utils import timezone,translation from django.utils import timezone,translation
from django.forms import ModelForm, Select from django.forms import ModelForm, Select
import dataprep import rowers.dataprep as dataprep
import mytypes import rowers.mytypes as mytypes
import datetime import datetime
from django.forms import formset_factory from django.forms import formset_factory
from utils import landingpages from rowers.utils import landingpages
from metrics import axes from rowers.metrics import axes
class FlexibleDecimalField(forms.DecimalField): class FlexibleDecimalField(forms.DecimalField):
@@ -84,7 +91,7 @@ for key, value in disqualificationreasons:
class DisqualificationForm(forms.Form): class DisqualificationForm(forms.Form):
reason = forms.ChoiceField(required=True, reason = forms.ChoiceField(required=True,
choices=disqualificationreasons, choices=disqualificationreasons,
widget = forms.RadioSelect,) widget = forms.RadioSelect,)
message = forms.CharField(required=True,widget=forms.Textarea) message = forms.CharField(required=True,widget=forms.Textarea)
@@ -139,10 +146,10 @@ class CourseForm(forms.Form):
class DocumentsForm(forms.Form): class DocumentsForm(forms.Form):
title = forms.CharField(required=False) title = forms.CharField(required=False)
file = forms.FileField(required=False, file = forms.FileField(required=False,
validators=[validate_file_extension]) validators=[validate_file_extension])
workouttype = forms.ChoiceField(required=True, workouttype = forms.ChoiceField(required=True,
choices=Workout.workouttypes) choices=Workout.workouttypes)
boattype = forms.ChoiceField(required=True, boattype = forms.ChoiceField(required=True,
choices=mytypes.boattypes, choices=mytypes.boattypes,
@@ -150,19 +157,19 @@ class DocumentsForm(forms.Form):
notes = forms.CharField(required=False, notes = forms.CharField(required=False,
widget=forms.Textarea) widget=forms.Textarea)
offline = forms.BooleanField(initial=False,required=False, offline = forms.BooleanField(initial=False,required=False,
label='Process in Background') label='Process in Background')
class Meta: class Meta:
fields = ['title','file','workouttype','boattype','fileformat','offline'] fields = ['title','file','workouttype','boattype','fileformat','offline']
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
from django.forms.widgets import HiddenInput from django.forms.widgets import HiddenInput
super(DocumentsForm, self).__init__(*args, **kwargs) super(DocumentsForm, self).__init__(*args, **kwargs)
# self.fields['offline'].widget = HiddenInput() # self.fields['offline'].widget = HiddenInput()
from utils import ( from rowers.utils import (
workflowleftpanel,workflowmiddlepanel, workflowleftpanel,workflowmiddlepanel,
defaultleft,defaultmiddle defaultleft,defaultmiddle
) )
@@ -267,14 +274,14 @@ class LandingPageForm(forms.Form):
class UploadOptionsForm(forms.Form): class UploadOptionsForm(forms.Form):
plotchoices = ( plotchoices = (
('timeplot','Time Plot'), ('timeplot','Time Plot'),
('distanceplot','Distance Plot'), ('distanceplot','Distance Plot'),
('pieplot','Heart Rate Pie Chart'), ('pieplot','Heart Rate Pie Chart'),
) )
make_plot = forms.BooleanField(initial=False,required=False) make_plot = forms.BooleanField(initial=False,required=False)
plottype = forms.ChoiceField(required=False, plottype = forms.ChoiceField(required=False,
choices=plotchoices, choices=plotchoices,
initial='timeplot', initial='timeplot',
label='Plot Type') label='Plot Type')
upload_to_C2 = forms.BooleanField(initial=False,required=False, upload_to_C2 = forms.BooleanField(initial=False,required=False,
label='Export to Concept2 logbook') label='Export to Concept2 logbook')
@@ -303,7 +310,7 @@ class UploadOptionsForm(forms.Form):
label='After Upload, go to') label='After Upload, go to')
class Meta: class Meta:
fields = ['make_plot','plottype','upload_toc2','makeprivate'] fields = ['make_plot','plottype','upload_toc2','makeprivate']
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request',None) self.request = kwargs.pop('request',None)
@@ -332,18 +339,18 @@ class UploadOptionsForm(forms.Form):
# a team member # a team member
class TeamUploadOptionsForm(forms.Form): class TeamUploadOptionsForm(forms.Form):
plotchoices = ( plotchoices = (
('timeplot','Time Plot'), ('timeplot','Time Plot'),
('distanceplot','Distance Plot'), ('distanceplot','Distance Plot'),
('pieplot','Pie Chart'), ('pieplot','Pie Chart'),
) )
make_plot = forms.BooleanField(initial=False,required=False) make_plot = forms.BooleanField(initial=False,required=False)
plottype = forms.ChoiceField(required=False, plottype = forms.ChoiceField(required=False,
choices=plotchoices, choices=plotchoices,
initial='timeplot', initial='timeplot',
label='Plot Type') label='Plot Type')
class Meta: class Meta:
fields = ['make_plot','plottype'] fields = ['make_plot','plottype']
# This form is used on the Workout Split page # This form is used on the Workout Split page
class WorkoutSplitForm(forms.Form): class WorkoutSplitForm(forms.Form):
@@ -392,9 +399,9 @@ class OteWorkoutTypeForm(forms.Form):
class PredictedPieceForm(forms.Form): class PredictedPieceForm(forms.Form):
unitchoices = ( unitchoices = (
('t','minutes'), ('t','minutes'),
('d','meters'), ('d','meters'),
) )
rankingdistancechoices = [] rankingdistancechoices = []
rankingdurationchoices = [] rankingdurationchoices = []
@@ -422,10 +429,10 @@ class PredictedPieceForm(forms.Form):
value = forms.FloatField(initial=10,label='Free ranking piece (minutes)') value = forms.FloatField(initial=10,label='Free ranking piece (minutes)')
pieceunit = forms.ChoiceField(required=True,choices=unitchoices, pieceunit = forms.ChoiceField(required=True,choices=unitchoices,
initial='t',label='Unit') initial='t',label='Unit')
class Meta: class Meta:
fields = ['value','pieceunit'] fields = ['value','pieceunit']
class PredictedPieceFormNoDistance(forms.Form): class PredictedPieceFormNoDistance(forms.Form):
@@ -450,76 +457,76 @@ class PredictedPieceFormNoDistance(forms.Form):
# On the Geeky side, to update stream information for river dwellers # On the Geeky side, to update stream information for river dwellers
class UpdateStreamForm(forms.Form): class UpdateStreamForm(forms.Form):
unitchoices = ( unitchoices = (
('m','m/s'), ('m','m/s'),
('f','foot/s'), ('f','foot/s'),
('k','knots'), ('k','knots'),
('p','pace difference (sec/500m)'), ('p','pace difference (sec/500m)'),
) )
dist1 = forms.FloatField(initial=0,label = 'Distance 1') dist1 = forms.FloatField(initial=0,label = 'Distance 1')
dist2 = forms.FloatField(initial=1000,label = 'Distance 2') dist2 = forms.FloatField(initial=1000,label = 'Distance 2')
stream1 = forms.FloatField(initial=0,label = 'Stream velocity 1') stream1 = forms.FloatField(initial=0,label = 'Stream velocity 1')
stream2 = forms.FloatField(initial=0,label = 'Stream velocity 2') stream2 = forms.FloatField(initial=0,label = 'Stream velocity 2')
streamunit = forms.ChoiceField(required=True, streamunit = forms.ChoiceField(required=True,
choices=unitchoices, choices=unitchoices,
initial='m', initial='m',
label='Unit') label='Unit')
class Meta: class Meta:
fields = ['dist1','dist2','stream1', 'stream2','streamunit'] fields = ['dist1','dist2','stream1', 'stream2','streamunit']
# add wind information to your workout # add wind information to your workout
class UpdateWindForm(forms.Form): class UpdateWindForm(forms.Form):
unitchoices = ( unitchoices = (
('m','m/s'), ('m','m/s'),
('k','knots'), ('k','knots'),
('b','beaufort'), ('b','beaufort'),
('kmh','km/h'), ('kmh','km/h'),
('mph','miles/hour'), ('mph','miles/hour'),
) )
dist1 = forms.FloatField(initial=0,label = 'Distance 1') dist1 = forms.FloatField(initial=0,label = 'Distance 1')
dist2 = forms.FloatField(initial=1000,label = 'Distance 2') dist2 = forms.FloatField(initial=1000,label = 'Distance 2')
vwind1 = forms.FloatField(initial=0,required=False,label = 'Wind Speed 1') vwind1 = forms.FloatField(initial=0,required=False,label = 'Wind Speed 1')
vwind2 = forms.FloatField(initial=0,required=False,label = 'Wind Speed 2') vwind2 = forms.FloatField(initial=0,required=False,label = 'Wind Speed 2')
windunit = forms.ChoiceField(required=True, windunit = forms.ChoiceField(required=True,
choices=unitchoices, choices=unitchoices,
initial='m', initial='m',
label='Unit') label='Unit')
winddirection1 = forms.IntegerField(initial=0,required=False, winddirection1 = forms.IntegerField(initial=0,required=False,
label = 'Wind Direction 1') label = 'Wind Direction 1')
winddirection2 = forms.IntegerField(initial=0,required=False, winddirection2 = forms.IntegerField(initial=0,required=False,
label = 'Wind Direction 2') label = 'Wind Direction 2')
class Meta: class Meta:
fields = ['dist1','dist2', fields = ['dist1','dist2',
'vwind1','vwind2', 'vwind1','vwind2',
'windunit', 'windunit',
'winddirection1','winddirection2'] 'winddirection1','winddirection2']
# Form to select a data range to show workouts from a certain time period # Form to select a data range to show workouts from a certain time period
class DateRangeForm(forms.Form): class DateRangeForm(forms.Form):
startdate = forms.DateField( startdate = forms.DateField(
initial=timezone.now()-datetime.timedelta(days=365), initial=timezone.now()-datetime.timedelta(days=365),
# widget=SelectDateWidget(years=range(1990,2050)), # widget=SelectDateWidget(years=range(1990,2050)),
widget=AdminDateWidget(), widget=AdminDateWidget(),
label='Start Date') label='Start Date')
enddate = forms.DateField( enddate = forms.DateField(
initial=timezone.now(), initial=timezone.now(),
widget=AdminDateWidget(), widget=AdminDateWidget(),
label='End Date') label='End Date')
class Meta: class Meta:
fields = ['startdate','enddate'] fields = ['startdate','enddate']
class FitnessMetricForm(forms.Form): class FitnessMetricForm(forms.Form):
startdate = forms.DateField( startdate = forms.DateField(
initial=timezone.now()-datetime.timedelta(days=365), initial=timezone.now()-datetime.timedelta(days=365),
# widget=SelectDateWidget(years=range(1990,2050)), # widget=SelectDateWidget(years=range(1990,2050)),
widget=AdminDateWidget(), widget=AdminDateWidget(),
label='Start Date') label='Start Date')
enddate = forms.DateField( enddate = forms.DateField(
initial=timezone.now(), initial=timezone.now(),
widget=AdminDateWidget(), widget=AdminDateWidget(),
label='End Date') label='End Date')
modechoices = ( modechoices = (
('rower','indoor rower'), ('rower','indoor rower'),
@@ -533,7 +540,7 @@ class FitnessMetricForm(forms.Form):
) )
class Meta: class Meta:
fields = ['startdate','enddate','mode'] fields = ['startdate','enddate','mode']
class SessionDateShiftForm(forms.Form): class SessionDateShiftForm(forms.Form):
shiftstartdate = forms.DateField( shiftstartdate = forms.DateField(
@@ -599,7 +606,7 @@ class RegistrationFormSex(RegistrationFormUniqueEmail):
weightcategories = ( weightcategories = (
('hwt','heavy-weight'), ('hwt','heavy-weight'),
('lwt','light-weight'), ('lwt','light-weight'),
) )
adaptivecategories = mytypes.adaptivetypes adaptivecategories = mytypes.adaptivetypes
@@ -637,8 +644,8 @@ class RegistrationFormSex(RegistrationFormUniqueEmail):
class MyTimeField(forms.TimeField): class MyTimeField(forms.TimeField):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(MyTimeField, self).__init__(*args, **kwargs) super(MyTimeField, self).__init__(*args, **kwargs)
supports_microseconds = True supports_microseconds = True
# Form used to automatically define intervals by pace or power # Form used to automatically define intervals by pace or power
class PowerIntervalUpdateForm(forms.Form): class PowerIntervalUpdateForm(forms.Form):
@@ -660,28 +667,28 @@ class PowerIntervalUpdateForm(forms.Form):
class IntervalUpdateForm(forms.Form): class IntervalUpdateForm(forms.Form):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
typechoices = ( typechoices = (
(1,'single time'), (1,'single time'),
(2,'single distance'), (2,'single distance'),
(3,'rest (time based)'), (3,'rest (time based)'),
(3,'rest (distance based)'), (3,'rest (distance based)'),
(4,'work (time based)'), (4,'work (time based)'),
(5,'work (distance based)'), (5,'work (distance based)'),
) )
aantal = int(kwargs.pop('aantal')) aantal = int(kwargs.pop('aantal'))
super(IntervalUpdateForm, self).__init__(*args, **kwargs) super(IntervalUpdateForm, self).__init__(*args, **kwargs)
for i in range(aantal): for i in range(aantal):
self.fields['intervalt_%s' % i] = forms.DurationField(label='Time '+str(i+1)) self.fields['intervalt_%s' % i] = forms.DurationField(label='Time '+str(i+1))
self.fields['intervald_%s' % i] = forms.IntegerField(label='Distance '+str(i+1)) self.fields['intervald_%s' % i] = forms.IntegerField(label='Distance '+str(i+1))
self.fields['type_%s' % i] = forms.ChoiceField(choices=typechoices, self.fields['type_%s' % i] = forms.ChoiceField(choices=typechoices,
required=True, required=True,
initial=4, initial=4,
label = 'Type '+str(i+1)) label = 'Type '+str(i+1))
self.fields['intervalt_%s' % i].widget.attrs['style'] = 'width:76px; height: 16px;' 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['intervald_%s' % i].widget.attrs['style'] = 'width:76px; height: 16px;'
self.fields['type_%s' % i].widget.attrs['style'] = 'width:156px; height: 22px;' self.fields['type_%s' % i].widget.attrs['style'] = 'width:156px; height: 22px;'
self.fields['intervald_%s' % i].widget = forms.TimeInput(format='%H:%M:%S.%f') self.fields['intervald_%s' % i].widget = forms.TimeInput(format='%H:%M:%S.%f')
boattypes = mytypes.boattypes boattypes = mytypes.boattypes
workouttypes = mytypes.workouttypes workouttypes = mytypes.workouttypes
@@ -824,7 +831,7 @@ formaxlabelsmultiflex.pop('distance')
formaxlabelsmultiflex['workoutid'] = 'Workout' formaxlabelsmultiflex['workoutid'] = 'Workout'
parchoicesmultiflex = list(sorted(formaxlabelsmultiflex.items(), key = lambda x:x[1])) parchoicesmultiflex = list(sorted(formaxlabelsmultiflex.items(), key = lambda x:x[1]))
from utils import palettes from rowers.utils import palettes
palettechoices = tuple((p,p) for p in palettes.keys()) palettechoices = tuple((p,p) for p in palettes.keys())
@@ -950,7 +957,7 @@ class RaceResultFilterForm(forms.Form):
weightcategories = ( weightcategories = (
('hwt','heavy-weight'), ('hwt','heavy-weight'),
('lwt','light-weight'), ('lwt','light-weight'),
) )
adaptivecategories = mytypes.adaptivetypes adaptivecategories = mytypes.adaptivetypes
+33 -28
View File
@@ -1,3 +1,9 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from __future__ import unicode_literals, absolute_import
# All the functionality to connect to SportTracks # All the functionality to connect to SportTracks
# Python # Python
@@ -16,7 +22,7 @@ from dateutil import parser
import time import time
from time import strftime from time import strftime
import dataprep import rowers.dataprep as dataprep
import math import math
from math import sin,cos,atan2,sqrt from math import sin,cos,atan2,sqrt
import os,sys import os,sys
@@ -38,7 +44,7 @@ from django.contrib.auth.decorators import login_required
from rowingdata import rowingdata, make_cumvalues from rowingdata import rowingdata, make_cumvalues
import pandas as pd import pandas as pd
from rowers.models import Rower,Workout,checkworkoutuser from rowers.models import Rower,Workout,checkworkoutuser
from rowers import mytypes import rowers.mytypes as mytypes
from rowsandall_app.settings import ( from rowsandall_app.settings import (
C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET, C2_CLIENT_ID, C2_REDIRECT_URI, C2_CLIENT_SECRET,
STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI, STRAVA_CLIENT_ID, STRAVA_REDIRECT_URI,
@@ -46,7 +52,7 @@ from rowsandall_app.settings import (
SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI SPORTTRACKS_CLIENT_ID, SPORTTRACKS_REDIRECT_URI
) )
from utils import ( from rowers.utils import (
NoTokenError, custom_exception_handler, ewmovingaverage, NoTokenError, custom_exception_handler, ewmovingaverage,
geo_distance,isprorower,uniqify geo_distance,isprorower,uniqify
) )
@@ -59,9 +65,9 @@ def splitstdata(lijst):
t = [] t = []
latlong = [] latlong = []
while len(lijst)>=2: while len(lijst)>=2:
t.append(lijst[0]) t.append(lijst[0])
latlong.append(lijst[1]) latlong.append(lijst[1])
lijst = lijst[2:] lijst = lijst[2:]
return [np.array(t),np.array(latlong)] return [np.array(t),np.array(latlong)]
@@ -103,7 +109,6 @@ def imports_open(user,oauth_data):
oauth_data, oauth_data,
) )
elif tokenexpirydate is None and expirydatename is not None and 'strava' in expirydatename: elif tokenexpirydate is None and expirydatename is not None and 'strava' in expirydatename:
print 'noot'
token = imports_token_refresh( token = imports_token_refresh(
user, user,
tokenname, tokenname,
@@ -124,13 +129,13 @@ def imports_do_refresh_token(refreshtoken,oauth_data,access_token=''):
) )
post_data = {"grant_type": "refresh_token", post_data = {"grant_type": "refresh_token",
"client_secret": oauth_data['client_secret'], "client_secret": oauth_data['client_secret'],
"client_id": oauth_data['client_id'], "client_id": oauth_data['client_id'],
"refresh_token": refreshtoken, "refresh_token": refreshtoken,
} }
headers = {'user-agent': 'sanderroosendaal', headers = {'user-agent': 'sanderroosendaal',
'Accept': 'application/json', 'Accept': 'application/json',
'Content-Type': oauth_data['content_type']} 'Content-Type': oauth_data['content_type']}
# for Strava # for Strava
if 'grant_type' in oauth_data: if 'grant_type' in oauth_data:
@@ -147,11 +152,11 @@ def imports_do_refresh_token(refreshtoken,oauth_data,access_token=''):
if 'json' in oauth_data['content_type']: if 'json' in oauth_data['content_type']:
response = requests.post(baseurl, response = requests.post(baseurl,
data=json.dumps(post_data), data=json.dumps(post_data),
headers=headers) headers=headers)
else: else:
response = requests.post(baseurl, response = requests.post(baseurl,
data=post_data, data=post_data,
headers=headers) headers=headers)
@@ -174,9 +179,9 @@ def imports_do_refresh_token(refreshtoken,oauth_data,access_token=''):
except KeyError: except KeyError:
expires_in = 0 expires_in = 0
try: try:
refresh_token = token_json['refresh_token'] refresh_token = token_json['refresh_token']
except KeyError: except KeyError:
refresh_token = refreshtoken refresh_token = refreshtoken
try: try:
expires_in = int(expires_in) expires_in = int(expires_in)
except (TypeError,ValueError): except (TypeError,ValueError):
@@ -204,16 +209,16 @@ def imports_get_token(
post_data = {"grant_type": "authorization_code", post_data = {"grant_type": "authorization_code",
"code": code, "code": code,
"redirect_uri": redirect_uri, "redirect_uri": redirect_uri,
"client_secret": client_secret, "client_secret": client_secret,
"client_id": client_id, "client_id": client_id,
} }
try: try:
headers = oauth_data['headers'] headers = oauth_data['headers']
except KeyError: except KeyError:
headers = {'Accept': 'application/json', headers = {'Accept': 'application/json',
'Api-Key': client_id, 'Api-Key': client_id,
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'user-agent': 'sanderroosendaal'} 'user-agent': 'sanderroosendaal'}
if 'grant_type' in oauth_data: if 'grant_type' in oauth_data:
@@ -229,12 +234,12 @@ def imports_get_token(
response = requests.post( response = requests.post(
base_uri, base_uri,
data=json.dumps(post_data), data=json.dumps(post_data),
headers=headers) headers=headers)
else: else:
response = requests.post( response = requests.post(
base_uri, base_uri,
data=post_data, data=post_data,
headers=headers) headers=headers)
if response.status_code == 200 or response.status_code == 201: if response.status_code == 200 or response.status_code == 201:
token_json = response.json() token_json = response.json()
@@ -243,9 +248,9 @@ def imports_get_token(
except KeyError: except KeyError:
return [0,0,0] return [0,0,0]
try: try:
refresh_token = token_json['refresh_token'] refresh_token = token_json['refresh_token']
except KeyError: except KeyError:
refresh_token = '' refresh_token = ''
try: try:
expires_in = token_json['expires_in'] expires_in = token_json['expires_in']
except KeyError: except KeyError:
@@ -270,12 +275,12 @@ def imports_make_authorization_url(oauth_data):
params = {"client_id": oauth_data['client_id'], params = {"client_id": oauth_data['client_id'],
"response_type": "code", "response_type": "code",
"redirect_uri": oauth_data['redirect_uri'], "redirect_uri": oauth_data['redirect_uri'],
"scope":oauth_data['scope'], "scope":oauth_data['scope'],
"state":state} "state":state}
import urllib import urllib
url = oauth_data['authorizaton_uri']+urllib.urlencode(params) url = oauth_data['authorizaton_uri']+urllib.parse.urlencode(params)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
+906 -714
View File
File diff suppressed because it is too large Load Diff
+8 -3
View File
@@ -1,3 +1,8 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from __future__ import absolute_import from __future__ import absolute_import
import numpy as np import numpy as np
import time import time
@@ -72,7 +77,7 @@ def longtask2(aantal,jobid=None,debug=False,secret=''):
counter = 0 counter = 0
progress = int(100.*i/aantal) progress = int(100.*i/aantal)
if debug: if debug:
print progress print(progress)
if jobid != None: if jobid != None:
if debug: if debug:
url = SITE_URL_DEV url = SITE_URL_DEV
@@ -83,8 +88,8 @@ def longtask2(aantal,jobid=None,debug=False,secret=''):
post_data = {"secret":secret} post_data = {"secret":secret}
s = requests.post(url, data=post_data) s = requests.post(url, data=post_data)
if debug: if debug:
print url print(url)
print s print(s)
return 1 return 1
+6 -1
View File
@@ -1,3 +1,8 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
# Processes emails sent to workouts@rowsandall.com # Processes emails sent to workouts@rowsandall.com
""" Processes emails sent to workouts@rowsandall.com """ """ Processes emails sent to workouts@rowsandall.com """
import shutil import shutil
@@ -92,7 +97,7 @@ def make_new_workout_from_email(rower, datafile, name, cntr=0,testing=False):
fileformat = fileformat[2] fileformat = fileformat[2]
if testing: if testing:
print 'Fileformat = ',fileformat print('Fileformat = ',fileformat)
if fileformat == 'unknown': if fileformat == 'unknown':
# extension = datafilename[-4:].lower() # extension = datafilename[-4:].lower()
+19 -10
View File
@@ -1,13 +1,17 @@
#!/srv/venv/bin/python #!/srv/venv/bin/python
""" Process emails """ """ Process emails """
import sys import sys
import os import os
PY3K = sys.version_info >= (3, 0)
import zipfile import zipfile
import re import re
import time import time
from time import strftime from time import strftime
import io
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django_mailbox.models import Message, MessageAttachment,Mailbox from django_mailbox.models import Message, MessageAttachment,Mailbox
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
@@ -57,20 +61,20 @@ def processattachment(rower, fileobj, title, uploadoptions,testing=False):
except AttributeError: except AttributeError:
filename = fileobj[6:] filename = fileobj[6:]
if testing: if testing:
print 'Attribute Error', filename print('Attribute Error', filename)
# test if file exists and is not empty # test if file exists and is not empty
try: try:
with open('media/'+filename,'r') as fop: with io.open('media/'+filename,'rb') as fop:
line = fop.readline() line = fop.readline()
except (IOError, UnicodeEncodeError): except (IOError, UnicodeEncodeError):
if testing: if testing:
print 'IOError',filename,'media/'+filename print('IOError',filename,'media/'+filename)
return 0 return 0
if testing: if testing:
print 'Creating workout from email' print('Creating workout from email')
# set user # set user
if rower.user.is_staff and 'username' in uploadoptions: if rower.user.is_staff and 'username' in uploadoptions:
@@ -102,7 +106,7 @@ def processattachment(rower, fileobj, title, uploadoptions,testing=False):
pass pass
if testing: if testing:
print 'Workout id = {workoutid}'.format(workoutid=workoutid) print('Workout id = {workoutid}'.format(workoutid=workoutid))
if workoutid[0]: if workoutid[0]:
link = settings.SITE_URL+reverse( link = settings.SITE_URL+reverse(
@@ -164,6 +168,11 @@ def get_from_address(message):
except IndexError: except IndexError:
first_line = '' first_line = ''
try:
first_line = first_line.decode('utf-8')
except AttributeError:
pass
if "quiske" in first_line: if "quiske" in first_line:
match = re.search(r'[\w\.-]+@[\w\.-]+', first_line) match = re.search(r'[\w\.-]+@[\w\.-]+', first_line)
return match.group(0) return match.group(0)
@@ -242,17 +251,17 @@ class Command(BaseCommand):
testing=testing testing=testing
) )
except: except:
print "Bad ZIP file" print("Bad ZIP file")
print attachment.document.name print(attachment.document.name)
else: else:
# move attachment and make workout # move attachment and make workout
if testing: if testing:
try: try:
print name print(name)
except UnicodeEncodeError: except UnicodeEncodeError:
print "Unicode Error" print("Unicode Error")
try: try:
print attachment.document print(attachment.document)
except UnicodeEncodeError: except UnicodeEncodeError:
pass pass
+8 -2
View File
@@ -1,6 +1,12 @@
from utils import lbstoN from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from __future__ import absolute_import
from rowers.utils import lbstoN
import numpy as np import numpy as np
from models import C2WorldClassAgePerformance from rowers.models import C2WorldClassAgePerformance
import pandas as pd import pandas as pd
from scipy import optimize from scipy import optimize
from django.utils import timezone from django.utils import timezone
+1 -1
View File
@@ -2,7 +2,7 @@
from django.utils import timezone from django.utils import timezone
from rowers.models import Workout, PowerTimeFitnessMetric, Rower from rowers.models import Workout, PowerTimeFitnessMetric, Rower
import datetime import datetime
from utils import myqueue from rowers.utils import myqueue
import django_rq import django_rq
queue = django_rq.get_queue('default') queue = django_rq.get_queue('default')
from rowers.tasks import handle_updatefitnessmetric,handle_sendemail_expired from rowers.tasks import handle_updatefitnessmetric,handle_sendemail_expired
+276 -255
View File
File diff suppressed because it is too large Load Diff
+11 -5
View File
@@ -1,3 +1,9 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from six import iteritems
workouttypes = ( workouttypes = (
('water','Standard Racing Shell'), ('water','Standard Racing Shell'),
('rower','Indoor Rower'), ('rower','Indoor Rower'),
@@ -208,15 +214,15 @@ c2mapping = {
} }
c2mappinginv = {value:key for key,value in c2mapping.iteritems() if value is not None} c2mappinginv = {value:key for key,value in iteritems(c2mapping) if value is not None}
stravamappinginv = {value:key for key,value in stravamapping.iteritems() if value is not None} stravamappinginv = {value:key for key,value in iteritems(stravamapping) if value is not None}
stmappinginv = {value:key for key,value in stmapping.iteritems() if value is not None} stmappinginv = {value:key for key,value in iteritems(stmapping) if value is not None}
rkmappinginv = {value:key for key,value in rkmapping.iteritems() if value is not None} rkmappinginv = {value:key for key,value in iteritems(rkmapping) if value is not None}
polarmappinginv = {value:key for key,value in polarmapping.iteritems() if value is not None} polarmappinginv = {value:key for key,value in iteritems(polarmapping) if value is not None}
otwtypes = ( otwtypes = (
'water', 'water',
+5
View File
@@ -1,3 +1,8 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import struct import struct
import base64 import base64
+99 -94
View File
@@ -1,3 +1,8 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from __future__ import unicode_literals, absolute_import
# Interactions with Rowsandall.com API. Not fully complete. # Interactions with Rowsandall.com API. Not fully complete.
# Python # Python
@@ -15,7 +20,7 @@ import math
from math import sin,cos,atan2,sqrt from math import sin,cos,atan2,sqrt
import urllib import urllib
import c2stuff import rowers.c2stuff as c2stuff
# Django # Django
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
@@ -41,13 +46,13 @@ TEST_REDIRECT_URI = "http://localhost:8000/rowers/test_callback"
def custom_exception_handler(exc,message): def custom_exception_handler(exc,message):
response = { response = {
"errors": [ "errors": [
{ {
"code": str(exc), "code": str(exc),
"detail": message, "detail": message,
} }
] ]
} }
res = HttpResponse(message) res = HttpResponse(message)
res.status_code = 401 res.status_code = 401
@@ -58,27 +63,27 @@ def custom_exception_handler(exc,message):
def do_refresh_token(refreshtoken): def do_refresh_token(refreshtoken):
client_auth = requests.auth.HTTPBasicAuth(TEST_CLIENT_ID, TEST_CLIENT_SECRET) client_auth = requests.auth.HTTPBasicAuth(TEST_CLIENT_ID, TEST_CLIENT_SECRET)
post_data = {"grant_type": "refresh_token", post_data = {"grant_type": "refresh_token",
"client_secret": TEST_CLIENT_SECRET, "client_secret": TEST_CLIENT_SECRET,
"client_id":TEST_CLIENT_ID, "client_id":TEST_CLIENT_ID,
"refresh_token": refreshtoken, "refresh_token": refreshtoken,
} }
headers = {'user-agent': 'sanderroosendaal', headers = {'user-agent': 'sanderroosendaal',
'Accept': 'application/json', 'Accept': 'application/json',
'Content-Type': 'application/json'} 'Content-Type': 'application/json'}
url = "http://localhost:8000/rowers/o/token" url = "http://localhost:8000/rowers/o/token"
response = requests.post(url, response = requests.post(url,
data=json.dumps(post_data), data=json.dumps(post_data),
headers=headers) headers=headers)
token_json = response.json() token_json = response.json()
thetoken = token_json['access_token'] thetoken = token_json['access_token']
expires_in = token_json['expires_in'] expires_in = token_json['expires_in']
try: try:
refresh_token = token_json['refresh_token'] refresh_token = token_json['refresh_token']
except KeyError: except KeyError:
refresh_token = refreshtoken refresh_token = refreshtoken
return [thetoken,expires_in,refresh_token] return [thetoken,expires_in,refresh_token]
@@ -88,18 +93,18 @@ def get_token(code):
post_data = {"grant_type": "authorization_code", post_data = {"grant_type": "authorization_code",
"code": code, "code": code,
"redirect_uri": "http://localhost:8000/rowers/test_callback", "redirect_uri": "http://localhost:8000/rowers/test_callback",
"client_secret": "aapnootmies", "client_secret": "aapnootmies",
"client_id":1, "client_id":1,
} }
headers = {'Accept': 'application/json', headers = {'Accept': 'application/json',
'Content-Type': 'application/json'} 'Content-Type': 'application/json'}
url = "http://localhost:8000/rowers/o/token/" url = "http://localhost:8000/rowers/o/token/"
response = requests.post(url, response = requests.post(url,
data=json.dumps(post_data), data=json.dumps(post_data),
headers=headers) headers=headers)
token_json = response.json() token_json = response.json()
thetoken = token_json['access_token'] thetoken = token_json['access_token']
@@ -118,12 +123,12 @@ def make_authorization_url(request):
params = {"client_id": TEST_CLIENT_ID, params = {"client_id": TEST_CLIENT_ID,
"response_type": "code", "response_type": "code",
"redirect_uri": TEST_REDIRECT_URI, "redirect_uri": TEST_REDIRECT_URI,
"scope":"write", "scope":"write",
"state":state} "state":state}
import urllib import urllib
url = "http://localhost:8000/rowers/o/authorize" +urllib.urlencode(params) url = "http://localhost:8000/rowers/o/authorize" +urllib.parse.urlencode(params)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
@@ -147,19 +152,19 @@ def rower_ownapi_token_refresh(user):
def get_ownapi_workout_list(user): def get_ownapi_workout_list(user):
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
if (r.ownapitoken == '') or (r.ownapitoken is None): if (r.ownapitoken == '') or (r.ownapitoken is None):
s = "Token doesn't exist. Need to authorize" s = "Token doesn't exist. Need to authorize"
return custom_exception_handler(401,s) return custom_exception_handler(401,s)
elif (timezone.now()>r.ownapitokenexpirydate): elif (timezone.now()>r.ownapitokenexpirydate):
s = "Token expired. Needs to refresh." s = "Token expired. Needs to refresh."
return custom_exception_handler(401,s) return custom_exception_handler(401,s)
else: else:
# ready to fetch. Hurray # ready to fetch. Hurray
authorizationstring = str('Bearer ' + r.ownapitoken) authorizationstring = str('Bearer ' + r.ownapitoken)
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'} 'Content-Type': 'application/json'}
url = "https://api.ownapi.mobi/api/v2/fitnessActivities" url = "https://api.ownapi.mobi/api/v2/fitnessActivities"
s = requests.get(url,headers=headers) s = requests.get(url,headers=headers)
return s return s
@@ -167,19 +172,19 @@ def get_ownapi_workout_list(user):
def get_ownapi_workout(user,ownapiid): def get_ownapi_workout(user,ownapiid):
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
if (r.ownapitoken == '') or (r.ownapitoken is None): if (r.ownapitoken == '') or (r.ownapitoken is None):
return custom_exception_handler(401,s) return custom_exception_handler(401,s)
s = "Token doesn't exist. Need to authorize" s = "Token doesn't exist. Need to authorize"
elif (timezone.now()>r.ownapitokenexpirydate): elif (timezone.now()>r.ownapitokenexpirydate):
s = "Token expired. Needs to refresh." s = "Token expired. Needs to refresh."
return custom_exception_handler(401,s) return custom_exception_handler(401,s)
else: else:
# ready to fetch. Hurray # ready to fetch. Hurray
authorizationstring = str('Bearer ' + r.ownapitoken) authorizationstring = str('Bearer ' + r.ownapitoken)
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'} 'Content-Type': 'application/json'}
url = "https://api.ownapi.mobi/api/v2/fitnessActivities/"+str(ownapiid) url = "https://api.ownapi.mobi/api/v2/fitnessActivities/"+str(ownapiid)
s = requests.get(url,headers=headers) s = requests.get(url,headers=headers)
return s return s
@@ -203,16 +208,16 @@ def createownapiworkoutdata(w):
haslatlon=1 haslatlon=1
try: try:
lat = row.df[' latitude'].values lat = row.df[' latitude'].values
lon = row.df[' longitude'].values lon = row.df[' longitude'].values
except KeyError: except KeyError:
haslatlon = 0 haslatlon = 0
haspower = 1 haspower = 1
try: try:
power = row.df[' Power (watts)'].values power = row.df[' Power (watts)'].values
except KeyError: except KeyError:
haspower = 0 haspower = 0
locdata = [] locdata = []
hrdata = [] hrdata = []
@@ -221,54 +226,54 @@ def createownapiworkoutdata(w):
powerdata = [] powerdata = []
for i in range(len(t)): for i in range(len(t)):
hrdata.append(t[i]) hrdata.append(t[i])
hrdata.append(hr[i]) hrdata.append(hr[i])
distancedata.append(t[i]) distancedata.append(t[i])
distancedata.append(d[i]) distancedata.append(d[i])
spmdata.append(t[i]) spmdata.append(t[i])
spmdata.append(spm[i]) spmdata.append(spm[i])
if haslatlon: if haslatlon:
locdata.append(t[i]) locdata.append(t[i])
locdata.append([lat[i],lon[i]]) locdata.append([lat[i],lon[i]])
if haspower: if haspower:
powerdata.append(t[i]) powerdata.append(t[i])
powerdata.append(power[i]) powerdata.append(power[i])
if haslatlon: if haslatlon:
data = { data = {
"type": "Rowing", "type": "Rowing",
"name": w.name, "name": w.name,
# "start_time": str(w.date)+"T"+str(w.starttime)+"Z", # "start_time": str(w.date)+"T"+str(w.starttime)+"Z",
"start_time": w.startdatetime.isoformat(), "start_time": w.startdatetime.isoformat(),
"total_distance": int(w.distance), "total_distance": int(w.distance),
"duration": int(max(t)), "duration": int(max(t)),
"notes": w.notes, "notes": w.notes,
"avg_heartrate": averagehr, "avg_heartrate": averagehr,
"max_heartrate": maxhr, "max_heartrate": maxhr,
"location": locdata, "location": locdata,
"distance": distancedata, "distance": distancedata,
"cadence": spmdata, "cadence": spmdata,
"heartrate": hrdata, "heartrate": hrdata,
} }
else: else:
data = { data = {
"type": "Rowing", "type": "Rowing",
"name": w.name, "name": w.name,
# "start_time": str(w.date)+"T"+str(w.starttime)+"Z", # "start_time": str(w.date)+"T"+str(w.starttime)+"Z",
"start_time": w.startdatetime.isoformat(), "start_time": w.startdatetime.isoformat(),
"total_distance": int(w.distance), "total_distance": int(w.distance),
"duration": int(max(t)), "duration": int(max(t)),
"notes": w.notes, "notes": w.notes,
"avg_heartrate": averagehr, "avg_heartrate": averagehr,
"max_heartrate": maxhr, "max_heartrate": maxhr,
"distance": distancedata, "distance": distancedata,
"cadence": spmdata, "cadence": spmdata,
"heartrate": hrdata, "heartrate": hrdata,
} }
if haspower: if haspower:
data['power'] = powerdata data['power'] = powerdata
return data return data
+4
View File
@@ -1,3 +1,7 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from rowers.models import Rower,PaidPlan from rowers.models import Rower,PaidPlan
# run once - copies plans to paypal # run once - copies plans to paypal
+4
View File
@@ -1,3 +1,7 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
# Defines permissions for objects (API related) # Defines permissions for objects (API related)
from rest_framework import permissions from rest_framework import permissions
+10 -5
View File
@@ -1,3 +1,8 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
# Python # Python
from django.utils import timezone from django.utils import timezone
from datetime import datetime from datetime import datetime
@@ -10,7 +15,7 @@ import uuid
from django.conf import settings from django.conf import settings
import pytz import pytz
from dateutil import parser from dateutil import parser
from utils import myqueue,calculate_age,totaltime_sec_to_string from rowers.utils import myqueue,calculate_age,totaltime_sec_to_string
import re import re
import django_rq import django_rq
queue = django_rq.get_queue('default') queue = django_rq.get_queue('default')
@@ -30,10 +35,10 @@ from rowers.emails import htmlstrip,htmlstripnobr
import rowers.mytypes as mytypes import rowers.mytypes as mytypes
import metrics import rowers.metrics as metrics
import numpy as np import numpy as np
import dataprep import rowers.dataprep as dataprep
import courses import rowers.courses as courses
import iso8601 import iso8601
from iso8601 import ParseError from iso8601 import ParseError
from rowers.tasks import handle_check_race_course from rowers.tasks import handle_check_race_course
@@ -360,7 +365,7 @@ def is_session_complete_ws(ws,ps):
return ratio,'partial',completiondate return ratio,'partial',completiondate
else: else:
thevalue = 0 thevalue = 0
for key,value in cratios.iteritems(): for key,value in cratios.items():
if ratio>value and value>thevalue: if ratio>value and value>thevalue:
verdict = key verdict = key
thevalue = value thevalue = value
+24 -19
View File
@@ -1,15 +1,20 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from matplotlib.ticker import MultipleLocator,FuncFormatter,NullFormatter from matplotlib.ticker import MultipleLocator,FuncFormatter,NullFormatter
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import numpy as np import numpy as np
from rows import format_pace_tick, format_pace, format_time, format_time_tick from rowers.rows import format_pace_tick, format_pace, format_time, format_time_tick
# Formatting the distance tick marks # Formatting the distance tick marks
#def format_dist_tick(x,pos=None): #def format_dist_tick(x,pos=None):
# km = x/1000. # km = x/1000.
# template='%6.3f' # template='%6.3f'
# return template % (km) # return template % (km)
# Utility to select reasonable y axis range # Utility to select reasonable y axis range
@@ -32,23 +37,23 @@ def y_axis_range(ydata,miny=0,padding=.1,ultimate=[-1e9,1e9]):
if (yrange == 0): if (yrange == 0):
if ymin == 0: if ymin == 0:
yrangemin = -padding yrangemin = -padding
else: else:
yrangemin = ymin-ymin*padding yrangemin = ymin-ymin*padding
if ymax == 0: if ymax == 0:
yrangemax = padding yrangemax = padding
else: else:
yrangemax = ymax+ymax*padding yrangemax = ymax+ymax*padding
else: else:
yrangemin = ymin-padding*yrange yrangemin = ymin-padding*yrange
yrangemax = ymax+padding*yrange yrangemax = ymax+padding*yrange
if (yrangemin < ultimate[0]): if (yrangemin < ultimate[0]):
yrangemin = ultimate[0] yrangemin = ultimate[0]
if (yrangemax > ultimate[1]): if (yrangemax > ultimate[1]):
yrangemax = ultimate[1] yrangemax = ultimate[1]
@@ -70,7 +75,7 @@ def mkplot(row,title):
ax1.set_ylabel('(sec/500)') ax1.set_ylabel('(sec/500)')
yrange = y_axis_range(df.loc[:,' Stroke500mPace (sec/500m)'], yrange = y_axis_range(df.loc[:,' Stroke500mPace (sec/500m)'],
ultimate = [85,190]) ultimate = [85,190])
plt.axis([0,end_time,yrange[1],yrange[0]]) plt.axis([0,end_time,yrange[1],yrange[0]])
ax1.set_xticks(range(1000,end_time,1000)) ax1.set_xticks(range(1000,end_time,1000))
@@ -84,7 +89,7 @@ def mkplot(row,title):
ax1.yaxis.set_major_formatter(majorFormatter) ax1.yaxis.set_major_formatter(majorFormatter)
for tl in ax1.get_yticklabels(): for tl in ax1.get_yticklabels():
tl.set_color('b') tl.set_color('b')
ax2 = ax1.twinx() ax2 = ax1.twinx()
ax2.plot(t,hr,'r-') ax2.plot(t,hr,'r-')
@@ -94,7 +99,7 @@ def mkplot(row,title):
ax2.xaxis.set_major_formatter(majorTimeFormatter) ax2.xaxis.set_major_formatter(majorTimeFormatter)
ax2.patch.set_alpha(0.0) ax2.patch.set_alpha(0.0)
for tl in ax2.get_yticklabels(): for tl in ax2.get_yticklabels():
tl.set_color('r') tl.set_color('r')
plt.subplots_adjust(hspace=0) plt.subplots_adjust(hspace=0)
+21 -11
View File
@@ -1,3 +1,9 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from __future__ import unicode_literals, absolute_import
# All the functionality needed to connect to Strava # All the functionality needed to connect to Strava
# Python # Python
@@ -33,8 +39,8 @@ from rowingdata import rowingdata
import pandas as pd import pandas as pd
from rowers.models import Rower,Workout from rowers.models import Rower,Workout
from rowers.models import checkworkoutuser from rowers.models import checkworkoutuser
import dataprep import rowers.dataprep as dataprep
from dataprep import columndict from rowers.dataprep import columndict
from io import StringIO from io import StringIO
@@ -51,8 +57,8 @@ from rowsandall_app.settings import (
baseurl = 'https://polaraccesslink.com/v3' baseurl = 'https://polaraccesslink.com/v3'
from utils import NoTokenError, custom_exception_handler from rowers.utils import NoTokenError, custom_exception_handler
import mytypes import rowers.mytypes as mytypes
# Exchange access code for long-lived access token # Exchange access code for long-lived access token
def get_token(code): def get_token(code):
@@ -67,7 +73,10 @@ def get_token(code):
secret=POLAR_CLIENT_SECRET secret=POLAR_CLIENT_SECRET
) )
headers = { 'Authorization': 'Basic %s' % base64.b64encode(auth_string) } try:
headers = { 'Authorization': 'Basic %s' % base64.b64encode(auth_string) }
except TypeError:
headers = { 'Authorization': 'Basic %s' % base64.b64encode(bytes(auth_string,'utf-8')) }
response = requests.post("https://polarremote.com/v2/oauth2/token", response = requests.post("https://polarremote.com/v2/oauth2/token",
data=post_data, data=post_data,
@@ -97,7 +106,7 @@ def make_authorization_url():
"redirect_uri": POLAR_REDIRECT_URI, "redirect_uri": POLAR_REDIRECT_URI,
"scope":"write"} "scope":"write"}
import urllib import urllib
url = "https://flow.polar.com/oauth2/authorization" +urllib.urlencode(params) url = "https://flow.polar.com/oauth2/authorization" +urllib.parse.urlencode(params)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
@@ -110,7 +119,10 @@ def get_polar_notifications():
secret=POLAR_CLIENT_SECRET secret=POLAR_CLIENT_SECRET
) )
headers = { 'Authorization': 'Basic %s' % base64.b64encode(auth_string) } try:
headers = { 'Authorization': 'Basic %s' % base64.b64encode(auth_string) }
except TypeError:
headers = { 'Authorization': 'Basic %s' % base64.b64encode(bytes(auth_string,'utf-8')) }
response = requests.get(url, headers=headers) response = requests.get(url, headers=headers)
@@ -126,7 +138,7 @@ from rowers.utils import isprorower
def get_all_new_workouts(available_data,testing=False): def get_all_new_workouts(available_data,testing=False):
for record in available_data: for record in available_data:
if testing: if testing:
print record print(record)
if record['data-type'] == 'EXERCISE': if record['data-type'] == 'EXERCISE':
try: try:
r = Rower.objects.get(polaruserid=record['user-id']) r = Rower.objects.get(polaruserid=record['user-id'])
@@ -134,7 +146,7 @@ def get_all_new_workouts(available_data,testing=False):
if r.polar_auto_import and isprorower(r): if r.polar_auto_import and isprorower(r):
exercise_list = get_polar_workouts(u) exercise_list = get_polar_workouts(u)
if testing: if testing:
print exercise_list print(exercise_list)
except Rower.DoesNotExist: except Rower.DoesNotExist:
pass pass
@@ -313,8 +325,6 @@ def get_polar_workout(user,id,transactionid):
response = requests.get(url,headers = headers2) response = requests.get(url,headers = headers2)
if response.status_code == 200: if response.status_code == 200:
print response.text
result = response.text result = response.text
# commit transaction # commit transaction
response = requests.put(url,headers=headers) response = requests.put(url,headers=headers)
+26 -21
View File
@@ -1,3 +1,8 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import time import time
import gzip import gzip
import shutil import shutil
@@ -6,29 +11,29 @@ from django.core.exceptions import ValidationError
def format_pace_tick(x,pos=None): def format_pace_tick(x,pos=None):
minu=int(x/60) minu=int(x/60)
sec=int(x-minu*60.) sec=int(x-minu*60.)
sec_str=str(sec).zfill(2) sec_str=str(sec).zfill(2)
template='%d:%s' template='%d:%s'
return template % (minu,sec_str) return template % (minu,sec_str)
def format_time_tick(x,pos=None): def format_time_tick(x,pos=None):
hour=int(x/3600) hour=int(x/3600)
min=int((x-hour*3600.)/60) min=int((x-hour*3600.)/60)
min_str=str(min).zfill(2) min_str=str(min).zfill(2)
template='%d:%s' template='%d:%s'
return template % (hour,min_str) return template % (hour,min_str)
def format_pace(x,pos=None): def format_pace(x,pos=None):
if isinf(x) or isnan(x): if isinf(x) or isnan(x):
x=0 x=0
min=int(x/60) min=int(x/60)
sec=(x-min*60.) sec=(x-min*60.)
str1 = "{min:0>2}:{sec:0>4.1f}".format( str1 = "{min:0>2}:{sec:0>4.1f}".format(
min = min, min = min,
sec = sec sec = sec
) )
return str1 return str1
@@ -39,9 +44,9 @@ def format_time(x,pos=None):
sec = int(x-min*60) sec = int(x-min*60)
str1 = "{min:0>2}:{sec:0>4.1f}".format( str1 = "{min:0>2}:{sec:0>4.1f}".format(
min=min, min=min,
sec=sec, sec=sec,
) )
return str1 return str1
@@ -67,14 +72,14 @@ def must_be_csv(value):
ext = os.path.splitext(value.name)[1] ext = os.path.splitext(value.name)[1]
valid_extensions = ['.csv','.CSV'] valid_extensions = ['.csv','.CSV']
if not ext in valid_extensions: if not ext in valid_extensions:
raise ValidationError(u'File not supported!') raise ValidationError(u'File not supported!')
def validate_kml(value): def validate_kml(value):
import os import os
ext = os.path.splitext(value.name)[1] ext = os.path.splitext(value.name)[1]
valid_extensions = ['.kml','.KML'] valid_extensions = ['.kml','.KML']
if not ext in valid_extensions: if not ext in valid_extensions:
raise ValidationError(u'File not supported!') raise ValidationError(u'File not supported!')
def handle_uploaded_image(i): def handle_uploaded_image(i):
@@ -83,7 +88,7 @@ def handle_uploaded_image(i):
import os import os
from django.core.files import File from django.core.files import File
image_str = "" image_str = ""
for chunk in i.chunks(): for chunk in i.chunks():
image_str += chunk image_str += chunk
imagefile = StringIO.StringIO(image_str) imagefile = StringIO.StringIO(image_str)
@@ -133,8 +138,8 @@ def handle_uploaded_file(f):
fname = timestr+'-'+fname fname = timestr+'-'+fname
fname2 = 'media/'+fname fname2 = 'media/'+fname
with open(fname2,'wb+') as destination: with open(fname2,'wb+') as destination:
for chunk in f.chunks(): for chunk in f.chunks():
destination.write(chunk) destination.write(chunk)
return fname,fname2 return fname,fname2
+119 -108
View File
@@ -1,3 +1,8 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from __future__ import unicode_literals, absolute_import
# All the functionality needed to connect to Runkeeper # All the functionality needed to connect to Runkeeper
from rowers.imports import * from rowers.imports import *
import re import re
@@ -24,6 +29,8 @@ oauth_data = {
} }
import numpy
def splitrunkeeperlatlongdata(lijst,tname,latname,lonname): def splitrunkeeperlatlongdata(lijst,tname,latname,lonname):
t = [] t = []
lat = [] lat = []
@@ -61,16 +68,16 @@ def make_authorization_url(request):
def get_runkeeper_workout_list(user): def get_runkeeper_workout_list(user):
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
if (r.runkeepertoken == '') or (r.runkeepertoken is None): if (r.runkeepertoken == '') or (r.runkeepertoken is None):
s = "Token doesn't exist. Need to authorize" s = "Token doesn't exist. Need to authorize"
return custom_exception_handler(401,s) return custom_exception_handler(401,s)
else: else:
# ready to fetch. Hurray # ready to fetch. Hurray
authorizationstring = str('Bearer ' + r.runkeepertoken) authorizationstring = str('Bearer ' + r.runkeepertoken)
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'} 'Content-Type': 'application/json'}
url = "https://api.runkeeper.com/fitnessActivities" url = "https://api.runkeeper.com/fitnessActivities"
s = requests.get(url,headers=headers) s = requests.get(url,headers=headers)
return s return s
@@ -78,16 +85,16 @@ def get_runkeeper_workout_list(user):
def get_workout(user,runkeeperid): def get_workout(user,runkeeperid):
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
if (r.runkeepertoken == '') or (r.runkeepertoken is None): if (r.runkeepertoken == '') or (r.runkeepertoken is None):
return custom_exception_handler(401,s) return custom_exception_handler(401,s)
s = "Token doesn't exist. Need to authorize" s = "Token doesn't exist. Need to authorize"
else: else:
# ready to fetch. Hurray # ready to fetch. Hurray
authorizationstring = str('Bearer ' + r.runkeepertoken) authorizationstring = str('Bearer ' + r.runkeepertoken)
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'} 'Content-Type': 'application/json'}
url = "https://api.runkeeper.com/fitnessActivities/"+str(runkeeperid) url = "https://api.runkeeper.com/fitnessActivities/"+str(runkeeperid)
s = requests.get(url,headers=headers) s = requests.get(url,headers=headers)
try: try:
data = s.json() data = s.json()
@@ -131,12 +138,12 @@ def createrunkeeperworkoutdata(w):
haslatlon=1 haslatlon=1
try: try:
lat = row.df[' latitude'].values lat = row.df[' latitude'].values
lon = row.df[' longitude'].values lon = row.df[' longitude'].values
if not lat.std() and not lon.std(): if not lat.std() and not lon.std():
haslatlon = 0 haslatlon = 0
except KeyError: except KeyError:
haslatlon = 0 haslatlon = 0
# path data # path data
if haslatlon: if haslatlon:
@@ -172,32 +179,32 @@ def createrunkeeperworkoutdata(w):
newnotes = 'from '+w.workoutsource+' via rowsandall.com' newnotes = 'from '+w.workoutsource+' via rowsandall.com'
if haslatlon: if haslatlon:
data = { data = {
"type": "Rowing", "type": "Rowing",
"start_time": start_time, "start_time": start_time,
"total_distance": int(w.distance), "total_distance": int(w.distance),
"duration": duration, "duration": duration,
"notes": newnotes, "notes": newnotes,
"average_heart_rate": averagehr, "average_heart_rate": averagehr,
"path": locdata, "path": locdata,
"distance": distancedata, "distance": distancedata,
"heart_rate": hrdata, "heart_rate": hrdata,
"post_to_twitter":"false", "post_to_twitter":"false",
"post_to_facebook":"false", "post_to_facebook":"false",
} }
else: else:
data = { data = {
"type": "Rowing", "type": "Rowing",
"start_time": start_time, "start_time": start_time,
"total_distance": int(w.distance), "total_distance": int(w.distance),
"duration": duration, "duration": duration,
"notes": newnotes, "notes": newnotes,
"avg_heartrate": averagehr, "avg_heartrate": averagehr,
"distance": distancedata, "distance": distancedata,
"heart_rate": hrdata, "heart_rate": hrdata,
"post_to_twitter":"false", "post_to_twitter":"false",
"post_to_facebook":"false", "post_to_facebook":"false",
} }
return data return data
@@ -215,8 +222,8 @@ def getidfromresponse(response):
def geturifromid(access_token,id): def geturifromid(access_token,id):
authorizationstring = str('Bearer ' + access_token) authorizationstring = str('Bearer ' + access_token)
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'} 'Content-Type': 'application/json'}
import urllib import urllib
url = "https://api.runkeeper.com/fitnessActivities/"+str(id) url = "https://api.runkeeper.com/fitnessActivities/"+str(id)
response = requests.get(url,headers=headers) response = requests.get(url,headers=headers)
@@ -238,8 +245,8 @@ def geturifromid(access_token,id):
def get_userid(access_token): def get_userid(access_token):
authorizationstring = str('Bearer ' + access_token) authorizationstring = str('Bearer ' + access_token)
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'} 'Content-Type': 'application/json'}
import urllib import urllib
url = "https://api.runkeeper.com/user" url = "https://api.runkeeper.com/user"
response = requests.get(url,headers=headers) response = requests.get(url,headers=headers)
@@ -257,6 +264,10 @@ def get_userid(access_token):
return str(res) return str(res)
def default(o):
if isinstance(o, numpy.int64): return int(o)
raise TypeError
def workout_runkeeper_upload(user,w): def workout_runkeeper_upload(user,w):
message = "Uploading to Runkeeper" message = "Uploading to Runkeeper"
rkid = 0 rkid = 0
@@ -269,42 +280,42 @@ def workout_runkeeper_upload(user,w):
# ready to upload. Hurray # ready to upload. Hurray
if (checkworkoutuser(user,w)): if (checkworkoutuser(user,w)):
data = createrunkeeperworkoutdata(w) data = createrunkeeperworkoutdata(w)
if not data: if not data:
message = "Data error in Runkeeper Upload" message = "Data error in Runkeeper Upload"
rkid = 0 rkid = 0
return message, rkid return message, rkid
authorizationstring = str('Bearer ' + thetoken) authorizationstring = str('Bearer ' + thetoken)
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'Content-Type': 'application/vnd.com.runkeeper.NewFitnessActivity+json', 'Content-Type': 'application/vnd.com.runkeeper.NewFitnessActivity+json',
'Content-Length':'nnn'} 'Content-Length':'nnn'}
url = "https://api.runkeeper.com/fitnessActivities" url = "https://api.runkeeper.com/fitnessActivities"
response = requests.post(url,headers=headers,data=json.dumps(data)) response = requests.post(url,headers=headers,data=json.dumps(data,default=default))
# check for duplicate error first # check for duplicate error first
if (response.status_code == 409 ): if (response.status_code == 409 ):
message = "Duplicate error" message = "Duplicate error"
w.uploadedtorunkeeper = -1 w.uploadedtorunkeeper = -1
rkid = -1 rkid = -1
w.save() w.save()
return message, rkid return message, rkid
elif (response.status_code == 201 or response.status_code==200): elif (response.status_code == 201 or response.status_code==200):
rkid = getidfromresponse(response) rkid = getidfromresponse(response)
rkuri = geturifromid(thetoken,rkid) rkuri = geturifromid(thetoken,rkid)
w.uploadedtorunkeeper = rkid w.uploadedtorunkeeper = rkid
w.save() w.save()
return 'Successfully synchronized to Runkeeper',rkid return 'Successfully synchronized to Runkeeper',rkid
else: else:
s = response s = response
message = "Something went wrong in workout_runkeeper_upload_view: %s - %s" % (s.reason,s.text) message = "Something went wrong in workout_runkeeper_upload_view: %s - %s" % (s.reason,s.text)
rkid = 0 rkid = 0
return message, rkid return message, rkid
else: else:
message = "You are not authorized to upload this workout" message = "You are not authorized to upload this workout"
rkid = 0 rkid = 0
return message, rkid return message, rkid
@@ -316,41 +327,41 @@ def add_workout_from_data(user,importid,data,strokedata,source='runkeeper',
# To Do - add utcoffset to time # To Do - add utcoffset to time
workouttype = data['type'] workouttype = data['type']
if workouttype not in [x[0] for x in Workout.workouttypes]: if workouttype not in [x[0] for x in Workout.workouttypes]:
workouttype = 'other' workouttype = 'other'
try: try:
comments = data['notes'] comments = data['notes']
except: except:
comments = '' comments = ''
try: try:
utcoffset = tz(data['utcoffset']) utcoffset = tz(data['utcoffset'])
except: except:
utcoffset = 0 utcoffset = 0
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
try: try:
rowdatetime = iso8601.parse_date(data['start_time']) rowdatetime = iso8601.parse_date(data['start_time'])
except iso8601.ParseError: except iso8601.ParseError:
try: try:
rowdatetime = datetime.strptime(data['start_time'],"%Y-%m-%d %H:%M:%S") rowdatetime = datetime.strptime(data['start_time'],"%Y-%m-%d %H:%M:%S")
rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc) rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
except ValueError: except ValueError:
try: try:
rowdatetime = parser.parse(data['start_time']) rowdatetime = parser.parse(data['start_time'])
#rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc) #rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
except: except:
rowdatetime = datetime.strptime(data['date'],"%Y-%m-%d %H:%M:%S") rowdatetime = datetime.strptime(data['date'],"%Y-%m-%d %H:%M:%S")
rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc) rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
starttimeunix = arrow.get(rowdatetime).timestamp starttimeunix = arrow.get(rowdatetime).timestamp
#starttimeunix = mktime(rowdatetime.utctimetuple()) #starttimeunix = mktime(rowdatetime.utctimetuple())
starttimeunix += utcoffset*3600 starttimeunix += utcoffset*3600
try: try:
title = data['name'] title = data['name']
except: except:
title = "Imported data" title = "Imported data"
@@ -360,35 +371,35 @@ def add_workout_from_data(user,importid,data,strokedata,source='runkeeper',
times_distance = res[0] times_distance = res[0]
try: try:
l = data['path'] l = data['path']
res = splitrunkeeperlatlongdata(l,'timestamp','latitude','longitude') res = splitrunkeeperlatlongdata(l,'timestamp','latitude','longitude')
times_location = res[0] times_location = res[0]
latcoord = res[1] latcoord = res[1]
loncoord = res[2] loncoord = res[2]
except: except:
times_location = times_distance times_location = times_distance
latcoord = np.zeros(len(times_distance)) latcoord = np.zeros(len(times_distance))
loncoord = np.zeros(len(times_distance)) loncoord = np.zeros(len(times_distance))
if workouttype in types.otwtypes: if workouttype in types.otwtypes:
workouttype = 'rower' workouttype = 'rower'
try: try:
res = splitrunkeeperdata(data['cadence'],'timestamp','cadence') res = splitrunkeeperdata(data['cadence'],'timestamp','cadence')
times_spm = res[0] times_spm = res[0]
spm = res[1] spm = res[1]
except KeyError: except KeyError:
times_spm = times_distance times_spm = times_distance
spm = 0*times_distance spm = 0*times_distance
try: try:
res = splitrunkeeperdata(data['heart_rate'],'timestamp','heart_rate') res = splitrunkeeperdata(data['heart_rate'],'timestamp','heart_rate')
hr = res[1] hr = res[1]
times_hr = res[0] times_hr = res[0]
except KeyError: except KeyError:
times_hr = times_distance times_hr = times_distance
hr = 0*times_distance hr = 0*times_distance
# create data series and remove duplicates # create data series and remove duplicates
@@ -417,12 +428,12 @@ def add_workout_from_data(user,importid,data,strokedata,source='runkeeper',
# Create dicts and big dataframe # Create dicts and big dataframe
d = { d = {
' Horizontal (meters)': distseries, ' Horizontal (meters)': distseries,
' latitude': latseries, ' latitude': latseries,
' longitude': lonseries, ' longitude': lonseries,
' Cadence (stokes/min)': spmseries, ' Cadence (stokes/min)': spmseries,
' HRCur (bpm)' : hrseries, ' HRCur (bpm)' : hrseries,
} }
+19 -15
View File
@@ -1,3 +1,8 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
# Serializers. Defines which fields from an object get to the JSON object # Serializers. Defines which fields from an object get to the JSON object
# Also optionally define POST, PATCH methods (create, update) # Also optionally define POST, PATCH methods (create, update)
@@ -74,20 +79,20 @@ class WorkoutSerializer(serializers.ModelSerializer):
t.second) t.second)
w = Workout(user=r, w = Workout(user=r,
name=validated_data['name'], name=validated_data['name'],
date=validated_data['date'], date=validated_data['date'],
workouttype=validated_data['workouttype'], workouttype=validated_data['workouttype'],
duration=validated_data['duration'], duration=validated_data['duration'],
distance=validated_data['distance'], distance=validated_data['distance'],
weightcategory=r.weightcategory, weightcategory=r.weightcategory,
adaptiveclass=r.adaptiveclass, adaptiveclass=r.adaptiveclass,
starttime=validated_data['starttime'], starttime=validated_data['starttime'],
csvfilename='', csvfilename='',
notes=validated_data['notes'], notes=validated_data['notes'],
uploadedtoc2=0, uploadedtoc2=0,
summary=validated_data['summary'], summary=validated_data['summary'],
averagehr=validated_data['averagehr'], averagehr=validated_data['averagehr'],
maxhr=validated_data['maxhr'], maxhr=validated_data['maxhr'],
startdatetime=rowdatetime, startdatetime=rowdatetime,
timezone=validated_data['timezone'], timezone=validated_data['timezone'],
forceunit=validated_data['forceunit'], forceunit=validated_data['forceunit'],
inboard=validated_data['inboard'], inboard=validated_data['inboard'],
@@ -109,16 +114,16 @@ class WorkoutSerializer(serializers.ModelSerializer):
instance.name=validated_data['name'] instance.name=validated_data['name']
instance.date=validated_data['date'] instance.date=validated_data['date']
instance.workouttype=validated_data['workouttype'] instance.workouttype=validated_data['workouttype']
instance.duration=validated_data['duration'] instance.duration=validated_data['duration']
instance.distance=validated_data['distance'] instance.distance=validated_data['distance']
instance.starttime=validated_data['starttime'] instance.starttime=validated_data['starttime']
instance.notes=validated_data['notes'] instance.notes=validated_data['notes']
instance.summary=validated_data['summary'] instance.summary=validated_data['summary']
instance.averagehr=validated_data['averagehr'] instance.averagehr=validated_data['averagehr']
instance.maxhr=validated_data['maxhr'] instance.maxhr=validated_data['maxhr']
instance.startdatetime=rowdatetime instance.startdatetime=rowdatetime
instance.timezone=validated_data['timezone'] instance.timezone=validated_data['timezone']
instance.forceunit=validated_data['forceunit'] instance.forceunit=validated_data['forceunit']
instance.inboard=validated_data['inboard'] instance.inboard=validated_data['inboard']
@@ -140,7 +145,6 @@ class StrokeDataSerializer(serializers.Serializer):
""" """
# do something # do something
print "fake serializer"
return 1 return 1
+148 -135
View File
@@ -1,5 +1,13 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from __future__ import unicode_literals, absolute_import
# All the functionality to connect to SportTracks # All the functionality to connect to SportTracks
import numpy
from rowers.imports import * from rowers.imports import *
import re import re
from rowsandall_app.settings import ( from rowsandall_app.settings import (
@@ -9,7 +17,7 @@ from rowsandall_app.settings import (
SPORTTRACKS_REDIRECT_URI SPORTTRACKS_REDIRECT_URI
) )
import mytypes import rowers.mytypes as mytypes
oauth_data = { oauth_data = {
'client_id': SPORTTRACKS_CLIENT_ID, 'client_id': SPORTTRACKS_CLIENT_ID,
@@ -64,19 +72,19 @@ def rower_sporttracks_token_refresh(user):
def get_sporttracks_workout_list(user): def get_sporttracks_workout_list(user):
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
if (r.sporttrackstoken == '') or (r.sporttrackstoken is None): if (r.sporttrackstoken == '') or (r.sporttrackstoken is None):
s = "Token doesn't exist. Need to authorize" s = "Token doesn't exist. Need to authorize"
return custom_exception_handler(401,s) return custom_exception_handler(401,s)
elif (timezone.now()>r.sporttrackstokenexpirydate): elif (timezone.now()>r.sporttrackstokenexpirydate):
s = "Token expired. Needs to refresh." s = "Token expired. Needs to refresh."
return custom_exception_handler(401,s) return custom_exception_handler(401,s)
else: else:
# ready to fetch. Hurray # ready to fetch. Hurray
authorizationstring = str('Bearer ' + r.sporttrackstoken) authorizationstring = str('Bearer ' + r.sporttrackstoken)
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'} 'Content-Type': 'application/json'}
url = "https://api.sporttracks.mobi/api/v2/fitnessActivities" url = "https://api.sporttracks.mobi/api/v2/fitnessActivities"
s = requests.get(url,headers=headers) s = requests.get(url,headers=headers)
return s return s
@@ -84,19 +92,19 @@ def get_sporttracks_workout_list(user):
def get_workout(user,sporttracksid): def get_workout(user,sporttracksid):
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
if (r.sporttrackstoken == '') or (r.sporttrackstoken is None): if (r.sporttrackstoken == '') or (r.sporttrackstoken is None):
return custom_exception_handler(401,s) return custom_exception_handler(401,s)
s = "Token doesn't exist. Need to authorize" s = "Token doesn't exist. Need to authorize"
elif (timezone.now()>r.sporttrackstokenexpirydate): elif (timezone.now()>r.sporttrackstokenexpirydate):
s = "Token expired. Needs to refresh." s = "Token expired. Needs to refresh."
return custom_exception_handler(401,s) return custom_exception_handler(401,s)
else: else:
# ready to fetch. Hurray # ready to fetch. Hurray
authorizationstring = str('Bearer ' + r.sporttrackstoken) authorizationstring = str('Bearer ' + r.sporttrackstoken)
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'} 'Content-Type': 'application/json'}
url = "https://api.sporttracks.mobi/api/v2/fitnessActivities/"+str(sporttracksid) url = "https://api.sporttracks.mobi/api/v2/fitnessActivities/"+str(sporttracksid)
s = requests.get(url,headers=headers) s = requests.get(url,headers=headers)
data = s.json() data = s.json()
@@ -144,19 +152,19 @@ def createsporttracksworkoutdata(w):
haslatlon=1 haslatlon=1
try: try:
lat = row.df[' latitude'].values lat = row.df[' latitude'].values
lon = row.df[' longitude'].values lon = row.df[' longitude'].values
if not lat.std() and not lon.std(): if not lat.std() and not lon.std():
haslatlon = 0 haslatlon = 0
except KeyError: except KeyError:
haslatlon = 0 haslatlon = 0
haspower = 1 haspower = 1
try: try:
power = row.df[' Power (watts)'].astype(int).values power = row.df[' Power (watts)'].astype(int).values
except KeyError: except KeyError:
haspower = 0 haspower = 0
locdata = [] locdata = []
hrdata = [] hrdata = []
@@ -165,18 +173,18 @@ def createsporttracksworkoutdata(w):
powerdata = [] powerdata = []
for i in range(len(t)): for i in range(len(t)):
hrdata.append(t[i]) hrdata.append(t[i])
hrdata.append(hr[i]) hrdata.append(hr[i])
distancedata.append(t[i]) distancedata.append(t[i])
distancedata.append(d[i]) distancedata.append(d[i])
spmdata.append(t[i]) spmdata.append(t[i])
spmdata.append(spm[i]) spmdata.append(spm[i])
if haslatlon: if haslatlon:
locdata.append(t[i]) locdata.append(t[i])
locdata.append([lat[i],lon[i]]) locdata.append([lat[i],lon[i]])
if haspower: if haspower:
powerdata.append(t[i]) powerdata.append(t[i])
powerdata.append(power[i]) powerdata.append(power[i])
try: try:
@@ -188,37 +196,37 @@ def createsporttracksworkoutdata(w):
st = st.replace(microsecond=0) st = st.replace(microsecond=0)
if haslatlon: if haslatlon:
data = { data = {
"type": "Rowing", "type": "Rowing",
"name": w.name, "name": w.name,
"start_time": st.isoformat(), "start_time": st.isoformat(),
"total_distance": int(w.distance), "total_distance": int(w.distance),
"duration": duration, "duration": duration,
"notes": w.notes, "notes": w.notes,
"avg_heartrate": averagehr, "avg_heartrate": averagehr,
"max_heartrate": maxhr, "max_heartrate": maxhr,
"location": locdata, "location": locdata,
"distance": distancedata, "distance": distancedata,
"cadence": spmdata, "cadence": spmdata,
"heartrate": hrdata, "heartrate": hrdata,
} }
else: else:
data = { data = {
"type": "Rowing", "type": "Rowing",
"name": w.name, "name": w.name,
"start_time": st.isoformat(), "start_time": st.isoformat(),
"total_distance": int(w.distance), "total_distance": int(w.distance),
"duration": duration, "duration": duration,
"notes": w.notes, "notes": w.notes,
"avg_heartrate": averagehr, "avg_heartrate": averagehr,
"max_heartrate": maxhr, "max_heartrate": maxhr,
"distance": distancedata, "distance": distancedata,
"cadence": spmdata, "cadence": spmdata,
"heartrate": hrdata, "heartrate": hrdata,
} }
if haspower: if haspower:
data['power'] = powerdata data['power'] = powerdata
return data return data
@@ -234,6 +242,11 @@ def getidfromresponse(response):
return int(id) return int(id)
def default(o):
if isinstance(o, numpy.int64): return int(o)
raise TypeError
def workout_sporttracks_upload(user,w): def workout_sporttracks_upload(user,w):
message = "Uploading to SportTracks" message = "Uploading to SportTracks"
@@ -244,41 +257,41 @@ def workout_sporttracks_upload(user,w):
thetoken = sporttracks_open(user) thetoken = sporttracks_open(user)
if (checkworkoutuser(user,w)): if (checkworkoutuser(user,w)):
data = createsporttracksworkoutdata(w) data = createsporttracksworkoutdata(w)
if not data: if not data:
message = "Data error" message = "Data error"
stid = 0 stid = 0
return message,stid return message,stid
authorizationstring = str('Bearer ' + thetoken) authorizationstring = str('Bearer ' + thetoken)
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'} 'Content-Type': 'application/json'}
url = "https://api.sporttracks.mobi/api/v2/fitnessActivities.json" url = "https://api.sporttracks.mobi/api/v2/fitnessActivities.json"
response = requests.post(url,headers=headers,data=json.dumps(data)) response = requests.post(url,headers=headers,data=json.dumps(data,default=default))
# check for duplicate error first # check for duplicate error first
if (response.status_code == 409 ): if (response.status_code == 409 ):
message = "Duplicate error" message = "Duplicate error"
w.uploadedtosporttracks = -1 w.uploadedtosporttracks = -1
stid = -1 stid = -1
w.save() w.save()
return message, stid return message, stid
elif (response.status_code == 201 or response.status_code==200): elif (response.status_code == 201 or response.status_code==200):
s= response.json() s= response.json()
stid = getidfromresponse(response) stid = getidfromresponse(response)
w.uploadedtosporttracks = stid w.uploadedtosporttracks = stid
w.save() w.save()
return 'Successfully synced to SportTracks',stid return 'Successfully synced to SportTracks',stid
else: else:
s = response s = response
message = "Something went wrong in workout_sporttracks_upload_view: %s" % s.reason message = "Something went wrong in workout_sporttracks_upload_view: %s" % s.reason
stid = 0 stid = 0
return message,stid return message,stid
else: else:
message = "You are not authorized to upload this workout" message = "You are not authorized to upload this workout"
stid = 0 stid = 0
return message,stid return message,stid
@@ -294,33 +307,33 @@ def add_workout_from_data(user,importid,data,strokedata,source='sporttracks',
workouttype = 'other' workouttype = 'other'
if workouttype not in [x[0] for x in Workout.workouttypes]: if workouttype not in [x[0] for x in Workout.workouttypes]:
workouttype = 'other' workouttype = 'other'
try: try:
comments = data['comments'] comments = data['comments']
except: except:
comments = '' comments = ''
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
try: try:
rowdatetime = iso8601.parse_date(data['start_time']) rowdatetime = iso8601.parse_date(data['start_time'])
except iso8601.ParseError: except iso8601.ParseError:
try: try:
rowdatetime = datetime.datetime.strptime(data['start_time'],"%Y-%m-%d %H:%M:%S") rowdatetime = datetime.datetime.strptime(data['start_time'],"%Y-%m-%d %H:%M:%S")
rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc) rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
except: except:
try: try:
rowdatetime = dateutil.parser.parse(data['start_time']) rowdatetime = dateutil.parser.parse(data['start_time'])
rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc) rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
except: except:
rowdatetime = datetime.datetime.strptime(data['date'],"%Y-%m-%d %H:%M:%S") rowdatetime = datetime.datetime.strptime(data['date'],"%Y-%m-%d %H:%M:%S")
rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc) rowdatetime = thetimezone.localize(rowdatetime).astimezone(utc)
starttimeunix = arrow.get(rowdatetime).timestamp starttimeunix = arrow.get(rowdatetime).timestamp
try: try:
title = data['name'] title = data['name']
except: except:
title = "Imported data" title = "Imported data"
try: try:
res = splitstdata(data['distance']) res = splitstdata(data['distance'])
@@ -336,41 +349,41 @@ def add_workout_from_data(user,importid,data,strokedata,source='sporttracks',
try: try:
l = data['location'] l = data['location']
res = splitstdata(l) res = splitstdata(l)
times_location = res[0] times_location = res[0]
latlong = res[1] latlong = res[1]
latcoord = [] latcoord = []
loncoord = [] loncoord = []
for coord in latlong: for coord in latlong:
lat = coord[0] lat = coord[0]
lon = coord[1] lon = coord[1]
latcoord.append(lat) latcoord.append(lat)
loncoord.append(lon) loncoord.append(lon)
except: except:
times_location = times_distance times_location = times_distance
latcoord = np.zeros(len(times_distance)) latcoord = np.zeros(len(times_distance))
loncoord = np.zeros(len(times_distance)) loncoord = np.zeros(len(times_distance))
if workouttype in mytypes.otwtypes: if workouttype in mytypes.otwtypes:
workouttype = 'rower' workouttype = 'rower'
try: try:
res = splitstdata(data['cadence']) res = splitstdata(data['cadence'])
times_spm = res[0] times_spm = res[0]
spm = res[1] spm = res[1]
except KeyError: except KeyError:
times_spm = times_distance times_spm = times_distance
spm = 0*times_distance spm = 0*times_distance
try: try:
res = splitstdata(data['heartrate']) res = splitstdata(data['heartrate'])
hr = res[1] hr = res[1]
times_hr = res[0] times_hr = res[0]
except KeyError: except KeyError:
times_hr = times_distance times_hr = times_distance
hr = 0*times_distance hr = 0*times_distance
# create data series and remove duplicates # create data series and remove duplicates
@@ -388,12 +401,12 @@ def add_workout_from_data(user,importid,data,strokedata,source='sporttracks',
# Create dicts and big dataframe # Create dicts and big dataframe
d = { d = {
' Horizontal (meters)': distseries, ' Horizontal (meters)': distseries,
' latitude': latseries, ' latitude': latseries,
' longitude': lonseries, ' longitude': lonseries,
' Cadence (stokes/min)': spmseries, ' Cadence (stokes/min)': spmseries,
' HRCur (bpm)' : hrseries, ' HRCur (bpm)' : hrseries,
} }
+180 -174
View File
@@ -1,3 +1,9 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from __future__ import unicode_literals, absolute_import
# All the functionality needed to connect to Strava # All the functionality needed to connect to Strava
from scipy import optimize from scipy import optimize
from scipy.signal import savgol_filter from scipy.signal import savgol_filter
@@ -10,15 +16,15 @@ queue = django_rq.get_queue('default')
queuelow = django_rq.get_queue('low') queuelow = django_rq.get_queue('low')
queuehigh = django_rq.get_queue('low') queuehigh = django_rq.get_queue('low')
from dataprep import columndict from rowers.dataprep import columndict
import stravalib import stravalib
from stravalib.exc import ActivityUploadFailed,TimeoutExceeded from stravalib.exc import ActivityUploadFailed,TimeoutExceeded
from iso8601 import ParseError from iso8601 import ParseError
from utils import myqueue from rowers.utils import myqueue
import mytypes import rowers.mytypes as mytypes
import gzip import gzip
from rowsandall_app.settings import ( from rowsandall_app.settings import (
@@ -35,7 +41,7 @@ from rowers.imports import *
headers = {'Accept': 'application/json', headers = {'Accept': 'application/json',
'Api-Key': STRAVA_CLIENT_ID, 'Api-Key': STRAVA_CLIENT_ID,
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'user-agent': 'sanderroosendaal'} 'user-agent': 'sanderroosendaal'}
@@ -89,26 +95,26 @@ def make_authorization_url(request):
def get_strava_workout_list(user,limit_n=0): def get_strava_workout_list(user,limit_n=0):
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
if (r.stravatoken == '') or (r.stravatoken is None): if (r.stravatoken == '') or (r.stravatoken is None):
s = "Token doesn't exist. Need to authorize" s = "Token doesn't exist. Need to authorize"
return custom_exception_handler(401,s) return custom_exception_handler(401,s)
elif (r.stravatokenexpirydate is None or timezone.now()+timedelta(seconds=3599)>r.stravatokenexpirydate): elif (r.stravatokenexpirydate is None or timezone.now()+timedelta(seconds=3599)>r.stravatokenexpirydate):
s = "Token expired. Needs to refresh." s = "Token expired. Needs to refresh."
return custom_exception_handler(401,s) return custom_exception_handler(401,s)
else: else:
# ready to fetch. Hurray # ready to fetch. Hurray
authorizationstring = str('Bearer ' + r.stravatoken) authorizationstring = str('Bearer ' + r.stravatoken)
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json'} 'Content-Type': 'application/json'}
url = "https://www.strava.com/api/v3/athlete/activities" url = "https://www.strava.com/api/v3/athlete/activities"
if limit_n==0: if limit_n==0:
params = {} params = {}
else: else:
params = {'per_page':limit_n} params = {'per_page':limit_n}
s = requests.get(url,headers=headers,params=params) s = requests.get(url,headers=headers,params=params)
return s return s
@@ -200,27 +206,27 @@ def create_async_workout(alldata,user,stravaid,debug=False):
thetimezone = 'UTC' thetimezone = 'UTC'
try: try:
rowdatetime = iso8601.parse_date(data['date_utc']) rowdatetime = iso8601.parse_date(data['date_utc'])
except KeyError: except KeyError:
rowdatetime = iso8601.parse_date(data['start_date']) rowdatetime = iso8601.parse_date(data['start_date'])
except ParseError: except ParseError:
rowdatetime = iso8601.parse_date(data['date']) rowdatetime = iso8601.parse_date(data['date'])
try: try:
c2intervaltype = data['workout_type'] c2intervaltype = data['workout_type']
except KeyError: except KeyError:
c2intervaltype = '' c2intervaltype = ''
try: try:
title = data['name'] title = data['name']
except KeyError: except KeyError:
title = "" title = ""
try: try:
t = data['comments'].split('\n', 1)[0] t = data['comments'].split('\n', 1)[0]
title += t[:20] title += t[:20]
except: except:
title = 'Imported' title = 'Imported'
workoutdate = rowdatetime.astimezone( workoutdate = rowdatetime.astimezone(
pytz.timezone(thetimezone) pytz.timezone(thetimezone)
@@ -259,7 +265,7 @@ def create_async_workout(alldata,user,stravaid,debug=False):
return 1 return 1
from utils import get_strava_stream from rowers.utils import get_strava_stream
@@ -273,26 +279,26 @@ def get_workout(user,stravaid):
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
if (r.stravatoken == '') or (r.stravatoken is None): if (r.stravatoken == '') or (r.stravatoken is None):
s = "Token doesn't exist. Need to authorize" s = "Token doesn't exist. Need to authorize"
return custom_exception_handler(401,s) return custom_exception_handler(401,s)
elif (r.stravatokenexpirydate is not None and timezone.now()>r.stravatokenexpirydate): elif (r.stravatokenexpirydate is not None and timezone.now()>r.stravatokenexpirydate):
s = "Token expired. Needs to refresh." s = "Token expired. Needs to refresh."
return custom_exception_handler(401,s) return custom_exception_handler(401,s)
else: else:
# ready to fetch. Hurray # ready to fetch. Hurray
fetchresolution = 'high' fetchresolution = 'high'
series_type = 'time' series_type = 'time'
authorizationstring = str('Bearer ' + r.stravatoken) authorizationstring = str('Bearer ' + r.stravatoken)
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'resolution': 'medium',} 'resolution': 'medium',}
url = "https://www.strava.com/api/v3/activities/"+str(stravaid) url = "https://www.strava.com/api/v3/activities/"+str(stravaid)
workoutsummary = requests.get(url,headers=headers).json() workoutsummary = requests.get(url,headers=headers).json()
workoutsummary['timezone'] = "Etc/UTC" workoutsummary['timezone'] = "Etc/UTC"
try: try:
startdatetime = workoutsummary['start_date'] startdatetime = workoutsummary['start_date']
except KeyError: except KeyError:
startdatetime = timezone.now() startdatetime = timezone.now()
@@ -306,9 +312,9 @@ def get_workout(user,stravaid):
try: try:
t = np.array(timejson.json()[0]['data']) t = np.array(timejson.json()[0]['data'])
nr_rows = len(t) nr_rows = len(t)
d = np.array(distancejson.json()[1]['data']) d = np.array(distancejson.json()[1]['data'])
if nr_rows == 0: if nr_rows == 0:
return (0,"Error: Time data had zero length") return (0,"Error: Time data had zero length")
@@ -318,70 +324,70 @@ def get_workout(user,stravaid):
except KeyError: except KeyError:
return (0,"something went wrong with the Strava import") return (0,"something went wrong with the Strava import")
try: try:
spm = np.array(spmjson.json()[1]['data']) spm = np.array(spmjson.json()[1]['data'])
except: except:
spm = np.zeros(nr_rows) spm = np.zeros(nr_rows)
try: try:
power = np.array(powerjson.json()[1]['data']) power = np.array(powerjson.json()[1]['data'])
except IndexError: except IndexError:
power = np.zeros(nr_rows) power = np.zeros(nr_rows)
try: try:
hr = np.array(hrjson.json()[1]['data']) hr = np.array(hrjson.json()[1]['data'])
except IndexError: except IndexError:
hr = np.zeros(nr_rows) hr = np.zeros(nr_rows)
except KeyError: except KeyError:
hr = np.zeros(nr_rows) hr = np.zeros(nr_rows)
try: try:
velo = np.array(velojson.json()[1]['data']) velo = np.array(velojson.json()[1]['data'])
except IndexError: except IndexError:
velo = np.zeros(nr_rows) velo = np.zeros(nr_rows)
except KeyError: except KeyError:
velo = np.zeros(nr_rows) velo = np.zeros(nr_rows)
dt = np.diff(t).mean() dt = np.diff(t).mean()
wsize = round(5./dt) wsize = round(5./dt)
velo2 = ewmovingaverage(velo,wsize) velo2 = ewmovingaverage(velo,wsize)
coords = np.array(latlongjson.json()[0]['data']) coords = np.array(latlongjson.json()[0]['data'])
try: try:
lat = coords[:,0] lat = coords[:,0]
lon = coords[:,1] lon = coords[:,1]
except IndexError: except IndexError:
lat = np.zeros(len(t)) lat = np.zeros(len(t))
lon = np.zeros(len(t)) lon = np.zeros(len(t))
except KeyError: except KeyError:
lat = np.zeros(len(t)) lat = np.zeros(len(t))
lon = np.zeros(len(t)) lon = np.zeros(len(t))
strokelength = velo*60./(spm) strokelength = velo*60./(spm)
strokelength[np.isinf(strokelength)] = 0.0 strokelength[np.isinf(strokelength)] = 0.0
pace = 500./(1.0*velo2) pace = 500./(1.0*velo2)
pace[np.isinf(pace)] = 0.0 pace[np.isinf(pace)] = 0.0
df = pd.DataFrame({'t':10*t, df = pd.DataFrame({'t':10*t,
'd':10*d, 'd':10*d,
'p':10*pace, 'p':10*pace,
'spm':spm, 'spm':spm,
'hr':hr, 'hr':hr,
'lat':lat, 'lat':lat,
'lon':lon, 'lon':lon,
'power':power, 'power':power,
'strokelength':strokelength, 'strokelength':strokelength,
}) })
# startdatetime = datetime.datetime.strptime(startdatetime,"%Y-%m-%d-%H:%M:%S") # startdatetime = datetime.datetime.strptime(startdatetime,"%Y-%m-%d-%H:%M:%S")
return [workoutsummary,df] return [workoutsummary,df]
# Generate Workout data for Strava (a TCX file) # Generate Workout data for Strava (a TCX file)
def createstravaworkoutdata(w,dozip=True): def createstravaworkoutdata(w,dozip=True):
@@ -478,41 +484,41 @@ def add_workout_from_data(user,importid,data,strokedata,
workouttype = 'water' workouttype = 'water'
if workouttype not in [x[0] for x in Workout.workouttypes]: if workouttype not in [x[0] for x in Workout.workouttypes]:
workouttype = 'other' workouttype = 'other'
try: try:
comments = data['comments'] comments = data['comments']
except: except:
comments = ' ' comments = ' '
try: try:
thetimezone = tz(data['timezone']) thetimezone = tz(data['timezone'])
except: except:
thetimezone = 'UTC' thetimezone = 'UTC'
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
try: try:
rowdatetime = iso8601.parse_date(data['date_utc']) rowdatetime = iso8601.parse_date(data['date_utc'])
except KeyError: except KeyError:
rowdatetime = iso8601.parse_date(data['start_date']) rowdatetime = iso8601.parse_date(data['start_date'])
except ParseError: except ParseError:
rowdatetime = iso8601.parse_date(data['date']) rowdatetime = iso8601.parse_date(data['date'])
try: try:
intervaltype = data['workout_type'] intervaltype = data['workout_type']
except KeyError: except KeyError:
intervaltype = '' intervaltype = ''
try: try:
title = data['name'] title = data['name']
except KeyError: except KeyError:
title = "" title = ""
try: try:
t = data['comments'].split('\n', 1)[0] t = data['comments'].split('\n', 1)[0]
title += t[:20] title += t[:20]
except: except:
title = 'Imported' title = 'Imported'
starttimeunix = arrow.get(rowdatetime).timestamp starttimeunix = arrow.get(rowdatetime).timestamp
@@ -526,17 +532,17 @@ def add_workout_from_data(user,importid,data,strokedata,
nr_rows = len(unixtime) nr_rows = len(unixtime)
try: try:
latcoord = strokedata.loc[:,'lat'] latcoord = strokedata.loc[:,'lat']
loncoord = strokedata.loc[:,'lon'] loncoord = strokedata.loc[:,'lon']
except: except:
latcoord = np.zeros(nr_rows) latcoord = np.zeros(nr_rows)
loncoord = np.zeros(nr_rows) loncoord = np.zeros(nr_rows)
try: try:
strokelength = strokedata.loc[:,'strokelength'] strokelength = strokedata.loc[:,'strokelength']
except: except:
strokelength = np.zeros(nr_rows) strokelength = np.zeros(nr_rows)
dist2 = 0.1*strokedata.loc[:,'d'] dist2 = 0.1*strokedata.loc[:,'d']
@@ -566,23 +572,23 @@ def add_workout_from_data(user,importid,data,strokedata,
# save csv # save csv
# Create data frame with all necessary data to write to csv # Create data frame with all necessary data to write to csv
df = pd.DataFrame({'TimeStamp (sec)':unixtime, df = pd.DataFrame({'TimeStamp (sec)':unixtime,
' Horizontal (meters)': dist2, ' Horizontal (meters)': dist2,
' Cadence (stokes/min)':spm, ' Cadence (stokes/min)':spm,
' HRCur (bpm)':hr, ' HRCur (bpm)':hr,
' longitude':loncoord, ' longitude':loncoord,
' latitude':latcoord, ' latitude':latcoord,
' Stroke500mPace (sec/500m)':pace, ' Stroke500mPace (sec/500m)':pace,
' Power (watts)':power, ' Power (watts)':power,
' DragFactor':np.zeros(nr_rows), ' DragFactor':np.zeros(nr_rows),
' DriveLength (meters)':np.zeros(nr_rows), ' DriveLength (meters)':np.zeros(nr_rows),
' StrokeDistance (meters)':strokelength, ' StrokeDistance (meters)':strokelength,
' DriveTime (ms)':np.zeros(nr_rows), ' DriveTime (ms)':np.zeros(nr_rows),
' StrokeRecoveryTime (ms)':np.zeros(nr_rows), ' StrokeRecoveryTime (ms)':np.zeros(nr_rows),
' AverageDriveForce (lbs)':np.zeros(nr_rows), ' AverageDriveForce (lbs)':np.zeros(nr_rows),
' PeakDriveForce (lbs)':np.zeros(nr_rows), ' PeakDriveForce (lbs)':np.zeros(nr_rows),
' lapIdx':lapidx, ' lapIdx':lapidx,
' ElapsedTime (sec)':seconds ' ElapsedTime (sec)':seconds
}) })
df.sort_values(by='TimeStamp (sec)',ascending=True) df.sort_values(by='TimeStamp (sec)',ascending=True)
@@ -623,34 +629,34 @@ def workout_strava_upload(user,w):
r = Rower.objects.get(user=user) r = Rower.objects.get(user=user)
res = -1 res = -1
if (r.stravatoken == '') or (r.stravatoken is None): if (r.stravatoken == '') or (r.stravatoken is None):
s = "Token doesn't exist. Need to authorize" s = "Token doesn't exist. Need to authorize"
raise NoTokenError("Your hovercraft is full of eels") raise NoTokenError("Your hovercraft is full of eels")
else: else:
if (checkworkoutuser(user,w)): if (checkworkoutuser(user,w)):
try: try:
tcxfile,tcxmesg = createstravaworkoutdata(w) tcxfile,tcxmesg = createstravaworkoutdata(w)
if tcxfile: if tcxfile:
with open(tcxfile,'rb') as f: with open(tcxfile,'rb') as f:
res,mes = handle_stravaexport( res,mes = handle_stravaexport(
f,w.name, f,w.name,
r.stravatoken, r.stravatoken,
description=w.notes+'\n from '+w.workoutsource+' via rowsandall.com', description=w.notes+'\n from '+w.workoutsource+' via rowsandall.com',
activity_type=r.stravaexportas) activity_type=r.stravaexportas)
if res==0: if res==0:
message = mes message = mes
w.uploadedtostrava = -1 w.uploadedtostrava = -1
stravaid = -1 stravaid = -1
w.save() w.save()
try: try:
os.remove(tcxfile) os.remove(tcxfile)
except WindowsError: except WindowsError:
pass pass
return message,stravaid return message,stravaid
w.uploadedtostrava = res w.uploadedtostrava = res
w.save() w.save()
try: try:
os.remove(tcxfile) os.remove(tcxfile)
except WindowsError: except WindowsError:
pass pass
message = mes message = mes
@@ -663,14 +669,14 @@ def workout_strava_upload(user,w):
w.save() w.save()
return message, stravaid return message, stravaid
except ActivityUploadFailed as e: except ActivityUploadFailed as e:
message = "Strava Upload error: %s" % e message = "Strava Upload error: %s" % e
w.uploadedtostrava = -1 w.uploadedtostrava = -1
stravaid = -1 stravaid = -1
w.save() w.save()
os.remove(tcxfile) os.remove(tcxfile)
return message,stravaid return message,stravaid
return message,stravaid return message,stravaid
return message,stravaid return message,stravaid
@@ -688,9 +694,9 @@ def handle_strava_import_stroke_data(title,
series_type = 'time' series_type = 'time'
authorizationstring = str('Bearer ' + stravatoken) authorizationstring = str('Bearer ' + stravatoken)
headers = {'Authorization': authorizationstring, headers = {'Authorization': authorizationstring,
'user-agent': 'sanderroosendaal', 'user-agent': 'sanderroosendaal',
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'resolution': 'medium',} 'resolution': 'medium',}
url = "https://www.strava.com/api/v3/activities/"+str(stravaid) url = "https://www.strava.com/api/v3/activities/"+str(stravaid)
workoutsummary = requests.get(url,headers=headers).json() workoutsummary = requests.get(url,headers=headers).json()
@@ -708,9 +714,9 @@ def handle_strava_import_stroke_data(title,
wattsjson = get_strava_stream(r,'watts',stravaid) wattsjson = get_strava_stream(r,'watts',stravaid)
try: try:
t = np.array(timejson.json()[0]['data']) t = np.array(timejson.json()[0]['data'])
nr_rows = len(t) nr_rows = len(t)
d = np.array(distancejson.json()[1]['data']) d = np.array(distancejson.json()[1]['data'])
if nr_rows == 0: if nr_rows == 0:
return 0 return 0
except IndexError: except IndexError:
@@ -720,9 +726,9 @@ def handle_strava_import_stroke_data(title,
return 0 return 0
try: try:
spm = np.array(spmjson.json()[1]['data']) spm = np.array(spmjson.json()[1]['data'])
except: except:
spm = np.zeros(nr_rows) spm = np.zeros(nr_rows)
try: try:
watts = np.array(wattsjson.json()[1]['data']) watts = np.array(wattsjson.json()[1]['data'])
@@ -730,16 +736,16 @@ def handle_strava_import_stroke_data(title,
watts = np.zeros(nr_rows) watts = np.zeros(nr_rows)
try: try:
hr = np.array(hrjson.json()[1]['data']) hr = np.array(hrjson.json()[1]['data'])
except IndexError: except IndexError:
hr = np.zeros(nr_rows) hr = np.zeros(nr_rows)
except KeyError: except KeyError:
hr = np.zeros(nr_rows) hr = np.zeros(nr_rows)
try: try:
velo = np.array(velojson.json()[1]['data']) velo = np.array(velojson.json()[1]['data'])
except IndexError: except IndexError:
velo = np.zeros(nr_rows) velo = np.zeros(nr_rows)
except KeyError: except KeyError:
velo = np.zeros(nr_rows) velo = np.zeros(nr_rows)
@@ -756,14 +762,14 @@ def handle_strava_import_stroke_data(title,
coords = np.array(latlongjson.json()[0]['data']) coords = np.array(latlongjson.json()[0]['data'])
try: try:
lat = coords[:,0] lat = coords[:,0]
lon = coords[:,1] lon = coords[:,1]
except IndexError: except IndexError:
lat = np.zeros(len(t)) lat = np.zeros(len(t))
lon = np.zeros(len(t)) lon = np.zeros(len(t))
except KeyError: except KeyError:
lat = np.zeros(len(t)) lat = np.zeros(len(t))
lon = np.zeros(len(t)) lon = np.zeros(len(t))
strokelength = velo*60./(spm) strokelength = velo*60./(spm)
strokelength[np.isinf(strokelength)] = 0.0 strokelength[np.isinf(strokelength)] = 0.0
@@ -779,22 +785,22 @@ def handle_strava_import_stroke_data(title,
df = pd.DataFrame({'TimeStamp (sec)':unixtime, df = pd.DataFrame({'TimeStamp (sec)':unixtime,
' ElapsedTime (sec)':t, ' ElapsedTime (sec)':t,
' Horizontal (meters)':d, ' Horizontal (meters)':d,
' Stroke500mPace (sec/500m)':pace, ' Stroke500mPace (sec/500m)':pace,
' Cadence (stokes/min)':spm, ' Cadence (stokes/min)':spm,
' HRCur (bpm)':hr, ' HRCur (bpm)':hr,
' latitude':lat, ' latitude':lat,
' longitude':lon, ' longitude':lon,
' StrokeDistance (meters)':strokelength, ' StrokeDistance (meters)':strokelength,
'cum_dist':d, 'cum_dist':d,
' DragFactor':np.zeros(nr_strokes), ' DragFactor':np.zeros(nr_strokes),
' DriveLength (meters)':np.zeros(nr_strokes), ' DriveLength (meters)':np.zeros(nr_strokes),
' StrokeDistance (meters)':strokedistance, ' StrokeDistance (meters)':strokedistance,
' DriveTime (ms)':np.zeros(nr_strokes), ' DriveTime (ms)':np.zeros(nr_strokes),
' StrokeRecoveryTime (ms)':np.zeros(nr_strokes), ' StrokeRecoveryTime (ms)':np.zeros(nr_strokes),
' AverageDriveForce (lbs)':np.zeros(nr_strokes), ' AverageDriveForce (lbs)':np.zeros(nr_strokes),
' PeakDriveForce (lbs)':np.zeros(nr_strokes), ' PeakDriveForce (lbs)':np.zeros(nr_strokes),
' lapIdx':np.zeros(nr_strokes), ' lapIdx':np.zeros(nr_strokes),
' Power (watts)':watts, ' Power (watts)':watts,
}) })
+29 -18
View File
@@ -1,3 +1,8 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
""" Background tasks done by Celery (develop) or QR (production) """ """ Background tasks done by Celery (develop) or QR (production) """
import os import os
import time import time
@@ -16,7 +21,10 @@ from rowingdata import rowingdata as rdata
from datetime import timedelta from datetime import timedelta
from sqlalchemy import create_engine from sqlalchemy import create_engine
from celery import app #from celery import app
from rowers.celery import app
from celery import shared_task
import datetime import datetime
import pytz import pytz
import iso8601 import iso8601
@@ -39,10 +47,11 @@ from django_rq import job
from django.utils import timezone from django.utils import timezone
from django.utils.html import strip_tags from django.utils.html import strip_tags
from utils import deserialize_list,ewmovingaverage,wavg from rowers.utils import deserialize_list,ewmovingaverage,wavg
from emails import htmlstrip from rowers.emails import htmlstrip
from HTMLParser import HTMLParser #from HTMLParser import HTMLParser
from html.parser import HTMLParser
class MLStripper(HTMLParser): class MLStripper(HTMLParser):
def __init__(self): def __init__(self):
self.reset() self.reset()
@@ -80,13 +89,13 @@ from django.db.utils import OperationalError
from jinja2 import Template,Environment,FileSystemLoader from jinja2 import Template,Environment,FileSystemLoader
env = Environment(loader = FileSystemLoader(["rowers/templates"])) env = Environment(loader = FileSystemLoader(["rowers/templates"]))
import datautils import rowers.datautils as datautils
import utils import rowers.utils as utils
import requests import requests
import longtask import rowers.longtask as longtask
import arrow import arrow
from utils import get_strava_stream from rowers.utils import get_strava_stream
siteurl = SITE_URL siteurl = SITE_URL
@@ -128,9 +137,9 @@ def handle_c2_import_stroke_data(c2token,
return 1 return 1
else: else:
url = "https://log.concept2.com/api/users/me/results/"+str(c2id) url = "https://log.concept2.com/api/users/me/results/{id}".format(id=c2id)
s = requests.get(url,headers=headers) s = requests.get(url,headers=headers)
if s.status_code == 200: if s.status_code == 200:
workoutdata = s.json()['data'] workoutdata = s.json()['data']
@@ -1238,21 +1247,21 @@ def handle_zip_file(emailfrom, subject, file,**kwargs):
debug = False debug = False
if debug: if debug:
print message print(message)
email = EmailMessage(subject, message, email = EmailMessage(subject, message,
emailfrom, emailfrom,
['workouts@rowsandall.com']) ['workouts@rowsandall.com'])
email.attach_file(file) email.attach_file(file)
if debug: if debug:
print "attaching" print("attaching")
res = email.send() res = email.send()
if debug: if debug:
print "sent" print("sent")
time.sleep(60) time.sleep(60)
return 1 return 1
@@ -1490,9 +1499,8 @@ def handle_updateergcp(rower_id,workoutfilenames,debug=False,**kwargs):
rowdata = rdata(f1 + '.gz') rowdata = rdata(f1 + '.gz')
except IOError: except IOError:
rowdata = 0 rowdata = 0
if rowdata != 0:
if rowdata != 0: therows.append(rowdata)
therows.append(rowdata)
cpdata = rowingdata.cumcpdata(therows) cpdata = rowingdata.cumcpdata(therows)
cpdata.columns = cpdata.columns.str.lower() cpdata.columns = cpdata.columns.str.lower()
@@ -1637,7 +1645,7 @@ def handle_makeplot(f1, f2, t, hrdata, plotnr, imagename,
hran = hrdata['hran'] hran = hrdata['hran']
ftp = hrdata['ftp'] ftp = hrdata['ftp']
powerzones = deserialize_list(hrdata['powerzones']) powerzones = deserialize_list(hrdata['powerzones'])
powerperc = np.array(deserialize_list(hrdata['powerperc'])).astype(int) powerperc = np.array(deserialize_list(hrdata['powerperc'])).astype(float)
rr = rowingdata.rower(hrmax=hrmax, hrut2=hrut2, rr = rowingdata.rower(hrmax=hrmax, hrut2=hrut2,
hrut1=hrut1, hrat=hrat, hrut1=hrut1, hrat=hrat,
@@ -1649,7 +1657,10 @@ def handle_makeplot(f1, f2, t, hrdata, plotnr, imagename,
except IOError: except IOError:
row = rdata(f2 + '.gz', rower=rr) row = rdata(f2 + '.gz', rower=rr)
haspower = row.df[' Power (watts)'].mean() > 50 try:
haspower = row.df[' Power (watts)'].mean() > 50
except TypeError:
haspower = True
nr_rows = len(row.df) nr_rows = len(row.df)
if (plotnr in [1, 2, 4, 5, 8, 11, 9, 12]) and (nr_rows > 1200): if (plotnr in [1, 2, 4, 5, 8, 11, 9, 12]) and (nr_rows > 1200):
+5
View File
@@ -1,3 +1,8 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import os import os
import sys import sys
import django import django
+5 -1
View File
@@ -1,3 +1,7 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
# All the Team related methods # All the Team related methods
@@ -10,7 +14,7 @@ from django.db import IntegrityError
import uuid import uuid
from django.conf import settings from django.conf import settings
from utils import myqueue from rowers.utils import myqueue
import django_rq import django_rq
queue = django_rq.get_queue('default') queue = django_rq.get_queue('default')
-106
View File
@@ -1,106 +0,0 @@
# This is just a scratch pad to temporarily park code, just in case I need
# it later. Hardly used since i have proper versioning
#
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": w.startdatetime.isoformat(),
"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
@login_required()
def workout_edit_view(request,id=0):
if request.method == 'POST':
# Form was submitted
form = WorkoutForm(request.POST)
if form.is_valid():
# Get values from form
name = request.POST['name']
date = request.POST['date']
workouttype = request.POST['workouttype']
notes = request.POST['notes']
try:
# check if valid ID exists (workout exists)
row = Workout.objects.get(id=id)
# check if user is owner of this workout
if checkworkoutuser(request.user,row):
row.name = name
row.date = date
row.workouttype = workouttype
row.notes = notes
row.save()
response = HttpResponse("data updated")
else:
response = HttpResponse("You are not allowed to change this workout")
except Workout.DoesNotExist:
# create new workout
r = Rower.objects.get(user=request.user)
w = Workout(name=name,date=date,workouttype=workouttype,
user=r)
w.save()
response = HttpResponse("New workout created")
else:
response = HttpResponse("invalid form")
return response
else:
try:
row = Workout.objects.get(id=id)
form = WorkoutForm(instance=row)
# check if user is owner of this workout
if (checkworkoutuser(request.user,row)==False):
return HttpResponse("You are not allowed to edit this workout")
except Workout.DoesNotExist:
form = WorkoutForm(
initial = {'workouttype' : 'rower'}
)
return render(request, 'workout_form.html',
{'form':form,})
-227
View File
@@ -1,227 +0,0 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}{{ workout.name }} Advanced {% endblock %}
{% block content %}
<div id="workouts" class="grid_6 alpha">
<div class="grid_6 alpha">
{% if form.errors %}
<p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below.
</p>
{% endif %}
<h1>{{ workout.name }} - Advanced</h1>
{% if user.rower.rowerplan == 'basic' %}
<p>Functionality marked with an asterisk (*) is limited to the paid plans. See
<a href="/rowers/paidplans">the page about Pro membership</a>
for more information and to sign up for Pro Membership</a></p>
{% endif %}
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id|encode }}/edit">Edit Workout</a>
</p>
</div>
<div class="grid_2">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id|encode }}/workflow">Workflow View</a>
</p>
</div>
<div class="grid_2 omega">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id|encode }}/export">Export</a>
</p>
</div>
</div>
<div class="grid_6 alpha">
<table width=100%>
<tr>
<th>Date:</th><td>{{ workout.date }}</td>
</tr><tr>
<th>Time:</th><td>{{ workout.starttime }}</td>
</tr><tr>
<th>Distance:</th><td>{{ workout.distance }}m</td>
</tr><tr>
<th>Duration:</th><td>{{ workout.duration |durationprint:"%H:%M:%S.%f" }}</td>
</tr>
<th>Public link to this workout</th>
<td>
<a href="/rowers/workout/{{ workout.id|encode }}">https://rowsandall.com/rowers/workout/{{ workout.id|encode }}</a>
<td>
</table>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/compare/{{ workout.id|encode }}">Compare Workouts</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans/">Compare Workouts*</a>
{% endif %}
<p>
Compare this workout to other workouts. Plot HR, SPM, or pace vs time or distance for the two workouts.
</p>
</div>
<div class="grid_2">
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/flexchart">
Flexible Interactive Plot
</a>
<p>
Flexible Interactive plot. Pick your own X and Y axis parameters.
</p>
</div>
<div class="grid_2 omega tooltip">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/editintervals">Edit Intervals</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Edit Intervals*</a>
{% endif %}
</p>
<span class="tooltiptext">Enter or change the interval and summary data for your workout</span>
<p>
Enter or change the interval and summary data for your workout
</p>
</div>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/adddistanceplot2">Dist Metrics Plot</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Dist Metrics Plot*</a>
{% endif %}
</p>
<p>
Various advanced stroke metrics plotted versus distance.
</p>
</div>
<div class="grid_2">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/addtimeplot2">Time Metrics Plot</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Time Metrics Plot*</a>
{% endif %}
</p>
<p>
Various advanced stroke metrics plotted versus time.
</p>
</div>
<div class="grid_2 omega">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/histo">Power Histogram</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Power Histogram*</a>
{% endif %}
</p>
<p>
Plot the Power Histogram of this workout
</p>
</div>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workouts-join-select">Glue</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Glue*</a>
{% endif %}
</p>
<p>
Glue multiple workouts together (into one workout). For example, to combine separately recorded intervals.
</p>
</div>
<div class="grid_2">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/fusion/{{ workout.id|encode }}/">Sensor Fusion</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Sensor Fusion*</a>
{% endif %}
</p>
<p>
Merge data from another source into this workout
</p>
</div>
<div class="grid_2 omega">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/split">Split Workout</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Split Workout*</a>
{% endif %}
</p>
<p>
Split workout into two seperate workouts
</p>
</div>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/interactiveplot">Big Interactive Plot</a>
</p>
<p>
See (and save) the big interactive plot
</p>
</div>
<div class="grid_2">
<p>
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/instroke">In-Stroke Metrics</a></p>
<p>
If your workout has in-stroke metrics (like a stroke power curve or boat acceleration curve), you will find it here.
</p>
</div>
</div>
</div>
<div id="advancedplots" class="grid_6 omega">
<div class="grid_6 alpha">
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ interactiveplot |safe }}
<script>
// Set things up to resize the plot on a window resize. You can play with
// the arguments of resize_width_height() to change the plot's behavior.
var plot_resize_setup = function () {
var plotid = Object.keys(Bokeh.index)[0]; // assume we have just one plot
var plot = Bokeh.index[plotid];
var plotresizer = function() {
// arguments: use width, use height, maintain aspect ratio
plot.resize_width_height(true, true, true);
};
window.addEventListener('resize', plotresizer);
plotresizer();
};
window.addEventListener('load', plot_resize_setup);
</script>
<style>
/* Need this to get the page in "desktop mode"; not having an infinite height.*/
html, body {height: 100%; margin:5px;}
</style>
<div id="interactiveplot" class="grid_6 omega">
{{ the_div |safe }}
</div>
</div>
</div>
{% endblock %}
-284
View File
@@ -1,284 +0,0 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}{{ workout.name }} Advanced {% endblock %}
{% block content %}
<div id="workouts" class="grid_6 alpha">
{% if form.errors %}
<p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below.
</p>
{% endif %}
<h1>{{ workout.name }} - Advanced OTW</h1>
{% if user.rower.rowerplan == 'basic' %}
<p>Functionality marked with an asterisk (*) is limited to the paid plans. See
<a href="/rowers/paidplans">the page about Pro membership</a>
for more information and to sign up for Pro Membership</a></p>
{% endif %}
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id|encode }}/edit">Edit Workout</a>
</p>
</div>
<div class="grid_2">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id|encode }}/workflow">Workflow View</a>
</p>
</div>
<div class="grid_2 omega">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id|encode }}/export">Export</a>
</p>
</div>
<div class="grid_6 alpha">
<table width=100%>
<tr>
<th>Date:</th><td>{{ workout.date }}</td>
</tr><tr>
<th>Time:</th><td>{{ workout.starttime }}</td>
</tr><tr>
<th>Distance:</th><td>{{ workout.distance }}m</td>
</tr><tr>
<th>Duration:</th><td>{{ workout.duration |durationprint:"%H:%M:%S.%f" }}</td>
</tr>
<th>Public link to this workout</th>
<td>
<a href="/rowers/workout/{{ workout.id|encode }}">https://rowsandall.com/rowers/workout/{{ workout.id|encode }}</a>
<td>
</table>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/compare/{{ workout.id|encode }}">Compare Workouts</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Compare Workouts*</a>
{% endif %}
</p>
<p>
Compare this workout to other workouts. Plot HR, SPM, or pace vs time or distance for the two workouts.
</p>
</div>
<div class="grid_2">
<p>
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/flexchart">Flexible Interactive Plot</a>
</p>
<p>
Flexible Interactive plot. Pick your own X and Y axis parameters.
</p>
</div>
<div class="grid_2 omega tooltip">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/editintervals">Edit Intervals</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Edit Intervals*</a>
{% endif %}
</p>
<span class="tooltiptext">Enter or change the interval and summary data for your workout</span>
<p>
Enter or change the interval and summary data for your workout
</p>
</div>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
{% if user|is_promember %}
<a class="button blue small"href="/rowers/workout/{{ workout.id|encode }}/crewnerdsummary">CrewNerd Summary</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">CrewNerd Summary*</a>
{% endif %}
</p>
<p>
Upload a CrewNerd Summary (CSV file) to this workout.
</p>
</div>
<div class="grid_2 tooltip">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/forcecurve">Stroke Profile (Empower)</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Stroke Profile (Empower)*</a>
{% endif %}
</p>
<span class="tooltiptext">Analyze your stroke force profile (need Empower Oarlock data)</span> <p>
Interactive plot of Stroke Profile (with Empower Oarlock)
</p>
</div>
<div class="grid_2 omega tooltip">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/addotwpowerplot">OTW Power Plot</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">OTW Power Plot*</a>
{% endif %}
</p>
<span class="tooltiptext">Note: You must run the OTW calculations under Geeky Stuff first. Otherwise the plot will be empty</span>
<p>
Pace, wind corrected pace, power, equivalent erg power in a static plot
</p>
</div>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/geeky">Geeky Stuff</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Geeky Stuff*</a>
{% endif %}
</p>
<p>
Add weather and current data and OTW power calculations.
</p>
</div>
<div class="grid_2 tooltip">
<p>
{% if user|is_promember %}
<a class="button blue small"href="/rowers/workout/{{ workout.id|encode }}/smoothenpace">Smooth out Pace Data</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Smooth out Pace Data*</a>
{% endif %}
</p>
<span class="tooltiptext">This will reduce noise on your pace data. The smoothing is irreversible but you can use the reset smoothing button.</span>
<p>
Pace data too noisy? Press this button!
</p>
</div>
<div class="grid_2 omega">
<p>
{% if user|is_promember %}
<a class="button blue small"href="/rowers/workout/{{ workout.id|encode }}/undosmoothenpace">Raw Data</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Reset Smoothing*</a>
{% endif %}
</p>
<p>
Reset pace data to values before smoothing
</p>
</div>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workouts-join-select">Glue</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Glue*</a>
{% endif %}
</p>
<p>
Glue multiple workouts together (into one workout). For example, to combine separately recorded intervals.
</p>
</div>
<div class="grid_2">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/fusion/{{ workout.id|encode }}/">Sensor Fusion</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Sensor Fusion*</a>
{% endif %}
</p>
<p>
Merge data from another source into this workout
</p>
</div>
<div class="grid_2 omega">
<p>
{% if user|is_promember %}
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/split">Split Workout</a>
{% else %}
<a class="button blue small" href="/rowers/paidplans">Split Workout*</a>
{% endif %}
</p>
<p>
Split workout into two seperate workouts
</p>
</div>
</div>
<div class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/interactiveplot">Big Interactive Plot</a>
</p>
<p>
See (and save) the big interactive plot
</p>
</div>
<div class="grid_2">
<p>
<a class="button blue small" href="/rowers/workout/{{ workout.id|encode }}/instroke">In-Stroke Metrics</a></p>
<p>
If your workout has in-stroke metrics (like a stroke power curve or boat acceleration curve), you will find it here.
</p>
</div>
</div>
</div>
<div id="advancedplots" class="grid_6 omega">
<div class="grid_6 alpha">
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ interactiveplot |safe }}
<script>
// Set things up to resize the plot on a window resize. You can play with
// the arguments of resize_width_height() to change the plot's behavior.
var plot_resize_setup = function () {
var plotid = Object.keys(Bokeh.index)[0]; // assume we have just one plot
var plot = Bokeh.index[plotid];
var plotresizer = function() {
// arguments: use width, use height, maintain aspect ratio
plot.resize_width_height(true, true, true);
};
window.addEventListener('resize', plotresizer);
plotresizer();
};
window.addEventListener('load', plot_resize_setup);
</script>
<style>
/* Need this to get the page in "desktop mode"; not having an infinite height.*/
html, body {height: 100%; margin:5px;}
</style>
<div id="interactiveplot" class="grid_6 omega">
{{ the_div |safe }}
</div>
</div>
</div>
{% endblock %}
+1 -1
View File
@@ -6,7 +6,7 @@
{% block main %} {% block main %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
+1 -1
View File
@@ -6,7 +6,7 @@
{% block main %} {% block main %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
-232
View File
@@ -1,232 +0,0 @@
{% load cookielaw_tags %}
{% load leaflet_tags %}
{% load tz_detect %}
{% tz_detect %}
{% load analytical %}
{% block filters %}
{% endblock %}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
{% block headtext %}{% endblock %}
<script src="/static/cookielaw/js/cookielaw.js"></script>
{% analytical_head_top %}
{% if GOOGLE_ANALYTICS_PROPERTY_ID %}
{% include "ga.html" %}
{% endif %}
<link rel="stylesheet" href="/static/css/bokeh-0.12.3.min.css" type="text/css" />
<link rel="stylesheet" href="/static/css/bokeh-widgets-0.12.3.min.css" type="text/css" />
<link rel="stylesheet" href="https://webapiv2.navionics.com/dist/webapi/webapi.min.css" >
<script type="text/javascript" src="https://webapiv2.navionics.com/dist/webapi/webapi.min.no-dep.js"></script>
<link rel="shortcut icon" href="/static/img/favicon.ico" type="image/x-icon" />
<link rel="icon" sizes="32x32" href="/static/img/favicon-32x32.png" type="image/png"/>
<link rel="icon" sizes="64x64" href="/static/img/favicon-64x64.png" type="image/png"/>
<link rel="icon" sizes="192x192" href="/static/img/favicon-192x192.png" type="image/png"/>
<link rel="icon" sizes="16x16" href="/static/img/favicon-16x16.png" type="image/png"/>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=0.67">
<title>{% block title %}Rowsandall{% endblock %}</title>
<link rel="stylesheet" href="/static/css/reset.css" />
<link rel="stylesheet" href="/static/css/text.css" />
<link rel="stylesheet" href="/static/css/960_12_col.css" />
<link rel="stylesheet" href="/static/css/rowsandall.css" />
<link rel="stylesheet" href="/static/css/leaflet.css" />
<script src="/static/css/leaflet.js" ></script>
{% block meta %} {% endblock %}
{% analytical_head_bottom %}
</head>
<body>
{% analytical_body_top %}
{% block body_top %}{% endblock %}
<div class="container_12">
<div class="grid_12">
&nbsp;
</div>
<div class="grid_12">
<div id="logo" class="grid_6 alpha">
{% if user.rower.rowerplan == 'pro' or user.rower.rowerplan == 'coach' %}
<p><a href="/"><img src="/static/img/logo7.png"
alt="Rowsandall logo" height="80"></a></p>
{% else %}
<p><a href="/"><img src="/static/img/logo7.png"
alt="Rowsandall logo" height="80"></a></p>
{% endif %}
</div>
<div class="grid_6 omega">
<div class="grid_4 alpha">
<div class="grid_1 alpha">
<p id="header">
<a class="button gray small" href="/rowers/videos">Videos</a></p>
</div>
<div class="grid_2">
<p id="header">
<a class="button gray small" href="http://analytics.rowsandall.com/">Rowing Analytics BLOG</a></p>
</div>
<div class="grid_1 omega">
<p id="header">
<a class="button gray small" href="/rowers/email">Contact</a>
</p>
</div>
<div class="grid_4 alpha">
<p>Free Data and Analysis. For Rowers. By Rowers.</p>
</div>
</div>
<div class="grid_1">
<div class="grid_1 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/me/edit">{{ user.first_name }}</a>
</p>
<span class="tooltiptext">Edit user account, e.g. heart rate zones, power zones, email, teams</span>
{% else %}
<p><a class="button gray small" href="{% url 'login' %}">login</a> </p>
{% endif %}
</div>
<div class="grid_1">
{% if user.is_authenticated %}
<p><a class="button gray small" href="{% url 'logout' %}">logout</a></p>
{% else %}
<p>&nbsp</p>
{% endif %}
</div>
</div>
<div class="grid_1 omega">
{% if user.rower.rowerplan == 'pro' or user.rower.rowerplan == 'coach' %}
<h6 class="graytext">Pro Member</h6>
{% else %}
<div class="grid_1"><a class="button green small" href="/rowers/paidplans">Upgrade to Pro</a></div>
{% endif %}
</div>
</div>
</div>
<div class="grid_12">
<div class="grid_1 alpha tooltip">
{% if user.is_authenticated %}
<p><a class="button gray small" href="/rowers/workout/upload/">Upload</a></p>
<span class="tooltiptext">Upload CSV, TCX, FIT data files to rowsandall.com</span>
{% else %}
<p><a class="button green small" href="/rowers/register">Register (free)</a></p>
{% endif %}
</div>
<div class="grid_1 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/imports/">Import</a>
</p>
<span class="tooltiptext">Import workouts from Strava, SportTracks, and C2 logbook</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_2 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/list-workouts/">Workouts</a>
</p>
<span class="tooltiptext">See your list of workouts</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_1 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/list-graphs/">Graphs</a>
</p>
<span class="tooltiptext">See your most recent charts</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_2 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/analysis">Analysis</a>
</p>
<span class="tooltiptext">Analysis of workouts over a period of time</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_1 tooltip">
{% block teams %}
{% endblock %}
</div>
</div>
<div class="clear"></div>
<div class="grid_12">
{% block message %}
{% if message %}
<p class="message">
{{ message }}
</p>
{% endif %}
{% if successmessage %}
<p class="successmessage">
{{ successmessage }}
</p>
{% endif %}
{% endblock %}
</div>
<div class="grid_12">
{% load tz %}
{% block content %}{% endblock %}
</div>
<div class="clear"></div>
<div class="grid_12 omega" >
{% block footer %}
<p id="footer">{{ versionstring }}</p>
<div class="grid_2 alpha">
<p id="footer"><a href="/rowers/email/">&copy; Sander Roosendaal</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/about">About</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/brochure">Brochure</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/developers">Developers</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/legal">Legal</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/partners">Partners</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/physics">Physics</a></p>
</div>
<div class="grid_2">
<p id="footer">
<a href="http://analytics.rowsandall.com/">Rowing Analytics BLOG</a></p>
</div>
<div class="grid_2 omega">
<p id="footer">
<a href="https://www.facebook.com/groups/rowsandall/">Facebook group</a></p>
</div>
{% endblock %}
</div>
{% cookielaw_banner %}
</div>
<!-- end container -->
{% analytical_body_bottom %}
{% block body_bottom %}{% endblock %}
</body>
</html>
-194
View File
@@ -1,194 +0,0 @@
{% load rowerfilters %}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<script src="/static/cookielaw/js/cookielaw.js"></script>
<link rel="stylesheet" href="/static/css/bokeh-0.12.3.min.css" type="text/css" />
<link rel="stylesheet" href="/static/css/bokeh-widgets-0.12.3.min.css" type="text/css" />
<link rel="shortcut icon" href="/static/img/myicon.png" />
<link rel="shortcut icon" href="/static/img/favicon.ico" />
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=0.67">
<title>Rowsandall</title>
<link rel="stylesheet" href="/static/css/reset.css" />
<link rel="stylesheet" href="/static/css/text.css" />
<link rel="stylesheet" href="/static/css/960_12_col.css" />
<link rel="stylesheet" href="/static/css/rowsandall.css" />
{% block meta %} {% endblock %}
</head>
<body>
<div class="container_12">
<div id="logo" class="grid_2">
{% if user|is_promember %}
<p><a href="/"><img src="/static/img/logocroppedpro.gif"
alt="Rowsandall logo" width="110" heigt="110"></a></p>
{% else %}
<p><a href="/"><img src="/static/img/logocropped.gif"
alt="Rowsandall logo" width="110" heigt="110"></a></p>
{% endif %}
</div>
<div class="grid_10 omega">
<div class="grid_8 alpha"><p>&nbsp</p></div>
<div class="grid_2 omega">
{% if user.is_authenticated %}
<p><a class="button gray small" href="/password_change/">Password Change</a></p>
{% else %}
<p><a class="button gray small" href="/password_reset/">Forgotten Password?</a></p>
{% endif %}
</div>
</div>
<div class="grid_10 omega">
<div class="grid_4 suffix_2 alpha">
<p>Free Data and Analysis. For Rowers. By Rowers.</p>
</div>
<div class="grid_3">
{% if user|is_promember %}
<h6>Pro Member</h6>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_1 omega">
{% if user.is_authenticated %}
<p><a class="button gray small" href="{% url 'logout' %}">logout</a></p>
{% else %}
<p>&nbsp</p>
{% endif %}
</div>
</div>
<div class="grid_10" omega>
<div class="grid_1 alpha tooltip">
{% if user.is_authenticated %}
<p><a class="button gray small" href="/rowers/workout/upload/">Upload</a></p>
<span class="tooltiptext">Upload CSV, TCX, FIT data files to rowsandall.com</span>
{% else %}
<p><a class="button green small" href="/rowers/register">Register (free)</a></p>
{% endif %}
</div>
<div class="grid_1 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/imports/">Import</a>
</p>
<span class="tooltiptext">Import workouts from Strava, SportTracks, and C2 logbook</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_2 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/list-workouts/">Workouts</a>
</p>
<span class="tooltiptext">See your list of workouts</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_2 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/list-graphs/">Graphs</a>
</p>
<span class="tooltiptext">See your most recent charts</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_2 suffix_1 tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/analysis">Analysis</a>
</p>
<span class="tooltiptext">Analysis of workouts over a period of time</span>
{% else %}
<p>&nbsp;</p>
{% endif %}
</div>
<div class="grid_1 omega tooltip">
{% if user.is_authenticated %}
<p>
<a class="button gray small" href="/rowers/me/edit">{{ user.first_name }}</a>
</p>
<span class="tooltiptext">Edit user data, e.g. heart rate zones</span>
{% else %}
<p><a class="button gray small" href="{% url 'login' %}">login</a> </p>
{% endif %}
</div>
</div>
<div class="clear"></div>
<div class="grid_12">
{% block message %}
{% if message %}
<p class="message">
{{ message }}
</p>
{% endif %}
{% if successmessage %}
<p class="successmessage">
{{ successmessage }}
</p>
{% endif %}
{% endblock %}
</div>
<div class="grid_12">
{% load tz %}
{% block content %}{% endblock %}
</div>
<div class="clear"></div>
<div class="grid_12 omega" >
{% block footer %}
<p id="footer"
>{{ versionstring }}</p>
<div class="grid_2 alpha">
<p id="footer"><a href="/rowers/email/">&copy; Sander Roosendaal</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/about">About</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/brochure">Brochure</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/developers">Developers</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/legal">Legal</a></p>
</div>
<div class="grid_1">
<p id="footer">
<a href="/rowers/physics">Physics</a></p>
</div>
<div class="grid_2">
<p id="footer">
<a href="/rowers/videos">Videos</a></p>
</div>
<div class="grid_2">
<p id="footer">
<a href="http://analytics.rowsandall.com/">Rowing Analytics BLOG</a></p>
</div>
<div class="grid_1 omega">
<p id="footer">
<a href="/rowers/email">Contact</a></p>
</div>
{% endblock %}
</div>
</div>
<!-- end container -->
</body>
</html>
-60
View File
@@ -1,60 +0,0 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}Rowsandall {% endblock %}
{% block content %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ interactiveplot |safe }}
<script>
// Set things up to resize the plot on a window resize. You can play with
// the arguments of resize_width_height() to change the plot's behavior.
var plot_resize_setup = function () {
var plotid = Object.keys(Bokeh.index)[0]; // assume we have just one plot
var plot = Bokeh.index[plotid];
var plotresizer = function() {
// arguments: use width, use height, maintain aspect ratio
plot.resize_width_height(true, false, false);
};
window.addEventListener('resize', plotresizer);
plotresizer();
};
window.addEventListener('load', plot_resize_setup);
</script>
<style>
/* Need this to get the page in "desktop mode"; not having an infinite height.*/
html, body {height: 100%; margin:5px;}
</style>
<div id="workouts" class="grid_12 alpha">
<h1>Interactive Plot</h1>
{% if user.is_authenticated and mayedit %}
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id|encode }}/edit">Edit Workout</a>
</p>
</div>
<div class="grid_2 suffix_2 omega">
<p>
<a class="button gray small" href="/rowers/workout/{{ workout.id|encode }}/advanced">Advanced Edit</a>
</p>
</div>
{% endif %}
{{ the_div|safe }}
</div>
{% endblock %}
+1 -1
View File
@@ -6,7 +6,7 @@
{% block main %} {% block main %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
-77
View File
@@ -1,77 +0,0 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %}Compare Workouts {% endblock %}
{% block content %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ interactiveplot |safe }}
<script>
// Set things up to resize the plot on a window resize. You can play with
// the arguments of resize_width_height() to change the plot's behavior.
var plot_resize_setup = function () {
var plotid = Object.keys(Bokeh.index)[0]; // assume we have just one plot
var plot = Bokeh.index[plotid];
var plotresizer = function() {
// arguments: use width, use height, maintain aspect ratio
plot.resize_width_height(true, true, false);
};
window.addEventListener('resize', plotresizer);
plotresizer();
};
window.addEventListener('load', plot_resize_setup);
</script>
<style>
/* Need this to get the page in "desktop mode"; not having an infinite height.*/
html, body {height: 100%; margin:5px;}
</style>
<div id="other" class="grid_12 alpha">
<div class="grid_2 alpha suffix_10">
<a class="button blue small"
href="/rowers/workout/compare/{{ id2 }}/{{ id1 }}/{{ xparam }}/{{ yparam }}">Swap Workouts</a>
</div>
</div>
<p>&nbsp;</p>
<div id="plotbuttons" class="grid_12 alpha">
<div id="x-axis" class="grid_6 alpha">
<div class="grid_2 alpha">
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/time/{{ yparam }}">Time</a>
</div>
<div class="grid_2 suffix_2 omega">
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/distance/{{ yparam }}">Distance</a>
</div>
</div>
<div id="y-axis" class="grid_6 omega">
<div class="grid_2 alpha">
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/pace">Pace</a>
</div>
<div class="grid_2">
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/hr">Heart Rate</a>
</div>
<div class="grid_2 omega">
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/spm">SPM</a>
</div>
</div>
</div>
<div id="theplot" class="grid_12 alpha">
{{ the_div|safe }}
</div>
{% endblock %}
-131
View File
@@ -1,131 +0,0 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %} Comparison Plot {% endblock %}
{% block content %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ interactiveplot |safe }}
<script>
// Set things up to resize the plot on a window resize. You can play with
// the arguments of resize_width_height() to change the plot's behavior.
var plot_resize_setup = function () {
var plotid = Object.keys(Bokeh.index)[0]; // assume we have just one plot
var plot = Bokeh.index[plotid];
var plotresizer = function() {
// arguments: use width, use height, maintain aspect ratio
plot.resize_width_height(true, true, false);
};
window.addEventListener('resize', plotresizer);
plotresizer();
};
window.addEventListener('load', plot_resize_setup);
</script>
<style>
/* Need this to get the page in "desktop mode"; not having an infinite height.*/
html, body {height: 100%; margin:5px;}
</style>
<div id="navigation" class="grid_12 alpha">
{% if user.is_authenticated and mayedit %}
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ id }}/edit">Edit Workout</a>
</p>
</div>
<div class="grid_2 suffix_8 omega">
<p>
<a class="button gray small" href="/rowers/workout/compare/{{ id }}/advanced">Advanced Edit</a>
</p>
</div>
{% endif %}
</div>
<div id="plotbuttons" class="grid_12 alpha">
<div id="x-axis" class="grid_6 alpha">
<div class="grid_2 alpha">
<p>
<a class="button blue small"
href="/rowers/workout/compare/{{ id2 }}/{{ id1 }}/{{ xparam }}/{{ yparam }}/{{ plottype }}">Swap Workouts</a>
</p>
</div>
<div class="grid_2 alpha dropdown">
<button class="grid_2 alpha button blue small dropbtn">X-axis</button>
<div class="dropdown-content">
{% for key, value in axchoicesbasic.items %}
{% if key != 'None' %}
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1}}/{{ id2 }}/{{ key }}/{{ yparam }}/{{ plottype }}">{{ value }}</a>
{% endif %}
{% endfor %}
{% if promember %}
{% for key, value in axchoicespro.items %}
<a class="button blue small alpha" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ key }}/{{ yparam }}/{{ plottype }}">{{ value }}</a>
{% endfor %}
{% else %}
{% for key, value in axchoicespro.items %}
<a class="button rosy small" href="/rowers/paidplans">{{ value }}</a>
{% endfor %}
{% endif %}
</div>
</div>
<div class="grid_2 omega dropdown">
<button class="grid_2 alpha button blue small dropbtn">Y-axis</button>
<div class="dropdown-content">
{% for key, value in axchoicesbasic.items %}
{% if key not in noylist and key != 'None' %}
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/{{ key }}/{{ plottype }}">{{ value }}</a>
{% endif %}
{% endfor %}
{% if promember %}
{% for key, value in axchoicespro.items %}
{% if key not in noylist %}
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/{{ key }}/{{ plottype }}">{{ value }}</a>
{% endif %}
{% endfor %}
{% else %}
{% for key, value in axchoicespro.items %}
{% if key not in noylist %}
<a class="button rosy small" href="/rowers/paidplans">{{ value }} (Pro)</a>
{% endif %}
{% endfor %}
{% endif %}
</div>
</div>
</div>
<div id="y-axis" class="grid_6 omega">
<div class="grid_2 prefix_2 alpha">
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/{{ yparam }}/line">Line Plot</a>
</div>
<div class="grid_2 omega">
<a class="button blue small" href="/rowers/workout/compare/{{ id1 }}/{{ id2 }}/{{ xparam }}/{{ yparam }}/scatter">Scatter Plot</a>
</div>
</div>
</div>
<div id="theplot" class="grid_12 alpha flexplot">
{{ the_div|safe }}
</div>
{% endblock %}
+4 -9
View File
@@ -58,18 +58,13 @@
</script> </script>
<div id="id_css_res"> <div id="id_css_res">
<link rel="stylesheet" href="/static/css/bokeh-0.12.3.min.css" type="text/css" /> <link rel="stylesheet" href="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.css" type="text/css" />
<link rel="stylesheet" href="/static/css/bokeh-widgets-0.12.3.min.css" type="text/css" /> <link rel="stylesheet" href="http://cdn.pydata.org/bokeh/release/bokeh-widgets-1.0.4.min.css" type="text/css" />
</div> </div>
<div id="id_js_res"> <div id="id_js_res">
<script <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"> <script src="http://cdn.pydata.org/bokeh/release/bokeh-widgets-1.0.4.min.js"></script>
</script>
<script
type="text/javascript" src="/static/js/bokeh-widgets-0.12.3.min.js">
</script>
</div> </div>
+4 -8
View File
@@ -60,16 +60,12 @@
<div id="id_css_res"> <div id="id_css_res">
<link rel="stylesheet" href="/static/css/bokeh-0.12.3.min.css" type="text/css" /> <link rel="stylesheet" href="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.css" type="text/css" />
<link rel="stylesheet" href="/static/css/bokeh-widgets-0.12.3.min.css" type="text/css" /> <link rel="stylesheet" href="http://cdn.pydata.org/bokeh/release/bokeh-widgets-1.0.4.min.css" type="text/css" />
</div> </div>
<div id="id_js_res"> <div id="id_js_res">
<script <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"> <script src="http://cdn.pydata.org/bokeh/release/bokeh-widgets-1.0.4.min.js"></script>
</script>
<script
type="text/javascript" src="/static/js/bokeh-widgets-0.12.3.min.js">
</script>
</div> </div>
+1 -1
View File
@@ -98,7 +98,7 @@
</li> </li>
{% endif %} {% endif %}
<li class="grid_2"> <li class="grid_2">
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
+1 -1
View File
@@ -48,7 +48,7 @@
}); });
</script> </script>
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
-182
View File
@@ -1,182 +0,0 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% load tz %}
{% block title %} Flexible Plot {% endblock %}
{% localtime on %}
{% block content %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script type="text/javascript" src="/static/js/bokeh-widgets-0.12.3.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ interactiveplot |safe }}
{{ widgetscript | safe }}
<script>
// Set things up to resize the plot on a window resize. You can play with
// the arguments of resize_width_height() to change the plot's behavior.
var plot_resize_setup = function () {
var plotid = Object.keys(Bokeh.index)[0]; // assume we have just one plot
var plot = Bokeh.index[plotid];
var plotresizer = function() {
// arguments: use width, use height, maintain aspect ratio
plot.resize_width_height(true, true, false);
};
window.addEventListener('resize', plotresizer);
plotresizer();
};
window.addEventListener('load', plot_resize_setup);
</script>
<style>
/* Need this to get the page in "desktop mode"; not having an infinite height.*/
html, body {height: 100%; margin:5px;}
</style>
<div id="navigation" class="grid_12 alpha">
{% if user.is_authenticated and mayedit %}
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ id }}/edit">Edit Workout</a>
</p>
</div>
<div class="grid_2 suffix_8 omega">
<p>
<a class="button gray small" href="/rowers/workout/{{ id }}/advanced">Advanced Edit</a>
</p>
</div>
{% endif %}
</div>
<p>&nbsp;</p>
<div id="plotbuttons" class="grid_12 alpha">
<div id="x-axis" class="grid_6 alpha">
<div class="grid_2 alpha dropdown">
<button class="grid_2 alpha button blue small dropbtn">X-axis</button>
<div class="dropdown-content">
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/time/{{ yparam1 }}/{{ yparam2 }}/{{ plottype }}">Time</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/distance/{{ yparam1 }}/{{ yparam2 }}/{{ plottype }}">Distance</a>
{% if promember %}
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/power/{{ yparam1 }}/{{ yparam2 }}/scatter">Power</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/hr/{{ yparam1 }}/{{ yparam2 }}/scatter">HR</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/spm/{{ yparam1 }}/{{ yparam2 }}/scatter">SPM</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/peakforce/{{ yparam1 }}/{{ yparam2 }}/scatter">Peak Force</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/averageforce/{{ yparam1 }}/{{ yparam2 }}/scatter">Average Force</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/drivelength/{{ yparam1 }}/{{ yparam2 }}/scatter">Drive Length</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/driveenergy/{{ yparam1 }}/{{ yparam2 }}/scatter">Work per Stroke</a>
<a class="button blue small alpha" href="/rowers/workout/{{ id }}/flexchart/drivespeed/{{ yparam1 }}/{{ yparam2 }}/scatter">Drive Speed</a>
{% else %}
<a class="button rosy small" href="/rowers/paidplans">Power (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">HR (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">SPM (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Peak Force (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Average Force (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Drive Length (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Work per Stroke (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Drive Speed (Pro)</a>
{% endif %}
</div>
</div>
<div class="grid_2 dropdown">
<button class="grid_2 alpha button blue small dropbtn">Left</button>
<div class="dropdown-content">
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/pace/{{ yparam2 }}/{{ plottype }}">Pace</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/hr/{{ yparam2 }}/{{ plottype }}">HR</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/spm/{{ yparam2 }}/{{ plottype }}">SPM</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/power/{{ yparam2 }}/{{ plottype }}">Power</a>
{% if promember %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/peakforce/{{ yparam2 }}/{{ plottype }}">Peak Force</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/averageforce/{{ yparam2 }}/{{ plottype }}">Average Force</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/drivelength/{{ yparam2 }}/{{ plottype }}">Drive Length</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/driveenergy/{{ yparam2 }}/{{ plottype }}">Work per Stroke</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/drivespeed/{{ yparam2 }}/{{ plottype }}">Drive Speed</a>
{% else %}
<a class="button rosy small" href="/rowers/paidplans">Peak Force (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Average Force (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Drive Length (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Work per Stroke (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Drive Speed (Pro)</a>
{% endif %}
</div>
</div>
<div class="grid_2 dropdown omega">
<button class="grid_2 alpha button blue small dropbtn">Right</button>
<div class="dropdown-content">
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/hr/{{ plottype }}">HR</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/spm/{{ plottype }}">SPM</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/power/{{ plottype }}">Power</a>
{% if promember %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/peakforce/{{ plottype }}">Peak Force</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/averageforce/{{ plottype }}">Average Force</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/drivelength/{{ plottype }}">Drive Length</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/driveenergy/{{ plottype }}">Work per Stroke</a>
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/drivespeed/{{ plottype }}">Drive Speed</a>
{% else %}
<a class="button rosy small" href="/rowers/paidplans">Peak Force (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Average Force (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Drive Length (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Work per Stroke (Pro)</a>
<a class="button rosy small" href="/rowers/paidplans">Drive Speed (Pro)</a>
{% endif %}
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/None/{{ plottype }}">None</a>
</div>
</div>
</div>
<div id="y-axis" class="grid_6 omega">
<div class="grid_2 alpha tooltip">
<form enctype="multipart/form-data" action="{{ formloc }}" method="post">
{% csrf_token %}
{% if workstrokesonly %}
<input type="hidden" name="workstrokesonly" value="True">
{% else %}
<input class="grid_2 alpha button blue small" type="hidden" name="workstrokesonly" value="False">
{% endif %}
<input class="grid_2 alpha button blue small" value="Toggle Work Strokes" type="Submit">
</form>
<span class="tooltiptext">If your data source allows, this will show or hide strokes taken during rest intervals.</span>
</div>
<div class="grid_2">
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/{{ yparam2 }}/line">Line Plot</a>
</div>
<div class="grid_2 omega">
<a class="button blue small" href="/rowers/workout/{{ id }}/flexchart/{{ xparam }}/{{ yparam1 }}/{{ yparam2 }}/scatter">Scatter Plot</a>
</div>
</div>
</div>
<div id="theplot" class="grid_12 alpha flexplot">
{{ the_div|safe }}
</div>
<div id="thewidgets" class="grid_12 alpha">
{{ widgetdiv1|safe }}
{{ widgetdiv2|safe }}
</div>
{% endblock %}
{% endlocaltime %}
+2 -2
View File
@@ -11,8 +11,8 @@
{{ js_res | safe }} {{ js_res | safe }}
{{ css_res| safe }} {{ css_res| safe }}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script type="text/javascript" src="/static/js/bokeh-widgets-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-widgets-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
+2 -2
View File
@@ -11,8 +11,8 @@
{{ js_res | safe }} {{ js_res | safe }}
{{ css_res| safe }} {{ css_res| safe }}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script type="text/javascript" src="/static/js/bokeh-widgets-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-widgets-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
+4 -9
View File
@@ -60,17 +60,12 @@
<div id="id_css_res"> <div id="id_css_res">
<link rel="stylesheet" href="/static/css/bokeh-0.12.3.min.css" type="text/css" /> <link rel="stylesheet" href="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.css" type="text/css" />
<link rel="stylesheet" href="/static/css/bokeh-widgets-0.12.3.min.css" type="text/css" /> <link rel="stylesheet" href="http://cdn.pydata.org/bokeh/release/bokeh-widgets-1.0.4.min.css" type="text/css" />
</div> </div>
<div id="id_js_res"> <div id="id_js_res">
<script <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"> <script src="http://cdn.pydata.org/bokeh/release/bokeh-widgets-1.0.4.min.js"></script>
</script>
<script
type="text/javascript" src="/static/js/bokeh-widgets-0.12.3.min.js">
</script>
</div> </div>
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script> <script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
+1 -1
View File
@@ -6,7 +6,7 @@
{% block main %} {% block main %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
+1 -1
View File
@@ -59,7 +59,7 @@
<ul class="main-content"> <ul class="main-content">
<li class="grid_2" style="min-height:200px;"> <li class="grid_2" style="min-height:200px;">
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
+1 -1
View File
@@ -6,7 +6,7 @@
{% block main %} {% block main %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
+1 -1
View File
@@ -6,7 +6,7 @@
{% block main %} {% block main %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
+1 -1
View File
@@ -6,7 +6,7 @@
{% block main %} {% block main %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
+1 -1
View File
@@ -10,7 +10,7 @@
{% block main %} {% block main %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
+1 -1
View File
@@ -138,7 +138,7 @@
<div id="advancedplots" class="grid_6 omega"> <div id="advancedplots" class="grid_6 omega">
<div class="grid_6 alpha"> <div class="grid_6 alpha">
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
+1 -1
View File
@@ -6,7 +6,7 @@
{% block main %} {% block main %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
+1 -1
View File
@@ -10,7 +10,7 @@
{% block main %} {% block main %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
+1 -1
View File
@@ -13,7 +13,7 @@
{% block main %} {% block main %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
+1 -1
View File
@@ -23,7 +23,7 @@
</li> </li>
<li class="grid_2"> <li class="grid_2">
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
+1 -1
View File
@@ -31,7 +31,7 @@
<img src="/static/img/rivercurrent.jpg" width="400"> <img src="/static/img/rivercurrent.jpg" width="400">
</li> </li>
<li class="grid_2"> <li class="grid_2">
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
+1 -1
View File
@@ -62,7 +62,7 @@
</form> </form>
</li> </li>
<li class="grid_2"> <li class="grid_2">
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
-54
View File
@@ -1,54 +0,0 @@
{% extends "base.html" %}
{% load staticfiles %}
{% load rowerfilters %}
{% block title %} Flexible Plot {% endblock %}
{% block content %}
{{ js_res | safe }}
{{ css_res| safe }}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
<script type="text/javascript" src="/static/js/bokeh-widgets-0.12.3.min.js"></script>
<script async="true" type="text/javascript">
Bokeh.set_log_level("info");
</script>
{{ the_script |safe }}
<style>
/* Need this to get the page in "desktop mode"; not having an infinite height.*/
html, body {height: 100%; margin:5px;}
</style>
<div id="navigation" class="grid_12 alpha">
{% if user.is_authenticated and mayedit %}
<div class="grid_2 alpha">
<p>
<a class="button gray small" href="/rowers/workout/{{ id }}/edit">Edit Workout</a>
</p>
</div>
<div class="grid_2 suffix_8 omega">
<p>
<a class="button gray small" href="/rowers/workout/{{ id }}/advanced">Advanced Edit</a>
</p>
</div>
{% endif %}
</div>
<p>&nbsp;</p>
<div id="theplot" class="grid_12 alpha">
{{ the_div|safe }}
</div>
{% endblock %}
+1 -1
View File
@@ -53,7 +53,7 @@
</li> </li>
<li class="grid_2"> <li class="grid_2">
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
+1 -1
View File
@@ -13,7 +13,7 @@
{{ workout.date }} - {{ workout.distance }}m - {{ workout.duration |durationprint:"%H:%M:%S.%f" }}{% endblock %} {{ workout.date }} - {{ workout.distance }}m - {{ workout.duration |durationprint:"%H:%M:%S.%f" }}{% endblock %}
{% block meta %} {% block meta %}
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
+1 -1
View File
@@ -86,7 +86,7 @@
</li> </li>
<li class="grid_2"> <li class="grid_2">
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
+1 -1
View File
@@ -130,7 +130,7 @@ $('#id_workouttype').change();
{% endif %} {% endif %}
{% if mapdiv %} {% if mapdiv %}
<li class="grid_2"> <li class="grid_2">
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
+1 -1
View File
@@ -121,7 +121,7 @@
</li> </li>
{% endif %} {% endif %}
<li class="grid_2"> <li class="grid_2">
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script> <script src="http://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script>
<script async="true" type="text/javascript"> <script async="true" type="text/javascript">
Bokeh.set_log_level("info"); Bokeh.set_log_level("info");
</script> </script>
+27 -22
View File
@@ -31,15 +31,16 @@ from rowers.opaque import encoder
import arrow import arrow
from six import string_types
def strfdelta(tdelta): def strfdelta(tdelta):
minutes,seconds = divmod(tdelta.seconds,60) minutes,seconds = divmod(tdelta.seconds,60)
tenths = int(tdelta.microseconds/1e5) tenths = int(tdelta.microseconds/1e5)
res = "{minutes:0>1}:{seconds:0>2}.{tenths:0>1}".format( res = "{minutes:0>1}:{seconds:0>2}.{tenths:0>1}".format(
minutes=minutes, minutes=minutes,
seconds=seconds, seconds=seconds,
tenths=tenths, tenths=tenths,
) )
return res return res
@@ -48,11 +49,11 @@ def strfdeltah(tdelta):
minutes,seconds = divmod(rest,60) minutes,seconds = divmod(rest,60)
tenths = int(tdelta.microseconds/1e5) tenths = int(tdelta.microseconds/1e5)
res = "{hours:0>2}:{minutes:0>2}:{seconds:0>2}.{tenths:0>1}".format( res = "{hours:0>2}:{minutes:0>2}:{seconds:0>2}.{tenths:0>1}".format(
hours=hours, hours=hours,
minutes=minutes, minutes=minutes,
seconds=seconds, seconds=seconds,
tenths=tenths, tenths=tenths,
) )
return res return res
@@ -60,10 +61,10 @@ def secondstotimestring(tdelta):
hours, rest = divmod(tdelta,3600) hours, rest = divmod(tdelta,3600)
minutes,seconds = divmod(rest,60) minutes,seconds = divmod(rest,60)
res = "{hours:0>2}:{minutes:0>2}:{seconds:0>2}".format( res = "{hours:0>2}:{minutes:0>2}:{seconds:0>2}".format(
hours=hours, hours=hours,
minutes=minutes, minutes=minutes,
seconds=seconds, seconds=seconds,
) )
return res return res
@@ -87,7 +88,11 @@ def aantalcomments(workout):
@register.filter @register.filter
def encode(id): def encode(id):
return str(encoder.encode_hex(id)) try:
result = str(encoder.encode_hex(id))
except TypeError:
result = ''
return result
@register.filter @register.filter
def water(workout): def water(workout):
@@ -107,9 +112,9 @@ def spacetohtml(t):
@register.filter @register.filter
def durationprint(d,dstring): def durationprint(d,dstring):
if (d == None): if (d == None):
return d return d
else: else:
return d.strftime(dstring)[:-5] return d.strftime(dstring)[:-5]
def getstartenddate(timeperiod): def getstartenddate(timeperiod):
s,e = timeperiod.split('/') s,e = timeperiod.split('/')
@@ -165,16 +170,16 @@ def previousperiodstart(timeperiod):
@register.filter @register.filter
def paceprint(d): def paceprint(d):
if (d == None): if (d == None):
return d return d
else: else:
return strfdelta(d) return strfdelta(d)
@register.filter @register.filter
def deltatimeprint(d): def deltatimeprint(d):
if (d == None): if (d == None):
return d return d
else: else:
return strfdeltah(d) return strfdeltah(d)
@register.filter @register.filter
def c2userid(user): def c2userid(user):
@@ -220,8 +225,8 @@ def jsdict(dict,key):
def lookup(dict, key): def lookup(dict, key):
s = dict.get(key) s = dict.get(key)
if isinstance(s,basestring) and len(s) > 22: if isinstance(s,string_types) and len(s) > 22:
s = s[:22] s = s[:22]
return s return s
@register.filter @register.filter
+13 -8
View File
@@ -1,3 +1,8 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import pytest import pytest
pytestmark = pytest.mark.django_db pytestmark = pytest.mark.django_db
@@ -31,7 +36,7 @@ from rowers.utils import NoTokenError
from shutil import copyfile from shutil import copyfile
from nose.tools import assert_true from nose.tools import assert_true
from mock import Mock, patch from mock import Mock, patch
from minimocktest import MockTestCase #from minimocktest import MockTestCase
import pandas as pd import pandas as pd
import rowers.c2stuff as c2stuff import rowers.c2stuff as c2stuff
import arrow import arrow
@@ -98,15 +103,15 @@ def mocked_sqlalchemy(*args, **kwargs):
return MockEngine() return MockEngine()
#@pytest.mark.django_db #@pytest.mark.django_db
class DjangoTestCase(TestCase, MockTestCase): class DjangoTestCase(TestCase): #, MockTestCase):
def _pre_setup(self): def _pre_setup(self):
MockTestCase.setUp(self) # MockTestCase.setUp(self)
TestCase._pre_setup(self) TestCase._pre_setup(self)
self.client = Client() self.client = Client()
def _post_teardown(self): def _post_teardown(self):
TestCase._post_teardown(self) TestCase._post_teardown(self)
MockTestCase.tearDown(self) #MockTestCase.tearDown(self)
# delete_strokedata(1) # delete_strokedata(1)
def mocked_tcx_parser(*args, **kwargs): def mocked_tcx_parser(*args, **kwargs):
@@ -220,7 +225,7 @@ def mocked_read_df_cols_sql_multiflex(ids, columns, convertnewtons=True):
def mocked_stravaexport(f2,workoutname,stravatoken,description='', def mocked_stravaexport(f2,workoutname,stravatoken,description='',
activity_type='Rowing'): activity_type='Rowing'):
print "this is mocked strava export" print("this is mocked strava export")
return 1,'success' return 1,'success'
def mocked_fetchcp(*args, **kwargs): def mocked_fetchcp(*args, **kwargs):
+6 -1
View File
@@ -3,6 +3,11 @@ try:
except NameError: except NameError:
WindowsError = None WindowsError = None
try:
FileNotFoundError
except NameError:
FileNotFoundError = None
try: try:
OSError OSError
except NameError: except NameError:
@@ -44,7 +49,7 @@ from rowers.plannedsessions import get_dates_timeperiod
from shutil import copyfile, copy from shutil import copyfile, copy
from nose.tools import assert_true from nose.tools import assert_true
from mock import Mock, patch from mock import Mock, patch
from minimocktest import MockTestCase #from minimocktest import MockTestCase
import pandas as pd import pandas as pd
import rowers.c2stuff as c2stuff import rowers.c2stuff as c2stuff
+15 -10
View File
@@ -1,5 +1,10 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
#from __future__ import print_function #from __future__ import print_function
from statements import * from .statements import *
from django.utils import timezone from django.utils import timezone
nu = datetime.datetime.now(tz=timezone.utc) nu = datetime.datetime.now(tz=timezone.utc)
@@ -327,7 +332,7 @@ class VirtualEventViewTest(TestCase):
def tearDown(self): def tearDown(self):
try: try:
os.remove('rowers/tests/testdata/temp/course.kml') os.remove('rowers/tests/testdata/temp/course.kml')
except (WindowsError, OSError, IOError): except (FileNotFoundError, OSError, IOError):
pass pass
@@ -408,7 +413,7 @@ class VirtualEventViewTest(TestCase):
form = IndoorVirtualRaceForm(form_data) form = IndoorVirtualRaceForm(form_data)
if not form.is_valid(): if not form.is_valid():
print form.errors print(form.errors)
self.assertTrue(form.is_valid()) self.assertTrue(form.is_valid())
response = self.c.post(url,form_data,follow=True) response = self.c.post(url,form_data,follow=True)
@@ -452,7 +457,7 @@ class VirtualEventViewTest(TestCase):
form = IndoorVirtualRaceForm(form_data) form = IndoorVirtualRaceForm(form_data)
if not form.is_valid(): if not form.is_valid():
print form.errors print(form.errors)
self.assertTrue(form.is_valid()) self.assertTrue(form.is_valid())
response = self.c.post(url,form_data,follow=True) response = self.c.post(url,form_data,follow=True)
@@ -496,7 +501,7 @@ class VirtualEventViewTest(TestCase):
form = VirtualRaceForm(form_data) form = VirtualRaceForm(form_data)
if not form.is_valid(): if not form.is_valid():
print form.errors print(form.errors)
self.assertTrue(form.is_valid()) self.assertTrue(form.is_valid())
response = self.c.post(url,form_data,follow=True) response = self.c.post(url,form_data,follow=True)
@@ -533,7 +538,7 @@ class VirtualEventViewTest(TestCase):
form = VirtualRaceForm(form_data) form = VirtualRaceForm(form_data)
if not form.is_valid(): if not form.is_valid():
print form.errors print(form.errors)
self.assertTrue(form.is_valid()) self.assertTrue(form.is_valid())
response = self.c.post(url,form_data,follow=True) response = self.c.post(url,form_data,follow=True)
@@ -588,7 +593,7 @@ class VirtualEventViewTest(TestCase):
form = IndoorVirtualRaceResultForm(form_data) form = IndoorVirtualRaceResultForm(form_data)
if not form.is_valid(): if not form.is_valid():
print form.errors print(form.errors)
self.assertTrue(form.is_valid()) self.assertTrue(form.is_valid())
response = self.c.post(url,form_data,follow=True) response = self.c.post(url,form_data,follow=True)
@@ -617,7 +622,7 @@ class VirtualEventViewTest(TestCase):
form = IndoorVirtualRaceResultForm(form_data) form = IndoorVirtualRaceResultForm(form_data)
if not form.is_valid(): if not form.is_valid():
print form.errors print(form.errors)
self.assertTrue(form.is_valid()) self.assertTrue(form.is_valid())
response = self.c.post(url,form_data,follow=True) response = self.c.post(url,form_data,follow=True)
@@ -666,7 +671,7 @@ class VirtualEventViewTest(TestCase):
form = VirtualRaceResultForm(form_data) form = VirtualRaceResultForm(form_data)
if not form.is_valid(): if not form.is_valid():
print form.errors print(form.errors)
self.assertTrue(form.is_valid()) self.assertTrue(form.is_valid())
response = self.c.post(url,form_data,follow=True) response = self.c.post(url,form_data,follow=True)
@@ -694,7 +699,7 @@ class VirtualEventViewTest(TestCase):
form = VirtualRaceResultForm(form_data) form = VirtualRaceResultForm(form_data)
if not form.is_valid(): if not form.is_valid():
print form.errors print(form.errors)
self.assertTrue(form.is_valid()) self.assertTrue(form.is_valid())
response = self.c.post(url,form_data,follow=True) response = self.c.post(url,form_data,follow=True)
+10 -5
View File
@@ -1,5 +1,10 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
#from __future__ import print_function #from __future__ import print_function
from statements import * from .statements import *
nu = datetime.datetime.now() nu = datetime.datetime.now()
from rowers.models import User,Rower from rowers.models import User,Rower
@@ -8,10 +13,10 @@ from rowers.models import User,Rower
@pytest.mark.django_db @pytest.mark.django_db
class AdminTests(TestCase): class AdminTests(TestCase):
def setUp(self): def setUp(self):
u = User.objects.create_user('john', u = User.objects.create_user('john',
'sander@ds.ds', 'sander@ds.ds',
'koeinsloot') 'koeinsloot')
r = Rower.objects.create(user=u) r = Rower.objects.create(user=u)
my_admin = User.objects.create_superuser('myuser', my_admin = User.objects.create_superuser('myuser',
'myemail@test.com', 'myemail@test.com',
'apensstaart') 'apensstaart')
+16 -11
View File
@@ -1,4 +1,9 @@
from statements import * from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from .statements import *
nu = datetime.datetime.now() nu = datetime.datetime.now()
@@ -26,7 +31,7 @@ class WorkoutCompareTest(TestCase):
for workout in self.user_workouts: for workout in self.user_workouts:
try: try:
os.remove(workout.csvfilename) os.remove(workout.csvfilename)
except (IOError, WindowsError,OSError): except (IOError, FileNotFoundError, OSError):
pass pass
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@@ -91,7 +96,7 @@ class BoxPlotTest(TestCase):
for workout in self.user_workouts: for workout in self.user_workouts:
try: try:
os.remove(workout.csvfilename) os.remove(workout.csvfilename)
except (IOError, WindowsError,OSError): except (IOError, FileNotFoundError,OSError):
pass pass
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@@ -110,7 +115,7 @@ class BoxPlotTest(TestCase):
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@patch('rowers.dataprep.getsmallrowdata_db') @patch('rowers.dataprep.getsmallrowdata_db')
@patch('rowers.dataprep.read_cols_df_sql', side_effect = mocked_read_df_cols_sql_multi) @patch('rowers.dataprep.read_cols_df_sql', side_effect = mocked_read_df_cols_sql_multi)
def test_workouts_boxplot_submit(self, mocked_sqlalchemy, def donot_test_workouts_boxplot_submit(self, mocked_sqlalchemy,
mocked_getsmallrowdata_db, mocked_getsmallrowdata_db,
mocked_df): mocked_df):
@@ -188,7 +193,7 @@ class ListWorkoutTest(TestCase):
for workout in self.user_workouts: for workout in self.user_workouts:
try: try:
os.remove(workout.csvfilename) os.remove(workout.csvfilename)
except (IOError, WindowsError,OSError): except (IOError, FileNotFoundError,OSError):
pass pass
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@@ -246,7 +251,7 @@ class PlannedSessionTests(TestCase):
for workout in self.user_workouts: for workout in self.user_workouts:
try: try:
os.remove(workout.csvfilename) os.remove(workout.csvfilename)
except (IOError, WindowsError,OSError): except (IOError, FileNotFoundError,OSError):
pass pass
def test_ics_download(self): def test_ics_download(self):
@@ -291,7 +296,7 @@ class ForcecurveTest(TestCase):
for workout in self.user_workouts: for workout in self.user_workouts:
try: try:
os.remove(workout.csvfilename) os.remove(workout.csvfilename)
except (IOError, WindowsError,OSError): except (IOError, FileNotFoundError,OSError):
pass pass
@patch('rowers.dataprep.getsmallrowdata_db',side_effect = mocked_getempowerdata_db) @patch('rowers.dataprep.getsmallrowdata_db',side_effect = mocked_getempowerdata_db)
@@ -326,7 +331,7 @@ class CumStatsTest(TestCase):
for workout in self.user_workouts: for workout in self.user_workouts:
try: try:
os.remove(workout.csvfilename) os.remove(workout.csvfilename)
except (IOError, WindowsError,OSError): except (IOError, FileNotFoundError,OSError):
pass pass
@patch('rowers.dataprep.read_cols_df_sql', side_effect = mocked_read_df_cols_sql_multistats) @patch('rowers.dataprep.read_cols_df_sql', side_effect = mocked_read_df_cols_sql_multistats)
@@ -363,7 +368,7 @@ class CumFlexTest(TestCase):
for workout in self.user_workouts: for workout in self.user_workouts:
try: try:
os.remove(workout.csvfilename) os.remove(workout.csvfilename)
except (IOError, WindowsError,OSError): except (IOError, FileNotFoundError,OSError):
pass pass
@patch('rowers.dataprep.read_cols_df_sql', side_effect = mocked_read_df_cols_sql_multiflex) @patch('rowers.dataprep.read_cols_df_sql', side_effect = mocked_read_df_cols_sql_multiflex)
@@ -468,7 +473,7 @@ class MultiFlexTest(TestCase):
for workout in self.user_workouts: for workout in self.user_workouts:
try: try:
os.remove(workout.csvfilename) os.remove(workout.csvfilename)
except (IOError, WindowsError,OSError): except (IOError, FileNotFoundError,OSError):
pass pass
@patch('rowers.dataprep.read_cols_df_sql', side_effect = mocked_read_df_cols_sql_multiflex) @patch('rowers.dataprep.read_cols_df_sql', side_effect = mocked_read_df_cols_sql_multiflex)
@@ -570,7 +575,7 @@ class HistoTest(TestCase):
for workout in self.user_workouts: for workout in self.user_workouts:
try: try:
os.remove(workout.csvfilename) os.remove(workout.csvfilename)
except (IOError, WindowsError,OSError): except (IOError, FileNotFoundError,OSError):
pass pass
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
+7 -2
View File
@@ -1,4 +1,9 @@
from statements import * from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from .statements import *
nu = datetime.datetime.now() nu = datetime.datetime.now()
@@ -211,7 +216,7 @@ class WorkoutViewTest(TestCase):
'userid':self.u.id, 'userid':self.u.id,
'id':encoder.encode_hex(self.werg1.id), 'id':encoder.encode_hex(self.werg1.id),
}) })
print url print(url)
form_data = { form_data = {
'xparam':'time', 'xparam':'time',
+7 -2
View File
@@ -1,4 +1,9 @@
from statements import * from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from .statements import *
nu = datetime.datetime.now() nu = datetime.datetime.now()
@@ -28,7 +33,7 @@ class SimpleViewTest(TestCase):
for workout in self.user_workouts: for workout in self.user_workouts:
try: try:
os.remove(workout.csvfilename) os.remove(workout.csvfilename)
except (IOError, WindowsError,OSError): except (IOError, FileNotFoundError,OSError):
pass pass
def test_start_protrial(self): def test_start_protrial(self):
+11 -6
View File
@@ -1,16 +1,21 @@
from statements import * from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from .statements import *
class CoursesTest(TestCase): class CoursesTest(TestCase):
def setUp(self): def setUp(self):
self.c = Client() self.c = Client()
self.u = User.objects.create_user('john', self.u = User.objects.create_user('john',
'sander@ds.ds', 'sander@ds.ds',
'koeinsloot') 'koeinsloot')
self.r = Rower.objects.create(user=self.u,gdproptin=True, self.r = Rower.objects.create(user=self.u,gdproptin=True,
gdproptindate=timezone.now(), gdproptindate=timezone.now(),
rowerplan='coach', rowerplan='coach',
) )
self.nu = datetime.datetime.now() self.nu = datetime.datetime.now()
def test_courses(self): def test_courses(self):
+9 -5
View File
@@ -1,5 +1,9 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from statements import * from .statements import *
nu = datetime.datetime.now() nu = datetime.datetime.now()
@@ -77,9 +81,9 @@ class OTWCPChartTest(TestCase):
if not os.path.isdir(path): if not os.path.isdir(path):
try: try:
os.remove(path) os.remove(path)
except (IOError,WindowsError,OSError): except (IOError,FileNotFoundError,OSError):
pass pass
except (IOError,WindowsError,OSError): except (IOError,FileNotFoundError,OSError):
pass pass
@patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_cpraw) @patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_cpraw)
@@ -157,7 +161,7 @@ class CPChartTest(TestCase):
for workout in self.user_workouts: for workout in self.user_workouts:
try: try:
os.remove(workout.csvfilename) os.remove(workout.csvfilename)
except (IOError, WindowsError,OSError): except (IOError, FileNotFoundError,OSError):
pass pass
def test_analytics_page(self): def test_analytics_page(self):
@@ -250,7 +254,7 @@ class CPChartTest(TestCase):
sex = r.sex, sex = r.sex,
weightc = r.weightcategory) weightc = r.weightcategory)
print url print(url)
response = self.c.get(url) response = self.c.get(url)
self.assertEqual(response.status_code,200) self.assertEqual(response.status_code,200)
+24 -20
View File
@@ -1,14 +1,18 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
#from __future__ import print_function #from __future__ import print_function
from statements import * from .statements import *
@override_settings(TESTING=True) @override_settings(TESTING=True)
class EmailUpload(TestCase): class EmailUpload(TestCase):
def setUp(self): def setUp(self):
redis_connection.publish('tasks','KILL') redis_connection.publish('tasks','KILL')
u = User.objects.create_user('john', u = User.objects.create_user('john',
'sander@ds.ds', 'sander@ds.ds',
'koeinsloot') 'koeinsloot')
r = Rower.objects.create(user=u,gdproptin=True, r = Rower.objects.create(user=u,gdproptin=True,
gdproptindate=timezone.now() gdproptindate=timezone.now()
) )
@@ -19,7 +23,7 @@ class EmailUpload(TestCase):
gdproptindate=timezone.now(), gdproptindate=timezone.now(),
rowerplan='coach') rowerplan='coach')
nu = datetime.datetime.now() nu = datetime.datetime.now()
workoutsbox = Mailbox.objects.create(name='workouts') workoutsbox = Mailbox.objects.create(name='workouts')
workoutsbox.save() workoutsbox.save()
failbox = Mailbox.objects.create(name='Failed') failbox = Mailbox.objects.create(name='Failed')
@@ -43,7 +47,7 @@ workout run
if not os.path.isdir(path): if not os.path.isdir(path):
try: try:
os.remove(path) os.remove(path)
except (IOError,WindowsError,OSError): except (IOError,FileNotFoundError,OSError):
pass pass
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@@ -69,10 +73,10 @@ workout run
class EmailTests(TestCase): class EmailTests(TestCase):
def setUp(self): def setUp(self):
redis_connection.publish('tasks','KILL') redis_connection.publish('tasks','KILL')
u = User.objects.create_user('john', u = User.objects.create_user('john',
'sander@ds.ds', 'sander@ds.ds',
'koeinsloot') 'koeinsloot')
r = Rower.objects.create(user=u,gdproptin=True, r = Rower.objects.create(user=u,gdproptin=True,
gdproptindate=timezone.now() gdproptindate=timezone.now()
) )
@@ -83,7 +87,7 @@ class EmailTests(TestCase):
gdproptindate=timezone.now(), gdproptindate=timezone.now(),
rowerplan='coach') rowerplan='coach')
nu = datetime.datetime.now() nu = datetime.datetime.now()
workoutsbox = Mailbox.objects.create(name='workouts') workoutsbox = Mailbox.objects.create(name='workouts')
workoutsbox.save() workoutsbox.save()
failbox = Mailbox.objects.create(name='Failed') failbox = Mailbox.objects.create(name='Failed')
@@ -133,7 +137,7 @@ race 1
if not os.path.isdir(path): if not os.path.isdir(path):
try: try:
os.remove(path) os.remove(path)
except (IOError,WindowsError,OSError): except (IOError,FileNotFoundError,OSError):
pass pass
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@@ -152,10 +156,10 @@ race 1
class EmailAdminUpload(TestCase): class EmailAdminUpload(TestCase):
def setUp(self): def setUp(self):
redis_connection.publish('tasks','KILL') redis_connection.publish('tasks','KILL')
u = User.objects.create_user('john', u = User.objects.create_user('john',
'sander@ds.ds', 'sander@ds.ds',
'koeinsloot') 'koeinsloot')
r = Rower.objects.create(user=u,gdproptin=True, r = Rower.objects.create(user=u,gdproptin=True,
gdproptindate=timezone.now(), gdproptindate=timezone.now(),
birthdate = faker.profile()['birthdate'] birthdate = faker.profile()['birthdate']
) )
@@ -169,7 +173,7 @@ class EmailAdminUpload(TestCase):
self.race = RaceFactory(manager = self.theadmin) self.race = RaceFactory(manager = self.theadmin)
nu = datetime.datetime.now() nu = datetime.datetime.now()
workoutsbox = Mailbox.objects.create(name='workouts') workoutsbox = Mailbox.objects.create(name='workouts')
workoutsbox.save() workoutsbox.save()
failbox = Mailbox.objects.create(name='Failed') failbox = Mailbox.objects.create(name='Failed')
@@ -194,7 +198,7 @@ race 1
if not os.path.isdir(path): if not os.path.isdir(path):
try: try:
os.remove(path) os.remove(path)
except (IOError,WindowsError,OSError): except (IOError,FileNotFoundError,OSError):
pass pass
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
@@ -210,7 +214,7 @@ race 1
ws = Workout.objects.filter(name="johnsworkout") ws = Workout.objects.filter(name="johnsworkout")
if not len(ws): if not len(ws):
for w in Workout.objects.all(): for w in Workout.objects.all():
print w print(w)
self.assertEqual(len(ws),1) self.assertEqual(len(ws),1)
+34 -30
View File
@@ -1,35 +1,39 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
#from __future__ import print_function #from __future__ import print_function
from statements import * from .statements import *
class EmpowerTest(TestCase): class EmpowerTest(TestCase):
def setUp(self): def setUp(self):
redis_connection.publish('tasks','KILL') redis_connection.publish('tasks','KILL')
u = User.objects.create_user('john', u = User.objects.create_user('john',
'sander@ds.ds', 'sander@ds.ds',
'koeinsloot', 'koeinsloot',
) )
r = Rower.objects.create(user=u,rowerplan='coach',gdproptin=True, r = Rower.objects.create(user=u,rowerplan='coach',gdproptin=True,
gdproptindate=timezone.now()) gdproptindate=timezone.now())
self.c = Client() self.c = Client()
self.nu = datetime.datetime.now() self.nu = datetime.datetime.now()
filename = 'rowers/tests/testdata/testdata.csv' filename = 'rowers/tests/testdata/testdata.csv'
otwfilename = 'rowers/tests/testdata/empower.csv' otwfilename = 'rowers/tests/testdata/empower.csv'
self.wotw = Workout.objects.create(name='testworkout', self.wotw = Workout.objects.create(name='testworkout',
workouttype='water', workouttype='water',
user=r,date=self.nu.strftime('%Y-%m-%d'), user=r,date=self.nu.strftime('%Y-%m-%d'),
starttime=self.nu.strftime('%H:%M:%S'), starttime=self.nu.strftime('%H:%M:%S'),
duration="0:55:00",distance=8000, duration="0:55:00",distance=8000,
csvfilename=filename) csvfilename=filename)
self.wote = Workout.objects.create(name='testworkout', self.wote = Workout.objects.create(name='testworkout',
workouttype='Indoor Rower', workouttype='Indoor Rower',
user=r,date=self.nu.strftime('%Y-%m-%d'), user=r,date=self.nu.strftime('%Y-%m-%d'),
starttime=self.nu.strftime('%H:%M:%S'), starttime=self.nu.strftime('%H:%M:%S'),
duration="0:55:00",distance=8000, duration="0:55:00",distance=8000,
csvfilename=otwfilename) csvfilename=otwfilename)
powerperc = 100*np.array([r.pw_ut2, powerperc = 100*np.array([r.pw_ut2,
r.pw_ut1, r.pw_ut1,
@@ -37,21 +41,21 @@ class EmpowerTest(TestCase):
r.pw_tr,r.pw_an])/r.ftp r.pw_tr,r.pw_an])/r.ftp
self.hrdata = { self.hrdata = {
'hrmax':r.max, 'hrmax':r.max,
'hrut2':r.ut2, 'hrut2':r.ut2,
'hrut1':r.ut1, 'hrut1':r.ut1,
'hrat':r.at, 'hrat':r.at,
'hrtr':r.tr, 'hrtr':r.tr,
'hran':r.an, 'hran':r.an,
'ftp':r.ftp, 'ftp':r.ftp,
'powerperc':serialize_list(powerperc), 'powerperc':serialize_list(powerperc),
'powerzones':serialize_list(r.powerzones), 'powerzones':serialize_list(r.powerzones),
} }
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
def test_workflow_map_view(self, mocked_sqlalchemy): def test_workflow_map_view(self, mocked_sqlalchemy):
login = self.c.login(username='john',password='koeinsloot') login = self.c.login(username='john',password='koeinsloot')
self.assertTrue(login) self.assertTrue(login)
encodedid = str(encoder.encode_hex(self.wote.id)) encodedid = str(encoder.encode_hex(self.wote.id))
+9 -3
View File
@@ -1,5 +1,10 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
#from __future__ import print_function #from __future__ import print_function
from statements import * from .statements import *
#@pytest.mark.django_db #@pytest.mark.django_db
@@ -11,11 +16,12 @@ class TestErrorPages(TestCase):
factory = RequestFactory() factory = RequestFactory()
request = factory.get('/') request = factory.get('/')
response = error404_view(request) response = error404_view(request)
self.assertEqual(response.status_code, 404) self.assertEqual(response.status_code, 404)
self.assertIn('404 Page not found', unicode(response)) self.assertIn('404 Page not found', str(response.content))
response = error500_view(request) response = error500_view(request)
self.assertEqual(response.status_code, 500) self.assertEqual(response.status_code, 500)
self.assertIn('500 Internal Server Error', unicode(response)) self.assertIn('500 Internal Server Error', str(response.content))
response = error400_view(request) response = error400_view(request)
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
+7 -2
View File
@@ -1,4 +1,9 @@
from statements import * from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from .statements import *
@override_settings(TESTING=True) @override_settings(TESTING=True)
class WorkoutViewTest(TestCase): class WorkoutViewTest(TestCase):
@@ -120,7 +125,7 @@ class WorkoutViewTest(TestCase):
form = FlexOptionsForm(form_data) form = FlexOptionsForm(form_data)
if not form.is_valid(): if not form.is_valid():
print form.errors print(form.errors)
self.assertTrue(form.is_valid()) self.assertTrue(form.is_valid())
response = self.c.post(url,form_data) response = self.c.post(url,form_data)
+218 -213
View File
@@ -1,5 +1,10 @@
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
#from __future__ import print_function #from __future__ import print_function
from statements import * from .statements import *
nu = datetime.datetime.now() nu = datetime.datetime.now()
@@ -10,14 +15,14 @@ import rowers
class C2Objects(DjangoTestCase): class C2Objects(DjangoTestCase):
def setUp(self): def setUp(self):
self.c = Client() self.c = Client()
self.u = User.objects.create_user('john', self.u = User.objects.create_user('john',
'sander@ds.ds', 'sander@ds.ds',
'koeinsloot') 'koeinsloot')
self.u.first_name = 'John' self.u.first_name = 'John'
self.u.last_name = 'Sander' self.u.last_name = 'Sander'
self.u.save() self.u.save()
self.r = Rower.objects.create(user=self.u,gdproptin=True, self.r = Rower.objects.create(user=self.u,gdproptin=True,
gdproptindate=timezone.now() gdproptindate=timezone.now()
) )
@@ -25,39 +30,39 @@ class C2Objects(DjangoTestCase):
self.r.c2refreshtoken = 'ab' self.r.c2refreshtoken = 'ab'
self.r.tokenexpirydate = datetime.datetime.now()+datetime.timedelta(days=1) self.r.tokenexpirydate = datetime.datetime.now()+datetime.timedelta(days=1)
self.r.save() self.r.save()
self.c.login(username='john',password='koeinsloot') self.c.login(username='john',password='koeinsloot')
self.nu = datetime.datetime.now() self.nu = datetime.datetime.now()
filename = 'rowers/tests/testdata/testdata.csv' filename = 'rowers/tests/testdata/testdata.csv'
rr = rrower(hrmax=self.r.max,hrut2=self.r.ut2, rr = rrower(hrmax=self.r.max,hrut2=self.r.ut2,
hrut1=self.r.ut1,hrat=self.r.at, hrut1=self.r.ut1,hrat=self.r.at,
hrtr=self.r.tr,hran=self.r.an,ftp=self.r.ftp) hrtr=self.r.tr,hran=self.r.an,ftp=self.r.ftp)
row = rdata(filename,rower=rr) row = rdata(filename,rower=rr)
totaldist = row.df['cum_dist'].max() totaldist = row.df['cum_dist'].max()
totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min() totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min()
totaltime = totaltime+row.df.loc[:,' ElapsedTime (sec)'].iloc[0] totaltime = totaltime+row.df.loc[:,' ElapsedTime (sec)'].iloc[0]
hours = int(totaltime/3600.) hours = int(totaltime/3600.)
minutes = int((totaltime - 3600.*hours)/60.) minutes = int((totaltime - 3600.*hours)/60.)
seconds = int(totaltime - 3600.*hours - 60.*minutes) seconds = int(totaltime - 3600.*hours - 60.*minutes)
tenths = int(10*(totaltime - 3600.*hours - 60.*minutes - seconds)) tenths = int(10*(totaltime - 3600.*hours - 60.*minutes - seconds))
duration = "%s:%s:%s.%s" % (hours,minutes,seconds,tenths) duration = "%s:%s:%s.%s" % (hours,minutes,seconds,tenths)
workoutdate = row.rowdatetime.strftime('%Y-%m-%d') workoutdate = row.rowdatetime.strftime('%Y-%m-%d')
workoutstarttime = row.rowdatetime.strftime('%H:%M:%S') workoutstarttime = row.rowdatetime.strftime('%H:%M:%S')
self.w = Workout.objects.create( self.w = Workout.objects.create(
name='testworkout',workouttype='water', name='testworkout',workouttype='water',
user=self.r,date=self.nu.strftime('%Y-%m-%d'), user=self.r,date=self.nu.strftime('%Y-%m-%d'),
starttime=workoutstarttime, starttime=workoutstarttime,
startdatetime=row.rowdatetime, startdatetime=row.rowdatetime,
duration=duration,distance=totaldist, duration=duration,distance=totaldist,
csvfilename=filename csvfilename=filename
) )
@@ -110,49 +115,49 @@ class C2Objects(DjangoTestCase):
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
def test_strokedata(self, mocked_sqlalchemy): def test_strokedata(self, mocked_sqlalchemy):
with open('rowers/tests/testdata/c2stroketestdata.txt','r') as infile: with open('rowers/tests/testdata/c2stroketestdata.txt','r') as infile:
res = json.load(infile) res = json.load(infile)
strokedata = pd.DataFrame.from_dict(res['data']) strokedata = pd.DataFrame.from_dict(res['data'])
with open('rowers/tests/testdata/c2testdata.txt','r') as infile: with open('rowers/tests/testdata/c2testdata.txt','r') as infile:
res = json.load(infile) res = json.load(infile)
data = res['data'] data = res['data']
from rowers.views import add_workout_from_strokedata from rowers.views import add_workout_from_strokedata
res = add_workout_from_strokedata(self.u,1,data,strokedata,source='c2') res = add_workout_from_strokedata(self.u,1,data,strokedata,source='c2')
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
def test_strokedatanohr(self, mocked_sqlalchemy): def test_strokedatanohr(self, mocked_sqlalchemy):
with open('rowers/tests/testdata/c2strokedatanohr.txt','r') as infile: with open('rowers/tests/testdata/c2strokedatanohr.txt','r') as infile:
res = json.load(infile) res = json.load(infile)
strokedata = pd.DataFrame.from_dict(res['data']) strokedata = pd.DataFrame.from_dict(res['data'])
with open('rowers/tests/testdata/c2testdata.txt','r') as infile: with open('rowers/tests/testdata/c2testdata.txt','r') as infile:
res = json.load(infile) res = json.load(infile)
data = res['data'] data = res['data']
from rowers.views import add_workout_from_strokedata from rowers.views import add_workout_from_strokedata
res = add_workout_from_strokedata(self.u,1,data,strokedata,source='c2') res = add_workout_from_strokedata(self.u,1,data,strokedata,source='c2')
@override_settings(TESTING=True) @override_settings(TESTING=True)
class C2ObjectsTokenExpired(DjangoTestCase): class C2ObjectsTokenExpired(DjangoTestCase):
def setUp(self): def setUp(self):
self.c = Client() self.c = Client()
self.u = User.objects.create_user('john', self.u = User.objects.create_user('john',
'sander@ds.ds', 'sander@ds.ds',
'koeinsloot') 'koeinsloot')
self.u.first_name = 'John' self.u.first_name = 'John'
self.u.last_name = 'Sander' self.u.last_name = 'Sander'
self.u.save() self.u.save()
self.r = Rower.objects.create(user=self.u,gdproptin=True, self.r = Rower.objects.create(user=self.u,gdproptin=True,
gdproptindate=timezone.now() gdproptindate=timezone.now()
) )
@@ -160,39 +165,39 @@ class C2ObjectsTokenExpired(DjangoTestCase):
self.r.c2refreshtoken = 'ab' self.r.c2refreshtoken = 'ab'
self.r.tokenexpirydate = datetime.datetime.now()-datetime.timedelta(days=1) self.r.tokenexpirydate = datetime.datetime.now()-datetime.timedelta(days=1)
self.r.save() self.r.save()
self.c.login(username='john',password='koeinsloot') self.c.login(username='john',password='koeinsloot')
self.nu = datetime.datetime.now() self.nu = datetime.datetime.now()
filename = 'rowers/tests/testdata/testdata.csv' filename = 'rowers/tests/testdata/testdata.csv'
rr = rrower(hrmax=self.r.max,hrut2=self.r.ut2, rr = rrower(hrmax=self.r.max,hrut2=self.r.ut2,
hrut1=self.r.ut1,hrat=self.r.at, hrut1=self.r.ut1,hrat=self.r.at,
hrtr=self.r.tr,hran=self.r.an,ftp=self.r.ftp) hrtr=self.r.tr,hran=self.r.an,ftp=self.r.ftp)
row = rdata(filename,rower=rr) row = rdata(filename,rower=rr)
totaldist = row.df['cum_dist'].max() totaldist = row.df['cum_dist'].max()
totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min() totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min()
totaltime = totaltime+row.df.loc[:,' ElapsedTime (sec)'].iloc[0] totaltime = totaltime+row.df.loc[:,' ElapsedTime (sec)'].iloc[0]
hours = int(totaltime/3600.) hours = int(totaltime/3600.)
minutes = int((totaltime - 3600.*hours)/60.) minutes = int((totaltime - 3600.*hours)/60.)
seconds = int(totaltime - 3600.*hours - 60.*minutes) seconds = int(totaltime - 3600.*hours - 60.*minutes)
tenths = int(10*(totaltime - 3600.*hours - 60.*minutes - seconds)) tenths = int(10*(totaltime - 3600.*hours - 60.*minutes - seconds))
duration = "%s:%s:%s.%s" % (hours,minutes,seconds,tenths) duration = "%s:%s:%s.%s" % (hours,minutes,seconds,tenths)
workoutdate = row.rowdatetime.strftime('%Y-%m-%d') workoutdate = row.rowdatetime.strftime('%Y-%m-%d')
workoutstarttime = row.rowdatetime.strftime('%H:%M:%S') workoutstarttime = row.rowdatetime.strftime('%H:%M:%S')
self.w = Workout.objects.create( self.w = Workout.objects.create(
name='testworkout',workouttype='water', name='testworkout',workouttype='water',
user=self.r,date=self.nu.strftime('%Y-%m-%d'), user=self.r,date=self.nu.strftime('%Y-%m-%d'),
starttime=workoutstarttime, starttime=workoutstarttime,
startdatetime=row.rowdatetime, startdatetime=row.rowdatetime,
duration=duration,distance=totaldist, duration=duration,distance=totaldist,
csvfilename=filename csvfilename=filename
) )
@@ -224,14 +229,14 @@ class C2ObjectsTokenExpired(DjangoTestCase):
class StravaObjects(DjangoTestCase): class StravaObjects(DjangoTestCase):
def setUp(self): def setUp(self):
self.c = Client() self.c = Client()
self.u = User.objects.create_user('john', self.u = User.objects.create_user('john',
'sander@ds.ds', 'sander@ds.ds',
'koeinsloot') 'koeinsloot')
self.u.first_name = 'John' self.u.first_name = 'John'
self.u.last_name = 'Sander' self.u.last_name = 'Sander'
self.u.save() self.u.save()
self.r = Rower.objects.create(user=self.u,gdproptin=True, self.r = Rower.objects.create(user=self.u,gdproptin=True,
gdproptindate=timezone.now() gdproptindate=timezone.now()
) )
@@ -240,38 +245,38 @@ class StravaObjects(DjangoTestCase):
self.r.stravatokenexpirydate = datetime.datetime.now()-datetime.timedelta(days=1) self.r.stravatokenexpirydate = datetime.datetime.now()-datetime.timedelta(days=1)
self.r.save() self.r.save()
self.c.login(username='john',password='koeinsloot') self.c.login(username='john',password='koeinsloot')
self.nu = datetime.datetime.now() self.nu = datetime.datetime.now()
filename = 'rowers/tests/testdata/testdata.csv' filename = 'rowers/tests/testdata/testdata.csv'
rr = rrower(hrmax=self.r.max,hrut2=self.r.ut2, rr = rrower(hrmax=self.r.max,hrut2=self.r.ut2,
hrut1=self.r.ut1,hrat=self.r.at, hrut1=self.r.ut1,hrat=self.r.at,
hrtr=self.r.tr,hran=self.r.an,ftp=self.r.ftp) hrtr=self.r.tr,hran=self.r.an,ftp=self.r.ftp)
row = rdata(filename,rower=rr) row = rdata(filename,rower=rr)
totaldist = row.df['cum_dist'].max() totaldist = row.df['cum_dist'].max()
totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min() totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min()
totaltime = totaltime+row.df.loc[:,' ElapsedTime (sec)'].iloc[0] totaltime = totaltime+row.df.loc[:,' ElapsedTime (sec)'].iloc[0]
hours = int(totaltime/3600.) hours = int(totaltime/3600.)
minutes = int((totaltime - 3600.*hours)/60.) minutes = int((totaltime - 3600.*hours)/60.)
seconds = int(totaltime - 3600.*hours - 60.*minutes) seconds = int(totaltime - 3600.*hours - 60.*minutes)
tenths = int(10*(totaltime - 3600.*hours - 60.*minutes - seconds)) tenths = int(10*(totaltime - 3600.*hours - 60.*minutes - seconds))
duration = "%s:%s:%s.%s" % (hours,minutes,seconds,tenths) duration = "%s:%s:%s.%s" % (hours,minutes,seconds,tenths)
workoutdate = row.rowdatetime.strftime('%Y-%m-%d') workoutdate = row.rowdatetime.strftime('%Y-%m-%d')
workoutstarttime = row.rowdatetime.strftime('%H:%M:%S') workoutstarttime = row.rowdatetime.strftime('%H:%M:%S')
self.w = Workout.objects.create( self.w = Workout.objects.create(
name='testworkout',workouttype='water', name='testworkout',workouttype='water',
user=self.r,date=self.nu.strftime('%Y-%m-%d'), user=self.r,date=self.nu.strftime('%Y-%m-%d'),
starttime=workoutstarttime, starttime=workoutstarttime,
startdatetime=row.rowdatetime, startdatetime=row.rowdatetime,
duration=duration,distance=totaldist, duration=duration,distance=totaldist,
csvfilename=filename csvfilename=filename
) )
@@ -330,14 +335,14 @@ class StravaObjects(DjangoTestCase):
class STObjects(DjangoTestCase): class STObjects(DjangoTestCase):
def setUp(self): def setUp(self):
self.c = Client() self.c = Client()
self.u = User.objects.create_user('john', self.u = User.objects.create_user('john',
'sander@ds.ds', 'sander@ds.ds',
'koeinsloot') 'koeinsloot')
self.u.first_name = 'John' self.u.first_name = 'John'
self.u.last_name = 'Sander' self.u.last_name = 'Sander'
self.u.save() self.u.save()
self.r = Rower.objects.create(user=self.u,gdproptin=True, self.r = Rower.objects.create(user=self.u,gdproptin=True,
gdproptindate=timezone.now() gdproptindate=timezone.now()
) )
@@ -347,39 +352,39 @@ class STObjects(DjangoTestCase):
self.r.sporttrackstokenexpirydate = datetime.datetime.now()+datetime.timedelta(days=1) self.r.sporttrackstokenexpirydate = datetime.datetime.now()+datetime.timedelta(days=1)
self.r.save() self.r.save()
self.c.login(username='john',password='koeinsloot') self.c.login(username='john',password='koeinsloot')
self.nu = datetime.datetime.now() self.nu = datetime.datetime.now()
filename = 'rowers/tests/testdata/testdata.csv' filename = 'rowers/tests/testdata/testdata.csv'
rr = rrower(hrmax=self.r.max,hrut2=self.r.ut2, rr = rrower(hrmax=self.r.max,hrut2=self.r.ut2,
hrut1=self.r.ut1,hrat=self.r.at, hrut1=self.r.ut1,hrat=self.r.at,
hrtr=self.r.tr,hran=self.r.an,ftp=self.r.ftp) hrtr=self.r.tr,hran=self.r.an,ftp=self.r.ftp)
row = rdata(filename,rower=rr) row = rdata(filename,rower=rr)
totaldist = row.df['cum_dist'].max() totaldist = row.df['cum_dist'].max()
totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min() totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min()
totaltime = totaltime+row.df.loc[:,' ElapsedTime (sec)'].iloc[0] totaltime = totaltime+row.df.loc[:,' ElapsedTime (sec)'].iloc[0]
hours = int(totaltime/3600.) hours = int(totaltime/3600.)
minutes = int((totaltime - 3600.*hours)/60.) minutes = int((totaltime - 3600.*hours)/60.)
seconds = int(totaltime - 3600.*hours - 60.*minutes) seconds = int(totaltime - 3600.*hours - 60.*minutes)
tenths = int(10*(totaltime - 3600.*hours - 60.*minutes - seconds)) tenths = int(10*(totaltime - 3600.*hours - 60.*minutes - seconds))
duration = "%s:%s:%s.%s" % (hours,minutes,seconds,tenths) duration = "%s:%s:%s.%s" % (hours,minutes,seconds,tenths)
workoutdate = row.rowdatetime.strftime('%Y-%m-%d') workoutdate = row.rowdatetime.strftime('%Y-%m-%d')
workoutstarttime = row.rowdatetime.strftime('%H:%M:%S') workoutstarttime = row.rowdatetime.strftime('%H:%M:%S')
self.w = Workout.objects.create( self.w = Workout.objects.create(
name='testworkout',workouttype='water', name='testworkout',workouttype='water',
user=self.r,date=self.nu.strftime('%Y-%m-%d'), user=self.r,date=self.nu.strftime('%Y-%m-%d'),
starttime=workoutstarttime, starttime=workoutstarttime,
startdatetime=row.rowdatetime, startdatetime=row.rowdatetime,
duration=duration,distance=totaldist, duration=duration,distance=totaldist,
csvfilename=filename csvfilename=filename
) )
@patch('rowers.sporttracksstuff.requests.post', side_effect=mocked_requests) @patch('rowers.sporttracksstuff.requests.post', side_effect=mocked_requests)
@@ -442,37 +447,37 @@ class STObjects(DjangoTestCase):
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
def test_strokedata(self, mocked_sqlalchemy): def test_strokedata(self, mocked_sqlalchemy):
with open('rowers/tests/testdata/sporttrackstestdata.txt','r') as infile: with open('rowers/tests/testdata/sporttrackstestdata.txt','r') as infile:
data = json.load(infile) data = json.load(infile)
from rowers.sporttracksstuff import add_workout_from_data from rowers.sporttracksstuff import add_workout_from_data
res = add_workout_from_data(self.u,1,data,data) res = add_workout_from_data(self.u,1,data,data)
@patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.create_engine')
def test_strokedatanohr(self, mocked_sqlalchemy): def test_strokedatanohr(self, mocked_sqlalchemy):
with open('rowers/tests/testdata/sporttrackstestnohr.txt','r') as infile: with open('rowers/tests/testdata/sporttrackstestnohr.txt','r') as infile:
data = json.load(infile) data = json.load(infile)
from rowers.sporttracksstuff import add_workout_from_data from rowers.sporttracksstuff import add_workout_from_data
res = add_workout_from_data(self.u,1,data,data) res = add_workout_from_data(self.u,1,data,data)
#@pytest.mark.django_db #@pytest.mark.django_db
@override_settings(TESTING=True) @override_settings(TESTING=True)
class RunKeeperObjects(DjangoTestCase): class RunKeeperObjects(DjangoTestCase):
def setUp(self): def setUp(self):
self.c = Client() self.c = Client()
self.u = User.objects.create_user('john', self.u = User.objects.create_user('john',
'sander@ds.ds', 'sander@ds.ds',
'koeinsloot') 'koeinsloot')
self.u.first_name = 'John' self.u.first_name = 'John'
self.u.last_name = 'Sander' self.u.last_name = 'Sander'
self.u.save() self.u.save()
self.r = Rower.objects.create(user=self.u,gdproptin=True, self.r = Rower.objects.create(user=self.u,gdproptin=True,
gdproptindate=timezone.now() gdproptindate=timezone.now()
) )
@@ -481,39 +486,39 @@ class RunKeeperObjects(DjangoTestCase):
self.r.save() self.r.save()
self.c.login(username='john',password='koeinsloot') self.c.login(username='john',password='koeinsloot')
self.nu = datetime.datetime.now() self.nu = datetime.datetime.now()
filename = 'rowers/tests/testdata/testdata.csv' filename = 'rowers/tests/testdata/testdata.csv'
rr = rrower(hrmax=self.r.max,hrut2=self.r.ut2, rr = rrower(hrmax=self.r.max,hrut2=self.r.ut2,
hrut1=self.r.ut1,hrat=self.r.at, hrut1=self.r.ut1,hrat=self.r.at,
hrtr=self.r.tr,hran=self.r.an,ftp=self.r.ftp) hrtr=self.r.tr,hran=self.r.an,ftp=self.r.ftp)
row = rdata(filename,rower=rr) row = rdata(filename,rower=rr)
totaldist = row.df['cum_dist'].max() totaldist = row.df['cum_dist'].max()
totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min() totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min()
totaltime = totaltime+row.df.loc[:,' ElapsedTime (sec)'].iloc[0] totaltime = totaltime+row.df.loc[:,' ElapsedTime (sec)'].iloc[0]
hours = int(totaltime/3600.) hours = int(totaltime/3600.)
minutes = int((totaltime - 3600.*hours)/60.) minutes = int((totaltime - 3600.*hours)/60.)
seconds = int(totaltime - 3600.*hours - 60.*minutes) seconds = int(totaltime - 3600.*hours - 60.*minutes)
tenths = int(10*(totaltime - 3600.*hours - 60.*minutes - seconds)) tenths = int(10*(totaltime - 3600.*hours - 60.*minutes - seconds))
duration = "%s:%s:%s.%s" % (hours,minutes,seconds,tenths) duration = "%s:%s:%s.%s" % (hours,minutes,seconds,tenths)
workoutdate = row.rowdatetime.strftime('%Y-%m-%d') workoutdate = row.rowdatetime.strftime('%Y-%m-%d')
workoutstarttime = row.rowdatetime.strftime('%H:%M:%S') workoutstarttime = row.rowdatetime.strftime('%H:%M:%S')
self.w = Workout.objects.create( self.w = Workout.objects.create(
name='testworkout',workouttype='water', name='testworkout',workouttype='water',
user=self.r,date=self.nu.strftime('%Y-%m-%d'), user=self.r,date=self.nu.strftime('%Y-%m-%d'),
starttime=workoutstarttime, starttime=workoutstarttime,
startdatetime=row.rowdatetime, startdatetime=row.rowdatetime,
duration=duration,distance=totaldist, duration=duration,distance=totaldist,
csvfilename=filename csvfilename=filename
) )
@patch('rowers.runkeeperstuff.requests.post', side_effect=mocked_requests) @patch('rowers.runkeeperstuff.requests.post', side_effect=mocked_requests)
@@ -558,14 +563,14 @@ class RunKeeperObjects(DjangoTestCase):
class UAObjects(DjangoTestCase): class UAObjects(DjangoTestCase):
def setUp(self): def setUp(self):
self.c = Client() self.c = Client()
self.u = User.objects.create_user('john', self.u = User.objects.create_user('john',
'sander@ds.ds', 'sander@ds.ds',
'koeinsloot') 'koeinsloot')
self.u.first_name = 'John' self.u.first_name = 'John'
self.u.last_name = 'Sander' self.u.last_name = 'Sander'
self.u.save() self.u.save()
self.r = Rower.objects.create(user=self.u,gdproptin=True, self.r = Rower.objects.create(user=self.u,gdproptin=True,
gdproptindate=timezone.now() gdproptindate=timezone.now()
) )
@@ -575,39 +580,39 @@ class UAObjects(DjangoTestCase):
self.r.underarmourtokenexpirydate = datetime.datetime.now()+datetime.timedelta(days=1) self.r.underarmourtokenexpirydate = datetime.datetime.now()+datetime.timedelta(days=1)
self.r.save() self.r.save()
self.c.login(username='john',password='koeinsloot') self.c.login(username='john',password='koeinsloot')
self.nu = datetime.datetime.now() self.nu = datetime.datetime.now()
filename = 'rowers/tests/testdata/testdata.csv' filename = 'rowers/tests/testdata/testdata.csv'
rr = rrower(hrmax=self.r.max,hrut2=self.r.ut2, rr = rrower(hrmax=self.r.max,hrut2=self.r.ut2,
hrut1=self.r.ut1,hrat=self.r.at, hrut1=self.r.ut1,hrat=self.r.at,
hrtr=self.r.tr,hran=self.r.an,ftp=self.r.ftp) hrtr=self.r.tr,hran=self.r.an,ftp=self.r.ftp)
row = rdata(filename,rower=rr) row = rdata(filename,rower=rr)
totaldist = row.df['cum_dist'].max() totaldist = row.df['cum_dist'].max()
totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min() totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min()
totaltime = totaltime+row.df.loc[:,' ElapsedTime (sec)'].iloc[0] totaltime = totaltime+row.df.loc[:,' ElapsedTime (sec)'].iloc[0]
hours = int(totaltime/3600.) hours = int(totaltime/3600.)
minutes = int((totaltime - 3600.*hours)/60.) minutes = int((totaltime - 3600.*hours)/60.)
seconds = int(totaltime - 3600.*hours - 60.*minutes) seconds = int(totaltime - 3600.*hours - 60.*minutes)
tenths = int(10*(totaltime - 3600.*hours - 60.*minutes - seconds)) tenths = int(10*(totaltime - 3600.*hours - 60.*minutes - seconds))
duration = "%s:%s:%s.%s" % (hours,minutes,seconds,tenths) duration = "%s:%s:%s.%s" % (hours,minutes,seconds,tenths)
workoutdate = row.rowdatetime.strftime('%Y-%m-%d') workoutdate = row.rowdatetime.strftime('%Y-%m-%d')
workoutstarttime = row.rowdatetime.strftime('%H:%M:%S') workoutstarttime = row.rowdatetime.strftime('%H:%M:%S')
self.w = Workout.objects.create( self.w = Workout.objects.create(
name='testworkout',workouttype='water', name='testworkout',workouttype='water',
user=self.r,date=self.nu.strftime('%Y-%m-%d'), user=self.r,date=self.nu.strftime('%Y-%m-%d'),
starttime=workoutstarttime, starttime=workoutstarttime,
startdatetime=row.rowdatetime, startdatetime=row.rowdatetime,
duration=duration,distance=totaldist, duration=duration,distance=totaldist,
csvfilename=filename csvfilename=filename
) )
@patch('rowers.imports.requests.post', side_effect=mocked_requests) @patch('rowers.imports.requests.post', side_effect=mocked_requests)
@@ -661,14 +666,14 @@ class UAObjects(DjangoTestCase):
class TPObjects(DjangoTestCase): class TPObjects(DjangoTestCase):
def setUp(self): def setUp(self):
self.c = Client() self.c = Client()
self.u = User.objects.create_user('john', self.u = User.objects.create_user('john',
'sander@ds.ds', 'sander@ds.ds',
'koeinsloot') 'koeinsloot')
self.u.first_name = 'John' self.u.first_name = 'John'
self.u.last_name = 'Sander' self.u.last_name = 'Sander'
self.u.save() self.u.save()
self.r = Rower.objects.create(user=self.u,gdproptin=True, self.r = Rower.objects.create(user=self.u,gdproptin=True,
gdproptindate=timezone.now() gdproptindate=timezone.now()
) )
@@ -678,39 +683,39 @@ class TPObjects(DjangoTestCase):
self.r.tptokenexpirydate = datetime.datetime.now()+datetime.timedelta(days=1) self.r.tptokenexpirydate = datetime.datetime.now()+datetime.timedelta(days=1)
self.r.save() self.r.save()
self.c.login(username='john',password='koeinsloot') self.c.login(username='john',password='koeinsloot')
self.nu = datetime.datetime.now() self.nu = datetime.datetime.now()
filename = 'rowers/tests/testdata/testdata.csv' filename = 'rowers/tests/testdata/testdata.csv'
rr = rrower(hrmax=self.r.max,hrut2=self.r.ut2, rr = rrower(hrmax=self.r.max,hrut2=self.r.ut2,
hrut1=self.r.ut1,hrat=self.r.at, hrut1=self.r.ut1,hrat=self.r.at,
hrtr=self.r.tr,hran=self.r.an,ftp=self.r.ftp) hrtr=self.r.tr,hran=self.r.an,ftp=self.r.ftp)
row = rdata(filename,rower=rr) row = rdata(filename,rower=rr)
totaldist = row.df['cum_dist'].max() totaldist = row.df['cum_dist'].max()
totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min() totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min()
totaltime = totaltime+row.df.loc[:,' ElapsedTime (sec)'].iloc[0] totaltime = totaltime+row.df.loc[:,' ElapsedTime (sec)'].iloc[0]
hours = int(totaltime/3600.) hours = int(totaltime/3600.)
minutes = int((totaltime - 3600.*hours)/60.) minutes = int((totaltime - 3600.*hours)/60.)
seconds = int(totaltime - 3600.*hours - 60.*minutes) seconds = int(totaltime - 3600.*hours - 60.*minutes)
tenths = int(10*(totaltime - 3600.*hours - 60.*minutes - seconds)) tenths = int(10*(totaltime - 3600.*hours - 60.*minutes - seconds))
duration = "%s:%s:%s.%s" % (hours,minutes,seconds,tenths) duration = "%s:%s:%s.%s" % (hours,minutes,seconds,tenths)
workoutdate = row.rowdatetime.strftime('%Y-%m-%d') workoutdate = row.rowdatetime.strftime('%Y-%m-%d')
workoutstarttime = row.rowdatetime.strftime('%H:%M:%S') workoutstarttime = row.rowdatetime.strftime('%H:%M:%S')
self.w = Workout.objects.create( self.w = Workout.objects.create(
name='testworkout',workouttype='water', name='testworkout',workouttype='water',
user=self.r,date=self.nu.strftime('%Y-%m-%d'), user=self.r,date=self.nu.strftime('%Y-%m-%d'),
starttime=workoutstarttime, starttime=workoutstarttime,
startdatetime=row.rowdatetime, startdatetime=row.rowdatetime,
duration=duration,distance=totaldist, duration=duration,distance=totaldist,
csvfilename=filename csvfilename=filename
) )
@patch('rowers.imports.requests.post', side_effect=mocked_requests) @patch('rowers.imports.requests.post', side_effect=mocked_requests)
@@ -748,10 +753,10 @@ class TPObjects(DjangoTestCase):
class AutoExportTests(TestCase): class AutoExportTests(TestCase):
def setUp(self): def setUp(self):
redis_connection.publish('tasks','KILL') redis_connection.publish('tasks','KILL')
u = User.objects.create_user('john', u = User.objects.create_user('john',
'sander@ds.ds', 'sander@ds.ds',
'koeinsloot') 'koeinsloot')
r = Rower.objects.create(user=u,gdproptin=True, r = Rower.objects.create(user=u,gdproptin=True,
gdproptindate=timezone.now() gdproptindate=timezone.now()
) )
@@ -762,7 +767,7 @@ class AutoExportTests(TestCase):
r.save() r.save()
nu = datetime.datetime.now() nu = datetime.datetime.now()
workoutsbox = Mailbox.objects.create(name='workouts') workoutsbox = Mailbox.objects.create(name='workouts')
workoutsbox.save() workoutsbox.save()
failbox = Mailbox.objects.create(name='Failed') failbox = Mailbox.objects.create(name='Failed')
@@ -791,7 +796,7 @@ boattype: 2x
if not os.path.isdir(path): if not os.path.isdir(path):
try: try:
os.remove(path) os.remove(path)
except (WindowsError,OSError): except (FileNotFoundError,OSError):
pass pass
@patch('rowers.tpstuff.requests.post', side_effect=mocked_requests) @patch('rowers.tpstuff.requests.post', side_effect=mocked_requests)

Some files were not shown because too many files have changed in this diff Show More