From 0a18100b89315e5009537f112e404dc0f2e3fca1 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 1 May 2018 08:34:47 +0200 Subject: [PATCH 1/6] better physics recipe --- rowers/templates/physics.html | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/rowers/templates/physics.html b/rowers/templates/physics.html index 4ed2bd7f..efc5757d 100644 --- a/rowers/templates/physics.html +++ b/rowers/templates/physics.html @@ -107,16 +107,27 @@ GPS. For now, export the data to Strava and then import them here.
  1. Click on the workout. This will bring you to the workout Edit view
  2. Click on the "Advanced" button
  3. -
  4. Click on Edit Wind Data (optional) to edit wind data. Go back to Advanced when you're done.
  5. -
  6. Click on Edit Stream Data (optional) to edit stream data. Go back to Advanced when you're done.
  7. +
  8. Click on "Geeky Stuff"
  9. +
  10. Look for three buttons labelled "Edit Wind Data", "Edit Stream + Data" and "OTW Power"
  11. +
  12. If you have wind or stream data, click on the appropriate + button and enter your data.
  13. +
  14. If you have both wind and stream, click the shortcut button + on the respective page to take you to the other parameter
  15. Click on OTW Power
  16. -
  17. Select the boat type and enter the average weight per crew member. - Do not use the crew total weight.
  18. +
  19. Select the boat type and enter the average weight + per crew member. + Do not use the crew total weight.
  20. Click "Update & Run"
  21. Go do something else. You will receive an email when the calculations are finished. The calculation itself will take about 10 minutes for an hour long row, but there may be other people's calculations in the queue, so -it may take longer.
  22. -
  23. From the "Advanced" view, click on "Corrected Pace Plot" to see the result. From here, you can re-run the calculation with different parameters.
  24. + it may take longer. +
  25. Progress can be monitored by clicking on "here" in the message + at the top of the page advising that the calculation has + begun.
  26. +
  27. + When the calculation is complete, go back to the "Geeky Stuff" page + and click on "Corrected Pace Plot" to see the result. From here, you can re-run the calculation with different parameters.

