Merge branch 'release/v23.8.1'
This commit is contained in:
@@ -348,7 +348,6 @@ def step_to_garmin(step, order=0):
|
||||
def ps_to_garmin(ps, r):
|
||||
payload = {
|
||||
'workoutName': ps.name,
|
||||
'sport': r.garminactivity,
|
||||
'description': ps_dict_get_description(ps.steps),
|
||||
'estimatedDurationInSecs': 60*ps.approximate_duration,
|
||||
'estimatedDistanceInMeters': ps.approximate_distance,
|
||||
|
||||
@@ -27,9 +27,6 @@
|
||||
{% if rower.polartoken is not None and rower.polartoken != '' %}
|
||||
Polar,
|
||||
{% endif %}
|
||||
{% if rower.garmintoken is not None and rower.garmintoken != '' %}
|
||||
Garmin Connect,
|
||||
{% endif %}
|
||||
{% if rower.stravatoken is not None and rower.stravatoken != '' %}
|
||||
Strava,
|
||||
{% endif %}
|
||||
@@ -136,30 +133,6 @@
|
||||
<p><a href="/rowers/me/polarauthorize/"><img src="/static/img/Polar_connectwith_btn_white.png"
|
||||
alt="connect with Polar" width="130"></a></p>
|
||||
</li>
|
||||
<li class="rounder">
|
||||
<h2>Garmin Connect</h2>
|
||||
<table>
|
||||
{{ forms.garmin.as_table }}
|
||||
<input type="submit" value="Save">
|
||||
</table>
|
||||
<p><a href="/rowers/me/garminauthorize"><img src="/static/img/garmin_badge_130.png"
|
||||
alt="connect with Garmin" width="130"></a></p>
|
||||
|
||||
<p>
|
||||
Garmin Connnect has no manual sync, so connecting your account to your Garmin account will
|
||||
automatically auto-sync workouts from Garmin to Rowsandall (but not in the other direction). If you
|
||||
want to export our structured workout sessions to your Garmin device, you have to set the "Garmin Activity"
|
||||
to a activity type that is supported by your watch. Not all watches support "Custom" activities, so
|
||||
you may have to set your activity to Run or Ride while rowing.
|
||||
</p>
|
||||
{% if rower.garmintoken and rower.garmintoken != '' %}
|
||||
<p>
|
||||
<em>You are connected to Garmin.</em> Switching off Garmin Connect sync is on the
|
||||
<a href="https://connect.garmin.com/modern/settings/accountInformation">Account settings</a>
|
||||
page. Look for the "Rowsandall" app.
|
||||
</p>
|
||||
{% endif %}
|
||||
</li>
|
||||
<li class="rounder">
|
||||
<h2>Strava</h2>
|
||||
<p><em>Warning: API restrictions!</em></p>
|
||||
|
||||
@@ -23,7 +23,7 @@ import rowers.rojabo_stuff as rojabo_stuff
|
||||
from rowers.integrations import *
|
||||
|
||||
from django.db import transaction
|
||||
import rowers.garmin_stuff as gs
|
||||
|
||||
import rowers.integrations.strava as strava
|
||||
from rowers.nkimportutils import *
|
||||
|
||||
@@ -193,179 +193,6 @@ class RojaboObjects(DjangoTestCase):
|
||||
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(TESTING=True)
|
||||
class GarminObjects(DjangoTestCase):
|
||||
def setUp(self):
|
||||
self.c = Client()
|
||||
self.u = User.objects.create_user('john',
|
||||
'sander@ds.ds',
|
||||
'koeinsloot')
|
||||
|
||||
self.u.first_name = 'John'
|
||||
self.u.last_name = 'Sander'
|
||||
self.u.save()
|
||||
self.r = Rower.objects.create(user=self.u,gdproptin=True, ftpset=True,surveydone=True,
|
||||
gdproptindate=timezone.now()
|
||||
)
|
||||
self.r.garmintoken = 'dfdzf'
|
||||
self.r.garminrefreshtoken = 'fsls'
|
||||
self.r.rowerplan = 'pro'
|
||||
self.r.save()
|
||||
self.c.login(username='john',password='koeinsloot')
|
||||
|
||||
self.nu = datetime.datetime.now()
|
||||
|
||||
startdate = nu.date()
|
||||
enddate = (nu+datetime.timedelta(days=3)).date()
|
||||
preferreddate = startdate
|
||||
|
||||
self.ps_trimp = SessionFactory(
|
||||
startdate=startdate,enddate=enddate,
|
||||
sessiontype='test',
|
||||
sessionmode = 'TRIMP',
|
||||
criterium = 'none',
|
||||
sessionvalue = 77,
|
||||
sessionunit='none',
|
||||
preferreddate=preferreddate,
|
||||
manager=self.u,
|
||||
)
|
||||
|
||||
self.ps_trimp.interval_string = '10min+4x1000m@200W/20sec+2000m@24spm+10min'
|
||||
self.ps_trimp.save()
|
||||
|
||||
|
||||
def tearDown(self):
|
||||
ws = Workout.objects.filter(user=self.r)
|
||||
for w in ws:
|
||||
w.delete()
|
||||
|
||||
def test_garmin_push_summaries(self):
|
||||
|
||||
with open('rowers/tests/testdata/garminsummarydata.txt','r') as f:
|
||||
data = json.load(f)
|
||||
response = self.c.post('/rowers/garmin/summaries/',json.dumps(data),
|
||||
content_type="application/json")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
#response = self.c.get('/rowers/workout/'+encoded1+'/', follow=True)
|
||||
#self.assertEqual(response.status_code, 200)
|
||||
|
||||
ws = Workout.objects.filter(user=self.r)
|
||||
self.assertEqual(ws.count(),3)
|
||||
|
||||
def test_garmin_push_details3(self):
|
||||
with open('rowers/tests/testdata/garmindetail3.txt','r') as f:
|
||||
data = json.load(f)
|
||||
response = self.c.post('/rowers/garmin/activities/',json.dumps(data),
|
||||
content_type='application/json')
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
ws = Workout.objects.filter(user=self.r)
|
||||
self.assertEqual(ws.count(),1)
|
||||
|
||||
data,w = dataprep.getrowdata_db(id=ws[0].id)
|
||||
|
||||
self.assertEqual(len(data),515)
|
||||
|
||||
def test_garmin_push_details4(self):
|
||||
with open('rowers/tests/testdata/garmindetail4.txt','r') as f:
|
||||
data = json.load(f)
|
||||
response = self.c.post('/rowers/garmin/activities/',json.dumps(data),
|
||||
content_type='application/json')
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
ws = Workout.objects.filter(user=self.r)
|
||||
self.assertEqual(ws.count(),1)
|
||||
|
||||
data,w = dataprep.getrowdata_db(id=ws[0].id)
|
||||
|
||||
self.assertEqual(len(data),18)
|
||||
|
||||
def test_garmin_push_details2(self):
|
||||
with open('rowers/tests/testdata/garmindetail2.txt','r') as f:
|
||||
data = json.load(f)
|
||||
response = self.c.post('/rowers/garmin/activities/',json.dumps(data),
|
||||
content_type='application/json')
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
ws = Workout.objects.filter(user=self.r)
|
||||
self.assertEqual(ws.count(),3)
|
||||
|
||||
data,w = dataprep.getrowdata_db(id=ws[0].id)
|
||||
self.assertEqual(len(data),451)
|
||||
|
||||
def test_garmin_push_details1(self):
|
||||
with open('rowers/tests/testdata/garmindetail1.txt','r') as f:
|
||||
data = json.load(f)
|
||||
response = self.c.post('/rowers/garmin/activities/',json.dumps(data),
|
||||
content_type='application/json')
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
response = self.c.get('/rowers/workout/'+encoded1+'/', follow=True)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
ws = Workout.objects.filter(user=self.r)
|
||||
self.assertEqual(ws.count(),2)
|
||||
|
||||
data,w = dataprep.getrowdata_db(id=ws[0].id)
|
||||
self.assertEqual(len(data),2)
|
||||
|
||||
def test_garmin_deregistration(self):
|
||||
data = {"deregistrations":[{"userAccessToken":"dfdzf"}]}
|
||||
response = self.c.post('/rowers/garmin/deregistration/',json.dumps(data),
|
||||
content_type='application/json')
|
||||
|
||||
self.assertEqual(response.status_code,200)
|
||||
|
||||
@patch('rowers.tasks.OAuth1Session',side_effect=mocked_requests)
|
||||
@patch('rowers.tasks.requests.session', side_effect=mocked_requests)
|
||||
def test_handle_get_garmin_file(self, MockSession, MockOAuth1Session):
|
||||
client_id = 'garmin'
|
||||
client_secret = 'noot'
|
||||
garmintoken = 'mies'
|
||||
garminrefreshtoken = 'jet'
|
||||
userid = self.r.user.id
|
||||
url = 'fake_url'
|
||||
filetype = 'fit'
|
||||
|
||||
res = tasks.handle_get_garmin_file(
|
||||
client_id,client_secret,garmintoken,garminrefreshtoken,userid,url,filetype
|
||||
)
|
||||
|
||||
self.assertEqual(res,1)
|
||||
|
||||
@patch('rowers.garmin_stuff.OAuth1Session')
|
||||
def notest_garmin_callback(self,MockOAuth1Session):
|
||||
with transaction.atomic():
|
||||
response = self.c.get('/garmin_callback/?oauth_token=528ea5d9-1163-434d-b172-f428c5d9f522&oauth_verifier=LW33ZMBP8H')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
@patch('rowers.garmin_stuff.requests.get',side_effect=mocked_requests)
|
||||
def test_garmin_can_export_session(self,mock_get):
|
||||
result = gs.garmin_can_export_session(self.u)
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_ps_to_garmin(self):
|
||||
res = gs.ps_to_garmin(self.ps_trimp,self.r)
|
||||
self.assertTrue(len(json.dumps(res))>500)
|
||||
|
||||
@patch('rowers.garmin_stuff.requests.get',side_effect=mocked_requests)
|
||||
@patch('rowers.garmin_stuff.requests.post',side_effect=mocked_requests)
|
||||
def test_garmin_session_create(self,mock_get,mock_post):
|
||||
res = gs.garmin_session_create(self.ps_trimp,self.u)
|
||||
self.assertEqual(res,1212)
|
||||
|
||||
@patch('rowers.garmin_stuff.requests.get',side_effect=mocked_requests)
|
||||
@patch('rowers.garmin_stuff.requests.post',side_effect=mocked_requests)
|
||||
def test_togarmin_view(self,mock_get,mock_post):
|
||||
url = reverse('plannedsession_togarmin_view',kwargs={'id':self.ps_trimp.id})
|
||||
response = self.c.get(url,follow=True)
|
||||
|
||||
self.assertEqual(response.status_code,200)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
|
||||
@@ -2134,8 +2134,6 @@ description: ""
|
||||
stepsdict = self.ps_trimp.steps['steps']
|
||||
self.assertEqual(len(stepsdict),2)
|
||||
|
||||
response = garmin_stuff.ps_to_garmin(self.ps_trimp,self.r)
|
||||
self.assertTrue(len(json.dumps(response))>800)
|
||||
|
||||
url = '0'
|
||||
request = self.factory.get(url)
|
||||
|
||||
BIN
rowers/tests/testdata/testdata.tcx.gz
vendored
BIN
rowers/tests/testdata/testdata.tcx.gz
vendored
Binary file not shown.
@@ -763,8 +763,8 @@ urlpatterns = [
|
||||
name='rower_polar_authorize'),
|
||||
re_path(r'^me/revokeapp/(?P<id>\d+)/$',
|
||||
views.rower_revokeapp_view, name='rower_revokeapp_view'),
|
||||
re_path(r'^me/garminauthorize/$', views.rower_garmin_authorize,
|
||||
name='rower_garmin_authorize'),
|
||||
# re_path(r'^me/garminauthorize/$', views.rower_garmin_authorize,
|
||||
# name='rower_garmin_authorize'),
|
||||
re_path(r'^me/edit/(.+.*)/$', views.rower_edit_view, name='rower_edit_view'),
|
||||
re_path(r'^me/(?P<source>\w+.*)authorize', views.rower_integration_authorize,
|
||||
name='rower_integration_authorize'),
|
||||
@@ -1002,8 +1002,8 @@ urlpatterns = [
|
||||
name='plannedsession_templateedit_view'),
|
||||
re_path(r'^sessions/(?P<id>\d+)/maketemplate/$', views.plannedsession_totemplate_view,
|
||||
name='plannedsession_totemplate_view'),
|
||||
re_path(r'^sessions/(?P<id>\d+)/togarmin/$', views.plannedsession_togarmin_view,
|
||||
name='plannedsession_togarmin_view'),
|
||||
# re_path(r'^sessions/(?P<id>\d+)/togarmin/$', views.plannedsession_togarmin_view,
|
||||
# name='plannedsession_togarmin_view'),
|
||||
re_path(r'^sessions/(?P<id>\d+)/tointervals/$', views.plannedsession_tointervals_view,
|
||||
name='plannedsession_tointervals_view'),
|
||||
re_path(r'^sessions/(?P<id>\d+)/compare/$',
|
||||
|
||||
@@ -26,7 +26,6 @@ importauthorizeviews = {
|
||||
'trainingpeaks': 'rower_integration_authorize',
|
||||
'nk': 'rower_integration_authorize',
|
||||
'rp3': 'rower_integration_authorize',
|
||||
'garmin': 'rower_garmin_authorize',
|
||||
'intervals': 'rower_integration_authorize',
|
||||
}
|
||||
|
||||
@@ -85,8 +84,6 @@ def rower_integration_authorize(request, source='c2'): # pragma: no cover
|
||||
try:
|
||||
integration = importsources[source](request.user)
|
||||
except KeyError:
|
||||
if source == 'garmin':
|
||||
return rower_garmin_authorize(request)
|
||||
if source == 'rojabo':
|
||||
return rower_rojabo_authorize(request)
|
||||
if source == 'polar':
|
||||
@@ -96,12 +93,6 @@ def rower_integration_authorize(request, source='c2'): # pragma: no cover
|
||||
|
||||
|
||||
|
||||
@login_required()
|
||||
def rower_garmin_authorize(request): # pragma: no cover
|
||||
authorization_url, token, secret = garmin_stuff.garmin_authorize()
|
||||
request.session['garmin_owner_key'] = token
|
||||
request.session['garmin_owner_secret'] = secret
|
||||
return HttpResponseRedirect(authorization_url)
|
||||
|
||||
|
||||
# Polar Authorization
|
||||
@@ -288,27 +279,6 @@ def rower_process_polarcallback(request):
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
|
||||
# process Garmin callback
|
||||
@login_required()
|
||||
def rower_process_garmincallback(request): # pragma: no cover
|
||||
r = getrower(request.user)
|
||||
absoluteurl = request.build_absolute_uri()
|
||||
|
||||
try:
|
||||
key = request.session['garmin_owner_key']
|
||||
secret = request.session['garmin_owner_secret']
|
||||
except KeyError:
|
||||
authorization_url, key, secret = garmin_stuff.garmin_authorize()
|
||||
garmintoken, garminrefreshtoken = garmin_stuff.garmin_processcallback(
|
||||
absoluteurl, key, secret)
|
||||
r.garmintoken = garmintoken
|
||||
r.garminrefreshtoken = garminrefreshtoken
|
||||
r.save()
|
||||
|
||||
successmessage = "Tokens stored. Good to go. Please check your import/export settings"
|
||||
messages.info(request, successmessage)
|
||||
url = reverse('rower_exportsettings_view')
|
||||
return HttpResponseRedirect(url)
|
||||
|
||||
# Process Rojabo callback
|
||||
@login_required()
|
||||
@@ -1108,81 +1078,6 @@ def strava_webhook_view(request):
|
||||
# For push notifications from Garmin
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
def garmin_summaries_view(request): # pragma: no cover
|
||||
if request.method != 'POST':
|
||||
return HttpResponse(status=200)
|
||||
|
||||
t = time.localtime()
|
||||
timestamp = time.strftime('%b-%d-%Y_%H%M', t)
|
||||
dologging("garminlog.log",request.body)
|
||||
# POST request
|
||||
data = json.loads(request.body)
|
||||
activities = data['activities']
|
||||
result = garmin_stuff.garmin_workouts_from_summaries(activities)
|
||||
|
||||
if result:
|
||||
return HttpResponse(status=200)
|
||||
|
||||
return HttpResponse(status=200)
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
def garmin_newfiles_ping(request): # pragma: no cover
|
||||
if request.method != 'POST':
|
||||
return HttpResponse(status=200)
|
||||
|
||||
data = json.loads(request.body)
|
||||
for file in data['activityFiles']:
|
||||
try:
|
||||
garmintoken = file['userAccessToken']
|
||||
try:
|
||||
r = Rower.objects.get(garmintoken=garmintoken)
|
||||
callbackURL = file['callbackURL']
|
||||
starttime = file['startTimeInSeconds']
|
||||
fileType = file['fileType']
|
||||
_ = garmin_stuff.get_garmin_file(r, callbackURL, starttime, fileType)
|
||||
except Rower.DoesNotExist:
|
||||
pass
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return HttpResponse(status=200) # pragma: no cover
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
def garmin_deregistration_view(request):
|
||||
if request.method != 'POST': # pragma: no cover
|
||||
return HttpResponse(status=200)
|
||||
|
||||
data = json.loads(request.body)
|
||||
deregistrations = data['deregistrations']
|
||||
for deregistration in deregistrations:
|
||||
try:
|
||||
garmintoken = deregistration['userAccessToken']
|
||||
try:
|
||||
r = Rower.objects.get(garmintoken=garmintoken)
|
||||
r.garmintoken = ''
|
||||
r.save()
|
||||
except Rower.DoesNotExist: # pragma: no cover
|
||||
pass
|
||||
except KeyError: # pragma: no cover
|
||||
pass
|
||||
|
||||
return HttpResponse(status=200)
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
def garmin_details_view(request):
|
||||
if request.method != 'POST': # pragma: no cover
|
||||
return HttpResponse(status=200)
|
||||
|
||||
# POST request
|
||||
data = json.loads(request.body)
|
||||
_ = garmin_stuff.garmin_workouts_from_details(data)
|
||||
|
||||
return HttpResponse(status=200)
|
||||
|
||||
|
||||
|
||||
@login_required()
|
||||
|
||||
@@ -490,7 +490,6 @@ def rower_exportsettings_view(request, userid=0):
|
||||
'rp3': RowerExportFormRP3(instance=r),
|
||||
'intervals': RowerExportFormIntervals(instance=r),
|
||||
'nk': RowerExportFormNK(instance=r),
|
||||
'garmin': RowerExportFormGarmin(instance=r),
|
||||
'imports_are_private': RowerPrivateImportForm(instance=r)
|
||||
}
|
||||
|
||||
@@ -505,7 +504,6 @@ def rower_exportsettings_view(request, userid=0):
|
||||
'rp3': RowerExportFormRP3(request.POST, instance=r),
|
||||
'intervals': RowerExportFormIntervals(request.POST, instance=r),
|
||||
'nk': RowerExportFormNK(request.POST, instance=r),
|
||||
'garmin': RowerExportFormGarmin(request.POST, instance=r),
|
||||
'imports_are_private': RowerPrivateImportForm(request.POST, instance=r),
|
||||
}
|
||||
if form.is_valid():
|
||||
|
||||
@@ -87,7 +87,7 @@ urlpatterns += [
|
||||
re_path(r'^nk\_callback', rowersviews.rower_process_nkcallback),
|
||||
re_path(r'^rojabo\_callback', rowersviews.rower_process_rojabocallback),
|
||||
re_path(r'^stravacall\_back', rowersviews.rower_process_stravacallback),
|
||||
re_path(r'^garmin\_callback', rowersviews.rower_process_garmincallback),
|
||||
# re_path(r'^garmin\_callback', rowersviews.rower_process_garmincallback),
|
||||
re_path(r'^sporttracks\_callback', rowersviews.rower_process_sporttrackscallback),
|
||||
re_path(r'^polarflowcallback', rowersviews.rower_process_polarcallback),
|
||||
re_path(r'^tp\_callback', rowersviews.rower_process_tpcallback),
|
||||
|
||||
Reference in New Issue
Block a user