From 77aba205c59d4a06cf66a1e312829ea1ffde4fec Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Fri, 8 Feb 2019 10:12:09 +0100 Subject: [PATCH] force curve with workstrokesonly --- rowers/tests/statements.py | 2 + rowers/tests/test_aworkouts.py | 52 ++++ rowers/tests/testdata/onwater.csv | 98 +++++++ rowers/tests/testdata/testdata.csv.gz | Bin 11426 -> 11457 bytes rowers/views/analysisviews.py | 171 ++++++++++++ rowers/views/statements.py | 9 + rowers/views/workoutviews.py | 361 +++++++------------------- 7 files changed, 424 insertions(+), 269 deletions(-) create mode 100644 rowers/tests/test_aworkouts.py create mode 100644 rowers/tests/testdata/onwater.csv diff --git a/rowers/tests/statements.py b/rowers/tests/statements.py index 51f1f25a..0b9aaf47 100644 --- a/rowers/tests/statements.py +++ b/rowers/tests/statements.py @@ -37,6 +37,8 @@ from minimocktest import MockTestCase import pandas as pd import rowers.c2stuff as c2stuff +from django.core.urlresolvers import reverse, reverse_lazy + import json import numpy as np diff --git a/rowers/tests/test_aworkouts.py b/rowers/tests/test_aworkouts.py new file mode 100644 index 00000000..cd936992 --- /dev/null +++ b/rowers/tests/test_aworkouts.py @@ -0,0 +1,52 @@ +from statements import * + +nu = datetime.datetime.now() + +class WaterWorkoutViewTest(TestCase): + def setUp(self): + self.u = UserFactory() + + self.r = Rower.objects.create(user=self.u, + birthdate=faker.profile()['birthdate'], + gdproptin=True, + gdproptindate=timezone.now(), + rowerplan='coach') + + self.c = Client() + self.user_workouts = WorkoutFactory.create_batch(5, user=self.r) + self.factory = RequestFactory() + self.password = faker.word() + self.u.set_password(self.password) + self.u.save() + + result = get_random_file(filename='rowers/tests/testdata/onwater.csv') + + self.wwater = WorkoutFactory(user=self.r, + csvfilename=result['filename'], + starttime=result['starttime'], + startdatetime=result['startdatetime'], + duration=result['duration'], + distance=result['totaldist'] + ) + + def tearDown(self): + pass + + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getsmallrowdata_db') + def test_forcecurve(self, mocked_sqlalchemy, mocked_getsmallrowdata_db): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + url = reverse('workout_forcecurve_view',kwargs={'id':self.wwater.id}) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + form_data = { + 'workstrokesonly': True + } + + response = self.c.post(url,form_data) + self.assertEqual(response.status_code,200) + diff --git a/rowers/tests/testdata/onwater.csv b/rowers/tests/testdata/onwater.csv new file mode 100644 index 00000000..1a07204c --- /dev/null +++ b/rowers/tests/testdata/onwater.csv @@ -0,0 +1,98 @@ +,index, lapIdx,TimeStamp (sec), Horizontal (meters),GPS Split,GPS Speed, Cadence (stokes/min), HRCur (bpm),Stroke Count,cum_dist, Stroke500mPace (sec/500m), ElapsedTime (sec), Power (watts), DriveLength (meters), StrokeDistance (meters), DriveTime (ms), DragFactor, StrokeRecoveryTime (ms), AverageDriveForce (lbs), AverageBoatSpeed (m/s), PeakDriveForce (lbs), AverageDriveForce (N), PeakDriveForce (N), WorkoutState, Stroke Number,originalvelo,hr_ut2,hr_ut1,hr_at,hr_tr,hr_an,hr_max,lim_ut2,lim_ut1,lim_at,lim_tr,lim_an,lim_max,pw_ut2,pw_ut1,pw_at,pw_tr,pw_an,pw_max,limpw_ut2,limpw_ut1,limpw_at,limpw_tr,limpw_an +0,0,0.0,1469705701.0,3.2,0.0,0.74,35.5,112,1,3.2,695.931477516,0.0,0,0,0,0,0,0,0,0.74,0,0.0,0.0,4,0,0.74,0.0,0.0,0.0,0.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +1,1,0.0,1469705702.6,8.6,0.0,2.07,38.5,113,2,8.6,228.8,1.59999990463,0,0,0,0,0,0,0,2.07,0,0.0,0.0,4,1,2.07,113.0,0.0,0.0,0.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +2,2,0.0,1469705704.5,15.1,0.0,3.36,38.5,115,3,15.1,153.336955279,3.5,0,0,0,0,0,0,0,3.36,0,0.0,0.0,4,3,3.35999999999,115.0,0.0,0.0,0.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +3,3,0.0,1469705705.6,21.4,0.0,4.09,39.0,115,4,21.4,124.977058407,4.59999990463,0,0,0,0,0,0,0,4.09,0,0.0,0.0,4,3,4.09,115.0,0.0,0.0,0.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +4,4,0.0,1469705707.5,28.8,0.0,4.43,38.5,118,5,28.8,112.083019815,6.5,0,0,0,0,0,0,0,4.43,0,0.0,0.0,4,5,4.42999999999,118.0,0.0,0.0,0.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +5,5,0.0,1469705708.8,36.2,0.0,4.64,37.5,125,6,36.2,106.442632632,7.79999995232,0,0,0,0,0,0,0,4.64,0,0.0,0.0,4,5,4.63999999999,125.0,0.0,0.0,0.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +6,6,0.0,1469705710.5,43.1,0.0,4.66,36.0,130,7,43.1,104.628021775,9.5,0,0,0,0,0,0,0,4.66,0,0.0,0.0,4,6,4.66,130.0,0.0,0.0,0.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +7,7,0.0,1469705712.2,52.0,0.0,4.76,34.0,135,8,52.0,106.01911804,11.2000000477,0,0,0,0,0,0,0,4.76,0,0.0,0.0,4,7,4.75999999999,135.0,0.0,0.0,0.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +8,8,0.0,1469705714.0,60.1,0.0,4.71,34.5,142,9,60.1,107.565165936,13.0,0,0,0,0,0,0,0,4.71,0,0.0,0.0,4,8,4.70999999998,142.0,0.0,0.0,0.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +9,9,0.0,1469705715.8,68.1,0.0,4.53,33.5,148,10,68.1,108.679681206,14.7999999523,0,0,0,0,0,0,0,4.53,0,0.0,0.0,4,9,4.53000000001,0.0,0.0,148.0,0.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +10,10,0.0,1469705717.6,76.3,0.0,4.44,32.5,154,11,76.3,109.466700689,16.5999999046,0,0,0,0,0,0,0,4.44,0,0.0,0.0,4,10,4.43999999998,0.0,0.0,154.0,0.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +11,11,0.0,1469705719.5,83.8,0.0,4.44,33.0,156,12,83.8,109.893487851,18.5,0,0,0,0,0,0,0,4.44,0,0.0,0.0,4,11,4.43999999998,0.0,0.0,156.0,0.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +12,12,0.0,1469705721.2,92.5,0.0,4.62,33.5,159,13,92.5,110.627456239,20.2000000477,0,0,0,0,0,0,0,4.62,0,0.0,0.0,4,12,4.62,0.0,0.0,159.0,0.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +13,13,0.0,1469705723.0,100.8,0.0,4.64,33.5,163,14,100.8,110.362782274,22.0,0,0,0,0,0,0,0,4.64,0,0.0,0.0,4,13,4.63999999999,0.0,0.0,0.0,163.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +14,14,0.0,1469705724.8,109.1,0.0,4.59,32.0,166,15,109.1,109.351182981,23.7999999523,0,0,0,0,0,0,0,4.59,0,0.0,0.0,4,14,4.58999999998,0.0,0.0,0.0,166.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +15,15,0.0,1469705726.8,118.1,0.0,4.52,32.5,168,16,118.1,108.636197885,25.7999999523,0,0,0,0,0,0,0,4.52,0,0.0,0.0,4,15,4.51999999998,0.0,0.0,0.0,0.0,168.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +16,16,0.0,1469705728.5,125.1,0.0,4.5,33.0,169,17,125.1,108.494979894,27.5,0,0,0,0,0,0,0,4.5,0,0.0,0.0,4,16,4.5,0.0,0.0,0.0,0.0,169.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +17,17,0.0,1469705730.4,134.7,0.0,4.66,33.5,171,18,134.7,109.232014911,29.4000000954,0,0,0,0,0,0,0,4.66,0,0.0,0.0,4,17,4.66,0.0,0.0,0.0,0.0,171.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +18,18,0.0,1469705732.2,143.0,0.0,4.66,32.5,172,19,143.0,109.4493854,31.2000000477,0,0,0,0,0,0,0,4.66,0,0.0,0.0,4,18,4.66,0.0,0.0,0.0,0.0,172.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +19,19,0.0,1469705734.0,151.2,0.0,4.58,33.0,172,20,151.2,109.222559423,33.0,0,0,0,0,0,0,0,4.58,0,0.0,0.0,4,19,4.57999999999,0.0,0.0,0.0,0.0,172.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +20,20,0.0,1469705735.8,159.4,0.0,4.52,33.0,173,21,159.4,109.097567302,34.7999999523,0,0,0,0,0,0,0,4.52,0,0.0,0.0,4,20,4.51999999998,0.0,0.0,0.0,0.0,173.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +21,21,0.0,1469705737.5,166.1,0.0,4.47,33.5,175,22,166.1,109.622785185,36.5,0,0,0,0,0,0,0,4.47,0,0.0,0.0,4,21,4.47000000001,0.0,0.0,0.0,0.0,175.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +22,22,0.0,1469705739.4,175.7,0.0,4.58,33.5,175,23,175.7,110.908884086,38.4000000954,0,0,0,0,0,0,0,4.58,0,0.0,0.0,4,22,4.57999999999,0.0,0.0,0.0,0.0,175.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +23,23,0.0,1469705741.2,183.8,0.0,4.57,32.5,175,24,183.8,111.571150665,40.2000000477,0,0,0,0,0,0,0,4.57,0,0.0,0.0,4,23,4.57,0.0,0.0,0.0,0.0,175.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +24,24,0.0,1469705743.0,191.8,0.0,4.47,33.0,175,25,191.8,111.781044645,42.0,0,0,0,0,0,0,0,4.47,0,0.0,0.0,4,24,4.47000000001,0.0,0.0,0.0,0.0,175.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +25,25,0.0,1469705744.8,199.8,0.0,4.39,32.0,176,26,199.8,111.973606594,43.7999999523,0,0,0,0,0,0,0,4.39,0,0.0,0.0,4,25,4.39,0.0,0.0,0.0,0.0,176.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +26,26,0.0,1469705746.7,207.8,0.0,4.39,32.5,176,27,207.8,112.082434148,45.7000000477,0,0,0,0,0,0,0,4.39,0,0.0,0.0,4,26,4.39,0.0,0.0,0.0,0.0,176.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +27,27,0.0,1469705748.6,216.7,0.0,4.49,33.0,174,28,216.7,112.462053887,47.5999999046,0,0,0,0,0,0,0,4.49,0,0.0,0.0,4,27,4.49000000001,0.0,0.0,0.0,0.0,174.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +28,28,0.0,1469705750.4,224.8,0.0,4.49,32.0,174,29,224.8,110.702820986,49.4000000954,0,0,0,0,0,0,0,4.49,0,0.0,0.0,4,28,4.49000000001,0.0,0.0,0.0,0.0,174.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +29,29,0.0,1469705752.5,232.9,0.0,4.47,33.0,174,30,232.9,111.287510895,51.5,0,0,0,0,0,0,0,4.47,0,0.0,0.0,4,30,4.47000000001,0.0,0.0,0.0,0.0,174.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +30,30,0.0,1469705754.0,240.9,0.0,4.48,32.0,175,31,240.9,113.880099386,53.0,0,0,0,0,0,0,0,4.48,0,0.0,0.0,4,30,4.48000000001,0.0,0.0,0.0,0.0,175.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +31,31,0.0,1469705755.8,248.9,0.0,4.43,32.0,175,32,248.9,117.397229535,54.7999999523,0,0,0,0,0,0,0,4.43,0,0.0,0.0,4,31,4.42999999999,0.0,0.0,0.0,0.0,175.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +32,32,0.0,1469705756.2,250.4,0.0,4.43,32.0,175,32,250.4,120.181532945,55.2000000477,0,0,0,0,0,0,0,4.43,0,0.0,0.0,4,32,4.42999999999,0.0,0.0,0.0,0.0,175.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +33,33,1.0,1469705757.6,5.7,0.0,3.62,30.5,160,1,256.1,121.464367621,56.5999999046,0,0,0,0,0,0,0,3.62,0,0.0,0.0,4,32,3.62000000001,0.0,0.0,160.0,0.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +34,34,1.0,1469705759.4,13.4,0.0,3.85,33.5,161,2,263.8,121.497391629,58.4000000954,0,0,0,0,0,0,0,3.85,0,0.0,0.0,4,33,3.85,0.0,0.0,0.0,161.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +35,35,1.0,1469705761.2,20.7,0.0,4.2,33.5,162,3,271.1,120.37982782,60.2000000477,0,0,0,0,0,0,0,4.2,0,0.0,0.0,4,34,4.19999999999,0.0,0.0,0.0,162.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +36,36,1.0,1469705763.0,29.2,0.0,4.47,33.5,162,4,279.6,118.387283716,62.0,0,0,0,0,0,0,0,4.47,0,0.0,0.0,4,35,4.47000000001,0.0,0.0,0.0,162.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +37,37,1.0,1469705764.8,37.3,0.0,4.47,33.0,164,5,287.7,115.233986774,63.7999999523,0,0,0,0,0,0,0,4.47,0,0.0,0.0,4,36,4.47000000001,0.0,0.0,0.0,164.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +38,38,1.0,1469705766.6,45.3,0.0,4.44,33.5,165,6,295.7,111.287510895,65.5999999046,0,0,0,0,0,0,0,4.44,0,0.0,0.0,4,37,4.43999999998,0.0,0.0,0.0,165.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +39,39,1.0,1469705768.5,53.5,0.0,4.42,33.5,165,7,303.9,111.718168135,67.5,0,0,0,0,0,0,0,4.42,0,0.0,0.0,4,38,4.41999999999,0.0,0.0,0.0,165.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +40,40,1.0,1469705770.2,60.7,0.0,4.38,32.0,167,8,311.1,113.471050335,69.2000000477,0,0,0,0,0,0,0,4.38,0,0.0,0.0,4,39,4.37999999998,0.0,0.0,0.0,167.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +41,41,1.0,1469705772.2,70.1,0.0,4.44,32.0,171,9,320.5,115.140584238,71.2000000477,0,0,0,0,0,0,0,4.44,0,0.0,0.0,4,40,4.43999999998,0.0,0.0,0.0,0.0,171.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +42,42,1.0,1469705774.1,77.8,0.0,4.3,32.0,172,10,328.2,116.273396972,73.0999999046,0,0,0,0,0,0,0,4.3,0,0.0,0.0,4,41,4.30000000002,0.0,0.0,0.0,0.0,172.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +43,43,1.0,1469705776.1,85.2,0.0,4.12,31.0,174,11,335.6,117.556791714,75.0999999046,0,0,0,0,0,0,0,4.12,0,0.0,0.0,4,42,4.12,0.0,0.0,0.0,0.0,174.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +44,44,1.0,1469705777.9,93.4,0.0,4.2,31.5,174,12,343.8,119.395508057,76.9000000954,0,0,0,0,0,0,0,4.2,0,0.0,0.0,4,43,4.19999999999,0.0,0.0,0.0,0.0,174.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +45,45,1.0,1469705779.9,101.5,0.0,4.21,32.0,176,13,351.9,121.165219651,78.9000000954,0,0,0,0,0,0,0,4.21,0,0.0,0.0,4,44,4.20999999999,0.0,0.0,0.0,0.0,176.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +46,46,1.0,1469705781.6,108.9,0.0,4.11,30.5,176,14,359.3,122.757318225,80.5999999046,0,0,0,0,0,0,0,4.11,0,0.0,0.0,4,45,4.10999999998,0.0,0.0,0.0,0.0,176.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +47,47,1.0,1469705783.6,116.9,0.0,4.02,32.0,175,15,367.3,123.722400388,82.5999999046,0,0,0,0,0,0,0,4.02,0,0.0,0.0,4,46,4.01999999999,0.0,0.0,0.0,0.0,175.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +48,48,1.0,1469705785.5,124.1,0.0,3.96,30.5,177,16,374.5,123.80237793,84.5,0,0,0,0,0,0,0,3.96,0,0.0,0.0,4,47,3.95999999999,0.0,0.0,0.0,0.0,177.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +49,49,1.0,1469705787.5,132.1,0.0,4.04,32.0,177,17,382.5,123.700282002,86.5,0,0,0,0,0,0,0,4.04,0,0.0,0.0,4,48,4.03999999999,0.0,0.0,0.0,0.0,177.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +50,50,1.0,1469705789.2,139.7,0.0,4.06,32.0,178,18,390.1,122.672370408,88.2000000477,0,0,0,0,0,0,0,4.06,0,0.0,0.0,4,49,4.05999999999,0.0,0.0,0.0,0.0,178.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +51,51,1.0,1469705791.1,146.7,0.0,4.09,33.0,178,19,397.1,121.011418513,90.0999999046,0,0,0,0,0,0,0,4.09,0,0.0,0.0,4,50,4.09,0.0,0.0,0.0,0.0,178.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +52,52,1.0,1469705793.1,155.4,0.0,4.27,31.0,177,20,405.8,119.62256154,92.0999999046,0,0,0,0,0,0,0,4.27,0,0.0,0.0,4,51,4.27000000001,0.0,0.0,0.0,0.0,177.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +53,53,1.0,1469705794.9,163.1,0.0,4.27,32.0,177,21,413.5,118.342215896,93.9000000954,0,0,0,0,0,0,0,4.27,0,0.0,0.0,4,52,4.27000000001,0.0,0.0,0.0,0.0,177.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +54,54,1.0,1469705797.1,171.6,0.0,4.24,31.5,178,22,422.0,117.740696015,96.0999999046,0,0,0,0,0,0,0,4.24,0,0.0,0.0,4,53,4.24,0.0,0.0,0.0,0.0,178.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +55,55,1.0,1469705798.6,179.3,0.0,4.22,32.0,179,23,429.7,117.599973684,97.5999999046,0,0,0,0,0,0,0,4.22,0,0.0,0.0,4,54,4.22000000001,0.0,0.0,0.0,0.0,179.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +56,56,1.0,1469705800.7,187.9,0.0,4.21,30.5,179,24,438.3,117.565167825,99.7000000477,0,0,0,0,0,0,0,4.21,0,0.0,0.0,4,55,4.20999999999,0.0,0.0,0.0,0.0,179.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +57,57,1.0,1469705802.5,195.8,0.0,4.28,30.5,180,25,446.2,117.967332124,101.5,0,0,0,0,0,0,0,4.28,0,0.0,0.0,4,56,4.27999999998,0.0,0.0,0.0,0.0,180.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +58,58,1.0,1469705804.5,204.3,0.0,4.22,30.5,180,26,454.7,118.489957355,103.5,0,0,0,0,0,0,0,4.22,0,0.0,0.0,4,57,4.22000000001,0.0,0.0,0.0,0.0,180.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +59,59,1.0,1469705806.4,212.2,0.0,4.2,30.5,180,27,462.6,118.763530461,105.400000095,0,0,0,0,0,0,0,4.2,0,0.0,0.0,4,58,4.19999999999,0.0,0.0,0.0,0.0,180.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +60,60,1.0,1469705808.5,221.4,0.0,4.26,29.5,180,28,471.8,117.583212735,107.5,0,0,0,0,0,0,0,4.26,0,0.0,0.0,4,59,4.25999999999,0.0,0.0,0.0,0.0,180.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +61,61,1.0,1469705810.4,229.8,0.0,4.17,30.5,180,29,480.2,119.140852815,109.400000095,0,0,0,0,0,0,0,4.17,0,0.0,0.0,4,60,4.16999999999,0.0,0.0,0.0,0.0,180.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +62,62,1.0,1469705812.3,236.6,0.0,4.11,33.5,181,30,487.0,121.480877381,111.299999952,0,0,0,0,0,0,0,4.11,0,0.0,0.0,4,61,4.10999999998,0.0,0.0,0.0,0.0,0.0,181.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +63,63,1.0,1469705814.0,244.8,0.0,4.24,33.5,181,31,495.2,122.38470448,113.0,0,0,0,0,0,0,0,4.24,0,0.0,0.0,4,62,4.24,0.0,0.0,0.0,0.0,0.0,181.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +64,64,1.0,1469705815.4,250.9,0.0,4.24,33.5,181,31,501.3,122.183234979,114.400000095,0,0,0,0,0,0,0,4.24,0,0.0,0.0,4,63,4.24,0.0,0.0,0.0,0.0,0.0,181.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +65,65,2.0,1469705816.3,0.9,0.0,3.66,28.0,158,1,502.2,121.2563172,115.299999952,0,0,0,0,0,0,0,3.66,0,0.0,0.0,4,63,3.66,0.0,0.0,158.0,0.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +66,66,2.0,1469705817.8,9.8,0.0,4.12,33.5,160,2,511.1,119.786227655,116.799999952,0,0,0,0,0,0,0,4.12,0,0.0,0.0,4,64,4.12,0.0,0.0,160.0,0.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +67,67,2.0,1469705819.6,18.0,0.0,4.44,32.5,162,3,519.3,117.678039467,118.599999905,0,0,0,0,0,0,0,4.44,0,0.0,0.0,4,65,4.43999999998,0.0,0.0,0.0,162.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +68,68,2.0,1469705821.5,26.1,0.0,4.47,33.0,161,4,527.4,115.319480659,120.5,0,0,0,0,0,0,0,4.47,0,0.0,0.0,4,66,4.47000000001,0.0,0.0,0.0,161.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +69,69,2.0,1469705823.4,35.0,0.0,4.43,32.0,164,5,536.3,112.531083761,122.400000095,0,0,0,0,0,0,0,4.43,0,0.0,0.0,4,67,4.42999999999,0.0,0.0,0.0,164.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +70,70,2.0,1469705825.1,41.9,0.0,4.43,33.0,165,6,543.2,109.693981917,124.099999905,0,0,0,0,0,0,0,4.43,0,0.0,0.0,4,68,4.42999999999,0.0,0.0,0.0,165.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +71,71,2.0,1469705827.1,51.5,0.0,4.6,33.5,166,7,552.8,110.111805834,126.099999905,0,0,0,0,0,0,0,4.6,0,0.0,0.0,4,69,4.6,0.0,0.0,0.0,166.0,0.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +72,72,2.0,1469705828.9,59.7,0.0,4.59,33.5,168,8,561.0,110.235734879,127.900000095,0,0,0,0,0,0,0,4.59,0,0.0,0.0,4,70,4.58999999998,0.0,0.0,0.0,0.0,168.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +73,73,2.0,1469705830.7,68.1,0.0,4.53,32.0,171,9,569.4,109.674351541,129.700000048,0,0,0,0,0,0,0,4.53,0,0.0,0.0,4,71,4.53000000001,0.0,0.0,0.0,0.0,171.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +74,74,2.0,1469705832.4,76.4,0.0,4.55,33.0,172,10,577.7,109.081478016,131.400000095,0,0,0,0,0,0,0,4.55,0,0.0,0.0,4,72,4.55,0.0,0.0,0.0,0.0,172.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +75,75,2.0,1469705834.3,84.0,0.0,4.54,32.5,173,11,585.3,108.95736186,133.299999952,0,0,0,0,0,0,0,4.54,0,0.0,0.0,4,73,4.54000000001,0.0,0.0,0.0,0.0,173.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +76,76,2.0,1469705836.0,92.9,0.0,4.63,33.0,174,12,594.2,109.581343074,135.0,0,0,0,0,0,0,0,4.63,0,0.0,0.0,4,74,4.63000000001,0.0,0.0,0.0,0.0,174.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +77,77,2.0,1469705837.9,101.3,0.0,4.61,33.5,176,13,602.6,109.437100452,136.900000095,0,0,0,0,0,0,0,4.61,0,0.0,0.0,4,75,4.61000000001,0.0,0.0,0.0,0.0,176.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +78,78,2.0,1469705839.7,109.6,0.0,4.58,33.5,177,14,610.9,108.898173861,138.700000048,0,0,0,0,0,0,0,4.58,0,0.0,0.0,4,76,4.57999999999,0.0,0.0,0.0,0.0,177.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +79,79,2.0,1469705841.5,118.0,0.0,4.54,33.5,178,15,619.3,108.514739286,140.5,0,0,0,0,0,0,0,4.54,0,0.0,0.0,4,77,4.54000000001,0.0,0.0,0.0,0.0,178.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +80,80,2.0,1469705843.1,125.0,0.0,4.54,34.0,178,16,626.3,108.408891045,142.099999905,0,0,0,0,0,0,0,4.54,0,0.0,0.0,4,78,4.54000000001,0.0,0.0,0.0,0.0,178.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +81,81,2.0,1469705845.1,134.8,0.0,4.69,34.5,179,17,636.1,108.970092917,144.099999905,0,0,0,0,0,0,0,4.69,0,0.0,0.0,4,79,4.69000000002,0.0,0.0,0.0,0.0,179.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +82,82,2.0,1469705846.7,142.2,0.0,4.67,34.0,180,18,643.5,108.943527129,145.700000048,0,0,0,0,0,0,0,4.67,0,0.0,0.0,4,80,4.67000000001,0.0,0.0,0.0,0.0,180.0,0.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +83,83,2.0,1469705848.5,150.6,0.0,4.59,34.5,181,19,651.9,108.491687362,147.5,0,0,0,0,0,0,0,4.59,0,0.0,0.0,4,81,4.58999999998,0.0,0.0,0.0,0.0,0.0,181.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +84,84,2.0,1469705850.3,158.8,0.0,4.52,33.5,181,20,660.1,108.195629805,149.299999952,0,0,0,0,0,0,0,4.52,0,0.0,0.0,4,82,4.51999999998,0.0,0.0,0.0,0.0,0.0,181.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +85,85,2.0,1469705852.1,166.4,0.0,4.52,33.5,181,21,667.7,108.474130564,151.099999905,0,0,0,0,0,0,0,4.52,0,0.0,0.0,4,83,4.51999999998,0.0,0.0,0.0,0.0,0.0,181.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +86,86,2.0,1469705853.9,175.2,0.0,4.68,33.5,182,22,676.5,109.550561798,152.900000095,0,0,0,0,0,0,0,4.68,0,0.0,0.0,4,84,4.67999999998,0.0,0.0,0.0,0.0,0.0,182.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +87,87,2.0,1469705855.7,183.2,0.0,4.65,32.0,182,23,684.5,110.12141592,154.700000048,0,0,0,0,0,0,0,4.65,0,0.0,0.0,4,85,4.65000000002,0.0,0.0,0.0,0.0,0.0,182.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +88,88,2.0,1469705857.5,191.2,0.0,4.51,33.0,182,24,692.5,110.388908617,156.5,0,0,0,0,0,0,0,4.51,0,0.0,0.0,4,86,4.51,0.0,0.0,0.0,0.0,0.0,182.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +89,89,2.0,1469705859.4,200.1,0.0,4.42,32.0,183,25,701.4,111.075438088,158.400000095,0,0,0,0,0,0,0,4.42,0,0.0,0.0,4,87,4.41999999999,0.0,0.0,0.0,0.0,0.0,183.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +90,90,2.0,1469705861.2,207.4,0.0,4.38,32.0,183,26,708.7,112.186192469,160.200000048,0,0,0,0,0,0,0,4.38,0,0.0,0.0,4,88,4.37999999998,0.0,0.0,0.0,0.0,0.0,183.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +91,91,2.0,1469705863.0,216.1,0.0,4.47,32.0,183,27,717.4,113.541324808,162.0,0,0,0,0,0,0,0,4.47,0,0.0,0.0,4,89,4.47000000001,0.0,0.0,0.0,0.0,0.0,183.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +92,92,2.0,1469705865.0,224.7,0.0,4.44,32.5,183,28,726.0,113.933934253,164.0,0,0,0,0,0,0,0,4.44,0,0.0,0.0,4,90,4.43999999998,0.0,0.0,0.0,0.0,0.0,183.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +93,93,2.0,1469705867.1,232.4,0.0,4.34,33.0,184,29,733.7,114.217860585,166.099999905,0,0,0,0,0,0,0,4.34,0,0.0,0.0,4,91,4.34,0.0,0.0,0.0,0.0,0.0,184.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +94,94,2.0,1469705868.6,240.6,0.0,4.35,32.0,184,30,741.9,114.508095643,167.599999905,0,0,0,0,0,0,0,4.35,0,0.0,0.0,4,92,4.34999999999,0.0,0.0,0.0,0.0,0.0,184.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +95,95,2.0,1469705870.7,249.0,0.0,4.34,30.5,184,31,750.3,114.922206507,169.700000048,0,0,0,0,0,0,0,4.34,0,0.0,0.0,4,93,4.34,0.0,0.0,0.0,0.0,0.0,184.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 +96,96,2.0,1469705871.0,250.4,0.0,4.34,30.5,184,31,751.7,115.581707376,170.0,0,0,0,0,0,0,0,4.34,0,0.0,0.0,4,93,4.34,0.0,0.0,0.0,0.0,0.0,184.0,142,146,160,167,180,192,0.0,0.0,0.0,0.0,0.0,0.0,124.3,169.5,203.4,237.3,271.2 diff --git a/rowers/tests/testdata/testdata.csv.gz b/rowers/tests/testdata/testdata.csv.gz index 5112eb13dbcf91d0fc9543432ae2b25ac758d3ac..7dc55d87c477e548aa76296844e2bf2104abde41 100644 GIT binary patch literal 11457 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 bE%OG|dV_eM5SB%T-Ol@e0koFQfOh}@SgjW_ literal 11426 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≧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 w5V 1: + modality = 'all' + else: + modality = modalities[0] + else: + modalities = [m[0] for m in mytypes.workouttypes] + modality = 'all' + + + try: + rankingonly = options['rankingonly'] + except KeyError: + rankingonly = False + + try: + includereststrokes = options['includereststrokes'] + except KeyError: + includereststrokes = False + + + workstrokesonly = not includereststrokes + + waterboattype = mytypes.waterboattype + + + if startdatestring != "": + startdate = iso8601.parse_date(startdatestring) + + if enddatestring != "": + enddate = iso8601.parse_date(enddatestring) + + if enddate < startdate: + s = enddate + enddate = startdate + startdate = s + + + # get all indoor rows of in date range + + # process form + if request.method == 'POST': + form = DateRangeForm(request.POST) + modalityform = TrendFlexModalForm(request.POST) + if form.is_valid(): + startdate = form.cleaned_data['startdate'] + enddate = form.cleaned_data['enddate'] + if startdate > enddate: + s = enddate + enddate = startdate + startdate = s + startdatestring = startdate.strftime('%Y-%m-%d') + enddatestring = enddate.strftime('%Y-%m-%d') + if modalityform.is_valid(): + modality = modalityform.cleaned_data['modality'] + waterboattype = modalityform.cleaned_data['waterboattype'] + rankingonly = modalityform.cleaned_data['rankingonly'] + if modality == 'all': + modalities = [m[0] for m in mytypes.workouttypes] + else: + modalities = [modality] + + if modality != 'water': + waterboattype = [b[0] for b in mytypes.boattypes] + + + request.session['modalities'] = modalities + request.session['waterboattype'] = waterboattype + request.session['rankingonly'] = rankingonly + form = DateRangeForm(initial={ + 'startdate': startdate, + 'enddate': enddate, + }) + else: + form = DateRangeForm(initial={ + 'startdate': startdate, + 'enddate': enddate, + }) + includereststrokes = False + + workstrokesonly = not includereststrokes + modalityform = TrendFlexModalForm( + initial={ + 'modality':modality, + 'waterboattype':waterboattype, + 'rankingonly':rankingonly, + } + ) + + negtypes = [] + for b in mytypes.boattypes: + if b[0] not in waterboattype: + negtypes.append(b[0]) + + + + script = '' + div = get_call() + js_resources = '' + css_resources = '' + + + + + options = { + 'modality': modality, + 'theuser': theuser.id, + 'waterboattype':waterboattype, + 'startdatestring':startdatestring, + 'enddatestring':enddatestring, + 'rankingonly':rankingonly, + 'includereststrokes':includereststrokes, + } + + request.session['options'] = options + + promember=0 + mayedit=0 + if not request.user.is_anonymous(): + result = request.user.is_authenticated() and ispromember(request.user) + if result: + promember = 1 + + + request.session['options'] = options + + return render(request, 'histo.html', + {'interactiveplot':script, + 'the_div':div, + 'id':theuser, + 'active':'nav-analysis', + 'theuser':theuser, + 'rower':r, + 'startdate':startdate, + 'enddate':enddate, + 'form':form, + 'optionsform':modalityform, + 'teams':get_my_teams(request.user), + }) # The Flex plot for a large selection of workouts @login_required() diff --git a/rowers/views/statements.py b/rowers/views/statements.py index 28319cb6..6b3cddeb 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -255,6 +255,15 @@ def getfavorites(r,row): return favorites,maxfav +def get_workout_default_page(request,id): + if request.user.is_anonymous(): + return reverse('workout_view',kwargs={'id':str(id)}) + else: + r = Rower.objects.get(user=request.user) + if r.defaultlandingpage == 'workout_edit_view': + return reverse('workout_edit_view',kwargs={'id':str(id)}) + else: + return reverse('workout_workflow_view',kwargs={'id':str(id)}) def getrequestrower(request,rowerid=0,userid=0,notpermanent=False): diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index e97af583..8fcb3866 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -41,7 +41,7 @@ def workout_forcecurve_view(request,id=0,workstrokesonly=False): 'name': row.name }, { - 'url':reverse(workout_forcecurve_view,kwargs={'id':id}), + 'url':reverse('workout_forcecurve_view',kwargs={'id':id}), 'name': 'Empower Force Curve' } @@ -73,7 +73,7 @@ def workout_test_task_view(request,id=0): res = myqueue(queuehigh,addcomment2,request.user.id,row.id) - url = reverse(workout_edit_view, + url = reverse('workout_edit_view', kwargs = { 'id':int(id), }) @@ -111,7 +111,7 @@ def workout_histo_view(request,id=0): 'name': w.name }, { - 'url':reverse(workout_histo_view,kwargs={'id':id}), + 'url':reverse('workout_histo_view',kwargs={'id':id}), 'name': 'Histogram' } @@ -133,177 +133,6 @@ def workout_histo_view(request,id=0): -# Histogram for a date/time range -@user_passes_test(ispromember,login_url="/rowers/paidplans", - message="This functionality requires a Pro plan or higher", - redirect_field_name=None) -def histo(request,theuser=0, - startdate=timezone.now()-datetime.timedelta(days=365), - enddate=timezone.now(), - deltadays=-1, - enddatestring=timezone.now().strftime("%Y-%m-%d"), - startdatestring=(timezone.now()-datetime.timedelta(days=30)).strftime("%Y-%m-%d"), - options={ - 'includereststrokes':False, - 'workouttypes':[i[0] for i in mytypes.workouttypes], - 'waterboattype':mytypes.waterboattype, - 'rankingonly': False, - }): - - r = getrequestrower(request,userid=theuser) - theuser = r.user - - if 'waterboattype' in request.session: - waterboattype = request.session['waterboattype'] - else: - waterboattype = mytypes.waterboattype - - - if 'rankingonly' in request.session: - rankingonly = request.session['rankingonly'] - else: - rankingonly = False - - if 'modalities' in request.session: - modalities = request.session['modalities'] - if len(modalities) > 1: - modality = 'all' - else: - modality = modalities[0] - else: - modalities = [m[0] for m in mytypes.workouttypes] - modality = 'all' - - - try: - rankingonly = options['rankingonly'] - except KeyError: - rankingonly = False - - try: - includereststrokes = options['includereststrokes'] - except KeyError: - includereststrokes = False - - - workstrokesonly = not includereststrokes - - waterboattype = mytypes.waterboattype - - - if startdatestring != "": - startdate = iso8601.parse_date(startdatestring) - - if enddatestring != "": - enddate = iso8601.parse_date(enddatestring) - - if enddate < startdate: - s = enddate - enddate = startdate - startdate = s - - - # get all indoor rows of in date range - - # process form - if request.method == 'POST': - form = DateRangeForm(request.POST) - modalityform = TrendFlexModalForm(request.POST) - if form.is_valid(): - startdate = form.cleaned_data['startdate'] - enddate = form.cleaned_data['enddate'] - if startdate > enddate: - s = enddate - enddate = startdate - startdate = s - startdatestring = startdate.strftime('%Y-%m-%d') - enddatestring = enddate.strftime('%Y-%m-%d') - if modalityform.is_valid(): - modality = modalityform.cleaned_data['modality'] - waterboattype = modalityform.cleaned_data['waterboattype'] - rankingonly = modalityform.cleaned_data['rankingonly'] - if modality == 'all': - modalities = [m[0] for m in mytypes.workouttypes] - else: - modalities = [modality] - - if modality != 'water': - waterboattype = [b[0] for b in mytypes.boattypes] - - - request.session['modalities'] = modalities - request.session['waterboattype'] = waterboattype - request.session['rankingonly'] = rankingonly - form = DateRangeForm(initial={ - 'startdate': startdate, - 'enddate': enddate, - }) - else: - form = DateRangeForm(initial={ - 'startdate': startdate, - 'enddate': enddate, - }) - includereststrokes = False - - workstrokesonly = not includereststrokes - modalityform = TrendFlexModalForm( - initial={ - 'modality':modality, - 'waterboattype':waterboattype, - 'rankingonly':rankingonly, - } - ) - - negtypes = [] - for b in mytypes.boattypes: - if b[0] not in waterboattype: - negtypes.append(b[0]) - - - - script = '' - div = get_call() - js_resources = '' - css_resources = '' - - - - - options = { - 'modality': modality, - 'theuser': theuser.id, - 'waterboattype':waterboattype, - 'startdatestring':startdatestring, - 'enddatestring':enddatestring, - 'rankingonly':rankingonly, - 'includereststrokes':includereststrokes, - } - - request.session['options'] = options - - promember=0 - mayedit=0 - if not request.user.is_anonymous(): - result = request.user.is_authenticated() and ispromember(request.user) - if result: - promember = 1 - - - request.session['options'] = options - - return render(request, 'histo.html', - {'interactiveplot':script, - 'the_div':div, - 'id':theuser, - 'active':'nav-analysis', - 'theuser':theuser, - 'rower':r, - 'startdate':startdate, - 'enddate':enddate, - 'form':form, - 'optionsform':modalityform, - 'teams':get_my_teams(request.user), - }) # add a workout manually @login_required() @@ -316,7 +145,7 @@ def addmanual_view(request): 'name':'Workouts' }, { - 'url':reverse(addmanual_view), + 'url':reverse('addmanual_view'), 'name': 'Add Manual Entry' }, ] @@ -418,7 +247,7 @@ def addmanual_view(request): messages.info(request,'New workout created') url = reverse( - workout_edit_view, + 'workout_edit_view', kwargs={'id':id} ) return HttpResponseRedirect(url) @@ -457,7 +286,7 @@ def fitness_metric_view(request,mode='rower',days=42): # test if not something already done ms = PowerTimeFitnessMetric.objects.filter(user=request.user) if not ms: - url = reverse(workouts_view) + url = reverse('workouts_view') return HttpResponseRedirect(url) max_workout_id = max([m.last_workout for m in ms]) @@ -502,7 +331,7 @@ def workout_update_cp_view(request,id=0): if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" messages.error(request,message) - url = reverse(workouts_view) + url = reverse('workouts_view') return HttpResponseRedirect(url) @@ -529,7 +358,7 @@ def workout_recalcsummary_view(request,id=0): if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" messages.error(request,message) - url = reverse(workouts_view) + url = reverse('workouts_view') return HttpResponseRedirect(url) @@ -540,14 +369,14 @@ def workout_recalcsummary_view(request,id=0): row.save() successmessage = "Summary Updated" messages.info(request,successmessage) - url = reverse(workout_edit_view, + url = reverse('workout_edit_view', kwargs = { 'id':int(id), }) else: message = "Something went wrong. Could not update summary" messages.error(request,message) - url = reverse(workout_edit_view, + url = reverse('workout_edit_view', kwargs = { 'id':int(id), }) @@ -599,7 +428,7 @@ def workouts_join_view(request): return HttpResponse("form is not valid") else: - url = reverse(workouts_join_select) + url = reverse('workouts_join_select') return HttpResponseRedirect(url) @user_passes_test(ispromember,login_url="/rowers/paidplans", @@ -1082,11 +911,11 @@ def virtualevent_compare_view(request,id=0): breadcrumbs = [ { - 'url': reverse(virtualevents_view), + 'url': reverse('virtualevents_view'), 'name': 'Racing' }, { - 'url':reverse(virtualevent_view, + 'url':reverse('virtualevent_view', kwargs={ 'id':race.id, } @@ -1094,7 +923,7 @@ def virtualevent_compare_view(request,id=0): 'name': race.name }, { - 'url':reverse(virtualevent_compare_view, + 'url':reverse('virtualevent_compare_view', kwargs={ 'id':race.id, } @@ -1159,7 +988,7 @@ def plannedsession_compare_view(request,id=0,userid=0): request.session['plottype'] = plottype request.session['ps'] = ps.id - url = reverse(multi_compare_view,kwargs={'userid':userid,'id':ids[0]}) + url = reverse('multi_compare_view',kwargs={'userid':userid,'id':ids[0]}) return HttpResponseRedirect(url) @@ -1237,7 +1066,7 @@ def multi_compare_view(request,id=0,userid=0): ) else: - url = reverse(team_comparison_select, + url = reverse('team_comparison_select', kwargs={ 'id':id, 'teamid':0}) @@ -1262,11 +1091,11 @@ def multi_compare_view(request,id=0,userid=0): 'name':'Workouts' }, { - 'url':reverse(team_comparison_select,kwargs={'teamid':teamid}), + 'url':reverse('team_comparison_select',kwargs={'teamid':teamid}), 'name': 'Compare Select' }, { - 'url':reverse(multi_compare_view), + 'url':reverse('multi_compare_view'), 'name': 'Comparison Chart' } ] @@ -1275,12 +1104,12 @@ def multi_compare_view(request,id=0,userid=0): ps = PlannedSession.objects.get(id=int(request.session['ps'])) breadcrumbs = [ { - 'url': reverse(plannedsessions_view, + 'url': reverse('plannedsessions_view', kwargs={'userid':userid}), 'name': 'Sessions' }, { - 'url':reverse(plannedsession_view, + 'url':reverse('plannedsession_view', kwargs={ 'userid':userid, 'id':ps.id, @@ -1289,7 +1118,7 @@ def multi_compare_view(request,id=0,userid=0): 'name': ps.id }, { - 'url':reverse(plannedsession_compare_view, + 'url':reverse('plannedsession_compare_view', kwargs={ 'userid':userid, 'id':ps.id, @@ -1610,7 +1439,7 @@ def workout_fusion_list(request,id=0,message='',successmessage='', 'name': row.name }, { - 'url':reverse(workout_fusion_list,kwargs={'id':id}), + 'url':reverse('workout_fusion_list',kwargs={'id':id}), 'name': 'Sensor Fusion' } @@ -1703,7 +1532,7 @@ def workout_view(request,id=0): 'name':'Workouts' }, { - 'url':reverse(workout_view,kwargs={'id':id}), + 'url':reverse('workout_view',kwargs={'id':id}), 'name': row.name, } @@ -1745,7 +1574,7 @@ def workout_undo_smoothenpace_view( if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" messages.error(request,message) - url = reverse(workouts_view) + url = reverse('workouts_view') return HttpResponseRedirect(url) @@ -1785,7 +1614,7 @@ def workout_smoothenpace_view(request,id=0,message="",successmessage=""): if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" messages.error(request,message) - url = reverse(workouts_view) + url = reverse('workouts_view') return HttpResponseRedirect(url) @@ -1841,7 +1670,7 @@ def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""): 'name': row.name }, { - 'url':reverse(workout_crewnerd_summary_view,kwargs={'id':id}), + 'url':reverse('workout_crewnerd_summary_view',kwargs={'id':id}), 'name': 'CrewNerd Summary' } @@ -1860,7 +1689,7 @@ def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""): os.remove(fname) successmessage = "CrewNerd summary added" messages.info(request,successmessage) - url = reverse(workout_edit_view, + url = reverse('workout_edit_view', kwargs = { 'id':int(id), }) @@ -1873,7 +1702,7 @@ def workout_crewnerd_summary_view(request,id=0,message="",successmessage=""): pass message = "Something went wrong (workout_crewnerd_summary_view)" messages.error(request,message) - url = reverse(workout_edit_view, + url = reverse('workout_edit_view', kwargs = { 'id':int(id), }) @@ -1914,7 +1743,7 @@ def workout_downloadwind_view(request,id=0, if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" messages.error(request,message) - url = reverse(workouts_view) + url = reverse('workouts_view') return HttpResponseRedirect(url) @@ -1954,7 +1783,7 @@ def workout_downloadwind_view(request,id=0, kwargs = { 'id':int(id)} - url = reverse(workout_wind_view,kwargs=kwargs) + url = reverse('workout_wind_view',kwargs=kwargs) response = HttpResponseRedirect(url) except KeyError: message = "No latitude/longitude data" @@ -1962,7 +1791,7 @@ def workout_downloadwind_view(request,id=0, kwargs = { 'id':int(id) } - url = reverse(workout_wind_view,kwargs=kwargs) + url = reverse('workout_wind_view',kwargs=kwargs) response = HttpResponseRedirect(url) @@ -1980,7 +1809,7 @@ def workout_downloadmetar_view(request,id=0, if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" messages.error(request,message) - url = reverse(workouts_view) + url = reverse('workouts_view') return HttpResponseRedirect(url) @@ -2020,7 +1849,7 @@ def workout_downloadmetar_view(request,id=0, kwargs = { 'id':int(id)} - url = reverse(workout_wind_view,kwargs=kwargs) + url = reverse('workout_wind_view',kwargs=kwargs) response = HttpResponseRedirect(url) except KeyError: message = "No latitude/longitude data" @@ -2028,7 +1857,7 @@ def workout_downloadmetar_view(request,id=0, kwargs = { 'id':int(id) } - url = reverse(workout_wind_view,kwargs=kwargs) + url = reverse('workout_wind_view',kwargs=kwargs) response = HttpResponseRedirect(url) @@ -2051,7 +1880,7 @@ def workout_wind_view(request,id=0,message="",successmessage=""): 'name': row.name }, { - 'url':reverse(workout_wind_view,kwargs={'id':id}), + 'url':reverse('workout_wind_view',kwargs={'id':id}), 'name': 'Wind' } @@ -2060,7 +1889,7 @@ def workout_wind_view(request,id=0,message="",successmessage=""): if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" messages.error(request,message) - url = reverse(workouts_view) + url = reverse('workouts_view') return HttpResponseRedirect(url) @@ -2132,7 +1961,7 @@ def workout_wind_view(request,id=0,message="",successmessage=""): kwargs = { 'id':int(id) } - url = reverse(workout_wind_view,kwargs=kwargs) + url = reverse('workout_wind_view',kwargs=kwargs) response = HttpResponseRedirect(url) else: @@ -2181,7 +2010,7 @@ def workout_stream_view(request,id=0,message="",successmessage=""): if (checkworkoutuser(request.user,row)==False): message = "You are not allowed to edit this workout" messages.error(request,message) - url = reverse(workouts_view) + url = reverse('workouts_view') return HttpResponseRedirect(url) @@ -2218,7 +2047,7 @@ def workout_stream_view(request,id=0,message="",successmessage=""): messages.error(request,message) kwargs = { 'id':int(id)} - url = reverse(workout_wind_view,kwargs=kwargs) + url = reverse('workout_wind_view',kwargs=kwargs) response = HttpResponseRedirect(url) else: @@ -2239,7 +2068,7 @@ def workout_stream_view(request,id=0,message="",successmessage=""): 'name': row.name }, { - 'url':reverse(workout_stream_view,kwargs={'id':id}), + 'url':reverse('workout_stream_view',kwargs={'id':id}), 'name': 'Stream' } @@ -2273,7 +2102,7 @@ def workout_otwsetpower_view(request,id=0,message="",successmessage=""): if (checkworkoutuser(request.user,w)==False): message = "You are not allowed to edit this workout" messages.error(request,message) - url = reverse(workouts_view) + url = reverse('workouts_view') return HttpResponseRedirect(url) @@ -2345,7 +2174,7 @@ def workout_otwsetpower_view(request,id=0,message="",successmessage=""): try: url = request.session['referer'] except KeyError: - url = reverse(workout_advanced_view,kwargs=kwargs) + url = reverse('workout_edit_view',kwargs=kwargs) response = HttpResponseRedirect(url) return response @@ -2355,7 +2184,7 @@ def workout_otwsetpower_view(request,id=0,message="",successmessage=""): messages.error(request,message) kwargs = { 'id':int(id)} - url = reverse(workout_otwsetpower_view,kwargs=kwargs) + url = reverse('workout_otwsetpower_view',kwargs=kwargs) response = HttpResponseRedirect(url) else: @@ -2371,7 +2200,7 @@ def workout_otwsetpower_view(request,id=0,message="",successmessage=""): 'name': w.name }, { - 'url':reverse(workout_otwsetpower_view,kwargs={'id':id}), + 'url':reverse('workout_otwsetpower_view',kwargs={'id':id}), 'name': 'OTW Power' } @@ -2411,7 +2240,7 @@ def instroke_view(request,id=0): 'name': w.name }, { - 'url':reverse(instroke_view,kwargs={'id':id}), + 'url':reverse('instroke_view',kwargs={'id':id}), 'name': 'In-Stroke Metrics' } @@ -2424,7 +2253,7 @@ def instroke_view(request,id=0): if (checkworkoutuser(request.user,w)==False): message = "You are not allowed to edit this workout" messages.error(request,message) - url = reverse(workouts_view) + url = reverse('workouts_view') return HttpResponseRedirect(url) @@ -2456,7 +2285,7 @@ def instroke_chart(request,id=0,metric=''): if (checkworkoutuser(request.user,w)==False): message = "You are not allowed to edit this workout" messages.error(request,message) - url = reverse(workouts_view) + url = reverse('workouts_view') return HttpResponseRedirect(url) @@ -2526,7 +2355,7 @@ def workout_data_view(request, id=0): 'name': w.name }, { - 'url':reverse(workout_data_view,kwargs={'id':id}), + 'url':reverse('workout_data_view',kwargs={'id':id}), 'name': 'Data Explorer' } @@ -2628,7 +2457,7 @@ def workout_stats_view(request,id=0,message="",successmessage=""): 'name': w.name }, { - 'url':reverse(workout_stats_view,kwargs={'id':id}), + 'url':reverse('workout_stats_view',kwargs={'id':id}), 'name': 'Stats' } @@ -2786,19 +2615,10 @@ def workflow_default_view(request): r.save() - url = reverse(workout_workflow_config2_view) + url = reverse('workout_workflow_config2_view') return HttpResponseRedirect(url) -def get_workout_default_page(request,id): - if request.user.is_anonymous(): - return reverse(workout_view,kwargs={'id':str(id)}) - else: - r = Rower.objects.get(user=request.user) - if r.defaultlandingpage == 'workout_edit_view': - return reverse(workout_edit_view,kwargs={'id':str(id)}) - else: - return reverse(workout_workflow_view,kwargs={'id':str(id)}) # Workflow configuration @login_required() @@ -2946,7 +2766,7 @@ def workout_workflow_view(request,id): 'name': row.name }, { - 'url':reverse(workout_workflow_view,kwargs={'id':id}), + 'url':reverse('workout_workflow_view',kwargs={'id':id}), 'name': 'View' } @@ -3232,7 +3052,7 @@ def workout_flexchart3_view(request,*args,**kwargs): 'name': row.name }, { - 'url':reverse(workout_flexchart3_view,kwargs=kwargs), + 'url':reverse('workout_flexchart3_view',kwargs=kwargs), 'name': 'Flex Chart' } @@ -3286,7 +3106,7 @@ def workout_otwpowerplot_view(request,id=0,message="",successmessage=""): 'name': w.name }, { - 'url':reverse(workout_otwpowerplot_view,kwargs={'id':id}), + 'url':reverse('workout_otwpowerplot_view',kwargs={'id':id}), 'name': 'Interactive OTW Power Plot' } @@ -3387,7 +3207,7 @@ def workout_comment_view(request,id=0): c = WorkoutComment(workout=w,user=request.user,comment=comment, notification=notification) c.save() - url = reverse(workout_comment_view, + url = reverse('workout_comment_view', kwargs={ 'id':id }) @@ -3428,8 +3248,9 @@ def workout_comment_view(request,id=0): emailbounced = ocr.emailbounced ) - url = reverse(workout_comment_view,kwargs = { - 'id':id}) + url = reverse('workout_comment_view', + kwargs = { + 'id':id}) return HttpResponseRedirect(url) form = WorkoutCommentForm() @@ -3456,7 +3277,7 @@ def workout_comment_view(request,id=0): 'name': w.name }, { - 'url':reverse(workout_comment_view,kwargs={'id':id}), + 'url':reverse('workout_comment_view',kwargs={'id':id}), 'name': 'Comments' } @@ -3600,7 +3421,7 @@ def workout_edit_view(request,id=0,message="",successmessage=""): dataprep.runcpupdate(row.user,type=row.workouttype) messages.info(request,successmessage) - url = reverse(workout_edit_view, + url = reverse('workout_edit_view', kwargs = { 'id':str(row.id), }) @@ -3665,7 +3486,7 @@ def workout_edit_view(request,id=0,message="",successmessage=""): 'name': row.name }, { - 'url':reverse(workout_edit_view,kwargs={'id':id}), + 'url':reverse('workout_edit_view',kwargs={'id':id}), 'name': 'Edit' } @@ -3713,7 +3534,7 @@ def workout_map_view(request,id=0): 'name': w.name }, { - 'url':reverse(workout_map_view,kwargs={'id':id}), + 'url':reverse('workout_map_view',kwargs={'id':id}), 'name': 'Map' } @@ -3789,7 +3610,7 @@ def workout_uploadimage_view(request,id): 'name': w.name }, { - 'url':reverse(workout_uploadimage_view,kwargs={'id':id}), + 'url':reverse('workout_uploadimage_view',kwargs={'id':id}), 'name': 'Upload Image' } @@ -3825,7 +3646,7 @@ def workout_uploadimage_view(request,id): message = "Not a valid image" messages.error(request,message) os.remove(path_and_filename) - url = reverse(workout_uploadimage_view, + url = reverse('workout_uploadimage_view', kwargs = {'id':id}) if is_ajax: @@ -3847,7 +3668,7 @@ def workout_uploadimage_view(request,id): return HttpResponseRedirect(url) else: messages.error(request,'Something went wrong - no file attached') - url = reverse(workout_uploadimage_view, + url = reverse('workout_uploadimage_view', kwargs = {'id':id}) if is_ajax: @@ -3929,7 +3750,7 @@ def workout_toggle_ranking(request,id=0): return response else: - url = reverse(workouts_view) + url = reverse('workouts_view') response = HttpResponseRedirect(url) return response @@ -3961,7 +3782,7 @@ def workout_upload_view(request, 'name':'Workouts' }, { - 'url': reverse(workout_upload_view), + 'url': reverse('workout_upload_view'), 'name': 'Upload' } ] @@ -4063,7 +3884,7 @@ def workout_upload_view(request, else: messages.error(request, "Something went wrong - no file attached") - url = reverse(workout_upload_view) + url = reverse('workout_upload_view') if is_ajax: return JSONResponse({'result':0,'url':0}) else: @@ -4151,7 +3972,7 @@ def workout_upload_view(request, messages.info( request, "The file was too large to process in real time. It will be processed in a background process. You will receive an email when it is ready") - url = reverse(workout_upload_view) + url = reverse('workout_upload_view') if is_ajax: return JSONResponse({'result':1,'url':url}) else: @@ -4160,7 +3981,7 @@ def workout_upload_view(request, if not id: messages.error(request,message) - url = reverse(workout_upload_view) + url = reverse('workout_upload_view') if is_ajax: return JSONResponse({'result':0,'url':url}) else: @@ -4169,7 +3990,7 @@ def workout_upload_view(request, elif id == -1: message = 'The zip archive will be processed in the background. The files in the archive will only be uploaded without the extra actions. You will receive email when the workouts are ready.' messages.info(request,message) - url = reverse(workout_upload_view) + url = reverse('workout_upload_view') if is_ajax: return JSONResponse({'result':1,'url':url}) else: @@ -4179,7 +4000,7 @@ def workout_upload_view(request, if message: messages.error(request,message) - url = reverse(workout_edit_view, + url = reverse('workout_edit_view', kwargs = { 'id':int(id), }) @@ -4387,7 +4208,7 @@ def team_workout_upload_view(request,message="", 'name':'Workouts' }, { - 'url': reverse(team_workout_upload_view), + 'url': reverse('team_workout_upload_view'), 'name': 'Team Upload' } ] @@ -4477,20 +4298,20 @@ def team_workout_upload_view(request,message="", ) - url = reverse(team_workout_upload_view) + url = reverse('team_workout_upload_view') response = HttpResponseRedirect(url) return response if not id: messages.error(request,message) - url = reverse(team_workout_upload_view) + url = reverse('team_workout_upload_view') response = HttpResponseRedirect(url) return response elif id == -1: message = 'The zip archive will be processed in the background. The files in the archive will only be uploaded without the extra actions. You will receive email when the workouts are ready.' messages.info(request,message) - url = reverse(team_workout_upload_view) + url = reverse('team_workout_upload_view') response = HttpResponseRedirect(url) return response @@ -4498,7 +4319,7 @@ def team_workout_upload_view(request,message="", successmessage = "The workout was added to the user's account" messages.info(request,successmessage) - url = reverse(team_workout_upload_view) + url = reverse('team_workout_upload_view') response = HttpResponseRedirect(url) w = Workout.objects.get(id=id) @@ -4546,7 +4367,7 @@ def team_workout_upload_view(request,message="", # A page with all the recent graphs (searchable on workout name) @login_required() def graphs_view(request): - request.session['referer'] = reverse(graphs_view) + request.session['referer'] = reverse('graphs_view') try: r = getrower(request.user) workouts = Workout.objects.filter(user=r).order_by("-date", "-starttime") @@ -4611,7 +4432,7 @@ def graph_show_view(request,id): 'name': w.name }, { - 'url':reverse(graph_show_view,kwargs={'id':id}), + 'url':reverse('graph_show_view',kwargs={'id':id}), 'name': 'Chart' } @@ -4679,7 +4500,7 @@ def workout_summary_restore_view(request,id,message="",successmessage=""): messages.info(request,'Original Interval Data Restored') - url = reverse(workout_summary_edit_view, + url = reverse('workout_summary_edit_view', kwargs={ 'id':int(id), } @@ -4703,7 +4524,8 @@ def workout_split_view(request,id=id): 'name': row.name }, { - 'url':reverse(graph_show_view,kwargs={'id':id}), + 'url':reverse('graph_show_view', + kwargs={'id':id}), 'name': 'Chart' } @@ -4729,7 +4551,7 @@ def workout_split_view(request,id=id): if request.user == r: - url = reverse(workouts_view) + url = reverse('workouts_view') else: mgrids = [team.id for team in Team.objects.filter(manager=request.user)] rwrids = [team.id for team in r.team.all()] @@ -4737,13 +4559,13 @@ def workout_split_view(request,id=id): if len(teamids) > 0: teamid = teamids[0] - url = reverse(workouts_view, + url = reverse('workouts_view', kwargs={ 'teamid':int(teamid), } ) else: - url = reverse(workouts_view) + url = reverse('workouts_view') rowname = row.name if isinstance(rowname,unicode): @@ -4817,7 +4639,7 @@ def workout_fusion_view(request,id1=0,id2=1): successmessage = 'Data fused' messages.info(request,message) - url = reverse(workout_edit_view, + url = reverse('workout_edit_view', kwargs={ 'id':idnew, }) @@ -4836,11 +4658,12 @@ def workout_fusion_view(request,id1=0,id2=1): 'name': str(w1.id) }, { - 'url':reverse(workout_fusion_list,kwargs={'id':id1}), + 'url':reverse('workout_fusion_list',kwargs={'id':id1}), 'name': 'Sensor Fusion' }, { - 'url':reverse(workout_fusion_view,kwargs={'id1':id1,'id2':id2}), + 'url':reverse('workout_fusion_view', + kwargs={'id1':id1,'id2':id2}), 'name': str(w2.id) } @@ -4874,7 +4697,7 @@ def workout_summary_edit_view(request,id,message="",successmessage="" 'name': row.name }, { - 'url':reverse(workout_summary_edit_view,kwargs={'id':id}), + 'url':reverse('workout_summary_edit_view',kwargs={'id':id}), 'name': 'Edit Intervals' } @@ -5296,7 +5119,7 @@ class GraphDelete(DeleteView): 'name': str(self.object.workout.id) }, { - 'url':reverse(graph_show_view,kwargs={'id':self.object.pk}), + 'url':reverse('graph_show_view',kwargs={'id':self.object.pk}), 'name': 'Chart' }, { 'url':reverse('graph_delete',kwargs={'pk':str(self.object.pk)}), @@ -5314,7 +5137,7 @@ class GraphDelete(DeleteView): def get_success_url(self): w = self.object.workout - return reverse(workout_edit_view,kwargs={'id':str(w.id)}) + return reverse('workout_edit_view',kwargs={'id':str(w.id)}) def get_object(self, *args, **kwargs): obj = super(GraphDelete, self).get_object(*args, **kwargs) @@ -5368,7 +5191,7 @@ class WorkoutDelete(DeleteView): def get_success_url(self): - return reverse(workouts_view) + return reverse('workouts_view') def get_object(self, *args, **kwargs): obj = super(WorkoutDelete, self).get_object(*args, **kwargs)