From bfd7a5b6649baff2373d8000bf2766e5d5a5cc9e Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 12 Apr 2021 19:10:15 +0200 Subject: [PATCH] rdata fixes --- rowers/tests/statements.py | 2 +- rowers/tests/test_races.py | 150 ++++++++++++++++++++++++++++++++++- rowers/views/apiviews.py | 4 +- rowers/views/exportviews.py | 6 +- rowers/views/importviews.py | 2 +- rowers/views/racesviews.py | 4 +- rowers/views/statements.py | 8 +- rowers/views/workoutviews.py | 68 ++++++++-------- 8 files changed, 199 insertions(+), 45 deletions(-) diff --git a/rowers/tests/statements.py b/rowers/tests/statements.py index 1170cae6..fabc164f 100644 --- a/rowers/tests/statements.py +++ b/rowers/tests/statements.py @@ -105,7 +105,7 @@ def get_random_file(filename='rowers/tests/testdata/testdata.csv',name=''): except AttributeError: fromstring = 'none_' - row = rdata(filename) + row = rdata(csvfile=filename) totaldist = row.df['cum_dist'].max() totaltime = row.df['TimeStamp (sec)'].max()-row.df['TimeStamp (sec)'].min() totaltime = totaltime+row.df.loc[row.df.index[0],' ElapsedTime (sec)'] diff --git a/rowers/tests/test_races.py b/rowers/tests/test_races.py index fe78d0d9..876c9d3d 100644 --- a/rowers/tests/test_races.py +++ b/rowers/tests/test_races.py @@ -3,6 +3,8 @@ from __future__ import division from __future__ import print_function from __future__ import unicode_literals +from django.test import RequestFactory + #from __future__ import print_function from .statements import * nu = datetime.datetime.now() @@ -14,8 +16,39 @@ import rowers.courses as courses import rowers.tasks as tasks from rowers.views.racesviews import * from rowers.utils import calculate_age +from rowers.views import addmanual_view -# to do - add test for manual form with image +from django.contrib.messages.storage.fallback import FallbackStorage + +from base64 import b64encode +import base64 + + +from django.core.files.base import File +from io import BytesIO + +from PIL import Image +from io import StringIO + +from django.core.files.uploadedfile import InMemoryUploadedFile +from django.core.files.uploadedfile import SimpleUploadedFile + +def create_image(storage, filename, size=(100, 100), image_mode='RGB', image_format='PNG'): + """ + Generate a test image, returning the filename that it was saved as. + + If ``storage`` is ``None``, the BytesIO containing the image data + will be passed instead. + """ + data = BytesIO() + Image.new(image_mode, size).save(data, image_format) + data.seek(0) + if not storage: + return data + image_file = ContentFile( + b64encode(data.read()) + ) + return storage.save(filename, image_file) @override_settings(TESTING=True) class ChallengesTest(TestCase): @@ -608,6 +641,14 @@ class ChallengesTest(TestCase): @override_settings(TESTING=True) class IndoorChallengesTest(TestCase): + @staticmethod + def get_image_file(name, ext='png', size=(50, 50), color=(256, 0, 0)): + file_obj = BytesIO() + image = Image.new("RGBA", size=size, color=color) + image.save(file_obj, ext) + file_obj.seek(0) + return File(file_obj, name=name) + def setUp(self): self.u = UserFactory() @@ -671,7 +712,7 @@ class IndoorChallengesTest(TestCase): contact_email=contact_email, country = 'Netherlands', manager=self.u, - sessionvalue=result['totaldist'], + sessionvalue=int(result['totaldist']), sessionunit='m', sessionmode='distance', ) @@ -740,6 +781,111 @@ class IndoorChallengesTest(TestCase): response = self.c.post(url) self.assertEqual(response.status_code,200) + @patch('rowers.dataprep.create_engine') + def test_upload_manual(self, mocked_sqlalchemy): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + race = self.IndoorSpeedOrder + + # look at event + url = reverse('virtualevent_view',kwargs={'id':race.id}) + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + if self.r.birthdate: + age = calculate_age(self.r.birthdate) + else: + age = 25 + + # register + url = reverse('indoorvirtualevent_register_view',kwargs={'id':race.id}) + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + + form_data = { + 'teamname': 'ApeTeam', + 'boatclass': 'rower', + 'weightcategory': 'hwt', + 'adaptiveclass': 'None', + 'age': age, + 'mix': False, + 'acceptsocialmedia': True, + } + form = IndoorVirtualRaceResultForm(form_data) + + self.assertTrue(form.is_valid()) + + + response = self.c.post(url,form_data,follow=True) + expected_url = reverse('virtualevent_view',kwargs={'id':race.id}) + self.assertRedirects(response, expected_url=expected_url, + status_code=302,target_status_code=200) + + self.assertEqual(response.status_code, 200) + + # submit result (manual) + + image = self.get_image_file('image.jpg') + + form_data = { + 'name': faker.word(), + 'date': nu.date().strftime("%Y-%m-%d"), + 'starttime': '10:01:43', + 'timezone': 'UTC', + 'duration': '00:02:00.0', + 'distance': race.sessionvalue, + 'workouttype': 'rower', + 'boattype': '1x', + 'weightcategory': 'hwt', + 'adaptiveclass': 'None', + 'notes': faker.text(), + 'rankingpiece': True, + 'duplicate': False, + 'avghr': '160', + 'avgpwr': 0, + 'rpe':4, + 'avgspm': 40, + } + + form = MetricsForm(form_data) + self.assertTrue(form.is_valid()) + + form = WorkoutForm(form_data) + self.assertTrue(form.is_valid()) + + file_data = {'file':image} + + form = ImageForm(form_data,file_data) + + self.assertTrue(form.is_valid()) + + url = '/rowers/workout/addmanual/'+str(race.id)+'/' + response = self.c.get(url) + + self.assertEqual(response.status_code,200) + + + #form_data['file'] = image + request = self.factory.post(url, + data=form_data, + files={'file':image}, + ) + setattr(request, 'session', 'session') + messages = FallbackStorage(request) + setattr(request, '_messages', messages) + + request.user = self.u + + response = addmanual_view(request,raceid=str(race.id)) + + expected = reverse('virtualevent_view',kwargs={'id':race.id}) + + self.assertEqual(response.status_code,302) + self.assertEqual(response.url,expected) + + @patch('rowers.views.racesviews.myqueue') def test_virtualevent_view(self,mocked_myqueue): login = self.c.login(username=self.u.username, password=self.password) diff --git a/rowers/views/apiviews.py b/rowers/views/apiviews.py index 4f532bc3..caf3457d 100644 --- a/rowers/views/apiviews.py +++ b/rowers/views/apiviews.py @@ -323,7 +323,7 @@ def strokedatajson_v2(request,id): hrut1=r.ut1,hrat=r.at, hrtr=r.tr,hran=r.an,ftp=ftp, powerperc=powerperc,powerzones=r.powerzones) - rowdata = rdata(row.csvfilename,rower=rr).df + rowdata = rdata(csvfile=row.csvfilename,rower=rr).df datadf = dataprep.dataprep(rowdata,id=row.id,bands=True,barchart=True,otwpower=True,empower=True) @@ -494,7 +494,7 @@ def strokedatajson(request,id): hrut1=r.ut1,hrat=r.at, hrtr=r.tr,hran=r.an,ftp=ftp, powerperc=powerperc,powerzones=r.powerzones) - rowdata = rdata(row.csvfilename,rower=rr).df + rowdata = rdata(csvfile=row.csvfilename,rower=rr).df datadf = dataprep.dataprep(rowdata,id=row.id,bands=True,barchart=True,otwpower=True,empower=True) # mangling diff --git a/rowers/views/exportviews.py b/rowers/views/exportviews.py index 1fa75bc4..e38e36cc 100644 --- a/rowers/views/exportviews.py +++ b/rowers/views/exportviews.py @@ -9,7 +9,7 @@ def workout_tcxemail_view(request,id=0): - row = rdata(w.csvfilename) + row = rdata(csvfile=w.csvfilename) code = str(uuid4()) tcxfilename = code+'.tcx' @@ -171,7 +171,7 @@ def workout_gpxemail_view(request,id=0): - row = rdata(w.csvfilename) + row = rdata(csvfile=w.csvfilename) code = str(uuid4()) gpxfilename = code+'.gpx' @@ -235,7 +235,7 @@ def workout_csvemail_view(request,id=0): w = get_workout(id) - rowdata = rdata(w.csvfilename) + rowdata = rdata(csvfile=w.csvfilename) code = str(uuid4()) filename = code+'.csv' diff --git a/rowers/views/importviews.py b/rowers/views/importviews.py index e644e53a..5fc3c2b9 100644 --- a/rowers/views/importviews.py +++ b/rowers/views/importviews.py @@ -2121,7 +2121,7 @@ def workout_getimportview(request,externalid,source = 'c2'): units = getlist(sa,sel='unit') types = getlist(sa,sel='type') - rowdata = rdata(w.csvfilename) + rowdata = rdata(csvfile=w.csvfilename) if rowdata: rowdata.updateintervaldata(values, units,types,results) diff --git a/rowers/views/racesviews.py b/rowers/views/racesviews.py index 9118ffd7..bf6dcf3a 100644 --- a/rowers/views/racesviews.py +++ b/rowers/views/racesviews.py @@ -1022,7 +1022,7 @@ def virtualevent_disqualify_view(request,id=0,recordid=0): script, div = interactive_chart(record.workoutid) f1 = workout.csvfilename - rowdata = rdata(f1) + rowdata = rdata(csvfile=f1) hascoordinates = 1 if rowdata != 0: try: @@ -1166,7 +1166,7 @@ def virtualevent_withdrawresult_view(request,id=0,recordid=0): script, div = interactive_chart(record.workoutid) f1 = workout.csvfilename - rowdata = rdata(f1) + rowdata = rdata(csvfile=f1) hascoordinates = 1 if rowdata != 0: try: diff --git a/rowers/views/statements.py b/rowers/views/statements.py index b323beb1..ba806041 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -1110,7 +1110,7 @@ def rowhascoordinates(row): f1 = row.csvfilename u = row.user.user r = getrower(u) - rowdata = rdata(f1) + rowdata = rdata(csvfile=f1) hascoordinates = 1 if rowdata != 0: try: @@ -1129,9 +1129,11 @@ def rowhascoordinates(row): # Wrapper around the rowingdata call to catch some exceptions # Checks for CSV file, then for gzipped CSV file, and if all fails, returns 0 -def rdata(file,rower=rrower()): +def rdata(csvfile=None,rower=rrower()): + if csvfile is None: + return 0 try: - res = rrdata(csvfile=file,rower=rower) + res = rrdata(csvfile=csvfile,rower=rower) except pd.errors.EmptyDataError: res = 0 except (IOError, IndexError, EOFError,FileNotFoundError): diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 1541b4c4..69311627 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -468,7 +468,7 @@ def otw_use_impeller(request,id=0): w = get_workoutuser(id, request) - row = rdata(w.csvfilename) + row = rdata(csvfile=w.csvfilename) success = row.use_impellerdata() if success: row.write_csv(w.csvfilename) @@ -490,7 +490,7 @@ def otw_use_gps(request,id=0): w = get_workoutuser(id, request) - row = rdata(w.csvfilename) + row = rdata(csvfile=w.csvfilename) success = row.use_gpsdata() if success: row.write_csv(w.csvfilename) @@ -572,10 +572,12 @@ def addmanual_view(request,raceid=0): }, ] + if request.method == 'POST': # Form was submitted form = WorkoutForm(request.POST) metricsform = MetricsForm(request.POST) + if form.is_valid() and metricsform.is_valid(): # Get values from form name = form.cleaned_data['name'] @@ -601,7 +603,7 @@ def addmanual_view(request,raceid=0): avgpwr = metricsform.cleaned_data['avgpwr'] avgspm = metricsform.cleaned_data['avgspm'] - ps = form.cleaned_data.get('plannedsession',None) + #ps = form.cleaned_data.get('plannedsession',None) boattype = form.cleaned_data.get('boattype','1x') privacy = form.cleaned_data.get('privacy','visible') rankingpiece = form.cleaned_data.get('rankingpiece',False) @@ -620,8 +622,6 @@ def addmanual_view(request,raceid=0): pytz.timezone(thetimezone) ) - - id,message = dataprep.create_row_df(r, distance, duration,startdatetime, @@ -642,6 +642,7 @@ def addmanual_view(request,raceid=0): if message: # pragma: no cover messages.error(request,message) + if id: w = Workout.objects.get(id=id) w.rankingpiece = rankingpiece @@ -649,19 +650,23 @@ def addmanual_view(request,raceid=0): w.weightcategory = weightcategory w.adaptiveclass = adaptiveclass w.notes = notes - w.plannedsession = ps + #w.plannedsession = ps w.name = name w.rpe = rpe w.workouttype = workouttype w.boattype = boattype + w.distance = distance + w.duration = duration w.save() - if ps: - add_workouts_plannedsession([w],ps,w.user) + #if ps: + # add_workouts_plannedsession([w],ps,w.user) messages.info(request,'New workout created') + iform = ImageForm(request.POST,request.FILES) - if iform.is_valid(): + + if iform.is_valid(): # this works but cannot get the tests to work f = iform.cleaned_data['file'] if f is not None: @@ -682,7 +687,7 @@ def addmanual_view(request,raceid=0): if raceid != 0: try: race = VirtualRace.objects.get(id=raceid) - except VirtualRace.DoesNotExist: + except VirtualRace.DoesNotExist: # pragma: no cover messages.error(request,"Race does not exist") url = reverse('workout_edit_view', kwargs = {'id':encoder.encode_hex(id)}) @@ -690,11 +695,11 @@ def addmanual_view(request,raceid=0): can_submit = race_can_submit(r,race) or race_can_resubmit(r,race) can_submit = can_submit and race.sessiontype == 'indoorrace' - if not can_submit: + if not can_submit: # pragma: no cover messages.error(request,'You cannot submit a result for this race') if can_submit: records = IndoorVirtualRaceResult.objects.filter(race=race,userid=r.id) - if not records: + if not records: # pragma: no cover messages.error(request,'You have to register for the race first') url = reverse('virtualevent_view',kwargs = {'id':race.id}) return HttpResponseRedirect(url) @@ -704,12 +709,13 @@ def addmanual_view(request,raceid=0): result, comments, errors, jobid = add_workout_indoorrace( [w],race,r,recordid=recordid ) - for c in comments: + for c in comments: # pragma: no cover messages.info(request,c) - for er in errors: + for er in errors: # pragma: no cover messages.error(request,er) if result: + print('mies') otherrecords = IndoorVirtualRaceResult.objects.filter( race = race ).exclude(userid=r.id) @@ -800,7 +806,7 @@ def workout_recalcsummary_view(request,id=0): row = get_workoutuser(id, request) filename = row.csvfilename - rowdata = rdata(filename) + rowdata = rdata(csvfile=filename) if rowdata: row.summary = rowdata.allstats() row.save() @@ -1277,7 +1283,7 @@ def remove_power_view(request,id=0): hrtr=r.tr, hran=r.an, ftp=r.ftp, powerperc=powerperc, powerzones=r.powerzones, hrzones=r.hrzones) - row = rdata(f,rower=rr) + row = rdata(csvfile=f,rower=rr) row.df[' Power (watts)'] = 0 row.write_csv(f) res = dataprep.dataprep(row.df, id=workout.id) @@ -2319,7 +2325,7 @@ def workout_view(request,id=0,raceresult=0,sessionresult=0,nocourseraceresult=0) # get row row = get_workout_by_opaqueid(request,id) f1 = row.csvfilename - rowdata = rdata(f1) + rowdata = rdata(csvfile=f1) summary = row.summary comments = WorkoutComment.objects.filter(workout=row) @@ -2479,7 +2485,7 @@ def workout_undo_smoothenpace_view( r = getrower(request.user) filename = row.csvfilename - row = rdata(filename) + row = rdata(csvfile=filename) if row == 0: # pragma: no cover return HttpResponse("Error: CSV Data File Not Found") @@ -2514,7 +2520,7 @@ def workout_smoothenpace_view(request,id=0,message="",successmessage=""): filename = row.csvfilename - row = rdata(filename) + row = rdata(csvfile=filename) if row == 0: # pragma: no cover return HttpResponse("Error: CSV Data File Not Found") @@ -2575,7 +2581,7 @@ def workout_downloadwind_view(request,id=0, f1 = row.csvfilename # create bearing - rowdata = rdata(f1) + rowdata = rdata(csvfile=f1) if rowdata == 0: # pragma: no cover return HttpResponse("Error: CSV Data File Not Found") @@ -2644,7 +2650,7 @@ def workout_downloadmetar_view(request,id=0, # create bearing - rowdata = rdata(f1) + rowdata = rdata(csvfile=f1) if rowdata == 0: # pragma: no cover return HttpResponse("Error: CSV Data File Not Found") @@ -2732,7 +2738,7 @@ def workout_wind_view(request,id=0,message="",successmessage=""): r = getrower(u) # create bearing - rowdata = rdata(f1) + rowdata = rdata(csvfile=f1) if row == 0: # pragma: no cover return HttpResponse("Error: CSV Data File Not Found") @@ -2848,7 +2854,7 @@ def workout_stream_view(request,id=0,message="",successmessage=""): u = row.user.user r = getrower(u) - rowdata = rdata(f1) + rowdata = rdata(csvfile=f1) if rowdata == 0: # pragma: no cover messages.info(request,"Error: CSV data file not found") url = reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(row.id)}) @@ -2948,7 +2954,7 @@ def workout_otwsetpower_view(request,id=0,message="",successmessage=""): # load row data & create power/wind/bearing columns if not set f1 = w.csvfilename - rowdata = rdata(f1) + rowdata = rdata(csvfile=f1) if rowdata == 0: # pragma: no cover return HttpResponse("Error: CSV Data File Not Found") try: @@ -3716,7 +3722,7 @@ def workout_workflow_view(request,id): if 'panel_map.html' in r.workflowmiddlepanel and rowhascoordinates(row): - rowdata = rdata(row.csvfilename) + rowdata = rdata(csvfile=row.csvfilename) mapscript,mapdiv = leaflet_chart2(rowdata.df[' latitude'], rowdata.df[' longitude'], row.name) @@ -3970,7 +3976,7 @@ def workout_flexchart3_view(request,*args,**kwargs): from rowers.metrics import nometrics - rowdata = rdata(row.csvfilename) + rowdata = rdata(csvfile=row.csvfilename) try: rowdata.set_instroke_metrics() except (AttributeError,TypeError): # pragma: no cover @@ -4495,7 +4501,7 @@ def workout_edit_view(request,id=0,message="",successmessage=""): # change data in csv file datachanged = (dragchanged or timechanged) if datachanged: - r = rdata(row.csvfilename) + r = rdata(csvfile=row.csvfilename) if dragchanged: try: r.change_drag(newdragfactor) @@ -4534,7 +4540,7 @@ def workout_edit_view(request,id=0,message="",successmessage=""): u = row.user.user r = getrower(u) - rowdata = rdata(f1) + rowdata = rdata(csvfile=f1) hascoordinates = 1 if rowdata != 0: @@ -4634,7 +4640,7 @@ def workout_map_view(request,id=0): f1 = w.csvfilename u = w.user.user r = getrower(u) - rowdata = rdata(f1) + rowdata = rdata(csvfile=f1) hascoordinates = 1 if rowdata != 0: try: @@ -5993,7 +5999,7 @@ def workout_summary_restore_view(request,id,message="",successmessage=""): hrtr=r.tr,hran=r.an,ftp=ftp, powerperc=powerperc,powerzones=r.powerzones, hrzones=r.hrzones) - rowdata = rdata(f1,rower=rr) + rowdata = rdata(csvfile=f1,rower=rr) if rowdata == 0: # pragma: no cover raise Http404("Error: CSV Data File Not Found") rowdata.restoreintervaldata() @@ -6251,7 +6257,7 @@ def workout_summary_edit_view(request,id,message="",successmessage="" hrtr=r.tr,hran=r.an,ftp=ftp, powerperc=powerperc,powerzones=r.powerzones, hrzones=r.hrzones) - rowdata = rdata(f1,rower=rr) + rowdata = rdata(csvfile=f1,rower=rr) if rowdata == 0: # pragma: no cover return HttpResponse("Error: CSV Data File Not Found") intervalstats = rowdata.allstats()