Merge branch 'feature/emailconfirm' into develop
This commit is contained in:
142
rowers/nkstuff.py
Normal file
142
rowers/nkstuff.py
Normal file
@@ -0,0 +1,142 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
import time
|
||||
from time import strftime
|
||||
|
||||
import requests
|
||||
|
||||
#https://oauth-stage.nkrowlink.com/oauth/authorizegrant_type=authorization_code&response_type=code&client_id=rowsandall-staging&scope=read&state=fc8fc3d8-ce0a-443e-838a-1c06fb5317c6&redirect_uri=https%3A%2F%2Fdunav.ngrok.io%2Fnk_callback%2F
|
||||
#https://oauth-stage.nkrowlink.com/oauth/authorize?grant_type=authorization_code&response_type=code&client_id=rowsandall-staging&scope=read&state=1234&redirect_uri=https%3A%2F%2Fdev.rowsandall.com%2Fnk_callback
|
||||
|
||||
from requests_oauthlib import OAuth2Session
|
||||
|
||||
import django_rq
|
||||
queue = django_rq.get_queue('default')
|
||||
queuelow = django_rq.get_queue('low')
|
||||
queuehigh = django_rq.get_queue('low')
|
||||
|
||||
from rowers.rower_rules import is_workout_user, ispromember
|
||||
|
||||
from iso8601 import ParseError
|
||||
from rowers.utils import myqueue
|
||||
|
||||
import rowers.mytypes as mytypes
|
||||
import gzip
|
||||
|
||||
from rowsandall_app.settings import (
|
||||
NK_CLIENT_ID, NK_REDIRECT_URI, NK_CLIENT_SECRET,
|
||||
SITE_URL, NK_API_LOCATION
|
||||
)
|
||||
|
||||
try:
|
||||
from json.decoder import JSONDecodeError
|
||||
except ImportError:
|
||||
JSONDecodeError = ValueError
|
||||
|
||||
from rowers.imports import *
|
||||
|
||||
oauth_data = {
|
||||
'client_id': NK_CLIENT_ID,
|
||||
'client_secret': NK_CLIENT_SECRET,
|
||||
'redirect_uri': NK_REDIRECT_URI,
|
||||
'autorization_uri': "https://oauth-stage.nkrowlink.com/oauth/authorize",
|
||||
'content_type': 'application/json',
|
||||
'tokenname': 'nktoken',
|
||||
'refreshtokenname': 'nkrefreshtoken',
|
||||
'expirydatename': 'nktokenexpirydate',
|
||||
'bearer_auth': True,
|
||||
'base_url': "https://oauth-stage.nkrowlink.com/oauth/token",
|
||||
'scope':'read',
|
||||
}
|
||||
|
||||
def get_token(code):
|
||||
#client_id = oauth_data['client_id']
|
||||
#client_secret = oauth_data['client_secret']
|
||||
#base_uri = oauth_data['base_url']
|
||||
|
||||
#callbackuri = 'https://'+callbackuri[9:]
|
||||
#print(callbackuri)
|
||||
|
||||
#nk = OAuth2Session(client_id)
|
||||
#token = nk.fetch_token(base_uri,client_secret=client_secret,authorization_response=callbackuri)
|
||||
|
||||
#print(token)
|
||||
#return [0,0,0]
|
||||
return imports_get_token(code, oauth_data)
|
||||
|
||||
def nk_open(user):
|
||||
t = time.localtime()
|
||||
timestamp = time.strftime('%b-%d-%Y_%H%M', t)
|
||||
token = imports_open(user,oauth_data)
|
||||
return token
|
||||
|
||||
def do_refresh_token(refreshtoken):
|
||||
return imports_do_refresh_token(refreshtoken, oauth_data)
|
||||
|
||||
def rower_nk_token_refresh(user):
|
||||
r = Rower.objects.get(user=user)
|
||||
res = do_refresh_token(r.nkrefreshtoken)
|
||||
access_token = res[0]
|
||||
expires_in = res[1]
|
||||
refresh_token = res[2]
|
||||
expirydatetime = timezone.now()+timedelta(seconds=expires_in)
|
||||
|
||||
r.nktoken = access_token
|
||||
r.nktokenexpirydate = expirydatetime
|
||||
r.nkrefreshtoken = refresh_token
|
||||
r.save()
|
||||
|
||||
return r.nktoken
|
||||
|
||||
def make_authorization_url(request):
|
||||
return imports_make_authorization_url(oauth_data)
|
||||
|
||||
def get_nk_workout_list(user,fake=False):
|
||||
r = Rower.objects.get(user=user)
|
||||
|
||||
if (r.nktoken == '') or (r.nktoken is None):
|
||||
s = "Token doesn't exist. Need to authorize"
|
||||
return custom_exception_handler(401,s)
|
||||
elif (r.nktokenexpirydate is None or timezone.now()+timedelta(seconds=3599)>r.nktokenexpirydate):
|
||||
s = "Token expired. Needs to refresh."
|
||||
return custom_exception_handler(401,s)
|
||||
else:
|
||||
# ready to fetch. Hurray
|
||||
authorizationstring = str('Bearer ' + r.nktoken)
|
||||
headers = {'Authorization': authorizationstring,
|
||||
'user-agent': 'sanderroosendaal',
|
||||
'Content-Type': 'application/json'}
|
||||
|
||||
url = NK_API_LOCATION+"api/v1/sessions"
|
||||
|
||||
params = {} # start / end time
|
||||
|
||||
s = requests.get(url,headers=headers,params=params)
|
||||
|
||||
return s
|
||||
|
||||
def get_nk_workout_strokes(user,nkid):
|
||||
r = Rower.objects.get(user=user)
|
||||
|
||||
if (r.nktoken == '') or (r.nktoken is None):
|
||||
return custom_exception_handler(401,s)
|
||||
s = "Token doesn't exist. Need to authorize"
|
||||
elif (timezone.now()>r.tokenexpirydate):
|
||||
s = "Token expired. Needs to refresh."
|
||||
return custom_exception_handler(401,s)
|
||||
else:
|
||||
# ready to fetch. Hurray
|
||||
authorizationstring = str('Bearer ' + r.nktoken)
|
||||
headers = {'Authorization': authorizationstring,
|
||||
'user-agent': 'sanderroosendaal',
|
||||
'Content-Type': 'application/json'}
|
||||
url = "https://log.concept2.com/api/users/me/results/"+str(nkid)+"/strokes"
|
||||
s = requests.get(url,headers=headers)
|
||||
|
||||
return s
|
||||
#
|
||||
#def get_workout(user,nkid):
|
||||
7
rowers/templates/acc_activate_email.html
Normal file
7
rowers/templates/acc_activate_email.html
Normal file
@@ -0,0 +1,7 @@
|
||||
{% autoescape off %}
|
||||
Hi {{ user.username }},
|
||||
Please click on the link to confirm your registration,
|
||||
|
||||
http://{{ domain }}{% url 'useractivate' uidb64=uid token=token %}
|
||||
If you think, it's not you, then just ignore this email.
|
||||
{% endautoescape %}
|
||||
58
rowers/templates/nk_list_import.html
Normal file
58
rowers/templates/nk_list_import.html
Normal file
@@ -0,0 +1,58 @@
|
||||
{% extends "newbase.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load rowerfilters %}
|
||||
|
||||
{% block title %}Workouts{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<h1>Available on nk</h1>
|
||||
{% if workouts %}
|
||||
<ul class="main-content">
|
||||
<li>
|
||||
<a href="/rowers/workout/nkimport/all/" class="blue button">Import all NEW</a>
|
||||
</li>
|
||||
<li class="grid_3">
|
||||
<p>This imports all workouts that have not been imported to rowsandall.com.
|
||||
The action may take a longer time to process, so please be patient. Click on Import in the list below to import an individual workout.
|
||||
</p>
|
||||
</li>
|
||||
|
||||
<li class="grid_4">
|
||||
<table width="70%" class="listtable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th> Import </th>
|
||||
<th> Name</th>
|
||||
<th> Date</th>
|
||||
<th> Duration </th>
|
||||
<th> Distance </th>
|
||||
<th> New</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for workout in workouts %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/rowers/workout/nkimport/{{ workout|lookup:'id' }}/">Import</a></td>
|
||||
<td>{{ workout|lookup:'name' }}</td>
|
||||
<td>{{ workout|lookup:'starttime' }}</td>
|
||||
<td>{{ workout|lookup:'duration' }} </td>
|
||||
<td>{{ workout|lookup:'distance' }} m</td>
|
||||
<td>{{ workout|lookup:'new' }}</td>
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</li>
|
||||
</ul>
|
||||
{% else %}
|
||||
<p>
|
||||
No workouts found
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block sidebar %}
|
||||
{% include 'menu_workouts.html' %}
|
||||
{% endblock %}
|
||||
@@ -42,20 +42,38 @@ class NewUserRegistrationTest(TestCase):
|
||||
self.assertTrue(form.is_valid())
|
||||
|
||||
response = self.c.post('/rowers/register/', form_data, follow=True)
|
||||
self.assertEqual(response.status_code,200)
|
||||
|
||||
self.assertRedirects(response,
|
||||
expected_url='/rowers/me/gdpr-optin/?next=/rowers/list-workouts/',
|
||||
status_code=302,target_status_code=200)
|
||||
# set opt-in
|
||||
user = User.objects.get(username='janderoeiert')
|
||||
user.rower.gdpr_optin = True
|
||||
user.set_password('aapindewei2')
|
||||
user.is_active = True
|
||||
user.save()
|
||||
user.rower.save()
|
||||
|
||||
login = self.c.login(username=user.username,password='aapindewei2')
|
||||
self.assertTrue(login)
|
||||
|
||||
|
||||
url = '/rowers/me/gdpr-optin-confirm/?next=/rowers/list-workouts/'
|
||||
url = '/rowers/list-workouts/'
|
||||
|
||||
response = self.c.get(url)
|
||||
|
||||
expected = '/rowers/me/gdpr-optin/?next=/rowers/list-workouts/'
|
||||
|
||||
self.assertRedirects(response,
|
||||
expected_url='/rowers/list-workouts/',
|
||||
status_code=302,target_status_code=200,
|
||||
)
|
||||
expected_url=expected,
|
||||
status_code=302,target_status_code=200)
|
||||
|
||||
url = '/rowers/me/gdpr-optin-confirm/?next=/rowers/list-workouts/'
|
||||
response = self.c.get(url)
|
||||
|
||||
expected = '/rowers/list-workouts/'
|
||||
self.assertRedirects(response,
|
||||
expected_url=expected,
|
||||
status_code=302,target_status_code=200)
|
||||
|
||||
|
||||
url = '/rowers/exportallworkouts/'
|
||||
|
||||
@@ -74,6 +92,8 @@ class NewUserRegistrationTest(TestCase):
|
||||
|
||||
self.assertTrue(response.status_code,200)
|
||||
|
||||
|
||||
|
||||
url = '/rowers/me/delete/'
|
||||
|
||||
form_data = {
|
||||
|
||||
84
rowers/tests/testdata/nkworkouts.txt
vendored
Normal file
84
rowers/tests/testdata/nkworkouts.txt
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "JustGo-7189M",
|
||||
"type": 0,
|
||||
"speedInput": 0,
|
||||
"startTime": 1614264826,
|
||||
"endTime": 1614269826,
|
||||
"location": "Some Location",
|
||||
"deviceId": 11,
|
||||
"elapsedTime": 1140000,
|
||||
"totalDistanceImp": 2920.69,
|
||||
"totalDistanceGps": 2286.5,
|
||||
"avgPaceImp": 195159.36302723,
|
||||
"avgPaceGps": 250466.812537141,
|
||||
"avgStrokeRate": 19.5,
|
||||
"distStrokeImp": 8.51,
|
||||
"distStrokeGps": 6.77,
|
||||
"avgHeartRate": 158,
|
||||
"totalStrokeCount": 343,
|
||||
"totalCalories": 4959900,
|
||||
"avgCalHour": 4482062.35252774,
|
||||
"avgSpeedGps": 2,
|
||||
"avgSpeedImp": 2.56,
|
||||
"avgPower": 68.6979270660324,
|
||||
"avgCatch": -46.7961491475831,
|
||||
"avgSlip": 16.1616225246003,
|
||||
"avgFinish": 48.1512290049444,
|
||||
"avgWash": 22.9514686031976,
|
||||
"avgForceAvg": 114.502194258813,
|
||||
"avgWork": 210.785493336831,
|
||||
"avgForceMax": 249.481436977143,
|
||||
"avgMaxForceAngle": 4.75378911974861,
|
||||
"startGpsLat": 39.7356346,
|
||||
"startGpsLon": -75.5581928,
|
||||
"intervals": [
|
||||
{
|
||||
"id": 1,
|
||||
"sessionId": 1,
|
||||
"startTime": 1614264826,
|
||||
"intervalNumber": 1,
|
||||
"sessionStrokeStartIndex": 473,
|
||||
"sessionStrokeEndIndex": 674,
|
||||
"sessionStrokeCount": 91,
|
||||
"elapsedTime": 1140000,
|
||||
"totalDistanceImp": 2920.69,
|
||||
"totalDistanceGps": 2286.5,
|
||||
"avgPaceImp": 195159.36302723,
|
||||
"avgPaceGps": 250466.812537141,
|
||||
"avgStrokeRate": 19.5,
|
||||
"distStrokeImp": 8.51,
|
||||
"distStrokeGps": 6.77,
|
||||
"avgHeartRate": 158,
|
||||
"totalStrokeCount": 343,
|
||||
"totalCalories": 4959900,
|
||||
"avgCalHour": 4482062.35252774,
|
||||
"avgSpeedGps": 2,
|
||||
"avgSpeedImp": 2.56,
|
||||
"avgPower": 68.6979270660324,
|
||||
"avgCatch": -46.7961491475831,
|
||||
"avgSlip": 16.1616225246003,
|
||||
"avgFinish": 48.1512290049444,
|
||||
"avgWash": 22.9514686031976,
|
||||
"avgForceAvg": 114.502194258813,
|
||||
"avgWork": 210.785493336831,
|
||||
"avgForceMax": 249.481436977143,
|
||||
"avgMaxForceAngle": 4.75378911974861,
|
||||
"startGpsLat": 39.7356346,
|
||||
"startGpsLon": -75.5581928
|
||||
}
|
||||
],
|
||||
"oarlockSessions": [
|
||||
{
|
||||
"id": 1,
|
||||
"sessionId": 1,
|
||||
"boatName": "Fast Boat",
|
||||
"seatNumber": 3,
|
||||
"portStarboard": 0,
|
||||
"oarLength": 284,
|
||||
"oarInboardLength": 85
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
12
rowers/tokens.py
Normal file
12
rowers/tokens.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from django.contrib.auth.tokens import PasswordResetTokenGenerator
|
||||
from django.utils import six
|
||||
|
||||
|
||||
class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
|
||||
def _make_hash_value(self, user, timestamp):
|
||||
return (
|
||||
six.text_type(user.pk) + six.text_type(timestamp) +
|
||||
six.text_type(user.is_active)
|
||||
)
|
||||
|
||||
account_activation_token = AccountActivationTokenGenerator()
|
||||
@@ -729,6 +729,7 @@ urlpatterns = [
|
||||
re_path(r'^legal', TemplateView.as_view(template_name='legal.html'),name='legal'),
|
||||
re_path(r'^register/$',views.rower_register_view,name='rower_register_view'),
|
||||
re_path(r'^coachregister/$',views.freecoach_register_view,name='freecoach_register_view'),
|
||||
path('activate/<uidb64>/<token>/',views.useractivate, name='useractivate'),
|
||||
re_path(r'^register/thankyou/$', TemplateView.as_view(template_name='registerthankyou.html'), name='registerthankyou'),
|
||||
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/workflow/$',views.workout_workflow_view,
|
||||
name='workout_workflow_view'),
|
||||
|
||||
@@ -4,6 +4,7 @@ from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from rowers.views.statements import *
|
||||
from django.core.mail import EmailMessage
|
||||
|
||||
@csrf_exempt
|
||||
def braintree_webhook_view(request):
|
||||
@@ -711,6 +712,59 @@ def downgrade_completed_view(request):
|
||||
'rower':r
|
||||
})
|
||||
|
||||
from django.utils.encoding import force_bytes, force_text
|
||||
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
|
||||
from django.contrib.sites.shortcuts import get_current_site
|
||||
from rowers.tokens import account_activation_token
|
||||
# Email activation
|
||||
def useractivate(request, uidb64, token):
|
||||
try:
|
||||
uid = force_text(urlsafe_base64_decode(uidb64))
|
||||
user = User.objects.get(id=uid)
|
||||
except(TypeError, ValueError, OverflowError, User.DoesNotExist):
|
||||
user = None
|
||||
if user is not None and account_activation_token.check_token(user, token):
|
||||
user.is_active = True
|
||||
user.save()
|
||||
# below is old
|
||||
fullemail = user.first_name + " " + user.last_name + " " + "<" + user.email + ">"
|
||||
subject = "Thank you for registering on rowsandall.com"
|
||||
from_address = 'Sander Roosendaal <info@rowsandall.com>'
|
||||
|
||||
d = {'first_name':user.first_name}
|
||||
|
||||
template = 'registeremail.html'
|
||||
if user.rower.rowerplan == 'freecoach':
|
||||
template = 'coachregisteremail.html'
|
||||
|
||||
send_template_email(from_address,[fullemail],
|
||||
subject,'registeremail.html',d)
|
||||
|
||||
|
||||
subject2 = "New User"
|
||||
message2 = "New user registered.\n"
|
||||
message2 += fullemail + "\n"
|
||||
message2 += "User name: "+user.username
|
||||
|
||||
if user.rower.rowerplan == 'freecoach':
|
||||
subject2 = "New Free Coach User"
|
||||
|
||||
|
||||
send_mail(subject2, message2,
|
||||
'Rowsandall Server <info@rowsandall.com>',
|
||||
['roosendaalsander@gmail.com'])
|
||||
|
||||
|
||||
messages.info(request,'Thank you for your email confirmation. Now you can login to your account.')
|
||||
url = '/login/'
|
||||
if user.rower.rowerplan == 'freecoach':
|
||||
url+='?next=/rowers/me/teams'
|
||||
return HttpResponseRedirect(url)
|
||||
else:
|
||||
return HttpResponse('Activation link is invalid!')
|
||||
|
||||
|
||||
|
||||
# User registration
|
||||
def rower_register_view(request):
|
||||
|
||||
@@ -736,6 +790,7 @@ def rower_register_view(request):
|
||||
theuser.first_name = first_name
|
||||
theuser.last_name = last_name
|
||||
theuser.email = email
|
||||
theuser.is_active = False
|
||||
theuser.save()
|
||||
|
||||
birthdate = birthdate.replace(tzinfo=None)
|
||||
@@ -766,26 +821,29 @@ def rower_register_view(request):
|
||||
w.save()
|
||||
|
||||
# Create and send email
|
||||
fullemail = first_name + " " + last_name + " " + "<" + email + ">"
|
||||
subject = "Thank you for registering on rowsandall.com"
|
||||
from_address = 'Sander Roosendaal <info@rowsandall.com>'
|
||||
|
||||
d = {'first_name':theuser.first_name}
|
||||
|
||||
send_template_email(from_address,[fullemail],
|
||||
subject,'registeremail.html',d)
|
||||
current_site = get_current_site(request)
|
||||
mail_subject = 'Activate your account.'
|
||||
d = {
|
||||
'user': theuser,
|
||||
'domain': current_site.domain,
|
||||
'uid': urlsafe_base64_encode(force_bytes(theuser.id)).decode(),
|
||||
'token': account_activation_token.make_token(theuser),
|
||||
}
|
||||
to_email = form.cleaned_data.get('email')
|
||||
message = render_to_string('acc_activate_email.html', {
|
||||
'user': theuser,
|
||||
'domain': current_site.domain,
|
||||
'uid': urlsafe_base64_encode(force_bytes(theuser.id)).decode(),
|
||||
'token': account_activation_token.make_token(theuser),
|
||||
})
|
||||
to_email = form.cleaned_data.get('email')
|
||||
email = EmailMessage(
|
||||
mail_subject, message, to=[to_email]
|
||||
)
|
||||
email.send()
|
||||
return HttpResponse('Please confirm your email address to complete the registration')
|
||||
|
||||
|
||||
subject2 = "New User"
|
||||
message2 = "New user registered.\n"
|
||||
message2 += fullemail + "\n"
|
||||
message2 += "User name: "+username
|
||||
|
||||
send_mail(subject2, message2,
|
||||
'Rowsandall Server <info@rowsandall.com>',
|
||||
['roosendaalsander@gmail.com'])
|
||||
|
||||
theuser = authenticate(username=username,password=password)
|
||||
login(request,theuser)
|
||||
|
||||
return HttpResponseRedirect(nextpage)
|
||||
@@ -842,29 +900,28 @@ def freecoach_register_view(request):
|
||||
# create default favorite charts
|
||||
add_defaultfavorites(therower)
|
||||
|
||||
|
||||
# Create and send email
|
||||
fullemail = first_name + " " + last_name + " " + "<" + email + ">"
|
||||
subject = "Thank you for registering on rowsandall.com"
|
||||
from_address = 'Sander Roosendaal <info@rowsandall.com>'
|
||||
|
||||
d = {'first_name':theuser.first_name}
|
||||
|
||||
send_template_email(from_address,[fullemail],
|
||||
subject,'coachregisteremail.html',d)
|
||||
|
||||
|
||||
subject2 = "New Free Coach"
|
||||
message2 = "New Free Coach registered.\n"
|
||||
message2 += fullemail + "\n"
|
||||
message2 += "User name: "+username
|
||||
|
||||
send_mail(subject2, message2,
|
||||
'Rowsandall Server <info@rowsandall.com>',
|
||||
['roosendaalsander@gmail.com'])
|
||||
|
||||
theuser = authenticate(username=username,password=password)
|
||||
login(request,theuser)
|
||||
current_site = get_current_site(request)
|
||||
mail_subject = 'Activate your account.'
|
||||
d = {
|
||||
'user': theuser,
|
||||
'domain': current_site.domain,
|
||||
'uid': urlsafe_base64_encode(force_bytes(theuser.id)).decode(),
|
||||
'token': account_activation_token.make_token(theuser),
|
||||
}
|
||||
to_email = form.cleaned_data.get('email')
|
||||
message = render_to_string('acc_activate_email.html', {
|
||||
'user': theuser,
|
||||
'domain': current_site.domain,
|
||||
'uid': urlsafe_base64_encode(force_bytes(theuser.id)).decode(),
|
||||
'token': account_activation_token.make_token(theuser),
|
||||
})
|
||||
to_email = form.cleaned_data.get('email')
|
||||
email = EmailMessage(
|
||||
mail_subject, message, to=[to_email]
|
||||
)
|
||||
email.send()
|
||||
return HttpResponse('Please confirm your email address to complete the registration')
|
||||
|
||||
return HttpResponseRedirect(nextpage)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user