@@ -152,4 +163,4 @@ well validated. For that, I need to collect data, so keep the workouts coming! - {% endblock content %} \ No newline at end of file + {% endblock content %} From 39ff62e560831e78e59ba25a86a7924726614ea8 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Tue, 1 May 2018 10:04:53 +0200 Subject: [PATCH 2/6] now uses NK summary for NK files --- rowers/dataprep.py | 2 +- rowers/dataprepnodjango.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/rowers/dataprep.py b/rowers/dataprep.py index 61116cea..3a1debc8 100644 --- a/rowers/dataprep.py +++ b/rowers/dataprep.py @@ -1308,7 +1308,7 @@ def new_workout_from_file(r, f2, message = 'Something went wrong: ' + errorstring return (0, message, '') - dosummary = (fileformat != 'fit') + dosummary = (fileformat != 'fit' and fileformat != 'speedcoach2') id, message = save_workout_database( f2, r, workouttype=workouttype, diff --git a/rowers/dataprepnodjango.py b/rowers/dataprepnodjango.py index 49a3e6dc..11c8e1e1 100644 --- a/rowers/dataprepnodjango.py +++ b/rowers/dataprepnodjango.py @@ -623,7 +623,8 @@ def new_workout_from_file(r,f2, - dosummary = (fileformat != 'fit') + dosummary = (fileformat != 'fit' and fileformat != 'speedcoach2') + id,message = save_workout_database(f2,r, workouttype=workouttype, makeprivate=makeprivate, From 379626f802a8ca2173f77bacd81e925b7a5c1b54 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 2 May 2018 09:41:59 +0200 Subject: [PATCH 3/6] correctly recognizes Empower version on new files --- rowers/dataprep.py | 36 +++++++++++++++++++++++------------- rowers/models.py | 2 +- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/rowers/dataprep.py b/rowers/dataprep.py index 3a1debc8..237edf1e 100644 --- a/rowers/dataprep.py +++ b/rowers/dataprep.py @@ -10,7 +10,9 @@ from rowingdata import rower as rrower from shutil import copyfile -from rowingdata import get_file_type, get_empower_rigging +from rowingdata import ( + get_file_type, get_empower_rigging,get_empower_firmware + ) from rowers.tasks import handle_sendemail_unrecognized from rowers.tasks import handle_zip_file @@ -1153,11 +1155,15 @@ def parsenonpainsled(fileformat,f2,summary): if (fileformat == 'speedcoach2'): row = SpeedCoach2Parser(f2) hasrecognized = True - try: - oarlength, inboard = get_empower_rigging(f2) - summary = row.allstats() - except: - pass + + oarlength, inboard = get_empower_rigging(f2) + empowerfirmware = get_empower_firmware(f2) + if empowerfirmware != '': + fileformat = fileformat+'v'+str(empowerfirmware) + else: + fileformat = 'speedcoach2v0' + summary = row.allstats() + # handle ErgStick if (fileformat == 'ergstick'): @@ -1176,7 +1182,7 @@ def parsenonpainsled(fileformat,f2,summary): pass hasrecognized = True - return row,hasrecognized,summary + return row,hasrecognized,summary,fileformat def handle_nonpainsled(f2, fileformat, summary=''): oarlength = 2.89 @@ -1184,7 +1190,7 @@ def handle_nonpainsled(f2, fileformat, summary=''): hasrecognized = False try: - row,hasrecognized,summary = parsenonpainsled(fileformat,f2,summary) + row,hasrecognized,summary,fileformat = parsenonpainsled(fileformat,f2,summary) except: pass @@ -1211,7 +1217,7 @@ def handle_nonpainsled(f2, fileformat, summary=''): except: os.remove(f_to_be_deleted + '.gz') - return (f2, summary, oarlength, inboard) + return (f2, summary, oarlength, inboard, fileformat) # Create new workout from file and store it in the database # This routine should be used everywhere in views.py and mailprocessing.py @@ -1297,9 +1303,11 @@ def new_workout_from_file(r, f2, # handle non-Painsled by converting it to painsled compatible CSV if (fileformat != 'csv'): try: - f2, summary, oarlength, inboard = handle_nonpainsled(f2, - fileformat, - summary=summary) + f2, summary, oarlength, inboard, fileformat = handle_nonpainsled( + f2, + fileformat, + summary=summary + ) if not f2: message = 'Something went wrong' return (0, message, '') @@ -1308,7 +1316,9 @@ def new_workout_from_file(r, f2, message = 'Something went wrong: ' + errorstring return (0, message, '') - dosummary = (fileformat != 'fit' and fileformat != 'speedcoach2') + dosummary = (fileformat != 'fit' and 'speedcoach2' not in fileformat) + + id, message = save_workout_database( f2, r, workouttype=workouttype, diff --git a/rowers/models.py b/rowers/models.py index b8c70042..75afa570 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -1221,7 +1221,7 @@ class Workout(models.Model): name = models.CharField(max_length=150) date = models.DateField() workouttype = models.CharField(choices=workouttypes,max_length=50) - workoutsource = models.CharField(choices=workoutsources,max_length=100, + workoutsource = models.CharField(max_length=100, default='unknown') boattype = models.CharField(choices=boattypes,max_length=50, default='1x', From 592077419716eb9a27fab4bdad0b047a1ded1f23 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 2 May 2018 14:20:20 +0200 Subject: [PATCH 4/6] empower fix done --- rowers/dataprepnodjango.py | 49 ++++++++++++ rowers/tasks.py | 85 +++++++++++++++++++- rowers/templates/empower_fix.html | 125 ++++++++++++++++++++++++++++++ rowers/templates/multiflex.html | 2 +- rowers/urls.py | 1 + rowers/views.py | 105 +++++++++++++++++++++++++ 6 files changed, 365 insertions(+), 2 deletions(-) create mode 100644 rowers/templates/empower_fix.html diff --git a/rowers/dataprepnodjango.py b/rowers/dataprepnodjango.py index 11c8e1e1..f17cbb8b 100644 --- a/rowers/dataprepnodjango.py +++ b/rowers/dataprepnodjango.py @@ -4,6 +4,7 @@ from rowingdata import rowingdata as rrdata from rowingdata import make_cumvalues from rowingdata import rower as rrower from rowingdata import main as rmain +from rowingdata import empower_bug_correction,get_empower_rigging from time import strftime from pandas import DataFrame,Series @@ -659,7 +660,55 @@ def update_strokedata(id,df,debug=False): debug=debug) return rowdata + +def update_empower(id, inboard, oarlength, boattype, df, f1, debug=False): + + corr_factor = 1.0 + if 'x' in boattype: + # sweep + a = 0.06 + b = 0.275 + else: + # scull + a = 0.15 + b = 0.275 + + corr_factor = empower_bug_correction(oarlength,inboard,a,b) + + success = False + + print df[' Power (watts)'].mean() + try: + df['power empower old'] = df[' Power (watts)'] + df[' Power (watts)'] = df[' Power (watts)'] * corr_factor + df['driveenergy empower old'] = df['driveenergy'] + df['driveenergy'] = df['driveenergy'] * corr_factor + success = True + except KeyError: + pass + + if success: + delete_strokedata(id,debug=debug) + if debug: + print "updated ",id + print "correction ",corr_factor + else: + if debug: + print "not updated ",id + + print df[' Power (watts)'].mean() + + + rowdata = dataprep(df,id=id,bands=True,barchart=True,otwpower=True, + debug=debug) + + row = rrdata(df=df) + row.write_csv(f1,gzip=True) + + return success + + def testdata(time,distance,pace,spm): t1 = np.issubdtype(time,np.number) t2 = np.issubdtype(distance,np.number) diff --git a/rowers/tasks.py b/rowers/tasks.py index 08f48c49..96c400a2 100644 --- a/rowers/tasks.py +++ b/rowers/tasks.py @@ -41,7 +41,7 @@ from rowers.dataprepnodjango import ( getsmallrowdata_db, updatecpdata_sql, update_agegroup_db,fitnessmetric_to_sql, add_c2_stroke_data_db,totaltime_sec_to_string, - create_c2_stroke_data_db + create_c2_stroke_data_db,update_empower ) @@ -265,6 +265,89 @@ def handle_new_workout_from_file(r, f2, # process and update workouts +@app.task(bind=True) +def handle_update_empower(self, + useremail, + workoutdicts, + debug=False, **kwargs): + + job = self.request + job_id = job.id + + if 'jobkey' in kwargs: + job_id = kwargs.pop('jobkey') + + aantal = len(workoutdicts) + counter = 0 + + for workoutdict in workoutdicts: + wid = workoutdict['id'] + inboard = workoutdict['inboard'] + oarlength = workoutdict['oarlength'] + boattype = workoutdict['boattype'] + f1 = workoutdict['filename'] + + print wid + + # oarlength consistency checks will be done in view + + havedata = 1 + try: + rowdata = rdata(f1) + except IOError: + try: + rowdata = rdata(f1 + '.csv') + except IOError: + try: + rowdata = rdata(f1 + '.gz') + except IOError: + havedata = 0 + + progressurl = SITE_URL + siteurl = SITE_URL + if debug: + progressurl = SITE_URL_DEV + siteurl = SITE_URL_DEV + secret = PROGRESS_CACHE_SECRET + + kwargs['job_id'] = job_id + + progressurl += "/rowers/record-progress/" + progressurl += job_id + + if havedata: + success = update_empower(wid, inboard, oarlength, boattype, + rowdata.df, f1, debug=debug) + + counter += 1 + + progress = 100.*float(counter)/float(aantal) + + post_data = { + "secret":secret, + "value":progress, + } + + s = requests.post(progressurl, data=post_data) + status_code = s.status_code + + subject = "Rowsandall.com Your Old Empower Oarlock data have been corrected" + message = """ +We have updated Power and Work per Stroke data according to the instructions by Nielsen-Kellerman. +""" + + email = EmailMessage(subject, message, + 'Rowsandall ', + [useremail]) + + if 'emailbounced' in kwargs: + emailbounced = kwargs['emailbounced'] + else: + emailbounced = False + + if not emailbounced: + res = email.send() + return 1 @app.task def handle_updatedps(useremail, workoutids, debug=False,**kwargs): diff --git a/rowers/templates/empower_fix.html b/rowers/templates/empower_fix.html new file mode 100644 index 00000000..c817d02e --- /dev/null +++ b/rowers/templates/empower_fix.html @@ -0,0 +1,125 @@ +{% extends "base.html" %} +{% load staticfiles %} +{% load rowerfilters %} + +{% block title %}Workouts{% endblock %} + +{% block content %} + + + + + + +
+ {% include "teambuttons.html" with teamid=team.id team=team %} +
+
+

Empower Workouts

+
+
+
+ +
+
+ + {{ dateform.as_table }} +
+ {% csrf_token %} +
+
+ +
+
+
+ +
+ + +
+
+ + + {% if workouts %} + + Toggle All
+ + + {{ form.as_table }} +
+ +{% else %} +

No workouts found

+{% endif %} +
+
+

Select workouts on the left, + and press submit

+
+

+ {% csrf_token %} + +

+
+
+

You can use the date form above to reduce the selection

+
+
+
+ + +{% endblock %} diff --git a/rowers/templates/multiflex.html b/rowers/templates/multiflex.html index 375602ea..2ab83e20 100644 --- a/rowers/templates/multiflex.html +++ b/rowers/templates/multiflex.html @@ -37,7 +37,7 @@
-

Multi Flex Chart

+

Trend Flex Chart

{{ the_div|safe }} diff --git a/rowers/urls.py b/rowers/urls.py index d37bf26b..b207fa47 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -122,6 +122,7 @@ urlpatterns = [ url(r'^403/$', TemplateView.as_view(template_name='403.html'),name='403'), url(r'^imports/$', TemplateView.as_view(template_name='imports.html'), name='imports'), url(r'^exportallworkouts/?$',views.workouts_summaries_email_view), + url(r'^update_empower$',views.rower_update_empower_view), url(r'^agegroupcp/(?P\d+)$',views.agegroupcpview), url(r'^agegroupcp/(?P\d+)/(?P\d+)$',views.agegroupcpview), url(r'^ajax_agegroup/(?P\d+)/(?P\w+.*)/(?P\w+.*)/(?P\d+)$', diff --git a/rowers/views.py b/rowers/views.py index ccb2b10c..9607f740 100644 --- a/rowers/views.py +++ b/rowers/views.py @@ -128,6 +128,7 @@ from rowers.tasks import ( handle_updatecp,long_test_task,long_test_task2, handle_zip_file,handle_getagegrouprecords, handle_updatefitnessmetric, + handle_update_empower, handle_sendemail_userdeleted, ) @@ -369,6 +370,7 @@ verbose_job_status = { 'make_plot': 'Create static chart', 'long_test_task': 'Long Test Task', 'long_test_task2': 'Long Test Task 2', + 'update_empower': 'Correct Empower Inflacted Power Bug', } def get_job_status(jobid): @@ -11812,6 +11814,109 @@ def rower_calcdps_view(request): url = reverse(workouts_view) return HttpResponseRedirect(url) +@login_required() +def rower_update_empower_view( + request, + startdate=timezone.now()-datetime.timedelta(days=365), + enddate=timezone.now() +): + try: + r = getrower(request.user) + except Rower.DoesNotExist: + raise Http404("Rower doesn't exist") + + if 'startdate' in request.session: + startdate = iso8601.parse_date(request.session['startdate']) + + + if 'enddate' in request.session: + enddate = iso8601.parse_date(request.session['enddate']) + + if request.method == 'POST' and 'daterange' in request.POST: + dateform = DateRangeForm(request.POST) + if dateform.is_valid(): + startdate = dateform.cleaned_data['startdate'] + enddate = dateform.cleaned_data['enddate'] + startdatestring = startdate.strftime('%Y-%m-%d') + enddatestring = enddate.strftime('%Y-%m-%d') + request.session['startdate'] = startdatestring + request.session['enddate'] = enddatestring + else: + dateform = DateRangeForm(initial={ + 'startdate':startdate, + 'enddate':enddate, + }) + + + if request.method == 'POST' and 'workouts' in request.POST: + form = WorkoutMultipleCompareForm(request.POST) + if form.is_valid(): + cd = form.cleaned_data + workouts = cd['workouts'] + workoutdicts = [] + + for w in workouts: + if 'x' in w.boattype and w.oarlength is not None and w.oarlength > 3.30: + message = "Oarlength and boat type mismatch for workout "+str(w.id)+". Skipping workout" + messages.error(request,message) + elif 'x' not in w.boattype and w.oarlength is not None and w.oarlength <= 3.30: + message = "Oarlength and boat type mismatch for workout "+str(w.id)+". Skipping workout" + messages.error(request,message) + elif w.oarlength is None: + message = "Incorrect oarlength in workout "+str(w.id)+". Skipping workout" + messages.error(request,message) + else: + + + workoutdict = { + 'id':w.id, + 'boattype':w.boattype, + 'filename':w.csvfilename, + 'inboard':w.inboard, + 'oarlength':w.oarlength + } + + workoutdicts.append(workoutdict) + + w.workoutsource = 'speedcoach2corrected' + w.save() + + + job = myqueue(queuelow,handle_update_empower, + request.user.email,workoutdicts, + emailbounced=r.emailbounced) + + try: + request.session['async_tasks'] += [(job.id,'update_empower')] + except KeyError: + request.session['async_tasks'] = [(job.id,'update_empower')] + + successmessage = 'Your workouts are being updated in the background. You will receive email when this is done. You can check the status of your calculations here' + + messages.info(request,successmessage) + + url = reverse(workouts_view) + return HttpResponseRedirect(url) + else: + + workouts = Workout.objects.filter( + startdatetime__gte=startdate, + startdatetime__lte=enddate, + workoutsource='speedcoach2', + user=r, + ).order_by("-date","-starttime") + + form = WorkoutMultipleCompareForm() + form.fields["workouts"].queryset = workouts + # GET request = prepare form + + return render(request, 'empower_fix.html', + {'workouts':workouts, + 'dateform':dateform, + 'form':form, + 'rower':r + }) + @login_required() def team_leave_view(request,id=0): r = getrower(request.user) From dc480c9e8c68be32b0db9a8332c36a9a86b780c8 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 2 May 2018 18:45:58 +0200 Subject: [PATCH 5/6] explanation on page --- rowers/templates/empower_fix.html | 21 ++++++++++++++++++++- rowers/templates/list_workouts.html | 9 ++++++--- rowers/views.py | 6 +++++- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/rowers/templates/empower_fix.html b/rowers/templates/empower_fix.html index c817d02e..8e059f80 100644 --- a/rowers/templates/empower_fix.html +++ b/rowers/templates/empower_fix.html @@ -69,7 +69,26 @@ {% include "teambuttons.html" with teamid=team.id team=team %}
-

Empower Workouts

+

Empower Workouts

+

This functionality is aimed at users who have uploaded workouts from + the Nielsen-Kellerman Empower Oarlock/SpeedCoach combination before the + power inflation bug was known (May 4, 2018).

+ +

Workouts recorded with a SpeedCoach running NK Firmware version 2.17 or + lower have Power and Work per Stroke values that are approximately + 9% (sculling) or 5% too high. The exact value of the error depends on your + inboard and oar length.

+ +

Currently, we autocorrect workouts recorded with old Firmware upon + their upload, but workouts that were present on the site before + the bug was known still have incorrect values for Power and Work per Stroke. +

+ +

+ You can use this page to correct those workouts. +

+ +
diff --git a/rowers/templates/list_workouts.html b/rowers/templates/list_workouts.html index 93c148e1..a3e07cbb 100644 --- a/rowers/templates/list_workouts.html +++ b/rowers/templates/list_workouts.html @@ -266,21 +266,24 @@
{% if rankingonly and not team %} -
+ {% elif not team %} -
+ {% endif %} -
+
{% if user|is_promember %} Glue Workouts {% else %} Glue {% endif %}
+

 

{% if team %}
3.30: message = "Oarlength and boat type mismatch for workout "+str(w.id)+". Skipping workout" messages.error(request,message) @@ -11884,6 +11887,7 @@ def rower_update_empower_view( job = myqueue(queuelow,handle_update_empower, request.user.email,workoutdicts, + debug=False, emailbounced=r.emailbounced) try: From 3390db05203928a87456f45b5d6a4ff25df6e9f9 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Thu, 3 May 2018 08:24:45 +0200 Subject: [PATCH 6/6] fixing empower bug & NK SC summary --- rowers/dataprep.py | 16 +++++++++------- rowers/mailprocessing.py | 4 ++-- rowers/tests.py | 6 +++--- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/rowers/dataprep.py b/rowers/dataprep.py index 237edf1e..1a7cb73c 100644 --- a/rowers/dataprep.py +++ b/rowers/dataprep.py @@ -1096,11 +1096,12 @@ def parsenonpainsled(fileformat,f2,summary): if (fileformat == 'rp'): row = RowProParser(f2) hasrecognized = True - # handle TCX + + # handle TCX if (fileformat == 'tcx'): row = TCXParser(f2) hasrecognized = True - + # handle Mystery if (fileformat == 'mystery'): row = MysteryParser(f2) @@ -1197,10 +1198,10 @@ def handle_nonpainsled(f2, fileformat, summary=''): # Handle c2log if (fileformat == 'c2log' or fileformat == 'rowprolog'): - return (0,0,0,0) + return (0,0,0,0,0) if not hasrecognized: - return (0,0,0,0) + return (0,0,0,0,0) f_to_be_deleted = f2 # should delete file @@ -1209,7 +1210,7 @@ def handle_nonpainsled(f2, fileformat, summary=''): row2 = rrdata(df = row.df) row2.write_csv(f2, gzip=True) except: - return (0,0,0,0) + return (0,0,0,0,0) # os.remove(f2) try: @@ -1311,9 +1312,10 @@ def new_workout_from_file(r, f2, if not f2: message = 'Something went wrong' return (0, message, '') - except: + except Exception as e: errorstring = str(sys.exc_info()[0]) - message = 'Something went wrong: ' + errorstring + print e.message + message = 'Something went wrong: ' + e.message return (0, message, '') dosummary = (fileformat != 'fit' and 'speedcoach2' not in fileformat) diff --git a/rowers/mailprocessing.py b/rowers/mailprocessing.py index 08c727fc..e01be4c5 100644 --- a/rowers/mailprocessing.py +++ b/rowers/mailprocessing.py @@ -121,7 +121,7 @@ def make_new_workout_from_email(rower, datafile, name, cntr=0,testing=False): # handle non-Painsled if fileformat != 'csv': - filename_mediadir, summary, oarlength, inboard = dataprep.handle_nonpainsled( + filename_mediadir, summary, oarlength, inboard,fileformat = dataprep.handle_nonpainsled( 'media/' + datafilename, fileformat, summary) if not filename_mediadir: return 0 @@ -148,7 +148,7 @@ def make_new_workout_from_email(rower, datafile, name, cntr=0,testing=False): pass row.write_csv(datafilename, gzip=True) - dosummary = (fileformat != 'fit') + dosummary = (fileformat != 'fit' and 'speedcoach2' not in fileformat) if name == '': name = 'Workout from Background Queue' diff --git a/rowers/tests.py b/rowers/tests.py index 8eef0ab1..5e64f2e4 100644 --- a/rowers/tests.py +++ b/rowers/tests.py @@ -846,7 +846,7 @@ class ViewTest(TestCase): f_to_be_deleted = w.csvfilename os.remove(f_to_be_deleted+'.gz') - def test_upload_view_TCX_SpeedCoach2(self): + def test_upload_view_TCX_SpeedCoach2a(self): self.c.login(username='john',password='koeinsloot') filename = 'rowers/testdata/Speedcoach2example.csv' @@ -876,7 +876,7 @@ class ViewTest(TestCase): f_to_be_deleted = w.csvfilename os.remove(f_to_be_deleted+'.gz') - def test_upload_view_TCX_SpeedCoach2(self): + def test_upload_view_TCX_SpeedCoach2b(self): self.c.login(username='john',password='koeinsloot') filename = 'rowers/testdata/Speedcoach2example.csv' @@ -908,7 +908,7 @@ class ViewTest(TestCase): - def test_upload_view_TCX_SpeedCoach2(self): + def test_upload_view_TCX_SpeedCoach2c(self): self.c.login(username='john',password='koeinsloot') filename = 'rowers/testdata/speedcoach3test3.csv'