diff --git a/rowers/admin.py b/rowers/admin.py index 5be9aa7f..4c2c861c 100644 --- a/rowers/admin.py +++ b/rowers/admin.py @@ -7,7 +7,7 @@ from .models import ( Team,TeamInvite,TeamRequest, WorkoutComment,C2WorldClassAgePerformance,PlannedSession, GeoCourse,GeoPolygon,GeoPoint,VirtualRace,VirtualRaceResult, - PaidPlan + PaidPlan,IndoorVirtualRaceResult, ) # Register your models here so you can use them in the Admin module @@ -123,6 +123,10 @@ class VirtualRaceResultAdmin(admin.ModelAdmin): list_display = ('race','userid','username','boattype','age','weightcategory') search_fields = ['race__name','username'] +class IndoorVirtualRaceResultAdmin(admin.ModelAdmin): + list_display = ('race','userid','username','boatclass','age','weightcategory') + search_fields = ['race__name','username'] + class PaidPlanAdmin(admin.ModelAdmin): list_display = ('name','shortname','price','paymenttype','paymentprocessor','clubsize','external_id') @@ -142,4 +146,5 @@ admin.site.register(PlannedSession,PlannedSessionAdmin) admin.site.register(GeoCourse, GeoCourseAdmin) admin.site.register(VirtualRace, VirtualRaceAdmin) admin.site.register(VirtualRaceResult, VirtualRaceResultAdmin) +admin.site.register(IndoorVirtualRaceResult, IndoorVirtualRaceResultAdmin) admin.site.register(PaidPlan,PaidPlanAdmin) diff --git a/rowers/c2stuff.py b/rowers/c2stuff.py index dd345736..8a82b6a5 100644 --- a/rowers/c2stuff.py +++ b/rowers/c2stuff.py @@ -572,8 +572,10 @@ def get_workout(user,c2id): if 'workout' in data: if 'splits' in data['workout']: splitdata = data['workout']['splits'] - if 'intervals' in data['workout']: + elif 'intervals' in data['workout']: splitdata = data['workout']['intervals'] + else: + splitdata = None # Check if workout has stroke data, and get the stroke data diff --git a/rowers/dataprep.py b/rowers/dataprep.py index 45914354..db7cd1fe 100644 --- a/rowers/dataprep.py +++ b/rowers/dataprep.py @@ -36,9 +36,12 @@ from rowingdata import ( SpeedCoach2Parser, FITParser, fitsummarydata, RitmoTimeParser,KinoMapParser, make_cumvalues,cumcpdata,ExcelTemplate, - summarydata, get_file_type, + summarydata, get_file_type, ) +from rowingdata.csvparsers import HumonParser + + from rowers.metrics import axes,calc_trimp,rowingmetrics from rowers.models import strokedatafields @@ -1160,6 +1163,7 @@ parsers = { 'ergstick': ErgStickParser, 'fit': FITParser, 'ergdata': ErgDataParser, + 'humon': HumonParser, } def parsenonpainsled(fileformat,f2,summary): diff --git a/rowers/templates/list_workouts.html b/rowers/templates/list_workouts.html index 73c5e5f3..7a42b7c2 100644 --- a/rowers/templates/list_workouts.html +++ b/rowers/templates/list_workouts.html @@ -221,9 +221,32 @@ {% endif %} - Flex - Delete + + + + + + + + + + + + + + + + + + diff --git a/rowers/templates/paidplans.html b/rowers/templates/paidplans.html index 3ad4c821..280ff45d 100644 --- a/rowers/templates/paidplans.html +++ b/rowers/templates/paidplans.html @@ -199,6 +199,10 @@ + {% elif rower and rower.rowerplan == 'pro' and rower.plantrialexpires|date_dif == 1 %} + {% else %}   {% endif %} diff --git a/rowers/templates/registeremail.html b/rowers/templates/registeremail.html index c516f592..20c81907 100644 --- a/rowers/templates/registeremail.html +++ b/rowers/templates/registeremail.html @@ -29,10 +29,6 @@ http://analytics.rowsandall.com/2017/11/02/rowsandall-settings-page-tutorial/.

-

- Also check out our instructional videos at - http://rowsandall.com/rowers/videos -

