diff --git a/rowers/courses.py b/rowers/courses.py index 7fa3d8f9..1c2afa85 100644 --- a/rowers/courses.py +++ b/rowers/courses.py @@ -4,6 +4,7 @@ from rowers.models import ( Rower, Workout, GeoPoint, GeoPolygon, GeoCourse, course_length, course_coord_center, course_coord_maxmin, + course_coord_crewnerd_navigation, polygon_coord_center, PlannedSession, polygon_to_path, coordinate_in_path ) @@ -22,6 +23,7 @@ import time from django.db import IntegrityError import uuid from django.conf import settings +import math import geocoder @@ -120,6 +122,50 @@ xmlns_uris_dict = {'gx': 'http://www.google.com/kml/ext/2.2', '': "http://www.opengis.net/kml/2.2"} +def get_polar_angle(point, reference_point): + """ + Calculate the polar angle of a point with respect to a reference point. + """ + try: + delta_x = point.longitude - reference_point.longitude + delta_y = point.latitude - reference_point.latitude + except AttributeError: + delta_x = point['longitude'] - reference_point.longitude + delta_y = point['latitude'] - reference_point.latitude + + + return math.atan2(delta_y, delta_x) + +class Coordinate: + def __init__(self, latitude, longitude): + self.latitude = latitude + self.longitude = longitude + +def get_coordinates_centerpoint(coordinates): + try: + centroid_latitude = sum(coord.latitude for coord in coordinates) / len(coordinates) + centroid_longitude = sum(coord.longitude for coord in coordinates) / len(coordinates) + centroid = Coordinate(centroid_latitude, centroid_longitude) + except AttributeError: + centroid_latitude = sum(coord['latitude'] for coord in coordinates) / len(coordinates) + centroid_longitude = sum(coord['longitude'] for coord in coordinates) / len(coordinates) + centroid = Coordinate(centroid_latitude, centroid_longitude) + + return centroid + +def sort_coordinates_ccw(coordinates): + """ + Sort coordinates in counterclockwise order. + """ + # Find the reference point (centroid) + centroid = get_coordinates_centerpoint(coordinates) + + # Sort coordinates based on polar angle with respect to the centroid + sorted_coords = sorted(coordinates, key=lambda coord: get_polar_angle(coord, centroid)) + + return sorted_coords + + def crewnerdcourse(doc): courses = [] for course in doc: @@ -162,6 +208,8 @@ def get_polygons(polygonpms): 'latitude': float(coordinates[1]), }) + points = sort_coordinates_ccw(points) + polygons.append({ 'name': name, 'points': points @@ -169,34 +217,45 @@ def get_polygons(polygonpms): return polygons +def crewnerdify(polygons): + polygons[0].name = "Start" + polygons[len(polygons)-1].name = "Finish" -def coursetokml(course): - top = Element('kml') - for prefix, uri in xmlns_uris_dict.items(): - if prefix != '': - top.attrib['xmlns:' + prefix] = uri - else: - top.attrib['xmlns'] = uri + if len(polygons) > 2: + for i in range(1,len(polygons)-1): + polygons[i].name = 'WP{n}'.format(n=polygons[i].order_in_course) - document = SubElement(top, 'Document') - name = SubElement(document, 'name') - name.text = 'Courses.kml' - folder = SubElement(document, 'Folder') - foldername = SubElement(folder, 'name') - foldername.text = 'Courses' - folder2 = SubElement(folder, 'Folder') + return polygons + +def removewhitespace(s): + regels = s.split('\n') + regels = [regel for regel in regels if regel.strip()] + return "\n".join(regels) + +def getcoursefolder(course, document, cn=False): + folder2 = SubElement(document, 'Folder', id="{id}".format(id=course.id)) coursename = SubElement(folder2, 'name') coursename.text = course.name - open = SubElement(folder2, 'open') - open.text = '1' + openst = SubElement(folder2, 'open') + openst.text = '1' + coursedescription = SubElement(folder2, 'description') + if len(removewhitespace(course.notes)): + coursedescription.text = "Rowsandall.com\n" + removewhitespace(course.notes) + else: + coursedescription.text = "Rowsandall.com " + course.name polygons = GeoPolygon.objects.filter( course=course).order_by("order_in_course") + if cn: + polygons = crewnerdify(polygons) + for polygon in polygons: placemark = SubElement(folder2, 'Placemark') polygonname = SubElement(placemark, 'name') polygonname.text = polygon.name + polygonstyle = SubElement(placemark, 'styleUrl') + polygonstyle.text = "#default0" p = SubElement(placemark, 'Polygon') tessellate = SubElement(p, 'tessellate') tessellate.text = '1' @@ -206,6 +265,7 @@ def coursetokml(course): coordinates.text = '' points = GeoPoint.objects.filter( polygon=polygon).order_by("order_in_poly") + points = sort_coordinates_ccw(points) for point in points: coordinates.text += '{lon},{lat},0 '.format( lat=point.latitude, @@ -215,6 +275,87 @@ def coursetokml(course): lat=points[0].latitude, lon=points[0].longitude, ) + return folder2 + +def getstyle(document, id): + style = SubElement(document,'Style', id=id) + iconstyle = SubElement(style, 'IconStyle') + scale = SubElement(iconstyle, 'scale') + scale.text = "1.2" + linestyle = SubElement(style, 'LineStyle') + linestylecolor = SubElement(linestyle, 'color') + linestylecolor.text = "ff00ffff" + polystyle = SubElement(style, 'PolyStyle') + polystylecolor = SubElement(polystyle, 'color') + polystylecolor.text = "ff7fffff" + + return style + +def getstylemap(document, id): + stylemap = SubElement(document, 'StyleMap', id=id) + pair1 = SubElement(stylemap, 'Pair') + pair1key = SubElement(pair1, 'key') + pair1key.text = "normal" + pair1styleurl = SubElement(pair1, 'styleUrl') + pair1styleurl.text = "#default" + pair2 = SubElement(stylemap, 'Pair') + pair2key = SubElement(pair2, 'key') + pair2key.text = "highlight" + pair2styleurl = SubElement(pair2, 'styleUrl') + pair2styleurl.text = "#hl" + + return stylemap + +def coursestokml(courseids, cn=False): + courses = GeoCourse.objects.filter(id__in=courseids) + top = Element('kml') + for prefix, uri in xmlns_uris_dict.items(): + if prefix != '': + top.attrib['xmlns:' + prefix] = uri + else: + top.attrib['xmlns'] = uri + + document = SubElement(top, 'Document') + name = SubElement(document, 'name') + name.text = 'courses' + + opendoc = SubElement(document,'open') + opendoc.text = '1' + + style = getstyle(document, "default") + + stylemap = getstylemap(document, 'default0') + + stylehl = getstyle(document, "hl") + + for course in courses: + coursefolder = getcoursefolder(course, document, cn=cn) + + return prettify(top) + +def coursetokml(course,cn=False): + top = Element('kml') + for prefix, uri in xmlns_uris_dict.items(): + if prefix != '': + top.attrib['xmlns:' + prefix] = uri + else: + top.attrib['xmlns'] = uri + + document = SubElement(top, 'Document') + name = SubElement(document, 'name') + name.text = 'courses' + + opendoc = SubElement(document,'open') + opendoc.text = '1' + + style = getstyle(document, "default") + + stylemap = getstylemap(document, 'default0') + + stylehl = getstyle(document, "hl") + + coursefolder = getcoursefolder(course, document, cn=cn) + return prettify(top) diff --git a/rowers/interactiveplots.py b/rowers/interactiveplots.py index 1264b7af..5bc84a34 100644 --- a/rowers/interactiveplots.py +++ b/rowers/interactiveplots.py @@ -33,7 +33,7 @@ import datetime from rowers import mytypes from rowers.courses import ( course_coord_center, course_coord_maxmin, - polygon_coord_center + polygon_coord_center, course_coord_crewnerd_navigation, ) from django.conf import settings from collections import OrderedDict @@ -2165,6 +2165,8 @@ def interactive_histoall(theworkouts, histoparam, includereststrokes, def course_map(course): latmean, lonmean, coordinates = course_coord_center(course) + if course.with_cn_nav_waypoints: + latmean, lonmean, coordinates = course_coord_crewnerd_navigation(course) lat_min, lat_max, long_min, long_max = course_coord_maxmin(course) coordinates = course_spline(coordinates) diff --git a/rowers/models.py b/rowers/models.py index 6d0adf97..a05e0be3 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -8,7 +8,7 @@ from rowers.utils import ( steps_read_fit, steps_write_fit, ps_dict_order, uniqify ) from rowers.metrics import axlabels -from rowers.utils import geo_distance +from rowers.utils import geo_distance, move_one_meter from rowers.formfields import * from rowers.database import * import uuid @@ -594,7 +594,65 @@ def course_spline(coordinates): return newcoordinates +def polygon_nearest_point(polygon, latitude, longitude,debug=False): + points = GeoPoint.objects.filter(polygon=polygon) + points = sorted(points, key = lambda p: geo_distance(p.latitude, p.longitude, latitude, longitude)) + #if debug: + # for p in points: + # print(p,p.latitude, p.longitude, latitude, longitude, geo_distance(p.latitude, p.longitude, latitude, longitude)) + + return points[0].latitude, points[0].longitude + +def polygon_exit_point(polygon, lat1, lon1, lat2, lon2): + dist, bearing = geo_distance(lat1, lon1, lat2, lon2) + dirveclat = (lat2-lat1)/dist + dirveclon = (lon2-lon1)/dist + + newlat, newlon = move_one_meter(lat2, lon2, bearing) + path = polygon_to_path(polygon) + while path.contains_points([(newlat, newlon)])[0]: + newlat, newlon = move_one_meter(newlat, newlon, bearing) + + return newlat, newlon + +def course_coord_crewnerd_navigation(course): + polygons = GeoPolygon.objects.filter( + course=course).order_by("order_in_course") + + latitudes = [] + longitudes = [] + + latitude, longitude = polygon_coord_center(polygons[0]) + + latitudes.append(latitude) + longitudes.append(longitude) + + debug = True + + for p in polygons[1:]: + oldlat = latitude + oldlon = longitude + latitude, longitude = polygon_nearest_point(p,latitude,longitude, debug=debug) + debug = False + latitudes.append(latitude) + longitudes.append(longitude) + latitude, longitude = polygon_exit_point(p, oldlat, oldlon, latitude, longitude) + latitudes.append(latitude) + longitudes.append(longitude) + + + latitude = pd.Series(latitudes).median() + longitude = pd.Series(longitudes).median() + + coordinates = pd.DataFrame({ + 'latitude': latitudes, + 'longitude': longitudes, + }) + + return latitude, longitude, coordinates + + def course_coord_center(course): polygons = GeoPolygon.objects.filter( @@ -1590,6 +1648,24 @@ class GeoCourse(models.Model): def coord(self): return course_coord_center(self) + @property + def with_cn_nav_waypoints(self): + polygons = GeoPolygon.objects.filter(course=self).order_by("order_in_course") + + if polygons[0].name != "Start": + return False + if polygons[len(polygons)-1].name != "Finish": + return False + for i in range(1,len(polygons)-1): + if polygons[i].name[0:2].lower() != 'wp': + return False + try: + getal = float(polygons[i].name[2:]) + except ValueError: + return False + + return True + class GeoCourseEditForm(ModelForm): class Meta: diff --git a/rowers/tasks.py b/rowers/tasks.py index dac27df8..388af488 100644 --- a/rowers/tasks.py +++ b/rowers/tasks.py @@ -94,7 +94,7 @@ SITE_URL_DEV = CFG['site_url'] PROGRESS_CACHE_SECRET = CFG['progress_cache_secret'] try: SETTINGS_NAME = CFG['settings_name'] -except KeyError: +except KeyError: # pragma: no cover SETTINGS_NAME = 'rowsandall_ap.settings' try: @@ -321,7 +321,7 @@ def summaryfromsplitdata(splitdata, data, filename, sep='|', workouttype='rower' return sums, sa, results @app.task -def handle_post_workout_api(uploadoptions, debug=False, **kwargs): +def handle_post_workout_api(uploadoptions, debug=False, **kwargs): # pragma: no cover session = requests.session() newHeaders = {'Content-type': 'application/json', 'Accept': 'text/plain'} session.headers.update(newHeaders) @@ -335,14 +335,14 @@ def handle_post_workout_api(uploadoptions, debug=False, **kwargs): @app.task -def handle_remove_workouts_team(ws, t, debug=False, **kwargs): +def handle_remove_workouts_team(ws, t, debug=False, **kwargs): # pragma: no cover for w in ws: w.team.remove(t) return 1 @app.task -def handle_add_workouts_team(ws, t, debug=False, **kwargs): +def handle_add_workouts_team(ws, t, debug=False, **kwargs): # pragma: no cover for w in ws: w.team.add(t) @@ -350,7 +350,7 @@ def handle_add_workouts_team(ws, t, debug=False, **kwargs): return 1 def uploadactivity(access_token, filename, description='', - name='Rowsandall.com workout'): + name='Rowsandall.com workout'): # pragma: no cover data_gz = BytesIO() try: @@ -393,7 +393,7 @@ def uploadactivity(access_token, filename, description='', @app.task -def check_tp_workout_id(workout, location, attempts=5, debug=False, **kwargs): +def check_tp_workout_id(workout, location, attempts=5, debug=False, **kwargs): # pragma: no cover authorizationstring = str('Bearer ' + workout.user.tptoken) headers = {'Authorization': authorizationstring, 'user-agent': 'sanderroosendaal', @@ -411,7 +411,7 @@ def check_tp_workout_id(workout, location, attempts=5, debug=False, **kwargs): return 1 @app.task -def handle_workout_tp_upload(w, thetoken, tcxfilename, debug=False, **kwargs): +def handle_workout_tp_upload(w, thetoken, tcxfilename, debug=False, **kwargs): # pragma: no cover tpid = 0 r = w.user if not tcxfilename: @@ -443,7 +443,7 @@ def handle_workout_tp_upload(w, thetoken, tcxfilename, debug=False, **kwargs): return tpid @app.task -def instroke_static(w, metric, debug=False, **kwargs): +def instroke_static(w, metric, debug=False, **kwargs): # pragma: no cover f1 = w.csvfilename[6:-4] rowdata = rdata(csvfile=w.csvfilename) @@ -512,7 +512,7 @@ def handle_c2_sync(workoutid, url, headers, data, debug=False, **kwargs): try: workout = Workout.objects.get(id=workoutid) - except Workout.DoesNotExist: + except Workout.DoesNotExist: # pragma: no cover dologging('c2_log.log','failed for c2id {c2id}'.format(c2id=c2id)) return 0 @@ -531,7 +531,7 @@ def handle_c2_sync(workoutid, url, headers, data, debug=False, **kwargs): return 1 -def splitstdata(lijst): +def splitstdata(lijst): # pragma: no cover t = [] latlong = [] while len(lijst) >= 2: @@ -543,7 +543,7 @@ def splitstdata(lijst): @app.task def handle_sporttracks_workout_from_data(user, importid, source, - workoutsource, debug=False, **kwargs): + workoutsource, debug=False, **kwargs): # pragma: no cover r = user.rower authorizationstring = str('Bearer ' + r.sporttrackstoken) @@ -812,7 +812,7 @@ def handle_strava_sync(stravatoken, if not failed: try: workout = Workout.objects.get(id=workoutid) - except Workout.DoesNotExist: + except Workout.DoesNotExist: # pragma: no cover return 0 workout.uploadedtostrava = res.id @@ -1562,7 +1562,7 @@ def handle_calctrimp(id, try: workout = Workout.objects.get(id=id) - except Workout.DoesNotExist: + except Workout.DoesNotExist: # pragma: no cover dologging('metrics.log','Could not find workout {id}'.format(id=id)) return 0 @@ -2037,7 +2037,7 @@ def handle_sendemail_expired(useremail, userfirstname, userlastname, expireddate return 1 @app.task -def handle_sendemail_newftp(rower,power,mode, **kwargs): +def handle_sendemail_newftp(rower,power,mode, **kwargs): # pragma: no cover subject = "You may want to update your FTP on rowsandall.com" from_email = 'Rowsandall ' @@ -2069,7 +2069,7 @@ def handle_sendemail_breakthrough(workoutid, useremail, lastname = '' - if surname: + if surname: # pragma: no cover lastname = userlastname tablevalues = [ @@ -2122,7 +2122,7 @@ def handle_sendemail_hard(workoutid, useremail, ] lastname = '' - if surname: + if surname: # pragma: no cover lastname = userlastname # send email with attachment @@ -3414,7 +3414,7 @@ def handle_nk_async_workout(alldata, userid, nktoken, nkid, delaysec, defaulttim jsonData = response.json() try: strokeData = jsonData[str(nkid)] - except KeyError: + except KeyError: # pragma: no cover dologging('nklog.log','Could not find strokeData') return 0 @@ -3424,7 +3424,7 @@ def handle_nk_async_workout(alldata, userid, nktoken, nkid, delaysec, defaulttim if oarlockSessions: oarlocksession = oarlockSessions[0] seatNumber = oarlocksession['seatNumber'] - except KeyError: + except KeyError: # pragma: no cover pass df = strokeDataToDf(strokeData, seatIndex=seatNumber) @@ -3439,14 +3439,14 @@ def handle_nk_async_workout(alldata, userid, nktoken, nkid, delaysec, defaulttim workoutid, error = add_workout_from_data(userid, nkid, data, df) # dologging('nklog.log','NK Workout ID {id}'.format(id=workoutid)) - if workoutid == 0: + if workoutid == 0: # pragma: no cover return 0 try: workout = Workout.objects.get(id=workoutid) newnkid = workout.uploadedtonk sr = create_or_update_syncrecord(workout.user, workout, nkid=newnkid) - except Workout.DoesNotExist: + except Workout.DoesNotExist: # pragma: no cover pass return workoutid @@ -3775,7 +3775,7 @@ def handle_c2_async_workout(alldata, userid, c2token, c2id, delaysec, return workoutid @app.task -def fetch_rojabo_session(id,alldata,userid,rowerid,debug=False, **kwargs): +def fetch_rojabo_session(id,alldata,userid,rowerid,debug=False, **kwargs): # pragma: no cover try: item = alldata[id] except KeyError: @@ -3880,7 +3880,7 @@ def fetch_strava_workout(stravatoken, oauth_data, stravaid, csvfilename, userid, wsize = round(5./dt) velo2 = ewmovingaverage(velo, wsize) - except ValueError: + except ValueError: # pragma: no cover velo2 = velo if coords is not None: diff --git a/rowers/templates/list_courses.html b/rowers/templates/list_courses.html index bc66697a..a70edd17 100644 --- a/rowers/templates/list_courses.html +++ b/rowers/templates/list_courses.html @@ -23,30 +23,36 @@
  • {% if courses %}

    - - - - - - - - - - {% for course in courses %} - - - - - - - - {% endfor %} - -
    Country Name Distance
    {{ course.country }} - {{ course.name }} - - {{ course.distance }} m -
    +

    + {% csrf_token %} + + + + + + + + + + + + {% for course in courses %} + + + + + + + + + {% endfor %} + +
    Download Country Name Distance
    {{ course.country }} + {{ course.name }} + + {{ course.distance }} m +
    +

    {% else %}

    No courses found

    @@ -106,6 +112,7 @@

    CrewNerd has published a nice video tutorial of the process. Click here to see the video. The part we're interested in starts at 2:05. + There is also a written tutorial by CrewNerd.

    diff --git a/rowers/tests/test_aworkouts.py b/rowers/tests/test_aworkouts.py index 3f5f8f24..595f0148 100644 --- a/rowers/tests/test_aworkouts.py +++ b/rowers/tests/test_aworkouts.py @@ -32,858 +32,902 @@ def create_image(storage, filename, size=(100, 100), image_mode='RGB', image_for return storage.save(filename, image_file) class ListWorkoutTest(TestCase): - def setUp(self): - self.u = UserFactory() + def setUp(self): + self.u = UserFactory() + + self.r = Rower.objects.create(user=self.u, + birthdate=faker.profile()['birthdate'], + gdproptin=True, ftpset=True,surveydone=True, + gdproptindate=timezone.now(), + rowerplan='coach') - self.r = Rower.objects.create(user=self.u, - birthdate=faker.profile()['birthdate'], - gdproptin=True, ftpset=True,surveydone=True, - gdproptindate=timezone.now(), - rowerplan='coach') + self.c = Client() + self.user_workouts = WorkoutFactory.create_batch(len(workouttypes), user=self.r) + i = 0 + for workouttype in workouttypes: + self.user_workouts[i].workouttype = workouttype[0] + self.user_workouts[i].save() + i = i+1 - self.c = Client() - self.user_workouts = WorkoutFactory.create_batch(len(workouttypes), user=self.r) - i = 0 - for workouttype in workouttypes: - self.user_workouts[i].workouttype = workouttype[0] - self.user_workouts[i].save() - i = i+1 + self.factory = RequestFactory() + self.password = faker.word() + self.u.set_password(self.password) + self.u.save() - 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, FileNotFoundError,OSError): + pass - def tearDown(self): - for workout in self.user_workouts: - try: - os.remove(workout.csvfilename) - except (IOError, FileNotFoundError,OSError): - pass + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getsmallrowdata_db') + @patch('rowers.dataprep.myqueue') + def test_list_workouts(self, mocked_sqlalchemy, + mocked_getsmallrowdata_db, + mocked_myqueue): - @patch('rowers.dataprep.create_engine') - @patch('rowers.dataprep.getsmallrowdata_db') - @patch('rowers.dataprep.myqueue') - def test_list_workouts(self, mocked_sqlalchemy, - mocked_getsmallrowdata_db, - mocked_myqueue): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) - login = self.c.login(username=self.u.username, password=self.password) - self.assertTrue(login) + url = reverse('workouts_view') + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + url2 = url+'?page=2' + + response = self.c.get(url2) + self.assertEqual(response.status_code,200) + + url3 = url+'?page=3' + + response = self.c.get(url3) + self.assertEqual(response.status_code,200) + + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getsmallrowdata_db') + @patch('rowers.dataprep.myqueue') + def test_bulk_workouts(self, mocked_sqlalchemy, + mocked_getsmallrowdata_db, + mocked_myqueue): + + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + url = '/rowers/list-workouts/?selectworkouts=true' + response = self.c.get(url) + self.assertEqual(response.status_code,200) - url = reverse('workouts_view') - response = self.c.get(url) - self.assertEqual(response.status_code,200) + form_data = { + 'action': ['remove'], + 'selectworkouts': ['Submit'], + 'workoutid': [encoder.encode_hex(1), encoder.encode_hex(2)], + } - url2 = url+'?page=2' + response = self.c.post(url, form_data, follow=True) + expected_url = reverse('workouts_bulk_actions') + self.assertRedirects(response, + expected_url=expected_url, + status_code=302, + target_status_code=200) - response = self.c.get(url2) - self.assertEqual(response.status_code,200) + form_data = { + 'workouts': ['1','2'], + 'action': ['export'], + 'destination': 'strava', + } - url3 = url+'?page=3' - - response = self.c.get(url3) - self.assertEqual(response.status_code,200) + response = self.c.post(expected_url, form_data, follow=True) + url = expected_url + expected_url = reverse('workouts_view') + self.assertRedirects(response, + expected_url=expected_url, + status_code=302, + target_status_code=200) + form_data = { + 'workouts': ['1','2'], + 'action': ['remove'], + } + response = self.c.post(url, form_data, follow=True) + expected_url = reverse('workouts_view') + self.assertRedirects(response, + expected_url=expected_url, + status_code=302, + target_status_code=200) + @override_settings(TESTING=True) class WorkoutViewTest(TestCase): - def setUp(self): - self.u = UserFactory() + def setUp(self): + self.u = UserFactory() - self.r = Rower.objects.create(user=self.u, - birthdate=faker.profile()['birthdate'], - gdproptin=True, ftpset=True,surveydone=True, - gdproptindate=timezone.now(), - rowerplan='coach') + self.r = Rower.objects.create(user=self.u, + birthdate=faker.profile()['birthdate'], + gdproptin=True, ftpset=True,surveydone=True, + gdproptindate=timezone.now(), + rowerplan='coach') - self.c = Client() - self.c2 = Client() # for anonymous - 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() + self.c = Client() + self.c2 = Client() # for anonymous + 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/onwater2.csv') + result = get_random_file(filename='rowers/tests/testdata/onwater2.csv') - self.wwater = WorkoutFactory(user=self.r, - csvfilename=result['filename'], - starttime=result['starttime'], - startdatetime=result['startdatetime'], - duration=result['duration'], - distance=result['totaldist'], - workouttype = 'water', - ) + self.wwater = WorkoutFactory(user=self.r, + csvfilename=result['filename'], + starttime=result['starttime'], + startdatetime=result['startdatetime'], + duration=result['duration'], + distance=result['totaldist'], + workouttype = 'water', + ) -# self.comments = CommentFactory.create_batch(5, -# user = self.u, -# created = timezone.now, -# workout = self.wwater) + result = get_random_file(filename='rowers/tests/testdata/quiske_in_stroke.csv') + + self.winstroke = WorkoutFactory(user=self.r, + csvfilename=result['filename'], + starttime=result['starttime'], + startdatetime=result['startdatetime'], + duration=result['duration'], + distance=result['totaldist'], + workouttype = 'water', + ) - result = get_random_file(filename='rowers/tests/testdata/quiske_in_stroke.csv') + result = get_random_file(filename='rowers/tests/testdata/erg1.csv') + + self.werg1 = WorkoutFactory(user=self.r, + csvfilename=result['filename'], + starttime=result['starttime'], + startdatetime=result['startdatetime'], + duration=result['duration'], + distance=result['totaldist'], + workouttype = 'rower', + ) - self.winstroke = WorkoutFactory(user=self.r, - csvfilename=result['filename'], - starttime=result['starttime'], - startdatetime=result['startdatetime'], - duration=result['duration'], - distance=result['totaldist'], - workouttype = 'water', - ) + result = get_random_file(filename='rowers/tests/testdata/erg2.csv') - result = get_random_file(filename='rowers/tests/testdata/erg1.csv') + self.werg2 = WorkoutFactory(user=self.r, + csvfilename=result['filename'], + starttime=result['starttime'], + startdatetime=result['startdatetime'], + duration=result['duration'], + distance=result['totaldist'], + workouttype = 'rower', + ) - self.werg1 = WorkoutFactory(user=self.r, - csvfilename=result['filename'], - starttime=result['starttime'], - startdatetime=result['startdatetime'], - duration=result['duration'], - distance=result['totaldist'], - workouttype = 'rower', - ) + result = get_random_file(filename='rowers/tests/testdata/erg2.csv') - result = get_random_file(filename='rowers/tests/testdata/erg2.csv') - - self.werg2 = WorkoutFactory(user=self.r, - csvfilename=result['filename'], - starttime=result['starttime'], - startdatetime=result['startdatetime'], - duration=result['duration'], - distance=result['totaldist'], - workouttype = 'rower', - ) - - result = get_random_file(filename='rowers/tests/testdata/erg2.csv') - - self.werg2copy = WorkoutFactory(user=self.r, - csvfilename=result['filename'], - starttime=result['starttime'], - startdatetime=result['startdatetime'], - duration=result['duration'], - distance=result['totaldist'], - workouttype = 'rower', - ) + self.werg2copy = WorkoutFactory(user=self.r, + csvfilename=result['filename'], + starttime=result['starttime'], + startdatetime=result['startdatetime'], + duration=result['duration'], + distance=result['totaldist'], + workouttype = 'rower', + ) - result = get_random_file(filename='rowers/tests/testdata/erg3.csv') + result = get_random_file(filename='rowers/tests/testdata/erg3.csv') - self.werg3 = WorkoutFactory(user=self.r, - csvfilename=result['filename'], - starttime=result['starttime'], - startdatetime=result['startdatetime'], - duration=result['duration'], - distance=result['totaldist'], - workouttype = 'rower', - ) - - def tearDown(self): - vs = VideoAnalysis.objects.all() - for v in vs: + self.werg3 = WorkoutFactory(user=self.r, + csvfilename=result['filename'], + starttime=result['starttime'], + startdatetime=result['startdatetime'], + duration=result['duration'], + distance=result['totaldist'], + workouttype = 'rower', + ) + + def tearDown(self): + vs = VideoAnalysis.objects.all() + for v in vs: v.delete() - @patch('rowers.dataprep.create_engine') - @patch('rowers.dataprep.getsmallrowdata_db') - @patch('rowers.middleware.myqueue') - def test_forcecurve(self, mocked_sqlalchemy, mocked_getsmallrowdata_db, - mocked_myqueue): - login = self.c.login(username=self.u.username, password=self.password) - self.assertTrue(login) + - url = reverse('workout_forcecurve_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getsmallrowdata_db') + @patch('rowers.middleware.myqueue') + def test_forcecurve(self, mocked_sqlalchemy, mocked_getsmallrowdata_db, + mocked_myqueue): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) - response = self.c.get(url) - self.assertEqual(response.status_code,200) + url = reverse('workout_forcecurve_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) - form_data = { - 'workstrokesonly': True, - 'plottype': 'scatter' - } + form_data = { + 'workstrokesonly': True, + 'plottype': 'scatter' + } - response = self.c.post(url,form_data) - self.assertEqual(response.status_code,200) + response = self.c.post(url,form_data) + self.assertEqual(response.status_code,200) - @patch('rowers.dataprep.create_engine') - @patch('rowers.dataprep.getsmallrowdata_db') - @pytest.mark.django_db(transaction=True) - def test_joins(self, mocked_sqlalchemy, mocked_getsmallrowdata_db): - with transaction.atomic(): - login = self.c.login(username=self.u.username, password=self.password) - self.assertTrue(login) + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getsmallrowdata_db') + @pytest.mark.django_db(transaction=True) + def test_joins(self, mocked_sqlalchemy, mocked_getsmallrowdata_db): + with transaction.atomic(): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + url = reverse('workouts_join_select') + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + d1 = self.werg1.date-datetime.timedelta(days=2) + d2 = self.werg2.date+datetime.timedelta(days=2) + + form_data = { + 'startdate': d1.strftime('%Y-%m-%d'), + 'enddate': d2.strftime('%Y-%m-%d'), + 'modality':'water', + 'waterboattype':['1x'], + } - url = reverse('workouts_join_select') - response = self.c.get(url) - self.assertEqual(response.status_code,200) + form = DateRangeForm(form_data) + self.assertTrue(form.is_valid()) - d1 = self.werg1.date-datetime.timedelta(days=2) - d2 = self.werg2.date+datetime.timedelta(days=2) + form = TrendFlexModalForm(form_data) + self.assertTrue(form.is_valid()) - form_data = { - 'startdate': d1.strftime('%Y-%m-%d'), - 'enddate': d2.strftime('%Y-%m-%d'), - 'modality':'water', - 'waterboattype':['1x'], - } + response = self.c.post(url,form_data) + self.assertEqual(response.status_code,200) - form = DateRangeForm(form_data) - self.assertTrue(form.is_valid()) + url = reverse('workouts_join_view') + + response = self.c.get(url,follow=True) + self.assertEqual(response.status_code,200) - form = TrendFlexModalForm(form_data) - self.assertTrue(form.is_valid()) + expected_url = reverse('workouts_join_select') + self.assertRedirects(response, + expected_url=expected_url, + status_code=302, + target_status_code=200) - response = self.c.post(url,form_data) - self.assertEqual(response.status_code,200) + form_data = { + 'workout_name': 'joined', + 'set_private': False, + 'workouts': [str(self.werg1.id),str(self.werg2.id),str(self.werg3.id)], + } - url = reverse('workouts_join_view') + form = WorkoutMultipleCompareForm(form_data) + self.assertTrue(form.is_valid()) - response = self.c.get(url,follow=True) - self.assertEqual(response.status_code,200) + form = WorkoutJoinParamForm(form_data) + self.assertTrue(form.is_valid()) - expected_url = reverse('workouts_join_select') - self.assertRedirects(response, - expected_url=expected_url, - status_code=302, - target_status_code=200) + response = self.c.post(url,form_data,follow=True) + self.assertEqual(response.status_code,200) - form_data = { - 'workout_name': 'joined', - 'set_private': False, - 'workouts': [str(self.werg1.id),str(self.werg2.id),str(self.werg3.id)], - } - - form = WorkoutMultipleCompareForm(form_data) - self.assertTrue(form.is_valid()) - - form = WorkoutJoinParamForm(form_data) - self.assertTrue(form.is_valid()) - - response = self.c.post(url,form_data,follow=True) - self.assertEqual(response.status_code,200) - - expected_url = reverse(self.r.defaultlandingpage, - kwargs = { + expected_url = reverse(self.r.defaultlandingpage, + kwargs = { 'id':encoder.encode_hex(12) - }) - - self.assertRedirects(response, - expected_url=expected_url, - status_code=302,target_status_code=200) - - - @patch('rowers.dataprep.create_engine') - @patch('rowers.dataprep.getsmallrowdata_db') - @patch('rowers.weather.requests.get', side_effect=mocked_requests) - def test_waterworkout_view(self, - mocked_sqlalchemy, mocked_getsmallrowdata_db, mocked_requests): - login = self.c.login(username=self.u.username, password=self.password) - self.assertTrue(login) - - url = reverse('workout_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) - - response = self.c.get(url) - self.assertEqual(response.status_code,200) - - url = reverse('workout_downloadmetar_view',kwargs={ - 'id': encoder.encode_hex(self.wwater.id), - 'airportcode': 'LKHO' - } - ) - - response = self.c.get(url,follow=True) - self.assertEqual(response.status_code,200) - - url = reverse('workout_downloadwind_view',kwargs={ - 'id': encoder.encode_hex(self.wwater.id), - } - ) - - response = self.c.get(url,follow=True) - self.assertEqual(response.status_code,200) - - # Stacked Flex Chart - url = reverse('workout_flexchart_stacked_view',kwargs={ - 'id': encoder.encode_hex(self.wwater.id), - }) - - response = self.c.get(url,follow=True) - self.assertEqual(response.status_code,200) - - form_data = { - 'xaxis':'time', - 'yaxis1':'power', - 'yaxis2':'pace', - 'yaxis3':'hr', - 'yaxis4':'spm' - } - - - - response = self.c.post(url,form_data) - self.assertEqual(response.status_code,200) - - url = reverse('workout_erase_column_view',kwargs={'id':encoder.encode_hex(self.wwater.id),'column':'hr'}) - expected_url = reverse('workout_data_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) - - response = self.c.get(url,follow=True) - self.assertEqual(response.status_code,200) - - form_data = {} - response = self.c.post(url,form_data,follow=True) - self.assertRedirects(response,expected_url=expected_url,status_code=302,target_status_code=200) - - url = expected_url - form_data = { - 'cols':['hr','spm'] - } - form = DataFrameColumnsForm(form_data) - - self.assertTrue(form.is_valid()) - - response = self.c.post(url,form_data) - self.assertEqual(response.status_code,200) - - @patch('rowers.dataprep.create_engine') - @patch('rowers.dataprep.getsmallrowdata_db') - def test_smoothen(self, mocked_sqlalchemy, mocked_getsmallrowdata_db): - login = self.c.login(username=self.u.username, password=self.password) - self.assertTrue(login) - - - url = reverse('workout_smoothenpace_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) - - response = self.c.get(url,follow=True) - self.assertEqual(response.status_code,200) - - expected_url = reverse(self.r.defaultlandingpage, - kwargs = { - 'id':encoder.encode_hex(self.wwater.id) - } - ) - - self.assertRedirects(response, - expected_url=expected_url, - status_code=302,target_status_code=200) - - url = reverse('workout_undo_smoothenpace_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) - - response = self.c.get(url,follow=True) - self.assertEqual(response.status_code,200) - - expected_url = reverse(self.r.defaultlandingpage, - kwargs = { - 'id':encoder.encode_hex(self.wwater.id) - } - ) - - self.assertRedirects(response, - expected_url=expected_url, - status_code=302,target_status_code=200) - - - @patch('rowers.dataprep.create_engine') - @patch('rowers.dataprep.getsmallrowdata_db') - def test_windform(self, mocked_sqlalchemy, mocked_getsmallrowdata_db): - login = self.c.login(username=self.u.username, password=self.password) - self.assertTrue(login) - - - url = reverse('workout_wind_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) - - response = self.c.get(url) - self.assertEqual(response.status_code,200) - - form_data = { - 'dist1':1000, - 'dist2':2000, - 'vwind1':2.0, - 'vwind2':1.4, - 'windunit':'m', - 'winddirection1': 0, - 'winddirection2': 90, - } - - form = UpdateWindForm(form_data) - self.assertTrue(form.is_valid()) - - response = self.c.post(url,form_data) - self.assertEqual(response.status_code,200) - - @patch('rowers.dataprep.create_engine') - @patch('rowers.dataprep.getsmallrowdata_db') - def test_streamform(self, mocked_sqlalchemy, mocked_getsmallrowdata_db): - login = self.c.login(username=self.u.username, password=self.password) - self.assertTrue(login) - - - url = reverse('workout_stream_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) - - response = self.c.get(url) - self.assertEqual(response.status_code,200) - - form_data = { - 'dist1':1000, - 'dist2':2000, - 'stream1':2.0, - 'stream2':1.4, - 'streamunit':'m', - } - - form = UpdateStreamForm(form_data) - self.assertTrue(form.is_valid()) - - response = self.c.post(url,form_data) - self.assertEqual(response.status_code,200) - - - - @patch('rowers.dataprep.create_engine') - @patch('rowers.dataprep.getsmallrowdata_db') - @patch('rowers.middleware.myqueue') - def test_setpowerform(self, mocked_sqlalchemy, mocked_getsmallrowdata_db, - mocked_myqueue): - login = self.c.login(username=self.u.username, password=self.password) - self.assertTrue(login) - - url = reverse('instroke_view',kwargs={'id':encoder.encode_hex(self.winstroke.id)}) - - response = self.c.get(url) - self.assertEqual(response.status_code,200) - - rowdata = rowingdata.rowingdata(csvfile=self.winstroke.csvfilename) - instrokemetrics = rowdata.get_instroke_columns() - - self.assertTrue(len(instrokemetrics)>0) - - url = reverse('instroke_chart', - kwargs={ - 'id':encoder.encode_hex(self.winstroke.id), - 'metric':instrokemetrics[0], - }) - url2 = reverse(self.r.defaultlandingpage,kwargs={'id':encoder.encode_hex(self.winstroke.id)}) - response = self.c.get(url) - self.assertRedirects(response, - expected_url=url2, - status_code=302,target_status_code=200) - - @patch('rowers.dataprep.create_engine') - @patch('rowers.dataprep.getsmallrowdata_db') - def test_setpowerform(self, mocked_sqlalchemy, mocked_getsmallrowdata_db): - login = self.c.login(username=self.u.username, password=self.password) - self.assertTrue(login) - - - url = reverse('workout_otwsetpower_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) - - response = self.c.get(url) - self.assertEqual(response.status_code,200) - - form_data = { - 'quick_calc':True, - 'boattype': '1x', - 'weightvalue': 75.0, - 'boatbrand':'maas', - } - - form = AdvancedWorkoutForm(form_data) - self.assertTrue(form.is_valid()) - - response = self.c.post(url,form_data,follow=True) - self.assertEqual(response.status_code,200) - - expected_url = reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) - - self.assertRedirects(response, - expected_url=expected_url, - status_code=302,target_status_code=200) - - @patch('rowers.dataprep.create_engine') - @patch('rowers.dataprep.getsmallrowdata_db') - def test_commentview(self, mocked_sqlalchemy, mocked_getsmallrowdata_db): - login = self.c.login(username=self.u.username, password=self.password) - self.assertTrue(login) - - - url = reverse('workout_comment_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) - - response = self.c.get(url) - self.assertEqual(response.status_code,200) - - form_data = { - 'comment': faker.text() - } - - form = WorkoutCommentForm(form_data) - self.assertTrue(form.is_valid()) - - response = self.c.post(url,form_data,follow=True) - self.assertEqual(response.status_code,200) - - url = reverse('workout_unsubscribe_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) - response = self.c.get(url) - self.assertEqual(response.status_code,200) - - @patch('rowers.dataprep.create_engine') - @patch('rowers.dataprep.getsmallrowdata_db') - def test_mapview(self, mocked_sqlalchemy, mocked_getsmallrowdata_db): - login = self.c.login(username=self.u.username, password=self.password) - self.assertTrue(login) - - - url = reverse('workout_map_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) - - response = self.c.get(url) - self.assertEqual(response.status_code,200) - - @patch('rowers.dataprep.create_engine') - @patch('rowers.dataprep.getsmallrowdata_db') - def notworking_test_workout_image(self, mocked_sqlalchemy, - mocked_getsmallrowdata_db): - login = self.c.login(username=self.u.username, password=self.password) - self.assertTrue(login) - - url = reverse('workout_uploadimage_view', - kwargs={'id':encoder.encode_hex(self.werg1.id)}) - - response = self.c.get(url) - self.assertEqual(response.status_code,200) - - with open('rowers/tests/testdata/lofoten.jpg','rb') as screenshot_file: - file_data = {'file':screenshot_file} - form_data = { - 'file':screenshot_file, - } - - #form = ImageForm(form_data, file_data) - #if not form.is_valid(): - # print form.errors - #self.assertTrue(form.is_valid()) - - response = self.c.post(url,form_data,format='multipart',follow=True) - - expected_url = reverse(self.r.defaultlandingpage, + }) + + self.assertRedirects(response, + expected_url=expected_url, + status_code=302,target_status_code=200) + + + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getsmallrowdata_db') + @patch('rowers.weather.requests.get', side_effect=mocked_requests) + def test_waterworkout_view(self, + mocked_sqlalchemy, mocked_getsmallrowdata_db, mocked_requests): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + url = reverse('workout_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + url = reverse('workout_downloadmetar_view',kwargs={ + 'id': encoder.encode_hex(self.wwater.id), + 'airportcode': 'LKHO' + } + ) + + response = self.c.get(url,follow=True) + self.assertEqual(response.status_code,200) + + url = reverse('workout_downloadwind_view',kwargs={ + 'id': encoder.encode_hex(self.wwater.id), + } + ) + + response = self.c.get(url,follow=True) + self.assertEqual(response.status_code,200) + + # Stacked Flex Chart + url = reverse('workout_flexchart_stacked_view',kwargs={ + 'id': encoder.encode_hex(self.wwater.id), + }) + + response = self.c.get(url,follow=True) + self.assertEqual(response.status_code,200) + + form_data = { + 'xaxis':'time', + 'yaxis1':'power', + 'yaxis2':'pace', + 'yaxis3':'hr', + 'yaxis4':'spm' + } + + + + response = self.c.post(url,form_data) + self.assertEqual(response.status_code,200) + + url = reverse('workout_erase_column_view',kwargs={'id':encoder.encode_hex(self.wwater.id),'column':'hr'}) + expected_url = reverse('workout_data_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) + + response = self.c.get(url,follow=True) + self.assertEqual(response.status_code,200) + + form_data = {} + response = self.c.post(url,form_data,follow=True) + self.assertRedirects(response,expected_url=expected_url,status_code=302,target_status_code=200) + + url = expected_url + form_data = { + 'cols':['hr','spm'] + } + form = DataFrameColumnsForm(form_data) + + self.assertTrue(form.is_valid()) + + response = self.c.post(url,form_data) + self.assertEqual(response.status_code,200) + + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getsmallrowdata_db') + def test_smoothen(self, mocked_sqlalchemy, mocked_getsmallrowdata_db): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + + url = reverse('workout_smoothenpace_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) + + response = self.c.get(url,follow=True) + self.assertEqual(response.status_code,200) + + expected_url = reverse(self.r.defaultlandingpage, + kwargs = { + 'id':encoder.encode_hex(self.wwater.id) + } + ) + + self.assertRedirects(response, + expected_url=expected_url, + status_code=302,target_status_code=200) + + url = reverse('workout_undo_smoothenpace_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) + + response = self.c.get(url,follow=True) + self.assertEqual(response.status_code,200) + + expected_url = reverse(self.r.defaultlandingpage, + kwargs = { + 'id':encoder.encode_hex(self.wwater.id) + } + ) + + self.assertRedirects(response, + expected_url=expected_url, + status_code=302,target_status_code=200) + + + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getsmallrowdata_db') + def test_windform(self, mocked_sqlalchemy, mocked_getsmallrowdata_db): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + + url = reverse('workout_wind_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + form_data = { + 'dist1':1000, + 'dist2':2000, + 'vwind1':2.0, + 'vwind2':1.4, + 'windunit':'m', + 'winddirection1': 0, + 'winddirection2': 90, + } + + form = UpdateWindForm(form_data) + self.assertTrue(form.is_valid()) + + response = self.c.post(url,form_data) + self.assertEqual(response.status_code,200) + + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getsmallrowdata_db') + def test_streamform(self, mocked_sqlalchemy, mocked_getsmallrowdata_db): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + + url = reverse('workout_stream_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + form_data = { + 'dist1':1000, + 'dist2':2000, + 'stream1':2.0, + 'stream2':1.4, + 'streamunit':'m', + } + + form = UpdateStreamForm(form_data) + self.assertTrue(form.is_valid()) + + response = self.c.post(url,form_data) + self.assertEqual(response.status_code,200) + + + + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getsmallrowdata_db') + @patch('rowers.middleware.myqueue') + def test_setpowerform(self, mocked_sqlalchemy, mocked_getsmallrowdata_db, + mocked_myqueue): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + url = reverse('instroke_view',kwargs={'id':encoder.encode_hex(self.winstroke.id)}) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + rowdata = rowingdata.rowingdata(csvfile=self.winstroke.csvfilename) + instrokemetrics = rowdata.get_instroke_columns() + + self.assertTrue(len(instrokemetrics)>0) + + url = reverse('instroke_chart', + kwargs={ + 'id':encoder.encode_hex(self.winstroke.id), + 'metric':instrokemetrics[0], + }) + url2 = reverse(self.r.defaultlandingpage,kwargs={'id':encoder.encode_hex(self.winstroke.id)}) + response = self.c.get(url) + self.assertRedirects(response, + expected_url=url2, + status_code=302,target_status_code=200) + + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getsmallrowdata_db') + def test_setpowerform(self, mocked_sqlalchemy, mocked_getsmallrowdata_db): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + + url = reverse('workout_otwsetpower_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + form_data = { + 'quick_calc':True, + 'boattype': '1x', + 'weightvalue': 75.0, + 'boatbrand':'maas', + } + + form = AdvancedWorkoutForm(form_data) + self.assertTrue(form.is_valid()) + + response = self.c.post(url,form_data,follow=True) + self.assertEqual(response.status_code,200) + + expected_url = reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) + + self.assertRedirects(response, + expected_url=expected_url, + status_code=302,target_status_code=200) + + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getsmallrowdata_db') + def test_commentview(self, mocked_sqlalchemy, mocked_getsmallrowdata_db): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + + url = reverse('workout_comment_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + form_data = { + 'comment': faker.text() + } + + form = WorkoutCommentForm(form_data) + self.assertTrue(form.is_valid()) + + response = self.c.post(url,form_data,follow=True) + self.assertEqual(response.status_code,200) + + url = reverse('workout_unsubscribe_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getsmallrowdata_db') + def test_mapview(self, mocked_sqlalchemy, mocked_getsmallrowdata_db): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + + url = reverse('workout_map_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getsmallrowdata_db') + def notworking_test_workout_image(self, mocked_sqlalchemy, + mocked_getsmallrowdata_db): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + url = reverse('workout_uploadimage_view', + kwargs={'id':encoder.encode_hex(self.werg1.id)}) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + with open('rowers/tests/testdata/lofoten.jpg','rb') as screenshot_file: + file_data = {'file':screenshot_file} + form_data = { + 'file':screenshot_file, + } + + response = self.c.post(url,form_data,format='multipart',follow=True) + + expected_url = reverse(self.r.defaultlandingpage, kwargs={'id':encoder.encode_hex(self.werg1.id)}) - self.assertRedirects(response, - expected_url=expected_url, - status_code=302,target_status_code=200) + self.assertRedirects(response, + expected_url=expected_url, + status_code=302,target_status_code=200) + + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getsmallrowdata_db') + @patch('rowers.dataprep.getrowdata_db',side_effect=mocked_getrowdata_db) + @pytest.mark.django_db(transaction=True) + def test_workout_split(self, mocked_sqlalchemy, mocked_getsmallrowdata_db, + mocked_getrowdata_db): + with transaction.atomic(): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + url = reverse('workout_split_view',kwargs={'id':encoder.encode_hex(self.werg1.id)}) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + form_data = { + 'splittime': '2:00', + 'splitmode': ['keep original','keep first','keep second'] + } + + form = WorkoutSplitForm(form_data) + + self.assertTrue(form.is_valid()) + + response = self.c.post(url, form_data,follow=True) + self.assertEqual(response.status_code,200) + + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getrowdata_db',side_effect=mocked_getrowdata_db) + @patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_forfusion) + def test_workout_fusion(self, mocked_sqlalchemy, mocked_getrowdata_db, + mocked_getsmallrowdata_db): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + url = reverse('workout_fusion_view', + kwargs={'id1':encoder.encode_hex(self.werg2.id), + 'id2':encoder.encode_hex(self.werg2copy.id)}) + + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + form_data = { + 'posneg':'pos', + 'offset': datetime.timedelta(seconds=0), + 'columns': ['power','hr' + ] + } + + form = FusionMetricChoiceForm(form_data,instance=self.werg2copy) + + self.assertTrue(form.is_valid()) + + response = self.c.post(url,form_data,follow=True) + self.assertEqual(response.status_code,200) + + + def test_remove_power_view(self): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + url = reverse('remove_power_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) + url2 = reverse(self.r.defaultlandingpage, + kwargs={ + 'id':encoder.encode_hex(self.wwater.id) + } + ) + response = self.c.get(url) + self.assertRedirects(response, + expected_url=url2, + status_code=302, + target_status_code=200 + ) - @patch('rowers.dataprep.create_engine') - @patch('rowers.dataprep.getsmallrowdata_db') - @patch('rowers.dataprep.getrowdata_db',side_effect=mocked_getrowdata_db) - @pytest.mark.django_db(transaction=True) - def test_workout_split(self, mocked_sqlalchemy, mocked_getsmallrowdata_db, - mocked_getrowdata_db): - with transaction.atomic(): - login = self.c.login(username=self.u.username, password=self.password) - self.assertTrue(login) - - url = reverse('workout_split_view',kwargs={'id':encoder.encode_hex(self.werg1.id)}) - - response = self.c.get(url) - self.assertEqual(response.status_code,200) - - form_data = { - 'splittime': '2:00', - 'splitmode': ['keep original','keep first','keep second'] - } - - form = WorkoutSplitForm(form_data) - - self.assertTrue(form.is_valid()) - - response = self.c.post(url, form_data,follow=True) - self.assertEqual(response.status_code,200) - - @patch('rowers.dataprep.create_engine') - @patch('rowers.dataprep.getrowdata_db',side_effect=mocked_getrowdata_db) - @patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_forfusion) - def test_workout_fusion(self, mocked_sqlalchemy, mocked_getrowdata_db, - mocked_getsmallrowdata_db): - login = self.c.login(username=self.u.username, password=self.password) - self.assertTrue(login) - - url = reverse('workout_fusion_view', - kwargs={'id1':encoder.encode_hex(self.werg2.id), - 'id2':encoder.encode_hex(self.werg2copy.id)}) + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getsmallrowdata_db') + @patch('rowers.dataprep.get_video_data',side_effect=mocked_videodata) + def test_workout_video_view(self, mocked_sqlalchemy, mocked_getsmallrowdata_db, + mocked_videodata): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + url = reverse('workout_video_create_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) + url2 = reverse('workout_video_view',kwargs={'id':encoder.encode_hex(1)}) - response = self.c.get(url) - self.assertEqual(response.status_code,200) + yturl = 'https://youtu.be/6UzaWm1Ybrw' + + response = self.c.get(url) + + self.assertEqual(response.status_code,200) + + form_data = { + 'url': yturl, + 'delay': -119, + 'groups': ['basic'], + 'name': 'Video A', + 'save_button':'Save', + } - form_data = { - 'posneg':'pos', - 'offset': datetime.timedelta(seconds=0), - 'columns': ['power','hr' - ] - } + response = self.c.post(url,form_data) + self.assertRedirects(response, + expected_url=url2, + status_code=302, + target_status_code=200) + + response = self.c.get(url2) + self.assertEqual(response.status_code,200) - form = FusionMetricChoiceForm(form_data,instance=self.werg2copy) + form_data = { + 'url': yturl, + 'delay': -119, + 'groups': ['basic'], + 'name': 'Video A', + 'save_button': 'Save', + } - self.assertTrue(form.is_valid()) + response = self.c.post(url2,form_data) + self.assertEqual(response.status_code,200) - response = self.c.post(url,form_data,follow=True) - self.assertEqual(response.status_code,200) + url = reverse('workout_video_view_mini',kwargs={'id':encoder.encode_hex(1)}) + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + response = self.c.post(url,form_data) + self.assertEqual(response.status_code,200) + + # delete video + url = reverse('video_delete',kwargs={'pk':1}) + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + response = self.c.post(url,{},follow=True) + expected_url = reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) + self.assertRedirects(response,expected_url=expected_url,status_code=302,target_status_code=200) + + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getsmallrowdata_db') + @patch('rowers.dataprep.get_video_data',side_effect=mocked_videodata) + def test_workout_video_view_erg(self, mocked_sqlalchemy, mocked_getsmallrowdata_db, + mocked_videodata): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + url = reverse('workout_video_create_view',kwargs={'id':encoder.encode_hex(self.werg1.id)}) + url2 = reverse('workout_video_view',kwargs={'id':encoder.encode_hex(1)}) + + + yturl = 'https://youtu.be/6UzaWm1Ybrw' + + response = self.c.get(url) + + self.assertEqual(response.status_code,200) + + form_data = { + 'url': yturl, + 'delay': -119, + 'groups': ['basic'], + 'name': 'Video A', + 'save_button':'Save', + } + + response = self.c.post(url,form_data) + self.assertRedirects(response, + expected_url=url2, + status_code=302, + target_status_code=200) + + response = self.c.get(url2) + self.assertEqual(response.status_code,200) + + form_data = { + 'url': yturl, + 'delay': -119, + 'groups': ['basic'], + 'name': 'Video A', + 'save_button': 'Save', + } + + response = self.c.post(url2,form_data) + self.assertEqual(response.status_code,200) + + url = reverse('workout_video_view_mini',kwargs={'id':encoder.encode_hex(1)}) + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + response = self.c.post(url,form_data) + self.assertEqual(response.status_code,200) + + url = reverse('workout_video_view_mini',kwargs={'id':encoder.encode_hex(1)}) + response = self.c2.get(url) + self.assertEqual(response.status_code,200) + + url = reverse('workout_video_view',kwargs={'id':encoder.encode_hex(1)}) + response = self.c2.get(url) + self.assertEqual(response.status_code,200) - def test_remove_power_view(self): - login = self.c.login(username=self.u.username, password=self.password) - self.assertTrue(login) - - url = reverse('remove_power_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) - url2 = reverse(self.r.defaultlandingpage, - kwargs={ - 'id':encoder.encode_hex(self.wwater.id) - } - ) - response = self.c.get(url) - self.assertRedirects(response, - expected_url=url2, - status_code=302, - target_status_code=200 - ) - - @patch('rowers.dataprep.create_engine') - @patch('rowers.dataprep.getsmallrowdata_db') - @patch('rowers.dataprep.get_video_data',side_effect=mocked_videodata) - def test_workout_video_view(self, mocked_sqlalchemy, mocked_getsmallrowdata_db, + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getrowdata_db',side_effect=mocked_getrowdata_db) + @patch('rowers.dataprep.get_video_data',side_effect=mocked_videodata) + def test_video_selectworkout(self, mocked_sqlalchemy, mocked_getsmallrowdata_db, mocked_videodata): - login = self.c.login(username=self.u.username, password=self.password) - self.assertTrue(login) + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) - url = reverse('workout_video_create_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) - url2 = reverse('workout_video_view',kwargs={'id':encoder.encode_hex(1)}) + url = reverse('video_selectworkout') + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + d1 = self.wwater.date-datetime.timedelta(days=24) + d2 = self.wwater.date+datetime.timedelta(days=24) + + form_data = { + 'workout':'1', + 'startdate': d1.strftime("%Y-%m-%d"), + 'enddate': d2.strftime("%Y-%m-%d") + } - yturl = 'https://youtu.be/6UzaWm1Ybrw' + url2 = reverse('workout_video_create_view',kwargs={'id':encoder.encode_hex(1)}) - response = self.c.get(url) - - self.assertEqual(response.status_code,200) - - form_data = { - 'url': yturl, - 'delay': -119, - 'groups': ['basic'], - 'name': 'Video A', - 'save_button':'Save', - } - - response = self.c.post(url,form_data) - self.assertRedirects(response, - expected_url=url2, - status_code=302, - target_status_code=200) - - response = self.c.get(url2) - self.assertEqual(response.status_code,200) - - form_data = { - 'url': yturl, - 'delay': -119, - 'groups': ['basic'], - 'name': 'Video A', - 'save_button': 'Save', - } - - response = self.c.post(url2,form_data) - self.assertEqual(response.status_code,200) - - url = reverse('workout_video_view_mini',kwargs={'id':encoder.encode_hex(1)}) - response = self.c.get(url) - self.assertEqual(response.status_code,200) - - response = self.c.post(url,form_data) - self.assertEqual(response.status_code,200) - - # delete video - url = reverse('video_delete',kwargs={'pk':1}) - response = self.c.get(url) - self.assertEqual(response.status_code,200) - - response = self.c.post(url,{},follow=True) - expected_url = reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) - self.assertRedirects(response,expected_url=expected_url,status_code=302,target_status_code=200) - - @patch('rowers.dataprep.create_engine') - @patch('rowers.dataprep.getsmallrowdata_db') - @patch('rowers.dataprep.get_video_data',side_effect=mocked_videodata) - def test_workout_video_view_erg(self, mocked_sqlalchemy, mocked_getsmallrowdata_db, - mocked_videodata): - login = self.c.login(username=self.u.username, password=self.password) - self.assertTrue(login) - - url = reverse('workout_video_create_view',kwargs={'id':encoder.encode_hex(self.werg1.id)}) - url2 = reverse('workout_video_view',kwargs={'id':encoder.encode_hex(1)}) - - - yturl = 'https://youtu.be/6UzaWm1Ybrw' - - response = self.c.get(url) - - self.assertEqual(response.status_code,200) - - form_data = { - 'url': yturl, - 'delay': -119, - 'groups': ['basic'], - 'name': 'Video A', - 'save_button':'Save', - } - - response = self.c.post(url,form_data) - self.assertRedirects(response, - expected_url=url2, - status_code=302, - target_status_code=200) - - response = self.c.get(url2) - self.assertEqual(response.status_code,200) - - form_data = { - 'url': yturl, - 'delay': -119, - 'groups': ['basic'], - 'name': 'Video A', - 'save_button': 'Save', - } - - response = self.c.post(url2,form_data) - self.assertEqual(response.status_code,200) - - url = reverse('workout_video_view_mini',kwargs={'id':encoder.encode_hex(1)}) - response = self.c.get(url) - self.assertEqual(response.status_code,200) - - response = self.c.post(url,form_data) - self.assertEqual(response.status_code,200) - - url = reverse('workout_video_view_mini',kwargs={'id':encoder.encode_hex(1)}) - response = self.c2.get(url) - self.assertEqual(response.status_code,200) - - url = reverse('workout_video_view',kwargs={'id':encoder.encode_hex(1)}) - response = self.c2.get(url) - self.assertEqual(response.status_code,200) - - - @patch('rowers.dataprep.create_engine') - @patch('rowers.dataprep.getrowdata_db',side_effect=mocked_getrowdata_db) - @patch('rowers.dataprep.get_video_data',side_effect=mocked_videodata) - def test_video_selectworkout(self, mocked_sqlalchemy, mocked_getsmallrowdata_db, - mocked_videodata): - login = self.c.login(username=self.u.username, password=self.password) - self.assertTrue(login) - - url = reverse('video_selectworkout') - - response = self.c.get(url) - self.assertEqual(response.status_code,200) - - d1 = self.wwater.date-datetime.timedelta(days=24) - d2 = self.wwater.date+datetime.timedelta(days=24) - - - form_data = { - 'workout':'1', - 'startdate': d1.strftime("%Y-%m-%d"), - 'enddate': d2.strftime("%Y-%m-%d") - } - - url2 = reverse('workout_video_create_view',kwargs={'id':encoder.encode_hex(1)}) - - response = self.c.post(url,form_data) - self.assertRedirects(response, - expected_url=url2, - status_code=302, - target_status_code=200) + response = self.c.post(url,form_data) + self.assertRedirects(response, + expected_url=url2, + status_code=302, + target_status_code=200) - @patch('rowers.dataprep.create_engine') - @patch('rowers.dataprep.getsmallrowdata_db') - def test_editsummaryview(self, mocked_sqlalchemy, mocked_getsmallrowdata_db): - login = self.c.login(username=self.u.username, password=self.password) - self.assertTrue(login) + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getsmallrowdata_db') + def test_editsummaryview(self, mocked_sqlalchemy, mocked_getsmallrowdata_db): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + + url = reverse('workout_summary_edit_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + form_data = { + 'ruptures':'ruptures', + } + + response = self.c.post(url,form_data) + self.assertEqual(response.status_code,200) + + form_data = { + 'intervalstring':'4x2min/1min', + } + + form = SummaryStringForm(form_data) + self.assertTrue(form.is_valid()) + + response = self.c.post(url,form_data) + self.assertEqual(response.status_code,200) + + form_data = { + 'powerorpace':'power', + 'value_pace':'2:23', + 'value_power':'200', + 'value_work':'400', + 'value_spm':'20', + 'activeminutesmin':'0', + 'activeminutesmax':'60', + 'savepowerpaceform':True, + } + + response = self.c.post(url,form_data) + self.assertEqual(response.status_code,200) + + form_data = { + 'savestringform':'4x2min/1min', + } + + response = self.c.post(url,form_data) + self.assertEqual(response.status_code,200) + + form_data = { + 'selector':'power', + 'power': 200, + 'pace': '2:30', + 'work': 400, + 'activeminutesmin':'0', + 'activeminutesmax': '60', + } + + form = PowerIntervalUpdateForm(form_data) + self.assertTrue(form.is_valid()) + + response = self.c.post(url,form_data) + self.assertEqual(response.status_code,200) - url = reverse('workout_summary_edit_view',kwargs={'id':encoder.encode_hex(self.wwater.id)}) - - response = self.c.get(url) - self.assertEqual(response.status_code,200) - - form_data = { - 'ruptures':'ruptures', - } - - response = self.c.post(url,form_data) - self.assertEqual(response.status_code,200) - - form_data = { - 'intervalstring':'4x2min/1min', - } - - form = SummaryStringForm(form_data) - self.assertTrue(form.is_valid()) - - response = self.c.post(url,form_data) - self.assertEqual(response.status_code,200) - - form_data = { - 'powerorpace':'power', - 'value_pace':'2:23', - 'value_power':'200', - 'value_work':'400', - 'value_spm':'20', - 'activeminutesmin':'0', - 'activeminutesmax':'60', - 'savepowerpaceform':True, - } - - response = self.c.post(url,form_data) - self.assertEqual(response.status_code,200) - - form_data = { - 'savestringform':'4x2min/1min', - } - - response = self.c.post(url,form_data) - self.assertEqual(response.status_code,200) - - form_data = { - 'selector':'power', - 'power': 200, - 'pace': '2:30', - 'work': 400, - 'activeminutesmin':'0', - 'activeminutesmax': '60', - } - - form = PowerIntervalUpdateForm(form_data) - self.assertTrue(form.is_valid()) - - response = self.c.post(url,form_data) - self.assertEqual(response.status_code,200) - - - @patch('rowers.dataprep.create_engine') - @patch('rowers.dataprep.getsmallrowdata_db') - def test_workout_delete(self, mocked_sqlalchemy, mocked_getsmallrowdata_db): - login = self.c.login(username=self.u.username, password=self.password) - self.assertTrue(login) - - url = reverse('workout_delete',kwargs={'pk':self.wwater.id}) - - response = self.c.get(url) - self.assertEqual(response.status_code,200) - - post_data = {} - - response = self.c.post(url,follow=True) - - expected_url = reverse('workouts_view') - - self.assertRedirects(response, - expected_url=expected_url, - status_code=302,target_status_code=200) + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getsmallrowdata_db') + def test_workout_delete(self, mocked_sqlalchemy, mocked_getsmallrowdata_db): + login = self.c.login(username=self.u.username, password=self.password) + self.assertTrue(login) + + url = reverse('workout_delete',kwargs={'pk':self.wwater.id}) + + response = self.c.get(url) + self.assertEqual(response.status_code,200) + + post_data = {} + + response = self.c.post(url,follow=True) + + expected_url = reverse('workouts_view') + + self.assertRedirects(response, + expected_url=expected_url, + status_code=302,target_status_code=200) diff --git a/rowers/tests/test_urls.py b/rowers/tests/test_urls.py index 87da9b7d..c798445f 100644 --- a/rowers/tests/test_urls.py +++ b/rowers/tests/test_urls.py @@ -7,6 +7,7 @@ from __future__ import unicode_literals from .statements import * nu = datetime.datetime.now() +import rowers.courses as courses tested = [ @@ -27,6 +28,14 @@ class URLTests(TestCase): r = Rower.objects.create(user=u,rowerplan='coach',gdproptin=True, ftpset=True, gdproptindate=timezone.now()) self.c = Client() + cs = courses.kmltocourse('rowers/tests/testdata/thyro.kml') + course = cs[0] + cname = course['name'] + cnotes = course['description'] + polygons = course['polygons'] + self.ThyroBaantje = courses.createcourse(r,cname,polygons,notes=cnotes) + self.ThyroBaantje.save() + self.nu = datetime.datetime.now() filename = 'rowers/tests/testdata/testdata.csv' @@ -168,6 +177,19 @@ class URLTests(TestCase): '/rowers/workout/upload/team/', '/rowers/workouts-join/', '/rowers/workouts-join-select/', + '/rowers/api/courses/', + '/rowers/api/courses/?name=Brno', + '/rowers/api/courses/?course_distance=2000', + '/rowers/api/courses/?course_distance=aap', + '/rowers/api/courses/?distance_from=52&latitude=42&longitude=7', + '/rowers/api/courses/?distance_from=50&latitude=42&longitude=-aap', + '/rowers/api/courses/', + '/rowers/api/courses/1/', + '/rowers/list-workouts/?selectworkouts=true', + '/rowers/api/courses/kml/', + '/rowers/api/courses/kml/?id=1', + '/rowers/api/courses/kml?id=1&id=4/', + '/rowers/api/courses/kml/?id=aap', ] diff --git a/rowers/tests/testdata/testdata.tcx.gz b/rowers/tests/testdata/testdata.tcx.gz index 4d5ff824..f67f2da0 100644 Binary files a/rowers/tests/testdata/testdata.tcx.gz and b/rowers/tests/testdata/testdata.tcx.gz differ diff --git a/rowers/urls.py b/rowers/urls.py index 6824aa6f..30326a73 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -251,6 +251,9 @@ urlpatterns = [ name='strokedatajson_v3'), re_path(r'^api/TCX/workouts/$', views.strokedata_tcx, name='strokedata_tcx'), + re_path(r'^api/courses/$', views.course_list, name='course_list'), + re_path(r'^api/courses/(?P\d+)/$', views.get_crewnerd_kml, name='get_crewnerd_kml'), + re_path(r'^api/courses/kml/$', views.get_crewnerd_multiple, name='get_crewnerd_multiple'), re_path(r'^500v/$', views.error500_view, name='error500_view'), re_path(r'^500q/$', views.servererror_view, name='servererror_view'), path('502/', TemplateView.as_view(template_name='502.html'), name='502'), diff --git a/rowers/utils.py b/rowers/utils.py index 92357775..bf71456b 100644 --- a/rowers/utils.py +++ b/rowers/utils.py @@ -312,6 +312,31 @@ def geo_distance(lat1, lon1, lat2, lon2): return [distance, bearing] +def move_one_meter(latitude, longitude, bearing): + # Earth radius in meters + earth_radius = 6371000 + + # Convert latitude and longitude from degrees to radians + lat_rad = math.radians(latitude) + lon_rad = math.radians(longitude) + + # Convert bearing from degrees to radians + bearing_rad = math.radians(bearing) + + # Calculate the new latitude + new_lat = math.asin(math.sin(lat_rad) * math.cos(1/earth_radius) + + math.cos(lat_rad) * math.sin(1/earth_radius) * math.cos(bearing_rad)) + + # Calculate the new longitude + new_lon = lon_rad + math.atan2(math.sin(bearing_rad) * math.sin(1/earth_radius) * math.cos(lat_rad), + math.cos(1/earth_radius) - math.sin(lat_rad) * math.sin(new_lat)) + + # Convert new latitude and longitude from radians to degrees + new_lat = math.degrees(new_lat) + new_lon = math.degrees(new_lon) + + return new_lat, new_lon + def isbreakthrough(delta, cpvalues, p0, p1, p2, p3, ratio): pwr = abs(p0)/(1+(delta/abs(p2))) diff --git a/rowers/views/analysisviews.py b/rowers/views/analysisviews.py index 38b7ec34..403b37b4 100644 --- a/rowers/views/analysisviews.py +++ b/rowers/views/analysisviews.py @@ -2680,9 +2680,10 @@ def history_view(request, userid=0): last28 = timezone.now() - datetime.timedelta(days=28) today = timezone.now() + lastyear = today - datetime.timedelta(days=365) - lastyear = datetime.datetime( - year=today.year - 1, month=today.month, day=today.day) + #lastyear = datetime.datetime( + # year=today.year - 1, month=today.month, day=today.day) firstmay = datetime.datetime( year=today.year, month=5, day=1).astimezone(usertimezone) diff --git a/rowers/views/apiviews.py b/rowers/views/apiviews.py index 4b07edbc..c6f60c98 100644 --- a/rowers/views/apiviews.py +++ b/rowers/views/apiviews.py @@ -1,6 +1,7 @@ from rowers.views.statements import * from rowers.tasks import handle_calctrimp from rowers.opaque import encoder +from rowers.courses import coursetokml, coursestokml from xml.etree import ElementTree as ET import arrow @@ -11,6 +12,8 @@ from rowers.dataroutines import get_workouttype_from_tcx, get_startdate_time_zon from rest_framework.decorators import parser_classes from rest_framework.parsers import BaseParser +from rowers.utils import geo_distance + from datetime import datetime as dt import rowingdata.tcxtools as tcxtools @@ -24,7 +27,7 @@ class XMLParser(BaseParser): dologging("apilog.log", "XML Parser") try: s = ET.parse(stream).getroot() - except Exception as e: + except Exception as e: # pragma: no cover dologging("apilog.log",e) return HttpResponse(status=500) return s @@ -228,13 +231,123 @@ def strokedataform_v2(request, id=0): def part_of_day(hour): if 5 <= hour < 12: return "Morning" - elif 12 <= hour < 18: + elif 12 <= hour < 18: # pragma: no cover return "Afternoon" - elif 18 <= hour < 24: + elif 18 <= hour < 24: # pragma: no cover return "Evening" - else: + else: # pragma: no cover return "Night" +# KML API views +""" +- Get a list of courses OK + - Nearby a certain coordinate + - Filtered by +- Get a (KML) course (in response.content rather than as attachment) OK +- Get multiple courses as one KML in response.content OK + - GET with parameters? +Optional, not for CN +- Create one or more new courses from KML + - Should check for duplicates (Placemark ID) +- Update one or more new courses from KML +""" +@api_view(["GET"]) +@permission_classes([AllowAny]) +def course_list(request): + if request.method != 'GET': # pragma: no cover + dologging('apilog.log','{m} request to KML endpoint'.format(m=request.method)) + return HttpResponseNotAllowed("Method not supported") + + query_data = {} + name = request.GET.get('name') + distance = request.GET.get('course_distance') + latitude = request.GET.get('latitude') + longitude = request.GET.get('longitude') + distance_from = request.GET.get('distance_from') + if name is not None: + query_data['name__contains'] = name + if distance is not None: + try: + query_data['distance__lte'] = int(distance)+50 + query_data['distance__gte'] = int(distance)-50 + except: + pass + + + + courses = GeoCourse.objects.filter(**query_data) + if latitude is not None and longitude is not None and distance_from is not None: + try: + newlist = [] + for c in courses: + distance = geo_distance(float(latitude), float(longitude), c.coord[0], c.coord[1])[0] + if distance < float(distance_from): # pragma: no cover + newlist.append(c) + courses = newlist + except ValueError: + pass + + + courselist = [] + for c in courses: + d = { + 'id': c.id, + 'name':c.name, + 'country':c.country, + 'distance':c.distance, + 'notes':c.notes, + 'location':(c.coord[0], c.coord[1]), + } + courselist.append(d) + + response_dict = {'courses': courselist} + + return JsonResponse(response_dict, content_type='application/json; charset=utf8') + +@api_view(["GET"]) +@permission_classes([AllowAny]) +def get_crewnerd_kml(request,id=0): + if request.method != 'GET': # pragma: no cover + dologging('apilog.log','{m} request to CrewNerd KML endpoint'.format(m=request.method)) + return HttpResponseNotAllowed("Method not supported") + + try: + c = GeoCourse.objects.get(id=id) + except GeoCourse.DoesNotExist: # pragma: no cover + raise Http404("This course does not exist") + + kml = coursetokml(c, cn=True) + + return HttpResponse(kml) + +#@csrf_exempt +@api_view(["GET"]) +@permission_classes([AllowAny]) +def get_crewnerd_multiple(request): + if request.method != 'GET': # pragma: no cover + dologging('apilog.log','{m} request to CrewNerd KML endpoint'.format(m=request.method)) + return HttpResponseNotAllowed("Method not supported") + + ids = request.GET.get('id') + if ids is not None: + tdict = dict(request.GET.lists()) + idsnew = [] + for id in tdict['id']: + try: + idsnew.append(int(id)) + except ValueError: + pass + ids = idsnew + else: + gcs = GeoCourse.objects.all() + ids = [c.id for c in gcs] + + kml = coursestokml(ids, cn=True) + + return HttpResponse(kml) + +# Stroke data views + @csrf_exempt @login_required() @api_view(["POST"]) @@ -244,11 +357,11 @@ def strokedata_tcx(request): """ Upload a TCX file through API """ - if request.method != 'POST': + if request.method != 'POST': # pragma: no cover dologging('apilog.log','GET request to TCX endpoint') - return HttpResponseNotAllowed("Method not supported") # pragma: no cover + return HttpResponseNotAllowed("Method not supported") - if 'application/xml' not in request.content_type.lower(): + if 'application/xml' not in request.content_type.lower(): # pragma: no cover dologging('apilog.log','POST data not application/xml, request to TCX endpoint') dologging('apilog.log', request.content_type.lower()) return HttpResponseNotAllowed("Need application/xml") @@ -275,7 +388,7 @@ def strokedata_tcx(request): lap_duration_seconds = float(lap_duration_node.text) total_duration += lap_duration_seconds - except Exception as e: + except Exception as e: # pragma: no cover dologging('apilog.log','TCX') dologging('apilog.log',e) return HttpResponseNotAllowed("Could not parse TCX data") @@ -329,7 +442,7 @@ def strokedata_tcx(request): "workout id": workoutid, "status": "success", }) - except Exception as e: + except Exception as e: # pragma: no cover dologging('apilog.log','TCX API endpoint') dologging('apilog.log',e) return HttpResponse(status=500) @@ -382,7 +495,7 @@ def strokedatajson_v3(request): title = request.data.get('name','') try: elapsedTime = request.data['elapsedTime'] - except KeyError: + except KeyError: # pragma: no cover try: duration = request.data['duration'] try: @@ -394,7 +507,7 @@ def strokedatajson_v3(request): return HttpResponse("Missing Elapsed Time", status=400) try: totalDistance = request.data['distance'] - except KeyError: + except KeyError: # pragma: no cover return HttpResponse("Missing Total Distance", status=400) timeZone = request.data.get('timezone','UTC') workouttype = request.data.get('workouttype','rower') @@ -415,12 +528,12 @@ def strokedatajson_v3(request): df = pd.DataFrame() try: strokes = request.data['strokes'] - except KeyError: + except KeyError: # pragma: no cover return HttpResponse("No Stroke Data in JSON", status=400) try: df = pd.DataFrame(strokes['data']) - except KeyError: + except KeyError: # pragma: no cover try: df = pd.DataFrame(request.data['strokedata']) except: @@ -431,7 +544,7 @@ def strokedatajson_v3(request): status, comment, data = api_get_dataframe(startdatetime, df) - if status != 200: + if status != 200: # pragma: no cover return HttpResponse(comment, status=status) @@ -541,7 +654,7 @@ def strokedatajson_v2(request, id): try: for d in request.data['data']: dologging('apilog.log',json.dumps(d)) - except KeyError: + except KeyError: # pragma: no cover try: for d in request.data['strokedata']: dologging('apilog.log',json.dumps(d)) @@ -565,7 +678,7 @@ def strokedatajson_v2(request, id): df.sort_index(inplace=True) status, comment, data = api_get_dataframe(row.startdatetime, df) - if status != 200: + if status != 200: # pragma: no cover return HttpResponse(comment, status=status) r = getrower(request.user) diff --git a/rowers/views/racesviews.py b/rowers/views/racesviews.py index e3526de1..ac7b6852 100644 --- a/rowers/views/racesviews.py +++ b/rowers/views/racesviews.py @@ -7,7 +7,7 @@ from django.contrib.gis.geoip2 import GeoIP2 from django import forms from rowers.plannedsessions import timefield_to_seconds_duration -from rowers.courses import getnearestraces, getnearestcourses +from rowers.courses import getnearestraces, getnearestcourses,coursetokml, coursestokml # List Courses @@ -16,6 +16,25 @@ def courses_view(request): r = getrower(request.user) g = GeoIP2() + if request.method == 'POST': + try: + tdict = dict(request.POST.lists()) + ids = tdict['courseid'] + courseids = [int(id) for id in ids] + + kmlstring = coursestokml(courseids) + kmlfilename = 'courses.kml' + response = HttpResponse(kmlstring) + response['Content-Disposition'] = 'attachment; filename="{filename}"'.format( + filename=kmlfilename) + response['Content-Type'] = 'application/octet-stream' + + return response + + except KeyError: + pass + + ip = request.META.get('HTTP_X_REAL_IP', '1.1.1.1') try: lat_lon = g.lat_lon(ip) diff --git a/rowers/views/statements.py b/rowers/views/statements.py index 4c7706e3..80006ded 100644 --- a/rowers/views/statements.py +++ b/rowers/views/statements.py @@ -22,7 +22,7 @@ from scipy.special import lambertw from io import BytesIO import rowers.plots as plots from rowers.permissions import IsOwnerOrNot, IsCompetitorOrNot -from rest_framework.permissions import IsAuthenticated +from rest_framework.permissions import IsAuthenticated, AllowAny from rest_framework.decorators import api_view, renderer_classes, permission_classes from rest_framework.response import Response from rq.job import Job diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 7aa5b6f5..83fbcadd 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -1664,7 +1664,7 @@ def course_compare_view(request, id=0): labeldict = { int(w.id): w.__str__() for w in workouts } - except: + except: # pragma: no cover labeldict = {} res = interactive_multiple_compare_chart(workoutids, xparam, yparam, @@ -1846,7 +1846,7 @@ def virtualevent_compare_view(request, id=0): labeldict = { int(w.id): w.__str__() for w in workouts } - except: + except: # pragma: no cover labeldict = {} res = interactive_multiple_compare_chart(workoutids, xparam, yparam, @@ -2048,9 +2048,9 @@ def workouts_bulk_actions(request): w = get_workout_by_opaqueid(request, encid) if w.user == r: workouts.append(w) - else: + else: # pragma: no cover messages.error(request,'Bulk actions are not accessible to coaches') - except KeyError: + except KeyError: # pragma: no cover pass if request.method == 'POST': @@ -2058,7 +2058,7 @@ def workouts_bulk_actions(request): form = WorkoutMultipleCompareForm(request.POST) if form.is_valid() and actionform.is_valid(): workouts = form.cleaned_data['workouts'] - if len(workouts) == 0: + if len(workouts) == 0: # pragma: no cover url = reverse('workouts_view') return HttpResponseRedirect(url) action = actionform.cleaned_data['action'] @@ -2078,14 +2078,14 @@ def workouts_bulk_actions(request): 'Workout {id} exported to {destination}'.format( id=encoder.encode_hex(w.id), destination=destination)) - except NoTokenError: + except NoTokenError: # pragma: no cover messages.error(request, 'Export to {destination} of workout {id} failed'.format( id=encoder.encode_hex(w.id), destination=destination)) url = reverse('workouts_view') return HttpResponseRedirect(url) - else: + else: # pragma: no cover if len(workouts) == 0: url = reverse(workouts_view) return HttpResponseRedirect(url)