This website is a labor of love "by rowers, for rowers". diff --git a/rowers/tests/c2jsonintervaldata.txt b/rowers/tests/c2jsonintervaldata.txt new file mode 100644 index 00000000..3c37d306 --- /dev/null +++ b/rowers/tests/c2jsonintervaldata.txt @@ -0,0 +1,64 @@ +{ + "date": "2017-05-01 14:33:00", + "timezone": "Australia/Melbourne", + "workout_type": "VariableInterval", + "type": "rower", + "weight_class": "H", + "time": 16800, + "distance": 6721, + "rest_distance": 236, + "rest_time": 2700, + "calories_total": 427, + "drag_factor": 175, + "stroke_count": 996, + "stroke_rate": 33, + "workout": { + "intervals": [ + { + "type": "time", + "time": 2400, + "distance": 1011, + "rest_time": 600, + "rest_distance": 43, + "stroke_rate": 35, + "calories_total": 68 + }, + { + "type": "time", + "time": 3000, + "distance": 1229, + "rest_time": 600, + "rest_distance": 59, + "stroke_rate": 34, + "calories_total": 80 + }, + { + "type": "time", + "time": 3000, + "distance": 1190, + "rest_time": 600, + "rest_distance": 59, + "stroke_rate": 33, + "calories_total": 75 + }, + { + "type": "time", + "time": 2400, + "distance": 971, + "rest_time": 750, + "rest_distance": 44, + "stroke_rate": 34, + "calories_total": 62 + }, + { + "type": "time", + "time": 6000, + "distance": 2320, + "rest_time": 150, + "rest_distance": 31, + "stroke_rate": 32, + "calories_total": 142 + } + ] + } +} diff --git a/rowers/tests/c2jsonsplitdata.txt b/rowers/tests/c2jsonsplitdata.txt new file mode 100644 index 00000000..ff6f9815 --- /dev/null +++ b/rowers/tests/c2jsonsplitdata.txt @@ -0,0 +1,47 @@ +{ + "date": "2017-05-16 17:24:00", + "timezone": "US/Pacific", + "workout_type": "FixedTimeSplits", + "type": "rower", + "weight_class": "H", + "time": 6000, + "distance": 1789, + "stroke_count": 314, + "drag_factor": 134, + "stroke_rate": 31, + "calories_total": 90, + "workout": { + "splits": [ + { + "time": 1200, + "calories_total": 18, + "stroke_rate": 33, + "distance": 354 + }, + { + "time": 1200, + "calories_total": 18, + "stroke_rate": 31, + "distance": 355 + }, + { + "time": 1200, + "calories_total": 18, + "stroke_rate": 32, + "distance": 357 + }, + { + "time": 1200, + "calories_total": 18, + "stroke_rate": 31, + "distance": 363 + }, + { + "time": 1200, + "calories_total": 18, + "stroke_rate": 30, + "distance": 361 + } + ] + } +} diff --git a/rowers/tests/test_plans.py b/rowers/tests/test_plans.py index de2fce3d..1a23bd23 100644 --- a/rowers/tests/test_plans.py +++ b/rowers/tests/test_plans.py @@ -1,3 +1,319 @@ #from __future__ import print_function from statements import * nu = datetime.datetime.now() + +import rowers.plannedsessions as plannedsessions + +class TrainingPlanTest(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() + + def tearDown(self): + for workout in self.user_workouts: + try: + os.remove(workout.csvfilename) + except (IOError, WindowsError): + pass + + def test_createplan(self): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + url = '/rowers/createplan/' + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + # add target + + targetdate = (nu+datetime.timedelta(days=300)) + + form_data = { + 'name': faker.word(), + 'date': targetdate.strftime("%Y-%m-%d"), + 'notes': faker.text(), + } + + targetform = TrainingTargetForm(form_data,user=self.u) + self.assertTrue(targetform.is_valid()) + + response = self.c.post(url,form_data) + self.assertEqual(response.status_code,200) + + form_data = { + 'name': faker.word(), + 'target': '1', + 'startdate': nu.strftime("%Y-%m-%d"), + 'enddate': targetdate.strftime("%Y-%m-%d"), + 'active': True, + } + + newplanform = TrainingPlanForm(form_data,user=self.u) + self.assertTrue(newplanform.is_valid()) + + response = self.c.post(url,form_data) + self.assertEqual(response.status_code,200) + + urlplan = '/rowers/plan/1/' + response = self.c.get(urlplan) + self.assertEqual(response.status_code,200) + + html = BeautifulSoup(response.content,'html.parser') + urls = [a['href'] for a in html.find_all('a')] + + for url in urls: + if 'macrocycle' in url and 'delete' not in url: + macrourl = url + print macrourl + response = self.c.get(macrourl) + self.assertEqual(response.status_code,200) + + form_data = { + 'name':faker.word(), + 'startdate':nu.strftime("%Y-%m-%d"), + 'enddate':targetdate.strftime("%Y-%m-%d"), + 'notes':faker.text(), + } + + macroform = TrainingMacroCycleForm(form_data) + self.assertTrue(macroform.is_valid()) + + response = self.c.post(macrourl,form_data,follow=True) + self.assertEqual(response.status_code,200) + self.assertRedirects(response, + expected_url='/rowers/plan/1/macro/3/', + status_code=302,target_status_code=200) + + response = self.c.get(urlplan) + self.assertEqual(response.status_code,200) + + html = BeautifulSoup(response.content,'html.parser') + urls = [a['href'] for a in html.find_all('a')] + + for url in urls: + if 'planbymonths' in url: + print url + response = self.c.get(url,follow=True) + self.assertEqual(response.status_code,200) + + response = self.c.get('/rowers/plan/1/macro/3/') + self.assertEqual(response.status_code,200) + + html = BeautifulSoup(response.content,'html.parser') + urls = [a['href'] for a in html.find_all('a')] + + tested = False + + for url in urls: + if 'planbyweeks' in url and not tested: + print url + response = self.c.get(url,follow=True) + self.assertEqual(response.status_code,200) + tested = True + + # add test for creating new sessions + def sessions_create(self): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + url = '/rowers/sessions/create/' + + startdate = nu.date() + enddate = (nu+datetime.timedelta(days=3)).date() + preferreddate = startdate + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + post_data = { + 'comment': faker.text(), + 'criterium': 'none', + 'enddate': enddate.strftime("%Y-%m-%d"), + 'preferreddate': preferreddate.strftime("%Y-%m-%d"), + 'startdate': startdate.strftime("%Y-%m-%d"), + 'sessionmode':'time', + 'sessiontype':'session', + 'sessionunit':'min', + 'sessionvalue': '60', + 'name': faker.word(), + } + + form = PlannedSessionForm(post_data) + self.assertEqual(form.is_valid()) + + response = self.c.post(url,post_data) + self.assertEqual(response.status_code,200) + + + +class SessionLinkTest(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() + + for w in self.user_workouts: + startdatetime = w.startdatetime + + startdate = (startdatetime-datetime.timedelta(days=1)).date() + enddate = (startdatetime+datetime.timedelta(days=1)).date() + preferreddate = startdatetime.date() + + ps = SessionFactory(startdate=startdate,enddate=enddate, + sessiontype='session', + sessionmode = 'time', + criterium = 'none', + sessionvalue = 60, + sessionunit='min', + preferreddate=preferreddate, + manager=self.u, + ) + + + ps.save() + result = plannedsessions.add_rower_session(self.r,ps) + + + def tearDown(self): + for workout in self.user_workouts: + try: + os.remove(workout.csvfilename) + except (IOError, WindowsError): + pass + + def test_plannedsessions(self): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + + w = self.user_workouts[0] + startdatetime = w.startdatetime + + startdate = (startdatetime-datetime.timedelta(days=1)).date() + enddate = (startdatetime+datetime.timedelta(days=1)).date() + + url = '/rowers/sessions/' + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + post_data = { + 'startdate':startdate.strftime("%Y-%m-%d"), + 'enddate': enddate.strftime("%Y-%m-%d"), + } + + response = self.c.post(url,post_data) + self.assertEqual(response.status_code,200) + + url2 = '/rowers/sessions/manage/' + response = self.c.post(url2,post_data) + self.assertEqual(response.status_code,200) + + pss = PlannedSession.objects.filter(startdate=startdate,enddate=enddate) + + self.assertTrue(len(pss)>0) + + ps = pss[0] + + post_data = { + 'plannedsession': str(ps.id), + 'workouts':[str(w.id)], + } + + plannedsessionstuple = [] + for ps in pss: + sessiontpl = (ps.id,ps.__unicode__()) + plannedsessionstuple.append(sessiontpl) + + plannedsessionstuple = tuple(plannedsessionstuple) + + + workoutdata = {} + workoutdata['initial'] = [] + + choices = [] + for w in self.user_workouts: + wtpl = (w.id,w.__unicode__()) + choices.append(wtpl) + + workoutdata['choices'] = tuple(choices) + + form = PlannedSessionSelectForm(plannedsessionstuple,post_data) + + self.assertTrue(form.is_valid()) + + form = WorkoutSessionSelectForm(workoutdata,post_data) + self.assertTrue(form.is_valid()) + + response = self.c.post(url2,post_data) + self.assertEqual(response.status_code,200) + + + urlsession = '/rowers/sessions/{id}/'.format(id=ps.id) + + response = self.c.get(urlsession) + self.assertEqual(response.status_code,200) + + url = '/rowers/sessions/coach/' + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + def test_multiplesessions(self): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + pss = PlannedSession.objects.all() + + earliestdate = min([ps.startdate for ps in pss]) + latestdate = max([ps.enddate for ps in pss]) + + url = '/rowers/sessions/multicreate/?when={be}/{en}'.format( + be = earliestdate.strftime("%Y-%m-%d"), + en = latestdate.strftime("%Y-%m-%d"), + ) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + url = '/rowers/sessions/multicreate/user/{userid}/extra/1/?when={be}/{en}'.format( + be = earliestdate.strftime("%Y-%m-%d"), + en = latestdate.strftime("%Y-%m-%d"), + userid = self.u.id + ) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + url = '/rowers/sessions/multiclone/user/{userid}/?when={be}/{en}'.format( + be = earliestdate.strftime("%Y-%m-%d"), + en = latestdate.strftime("%Y-%m-%d"), + userid = self.u.id + ) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + + diff --git a/rowers/tests/test_rowerplans.py b/rowers/tests/test_rowerplans.py new file mode 100644 index 00000000..e9be5c6d --- /dev/null +++ b/rowers/tests/test_rowerplans.py @@ -0,0 +1,64 @@ +from statements import * +nu = datetime.datetime.now() + +from rowers.views import hasplannedsessions,iscoachmember,ispromember + +class TrialsTest(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='basic') + + 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() + + def tearDown(self): + for workout in self.user_workouts: + try: + os.remove(workout.csvfilename) + except (IOError, WindowsError): + pass + + def test_basictrial(self): + self.assertEqual(hasplannedsessions(self.u),False) + self.assertEqual(iscoachmember(self.u),False) + self.assertEqual(ispromember(self.u),False) + + self.r.protrialexpires = (nu+datetime.timedelta(days=10)).date() + self.r.save() + + self.assertEqual(hasplannedsessions(self.u),False) + self.assertEqual(iscoachmember(self.u),False) + self.assertEqual(ispromember(self.u),True) + + self.r.plantrialexpires = (nu+datetime.timedelta(days=10)).date() + self.r.save() + + self.assertEqual(hasplannedsessions(self.u),True) + self.assertEqual(iscoachmember(self.u),False) + self.assertEqual(ispromember(self.u),True) + + def test_protrial(self): + self.r.rowerplan = 'pro' + self.r.save() + self.assertEqual(hasplannedsessions(self.u),False) + self.assertEqual(iscoachmember(self.u),False) + self.assertEqual(ispromember(self.u),True) + + self.r.plantrialexpires = (nu+datetime.timedelta(days=10)).date() + self.r.save() + + self.assertEqual(hasplannedsessions(self.u),True) + self.assertEqual(iscoachmember(self.u),False) + self.assertEqual(ispromember(self.u),True) + + + diff --git a/rowers/tests/testdata/testdata.tcx b/rowers/tests/testdata/testdata.tcx index 28c4ef15..144ed946 100644 --- a/rowers/tests/testdata/testdata.tcx +++ b/rowers/tests/testdata/testdata.tcx @@ -2502,7 +2502,11 @@ +<<<<<<< HEAD <Element 'Notes' at 0x157a2e48> +======= + <Element 'Notes' at 0x1d49a6d8> +>>>>>>> develop diff --git a/rowers/utils.py b/rowers/utils.py index eb5627b3..9e42917f 100644 --- a/rowers/utils.py +++ b/rowers/utils.py @@ -391,8 +391,6 @@ def isprorower(r): result = r.rowerplan in ['pro','coach','plan'] if not result and r.protrialexpires: result = r.rowerplan == 'basic' and r.protrialexpires >= datetime.date.today() - print r.rowerplan,r.protrialexpires,datetime.date.today(),result - print r.protrialexpires >= datetime.date.today() return result diff --git a/rowers/views.py b/rowers/views.py index 889d9fdd..8d6eb60b 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -1030,7 +1030,7 @@ def hasplannedsessions(user): result = user.is_authenticated() and (r.rowerplan=='coach' or r.rowerplan=='plan') if not result and r.plantrialexpires: - result = user.is_authenticated() and r.rowerplan=='basic' and r.plantrialexpires >= datetime.date.today() + result = user.is_authenticated() and r.plantrialexpires >= datetime.date.today() else: result = False @@ -11451,8 +11451,10 @@ def workout_getimportview(request,externalid,source = 'c2'): if 'workout' in data: if 'splits' in data['workout']: splitdata = data['workout']['splits'] - if 'intervals' in data['workout']: + elif 'intervals' in data['workout']: splitdata = data['workout']['intervals'] + else: + splitdata = False else: splitdata = False